0/**********************************************************************************************
1*
2* rcore - Window/display management, Graphic device/context management and input management
3*
4* PLATFORMS SUPPORTED:
5* > PLATFORM_DESKTOP_GLFW (GLFW backend):
6* - Windows (Win32, Win64)
7* - Linux (X11/Wayland desktop mode)
8* - macOS/OSX (x64, arm64)
9* - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop)
10* > PLATFORM_DESKTOP_SDL (SDL backend):
11* - Windows (Win32, Win64)
12* - Linux (X11/Wayland desktop mode)
13* - Others (not tested)
14* > PLATFORM_DESKTOP_RGFW (RGFW backend):
15* - Windows (Win32, Win64)
16* - Linux (X11/Wayland desktop mode)
17* - macOS/OSX (x64, arm64)
18* - Others (not tested)
19* > PLATFORM_WEB_RGFW:
20* - HTML5 (WebAssembly)
21* > PLATFORM_WEB:
22* - HTML5 (WebAssembly)
23* > PLATFORM_DRM:
24* - Raspberry Pi 0-5 (DRM/KMS)
25* - Linux DRM subsystem (KMS mode)
26* > PLATFORM_ANDROID:
27* - Android (ARM, ARM64)
28* > PLATFORM_DESKTOP_WIN32 (Native Win32):
29* - Windows (Win32, Win64)
30* > PLATFORM_MEMORY
31* - Memory framebuffer output, using software renderer, no OS required
32* CONFIGURATION:
33* #define SUPPORT_DEFAULT_FONT (default)
34* Default font is loaded on window initialization to be available for the user to render simple text
35* NOTE: If enabled, uses external module functions to load default raylib font (module: text)
36*
37* #define SUPPORT_CAMERA_SYSTEM
38* Camera module is included (rcamera.h) and multiple predefined cameras are available:
39* free, 1st/3rd person, orbital, custom
40*
41* #define SUPPORT_GESTURES_SYSTEM
42* Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag
43*
44* #define SUPPORT_MOUSE_GESTURES
45* Mouse gestures are directly mapped like touches and processed by gestures system
46*
47* #define SUPPORT_BUSY_WAIT_LOOP
48* Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used
49*
50* #define SUPPORT_PARTIALBUSY_WAIT_LOOP
51* Use a partial-busy wait loop, in this case frame sleeps for most of the time and runs a busy-wait-loop at the end
52*
53* #define SUPPORT_SCREEN_CAPTURE
54* Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()
55*
56* #define SUPPORT_COMPRESSION_API
57* Support CompressData() and DecompressData() functions, those functions use zlib implementation
58* provided by stb_image and stb_image_write libraries, so, those libraries must be enabled on textures module
59* for linkage
60*
61* #define SUPPORT_AUTOMATION_EVENTS
62* Support automatic events recording and playing, useful for automated testing systems or AI based game playing
63*
64* DEPENDENCIES:
65* raymath - 3D math functionality (Vector2, Vector3, Matrix, Quaternion)
66* camera - Multiple 3D camera modes (free, orbital, 1st person, 3rd person)
67* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs)
68*
69*
70* LICENSE: zlib/libpng
71*
72* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) and contributors
73*
74* This software is provided "as-is", without any express or implied warranty. In no event
75* will the authors be held liable for any damages arising from the use of this software.
76*
77* Permission is granted to anyone to use this software for any purpose, including commercial
78* applications, and to alter it and redistribute it freely, subject to the following restrictions:
79*
80* 1. The origin of this software must not be misrepresented; you must not claim that you
81* wrote the original software. If you use this software in a product, an acknowledgment
82* in the product documentation would be appreciated but is not required.
83*
84* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
85* as being the original software.
86*
87* 3. This notice may not be removed or altered from any source distribution.
88*
89**********************************************************************************************/
91//----------------------------------------------------------------------------------
92// Feature Test Macros required for this module
93//----------------------------------------------------------------------------------
94#if (defined(__linux__) || defined(PLATFORM_WEB) || defined(PLATFORM_WEB_RGFW)) && (_XOPEN_SOURCE < 500)
95 #undef _XOPEN_SOURCE
96 #define _XOPEN_SOURCE 500 // Required for: readlink if compiled with c99 without GNU extensions
97#endif
99#if (defined(__linux__) || defined(PLATFORM_WEB) || defined(PLATFORM_WEB_RGFW)) && (_POSIX_C_SOURCE < 199309L)
100 #undef _POSIX_C_SOURCE
101 #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without GNU extensions
102#endif
104#include "raylib.h" // Declares module functions
106// Check if config flags have been externally provided on compilation line
107#if !defined(EXTERNAL_CONFIG_FLAGS)
108 #include "config.h" // Defines module configuration flags
109#endif
111#include "utils.h" // Required for: TRACELOG() macros
113#include <stdlib.h> // Required for: srand(), rand(), atexit()
114#include <stdio.h> // Required for: sprintf() [Used in OpenURL()]
115#include <string.h> // Required for: strlen(), strncpy(), strcmp(), strrchr(), memset()
116#include <time.h> // Required for: time() [Used in InitTimer()]
117#include <math.h> // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()]
119#if defined(PLATFORM_MEMORY) || defined(PLATFORM_WEB)
120 #define SW_GL_FRAMEBUFFER_COPY_BGRA false
121#endif
122#define RLGL_IMPLEMENTATION
123#include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
125#define RAYMATH_IMPLEMENTATION
126#include "raymath.h" // Vector2, Vector3, Quaternion and Matrix functionality
128#if defined(SUPPORT_GESTURES_SYSTEM)
129 #define RGESTURES_IMPLEMENTATION
130 #include "rgestures.h" // Gestures detection functionality
131#endif
133#if defined(SUPPORT_CAMERA_SYSTEM)
134 #define RCAMERA_IMPLEMENTATION
135 #include "rcamera.h" // Camera system functionality
136#endif
138#if defined(SUPPORT_COMPRESSION_API)
139 #define SINFL_IMPLEMENTATION
140 #define SINFL_NO_SIMD
141 #include "external/sinfl.h" // Deflate (RFC 1951) decompressor
143 #define SDEFL_IMPLEMENTATION
144 #include "external/sdefl.h" // Deflate (RFC 1951) compressor
145#endif
147#if defined(SUPPORT_RPRAND_GENERATOR)
148 #define RPRAND_IMPLEMENTATION
149 #include "external/rprand.h"
150#endif
152#if defined(__linux__) && !defined(_GNU_SOURCE)
153 #define _GNU_SOURCE
154#endif
156// Platform specific defines to handle GetApplicationDirectory()
157#if defined(_WIN32)
158 #if !defined(MAX_PATH)
159 #define MAX_PATH 260
160 #endif
162 struct HINSTANCE__;
163 #if defined(__cplusplus)
164 extern "C" {
165 #endif
166 __declspec(dllimport) unsigned long __stdcall GetModuleFileNameA(struct HINSTANCE__ *hModule, char *lpFilename, unsigned long nSize);
167 __declspec(dllimport) unsigned long __stdcall GetModuleFileNameW(struct HINSTANCE__ *hModule, wchar_t *lpFilename, unsigned long nSize);
168 __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default);
169 __declspec(dllimport) unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod);
170 __declspec(dllimport) unsigned int __stdcall timeEndPeriod(unsigned int uPeriod);
171 #if defined(__cplusplus)
172 }
173 #endif
174#elif defined(__linux__)
175 #include <unistd.h>
176#elif defined(__FreeBSD__)
177 #include <sys/types.h>
178 #include <sys/sysctl.h>
179 #include <unistd.h>
180#elif defined(__APPLE__)
181 #include <sys/syslimits.h>
182 #include <mach-o/dyld.h>
183#endif // OSs
185#define _CRT_INTERNAL_NONSTDC_NAMES 1
186#include <sys/stat.h> // Required for: stat(), S_ISREG [Used in GetFileModTime(), IsFilePath()]
188#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
189 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
190#endif
192#if defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__))
193 #define DIRENT_MALLOC RL_MALLOC
194 #define DIRENT_FREE RL_FREE
196 #include "external/dirent.h" // Required for: DIR, opendir(), closedir() [Used in LoadDirectoryFiles()]
197#else
198 #include <dirent.h> // Required for: DIR, opendir(), closedir() [Used in LoadDirectoryFiles()]
199#endif
201#if defined(_WIN32)
202 #include <io.h> // Required for: _access() [Used in FileExists()]
203 #include <direct.h> // Required for: _getch(), _chdir(), _mkdir()
204 #define GETCWD _getcwd // NOTE: MSDN recommends not to use getcwd(), chdir()
205 #define CHDIR _chdir
206 #define MKDIR(dir) _mkdir(dir)
207#else
208 #include <unistd.h> // Required for: getch(), chdir(), mkdir(), access()
209 #define GETCWD getcwd
210 #define CHDIR chdir
211 #define MKDIR(dir) mkdir(dir, 0777)
212#endif
214//----------------------------------------------------------------------------------
215// Defines and Macros
216//----------------------------------------------------------------------------------
217#ifndef MAX_FILEPATH_CAPACITY
218 #define MAX_FILEPATH_CAPACITY 8192 // Maximum capacity for filepath
219#endif
220#ifndef MAX_FILEPATH_LENGTH
221 #if defined(_WIN32)
222 #define MAX_FILEPATH_LENGTH 256 // On Win32, MAX_PATH = 260 (limits.h) but Windows 10, Version 1607 enables long paths...
223 #else
224 #define MAX_FILEPATH_LENGTH 4096 // On Linux, PATH_MAX = 4096 by default (limits.h)
225 #endif
226#endif
228#ifndef MAX_KEYBOARD_KEYS
229 #define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported
230#endif
231#ifndef MAX_MOUSE_BUTTONS
232 #define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported
233#endif
234#ifndef MAX_GAMEPADS
235 #define MAX_GAMEPADS 4 // Maximum number of gamepads supported
236#endif
237#ifndef MAX_GAMEPAD_NAME_LENGTH
238 #define MAX_GAMEPAD_NAME_LENGTH 128 // Maximum number of characters in a gamepad name (byte size)
239#endif
240#ifndef MAX_GAMEPAD_AXES
241 #define MAX_GAMEPAD_AXES 8 // Maximum number of axes supported (per gamepad)
242#endif
243#ifndef MAX_GAMEPAD_BUTTONS
244 #define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad)
245#endif
246#ifndef MAX_GAMEPAD_VIBRATION_TIME
247 #define MAX_GAMEPAD_VIBRATION_TIME 2.0f // Maximum vibration time in seconds
248#endif
249#ifndef MAX_TOUCH_POINTS
250 #define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported
251#endif
252#ifndef MAX_KEY_PRESSED_QUEUE
253 #define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue
254#endif
255#ifndef MAX_CHAR_PRESSED_QUEUE
256 #define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue
257#endif
259#ifndef MAX_DECOMPRESSION_SIZE
260 #define MAX_DECOMPRESSION_SIZE 64 // Maximum size allocated for decompression in MB
261#endif
263#ifndef MAX_AUTOMATION_EVENTS
264 #define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record
265#endif
267#ifndef DIRECTORY_FILTER_TAG
268 #define DIRECTORY_FILTER_TAG "DIR" // Name tag used to request directory inclusion on directory scan
269#endif // NOTE: Used in ScanDirectoryFiles(), ScanDirectoryFilesRecursively() and LoadDirectoryFilesEx()
271// Flags operation macros
272#define FLAG_SET(n, f) ((n) |= (f))
273#define FLAG_CLEAR(n, f) ((n) &= ~(f))
274#define FLAG_TOGGLE(n, f) ((n) ^= (f))
275#define FLAG_IS_SET(n, f) (((n) & (f)) == (f))
277//----------------------------------------------------------------------------------
278// Types and Structures Definition
279//----------------------------------------------------------------------------------
280typedef struct { int x; int y; } Point;
281typedef struct { unsigned int width; unsigned int height; } Size;
283// Core global state context data
284typedef struct CoreData {
285 struct {
286 const char *title; // Window text title const pointer
287 unsigned int flags; // Configuration flags (bit based), keeps window state
288 bool ready; // Check if window has been initialized successfully
289 bool shouldClose; // Check if window set for closing
290 bool resizedLastFrame; // Check if window has been resized last frame
291 bool eventWaiting; // Wait for events before ending frame
292 bool usingFbo; // Using FBO (RenderTexture) for rendering instead of default framebuffer
294 Size display; // Display width and height (monitor, device-screen, LCD, ...)
295 Size screen; // Screen current width and height
296 Point position; // Window current position
297 Size previousScreen; // Screen previous width and height (required on fullscreen/borderless-windowed toggle)
298 Point previousPosition; // Window previous position (required on fullscreeen/borderless-windowed toggle)
299 Size render; // Screen framebuffer width and height
300 Point renderOffset; // Screen framebuffer render offset (Not required anymore?)
301 Size currentFbo; // Current framebuffer render width and height (depends on active render texture)
302 Size screenMin; // Screen minimum width and height (for resizable window)
303 Size screenMax; // Screen maximum width and height (for resizable window)
304 Matrix screenScale; // Matrix to scale screen (framebuffer rendering)
306 char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW)
307 unsigned int dropFileCount; // Count dropped files strings
309 } Window;
310 struct {
311 const char *basePath; // Base path for data storage
313 } Storage;
314 struct {
315 struct {
316 int exitKey; // Default exit key
317 char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state
318 char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state
320 // NOTE: Since key press logic involves comparing previous vs currrent key state,
321 // key repeats needs to be handled specially
322 char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame
324 int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue
325 int keyPressedQueueCount; // Input keys queue count
327 int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode)
328 int charPressedQueueCount; // Input characters queue count
330 } Keyboard;
331 struct {
332 Vector2 offset; // Mouse offset
333 Vector2 scale; // Mouse scaling
334 Vector2 currentPosition; // Mouse position on screen
335 Vector2 previousPosition; // Previous mouse position
336 Vector2 lockedPosition; // Mouse position when locked
338 int cursor; // Tracks current mouse cursor
339 bool cursorHidden; // Track if cursor is hidden
340 bool cursorLocked; // Track if cursor is locked (disabled)
341 bool cursorOnScreen; // Tracks if cursor is inside client area
343 char currentButtonState[MAX_MOUSE_BUTTONS]; // Registers current mouse button state
344 char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state
345 Vector2 currentWheelMove; // Registers current mouse wheel variation
346 Vector2 previousWheelMove; // Registers previous mouse wheel variation
348 } Mouse;
349 struct {
350 int pointCount; // Number of touch points active
351 int pointId[MAX_TOUCH_POINTS]; // Point identifiers
352 Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen
353 char currentTouchState[MAX_TOUCH_POINTS]; // Registers current touch state
354 char previousTouchState[MAX_TOUCH_POINTS]; // Registers previous touch state
356 } Touch;
357 struct {
358 int lastButtonPressed; // Register last gamepad button pressed
359 int axisCount[MAX_GAMEPADS]; // Register number of available gamepad axes
360 bool ready[MAX_GAMEPADS]; // Flag to know if gamepad is ready
361 char name[MAX_GAMEPADS][MAX_GAMEPAD_NAME_LENGTH]; // Gamepad name holder
362 char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state
363 char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state
364 float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXES]; // Gamepad axes state
366 } Gamepad;
367 } Input;
368 struct {
369 double current; // Current time measure
370 double previous; // Previous time measure
371 double update; // Time measure for frame update
372 double draw; // Time measure for frame draw
373 double frame; // Time measure for one frame
374 double target; // Desired time for one frame, if 0 not applied
375 unsigned long long int base; // Base time measure for hi-res timer (PLATFORM_ANDROID, PLATFORM_DRM)
376 unsigned int frameCounter; // Frame counter
378 } Time;
379} CoreData;
381//----------------------------------------------------------------------------------
382// Global Variables Definition
383//----------------------------------------------------------------------------------
384RLAPI const char *raylib_version = RAYLIB_VERSION; // raylib version exported symbol, required for some bindings
386CoreData CORE = { 0 }; // Global CORE state context
388#if defined(SUPPORT_SCREEN_CAPTURE)
389static int screenshotCounter = 0; // Screenshots counter
390#endif
392#if defined(SUPPORT_AUTOMATION_EVENTS)
393// Automation events type
394typedef enum AutomationEventType {
395 EVENT_NONE = 0,
396 // Input events
397 INPUT_KEY_UP, // param[0]: key
398 INPUT_KEY_DOWN, // param[0]: key
399 INPUT_KEY_PRESSED, // param[0]: key
400 INPUT_KEY_RELEASED, // param[0]: key
401 INPUT_MOUSE_BUTTON_UP, // param[0]: button
402 INPUT_MOUSE_BUTTON_DOWN, // param[0]: button
403 INPUT_MOUSE_POSITION, // param[0]: x, param[1]: y
404 INPUT_MOUSE_WHEEL_MOTION, // param[0]: x delta, param[1]: y delta
405 INPUT_GAMEPAD_CONNECT, // param[0]: gamepad
406 INPUT_GAMEPAD_DISCONNECT, // param[0]: gamepad
407 INPUT_GAMEPAD_BUTTON_UP, // param[0]: button
408 INPUT_GAMEPAD_BUTTON_DOWN, // param[0]: button
409 INPUT_GAMEPAD_AXIS_MOTION, // param[0]: axis, param[1]: delta
410 INPUT_TOUCH_UP, // param[0]: id
411 INPUT_TOUCH_DOWN, // param[0]: id
412 INPUT_TOUCH_POSITION, // param[0]: x, param[1]: y
413 INPUT_GESTURE, // param[0]: gesture
414 // Window events
415 WINDOW_CLOSE, // no params
416 WINDOW_MAXIMIZE, // no params
417 WINDOW_MINIMIZE, // no params
418 WINDOW_RESIZE, // param[0]: width, param[1]: height
419 // Custom events
420 ACTION_TAKE_SCREENSHOT, // no params
421 ACTION_SETTARGETFPS // param[0]: fps
422} AutomationEventType;
424// Event type to config events flags
425// WARNING: Not used at the moment
426typedef enum {
427 EVENT_INPUT_KEYBOARD = 0,
428 EVENT_INPUT_MOUSE = 1,
429 EVENT_INPUT_GAMEPAD = 2,
430 EVENT_INPUT_TOUCH = 4,
431 EVENT_INPUT_GESTURE = 8,
432 EVENT_WINDOW = 16,
433 EVENT_CUSTOM = 32
434} EventType;
436// Event type name strings, required for export
437static const char *autoEventTypeName[] = {
438 "EVENT_NONE",
439 "INPUT_KEY_UP",
440 "INPUT_KEY_DOWN",
441 "INPUT_KEY_PRESSED",
442 "INPUT_KEY_RELEASED",
443 "INPUT_MOUSE_BUTTON_UP",
444 "INPUT_MOUSE_BUTTON_DOWN",
445 "INPUT_MOUSE_POSITION",
446 "INPUT_MOUSE_WHEEL_MOTION",
447 "INPUT_GAMEPAD_CONNECT",
448 "INPUT_GAMEPAD_DISCONNECT",
449 "INPUT_GAMEPAD_BUTTON_UP",
450 "INPUT_GAMEPAD_BUTTON_DOWN",
451 "INPUT_GAMEPAD_AXIS_MOTION",
452 "INPUT_TOUCH_UP",
453 "INPUT_TOUCH_DOWN",
454 "INPUT_TOUCH_POSITION",
455 "INPUT_GESTURE",
456 "WINDOW_CLOSE",
457 "WINDOW_MAXIMIZE",
458 "WINDOW_MINIMIZE",
459 "WINDOW_RESIZE",
460 "ACTION_TAKE_SCREENSHOT",
461 "ACTION_SETTARGETFPS"
462};
464/*
465// Automation event (24 bytes)
466// NOTE: Opaque struct, internal to raylib
467struct AutomationEvent {
468 unsigned int frame; // Event frame
469 unsigned int type; // Event type (AutomationEventType)
470 int params[4]; // Event parameters (if required)
471};
472*/
474static AutomationEventList *currentEventList = NULL; // Current automation events list, set by user, keep internal pointer
475static bool automationEventRecording = false; // Recording automation events flag
476//static short automationEventEnabled = 0b0000001111111111; // TODO: Automation events enabled for recording/playing
477#endif
478//-----------------------------------------------------------------------------------
480//----------------------------------------------------------------------------------
481// Module Functions Declaration
482// NOTE: Those functions are common for all platforms!
483//----------------------------------------------------------------------------------
485#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
486extern void LoadFontDefault(void); // [Module: text] Loads default font on InitWindow()
487extern void UnloadFontDefault(void); // [Module: text] Unloads default font from GPU memory
488#endif
490extern int InitPlatform(void); // Initialize platform (graphics, inputs and more)
491extern void ClosePlatform(void); // Close platform
493static void InitTimer(void); // Initialize timer, hi-resolution if available (required by InitPlatform())
494static void SetupViewport(int width, int height); // Set viewport for a provided width and height
496static void ScanDirectoryFiles(const char *basePath, FilePathList *list, const char *filter); // Scan all files and directories in a base path
497static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *list, const char *filter); // Scan all files and directories recursively from a base path
499#if defined(SUPPORT_AUTOMATION_EVENTS)
500static void RecordAutomationEvent(void); // Record frame events (to internal events array)
501#endif
503#if defined(_WIN32) && !defined(PLATFORM_DESKTOP_RGFW)
504// NOTE: We declare Sleep() function symbol to avoid including windows.h (kernel32.lib linkage required)
505__declspec(dllimport) void __stdcall Sleep(unsigned long msTimeout); // Required for: WaitTime()
506#endif
508#if !defined(SUPPORT_MODULE_RTEXT)
509const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed'
510#endif // !SUPPORT_MODULE_RTEXT
512#if defined(PLATFORM_DESKTOP)
513 #define PLATFORM_DESKTOP_GLFW
514#endif
516// We're using '#pragma message' because '#warning' is not adopted by MSVC
517#if defined(SUPPORT_CLIPBOARD_IMAGE)
518 #if !defined(SUPPORT_MODULE_RTEXTURES)
519 #pragma message ("WARNING: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly")
520 #endif
522 // It's nice to have support Bitmap on Linux as well, but not as necessary as Windows
523 #if !defined(SUPPORT_FILEFORMAT_BMP) && defined(_WIN32)
524 #pragma message ("WARNING: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_FILEFORMAT_BMP, specially on Windows")
525 #endif
527 // From what I've tested applications on Wayland saves images on clipboard as PNG
528 #if (!defined(SUPPORT_FILEFORMAT_PNG) || !defined(SUPPORT_FILEFORMAT_JPG)) && !defined(_WIN32)
529 #pragma message ("WARNING: Getting image from the clipboard might not work without SUPPORT_FILEFORMAT_PNG or SUPPORT_FILEFORMAT_JPG")
530 #endif
531#endif // SUPPORT_CLIPBOARD_IMAGE
533// Include platform-specific submodules
534#if defined(PLATFORM_DESKTOP_GLFW)
535 #include "platforms/rcore_desktop_glfw.c"
536#elif defined(PLATFORM_DESKTOP_SDL)
537 #include "platforms/rcore_desktop_sdl.c"
538#elif (defined(PLATFORM_DESKTOP_RGFW) || defined(PLATFORM_WEB_RGFW))
539 #include "platforms/rcore_desktop_rgfw.c"
540#elif defined(PLATFORM_DESKTOP_WIN32)
541 #include "platforms/rcore_desktop_win32.c"
542#elif defined(PLATFORM_WEB)
543 #include "platforms/rcore_web.c"
544#elif defined(PLATFORM_DRM)
545 #include "platforms/rcore_drm.c"
546#elif defined(PLATFORM_ANDROID)
547 #include "platforms/rcore_android.c"
548#elif defined(PLATFORM_MEMORY)
549 #include "platforms/rcore_memory.c"
550#else
551 // TODO: Include your custom platform backend!
552 // i.e software rendering backend or console backend!
553 #pragma message ("WARNING: No [rcore] platform defined")
554#endif
556//----------------------------------------------------------------------------------
557// Module Functions Definition: Window and Graphics Device
558//----------------------------------------------------------------------------------
560// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
561//bool WindowShouldClose(void)
562//void ToggleFullscreen(void)
563//void ToggleBorderlessWindowed(void)
564//void MaximizeWindow(void)
565//void MinimizeWindow(void)
566//void RestoreWindow(void)
568//void SetWindowState(unsigned int flags)
569//void ClearWindowState(unsigned int flags)
571//void SetWindowIcon(Image image)
572//void SetWindowIcons(Image *images, int count)
573//void SetWindowTitle(const char *title)
574//void SetWindowPosition(int x, int y)
575//void SetWindowMonitor(int monitor)
576//void SetWindowMinSize(int width, int height)
577//void SetWindowMaxSize(int width, int height)
578//void SetWindowSize(int width, int height)
579//void SetWindowOpacity(float opacity)
580//void SetWindowFocused(void)
581//void *GetWindowHandle(void)
582//Vector2 GetWindowPosition(void)
583//Vector2 GetWindowScaleDPI(void)
585//int GetMonitorCount(void)
586//int GetCurrentMonitor(void)
587//int GetMonitorWidth(int monitor)
588//int GetMonitorHeight(int monitor)
589//int GetMonitorPhysicalWidth(int monitor)
590//int GetMonitorPhysicalHeight(int monitor)
591//int GetMonitorRefreshRate(int monitor)
592//Vector2 GetMonitorPosition(int monitor)
593//const char *GetMonitorName(int monitor)
595//void SetClipboardText(const char *text)
596//const char *GetClipboardText(void)
598//void ShowCursor(void)
599//void HideCursor(void)
600//void EnableCursor(void)
601//void DisableCursor(void)
603// Initialize window and OpenGL context
604void InitWindow(int width, int height, const char *title)
605{
606 TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION);
608#if defined(PLATFORM_DESKTOP_GLFW)
609 TRACELOG(LOG_INFO, "Platform backend: DESKTOP (GLFW)");
610#elif defined(PLATFORM_DESKTOP_SDL)
611 TRACELOG(LOG_INFO, "Platform backend: DESKTOP (SDL)");
612#elif defined(PLATFORM_DESKTOP_RGFW)
613 TRACELOG(LOG_INFO, "Platform backend: DESKTOP (RGFW)");
614#elif defined(PLATFORM_DESKTOP_WIN32)
615 TRACELOG(LOG_INFO, "Platform backend: DESKTOP (WIN32)");
616#elif defined(PLATFORM_WEB_RGFW)
617 TRACELOG(LOG_INFO, "Platform backend: WEB (RGFW) (HTML5)");
618#elif defined(PLATFORM_WEB)
619 TRACELOG(LOG_INFO, "Platform backend: WEB (HTML5)");
620#elif defined(PLATFORM_DRM)
621 TRACELOG(LOG_INFO, "Platform backend: NATIVE DRM");
622#elif defined(PLATFORM_ANDROID)
623 TRACELOG(LOG_INFO, "Platform backend: ANDROID");
624#elif defined(PLATFORM_MEMORY)
625 TRACELOG(LOG_INFO, "Platform backend: MEMORY (No OS)");
626#else
627 // TODO: Include your custom platform backend!
628 // i.e software rendering backend or console backend!
629 TRACELOG(LOG_INFO, "Platform backend: CUSTOM");
630#endif
632 TRACELOG(LOG_INFO, "Supported raylib modules:");
633 TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)");
634 TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)");
635#if defined(SUPPORT_MODULE_RSHAPES)
636 TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)");
637#else
638 TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)");
639#endif
640#if defined(SUPPORT_MODULE_RTEXTURES)
641 TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)");
642#else
643 TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)");
644#endif
645#if defined(SUPPORT_MODULE_RTEXT)
646 TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)");
647#else
648 TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)");
649#endif
650#if defined(SUPPORT_MODULE_RMODELS)
651 TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)");
652#else
653 TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)");
654#endif
655#if defined(SUPPORT_MODULE_RAUDIO)
656 TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)");
657#else
658 TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)");
659#endif
661 // Initialize window data
662 CORE.Window.screen.width = width;
663 CORE.Window.screen.height = height;
664 CORE.Window.currentFbo.width = CORE.Window.screen.width;
665 CORE.Window.currentFbo.height = CORE.Window.screen.height;
667 CORE.Window.eventWaiting = false;
668 CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default
669 if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title;
671 // Initialize global input state
672 memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0
673 CORE.Input.Keyboard.exitKey = KEY_ESCAPE;
674 CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f };
675 CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW;
676 CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN;
678 // Initialize platform
679 //--------------------------------------------------------------
680 int result = InitPlatform();
682 if (result != 0)
683 {
684 TRACELOG(LOG_WARNING, "SYSTEM: Failed to initialize platform");
685 return;
686 }
687 //--------------------------------------------------------------
689 // Initialize rlgl default data (buffers and shaders)
690 // NOTE: Current fbo size stored as globals in rlgl for convenience
691 rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
693 // Setup default viewport
694 SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
696#if defined(SUPPORT_MODULE_RTEXT)
697 #if defined(SUPPORT_DEFAULT_FONT)
698 // Load default font
699 // WARNING: External function: Module required: rtext
700 LoadFontDefault();
701 #if defined(SUPPORT_MODULE_RSHAPES)
702 // Set font white rectangle for shapes drawing, so shapes and text can be batched together
703 // WARNING: rshapes module is required, if not available, default internal white rectangle is used
704 Rectangle rec = GetFontDefault().recs[95];
705 if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
706 {
707 // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering
708 SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 });
709 }
710 else
711 {
712 // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding
713 SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 });
714 }
715 #endif
716 #endif
717#else
718 #if defined(SUPPORT_MODULE_RSHAPES)
719 // Set default texture and rectangle to be used for shapes drawing
720 // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
721 Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
722 SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes
723 #endif
724#endif
726 CORE.Time.frameCounter = 0;
727 CORE.Window.shouldClose = false;
729 // Initialize random seed
730 SetRandomSeed((unsigned int)time(NULL));
732 TRACELOG(LOG_INFO, "SYSTEM: Working Directory: %s", GetWorkingDirectory());
733}
735// Close window and unload OpenGL context
736void CloseWindow(void)
737{
738#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
739 UnloadFontDefault(); // WARNING: Module required: rtext
740#endif
742 rlglClose(); // De-init rlgl
744 // De-initialize platform
745 //--------------------------------------------------------------
746 ClosePlatform();
747 //--------------------------------------------------------------
749 CORE.Window.ready = false;
750 TRACELOG(LOG_INFO, "Window closed successfully");
751}
753// Check if window has been initialized successfully
754bool IsWindowReady(void)
755{
756 return CORE.Window.ready;
757}
759// Check if window is currently fullscreen
760bool IsWindowFullscreen(void)
761{
762 return FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
763}
765// Check if window is currently hidden
766bool IsWindowHidden(void)
767{
768 return FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN);
769}
771// Check if window has been minimized
772bool IsWindowMinimized(void)
773{
774 return FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);
775}
777// Check if window has been maximized
778bool IsWindowMaximized(void)
779{
780 return FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
781}
783// Check if window has the focus
784bool IsWindowFocused(void)
785{
786 return !FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
787}
789// Check if window has been resizedLastFrame
790bool IsWindowResized(void)
791{
792 return CORE.Window.resizedLastFrame;
793}
795// Check if one specific window flag is enabled
796bool IsWindowState(unsigned int flag)
797{
798 return FLAG_IS_SET(CORE.Window.flags, flag);
799}
801// Get current screen width
802int GetScreenWidth(void)
803{
804 return CORE.Window.screen.width;
805}
807// Get current screen height
808int GetScreenHeight(void)
809{
810 return CORE.Window.screen.height;
811}
813// Get current render width which is equal to screen width*dpi scale
814int GetRenderWidth(void)
815{
816 int width = 0;
818 if (CORE.Window.usingFbo) return CORE.Window.currentFbo.width;
819 else width = CORE.Window.render.width;
821 return width;
822}
824// Get current screen height which is equal to screen height*dpi scale
825int GetRenderHeight(void)
826{
827 int height = 0;
829 if (CORE.Window.usingFbo) return CORE.Window.currentFbo.height;
830 else height = CORE.Window.render.height;
832 return height;
833}
835// Enable waiting for events on EndDrawing(), no automatic event polling
836void EnableEventWaiting(void)
837{
838 CORE.Window.eventWaiting = true;
839}
841// Disable waiting for events on EndDrawing(), automatic events polling
842void DisableEventWaiting(void)
843{
844 CORE.Window.eventWaiting = false;
845}
847// Check if cursor is not visible
848bool IsCursorHidden(void)
849{
850 return CORE.Input.Mouse.cursorHidden;
851}
853// Check if cursor is on the current screen
854bool IsCursorOnScreen(void)
855{
856 return CORE.Input.Mouse.cursorOnScreen;
857}
859//----------------------------------------------------------------------------------
860// Module Functions Definition: Screen Drawing
861//----------------------------------------------------------------------------------
863// Set background color (framebuffer clear color)
864void ClearBackground(Color color)
865{
866 rlClearColor(color.r, color.g, color.b, color.a); // Set clear color
867 rlClearScreenBuffers(); // Clear current framebuffers
868}
870// Setup canvas (framebuffer) to start drawing
871void BeginDrawing(void)
872{
873 // WARNING: Previously to BeginDrawing() other render textures drawing could happen,
874 // consequently the measure for update vs draw is not accurate (only the total frame time is accurate)
876 CORE.Time.current = GetTime(); // Number of elapsed seconds since InitTimer()
877 CORE.Time.update = CORE.Time.current - CORE.Time.previous;
878 CORE.Time.previous = CORE.Time.current;
880 rlLoadIdentity(); // Reset current matrix (modelview)
881 rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling
883 //rlTranslatef(0.375, 0.375, 0); // HACK to have 2D pixel-perfect drawing on OpenGL 1.1
884 // NOTE: Not required with OpenGL 3.3+
885}
887// End canvas drawing and swap buffers (double buffering)
888void EndDrawing(void)
889{
890 rlDrawRenderBatchActive(); // Update and draw internal render batch
892#if defined(SUPPORT_AUTOMATION_EVENTS)
893 if (automationEventRecording) RecordAutomationEvent(); // Event recording
894#endif
896#if !defined(SUPPORT_CUSTOM_FRAME_CONTROL)
897 SwapScreenBuffer(); // Copy back buffer to front buffer (screen)
899 // Frame time control system
900 CORE.Time.current = GetTime();
901 CORE.Time.draw = CORE.Time.current - CORE.Time.previous;
902 CORE.Time.previous = CORE.Time.current;
904 CORE.Time.frame = CORE.Time.update + CORE.Time.draw;
906 // Wait for some milliseconds...
907 if (CORE.Time.frame < CORE.Time.target)
908 {
909 WaitTime(CORE.Time.target - CORE.Time.frame);
911 CORE.Time.current = GetTime();
912 double waitTime = CORE.Time.current - CORE.Time.previous;
913 CORE.Time.previous = CORE.Time.current;
915 CORE.Time.frame += waitTime; // Total frame time: update + draw + wait
916 }
918 PollInputEvents(); // Poll user events (before next frame update)
919#endif
921#if defined(SUPPORT_SCREEN_CAPTURE)
922 if (IsKeyPressed(KEY_F12))
923 {
924 TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
925 screenshotCounter++;
926 }
927#endif // SUPPORT_SCREEN_CAPTURE
929 CORE.Time.frameCounter++;
930}
932// Initialize 2D mode with custom camera (2D)
933void BeginMode2D(Camera2D camera)
934{
935 rlDrawRenderBatchActive(); // Update and draw internal render batch
937 rlLoadIdentity(); // Reset current matrix (modelview)
939 // Apply 2d camera transformation to modelview
940 rlMultMatrixf(MatrixToFloat(GetCameraMatrix2D(camera)));
941}
943// Ends 2D mode with custom camera
944void EndMode2D(void)
945{
946 rlDrawRenderBatchActive(); // Update and draw internal render batch
948 rlLoadIdentity(); // Reset current matrix (modelview)
950 if (rlGetActiveFramebuffer() == 0) rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
951}
953// Initializes 3D mode with custom camera (3D)
954void BeginMode3D(Camera camera)
955{
956 rlDrawRenderBatchActive(); // Update and draw internal render batch
958 rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
959 rlPushMatrix(); // Save previous matrix, which contains the settings for the 2d ortho projection
960 rlLoadIdentity(); // Reset current matrix (projection)
962 float aspect = (float)CORE.Window.currentFbo.width/(float)CORE.Window.currentFbo.height;
964 // NOTE: zNear and zFar values are important when computing depth buffer values
965 if (camera.projection == CAMERA_PERSPECTIVE)
966 {
967 // Setup perspective projection
968 double top = rlGetCullDistanceNear()*tan(camera.fovy*0.5*DEG2RAD);
969 double right = top*aspect;
971 rlFrustum(-right, right, -top, top, rlGetCullDistanceNear(), rlGetCullDistanceFar());
972 }
973 else if (camera.projection == CAMERA_ORTHOGRAPHIC)
974 {
975 // Setup orthographic projection
976 double top = camera.fovy/2.0;
977 double right = top*aspect;
979 rlOrtho(-right, right, -top,top, rlGetCullDistanceNear(), rlGetCullDistanceFar());
980 }
982 rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
983 rlLoadIdentity(); // Reset current matrix (modelview)
985 // Setup Camera view
986 Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
987 rlMultMatrixf(MatrixToFloat(matView)); // Multiply modelview matrix by view matrix (camera)
989 rlEnableDepthTest(); // Enable DEPTH_TEST for 3D
990}
992// Ends 3D mode and returns to default 2D orthographic mode
993void EndMode3D(void)
994{
995 rlDrawRenderBatchActive(); // Update and draw internal render batch
997 rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
998 rlPopMatrix(); // Restore previous matrix (projection) from matrix stack
1000 rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
1001 rlLoadIdentity(); // Reset current matrix (modelview)
1003 if (rlGetActiveFramebuffer() == 0) rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
1005 rlDisableDepthTest(); // Disable DEPTH_TEST for 2D
1006}
1008// Initializes render texture for drawing
1009void BeginTextureMode(RenderTexture2D target)
1010{
1011 rlDrawRenderBatchActive(); // Update and draw internal render batch
1013 rlEnableFramebuffer(target.id); // Enable render target
1015 // Set viewport and RLGL internal framebuffer size
1016 rlViewport(0, 0, target.texture.width, target.texture.height);
1017 rlSetFramebufferWidth(target.texture.width);
1018 rlSetFramebufferHeight(target.texture.height);
1020 rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
1021 rlLoadIdentity(); // Reset current matrix (projection)
1023 // Set orthographic projection to current framebuffer size
1024 // NOTE: Configured top-left corner as (0, 0)
1025 rlOrtho(0, target.texture.width, target.texture.height, 0, 0.0f, 1.0f);
1027 rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
1028 rlLoadIdentity(); // Reset current matrix (modelview)
1030 //rlScalef(0.0f, -1.0f, 0.0f); // Flip Y-drawing (?)
1032 // Setup current width/height for proper aspect ratio
1033 // calculation when using BeginTextureMode()
1034 CORE.Window.currentFbo.width = target.texture.width;
1035 CORE.Window.currentFbo.height = target.texture.height;
1036 CORE.Window.usingFbo = true;
1037}
1039// Ends drawing to render texture
1040void EndTextureMode(void)
1041{
1042 rlDrawRenderBatchActive(); // Update and draw internal render batch
1044 rlDisableFramebuffer(); // Disable render target (fbo)
1046 // Set viewport to default framebuffer size
1047 SetupViewport(CORE.Window.render.width, CORE.Window.render.height);
1049 // Go back to the modelview state from BeginDrawing since we are back to the default FBO
1050 rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
1051 rlLoadIdentity(); // Reset current matrix (modelview)
1052 rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
1054 // Reset current fbo to screen size
1055 CORE.Window.currentFbo.width = CORE.Window.render.width;
1056 CORE.Window.currentFbo.height = CORE.Window.render.height;
1057 CORE.Window.usingFbo = false;
1058}
1060// Begin custom shader mode
1061void BeginShaderMode(Shader shader)
1062{
1063 rlSetShader(shader.id, shader.locs);
1064}
1066// End custom shader mode (returns to default shader)
1067void EndShaderMode(void)
1068{
1069 rlSetShader(rlGetShaderIdDefault(), rlGetShaderLocsDefault());
1070}
1072// Begin blending mode (alpha, additive, multiplied, subtract, custom)
1073// NOTE: Blend modes supported are enumerated in BlendMode enum
1074void BeginBlendMode(int mode)
1075{
1076 rlSetBlendMode(mode);
1077}
1079// End blending mode (reset to default: alpha blending)
1080void EndBlendMode(void)
1081{
1082 rlSetBlendMode(BLEND_ALPHA);
1083}
1085// Begin scissor mode (define screen area for following drawing)
1086// NOTE: Scissor rec refers to bottom-left corner, we change it to upper-left
1087void BeginScissorMode(int x, int y, int width, int height)
1088{
1089 rlDrawRenderBatchActive(); // Update and draw internal render batch
1091 rlEnableScissorTest();
1093#if defined(__APPLE__)
1094 if (!CORE.Window.usingFbo)
1095 {
1096 Vector2 scale = GetWindowScaleDPI();
1097 rlScissor((int)(x*scale.x), (int)(GetScreenHeight()*scale.y - (((y + height)*scale.y))), (int)(width*scale.x), (int)(height*scale.y));
1098 }
1099#else
1100 if (!CORE.Window.usingFbo && FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
1101 {
1102 Vector2 scale = GetWindowScaleDPI();
1103 rlScissor((int)(x*scale.x), (int)(CORE.Window.currentFbo.height - (y + height)*scale.y), (int)(width*scale.x), (int)(height*scale.y));
1104 }
1105#endif
1106 else
1107 {
1108 rlScissor(x, CORE.Window.currentFbo.height - (y + height), width, height);
1109 }
1110}
1112// End scissor mode
1113void EndScissorMode(void)
1114{
1115 rlDrawRenderBatchActive(); // Update and draw internal render batch
1116 rlDisableScissorTest();
1117}
1119//----------------------------------------------------------------------------------
1120// Module Functions Definition: VR Stereo Rendering
1121//----------------------------------------------------------------------------------
1123// Begin VR drawing configuration
1124void BeginVrStereoMode(VrStereoConfig config)
1125{
1126 rlEnableStereoRender();
1128 // Set stereo render matrices
1129 rlSetMatrixProjectionStereo(config.projection[0], config.projection[1]);
1130 rlSetMatrixViewOffsetStereo(config.viewOffset[0], config.viewOffset[1]);
1131}
1133// End VR drawing process (and desktop mirror)
1134void EndVrStereoMode(void)
1135{
1136 rlDisableStereoRender();
1137}
1139// Load VR stereo config for VR simulator device parameters
1140VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device)
1141{
1142 VrStereoConfig config = { 0 };
1144 if (rlGetVersion() != RL_OPENGL_11)
1145 {
1146 // Compute aspect ratio
1147 float aspect = ((float)device.hResolution*0.5f)/(float)device.vResolution;
1149 // Compute lens parameters
1150 float lensShift = (device.hScreenSize*0.25f - device.lensSeparationDistance*0.5f)/device.hScreenSize;
1151 config.leftLensCenter[0] = 0.25f + lensShift;
1152 config.leftLensCenter[1] = 0.5f;
1153 config.rightLensCenter[0] = 0.75f - lensShift;
1154 config.rightLensCenter[1] = 0.5f;
1155 config.leftScreenCenter[0] = 0.25f;
1156 config.leftScreenCenter[1] = 0.5f;
1157 config.rightScreenCenter[0] = 0.75f;
1158 config.rightScreenCenter[1] = 0.5f;
1160 // Compute distortion scale parameters
1161 // NOTE: To get lens max radius, lensShift must be normalized to [-1..1]
1162 float lensRadius = fabsf(-1.0f - 4.0f*lensShift);
1163 float lensRadiusSq = lensRadius*lensRadius;
1164 float distortionScale = device.lensDistortionValues[0] +
1165 device.lensDistortionValues[1]*lensRadiusSq +
1166 device.lensDistortionValues[2]*lensRadiusSq*lensRadiusSq +
1167 device.lensDistortionValues[3]*lensRadiusSq*lensRadiusSq*lensRadiusSq;
1169 float normScreenWidth = 0.5f;
1170 float normScreenHeight = 1.0f;
1171 config.scaleIn[0] = 2.0f/normScreenWidth;
1172 config.scaleIn[1] = 2.0f/normScreenHeight/aspect;
1173 config.scale[0] = normScreenWidth*0.5f/distortionScale;
1174 config.scale[1] = normScreenHeight*0.5f*aspect/distortionScale;
1176 // Fovy is normally computed with: 2*atan2f(device.vScreenSize, 2*device.eyeToScreenDistance)
1177 // ...but with lens distortion it is increased (see Oculus SDK Documentation)
1178 float fovy = 2.0f*atan2f(device.vScreenSize*0.5f*distortionScale, device.eyeToScreenDistance); // Really need distortionScale?
1179 // float fovy = 2.0f*(float)atan2f(device.vScreenSize*0.5f, device.eyeToScreenDistance);
1181 // Compute camera projection matrices
1182 float projOffset = 4.0f*lensShift; // Scaled to projection space coordinates [-1..1]
1183 Matrix proj = MatrixPerspective(fovy, aspect, rlGetCullDistanceNear(), rlGetCullDistanceFar());
1185 config.projection[0] = MatrixMultiply(proj, MatrixTranslate(projOffset, 0.0f, 0.0f));
1186 config.projection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f));
1188 // Compute camera transformation matrices
1189 // NOTE: Camera movement might seem more natural if we model the head
1190 // Our axis of rotation is the base of our head, so we might want to add
1191 // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions
1192 config.viewOffset[0] = MatrixTranslate(device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
1193 config.viewOffset[1] = MatrixTranslate(-device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
1195 // Compute eyes Viewports
1196 /*
1197 config.eyeViewportRight[0] = 0;
1198 config.eyeViewportRight[1] = 0;
1199 config.eyeViewportRight[2] = device.hResolution/2;
1200 config.eyeViewportRight[3] = device.vResolution;
1202 config.eyeViewportLeft[0] = device.hResolution/2;
1203 config.eyeViewportLeft[1] = 0;
1204 config.eyeViewportLeft[2] = device.hResolution/2;
1205 config.eyeViewportLeft[3] = device.vResolution;
1206 */
1207 }
1208 else TRACELOG(LOG_WARNING, "RLGL: VR Simulator not supported on OpenGL 1.1");
1210 return config;
1211}
1213// Unload VR stereo config properties
1214void UnloadVrStereoConfig(VrStereoConfig config)
1215{
1216 TRACELOG(LOG_INFO, "UnloadVrStereoConfig not implemented in rcore.c");
1217}
1219//----------------------------------------------------------------------------------
1220// Module Functions Definition: Shaders Management
1221//----------------------------------------------------------------------------------
1223// Load shader from files and bind default locations
1224// NOTE: If shader string is NULL, using default vertex/fragment shaders
1225Shader LoadShader(const char *vsFileName, const char *fsFileName)
1226{
1227 Shader shader = { 0 };
1229 char *vShaderStr = NULL;
1230 char *fShaderStr = NULL;
1232 if (vsFileName != NULL) vShaderStr = LoadFileText(vsFileName);
1233 if (fsFileName != NULL) fShaderStr = LoadFileText(fsFileName);
1235 if ((vShaderStr == NULL) && (fShaderStr == NULL)) TRACELOG(LOG_WARNING, "SHADER: Shader files provided are not valid, using default shader");
1237 shader = LoadShaderFromMemory(vShaderStr, fShaderStr);
1239 UnloadFileText(vShaderStr);
1240 UnloadFileText(fShaderStr);
1242 return shader;
1243}
1245// Load shader from code strings and bind default locations
1246Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
1247{
1248 Shader shader = { 0 };
1250 shader.id = rlLoadShaderCode(vsCode, fsCode);
1252 if (shader.id == 0)
1253 {
1254 // Shader could not be loaded but we still load the location points to avoid potential crashes
1255 // NOTE: All locations set to -1 (no location)
1256 shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int));
1257 for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1;
1258 }
1259 else if (shader.id == rlGetShaderIdDefault()) shader.locs = rlGetShaderLocsDefault();
1260 else if (shader.id > 0)
1261 {
1262 // After custom shader loading, we TRY to set default location names
1263 // Default shader attribute locations have been binded before linking:
1264 // - vertex position location = 0
1265 // - vertex texcoord location = 1
1266 // - vertex normal location = 2
1267 // - vertex color location = 3
1268 // - vertex tangent location = 4
1269 // - vertex texcoord2 location = 5
1270 // - vertex boneIds location = 6
1271 // - vertex boneWeights location = 7
1273 // NOTE: If any location is not found, loc point becomes -1
1275 // Load shader locations array
1276 // NOTE: All locations set to -1 (no location)
1277 shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int));
1278 for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1;
1280 // Get handles to GLSL input attribute locations
1281 shader.locs[SHADER_LOC_VERTEX_POSITION] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION);
1282 shader.locs[SHADER_LOC_VERTEX_TEXCOORD01] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD);
1283 shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2);
1284 shader.locs[SHADER_LOC_VERTEX_NORMAL] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL);
1285 shader.locs[SHADER_LOC_VERTEX_TANGENT] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT);
1286 shader.locs[SHADER_LOC_VERTEX_COLOR] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
1287 shader.locs[SHADER_LOC_VERTEX_BONEIDS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS);
1288 shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS);
1289 shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX);
1291 // Get handles to GLSL uniform locations (vertex shader)
1292 shader.locs[SHADER_LOC_MATRIX_MVP] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MVP);
1293 shader.locs[SHADER_LOC_MATRIX_VIEW] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW);
1294 shader.locs[SHADER_LOC_MATRIX_PROJECTION] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION);
1295 shader.locs[SHADER_LOC_MATRIX_MODEL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL);
1296 shader.locs[SHADER_LOC_MATRIX_NORMAL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL);
1297 shader.locs[SHADER_LOC_BONE_MATRICES] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES);
1299 // Get handles to GLSL uniform locations (fragment shader)
1300 shader.locs[SHADER_LOC_COLOR_DIFFUSE] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR);
1301 shader.locs[SHADER_LOC_MAP_DIFFUSE] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0); // SHADER_LOC_MAP_ALBEDO
1302 shader.locs[SHADER_LOC_MAP_SPECULAR] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1); // SHADER_LOC_MAP_METALNESS
1303 shader.locs[SHADER_LOC_MAP_NORMAL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2);
1304 }
1306 return shader;
1307}
1309// Check if a shader is valid (loaded on GPU)
1310bool IsShaderValid(Shader shader)
1311{
1312 return ((shader.id > 0) && // Validate shader id (GPU loaded successfully)
1313 (shader.locs != NULL)); // Validate memory has been allocated for default shader locations
1315 // The following locations are tried to be set automatically (locs[i] >= 0),
1316 // any of them can be checked for validation but the only mandatory one is, afaik, SHADER_LOC_VERTEX_POSITION
1317 // NOTE: Users can also setup manually their own attributes/uniforms and do not used the default raylib ones
1319 // Vertex shader attribute locations (default)
1320 // shader.locs[SHADER_LOC_VERTEX_POSITION] // Set by default internal shader
1321 // shader.locs[SHADER_LOC_VERTEX_TEXCOORD01] // Set by default internal shader
1322 // shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]
1323 // shader.locs[SHADER_LOC_VERTEX_NORMAL]
1324 // shader.locs[SHADER_LOC_VERTEX_TANGENT]
1325 // shader.locs[SHADER_LOC_VERTEX_COLOR] // Set by default internal shader
1327 // Vertex shader uniform locations (default)
1328 // shader.locs[SHADER_LOC_MATRIX_MVP] // Set by default internal shader
1329 // shader.locs[SHADER_LOC_MATRIX_VIEW]
1330 // shader.locs[SHADER_LOC_MATRIX_PROJECTION]
1331 // shader.locs[SHADER_LOC_MATRIX_MODEL]
1332 // shader.locs[SHADER_LOC_MATRIX_NORMAL]
1334 // Fragment shader uniform locations (default)
1335 // shader.locs[SHADER_LOC_COLOR_DIFFUSE] // Set by default internal shader
1336 // shader.locs[SHADER_LOC_MAP_DIFFUSE] // Set by default internal shader
1337 // shader.locs[SHADER_LOC_MAP_SPECULAR]
1338 // shader.locs[SHADER_LOC_MAP_NORMAL]
1339}
1341// Unload shader from GPU memory (VRAM)
1342void UnloadShader(Shader shader)
1343{
1344 if (shader.id != rlGetShaderIdDefault())
1345 {
1346 rlUnloadShaderProgram(shader.id);
1348 // NOTE: If shader loading failed, it should be 0
1349 RL_FREE(shader.locs);
1350 }
1351}
1353// Get shader uniform location
1354int GetShaderLocation(Shader shader, const char *uniformName)
1355{
1356 return rlGetLocationUniform(shader.id, uniformName);
1357}
1359// Get shader attribute location
1360int GetShaderLocationAttrib(Shader shader, const char *attribName)
1361{
1362 return rlGetLocationAttrib(shader.id, attribName);
1363}
1365// Set shader uniform value
1366void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType)
1367{
1368 SetShaderValueV(shader, locIndex, value, uniformType, 1);
1369}
1371// Set shader uniform value vector
1372void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count)
1373{
1374 if (locIndex > -1)
1375 {
1376 rlEnableShader(shader.id);
1377 rlSetUniform(locIndex, value, uniformType, count);
1378 //rlDisableShader(); // Avoid resetting current shader program, in case other uniforms are set
1379 }
1380}
1382// Set shader uniform value (matrix 4x4)
1383void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat)
1384{
1385 if (locIndex > -1)
1386 {
1387 rlEnableShader(shader.id);
1388 rlSetUniformMatrix(locIndex, mat);
1389 //rlDisableShader();
1390 }
1391}
1393// Set shader uniform value for texture
1394void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture)
1395{
1396 if (locIndex > -1)
1397 {
1398 rlEnableShader(shader.id);
1399 rlSetUniformSampler(locIndex, texture.id);
1400 //rlDisableShader();
1401 }
1402}
1404//----------------------------------------------------------------------------------
1405// Module Functions Definition: Screen-space Queries
1406//----------------------------------------------------------------------------------
1408// Get a ray trace from screen position (i.e mouse)
1409Ray GetScreenToWorldRay(Vector2 position, Camera camera)
1410{
1411 Ray ray = GetScreenToWorldRayEx(position, camera, GetScreenWidth(), GetScreenHeight());
1413 return ray;
1414}
1416// Get a ray trace from the screen position (i.e mouse) within a specific section of the screen
1417Ray GetScreenToWorldRayEx(Vector2 position, Camera camera, int width, int height)
1418{
1419 Ray ray = { 0 };
1421 // Calculate normalized device coordinates
1422 // NOTE: y value is negative
1423 float x = (2.0f*position.x)/(float)width - 1.0f;
1424 float y = 1.0f - (2.0f*position.y)/(float)height;
1425 float z = 1.0f;
1427 // Store values in a vector
1428 Vector3 deviceCoords = { x, y, z };
1430 // Calculate view matrix from camera look at
1431 Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
1433 Matrix matProj = MatrixIdentity();
1435 if (camera.projection == CAMERA_PERSPECTIVE)
1436 {
1437 // Calculate projection matrix from perspective
1438 matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)width/(double)height), rlGetCullDistanceNear(), rlGetCullDistanceFar());
1439 }
1440 else if (camera.projection == CAMERA_ORTHOGRAPHIC)
1441 {
1442 double aspect = (double)width/(double)height;
1443 double top = camera.fovy/2.0;
1444 double right = top*aspect;
1446 // Calculate projection matrix from orthographic
1447 matProj = MatrixOrtho(-right, right, -top, top, rlGetCullDistanceNear(), rlGetCullDistanceFar());
1448 }
1450 // Unproject far/near points
1451 Vector3 nearPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 0.0f }, matProj, matView);
1452 Vector3 farPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView);
1454 // Unproject the mouse cursor in the near plane
1455 // We need this as the source position because orthographic projects,
1456 // compared to perspective doesn't have a convergence point,
1457 // meaning that the "eye" of the camera is more like a plane than a point
1458 Vector3 cameraPlanePointerPos = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, -1.0f }, matProj, matView);
1460 // Calculate normalized direction vector
1461 Vector3 direction = Vector3Normalize(Vector3Subtract(farPoint, nearPoint));
1463 if (camera.projection == CAMERA_PERSPECTIVE) ray.position = camera.position;
1464 else if (camera.projection == CAMERA_ORTHOGRAPHIC) ray.position = cameraPlanePointerPos;
1466 // Apply calculated vectors to ray
1467 ray.direction = direction;
1469 return ray;
1470}
1472// Get transform matrix for camera
1473Matrix GetCameraMatrix(Camera camera)
1474{
1475 Matrix mat = MatrixLookAt(camera.position, camera.target, camera.up);
1477 return mat;
1478}
1480// Get camera 2d transform matrix
1481Matrix GetCameraMatrix2D(Camera2D camera)
1482{
1483 Matrix matTransform = { 0 };
1484 // The camera in world-space is set by
1485 // 1. Move it to target
1486 // 2. Rotate by -rotation and scale by (1/zoom)
1487 // When setting higher scale, it's more intuitive for the world to become bigger (= camera become smaller),
1488 // not for the camera getting bigger, hence the invert. Same deal with rotation
1489 // 3. Move it by (-offset);
1490 // Offset defines target transform relative to screen, but since we're effectively "moving" screen (camera)
1491 // we need to do it into opposite direction (inverse transform)
1493 // Having camera transform in world-space, inverse of it gives the modelview transform
1494 // Since (A*B*C)' = C'*B'*A', the modelview is
1495 // 1. Move to offset
1496 // 2. Rotate and Scale
1497 // 3. Move by -target
1498 Matrix matOrigin = MatrixTranslate(-camera.target.x, -camera.target.y, 0.0f);
1499 Matrix matRotation = MatrixRotate((Vector3){ 0.0f, 0.0f, 1.0f }, camera.rotation*DEG2RAD);
1500 Matrix matScale = MatrixScale(camera.zoom, camera.zoom, 1.0f);
1501 Matrix matTranslation = MatrixTranslate(camera.offset.x, camera.offset.y, 0.0f);
1503 matTransform = MatrixMultiply(MatrixMultiply(matOrigin, MatrixMultiply(matScale, matRotation)), matTranslation);
1505 return matTransform;
1506}
1508// Get the screen space position from a 3d world space position
1509Vector2 GetWorldToScreen(Vector3 position, Camera camera)
1510{
1511 Vector2 screenPosition = GetWorldToScreenEx(position, camera, GetScreenWidth(), GetScreenHeight());
1513 return screenPosition;
1514}
1516// Get size position for a 3d world space position (useful for texture drawing)
1517Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int height)
1518{
1519 // Calculate projection matrix (from perspective instead of frustum
1520 Matrix matProj = MatrixIdentity();
1522 if (camera.projection == CAMERA_PERSPECTIVE)
1523 {
1524 // Calculate projection matrix from perspective
1525 matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)width/(double)height), rlGetCullDistanceNear(), rlGetCullDistanceFar());
1526 }
1527 else if (camera.projection == CAMERA_ORTHOGRAPHIC)
1528 {
1529 double aspect = (double)width/(double)height;
1530 double top = camera.fovy/2.0;
1531 double right = top*aspect;
1533 // Calculate projection matrix from orthographic
1534 matProj = MatrixOrtho(-right, right, -top, top, rlGetCullDistanceNear(), rlGetCullDistanceFar());
1535 }
1537 // Calculate view matrix from camera look at (and transpose it)
1538 Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
1540 // Convert world position vector to quaternion
1541 Quaternion worldPos = { position.x, position.y, position.z, 1.0f };
1543 // Transform world position to view
1544 worldPos = QuaternionTransform(worldPos, matView);
1546 // Transform result to projection (clip space position)
1547 worldPos = QuaternionTransform(worldPos, matProj);
1549 // Calculate normalized device coordinates (inverted y)
1550 Vector3 ndcPos = { worldPos.x/worldPos.w, -worldPos.y/worldPos.w, worldPos.z/worldPos.w };
1552 // Calculate 2d screen position vector
1553 Vector2 screenPosition = { (ndcPos.x + 1.0f)/2.0f*(float)width, (ndcPos.y + 1.0f)/2.0f*(float)height };
1555 return screenPosition;
1556}
1558// Get the screen space position for a 2d camera world space position
1559Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera)
1560{
1561 Matrix matCamera = GetCameraMatrix2D(camera);
1562 Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, matCamera);
1564 return (Vector2){ transform.x, transform.y };
1565}
1567// Get the world space position for a 2d camera screen space position
1568Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera)
1569{
1570 Matrix invMatCamera = MatrixInvert(GetCameraMatrix2D(camera));
1571 Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, invMatCamera);
1573 return (Vector2){ transform.x, transform.y };
1574}
1576//----------------------------------------------------------------------------------
1577// Module Functions Definition: Timming
1578//----------------------------------------------------------------------------------
1580// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
1581//double GetTime(void)
1583// Set target FPS (maximum)
1584void SetTargetFPS(int fps)
1585{
1586 if (fps < 1) CORE.Time.target = 0.0;
1587 else CORE.Time.target = 1.0/(double)fps;
1589 TRACELOG(LOG_INFO, "TIMER: Target time per frame: %02.03f milliseconds", (float)CORE.Time.target*1000.0f);
1590}
1592// Get current FPS
1593// NOTE: We calculate an average framerate
1594int GetFPS(void)
1595{
1596 int fps = 0;
1598#if !defined(SUPPORT_CUSTOM_FRAME_CONTROL)
1599 #define FPS_CAPTURE_FRAMES_COUNT 30 // 30 captures
1600 #define FPS_AVERAGE_TIME_SECONDS 0.5f // 500 milliseconds
1601 #define FPS_STEP (FPS_AVERAGE_TIME_SECONDS/FPS_CAPTURE_FRAMES_COUNT)
1603 static int index = 0;
1604 static float history[FPS_CAPTURE_FRAMES_COUNT] = { 0 };
1605 static float average = 0, last = 0;
1606 float fpsFrame = GetFrameTime();
1608 // if we reset the window, reset the FPS info
1609 if (CORE.Time.frameCounter == 0)
1610 {
1611 average = 0;
1612 last = 0;
1613 index = 0;
1615 for (int i = 0; i < FPS_CAPTURE_FRAMES_COUNT; i++) history[i] = 0;
1616 }
1618 if (fpsFrame == 0) return 0;
1620 if ((GetTime() - last) > FPS_STEP)
1621 {
1622 last = (float)GetTime();
1623 index = (index + 1)%FPS_CAPTURE_FRAMES_COUNT;
1624 average -= history[index];
1625 history[index] = fpsFrame/FPS_CAPTURE_FRAMES_COUNT;
1626 average += history[index];
1627 }
1629 fps = (int)roundf(1.0f/average);
1630#endif
1632 return fps;
1633}
1635// Get time in seconds for last frame drawn (delta time)
1636float GetFrameTime(void)
1637{
1638 return (float)CORE.Time.frame;
1639}
1641//----------------------------------------------------------------------------------
1642// Module Functions Definition: Custom frame control
1643//----------------------------------------------------------------------------------
1645// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
1646//void SwapScreenBuffer(void);
1647//void PollInputEvents(void);
1649// Wait for some time (stop program execution)
1650// NOTE: Sleep() granularity could be around 10 ms, it means, Sleep() could
1651// take longer than expected... for that reason we use the busy wait loop
1652// REF: http://stackoverflow.com/questions/43057578/c-programming-win32-games-sleep-taking-longer-than-expected
1653// REF: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timing on Win32!
1654void WaitTime(double seconds)
1655{
1656 if (seconds < 0) return; // Security check
1658#if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
1659 double destinationTime = GetTime() + seconds;
1660#endif
1662#if defined(SUPPORT_BUSY_WAIT_LOOP)
1663 while (GetTime() < destinationTime) { }
1664#else
1665 #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
1666 double sleepSeconds = seconds - seconds*0.05; // NOTE: We reserve a percentage of the time for busy waiting
1667 #else
1668 double sleepSeconds = seconds;
1669 #endif
1671 // System halt functions
1672 #if defined(_WIN32)
1673 Sleep((unsigned long)(sleepSeconds*1000.0));
1674 #endif
1675 #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
1676 struct timespec req = { 0 };
1677 time_t sec = sleepSeconds;
1678 long nsec = (sleepSeconds - sec)*1000000000L;
1679 req.tv_sec = sec;
1680 req.tv_nsec = nsec;
1682 // NOTE: Use nanosleep() on Unix platforms... usleep() it's deprecated
1683 while (nanosleep(&req, &req) == -1) continue;
1684 #endif
1685 #if defined(__APPLE__)
1686 usleep(sleepSeconds*1000000.0);
1687 #endif
1689 #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
1690 while (GetTime() < destinationTime) { }
1691 #endif
1692#endif
1693}
1695//----------------------------------------------------------------------------------
1696// Module Functions Definition: Misc
1697//----------------------------------------------------------------------------------
1699// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
1700//void OpenURL(const char *url)
1702// Set the seed for the random number generator
1703void SetRandomSeed(unsigned int seed)
1704{
1705#if defined(SUPPORT_RPRAND_GENERATOR)
1706 rprand_set_seed(seed);
1707#else
1708 srand(seed);
1709#endif
1710}
1712// Get a random value between min and max included
1713int GetRandomValue(int min, int max)
1714{
1715 int value = 0;
1717 if (min > max)
1718 {
1719 int tmp = max;
1720 max = min;
1721 min = tmp;
1722 }
1724#if defined(SUPPORT_RPRAND_GENERATOR)
1725 value = rprand_get_value(min, max);
1726#else
1727 // WARNING: Ranges higher than RAND_MAX will return invalid results
1728 // More specifically, if (max - min) > INT_MAX there will be an overflow,
1729 // and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold
1730 // NOTE: Depending on the library it can be as low as 32767
1731 if ((unsigned int)(max - min) > (unsigned int)RAND_MAX)
1732 {
1733 TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX);
1734 }
1736 // NOTE: This one-line approach produces a non-uniform distribution,
1737 // as stated by Donald Knuth in the book The Art of Programming, so
1738 // using below approach for more uniform results
1739 //value = (rand()%(abs(max - min) + 1) + min);
1741 // More uniform range solution
1742 int range = (max - min) + 1;
1744 // Degenerate/overflow case: fall back to min (same behavior as "always min" instead of UB)
1745 if (range <= 0) value = min;
1746 else
1747 {
1748 // Rejection sampling to get a uniform integer in [min, max]
1749 unsigned long c = (unsigned long)RAND_MAX + 1UL; // number of possible rand() results
1750 unsigned long m = (unsigned long)range; // size of the target interval
1751 unsigned long t = c - (c%m); // largest multiple of m <= c
1752 unsigned long r = 0;
1754 for (;;)
1755 {
1756 r = (unsigned long)rand();
1757 if (r < t) break; // Only accept values within the fair region
1758 }
1760 value = min + (int)(r%m);
1761 }
1762#endif
1763 return value;
1764}
1766// Load random values sequence, no values repeated, min and max included
1767int *LoadRandomSequence(unsigned int count, int min, int max)
1768{
1769 int *values = NULL;
1771#if defined(SUPPORT_RPRAND_GENERATOR)
1772 values = rprand_load_sequence(count, min, max);
1773#else
1774 if (count > ((unsigned int)abs(max - min) + 1)) return values; // Security check
1776 values = (int *)RL_CALLOC(count, sizeof(int));
1778 int value = 0;
1779 bool dupValue = false;
1781 for (int i = 0; i < (int)count;)
1782 {
1783 value = GetRandomValue(min, max);
1784 dupValue = false;
1786 for (int j = 0; j < i; j++)
1787 {
1788 if (values[j] == value)
1789 {
1790 dupValue = true;
1791 break;
1792 }
1793 }
1795 if (!dupValue)
1796 {
1797 values[i] = value;
1798 i++;
1799 }
1800 }
1801#endif
1802 return values;
1803}
1805// Unload random values sequence
1806void UnloadRandomSequence(int *sequence)
1807{
1808#if defined(SUPPORT_RPRAND_GENERATOR)
1809 rprand_unload_sequence(sequence);
1810#else
1811 RL_FREE(sequence);
1812#endif
1813}
1815// Takes a screenshot of current screen
1816// NOTE: Provided fileName should not contain paths, saving to working directory
1817void TakeScreenshot(const char *fileName)
1818{
1819#if defined(SUPPORT_MODULE_RTEXTURES)
1820 // Security check to (partially) avoid malicious code
1821 if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; }
1823 // Apply a scale if we are doing HIGHDPI auto-scaling
1824 Vector2 scale = { 1.0f, 1.0f };
1825 if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) scale = GetWindowScaleDPI();
1827 unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
1828 Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
1830 char path[MAX_FILEPATH_LENGTH] = { 0 };
1831 strncpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName), MAX_FILEPATH_LENGTH - 1);
1833 ExportImage(image, path); // WARNING: Module required: rtextures
1834 RL_FREE(imgData);
1836 if (FileExists(path)) TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path);
1837 else TRACELOG(LOG_WARNING, "SYSTEM: [%s] Screenshot could not be saved", path);
1838#else
1839 TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures");
1840#endif
1841}
1843// Setup window configuration flags (view FLAGS)
1844// NOTE: This function is expected to be called before window creation,
1845// because it sets up some flags for the window creation process
1846// To configure window states after creation, just use SetWindowState()
1847void SetConfigFlags(unsigned int flags)
1848{
1849 if (CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetConfigFlags called after window initialization, Use \"SetWindowState\" to set flags instead");
1851 // Selected flags are set but not evaluated at this point,
1852 // flag evaluation happens at InitWindow() or SetWindowState()
1853 FLAG_SET(CORE.Window.flags, flags);
1854}
1856//----------------------------------------------------------------------------------
1857// Module Functions Definition: File system
1858//----------------------------------------------------------------------------------
1860// Rename file (if exists)
1861// NOTE: Only rename file name required, not full path
1862int FileRename(const char *fileName, const char *fileRename)
1863{
1864 int result = 0;
1866 if (FileExists(fileName))
1867 {
1868 result = rename(fileName, fileRename);
1869 }
1870 else result = -1;
1872 return result;
1873}
1875// Remove file (if exists)
1876int FileRemove(const char *fileName)
1877{
1878 int result = 0;
1880 if (FileExists(fileName))
1881 {
1882 result = remove(fileName);
1883 }
1884 else result = -1;
1886 return result;
1887}
1889// Copy file from one path to another
1890// NOTE: If destination path does not exist, it is created!
1891int FileCopy(const char *srcPath, const char *dstPath)
1892{
1893 int result = 0;
1894 int srcDataSize = 0;
1895 unsigned char *srcFileData = LoadFileData(srcPath, &srcDataSize);
1897 // Create required paths if they do not exist
1898 if (!DirectoryExists(GetDirectoryPath(dstPath)))
1899 result = MakeDirectory(GetDirectoryPath(dstPath));
1901 if (result == 0) // Directory created successfully (or already exists)
1902 {
1903 if ((srcFileData != NULL) && (srcDataSize > 0))
1904 result = SaveFileData(dstPath, srcFileData, srcDataSize);
1905 }
1907 UnloadFileData(srcFileData);
1909 return result;
1910}
1912// Move file from one directory to another
1913// NOTE: If dst directories do not exists they are created
1914int FileMove(const char *srcPath, const char *dstPath)
1915{
1916 int result = 0;
1918 if (FileExists(srcPath))
1919 {
1920 FileCopy(srcPath, dstPath);
1921 FileRemove(srcPath);
1922 }
1923 else result = -1;
1925 return result;
1926}
1928// Replace text in an existing file
1929// WARNING: DEPENDENCY: [rtext] module
1930int FileTextReplace(const char *fileName, const char *search, const char *replacement)
1931{
1932 int result = 0;
1933 char *fileText = NULL;
1934 char *fileTextUpdated = { 0 };
1936#if defined(SUPPORT_MODULE_RTEXT)
1937 if (FileExists(fileName))
1938 {
1939 fileText = LoadFileText(fileName);
1940 fileTextUpdated = TextReplace(fileText, search, replacement);
1941 result = SaveFileText(fileName, fileTextUpdated);
1942 MemFree(fileTextUpdated);
1943 UnloadFileText(fileText);
1944 }
1945#else
1946 TRACELOG(LOG_WARNING, "FILE: File text replace requires [rtext] module");
1947#endif
1949 return result;
1950}
1952// Find text index position in existing file
1953// WARNING: DEPENDENCY: [rtext] module
1954int FileTextFindIndex(const char *fileName, const char *search)
1955{
1956 int result = -1;
1958 if (FileExists(fileName))
1959 {
1960 char *fileText = LoadFileText(fileName);
1961 char *ptr = strstr(fileText, search);
1962 if (ptr != NULL) result = (int)(ptr - fileText);
1963 UnloadFileText(fileText);
1964 }
1966 return result;
1967}
1969// Check if the file exists
1970bool FileExists(const char *fileName)
1971{
1972 bool result = false;
1974#if defined(_WIN32)
1975 if (_access(fileName, 0) != -1) result = true;
1976#else
1977 if (access(fileName, F_OK) != -1) result = true;
1978#endif
1980 // NOTE: Alternatively, stat() can be used instead of access()
1981 //#include <sys/stat.h>
1982 //struct stat statbuf;
1983 //if (stat(filename, &statbuf) == 0) result = true;
1985 return result;
1986}
1988// Check file extension
1989bool IsFileExtension(const char *fileName, const char *ext)
1990{
1991 #define MAX_FILE_EXTENSIONS 32
1993 bool result = false;
1994 const char *fileExt = GetFileExtension(fileName);
1996 // WARNING: fileExt points to last '.' on fileName string but it could happen
1997 // that fileName is not correct: "myfile.png more text following\n"
1999 if (fileExt != NULL)
2000 {
2001 int fileExtLength = (int)strlen(fileExt);
2002 char fileExtLower[16] = { 0 };
2003 char *fileExtLowerPtr = fileExtLower;
2004 for (int i = 0; (i < fileExtLength) && (i < 16); i++)
2005 {
2006 // Copy and convert to lower-case
2007 if ((fileExt[i] >= 'A') && (fileExt[i] <= 'Z')) fileExtLower[i] = fileExt[i] + 32;
2008 else fileExtLower[i] = fileExt[i];
2009 }
2011 int extCount = 1;
2012 int extLength = (int)strlen(ext);
2013 char *extList = (char *)RL_CALLOC(extLength + 1, 1);
2014 char *extListPtrs[MAX_FILE_EXTENSIONS] = { 0 };
2015 strncpy(extList, ext, extLength);
2016 extListPtrs[0] = extList;
2018 for (int i = 0; i < extLength; i++)
2019 {
2020 // Convert to lower-case if extension is upper-case
2021 if ((extList[i] >= 'A') && (extList[i] <= 'Z')) extList[i] += 32;
2023 // Get pointer to next extension and add null-terminator
2024 if ((extList[i] == ';') && (extCount < (MAX_FILE_EXTENSIONS - 1)))
2025 {
2026 extList[i] = '\0';
2027 extListPtrs[extCount] = extList + i + 1;
2028 extCount++;
2029 }
2030 }
2032 for (int i = 0; i < extCount; i++)
2033 {
2034 // Consider the case where extension provided
2035 // does not start with the '.'
2036 fileExtLowerPtr = fileExtLower;
2037 if (extListPtrs[i][0] != '.') fileExtLowerPtr++;
2039 if (strcmp(fileExtLowerPtr, extListPtrs[i]) == 0)
2040 {
2041 result = true;
2042 break;
2043 }
2044 }
2046 RL_FREE(extList);
2047 }
2049 return result;
2050}
2052// Check if a directory path exists
2053bool DirectoryExists(const char *dirPath)
2054{
2055 bool result = false;
2056 DIR *dir = opendir(dirPath);
2058 if (dir != NULL)
2059 {
2060 result = true;
2061 closedir(dir);
2062 }
2064 return result;
2065}
2067// Get file length in bytes
2068// NOTE: GetFileSize() conflicts with windows.h
2069int GetFileLength(const char *fileName)
2070{
2071 int size = 0;
2073 // NOTE: On Unix-like systems, it can by used the POSIX system call: stat(),
2074 // but depending on the platform that call could not be available
2075 //struct stat result = { 0 };
2076 //stat(fileName, &result);
2077 //return result.st_size;
2079 FILE *file = fopen(fileName, "rb");
2081 if (file != NULL)
2082 {
2083 fseek(file, 0L, SEEK_END);
2084 long int fileSize = ftell(file);
2086 // Check for size overflow (INT_MAX)
2087 if (fileSize > 2147483647) TRACELOG(LOG_WARNING, "[%s] File size overflows expected limit, do not use GetFileLength()", fileName);
2088 else size = (int)fileSize;
2090 fclose(file);
2091 }
2093 return size;
2094}
2096// Get file modification time (last write time)
2097long GetFileModTime(const char *fileName)
2098{
2099 struct stat result = { 0 };
2100 long modTime = 0;
2102 if (stat(fileName, &result) == 0)
2103 {
2104 time_t mod = result.st_mtime;
2105 modTime = (long)mod;
2106 }
2108 return modTime;
2109}
2111// Get pointer to extension for a filename string (includes the dot: .png)
2112// WARNING: We just get the ptr but not the extension as a separate string
2113const char *GetFileExtension(const char *fileName)
2114{
2115 const char *dot = strrchr(fileName, '.');
2117 if (!dot || (dot == fileName)) return NULL;
2119 return dot;
2120}
2122// String pointer reverse break: returns right-most occurrence of charset in s
2123static const char *strprbrk(const char *text, const char *charset)
2124{
2125 const char *latestMatch = NULL;
2127 for (; (text != NULL) && (text = strpbrk(text, charset)); latestMatch = text++) { }
2129 return latestMatch;
2130}
2132// Get pointer to filename for a path string
2133const char *GetFileName(const char *filePath)
2134{
2135 const char *fileName = NULL;
2137 if (filePath != NULL) fileName = strprbrk(filePath, "\\/");
2139 if (fileName == NULL) return filePath;
2141 return fileName + 1;
2142}
2144// Get filename string without extension (uses static string)
2145const char *GetFileNameWithoutExt(const char *filePath)
2146{
2147 #define MAX_FILENAME_LENGTH 256
2149 static char fileName[MAX_FILENAME_LENGTH] = { 0 };
2150 memset(fileName, 0, MAX_FILENAME_LENGTH);
2152 if (filePath != NULL)
2153 {
2154 strncpy(fileName, GetFileName(filePath), MAX_FILENAME_LENGTH - 1); // Get filename.ext without path
2155 int fileNameLenght = (int)strlen(fileName); // Get size in bytes
2157 for (int i = fileNameLenght; i > 0; i--) // Reverse search '.'
2158 {
2159 if (fileName[i] == '.')
2160 {
2161 // NOTE: We break on first '.' found
2162 fileName[i] = '\0';
2163 break;
2164 }
2165 }
2166 }
2168 return fileName;
2169}
2171// Get directory for a given filePath
2172const char *GetDirectoryPath(const char *filePath)
2173{
2174 /*
2175 // NOTE: Directory separator is different in Windows and other platforms,
2176 // fortunately, Windows also support the '/' separator, that's the one should be used
2177 #if defined(_WIN32)
2178 char separator = '\\';
2179 #else
2180 char separator = '/';
2181 #endif
2182 */
2183 const char *lastSlash = NULL;
2184 static char dirPath[MAX_FILEPATH_LENGTH] = { 0 };
2185 memset(dirPath, 0, MAX_FILEPATH_LENGTH);
2187 // In case provided path does not contain a root drive letter (C:\, D:\) nor leading path separator (\, /),
2188 // we add the current directory path to dirPath
2189 if ((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/'))
2190 {
2191 // For security, we set starting path to current directory,
2192 // obtained path will be concatenated to this
2193 dirPath[0] = '.';
2194 dirPath[1] = '/';
2195 }
2197 lastSlash = strprbrk(filePath, "\\/");
2198 if (lastSlash)
2199 {
2200 if (lastSlash == filePath)
2201 {
2202 // The last and only slash is the leading one: path is in a root directory
2203 dirPath[0] = filePath[0];
2204 dirPath[1] = '\0';
2205 }
2206 else
2207 {
2208 // NOTE: Be careful, strncpy() is not safe, it does not care about '\0'
2209 char *dirPathPtr = dirPath;
2210 if ((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/')) dirPathPtr += 2; // Skip drive letter, "C:"
2211 memcpy(dirPathPtr, filePath, strlen(filePath) - (strlen(lastSlash) - 1));
2212 dirPath[strlen(filePath) - strlen(lastSlash) + (((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/'))? 2 : 0)] = '\0'; // Add '\0' manually
2213 }
2214 }
2216 return dirPath;
2217}
2219// Get previous directory path for a given path
2220const char *GetPrevDirectoryPath(const char *dirPath)
2221{
2222 static char prevDirPath[MAX_FILEPATH_LENGTH] = { 0 };
2223 memset(prevDirPath, 0, MAX_FILEPATH_LENGTH);
2224 int dirPathLength = (int)strlen(dirPath);
2226 if (dirPathLength <= 3) strncpy(prevDirPath, dirPath, MAX_FILEPATH_LENGTH - 1);
2228 for (int i = (dirPathLength - 1); (i >= 0) && (dirPathLength > 3); i--)
2229 {
2230 if ((dirPath[i] == '\\') || (dirPath[i] == '/'))
2231 {
2232 // Check for root: "C:\" or "/"
2233 if (((i == 2) && (dirPath[1] ==':')) || (i == 0)) i++;
2235 strncpy(prevDirPath, dirPath, i);
2236 break;
2237 }
2238 }
2240 return prevDirPath;
2241}
2243// Get current working directory
2244const char *GetWorkingDirectory(void)
2245{
2246 static char currentDir[MAX_FILEPATH_LENGTH] = { 0 };
2247 memset(currentDir, 0, MAX_FILEPATH_LENGTH);
2249 char *path = GETCWD(currentDir, MAX_FILEPATH_LENGTH - 1);
2251 return path;
2252}
2254const char *GetApplicationDirectory(void)
2255{
2256 static char appDir[MAX_FILEPATH_LENGTH] = { 0 };
2257 memset(appDir, 0, MAX_FILEPATH_LENGTH);
2259#if defined(_WIN32)
2260 int len = 0;
2262 #if defined(UNICODE)
2263 unsigned short widePath[MAX_PATH];
2264 len = GetModuleFileNameW(NULL, (wchar_t *)widePath, MAX_PATH);
2265 len = WideCharToMultiByte(0, 0, (wchar_t *)widePath, len, appDir, MAX_PATH, NULL, NULL);
2266 #else
2267 len = GetModuleFileNameA(NULL, appDir, MAX_PATH);
2268 #endif
2270 if (len > 0)
2271 {
2272 for (int i = len; i >= 0; --i)
2273 {
2274 if (appDir[i] == '\\')
2275 {
2276 appDir[i + 1] = '\0';
2277 break;
2278 }
2279 }
2280 }
2281 else
2282 {
2283 appDir[0] = '.';
2284 appDir[1] = '\\';
2285 }
2287#elif defined(__linux__)
2289 unsigned int size = sizeof(appDir);
2290 ssize_t len = readlink("/proc/self/exe", appDir, size);
2292 if (len > 0)
2293 {
2294 for (int i = len; i >= 0; --i)
2295 {
2296 if (appDir[i] == '/')
2297 {
2298 appDir[i + 1] = '\0';
2299 break;
2300 }
2301 }
2302 }
2303 else
2304 {
2305 appDir[0] = '.';
2306 appDir[1] = '/';
2307 }
2309#elif defined(__APPLE__)
2311 uint32_t size = sizeof(appDir);
2313 if (_NSGetExecutablePath(appDir, &size) == 0)
2314 {
2315 int appDirLength = (int)strlen(appDir);
2316 for (int i = appDirLength; i >= 0; --i)
2317 {
2318 if (appDir[i] == '/')
2319 {
2320 appDir[i + 1] = '\0';
2321 break;
2322 }
2323 }
2324 }
2325 else
2326 {
2327 appDir[0] = '.';
2328 appDir[1] = '/';
2329 }
2331#elif defined(__FreeBSD__)
2333 size_t size = sizeof(appDir);
2334 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
2336 if (sysctl(mib, 4, appDir, &size, NULL, 0) == 0)
2337 {
2338 int appDirLength = (int)strlen(appDir);
2339 for (int i = appDirLength; i >= 0; --i)
2340 {
2341 if (appDir[i] == '/')
2342 {
2343 appDir[i + 1] = '\0';
2344 break;
2345 }
2346 }
2347 }
2348 else
2349 {
2350 appDir[0] = '.';
2351 appDir[1] = '/';
2352 }
2353#endif
2355 return appDir;
2356}
2358// Load directory filepaths
2359// NOTE: Base path is prepended to the scanned filepaths
2360// WARNING: Directory is scanned twice, first time to get files count
2361// No recursive scanning is done!
2362FilePathList LoadDirectoryFiles(const char *dirPath)
2363{
2364 FilePathList files = { 0 };
2365 unsigned int fileCounter = 0;
2367 struct dirent *entity;
2368 DIR *dir = opendir(dirPath);
2370 if (dir != NULL) // It's a directory
2371 {
2372 // SCAN 1: Count files
2373 while ((entity = readdir(dir)) != NULL)
2374 {
2375 // NOTE: We skip '.' (current dir) and '..' (parent dir) filepaths
2376 if ((strcmp(entity->d_name, ".") != 0) && (strcmp(entity->d_name, "..") != 0)) fileCounter++;
2377 }
2379 // Memory allocation for dirFileCount
2380 files.capacity = fileCounter;
2381 files.paths = (char **)RL_CALLOC(files.capacity, sizeof(char *));
2382 for (unsigned int i = 0; i < files.capacity; i++) files.paths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
2384 closedir(dir);
2386 // SCAN 2: Read filepaths
2387 // NOTE: Directory paths are also registered
2388 ScanDirectoryFiles(dirPath, &files, NULL);
2390 // Security check: read files.count should match fileCounter
2391 if (files.count != files.capacity) TRACELOG(LOG_WARNING, "FILEIO: Read files count do not match capacity allocated");
2392 }
2393 else TRACELOG(LOG_WARNING, "FILEIO: Failed to open requested directory"); // Maybe it's a file...
2395 return files;
2396}
2398// Load directory filepaths with extension filtering and recursive directory scan
2399// NOTE: On recursive loading we do not pre-scan for file count, we use MAX_FILEPATH_CAPACITY
2400FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs)
2401{
2402 FilePathList files = { 0 };
2404 files.capacity = MAX_FILEPATH_CAPACITY;
2405 files.paths = (char **)RL_CALLOC(files.capacity, sizeof(char *));
2406 for (unsigned int i = 0; i < files.capacity; i++) files.paths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
2408 // WARNING: basePath is always prepended to scanned paths
2409 if (scanSubdirs) ScanDirectoryFilesRecursively(basePath, &files, filter);
2410 else ScanDirectoryFiles(basePath, &files, filter);
2412 return files;
2413}
2415// Unload directory filepaths
2416// WARNING: files.count is not reseted to 0 after unloading
2417void UnloadDirectoryFiles(FilePathList files)
2418{
2419 if (files.paths != NULL)
2420 {
2421 for (unsigned int i = 0; i < files.capacity; i++) RL_FREE(files.paths[i]);
2423 RL_FREE(files.paths);
2424 }
2425}
2427// Create directories (including full path requested), returns 0 on success
2428int MakeDirectory(const char *dirPath)
2429{
2430 if ((dirPath == NULL) || (dirPath[0] == '\0')) return -1; // Path is not valid
2431 if (DirectoryExists(dirPath)) return 0; // Path already exists (is valid)
2433 // Copy path string to avoid modifying original
2434 int dirPathLength = (int)strlen(dirPath) + 1;
2435 char *pathcpy = (char *)RL_CALLOC(dirPathLength, 1);
2436 memcpy(pathcpy, dirPath, dirPathLength);
2438 // Iterate over pathcpy, create each subdirectory as needed
2439 for (int i = 0; (i < dirPathLength) && (pathcpy[i] != '\0'); i++)
2440 {
2441 if (pathcpy[i] == ':') i++;
2442 else
2443 {
2444 if ((pathcpy[i] == '\\') || (pathcpy[i] == '/'))
2445 {
2446 pathcpy[i] = '\0';
2447 if (!DirectoryExists(pathcpy)) MKDIR(pathcpy);
2448 pathcpy[i] = '/';
2449 }
2450 }
2451 }
2453 // Create final directory
2454 if (!DirectoryExists(pathcpy)) MKDIR(pathcpy);
2455 RL_FREE(pathcpy);
2457 // In case something failed and requested directory
2458 // was not successfully created, return -1
2459 if (!DirectoryExists(dirPath)) return -1;
2461 return 0;
2462}
2464// Change working directory, returns true on success
2465bool ChangeDirectory(const char *dirPath)
2466{
2467 bool result = CHDIR(dirPath);
2469 if (result != 0) TRACELOG(LOG_WARNING, "SYSTEM: Failed to change to directory: %s", dirPath);
2470 else TRACELOG(LOG_INFO, "SYSTEM: Working Directory: %s", dirPath);
2472 return (result == 0);
2473}
2475// Check if a given path point to a file
2476bool IsPathFile(const char *path)
2477{
2478 struct stat result = { 0 };
2479 stat(path, &result);
2481 return S_ISREG(result.st_mode);
2482}
2484// Check if fileName is valid for the platform/OS
2485bool IsFileNameValid(const char *fileName)
2486{
2487 bool valid = true;
2489 if ((fileName != NULL) && (fileName[0] != '\0'))
2490 {
2491 int fileNameLength = (int)strlen(fileName);
2492 bool allPeriods = true;
2494 for (int i = 0; i < fileNameLength; i++)
2495 {
2496 // Check invalid characters
2497 if ((fileName[i] == '<') ||
2498 (fileName[i] == '>') ||
2499 (fileName[i] == ':') ||
2500 (fileName[i] == '\"') ||
2501 (fileName[i] == '/') ||
2502 (fileName[i] == '\\') ||
2503 (fileName[i] == '|') ||
2504 (fileName[i] == '?') ||
2505 (fileName[i] == '*')) { valid = false; break; }
2507 // Check non-glyph characters
2508 if ((unsigned char)fileName[i] < 32) { valid = false; break; }
2510 // Check if filename is not all periods
2511 if (fileName[i] != '.') allPeriods = false;
2512 }
2514 if (allPeriods) valid = false;
2516/*
2517 if (valid)
2518 {
2519 // Check invalid DOS names
2520 if (fileNameLength >= 3)
2521 {
2522 if (((fileName[0] == 'C') && (fileName[1] == 'O') && (fileName[2] == 'N')) || // CON
2523 ((fileName[0] == 'P') && (fileName[1] == 'R') && (fileName[2] == 'N')) || // PRN
2524 ((fileName[0] == 'A') && (fileName[1] == 'U') && (fileName[2] == 'X')) || // AUX
2525 ((fileName[0] == 'N') && (fileName[1] == 'U') && (fileName[2] == 'L'))) valid = false; // NUL
2526 }
2528 if (fileNameLength >= 4)
2529 {
2530 if (((fileName[0] == 'C') && (fileName[1] == 'O') && (fileName[2] == 'M') && ((fileName[3] >= '0') && (fileName[3] <= '9'))) || // COM0-9
2531 ((fileName[0] == 'L') && (fileName[1] == 'P') && (fileName[2] == 'T') && ((fileName[3] >= '0') && (fileName[3] <= '9')))) valid = false; // LPT0-9
2532 }
2533 }
2534*/
2535 }
2537 return valid;
2538}
2540// Check if a file has been dropped into window
2541bool IsFileDropped(void)
2542{
2543 bool result = false;
2545 if (CORE.Window.dropFileCount > 0) result = true;
2547 return result;
2548}
2550// Load dropped filepaths
2551FilePathList LoadDroppedFiles(void)
2552{
2553 FilePathList files = { 0 };
2555 files.count = CORE.Window.dropFileCount;
2556 files.paths = CORE.Window.dropFilepaths;
2558 return files;
2559}
2561// Unload dropped filepaths
2562void UnloadDroppedFiles(FilePathList files)
2563{
2564 // WARNING: files pointers are the same as internal ones
2566 if (files.count > 0)
2567 {
2568 for (unsigned int i = 0; i < files.count; i++) RL_FREE(files.paths[i]);
2570 RL_FREE(files.paths);
2572 CORE.Window.dropFileCount = 0;
2573 CORE.Window.dropFilepaths = NULL;
2574 }
2575}
2577//----------------------------------------------------------------------------------
2578// Module Functions Definition: Compression and Encoding
2579//----------------------------------------------------------------------------------
2581// Compress data (DEFLATE algorithm)
2582unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize)
2583{
2584 #define COMPRESSION_QUALITY_DEFLATE 8
2586 unsigned char *compData = NULL;
2588#if defined(SUPPORT_COMPRESSION_API)
2589 // Compress data and generate a valid DEFLATE stream
2590 struct sdefl *sdefl = (struct sdefl *)RL_CALLOC(1, sizeof(struct sdefl)); // WARNING: Possible stack overflow, struct sdefl is almost 1MB
2591 int bounds = sdefl_bound(dataSize);
2592 compData = (unsigned char *)RL_CALLOC(bounds, 1);
2594 *compDataSize = sdeflate(sdefl, compData, data, dataSize, COMPRESSION_QUALITY_DEFLATE); // Compression level 8, same as stbiw
2595 RL_FREE(sdefl);
2597 TRACELOG(LOG_INFO, "SYSTEM: Compress data: Original size: %i -> Comp. size: %i", dataSize, *compDataSize);
2598#endif
2600 return compData;
2601}
2603// Decompress data (DEFLATE algorithm)
2604unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize)
2605{
2606 unsigned char *data = NULL;
2608#if defined(SUPPORT_COMPRESSION_API)
2609 // Decompress data from a valid DEFLATE stream
2610 unsigned char *data0 = (unsigned char *)RL_CALLOC(MAX_DECOMPRESSION_SIZE*1024*1024, 1);
2611 int size = sinflate(data0, MAX_DECOMPRESSION_SIZE*1024*1024, compData, compDataSize);
2613 // WARNING: RL_REALLOC can make (and leave) data copies in memory,
2614 // that can be a security concern in case of compression of sensitive data
2615 // So, we use a second buffer to copy data manually, wiping original buffer memory
2616 data = (unsigned char *)RL_CALLOC(size, 1);
2617 memcpy(data, data0, size);
2618 memset(data0, 0, MAX_DECOMPRESSION_SIZE*1024*1024); // Wipe memory, is memset() safe?
2619 RL_FREE(data0);
2621 TRACELOG(LOG_INFO, "SYSTEM: Decompress data: Comp. size: %i -> Original size: %i", compDataSize, size);
2623 *dataSize = size;
2624#endif
2626 return data;
2627}
2629// Encode data to Base64 string
2630// NOTE: Returned string includes NULL terminator, considered on outputSize
2631char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize)
2632{
2633 // Base64 conversion table from RFC 4648 [0..63]
2634 // NOTE: They represent 64 values (6 bits), to encode 3 bytes of data into 4 "sixtets" (6bit characters)
2635 static const char base64EncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2637 // Compute expected size and padding
2638 int paddedSize = dataSize;
2639 while (paddedSize%3 != 0) paddedSize++; // Padding bytes to round 4*(dataSize/3) to 4 bytes
2640 int estimatedOutputSize = 4*(paddedSize/3);
2641 int padding = paddedSize - dataSize;
2643 // Adding null terminator to string
2644 estimatedOutputSize += 1;
2646 // Load some memory to store encoded string
2647 char *encodedData = (char *)RL_CALLOC(estimatedOutputSize, 1);
2648 if (encodedData == NULL) return NULL;
2650 int outputCount = 0;
2651 for (int i = 0; i < dataSize;)
2652 {
2653 unsigned int octetA = 0;
2654 unsigned int octetB = 0;
2655 unsigned int octetC = 0;
2656 unsigned int octetPack = 0;
2658 octetA = data[i]; // Generates 2 sextets
2659 octetB = ((i + 1) < dataSize)? data[i + 1] : 0; // Generates 3 sextets
2660 octetC = ((i + 2) < dataSize)? data[i + 2] : 0; // Generates 4 sextets
2662 octetPack = (octetA << 16) | (octetB << 8) | octetC;
2664 encodedData[outputCount + 0] = (unsigned char)(base64EncodeTable[(octetPack >> 18) & 0x3f]);
2665 encodedData[outputCount + 1] = (unsigned char)(base64EncodeTable[(octetPack >> 12) & 0x3f]);
2666 encodedData[outputCount + 2] = (unsigned char)(base64EncodeTable[(octetPack >> 6) & 0x3f]);
2667 encodedData[outputCount + 3] = (unsigned char)(base64EncodeTable[octetPack & 0x3f]);
2668 outputCount += 4;
2669 i += 3;
2670 }
2672 // Add required padding bytes
2673 for (int p = 0; p < padding; p++) encodedData[outputCount - p - 1] = '=';
2675 // Add null terminator to string
2676 encodedData[outputCount] = '\0';
2677 outputCount++;
2679 if (outputCount != estimatedOutputSize) TRACELOG(LOG_WARNING, "BASE64: Output size differs from estimation");
2681 *outputSize = estimatedOutputSize;
2682 return encodedData;
2683}
2685// Decode Base64 string (expected NULL terminated)
2686unsigned char *DecodeDataBase64(const char *text, int *outputSize)
2687{
2688 // Base64 decode table
2689 // NOTE: Following ASCII order [0..255] assigning the expected sixtet value to
2690 // every character in the corresponding ASCII position
2691 static const unsigned char base64DecodeTable[256] = {
2692 ['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, ['E'] = 4, ['F'] = 5, ['G'] = 6, ['H'] = 7,
2693 ['I'] = 8, ['J'] = 9, ['K'] = 10, ['L'] = 11, ['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15,
2694 ['Q'] = 16, ['R'] = 17, ['S'] = 18, ['T'] = 19, ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23, ['Y'] = 24, ['Z'] = 25,
2695 ['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29, ['e'] = 30, ['f'] = 31, ['g'] = 32, ['h'] = 33,
2696 ['i'] = 34, ['j'] = 35, ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39, ['o'] = 40, ['p'] = 41,
2697 ['q'] = 42, ['r'] = 43, ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47, ['w'] = 48, ['x'] = 49, ['y'] = 50, ['z'] = 51,
2698 ['0'] = 52, ['1'] = 53, ['2'] = 54, ['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59,
2699 ['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63
2700 };
2702 *outputSize = 0;
2703 if (text == NULL) return NULL;
2705 // Compute expected size and padding
2706 int dataSize = (int)strlen(text); // WARNING: Expecting NULL terminated strings!
2707 int ending = dataSize - 1;
2708 int padding = 0;
2709 while (text[ending] == '=') { padding++; ending--; }
2710 int estimatedOutputSize = 3*(dataSize/4) - padding;
2711 int maxOutputSize = 3*(dataSize/4);
2713 // Load some memory to store decoded data
2714 // NOTE: Allocated enough size to include padding
2715 unsigned char *decodedData = (unsigned char *)RL_CALLOC(maxOutputSize, 1);
2716 if (decodedData == NULL) return NULL;
2718 int outputCount = 0;
2719 for (int i = 0; i < dataSize;)
2720 {
2721 // Every 4 sixtets must generate 3 octets
2722 if ((i + 2) >= dataSize)
2723 {
2724 TRACELOG(LOG_WARNING, "BASE64: Decoding error: Input data size is not valid");
2725 break;
2726 }
2728 unsigned int sixtetA = base64DecodeTable[(unsigned char)text[i]];
2729 unsigned int sixtetB = base64DecodeTable[(unsigned char)text[i + 1]];
2730 unsigned int sixtetC = (((i + 2) < dataSize) && (unsigned char)text[i + 2] != '=')? base64DecodeTable[(unsigned char)text[i + 2]] : 0;
2731 unsigned int sixtetD = (((i + 3) < dataSize) && (unsigned char)text[i + 3] != '=')? base64DecodeTable[(unsigned char)text[i + 3]] : 0;
2733 unsigned int octetPack = (sixtetA << 18) | (sixtetB << 12) | (sixtetC << 6) | sixtetD;
2735 if ((outputCount + 3) > maxOutputSize)
2736 {
2737 TRACELOG(LOG_WARNING, "BASE64: Decoding error: Output data size is too small");
2738 break;
2739 }
2741 decodedData[outputCount + 0] = (octetPack >> 16) & 0xff;
2742 decodedData[outputCount + 1] = (octetPack >> 8) & 0xff;
2743 decodedData[outputCount + 2] = octetPack & 0xff;
2744 outputCount += 3;
2745 i += 4;
2746 }
2748 if (estimatedOutputSize != (outputCount - padding)) TRACELOG(LOG_WARNING, "BASE64: Decoded size differs from estimation");
2750 *outputSize = estimatedOutputSize;
2751 return decodedData;
2752}
2754// Compute CRC32 hash code
2755unsigned int ComputeCRC32(unsigned char *data, int dataSize)
2756{
2757 static unsigned int crcTable[256] = {
2758 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
2759 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
2760 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
2761 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
2762 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
2763 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
2764 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
2765 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
2766 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
2767 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
2768 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
2769 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
2770 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
2771 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
2772 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
2773 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
2774 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
2775 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
2776 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
2777 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
2778 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
2779 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
2780 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
2781 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
2782 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
2783 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
2784 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
2785 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
2786 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
2787 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
2788 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
2789 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
2790 };
2792 unsigned int crc = ~0u;
2794 for (int i = 0; i < dataSize; i++) crc = (crc >> 8) ^ crcTable[data[i] ^ (crc & 0xff)];
2796 return ~crc;
2797}
2799// Compute MD5 hash code
2800// NOTE: Returns a static int[4] array (16 bytes)
2801unsigned int *ComputeMD5(unsigned char *data, int dataSize)
2802{
2803 #define ROTATE_LEFT(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
2805 static unsigned int hash[4] = { 0 }; // Hash to be returned
2807 // WARNING: All variables are unsigned 32 bit and wrap modulo 2^32 when calculating
2809 // NOTE: r specifies the per-round shift amounts
2810 unsigned int r[] = {
2811 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
2812 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
2813 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
2814 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
2815 };
2817 // Using binary integer part of the sines of integers (in radians) as constants
2818 unsigned int k[] = {
2819 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
2820 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
2821 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
2822 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
2823 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
2824 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
2825 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
2826 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
2827 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
2828 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
2829 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
2830 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
2831 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
2832 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
2833 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
2834 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
2835 };
2837 hash[0] = 0x67452301;
2838 hash[1] = 0xefcdab89;
2839 hash[2] = 0x98badcfe;
2840 hash[3] = 0x10325476;
2842 // Pre-processing: adding a single 1 bit
2843 // Append '1' bit to message
2844 // NOTE: The input bytes are considered as bits strings,
2845 // where the first bit is the most significant bit of the byte
2847 // Pre-processing: padding with zeros
2848 // Append '0' bit until message length in bit 448 (mod 512)
2849 // Append length mod (2 pow 64) to message
2851 int newDataSize = ((((dataSize + 8)/64) + 1)*64) - 8;
2853 unsigned char *msg = (unsigned char *)RL_CALLOC(newDataSize + 64, 1); // Initialize with '0' bits, allocating 64 extra bytes
2854 memcpy(msg, data, dataSize);
2855 msg[dataSize] = 128; // Write the '1' bit
2857 unsigned int bitsLen = 8*dataSize;
2858 memcpy(msg + newDataSize, &bitsLen, 4); // Append the len in bits at the end of the buffer
2860 // Process the message in successive 512-bit chunks for each 512-bit chunk of message
2861 for (int offset = 0; offset < newDataSize; offset += (512/8))
2862 {
2863 // Break chunk into sixteen 32-bit words w[j], 0 <= j <= 15
2864 unsigned int *w = (unsigned int *)(msg + offset);
2866 // Initialize hash value for this chunk
2867 unsigned int a = hash[0];
2868 unsigned int b = hash[1];
2869 unsigned int c = hash[2];
2870 unsigned int d = hash[3];
2872 for (int i = 0; i < 64; i++)
2873 {
2874 unsigned int f = 0;
2875 unsigned int g = 0;
2877 if (i < 16)
2878 {
2879 f = (b & c) | ((~b) & d);
2880 g = i;
2881 }
2882 else if (i < 32)
2883 {
2884 f = (d & b) | ((~d) & c);
2885 g = (5*i + 1)%16;
2886 }
2887 else if (i < 48)
2888 {
2889 f = b ^ c ^ d;
2890 g = (3*i + 5)%16;
2891 }
2892 else
2893 {
2894 f = c ^ (b | (~d));
2895 g = (7*i)%16;
2896 }
2898 unsigned int temp = d;
2899 d = c;
2900 c = b;
2901 b = b + ROTATE_LEFT((a + f + k[i] + w[g]), r[i]);
2902 a = temp;
2903 }
2905 // Add chunk's hash to result so far
2906 hash[0] += a;
2907 hash[1] += b;
2908 hash[2] += c;
2909 hash[3] += d;
2910 }
2912 RL_FREE(msg);
2914 return hash;
2915}
2917// Compute SHA-1 hash code
2918// NOTE: Returns a static int[5] array (20 bytes)
2919unsigned int *ComputeSHA1(unsigned char *data, int dataSize)
2920{
2921 #define SHA1_ROTATE_LEFT(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
2923 static unsigned int hash[5] = { 0 }; // Hash to be returned
2925 // Initialize hash values
2926 hash[0] = 0x67452301;
2927 hash[1] = 0xEFCDAB89;
2928 hash[2] = 0x98BADCFE;
2929 hash[3] = 0x10325476;
2930 hash[4] = 0xC3D2E1F0;
2932 // Pre-processing: adding a single 1 bit
2933 // Append '1' bit to message
2934 // NOTE: The input bytes are considered as bits strings,
2935 // where the first bit is the most significant bit of the byte
2937 // Pre-processing: padding with zeros
2938 // Append '0' bit until message length in bit 448 (mod 512)
2939 // Append length mod (2 pow 64) to message
2941 int newDataSize = ((((dataSize + 8)/64) + 1)*64);
2943 unsigned char *msg = (unsigned char *)RL_CALLOC(newDataSize, 1); // Initialize with '0' bits
2944 memcpy(msg, data, dataSize);
2945 msg[dataSize] = 128; // Write the '1' bit
2947 unsigned long long bitsLen = 8ULL * dataSize;
2948 msg[newDataSize-1] = (unsigned char)(bitsLen);
2949 msg[newDataSize-2] = (unsigned char)(bitsLen >> 8);
2950 msg[newDataSize-3] = (unsigned char)(bitsLen >> 16);
2951 msg[newDataSize-4] = (unsigned char)(bitsLen >> 24);
2952 msg[newDataSize-5] = (unsigned char)(bitsLen >> 32);
2953 msg[newDataSize-6] = (unsigned char)(bitsLen >> 40);
2954 msg[newDataSize-7] = (unsigned char)(bitsLen >> 48);
2955 msg[newDataSize-8] = (unsigned char)(bitsLen >> 56);
2957 // Process the message in successive 512-bit chunks
2958 for (int offset = 0; offset < newDataSize; offset += (512/8))
2959 {
2960 // Break chunk into sixteen 32-bit words w[j], 0 <= j <= 15
2961 unsigned int w[80] = { 0 };
2962 for (int i = 0; i < 16; i++)
2963 {
2964 w[i] = (msg[offset + (i*4) + 0] << 24) |
2965 (msg[offset + (i*4) + 1] << 16) |
2966 (msg[offset + (i*4) + 2] << 8) |
2967 (msg[offset + (i*4) + 3]);
2968 }
2970 // Message schedule: extend the sixteen 32-bit words into eighty 32-bit words:
2971 for (int i = 16; i < 80; i++) w[i] = SHA1_ROTATE_LEFT(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);
2973 // Initialize hash value for this chunk
2974 unsigned int a = hash[0];
2975 unsigned int b = hash[1];
2976 unsigned int c = hash[2];
2977 unsigned int d = hash[3];
2978 unsigned int e = hash[4];
2980 for (int i = 0; i < 80; i++)
2981 {
2982 unsigned int f = 0;
2983 unsigned int k = 0;
2985 if (i < 20)
2986 {
2987 f = (b & c) | ((~b) & d);
2988 k = 0x5A827999;
2989 }
2990 else if (i < 40)
2991 {
2992 f = b ^ c ^ d;
2993 k = 0x6ED9EBA1;
2994 }
2995 else if (i < 60)
2996 {
2997 f = (b & c) | (b & d) | (c & d);
2998 k = 0x8F1BBCDC;
2999 }
3000 else
3001 {
3002 f = b ^ c ^ d;
3003 k = 0xCA62C1D6;
3004 }
3006 unsigned int temp = SHA1_ROTATE_LEFT(a, 5) + f + e + k + w[i];
3007 e = d;
3008 d = c;
3009 c = SHA1_ROTATE_LEFT(b, 30);
3010 b = a;
3011 a = temp;
3012 }
3014 // Add this chunk's hash to result so far
3015 hash[0] += a;
3016 hash[1] += b;
3017 hash[2] += c;
3018 hash[3] += d;
3019 hash[4] += e;
3020 }
3022 RL_FREE(msg);
3024 return hash;
3025}
3027// Compute SHA-256 hash code
3028// NOTE: Returns a static int[8] array (32 bytes)
3029unsigned int *ComputeSHA256(unsigned char *data, int dataSize)
3030{
3031 #define SHA256_ROTATE_RIGHT(x, c) ((x >> c) | (x << ((sizeof(unsigned int)*8) - c)))
3032 #define SHA256_A0(x) (SHA256_ROTATE_RIGHT(x, 7) ^ SHA256_ROTATE_RIGHT(x, 18) ^ (x >> 3))
3033 #define SHA256_A1(x) (SHA256_ROTATE_RIGHT(x, 17) ^ SHA256_ROTATE_RIGHT(x, 19) ^ (x >> 10))
3035 static const unsigned int k[64] = {
3036 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
3037 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
3038 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
3039 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
3040 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
3041 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
3042 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
3043 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
3044 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
3045 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
3046 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
3047 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
3048 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
3049 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
3050 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
3051 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
3052 };
3054 static unsigned int hash[8] = { 0 };
3055 hash[0] = 0x6A09e667;
3056 hash[1] = 0xbb67ae85;
3057 hash[2] = 0x3c6ef372;
3058 hash[3] = 0xa54ff53a;
3059 hash[4] = 0x510e527f;
3060 hash[5] = 0x9b05688c;
3061 hash[6] = 0x1f83d9ab;
3062 hash[7] = 0x5be0cd19;
3064 const unsigned long long int bitLen = ((unsigned long long int)dataSize)*8;
3065 unsigned long long int paddedSize = dataSize + sizeof(dataSize);
3066 paddedSize += (64 - (paddedSize%64));
3067 unsigned char *buffer = (unsigned char *)RL_CALLOC(paddedSize, sizeof(unsigned char));
3069 memcpy(buffer, data, dataSize);
3070 buffer[dataSize] = 0x80;
3071 for (int i = 1; i <= sizeof(bitLen); i++)
3072 {
3073 buffer[(paddedSize - sizeof(bitLen)) + (i - 1)] = (bitLen >> (8*(sizeof(bitLen) - i))) & 0xFF;
3074 }
3076 for (unsigned long long int blockN = 0; blockN < paddedSize/64; blockN++)
3077 {
3078 unsigned int a = hash[0];
3079 unsigned int b = hash[1];
3080 unsigned int c = hash[2];
3081 unsigned int d = hash[3];
3082 unsigned int e = hash[4];
3083 unsigned int f = hash[5];
3084 unsigned int g = hash[6];
3085 unsigned int h = hash[7];
3087 unsigned char *block = buffer + (blockN*64);
3088 unsigned int w[64] = { 0 };
3089 for (int i = 0; i < 16; i++)
3090 {
3091 w[i] = ((unsigned int)block[i*4 + 0] << 24) |
3092 ((unsigned int)block[i*4 + 1] << 16) |
3093 ((unsigned int)block[i*4 + 2] << 8) |
3094 ((unsigned int)block[i*4 + 3]);
3095 }
3096 for (int t = 16; t < 64; t++) w[t] = SHA256_A1(w[t - 2]) + w[t - 7] + SHA256_A0(w[t - 15]) + w[t - 16];
3098 for (unsigned long long int t = 0; t < 64; t++)
3099 {
3100 unsigned int e1 = (SHA256_ROTATE_RIGHT(e, 6) ^ SHA256_ROTATE_RIGHT(e, 11) ^ SHA256_ROTATE_RIGHT(e, 25));
3101 unsigned int ch = ((e & f) ^ (~e & g));
3102 unsigned int t1 = (h + e1 + ch + k[t] + w[t]);
3103 unsigned int e0 = (SHA256_ROTATE_RIGHT(a, 2) ^ SHA256_ROTATE_RIGHT(a, 13) ^ SHA256_ROTATE_RIGHT(a, 22));
3104 unsigned int maj = ((a & b) ^ (a & c) ^ (b & c));
3105 unsigned int t2 = e0 + maj;
3107 h = g;
3108 g = f;
3109 f = e;
3110 e = d + t1;
3111 d = c;
3112 c = b;
3113 b = a;
3114 a = t1 + t2;
3115 }
3117 hash[0] += a;
3118 hash[1] += b;
3119 hash[2] += c;
3120 hash[3] += d;
3121 hash[4] += e;
3122 hash[5] += f;
3123 hash[6] += g;
3124 hash[7] += h;
3125 }
3127 RL_FREE(buffer);
3129 return hash;
3130}
3132//----------------------------------------------------------------------------------
3133// Module Functions Definition: Automation Events Recording and Playing
3134//----------------------------------------------------------------------------------
3136// Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS
3137AutomationEventList LoadAutomationEventList(const char *fileName)
3138{
3139 AutomationEventList list = { 0 };
3141 // Allocate and empty automation event list, ready to record new events
3142 list.events = (AutomationEvent *)RL_CALLOC(MAX_AUTOMATION_EVENTS, sizeof(AutomationEvent));
3143 list.capacity = MAX_AUTOMATION_EVENTS;
3145#if defined(SUPPORT_AUTOMATION_EVENTS)
3146 if (fileName == NULL) TRACELOG(LOG_INFO, "AUTOMATION: New empty events list loaded successfully");
3147 else
3148 {
3149 // Load automation events file (binary)
3150 /*
3151 //int dataSize = 0;
3152 //unsigned char *data = LoadFileData(fileName, &dataSize);
3154 FILE *raeFile = fopen(fileName, "rb");
3155 unsigned char fileId[4] = { 0 };
3157 fread(fileId, 1, 4, raeFile);
3159 if ((fileId[0] == 'r') && (fileId[1] == 'A') && (fileId[2] == 'E') && (fileId[1] == ' '))
3160 {
3161 fread(&eventCount, sizeof(int), 1, raeFile);
3162 TRACELOG(LOG_WARNING, "Events loaded: %i\n", eventCount);
3163 fread(events, sizeof(AutomationEvent), eventCount, raeFile);
3164 }
3166 fclose(raeFile);
3167 */
3169 // Load events file (text)
3170 //unsigned char *buffer = LoadFileText(fileName);
3171 FILE *raeFile = fopen(fileName, "rt");
3173 if (raeFile != NULL)
3174 {
3175 unsigned int counter = 0;
3176 char buffer[256] = { 0 };
3177 char eventDesc[64] = { 0 };
3179 char *result = fgets(buffer, 256, raeFile);
3180 if (result != buffer) TRACELOG(LOG_WARNING, "AUTOMATION: [%s] Issue reading line to buffer", fileName);
3182 while (!feof(raeFile))
3183 {
3184 switch (buffer[0])
3185 {
3186 case 'c': sscanf(buffer, "c %i", &list.count); break;
3187 case 'e':
3188 {
3189 sscanf(buffer, "e %d %d %d %d %d %d %[^\n]s", &list.events[counter].frame, &list.events[counter].type,
3190 &list.events[counter].params[0], &list.events[counter].params[1], &list.events[counter].params[2], &list.events[counter].params[3], eventDesc);
3192 counter++;
3193 } break;
3194 default: break;
3195 }
3197 result = fgets(buffer, 256, raeFile);
3198 if (result != buffer) TRACELOG(LOG_WARNING, "AUTOMATION: [%s] Issue reading line to buffer", fileName);
3199 }
3201 if (counter != list.count)
3202 {
3203 TRACELOG(LOG_WARNING, "AUTOMATION: Events read from file [%i] do not mach event count specified [%i]", counter, list.count);
3204 list.count = counter;
3205 }
3207 fclose(raeFile);
3209 TRACELOG(LOG_INFO, "AUTOMATION: Events file loaded successfully");
3210 }
3212 TRACELOG(LOG_INFO, "AUTOMATION: Events loaded from file: %i", list.count);
3213 }
3214#endif
3215 return list;
3216}
3218// Unload automation events list from file
3219void UnloadAutomationEventList(AutomationEventList list)
3220{
3221#if defined(SUPPORT_AUTOMATION_EVENTS)
3222 RL_FREE(list.events);
3223#endif
3224}
3226// Export automation events list as text file
3227bool ExportAutomationEventList(AutomationEventList list, const char *fileName)
3228{
3229 bool success = false;
3231#if defined(SUPPORT_AUTOMATION_EVENTS)
3232 // Export events as binary file
3233 // TODO: Save to memory buffer and SaveFileData()
3234 /*
3235 unsigned char fileId[4] = "rAE ";
3236 FILE *raeFile = fopen(fileName, "wb");
3237 fwrite(fileId, sizeof(unsigned char), 4, raeFile);
3238 fwrite(&eventCount, sizeof(int), 1, raeFile);
3239 fwrite(events, sizeof(AutomationEvent), eventCount, raeFile);
3240 fclose(raeFile);
3241 */
3243 // Export events as text
3244 // NOTE: Save to memory buffer and SaveFileText()
3245 char *txtData = (char *)RL_CALLOC(256*list.count + 2048, sizeof(char)); // 256 characters per line plus some header
3247 int byteCount = 0;
3248 byteCount += sprintf(txtData + byteCount, "#\n");
3249 byteCount += sprintf(txtData + byteCount, "# Automation events exporter v1.0 - raylib automation events list\n");
3250 byteCount += sprintf(txtData + byteCount, "#\n");
3251 byteCount += sprintf(txtData + byteCount, "# c <events_count>\n");
3252 byteCount += sprintf(txtData + byteCount, "# e <frame> <event_type> <param0> <param1> <param2> <param3> // <event_type_name>\n");
3253 byteCount += sprintf(txtData + byteCount, "#\n");
3254 byteCount += sprintf(txtData + byteCount, "# more info and bugs-report: github.com/raysan5/raylib\n");
3255 byteCount += sprintf(txtData + byteCount, "# feedback and support: ray[at]raylib.com\n");
3256 byteCount += sprintf(txtData + byteCount, "#\n");
3257 byteCount += sprintf(txtData + byteCount, "# Copyright (c) 2023-2025 Ramon Santamaria (@raysan5)\n");
3258 byteCount += sprintf(txtData + byteCount, "#\n\n");
3260 // Add events data
3261 byteCount += sprintf(txtData + byteCount, "c %i\n", list.count);
3262 for (unsigned int i = 0; i < list.count; i++)
3263 {
3264 byteCount += snprintf(txtData + byteCount, 256, "e %i %i %i %i %i %i // Event: %s\n", list.events[i].frame, list.events[i].type,
3265 list.events[i].params[0], list.events[i].params[1], list.events[i].params[2], list.events[i].params[3], autoEventTypeName[list.events[i].type]);
3266 }
3268 // NOTE: Text data size exported is determined by '\0' (NULL) character
3269 success = SaveFileText(fileName, txtData);
3271 RL_FREE(txtData);
3272#endif
3274 return success;
3275}
3277// Setup automation event list to record to
3278void SetAutomationEventList(AutomationEventList *list)
3279{
3280#if defined(SUPPORT_AUTOMATION_EVENTS)
3281 currentEventList = list;
3282#endif
3283}
3285// Set automation event internal base frame to start recording
3286void SetAutomationEventBaseFrame(int frame)
3287{
3288 CORE.Time.frameCounter = frame;
3289}
3291// Start recording automation events (AutomationEventList must be set)
3292void StartAutomationEventRecording(void)
3293{
3294#if defined(SUPPORT_AUTOMATION_EVENTS)
3295 automationEventRecording = true;
3296#endif
3297}
3299// Stop recording automation events
3300void StopAutomationEventRecording(void)
3301{
3302#if defined(SUPPORT_AUTOMATION_EVENTS)
3303 automationEventRecording = false;
3304#endif
3305}
3307// Play a recorded automation event
3308void PlayAutomationEvent(AutomationEvent event)
3309{
3310#if defined(SUPPORT_AUTOMATION_EVENTS)
3311 // WARNING: When should event be played? After/before/replace PollInputEvents()? -> Up to the user!
3313 if (!automationEventRecording)
3314 {
3315 switch (event.type)
3316 {
3317 // Input event
3318 case INPUT_KEY_UP: CORE.Input.Keyboard.currentKeyState[event.params[0]] = false; break; // param[0]: key
3319 case INPUT_KEY_DOWN: { // param[0]: key
3320 CORE.Input.Keyboard.currentKeyState[event.params[0]] = true;
3322 if (CORE.Input.Keyboard.previousKeyState[event.params[0]] == false)
3323 {
3324 if (CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE)
3325 {
3326 // Add character to the queue
3327 CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = event.params[0];
3328 CORE.Input.Keyboard.keyPressedQueueCount++;
3329 }
3330 }
3331 } break;
3332 case INPUT_MOUSE_BUTTON_UP: CORE.Input.Mouse.currentButtonState[event.params[0]] = false; break; // param[0]: key
3333 case INPUT_MOUSE_BUTTON_DOWN: CORE.Input.Mouse.currentButtonState[event.params[0]] = true; break; // param[0]: key
3334 case INPUT_MOUSE_POSITION: // param[0]: x, param[1]: y
3335 {
3336 CORE.Input.Mouse.currentPosition.x = (float)event.params[0];
3337 CORE.Input.Mouse.currentPosition.y = (float)event.params[1];
3338 } break;
3339 case INPUT_MOUSE_WHEEL_MOTION: // param[0]: x delta, param[1]: y delta
3340 {
3341 CORE.Input.Mouse.currentWheelMove.x = (float)event.params[0];
3342 CORE.Input.Mouse.currentWheelMove.y = (float)event.params[1];
3343 } break;
3344 case INPUT_TOUCH_UP: CORE.Input.Touch.currentTouchState[event.params[0]] = false; break; // param[0]: id
3345 case INPUT_TOUCH_DOWN: CORE.Input.Touch.currentTouchState[event.params[0]] = true; break; // param[0]: id
3346 case INPUT_TOUCH_POSITION: // param[0]: id, param[1]: x, param[2]: y
3347 {
3348 CORE.Input.Touch.position[event.params[0]].x = (float)event.params[1];
3349 CORE.Input.Touch.position[event.params[0]].y = (float)event.params[2];
3350 } break;
3351 case INPUT_GAMEPAD_CONNECT: CORE.Input.Gamepad.ready[event.params[0]] = true; break; // param[0]: gamepad
3352 case INPUT_GAMEPAD_DISCONNECT: CORE.Input.Gamepad.ready[event.params[0]] = false; break; // param[0]: gamepad
3353 case INPUT_GAMEPAD_BUTTON_UP: CORE.Input.Gamepad.currentButtonState[event.params[0]][event.params[1]] = false; break; // param[0]: gamepad, param[1]: button
3354 case INPUT_GAMEPAD_BUTTON_DOWN: CORE.Input.Gamepad.currentButtonState[event.params[0]][event.params[1]] = true; break; // param[0]: gamepad, param[1]: button
3355 case INPUT_GAMEPAD_AXIS_MOTION: // param[0]: gamepad, param[1]: axis, param[2]: delta
3356 {
3357 CORE.Input.Gamepad.axisState[event.params[0]][event.params[1]] = ((float)event.params[2]/32768.0f);
3358 } break;
3359 #if defined(SUPPORT_GESTURES_SYSTEM)
3360 case INPUT_GESTURE: GESTURES.current = event.params[0]; break; // param[0]: gesture (enum Gesture) -> rgestures.h: GESTURES.current
3361 #endif
3362 // Window event
3363 case WINDOW_CLOSE: CORE.Window.shouldClose = true; break;
3364 case WINDOW_MAXIMIZE: MaximizeWindow(); break;
3365 case WINDOW_MINIMIZE: MinimizeWindow(); break;
3366 case WINDOW_RESIZE: SetWindowSize(event.params[0], event.params[1]); break;
3368 // Custom event
3369 #if defined(SUPPORT_SCREEN_CAPTURE)
3370 case ACTION_TAKE_SCREENSHOT:
3371 {
3372 TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
3373 screenshotCounter++;
3374 } break;
3375 #endif
3376 case ACTION_SETTARGETFPS: SetTargetFPS(event.params[0]); break;
3377 default: break;
3378 }
3380 TRACELOG(LOG_INFO, "AUTOMATION PLAY: Frame: %i | Event type: %i | Event parameters: %i, %i, %i", event.frame, event.type, event.params[0], event.params[1], event.params[2]);
3381 }
3382#endif
3383}
3385//----------------------------------------------------------------------------------
3386// Module Functions Definition: Input Handling: Keyboard
3387//----------------------------------------------------------------------------------
3389// Check if a key has been pressed once
3390bool IsKeyPressed(int key)
3391{
3393 bool pressed = false;
3395 if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
3396 {
3397 if ((CORE.Input.Keyboard.previousKeyState[key] == 0) && (CORE.Input.Keyboard.currentKeyState[key] == 1)) pressed = true;
3398 }
3400 return pressed;
3401}
3403// Check if a key has been pressed again
3404bool IsKeyPressedRepeat(int key)
3405{
3406 bool repeat = false;
3408 if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
3409 {
3410 if (CORE.Input.Keyboard.keyRepeatInFrame[key] == 1) repeat = true;
3411 }
3413 return repeat;
3414}
3416// Check if a key is being pressed (key held down)
3417bool IsKeyDown(int key)
3418{
3419 bool down = false;
3421 if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
3422 {
3423 if (CORE.Input.Keyboard.currentKeyState[key] == 1) down = true;
3424 }
3426 return down;
3427}
3429// Check if a key has been released once
3430bool IsKeyReleased(int key)
3431{
3432 bool released = false;
3434 if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
3435 {
3436 if ((CORE.Input.Keyboard.previousKeyState[key] == 1) && (CORE.Input.Keyboard.currentKeyState[key] == 0)) released = true;
3437 }
3439 return released;
3440}
3442// Check if a key is NOT being pressed (key not held down)
3443bool IsKeyUp(int key)
3444{
3445 bool up = false;
3447 if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
3448 {
3449 if (CORE.Input.Keyboard.currentKeyState[key] == 0) up = true;
3450 }
3452 return up;
3453}
3455// Get the last key pressed
3456int GetKeyPressed(void)
3457{
3458 int value = 0;
3460 if (CORE.Input.Keyboard.keyPressedQueueCount > 0)
3461 {
3462 // Get character from the queue head
3463 value = CORE.Input.Keyboard.keyPressedQueue[0];
3465 // Shift elements 1 step toward the head
3466 for (int i = 0; i < (CORE.Input.Keyboard.keyPressedQueueCount - 1); i++)
3467 CORE.Input.Keyboard.keyPressedQueue[i] = CORE.Input.Keyboard.keyPressedQueue[i + 1];
3469 // Reset last character in the queue
3470 CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount - 1] = 0;
3471 CORE.Input.Keyboard.keyPressedQueueCount--;
3472 }
3474 return value;
3475}
3477// Get the last char pressed
3478int GetCharPressed(void)
3479{
3480 int value = 0;
3482 if (CORE.Input.Keyboard.charPressedQueueCount > 0)
3483 {
3484 // Get character from the queue head
3485 value = CORE.Input.Keyboard.charPressedQueue[0];
3487 // Shift elements 1 step toward the head
3488 for (int i = 0; i < (CORE.Input.Keyboard.charPressedQueueCount - 1); i++)
3489 CORE.Input.Keyboard.charPressedQueue[i] = CORE.Input.Keyboard.charPressedQueue[i + 1];
3491 // Reset last character in the queue
3492 CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount - 1] = 0;
3493 CORE.Input.Keyboard.charPressedQueueCount--;
3494 }
3496 return value;
3497}
3499// Set a custom key to exit program
3500// NOTE: default exitKey is set to ESCAPE
3501void SetExitKey(int key)
3502{
3503 CORE.Input.Keyboard.exitKey = key;
3504}
3506//----------------------------------------------------------------------------------
3507// Module Functions Definition: Input Handling: Gamepad
3508//----------------------------------------------------------------------------------
3510// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
3511//int SetGamepadMappings(const char *mappings)
3513// Check if a gamepad is available
3514bool IsGamepadAvailable(int gamepad)
3515{
3516 bool result = false;
3518 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad]) result = true;
3520 return result;
3521}
3523// Get gamepad internal name id
3524const char *GetGamepadName(int gamepad)
3525{
3526 return CORE.Input.Gamepad.name[gamepad];
3527}
3529// Check if a gamepad button has been pressed once
3530bool IsGamepadButtonPressed(int gamepad, int button)
3531{
3532 bool pressed = false;
3534 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
3535 (CORE.Input.Gamepad.previousButtonState[gamepad][button] == 0) && (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 1)) pressed = true;
3537 return pressed;
3538}
3540// Check if a gamepad button is being pressed
3541bool IsGamepadButtonDown(int gamepad, int button)
3542{
3543 bool down = false;
3545 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
3546 (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 1)) down = true;
3548 return down;
3549}
3551// Check if a gamepad button has NOT been pressed once
3552bool IsGamepadButtonReleased(int gamepad, int button)
3553{
3554 bool released = false;
3556 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
3557 (CORE.Input.Gamepad.previousButtonState[gamepad][button] == 1) && (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 0)) released = true;
3559 return released;
3560}
3562// Check if a gamepad button is NOT being pressed
3563bool IsGamepadButtonUp(int gamepad, int button)
3564{
3565 bool up = false;
3567 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
3568 (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 0)) up = true;
3570 return up;
3571}
3573// Get the last gamepad button pressed
3574int GetGamepadButtonPressed(void)
3575{
3576 return CORE.Input.Gamepad.lastButtonPressed;
3577}
3579// Get gamepad axis count
3580int GetGamepadAxisCount(int gamepad)
3581{
3582 return CORE.Input.Gamepad.axisCount[gamepad];
3583}
3585// Get axis movement vector for a gamepad
3586float GetGamepadAxisMovement(int gamepad, int axis)
3587{
3588 float value = ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER))? -1.0f : 0.0f;
3590 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXES))
3591 {
3592 float movement = (value < 0.0f)? CORE.Input.Gamepad.axisState[gamepad][axis] : fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]);
3594 if (movement > value) value = CORE.Input.Gamepad.axisState[gamepad][axis];
3595 }
3597 return value;
3598}
3600//----------------------------------------------------------------------------------
3601// Module Functions Definition: Input Handling: Mouse
3602//----------------------------------------------------------------------------------
3604// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
3605//void SetMousePosition(int x, int y)
3606//void SetMouseCursor(int cursor)
3608// Check if a mouse button has been pressed once
3609bool IsMouseButtonPressed(int button)
3610{
3611 bool pressed = false;
3613 if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) pressed = true;
3615 // Map touches to mouse buttons checking
3616 if ((CORE.Input.Touch.currentTouchState[button] == 1) && (CORE.Input.Touch.previousTouchState[button] == 0)) pressed = true;
3618 return pressed;
3619}
3621// Check if a mouse button is being pressed
3622bool IsMouseButtonDown(int button)
3623{
3624 bool down = false;
3626 if (CORE.Input.Mouse.currentButtonState[button] == 1) down = true;
3628 // NOTE: Touches are considered like mouse buttons
3629 if (CORE.Input.Touch.currentTouchState[button] == 1) down = true;
3631 return down;
3632}
3634// Check if a mouse button has been released once
3635bool IsMouseButtonReleased(int button)
3636{
3637 bool released = false;
3639 if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) released = true;
3641 // Map touches to mouse buttons checking
3642 if ((CORE.Input.Touch.currentTouchState[button] == 0) && (CORE.Input.Touch.previousTouchState[button] == 1)) released = true;
3644 return released;
3645}
3647// Check if a mouse button is NOT being pressed
3648bool IsMouseButtonUp(int button)
3649{
3650 bool up = false;
3652 if (CORE.Input.Mouse.currentButtonState[button] == 0) up = true;
3654 // NOTE: Touches are considered like mouse buttons
3655 if (CORE.Input.Touch.currentTouchState[button] == 0) up = true;
3657 return up;
3658}
3660// Get mouse position X
3661int GetMouseX(void)
3662{
3663 int mouseX = (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x);
3664 return mouseX;
3665}
3667// Get mouse position Y
3668int GetMouseY(void)
3669{
3670 int mouseY = (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y);
3671 return mouseY;
3672}
3674// Get mouse position XY
3675Vector2 GetMousePosition(void)
3676{
3677 Vector2 position = { 0 };
3679 position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x;
3680 position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y;
3682 return position;
3683}
3685// Get mouse delta between frames
3686Vector2 GetMouseDelta(void)
3687{
3688 Vector2 delta = { 0 };
3690 delta.x = CORE.Input.Mouse.currentPosition.x - CORE.Input.Mouse.previousPosition.x;
3691 delta.y = CORE.Input.Mouse.currentPosition.y - CORE.Input.Mouse.previousPosition.y;
3693 return delta;
3694}
3696// Set mouse offset
3697// NOTE: Useful when rendering to different size targets
3698void SetMouseOffset(int offsetX, int offsetY)
3699{
3700 CORE.Input.Mouse.offset = (Vector2){ (float)offsetX, (float)offsetY };
3701}
3703// Set mouse scaling
3704// NOTE: Useful when rendering to different size targets
3705void SetMouseScale(float scaleX, float scaleY)
3706{
3707 CORE.Input.Mouse.scale = (Vector2){ scaleX, scaleY };
3708}
3710// Get mouse wheel movement Y
3711float GetMouseWheelMove(void)
3712{
3713 float result = 0.0f;
3715 if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x;
3716 else result = (float)CORE.Input.Mouse.currentWheelMove.y;
3718 return result;
3719}
3721// Get mouse wheel movement X/Y as a vector
3722Vector2 GetMouseWheelMoveV(void)
3723{
3724 Vector2 result = { 0 };
3726 result = CORE.Input.Mouse.currentWheelMove;
3728 return result;
3729}
3731//----------------------------------------------------------------------------------
3732// Module Functions Definition: Input Handling: Touch
3733//----------------------------------------------------------------------------------
3735// Get touch position X for touch point 0 (relative to screen size)
3736int GetTouchX(void)
3737{
3738 int touchX = (int)CORE.Input.Touch.position[0].x;
3739 return touchX;
3740}
3742// Get touch position Y for touch point 0 (relative to screen size)
3743int GetTouchY(void)
3744{
3745 int touchY = (int)CORE.Input.Touch.position[0].y;
3746 return touchY;
3747}
3749// Get touch position XY for a touch point index (relative to screen size)
3750Vector2 GetTouchPosition(int index)
3751{
3752 Vector2 position = { -1.0f, -1.0f };
3754 if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index];
3755 else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS);
3757 return position;
3758}
3760// Get touch point identifier for given index
3761int GetTouchPointId(int index)
3762{
3763 int id = -1;
3765 if (index < MAX_TOUCH_POINTS) id = CORE.Input.Touch.pointId[index];
3767 return id;
3768}
3770// Get number of touch points
3771int GetTouchPointCount(void)
3772{
3773 return CORE.Input.Touch.pointCount;
3774}
3776//----------------------------------------------------------------------------------
3777// Module Internal Functions Definition
3778//----------------------------------------------------------------------------------
3780// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
3781//int InitPlatform(void)
3782//void ClosePlatform(void)
3784// Initialize hi-resolution timer
3785void InitTimer(void)
3786{
3787 // Setting a higher resolution can improve the accuracy of time-out intervals in wait functions
3788 // However, it can also reduce overall system performance, because the thread scheduler switches tasks more often
3789 // High resolutions can also prevent the CPU power management system from entering power-saving modes
3790 // Setting a higher resolution does not improve the accuracy of the high-resolution performance counter
3791#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) && !defined(PLATFORM_DESKTOP_SDL)
3792 timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms)
3793#endif
3795#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
3796 struct timespec now = { 0 };
3798 if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success
3799 {
3800 CORE.Time.base = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec;
3801 }
3802 else TRACELOG(LOG_WARNING, "TIMER: Hi-resolution timer not available");
3803#endif
3805 CORE.Time.previous = GetTime(); // Get time as double
3806}
3808// Set viewport for a provided width and height
3809void SetupViewport(int width, int height)
3810{
3811 CORE.Window.render.width = width;
3812 CORE.Window.render.height = height;
3814 // Set viewport width and height
3815 rlViewport(CORE.Window.renderOffset.x/2, CORE.Window.renderOffset.y/2, CORE.Window.render.width, CORE.Window.render.height);
3817 rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
3818 rlLoadIdentity(); // Reset current matrix (projection)
3820 // Set orthographic projection to current framebuffer size
3821 // NOTE: Configured top-left corner as (0, 0)
3822 rlOrtho(0, CORE.Window.render.width, CORE.Window.render.height, 0, 0.0f, 1.0f);
3824 rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
3825 rlLoadIdentity(); // Reset current matrix (modelview)
3826}
3828// Scan all files and directories in a base path
3829// WARNING: files.paths[] must be previously allocated and
3830// contain enough space to store all required paths
3831static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const char *filter)
3832{
3833 static char path[MAX_FILEPATH_LENGTH] = { 0 };
3834 memset(path, 0, MAX_FILEPATH_LENGTH);
3836 struct dirent *dp = NULL;
3837 DIR *dir = opendir(basePath);
3839 if (dir != NULL)
3840 {
3841 while ((dp = readdir(dir)) != NULL)
3842 {
3843 if ((strcmp(dp->d_name, ".") != 0) &&
3844 (strcmp(dp->d_name, "..") != 0))
3845 {
3846 // Construct new path from our base path
3847 #if defined(_WIN32)
3848 int pathLength = snprintf(path, MAX_FILEPATH_LENGTH - 1, "%s\\%s", basePath, dp->d_name);
3849 #else
3850 int pathLength = snprintf(path, MAX_FILEPATH_LENGTH - 1, "%s/%s", basePath, dp->d_name);
3851 #endif
3853 if ((pathLength < 0) || (pathLength >= MAX_FILEPATH_LENGTH))
3854 {
3855 TRACELOG(LOG_WARNING, "FILEIO: Path longer than %d characters (%s...)", MAX_FILEPATH_LENGTH, basePath);
3856 }
3857 else if (filter != NULL)
3858 {
3859 if (IsPathFile(path))
3860 {
3861 if (IsFileExtension(path, filter))
3862 {
3863 strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
3864 files->count++;
3865 }
3866 }
3867 else
3868 {
3869 if (strstr(filter, DIRECTORY_FILTER_TAG) != NULL)
3870 {
3871 strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
3872 files->count++;
3873 }
3874 }
3875 }
3876 else
3877 {
3878 strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
3879 files->count++;
3880 }
3881 }
3882 }
3884 closedir(dir);
3885 }
3886 else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath);
3887}
3889// Scan all files and directories recursively from a base path
3890static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *files, const char *filter)
3891{
3892 // WARNING: Path can not be static or it will be reused between recursive function calls!
3893 char path[MAX_FILEPATH_LENGTH] = { 0 };
3894 memset(path, 0, MAX_FILEPATH_LENGTH);
3896 struct dirent *dp = NULL;
3897 DIR *dir = opendir(basePath);
3899 if (dir != NULL)
3900 {
3901 while (((dp = readdir(dir)) != NULL) && (files->count < files->capacity))
3902 {
3903 if ((strcmp(dp->d_name, ".") != 0) && (strcmp(dp->d_name, "..") != 0))
3904 {
3905 // Construct new path from our base path
3906 #if defined(_WIN32)
3907 int pathLength = snprintf(path, MAX_FILEPATH_LENGTH - 1, "%s\\%s", basePath, dp->d_name);
3908 #else
3909 int pathLength = snprintf(path, MAX_FILEPATH_LENGTH - 1, "%s/%s", basePath, dp->d_name);
3910 #endif
3912 if ((pathLength < 0) || (pathLength >= MAX_FILEPATH_LENGTH))
3913 {
3914 TRACELOG(LOG_WARNING, "FILEIO: Path longer than %d characters (%s...)", MAX_FILEPATH_LENGTH, basePath);
3915 }
3916 else if (IsPathFile(path))
3917 {
3918 if (filter != NULL)
3919 {
3920 if (IsFileExtension(path, filter))
3921 {
3922 strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
3923 files->count++;
3924 }
3925 }
3926 else
3927 {
3928 strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
3929 files->count++;
3930 }
3932 if (files->count >= files->capacity)
3933 {
3934 TRACELOG(LOG_WARNING, "FILEIO: Maximum filepath scan capacity reached (%i files)", files->capacity);
3935 break;
3936 }
3937 }
3938 else
3939 {
3940 if ((filter != NULL) && (strstr(filter, DIRECTORY_FILTER_TAG) != NULL))
3941 {
3942 strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
3943 files->count++;
3944 }
3946 if (files->count >= files->capacity)
3947 {
3948 TRACELOG(LOG_WARNING, "FILEIO: Maximum filepath scan capacity reached (%i files)", files->capacity);
3949 break;
3950 }
3952 ScanDirectoryFilesRecursively(path, files, filter);
3953 }
3954 }
3955 }
3957 closedir(dir);
3958 }
3959 else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath);
3960}
3962#if defined(SUPPORT_AUTOMATION_EVENTS)
3963// Automation event recording
3964// Checking events in current frame and save them into currentEventList
3965// NOTE: Recording is by default done at EndDrawing(), before PollInputEvents()
3966static void RecordAutomationEvent(void)
3967{
3968 if (currentEventList->count == currentEventList->capacity) return;
3970 // Keyboard input events recording
3971 //-------------------------------------------------------------------------------------
3972 for (int key = 0; key < MAX_KEYBOARD_KEYS; key++)
3973 {
3974 // Event type: INPUT_KEY_UP (only saved once)
3975 if (CORE.Input.Keyboard.previousKeyState[key] && !CORE.Input.Keyboard.currentKeyState[key])
3976 {
3977 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
3978 currentEventList->events[currentEventList->count].type = INPUT_KEY_UP;
3979 currentEventList->events[currentEventList->count].params[0] = key;
3980 currentEventList->events[currentEventList->count].params[1] = 0;
3981 currentEventList->events[currentEventList->count].params[2] = 0;
3983 TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_KEY_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
3984 currentEventList->count++;
3985 }
3987 if (currentEventList->count == currentEventList->capacity) return; // Security check
3989 // Event type: INPUT_KEY_DOWN
3990 if (CORE.Input.Keyboard.currentKeyState[key])
3991 {
3992 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
3993 currentEventList->events[currentEventList->count].type = INPUT_KEY_DOWN;
3994 currentEventList->events[currentEventList->count].params[0] = key;
3995 currentEventList->events[currentEventList->count].params[1] = 0;
3996 currentEventList->events[currentEventList->count].params[2] = 0;
3998 TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_KEY_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
3999 currentEventList->count++;
4000 }
4002 if (currentEventList->count == currentEventList->capacity) return; // Security check
4003 }
4004 //-------------------------------------------------------------------------------------
4006 // Mouse input currentEventList->events recording
4007 //-------------------------------------------------------------------------------------
4008 for (int button = 0; button < MAX_MOUSE_BUTTONS; button++)
4009 {
4010 // Event type: INPUT_MOUSE_BUTTON_UP
4011 if (CORE.Input.Mouse.previousButtonState[button] && !CORE.Input.Mouse.currentButtonState[button])
4012 {
4013 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
4014 currentEventList->events[currentEventList->count].type = INPUT_MOUSE_BUTTON_UP;
4015 currentEventList->events[currentEventList->count].params[0] = button;
4016 currentEventList->events[currentEventList->count].params[1] = 0;
4017 currentEventList->events[currentEventList->count].params[2] = 0;
4019 TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_BUTTON_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
4020 currentEventList->count++;
4021 }
4023 if (currentEventList->count == currentEventList->capacity) return; // Security check
4025 // Event type: INPUT_MOUSE_BUTTON_DOWN
4026 if (CORE.Input.Mouse.currentButtonState[button])
4027 {
4028 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
4029 currentEventList->events[currentEventList->count].type = INPUT_MOUSE_BUTTON_DOWN;
4030 currentEventList->events[currentEventList->count].params[0] = button;
4031 currentEventList->events[currentEventList->count].params[1] = 0;
4032 currentEventList->events[currentEventList->count].params[2] = 0;
4034 TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_BUTTON_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
4035 currentEventList->count++;
4036 }
4038 if (currentEventList->count == currentEventList->capacity) return; // Security check
4039 }
4041 // Event type: INPUT_MOUSE_POSITION (only saved if changed)
4042 if (((int)CORE.Input.Mouse.currentPosition.x != (int)CORE.Input.Mouse.previousPosition.x) ||
4043 ((int)CORE.Input.Mouse.currentPosition.y != (int)CORE.Input.Mouse.previousPosition.y))
4044 {
4045 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
4046 currentEventList->events[currentEventList->count].type = INPUT_MOUSE_POSITION;
4047 currentEventList->events[currentEventList->count].params[0] = (int)CORE.Input.Mouse.currentPosition.x;
4048 currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Mouse.currentPosition.y;
4049 currentEventList->events[currentEventList->count].params[2] = 0;
4051 TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_POSITION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
4052 currentEventList->count++;
4054 if (currentEventList->count == currentEventList->capacity) return; // Security check
4055 }
4057 // Event type: INPUT_MOUSE_WHEEL_MOTION
4058 if (((int)CORE.Input.Mouse.currentWheelMove.x != (int)CORE.Input.Mouse.previousWheelMove.x) ||
4059 ((int)CORE.Input.Mouse.currentWheelMove.y != (int)CORE.Input.Mouse.previousWheelMove.y))
4060 {
4061 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
4062 currentEventList->events[currentEventList->count].type = INPUT_MOUSE_WHEEL_MOTION;
4063 currentEventList->events[currentEventList->count].params[0] = (int)CORE.Input.Mouse.currentWheelMove.x;
4064 currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Mouse.currentWheelMove.y;
4065 currentEventList->events[currentEventList->count].params[2] = 0;
4067 TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_WHEEL_MOTION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
4068 currentEventList->count++;
4070 if (currentEventList->count == currentEventList->capacity) return; // Security check
4071 }
4072 //-------------------------------------------------------------------------------------
4074 // Touch input currentEventList->events recording
4075 //-------------------------------------------------------------------------------------
4076 for (int id = 0; id < MAX_TOUCH_POINTS; id++)
4077 {
4078 // Event type: INPUT_TOUCH_UP
4079 if (CORE.Input.Touch.previousTouchState[id] && !CORE.Input.Touch.currentTouchState[id])
4080 {
4081 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
4082 currentEventList->events[currentEventList->count].type = INPUT_TOUCH_UP;
4083 currentEventList->events[currentEventList->count].params[0] = id;
4084 currentEventList->events[currentEventList->count].params[1] = 0;
4085 currentEventList->events[currentEventList->count].params[2] = 0;
4087 TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
4088 currentEventList->count++;
4089 }
4091 if (currentEventList->count == currentEventList->capacity) return; // Security check
4093 // Event type: INPUT_TOUCH_DOWN
4094 if (CORE.Input.Touch.currentTouchState[id])
4095 {
4096 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
4097 currentEventList->events[currentEventList->count].type = INPUT_TOUCH_DOWN;
4098 currentEventList->events[currentEventList->count].params[0] = id;
4099 currentEventList->events[currentEventList->count].params[1] = 0;
4100 currentEventList->events[currentEventList->count].params[2] = 0;
4102 TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
4103 currentEventList->count++;
4104 }
4106 if (currentEventList->count == currentEventList->capacity) return; // Security check
4108 // Event type: INPUT_TOUCH_POSITION
4109 // TODO: It requires the id!
4110 /*
4111 if (((int)CORE.Input.Touch.currentPosition[id].x != (int)CORE.Input.Touch.previousPosition[id].x) ||
4112 ((int)CORE.Input.Touch.currentPosition[id].y != (int)CORE.Input.Touch.previousPosition[id].y))
4113 {
4114 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
4115 currentEventList->events[currentEventList->count].type = INPUT_TOUCH_POSITION;
4116 currentEventList->events[currentEventList->count].params[0] = id;
4117 currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Touch.currentPosition[id].x;
4118 currentEventList->events[currentEventList->count].params[2] = (int)CORE.Input.Touch.currentPosition[id].y;
4120 TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_POSITION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
4121 currentEventList->count++;
4122 }
4123 */
4125 if (currentEventList->count == currentEventList->capacity) return; // Security check
4126 }
4127 //-------------------------------------------------------------------------------------
4129 // Gamepad input currentEventList->events recording
4130 //-------------------------------------------------------------------------------------
4131 for (int gamepad = 0; gamepad < MAX_GAMEPADS; gamepad++)
4132 {
4133 // Event type: INPUT_GAMEPAD_CONNECT
4134 /*
4135 if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) &&
4136 (CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to ready
4137 {
4138 // TODO: Save gamepad connect event
4139 }
4140 */
4142 // Event type: INPUT_GAMEPAD_DISCONNECT
4143 /*
4144 if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) &&
4145 (!CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to not-ready
4146 {
4147 // TODO: Save gamepad disconnect event
4148 }
4149 */
4151 for (int button = 0; button < MAX_GAMEPAD_BUTTONS; button++)
4152 {
4153 // Event type: INPUT_GAMEPAD_BUTTON_UP
4154 if (CORE.Input.Gamepad.previousButtonState[gamepad][button] && !CORE.Input.Gamepad.currentButtonState[gamepad][button])
4155 {
4156 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
4157 currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_BUTTON_UP;
4158 currentEventList->events[currentEventList->count].params[0] = gamepad;
4159 currentEventList->events[currentEventList->count].params[1] = button;
4160 currentEventList->events[currentEventList->count].params[2] = 0;
4162 TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_BUTTON_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
4163 currentEventList->count++;
4164 }
4166 if (currentEventList->count == currentEventList->capacity) return; // Security check
4168 // Event type: INPUT_GAMEPAD_BUTTON_DOWN
4169 if (CORE.Input.Gamepad.currentButtonState[gamepad][button])
4170 {
4171 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
4172 currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_BUTTON_DOWN;
4173 currentEventList->events[currentEventList->count].params[0] = gamepad;
4174 currentEventList->events[currentEventList->count].params[1] = button;
4175 currentEventList->events[currentEventList->count].params[2] = 0;
4177 TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_BUTTON_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
4178 currentEventList->count++;
4179 }
4181 if (currentEventList->count == currentEventList->capacity) return; // Security check
4182 }
4184 for (int axis = 0; axis < MAX_GAMEPAD_AXES; axis++)
4185 {
4186 // Event type: INPUT_GAMEPAD_AXIS_MOTION
4187 float defaultMovement = ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER))? -1.0f : 0.0f;
4188 if (GetGamepadAxisMovement(gamepad, axis) != defaultMovement)
4189 {
4190 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
4191 currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_AXIS_MOTION;
4192 currentEventList->events[currentEventList->count].params[0] = gamepad;
4193 currentEventList->events[currentEventList->count].params[1] = axis;
4194 currentEventList->events[currentEventList->count].params[2] = (int)(CORE.Input.Gamepad.axisState[gamepad][axis]*32768.0f);
4196 TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_AXIS_MOTION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
4197 currentEventList->count++;
4198 }
4200 if (currentEventList->count == currentEventList->capacity) return; // Security check
4201 }
4202 }
4203 //-------------------------------------------------------------------------------------
4205#if defined(SUPPORT_GESTURES_SYSTEM)
4206 // Gestures input currentEventList->events recording
4207 //-------------------------------------------------------------------------------------
4208 if (GESTURES.current != GESTURE_NONE)
4209 {
4210 // Event type: INPUT_GESTURE
4211 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
4212 currentEventList->events[currentEventList->count].type = INPUT_GESTURE;
4213 currentEventList->events[currentEventList->count].params[0] = GESTURES.current;
4214 currentEventList->events[currentEventList->count].params[1] = 0;
4215 currentEventList->events[currentEventList->count].params[2] = 0;
4217 TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GESTURE | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
4218 currentEventList->count++;
4220 if (currentEventList->count == currentEventList->capacity) return; // Security check
4221 }
4222 //-------------------------------------------------------------------------------------
4223#endif
4224}
4225#endif
4227#if !defined(SUPPORT_MODULE_RTEXT)
4228// Formatting of text with variables to 'embed'
4229// WARNING: String returned will expire after this function is called MAX_TEXTFORMAT_BUFFERS times
4230const char *TextFormat(const char *text, ...)
4231{
4232#ifndef MAX_TEXTFORMAT_BUFFERS
4233 #define MAX_TEXTFORMAT_BUFFERS 4 // Maximum number of static buffers for text formatting
4234#endif
4235#ifndef MAX_TEXT_BUFFER_LENGTH
4236 #define MAX_TEXT_BUFFER_LENGTH 1024 // Maximum size of static text buffer
4237#endif
4239 // We create an array of buffers so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations
4240 static char buffers[MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH] = { 0 };
4241 static int index = 0;
4243 char *currentBuffer = buffers[index];
4244 memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH); // Clear buffer before using
4246 if (text != NULL)
4247 {
4248 va_list args;
4249 va_start(args, text);
4250 int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
4251 va_end(args);
4253 // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occurred
4254 if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
4255 {
4256 // Inserting "..." at the end of the string to mark as truncated
4257 char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0"
4258 snprintf(truncBuffer, 4, "...");
4259 }
4261 index += 1; // Move to next buffer for next function call
4262 if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
4263 }
4265 return currentBuffer;
4266}
4268#endif // !SUPPORT_MODULE_RTEXT
index : raylib-jai
---