aboutsummaryrefslogtreecommitdiff
path: root/raylib/src/rcore.c
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano@tronto.net>2025-04-21 11:09:56 +0200
committerSebastiano Tronto <sebastiano@tronto.net>2025-04-21 11:09:56 +0200
commit123144c93bfc77883c8fb517828b47bbe13b8671 (patch)
tree762739afedb5f3f168051367515cb9b9a06ec68d /raylib/src/rcore.c
downloadminesweeper-master.tar.gz
minesweeper-master.zip
Initial commitHEADmaster
Diffstat (limited to '')
-rw-r--r--raylib/src/rcore.c4070
1 files changed, 4070 insertions, 0 deletions
diff --git a/raylib/src/rcore.c b/raylib/src/rcore.c
new file mode 100644
index 0000000..ba23df7
--- /dev/null
+++ b/raylib/src/rcore.c
@@ -0,0 +1,4070 @@
1/**********************************************************************************************
2*
3* rcore - Window/display management, Graphic device/context management and input management
4*
5* PLATFORMS SUPPORTED:
6* > PLATFORM_DESKTOP_GLFW (GLFW backend):
7* - Windows (Win32, Win64)
8* - Linux (X11/Wayland desktop mode)
9* - macOS/OSX (x64, arm64)
10* - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop)
11* > PLATFORM_DESKTOP_SDL (SDL backend):
12* - Windows (Win32, Win64)
13* - Linux (X11/Wayland desktop mode)
14* - Others (not tested)
15* > PLATFORM_WEB:
16* - HTML5 (WebAssembly)
17* > PLATFORM_DRM:
18* - Raspberry Pi 0-5 (DRM/KMS)
19* - Linux DRM subsystem (KMS mode)
20* > PLATFORM_ANDROID:
21* - Android (ARM, ARM64)
22*
23* CONFIGURATION:
24* #define SUPPORT_DEFAULT_FONT (default)
25* Default font is loaded on window initialization to be available for the user to render simple text.
26* NOTE: If enabled, uses external module functions to load default raylib font (module: text)
27*
28* #define SUPPORT_CAMERA_SYSTEM
29* Camera module is included (rcamera.h) and multiple predefined cameras are available:
30* free, 1st/3rd person, orbital, custom
31*
32* #define SUPPORT_GESTURES_SYSTEM
33* Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag
34*
35* #define SUPPORT_MOUSE_GESTURES
36* Mouse gestures are directly mapped like touches and processed by gestures system.
37*
38* #define SUPPORT_BUSY_WAIT_LOOP
39* Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used
40*
41* #define SUPPORT_PARTIALBUSY_WAIT_LOOP
42* 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
43*
44* #define SUPPORT_SCREEN_CAPTURE
45* Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()
46*
47* #define SUPPORT_GIF_RECORDING
48* Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback()
49*
50* #define SUPPORT_COMPRESSION_API
51* Support CompressData() and DecompressData() functions, those functions use zlib implementation
52* provided by stb_image and stb_image_write libraries, so, those libraries must be enabled on textures module
53* for linkage
54*
55* #define SUPPORT_AUTOMATION_EVENTS
56* Support automatic events recording and playing, useful for automated testing systems or AI based game playing
57*
58* DEPENDENCIES:
59* raymath - 3D math functionality (Vector2, Vector3, Matrix, Quaternion)
60* camera - Multiple 3D camera modes (free, orbital, 1st person, 3rd person)
61* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs)
62*
63*
64* LICENSE: zlib/libpng
65*
66* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
67*
68* This software is provided "as-is", without any express or implied warranty. In no event
69* will the authors be held liable for any damages arising from the use of this software.
70*
71* Permission is granted to anyone to use this software for any purpose, including commercial
72* applications, and to alter it and redistribute it freely, subject to the following restrictions:
73*
74* 1. The origin of this software must not be misrepresented; you must not claim that you
75* wrote the original software. If you use this software in a product, an acknowledgment
76* in the product documentation would be appreciated but is not required.
77*
78* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
79* as being the original software.
80*
81* 3. This notice may not be removed or altered from any source distribution.
82*
83**********************************************************************************************/
84
85//----------------------------------------------------------------------------------
86// Feature Test Macros required for this module
87//----------------------------------------------------------------------------------
88#if (defined(__linux__) || defined(PLATFORM_WEB)) && (_XOPEN_SOURCE < 500)
89 #undef _XOPEN_SOURCE
90 #define _XOPEN_SOURCE 500 // Required for: readlink if compiled with c99 without gnu ext.
91#endif
92
93#if (defined(__linux__) || defined(PLATFORM_WEB)) && (_POSIX_C_SOURCE < 199309L)
94 #undef _POSIX_C_SOURCE
95 #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext.
96#endif
97
98#include "raylib.h" // Declares module functions
99
100// Check if config flags have been externally provided on compilation line
101#if !defined(EXTERNAL_CONFIG_FLAGS)
102 #include "config.h" // Defines module configuration flags
103#endif
104
105#include "utils.h" // Required for: TRACELOG() macros
106
107#include <stdlib.h> // Required for: srand(), rand(), atexit()
108#include <stdio.h> // Required for: sprintf() [Used in OpenURL()]
109#include <string.h> // Required for: strrchr(), strcmp(), strlen(), memset()
110#include <time.h> // Required for: time() [Used in InitTimer()]
111#include <math.h> // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()]
112
113#define RLGL_IMPLEMENTATION
114#include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
115
116#define RAYMATH_IMPLEMENTATION
117#include "raymath.h" // Vector2, Vector3, Quaternion and Matrix functionality
118
119#if defined(SUPPORT_GESTURES_SYSTEM)
120 #define RGESTURES_IMPLEMENTATION
121 #include "rgestures.h" // Gestures detection functionality
122#endif
123
124#if defined(SUPPORT_CAMERA_SYSTEM)
125 #define RCAMERA_IMPLEMENTATION
126 #include "rcamera.h" // Camera system functionality
127#endif
128
129#if defined(SUPPORT_GIF_RECORDING)
130 #define MSF_GIF_MALLOC(contextPointer, newSize) RL_MALLOC(newSize)
131 #define MSF_GIF_REALLOC(contextPointer, oldMemory, oldSize, newSize) RL_REALLOC(oldMemory, newSize)
132 #define MSF_GIF_FREE(contextPointer, oldMemory, oldSize) RL_FREE(oldMemory)
133
134 #define MSF_GIF_IMPL
135 #include "external/msf_gif.h" // GIF recording functionality
136#endif
137
138#if defined(SUPPORT_COMPRESSION_API)
139 #define SINFL_IMPLEMENTATION
140 #define SINFL_NO_SIMD
141 #include "external/sinfl.h" // Deflate (RFC 1951) decompressor
142
143 #define SDEFL_IMPLEMENTATION
144 #include "external/sdefl.h" // Deflate (RFC 1951) compressor
145#endif
146
147#if defined(SUPPORT_RPRAND_GENERATOR)
148 #define RPRAND_IMPLEMENTATION
149 #include "external/rprand.h"
150#endif
151
152#if defined(__linux__) && !defined(_GNU_SOURCE)
153 #define _GNU_SOURCE
154#endif
155
156// Platform specific defines to handle GetApplicationDirectory()
157#if (defined(_WIN32) && !defined(PLATFORM_DESKTOP_RGFW)) || (defined(_MSC_VER) && defined(PLATFORM_DESKTOP_RGFW))
158 #ifndef MAX_PATH
159 #define MAX_PATH 1025
160 #endif
161__declspec(dllimport) unsigned long __stdcall GetModuleFileNameA(void *hModule, void *lpFilename, unsigned long nSize);
162__declspec(dllimport) unsigned long __stdcall GetModuleFileNameW(void *hModule, void *lpFilename, unsigned long nSize);
163__declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, void *widestr, int cchwide, void *str, int cbmb, void *defchar, int *used_default);
164__declspec(dllimport) unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod);
165__declspec(dllimport) unsigned int __stdcall timeEndPeriod(unsigned int uPeriod);
166#elif defined(__linux__)
167 #include <unistd.h>
168#elif defined(__FreeBSD__)
169 #include <sys/types.h>
170 #include <sys/sysctl.h>
171 #include <unistd.h>
172#elif defined(__APPLE__)
173 #include <sys/syslimits.h>
174 #include <mach-o/dyld.h>
175#endif // OSs
176
177#define _CRT_INTERNAL_NONSTDC_NAMES 1
178#include <sys/stat.h> // Required for: stat(), S_ISREG [Used in GetFileModTime(), IsFilePath()]
179
180#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
181 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
182#endif
183
184#if defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__))
185 #define DIRENT_MALLOC RL_MALLOC
186 #define DIRENT_FREE RL_FREE
187
188 #include "external/dirent.h" // Required for: DIR, opendir(), closedir() [Used in LoadDirectoryFiles()]
189#else
190 #include <dirent.h> // Required for: DIR, opendir(), closedir() [Used in LoadDirectoryFiles()]
191#endif
192
193#if defined(_WIN32)
194 #include <io.h> // Required for: _access() [Used in FileExists()]
195 #include <direct.h> // Required for: _getch(), _chdir(), _mkdir()
196 #define GETCWD _getcwd // NOTE: MSDN recommends not to use getcwd(), chdir()
197 #define CHDIR _chdir
198 #define MKDIR(dir) _mkdir(dir)
199#else
200 #include <unistd.h> // Required for: getch(), chdir(), mkdir(), access()
201 #define GETCWD getcwd
202 #define CHDIR chdir
203 #define MKDIR(dir) mkdir(dir, 0777)
204#endif
205
206//----------------------------------------------------------------------------------
207// Defines and Macros
208//----------------------------------------------------------------------------------
209#ifndef MAX_FILEPATH_CAPACITY
210 #define MAX_FILEPATH_CAPACITY 8192 // Maximum capacity for filepath
211#endif
212#ifndef MAX_FILEPATH_LENGTH
213 #if defined(_WIN32)
214 #define MAX_FILEPATH_LENGTH 256 // On Win32, MAX_PATH = 260 (limits.h) but Windows 10, Version 1607 enables long paths...
215 #else
216 #define MAX_FILEPATH_LENGTH 4096 // On Linux, PATH_MAX = 4096 by default (limits.h)
217 #endif
218#endif
219
220#ifndef MAX_KEYBOARD_KEYS
221 #define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported
222#endif
223#ifndef MAX_MOUSE_BUTTONS
224 #define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported
225#endif
226#ifndef MAX_GAMEPADS
227 #define MAX_GAMEPADS 4 // Maximum number of gamepads supported
228#endif
229#ifndef MAX_GAMEPAD_AXIS
230 #define MAX_GAMEPAD_AXIS 8 // Maximum number of axis supported (per gamepad)
231#endif
232#ifndef MAX_GAMEPAD_BUTTONS
233 #define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad)
234#endif
235#ifndef MAX_GAMEPAD_VIBRATION_TIME
236 #define MAX_GAMEPAD_VIBRATION_TIME 2.0f // Maximum vibration time in seconds
237#endif
238#ifndef MAX_TOUCH_POINTS
239 #define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported
240#endif
241#ifndef MAX_KEY_PRESSED_QUEUE
242 #define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue
243#endif
244#ifndef MAX_CHAR_PRESSED_QUEUE
245 #define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue
246#endif
247
248#ifndef MAX_DECOMPRESSION_SIZE
249 #define MAX_DECOMPRESSION_SIZE 64 // Maximum size allocated for decompression in MB
250#endif
251
252#ifndef MAX_AUTOMATION_EVENTS
253 #define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record
254#endif
255
256#ifndef DIRECTORY_FILTER_TAG
257 #define DIRECTORY_FILTER_TAG "DIR" // Name tag used to request directory inclusion on directory scan
258#endif // NOTE: Used in ScanDirectoryFiles(), ScanDirectoryFilesRecursively() and LoadDirectoryFilesEx()
259
260// Flags operation macros
261#define FLAG_SET(n, f) ((n) |= (f))
262#define FLAG_CLEAR(n, f) ((n) &= ~(f))
263#define FLAG_TOGGLE(n, f) ((n) ^= (f))
264#define FLAG_CHECK(n, f) ((n) & (f))
265
266//----------------------------------------------------------------------------------
267// Types and Structures Definition
268//----------------------------------------------------------------------------------
269typedef struct { int x; int y; } Point;
270typedef struct { unsigned int width; unsigned int height; } Size;
271
272// Core global state context data
273typedef struct CoreData {
274 struct {
275 const char *title; // Window text title const pointer
276 unsigned int flags; // Configuration flags (bit based), keeps window state
277 bool ready; // Check if window has been initialized successfully
278 bool fullscreen; // Check if fullscreen mode is enabled
279 bool shouldClose; // Check if window set for closing
280 bool resizedLastFrame; // Check if window has been resized last frame
281 bool eventWaiting; // Wait for events before ending frame
282 bool usingFbo; // Using FBO (RenderTexture) for rendering instead of default framebuffer
283
284 Point position; // Window position (required on fullscreen toggle)
285 Point previousPosition; // Window previous position (required on borderless windowed toggle)
286 Size display; // Display width and height (monitor, device-screen, LCD, ...)
287 Size screen; // Screen width and height (used render area)
288 Size previousScreen; // Screen previous width and height (required on borderless windowed toggle)
289 Size currentFbo; // Current render width and height (depends on active fbo)
290 Size render; // Framebuffer width and height (render area, including black bars if required)
291 Point renderOffset; // Offset from render area (must be divided by 2)
292 Size screenMin; // Screen minimum width and height (for resizable window)
293 Size screenMax; // Screen maximum width and height (for resizable window)
294 Matrix screenScale; // Matrix to scale screen (framebuffer rendering)
295
296 char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW)
297 unsigned int dropFileCount; // Count dropped files strings
298
299 } Window;
300 struct {
301 const char *basePath; // Base path for data storage
302
303 } Storage;
304 struct {
305 struct {
306 int exitKey; // Default exit key
307 char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state
308 char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state
309
310 // NOTE: Since key press logic involves comparing prev vs cur key state, we need to handle key repeats specially
311 char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame
312
313 int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue
314 int keyPressedQueueCount; // Input keys queue count
315
316 int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode)
317 int charPressedQueueCount; // Input characters queue count
318
319 } Keyboard;
320 struct {
321 Vector2 offset; // Mouse offset
322 Vector2 scale; // Mouse scaling
323 Vector2 currentPosition; // Mouse position on screen
324 Vector2 previousPosition; // Previous mouse position
325
326 int cursor; // Tracks current mouse cursor
327 bool cursorHidden; // Track if cursor is hidden
328 bool cursorOnScreen; // Tracks if cursor is inside client area
329
330 char currentButtonState[MAX_MOUSE_BUTTONS]; // Registers current mouse button state
331 char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state
332 Vector2 currentWheelMove; // Registers current mouse wheel variation
333 Vector2 previousWheelMove; // Registers previous mouse wheel variation
334
335 } Mouse;
336 struct {
337 int pointCount; // Number of touch points active
338 int pointId[MAX_TOUCH_POINTS]; // Point identifiers
339 Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen
340 char currentTouchState[MAX_TOUCH_POINTS]; // Registers current touch state
341 char previousTouchState[MAX_TOUCH_POINTS]; // Registers previous touch state
342
343 } Touch;
344 struct {
345 int lastButtonPressed; // Register last gamepad button pressed
346 int axisCount[MAX_GAMEPADS]; // Register number of available gamepad axis
347 bool ready[MAX_GAMEPADS]; // Flag to know if gamepad is ready
348 char name[MAX_GAMEPADS][64]; // Gamepad name holder
349 char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state
350 char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state
351 float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state
352
353 } Gamepad;
354 } Input;
355 struct {
356 double current; // Current time measure
357 double previous; // Previous time measure
358 double update; // Time measure for frame update
359 double draw; // Time measure for frame draw
360 double frame; // Time measure for one frame
361 double target; // Desired time for one frame, if 0 not applied
362 unsigned long long int base; // Base time measure for hi-res timer (PLATFORM_ANDROID, PLATFORM_DRM)
363 unsigned int frameCounter; // Frame counter
364
365 } Time;
366} CoreData;
367
368//----------------------------------------------------------------------------------
369// Global Variables Definition
370//----------------------------------------------------------------------------------
371RLAPI const char *raylib_version = RAYLIB_VERSION; // raylib version exported symbol, required for some bindings
372
373CoreData CORE = { 0 }; // Global CORE state context
374
375// Flag to note GPU acceleration is available,
376// referenced from other modules to support GPU data loading
377// NOTE: Useful to allow Texture, RenderTexture, Font.texture, Mesh.vaoId/vboId, Shader loading
378bool isGpuReady = false;
379
380#if defined(SUPPORT_SCREEN_CAPTURE)
381static int screenshotCounter = 0; // Screenshots counter
382#endif
383
384#if defined(SUPPORT_GIF_RECORDING)
385static unsigned int gifFrameCounter = 0; // GIF frames counter
386static bool gifRecording = false; // GIF recording state
387static MsfGifState gifState = { 0 }; // MSGIF context state
388#endif
389
390#if defined(SUPPORT_AUTOMATION_EVENTS)
391// Automation events type
392typedef enum AutomationEventType {
393 EVENT_NONE = 0,
394 // Input events
395 INPUT_KEY_UP, // param[0]: key
396 INPUT_KEY_DOWN, // param[0]: key
397 INPUT_KEY_PRESSED, // param[0]: key
398 INPUT_KEY_RELEASED, // param[0]: key
399 INPUT_MOUSE_BUTTON_UP, // param[0]: button
400 INPUT_MOUSE_BUTTON_DOWN, // param[0]: button
401 INPUT_MOUSE_POSITION, // param[0]: x, param[1]: y
402 INPUT_MOUSE_WHEEL_MOTION, // param[0]: x delta, param[1]: y delta
403 INPUT_GAMEPAD_CONNECT, // param[0]: gamepad
404 INPUT_GAMEPAD_DISCONNECT, // param[0]: gamepad
405 INPUT_GAMEPAD_BUTTON_UP, // param[0]: button
406 INPUT_GAMEPAD_BUTTON_DOWN, // param[0]: button
407 INPUT_GAMEPAD_AXIS_MOTION, // param[0]: axis, param[1]: delta
408 INPUT_TOUCH_UP, // param[0]: id
409 INPUT_TOUCH_DOWN, // param[0]: id
410 INPUT_TOUCH_POSITION, // param[0]: x, param[1]: y
411 INPUT_GESTURE, // param[0]: gesture
412 // Window events
413 WINDOW_CLOSE, // no params
414 WINDOW_MAXIMIZE, // no params
415 WINDOW_MINIMIZE, // no params
416 WINDOW_RESIZE, // param[0]: width, param[1]: height
417 // Custom events
418 ACTION_TAKE_SCREENSHOT, // no params
419 ACTION_SETTARGETFPS // param[0]: fps
420} AutomationEventType;
421
422// Event type to config events flags
423// TODO: Not used at the moment
424typedef enum {
425 EVENT_INPUT_KEYBOARD = 0,
426 EVENT_INPUT_MOUSE = 1,
427 EVENT_INPUT_GAMEPAD = 2,
428 EVENT_INPUT_TOUCH = 4,
429 EVENT_INPUT_GESTURE = 8,
430 EVENT_WINDOW = 16,
431 EVENT_CUSTOM = 32
432} EventType;
433
434// Event type name strings, required for export
435static const char *autoEventTypeName[] = {
436 "EVENT_NONE",
437 "INPUT_KEY_UP",
438 "INPUT_KEY_DOWN",
439 "INPUT_KEY_PRESSED",
440 "INPUT_KEY_RELEASED",
441 "INPUT_MOUSE_BUTTON_UP",
442 "INPUT_MOUSE_BUTTON_DOWN",
443 "INPUT_MOUSE_POSITION",
444 "INPUT_MOUSE_WHEEL_MOTION",
445 "INPUT_GAMEPAD_CONNECT",
446 "INPUT_GAMEPAD_DISCONNECT",
447 "INPUT_GAMEPAD_BUTTON_UP",
448 "INPUT_GAMEPAD_BUTTON_DOWN",
449 "INPUT_GAMEPAD_AXIS_MOTION",
450 "INPUT_TOUCH_UP",
451 "INPUT_TOUCH_DOWN",
452 "INPUT_TOUCH_POSITION",
453 "INPUT_GESTURE",
454 "WINDOW_CLOSE",
455 "WINDOW_MAXIMIZE",
456 "WINDOW_MINIMIZE",
457 "WINDOW_RESIZE",
458 "ACTION_TAKE_SCREENSHOT",
459 "ACTION_SETTARGETFPS"
460};
461
462/*
463// Automation event (24 bytes)
464// NOTE: Opaque struct, internal to raylib
465struct AutomationEvent {
466 unsigned int frame; // Event frame
467 unsigned int type; // Event type (AutomationEventType)
468 int params[4]; // Event parameters (if required)
469};
470*/
471
472static AutomationEventList *currentEventList = NULL; // Current automation events list, set by user, keep internal pointer
473static bool automationEventRecording = false; // Recording automation events flag
474//static short automationEventEnabled = 0b0000001111111111; // TODO: Automation events enabled for recording/playing
475#endif
476//-----------------------------------------------------------------------------------
477
478//----------------------------------------------------------------------------------
479// Module Functions Declaration
480// NOTE: Those functions are common for all platforms!
481//----------------------------------------------------------------------------------
482
483#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
484extern void LoadFontDefault(void); // [Module: text] Loads default font on InitWindow()
485extern void UnloadFontDefault(void); // [Module: text] Unloads default font from GPU memory
486#endif
487
488extern int InitPlatform(void); // Initialize platform (graphics, inputs and more)
489extern void ClosePlatform(void); // Close platform
490
491static void InitTimer(void); // Initialize timer, hi-resolution if available (required by InitPlatform())
492static void SetupFramebuffer(int width, int height); // Setup main framebuffer (required by InitPlatform())
493static void SetupViewport(int width, int height); // Set viewport for a provided width and height
494
495static void ScanDirectoryFiles(const char *basePath, FilePathList *list, const char *filter); // Scan all files and directories in a base path
496static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *list, const char *filter); // Scan all files and directories recursively from a base path
497
498#if defined(SUPPORT_AUTOMATION_EVENTS)
499static void RecordAutomationEvent(void); // Record frame events (to internal events array)
500#endif
501
502#if defined(_WIN32) && !defined(PLATFORM_DESKTOP_RGFW)
503// NOTE: We declare Sleep() function symbol to avoid including windows.h (kernel32.lib linkage required)
504void __stdcall Sleep(unsigned long msTimeout); // Required for: WaitTime()
505#endif
506
507#if !defined(SUPPORT_MODULE_RTEXT)
508const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed'
509#endif // !SUPPORT_MODULE_RTEXT
510
511#if defined(PLATFORM_DESKTOP)
512 #define PLATFORM_DESKTOP_GLFW
513#endif
514
515// We're using `#pragma message` because `#warning` is not adopted by MSVC.
516#if defined(SUPPORT_CLIPBOARD_IMAGE)
517 #if !defined(SUPPORT_MODULE_RTEXTURES)
518 #pragma message ("Warning: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly")
519 #endif
520
521 // It's nice to have support Bitmap on Linux as well, but not as necessary as Windows
522 #if !defined(SUPPORT_FILEFORMAT_BMP) && defined(_WIN32)
523 #pragma message ("Warning: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_FILEFORMAT_BMP, specially on Windows")
524 #endif
525
526 // From what I've tested applications on Wayland saves images on clipboard as PNG.
527 #if (!defined(SUPPORT_FILEFORMAT_PNG) || !defined(SUPPORT_FILEFORMAT_JPG)) && !defined(_WIN32)
528 #pragma message ("Warning: Getting image from the clipboard might not work without SUPPORT_FILEFORMAT_PNG or SUPPORT_FILEFORMAT_JPG")
529 #endif
530
531 // Not needed because `rtexture.c` will automatically defined STBI_REQUIRED when any SUPPORT_FILEFORMAT_* is defined.
532 // #if !defined(STBI_REQUIRED)
533 // #pragma message ("Warning: "STBI_REQUIRED is not defined, that means we can't load images from clipbard"
534 // #endif
535
536#endif // SUPPORT_CLIPBOARD_IMAGE
537
538// Include platform-specific submodules
539#if defined(PLATFORM_DESKTOP_GLFW)
540 #include "platforms/rcore_desktop_glfw.c"
541#elif defined(PLATFORM_DESKTOP_SDL)
542 #include "platforms/rcore_desktop_sdl.c"
543#elif defined(PLATFORM_DESKTOP_RGFW)
544 #include "platforms/rcore_desktop_rgfw.c"
545#elif defined(PLATFORM_WEB)
546 #include "platforms/rcore_web.c"
547#elif defined(PLATFORM_DRM)
548 #include "platforms/rcore_drm.c"
549#elif defined(PLATFORM_ANDROID)
550 #include "platforms/rcore_android.c"
551#else
552 // TODO: Include your custom platform backend!
553 // i.e software rendering backend or console backend!
554#endif
555
556//----------------------------------------------------------------------------------
557// Module Functions Definition: Window and Graphics Device
558//----------------------------------------------------------------------------------
559
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)
567
568//void SetWindowState(unsigned int flags)
569//void ClearWindowState(unsigned int flags)
570
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)
584
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)
594
595//void SetClipboardText(const char *text)
596//const char *GetClipboardText(void)
597
598//void ShowCursor(void)
599//void HideCursor(void)
600//void EnableCursor(void)
601//void DisableCursor(void)
602
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);
607
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_WEB)
615 TRACELOG(LOG_INFO, "Platform backend: WEB (HTML5)");
616#elif defined(PLATFORM_DRM)
617 TRACELOG(LOG_INFO, "Platform backend: NATIVE DRM");
618#elif defined(PLATFORM_ANDROID)
619 TRACELOG(LOG_INFO, "Platform backend: ANDROID");
620#else
621 // TODO: Include your custom platform backend!
622 // i.e software rendering backend or console backend!
623 TRACELOG(LOG_INFO, "Platform backend: CUSTOM");
624#endif
625
626 TRACELOG(LOG_INFO, "Supported raylib modules:");
627 TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)");
628 TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)");
629#if defined(SUPPORT_MODULE_RSHAPES)
630 TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)");
631#else
632 TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)");
633#endif
634#if defined(SUPPORT_MODULE_RTEXTURES)
635 TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)");
636#else
637 TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)");
638#endif
639#if defined(SUPPORT_MODULE_RTEXT)
640 TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)");
641#else
642 TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)");
643#endif
644#if defined(SUPPORT_MODULE_RMODELS)
645 TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)");
646#else
647 TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)");
648#endif
649#if defined(SUPPORT_MODULE_RAUDIO)
650 TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)");
651#else
652 TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)");
653#endif
654
655 // Initialize window data
656 CORE.Window.screen.width = width;
657 CORE.Window.screen.height = height;
658 CORE.Window.eventWaiting = false;
659 CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default
660 if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title;
661
662 // Initialize global input state
663 memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0
664 CORE.Input.Keyboard.exitKey = KEY_ESCAPE;
665 CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f };
666 CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW;
667 CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN;
668
669 // Initialize platform
670 //--------------------------------------------------------------
671 InitPlatform();
672 //--------------------------------------------------------------
673
674 // Initialize rlgl default data (buffers and shaders)
675 // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
676 rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
677 isGpuReady = true; // Flag to note GPU has been initialized successfully
678
679 // Setup default viewport
680 SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
681
682#if defined(SUPPORT_MODULE_RTEXT)
683 #if defined(SUPPORT_DEFAULT_FONT)
684 // Load default font
685 // WARNING: External function: Module required: rtext
686 LoadFontDefault();
687 #if defined(SUPPORT_MODULE_RSHAPES)
688 // Set font white rectangle for shapes drawing, so shapes and text can be batched together
689 // WARNING: rshapes module is required, if not available, default internal white rectangle is used
690 Rectangle rec = GetFontDefault().recs[95];
691 if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
692 {
693 // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering
694 SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 });
695 }
696 else
697 {
698 // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding
699 SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 });
700 }
701 #endif
702 #endif
703#else
704 #if defined(SUPPORT_MODULE_RSHAPES)
705 // Set default texture and rectangle to be used for shapes drawing
706 // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
707 Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
708 SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes
709 #endif
710#endif
711
712 CORE.Time.frameCounter = 0;
713 CORE.Window.shouldClose = false;
714
715 // Initialize random seed
716 SetRandomSeed((unsigned int)time(NULL));
717
718 TRACELOG(LOG_INFO, "SYSTEM: Working Directory: %s", GetWorkingDirectory());
719}
720
721// Close window and unload OpenGL context
722void CloseWindow(void)
723{
724#if defined(SUPPORT_GIF_RECORDING)
725 if (gifRecording)
726 {
727 MsfGifResult result = msf_gif_end(&gifState);
728 msf_gif_free(result);
729 gifRecording = false;
730 }
731#endif
732
733#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
734 UnloadFontDefault(); // WARNING: Module required: rtext
735#endif
736
737 rlglClose(); // De-init rlgl
738
739 // De-initialize platform
740 //--------------------------------------------------------------
741 ClosePlatform();
742 //--------------------------------------------------------------
743
744 CORE.Window.ready = false;
745 TRACELOG(LOG_INFO, "Window closed successfully");
746}
747
748// Check if window has been initialized successfully
749bool IsWindowReady(void)
750{
751 return CORE.Window.ready;
752}
753
754// Check if window is currently fullscreen
755bool IsWindowFullscreen(void)
756{
757 return CORE.Window.fullscreen;
758}
759
760// Check if window is currently hidden
761bool IsWindowHidden(void)
762{
763 return ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0);
764}
765
766// Check if window has been minimized
767bool IsWindowMinimized(void)
768{
769 return ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0);
770}
771
772// Check if window has been maximized
773bool IsWindowMaximized(void)
774{
775 return ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0);
776}
777
778// Check if window has the focus
779bool IsWindowFocused(void)
780{
781 return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0);
782}
783
784// Check if window has been resizedLastFrame
785bool IsWindowResized(void)
786{
787 return CORE.Window.resizedLastFrame;
788}
789
790// Check if one specific window flag is enabled
791bool IsWindowState(unsigned int flag)
792{
793 return ((CORE.Window.flags & flag) > 0);
794}
795
796// Get current screen width
797int GetScreenWidth(void)
798{
799 return CORE.Window.screen.width;
800}
801
802// Get current screen height
803int GetScreenHeight(void)
804{
805 return CORE.Window.screen.height;
806}
807
808// Get current render width which is equal to screen width*dpi scale
809int GetRenderWidth(void)
810{
811 int width = 0;
812#if defined(__APPLE__)
813 Vector2 scale = GetWindowScaleDPI();
814 width = (int)((float)CORE.Window.render.width*scale.x);
815#else
816 width = CORE.Window.render.width;
817#endif
818 return width;
819}
820
821// Get current screen height which is equal to screen height*dpi scale
822int GetRenderHeight(void)
823{
824 int height = 0;
825#if defined(__APPLE__)
826 Vector2 scale = GetWindowScaleDPI();
827 height = (int)((float)CORE.Window.render.height*scale.y);
828#else
829 height = CORE.Window.render.height;
830#endif
831 return height;
832}
833
834// Enable waiting for events on EndDrawing(), no automatic event polling
835void EnableEventWaiting(void)
836{
837 CORE.Window.eventWaiting = true;
838}
839
840// Disable waiting for events on EndDrawing(), automatic events polling
841void DisableEventWaiting(void)
842{
843 CORE.Window.eventWaiting = false;
844}
845
846// Check if cursor is not visible
847bool IsCursorHidden(void)
848{
849 return CORE.Input.Mouse.cursorHidden;
850}
851
852// Check if cursor is on the current screen
853bool IsCursorOnScreen(void)
854{
855 return CORE.Input.Mouse.cursorOnScreen;
856}
857
858//----------------------------------------------------------------------------------
859// Module Functions Definition: Screen Drawing
860//----------------------------------------------------------------------------------
861
862// Set background color (framebuffer clear color)
863void ClearBackground(Color color)
864{
865 rlClearColor(color.r, color.g, color.b, color.a); // Set clear color
866 rlClearScreenBuffers(); // Clear current framebuffers
867}
868
869// Setup canvas (framebuffer) to start drawing
870void BeginDrawing(void)
871{
872 // WARNING: Previously to BeginDrawing() other render textures drawing could happen,
873 // consequently the measure for update vs draw is not accurate (only the total frame time is accurate)
874
875 CORE.Time.current = GetTime(); // Number of elapsed seconds since InitTimer()
876 CORE.Time.update = CORE.Time.current - CORE.Time.previous;
877 CORE.Time.previous = CORE.Time.current;
878
879 rlLoadIdentity(); // Reset current matrix (modelview)
880 rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling
881
882 //rlTranslatef(0.375, 0.375, 0); // HACK to have 2D pixel-perfect drawing on OpenGL 1.1
883 // NOTE: Not required with OpenGL 3.3+
884}
885
886// End canvas drawing and swap buffers (double buffering)
887void EndDrawing(void)
888{
889 rlDrawRenderBatchActive(); // Update and draw internal render batch
890
891#if defined(SUPPORT_GIF_RECORDING)
892 // Draw record indicator
893 if (gifRecording)
894 {
895 #ifndef GIF_RECORD_FRAMERATE
896 #define GIF_RECORD_FRAMERATE 10
897 #endif
898 gifFrameCounter += (unsigned int)(GetFrameTime()*1000);
899
900 // NOTE: We record one gif frame depending on the desired gif framerate
901 if (gifFrameCounter > 1000/GIF_RECORD_FRAMERATE)
902 {
903 // Get image data for the current frame (from backbuffer)
904 // NOTE: This process is quite slow... :(
905 Vector2 scale = GetWindowScaleDPI();
906 unsigned char *screenData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
907
908 #ifndef GIF_RECORD_BITRATE
909 #define GIF_RECORD_BITRATE 16
910 #endif
911
912 // Add the frame to the gif recording, given how many frames have passed in centiseconds
913 msf_gif_frame(&gifState, screenData, gifFrameCounter/10, GIF_RECORD_BITRATE, (int)((float)CORE.Window.render.width*scale.x)*4);
914 gifFrameCounter -= 1000/GIF_RECORD_FRAMERATE;
915
916 RL_FREE(screenData); // Free image data
917 }
918
919 #if defined(SUPPORT_MODULE_RSHAPES) && defined(SUPPORT_MODULE_RTEXT)
920 // Display the recording indicator every half-second
921 if ((int)(GetTime()/0.5)%2 == 1)
922 {
923 DrawCircle(30, CORE.Window.screen.height - 20, 10, MAROON); // WARNING: Module required: rshapes
924 DrawText("GIF RECORDING", 50, CORE.Window.screen.height - 25, 10, RED); // WARNING: Module required: rtext
925 }
926 #endif
927
928 rlDrawRenderBatchActive(); // Update and draw internal render batch
929 }
930#endif
931
932#if defined(SUPPORT_AUTOMATION_EVENTS)
933 if (automationEventRecording) RecordAutomationEvent(); // Event recording
934#endif
935
936#if !defined(SUPPORT_CUSTOM_FRAME_CONTROL)
937 SwapScreenBuffer(); // Copy back buffer to front buffer (screen)
938
939 // Frame time control system
940 CORE.Time.current = GetTime();
941 CORE.Time.draw = CORE.Time.current - CORE.Time.previous;
942 CORE.Time.previous = CORE.Time.current;
943
944 CORE.Time.frame = CORE.Time.update + CORE.Time.draw;
945
946 // Wait for some milliseconds...
947 if (CORE.Time.frame < CORE.Time.target)
948 {
949 WaitTime(CORE.Time.target - CORE.Time.frame);
950
951 CORE.Time.current = GetTime();
952 double waitTime = CORE.Time.current - CORE.Time.previous;
953 CORE.Time.previous = CORE.Time.current;
954
955 CORE.Time.frame += waitTime; // Total frame time: update + draw + wait
956 }
957
958 PollInputEvents(); // Poll user events (before next frame update)
959#endif
960
961#if defined(SUPPORT_SCREEN_CAPTURE)
962 if (IsKeyPressed(KEY_F12))
963 {
964#if defined(SUPPORT_GIF_RECORDING)
965 if (IsKeyDown(KEY_LEFT_CONTROL))
966 {
967 if (gifRecording)
968 {
969 gifRecording = false;
970
971 MsfGifResult result = msf_gif_end(&gifState);
972
973 SaveFileData(TextFormat("%s/screenrec%03i.gif", CORE.Storage.basePath, screenshotCounter), result.data, (unsigned int)result.dataSize);
974 msf_gif_free(result);
975
976 TRACELOG(LOG_INFO, "SYSTEM: Finish animated GIF recording");
977 }
978 else
979 {
980 gifRecording = true;
981 gifFrameCounter = 0;
982
983 Vector2 scale = GetWindowScaleDPI();
984 msf_gif_begin(&gifState, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
985 screenshotCounter++;
986
987 TRACELOG(LOG_INFO, "SYSTEM: Start animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter));
988 }
989 }
990 else
991#endif // SUPPORT_GIF_RECORDING
992 {
993 TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
994 screenshotCounter++;
995 }
996 }
997#endif // SUPPORT_SCREEN_CAPTURE
998
999 CORE.Time.frameCounter++;
1000}
1001
1002// Initialize 2D mode with custom camera (2D)
1003void BeginMode2D(Camera2D camera)
1004{
1005 rlDrawRenderBatchActive(); // Update and draw internal render batch
1006
1007 rlLoadIdentity(); // Reset current matrix (modelview)
1008
1009 // Apply 2d camera transformation to modelview
1010 rlMultMatrixf(MatrixToFloat(GetCameraMatrix2D(camera)));
1011}
1012
1013// Ends 2D mode with custom camera
1014void EndMode2D(void)
1015{
1016 rlDrawRenderBatchActive(); // Update and draw internal render batch
1017
1018 rlLoadIdentity(); // Reset current matrix (modelview)
1019
1020 if (rlGetActiveFramebuffer() == 0) rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
1021}
1022
1023// Initializes 3D mode with custom camera (3D)
1024void BeginMode3D(Camera camera)
1025{
1026 rlDrawRenderBatchActive(); // Update and draw internal render batch
1027
1028 rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
1029 rlPushMatrix(); // Save previous matrix, which contains the settings for the 2d ortho projection
1030 rlLoadIdentity(); // Reset current matrix (projection)
1031
1032 float aspect = (float)CORE.Window.currentFbo.width/(float)CORE.Window.currentFbo.height;
1033
1034 // NOTE: zNear and zFar values are important when computing depth buffer values
1035 if (camera.projection == CAMERA_PERSPECTIVE)
1036 {
1037 // Setup perspective projection
1038 double top = rlGetCullDistanceNear()*tan(camera.fovy*0.5*DEG2RAD);
1039 double right = top*aspect;
1040
1041 rlFrustum(-right, right, -top, top, rlGetCullDistanceNear(), rlGetCullDistanceFar());
1042 }
1043 else if (camera.projection == CAMERA_ORTHOGRAPHIC)
1044 {
1045 // Setup orthographic projection
1046 double top = camera.fovy/2.0;
1047 double right = top*aspect;
1048
1049 rlOrtho(-right, right, -top,top, rlGetCullDistanceNear(), rlGetCullDistanceFar());
1050 }
1051
1052 rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
1053 rlLoadIdentity(); // Reset current matrix (modelview)
1054
1055 // Setup Camera view
1056 Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
1057 rlMultMatrixf(MatrixToFloat(matView)); // Multiply modelview matrix by view matrix (camera)
1058
1059 rlEnableDepthTest(); // Enable DEPTH_TEST for 3D
1060}
1061
1062// Ends 3D mode and returns to default 2D orthographic mode
1063void EndMode3D(void)
1064{
1065 rlDrawRenderBatchActive(); // Update and draw internal render batch
1066
1067 rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
1068 rlPopMatrix(); // Restore previous matrix (projection) from matrix stack
1069
1070 rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
1071 rlLoadIdentity(); // Reset current matrix (modelview)
1072
1073 if (rlGetActiveFramebuffer() == 0) rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
1074
1075 rlDisableDepthTest(); // Disable DEPTH_TEST for 2D
1076}
1077
1078// Initializes render texture for drawing
1079void BeginTextureMode(RenderTexture2D target)
1080{
1081 rlDrawRenderBatchActive(); // Update and draw internal render batch
1082
1083 rlEnableFramebuffer(target.id); // Enable render target
1084
1085 // Set viewport and RLGL internal framebuffer size
1086 rlViewport(0, 0, target.texture.width, target.texture.height);
1087 rlSetFramebufferWidth(target.texture.width);
1088 rlSetFramebufferHeight(target.texture.height);
1089
1090 rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
1091 rlLoadIdentity(); // Reset current matrix (projection)
1092
1093 // Set orthographic projection to current framebuffer size
1094 // NOTE: Configured top-left corner as (0, 0)
1095 rlOrtho(0, target.texture.width, target.texture.height, 0, 0.0f, 1.0f);
1096
1097 rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
1098 rlLoadIdentity(); // Reset current matrix (modelview)
1099
1100 //rlScalef(0.0f, -1.0f, 0.0f); // Flip Y-drawing (?)
1101
1102 // Setup current width/height for proper aspect ratio
1103 // calculation when using BeginMode3D()
1104 CORE.Window.currentFbo.width = target.texture.width;
1105 CORE.Window.currentFbo.height = target.texture.height;
1106 CORE.Window.usingFbo = true;
1107}
1108
1109// Ends drawing to render texture
1110void EndTextureMode(void)
1111{
1112 rlDrawRenderBatchActive(); // Update and draw internal render batch
1113
1114 rlDisableFramebuffer(); // Disable render target (fbo)
1115
1116 // Set viewport to default framebuffer size
1117 SetupViewport(CORE.Window.render.width, CORE.Window.render.height);
1118
1119 // Go back to the modelview state from BeginDrawing since we are back to the default FBO
1120 rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
1121 rlLoadIdentity(); // Reset current matrix (modelview)
1122 rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
1123
1124 // Reset current fbo to screen size
1125 CORE.Window.currentFbo.width = CORE.Window.render.width;
1126 CORE.Window.currentFbo.height = CORE.Window.render.height;
1127 CORE.Window.usingFbo = false;
1128}
1129
1130// Begin custom shader mode
1131void BeginShaderMode(Shader shader)
1132{
1133 rlSetShader(shader.id, shader.locs);
1134}
1135
1136// End custom shader mode (returns to default shader)
1137void EndShaderMode(void)
1138{
1139 rlSetShader(rlGetShaderIdDefault(), rlGetShaderLocsDefault());
1140}
1141
1142// Begin blending mode (alpha, additive, multiplied, subtract, custom)
1143// NOTE: Blend modes supported are enumerated in BlendMode enum
1144void BeginBlendMode(int mode)
1145{
1146 rlSetBlendMode(mode);
1147}
1148
1149// End blending mode (reset to default: alpha blending)
1150void EndBlendMode(void)
1151{
1152 rlSetBlendMode(BLEND_ALPHA);
1153}
1154
1155// Begin scissor mode (define screen area for following drawing)
1156// NOTE: Scissor rec refers to bottom-left corner, we change it to upper-left
1157void BeginScissorMode(int x, int y, int width, int height)
1158{
1159 rlDrawRenderBatchActive(); // Update and draw internal render batch
1160
1161 rlEnableScissorTest();
1162
1163#if defined(__APPLE__)
1164 if (!CORE.Window.usingFbo)
1165 {
1166 Vector2 scale = GetWindowScaleDPI();
1167 rlScissor((int)(x*scale.x), (int)(GetScreenHeight()*scale.y - (((y + height)*scale.y))), (int)(width*scale.x), (int)(height*scale.y));
1168 }
1169#else
1170 if (!CORE.Window.usingFbo && ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0))
1171 {
1172 Vector2 scale = GetWindowScaleDPI();
1173 rlScissor((int)(x*scale.x), (int)(CORE.Window.currentFbo.height - (y + height)*scale.y), (int)(width*scale.x), (int)(height*scale.y));
1174 }
1175#endif
1176 else
1177 {
1178 rlScissor(x, CORE.Window.currentFbo.height - (y + height), width, height);
1179 }
1180}
1181
1182// End scissor mode
1183void EndScissorMode(void)
1184{
1185 rlDrawRenderBatchActive(); // Update and draw internal render batch
1186 rlDisableScissorTest();
1187}
1188
1189//----------------------------------------------------------------------------------
1190// Module Functions Definition: VR Stereo Rendering
1191//----------------------------------------------------------------------------------
1192
1193// Begin VR drawing configuration
1194void BeginVrStereoMode(VrStereoConfig config)
1195{
1196 rlEnableStereoRender();
1197
1198 // Set stereo render matrices
1199 rlSetMatrixProjectionStereo(config.projection[0], config.projection[1]);
1200 rlSetMatrixViewOffsetStereo(config.viewOffset[0], config.viewOffset[1]);
1201}
1202
1203// End VR drawing process (and desktop mirror)
1204void EndVrStereoMode(void)
1205{
1206 rlDisableStereoRender();
1207}
1208
1209// Load VR stereo config for VR simulator device parameters
1210VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device)
1211{
1212 VrStereoConfig config = { 0 };
1213
1214 if (rlGetVersion() != RL_OPENGL_11)
1215 {
1216 // Compute aspect ratio
1217 float aspect = ((float)device.hResolution*0.5f)/(float)device.vResolution;
1218
1219 // Compute lens parameters
1220 float lensShift = (device.hScreenSize*0.25f - device.lensSeparationDistance*0.5f)/device.hScreenSize;
1221 config.leftLensCenter[0] = 0.25f + lensShift;
1222 config.leftLensCenter[1] = 0.5f;
1223 config.rightLensCenter[0] = 0.75f - lensShift;
1224 config.rightLensCenter[1] = 0.5f;
1225 config.leftScreenCenter[0] = 0.25f;
1226 config.leftScreenCenter[1] = 0.5f;
1227 config.rightScreenCenter[0] = 0.75f;
1228 config.rightScreenCenter[1] = 0.5f;
1229
1230 // Compute distortion scale parameters
1231 // NOTE: To get lens max radius, lensShift must be normalized to [-1..1]
1232 float lensRadius = fabsf(-1.0f - 4.0f*lensShift);
1233 float lensRadiusSq = lensRadius*lensRadius;
1234 float distortionScale = device.lensDistortionValues[0] +
1235 device.lensDistortionValues[1]*lensRadiusSq +
1236 device.lensDistortionValues[2]*lensRadiusSq*lensRadiusSq +
1237 device.lensDistortionValues[3]*lensRadiusSq*lensRadiusSq*lensRadiusSq;
1238
1239 float normScreenWidth = 0.5f;
1240 float normScreenHeight = 1.0f;
1241 config.scaleIn[0] = 2.0f/normScreenWidth;
1242 config.scaleIn[1] = 2.0f/normScreenHeight/aspect;
1243 config.scale[0] = normScreenWidth*0.5f/distortionScale;
1244 config.scale[1] = normScreenHeight*0.5f*aspect/distortionScale;
1245
1246 // Fovy is normally computed with: 2*atan2f(device.vScreenSize, 2*device.eyeToScreenDistance)
1247 // ...but with lens distortion it is increased (see Oculus SDK Documentation)
1248 float fovy = 2.0f*atan2f(device.vScreenSize*0.5f*distortionScale, device.eyeToScreenDistance); // Really need distortionScale?
1249 // float fovy = 2.0f*(float)atan2f(device.vScreenSize*0.5f, device.eyeToScreenDistance);
1250
1251 // Compute camera projection matrices
1252 float projOffset = 4.0f*lensShift; // Scaled to projection space coordinates [-1..1]
1253 Matrix proj = MatrixPerspective(fovy, aspect, rlGetCullDistanceNear(), rlGetCullDistanceFar());
1254
1255 config.projection[0] = MatrixMultiply(proj, MatrixTranslate(projOffset, 0.0f, 0.0f));
1256 config.projection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f));
1257
1258 // Compute camera transformation matrices
1259 // NOTE: Camera movement might seem more natural if we model the head
1260 // Our axis of rotation is the base of our head, so we might want to add
1261 // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions
1262 config.viewOffset[0] = MatrixTranslate(device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
1263 config.viewOffset[1] = MatrixTranslate(-device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
1264
1265 // Compute eyes Viewports
1266 /*
1267 config.eyeViewportRight[0] = 0;
1268 config.eyeViewportRight[1] = 0;
1269 config.eyeViewportRight[2] = device.hResolution/2;
1270 config.eyeViewportRight[3] = device.vResolution;
1271
1272 config.eyeViewportLeft[0] = device.hResolution/2;
1273 config.eyeViewportLeft[1] = 0;
1274 config.eyeViewportLeft[2] = device.hResolution/2;
1275 config.eyeViewportLeft[3] = device.vResolution;
1276 */
1277 }
1278 else TRACELOG(LOG_WARNING, "RLGL: VR Simulator not supported on OpenGL 1.1");
1279
1280 return config;
1281}
1282
1283// Unload VR stereo config properties
1284void UnloadVrStereoConfig(VrStereoConfig config)
1285{
1286 TRACELOG(LOG_INFO, "UnloadVrStereoConfig not implemented in rcore.c");
1287}
1288
1289//----------------------------------------------------------------------------------
1290// Module Functions Definition: Shaders Management
1291//----------------------------------------------------------------------------------
1292
1293// Load shader from files and bind default locations
1294// NOTE: If shader string is NULL, using default vertex/fragment shaders
1295Shader LoadShader(const char *vsFileName, const char *fsFileName)
1296{
1297 Shader shader = { 0 };
1298
1299 char *vShaderStr = NULL;
1300 char *fShaderStr = NULL;
1301
1302 if (vsFileName != NULL) vShaderStr = LoadFileText(vsFileName);
1303 if (fsFileName != NULL) fShaderStr = LoadFileText(fsFileName);
1304
1305 shader = LoadShaderFromMemory(vShaderStr, fShaderStr);
1306
1307 UnloadFileText(vShaderStr);
1308 UnloadFileText(fShaderStr);
1309
1310 return shader;
1311}
1312
1313// Load shader from code strings and bind default locations
1314Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
1315{
1316 Shader shader = { 0 };
1317
1318 shader.id = rlLoadShaderCode(vsCode, fsCode);
1319
1320 // After shader loading, we TRY to set default location names
1321 if (shader.id > 0)
1322 {
1323 // Default shader attribute locations have been binded before linking:
1324 // vertex position location = 0
1325 // vertex texcoord location = 1
1326 // vertex normal location = 2
1327 // vertex color location = 3
1328 // vertex tangent location = 4
1329 // vertex texcoord2 location = 5
1330 // vertex boneIds location = 6
1331 // vertex boneWeights location = 7
1332
1333 // NOTE: If any location is not found, loc point becomes -1
1334
1335 shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int));
1336
1337 // All locations reset to -1 (no location)
1338 for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1;
1339
1340 // Get handles to GLSL input attribute locations
1341 shader.locs[SHADER_LOC_VERTEX_POSITION] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION);
1342 shader.locs[SHADER_LOC_VERTEX_TEXCOORD01] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD);
1343 shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2);
1344 shader.locs[SHADER_LOC_VERTEX_NORMAL] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL);
1345 shader.locs[SHADER_LOC_VERTEX_TANGENT] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT);
1346 shader.locs[SHADER_LOC_VERTEX_COLOR] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
1347 shader.locs[SHADER_LOC_VERTEX_BONEIDS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS);
1348 shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS);
1349
1350 // Get handles to GLSL uniform locations (vertex shader)
1351 shader.locs[SHADER_LOC_MATRIX_MVP] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MVP);
1352 shader.locs[SHADER_LOC_MATRIX_VIEW] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW);
1353 shader.locs[SHADER_LOC_MATRIX_PROJECTION] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION);
1354 shader.locs[SHADER_LOC_MATRIX_MODEL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL);
1355 shader.locs[SHADER_LOC_MATRIX_NORMAL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL);
1356 shader.locs[SHADER_LOC_BONE_MATRICES] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES);
1357
1358 // Get handles to GLSL uniform locations (fragment shader)
1359 shader.locs[SHADER_LOC_COLOR_DIFFUSE] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR);
1360 shader.locs[SHADER_LOC_MAP_DIFFUSE] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0); // SHADER_LOC_MAP_ALBEDO
1361 shader.locs[SHADER_LOC_MAP_SPECULAR] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1); // SHADER_LOC_MAP_METALNESS
1362 shader.locs[SHADER_LOC_MAP_NORMAL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2);
1363 }
1364
1365 return shader;
1366}
1367
1368// Check if a shader is valid (loaded on GPU)
1369bool IsShaderValid(Shader shader)
1370{
1371 return ((shader.id > 0) && // Validate shader id (GPU loaded successfully)
1372 (shader.locs != NULL)); // Validate memory has been allocated for default shader locations
1373
1374 // The following locations are tried to be set automatically (locs[i] >= 0),
1375 // any of them can be checked for validation but the only mandatory one is, afaik, SHADER_LOC_VERTEX_POSITION
1376 // NOTE: Users can also setup manually their own attributes/uniforms and do not used the default raylib ones
1377
1378 // Vertex shader attribute locations (default)
1379 // shader.locs[SHADER_LOC_VERTEX_POSITION] // Set by default internal shader
1380 // shader.locs[SHADER_LOC_VERTEX_TEXCOORD01] // Set by default internal shader
1381 // shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]
1382 // shader.locs[SHADER_LOC_VERTEX_NORMAL]
1383 // shader.locs[SHADER_LOC_VERTEX_TANGENT]
1384 // shader.locs[SHADER_LOC_VERTEX_COLOR] // Set by default internal shader
1385
1386 // Vertex shader uniform locations (default)
1387 // shader.locs[SHADER_LOC_MATRIX_MVP] // Set by default internal shader
1388 // shader.locs[SHADER_LOC_MATRIX_VIEW]
1389 // shader.locs[SHADER_LOC_MATRIX_PROJECTION]
1390 // shader.locs[SHADER_LOC_MATRIX_MODEL]
1391 // shader.locs[SHADER_LOC_MATRIX_NORMAL]
1392
1393 // Fragment shader uniform locations (default)
1394 // shader.locs[SHADER_LOC_COLOR_DIFFUSE] // Set by default internal shader
1395 // shader.locs[SHADER_LOC_MAP_DIFFUSE] // Set by default internal shader
1396 // shader.locs[SHADER_LOC_MAP_SPECULAR]
1397 // shader.locs[SHADER_LOC_MAP_NORMAL]
1398}
1399
1400// Unload shader from GPU memory (VRAM)
1401void UnloadShader(Shader shader)
1402{
1403 if (shader.id != rlGetShaderIdDefault())
1404 {
1405 rlUnloadShaderProgram(shader.id);
1406
1407 // NOTE: If shader loading failed, it should be 0
1408 RL_FREE(shader.locs);
1409 }
1410}
1411
1412// Get shader uniform location
1413int GetShaderLocation(Shader shader, const char *uniformName)
1414{
1415 return rlGetLocationUniform(shader.id, uniformName);
1416}
1417
1418// Get shader attribute location
1419int GetShaderLocationAttrib(Shader shader, const char *attribName)
1420{
1421 return rlGetLocationAttrib(shader.id, attribName);
1422}
1423
1424// Set shader uniform value
1425void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType)
1426{
1427 SetShaderValueV(shader, locIndex, value, uniformType, 1);
1428}
1429
1430// Set shader uniform value vector
1431void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count)
1432{
1433 if (locIndex > -1)
1434 {
1435 rlEnableShader(shader.id);
1436 rlSetUniform(locIndex, value, uniformType, count);
1437 //rlDisableShader(); // Avoid resetting current shader program, in case other uniforms are set
1438 }
1439}
1440
1441// Set shader uniform value (matrix 4x4)
1442void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat)
1443{
1444 if (locIndex > -1)
1445 {
1446 rlEnableShader(shader.id);
1447 rlSetUniformMatrix(locIndex, mat);
1448 //rlDisableShader();
1449 }
1450}
1451
1452// Set shader uniform value for texture
1453void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture)
1454{
1455 if (locIndex > -1)
1456 {
1457 rlEnableShader(shader.id);
1458 rlSetUniformSampler(locIndex, texture.id);
1459 //rlDisableShader();
1460 }
1461}
1462
1463//----------------------------------------------------------------------------------
1464// Module Functions Definition: Screen-space Queries
1465//----------------------------------------------------------------------------------
1466
1467// Get a ray trace from screen position (i.e mouse)
1468Ray GetScreenToWorldRay(Vector2 position, Camera camera)
1469{
1470 Ray ray = GetScreenToWorldRayEx(position, camera, GetScreenWidth(), GetScreenHeight());
1471
1472 return ray;
1473}
1474
1475// Get a ray trace from the screen position (i.e mouse) within a specific section of the screen
1476Ray GetScreenToWorldRayEx(Vector2 position, Camera camera, int width, int height)
1477{
1478 Ray ray = { 0 };
1479
1480 // Calculate normalized device coordinates
1481 // NOTE: y value is negative
1482 float x = (2.0f*position.x)/(float)width - 1.0f;
1483 float y = 1.0f - (2.0f*position.y)/(float)height;
1484 float z = 1.0f;
1485
1486 // Store values in a vector
1487 Vector3 deviceCoords = { x, y, z };
1488
1489 // Calculate view matrix from camera look at
1490 Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
1491
1492 Matrix matProj = MatrixIdentity();
1493
1494 if (camera.projection == CAMERA_PERSPECTIVE)
1495 {
1496 // Calculate projection matrix from perspective
1497 matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)width/(double)height), rlGetCullDistanceNear(), rlGetCullDistanceFar());
1498 }
1499 else if (camera.projection == CAMERA_ORTHOGRAPHIC)
1500 {
1501 double aspect = (double)width/(double)height;
1502 double top = camera.fovy/2.0;
1503 double right = top*aspect;
1504
1505 // Calculate projection matrix from orthographic
1506 matProj = MatrixOrtho(-right, right, -top, top, 0.01, 1000.0);
1507 }
1508
1509 // Unproject far/near points
1510 Vector3 nearPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 0.0f }, matProj, matView);
1511 Vector3 farPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView);
1512
1513 // Unproject the mouse cursor in the near plane
1514 // We need this as the source position because orthographic projects,
1515 // compared to perspective doesn't have a convergence point,
1516 // meaning that the "eye" of the camera is more like a plane than a point
1517 Vector3 cameraPlanePointerPos = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, -1.0f }, matProj, matView);
1518
1519 // Calculate normalized direction vector
1520 Vector3 direction = Vector3Normalize(Vector3Subtract(farPoint, nearPoint));
1521
1522 if (camera.projection == CAMERA_PERSPECTIVE) ray.position = camera.position;
1523 else if (camera.projection == CAMERA_ORTHOGRAPHIC) ray.position = cameraPlanePointerPos;
1524
1525 // Apply calculated vectors to ray
1526 ray.direction = direction;
1527
1528 return ray;
1529}
1530
1531// Get transform matrix for camera
1532Matrix GetCameraMatrix(Camera camera)
1533{
1534 Matrix mat = MatrixLookAt(camera.position, camera.target, camera.up);
1535
1536 return mat;
1537}
1538
1539// Get camera 2d transform matrix
1540Matrix GetCameraMatrix2D(Camera2D camera)
1541{
1542 Matrix matTransform = { 0 };
1543 // The camera in world-space is set by
1544 // 1. Move it to target
1545 // 2. Rotate by -rotation and scale by (1/zoom)
1546 // When setting higher scale, it's more intuitive for the world to become bigger (= camera become smaller),
1547 // not for the camera getting bigger, hence the invert. Same deal with rotation
1548 // 3. Move it by (-offset);
1549 // Offset defines target transform relative to screen, but since we're effectively "moving" screen (camera)
1550 // we need to do it into opposite direction (inverse transform)
1551
1552 // Having camera transform in world-space, inverse of it gives the modelview transform
1553 // Since (A*B*C)' = C'*B'*A', the modelview is
1554 // 1. Move to offset
1555 // 2. Rotate and Scale
1556 // 3. Move by -target
1557 Matrix matOrigin = MatrixTranslate(-camera.target.x, -camera.target.y, 0.0f);
1558 Matrix matRotation = MatrixRotate((Vector3){ 0.0f, 0.0f, 1.0f }, camera.rotation*DEG2RAD);
1559 Matrix matScale = MatrixScale(camera.zoom, camera.zoom, 1.0f);
1560 Matrix matTranslation = MatrixTranslate(camera.offset.x, camera.offset.y, 0.0f);
1561
1562 matTransform = MatrixMultiply(MatrixMultiply(matOrigin, MatrixMultiply(matScale, matRotation)), matTranslation);
1563
1564 return matTransform;
1565}
1566
1567// Get the screen space position from a 3d world space position
1568Vector2 GetWorldToScreen(Vector3 position, Camera camera)
1569{
1570 Vector2 screenPosition = GetWorldToScreenEx(position, camera, GetScreenWidth(), GetScreenHeight());
1571
1572 return screenPosition;
1573}
1574
1575// Get size position for a 3d world space position (useful for texture drawing)
1576Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int height)
1577{
1578 // Calculate projection matrix (from perspective instead of frustum
1579 Matrix matProj = MatrixIdentity();
1580
1581 if (camera.projection == CAMERA_PERSPECTIVE)
1582 {
1583 // Calculate projection matrix from perspective
1584 matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)width/(double)height), rlGetCullDistanceNear(), rlGetCullDistanceFar());
1585 }
1586 else if (camera.projection == CAMERA_ORTHOGRAPHIC)
1587 {
1588 double aspect = (double)width/(double)height;
1589 double top = camera.fovy/2.0;
1590 double right = top*aspect;
1591
1592 // Calculate projection matrix from orthographic
1593 matProj = MatrixOrtho(-right, right, -top, top, rlGetCullDistanceNear(), rlGetCullDistanceFar());
1594 }
1595
1596 // Calculate view matrix from camera look at (and transpose it)
1597 Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
1598
1599 // TODO: Why not use Vector3Transform(Vector3 v, Matrix mat)?
1600
1601 // Convert world position vector to quaternion
1602 Quaternion worldPos = { position.x, position.y, position.z, 1.0f };
1603
1604 // Transform world position to view
1605 worldPos = QuaternionTransform(worldPos, matView);
1606
1607 // Transform result to projection (clip space position)
1608 worldPos = QuaternionTransform(worldPos, matProj);
1609
1610 // Calculate normalized device coordinates (inverted y)
1611 Vector3 ndcPos = { worldPos.x/worldPos.w, -worldPos.y/worldPos.w, worldPos.z/worldPos.w };
1612
1613 // Calculate 2d screen position vector
1614 Vector2 screenPosition = { (ndcPos.x + 1.0f)/2.0f*(float)width, (ndcPos.y + 1.0f)/2.0f*(float)height };
1615
1616 return screenPosition;
1617}
1618
1619// Get the screen space position for a 2d camera world space position
1620Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera)
1621{
1622 Matrix matCamera = GetCameraMatrix2D(camera);
1623 Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, matCamera);
1624
1625 return (Vector2){ transform.x, transform.y };
1626}
1627
1628// Get the world space position for a 2d camera screen space position
1629Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera)
1630{
1631 Matrix invMatCamera = MatrixInvert(GetCameraMatrix2D(camera));
1632 Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, invMatCamera);
1633
1634 return (Vector2){ transform.x, transform.y };
1635}
1636
1637//----------------------------------------------------------------------------------
1638// Module Functions Definition: Timming
1639//----------------------------------------------------------------------------------
1640
1641// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
1642//double GetTime(void)
1643
1644// Set target FPS (maximum)
1645void SetTargetFPS(int fps)
1646{
1647 if (fps < 1) CORE.Time.target = 0.0;
1648 else CORE.Time.target = 1.0/(double)fps;
1649
1650 TRACELOG(LOG_INFO, "TIMER: Target time per frame: %02.03f milliseconds", (float)CORE.Time.target*1000.0f);
1651}
1652
1653// Get current FPS
1654// NOTE: We calculate an average framerate
1655int GetFPS(void)
1656{
1657 int fps = 0;
1658
1659#if !defined(SUPPORT_CUSTOM_FRAME_CONTROL)
1660 #define FPS_CAPTURE_FRAMES_COUNT 30 // 30 captures
1661 #define FPS_AVERAGE_TIME_SECONDS 0.5f // 500 milliseconds
1662 #define FPS_STEP (FPS_AVERAGE_TIME_SECONDS/FPS_CAPTURE_FRAMES_COUNT)
1663
1664 static int index = 0;
1665 static float history[FPS_CAPTURE_FRAMES_COUNT] = { 0 };
1666 static float average = 0, last = 0;
1667 float fpsFrame = GetFrameTime();
1668
1669 // if we reset the window, reset the FPS info
1670 if (CORE.Time.frameCounter == 0)
1671 {
1672 average = 0;
1673 last = 0;
1674 index = 0;
1675
1676 for (int i = 0; i < FPS_CAPTURE_FRAMES_COUNT; i++) history[i] = 0;
1677 }
1678
1679 if (fpsFrame == 0) return 0;
1680
1681 if ((GetTime() - last) > FPS_STEP)
1682 {
1683 last = (float)GetTime();
1684 index = (index + 1)%FPS_CAPTURE_FRAMES_COUNT;
1685 average -= history[index];
1686 history[index] = fpsFrame/FPS_CAPTURE_FRAMES_COUNT;
1687 average += history[index];
1688 }
1689
1690 fps = (int)roundf(1.0f/average);
1691#endif
1692
1693 return fps;
1694}
1695
1696// Get time in seconds for last frame drawn (delta time)
1697float GetFrameTime(void)
1698{
1699 return (float)CORE.Time.frame;
1700}
1701
1702//----------------------------------------------------------------------------------
1703// Module Functions Definition: Custom frame control
1704//----------------------------------------------------------------------------------
1705
1706// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
1707//void SwapScreenBuffer(void);
1708//void PollInputEvents(void);
1709
1710// Wait for some time (stop program execution)
1711// NOTE: Sleep() granularity could be around 10 ms, it means, Sleep() could
1712// take longer than expected... for that reason we use the busy wait loop
1713// Ref: http://stackoverflow.com/questions/43057578/c-programming-win32-games-sleep-taking-longer-than-expected
1714// Ref: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timing on Win32!
1715void WaitTime(double seconds)
1716{
1717 if (seconds < 0) return; // Security check
1718
1719#if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
1720 double destinationTime = GetTime() + seconds;
1721#endif
1722
1723#if defined(SUPPORT_BUSY_WAIT_LOOP)
1724 while (GetTime() < destinationTime) { }
1725#else
1726 #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
1727 double sleepSeconds = seconds - seconds*0.05; // NOTE: We reserve a percentage of the time for busy waiting
1728 #else
1729 double sleepSeconds = seconds;
1730 #endif
1731
1732 // System halt functions
1733 #if defined(_WIN32)
1734 Sleep((unsigned long)(sleepSeconds*1000.0));
1735 #endif
1736 #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
1737 struct timespec req = { 0 };
1738 time_t sec = sleepSeconds;
1739 long nsec = (sleepSeconds - sec)*1000000000L;
1740 req.tv_sec = sec;
1741 req.tv_nsec = nsec;
1742
1743 // NOTE: Use nanosleep() on Unix platforms... usleep() it's deprecated
1744 while (nanosleep(&req, &req) == -1) continue;
1745 #endif
1746 #if defined(__APPLE__)
1747 usleep(sleepSeconds*1000000.0);
1748 #endif
1749
1750 #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
1751 while (GetTime() < destinationTime) { }
1752 #endif
1753#endif
1754}
1755
1756//----------------------------------------------------------------------------------
1757// Module Functions Definition: Misc
1758//----------------------------------------------------------------------------------
1759
1760// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
1761//void OpenURL(const char *url)
1762
1763// Set the seed for the random number generator
1764void SetRandomSeed(unsigned int seed)
1765{
1766#if defined(SUPPORT_RPRAND_GENERATOR)
1767 rprand_set_seed(seed);
1768#else
1769 srand(seed);
1770#endif
1771}
1772
1773// Get a random value between min and max included
1774int GetRandomValue(int min, int max)
1775{
1776 int value = 0;
1777
1778 if (min > max)
1779 {
1780 int tmp = max;
1781 max = min;
1782 min = tmp;
1783 }
1784
1785#if defined(SUPPORT_RPRAND_GENERATOR)
1786 value = rprand_get_value(min, max);
1787#else
1788 // WARNING: Ranges higher than RAND_MAX will return invalid results
1789 // More specifically, if (max - min) > INT_MAX there will be an overflow,
1790 // and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold
1791 // NOTE: Depending on the library it can be as low as 32767
1792 if ((unsigned int)(max - min) > (unsigned int)RAND_MAX)
1793 {
1794 TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX);
1795 }
1796
1797 value = (rand()%(abs(max - min) + 1) + min);
1798#endif
1799 return value;
1800}
1801
1802// Load random values sequence, no values repeated, min and max included
1803int *LoadRandomSequence(unsigned int count, int min, int max)
1804{
1805 int *values = NULL;
1806
1807#if defined(SUPPORT_RPRAND_GENERATOR)
1808 values = rprand_load_sequence(count, min, max);
1809#else
1810 if (count > ((unsigned int)abs(max - min) + 1)) return values; // Security check
1811
1812 values = (int *)RL_CALLOC(count, sizeof(int));
1813
1814 int value = 0;
1815 bool dupValue = false;
1816
1817 for (int i = 0; i < (int)count;)
1818 {
1819 value = (rand()%(abs(max - min) + 1) + min);
1820 dupValue = false;
1821
1822 for (int j = 0; j < i; j++)
1823 {
1824 if (values[j] == value)
1825 {
1826 dupValue = true;
1827 break;
1828 }
1829 }
1830
1831 if (!dupValue)
1832 {
1833 values[i] = value;
1834 i++;
1835 }
1836 }
1837#endif
1838 return values;
1839}
1840
1841// Unload random values sequence
1842void UnloadRandomSequence(int *sequence)
1843{
1844#if defined(SUPPORT_RPRAND_GENERATOR)
1845 rprand_unload_sequence(sequence);
1846#else
1847 RL_FREE(sequence);
1848#endif
1849}
1850
1851// Takes a screenshot of current screen
1852// NOTE: Provided fileName should not contain paths, saving to working directory
1853void TakeScreenshot(const char *fileName)
1854{
1855#if defined(SUPPORT_MODULE_RTEXTURES)
1856 // Security check to (partially) avoid malicious code
1857 if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; }
1858
1859 Vector2 scale = GetWindowScaleDPI();
1860 unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
1861 Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
1862
1863 char path[512] = { 0 };
1864 strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, GetFileName(fileName)));
1865
1866 ExportImage(image, path); // WARNING: Module required: rtextures
1867 RL_FREE(imgData);
1868
1869 if (FileExists(path)) TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path);
1870 else TRACELOG(LOG_WARNING, "SYSTEM: [%s] Screenshot could not be saved", path);
1871#else
1872 TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures");
1873#endif
1874}
1875
1876// Setup window configuration flags (view FLAGS)
1877// NOTE: This function is expected to be called before window creation,
1878// because it sets up some flags for the window creation process
1879// To configure window states after creation, just use SetWindowState()
1880void SetConfigFlags(unsigned int flags)
1881{
1882 // Selected flags are set but not evaluated at this point,
1883 // flag evaluation happens at InitWindow() or SetWindowState()
1884 CORE.Window.flags |= flags;
1885}
1886
1887//----------------------------------------------------------------------------------
1888// Module Functions Definition: File system
1889//----------------------------------------------------------------------------------
1890
1891// Check if the file exists
1892bool FileExists(const char *fileName)
1893{
1894 bool result = false;
1895
1896#if defined(_WIN32)
1897 if (_access(fileName, 0) != -1) result = true;
1898#else
1899 if (access(fileName, F_OK) != -1) result = true;
1900#endif
1901
1902 // NOTE: Alternatively, stat() can be used instead of access()
1903 //#include <sys/stat.h>
1904 //struct stat statbuf;
1905 //if (stat(filename, &statbuf) == 0) result = true;
1906
1907 return result;
1908}
1909
1910// Check file extension
1911// NOTE: Extensions checking is not case-sensitive
1912bool IsFileExtension(const char *fileName, const char *ext)
1913{
1914 #define MAX_FILE_EXTENSION_LENGTH 16
1915
1916 bool result = false;
1917 const char *fileExt = GetFileExtension(fileName);
1918
1919 if (fileExt != NULL)
1920 {
1921#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_TEXT_MANIPULATION)
1922 int extCount = 0;
1923 const char **checkExts = TextSplit(ext, ';', &extCount); // WARNING: Module required: rtext
1924
1925 char fileExtLower[MAX_FILE_EXTENSION_LENGTH + 1] = { 0 };
1926 strncpy(fileExtLower, TextToLower(fileExt), MAX_FILE_EXTENSION_LENGTH); // WARNING: Module required: rtext
1927
1928 for (int i = 0; i < extCount; i++)
1929 {
1930 if (strcmp(fileExtLower, TextToLower(checkExts[i])) == 0)
1931 {
1932 result = true;
1933 break;
1934 }
1935 }
1936#else
1937 if (strcmp(fileExt, ext) == 0) result = true;
1938#endif
1939 }
1940
1941 return result;
1942}
1943
1944// Check if a directory path exists
1945bool DirectoryExists(const char *dirPath)
1946{
1947 bool result = false;
1948 DIR *dir = opendir(dirPath);
1949
1950 if (dir != NULL)
1951 {
1952 result = true;
1953 closedir(dir);
1954 }
1955
1956 return result;
1957}
1958
1959// Get file length in bytes
1960// NOTE: GetFileSize() conflicts with windows.h
1961int GetFileLength(const char *fileName)
1962{
1963 int size = 0;
1964
1965 // NOTE: On Unix-like systems, it can by used the POSIX system call: stat(),
1966 // but depending on the platform that call could not be available
1967 //struct stat result = { 0 };
1968 //stat(fileName, &result);
1969 //return result.st_size;
1970
1971 FILE *file = fopen(fileName, "rb");
1972
1973 if (file != NULL)
1974 {
1975 fseek(file, 0L, SEEK_END);
1976 long int fileSize = ftell(file);
1977
1978 // Check for size overflow (INT_MAX)
1979 if (fileSize > 2147483647) TRACELOG(LOG_WARNING, "[%s] File size overflows expected limit, do not use GetFileLength()", fileName);
1980 else size = (int)fileSize;
1981
1982 fclose(file);
1983 }
1984
1985 return size;
1986}
1987
1988// Get pointer to extension for a filename string (includes the dot: .png)
1989const char *GetFileExtension(const char *fileName)
1990{
1991 const char *dot = strrchr(fileName, '.');
1992
1993 if (!dot || dot == fileName) return NULL;
1994
1995 return dot;
1996}
1997
1998// String pointer reverse break: returns right-most occurrence of charset in s
1999static const char *strprbrk(const char *s, const char *charset)
2000{
2001 const char *latestMatch = NULL;
2002
2003 for (; s = strpbrk(s, charset), s != NULL; latestMatch = s++) { }
2004
2005 return latestMatch;
2006}
2007
2008// Get pointer to filename for a path string
2009const char *GetFileName(const char *filePath)
2010{
2011 const char *fileName = NULL;
2012
2013 if (filePath != NULL) fileName = strprbrk(filePath, "\\/");
2014
2015 if (fileName == NULL) return filePath;
2016
2017 return fileName + 1;
2018}
2019
2020// Get filename string without extension (uses static string)
2021const char *GetFileNameWithoutExt(const char *filePath)
2022{
2023 #define MAX_FILENAME_LENGTH 256
2024
2025 static char fileName[MAX_FILENAME_LENGTH] = { 0 };
2026 memset(fileName, 0, MAX_FILENAME_LENGTH);
2027
2028 if (filePath != NULL)
2029 {
2030 strcpy(fileName, GetFileName(filePath)); // Get filename.ext without path
2031 int size = (int)strlen(fileName); // Get size in bytes
2032
2033 for (int i = size; i > 0; i--) // Reverse search '.'
2034 {
2035 if (fileName[i] == '.')
2036 {
2037 // NOTE: We break on first '.' found
2038 fileName[i] = '\0';
2039 break;
2040 }
2041 }
2042 }
2043
2044 return fileName;
2045}
2046
2047// Get directory for a given filePath
2048const char *GetDirectoryPath(const char *filePath)
2049{
2050 /*
2051 // NOTE: Directory separator is different in Windows and other platforms,
2052 // fortunately, Windows also support the '/' separator, that's the one should be used
2053 #if defined(_WIN32)
2054 char separator = '\\';
2055 #else
2056 char separator = '/';
2057 #endif
2058 */
2059 const char *lastSlash = NULL;
2060 static char dirPath[MAX_FILEPATH_LENGTH] = { 0 };
2061 memset(dirPath, 0, MAX_FILEPATH_LENGTH);
2062
2063 // In case provided path does not contain a root drive letter (C:\, D:\) nor leading path separator (\, /),
2064 // we add the current directory path to dirPath
2065 if (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/')
2066 {
2067 // For security, we set starting path to current directory,
2068 // obtained path will be concatenated to this
2069 dirPath[0] = '.';
2070 dirPath[1] = '/';
2071 }
2072
2073 lastSlash = strprbrk(filePath, "\\/");
2074 if (lastSlash)
2075 {
2076 if (lastSlash == filePath)
2077 {
2078 // The last and only slash is the leading one: path is in a root directory
2079 dirPath[0] = filePath[0];
2080 dirPath[1] = '\0';
2081 }
2082 else
2083 {
2084 // NOTE: Be careful, strncpy() is not safe, it does not care about '\0'
2085 char *dirPathPtr = dirPath;
2086 if ((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/')) dirPathPtr += 2; // Skip drive letter, "C:"
2087 memcpy(dirPathPtr, filePath, strlen(filePath) - (strlen(lastSlash) - 1));
2088 dirPath[strlen(filePath) - strlen(lastSlash) + (((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/'))? 2 : 0)] = '\0'; // Add '\0' manually
2089 }
2090 }
2091
2092 return dirPath;
2093}
2094
2095// Get previous directory path for a given path
2096const char *GetPrevDirectoryPath(const char *dirPath)
2097{
2098 static char prevDirPath[MAX_FILEPATH_LENGTH] = { 0 };
2099 memset(prevDirPath, 0, MAX_FILEPATH_LENGTH);
2100 int pathLen = (int)strlen(dirPath);
2101
2102 if (pathLen <= 3) strcpy(prevDirPath, dirPath);
2103
2104 for (int i = (pathLen - 1); (i >= 0) && (pathLen > 3); i--)
2105 {
2106 if ((dirPath[i] == '\\') || (dirPath[i] == '/'))
2107 {
2108 // Check for root: "C:\" or "/"
2109 if (((i == 2) && (dirPath[1] ==':')) || (i == 0)) i++;
2110
2111 strncpy(prevDirPath, dirPath, i);
2112 break;
2113 }
2114 }
2115
2116 return prevDirPath;
2117}
2118
2119// Get current working directory
2120const char *GetWorkingDirectory(void)
2121{
2122 static char currentDir[MAX_FILEPATH_LENGTH] = { 0 };
2123 memset(currentDir, 0, MAX_FILEPATH_LENGTH);
2124
2125 char *path = GETCWD(currentDir, MAX_FILEPATH_LENGTH - 1);
2126
2127 return path;
2128}
2129
2130const char *GetApplicationDirectory(void)
2131{
2132 static char appDir[MAX_FILEPATH_LENGTH] = { 0 };
2133 memset(appDir, 0, MAX_FILEPATH_LENGTH);
2134
2135#if defined(_WIN32)
2136 int len = 0;
2137#if defined(UNICODE)
2138 unsigned short widePath[MAX_PATH];
2139 len = GetModuleFileNameW(NULL, widePath, MAX_PATH);
2140 len = WideCharToMultiByte(0, 0, widePath, len, appDir, MAX_PATH, NULL, NULL);
2141#else
2142 len = GetModuleFileNameA(NULL, appDir, MAX_PATH);
2143#endif
2144 if (len > 0)
2145 {
2146 for (int i = len; i >= 0; --i)
2147 {
2148 if (appDir[i] == '\\')
2149 {
2150 appDir[i + 1] = '\0';
2151 break;
2152 }
2153 }
2154 }
2155 else
2156 {
2157 appDir[0] = '.';
2158 appDir[1] = '\\';
2159 }
2160
2161#elif defined(__linux__)
2162 unsigned int size = sizeof(appDir);
2163 ssize_t len = readlink("/proc/self/exe", appDir, size);
2164
2165 if (len > 0)
2166 {
2167 for (int i = len; i >= 0; --i)
2168 {
2169 if (appDir[i] == '/')
2170 {
2171 appDir[i + 1] = '\0';
2172 break;
2173 }
2174 }
2175 }
2176 else
2177 {
2178 appDir[0] = '.';
2179 appDir[1] = '/';
2180 }
2181#elif defined(__APPLE__)
2182 uint32_t size = sizeof(appDir);
2183
2184 if (_NSGetExecutablePath(appDir, &size) == 0)
2185 {
2186 int len = strlen(appDir);
2187 for (int i = len; i >= 0; --i)
2188 {
2189 if (appDir[i] == '/')
2190 {
2191 appDir[i + 1] = '\0';
2192 break;
2193 }
2194 }
2195 }
2196 else
2197 {
2198 appDir[0] = '.';
2199 appDir[1] = '/';
2200 }
2201#elif defined(__FreeBSD__)
2202 size_t size = sizeof(appDir);
2203 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
2204
2205 if (sysctl(mib, 4, appDir, &size, NULL, 0) == 0)
2206 {
2207 int len = strlen(appDir);
2208 for (int i = len; i >= 0; --i)
2209 {
2210 if (appDir[i] == '/')
2211 {
2212 appDir[i + 1] = '\0';
2213 break;
2214 }
2215 }
2216 }
2217 else
2218 {
2219 appDir[0] = '.';
2220 appDir[1] = '/';
2221 }
2222
2223#endif
2224
2225 return appDir;
2226}
2227
2228// Load directory filepaths
2229// NOTE: Base path is prepended to the scanned filepaths
2230// WARNING: Directory is scanned twice, first time to get files count
2231// No recursive scanning is done!
2232FilePathList LoadDirectoryFiles(const char *dirPath)
2233{
2234 FilePathList files = { 0 };
2235 unsigned int fileCounter = 0;
2236
2237 struct dirent *entity;
2238 DIR *dir = opendir(dirPath);
2239
2240 if (dir != NULL) // It's a directory
2241 {
2242 // SCAN 1: Count files
2243 while ((entity = readdir(dir)) != NULL)
2244 {
2245 // NOTE: We skip '.' (current dir) and '..' (parent dir) filepaths
2246 if ((strcmp(entity->d_name, ".") != 0) && (strcmp(entity->d_name, "..") != 0)) fileCounter++;
2247 }
2248
2249 // Memory allocation for dirFileCount
2250 files.capacity = fileCounter;
2251 files.paths = (char **)RL_MALLOC(files.capacity*sizeof(char *));
2252 for (unsigned int i = 0; i < files.capacity; i++) files.paths[i] = (char *)RL_MALLOC(MAX_FILEPATH_LENGTH*sizeof(char));
2253
2254 closedir(dir);
2255
2256 // SCAN 2: Read filepaths
2257 // NOTE: Directory paths are also registered
2258 ScanDirectoryFiles(dirPath, &files, NULL);
2259
2260 // Security check: read files.count should match fileCounter
2261 if (files.count != files.capacity) TRACELOG(LOG_WARNING, "FILEIO: Read files count do not match capacity allocated");
2262 }
2263 else TRACELOG(LOG_WARNING, "FILEIO: Failed to open requested directory"); // Maybe it's a file...
2264
2265 return files;
2266}
2267
2268// Load directory filepaths with extension filtering and recursive directory scan
2269// NOTE: On recursive loading we do not pre-scan for file count, we use MAX_FILEPATH_CAPACITY
2270FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs)
2271{
2272 FilePathList files = { 0 };
2273
2274 files.capacity = MAX_FILEPATH_CAPACITY;
2275 files.paths = (char **)RL_CALLOC(files.capacity, sizeof(char *));
2276 for (unsigned int i = 0; i < files.capacity; i++) files.paths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
2277
2278 // WARNING: basePath is always prepended to scanned paths
2279 if (scanSubdirs) ScanDirectoryFilesRecursively(basePath, &files, filter);
2280 else ScanDirectoryFiles(basePath, &files, filter);
2281
2282 return files;
2283}
2284
2285// Unload directory filepaths
2286// WARNING: files.count is not reseted to 0 after unloading
2287void UnloadDirectoryFiles(FilePathList files)
2288{
2289 for (unsigned int i = 0; i < files.capacity; i++) RL_FREE(files.paths[i]);
2290
2291 RL_FREE(files.paths);
2292}
2293
2294// Create directories (including full path requested), returns 0 on success
2295int MakeDirectory(const char *dirPath)
2296{
2297 if ((dirPath == NULL) || (dirPath[0] == '\0')) return 1; // Path is not valid
2298 if (DirectoryExists(dirPath)) return 0; // Path already exists (is valid)
2299
2300 // Copy path string to avoid modifying original
2301 int len = (int)strlen(dirPath) + 1;
2302 char *pathcpy = (char *)RL_CALLOC(len, 1);
2303 memcpy(pathcpy, dirPath, len);
2304
2305 // Iterate over pathcpy, create each subdirectory as needed
2306 for (int i = 0; (i < len) && (pathcpy[i] != '\0'); i++)
2307 {
2308 if (pathcpy[i] == ':') i++;
2309 else
2310 {
2311 if ((pathcpy[i] == '\\') || (pathcpy[i] == '/'))
2312 {
2313 pathcpy[i] = '\0';
2314 if (!DirectoryExists(pathcpy)) MKDIR(pathcpy);
2315 pathcpy[i] = '/';
2316 }
2317 }
2318 }
2319
2320 // Create final directory
2321 if (!DirectoryExists(pathcpy)) MKDIR(pathcpy);
2322
2323 RL_FREE(pathcpy);
2324
2325 return 0;
2326}
2327
2328// Change working directory, returns true on success
2329bool ChangeDirectory(const char *dir)
2330{
2331 bool result = CHDIR(dir);
2332
2333 if (result != 0) TRACELOG(LOG_WARNING, "SYSTEM: Failed to change to directory: %s", dir);
2334
2335 return (result == 0);
2336}
2337
2338// Check if a given path point to a file
2339bool IsPathFile(const char *path)
2340{
2341 struct stat result = { 0 };
2342 stat(path, &result);
2343
2344 return S_ISREG(result.st_mode);
2345}
2346
2347// Check if fileName is valid for the platform/OS
2348bool IsFileNameValid(const char *fileName)
2349{
2350 bool valid = true;
2351
2352 if ((fileName != NULL) && (fileName[0] != '\0'))
2353 {
2354 int length = (int)strlen(fileName);
2355 bool allPeriods = true;
2356
2357 for (int i = 0; i < length; i++)
2358 {
2359 // Check invalid characters
2360 if ((fileName[i] == '<') ||
2361 (fileName[i] == '>') ||
2362 (fileName[i] == ':') ||
2363 (fileName[i] == '\"') ||
2364 (fileName[i] == '/') ||
2365 (fileName[i] == '\\') ||
2366 (fileName[i] == '|') ||
2367 (fileName[i] == '?') ||
2368 (fileName[i] == '*')) { valid = false; break; }
2369
2370 // Check non-glyph characters
2371 if ((unsigned char)fileName[i] < 32) { valid = false; break; }
2372
2373 // TODO: Check trailing periods/spaces?
2374
2375 // Check if filename is not all periods
2376 if (fileName[i] != '.') allPeriods = false;
2377 }
2378
2379 if (allPeriods) valid = false;
2380
2381/*
2382 if (valid)
2383 {
2384 // Check invalid DOS names
2385 if (length >= 3)
2386 {
2387 if (((fileName[0] == 'C') && (fileName[1] == 'O') && (fileName[2] == 'N')) || // CON
2388 ((fileName[0] == 'P') && (fileName[1] == 'R') && (fileName[2] == 'N')) || // PRN
2389 ((fileName[0] == 'A') && (fileName[1] == 'U') && (fileName[2] == 'X')) || // AUX
2390 ((fileName[0] == 'N') && (fileName[1] == 'U') && (fileName[2] == 'L'))) valid = false; // NUL
2391 }
2392
2393 if (length >= 4)
2394 {
2395 if (((fileName[0] == 'C') && (fileName[1] == 'O') && (fileName[2] == 'M') && ((fileName[3] >= '0') && (fileName[3] <= '9'))) || // COM0-9
2396 ((fileName[0] == 'L') && (fileName[1] == 'P') && (fileName[2] == 'T') && ((fileName[3] >= '0') && (fileName[3] <= '9')))) valid = false; // LPT0-9
2397 }
2398 }
2399*/
2400 }
2401
2402 return valid;
2403}
2404
2405// Check if a file has been dropped into window
2406bool IsFileDropped(void)
2407{
2408 bool result = false;
2409
2410 if (CORE.Window.dropFileCount > 0) result = true;
2411
2412 return result;
2413}
2414
2415// Load dropped filepaths
2416FilePathList LoadDroppedFiles(void)
2417{
2418 FilePathList files = { 0 };
2419
2420 files.count = CORE.Window.dropFileCount;
2421 files.paths = CORE.Window.dropFilepaths;
2422
2423 return files;
2424}
2425
2426// Unload dropped filepaths
2427void UnloadDroppedFiles(FilePathList files)
2428{
2429 // WARNING: files pointers are the same as internal ones
2430
2431 if (files.count > 0)
2432 {
2433 for (unsigned int i = 0; i < files.count; i++) RL_FREE(files.paths[i]);
2434
2435 RL_FREE(files.paths);
2436
2437 CORE.Window.dropFileCount = 0;
2438 CORE.Window.dropFilepaths = NULL;
2439 }
2440}
2441
2442// Get file modification time (last write time)
2443long GetFileModTime(const char *fileName)
2444{
2445 struct stat result = { 0 };
2446 long modTime = 0;
2447
2448 if (stat(fileName, &result) == 0)
2449 {
2450 time_t mod = result.st_mtime;
2451
2452 modTime = (long)mod;
2453 }
2454
2455 return modTime;
2456}
2457
2458//----------------------------------------------------------------------------------
2459// Module Functions Definition: Compression and Encoding
2460//----------------------------------------------------------------------------------
2461
2462// Compress data (DEFLATE algorithm)
2463unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize)
2464{
2465 #define COMPRESSION_QUALITY_DEFLATE 8
2466
2467 unsigned char *compData = NULL;
2468
2469#if defined(SUPPORT_COMPRESSION_API)
2470 // Compress data and generate a valid DEFLATE stream
2471 struct sdefl *sdefl = RL_CALLOC(1, sizeof(struct sdefl)); // WARNING: Possible stack overflow, struct sdefl is almost 1MB
2472 int bounds = sdefl_bound(dataSize);
2473 compData = (unsigned char *)RL_CALLOC(bounds, 1);
2474
2475 *compDataSize = sdeflate(sdefl, compData, data, dataSize, COMPRESSION_QUALITY_DEFLATE); // Compression level 8, same as stbiw
2476 RL_FREE(sdefl);
2477
2478 TRACELOG(LOG_INFO, "SYSTEM: Compress data: Original size: %i -> Comp. size: %i", dataSize, *compDataSize);
2479#endif
2480
2481 return compData;
2482}
2483
2484// Decompress data (DEFLATE algorithm)
2485unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize)
2486{
2487 unsigned char *data = NULL;
2488
2489#if defined(SUPPORT_COMPRESSION_API)
2490 // Decompress data from a valid DEFLATE stream
2491 data = (unsigned char *)RL_CALLOC(MAX_DECOMPRESSION_SIZE*1024*1024, 1);
2492 int length = sinflate(data, MAX_DECOMPRESSION_SIZE*1024*1024, compData, compDataSize);
2493
2494 // WARNING: RL_REALLOC can make (and leave) data copies in memory, be careful with sensitive compressed data!
2495 // TODO: Use a different approach, create another buffer, copy data manually to it and wipe original buffer memory
2496 unsigned char *temp = (unsigned char *)RL_REALLOC(data, length);
2497
2498 if (temp != NULL) data = temp;
2499 else TRACELOG(LOG_WARNING, "SYSTEM: Failed to re-allocate required decompression memory");
2500
2501 *dataSize = length;
2502
2503 TRACELOG(LOG_INFO, "SYSTEM: Decompress data: Comp. size: %i -> Original size: %i", compDataSize, *dataSize);
2504#endif
2505
2506 return data;
2507}
2508
2509// Encode data to Base64 string
2510char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize)
2511{
2512 static const unsigned char base64encodeTable[] = {
2513 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
2514 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
2515 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
2516 };
2517
2518 static const int modTable[] = { 0, 2, 1 };
2519
2520 *outputSize = 4*((dataSize + 2)/3);
2521
2522 char *encodedData = (char *)RL_MALLOC(*outputSize);
2523
2524 if (encodedData == NULL) return NULL; // Security check
2525
2526 for (int i = 0, j = 0; i < dataSize;)
2527 {
2528 unsigned int octetA = (i < dataSize)? (unsigned char)data[i++] : 0;
2529 unsigned int octetB = (i < dataSize)? (unsigned char)data[i++] : 0;
2530 unsigned int octetC = (i < dataSize)? (unsigned char)data[i++] : 0;
2531
2532 unsigned int triple = (octetA << 0x10) + (octetB << 0x08) + octetC;
2533
2534 encodedData[j++] = base64encodeTable[(triple >> 3*6) & 0x3F];
2535 encodedData[j++] = base64encodeTable[(triple >> 2*6) & 0x3F];
2536 encodedData[j++] = base64encodeTable[(triple >> 1*6) & 0x3F];
2537 encodedData[j++] = base64encodeTable[(triple >> 0*6) & 0x3F];
2538 }
2539
2540 for (int i = 0; i < modTable[dataSize%3]; i++) encodedData[*outputSize - 1 - i] = '='; // Padding character
2541
2542 return encodedData;
2543}
2544
2545// Decode Base64 string data
2546unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize)
2547{
2548 static const unsigned char base64decodeTable[] = {
2549 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2550 0, 0, 0, 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
2551 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
2552 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
2553 };
2554
2555 // Get output size of Base64 input data
2556 int outSize = 0;
2557 for (int i = 0; data[4*i] != 0; i++)
2558 {
2559 if (data[4*i + 3] == '=')
2560 {
2561 if (data[4*i + 2] == '=') outSize += 1;
2562 else outSize += 2;
2563 }
2564 else outSize += 3;
2565 }
2566
2567 // Allocate memory to store decoded Base64 data
2568 unsigned char *decodedData = (unsigned char *)RL_MALLOC(outSize);
2569
2570 for (int i = 0; i < outSize/3; i++)
2571 {
2572 unsigned char a = base64decodeTable[(int)data[4*i]];
2573 unsigned char b = base64decodeTable[(int)data[4*i + 1]];
2574 unsigned char c = base64decodeTable[(int)data[4*i + 2]];
2575 unsigned char d = base64decodeTable[(int)data[4*i + 3]];
2576
2577 decodedData[3*i] = (a << 2) | (b >> 4);
2578 decodedData[3*i + 1] = (b << 4) | (c >> 2);
2579 decodedData[3*i + 2] = (c << 6) | d;
2580 }
2581
2582 if (outSize%3 == 1)
2583 {
2584 int n = outSize/3;
2585 unsigned char a = base64decodeTable[(int)data[4*n]];
2586 unsigned char b = base64decodeTable[(int)data[4*n + 1]];
2587 decodedData[outSize - 1] = (a << 2) | (b >> 4);
2588 }
2589 else if (outSize%3 == 2)
2590 {
2591 int n = outSize/3;
2592 unsigned char a = base64decodeTable[(int)data[4*n]];
2593 unsigned char b = base64decodeTable[(int)data[4*n + 1]];
2594 unsigned char c = base64decodeTable[(int)data[4*n + 2]];
2595 decodedData[outSize - 2] = (a << 2) | (b >> 4);
2596 decodedData[outSize - 1] = (b << 4) | (c >> 2);
2597 }
2598
2599 *outputSize = outSize;
2600 return decodedData;
2601}
2602
2603// Compute CRC32 hash code
2604unsigned int ComputeCRC32(unsigned char *data, int dataSize)
2605{
2606 static unsigned int crcTable[256] = {
2607 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
2608 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
2609 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
2610 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
2611 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
2612 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
2613 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
2614 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
2615 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
2616 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
2617 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
2618 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
2619 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
2620 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
2621 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
2622 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
2623 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
2624 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
2625 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
2626 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
2627 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
2628 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
2629 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
2630 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
2631 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
2632 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
2633 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
2634 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
2635 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
2636 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
2637 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
2638 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
2639 };
2640
2641 unsigned int crc = ~0u;
2642
2643 for (int i = 0; i < dataSize; i++) crc = (crc >> 8) ^ crcTable[data[i] ^ (crc & 0xff)];
2644
2645 return ~crc;
2646}
2647
2648// Compute MD5 hash code
2649// NOTE: Returns a static int[4] array (16 bytes)
2650unsigned int *ComputeMD5(unsigned char *data, int dataSize)
2651{
2652 #define ROTATE_LEFT(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
2653
2654 static unsigned int hash[4] = { 0 }; // Hash to be returned
2655
2656 // WARNING: All variables are unsigned 32 bit and wrap modulo 2^32 when calculating
2657
2658 // NOTE: r specifies the per-round shift amounts
2659 unsigned int r[] = {
2660 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
2661 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
2662 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
2663 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
2664 };
2665
2666 // Using binary integer part of the sines of integers (in radians) as constants
2667 unsigned int k[] = {
2668 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
2669 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
2670 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
2671 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
2672 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
2673 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
2674 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
2675 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
2676 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
2677 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
2678 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
2679 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
2680 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
2681 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
2682 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
2683 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
2684 };
2685
2686 hash[0] = 0x67452301;
2687 hash[1] = 0xefcdab89;
2688 hash[2] = 0x98badcfe;
2689 hash[3] = 0x10325476;
2690
2691 // Pre-processing: adding a single 1 bit
2692 // Append '1' bit to message
2693 // NOTE: The input bytes are considered as bits strings,
2694 // where the first bit is the most significant bit of the byte
2695
2696 // Pre-processing: padding with zeros
2697 // Append '0' bit until message length in bit 448 (mod 512)
2698 // Append length mod (2 pow 64) to message
2699
2700 int newDataSize = ((((dataSize + 8)/64) + 1)*64) - 8;
2701
2702 unsigned char *msg = RL_CALLOC(newDataSize + 64, 1); // Initialize with '0' bits, allocating 64 extra bytes
2703 memcpy(msg, data, dataSize);
2704 msg[dataSize] = 128; // Write the '1' bit
2705
2706 unsigned int bitsLen = 8*dataSize;
2707 memcpy(msg + newDataSize, &bitsLen, 4); // Append the len in bits at the end of the buffer
2708
2709 // Process the message in successive 512-bit chunks for each 512-bit chunk of message
2710 for (int offset = 0; offset < newDataSize; offset += (512/8))
2711 {
2712 // Break chunk into sixteen 32-bit words w[j], 0 <= j <= 15
2713 unsigned int *w = (unsigned int *)(msg + offset);
2714
2715 // Initialize hash value for this chunk
2716 unsigned int a = hash[0];
2717 unsigned int b = hash[1];
2718 unsigned int c = hash[2];
2719 unsigned int d = hash[3];
2720
2721 for (int i = 0; i < 64; i++)
2722 {
2723 unsigned int f = 0;
2724 unsigned int g = 0;
2725
2726 if (i < 16)
2727 {
2728 f = (b & c) | ((~b) & d);
2729 g = i;
2730 }
2731 else if (i < 32)
2732 {
2733 f = (d & b) | ((~d) & c);
2734 g = (5*i + 1)%16;
2735 }
2736 else if (i < 48)
2737 {
2738 f = b ^ c ^ d;
2739 g = (3*i + 5)%16;
2740 }
2741 else
2742 {
2743 f = c ^ (b | (~d));
2744 g = (7*i)%16;
2745 }
2746
2747 unsigned int temp = d;
2748 d = c;
2749 c = b;
2750 b = b + ROTATE_LEFT((a + f + k[i] + w[g]), r[i]);
2751 a = temp;
2752 }
2753
2754 // Add chunk's hash to result so far
2755 hash[0] += a;
2756 hash[1] += b;
2757 hash[2] += c;
2758 hash[3] += d;
2759 }
2760
2761 RL_FREE(msg);
2762
2763 return hash;
2764}
2765
2766// Compute SHA-1 hash code
2767// NOTE: Returns a static int[5] array (20 bytes)
2768unsigned int *ComputeSHA1(unsigned char *data, int dataSize) {
2769 #define ROTATE_LEFT(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
2770
2771 static unsigned int hash[5] = { 0 }; // Hash to be returned
2772
2773 // Initialize hash values
2774 hash[0] = 0x67452301;
2775 hash[1] = 0xEFCDAB89;
2776 hash[2] = 0x98BADCFE;
2777 hash[3] = 0x10325476;
2778 hash[4] = 0xC3D2E1F0;
2779
2780 // Pre-processing: adding a single 1 bit
2781 // Append '1' bit to message
2782 // NOTE: The input bytes are considered as bits strings,
2783 // where the first bit is the most significant bit of the byte
2784
2785 // Pre-processing: padding with zeros
2786 // Append '0' bit until message length in bit 448 (mod 512)
2787 // Append length mod (2 pow 64) to message
2788
2789 int newDataSize = ((((dataSize + 8)/64) + 1)*64);
2790
2791 unsigned char *msg = RL_CALLOC(newDataSize, 1); // Initialize with '0' bits
2792 memcpy(msg, data, dataSize);
2793 msg[dataSize] = 128; // Write the '1' bit
2794
2795 unsigned int bitsLen = 8*dataSize;
2796 msg[newDataSize-1] = bitsLen;
2797
2798 // Process the message in successive 512-bit chunks
2799 for (int offset = 0; offset < newDataSize; offset += (512/8))
2800 {
2801 // Break chunk into sixteen 32-bit words w[j], 0 <= j <= 15
2802 unsigned int w[80] = {0};
2803 for (int i = 0; i < 16; i++) {
2804 w[i] = (msg[offset + (i * 4) + 0] << 24) |
2805 (msg[offset + (i * 4) + 1] << 16) |
2806 (msg[offset + (i * 4) + 2] << 8) |
2807 (msg[offset + (i * 4) + 3]);
2808 }
2809
2810 // Message schedule: extend the sixteen 32-bit words into eighty 32-bit words:
2811 for (int i = 16; i < 80; ++i) {
2812 w[i] = ROTATE_LEFT(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);
2813 }
2814
2815 // Initialize hash value for this chunk
2816 unsigned int a = hash[0];
2817 unsigned int b = hash[1];
2818 unsigned int c = hash[2];
2819 unsigned int d = hash[3];
2820 unsigned int e = hash[4];
2821
2822 for (int i = 0; i < 80; i++)
2823 {
2824 unsigned int f = 0;
2825 unsigned int k = 0;
2826
2827 if (i < 20) {
2828 f = (b & c) | ((~b) & d);
2829 k = 0x5A827999;
2830 } else if (i < 40) {
2831 f = b ^ c ^ d;
2832 k = 0x6ED9EBA1;
2833 } else if (i < 60) {
2834 f = (b & c) | (b & d) | (c & d);
2835 k = 0x8F1BBCDC;
2836 } else {
2837 f = b ^ c ^ d;
2838 k = 0xCA62C1D6;
2839 }
2840
2841 unsigned int temp = ROTATE_LEFT(a, 5) + f + e + k + w[i];
2842 e = d;
2843 d = c;
2844 c = ROTATE_LEFT(b, 30);
2845 b = a;
2846 a = temp;
2847 }
2848
2849 // Add this chunk's hash to result so far
2850 hash[0] += a;
2851 hash[1] += b;
2852 hash[2] += c;
2853 hash[3] += d;
2854 hash[4] += e;
2855 }
2856
2857 free(msg);
2858
2859 return hash;
2860}
2861
2862//----------------------------------------------------------------------------------
2863// Module Functions Definition: Automation Events Recording and Playing
2864//----------------------------------------------------------------------------------
2865
2866// Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS
2867AutomationEventList LoadAutomationEventList(const char *fileName)
2868{
2869 AutomationEventList list = { 0 };
2870
2871 // Allocate and empty automation event list, ready to record new events
2872 list.events = (AutomationEvent *)RL_CALLOC(MAX_AUTOMATION_EVENTS, sizeof(AutomationEvent));
2873 list.capacity = MAX_AUTOMATION_EVENTS;
2874
2875#if defined(SUPPORT_AUTOMATION_EVENTS)
2876 if (fileName == NULL) TRACELOG(LOG_INFO, "AUTOMATION: New empty events list loaded successfully");
2877 else
2878 {
2879 // Load automation events file (binary)
2880 /*
2881 //int dataSize = 0;
2882 //unsigned char *data = LoadFileData(fileName, &dataSize);
2883
2884 FILE *raeFile = fopen(fileName, "rb");
2885 unsigned char fileId[4] = { 0 };
2886
2887 fread(fileId, 1, 4, raeFile);
2888
2889 if ((fileId[0] == 'r') && (fileId[1] == 'A') && (fileId[2] == 'E') && (fileId[1] == ' '))
2890 {
2891 fread(&eventCount, sizeof(int), 1, raeFile);
2892 TRACELOG(LOG_WARNING, "Events loaded: %i\n", eventCount);
2893 fread(events, sizeof(AutomationEvent), eventCount, raeFile);
2894 }
2895
2896 fclose(raeFile);
2897 */
2898
2899 // Load events file (text)
2900 //unsigned char *buffer = LoadFileText(fileName);
2901 FILE *raeFile = fopen(fileName, "rt");
2902
2903 if (raeFile != NULL)
2904 {
2905 unsigned int counter = 0;
2906 char buffer[256] = { 0 };
2907 char eventDesc[64] = { 0 };
2908
2909 fgets(buffer, 256, raeFile);
2910
2911 while (!feof(raeFile))
2912 {
2913 switch (buffer[0])
2914 {
2915 case 'c': sscanf(buffer, "c %i", &list.count); break;
2916 case 'e':
2917 {
2918 sscanf(buffer, "e %d %d %d %d %d %d %[^\n]s", &list.events[counter].frame, &list.events[counter].type,
2919 &list.events[counter].params[0], &list.events[counter].params[1], &list.events[counter].params[2], &list.events[counter].params[3], eventDesc);
2920
2921 counter++;
2922 } break;
2923 default: break;
2924 }
2925
2926 fgets(buffer, 256, raeFile);
2927 }
2928
2929 if (counter != list.count)
2930 {
2931 TRACELOG(LOG_WARNING, "AUTOMATION: Events read from file [%i] do not mach event count specified [%i]", counter, list.count);
2932 list.count = counter;
2933 }
2934
2935 fclose(raeFile);
2936
2937 TRACELOG(LOG_INFO, "AUTOMATION: Events file loaded successfully");
2938 }
2939
2940 TRACELOG(LOG_INFO, "AUTOMATION: Events loaded from file: %i", list.count);
2941 }
2942#endif
2943 return list;
2944}
2945
2946// Unload automation events list from file
2947void UnloadAutomationEventList(AutomationEventList list)
2948{
2949#if defined(SUPPORT_AUTOMATION_EVENTS)
2950 RL_FREE(list.events);
2951#endif
2952}
2953
2954// Export automation events list as text file
2955bool ExportAutomationEventList(AutomationEventList list, const char *fileName)
2956{
2957 bool success = false;
2958
2959#if defined(SUPPORT_AUTOMATION_EVENTS)
2960 // Export events as binary file
2961 // TODO: Save to memory buffer and SaveFileData()
2962 /*
2963 unsigned char fileId[4] = "rAE ";
2964 FILE *raeFile = fopen(fileName, "wb");
2965 fwrite(fileId, sizeof(unsigned char), 4, raeFile);
2966 fwrite(&eventCount, sizeof(int), 1, raeFile);
2967 fwrite(events, sizeof(AutomationEvent), eventCount, raeFile);
2968 fclose(raeFile);
2969 */
2970
2971 // Export events as text
2972 // TODO: Save to memory buffer and SaveFileText()
2973 char *txtData = (char *)RL_CALLOC(256*list.count + 2048, sizeof(char)); // 256 characters per line plus some header
2974
2975 int byteCount = 0;
2976 byteCount += sprintf(txtData + byteCount, "#\n");
2977 byteCount += sprintf(txtData + byteCount, "# Automation events exporter v1.0 - raylib automation events list\n");
2978 byteCount += sprintf(txtData + byteCount, "#\n");
2979 byteCount += sprintf(txtData + byteCount, "# c <events_count>\n");
2980 byteCount += sprintf(txtData + byteCount, "# e <frame> <event_type> <param0> <param1> <param2> <param3> // <event_type_name>\n");
2981 byteCount += sprintf(txtData + byteCount, "#\n");
2982 byteCount += sprintf(txtData + byteCount, "# more info and bugs-report: github.com/raysan5/raylib\n");
2983 byteCount += sprintf(txtData + byteCount, "# feedback and support: ray[at]raylib.com\n");
2984 byteCount += sprintf(txtData + byteCount, "#\n");
2985 byteCount += sprintf(txtData + byteCount, "# Copyright (c) 2023-2024 Ramon Santamaria (@raysan5)\n");
2986 byteCount += sprintf(txtData + byteCount, "#\n\n");
2987
2988 // Add events data
2989 byteCount += sprintf(txtData + byteCount, "c %i\n", list.count);
2990 for (unsigned int i = 0; i < list.count; i++)
2991 {
2992 byteCount += snprintf(txtData + byteCount, 256, "e %i %i %i %i %i %i // Event: %s\n", list.events[i].frame, list.events[i].type,
2993 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]);
2994 }
2995
2996 // NOTE: Text data size exported is determined by '\0' (NULL) character
2997 success = SaveFileText(fileName, txtData);
2998
2999 RL_FREE(txtData);
3000#endif
3001
3002 return success;
3003}
3004
3005// Setup automation event list to record to
3006void SetAutomationEventList(AutomationEventList *list)
3007{
3008#if defined(SUPPORT_AUTOMATION_EVENTS)
3009 currentEventList = list;
3010#endif
3011}
3012
3013// Set automation event internal base frame to start recording
3014void SetAutomationEventBaseFrame(int frame)
3015{
3016 CORE.Time.frameCounter = frame;
3017}
3018
3019// Start recording automation events (AutomationEventList must be set)
3020void StartAutomationEventRecording(void)
3021{
3022#if defined(SUPPORT_AUTOMATION_EVENTS)
3023 automationEventRecording = true;
3024#endif
3025}
3026
3027// Stop recording automation events
3028void StopAutomationEventRecording(void)
3029{
3030#if defined(SUPPORT_AUTOMATION_EVENTS)
3031 automationEventRecording = false;
3032#endif
3033}
3034
3035// Play a recorded automation event
3036void PlayAutomationEvent(AutomationEvent event)
3037{
3038#if defined(SUPPORT_AUTOMATION_EVENTS)
3039 // WARNING: When should event be played? After/before/replace PollInputEvents()? -> Up to the user!
3040
3041 if (!automationEventRecording) // TODO: Allow recording events while playing?
3042 {
3043 switch (event.type)
3044 {
3045 // Input event
3046 case INPUT_KEY_UP: CORE.Input.Keyboard.currentKeyState[event.params[0]] = false; break; // param[0]: key
3047 case INPUT_KEY_DOWN: { // param[0]: key
3048 CORE.Input.Keyboard.currentKeyState[event.params[0]] = true;
3049
3050 if (CORE.Input.Keyboard.previousKeyState[event.params[0]] == false)
3051 {
3052 if (CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE)
3053 {
3054 // Add character to the queue
3055 CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = event.params[0];
3056 CORE.Input.Keyboard.keyPressedQueueCount++;
3057 }
3058 }
3059 } break;
3060 case INPUT_MOUSE_BUTTON_UP: CORE.Input.Mouse.currentButtonState[event.params[0]] = false; break; // param[0]: key
3061 case INPUT_MOUSE_BUTTON_DOWN: CORE.Input.Mouse.currentButtonState[event.params[0]] = true; break; // param[0]: key
3062 case INPUT_MOUSE_POSITION: // param[0]: x, param[1]: y
3063 {
3064 CORE.Input.Mouse.currentPosition.x = (float)event.params[0];
3065 CORE.Input.Mouse.currentPosition.y = (float)event.params[1];
3066 } break;
3067 case INPUT_MOUSE_WHEEL_MOTION: // param[0]: x delta, param[1]: y delta
3068 {
3069 CORE.Input.Mouse.currentWheelMove.x = (float)event.params[0];
3070 CORE.Input.Mouse.currentWheelMove.y = (float)event.params[1];
3071 } break;
3072 case INPUT_TOUCH_UP: CORE.Input.Touch.currentTouchState[event.params[0]] = false; break; // param[0]: id
3073 case INPUT_TOUCH_DOWN: CORE.Input.Touch.currentTouchState[event.params[0]] = true; break; // param[0]: id
3074 case INPUT_TOUCH_POSITION: // param[0]: id, param[1]: x, param[2]: y
3075 {
3076 CORE.Input.Touch.position[event.params[0]].x = (float)event.params[1];
3077 CORE.Input.Touch.position[event.params[0]].y = (float)event.params[2];
3078 } break;
3079 case INPUT_GAMEPAD_CONNECT: CORE.Input.Gamepad.ready[event.params[0]] = true; break; // param[0]: gamepad
3080 case INPUT_GAMEPAD_DISCONNECT: CORE.Input.Gamepad.ready[event.params[0]] = false; break; // param[0]: gamepad
3081 case INPUT_GAMEPAD_BUTTON_UP: CORE.Input.Gamepad.currentButtonState[event.params[0]][event.params[1]] = false; break; // param[0]: gamepad, param[1]: button
3082 case INPUT_GAMEPAD_BUTTON_DOWN: CORE.Input.Gamepad.currentButtonState[event.params[0]][event.params[1]] = true; break; // param[0]: gamepad, param[1]: button
3083 case INPUT_GAMEPAD_AXIS_MOTION: // param[0]: gamepad, param[1]: axis, param[2]: delta
3084 {
3085 CORE.Input.Gamepad.axisState[event.params[0]][event.params[1]] = ((float)event.params[2]/32768.0f);
3086 } break;
3087 #if defined(SUPPORT_GESTURES_SYSTEM)
3088 case INPUT_GESTURE: GESTURES.current = event.params[0]; break; // param[0]: gesture (enum Gesture) -> rgestures.h: GESTURES.current
3089 #endif
3090 // Window event
3091 case WINDOW_CLOSE: CORE.Window.shouldClose = true; break;
3092 case WINDOW_MAXIMIZE: MaximizeWindow(); break;
3093 case WINDOW_MINIMIZE: MinimizeWindow(); break;
3094 case WINDOW_RESIZE: SetWindowSize(event.params[0], event.params[1]); break;
3095
3096 // Custom event
3097 #if defined(SUPPORT_SCREEN_CAPTURE)
3098 case ACTION_TAKE_SCREENSHOT:
3099 {
3100 TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
3101 screenshotCounter++;
3102 } break;
3103 #endif
3104 case ACTION_SETTARGETFPS: SetTargetFPS(event.params[0]); break;
3105 default: break;
3106 }
3107
3108 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]);
3109 }
3110#endif
3111}
3112
3113//----------------------------------------------------------------------------------
3114// Module Functions Definition: Input Handling: Keyboard
3115//----------------------------------------------------------------------------------
3116
3117// Check if a key has been pressed once
3118bool IsKeyPressed(int key)
3119{
3120
3121 bool pressed = false;
3122
3123 if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
3124 {
3125 if ((CORE.Input.Keyboard.previousKeyState[key] == 0) && (CORE.Input.Keyboard.currentKeyState[key] == 1)) pressed = true;
3126 }
3127
3128 return pressed;
3129}
3130
3131// Check if a key has been pressed again
3132bool IsKeyPressedRepeat(int key)
3133{
3134 bool repeat = false;
3135
3136 if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
3137 {
3138 if (CORE.Input.Keyboard.keyRepeatInFrame[key] == 1) repeat = true;
3139 }
3140
3141 return repeat;
3142}
3143
3144// Check if a key is being pressed (key held down)
3145bool IsKeyDown(int key)
3146{
3147 bool down = false;
3148
3149 if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
3150 {
3151 if (CORE.Input.Keyboard.currentKeyState[key] == 1) down = true;
3152 }
3153
3154 return down;
3155}
3156
3157// Check if a key has been released once
3158bool IsKeyReleased(int key)
3159{
3160 bool released = false;
3161
3162 if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
3163 {
3164 if ((CORE.Input.Keyboard.previousKeyState[key] == 1) && (CORE.Input.Keyboard.currentKeyState[key] == 0)) released = true;
3165 }
3166
3167 return released;
3168}
3169
3170// Check if a key is NOT being pressed (key not held down)
3171bool IsKeyUp(int key)
3172{
3173 bool up = false;
3174
3175 if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
3176 {
3177 if (CORE.Input.Keyboard.currentKeyState[key] == 0) up = true;
3178 }
3179
3180 return up;
3181}
3182
3183// Get the last key pressed
3184int GetKeyPressed(void)
3185{
3186 int value = 0;
3187
3188 if (CORE.Input.Keyboard.keyPressedQueueCount > 0)
3189 {
3190 // Get character from the queue head
3191 value = CORE.Input.Keyboard.keyPressedQueue[0];
3192
3193 // Shift elements 1 step toward the head
3194 for (int i = 0; i < (CORE.Input.Keyboard.keyPressedQueueCount - 1); i++)
3195 CORE.Input.Keyboard.keyPressedQueue[i] = CORE.Input.Keyboard.keyPressedQueue[i + 1];
3196
3197 // Reset last character in the queue
3198 CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount - 1] = 0;
3199 CORE.Input.Keyboard.keyPressedQueueCount--;
3200 }
3201
3202 return value;
3203}
3204
3205// Get the last char pressed
3206int GetCharPressed(void)
3207{
3208 int value = 0;
3209
3210 if (CORE.Input.Keyboard.charPressedQueueCount > 0)
3211 {
3212 // Get character from the queue head
3213 value = CORE.Input.Keyboard.charPressedQueue[0];
3214
3215 // Shift elements 1 step toward the head
3216 for (int i = 0; i < (CORE.Input.Keyboard.charPressedQueueCount - 1); i++)
3217 CORE.Input.Keyboard.charPressedQueue[i] = CORE.Input.Keyboard.charPressedQueue[i + 1];
3218
3219 // Reset last character in the queue
3220 CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount - 1] = 0;
3221 CORE.Input.Keyboard.charPressedQueueCount--;
3222 }
3223
3224 return value;
3225}
3226
3227// Set a custom key to exit program
3228// NOTE: default exitKey is set to ESCAPE
3229void SetExitKey(int key)
3230{
3231 CORE.Input.Keyboard.exitKey = key;
3232}
3233
3234//----------------------------------------------------------------------------------
3235// Module Functions Definition: Input Handling: Gamepad
3236//----------------------------------------------------------------------------------
3237
3238// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
3239//int SetGamepadMappings(const char *mappings)
3240
3241// Check if a gamepad is available
3242bool IsGamepadAvailable(int gamepad)
3243{
3244 bool result = false;
3245
3246 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad]) result = true;
3247
3248 return result;
3249}
3250
3251// Get gamepad internal name id
3252const char *GetGamepadName(int gamepad)
3253{
3254 return CORE.Input.Gamepad.name[gamepad];
3255}
3256
3257// Check if a gamepad button has been pressed once
3258bool IsGamepadButtonPressed(int gamepad, int button)
3259{
3260 bool pressed = false;
3261
3262 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
3263 (CORE.Input.Gamepad.previousButtonState[gamepad][button] == 0) && (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 1)) pressed = true;
3264
3265 return pressed;
3266}
3267
3268// Check if a gamepad button is being pressed
3269bool IsGamepadButtonDown(int gamepad, int button)
3270{
3271 bool down = false;
3272
3273 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
3274 (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 1)) down = true;
3275
3276 return down;
3277}
3278
3279// Check if a gamepad button has NOT been pressed once
3280bool IsGamepadButtonReleased(int gamepad, int button)
3281{
3282 bool released = false;
3283
3284 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
3285 (CORE.Input.Gamepad.previousButtonState[gamepad][button] == 1) && (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 0)) released = true;
3286
3287 return released;
3288}
3289
3290// Check if a gamepad button is NOT being pressed
3291bool IsGamepadButtonUp(int gamepad, int button)
3292{
3293 bool up = false;
3294
3295 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
3296 (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 0)) up = true;
3297
3298 return up;
3299}
3300
3301// Get the last gamepad button pressed
3302int GetGamepadButtonPressed(void)
3303{
3304 return CORE.Input.Gamepad.lastButtonPressed;
3305}
3306
3307// Get gamepad axis count
3308int GetGamepadAxisCount(int gamepad)
3309{
3310 return CORE.Input.Gamepad.axisCount[gamepad];
3311}
3312
3313// Get axis movement vector for a gamepad
3314float GetGamepadAxisMovement(int gamepad, int axis)
3315{
3316 float value = (axis == GAMEPAD_AXIS_LEFT_TRIGGER || axis == GAMEPAD_AXIS_RIGHT_TRIGGER)? -1.0f : 0.0f;
3317
3318 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXIS)) {
3319 float movement = value < 0.0f ? CORE.Input.Gamepad.axisState[gamepad][axis] : fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]);
3320
3321 if (movement > value) value = CORE.Input.Gamepad.axisState[gamepad][axis];
3322 }
3323
3324 return value;
3325}
3326
3327//----------------------------------------------------------------------------------
3328// Module Functions Definition: Input Handling: Mouse
3329//----------------------------------------------------------------------------------
3330
3331// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
3332//void SetMousePosition(int x, int y)
3333//void SetMouseCursor(int cursor)
3334
3335// Check if a mouse button has been pressed once
3336bool IsMouseButtonPressed(int button)
3337{
3338 bool pressed = false;
3339
3340 if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) pressed = true;
3341
3342 // Map touches to mouse buttons checking
3343 if ((CORE.Input.Touch.currentTouchState[button] == 1) && (CORE.Input.Touch.previousTouchState[button] == 0)) pressed = true;
3344
3345 return pressed;
3346}
3347
3348// Check if a mouse button is being pressed
3349bool IsMouseButtonDown(int button)
3350{
3351 bool down = false;
3352
3353 if (CORE.Input.Mouse.currentButtonState[button] == 1) down = true;
3354
3355 // NOTE: Touches are considered like mouse buttons
3356 if (CORE.Input.Touch.currentTouchState[button] == 1) down = true;
3357
3358 return down;
3359}
3360
3361// Check if a mouse button has been released once
3362bool IsMouseButtonReleased(int button)
3363{
3364 bool released = false;
3365
3366 if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) released = true;
3367
3368 // Map touches to mouse buttons checking
3369 if ((CORE.Input.Touch.currentTouchState[button] == 0) && (CORE.Input.Touch.previousTouchState[button] == 1)) released = true;
3370
3371 return released;
3372}
3373
3374// Check if a mouse button is NOT being pressed
3375bool IsMouseButtonUp(int button)
3376{
3377 bool up = false;
3378
3379 if (CORE.Input.Mouse.currentButtonState[button] == 0) up = true;
3380
3381 // NOTE: Touches are considered like mouse buttons
3382 if (CORE.Input.Touch.currentTouchState[button] == 0) up = true;
3383
3384 return up;
3385}
3386
3387// Get mouse position X
3388int GetMouseX(void)
3389{
3390 int mouseX = (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x);
3391 return mouseX;
3392}
3393
3394// Get mouse position Y
3395int GetMouseY(void)
3396{
3397 int mouseY = (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y);
3398 return mouseY;
3399}
3400
3401// Get mouse position XY
3402Vector2 GetMousePosition(void)
3403{
3404 Vector2 position = { 0 };
3405
3406 position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x;
3407 position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y;
3408
3409 return position;
3410}
3411
3412// Get mouse delta between frames
3413Vector2 GetMouseDelta(void)
3414{
3415 Vector2 delta = { 0 };
3416
3417 delta.x = CORE.Input.Mouse.currentPosition.x - CORE.Input.Mouse.previousPosition.x;
3418 delta.y = CORE.Input.Mouse.currentPosition.y - CORE.Input.Mouse.previousPosition.y;
3419
3420 return delta;
3421}
3422
3423// Set mouse offset
3424// NOTE: Useful when rendering to different size targets
3425void SetMouseOffset(int offsetX, int offsetY)
3426{
3427 CORE.Input.Mouse.offset = (Vector2){ (float)offsetX, (float)offsetY };
3428}
3429
3430// Set mouse scaling
3431// NOTE: Useful when rendering to different size targets
3432void SetMouseScale(float scaleX, float scaleY)
3433{
3434 CORE.Input.Mouse.scale = (Vector2){ scaleX, scaleY };
3435}
3436
3437// Get mouse wheel movement Y
3438float GetMouseWheelMove(void)
3439{
3440 float result = 0.0f;
3441
3442 if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x;
3443 else result = (float)CORE.Input.Mouse.currentWheelMove.y;
3444
3445 return result;
3446}
3447
3448// Get mouse wheel movement X/Y as a vector
3449Vector2 GetMouseWheelMoveV(void)
3450{
3451 Vector2 result = { 0 };
3452
3453 result = CORE.Input.Mouse.currentWheelMove;
3454
3455 return result;
3456}
3457
3458//----------------------------------------------------------------------------------
3459// Module Functions Definition: Input Handling: Touch
3460//----------------------------------------------------------------------------------
3461
3462// Get touch position X for touch point 0 (relative to screen size)
3463int GetTouchX(void)
3464{
3465 int touchX = (int)CORE.Input.Touch.position[0].x;
3466 return touchX;
3467}
3468
3469// Get touch position Y for touch point 0 (relative to screen size)
3470int GetTouchY(void)
3471{
3472 int touchY = (int)CORE.Input.Touch.position[0].y;
3473 return touchY;
3474}
3475
3476// Get touch position XY for a touch point index (relative to screen size)
3477// TODO: Touch position should be scaled depending on display size and render size
3478Vector2 GetTouchPosition(int index)
3479{
3480 Vector2 position = { -1.0f, -1.0f };
3481
3482 if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index];
3483 else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS);
3484
3485 return position;
3486}
3487
3488// Get touch point identifier for given index
3489int GetTouchPointId(int index)
3490{
3491 int id = -1;
3492
3493 if (index < MAX_TOUCH_POINTS) id = CORE.Input.Touch.pointId[index];
3494
3495 return id;
3496}
3497
3498// Get number of touch points
3499int GetTouchPointCount(void)
3500{
3501 return CORE.Input.Touch.pointCount;
3502}
3503
3504//----------------------------------------------------------------------------------
3505// Module Internal Functions Definition
3506//----------------------------------------------------------------------------------
3507
3508// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
3509//int InitPlatform(void)
3510//void ClosePlatform(void)
3511
3512// Initialize hi-resolution timer
3513void InitTimer(void)
3514{
3515 // Setting a higher resolution can improve the accuracy of time-out intervals in wait functions
3516 // However, it can also reduce overall system performance, because the thread scheduler switches tasks more often
3517 // High resolutions can also prevent the CPU power management system from entering power-saving modes
3518 // Setting a higher resolution does not improve the accuracy of the high-resolution performance counter
3519#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) && !defined(PLATFORM_DESKTOP_SDL)
3520 timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms)
3521#endif
3522
3523#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
3524 struct timespec now = { 0 };
3525
3526 if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success
3527 {
3528 CORE.Time.base = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec;
3529 }
3530 else TRACELOG(LOG_WARNING, "TIMER: Hi-resolution timer not available");
3531#endif
3532
3533 CORE.Time.previous = GetTime(); // Get time as double
3534}
3535
3536// Set viewport for a provided width and height
3537void SetupViewport(int width, int height)
3538{
3539 CORE.Window.render.width = width;
3540 CORE.Window.render.height = height;
3541
3542 // Set viewport width and height
3543 // NOTE: We consider render size (scaled) and offset in case black bars are required and
3544 // render area does not match full display area (this situation is only applicable on fullscreen mode)
3545#if defined(__APPLE__)
3546 Vector2 scale = GetWindowScaleDPI();
3547 rlViewport(CORE.Window.renderOffset.x/2*scale.x, CORE.Window.renderOffset.y/2*scale.y, (CORE.Window.render.width)*scale.x, (CORE.Window.render.height)*scale.y);
3548#else
3549 rlViewport(CORE.Window.renderOffset.x/2, CORE.Window.renderOffset.y/2, CORE.Window.render.width, CORE.Window.render.height);
3550#endif
3551
3552 rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
3553 rlLoadIdentity(); // Reset current matrix (projection)
3554
3555 // Set orthographic projection to current framebuffer size
3556 // NOTE: Configured top-left corner as (0, 0)
3557 rlOrtho(0, CORE.Window.render.width, CORE.Window.render.height, 0, 0.0f, 1.0f);
3558
3559 rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
3560 rlLoadIdentity(); // Reset current matrix (modelview)
3561}
3562
3563// Compute framebuffer size relative to screen size and display size
3564// NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified
3565void SetupFramebuffer(int width, int height)
3566{
3567 // Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var)
3568 if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height))
3569 {
3570 TRACELOG(LOG_WARNING, "DISPLAY: Downscaling required: Screen size (%ix%i) is bigger than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
3571
3572 // Downscaling to fit display with border-bars
3573 float widthRatio = (float)CORE.Window.display.width/(float)CORE.Window.screen.width;
3574 float heightRatio = (float)CORE.Window.display.height/(float)CORE.Window.screen.height;
3575
3576 if (widthRatio <= heightRatio)
3577 {
3578 CORE.Window.render.width = CORE.Window.display.width;
3579 CORE.Window.render.height = (int)round((float)CORE.Window.screen.height*widthRatio);
3580 CORE.Window.renderOffset.x = 0;
3581 CORE.Window.renderOffset.y = (CORE.Window.display.height - CORE.Window.render.height);
3582 }
3583 else
3584 {
3585 CORE.Window.render.width = (int)round((float)CORE.Window.screen.width*heightRatio);
3586 CORE.Window.render.height = CORE.Window.display.height;
3587 CORE.Window.renderOffset.x = (CORE.Window.display.width - CORE.Window.render.width);
3588 CORE.Window.renderOffset.y = 0;
3589 }
3590
3591 // Screen scaling required
3592 float scaleRatio = (float)CORE.Window.render.width/(float)CORE.Window.screen.width;
3593 CORE.Window.screenScale = MatrixScale(scaleRatio, scaleRatio, 1.0f);
3594
3595 // NOTE: We render to full display resolution!
3596 // We just need to calculate above parameters for downscale matrix and offsets
3597 CORE.Window.render.width = CORE.Window.display.width;
3598 CORE.Window.render.height = CORE.Window.display.height;
3599
3600 TRACELOG(LOG_WARNING, "DISPLAY: Downscale matrix generated, content will be rendered at (%ix%i)", CORE.Window.render.width, CORE.Window.render.height);
3601 }
3602 else if ((CORE.Window.screen.width < CORE.Window.display.width) || (CORE.Window.screen.height < CORE.Window.display.height))
3603 {
3604 // Required screen size is smaller than display size
3605 TRACELOG(LOG_INFO, "DISPLAY: Upscaling required: Screen size (%ix%i) smaller than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
3606
3607 if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0))
3608 {
3609 CORE.Window.screen.width = CORE.Window.display.width;
3610 CORE.Window.screen.height = CORE.Window.display.height;
3611 }
3612
3613 // Upscaling to fit display with border-bars
3614 float displayRatio = (float)CORE.Window.display.width/(float)CORE.Window.display.height;
3615 float screenRatio = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height;
3616
3617 if (displayRatio <= screenRatio)
3618 {
3619 CORE.Window.render.width = CORE.Window.screen.width;
3620 CORE.Window.render.height = (int)round((float)CORE.Window.screen.width/displayRatio);
3621 CORE.Window.renderOffset.x = 0;
3622 CORE.Window.renderOffset.y = (CORE.Window.render.height - CORE.Window.screen.height);
3623 }
3624 else
3625 {
3626 CORE.Window.render.width = (int)round((float)CORE.Window.screen.height*displayRatio);
3627 CORE.Window.render.height = CORE.Window.screen.height;
3628 CORE.Window.renderOffset.x = (CORE.Window.render.width - CORE.Window.screen.width);
3629 CORE.Window.renderOffset.y = 0;
3630 }
3631 }
3632 else
3633 {
3634 CORE.Window.render.width = CORE.Window.screen.width;
3635 CORE.Window.render.height = CORE.Window.screen.height;
3636 CORE.Window.renderOffset.x = 0;
3637 CORE.Window.renderOffset.y = 0;
3638 }
3639}
3640
3641// Scan all files and directories in a base path
3642// WARNING: files.paths[] must be previously allocated and
3643// contain enough space to store all required paths
3644static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const char *filter)
3645{
3646 static char path[MAX_FILEPATH_LENGTH] = { 0 };
3647 memset(path, 0, MAX_FILEPATH_LENGTH);
3648
3649 struct dirent *dp = NULL;
3650 DIR *dir = opendir(basePath);
3651
3652 if (dir != NULL)
3653 {
3654 while ((dp = readdir(dir)) != NULL)
3655 {
3656 if ((strcmp(dp->d_name, ".") != 0) &&
3657 (strcmp(dp->d_name, "..") != 0))
3658 {
3659 #if defined(_WIN32)
3660 sprintf(path, "%s\\%s", basePath, dp->d_name);
3661 #else
3662 sprintf(path, "%s/%s", basePath, dp->d_name);
3663 #endif
3664
3665 if (filter != NULL)
3666 {
3667 if (IsPathFile(path))
3668 {
3669 if (IsFileExtension(path, filter))
3670 {
3671 strcpy(files->paths[files->count], path);
3672 files->count++;
3673 }
3674 }
3675 else
3676 {
3677 if (TextFindIndex(filter, DIRECTORY_FILTER_TAG) >= 0)
3678 {
3679 strcpy(files->paths[files->count], path);
3680 files->count++;
3681 }
3682 }
3683 }
3684 else
3685 {
3686 strcpy(files->paths[files->count], path);
3687 files->count++;
3688 }
3689 }
3690 }
3691
3692 closedir(dir);
3693 }
3694 else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath);
3695}
3696
3697// Scan all files and directories recursively from a base path
3698static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *files, const char *filter)
3699{
3700 char path[MAX_FILEPATH_LENGTH] = { 0 };
3701 memset(path, 0, MAX_FILEPATH_LENGTH);
3702
3703 struct dirent *dp = NULL;
3704 DIR *dir = opendir(basePath);
3705
3706 if (dir != NULL)
3707 {
3708 while (((dp = readdir(dir)) != NULL) && (files->count < files->capacity))
3709 {
3710 if ((strcmp(dp->d_name, ".") != 0) && (strcmp(dp->d_name, "..") != 0))
3711 {
3712 // Construct new path from our base path
3713 #if defined(_WIN32)
3714 sprintf(path, "%s\\%s", basePath, dp->d_name);
3715 #else
3716 sprintf(path, "%s/%s", basePath, dp->d_name);
3717 #endif
3718
3719 if (IsPathFile(path))
3720 {
3721 if (filter != NULL)
3722 {
3723 if (IsFileExtension(path, filter))
3724 {
3725 strcpy(files->paths[files->count], path);
3726 files->count++;
3727 }
3728 }
3729 else
3730 {
3731 strcpy(files->paths[files->count], path);
3732 files->count++;
3733 }
3734
3735 if (files->count >= files->capacity)
3736 {
3737 TRACELOG(LOG_WARNING, "FILEIO: Maximum filepath scan capacity reached (%i files)", files->capacity);
3738 break;
3739 }
3740 }
3741 else
3742 {
3743 if ((filter != NULL) && (TextFindIndex(filter, DIRECTORY_FILTER_TAG) >= 0))
3744 {
3745 strcpy(files->paths[files->count], path);
3746 files->count++;
3747 }
3748
3749 if (files->count >= files->capacity)
3750 {
3751 TRACELOG(LOG_WARNING, "FILEIO: Maximum filepath scan capacity reached (%i files)", files->capacity);
3752 break;
3753 }
3754
3755 ScanDirectoryFilesRecursively(path, files, filter);
3756 }
3757 }
3758 }
3759
3760 closedir(dir);
3761 }
3762 else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath);
3763}
3764
3765#if defined(SUPPORT_AUTOMATION_EVENTS)
3766// Automation event recording
3767// NOTE: Recording is by default done at EndDrawing(), before PollInputEvents()
3768static void RecordAutomationEvent(void)
3769{
3770 // Checking events in current frame and save them into currentEventList
3771 // TODO: How important is the current frame? Could it be modified?
3772
3773 if (currentEventList->count == currentEventList->capacity) return; // Security check
3774
3775 // Keyboard input events recording
3776 //-------------------------------------------------------------------------------------
3777 for (int key = 0; key < MAX_KEYBOARD_KEYS; key++)
3778 {
3779 // Event type: INPUT_KEY_UP (only saved once)
3780 if (CORE.Input.Keyboard.previousKeyState[key] && !CORE.Input.Keyboard.currentKeyState[key])
3781 {
3782 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
3783 currentEventList->events[currentEventList->count].type = INPUT_KEY_UP;
3784 currentEventList->events[currentEventList->count].params[0] = key;
3785 currentEventList->events[currentEventList->count].params[1] = 0;
3786 currentEventList->events[currentEventList->count].params[2] = 0;
3787
3788 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]);
3789 currentEventList->count++;
3790 }
3791
3792 if (currentEventList->count == currentEventList->capacity) return; // Security check
3793
3794 // Event type: INPUT_KEY_DOWN
3795 if (CORE.Input.Keyboard.currentKeyState[key])
3796 {
3797 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
3798 currentEventList->events[currentEventList->count].type = INPUT_KEY_DOWN;
3799 currentEventList->events[currentEventList->count].params[0] = key;
3800 currentEventList->events[currentEventList->count].params[1] = 0;
3801 currentEventList->events[currentEventList->count].params[2] = 0;
3802
3803 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]);
3804 currentEventList->count++;
3805 }
3806
3807 if (currentEventList->count == currentEventList->capacity) return; // Security check
3808 }
3809 //-------------------------------------------------------------------------------------
3810
3811 // Mouse input currentEventList->events recording
3812 //-------------------------------------------------------------------------------------
3813 for (int button = 0; button < MAX_MOUSE_BUTTONS; button++)
3814 {
3815 // Event type: INPUT_MOUSE_BUTTON_UP
3816 if (CORE.Input.Mouse.previousButtonState[button] && !CORE.Input.Mouse.currentButtonState[button])
3817 {
3818 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
3819 currentEventList->events[currentEventList->count].type = INPUT_MOUSE_BUTTON_UP;
3820 currentEventList->events[currentEventList->count].params[0] = button;
3821 currentEventList->events[currentEventList->count].params[1] = 0;
3822 currentEventList->events[currentEventList->count].params[2] = 0;
3823
3824 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]);
3825 currentEventList->count++;
3826 }
3827
3828 if (currentEventList->count == currentEventList->capacity) return; // Security check
3829
3830 // Event type: INPUT_MOUSE_BUTTON_DOWN
3831 if (CORE.Input.Mouse.currentButtonState[button])
3832 {
3833 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
3834 currentEventList->events[currentEventList->count].type = INPUT_MOUSE_BUTTON_DOWN;
3835 currentEventList->events[currentEventList->count].params[0] = button;
3836 currentEventList->events[currentEventList->count].params[1] = 0;
3837 currentEventList->events[currentEventList->count].params[2] = 0;
3838
3839 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]);
3840 currentEventList->count++;
3841 }
3842
3843 if (currentEventList->count == currentEventList->capacity) return; // Security check
3844 }
3845
3846 // Event type: INPUT_MOUSE_POSITION (only saved if changed)
3847 if (((int)CORE.Input.Mouse.currentPosition.x != (int)CORE.Input.Mouse.previousPosition.x) ||
3848 ((int)CORE.Input.Mouse.currentPosition.y != (int)CORE.Input.Mouse.previousPosition.y))
3849 {
3850 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
3851 currentEventList->events[currentEventList->count].type = INPUT_MOUSE_POSITION;
3852 currentEventList->events[currentEventList->count].params[0] = (int)CORE.Input.Mouse.currentPosition.x;
3853 currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Mouse.currentPosition.y;
3854 currentEventList->events[currentEventList->count].params[2] = 0;
3855
3856 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]);
3857 currentEventList->count++;
3858
3859 if (currentEventList->count == currentEventList->capacity) return; // Security check
3860 }
3861
3862 // Event type: INPUT_MOUSE_WHEEL_MOTION
3863 if (((int)CORE.Input.Mouse.currentWheelMove.x != (int)CORE.Input.Mouse.previousWheelMove.x) ||
3864 ((int)CORE.Input.Mouse.currentWheelMove.y != (int)CORE.Input.Mouse.previousWheelMove.y))
3865 {
3866 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
3867 currentEventList->events[currentEventList->count].type = INPUT_MOUSE_WHEEL_MOTION;
3868 currentEventList->events[currentEventList->count].params[0] = (int)CORE.Input.Mouse.currentWheelMove.x;
3869 currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Mouse.currentWheelMove.y;
3870 currentEventList->events[currentEventList->count].params[2] = 0;
3871
3872 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]);
3873 currentEventList->count++;
3874
3875 if (currentEventList->count == currentEventList->capacity) return; // Security check
3876 }
3877 //-------------------------------------------------------------------------------------
3878
3879 // Touch input currentEventList->events recording
3880 //-------------------------------------------------------------------------------------
3881 for (int id = 0; id < MAX_TOUCH_POINTS; id++)
3882 {
3883 // Event type: INPUT_TOUCH_UP
3884 if (CORE.Input.Touch.previousTouchState[id] && !CORE.Input.Touch.currentTouchState[id])
3885 {
3886 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
3887 currentEventList->events[currentEventList->count].type = INPUT_TOUCH_UP;
3888 currentEventList->events[currentEventList->count].params[0] = id;
3889 currentEventList->events[currentEventList->count].params[1] = 0;
3890 currentEventList->events[currentEventList->count].params[2] = 0;
3891
3892 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]);
3893 currentEventList->count++;
3894 }
3895
3896 if (currentEventList->count == currentEventList->capacity) return; // Security check
3897
3898 // Event type: INPUT_TOUCH_DOWN
3899 if (CORE.Input.Touch.currentTouchState[id])
3900 {
3901 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
3902 currentEventList->events[currentEventList->count].type = INPUT_TOUCH_DOWN;
3903 currentEventList->events[currentEventList->count].params[0] = id;
3904 currentEventList->events[currentEventList->count].params[1] = 0;
3905 currentEventList->events[currentEventList->count].params[2] = 0;
3906
3907 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]);
3908 currentEventList->count++;
3909 }
3910
3911 if (currentEventList->count == currentEventList->capacity) return; // Security check
3912
3913 // Event type: INPUT_TOUCH_POSITION
3914 // TODO: It requires the id!
3915 /*
3916 if (((int)CORE.Input.Touch.currentPosition[id].x != (int)CORE.Input.Touch.previousPosition[id].x) ||
3917 ((int)CORE.Input.Touch.currentPosition[id].y != (int)CORE.Input.Touch.previousPosition[id].y))
3918 {
3919 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
3920 currentEventList->events[currentEventList->count].type = INPUT_TOUCH_POSITION;
3921 currentEventList->events[currentEventList->count].params[0] = id;
3922 currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Touch.currentPosition[id].x;
3923 currentEventList->events[currentEventList->count].params[2] = (int)CORE.Input.Touch.currentPosition[id].y;
3924
3925 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]);
3926 currentEventList->count++;
3927 }
3928 */
3929
3930 if (currentEventList->count == currentEventList->capacity) return; // Security check
3931 }
3932 //-------------------------------------------------------------------------------------
3933
3934 // Gamepad input currentEventList->events recording
3935 //-------------------------------------------------------------------------------------
3936 for (int gamepad = 0; gamepad < MAX_GAMEPADS; gamepad++)
3937 {
3938 // Event type: INPUT_GAMEPAD_CONNECT
3939 /*
3940 if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) &&
3941 (CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to ready
3942 {
3943 // TODO: Save gamepad connect event
3944 }
3945 */
3946
3947 // Event type: INPUT_GAMEPAD_DISCONNECT
3948 /*
3949 if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) &&
3950 (!CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to not-ready
3951 {
3952 // TODO: Save gamepad disconnect event
3953 }
3954 */
3955
3956 for (int button = 0; button < MAX_GAMEPAD_BUTTONS; button++)
3957 {
3958 // Event type: INPUT_GAMEPAD_BUTTON_UP
3959 if (CORE.Input.Gamepad.previousButtonState[gamepad][button] && !CORE.Input.Gamepad.currentButtonState[gamepad][button])
3960 {
3961 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
3962 currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_BUTTON_UP;
3963 currentEventList->events[currentEventList->count].params[0] = gamepad;
3964 currentEventList->events[currentEventList->count].params[1] = button;
3965 currentEventList->events[currentEventList->count].params[2] = 0;
3966
3967 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]);
3968 currentEventList->count++;
3969 }
3970
3971 if (currentEventList->count == currentEventList->capacity) return; // Security check
3972
3973 // Event type: INPUT_GAMEPAD_BUTTON_DOWN
3974 if (CORE.Input.Gamepad.currentButtonState[gamepad][button])
3975 {
3976 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
3977 currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_BUTTON_DOWN;
3978 currentEventList->events[currentEventList->count].params[0] = gamepad;
3979 currentEventList->events[currentEventList->count].params[1] = button;
3980 currentEventList->events[currentEventList->count].params[2] = 0;
3981
3982 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]);
3983 currentEventList->count++;
3984 }
3985
3986 if (currentEventList->count == currentEventList->capacity) return; // Security check
3987 }
3988
3989 for (int axis = 0; axis < MAX_GAMEPAD_AXIS; axis++)
3990 {
3991 // Event type: INPUT_GAMEPAD_AXIS_MOTION
3992 float defaultMovement = (axis == GAMEPAD_AXIS_LEFT_TRIGGER || axis == GAMEPAD_AXIS_RIGHT_TRIGGER)? -1.0f : 0.0f;
3993 if (GetGamepadAxisMovement(gamepad, axis) != defaultMovement)
3994 {
3995 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
3996 currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_AXIS_MOTION;
3997 currentEventList->events[currentEventList->count].params[0] = gamepad;
3998 currentEventList->events[currentEventList->count].params[1] = axis;
3999 currentEventList->events[currentEventList->count].params[2] = (int)(CORE.Input.Gamepad.axisState[gamepad][axis]*32768.0f);
4000
4001 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]);
4002 currentEventList->count++;
4003 }
4004
4005 if (currentEventList->count == currentEventList->capacity) return; // Security check
4006 }
4007 }
4008 //-------------------------------------------------------------------------------------
4009
4010#if defined(SUPPORT_GESTURES_SYSTEM)
4011 // Gestures input currentEventList->events recording
4012 //-------------------------------------------------------------------------------------
4013 if (GESTURES.current != GESTURE_NONE)
4014 {
4015 // Event type: INPUT_GESTURE
4016 currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
4017 currentEventList->events[currentEventList->count].type = INPUT_GESTURE;
4018 currentEventList->events[currentEventList->count].params[0] = GESTURES.current;
4019 currentEventList->events[currentEventList->count].params[1] = 0;
4020 currentEventList->events[currentEventList->count].params[2] = 0;
4021
4022 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]);
4023 currentEventList->count++;
4024
4025 if (currentEventList->count == currentEventList->capacity) return; // Security check
4026 }
4027 //-------------------------------------------------------------------------------------
4028#endif
4029}
4030#endif
4031
4032#if !defined(SUPPORT_MODULE_RTEXT)
4033// Formatting of text with variables to 'embed'
4034// WARNING: String returned will expire after this function is called MAX_TEXTFORMAT_BUFFERS times
4035const char *TextFormat(const char *text, ...)
4036{
4037#ifndef MAX_TEXTFORMAT_BUFFERS
4038 #define MAX_TEXTFORMAT_BUFFERS 4 // Maximum number of static buffers for text formatting
4039#endif
4040#ifndef MAX_TEXT_BUFFER_LENGTH
4041 #define MAX_TEXT_BUFFER_LENGTH 1024 // Maximum size of static text buffer
4042#endif
4043
4044 // We create an array of buffers so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations
4045 static char buffers[MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH] = { 0 };
4046 static int index = 0;
4047
4048 char *currentBuffer = buffers[index];
4049 memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH); // Clear buffer before using
4050
4051 va_list args;
4052 va_start(args, text);
4053 int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
4054 va_end(args);
4055
4056 // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured
4057 if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
4058 {
4059 // Inserting "..." at the end of the string to mark as truncated
4060 char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0"
4061 sprintf(truncBuffer, "...");
4062 }
4063
4064 index += 1; // Move to next buffer for next function call
4065 if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
4066
4067 return currentBuffer;
4068}
4069
4070#endif // !SUPPORT_MODULE_RTEXT

Generated with cgit - Back to sebastiano.tronto.net