Update C sources

This commit is contained in:
JupiterRider 2023-04-07 20:23:18 +02:00
parent 22ca1484d1
commit 781c207678
27 changed files with 13963 additions and 4637 deletions

View file

@ -89,7 +89,7 @@
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2013-2022 Ramon Santamaria (@raysan5)
* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5)
*
* This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software.
@ -151,10 +151,13 @@
#include "external/sdefl.h" // Deflate (RFC 1951) compressor
#endif
#if (defined(__linux__) || defined(PLATFORM_WEB)) && _POSIX_C_SOURCE < 199309L
#if (defined(__linux__) || defined(PLATFORM_WEB)) && (_POSIX_C_SOURCE < 199309L)
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext.
#endif
#if defined(__linux__) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
// Platform specific defines to handle GetApplicationDirectory()
#if defined (PLATFORM_DESKTOP)
@ -239,6 +242,7 @@
#include <unistd.h> // Required for: usleep()
//#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition
void *glfwGetCocoaWindow(GLFWwindow* handle);
#include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow()
#endif
@ -705,17 +709,15 @@ const char *TextFormat(const char *text, ...); // Formatting of text with
#if defined(PLATFORM_ANDROID)
// To allow easier porting to android, we allow the user to define a
// main function which we call from android_main, defined by ourselves
//extern int main(int argc, char *argv[]);
extern void android_run();
extern int main(int argc, char *argv[]);
void android_main(struct android_app *app)
{
char arg0[] = "raylib"; // NOTE: argv[] are mutable
CORE.Android.app = app;
(void)android_run();
// NOTE: Return codes != 0 are skipped
//(void)main(1, (char *[]) { arg0, NULL });
(void)main(1, (char *[]) { arg0, NULL });
}
// NOTE: Add this to header (if apps really need it)
@ -767,7 +769,7 @@ void InitWindow(int width, int height, const char *title)
CORE.Input.Keyboard.exitKey = KEY_ESCAPE;
CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f };
CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW;
CORE.Input.Gamepad.lastButtonPressed = -1;
CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
#if defined(SUPPORT_EVENTS_WAITING)
CORE.Window.eventWaiting = true;
#endif
@ -847,6 +849,7 @@ void InitWindow(int width, int height, const char *title)
TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device");
return;
}
else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2);
// Initialize hi-res timer
InitTimer();
@ -863,7 +866,7 @@ void InitWindow(int width, int height, const char *title)
LoadFontDefault();
#if defined(SUPPORT_MODULE_RSHAPES)
Rectangle rec = GetFontDefault().recs[95];
// NOTE: We setup a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering
// NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering
SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); // WARNING: Module required: rshapes
#endif
#else
@ -921,9 +924,6 @@ void InitWindow(int width, int height, const char *title)
emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback);
#endif
CORE.Input.Mouse.currentPosition.x = (float)CORE.Window.screen.width/2.0f;
CORE.Input.Mouse.currentPosition.y = (float)CORE.Window.screen.height/2.0f;
#if defined(SUPPORT_EVENTS_AUTOMATION)
events = (AutomationEvent *)malloc(MAX_CODE_AUTOMATION_EVENTS*sizeof(AutomationEvent));
CORE.Time.frameCounter = 0;
@ -1141,9 +1141,8 @@ bool IsWindowMinimized(void)
{
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
return ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0);
#else
return false;
#endif
return false;
}
// Check if window has been maximized (only PLATFORM_DESKTOP)
@ -1151,9 +1150,8 @@ bool IsWindowMaximized(void)
{
#if defined(PLATFORM_DESKTOP)
return ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0);
#else
return false;
#endif
return false;
}
// Check if window has the focus
@ -1161,9 +1159,11 @@ bool IsWindowFocused(void)
{
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0);
#else
return true;
#endif
#if defined(PLATFORM_ANDROID)
return CORE.Android.appEnabled;
#endif
return true;
}
// Check if window has been resizedLastFrame
@ -1316,7 +1316,7 @@ void MaximizeWindow(void)
void MinimizeWindow(void)
{
#if defined(PLATFORM_DESKTOP)
// NOTE: Following function launches callback that sets appropiate flag!
// NOTE: Following function launches callback that sets appropriate flag!
glfwIconifyWindow(CORE.Window.handle);
#endif
}
@ -1415,13 +1415,13 @@ void SetWindowState(unsigned int flags)
// State change: FLAG_WINDOW_TRANSPARENT
if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) != (flags & FLAG_WINDOW_TRANSPARENT)) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0))
{
TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only by configured before window initialization");
TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization");
}
// State change: FLAG_WINDOW_HIGHDPI
if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) != (flags & FLAG_WINDOW_HIGHDPI)) && ((flags & FLAG_WINDOW_HIGHDPI) > 0))
{
TRACELOG(LOG_WARNING, "WINDOW: High DPI can only by configured before window initialization");
TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization");
}
// State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
@ -1434,13 +1434,13 @@ void SetWindowState(unsigned int flags)
// State change: FLAG_MSAA_4X_HINT
if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) != (flags & FLAG_MSAA_4X_HINT)) && ((flags & FLAG_MSAA_4X_HINT) > 0))
{
TRACELOG(LOG_WARNING, "WINDOW: MSAA can only by configured before window initialization");
TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization");
}
// State change: FLAG_INTERLACED_HINT
if (((CORE.Window.flags & FLAG_INTERLACED_HINT) != (flags & FLAG_INTERLACED_HINT)) && ((flags & FLAG_INTERLACED_HINT) > 0))
{
TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only by configured before window initialization");
TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization");
}
#endif
}
@ -1523,13 +1523,13 @@ void ClearWindowState(unsigned int flags)
// State change: FLAG_WINDOW_TRANSPARENT
if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0))
{
TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only by configured before window initialization");
TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization");
}
// State change: FLAG_WINDOW_HIGHDPI
if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) && ((flags & FLAG_WINDOW_HIGHDPI) > 0))
{
TRACELOG(LOG_WARNING, "WINDOW: High DPI can only by configured before window initialization");
TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization");
}
// State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
@ -1542,35 +1542,81 @@ void ClearWindowState(unsigned int flags)
// State change: FLAG_MSAA_4X_HINT
if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) > 0) && ((flags & FLAG_MSAA_4X_HINT) > 0))
{
TRACELOG(LOG_WARNING, "WINDOW: MSAA can only by configured before window initialization");
TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization");
}
// State change: FLAG_INTERLACED_HINT
if (((CORE.Window.flags & FLAG_INTERLACED_HINT) > 0) && ((flags & FLAG_INTERLACED_HINT) > 0))
{
TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only by configured before window initialization");
TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization");
}
#endif
}
// Set icon for window (only PLATFORM_DESKTOP)
// NOTE: Image must be in RGBA format, 8bit per channel
// NOTE 1: Image must be in RGBA format, 8bit per channel
// NOTE 2: Image is scaled by the OS for all required sizes
void SetWindowIcon(Image image)
{
#if defined(PLATFORM_DESKTOP)
if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)
if (image.data == NULL)
{
GLFWimage icon[1] = { 0 };
icon[0].width = image.width;
icon[0].height = image.height;
icon[0].pixels = (unsigned char *)image.data;
// NOTE 1: We only support one image icon
// NOTE 2: The specified image data is copied before this function returns
glfwSetWindowIcon(CORE.Window.handle, 1, icon);
// Revert to the default window icon, pass in an empty image array
glfwSetWindowIcon(CORE.Window.handle, 0, NULL);
}
else
{
if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)
{
GLFWimage icon[1] = { 0 };
icon[0].width = image.width;
icon[0].height = image.height;
icon[0].pixels = (unsigned char *)image.data;
// NOTE 1: We only support one image icon
// NOTE 2: The specified image data is copied before this function returns
glfwSetWindowIcon(CORE.Window.handle, 1, icon);
}
else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format");
}
#endif
}
// Set icon for window (multiple images, only PLATFORM_DESKTOP)
// NOTE 1: Images must be in RGBA format, 8bit per channel
// NOTE 2: The multiple images are used depending on provided sizes
// Standard Windows icon sizes: 256, 128, 96, 64, 48, 32, 24, 16
void SetWindowIcons(Image *images, int count)
{
#if defined(PLATFORM_DESKTOP)
if ((images == NULL) || (count <= 0))
{
// Revert to the default window icon, pass in an empty image array
glfwSetWindowIcon(CORE.Window.handle, 0, NULL);
}
else
{
int valid = 0;
GLFWimage *icons = RL_CALLOC(count, sizeof(GLFWimage));
for (int i = 0; i < count; i++)
{
if (images[i].format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)
{
icons[valid].width = images[i].width;
icons[valid].height = images[i].height;
icons[valid].pixels = (unsigned char *)images[i].data;
valid++;
}
else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format");
}
// NOTE: Images data is copied internally before this function returns
glfwSetWindowIcon(CORE.Window.handle, valid, icons);
RL_FREE(icons);
}
else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format");
#endif
}
@ -1667,16 +1713,17 @@ void *GetWindowHandle(void)
// NOTE: Returned handle is: void *HWND (windows.h)
return glfwGetWin32Window(CORE.Window.handle);
#endif
#if defined(__linux__)
#if defined(PLATFORM_DESKTOP) && defined(__linux__)
// NOTE: Returned handle is: unsigned long Window (X.h)
// typedef unsigned long XID;
// typedef XID Window;
//unsigned long id = (unsigned long)glfwGetX11Window(window);
return NULL; // TODO: Find a way to return value... cast to void *?
//unsigned long id = (unsigned long)glfwGetX11Window(CORE.Window.handle);
//return NULL; // TODO: Find a way to return value... cast to void *?
return (void *)CORE.Window.handle;
#endif
#if defined(__APPLE__)
// NOTE: Returned handle is: (objc_object *)
return NULL; // TODO: return (void *)glfwGetCocoaWindow(window);
return (void *)glfwGetCocoaWindow(CORE.Window.handle);
#endif
return NULL;
@ -1785,6 +1832,12 @@ int GetMonitorWidth(int monitor)
else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor");
}
else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
#endif
#if defined(PLATFORM_ANDROID)
if (CORE.Android.app->window != NULL)
{
return ANativeWindow_getWidth(CORE.Android.app->window);
}
#endif
return 0;
}
@ -1804,6 +1857,12 @@ int GetMonitorHeight(int monitor)
else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor");
}
else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
#endif
#if defined(PLATFORM_ANDROID)
if (CORE.Android.app->window != NULL)
{
return ANativeWindow_getHeight(CORE.Android.app->window);
}
#endif
return 0;
}
@ -1935,10 +1994,40 @@ void SetClipboardText(const char *text)
glfwSetClipboardString(CORE.Window.handle, text);
#endif
#if defined(PLATFORM_WEB)
emscripten_run_script(TextFormat("navigator.clipboard.writeText('%s')", text));
// Security check to (partially) avoid malicious code
if (strchr(text, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided Clipboard could be potentially malicious, avoid [\'] character");
else emscripten_run_script(TextFormat("navigator.clipboard.writeText('%s')", text));
#endif
}
// Get clipboard text content
// NOTE: returned string is allocated and freed by GLFW
const char *GetClipboardText(void)
{
#if defined(PLATFORM_DESKTOP)
return glfwGetClipboardString(CORE.Window.handle);
#endif
#if defined(PLATFORM_WEB)
/*
// Accessing clipboard data from browser is tricky due to security reasons
// The method to use is navigator.clipboard.readText() but this is an asynchronous method
// that will return at some moment after the function is called with the required data
emscripten_run_script_string("navigator.clipboard.readText() \
.then(text => { document.getElementById('clipboard').innerText = text; console.log('Pasted content: ', text); }) \
.catch(err => { console.error('Failed to read clipboard contents: ', err); });"
);
// The main issue is getting that data, one approach could be using ASYNCIFY and wait
// for the data but it requires adding Asyncify emscripten library on compilation
// Another approach could be just copy the data in a HTML text field and try to retrieve it
// later on if available... and clean it for future accesses
*/
return NULL;
#endif
return NULL;
}
// Enable waiting for events on EndDrawing(), no automatic event polling
void EnableEventWaiting(void)
{
@ -1951,19 +2040,6 @@ void DisableEventWaiting(void)
CORE.Window.eventWaiting = false;
}
// Get clipboard text content
// NOTE: returned string is allocated and freed by GLFW
const char *GetClipboardText(void)
{
#if defined(PLATFORM_DESKTOP)
return glfwGetClipboardString(CORE.Window.handle);
#endif
#if defined(PLATFORM_WEB)
return emscripten_run_script_string("navigator.clipboard.readText()");
#endif
return NULL;
}
// Show mouse cursor
void ShowCursor(void)
{
@ -1999,6 +2075,8 @@ void EnableCursor(void)
#if defined(PLATFORM_WEB)
emscripten_exit_pointerlock();
#endif
// Set cursor position in the middle
SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
CORE.Input.Mouse.cursorHidden = false;
}
@ -2012,6 +2090,8 @@ void DisableCursor(void)
#if defined(PLATFORM_WEB)
emscripten_request_pointerlock("#canvas", 1);
#endif
// Set cursor position in the middle
SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
CORE.Input.Mouse.cursorHidden = true;
}
@ -2173,7 +2253,7 @@ void EndMode2D(void)
}
// Initializes 3D mode with custom camera (3D)
void BeginMode3D(Camera3D camera)
void BeginMode3D(Camera camera)
{
rlDrawRenderBatchActive(); // Update and draw internal render batch
@ -2448,10 +2528,6 @@ Shader LoadShader(const char *vsFileName, const char *fsFileName)
Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
{
Shader shader = { 0 };
shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int));
// NOTE: All locations must be reseted to -1 (no location)
for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1;
shader.id = rlLoadShaderCode(vsCode, fsCode);
@ -2468,7 +2544,12 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
// NOTE: If any location is not found, loc point becomes -1
// Get handles to GLSL input attibute locations
shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int));
// All locations reset to -1 (no location)
for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1;
// Get handles to GLSL input attribute locations
shader.locs[SHADER_LOC_VERTEX_POSITION] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION);
shader.locs[SHADER_LOC_VERTEX_TEXCOORD01] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD);
shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2);
@ -2493,12 +2574,46 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
return shader;
}
// Check if a shader is ready
bool IsShaderReady(Shader shader)
{
return ((shader.id > 0) && // Validate shader id (loaded successfully)
(shader.locs != NULL)); // Validate memory has been allocated for default shader locations
// The following locations are tried to be set automatically (locs[i] >= 0),
// any of them can be checked for validation but the only mandatory one is, afaik, SHADER_LOC_VERTEX_POSITION
// NOTE: Users can also setup manually their own attributes/uniforms and do not used the default raylib ones
// Vertex shader attribute locations (default)
// shader.locs[SHADER_LOC_VERTEX_POSITION] // Set by default internal shader
// shader.locs[SHADER_LOC_VERTEX_TEXCOORD01] // Set by default internal shader
// shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]
// shader.locs[SHADER_LOC_VERTEX_NORMAL]
// shader.locs[SHADER_LOC_VERTEX_TANGENT]
// shader.locs[SHADER_LOC_VERTEX_COLOR] // Set by default internal shader
// Vertex shader uniform locations (default)
// shader.locs[SHADER_LOC_MATRIX_MVP] // Set by default internal shader
// shader.locs[SHADER_LOC_MATRIX_VIEW]
// shader.locs[SHADER_LOC_MATRIX_PROJECTION]
// shader.locs[SHADER_LOC_MATRIX_MODEL]
// shader.locs[SHADER_LOC_MATRIX_NORMAL]
// Fragment shader uniform locations (default)
// shader.locs[SHADER_LOC_COLOR_DIFFUSE] // Set by default internal shader
// shader.locs[SHADER_LOC_MAP_DIFFUSE] // Set by default internal shader
// shader.locs[SHADER_LOC_MAP_SPECULAR]
// shader.locs[SHADER_LOC_MAP_NORMAL]
}
// Unload shader from GPU memory (VRAM)
void UnloadShader(Shader shader)
{
if (shader.id != rlGetShaderIdDefault())
{
rlUnloadShaderProgram(shader.id);
// NOTE: If shader loading failed, it should be 0
RL_FREE(shader.locs);
}
}
@ -2524,25 +2639,34 @@ void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformT
// Set shader uniform value vector
void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count)
{
rlEnableShader(shader.id);
rlSetUniform(locIndex, value, uniformType, count);
//rlDisableShader(); // Avoid reseting current shader program, in case other uniforms are set
if (locIndex > -1)
{
rlEnableShader(shader.id);
rlSetUniform(locIndex, value, uniformType, count);
//rlDisableShader(); // Avoid resetting current shader program, in case other uniforms are set
}
}
// Set shader uniform value (matrix 4x4)
void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat)
{
rlEnableShader(shader.id);
rlSetUniformMatrix(locIndex, mat);
//rlDisableShader();
if (locIndex > -1)
{
rlEnableShader(shader.id);
rlSetUniformMatrix(locIndex, mat);
//rlDisableShader();
}
}
// Set shader uniform value for texture
void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture)
{
rlEnableShader(shader.id);
rlSetUniformSampler(locIndex, texture.id);
//rlDisableShader();
if (locIndex > -1)
{
rlEnableShader(shader.id);
rlSetUniformSampler(locIndex, texture.id);
//rlDisableShader();
}
}
// Get a ray trace from mouse position
@ -2584,7 +2708,7 @@ Ray GetMouseRay(Vector2 mouse, Camera camera)
Vector3 farPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView);
// Unproject the mouse cursor in the near plane.
// We need this as the source position because orthographic projects, compared to perspect doesn't have a
// We need this as the source position because orthographic projects, compared to perspective doesn't have a
// convergence point, meaning that the "eye" of the camera is more like a plane than a point.
Vector3 cameraPlanePointerPos = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, -1.0f }, matProj, matView);
@ -2757,22 +2881,24 @@ float GetFrameTime(void)
// NOTE: On PLATFORM_DESKTOP, timer is initialized on glfwInit()
double GetTime(void)
{
double time = 0.0;
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
return glfwGetTime(); // Elapsed time since glfwInit()
time = glfwGetTime(); // Elapsed time since glfwInit()
#endif
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
struct timespec ts = { 0 };
clock_gettime(CLOCK_MONOTONIC, &ts);
unsigned long long int time = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
return (double)(time - CORE.Time.base)*1e-9; // Elapsed time since InitTimer()
time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer()
#endif
return time;
}
// Setup window configuration flags (view FLAGS)
// NOTE: This function is expected to be called before window creation,
// because it setups some flags for the window creation process.
// because it sets up some flags for the window creation process.
// To configure window states after creation, just use SetWindowState()
void SetConfigFlags(unsigned int flags)
{
@ -2787,6 +2913,9 @@ void SetConfigFlags(unsigned int flags)
void TakeScreenshot(const char *fileName)
{
#if defined(SUPPORT_MODULE_RTEXTURES)
// Security check to (partially) avoid malicious code on PLATFORM_WEB
if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; }
Vector2 scale = GetWindowScaleDPI();
unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
@ -2810,6 +2939,9 @@ void TakeScreenshot(const char *fileName)
}
// Get a random value between min and max (both included)
// WARNING: Ranges higher than RAND_MAX will return invalid results
// More specifically, if (max - min) > INT_MAX there will be an overflow,
// and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold
int GetRandomValue(int min, int max)
{
if (min > max)
@ -2819,6 +2951,11 @@ int GetRandomValue(int min, int max)
min = tmp;
}
if ((unsigned int)(max - min) > (unsigned int)RAND_MAX)
{
TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX);
}
return (rand()%(abs(max - min) + 1) + min);
}
@ -2989,7 +3126,7 @@ const char *GetDirectoryPath(const char *filePath)
if (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/')
{
// For security, we set starting path to current directory,
// obtained path will be concated to this
// obtained path will be concatenated to this
dirPath[0] = '.';
dirPath[1] = '/';
}
@ -3183,14 +3320,12 @@ FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool
}
// Unload directory filepaths
// WARNING: files.count is not reseted to 0 after unloading
void UnloadDirectoryFiles(FilePathList files)
{
if (files.capacity > 0)
{
for (unsigned int i = 0; i < files.capacity; i++) RL_FREE(files.paths[i]);
for (unsigned int i = 0; i < files.capacity; i++) RL_FREE(files.paths[i]);
RL_FREE(files.paths);
}
RL_FREE(files.paths);
}
// Change working directory, returns true on success
@ -3275,7 +3410,7 @@ unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDa
compData = (unsigned char *)RL_CALLOC(bounds, 1);
*compDataSize = sdeflate(&sdefl, compData, data, dataSize, COMPRESSION_QUALITY_DEFLATE); // Compression level 8, same as stbwi
TraceLog(LOG_INFO, "SYSTEM: Compress data: Original size: %i -> Comp. size: %i", dataSize, *compDataSize);
TRACELOG(LOG_INFO, "SYSTEM: Compress data: Original size: %i -> Comp. size: %i", dataSize, *compDataSize);
#endif
return compData;
@ -3288,16 +3423,19 @@ unsigned char *DecompressData(const unsigned char *compData, int compDataSize, i
#if defined(SUPPORT_COMPRESSION_API)
// Decompress data from a valid DEFLATE stream
data = RL_CALLOC(MAX_DECOMPRESSION_SIZE*1024*1024, 1);
data = (unsigned char *)RL_CALLOC(MAX_DECOMPRESSION_SIZE*1024*1024, 1);
int length = sinflate(data, MAX_DECOMPRESSION_SIZE*1024*1024, compData, compDataSize);
unsigned char *temp = RL_REALLOC(data, length);
// WARNING: RL_REALLOC can make (and leave) data copies in memory, be careful with sensitive compressed data!
// TODO: Use a different approach, create another buffer, copy data manually to it and wipe original buffer memory
unsigned char *temp = (unsigned char *)RL_REALLOC(data, length);
if (temp != NULL) data = temp;
else TRACELOG(LOG_WARNING, "SYSTEM: Failed to re-allocate required decompression memory");
*dataSize = length;
TraceLog(LOG_INFO, "SYSTEM: Decompress data: Comp. size: %i -> Original size: %i", compDataSize, *dataSize);
TRACELOG(LOG_INFO, "SYSTEM: Decompress data: Comp. size: %i -> Original size: %i", compDataSize, *dataSize);
#endif
return data;
@ -3316,7 +3454,7 @@ char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize)
*outputSize = 4*((dataSize + 2)/3);
char *encodedData = RL_MALLOC(*outputSize);
char *encodedData = (char *)RL_MALLOC(*outputSize);
if (encodedData == NULL) return NULL;
@ -3404,16 +3542,12 @@ unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize)
// Ref: https://github.com/raysan5/raylib/issues/686
void OpenURL(const char *url)
{
// Small security check trying to avoid (partially) malicious code...
// sorry for the inconvenience when you hit this point...
if (strchr(url, '\'') != NULL)
{
TRACELOG(LOG_WARNING, "SYSTEM: Provided URL is not valid");
}
// Security check to (aprtially) avoid malicious code on PLATFORM_WEB
if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
else
{
#if defined(PLATFORM_DESKTOP)
char *cmd = (char *)RL_CALLOC(strlen(url) + 10, sizeof(char));
char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char));
#if defined(_WIN32)
sprintf(cmd, "explorer \"%s\"", url);
#endif
@ -3508,7 +3642,7 @@ int GetKeyPressed(void)
CORE.Input.Keyboard.keyPressedQueue[i] = CORE.Input.Keyboard.keyPressedQueue[i + 1];
// Reset last character in the queue
CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 0;
CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount - 1] = 0;
CORE.Input.Keyboard.keyPressedQueueCount--;
}
@ -3530,7 +3664,7 @@ int GetCharPressed(void)
CORE.Input.Keyboard.charPressedQueue[i] = CORE.Input.Keyboard.charPressedQueue[i + 1];
// Reset last character in the queue
CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = 0;
CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount - 1] = 0;
CORE.Input.Keyboard.charPressedQueueCount--;
}
@ -3743,7 +3877,7 @@ Vector2 GetMousePosition(void)
// Get mouse delta between frames
Vector2 GetMouseDelta(void)
{
Vector2 delta = {0};
Vector2 delta = { 0 };
delta.x = CORE.Input.Mouse.currentPosition.x - CORE.Input.Mouse.previousPosition.x;
delta.y = CORE.Input.Mouse.currentPosition.y - CORE.Input.Mouse.previousPosition.y;
@ -3755,6 +3889,8 @@ Vector2 GetMouseDelta(void)
void SetMousePosition(int x, int y)
{
CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
// NOTE: emscripten not implemented
glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y);
@ -3884,7 +4020,7 @@ static bool InitGraphicsDevice(int width, int height)
CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default
// NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars...
// ...in top-down or left-right to match display aspect ratio (no weird scalings)
// ...in top-down or left-right to match display aspect ratio (no weird scaling)
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
glfwSetErrorCallback(ErrorCallback);
@ -4005,7 +4141,7 @@ static bool InitGraphicsDevice(int width, int height)
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context
#endif
}
else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context
else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context
{
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
@ -4053,8 +4189,18 @@ static bool InitGraphicsDevice(int width, int height)
if (CORE.Window.fullscreen)
{
// remember center for switchinging from fullscreen to window
CORE.Window.position.x = CORE.Window.display.width/2 - CORE.Window.screen.width/2;
CORE.Window.position.y = CORE.Window.display.height/2 - CORE.Window.screen.height/2;
if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width))
{
// If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed.
// Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11.
CORE.Window.position.x = CORE.Window.display.width/4;
CORE.Window.position.y = CORE.Window.display.height/4;
}
else
{
CORE.Window.position.x = CORE.Window.display.width/2 - CORE.Window.screen.width/2;
CORE.Window.position.y = CORE.Window.display.height/2 - CORE.Window.screen.height/2;
}
if (CORE.Window.position.x < 0) CORE.Window.position.x = 0;
if (CORE.Window.position.y < 0) CORE.Window.position.y = 0;
@ -4083,7 +4229,7 @@ static bool InitGraphicsDevice(int width, int height)
// framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched
// by the sides to fit all monitor space...
// Try to setup the most appropiate fullscreen framebuffer for the requested screenWidth/screenHeight
// Try to setup the most appropriate fullscreen framebuffer for the requested screenWidth/screenHeight
// It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset)
// Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale
// TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed...
@ -4150,7 +4296,7 @@ static bool InitGraphicsDevice(int width, int height)
// NOTE: V-Sync can be enabled by graphic driver configuration
if (CORE.Window.flags & FLAG_VSYNC_HINT)
{
// WARNING: It seems to hits a critical render path in Intel HD Graphics
// WARNING: It seems to hit a critical render path in Intel HD Graphics
glfwSwapInterval(1);
TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC");
}
@ -4161,12 +4307,12 @@ static bool InitGraphicsDevice(int width, int height)
#if defined(PLATFORM_DESKTOP)
if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
{
// NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling
// NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling.
// Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE);
#if !defined(__APPLE__)
glfwGetFramebufferSize(CORE.Window.handle, &fbWidth, &fbHeight);
// Screen scaling matrix is required in case desired screen area is different than display area
// Screen scaling matrix is required in case desired screen area is different from display area
CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f);
// Mouse input scaling for the new screen size
@ -4764,7 +4910,7 @@ static void InitTimer(void)
// NOTE: Sleep() granularity could be around 10 ms, it means, Sleep() could
// take longer than expected... for that reason we use the busy wait loop
// Ref: http://stackoverflow.com/questions/43057578/c-programming-win32-games-sleep-taking-longer-than-expected
// Ref: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timming on Win32!
// Ref: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timing on Win32!
void WaitTime(double seconds)
{
#if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
@ -4859,7 +5005,7 @@ void PollInputEvents(void)
#if !(defined(PLATFORM_RPI) || defined(PLATFORM_DRM))
// Reset last gamepad button/axis registered state
CORE.Input.Gamepad.lastButtonPressed = -1;
CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
CORE.Input.Gamepad.axisCount = 0;
#endif
@ -5359,12 +5505,12 @@ static void CharCallback(GLFWwindow *window, unsigned int key)
//TRACELOG(LOG_DEBUG, "Char Callback: KEY:%i(%c)", key, key);
// NOTE: Registers any key down considering OS keyboard layout but
// do not detects action events, those should be managed by user...
// does not detect action events, those should be managed by user...
// Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907
// Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char
// Check if there is space available in the queue
if (CORE.Input.Keyboard.charPressedQueueCount < MAX_KEY_PRESSED_QUEUE)
if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
{
// Add character to the queue
CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = key;
@ -5402,7 +5548,7 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int
gestureEvent.position[0].x /= (float)GetScreenWidth();
gestureEvent.position[0].y /= (float)GetScreenHeight();
// Gesture data is sent to gestures system for processing
// Gesture data is sent to gestures-system for processing
ProcessGestureEvent(gestureEvent);
#endif
}
@ -5433,7 +5579,7 @@ static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
gestureEvent.position[0].x /= (float)GetScreenWidth();
gestureEvent.position[0].y /= (float)GetScreenHeight();
// Gesture data is sent to gestures system for processing
// Gesture data is sent to gestures-system for processing
ProcessGestureEvent(gestureEvent);
#endif
}
@ -5454,25 +5600,28 @@ static void CursorEnterCallback(GLFWwindow *window, int enter)
// GLFW3 Window Drop Callback, runs when drop files into window
static void WindowDropCallback(GLFWwindow *window, int count, const char **paths)
{
// In case previous dropped filepaths have not been freed, we free them
if (CORE.Window.dropFileCount > 0)
if (count > 0)
{
for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilepaths[i]);
// In case previous dropped filepaths have not been freed, we free them
if (CORE.Window.dropFileCount > 0)
{
for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilepaths[i]);
RL_FREE(CORE.Window.dropFilepaths);
RL_FREE(CORE.Window.dropFilepaths);
CORE.Window.dropFileCount = 0;
CORE.Window.dropFilepaths = NULL;
}
CORE.Window.dropFileCount = 0;
CORE.Window.dropFilepaths = NULL;
}
// WARNING: Paths are freed by GLFW when the callback returns, we must keep an internal copy
CORE.Window.dropFileCount = count;
CORE.Window.dropFilepaths = (char **)RL_CALLOC(CORE.Window.dropFileCount, sizeof(char *));
// WARNING: Paths are freed by GLFW when the callback returns, we must keep an internal copy
CORE.Window.dropFileCount = count;
CORE.Window.dropFilepaths = (char **)RL_CALLOC(CORE.Window.dropFileCount, sizeof(char *));
for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++)
{
CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
strcpy(CORE.Window.dropFilepaths[i], paths[i]);
for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++)
{
CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
strcpy(CORE.Window.dropFilepaths[i], paths[i]);
}
}
}
#endif
@ -5702,14 +5851,15 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
CORE.Input.Gamepad.ready[0] = true;
GamepadButton button = AndroidTranslateGamepadButton(keycode);
if (button == GAMEPAD_BUTTON_UNKNOWN)
return 1;
if (button == GAMEPAD_BUTTON_UNKNOWN) return 1;
if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN)
{
CORE.Input.Gamepad.currentButtonState[0][button] = 1;
}
else CORE.Input.Gamepad.currentButtonState[0][button] = 0; // Key up
return 1; // Handled gamepad button
}
@ -6673,7 +6823,7 @@ static void *GamepadThread(void *arg)
CORE.Input.Gamepad.currentButtonState[i][gamepadEvent.number] = (int)gamepadEvent.value;
if ((int)gamepadEvent.value == 1) CORE.Input.Gamepad.lastButtonPressed = gamepadEvent.number;
else CORE.Input.Gamepad.lastButtonPressed = -1;
else CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
}
}
else if (gamepadEvent.type == JS_EVENT_AXIS)
@ -6800,12 +6950,12 @@ static void LoadAutomationEvents(const char *fileName)
// Load binary
/*
FILE *repFile = fopen(fileName, "rb");
fread(fileId, 4, 1, repFile);
fread(fileId, 1, 4, repFile);
if ((fileId[0] == 'r') && (fileId[1] == 'E') && (fileId[2] == 'P') && (fileId[1] == ' '))
{
fread(&eventCount, sizeof(int), 1, repFile);
TraceLog(LOG_WARNING, "Events loaded: %i\n", eventCount);
TRACELOG(LOG_WARNING, "Events loaded: %i\n", eventCount);
fread(events, sizeof(AutomationEvent), eventCount, repFile);
}
@ -6852,7 +7002,7 @@ static void ExportAutomationEvents(const char *fileName)
// Save as binary
/*
FILE *repFile = fopen(fileName, "wb");
fwrite(fileId, 4, 1, repFile);
fwrite(fileId, sizeof(unsigned char), 4, repFile);
fwrite(&eventCount, sizeof(int), 1, repFile);
fwrite(events, sizeof(AutomationEvent), eventCount, repFile);
fclose(repFile);