rcore.c (159352B)
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 //---------------------------------------------------------------------------------- 269 typedef struct { int x; int y; } Point; 270 typedef struct { unsigned int width; unsigned int height; } Size; 271 272 // Core global state context data 273 typedef 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 //---------------------------------------------------------------------------------- 371 RLAPI const char *raylib_version = RAYLIB_VERSION; // raylib version exported symbol, required for some bindings 372 373 CoreData 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 378 bool isGpuReady = false; 379 380 #if defined(SUPPORT_SCREEN_CAPTURE) 381 static int screenshotCounter = 0; // Screenshots counter 382 #endif 383 384 #if defined(SUPPORT_GIF_RECORDING) 385 static unsigned int gifFrameCounter = 0; // GIF frames counter 386 static bool gifRecording = false; // GIF recording state 387 static MsfGifState gifState = { 0 }; // MSGIF context state 388 #endif 389 390 #if defined(SUPPORT_AUTOMATION_EVENTS) 391 // Automation events type 392 typedef 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 424 typedef 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 435 static 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 465 struct 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 472 static AutomationEventList *currentEventList = NULL; // Current automation events list, set by user, keep internal pointer 473 static 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) 484 extern void LoadFontDefault(void); // [Module: text] Loads default font on InitWindow() 485 extern void UnloadFontDefault(void); // [Module: text] Unloads default font from GPU memory 486 #endif 487 488 extern int InitPlatform(void); // Initialize platform (graphics, inputs and more) 489 extern void ClosePlatform(void); // Close platform 490 491 static void InitTimer(void); // Initialize timer, hi-resolution if available (required by InitPlatform()) 492 static void SetupFramebuffer(int width, int height); // Setup main framebuffer (required by InitPlatform()) 493 static void SetupViewport(int width, int height); // Set viewport for a provided width and height 494 495 static void ScanDirectoryFiles(const char *basePath, FilePathList *list, const char *filter); // Scan all files and directories in a base path 496 static 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) 499 static 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) 504 void __stdcall Sleep(unsigned long msTimeout); // Required for: WaitTime() 505 #endif 506 507 #if !defined(SUPPORT_MODULE_RTEXT) 508 const 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 604 void 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 722 void 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 749 bool IsWindowReady(void) 750 { 751 return CORE.Window.ready; 752 } 753 754 // Check if window is currently fullscreen 755 bool IsWindowFullscreen(void) 756 { 757 return CORE.Window.fullscreen; 758 } 759 760 // Check if window is currently hidden 761 bool IsWindowHidden(void) 762 { 763 return ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0); 764 } 765 766 // Check if window has been minimized 767 bool IsWindowMinimized(void) 768 { 769 return ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0); 770 } 771 772 // Check if window has been maximized 773 bool IsWindowMaximized(void) 774 { 775 return ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0); 776 } 777 778 // Check if window has the focus 779 bool IsWindowFocused(void) 780 { 781 return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0); 782 } 783 784 // Check if window has been resizedLastFrame 785 bool IsWindowResized(void) 786 { 787 return CORE.Window.resizedLastFrame; 788 } 789 790 // Check if one specific window flag is enabled 791 bool IsWindowState(unsigned int flag) 792 { 793 return ((CORE.Window.flags & flag) > 0); 794 } 795 796 // Get current screen width 797 int GetScreenWidth(void) 798 { 799 return CORE.Window.screen.width; 800 } 801 802 // Get current screen height 803 int GetScreenHeight(void) 804 { 805 return CORE.Window.screen.height; 806 } 807 808 // Get current render width which is equal to screen width*dpi scale 809 int 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 822 int 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 835 void EnableEventWaiting(void) 836 { 837 CORE.Window.eventWaiting = true; 838 } 839 840 // Disable waiting for events on EndDrawing(), automatic events polling 841 void DisableEventWaiting(void) 842 { 843 CORE.Window.eventWaiting = false; 844 } 845 846 // Check if cursor is not visible 847 bool IsCursorHidden(void) 848 { 849 return CORE.Input.Mouse.cursorHidden; 850 } 851 852 // Check if cursor is on the current screen 853 bool 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) 863 void 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 870 void 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) 887 void 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) 1003 void 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 1014 void 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) 1024 void 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 1063 void 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 1079 void 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 1110 void 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 1131 void BeginShaderMode(Shader shader) 1132 { 1133 rlSetShader(shader.id, shader.locs); 1134 } 1135 1136 // End custom shader mode (returns to default shader) 1137 void 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 1144 void BeginBlendMode(int mode) 1145 { 1146 rlSetBlendMode(mode); 1147 } 1148 1149 // End blending mode (reset to default: alpha blending) 1150 void 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 1157 void 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 1183 void 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 1194 void 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) 1204 void EndVrStereoMode(void) 1205 { 1206 rlDisableStereoRender(); 1207 } 1208 1209 // Load VR stereo config for VR simulator device parameters 1210 VrStereoConfig 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 1284 void 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 1295 Shader 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 1314 Shader 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) 1369 bool 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) 1401 void 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 1413 int GetShaderLocation(Shader shader, const char *uniformName) 1414 { 1415 return rlGetLocationUniform(shader.id, uniformName); 1416 } 1417 1418 // Get shader attribute location 1419 int GetShaderLocationAttrib(Shader shader, const char *attribName) 1420 { 1421 return rlGetLocationAttrib(shader.id, attribName); 1422 } 1423 1424 // Set shader uniform value 1425 void 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 1431 void 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) 1442 void 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 1453 void 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) 1468 Ray 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 1476 Ray 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 1532 Matrix 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 1540 Matrix 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 1568 Vector2 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) 1576 Vector2 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 1620 Vector2 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 1629 Vector2 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) 1645 void 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 1655 int 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) 1697 float 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! 1715 void 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 1764 void 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 1774 int 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 1803 int *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 1842 void 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 1853 void 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() 1880 void 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 1892 bool 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 1912 bool 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 1945 bool 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 1961 int 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) 1989 const 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 1999 static 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 2009 const 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) 2021 const 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 2048 const 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 2096 const 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 2120 const 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 2130 const 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! 2232 FilePathList 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 2270 FilePathList 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 2287 void 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 2295 int 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 2329 bool 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 2339 bool 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 2348 bool 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 2406 bool 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 2416 FilePathList 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 2427 void 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) 2443 long 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) 2463 unsigned 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) 2485 unsigned 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 2510 char *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 2546 unsigned 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 2604 unsigned 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) 2650 unsigned 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) 2768 unsigned 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 2867 AutomationEventList 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 2947 void 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 2955 bool 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 3006 void 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 3014 void SetAutomationEventBaseFrame(int frame) 3015 { 3016 CORE.Time.frameCounter = frame; 3017 } 3018 3019 // Start recording automation events (AutomationEventList must be set) 3020 void StartAutomationEventRecording(void) 3021 { 3022 #if defined(SUPPORT_AUTOMATION_EVENTS) 3023 automationEventRecording = true; 3024 #endif 3025 } 3026 3027 // Stop recording automation events 3028 void StopAutomationEventRecording(void) 3029 { 3030 #if defined(SUPPORT_AUTOMATION_EVENTS) 3031 automationEventRecording = false; 3032 #endif 3033 } 3034 3035 // Play a recorded automation event 3036 void 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 3118 bool 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 3132 bool 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) 3145 bool 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 3158 bool 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) 3171 bool 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 3184 int 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 3206 int 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 3229 void 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 3242 bool 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 3252 const char *GetGamepadName(int gamepad) 3253 { 3254 return CORE.Input.Gamepad.name[gamepad]; 3255 } 3256 3257 // Check if a gamepad button has been pressed once 3258 bool 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 3269 bool 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 3280 bool 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 3291 bool 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 3302 int GetGamepadButtonPressed(void) 3303 { 3304 return CORE.Input.Gamepad.lastButtonPressed; 3305 } 3306 3307 // Get gamepad axis count 3308 int GetGamepadAxisCount(int gamepad) 3309 { 3310 return CORE.Input.Gamepad.axisCount[gamepad]; 3311 } 3312 3313 // Get axis movement vector for a gamepad 3314 float 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 3336 bool 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 3349 bool 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 3362 bool 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 3375 bool 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 3388 int 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 3395 int 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 3402 Vector2 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 3413 Vector2 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 3425 void 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 3432 void SetMouseScale(float scaleX, float scaleY) 3433 { 3434 CORE.Input.Mouse.scale = (Vector2){ scaleX, scaleY }; 3435 } 3436 3437 // Get mouse wheel movement Y 3438 float 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 3449 Vector2 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) 3463 int 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) 3470 int 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 3478 Vector2 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 3489 int 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 3499 int 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 3513 void 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 3537 void 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 3565 void 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 3644 static 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 3698 static 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() 3768 static 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 4035 const 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