24 Commits
6.2 ... master

Author SHA1 Message Date
dcdbdde89b [examples] shapes_penrose_tile: fix memory leak on exit (#6301) 2026-10-19 10:11:00 +02:00
9c5d9f2856 chore(bindings): update raylib-d to 6.2 (#6302) 2026-10-19 10:09:51 +02:00
a750741cf8 [rcore][win32] Implement IsWindowFocused() and SetWindowFocused() (#6300)
* [rcore][win32] Implement IsWindowFocused()

* FLAG_WINDOW_UNFOCUSED and SetWindowFocused

* Minor formatting changes
2026-10-18 23:33:47 +02:00
2e66d4080c [rtextures] Fix ImageCrop() float precision issues (#6295) 2026-10-18 15:36:22 +02:00
ab1dde8fbb [rcore][GLFW] Skip workarea clamp when the reported workarea size is invalid (#6296)
Some window managers on X11 multi-monitor setups report a _NET_WORKAREA that does not overlap the primary monitor, so glfwGetMonitorWorkarea() returns a zero width or height. The window size was then clamped to 0 and window creation failed with "Invalid window size 0x450".

Fixes #6292
2026-10-18 15:33:43 +02:00
f400535eb6 [examples] shapes_pie_chart: use DrawRing() for donut mode, fix related bugs (#6293) 2026-10-18 15:28:55 +02:00
d9ff09c3c9 [rcore][win32] Re-add EnableCursor() to ClosePlatform() (#6294) 2026-10-18 15:28:06 +02:00
9b6d185ef0 rlparser: update raylib_api.* by CI 2026-10-17 18:20:52 +00:00
Ray
28789122ce Update version to avoid confusions with latest release 2026-10-17 20:20:35 +02:00
Ray
5dc3674c00 Update rcore.c 2026-10-17 20:15:03 +02:00
Ray
bf08870034 REVIEWED: CloseWindow(), clear CORE data
NOTE: Win32, flags are not kept between windows initializations
2026-10-17 19:43:11 +02:00
3061e73f0e [rcore][win32] Support legacy versions of Windows (#6284) 2026-10-17 18:25:26 +02:00
986e8b0f1a [rtextures] Fix heap-buffer-overflow when saving KTX images (#6290)
rl_save_ktx allocated only header + pixel bytes, but each mipmap level
is written as UInt32 imageSize followed by the pixels. Include those
4 bytes per level in the allocation size.

Fixes #6289
2026-10-17 18:23:35 +02:00
777e17f9d4 [rcore] Normalize mouse wheel behavior across backends (#6291) 2026-10-17 18:10:12 +02:00
c4a49fd24f [rcore][win32] Allow close requests to be cancelled (#6285) 2026-10-17 15:03:33 +02:00
dec2fdfbf8 [examples] shapes_pie_chart: fix slice list not scrolling (#6282) 2026-10-17 14:59:19 +02:00
3d5d33bc51 [examples] shapes_pie_chart: fix slice labels not editable (#6281) 2026-10-17 14:58:28 +02:00
430439e63b [rmodels] Fix heap overflow and dropped faces for OBJ n-gons (#6283)
parseLine() set command->type = COMMAND_F before the face-size checks.
A face with 6+ vertices then returned 0 early, but the command was left
looking like a valid COMMAND_F while its num_f/f[] fields were
uninitialized (commands[] is malloc'd, not zeroed). The
attribute-construction pass then looped over garbage num_f values,
writing out of bounds into attrib->faces. This caused intermittent
SIGSEGV / "malloc(): invalid size" crashes in LoadModel() and silently
dropped n-gon faces (e.g. a mesh missing its floor).

Changes:
- Set command->type = COMMAND_F only after the face is fully validated
  and filled.
- Bound the f[num_f] collection loop against the fixed-size buffer.
- Reject faces with fewer than 3 vertices.
- Validate the triangulated output size as 3*(num_f-2) rather than
  3*num_f.
- Raise TINYOBJ_MAX_FACES_PER_F_LINE from 16 to 64 so triangulated n-gons
  fit.

Verified with ASan (reported heap-buffer-overflow at
tinyobj_loader_c.h:1432) and Valgrind (uninitialized reads at
:1427/1432-1434/1437). After the fix a 6-gon loads as 4 triangles and
an 8-gon bathtub floor loads (248 vs 204 triangles).
2026-10-17 14:56:34 +02:00
226b281e55 [rtextures] Fix use-after-free in LoadImageFromMemory for PEP format (#6288) 2026-10-17 14:54:37 +02:00
c757c7435a fix_app_freeze_SDL (#6279) 2026-10-16 10:43:21 +02:00
b9758a6403 [rtextures] Fix QOI loading reading past the end of short file data (#6245)
LoadImageFromMemory() passed fileData[12] (the channels byte of the QOI
header) to qoi_decode() as the requested channel count, reading it before
qoi_decode() checks the data size. Data shorter than 13 bytes made it read
past the end of the buffer.

Pass 0 instead, so qoi_decode() uses the channel count from the header after
checking the size. For valid files this gives the same result, since the
value passed was that same header byte.
2026-10-16 10:09:45 +02:00
426d656185 Fix typo in HISTORY.md (#6278) 2026-10-16 09:41:11 +02:00
c0921587c3 Update Ray4Laz version to 6.2 in BINDINGS.md (#6277) 2026-10-16 00:05:05 +02:00
Ray
8658e4e551 Update README.md 2026-10-15 18:27:36 +02:00
22 changed files with 214 additions and 117 deletions

View File

@ -44,7 +44,7 @@ This is a set of recomendations and guidelines for binding authors, meant to hel
| [dart-raylib](https://gitlab.com/wolfenrain/dart-raylib) | 4.0 | [Dart](https://dart.dev) | MIT | | [dart-raylib](https://gitlab.com/wolfenrain/dart-raylib) | 4.0 | [Dart](https://dart.dev) | MIT |
| [raylib_dart](https://pub.dev/packages/raylib_dart) | 6.0 | [Dart](https://dart.dev) | MIT | | [raylib_dart](https://pub.dev/packages/raylib_dart) | 6.0 | [Dart](https://dart.dev) | MIT |
| [bindbc-raylib3](https://github.com/o3o/bindbc-raylib3) | **5.0** | [D](https://dlang.org) | BSL-1.0 | | [bindbc-raylib3](https://github.com/o3o/bindbc-raylib3) | **5.0** | [D](https://dlang.org) | BSL-1.0 |
| [raylib-d](https://github.com/schveiguy/raylib-d) | **6.0** | [D](https://dlang.org) | Zlib | | [raylib-d](https://github.com/schveiguy/raylib-d) | **6.2** | [D](https://dlang.org) | Zlib |
| [Deno-Raylib](https://github.com/JJLDonley/Deno-Raylib) | **6.0** | [Deno / TS](https://deno.land) | MIT | | [Deno-Raylib](https://github.com/JJLDonley/Deno-Raylib) | **6.0** | [Deno / TS](https://deno.land) | MIT |
| [rayex](https://github.com/shiryel/rayex) | 5.5 | [elixir](https://elixir-lang.org) | Apache-2.0 | | [rayex](https://github.com/shiryel/rayex) | 5.5 | [elixir](https://elixir-lang.org) | Apache-2.0 |
| [raylib-elfscript](https://github.com/ohmtal/raylib-elfscript) | 6.0 | [ElfScript](https://github.com/ohmtal/ElfScript) | MIT | | [raylib-elfscript](https://github.com/ohmtal/raylib-elfscript) | 6.0 | [ElfScript](https://github.com/ohmtal/ElfScript) | MIT |
@ -83,7 +83,7 @@ This is a set of recomendations and guidelines for binding authors, meant to hel
| [TurboRaylib](https://github.com/turborium/TurboRaylib) | 4.5 | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT | | [TurboRaylib](https://github.com/turborium/TurboRaylib) | 4.5 | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT |
| [Rayberon](https://github.com/RobertFlexx/Rayberon) | **6.0** | [Oberon-2](https://en.wikipedia.org/wiki/Oberon-2) | MIT | | [Rayberon](https://github.com/RobertFlexx/Rayberon) | **6.0** | [Oberon-2](https://en.wikipedia.org/wiki/Oberon-2) | MIT |
| [EuRaylib](https://github.com/gAndy50/EuRaylib6) | **6.0** | [openEuphoria](https://openeuphoria.org/) | Zlib | | [EuRaylib](https://github.com/gAndy50/EuRaylib6) | **6.0** | [openEuphoria](https://openeuphoria.org/) | Zlib |
| [Ray4Laz](https://github.com/GuvaCode/Ray4Laz) | **6.0** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal) | Zlib | | [Ray4Laz](https://github.com/GuvaCode/Ray4Laz) | **6.2** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal) | Zlib |
| [Raylib.4.0.Pascal](https://github.com/sysrpl/Raylib.4.0.Pascal) | 4.0 | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal) | Zlib | | [Raylib.4.0.Pascal](https://github.com/sysrpl/Raylib.4.0.Pascal) | 4.0 | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal) | Zlib |
| [raylib-python-cffi](https://github.com/electronstudio/raylib-python-cffi) | **6.0** | [Python](https://www.python.org) | EPL-2.0 | | [raylib-python-cffi](https://github.com/electronstudio/raylib-python-cffi) | **6.0** | [Python](https://www.python.org) | EPL-2.0 |
| [raylibpyctbg](https://github.com/overdev/raylibpyctbg) | 5.5 | [Python](https://www.python.org) | MIT | | [raylibpyctbg](https://github.com/overdev/raylibpyctbg) | 5.5 | [Python](https://www.python.org) | MIT |

View File

@ -689,6 +689,6 @@ Make sure to check raylib [CHANGELOG](https://github.com/raysan5/raylib/blob/mas
Again, I want to **thank all the contributors (+950!**) that helped improving raylib, this time there have been **more than 100 new contributors** sending fixes and improvements! Again, I want to **thank all the contributors (+950!**) that helped improving raylib, this time there have been **more than 100 new contributors** sending fixes and improvements!
After 13 years of development, `raylib 6.2` is today probably one of the bests libraries to enjoy **games/tools/graphics/AI programming!** After 13 years of development, `raylib 6.2` is today probably one of the best libraries to enjoy **games/tools/graphics/AI programming!**
**Hope you keep enjoying programming with raylib!** :) **Hope you keep enjoying programming with raylib!** :)

View File

@ -14,7 +14,7 @@ Ready to learn? Jump to [code examples!](https://www.raylib.com/examples.html)
[![GitHub Releases Downloads](https://img.shields.io/github/downloads/raysan5/raylib/total)](https://github.com/raysan5/raylib/releases) [![GitHub Releases Downloads](https://img.shields.io/github/downloads/raysan5/raylib/total)](https://github.com/raysan5/raylib/releases)
[![GitHub Stars](https://img.shields.io/github/stars/raysan5/raylib?style=flat&label=stars)](https://github.com/raysan5/raylib/stargazers) [![GitHub Stars](https://img.shields.io/github/stars/raysan5/raylib?style=flat&label=stars)](https://github.com/raysan5/raylib/stargazers)
[![GitHub commits since tagged version](https://img.shields.io/github/commits-since/raysan5/raylib/6.0)](https://github.com/raysan5/raylib/commits/master) [![GitHub commits since tagged version](https://img.shields.io/github/commits-since/raysan5/raylib/6.2)](https://github.com/raysan5/raylib/commits/master)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/raysan5?label=sponsors)](https://github.com/sponsors/raysan5) [![GitHub Sponsors](https://img.shields.io/github/sponsors/raysan5?label=sponsors)](https://github.com/sponsors/raysan5)
[![License](https://img.shields.io/badge/license-zlib%2Flibpng-blue.svg)](LICENSE) [![License](https://img.shields.io/badge/license-zlib%2Flibpng-blue.svg)](LICENSE)

View File

@ -77,7 +77,7 @@ int main(void)
int maxGenerations = 4; int maxGenerations = 4;
int generations = 0; int generations = 0;
// Initializee new penrose tile // Initialize new penrose tile
PenroseLSystem ls = CreatePenroseLSystem(drawLength*(generations/(float)maxGenerations)); PenroseLSystem ls = CreatePenroseLSystem(drawLength*(generations/(float)maxGenerations));
for (int i = 0; i < generations; i++) BuildProductionStep(&ls); for (int i = 0; i < generations; i++) BuildProductionStep(&ls);
@ -133,6 +133,8 @@ int main(void)
// De-Initialization // De-Initialization
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
RL_FREE(ls.production); // Free production string
CloseWindow(); // Close window and OpenGL context CloseWindow(); // Close window and OpenGL context
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------

View File

@ -41,6 +41,7 @@ int main(void)
float donutInnerRadius = 25.0f; float donutInnerRadius = 25.0f;
float values[MAX_PIE_SLICES] = { 300.0f, 100.0f, 450.0f, 350.0f, 600.0f, 380.0f, 750.0f }; // Initial slice values float values[MAX_PIE_SLICES] = { 300.0f, 100.0f, 450.0f, 350.0f, 600.0f, 380.0f, 750.0f }; // Initial slice values
bool editingLabel[MAX_PIE_SLICES] = { 0 }; bool editingLabel[MAX_PIE_SLICES] = { 0 };
char labels[MAX_PIE_SLICES][32] = { 0 }; // Slice label texts, editable
bool showValues = true; bool showValues = true;
bool showPercentages = false; bool showPercentages = false;
@ -75,6 +76,9 @@ int main(void)
// Total value for percentage calculations // Total value for percentage calculations
float totalValue = 0.0f; float totalValue = 0.0f;
// Initialize slice labels
for (int i = 0; i < MAX_PIE_SLICES; i++) snprintf(labels[i], 32, "Slice %02i", i + 1);
SetTargetFPS(60); SetTargetFPS(60);
//-------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------
@ -97,7 +101,7 @@ int main(void)
float dy = mousePos.y - center.y; float dy = mousePos.y - center.y;
float distance = sqrtf(dx*dx + dy*dy); float distance = sqrtf(dx*dx + dy*dy);
if (distance <= radius) // Inside the pie radius if ((distance <= radius) && (!showDonut || distance >= donutInnerRadius)) // Inside the pie, outside the donut hole
{ {
float angle = atan2f(dy, dx)*RAD2DEG; float angle = atan2f(dy, dx)*RAD2DEG;
if (angle < 0) angle += 360; if (angle < 0) angle += 360;
@ -134,11 +138,12 @@ int main(void)
Color color = ColorFromHSV((float)i/sliceCount*360.0f, 0.75f, 0.9f); Color color = ColorFromHSV((float)i/sliceCount*360.0f, 0.75f, 0.9f);
float currentRadius = radius; float currentRadius = radius;
// Make the hovered slice pop out by adding 5 pixels to its radius // Make the hovered slice pop out by adding 20 pixels to its radius
if (i == hoveredSlice) currentRadius += 20.0f; if (i == hoveredSlice) currentRadius += 20.0f;
// Draw the pie slice using raylib's DrawCircleSector function // Draw the pie slice (DrawRing() for donut mode)
DrawCircleSector(center, currentRadius, startAngle, startAngle + sweepAngle, 120, color); if (showDonut) DrawRing(center, donutInnerRadius, currentRadius, startAngle, startAngle + sweepAngle, 120, color);
else DrawCircleSector(center, currentRadius, startAngle, startAngle + sweepAngle, 120, color);
// Draw the label for the current slice // Draw the label for the current slice
if (values[i] > 0) if (values[i] > 0)
@ -150,16 +155,12 @@ int main(void)
else labelText[0] = '\0'; else labelText[0] = '\0';
Vector2 textSize = MeasureTextEx(GetFontDefault(), labelText, 20, 1); Vector2 textSize = MeasureTextEx(GetFontDefault(), labelText, 20, 1);
float labelRadius = radius*0.7f; float labelRadius = showDonut? (donutInnerRadius + radius)/2.0f : radius*0.7f;
Vector2 labelPos = { center.x + cosf(midAngle*DEG2RAD)*labelRadius - textSize.x/2.0f, Vector2 labelPos = { center.x + cosf(midAngle*DEG2RAD)*labelRadius - textSize.x/2.0f,
center.y + sinf(midAngle*DEG2RAD)*labelRadius - textSize.y/2.0f }; center.y + sinf(midAngle*DEG2RAD)*labelRadius - textSize.y/2.0f };
DrawText(labelText, (int)labelPos.x, (int)labelPos.y, 20, WHITE); DrawText(labelText, (int)labelPos.x, (int)labelPos.y, 20, WHITE);
} }
// Draw inner circle to create donut effect
// TODO: This is a hacky solution, better use DrawRing()
if (showDonut) DrawCircleV(center, donutInnerRadius, RAYWHITE);
startAngle += sweepAngle; startAngle += sweepAngle;
} }
@ -172,7 +173,7 @@ int main(void)
GuiCheckBox((Rectangle){ panelPos.x + 20, (float)panelPos.y + 12 + 70, 20, 20 }, "Show Percentages", &showPercentages); GuiCheckBox((Rectangle){ panelPos.x + 20, (float)panelPos.y + 12 + 70, 20, 20 }, "Show Percentages", &showPercentages);
GuiCheckBox((Rectangle){ panelPos.x + 20, (float)panelPos.y + 12 + 100, 20, 20 }, "Make Donut", &showDonut); GuiCheckBox((Rectangle){ panelPos.x + 20, (float)panelPos.y + 12 + 100, 20, 20 }, "Make Donut", &showDonut);
if (showDonut) GuiDisable(); if (!showDonut) GuiDisable();
GuiSliderBar((Rectangle){ panelPos.x + 80, (float)panelPos.y + 12 + 130, panelRect.width - 100, 30 }, GuiSliderBar((Rectangle){ panelPos.x + 80, (float)panelPos.y + 12 + 130, panelRect.width - 100, 30 },
"Inner Radius", NULL, & donutInnerRadius, 5.0f, radius - 10.0f); "Inner Radius", NULL, & donutInnerRadius, 5.0f, radius - 10.0f);
GuiEnable(); GuiEnable();
@ -184,7 +185,7 @@ int main(void)
panelPos.x + panelMargin, panelPos.x + panelMargin,
(float)panelPos.y + 12 + 190, (float)panelPos.y + 12 + 190,
panelRect.width - panelMargin*2, panelRect.width - panelMargin*2,
panelRect.y + panelRect.height - panelPos.y + 12 + 190 - panelMargin panelRect.y + panelRect.height - (panelPos.y + 12 + 190) - panelMargin
}; };
int contentHeight = sliceCount*35; int contentHeight = sliceCount*35;
@ -205,7 +206,7 @@ int main(void)
DrawRectangle((int)(contentX + 15), rowY + 5, 20, 20, color); DrawRectangle((int)(contentX + 15), rowY + 5, 20, 20, color);
// Label textbox // Label textbox
if (GuiTextBox((Rectangle){ contentX + 45, (float)rowY, 75, 30 }, TextFormat("Slice %02i", i + 1), 32, editingLabel[i])) if (GuiTextBox((Rectangle){ contentX + 45, (float)rowY, 75, 30 }, labels[i], 32, editingLabel[i]))
editingLabel[i] = !editingLabel[i]; editingLabel[i] = !editingLabel[i];
GuiSliderBar((Rectangle){ contentX + 130, (float)rowY, 110, 30 }, NULL, NULL, &values[i], 0.0f, 1000.0f); GuiSliderBar((Rectangle){ contentX + 130, (float)rowY, 110, 30 }, NULL, NULL, &values[i], 0.0f, 1000.0f);

View File

@ -605,8 +605,10 @@ int rl_save_ktx(const char *file_name, void *data, int width, int height, int fo
// Calculate file data_size required // Calculate file data_size required
int data_size = sizeof(ktx_header); int data_size = sizeof(ktx_header);
// NOTE: Each mipmap level is stored as: UInt32 imageSize + pixel data
for (int i = 0, w = width, h = height; i < mipmaps; i++) for (int i = 0, w = width, h = height; i < mipmaps; i++)
{ {
data_size += (int)sizeof(unsigned int);
data_size += get_pixel_data_size(w, h, format); data_size += get_pixel_data_size(w, h, format);
w /= 2; h /= 2; w /= 2; h /= 2;
} }

View File

@ -128,7 +128,7 @@ extern void tinyobj_materials_free(tinyobj_material_t *materials,
#define TINYOBJ_FREE free #define TINYOBJ_FREE free
#endif #endif
#define TINYOBJ_MAX_FACES_PER_F_LINE (16) #define TINYOBJ_MAX_FACES_PER_F_LINE (64)
#define IS_SPACE(x) (((x) == ' ') || ((x) == '\t')) #define IS_SPACE(x) (((x) == ' ') || ((x) == '\t'))
#define IS_DIGIT(x) ((unsigned int)((x) - '0') < (unsigned int)(10)) #define IS_DIGIT(x) ((unsigned int)((x) - '0') < (unsigned int)(10))
@ -1093,11 +1093,20 @@ static int parseLine(Command *command, const char *p, unsigned int p_len,
tinyobj_vertex_index_t vi = parseRawTriple(&token); tinyobj_vertex_index_t vi = parseRawTriple(&token);
skip_space_and_cr(&token); skip_space_and_cr(&token);
// Guard the fixed-size f[] buffer against overrun.
if (num_f >= TINYOBJ_MAX_FACES_PER_F_LINE) return 0;
f[num_f] = vi; f[num_f] = vi;
num_f++; num_f++;
} }
command->type = COMMAND_F; // A face needs at least 3 vertices, otherwise the triangulation
// below reads uninitialized f[] entries.
if (num_f < 3) return 0;
// NOTE: command->type is deliberately not set here. A face rejected by
// the size checks below must not look like COMMAND_F to the later
// attribute-construction pass, which would otherwise consume
// uninitialized command->num_f/f[] fields (heap buffer overflow).
if (triangulate) { if (triangulate) {
unsigned int k; unsigned int k;
@ -1111,7 +1120,7 @@ static int parseLine(Command *command, const char *p, unsigned int p_len,
// WARNING: If -DNDEBUG is set when compiling, assertions will not // WARNING: If -DNDEBUG is set when compiling, assertions will not
// be present in the compiled binary, replacing by a runtime check // be present in the compiled binary, replacing by a runtime check
//assert(3 * num_f < TINYOBJ_MAX_FACES_PER_F_LINE); //assert(3 * num_f < TINYOBJ_MAX_FACES_PER_F_LINE);
if (3*num_f > TINYOBJ_MAX_FACES_PER_F_LINE) return 0; if ((num_f - 2) * 3 > TINYOBJ_MAX_FACES_PER_F_LINE) return 0;
for (k = 2; k < num_f; k++) { for (k = 2; k < num_f; k++) {
i1 = i2; i1 = i2;
@ -1144,6 +1153,9 @@ static int parseLine(Command *command, const char *p, unsigned int p_len,
command->num_f_num_verts = 1; command->num_f_num_verts = 1;
} }
// Mark COMMAND_F only after full validation/fill so a rejected face
// stays COMMAND_EMPTY for the attribute pass below.
command->type = COMMAND_F;
return 1; return 1;
} }

View File

@ -1677,8 +1677,10 @@ int InitPlatform(void)
// If the area requested by the user exceeds the maximum workable area, clamp it to that // If the area requested by the user exceeds the maximum workable area, clamp it to that
// GLFW has a problem where if the window is greater than the workable area (this means // GLFW has a problem where if the window is greater than the workable area (this means
// the taskbar / dockable areas) it won't show up if the window isn't fullscreen // the taskbar / dockable areas) it won't show up if the window isn't fullscreen
if (CORE.Window.screen.width > (unsigned int)workWidth) CORE.Window.screen.width = workWidth; // NOTE: Some window managers report a workarea that does not overlap the primary monitor
if (CORE.Window.screen.height > (unsigned int)workHeight) CORE.Window.screen.height = workHeight; // (i.e. multi-monitor setups on X11), resulting in a zero/negative size, skip clamping then
if ((workWidth > 0) && (CORE.Window.screen.width > (unsigned int)workWidth)) CORE.Window.screen.width = workWidth;
if ((workHeight > 0) && (CORE.Window.screen.height > (unsigned int)workHeight)) CORE.Window.screen.height = workHeight;
platform.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); platform.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL);
if (!platform.handle) if (!platform.handle)
@ -2206,7 +2208,8 @@ static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
// GLFW3: Mouse wheel scroll callback, runs on mouse wheel changes // GLFW3: Mouse wheel scroll callback, runs on mouse wheel changes
static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset) static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset)
{ {
CORE.Input.Mouse.currentWheelMove = (Vector2){ (float)xoffset, (float)yoffset }; CORE.Input.Mouse.currentWheelMove.x += (float)xoffset;
CORE.Input.Mouse.currentWheelMove.y += (float)yoffset;
} }
// GLFW3: Cursor ennter callback, when cursor enters the window // GLFW3: Cursor ennter callback, when cursor enters the window

View File

@ -2020,19 +2020,12 @@ int InitPlatform(void)
// Close platform // Close platform
void ClosePlatform(void) void ClosePlatform(void)
{ {
mg_gamepads_free(&platform.minigamepad); mg_gamepads_free(&platform.minigamepad); // Clear gamepad data
RGFW_window_close(platform.window); RGFW_window_close(platform.window);
#if defined(GRAPHICS_API_OPENGL_SOFTWARE) #if defined(GRAPHICS_API_OPENGL_SOFTWARE)
if (platform.surfacePixels != NULL) if (platform.surfacePixels != NULL) RL_FREE(platform.surfacePixels);
{ if (platform.surface != NULL) RGFW_surface_free(platform.surface);
RL_FREE(platform.surfacePixels);
}
if (platform.surface != NULL)
{
RGFW_surface_free(platform.surface);
}
#endif #endif
} }

View File

@ -1720,11 +1720,11 @@ void PollInputEvents(void)
case SDL_MOUSEWHEEL: case SDL_MOUSEWHEEL:
{ {
#if defined(USING_VERSION_SDL3) #if defined(USING_VERSION_SDL3)
CORE.Input.Mouse.currentWheelMove.x = event.wheel.x; CORE.Input.Mouse.currentWheelMove.x -= event.wheel.x;
CORE.Input.Mouse.currentWheelMove.y = event.wheel.y; CORE.Input.Mouse.currentWheelMove.y += event.wheel.y;
#else #else
CORE.Input.Mouse.currentWheelMove.x = event.wheel.preciseX; CORE.Input.Mouse.currentWheelMove.x -= event.wheel.preciseX;
CORE.Input.Mouse.currentWheelMove.y = event.wheel.preciseY; CORE.Input.Mouse.currentWheelMove.y += event.wheel.preciseY;
#endif #endif
} break; } break;
case SDL_MOUSEMOTION: case SDL_MOUSEMOTION:
@ -2198,6 +2198,7 @@ int InitPlatform(void)
// NOTE: No need to call InitTimer(), let SDL manage it internally // NOTE: No need to call InitTimer(), let SDL manage it internally
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
CORE.Time.previous = 0.0;
// Initialize storage system // Initialize storage system
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -2218,8 +2219,10 @@ int InitPlatform(void)
void ClosePlatform(void) void ClosePlatform(void)
{ {
SDL_FreeCursor(platform.cursor); // Free cursor SDL_FreeCursor(platform.cursor); // Free cursor
if (platform.glContext != NULL) SDL_GL_DeleteContext(platform.glContext); // Deinitialize OpenGL context if (platform.glContext != NULL) SDL_GL_DeleteContext(platform.glContext); // Deinitialize OpenGL context
SDL_DestroyWindow(platform.window); SDL_DestroyWindow(platform.window);
SDL_Quit(); // Deinitialize SDL internal global state SDL_Quit(); // Deinitialize SDL internal global state
} }

View File

@ -107,6 +107,10 @@ typedef BOOL (WINAPI *PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC, const int *, const FL
typedef BOOL (WINAPI *PFNWGLSWAPINTERVALEXTPROC)(int); typedef BOOL (WINAPI *PFNWGLSWAPINTERVALEXTPROC)(int);
typedef const char *(WINAPI *PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC hdc); typedef const char *(WINAPI *PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC hdc);
// Define Win32 function pointer types
typedef UINT (WINAPI *GetDpiForWindowProc)(HWND hwnd);
typedef BOOL (WINAPI *AdjustWindowRectExForDpiProc)(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi);
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Global Variables Definition // Global Variables Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -120,6 +124,10 @@ static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL;
static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
static PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = NULL; static PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = NULL;
// Win32 functions that need to be dynamically loaded to support older versions of Windows
static GetDpiForWindowProc pGetDpiForWindow = NULL;
static AdjustWindowRectExForDpiProc pAdjustWindowRectExForDpi = NULL;
// -------------------------------------------------------------------------------- // --------------------------------------------------------------------------------
// This part of the file contains pure functions that never access global state // This part of the file contains pure functions that never access global state
// This distinction helps keep the backend maintainable as the inputs and outputs // This distinction helps keep the backend maintainable as the inputs and outputs
@ -152,7 +160,7 @@ static PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = NULL;
#define CLASS_NAME L"raylibWindow" #define CLASS_NAME L"raylibWindow"
#define FLAG_MASK_OPTIONAL (FLAG_VSYNC_HINT) #define FLAG_MASK_OPTIONAL (FLAG_VSYNC_HINT | FLAG_WINDOW_UNFOCUSED)
#define FLAG_MASK_REQUIRED ~(FLAG_MASK_OPTIONAL) #define FLAG_MASK_REQUIRED ~(FLAG_MASK_OPTIONAL)
// Flags that have no operations to perform during an update // Flags that have no operations to perform during an update
@ -200,6 +208,20 @@ static PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = NULL;
#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004 #define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004
#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 #define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004
// Older versions of Windows don't support these events
// These events won't be triggered, but this allows them to compile
#ifndef WM_DPICHANGED
#define WM_DPICHANGED 0x02E0
#endif
#ifndef WM_GETDPISCALEDSIZE
#define WM_GETDPISCALEDSIZE 0x02E4
#endif
#ifndef WM_MOUSEHWHEEL
#define WM_MOUSEHWHEEL 0x020E
#endif
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Types and Structures Definition // Types and Structures Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -368,6 +390,51 @@ static void AToWCopy(const char *ascii, wchar_t *outPtr, size_t outLen)
if (size != outLen) TRACELOG(LOG_WARNING, "WIN32: Failed to convert %i UTF-8 chars to WCHAR, converted %i chars", outLen, size); if (size != outLen) TRACELOG(LOG_WARNING, "WIN32: Failed to convert %i UTF-8 chars to WCHAR, converted %i chars", outLen, size);
} }
// Dynamically load Windows functions that may not be available on older versions
static void LoadWin32FunctionPointers(void)
{
HMODULE user32 = GetModuleHandleW(L"user32.dll");
pGetDpiForWindow = (GetDpiForWindowProc)GetProcAddress(user32, "GetDpiForWindow");
pAdjustWindowRectExForDpi = (AdjustWindowRectExForDpiProc)GetProcAddress(user32, "AdjustWindowRectExForDpi");
}
// GetDpiForWindow with a fallback for older versions of Windows
static UINT GetDpiForWindowCompat(HWND hwnd)
{
UINT result = 0;
if (pGetDpiForWindow != NULL)
{
// This API requires Windows 10 v1607 or newer
result = pGetDpiForWindow(hwnd);
}
else
{
// Window uses CS_OWNDC, so we don't need to call ReleaseDC
HDC dc = GetDC(hwnd);
// Before Windows 10 v1607: fallback to system-wide DPI (96 for DPI-unaware processes)
result = GetDeviceCaps(dc, LOGPIXELSX);
}
return result;
}
// AdjustWindowRectExForDpi with a fallback for older versions of Windows
static BOOL AdjustWindowRectExForDpiCompat(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi)
{
BOOL result = FALSE;
if (pAdjustWindowRectExForDpi != NULL)
{
result = pAdjustWindowRectExForDpi(lpRect, dwStyle, bMenu, dwExStyle, dpi);
}
else
{
result = AdjustWindowRectEx(lpRect, dwStyle, bMenu, dwExStyle);
}
return result;
}
static bool DecoratedFromStyle(DWORD style) static bool DecoratedFromStyle(DWORD style)
{ {
if (style & STYLE_FLAGS_UNDECORATED_ON) if (style & STYLE_FLAGS_UNDECORATED_ON)
@ -464,7 +531,7 @@ static SIZE CalcWindowSize(UINT dpi, SIZE clientSize, DWORD style)
{ {
RECT rect = { 0, 0, clientSize.cx, clientSize.cy }; RECT rect = { 0, 0, clientSize.cx, clientSize.cy };
int result = AdjustWindowRectExForDpi(&rect, style, 0, WINDOW_STYLE_EX, dpi); int result = AdjustWindowRectExForDpiCompat(&rect, style, 0, WINDOW_STYLE_EX, dpi);
if (result == 0) TRACELOG(LOG_ERROR, "WIN32: Failed to adjust window rect [ERROR: %lu]", GetLastError()); if (result == 0) TRACELOG(LOG_ERROR, "WIN32: Failed to adjust window rect [ERROR: %lu]", GetLastError());
return (SIZE){ rect.right - rect.left, rect.bottom - rect.top }; return (SIZE){ rect.right - rect.left, rect.bottom - rect.top };
@ -510,7 +577,7 @@ static bool UpdateWindowSize(int mode, HWND hwnd, int width, int height, unsigne
} }
// Get size in pixels from points, considering high-dpi // Get size in pixels from points, considering high-dpi
UINT dpi = GetDpiForWindow(hwnd); UINT dpi = GetDpiForWindowCompat(hwnd);
float dpiScale = ((float)dpi)/96.0f; float dpiScale = ((float)dpi)/96.0f;
bool dpiScaling = flags & FLAG_WINDOW_HIGHDPI; bool dpiScaling = flags & FLAG_WINDOW_HIGHDPI;
SIZE desiredSize = { SIZE desiredSize = {
@ -965,7 +1032,11 @@ void SetWindowOpacity(float opacity)
// Set window focused // Set window focused
void SetWindowFocused(void) void SetWindowFocused(void)
{ {
TRACELOG(LOG_WARNING, "SetWindowFocused not implemented"); BringWindowToTop(platform.hwnd);
SetForegroundWindow(platform.hwnd);
SetFocus(platform.hwnd);
if (GetForegroundWindow() != platform.hwnd) TRACELOG(LOG_WARNING, "WIN32: WINDOW: Failed to set window focus");
} }
// Get native window handle // Get native window handle
@ -1060,7 +1131,7 @@ Vector2 GetWindowPosition(void)
// Get window scale DPI factor for current monitor // Get window scale DPI factor for current monitor
Vector2 GetWindowScaleDPI(void) Vector2 GetWindowScaleDPI(void)
{ {
float scale = ((float)GetDpiForWindow(platform.hwnd))/96.0f; float scale = ((float)GetDpiForWindowCompat(platform.hwnd))/96.0f;
return (Vector2){ scale, scale }; return (Vector2){ scale, scale };
} }
@ -1069,10 +1140,7 @@ void SetClipboardText(const char *text)
{ {
if (OpenClipboard(platform.hwnd)) if (OpenClipboard(platform.hwnd))
{ {
if (text == NULL) if (text == NULL) EmptyClipboard();
{
EmptyClipboard();
}
else else
{ {
size_t length = strlen(text); size_t length = strlen(text);
@ -1105,20 +1173,14 @@ void SetClipboardText(const char *text)
GlobalFree(globalHandle); GlobalFree(globalHandle);
} }
} }
else else GlobalFree(globalHandle);
{
GlobalFree(globalHandle);
}
} }
} }
} }
CloseClipboard(); CloseClipboard();
} }
else else TRACELOG(LOG_WARNING, "Clipboard: Failed to write to clipboard. It may be locked by another program.");
{
TRACELOG(LOG_WARNING, "Clipboard: Failed to write to clipboard. It may be locked by another program.");
}
} }
// Get clipboard text content // Get clipboard text content
@ -1189,7 +1251,6 @@ Image GetClipboardImage(void)
else else
{ {
image = LoadImageFromMemory(".bmp", (const unsigned char*)fileData, (int)dataSize); image = LoadImageFromMemory(".bmp", (const unsigned char*)fileData, (int)dataSize);
RL_FREE(fileData); RL_FREE(fileData);
} }
#else #else
@ -1426,6 +1487,7 @@ void PollInputEvents(void)
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
CORE.Window.resizedLastFrame = false; CORE.Window.resizedLastFrame = false;
CORE.Window.shouldClose = false;
// Process windows messages // Process windows messages
MSG msg = { 0 }; MSG msg = { 0 };
@ -1444,7 +1506,7 @@ void PollInputEvents(void)
// Windows won't send a WM_KEYDOWN event for print screen; check it manually // Windows won't send a WM_KEYDOWN event for print screen; check it manually
bool isPrintScreenDown = ((GetAsyncKeyState(VK_SNAPSHOT) & 0x8000) != 0) && (GetForegroundWindow() == platform.hwnd); bool isPrintScreenDown = ((GetAsyncKeyState(VK_SNAPSHOT) & 0x8000) != 0) && (GetForegroundWindow() == platform.hwnd);
CORE.Input.Keyboard.currentKeyState[KEY_PRINT_SCREEN] = isPrintScreenDown; CORE.Input.Keyboard.currentKeyState[KEY_PRINT_SCREEN] = isPrintScreenDown;
if (isPrintScreenDown && (CORE.Input.Keyboard.exitKey == KEY_PRINT_SCREEN)) CORE.Window.shouldClose = true; if (IsKeyPressed(KEY_PRINT_SCREEN) && (CORE.Input.Keyboard.exitKey == KEY_PRINT_SCREEN)) CORE.Window.shouldClose = true;
} }
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -1642,6 +1704,8 @@ int InitPlatform(void)
CORE.Window.flags = FLAG_WINDOW_HIDDEN | (platform.desiredFlags & FLAG_MASK_NO_UPDATE); CORE.Window.flags = FLAG_WINDOW_HIDDEN | (platform.desiredFlags & FLAG_MASK_NO_UPDATE);
CORE.Window.screenMax.width = 9999; CORE.Window.screenMax.width = 9999;
CORE.Window.screenMax.height = 9999; CORE.Window.screenMax.height = 9999;
LoadWin32FunctionPointers();
/* /*
// TODO: Review SetProcessDpiAwarenessContext() // TODO: Review SetProcessDpiAwarenessContext()
// NOTE: SetProcessDpiAwarenessContext() requires Windows 10, version 1703 and shcore.lib linkage // NOTE: SetProcessDpiAwarenessContext() requires Windows 10, version 1703 and shcore.lib linkage
@ -1764,8 +1828,14 @@ int InitPlatform(void)
CORE.Window.ready = true; CORE.Window.ready = true;
// Activate window to set focus and show taskbar icon if ((platform.desiredFlags & FLAG_WINDOW_HIDDEN) == 0)
ShowWindow(platform.hwnd, SW_SHOWDEFAULT); {
// Show window and taskbar icon, activate unless FLAG_WINDOW_UNFOCUSED is set
bool isUnfocused = ((platform.desiredFlags & FLAG_WINDOW_UNFOCUSED) != 0);
if (isUnfocused) CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
int nCmdShow = (isUnfocused)? SW_SHOWNA : SW_SHOWDEFAULT;
ShowWindow(platform.hwnd, nCmdShow);
}
// Update flags (in case of deferred state change required) // Update flags (in case of deferred state change required)
UpdateFlags(platform.hwnd, platform.desiredFlags, platform.appScreenWidth, platform.appScreenHeight); UpdateFlags(platform.hwnd, platform.desiredFlags, platform.appScreenWidth, platform.appScreenHeight);
@ -1814,16 +1884,12 @@ int InitPlatform(void)
// Close platform // Close platform
void ClosePlatform(void) void ClosePlatform(void)
{ {
// Undo disable cursor if it's locked and unregister raw input devices // Clear required platform data
EnableCursor();
// Calling DestroyWindow invokes a WindowProc event that sets the FLAG_WINDOW_HIDDEN flag to true
// If InitWindow is called again, the hidden flag will be interpreted as something the user requested
// Save the current flags and restore them after
unsigned int savedFlags = CORE.Window.flags;
if (platform.hwnd) if (platform.hwnd)
{ {
// Disable cursor lock (if enabled) and unregister raw input devices
EnableCursor();
// SetWindowIcon uses the same resource for both ICON_BIG and ICON_SMALL // SetWindowIcon uses the same resource for both ICON_BIG and ICON_SMALL
// If this ever changes, this code may have to be updated // If this ever changes, this code may have to be updated
HICON icon = (HICON)SendMessage(platform.hwnd, WM_GETICON, ICON_BIG, (LPARAM)0); HICON icon = (HICON)SendMessage(platform.hwnd, WM_GETICON, ICON_BIG, (LPARAM)0);
@ -1840,14 +1906,13 @@ void ClosePlatform(void)
platform.hwnd = NULL; platform.hwnd = NULL;
} }
// Clear clipboard
if (platform.clipboardText != NULL) if (platform.clipboardText != NULL)
{ {
RL_FREE(platform.clipboardText); RL_FREE(platform.clipboardText);
platform.clipboardText = NULL; platform.clipboardText = NULL;
} }
CORE.Window.flags = savedFlags;
#if SUPPORT_WINMM_HIGHRES_TIMER && !SUPPORT_BUSY_WAIT_LOOP #if SUPPORT_WINMM_HIGHRES_TIMER && !SUPPORT_BUSY_WAIT_LOOP
timeEndPeriod(1); // Restore time period timeEndPeriod(1); // Restore time period
#endif #endif
@ -1921,10 +1986,12 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara
//case WM_QUIT: // Application closing, not related to window //case WM_QUIT: // Application closing, not related to window
case WM_KILLFOCUS: case WM_KILLFOCUS:
{ {
CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
memset(CORE.Input.Keyboard.currentKeyState, 0, sizeof(CORE.Input.Keyboard.currentKeyState)); memset(CORE.Input.Keyboard.currentKeyState, 0, sizeof(CORE.Input.Keyboard.currentKeyState));
memset(CORE.Input.Mouse.currentButtonState, 0, sizeof(CORE.Input.Mouse.currentButtonState)); memset(CORE.Input.Mouse.currentButtonState, 0, sizeof(CORE.Input.Mouse.currentButtonState));
memset(CORE.Input.Touch.currentTouchState, 0, sizeof(CORE.Input.Touch.currentTouchState)); memset(CORE.Input.Touch.currentTouchState, 0, sizeof(CORE.Input.Touch.currentTouchState));
} break; } break;
case WM_SETFOCUS: CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; break;
case WM_SIZING: // Sent to a window that the user is resizing case WM_SIZING: // Sent to a window that the user is resizing
{ {
if (CORE.Window.flags & FLAG_WINDOW_RESIZABLE) if (CORE.Window.flags & FLAG_WINDOW_RESIZABLE)
@ -1973,7 +2040,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara
STYLESTRUCT *ss = (STYLESTRUCT *)lparam; STYLESTRUCT *ss = (STYLESTRUCT *)lparam;
GetStyleChangeFlagOps(CORE.Window.flags, ss, deferredFlags); GetStyleChangeFlagOps(CORE.Window.flags, ss, deferredFlags);
UINT dpi = GetDpiForWindow(hwnd); UINT dpi = GetDpiForWindowCompat(hwnd);
// Get client size (framebuffer inside the window) // Get client size (framebuffer inside the window)
RECT rect = { 0 }; RECT rect = { 0 };
GetClientRect(hwnd, &rect); GetClientRect(hwnd, &rect);
@ -2187,8 +2254,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara
default: TRACELOG(LOG_WARNING, "TODO: Handle ex mouse button UP [wparam=%u]", HIWORD(wparam)); break; default: TRACELOG(LOG_WARNING, "TODO: Handle ex mouse button UP [wparam=%u]", HIWORD(wparam)); break;
} }
} break; } break;
case WM_MOUSEWHEEL: CORE.Input.Mouse.currentWheelMove.y = ((float)GET_WHEEL_DELTA_WPARAM(wparam))/WHEEL_DELTA; break; case WM_MOUSEWHEEL: CORE.Input.Mouse.currentWheelMove.y += ((float)GET_WHEEL_DELTA_WPARAM(wparam))/WHEEL_DELTA; break;
case WM_MOUSEHWHEEL: CORE.Input.Mouse.currentWheelMove.x = ((float)GET_WHEEL_DELTA_WPARAM(wparam))/WHEEL_DELTA; break; // Horizontal wheel movement is inverted to match GLFW
case WM_MOUSEHWHEEL: CORE.Input.Mouse.currentWheelMove.x -= ((float)GET_WHEEL_DELTA_WPARAM(wparam))/WHEEL_DELTA; break;
default: result = DefWindowProcW(hwnd, msg, wparam, lparam); // Message passed directly for execution (default behaviour) default: result = DefWindowProcW(hwnd, msg, wparam, lparam); // Message passed directly for execution (default behaviour)
} }
@ -2260,8 +2328,9 @@ static void HandleKey(WPARAM wparam, LPARAM lparam, char state)
{ {
CORE.Input.Keyboard.currentKeyState[key] = state; CORE.Input.Keyboard.currentKeyState[key] = state;
if (((HIWORD(lparam) & KF_REPEAT) != 0) && (state == 1)) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1; bool isKeyRepeatEvent = ((HIWORD(lparam) & KF_REPEAT) != 0);
if ((key == CORE.Input.Keyboard.exitKey) && (state == 1)) CORE.Window.shouldClose = true; if (isKeyRepeatEvent && (state == 1)) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
if (!isKeyRepeatEvent && (key == CORE.Input.Keyboard.exitKey) && (state == 1)) CORE.Window.shouldClose = true;
} }
else TRACELOG(LOG_WARNING, "INPUT: Unknown (or currently unhandled) virtual keycode %d (0x%x)", wparam, wparam); else TRACELOG(LOG_WARNING, "INPUT: Unknown (or currently unhandled) virtual keycode %d (0x%x)", wparam, wparam);
} }
@ -2351,7 +2420,7 @@ static void HandleWindowResize(HWND hwnd, int *width, int *height)
SetupViewport(clientSize.cx, clientSize.cy); SetupViewport(clientSize.cx, clientSize.cy);
CORE.Window.resizedLastFrame = true; CORE.Window.resizedLastFrame = true;
float dpiScale = ((float)GetDpiForWindow(hwnd))/96.0f; float dpiScale = ((float)GetDpiForWindowCompat(hwnd))/96.0f;
bool highdpi = !!(CORE.Window.flags & FLAG_WINDOW_HIGHDPI); bool highdpi = !!(CORE.Window.flags & FLAG_WINDOW_HIGHDPI);
unsigned int screenWidth = highdpi? (unsigned int)(((float)clientSize.cx)/dpiScale) : clientSize.cx; unsigned int screenWidth = highdpi? (unsigned int)(((float)clientSize.cx)/dpiScale) : clientSize.cx;
unsigned int screenHeight = highdpi? (unsigned int)(((float)clientSize.cy)/dpiScale) : clientSize.cy; unsigned int screenHeight = highdpi? (unsigned int)(((float)clientSize.cy)/dpiScale) : clientSize.cy;

View File

@ -1735,7 +1735,6 @@ void ClosePlatform(void)
CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called
// Close the evdev devices // Close the evdev devices
if (platform.mouseFd != -1) if (platform.mouseFd != -1)
{ {
close(platform.mouseFd); close(platform.mouseFd);

View File

@ -554,7 +554,7 @@ int InitPlatform(void)
// Close platform // Close platform
void ClosePlatform(void) void ClosePlatform(void)
{ {
RL_FREE(platform.pixels); RL_FREE(platform.pixels); // Free pixels buffer
} }
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------

View File

@ -1450,6 +1450,7 @@ int InitPlatform(void)
void ClosePlatform(void) void ClosePlatform(void)
{ {
if (platform.pixels != NULL) RL_FREE(platform.pixels); if (platform.pixels != NULL) RL_FREE(platform.pixels);
if (platform.glContext != 0) emscripten_webgl_destroy_context(platform.glContext); if (platform.glContext != 0) emscripten_webgl_destroy_context(platform.glContext);
} }

View File

@ -1,6 +1,6 @@
/********************************************************************************************** /**********************************************************************************************
* *
* raylib v6.2 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) * raylib v6.5-dev - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com)
* *
* FEATURES: * FEATURES:
* - NO external dependencies, all required libraries included with raylib * - NO external dependencies, all required libraries included with raylib
@ -87,9 +87,9 @@
#include <stdarg.h> // Required for: va_list - Only used by TraceLogCallback #include <stdarg.h> // Required for: va_list - Only used by TraceLogCallback
#define RAYLIB_VERSION_MAJOR 6 #define RAYLIB_VERSION_MAJOR 6
#define RAYLIB_VERSION_MINOR 2 #define RAYLIB_VERSION_MINOR 5
#define RAYLIB_VERSION_PATCH 0 #define RAYLIB_VERSION_PATCH 0
#define RAYLIB_VERSION "6.2" #define RAYLIB_VERSION "6.5-dev"
// Function specifiers in case library is build/used as a shared library // Function specifiers in case library is build/used as a shared library
// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll // NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll

View File

@ -379,6 +379,7 @@ typedef struct CoreData {
float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXES]; // Gamepad axes state float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXES]; // Gamepad axes state
} Gamepad; } Gamepad;
} Input; } Input;
struct { struct {
double current; // Current time measure (seconds) double current; // Current time measure (seconds)
@ -646,7 +647,6 @@ void InitWindow(int width, int height, const char *title)
if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title;
// Initialize global input state // Initialize global input state
memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0
CORE.Input.Keyboard.exitKey = KEY_ESCAPE; CORE.Input.Keyboard.exitKey = KEY_ESCAPE;
CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f };
CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW;
@ -708,6 +708,7 @@ void InitWindow(int width, int height, const char *title)
#endif #endif
#endif #endif
// NOTE: Should be already initialized to 0 but just in case
CORE.Time.frameCounter = 0; CORE.Time.frameCounter = 0;
CORE.Window.shouldClose = false; CORE.Window.shouldClose = false;
@ -735,7 +736,9 @@ void CloseWindow(void)
ClosePlatform(); ClosePlatform();
//-------------------------------------------------------------- //--------------------------------------------------------------
CORE.Window.ready = false; // Clear CORE data, in case new window is initialized
CORE = (CoreData){ 0 };
TRACELOG(LOG_INFO, "Window closed successfully"); TRACELOG(LOG_INFO, "Window closed successfully");
} }

View File

@ -512,7 +512,7 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i
if (fileData != NULL) if (fileData != NULL)
{ {
qoi_desc desc = { 0 }; qoi_desc desc = { 0 };
image.data = qoi_decode(fileData, dataSize, &desc, (int) fileData[12]); image.data = qoi_decode(fileData, dataSize, &desc, 0); // NOTE: Using channels from file data
image.width = desc.width; image.width = desc.width;
image.height = desc.height; image.height = desc.height;
image.format = (desc.channels == 4)? PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 : PIXELFORMAT_UNCOMPRESSED_R8G8B8; image.format = (desc.channels == 4)? PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 : PIXELFORMAT_UNCOMPRESSED_R8G8B8;
@ -540,7 +540,6 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i
image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
image.mipmaps = 1; image.mipmaps = 1;
PEP_FREE(pepData);
pep_free(&p); pep_free(&p);
} }
} }
@ -1269,48 +1268,58 @@ void ImageCrop(Image *image, Rectangle crop)
// Security check to avoid program crash // Security check to avoid program crash
if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
crop.x = floorf(crop.x);
crop.y = floorf(crop.y);
crop.width = floorf(crop.width);
crop.height = floorf(crop.height);
// Security checks to validate crop rectangle // Security checks to validate crop rectangle
if (crop.x < 0) { crop.width += crop.x; crop.x = 0; } if (crop.x < 0) { crop.width += crop.x; crop.x = 0; }
if (crop.y < 0) { crop.height += crop.y; crop.y = 0; } if (crop.y < 0) { crop.height += crop.y; crop.y = 0; }
if ((crop.x + crop.width) > image->width) crop.width = image->width - crop.x; if ((crop.x + crop.width) > image->width) crop.width = image->width - crop.x;
if ((crop.y + crop.height) > image->height) crop.height = image->height - crop.y; if ((crop.y + crop.height) > image->height) crop.height = image->height - crop.y;
if ((crop.x > image->width) || (crop.y > image->height)) if ((crop.x > image->width) || (crop.y > image->height) || (crop.width < 1) || (crop.height < 1))
{ {
TRACELOG(LOG_WARNING, "IMAGE: Failed to crop, rectangle out of bounds"); TRACELOG(LOG_WARNING, "IMAGE: Failed to crop, rectangle out of bounds");
return; return;
} }
int cropX = (int)crop.x;
int cropY = (int)crop.y;
int cropWidth = (int)crop.width;
int cropHeight = (int)crop.height;
if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level"); if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats"); if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
else else
{ {
int bytesPerPixel = GetPixelDataSize(1, 1, image->format); int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
unsigned char *croppedData = (unsigned char *)RL_MALLOC((int)(crop.width*crop.height)*bytesPerPixel); unsigned char *croppedData = (unsigned char *)RL_MALLOC(cropWidth*cropHeight*bytesPerPixel);
// OPTION 1: Move cropped data line-by-line // OPTION 1: Move cropped data line-by-line
for (int y = (int)crop.y, offsetSize = 0; y < (int)(crop.y + crop.height); y++) for (int y = cropY, offsetSize = 0; y < cropY + cropHeight; y++)
{ {
memcpy(croppedData + offsetSize, ((unsigned char *)image->data) + (y*image->width + (int)crop.x)*bytesPerPixel, (int)crop.width*bytesPerPixel); memcpy(croppedData + offsetSize, ((unsigned char *)image->data) + (y*image->width + cropX)*bytesPerPixel, cropWidth*bytesPerPixel);
offsetSize += ((int)crop.width*bytesPerPixel); offsetSize += (cropWidth*bytesPerPixel);
} }
/* /*
// OPTION 2: Move cropped data pixel-by-pixel or byte-by-byte // OPTION 2: Move cropped data pixel-by-pixel or byte-by-byte
for (int y = (int)crop.y; y < (int)(crop.y + crop.height); y++) for (int y = cropY; y < cropY + cropHeight; y++)
{ {
for (int x = (int)crop.x; x < (int)(crop.x + crop.width); x++) for (int x = cropX; x < cropX + cropWidth; x++)
{ {
//memcpy(croppedData + ((y - (int)crop.y)*(int)crop.width + (x - (int)crop.x))*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + x)*bytesPerPixel, bytesPerPixel); //memcpy(croppedData + ((y - cropY)*cropWidth + (x - cropX))*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + x)*bytesPerPixel, bytesPerPixel);
for (int i = 0; i < bytesPerPixel; i++) croppedData[((y - (int)crop.y)*(int)crop.width + (x - (int)crop.x))*bytesPerPixel + i] = ((unsigned char *)image->data)[(y*image->width + x)*bytesPerPixel + i]; for (int i = 0; i < bytesPerPixel; i++) croppedData[((y - cropY)*cropWidth + (x - cropX))*bytesPerPixel + i] = ((unsigned char *)image->data)[(y*image->width + x)*bytesPerPixel + i];
} }
} }
*/ */
RL_FREE(image->data); RL_FREE(image->data);
image->data = croppedData; image->data = croppedData;
image->width = (int)crop.width; image->width = cropWidth;
image->height = (int)crop.height; image->height = cropHeight;
} }
} }

View File

@ -15,7 +15,7 @@
{ {
"name": "RAYLIB_VERSION_MINOR", "name": "RAYLIB_VERSION_MINOR",
"type": "INT", "type": "INT",
"value": 2, "value": 5,
"description": "" "description": ""
}, },
{ {
@ -27,7 +27,7 @@
{ {
"name": "RAYLIB_VERSION", "name": "RAYLIB_VERSION",
"type": "STRING", "type": "STRING",
"value": "6.2", "value": "6.5-dev",
"description": "" "description": ""
}, },
{ {

View File

@ -15,7 +15,7 @@ return {
{ {
name = "RAYLIB_VERSION_MINOR", name = "RAYLIB_VERSION_MINOR",
type = "INT", type = "INT",
value = 2, value = 5,
description = "" description = ""
}, },
{ {
@ -27,7 +27,7 @@ return {
{ {
name = "RAYLIB_VERSION", name = "RAYLIB_VERSION",
type = "STRING", type = "STRING",
value = "6.2", value = "6.5-dev",
description = "" description = ""
}, },
{ {

View File

@ -9,7 +9,7 @@
(description "")) (description ""))
((name "RAYLIB_VERSION_MINOR") ((name "RAYLIB_VERSION_MINOR")
(type "INT") (type "INT")
(value 2) (value 5)
(description "")) (description ""))
((name "RAYLIB_VERSION_PATCH") ((name "RAYLIB_VERSION_PATCH")
(type "INT") (type "INT")
@ -17,7 +17,7 @@
(description "")) (description ""))
((name "RAYLIB_VERSION") ((name "RAYLIB_VERSION")
(type "STRING") (type "STRING")
(value "6.2") (value "6.5-dev")
(description "")) (description ""))
((name "__declspec(x)") ((name "__declspec(x)")
(type "MACRO") (type "MACRO")

View File

@ -14,7 +14,7 @@ Define 002: RAYLIB_VERSION_MAJOR
Define 003: RAYLIB_VERSION_MINOR Define 003: RAYLIB_VERSION_MINOR
Name: RAYLIB_VERSION_MINOR Name: RAYLIB_VERSION_MINOR
Type: INT Type: INT
Value: 2 Value: 5
Description: Description:
Define 004: RAYLIB_VERSION_PATCH Define 004: RAYLIB_VERSION_PATCH
Name: RAYLIB_VERSION_PATCH Name: RAYLIB_VERSION_PATCH
@ -24,7 +24,7 @@ Define 004: RAYLIB_VERSION_PATCH
Define 005: RAYLIB_VERSION Define 005: RAYLIB_VERSION
Name: RAYLIB_VERSION Name: RAYLIB_VERSION
Type: STRING Type: STRING
Value: "6.2" Value: "6.5-dev"
Description: Description:
Define 006: __declspec(x) Define 006: __declspec(x)
Name: __declspec(x) Name: __declspec(x)

View File

@ -3,9 +3,9 @@
<Defines count="56"> <Defines count="56">
<Define name="RAYLIB_H" type="GUARD" value="" desc="" /> <Define name="RAYLIB_H" type="GUARD" value="" desc="" />
<Define name="RAYLIB_VERSION_MAJOR" type="INT" value="6" desc="" /> <Define name="RAYLIB_VERSION_MAJOR" type="INT" value="6" desc="" />
<Define name="RAYLIB_VERSION_MINOR" type="INT" value="2" desc="" /> <Define name="RAYLIB_VERSION_MINOR" type="INT" value="5" desc="" />
<Define name="RAYLIB_VERSION_PATCH" type="INT" value="0" desc="" /> <Define name="RAYLIB_VERSION_PATCH" type="INT" value="0" desc="" />
<Define name="RAYLIB_VERSION" type="STRING" value="6.2" desc="" /> <Define name="RAYLIB_VERSION" type="STRING" value="6.5-dev" desc="" />
<Define name="__declspec(x)" type="MACRO" value="__attribute__((x))" desc="" /> <Define name="__declspec(x)" type="MACRO" value="__attribute__((x))" desc="" />
<Define name="RLAPI" type="UNKNOWN" value="__declspec(dllexport)" desc="Building the library as a Win32 shared library (.dll)" /> <Define name="RLAPI" type="UNKNOWN" value="__declspec(dllexport)" desc="Building the library as a Win32 shared library (.dll)" />
<Define name="PI" type="FLOAT" value="3.14159265358979323846" desc="" /> <Define name="PI" type="FLOAT" value="3.14159265358979323846" desc="" />