summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--README.md7
-rw-r--r--build.zig61
-rw-r--r--build.zig.zon7
-rw-r--r--include/RFont.h3239
-rw-r--r--include/RGFW.h16191
-rw-r--r--include/RSGL.h1926
-rw-r--r--include/RSGL_gl.h1013
-rw-r--r--include/stb_image.h7988
-rw-r--r--rsgl.zig411
-rw-r--r--shell.nix12
11 files changed, 30857 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f89eb5b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+zig-out
+.zig-cache \ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7133cfe
--- /dev/null
+++ b/README.md
@@ -0,0 +1,7 @@
+# zig-R*
+
+zig-R* (ausgesprochen zig-rstar) ist eine Sammlung der [ColleagueRiley](https://github.com/ColleagueRiley)s fantastischen single-header libraries.
+
+- [RGFW.h](https://github.com/ColleagueRiley/RGFW)
+- [RSGL.h](https://github.com/ColleagueRiley/RSGL)
+- [RFont.h](https://github.com/ColleagueRiley/RFont) \ No newline at end of file
diff --git a/build.zig b/build.zig
new file mode 100644
index 0000000..96808aa
--- /dev/null
+++ b/build.zig
@@ -0,0 +1,61 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) !void {
+ const opengl = b.option(bool, "opengl", "Exposes OpenGL helper functions in RGFW.h and links OpenGL. Default: true") orelse true;
+ const x11 = b.option(bool, "x11", "X11 backend. Default: true") orelse true;
+
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+ var macros = std.ArrayList([2][]const u8).empty;
+ defer macros.deinit(b.allocator);
+ var syslibs = std.ArrayList(struct {[]const u8, std.Build.Module.LinkSystemLibraryOptions}).empty;
+ defer syslibs.deinit(b.allocator);
+
+ try macros.append(b.allocator, [2][]const u8{"RSGL_RFONT", ""});
+
+ if (target.result.os.tag == std.Target.Os.Tag.linux or target.result.os.tag == std.Target.Os.Tag.macos)
+ try macros.append(b.allocator, [2][]const u8{"RGFW_UNIX", ""});
+
+ if (x11) {
+ try macros.append(b.allocator, [2][]const u8{"RGFW_X11", ""});
+ try syslibs.append(b.allocator, struct {[]const u8, std.Build.Module.LinkSystemLibraryOptions}{"X11", .{}});
+ try syslibs.append(b.allocator, struct {[]const u8, std.Build.Module.LinkSystemLibraryOptions}{"Xrandr", .{}});
+ } else {
+ try macros.append(b.allocator, [2][]const u8{"RGFW_NO_X11", ""});
+ }
+ if (opengl) {
+ try macros.append(b.allocator, [2][]const u8{"RGFW_OPENGL", ""});
+ try syslibs.append(b.allocator, struct {[]const u8, std.Build.Module.LinkSystemLibraryOptions}{"GL", .{}});
+ } else {
+ std.debug.panic("no backend other than OpenGL supported by RSGL.h", .{});
+ }
+
+ const rgfw_raw_c = b.addTranslateC(.{
+ .root_source_file = b.path("include/RGFW.h"),
+ .target = target,
+ .optimize = optimize,
+ });
+ for (macros.items) |macro| {rgfw_raw_c.defineCMacro(macro[0], macro[1]);}
+ _ = rgfw_raw_c.addModule("rgfw_raw");
+
+ const rsgl_raw_c = b.addTranslateC(.{
+ .root_source_file = b.path("include/RSGL_gl.h"),
+ .target = target,
+ .optimize = optimize,
+ });
+ for (macros.items) |macro| {rsgl_raw_c.defineCMacro(macro[0], macro[1]);}
+ const rsgl_raw_mod = rsgl_raw_c.addModule("rsgl_raw");
+
+ const rsgl_mod = b.addModule("rsgl", .{
+ .root_source_file = b.path("rsgl.zig"),
+ .target = target,
+ .optimize = optimize,
+ .imports = &.{
+ .{.name = "rsgl_raw", .module = rsgl_raw_mod},
+ },
+ });
+ rsgl_mod.addCMacro("RSGL_IMPLEMENTATION", "");
+ rsgl_mod.addIncludePath(b.path("include"));
+ rsgl_mod.addCSourceFile(.{.file = b.addWriteFiles().add("rsgl_gl.c", "#include <RSGL_gl.h>"),});
+ for (syslibs.items) |lib| {rsgl_mod.linkSystemLibrary(lib[0], lib[1]);}
+} \ No newline at end of file
diff --git a/build.zig.zon b/build.zig.zon
new file mode 100644
index 0000000..d16d543
--- /dev/null
+++ b/build.zig.zon
@@ -0,0 +1,7 @@
+.{
+ .name = .zig_rstar,
+ .version = "0.0.1",
+ .minimum_zig_version = "0.16.0",
+ .paths = .{""},
+ .fingerprint = 0x9464189b61d132a7,
+}
diff --git a/include/RFont.h b/include/RFont.h
new file mode 100644
index 0000000..6d4b511
--- /dev/null
+++ b/include/RFont.h
@@ -0,0 +1,3239 @@
+/*
+* Copyright (c) 2021-25 ColleagueRiley ColleagueRiley@gmail.com
+*
+* 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.
+*
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following r estrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked as such, and must not be
+* misrepresented as being the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*
+*
+*/
+
+/*
+preprocessor args
+
+make sure
+
+** #define RFONT_IMPLEMENTATION ** - include function defines
+
+is in exactly one of your files or arguments
+
+#define RFONT_INT_DEFINED - int types are already defined
+#define RFONT_C89 - uses cints instead of stdint.h and __inline instead of inline
+#define RFONT_INLINE x - set your own inline
+
+#define RFONT_NO_STDIO - do not include stdio.h
+#define RFONT_EXTERNAL_STB - load stb_truetype from stb_truetype.h instead of using the internal version
+#define RFONT_EXTERNAL_STB_IMPLEMENTATION - the same as RFONT_EXTERNAL_STB but doesn't define STB_TRUETYPE_IMPLEMENTATION
+-- NOTE: By default, opengl 3.3 vbos are used for rendering --
+*/
+
+/*
+credits :
+
+stb_truetype.h - a dependency for RFont, most of (a slightly motified version of) stb_truetype.h is included directly into RFont.h
+http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ - UTF-8 decoding function
+fontstash - fontstash was used as a refference for some parts
+*/
+
+/*
+
+... = [add code here]
+
+BASIC TEMPLATE :
+#define RFONT_IMPLEMENTATION
+#include "RFont.h"
+
+...
+
+int main () {
+ ...
+
+ RFont_renderer renderer = ...;
+ RFont_renderer_init(&renderer);
+ RFont_font* font = RFont_font_init(&renderer, "font.ttf", 20, 500, 500);
+
+ i32 w = ...;
+ i32 h = ...;
+
+ while (1) {
+ ...
+ RFont_renderer_set_framebuffer(&renderer, (u32)w, (u32)h);
+ RFont_renderer_set_color(&renderer, 0.0f, 1.0f, 0, 1.0f);
+ RFont_draw_text(&renderer, font, "text", 100, 100, 20);
+ ...
+ }
+
+ RFont_font_free(&renderer, font);
+ RFont_renderer_free(&renderer);
+ ...
+}
+*/
+
+#ifndef RFONT_INLINE
+ #ifdef RFONT_C89
+ #define RFONT_INLINE
+ #else
+ #define RFONT_INLINE inline
+ #endif
+#endif
+
+#ifndef RFONT_API
+ #ifdef RFONT_STATIC
+ #define RFONT_API static
+ #else
+ #define RFONT_API extern RFONT_INLINE
+ #endif
+#endif
+
+
+#ifndef RFONT_NO_STDIO
+#include <stdio.h>
+#endif
+
+#ifndef RFONT_MALLOC
+#include <stdlib.h>
+#define RFONT_MALLOC malloc
+#define RFONT_FREE free
+#endif
+
+#if !defined(RFONT_MEMCPY) || !defined(RFONT_MEMSET)
+ #include <string.h>
+#endif
+
+#ifndef RFONT_MEMSET
+ #define RFONT_MEMSET(ptr, value, num) memset(ptr, value, num)
+#endif
+
+#ifndef RFONT_MEMCPY
+ #define RFONT_MEMCPY(dist, src, len) memcpy(dist, src, len)
+#endif
+
+#ifndef RFONT_ASSERT
+ #include <assert.h>
+ #define RFONT_ASSERT(x) assert(x)
+#endif
+
+#if !defined(RFONT_FLOOR) || !defined(RFONT_CEIL) || !defined(RFONT_FABS) || !defined(RFONT_SQRT)
+ #include <math.h>
+#endif
+
+#ifndef RFONT_FLOOR
+ #define RFONT_FLOOR(x) floor(x)
+#endif
+
+#ifndef RFONT_CEIL
+ #define RFONT_CEIL(x) ceil(x)
+#endif
+
+#ifndef RFONT_FABS
+ #define RFONT_FABS(x) fabs(x)
+#endif
+
+#ifndef RFONT_SQRT
+ #define RFONT_SQRT(x) sqrt(x)
+#endif
+
+#if defined(__STDC__) && !defined(__STDC_VERSION__)
+ #define RFONT_C89
+#endif
+
+#if !defined(RFONT_INT_DEFINED)
+ #define RFONT_INT_DEFINED
+ #if defined(_MSC_VER) || defined(__SYMBIAN32__) || defined(RFONT_C89)
+ typedef unsigned char u8;
+ typedef signed char i8;
+ typedef unsigned short u16;
+ typedef signed short i16;
+ typedef unsigned int u32;
+ typedef signed int i32;
+ typedef unsigned long u64;
+ typedef signed long i64;
+ #else
+ #include <stdint.h>
+
+ typedef uint8_t u8;
+ typedef int8_t i8;
+ typedef uint16_t u16;
+ typedef int16_t i16;
+ typedef uint32_t u32;
+ typedef int32_t i32;
+ typedef uint64_t u64;
+ typedef int64_t i64;
+ #endif
+#endif
+
+#if !defined(b8)
+ typedef u8 b8;
+#endif
+
+/*
+You can define these yourself if
+you want to change anything
+*/
+
+#ifndef RFont_texture
+typedef size_t RFont_texture;
+#endif
+
+#ifndef RFont_surface
+typedef void* RFont_surface;
+#endif
+
+#ifndef RFONT_MAX_GLYPHS
+#define RFONT_MAX_GLYPHS 256
+#endif
+
+#ifndef RFONT_INIT_VERTS
+#define RFONT_INIT_VERTS 20 * RFONT_MAX_GLYPHS
+#endif
+
+#ifndef RFONT_UNUSED
+#define RFONT_UNUSED(x) (void) (x);
+#endif
+
+#ifndef RFONT_RENDERER_H
+#define RFONT_RENDERER_H
+
+typedef struct RFont_render_data {
+ float* verts;
+ float* tcoords;
+ u16* elements;
+
+ RFont_texture atlas;
+ size_t nverts;
+ size_t nelements;
+} RFont_render_data;
+
+typedef struct RFont_renderer_proc {
+ size_t (*size)(void); /*!< get the size of the renderer context */
+ void (*initPtr)(void* ctx); /* any initalizations the renderer needs to do */
+ RFont_texture (*create_atlas)(void* ctx, u32 atlasWidth, u32 atlasHeight); /* create a bitmap texture based on the given size */
+ void (*free_atlas)(void* ctx, RFont_texture atlas);
+ void (*bitmap_to_atlas)(void* ctx, RFont_texture atlas, u32 atlasWidth, u32 atlasHeight, u32 maxHeight, u8* bitmap, float w, float h, float* x, float* y); /* add the given bitmap to the texture based on the given coords and size data */
+ void (*render)(void* ctx, const RFont_render_data* data); /* render the text, using the vertices, atlas texture, and texture coords given. */
+ void (*set_framebuffer)(void* ctx, u32 weight, u32 height); /*!< set the frame buffer size (for ortho, for example) */
+ void (*set_color)(void* ctx, float r, float g, float b, float a); /*!< set the current rendering color */
+ void (*set_surface)(void* ctx, RFont_surface surface);
+ void (*freePtr)(void* ctx); /* free any memory the renderer might need to free */
+} RFont_renderer_proc;
+
+typedef struct RFont_renderer {
+ void* ctx; /*!< source renderer data */
+ RFont_renderer_proc proc;
+} RFont_renderer;
+
+typedef struct RFont_font RFont_font;
+
+#endif /* RFONT_RENDERER_H */
+
+#ifndef RFONT_H
+#define RFONT_H
+
+RFONT_API size_t RFont_renderer_size(RFont_renderer* renderer);
+
+RFONT_API RFont_renderer* RFont_renderer_init(RFont_renderer_proc proc);
+RFONT_API void RFont_renderer_initPtr(RFont_renderer_proc proc, void* ptr, RFont_renderer* renderer);
+
+RFONT_API void RFont_renderer_set_framebuffer(RFont_renderer* renderer, u32 w, u32 h);
+RFONT_API void RFont_renderer_set_surface(RFont_renderer* renderer, RFont_surface surface);
+RFONT_API void RFont_renderer_set_color(RFont_renderer* renderer, float r, float g, float b, float a);
+
+RFONT_API void RFont_renderer_free(RFont_renderer* renderer);
+RFONT_API void RFont_renderer_freePtr(RFont_renderer* renderer);
+
+#define RFONT_GET_FONT_WIDTH(fontHeight) RFONT_MAX_GLYPHS * fontHeight
+
+typedef struct {
+ u32 codepoint; /* the character (for checking) */
+ size_t size; /* the size of the glyph */
+ i32 x, x2, y, y2; /* coords of the character on the texture */
+ RFont_font* font; /* the font that the glyph belongs to */
+
+ /* source glyph data */
+ i32 src;
+ float w, h, x1, y1, advance;
+} RFont_glyph;
+
+typedef struct RFont_src RFont_src;
+
+struct RFont_font {
+ RFont_src* src; /* source stb font info */
+ float fheight; /* source font height */
+ float descent; /* font descent */
+ float numOfLongHorMetrics;
+ float space_adv;
+ u32 maxHeight;
+
+ RFont_glyph glyphs[RFONT_MAX_GLYPHS]; /* glyphs */
+ size_t glyph_len;
+
+ RFont_texture atlas; /* atlas texture */
+ size_t atlasWidth, atlasHeight;
+ float atlasX, atlasY; /* the current position inside the atlas */
+
+ float verts[RFONT_INIT_VERTS * 3];
+ float tcoords[RFONT_INIT_VERTS * 2];
+ u16 elements[RFONT_INIT_VERTS * 6];
+};
+
+
+/**
+ * @brief Converts a codepoint to a utf8 string.
+ * @param codepoint The codepoint to convert to utf8.
+ * @return The utf8 string.
+*/
+RFONT_API char* RFont_codepoint_to_utf8(u32 codepoint);
+
+#ifndef RFONT_NO_STDIO
+/**
+ * @brief Init font stucture with a TTF file path.
+ * @param font_name The TTF file path.
+ * @param atlasWidth The width of the atlas texture.
+ * @param atlasHeight The height of the atlas texture. (This should == the max text size)
+ * @return The `RFont_font` created using the TTF file data.
+*/
+RFONT_API RFont_font* RFont_font_init(RFont_renderer* renderer, const char* font_name, u32 maxHeight, size_t atlasWidth, size_t atlasHeight);
+/**
+ * @brief Init a given font stucture with a TTF file path.
+ * @param font_name The TTF file path.
+ * @param atlasWidth The width of the atlas texture.
+ * @param atlasHeight The height of the atlas texture. (This should == the max text size)
+ * @pram ptr Pointer to the given font structure
+ * @return returns the same pointer or NULL if the font failed to load
+*/
+RFONT_API RFont_font* RFont_font_init_ptr(RFont_renderer* renderer, const char* font_name, u32 maxHeight, size_t atlasWidth, size_t atlasHeight, RFont_font* font);
+
+#endif
+
+/**
+ * @brief Init font stucture with raw TTF data.
+ * @param font_data The raw TTF data.
+ * @param atlasWidth The width of the atlas texture.
+ * @param atlasHeight The height of the atlas texture. (This should == the max text size)
+ * @return The `RFont_font` created from the data.
+*/
+RFONT_API RFont_font* RFont_font_init_data(RFont_renderer* renderer, u8* font_data, u32 maxHeight, size_t atlasWidth, size_t atlasHeight);
+
+/**
+ * @brief Init a given font stucture with raw TTF data.
+ * @param font_data The raw TTF data.
+ * @param atlasWidth The width of the atlas texture.
+ * @param atlasHeight The height of the atlas texture. (This should == the max text size)
+ * @return The `RFont_font` created from the data.
+ * @return returns the same pointer or NULL if the font failed to load
+*/
+RFONT_API RFont_font* RFont_font_init_data_ptr(RFont_renderer* renderer, u8* font_data, u32 maxHeight, size_t atlasWidth, size_t atlasHeight, RFont_font* ptr);
+
+/**
+ * @brief Free data from the font stucture, including the stucture itself
+ * @param font The font stucture to free
+*/
+RFONT_API void RFont_font_free(RFont_renderer* renderer, RFont_font* font);
+
+/**
+ * @brief Free data from the font stucture only (not including the stucture)
+ * @param font The strucutre with the font data to free
+*/
+RFONT_API void RFont_font_free_ptr(RFont_renderer* renderer, RFont_font* font);
+
+typedef RFont_glyph (*RFont_glyph_fallback_callback)(RFont_renderer* renderer, RFont_font* font, u32 codepoint, size_t size);
+RFont_glyph_fallback_callback RFont_set_glyph_fallback_callback(RFont_glyph_fallback_callback callback);
+
+/**
+ * @brief Add a character to the font's atlas.
+ * @param font The font to use.
+ * @param ch The character to add to the atlas.
+ * @param size The size of the character.
+ * @return The `RFont_glyph` created from the data and added to the atlas.
+*/
+RFONT_API RFont_glyph RFont_font_add_char(RFont_renderer* renderer,RFont_font* font, char ch, size_t size);
+
+/**
+ * @brief Add a codepoint to the font's atlas.
+ * @param font The font to use.
+ * @param codepoint The codepoint to add to the atlas.
+ * @param size The size of the character.
+ * @return The `RFont_glyph` created from the data and added to the atlas.
+*/
+RFONT_API RFont_glyph RFont_font_add_codepoint(RFont_renderer* renderer, RFont_font* font, u32 codepoint, size_t size);
+
+/**
+ * @brief Add a codepoint to the font's atlas.
+ * @param font The font to use.
+ * @param codepoint The codepoint to add to the atlas.
+ * @param size The size of the character.
+ * @param fallback If the fallback function should not be called.
+ * @return The `RFont_glyph` created from the data and added to the atlas.
+*/
+RFONT_API RFont_glyph RFont_font_add_codepoint_ex(RFont_renderer* renderer, RFont_font* font, u32 codepoint, size_t size, b8 fallback);
+
+/**
+ * @brief Add a string to the font's atlas.
+ * @param font The font to use.
+ * @param ch The character to add to the atlas.
+ * @param sizes The supported sizes of the character.
+ * @param sizeLen length of the size array
+*/
+RFONT_API void RFont_font_add_string(RFont_renderer* renderer, RFont_font* font, const char* string, size_t* sizes, size_t sizeLen);
+
+/**
+ * @brief Add a string to the font's atlas based on a given string length.
+ * @param font The font to use.
+ * @param ch The character to add to the atlas.
+ * @param strLen length of the string
+ * @param sizes The supported sizes of the character.
+ * @param sizeLen length of the size array
+*/
+RFONT_API void RFont_font_add_string_len(RFont_renderer* renderer, RFont_font* font, const char* string, size_t strLen, size_t* sizes, size_t sizeLen);
+
+/**
+ * @brief Get the area of the text based on the size using the font.
+ * @param font The font stucture to use for drawing
+ * @param text The string to draw
+ * @param size The size of the text
+ * @param [OUTPUT] the output width
+ * @param [OUTPUT] the output height
+*/
+RFONT_API void RFont_text_area(RFont_renderer* renderer, RFont_font* font, const char* text, u32 size, u32* w, u32* h);
+
+/**
+ * @brief Get the area of the text based on the size using the font, using a given length.
+ * @param font The font stucture to use for drawing
+ * @param text The string to draw
+ * @param size The size of the text
+ * @param spacing The spacing of the text
+ * @param [OUTPUT] the output width
+ * @param [OUTPUT] the output height
+*/
+RFONT_API void RFont_text_area_spacing(RFont_renderer* renderer, RFont_font* font, const char* text, float spacing, u32 size, u32* w, u32* h);
+
+/**
+ * @brief Get the area of the text based on the size using the font, using a given length.
+ * @param font The font stucture to use for drawing
+ * @param text The string to draw
+ * @param len The length of the string
+ * @param size The size of the text
+ * @param stopNL the number of \n s until it stops (0 = don't stop until the end)
+ * @param spacing The spacing of the text
+ * @param [OUTPUT] the output width
+ * @param [OUTPUT] the output height
+*/
+RFONT_API void RFont_text_area_len(RFont_renderer* renderer, RFont_font* font, const char* text, size_t len, u32 size, size_t stopNL, float spacing, u32* w, u32* h);
+
+/**
+ * @brief Draw a text string using the font.
+ * @param font The font stucture to use for drawing
+ * @param text The string to draw
+ * @param x The x position of the text
+ * @param y The y position of the text
+ * @param size The size of the text
+ * @return the number of verts rendered
+*/
+RFONT_API size_t RFont_draw_text(RFont_renderer* renderer, RFont_font* font, const char* text, float x, float y, u32 size);
+
+/**
+ * @brief Draw a text string using the font and a given spacing.
+ * @param font The font stucture to use for drawing
+ * @param text The string to draw
+ * @param x The x position of the text
+ * @param y The y position of the text
+ * @param size The size of the text
+ * @param spacing The spacing of the text
+ * @return the number of verts rendered
+*/
+RFONT_API size_t RFont_draw_text_spacing(RFont_renderer* renderer, RFont_font* font, const char* text, float x, float y, u32 size, float spacing);
+
+/**
+ * @brief Draw a text string using the font using a given length and a given spacing.
+ * @param font The font stucture to use for drawing
+ * @param text The string to draw
+ * @param len The length of the string
+ * @param x The x position of the text
+ * @param y The y position of the text
+ * @param size The size of the text
+ * @param spacing The spacing of the text
+ * @return the number of verts rendered
+*/
+RFONT_API size_t RFont_draw_text_len(RFont_renderer* renderer, RFont_font* font, const char* text, size_t len, float x, float y, u32 size, float spacing);
+#endif /* RFONT_H */
+
+#ifdef RFONT_IMPLEMENTATION
+
+#ifndef RFONT_GET_TEXPOSX
+#define RFONT_GET_TEXPOSX(x, w) (float)((float)(x) / (float)(w))
+#define RFONT_GET_TEXPOSY(y, h) (float)((float)(y) / (float)(h))
+#endif
+
+size_t RFont_renderer_size(RFont_renderer* renderer) {
+ size_t size = 0;
+ if (renderer->proc.size)
+ size = renderer->proc.size();
+ return size;
+}
+
+RFont_renderer* RFont_renderer_init(RFont_renderer_proc proc) {
+ void* ptr = NULL;
+ size_t size;
+
+ RFont_renderer* renderer = (RFont_renderer*)RFONT_MALLOC(sizeof(RFont_renderer));
+ renderer->proc = proc;
+
+ size = RFont_renderer_size(renderer);
+ if (size) ptr = RFONT_MALLOC(size);
+ RFont_renderer_initPtr(proc, ptr, renderer);
+ return renderer;
+}
+
+void RFont_renderer_initPtr(RFont_renderer_proc proc, void* ptr, RFont_renderer* renderer) {
+ renderer->ctx = ptr;
+ renderer->proc = proc;
+ if (renderer->proc.initPtr)
+ renderer->proc.initPtr(renderer->ctx);
+}
+
+void RFont_renderer_set_framebuffer(RFont_renderer* renderer, u32 w, u32 h) {
+ if (renderer->proc.set_framebuffer)
+ renderer->proc.set_framebuffer(renderer->ctx, w, h);
+}
+
+void RFont_renderer_set_surface(RFont_renderer* renderer, RFont_surface surface) {
+ if (renderer->proc.set_surface)
+ renderer->proc.set_surface(renderer, surface);
+}
+
+void RFont_renderer_set_color(RFont_renderer* renderer, float r, float g, float b, float a) {
+ if (renderer->proc.set_color)
+ renderer->proc.set_color(renderer->ctx, r, g, b, a);
+}
+
+void RFont_renderer_free(RFont_renderer* renderer) {
+ RFont_renderer_freePtr(renderer);
+ if (renderer->ctx) RFONT_FREE(renderer->ctx);
+ RFONT_FREE(renderer);
+}
+
+void RFont_renderer_freePtr(RFont_renderer* renderer) {
+ if (renderer->proc.freePtr)
+ renderer->proc.freePtr(renderer->ctx);
+}
+
+#define RFONT_CHAR(p, index) (((char*)p)[index])
+#define RFONT_BYTE(p, index) (((u8*)p)[index])
+#define RFONT_SHORT(arr, index) (i16)((i16)((u8*)arr)[(size_t)(index)]*256 + (i16)(((u8*)arr)[(size_t)(index) + 1]))
+#define RFONT_USHORT(arr, index) (u16)((u16)((u8*)arr)[(size_t)(index)]*256 + (u16)(((u8*)arr)[(size_t)(index) + 1]))
+#define RFONT_ULONG(arr, index) (u32)((u32)(((u8*)arr)[(size_t)(index)]<<24) + (u32)(((u8*)arr)[(size_t)(index) + 1]<<16) + (u32)(((u8*)arr)[(size_t)(index) + 2]<<8) + (u32)(((u8*)arr))[(size_t)(index) + 3])
+
+/*
+stb defines required by RFont
+
+you probably don't care about this part if you're reading just the RFont code
+*/
+
+#ifndef RFONT_EXTERNAL_STB
+typedef struct {
+ unsigned char *data;
+ int cursor;
+ int size;
+} rstbtt__buf;
+
+typedef struct rstbtt_fontinfo rstbtt_fontinfo;
+
+struct rstbtt_fontinfo {
+ unsigned char * data; /* pointer to .ttf file */
+ int fontstart; /* offset of start of font */
+
+ int numGlyphs; /* number of glyphs, needed for range checking */
+
+ int loca,head,glyf,hhea,hmtx,kern,gpos,svg; /* table locations as offset from start of .ttf */
+ int index_map; /* a cmap mapping for our chosen character encoding */
+ int indexToLocFormat; /* format needed to map from glyph index to glyph */
+
+ rstbtt__buf cff; /* cff font data */
+ rstbtt__buf charstrings; /* the charstring index */
+ rstbtt__buf gsubrs; /* global charstring subroutines index */
+ rstbtt__buf subrs; /* private charstring subroutines index */
+ rstbtt__buf fontdicts; /* array of font dicts */
+ rstbtt__buf fdselect; /* map from glyph to fontdict */
+};
+
+RFONT_API int rstbtt_InitFont(rstbtt_fontinfo *info, const unsigned char *data, int offset);
+
+RFONT_API unsigned char* rstbtt_GetGlyphBitmapSubpixel(const rstbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff);
+
+RFONT_API int rstbtt_FindGlyphIndex(const rstbtt_fontinfo *info, int unicode_codepoint);
+
+RFONT_API int rstbtt_GetGlyphKernAdvance(const rstbtt_fontinfo *info, int glyph1, int glyph2);
+RFONT_API int rstbtt_GetGlyphBox(const rstbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
+#else
+#ifdef RFONT_EXTERNAL_STB_IMPLEMENTATION
+ #define RFONT_EXTERNAL_STB
+#endif
+
+#ifndef RFONT_EXTERNAL_STB_IMPLEMENTATION
+#define STB_TRUETYPE_IMPLEMENTATION
+#endif
+#include "stb_truetype.h"
+
+typedef struct stbtt_fontinfo rstbtt_fontinfo;
+
+#define rstbtt_InitFont stbtt_InitFont
+#define rstbtt_GetGlyphBitmapSubpixel stbtt_GetGlyphBitmapSubpixel
+#define rstbtt_FindGlyphIndex stbtt_FindGlyphIndex
+#define rstbtt_GetGlyphKernAdvance stbtt_GetGlyphKernAdvance
+#define rstbtt_GetGlyphBox stbtt_GetGlyphBox
+#endif /* RFONT_EXTERNAL_STB */
+
+struct RFont_src {
+ rstbtt_fontinfo info;
+};
+
+/*
+END of stb defines required by RFont
+
+you probably care about this part
+*/
+
+#ifndef RFONT_NO_STDIO
+char* RFont_read_file(const char* font_name) {
+ size_t size, out;
+ char* ttf_buffer;
+ FILE* ttf_file = fopen(font_name, "rb");
+
+ if (ttf_file == NULL) return NULL;
+
+ fseek(ttf_file, 0U, SEEK_END);
+ size = (size_t)ftell(ttf_file);
+ if (size <= 0) return NULL;
+
+ ttf_buffer = (char*)RFONT_MALLOC(sizeof(char) * (size_t)size);
+ fseek(ttf_file, 0U, SEEK_SET);
+
+ out = fread(ttf_buffer, 1, (size_t)size, ttf_file);
+ RFONT_UNUSED(out);
+
+ return ttf_buffer;
+}
+
+RFont_font* RFont_font_init(RFont_renderer* renderer, const char* font_name, u32 maxHeight, size_t atlasWidth, size_t atlasHeight) {
+ char* ttf_buffer = RFont_read_file(font_name);
+ RFont_font* font = RFont_font_init_data(renderer, (u8*)ttf_buffer, maxHeight, atlasWidth, atlasHeight);
+ return font;
+}
+
+RFont_font* RFont_font_init_ptr(RFont_renderer* renderer, const char* font_name, u32 maxHeight, size_t atlasWidth, size_t atlasHeight, RFont_font* ptr) {
+ char* ttf_buffer = RFont_read_file(font_name);
+ RFont_font* font = RFont_font_init_data_ptr(renderer, (u8*)ttf_buffer, maxHeight, atlasWidth, atlasHeight, ptr);
+ return font;
+}
+#endif
+
+RFont_font* RFont_font_init_data(RFont_renderer* renderer, u8* font_data, u32 maxHeight, size_t atlasWidth, size_t atlasHeight) {
+ RFont_font* font = (RFont_font*)RFONT_MALLOC(sizeof(RFont_font));
+ return RFont_font_init_data_ptr(renderer, font_data, maxHeight, atlasWidth, atlasHeight, font);
+}
+
+RFont_font* RFont_font_init_data_ptr(RFont_renderer* renderer, u8* font_data, u32 maxHeight, size_t atlasWidth, size_t atlasHeight, RFont_font* font) {
+ u16 index = 0;
+ u16 vert_index = 0;
+
+ i32 space_codepoint = 0;
+ font->src = (RFont_src*)RFONT_MALLOC(sizeof(RFont_src));
+ font->atlasWidth = atlasWidth;
+ font->atlasHeight = atlasHeight;
+ font->maxHeight = maxHeight;
+
+ rstbtt_InitFont(&font->src->info, font_data, 0);
+
+ font->fheight = RFONT_SHORT(font->src->info.data, font->src->info.hhea + 4) - RFONT_SHORT(font->src->info.data, font->src->info.hhea + 6);
+ font->descent = RFONT_SHORT(font->src->info.data, font->src->info.hhea + 6);
+
+ font->numOfLongHorMetrics = RFONT_USHORT(font->src->info.data, font->src->info.hhea + 34);
+
+
+ space_codepoint = rstbtt_FindGlyphIndex(&font->src->info, (int)' ');
+ if (' ' < font->numOfLongHorMetrics)
+ font->space_adv = RFONT_SHORT(font->src->info.data, font->src->info.hmtx + 4 * space_codepoint);
+ else
+ font->space_adv = RFONT_SHORT(font->src->info.data, font->src->info.hmtx + 4 * (i32)(font->numOfLongHorMetrics - 1));
+
+ if (renderer->proc.create_atlas)
+ font->atlas = renderer->proc.create_atlas(renderer->ctx, (u32)atlasWidth, (u32)atlasHeight);
+
+ font->atlasX = 0;
+ font->atlasY = 0;
+ font->glyph_len = 0;
+
+ for (index = 0; index < RFONT_INIT_VERTS; index += 6) {
+ font->elements[index + 0] = vert_index + 0;
+ font->elements[index + 1] = vert_index + 1;
+ font->elements[index + 2] = vert_index + 2;
+ font->elements[index + 3] = vert_index + 3;
+ font->elements[index + 4] = vert_index + 0;
+ font->elements[index + 5] = vert_index + 2;
+ vert_index += 4;
+ }
+
+ return font;
+}
+
+void RFont_font_free_ptr(RFont_renderer* renderer, RFont_font* font) {
+ if (renderer->proc.free_atlas)
+ renderer->proc.free_atlas(renderer->ctx, font->atlas);
+ RFONT_FREE(font->src);
+}
+
+void RFont_font_free(RFont_renderer* renderer, RFont_font* font) {
+ RFONT_FREE(font->src->info.data);
+ RFont_font_free_ptr(renderer, font);
+ RFONT_FREE(font);
+}
+
+/*
+decode utf8 character to codepoint
+*/
+
+/* Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+ See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
+*/
+
+#define RFONT_UTF8_ACCEPT 0
+#define RFont_UTF8_REJECT 12
+
+RFONT_API u32 RFont_decode_utf8(u32* state, u32* codep, u32 byte);
+
+u32 RFont_decode_utf8(u32* state, u32* codep, u32 byte) {
+ static const u8 utf8d[] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 00..1f */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 20..3f */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 40..5f */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 60..7f */
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, /* 80..9f */
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* a0..bf */
+ 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* c0..df */
+ 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, /* e0..ef */
+ 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, /* f0..ff */
+ 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, /* s0..s0 */
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, /* s1..s2 */
+ 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, /* s3..s4 */
+ 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, /* s5..s6 */
+ 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* s7..s8 */
+ };
+
+ u32 type = utf8d[byte];
+
+ *codep = (*state != RFONT_UTF8_ACCEPT) ?
+ (byte & 0x3fu) | (*codep << 6) :
+ (0xff >> type) & (byte);
+
+ *state = utf8d[256 + *state * 16 + type];
+ return *state;
+}
+
+void RFont_font_add_string(RFont_renderer* renderer, RFont_font* font, const char* string, size_t* sizes, size_t sizeLen) {
+ RFont_font_add_string_len(renderer, font, string, 0, sizes, sizeLen);
+}
+
+void RFont_font_add_string_len(RFont_renderer* renderer, RFont_font* font, const char* string, size_t strLen, size_t* sizes, size_t sizeLen) {
+ u32 i;
+ char* str;
+ for (str = (char*)string; (!strLen || (size_t)(str - string) < strLen) && *str; str++)
+ for (i = 0; i < sizeLen; i++)
+ RFont_font_add_char(renderer, font, *str, sizes[i]);
+}
+
+RFont_glyph_fallback_callback RFont_glyph_fallback = NULL;
+RFont_glyph_fallback_callback RFont_set_glyph_fallback_callback(RFont_glyph_fallback_callback callback) {
+ RFont_glyph_fallback_callback old = RFont_glyph_fallback;
+ RFont_glyph_fallback = callback;
+ return old;
+}
+
+RFont_glyph RFont_font_add_char(RFont_renderer* renderer, RFont_font* font, char ch, size_t size) {
+ static u32 utf8state = 0, codepoint = 0;
+
+ if (RFont_decode_utf8(&utf8state, &codepoint, (u8)ch) != RFONT_UTF8_ACCEPT) {
+ RFont_glyph g;
+ RFONT_MEMSET(&g, 0, sizeof(RFont_glyph));
+ return g;
+ }
+
+ return RFont_font_add_codepoint(renderer, font, codepoint, size);
+}
+
+RFont_glyph RFont_font_add_codepoint(RFont_renderer* renderer, RFont_font* font, u32 codepoint, size_t size) {
+ return RFont_font_add_codepoint_ex(renderer, font, codepoint, size, 1);
+}
+
+RFont_glyph RFont_font_add_codepoint_ex(RFont_renderer* renderer, RFont_font* font, u32 codepoint, size_t size, b8 fallback) {
+ RFont_glyph* glyph;
+ RFont_glyph glyphNull;
+
+ u8* bitmap;
+ float scale;
+
+ i32 x0, y0, x1, y1, w = 0, h = 0, advanceX = 0;
+ u32 i;
+ for (i = 0; i < font->glyph_len; i++)
+ if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == size)
+ return font->glyphs[i];
+
+ RFONT_MEMSET(&glyphNull, 0, sizeof(glyphNull));
+ if (i < sizeof(font->glyphs)) {
+ glyph = &font->glyphs[i];
+ } else {
+ return glyphNull;
+ }
+
+ glyph->src = rstbtt_FindGlyphIndex(&font->src->info, (int)codepoint);
+
+ if ((glyph->src == 0 && codepoint) && fallback && RFont_glyph_fallback) {
+ RFont_glyph fallbackGlyph = RFont_glyph_fallback(renderer, font, codepoint, size);
+ if (fallbackGlyph.codepoint != 0 && fallbackGlyph.size != 0) {
+ return fallbackGlyph;
+ }
+ }
+
+ if (glyph->src == 0 && codepoint) return RFont_font_add_codepoint_ex(renderer, font, 0, size, fallback);
+ font->glyph_len++;
+
+ if (codepoint && rstbtt_GetGlyphBox(&font->src->info, glyph->src, &x0, &y0, &x1, &y1) == 0) {
+ return glyphNull;
+ }
+
+ scale = ((float)size) / font->fheight;
+ bitmap = rstbtt_GetGlyphBitmapSubpixel(&font->src->info, 0, scale, 0.0f, 0.0f, glyph->src, &w, &h, 0, 0);
+ glyph->w = (float)w;
+ glyph->h = (float)h;
+
+ if (codepoint) {
+ glyph->x1 = (float)floor((float)x0 * scale);
+ glyph->y1 = (float)floor((float)-y1 * scale);
+ } else glyph->y1 = (float)-((float)h * 0.75f);
+
+ glyph->codepoint = codepoint;
+ glyph->size = size;
+ glyph->font = font;
+
+ if (renderer->proc.bitmap_to_atlas)
+ renderer->proc.bitmap_to_atlas(renderer->ctx, font->atlas, (u32)font->atlasWidth, (u32)font->atlasHeight, font->maxHeight, bitmap, glyph->w, glyph->h, &font->atlasX, &font->atlasY);
+
+ RFONT_FREE(bitmap);
+ glyph->x = (i32)(font->atlasX - glyph->w);
+ glyph->x2 = (i32)(font->atlasX);
+
+ glyph->y = (i32)(font->atlasY);
+ glyph->y2 = (i32)((font->atlasY) + glyph->h);
+
+ if (glyph->src < font->numOfLongHorMetrics)
+ advanceX = RFONT_SHORT(font->src->info.data, font->src->info.hmtx + 4 * glyph->src);
+ else
+ advanceX = RFONT_SHORT(font->src->info.data, font->src->info.hmtx + 4 * (i32)(font->numOfLongHorMetrics - 1));
+
+ glyph->advance = (u32)((float)advanceX * scale);
+
+ return *glyph;
+}
+
+void RFont_text_area(RFont_renderer* renderer, RFont_font* font, const char* text, u32 size, u32* w, u32* h) {
+ RFont_text_area_len(renderer, font, text, 0, size, 0, 0.0f, w, h);
+}
+
+void RFont_text_area_spacing(RFont_renderer* renderer, RFont_font* font, const char* text, float spacing, u32 size, u32* w, u32* h) {
+ RFont_text_area_len(renderer, font, text, 0, size, 0, spacing, w, h);
+}
+
+void RFont_text_area_len(RFont_renderer* renderer, RFont_font* font, const char* text, size_t len, u32 size, size_t stopNL, float spacing, u32* w, u32* h) {
+ float x = 0;
+ size_t y = 1;
+
+ char* str;
+
+ float scale = (((float)size) / font->fheight);
+ float space_adv = (scale * font->space_adv);
+ RFont_glyph glyph;
+
+ for (str = (char*)text; (len == 0 || (size_t)(str - text) < len) && *str; str++) {
+ if (*str == '\n') {
+ if (y == stopNL) {
+ if (w) *w = (u32)x;
+ if (h) *h = (u32)y * size;
+ return;
+ }
+
+ y++;
+ x = 0;
+ continue;
+ }
+
+ if (*str == ' ' || *str == '\t') {
+ x += space_adv + spacing;
+ continue;
+ }
+
+ glyph = RFont_font_add_char(renderer, font, *str, size);
+
+ if (glyph.codepoint == 0 && glyph.size == 0)
+ continue;
+
+ x += (float)glyph.advance + spacing;
+ }
+
+ if (w) *w = (u32)x;
+ if(h) *h = (u32)(y * size);
+}
+
+size_t RFont_draw_text(RFont_renderer* renderer, RFont_font* font, const char* text, float x, float y, u32 size) {
+ return RFont_draw_text_len(renderer, font, text, 0, x, y, size, 0.0f);
+}
+
+size_t RFont_draw_text_spacing(RFont_renderer* renderer, RFont_font* font, const char* text, float x, float y, u32 size, float spacing) {
+ return RFont_draw_text_len(renderer, font, text, 0, x, y, size, spacing);
+}
+
+char* RFont_codepoint_to_utf8(u32 codepoint) {
+ static char utf8[5];
+ if (codepoint <= 0x7F) {
+ utf8[0] = (char)codepoint;
+ utf8[1] = 0;
+ } else if (codepoint <= 0x7FF) {
+ utf8[0] = (char)(0xC0 | (codepoint >> 6));
+ utf8[1] = (char)(0x80 | (codepoint & 0x3F));
+ utf8[2] = 0;
+ } else if (codepoint <= 0xFFFF) {
+ utf8[0] = (char)(0xE0 | (codepoint >> 12));
+ utf8[1] = (char)(0x80 | ((codepoint >> 6) & 0x3F));
+ utf8[2] = (char)(0x80 | (codepoint & 0x3F));
+ utf8[3] = 0;
+ } else if (codepoint <= 0x10FFFF) {
+ utf8[0] = (char)(0xF0 | (codepoint >> 18));
+ utf8[1] = (char)(0x80 | ((codepoint >> 12) & 0x3F));
+ utf8[2] = (char)(0x80 | ((codepoint >> 6) & 0x3F));
+ utf8[3] = (char)(0x80 | (codepoint & 0x3F));
+ utf8[4] = 0;
+ } else {
+ utf8[0] = 0;
+ }
+
+ return utf8;
+}
+
+size_t RFont_draw_text_len(RFont_renderer* renderer, RFont_font* font, const char* text, size_t len, float x, float y, u32 size, float spacing) {
+ RFont_render_data data;
+ float startX = x;
+ float startY = y;
+
+ size_t i = 0;
+ size_t tIndex = 0;
+
+ char* str;
+ RFont_glyph glyph;
+ float realX, realY;
+
+ float scale = (((float)size) / font->fheight);
+ float space_adv = (scale * font->space_adv);
+
+ float descent_offset = (-font->descent * scale);
+
+ data.verts = font->verts;
+ data.tcoords = font->tcoords;
+ data.elements = font->elements;
+ data.atlas = font->atlas;
+ data.nverts = 0;
+ data.nelements = 0;
+
+ RFONT_UNUSED(startY);
+
+ y = (y + (float)size - descent_offset);
+
+ for (str = (char*)text; (len == 0 || (size_t)(str - text) < len) && *str; str++) {
+ if (*str == '\n') {
+ x = startX;
+ y += (float)size;
+ continue;
+ }
+
+ if (*str == ' ' || *str == '\t') {
+ x += space_adv + spacing;
+ continue;
+ }
+
+ glyph = RFont_font_add_char(renderer, font, *str, size);
+
+ if (glyph.codepoint == 0 && glyph.size == 0)
+ continue;
+
+ if (glyph.font != font) {
+ RFont_draw_text_len(renderer, glyph.font, RFont_codepoint_to_utf8(glyph.codepoint), 4, x, y - (float)size + descent_offset, size, spacing);
+ x += glyph.advance + spacing;
+ continue;
+ }
+
+ realX = x + glyph.x1;
+ realY = y + glyph.y1;
+
+ i = data.nverts * 3;
+ tIndex = data.nverts * 2;
+
+ data.verts[i] = (i32)realX;
+ data.verts[i + 1] = realY;
+ data.verts[i + 2] = 0;
+ /* */
+ data.verts[i + 3] = (i32)realX;
+ data.verts[i + 4] = realY + glyph.h;
+ data.verts[i + 5] = 0;
+ /* */
+ data.verts[i + 6] = (i32)(realX + glyph.w);
+ data.verts[i + 7] = realY + glyph.h;
+ data.verts[i + 8] = 0;
+ /* */
+ /* */
+ data.verts[i + 9] = (i32)(realX + glyph.w);
+ data.verts[i + 10] = realY;
+ data.verts[i + 11] = 0;
+
+ /* texture coords */
+ data.tcoords[tIndex] = RFONT_GET_TEXPOSX(glyph.x, font->atlasWidth);
+ data.tcoords[tIndex + 1] = RFONT_GET_TEXPOSY(glyph.y, font->atlasWidth);
+
+ /* */
+ data.tcoords[tIndex + 2] = RFONT_GET_TEXPOSX(glyph.x, font->atlasWidth);
+ data.tcoords[tIndex + 3] = RFONT_GET_TEXPOSY(glyph.y2, font->atlasHeight);
+ /* */
+ data.tcoords[tIndex + 4] = RFONT_GET_TEXPOSX(glyph.x2, font->atlasWidth);
+ data.tcoords[tIndex + 5] = RFONT_GET_TEXPOSY(glyph.y2, font->atlasHeight);
+ /* */
+ /* */
+ data.tcoords[tIndex + 6] = RFONT_GET_TEXPOSX(glyph.x2, font->atlasWidth);
+ data.tcoords[tIndex + 7] = RFONT_GET_TEXPOSY(glyph.y, font->atlasWidth);
+
+ x += glyph.advance + spacing;
+
+ data.nverts += 4;
+ data.nelements += 6;
+ }
+
+ if (i && renderer->proc.render)
+ renderer->proc.render(renderer->ctx, &data);
+
+ return data.nelements;
+}
+
+/*
+stb_truetype defines and source code required by RFont
+
+you probably don't care about this part if you're reading just the RFont code
+*/
+
+#ifndef RFONT_EXTERNAL_STB
+ typedef char rstbtt__check_size32[sizeof(i32)==4 ? 1 : -1];
+ typedef char rstbtt__check_size16[sizeof(i16)==2 ? 1 : -1];
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ rstbtt_vmove=1,
+ rstbtt_vline,
+ rstbtt_vcurve,
+ rstbtt_vcubic
+};
+
+typedef struct rstbtt_vertex {
+ float x,y,cx,cy,cx1,cy1;
+ unsigned char type,padding;
+} rstbtt_vertex;
+
+RFONT_API int rstbtt_GetGlyphShape(const rstbtt_fontinfo *info, int glyph_index, rstbtt_vertex **vertices);
+
+/* @TODO: don't expose this structure */
+typedef struct
+{
+ int w,h,stride;
+ unsigned char *pixels;
+} rstbtt__bitmap;
+
+/* rasterize a shape with quadratic beziers into a bitmap */
+RFONT_API void rstbtt_Rasterize(rstbtt__bitmap *result, /* 1-channel bitmap to draw into */
+ float flatness_in_pixels, /* allowable error of curve in pixels */
+ rstbtt_vertex *vertices, /* array of vertices defining shape */
+ int num_verts, /* number of vertices in above array */
+ float scale_x, float scale_y, /* scale applied to input vertices */
+ float shift_x, float shift_y, /* translation applied to input vertices */
+ int x_off, int y_off, /* another translation applied to input */
+ int invert); /* if non-zero, vertically flip shape */
+
+enum { /* platformID */
+ rstbtt_PLATFORM_ID_UNICODE =0,
+ rstbtt_PLATFORM_ID_MAC =1,
+ rstbtt_PLATFORM_ID_ISO =2,
+ rstbtt_PLATFORM_ID_MICROSOFT =3
+};
+
+enum { /* encodingID for rstbtt_PLATFORM_ID_UNICODE */
+ rstbtt_UNICODE_EID_UNICODE_1_0 =0,
+ rstbtt_UNICODE_EID_UNICODE_1_1 =1,
+ rstbtt_UNICODE_EID_ISO_10646 =2,
+ rstbtt_UNICODE_EID_UNICODE_2_0_BMP=3,
+ rstbtt_UNICODE_EID_UNICODE_2_0_FULL=4
+};
+
+enum { /* encodingID for rstbtt_PLATFORM_ID_MICROSOFT */
+ rstbtt_MS_EID_SYMBOL =0,
+ rstbtt_MS_EID_UNICODE_BMP =1,
+ rstbtt_MS_EID_SHIFTJIS =2,
+ rstbtt_MS_EID_UNICODE_FULL =10
+};
+
+enum { /* encodingID for rstbtt_PLATFORM_ID_MAC; same as Script Manager codes */
+ rstbtt_MAC_EID_ROMAN =0, rstbtt_MAC_EID_ARABIC =4,
+ rstbtt_MAC_EID_JAPANESE =1, rstbtt_MAC_EID_HEBREW =5,
+ rstbtt_MAC_EID_CHINESE_TRAD =2, rstbtt_MAC_EID_GREEK =6,
+ rstbtt_MAC_EID_KOREAN =3, rstbtt_MAC_EID_RUSSIAN =7
+};
+
+enum { /* languageID for rstbtt_PLATFORM_ID_MICROSOFT; same as LCID...
+ problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs */
+ rstbtt_MS_LANG_ENGLISH =0x0409, rstbtt_MS_LANG_ITALIAN =0x0410,
+ rstbtt_MS_LANG_CHINESE =0x0804, rstbtt_MS_LANG_JAPANESE =0x0411,
+ rstbtt_MS_LANG_DUTCH =0x0413, rstbtt_MS_LANG_KOREAN =0x0412,
+ rstbtt_MS_LANG_FRENCH =0x040c, rstbtt_MS_LANG_RUSSIAN =0x0419,
+ rstbtt_MS_LANG_GERMAN =0x0407, rstbtt_MS_LANG_SPANISH =0x0409,
+ rstbtt_MS_LANG_HEBREW =0x040d, rstbtt_MS_LANG_SWEDISH =0x041D
+};
+
+enum { /* languageID for rstbtt_PLATFORM_ID_MAC */
+ rstbtt_MAC_LANG_ENGLISH =0 , rstbtt_MAC_LANG_JAPANESE =11,
+ rstbtt_MAC_LANG_ARABIC =12, rstbtt_MAC_LANG_KOREAN =23,
+ rstbtt_MAC_LANG_DUTCH =4 , rstbtt_MAC_LANG_RUSSIAN =32,
+ rstbtt_MAC_LANG_FRENCH =1 , rstbtt_MAC_LANG_SPANISH =6 ,
+ rstbtt_MAC_LANG_GERMAN =2 , rstbtt_MAC_LANG_SWEDISH =5 ,
+ rstbtt_MAC_LANG_HEBREW =10, rstbtt_MAC_LANG_CHINESE_SIMPLIFIED =33,
+ rstbtt_MAC_LANG_ITALIAN =3 , rstbtt_MAC_LANG_CHINESE_TRAD =19
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifndef rstbtt_MAX_OVERSAMPLE
+#define rstbtt_MAX_OVERSAMPLE 8
+#endif
+
+#if rstbtt_MAX_OVERSAMPLE > 255
+#error "rstbtt_MAX_OVERSAMPLE cannot be > 255"
+#endif
+
+typedef int rstbtt__test_oversample_pow2[(rstbtt_MAX_OVERSAMPLE & (rstbtt_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1];
+
+#ifndef rstbtt_RASTERIZER_VERSION
+#define rstbtt_RASTERIZER_VERSION 2
+#endif
+
+#ifdef _MSC_VER
+#define rstbtt__NOTUSED(v) (void)(v)
+#else
+#define rstbtt__NOTUSED(v) (void)sizeof(v)
+#endif
+
+#undef RFONT_API
+#define RFONT_API extern
+
+
+
+RFONT_API u8 rstbtt__buf_get8(rstbtt__buf *b)
+{
+ if (b->cursor >= b->size)
+ return 0;
+ return b->data[b->cursor++];
+}
+
+RFONT_API u8 rstbtt__buf_peek8(rstbtt__buf *b)
+{
+ if (b->cursor >= b->size)
+ return 0;
+ return b->data[b->cursor];
+}
+
+RFONT_API void rstbtt__buf_seek(rstbtt__buf *b, int o)
+{
+ assert(!(o > b->size || o < 0));
+ b->cursor = (o > b->size || o < 0) ? b->size : o;
+}
+
+RFONT_API void rstbtt__buf_skip(rstbtt__buf *b, int o)
+{
+ rstbtt__buf_seek(b, b->cursor + o);
+}
+
+RFONT_API u32 rstbtt__buf_get(rstbtt__buf *b, int n)
+{
+ u32 v = 0;
+ int i;
+ assert(n >= 1 && n <= 4);
+ for (i = 0; i < n; i++)
+ v = (v << 8) | rstbtt__buf_get8(b);
+ return v;
+}
+
+RFONT_API rstbtt__buf rstbtt__new_buf(const void *p, size_t size)
+{
+ rstbtt__buf r;
+ assert(size < 0x40000000);
+ r.data = (u8*) p;
+ r.size = (int) size;
+ r.cursor = 0;
+ return r;
+}
+
+#define rstbtt__buf_get16(b) (u16)rstbtt__buf_get((b), 2)
+#define rstbtt__buf_get32(b) (u32)rstbtt__buf_get((b), 4)
+
+RFONT_API rstbtt__buf rstbtt__buf_range(const rstbtt__buf *b, int o, int s)
+{
+ rstbtt__buf r = rstbtt__new_buf(NULL, 0);
+ if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r;
+ r.data = b->data + o;
+ r.size = s;
+ return r;
+}
+
+RFONT_API rstbtt__buf rstbtt__cff_get_index(rstbtt__buf *b)
+{
+ int count, start, offsize;
+ start = b->cursor;
+ count = rstbtt__buf_get16(b);
+ if (count) {
+ offsize = rstbtt__buf_get8(b);
+ assert(offsize >= 1 && offsize <= 4);
+ rstbtt__buf_skip(b, offsize * count);
+ rstbtt__buf_skip(b, (int)rstbtt__buf_get(b, offsize) - 1);
+ }
+ return rstbtt__buf_range(b, start, b->cursor - start);
+}
+
+RFONT_API u32 rstbtt__cff_int(rstbtt__buf *b)
+{
+ int b0 = rstbtt__buf_get8(b);
+ if (b0 >= 32 && b0 <= 246) return (u32)b0 - 139;
+ else if (b0 >= 247 && b0 <= 250) return (u32)((b0 - 247)*256 + rstbtt__buf_get8(b) + 108);
+ else if (b0 >= 251 && b0 <= 254) return (u32)(-(b0 - 251)*256 - rstbtt__buf_get8(b) - 108);
+ else if (b0 == 28) return rstbtt__buf_get16(b);
+ else if (b0 == 29) return rstbtt__buf_get32(b);
+ assert(0);
+ return 0;
+}
+
+RFONT_API void rstbtt__cff_skip_operand(rstbtt__buf *b) {
+ int v, b0 = rstbtt__buf_peek8(b);
+ assert(b0 >= 28);
+ if (b0 == 30) {
+ rstbtt__buf_skip(b, 1);
+ while (b->cursor < b->size) {
+ v = rstbtt__buf_get8(b);
+ if ((v & 0xF) == 0xF || (v >> 4) == 0xF)
+ break;
+ }
+ } else {
+ rstbtt__cff_int(b);
+ }
+}
+
+RFONT_API rstbtt__buf rstbtt__dict_get(rstbtt__buf *b, int key)
+{
+ rstbtt__buf_seek(b, 0);
+ while (b->cursor < b->size) {
+ int start = b->cursor, end, op;
+ while (rstbtt__buf_peek8(b) >= 28)
+ rstbtt__cff_skip_operand(b);
+ end = b->cursor;
+ op = rstbtt__buf_get8(b);
+ if (op == 12) op = rstbtt__buf_get8(b) | 0x100;
+ if (op == key) return rstbtt__buf_range(b, start, end-start);
+ }
+ return rstbtt__buf_range(b, 0, 0);
+}
+
+RFONT_API void rstbtt__dict_get_ints(rstbtt__buf *b, int key, int outcount, u32 *out)
+{
+ int i;
+ rstbtt__buf operands = rstbtt__dict_get(b, key);
+ for (i = 0; i < outcount && operands.cursor < operands.size; i++)
+ out[i] = rstbtt__cff_int(&operands);
+}
+
+RFONT_API int rstbtt__cff_index_count(rstbtt__buf *b)
+{
+ rstbtt__buf_seek(b, 0);
+ return rstbtt__buf_get16(b);
+}
+
+RFONT_API rstbtt__buf rstbtt__cff_index_get(rstbtt__buf b, int i)
+{
+ int count, offsize, start, end;
+ rstbtt__buf_seek(&b, 0);
+ count = rstbtt__buf_get16(&b);
+ offsize = rstbtt__buf_get8(&b);
+ assert(i >= 0 && i < count);
+ assert(offsize >= 1 && offsize <= 4);
+ rstbtt__buf_skip(&b, i*offsize);
+ start = (int)rstbtt__buf_get(&b, offsize);
+ end = (int)rstbtt__buf_get(&b, offsize);
+ return rstbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start);
+}
+
+#define rstbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3))
+#define rstbtt_tag(p,str) rstbtt_tag4(p,str[0],str[1],str[2],str[3])
+
+/* @OPTIMIZE: binary search */
+RFONT_API u32 rstbtt__find_table(u8 *data, u32 fontstart, const char *tag)
+{
+ i32 num_tables = RFONT_USHORT(data, fontstart+4);
+ u32 tabledir = fontstart + 12;
+ i32 i;
+ for (i=0; i < num_tables; ++i) {
+ u32 loc = tabledir + 16 * (u32)i;
+ if (rstbtt_tag(data+loc+0, tag))
+ return RFONT_ULONG(data, loc+8);
+ }
+ return 0;
+}
+
+RFONT_API rstbtt__buf rstbtt__get_subrs(rstbtt__buf cff, rstbtt__buf fontdict)
+{
+ u32 subrsoff = 0, private_loc[2] = { 0, 0 };
+ rstbtt__buf pdict;
+ rstbtt__dict_get_ints(&fontdict, 18, 2, private_loc);
+ if (!private_loc[1] || !private_loc[0]) return rstbtt__new_buf(NULL, 0);
+ pdict = rstbtt__buf_range(&cff, (int)private_loc[1], (int)private_loc[0]);
+ rstbtt__dict_get_ints(&pdict, 19, 1, &subrsoff);
+ if (!subrsoff) return rstbtt__new_buf(NULL, 0);
+ rstbtt__buf_seek(&cff, (int)(private_loc[1] + subrsoff));
+ return rstbtt__cff_get_index(&cff);
+}
+
+RFONT_API void rstbtt_setvertex(rstbtt_vertex *v, u8 type, i32 x, i32 y, i32 cx, i32 cy)
+{
+ v->type = type;
+ v->x = (i16) x;
+ v->y = (i16) y;
+ v->cx = (i16) cx;
+ v->cy = (i16) cy;
+}
+
+RFONT_API int rstbtt__close_shape(rstbtt_vertex *vertices, int num_vertices, int was_off, int start_off,
+ i32 sx, i32 sy, i32 scx, i32 scy, i32 cx, i32 cy)
+{
+ if (start_off) {
+ if (was_off)
+ rstbtt_setvertex(&vertices[num_vertices++], rstbtt_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy);
+ rstbtt_setvertex(&vertices[num_vertices++], rstbtt_vcurve, sx,sy,scx,scy);
+ } else {
+ if (was_off)
+ rstbtt_setvertex(&vertices[num_vertices++], rstbtt_vcurve,sx,sy,cx,cy);
+ else
+ rstbtt_setvertex(&vertices[num_vertices++], rstbtt_vline,sx,sy,0,0);
+ }
+ return num_vertices;
+}
+
+RFONT_API int rstbtt__GetGlyfOffset(const rstbtt_fontinfo *info, int glyph_index);
+
+RFONT_API int rstbtt__GetGlyphShapeTT(const rstbtt_fontinfo *info, int glyph_index, rstbtt_vertex **pvertices)
+{
+ i16 numberOfContours;
+ u8 *endPtsOfContours;
+ u8 *data = info->data;
+ rstbtt_vertex *vertices=0;
+ int num_vertices=0;
+ int g = rstbtt__GetGlyfOffset(info, glyph_index);
+
+ *pvertices = NULL;
+
+ if (g < 0) return 0;
+
+ numberOfContours = RFONT_SHORT(data, g);
+
+ if (numberOfContours > 0) {
+ u8 flags=0,flagcount;
+ i32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0;
+ i32 x,y,cx,cy,sx,sy, scx,scy;
+ u8 *points;
+ endPtsOfContours = (data + g + 10);
+ ins = RFONT_USHORT(data, g + 10 + numberOfContours * 2);
+ points = data + g + 10 + numberOfContours * 2 + 2 + ins;
+
+ n = 1+RFONT_USHORT(endPtsOfContours, numberOfContours*2-2);
+
+ m = n + 2*numberOfContours; /* a loose bound on how many vertices we might need */
+ vertices = (rstbtt_vertex *) RFONT_MALLOC((size_t)m * sizeof(vertices[0]));
+ if (vertices == 0)
+ return 0;
+
+ next_move = 0;
+ flagcount=0;
+
+ off = m - n; /* starting offset for uninterpreted data, regardless of how m ends up being calculated */
+
+ /* first load flags */
+
+ for (i=0; i < n; ++i) {
+ if (flagcount == 0) {
+ flags = *points++;
+ if (flags & 8)
+ flagcount = *points++;
+ } else
+ --flagcount;
+ vertices[off+i].type = flags;
+ }
+
+ /* now load x coordinates */
+ x=0;
+ for (i=0; i < n; ++i) {
+ flags = vertices[off+i].type;
+ if (flags & 2) {
+ i16 dx = *points++;
+ x += (flags & 16) ? dx : -dx; /* ??? */
+ } else {
+ if (!(flags & 16)) {
+ x = x + (i16) (points[0]*256 + points[1]);
+ points += 2;
+ }
+ }
+ vertices[off+i].x = (i16) x;
+ }
+
+ /* now load y coordinates */
+ y=0;
+ for (i=0; i < n; ++i) {
+ flags = vertices[off+i].type;
+ if (flags & 4) {
+ i16 dy = *points++;
+ y += (flags & 32) ? dy : -dy; /* ??? */
+ } else {
+ if (!(flags & 32)) {
+ y = y + (i16) (points[0]*256 + points[1]);
+ points += 2;
+ }
+ }
+ vertices[off+i].y = (i16) y;
+ }
+
+ /* now convert them to our format */
+ num_vertices=0;
+ sx = sy = cx = cy = scx = scy = 0;
+ for (i=0; i < n; ++i) {
+ flags = vertices[off+i].type;
+ x = (i16) vertices[off+i].x;
+ y = (i16) vertices[off+i].y;
+
+ if (next_move == i) {
+ if (i != 0)
+ num_vertices = rstbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
+
+ /* now start the new one */
+ start_off = !(flags & 1);
+ if (start_off) {
+ /* if we start off with an off-curve point, then when we need to find a point on the curve */
+ /* where we can start, and we need to save some state for when we wraparound. */
+ scx = x;
+ scy = y;
+ if (!(vertices[off+i+1].type & 1)) {
+ /* next point is also a curve point, so interpolate an on-point curve */
+ sx = (x + (i32) vertices[off+i+1].x) >> 1;
+ sy = (y + (i32) vertices[off+i+1].y) >> 1;
+ } else {
+ /* otherwise just use the next point as our start point */
+ sx = (i32) vertices[off+i+1].x;
+ sy = (i32) vertices[off+i+1].y;
+ ++i; /* we're using point i+1 as the starting point, so skip it */
+ }
+ } else {
+ sx = x;
+ sy = y;
+ }
+ rstbtt_setvertex(&vertices[num_vertices++], rstbtt_vmove,sx,sy,0,0);
+ was_off = 0;
+ next_move = 1 + RFONT_USHORT(endPtsOfContours, j*2);
+ ++j;
+ } else {
+ if (!(flags & 1)) { /* if it's a curve */
+ if (was_off) /* two off-curve control points in a row means interpolate an on-curve midpoint */
+ rstbtt_setvertex(&vertices[num_vertices++], rstbtt_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy);
+ cx = x;
+ cy = y;
+ was_off = 1;
+ } else {
+ if (was_off)
+ rstbtt_setvertex(&vertices[num_vertices++], rstbtt_vcurve, x,y, cx, cy);
+ else
+ rstbtt_setvertex(&vertices[num_vertices++], rstbtt_vline, x,y,0,0);
+ was_off = 0;
+ }
+ }
+ }
+ num_vertices = rstbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
+ } else if (numberOfContours < 0) {
+ /* Compound shapes. */
+ int more = 1;
+ u8 *comp = data + g + 10;
+ num_vertices = 0;
+ vertices = 0;
+ while (more) {
+ u16 flags, gidx;
+ int comp_num_verts = 0, i;
+ rstbtt_vertex *comp_verts = 0, *tmp = 0;
+ float mtx[6] = {1,0,0,1,0,0}, m, n;
+
+ flags = (u16)RFONT_SHORT(comp, 0); comp+=2;
+ gidx = (u16)RFONT_SHORT(comp, 0); comp+=2;
+
+ if (flags & 2) { /* XY values */
+ if (flags & 1) { /* shorts */
+ mtx[4] = RFONT_SHORT(comp, 0); comp+=2;
+ mtx[5] = RFONT_SHORT(comp, 0); comp+=2;
+ } else {
+ mtx[4] = RFONT_CHAR(comp, 0); comp+=1;
+ mtx[5] = RFONT_CHAR(comp, 0); comp+=1;
+ }
+ }
+ else {
+ /* @TODO handle matching point */
+ assert(0);
+ }
+ if (flags & (1<<3)) { /* WE_HAVE_A_SCALE */
+ mtx[0] = mtx[3] = RFONT_SHORT(comp, 0 )/16384.0f; comp+=2;
+ mtx[1] = mtx[2] = 0;
+ } else if (flags & (1<<6)) { /* WE_HAVE_AN_X_AND_YSCALE */
+ mtx[0] = RFONT_SHORT(comp, 0)/16384.0f; comp+=2;
+ mtx[1] = mtx[2] = 0;
+ mtx[3] = RFONT_SHORT(comp, 0)/16384.0f; comp+=2;
+ } else if (flags & (1<<7)) { /* WE_HAVE_A_TWO_BY_TWO */
+ mtx[0] = RFONT_SHORT(comp, 0)/16384.0f; comp+=2;
+ mtx[1] = RFONT_SHORT(comp, 0)/16384.0f; comp+=2;
+ mtx[2] = RFONT_SHORT(comp, 0)/16384.0f; comp+=2;
+ mtx[3] = RFONT_SHORT(comp, 0)/16384.0f; comp+=2;
+ }
+
+ /* Find transformation scales. */
+ m = (float) sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]);
+ n = (float) sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]);
+
+ /* Get indexed glyph. */
+ comp_num_verts = rstbtt_GetGlyphShape(info, gidx, &comp_verts);
+ if (comp_num_verts > 0) {
+ /* Transform vertices. */
+ for (i = 0; i < comp_num_verts; ++i) {
+ rstbtt_vertex* v = &comp_verts[i];
+ float x,y;
+ x=v->x; y=v->y;
+ v->x = (float)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
+ v->y = (float)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
+ x=v->cx; y=v->cy;
+ v->cx = (float)(m * (mtx[0] * (float)x + mtx[2] * (float)y + mtx[4]));
+ v->cy = (float)(n * (mtx[1] * (float)x + mtx[3] * (float)y + mtx[5]));
+ }
+ /* Append vertices. */
+ tmp = (rstbtt_vertex*)RFONT_MALLOC((size_t)(num_vertices + comp_num_verts) * sizeof(rstbtt_vertex));
+ if (!tmp) {
+ if (vertices) RFONT_FREE(vertices);
+ if (comp_verts) RFONT_FREE(comp_verts);
+ return 0;
+ }
+ if (num_vertices > 0) RFONT_MEMCPY(tmp, vertices, (size_t)num_vertices * sizeof(rstbtt_vertex));
+ RFONT_MEMCPY(tmp+num_vertices, comp_verts, (size_t)comp_num_verts * sizeof(rstbtt_vertex));
+ if (vertices) RFONT_FREE(vertices);
+ vertices = tmp;
+ RFONT_FREE(comp_verts);
+ num_vertices += comp_num_verts;
+ }
+ /* More components ? */
+ more = flags & (1<<5);
+ }
+ } else {
+ /* numberOfCounters == 0, do nothing */
+ }
+
+ *pvertices = vertices;
+ return num_vertices;
+}
+
+typedef struct
+{
+ int bounds;
+ int started;
+ float first_x, first_y;
+ float x, y;
+ i32 min_x, max_x, min_y, max_y;
+
+ rstbtt_vertex *pvertices;
+ int num_vertices;
+} rstbtt__csctx;
+
+#define rstbtt__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0}
+
+RFONT_API void rstbtt__track_vertex(rstbtt__csctx *c, i32 x, i32 y)
+{
+ if (x > c->max_x || !c->started) c->max_x = x;
+ if (y > c->max_y || !c->started) c->max_y = y;
+ if (x < c->min_x || !c->started) c->min_x = x;
+ if (y < c->min_y || !c->started) c->min_y = y;
+ c->started = 1;
+}
+
+RFONT_API void rstbtt__csctx_v(rstbtt__csctx *c, u8 type, i32 x, i32 y, i32 cx, i32 cy, i32 cx1, i32 cy1)
+{
+ if (c->bounds) {
+ rstbtt__track_vertex(c, x, y);
+ if (type == rstbtt_vcubic) {
+ rstbtt__track_vertex(c, cx, cy);
+ rstbtt__track_vertex(c, cx1, cy1);
+ }
+ } else {
+ rstbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy);
+ c->pvertices[c->num_vertices].cx1 = (i16) cx1;
+ c->pvertices[c->num_vertices].cy1 = (i16) cy1;
+ }
+ c->num_vertices++;
+}
+
+RFONT_API void rstbtt__csctx_close_shape(rstbtt__csctx *ctx)
+{
+ if (ctx->first_x != ctx->x || ctx->first_y != ctx->y)
+ rstbtt__csctx_v(ctx, rstbtt_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0);
+}
+
+RFONT_API void rstbtt__csctx_rmove_to(rstbtt__csctx *ctx, float dx, float dy)
+{
+ rstbtt__csctx_close_shape(ctx);
+ ctx->first_x = ctx->x = ctx->x + dx;
+ ctx->first_y = ctx->y = ctx->y + dy;
+ rstbtt__csctx_v(ctx, rstbtt_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0);
+}
+
+RFONT_API void rstbtt__csctx_rline_to(rstbtt__csctx *ctx, float dx, float dy)
+{
+ ctx->x += dx;
+ ctx->y += dy;
+ rstbtt__csctx_v(ctx, rstbtt_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0);
+}
+
+RFONT_API void rstbtt__csctx_rccurve_to(rstbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3)
+{
+ float cx1 = ctx->x + dx1;
+ float cy1 = ctx->y + dy1;
+ float cx2 = cx1 + dx2;
+ float cy2 = cy1 + dy2;
+ ctx->x = cx2 + dx3;
+ ctx->y = cy2 + dy3;
+ rstbtt__csctx_v(ctx, rstbtt_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2);
+}
+
+RFONT_API rstbtt__buf rstbtt__get_subr(rstbtt__buf idx, int n)
+{
+ int count = rstbtt__cff_index_count(&idx);
+ int bias = 107;
+ if (count >= 33900)
+ bias = 32768;
+ else if (count >= 1240)
+ bias = 1131;
+ n += bias;
+ if (n < 0 || n >= count)
+ return rstbtt__new_buf(NULL, 0);
+ return rstbtt__cff_index_get(idx, n);
+}
+
+RFONT_API rstbtt__buf rstbtt__cid_get_glyph_subrs(const rstbtt_fontinfo *info, int glyph_index)
+{
+ rstbtt__buf fdselect = info->fdselect;
+ int nranges, start, end, v, fmt, fdselector = -1, i;
+
+ rstbtt__buf_seek(&fdselect, 0);
+ fmt = rstbtt__buf_get8(&fdselect);
+ if (fmt == 0) {
+ /* untested */
+ rstbtt__buf_skip(&fdselect, glyph_index);
+ fdselector = rstbtt__buf_get8(&fdselect);
+ } else if (fmt == 3) {
+ nranges = rstbtt__buf_get16(&fdselect);
+ start = rstbtt__buf_get16(&fdselect);
+ for (i = 0; i < nranges; i++) {
+ v = rstbtt__buf_get8(&fdselect);
+ end = rstbtt__buf_get16(&fdselect);
+ if (glyph_index >= start && glyph_index < end) {
+ fdselector = v;
+ break;
+ }
+ start = end;
+ }
+ }
+ if (fdselector == -1) rstbtt__new_buf(NULL, 0);
+ return rstbtt__get_subrs(info->cff, rstbtt__cff_index_get(info->fontdicts, fdselector));
+}
+
+RFONT_API int rstbtt__run_charstring(const rstbtt_fontinfo *info, int glyph_index, rstbtt__csctx *c)
+{
+ int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0;
+ int has_subrs = 0, clear_stack;
+ float s[48];
+ rstbtt__buf subr_stack[10], subrs = info->subrs, b;
+ float f;
+
+#define rstbtt__CSERR(s) (0)
+
+ /* this currently ignores the initial width value, which isn't needed if we have hmtx */
+ b = rstbtt__cff_index_get(info->charstrings, glyph_index);
+ while (b.cursor < b.size) {
+ i = 0;
+ clear_stack = 1;
+ b0 = rstbtt__buf_get8(&b);
+ switch (b0) {
+ /* @TODO implement hinting */
+ case 0x13: /* hintmask */
+ case 0x14: /* cntrmask */
+ if (in_header)
+ maskbits += (sp / 2); /* implicit "vstem" */
+ in_header = 0;
+ rstbtt__buf_skip(&b, (maskbits + 7) / 8);
+ break;
+
+ case 0x01: /* hstem */
+ case 0x03: /* vstem */
+ case 0x12: /* hstemhm */
+ case 0x17: /* vstemhm */
+ maskbits += (sp / 2);
+ break;
+
+ case 0x15: /* rmoveto */
+ in_header = 0;
+ if (sp < 2) return rstbtt__CSERR("rmoveto stack");
+ rstbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]);
+ break;
+ case 0x04: /* vmoveto */
+ in_header = 0;
+ if (sp < 1) return rstbtt__CSERR("vmoveto stack");
+ rstbtt__csctx_rmove_to(c, 0, s[sp-1]);
+ break;
+ case 0x16: /* hmoveto */
+ in_header = 0;
+ if (sp < 1) return rstbtt__CSERR("hmoveto stack");
+ rstbtt__csctx_rmove_to(c, s[sp-1], 0);
+ break;
+
+ case 0x05: /* rlineto */
+ if (sp < 2) return rstbtt__CSERR("rlineto stack");
+ for (; i + 1 < sp; i += 2)
+ rstbtt__csctx_rline_to(c, s[i], s[i+1]);
+ break;
+
+ /* hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical
+ starting from a different place. */
+
+ case 0x07: /* vlineto */
+ if (sp < 1) return rstbtt__CSERR("vlineto stack");
+ goto vlineto;
+ case 0x06: /* hlineto */
+ if (sp < 1) return rstbtt__CSERR("hlineto stack");
+ for (;;) {
+ if (i >= sp) break;
+ rstbtt__csctx_rline_to(c, s[i], 0);
+ i++;
+ vlineto:
+ if (i >= sp) break;
+ rstbtt__csctx_rline_to(c, 0, s[i]);
+ i++;
+ }
+ break;
+
+ case 0x1F: /* hvcurveto */
+ if (sp < 4) return rstbtt__CSERR("hvcurveto stack");
+ goto hvcurveto;
+ case 0x1E: /* vhcurveto */
+ if (sp < 4) return rstbtt__CSERR("vhcurveto stack");
+ for (;;) {
+ if (i + 3 >= sp) break;
+ rstbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f);
+ i += 4;
+ hvcurveto:
+ if (i + 3 >= sp) break;
+ rstbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]);
+ i += 4;
+ }
+ break;
+
+ case 0x08: /* rrcurveto */
+ if (sp < 6) return rstbtt__CSERR("rcurveline stack");
+ for (; i + 5 < sp; i += 6)
+ rstbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
+ break;
+
+ case 0x18: /* rcurveline */
+ if (sp < 8) return rstbtt__CSERR("rcurveline stack");
+ for (; i + 5 < sp - 2; i += 6)
+ rstbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
+ if (i + 1 >= sp) return rstbtt__CSERR("rcurveline stack");
+ rstbtt__csctx_rline_to(c, s[i], s[i+1]);
+ break;
+
+ case 0x19: /* rlinecurve */
+ if (sp < 8) return rstbtt__CSERR("rlinecurve stack");
+ for (; i + 1 < sp - 6; i += 2)
+ rstbtt__csctx_rline_to(c, s[i], s[i+1]);
+ if (i + 5 >= sp) return rstbtt__CSERR("rlinecurve stack");
+ rstbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
+ break;
+
+ case 0x1A: /* vvcurveto */
+ case 0x1B: /* hhcurveto */
+ if (sp < 4) return rstbtt__CSERR("(vv|hh)curveto stack");
+ f = 0.0;
+ if (sp & 1) { f = s[i]; i++; }
+ for (; i + 3 < sp; i += 4) {
+ if (b0 == 0x1B)
+ rstbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0);
+ else
+ rstbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]);
+ f = 0.0;
+ }
+ break;
+
+ case 0x0A: /* callsubr */
+ if (!has_subrs) {
+ if (info->fdselect.size)
+ subrs = rstbtt__cid_get_glyph_subrs(info, glyph_index);
+ has_subrs = 1;
+ }
+ /* fallthrough */
+ case 0x1D: /* callgsubr */
+ if (sp < 1) return rstbtt__CSERR("call(g|)subr stack");
+ v = (int) s[--sp];
+ if (subr_stack_height >= 10) return rstbtt__CSERR("recursion limit");
+ subr_stack[subr_stack_height++] = b;
+ b = rstbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v);
+ if (b.size == 0) return rstbtt__CSERR("subr not found");
+ b.cursor = 0;
+ clear_stack = 0;
+ break;
+
+ case 0x0B: /* return */
+ if (subr_stack_height <= 0) return rstbtt__CSERR("return outside subr");
+ b = subr_stack[--subr_stack_height];
+ clear_stack = 0;
+ break;
+
+ case 0x0E: /* endchar */
+ rstbtt__csctx_close_shape(c);
+ return 1;
+
+ case 0x0C: { /* two-byte escape */
+ float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6;
+ float dx, dy;
+ int b1 = rstbtt__buf_get8(&b);
+ switch (b1) {
+ /* @TODO These "flex" implementations ignore the flex-depth and resolution,
+ and always draw beziers. */
+ case 0x22: /* hflex */
+ if (sp < 7) return rstbtt__CSERR("hflex stack");
+ dx1 = s[0];
+ dx2 = s[1];
+ dy2 = s[2];
+ dx3 = s[3];
+ dx4 = s[4];
+ dx5 = s[5];
+ dx6 = s[6];
+ rstbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0);
+ rstbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0);
+ break;
+
+ case 0x23: /* flex */
+ if (sp < 13) return rstbtt__CSERR("flex stack");
+ dx1 = s[0];
+ dy1 = s[1];
+ dx2 = s[2];
+ dy2 = s[3];
+ dx3 = s[4];
+ dy3 = s[5];
+ dx4 = s[6];
+ dy4 = s[7];
+ dx5 = s[8];
+ dy5 = s[9];
+ dx6 = s[10];
+ dy6 = s[11];
+ /* fd is s[12] */
+ rstbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3);
+ rstbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6);
+ break;
+
+ case 0x24: /* hflex1 */
+ if (sp < 9) return rstbtt__CSERR("hflex1 stack");
+ dx1 = s[0];
+ dy1 = s[1];
+ dx2 = s[2];
+ dy2 = s[3];
+ dx3 = s[4];
+ dx4 = s[5];
+ dx5 = s[6];
+ dy5 = s[7];
+ dx6 = s[8];
+ rstbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0);
+ rstbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5));
+ break;
+
+ case 0x25: /* flex1 */
+ if (sp < 11) return rstbtt__CSERR("flex1 stack");
+ dx1 = s[0];
+ dy1 = s[1];
+ dx2 = s[2];
+ dy2 = s[3];
+ dx3 = s[4];
+ dy3 = s[5];
+ dx4 = s[6];
+ dy4 = s[7];
+ dx5 = s[8];
+ dy5 = s[9];
+ dx6 = dy6 = s[10];
+ dx = dx1+dx2+dx3+dx4+dx5;
+ dy = dy1+dy2+dy3+dy4+dy5;
+ if (fabs(dx) > fabs(dy))
+ dy6 = -dy;
+ else
+ dx6 = -dx;
+ rstbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3);
+ rstbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6);
+ break;
+
+ default:
+ return rstbtt__CSERR("unimplemented");
+ }
+ } break;
+
+ default:
+ if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254))
+ return rstbtt__CSERR("reserved operator");
+
+ /* push immediate */
+ if (b0 == 255) {
+ f = (float)(i32)rstbtt__buf_get32(&b) / 0x10000;
+ } else {
+ rstbtt__buf_skip(&b, -1);
+ f = (float)(i16)rstbtt__cff_int(&b);
+ }
+ if (sp >= 48) return rstbtt__CSERR("push stack overflow");
+ s[sp++] = f;
+ clear_stack = 0;
+ break;
+ }
+ if (clear_stack) sp = 0;
+ }
+ return rstbtt__CSERR("no endchar");
+
+#undef rstbtt__CSERR
+}
+
+RFONT_API int rstbtt__GetGlyphShapeT2(const rstbtt_fontinfo *info, int glyph_index, rstbtt_vertex **pvertices)
+{
+ /* runs the charstring twice, once to count and once to output (to avoid realloc) */
+ rstbtt__csctx count_ctx = rstbtt__CSCTX_INIT(1);
+ rstbtt__csctx output_ctx = rstbtt__CSCTX_INIT(0);
+ if (rstbtt__run_charstring(info, glyph_index, &count_ctx)) {
+ *pvertices = (rstbtt_vertex*)RFONT_MALLOC((size_t)count_ctx.num_vertices * sizeof(rstbtt_vertex));
+ output_ctx.pvertices = *pvertices;
+ if (rstbtt__run_charstring(info, glyph_index, &output_ctx)) {
+ assert(output_ctx.num_vertices == count_ctx.num_vertices);
+ return output_ctx.num_vertices;
+ }
+ }
+ *pvertices = NULL;
+ return 0;
+}
+
+RFONT_API int rstbtt_GetGlyphShape(const rstbtt_fontinfo *info, int glyph_index, rstbtt_vertex **pvertices)
+{
+ if (!info->cff.size)
+ return rstbtt__GetGlyphShapeTT(info, glyph_index, pvertices);
+ else
+ return rstbtt__GetGlyphShapeT2(info, glyph_index, pvertices);
+}
+
+RFONT_API int rstbtt__GetGlyphKernInfoAdvance(const rstbtt_fontinfo *info, int glyph1, int glyph2)
+{
+ u8 *data = info->data + info->kern;
+ u32 needle, straw;
+ int l, r, m;
+
+ /* we only look at the first table. it must be 'horizontal' and format 0. */
+ if (!info->kern)
+ return 0;
+ if (RFONT_USHORT(data, 2) < 1) /* number of tables, need at least 1 */
+ return 0;
+ if (RFONT_USHORT(data, 8) != 1) /* horizontal flag must be set in format */
+ return 0;
+
+ l = 0;
+ r = RFONT_USHORT(data, 10) - 1;
+ needle = (u32)(glyph1 << 16 | glyph2);
+ while (l <= r) {
+ m = (l + r) >> 1;
+ straw = RFONT_ULONG(data, 18+(m*6)); /* note: unaligned read */
+ if (needle < straw)
+ r = m - 1;
+ else if (needle > straw)
+ l = m + 1;
+ else
+ return RFONT_SHORT(data, 22+(m*6));
+ }
+ return 0;
+}
+
+RFONT_API i32 rstbtt__GetCoverageIndex(u8 *coverageTable, int glyph)
+{
+ u16 coverageFormat = RFONT_USHORT(coverageTable, 0);
+ switch(coverageFormat) {
+ case 1: {
+ u16 glyphCount = RFONT_USHORT(coverageTable, 2);
+
+ /* Binary search. */
+ i32 l=0, r=glyphCount-1, m;
+ int straw, needle=glyph;
+ while (l <= r) {
+ u8 *glyphArray = coverageTable + 4;
+ u16 glyphID;
+ m = (l + r) >> 1;
+ glyphID = RFONT_USHORT(glyphArray, 2 * m);
+ straw = glyphID;
+ if (needle < straw)
+ r = m - 1;
+ else if (needle > straw)
+ l = m + 1;
+ else {
+ return m;
+ }
+ }
+ } break;
+
+ case 2: {
+ u16 rangeCount = RFONT_USHORT(coverageTable, 2);
+ u8 *rangeArray = coverageTable + 4;
+
+ /* Binary search. */
+ i32 l=0, r=rangeCount-1, m;
+ int strawStart, strawEnd, needle=glyph;
+ while (l <= r) {
+ u8 *rangeRecord;
+ m = (l + r) >> 1;
+ rangeRecord = rangeArray + 6 * m;
+ strawStart = RFONT_USHORT(rangeRecord, 0);
+ strawEnd = RFONT_USHORT(rangeRecord, 2);
+ if (needle < strawStart)
+ r = m - 1;
+ else if (needle > strawEnd)
+ l = m + 1;
+ else {
+ u16 startCoverageIndex = RFONT_USHORT(rangeRecord, 4);
+ return startCoverageIndex + glyph - strawStart;
+ }
+ }
+ } break;
+
+ default: {
+ /* There are no other cases. */
+ assert(0);
+ } break;
+ }
+
+ return -1;
+}
+
+RFONT_API i32 rstbtt__GetGlyphClass(u8 *classDefTable, int glyph)
+{
+ u16 classDefFormat = RFONT_USHORT(classDefTable, 0);
+ switch(classDefFormat)
+ {
+ case 1: {
+ u16 startGlyphID = RFONT_USHORT(classDefTable, 2);
+ u16 glyphCount = RFONT_USHORT(classDefTable, 4);
+ u8 *classDef1ValueArray = classDefTable + 6;
+
+ if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount)
+ return (i32)RFONT_USHORT(classDef1ValueArray, 2 * (glyph - startGlyphID));
+
+ classDefTable = classDef1ValueArray + 2 * glyphCount;
+ } break;
+
+ case 2: {
+ u16 classRangeCount = RFONT_USHORT(classDefTable, 2);
+ u8 *classRangeRecords = classDefTable + 4;
+
+ /* Binary search. */
+ i32 l=0, r=classRangeCount-1, m;
+ int strawStart, strawEnd, needle=glyph;
+ while (l <= r) {
+ u8 *classRangeRecord;
+ m = (l + r) >> 1;
+ classRangeRecord = classRangeRecords + 6 * m;
+ strawStart = RFONT_USHORT(classRangeRecord, 0);
+ strawEnd = RFONT_USHORT(classRangeRecord, 2);
+ if (needle < strawStart)
+ r = m - 1;
+ else if (needle > strawEnd)
+ l = m + 1;
+ else
+ return (i32)RFONT_USHORT(classRangeRecord, 4);
+ }
+
+ classDefTable = classRangeRecords + 6 * classRangeCount;
+ } break;
+
+ default: {
+ /* There are no other cases. */
+ assert(0);
+ } break;
+ }
+
+ return -1;
+}
+
+/* Define to assert(x) if you want to break on unimplemented formats. */
+#define rstbtt_GPOS_TODO_assert(x) assert(x)
+
+RFONT_API i32 rstbtt__GetGlyphGPOSInfoAdvance(const rstbtt_fontinfo *info, int glyph1, int glyph2)
+{
+ u16 lookupListOffset;
+ u8 *lookupList;
+ u16 lookupCount;
+ u8 *data;
+ i32 i;
+
+ if (!info->gpos) return 0;
+
+ data = info->data + info->gpos;
+
+ if (RFONT_USHORT(data, 0) != 1) return 0; /* Major version 1 */
+ if (RFONT_USHORT(data, 2) != 0) return 0; /* Minor version 0 */
+
+ lookupListOffset = RFONT_USHORT(data, 8);
+ lookupList = data + lookupListOffset;
+ lookupCount = RFONT_USHORT(lookupList, 0);
+
+ for (i=0; i<lookupCount; ++i) {
+ u16 lookupOffset = RFONT_USHORT(lookupList, 2 + 2 * i);
+ u8 *lookupTable = lookupList + lookupOffset;
+
+ u16 lookupType = RFONT_USHORT(lookupTable, 0);
+ u16 subTableCount = RFONT_USHORT(lookupTable, 4);
+ u8 *subTableOffsets = lookupTable + 6;
+ switch(lookupType) {
+ case 2: { /* Pair Adjustment Positioning Subtable */
+ i32 sti;
+ for (sti=0; sti<subTableCount; sti++) {
+ u16 subtableOffset = RFONT_USHORT(subTableOffsets, 2 * sti);
+ u8 *table = lookupTable + subtableOffset;
+ u16 posFormat = RFONT_USHORT(table, 0);
+ u16 coverageOffset = RFONT_USHORT(table, 2);
+ i32 coverageIndex = rstbtt__GetCoverageIndex(table + coverageOffset, glyph1);
+ if (coverageIndex == -1) continue;
+
+ switch (posFormat) {
+ case 1: {
+ i32 l, r, m;
+ int straw, needle;
+ u16 valueFormat1 = RFONT_USHORT(table, 4);
+ u16 valueFormat2 = RFONT_USHORT(table, 6);
+ i32 valueRecordPairSizeInBytes = 2;
+ u16 pairSetCount = RFONT_USHORT(table, 8);
+ u16 pairPosOffset = RFONT_USHORT(table, 10 + 2 * coverageIndex);
+ u8 *pairValueTable = table + pairPosOffset;
+ u16 pairValueCount = RFONT_USHORT(pairValueTable, 0);
+ u8 *pairValueArray = pairValueTable + 2;
+ /* TODO: Support more formats. */
+ rstbtt_GPOS_TODO_assert(valueFormat1 == 4);
+ if (valueFormat1 != 4) return 0;
+ rstbtt_GPOS_TODO_assert(valueFormat2 == 0);
+ if (valueFormat2 != 0) return 0;
+
+ assert(coverageIndex < pairSetCount);
+ rstbtt__NOTUSED(pairSetCount);
+
+ needle=glyph2;
+ r=pairValueCount-1;
+ l=0;
+
+ /* Binary search. */
+ while (l <= r) {
+ u16 secondGlyph;
+ u8 *pairValue;
+ m = (l + r) >> 1;
+ pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m;
+ secondGlyph = RFONT_USHORT(pairValue, 0);
+ straw = secondGlyph;
+ if (needle < straw)
+ r = m - 1;
+ else if (needle > straw)
+ l = m + 1;
+ else {
+ i16 xAdvance = RFONT_SHORT(pairValue, 2);
+ return xAdvance;
+ }
+ }
+ } break;
+
+ case 2: {
+ u16 valueFormat1 = RFONT_USHORT(table, 4);
+ u16 valueFormat2 = RFONT_USHORT(table, 6);
+
+ u16 classDef1Offset = RFONT_USHORT(table, 8);
+ u16 classDef2Offset = RFONT_USHORT(table, 10);
+ int glyph1class = rstbtt__GetGlyphClass(table + classDef1Offset, glyph1);
+ int glyph2class = rstbtt__GetGlyphClass(table + classDef2Offset, glyph2);
+
+ u16 class1Count = RFONT_USHORT(table, 12);
+ u16 class2Count = RFONT_USHORT(table, 14);
+ assert(glyph1class < class1Count);
+ assert(glyph2class < class2Count);
+
+ /* TODO: Support more formats. */
+ rstbtt_GPOS_TODO_assert(valueFormat1 == 4);
+ if (valueFormat1 != 4) return 0;
+ rstbtt_GPOS_TODO_assert(valueFormat2 == 0);
+ if (valueFormat2 != 0) return 0;
+
+ if (glyph1class >= 0 && glyph1class < class1Count && glyph2class >= 0 && glyph2class < class2Count) {
+ u8 *class1Records = table + 16;
+ u8 *class2Records = class1Records + 2 * (glyph1class * class2Count);
+ i16 xAdvance = RFONT_SHORT(class2Records, 2 * glyph2class);
+ return xAdvance;
+ }
+ } break;
+
+ default: {
+ /* There are no other cases. */
+ assert(0);
+ break;
+ };
+ }
+ }
+ break;
+ };
+
+ default:
+ /* TODO: Implement other stuff. */
+ break;
+ }
+ }
+
+ return 0;
+}
+
+RFONT_API int rstbtt_GetGlyphKernAdvance(const rstbtt_fontinfo *info, int g1, int g2)
+{
+ int xAdvance = 0;
+
+ if (info->gpos)
+ xAdvance += rstbtt__GetGlyphGPOSInfoAdvance(info, g1, g2);
+ else if (info->kern)
+ xAdvance += rstbtt__GetGlyphKernInfoAdvance(info, g1, g2);
+
+ return xAdvance;
+}
+
+typedef struct rstbtt__hheap_chunk
+{
+ struct rstbtt__hheap_chunk *next;
+} rstbtt__hheap_chunk;
+
+typedef struct rstbtt__hheap
+{
+ struct rstbtt__hheap_chunk *head;
+ void *first_free;
+ int num_remaining_in_head_chunk;
+} rstbtt__hheap;
+
+RFONT_API void *rstbtt__hheap_alloc(rstbtt__hheap *hh, size_t size)
+{
+ if (hh->first_free) {
+ void *p = hh->first_free;
+ hh->first_free = * (void **) p;
+ return p;
+ } else {
+ if (hh->num_remaining_in_head_chunk == 0) {
+ int count = (size < 32 ? 2000 : size < 128 ? 800 : 100);
+ rstbtt__hheap_chunk *c = (rstbtt__hheap_chunk *) RFONT_MALLOC(sizeof(rstbtt__hheap_chunk) + size * (size_t)count);
+ if (c == NULL)
+ return NULL;
+ c->next = hh->head;
+ hh->head = c;
+ hh->num_remaining_in_head_chunk = count;
+ }
+ --hh->num_remaining_in_head_chunk;
+ return (char *) (hh->head) + sizeof(rstbtt__hheap_chunk) + size * (size_t)hh->num_remaining_in_head_chunk;
+ }
+}
+
+RFONT_API void rstbtt__hheap_free(rstbtt__hheap *hh, void *p)
+{
+ *(void **) p = hh->first_free;
+ hh->first_free = p;
+}
+
+RFONT_API void rstbtt__hheap_cleanup(rstbtt__hheap *hh)
+{
+ rstbtt__hheap_chunk *c = hh->head;
+ while (c) {
+ rstbtt__hheap_chunk *n = c->next;
+ RFONT_FREE(c);
+ c = n;
+ }
+}
+
+typedef struct rstbtt__edge {
+ float x0,y0, x1,y1;
+ int invert;
+} rstbtt__edge;
+
+
+typedef struct rstbtt__active_edge
+{
+ struct rstbtt__active_edge *next;
+ float fx,fdx,fdy;
+ float direction;
+ float sy;
+ float ey;
+} rstbtt__active_edge;
+
+
+RFONT_API rstbtt__active_edge *rstbtt__new_active(rstbtt__hheap *hh, rstbtt__edge *e, int off_x, float start_point) {
+ rstbtt__active_edge *z = (rstbtt__active_edge *) rstbtt__hheap_alloc(hh, sizeof(*z));
+ float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
+ assert(z != NULL);
+ /* assert(e->y0 <= start_point); */
+ if (!z) return z;
+ z->fdx = dxdy;
+ z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f;
+ z->fx = e->x0 + dxdy * (start_point - e->y0);
+ z->fx -= (float)off_x;
+ z->direction = e->invert ? 1.0f : -1.0f;
+ z->sy = e->y0;
+ z->ey = e->y1;
+ z->next = 0;
+ return z;
+}
+
+RFONT_API void rstbtt__handle_clipped_edge(float *scanline, int x, rstbtt__active_edge *e, float x0, float y0, float x1, float y1) {
+ if (y0 == y1) return;
+ assert(y0 < y1);
+ assert(e->sy <= e->ey);
+ if (y0 > e->ey) return;
+ if (y1 < e->sy) return;
+ if (y0 < e->sy) {
+ x0 += (x1-x0) * (e->sy - y0) / (y1-y0);
+ y0 = e->sy;
+ }
+ if (y1 > e->ey) {
+ x1 += (x1-x0) * (e->ey - y1) / (y1-y0);
+ y1 = e->ey;
+ }
+
+ if (x0 == x)
+ assert(x1 <= x+1);
+ else if (x0 == x+1)
+ assert(x1 >= x);
+ else if (x0 <= x)
+ assert(x1 <= x);
+ else if (x0 >= x+1)
+ assert(x1 >= x+1);
+ else
+ assert(x1 >= x && x1 <= x+1);
+
+ if (x0 <= x && x1 <= x)
+ scanline[x] += e->direction * (y1-y0);
+ else if (x0 >= x+1 && x1 >= x+1)
+ ;
+ else {
+ assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1);
+ scanline[x] += e->direction * (y1-y0) * (1-((x0 - (float)x)+(x1 - (float)x))/2.0f); /* coverage = 1 - average x position */
+ }
+}
+
+RFONT_API void rstbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, rstbtt__active_edge *e, float y_top)
+{
+ float y_bottom = y_top+1;
+
+ while (e) {
+ /* brute force every pixel
+ compute intersection points with top & bottom */
+ assert(e->ey >= y_top);
+
+ if (e->fdx == 0) {
+ float x0 = e->fx;
+ if (x0 < len) {
+ if (x0 >= 0) {
+ rstbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom);
+ rstbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom);
+ } else {
+ rstbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom);
+ }
+ }
+ } else {
+ float x0 = e->fx;
+ float dx = e->fdx;
+ float xb = x0 + dx;
+ float x_top, x_bottom;
+ float sy0,sy1;
+ float dy = e->fdy;
+ assert(e->sy <= y_bottom && e->ey >= y_top);
+
+ if (e->sy > y_top) {
+ x_top = x0 + dx * (e->sy - y_top);
+ sy0 = e->sy;
+ } else {
+ x_top = x0;
+ sy0 = y_top;
+ }
+ if (e->ey < y_bottom) {
+ x_bottom = x0 + dx * (e->ey - y_top);
+ sy1 = e->ey;
+ } else {
+ x_bottom = xb;
+ sy1 = y_bottom;
+ }
+
+ if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) {
+ /* from here on, we don't have to range check x values */
+
+ if ((int) x_top == (int) x_bottom) {
+ float height;
+ /* simple case, only spans one pixel */
+ int x = (int) x_top;
+ height = sy1 - sy0;
+ assert(x >= 0 && x < len);
+ scanline[x] += e->direction * (float)((1-((x_top - (float)x) + (x_bottom-(float)x))/2)) * height;
+ scanline_fill[x] += e->direction * height; /* everything right of this pixel is filled */
+ } else {
+ int x,x1,x2;
+ float y_crossing, step, sign, area;
+ /* covers 2+ pixels */
+ if (x_top > x_bottom) {
+ /* flip scanline vertically; signed area is the same */
+ float t;
+ sy0 = y_bottom - (sy0 - y_top);
+ sy1 = y_bottom - (sy1 - y_top);
+ t = sy0, sy0 = sy1, sy1 = t;
+ t = x_bottom, x_bottom = x_top, x_top = t;
+ dx = -dx;
+ dy = -dy;
+ t = x0, x0 = xb, xb = t;
+ }
+
+ x1 = (int) x_top;
+ x2 = (int) x_bottom;
+ /* compute intersection with y axis at x1+1 */
+ y_crossing = ((float)x1 + 1.0f - (float)x0) * dy + y_top;
+
+ sign = e->direction;
+ /* area of the rectangle covered from y0..y_crossing */
+ area = sign * (y_crossing-sy0);
+ /* area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) */
+ scanline[x1] += area * (1-(((float)x_top - (float)x1)+ ((float)(x1+1-x1)) / 2.0f));
+
+ step = sign * dy;
+ for (x = x1+1; x < x2; ++x) {
+ scanline[x] += area + step/2;
+ area += step;
+ }
+ y_crossing += dy * ((float)x2 - (float)(x1+1));
+
+ assert(fabs(area) <= 1.01f);
+
+ scanline[x2] += area + sign * (1.0f-((float)((float)x2 - (float)x2) + ((float)x_bottom - (float)x2)) / 2.0f) * (float)(sy1-y_crossing);
+
+ scanline_fill[x2] += sign * (sy1-sy0);
+ }
+ } else {
+ int x;
+ for (x=0; x < len; ++x) {
+
+ float y0 = y_top;
+ float x1 = (float) (x);
+ float x2 = (float) (x+1);
+ float x3 = xb;
+ float y3 = y_bottom;
+
+ float y1 = ((float)x - x0) / dx + y_top;
+ float y2 = ((float)x + 1 - x0) / dx + y_top;
+
+ if (x0 < x1 && x3 > x2) { /*three segments descending down-right */
+ rstbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
+ rstbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2);
+ rstbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
+ } else if (x3 < x1 && x0 > x2) { /* three segments descending down-left */
+ rstbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
+ rstbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1);
+ rstbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
+ } else if (x0 < x1 && x3 > x1) { /* two segments across x, down-right */
+ rstbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
+ rstbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
+ } else if (x3 < x1 && x0 > x1) { /* two segments across x, down-left */
+ rstbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
+ rstbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
+ } else if (x0 < x2 && x3 > x2) { /* two segments across x+1, down-right */
+ rstbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
+ rstbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
+ } else if (x3 < x2 && x0 > x2) { /* two segments across x+1, down-left */
+ rstbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
+ rstbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
+ } else { /* one segment */
+ rstbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3);
+ }
+ }
+ }
+ }
+ e = e->next;
+ }
+}
+
+/* directly AA rasterize edges w/o supersampling */
+RFONT_API void rstbtt__rasterize_sorted_edges(rstbtt__bitmap *result, rstbtt__edge *e, int n, int vsubsample, int off_x, int off_y)
+{
+ rstbtt__hheap hh = { 0, 0, 0 };
+ rstbtt__active_edge *active = NULL;
+ int y,j=0, i;
+ float scanline_data[129], *scanline, *scanline2;
+
+ rstbtt__NOTUSED(vsubsample);
+
+ if (result->w > 64)
+ scanline = (float *) RFONT_MALLOC((size_t)(result->w*2+1) * sizeof(float));
+ else
+ scanline = scanline_data;
+
+ scanline2 = scanline + result->w;
+
+ y = off_y;
+ e[n].y0 = (float) (off_y + result->h) + 1;
+
+ while (j < result->h) {
+ /* find center of pixel for this scanline */
+ float scan_y_top = (float)y + 0.0f;
+ float scan_y_bottom = (float)y + 1.0f;
+ rstbtt__active_edge **step = &active;
+
+ RFONT_MEMSET(scanline , 0, (size_t)result->w * sizeof(scanline[0]));
+ RFONT_MEMSET(scanline2, 0, (size_t)(result->w + 1) * sizeof(scanline[0]));
+
+ /* update all active edges;
+ remove all active edges that terminate before the top of this scanline */
+ while (*step) {
+ rstbtt__active_edge * z = *step;
+ if (z->ey <= scan_y_top) {
+ *step = z->next; /* delete from list (/) */
+ assert(z->direction);
+ z->direction = 0;
+ rstbtt__hheap_free(&hh, z);
+ } else {
+ step = &((*step)->next); /* advance through list */
+ }
+ }
+
+ /* insert all edges that start before the bottom of this scanline */
+ while (e->y0 <= scan_y_bottom) {
+ if (e->y0 != e->y1) {
+ rstbtt__active_edge *z = rstbtt__new_active(&hh, e, off_x, scan_y_top);
+ if (z != NULL) {
+ if (j == 0 && off_y != 0) {
+ if (z->ey < scan_y_top) {
+ /* this can happen due to subpixel positioning and some kind of fp rounding error i think */
+ z->ey = scan_y_top;
+ }
+ }
+ assert(z->ey >= scan_y_top); /* if we get really unlucky a tiny bit of an edge can be out of bounds */
+ /* insert at front */
+ z->next = active;
+ active = z;
+ }
+ }
+ ++e;
+ }
+
+ /* now process all active edges */
+ if (active)
+ rstbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top);
+
+ {
+ float sum = 0;
+ for (i=0; i < result->w; ++i) {
+ float k;
+ int m;
+ sum += scanline2[i];
+ k = scanline[i] + sum;
+ k = (float) fabs(k)*255 + 0.5f;
+ m = (int) k;
+ if (m > 255) m = 255;
+ result->pixels[j*result->stride + i] = (unsigned char) m;
+ }
+ }
+ /* advance all the edges */
+ step = &active;
+ while (*step) {
+ rstbtt__active_edge *z = *step;
+ z->fx += z->fdx; /* advance to position for current scanline */
+ step = &((*step)->next); /* advance through list */
+ }
+
+ ++y;
+ ++j;
+ }
+
+ rstbtt__hheap_cleanup(&hh);
+
+ if (scanline != scanline_data)
+ RFONT_FREE(scanline);
+}
+
+#define rstbtt__COMPARE(a,b) ((a)->y0 < (b)->y0)
+
+RFONT_API void rstbtt__sort_edges_ins_sort(rstbtt__edge *p, int n)
+{
+ int i,j;
+ for (i=1; i < n; ++i) {
+ rstbtt__edge t = p[i], *a = &t;
+ j = i;
+ while (j > 0) {
+ rstbtt__edge *b = &p[j-1];
+ int c = rstbtt__COMPARE(a,b);
+ if (!c) break;
+ p[j] = p[j-1];
+ --j;
+ }
+ if (i != j)
+ p[j] = t;
+ }
+}
+
+RFONT_API void rstbtt__sort_edges_quicksort(rstbtt__edge *p, int n)
+{
+ /* threshold for transitioning to insertion sort */
+ while (n > 12) {
+ rstbtt__edge t;
+ int c01,c12,c,m,i,j;
+
+ /* compute median of three */
+ m = n >> 1;
+ c01 = rstbtt__COMPARE(&p[0],&p[m]);
+ c12 = rstbtt__COMPARE(&p[m],&p[n-1]);
+ /* if 0 >= mid >= end, or 0 < mid < end, then use mid */
+ if (c01 != c12) {
+ /* otherwise, we'll need to swap something else to middle */
+ int z;
+ c = rstbtt__COMPARE(&p[0],&p[n-1]);
+ /* 0>mid && mid<n: 0>n => n; 0<n => 0 */
+ /* 0<mid && mid>n: 0>n => 0; 0<n => n */
+ z = (c == c12) ? 0 : n-1;
+ t = p[z];
+ p[z] = p[m];
+ p[m] = t;
+ }
+ /* now p[m] is the median-of-three */
+ /* swap it to the beginning so it won't move around */
+ t = p[0];
+ p[0] = p[m];
+ p[m] = t;
+
+ /* partition loop */
+ i=1;
+ j=n-1;
+ for(;;) {
+ /* handling of equality is crucial here */
+ /* for sentinels & efficiency with duplicates */
+ for (;;++i) {
+ if (!rstbtt__COMPARE(&p[i], &p[0])) break;
+ }
+ for (;;--j) {
+ if (!rstbtt__COMPARE(&p[0], &p[j])) break;
+ }
+ /* make sure we haven't crossed */
+ if (i >= j) break;
+ t = p[i];
+ p[i] = p[j];
+ p[j] = t;
+
+ ++i;
+ --j;
+ }
+ /* recurse on smaller side, iterate on larger */
+ if (j < (n-i)) {
+ rstbtt__sort_edges_quicksort(p,j);
+ p = p+i;
+ n = n-i;
+ } else {
+ rstbtt__sort_edges_quicksort(p+i, n-i);
+ n = j;
+ }
+ }
+}
+
+RFONT_API void rstbtt__sort_edges(rstbtt__edge *p, int n)
+{
+ rstbtt__sort_edges_quicksort(p, n);
+ rstbtt__sort_edges_ins_sort(p, n);
+}
+
+typedef struct
+{
+ float x,y;
+} rstbtt__point;
+
+RFONT_API void rstbtt__rasterize(rstbtt__bitmap *result, rstbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert)
+{
+ float y_scale_inv = invert ? -scale_y : scale_y;
+ rstbtt__edge *e;
+ int n,i,j,k,m;
+#if rstbtt_RASTERIZER_VERSION == 1
+ int vsubsample = result->h < 8 ? 15 : 5;
+#elif rstbtt_RASTERIZER_VERSION == 2
+ int vsubsample = 1;
+#else
+ #error "Unrecognized value of rstbtt_RASTERIZER_VERSION"
+#endif
+ /* vsubsample should divide 255 evenly; otherwise we won't reach full opacity
+
+ now we have to blow out the windings into explicit edge lists */
+ n = 0;
+ for (i=0; i < windings; ++i)
+ n += wcount[i];
+
+ e = (rstbtt__edge *) RFONT_MALLOC(sizeof(*e) * (size_t)(n+1)); /* add an extra one as a sentinel */
+ if (e == 0) return;
+ n = 0;
+
+ m=0;
+ for (i=0; i < windings; ++i) {
+ rstbtt__point *p = pts + m;
+ m += wcount[i];
+ j = wcount[i]-1;
+ for (k=0; k < wcount[i]; j=k++) {
+ int a=k,b=j;
+ /* skip the edge if horizontal */
+ if (p[j].y == p[k].y)
+ continue;
+ /* add edge from j to k to the list */
+ e[n].invert = 0;
+ if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) {
+ e[n].invert = 1;
+ a=j,b=k;
+ }
+ e[n].x0 = p[a].x * scale_x + shift_x;
+ e[n].y0 = (p[a].y * y_scale_inv + shift_y) * (float)vsubsample;
+ e[n].x1 = p[b].x * scale_x + shift_x;
+ e[n].y1 = (p[b].y * y_scale_inv + shift_y) * (float)vsubsample;
+ ++n;
+ }
+ }
+
+ /* now sort the edges by their highest point (should snap to integer, and then by x)
+ rstbtt_sort(e, n, sizeof(e[0]), rstbtt__edge_compare); */
+ rstbtt__sort_edges(e, n);
+
+ /* now, traverse the scanlines and find the intersections on each scanline, use xor winding rule */
+ rstbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y);
+
+ RFONT_FREE(e);
+}
+
+RFONT_API void rstbtt__add_point(rstbtt__point *points, int n, float x, float y)
+{
+ if (!points) return; /* during first pass, it's unallocated */
+ points[n].x = x;
+ points[n].y = y;
+}
+
+/* tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching */
+RFONT_API int rstbtt__tesselate_curve(rstbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n)
+{
+ /* midpoint */
+ float mx = (x0 + 2*x1 + x2)/4;
+ float my = (y0 + 2*y1 + y2)/4;
+ /* versus directly drawn line */
+ float dx = (x0+x2)/2 - mx;
+ float dy = (y0+y2)/2 - my;
+ if (n > 16) /* 65536 segments on one curve better be enough! */
+ return 1;
+ if (dx*dx+dy*dy > objspace_flatness_squared) { /* half-pixel error allowed... need to be smaller if AA */
+ rstbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1);
+ rstbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1);
+ } else {
+ rstbtt__add_point(points, *num_points,x2,y2);
+ *num_points = *num_points+1;
+ }
+ return 1;
+}
+
+RFONT_API void rstbtt__tesselate_cubic(rstbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n)
+{
+ /* @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough */
+ float dx0 = x1-x0;
+ float dy0 = y1-y0;
+ float dx1 = x2-x1;
+ float dy1 = y2-y1;
+ float dx2 = x3-x2;
+ float dy2 = y3-y2;
+ float dx = x3-x0;
+ float dy = y3-y0;
+ float longlen = (float) (sqrt(dx0*dx0+dy0*dy0)+sqrt(dx1*dx1+dy1*dy1)+sqrt(dx2*dx2+dy2*dy2));
+ float shortlen = (float) sqrt(dx*dx+dy*dy);
+ float flatness_squared = longlen*longlen-shortlen*shortlen;
+
+ if (n > 16) /* 65536 segments on one curve better be enough! */
+ return;
+
+ if (flatness_squared > objspace_flatness_squared) {
+ float x01 = (x0+x1)/2;
+ float y01 = (y0+y1)/2;
+ float x12 = (x1+x2)/2;
+ float y12 = (y1+y2)/2;
+ float x23 = (x2+x3)/2;
+ float y23 = (y2+y3)/2;
+
+ float xa = (x01+x12)/2;
+ float ya = (y01+y12)/2;
+ float xb = (x12+x23)/2;
+ float yb = (y12+y23)/2;
+
+ float mx = (xa+xb)/2;
+ float my = (ya+yb)/2;
+
+ rstbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1);
+ rstbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1);
+ } else {
+ rstbtt__add_point(points, *num_points,x3,y3);
+ *num_points = *num_points+1;
+ }
+}
+
+/* returns number of contours */
+RFONT_API rstbtt__point *rstbtt_FlattenCurves(rstbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours)
+{
+ rstbtt__point *points=0;
+ int num_points=0;
+
+ float objspace_flatness_squared = objspace_flatness * objspace_flatness;
+ int i,n=0,start=0, pass;
+
+ /* count how many "moves" there are to get the contour count */
+ for (i=0; i < num_verts; ++i)
+ if (vertices[i].type == rstbtt_vmove)
+ ++n;
+
+ *num_contours = n;
+ if (n == 0) return 0;
+
+ *contour_lengths = (int *) RFONT_MALLOC((size_t)(sizeof(**contour_lengths) * (size_t)n));
+
+ if (*contour_lengths == 0) {
+ *num_contours = 0;
+ return 0;
+ }
+
+ /* make two passes through the points so we don't need to realloc */
+ for (pass=0; pass < 2; ++pass) {
+ float x=0,y=0;
+ if (pass == 1) {
+ points = (rstbtt__point *) RFONT_MALLOC((size_t)num_points * sizeof(points[0]));
+ if (points == NULL) goto error;
+ }
+ num_points = 0;
+ n= -1;
+ for (i=0; i < num_verts; ++i) {
+ switch (vertices[i].type) {
+ case rstbtt_vmove:
+ /* start the next contour */
+ if (n >= 0)
+ (*contour_lengths)[n] = num_points - start;
+ ++n;
+ start = num_points;
+
+ x = vertices[i].x, y = vertices[i].y;
+ rstbtt__add_point(points, num_points++, x,y);
+ break;
+ case rstbtt_vline:
+ x = vertices[i].x, y = vertices[i].y;
+ rstbtt__add_point(points, num_points++, x, y);
+ break;
+ case rstbtt_vcurve:
+ rstbtt__tesselate_curve(points, &num_points, x,y,
+ vertices[i].cx, vertices[i].cy,
+ vertices[i].x, vertices[i].y,
+ objspace_flatness_squared, 0);
+ x = vertices[i].x, y = vertices[i].y;
+ break;
+ case rstbtt_vcubic:
+ rstbtt__tesselate_cubic(points, &num_points, x,y,
+ vertices[i].cx, vertices[i].cy,
+ vertices[i].cx1, vertices[i].cy1,
+ vertices[i].x, vertices[i].y,
+ objspace_flatness_squared, 0);
+ x = vertices[i].x;
+ y = vertices[i].y;
+ break;
+ }
+ }
+ (*contour_lengths)[n] = num_points - start;
+ }
+
+ return points;
+error:
+ RFONT_FREE(points);
+ RFONT_FREE(*contour_lengths);
+ *contour_lengths = 0;
+ *num_contours = 0;
+ return NULL;
+}
+
+RFONT_API void rstbtt_Rasterize(rstbtt__bitmap *result, float flatness_in_pixels, rstbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert)
+{
+ float scale = scale_x > scale_y ? scale_y : scale_x;
+ int winding_count = 0;
+ int *winding_lengths = NULL;
+ rstbtt__point *windings = rstbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count);
+ if (windings) {
+ rstbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert);
+ RFONT_FREE(winding_lengths);
+ RFONT_FREE(windings);
+ }
+}
+
+RFONT_API void rstbtt_GetGlyphBitmapBoxSubpixel(const rstbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
+{
+ int x0=0,y0=0,x1,y1; /* =0 suppresses compiler warning */
+ if (!rstbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) {
+ /* e.g. space character */
+ if (ix0) *ix0 = 0;
+ if (iy0) *iy0 = 0;
+ if (ix1) *ix1 = 0;
+ if (iy1) *iy1 = 0;
+ } else {
+ /* move to integral bboxes (treating pixels as little squares, what pixels get touched)? */
+ if (ix0) *ix0 = (int)floor( (float)x0 * scale_x + shift_x);
+ if (iy0) *iy0 = (int)floor( (float)-y1 * scale_y + shift_y);
+ if (ix1) *ix1 = (int)ceil ( (float)x1 * scale_x + shift_x);
+ if (iy1) *iy1 = (int)ceil ((float)-y0 * scale_y + shift_y);
+ }
+}
+
+RFONT_API unsigned char *rstbtt_GetGlyphBitmapSubpixel(const rstbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff)
+{
+ int ix0,iy0,ix1,iy1;
+ rstbtt__bitmap gbm;
+ rstbtt_vertex *vertices;
+ int num_verts = rstbtt_GetGlyphShape(info, glyph, &vertices);
+
+ if (scale_x == 0) scale_x = scale_y;
+ if (scale_y == 0) {
+ if (scale_x == 0) {
+ RFONT_FREE(vertices);
+ return NULL;
+ }
+ scale_y = scale_x;
+ }
+
+ rstbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1);
+
+ /* now we get the size */
+ gbm.w = (ix1 - ix0);
+ gbm.h = (iy1 - iy0);
+ gbm.pixels = NULL; /* in case we error */
+
+ if (width ) *width = gbm.w;
+ if (height) *height = gbm.h;
+ if (xoff ) *xoff = ix0;
+ if (yoff ) *yoff = iy0;
+
+ if (gbm.w && gbm.h) {
+ gbm.pixels = (unsigned char *) RFONT_MALLOC((size_t)(gbm.w * gbm.h));
+ if (gbm.pixels) {
+ gbm.stride = gbm.w;
+
+ rstbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1);
+ }
+ }
+ RFONT_FREE(vertices);
+ return gbm.pixels;
+}
+
+
+RFONT_API int rstbtt_InitFont(rstbtt_fontinfo *info, const unsigned char* const_data, int fontstart)
+{
+ unsigned char* data = (unsigned char*)const_data;
+
+ u32 cmap, t;
+ i32 i,numTables;
+
+ info->data = (unsigned char*)data;
+ info->fontstart = fontstart;
+ info->cff = rstbtt__new_buf(NULL, 0);
+
+ cmap = rstbtt__find_table(data, (u32)fontstart, "cmap"); /* required */
+ info->loca = (int)rstbtt__find_table(data, (u32)fontstart, "loca"); /* required */
+ info->head = (int)rstbtt__find_table(data, (u32)fontstart, "head"); /* required */
+ info->glyf = (int)rstbtt__find_table(data, (u32)fontstart, "glyf"); /* required */
+ info->hhea = (int)rstbtt__find_table(data, (u32)fontstart, "hhea"); /* required */
+ info->hmtx = (int)rstbtt__find_table(data, (u32)fontstart, "hmtx"); /* required */
+ info->kern = (int)rstbtt__find_table(data, (u32)fontstart, "kern"); /* not required */
+ info->gpos = (int)rstbtt__find_table(data, (u32)fontstart, "GPOS"); /* not required */
+
+ if (!cmap || !info->head || !info->hhea || !info->hmtx)
+ return 0;
+ if (info->glyf) {
+ /* required for truetype */
+ if (!info->loca) return 0;
+ } else {
+ /* initialization for CFF / Type2 fonts (OTF) */
+ rstbtt__buf b, topdict, topdictidx;
+ u32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0;
+ u32 cff;
+
+ cff = rstbtt__find_table(data, (u32)fontstart, "CFF ");
+ if (!cff) return 0;
+
+ info->fontdicts = rstbtt__new_buf(NULL, 0);
+ info->fdselect = rstbtt__new_buf(NULL, 0);
+
+ /* @TODO this should use size from table (not 512MB) */
+ info->cff = rstbtt__new_buf(data+cff, 512*1024*1024);
+ b = info->cff;
+
+ /* read the header */
+ rstbtt__buf_skip(&b, 2);
+ rstbtt__buf_seek(&b, rstbtt__buf_get8(&b)); /* hdrsize */
+
+ /* @TODO the name INDEX could list multiple fonts,
+ but we just use the first one. */
+ rstbtt__cff_get_index(&b); /* name INDEX */
+ topdictidx = rstbtt__cff_get_index(&b);
+ topdict = rstbtt__cff_index_get(topdictidx, 0);
+ rstbtt__cff_get_index(&b); /* string INDEX */
+ info->gsubrs = rstbtt__cff_get_index(&b);
+
+ rstbtt__dict_get_ints(&topdict, 17, 1, &charstrings);
+ rstbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype);
+ rstbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff);
+ rstbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff);
+ info->subrs = rstbtt__get_subrs(b, topdict);
+
+ /* weonly support Type 2 charstrings */
+ if (cstype != 2) return 0;
+ if (charstrings == 0) return 0;
+
+ if (fdarrayoff) {
+ /* looks like a CID font */
+ if (!fdselectoff) return 0;
+ rstbtt__buf_seek(&b, (int)fdarrayoff);
+ info->fontdicts = rstbtt__cff_get_index(&b);
+ info->fdselect = rstbtt__buf_range(&b, (int)fdselectoff, (int)b.size - (int)fdselectoff);
+ }
+
+ rstbtt__buf_seek(&b, (int)charstrings);
+ info->charstrings = rstbtt__cff_get_index(&b);
+ }
+
+ t = rstbtt__find_table(data, (u32)fontstart, "maxp");
+ if (t)
+ info->numGlyphs = RFONT_USHORT(data, t + 4);
+ else
+ info->numGlyphs = 0xffff;
+
+ info->svg = -1;
+
+ numTables = RFONT_USHORT(data, cmap + 2);
+ info->index_map = 0;
+ for (i=0; i < numTables; ++i) {
+ u32 encoding_record = (u32)(cmap + 4 + 8 * (u32)i);
+ /* find an encoding we understand: */
+ switch(RFONT_USHORT(data, encoding_record)) {
+ case rstbtt_PLATFORM_ID_MICROSOFT:
+ switch (RFONT_USHORT(data, (int)encoding_record+2)) {
+ case rstbtt_MS_EID_UNICODE_BMP:
+ case rstbtt_MS_EID_UNICODE_FULL:
+ /* MS/Unicode */
+ info->index_map = (int)(cmap + RFONT_ULONG(data, encoding_record + 4));
+ break;
+ }
+ break;
+ case rstbtt_PLATFORM_ID_UNICODE:
+ /* Mac/iOS has these */
+ /* all the encodingIDs are unicode, so we don't bother to check it */
+ info->index_map = (int)(cmap + RFONT_ULONG(data, encoding_record+4));
+ break;
+ }
+ }
+ if (info->index_map == 0)
+ return 0;
+
+ info->indexToLocFormat = RFONT_USHORT(((u8*)data), info->head + 50);
+ return 1;
+}
+
+RFONT_API int rstbtt_FindGlyphIndex(const rstbtt_fontinfo *info, int unicode_codepoint) {
+ u8 *data = info->data;
+ u32 index_map = (u32)info->index_map;
+
+ u16 format = RFONT_USHORT(data, index_map);
+ if (format == 0) { /* apple byte encoding */
+ i32 bytes = RFONT_USHORT(data, index_map + 2);
+ if (unicode_codepoint < bytes-6)
+ return RFONT_BYTE(data, (int)index_map + 6 + unicode_codepoint);
+ return 0;
+ } else if (format == 6) {
+ u32 first = RFONT_USHORT(data, index_map + 6);
+ u32 count = RFONT_USHORT(data, index_map + 8);
+ if ((u32) unicode_codepoint >= first && (u32) unicode_codepoint < (u32)(first + count))
+ return RFONT_USHORT(data, (int)index_map + 10 + (unicode_codepoint - (int)first)*2);
+ return 0;
+ } else if (format == 2) {
+ assert(0); /* @TODO: high-byte mapping for japanese/chinese/korean */
+ return 0;
+ } else if (format == 4) { /* standard mapping for windows fonts: binary search collection of ranges */
+ u16 segcount = RFONT_USHORT(data, index_map+6) >> 1;
+ u16 searchRange = RFONT_USHORT(data, index_map+8) >> 1;
+ u16 entrySelector = RFONT_USHORT(data, index_map+10);
+ u16 rangeShift = RFONT_USHORT(data, index_map+12) >> 1;
+
+ /* do a binary search of the segments */
+ u32 endCount = index_map + 14;
+ u32 search = endCount;
+
+ if (unicode_codepoint > 0xffff)
+ return 0;
+
+ /* they lie from endCount .. endCount + segCount
+ but searchRange is the nearest power of two, so... */
+ if (unicode_codepoint >= RFONT_USHORT(data, search + rangeShift*2))
+ search += rangeShift*2;
+
+ /* now decrement to bias correctly to find smallest */
+ search -= 2;
+ while (entrySelector) {
+ u16 end;
+ searchRange >>= 1;
+ end = RFONT_USHORT(data, search + searchRange*2);
+ if (unicode_codepoint > end)
+ search += searchRange*2;
+ --entrySelector;
+ }
+ search += 2;
+
+ {
+ u16 offset, start;
+ u16 item = (u16) ((search - endCount) >> 1);
+
+ assert(unicode_codepoint <= RFONT_USHORT(data, endCount + 2*item));
+ start = RFONT_USHORT(data, index_map + 14 + segcount*2 + 2 + 2*item);
+ if (unicode_codepoint < start)
+ return 0;
+
+ offset = RFONT_USHORT(data, index_map + 14 + segcount*6 + 2 + 2*item);
+ if (offset == 0)
+ return (u16) (unicode_codepoint + RFONT_SHORT(data, index_map + 14 + segcount*4 + 2 + 2*item));
+
+ return RFONT_USHORT(data, offset + (unicode_codepoint-start)*2 + (int)index_map + 14 + segcount*6 + 2 + 2*item);
+ }
+ } else if (format == 12 || format == 13) {
+ u32 ngroups = RFONT_ULONG(data, index_map+12);
+ i32 low,high;
+ low = 0; high = (i32)ngroups;
+ /* Binary search the right group. */
+ while (low < high) {
+ i32 mid = low + ((high-low) >> 1); /* rounds down, so low <= mid < high */
+ u32 start_char = RFONT_ULONG(data, (int)index_map + 16 + mid * 12);
+ u32 end_char = RFONT_ULONG(data, (int)index_map + 16 + mid * 12 + 4);
+ if ((u32) unicode_codepoint < start_char)
+ high = mid;
+ else if ((u32) unicode_codepoint > end_char)
+ low = mid+1;
+ else {
+ u32 start_glyph = RFONT_ULONG(data, (int)index_map + 16 + mid * 12 + 8);
+ if (format == 12)
+ return (int)((int)start_glyph + unicode_codepoint - (int)start_char);
+ else /* format == 13 */
+ return (int)start_glyph;
+ }
+ }
+ return 0; /* not found */
+ }
+ /* @TODO */
+ assert(0);
+ return 0;
+}
+
+RFONT_API int rstbtt__GetGlyfOffset(const rstbtt_fontinfo *info, int glyph_index)
+{
+ int g1,g2;
+
+ assert(!info->cff.size);
+
+ if (glyph_index >= info->numGlyphs) return -1; /* glyph index out of range */
+ if (info->indexToLocFormat >= 2) return -1; /* unknown index->glyph map format */
+
+ if (info->indexToLocFormat == 0) {
+ g1 = info->glyf + RFONT_USHORT(info->data, info->loca + glyph_index * 2) * 2;
+ g2 = info->glyf + RFONT_USHORT(info->data, info->loca + glyph_index * 2 + 2) * 2;
+ } else {
+ g1 = info->glyf + (int)RFONT_ULONG (info->data, info->loca + glyph_index * 4);
+ g2 = info->glyf + (int)RFONT_ULONG (info->data, info->loca + glyph_index * 4 + 4);
+ }
+
+ return g1==g2 ? -1 : g1; /* if length is 0, return -1 */
+}
+
+RFONT_API int rstbtt__GetGlyphInfoT2(const rstbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
+{
+ rstbtt__csctx c = rstbtt__CSCTX_INIT(1);
+ int r = rstbtt__run_charstring(info, glyph_index, &c);
+ if (x0) *x0 = r ? c.min_x : 0;
+ if (y0) *y0 = r ? c.min_y : 0;
+ if (x1) *x1 = r ? c.max_x : 0;
+ if (y1) *y1 = r ? c.max_y : 0;
+ return r ? c.num_vertices : 0;
+}
+
+RFONT_API int rstbtt_GetGlyphBox(const rstbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
+{
+ if (info->cff.size) {
+ rstbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1);
+ } else {
+ int g = rstbtt__GetGlyfOffset(info, glyph_index);
+ if (g < 0) return 0;
+
+ if (x0) *x0 = RFONT_SHORT(info->data, g + 2);
+ if (y0) *y0 = RFONT_SHORT(info->data, g + 4);
+ if (x1) *x1 = RFONT_SHORT(info->data, g + 6);
+ if (y1) *y1 = RFONT_SHORT(info->data, g + 8);
+ }
+ return 1;
+}
+
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+#endif /* n RFONT_EXTERNAL_STB */
+
+/*
+END of stb_truetype defines and source code required by RFont
+*/
+
+#endif /* RFONT_IMPLEMENTATION */
diff --git a/include/RGFW.h b/include/RGFW.h
new file mode 100644
index 0000000..40f197f
--- /dev/null
+++ b/include/RGFW.h
@@ -0,0 +1,16191 @@
+/*
+*
+* RGFW 2.0.0-dev
+
+* Copyright (C) 2022-26 Riley Mabb (@ColleagueRiley)
+*
+* libpng license
+*
+* 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.
+
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked as such, and must not be
+* misrepresented as being the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*
+*
+*/
+
+/*
+ (MAKE SURE RGFW_IMPLEMENTATION is in exactly one header or you use -D RGFW_IMPLEMENTATION)
+ #define RGFW_IMPLEMENTATION - makes it so source code is included with header
+*/
+
+/*
+ #define RGFW_IMPLEMENTATION - (required) makes it so the source code is included
+ #define RGFW_DEBUG - (optional) makes it so RGFW prints debug messages and errors when they're found
+ #define RGFW_EGL - (optional) compile with OpenGL functions, allowing you to use to use EGL instead of the native OpenGL functions
+ #define RGFW_DIRECTX - (optional) include integration directX functions (windows only)
+ #define RGFW_VULKAN - (optional) include helpful vulkan integration functions and macros
+ #define RGFW_WEBGPU - (optional) use WebGPU for rendering
+ #define RGFW_NATIVE - (optional) define native RGFW types that use native API structures
+
+ #define RGFW_X11 (optional) (unix only) if X11 should be used. This option is turned on by default by unix systems except for MacOS
+ #define RGFW_WAYLAND (optional) (unix only) use Wayland. (This can be used with X11)
+ #define RGFW_NO_STATIC_CONTEXT - do not initalizize with a static variable, use the heap if RGFW is not manually initalized
+ #define RGFW_NO_X11 (optional) (unix only) don't fallback to X11 when using Wayland
+ #define RGFW_NO_LOAD_WGL (optional) (windows only) if WGL should be loaded dynamically during runtime
+ #define RGFW_NO_X11_CURSOR (optional) (unix only) don't use XCursor
+ #define RGFW_NO_X11_CURSOR_PRELOAD (optional) (unix only) use XCursor, but don't link it in code, (you'll have to link it with -lXcursor)
+ #define RGFW_NO_X11_EXT_PRELOAD (optional) (unix only) use Xext, but don't link it in code, (you'll have to link it with -lXext)
+ #define RGFW_NO_LOAD_WINMM (optional) (windows only) use winmm (timeBeginPeriod), but don't link it in code, (you'll have to link it with -lwinmm)
+ #define RGFW_NO_WINMM (optional) (windows only) don't use winmm
+ #define RGFW_NO_IOKIT (optional) (macOS) don't use IOKit
+ #define RGFW_NO_UNIX_CLOCK (optional) (unix) don't link unix clock functions
+ #define RGFW_NO_DWM (windows only) - do not use or link dwmapi
+ #define RGFW_USE_XDL (optional) (X11) if XDL (XLib Dynamic Loader) should be used to load X11 dynamically during runtime (must include XDL.h along with RGFW)
+ #define RGFW_COCOA_GRAPHICS_SWITCHING - (optional) (cocoa) use automatic graphics switching (allow the system to choose to use GPU or iGPU)
+ #define RGFW_COCOA_FRAME_NAME (optional) (cocoa) set frame name
+ #define RGFW_NO_DPI - do not calculate DPI and don't use libShcore (win32)
+ #define RGFW_ADVANCED_SMOOTH_RESIZE - use advanced methods for smooth resizing (may result in a spike in memory usage or worse performance) (eg. WM_TIMER and XSyncValue)
+ #define RGFW_NO_INFO - do not define the RGFW_info struct (without RGFW_IMPLEMENTATION)
+ #define RGFW_NO_GLXWINDOW - do not use GLXWindow
+ #define RGFW_NO_ALLOCATE_MONITORS - do not allocate monitors on the heap at all (when there's no pre-allocated space left)
+ #define RGFW_NO_INCLUDE_VULKAN - do not include the Vulkan.h header, you have to include it yourself
+
+ #define RGFW_ALLOC x - choose the default allocation function (defaults to standard malloc)
+ #define RGFW_FREE x - choose the default deallocation function (defaults to standard free)
+ #define RGFW_USERPTR x - choose the default userptr sent to the malloc call, (NULL by default)
+
+ #define RGFW_EXPORT - use when building RGFW
+ #define RGFW_IMPORT - use when linking with RGFW (not as a single-header)
+
+ #define RGFW_USE_INT - force the use c-types rather than stdint.h (for systems that might not have stdint.h (msvc))
+ #define RGFW_bool x - choose what type to use for bool, by default u32 is used
+
+ #define RGFW_PREALLOCATED_MONITORS x - choose the default amount of pre-allocated monitors (can be zero)
+*/
+
+/*
+Example to get you started :
+
+*nix : gcc main.c -lX11 -lXrandr -lm
+windows : gcc main.c -lgdi32
+macos : gcc main.c -framework Cocoa -framework CoreVideo -framework IOKit
+
+#define RGFW_IMPLEMENTATION
+#include "RGFW.h"
+
+int main() {
+ RGFW_window* win = RGFW_createWindow("name", 100, 100, 500, 500, 0);
+
+ while (RGFW_window_shouldClose(win) == RGFW_FALSE) {
+ RGFW_pollEvents();
+ }
+
+ RGFW_window_close(win);
+}
+
+ compiling :
+
+ if you wish to compile the library all you have to do is create a new file with this in it
+
+ rgfw.c
+ #define RGFW_IMPLEMENTATION
+ #include "RGFW.h"
+
+ You may also want to add
+ `#define RGFW_EXPORT` when compiling and
+ `#define RGFW_IMPORT`when linking RGFW on it's own:
+ this reduces inline functions and prevents bloat in the object file
+
+ then you can use gcc (or whatever compile you wish to use) to compile the library into object file
+
+ ex. gcc -c RGFW.c -fPIC
+
+ after you compile the library into an object file, you can also turn the object file into an static or shared library
+
+ (commands ar and gcc can be replaced with whatever equivalent your system uses)
+
+ static : ar rcs RGFW.a RGFW.o
+ shared :
+ windows:
+ gcc -shared RGFW.o -lopengl32 -lgdi32 -o RGFW.dll
+ unix:
+ gcc -shared RGFW.o -lX11 -lGL -lXrandr -o RGFW.so
+ macos:
+ gcc -shared RGFW.o -framework CoreVideo -framework Cocoa -framework OpenGL -framework IOKit
+*/
+
+
+
+/*
+ Credits :
+ EimaMei/Sacode : Code review, helped with X11, MacOS and Windows support, Silicon, siliapp.h -> referencing
+
+ contributors : (feel free to put yourself here if you contribute)
+ krisvers (@krisvers) -> code review
+ EimaMei (@SaCode) -> code review
+ Nycticebus (@Code-Nycticebus) -> bug fixes
+ Rob Rohan (@robrohan) -> X11 bugs and missing features, MacOS/Cocoa fixing memory issues/bugs
+ AICDG (@THISISAGOODNAME) -> vulkan support (example)
+ @Easymode -> support, testing/debugging, bug fixes and reviews
+ Joshua Rowe (omnisci3nce) - bug fix, review (macOS)
+ @lesleyrs -> bug fix, review (OpenGL)
+ Nick Porcino (@meshula) - testing, organization, review (MacOS, examples)
+ @therealmarrakesh -> documentation
+ @DarekParodia -> code review (X11) (C++)
+ @NishiOwO -> fix BSD support, fix OSMesa example
+ @BaynariKattu -> code review and documentation
+ Miguel Pinto (@konopimi) -> code review, fix vulkan example
+ @m-doescode -> code review (wayland)
+ Robert Gonzalez (@uni-dos) -> code review (wayland)
+ @TheLastVoyager -> code review
+ @yehoravramenko -> code review (winapi)
+ @halocupcake -> code review (OpenGL)
+ @GideonSerf -> documentation
+ Alexandre Almeida (@M374LX) -> code review (keycodes)
+ Vũ Xuân Trường (@wanwanvxt) -> code review (winapi)
+ Lucas (@lightspeedlucas) -> code review (msvc++)
+ Jeffery Myers (@JeffM2501) -> code review (msvc)
+ Zeni (@zenitsuyo) -> documentation
+ TheYahton (@TheYahton) -> documentation
+ nonexistant_object (@DiarrheaMcgee)
+ AC Gaudette (@acgaudette)
+*/
+
+#if _MSC_VER
+ #pragma comment(lib, "gdi32")
+ #pragma comment(lib, "shell32")
+ #pragma comment(lib, "User32")
+ #pragma comment(lib, "Advapi32")
+ #pragma warning( push )
+ #pragma warning( disable : 4996 4191 4127)
+ #if _MSC_VER < 600
+ #define RGFW_C89
+ #endif
+#else
+ #if defined(__STDC__) && !defined(__STDC_VERSION__)
+ #define RGFW_C89
+ #endif
+#endif
+
+#if defined(RGFW_EGL) && !defined(RGFW_OPENGL)
+ #define RGFW_OPENGL
+#endif
+
+/* these OS macros look better & are standardized */
+/* plus it helps with cross-compiling */
+
+#ifdef __EMSCRIPTEN__
+ #define RGFW_WASM
+#endif
+
+#if defined(RGFW_X11) && defined(__APPLE__) && !defined(RGFW_CUSTOM_BACKEND)
+ #define RGFW_MACOS_X11
+ #define RGFW_UNIX
+#endif
+
+#if defined(_WIN32) && !defined(RGFW_X11) && !defined(RGFW_UNIX) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND) /* (if you're using X11 on windows some how) */
+ #define RGFW_WINDOWS
+#endif
+#if defined(RGFW_WAYLAND)
+ #define RGFW_DEBUG /* wayland will be in debug mode by default for now */
+ #define RGFW_UNIX
+ #ifdef RGFW_OPENGL
+ #define RGFW_EGL
+ #endif
+ #ifdef RGFW_X11
+ #define RGFW_DYNAMIC
+ #endif
+#endif
+#if (!defined(RGFW_WAYLAND) && !defined(RGFW_X11)) && (defined(__unix__) || defined(RGFW_MACOS_X11) || defined(RGFW_X11)) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND)
+ #define RGFW_MACOS_X11
+ #define RGFW_X11
+ #define RGFW_UNIX
+#elif defined(__APPLE__) && !defined(RGFW_MACOS_X11) && !defined(RGFW_X11) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND)
+ #define RGFW_MACOS
+#endif
+
+#if defined(RGFW_X11) || defined(RGFW_WASM) || defined(RGFW_WAYLAND)
+ #ifndef _POSIX_C_SOURCE
+ #define _POSIX_C_SOURCE 199309L
+ #endif
+
+ /* __USE_POSIX199309 is part of glibc internals, and isn't intended to be used outside. */
+ /* However, existing RGFW code may depend on it implicitly. */
+ #ifndef __USE_POSIX199309
+ #define __USE_POSIX199309
+ #endif
+#endif
+
+#ifndef RGFW_ASSERT
+ #include <assert.h>
+ #define RGFW_ASSERT assert
+#endif
+
+#ifndef RGFW_STATIC_ASSERT
+ #define RGFW_STATIC_ASSERT(check_name, x) typedef char RGFW_check_##check_name[(x) ? 1 : -1];
+#endif
+
+#if !defined(__STDC_VERSION__)
+ #define RGFW_C89
+#endif
+
+#if !defined(RGFW_SNPRINTF) && (defined(RGFW_X11) || defined(RGFW_WAYLAND))
+ /* required for X11 errors */
+ #include <stdio.h>
+ #define RGFW_SNPRINTF snprintf
+#endif
+
+#ifndef RGFW_USERPTR
+ #define RGFW_USERPTR NULL
+#endif
+
+#ifndef RGFW_UNUSED
+ #define RGFW_UNUSED(x) (void)(x)
+#endif
+
+#ifndef RGFW_ROUND
+ #define RGFW_ROUND(x) (i32)((x) >= 0 ? (x) + 0.5f : (x) - 0.5f)
+#endif
+
+#ifndef RGFW_ROUNDF
+ #define RGFW_ROUNDF(x) (float)((i32)((x) + ((x) < 0.0f ? -0.5f : 0.5f)))
+#endif
+
+#ifndef RGFW_MIN
+ #define RGFW_MIN(x, y) ((x < y) ? x : y)
+#endif
+
+#ifndef RGFW_ALLOC
+ #include <stdlib.h>
+ #define RGFW_ALLOC malloc
+ #define RGFW_FREE free
+#endif
+
+#if !defined(RGFW_MEMCPY) || !defined(RGFW_STRNCMP) || !defined(RGFW_STRNCPY) || !defined(RGFW_MEMZERO)
+ #include <string.h>
+#endif
+
+#ifndef RGFW_MEMZERO
+ #define RGFW_MEMZERO(ptr, num) memset(ptr, 0, num)
+#endif
+
+#ifndef RGFW_MEMCPY
+ #define RGFW_MEMCPY(dist, src, len) memcpy(dist, src, len)
+#endif
+
+#ifndef RGFW_STRNCMP
+ #define RGFW_STRNCMP(s1, s2, max) strncmp(s1, s2, max)
+#endif
+
+#ifndef RGFW_STRNCPY
+ #define RGFW_STRNCPY(dist, src, len) strncpy(dist, src, len)
+#endif
+
+#ifndef RGFW_STRSTR
+ #define RGFW_STRSTR(str, substr) strstr(str, substr)
+#endif
+
+#ifndef RGFW_STRTOL
+ /* required for X11 XDnD and X11 Monitor DPI */
+ #include <stdlib.h>
+ #define RGFW_STRTOL(str, endptr, base) strtol(str, endptr, base)
+ #define RGFW_ATOF(num) atof(num)
+#endif
+
+#if !defined(RGFW_PRINTF) && ( defined(RGFW_DEBUG) || defined(RGFW_WAYLAND) )
+ /* required when using RGFW_DEBUG */
+ #include <stdio.h>
+ #define RGFW_PRINTF printf
+#endif
+
+#ifndef RGFW_MAX_EVENTS
+ #define RGFW_MAX_EVENTS 32
+#endif
+#ifndef RGFW_PREALLOCATED_MONITORS
+ #define RGFW_PREALLOCATED_MONITORS 6 /* the number of preallocated monitors */
+#elif defined(RGFW_NO_ALLOCATE_MONITORS) && (RGFW_PREALLOCATED_MONITORS == 0)
+ #warning RGFW monitors have no place to be allocated
+#endif
+
+#ifndef RGFW_COCOA_FRAME_NAME
+ #define RGFW_COCOA_FRAME_NAME NULL
+#endif
+
+#ifdef RGFW_WIN95 /* for windows 95 testing (not that it really works) */
+ #define RGFW_NO_PASSTHROUGH
+#endif
+
+#if defined(RGFW_EXPORT) || defined(RGFW_IMPORT)
+ #if defined(_WIN32)
+ #if defined(__TINYC__) && (defined(RGFW_EXPORT) || defined(RGFW_IMPORT))
+ #define __declspec(x) __attribute__((x))
+ #endif
+
+ #if defined(RGFW_EXPORT)
+ #define RGFWDEF __declspec(dllexport)
+ #else
+ #define RGFWDEF __declspec(dllimport)
+ #endif
+ #else
+ #if defined(RGFW_EXPORT)
+ #define RGFWDEF __attribute__((visibility("default")))
+ #endif
+ #endif
+ #ifndef RGFWDEF
+ #define RGFWDEF
+ #endif
+#endif
+
+#ifndef RGFWDEF
+ #ifdef RGFW_C89
+ #define RGFWDEF __inline
+ #else
+ #define RGFWDEF inline
+ #endif
+#endif
+
+#if defined(__cplusplus) && !defined(__EMSCRIPTEN__)
+ extern "C" {
+#endif
+
+/* makes sure the header file part is only defined once by default */
+#ifndef RGFW_HEADER
+
+#define RGFW_HEADER
+
+#include <stddef.h>
+
+#ifndef RGFW_INT_DEFINED
+ #ifdef RGFW_USE_INT /* optional for any system that might not have stdint.h */
+ #include <limits.h>
+ typedef unsigned char u8;
+ typedef signed char i8;
+ typedef unsigned short u16;
+ typedef signed short i16;
+ #if INT_MAX == 0x7FFFFFFF
+ typedef unsigned int u32;
+ typedef signed int i32;
+ #else
+ typedef unsigned long int u32;
+ typedef signed long int i32;
+ #endif
+ #if LONG_MAX == 0x7FFFFFFFFFFFFFFFL
+ typedef unsigned long u64;
+ typedef signed long i64;
+ #else
+ typedef unsigned long long u64;
+ typedef signed long long i64;
+ #endif
+ #else /* use stdint standard types instead of c "standard" types */
+ #include <stdint.h>
+
+ typedef uint8_t u8;
+ typedef int8_t i8;
+ typedef uint16_t u16;
+ typedef int16_t i16;
+ typedef uint32_t u32;
+ typedef int32_t i32;
+ typedef uint64_t u64;
+ typedef int64_t i64;
+ #endif
+ #define RGFW_INT_DEFINED
+#endif
+
+RGFW_STATIC_ASSERT(size64, sizeof(i64) == 8)
+RGFW_STATIC_ASSERT(size32, sizeof(i32) == 4)
+RGFW_STATIC_ASSERT(size16, sizeof(i16) == 2)
+
+#ifndef RGFW_BOOL_DEFINED
+ #define RGFW_BOOL_DEFINED
+ typedef u8 RGFW_bool;
+#endif
+
+#define RGFW_BOOL(x) (RGFW_bool)((x) != 0) /* force a value to be 0 or 1 */
+#define RGFW_TRUE (RGFW_bool)1
+#define RGFW_FALSE (RGFW_bool)0
+
+#define RGFW_ENUM(type, name) type name; enum name##_enum
+#define RGFW_BIT(x) (1 << (x))
+
+#ifdef RGFW_VULKAN
+
+ #if defined(RGFW_WAYLAND) && defined(RGFW_X11)
+ #define VK_USE_PLATFORM_WAYLAND_KHR
+ #define VK_USE_PLATFORM_XLIB_KHR
+ #define RGFW_VK_SURFACE ((RGFW_usingWayland()) ? ("VK_KHR_wayland_surface") : ("VK_KHR_xlib_surface"))
+ #elif defined(RGFW_WAYLAND)
+ #define VK_USE_PLATFORM_WAYLAND_KHR
+ #define VK_USE_PLATFORM_XLIB_KHR
+ #define RGFW_VK_SURFACE "VK_KHR_wayland_surface"
+ #elif defined(RGFW_X11)
+ #define VK_USE_PLATFORM_XLIB_KHR
+ #define RGFW_VK_SURFACE "VK_KHR_xlib_surface"
+ #elif defined(RGFW_WINDOWS)
+ #define VK_USE_PLATFORM_WIN32_KHR
+ #define OEMRESOURCE
+ #define RGFW_VK_SURFACE "VK_KHR_win32_surface"
+ #elif defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11)
+ #define VK_USE_PLATFORM_MACOS_MVK
+ #define RGFW_VK_SURFACE "VK_MVK_macos_surface"
+ #else
+ #define RGFW_VK_SURFACE NULL
+ #endif
+
+#endif
+
+
+/*! @brief The stucture that contains information about the current RGFW instance */
+typedef struct RGFW_info RGFW_info;
+
+/*! @brief The window stucture for interfacing with the window */
+typedef struct RGFW_window RGFW_window;
+
+/*! @brief The source window stucture for interfacing with the underlying windowing API (e.g. winapi, wayland, cocoa, etc) */
+typedef struct RGFW_window_src RGFW_window_src;
+
+/*! @brief The color format for pixel data */
+typedef RGFW_ENUM(u8, RGFW_format) {
+ RGFW_formatRGB8 = 0, /*!< 8-bit RGB (3 channels) */
+ RGFW_formatBGR8, /*!< 8-bit BGR (3 channels) */
+ RGFW_formatRGBA8, /*!< 8-bit RGBA (4 channels) */
+ RGFW_formatARGB8, /*!< 8-bit RGBA (4 channels) */
+ RGFW_formatBGRA8, /*!< 8-bit BGRA (4 channels) */
+ RGFW_formatABGR8, /*!< 8-bit BGRA (4 channels) */
+ RGFW_formatCount
+};
+
+/*! @brief layout struct for mapping out format types */
+typedef struct RGFW_colorLayout { i32 r, g, b, a; u32 channels; } RGFW_colorLayout;
+
+/*! @brief function type converting raw image data between formats */
+typedef void (* RGFW_convertImageDataFunc)(u8* dest_data, u8* src_data, const RGFW_colorLayout* srcLayout, const RGFW_colorLayout* destLayout, size_t count);
+
+/*! @brief a stucture for interfacing with the underlying native image (e.g. XImage, HBITMAP, etc) */
+typedef struct RGFW_nativeImage RGFW_nativeImage;
+
+/*! @brief a stucture for interfacing with pixel data as a renderable surface */
+typedef struct RGFW_surface RGFW_surface;
+
+/*! @brief gamma struct for monitors */
+typedef struct RGFW_gammaRamp {
+ u16* red; /*!< array for the red channel */
+ u16* green; /*!< array for the green channel */
+ u16* blue; /*!< array for the blue channel */
+ size_t count; /*! count of elements in each channel */
+} RGFW_gammaRamp;
+
+/*! @brief monitor mode data | can be changed by the user (with functions)*/
+typedef struct RGFW_monitorMode {
+ i32 w, h; /*!< monitor workarea size */
+ float refreshRate; /*!< monitor refresh rate */
+ u8 red, blue, green; /*!< sizeof rgb values */
+ void* src; /*!< source API mode */
+} RGFW_monitorMode;
+
+/*! @brief structure for monitor node and source monitor data */
+typedef struct RGFW_monitorNode RGFW_monitorNode;
+
+/*! @brief structure for monitor data */
+typedef struct RGFW_monitor {
+ i32 x, y; /*!< x - y of the monitor workarea */
+ char name[128]; /*!< monitor name */
+ float scaleX, scaleY; /*!< monitor content scale */
+ float pixelRatio; /*!< pixel ratio for monitor (1.0 for regular, 2.0 for hiDPI) */
+ float physW, physH; /*!< monitor physical size in inches */
+ RGFW_monitorMode mode; /*!< current mode of the monitor */
+ void* userPtr; /*!< pointer for user data */
+ RGFW_monitorNode* node; /*!< source node data of the monitor */
+} RGFW_monitor;
+
+/*! @brief what type of request you are making for the monitor */
+typedef RGFW_ENUM(u8, RGFW_modeRequest) {
+ RGFW_monitorScale = RGFW_BIT(0), /*!< scale the monitor size */
+ RGFW_monitorRefresh = RGFW_BIT(1), /*!< change the refresh rate */
+ RGFW_monitorRGB = RGFW_BIT(2), /*!< change the monitor RGB bits size */
+ RGFW_monitorAll = RGFW_monitorScale | RGFW_monitorRefresh | RGFW_monitorRGB
+};
+
+/*! a raw pointer to the underlying mouse handle for setting and creating custom mouse icons */
+typedef void RGFW_mouse;
+
+/*! @brief RGFW's abstract keycodes */
+typedef RGFW_ENUM(u8, RGFW_key) {
+ RGFW_keyNULL = 0,
+ RGFW_keyEscape = '\033',
+ RGFW_keyBacktick = '`',
+ RGFW_key0 = '0',
+ RGFW_key1 = '1',
+ RGFW_key2 = '2',
+ RGFW_key3 = '3',
+ RGFW_key4 = '4',
+ RGFW_key5 = '5',
+ RGFW_key6 = '6',
+ RGFW_key7 = '7',
+ RGFW_key8 = '8',
+ RGFW_key9 = '9',
+ RGFW_keyMinus = '-',
+ RGFW_keyEqual = '=',
+ RGFW_keyEquals = RGFW_keyEqual,
+ RGFW_keyBackSpace = '\b',
+ RGFW_keyTab = '\t',
+ RGFW_keySpace = ' ',
+ RGFW_keyA = 'a',
+ RGFW_keyB = 'b',
+ RGFW_keyC = 'c',
+ RGFW_keyD = 'd',
+ RGFW_keyE = 'e',
+ RGFW_keyF = 'f',
+ RGFW_keyG = 'g',
+ RGFW_keyH = 'h',
+ RGFW_keyI = 'i',
+ RGFW_keyJ = 'j',
+ RGFW_keyK = 'k',
+ RGFW_keyL = 'l',
+ RGFW_keyM = 'm',
+ RGFW_keyN = 'n',
+ RGFW_keyO = 'o',
+ RGFW_keyP = 'p',
+ RGFW_keyQ = 'q',
+ RGFW_keyR = 'r',
+ RGFW_keyS = 's',
+ RGFW_keyT = 't',
+ RGFW_keyU = 'u',
+ RGFW_keyV = 'v',
+ RGFW_keyW = 'w',
+ RGFW_keyX = 'x',
+ RGFW_keyY = 'y',
+ RGFW_keyZ = 'z',
+ RGFW_keyPeriod = '.',
+ RGFW_keyComma = ',',
+ RGFW_keySlash = '/',
+ RGFW_keyBracket = '[',
+ RGFW_keyCloseBracket = ']',
+ RGFW_keySemicolon = ';',
+ RGFW_keyApostrophe = '\'',
+ RGFW_keyBackSlash = '\\',
+ RGFW_keyReturn = '\n',
+ RGFW_keyEnter = RGFW_keyReturn,
+ RGFW_keyDelete = '\177', /* 127 */
+ RGFW_keyF1,
+ RGFW_keyF2,
+ RGFW_keyF3,
+ RGFW_keyF4,
+ RGFW_keyF5,
+ RGFW_keyF6,
+ RGFW_keyF7,
+ RGFW_keyF8,
+ RGFW_keyF9,
+ RGFW_keyF10,
+ RGFW_keyF11,
+ RGFW_keyF12,
+ RGFW_keyF13,
+ RGFW_keyF14,
+ RGFW_keyF15,
+ RGFW_keyF16,
+ RGFW_keyF17,
+ RGFW_keyF18,
+ RGFW_keyF19,
+ RGFW_keyF20,
+ RGFW_keyF21,
+ RGFW_keyF22,
+ RGFW_keyF23,
+ RGFW_keyF24,
+ RGFW_keyF25,
+ RGFW_keyCapsLock,
+ RGFW_keyShiftL,
+ RGFW_keyControlL,
+ RGFW_keyAltL,
+ RGFW_keySuperL,
+ RGFW_keyShiftR,
+ RGFW_keyControlR,
+ RGFW_keyAltR,
+ RGFW_keySuperR,
+ RGFW_keyUp,
+ RGFW_keyDown,
+ RGFW_keyLeft,
+ RGFW_keyRight,
+ RGFW_keyInsert,
+ RGFW_keyMenu,
+ RGFW_keyEnd,
+ RGFW_keyHome,
+ RGFW_keyPageUp,
+ RGFW_keyPageDown,
+ RGFW_keyNumLock,
+ RGFW_keyPadSlash,
+ RGFW_keyPadMultiply,
+ RGFW_keyPadPlus,
+ RGFW_keyPadMinus,
+ RGFW_keyPadEqual,
+ RGFW_keyPadEquals = RGFW_keyPadEqual,
+ RGFW_keyPad1,
+ RGFW_keyPad2,
+ RGFW_keyPad3,
+ RGFW_keyPad4,
+ RGFW_keyPad5,
+ RGFW_keyPad6,
+ RGFW_keyPad7,
+ RGFW_keyPad8,
+ RGFW_keyPad9,
+ RGFW_keyPad0,
+ RGFW_keyPadPeriod,
+ RGFW_keyPadReturn,
+ RGFW_keyScrollLock,
+ RGFW_keyPrintScreen,
+ RGFW_keyPause,
+ RGFW_keyWorld1,
+ RGFW_keyWorld2,
+ RGFW_keyLast = 256 /* padding for alignment ~(175 by default) */
+};
+
+/*! @brief abstract mouse button codes */
+typedef RGFW_ENUM(u8, RGFW_mouseButton) {
+ RGFW_mouseLeft = 0, /*!< left mouse button */
+ RGFW_mouseMiddle, /*!< mouse-wheel-button */
+ RGFW_mouseRight, /*!< right mouse button */
+ RGFW_mouseMisc1, RGFW_mouseMisc2, RGFW_mouseMisc3, RGFW_mouseMisc4, RGFW_mouseMisc5,
+ RGFW_mouseFinal
+};
+
+/*! abstract key modifier codes */
+typedef RGFW_ENUM(u8, RGFW_keymod) {
+ RGFW_modCapsLock = RGFW_BIT(0),
+ RGFW_modNumLock = RGFW_BIT(1),
+ RGFW_modControl = RGFW_BIT(2),
+ RGFW_modAlt = RGFW_BIT(3),
+ RGFW_modShift = RGFW_BIT(4),
+ RGFW_modSuper = RGFW_BIT(5),
+ RGFW_modScrollLock = RGFW_BIT(6)
+};
+
+/*! types of dnd drag actions */
+typedef RGFW_ENUM(u8, RGFW_dndActionType) {
+ RGFW_dndActionNone = 0,
+ RGFW_dndActionEnter, /*!< data has been dragged into the window area */
+ RGFW_dndActionMove, /*!< the data that was dragged into the window area has moved inside the window */
+ RGFW_dndActionExit, /*!< the data that was dragged into the window area has left the window */
+};
+
+/*! types of transfered data (clipboard, dnd) */
+typedef RGFW_ENUM(u8, RGFW_dataTransferType) {
+ RGFW_dataNone = 0,
+ RGFW_dataText, /*!< plain text string */
+ RGFW_dataFile, /*!< file string */
+ RGFW_dataURL, /*!< URL string */
+ RGFW_dataImage, /*!< raw image data */
+ RGFW_dataUnknown /*!< unknown raw data */
+};
+
+/*! struct for data transfers, mostly used for the clipboard API */
+typedef struct RGFW_dataTransfer {
+ const char* data; /*!< transfered data */
+ size_t length; /*!< the full length of the data in bytes, including null-terminator, if included. null-terminators are ensured when reading data from RGFW */
+ RGFW_dataTransferType type; /*!< the type of data being transfered */
+} RGFW_dataTransfer;
+
+/*! internal node for a individual data drop */
+typedef struct RGFW_dataDropNode {
+ const char* data; /*!< dropped data */
+ size_t length; /*!< the size of the data in bytes */
+ RGFW_dataTransferType type; /*!< the type of data being dropped */
+ struct RGFW_dataDropNode* next; /*!< the next drop data node if any [when handling callbacks, this will always be NULL because the linked list is built as events are processed] */
+} RGFW_dataDropNode;
+
+/*! @brief codes for the event types that can be sent */
+typedef RGFW_ENUM(u8, RGFW_eventType) {
+ RGFW_eventNone = 0, /*!< no event has been sent */
+ RGFW_keyPressed, /*!< a key has been pressed */
+ RGFW_keyReleased, /*!< a key has been released */
+ RGFW_keyChar, /*!< keyboard character input event specifically for utf8 input */
+ RGFW_mouseButtonPressed, /*!< a mouse button has been pressed (left,middle,right) */
+ RGFW_mouseButtonReleased, /*!< a mouse button has been released (left,middle,right) */
+ RGFW_mouseScroll, /*!< a mouse scroll event */
+ RGFW_mouseMotion, /*!< the position of the mouse has been changed / the mouse has moved */
+ RGFW_mouseRawMotion, /*!< raw mouse motion */
+ RGFW_mouseEnter, /*!< mouse entered the window */
+ RGFW_mouseLeave, /*!< mouse left the window */
+ RGFW_windowMoved, /*!< the window was moved (by the user) */
+ RGFW_windowResized, /*!< the window was resized (by the user), [on WASM this means the browser was resized] */
+ RGFW_windowFocusIn, /*!< window is in focus now */
+ RGFW_windowFocusOut, /*!< window is out of focus now */
+ RGFW_windowRefresh, /*!< The window content needs to be refreshed */
+ RGFW_windowClose, /*!< the user attempts to close the window */
+ RGFW_windowMaximized, /*!< the window was maximized */
+ RGFW_windowMinimized, /*!< the window was minimized */
+ RGFW_windowRestored, /*!< the window was restored */
+ RGFW_dataDrop, /*!< data has been dropped into the window */
+ RGFW_dataDrag, /*!< the start of a drag and drop event, when data is being dragged */
+ RGFW_scaleUpdated, /*!< content scale factor changed */
+ RGFW_monitorConnected, /*!< a monitor has been connected */
+ RGFW_monitorDisconnected, /*!< a monitor has been disconnected */
+ RGFW_eventCount, /*!< the number of event types there are */
+ RGFW_mousePosChanged = RGFW_mouseMotion, /*!< alias for RGFW_mouseMotion (may be deleted at some point) */
+};
+
+/*! @brief flags for toggling whether or not an event should be processed */
+typedef RGFW_ENUM(u32, RGFW_eventFlag) {
+ RGFW_keyPressedFlag = RGFW_BIT(RGFW_keyPressed),
+ RGFW_keyReleasedFlag = RGFW_BIT(RGFW_keyReleased),
+ RGFW_keyCharFlag = RGFW_BIT(RGFW_keyChar),
+ RGFW_mouseScrollFlag = RGFW_BIT(RGFW_mouseScroll),
+ RGFW_mouseButtonPressedFlag = RGFW_BIT(RGFW_mouseButtonPressed),
+ RGFW_mouseButtonReleasedFlag = RGFW_BIT(RGFW_mouseButtonReleased),
+ RGFW_mouseMotionFlag = RGFW_BIT(RGFW_mouseMotion),
+ RGFW_mouseRawMotionFlag = RGFW_BIT(RGFW_mouseRawMotion),
+ RGFW_mouseEnterFlag = RGFW_BIT(RGFW_mouseEnter),
+ RGFW_mouseLeaveFlag = RGFW_BIT(RGFW_mouseLeave),
+ RGFW_windowMovedFlag = RGFW_BIT(RGFW_windowMoved),
+ RGFW_windowResizedFlag = RGFW_BIT(RGFW_windowResized),
+ RGFW_windowFocusInFlag = RGFW_BIT(RGFW_windowFocusIn),
+ RGFW_windowFocusOutFlag = RGFW_BIT(RGFW_windowFocusOut),
+ RGFW_windowRefreshFlag = RGFW_BIT(RGFW_windowRefresh),
+ RGFW_windowMaximizedFlag = RGFW_BIT(RGFW_windowMaximized),
+ RGFW_windowMinimizedFlag = RGFW_BIT(RGFW_windowMinimized),
+ RGFW_windowRestoredFlag = RGFW_BIT(RGFW_windowRestored),
+ RGFW_scaleUpdatedFlag = RGFW_BIT(RGFW_scaleUpdated),
+ RGFW_windowCloseFlag = RGFW_BIT(RGFW_windowClose),
+ RGFW_dataDropFlag = RGFW_BIT(RGFW_dataDrop),
+ RGFW_dataDragFlag = RGFW_BIT(RGFW_dataDrag),
+ RGFW_monitorConnectedFlag = RGFW_BIT(RGFW_monitorConnected),
+ RGFW_monitorDisconnectedFlag = RGFW_BIT(RGFW_monitorDisconnected),
+ RGFW_mousePosChangedFlag = RGFW_mouseMotionFlag, /* alias for RGFW_mouseMotionFlag (may be deleted at some point) */
+
+ RGFW_keyEventsFlag = RGFW_keyPressedFlag | RGFW_keyReleasedFlag | RGFW_keyCharFlag,
+ RGFW_mouseEventsFlag = RGFW_mouseButtonPressedFlag | RGFW_mouseButtonReleasedFlag | RGFW_mouseMotionFlag | RGFW_mouseEnterFlag | RGFW_mouseLeaveFlag | RGFW_mouseScrollFlag | RGFW_mouseRawMotionFlag,
+ RGFW_windowEventsFlag = RGFW_windowMovedFlag | RGFW_windowResizedFlag | RGFW_windowRefreshFlag | RGFW_windowMaximizedFlag | RGFW_windowMinimizedFlag | RGFW_windowRestoredFlag | RGFW_scaleUpdatedFlag,
+ RGFW_windowFocusEventsFlag = RGFW_windowFocusInFlag | RGFW_windowFocusOutFlag,
+ RGFW_dataDragDropEventsFlag = RGFW_dataDropFlag | RGFW_dataDragFlag,
+ RGFW_monitorEventsFlag = RGFW_monitorConnectedFlag | RGFW_monitorDisconnectedFlag,
+ RGFW_allEventFlags = RGFW_keyEventsFlag | RGFW_mouseEventsFlag | RGFW_windowEventsFlag | RGFW_windowFocusEventsFlag | RGFW_dataDragDropEventsFlag | RGFW_windowCloseFlag | RGFW_monitorEventsFlag
+};
+
+/*! Event structure(s) and union for checking/getting events */
+
+/*! @brief common event data across all events */
+typedef struct RGFW_commonEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+} RGFW_commonEvent;
+
+/*! @brief event data for all focus events */
+typedef struct RGFW_windowFocusEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+ RGFW_bool state; /*!< wether or not the window is in focus or not */
+} RGFW_windowFocusEvent;
+
+/*! @brief event data for any mouse button event (press/release) */
+typedef struct RGFW_mouseButtonEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+ RGFW_mouseButton value; /* !< which mouse button was pressed */
+ RGFW_bool state; /*!< if the button was pressed or released */
+} RGFW_mouseButtonEvent;
+
+/*! @brief event data for any mouse scroll or raw motion event */
+typedef struct RGFW_mouseDeltaEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+ float x, y; /*!< the raw mouse scroll or motion delta value */
+} RGFW_mouseDeltaEvent;
+
+/*! @brief event data for a mouse position event (RGFW_mouseMotion) */
+typedef struct RGFW_mouseMotionEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+ i32 x, y; /*!< mouse x, y of event (or drop point) */
+ RGFW_bool inWindow; /*!< if the mouse is in the window or not */
+} RGFW_mouseMotionEvent;
+
+/*! @brief event data for a key press/release event */
+typedef struct RGFW_keyEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+ RGFW_key value; /*!< the physical key of the event, refers to where key is physically */
+ RGFW_bool repeat; /*!< key press event repeated (the key is being held) */
+ RGFW_keymod mod; /*!< state of the key modifier state */
+ RGFW_bool state; /*!< if the key was pressed or released */
+} RGFW_keyEvent;
+
+/*! @brief event data for a key character event */
+typedef struct RGFW_keyCharEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+ u32 value; /*!< the unicode value of the key */
+} RGFW_keyCharEvent;
+
+/*! @brief event data for any data drop event */
+typedef struct RGFW_dataDropEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+ const RGFW_dataDropNode* value;
+} RGFW_dataDropEvent;
+
+/*! @brief event data for any data drag event */
+typedef struct RGFW_dataDragEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+ i32 x, y; /*!< mouse x, y of event (or drop point) */
+ RGFW_dndActionType action; /*!< the type of drag action, e.g. enter, leave, move */
+ RGFW_dataTransferType dataType; /*!< the type of data being dragged*/
+} RGFW_dataDragEvent;
+
+/*! @brief event data for when the window scale (DPI) is updated */
+typedef struct RGFW_scaleUpdatedEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+ float x, y; /*!< DPI scaling */
+} RGFW_scaleUpdatedEvent;
+
+/*! @brief event data for when a monitor is connected, disconnected or updated */
+typedef struct RGFW_monitorEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+ const RGFW_monitor* monitor; /*!< the monitor that this event applies to */
+ RGFW_bool state; /*!< if the monitor is connected or disconnected */
+} RGFW_monitorEvent;
+
+/*! @breif event data for when the window is updated, moved, resized or refreshed */
+typedef struct RGFW_windowUpdateEvent {
+ RGFW_eventType type; /*!< the specific event type */
+ RGFW_window* win; /*!< the window that was updated */
+ i32 x; /*!< the new window x OR the x of the rectanglular refresh area */
+ i32 y; /*!< the new window y OR the y of the rectanglular refresh area */
+ i32 w; /*!< the new window width OR the width of the rectanglular refresh area */
+ i32 h; /*!< the new window height OR the height of the rectanglular refresh area */
+} RGFW_windowUpdateEvent;
+
+/*! @brief union for all of the event stucture types */
+typedef union RGFW_event {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_commonEvent common; /*!< common event data (e.g.) type and win */
+ RGFW_windowFocusEvent focus; /*!< event data for focus in/out events */
+ RGFW_windowUpdateEvent update; /*!< data for window update/move/resize/refresh events */
+ RGFW_mouseButtonEvent button; /*!< data for a button press/release */
+ RGFW_mouseDeltaEvent delta; /*!< data for a mouse scroll or raw motion */
+ RGFW_mouseMotionEvent mouse; /*!< data for mouse motion events */
+ RGFW_keyEvent key; /*!< data for key press/release/hold events */
+ RGFW_keyCharEvent keyChar; /*!< data for key character events */
+ RGFW_dataDropEvent drop; /*!< data dropping events */
+ RGFW_dataDragEvent drag; /*!< data for data dragging events */
+ RGFW_scaleUpdatedEvent scale; /*!< data for dpi scaling update events */
+ RGFW_monitorEvent monitor; /*!< data for monitor events */
+} RGFW_event;
+
+/*!
+ @!brief codes for for RGFW_the code is stupid and C++ waitForEvent
+ waitMS -> Allows the function to keep checking for events even after there are no more events
+ if waitMS == 0, the loop will not wait for events
+ if waitMS > 0, the loop will wait that many miliseconds after there are no more events until it returns
+ if waitMS == -1 or waitMS == the max size of an unsigned 32-bit int, the loop will not return until it gets another event
+*/
+typedef RGFW_ENUM(i32, RGFW_eventWait) {
+ RGFW_eventNoWait = 0,
+ RGFW_eventWaitNext = -1
+};
+
+/*! @brief generic event callback function type */
+typedef void (*RGFW_genericFunc)(const RGFW_event* e);
+
+/*! brief structure that holds an array to callback data*/
+typedef struct RGFW_callbacks {
+ RGFW_genericFunc arr[RGFW_eventCount]; /*!< an array of all the callbacks */
+} RGFW_callbacks;
+
+/*! @brief optional bitwise arguments for making a windows, these can be OR'd together */
+typedef RGFW_ENUM(u32, RGFW_windowFlags) {
+ RGFW_windowNoBorder = RGFW_BIT(0), /*!< the window doesn't have a border / frame / decor */
+ RGFW_windowNoResize = RGFW_BIT(1), /*!< the window cannot be resized by the user */
+ RGFW_windowAllowDND = RGFW_BIT(2), /*!< the window supports drag and drop */
+ RGFW_windowHideMouse = RGFW_BIT(3), /*! the window should hide the mouse (can be toggled later on using `RGFW_window_showMouse`) */
+ RGFW_windowFullscreen = RGFW_BIT(4), /*!< the window is fullscreen by default */
+ RGFW_windowTranslucent = RGFW_BIT(5), /*!< the window is translucent (only properly works on X11 and MacOS, although it's meant for for windows) */
+ RGFW_windowTransparent = RGFW_windowTranslucent, /*!< the window is translucent (only properly works on X11 and MacOS, although it's meant for for windows) */
+ RGFW_windowCenter = RGFW_BIT(6), /*! center the window on the screen */
+ RGFW_windowRawMouse = RGFW_BIT(7), /*!< use raw mouse mouse on window creation */
+ RGFW_windowScaleToMonitor = RGFW_BIT(8), /*! scale the window to the screen */
+ RGFW_windowHide = RGFW_BIT(9), /*! the window is hidden */
+ RGFW_windowMaximize = RGFW_BIT(10), /*!< maximize the window on creation */
+ RGFW_windowCenterCursor = RGFW_BIT(11), /*!< center the cursor to the window on creation */
+ RGFW_windowFloating = RGFW_BIT(12), /*!< create a floating window */
+ RGFW_windowFocusOnShow = RGFW_BIT(13), /*!< focus the window when it's shown */
+ RGFW_windowMinimize = RGFW_BIT(14), /*!< focus the window when it's shown */
+ RGFW_windowFocus = RGFW_BIT(15), /*!< if the window is in focus */
+ RGFW_windowCaptureMouse = RGFW_BIT(16), /*!< capture the mouse mouse mouse on window creation */
+ RGFW_windowOpenGL = RGFW_BIT(17), /*!< create an OpenGL context (you can also do this manually with RGFW_window_createContext_OpenGL) */
+ RGFW_windowEGL = RGFW_BIT(18), /*!< create an EGL context (you can also do this manually with RGFW_window_createContext_EGL) */
+ RGFW_noDeinitOnClose = RGFW_BIT(19), /*!< do not auto deinit RGFW if the window closes and this is the last window open */
+ RGFW_windowedFullscreen = RGFW_windowNoBorder | RGFW_windowMaximize,
+ RGFW_windowCaptureRawMouse = RGFW_windowCaptureMouse | RGFW_windowRawMouse
+};
+
+/*! @brief the types of icon to set */
+typedef RGFW_ENUM(u8, RGFW_icon) {
+ RGFW_iconTaskbar = RGFW_BIT(0),
+ RGFW_iconWindow = RGFW_BIT(1),
+ RGFW_iconBoth = RGFW_iconTaskbar | RGFW_iconWindow
+};
+
+/*! @brief standard mouse icons */
+typedef RGFW_ENUM(u8, RGFW_mouseIcon) {
+ RGFW_mouseNormal = 0,
+ RGFW_mouseArrow,
+ RGFW_mouseIbeam,
+ RGFW_mouseText = RGFW_mouseIbeam,
+ RGFW_mouseCrosshair,
+ RGFW_mousePointingHand,
+ RGFW_mouseResizeEW,
+ RGFW_mouseResizeNS,
+ RGFW_mouseResizeNWSE,
+ RGFW_mouseResizeNESW,
+ RGFW_mouseResizeNW,
+ RGFW_mouseResizeN,
+ RGFW_mouseResizeNE,
+ RGFW_mouseResizeE,
+ RGFW_mouseResizeSE,
+ RGFW_mouseResizeS,
+ RGFW_mouseResizeSW,
+ RGFW_mouseResizeW,
+ RGFW_mouseResizeAll,
+ RGFW_mouseNotAllowed,
+ RGFW_mouseWait,
+ RGFW_mouseProgress,
+ RGFW_mouseIconCount,
+ RGFW_mouseIconFinal = 16 /* padding for alignment */
+};
+
+/*! @breif flash request type */
+typedef RGFW_ENUM(u8, RGFW_flashRequest) {
+ RGFW_flashCancel = 0,
+ RGFW_flashBriefly,
+ RGFW_flashUntilFocused
+};
+
+/*! @brief the type of debug message */
+typedef RGFW_ENUM(u8, RGFW_debugType) {
+ RGFW_typeError = 0, RGFW_typeWarning, RGFW_typeInfo
+};
+
+/*! @brief error codes for known failure types */
+typedef RGFW_ENUM(u8, RGFW_errorCode) {
+ RGFW_noError = 0, /*!< no error */
+ RGFW_errOutOfMemory,
+ RGFW_errOpenGLContext, RGFW_errEGLContext, /*!< error with the OpenGL context */
+ RGFW_errWayland, RGFW_errX11,
+ RGFW_errDirectXContext,
+ RGFW_errIOKit,
+ RGFW_errClipboard,
+ RGFW_errFailedFuncLoad,
+ RGFW_errBuffer,
+ RGFW_errMetal,
+ RGFW_errPlatform,
+ RGFW_errEventQueue,
+ RGFW_infoWindow, RGFW_infoBuffer, RGFW_infoGlobal, RGFW_infoOpenGL,
+ RGFW_warningWayland, RGFW_warningOpenGL
+};
+
+/*! @brief data for debug messages */
+typedef struct RGFW_debugInfo {
+ RGFW_debugType type; /*!< the type of message */
+ RGFW_errorCode code; /*!< the code for the specific type of debug message */
+ const char* msg; /*!< string message */
+} RGFW_debugInfo;
+
+/*! @brief callback function type for debug messags */
+typedef void (* RGFW_debugFunc)(const RGFW_debugInfo* info);
+
+/*! @brief function pointer equivalent of void* */
+typedef void (*RGFW_proc)(void);
+
+#if defined(RGFW_OPENGL)
+
+/*! @brief abstract structure for interfacing with the underlying OpenGL API */
+typedef struct RGFW_glContext RGFW_glContext;
+
+/*! @brief abstract structure for interfacing with the underlying EGL API */
+typedef struct RGFW_eglContext RGFW_eglContext;
+
+/*! values for the releaseBehavior hint */
+typedef RGFW_ENUM(i32, RGFW_glReleaseBehavior) {
+ RGFW_glReleaseFlush = 0, /*!< flush the pipeline will be flushed when the context is release */
+ RGFW_glReleaseNone /*!< do nothing on release */
+};
+
+/*! values for the profile hint */
+typedef RGFW_ENUM(i32, RGFW_glProfile) {
+ RGFW_glCore = 0, /*!< the core OpenGL version, e.g. just support for that version */
+ RGFW_glForwardCompatibility, /*!< only compatibility for newer versions of OpenGL as well as the requested version */
+ RGFW_glCompatibility, /*!< allow compatibility for older versions of OpenGL as well as the requested version */
+ RGFW_glES, /*!< use OpenGL ES */
+ RGFW_glWeb /*!< use WebGL version (otherwise the version is changed to match it's GLES equivalent) */
+};
+
+/*! values for the renderer hint */
+typedef RGFW_ENUM(i32, RGFW_glRenderer) {
+ RGFW_glAccelerated = 0, /*!< hardware accelerated (GPU) */
+ RGFW_glSoftware /*!< software rendered (CPU) */
+};
+
+/*! OpenGL initalization hints */
+typedef struct RGFW_glHints {
+ i32 stencil; /*!< set stencil buffer bit size (0 by default) */
+ i32 samples; /*!< set number of sample buffers (0 by default) */
+ i32 stereo; /*!< hint the context to use stereoscopic frame buffers for 3D (false by default) */
+ i32 auxBuffers; /*!< number of aux buffers (0 by default) */
+ i32 doubleBuffer; /*!< request double buffering (true by default) */
+ i32 red, green, blue, alpha; /*!< set color bit sizes (all 8 by default) */
+ i32 depth; /*!< set depth buffer bit size (24 by default) */
+ i32 accumRed, accumGreen, accumBlue, accumAlpha; /*!< set accumulated RGBA bit sizes (all 0 by default) */
+ RGFW_bool sRGB; /*!< request sRGA format (false by default) */
+ RGFW_bool robustness; /*!< request a "robust" (as in memory-safe) context (false by default). For more information check the overview section: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_robustness.txt */
+ RGFW_bool debug; /*!< request OpenGL debugging (false by default). */
+ RGFW_bool noError; /*!< request no OpenGL errors (false by default). This causes OpenGL errors to be undefined behavior. For more information check the overview section: https://registry.khronos.org/OpenGL/extensions/KHR/KHR_no_error.txt */
+ RGFW_glReleaseBehavior releaseBehavior; /*!< hint how the OpenGL driver should behave when changing contexts (RGFW_glReleaseNone by default). For more information check the overview section: https://registry.khronos.org/OpenGL/extensions/KHR/KHR_context_flush_control.txt */
+ RGFW_glProfile profile; /*!< set OpenGL API profile (RGFW_glCore by default) */
+ i32 major, minor; /*!< set the OpenGL API profile version (by default RGFW_glMajor is 1, RGFW_glMinor is 0) */
+ RGFW_glContext* share; /*!< Share this OpenGL context with newly created OpenGL contexts; defaults to NULL. */
+ RGFW_eglContext* shareEGL; /*!< Share this EGL context with newly created OpenGL contexts; defaults to NULL. */
+ RGFW_glRenderer renderer; /*!< renderer to use e.g. accelerated or software defaults to accelerated */
+} RGFW_glHints;
+
+#endif /* RGFW_OPENGL */
+
+/**!
+ * @brief Allocates memory using the allocator defined by RGFW_ALLOC at compile time.
+ * @param size The size (in bytes) of the memory block to allocate.
+ * @return A pointer to the allocated memory block.
+*/
+RGFWDEF void* RGFW_alloc(size_t size);
+
+/**!
+ * @brief Frees memory using the deallocator defined by RGFW_FREE at compile time.
+ * @param ptr A pointer to the memory block to free.
+*/
+RGFWDEF void RGFW_free(void* ptr);
+
+/**!
+ * @brief Returns the size (in bytes) of the RGFW_window structure.
+ * @return The size of the RGFW_window structure.
+*/
+RGFWDEF size_t RGFW_sizeofWindow(void);
+
+/**!
+ * @brief Returns the size (in bytes) of the RGFW_window_src structure.
+ * @return The size of the RGFW_window_src structure.
+*/
+RGFWDEF size_t RGFW_sizeofWindowSrc(void);
+
+/**!
+ * @brief (Unix) Toggles the use of Wayland.
+ * This is enabled by default when compiled with `RGFW_WAYLAND`.
+ * If not using `RGFW_WAYLAND`, Wayland functions are not exposed.
+ * This function can be used to force the use of XWayland.
+ * @param wayland A boolean value indicating whether to use Wayland (true) or not (false).
+*/
+RGFWDEF void RGFW_useWayland(RGFW_bool wayland);
+
+/**!
+ * @brief Checks if Wayland is currently being used.
+ * @return RGFW_TRUE if using Wayland, RGFW_FALSE otherwise.
+*/
+RGFWDEF RGFW_bool RGFW_usingWayland(void);
+
+/**!
+ * @brief Retrieves the current Cocoa layer (macOS only).
+ * @return A pointer to the Cocoa layer, or NULL if the platform is not in use.
+*/
+RGFWDEF void* RGFW_getLayer_OSX(void);
+
+/**!
+ * @brief Retrieves the current X11 display connection.
+ * @return A pointer to the X11 display, or NULL if the platform is not in use.
+*/
+RGFWDEF void* RGFW_getDisplay_X11(void);
+
+/**!
+ * @brief Retrieves the current Wayland display connection.
+ * @return A pointer to the Wayland display (`struct wl_display*`), or NULL if the platform is not in use.
+*/
+RGFWDEF struct wl_display* RGFW_getDisplay_Wayland(void);
+
+/**!
+ * @brief Sets the class name for X11 and WinAPI windows.
+ * Windows with the same class name will be grouped by the window manager.
+ * By default, the class name matches the root window’s name.
+ * @param name The class name to assign.
+*/
+RGFWDEF void RGFW_setClassName(const char* name);
+
+/**!
+ * @brief Sets the X11 instance name.
+ * By default, the window name will be used as the instance name.
+ * @param name The X11 instance name to set.
+*/
+RGFWDEF void RGFW_setXInstName(const char* name);
+
+/**!
+ * @brief (macOS only) Changes the current working directory to the application’s resource folder.
+*/
+RGFWDEF void RGFW_moveToMacOSResourceDir(void);
+
+/*! copy image to another image, respecting each image's format */
+RGFWDEF void RGFW_copyImageData(u8* dest_data, i32 w, i32 h, RGFW_format dest_format, u8* src_data, RGFW_format src_format, RGFW_convertImageDataFunc func);
+
+/**!
+ * @brief Returns the size (in bytes) of the RGFW_nativeImage structure.
+ * @return The size of the RGFW_nativeImage structure.
+*/
+RGFWDEF size_t RGFW_sizeofNativeImage(void);
+
+/**!
+ * @brief Returns the size (in bytes) of the RGFW_surface structure.
+ * @return The size of the RGFW_surface structure.
+*/
+RGFWDEF size_t RGFW_sizeofSurface(void);
+
+/**!
+ * @brief Returns the native format type for the system
+ * @return the native format type for the system as a RGFW_format enum value
+*/
+RGFWDEF RGFW_format RGFW_nativeFormat(void);
+
+/**!
+ * @brief Creates a new surface from raw pixel data.
+ * @param data A pointer to the pixel data buffer.
+ * @param w The width of the surface in pixels.
+ * @param h The height of the surface in pixels.
+ * @param format The pixel format of the data.
+ * @return A pointer to the newly created RGFW_surface.
+ *
+ * NOTE: when you create a surface using RGFW_createSurface / ptr, on X11 it uses the root window's visual
+ * this means it may fail to render on any other window if the visual does not match
+ * RGFW_window_createSurface and RGFW_window_createSurfacePtr exist only for X11 to address this issues
+ * Of course, you can also manually set the root window with RGFW_setRootWindow
+*/
+RGFWDEF RGFW_surface* RGFW_createSurface(u8* data, i32 w, i32 h, RGFW_format format);
+
+/**!
+ * @brief Creates a surface using a pre-allocated RGFW_surface structure.
+ * @param data A pointer to the pixel data buffer.
+ * @param w The width of the surface in pixels.
+ * @param h The height of the surface in pixels.
+ * @param format The pixel format of the data.
+ * @param surface A pointer to a pre-allocated RGFW_surface structure.
+ * @return RGFW_TRUE if successful, RGFW_FALSE otherwise.
+*/
+RGFWDEF RGFW_bool RGFW_createSurfacePtr(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface);
+
+/**!
+ * @brief Retrieves the native image associated with a surface.
+ * @param surface A pointer to the RGFW_surface.
+ * @return A pointer to the native RGFW_nativeImage associated with the surface.
+*/
+RGFWDEF RGFW_nativeImage* RGFW_surface_getNativeImage(RGFW_surface* surface);
+
+/**!
+ * @brief Frees the surface pointer and any buffers used for software rendering.
+ * @param surface A pointer to the RGFW_surface to free.
+*/
+RGFWDEF void RGFW_surface_free(RGFW_surface* surface);
+
+/**!
+ * @brief Frees only the internal buffers used for software rendering, leaving the surface struct intact.
+ * @param surface A pointer to the RGFW_surface whose buffers should be freed.
+*/
+RGFWDEF void RGFW_surface_freePtr(RGFW_surface* surface);
+
+
+/**!
+ * @brief create a mouse icon from bitmap data (similar to RGFW_window_setIcon).
+ * @param data A pointer to the bitmap pixel data.
+ * @param w The width of the mouse icon in pixels.
+ * @param h The height of the mouse icon in pixels.
+ * @param format The pixel format of the data.
+ * @return A pointer to the newly loaded RGFW_mouse structure.
+ *
+ * @note The icon is not resized by default.
+*/
+RGFWDEF RGFW_mouse* RGFW_createMouse(u8* data, i32 w, i32 h, RGFW_format format);
+
+/**!
+ * @brief create a standard mouse icon
+ * @param mouse The standard cursor type (see RGFW_MOUSE enum).
+ * @return A pointer to the newly loaded RGFW_mouse structure.
+*/
+RGFWDEF RGFW_mouse* RGFW_createMouseStandard(RGFW_mouseIcon mouse);
+
+/**!
+ * @brief Frees the data associated with an RGFW_mouse structure.
+ * @param mouse A pointer to the RGFW_mouse to free.
+*/
+RGFWDEF void RGFW_freeMouse(RGFW_mouse* mouse);
+
+/**!
+ * @brief Get an allocated array of the supported modes of a monitor
+ * @param monitor the source monitor object
+ * @param count [OUTPUT] the count of the array
+ * @return the allocated array of supported modes
+*/
+RGFWDEF RGFW_monitorMode* RGFW_monitor_getModes(RGFW_monitor* monitor, size_t* count);
+
+/**!
+ * @brief Free RGFW allocated modes array
+ * @param monitor the source monitor object
+ * @param modes a pointer to an allocated array of modes
+*/
+RGFWDEF void RGFW_freeModes(RGFW_monitorMode* modes);
+
+/**!
+ * @brief Get the supported modes of a monitor using a pre-allocated array
+ * @param monitor the source monitor object
+ * @param modes [OUTPUT] a pointer to an allocated array of modes
+ * @return the number of (possible) modes, if [modes == NULL] the possible nodes *may* be less than the actual modes
+*/
+RGFWDEF size_t RGFW_monitor_getModesPtr(RGFW_monitor* monitor, RGFW_monitorMode** modes);
+
+/**!
+ * @brief find the closest monitor mode based on the give mode with size being the highest priority, format being the second and refreshrate being the third.
+ * @param monitor the source monitor object
+ * @param mode user filled mode to use for comparison
+ * @param modes [OUTPUT] a pointer to be filled with the output closest monitor
+ * @return returns true if a suitable monitor was found and false if no suitable monitor was found at all
+*/
+
+RGFWDEF RGFW_bool RGFW_monitor_findClosestMode(RGFW_monitor* monitor, RGFW_monitorMode* mode, RGFW_monitorMode* closest);
+
+/**!
+ * @brief Get the allocated gamma ramp
+ * @param monitor the source monitor object
+*/
+RGFWDEF RGFW_gammaRamp* RGFW_monitor_getGammaRamp(RGFW_monitor* monitor);
+
+/**!
+ * @brief Free the gamma ramp allocated by RGFW
+ * @param allocated gamma ramp
+*/
+RGFWDEF void RGFW_freeGammaRamp(RGFW_gammaRamp* ramp);
+
+/**!
+ * @brief Get the monitor's gamma ramp using a pre-allocated struct with allocated data
+ * @param monitor the source monitor object
+ * @param ramp [OUTPUT] a pointer to an allocated gamma ramp (can be NULL to just get the count)
+ * @return the count of the gamma ramp
+*/
+RGFWDEF size_t RGFW_monitor_getGammaRampPtr(RGFW_monitor* monitor, RGFW_gammaRamp* ramp);
+
+/**!
+ * @brief Set the monitor's gamma ramp using a pre-allocated struct with allocated data
+ * @param monitor the source monitor object
+ * @param ramp a pointer to an allocated gamma ramp
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_monitor_setGammaRamp(RGFW_monitor* monitor, RGFW_gammaRamp* ramp);
+
+/**!
+ * @brief Create and set the monitor's gamma ramp with a base gamma exponent
+ * @param monitor the source monitor object
+ * @param the gamma exponent
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_monitor_setGamma(RGFW_monitor* monitor, float gamma);
+
+/**!
+ * @brief Create and set the monitor's gamma ramp with a base gamma exponent using a pre-allocated array
+ * @param monitor the source monitor object
+ * @param gamma the gamma exponent
+ * @param pre-allocated gammaramp channel
+ * @param count the length of the allocated channel array
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_monitor_setGammaPtr(RGFW_monitor* monitor, float gamma, u16* ptr, size_t count);
+
+/**!
+ * @brief Get the workarea of a monitor, meaning the parts not occupied by OS graphics (i.e. the taskbar)
+ * @param monitor the source monitor object
+ * @param x [OUTPUT] the x pos of the workarea
+ * @param y [OUTPUT] the y pos of the workarea
+ * @param w [OUTPUT] the width of the workarea
+ * @param h [OUTPUT] the height of the workarea
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_monitor_getWorkarea(RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height);
+
+/**!
+ * @brief Get the position of a monitor (the same as monitor.x / monitor.y)
+ * @param x [OUTPUT] the x position of the monitor
+ * @param y [OUTPUT] the y position of the monitor
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_monitor_getPosition(RGFW_monitor* monitor, i32* x, i32* y);
+
+/**!
+ * @brief Get the name of a monitor (the same as monitor.name)
+ * @return the cstring of the monitor's name
+*/
+RGFWDEF const char* RGFW_monitor_getName(RGFW_monitor* monitor);
+
+/**!
+ * @brief Get the scale of a monitor (the same as monitor.scaleX / monitor.scaleY)
+ * @param monitor the source monitor object
+ * @param x [OUTPUT] the x scale of the monitor
+ * @param y [OUTPUT] the y scale of the monitor
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_monitor_getScale(RGFW_monitor* monitor, float* x, float* y);
+
+/**!
+ * @brief Get the physical size of a monitor (the same as monitor.physW / monitor.physH)
+ * @param monitor the source monitor object
+ * @param w [OUTPUT] the physical width of the monitor
+ * @param h [OUTPUT] the physical height of the monitor
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_monitor_getPhysicalSize(RGFW_monitor* monitor, float* w, float* h);
+
+/**!
+ * @brief Set the user pointer of a monitor (the same as monitor.userPtr = userPtr)
+ * @param monitor the source monitor object
+ * @param userPtr the new user pointer for the monitor
+*/
+RGFWDEF void RGFW_monitor_setUserPtr(RGFW_monitor* monitor, void* userPtr);
+
+/**!
+ * @brief Get the user pointer of a monitor (the same as monitor.userPtr)
+ * @param monitor the source monitor object
+ * @return the user pointer of the monitor
+*/
+RGFWDEF void* RGFW_monitor_getUserPtr(RGFW_monitor* monitor);
+
+/**!
+ * @brief Get the mode of a monitor (the same as monitor.mode)
+ * @param monitor the source monitor object
+ * @param mode [OUTPUT] current mode the monitor
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_monitor_getMode(RGFW_monitor* monitor, RGFW_monitorMode* mode);
+
+/**!
+ * @brief Poll and check for monitor updates (this is called internally on monitor update events and RGFW_init)
+*/
+RGFWDEF void RGFW_pollMonitors(void);
+
+/**!
+ * @brief Allocates and returns an array of all available monitors.
+ * @param len [OUTPUT] A pointer to store the number of monitors found.
+ * @return An allocated array of pointers to RGFW_monitor structures that must be freed.
+*/
+RGFWDEF RGFW_monitor** RGFW_getMonitors(size_t* len);
+
+/**!
+ * @brief fills a pre-allocated array with available monitors.
+ * @param maximum number of monitors that the passed [monitors] buffer supports, can be zero to get the length alone
+ * @param pre-allocated buffer of monitors, can be NULL to get the length alone
+ * @param len [OUTPUT] A pointer to store the number of monitors found, if max is not zero and monitors is not NULL, length will be set to max.
+ * @return An array of pointers to RGFW_monitor structures or NULL if the function failed.
+*/
+RGFWDEF RGFW_bool RGFW_getMonitorsPtr(size_t max, RGFW_monitor** monitors, size_t* len);
+
+/**!
+ * @brief Retrieves the primary monitor.
+ * @return A pointer to the RGFW_monitor structure representing the primary monitor.
+*/
+RGFWDEF RGFW_monitor* RGFW_getPrimaryMonitor(void);
+
+/**!
+ * @brief Requests the display mode for a monitor (based on what attributes are directly requested).
+ * @param mon The monitor to apply the mode change to.
+ * @param mode The desired RGFW_monitorMode.
+ * @param request The RGFW_modeRequest describing how to handle the mode change.
+ * @return RGFW_TRUE if the mode was successfully applied, otherwise RGFW_FALSE.
+*/
+RGFWDEF RGFW_bool RGFW_monitor_requestMode(RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request);
+
+/**!
+ * @brief Sets a specific display mode for a monitor directly.
+ * @param mon The monitor to apply the mode change to.
+ * @param mode The desired RGFW_monitorMode.
+ * @param request The RGFW_modeRequest describing how to handle the mode change.
+ * @return RGFW_TRUE if the mode was successfully applied, otherwise RGFW_FALSE.
+*/
+RGFWDEF RGFW_bool RGFW_monitor_setMode(RGFW_monitor* mon, RGFW_monitorMode* mode);
+
+/**!
+ * @brief Compares two monitor modes to check if they are equivalent.
+ * @param mon The first monitor mode.
+ * @param mon2 The second monitor mode.
+ * @param request The RGFW_modeRequest that defines the comparison parameters.
+ * @return RGFW_TRUE if both modes are equivalent, otherwise RGFW_FALSE.
+*/
+RGFWDEF RGFW_bool RGFW_monitorModeCompare(RGFW_monitorMode* mon, RGFW_monitorMode* mon2, RGFW_modeRequest request);
+
+/**!
+ * @brief Scales a monitor’s mode to match a window’s size.
+ * @param mon The monitor to be scaled.
+ * @param win The window whose size should be used as a reference.
+ * @return RGFW_TRUE if the scaling was successful, otherwise RGFW_FALSE.
+*/
+RGFWDEF RGFW_bool RGFW_monitor_scaleToWindow(RGFW_monitor* mon, struct RGFW_window* win);
+
+ /**!
+ * @brief set (enable or disable) raw mouse mode globally
+ * @param the boolean state of raw mouse mode
+ *
+*/
+RGFWDEF void RGFW_setRawMouseMode(RGFW_bool state);
+
+/**!
+ * @brief toggles building the drag-and-drop (DND) linked list
+ * @param allow RGFW_TRUE to allow DND building, RGFW_FALSE to disable
+ * @note this is for state checking, the list is created by default if you are using the event queue
+*/
+RGFWDEF void RGFW_setBuildDND(RGFW_bool allow);
+
+/**!
+* @brief sleep until RGFW gets an event or the timer ends (defined by OS)
+* @param waitMS how long to wait for the next event (in miliseconds)
+*/
+RGFWDEF void RGFW_waitForEvent(i32 waitMS);
+
+/**!
+* @brief Set if events should be queued or not (enabled by default if the event queue is checked)
+* @param queue boolean value if RGFW should queue events or not
+*/
+RGFWDEF void RGFW_setQueueEvents(RGFW_bool queue);
+
+/**!
+ * @brief Sets the callback function for the event.
+ * @param the event type for the callback
+ * @param func The function to be called when the event is triggered.
+ * @return The previously set callback function, if any.
+*/
+RGFWDEF RGFW_genericFunc RGFW_setEventCallback(RGFW_eventType type, RGFW_genericFunc func);
+
+/**!
+ * @brief Sets the callback function for two continuous events.
+ * @param func The function to be called when the event is triggered.
+ * @param [OUTPUT] The previously set callback function for the first event, if any.
+ * @param [OUTPUT] The previously set callback function for the second event, if any.
+*/
+RGFWDEF void RGFW_setDualEventCallback(RGFW_eventType type, RGFW_genericFunc func, RGFW_genericFunc* first, RGFW_genericFunc* second);
+
+/**!
+ * @brief Sets the callback function for all events.
+ * @param func The function to be called when the event is triggered.
+ * @param [OUTPUT] a structure that holds an array of all the event callbacks
+*/
+RGFWDEF void RGFW_setAllEventCallbacks(RGFW_genericFunc func, RGFW_callbacks* callbacks);
+
+/**!
+* @brief check all the events until there are none left and updates window structure attributes
+*/
+RGFWDEF void RGFW_pollEvents(void);
+
+/**!
+* @brief check all the events until there are none left and updates window structure attributes
+* queues events if the queue is checked and/or requested
+*/
+RGFWDEF void RGFW_stopCheckEvents(void);
+
+/**!
+ * @brief polls and pops the next event
+ * @param event [OUTPUT] a pointer to store the retrieved event
+ * @return RGFW_TRUE if an event was found, RGFW_FALSE otherwise
+ *
+ * NOTE: Using this function without a loop may cause event lag.
+ * For multi-threaded systems, use RGFW_pollEvents combined with RGFW_checkQueuedEvent.
+ *
+ * Example:
+ * RGFW_event event;
+ * while (RGFW_checkEvent(win, &event)) {
+ * // handle event
+ * }
+*/
+RGFWDEF RGFW_bool RGFW_checkEvent(RGFW_event* event);
+
+/**!
+ * @brief pops the first queued event
+ * @param event [OUTPUT] a pointer to store the retrieved event
+ * @return RGFW_TRUE if an event was found, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_checkQueuedEvent(RGFW_event* event);
+
+/** * @defgroup Input
+* @{ */
+
+/**!
+ * @brief returns true if the key is pressed during the current frame
+ * @param key the key code of the key you want to check
+ * @return The boolean value if the key is pressed or not
+*/
+RGFWDEF RGFW_bool RGFW_isKeyPressed(RGFW_key key);
+
+/**!
+ * @brief returns true if the key was released during the current frame
+ * @param key the key code of the key you want to check
+ * @return The boolean value if the key is released or not
+*/
+RGFWDEF RGFW_bool RGFW_isKeyReleased(RGFW_key key);
+
+/**!
+ * @brief returns true if the key is down
+ * @param key the key code of the key you want to check
+ * @return The boolean value if the key is down or not
+*/
+RGFWDEF RGFW_bool RGFW_isKeyDown(RGFW_key key);
+
+/**!
+ * @brief returns true if the mouse button is pressed during the current frame
+ * @param button the mouse button code of the button you want to check
+ * @return The boolean value if the button is pressed or not
+*/
+RGFWDEF RGFW_bool RGFW_isMousePressed(RGFW_mouseButton button);
+
+/**!
+ * @brief returns true if the mouse button is released during the current frame
+ * @param button the mouse button code of the button you want to check
+ * @return The boolean value if the button is released or not
+*/
+RGFWDEF RGFW_bool RGFW_isMouseReleased(RGFW_mouseButton button);
+
+/**!
+ * @brief returns true if the mouse button is down
+ * @param button the mouse button code of the button you want to check
+ * @return The boolean value if the button is down or not
+*/
+RGFWDEF RGFW_bool RGFW_isMouseDown(RGFW_mouseButton button);
+
+/**!
+ * @brief outputs the current x, y position of the mouse
+ * @param X [OUTPUT] a pointer for the output X value
+ * @param Y [OUTPUT] a pointer for the output Y value
+*/
+RGFWDEF void RGFW_getMouseScroll(float* x, float* y);
+
+/**!
+ * @brief outputs the current x, y movement vector of the mouse
+ * @param X [OUTPUT] a pointer for the output X vector value
+ * @param Y [OUTPUT] a pointer for the output Y vector value
+*/
+RGFWDEF void RGFW_getMouseVector(float* x, float* y);
+/** @} */
+
+/**!
+ * @brief creates a new window
+ * @param name the requested title of the window
+ * @param x the requested x position of the window
+ * @param y the requested y position of the window
+ * @param w the requested width of the window
+ * @param h the requested height of the window
+ * @param flags extra arguments ((u32)0 means no flags used)
+ * @return A pointer to the newly created window structure
+ *
+ * NOTE: (windows) if the executable has an icon resource named RGFW_ICON, it will be set as the initial icon for the window
+*/
+RGFWDEF RGFW_window* RGFW_createWindow(const char* name, i32 x, i32 y, i32 w, i32 h, RGFW_windowFlags flags);
+
+/**!
+ * @brief creates a new window using a pre-allocated window structure
+ * @param name the requested title of the window
+ * @param x the requested x position of the window
+ * @param y the requested y position of the window
+ * @param w the requested width of the window
+ * @param h the requested height of the window
+ * @param flags extra arguments ((u32)0 means no flags used)
+ * @param win a pointer the pre-allocated window structure
+ * @return A pointer to the newly created window structure
+*/
+RGFWDEF RGFW_window* RGFW_createWindowPtr(const char* name, i32 x, i32 y, i32 w, i32 h, RGFW_windowFlags flags, RGFW_window* win);
+
+/**!
+ * @brief creates a new surface structure
+ * @param win the source window of the surface
+ * @param data a pointer to the raw data of the structure (you allocate this)
+ * @param w the width the data
+ * @param h the height of the data
+ * @return A pointer to the newly created surface structure
+ *
+ * NOTE: when you create a surface using RGFW_createSurface / ptr, on X11 it uses the root window's visual
+ * this means it may fail to render on any other window if the visual does not match
+ * RGFW_window_createSurface and RGFW_window_createSurfacePtr exist only for X11 to address this issues
+ * Of course, you can also manually set the root window with RGFW_setRootWindow
+ */
+RGFWDEF RGFW_surface* RGFW_window_createSurface(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format);
+
+/**!
+ * @brief creates a new surface structure using a pre-allocated surface structure
+ * @param win the source window of the surface
+ * @param data a pointer to the raw data of the structure (you allocate this)
+ * @param w the width the data
+ * @param h the height of the data
+ * @param a pointer to the pre-allocated surface structure
+ * @return a bool if the creation was successful or not
+*/
+RGFWDEF RGFW_bool RGFW_window_createSurfacePtr(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface);
+
+/**!
+ * @brief set the function/callback used for converting surface data between formats
+ * @param surface a pointer to the surface
+ * @param a function pointer for the function to use [if NULL the default function is used]
+*/
+RGFWDEF void RGFW_surface_setConvertFunc(RGFW_surface* surface, RGFW_convertImageDataFunc func);
+
+/**!
+ * @brief blits a surface stucture to the window
+ * @param win a pointer the window to blit to
+ * @param surface a pointer to the surface
+*/
+RGFWDEF void RGFW_window_blitSurface(RGFW_window* win, RGFW_surface* surface);
+
+/**!
+ * @brief gets the position of the window | with RGFW_window.x and window.y
+ * @param x [OUTPUT] the x position of the window
+ * @param y [OUTPUT] the y position of the window
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_window_getPosition(RGFW_window* win, i32* x, i32* y); /*!< */
+
+/**!
+ * @brief gets the size of the window | with RGFW_window.w and window.h
+ * @param win a pointer to the window
+ * @param w [OUTPUT] the width of the window
+ * @param h [OUTPUT] the height of the window
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_window_getSize(RGFW_window* win, i32* w, i32* h);
+
+/**!
+ * @brief gets the size of the window in exact pixels
+ * @param win a pointer to the window
+ * @param w [OUTPUT] the width of the window
+ * @param h [OUTPUT] the height of the window
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_window_getSizeInPixels(RGFW_window* win, i32* w, i32* h);
+
+/**!
+ * @brief gets the flags of the window | returns RGFW_window._flags
+ * @param win a pointer to the window
+ * @return the window flags
+*/
+RGFWDEF u32 RGFW_window_getFlags(RGFW_window* win);
+
+/**!
+ * @brief returns the exit key assigned to the window
+ * @param win a pointer to the target window
+ * @return The key code assigned as the exit key
+*/
+RGFWDEF RGFW_key RGFW_window_getExitKey(RGFW_window* win);
+
+/**!
+ * @brief sets the exit key for the window
+ * @param win a pointer to the target window
+ * @param key the key code to assign as the exit key
+*/
+RGFWDEF void RGFW_window_setExitKey(RGFW_window* win, RGFW_key key);
+
+/**!
+ * @brief sets the types of events you want the window to receive
+ * @param win a pointer to the target window
+ * @param events the event flags to enable (use RGFW_allEventFlags for all)
+*/
+RGFWDEF void RGFW_window_setEnabledEvents(RGFW_window* win, RGFW_eventFlag events);
+
+/**!
+ * @brief gets the currently enabled events for the window
+ * @param win a pointer to the target window
+ * @return The enabled event flags for the window
+*/
+RGFWDEF RGFW_eventFlag RGFW_window_getEnabledEvents(RGFW_window* win);
+
+/**!
+ * @brief enables all events and disables selected ones
+ * @param win a pointer to the target window
+ * @param events the event flags to disable
+*/
+RGFWDEF void RGFW_window_setDisabledEvents(RGFW_window* win, RGFW_eventFlag events);
+
+/**!
+ * @brief directly enables or disables a specific event or group of events
+ * @param win a pointer to the target window
+ * @param event the event flag or group of flags to modify
+ * @param state RGFW_TRUE to enable, RGFW_FALSE to disable
+*/
+RGFWDEF void RGFW_window_setEventState(RGFW_window* win, RGFW_eventFlag event, RGFW_bool state);
+
+/**!
+ * @brief gets the user pointer associated with the window
+ * @param win a pointer to the target window
+ * @return The user-defined pointer stored in the window
+*/
+RGFWDEF void* RGFW_window_getUserPtr(RGFW_window* win);
+
+/**!
+ * @brief sets a user pointer for the window
+ * @param win a pointer to the target window
+ * @param ptr a pointer to associate with the window
+*/
+RGFWDEF void RGFW_window_setUserPtr(RGFW_window* win, void* ptr);
+
+/**!
+ * @brief retrieves the platform-specific window source pointer
+ * @param win a pointer to the target window
+ * @return A pointer to the internal RGFW_window_src structure
+*/
+RGFWDEF RGFW_window_src* RGFW_window_getSrc(RGFW_window* win);
+
+/**!
+ * @brief sets the macOS layer object associated with the window
+ * @param win a pointer to the target window
+ * @param layer a pointer to the macOS layer object
+ * @note Only available on macOS platforms
+*/
+RGFWDEF void RGFW_window_setLayer_OSX(RGFW_window* win, void* layer);
+
+/**!
+ * @brief retrieves the macOS view object associated with the window
+ * @param win a pointer to the target window
+ * @return A pointer to the macOS view object, or NULL if not on macOS
+*/
+RGFWDEF void* RGFW_window_getView_OSX(RGFW_window* win);
+
+/**!
+ * @brief retrieves the macOS window object
+ * @param win a pointer to the target window
+ * @return A pointer to the macOS window object, or NULL if not on macOS
+*/
+RGFWDEF void* RGFW_window_getWindow_OSX(RGFW_window* win);
+
+/**!
+ * @brief retrieves the HWND handle for the window
+ * @param win a pointer to the target window
+ * @return A pointer to the Windows HWND handle, or NULL if not on Windows
+*/
+RGFWDEF void* RGFW_window_getHWND(RGFW_window* win);
+
+/**!
+ * @brief retrieves the HDC handle for the window
+ * @param win a pointer to the target window
+ * @return A pointer to the Windows HDC handle, or NULL if not on Windows
+*/
+RGFWDEF void* RGFW_window_getHDC(RGFW_window* win);
+
+/**!
+ * @brief retrieves the X11 Window handle for the window
+ * @param win a pointer to the target window
+ * @return The X11 Window handle, or 0 if not on X11
+*/
+RGFWDEF u64 RGFW_window_getWindow_X11(RGFW_window* win);
+
+/**!
+ * @brief retrieves the Wayland surface handle for the window
+ * @param win a pointer to the target window
+ * @return A pointer to the Wayland wl_surface, or NULL if not on Wayland
+*/
+RGFWDEF struct wl_surface* RGFW_window_getWindow_Wayland(RGFW_window* win);
+
+/** * @defgroup Window_management
+* @{ */
+
+/*! set the window flags (will undo flags if they don't match the old ones) */
+RGFWDEF void RGFW_window_setFlags(RGFW_window* win, RGFW_windowFlags);
+
+/**!
+ * @brief polls and pops the next event with the matching target window in event queue, pushes back events that don't match
+ * @param win a pointer to the target window
+ * @param event [OUTPUT] a pointer to store the retrieved event
+ * @return RGFW_TRUE if an event was found, RGFW_FALSE otherwise
+ *
+ * NOTE: Using this function without a loop may cause event lag.
+ * For multi-threaded systems, use RGFW_pollEvents combined with RGFW_window_checkQueuedEvent.
+ *
+ * Example:
+ * RGFW_event event;
+ * while (RGFW_window_checkEvent(win, &event)) {
+ * // handle event
+ * }
+*/
+RGFWDEF RGFW_bool RGFW_window_checkEvent(RGFW_window* win, RGFW_event* event);
+
+/**!
+ * @brief pops the first queued event with the matching target window, pushes back events that don't match
+ * @param win a pointer to the target window
+ * @param event [OUTPUT] a pointer to store the retrieved event
+ * @return RGFW_TRUE if an event was found, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_checkQueuedEvent(RGFW_window* win, RGFW_event* event);
+
+/**!
+ * @brief checks if a key was pressed while the window is in focus
+ * @param win a pointer to the target window
+ * @param key the key code to check
+ * @return RGFW_TRUE if the key was pressed, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_isKeyPressed(RGFW_window* win, RGFW_key key);
+
+/**!
+ * @brief checks if a key is currently being held down
+ * @param win a pointer to the target window
+ * @param key the key code to check
+ * @return RGFW_TRUE if the key is held down, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_isKeyDown(RGFW_window* win, RGFW_key key);
+
+/**!
+ * @brief checks if a key was released
+ * @param win a pointer to the target window
+ * @param key the key code to check
+ * @return RGFW_TRUE if the key was released, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_isKeyReleased(RGFW_window* win, RGFW_key key);
+
+/**!
+ * @brief checks if a mouse button was pressed
+ * @param win a pointer to the target window
+ * @param button the mouse button code to check
+ * @return RGFW_TRUE if the mouse button was pressed, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_isMousePressed(RGFW_window* win, RGFW_mouseButton button);
+
+/**!
+ * @brief checks if a mouse button is currently held down
+ * @param win a pointer to the target window
+ * @param button the mouse button code to check
+ * @return RGFW_TRUE if the mouse button is down, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_isMouseDown(RGFW_window* win, RGFW_mouseButton button);
+
+/**!
+ * @brief checks if a mouse button was released
+ * @param win a pointer to the target window
+ * @param button the mouse button code to check
+ * @return RGFW_TRUE if the mouse button was released, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_isMouseReleased(RGFW_window* win, RGFW_mouseButton button);
+
+/**!
+ * @brief checks if the mouse left the window (true only for the first frame)
+ * @param win a pointer to the target window
+ * @return RGFW_TRUE if the mouse left, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_didMouseLeave(RGFW_window* win);
+
+/**!
+ * @brief checks if the mouse entered the window (true only for the first frame)
+ * @param win a pointer to the target window
+ * @return RGFW_TRUE if the mouse entered, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_didMouseEnter(RGFW_window* win);
+
+/**!
+ * @brief checks if the mouse is currently inside the window bounds
+ * @param win a pointer to the target window
+ * @return RGFW_TRUE if the mouse is inside, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_isMouseInside(RGFW_window* win);
+
+/**!
+ * @brief checks if there is data being dragged into or within the window
+ * @param win a pointer to the target window
+ * @return RGFW_TRUE if data is being dragged, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_isDataDragging(RGFW_window* win);
+
+/**!
+ * @brief gets the position of a data drag
+ * @param win a pointer to the target window
+ * @param x [OUTPUT] pointer to store the x position
+ * @param y [OUTPUT] pointer to store the y position
+ * @return RGFW_TRUE if there is an active drag, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_getDataDrag(RGFW_window* win, i32* x, i32* y);
+
+/**!
+ * @brief checks if a data drop occurred in the window (first frame only)
+ * @param win a pointer to the target window
+ * @return RGFW_TRUE if data was dropped, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_didDataDrop(RGFW_window* win);
+
+/**!
+ * @brief retrieves data from a data drop (drag and drop)
+ * @param win a pointer to the target window
+ * @return a valid pointer to the root drag node if a data drop occurred, NULL otherwise
+*/
+RGFWDEF RGFW_dataDropNode* RGFW_window_getDataDrop(RGFW_window* win);
+
+/**!
+ * @brief closes the window and frees its associated structure
+ * @param win a pointer to the target window
+*/
+RGFWDEF void RGFW_window_close(RGFW_window* win);
+
+/**!
+ * @brief closes the window without freeing its structure
+ * @param win a pointer to the target window
+*/
+RGFWDEF void RGFW_window_closePtr(RGFW_window* win);
+
+/**!
+ * @brief fetches the size of the window through the OS (and updates the internal values)
+ * @param win a pointer to the window
+ * @param w [OUTPUT] the width of the window
+ * @param h [OUTPUT] the height of the window
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_window_fetchSize(RGFW_window* win, i32* w, i32* h);
+
+/**!
+ * @brief moves the window to a new position on the screen
+ * @param win a pointer to the target window
+ * @param x the new x position
+ * @param y the new y position
+*/
+RGFWDEF void RGFW_window_move(RGFW_window* win, i32 x, i32 y);
+
+/**!
+ * @brief moves the window to a specific monitor
+ * @param win a pointer to the target window
+ * @param m the target monitor
+*/
+RGFWDEF void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor* m);
+
+/**!
+ * @brief resizes the window to the given dimensions
+ * @param win a pointer to the target window
+ * @param w the new width
+ * @param h the new height
+*/
+RGFWDEF void RGFW_window_resize(RGFW_window* win, i32 w, i32 h);
+
+/**!
+ * @brief sets the aspect ratio of the window
+ * @param win a pointer to the target window
+ * @param w the width ratio
+ * @param h the height ratio
+*/
+RGFWDEF void RGFW_window_setAspectRatio(RGFW_window* win, i32 w, i32 h);
+
+/**!
+ * @brief sets the minimum size of the window
+ * @param win a pointer to the target window
+ * @param w the minimum width
+ * @param h the minimum height
+*/
+RGFWDEF void RGFW_window_setMinSize(RGFW_window* win, i32 w, i32 h);
+
+/**!
+ * @brief sets the maximum size of the window
+ * @param win a pointer to the target window
+ * @param w the maximum width
+ * @param h the maximum height
+*/
+RGFWDEF void RGFW_window_setMaxSize(RGFW_window* win, i32 w, i32 h);
+
+/**!
+ * @brief sets focus to the window
+ * @param win a pointer to the target window
+*/
+RGFWDEF void RGFW_window_focus(RGFW_window* win);
+
+/**!
+ * @brief checks if the window is currently in focus
+ * @param win a pointer to the target window
+ * @return RGFW_TRUE if the window is in focus, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_isInFocus(RGFW_window* win);
+
+/**!
+ * @brief raises the window to the top of the stack
+ * @param win a pointer to the target window
+*/
+RGFWDEF void RGFW_window_raise(RGFW_window* win);
+
+/**!
+ * @brief maximizes the window
+ * @param win a pointer to the target window
+*/
+RGFWDEF void RGFW_window_maximize(RGFW_window* win);
+
+/**!
+ * @brief toggles fullscreen mode for the window
+ * @param win a pointer to the target window
+ * @param fullscreen RGFW_TRUE to enable fullscreen, RGFW_FALSE to disable
+*/
+RGFWDEF void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen);
+
+/**!
+ * @brief centers the window on the screen
+ * @param win a pointer to the target window
+*/
+RGFWDEF void RGFW_window_center(RGFW_window* win);
+
+/**!
+ * @brief minimizes the window
+ * @param win a pointer to the target window
+*/
+RGFWDEF void RGFW_window_minimize(RGFW_window* win);
+
+/**!
+ * @brief restores the window from minimized state
+ * @param win a pointer to the target window
+*/
+RGFWDEF void RGFW_window_restore(RGFW_window* win);
+
+/**!
+ * @brief makes the window a floating window
+ * @param win a pointer to the target window
+ * @param floating RGFW_TRUE to float, RGFW_FALSE to disable
+*/
+RGFWDEF void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating);
+
+/**!
+ * @brief sets the opacity level of the window
+ * @param win a pointer to the target window
+ * @param opacity the opacity level (0–255)
+*/
+RGFWDEF void RGFW_window_setOpacity(RGFW_window* win, u8 opacity);
+
+/**!
+ * @brief toggles window borders / frame / decor
+ * @param win a pointer to the target window
+ * @param border RGFW_TRUE for bordered, RGFW_FALSE for borderless
+*/
+RGFWDEF void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border);
+
+/**!
+ * @brief checks if the window is borderless (has a border, frame or decor)
+ * @param win a pointer to the target window
+ * @return RGFW_TRUE if borderless, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_borderless(RGFW_window* win);
+
+/**!
+ * @brief toggles drag-and-drop (DND) support for the window
+ * @param win a pointer to the target window
+ * @param allow RGFW_TRUE to allow DND, RGFW_FALSE to disable
+ * @note RGFW_windowAllowDND must still be passed when creating the window
+*/
+RGFWDEF void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow);
+
+/**!
+ * @brief checks if drag-and-drop (DND) is allowed
+ * @param win a pointer to the target window
+ * @return RGFW_TRUE if DND is enabled, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_allowsDND(RGFW_window* win);
+
+#ifndef RGFW_NO_PASSTHROUGH
+/**!
+ * @brief toggles mouse passthrough for the window
+ * @param win a pointer to the target window
+ * @param passthrough RGFW_TRUE to enable passthrough, RGFW_FALSE to disable
+*/
+RGFWDEF void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough);
+#endif
+
+/**!
+ * @brief renames the window
+ * @param win a pointer to the target window
+ * @param name the new title string for the window
+*/
+RGFWDEF void RGFW_window_setName(RGFW_window* win, const char* name);
+
+/**!
+ * @brief sets the icon for the window and taskbar
+ * @param win a pointer to the target window
+ * @param data the image data
+ * @param w the width of the icon
+ * @param h the height of the icon
+ * @param format the image format
+ * @return RGFW_TRUE if successful, RGFW_FALSE otherwise
+ *
+ * NOTE: The image may be resized by default.
+*/
+RGFWDEF RGFW_bool RGFW_window_setIcon(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format);
+
+/**!
+ * @brief sets the icon for the window and/or taskbar
+ * @param win a pointer to the target window
+ * @param data the image data
+ * @param w the width of the icon
+ * @param h the height of the icon
+ * @param format the image format
+ * @param type the target icon type (taskbar, window, or both)
+ * @return RGFW_TRUE if successful, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_icon type);
+
+/**!
+ * @brief sets the mouse icon for the window using a loaded mouse icon
+ * @param win a pointer to the target window
+ * @param mouse a pointer to the RGFW_mouse struct containing the icon
+*/
+RGFWDEF RGFW_bool RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse);
+
+/**!
+ * @brief Sets the mouse to a preloaded [by RGFW] standard system cursor.
+ * @param win The target window.
+ * @param mouse The standardmouse icon (see RGFW_mouseIcon enum).
+ * @return True if the standard cursor was successfully applied.
+*/
+RGFWDEF RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, RGFW_mouseIcon icon);
+
+/**!
+ * @brief Sets the mouse to the default cursor icon.
+ * @param win The target window.
+ * @return True if the default cursor was successfully set.
+*/
+RGFWDEF RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win);
+
+/**!
+ * @brief set (enable or disable) raw mouse mode only for the select window
+ * @param win The target window.
+ * @param the boolean state of raw mouse mode
+ *
+*/
+RGFWDEF void RGFW_window_setRawMouseMode(RGFW_window* win, RGFW_bool state);
+
+/**!
+ * @brief lock/unlock the cursor.
+ * @param win The target window.
+ * @param the boolean state of the mouse's capture state
+ *
+*/
+RGFWDEF void RGFW_window_captureMouse(RGFW_window* win, RGFW_bool state);
+
+/**!
+ * @brief lock/unlock the cursor and enable raw mpuise mode.
+ * @param win The target window.
+ * @param the boolean state of raw mouse mode
+ *
+*/
+RGFWDEF void RGFW_window_captureRawMouse(RGFW_window* win, RGFW_bool state);
+
+/**!
+ * @brief Returns true if the mouse is using raw mouse mode
+ * @param win The target window.
+ * @return True if the mouse is using raw mouse input mode.
+*/
+RGFWDEF RGFW_bool RGFW_window_isRawMouseMode(RGFW_window* win);
+
+
+/**!
+ * @brief Returns true if the mouse is captured
+ * @param win The target window.
+ * @return True if the mouse is being captured.
+*/
+RGFWDEF RGFW_bool RGFW_window_isCaptured(RGFW_window* win);
+
+/**!
+ * @brief Hides the window from view.
+ * @param win The target window.
+*/
+RGFWDEF void RGFW_window_hide(RGFW_window* win);
+
+/**!
+ * @brief Shows the window if it was hidden.
+ * @param win The target window.
+*/
+RGFWDEF void RGFW_window_show(RGFW_window* win);
+
+/**!
+ * @breif request a window flash to get attention from the user
+ * @param win the target window
+ * @param request the flash operation requested
+*/
+RGFWDEF void RGFW_window_flash(RGFW_window* win, RGFW_flashRequest request);
+
+/**!
+ * @brief Sets whether the window should close.
+ * @param win The target window.
+ * @param shouldClose True to signal the window should close, false to keep it open.
+ *
+ * This can override or trigger the `RGFW_window_shouldClose` state by modifying window flags.
+*/
+RGFWDEF void RGFW_window_setShouldClose(RGFW_window* win, RGFW_bool shouldClose);
+
+/**!
+ * @brief Retrieves the current global mouse position.
+ * @param x [OUTPUT] Pointer to store the X position of the mouse on the screen.
+ * @param y [OUTPUT] Pointer to store the Y position of the mouse on the screen.
+ * @return True if the position was successfully retrieved.
+*/
+RGFWDEF RGFW_bool RGFW_getGlobalMouse(i32* x, i32* y);
+
+/**!
+ * @brief Retrieves the mouse position relative to the window.
+ * @param win The target window.
+ * @param x [OUTPUT] Pointer to store the X position within the window.
+ * @param y [OUTPUT] Pointer to store the Y position within the window.
+ * @return True if the position was successfully retrieved.
+*/
+RGFWDEF RGFW_bool RGFW_window_getMouse(RGFW_window* win, i32* x, i32* y);
+
+/**!
+ * @brief Shows or hides the mouse cursor for the window.
+ * @param win The target window.
+ * @param show True to show the mouse, false to hide it.
+*/
+RGFWDEF void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show);
+
+/**!
+ * @brief Checks if the mouse is currently hidden in the window.
+ * @param win The target window.
+ * @return True if the mouse is hidden.
+*/
+RGFWDEF RGFW_bool RGFW_window_isMouseHidden(RGFW_window* win);
+
+/**!
+ * @brief Moves the mouse to the specified position within the window.
+ * @param win The target window.
+ * @param x The new X position.
+ * @param y The new Y position.
+*/
+RGFWDEF void RGFW_window_moveMouse(RGFW_window* win, i32 x, i32 y);
+
+/**!
+ * @brief Checks if the window should close.
+ * @param win The target window.
+ * @return True if the window should close (for example, if ESC was pressed or a close event occurred).
+*/
+RGFWDEF RGFW_bool RGFW_window_shouldClose(RGFW_window* win);
+
+/**!
+ * @brief Checks if the window is currently fullscreen.
+ * @param win The target window.
+ * @return True if the window is fullscreen.
+*/
+RGFWDEF RGFW_bool RGFW_window_isFullscreen(RGFW_window* win);
+
+/**!
+ * @brief Checks if the window is currently hidden.
+ * @param win The target window.
+ * @return True if the window is hidden.
+*/
+RGFWDEF RGFW_bool RGFW_window_isHidden(RGFW_window* win);
+
+/**!
+ * @brief Checks if the window is minimized.
+ * @param win The target window.
+ * @return True if the window is minimized.
+*/
+RGFWDEF RGFW_bool RGFW_window_isMinimized(RGFW_window* win);
+
+/**!
+ * @brief Checks if the window is maximized.
+ * @param win The target window.
+ * @return True if the window is maximized.
+*/
+RGFWDEF RGFW_bool RGFW_window_isMaximized(RGFW_window* win);
+
+/**!
+ * @brief Checks if the window is floating.
+ * @param win The target window.
+ * @return True if the window is floating.
+*/
+RGFWDEF RGFW_bool RGFW_window_isFloating(RGFW_window* win);
+/** @} */
+
+/** * @defgroup Monitor
+* @{ */
+
+/**!
+ * @brief Scales the window to match its monitor’s resolution.
+ * @param win The target window.
+ *
+ * This function is automatically called when the flag `RGFW_scaleToMonitor`
+ * is used during window creation.
+*/
+RGFWDEF void RGFW_window_scaleToMonitor(RGFW_window* win);
+
+/**!
+ * @brief Retrieves the monitor structure associated with the window.
+ * @param win The target window.
+ * @return The monitor structure of the window.
+*/
+RGFWDEF RGFW_monitor* RGFW_window_getMonitor(RGFW_window* win);
+
+/** @} */
+
+/** * @defgroup Clipboard
+* @{ */
+
+/**!
+ * @brief Reads clipboard data.
+ * @return A pointer to the clipboard data object or NULL on failure.
+*/
+RGFWDEF const RGFW_dataTransfer* RGFW_readClipboard(void);
+
+/**!
+ * @brief Reads clipboard data into your object pointer using your provided buffer, or returns the required length if the buffer is NULL or bufferCapacity is 0.
+ * @param buffer the buffer used to fill the output dataTransfer object's data
+ * @param capacity the capacity/length of the buffer in bytes
+ * @param data [OUTPUT] A pointer to the dataTransfer object that will receive the clipboard data. (cannot be NULL)
+ * @return returns RGFW_TRUE on success and RGFW_FALSE on failure
+*/
+RGFWDEF RGFW_bool RGFW_readClipboardPtr(u8* buffer, size_t capacity, RGFW_dataTransfer* data);
+
+/**!
+ * @brief Writes data to the clipboard.
+ * @param data The data to be written to the clipboard, including the length and type.
+ * @param returns RGFW_TRUE on success and RGFW_FALSE on failure
+*/
+RGFWDEF RGFW_bool RGFW_writeClipboard(const RGFW_dataTransfer* data);
+/** @} */
+
+
+
+/** * @defgroup error handling
+* @{ */
+/**!
+ * @brief Sets the callback function to handle debug messages from RGFW.
+ * @param func The function pointer to be used as the debug callback.
+ * @return The previously set debug callback function.
+*/
+RGFWDEF RGFW_debugFunc RGFW_setDebugCallback(RGFW_debugFunc func);
+
+/**!
+ * @brief Sends a debug message manually through the currently set debug callback.
+ * @param type The type of debug message being sent.
+ * @param err The associated error code.
+ * @param msg The debug message text.
+*/
+RGFWDEF void RGFW_debugCallback(RGFW_debugType type, RGFW_errorCode code, const char* msg);
+/** @} */
+
+/** * @defgroup graphics_API
+* @{ */
+
+/*! native rendering API functions */
+#if defined(RGFW_OPENGL)
+/* these are native opengl specific functions and will NOT work with EGL */
+
+/*!< make the window the current OpenGL drawing context
+
+ NOTE:
+ if you want to switch the graphics context's thread,
+ you have to run RGFW_window_makeCurrentContext_OpenGL(NULL); on the old thread
+ then RGFW_window_makeCurrentContext_OpenGL(valid_window) on the new thread
+*/
+
+/**!
+ * @brief Sets the global OpenGL hints to the specified pointer.
+ * @param hints A pointer to the RGFW_glHints structure containing the desired OpenGL settings.
+*/
+RGFWDEF void RGFW_setGlobalHints_OpenGL(RGFW_glHints* hints);
+
+/**!
+ * @brief Resets the global OpenGL hints to their default values.
+*/
+RGFWDEF void RGFW_resetGlobalHints_OpenGL(void);
+
+/**!
+ * @brief Gets the current global OpenGL hints pointer.
+ * @return A pointer to the currently active RGFW_glHints structure.
+*/
+RGFWDEF RGFW_glHints* RGFW_getGlobalHints_OpenGL(void);
+
+/**!
+ * @brief Creates and allocates an OpenGL context for the specified window.
+ * @param win A pointer to the target RGFW_window.
+ * @param hints A pointer to an RGFW_glHints structure defining context creation parameters.
+ * @return A pointer to the newly created RGFW_glContext.
+*/
+RGFWDEF RGFW_glContext* RGFW_window_createContext_OpenGL(RGFW_window* win, RGFW_glHints* hints);
+
+/**!
+ * @brief Creates an OpenGL context for the specified window using a preallocated context structure.
+ * @param win A pointer to the target RGFW_window.
+ * @param ctx A pointer to an already allocated RGFW_glContext structure.
+ * @param hints A pointer to an RGFW_glHints structure defining context creation parameters.
+ * @return RGFW_TRUE on success, RGFW_FALSE on failure.
+*/
+RGFWDEF RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints);
+
+/**!
+ * @brief Retrieves the OpenGL context associated with a window.
+ * @param win A pointer to the RGFW_window.
+ * @return A pointer to the associated RGFW_glContext, or NULL if none exists or if the context is EGL-based.
+*/
+RGFWDEF RGFW_glContext* RGFW_window_getContext_OpenGL(RGFW_window* win);
+
+/**!
+ * @brief Deletes and frees the OpenGL context.
+ * @param win A pointer to the RGFW_window.
+ * @param ctx A pointer to the RGFW_glContext to delete.
+ *
+ * @note This is automatically called by RGFW_window_close if the window’s context is not NULL.
+*/
+RGFWDEF void RGFW_window_deleteContext_OpenGL(RGFW_window* win, RGFW_glContext* ctx);
+
+/**!
+ * @brief Deletes the OpenGL context without freeing its memory.
+ * @param win A pointer to the RGFW_window.
+ * @param ctx A pointer to the RGFW_glContext to delete.
+ *
+ * @note This is automatically called by RGFW_window_close if the window’s context is not NULL.
+*/
+RGFWDEF void RGFW_window_deleteContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx);
+
+/**!
+ * @brief Retrieves the native source context from an RGFW_glContext.
+ * @param ctx A pointer to the RGFW_glContext.
+ * @return A pointer to the native OpenGL context handle.
+*/
+RGFWDEF void* RGFW_glContext_getSourceContext(RGFW_glContext* ctx);
+
+/**!
+ * @brief Makes the specified window the current OpenGL rendering target.
+ * @param win A pointer to the RGFW_window to make current.
+ *
+ * @note This is typically called internally by RGFW_window_makeCurrent.
+*/
+RGFWDEF void RGFW_window_makeCurrentWindow_OpenGL(RGFW_window* win);
+
+/**!
+ * @brief Makes the OpenGL context of the specified window current.
+ * @param win A pointer to the RGFW_window whose context should be made current.
+ *
+ * @note To move a context between threads, call RGFW_window_makeCurrentContext_OpenGL(NULL)
+ * on the old thread before making it current on the new one.
+*/
+RGFWDEF void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win);
+
+/**!
+ * @brief Swaps the OpenGL buffers for the specified window.
+ * @param win A pointer to the RGFW_window whose buffers should be swapped.
+*/
+RGFWDEF void RGFW_window_swapBuffers_OpenGL(RGFW_window* win);
+
+/**!
+ * @brief Retrieves the current OpenGL context.
+ * @return A pointer to the currently active OpenGL context (GLX, WGL, Cocoa, or WebGL backend).
+*/
+RGFWDEF void* RGFW_getCurrentContext_OpenGL(void);
+
+/**!
+ * @brief Retrieves the current OpenGL window.
+ * @return A pointer to the RGFW_window currently bound as the OpenGL context target.
+*/
+RGFWDEF RGFW_window* RGFW_getCurrentWindow_OpenGL(void);
+
+/**!
+ * @brief Sets the OpenGL swap interval (vsync).
+ * @param win A pointer to the RGFW_window.
+ * @param swapInterval The desired swap interval value (0 to disable vsync, 1 to enable).
+*/
+RGFWDEF void RGFW_window_swapInterval_OpenGL(RGFW_window* win, i32 swapInterval);
+
+/**!
+ * @brief Retrieves the address of a native OpenGL procedure.
+ * @param procname The name of the OpenGL function to look up.
+ * @return A pointer to the function, or NULL if not found.
+*/
+RGFWDEF RGFW_proc RGFW_getProcAddress_OpenGL(const char* procname);
+
+/**!
+ * @brief Checks whether a specific OpenGL or OpenGL ES API extension is supported.
+ * @param extension The name of the extension to check.
+ * @param len The length of the extension string.
+ * @return RGFW_TRUE if supported, RGFW_FALSE otherwise.
+*/
+RGFWDEF RGFW_bool RGFW_extensionSupported_OpenGL(const char* extension, size_t len);
+
+/**!
+ * @brief Checks whether a specific platform-dependent OpenGL extension is supported.
+ * @param extension The name of the extension to check.
+ * @param len The length of the extension string.
+ * @return RGFW_TRUE if supported, RGFW_FALSE otherwise.
+*/
+RGFWDEF RGFW_bool RGFW_extensionSupportedPlatform_OpenGL(const char* extension, size_t len);
+
+/* these are EGL specific functions, they may fallback to OpenGL */
+#ifdef RGFW_EGL
+/**!
+ * @brief Creates and allocates an OpenGL/EGL context for the specified window.
+ * @param win A pointer to the target RGFW_window.
+ * @param hints A pointer to an RGFW_glHints structure defining context creation parameters.
+ * @return A pointer to the newly created RGFW_eglContext.
+*/
+RGFWDEF RGFW_eglContext* RGFW_window_createContext_EGL(RGFW_window* win, RGFW_glHints* hints);
+
+/**!
+ * @brief Creates an OpenGL/EGL context for the specified window using a preallocated context structure.
+ * @param win A pointer to the target RGFW_window.
+ * @param ctx A pointer to an already allocated RGFW_eglContext structure.
+ * @param hints A pointer to an RGFW_glHints structure defining context creation parameters.
+ * @return RGFW_TRUE on success, RGFW_FALSE on failure.
+*/
+RGFWDEF RGFW_bool RGFW_window_createContextPtr_EGL(RGFW_window* win, RGFW_eglContext* ctx, RGFW_glHints* hints);
+
+/**!
+ * @brief Frees and deletes an OpenGL/EGL context.
+ * @param win A pointer to the RGFW_window.
+ * @param ctx A pointer to the RGFW_eglContext to delete.
+ *
+ * @note Automatically called by RGFW_window_close if RGFW owns the context.
+*/
+RGFWDEF void RGFW_window_deleteContext_EGL(RGFW_window* win, RGFW_eglContext* ctx);
+
+/**!
+ * @brief Deletes an OpenGL/EGL context without freeing its memory.
+ * @param win A pointer to the RGFW_window.
+ * @param ctx A pointer to the RGFW_eglContext to delete.
+ *
+ * @note Automatically called by RGFW_window_close if RGFW owns the context.
+*/
+RGFWDEF void RGFW_window_deleteContextPtr_EGL(RGFW_window* win, RGFW_eglContext* ctx);
+
+/**!
+ * @brief Retrieves the OpenGL/EGL context associated with a window.
+ * @param win A pointer to the RGFW_window.
+ * @return A pointer to the associated RGFW_eglContext, or NULL if none exists or if the context is a native OpenGL context.
+*/
+RGFWDEF RGFW_eglContext* RGFW_window_getContext_EGL(RGFW_window* win);
+
+/**!
+ * @brief Retrieves the EGL display handle.
+ * @return A pointer to the native EGLDisplay.
+*/
+RGFWDEF void* RGFW_getDisplay_EGL(void);
+
+/**!
+ * @brief Retrieves the native source context from an RGFW_eglContext.
+ * @param ctx A pointer to the RGFW_eglContext.
+ * @return A pointer to the native EGLContext handle.
+*/
+RGFWDEF void* RGFW_eglContext_getSourceContext(RGFW_eglContext* ctx);
+
+/**!
+ * @brief Retrieves the EGL surface handle from an RGFW_eglContext.
+ * @param ctx A pointer to the RGFW_eglContext.
+ * @return A pointer to the EGLSurface associated with the context.
+*/
+RGFWDEF void* RGFW_eglContext_getSurface(RGFW_eglContext* ctx);
+
+/**!
+ * @brief Retrieves the Wayland EGL window handle from an RGFW_eglContext.
+ * @param ctx A pointer to the RGFW_eglContext.
+ * @return A pointer to the wl_egl_window associated with the EGL context.
+*/
+RGFWDEF struct wl_egl_window* RGFW_eglContext_wlEGLWindow(RGFW_eglContext* ctx);
+
+/**!
+ * @brief Swaps the EGL buffers for the specified window.
+ * @param win A pointer to the RGFW_window whose buffers should be swapped.
+ *
+ * @note Typically called by RGFW_window_swapInterval.
+*/
+RGFWDEF void RGFW_window_swapBuffers_EGL(RGFW_window* win);
+
+/**!
+ * @brief Makes the specified window the current EGL rendering target.
+ * @param win A pointer to the RGFW_window to make current.
+ *
+ * @note This is typically called internally by RGFW_window_makeCurrent.
+*/
+RGFWDEF void RGFW_window_makeCurrentWindow_EGL(RGFW_window* win);
+
+/**!
+ * @brief Makes the EGL context of the specified window current.
+ * @param win A pointer to the RGFW_window whose context should be made current.
+ *
+ * @note To move a context between threads, call RGFW_window_makeCurrentContext_EGL(NULL)
+ * on the old thread before making it current on the new one.
+*/
+RGFWDEF void RGFW_window_makeCurrentContext_EGL(RGFW_window* win);
+
+/**!
+ * @brief Retrieves the current EGL context.
+ * @return A pointer to the currently active EGLContext.
+*/
+RGFWDEF void* RGFW_getCurrentContext_EGL(void);
+
+/**!
+ * @brief Retrieves the current EGL window.
+ * @return A pointer to the RGFW_window currently bound as the EGL context target.
+*/
+RGFWDEF RGFW_window* RGFW_getCurrentWindow_EGL(void);
+
+/**!
+ * @brief Sets the EGL swap interval (vsync).
+ * @param win A pointer to the RGFW_window.
+ * @param swapInterval The desired swap interval value (0 to disable vsync, 1 to enable).
+*/
+RGFWDEF void RGFW_window_swapInterval_EGL(RGFW_window* win, i32 swapInterval);
+
+/**!
+ * @brief Retrieves the address of a native OpenGL or OpenGL ES procedure in an EGL context.
+ * @param procname The name of the OpenGL function to look up.
+ * @return A pointer to the function, or NULL if not found.
+*/
+RGFWDEF RGFW_proc RGFW_getProcAddress_EGL(const char* procname);
+
+/**!
+ * @brief Checks whether a specific OpenGL or OpenGL ES API extension is supported in the current EGL context.
+ * @param extension The name of the extension to check.
+ * @param len The length of the extension string.
+ * @return RGFW_TRUE if supported, RGFW_FALSE otherwise.
+*/
+RGFWDEF RGFW_bool RGFW_extensionSupported_EGL(const char* extension, size_t len);
+
+/**!
+ * @brief Checks whether a specific platform-dependent EGL extension is supported in the current context.
+ * @param extension The name of the extension to check.
+ * @param len The length of the extension string.
+ * @return RGFW_TRUE if supported, RGFW_FALSE otherwise.
+*/
+RGFWDEF RGFW_bool RGFW_extensionSupportedPlatform_EGL(const char* extension, size_t len);
+#endif
+#endif
+
+#ifdef RGFW_VULKAN
+#ifndef RGFW_NO_INCLUDE_VULKAN
+ #include <vulkan/vulkan.h>
+#endif
+
+/* if you don't want to use the above macros */
+
+/**!
+ * @brief Retrieves the Vulkan instance extensions required by RGFW.
+ * @param count [OUTPUT] A pointer that will receive the number of required extensions (typically 2).
+ * @return A pointer to a static array of required Vulkan instance extension names.
+*/
+RGFWDEF const char** RGFW_getRequiredInstanceExtensions_Vulkan(size_t* count);
+
+/**!
+ * @brief Creates a Vulkan surface for the specified window.
+ * @param win A pointer to the RGFW_window for which to create the Vulkan surface.
+ * @param instance The Vulkan instance used to create the surface.
+ * @param surface [OUTPUT] A pointer to a VkSurfaceKHR handle that will receive the created surface.
+ * @return A VkResult indicating success or failure.
+*/
+RGFWDEF VkResult RGFW_window_createSurface_Vulkan(RGFW_window* win, VkInstance instance, VkSurfaceKHR* surface);
+
+/**!
+ * @brief Checks whether the specified Vulkan physical device and queue family support presentation for RGFW.
+ * @param instance The Vulkan instance.
+ * @param physicalDevice The Vulkan physical device to check.
+ * @param queueFamilyIndex The index of the queue family to query for presentation support.
+ * @return RGFW_TRUE if presentation is supported, RGFW_FALSE otherwise.
+*/
+RGFWDEF RGFW_bool RGFW_getPresentationSupport_Vulkan(VkPhysicalDevice physicalDevice, u32 queueFamilyIndex);
+#endif
+
+#ifdef RGFW_DIRECTX
+#ifndef RGFW_WINDOWS
+ #undef RGFW_DIRECTX
+#else
+ #define OEMRESOURCE
+ #include <dxgi.h>
+
+ #ifndef __cplusplus
+ #define __uuidof(T) IID_##T
+ #endif
+/**!
+ * @brief Creates a DirectX swap chain for the specified RGFW window.
+ * @param win A pointer to the RGFW_window for which to create the swap chain.
+ * @param pFactory A pointer to the IDXGIFactory used to create the swap chain.
+ * @param pDevice A pointer to the DirectX device (e.g., ID3D11Device or ID3D12Device).
+ * @param swapchain [OUTPUT] A pointer to an IDXGISwapChain pointer that will receive the created swap chain.
+ * @return An integer result code (0 on success, or a DirectX error code on failure).
+*/
+RGFWDEF int RGFW_window_createSwapChain_DirectX(RGFW_window* win, IDXGIFactory* pFactory, IUnknown* pDevice, IDXGISwapChain** swapchain);
+#endif
+#endif
+
+#ifdef RGFW_WEBGPU
+ #include <webgpu/webgpu.h>
+ /**!
+ * @brief Creates a WebGPU surface for the specified RGFW window.
+ * @param window A pointer to the RGFW_window for which to create the surface.
+ * @param instance The WebGPU instance used to create the surface.
+ * @return The created WGPUSurface handle.
+ */
+ RGFWDEF WGPUSurface RGFW_window_createSurface_WebGPU(RGFW_window* window, WGPUInstance instance);
+#endif
+
+/** @} */
+
+/** * @defgroup Supporting
+* @{ */
+
+/**!
+ * @brief Sets the root (main) RGFW window.
+ * @param win A pointer to the RGFW_window to set as the root window.
+*/
+RGFWDEF void RGFW_setRootWindow(RGFW_window* win);
+
+/**!
+ * @brief Retrieves the current root RGFW window.
+ * @return A pointer to the current root RGFW_window.
+*/
+RGFWDEF RGFW_window* RGFW_getRootWindow(void);
+
+/**!
+ * @brief Pushes an event into the standard RGFW event queue.
+ * @param event A pointer to the RGFW_event to be added to the queue.
+*/
+RGFWDEF void RGFW_eventQueuePush(const RGFW_event* event);
+
+/**!
+ * @brief Pushes an event into the standard RGFW event queue and call the callback.
+ * @param event A pointer to the RGFW_event to be added to the queue.
+*/
+RGFWDEF void RGFW_eventQueuePushAndCall(const RGFW_event* event);
+
+/**!
+ * @brief Clears all events from the RGFW event queue without processing them.
+*/
+RGFWDEF void RGFW_eventQueueFlush(void);
+
+/**!
+ * @brief Pops the next event from the RGFW event queue.
+ * @return A pointer to the popped RGFW_event, or NULL if the queue is empty.
+*/
+RGFWDEF RGFW_event* RGFW_eventQueuePop(void);
+
+/**!
+ * @brief Pops the next event from the RGFW event queue that matches the target window, pushes back events that don't matchj.
+ * @param win A pointer to the target RGFW_window.
+ * @return A pointer to the popped RGFW_event, or NULL if the queue is empty.
+*/
+RGFWDEF RGFW_event* RGFW_window_eventQueuePop(RGFW_window* win);
+
+/**!
+ * @brief Converts an API keycode to the RGFW unmapped (physical) key.
+ * @param keycode The platform-specific keycode.
+ * @return The corresponding RGFW keycode.
+*/
+RGFWDEF RGFW_key RGFW_apiKeyToRGFW(u32 keycode);
+
+/**!
+ * @brief Converts an RGFW keycode to the unmapped (physical) API key.
+ * @param keycode The RGFW keycode.
+ * @return The corresponding platform-specific keycode.
+*/
+RGFWDEF u32 RGFW_rgfwToApiKey(RGFW_key keycode);
+
+/**!
+ * @brief Converts an physical RGFW keycode to a mapped RGFW keycode.
+ * @param keycode the physical RGFW keycode.
+ * @return The corresponding mapped RGFW keycode.
+*/
+RGFWDEF RGFW_key RGFW_physicalToMappedKey(RGFW_key keycode);
+
+/**!
+ * @brief Retrieves the size of the RGFW_info structure.
+ * @return The size (in bytes) of RGFW_info.
+*/
+RGFWDEF size_t RGFW_sizeofInfo(void);
+
+/**!
+ * @brief Initializes the RGFW library internally.
+ * @return 0 on success, a negative number error error on failure.
+ * @note This is automatically called when the first window is created.
+*/
+RGFWDEF i32 RGFW_init(void);
+
+/**!
+ * @brief Deinitializes the current instance of the RGFW library.
+ * @note This is automatically called when the last open window is closed.
+*/
+RGFWDEF void RGFW_deinit(void);
+
+/**!
+ * @brief Initializes RGFW using a user-provided RGFW_info structure.
+ * @param info A pointer to an RGFW_info structure to be used for initialization.
+ * @return 0 on success, a negative number error error on failure and a positive number for a warning.
+*/
+RGFWDEF i32 RGFW_init_ptr(RGFW_info* info);
+
+/**!
+ * @brief Deinitializes a specific RGFW instance stored in the provided RGFW_info pointer.
+ * @param info A pointer to the RGFW_info structure representing the instance to deinitialize.
+*/
+RGFWDEF void RGFW_deinit_ptr(RGFW_info* info);
+
+/**!
+ * @brief Sets the global RGFW_info structure pointer.
+ * @param info A pointer to the RGFW_info structure to set.
+*/
+RGFWDEF void RGFW_setInfo(RGFW_info* info);
+
+/**!
+ * @brief Retrieves the global RGFW_info structure pointer.
+ * @return A pointer to the current RGFW_info structure.
+*/
+RGFWDEF RGFW_info* RGFW_getInfo(void);
+
+/** @} */
+#endif /* RGFW_HEADER */
+
+#if !defined(RGFW_NATIVE_HEADER) && (defined(RGFW_NATIVE) || defined(RGFW_IMPLEMENTATION))
+#define RGFW_NATIVE_HEADER
+ #if (defined(RGFW_OPENGL) || defined(RGFW_WEGL)) && defined(_MSC_VER)
+ #pragma comment(lib, "opengl32")
+ #endif
+
+ #ifdef RGFW_OPENGL
+ struct RGFW_eglContext {
+ void* ctx;
+ void* surface;
+ struct wl_egl_window* eglWindow;
+ };
+
+ typedef union RGFW_gfxContext {
+ RGFW_glContext* native;
+ RGFW_eglContext* egl;
+ } RGFW_gfxContext;
+
+ typedef RGFW_ENUM(u32, RGFW_gfxContextType) {
+ RGFW_gfxNativeOpenGL = RGFW_BIT(0),
+ RGFW_gfxEGL = RGFW_BIT(1),
+ RGFW_gfxOwnedByRGFW = RGFW_BIT(2)
+ };
+ #endif
+
+ /*! source data for the window (used by the APIs) */
+ #ifdef RGFW_WINDOWS
+
+ #ifndef WIN32_LEAN_AND_MEAN
+ #define WIN32_LEAN_AND_MEAN
+ #endif
+ #ifndef OEMRESOURCE
+ #define OEMRESOURCE
+ #endif
+
+ #include <windows.h>
+
+ struct RGFW_nativeImage {
+ HBITMAP bitmap;
+ u8* bitmapBits;
+ RGFW_format format;
+ HDC hdcMem;
+ };
+
+ #ifdef RGFW_OPENGL
+ struct RGFW_glContext { HGLRC ctx; };
+ #endif
+
+ struct RGFW_window_src {
+ HWND window; /*!< source window */
+ HDC hdc; /*!< source HDC */
+ HICON hIconSmall, hIconBig; /*!< source window icons */
+ i32 maxSizeW, maxSizeH, minSizeW, minSizeH, aspectRatioW, aspectRatioH; /*!< for setting max/min resize (RGFW_WINDOWS) */
+ RGFW_bool actionFrame; /* frame after a caption button was toggled (e.g. minimize, maximize or close) */
+ WCHAR highSurrogate;
+ #ifdef RGFW_OPENGL
+ RGFW_gfxContext ctx;
+ RGFW_gfxContextType gfxType;
+ #endif
+ };
+
+#elif defined(RGFW_UNIX)
+ #ifdef RGFW_X11
+ #include <X11/Xlib.h>
+ #include <X11/Xutil.h>
+
+ #include <X11/extensions/Xrandr.h>
+ #include <X11/Xresource.h>
+
+ #ifndef RGFW_XDND_VERSION
+ #define RGFW_XDND_VERSION 5
+ #endif
+ #endif
+
+ #ifdef RGFW_WAYLAND
+ #ifdef RGFW_LIBDECOR
+ #include <libdecor-0/libdecor.h>
+ #endif
+
+ #include <wayland-client.h>
+ #include <errno.h>
+ #endif
+
+ struct RGFW_nativeImage {
+ #ifdef RGFW_X11
+ XImage* bitmap;
+ #endif
+ #ifdef RGFW_WAYLAND
+ struct wl_buffer* wl_buffer;
+ i32 fd;
+ struct wl_shm_pool* pool;
+ #endif
+ u8* buffer;
+ RGFW_format format;
+ };
+
+ #ifdef RGFW_OPENGL
+ struct RGFW_glContext {
+ #ifdef RGFW_X11
+ struct __GLXcontextRec* ctx; /*!< source graphics context */
+ Window window;
+ #endif
+ #ifdef RGFW_WAYLAND
+ RGFW_eglContext egl;
+ #endif
+ };
+ #endif
+
+ struct RGFW_window_src {
+ i32 x, y, w, h;
+ #ifdef RGFW_OPENGL
+ RGFW_gfxContext ctx;
+ RGFW_gfxContextType gfxType;
+ #endif
+#ifdef RGFW_X11
+ Window window; /*!< source window */
+ Window parent; /*!< parent window */
+ GC gc;
+ XIC ic;
+ u64 flashEnd;
+ #ifdef RGFW_ADVANCED_SMOOTH_RESIZE
+ i64 counter_value;
+ XID counter;
+ #endif
+#endif /* RGFW_X11 */
+
+#if defined(RGFW_WAYLAND)
+ struct wl_surface* surface;
+ struct xdg_surface* xdg_surface;
+ struct xdg_toplevel* xdg_toplevel;
+ struct zxdg_toplevel_decoration_v1* decoration;
+ struct zwp_locked_pointer_v1 *locked_pointer;
+ struct xdg_toplevel_icon_v1 *icon;
+ u32 decoration_mode;
+ /* State flags to configure the window */
+ RGFW_bool pending_activated;
+ RGFW_bool activated;
+ RGFW_bool resizing;
+ RGFW_bool pending_maximized;
+ RGFW_bool maximized;
+ RGFW_bool minimized;
+ RGFW_bool configured;
+
+ RGFW_bool using_custom_cursor;
+ struct wl_surface* custom_cursor_surface;
+
+ RGFW_monitorNode* active_monitor;
+
+ struct wl_data_source *data_source; // offer data to other clients
+
+ #ifdef RGFW_LIBDECOR
+ struct libdecor* decorContext;
+ #endif
+#endif /* RGFW_WAYLAND */
+ };
+
+#elif defined(RGFW_MACOS)
+ #include <CoreVideo/CoreVideo.h>
+
+ struct RGFW_nativeImage {
+ RGFW_format format;
+ u8* buffer;
+ void* rep;
+ };
+
+ #ifdef RGFW_OPENGL
+ struct RGFW_glContext {
+ void* ctx;
+ void* format;
+ };
+ #endif
+
+ struct RGFW_window_src {
+ void* window;
+ void* view; /* apple viewpoint thingy */
+ void* mouse;
+ void* delegate;
+ #ifdef RGFW_OPENGL
+ RGFW_gfxContext ctx;
+ RGFW_gfxContextType gfxType;
+ #endif
+ };
+
+#elif defined(RGFW_WASM)
+
+ #include <emscripten/html5.h>
+ #include <emscripten/key_codes.h>
+
+ struct RGFW_nativeImage {
+ RGFW_format format;
+ };
+
+ #ifdef RGFW_OPENGL
+ struct RGFW_glContext {
+ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx;
+ };
+ #endif
+
+ struct RGFW_window_src {
+ #ifdef RGFW_OPENGL
+ RGFW_gfxContext ctx;
+ RGFW_gfxContextType gfxType;
+ #endif
+ };
+
+#endif
+
+struct RGFW_surface {
+ u8* data;
+ i32 w, h;
+ RGFW_format format;
+ RGFW_convertImageDataFunc convertFunc;
+ RGFW_nativeImage native;
+};
+
+/*! internal window data that is not specific to the OS */
+typedef struct RGFW_windowInternal {
+ /*! which key RGFW_window_shouldClose checks. Settting this to RGFW_keyNULL disables the feature. */
+ RGFW_key exitKey;
+ i32 lastMouseX, lastMouseY; /*!< last cusor point (for raw mouse data) */
+
+ RGFW_bool shouldClose;
+ RGFW_bool rawMouse;
+ RGFW_bool captureMouse;
+ RGFW_bool inFocus;
+ RGFW_bool mouseInside;
+ RGFW_keymod mod;
+ RGFW_eventFlag enabledEvents;
+ u32 flags; /*!< windows flags (for RGFW to check and modify) */
+ i32 oldX, oldY, oldW, oldH;
+ RGFW_monitorMode oldMode;
+ RGFW_mouse* mouse;
+} RGFW_windowInternal;
+
+struct RGFW_window {
+ RGFW_window_src src; /*!< src window data */
+ RGFW_windowInternal internal; /*!< internal window data that is not specific to the OS */
+ void* userPtr; /* ptr for user data */
+ i32 x, y, w, h; /*!< position and size of the window */
+}; /*!< window structure for the window */
+
+typedef struct RGFW_windowState {
+ RGFW_bool mouseEnter;
+ RGFW_bool dataDragging;
+ RGFW_bool dataDrop;
+ size_t dataLength;
+ i32 dropX, dropY;
+ RGFW_window* win; /*!< it's not possible for one of these events to happen in the frame that the other event happened */
+
+ RGFW_bool mouseLeave;
+ RGFW_window* winLeave; /*!< if a mouse leaves one window and enters the next */
+} RGFW_windowState;
+
+typedef struct {
+ RGFW_bool current;
+ RGFW_bool prev;
+} RGFW_keyState;
+
+struct RGFW_monitorNode {
+ RGFW_monitor mon;
+ RGFW_bool disconnected;
+ RGFW_monitorNode* next;
+#ifdef RGFW_WAYLAND
+ u32 id; /* Add id so wl_outputs can be removed */
+ struct wl_output *output;
+ struct zxdg_output_v1 *xdg_output;
+ RGFW_monitorMode* modes;
+ size_t modeCount;
+#endif
+#if defined(RGFW_X11)
+ i32 screen;
+ RROutput rrOutput;
+ RRCrtc crtc;
+#endif
+#ifdef RGFW_WINDOWS
+ HMONITOR hMonitor;
+ WCHAR adapterName[32];
+ WCHAR deviceName[32];
+#endif
+#ifdef RGFW_MACOS
+ void* screen;
+ CGDirectDisplayID display;
+ u32 uintNum;
+#endif
+};
+
+typedef struct RGFW_monitorList {
+ RGFW_monitorNode* head;
+ RGFW_monitorNode* cur;
+} RGFW_monitorList;
+
+typedef struct RGFW_monitors {
+ RGFW_monitorList list;
+ size_t count;
+
+ RGFW_monitorNode* primary;
+ #if (RGFW_PREALLOCATED_MONITORS)
+ RGFW_monitorList freeList;
+ RGFW_monitorNode data[RGFW_PREALLOCATED_MONITORS];
+ #endif
+} RGFW_monitors;
+
+struct RGFW_info {
+ RGFW_window* root;
+ i32 windowCount;
+
+ RGFW_mouse* hiddenMouse;
+ RGFW_mouse* standardMice[RGFW_mouseIconCount];
+
+ RGFW_debugFunc debugCallbackSrc;
+ RGFW_genericFunc callbacks[RGFW_eventCount];
+ RGFW_event events[RGFW_MAX_EVENTS]; /* A circular buffer (FIFO), using eventBottom/Len */
+
+ i32 eventBottom;
+ i32 eventLen;
+ RGFW_bool queueEvents;
+ RGFW_bool polledEvents;
+
+ u32 apiKeycodes[RGFW_keyLast];
+ #if defined(RGFW_X11) || defined(RGFW_WAYLAND)
+ RGFW_key keycodes[256];
+ #elif defined(RGFW_WINDOWS)
+ RGFW_key keycodes[512];
+ #elif defined(RGFW_MACOS)
+ RGFW_key keycodes[128];
+ #elif defined(RGFW_WASM)
+ RGFW_key keycodes[256];
+ #endif
+
+ RGFW_bool stopCheckEvents_bool ;
+ u64 timerOffset;
+
+ RGFW_dataTransfer* clipboard;
+
+ RGFW_bool dndBuild;
+ RGFW_dataDropNode* dndRoot;
+ RGFW_dataDropNode* dndCur;
+
+ #ifdef RGFW_X11
+ Display* display;
+ XContext context;
+ Window helperWindow;
+ const char* instName;
+ XErrorEvent* x11Error;
+ i32 xrandrEventBase;
+ XIM im;
+ Window x11Source;
+ long x11Version;
+ i32 x11Format;
+ RGFW_dataTransferType x11TransferType;
+ #endif
+ #ifdef RGFW_WAYLAND
+ struct wl_display* wl_display;
+ struct xkb_context *xkb_context;
+ struct xkb_keymap *keymap;
+ struct xkb_state *xkb_state;
+ struct zxdg_decoration_manager_v1 *decoration_manager;
+ struct zwp_relative_pointer_manager_v1 *relative_pointer_manager;
+ struct zwp_relative_pointer_v1 *relative_pointer;
+ struct zwp_pointer_constraints_v1 *constraint_manager;
+ struct xdg_toplevel_icon_manager_v1 *icon_manager;
+
+ struct zxdg_output_manager_v1 *xdg_output_manager;
+
+ struct wl_data_device_manager *data_device_manager;
+ struct wl_data_device *data_device; // supports clipboard and DND
+ struct wp_pointer_warp_v1* wp_pointer_warp;
+
+ struct wl_keyboard* wl_keyboard;
+ struct wl_pointer* wl_pointer;
+ struct wl_compositor* compositor;
+ struct xdg_wm_base* xdg_wm_base;
+ struct wl_shm* shm;
+ struct wl_seat *seat;
+ struct wl_registry *registry;
+ u32 mouse_enter_serial;
+ struct wl_cursor_theme* wl_cursor_theme;
+ struct wl_surface* cursor_surface;
+ struct xkb_compose_state* composeState;
+
+ RGFW_window* kbOwner;
+ RGFW_window* mouseOwner; /* what window has access to the mouse */
+
+ u32 last_key; /* wayland key repeat data */
+ i32 wl_repeat_info_rate, wl_repeat_info_delay;
+ u32 last_key_time;
+ #endif
+ #ifdef RGFW_WINDOWS
+ HINSTANCE instance;
+ WNDCLASSW wndClass;
+ HWND helperWindow;
+ #endif
+ RGFW_monitors monitors;
+
+ #ifdef RGFW_UNIX
+ int eventWait_forceStop[3];
+ RGFW_dataTransfer* unixClipboard;
+ #endif
+
+ #ifdef RGFW_MACOS
+ void* NSApp;
+ i64 flash;
+ void* customViewClasses[2]; /* NSView and NSOpenGLView */
+ void* customNSAppDelegateClass;
+ void* customWindowDelegateClass;
+ void* customNSAppDelegate;
+ void* tisBundle;
+ #endif
+
+ #ifdef RGFW_OPENGL
+ RGFW_window* current;
+ #endif
+ #ifdef RGFW_EGL
+ void* EGL_display;
+ #endif
+
+ RGFW_bool rawMouse; /* global raw mouse toggle */
+
+ RGFW_windowState windowState; /*! for checking window state events */
+
+ RGFW_keyState mouseButtons[RGFW_mouseFinal];
+ RGFW_keyState keyboard[RGFW_keyLast];
+ float scrollX, scrollY;
+ float vectorX, vectorY;
+};
+#endif /* RGFW_NATIVE_HEADER */
+
+#ifdef RGFW_IMPLEMENTATION
+
+#ifndef RGFW_NO_MATH
+#include <math.h>
+#endif
+
+/* global private API */
+
+/* for C++ / C89 */
+RGFWDEF RGFW_window* RGFW_createWindowPlatform(const char* name, RGFW_windowFlags flags, RGFW_window* win);
+RGFWDEF void RGFW_window_closePlatform(RGFW_window* win);
+RGFWDEF RGFW_bool RGFW_window_setMousePlatform(RGFW_window* win, RGFW_mouse* mouse);
+
+RGFWDEF void RGFW_window_setFlagsInternal(RGFW_window* win, RGFW_windowFlags flags, RGFW_windowFlags cmpFlags);
+
+RGFWDEF void RGFW_initKeycodes(void);
+RGFWDEF void RGFW_initKeycodesPlatform(void);
+RGFWDEF void RGFW_resetPrevState(void);
+RGFWDEF void RGFW_resetKey(void);
+RGFWDEF void RGFW_unloadEGL(void);
+RGFWDEF void RGFW_keyUpdateKeyModsEx(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll);
+RGFWDEF void RGFW_keyUpdateKeyMods(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool scroll);
+RGFWDEF void RGFW_window_showMouseFlags(RGFW_window* win, RGFW_bool show);
+RGFWDEF void RGFW_keyUpdateKeyMod(RGFW_window* win, RGFW_keymod mod, RGFW_bool value);
+
+RGFWDEF void RGFW_monitors_refresh(void);
+RGFWDEF RGFW_monitorNode* RGFW_monitors_add(const RGFW_monitor* mon);
+RGFWDEF void RGFW_monitors_remove(RGFW_monitorNode* node, RGFW_monitorNode* prev);
+
+RGFWDEF void RGFW_windowMaximizedCallback(RGFW_window* win, i32 x, i32 y, i32 w, i32 h);
+RGFWDEF void RGFW_windowMinimizedCallback(RGFW_window* win);
+RGFWDEF void RGFW_windowRestoredCallback(RGFW_window* win, i32 x, i32 y, i32 w, i32 h);
+RGFWDEF void RGFW_windowMovedCallback(RGFW_window* win, i32 x, i32 y);
+RGFWDEF void RGFW_windowResizedCallback(RGFW_window* win, i32 w, i32 h);
+RGFWDEF void RGFW_windowCloseCallback(RGFW_window* win);
+RGFWDEF void RGFW_mouseMotionCallback(RGFW_window* win, i32 x, i32 y);
+RGFWDEF void RGFW_rawMotionCallback(RGFW_window* win, float x, float y);
+RGFWDEF void RGFW_windowRefreshCallback(RGFW_window* win, i32 x, i32 y, i32 w, i32 h);
+RGFWDEF void RGFW_windowFocusCallback(RGFW_window* win, RGFW_bool inFocus);
+RGFWDEF void RGFW_mouseNotifyCallback(RGFW_window* win, i32 x, i32 y, RGFW_bool status);
+RGFWDEF void RGFW_dataDropCallback(RGFW_window* win, const char* data, size_t count, RGFW_dataTransferType dataType);
+RGFWDEF void RGFW_dataDragCallback(RGFW_window* win, RGFW_dataTransferType dataType, RGFW_dndActionType action, i32 x, i32 y);
+RGFWDEF void RGFW_keyCharCallback(RGFW_window* win, u32 codepoint);
+RGFWDEF void RGFW_keyCallback(RGFW_window* win, RGFW_key key, RGFW_keymod mod, RGFW_bool repeat, RGFW_bool press);
+RGFWDEF void RGFW_mouseButtonCallback(RGFW_window* win, RGFW_mouseButton button, RGFW_bool press);
+RGFWDEF void RGFW_mouseScrollCallback(RGFW_window* win, float x, float y);
+RGFWDEF void RGFW_scaleUpdatedCallback(RGFW_window* win, float scaleX, float scaleY);
+RGFWDEF void RGFW_monitorCallback(RGFW_window* win, const RGFW_monitor* monitor, RGFW_bool connected);
+
+RGFWDEF void RGFW_setBit(u32* var, u32 mask, RGFW_bool set);
+RGFWDEF void RGFW_splitBPP(u32 bpp, RGFW_monitorMode* mode);
+
+RGFWDEF void RGFW_window_captureMousePlatform(RGFW_window* win, RGFW_bool state);
+RGFWDEF void RGFW_window_setRawMouseModePlatform(RGFW_window *win, RGFW_bool state);
+
+RGFWDEF void RGFW_copyImageData64(u8* dest_data, i32 w, i32 h, RGFW_format dest_format,
+ u8* src_data, RGFW_format src_format, RGFW_bool is64bit, RGFW_convertImageDataFunc func);
+
+RGFWDEF RGFW_bool RGFW_loadEGL(void);
+
+#ifdef RGFW_OPENGL
+typedef struct RGFW_attribStack {
+ i32* attribs;
+ size_t count;
+ size_t max;
+} RGFW_attribStack;
+RGFWDEF void RGFW_attribStack_init(RGFW_attribStack* stack, i32* attribs, size_t max);
+RGFWDEF void RGFW_attribStack_pushAttrib(RGFW_attribStack* stack, i32 attrib);
+RGFWDEF void RGFW_attribStack_pushAttribs(RGFW_attribStack* stack, i32 attrib1, i32 attrib2);
+
+RGFWDEF RGFW_bool RGFW_extensionSupportedStr(const char* extensions, const char* ext, size_t len);
+#endif
+
+#ifdef RGFW_X11
+RGFWDEF void RGFW_XCreateWindow (XVisualInfo visual, const char* name, RGFW_windowFlags flags, RGFW_window* win);
+#endif
+#ifdef RGFW_MACOS
+RGFWDEF void RGFW_osx_initView(RGFW_window* win);
+#endif
+/* end of global private API defs */
+
+RGFW_info* _RGFW = NULL;
+void RGFW_setInfo(RGFW_info* info) { _RGFW = info; }
+RGFW_info* RGFW_getInfo(void) { return _RGFW; }
+
+
+void* RGFW_alloc(size_t size) { return RGFW_ALLOC(size); }
+void RGFW_free(void* ptr) { RGFW_FREE(ptr); }
+
+void RGFW_setRawMouseMode(RGFW_bool state) {
+ _RGFW->rawMouse = state;
+ RGFW_window_setRawMouseModePlatform(_RGFW->root, state);
+}
+
+const RGFW_dataTransfer* RGFW_readClipboard(void) {
+ RGFW_dataTransfer data_check;
+ RGFW_bool ret = RGFW_readClipboardPtr(NULL, 0, &data_check);
+ if (ret == RGFW_FALSE || data_check.length == 0) return _RGFW->clipboard;
+
+ u8* cont_data = (u8*)RGFW_ALLOC(sizeof(RGFW_dataTransfer) + (size_t)data_check.length);
+ RGFW_ASSERT(cont_data != NULL);
+
+ RGFW_dataTransfer* data = (RGFW_dataTransfer*)(void*)cont_data;
+ ret = RGFW_readClipboardPtr((u8*)&cont_data[sizeof(RGFW_dataTransfer) - 1], data_check.length, data);
+
+ if (ret == RGFW_FALSE || data->length == 0) {
+ RGFW_FREE(cont_data);
+ data = NULL;
+ } else if (_RGFW->clipboard) {
+ RGFW_FREE(_RGFW->clipboard);
+ }
+
+ _RGFW->clipboard = data;
+
+ return _RGFW->clipboard;
+}
+
+/* generic RGFW defines */
+
+void RGFW_initKeycodes(void) {
+ RGFW_MEMZERO(_RGFW->keycodes, sizeof(_RGFW->keycodes));
+ RGFW_initKeycodesPlatform();
+ size_t i, y;
+ for (i = 0; i < RGFW_keyLast; i++) {
+ for (y = 0; y < (sizeof(_RGFW->keycodes) / sizeof(RGFW_key)); y++) {
+ if (_RGFW->keycodes[y] == i) {
+ _RGFW->apiKeycodes[i] = (RGFW_key)y;
+ break;
+ }
+ }
+ }
+
+
+ RGFW_resetKey();
+}
+
+RGFW_key RGFW_apiKeyToRGFW(u32 keycode) {
+ /* make sure the key isn't out of bounds */
+ if (keycode > (sizeof(_RGFW->keycodes) / sizeof(RGFW_key)))
+ return 0;
+
+ return _RGFW->keycodes[keycode];
+}
+
+u32 RGFW_rgfwToApiKey(RGFW_key keycode) {
+ /* make sure the key isn't out of bounds */
+ return _RGFW->apiKeycodes[keycode];
+}
+
+void RGFW_resetKey(void) { RGFW_MEMZERO(_RGFW->keyboard, sizeof(_RGFW->keyboard)); }
+/*
+ this is the end of keycode data
+*/
+
+RGFW_genericFunc RGFW_setEventCallback(RGFW_eventType type, RGFW_genericFunc func) {
+ RGFW_ASSERT(type > RGFW_eventNone && type < RGFW_eventCount);
+ RGFW_init();
+
+ RGFW_genericFunc old = _RGFW->callbacks[type];
+ _RGFW->callbacks[type] = func;
+
+ return old;
+}
+
+void RGFW_setDualEventCallback(RGFW_eventType type, RGFW_genericFunc func, RGFW_genericFunc* first, RGFW_genericFunc* second) {
+ RGFW_genericFunc func1 = RGFW_setEventCallback(type, func);
+ RGFW_genericFunc func2 = RGFW_setEventCallback(type + 1, func);
+
+ if (first) *first = func1;
+ if (second) *second = func2;
+}
+
+void RGFW_setAllEventCallbacks(RGFW_genericFunc func, RGFW_callbacks* callbacks) {
+ for (RGFW_eventType i = RGFW_eventNone + 1; i < RGFW_eventCount; i++) {
+ if (callbacks) callbacks->arr[i] = _RGFW->callbacks[i];
+ RGFW_setEventCallback(i, func);
+ }
+}
+
+RGFW_debugFunc RGFW_setDebugCallback(RGFW_debugFunc func) {
+ RGFW_init();
+ RGFW_debugFunc prev = _RGFW->debugCallbackSrc;
+ _RGFW->debugCallbackSrc = func;
+ return prev;
+}
+
+void RGFW_eventQueuePushAndCall(const RGFW_event* event) {
+ RGFW_ASSERT(event->type > RGFW_eventNone && event->type < RGFW_eventCount);
+ if (_RGFW->callbacks[event->type]) (_RGFW->callbacks[event->type])(event);
+ RGFW_eventQueuePush(event);
+}
+
+void RGFW_windowMaximizedCallback(RGFW_window* win, i32 x, i32 y, i32 w, i32 h) {
+ win->internal.flags |= RGFW_windowMaximize;
+ win->x = x;
+ win->y = y;
+ win->w = w;
+ win->h = h;
+
+ if (!(win->internal.enabledEvents & RGFW_windowMaximizedFlag)) return;
+
+ RGFW_event event;
+ event.type = RGFW_windowMaximized;
+ event.update.x = x;
+ event.update.y = y;
+ event.update.w = w;
+ event.update.h = h;
+ event.common.win = win;
+ RGFW_eventQueuePushAndCall(&event);
+}
+
+void RGFW_windowMinimizedCallback(RGFW_window* win) {
+ win->internal.flags |= RGFW_windowMinimize;
+
+ if (!(win->internal.enabledEvents & RGFW_windowMinimizedFlag)) return;
+
+ RGFW_event event;
+ event.type = RGFW_windowMinimized;
+ event.update.x = win->x;
+ event.update.y = win->y;
+ event.update.w = win->w;
+ event.update.h = win->h;
+ event.common.win = win;
+ RGFW_eventQueuePushAndCall(&event);
+}
+
+void RGFW_windowRestoredCallback(RGFW_window* win, i32 x, i32 y, i32 w, i32 h) {
+ win->internal.flags &= ~(u32)RGFW_windowMinimize;
+ win->x = x;
+ win->y = y;
+ win->w = w;
+ win->h = h;
+
+ if (RGFW_window_isMaximized(win) == RGFW_FALSE) win->internal.flags &= ~(u32)RGFW_windowMaximize;
+
+ if (!(win->internal.enabledEvents & RGFW_windowRestoredFlag)) return;
+
+ RGFW_event event;
+ event.type = RGFW_windowRestored;
+ event.update.x = x;
+ event.update.y = y;
+ event.update.w = w;
+ event.update.h = h;
+ event.common.win = win;
+ RGFW_eventQueuePushAndCall(&event);
+}
+
+void RGFW_windowMovedCallback(RGFW_window* win, i32 x, i32 y) {
+ win->x = x;
+ win->y = y;
+ if (!(win->internal.enabledEvents & RGFW_windowMovedFlag)) return;
+
+ RGFW_event event;
+ event.type = RGFW_windowMoved;
+ event.update.x = x;
+ event.update.y = y;
+ event.update.w = win->w;
+ event.update.h = win->h;
+ event.common.win = win;
+ RGFW_eventQueuePushAndCall(&event);
+}
+
+void RGFW_windowResizedCallback(RGFW_window* win, i32 w, i32 h) {
+ win->w = w;
+ win->h = h;
+
+ if (!(win->internal.enabledEvents & RGFW_windowResizedFlag)) return;
+ RGFW_event event;
+ event.type = RGFW_windowResized;
+ event.update.x = win->x;
+ event.update.y = win->y;
+ event.update.w = w;
+ event.update.h = h;
+ event.common.win = win;
+ RGFW_eventQueuePushAndCall(&event);
+}
+
+void RGFW_windowCloseCallback(RGFW_window* win) {
+ win->internal.shouldClose = RGFW_TRUE;
+
+ RGFW_event event;
+ event.type = RGFW_windowClose;
+ event.common.win = win;
+ RGFW_eventQueuePushAndCall(&event);
+}
+
+void RGFW_mouseMotionCallback(RGFW_window* win, i32 x, i32 y) {
+ win->internal.lastMouseX = x;
+ win->internal.lastMouseY = y;
+
+ if (!(win->internal.enabledEvents & RGFW_mouseMotionFlag)) return;
+
+ RGFW_event event;
+ event.type = RGFW_mouseMotion;
+ event.mouse.x = x;
+ event.mouse.y = y;
+ event.mouse.inWindow = win->internal.mouseInside;
+ event.common.win = win;
+ RGFW_eventQueuePushAndCall(&event);
+}
+
+void RGFW_rawMotionCallback(RGFW_window* win, float x, float y) {
+ _RGFW->vectorX = x;
+ _RGFW->vectorY = y;
+ if (!(win->internal.enabledEvents & RGFW_mouseRawMotionFlag)) return;
+
+ RGFW_event event;
+ event.type = RGFW_mouseRawMotion;
+ event.delta.x = x;
+ event.delta.y = y;
+ event.common.win = win;
+ RGFW_eventQueuePushAndCall(&event);
+}
+
+void RGFW_windowRefreshCallback(RGFW_window* win, i32 x, i32 y, i32 w, i32 h) {
+ if (!(win->internal.enabledEvents & RGFW_windowRefreshFlag)) return;
+ RGFW_event event;
+ event.type = RGFW_windowRefresh;
+ event.update.x = x;
+ event.update.y = y;
+ event.update.w = w;
+ event.update.h = h;
+ event.common.win = win;
+ RGFW_eventQueuePushAndCall(&event);
+}
+
+void RGFW_windowFocusCallback(RGFW_window* win, RGFW_bool inFocus) {
+ win->internal.inFocus = inFocus;
+
+ if (win->internal.captureMouse) {
+ RGFW_window_captureMousePlatform(win, inFocus);
+ }
+
+ RGFW_event event;
+ event.common.win = win;
+ event.focus.state = inFocus;
+
+ if (inFocus == RGFW_TRUE) {
+ if ((win->internal.flags & RGFW_windowFullscreen))
+ RGFW_window_raise(win);
+
+ event.type = RGFW_windowFocusIn;
+ } else if (inFocus == RGFW_FALSE) {
+ if ((win->internal.flags & RGFW_windowFullscreen))
+ RGFW_window_minimize(win);
+
+ size_t key;
+ for (key = 0; key < RGFW_keyLast; key++) {
+ if (RGFW_isKeyDown((u8)key) == RGFW_FALSE) continue;
+
+ _RGFW->keyboard[key].current = RGFW_FALSE;
+ if ((win->internal.enabledEvents & RGFW_BIT(RGFW_keyReleased))) {
+ RGFW_keyCallback(win, (u8)key, win->internal.mod, RGFW_FALSE, RGFW_FALSE);
+ }
+ }
+
+ RGFW_resetKey();
+ event.type = RGFW_windowFocusOut;
+ }
+
+ event.common.win = win;
+
+ RGFW_eventQueuePushAndCall(&event);
+}
+
+void RGFW_mouseNotifyCallback(RGFW_window* win, i32 x, i32 y, RGFW_bool status) {
+ win->internal.mouseInside = status;
+ _RGFW->windowState.win = win;
+
+ win->internal.lastMouseX = x;
+ win->internal.lastMouseY = y;
+
+ RGFW_event event;
+ event.common.win = win;
+ event.mouse.x = x;
+ event.mouse.y = y;
+ event.mouse.inWindow = win->internal.mouseInside;
+
+ if (status) {
+ if (!(win->internal.enabledEvents & RGFW_mouseEnterFlag)) return;
+ _RGFW->windowState.mouseEnter = RGFW_TRUE;
+ _RGFW->windowState.win = win;
+ event.type = RGFW_mouseEnter;
+ } else {
+ if (!(win->internal.enabledEvents & RGFW_mouseLeaveFlag)) return;
+ _RGFW->windowState.winLeave = win;
+ _RGFW->windowState.mouseLeave = RGFW_TRUE;
+ event.type = RGFW_mouseLeave;
+ }
+
+ RGFW_eventQueuePushAndCall(&event);
+}
+
+void RGFW_dataDropCallback(RGFW_window* win, const char* data, size_t length, RGFW_dataTransferType dataType) {
+ if (!(win->internal.enabledEvents & RGFW_dataDropFlag) || !(win->internal.flags & RGFW_windowAllowDND))
+ return;
+
+ _RGFW->windowState.win = win;
+ _RGFW->windowState.dataDrop = RGFW_TRUE;
+ _RGFW->windowState.dataLength = length;
+
+ RGFW_dataDropNode node;
+ RGFW_MEMZERO(&node, sizeof(node));
+ node.data = data;
+ node.length = length;
+ node.type = dataType;
+ node.next = NULL;
+
+ RGFW_event event;
+ event.type = RGFW_dataDrop;
+ event.drop.value = &node;
+ event.drop.win = win;
+
+ if (_RGFW->callbacks[event.type]) (_RGFW->callbacks[event.type])(&event);
+
+ if (_RGFW->queueEvents == RGFW_TRUE || _RGFW->dndBuild) {
+ if (_RGFW->dndRoot == NULL) {
+ _RGFW->dndRoot = (RGFW_dataDropNode*)RGFW_ALLOC(sizeof(RGFW_dataDropNode));
+ _RGFW->dndCur = _RGFW->dndRoot;
+ } else if (_RGFW->dndCur) {
+ _RGFW->dndCur->next = (RGFW_dataDropNode*)RGFW_ALLOC(sizeof(RGFW_dataDropNode));
+ _RGFW->dndCur = _RGFW->dndCur->next;
+ } else { RGFW_ASSERT(0); }
+
+ char* dataCopy = (char*)RGFW_ALLOC(length);
+ RGFW_MEMCPY(dataCopy, data, length);
+ node.data = dataCopy;
+
+ RGFW_MEMCPY(_RGFW->dndCur, &node, sizeof(node));
+
+ event.drop.value = _RGFW->dndCur;
+ RGFW_eventQueuePush(&event);
+ }
+}
+
+void RGFW_dataDragCallback(RGFW_window* win, RGFW_dataTransferType dataType, RGFW_dndActionType action, i32 x, i32 y) {
+ if (!(win->internal.enabledEvents & RGFW_dataDragFlag) || !(win->internal.flags & RGFW_windowAllowDND)) return;
+
+ _RGFW->windowState.win = win;
+ _RGFW->windowState.dataDragging = RGFW_TRUE;
+ _RGFW->windowState.dropX = x;
+ _RGFW->windowState.dropY = y;
+
+ RGFW_event event;
+ event.type = RGFW_dataDrag;
+ event.drag.x = x;
+ event.drag.y = y;
+ event.drag.action = action;
+ event.drag.dataType = dataType;
+ event.common.win = win;
+
+ RGFW_eventQueuePushAndCall(&event);
+}
+
+void RGFW_keyCharCallback(RGFW_window* win, u32 codepoint) {
+ if (!(win->internal.enabledEvents & RGFW_keyCharFlag)) return;
+
+ RGFW_event event;
+ event.type = RGFW_keyChar;
+ event.keyChar.value = codepoint;
+ event.common.win = win;
+ RGFW_eventQueuePushAndCall(&event);
+}
+
+void RGFW_keyCallback(RGFW_window* win, RGFW_key key, RGFW_keymod mod, RGFW_bool repeat, RGFW_bool state) {
+ RGFW_event event;
+
+ if (state) {
+ if (!(win->internal.enabledEvents & RGFW_keyPressedFlag)) return;
+ event.type = RGFW_keyPressed;
+ } else {
+ if (!(win->internal.enabledEvents & RGFW_keyReleasedFlag)) return;
+ event.type = RGFW_keyReleased;
+ }
+
+ _RGFW->keyboard[key].prev = _RGFW->keyboard[key].current;
+ _RGFW->keyboard[key].current = state;
+
+ event.key.value = key;
+ event.key.repeat = repeat;
+ event.key.mod = mod;
+ event.key.state = state;
+ event.common.win = win;
+ RGFW_eventQueuePushAndCall(&event);
+}
+
+void RGFW_mouseButtonCallback(RGFW_window* win, RGFW_mouseButton button, RGFW_bool press) {
+ RGFW_event event;
+
+ if (press) {
+ if (!(win->internal.enabledEvents & RGFW_mouseButtonPressedFlag)) return;
+ event.type = RGFW_mouseButtonPressed;
+ } else {
+ if (!(win->internal.enabledEvents & RGFW_mouseButtonReleasedFlag)) return;
+ event.type = RGFW_mouseButtonReleased;
+ }
+
+ _RGFW->mouseButtons[button].prev = _RGFW->mouseButtons[button].current;
+ _RGFW->mouseButtons[button].current = press;
+
+ event.button.value = button;
+ event.button.state = press;
+ event.common.win = win;
+ RGFW_eventQueuePushAndCall(&event);
+}
+
+void RGFW_mouseScrollCallback(RGFW_window* win, float x, float y) {
+ if (!(win->internal.enabledEvents & RGFW_mouseScrollFlag)) return;
+ _RGFW->scrollX = x;
+ _RGFW->scrollY = y;
+
+ RGFW_event event;
+ event.type = RGFW_mouseScroll;
+ event.delta.x = x;
+ event.delta.y = y;
+ event.common.win = win;
+ RGFW_eventQueuePushAndCall(&event);
+}
+
+void RGFW_scaleUpdatedCallback(RGFW_window* win, float scaleX, float scaleY) {
+ if (!(win->internal.enabledEvents & RGFW_scaleUpdatedFlag)) return;
+
+ RGFW_event event;
+ event.type = RGFW_scaleUpdated;
+ event.scale.x = scaleX;
+ event.scale.y = scaleY;
+ event.common.win = win;
+ RGFW_eventQueuePushAndCall(&event);
+}
+
+void RGFW_monitorCallback(RGFW_window* win, const RGFW_monitor* monitor, RGFW_bool connected) {
+ if (win) {
+ if (connected && !(win->internal.enabledEvents & RGFW_monitorConnectedFlag)) return;
+ if (!connected && !(win->internal.enabledEvents & RGFW_monitorDisconnectedFlag)) return;
+ }
+
+ RGFW_event event;
+ event.type = (connected) ? (RGFW_eventType)(RGFW_monitorConnected) : (RGFW_eventType)(RGFW_monitorDisconnected);
+ event.monitor.monitor = monitor;
+ event.monitor.state = connected;
+ event.common.win = win;
+ RGFW_eventQueuePushAndCall(&event);
+}
+
+#ifdef RGFW_DEBUG
+#include <stdio.h>
+#endif
+
+void RGFW_debugCallback(RGFW_debugType type, RGFW_errorCode code, const char* msg) {
+ RGFW_debugInfo info;
+ info.type = type;
+ info.code = code;
+ info.msg = msg;
+
+ if (_RGFW && _RGFW->debugCallbackSrc) _RGFW->debugCallbackSrc(&info);
+
+ #ifdef RGFW_DEBUG
+ switch (type) {
+ case RGFW_typeInfo: RGFW_PRINTF("RGFW INFO (%i %i): %s", type, code, msg); break;
+ case RGFW_typeError: RGFW_PRINTF("RGFW DEBUG (%i %i): %s", type, code, msg); break;
+ case RGFW_typeWarning: RGFW_PRINTF("RGFW WARNING (%i %i): %s", type, code, msg); break;
+ default: break;
+ }
+
+ RGFW_PRINTF("\n");
+ #endif
+}
+
+void RGFW_window_checkMode(RGFW_window* win);
+void RGFW_window_checkMode(RGFW_window* win) {
+ if (RGFW_window_isMinimized(win) && (win->internal.enabledEvents & RGFW_windowMinimizedFlag)) {
+ RGFW_windowMinimizedCallback(win);
+ } else if (RGFW_window_isMaximized(win) && (win->internal.enabledEvents & RGFW_windowMaximizedFlag)) {
+ RGFW_windowMaximizedCallback(win, win->x, win->y, win->w, win->h);
+ } else if ((((win->internal.flags & RGFW_windowMinimize) && !RGFW_window_isMaximized(win)) ||
+ (win->internal.flags & RGFW_windowMaximize && !RGFW_window_isMaximized(win))) && (win->internal.enabledEvents & RGFW_windowRestoredFlag)) {
+ RGFW_windowRestoredCallback(win, win->x, win->y, win->w, win->h);
+ }
+}
+
+/*
+no more event call back defines
+*/
+
+size_t RGFW_sizeofInfo(void) { return sizeof(RGFW_info); }
+size_t RGFW_sizeofNativeImage(void) { return sizeof(RGFW_nativeImage); }
+size_t RGFW_sizeofSurface(void) { return sizeof(RGFW_surface); }
+size_t RGFW_sizeofWindow(void) { return sizeof(RGFW_window); }
+size_t RGFW_sizeofWindowSrc(void) { return sizeof(RGFW_window_src); }
+
+RGFW_window_src* RGFW_window_getSrc(RGFW_window* win) { return &win->src; }
+RGFW_bool RGFW_window_getPosition(RGFW_window* win, i32* x, i32* y) { if (x) *x = win->x; if (y) *y = win->y; return RGFW_TRUE; }
+RGFW_bool RGFW_window_getSize(RGFW_window* win, i32* w, i32* h) { if (w) *w = win->w; if (h) *h = win->h; return RGFW_TRUE; }
+u32 RGFW_window_getFlags(RGFW_window* win) { return win->internal.flags; }
+RGFW_key RGFW_window_getExitKey(RGFW_window* win) { return win->internal.exitKey; }
+void RGFW_window_setExitKey(RGFW_window* win, RGFW_key key) { win->internal.exitKey = key; }
+void RGFW_window_setEnabledEvents(RGFW_window* win, RGFW_eventFlag events) { win->internal.enabledEvents = events; }
+RGFW_eventFlag RGFW_window_getEnabledEvents(RGFW_window* win) { return win->internal.enabledEvents; }
+void RGFW_window_setDisabledEvents(RGFW_window* win, RGFW_eventFlag events) { RGFW_window_setEnabledEvents(win, (RGFW_allEventFlags) & ~(u32)events); }
+void RGFW_window_setEventState(RGFW_window* win, RGFW_eventFlag event, RGFW_bool state) { RGFW_setBit(&win->internal.enabledEvents, event, state); }
+void* RGFW_window_getUserPtr(RGFW_window* win) { return win->userPtr; }
+void RGFW_window_setUserPtr(RGFW_window* win, void* ptr) { win->userPtr = ptr; }
+
+RGFW_bool RGFW_window_getSizeInPixels(RGFW_window* win, i32* w, i32* h) {
+ RGFW_monitor* mon = RGFW_window_getMonitor(win);
+ if (mon == NULL) return RGFW_FALSE;
+
+ if (w) *w = (i32)((float)win->w * mon->pixelRatio);
+ if (h) *h = (i32)((float)win->h * mon->pixelRatio);
+
+ return RGFW_TRUE;
+}
+
+
+#if defined(RGFW_USE_XDL) && defined(RGFW_X11)
+ #define XDL_IMPLEMENTATION
+ #include "XDL.h"
+#endif
+
+#ifndef RGFW_NO_STATIC_CONTEXT
+
+i32 RGFW_init(void) {
+ static RGFW_info _rgfwGlobal;
+ return RGFW_init_ptr(&_rgfwGlobal);
+}
+
+
+void RGFW_deinit(void) { RGFW_deinit_ptr(_RGFW); }
+
+#else
+
+RGFW_info* _rgfwGlobal;
+
+i32 RGFW_init(void) {
+ if (_rgfwGlobal != NULL) {
+ RGFW_FREE(_rgfwGlobal);
+ }
+
+ _rgfwGlobal = (RGFW_info*)RGFW_ALLOC(sizeof(RGFW_info));
+
+ return RGFW_init_ptr(&_rgfwGlobal);
+}
+
+void RGFW_deinit(void) {
+ if (_RGFW == _rgfwGlobal) {
+ RGFW_FREE(_rgfwGlobal);
+ _rgfwGlobal = NULL;
+ }
+ RGFW_deinit_ptr(_RGFW);
+}
+
+#endif
+
+i32 RGFW_initPlatform(void);
+void RGFW_deinitPlatform(void);
+
+i32 RGFW_init_ptr(RGFW_info* info) {
+ if (info == _RGFW || info == NULL) return 1;
+
+ RGFW_setInfo(info);
+ RGFW_MEMZERO(_RGFW, sizeof(RGFW_info));
+ _RGFW->queueEvents = RGFW_FALSE;
+ _RGFW->polledEvents = RGFW_FALSE;
+
+ #if (RGFW_PREALLOCATED_MONITORS)
+ _RGFW->monitors.freeList.head = &_RGFW->monitors.data[0];
+ _RGFW->monitors.freeList.cur = _RGFW->monitors.freeList.head;
+
+ for (size_t i = 1; i < RGFW_PREALLOCATED_MONITORS; i++) {
+ RGFW_monitorNode* newNode = &_RGFW->monitors.data[i];
+ _RGFW->monitors.freeList.cur->next = newNode;
+ _RGFW->monitors.freeList.cur = _RGFW->monitors.freeList.cur->next;
+ }
+ #endif
+
+ _RGFW->monitors.list.head = NULL;
+ _RGFW->monitors.list.head = NULL;
+ RGFW_initKeycodes();
+ i32 out = RGFW_initPlatform();
+
+ for (size_t i = 0; i < RGFW_mouseIconCount; i++) {
+ _RGFW->standardMice[i] = RGFW_createMouseStandard((RGFW_mouseIcon)i);
+ }
+
+ RGFW_pollMonitors();
+
+ RGFW_debugCallback(RGFW_typeInfo, RGFW_infoGlobal, "global context initialized");
+
+ return out;
+}
+
+#ifndef RGFW_EGL
+void RGFW_unloadEGL(void) { }
+#endif
+
+void RGFW_deinit_ptr(RGFW_info* info) {
+ if (info == NULL) return;
+
+ RGFW_setInfo(info);
+ RGFW_unloadEGL();
+
+ for (RGFW_mouseIcon i = 0; i < RGFW_mouseIconCount; i++) {
+ if (_RGFW->standardMice[i]) RGFW_freeMouse(_RGFW->standardMice[i]);
+ }
+
+ RGFW_debugCallback(RGFW_typeInfo, RGFW_infoGlobal, "global context deinitialized");
+ RGFW_deinitPlatform();
+
+ _RGFW->root = NULL;
+ _RGFW->windowCount = 0;
+ RGFW_setInfo(NULL);
+}
+
+RGFW_window* RGFW_createWindow(const char* name, i32 x, i32 y, i32 w, i32 h, RGFW_windowFlags flags) {
+ RGFW_window* win = (RGFW_window*)RGFW_ALLOC(sizeof(RGFW_window));
+ RGFW_ASSERT(win != NULL);
+ return RGFW_createWindowPtr(name, x, y, w, h, flags, win);
+}
+
+void RGFW_window_close(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_window_closePtr(win);
+ RGFW_FREE(win);
+}
+
+RGFW_window* RGFW_createWindowPtr(const char* name, i32 x, i32 y, i32 w, i32 h, RGFW_windowFlags flags, RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ if (name == NULL) name = "\0";
+
+ RGFW_MEMZERO(win, sizeof(RGFW_window));
+
+ if (_RGFW == NULL) RGFW_init();
+ _RGFW->windowCount++;
+
+ /* rect based the requested flags */
+ if (_RGFW->root == NULL) {
+ RGFW_setRootWindow(win);
+ }
+
+ /* set and init the new window's data */
+ win->x = x;
+ win->y = y;
+ win->w = w;
+ win->h = h;
+ win->internal.mouse = _RGFW->standardMice[RGFW_mouseNormal];
+ win->internal.flags = flags;
+ win->internal.enabledEvents = RGFW_allEventFlags;
+
+ RGFW_windowFlags reservedFlags = flags & (RGFW_windowScaleToMonitor);
+ flags &= ~reservedFlags;
+
+ RGFW_window* ret = RGFW_createWindowPlatform(name, flags, win);
+
+ flags |= reservedFlags;
+
+#ifndef RGFW_X11
+ RGFW_window_setFlagsInternal(win, flags, 0);
+#endif
+
+#ifdef RGFW_OPENGL
+ win->src.gfxType = 0;
+ if (flags & RGFW_windowOpenGL)
+ RGFW_window_createContext_OpenGL(win, RGFW_getGlobalHints_OpenGL());
+#endif
+
+#ifdef RGFW_EGL
+ if (flags & RGFW_windowEGL)
+ RGFW_window_createContext_EGL(win, RGFW_getGlobalHints_OpenGL());
+#endif
+
+ /* X11 creates the window after the OpenGL context is created (because of visual garbage),
+ * so we have to wait to set the flags
+ * This is required so that way the user can create their own OpenGL context after RGFW_createWindow is used
+ * if a window is created, CreateContext will delete the window and create a new one
+ * */
+#ifdef RGFW_X11
+ RGFW_window_setFlagsInternal(win, flags, 0);
+#endif
+
+#ifdef RGFW_MACOS
+ /*NOTE: another OpenGL/setFlags related hack, this because OSX the 'view' class must be setup after the NSOpenGL view is made AND after setFlags happens */
+ RGFW_osx_initView(win);
+#endif
+
+#ifdef RGFW_WAYLAND
+ /* recieve all events needed to configure the surface */
+ /* also gets the wl_outputs */
+ if (RGFW_usingWayland()) {
+ wl_display_roundtrip(_RGFW->wl_display);
+ /* NOTE: this is a hack so that way wayland spawns a window, even if nothing is drawn */
+ if (!(flags & RGFW_windowOpenGL) && !(flags & RGFW_windowEGL)) {
+ u8* data = (u8*)RGFW_ALLOC((u32)(win->w * win->h * 3));
+ RGFW_MEMZERO(data, (u32)(win->w * win->h * 3) * sizeof(u8));
+ RGFW_surface* surface = RGFW_createSurface(data, win->w, win->h, RGFW_formatBGR8);
+ RGFW_window_blitSurface(win, surface);
+ RGFW_FREE(data);
+ RGFW_surface_free(surface);
+ }
+ }
+#endif
+
+ if (!(flags & RGFW_windowHideMouse)) {
+ RGFW_window_setMouseDefault(win);
+ }
+
+ RGFW_window_setName(win, name);
+ if (!(flags & RGFW_windowHide)) {
+ flags |= RGFW_windowHide;
+ RGFW_window_show(win);
+ }
+
+ RGFW_debugCallback(RGFW_typeInfo, RGFW_infoWindow, "a new window was created");
+
+ return ret;
+}
+
+void RGFW_window_closePtr(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+
+ if (win->internal.captureMouse) {
+ RGFW_window_captureMouse(win, RGFW_FALSE);
+ }
+
+ #ifdef RGFW_EGL
+ if ((win->src.gfxType & RGFW_gfxEGL) && win->src.ctx.egl) {
+ RGFW_window_deleteContext_EGL(win, win->src.ctx.egl);
+ win->src.ctx.egl = NULL;
+ }
+ #endif
+
+ #ifdef RGFW_OPENGL
+ if ((win->src.gfxType & RGFW_gfxNativeOpenGL) && win->src.ctx.native) {
+ RGFW_window_deleteContext_OpenGL(win, win->src.ctx.native);
+ win->src.ctx.native = NULL;
+ }
+ #endif
+
+ RGFW_window_closePlatform(win);
+
+ if (_RGFW->clipboard != NULL)
+ RGFW_FREE(_RGFW->clipboard);
+ _RGFW->clipboard = NULL;
+
+ _RGFW->windowCount--;
+ RGFW_debugCallback(RGFW_typeInfo, RGFW_infoWindow, "a window was freed");
+
+ if (_RGFW->windowCount == 0 && !(win->internal.flags & RGFW_noDeinitOnClose)) RGFW_deinit();
+}
+
+void RGFW_setQueueEvents(RGFW_bool queue) { _RGFW->queueEvents = RGFW_BOOL(queue); }
+
+void RGFW_eventQueueFlush(void) { _RGFW->eventLen = 0; }
+
+void RGFW_eventQueuePush(const RGFW_event* event) {
+ if (_RGFW->queueEvents == RGFW_FALSE) return;
+ RGFW_ASSERT(_RGFW->eventLen >= 0);
+
+ if (_RGFW->eventLen >= RGFW_MAX_EVENTS) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errEventQueue, "Event queue limit 'RGFW_MAX_EVENTS' has been reached automatically flushing queue.");
+ RGFW_eventQueueFlush();
+ return;
+ }
+
+ i32 eventTop = (_RGFW->eventBottom + _RGFW->eventLen) % RGFW_MAX_EVENTS;
+ _RGFW->eventLen += 1;
+ _RGFW->events[eventTop] = *event;
+}
+
+RGFW_event* RGFW_eventQueuePop(void) {
+ RGFW_ASSERT(_RGFW->eventLen >= 0 && _RGFW->eventLen <= RGFW_MAX_EVENTS);
+ RGFW_event* ev;
+
+ if (_RGFW->eventLen == 0) {
+ return NULL;
+ }
+
+ ev = &_RGFW->events[_RGFW->eventBottom];
+ _RGFW->eventLen -= 1;
+ _RGFW->eventBottom = (_RGFW->eventBottom + 1) % RGFW_MAX_EVENTS;
+
+ return ev;
+}
+
+RGFW_bool RGFW_checkEvent(RGFW_event* event) {
+ if (_RGFW->eventLen == 0 && _RGFW->polledEvents == RGFW_FALSE) {
+ _RGFW->queueEvents = RGFW_TRUE;
+ RGFW_pollEvents();
+ _RGFW->polledEvents = RGFW_TRUE;
+ }
+
+ if (RGFW_checkQueuedEvent(event) == RGFW_FALSE) {
+ _RGFW->polledEvents = RGFW_FALSE;
+ return RGFW_FALSE;
+ }
+
+ return RGFW_TRUE;
+}
+
+RGFW_bool RGFW_checkQueuedEvent(RGFW_event* event) {
+ RGFW_event* ev;
+ _RGFW->queueEvents = RGFW_TRUE;
+ /* check queued events */
+ ev = RGFW_eventQueuePop();
+ if (ev != NULL) {
+ *event = *ev;
+ return RGFW_TRUE;
+ }
+
+ return RGFW_FALSE;
+}
+
+void RGFW_resetPrevState(void) {
+ size_t i; /*!< reset each previous state */
+ for (i = 0; i < RGFW_keyLast; i++) _RGFW->keyboard[i].prev = _RGFW->keyboard[i].current;
+ for (i = 0; i < RGFW_mouseFinal; i++) _RGFW->mouseButtons[i].prev = _RGFW->mouseButtons[i].current;
+ _RGFW->scrollX = 0.0f;
+ _RGFW->scrollY = 0.0f;
+ _RGFW->vectorX = (float)0.0f;
+ _RGFW->vectorY = (float)0.0f;
+ RGFW_MEMZERO(&_RGFW->windowState, sizeof(_RGFW->windowState));
+
+ for (RGFW_dataDropNode* node = _RGFW->dndRoot; node; ) {
+ RGFW_dataDropNode* next = node->next;
+ RGFW_FREE(node);
+ node = next;
+ }
+
+ _RGFW->dndRoot = NULL;
+ _RGFW->dndCur = NULL;
+}
+
+RGFW_bool RGFW_isKeyPressed(RGFW_key key) {
+ RGFW_ASSERT(_RGFW != NULL);
+ return _RGFW->keyboard[key].current && !_RGFW->keyboard[key].prev;
+}
+RGFW_bool RGFW_isKeyDown(RGFW_key key) {
+ RGFW_ASSERT(_RGFW != NULL);
+ return _RGFW->keyboard[key].current;
+}
+RGFW_bool RGFW_isKeyReleased(RGFW_key key) {
+ RGFW_ASSERT(_RGFW != NULL);
+ return !_RGFW->keyboard[key].current && _RGFW->keyboard[key].prev;
+}
+
+
+RGFW_bool RGFW_isMousePressed(RGFW_mouseButton button) {
+ RGFW_ASSERT(_RGFW != NULL);
+ return _RGFW->mouseButtons[button].current && !_RGFW->mouseButtons[button].prev;
+}
+RGFW_bool RGFW_isMouseDown(RGFW_mouseButton button) {
+ RGFW_ASSERT(_RGFW != NULL);
+ return _RGFW->mouseButtons[button].current;
+}
+RGFW_bool RGFW_isMouseReleased(RGFW_mouseButton button) {
+ RGFW_ASSERT(_RGFW != NULL);
+ return !_RGFW->mouseButtons[button].current && _RGFW->mouseButtons[button].prev;
+}
+
+void RGFW_getMouseScroll(float* x, float* y) {
+ RGFW_ASSERT(_RGFW != NULL);
+ if (x) *x = _RGFW->scrollX;
+ if (y) *y = _RGFW->scrollY;
+}
+
+void RGFW_getMouseVector(float* x, float* y) {
+ RGFW_ASSERT(_RGFW != NULL);
+ if (x) *x = _RGFW->vectorX;
+ if (y) *y = _RGFW->vectorY;
+}
+
+RGFW_bool RGFW_window_didMouseLeave(RGFW_window* win) { return _RGFW->windowState.winLeave == win && _RGFW->windowState.mouseLeave; }
+RGFW_bool RGFW_window_didMouseEnter(RGFW_window* win) { return _RGFW->windowState.win == win && _RGFW->windowState.mouseEnter; }
+RGFW_bool RGFW_window_isMouseInside(RGFW_window* win) { return win->internal.mouseInside; }
+
+RGFW_bool RGFW_window_isDataDragging(RGFW_window* win) { return RGFW_window_getDataDrag(win, (i32*)NULL, (i32*)NULL); }
+RGFW_bool RGFW_window_didDataDrop(RGFW_window* win) { return RGFW_window_getDataDrop(win) != NULL;}
+
+
+RGFW_bool RGFW_window_getDataDrag(RGFW_window* win, i32* x, i32* y) {
+ if (_RGFW->windowState.win != win || _RGFW->windowState.dataDragging == RGFW_FALSE) return RGFW_FALSE;
+ if (x) *x = _RGFW->windowState.dropX;
+ if (y) *y = _RGFW->windowState.dropY;
+ return RGFW_TRUE;
+}
+RGFW_dataDropNode* RGFW_window_getDataDrop(RGFW_window* win) {
+ if (_RGFW->windowState.win != win || _RGFW->windowState.dataDrop == RGFW_FALSE) return NULL;
+ return _RGFW->dndRoot;
+}
+
+RGFW_bool RGFW_window_checkEvent(RGFW_window* win, RGFW_event* event) {
+ if (_RGFW->eventLen == 0 && _RGFW->polledEvents == RGFW_FALSE) {
+ _RGFW->queueEvents = RGFW_TRUE;
+ RGFW_pollEvents();
+ _RGFW->polledEvents = RGFW_TRUE;
+ }
+
+ if (RGFW_window_checkQueuedEvent(win, event) == RGFW_FALSE) {
+ _RGFW->polledEvents = RGFW_FALSE;
+ return RGFW_FALSE;
+ }
+
+ return RGFW_TRUE;
+}
+
+RGFW_bool RGFW_window_checkQueuedEvent(RGFW_window* win, RGFW_event* event) {
+ RGFW_event* ev;
+ RGFW_ASSERT(win != NULL);
+ _RGFW->queueEvents = RGFW_TRUE;
+ /* check queued events */
+ ev = RGFW_window_eventQueuePop(win);
+ if (ev == NULL) return RGFW_FALSE;
+
+ *event = *ev;
+ return RGFW_TRUE;
+}
+
+RGFW_event* RGFW_window_eventQueuePop(RGFW_window* win) {
+ RGFW_event* ev = RGFW_eventQueuePop();
+ if (ev == NULL) return ev;
+
+ for (i32 i = 1; i < _RGFW->eventLen && ev->common.win != win && ev->common.win != NULL; i++) {
+ RGFW_eventQueuePush(ev);
+ ev = RGFW_eventQueuePop();
+ }
+
+ if (ev->common.win != win && ev->common.win != NULL) {
+ return NULL;
+ }
+
+ return ev;
+}
+
+void RGFW_setRootWindow(RGFW_window* win) { _RGFW->root = win; }
+RGFW_window* RGFW_getRootWindow(void) { return _RGFW->root; }
+
+#ifndef RGFW_EGL
+RGFW_bool RGFW_loadEGL(void) { return RGFW_FALSE; }
+#endif
+
+void RGFW_window_setFlagsInternal(RGFW_window* win, RGFW_windowFlags flags, RGFW_windowFlags cmpFlags) {
+ if (flags & RGFW_windowNoBorder) RGFW_window_setBorder(win, 0);
+ else if (cmpFlags & RGFW_windowNoBorder) RGFW_window_setBorder(win, 1);
+ if (flags & RGFW_windowMaximize) RGFW_window_maximize(win);
+ else if (cmpFlags & RGFW_windowMaximize) RGFW_window_restore(win);
+ if (flags & RGFW_windowMinimize) RGFW_window_minimize(win);
+ else if (cmpFlags & RGFW_windowMinimize) RGFW_window_restore(win);
+ if (flags & RGFW_windowCenter) RGFW_window_center(win);
+ if (flags & RGFW_windowCenterCursor) RGFW_window_moveMouse(win, win->x + (win->w / 2), win->y + (win->h / 2));
+ if (flags & RGFW_windowFullscreen) RGFW_window_setFullscreen(win, RGFW_TRUE);
+ else if (cmpFlags & RGFW_windowFullscreen) RGFW_window_setFullscreen(win, 0);
+ if (flags & RGFW_windowHideMouse) RGFW_window_showMouse(win, 0);
+ else if (cmpFlags & RGFW_windowHideMouse) RGFW_window_showMouse(win, 1);
+ if (flags & RGFW_windowHide) RGFW_window_hide(win);
+ else if (cmpFlags & RGFW_windowHide) RGFW_window_show(win);
+ if (flags & RGFW_windowFloating) RGFW_window_setFloating(win, 1);
+ else if (cmpFlags & RGFW_windowFloating) RGFW_window_setFloating(win, 0);
+ if (flags & RGFW_windowRawMouse) RGFW_window_setRawMouseMode(win, RGFW_TRUE);
+ else if (cmpFlags & RGFW_windowRawMouse) RGFW_window_setRawMouseMode(win, RGFW_FALSE);
+ if (flags & RGFW_windowCaptureMouse) RGFW_window_captureRawMouse(win, RGFW_TRUE);
+ else if (cmpFlags & RGFW_windowCaptureMouse) RGFW_window_captureMouse(win, RGFW_FALSE);
+ if (flags & RGFW_windowFocus) RGFW_window_focus(win);
+ if (flags & RGFW_windowScaleToMonitor) RGFW_window_scaleToMonitor(win);
+
+ if (flags & RGFW_windowNoResize) {
+ RGFW_window_setMaxSize(win, win->w, win->h);
+ RGFW_window_setMinSize(win, win->w, win->h);
+ } else if (cmpFlags & RGFW_windowNoResize) {
+ RGFW_window_setMaxSize(win, 0, 0);
+ RGFW_window_setMinSize(win, 0, 0);
+ }
+
+ win->internal.flags = flags;
+}
+
+
+void RGFW_window_setFlags(RGFW_window* win, RGFW_windowFlags flags) { RGFW_window_setFlagsInternal(win, flags, win->internal.flags); }
+
+RGFW_bool RGFW_window_isInFocus(RGFW_window* win) {
+#ifdef RGFW_WASM
+ return RGFW_TRUE;
+#else
+ return RGFW_BOOL(win->internal.inFocus);
+#endif
+}
+
+const char* RGFW_className = "RGFW";
+void RGFW_setClassName(const char* name) { RGFW_className = (name != NULL) ? name : "RGFW"; }
+void RGFW_setBuildDND(RGFW_bool state) { _RGFW->dndBuild = state; }
+
+#ifndef RGFW_X11
+void RGFW_setXInstName(const char* name) { RGFW_UNUSED(name); }
+#endif
+
+RGFW_bool RGFW_window_getMouse(RGFW_window* win, i32* x, i32* y) {
+ RGFW_ASSERT(win != NULL);
+ if (x) *x = win->internal.lastMouseX;
+ if (y) *y = win->internal.lastMouseY;
+ return RGFW_TRUE;
+}
+
+RGFW_bool RGFW_window_isKeyPressed(RGFW_window* win, RGFW_key key) { return RGFW_isKeyPressed(key) && RGFW_window_isInFocus(win); }
+RGFW_bool RGFW_window_isKeyDown(RGFW_window* win, RGFW_key key) { return RGFW_isKeyDown(key) && RGFW_window_isInFocus(win); }
+RGFW_bool RGFW_window_isKeyReleased(RGFW_window* win, RGFW_key key) { return RGFW_isKeyReleased(key) && RGFW_window_isInFocus(win); }
+
+RGFW_bool RGFW_window_isMousePressed(RGFW_window* win, RGFW_mouseButton button) { return RGFW_isMousePressed(button) && RGFW_window_isInFocus(win); }
+RGFW_bool RGFW_window_isMouseDown(RGFW_window* win, RGFW_mouseButton button) { return RGFW_isMouseDown(button) && RGFW_window_isInFocus(win); }
+RGFW_bool RGFW_window_isMouseReleased(RGFW_window* win, RGFW_mouseButton button) { return RGFW_isMouseReleased(button) && RGFW_window_isInFocus(win); }
+
+
+
+#ifndef RGFW_X11
+void* RGFW_getDisplay_X11(void) { return NULL; }
+u64 RGFW_window_getWindow_X11(RGFW_window* win) { RGFW_UNUSED(win); return 0; }
+#endif
+
+#ifndef RGFW_WAYLAND
+struct wl_display* RGFW_getDisplay_Wayland(void) { return NULL; }
+struct wl_surface* RGFW_window_getWindow_Wayland(RGFW_window* win) { RGFW_UNUSED(win); return NULL; }
+void RGFW_useWayland(RGFW_bool wayland) { RGFW_UNUSED(wayland); }
+RGFW_bool RGFW_usingWayland(void) { return RGFW_FALSE; }
+#endif
+
+#ifndef RGFW_WINDOWS
+void* RGFW_window_getHWND(RGFW_window* win) { RGFW_UNUSED(win); return NULL; }
+void* RGFW_window_getHDC(RGFW_window* win) { RGFW_UNUSED(win); return NULL; }
+#endif
+
+#ifndef RGFW_MACOS
+void* RGFW_window_getView_OSX(RGFW_window* win) { RGFW_UNUSED(win); return NULL; }
+void RGFW_window_setLayer_OSX(RGFW_window* win, void* layer) { RGFW_UNUSED(win); RGFW_UNUSED(layer); }
+void* RGFW_getLayer_OSX(void) { return NULL; }
+void* RGFW_window_getWindow_OSX(RGFW_window* win) { RGFW_UNUSED(win); return NULL; }
+#endif
+
+void RGFW_setBit(u32* var, u32 mask, RGFW_bool set) {
+ if (set) *var |= mask;
+ else *var &= ~mask;
+}
+
+void RGFW_window_center(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_monitor* mon = RGFW_window_getMonitor(win);
+ if (mon == NULL) return;
+
+ RGFW_window_move(win, mon->x + ((i32)(mon->mode.w - win->w) / 2), mon->y + ((mon->mode.h - win->h) / 2));
+}
+
+RGFW_bool RGFW_monitor_scaleToWindow(RGFW_monitor* mon, RGFW_window* win) {
+ RGFW_monitorMode mode;
+ RGFW_ASSERT(win != NULL);
+
+ mode.w = win->w;
+ mode.h = win->h;
+ RGFW_bool ret = RGFW_monitor_requestMode(mon, &mode, RGFW_monitorScale);
+
+ /* move window to monitor origin so it doesn't move to the next monitor */
+ RGFW_window_move(win, mon->x, mon->y);
+
+ return ret;
+}
+
+void RGFW_splitBPP(u32 bpp, RGFW_monitorMode* mode) {
+ if (bpp == 32) bpp = 24;
+ mode->red = mode->green = mode->blue = (u8)(bpp / 3);
+
+ u32 delta = bpp - (mode->red * 3); /* handle leftovers */
+ if (delta >= 1) mode->green = mode->green + 1;
+ if (delta == 2) mode->red = mode->red + 1;
+}
+
+RGFW_bool RGFW_monitorModeCompare(RGFW_monitorMode* mon, RGFW_monitorMode* mon2, RGFW_modeRequest request) {
+ RGFW_ASSERT(mon);
+ RGFW_ASSERT(mon2);
+
+ return (((mon->w == mon2->w && mon->h == mon2->h) || !(request & RGFW_monitorScale)) &&
+ ((mon->refreshRate == mon2->refreshRate) || !(request & RGFW_monitorRefresh)) &&
+ ((mon->red == mon2->red && mon->green == mon2->green && mon->blue == mon2->blue) || !(request & RGFW_monitorRGB)));
+}
+
+RGFW_bool RGFW_window_shouldClose(RGFW_window* win) {
+ return (win == NULL || win->internal.shouldClose || (win->internal.exitKey && RGFW_isKeyDown(win->internal.exitKey)));
+}
+
+void RGFW_window_setShouldClose(RGFW_window* win, RGFW_bool shouldClose) {
+ if (shouldClose) {
+ RGFW_windowCloseCallback(win);
+ } else {
+ win->internal.shouldClose = RGFW_FALSE;
+ }
+}
+
+void RGFW_window_scaleToMonitor(RGFW_window* win) {
+ RGFW_monitor* monitor = RGFW_window_getMonitor(win);
+ if (monitor->scaleX == 0 && monitor->scaleY == 0)
+ return;
+
+ RGFW_window_resize(win, (i32)(monitor->scaleX * (float)win->w), (i32)(monitor->scaleY * (float)win->h));
+}
+
+void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor* m) {
+ RGFW_window_move(win, m->x + win->x, m->y + win->y);
+}
+
+RGFW_surface* RGFW_createSurface(u8* data, i32 w, i32 h, RGFW_format format) {
+ RGFW_surface* surface = (RGFW_surface*)RGFW_ALLOC(sizeof(RGFW_surface));
+ RGFW_MEMZERO(surface, sizeof(RGFW_surface));
+ RGFW_createSurfacePtr(data, w, h, format, surface);
+ return surface;
+}
+
+void RGFW_surface_setConvertFunc(RGFW_surface* surface, RGFW_convertImageDataFunc func) {
+ surface->convertFunc = func;
+}
+
+void RGFW_surface_free(RGFW_surface* surface) {
+ RGFW_surface_freePtr(surface);
+ RGFW_FREE(surface);
+}
+
+RGFW_nativeImage* RGFW_surface_getNativeImage(RGFW_surface* surface) {
+ return &surface->native;
+}
+
+RGFW_surface* RGFW_window_createSurface(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format) {
+ RGFW_surface* surface = (RGFW_surface*)RGFW_ALLOC(sizeof(RGFW_surface));
+ RGFW_MEMZERO(surface, sizeof(RGFW_surface));
+ RGFW_window_createSurfacePtr(win, data, w, h, format, surface);
+ return surface;
+}
+
+#ifndef RGFW_X11
+RGFW_bool RGFW_window_createSurfacePtr(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) {
+ RGFW_UNUSED(win);
+ return RGFW_createSurfacePtr(data, w, h, format, surface);
+}
+#endif
+
+const RGFW_colorLayout RGFW_layouts[RGFW_formatCount] = {
+ { 0, 1, 2, 3, 3 }, /* RGFW_formatRGB8 */
+ { 2, 1, 0, 3, 3 }, /* RGFW_formatBGR8 */
+ { 0, 1, 2, 3, 4 }, /* RGFW_formatRGBA8 */
+ { 1, 2, 3, 0, 4 }, /* RGFW_formatARGB8 */
+ { 2, 1, 0, 3, 4 }, /* RGFW_formatBGRA8 */
+ { 3, 2, 1, 0, 4 }, /* RGFW_formatABGR8 */
+};
+
+
+void RGFW_copyImageData(u8* dest_data, i32 w, i32 h, RGFW_format dest_format, u8* src_data, RGFW_format src_format, RGFW_convertImageDataFunc func) {
+ RGFW_copyImageData64(dest_data, w, h, dest_format, src_data, src_format, RGFW_FALSE, func);
+}
+
+RGFWDEF void RGFW_convertImageData64(u8* dest_data, u8* src_data, const RGFW_colorLayout* srcLayout, const RGFW_colorLayout* destLayout, size_t count, RGFW_bool is64bit);
+void RGFW_convertImageData64(u8* dest_data, u8* src_data, const RGFW_colorLayout* srcLayout, const RGFW_colorLayout* destLayout, size_t count, RGFW_bool is64bit) {
+ u32 i, i2 = 0;
+ u8 rgba[4] = {0};
+
+ for (i = 0; i < count; i++) {
+ const u8* src_px = &src_data[i * srcLayout->channels];
+ u8* dst_px = &dest_data[i2 * destLayout->channels];
+ rgba[0] = src_px[srcLayout->r];
+ rgba[1] = src_px[srcLayout->g];
+ rgba[2] = src_px[srcLayout->b];
+ rgba[3] = (srcLayout->channels == 4) ? src_px[srcLayout->a] : 255;
+
+ dst_px[destLayout->r] = rgba[0];
+ dst_px[destLayout->g] = rgba[1];
+ dst_px[destLayout->b] = rgba[2];
+ if (destLayout->channels == 4)
+ dst_px[destLayout->a] = rgba[3];
+
+ i2 += 1 + is64bit;
+ }
+}
+
+void RGFW_copyImageData64(u8* dest_data, i32 dest_w, i32 dest_h, RGFW_format dest_format, u8* src_data, RGFW_format src_format, RGFW_bool is64bit, RGFW_convertImageDataFunc func) {
+ RGFW_ASSERT(dest_data && src_data);
+
+ u32 count = (u32)(dest_w * dest_h);
+
+ if (src_format == dest_format) {
+ u32 channels = (dest_format >= RGFW_formatRGBA8) ? 4 : 3;
+ RGFW_MEMCPY(dest_data, src_data, count * channels);
+ return;
+ }
+
+ const RGFW_colorLayout* srcLayout = &RGFW_layouts[src_format];
+ const RGFW_colorLayout* destLayout = &RGFW_layouts[dest_format];
+
+ if (is64bit || func == NULL) {
+ RGFW_convertImageData64(dest_data, src_data, srcLayout, destLayout, count, is64bit);
+ } else {
+ func(dest_data, src_data, srcLayout, destLayout, count);
+ }
+}
+
+RGFW_monitorNode* RGFW_monitors_add(const RGFW_monitor* mon) {
+ RGFW_monitorNode* node = NULL;
+
+ #if (RGFW_PREALLOCATED_MONITORS)
+ node = _RGFW->monitors.freeList.head;
+ if (node) {
+ _RGFW->monitors.freeList.head = node->next;
+ if (_RGFW->monitors.freeList.head == NULL) {
+ _RGFW->monitors.freeList.cur = NULL;
+ }
+ } else
+ #elif !defined(RGFW_NO_ALLOCATE_MONITORS)
+ {
+ node = (RGFW_monitorNode*)RGFW_ALLOC(sizeof(RGFW_monitorNode));
+ }
+ #endif
+
+ if (node == NULL) return NULL;
+
+ node->next = NULL;
+
+ if (_RGFW->monitors.list.head == NULL) {
+ _RGFW->monitors.list.head = node;
+ } else {
+ _RGFW->monitors.list.cur->next = node;
+ }
+
+ _RGFW->monitors.list.cur = node;
+
+ if (mon) node->mon = *mon;
+ node->mon.node = node;
+ node->disconnected = RGFW_FALSE;
+
+ _RGFW->monitors.count += 1;
+ return node;
+}
+
+void RGFW_monitors_remove(RGFW_monitorNode* node, RGFW_monitorNode* prev) {
+ _RGFW->monitors.count -= 1;
+
+ /* remove node from the list */
+ if (prev != node) {
+ prev->next = node->next;
+ } else { /* node is the head */
+ _RGFW->monitors.list.head = NULL;
+ }
+
+ node->next = NULL;
+
+ #if (RGFW_PREALLOCATED_MONITORS)
+ /* check if the monitor was allocated in the heap or not */
+ if (node >= _RGFW->monitors.data && node <= &_RGFW->monitors.data[RGFW_PREALLOCATED_MONITORS - 1]) {
+ /* move node to the free list */
+ if (_RGFW->monitors.freeList.head == NULL) {
+ _RGFW->monitors.freeList.head = node;
+ } else {
+ _RGFW->monitors.freeList.cur->next = node;
+ }
+
+ _RGFW->monitors.freeList.cur = node;
+ } else
+ {
+ #elif !defined(RGFW_NO_ALLOCATE_MONITORS)
+ RGFW_FREE(node);
+ #endif
+ }
+}
+
+void RGFW_monitors_refresh(void) {
+ RGFW_monitorNode* prev = _RGFW->monitors.list.head;
+ for (RGFW_monitorNode* node = _RGFW->monitors.list.head; node; node = node->next) {
+ if (node->disconnected == RGFW_FALSE) continue;
+
+ RGFW_monitorCallback(_RGFW->root, &node->mon, RGFW_FALSE);
+ RGFW_monitors_remove(node, prev);
+ prev = node;
+ }
+}
+
+RGFW_monitorMode* RGFW_monitor_getModes(RGFW_monitor* monitor, size_t* count) {
+ size_t num = RGFW_monitor_getModesPtr(monitor, NULL);
+ RGFW_monitorMode* modes = (RGFW_monitorMode*)RGFW_ALLOC(num * sizeof(RGFW_monitorNode));
+ num = RGFW_monitor_getModesPtr(monitor, &modes);
+
+ if (count) *count = num;
+ return modes;
+}
+
+void RGFW_freeModes(RGFW_monitorMode* modes) {
+ RGFW_FREE(modes);
+}
+
+RGFW_bool RGFW_monitor_findClosestMode(RGFW_monitor* monitor, RGFW_monitorMode* mode, RGFW_monitorMode* closest) {
+ size_t count = RGFW_monitor_getModesPtr(monitor, NULL);
+ RGFW_monitorMode* modes = (RGFW_monitorMode*)RGFW_ALLOC(count * sizeof(RGFW_monitorNode));
+ count = RGFW_monitor_getModesPtr(monitor, &modes);
+
+ RGFW_monitorMode* chosen = NULL;
+
+ u32 topScore = 1;
+ for (size_t i = 0; i < count; i++) {
+ RGFW_monitorMode* mode2 = &modes[i];
+
+ u32 score = 0;
+ if (mode->w == mode2->w && mode->h == mode2->h) score += 1000;
+ if (mode->red == mode2->red && mode->green == mode2->green && mode->blue == mode2->blue) score += 100;
+ if (mode->refreshRate == mode->refreshRate) score += 10;
+
+ if (score > topScore) {
+ topScore = score;
+ chosen = mode2;
+ }
+ }
+
+ if (chosen && closest) *closest = *chosen;
+
+
+ RGFW_FREE(modes);
+
+ return (chosen == NULL) ? RGFW_FALSE : RGFW_TRUE;
+}
+
+RGFW_bool RGFW_monitor_getPosition(RGFW_monitor* monitor, i32* x, i32* y) {
+ if (x) *x = monitor->x;
+ if (y) *y = monitor->y;
+ return RGFW_TRUE;
+}
+
+const char* RGFW_monitor_getName(RGFW_monitor* monitor) {
+ return monitor->name;
+}
+
+RGFW_bool RGFW_monitor_getScale(RGFW_monitor* monitor, float* x, float* y) {
+ if (x) *x = monitor->scaleX;
+ if (y) *y = monitor->scaleY;
+ return RGFW_TRUE;
+}
+
+RGFW_bool RGFW_monitor_getPhysicalSize(RGFW_monitor* monitor, float* w, float* h) {
+ if (w) *w = monitor->physW;
+ if (h) *h = monitor->physH;
+ return RGFW_TRUE;
+}
+
+void RGFW_monitor_setUserPtr(RGFW_monitor* monitor, void* userPtr) {
+ monitor->userPtr = userPtr;
+}
+
+void* RGFW_monitor_getUserPtr(RGFW_monitor* monitor) {
+ return monitor->userPtr;
+}
+
+RGFW_bool RGFW_monitor_getMode(RGFW_monitor* monitor, RGFW_monitorMode* mode) {
+ if (mode) *mode = monitor->mode;
+ return RGFW_TRUE;
+}
+
+RGFW_gammaRamp* RGFW_monitor_getGammaRamp(RGFW_monitor* monitor) {
+ RGFW_gammaRamp* ramp = (RGFW_gammaRamp*)RGFW_ALLOC(sizeof(RGFW_gammaRamp));
+ ramp->count = RGFW_monitor_getGammaRampPtr(monitor, NULL);
+ ramp->red = (u16*)RGFW_ALLOC(sizeof(u16) * ramp->count);
+ ramp->green = (u16*)RGFW_ALLOC(sizeof(u16) * ramp->count);
+ ramp->blue = (u16*)RGFW_ALLOC(sizeof(u16) * ramp->count);
+ ramp->count = RGFW_monitor_getGammaRampPtr(monitor, ramp);
+
+ return ramp;
+}
+
+void RGFW_freeGammaRamp(RGFW_gammaRamp* ramp) {
+ RGFW_FREE(ramp->red);
+ RGFW_FREE(ramp->green);
+ RGFW_FREE(ramp->blue);
+ RGFW_FREE(ramp);
+}
+
+RGFW_bool RGFW_monitor_setGammaPtr(RGFW_monitor* monitor, float gamma, u16* ptr, size_t count) {
+ RGFW_ASSERT(monitor);
+ RGFW_ASSERT(gamma > 0.0f);
+
+ size_t i;
+ for (i = 0; i < count; i++) {
+ float value = (float)i / (float) (count - 1);
+ #ifndef RGFW_NO_MATH
+ value = powf(value, 1.f / gamma) * 65535.f + 0.5f;
+ #endif
+ value = RGFW_MIN(value, 65535.f);
+
+ ptr[i] = (u16)value;
+ }
+
+ RGFW_gammaRamp ramp;
+ ramp.red = ptr;
+ ramp.green = ptr;
+ ramp.blue = ptr;
+ ramp.count = count;
+
+ return RGFW_monitor_setGammaRamp(monitor, &ramp);
+}
+
+RGFW_bool RGFW_monitor_setGamma(RGFW_monitor* monitor, float gamma) {
+ size_t count = RGFW_monitor_getGammaRampPtr(monitor, NULL);
+ u16* ptr = (u16*)RGFW_ALLOC(count * sizeof(u16));
+
+ RGFW_bool ret = RGFW_monitor_setGammaPtr(monitor, gamma, ptr, count);
+ RGFW_FREE(ptr);
+
+ return ret;
+}
+
+RGFW_monitor** RGFW_getMonitors(size_t* len) {
+ if (len != NULL) *len = 0;
+
+ size_t count = 0;
+ if (RGFW_getMonitorsPtr(0, NULL, &count) == RGFW_FALSE || count == 0) return NULL;
+
+ RGFW_monitor** monitors = (RGFW_monitor**)RGFW_ALLOC(sizeof(RGFW_monitor*) * count);
+
+ if (RGFW_getMonitorsPtr(count, monitors, &count) == RGFW_FALSE) {
+ RGFW_FREE(monitors);
+ return NULL;
+ }
+
+ if (len != NULL) *len = count;
+
+ return monitors;
+}
+
+RGFW_bool RGFW_getMonitorsPtr(size_t max, RGFW_monitor** monitors, size_t* len) {
+ RGFW_init();
+ if (len != NULL) {
+ *len = _RGFW->monitors.count;
+ }
+
+ if (monitors == NULL || max == 0) return RGFW_TRUE;
+
+
+ if (len != NULL) {
+ *len = max;
+ }
+
+ size_t i = 0;
+ RGFW_monitorNode* cur_node = _RGFW->monitors.list.head;
+ while (cur_node != NULL && i < max) {
+ monitors[i] = &cur_node->mon;
+ i++;
+ cur_node = cur_node->next;
+ }
+
+ return RGFW_TRUE;
+}
+
+RGFW_monitor* RGFW_getPrimaryMonitor(void) {
+ RGFW_init();
+ if (_RGFW->monitors.primary == NULL) {
+ _RGFW->monitors.primary = _RGFW->monitors.list.head;
+ }
+
+ return &_RGFW->monitors.primary->mon;
+}
+
+RGFW_bool RGFW_window_setIcon(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format) {
+ return RGFW_window_setIconEx(win, data, w, h, format, RGFW_iconBoth);
+}
+
+void RGFW_window_captureMouse(RGFW_window* win, RGFW_bool state) {
+ win->internal.captureMouse = state;
+ RGFW_window_captureMousePlatform(win, state);
+}
+
+void RGFW_window_setRawMouseMode(RGFW_window* win, RGFW_bool state) {
+ win->internal.rawMouse = state;
+ RGFW_window_setRawMouseModePlatform(win, state);
+}
+
+void RGFW_window_captureRawMouse(RGFW_window* win, RGFW_bool state) {
+ RGFW_window_captureMouse(win, state);
+ RGFW_window_setRawMouseMode(win, state);
+}
+
+RGFW_bool RGFW_window_isRawMouseMode(RGFW_window* win) { return RGFW_BOOL(win->internal.rawMouse); }
+RGFW_bool RGFW_window_isCaptured(RGFW_window* win) { return RGFW_BOOL(win->internal.captureMouse); }
+
+void RGFW_keyUpdateKeyMod(RGFW_window* win, RGFW_keymod mod, RGFW_bool value) {
+ if (value) win->internal.mod |= mod;
+ else win->internal.mod &= ~mod;
+}
+
+void RGFW_keyUpdateKeyModsEx(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll) {
+ RGFW_keyUpdateKeyMod(win, RGFW_modCapsLock, capital);
+ RGFW_keyUpdateKeyMod(win, RGFW_modNumLock, numlock);
+ RGFW_keyUpdateKeyMod(win, RGFW_modControl, control);
+ RGFW_keyUpdateKeyMod(win, RGFW_modAlt, alt);
+ RGFW_keyUpdateKeyMod(win, RGFW_modShift, shift);
+ RGFW_keyUpdateKeyMod(win, RGFW_modSuper, super);
+ RGFW_keyUpdateKeyMod(win, RGFW_modScrollLock, scroll);
+}
+
+void RGFW_keyUpdateKeyMods(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool scroll) {
+ RGFW_keyUpdateKeyModsEx(win, capital, numlock,
+ RGFW_isKeyDown(RGFW_keyControlL) || RGFW_isKeyDown(RGFW_keyControlR),
+ RGFW_isKeyDown(RGFW_keyAltL) || RGFW_isKeyDown(RGFW_keyAltR),
+ RGFW_isKeyDown(RGFW_keyShiftL) || RGFW_isKeyDown(RGFW_keyShiftR),
+ RGFW_isKeyDown(RGFW_keySuperL) || RGFW_isKeyDown(RGFW_keySuperR),
+ scroll);
+}
+
+void RGFW_window_showMouseFlags(RGFW_window* win, RGFW_bool show) {
+ if (show && (win->internal.flags & RGFW_windowHideMouse))
+ win->internal.flags ^= RGFW_windowHideMouse;
+ else if (!show && !(win->internal.flags & RGFW_windowHideMouse))
+ win->internal.flags |= RGFW_windowHideMouse;
+}
+
+RGFW_bool RGFW_window_isMouseHidden(RGFW_window* win) {
+ return (RGFW_bool)RGFW_BOOL(((RGFW_window*)win)->internal.flags & RGFW_windowHideMouse);
+}
+
+RGFW_bool RGFW_window_borderless(RGFW_window* win) {
+ return (RGFW_bool)RGFW_BOOL(win->internal.flags & RGFW_windowNoBorder);
+}
+
+RGFW_bool RGFW_window_isFullscreen(RGFW_window* win){ return RGFW_BOOL(win->internal.flags & RGFW_windowFullscreen); }
+RGFW_bool RGFW_window_allowsDND(RGFW_window* win) { return RGFW_BOOL(win->internal.flags & RGFW_windowAllowDND); }
+
+RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) {
+ return RGFW_window_setMouseStandard(win, RGFW_mouseNormal);
+}
+
+RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, RGFW_mouseIcon icon) {
+ RGFW_ASSERT(win);
+ RGFW_ASSERT(icon < RGFW_mouseIconCount);
+ return RGFW_window_setMouse(win, _RGFW->standardMice[icon]);
+}
+
+RGFW_bool RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) {
+ RGFW_ASSERT(win && mouse);
+ if (mouse != _RGFW->hiddenMouse) {
+ win->internal.mouse = mouse;
+ }
+
+ return RGFW_window_setMousePlatform(win, mouse);
+}
+
+#ifndef RGFW_WINDOWS
+void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow) {
+ RGFW_setBit(&win->internal.flags, RGFW_windowAllowDND, allow);
+}
+#endif
+
+#if defined(RGFW_WAYLAND) || defined(RGFW_X11) || defined(RGFW_WINDOWS)
+void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) {
+ RGFW_window_showMouseFlags(win, show);
+ if (show == RGFW_FALSE) {
+ RGFW_window_setMouse(win, _RGFW->hiddenMouse);
+ } else {
+ RGFW_window_setMouse(win, win->internal.mouse);
+ }
+}
+#endif
+
+#ifndef RGFW_MACOS
+void RGFW_moveToMacOSResourceDir(void) { }
+#endif
+
+RGFWDEF RGFW_bool RGFW_isLatin(const char *string, size_t length);
+RGFW_bool RGFW_isLatin(const char *string, size_t length) {
+ for (size_t i = 0; i < length; i++) {
+ if ((u8)string[i] >= 0x80) {
+ return RGFW_TRUE;
+ }
+ }
+ return RGFW_FALSE;
+}
+
+RGFWDEF u32 RGFW_decodeUTF8(const char* string, size_t* starting_index);
+u32 RGFW_decodeUTF8(const char* string, size_t* starting_index) {
+ static const u32 offsets[] = {
+ 0x00000000u, 0x00003080u, 0x000e2080u,
+ 0x03c82080u, 0xfa082080u, 0x82082080u
+ };
+
+ u32 codepoint = (u8)string[(*starting_index)];
+ size_t count;
+ for (count = 1; (string[count + (*starting_index)] & 0xc0) == 0x80; count++) {
+ codepoint = (codepoint << 6) + (u8)string[count + (*starting_index)];
+ }
+
+ *starting_index += count;
+
+ RGFW_ASSERT(count <= 6);
+ return codepoint - offsets[count - 1];
+}
+
+/*
+ graphics API specific code (end of generic code)
+ starts here
+*/
+
+
+/*
+ OpenGL defines start here (Normal, EGL, OSMesa)
+*/
+
+#if defined(RGFW_OPENGL)
+/* EGL, OpenGL */
+#define RGFW_DEFAULT_GL_HINTS { \
+ /* Stencil */ 0, \
+ /* Samples */ 0, \
+ /* Stereo */ RGFW_FALSE, \
+ /* AuxBuffers */ 0, \
+ /* DoubleBuffer */ RGFW_TRUE, \
+ /* Red */ 8, \
+ /* Green */ 8, \
+ /* Blue */ 8, \
+ /* Alpha */ 8, \
+ /* Depth */ 24, \
+ /* AccumRed */ 0, \
+ /* AccumGreen */ 0, \
+ /* AccumBlue */ 0, \
+ /* AccumAlpha */ 0, \
+ /* SRGB */ RGFW_FALSE, \
+ /* Robustness */ RGFW_FALSE, \
+ /* Debug */ RGFW_FALSE, \
+ /* NoError */ RGFW_FALSE, \
+ /* ReleaseBehavior */ RGFW_glReleaseNone, \
+ /* Profile */ RGFW_glCore, \
+ /* Major */ 1, \
+ /* Minor */ 0, \
+ /* Share */ NULL, \
+ /* Share_EGL */ NULL, \
+ /* renderer */ RGFW_glAccelerated \
+}
+
+RGFW_glHints RGFW_globalHints_OpenGL_SRC = RGFW_DEFAULT_GL_HINTS;
+RGFW_glHints* RGFW_globalHints_OpenGL = &RGFW_globalHints_OpenGL_SRC;
+
+void RGFW_resetGlobalHints_OpenGL(void) {
+#if !defined(__cplusplus) || defined(RGFW_MACOS)
+ RGFW_globalHints_OpenGL_SRC = (RGFW_glHints)RGFW_DEFAULT_GL_HINTS;
+#else
+ RGFW_globalHints_OpenGL_SRC = RGFW_DEFAULT_GL_HINTS;
+#endif
+}
+void RGFW_setGlobalHints_OpenGL(RGFW_glHints* hints) { RGFW_globalHints_OpenGL = hints; }
+RGFW_glHints* RGFW_getGlobalHints_OpenGL(void) { RGFW_init(); return RGFW_globalHints_OpenGL; }
+
+
+void* RGFW_glContext_getSourceContext(RGFW_glContext* ctx) {
+ RGFW_UNUSED(ctx);
+
+#ifdef RGFW_WAYLAND
+ if (RGFW_usingWayland()) return (void*)ctx->egl.ctx;
+#endif
+
+#if defined(RGFW_X11)
+ return (void*)ctx->ctx;
+#else
+ return NULL;
+#endif
+}
+
+RGFW_glContext* RGFW_window_createContext_OpenGL(RGFW_window* win, RGFW_glHints* hints) {
+ #ifdef RGFW_WAYLAND
+ if (RGFW_usingWayland()) {
+ return (RGFW_glContext*)RGFW_window_createContext_EGL(win, hints);
+ }
+ #endif
+ RGFW_glContext* ctx = (RGFW_glContext*)RGFW_ALLOC(sizeof(RGFW_glContext));
+ if (RGFW_window_createContextPtr_OpenGL(win, ctx, hints) == RGFW_FALSE) {
+ RGFW_FREE(ctx);
+ win->src.ctx.native = NULL;
+ return NULL;
+ }
+ win->src.gfxType |= RGFW_gfxOwnedByRGFW;
+ return ctx;
+}
+
+RGFW_glContext* RGFW_window_getContext_OpenGL(RGFW_window* win) {
+ if (win->src.gfxType & RGFW_windowEGL) return NULL;
+ return win->src.ctx.native;
+}
+
+void RGFW_window_deleteContext_OpenGL(RGFW_window* win, RGFW_glContext* ctx) {
+ RGFW_window_deleteContextPtr_OpenGL(win, ctx);
+ if (win->src.gfxType & RGFW_gfxOwnedByRGFW) RGFW_FREE(ctx);
+}
+
+RGFW_bool RGFW_extensionSupportedStr(const char* extensions, const char* ext, size_t len) {
+ const char *start = extensions;
+ const char *where;
+ const char* terminator;
+
+ if (extensions == NULL || ext == NULL) {
+ return RGFW_FALSE;
+ }
+
+ while (ext[len - 1] == '\0' && len > 3) {
+ len--;
+ }
+
+ where = RGFW_STRSTR(extensions, ext);
+ while (where) {
+ terminator = where + len;
+ if ((where == start || *(where - 1) == ' ') &&
+ (*terminator == ' ' || *terminator == '\0')) {
+ return RGFW_TRUE;
+ }
+ where = RGFW_STRSTR(terminator, ext);
+ }
+
+ return RGFW_FALSE;
+}
+
+RGFWDEF RGFW_bool RGFW_extensionSupported_base(const char* extension, size_t len, RGFW_proc (*getProcAddress)(const char* procname));
+RGFW_bool RGFW_extensionSupported_base(const char* extension, size_t len, RGFW_proc (*getProcAddress)(const char* procname)) {
+ #ifdef GL_NUM_EXTENSIONS
+ if (RGFW_globalHints_OpenGL->major >= 3) {
+ i32 i;
+
+ GLint count = 0;
+
+ RGFW_proc RGFW_glGetStringi = getProcAddress("glGetStringi");
+ RGFW_proc RGFW_glGetIntegerv = getProcAddress("glGetIntegerv");
+ if (RGFW_glGetIntegerv)
+ ((void(*)(GLenum, GLint*))RGFW_glGetIntegerv)(GL_NUM_EXTENSIONS, &count);
+
+ for (i = 0; RGFW_glGetStringi && i < count; i++) {
+ const char* en = ((const char* (*)(u32, u32))RGFW_glGetStringi)(GL_EXTENSIONS, (u32)i);
+ if (en && RGFW_STRNCMP(en, extension, len) == 0) {
+ return RGFW_TRUE;
+ }
+ }
+ } else
+#endif
+ {
+ RGFW_proc RGFW_glGetString = getProcAddress("glGetString");
+ #define RGFW_GL_EXTENSIONS 0x1F03
+ if (RGFW_glGetString) {
+ const char* extensions = ((const char*(*)(u32))RGFW_glGetString)(RGFW_GL_EXTENSIONS);
+
+ if ((extensions != NULL) && RGFW_extensionSupportedStr(extensions, extension, len)) {
+ return RGFW_TRUE;
+ }
+ }
+ }
+ return RGFW_FALSE;
+}
+
+RGFW_bool RGFW_extensionSupported_OpenGL(const char* extension, size_t len) {
+ if (RGFW_extensionSupported_base(extension, len, RGFW_getProcAddress_OpenGL)) return RGFW_TRUE;
+ return RGFW_extensionSupportedPlatform_OpenGL(extension, len);
+}
+
+void RGFW_window_makeCurrentWindow_OpenGL(RGFW_window* win) {
+ if (win) {
+ _RGFW->current = win;
+ }
+
+ RGFW_window_makeCurrentContext_OpenGL(win);
+}
+
+RGFW_window* RGFW_getCurrentWindow_OpenGL(void) { return _RGFW->current; }
+void RGFW_attribStack_init(RGFW_attribStack* stack, i32* attribs, size_t max) { stack->attribs = attribs; stack->count = 0; stack->max = max; }
+void RGFW_attribStack_pushAttrib(RGFW_attribStack* stack, i32 attrib) {
+ RGFW_ASSERT(stack->count < stack->max);
+ stack->attribs[stack->count] = attrib;
+ stack->count += 1;
+}
+void RGFW_attribStack_pushAttribs(RGFW_attribStack* stack, i32 attrib1, i32 attrib2) {
+ RGFW_attribStack_pushAttrib(stack, attrib1);
+ RGFW_attribStack_pushAttrib(stack, attrib2);
+}
+
+/* EGL */
+#ifdef RGFW_EGL
+#include <EGL/egl.h>
+
+PFNEGLINITIALIZEPROC RGFW_eglInitialize;
+PFNEGLGETCONFIGSPROC RGFW_eglGetConfigs;
+PFNEGLCHOOSECONFIGPROC RGFW_eglChooseConfig;
+PFNEGLCREATEWINDOWSURFACEPROC RGFW_eglCreateWindowSurface;
+PFNEGLCREATECONTEXTPROC RGFW_eglCreateContext;
+PFNEGLMAKECURRENTPROC RGFW_eglMakeCurrent;
+PFNEGLGETDISPLAYPROC RGFW_eglGetDisplay;
+PFNEGLSWAPBUFFERSPROC RGFW_eglSwapBuffers;
+PFNEGLSWAPINTERVALPROC RGFW_eglSwapInterval;
+PFNEGLBINDAPIPROC RGFW_eglBindAPI;
+PFNEGLDESTROYCONTEXTPROC RGFW_eglDestroyContext;
+PFNEGLTERMINATEPROC RGFW_eglTerminate;
+PFNEGLDESTROYSURFACEPROC RGFW_eglDestroySurface;
+PFNEGLGETCURRENTCONTEXTPROC RGFW_eglGetCurrentContext;
+PFNEGLGETPROCADDRESSPROC RGFW_eglGetProcAddress = NULL;
+PFNEGLQUERYSTRINGPROC RGFW_eglQueryString;
+PFNEGLGETCONFIGATTRIBPROC RGFW_eglGetConfigAttrib;
+
+#define EGL_SURFACE_MAJOR_VERSION_KHR 0x3098
+#define EGL_SURFACE_MINOR_VERSION_KHR 0x30fb
+
+#ifdef RGFW_WINDOWS
+ #include <windows.h>
+#elif defined(RGFW_MACOS) || defined(RGFW_UNIX)
+ #include <dlfcn.h>
+#endif
+
+#ifdef RGFW_WAYLAND
+#include <wayland-egl.h>
+#endif
+
+void* RGFW_eglLibHandle = NULL;
+
+void* RGFW_getDisplay_EGL(void) { return _RGFW->EGL_display; }
+void* RGFW_eglContext_getSourceContext(RGFW_eglContext* ctx) { return ctx->ctx; }
+void* RGFW_eglContext_getSurface(RGFW_eglContext* ctx) { return ctx->surface; }
+struct wl_egl_window* RGFW_eglContext_wlEGLWindow(RGFW_eglContext* ctx) { return ctx->eglWindow; }
+
+RGFW_bool RGFW_loadEGL(void) {
+ RGFW_init();
+ if (RGFW_eglGetProcAddress != NULL) {
+ return RGFW_TRUE;
+ }
+
+#ifndef RGFW_WASM
+ #ifdef RGFW_WINDOWS
+ const char* libNames[] = { "libEGL.dll", "EGL.dll" };
+ #elif defined(RGFW_MACOS) || defined(RGFW_UNIX)
+ /* unix (including macOS) */
+ const char* libNames[] = {
+ "libEGL.so.1", /* most common */
+ "libEGL.so", /* fallback */
+ "/System/Library/Frameworks/OpenGL.framework/OpenGL" /* fallback for older macOS EGL-like systems */
+ };
+ #endif
+
+ for (size_t i = 0; i < sizeof(libNames) / sizeof(libNames[0]); i++) {
+ #ifdef RGFW_WINDOWS
+ RGFW_eglLibHandle = (void*)LoadLibraryA(libNames[i]);
+ if (RGFW_eglLibHandle) {
+ RGFW_eglGetProcAddress = (PFNEGLGETPROCADDRESSPROC)(RGFW_proc)GetProcAddress((HMODULE)RGFW_eglLibHandle, "eglGetProcAddress");
+ break;
+ }
+ #elif defined(RGFW_MACOS) || defined(RGFW_UNIX)
+ RGFW_eglLibHandle = dlopen(libNames[i], RTLD_LAZY | RTLD_GLOBAL);
+ if (RGFW_eglLibHandle) {
+ void* lib = dlsym(RGFW_eglLibHandle, "eglGetProcAddress");
+ if (lib != NULL) RGFW_MEMCPY(&RGFW_eglGetProcAddress, &lib, sizeof(PFNEGLGETPROCADDRESSPROC));
+ break;
+ }
+ #endif
+ }
+
+ if (!RGFW_eglLibHandle || !RGFW_eglGetProcAddress) {
+ return RGFW_FALSE;
+ }
+
+ RGFW_eglInitialize = (PFNEGLINITIALIZEPROC) RGFW_eglGetProcAddress("eglInitialize");
+ RGFW_eglGetConfigs = (PFNEGLGETCONFIGSPROC) RGFW_eglGetProcAddress("eglGetConfigs");
+ RGFW_eglChooseConfig = (PFNEGLCHOOSECONFIGPROC) RGFW_eglGetProcAddress("eglChooseConfig");
+ RGFW_eglCreateWindowSurface = (PFNEGLCREATEWINDOWSURFACEPROC) RGFW_eglGetProcAddress("eglCreateWindowSurface");
+ RGFW_eglCreateContext = (PFNEGLCREATECONTEXTPROC) RGFW_eglGetProcAddress("eglCreateContext");
+ RGFW_eglMakeCurrent = (PFNEGLMAKECURRENTPROC) RGFW_eglGetProcAddress("eglMakeCurrent");
+ RGFW_eglGetDisplay = (PFNEGLGETDISPLAYPROC) RGFW_eglGetProcAddress("eglGetDisplay");
+ RGFW_eglSwapBuffers = (PFNEGLSWAPBUFFERSPROC) RGFW_eglGetProcAddress("eglSwapBuffers");
+ RGFW_eglSwapInterval = (PFNEGLSWAPINTERVALPROC) RGFW_eglGetProcAddress("eglSwapInterval");
+ RGFW_eglBindAPI = (PFNEGLBINDAPIPROC) RGFW_eglGetProcAddress("eglBindAPI");
+ RGFW_eglDestroyContext = (PFNEGLDESTROYCONTEXTPROC) RGFW_eglGetProcAddress("eglDestroyContext");
+ RGFW_eglTerminate = (PFNEGLTERMINATEPROC) RGFW_eglGetProcAddress("eglTerminate");
+ RGFW_eglDestroySurface = (PFNEGLDESTROYSURFACEPROC) RGFW_eglGetProcAddress("eglDestroySurface");
+ RGFW_eglQueryString = (PFNEGLQUERYSTRINGPROC) RGFW_eglGetProcAddress("eglQueryString");
+ RGFW_eglGetCurrentContext = (PFNEGLGETCURRENTCONTEXTPROC) RGFW_eglGetProcAddress("eglGetCurrentContext");
+ RGFW_eglGetConfigAttrib = (PFNEGLGETCONFIGATTRIBPROC) RGFW_eglGetProcAddress("eglGetConfigAttrib");
+
+#else
+ RGFW_eglGetProcAddress = eglGetProcAddress;
+ RGFW_eglInitialize = (PFNEGLINITIALIZEPROC) eglInitialize;
+ RGFW_eglGetConfigs = (PFNEGLGETCONFIGSPROC) eglGetConfigs;
+ RGFW_eglChooseConfig = (PFNEGLCHOOSECONFIGPROC) eglChooseConfig;
+ RGFW_eglCreateWindowSurface = (PFNEGLCREATEWINDOWSURFACEPROC) eglCreateWindowSurface;
+ RGFW_eglCreateContext = (PFNEGLCREATECONTEXTPROC) eglCreateContext;
+ RGFW_eglMakeCurrent = (PFNEGLMAKECURRENTPROC) eglMakeCurrent;
+ RGFW_eglGetDisplay = (PFNEGLGETDISPLAYPROC) eglGetDisplay;
+ RGFW_eglSwapBuffers = (PFNEGLSWAPBUFFERSPROC) eglSwapBuffers;
+ RGFW_eglSwapInterval = (PFNEGLSWAPINTERVALPROC) eglSwapInterval;
+ RGFW_eglBindAPI = (PFNEGLBINDAPIPROC) eglBindAPI;
+ RGFW_eglDestroyContext = (PFNEGLDESTROYCONTEXTPROC) eglDestroyContext;
+ RGFW_eglTerminate = (PFNEGLTERMINATEPROC) eglTerminate;
+ RGFW_eglDestroySurface = (PFNEGLDESTROYSURFACEPROC) eglDestroySurface;
+ RGFW_eglQueryString = (PFNEGLQUERYSTRINGPROC) eglQueryString;
+ RGFW_eglGetCurrentContext = (PFNEGLGETCURRENTCONTEXTPROC) eglGetCurrentContext;
+ RGFW_eglGetConfigAttrib = (PFNEGLGETCONFIGATTRIBPROC)eglGetConfigAttrib;
+#endif
+
+ RGFW_bool out = RGFW_BOOL(RGFW_eglInitialize!= NULL &&
+ RGFW_eglGetConfigs!= NULL &&
+ RGFW_eglChooseConfig!= NULL &&
+ RGFW_eglCreateWindowSurface!= NULL &&
+ RGFW_eglCreateContext!= NULL &&
+ RGFW_eglMakeCurrent!= NULL &&
+ RGFW_eglGetDisplay!= NULL &&
+ RGFW_eglSwapBuffers!= NULL &&
+ RGFW_eglSwapInterval != NULL &&
+ RGFW_eglBindAPI!= NULL &&
+ RGFW_eglDestroyContext!= NULL &&
+ RGFW_eglTerminate!= NULL &&
+ RGFW_eglDestroySurface!= NULL &&
+ RGFW_eglQueryString != NULL &&
+ RGFW_eglGetCurrentContext != NULL &&
+ RGFW_eglGetConfigAttrib != NULL);
+
+ if (out) {
+ #ifdef RGFW_WINDOWS
+ HDC dc = GetDC(NULL);
+ _RGFW->EGL_display = RGFW_eglGetDisplay((EGLNativeDisplayType) dc);
+ ReleaseDC(NULL, dc);
+ #elif defined(RGFW_WAYLAND)
+ if (RGFW_usingWayland() == RGFW_TRUE)
+ _RGFW->EGL_display = RGFW_eglGetDisplay((EGLNativeDisplayType) _RGFW->wl_display);
+ else
+ #endif
+ #ifdef RGFW_X11
+ _RGFW->EGL_display = RGFW_eglGetDisplay((EGLNativeDisplayType) _RGFW->display);
+ #else
+ {}
+ #endif
+ #if !defined(RGFW_WAYLAND) && !defined(RGFW_WINDOWS) && !defined(RGFW_X11)
+ _RGFW->EGL_display = RGFW_eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ #endif
+ }
+
+ RGFW_eglInitialize(_RGFW->EGL_display, NULL, NULL);
+ return out;
+}
+
+
+void RGFW_unloadEGL(void) {
+ if (!RGFW_eglLibHandle) return;
+ RGFW_eglTerminate(_RGFW->EGL_display);
+ #ifdef RGFW_WINDOWS
+ FreeLibrary((HMODULE)RGFW_eglLibHandle);
+ #elif defined(RGFW_MACOS) || defined(RGFW_UNIX)
+ dlclose(RGFW_eglLibHandle);
+ #endif
+
+ RGFW_eglLibHandle = NULL;
+ RGFW_eglGetProcAddress = NULL;
+}
+
+RGFW_bool RGFW_window_createContextPtr_EGL(RGFW_window* win, RGFW_eglContext* ctx, RGFW_glHints* hints) {
+ if (RGFW_loadEGL() == RGFW_FALSE) return RGFW_FALSE;
+ win->src.ctx.egl = ctx;
+ win->src.gfxType = RGFW_gfxEGL;
+
+#ifdef RGFW_WAYLAND
+ if (RGFW_usingWayland() == RGFW_TRUE)
+ win->src.ctx.egl->eglWindow = wl_egl_window_create(win->src.surface, win->w, win->h);
+#endif
+
+ #ifndef EGL_OPENGL_ES1_BIT
+ #define EGL_OPENGL_ES1_BIT 0x1
+ #endif
+
+ EGLint egl_config[24];
+
+ {
+ RGFW_attribStack stack;
+ RGFW_attribStack_init(&stack, egl_config, 24);
+
+ RGFW_attribStack_pushAttribs(&stack, EGL_SURFACE_TYPE, EGL_WINDOW_BIT);
+ RGFW_attribStack_pushAttrib(&stack, EGL_RENDERABLE_TYPE);
+
+ if (hints->profile == RGFW_glES) {
+ switch (hints->major) {
+ case 1: RGFW_attribStack_pushAttrib(&stack, EGL_OPENGL_ES1_BIT); break;
+ case 2: RGFW_attribStack_pushAttrib(&stack, EGL_OPENGL_ES2_BIT); break;
+ case 3: RGFW_attribStack_pushAttrib(&stack, EGL_OPENGL_ES3_BIT); break;
+ default: break;
+ }
+ } else {
+ RGFW_attribStack_pushAttrib(&stack, EGL_OPENGL_BIT);
+ }
+
+ RGFW_attribStack_pushAttribs(&stack, EGL_RED_SIZE, hints->red);
+ RGFW_attribStack_pushAttribs(&stack, EGL_GREEN_SIZE, hints->green);
+ RGFW_attribStack_pushAttribs(&stack, EGL_BLUE_SIZE, hints->blue);
+ RGFW_attribStack_pushAttribs(&stack, EGL_ALPHA_SIZE, hints->alpha);
+ RGFW_attribStack_pushAttribs(&stack, EGL_DEPTH_SIZE, hints->depth);
+
+ RGFW_attribStack_pushAttribs(&stack, EGL_STENCIL_SIZE, hints->stencil);
+ if (hints->samples) {
+ RGFW_attribStack_pushAttribs(&stack, EGL_SAMPLE_BUFFERS, 1);
+ RGFW_attribStack_pushAttribs(&stack, EGL_SAMPLES, hints->samples);
+ }
+
+ RGFW_attribStack_pushAttribs(&stack, EGL_NONE, EGL_NONE);
+ }
+
+ EGLint numConfigs, best_config = -1, best_samples = 0;
+
+ RGFW_eglChooseConfig(_RGFW->EGL_display, egl_config, NULL, 0, &numConfigs);
+ EGLConfig* configs = (EGLConfig*)RGFW_ALLOC(sizeof(EGLConfig) * (u32)numConfigs);
+
+ RGFW_eglChooseConfig(_RGFW->EGL_display, egl_config, configs, numConfigs, &numConfigs);
+
+#ifdef RGFW_X11
+ RGFW_bool transparent = (win->internal.flags & RGFW_windowTransparent);
+ EGLint best_depth = 0;
+#endif
+
+ for (EGLint i = 0; i < numConfigs; i++) {
+ EGLint visual_id = 0;
+ EGLint samples = 0;
+
+ RGFW_eglGetConfigAttrib(_RGFW->EGL_display, configs[i], EGL_NATIVE_VISUAL_ID, &visual_id);
+ RGFW_eglGetConfigAttrib(_RGFW->EGL_display, configs[i], EGL_SAMPLES, &samples);
+
+ if (best_config == -1) best_config = i;
+
+#ifdef RGFW_X11
+ if (RGFW_usingWayland() == RGFW_FALSE) {
+ XVisualInfo vinfo_template;
+ vinfo_template.visualid = (VisualID)visual_id;
+
+ int num_visuals = 0;
+ XVisualInfo* vi = XGetVisualInfo(_RGFW->display, VisualIDMask, &vinfo_template, &num_visuals);
+ if (!vi) continue;
+ if ((!transparent || vi->depth == 32) && best_depth == 0) {
+ best_config = i;
+ best_depth = vi->depth;
+ }
+
+ if ((!(transparent) || vi->depth == 32) && (samples <= hints->samples && samples > best_samples)) {
+ best_depth = vi->depth;
+ best_config = i;
+ best_samples = samples;
+ XFree(vi);
+ continue;
+ }
+ }
+#endif
+
+ if (samples <= hints->samples && samples > best_samples) {
+ best_config = i;
+ best_samples = samples;
+ }
+ }
+
+ EGLConfig config = configs[best_config];
+ RGFW_FREE(configs);
+#ifdef RGFW_X11
+ if (RGFW_usingWayland() == RGFW_FALSE) {
+ /* This is required so that way the user can create their own OpenGL context after RGFW_createWindow is used */
+ XVisualInfo* result;
+ XVisualInfo desired;
+ EGLint visualID = 0, count = 0;
+
+ RGFW_eglGetConfigAttrib(_RGFW->EGL_display, config, EGL_NATIVE_VISUAL_ID, &visualID);
+ if (visualID) {
+ desired.visualid = (VisualID)visualID;
+ result = XGetVisualInfo(_RGFW->display, VisualIDMask, &desired, &count);
+ } else RGFW_debugCallback(RGFW_typeError, RGFW_errEGLContext, "Failed to fetch a valid EGL VisualID");
+
+ if (result == NULL || count == 0) {
+ if (win->src.window == 0) {
+ /* try to create a EGL context anyway (this will work if you're not using a NVidia driver) */
+ win->internal.flags &= ~(u32)RGFW_windowEGL;
+ RGFW_createWindowPlatform("", win->internal.flags, win);
+ }
+ RGFW_debugCallback(RGFW_typeError, RGFW_errEGLContext, "Failed to find a valid visual for the EGL config");
+ } else {
+ RGFW_bool showWindow = RGFW_FALSE;
+ if (win->src.window) {
+ showWindow = (RGFW_window_isMinimized(win) == RGFW_FALSE);
+ RGFW_window_closePlatform(win);
+ }
+
+ RGFW_XCreateWindow(*result, "", win->internal.flags, win);
+
+ if (showWindow) {
+ RGFW_window_show(win);
+ }
+ XFree(result);
+ }
+ }
+#endif
+
+ EGLint surf_attribs[9];
+
+ {
+ RGFW_attribStack stack;
+ RGFW_attribStack_init(&stack, surf_attribs, 9);
+
+ const char present_opaque_str[] = "EGL_EXT_present_opaque";
+ RGFW_bool opaque_extension_Found = RGFW_extensionSupportedPlatform_EGL(present_opaque_str, sizeof(present_opaque_str));
+
+ #ifndef EGL_PRESENT_OPAQUE_EXT
+ #define EGL_PRESENT_OPAQUE_EXT 0x31df
+ #endif
+
+ #ifndef EGL_GL_COLORSPACE_KHR
+ #define EGL_GL_COLORSPACE_KHR 0x309D
+ #ifndef EGL_GL_COLORSPACE_SRGB_KHR
+ #define EGL_GL_COLORSPACE_SRGB_KHR 0x3089
+ #endif
+ #endif
+
+ const char gl_colorspace_str[] = "EGL_KHR_gl_colorspace";
+ RGFW_bool gl_colorspace_Found = RGFW_extensionSupportedPlatform_EGL(gl_colorspace_str, sizeof(gl_colorspace_str));
+
+ if (hints->sRGB && gl_colorspace_Found) {
+ RGFW_attribStack_pushAttribs(&stack, EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR);
+ }
+
+ if (!(win->internal.flags & RGFW_windowTransparent) && opaque_extension_Found)
+ RGFW_attribStack_pushAttribs(&stack, EGL_PRESENT_OPAQUE_EXT, EGL_TRUE);
+
+ if (hints->doubleBuffer == 0) {
+ RGFW_attribStack_pushAttribs(&stack, EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER);
+ }
+
+ RGFW_attribStack_pushAttribs(&stack, EGL_NONE, EGL_NONE);
+ }
+ #if defined(RGFW_MACOS)
+ void* layer = RGFW_getLayer_OSX();
+
+ RGFW_window_setLayer_OSX(win, layer);
+
+ win->src.ctx.egl->surface = RGFW_eglCreateWindowSurface(_RGFW->EGL_display, config, (EGLNativeWindowType) layer, surf_attribs);
+ #elif defined(RGFW_WINDOWS)
+ win->src.ctx.egl->surface = RGFW_eglCreateWindowSurface(_RGFW->EGL_display, config, (EGLNativeWindowType) win->src.window, surf_attribs);
+ #elif defined(RGFW_WAYLAND)
+ if (RGFW_usingWayland() == RGFW_TRUE)
+ win->src.ctx.egl->surface = RGFW_eglCreateWindowSurface(_RGFW->EGL_display, config, (EGLNativeWindowType) win->src.ctx.egl->eglWindow, surf_attribs);
+ else
+ #endif
+ #ifdef RGFW_X11
+ win->src.ctx.egl->surface = RGFW_eglCreateWindowSurface(_RGFW->EGL_display, config, (EGLNativeWindowType) win->src.window, surf_attribs);
+ #else
+ {}
+ #endif
+ #ifdef RGFW_WASM
+ win->src.ctx.egl->surface = eglCreateWindowSurface(_RGFW->EGL_display, config, 0, 0);
+ #endif
+
+ if (win->src.ctx.egl->surface == NULL) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errEGLContext, "Failed to create an EGL surface.");
+ return RGFW_FALSE;
+ }
+
+ EGLint attribs[20];
+ {
+ RGFW_attribStack stack;
+ RGFW_attribStack_init(&stack, attribs, 20);
+
+ if (hints->major || hints->minor) {
+ RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_MAJOR_VERSION, hints->major);
+ RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_MINOR_VERSION, hints->minor);
+ }
+
+ if (hints->profile == RGFW_glCore) {
+ RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT);
+ } else if (hints->profile == RGFW_glCompatibility) {
+ RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT);
+ } else if (hints->profile == RGFW_glForwardCompatibility) {
+ RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE, EGL_TRUE);
+ }
+
+
+ RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_OPENGL_ROBUST_ACCESS, hints->robustness);
+ RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_OPENGL_DEBUG, hints->debug);
+
+ #ifndef EGL_CONTEXT_RELEASE_BEHAVIOR_KHR
+ #define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097
+ #endif
+
+ #ifndef EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR
+ #define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098
+ #endif
+
+ if (hints->releaseBehavior == RGFW_glReleaseFlush) {
+ RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR);
+ } else {
+ RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, 0x0000);
+ }
+
+ RGFW_attribStack_pushAttribs(&stack, EGL_NONE, EGL_NONE);
+ }
+
+ if (hints->profile == RGFW_glES)
+ RGFW_eglBindAPI(EGL_OPENGL_ES_API);
+ else
+ RGFW_eglBindAPI(EGL_OPENGL_API);
+
+ win->src.ctx.egl->ctx = RGFW_eglCreateContext(_RGFW->EGL_display, config, hints->shareEGL, attribs);
+
+ if (win->src.ctx.egl->ctx == NULL) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errEGLContext, "Failed to create an EGL context.");
+ return RGFW_FALSE;
+ }
+
+ RGFW_eglMakeCurrent(_RGFW->EGL_display, win->src.ctx.egl->surface, win->src.ctx.egl->surface, win->src.ctx.egl->ctx);
+ RGFW_eglSwapBuffers(_RGFW->EGL_display, win->src.ctx.egl->surface);
+ RGFW_debugCallback(RGFW_typeInfo, RGFW_infoOpenGL, "EGL context initalized.");
+ return RGFW_TRUE;
+}
+
+RGFW_eglContext* RGFW_window_getContext_EGL(RGFW_window* win) {
+ if (win->src.gfxType == RGFW_windowOpenGL) return NULL;
+ return win->src.ctx.egl;
+}
+
+void RGFW_window_deleteContextPtr_EGL(RGFW_window* win, RGFW_eglContext* ctx) {
+ if (_RGFW->EGL_display == NULL) return;
+
+ RGFW_eglDestroySurface(_RGFW->EGL_display, ctx->surface);
+ RGFW_eglDestroyContext(_RGFW->EGL_display, ctx->ctx);
+ RGFW_debugCallback(RGFW_typeInfo, RGFW_infoOpenGL, "EGL context freed");
+ #ifdef RGFW_WAYLAND
+ if (RGFW_usingWayland() == RGFW_FALSE) return;
+ wl_egl_window_destroy(win->src.ctx.egl->eglWindow);
+ RGFW_debugCallback(RGFW_typeInfo, RGFW_infoOpenGL, "EGL window context freed");
+ #endif
+ win->src.ctx.egl = NULL;
+}
+
+void RGFW_window_makeCurrentContext_EGL(RGFW_window* win) { if (win) RGFW_ASSERT(win->src.ctx.egl);
+ if (win == NULL)
+ RGFW_eglMakeCurrent(_RGFW->EGL_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ else {
+ RGFW_eglMakeCurrent(_RGFW->EGL_display, win->src.ctx.egl->surface, win->src.ctx.egl->surface, win->src.ctx.egl->ctx);
+ }
+}
+
+void RGFW_window_swapBuffers_EGL(RGFW_window* win) {
+ if (RGFW_eglSwapBuffers)
+ RGFW_eglSwapBuffers(_RGFW->EGL_display, win->src.ctx.egl->surface);
+ else RGFW_window_swapBuffers_OpenGL(win);
+}
+
+void* RGFW_getCurrentContext_EGL(void) {
+ return RGFW_eglGetCurrentContext();
+}
+
+RGFW_proc RGFW_getProcAddress_EGL(const char* procname) {
+ #if defined(RGFW_WINDOWS)
+ RGFW_proc proc = (RGFW_proc) GetProcAddress(RGFW_wgl_dll, procname);
+
+ if (proc)
+ return proc;
+ #endif
+
+ return (RGFW_proc) RGFW_eglGetProcAddress(procname);
+}
+
+RGFW_bool RGFW_extensionSupportedPlatform_EGL(const char* extension, size_t len) {
+ if (RGFW_loadEGL() == RGFW_FALSE) return RGFW_FALSE;
+ const char* extensions = RGFW_eglQueryString(_RGFW->EGL_display, EGL_EXTENSIONS);
+ return extensions != NULL && RGFW_extensionSupportedStr(extensions, extension, len);
+}
+
+void RGFW_window_swapInterval_EGL(RGFW_window* win, i32 swapInterval) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_eglSwapInterval(_RGFW->EGL_display, swapInterval);
+}
+
+RGFW_bool RGFW_extensionSupported_EGL(const char* extension, size_t len) {
+ if (RGFW_extensionSupported_base(extension, len, RGFW_getProcAddress_EGL)) return RGFW_TRUE;
+ return RGFW_extensionSupportedPlatform_EGL(extension, len);
+}
+
+void RGFW_window_makeCurrentWindow_EGL(RGFW_window* win) {
+ _RGFW->current = win;
+ RGFW_window_makeCurrentContext_EGL(win);
+}
+
+RGFW_window* RGFW_getCurrentWindow_EGL(void) { return _RGFW->current; }
+
+RGFW_eglContext* RGFW_window_createContext_EGL(RGFW_window* win, RGFW_glHints* hints) {
+ RGFW_eglContext* ctx = (RGFW_eglContext*)RGFW_ALLOC(sizeof(RGFW_eglContext));
+ if (RGFW_window_createContextPtr_EGL(win, ctx, hints) == RGFW_FALSE) {
+ RGFW_FREE(ctx);
+ win->src.ctx.egl = NULL;
+ return NULL;
+ }
+ win->src.gfxType |= RGFW_gfxOwnedByRGFW;
+ return ctx;
+}
+
+void RGFW_window_deleteContext_EGL(RGFW_window* win, RGFW_eglContext* ctx) {
+ RGFW_window_deleteContextPtr_EGL(win, ctx);
+ if (win->src.gfxType & RGFW_gfxOwnedByRGFW) RGFW_FREE(ctx);
+}
+
+#endif /* RGFW_EGL */
+
+/*
+ end of RGFW_EGL defines
+*/
+#endif /* end of RGFW_GL (OpenGL, EGL, OSMesa )*/
+
+/*
+ RGFW_VULKAN defines
+*/
+#ifdef RGFW_VULKAN
+#ifdef RGFW_MACOS
+#include <objc/message.h>
+#endif
+
+const char** RGFW_getRequiredInstanceExtensions_Vulkan(size_t* count) {
+ static const char* arr[2] = {VK_KHR_SURFACE_EXTENSION_NAME};
+ arr[1] = RGFW_VK_SURFACE;
+ if (count != NULL) *count = 2;
+
+ return (const char**)arr;
+}
+
+#ifndef RGFW_MACOS
+VkResult RGFW_window_createSurface_Vulkan(RGFW_window* win, VkInstance instance, VkSurfaceKHR* surface) {
+ RGFW_ASSERT(win != NULL); RGFW_ASSERT(instance);
+ RGFW_ASSERT(surface != NULL);
+
+ *surface = VK_NULL_HANDLE;
+
+#ifdef RGFW_X11
+
+ VkXlibSurfaceCreateInfoKHR x11 = { VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, 0, 0, (Display*) _RGFW->display, (Window) win->src.window };
+ return vkCreateXlibSurfaceKHR(instance, &x11, NULL, surface);
+#endif
+#if defined(RGFW_WAYLAND)
+
+ VkWaylandSurfaceCreateInfoKHR wayland = { VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, 0, 0, (struct wl_display*) _RGFW->wl_display, (struct wl_surface*) win->src.surface };
+ return vkCreateWaylandSurfaceKHR(instance, &wayland, NULL, surface);
+#elif defined(RGFW_WINDOWS)
+ VkWin32SurfaceCreateInfoKHR win32 = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, 0, 0, _RGFW->instance, (HWND)win->src.window };
+
+ return vkCreateWin32SurfaceKHR(instance, &win32, NULL, surface);
+#endif
+}
+#endif
+
+RGFW_bool RGFW_getPresentationSupport_Vulkan(VkPhysicalDevice physicalDevice, u32 queueFamilyIndex) {
+ if (_RGFW == NULL) RGFW_init();
+#ifdef RGFW_X11
+
+ Visual* visual = DefaultVisual(_RGFW->display, DefaultScreen(_RGFW->display));
+ RGFW_bool out = vkGetPhysicalDeviceXlibPresentationSupportKHR(physicalDevice, queueFamilyIndex, _RGFW->display, XVisualIDFromVisual(visual));
+ return out;
+#endif
+#if defined(RGFW_WAYLAND)
+
+ RGFW_bool wlout = vkGetPhysicalDeviceWaylandPresentationSupportKHR(physicalDevice, queueFamilyIndex, _RGFW->wl_display);
+ return wlout;
+#elif defined(RGFW_WINDOWS)
+ RGFW_bool out = vkGetPhysicalDeviceWin32PresentationSupportKHR(physicalDevice, queueFamilyIndex);
+ return out;
+#elif defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11)
+ RGFW_UNUSED(physicalDevice);
+ RGFW_UNUSED(queueFamilyIndex);
+ return RGFW_FALSE; /* TODO */
+#endif
+}
+#endif /* end of RGFW_vulkan */
+
+/*
+This is where OS specific stuff starts
+*/
+
+/* start of unix (wayland or X11 (unix) ) defines */
+
+#ifdef RGFW_UNIX
+
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+#include <time.h>
+
+void RGFW_stopCheckEvents(void) {
+
+ _RGFW->eventWait_forceStop[2] = 1;
+ while (1) {
+ const char byte = 0;
+ const ssize_t result = write(_RGFW->eventWait_forceStop[1], &byte, 1);
+ if (result == 1 || result == -1)
+ break;
+ }
+}
+
+RGFWDEF u64 RGFW_unix_getTimeNS(void);
+u64 RGFW_unix_getTimeNS(void) {
+ struct timespec ts;
+ static i32 clock = -1;
+ if (clock == -1) {
+ #if defined(_POSIX_MONOTONIC_CLOCK)
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
+ clock = CLOCK_MONOTONIC;
+ else
+ #endif
+ clock = CLOCK_REALTIME;
+ }
+
+ const u64 scale_factor = 1000000000;
+ clock_gettime(clock, &ts);
+ return (u64)ts.tv_sec * scale_factor + (u64)ts.tv_nsec;
+}
+
+void RGFW_waitForEvent(i32 waitMS) {
+ if (waitMS == 0) return;
+
+ if (_RGFW->eventWait_forceStop[0] == 0 || _RGFW->eventWait_forceStop[1] == 0) {
+ if (pipe(_RGFW->eventWait_forceStop) != -1) {
+ fcntl(_RGFW->eventWait_forceStop[0], F_GETFL, 0);
+ fcntl(_RGFW->eventWait_forceStop[0], F_GETFD, 0);
+ fcntl(_RGFW->eventWait_forceStop[1], F_GETFL, 0);
+ fcntl(_RGFW->eventWait_forceStop[1], F_GETFD, 0);
+ }
+ }
+
+ struct pollfd fds[2];
+ fds[0].fd = 0;
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+ fds[1].fd = _RGFW->eventWait_forceStop[0];
+ fds[1].events = POLLIN;
+ fds[1].revents = 0;
+
+
+ if (RGFW_usingWayland()) {
+ #ifdef RGFW_WAYLAND
+ fds[0].fd = wl_display_get_fd(_RGFW->wl_display);
+
+ /* empty the queue */
+ while (wl_display_prepare_read(_RGFW->wl_display) != 0) {
+ /* error occured when dispatching the queue */
+ if (wl_display_dispatch_pending(_RGFW->wl_display) == -1) {
+ return;
+ }
+ }
+
+ /* send any pending requests to the compositor */
+ while (wl_display_flush(_RGFW->wl_display) == -1) {
+
+ /* queue is full dispatch them */
+ if (errno == EAGAIN) {
+ if (wl_display_dispatch_pending(_RGFW->wl_display) == -1) {
+ return;
+ }
+ } else {
+ return;
+ }
+ }
+ #endif
+ } else {
+ #ifdef RGFW_X11
+ fds[0].fd = ConnectionNumber(_RGFW->display);
+ #endif
+ }
+
+
+ u64 start = RGFW_unix_getTimeNS();
+ if (RGFW_usingWayland()) {
+ #ifdef RGFW_WAYLAND
+ while (wl_display_dispatch_pending(_RGFW->wl_display) == 0) {
+ if (poll(fds, 1, waitMS) <= 0) {
+ wl_display_cancel_read(_RGFW->wl_display);
+ break;
+ } else {
+ if (wl_display_read_events(_RGFW->wl_display) == -1)
+ return;
+ }
+
+ if (waitMS != RGFW_eventWaitNext) {
+ waitMS -= (i32)(RGFW_unix_getTimeNS() - start) / (i32)1e+6;
+ }
+ }
+
+ /* queue contains events from read, dispatch them */
+ if (wl_display_dispatch_pending(_RGFW->wl_display) == -1) {
+ return;
+ }
+ #endif
+ } else {
+ #ifdef RGFW_X11
+ while (XPending(_RGFW->display) == 0) {
+ if (poll(fds, 1, waitMS) <= 0)
+ break;
+
+ if (waitMS != RGFW_eventWaitNext) {
+ waitMS -= (i32)(RGFW_unix_getTimeNS() - start) / (i32)1e+6;
+ }
+ }
+ #endif
+ }
+
+ /* drain any data in the stop request */
+ if (_RGFW->eventWait_forceStop[2]) {
+ char data[64];
+ RGFW_MEMZERO(data, sizeof(data));
+ (void)!read(_RGFW->eventWait_forceStop[0], data, sizeof(data));
+
+ _RGFW->eventWait_forceStop[2] = 0;
+ }
+}
+
+char* RGFW_strtok(char* str, const char* delimStr);
+char* RGFW_strtok(char* str, const char* delimStr) {
+ static char* static_str = NULL;
+
+ if (str != NULL)
+ static_str = str;
+
+ if (static_str == NULL) {
+ return NULL;
+ }
+
+ while (*static_str != '\0') {
+ RGFW_bool delim = 0;
+ const char* d;
+ for (d = delimStr; *d != '\0'; d++) {
+ if (*static_str == *d) {
+ delim = 1;
+ break;
+ }
+ }
+ if (!delim)
+ break;
+ static_str++;
+ }
+
+ if (*static_str == '\0')
+ return NULL;
+
+ char* token_start = static_str;
+ while (*static_str != '\0') {
+ int delim = 0;
+ const char* d;
+ for (d = delimStr; *d != '\0'; d++) {
+ if (*static_str == *d) {
+ delim = 1;
+ break;
+ }
+ }
+
+ if (delim) {
+ *static_str = '\0';
+ static_str++;
+ break;
+ }
+ static_str++;
+ }
+
+ return token_start;
+}
+
+#ifdef RGFW_X11
+RGFWDEF i32 RGFW_initPlatform_X11(void);
+RGFWDEF void RGFW_deinitPlatform_X11(void);
+#endif
+#ifdef RGFW_WAYLAND
+RGFWDEF i32 RGFW_initPlatform_Wayland(void);
+RGFWDEF void RGFW_deinitPlatform_Wayland(void);
+#endif
+
+RGFWDEF void RGFW_load_X11(void);
+RGFWDEF void RGFW_load_Wayland(void);
+
+#if !defined(RGFW_X11) || !defined(RGFW_WAYLAND)
+void RGFW_load_X11(void) { }
+void RGFW_load_Wayland(void) { }
+#endif
+
+/*
+ * Sadly we have to use magic linux keycodes
+ * We can't use X11 functions, because that breaks Wayland, but they use the same keycodes so there's no use redeffing them
+ * We can't use linux enums, because the headers don't exist on BSD
+ */
+void RGFW_initKeycodesPlatform(void) {
+ _RGFW->keycodes[49] = RGFW_keyBacktick;
+ _RGFW->keycodes[19] = RGFW_key0;
+ _RGFW->keycodes[10] = RGFW_key1;
+ _RGFW->keycodes[11] = RGFW_key2;
+ _RGFW->keycodes[12] = RGFW_key3;
+ _RGFW->keycodes[13] = RGFW_key4;
+ _RGFW->keycodes[14] = RGFW_key5;
+ _RGFW->keycodes[15] = RGFW_key6;
+ _RGFW->keycodes[16] = RGFW_key7;
+ _RGFW->keycodes[17] = RGFW_key8;
+ _RGFW->keycodes[18] = RGFW_key9;
+ _RGFW->keycodes[65] = RGFW_keySpace;
+ _RGFW->keycodes[38] = RGFW_keyA;
+ _RGFW->keycodes[56] = RGFW_keyB;
+ _RGFW->keycodes[54] = RGFW_keyC;
+ _RGFW->keycodes[40] = RGFW_keyD;
+ _RGFW->keycodes[26] = RGFW_keyE;
+ _RGFW->keycodes[41] = RGFW_keyF;
+ _RGFW->keycodes[42] = RGFW_keyG;
+ _RGFW->keycodes[43] = RGFW_keyH;
+ _RGFW->keycodes[31] = RGFW_keyI;
+ _RGFW->keycodes[44] = RGFW_keyJ;
+ _RGFW->keycodes[45] = RGFW_keyK;
+ _RGFW->keycodes[46] = RGFW_keyL;
+ _RGFW->keycodes[58] = RGFW_keyM;
+ _RGFW->keycodes[57] = RGFW_keyN;
+ _RGFW->keycodes[32] = RGFW_keyO;
+ _RGFW->keycodes[33] = RGFW_keyP;
+ _RGFW->keycodes[24] = RGFW_keyQ;
+ _RGFW->keycodes[27] = RGFW_keyR;
+ _RGFW->keycodes[39] = RGFW_keyS;
+ _RGFW->keycodes[28] = RGFW_keyT;
+ _RGFW->keycodes[30] = RGFW_keyU;
+ _RGFW->keycodes[55] = RGFW_keyV;
+ _RGFW->keycodes[25] = RGFW_keyW;
+ _RGFW->keycodes[53] = RGFW_keyX;
+ _RGFW->keycodes[29] = RGFW_keyY;
+ _RGFW->keycodes[52] = RGFW_keyZ;
+ _RGFW->keycodes[60] = RGFW_keyPeriod;
+ _RGFW->keycodes[59] = RGFW_keyComma;
+ _RGFW->keycodes[61] = RGFW_keySlash;
+ _RGFW->keycodes[34] = RGFW_keyBracket;
+ _RGFW->keycodes[35] = RGFW_keyCloseBracket;
+ _RGFW->keycodes[47] = RGFW_keySemicolon;
+ _RGFW->keycodes[48] = RGFW_keyApostrophe;
+ _RGFW->keycodes[51] = RGFW_keyBackSlash;
+ _RGFW->keycodes[36] = RGFW_keyReturn;
+ _RGFW->keycodes[119] = RGFW_keyDelete;
+ _RGFW->keycodes[77] = RGFW_keyNumLock;
+ _RGFW->keycodes[106] = RGFW_keyPadSlash;
+ _RGFW->keycodes[63] = RGFW_keyPadMultiply;
+ _RGFW->keycodes[86] = RGFW_keyPadPlus;
+ _RGFW->keycodes[82] = RGFW_keyPadMinus;
+ _RGFW->keycodes[87] = RGFW_keyPad1;
+ _RGFW->keycodes[88] = RGFW_keyPad2;
+ _RGFW->keycodes[89] = RGFW_keyPad3;
+ _RGFW->keycodes[83] = RGFW_keyPad4;
+ _RGFW->keycodes[84] = RGFW_keyPad5;
+ _RGFW->keycodes[85] = RGFW_keyPad6;
+ _RGFW->keycodes[81] = RGFW_keyPad9;
+ _RGFW->keycodes[90] = RGFW_keyPad0;
+ _RGFW->keycodes[91] = RGFW_keyPadPeriod;
+ _RGFW->keycodes[104] = RGFW_keyPadReturn;
+ _RGFW->keycodes[20] = RGFW_keyMinus;
+ _RGFW->keycodes[21] = RGFW_keyEquals;
+ _RGFW->keycodes[22] = RGFW_keyBackSpace;
+ _RGFW->keycodes[23] = RGFW_keyTab;
+ _RGFW->keycodes[66] = RGFW_keyCapsLock;
+ _RGFW->keycodes[50] = RGFW_keyShiftL;
+ _RGFW->keycodes[37] = RGFW_keyControlL;
+ _RGFW->keycodes[64] = RGFW_keyAltL;
+ _RGFW->keycodes[133] = RGFW_keySuperL;
+ _RGFW->keycodes[105] = RGFW_keyControlR;
+ _RGFW->keycodes[134] = RGFW_keySuperR;
+ _RGFW->keycodes[62] = RGFW_keyShiftR;
+ _RGFW->keycodes[108] = RGFW_keyAltR;
+ _RGFW->keycodes[67] = RGFW_keyF1;
+ _RGFW->keycodes[68] = RGFW_keyF2;
+ _RGFW->keycodes[69] = RGFW_keyF3;
+ _RGFW->keycodes[70] = RGFW_keyF4;
+ _RGFW->keycodes[71] = RGFW_keyF5;
+ _RGFW->keycodes[72] = RGFW_keyF6;
+ _RGFW->keycodes[73] = RGFW_keyF7;
+ _RGFW->keycodes[74] = RGFW_keyF8;
+ _RGFW->keycodes[75] = RGFW_keyF9;
+ _RGFW->keycodes[76] = RGFW_keyF10;
+ _RGFW->keycodes[95] = RGFW_keyF11;
+ _RGFW->keycodes[96] = RGFW_keyF12;
+ _RGFW->keycodes[111] = RGFW_keyUp;
+ _RGFW->keycodes[116] = RGFW_keyDown;
+ _RGFW->keycodes[113] = RGFW_keyLeft;
+ _RGFW->keycodes[114] = RGFW_keyRight;
+ _RGFW->keycodes[118] = RGFW_keyInsert;
+ _RGFW->keycodes[115] = RGFW_keyEnd;
+ _RGFW->keycodes[112] = RGFW_keyPageUp;
+ _RGFW->keycodes[117] = RGFW_keyPageDown;
+ _RGFW->keycodes[9] = RGFW_keyEscape;
+ _RGFW->keycodes[110] = RGFW_keyHome;
+ _RGFW->keycodes[78] = RGFW_keyScrollLock;
+ _RGFW->keycodes[107] = RGFW_keyPrintScreen;
+ _RGFW->keycodes[128] = RGFW_keyPause;
+ _RGFW->keycodes[191] = RGFW_keyF13;
+ _RGFW->keycodes[192] = RGFW_keyF14;
+ _RGFW->keycodes[193] = RGFW_keyF15;
+ _RGFW->keycodes[194] = RGFW_keyF16;
+ _RGFW->keycodes[195] = RGFW_keyF17;
+ _RGFW->keycodes[196] = RGFW_keyF18;
+ _RGFW->keycodes[197] = RGFW_keyF19;
+ _RGFW->keycodes[198] = RGFW_keyF20;
+ _RGFW->keycodes[199] = RGFW_keyF21;
+ _RGFW->keycodes[200] = RGFW_keyF22;
+ _RGFW->keycodes[201] = RGFW_keyF23;
+ _RGFW->keycodes[202] = RGFW_keyF24;
+ _RGFW->keycodes[203] = RGFW_keyF25;
+ _RGFW->keycodes[142] = RGFW_keyPadEqual;
+ _RGFW->keycodes[161] = RGFW_keyWorld1; /* non-US key #1 */
+ _RGFW->keycodes[162] = RGFW_keyWorld2; /* non-US key #2 */
+}
+
+i32 RGFW_initPlatform(void) {
+#ifdef RGFW_WAYLAND
+ RGFW_load_Wayland();
+ i32 ret = RGFW_initPlatform_Wayland();
+
+ if (ret == 0) {
+ return 0;
+ } else {
+ #ifdef RGFW_X11
+ RGFW_debugCallback(RGFW_typeWarning, RGFW_warningWayland, "Falling back to X11");
+ RGFW_useWayland(0);
+ #else
+ return ret;
+ #endif
+ }
+#endif
+#ifdef RGFW_X11
+ RGFW_load_X11();
+ return RGFW_initPlatform_X11();
+#else
+ return 0;
+#endif
+}
+
+
+void RGFW_deinitPlatform(void) {
+ if (_RGFW->eventWait_forceStop[0] || _RGFW->eventWait_forceStop[1]){
+ close(_RGFW->eventWait_forceStop[0]);
+ close(_RGFW->eventWait_forceStop[1]);
+ }
+#ifdef RGFW_WAYLAND
+ if (RGFW_usingWayland()) {
+ RGFW_deinitPlatform_Wayland();
+ return;
+ }
+#endif
+#ifdef RGFW_X11
+ RGFW_deinitPlatform_X11();
+#endif
+}
+
+RGFWDEF size_t RGFW_unix_stringlen(const char* name);
+size_t RGFW_unix_stringlen(const char* name) {
+ size_t i = 0;
+ while (name[i]) { i++; }
+ return i;
+}
+
+RGFWDEF void RGFW_unix_parseURI(RGFW_window* win, char* data);
+void RGFW_unix_parseURI(RGFW_window* win, char* data) {
+ const char* prefix = (const char*)"file://";
+ char* line;
+ while ((line = (char*)RGFW_strtok(data, "\r\n"))) {
+ data = NULL;
+
+ if (line[0] == '#')
+ continue;
+
+ char* l;
+ for (l = line; 1; l++) {
+ if ((l - line) > 7)
+ break;
+ else if (*l != prefix[(l - line)])
+ break;
+ else if (*l == '\0' && prefix[(l - line)] == '\0') {
+ line += 7;
+ while (*line != '/')
+ line++;
+ break;
+ } else if (*l == '\0')
+ break;
+ }
+
+ size_t len = RGFW_unix_stringlen(line);
+ char* path = (char*)RGFW_ALLOC(len + 1);
+
+ size_t index = 0;
+ while (*line) {
+ if (line[0] == '%' && line[1] && line[2]) {
+ char digits[3] = {0};
+ digits[0] = line[1];
+ digits[1] = line[2];
+ digits[2] = '\0';
+ path[index] = (char) RGFW_STRTOL(digits, NULL, 16);
+ line += 2;
+ } else {
+ if (index >= len) {
+ break;
+ }
+
+ path[index] = *line;
+ }
+
+ index++;
+ line++;
+ }
+
+ path[len] = '\0';
+ RGFW_dataDropCallback(win, (const char*)path, len + 1, RGFW_dataFile);
+ RGFW_FREE(path);
+ }
+}
+
+
+#endif /* end of wayland or X11 defines */
+
+
+/*
+
+
+Start of *nix defines
+
+
+*/
+
+#ifdef RGFW_X11
+#ifdef RGFW_WAYLAND
+#define RGFW_FUNC(func) func##_X11
+#else
+#define RGFW_FUNC(func) func
+#endif
+
+#include <dlfcn.h>
+#include <unistd.h>
+
+#include <limits.h> /* for data limits (mainly used in drag and drop functions) */
+#include <poll.h>
+
+void RGFW_setXInstName(const char* name) { _RGFW->instName = name; }
+#if !defined(RGFW_NO_X11_CURSOR) && defined(RGFW_X11)
+ #include <X11/Xcursor/Xcursor.h>
+#endif
+
+#include <X11/Xatom.h>
+#include <X11/keysymdef.h>
+#include <X11/extensions/sync.h>
+
+#include <X11/XKBlib.h> /* for converting keycode to string */
+#include <X11/cursorfont.h> /* for hiding */
+#include <X11/extensions/shapeconst.h>
+#include <X11/extensions/shape.h>
+#include <X11/extensions/XInput2.h>
+
+#ifdef RGFW_OPENGL
+ #ifndef __gl_h_
+ #define __gl_h_
+ #define RGFW_gl_ndef
+ #define GLubyte unsigned char
+ #define GLenum unsigned int
+ #define GLint int
+ #define GLuint unsigned int
+ #define GLsizei int
+ #define GLfloat float
+ #define GLvoid void
+ #define GLbitfield unsigned int
+ #define GLintptr ptrdiff_t
+ #define GLsizeiptr ptrdiff_t
+ #define GLboolean unsigned char
+ #endif
+
+ #include <GL/glx.h> /* GLX defs, xlib.h, gl.h */
+ #ifndef GLX_MESA_swap_control
+ #define GLX_MESA_swap_control
+ #endif
+
+ #ifdef RGFW_gl_ndef
+ #undef __gl_h_
+ #undef GLubyte
+ #undef GLenum
+ #undef GLint
+ #undef GLuint
+ #undef GLsizei
+ #undef GLfloat
+ #undef GLvoid
+ #undef GLbitfield
+ #undef GLintptr
+ #undef GLsizeiptr
+ #undef GLboolean
+ #endif
+ typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
+#endif
+
+/* atoms needed for drag and drop */
+#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD)
+ typedef XcursorImage* (*PFN_XcursorImageCreate)(int, int);
+ typedef void (*PFN_XcursorImageDestroy)(XcursorImage*);
+ typedef Cursor(*PFN_XcursorImageLoadCursor)(Display*, const XcursorImage*);
+#endif
+
+#if !defined(RGFW_NO_X11_XI_PRELOAD)
+ typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int);
+ PFN_XISelectEvents XISelectEventsSRC = NULL;
+ #define XISelectEvents XISelectEventsSRC
+
+ void* X11Xihandle = NULL;
+#endif
+
+#if !defined(RGFW_NO_X11_EXT_PRELOAD)
+ typedef void (* PFN_XSyncIntToValue)(XSyncValue*, int);
+ PFN_XSyncIntToValue XSyncIntToValueSRC = NULL;
+ #define XSyncIntToValue XSyncIntToValueSRC
+
+ typedef Status (* PFN_XSyncSetCounter)(Display*, XSyncCounter, XSyncValue);
+ PFN_XSyncSetCounter XSyncSetCounterSRC = NULL;
+ #define XSyncSetCounter XSyncSetCounterSRC
+
+ typedef XSyncCounter (* PFN_XSyncCreateCounter)(Display*, XSyncValue);
+ PFN_XSyncCreateCounter XSyncCreateCounterSRC = NULL;
+ #define XSyncCreateCounter XSyncCreateCounterSRC
+
+ typedef void (* PFN_XShapeCombineMask)(Display*,Window,int,int,int,Pixmap,int);
+ PFN_XShapeCombineMask XShapeCombineMaskSRC;
+ #define XShapeCombineMask XShapeCombineMaskSRC
+
+ typedef void (* PFN_XShapeCombineRegion)(Display*,Window,int,int,int,Region,int);
+ PFN_XShapeCombineRegion XShapeCombineRegionSRC;
+ #define XShapeCombineRegion XShapeCombineRegionSRC
+ void* X11XEXThandle = NULL;
+#endif
+
+#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD)
+ PFN_XcursorImageLoadCursor XcursorImageLoadCursorSRC = NULL;
+ PFN_XcursorImageCreate XcursorImageCreateSRC = NULL;
+ PFN_XcursorImageDestroy XcursorImageDestroySRC = NULL;
+
+ #define XcursorImageLoadCursor XcursorImageLoadCursorSRC
+ #define XcursorImageCreate XcursorImageCreateSRC
+ #define XcursorImageDestroy XcursorImageDestroySRC
+
+ void* X11Cursorhandle = NULL;
+#endif
+
+RGFWDEF RGFW_bool RGFW_waitForShowEvent_X11(RGFW_window* win);
+RGFW_bool RGFW_waitForShowEvent_X11(RGFW_window* win) {
+ XEvent dummy;
+ while (!XCheckTypedWindowEvent(_RGFW->display, win->src.window, VisibilityNotify, &dummy)) {
+ RGFW_waitForEvent(100);
+ }
+
+ return RGFW_TRUE;
+}
+
+RGFWDEF void RGFW_x11_icCallback(XIC ic, char* clientData, char* callData);
+void RGFW_x11_icCallback(XIC ic, char* clientData, char* callData) {
+ RGFW_UNUSED(ic); RGFW_UNUSED(callData);
+ RGFW_window* win = (RGFW_window*)(void*)clientData;
+ win->src.ic = NULL;
+}
+
+RGFWDEF void RGFW_x11_imCallback(XIM im, char* clientData, char* callData);
+void RGFW_x11_imCallback(XIM im, char* clientData, char* callData) {
+ RGFW_UNUSED(im); RGFW_UNUSED(clientData); RGFW_UNUSED(callData);
+ _RGFW->im = NULL;
+}
+
+RGFWDEF void RGFW_x11_imInitCallback(Display* display, XPointer clientData, XPointer callData);
+void RGFW_x11_imInitCallback(Display* display, XPointer clientData, XPointer callData) {
+ RGFW_UNUSED(display); RGFW_UNUSED(clientData); RGFW_UNUSED(callData);
+
+ if (_RGFW->im) {
+ return;
+ }
+
+ _RGFW->im = XOpenIM(_RGFW->display, 0, NULL, NULL);
+ if (_RGFW->im == NULL) {
+ return;
+ }
+
+ RGFW_bool found = RGFW_FALSE;
+ XIMStyles* styles = NULL;
+
+ if (XGetIMValues(_RGFW->im, XNQueryInputStyle, &styles, NULL) != NULL) {
+ found = RGFW_FALSE;
+ } else {
+ for (unsigned int i = 0; i < styles->count_styles; i++) {
+ if (styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing)) {
+ found = RGFW_TRUE;
+ break;
+ }
+ }
+
+ XFree(styles);
+ }
+
+ if (found == RGFW_FALSE) {
+ XCloseIM(_RGFW->im);
+ _RGFW->im = NULL;
+ }
+
+ XIMCallback callback;
+ callback.callback = (XIMProc) RGFW_x11_imCallback;
+ callback.client_data = NULL;
+ XSetIMValues(_RGFW->im, XNDestroyCallback, &callback, NULL);
+}
+
+void* RGFW_getDisplay_X11(void) { return _RGFW->display; }
+u64 RGFW_window_getWindow_X11(RGFW_window* win) { return (u64)win->src.window; }
+
+RGFWDEF RGFW_format RGFW_XImage_getFormat(XImage* image);
+RGFW_format RGFW_XImage_getFormat(XImage* image) {
+ switch (image->bits_per_pixel) {
+ case 24:
+ if (image->red_mask == 0xFF0000 && image->green_mask == 0x00FF00 && image->blue_mask == 0x0000FF)
+ return RGFW_formatRGB8;
+ if (image->red_mask == 0x0000FF && image->green_mask == 0x00FF00 && image->blue_mask == 0xFF0000)
+ return RGFW_formatBGR8;
+ break;
+ case 32:
+ if (image->red_mask == 0x00FF0000 && image->green_mask == 0x0000FF00 && image->blue_mask == 0x000000FF)
+ return RGFW_formatBGRA8;
+ if (image->red_mask == 0x000000FF && image->green_mask == 0x0000FF00 && image->blue_mask == 0x00FF0000)
+ return RGFW_formatRGBA8;
+ if (image->red_mask == 0x0000FF00 && image->green_mask == 0x00FF0000 && image->blue_mask == 0xFF000000)
+ return RGFW_formatABGR8;
+ if (image->red_mask == 0x00FF0000 && image->green_mask == 0x0000FF00 && image->blue_mask == 0x000000FF)
+ return RGFW_formatARGB8; /* ambiguous without alpha */
+ break;
+ }
+ return RGFW_formatARGB8;
+}
+
+
+RGFW_bool RGFW_window_createSurfacePtr(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) {
+ RGFW_ASSERT(surface != NULL);
+ surface->data = data;
+ surface->w = w;
+ surface->h = h;
+ surface->format = format;
+
+ XWindowAttributes attrs;
+ if (XGetWindowAttributes(_RGFW->display, win->src.window, &attrs) == 0) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errBuffer, "Failed to get window attributes.");
+ return RGFW_FALSE;
+ }
+
+ surface->native.bitmap = XCreateImage(_RGFW->display, attrs.visual, (u32)attrs.depth,
+ ZPixmap, 0, NULL, (u32)surface->w, (u32)surface->h, 32, 0);
+
+ surface->native.buffer = (u8*)RGFW_ALLOC((size_t)(w * h * 4));
+ surface->native.format = RGFW_XImage_getFormat(surface->native.bitmap);
+
+ if (surface->native.bitmap == NULL) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errBuffer, "Failed to create XImage.");
+ return RGFW_FALSE;
+ }
+
+ surface->native.format = RGFW_formatBGRA8;
+ return RGFW_TRUE;
+}
+
+RGFW_format RGFW_FUNC(RGFW_nativeFormat)(void) { return RGFW_formatBGRA8; }
+
+RGFW_bool RGFW_FUNC(RGFW_createSurfacePtr) (u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) {
+ return RGFW_window_createSurfacePtr(_RGFW->root, data, w, h, format, surface);
+}
+
+void RGFW_FUNC(RGFW_window_blitSurface) (RGFW_window* win, RGFW_surface* surface) {
+ RGFW_ASSERT(surface != NULL);
+ surface->native.bitmap->data = (char*)surface->native.buffer;
+ RGFW_copyImageData((u8*)surface->native.buffer, surface->w, RGFW_MIN(win->h, surface->h), surface->native.format, surface->data, surface->format, surface->convertFunc);
+
+ XPutImage(_RGFW->display, win->src.window, win->src.gc, surface->native.bitmap, 0, 0, 0, 0, (u32)RGFW_MIN(win->w, surface->w), (u32)RGFW_MIN(win->h, surface->h));
+ surface->native.bitmap->data = NULL;
+ return;
+}
+
+void RGFW_FUNC(RGFW_surface_freePtr) (RGFW_surface* surface) {
+ RGFW_ASSERT(surface != NULL);
+ RGFW_FREE(surface->native.buffer);
+ XDestroyImage(surface->native.bitmap);
+ return;
+}
+
+#define RGFW_LOAD_ATOM(name) \
+ static Atom name = 0; \
+ if (name == 0) name = XInternAtom(_RGFW->display, #name, False);
+
+void RGFW_FUNC(RGFW_window_setBorder) (RGFW_window* win, RGFW_bool border) {
+ RGFW_setBit(&win->internal.flags, RGFW_windowNoBorder, !border);
+ RGFW_LOAD_ATOM(_MOTIF_WM_HINTS);
+
+ struct __x11WindowHints {
+ unsigned long flags, functions, decorations, status;
+ long input_mode;
+ } hints;
+ hints.flags = 2;
+ hints.decorations = border;
+
+ XChangeProperty(_RGFW->display, win->src.window, _MOTIF_WM_HINTS, _MOTIF_WM_HINTS, 32, PropModeReplace, (u8*)&hints, 5);
+
+ if (RGFW_window_isHidden(win) == 0) {
+ RGFW_window_hide(win);
+ RGFW_window_show(win);
+ }
+}
+
+void RGFW_FUNC(RGFW_window_setRawMouseModePlatform) (RGFW_window* win, RGFW_bool state) {
+ RGFW_UNUSED(win);
+ unsigned char mask[XIMaskLen(XI_RawMotion)];
+ RGFW_MEMZERO(mask, sizeof(mask));
+ if (state) XISetMask(mask, XI_RawMotion);
+
+ XIEventMask em;
+ em.deviceid = XIAllMasterDevices;
+ em.mask_len = sizeof(mask);
+ em.mask = mask;
+
+ XISelectEvents(_RGFW->display, XDefaultRootWindow(_RGFW->display), &em, 1);
+}
+
+void RGFW_FUNC(RGFW_window_captureMousePlatform) (RGFW_window* win, RGFW_bool state) {
+ if (state) {
+ unsigned int event_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
+ XGrabPointer(_RGFW->display, win->src.window, True, event_mask, GrabModeAsync, GrabModeAsync, win->src.window, None, CurrentTime);
+ } else {
+ XUngrabPointer(_RGFW->display, CurrentTime);
+ }
+}
+
+#define RGFW_LOAD_LIBRARY(x, lib) if (x == NULL) x = dlopen(lib, RTLD_LAZY | RTLD_LOCAL)
+#define RGFW_PROC_DEF(proc, name) if (name##SRC == NULL && proc != NULL) { \
+ void* ptr = dlsym(proc, #name); \
+ if (ptr != NULL) RGFW_MEMCPY(&name##SRC, &ptr, sizeof(PFN_##name)); \
+}
+
+RGFWDEF void RGFW_window_getVisual(XVisualInfo* visual, RGFW_bool transparent);
+void RGFW_window_getVisual(XVisualInfo* visual, RGFW_bool transparent) {
+ visual->visual = DefaultVisual(_RGFW->display, DefaultScreen(_RGFW->display));
+ visual->depth = DefaultDepth(_RGFW->display, DefaultScreen(_RGFW->display));
+ if (transparent) {
+ XMatchVisualInfo(_RGFW->display, DefaultScreen(_RGFW->display), 32, TrueColor, visual); /*!< for RGBA backgrounds */
+ if (visual->depth != 32)
+ RGFW_debugCallback(RGFW_typeWarning, RGFW_warningOpenGL, "Failed to load a 32-bit depth.");
+ }
+}
+
+RGFWDEF int RGFW_XErrorHandler(Display* display, XErrorEvent* ev);
+int RGFW_XErrorHandler(Display* display, XErrorEvent* ev) {
+ char errorText[512];
+ XGetErrorText(display, ev->error_code, errorText, sizeof(errorText));
+
+ char buf[1024];
+ RGFW_SNPRINTF(buf, sizeof(buf), "[X Error] %s\n Error code: %d\n Request code: %d\n Minor code: %d\n Serial: %lu\n",
+ errorText,
+ ev->error_code, ev->request_code, ev->minor_code, ev->serial);
+
+ RGFW_debugCallback(RGFW_typeError, RGFW_errX11, buf);
+ _RGFW->x11Error = ev;
+ return 0;
+}
+
+void RGFW_XCreateWindow (XVisualInfo visual, const char* name, RGFW_windowFlags flags, RGFW_window* win) {
+ i64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask |
+ LeaveWindowMask | EnterWindowMask | ExposureMask | VisibilityChangeMask | PropertyChangeMask;
+
+ /* make X window attrubutes */
+ XSetWindowAttributes swa;
+ RGFW_MEMZERO(&swa, sizeof(swa));
+
+ win->src.parent = DefaultRootWindow(_RGFW->display);
+
+ Colormap cmap;
+ swa.colormap = cmap = XCreateColormap(_RGFW->display,
+ win->src.parent,
+ visual.visual, AllocNone);
+ swa.event_mask = event_mask;
+ swa.background_pixmap = None;
+
+ /* create the window */
+ win->src.window = XCreateWindow(_RGFW->display, win->src.parent, win->x, win->y, (u32)win->w, (u32)win->h,
+ 0, visual.depth, InputOutput, visual.visual,
+ CWBorderPixel | CWColormap | CWEventMask, &swa);
+
+ win->src.flashEnd = 0;
+
+ XFreeColors(_RGFW->display, cmap, NULL, 0, 0);
+
+ XSaveContext(_RGFW->display, win->src.window, _RGFW->context, (XPointer)win);
+
+ win->src.gc = XCreateGC(_RGFW->display, win->src.window, 0, NULL);
+
+ if (_RGFW->im) {
+ XIMCallback callback;
+ callback.callback = (XIMProc) RGFW_x11_icCallback;
+ callback.client_data = (XPointer) win;
+
+ win->src.ic = XCreateIC(_RGFW->im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, win->src.window, XNFocusWindow, win->src.window, XNDestroyCallback, &callback, NULL);
+ }
+
+
+ /* In your .desktop app, if you set the property
+ StartupWMClass=RGFW that will assoicate the launcher icon
+ with your application - robrohan */
+
+ XClassHint hint;
+ hint.res_class = (char*)RGFW_className;
+
+ if (_RGFW->instName == NULL) hint.res_name = (char*)name;
+ else hint.res_name = (char*)_RGFW->instName;
+
+ XSetClassHint(_RGFW->display, win->src.window, &hint);
+
+ XWMHints hints;
+ hints.flags = StateHint;
+ hints.initial_state = NormalState;
+
+ XSetWMHints(_RGFW->display, win->src.window, &hints);
+
+ XSelectInput(_RGFW->display, (Drawable) win->src.window, event_mask); /*!< tell X11 what events we want */
+
+ /* make it so the user can't close the window until the program does */
+ RGFW_LOAD_ATOM(WM_DELETE_WINDOW);
+ XSetWMProtocols(_RGFW->display, (Drawable) win->src.window, &WM_DELETE_WINDOW, 1);
+ /* set the background */
+ RGFW_window_setName(win, name);
+
+ XMoveWindow(_RGFW->display, (Drawable) win->src.window, win->x, win->y); /*!< move the window to it's proper cords */
+
+ if (flags & RGFW_windowAllowDND) { /* init drag and drop atoms and turn on drag and drop for this window */
+ win->internal.flags |= RGFW_windowAllowDND;
+
+ /* actions */
+ Atom XdndAware = XInternAtom(_RGFW->display, "XdndAware", False);
+ const u8 version = 5;
+
+ XChangeProperty(_RGFW->display, win->src.window,
+ XdndAware, 4, 32,
+ PropModeReplace, &version, 1); /*!< turns on drag and drop */
+ }
+
+#ifdef RGFW_ADVANCED_SMOOTH_RESIZE
+ RGFW_LOAD_ATOM(_NET_WM_SYNC_REQUEST_COUNTER)
+ RGFW_LOAD_ATOM(_NET_WM_SYNC_REQUEST)
+
+ Atom protcols[2] = {_NET_WM_SYNC_REQUEST, WM_DELETE_WINDOW};
+ XSetWMProtocols(_RGFW->display, win->src.window, protcols, 2);
+
+ XSyncValue initial_value;
+ XSyncIntToValue(&initial_value, 0);
+ win->src.counter = XSyncCreateCounter(_RGFW->display, initial_value);
+
+ XChangeProperty(_RGFW->display, win->src.window, _NET_WM_SYNC_REQUEST_COUNTER, XA_CARDINAL, 32, PropModeReplace, (u8*)&win->src.counter, 1);
+#endif
+
+ win->src.x = win->x;
+ win->src.y = win->y;
+ win->src.w = win->w;
+ win->src.h = win->h;
+
+ XSetWindowBackground(_RGFW->display, win->src.window, None);
+ XClearWindow(_RGFW->display, win->src.window);
+
+ /* stupid hack to make resizing the window less bad */
+ XSetWindowBackgroundPixmap(_RGFW->display, win->src.window, None);
+}
+
+RGFW_window* RGFW_FUNC(RGFW_createWindowPlatform) (const char* name, RGFW_windowFlags flags, RGFW_window* win) {
+ if ((flags & RGFW_windowOpenGL) || (flags & RGFW_windowEGL)) {
+ win->src.window = 0;
+ return win;
+ }
+
+ XVisualInfo visual;
+ RGFW_window_getVisual(&visual, RGFW_BOOL(win->internal.flags & RGFW_windowTransparent));
+ RGFW_XCreateWindow(visual, name, flags, win);
+ return win; /*return newly created window */
+}
+
+RGFW_bool RGFW_FUNC(RGFW_getGlobalMouse) (i32* fX, i32* fY) {
+ RGFW_init();
+ i32 x, y;
+ u32 z;
+ Window window1, window2;
+ XQueryPointer(_RGFW->display, XDefaultRootWindow(_RGFW->display), &window1, &window2, fX, fY, &x, &y, &z);
+ return RGFW_TRUE;
+}
+
+RGFWDEF void RGFW_XHandleClipboardSelection(XEvent* event);
+void RGFW_XHandleClipboardSelection(XEvent* event) { RGFW_UNUSED(event);
+ RGFW_LOAD_ATOM(ATOM_PAIR);
+ RGFW_LOAD_ATOM(MULTIPLE);
+ RGFW_LOAD_ATOM(TARGETS);
+ RGFW_LOAD_ATOM(SAVE_TARGETS);
+ RGFW_LOAD_ATOM(UTF8_STRING);
+
+ const XSelectionRequestEvent* request = &event->xselectionrequest;
+ Atom formats[2] = {0};
+ formats[0] = UTF8_STRING;
+ formats[1] = XA_STRING;
+ const int formatCount = sizeof(formats) / sizeof(formats[0]);
+
+ if (request->target == TARGETS) {
+ Atom targets[4] = {0};
+ targets[0] = TARGETS;
+ targets[1] = MULTIPLE;
+ targets[2] = UTF8_STRING;
+ targets[3] = XA_STRING;
+
+ XChangeProperty(_RGFW->display, request->requestor, request->property,
+ XA_ATOM, 32, PropModeReplace, (u8*) targets, sizeof(targets) / sizeof(Atom));
+ } else if (request->target == MULTIPLE) {
+ Atom* targets = NULL;
+
+ Atom actualType = 0;
+ int actualFormat = 0;
+ unsigned long count = 0, bytesAfter = 0;
+
+ XGetWindowProperty(_RGFW->display, request->requestor, request->property, 0, LONG_MAX,
+ False, ATOM_PAIR, &actualType, &actualFormat, &count, &bytesAfter, (u8**) &targets);
+
+ unsigned long i;
+ for (i = 0; i < (u32)count; i += 2) {
+ if (targets[i] == UTF8_STRING || targets[i] == XA_STRING)
+ XChangeProperty(_RGFW->display, request->requestor, targets[i + 1], targets[i],
+ 8, PropModeReplace, (const unsigned char *)_RGFW->unixClipboard->data, (i32)_RGFW->unixClipboard->length);
+ else
+ targets[i + 1] = None;
+ }
+
+ XChangeProperty(_RGFW->display,
+ request->requestor, request->property, ATOM_PAIR, 32,
+ PropModeReplace, (u8*) targets, (i32)count);
+
+ XFlush(_RGFW->display);
+ XFree(targets);
+ } else if (request->target == SAVE_TARGETS)
+ XChangeProperty(_RGFW->display, request->requestor, request->property, 0, 32, PropModeReplace, NULL, 0);
+ else {
+ int i;
+ for (i = 0; i < formatCount; i++) {
+ if (request->target != formats[i])
+ continue;
+ XChangeProperty(_RGFW->display, request->requestor, request->property, request->target,
+ 8, PropModeReplace, (u8*) _RGFW->unixClipboard->data, (i32)_RGFW->unixClipboard->length);
+ }
+ }
+
+ XEvent reply = { SelectionNotify };
+ reply.xselection.property = request->property;
+ reply.xselection.display = request->display;
+ reply.xselection.requestor = request->requestor;
+ reply.xselection.selection = request->selection;
+ reply.xselection.target = request->target;
+ reply.xselection.time = request->time;
+
+ XSendEvent(_RGFW->display, request->requestor, False, 0, &reply);
+ XFlush(_RGFW->display);
+}
+
+i32 RGFW_XHandleClipboardSelectionHelper(void);
+
+RGFW_key RGFW_FUNC(RGFW_physicalToMappedKey) (RGFW_key key) {
+ KeyCode keycode = (KeyCode)RGFW_rgfwToApiKey(key);
+ KeySym sym = XkbKeycodeToKeysym(_RGFW->display, keycode, 0, 0);
+
+ if (sym < 256) {
+ return (RGFW_key)sym;
+ }
+
+ switch (sym) {
+ case XK_F1: return RGFW_keyF1;
+ case XK_F2: return RGFW_keyF2;
+ case XK_F3: return RGFW_keyF3;
+ case XK_F4: return RGFW_keyF4;
+ case XK_F5: return RGFW_keyF5;
+ case XK_F6: return RGFW_keyF6;
+ case XK_F7: return RGFW_keyF7;
+ case XK_F8: return RGFW_keyF8;
+ case XK_F9: return RGFW_keyF9;
+ case XK_F10: return RGFW_keyF10;
+ case XK_F11: return RGFW_keyF11;
+ case XK_F12: return RGFW_keyF12;
+ case XK_F13: return RGFW_keyF13;
+ case XK_F14: return RGFW_keyF14;
+ case XK_F15: return RGFW_keyF15;
+ case XK_F16: return RGFW_keyF16;
+ case XK_F17: return RGFW_keyF17;
+ case XK_F18: return RGFW_keyF18;
+ case XK_F19: return RGFW_keyF19;
+ case XK_F20: return RGFW_keyF20;
+ case XK_F21: return RGFW_keyF21;
+ case XK_F22: return RGFW_keyF22;
+ case XK_F23: return RGFW_keyF23;
+ case XK_F24: return RGFW_keyF24;
+ case XK_F25: return RGFW_keyF25;
+ case XK_Shift_L: return RGFW_keyShiftL;
+ case XK_Shift_R: return RGFW_keyShiftR;
+ case XK_Control_L: return RGFW_keyControlL;
+ case XK_Control_R: return RGFW_keyControlR;
+ case XK_Alt_L: return RGFW_keyAltL;
+ case XK_Alt_R: return RGFW_keyAltR;
+ case XK_Super_L: return RGFW_keySuperL;
+ case XK_Super_R: return RGFW_keySuperR;
+ case XK_Caps_Lock: return RGFW_keyCapsLock;
+ case XK_Num_Lock: return RGFW_keyNumLock;
+ case XK_Scroll_Lock:return RGFW_keyScrollLock;
+ case XK_Up: return RGFW_keyUp;
+ case XK_Down: return RGFW_keyDown;
+ case XK_Left: return RGFW_keyLeft;
+ case XK_Right: return RGFW_keyRight;
+ case XK_Home: return RGFW_keyHome;
+ case XK_End: return RGFW_keyEnd;
+ case XK_Page_Up: return RGFW_keyPageUp;
+ case XK_Page_Down: return RGFW_keyPageDown;
+ case XK_Insert: return RGFW_keyInsert;
+ case XK_Menu: return RGFW_keyMenu;
+ case XK_KP_Add: return RGFW_keyPadPlus;
+ case XK_KP_Subtract: return RGFW_keyPadMinus;
+ case XK_KP_Multiply: return RGFW_keyPadMultiply;
+ case XK_KP_Divide: return RGFW_keyPadSlash;
+ case XK_KP_Equal: return RGFW_keyPadEqual;
+ case XK_KP_Enter: return RGFW_keyPadReturn;
+ case XK_KP_Decimal: return RGFW_keyPadPeriod;
+ case XK_KP_0: return RGFW_keyPad0;
+ case XK_KP_1: return RGFW_keyPad1;
+ case XK_KP_2: return RGFW_keyPad2;
+ case XK_KP_3: return RGFW_keyPad3;
+ case XK_KP_4: return RGFW_keyPad4;
+ case XK_KP_5: return RGFW_keyPad5;
+ case XK_KP_6: return RGFW_keyPad6;
+ case XK_KP_7: return RGFW_keyPad7;
+ case XK_KP_8: return RGFW_keyPad8;
+ case XK_KP_9: return RGFW_keyPad9;
+ case XK_Print: return RGFW_keyPrintScreen;
+ case XK_Pause: return RGFW_keyPause;
+ default: break;
+ }
+
+ return RGFW_keyNULL;
+}
+
+RGFWDEF void RGFW_XHandleEvent(void);
+void RGFW_XHandleEvent(void) {
+ RGFW_LOAD_ATOM(XdndTypeList);
+ RGFW_LOAD_ATOM(XdndSelection);
+ RGFW_LOAD_ATOM(XdndEnter);
+ RGFW_LOAD_ATOM(XdndPosition);
+ RGFW_LOAD_ATOM(XdndStatus);
+ RGFW_LOAD_ATOM(XdndLeave);
+ RGFW_LOAD_ATOM(XdndDrop);
+ RGFW_LOAD_ATOM(XdndFinished);
+ RGFW_LOAD_ATOM(XdndActionCopy);
+ RGFW_LOAD_ATOM(_NET_WM_SYNC_REQUEST);
+ RGFW_LOAD_ATOM(WM_PROTOCOLS);
+ RGFW_LOAD_ATOM(WM_STATE);
+ RGFW_LOAD_ATOM(_NET_WM_STATE);
+
+ static float deltaX = 0.0f;
+ static float deltaY = 0.0f;
+
+ XEvent E;
+
+ XNextEvent(_RGFW->display, &E);
+
+ if (E.type != GenericEvent) {
+ deltaX = 0.0f;
+ deltaY = 0.0f;
+ }
+
+ if (E.type == _RGFW->xrandrEventBase + RRNotify) {
+ RGFW_pollMonitors();
+ return;
+ }
+
+ switch (E.type) {
+ case SelectionRequest:
+ RGFW_XHandleClipboardSelection(&E);
+ return;
+ case GenericEvent: {
+ XGetEventData(_RGFW->display, &E.xcookie);
+ switch (E.xcookie.evtype) {
+ case XI_RawMotion: {
+ XIRawEvent* raw = (XIRawEvent *)E.xcookie.data;
+ if (raw->valuators.mask_len == 0) {
+ XFreeEventData(_RGFW->display, &E.xcookie);
+ return;
+ }
+
+ i32 index = 0;
+ if (XIMaskIsSet(raw->valuators.mask, 0) != 0) {
+ deltaX += (float)raw->raw_values[index];
+ index += 1;
+ }
+
+ if (XIMaskIsSet(raw->valuators.mask, 1) != 0)
+ deltaY += (float)raw->raw_values[index];
+
+ _RGFW->vectorX = (float)deltaX;
+ _RGFW->vectorY = (float)deltaY;
+ RGFW_rawMotionCallback(_RGFW->root, _RGFW->vectorX, _RGFW->vectorY);
+ }
+ default: break;
+ }
+
+ XFreeEventData(_RGFW->display, &E.xcookie);
+ return;
+ }
+ }
+
+ RGFW_window* win = NULL;
+ if (XFindContext(_RGFW->display, E.xany.window, _RGFW->context, (XPointer*) &win) != 0) {
+ return;
+ }
+
+ if (win->src.flashEnd) {
+ if ((win->src.flashEnd <= RGFW_unix_getTimeNS()) || RGFW_window_isInFocus(win)) {
+ RGFW_window_flash(win, RGFW_flashCancel);
+ }
+ }
+
+
+ /*
+ Repeated key presses are sent as a release followed by another press at the same time.
+ We want to convert that into a single key press event with the repeat flag set
+ */
+
+ RGFW_bool keyRepeat = RGFW_FALSE;
+
+ if (E.type == KeyRelease && XEventsQueued(_RGFW->display, QueuedAfterReading)) {
+ XEvent NE;
+ XPeekEvent(_RGFW->display, &NE);
+ if (NE.type == KeyPress && E.xkey.time == NE.xkey.time && E.xkey.keycode == NE.xkey.keycode) {
+ /* Use the next KeyPress event */
+ XNextEvent(_RGFW->display, &E);
+ keyRepeat = RGFW_TRUE;
+ }
+ }
+
+ switch (E.type) {
+ case KeyPress: {
+ if (!(win->internal.enabledEvents & RGFW_keyPressedFlag)) return;
+ RGFW_key value = (u8)RGFW_apiKeyToRGFW(E.xkey.keycode);
+
+ XkbStateRec state;
+ XkbGetState(_RGFW->display, XkbUseCoreKbd, &state);
+ RGFW_keyUpdateKeyMods(win, (state.locked_mods & LockMask), (state.locked_mods & Mod2Mask), (state.locked_mods & Mod3Mask));
+
+ if (win->src.ic && XFilterEvent(&E, None) == False) {
+ char buffer[100];
+ char* chars = buffer;
+
+ Status status;
+ size_t count = (size_t)Xutf8LookupString(win->src.ic, &E.xkey, buffer, sizeof(buffer) - 1, NULL, &status);
+
+ if (status == XBufferOverflow) {
+ chars = (char*)RGFW_ALLOC(count + 1);
+ count = (size_t)Xutf8LookupString(win->src.ic, &E.xkey, chars, (int)count, NULL, &status);
+ }
+
+ if (status == XLookupChars || status == XLookupBoth) {
+ chars[count] = '\0';
+ for (size_t index = 0; index < count;
+ RGFW_keyCharCallback(win, RGFW_decodeUTF8(&chars[index], &index))
+ );
+ }
+
+ if (chars != buffer)
+ RGFW_FREE(chars);
+ } else {
+ Window root = DefaultRootWindow(_RGFW->display);
+ Window ret_root, ret_child;
+ int root_x, root_y, win_x, win_y;
+ unsigned int mask;
+ XQueryPointer(_RGFW->display, root, &ret_root, &ret_child, &root_x, &root_y, &win_x, &win_y, &mask);
+ KeySym sym = (KeySym)XkbKeycodeToKeysym(_RGFW->display, (KeyCode)E.xkey.keycode, 0, (KeyCode)mask & ShiftMask ? 1 : 0);
+
+ if ((mask & LockMask) && sym >= XK_a && sym <= XK_z)
+ sym = (mask & ShiftMask) ? sym + 32 : sym - 32;
+ if ((u8)sym != (u32)sym)
+ sym = 0;
+
+ RGFW_keyCharCallback(win, (u8)sym);
+ }
+
+ RGFW_keyCallback(win, value, win->internal.mod, keyRepeat, RGFW_TRUE);
+ break;
+ }
+ case KeyRelease: {
+ if (!(win->internal.enabledEvents & RGFW_keyReleasedFlag)) return;
+
+ RGFW_key value = (u8)RGFW_apiKeyToRGFW(E.xkey.keycode);
+
+ XkbStateRec state;
+ XkbGetState(_RGFW->display, XkbUseCoreKbd, &state);
+ RGFW_keyUpdateKeyMods(win, (state.locked_mods & LockMask), (state.locked_mods & Mod2Mask), (state.locked_mods & Mod3Mask));
+
+ RGFW_keyCallback(win, value, win->internal.mod, RGFW_FALSE, RGFW_FALSE);
+ break;
+ }
+ case ButtonPress: {
+ RGFW_bool scroll = RGFW_FALSE;
+ if (E.xbutton.button >= Button4 && E.xbutton.button <= 7) {
+ scroll = RGFW_TRUE;
+ }
+
+ float scrollX = 0.0f;
+ float scrollY = 0.0f;
+ RGFW_mouseButton value = 0;
+
+ switch (E.xbutton.button) {
+ case Button1: value = RGFW_mouseLeft; break;
+ case Button2: value = RGFW_mouseMiddle; break;
+ case Button3: value = RGFW_mouseRight; break;
+ case Button4: scrollY = 1.0; break;
+ case Button5: scrollY = -1.0; break;
+ case 6: scrollX = 1.0f; break;
+ case 7: scrollX = -1.0f; break;
+ default:
+ value = (u8)E.xbutton.button - Button1 - 4;
+ break;
+ }
+
+ if (scroll) {
+ RGFW_mouseScrollCallback(win, scrollX, scrollY);
+ break;
+ }
+
+ RGFW_mouseButtonCallback(win, value, RGFW_TRUE);
+ break;
+ }
+ case ButtonRelease: {
+ if (E.xbutton.button >= Button4 && E.xbutton.button <= 7) break;
+
+ RGFW_mouseButton value = 0;
+ switch(E.xbutton.button) {
+ case Button1: value = RGFW_mouseLeft; break;
+ case Button2: value = RGFW_mouseMiddle; break;
+ case Button3: value = RGFW_mouseRight; break;
+ default:
+ value = (u8)E.xbutton.button - Button1 - 4;
+ break;
+ }
+
+ RGFW_mouseButtonCallback(win, value, RGFW_FALSE);
+ break;
+ }
+ case MotionNotify:
+ RGFW_mouseMotionCallback(win, E.xmotion.x, E.xmotion.y);
+ break;
+
+ case Expose: {
+ RGFW_windowRefreshCallback(win, E.xexpose.x, E.xexpose.y, E.xexpose.width, E.xexpose.height);
+
+#ifdef RGFW_ADVANCED_SMOOTH_RESIZE
+ XSyncValue value;
+ XSyncIntToValue(&value, (i32)win->src.counter_value);
+ XSyncSetCounter(_RGFW->display, win->src.counter, value);
+#endif
+ break;
+ }
+
+ case PropertyNotify:
+ if (E.xproperty.state != PropertyNewValue) break;
+
+ if (E.xproperty.atom == WM_STATE) {
+ if (RGFW_window_isMinimized(win) && !(win->internal.flags & RGFW_windowMinimized)) {
+ RGFW_windowMinimizedCallback(win);
+ break;
+ }
+ } else if (E.xproperty.atom == _NET_WM_STATE) {
+ if (RGFW_window_isMaximized(win) && !(win->internal.flags & RGFW_windowMaximize)) {
+ RGFW_windowMaximizedCallback(win, win->x, win->y, win->w, win->h);
+ break;
+ }
+ }
+
+ RGFW_window_checkMode(win);
+ break;
+ case MapNotify: case UnmapNotify: RGFW_window_checkMode(win); break;
+ case ClientMessage: {
+ RGFW_LOAD_ATOM(WM_DELETE_WINDOW);
+ /* if the client closed the window */
+ if (E.xclient.data.l[0] == (long)WM_DELETE_WINDOW) {
+ RGFW_windowCloseCallback(win);
+ break;
+ }
+#ifdef RGFW_ADVANCED_SMOOTH_RESIZE
+ if (E.xclient.message_type == WM_PROTOCOLS && (Atom)E.xclient.data.l[0] == _NET_WM_SYNC_REQUEST) {
+ RGFW_windowRefreshCallback(win, 0, 0, win->w, win->h);
+ win->src.counter_value = 0;
+ win->src.counter_value |= E.xclient.data.l[2];
+ win->src.counter_value |= (E.xclient.data.l[3] << 32);
+
+ XSyncValue value;
+ XSyncIntToValue(&value, (i32)win->src.counter_value);
+ XSyncSetCounter(_RGFW->display, win->src.counter, value);
+ break;
+ }
+#endif
+ if ((win->internal.flags & RGFW_windowAllowDND) == 0 || _RGFW->x11Version > RGFW_XDND_VERSION) {
+ return;
+ }
+
+ i32 dragX = 0;
+ i32 dragY = 0;
+
+ if (E.xclient.message_type == XdndEnter) {
+ unsigned long count;
+ Atom* formats;
+ Atom real_formats[3];
+ Bool list = E.xclient.data.l[1] & 1;
+
+ _RGFW->x11Source = (Window)E.xclient.data.l[0];
+ _RGFW->x11Version = E.xclient.data.l[1] >> 24;
+ _RGFW->x11Format = None;
+ if (list) {
+ Atom actualType;
+ i32 actualFormat;
+ unsigned long bytesAfter;
+
+ XGetWindowProperty(
+ _RGFW->display, _RGFW->x11Source, XdndTypeList,
+ 0, LONG_MAX, False, 4,
+ &actualType, &actualFormat, &count, &bytesAfter, (u8**)&formats
+ );
+ } else {
+ count = 0;
+
+ size_t i;
+ for (i = 2; i < (2 + 3); i++) {
+ if (E.xclient.data.l[i] != None) {
+ real_formats[count] = (unsigned long int)E.xclient.data.l[i];
+ count += 1;
+ }
+ }
+
+ formats = real_formats;
+ }
+
+ Atom XtextPlain = XInternAtom(_RGFW->display, "text/plain", False);
+ Atom XtextUriList = XInternAtom(_RGFW->display, "text/uri-list", False);
+
+ size_t i;
+ for (i = 0; i < count; i++) {
+ if (formats[i] == XtextUriList) _RGFW->x11TransferType = RGFW_dataFile;
+ else if (formats[i] == XtextPlain) _RGFW->x11TransferType = RGFW_dataText;
+ else continue;
+
+ _RGFW->x11Format = (int)formats[i];
+ break;
+ }
+
+ if (list && formats) {
+ XFree(formats);
+ }
+
+
+ RGFW_dataDragCallback(win, _RGFW->x11TransferType , RGFW_dndActionEnter, dragX, dragY);
+ } else if (E.xclient.message_type == XdndPosition) {
+ const i32 xabs = (E.xclient.data.l[2] >> 16) & 0xffff;
+ const i32 yabs = (E.xclient.data.l[2]) & 0xffff;
+ Window dummy;
+ i32 xpos, ypos;
+
+ XTranslateCoordinates(
+ _RGFW->display, XDefaultRootWindow(_RGFW->display), win->src.window,
+ xabs, yabs, &xpos, &ypos, &dummy
+ );
+
+ dragX = xpos;
+ dragY = ypos;
+
+ RGFW_mouseMotionCallback(win, xpos, ypos);
+ XEvent reply = { ClientMessage };
+ reply.xclient.window = _RGFW->x11Source;
+ reply.xclient.message_type = XdndStatus;
+ reply.xclient.format = 32;
+ reply.xclient.data.l[0] = (long)win->src.window;
+ reply.xclient.data.l[2] = 0;
+ reply.xclient.data.l[3] = 0;
+
+ if (_RGFW->x11Format) {
+ reply.xclient.data.l[1] = 1;
+ if (_RGFW->x11Version >= 2)
+ reply.xclient.data.l[4] = (long)XdndActionCopy;
+ }
+
+ XSendEvent(_RGFW->display, _RGFW->x11Source, False, NoEventMask, &reply);
+ XFlush(_RGFW->display);
+
+
+ RGFW_dataDragCallback(win, _RGFW->x11TransferType, RGFW_dndActionMove, dragX, dragY);
+ } else if (E.xclient.message_type == XdndLeave) {
+ RGFW_dataDragCallback(win, _RGFW->x11TransferType, RGFW_dndActionExit, dragX, dragY);
+ } else if (E.xclient.message_type == XdndDrop) {
+ if (_RGFW->x11Format) {
+ Time time = (_RGFW->x11Version >= 1)
+ ? (Time)E.xclient.data.l[2]
+ : CurrentTime;
+ XConvertSelection(
+ _RGFW->display, XdndSelection, (Atom)_RGFW->x11Format,
+ XdndSelection, win->src.window, time
+ );
+ } else if (_RGFW->x11Version >= 2) {
+ XEvent reply = { ClientMessage };
+ reply.xclient.window = _RGFW->x11Source;
+ reply.xclient.message_type = XdndFinished;
+ reply.xclient.format = 32;
+ reply.xclient.data.l[0] = (long)win->src.window;
+ reply.xclient.data.l[1] = 0;
+ reply.xclient.data.l[2] = None;
+
+ XSendEvent(_RGFW->display, _RGFW->x11Source, False, NoEventMask, &reply);
+ XFlush(_RGFW->display);
+ }
+ }
+ } break;
+ case SelectionNotify: {
+ /* this is only for checking for xdnd drops */
+ if (!(win->internal.enabledEvents & RGFW_dataDropFlag) || E.xselection.property != XdndSelection || !(win->internal.flags & RGFW_windowAllowDND))
+ return;
+ char* data;
+ unsigned long result;
+
+ Atom actualType;
+ i32 actualFormat;
+ unsigned long bytesAfter;
+
+ XGetWindowProperty(_RGFW->display, E.xselection.requestor, E.xselection.property, 0, LONG_MAX, False, E.xselection.target, &actualType, &actualFormat, &result, &bytesAfter, (u8**) &data);
+
+ if (result != 0) {
+ RGFW_unix_parseURI(win, data);
+
+ if (data)
+ XFree(data);
+ }
+
+ if (_RGFW->x11Version >= 2) {
+ XEvent reply = { ClientMessage };
+ reply.xclient.window = _RGFW->x11Source;
+ reply.xclient.message_type = XdndFinished;
+ reply.xclient.format = 32;
+ reply.xclient.data.l[0] = (long)win->src.window;
+ reply.xclient.data.l[1] = (long int)result;
+ reply.xclient.data.l[2] = (long int)XdndActionCopy;
+ XSendEvent(_RGFW->display, _RGFW->x11Source, False, NoEventMask, &reply);
+ XFlush(_RGFW->display);
+ }
+ break;
+ }
+ case FocusIn:
+ if (win->src.ic) XSetICFocus(win->src.ic);
+ RGFW_windowFocusCallback(win, 1);
+ break;
+ case FocusOut:
+ if (win->src.ic) XUnsetICFocus(win->src.ic);
+ RGFW_windowFocusCallback(win, 0);
+ break;
+ case EnterNotify: {
+ RGFW_mouseNotifyCallback(win, E.xcrossing.x, E.xcrossing.y, RGFW_TRUE);
+ break;
+ }
+
+ case LeaveNotify: {
+ RGFW_mouseNotifyCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, RGFW_FALSE);
+ break;
+ }
+ case ReparentNotify:
+ win->src.parent = E.xreparent.parent;
+ break;
+ case ConfigureNotify: {
+ /* detect resize */
+ if (E.xconfigure.width != win->src.w || E.xconfigure.height != win->src.h) {
+ RGFW_window_checkMode(win);
+ win->src.w = E.xconfigure.width;
+ win->src.h = E.xconfigure.height;
+ RGFW_windowResizedCallback(win, E.xconfigure.width, E.xconfigure.height);
+ }
+
+ i32 x = E.xconfigure.x;
+ i32 y = E.xconfigure.y;
+
+ /*
+ if the event came from the server and we're not a direct child of the root window then
+ we're using local coords which need to be translated into screen coords
+ */
+ Window root = DefaultRootWindow(_RGFW->display);
+ if (E.xany.send_event == 0 && win->src.parent != root) {
+ Window dummy = 0;
+ XTranslateCoordinates(_RGFW->display, win->src.parent, root, x, y, &x, &y, &dummy);
+ }
+
+ /* detect move */
+ if (E.xconfigure.x != win->src.x || E.xconfigure.y != win->src.y) {
+ win->src.x = E.xconfigure.x;
+ win->src.y = E.xconfigure.y;
+ RGFW_windowMovedCallback(win, E.xconfigure.x, E.xconfigure.y);
+ }
+ return;
+ }
+ default:
+ break;
+ }
+
+ XFlush(_RGFW->display);
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_fetchSize) (RGFW_window* win, i32* w, i32* h) {
+ XWindowAttributes attribs;
+ XGetWindowAttributes(_RGFW->display, win->src.window, &attribs);
+
+ win->w = attribs.width;
+ win->h = attribs.height;
+
+ return RGFW_window_getSize(win, w, h);
+}
+
+void RGFW_FUNC(RGFW_pollEvents) (void) {
+ RGFW_resetPrevState();
+
+ XPending(_RGFW->display);
+ /* if there is no unread queued events, get a new one */
+ while (QLength(_RGFW->display)) {
+ RGFW_XHandleEvent();
+ }
+}
+
+void RGFW_FUNC(RGFW_window_move) (RGFW_window* win, i32 x, i32 y) {
+ RGFW_ASSERT(win != NULL);
+ win->x = x;
+ win->y = y;
+
+ XMoveWindow(_RGFW->display, win->src.window, x, y);
+ return;
+}
+
+
+void RGFW_FUNC(RGFW_window_resize) (RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+ win->w = (i32)w;
+ win->h = (i32)h;
+
+ XResizeWindow(_RGFW->display, win->src.window, (u32)w, (u32)h);
+
+ if ((win->internal.flags & RGFW_windowNoResize)) {
+ XSizeHints sh;
+ sh.flags = (1L << 4) | (1L << 5);
+ sh.min_width = sh.max_width = (i32)w;
+ sh.min_height = sh.max_height = (i32)h;
+
+ XSetWMSizeHints(_RGFW->display, (Drawable) win->src.window, &sh, XA_WM_NORMAL_HINTS);
+ }
+ return;
+}
+
+void RGFW_FUNC(RGFW_window_setAspectRatio) (RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+
+
+ if (w == 0 && h == 0)
+ return;
+ XSizeHints hints;
+ long flags;
+
+ XGetWMNormalHints(_RGFW->display, win->src.window, &hints, &flags);
+
+ hints.flags |= PAspect;
+
+ hints.min_aspect.x = hints.max_aspect.x = (i32)w;
+ hints.min_aspect.y = hints.max_aspect.y = (i32)h;
+
+ XSetWMNormalHints(_RGFW->display, win->src.window, &hints);
+ return;
+}
+
+void RGFW_FUNC(RGFW_window_setMinSize) (RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+
+ long flags;
+ XSizeHints hints;
+ RGFW_MEMZERO(&hints, sizeof(XSizeHints));
+
+ XGetWMNormalHints(_RGFW->display, win->src.window, &hints, &flags);
+
+ hints.flags |= PMinSize;
+
+ hints.min_width = (i32)w;
+ hints.min_height = (i32)h;
+
+ XSetWMNormalHints(_RGFW->display, win->src.window, &hints);
+ return;
+}
+
+void RGFW_FUNC(RGFW_window_setMaxSize) (RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+
+ long flags;
+ XSizeHints hints;
+ RGFW_MEMZERO(&hints, sizeof(XSizeHints));
+
+ XGetWMNormalHints(_RGFW->display, win->src.window, &hints, &flags);
+
+ hints.flags |= PMaxSize;
+
+ hints.max_width = (i32)w;
+ hints.max_height = (i32)h;
+
+ XSetWMNormalHints(_RGFW->display, win->src.window, &hints);
+ return;
+}
+
+void RGFW_toggleXMaximized(RGFW_window* win, RGFW_bool maximized);
+void RGFW_toggleXMaximized(RGFW_window* win, RGFW_bool maximized) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_LOAD_ATOM(_NET_WM_STATE);
+ RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
+ RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
+
+ XEvent xev = {0};
+ xev.type = ClientMessage;
+ xev.xclient.window = win->src.window;
+ xev.xclient.message_type = _NET_WM_STATE;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = maximized;
+ xev.xclient.data.l[1] = (long int)_NET_WM_STATE_MAXIMIZED_HORZ;
+ xev.xclient.data.l[2] = (long int)_NET_WM_STATE_MAXIMIZED_VERT;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = 0;
+
+ XSendEvent(_RGFW->display, DefaultRootWindow(_RGFW->display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+}
+
+void RGFW_FUNC(RGFW_window_maximize) (RGFW_window* win) {
+ win->internal.oldX = win->x;
+ win->internal.oldY = win->y;
+ win->internal.oldW = win->w;
+ win->internal.oldH = win->h;
+
+ RGFW_toggleXMaximized(win, 1);
+ RGFW_window_fetchSize(win, NULL, NULL);
+ return;
+}
+
+void RGFW_FUNC(RGFW_window_focus) (RGFW_window* win) {
+ RGFW_ASSERT(win);
+
+ XWindowAttributes attr;
+ XGetWindowAttributes(_RGFW->display, win->src.window, &attr);
+ if (attr.map_state != IsViewable) return;
+
+ XSetInputFocus(_RGFW->display, win->src.window, RevertToPointerRoot, CurrentTime);
+ XFlush(_RGFW->display);
+}
+
+void RGFW_FUNC(RGFW_window_raise) (RGFW_window* win) {
+ RGFW_ASSERT(win);
+ XMapRaised(_RGFW->display, win->src.window);
+ RGFW_window_setFullscreen(win, RGFW_window_isFullscreen(win));
+}
+
+void RGFW_window_setXAtom(RGFW_window* win, Atom netAtom, RGFW_bool fullscreen);
+void RGFW_window_setXAtom(RGFW_window* win, Atom netAtom, RGFW_bool fullscreen) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_LOAD_ATOM(_NET_WM_STATE);
+
+ XEvent xev = {0};
+ xev.xclient.type = ClientMessage;
+ xev.xclient.serial = 0;
+ xev.xclient.send_event = True;
+ xev.xclient.message_type = _NET_WM_STATE;
+ xev.xclient.window = win->src.window;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = fullscreen;
+ xev.xclient.data.l[1] = (long int)netAtom;
+ xev.xclient.data.l[2] = 0;
+
+ XSendEvent(_RGFW->display, DefaultRootWindow(_RGFW->display), False, SubstructureNotifyMask | SubstructureRedirectMask, &xev);
+}
+
+void RGFW_FUNC(RGFW_window_setFullscreen)(RGFW_window* win, RGFW_bool fullscreen) {
+ RGFW_ASSERT(win != NULL);
+
+ if (fullscreen) {
+ win->internal.flags |= RGFW_windowFullscreen;
+ win->internal.oldX = win->x;
+ win->internal.oldY = win->y;
+ win->internal.oldW = win->w;
+ win->internal.oldH = win->h;
+ }
+ else win->internal.flags &= ~(u32)RGFW_windowFullscreen;
+
+ XRaiseWindow(_RGFW->display, win->src.window);
+
+ RGFW_LOAD_ATOM(_NET_WM_STATE_FULLSCREEN);
+ RGFW_window_setXAtom(win, _NET_WM_STATE_FULLSCREEN, fullscreen);
+
+ if (!(win->internal.flags & RGFW_windowTransparent)) {
+ const unsigned char value = fullscreen;
+ RGFW_LOAD_ATOM(_NET_WM_BYPASS_COMPOSITOR);
+ XChangeProperty(
+ _RGFW->display, win->src.window,
+ _NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32,
+ PropModeReplace, &value, 1);
+ }
+}
+
+void RGFW_FUNC(RGFW_window_setFloating)(RGFW_window* win, RGFW_bool floating) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_LOAD_ATOM(_NET_WM_STATE_ABOVE);
+ RGFW_window_setXAtom(win, _NET_WM_STATE_ABOVE, floating);
+}
+
+void RGFW_FUNC(RGFW_window_setOpacity)(RGFW_window* win, u8 opacity) {
+ RGFW_ASSERT(win != NULL);
+ const u32 value = (u32) (0xffffffffu * (double) opacity);
+ RGFW_LOAD_ATOM(NET_WM_WINDOW_OPACITY);
+ XChangeProperty(_RGFW->display, win->src.window,
+ NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &value, 1);
+}
+
+void RGFW_FUNC(RGFW_window_minimize)(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+
+ if (RGFW_window_isMaximized(win)) return;
+
+ win->internal.oldX = win->x;
+ win->internal.oldY = win->y;
+ win->internal.oldW = win->w;
+ win->internal.oldH = win->h;
+ XIconifyWindow(_RGFW->display, win->src.window, DefaultScreen(_RGFW->display));
+ XFlush(_RGFW->display);
+}
+
+void RGFW_FUNC(RGFW_window_restore)(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_toggleXMaximized(win, RGFW_FALSE);
+ RGFW_window_move(win, win->internal.oldX, win->internal.oldY);
+ RGFW_window_resize(win, win->internal.oldW, win->internal.oldH);
+
+ RGFW_window_show(win);
+ XFlush(_RGFW->display);
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_isFloating)(RGFW_window* win) {
+ RGFW_LOAD_ATOM(_NET_WM_STATE);
+ RGFW_LOAD_ATOM(_NET_WM_STATE_ABOVE);
+
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems, bytes_after;
+ Atom* prop_return = NULL;
+
+ int status = XGetWindowProperty(_RGFW->display, win->src.window, _NET_WM_STATE, 0, (~0L), False, XA_ATOM,
+ &actual_type, &actual_format, &nitems, &bytes_after,
+ (unsigned char **)&prop_return);
+
+ if (status != Success || actual_type != XA_ATOM)
+ return RGFW_FALSE;
+
+ unsigned long i;
+ for (i = 0; i < nitems; i++)
+ if (prop_return[i] == _NET_WM_STATE_ABOVE) return RGFW_TRUE;
+
+ if (prop_return)
+ XFree(prop_return);
+ return RGFW_FALSE;
+}
+
+void RGFW_FUNC(RGFW_window_setName)(RGFW_window* win, const char* name) {
+ RGFW_ASSERT(win != NULL);
+ if (name == NULL) name = "\0";
+
+ Xutf8SetWMProperties(_RGFW->display, win->src.window, name, name, NULL, 0, NULL, NULL, NULL);
+ XStoreName(_RGFW->display, win->src.window, name);
+
+ RGFW_LOAD_ATOM(_NET_WM_NAME); RGFW_LOAD_ATOM(UTF8_STRING);
+
+ XChangeProperty(
+ _RGFW->display, win->src.window, _NET_WM_NAME, UTF8_STRING,
+ 8, PropModeReplace, (u8*)name, (int)RGFW_unix_stringlen(name)
+ );
+}
+
+#ifndef RGFW_NO_PASSTHROUGH
+void RGFW_FUNC(RGFW_window_setMousePassthrough) (RGFW_window* win, RGFW_bool passthrough) {
+ RGFW_ASSERT(win != NULL);
+ if (passthrough) {
+ Region region = XCreateRegion();
+ XShapeCombineRegion(_RGFW->display, win->src.window, ShapeInput, 0, 0, region, ShapeSet);
+ XDestroyRegion(region);
+
+ return;
+ }
+
+ XShapeCombineMask(_RGFW->display, win->src.window, ShapeInput, 0, 0, None, ShapeSet);
+}
+#endif /* RGFW_NO_PASSTHROUGH */
+
+RGFW_bool RGFW_FUNC(RGFW_window_setIconEx) (RGFW_window* win, u8* data_src, i32 w, i32 h, RGFW_format format, RGFW_icon type) {
+ Atom _NET_WM_ICON = XInternAtom(_RGFW->display, "_NET_WM_ICON", False);
+ RGFW_ASSERT(win != NULL);
+ if (data_src == NULL) {
+ RGFW_bool res = (RGFW_bool)XChangeProperty(
+ _RGFW->display, win->src.window, _NET_WM_ICON, XA_CARDINAL, 32,
+ PropModeReplace, (u8*)NULL, 0
+ );
+ return res;
+ }
+
+ i32 count = (i32)(2 + (w * h));
+
+ unsigned long* data = (unsigned long*) RGFW_ALLOC((u32)count * sizeof(unsigned long));
+ RGFW_ASSERT(data != NULL);
+
+ RGFW_MEMZERO(data, (u32)count * sizeof(unsigned long));
+ data[0] = (unsigned long)w;
+ data[1] = (unsigned long)h;
+
+ RGFW_copyImageData64((u8*)&data[2], w, h, RGFW_formatBGRA8, data_src, format, RGFW_TRUE, NULL);
+ RGFW_bool res = RGFW_TRUE;
+ if (type & RGFW_iconTaskbar) {
+ res = (RGFW_bool)XChangeProperty(
+ _RGFW->display, win->src.window, _NET_WM_ICON, XA_CARDINAL, 32,
+ PropModeReplace, (u8*)data, count
+ );
+ }
+
+ RGFW_copyImageData64((u8*)&data[2], w, h, RGFW_formatBGRA8, data_src, format, RGFW_FALSE, NULL);
+
+ if (type & RGFW_iconWindow) {
+ XWMHints wm_hints;
+ wm_hints.flags = IconPixmapHint;
+
+ i32 depth = DefaultDepth(_RGFW->display, DefaultScreen(_RGFW->display));
+ XImage *image = XCreateImage(_RGFW->display, DefaultVisual(_RGFW->display, DefaultScreen(_RGFW->display)),
+ (u32)depth, ZPixmap, 0, (char *)&data[2], (u32)w, (u32)h, 32, 0);
+
+ wm_hints.icon_pixmap = XCreatePixmap(_RGFW->display, win->src.window, (u32)w, (u32)h, (u32)depth);
+ XPutImage(_RGFW->display, wm_hints.icon_pixmap, DefaultGC(_RGFW->display, DefaultScreen(_RGFW->display)), image, 0, 0, 0, 0, (u32)w, (u32)h);
+ image->data = NULL;
+ XDestroyImage(image);
+
+ XSetWMHints(_RGFW->display, win->src.window, &wm_hints);
+ }
+
+ RGFW_FREE(data);
+ XFlush(_RGFW->display);
+ return RGFW_BOOL(res);
+}
+
+RGFW_mouse* RGFW_FUNC(RGFW_createMouseStandard) (RGFW_mouseIcon mouse) {
+ u32 mouseIcon = 0;
+
+ switch (mouse) {
+ case RGFW_mouseNormal: mouseIcon = XC_left_ptr; break;
+ case RGFW_mouseArrow: mouseIcon = XC_left_ptr; break;
+ case RGFW_mouseIbeam: mouseIcon = XC_xterm; break;
+ case RGFW_mouseWait: mouseIcon = XC_watch; break;
+ case RGFW_mouseCrosshair: mouseIcon = XC_tcross; break;
+ case RGFW_mouseProgress: mouseIcon = XC_watch; break;
+ case RGFW_mouseResizeNWSE: mouseIcon = XC_top_left_corner; break;
+ case RGFW_mouseResizeNESW: mouseIcon = XC_top_right_corner; break;
+ case RGFW_mouseResizeEW: mouseIcon = XC_sb_h_double_arrow; break;
+ case RGFW_mouseResizeNS: mouseIcon = XC_sb_v_double_arrow; break;
+ case RGFW_mouseResizeNW: mouseIcon = XC_top_left_corner; break;
+ case RGFW_mouseResizeN: mouseIcon = XC_top_side; break;
+ case RGFW_mouseResizeNE: mouseIcon = XC_top_right_corner; break;
+ case RGFW_mouseResizeE: mouseIcon = XC_right_side; break;
+ case RGFW_mouseResizeSE: mouseIcon = XC_bottom_right_corner; break;
+ case RGFW_mouseResizeS: mouseIcon = XC_bottom_side; break;
+ case RGFW_mouseResizeSW: mouseIcon = XC_bottom_left_corner; break;
+ case RGFW_mouseResizeW: mouseIcon = XC_left_side; break;
+ case RGFW_mouseResizeAll: mouseIcon = XC_fleur; break;
+ case RGFW_mouseNotAllowed: mouseIcon = XC_pirate; break;
+ case RGFW_mousePointingHand: mouseIcon = XC_hand2; break;
+ default: return NULL;
+ }
+
+ Cursor cursor = XCreateFontCursor(_RGFW->display, mouseIcon);
+ return (RGFW_mouse*)cursor;
+}
+
+RGFW_mouse* RGFW_FUNC(RGFW_createMouse) (u8* data, i32 w, i32 h, RGFW_format format) {
+ RGFW_ASSERT(data);
+#ifndef RGFW_NO_X11_CURSOR
+ RGFW_init();
+ XcursorImage* native = XcursorImageCreate((i32)w, (i32)h);
+ native->xhot = 0;
+ native->yhot = 0;
+ RGFW_MEMZERO(native->pixels, (u32)(w * h * 4));
+ RGFW_copyImageData((u8*)native->pixels, w, h, RGFW_formatBGRA8, data, format, NULL);
+
+ Cursor cursor = XcursorImageLoadCursor(_RGFW->display, native);
+ XcursorImageDestroy(native);
+
+ return (void*)cursor;
+#else
+ RGFW_UNUSED(data); RGFW_UNUSED(w); RGFW_UNUSED(h); RGFW_UNUSED(format);
+ return NULL;
+#endif
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_setMousePlatform)(RGFW_window* win, RGFW_mouse* mouse) {
+ RGFW_ASSERT(win && mouse);
+ XDefineCursor(_RGFW->display, win->src.window, (Cursor)mouse);
+ return RGFW_TRUE;
+}
+
+void RGFW_FUNC(RGFW_freeMouse)(RGFW_mouse* mouse) {
+ RGFW_ASSERT(mouse);
+ XFreeCursor(_RGFW->display, (Cursor)mouse);
+}
+
+void RGFW_FUNC(RGFW_window_moveMouse)(RGFW_window* win, i32 x, i32 y) {
+ RGFW_ASSERT(win != NULL);
+
+ XEvent event;
+ XQueryPointer(_RGFW->display, DefaultRootWindow(_RGFW->display),
+ &event.xbutton.root, &event.xbutton.window,
+ &event.xbutton.x_root, &event.xbutton.y_root,
+ &event.xbutton.x, &event.xbutton.y,
+ &event.xbutton.state);
+
+ win->internal.lastMouseX = x - win->x;
+ win->internal.lastMouseY = y - win->y;
+ if (event.xbutton.x == x && event.xbutton.y == y)
+ return;
+
+ XWarpPointer(_RGFW->display, None, win->src.window, 0, 0, 0, 0, (int) x - win->x, (int) y - win->y);
+}
+
+void RGFW_FUNC(RGFW_window_hide)(RGFW_window* win) {
+ win->internal.flags |= (u32)RGFW_windowHide;
+ XUnmapWindow(_RGFW->display, win->src.window);
+
+ XFlush(_RGFW->display);
+}
+
+void RGFW_FUNC(RGFW_window_show) (RGFW_window* win) {
+ win->internal.flags &= ~(u32)RGFW_windowHide;
+ if (win->internal.flags & RGFW_windowFocusOnShow) RGFW_window_focus(win);
+
+ if (RGFW_window_isHidden(win) == RGFW_FALSE) {
+ return;
+ }
+
+ XMapWindow(_RGFW->display, win->src.window);
+ RGFW_window_move(win, win->x, win->y);
+
+ RGFW_waitForShowEvent_X11(win);
+ RGFW_window_setFullscreen(win, RGFW_window_isFullscreen(win));
+ return;
+}
+
+void RGFW_FUNC(RGFW_window_flash) (RGFW_window* win, RGFW_flashRequest request) {
+ if (RGFW_window_isInFocus(win) && request) {
+ return;
+ }
+
+ XWMHints* wmhints = XGetWMHints(_RGFW->display, win->src.window);
+ if (wmhints == NULL) return;
+
+ if (request) {
+ wmhints->flags |= XUrgencyHint;
+ if (request == RGFW_flashBriefly)
+ win->src.flashEnd = RGFW_unix_getTimeNS() + (u64)1e+9;
+ if (request == RGFW_flashUntilFocused)
+ win->src.flashEnd = (u64)-1;
+ } else {
+ win->src.flashEnd = 0;
+ wmhints->flags &= ~XUrgencyHint;
+ }
+
+ XSetWMHints(_RGFW->display, win->src.window, wmhints);
+ XFree(wmhints);
+}
+
+RGFW_bool RGFW_FUNC(RGFW_readClipboardPtr) (u8* buffer, size_t capacity, RGFW_dataTransfer* dataTransfer) {
+ RGFW_ASSERT(dataTransfer != NULL);
+ dataTransfer->data = (char*)buffer;
+
+ RGFW_init();
+ RGFW_LOAD_ATOM(XSEL_DATA); RGFW_LOAD_ATOM(UTF8_STRING); RGFW_LOAD_ATOM(CLIPBOARD);
+
+ if (XGetSelectionOwner(_RGFW->display, CLIPBOARD) == _RGFW->helperWindow) {
+ dataTransfer->length = _RGFW->unixClipboard->length;
+ if (buffer != NULL && _RGFW->unixClipboard->data != NULL) {
+ if (_RGFW->unixClipboard->length > capacity) return RGFW_FALSE;
+
+ RGFW_MEMCPY((char*)buffer, _RGFW->unixClipboard->data, _RGFW->unixClipboard->length);
+ }
+
+ dataTransfer->type = RGFW_dataText;
+ return RGFW_TRUE;
+ }
+
+ XEvent event;
+ int format;
+ unsigned long N, size;
+ char* data;
+ Atom target;
+
+ XConvertSelection(_RGFW->display, CLIPBOARD, UTF8_STRING, XSEL_DATA, _RGFW->helperWindow, CurrentTime);
+ XSync(_RGFW->display, 0);
+ while (1) {
+ XNextEvent(_RGFW->display, &event);
+ if (event.type != SelectionNotify) continue;
+
+ if (event.xselection.selection != CLIPBOARD || event.xselection.property == 0)
+ return RGFW_FALSE;
+ break;
+ }
+
+ XGetWindowProperty(event.xselection.display, event.xselection.requestor,
+ event.xselection.property, 0L, (~0L), 0, AnyPropertyType, &target,
+ &format, &size, &N, (u8**) &data);
+
+ RGFW_bool ret = RGFW_TRUE;
+
+ size_t length = size;
+ if (data[size - 1] != '\0') length += 1;
+
+ if (size > capacity && buffer != NULL)
+ ret = RGFW_FALSE;
+ else if ((target == UTF8_STRING || target == XA_STRING) && buffer != NULL) {
+ RGFW_MEMCPY(buffer, data, size);
+ buffer[length - 1] = '\0';
+
+ XFree(data);
+ } else if (buffer != NULL) ret = RGFW_FALSE;
+
+ XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property);
+
+ dataTransfer->length = length;
+ dataTransfer->type = RGFW_dataText;
+ return ret;
+}
+
+i32 RGFW_XHandleClipboardSelectionHelper(void) {
+ RGFW_LOAD_ATOM(SAVE_TARGETS);
+
+ XEvent event;
+ XPending(_RGFW->display);
+
+ if (QLength(_RGFW->display) || XEventsQueued(_RGFW->display, QueuedAlready) + XEventsQueued(_RGFW->display, QueuedAfterReading))
+ XNextEvent(_RGFW->display, &event);
+ else
+ return 0;
+
+ switch (event.type) {
+ case SelectionRequest:
+ RGFW_XHandleClipboardSelection(&event);
+ return 0;
+ case SelectionNotify:
+ if (event.xselection.target == SAVE_TARGETS)
+ return 0;
+ break;
+ default: break;
+ }
+
+ return 0;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_writeClipboard)(const RGFW_dataTransfer* data) {
+ RGFW_LOAD_ATOM(SAVE_TARGETS); RGFW_LOAD_ATOM(CLIPBOARD);
+ RGFW_init();
+
+ /* request ownership of the clipboard section and request to convert it, this means its our job to convert it */
+ XSetSelectionOwner(_RGFW->display, CLIPBOARD, _RGFW->helperWindow, CurrentTime);
+ if (XGetSelectionOwner(_RGFW->display, CLIPBOARD) != _RGFW->helperWindow) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errClipboard, "X11 failed to become owner of clipboard selection");
+ return RGFW_FALSE;
+ }
+
+ if (_RGFW->unixClipboard) {
+ RGFW_FREE(_RGFW->unixClipboard);
+ _RGFW->unixClipboard = NULL;
+ }
+
+ size_t length = data->length;
+ if (data->data[data->length - 1] != '\0') {
+ length += 1;
+ }
+
+ _RGFW->unixClipboard = (RGFW_dataTransfer*)RGFW_ALLOC(sizeof(RGFW_dataTransfer) + data->length);
+ RGFW_ASSERT(_RGFW->unixClipboard != NULL);
+
+ char* data_ptr = &((char*)(void*)_RGFW->unixClipboard)[sizeof(RGFW_dataTransfer) - 1];
+ RGFW_MEMCPY(data_ptr, data->data, data->length);
+ data_ptr[length - 1] = '\0';
+
+ _RGFW->unixClipboard->data = (const char*)data_ptr;
+ _RGFW->unixClipboard->type = RGFW_dataText;
+ _RGFW->unixClipboard->length = length;
+ return RGFW_TRUE;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_isHidden)(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ XWindowAttributes windowAttributes;
+ XGetWindowAttributes(_RGFW->display, win->src.window, &windowAttributes);
+
+ return (windowAttributes.map_state != IsViewable);
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_isMinimized)(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_LOAD_ATOM(WM_STATE);
+
+ Atom actual_type;
+ i32 actual_format;
+ unsigned long nitems, bytes_after;
+ unsigned char* prop_data;
+
+ i32 status = XGetWindowProperty(_RGFW->display, win->src.window, WM_STATE, 0, 2, False,
+ AnyPropertyType, &actual_type, &actual_format,
+ &nitems, &bytes_after, &prop_data);
+
+ if (status == Success && nitems >= 1 && prop_data == (unsigned char*)IconicState) {
+ XFree(prop_data);
+ return RGFW_TRUE;
+ }
+
+ if (prop_data != NULL)
+ XFree(prop_data);
+
+ XWindowAttributes windowAttributes;
+ XGetWindowAttributes(_RGFW->display, win->src.window, &windowAttributes);
+ return windowAttributes.map_state != IsViewable;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_isMaximized)(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_LOAD_ATOM(_NET_WM_STATE);
+ RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
+ RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
+
+ Atom actual_type;
+ i32 actual_format;
+ unsigned long nitems, bytes_after;
+ unsigned char* prop_data;
+
+ i32 status = XGetWindowProperty(_RGFW->display, win->src.window, _NET_WM_STATE, 0, 1024, False,
+ XA_ATOM, &actual_type, &actual_format,
+ &nitems, &bytes_after, &prop_data);
+
+ if (status != Success) {
+ if (prop_data != NULL)
+ XFree(prop_data);
+
+ return RGFW_FALSE;
+ }
+
+ u64 i;
+ for (i = 0; i < nitems; i++) {
+ if (prop_data[i] == _NET_WM_STATE_MAXIMIZED_VERT ||
+ prop_data[i] == _NET_WM_STATE_MAXIMIZED_HORZ) {
+ XFree(prop_data);
+ return RGFW_TRUE;
+ }
+ }
+
+ if (prop_data != NULL)
+ XFree(prop_data);
+
+ return RGFW_FALSE;
+}
+
+RGFWDEF void RGFW_XGetSystemContentDPI(float* dpi);
+void RGFW_XGetSystemContentDPI(float* dpi) {
+ if (dpi == NULL) return;
+ float dpiOutput = 96.0f;
+
+ char* rms = XResourceManagerString(_RGFW->display);
+ if (rms == NULL) return;
+
+ XrmDatabase db = XrmGetStringDatabase(rms);
+ if (db == NULL) return;
+
+ XrmValue value;
+ char* type = NULL;
+
+ if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value) && type && RGFW_STRNCMP(type, "String", 7) == 0)
+ dpiOutput = (float)RGFW_ATOF(value.addr);
+ XrmDestroyDatabase(db);
+
+ if (dpi) *dpi = dpiOutput;
+}
+
+RGFWDEF XRRModeInfo* RGFW_XGetMode(XRRCrtcInfo* ci, XRRScreenResources* res, RRMode mode, RGFW_monitorMode* foundMode);
+XRRModeInfo* RGFW_XGetMode(XRRCrtcInfo* ci, XRRScreenResources* res, RRMode mode, RGFW_monitorMode* foundMode) {
+ XRRModeInfo* mi = None;
+ for (i32 j = 0; j < res->nmode; j++) {
+ if (res->modes[j].id == mode)
+ mi = &res->modes[j];
+ }
+
+ if (mi == None) return NULL;
+
+ if ((mi->modeFlags & RR_Interlace) != 0) return NULL;
+
+ foundMode->w = (i32)mi->width;
+ foundMode->h = (i32)mi->height;
+ if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) {
+ foundMode->w = (i32)mi->height;
+ foundMode->h = (i32)mi->width;
+ } else {
+ foundMode->w = (i32)mi->width;
+ foundMode->h = (i32)mi->height;
+ }
+
+ RGFW_splitBPP((u32)DefaultDepth(_RGFW->display, DefaultScreen(_RGFW->display)), foundMode);
+
+ foundMode->src = (void*)mode;
+
+ foundMode->refreshRate = 0;
+ if (mi->hTotal == 0 || mi->vTotal == 0)
+ return mi;
+
+ u32 vTotal = mi->vTotal;
+
+ if (mi->modeFlags & RR_DoubleScan) {
+ vTotal *= 2;
+ }
+
+ if (mi->modeFlags & RR_Interlace) {
+ vTotal /= 2;
+ }
+
+ i32 numerator = (i32)mi->dotClock;
+ i32 denominator = (i32)(mi->hTotal * vTotal);
+ float refreshRate = 0;
+
+ if (denominator <= 0) {
+ denominator = 1;
+ }
+
+ refreshRate = ((float)numerator / (float)denominator);
+
+ foundMode->refreshRate = RGFW_ROUNDF((refreshRate * 100)) / 100.0f;
+ return mi;
+}
+
+void RGFW_FUNC(RGFW_pollMonitors) (void) {
+ RGFW_init();
+
+ Window root = XDefaultRootWindow(_RGFW->display);
+ XRRScreenResources* res = XRRGetScreenResourcesCurrent(_RGFW->display, root);
+ if (res == 0) {
+ return;
+ }
+
+ RROutput primary = XRRGetOutputPrimary(_RGFW->display, root);
+
+ for (RGFW_monitorNode* node = _RGFW->monitors.list.head; node; node = node->next) {
+ node->disconnected = RGFW_TRUE;
+ }
+
+ for (i32 i = 0; i < res->noutput; i++) {
+ RGFW_monitorNode* node = NULL;
+ for (node = _RGFW->monitors.list.head; node; node = node->next) {
+ if (node->rrOutput == res->outputs[i]) {
+ break;
+ }
+ }
+
+ if (node) {
+ node->disconnected = RGFW_FALSE;
+ if (node->rrOutput == primary) {
+ _RGFW->monitors.primary = node;
+ }
+ continue;
+ }
+
+ RGFW_monitor monitor;
+
+ XRROutputInfo* info = XRRGetOutputInfo(_RGFW->display, res, res->outputs[i]);
+ if (info == NULL) continue;
+ if (info->connection != RR_Connected || info->crtc == None) {
+ XRRFreeOutputInfo(info);
+ continue;
+ }
+
+ XRRCrtcInfo* ci = XRRGetCrtcInfo(_RGFW->display, res, info->crtc);
+
+ if (ci == NULL) {
+ continue;
+ }
+
+ float physW = (float)info->mm_width / 25.4f;
+ float physH = (float)info->mm_height / 25.4f;
+
+ RGFW_STRNCPY(monitor.name, info->name, sizeof(monitor.name) - 1);
+ monitor.name[sizeof(monitor.name) - 1] = '\0';
+
+ if (physW > 0.0f && physH > 0.0f) {
+ monitor.physW = physW;
+ monitor.physH = physH;
+ } else {
+ monitor.physW = (float) ((float)ci->width / 96.f);
+ monitor.physH = (float) ((float)ci->height / 96.f);
+ }
+
+ monitor.x = ci->x;
+ monitor.y = ci->y;
+
+ float dpi = 96.0f;
+ RGFW_XGetSystemContentDPI(&dpi);
+
+ monitor.scaleX = dpi / 96.0f;
+ monitor.scaleY = dpi / 96.0f;
+
+ monitor.pixelRatio = dpi >= 192.0f ? 2.0f : 1.0f;
+
+ XRRModeInfo* mi = RGFW_XGetMode(ci, res, ci->mode, &monitor.mode);
+
+ if (mi == NULL) {
+ break;
+ }
+
+ XRRFreeCrtcInfo(ci);
+
+ node = RGFW_monitors_add(&monitor);
+ if (node == NULL) break;
+
+ node->rrOutput = res->outputs[i];
+ node->crtc = info->crtc;
+
+ if (node->rrOutput == primary) {
+ _RGFW->monitors.primary = node;
+ }
+
+ XRRFreeOutputInfo(info);
+ info = NULL;
+
+ RGFW_monitorCallback(_RGFW->root, &node->mon, RGFW_TRUE);
+ }
+
+ XRRFreeScreenResources(res);
+
+ RGFW_monitors_refresh();
+}
+
+RGFW_bool RGFW_FUNC(RGFW_monitor_getWorkarea) (RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height) {
+ RGFW_LOAD_ATOM(_NET_WORKAREA);
+ RGFW_LOAD_ATOM(_NET_CURRENT_DESKTOP);
+
+ Window root = DefaultRootWindow(_RGFW->display);
+
+ i32 areaX = monitor->x;
+ i32 areaY = monitor->y;
+ i32 areaW = monitor->mode.w;
+ i32 areaH = monitor->mode.h;
+
+ if (_NET_WORKAREA && _NET_CURRENT_DESKTOP) {
+ Atom* extents = NULL;
+ Atom* desktop = NULL;
+
+ Atom actualType = 0;
+ int actualFormat = 0;
+ unsigned long extentCount = 0, bytesAfter = 0;
+ XGetWindowProperty(_RGFW->display, root, _NET_WORKAREA, 0, LONG_MAX, False, XA_CARDINAL, &actualType, &actualFormat, &extentCount, &bytesAfter, (u8**) &extents);
+
+ unsigned long count;
+ XGetWindowProperty(_RGFW->display, root, _NET_CURRENT_DESKTOP, 0, LONG_MAX, False, XA_CARDINAL, &actualType, &actualFormat, &count, &bytesAfter, (u8**) &desktop);
+
+ if (count) {
+ if (extentCount >= 4 && *desktop < extentCount / 4) {
+ i32 globalX = (i32)extents[*desktop * 4 + 0];
+ i32 globalY = (i32)extents[*desktop * 4 + 1];
+ i32 globalW = (i32)extents[*desktop * 4 + 2];
+ i32 globalH = (i32)extents[*desktop * 4 + 3];
+
+ if (areaX < globalX) {
+ areaW -= globalX - areaX;
+ areaX = globalX;
+ }
+
+ if (areaY < globalY) {
+ areaH -= globalY - areaY;
+ areaY = globalY;
+ }
+
+ if (areaX + areaW > globalX + globalW)
+ areaW = globalX - areaX + globalW;
+ if (areaY + areaH > globalY + globalH)
+ areaH = globalY - areaY + globalH;
+ }
+ }
+
+ if (extents)
+ XFree(extents);
+ if (desktop)
+ XFree(desktop);
+ }
+
+ if (x) *x = areaX;
+ if (y) *y = areaY;
+ if (width) *width = areaW;
+ if (height) *height = areaH;
+
+ return RGFW_TRUE;
+}
+
+size_t RGFW_FUNC(RGFW_monitor_getModesPtr) (RGFW_monitor* monitor, RGFW_monitorMode** modes) {
+ size_t count = 0;
+
+ XRRScreenResources* res = XRRGetScreenResourcesCurrent(_RGFW->display, DefaultRootWindow(_RGFW->display));
+ if (res == NULL) return 0;
+
+ XRRCrtcInfo* ci = XRRGetCrtcInfo(_RGFW->display, res, monitor->node->crtc);
+ XRROutputInfo* oi = XRRGetOutputInfo(_RGFW->display, res, monitor->node->rrOutput);
+ count = (size_t)oi->nmode;
+
+ int i;
+ for (i = 0; modes && i < oi->nmode; i++) {
+ XRRModeInfo* mi = RGFW_XGetMode(ci, res, oi->modes[i], &((*modes)[i]));
+ RGFW_UNUSED(mi);
+ }
+
+ XRRFreeOutputInfo(oi);
+ XRRFreeCrtcInfo(ci);
+ XRRFreeScreenResources(res);
+
+ return count;
+}
+
+size_t RGFW_FUNC(RGFW_monitor_getGammaRampPtr) (RGFW_monitor* monitor, RGFW_gammaRamp* ramp) {
+ RGFW_UNUSED(monitor); RGFW_UNUSED(ramp);
+ size_t size = (size_t)XRRGetCrtcGammaSize(_RGFW->display, monitor->node->crtc);
+ XRRCrtcGamma* gamma = XRRGetCrtcGamma(_RGFW->display, monitor->node->crtc);
+
+ if (ramp) {
+ RGFW_MEMCPY(ramp->red, gamma->red, size * sizeof(unsigned short));
+ RGFW_MEMCPY(ramp->green, gamma->green, size * sizeof(unsigned short));
+ RGFW_MEMCPY(ramp->blue, gamma->blue, size * sizeof(unsigned short));
+ }
+
+ XRRFreeGamma(gamma);
+ return size;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_monitor_setGammaRamp) (RGFW_monitor* monitor, RGFW_gammaRamp* ramp) {
+ RGFW_UNUSED(monitor); RGFW_UNUSED(ramp);
+
+ size_t size = (size_t)XRRGetCrtcGammaSize(_RGFW->display, monitor->node->crtc);
+ if (size != ramp->count) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errX11, "X11: Gamma ramp size must match current ramp size");
+ return RGFW_FALSE;
+ }
+
+ XRRCrtcGamma* gamma = XRRAllocGamma((int)ramp->count);
+
+ memcpy(gamma->red, ramp->red, ramp->count * sizeof(unsigned short));
+ memcpy(gamma->green, ramp->green, ramp->count * sizeof(unsigned short));
+ memcpy(gamma->blue, ramp->blue, ramp->count * sizeof(unsigned short));
+
+ XRRSetCrtcGamma(_RGFW->display, monitor->node->crtc, gamma);
+ XRRFreeGamma(gamma);
+
+ return RGFW_TRUE;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_monitor_setMode)(RGFW_monitor* mon, RGFW_monitorMode* mode) {
+ RGFW_bool out = RGFW_FALSE;
+
+ XRRScreenResources* res = XRRGetScreenResourcesCurrent(_RGFW->display, DefaultRootWindow(_RGFW->display));
+ XRRCrtcInfo* ci = XRRGetCrtcInfo(_RGFW->display, res, mon->node->crtc);
+
+ if (XRRSetCrtcConfig(_RGFW->display, res, mon->node->crtc, CurrentTime, ci->x, ci->y, (RRMode)mode->src, ci->rotation, ci->outputs, ci->noutput) == True) {
+ out = RGFW_TRUE;
+ }
+
+ XRRFreeCrtcInfo(ci);
+ XRRFreeScreenResources(res);
+ return out;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_monitor_requestMode)(RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request) {
+ RGFW_init();
+
+ RGFW_bool output = RGFW_FALSE;
+
+ XRRScreenResources* res = XRRGetScreenResourcesCurrent(_RGFW->display, DefaultRootWindow(_RGFW->display));
+ if (res == NULL) return RGFW_FALSE;
+
+ XRRCrtcInfo* ci = XRRGetCrtcInfo(_RGFW->display, res, mon->node->crtc);
+ XRROutputInfo* oi = XRRGetOutputInfo(_RGFW->display, res, mon->node->rrOutput);
+
+ RRMode native = None;
+
+ int i;
+ for (i = 0; i < oi->nmode; i++) {
+ RGFW_monitorMode foundMode;
+ XRRModeInfo* mi = RGFW_XGetMode(ci, res, oi->modes[i], &foundMode);
+ if (mi == NULL) {
+ continue;
+ }
+
+ if (RGFW_monitorModeCompare(mode, &foundMode, request)) {
+ native = mi->id;
+ output = RGFW_TRUE;
+ mon->mode = foundMode;
+ break;
+ }
+ }
+
+ if (native) {
+ XRRSetCrtcConfig(_RGFW->display, res, mon->node->crtc, CurrentTime, ci->x, ci->y, native, ci->rotation, ci->outputs, ci->noutput);
+ }
+
+ XRRFreeOutputInfo(oi);
+ XRRFreeCrtcInfo(ci);
+ XRRFreeScreenResources(res);
+ return output;
+}
+
+RGFW_monitor* RGFW_FUNC(RGFW_window_getMonitor) (RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+
+ XWindowAttributes attrs;
+ if (!XGetWindowAttributes(_RGFW->display, win->src.window, &attrs)) {
+ return NULL;
+ }
+
+ for (RGFW_monitorNode* node = _RGFW->monitors.list.head; node; node = node->next) {
+ if ((attrs.x < node->mon.x + node->mon.mode.w) && (attrs.x + attrs.width > node->mon.x) && (attrs.y < node->mon.y + node->mon.mode.h) && (attrs.y + attrs.height > node->mon.y))
+ return &node->mon;
+ }
+
+
+ return &_RGFW->monitors.list.head->mon;
+}
+
+#ifdef RGFW_OPENGL
+RGFW_bool RGFW_FUNC(RGFW_window_createContextPtr_OpenGL) (RGFW_window* win, RGFW_glContext* context, RGFW_glHints* hints) {
+ /* for checking extensions later */
+ const char sRGBARBstr[] = "GLX_ARB_framebuffer_sRGB";
+ const char sRGBEXTstr[] = "GLX_EXT_framebuffer_sRGB";
+ const char noErorrStr[] = "GLX_ARB_create_context_no_error";
+ const char flushStr[] = "GLX_ARB_context_flush_control";
+ const char robustStr[] = "GLX_ARB_create_context_robustness";
+
+ /* basic RGFW int */
+ win->src.ctx.native = context;
+ win->src.gfxType = RGFW_gfxNativeOpenGL;
+
+ /* This is required so that way the user can create their own OpenGL context after RGFW_createWindow is used */
+ RGFW_bool showWindow = RGFW_FALSE;
+ if (win->src.window) {
+ showWindow = (RGFW_window_isMinimized(win) == RGFW_FALSE);
+ RGFW_window_closePlatform(win);
+ }
+
+ RGFW_bool transparent = (win->internal.flags & RGFW_windowTransparent);
+
+ /* start by creating a GLX config / X11 Viusal */
+ XVisualInfo visual;
+ GLXFBConfig bestFbc;
+
+ i32 visual_attribs[40];
+ RGFW_attribStack stack;
+ RGFW_attribStack_init(&stack, visual_attribs, 40);
+ RGFW_attribStack_pushAttribs(&stack, GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR);
+ RGFW_attribStack_pushAttribs(&stack, GLX_X_RENDERABLE, 1);
+ RGFW_attribStack_pushAttribs(&stack, GLX_RENDER_TYPE, GLX_RGBA_BIT);
+ RGFW_attribStack_pushAttribs(&stack, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT);
+ RGFW_attribStack_pushAttribs(&stack, GLX_DOUBLEBUFFER, 1);
+ RGFW_attribStack_pushAttribs(&stack, GLX_ALPHA_SIZE, hints->alpha);
+ RGFW_attribStack_pushAttribs(&stack, GLX_DEPTH_SIZE, hints->depth);
+ RGFW_attribStack_pushAttribs(&stack, GLX_STENCIL_SIZE, hints->stencil);
+ RGFW_attribStack_pushAttribs(&stack, GLX_STEREO, hints->stereo);
+ RGFW_attribStack_pushAttribs(&stack, GLX_AUX_BUFFERS, hints->auxBuffers);
+ RGFW_attribStack_pushAttribs(&stack, GLX_RED_SIZE, hints->red);
+ RGFW_attribStack_pushAttribs(&stack, GLX_GREEN_SIZE, hints->green);
+ RGFW_attribStack_pushAttribs(&stack, GLX_BLUE_SIZE, hints->blue);
+ RGFW_attribStack_pushAttribs(&stack, GLX_ACCUM_RED_SIZE, hints->accumRed);
+ RGFW_attribStack_pushAttribs(&stack, GLX_ACCUM_GREEN_SIZE, hints->accumGreen);
+ RGFW_attribStack_pushAttribs(&stack, GLX_ACCUM_BLUE_SIZE, hints->accumBlue);
+ RGFW_attribStack_pushAttribs(&stack, GLX_ACCUM_ALPHA_SIZE, hints->accumAlpha);
+
+ if (hints->sRGB) {
+ if (RGFW_extensionSupportedPlatform_OpenGL(sRGBARBstr, sizeof(sRGBARBstr)))
+ RGFW_attribStack_pushAttribs(&stack, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, hints->sRGB);
+ if (RGFW_extensionSupportedPlatform_OpenGL(sRGBEXTstr, sizeof(sRGBEXTstr)))
+ RGFW_attribStack_pushAttribs(&stack, GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, hints->sRGB);
+ }
+
+ RGFW_attribStack_pushAttribs(&stack, 0, 0);
+
+ /* find the configs */
+ i32 fbcount;
+ GLXFBConfig* fbc = glXChooseFBConfig(_RGFW->display, DefaultScreen(_RGFW->display), visual_attribs, &fbcount);
+
+ i32 best_fbc = -1;
+ i32 best_depth = 0;
+ i32 best_samples = 0;
+
+ if (fbcount == 0) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errOpenGLContext, "Failed to find any valid GLX visual configs.");
+ return 0;
+ }
+
+ /* search through all found configs to find the best match */
+ i32 i;
+ for (i = 0; i < fbcount; i++) {
+ XVisualInfo* vi = glXGetVisualFromFBConfig(_RGFW->display, fbc[i]);
+ if (vi == NULL)
+ continue;
+
+ i32 samp_buf, samples;
+ glXGetFBConfigAttrib(_RGFW->display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
+ glXGetFBConfigAttrib(_RGFW->display, fbc[i], GLX_SAMPLES, &samples);
+
+ if (best_fbc == -1) best_fbc = i;
+ if ((!(transparent) || vi->depth == 32) && best_depth == 0) {
+ best_fbc = i;
+ best_depth = vi->depth;
+ }
+ if ((!(transparent) || vi->depth == 32) && samples <= hints->samples && samples > best_samples) {
+ best_fbc = i;
+ best_depth = vi->depth;
+ best_samples = samples;
+ }
+ XFree(vi);
+ }
+
+ if (best_fbc == -1) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errOpenGLContext, "Failed to get a valid GLX visual.");
+ return 0;
+ }
+
+ /* we found a config */
+ bestFbc = fbc[best_fbc];
+ XVisualInfo* vi = glXGetVisualFromFBConfig(_RGFW->display, bestFbc);
+ if (vi->depth != 32 && transparent)
+ RGFW_debugCallback(RGFW_typeWarning, RGFW_warningOpenGL, "Failed to to find a matching visual with a 32-bit depth.");
+
+ if (best_samples < hints->samples)
+ RGFW_debugCallback(RGFW_typeWarning, RGFW_warningOpenGL, "Failed to load a matching sample count.");
+
+ XFree(fbc);
+ visual = *vi;
+ XFree(vi);
+
+ /* use the visual to create a new window */
+ RGFW_XCreateWindow(visual, "", win->internal.flags, win);
+
+ if (showWindow) {
+ RGFW_window_show(win);
+ }
+
+ /* create the actual OpenGL context */
+ i32 context_attribs[40];
+ RGFW_attribStack_init(&stack, context_attribs, 40);
+
+ i32 mask = 0;
+ switch (hints->profile) {
+ case RGFW_glES: mask |= GLX_CONTEXT_ES_PROFILE_BIT_EXT; break;
+ case RGFW_glForwardCompatibility: mask |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; break;
+ case RGFW_glCompatibility: mask |= GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; break;
+ case RGFW_glCore: mask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB; break;
+ default: mask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB; break;
+ }
+
+ RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_PROFILE_MASK_ARB, mask);
+
+ if (hints->minor || hints->major) {
+ RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_MAJOR_VERSION_ARB, hints->major);
+ RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_MINOR_VERSION_ARB, hints->minor);
+ }
+
+
+ if (RGFW_extensionSupportedPlatform_OpenGL(flushStr, sizeof(flushStr))) {
+ if (hints->releaseBehavior == RGFW_glReleaseFlush) {
+ RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB);
+ } else if (hints->releaseBehavior == RGFW_glReleaseNone) {
+ RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB);
+ }
+ }
+
+ i32 flags = 0;
+ if (hints->debug) flags |= GLX_CONTEXT_DEBUG_BIT_ARB;
+ if (hints->robustness && RGFW_extensionSupportedPlatform_OpenGL(robustStr, sizeof(robustStr))) flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB;
+ if (flags) {
+ RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_FLAGS_ARB, flags);
+ }
+
+ if (RGFW_extensionSupportedPlatform_OpenGL(noErorrStr, sizeof(noErorrStr))) {
+ RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_OPENGL_NO_ERROR_ARB, hints->noError);
+ }
+
+ RGFW_attribStack_pushAttribs(&stack, 0, 0);
+
+ /* create the context */
+ glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
+ char str[] = "glXCreateContextAttribsARB";
+ glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)glXGetProcAddressARB((u8*) str);
+
+ GLXContext ctx = NULL;
+ if (hints->share) {
+ ctx = hints->share->ctx;
+ }
+
+ if (glXCreateContextAttribsARB == NULL) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errOpenGLContext, "Failed to load proc address 'glXCreateContextAttribsARB', loading a generic OpenGL context.");
+ win->src.ctx.native->ctx = glXCreateContext(_RGFW->display, &visual, ctx, True);
+ } else {
+ _RGFW->x11Error = NULL;
+ win->src.ctx.native->ctx = glXCreateContextAttribsARB(_RGFW->display, bestFbc, ctx, True, context_attribs);
+ if (_RGFW->x11Error || win->src.ctx.native->ctx == NULL) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errOpenGLContext, "Failed to create an OpenGL context with AttribsARB, loading a generic OpenGL context.");
+ win->src.ctx.native->ctx = glXCreateContext(_RGFW->display, &visual, ctx, True);
+ }
+ }
+
+ #ifndef RGFW_NO_GLXWINDOW
+ win->src.ctx.native->window = glXCreateWindow(_RGFW->display, bestFbc, win->src.window, NULL);
+ #else
+ win->src.ctx.native->window = win->src.window;
+ #endif
+
+ glXMakeCurrent(_RGFW->display, (Drawable)win->src.ctx.native->window, (GLXContext)win->src.ctx.native->ctx);
+ RGFW_debugCallback(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context initalized.");
+
+ RGFW_window_swapInterval_OpenGL(win, 0);
+
+ return RGFW_TRUE;
+}
+
+void RGFW_FUNC(RGFW_window_deleteContextPtr_OpenGL) (RGFW_window* win, RGFW_glContext* ctx) {
+ #ifndef RGFW_NO_GLXWINDOW
+ if (win->src.ctx.native->window != win->src.window) {
+ glXDestroyWindow(_RGFW->display, win->src.ctx.native->window);
+ }
+ #endif
+
+ glXDestroyContext(_RGFW->display, ctx->ctx);
+ win->src.ctx.native = NULL;
+ RGFW_debugCallback(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context freed.");
+}
+
+RGFW_bool RGFW_FUNC(RGFW_extensionSupportedPlatform_OpenGL)(const char * extension, size_t len) {
+ RGFW_init();
+ const char* extensions = glXQueryExtensionsString(_RGFW->display, XDefaultScreen(_RGFW->display));
+ return (extensions != NULL) && RGFW_extensionSupportedStr(extensions, extension, len);
+}
+
+RGFW_proc RGFW_FUNC(RGFW_getProcAddress_OpenGL)(const char* procname) { return glXGetProcAddress((u8*) procname); }
+
+void RGFW_FUNC(RGFW_window_makeCurrentContext_OpenGL) (RGFW_window* win) { if (win) RGFW_ASSERT(win->src.ctx.native);
+ if (win == NULL)
+ glXMakeCurrent(NULL, (Drawable)NULL, (GLXContext) NULL);
+ else
+ glXMakeCurrent(_RGFW->display, (Drawable)win->src.ctx.native->window, (GLXContext) win->src.ctx.native->ctx);
+ return;
+}
+void* RGFW_FUNC(RGFW_getCurrentContext_OpenGL) (void) { return glXGetCurrentContext(); }
+void RGFW_FUNC(RGFW_window_swapBuffers_OpenGL) (RGFW_window* win) { RGFW_ASSERT(win->src.ctx.native); glXSwapBuffers(_RGFW->display, win->src.ctx.native->window); }
+
+void RGFW_FUNC(RGFW_window_swapInterval_OpenGL) (RGFW_window* win, i32 swapInterval) {
+ RGFW_ASSERT(win != NULL);
+ /* cached pfn to avoid calling glXGetProcAddress more than once */
+ static PFNGLXSWAPINTERVALEXTPROC pfn = NULL;
+ static int (*pfn2)(int) = NULL;
+
+ if (pfn == NULL) {
+ u8 str[] = "glXSwapIntervalEXT";
+ pfn = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress(str);
+ if (pfn == NULL) {
+ pfn = (PFNGLXSWAPINTERVALEXTPROC)1;
+ const char* array[] = {"GLX_MESA_swap_control", "GLX_SGI_swap_control"};
+
+ size_t i;
+ for (i = 0; i < sizeof(array) / sizeof(char*) && pfn2 == NULL; i++) {
+ pfn2 = (int(*)(int))glXGetProcAddress((u8*)array[i]);
+ }
+
+ if (pfn2 != NULL) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errOpenGLContext, "Failed to load swap interval function, fallingback to the native swapinterval function");
+ } else {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errOpenGLContext, "Failed to load swap interval function");
+ }
+ }
+ }
+
+ if (pfn != (PFNGLXSWAPINTERVALEXTPROC)1) {
+ pfn(_RGFW->display, win->src.ctx.native->window, swapInterval);
+ }
+ else if (pfn2 != NULL) {
+ pfn2(swapInterval);
+ }
+}
+#endif /* RGFW_OPENGL */
+
+i32 RGFW_initPlatform_X11(void) {
+ #ifdef RGFW_USE_XDL
+ XDL_init();
+ #endif
+
+ #if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD)
+ #if defined(__CYGWIN__)
+ RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor-1.so");
+ #elif defined(__OpenBSD__) || defined(__NetBSD__)
+ RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor.so");
+ #else
+ RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor.so.1");
+ #endif
+ RGFW_PROC_DEF(X11Cursorhandle, XcursorImageCreate);
+ RGFW_PROC_DEF(X11Cursorhandle, XcursorImageDestroy);
+ RGFW_PROC_DEF(X11Cursorhandle, XcursorImageLoadCursor);
+ #endif
+
+ #if !defined(RGFW_NO_X11_XI_PRELOAD)
+ #if defined(__CYGWIN__)
+ RGFW_LOAD_LIBRARY(X11Xihandle, "libXi-6.so");
+ #elif defined(__OpenBSD__) || defined(__NetBSD__)
+ RGFW_LOAD_LIBRARY(X11Xihandle, "libXi.so");
+ #else
+ RGFW_LOAD_LIBRARY(X11Xihandle, "libXi.so.6");
+ #endif
+ RGFW_PROC_DEF(X11Xihandle, XISelectEvents);
+ #endif
+
+ #if !defined(RGFW_NO_X11_EXT_PRELOAD)
+ #if defined(__CYGWIN__)
+ RGFW_LOAD_LIBRARY(X11XEXThandle, "libXext-6.so");
+ #elif defined(__OpenBSD__) || defined(__NetBSD__)
+ RGFW_LOAD_LIBRARY(X11XEXThandle, "libXext.so");
+ #else
+ RGFW_LOAD_LIBRARY(X11XEXThandle, "libXext.so.6");
+ #endif
+ RGFW_PROC_DEF(X11XEXThandle, XSyncCreateCounter);
+ RGFW_PROC_DEF(X11XEXThandle, XSyncIntToValue);
+ RGFW_PROC_DEF(X11XEXThandle, XSyncSetCounter);
+ RGFW_PROC_DEF(X11XEXThandle, XShapeCombineRegion);
+ RGFW_PROC_DEF(X11XEXThandle, XShapeCombineMask);
+ #endif
+
+ XInitThreads(); /*!< init X11 threading */
+ _RGFW->display = XOpenDisplay(0);
+ _RGFW->context = XUniqueContext();
+
+ XSetWindowAttributes wa;
+ RGFW_MEMZERO(&wa, sizeof(wa));
+ wa.event_mask = PropertyChangeMask;
+ _RGFW->helperWindow = XCreateWindow(_RGFW->display, XDefaultRootWindow(_RGFW->display), 0, 0, 1, 1, 0, 0,
+ InputOnly, DefaultVisual(_RGFW->display, DefaultScreen(_RGFW->display)), CWEventMask, &wa);
+
+ u8 RGFW_blk[] = { 0, 0, 0, 0 };
+ _RGFW->hiddenMouse = RGFW_createMouse(RGFW_blk, 1, 1, RGFW_formatRGBA8);
+
+ _RGFW->unixClipboard = NULL;
+
+ XkbComponentNamesRec rec;
+ XkbDescPtr desc = XkbGetMap(_RGFW->display, 0, XkbUseCoreKbd);
+ XkbDescPtr evdesc;
+ XSetErrorHandler(RGFW_XErrorHandler);
+ u8 old[256];
+
+ XkbGetNames(_RGFW->display, XkbKeyNamesMask, desc);
+
+ RGFW_MEMZERO(&rec, sizeof(rec));
+ char evdev[] = "evdev";
+ rec.keycodes = evdev;
+ evdesc = XkbGetKeyboardByName(_RGFW->display, XkbUseCoreKbd, &rec, XkbGBN_KeyNamesMask, XkbGBN_KeyNamesMask, False);
+ /* memo: RGFW_keycodes[x11 keycode] = rgfw keycode */
+ if(evdesc != NULL && desc != NULL) {
+ int i, j;
+ for(i = 0; i < (int)sizeof(old); i++){
+ old[i] = _RGFW->keycodes[i];
+ _RGFW->keycodes[i] = 0;
+ }
+ for(i = evdesc->min_key_code; i <= evdesc->max_key_code; i++){
+ for(j = desc->min_key_code; j <= desc->max_key_code; j++){
+ if(RGFW_STRNCMP(evdesc->names->keys[i].name, desc->names->keys[j].name, XkbKeyNameLength) == 0){
+ _RGFW->keycodes[j] = old[i];
+ break;
+ }
+ }
+ }
+ XkbFreeKeyboard(desc, 0, True);
+ XkbFreeKeyboard(evdesc, 0, True);
+ }
+
+ XSetLocaleModifiers("");
+ XRegisterIMInstantiateCallback(_RGFW->display, NULL, NULL, NULL, RGFW_x11_imInitCallback, NULL);
+
+ i32 errorBase;
+ if (XRRQueryExtension(_RGFW->display, &_RGFW->xrandrEventBase, &errorBase)) {
+ XRRSelectInput(_RGFW->display, RootWindow(_RGFW->display, DefaultScreen(_RGFW->display)), RROutputChangeNotifyMask);
+ }
+
+ return 0;
+}
+
+void RGFW_deinitPlatform_X11(void) {
+ #define RGFW_FREE_LIBRARY(x) if (x != NULL) dlclose(x); x = NULL;
+ /* to save the clipboard on the x server after the window is closed */
+ RGFW_LOAD_ATOM(CLIPBOARD_MANAGER); RGFW_LOAD_ATOM(CLIPBOARD);
+ RGFW_LOAD_ATOM(SAVE_TARGETS);
+ if (XGetSelectionOwner(_RGFW->display, CLIPBOARD) == _RGFW->helperWindow) {
+ XConvertSelection(_RGFW->display, CLIPBOARD_MANAGER, SAVE_TARGETS, None, _RGFW->helperWindow, CurrentTime);
+ while (RGFW_XHandleClipboardSelectionHelper());
+ }
+
+ XUnregisterIMInstantiateCallback(_RGFW->display, NULL, NULL, NULL, RGFW_x11_imInitCallback, NULL);
+
+ if (_RGFW->im) {
+ XCloseIM(_RGFW->im);
+ _RGFW->im = NULL;
+ }
+
+ if (_RGFW->unixClipboard) {
+ RGFW_FREE(_RGFW->unixClipboard);
+ _RGFW->unixClipboard = NULL;
+ }
+
+ if (_RGFW->hiddenMouse) {
+ RGFW_freeMouse(_RGFW->hiddenMouse);
+ _RGFW->hiddenMouse = NULL;
+ }
+
+ XDestroyWindow(_RGFW->display, (Drawable) _RGFW->helperWindow); /*!< close the window */
+ XCloseDisplay(_RGFW->display); /*!< kill connection to the x server */
+
+ #if !defined(RGFW_NO_X11_CURSOR_PRELOAD) && !defined(RGFW_NO_X11_CURSOR)
+ RGFW_FREE_LIBRARY(X11Cursorhandle);
+ #endif
+ #if !defined(RGFW_NO_X11_XI_PRELOAD)
+ RGFW_FREE_LIBRARY(X11Xihandle);
+ #endif
+
+ #ifdef RGFW_USE_XDL
+ XDL_close();
+ #endif
+
+ #if !defined(RGFW_NO_X11_EXT_PRELOAD)
+ RGFW_FREE_LIBRARY(X11XEXThandle);
+ #endif
+}
+
+void RGFW_FUNC(RGFW_window_closePlatform)(RGFW_window* win) {
+ if (win->src.ic) {
+ XDestroyIC(win->src.ic);
+ win->src.ic = NULL;
+ }
+
+ XFreeGC(_RGFW->display, win->src.gc);
+ XDeleteContext(_RGFW->display, win->src.window, _RGFW->context);
+ XDestroyWindow(_RGFW->display, (Drawable) win->src.window); /*!< close the window */
+ return;
+}
+
+#ifdef RGFW_WEBGPU
+WGPUSurface RGFW_FUNC(RGFW_window_createSurface_WebGPU) (RGFW_window* window, WGPUInstance instance) {
+ WGPUSurfaceDescriptor surfaceDesc = {0};
+ WGPUSurfaceSourceXlibWindow fromXlib = {0};
+ fromXlib.chain.sType = WGPUSType_SurfaceSourceXlibWindow;
+ fromXlib.display = _RGFW->display;
+ fromXlib.window = window->src.window;
+
+ surfaceDesc.nextInChain = (WGPUChainedStruct*)&fromXlib.chain;
+ return wgpuInstanceCreateSurface(instance, &surfaceDesc);
+}
+#endif
+
+#endif
+/*
+ End of *nix
+*/
+
+/*
+
+ Start of Wayland defines
+*/
+
+#ifdef RGFW_WAYLAND
+#ifdef RGFW_X11
+#undef RGFW_FUNC /* remove previous define */
+#define RGFW_FUNC(func) func##_Wayland
+#else
+#define RGFW_FUNC(func) func
+#endif
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <xkbcommon/xkbcommon.h>
+#include <xkbcommon/xkbcommon-keysyms.h>
+#include <xkbcommon/xkbcommon-compose.h>
+#include <dirent.h>
+#include <linux/kd.h>
+#include <wayland-cursor.h>
+#include <fcntl.h>
+
+#ifndef RGFW_X11
+void RGFW_useWayland(RGFW_bool wayland) { RGFW_UNUSED(wayland); }
+RGFW_bool RGFW_usingWayland(void) { return RGFW_TRUE; }
+#endif
+
+struct wl_display* RGFW_getDisplay_Wayland(void) { return _RGFW->wl_display; }
+struct wl_surface* RGFW_window_getWindow_Wayland(RGFW_window* win) { return win->src.surface; }
+
+/* wayland global garbage (wayland bad, X11 is fine (ish) (not really)) */
+#include "xdg-shell.h"
+#include "xdg-toplevel-icon-v1.h"
+#include "xdg-decoration-unstable-v1.h"
+#include "relative-pointer-unstable-v1.h"
+#include "pointer-constraints-unstable-v1.h"
+#include "xdg-output-unstable-v1.h"
+#include "pointer-warp-v1.h"
+
+void RGFW_toggleWaylandMaximized(RGFW_window* win, RGFW_bool maximized);
+
+static void RGFW_wl_setOpaque(RGFW_window* win) {
+ struct wl_region* wl_region = wl_compositor_create_region(_RGFW->compositor);
+
+ if (!wl_region) return; /* return if no region was created */
+
+ wl_region_add(wl_region, 0, 0, win->w, win->h);
+ wl_surface_set_opaque_region(win->src.surface, wl_region);
+ wl_region_destroy(wl_region);
+
+}
+
+static void RGFW_wl_xdg_wm_base_ping_handler(void* data, struct xdg_wm_base* wm_base,
+ u32 serial) {
+ RGFW_UNUSED(data);
+ xdg_wm_base_pong(wm_base, serial);
+}
+static void RGFW_wl_xdg_surface_configure_handler(void* data, struct xdg_surface* xdg_surface,
+ u32 serial) {
+
+ xdg_surface_ack_configure(xdg_surface, serial);
+
+ RGFW_window* win = (RGFW_window*)data;
+
+ if (win == NULL) {
+ win = _RGFW->kbOwner;
+ if (win == NULL)
+ return;
+ }
+
+ /* useful for libdecor */
+ if (win->src.activated != win->src.pending_activated) {
+ win->src.activated = win->src.pending_activated;
+ }
+
+ if (win->src.maximized != win->src.pending_maximized) {
+ RGFW_toggleWaylandMaximized(win, win->src.pending_maximized);
+
+ RGFW_window_checkMode(win);
+ }
+
+
+ if (win->src.resizing) {
+
+ RGFW_windowResizedCallback(win, win->w, win->h);
+ RGFW_window_resize(win, win->w, win->h);
+ if (!(win->internal.flags & RGFW_windowTransparent)) {
+ RGFW_wl_setOpaque(win);
+ }
+ }
+
+ win->src.configured = RGFW_TRUE;
+}
+
+static void RGFW_wl_xdg_toplevel_configure_handler(void* data, struct xdg_toplevel* toplevel,
+ i32 width, i32 height, struct wl_array* states) {
+
+ RGFW_UNUSED(toplevel);
+ RGFW_window* win = (RGFW_window*)data;
+
+
+ win->src.pending_activated = RGFW_FALSE;
+ win->src.pending_maximized = RGFW_FALSE;
+ win->src.resizing = RGFW_FALSE;
+
+
+ enum xdg_toplevel_state* state;
+ wl_array_for_each(state, states) {
+ switch (*state) {
+ case XDG_TOPLEVEL_STATE_ACTIVATED:
+ win->src.pending_activated = RGFW_TRUE;
+ break;
+ case XDG_TOPLEVEL_STATE_MAXIMIZED:
+ win->src.pending_maximized = RGFW_TRUE;
+ break;
+ default:
+ break;
+ }
+
+ }
+ /* if width and height are not zero and are not the same as the window */
+ /* the window is resizing so update the values */
+ if ((width && height) && (win->w != width || win->h != height)) {
+ win->src.resizing = RGFW_TRUE;
+ win->src.w = win->w = width;
+ win->src.h = win->h = height;
+ }
+}
+
+static void RGFW_wl_xdg_toplevel_close_handler(void* data, struct xdg_toplevel *toplevel) {
+ RGFW_UNUSED(toplevel);
+ RGFW_window* win = (RGFW_window*)data;
+
+ if (!win->internal.shouldClose) {
+ RGFW_windowCloseCallback(win);
+ }
+}
+
+static void RGFW_wl_xdg_decoration_configure_handler(void* data,
+ struct zxdg_toplevel_decoration_v1* zxdg_toplevel_decoration_v1, u32 mode) {
+ RGFW_window* win = (RGFW_window*)data; RGFW_UNUSED(zxdg_toplevel_decoration_v1);
+
+ /* this is expected to run once */
+ /* set the decoration mode set by earlier request */
+ if (mode != win->src.decoration_mode) {
+ win->src.decoration_mode = mode;
+ }
+}
+
+static void RGFW_wl_shm_format_handler(void* data, struct wl_shm *shm, u32 format) {
+ RGFW_UNUSED(data); RGFW_UNUSED(shm); RGFW_UNUSED(format);
+}
+
+static void RGFW_wl_relative_pointer_motion(void *data, struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1,
+ u32 time_hi, u32 time_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel) {
+
+ RGFW_UNUSED(zwp_relative_pointer_v1); RGFW_UNUSED(time_hi); RGFW_UNUSED(time_lo);
+ RGFW_UNUSED(dx_unaccel); RGFW_UNUSED(dy_unaccel);
+
+ RGFW_info* RGFW = (RGFW_info*)data;
+
+ RGFW_ASSERT(RGFW->mouseOwner != NULL);
+ RGFW_window* win = RGFW->mouseOwner;
+
+ RGFW_ASSERT(win);
+
+ float vecX = (float)wl_fixed_to_double(dx);
+ float vecY = (float)wl_fixed_to_double(dy);
+ RGFW_rawMotionCallback(win, vecX, vecY);
+}
+
+static void RGFW_wl_pointer_locked(void *data, struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1) {
+ RGFW_UNUSED(zwp_locked_pointer_v1);
+ RGFW_info* RGFW = (RGFW_info*)data;
+ wl_pointer_set_cursor(RGFW->wl_pointer, RGFW->mouse_enter_serial, NULL, 0, 0); /* draw no cursor */
+}
+
+static void RGFW_wl_pointer_enter(void* data, struct wl_pointer* pointer, u32 serial,
+ struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) {
+ RGFW_info* RGFW = (RGFW_info*)data;
+ RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface);
+
+ /* save when the pointer is locked or using default cursor */
+ RGFW->mouse_enter_serial = serial;
+ win->internal.mouseInside = RGFW_TRUE;
+ RGFW->windowState.mouseEnter = RGFW_TRUE;
+
+ RGFW->mouseOwner = win;
+
+ /* set the cursor */
+ if (win->src.using_custom_cursor) {
+ wl_pointer_set_cursor(pointer, serial, win->src.custom_cursor_surface, 0, 0);
+ }
+ else {
+ RGFW_window_setMouseDefault(win);
+ }
+
+ i32 x = (i32)wl_fixed_to_double(surface_x);
+ i32 y = (i32)wl_fixed_to_double(surface_y);
+ RGFW_mouseNotifyCallback(win, x, y, RGFW_TRUE);
+}
+
+static void RGFW_wl_pointer_leave(void* data, struct wl_pointer *pointer, u32 serial, struct wl_surface *surface) {
+ RGFW_UNUSED(pointer); RGFW_UNUSED(serial);
+ RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface);
+ RGFW_info* RGFW = (RGFW_info*)data;
+ if (RGFW->mouseOwner == win)
+ RGFW->mouseOwner = NULL;
+
+ RGFW_mouseNotifyCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, RGFW_FALSE);
+}
+
+static void RGFW_wl_pointer_motion(void* data, struct wl_pointer *pointer, u32 time, wl_fixed_t x, wl_fixed_t y) {
+ RGFW_UNUSED(pointer); RGFW_UNUSED(time);
+ RGFW_info* RGFW = (RGFW_info*)data;
+ RGFW_ASSERT(RGFW->mouseOwner != NULL);
+
+ RGFW_window* win = RGFW->mouseOwner;
+
+ i32 convertedX = (i32)wl_fixed_to_double(x);
+ i32 convertedY = (i32)wl_fixed_to_double(y);
+
+ RGFW_mouseMotionCallback(win, convertedX, convertedY);
+}
+
+static void RGFW_wl_pointer_button(void* data, struct wl_pointer *pointer, u32 serial, u32 time, u32 button, u32 state) {
+ RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(serial);
+ RGFW_info* RGFW = (RGFW_info*)data;
+
+ RGFW_ASSERT(RGFW->mouseOwner != NULL);
+ RGFW_window* win = RGFW->mouseOwner;
+
+ u32 b = (button - 0x110);
+
+ /* flip right and middle button codes */
+ if (b == 1) b = 2;
+ else if (b == 2) b = 1;
+
+ RGFW_mouseButtonCallback(win, (u8)b, RGFW_BOOL(state));
+}
+
+static void RGFW_wl_pointer_axis(void* data, struct wl_pointer *pointer, u32 time, u32 axis, wl_fixed_t value) {
+ RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(axis);
+
+ RGFW_info* RGFW = (RGFW_info*)data;
+ RGFW_ASSERT(RGFW->mouseOwner != NULL);
+ RGFW_window* win = RGFW->mouseOwner;
+
+ float scrollX = 0.0;
+ float scrollY = 0.0;
+
+ if (!(win->internal.enabledEvents & (RGFW_BIT(RGFW_mouseScroll)))) return;
+
+ if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
+ scrollX = (float)(-wl_fixed_to_double(value) / 10.0);
+ else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
+ scrollY = (float)(-wl_fixed_to_double(value) / 10.0);
+
+ RGFW_mouseScrollCallback(win, scrollX, scrollY);
+}
+
+
+static void RGFW_doNothing(void) { }
+
+static void RGFW_wl_keyboard_keymap(void* data, struct wl_keyboard *keyboard, u32 format, i32 fd, u32 size) {
+ RGFW_UNUSED(keyboard); RGFW_UNUSED(format);
+ RGFW_info* RGFW = (RGFW_info*)data;
+
+ char *keymap_string = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+ xkb_keymap_unref(RGFW->keymap);
+ RGFW->keymap = xkb_keymap_new_from_string(RGFW->xkb_context, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
+
+ munmap(keymap_string, size);
+ close(fd);
+ xkb_state_unref(RGFW->xkb_state);
+ RGFW->xkb_state = xkb_state_new(RGFW->keymap);
+
+ const char* locale = getenv("LC_ALL");
+ if (!locale)
+ locale = getenv("LC_CTYPE");
+ if (!locale)
+ locale = getenv("LANG");
+ if (!locale)
+ locale = "C";
+
+ struct xkb_compose_table* composeTable = xkb_compose_table_new_from_locale(RGFW->xkb_context, locale, XKB_COMPOSE_COMPILE_NO_FLAGS);
+ if (composeTable) {
+ RGFW->composeState = xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS);
+ xkb_compose_table_unref(composeTable);
+ }
+}
+
+static void RGFW_wl_keyboard_enter(void* data, struct wl_keyboard *keyboard, u32 serial, struct wl_surface *surface, struct wl_array *keys) {
+ RGFW_UNUSED(keyboard); RGFW_UNUSED(keys);
+
+ RGFW_info* RGFW = (RGFW_info*)data;
+ RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface);
+ RGFW->kbOwner = win;
+
+ // this is to prevent race conditions
+ if (RGFW->data_device != NULL && win->src.data_source != NULL) {
+ wl_data_device_set_selection(RGFW->data_device, win->src.data_source, serial);
+ }
+ /* is set when RGFW_window_minimize is called; if the minimize button is */
+ /* pressed this flag is not set since there is no event to listen for */
+ if (win->src.minimized == RGFW_TRUE) win->src.minimized = RGFW_FALSE;
+
+ RGFW_windowFocusCallback(win, RGFW_TRUE);
+}
+
+static void RGFW_wl_keyboard_leave(void* data, struct wl_keyboard *keyboard, u32 serial, struct wl_surface *surface) {
+ RGFW_UNUSED(keyboard); RGFW_UNUSED(serial);
+
+ RGFW_info* RGFW = (RGFW_info*)data;
+ RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface);
+ if (RGFW->kbOwner == win)
+ RGFW->kbOwner = NULL;
+
+ RGFW_windowFocusCallback(win, RGFW_FALSE);
+}
+
+static xkb_keysym_t RGFW_wl_composeSymbol(RGFW_info* RGFW, xkb_keysym_t sym) {
+ if (sym == XKB_KEY_NoSymbol || !RGFW->composeState)
+ return sym;
+ if (xkb_compose_state_feed(RGFW->composeState, sym) != XKB_COMPOSE_FEED_ACCEPTED)
+ return sym;
+ switch (xkb_compose_state_get_status(RGFW->composeState)) {
+ case XKB_COMPOSE_COMPOSED:
+ return xkb_compose_state_get_one_sym(RGFW->composeState);
+ case XKB_COMPOSE_COMPOSING:
+ case XKB_COMPOSE_CANCELLED:
+ return XKB_KEY_NoSymbol;
+ case XKB_COMPOSE_NOTHING:
+ default:
+ return sym;
+ }
+}
+
+static void RGFW_wl_send_key_event(u32 key) {
+ const xkb_keysym_t* keysyms;
+ if (xkb_state_key_get_syms(_RGFW->xkb_state, key + 8, &keysyms) == 1) {
+ xkb_keysym_t keysym = RGFW_wl_composeSymbol(_RGFW, keysyms[0]);
+ u32 codepoint = xkb_keysym_to_utf32(keysym);
+ if (codepoint != 0) {
+ RGFW_keyCharCallback(_RGFW->kbOwner, codepoint);
+ }
+ }
+}
+
+static void RGFW_wl_keyboard_key(void* data, struct wl_keyboard *keyboard, u32 serial, u32 time, u32 key, u32 state) {
+ RGFW_UNUSED(keyboard); RGFW_UNUSED(serial);
+
+ RGFW_info* RGFW = (RGFW_info*)data;
+ if (RGFW->kbOwner == NULL) return;
+
+ RGFW_window *RGFW_key_win = RGFW->kbOwner;
+ RGFW_key RGFWkey = RGFW_apiKeyToRGFW(key + 8);
+
+ RGFW_keyUpdateKeyMods(RGFW_key_win, RGFW_BOOL(xkb_keymap_mod_get_index(RGFW->keymap, "Lock")), RGFW_BOOL(xkb_keymap_mod_get_index(RGFW->keymap, "Mod2")), RGFW_BOOL(xkb_keymap_mod_get_index(RGFW->keymap, "ScrollLock")));
+ RGFW_keyCallback(RGFW_key_win, (u8)RGFWkey, RGFW_key_win->internal.mod, RGFW_isKeyDown((u8)RGFWkey) && RGFW_BOOL(state), RGFW_BOOL(state));
+
+ /* [comment by Kala Telo (@kala-telo) and edited by Riley Mabb (@ColleagueRiley)]
+ we send the event at the moment we receive it, and
+ repeated key presses will be handled by RGFW_pollEvents
+ if the compositor doesn't support proxy (seat?) version
+ of at least 4, it won't initialize wl_repeat_info_rate,
+ and by spec, rate of 0 means disabled, thus repeating
+ keys are disabled by being zero-initialized
+ */
+ RGFW->last_key = state ? key : 0;
+ RGFW->last_key_time = time + (u32)_RGFW->wl_repeat_info_delay;
+ if (state) {
+ RGFW_wl_send_key_event(_RGFW->last_key);
+ }
+}
+
+static void RGFW_wl_keyboard_modifiers(void* data, struct wl_keyboard *keyboard, u32 serial, u32 mods_depressed, u32 mods_latched, u32 mods_locked, u32 group) {
+ RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time);
+ RGFW_info* RGFW = (RGFW_info*)data;
+ xkb_state_update_mask(RGFW->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
+}
+
+static void RGFW_wl_keyboard_repeat_info(void* data, struct wl_keyboard *keyboard, i32 rate, i32 delay) {
+ RGFW_UNUSED(data);
+ RGFW_UNUSED(keyboard);
+ _RGFW->wl_repeat_info_rate = rate;
+ _RGFW->wl_repeat_info_delay = delay;
+}
+
+static void RGFW_wl_seat_capabilities(void* data, struct wl_seat *seat, u32 capabilities) {
+ RGFW_info* RGFW = (RGFW_info*)data;
+ static struct wl_pointer_listener pointer_listener;
+ RGFW_MEMZERO(&pointer_listener, sizeof(pointer_listener));
+ pointer_listener.enter = &RGFW_wl_pointer_enter;
+ pointer_listener.leave = &RGFW_wl_pointer_leave;
+ pointer_listener.motion = &RGFW_wl_pointer_motion;
+ pointer_listener.button = &RGFW_wl_pointer_button;
+ pointer_listener.axis = &RGFW_wl_pointer_axis;
+
+ static struct wl_keyboard_listener keyboard_listener;
+ RGFW_MEMZERO(&keyboard_listener, sizeof(keyboard_listener));
+ keyboard_listener.keymap = &RGFW_wl_keyboard_keymap;
+ keyboard_listener.enter = &RGFW_wl_keyboard_enter;
+ keyboard_listener.leave = &RGFW_wl_keyboard_leave;
+ keyboard_listener.key = &RGFW_wl_keyboard_key;
+ keyboard_listener.modifiers = &RGFW_wl_keyboard_modifiers;
+ keyboard_listener.repeat_info = &RGFW_wl_keyboard_repeat_info;
+
+ if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !RGFW->wl_pointer) {
+ RGFW->wl_pointer = wl_seat_get_pointer(seat);
+ wl_pointer_add_listener(RGFW->wl_pointer, &pointer_listener, RGFW);
+ }
+ if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && !RGFW->wl_keyboard) {
+ RGFW->wl_keyboard = wl_seat_get_keyboard(seat);
+ wl_keyboard_add_listener(RGFW->wl_keyboard, &keyboard_listener, RGFW);
+ }
+
+ if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && RGFW->wl_pointer) {
+ wl_pointer_destroy(RGFW->wl_pointer);
+ }
+ if (!(capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && RGFW->wl_keyboard) {
+ wl_keyboard_destroy(RGFW->wl_keyboard);
+ }
+}
+
+static void RGFW_wl_output_set_geometry(void *data, struct wl_output *wl_output,
+ i32 x, i32 y, i32 physical_width, i32 physical_height,
+ i32 subpixel, const char *make, const char *model, i32 transform) {
+
+ RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon;
+ monitor->x = x;
+ monitor->y = y;
+
+ monitor->physW = (float)physical_width / 25.4f;
+ monitor->physH = (float)physical_height / 25.4f;
+
+ RGFW_UNUSED(wl_output);
+ RGFW_UNUSED(subpixel);
+ RGFW_UNUSED(make);
+ RGFW_UNUSED(model);
+ RGFW_UNUSED(transform);
+}
+
+static void RGFW_wl_output_handle_mode(void *data, struct wl_output *wl_output, u32 flags,
+ i32 width, i32 height, i32 refresh) {
+
+ RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon;
+
+ RGFW_monitorMode mode;
+ mode.w = width;
+ mode.h = height;
+ mode.refreshRate = (float)refresh / 1000.0f;
+ mode.src = wl_output;
+
+ monitor->node->modeCount += 1;
+
+ RGFW_monitorMode* modes = (RGFW_monitorMode*)RGFW_ALLOC(monitor->node->modeCount * sizeof(RGFW_monitorMode));
+
+ if (monitor->node->modeCount > 1) {
+ RGFW_monitor_getModesPtr(monitor, &modes);
+ RGFW_FREE(monitor->node->modes);
+ }
+
+ modes[monitor->node->modeCount - 1] = mode;
+ monitor->node->modes = modes;
+
+ if (flags & WL_OUTPUT_MODE_CURRENT) {
+ monitor->mode = mode;
+ } else {
+ }
+}
+
+static void RGFW_wl_output_set_scale(void *data, struct wl_output *wl_output, i32 factor) {
+ RGFW_UNUSED(wl_output);
+ RGFW_monitor* mon = &((RGFW_monitorNode*)data)->mon;
+
+ mon->scaleX = (float)factor;
+ mon->scaleY = (float)factor;
+}
+
+static void RGFW_wl_output_set_name(void *data, struct wl_output *wl_output, const char *name) {
+ RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon;
+
+ RGFW_STRNCPY(monitor->name, name, sizeof(monitor->name) - 1);
+ monitor->name[sizeof(monitor->name) - 1] = '\0';
+
+ RGFW_UNUSED(wl_output);
+
+}
+
+static void RGFW_xdg_output_logical_pos(void *data, struct zxdg_output_v1 *zxdg_output_v1, i32 x, i32 y) {
+ RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon;
+ monitor->x = x;
+ monitor->y = y;
+ RGFW_UNUSED(zxdg_output_v1);
+}
+
+static void RGFW_xdg_output_logical_size(void *data, struct zxdg_output_v1 *zxdg_output_v1, i32 width, i32 height) {
+ RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon;
+
+ float mon_float_width = (float) monitor->mode.w;
+ float mon_float_height = (float) monitor->mode.h;
+
+ float scaleX = (mon_float_width / (float) width);
+ float scaleY = (mon_float_height / (float) height);
+ RGFW_UNUSED(scaleY);
+
+ float dpi = scaleX * 96.0f;
+
+ monitor->pixelRatio = dpi >= 192.0f ? 2.0f : 1.0f;
+
+ /* under xwayland the monitor changes w & h when compositor scales it */
+ monitor->mode.w = width;
+ monitor->mode.h = height;
+ RGFW_UNUSED(zxdg_output_v1);
+}
+
+
+static void RGFW_wl_output_handle_done(void* data, struct wl_output* output) {
+ RGFW_UNUSED(output);
+
+ RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon;
+
+ if (monitor->physW <= 0 || monitor->physH <= 0) {
+ monitor->physW = (i32) ((float)monitor->mode.w / 96.0f);
+ monitor->physH = (i32) ((float)monitor->mode.h / 96.0f);
+ }
+
+ if (((RGFW_monitorNode*)data)->disconnected == RGFW_FALSE) {
+ return;
+ }
+
+ ((RGFW_monitorNode*)data)->disconnected = RGFW_TRUE;
+
+ RGFW_monitorCallback(_RGFW->root, monitor, RGFW_TRUE);
+}
+
+static void RGFW_wl_create_outputs(struct wl_registry *const registry, u32 id) {
+ struct wl_output *output = wl_registry_bind(registry, id, &wl_output_interface, wl_proxy_get_version((struct wl_proxy*)_RGFW->seat));
+ RGFW_monitorNode* node;
+ RGFW_monitor mon;
+
+ if (!output) return;
+
+ char RGFW_mon_default_name[10];
+
+ RGFW_SNPRINTF(RGFW_mon_default_name, sizeof(RGFW_mon_default_name), "monitor-%zu", _RGFW->monitors.count);
+ RGFW_STRNCPY(mon.name, RGFW_mon_default_name, sizeof(mon.name) - 1);
+ mon.name[sizeof(mon.name) - 1] = '\0';
+
+ /* set in case compositor does not send one */
+ /* or no xdg_output support */
+ mon.scaleY = mon.scaleX = mon.pixelRatio = 1.0f;
+
+ node = RGFW_monitors_add(&mon);
+ if (node == NULL) return;
+
+ node->modeCount = 0;
+ node->disconnected = RGFW_TRUE;
+ node->id = id;
+ node->output = output;
+
+ static const struct wl_output_listener wl_output_listener = {
+ .geometry = RGFW_wl_output_set_geometry,
+ .mode = RGFW_wl_output_handle_mode,
+ .done = RGFW_wl_output_handle_done,
+ .scale = RGFW_wl_output_set_scale,
+ .name = RGFW_wl_output_set_name,
+ .description = (void (*)(void *, struct wl_output *, const char *))&RGFW_doNothing
+ };
+
+ /* the wl_output will have a reference to the node */
+ wl_output_set_user_data(output, node);
+
+ /* pass the monitor so we can access it in the callback functions */
+ wl_output_add_listener(output, &wl_output_listener, node);
+
+ if (!_RGFW->xdg_output_manager)
+ return; /* compositor does not support it */
+
+ static const struct zxdg_output_v1_listener xdg_output_listener = {
+ .name = (void (*)(void *,struct zxdg_output_v1 *, const char *))&RGFW_doNothing,
+ .done = (void (*)(void *,struct zxdg_output_v1 *))&RGFW_doNothing,
+ .description = (void (*)(void *,struct zxdg_output_v1 *, const char *))&RGFW_doNothing,
+ .logical_position = RGFW_xdg_output_logical_pos,
+ .logical_size = RGFW_xdg_output_logical_size
+ };
+
+ node->xdg_output = zxdg_output_manager_v1_get_xdg_output(_RGFW->xdg_output_manager, node->output);
+ zxdg_output_v1_add_listener(node->xdg_output, &xdg_output_listener, node);
+}
+
+static void RGFW_wl_surface_enter(void *data, struct wl_surface *wl_surface, struct wl_output *output) {
+ RGFW_UNUSED(wl_surface);
+
+ RGFW_window* win = (RGFW_window*)data;
+ RGFW_monitorNode* node = wl_output_get_user_data(output);
+ if (node == NULL) return;
+
+ win->src.active_monitor = node;
+}
+
+static void RGFW_wl_data_source_send(void *data, struct wl_data_source *wl_data_source, const char *mime_type, i32 fd) {
+ RGFW_UNUSED(data); RGFW_UNUSED(wl_data_source);
+
+ // a client can accept our clipboard
+ if (RGFW_STRNCMP(mime_type, "text/plain;charset=utf-8", 25) == 0) {
+ // do not write \0
+ size_t length = _RGFW->unixClipboard->length;
+ if (_RGFW->unixClipboard->data[0] == '\0') {
+ length -= 1;
+ }
+ write(fd, _RGFW->unixClipboard->data, length);
+ }
+
+ close(fd);
+}
+
+static void RGFW_wl_data_source_cancelled(void *data, struct wl_data_source *wl_data_source) {
+
+ RGFW_info* RGFW = (RGFW_info*)data;
+
+ if (RGFW->kbOwner && RGFW->kbOwner->src.data_source == wl_data_source) {
+ RGFW->kbOwner->src.data_source = NULL;
+ }
+
+ wl_data_source_destroy(wl_data_source);
+
+}
+
+static void RGFW_wl_data_device_data_offer(void *data, struct wl_data_device *wl_data_device, struct wl_data_offer *wl_data_offer) {
+
+ RGFW_UNUSED(data); RGFW_UNUSED(wl_data_device);
+ static const struct wl_data_offer_listener wl_data_offer_listener = {
+ .offer = (void (*)(void *data, struct wl_data_offer *wl_data_offer, const char *))RGFW_doNothing,
+ .source_actions = (void (*)(void *data, struct wl_data_offer *wl_data_offer, u32 dnd_action))RGFW_doNothing,
+ .action = (void (*)(void *data, struct wl_data_offer *wl_data_offer, u32 dnd_action))RGFW_doNothing
+ };
+ wl_data_offer_add_listener(wl_data_offer, &wl_data_offer_listener, NULL);
+}
+
+static void RGFW_wl_data_device_selection(void *data, struct wl_data_device *wl_data_device, struct wl_data_offer *wl_data_offer) {
+ RGFW_UNUSED(data); RGFW_UNUSED(wl_data_device);
+ /* Clipboard is empty */
+ if (wl_data_offer == NULL) {
+ return;
+ }
+
+ int pfds[2];
+ pipe(pfds);
+
+ wl_data_offer_receive(wl_data_offer, "text/plain", pfds[1]);
+ close(pfds[1]);
+
+ wl_display_roundtrip(_RGFW->wl_display);
+
+ char buf[1024];
+
+ ssize_t n = read(pfds[0], buf, sizeof(buf));
+ if (n <= 0) {
+ close(pfds[0]);
+ wl_data_offer_destroy(wl_data_offer);
+ return;
+ }
+
+ size_t length = (size_t)n;
+ if (buf[n - 1] != '\0') length += 1;
+
+ if (_RGFW->unixClipboard != NULL) RGFW_FREE(_RGFW->unixClipboard);
+
+ _RGFW->unixClipboard = (RGFW_dataTransfer*)RGFW_ALLOC(sizeof(RGFW_dataTransfer) + (size_t)n);
+ RGFW_ASSERT(_RGFW->unixClipboard != NULL);
+
+ char* data_ptr = &((char*)(void*)_RGFW->unixClipboard)[sizeof(RGFW_dataTransfer) - 1];
+ RGFW_MEMCPY(data_ptr, buf, (size_t)n);
+ data_ptr[length - 1] = '\0';
+
+ _RGFW->unixClipboard->data = data_ptr;
+ _RGFW->unixClipboard->type = RGFW_dataText;
+ _RGFW->unixClipboard->length = length;
+
+ close(pfds[0]);
+
+ wl_data_offer_destroy(wl_data_offer);
+
+}
+
+static void RGFW_wl_global_registry_handler(void* data, struct wl_registry *registry, u32 id, const char *interface, u32 version) {
+
+ static struct wl_seat_listener seat_listener = {&RGFW_wl_seat_capabilities, (void (*)(void *, struct wl_seat *, const char *))&RGFW_doNothing};
+ static const struct wl_shm_listener shm_listener = { .format = RGFW_wl_shm_format_handler };
+
+ RGFW_info* RGFW = (RGFW_info*)data;
+ RGFW_UNUSED(version);
+
+ if (RGFW_STRNCMP(interface, "wl_compositor", 16) == 0) {
+ RGFW->compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 4);
+ } else if (RGFW_STRNCMP(interface, "xdg_wm_base", 12) == 0) {
+ RGFW->xdg_wm_base = wl_registry_bind(registry, id, &xdg_wm_base_interface, 1);
+ } else if (RGFW_STRNCMP(interface, zxdg_decoration_manager_v1_interface.name, 255) == 0) {
+ RGFW->decoration_manager = wl_registry_bind(registry, id, &zxdg_decoration_manager_v1_interface, 1);
+ } else if (RGFW_STRNCMP(interface, zwp_pointer_constraints_v1_interface.name, 255) == 0) {
+ RGFW->constraint_manager = wl_registry_bind(registry, id, &zwp_pointer_constraints_v1_interface, 1);
+ } else if (RGFW_STRNCMP(interface, zwp_relative_pointer_manager_v1_interface.name, 255) == 0) {
+ RGFW->relative_pointer_manager = wl_registry_bind(registry, id, &zwp_relative_pointer_manager_v1_interface, 1);
+ } else if (RGFW_STRNCMP(interface, xdg_toplevel_icon_manager_v1_interface.name, 255) == 0) {
+ RGFW->icon_manager = wl_registry_bind(registry, id, &xdg_toplevel_icon_manager_v1_interface, 1);
+ } else if (RGFW_STRNCMP(interface, "wl_shm", 7) == 0) {
+ RGFW->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
+ wl_shm_add_listener(RGFW->shm, &shm_listener, RGFW);
+ } else if (RGFW_STRNCMP(interface,"wl_seat", 8) == 0) {
+ RGFW->seat = wl_registry_bind(registry, id, &wl_seat_interface, version < 4 ? 3 : 4);
+ wl_seat_add_listener(RGFW->seat, &seat_listener, RGFW);
+ } else if (RGFW_STRNCMP(interface, zxdg_output_manager_v1_interface.name, 255) == 0) {
+ RGFW->xdg_output_manager = wl_registry_bind(registry, id, &zxdg_output_manager_v1_interface, 1);
+ } else if (RGFW_STRNCMP(interface,"wl_output", 10) == 0) {
+ RGFW_wl_create_outputs(registry, id);
+ } else if (RGFW_STRNCMP(interface, wp_pointer_warp_v1_interface.name, 255) == 0) {
+ RGFW->wp_pointer_warp = wl_registry_bind(registry, id, &wp_pointer_warp_v1_interface, 1);
+ } else if (RGFW_STRNCMP(interface,"wl_data_device_manager", 23) == 0) {
+ RGFW->data_device_manager = wl_registry_bind(registry, id, &wl_data_device_manager_interface, 1);
+ }
+}
+
+static void RGFW_wl_global_registry_remove(void* data, struct wl_registry *registry, u32 id) {
+ RGFW_UNUSED(data); RGFW_UNUSED(registry);
+ RGFW_info* RGFW = (RGFW_info*)data;
+ RGFW_monitorNode* prev = RGFW->monitors.list.head;
+ RGFW_monitorNode* node = NULL;
+ if (prev == NULL) return;
+
+ if (prev->id != id) {
+ /* find the first node that has a matching id */
+ while(prev->next != NULL && prev->next->id != id) {
+ prev = prev->next;
+ }
+
+ if (prev->next == NULL) return;
+ node = prev->next;
+ } else {
+ node = prev;
+ }
+
+ if (node->output) {
+ wl_output_destroy(node->output);
+ }
+
+ if (node->xdg_output) {
+ zxdg_output_v1_destroy(node->xdg_output);
+ }
+
+ if (node->modeCount) {
+ RGFW_FREE(node->modes);
+ node->modeCount = 0;
+ }
+
+ RGFW_monitorCallback(_RGFW->root, &node->mon, RGFW_FALSE);
+ RGFW_monitors_remove(node, prev);
+}
+
+static void RGFW_wl_randname(char *buf) {
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ long r = ts.tv_nsec;
+
+ int i;
+ for (i = 0; i < 6; i++) {
+ buf[i] = (char)('A'+(r&15)+(r&16)*2);
+ r >>= 5;
+ }
+}
+
+static int RGFW_wl_anonymous_shm_open(void) {
+ char name[] = "/RGFW-wayland-XXXXXX";
+ int retries = 100;
+
+ do {
+ RGFW_wl_randname(name + RGFW_unix_stringlen(name) - 6);
+
+ --retries;
+ /* shm_open guarantees that O_CLOEXEC is set */
+ int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
+ if (fd >= 0) {
+ shm_unlink(name);
+ return fd;
+ }
+ } while (retries > 0 && errno == EEXIST);
+
+ return -1;
+}
+
+static int RGFW_wl_create_shm_file(off_t size) {
+ int fd = RGFW_wl_anonymous_shm_open();
+ if (fd < 0) {
+ return fd;
+ }
+
+ if (ftruncate(fd, size) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+i32 RGFW_initPlatform_Wayland(void) {
+ _RGFW->wl_display = wl_display_connect(NULL);
+ if (_RGFW->wl_display == NULL) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errWayland, "Failed to load Wayland display");
+ return -1;
+ }
+
+ _RGFW->compositor = NULL;
+ static const struct wl_registry_listener registry_listener = {
+ .global = RGFW_wl_global_registry_handler,
+ .global_remove = RGFW_wl_global_registry_remove,
+ };
+
+ _RGFW->registry = wl_display_get_registry(_RGFW->wl_display);
+ wl_registry_add_listener(_RGFW->registry, &registry_listener, _RGFW);
+
+ wl_display_roundtrip(_RGFW->wl_display); /* bind to globals */
+
+ if (_RGFW->compositor == NULL) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errWayland, "Can't find compositor.");
+ return -1;
+ }
+
+ if (_RGFW->wl_cursor_theme == NULL) {
+ _RGFW->wl_cursor_theme = wl_cursor_theme_load(NULL, 24, _RGFW->shm);
+ _RGFW->cursor_surface = wl_compositor_create_surface(_RGFW->compositor);
+ }
+
+ u8 RGFW_blk[] = { 0, 0, 0, 0 };
+ _RGFW->hiddenMouse = RGFW_createMouse(RGFW_blk, 1, 1, RGFW_formatRGBA8);
+
+ static const struct xdg_wm_base_listener xdg_wm_base_listener = {
+ .ping = RGFW_wl_xdg_wm_base_ping_handler,
+ };
+
+ xdg_wm_base_add_listener(_RGFW->xdg_wm_base, &xdg_wm_base_listener, NULL);
+
+ _RGFW->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+
+ static const struct wl_data_device_listener wl_data_device_listener = {
+ .data_offer = RGFW_wl_data_device_data_offer,
+ .enter = (void (*)(void *, struct wl_data_device *, u32, struct wl_surface*, wl_fixed_t, wl_fixed_t, struct wl_data_offer *))&RGFW_doNothing,
+ .leave = (void (*)(void *, struct wl_data_device *))&RGFW_doNothing,
+ .motion = (void (*)(void *, struct wl_data_device *, u32, wl_fixed_t, wl_fixed_t))&RGFW_doNothing,
+ .drop = (void (*)(void *, struct wl_data_device *))&RGFW_doNothing,
+ .selection = RGFW_wl_data_device_selection
+ };
+
+ if (_RGFW->seat && _RGFW->data_device_manager) {
+ _RGFW->data_device = wl_data_device_manager_get_data_device(_RGFW->data_device_manager, _RGFW->seat);
+ wl_data_device_add_listener(_RGFW->data_device, &wl_data_device_listener, NULL);
+ }
+
+ return 0;
+}
+
+void RGFW_deinitPlatform_Wayland(void) {
+ if (_RGFW->unixClipboard) {
+ RGFW_FREE(_RGFW->unixClipboard);
+ _RGFW->unixClipboard = NULL;
+ }
+
+ if (_RGFW->wl_pointer) {
+ wl_pointer_destroy(_RGFW->wl_pointer);
+ }
+ if (_RGFW->wl_keyboard) {
+ wl_keyboard_destroy(_RGFW->wl_keyboard);
+ }
+
+ wl_registry_destroy(_RGFW->registry);
+ if (_RGFW->decoration_manager != NULL)
+ zxdg_decoration_manager_v1_destroy(_RGFW->decoration_manager);
+ if (_RGFW->relative_pointer_manager != NULL) {
+ zwp_relative_pointer_manager_v1_destroy(_RGFW->relative_pointer_manager);
+ }
+
+ if (_RGFW->relative_pointer) {
+ zwp_relative_pointer_v1_destroy(_RGFW->relative_pointer);
+ }
+
+ if (_RGFW->constraint_manager != NULL) {
+ zwp_pointer_constraints_v1_destroy(_RGFW->constraint_manager);
+ }
+
+ if (_RGFW->xdg_output_manager != NULL)
+ if (_RGFW->icon_manager != NULL) {
+ xdg_toplevel_icon_manager_v1_destroy(_RGFW->icon_manager);
+ }
+
+ if (_RGFW->xdg_output_manager) {
+ zxdg_output_manager_v1_destroy(_RGFW->xdg_output_manager);
+ }
+
+ if (_RGFW->data_device_manager) {
+ wl_data_device_manager_destroy(_RGFW->data_device_manager);
+ }
+
+ if (_RGFW->data_device) {
+ wl_data_device_destroy(_RGFW->data_device);
+ }
+
+ if (_RGFW->wl_cursor_theme != NULL) {
+ wl_cursor_theme_destroy(_RGFW->wl_cursor_theme);
+ }
+
+ if (_RGFW->wp_pointer_warp != NULL) {
+ wp_pointer_warp_v1_destroy(_RGFW->wp_pointer_warp);
+ }
+
+ RGFW_freeMouse(_RGFW->hiddenMouse);
+
+ RGFW_monitorNode* node = _RGFW->monitors.list.head;
+
+ while (node != NULL) {
+ if (node->output) {
+ wl_output_destroy(node->output);
+ }
+
+ if (node->xdg_output) {
+ zxdg_output_v1_destroy(node->xdg_output);
+ }
+
+ _RGFW->monitors.count -= 1;
+ node = node->next;
+
+ }
+
+ wl_surface_destroy(_RGFW->cursor_surface);
+ wl_shm_destroy(_RGFW->shm);
+ wl_seat_release(_RGFW->seat);
+ xdg_wm_base_destroy(_RGFW->xdg_wm_base);
+ wl_compositor_destroy(_RGFW->compositor);
+ wl_display_disconnect(_RGFW->wl_display);
+}
+
+RGFW_format RGFW_FUNC(RGFW_nativeFormat)(void) { return RGFW_formatBGRA8; }
+
+RGFW_bool RGFW_FUNC(RGFW_createSurfacePtr) (u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) {
+ RGFW_ASSERT(surface != NULL);
+ surface->data = data;
+ surface->w = w;
+ surface->h = h;
+ surface->format = format;
+ RGFW_debugCallback(RGFW_typeInfo, RGFW_infoBuffer, "Creating a 4 channel buffer");
+
+ u32 size = (u32)(surface->w * surface->h * 4);
+ int fd = RGFW_wl_create_shm_file(size);
+ if (fd < 0) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errBuffer, "Failed to create a buffer.");
+ return RGFW_FALSE;
+ }
+
+ surface->native.pool = wl_shm_create_pool(_RGFW->shm, fd, (i32)size);
+
+ surface->native.buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (surface->native.buffer == MAP_FAILED) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errBuffer, "mmap failed.");
+ return RGFW_FALSE;
+ }
+
+ surface->native.fd = fd;
+ surface->native.format = RGFW_formatBGRA8;
+ return RGFW_TRUE;
+}
+
+void RGFW_FUNC(RGFW_window_blitSurface) (RGFW_window* win, RGFW_surface* surface) {
+ RGFW_ASSERT(surface != NULL);
+
+ surface->native.wl_buffer = wl_shm_pool_create_buffer(surface->native.pool, 0, RGFW_MIN(win->w, surface->w), RGFW_MIN(win->h, surface->h), (i32)surface->w * 4, WL_SHM_FORMAT_ARGB8888);
+ RGFW_copyImageData(surface->native.buffer, surface->w, RGFW_MIN(win->h, surface->h), surface->native.format, surface->data, surface->format, surface->convertFunc);
+
+ wl_surface_attach(win->src.surface, surface->native.wl_buffer, 0, 0);
+ wl_surface_damage(win->src.surface, 0, 0, RGFW_MIN(win->w, surface->w), RGFW_MIN(win->h, surface->h));
+ wl_surface_commit(win->src.surface);
+
+ wl_buffer_destroy(surface->native.wl_buffer);
+
+ surface->native.wl_buffer = NULL;
+}
+
+void RGFW_FUNC(RGFW_surface_freePtr) (RGFW_surface* surface) {
+ RGFW_ASSERT(surface != NULL);
+
+ if (surface->native.pool) wl_shm_pool_destroy(surface->native.pool);
+ if (surface->native.fd) close(surface->native.fd);
+ if (surface->native.buffer) munmap(surface->native.buffer, (size_t)(surface->w * surface->h * 4));
+}
+
+void RGFW_FUNC(RGFW_window_setBorder) (RGFW_window* win, RGFW_bool border) {
+ RGFW_setBit(&win->internal.flags, RGFW_windowNoBorder, !border);
+
+ /* for now just toggle between SSD & CSD depending on the bool */
+ if (_RGFW->decoration_manager != NULL) {
+ zxdg_toplevel_decoration_v1_set_mode(win->src.decoration, (border ? ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE : ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE));
+ }
+}
+
+void RGFW_FUNC(RGFW_window_setRawMouseModePlatform) (RGFW_window* win, RGFW_bool state) {
+ RGFW_ASSERT(win);
+ if (_RGFW->relative_pointer_manager == NULL) return;
+
+ if (state == RGFW_FALSE) {
+ if (_RGFW->relative_pointer != NULL)
+ zwp_relative_pointer_v1_destroy(_RGFW->relative_pointer);
+ _RGFW->relative_pointer = NULL;
+ return;
+ }
+
+ if (_RGFW->relative_pointer != NULL) return;
+
+ _RGFW->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(_RGFW->relative_pointer_manager, _RGFW->wl_pointer);
+
+ static const struct zwp_relative_pointer_v1_listener relative_motion_listener = {
+ .relative_motion = RGFW_wl_relative_pointer_motion
+ };
+
+ zwp_relative_pointer_v1_add_listener(_RGFW->relative_pointer, &relative_motion_listener, _RGFW);
+}
+
+void RGFW_FUNC(RGFW_window_captureMousePlatform) (RGFW_window* win, RGFW_bool state) {
+ RGFW_ASSERT(win);
+
+ /* compositor has no support or window already is locked do nothing */
+ if (_RGFW->constraint_manager == NULL) return;
+
+ if (state == RGFW_FALSE) {
+ if (win->src.locked_pointer != NULL)
+ zwp_locked_pointer_v1_destroy(win->src.locked_pointer);
+ win->src.locked_pointer = NULL;
+ return;
+ }
+
+
+ if (win->src.locked_pointer != NULL) return;
+ win->src.locked_pointer = zwp_pointer_constraints_v1_lock_pointer(_RGFW->constraint_manager, win->src.surface, _RGFW->wl_pointer, NULL, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
+
+ static const struct zwp_locked_pointer_v1_listener locked_listener = {
+ .locked = RGFW_wl_pointer_locked,
+ .unlocked = (void (*)(void *, struct zwp_locked_pointer_v1 *))RGFW_doNothing
+ };
+
+ zwp_locked_pointer_v1_add_listener(win->src.locked_pointer, &locked_listener, _RGFW);
+}
+
+RGFW_window* RGFW_FUNC(RGFW_createWindowPlatform) (const char* name, RGFW_windowFlags flags, RGFW_window* win) {
+ RGFW_debugCallback(RGFW_typeWarning, RGFW_warningWayland, "RGFW Wayland support is experimental");
+
+ static const struct xdg_surface_listener xdg_surface_listener = {
+ .configure = RGFW_wl_xdg_surface_configure_handler,
+ };
+
+ static const struct wl_surface_listener wl_surface_listener = {
+ .enter = RGFW_wl_surface_enter,
+ .leave = (void (*)(void *, struct wl_surface *, struct wl_output *))&RGFW_doNothing,
+ .preferred_buffer_scale = (void (*)(void *, struct wl_surface *, i32))&RGFW_doNothing,
+ .preferred_buffer_transform = (void (*)(void *, struct wl_surface *, u32))&RGFW_doNothing
+ };
+
+ win->src.surface = wl_compositor_create_surface(_RGFW->compositor);
+ wl_surface_add_listener(win->src.surface, &wl_surface_listener, win);
+
+ /* create a surface for a custom cursor */
+ win->src.custom_cursor_surface = wl_compositor_create_surface(_RGFW->compositor);
+
+ win->src.xdg_surface = xdg_wm_base_get_xdg_surface(_RGFW->xdg_wm_base, win->src.surface);
+ xdg_surface_add_listener(win->src.xdg_surface, &xdg_surface_listener, win);
+
+ xdg_wm_base_set_user_data(_RGFW->xdg_wm_base, win);
+
+ win->src.xdg_toplevel = xdg_surface_get_toplevel(win->src.xdg_surface);
+
+ xdg_toplevel_set_app_id(win->src.xdg_toplevel, name);
+
+ xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->w, win->h);
+
+ if (!(win->internal.flags & RGFW_windowTransparent)) { /* no transparency */
+ RGFW_wl_setOpaque(win);
+ }
+
+ static const struct xdg_toplevel_listener xdg_toplevel_listener = {
+ .configure = RGFW_wl_xdg_toplevel_configure_handler,
+ .close = RGFW_wl_xdg_toplevel_close_handler,
+ };
+
+ xdg_toplevel_add_listener(win->src.xdg_toplevel, &xdg_toplevel_listener, win);
+
+ /* compositor supports both SSD & CSD
+ So choose accordingly
+ */
+ if (_RGFW->decoration_manager) {
+ u32 decoration_mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
+ win->src.decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
+ _RGFW->decoration_manager, win->src.xdg_toplevel);
+
+ static const struct zxdg_toplevel_decoration_v1_listener xdg_decoration_listener = {
+ .configure = RGFW_wl_xdg_decoration_configure_handler
+ };
+
+ zxdg_toplevel_decoration_v1_add_listener(win->src.decoration, &xdg_decoration_listener, win);
+
+ /* we want no decorations */
+ if ((flags & RGFW_windowNoBorder)) {
+ decoration_mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
+ }
+
+ zxdg_toplevel_decoration_v1_set_mode(win->src.decoration, decoration_mode);
+
+ /* no xdg_decoration support */
+ } else if (!(flags & RGFW_windowNoBorder)) {
+ /* TODO, some fallback */
+ #ifdef RGFW_LIBDECOR
+ static struct libdecor_interface interface = {
+ .error = NULL,
+ };
+
+ static struct libdecor_frame_interface frameInterface = {0}; /*= {
+ RGFW_wl_handle_configure,
+ RGFW_wl_handle_close,
+ RGFW_wl_handle_commit,
+ RGFW_wl_handle_dismiss_popup,
+ };*/
+
+ win->src.decorContext = libdecor_new(_RGFW->wl_display, &interface);
+ if (win->src.decorContext) {
+ struct libdecor_frame *frame = libdecor_decorate(win->src.decorContext, win->src.surface, &frameInterface, win);
+ if (!frame) {
+ libdecor_unref(win->src.decorContext);
+ win->src.decorContext = NULL;
+ } else {
+ libdecor_frame_set_app_id(frame, "my-libdecor-app");
+ libdecor_frame_set_title(frame, "My Libdecor Window");
+ }
+ }
+ #endif
+ }
+
+ if (_RGFW->icon_manager != NULL) {
+ /* set the default wayland icon */
+ xdg_toplevel_icon_manager_v1_set_icon(_RGFW->icon_manager, win->src.xdg_toplevel, NULL);
+ }
+
+ wl_surface_commit(win->src.surface);
+
+ while (win->src.configured == RGFW_FALSE) {
+ wl_display_dispatch(_RGFW->wl_display);
+ }
+
+ RGFW_UNUSED(name);
+
+ return win;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_getGlobalMouse) (i32* x, i32* y) {
+ RGFW_init();
+ if (x) *x = 0;
+ if (y) *y = 0;
+ return RGFW_FALSE;
+}
+
+RGFW_key RGFW_FUNC(RGFW_physicalToMappedKey)(RGFW_key key) {
+ u32 keycode = RGFW_rgfwToApiKey(key);
+ xkb_keycode_t kc = keycode + 8;
+ xkb_keysym_t sym = xkb_state_key_get_one_sym(_RGFW->xkb_state, kc);
+ if (sym < 256) {
+ return (RGFW_key)sym;
+ }
+
+ switch (sym) {
+ case XKB_KEY_F1: return RGFW_keyF1;
+ case XKB_KEY_F2: return RGFW_keyF2;
+ case XKB_KEY_F3: return RGFW_keyF3;
+ case XKB_KEY_F4: return RGFW_keyF4;
+ case XKB_KEY_F5: return RGFW_keyF5;
+ case XKB_KEY_F6: return RGFW_keyF6;
+ case XKB_KEY_F7: return RGFW_keyF7;
+ case XKB_KEY_F8: return RGFW_keyF8;
+ case XKB_KEY_F9: return RGFW_keyF9;
+ case XKB_KEY_F10: return RGFW_keyF10;
+ case XKB_KEY_F11: return RGFW_keyF11;
+ case XKB_KEY_F12: return RGFW_keyF12;
+ case XKB_KEY_F13: return RGFW_keyF13;
+ case XKB_KEY_F14: return RGFW_keyF14;
+ case XKB_KEY_F15: return RGFW_keyF15;
+ case XKB_KEY_F16: return RGFW_keyF16;
+ case XKB_KEY_F17: return RGFW_keyF17;
+ case XKB_KEY_F18: return RGFW_keyF18;
+ case XKB_KEY_F19: return RGFW_keyF19;
+ case XKB_KEY_F20: return RGFW_keyF20;
+ case XKB_KEY_F21: return RGFW_keyF21;
+ case XKB_KEY_F22: return RGFW_keyF22;
+ case XKB_KEY_F23: return RGFW_keyF23;
+ case XKB_KEY_F24: return RGFW_keyF24;
+ case XKB_KEY_F25: return RGFW_keyF25;
+ case XKB_KEY_Shift_L: return RGFW_keyShiftL;
+ case XKB_KEY_Shift_R: return RGFW_keyShiftR;
+ case XKB_KEY_Control_L: return RGFW_keyControlL;
+ case XKB_KEY_Control_R: return RGFW_keyControlR;
+ case XKB_KEY_Alt_L: return RGFW_keyAltL;
+ case XKB_KEY_Alt_R: return RGFW_keyAltR;
+ case XKB_KEY_Super_L: return RGFW_keySuperL;
+ case XKB_KEY_Super_R: return RGFW_keySuperR;
+ case XKB_KEY_Caps_Lock: return RGFW_keyCapsLock;
+ case XKB_KEY_Num_Lock: return RGFW_keyNumLock;
+ case XKB_KEY_Scroll_Lock:return RGFW_keyScrollLock;
+ case XKB_KEY_Up: return RGFW_keyUp;
+ case XKB_KEY_Down: return RGFW_keyDown;
+ case XKB_KEY_Left: return RGFW_keyLeft;
+ case XKB_KEY_Right: return RGFW_keyRight;
+ case XKB_KEY_Home: return RGFW_keyHome;
+ case XKB_KEY_End: return RGFW_keyEnd;
+ case XKB_KEY_Page_Up: return RGFW_keyPageUp;
+ case XKB_KEY_Page_Down: return RGFW_keyPageDown;
+ case XKB_KEY_Insert: return RGFW_keyInsert;
+ case XKB_KEY_Menu: return RGFW_keyMenu;
+ case XKB_KEY_KP_Add: return RGFW_keyPadPlus;
+ case XKB_KEY_KP_Subtract: return RGFW_keyPadMinus;
+ case XKB_KEY_KP_Multiply: return RGFW_keyPadMultiply;
+ case XKB_KEY_KP_Divide: return RGFW_keyPadSlash;
+ case XKB_KEY_KP_Equal: return RGFW_keyPadEqual;
+ case XKB_KEY_KP_Enter: return RGFW_keyPadReturn;
+ case XKB_KEY_KP_Decimal: return RGFW_keyPadPeriod;
+ case XKB_KEY_KP_0: return RGFW_keyPad0;
+ case XKB_KEY_KP_1: return RGFW_keyPad1;
+ case XKB_KEY_KP_2: return RGFW_keyPad2;
+ case XKB_KEY_KP_3: return RGFW_keyPad3;
+ case XKB_KEY_KP_4: return RGFW_keyPad4;
+ case XKB_KEY_KP_5: return RGFW_keyPad5;
+ case XKB_KEY_KP_6: return RGFW_keyPad6;
+ case XKB_KEY_KP_7: return RGFW_keyPad7;
+ case XKB_KEY_KP_8: return RGFW_keyPad8;
+ case XKB_KEY_KP_9: return RGFW_keyPad9;
+ case XKB_KEY_Print: return RGFW_keyPrintScreen;
+ case XKB_KEY_Pause: return RGFW_keyPause;
+ default: break;
+ }
+
+ return RGFW_keyNULL;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_fetchSize) (RGFW_window* win, i32* w, i32* h) {
+ return RGFW_window_getSize(win, w, h);
+}
+
+void RGFW_FUNC(RGFW_pollEvents) (void) {
+ RGFW_resetPrevState();
+
+ /* send buffered requests to compositor */
+ while (wl_display_flush(_RGFW->wl_display) == -1) {
+ /* compositor not responding to new requests */
+ /* so let's dispatch some events so the compositor responds */
+ if (errno == EAGAIN) {
+ if (wl_display_dispatch_pending(_RGFW->wl_display) == -1) {
+ return;
+ }
+ } else {
+ return;
+ }
+ }
+ if (_RGFW->wl_repeat_info_rate != 0 && _RGFW->last_key) {
+ u32 now = (u32)(RGFW_unix_getTimeNS() / 1000000);
+ if (now > _RGFW->last_key_time) {
+ RGFW_wl_send_key_event(_RGFW->last_key);
+ _RGFW->last_key_time = now + 1000 / (u32)_RGFW->wl_repeat_info_rate;
+ }
+ }
+
+ /* read the events; if empty this reads from the */
+ /* wayland file descriptor */
+ struct pollfd fds;
+ memset(&fds, 0, sizeof(fds));
+ fds.fd = wl_display_get_fd(_RGFW->wl_display);
+ fds.events = POLLIN;
+ fds.revents = 0;
+
+ while (1) {
+ while (wl_display_prepare_read(_RGFW->wl_display) != 0) {
+ if (wl_display_dispatch_pending(_RGFW->wl_display) > 0)
+ return;
+ }
+
+ if (poll(&fds, 1, 0) == 0) {
+ wl_display_cancel_read(_RGFW->wl_display);
+ return;
+ }
+
+ if (fds.revents & POLLIN) {
+ wl_display_read_events(_RGFW->wl_display);
+ if (wl_display_dispatch_pending(_RGFW->wl_display) > 0) {
+ return;
+ }
+ } else {
+ wl_display_cancel_read(_RGFW->wl_display);
+ }
+ }
+}
+
+void RGFW_FUNC(RGFW_window_move) (RGFW_window* win, i32 x, i32 y) {
+ RGFW_ASSERT(win != NULL);
+ win->x = x;
+ win->y = y;
+}
+
+
+void RGFW_FUNC(RGFW_window_resize) (RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+ win->w = w;
+ win->h = h;
+ if (_RGFW->compositor) {
+ xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->w, win->h);
+ #ifdef RGFW_OPENGL
+ if (win->src.ctx.egl)
+ wl_egl_window_resize(win->src.ctx.egl->eglWindow, (i32)w, (i32)h, 0, 0);
+ #endif
+ }
+}
+
+void RGFW_FUNC(RGFW_window_setAspectRatio) (RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+
+ if (w == 0 && h == 0)
+ return;
+ xdg_toplevel_set_max_size(win->src.xdg_toplevel, (i32)w, (i32)h);
+}
+
+void RGFW_FUNC(RGFW_window_setMinSize) (RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+ xdg_toplevel_set_min_size(win->src.xdg_toplevel, w, h);
+}
+
+void RGFW_FUNC(RGFW_window_setMaxSize) (RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+ xdg_toplevel_set_max_size(win->src.xdg_toplevel, w, h);
+}
+
+void RGFW_toggleWaylandMaximized(RGFW_window* win, RGFW_bool maximized) {
+ win->src.maximized = maximized;
+ if (maximized) {
+ xdg_toplevel_set_maximized(win->src.xdg_toplevel);
+ } else {
+ xdg_toplevel_unset_maximized(win->src.xdg_toplevel);
+ }
+}
+
+void RGFW_FUNC(RGFW_window_maximize) (RGFW_window* win) {
+ win->internal.oldX = win->x;
+ win->internal.oldY = win->y;
+ win->internal.oldW = win->w;
+ win->internal.oldH = win->h;
+ RGFW_toggleWaylandMaximized(win, 1);
+ RGFW_window_fetchSize(win, NULL, NULL);
+ return;
+}
+
+void RGFW_FUNC(RGFW_window_focus)(RGFW_window* win) {
+ RGFW_ASSERT(win);
+}
+
+void RGFW_FUNC(RGFW_window_raise)(RGFW_window* win) {
+ RGFW_ASSERT(win);
+}
+
+void RGFW_FUNC(RGFW_window_setFullscreen)(RGFW_window* win, RGFW_bool fullscreen) {
+ RGFW_ASSERT(win != NULL);
+ if (fullscreen) {
+
+ win->internal.flags |= RGFW_windowFullscreen;
+ win->internal.oldX = win->x;
+ win->internal.oldY = win->y;
+ win->internal.oldW = win->w;
+ win->internal.oldH = win->h;
+ xdg_toplevel_set_fullscreen(win->src.xdg_toplevel, NULL); /* let the compositor decide */
+ } else {
+ win->internal.flags &= ~(u32)RGFW_windowFullscreen;
+ xdg_toplevel_unset_fullscreen(win->src.xdg_toplevel);
+ }
+
+}
+
+void RGFW_FUNC(RGFW_window_setFloating) (RGFW_window* win, RGFW_bool floating) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_UNUSED(floating);
+}
+
+void RGFW_FUNC(RGFW_window_setOpacity) (RGFW_window* win, u8 opacity) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_UNUSED(opacity);
+}
+
+void RGFW_FUNC(RGFW_window_minimize)(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ if (RGFW_window_isMaximized(win)) return;
+ win->internal.oldX = win->x;
+ win->internal.oldY = win->y;
+ win->internal.oldW = win->w;
+ win->internal.oldH = win->h;
+ win->src.minimized = RGFW_TRUE;
+ xdg_toplevel_set_minimized(win->src.xdg_toplevel);
+}
+
+void RGFW_FUNC(RGFW_window_restore)(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_toggleWaylandMaximized(win, RGFW_FALSE);
+
+ RGFW_window_move(win, win->internal.oldX, win->internal.oldY);
+ RGFW_window_resize(win, win->internal.oldW, win->internal.oldH);
+
+ RGFW_window_show(win);
+ RGFW_window_move(win, win->internal.oldX, win->internal.oldY);
+ RGFW_window_resize(win, win->internal.oldW, win->internal.oldH);
+
+ RGFW_window_show(win);
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_isFloating)(RGFW_window* win) {
+ return (!RGFW_window_isFullscreen(win) && !RGFW_window_isMaximized(win));
+}
+
+void RGFW_FUNC(RGFW_window_setName) (RGFW_window* win, const char* name) {
+ RGFW_ASSERT(win != NULL);
+ if (name == NULL) name = "\0";
+
+ if (_RGFW->compositor)
+ xdg_toplevel_set_title(win->src.xdg_toplevel, name);
+}
+
+#ifndef RGFW_NO_PASSTHROUGH
+void RGFW_FUNC(RGFW_window_setMousePassthrough) (RGFW_window* win, RGFW_bool passthrough) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_UNUSED(passthrough);
+}
+#endif /* RGFW_NO_PASSTHROUGH */
+
+RGFW_bool RGFW_FUNC(RGFW_window_setIconEx) (RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_icon type) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_UNUSED(type);
+
+ if (_RGFW->icon_manager == NULL || w != h) return RGFW_FALSE;
+
+ if (win->src.icon) {
+ xdg_toplevel_icon_v1_destroy(win->src.icon);
+ win->src.icon= NULL;
+ }
+
+ RGFW_surface* surface = RGFW_createSurface(data, w, h, format);
+
+ if (surface == NULL) return RGFW_FALSE;
+
+ RGFW_copyImageData(surface->native.buffer, RGFW_MIN(w, surface->w), RGFW_MIN(h, surface->h), surface->native.format, surface->data, surface->format, NULL);
+
+ win->src.icon = xdg_toplevel_icon_manager_v1_create_icon(_RGFW->icon_manager);
+ xdg_toplevel_icon_v1_add_buffer(win->src.icon, surface->native.wl_buffer, 1);
+ xdg_toplevel_icon_manager_v1_set_icon(_RGFW->icon_manager, win->src.xdg_toplevel, win->src.icon);
+
+ RGFW_surface_free(surface);
+ return RGFW_TRUE;
+}
+
+RGFW_mouse* RGFW_FUNC(RGFW_createMouseStandard) (RGFW_mouseIcon mouse) {
+ char* cursorName = NULL;
+ switch (mouse) {
+ case RGFW_mouseNormal: cursorName = (char*)"left_ptr"; break;
+ case RGFW_mouseArrow: cursorName = (char*)"left_ptr"; break;
+ case RGFW_mouseIbeam: cursorName = (char*)"xterm"; break;
+ case RGFW_mouseCrosshair: cursorName = (char*)"crosshair"; break;
+ case RGFW_mousePointingHand: cursorName = (char*)"hand2"; break;
+ case RGFW_mouseResizeEW: cursorName = (char*)"sb_h_double_arrow"; break;
+ case RGFW_mouseResizeNS: cursorName = (char*)"sb_v_double_arrow"; break;
+ case RGFW_mouseResizeNWSE: cursorName = (char*)"top_left_corner"; break; /* or fd_double_arrow */
+ case RGFW_mouseResizeNESW: cursorName = (char*)"top_right_corner"; break; /* or bd_double_arrow */
+ case RGFW_mouseResizeNW: cursorName = (char*)"top_left_corner"; break;
+ case RGFW_mouseResizeN: cursorName = (char*)"top_side"; break;
+ case RGFW_mouseResizeNE: cursorName = (char*)"top_right_corner"; break;
+ case RGFW_mouseResizeE: cursorName = (char*)"right_side"; break;
+ case RGFW_mouseResizeSE: cursorName = (char*)"bottom_right_corner"; break;
+ case RGFW_mouseResizeS: cursorName = (char*)"bottom_side"; break;
+ case RGFW_mouseResizeSW: cursorName = (char*)"bottom_left_corner"; break;
+ case RGFW_mouseResizeW: cursorName = (char*)"left_side"; break;
+ case RGFW_mouseResizeAll: cursorName = (char*)"fleur"; break;
+ case RGFW_mouseNotAllowed: cursorName = (char*)"not-allowed"; break;
+ case RGFW_mouseWait: cursorName = (char*)"watch"; break;
+ case RGFW_mouseProgress: cursorName = (char*)"watch"; break;
+ default: return NULL;
+ }
+
+ struct wl_cursor* wlcursor = wl_cursor_theme_get_cursor(_RGFW->wl_cursor_theme, cursorName);
+ if (wlcursor == NULL)
+ return NULL;
+ struct wl_cursor_image* cursor_image = wlcursor->images[0];
+ struct wl_buffer* cursor_buffer = wl_cursor_image_get_buffer(cursor_image);
+
+ RGFW_surface* surface = RGFW_ALLOC(sizeof(RGFW_surface));
+ RGFW_MEMZERO(surface, sizeof(RGFW_surface));
+ surface->w = (i32)cursor_image->width;
+ surface->h = (i32)cursor_image->height;
+ surface->native.wl_buffer = cursor_buffer;
+
+ return (RGFW_mouse*)surface;
+}
+
+RGFW_mouse* RGFW_FUNC(RGFW_createMouse)(u8* data, i32 w, i32 h, RGFW_format format) {
+ RGFW_surface* surface = RGFW_createSurface(data, w, h, format);
+ if (surface == NULL) return NULL;
+
+ surface->native.wl_buffer = wl_shm_pool_create_buffer(surface->native.pool, 0, surface->w, surface->h, (i32)surface->w * 4, WL_SHM_FORMAT_ARGB8888);
+
+ RGFW_copyImageData(surface->native.buffer, RGFW_MIN(w, surface->w), RGFW_MIN(h, surface->h), surface->native.format, surface->data, surface->format, NULL);
+
+ return (RGFW_mouse*)surface;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_setMousePlatform)(RGFW_window* win, RGFW_mouse* mouse) {
+ RGFW_ASSERT(win); RGFW_ASSERT(mouse);
+ RGFW_surface* surface = (RGFW_surface*)mouse;
+
+ win->src.using_custom_cursor = RGFW_TRUE;
+
+ wl_surface_attach(win->src.custom_cursor_surface, surface->native.wl_buffer, 0, 0);
+ wl_surface_damage(win->src.custom_cursor_surface, 0, 0, surface->w, surface->h);
+ wl_surface_commit(win->src.custom_cursor_surface);
+
+ return RGFW_TRUE;
+}
+
+void RGFW_FUNC(RGFW_freeMouse)(RGFW_mouse* mouse) {
+ if (mouse != NULL) {
+ RGFW_surface* surface = (RGFW_surface*)mouse;
+
+ if (surface->native.buffer && surface->native.wl_buffer) {
+ wl_buffer_destroy(surface->native.wl_buffer);
+ }
+
+ RGFW_surface_free(surface);
+ }
+}
+
+void RGFW_FUNC(RGFW_window_moveMouse)(RGFW_window* win, i32 x, i32 y) {
+ if (_RGFW->wp_pointer_warp != NULL) {
+ wp_pointer_warp_v1_warp_pointer(_RGFW->wp_pointer_warp, win->src.surface, _RGFW->wl_pointer, wl_fixed_from_int(x), wl_fixed_from_int(y), _RGFW->mouse_enter_serial);
+ }
+}
+
+void RGFW_FUNC(RGFW_window_hide) (RGFW_window* win) {
+ wl_surface_attach(win->src.surface, NULL, 0, 0);
+ wl_surface_commit(win->src.surface);
+ win->internal.flags |= RGFW_windowHide;
+}
+
+void RGFW_FUNC(RGFW_window_show) (RGFW_window* win) {
+ win->internal.flags &= ~(u32)RGFW_windowHide;
+ if (win->internal.flags & RGFW_windowFocusOnShow) RGFW_window_focus(win);
+ /* wl_surface_attach(win->src.surface, win->x, win->y, win->w, win->h, 0, 0); */
+ wl_surface_commit(win->src.surface);
+}
+
+void RGFW_FUNC(RGFW_window_flash) (RGFW_window* win, RGFW_flashRequest request) {
+ if (RGFW_window_isInFocus(win) && request) {
+ return;
+ }
+}
+
+RGFW_bool RGFW_FUNC(RGFW_readClipboardPtr) (u8* buffer, size_t capacity, RGFW_dataTransfer* data) {
+ RGFW_ASSERT(data != NULL);
+
+ if (_RGFW->unixClipboard == NULL || _RGFW->unixClipboard->length == 0) {
+ data->length = 0;
+ data->type = RGFW_dataNone;
+ return RGFW_FALSE;
+ }
+
+ data->length = _RGFW->unixClipboard->length;
+ data->type = RGFW_dataText;
+
+ if (buffer == NULL) return RGFW_TRUE;
+
+ if (_RGFW->unixClipboard->length > capacity) return RGFW_FALSE;
+
+ RGFW_MEMCPY(buffer, _RGFW->unixClipboard->data, _RGFW->unixClipboard->length);
+ data->data = (const char*)buffer;
+
+ return RGFW_TRUE;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_writeClipboard) (const RGFW_dataTransfer* data) {
+ RGFW_ASSERT(data != NULL);
+
+ // compositor does not support wl_data_device_manager
+ // clients cannot read rgfw's clipboard
+ if (_RGFW->data_device_manager == NULL) return RGFW_FALSE;
+ // clear the clipboard
+ if (_RGFW->unixClipboard) {
+ RGFW_FREE(_RGFW->unixClipboard);
+ _RGFW->unixClipboard = NULL;
+ }
+
+ // set the contents
+ _RGFW->unixClipboard = (RGFW_dataTransfer*)RGFW_ALLOC(sizeof(RGFW_dataTransfer) + data->length);
+ RGFW_ASSERT(_RGFW->unixClipboard!= NULL);
+
+ size_t length = data->length;
+ if (data->data[length - 1] != '\0') length += 1;
+
+ char* data_ptr = &((char*)(void*)_RGFW->unixClipboard)[sizeof(RGFW_dataTransfer) - 1];
+ RGFW_MEMCPY(data_ptr, data->data, data->length);
+ data_ptr[length - 1] = '\0';
+
+ _RGFW->unixClipboard->data = data_ptr;
+ _RGFW->unixClipboard->type = RGFW_dataText;
+ _RGFW->unixClipboard->length = length;
+
+ // means we already wrote to the clipboard
+ // so destroy it to create a new one
+ RGFW_window* win = _RGFW->kbOwner;
+
+ if (win->src.data_source != NULL) {
+ wl_data_source_destroy(win->src.data_source);
+ win->src.data_source = NULL;
+ }
+
+ // advertise to other clients that we offer text
+ win->src.data_source = wl_data_device_manager_create_data_source(_RGFW->data_device_manager);
+
+ // basic error checking
+ if (win->src.data_source == NULL) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errClipboard, "Could not create clipboard data source");
+ return RGFW_FALSE;
+ }
+
+ // needed RGFW_doNothing because wayland will call the functions
+ // if not set they are random data that lead to a crash
+ static const struct wl_data_source_listener data_source_listener = {
+ .target = (void (*)(void *, struct wl_data_source *, const char *))&RGFW_doNothing,
+ .action = (void (*)(void *, struct wl_data_source *, u32))&RGFW_doNothing,
+ .dnd_drop_performed = (void (*)(void *, struct wl_data_source *))&RGFW_doNothing,
+ .dnd_finished = (void (*)(void *, struct wl_data_source *))&RGFW_doNothing,
+ .send = RGFW_wl_data_source_send,
+ .cancelled = RGFW_wl_data_source_cancelled
+ };
+
+ wl_data_source_add_listener(win->src.data_source, &data_source_listener, _RGFW);
+ wl_data_source_offer(win->src.data_source , "text/plain;charset=utf-8");
+
+ return RGFW_TRUE;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_isHidden) (RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ return RGFW_FALSE;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_isMinimized) (RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ return win->src.minimized;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_isMaximized) (RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ return win->src.maximized;
+}
+
+void RGFW_FUNC(RGFW_pollMonitors) (void) {
+ _RGFW->monitors.primary = _RGFW->monitors.list.head;
+}
+
+
+RGFW_bool RGFW_FUNC(RGFW_monitor_getWorkarea) (RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height) {
+ /* NOTE: Wayland has no way to get the actual workarea as far as I'm aware :( */
+ if (x) *x = monitor->x;
+ if (y) *y = monitor->y;
+ if (width) *width = monitor->mode.w;
+ if (height) *height = monitor->mode.h;
+ return RGFW_TRUE;
+}
+
+size_t RGFW_FUNC(RGFW_monitor_getModesPtr) (RGFW_monitor* monitor, RGFW_monitorMode** modes) {
+ if (modes) {
+ RGFW_MEMCPY((*modes), monitor->node->modes, monitor->node->modeCount * sizeof(RGFW_monitorMode));
+ }
+
+ return monitor->node->modeCount;
+}
+
+size_t RGFW_FUNC(RGFW_monitor_getGammaRampPtr) (RGFW_monitor* monitor, RGFW_gammaRamp* ramp) {
+ RGFW_UNUSED(monitor); RGFW_UNUSED(ramp);
+ return 0;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_monitor_setGammaRamp) (RGFW_monitor* monitor, RGFW_gammaRamp* ramp) {
+ RGFW_UNUSED(monitor); RGFW_UNUSED(ramp);
+ return RGFW_FALSE;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_monitor_requestMode) (RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request) {
+ for (size_t i = 0; i < mon->node->modeCount; i++) {
+ if (RGFW_monitorModeCompare(mode, &mon->node->modes[i], request) == RGFW_FALSE) {
+ continue;
+ }
+
+ RGFW_monitor_setMode(mon, &mon->node->modes[i]);
+ return RGFW_TRUE;
+ }
+
+ return RGFW_FALSE;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_monitor_setMode) (RGFW_monitor* mon, RGFW_monitorMode* mode) {
+ RGFW_UNUSED(mon); RGFW_UNUSED(mode);
+ return RGFW_FALSE;
+}
+
+RGFW_monitor* RGFW_FUNC(RGFW_window_getMonitor) (RGFW_window* win) {
+ RGFW_ASSERT(win);
+ if (win->src.active_monitor == NULL) {
+ /* TODO: fix race condition [probably a problem with wayland] */
+ return RGFW_getPrimaryMonitor();
+ }
+
+ return &win->src.active_monitor->mon;
+}
+
+#ifdef RGFW_OPENGL
+RGFW_bool RGFW_FUNC(RGFW_extensionSupportedPlatform_OpenGL) (const char * extension, size_t len) { return RGFW_extensionSupportedPlatform_EGL(extension, len); }
+RGFW_proc RGFW_FUNC(RGFW_getProcAddress_OpenGL) (const char* procname) { return RGFW_getProcAddress_EGL(procname); }
+
+
+RGFW_bool RGFW_FUNC(RGFW_window_createContextPtr_OpenGL)(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) {
+ RGFW_bool out = RGFW_window_createContextPtr_EGL(win, &ctx->egl, hints);
+ win->src.gfxType = RGFW_gfxNativeOpenGL;
+
+ RGFW_window_swapInterval_OpenGL(win, 0);
+ return out;
+}
+void RGFW_FUNC(RGFW_window_deleteContextPtr_OpenGL) (RGFW_window* win, RGFW_glContext* ctx) { RGFW_window_deleteContextPtr_EGL(win, &ctx->egl); win->src.ctx.native = NULL; }
+
+void RGFW_FUNC(RGFW_window_makeCurrentContext_OpenGL) (RGFW_window* win) { RGFW_window_makeCurrentContext_EGL(win); }
+void* RGFW_FUNC(RGFW_getCurrentContext_OpenGL) (void) { return RGFW_getCurrentContext_EGL(); }
+void RGFW_FUNC(RGFW_window_swapBuffers_OpenGL) (RGFW_window* win) { RGFW_window_swapBuffers_EGL(win); }
+void RGFW_FUNC(RGFW_window_swapInterval_OpenGL) (RGFW_window* win, i32 swapInterval) { RGFW_window_swapInterval_EGL(win, swapInterval); }
+#endif /* RGFW_OPENGL */
+
+void RGFW_FUNC(RGFW_window_closePlatform)(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_debugCallback(RGFW_typeInfo, RGFW_infoWindow, "a window was freed");
+ #ifdef RGFW_LIBDECOR
+ if (win->src.decorContext)
+ libdecor_unref(win->src.decorContext);
+ #endif
+
+ if (win->src.decoration) {
+ zxdg_toplevel_decoration_v1_destroy(win->src.decoration);
+ }
+
+ if (win->src.xdg_toplevel) {
+ xdg_toplevel_destroy(win->src.xdg_toplevel);
+ }
+
+ wl_surface_destroy(win->src.custom_cursor_surface);
+
+ if (win->src.locked_pointer) {
+ zwp_locked_pointer_v1_destroy(win->src.locked_pointer);
+ }
+
+ if (win->src.icon) {
+ xdg_toplevel_icon_v1_destroy(win->src.icon);
+ }
+
+ xdg_surface_destroy(win->src.xdg_surface);
+ wl_surface_destroy(win->src.surface);
+}
+
+#ifdef RGFW_WEBGPU
+WGPUSurface RGFW_FUNC(RGFW_window_createSurface_WebGPU) (RGFW_window* window, WGPUInstance instance) {
+ WGPUSurfaceDescriptor surfaceDesc = {0};
+ WGPUSurfaceSourceWaylandSurface fromWl = {0};
+ fromWl.chain.sType = WGPUSType_SurfaceSourceWaylandSurface;
+ fromWl.display = _RGFW->wl_display;
+ fromWl.surface = window->src.surface;
+
+ surfaceDesc.nextInChain = (WGPUChainedStruct*)&fromWl.chain;
+ return wgpuInstanceCreateSurface(instance, &surfaceDesc);
+}
+#endif
+
+
+
+#endif /* RGFW_WAYLAND */
+/*
+ End of Wayland defines
+*/
+
+/*
+
+ Start of Windows defines
+
+
+*/
+
+#ifdef RGFW_WINDOWS
+#ifndef WIN32_LEAN_AND_MEAN
+ #define WIN32_LEAN_AND_MEAN
+#endif
+
+#ifndef OEMRESOURCE
+ #define OEMRESOURCE
+#endif
+
+#include <windows.h>
+
+#ifndef OCR_NORMAL
+#define OCR_NORMAL 32512
+#define OCR_IBEAM 32513
+#define OCR_WAIT 32514
+#define OCR_CROSS 32515
+#define OCR_UP 32516
+#define OCR_SIZENWSE 32642
+#define OCR_SIZENESW 32643
+#define OCR_SIZEWE 32644
+#define OCR_SIZENS 32645
+#define OCR_SIZEALL 32646
+#define OCR_NO 32648
+#define OCR_HAND 32649
+#define OCR_APPSTARTING 32650
+#endif
+
+#include <windowsx.h>
+#include <shellapi.h>
+#include <shellscalingapi.h>
+#include <wchar.h>
+#include <locale.h>
+#include <winuser.h>
+
+#ifndef WM_DPICHANGED
+#define WM_DPICHANGED 0x02E0
+#endif
+
+RGFWDEF DWORD RGFW_winapi_window_getStyle(RGFW_window* win, RGFW_windowFlags flags);
+DWORD RGFW_winapi_window_getStyle(RGFW_window* win, RGFW_windowFlags flags) {
+ RGFW_UNUSED(win);
+ DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
+
+ if ((flags & RGFW_windowFullscreen)) {
+ style |= WS_POPUP;
+ } else {
+ style |= WS_SYSMENU | WS_MINIMIZEBOX;
+
+ if (!(flags & RGFW_windowNoBorder)) {
+ style |= WS_CAPTION;
+
+ if (!(flags & RGFW_windowNoResize))
+ style |= WS_MAXIMIZEBOX | WS_THICKFRAME;
+ }
+ else
+ style |= WS_POPUP;
+ }
+
+ return style;
+}
+
+RGFWDEF DWORD RGFW_winapi_window_getExStyle(RGFW_window* win, RGFW_windowFlags flags);
+DWORD RGFW_winapi_window_getExStyle(RGFW_window* win, RGFW_windowFlags flags) {
+ DWORD style = WS_EX_APPWINDOW;
+ if (flags & RGFW_windowFullscreen || (flags & RGFW_windowFloating || RGFW_window_isFloating(win))) {
+ style |= WS_EX_TOPMOST;
+ }
+
+ return style;
+}
+
+RGFW_bool RGFW_createUTF8FromWideStringWin32(const WCHAR* source, char* out, size_t max);
+
+#define GL_FRONT 0x0404
+#define GL_BACK 0x0405
+#define GL_LEFT 0x0406
+#define GL_RIGHT 0x0407
+
+typedef int (*PFN_wglGetSwapIntervalEXT)(void);
+PFN_wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc = NULL;
+#define wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc
+
+/* these two wgl functions need to be preloaded */
+typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList);
+PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
+
+HMODULE RGFW_wgl_dll = NULL;
+
+#ifndef RGFW_NO_LOAD_WGL
+ typedef HGLRC(WINAPI* PFN_wglCreateContext)(HDC);
+ typedef BOOL(WINAPI* PFN_wglDeleteContext)(HGLRC);
+ typedef PROC(WINAPI* PFN_wglGetProcAddress)(LPCSTR);
+ typedef BOOL(WINAPI* PFN_wglMakeCurrent)(HDC, HGLRC);
+ typedef HDC(WINAPI* PFN_wglGetCurrentDC)(void);
+ typedef HGLRC(WINAPI* PFN_wglGetCurrentContext)(void);
+ typedef BOOL(WINAPI* PFN_wglShareLists)(HGLRC, HGLRC);
+
+ PFN_wglCreateContext wglCreateContextSRC;
+ PFN_wglDeleteContext wglDeleteContextSRC;
+ PFN_wglGetProcAddress wglGetProcAddressSRC;
+ PFN_wglMakeCurrent wglMakeCurrentSRC;
+ PFN_wglGetCurrentDC wglGetCurrentDCSRC;
+ PFN_wglGetCurrentContext wglGetCurrentContextSRC;
+ PFN_wglShareLists wglShareListsSRC;
+
+ #define wglCreateContext wglCreateContextSRC
+ #define wglDeleteContext wglDeleteContextSRC
+ #define wglGetProcAddress wglGetProcAddressSRC
+ #define wglMakeCurrent wglMakeCurrentSRC
+ #define wglGetCurrentDC wglGetCurrentDCSRC
+ #define wglGetCurrentContext wglGetCurrentContextSRC
+ #define wglShareLists wglShareListsSRC
+#endif
+
+void* RGFW_window_getHWND(RGFW_window* win) { return win->src.window; }
+void* RGFW_window_getHDC(RGFW_window* win) { return win->src.hdc; }
+
+#ifdef RGFW_OPENGL
+RGFWDEF void RGFW_win32_loadOpenGLFuncs(HWND dummyWin);
+
+typedef HRESULT (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats);
+PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL;
+
+typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval);
+PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
+#endif
+
+#ifndef RGFW_NO_DWM
+HMODULE RGFW_dwm_dll = NULL;
+#ifndef _DWMAPI_H_
+typedef struct { DWORD dwFlags; int fEnable; HRGN hRgnBlur; int fTransitionOnMaximized;} DWM_BLURBEHIND;
+#endif
+typedef HRESULT (WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND, const DWM_BLURBEHIND*);
+PFN_DwmEnableBlurBehindWindow DwmEnableBlurBehindWindowSRC = NULL;
+
+typedef HRESULT (WINAPI * PFN_DwmSetWindowAttribute)(HWND, DWORD, LPCVOID, DWORD);
+PFN_DwmSetWindowAttribute DwmSetWindowAttributeSRC = NULL;
+#endif
+void RGFW_win32_makeWindowTransparent(RGFW_window* win);
+void RGFW_win32_makeWindowTransparent(RGFW_window* win) {
+ if (!(win->internal.flags & RGFW_windowTransparent)) return;
+
+ #ifndef RGFW_NO_DWM
+ if (DwmEnableBlurBehindWindowSRC != NULL) {
+ DWM_BLURBEHIND bb = {0, 0, 0, 0};
+ bb.dwFlags = 0x1;
+ bb.fEnable = TRUE;
+ bb.hRgnBlur = NULL;
+ DwmEnableBlurBehindWindowSRC(win->src.window, &bb);
+
+ } else
+ #endif
+ {
+ SetWindowLong(win->src.window, GWL_EXSTYLE, WS_EX_LAYERED);
+ SetLayeredWindowAttributes(win->src.window, 0, 128, LWA_ALPHA);
+ }
+}
+
+RGFWDEF RGFW_bool RGFW_win32_getDarkModeState(void);
+RGFW_bool RGFW_win32_getDarkModeState(void) {
+ u32 lightMode = 1;
+#if (_WIN32_WINNT >= 0x0600)
+ DWORD len = sizeof(lightMode);
+
+ RegGetValueW(
+ HKEY_CURRENT_USER,
+ L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
+ L"AppsUseLightTheme", RRF_RT_REG_DWORD, NULL, &lightMode, &len
+ );
+#endif
+
+ return (lightMode == 0);
+}
+
+RGFWDEF void RGFW_win32_makeWindowDarkMode(RGFW_window* win, RGFW_bool state);
+void RGFW_win32_makeWindowDarkMode(RGFW_window* win, RGFW_bool state) {
+ BOOL value = (state == RGFW_TRUE) ? TRUE : FALSE;
+ DwmSetWindowAttributeSRC(win->src.window, 20 /* DWMWA_USE_IMMERSIVE_DARK_MODE */, &value, sizeof(value));
+}
+
+LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
+ switch (message) {
+ case WM_DISPLAYCHANGE:
+ RGFW_pollMonitors();
+ break;
+ default: break;
+ }
+
+ if (hWnd == _RGFW->helperWindow) return DefWindowProcW(hWnd, message, wParam, lParam);
+
+ RGFW_window* win = (RGFW_window*)GetPropW(hWnd, L"RGFW");
+ if (win == NULL) return DefWindowProcW(hWnd, message, wParam, lParam);
+
+ static BYTE keyboardState[256];
+ GetKeyboardState(keyboardState);
+
+ RECT frame;
+ ZeroMemory(&frame, sizeof(frame));
+ DWORD style = RGFW_winapi_window_getStyle(win, win->internal.flags);
+ DWORD exStyle = RGFW_winapi_window_getExStyle(win, win->internal.flags);
+ AdjustWindowRectEx(&frame, style, FALSE, exStyle);
+
+ switch (message) {
+ case WM_CLOSE:
+ case WM_QUIT:
+ RGFW_windowCloseCallback(win);
+ return 0;
+ case WM_ACTIVATE: {
+ RGFW_bool inFocus = RGFW_BOOL(LOWORD(wParam) != WA_INACTIVE);
+ RGFW_windowFocusCallback(win, inFocus);
+
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+ }
+ case WM_MOVE:
+ if (win->internal.captureMouse) {
+ RGFW_window_captureMousePlatform(win, RGFW_TRUE);
+ }
+
+ RGFW_windowMovedCallback(win, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+ case WM_SIZE: {
+ if (win->internal.captureMouse) {
+ RGFW_window_captureMousePlatform(win, RGFW_TRUE);
+ }
+
+ RGFW_windowResizedCallback(win, LOWORD(lParam), HIWORD(lParam));
+ RGFW_window_checkMode(win);
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+ }
+ case WM_MOUSEACTIVATE: {
+ if (HIWORD(lParam) == WM_LBUTTONDOWN) {
+ if (LOWORD(lParam) != HTCLIENT)
+ win->src.actionFrame = RGFW_TRUE;
+ }
+
+ break;
+ }
+ case WM_CAPTURECHANGED: {
+ if (lParam == 0 && win->src.actionFrame) {
+ RGFW_window_captureMousePlatform(win, win->internal.captureMouse);
+ win->src.actionFrame = RGFW_FALSE;
+ }
+
+ break;
+ }
+ #ifndef RGFW_NO_DPI
+ case WM_DPICHANGED: {
+ const float scaleX = HIWORD(wParam) / (float) 96;
+ const float scaleY = LOWORD(wParam) / (float) 96;
+
+ RGFW_scaleUpdatedCallback(win, scaleX, scaleY);
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+ }
+ #endif
+ case WM_SIZING: {
+ if (win->src.aspectRatioW == 0 && win->src.aspectRatioH == 0) {
+ break;
+ }
+
+ RECT* area = (RECT*)lParam;
+ i32 edge = (i32)wParam;
+
+ double ratio = (double)win->src.aspectRatioW / (double) win->src.aspectRatioH;
+
+ if (edge == WMSZ_LEFT || edge == WMSZ_BOTTOMLEFT || edge == WMSZ_RIGHT || edge == WMSZ_BOTTOMRIGHT) {
+ area->bottom = area->top + (frame.bottom - frame.top) + (i32) (((area->right - area->left) - (frame.right - frame.left)) / ratio);
+ } else if (edge == WMSZ_TOPLEFT || edge == WMSZ_TOPRIGHT) {
+ area->top = area->bottom - (frame.bottom - frame.top) - (i32) (((area->right - area->left) - (frame.right - frame.left)) / ratio);
+ } else if (edge == WMSZ_TOP || edge == WMSZ_BOTTOM) {
+ area->right = area->left + (frame.right - frame.left) + (i32) (((area->bottom - area->top) - (frame.bottom - frame.top)) * ratio);
+ }
+
+ return TRUE;
+ }
+ case WM_GETMINMAXINFO: {
+ MINMAXINFO* mmi = (MINMAXINFO*) lParam;
+ RGFW_bool resize = ((win->src.minSizeW == win->src.maxSizeW) && (win->src.minSizeH == win->src.maxSizeH));
+ RGFW_setBit(&win->internal.flags, RGFW_windowNoResize, resize);
+
+ mmi->ptMinTrackSize.x = (LONG)(win->src.minSizeW + (frame.right - frame.left));
+ mmi->ptMinTrackSize.y = (LONG)(win->src.minSizeH + (frame.bottom - frame.top));
+ if (win->src.maxSizeW == 0 && win->src.maxSizeH == 0)
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+
+ mmi->ptMaxTrackSize.x = (LONG)(win->src.maxSizeW + (frame.right - frame.left));
+ mmi->ptMaxTrackSize.y = (LONG)(win->src.maxSizeH + (frame.bottom - frame.top));
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+ }
+ case WM_PAINT: {
+ RECT rect;
+ if (GetUpdateRect(hWnd, &rect, FALSE)) {
+ RGFW_windowRefreshCallback(win, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
+ } else {
+ PAINTSTRUCT ps;
+ BeginPaint(hWnd, &ps);
+ RGFW_windowRefreshCallback(win, 0, 0, win->w, win->h);
+ EndPaint(hWnd, &ps);
+ }
+
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+ }
+ #if(_WIN32_WINNT >= 0x0600)
+ case WM_DWMCOMPOSITIONCHANGED:
+ case WM_DWMCOLORIZATIONCOLORCHANGED:
+ RGFW_win32_makeWindowTransparent(win);
+ break;
+ #endif
+
+ case WM_ENTERSIZEMOVE: {
+ if (win->src.actionFrame)
+ RGFW_window_captureMousePlatform(win, win->internal.captureMouse);
+
+ #ifdef RGFW_ADVANCED_SMOOTH_RESIZE
+ SetTimer(win->src.window, 1, USER_TIMER_MINIMUM, NULL); break;
+ #endif
+ break;
+ }
+ case WM_EXITSIZEMOVE: {
+ if (win->src.actionFrame)
+ RGFW_window_captureMousePlatform(win, win->internal.captureMouse);
+
+ #ifdef RGFW_ADVANCED_SMOOTH_RESIZE
+ KillTimer(win->src.window, 1); break;
+ #endif
+ break;
+ }
+ case WM_TIMER:
+ RGFW_windowRefreshCallback(win, 0, 0, win->w, win->h);
+ break;
+
+ case WM_NCLBUTTONDOWN: {
+ /* workaround for half-second pause when starting to move window
+ see: https://gamedev.net/forums/topic/672094-keeping-things-moving-during-win32-moveresize-events/5254386/
+ */
+ POINT point = { 0, 0 };
+ if (SendMessage(win->src.window, WM_NCHITTEST, wParam, lParam) != HTCAPTION || GetCursorPos(&point) == FALSE)
+ break;
+
+ ScreenToClient(win->src.window, &point);
+ PostMessage(win->src.window, WM_MOUSEMOVE, 0, (u32)(point.x)|((u32)(point.y) << 16));
+ break;
+ }
+ case WM_MOUSELEAVE:
+ RGFW_mouseNotifyCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, RGFW_FALSE);
+ break;
+
+ case WM_CHAR:
+ case WM_SYSCHAR: {
+ if (wParam >= 0xd800 && wParam <= 0xdbff)
+ win->src.highSurrogate = (WCHAR) wParam;
+ else {
+ u32 codepoint = 0;
+
+ if (wParam >= 0xdc00 && wParam <= 0xdfff) {
+ if (win->src.highSurrogate) {
+ codepoint += (u32)((win->src.highSurrogate - 0xd800) << 10);
+ codepoint += (u32)((WCHAR) wParam - 0xdc00);
+ codepoint += 0x10000;
+ }
+ }
+ else
+ codepoint = (WCHAR) wParam;
+
+ win->src.highSurrogate = 0;
+ RGFW_keyCharCallback(win, (u32)codepoint);
+ }
+
+ return 0;
+ }
+
+ case WM_UNICHAR: {
+ if (wParam == UNICODE_NOCHAR) {
+ return TRUE;
+ }
+
+ RGFW_keyCharCallback(win, (u32)wParam);
+ return 0;
+ }
+ case WM_SYSKEYUP: case WM_KEYUP: {
+ if (!(win->internal.enabledEvents & RGFW_keyReleasedFlag)) return DefWindowProcW(hWnd, message, wParam, lParam);
+ i32 scancode = (HIWORD(lParam) & (KF_EXTENDED | 0xff));
+ if (scancode == 0)
+ scancode = (i32)MapVirtualKeyW((UINT)wParam, MAPVK_VK_TO_VSC);
+
+ switch (scancode) {
+ case 0x54: scancode = 0x137; break; /* Alt+PrtS */
+ case 0x146: scancode = 0x45; break; /* Ctrl+Pause */
+ case 0x136: scancode = 0x36; break; /* CJK IME sets the extended bit for right Shift */
+ default: break;
+ }
+
+ RGFW_key value = (u8)RGFW_apiKeyToRGFW((u32) scancode);
+
+ if (wParam == VK_CONTROL) {
+ if (HIWORD(lParam) & KF_EXTENDED)
+ value = RGFW_keyControlR;
+ else value = RGFW_keyControlL;
+ }
+
+ RGFW_keyUpdateKeyMods(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001), (GetKeyState(VK_SCROLL) & 0x0001));
+ RGFW_keyCallback(win, value, win->internal.mod, RGFW_FALSE, RGFW_FALSE);
+ break;
+ }
+ case WM_SYSKEYDOWN: case WM_KEYDOWN: {
+ if (!(win->internal.enabledEvents & RGFW_keyPressedFlag)) return DefWindowProcW(hWnd, message, wParam, lParam);
+ i32 scancode = (HIWORD(lParam) & (KF_EXTENDED | 0xff));
+ if (scancode == 0)
+ scancode = (i32)MapVirtualKeyW((u32)wParam, MAPVK_VK_TO_VSC);
+
+ switch (scancode) {
+ case 0x54: scancode = 0x137; break; /* Alt+PrtS */
+ case 0x146: scancode = 0x45; break; /* Ctrl+Pause */
+ case 0x136: scancode = 0x36; break; /* CJK IME sets the extended bit for right Shift */
+ default: break;
+ }
+
+ RGFW_key value = (u8)RGFW_apiKeyToRGFW((u32) scancode);
+ if (wParam == VK_CONTROL) {
+ if (HIWORD(lParam) & KF_EXTENDED)
+ value = RGFW_keyControlR;
+ else value = RGFW_keyControlL;
+ }
+
+ RGFW_bool repeat = RGFW_isKeyDown(value);
+
+ RGFW_keyUpdateKeyMods(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001), (GetKeyState(VK_SCROLL) & 0x0001));
+ RGFW_keyCallback(win, value, win->internal.mod, repeat, 1);
+ break;
+ }
+ case WM_MOUSEMOVE: {
+ if (win->internal.mouseInside == RGFW_FALSE) {
+ RGFW_mouseNotifyCallback(win, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), RGFW_TRUE);
+ }
+
+ RGFW_mouseMotionCallback(win, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+ break;
+ }
+ case WM_INPUT: {
+ if (!(win->internal.rawMouse || _RGFW->rawMouse)) return DefWindowProcW(hWnd, message, wParam, lParam);
+ unsigned size = sizeof(RAWINPUT);
+ static RAWINPUT raw;
+
+ GetRawInputData((HRAWINPUT)lParam, RID_INPUT, &raw, &size, sizeof(RAWINPUTHEADER));
+
+ if (raw.header.dwType != RIM_TYPEMOUSE || (raw.data.mouse.lLastX == 0 && raw.data.mouse.lLastY == 0) )
+ break;
+
+ float vecX = 0.0f;
+ float vecY = 0.0f;
+
+ if (raw.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) {
+ POINT pos = {0, 0};
+ int width, height;
+
+ if (raw.data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) {
+ pos.x += GetSystemMetrics(SM_XVIRTUALSCREEN);
+ pos.y += GetSystemMetrics(SM_YVIRTUALSCREEN);
+ width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
+ height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
+ }
+ else {
+ width = GetSystemMetrics(SM_CXSCREEN);
+ height = GetSystemMetrics(SM_CYSCREEN);
+ }
+
+ pos.x += (int) (((float)raw.data.mouse.lLastX / 65535.f) * (float)width);
+ pos.y += (int) (((float)raw.data.mouse.lLastY / 65535.f) * (float)height);
+ ScreenToClient(win->src.window, &pos);
+
+ vecX = (float)(pos.x - win->internal.lastMouseX);
+ vecY = (float)(pos.y - win->internal.lastMouseY);
+ } else {
+ vecX = (float)(raw.data.mouse.lLastX);
+ vecY = (float)(raw.data.mouse.lLastY);
+ }
+
+ RGFW_rawMotionCallback(win, vecX, vecY);
+ break;
+ }
+ case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: case WM_XBUTTONDOWN: {
+ RGFW_mouseButton value = 0;
+ if (message == WM_XBUTTONDOWN)
+ value = RGFW_mouseMisc1 + (GET_XBUTTON_WPARAM(wParam) == XBUTTON2);
+ else value = (message == WM_LBUTTONDOWN) ? (u8)RGFW_mouseLeft :
+ (message == WM_RBUTTONDOWN) ? (u8)RGFW_mouseRight : (u8)RGFW_mouseMiddle;
+
+ RGFW_mouseButtonCallback(win, value, 1);
+ break;
+ }
+ case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: case WM_XBUTTONUP: {
+ RGFW_mouseButton value = 0;
+ if (message == WM_XBUTTONUP)
+ value = RGFW_mouseMisc1 + (GET_XBUTTON_WPARAM(wParam) == XBUTTON2);
+ else value = (message == WM_LBUTTONUP) ? (u8)RGFW_mouseLeft :
+ (message == WM_RBUTTONUP) ? (u8)RGFW_mouseRight : (u8)RGFW_mouseMiddle;
+
+ RGFW_mouseButtonCallback(win, value, 0);
+ break;
+ }
+ case WM_MOUSEWHEEL: {
+ float scrollY = (float)((i16) HIWORD(wParam) / (double) WHEEL_DELTA);
+ RGFW_mouseScrollCallback(win, 0.0f, scrollY);
+ break;
+ }
+ case 0x020E: {/* WM_MOUSEHWHEEL */
+ float scrollX = -(float)((i16) HIWORD(wParam) / (double) WHEEL_DELTA);
+ RGFW_mouseScrollCallback(win, scrollX, 0.0f);
+ break;
+ }
+ case WM_DROPFILES: {
+ HDROP drop = (HDROP) wParam;
+ POINT pt;
+
+ /* Move the mouse to the position of the drop */
+ DragQueryPoint(drop, &pt);
+ RGFW_dataDragCallback(win, RGFW_dataFile, RGFW_dndActionMove, pt.x, pt.y);
+
+ if (!(win->internal.enabledEvents & RGFW_dataDrop)) return DefWindowProcW(hWnd, message, wParam, lParam);
+ size_t count = DragQueryFileW(drop, 0xffffffff, NULL, 0);
+
+ u32 i;
+ for (i = 0; i < count; i++) {
+ UINT length = DragQueryFileW(drop, i, NULL, 0);
+ if (length == 0)
+ continue;
+
+ WCHAR* buffer = (WCHAR*)RGFW_ALLOC(sizeof(WCHAR) * (length + 1));
+ char* cbuffer = (char*)RGFW_ALLOC(length + 1);
+
+ DragQueryFileW(drop, i, buffer, length + 1);
+
+ RGFW_createUTF8FromWideStringWin32(buffer, cbuffer, length);
+
+ RGFW_dataDropCallback(win, cbuffer, length + 1, RGFW_dataFile);
+ RGFW_FREE(buffer);
+ RGFW_FREE(cbuffer);
+ }
+
+ DragFinish(drop);
+
+ break;
+ }
+ default: break;
+ }
+
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+}
+
+#ifndef RGFW_NO_DPI
+ HMODULE RGFW_Shcore_dll = NULL;
+ typedef HRESULT (WINAPI *PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*);
+ PFN_GetDpiForMonitor GetDpiForMonitorSRC = NULL;
+ #define GetDpiForMonitor GetDpiForMonitorSRC
+#endif
+
+#if !defined(RGFW_NO_LOAD_WINMM) && !defined(RGFW_NO_WINMM)
+ HMODULE RGFW_winmm_dll = NULL;
+ typedef u32 (WINAPI * PFN_timeBeginPeriod)(u32);
+ typedef PFN_timeBeginPeriod PFN_timeEndPeriod;
+ PFN_timeBeginPeriod timeBeginPeriodSRC, timeEndPeriodSRC;
+ #define timeBeginPeriod timeBeginPeriodSRC
+ #define timeEndPeriod timeEndPeriodSRC
+#elif !defined(RGFW_NO_WINMM)
+ __declspec(dllimport) u32 __stdcall timeBeginPeriod(u32 uPeriod);
+ __declspec(dllimport) u32 __stdcall timeEndPeriod(u32 uPeriod);
+#endif
+#define RGFW_PROC_DEF(proc, name) if (name##SRC == NULL && proc != NULL) { \
+ name##SRC = (PFN_##name)(RGFW_proc)GetProcAddress((proc), (#name)); \
+ RGFW_ASSERT(name##SRC != NULL); \
+ }
+
+RGFW_format RGFW_nativeFormat(void) { return RGFW_formatBGRA8; }
+
+RGFW_bool RGFW_createSurfacePtr(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) {
+ RGFW_ASSERT(surface != NULL);
+ surface->data = data;
+ surface->w = w;
+ surface->h = h;
+ surface->format = format;
+
+ BITMAPV5HEADER bi;
+ ZeroMemory(&bi, sizeof(bi));
+ bi.bV5Size = sizeof(bi);
+ bi.bV5Width = (i32)w;
+ bi.bV5Height = -((LONG) h);
+ bi.bV5Planes = 1;
+ bi.bV5BitCount = (format >= RGFW_formatRGBA8) ? 32 : 24;
+ bi.bV5Compression = BI_RGB;
+
+ surface->native.bitmap = CreateDIBSection(_RGFW->root->src.hdc,
+ (BITMAPINFO*) &bi, DIB_RGB_COLORS,
+ (void**) &surface->native.bitmapBits,
+ NULL, (DWORD) 0);
+
+ surface->native.format = (format >= RGFW_formatRGBA8) ? (RGFW_format) RGFW_formatBGRA8 : (RGFW_format) RGFW_formatBGR8;
+
+ if (surface->native.bitmap == NULL) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errBuffer, "Failed to create DIB section.");
+ return RGFW_FALSE;
+ }
+
+ surface->native.hdcMem = CreateCompatibleDC(_RGFW->root->src.hdc);
+ SelectObject(surface->native.hdcMem, surface->native.bitmap);
+
+ return RGFW_TRUE;
+}
+
+void RGFW_surface_freePtr(RGFW_surface* surface) {
+ RGFW_ASSERT(surface != NULL);
+
+ DeleteDC(surface->native.hdcMem);
+ DeleteObject(surface->native.bitmap);
+}
+
+void RGFW_window_blitSurface(RGFW_window* win, RGFW_surface* surface) {
+ RGFW_copyImageData(surface->native.bitmapBits, surface->w, RGFW_MIN(win->h, surface->h), surface->native.format, surface->data, surface->format, surface->convertFunc);
+ BitBlt(win->src.hdc, 0, 0, RGFW_MIN(win->w, surface->w), RGFW_MIN(win->h, surface->h), surface->native.hdcMem, 0, 0, SRCCOPY);
+}
+
+void RGFW_window_setRawMouseModePlatform(RGFW_window* win, RGFW_bool state) {
+ RGFW_UNUSED(win);
+ RAWINPUTDEVICE id = { 0x01, 0x02, 0, win->src.window };
+ id.dwFlags = (state == RGFW_TRUE) ? 0 : RIDEV_REMOVE;
+
+ RegisterRawInputDevices(&id, 1, sizeof(id));
+}
+
+void RGFW_window_captureMousePlatform(RGFW_window* win, RGFW_bool state) {
+ if (state == RGFW_FALSE) {
+ ClipCursor(NULL);
+ return;
+ }
+
+ RECT clipRect;
+ GetClientRect(win->src.window, &clipRect);
+ ClientToScreen(win->src.window, (POINT*) &clipRect.left);
+ ClientToScreen(win->src.window, (POINT*) &clipRect.right);
+ ClipCursor(&clipRect);
+}
+
+#define RGFW_LOAD_LIBRARY(x, lib) if (x == NULL) { x = LoadLibraryA(lib); RGFW_ASSERT(x != NULL); }
+
+#ifdef RGFW_DIRECTX
+int RGFW_window_createSwapChain_DirectX(RGFW_window* win, IDXGIFactory* pFactory, IUnknown* pDevice, IDXGISwapChain** swapchain) {
+ RGFW_ASSERT(win && pFactory && pDevice && swapchain);
+
+ static DXGI_SWAP_CHAIN_DESC swapChainDesc;
+ RGFW_MEMZERO(&swapChainDesc, sizeof(swapChainDesc));
+ swapChainDesc.BufferCount = 2;
+ swapChainDesc.BufferDesc.Width = win->w;
+ swapChainDesc.BufferDesc.Height = win->h;
+ swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ swapChainDesc.OutputWindow = (HWND)win->src.window;
+ swapChainDesc.SampleDesc.Count = 1;
+ swapChainDesc.SampleDesc.Quality = 0;
+ swapChainDesc.Windowed = TRUE;
+ swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
+
+ HRESULT hr = pFactory->lpVtbl->CreateSwapChain(pFactory, (IUnknown*)pDevice, &swapChainDesc, swapchain);
+ if (FAILED(hr)) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errDirectXContext, "Failed to create DirectX swap chain!");
+ return -2;
+ }
+
+ return 0;
+}
+#endif
+
+/* we're doing it with magic numbers because some keys are missing */
+void RGFW_initKeycodesPlatform(void) {
+ _RGFW->keycodes[0x00B] = RGFW_key0;
+ _RGFW->keycodes[0x002] = RGFW_key1;
+ _RGFW->keycodes[0x003] = RGFW_key2;
+ _RGFW->keycodes[0x004] = RGFW_key3;
+ _RGFW->keycodes[0x005] = RGFW_key4;
+ _RGFW->keycodes[0x006] = RGFW_key5;
+ _RGFW->keycodes[0x007] = RGFW_key6;
+ _RGFW->keycodes[0x008] = RGFW_key7;
+ _RGFW->keycodes[0x009] = RGFW_key8;
+ _RGFW->keycodes[0x00A] = RGFW_key9;
+ _RGFW->keycodes[0x01E] = RGFW_keyA;
+ _RGFW->keycodes[0x030] = RGFW_keyB;
+ _RGFW->keycodes[0x02E] = RGFW_keyC;
+ _RGFW->keycodes[0x020] = RGFW_keyD;
+ _RGFW->keycodes[0x012] = RGFW_keyE;
+ _RGFW->keycodes[0x021] = RGFW_keyF;
+ _RGFW->keycodes[0x022] = RGFW_keyG;
+ _RGFW->keycodes[0x023] = RGFW_keyH;
+ _RGFW->keycodes[0x017] = RGFW_keyI;
+ _RGFW->keycodes[0x024] = RGFW_keyJ;
+ _RGFW->keycodes[0x025] = RGFW_keyK;
+ _RGFW->keycodes[0x026] = RGFW_keyL;
+ _RGFW->keycodes[0x032] = RGFW_keyM;
+ _RGFW->keycodes[0x031] = RGFW_keyN;
+ _RGFW->keycodes[0x018] = RGFW_keyO;
+ _RGFW->keycodes[0x019] = RGFW_keyP;
+ _RGFW->keycodes[0x010] = RGFW_keyQ;
+ _RGFW->keycodes[0x013] = RGFW_keyR;
+ _RGFW->keycodes[0x01F] = RGFW_keyS;
+ _RGFW->keycodes[0x014] = RGFW_keyT;
+ _RGFW->keycodes[0x016] = RGFW_keyU;
+ _RGFW->keycodes[0x02F] = RGFW_keyV;
+ _RGFW->keycodes[0x011] = RGFW_keyW;
+ _RGFW->keycodes[0x02D] = RGFW_keyX;
+ _RGFW->keycodes[0x015] = RGFW_keyY;
+ _RGFW->keycodes[0x02C] = RGFW_keyZ;
+ _RGFW->keycodes[0x028] = RGFW_keyApostrophe;
+ _RGFW->keycodes[0x02B] = RGFW_keyBackSlash;
+ _RGFW->keycodes[0x033] = RGFW_keyComma;
+ _RGFW->keycodes[0x00D] = RGFW_keyEquals;
+ _RGFW->keycodes[0x029] = RGFW_keyBacktick;
+ _RGFW->keycodes[0x01A] = RGFW_keyBracket;
+ _RGFW->keycodes[0x00C] = RGFW_keyMinus;
+ _RGFW->keycodes[0x034] = RGFW_keyPeriod;
+ _RGFW->keycodes[0x01B] = RGFW_keyCloseBracket;
+ _RGFW->keycodes[0x027] = RGFW_keySemicolon;
+ _RGFW->keycodes[0x035] = RGFW_keySlash;
+ _RGFW->keycodes[0x056] = RGFW_keyWorld2;
+ _RGFW->keycodes[0x00E] = RGFW_keyBackSpace;
+ _RGFW->keycodes[0x153] = RGFW_keyDelete;
+ _RGFW->keycodes[0x14F] = RGFW_keyEnd;
+ _RGFW->keycodes[0x01C] = RGFW_keyEnter;
+ _RGFW->keycodes[0x001] = RGFW_keyEscape;
+ _RGFW->keycodes[0x147] = RGFW_keyHome;
+ _RGFW->keycodes[0x152] = RGFW_keyInsert;
+ _RGFW->keycodes[0x15D] = RGFW_keyMenu;
+ _RGFW->keycodes[0x151] = RGFW_keyPageDown;
+ _RGFW->keycodes[0x149] = RGFW_keyPageUp;
+ _RGFW->keycodes[0x045] = RGFW_keyPause;
+ _RGFW->keycodes[0x039] = RGFW_keySpace;
+ _RGFW->keycodes[0x00F] = RGFW_keyTab;
+ _RGFW->keycodes[0x03A] = RGFW_keyCapsLock;
+ _RGFW->keycodes[0x145] = RGFW_keyNumLock;
+ _RGFW->keycodes[0x046] = RGFW_keyScrollLock;
+ _RGFW->keycodes[0x03B] = RGFW_keyF1;
+ _RGFW->keycodes[0x03C] = RGFW_keyF2;
+ _RGFW->keycodes[0x03D] = RGFW_keyF3;
+ _RGFW->keycodes[0x03E] = RGFW_keyF4;
+ _RGFW->keycodes[0x03F] = RGFW_keyF5;
+ _RGFW->keycodes[0x040] = RGFW_keyF6;
+ _RGFW->keycodes[0x041] = RGFW_keyF7;
+ _RGFW->keycodes[0x042] = RGFW_keyF8;
+ _RGFW->keycodes[0x043] = RGFW_keyF9;
+ _RGFW->keycodes[0x044] = RGFW_keyF10;
+ _RGFW->keycodes[0x057] = RGFW_keyF11;
+ _RGFW->keycodes[0x058] = RGFW_keyF12;
+ _RGFW->keycodes[0x064] = RGFW_keyF13;
+ _RGFW->keycodes[0x065] = RGFW_keyF14;
+ _RGFW->keycodes[0x066] = RGFW_keyF15;
+ _RGFW->keycodes[0x067] = RGFW_keyF16;
+ _RGFW->keycodes[0x068] = RGFW_keyF17;
+ _RGFW->keycodes[0x069] = RGFW_keyF18;
+ _RGFW->keycodes[0x06A] = RGFW_keyF19;
+ _RGFW->keycodes[0x06B] = RGFW_keyF20;
+ _RGFW->keycodes[0x06C] = RGFW_keyF21;
+ _RGFW->keycodes[0x06D] = RGFW_keyF22;
+ _RGFW->keycodes[0x06E] = RGFW_keyF23;
+ _RGFW->keycodes[0x076] = RGFW_keyF24;
+ _RGFW->keycodes[0x038] = RGFW_keyAltL;
+ _RGFW->keycodes[0x01D] = RGFW_keyControlL;
+ _RGFW->keycodes[0x02A] = RGFW_keyShiftL;
+ _RGFW->keycodes[0x15B] = RGFW_keySuperL;
+ _RGFW->keycodes[0x137] = RGFW_keyPrintScreen;
+ _RGFW->keycodes[0x138] = RGFW_keyAltR;
+ _RGFW->keycodes[0x11D] = RGFW_keyControlR;
+ _RGFW->keycodes[0x036] = RGFW_keyShiftR;
+ _RGFW->keycodes[0x15C] = RGFW_keySuperR;
+ _RGFW->keycodes[0x150] = RGFW_keyDown;
+ _RGFW->keycodes[0x14B] = RGFW_keyLeft;
+ _RGFW->keycodes[0x14D] = RGFW_keyRight;
+ _RGFW->keycodes[0x148] = RGFW_keyUp;
+ _RGFW->keycodes[0x052] = RGFW_keyPad0;
+ _RGFW->keycodes[0x04F] = RGFW_keyPad1;
+ _RGFW->keycodes[0x050] = RGFW_keyPad2;
+ _RGFW->keycodes[0x051] = RGFW_keyPad3;
+ _RGFW->keycodes[0x04B] = RGFW_keyPad4;
+ _RGFW->keycodes[0x04C] = RGFW_keyPad5;
+ _RGFW->keycodes[0x04D] = RGFW_keyPad6;
+ _RGFW->keycodes[0x047] = RGFW_keyPad7;
+ _RGFW->keycodes[0x048] = RGFW_keyPad8;
+ _RGFW->keycodes[0x049] = RGFW_keyPad9;
+ _RGFW->keycodes[0x04E] = RGFW_keyPadPlus;
+ _RGFW->keycodes[0x053] = RGFW_keyPadPeriod;
+ _RGFW->keycodes[0x135] = RGFW_keyPadSlash;
+ _RGFW->keycodes[0x11C] = RGFW_keyPadReturn;
+ _RGFW->keycodes[0x059] = RGFW_keyPadEqual;
+ _RGFW->keycodes[0x037] = RGFW_keyPadMultiply;
+ _RGFW->keycodes[0x04A] = RGFW_keyPadMinus;
+}
+
+
+i32 RGFW_initPlatform(void) {
+#ifndef RGFW_NO_DPI
+ #if (_WIN32_WINNT >= 0x0600)
+ SetProcessDPIAware();
+ #endif
+#endif
+
+ #ifndef RGFW_NO_WINMM
+ #ifndef RGFW_NO_LOAD_WINMM
+ RGFW_LOAD_LIBRARY(RGFW_winmm_dll, "winmm.dll");
+ RGFW_PROC_DEF(RGFW_winmm_dll, timeBeginPeriod);
+ RGFW_PROC_DEF(RGFW_winmm_dll, timeEndPeriod);
+ #endif
+ timeBeginPeriod(1);
+ #endif
+
+ #ifndef RGFW_NO_DWM
+ RGFW_LOAD_LIBRARY(RGFW_dwm_dll, "dwmapi.dll");
+ RGFW_PROC_DEF(RGFW_dwm_dll, DwmEnableBlurBehindWindow);
+ RGFW_PROC_DEF(RGFW_dwm_dll, DwmSetWindowAttribute);
+ #endif
+
+ RGFW_LOAD_LIBRARY(RGFW_wgl_dll, "opengl32.dll");
+ #ifndef RGFW_NO_LOAD_WGL
+ RGFW_PROC_DEF(RGFW_wgl_dll, wglCreateContext);
+ RGFW_PROC_DEF(RGFW_wgl_dll, wglDeleteContext);
+ RGFW_PROC_DEF(RGFW_wgl_dll, wglGetProcAddress);
+ RGFW_PROC_DEF(RGFW_wgl_dll, wglMakeCurrent);
+ RGFW_PROC_DEF(RGFW_wgl_dll, wglGetCurrentDC);
+ RGFW_PROC_DEF(RGFW_wgl_dll, wglGetCurrentContext);
+ RGFW_PROC_DEF(RGFW_wgl_dll, wglShareLists);
+ #endif
+
+
+ _RGFW->instance = GetModuleHandleW(NULL);
+ static wchar_t wide_class[256];
+ MultiByteToWideChar(CP_UTF8, 0, RGFW_className, -1, wide_class, 255);
+
+ RGFW_MEMZERO(&_RGFW->wndClass, sizeof(_RGFW->wndClass));
+
+ _RGFW->wndClass.lpszClassName = wide_class;
+ _RGFW->wndClass.hInstance = _RGFW->instance;
+ _RGFW->wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
+ _RGFW->wndClass.lpfnWndProc = WndProcW;
+ _RGFW->wndClass.cbClsExtra = sizeof(RGFW_window*);
+
+ _RGFW->wndClass.hIcon = (HICON)LoadImageA(_RGFW->instance, "RGFW_ICON", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+ if (_RGFW->wndClass.hIcon == NULL)
+ _RGFW->wndClass.hIcon = (HICON)LoadImageA(NULL, (LPCSTR)IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+
+ RegisterClassW(&_RGFW->wndClass);
+
+ _RGFW->helperWindow = CreateWindowW(_RGFW->wndClass.lpszClassName, (wchar_t*)NULL, 0, 0, 0, 0, 0, 0, 0, _RGFW->instance, 0);
+
+ u8 RGFW_blk[] = { 0, 0, 0, 0 };
+ _RGFW->hiddenMouse = RGFW_createMouse(RGFW_blk, 1, 1, RGFW_formatRGBA8);
+ return 0;
+}
+
+RGFW_window* RGFW_createWindowPlatform(const char* name, RGFW_windowFlags flags, RGFW_window* win) {
+ if (name[0] == 0) name = (char*) " ";
+ win->src.hIconSmall = win->src.hIconBig = NULL;
+ win->src.maxSizeW = 0;
+ win->src.maxSizeH = 0;
+ win->src.minSizeW = 0;
+ win->src.minSizeH = 0;
+ win->src.aspectRatioW = 0;
+ win->src.aspectRatioH = 0;
+
+ DWORD window_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
+
+ if (!(flags & RGFW_windowNoBorder)) {
+ window_style |= WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_MINIMIZEBOX;
+
+ if (!(flags & RGFW_windowNoResize))
+ window_style |= WS_SIZEBOX | WS_MAXIMIZEBOX;
+ } else
+ window_style |= WS_POPUP | WS_VISIBLE | WS_SYSMENU;
+
+ wchar_t wide_name[256];
+ MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_name, 255);
+ HWND dummyWin = CreateWindowW(_RGFW->wndClass.lpszClassName, (wchar_t*)wide_name, window_style, win->x, win->y, win->w, win->h, 0, 0, _RGFW->instance, 0);
+
+#ifdef RGFW_OPENGL
+ RGFW_win32_loadOpenGLFuncs(dummyWin);
+#endif
+
+ DestroyWindow(dummyWin);
+
+ RECT rect = { 0, 0, win->w, win->h};
+ DWORD style = RGFW_winapi_window_getStyle(win, flags);
+ DWORD exStyle = RGFW_winapi_window_getExStyle(win, flags);
+ AdjustWindowRectEx(&rect, style, FALSE, exStyle);
+
+ win->src.window = CreateWindowW(_RGFW->wndClass.lpszClassName, (wchar_t*)wide_name, window_style, win->x + rect.left, win->y + rect.top, rect.right - rect.left, rect.bottom - rect.top, 0, 0, _RGFW->instance, 0);
+ SetPropW(win->src.window, L"RGFW", win);
+ RGFW_window_resize(win, win->w, win->h); /* so WM_GETMINMAXINFO gets called again */
+
+ if (flags & RGFW_windowAllowDND) {
+ win->internal.flags |= RGFW_windowAllowDND;
+ RGFW_window_setDND(win, 1);
+ }
+ win->src.hdc = GetDC(win->src.window);
+
+ RGFW_win32_makeWindowDarkMode(win, RGFW_win32_getDarkModeState());
+ RGFW_win32_makeWindowTransparent(win);
+ return win;
+}
+
+void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) {
+ RGFW_setBit(&win->internal.flags, RGFW_windowNoBorder, !border);
+
+ RECT rect;
+ GetClientRect(win->src.window, &rect);
+
+ LONG style = GetWindowLong(win->src.window, GWL_STYLE);
+ style |= (LONG)RGFW_winapi_window_getStyle(win, win->internal.flags);
+
+ if (border == 0) {
+ style &= ~WS_OVERLAPPEDWINDOW;
+ } else {
+ if (win->internal.flags & RGFW_windowNoResize) style &= ~WS_MAXIMIZEBOX;
+ style |= WS_OVERLAPPEDWINDOW;
+ }
+
+ DWORD exStyle = RGFW_winapi_window_getExStyle(win, win->internal.flags);
+ ClientToScreen(win->src.window, (POINT*) &rect.left);
+ ClientToScreen(win->src.window, (POINT*) &rect.right);
+
+ AdjustWindowRectEx(&rect, (DWORD)style, FALSE, exStyle);
+ SetWindowLong(win->src.window, GWL_STYLE, style);
+
+ SetWindowLongW(win->src.window, GWL_STYLE, style);
+ SetWindowPos(win->src.window, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER);
+}
+
+void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow) {
+ RGFW_setBit(&win->internal.flags, RGFW_windowAllowDND, allow);
+ DragAcceptFiles(win->src.window, allow);
+}
+
+RGFW_bool RGFW_getGlobalMouse(i32* x, i32* y) {
+ POINT p;
+ GetCursorPos(&p);
+ if (x) *x = p.x;
+ if (y) *y = p.y;
+ return RGFW_TRUE;
+}
+
+void RGFW_window_setAspectRatio(RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+ win->src.aspectRatioW = w;
+ win->src.aspectRatioH = h;
+}
+
+void RGFW_window_setMinSize(RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+ win->src.minSizeW = w;
+ win->src.minSizeH = h;
+}
+
+void RGFW_window_setMaxSize(RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+ win->src.maxSizeW = w;
+ win->src.maxSizeH = h;
+}
+
+void RGFW_window_focus(RGFW_window* win) {
+ RGFW_ASSERT(win);
+ SetForegroundWindow(win->src.window);
+ SetFocus(win->src.window);
+}
+
+void RGFW_window_raise(RGFW_window* win) {
+ RGFW_ASSERT(win);
+ BringWindowToTop(win->src.window);
+ SetWindowPos(win->src.window, HWND_TOP, win->x, win->y, win->w, win->h, SWP_NOSIZE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
+}
+
+void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) {
+ RGFW_ASSERT(win != NULL);
+
+ RGFW_monitor* mon = RGFW_window_getMonitor(win);
+
+ if (fullscreen == RGFW_FALSE) {
+ RGFW_monitor_setMode(mon, &win->internal.oldMode);
+
+ RGFW_window_setBorder(win, 1);
+
+ RECT rect = { 0, 0, win->internal.oldW, win->internal.oldH};
+ DWORD style = RGFW_winapi_window_getStyle(win, win->internal.flags);
+ DWORD exStyle = RGFW_winapi_window_getExStyle(win, win->internal.flags);
+ AdjustWindowRectEx(&rect, style, FALSE, exStyle);
+ SetWindowPos(win->src.window, HWND_TOP, win->internal.oldX, win->internal.oldY, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
+
+ win->internal.flags &= ~(u32)RGFW_windowFullscreen;
+ win->x = win->internal.oldX;
+ win->y = win->internal.oldY;
+ win->w = win->internal.oldW;
+ win->h = win->internal.oldH;
+ return;
+ }
+
+ win->internal.oldX = win->x;
+ win->internal.oldY = win->y;
+ win->internal.oldW = win->w;
+ win->internal.oldH = win->h;
+ win->internal.oldMode = mon->mode;
+ win->internal.flags |= RGFW_windowFullscreen;
+
+ RGFW_window_setBorder(win, 0);
+
+ SetWindowPos(win->src.window, HWND_TOPMOST, (i32)mon->x, (i32)mon->y, 0, 0, SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOSIZE);
+ win->x = mon->x;
+ win->y = mon->y;
+
+ RGFW_monitor_scaleToWindow(mon, win);
+ SetWindowPos(win->src.window, HWND_TOPMOST, 0, 0, (i32)mon->mode.w, (i32)mon->mode.h, SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE);
+ win->w = mon->mode.w;
+ win->h = mon->mode.h;
+}
+
+void RGFW_window_maximize(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_window_hide(win);
+ ShowWindow(win->src.window, SW_MAXIMIZE);
+ RGFW_window_fetchSize(win, NULL, NULL);
+}
+
+void RGFW_window_minimize(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ ShowWindow(win->src.window, SW_MINIMIZE);
+}
+
+void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) {
+ RGFW_ASSERT(win != NULL);
+ if (floating) SetWindowPos(win->src.window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
+ else SetWindowPos(win->src.window, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
+}
+
+void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) {
+ SetWindowLong(win->src.window, GWL_EXSTYLE, WS_EX_LAYERED);
+ SetLayeredWindowAttributes(win->src.window, 0, opacity, LWA_ALPHA);
+}
+
+void RGFW_window_restore(RGFW_window* win) { RGFW_window_show(win); }
+
+RGFW_bool RGFW_window_isFloating(RGFW_window* win) {
+ return (GetWindowLongPtr(win->src.window, GWL_EXSTYLE) & WS_EX_TOPMOST) != 0;
+}
+
+void RGFW_stopCheckEvents(void) {
+ PostMessageW(_RGFW->helperWindow, WM_NULL, 0, 0);
+}
+
+void RGFW_waitForEvent(i32 waitMS) {
+ MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD)waitMS, QS_ALLINPUT);
+}
+
+RGFW_key RGFW_physicalToMappedKey(RGFW_key key) {
+ UINT vsc = RGFW_rgfwToApiKey(key);
+ BYTE keyboardState[256] = {0};
+
+ if (!GetKeyboardState(keyboardState))
+ return key;
+
+ UINT vk = MapVirtualKeyW(vsc, MAPVK_VSC_TO_VK);
+ HKL layout = GetKeyboardLayout(0);
+
+ wchar_t charBuffer[4] = {0};
+ int result = ToUnicodeEx(vk, vsc, keyboardState, charBuffer, 1, 0, layout);
+
+ if (result == 1 && charBuffer[0] < 256) {
+ return (RGFW_key)charBuffer[0];
+ }
+
+ switch (vk) {
+ case VK_F1: return RGFW_keyF1;
+ case VK_F2: return RGFW_keyF2;
+ case VK_F3: return RGFW_keyF3;
+ case VK_F4: return RGFW_keyF4;
+ case VK_F5: return RGFW_keyF5;
+ case VK_F6: return RGFW_keyF6;
+ case VK_F7: return RGFW_keyF7;
+ case VK_F8: return RGFW_keyF8;
+ case VK_F9: return RGFW_keyF9;
+ case VK_F10: return RGFW_keyF10;
+ case VK_F11: return RGFW_keyF11;
+ case VK_F12: return RGFW_keyF12;
+ case VK_F13: return RGFW_keyF13;
+ case VK_F14: return RGFW_keyF14;
+ case VK_F15: return RGFW_keyF15;
+ case VK_F16: return RGFW_keyF16;
+ case VK_F17: return RGFW_keyF17;
+ case VK_F18: return RGFW_keyF18;
+ case VK_F19: return RGFW_keyF19;
+ case VK_F20: return RGFW_keyF20;
+ case VK_F21: return RGFW_keyF21;
+ case VK_F22: return RGFW_keyF22;
+ case VK_F23: return RGFW_keyF23;
+ case VK_F24: return RGFW_keyF24;
+ case VK_LSHIFT: return RGFW_keyShiftL;
+ case VK_RSHIFT: return RGFW_keyShiftR;
+ case VK_LCONTROL: return RGFW_keyControlL;
+ case VK_RCONTROL: return RGFW_keyControlR;
+ case VK_LMENU: return RGFW_keyAltL;
+ case VK_RMENU: return RGFW_keyAltR;
+ case VK_LWIN: return RGFW_keySuperL;
+ case VK_RWIN: return RGFW_keySuperR;
+ case VK_CAPITAL: return RGFW_keyCapsLock;
+ case VK_NUMLOCK: return RGFW_keyNumLock;
+ case VK_SCROLL: return RGFW_keyScrollLock;
+ case VK_UP: return RGFW_keyUp;
+ case VK_DOWN: return RGFW_keyDown;
+ case VK_LEFT: return RGFW_keyLeft;
+ case VK_RIGHT: return RGFW_keyRight;
+ case VK_HOME: return RGFW_keyHome;
+ case VK_END: return RGFW_keyEnd;
+ case VK_PRIOR: return RGFW_keyPageUp;
+ case VK_NEXT: return RGFW_keyPageDown;
+ case VK_INSERT: return RGFW_keyInsert;
+ case VK_APPS: return RGFW_keyMenu;
+ case VK_ADD: return RGFW_keyPadPlus;
+ case VK_SUBTRACT: return RGFW_keyPadMinus;
+ case VK_MULTIPLY: return RGFW_keyPadMultiply;
+ case VK_DIVIDE: return RGFW_keyPadSlash;
+ case VK_RETURN: return RGFW_keyPadReturn;
+ case VK_DECIMAL: return RGFW_keyPadPeriod;
+ case VK_NUMPAD0: return RGFW_keyPad0;
+ case VK_NUMPAD1: return RGFW_keyPad1;
+ case VK_NUMPAD2: return RGFW_keyPad2;
+ case VK_NUMPAD3: return RGFW_keyPad3;
+ case VK_NUMPAD4: return RGFW_keyPad4;
+ case VK_NUMPAD5: return RGFW_keyPad5;
+ case VK_NUMPAD6: return RGFW_keyPad6;
+ case VK_NUMPAD7: return RGFW_keyPad7;
+ case VK_NUMPAD8: return RGFW_keyPad8;
+ case VK_NUMPAD9: return RGFW_keyPad9;
+ case VK_SNAPSHOT: return RGFW_keyPrintScreen;
+ case VK_PAUSE: return RGFW_keyPause;
+ default: return RGFW_keyNULL;
+ }
+
+ return RGFW_keyNULL;
+}
+
+RGFW_bool RGFW_window_fetchSize(RGFW_window* win, i32* w, i32* h) {
+ RECT area;
+ GetClientRect(win->src.window, &area);
+
+ win->w = area.right;
+ win->h = area.bottom;
+
+ return RGFW_window_getSize(win, w, h);
+}
+
+void RGFW_pollEvents(void) {
+ RGFW_resetPrevState();
+ MSG msg;
+ while (PeekMessageA(&msg, NULL, 0u, 0u, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessageA(&msg);
+ }
+}
+
+RGFW_bool RGFW_window_isHidden(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ return IsWindowVisible(win->src.window) == 0 && !RGFW_window_isMinimized(win);
+}
+
+RGFW_bool RGFW_window_isMinimized(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+
+ WINDOWPLACEMENT placement;
+ RGFW_MEMZERO(&placement, sizeof(placement));
+ GetWindowPlacement(win->src.window, &placement);
+ return placement.showCmd == SW_SHOWMINIMIZED;
+}
+
+RGFW_bool RGFW_window_isMaximized(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+
+ WINDOWPLACEMENT placement;
+ RGFW_MEMZERO(&placement, sizeof(placement));
+ GetWindowPlacement(win->src.window, &placement);
+ return placement.showCmd == SW_SHOWMAXIMIZED || IsZoomed(win->src.window);
+}
+
+RGFW_bool RGFW_monitor_getWorkarea(RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height) {
+ MONITORINFOEX mi;
+ mi.cbSize = sizeof(MONITORINFOEX);
+ GetMonitorInfoA(monitor->node->hMonitor, (LPMONITORINFO)&mi);
+
+ if (x) *x = mi.rcWork.left;
+ if (y) *y = mi.rcWork.top;
+ if (width) *width = mi.rcWork.right - mi.rcWork.left;
+ if (height) *height = mi.rcWork.bottom - mi.rcWork.top;
+
+ return RGFW_TRUE;
+}
+
+size_t RGFW_monitor_getGammaRampPtr(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) {
+ WORD values[3][256];
+
+ HDC dc = CreateDCW(L"DISPLAY", monitor->node->adapterName, NULL, NULL);
+ GetDeviceGammaRamp(dc, values);
+ DeleteDC(dc);
+
+ if (ramp) {
+ memcpy(ramp->red, values[0], sizeof(values[0]));
+ memcpy(ramp->green, values[1], sizeof(values[1]));
+ memcpy(ramp->blue, values[2], sizeof(values[2]));
+ }
+
+ return sizeof(values[0]) / sizeof(WORD);
+}
+
+RGFW_bool RGFW_monitor_setGammaRamp(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) {
+ WORD values[3][256];
+ if (ramp->count != 256) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errX11, "Win32: Gamma ramp size must be 256");
+ return RGFW_FALSE;
+ }
+
+ memcpy(values[0], ramp->red, sizeof(values[0]));
+ memcpy(values[1], ramp->green, sizeof(values[1]));
+ memcpy(values[2], ramp->blue, sizeof(values[2]));
+
+ HDC dc = CreateDCW(L"DISPLAY", monitor->node->adapterName, NULL, NULL);
+ SetDeviceGammaRamp(dc, values);
+ DeleteDC(dc);
+ return RGFW_TRUE;
+}
+
+BOOL CALLBACK RGFW_win32_getMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData);
+BOOL CALLBACK RGFW_win32_getMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
+ RGFW_UNUSED(hMonitor);
+ RGFW_UNUSED(hdcMonitor);
+ RGFW_UNUSED(lprcMonitor);
+ RGFW_UNUSED(dwData);
+
+ MONITORINFOEXW mi;
+ ZeroMemory(&mi, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+
+ if (GetMonitorInfoW(hMonitor, (MONITORINFO*) &mi)) {
+ RGFW_monitorNode* node = (RGFW_monitorNode*)dwData;
+ if (wcscmp(mi.szDevice, node->adapterName) == 0) {
+ node->hMonitor = hMonitor;
+ }
+ }
+
+ return TRUE;
+}
+
+RGFWDEF void RGFW_win32_getMode(DEVMODEW* dm, RGFW_monitorMode* mode);
+void RGFW_win32_getMode(DEVMODEW* dm, RGFW_monitorMode* mode) {
+ mode->w = (i32)dm->dmPelsWidth;
+ mode->h = (i32)dm->dmPelsHeight;
+ RGFW_splitBPP(dm->dmBitsPerPel, mode);
+
+ switch (dm->dmDisplayFrequency) {
+ case 119:
+ case 59:
+ case 29:
+ mode->refreshRate = ((float)(dm->dmDisplayFrequency + 1) * 1000.0f) / 1001.f;
+ break;
+ default:
+ mode->refreshRate = (float)dm->dmDisplayFrequency;
+ break;
+ }
+}
+
+size_t RGFW_monitor_getModesPtr(RGFW_monitor* monitor, RGFW_monitorMode** modes){
+ size_t count = 0;
+ DWORD modeIndex = 0;
+
+ for (;;) {
+ DEVMODEW dm;
+ ZeroMemory(&dm, sizeof(dm));
+ dm.dmSize = sizeof(dm);
+
+ if (!EnumDisplaySettingsW(monitor->node->adapterName, modeIndex, &dm))
+ break;
+
+ if (ChangeDisplaySettingsExW(monitor->node->adapterName, &dm, NULL, CDS_TEST, NULL) != DISP_CHANGE_SUCCESSFUL) {
+ continue;
+ }
+
+ modeIndex++;
+
+ if (dm.dmBitsPerPel < 15)
+ continue;
+
+ if (modes) {
+ RGFW_monitorMode mode;
+ RGFW_win32_getMode(&dm, &mode);
+
+ size_t i;
+ for (i = 0; i < count; i++) {
+ if (RGFW_monitorModeCompare(&(*modes)[i], &mode, RGFW_monitorAll) == RGFW_TRUE) {
+ break;
+ }
+ }
+
+ if (i < count) {
+ continue;
+ }
+
+ (*modes)[count] = mode;
+ }
+
+ count += 1;
+ }
+
+ return count;
+}
+
+RGFWDEF void RGFW_win32_createMonitor(DISPLAY_DEVICEW* adapter, DISPLAY_DEVICEW* dd);
+void RGFW_win32_createMonitor(DISPLAY_DEVICEW* adapter, DISPLAY_DEVICEW* dd) {
+ DEVMODEW dm;
+ ZeroMemory(&dm, sizeof(dm));
+ dm.dmSize = sizeof(dm);
+
+ if (!EnumDisplaySettingsW(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &dm)) {
+ return;
+ }
+
+ RGFW_monitorNode* node = RGFW_monitors_add(NULL);
+
+ wcscpy(node->adapterName, adapter->DeviceName);
+ wcscpy(node->deviceName, dd->DeviceName);
+
+ RGFW_createUTF8FromWideStringWin32(dd->DeviceString, node->mon.name, sizeof(node->mon.name));
+ node->mon.name[sizeof(node->mon.name) - 1] = '\0';
+
+ RECT rect;
+ rect.left = (LONG)dm.dmPosition.x;
+ rect.top = (LONG)dm.dmPosition.y;
+ rect.right = (LONG)((LONG)dm.dmPosition.x + (LONG)dm.dmPelsWidth);
+ rect.bottom = (LONG)((long)dm.dmPosition.y + (LONG)dm.dmPelsHeight);
+ EnumDisplayMonitors(NULL, &rect, RGFW_win32_getMonitorHandle, (LPARAM)node);
+
+ RGFW_win32_getMode(&dm, &node->mon.mode);
+
+ MONITORINFOEXW monitorInfo;
+ monitorInfo.cbSize = sizeof(MONITORINFOEXW);
+ GetMonitorInfoW(node->hMonitor, (LPMONITORINFO)&monitorInfo);
+
+ node->mon.x = monitorInfo.rcMonitor.left;
+ node->mon.y = monitorInfo.rcMonitor.top;
+
+ HDC hdc = CreateDCW(monitorInfo.szDevice, NULL, NULL, NULL);
+
+ float dpiX = (float)GetDeviceCaps(hdc, LOGPIXELSX);
+ float dpiY = (float)GetDeviceCaps(hdc, LOGPIXELSY);
+
+ node->mon.scaleX = dpiX / 96.0f;
+ node->mon.scaleY = dpiY / 96.0f;
+ node->mon.pixelRatio = dpiX >= 192.0f ? 2.0f : 1.0f;
+
+ node->mon.physW = (float)GetDeviceCaps(hdc, HORZSIZE) / 25.4f;
+ node->mon.physH = (float)GetDeviceCaps(hdc, VERTSIZE) / 25.4f;
+ DeleteDC(hdc);
+
+#ifndef RGFW_NO_DPI
+ RGFW_LOAD_LIBRARY(RGFW_Shcore_dll, "shcore.dll");
+ RGFW_PROC_DEF(RGFW_Shcore_dll, GetDpiForMonitor);
+
+ if (GetDpiForMonitor != NULL) {
+ u32 x, y;
+ GetDpiForMonitor(node->hMonitor, MDT_EFFECTIVE_DPI, &x, &y);
+ node->mon.scaleX = (float) (x) / (float) 96.0f;
+ node->mon.scaleY = (float) (y) / (float) 96.0f;
+ node->mon.pixelRatio = dpiX >= 192.0f ? 2.0f : 1.0f;
+ }
+#endif
+
+ if (monitorInfo.dwFlags & MONITORINFOF_PRIMARY) {
+ _RGFW->monitors.primary = node;
+ }
+
+ RGFW_monitorCallback(_RGFW->root, &node->mon, RGFW_TRUE);
+}
+
+void RGFW_pollMonitors(void) {
+ for (RGFW_monitorNode* node = _RGFW->monitors.list.head; node; node = node->next) {
+ node->disconnected = RGFW_TRUE;
+ }
+
+ /* loop through display adapters (GPU) */
+ DISPLAY_DEVICEW adapter;
+ DWORD adapterNum;
+ for (adapterNum = 0; ; adapterNum++) {
+ ZeroMemory(&adapter, sizeof(adapter));
+ adapter.cb = sizeof(adapter);
+
+ if (!EnumDisplayDevicesW(NULL, adapterNum, &adapter, 0))
+ break;
+
+ if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE))
+ continue;
+
+ DISPLAY_DEVICEW dd;
+ dd.cb = sizeof(dd);
+
+ /* loop through display devices (monitors) */
+ DWORD deviceNum;
+ for (deviceNum = 0; ; deviceNum++) {
+ ZeroMemory(&dd, sizeof(dd));
+ dd.cb = sizeof(dd);
+
+ if (!EnumDisplayDevicesW(adapter.DeviceName, deviceNum, &dd, 0))
+ break;
+
+ if (!(dd.StateFlags & DISPLAY_DEVICE_ACTIVE))
+ continue;
+
+ RGFW_monitorNode* node;
+ for (node = _RGFW->monitors.list.head; node; node = node->next) {
+ if (node->disconnected == RGFW_TRUE && wcscmp(node->deviceName, dd.DeviceName) == 0) {
+ node->disconnected = RGFW_FALSE;
+ EnumDisplayMonitors(NULL, NULL, RGFW_win32_getMonitorHandle, (LPARAM) &node->mon);
+ break;
+ }
+ }
+
+ if (node) {
+ continue;
+ }
+
+ RGFW_win32_createMonitor(&adapter, &dd);
+ }
+
+ /* if there are no display devices, just use the monitor directly (hack borrowed from GLFW (I'm not giving it back)) */
+ if (deviceNum == 0) {
+ RGFW_monitorNode* node;
+ for (node = _RGFW->monitors.list.head; node; node = node->next) {
+ if (node->disconnected == RGFW_TRUE && wcscmp(node->adapterName, adapter.DeviceName) == 0) {
+ node->disconnected = RGFW_FALSE;
+ break;
+ }
+ }
+
+ if (node) {
+ continue;
+ }
+
+ RGFW_win32_createMonitor(&adapter, NULL);
+ }
+ }
+
+ RGFW_monitors_refresh();
+}
+
+RGFW_monitor* RGFW_window_getMonitor(RGFW_window* win) {
+ HMONITOR src = MonitorFromWindow(win->src.window, MONITOR_DEFAULTTOPRIMARY);
+ RGFW_monitorNode* node = _RGFW->monitors.list.head;
+
+ for (node = _RGFW->monitors.list.head; node; node = node->next) {
+ if (node->hMonitor == src) {
+ return &node->mon;
+ }
+ }
+
+ return RGFW_getPrimaryMonitor();
+}
+
+RGFW_bool RGFW_monitor_setMode(RGFW_monitor* mon, RGFW_monitorMode* mode) {
+ DEVMODEW dm;
+ ZeroMemory(&dm, sizeof(dm));
+ dm.dmSize = sizeof(dm);
+
+ dm.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT;
+ dm.dmPelsWidth = (u32)mode->w;
+ dm.dmPelsHeight = (u32)mode->h;
+
+ dm.dmFields |= DM_DISPLAYFREQUENCY;
+ dm.dmDisplayFrequency = (DWORD)mode->refreshRate;
+
+ dm.dmFields |= DM_BITSPERPEL;
+ dm.dmBitsPerPel = (DWORD)(mode->red + mode->green + mode->blue);
+
+ if (ChangeDisplaySettingsExW(mon->node->adapterName, &dm, NULL, CDS_TEST, NULL) == DISP_CHANGE_SUCCESSFUL) {
+ if (ChangeDisplaySettingsExW(mon->node->adapterName, &dm, NULL, CDS_UPDATEREGISTRY, NULL) == DISP_CHANGE_SUCCESSFUL) {
+ RGFW_win32_getMode(&dm, &mon->mode);
+ return RGFW_TRUE;
+ }
+ return RGFW_FALSE;
+ } else return RGFW_FALSE;
+}
+
+RGFW_bool RGFW_monitor_requestMode(RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request) {
+HMONITOR src = mon->node->hMonitor;
+
+ MONITORINFOEX monitorInfo;
+ monitorInfo.cbSize = sizeof(MONITORINFOEX);
+ GetMonitorInfoA(src, (LPMONITORINFO)&monitorInfo);
+
+ DEVMODEW dm;
+ ZeroMemory(&dm, sizeof(dm));
+ dm.dmSize = sizeof(dm);
+
+ DWORD index = 0;
+
+ for (;;) {
+ if (EnumDisplaySettingsW(mon->node->adapterName, index, &dm) == 0) {
+ break;
+ }
+
+ index += 1;
+
+ if (request & RGFW_monitorScale) {
+ dm.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT;
+ dm.dmPelsWidth = (u32)mode->w;
+ dm.dmPelsHeight = (u32)mode->h;
+ }
+
+ if (request & RGFW_monitorRefresh) {
+ dm.dmFields |= DM_DISPLAYFREQUENCY;
+ dm.dmDisplayFrequency = (DWORD)mode->refreshRate;
+ }
+
+ if (request & RGFW_monitorRGB) {
+ dm.dmFields |= DM_BITSPERPEL;
+ dm.dmBitsPerPel = (DWORD)(mode->red + mode->green + mode->blue);
+ }
+
+ if (ChangeDisplaySettingsExW(mon->node->adapterName, &dm, NULL, CDS_TEST, NULL) == DISP_CHANGE_SUCCESSFUL) {
+ if (ChangeDisplaySettingsExW(mon->node->adapterName, &dm, NULL, CDS_UPDATEREGISTRY, NULL) == DISP_CHANGE_SUCCESSFUL) {
+ RGFW_win32_getMode(&dm, &mon->mode);
+ return RGFW_TRUE;
+ }
+ return RGFW_FALSE;
+ }
+ }
+
+ return RGFW_FALSE;
+}
+
+HICON RGFW_loadHandleImage(u8* data, i32 w, i32 h, RGFW_format format, BOOL icon);
+HICON RGFW_loadHandleImage(u8* data, i32 w, i32 h, RGFW_format format, BOOL icon) {
+ BITMAPV5HEADER bi;
+ ZeroMemory(&bi, sizeof(bi));
+ bi.bV5Size = sizeof(bi);
+ bi.bV5Width = (i32)w;
+ bi.bV5Height = -((LONG) h);
+ bi.bV5Planes = 1;
+ bi.bV5BitCount = (WORD)32;
+ bi.bV5Compression = BI_RGB;
+ HDC dc = GetDC(NULL);
+ u8* target = NULL;
+
+ HBITMAP color = CreateDIBSection(dc,
+ (BITMAPINFO*) &bi, DIB_RGB_COLORS, (void**) &target,
+ NULL, (DWORD) 0);
+
+ RGFW_copyImageData(target, w, h, RGFW_formatBGRA8, data, format, NULL);
+ ReleaseDC(NULL, dc);
+
+ HBITMAP mask = CreateBitmap((i32)w, (i32)h, 1, 1, NULL);
+
+ ICONINFO ii;
+ ZeroMemory(&ii, sizeof(ii));
+ ii.fIcon = icon;
+ ii.xHotspot = (u32)w / 2;
+ ii.yHotspot = (u32)h / 2;
+ ii.hbmMask = mask;
+ ii.hbmColor = color;
+
+ HICON handle = CreateIconIndirect(&ii);
+
+ DeleteObject(color);
+ DeleteObject(mask);
+
+ return handle;
+}
+
+RGFW_mouse* RGFW_createMouseStandard(RGFW_mouseIcon mouse) {
+ u32 mouseIcon = 0;
+
+ switch (mouse) {
+ case RGFW_mouseNormal: mouseIcon = OCR_NORMAL; break;
+ case RGFW_mouseArrow: mouseIcon = OCR_NORMAL; break;
+ case RGFW_mouseIbeam: mouseIcon = OCR_IBEAM; break;
+ case RGFW_mouseWait: mouseIcon = OCR_WAIT; break;
+ case RGFW_mouseCrosshair: mouseIcon = OCR_CROSS; break;
+ case RGFW_mouseProgress: mouseIcon = OCR_APPSTARTING; break;
+ case RGFW_mouseResizeNWSE: mouseIcon = OCR_SIZENWSE; break;
+ case RGFW_mouseResizeNESW: mouseIcon = OCR_SIZENESW; break;
+ case RGFW_mouseResizeEW: mouseIcon = OCR_SIZEWE; break;
+ case RGFW_mouseResizeNS: mouseIcon = OCR_SIZENS; break;
+ case RGFW_mouseResizeAll: mouseIcon = OCR_SIZEALL; break;
+ case RGFW_mouseNotAllowed: mouseIcon = OCR_NO; break;
+ case RGFW_mousePointingHand: mouseIcon = OCR_HAND; break;
+ case RGFW_mouseResizeNW: mouseIcon = OCR_SIZENWSE; break;
+ case RGFW_mouseResizeN: mouseIcon = OCR_SIZENS; break;
+ case RGFW_mouseResizeNE: mouseIcon = OCR_SIZENESW; break;
+ case RGFW_mouseResizeE: mouseIcon = OCR_SIZEWE; break;
+ case RGFW_mouseResizeSE: mouseIcon = OCR_SIZENWSE; break;
+ case RGFW_mouseResizeS: mouseIcon = OCR_SIZENS; break;
+ case RGFW_mouseResizeSW: mouseIcon = OCR_SIZENESW; break;
+ case RGFW_mouseResizeW: mouseIcon = OCR_SIZEWE; break;
+ default: return NULL;
+ }
+
+ char* icon = MAKEINTRESOURCEA(mouseIcon);
+ return LoadCursorA(NULL, icon);
+}
+
+RGFW_mouse* RGFW_createMouse(u8* data, i32 w, i32 h, RGFW_format format) {
+ HCURSOR cursor = (HCURSOR) RGFW_loadHandleImage(data, w, h, format, FALSE);
+ return cursor;
+}
+
+RGFW_bool RGFW_window_setMousePlatform(RGFW_window* win, RGFW_mouse* mouse) {
+ RGFW_ASSERT(win && mouse);
+ SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) mouse);
+ SetCursor((HCURSOR)mouse);
+
+ return RGFW_FALSE;
+}
+
+void RGFW_freeMouse(RGFW_mouse* mouse) {
+ RGFW_ASSERT(mouse);
+ DestroyCursor((HCURSOR)mouse);
+}
+
+void RGFW_window_hide(RGFW_window* win) {
+ ShowWindow(win->src.window, SW_HIDE);
+}
+
+void RGFW_window_show(RGFW_window* win) {
+ if (win->internal.flags & RGFW_windowFocusOnShow) RGFW_window_focus(win);
+ ShowWindow(win->src.window, SW_RESTORE);
+}
+
+void RGFW_window_flash(RGFW_window* win, RGFW_flashRequest request) {
+ if (RGFW_window_isInFocus(win) && request) {
+ return;
+ }
+
+ FLASHWINFO desc;
+ RGFW_MEMZERO(&desc, sizeof(desc));
+
+ desc.cbSize = sizeof(desc);
+ desc.hwnd = win->src.window;
+
+ switch (request) {
+ case RGFW_flashCancel:
+ desc.dwFlags = FLASHW_STOP;
+ break;
+ case RGFW_flashBriefly:
+ desc.dwFlags = FLASHW_TRAY;
+ desc.uCount = 1;
+ break;
+ case RGFW_flashUntilFocused:
+ desc.dwFlags = (FLASHW_TRAY | FLASHW_TIMERNOFG);
+ break;
+ default: break;
+ }
+
+ FlashWindowEx(&desc);
+}
+
+#define RGFW_FREE_LIBRARY(x) if (x != NULL) FreeLibrary(x); x = NULL;
+void RGFW_deinitPlatform(void) {
+ #ifndef RGFW_NO_DPI
+ RGFW_FREE_LIBRARY(RGFW_Shcore_dll);
+ #endif
+
+ #ifndef RGFW_NO_WINMM
+ timeEndPeriod(1);
+ #ifndef RGFW_NO_LOAD_WINMM
+ RGFW_FREE_LIBRARY(RGFW_winmm_dll);
+ #endif
+ #endif
+
+ RGFW_FREE_LIBRARY(RGFW_wgl_dll);
+
+ DestroyWindow(_RGFW->helperWindow);
+ UnregisterClassW(_RGFW->wndClass.lpszClassName, _RGFW->instance);
+
+ RGFW_freeMouse(_RGFW->hiddenMouse);
+}
+
+
+void RGFW_window_closePlatform(RGFW_window* win) {
+ RemovePropW(win->src.window, L"RGFW");
+ ReleaseDC(win->src.window, win->src.hdc); /*!< delete device context */
+ DestroyWindow(win->src.window); /*!< delete window */
+
+ if (win->src.hIconSmall) DestroyIcon(win->src.hIconSmall);
+ if (win->src.hIconBig) DestroyIcon(win->src.hIconBig);
+}
+
+void RGFW_window_move(RGFW_window* win, i32 x, i32 y) {
+ RGFW_ASSERT(win != NULL);
+
+ win->x = x;
+ win->y = y;
+ SetWindowPos(win->src.window, HWND_TOP, win->x, win->y, 0, 0, SWP_NOSIZE);
+}
+
+void RGFW_window_resize(RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+
+ win->w = w;
+ win->h = h;
+ RECT rect = { 0, 0, w, h};
+ DWORD style = RGFW_winapi_window_getStyle(win, win->internal.flags);
+ DWORD exStyle = RGFW_winapi_window_getExStyle(win, win->internal.flags);
+ AdjustWindowRectEx(&rect, style, FALSE, exStyle);
+ SetWindowPos(win->src.window, HWND_TOP, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER);
+}
+
+void RGFW_window_setName(RGFW_window* win, const char* name) {
+ RGFW_ASSERT(win != NULL);
+ if (name == NULL) name = "\0";
+
+ wchar_t wide_name[256];
+ MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_name, 256);
+ SetWindowTextW(win->src.window, wide_name);
+}
+
+#ifndef RGFW_NO_PASSTHROUGH
+void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) {
+ RGFW_ASSERT(win != NULL);
+ COLORREF key = 0;
+ BYTE alpha = 0;
+ DWORD flags = 0;
+ i32 exStyle = GetWindowLongW(win->src.window, GWL_EXSTYLE);
+
+ if (exStyle & WS_EX_LAYERED)
+ GetLayeredWindowAttributes(win->src.window, &key, &alpha, &flags);
+
+ if (passthrough)
+ exStyle |= (WS_EX_TRANSPARENT | WS_EX_LAYERED);
+ else {
+ exStyle &= ~WS_EX_TRANSPARENT;
+ if (exStyle & WS_EX_LAYERED && !(flags & LWA_ALPHA))
+ exStyle &= ~WS_EX_LAYERED;
+ }
+
+ SetWindowLongW(win->src.window, GWL_EXSTYLE, exStyle);
+
+ if (passthrough)
+ SetLayeredWindowAttributes(win->src.window, key, alpha, flags);
+}
+#endif
+
+RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_icon type) {
+ RGFW_ASSERT(win != NULL);
+ #ifndef RGFW_WIN95
+ if (win->src.hIconSmall && (type & RGFW_iconWindow)) DestroyIcon(win->src.hIconSmall);
+ if (win->src.hIconBig && (type & RGFW_iconTaskbar)) DestroyIcon(win->src.hIconBig);
+
+ if (data == NULL) {
+ HICON defaultIcon = LoadIcon(NULL, IDI_APPLICATION);
+ if (type & RGFW_iconWindow)
+ SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)defaultIcon);
+ if (type & RGFW_iconTaskbar)
+ SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)defaultIcon);
+ return RGFW_TRUE;
+ }
+
+ if (type & RGFW_iconWindow) {
+ win->src.hIconSmall = RGFW_loadHandleImage(data, w, h, format, TRUE);
+ SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)win->src.hIconSmall);
+ }
+ if (type & RGFW_iconTaskbar) {
+ win->src.hIconBig = RGFW_loadHandleImage(data, w, h, format, TRUE);
+ SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)win->src.hIconBig);
+ }
+ return RGFW_TRUE;
+ #else
+ RGFW_UNUSED(img);
+ RGFW_UNUSED(type);
+ return RGFW_FALSE;
+ #endif
+}
+
+RGFW_bool RGFW_readClipboardPtr(u8* buffer, size_t capacity, RGFW_dataTransfer* data) {
+ RGFW_ASSERT(data != NULL);
+ /* Open the clipboard */
+ size_t retry = 0;
+ BOOL isOpen = FALSE;
+ while (isOpen == FALSE && retry < 3) {
+ isOpen = OpenClipboard(NULL);
+ retry += 1;
+ }
+
+ if (isOpen == FALSE) return RGFW_FALSE;
+
+ /* Get the clipboard data as a Unicode string */
+ HANDLE hData = GetClipboardData(CF_UNICODETEXT);
+ if (hData == NULL) {
+ CloseClipboard();
+ return RGFW_FALSE;
+ }
+
+ wchar_t* wstr = (wchar_t*) GlobalLock(hData);
+
+ RGFW_bool ret = RGFW_TRUE;
+
+ i32 length = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
+ if (length <= 0) return RGFW_FALSE;
+
+ data->length = (size_t)length;
+ data->type = RGFW_dataText;
+
+ if (buffer != NULL && capacity < data->length) {
+ ret = RGFW_FALSE;
+ } else if (buffer != NULL && data->length) {
+ if (WideCharToMultiByte(CP_UTF8, 0, wstr, -1, (char*)buffer, length, NULL, NULL) <= 0) return RGFW_FALSE;
+ data->data = (const char*)buffer;
+ }
+
+ /* Release the clipboard data */
+ GlobalUnlock(hData);
+ CloseClipboard();
+
+ return ret;
+}
+
+RGFW_bool RGFW_writeClipboard(const RGFW_dataTransfer* data) {
+ RGFW_ASSERT(data != NULL);
+ HANDLE object = GlobalAlloc(GMEM_MOVEABLE, data->length * sizeof(WCHAR));
+ if (!object)
+ return RGFW_FALSE;
+
+ WCHAR* buffer = (WCHAR*) GlobalLock(object);
+ if (!buffer) {
+ GlobalFree(object);
+ return RGFW_FALSE;
+ }
+
+ MultiByteToWideChar(CP_UTF8, 0, data->data, -1, buffer, (i32)data->length);
+ GlobalUnlock(object);
+
+ size_t retry = 0;
+ BOOL isOpen = FALSE;
+ while (isOpen == FALSE && retry < 3) {
+ isOpen = OpenClipboard(NULL);
+ retry += 1;
+ }
+
+ if (isOpen == FALSE) {
+ GlobalFree(object);
+ return RGFW_FALSE;
+ }
+
+ EmptyClipboard();
+ SetClipboardData(CF_UNICODETEXT, object);
+ CloseClipboard();
+
+ return RGFW_TRUE;
+}
+
+void RGFW_window_moveMouse(RGFW_window* win, i32 x, i32 y) {
+ RGFW_ASSERT(win != NULL);
+ win->internal.lastMouseX = x - win->x;
+ win->internal.lastMouseY = y - win->y;
+ SetCursorPos(x, y);
+}
+
+#ifdef RGFW_OPENGL
+RGFW_bool RGFW_extensionSupportedPlatform_OpenGL(const char * extension, size_t len) {
+ const char* extensions = NULL;
+
+ RGFW_proc proc = RGFW_getProcAddress_OpenGL("wglGetExtensionsStringARB");
+ RGFW_proc proc2 = RGFW_getProcAddress_OpenGL("wglGetExtensionsStringEXT");
+
+ if (proc)
+ extensions = ((const char* (*)(HDC))proc)(wglGetCurrentDC());
+ else if (proc2)
+ extensions = ((const char*(*)(void))proc2)();
+ return extensions != NULL && RGFW_extensionSupportedStr(extensions, extension, len);
+}
+
+RGFW_proc RGFW_getProcAddress_OpenGL(const char* procname) {
+ RGFW_proc proc = (RGFW_proc)wglGetProcAddress(procname);
+ if (proc)
+ return proc;
+
+ return (RGFW_proc) GetProcAddress(RGFW_wgl_dll, procname);
+}
+
+void RGFW_win32_loadOpenGLFuncs(HWND dummyWin) {
+ if (wglSwapIntervalEXT != NULL && wglChoosePixelFormatARB != NULL && wglChoosePixelFormatARB != NULL)
+ return;
+
+ HDC dummy_dc = GetDC(dummyWin);
+ u32 pfd_flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
+
+ PIXELFORMATDESCRIPTOR pfd = {sizeof(pfd), 1, pfd_flags, PFD_TYPE_RGBA, 32, 8, PFD_MAIN_PLANE, 32, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 32, 8, 0, PFD_MAIN_PLANE, 0, 0, 0, 0};
+
+ int dummy_pixel_format = ChoosePixelFormat(dummy_dc, &pfd);
+ SetPixelFormat(dummy_dc, dummy_pixel_format, &pfd);
+
+ HGLRC dummy_context = wglCreateContext(dummy_dc);
+
+ HGLRC cur = wglGetCurrentContext();
+ wglMakeCurrent(dummy_dc, dummy_context);
+
+ wglCreateContextAttribsARB = ((PFNWGLCREATECONTEXTATTRIBSARBPROC(WINAPI *)(const char*)) wglGetProcAddress)("wglCreateContextAttribsARB");
+ wglChoosePixelFormatARB = ((PFNWGLCHOOSEPIXELFORMATARBPROC(WINAPI *)(const char*)) wglGetProcAddress)("wglChoosePixelFormatARB");
+
+ wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)(RGFW_proc)wglGetProcAddress("wglSwapIntervalEXT");
+ if (wglSwapIntervalEXT == NULL) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errOpenGLContext, "Failed to load swap interval function");
+ }
+
+ wglMakeCurrent(dummy_dc, cur);
+ wglDeleteContext(dummy_context);
+ ReleaseDC(dummyWin, dummy_dc);
+}
+
+#define WGL_ACCELERATION_ARB 0x2003
+#define WGL_FULL_ACCELERATION_ARB 0x2027
+#define WGL_DRAW_TO_WINDOW_ARB 0x2001
+#define WGL_PIXEL_TYPE_ARB 0x2013
+#define WGL_TYPE_RGBA_ARB 0x202b
+#define WGL_SUPPORT_OPENGL_ARB 0x2010
+#define WGL_COLOR_BITS_ARB 0x2014
+#define WGL_DOUBLE_BUFFER_ARB 0x2011
+#define WGL_ALPHA_BITS_ARB 0x201b
+#define WGL_DEPTH_BITS_ARB 0x2022
+#define WGL_STENCIL_BITS_ARB 0x2023
+#define WGL_STEREO_ARB 0x2012
+#define WGL_AUX_BUFFERS_ARB 0x2024
+#define WGL_RED_BITS_ARB 0x2015
+#define WGL_GREEN_BITS_ARB 0x2017
+#define WGL_BLUE_BITS_ARB 0x2019
+#define WGL_ACCUM_RED_BITS_ARB 0x201e
+#define WGL_ACCUM_GREEN_BITS_ARB 0x201f
+#define WGL_ACCUM_BLUE_BITS_ARB 0x2020
+#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021
+#define WGL_COLORSPACE_SRGB_EXT 0x3089
+#define WGL_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3
+#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097
+#define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0x0000
+#define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098
+#define WGL_CONTEXT_FLAGS_ARB 0x2094
+#define WGL_ACCESS_READ_WRITE_NV 0x00000001
+#define WGL_COVERAGE_SAMPLES_NV 0x2042
+#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004
+#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
+#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
+#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
+#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
+#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
+#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
+#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20A9
+#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097
+#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001
+#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004
+
+RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) {
+ const char flushControl[] = "WGL_ARB_context_flush_control";
+ const char noError[] = "WGL_ARB_create_context_no_error";
+ const char robustness[] = "WGL_ARB_create_context_robustness";
+
+ win->src.ctx.native = ctx;
+ win->src.gfxType = RGFW_gfxNativeOpenGL;
+
+ PIXELFORMATDESCRIPTOR pfd;
+ pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
+ pfd.nVersion = 1;
+ pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
+ pfd.iPixelType = PFD_TYPE_RGBA;
+ pfd.iLayerType = PFD_MAIN_PLANE;
+ pfd.cColorBits = 32;
+ pfd.cAlphaBits = 8;
+ pfd.cDepthBits = 24;
+ pfd.cStencilBits = (BYTE)hints->stencil;
+ pfd.cAuxBuffers = (BYTE)hints->auxBuffers;
+ if (hints->stereo) pfd.dwFlags |= PFD_STEREO;
+
+ /* try to create the pixel format we want for OpenGL and then try to create an OpenGL context for the specified version */
+ if (hints->renderer == RGFW_glSoftware)
+ pfd.dwFlags |= PFD_GENERIC_FORMAT | PFD_GENERIC_ACCELERATED;
+
+ /* get pixel format, default to a basic pixel format */
+ int pixel_format = ChoosePixelFormat(win->src.hdc, &pfd);
+ if (wglChoosePixelFormatARB != NULL) {
+ i32 pixel_format_attribs[50];
+ RGFW_attribStack stack;
+ RGFW_attribStack_init(&stack, pixel_format_attribs, 50);
+
+ RGFW_attribStack_pushAttribs(&stack, WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB);
+ RGFW_attribStack_pushAttribs(&stack, WGL_DRAW_TO_WINDOW_ARB, 1);
+ RGFW_attribStack_pushAttribs(&stack, WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB);
+ RGFW_attribStack_pushAttribs(&stack, WGL_SUPPORT_OPENGL_ARB, 1);
+ RGFW_attribStack_pushAttribs(&stack, WGL_COLOR_BITS_ARB, 32);
+ RGFW_attribStack_pushAttribs(&stack, WGL_DOUBLE_BUFFER_ARB, 1);
+ RGFW_attribStack_pushAttribs(&stack, WGL_ALPHA_BITS_ARB, hints->alpha);
+ RGFW_attribStack_pushAttribs(&stack, WGL_DEPTH_BITS_ARB, hints->depth);
+ RGFW_attribStack_pushAttribs(&stack, WGL_STENCIL_BITS_ARB, hints->stencil);
+ RGFW_attribStack_pushAttribs(&stack, WGL_STEREO_ARB, hints->stereo);
+ RGFW_attribStack_pushAttribs(&stack, WGL_AUX_BUFFERS_ARB, hints->auxBuffers);
+ RGFW_attribStack_pushAttribs(&stack, WGL_RED_BITS_ARB, hints->red);
+ RGFW_attribStack_pushAttribs(&stack, WGL_GREEN_BITS_ARB, hints->blue);
+ RGFW_attribStack_pushAttribs(&stack, WGL_BLUE_BITS_ARB, hints->green);
+ RGFW_attribStack_pushAttribs(&stack, WGL_ACCUM_RED_BITS_ARB, hints->accumRed);
+ RGFW_attribStack_pushAttribs(&stack, WGL_ACCUM_GREEN_BITS_ARB, hints->accumGreen);
+ RGFW_attribStack_pushAttribs(&stack, WGL_ACCUM_BLUE_BITS_ARB, hints->accumBlue);
+ RGFW_attribStack_pushAttribs(&stack, WGL_ACCUM_ALPHA_BITS_ARB, hints->accumAlpha);
+
+ if(hints->sRGB) {
+ if (hints->profile != RGFW_glES)
+ RGFW_attribStack_pushAttribs(&stack, WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, 1);
+ else
+ RGFW_attribStack_pushAttribs(&stack, WGL_COLORSPACE_SRGB_EXT, hints->sRGB);
+ }
+
+ RGFW_attribStack_pushAttribs(&stack, WGL_COVERAGE_SAMPLES_NV, hints->samples);
+
+ RGFW_attribStack_pushAttribs(&stack, 0, 0);
+
+ int new_pixel_format;
+ UINT num_formats;
+ wglChoosePixelFormatARB(win->src.hdc, pixel_format_attribs, 0, 1, &new_pixel_format, &num_formats);
+ if (!num_formats)
+ RGFW_debugCallback(RGFW_typeError, RGFW_errOpenGLContext, "Failed to create a pixel format for WGL");
+ else pixel_format = new_pixel_format;
+ }
+
+ PIXELFORMATDESCRIPTOR suggested;
+ if (!DescribePixelFormat(win->src.hdc, pixel_format, sizeof(suggested), &suggested) ||
+ !SetPixelFormat(win->src.hdc, pixel_format, &pfd))
+ RGFW_debugCallback(RGFW_typeError, RGFW_errOpenGLContext, "Failed to set the WGL pixel format");
+
+ if (wglCreateContextAttribsARB != NULL) {
+ /* create OpenGL/WGL context for the specified version */
+ i32 attribs[40];
+ RGFW_attribStack stack;
+ RGFW_attribStack_init(&stack, attribs, 50);
+
+
+ i32 mask = 0;
+ switch (hints->profile) {
+ case RGFW_glES: mask |= WGL_CONTEXT_ES_PROFILE_BIT_EXT; break;
+ case RGFW_glCompatibility: mask |= WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; break;
+ case RGFW_glForwardCompatibility: mask |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; break;
+ case RGFW_glCore: mask |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB; break;
+ default: mask |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB; break;
+ }
+
+ RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_PROFILE_MASK_ARB, mask);
+
+ if (hints->minor || hints->major) {
+ RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_MAJOR_VERSION_ARB, hints->major);
+ RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_MINOR_VERSION_ARB, hints->minor);
+ }
+
+ if (RGFW_extensionSupportedPlatform_OpenGL(noError, sizeof(noError)))
+ RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_OPENGL_NO_ERROR_ARB, hints->noError);
+
+ if (RGFW_extensionSupportedPlatform_OpenGL(flushControl, sizeof(flushControl))) {
+ if (hints->releaseBehavior == RGFW_glReleaseFlush) {
+ RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); /* WGL_CONTEXT_RELEASE_BEHAVIOR_ARB */
+ } else if (hints->releaseBehavior == RGFW_glReleaseNone) {
+ RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB);
+ }
+ }
+
+ i32 flags = 0;
+ if (hints->debug) flags |= WGL_CONTEXT_DEBUG_BIT_ARB;
+ if (hints->robustness && RGFW_extensionSupportedPlatform_OpenGL(robustness, sizeof(robustness))) flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB;
+ if (flags) {
+ RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_FLAGS_ARB, flags);
+ }
+
+
+ RGFW_attribStack_pushAttribs(&stack, 0, 0);
+
+ win->src.ctx.native->ctx = (HGLRC)wglCreateContextAttribsARB(win->src.hdc, NULL, attribs);
+ }
+
+ if (wglCreateContextAttribsARB == NULL || win->src.ctx.native->ctx == NULL) { /* fall back to a default context (probably OpenGL 2 or something) */
+ RGFW_debugCallback(RGFW_typeError, RGFW_errOpenGLContext, "Failed to create an accelerated OpenGL Context.");
+ win->src.ctx.native->ctx = wglCreateContext(win->src.hdc);
+ }
+
+ ReleaseDC(win->src.window, win->src.hdc);
+ win->src.hdc = GetDC(win->src.window);
+
+ if (hints->share) {
+ wglShareLists((HGLRC)RGFW_getCurrentContext_OpenGL(), hints->share->ctx);
+ }
+
+ wglMakeCurrent(win->src.hdc, win->src.ctx.native->ctx);
+ RGFW_debugCallback(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context initalized.");
+ return RGFW_TRUE;
+}
+
+void RGFW_window_deleteContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx) {
+ wglDeleteContext((HGLRC) ctx->ctx); /*!< delete OpenGL context */
+ win->src.ctx.native->ctx = NULL;
+ RGFW_debugCallback(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context freed.");
+}
+
+void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win) {
+ if (win == NULL)
+ wglMakeCurrent(NULL, NULL);
+ else
+ wglMakeCurrent(win->src.hdc, (HGLRC) win->src.ctx.native->ctx);
+}
+void* RGFW_getCurrentContext_OpenGL(void) {
+ return wglGetCurrentContext();
+}
+void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) {
+ RGFW_ASSERT(win->src.ctx.native);
+ SwapBuffers(win->src.hdc);
+}
+
+void RGFW_window_swapInterval_OpenGL(RGFW_window* win, i32 swapInterval) {
+ RGFW_ASSERT(win != NULL);
+ if (wglSwapIntervalEXT == NULL || wglSwapIntervalEXT(swapInterval) == FALSE)
+ RGFW_debugCallback(RGFW_typeError, RGFW_errOpenGLContext, "Failed to set swap interval");
+}
+#endif
+
+RGFW_bool RGFW_createUTF8FromWideStringWin32(const WCHAR* source, char* output, size_t max) {
+ i32 size = 0;
+ if (source == NULL) {
+ return RGFW_FALSE;
+ }
+ size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL);
+ if (!size) {
+ return RGFW_FALSE;
+ }
+
+ if (size > (i32)max)
+ size = (i32)max;
+
+ if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, output, size, NULL, NULL)) {
+ return RGFW_FALSE;
+ }
+
+ output[size] = 0;
+ return RGFW_TRUE;
+}
+
+#ifdef RGFW_WEBGPU
+WGPUSurface RGFW_window_createSurface_WebGPU(RGFW_window* window, WGPUInstance instance) {
+ WGPUSurfaceDescriptor surfaceDesc = {0};
+ WGPUSurfaceSourceWindowsHWND fromHwnd = {0};
+ fromHwnd.chain.sType = WGPUSType_SurfaceSourceWindowsHWND;
+ fromHwnd.hwnd = window->src.window;
+
+ fromHwnd.hinstance = _RGFW->instance;
+
+ surfaceDesc.nextInChain = (WGPUChainedStruct*)&fromHwnd.chain;
+ return wgpuInstanceCreateSurface(instance, &surfaceDesc);
+}
+#endif
+
+#endif /* RGFW_WINDOWS */
+
+/*
+ End of Windows defines
+*/
+
+
+
+/*
+
+ Start of MacOS defines
+
+
+*/
+
+#if defined(RGFW_MACOS)
+/*
+ based on silicon.h
+ start of cocoa wrapper
+*/
+
+#include <CoreGraphics/CoreGraphics.h>
+#include <ApplicationServices/ApplicationServices.h>
+#include <objc/runtime.h>
+#include <objc/message.h>
+#include <mach/mach_time.h>
+
+#include <Carbon/Carbon.h>
+
+typedef TISInputSourceRef (*PFN_TISCopyCurrentKeyboardLayoutInputSource)(void);
+PFN_TISCopyCurrentKeyboardLayoutInputSource TISCopyCurrentKeyboardLayoutInputSourceSrc;
+#define TISCopyCurrentKeyboardLayoutInputSource TISCopyCurrentKeyboardLayoutInputSourceSrc
+
+typedef CFDataRef (*PFN_TISGetInputSourceProperty)(TISInputSourceRef, CFStringRef);
+PFN_TISGetInputSourceProperty TISGetInputSourcePropertySrc;
+#define TISGetInputSourceProperty TISGetInputSourcePropertySrc
+
+typedef u8 (*PFN_LMGetKbdType)(void);
+PFN_LMGetKbdType LMGetKbdTypeSrc;
+#define LMGetKbdType LMGetKbdTypeSrc
+
+CFStringRef kTISPropertyUnicodeKeyLayoutDataSrc;
+
+#ifndef __OBJC__
+typedef CGRect NSRect;
+typedef CGPoint NSPoint;
+typedef CGSize NSSize;
+
+typedef const char* NSPasteboardType;
+typedef unsigned long NSUInteger;
+typedef long NSInteger;
+typedef NSInteger NSModalResponse;
+
+typedef enum NSRequestUserAttentionType {
+ NSCriticalRequest = 0,
+ NSInformationalRequest = 10
+} NSRequestUserAttentionType;
+
+typedef enum NSApplicationActivationPolicy {
+ NSApplicationActivationPolicyRegular,
+ NSApplicationActivationPolicyAccessory,
+ NSApplicationActivationPolicyProhibited
+} NSApplicationActivationPolicy;
+
+typedef RGFW_ENUM(u32, NSBackingStoreType) {
+ NSBackingStoreRetained = 0,
+ NSBackingStoreNonretained = 1,
+ NSBackingStoreBuffered = 2
+};
+
+typedef RGFW_ENUM(u32, NSWindowStyleMask) {
+ NSWindowStyleMaskBorderless = 0,
+ NSWindowStyleMaskTitled = 1 << 0,
+ NSWindowStyleMaskClosable = 1 << 1,
+ NSWindowStyleMaskMiniaturizable = 1 << 2,
+ NSWindowStyleMaskResizable = 1 << 3,
+ NSWindowStyleMaskTexturedBackground = 1 << 8, /* deprecated */
+ NSWindowStyleMaskUnifiedTitleAndToolbar = 1 << 12,
+ NSWindowStyleMaskFullScreen = 1 << 14,
+ NSWindowStyleMaskFullSizeContentView = 1 << 15,
+ NSWindowStyleMaskUtilityWindow = 1 << 4,
+ NSWindowStyleMaskDocModalWindow = 1 << 6,
+ NSWindowStyleMaskNonactivatingpanel = 1 << 7,
+ NSWindowStyleMaskHUDWindow = 1 << 13
+};
+
+#define NSPasteboardTypeString "public.utf8-plain-text"
+
+typedef RGFW_ENUM(i32, NSDragOperation) {
+ NSDragOperationNone = 0,
+ NSDragOperationCopy = 1,
+ NSDragOperationLink = 2,
+ NSDragOperationGeneric = 4,
+ NSDragOperationPrivate = 8,
+ NSDragOperationMove = 16,
+ NSDragOperationDelete = 32,
+ NSDragOperationEvery = (int)ULONG_MAX
+};
+
+typedef RGFW_ENUM(NSInteger, NSOpenGLContextParameter) {
+ NSOpenGLContextParameterSwapInterval = 222, /* 1 param. 0 -> Don't sync, 1 -> Sync to vertical retrace */
+ NSOpenGLContextParametectxaceOrder = 235, /* 1 param. 1 -> Above Window (default), -1 -> Below Window */
+ NSOpenGLContextParametectxaceOpacity = 236, /* 1 param. 1-> Surface is opaque (default), 0 -> non-opaque */
+ NSOpenGLContextParametectxaceBackingSize = 304, /* 2 params. Width/height of surface backing size */
+ NSOpenGLContextParameterReclaimResources = 308, /* 0 params. */
+ NSOpenGLContextParameterCurrentRendererID = 309, /* 1 param. Retrieves the current renderer ID */
+ NSOpenGLContextParameterGPUVertexProcessing = 310, /* 1 param. Currently processing vertices with GPU (get) */
+ NSOpenGLContextParameterGPUFragmentProcessing = 311, /* 1 param. Currently processing fragments with GPU (get) */
+ NSOpenGLContextParameterHasDrawable = 314, /* 1 param. Boolean returned if drawable is attached */
+ NSOpenGLContextParameterMPSwapsInFlight = 315, /* 1 param. Max number of swaps queued by the MP GL engine */
+
+ NSOpenGLContextParameterSwapRectangle API_DEPRECATED("", macos(10.0, 10.14)) = 200, /* 4 params. Set or get the swap rectangle {x, y, w, h} */
+ NSOpenGLContextParameterSwapRectangleEnable API_DEPRECATED("", macos(10.0, 10.14)) = 201, /* Enable or disable the swap rectangle */
+ NSOpenGLContextParameterRasterizationEnable API_DEPRECATED("", macos(10.0, 10.14)) = 221, /* Enable or disable all rasterization */
+ NSOpenGLContextParameterStateValidation API_DEPRECATED("", macos(10.0, 10.14)) = 301, /* Validate state for multi-screen functionality */
+ NSOpenGLContextParametectxaceSurfaceVolatile API_DEPRECATED("", macos(10.0, 10.14)) = 306, /* 1 param. Surface volatile state */
+};
+
+typedef RGFW_ENUM(NSInteger, NSWindowButton) {
+ NSWindowCloseButton = 0,
+ NSWindowMiniaturizeButton = 1,
+ NSWindowZoomButton = 2,
+ NSWindowToolbarButton = 3,
+ NSWindowDocumentIconButton = 4,
+ NSWindowDocumentVersionsButton = 6,
+ NSWindowFullScreenButton = 7,
+};
+
+#define NSPasteboardTypeURL "public.url"
+#define NSPasteboardTypeFileURL "public.file-url"
+#define NSTrackingMouseEnteredAndExited 0x01
+#define NSTrackingMouseMoved 0x02
+#define NSTrackingCursorUpdate 0x04
+#define NSTrackingActiveWhenFirstResponder 0x10
+#define NSTrackingActiveInKeyWindow 0x20
+#define NSTrackingActiveInActiveApp 0x40
+#define NSTrackingActiveAlways 0x80
+#define NSTrackingAssumeInside 0x100
+#define NSTrackingInVisibleRect 0x200
+#define NSTrackingEnabledDuringMouseDrag 0x400
+enum {
+ NSOpenGLPFAAllRenderers = 1, /* choose from all available renderers */
+ NSOpenGLPFATripleBuffer = 3, /* choose a triple buffered pixel format */
+ NSOpenGLPFADoubleBuffer = 5, /* choose a double buffered pixel format */
+ NSOpenGLPFAAuxBuffers = 7, /* number of aux buffers */
+ NSOpenGLPFAColorSize = 8, /* number of color buffer bits */
+ NSOpenGLPFAAlphaSize = 11, /* number of alpha component bits */
+ NSOpenGLPFADepthSize = 12, /* number of depth buffer bits */
+ NSOpenGLPFAStencilSize = 13, /* number of stencil buffer bits */
+ NSOpenGLPFAAccumSize = 14, /* number of accum buffer bits */
+ NSOpenGLPFAMinimumPolicy = 51, /* never choose smaller buffers than requested */
+ NSOpenGLPFAMaximumPolicy = 52, /* choose largest buffers of type requested */
+ NSOpenGLPFASampleBuffers = 55, /* number of multi sample buffers */
+ NSOpenGLPFASamples = 56, /* number of samples per multi sample buffer */
+ NSOpenGLPFAAuxDepthStencil = 57, /* each aux buffer has its own depth stencil */
+ NSOpenGLPFAColorFloat = 58, /* color buffers store floating point pixels */
+ NSOpenGLPFAMultisample = 59, /* choose multisampling */
+ NSOpenGLPFASupersample = 60, /* choose supersampling */
+ NSOpenGLPFASampleAlpha = 61, /* request alpha filtering */
+ NSOpenGLPFARendererID = 70, /* request renderer by ID */
+ NSOpenGLPFANoRecovery = 72, /* disable all failure recovery systems */
+ NSOpenGLPFAAccelerated = 73, /* choose a hardware accelerated renderer */
+ NSOpenGLPFAClosestPolicy = 74, /* choose the closest color buffer to request */
+ NSOpenGLPFABackingStore = 76, /* back buffer contents are valid after swap */
+ NSOpenGLPFAScreenMask = 84, /* bit mask of supported physical screens */
+ NSOpenGLPFAAllowOfflineRenderers = 96, /* allow use of offline renderers */
+ NSOpenGLPFAAcceleratedCompute = 97, /* choose a hardware accelerated compute device */
+ NSOpenGLPFAOpenGLProfile = 99, /* specify an OpenGL Profile to use */
+ NSOpenGLProfileVersionLegacy = 0x1000, /* The requested profile is a legacy (pre-OpenGL 3.0) profile. */
+ NSOpenGLProfileVersion3_2Core = 0x3200, /* The 3.2 Profile of OpenGL */
+ NSOpenGLProfileVersion4_1Core = 0x3200, /* The 4.1 profile of OpenGL */
+ NSOpenGLPFAVirtualScreenCount = 128, /* number of virtual screens in this format */
+ NSOpenGLPFAStereo = 6,
+ NSOpenGLPFAOffScreen = 53,
+ NSOpenGLPFAFullScreen = 54,
+ NSOpenGLPFASingleRenderer = 71,
+ NSOpenGLPFARobust = 75,
+ NSOpenGLPFAMPSafe = 78,
+ NSOpenGLPFAWindow = 80,
+ NSOpenGLPFAMultiScreen = 81,
+ NSOpenGLPFACompliant = 83,
+ NSOpenGLPFAPixelBuffer = 90,
+ NSOpenGLPFARemotePixelBuffer = 91,
+};
+
+typedef RGFW_ENUM(u32, NSEventType) { /* various types of events */
+ NSEventTypeApplicationDefined = 15,
+};
+typedef unsigned long long NSEventMask;
+
+typedef enum NSEventModifierFlags {
+ NSEventModifierFlagCapsLock = 1 << 16,
+ NSEventModifierFlagShift = 1 << 17,
+ NSEventModifierFlagControl = 1 << 18,
+ NSEventModifierFlagOption = 1 << 19,
+ NSEventModifierFlagCommand = 1 << 20,
+ NSEventModifierFlagNumericPad = 1 << 21
+} NSEventModifierFlags;
+
+typedef RGFW_ENUM(NSUInteger, NSBitmapFormat) {
+ NSBitmapFormatAlphaFirst = 1 << 0, /* 0 means is alpha last (RGBA, CMYKA, etc.) */
+ NSBitmapFormatAlphaNonpremultiplied = 1 << 1, /* 0 means is premultiplied */
+ NSBitmapFormatFloatingpointSamples = 1 << 2, /* 0 is integer */
+
+ NSBitmapFormatSixteenBitLittleEndian = (1 << 8),
+ NSBitmapFormatThirtyTwoBitLittleEndian = (1 << 9),
+ NSBitmapFormatSixteenBitBigEndian = (1 << 10),
+ NSBitmapFormatThirtyTwoBitBigEndian = (1 << 11)
+};
+
+#else
+#import <AppKit/AppKit.h>
+#include <Foundation/Foundation.h>
+#endif /* notdef __OBJC__ */
+
+#ifdef __arm64__
+ /* ARM just uses objc_msgSend */
+#define abi_objc_msgSend_stret objc_msgSend
+#define abi_objc_msgSend_fpret objc_msgSend
+#else /* __i386__ */
+ /* x86 just uses abi_objc_msgSend_fpret and (NSColor *)objc_msgSend_id respectively */
+#define abi_objc_msgSend_stret objc_msgSend_stret
+#define abi_objc_msgSend_fpret objc_msgSend_fpret
+#endif
+
+#define NSAlloc(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("alloc"))
+#define objc_msgSend_bool(x, y) ((BOOL (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y)
+#define objc_msgSend_void(x, y) ((void (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y)
+#define objc_msgSend_void_id(x, y, z) ((void (*)(id, SEL, id))objc_msgSend) ((id)x, (SEL)y, (id)z)
+#define objc_msgSend_uint(x, y) ((NSUInteger (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y)
+#define objc_msgSend_void_bool(x, y, z) ((void (*)(id, SEL, BOOL))objc_msgSend) ((id)(x), (SEL)y, (BOOL)z)
+#define objc_msgSend_bool_void(x, y) ((BOOL (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y)
+#define objc_msgSend_void_SEL(x, y, z) ((void (*)(id, SEL, SEL))objc_msgSend) ((id)(x), (SEL)y, (SEL)z)
+#define objc_msgSend_id(x, y) ((id (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y)
+#define objc_msgSend_id_id(x, y, z) ((id (*)(id, SEL, id))objc_msgSend) ((id)(x), (SEL)y, (id)z)
+#define objc_msgSend_id_bool(x, y, z) ((BOOL (*)(id, SEL, id))objc_msgSend) ((id)(x), (SEL)y, (id)z)
+#define objc_msgSend_int(x, y, z) ((id (*)(id, SEL, int))objc_msgSend) ((id)(x), (SEL)y, (int)z)
+#define objc_msgSend_arr(x, y, z) ((id (*)(id, SEL, int))objc_msgSend) ((id)(x), (SEL)y, (int)z)
+#define objc_msgSend_ptr(x, y, z) ((id (*)(id, SEL, void*))objc_msgSend) ((id)(x), (SEL)y, (void*)z)
+#define objc_msgSend_class(x, y) ((id (*)(Class, SEL))objc_msgSend) ((Class)(x), (SEL)y)
+#define objc_msgSend_class_char(x, y, z) ((id (*)(Class, SEL, char*))objc_msgSend) ((Class)(x), (SEL)y, (char*)z)
+
+#define NSRelease(obj) objc_msgSend_void((id)obj, sel_registerName("release"))
+RGFWDEF id NSString_stringWithUTF8String(const char* str);
+id NSString_stringWithUTF8String(const char* str) {
+ return ((id(*)(id, SEL, const char*))objc_msgSend) ((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), str);
+}
+
+RGFWDEF float RGFW_cocoaYTransform(float y);
+float RGFW_cocoaYTransform(float y) { return (float)(CGDisplayBounds(CGMainDisplayID()).size.height - (double)y - (double)1.0f); }
+
+const char* NSString_to_char(id str);
+const char* NSString_to_char(id str) {
+ return ((const char* (*)(id, SEL)) objc_msgSend) ((id)(id)str, sel_registerName("UTF8String"));
+}
+
+unsigned char* NSBitmapImageRep_bitmapData(id imageRep);
+unsigned char* NSBitmapImageRep_bitmapData(id imageRep) {
+ return ((unsigned char* (*)(id, SEL))objc_msgSend) ((id)imageRep, sel_registerName("bitmapData"));
+}
+
+id NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits);
+id NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits) {
+ SEL func = sel_registerName("initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bitmapFormat:bytesPerRow:bitsPerPixel:");
+
+ return (id) ((id(*)(id, SEL, unsigned char**, NSInteger, NSInteger, NSInteger, NSInteger, bool, bool, id, NSBitmapFormat, NSInteger, NSInteger))objc_msgSend)
+ (NSAlloc((id)objc_getClass("NSBitmapImageRep")), func, planes, width, height, bps, spp, alpha, isPlanar, NSString_stringWithUTF8String(colorSpaceName), bitmapFormat, rowBytes, pixelBits);
+}
+
+id NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha);
+id NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha) {
+ Class nsclass = objc_getClass("NSColor");
+ SEL func = sel_registerName("colorWithSRGBRed:green:blue:alpha:");
+ return ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend)
+ ((id)nsclass, func, red, green, blue, alpha);
+}
+
+id NSPasteboard_generalPasteboard(void);
+id NSPasteboard_generalPasteboard(void) {
+ return (id) objc_msgSend_id((id)objc_getClass("NSPasteboard"), sel_registerName("generalPasteboard"));
+}
+
+id* cstrToNSStringArray(char** strs, size_t len);
+id* cstrToNSStringArray(char** strs, size_t len) {
+ static id nstrs[6];
+ size_t i;
+ for (i = 0; i < len; i++)
+ nstrs[i] = NSString_stringWithUTF8String(strs[i]);
+
+ return nstrs;
+}
+
+const char* NSPasteboard_stringForType(id pasteboard, NSPasteboardType dataType, size_t* len);
+const char* NSPasteboard_stringForType(id pasteboard, NSPasteboardType dataType, size_t* len) {
+ SEL func = sel_registerName("stringForType:");
+ id nsstr = NSString_stringWithUTF8String((const char*)dataType);
+ id nsString = ((id(*)(id, SEL, id))objc_msgSend)(pasteboard, func, nsstr);
+ const char* str = NSString_to_char(nsString);
+ if (len != NULL)
+ *len = (size_t)((NSUInteger(*)(id, SEL, int))objc_msgSend)(nsString, sel_registerName("maximumLengthOfBytesUsingEncoding:"), 4);
+ return str;
+}
+
+id c_array_to_NSArray(void* array, size_t len);
+id c_array_to_NSArray(void* array, size_t len) {
+ return ((id (*)(id, SEL, void*, NSUInteger))objc_msgSend) (NSAlloc(objc_getClass("NSArray")), sel_registerName("initWithObjects:count:"), array, len);
+}
+
+
+void NSregisterForDraggedTypes(id view, NSPasteboardType* newTypes, size_t len);
+void NSregisterForDraggedTypes(id view, NSPasteboardType* newTypes, size_t len) {
+ id* ntypes = cstrToNSStringArray((char**)newTypes, len);
+
+ id array = c_array_to_NSArray(ntypes, len);
+ objc_msgSend_void_id(view, sel_registerName("registerForDraggedTypes:"), array);
+ NSRelease(array);
+}
+
+NSInteger NSPasteBoard_declareTypes(id pasteboard, NSPasteboardType* newTypes, size_t len, void* owner);
+NSInteger NSPasteBoard_declareTypes(id pasteboard, NSPasteboardType* newTypes, size_t len, void* owner) {
+ id* ntypes = cstrToNSStringArray((char**)newTypes, len);
+
+ SEL func = sel_registerName("declareTypes:owner:");
+
+ id array = c_array_to_NSArray(ntypes, len);
+
+ NSInteger output = ((NSInteger(*)(id, SEL, id, void*))objc_msgSend)
+ (pasteboard, func, array, owner);
+ NSRelease(array);
+
+ return output;
+}
+
+#define NSRetain(obj) objc_msgSend_void((id)obj, sel_registerName("retain"))
+
+/*
+ End of cocoa wrapper
+*/
+
+static id RGFW__osxCustomInitWithRGFWWindow(id self, SEL _cmd, RGFW_window* win) {
+ RGFW_UNUSED(_cmd);
+ struct objc_super s = { self, class_getSuperclass(object_getClass(self)) };
+
+ CGRect rect;
+ rect.origin.x = 0;
+ rect.origin.y = 0;
+ rect.size.width = (double)win->w;
+ rect.size.height = (double)win->h;
+
+ self = ((id (*)(struct objc_super*, SEL, CGRect))objc_msgSendSuper)(
+ &s, sel_registerName("initWithFrame:"), rect
+ );
+
+ if (self != nil) {
+ object_setInstanceVariable(self, "RGFW_window", win);
+ object_setInstanceVariable(self, "trackingArea", nil);
+
+ object_setInstanceVariable(
+ self, "markedText",
+ ((id (*)(id, SEL))objc_msgSend)(
+ ((id (*)(Class, SEL))objc_msgSend)(objc_getClass("NSMutableAttributedString"), sel_registerName("alloc")),
+ sel_registerName("init")
+ )
+ );
+
+ ((void (*)(id, SEL))objc_msgSend)(self, sel_registerName("updateTrackingAreas"));
+
+ ((void (*)(id, SEL, id))objc_msgSend)(
+ self, sel_registerName("registerForDraggedTypes:"),
+ ((id (*)(Class, SEL, id))objc_msgSend)(
+ objc_getClass("NSArray"),
+ sel_registerName("arrayWithObject:"),
+ ((id (*)(Class, SEL, const char*))objc_msgSend)(
+ objc_getClass("NSString"),
+ sel_registerName("stringWithUTF8String:"),
+ "public.url"
+ )
+ )
+ );
+ }
+
+ return self;
+}
+
+static u32 RGFW_OnClose(id self) {
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, (const char*)"RGFW_window", (void**)&win);
+ if (win == NULL) return true;
+
+ RGFW_windowCloseCallback(win);
+ return false;
+}
+
+/* NOTE(EimaMei): Fixes the constant clicking when the app is running under a terminal. */
+static bool RGFW__osxAcceptsFirstResponder(void) { return true; }
+static bool RGFW__osxPerformKeyEquivalent(id event) { RGFW_UNUSED(event); return true; }
+
+static NSDragOperation RGFW__osxDraggingEntered(id self, SEL sel, id sender) {
+ RGFW_UNUSED(sel);
+
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL)
+ return 0;
+
+ NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation"));
+ RGFW_dataDragCallback(win, RGFW_dataFile, RGFW_dndActionEnter, (i32) p.x, (i32) (win->h - p.y));
+ return NSDragOperationCopy;
+}
+static NSDragOperation RGFW__osxDraggingUpdated(id self, SEL sel, id sender) {
+ RGFW_UNUSED(sel);
+
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL)
+ return 0;
+ if (!(win->internal.enabledEvents & RGFW_dataDragFlag)) return NSDragOperationCopy;
+
+ NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation"));
+ RGFW_dataDragCallback(win, RGFW_dataFile, RGFW_dndActionMove, (i32) p.x, (i32) (win->h - p.y));
+ return NSDragOperationCopy;
+}
+static bool RGFW__osxPrepareForDragOperation(id self) {
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL || (!(win->internal.enabledEvents & RGFW_dataDropFlag)))
+ return true;
+
+ if (!(win->internal.flags & RGFW_windowAllowDND)) {
+ return false;
+ }
+
+ return true;
+}
+
+static void RGFW__osxDraggingEnded(id self, SEL sel, id sender) {
+ RGFW_UNUSED(sel);
+
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL)
+ return;
+ if (!(win->internal.enabledEvents & RGFW_dataDragFlag)) return;
+
+ NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation"));
+ RGFW_dataDragCallback(win, RGFW_dataFile, RGFW_dndActionExit, (i32) p.x, (i32) (win->h - p.y));
+ return;
+}
+
+static bool RGFW__osxPerformDragOperation(id self, SEL sel, id sender) {
+ RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL || (!(win->internal.enabledEvents & RGFW_dataDropFlag)))
+ return false;
+
+ /* id pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard")); */
+
+ id pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard"));
+
+ /* Get the types of data available on the pasteboard */
+ id types = objc_msgSend_id(pasteBoard, sel_registerName("types"));
+
+ /* Get the string type for file URLs */
+ id fileURLsType = objc_msgSend_class_char(objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "NSFilenamesPboardType");
+
+ /* Check if the pasteboard contains file URLs */
+ if (objc_msgSend_id_bool(types, sel_registerName("containsObject:"), fileURLsType) == 0) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errClipboard, "No files found on the pasteboard.");
+ return 0;
+ }
+
+ id fileURLs = objc_msgSend_id_id(pasteBoard, sel_registerName("propertyListForType:"), fileURLsType);
+ int count = ((int (*)(id, SEL))objc_msgSend)(fileURLs, sel_registerName("count"));
+
+ if (count == 0)
+ return 0;
+
+ u32 i;
+ for (i = 0; i < (u32)count; i++) {
+ id fileURL = objc_msgSend_arr(fileURLs, sel_registerName("objectAtIndex:"), i);
+ const char *filePath = ((const char* (*)(id, SEL))objc_msgSend)(fileURL, sel_registerName("UTF8String"));
+ int string_count = ((int (*)(id, SEL))objc_msgSend)(fileURL, sel_registerName("count"));
+
+ RGFW_dataDropCallback(win, filePath, (size_t)string_count + 1, RGFW_dataFile);
+ }
+
+ return false;
+}
+
+#ifndef RGFW_NO_IOKIT
+#include <IOKit/IOKitLib.h>
+
+float RGFW_osx_getFallbackRefreshRate(CGDirectDisplayID displayID);
+float RGFW_osx_getFallbackRefreshRate(CGDirectDisplayID displayID) {
+ float refreshRate = 0;
+ io_iterator_t it;
+ io_service_t service;
+ CFNumberRef indexRef, clockRef, countRef;
+ u32 clock, count;
+
+#ifdef kIOMainPortDefault
+ if (IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceMatching("IOFramebuffer"), &it) != 0)
+#elif defined(kIOMasterPortDefault)
+ if (IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceMatching("IOFramebuffer"), &it) != 0)
+#endif
+ return RGFW_FALSE;
+
+ while ((service = IOIteratorNext(it)) != 0) {
+ u32 index;
+ indexRef = (CFNumberRef)IORegistryEntryCreateCFProperty(service, CFSTR("IOFramebufferOpenGLIndex"), kCFAllocatorDefault, kNilOptions);
+ if (indexRef == 0) continue;
+
+ if (CFNumberGetValue(indexRef, kCFNumberIntType, &index) && CGOpenGLDisplayMaskToDisplayID(1 << index) == displayID) {
+ CFRelease(indexRef);
+ break;
+ }
+
+ CFRelease(indexRef);
+ }
+
+ if (service) {
+ clockRef = (CFNumberRef)IORegistryEntryCreateCFProperty(service, CFSTR("IOFBCurrentPixelClock"), kCFAllocatorDefault, kNilOptions);
+ if (clockRef) {
+ if (CFNumberGetValue(clockRef, kCFNumberIntType, &clock) && clock) {
+ countRef = (CFNumberRef)IORegistryEntryCreateCFProperty(service, CFSTR("IOFBCurrentPixelCount"), kCFAllocatorDefault, kNilOptions);
+ if (countRef && CFNumberGetValue(countRef, kCFNumberIntType, &count) && count) {
+ refreshRate = (float)((double)clock / (double) count);
+ CFRelease(countRef);
+ }
+ }
+ CFRelease(clockRef);
+ }
+ }
+
+ IOObjectRelease(it);
+ return refreshRate;
+}
+#endif
+
+void RGFW_moveToMacOSResourceDir(void) {
+ char resourcesPath[256];
+
+ CFBundleRef bundle = CFBundleGetMainBundle();
+ if (!bundle)
+ return;
+
+ CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle);
+ CFStringRef last = CFURLCopyLastPathComponent(resourcesURL);
+
+ if (
+ CFStringCompare(CFSTR("Resources"), last, 0) != kCFCompareEqualTo ||
+ CFURLGetFileSystemRepresentation(resourcesURL, true, (u8*) resourcesPath, 255) == 0
+ ) {
+ CFRelease(last);
+ CFRelease(resourcesURL);
+ return;
+ }
+
+ CFRelease(last);
+ CFRelease(resourcesURL);
+
+ chdir(resourcesPath);
+}
+
+static void RGFW__osxDidChangeScreenParameters(id self, SEL _cmd, id notification) {
+ RGFW_UNUSED(self); RGFW_UNUSED(_cmd); RGFW_UNUSED(notification);
+ RGFW_pollMonitors();
+}
+
+static void RGFW__osxWindowDeminiaturize(id self, SEL sel) {
+ RGFW_UNUSED(sel);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ RGFW_windowRestoredCallback(win, win->x, win->y, win->w, win->h);
+
+}
+static void RGFW__osxWindowMiniaturize(id self, SEL sel) {
+ RGFW_UNUSED(sel);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ RGFW_windowMinimizedCallback(win);
+
+}
+
+static void RGFW__osxWindowBecameKey(id self, SEL sel) {
+ RGFW_UNUSED(sel);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ RGFW_windowFocusCallback(win, RGFW_TRUE);
+}
+
+static void RGFW__osxWindowResignKey(id self, SEL sel) {
+ RGFW_UNUSED(sel);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ RGFW_windowFocusCallback(win, RGFW_FALSE);
+}
+
+static void RGFW__osxDidWindowResize(id self, SEL _cmd, id notification) {
+ RGFW_UNUSED(_cmd); RGFW_UNUSED(notification);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ NSRect frame;
+ if (win->src.view) frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame"));
+ else return;
+
+ if (frame.size.width == 0 || frame.size.height == 0) return;
+ win->w = (i32)frame.size.width;
+ win->h = (i32)frame.size.height;
+
+ RGFW_monitor* mon = RGFW_window_getMonitor(win);
+ if (mon == NULL) return;
+
+ if ((i32)mon->mode.w == win->w && (i32)mon->mode.h - 102 <= win->h) {
+ RGFW_windowMaximizedCallback(win, 0, 0, win->w, win->h);
+ } else if (win->internal.flags & RGFW_windowMaximize) {
+ RGFW_windowRestoredCallback(win, win->x, win->y, win->w, win->h);
+ }
+
+ RGFW_windowResizedCallback(win, win->w, win->h);
+}
+
+static void RGFW__osxWindowMove(id self, SEL sel) {
+ RGFW_UNUSED(sel);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame"));
+ NSRect content = ((NSRect(*)(id, SEL, NSRect))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("contentRectForFrameRect:"), frame);
+
+ float y = RGFW_cocoaYTransform((float)(content.origin.y + content.size.height - 1));
+
+ RGFW_windowMovedCallback(win, (i32)content.origin.x, (i32)y);
+}
+
+static void RGFW__osxViewDidChangeBackingProperties(id self, SEL _cmd) {
+ RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ RGFW_monitor* mon = RGFW_window_getMonitor(win);
+ if (mon == NULL) return;
+
+ RGFW_scaleUpdatedCallback(win, mon->scaleX, mon->scaleY);
+}
+
+static BOOL RGFW__osxWantsUpdateLayer(id self, SEL _cmd) { RGFW_UNUSED(self); RGFW_UNUSED(_cmd); return YES; }
+
+static void RGFW__osxUpdateLayer(id self, SEL _cmd) {
+ RGFW_UNUSED(self); RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+ RGFW_windowRefreshCallback(win, 0, 0, win->w, win->h);
+}
+
+static void RGFW__osxDrawRect(id self, SEL _cmd, CGRect rect) {
+ RGFW_UNUSED(rect); RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ float y = RGFW_cocoaYTransform((float)(rect.size.height - 1));
+
+ RGFW_windowRefreshCallback(win, (i32)rect.origin.x, (i32)y, (i32)rect.size.width, (i32)rect.size.height);
+}
+
+static void RGFW__osxMouseEntered(id self, SEL _cmd, id event) {
+ RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ NSPoint p = ((NSPoint(*)(id, SEL))objc_msgSend)(event, sel_registerName("locationInWindow"));
+ RGFW_mouseNotifyCallback(win, (i32)p.x, (i32)(win->h - p.y), 1);
+}
+
+static void RGFW__osxMouseExited(id self, SEL _cmd, id event) {
+ RGFW_UNUSED(_cmd); RGFW_UNUSED(event);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ RGFW_mouseNotifyCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, RGFW_FALSE);
+}
+
+static void RGFW__osxKeyDown(id self, SEL _cmd, id event) {
+ RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL || !(win->internal.enabledEvents & RGFW_keyPressedFlag)) return;
+
+ u32 key = (u16)((u32(*)(id, SEL))objc_msgSend)(event, sel_registerName("keyCode"));
+
+ RGFW_key value = (u8)RGFW_apiKeyToRGFW(key);
+ RGFW_bool repeat = RGFW_isKeyDown(value);
+
+ RGFW_keyCallback(win, value, win->internal.mod, repeat, 1);
+
+ id nsstring = ((id(*)(id, SEL))objc_msgSend)(event, sel_registerName("charactersIgnoringModifiers"));
+ const char* string = NSString_to_char(nsstring);
+ size_t count = (size_t)((int (*)(id, SEL))objc_msgSend)(nsstring, sel_registerName("length"));
+
+ for (size_t index = 0; index < count;
+ RGFW_keyCharCallback(win, RGFW_decodeUTF8(&string[index], &index))
+ );
+}
+
+static void RGFW__osxKeyUp(id self, SEL _cmd, id event) {
+ RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL || !(win->internal.enabledEvents & RGFW_keyReleasedFlag)) return;
+
+ u32 key = (u16)((u32(*)(id, SEL))objc_msgSend)(event, sel_registerName("keyCode"));
+
+ RGFW_key value = (u8)RGFW_apiKeyToRGFW(key);
+
+ RGFW_keyCallback(win, value, win->internal.mod, RGFW_FALSE, 0);
+}
+
+static void RGFW__osxFlagsChanged(id self, SEL _cmd, id event) {
+ RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ RGFW_key value = 0;
+ RGFW_bool pressed = RGFW_FALSE;
+
+ u32 flags = (u32)((u32(*)(id, SEL))objc_msgSend)(event, sel_registerName("modifierFlags"));
+ RGFW_keyUpdateKeyModsEx(win,
+ ((u32)(flags & NSEventModifierFlagCapsLock) % 255),
+ ((flags & NSEventModifierFlagNumericPad) % 255),
+ ((flags & NSEventModifierFlagControl) % 255),
+ ((flags & NSEventModifierFlagOption) % 255),
+ ((flags & NSEventModifierFlagShift) % 255),
+ ((flags & NSEventModifierFlagCommand) % 255), 0);
+ u8 i;
+ for (i = 0; i < 9; i++)
+ _RGFW->keyboard[i + RGFW_keyCapsLock].prev = _RGFW->keyboard[i + RGFW_keyCapsLock].current;
+
+ for (i = 0; i < 5; i++) {
+ u32 shift = (1 << (i + 16));
+ RGFW_key key = i + RGFW_keyCapsLock;
+ if ((flags & shift) && !RGFW_isKeyDown((u8)key)) {
+ pressed = RGFW_TRUE;
+ value = (u8)key;
+ break;
+ }
+ if (!(flags & shift) && RGFW_isKeyDown((u8)key)) {
+ pressed = RGFW_FALSE;
+ value = (u8)key;
+ break;
+ }
+ }
+
+ RGFW_keyCallback(win, value, win->internal.mod, RGFW_isKeyDown(value) && pressed, pressed);
+
+ if (value != RGFW_keyCapsLock) {
+ RGFW_keyCallback(win, value + 4, win->internal.mod, RGFW_isKeyDown(value + 4) && pressed, pressed);
+ }
+
+}
+
+static void RGFW__osxMouseMoved(id self, SEL _cmd, id event) {
+ RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ NSPoint p = ((NSPoint(*)(id, SEL))objc_msgSend)(event, sel_registerName("locationInWindow"));
+
+ CGFloat vecX = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("deltaX"));
+ CGFloat vecY = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("deltaY"));
+
+ RGFW_mouseMotionCallback(win, (i32)p.x, (i32)(win->h - p.y));
+ RGFW_rawMotionCallback(win, (float)vecX, (float)vecY);
+}
+
+static void RGFW__osxMouseDown(id self, SEL _cmd, id event) {
+ RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ u32 buttonNumber = (u32)((u32(*)(id, SEL))objc_msgSend)(event, sel_registerName("buttonNumber"));
+
+ RGFW_mouseButton value = 0;
+ switch (buttonNumber) {
+ case 0: value = RGFW_mouseLeft; break;
+ case 1: value = RGFW_mouseRight; break;
+ case 2: value = RGFW_mouseMiddle; break;
+ default: value = (u8)buttonNumber;
+ }
+
+ RGFW_mouseButtonCallback(win, value, 1);
+}
+
+static void RGFW__osxMouseUp(id self, SEL _cmd, id event) {
+ RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ u32 buttonNumber = (u32)((u32(*)(id, SEL))objc_msgSend)(event, sel_registerName("buttonNumber"));
+
+ RGFW_mouseButton value = 0;
+ switch (buttonNumber) {
+ case 0: value = RGFW_mouseLeft; break;
+ case 1: value = RGFW_mouseRight; break;
+ case 2: value = RGFW_mouseMiddle; break;
+ default: value = (u8)buttonNumber;
+ }
+
+ RGFW_mouseButtonCallback(win, value, 0);
+}
+
+static void RGFW__osxScrollWheel(id self, SEL _cmd, id event) {
+ RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ float deltaX = (float)((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("deltaX"));
+ float deltaY = (float)((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("deltaY"));
+
+ RGFW_mouseScrollCallback(win, deltaX, deltaY);
+}
+
+RGFW_format RGFW_nativeFormat(void) { return RGFW_formatRGBA8; }
+
+RGFW_bool RGFW_createSurfacePtr(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) {
+ surface->data = data;
+ surface->w = w;
+ surface->h = h;
+ surface->format = format;
+ surface->native.format = RGFW_formatRGBA8;
+
+ surface->native.buffer = (u8*)RGFW_ALLOC((size_t)(w * h * 4));
+ return RGFW_TRUE;
+}
+
+void RGFW_surface_freePtr(RGFW_surface* surface) {
+ RGFW_FREE(surface->native.buffer);
+}
+
+void RGFW_window_blitSurface(RGFW_window* win, RGFW_surface* surface) {
+ id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ pool = objc_msgSend_id(pool, sel_registerName("init"));
+
+ int minW = RGFW_MIN(win->w, surface->w);
+ int minH = RGFW_MIN(win->h, surface->h);
+
+ RGFW_monitor* mon = RGFW_window_getMonitor(win);
+ if (mon == NULL) return;
+
+ minW = (i32)((float)minW * mon->pixelRatio);
+ minH = (i32)((float)minH * mon->pixelRatio);
+
+ surface->native.rep = (void*)NSBitmapImageRep_initWithBitmapData(&surface->native.buffer, minW, minH, 8, 4, true, false, "NSDeviceRGBColorSpace", 1 << 1, (u32)surface->w * 4, 32);
+
+ id image = ((id (*)(Class, SEL))objc_msgSend)(objc_getClass("NSImage"), sel_getUid("alloc"));
+ NSSize size = (NSSize){(double)minW, (double)minH};
+ image = ((id (*)(id, SEL, NSSize))objc_msgSend)((id)image, sel_getUid("initWithSize:"), size);
+
+ RGFW_copyImageData(NSBitmapImageRep_bitmapData((id)surface->native.rep), surface->w, minH, RGFW_formatRGBA8, surface->data, surface->native.format, surface->convertFunc);
+ ((void (*)(id, SEL, id))objc_msgSend)((id)image, sel_getUid("addRepresentation:"), (id)surface->native.rep);
+
+ id layer = ((id (*)(id, SEL))objc_msgSend)((id)win->src.view, sel_getUid("layer"));
+ ((void (*)(id, SEL, id))objc_msgSend)(layer, sel_getUid("setContents:"), (id)image);
+
+ NSRelease(image);
+ NSRelease(surface->native.rep);
+
+ objc_msgSend_bool_void(pool, sel_registerName("drain"));
+}
+
+void* RGFW_window_getView_OSX(RGFW_window* win) { return win->src.view; }
+
+void RGFW_window_setLayer_OSX(RGFW_window* win, void* layer) {
+ objc_msgSend_void_id((id)win->src.view, sel_registerName("setLayer:"), (id)layer);
+}
+
+void* RGFW_getLayer_OSX(void) {
+ return objc_msgSend_class((id)objc_getClass("CAMetalLayer"), (SEL)sel_registerName("layer"));
+}
+void* RGFW_window_getWindow_OSX(RGFW_window* win) { return win->src.window; }
+
+void RGFW_initKeycodesPlatform(void) {
+ _RGFW->keycodes[0x1D] = RGFW_key0;
+ _RGFW->keycodes[0x12] = RGFW_key1;
+ _RGFW->keycodes[0x13] = RGFW_key2;
+ _RGFW->keycodes[0x14] = RGFW_key3;
+ _RGFW->keycodes[0x15] = RGFW_key4;
+ _RGFW->keycodes[0x17] = RGFW_key5;
+ _RGFW->keycodes[0x16] = RGFW_key6;
+ _RGFW->keycodes[0x1A] = RGFW_key7;
+ _RGFW->keycodes[0x1C] = RGFW_key8;
+ _RGFW->keycodes[0x19] = RGFW_key9;
+ _RGFW->keycodes[0x00] = RGFW_keyA;
+ _RGFW->keycodes[0x0B] = RGFW_keyB;
+ _RGFW->keycodes[0x08] = RGFW_keyC;
+ _RGFW->keycodes[0x02] = RGFW_keyD;
+ _RGFW->keycodes[0x0E] = RGFW_keyE;
+ _RGFW->keycodes[0x03] = RGFW_keyF;
+ _RGFW->keycodes[0x05] = RGFW_keyG;
+ _RGFW->keycodes[0x04] = RGFW_keyH;
+ _RGFW->keycodes[0x22] = RGFW_keyI;
+ _RGFW->keycodes[0x26] = RGFW_keyJ;
+ _RGFW->keycodes[0x28] = RGFW_keyK;
+ _RGFW->keycodes[0x25] = RGFW_keyL;
+ _RGFW->keycodes[0x2E] = RGFW_keyM;
+ _RGFW->keycodes[0x2D] = RGFW_keyN;
+ _RGFW->keycodes[0x1F] = RGFW_keyO;
+ _RGFW->keycodes[0x23] = RGFW_keyP;
+ _RGFW->keycodes[0x0C] = RGFW_keyQ;
+ _RGFW->keycodes[0x0F] = RGFW_keyR;
+ _RGFW->keycodes[0x01] = RGFW_keyS;
+ _RGFW->keycodes[0x11] = RGFW_keyT;
+ _RGFW->keycodes[0x20] = RGFW_keyU;
+ _RGFW->keycodes[0x09] = RGFW_keyV;
+ _RGFW->keycodes[0x0D] = RGFW_keyW;
+ _RGFW->keycodes[0x07] = RGFW_keyX;
+ _RGFW->keycodes[0x10] = RGFW_keyY;
+ _RGFW->keycodes[0x06] = RGFW_keyZ;
+ _RGFW->keycodes[0x27] = RGFW_keyApostrophe;
+ _RGFW->keycodes[0x2A] = RGFW_keyBackSlash;
+ _RGFW->keycodes[0x2B] = RGFW_keyComma;
+ _RGFW->keycodes[0x18] = RGFW_keyEquals;
+ _RGFW->keycodes[0x32] = RGFW_keyBacktick;
+ _RGFW->keycodes[0x21] = RGFW_keyBracket;
+ _RGFW->keycodes[0x1B] = RGFW_keyMinus;
+ _RGFW->keycodes[0x2F] = RGFW_keyPeriod;
+ _RGFW->keycodes[0x1E] = RGFW_keyCloseBracket;
+ _RGFW->keycodes[0x29] = RGFW_keySemicolon;
+ _RGFW->keycodes[0x2C] = RGFW_keySlash;
+ _RGFW->keycodes[0x0A] = RGFW_keyWorld1;
+ _RGFW->keycodes[0x33] = RGFW_keyBackSpace;
+ _RGFW->keycodes[0x39] = RGFW_keyCapsLock;
+ _RGFW->keycodes[0x75] = RGFW_keyDelete;
+ _RGFW->keycodes[0x7D] = RGFW_keyDown;
+ _RGFW->keycodes[0x77] = RGFW_keyEnd;
+ _RGFW->keycodes[0x24] = RGFW_keyEnter;
+ _RGFW->keycodes[0x35] = RGFW_keyEscape;
+ _RGFW->keycodes[0x7A] = RGFW_keyF1;
+ _RGFW->keycodes[0x78] = RGFW_keyF2;
+ _RGFW->keycodes[0x63] = RGFW_keyF3;
+ _RGFW->keycodes[0x76] = RGFW_keyF4;
+ _RGFW->keycodes[0x60] = RGFW_keyF5;
+ _RGFW->keycodes[0x61] = RGFW_keyF6;
+ _RGFW->keycodes[0x62] = RGFW_keyF7;
+ _RGFW->keycodes[0x64] = RGFW_keyF8;
+ _RGFW->keycodes[0x65] = RGFW_keyF9;
+ _RGFW->keycodes[0x6D] = RGFW_keyF10;
+ _RGFW->keycodes[0x67] = RGFW_keyF11;
+ _RGFW->keycodes[0x6F] = RGFW_keyF12;
+ _RGFW->keycodes[0x69] = RGFW_keyPrintScreen;
+ _RGFW->keycodes[0x6B] = RGFW_keyF14;
+ _RGFW->keycodes[0x71] = RGFW_keyF15;
+ _RGFW->keycodes[0x6A] = RGFW_keyF16;
+ _RGFW->keycodes[0x40] = RGFW_keyF17;
+ _RGFW->keycodes[0x4F] = RGFW_keyF18;
+ _RGFW->keycodes[0x50] = RGFW_keyF19;
+ _RGFW->keycodes[0x5A] = RGFW_keyF20;
+ _RGFW->keycodes[0x73] = RGFW_keyHome;
+ _RGFW->keycodes[0x72] = RGFW_keyInsert;
+ _RGFW->keycodes[0x7B] = RGFW_keyLeft;
+ _RGFW->keycodes[0x3A] = RGFW_keyAltL;
+ _RGFW->keycodes[0x3B] = RGFW_keyControlL;
+ _RGFW->keycodes[0x38] = RGFW_keyShiftL;
+ _RGFW->keycodes[0x37] = RGFW_keySuperL;
+ _RGFW->keycodes[0x6E] = RGFW_keyMenu;
+ _RGFW->keycodes[0x47] = RGFW_keyNumLock;
+ _RGFW->keycodes[0x79] = RGFW_keyPageDown;
+ _RGFW->keycodes[0x74] = RGFW_keyPageUp;
+ _RGFW->keycodes[0x7C] = RGFW_keyRight;
+ _RGFW->keycodes[0x3D] = RGFW_keyAltR;
+ _RGFW->keycodes[0x3E] = RGFW_keyControlR;
+ _RGFW->keycodes[0x3C] = RGFW_keyShiftR;
+ _RGFW->keycodes[0x36] = RGFW_keySuperR;
+ _RGFW->keycodes[0x31] = RGFW_keySpace;
+ _RGFW->keycodes[0x30] = RGFW_keyTab;
+ _RGFW->keycodes[0x7E] = RGFW_keyUp;
+ _RGFW->keycodes[0x52] = RGFW_keyPad0;
+ _RGFW->keycodes[0x53] = RGFW_keyPad1;
+ _RGFW->keycodes[0x54] = RGFW_keyPad2;
+ _RGFW->keycodes[0x55] = RGFW_keyPad3;
+ _RGFW->keycodes[0x56] = RGFW_keyPad4;
+ _RGFW->keycodes[0x57] = RGFW_keyPad5;
+ _RGFW->keycodes[0x58] = RGFW_keyPad6;
+ _RGFW->keycodes[0x59] = RGFW_keyPad7;
+ _RGFW->keycodes[0x5B] = RGFW_keyPad8;
+ _RGFW->keycodes[0x5C] = RGFW_keyPad9;
+ _RGFW->keycodes[0x45] = RGFW_keyPadSlash;
+ _RGFW->keycodes[0x41] = RGFW_keyPadPeriod;
+ _RGFW->keycodes[0x4B] = RGFW_keyPadSlash;
+ _RGFW->keycodes[0x4C] = RGFW_keyPadReturn;
+ _RGFW->keycodes[0x51] = RGFW_keyPadEqual;
+ _RGFW->keycodes[0x43] = RGFW_keyPadMultiply;
+ _RGFW->keycodes[0x4E] = RGFW_keyPadMinus;
+}
+
+i32 RGFW_initPlatform(void) {
+ _RGFW->tisBundle = (void*)CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIToolbox"));
+
+ TISGetInputSourcePropertySrc = (PFN_TISGetInputSourceProperty)CFBundleGetFunctionPointerForName((CFBundleRef)_RGFW->tisBundle, CFSTR("TISGetInputSourceProperty"));
+ TISCopyCurrentKeyboardLayoutInputSourceSrc = (PFN_TISCopyCurrentKeyboardLayoutInputSource)CFBundleGetFunctionPointerForName((CFBundleRef)_RGFW->tisBundle, CFSTR("TISCopyCurrentKeyboardLayoutInputSource"));
+ LMGetKbdTypeSrc = (PFN_LMGetKbdType)CFBundleGetFunctionPointerForName((CFBundleRef)_RGFW->tisBundle, CFSTR("LMGetKbdType"));
+
+ CFStringRef* cfStr = (CFStringRef*)CFBundleGetDataPointerForName((CFBundleRef)_RGFW->tisBundle, CFSTR("kTISPropertyUnicodeKeyLayoutData"));;
+ if (cfStr) kTISPropertyUnicodeKeyLayoutDataSrc = *cfStr;
+
+ class_addMethod(objc_getClass("NSObject"), sel_registerName("windowShouldClose:"), (IMP)(void*)RGFW_OnClose, 0);
+
+ /* NOTE(EimaMei): Fixes the 'Boop' sfx from constantly playing each time you click a key. Only a problem when running in the terminal. */
+ class_addMethod(objc_getClass("NSWindowClass"), sel_registerName("acceptsFirstResponder:"), (IMP)(void*)RGFW__osxAcceptsFirstResponder, 0);
+ class_addMethod(objc_getClass("NSWindowClass"), sel_registerName("performKeyEquivalent:"), (IMP)(void*)RGFW__osxPerformKeyEquivalent, 0);
+
+ _RGFW->NSApp = objc_msgSend_id(objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
+
+ NSRetain(_RGFW->NSApp);
+
+ _RGFW->customNSAppDelegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "RGFWNSAppDelegate", 0);
+ class_addMethod((Class)_RGFW->customNSAppDelegateClass, sel_registerName("applicationDidChangeScreenParameters:"), (IMP)RGFW__osxDidChangeScreenParameters, "v@:@");
+ objc_registerClassPair((Class)_RGFW->customNSAppDelegateClass);
+ _RGFW->customNSAppDelegate = objc_msgSend_id(NSAlloc(_RGFW->customNSAppDelegateClass), sel_registerName("init"));
+
+ objc_msgSend_void_id(_RGFW->NSApp, sel_registerName("setDelegate:"), _RGFW->customNSAppDelegate);
+
+ ((void (*)(id, SEL, NSUInteger))objc_msgSend) ((id)_RGFW->NSApp, sel_registerName("setActivationPolicy:"), NSApplicationActivationPolicyRegular);
+
+ _RGFW->customViewClasses[0] = objc_allocateClassPair(objc_getClass("NSView"), "RGFWCustomView", 0);
+ _RGFW->customViewClasses[1] = objc_allocateClassPair(objc_getClass("NSOpenGLView"), "RGFWOpenGLCustomView", 0);
+ for (size_t i = 0; i < 2; i++) {
+ class_addIvar((Class)_RGFW->customViewClasses[i], "RGFW_window", sizeof(RGFW_window*), sizeof(RGFW_window*), "L");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("drawRect:"), (IMP)RGFW__osxDrawRect, "v@:{CGRect=ffff}");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("viewDidChangeBackingProperties"), (IMP)RGFW__osxViewDidChangeBackingProperties, "v@:");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseDown:"), (IMP)RGFW__osxMouseDown, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("rightMouseDown:"), (IMP)RGFW__osxMouseDown, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("otherMouseDown:"), (IMP)RGFW__osxMouseDown, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseUp:"), (IMP)RGFW__osxMouseUp, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("rightMouseUp:"), (IMP)RGFW__osxMouseUp, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("otherMouseUp:"), (IMP)RGFW__osxMouseUp, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("scrollWheel:"), (IMP)RGFW__osxScrollWheel, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseDragged:"), (IMP)RGFW__osxMouseMoved, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("rightMouseDragged:"), (IMP)RGFW__osxMouseMoved, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("otherMouseDragged:"), (IMP)RGFW__osxMouseMoved, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("keyDown:"), (IMP)RGFW__osxKeyDown, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("keyUp:"), (IMP)RGFW__osxKeyUp, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseMoved:"), (IMP)RGFW__osxMouseMoved, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseEntered:"), (IMP)RGFW__osxMouseEntered, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseExited:"), (IMP)RGFW__osxMouseExited, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("flagsChanged:"), (IMP)RGFW__osxFlagsChanged, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_getUid("acceptsFirstResponder"), (IMP)RGFW__osxAcceptsFirstResponder, "B@:");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("initWithRGFWWindow:"), (IMP)RGFW__osxCustomInitWithRGFWWindow, "@@:{CGRect={CGPoint=dd}{CGSize=dd}}");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("wantsUpdateLayer"), (IMP)RGFW__osxWantsUpdateLayer, "B@:");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("updateLayer"), (IMP)RGFW__osxUpdateLayer, "v@:");
+ objc_registerClassPair((Class)_RGFW->customViewClasses[i]);
+ }
+
+ _RGFW->customWindowDelegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "RGFWWindowDelegate", 0);
+ class_addIvar((Class)_RGFW->customWindowDelegateClass, "RGFW_window", sizeof(RGFW_window*), sizeof(RGFW_window*), "L");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidResize:"), (IMP)RGFW__osxDidWindowResize, "v@:@");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidMove:"), (IMP) RGFW__osxWindowMove, "");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidMiniaturize:"), (IMP) RGFW__osxWindowMiniaturize, "");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidDeminiaturize:"), (IMP) RGFW__osxWindowDeminiaturize, "");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidBecomeKey:"), (IMP) RGFW__osxWindowBecameKey, "");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidResignKey:"), (IMP) RGFW__osxWindowResignKey, "");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("draggingEntered:"), (IMP)RGFW__osxDraggingEntered, "l@:@");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("draggingUpdated:"), (IMP)RGFW__osxDraggingUpdated, "l@:@");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("draggingExited:"), (IMP)RGFW__osxDraggingEnded, "v@:@");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("draggingEnded:"), (IMP)RGFW__osxDraggingEnded, "v@:@");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("prepareForDragOperation:"), (IMP)RGFW__osxPrepareForDragOperation, "B@:@");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("performDragOperation:"), (IMP)RGFW__osxPerformDragOperation, "B@:@");
+ objc_registerClassPair((Class)_RGFW->customWindowDelegateClass);
+ return 0;
+}
+
+void RGFW_osx_initView(RGFW_window* win) {
+ NSRect contentRect;
+ contentRect.origin.x = 0;
+ contentRect.origin.y = 0;
+ contentRect.size.width = (double)win->w;
+ contentRect.size.height = (double)win->h;
+ ((void(*)(id, SEL, CGRect))objc_msgSend)((id)win->src.view, sel_registerName("setFrame:"), contentRect);
+
+
+ if (RGFW_COCOA_FRAME_NAME)
+ objc_msgSend_ptr(win->src.view, sel_registerName("setFrameAutosaveName:"), RGFW_COCOA_FRAME_NAME);
+
+ object_setInstanceVariable((id)win->src.view, "RGFW_window", win);
+ objc_msgSend_void_id((id)win->src.window, sel_registerName("setContentView:"), win->src.view);
+ objc_msgSend_void_bool(win->src.view, sel_registerName("setWantsLayer:"), true);
+ objc_msgSend_int((id)win->src.view, sel_registerName("setLayerContentsPlacement:"), 4);
+
+ id trackingArea = objc_msgSend_id(objc_getClass("NSTrackingArea"), sel_registerName("alloc"));
+ trackingArea = ((id (*)(id, SEL, NSRect, NSUInteger, id, id))objc_msgSend)(
+ trackingArea,
+ sel_registerName("initWithRect:options:owner:userInfo:"),
+ contentRect,
+ NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect,
+ (id)win->src.view,
+ nil
+ );
+
+ ((void (*)(id, SEL, id))objc_msgSend)((id)win->src.view, sel_registerName("addTrackingArea:"), trackingArea);
+ ((void (*)(id, SEL))objc_msgSend)(trackingArea, sel_registerName("release"));
+}
+
+RGFW_window* RGFW_createWindowPlatform(const char* name, RGFW_windowFlags flags, RGFW_window* win) {
+ /* RR Create an autorelease pool */
+ id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ pool = objc_msgSend_id(pool, sel_registerName("init"));
+
+ RGFW_window_setMouseDefault(win);
+
+ NSRect windowRect;
+ windowRect.origin.x = (double)win->x;
+ windowRect.origin.y = (double)RGFW_cocoaYTransform((float)(win->y + win->h - 1));
+ windowRect.size.width = (double)win->w;
+ windowRect.size.height = (double)win->h;
+ NSBackingStoreType macArgs = (NSBackingStoreType)(NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSBackingStoreBuffered | NSWindowStyleMaskTitled);
+
+ if (!(flags & RGFW_windowNoResize))
+ macArgs = (NSBackingStoreType)(macArgs | (NSBackingStoreType)NSWindowStyleMaskResizable);
+ if (!(flags & RGFW_windowNoBorder))
+ macArgs = (NSBackingStoreType)(macArgs | (NSBackingStoreType)NSWindowStyleMaskTitled);
+ {
+ void* nsclass = objc_getClass("NSWindow");
+ SEL func = sel_registerName("initWithContentRect:styleMask:backing:defer:");
+
+ win->src.window = ((id(*)(id, SEL, NSRect, NSWindowStyleMask, NSBackingStoreType, bool))objc_msgSend)
+ (NSAlloc(nsclass), func, windowRect, (NSWindowStyleMask)macArgs, macArgs, false);
+ }
+
+ id str = NSString_stringWithUTF8String(name);
+ objc_msgSend_void_id((id)win->src.window, sel_registerName("setTitle:"), str);
+
+ win->src.delegate = (void*)objc_msgSend_id(NSAlloc((Class)_RGFW->customWindowDelegateClass), sel_registerName("init"));
+ object_setInstanceVariable((id)win->src.delegate, "RGFW_window", win);
+
+ objc_msgSend_void_id((id)win->src.window, sel_registerName("setDelegate:"), (id)win->src.delegate);
+
+ if (flags & RGFW_windowAllowDND) {
+ win->internal.flags |= RGFW_windowAllowDND;
+
+ NSPasteboardType types[] = {NSPasteboardTypeURL, NSPasteboardTypeFileURL, NSPasteboardTypeString};
+ NSregisterForDraggedTypes((id)win->src.window, types, 3);
+ }
+
+ objc_msgSend_void_bool((id)win->src.window, sel_registerName("setAcceptsMouseMovedEvents:"), true);
+
+ if (flags & RGFW_windowTransparent) {
+ objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), false);
+
+ objc_msgSend_void_id((id)win->src.window, sel_registerName("setBackgroundColor:"),
+ NSColor_colorWithSRGB(0, 0, 0, 0));
+ }
+
+ /* Show the window */
+ objc_msgSend_void_bool((id)_RGFW->NSApp, sel_registerName("activateIgnoringOtherApps:"), true);
+
+ if (_RGFW->root == NULL) {
+ objc_msgSend_void(win->src.window, sel_registerName("makeMainWindow"));
+ }
+
+ objc_msgSend_void(win->src.window, sel_registerName("makeKeyWindow"));
+
+ NSRetain(win->src.window);
+
+ win->src.view = ((id(*)(id, SEL, RGFW_window*))objc_msgSend) (NSAlloc((Class)_RGFW->customViewClasses[0]), sel_registerName("initWithRGFWWindow:"), win);
+ return win;
+}
+
+void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) {
+ NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame"));
+ NSRect content = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame"));
+ double offset = 0;
+
+ RGFW_setBit(&win->internal.flags, RGFW_windowNoBorder, !border);
+ NSBackingStoreType storeType = (NSBackingStoreType)(NSWindowStyleMaskBorderless | NSWindowStyleMaskFullSizeContentView);
+ if (border)
+ storeType = (NSBackingStoreType)(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable);
+ if (!(win->internal.flags & RGFW_windowNoResize)) {
+ storeType = (NSBackingStoreType)(storeType | (NSBackingStoreType)NSWindowStyleMaskResizable);
+ }
+
+ ((void (*)(id, SEL, NSBackingStoreType))objc_msgSend)((id)win->src.window, sel_registerName("setStyleMask:"), storeType);
+
+ if (!border) {
+ id miniaturizeButton = objc_msgSend_int((id)win->src.window, sel_registerName("standardWindowButton:"), NSWindowMiniaturizeButton);
+ id titleBarView = objc_msgSend_id(miniaturizeButton, sel_registerName("superview"));
+ objc_msgSend_void_bool(titleBarView, sel_registerName("setHidden:"), true);
+
+ offset = (double)(frame.size.height - content.size.height);
+ }
+
+ RGFW_window_resize(win, win->w, win->h + (i32)offset);
+ win->h -= (i32)offset;
+}
+
+RGFW_bool RGFW_getGlobalMouse(i32* x, i32* y) {
+ RGFW_ASSERT(_RGFW->root != NULL);
+
+ CGEventRef e = CGEventCreate(NULL);
+ CGPoint point = CGEventGetLocation(e);
+ CFRelease(e);
+
+ if (x) *x = (i32)point.x;
+ if (y) *y = (i32)point.y;
+ return RGFW_TRUE;
+}
+
+void RGFW_stopCheckEvents(void) {
+ id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ eventPool = objc_msgSend_id(eventPool, sel_registerName("init"));
+
+ id e = (id) ((id(*)(Class, SEL, NSEventType, NSPoint, NSEventModifierFlags, void*, NSInteger, void**, short, NSInteger, NSInteger))objc_msgSend)
+ (objc_getClass("NSEvent"), sel_registerName("otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:"),
+ NSEventTypeApplicationDefined, (NSPoint){0, 0}, (NSEventModifierFlags)0, NULL, (NSInteger)0, NULL, 0, 0, 0);
+
+ ((void (*)(id, SEL, id, bool))objc_msgSend)
+ ((id)_RGFW->NSApp, sel_registerName("postEvent:atStart:"), e, 1);
+
+ objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
+}
+
+void RGFW_waitForEvent(i32 waitMS) {
+ id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ eventPool = objc_msgSend_id(eventPool, sel_registerName("init"));
+
+ void* date = (void*) ((id(*)(Class, SEL, double))objc_msgSend)
+ (objc_getClass("NSDate"), sel_registerName("dateWithTimeIntervalSinceNow:"), waitMS);
+
+ SEL eventFunc = sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:");
+ id e = (id) ((id(*)(id, SEL, NSEventMask, void*, id, bool))objc_msgSend)
+ ((id)_RGFW->NSApp, eventFunc,
+ ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true);
+
+ if (e) {
+ ((void (*)(id, SEL, id, bool))objc_msgSend)
+ ((id)_RGFW->NSApp, sel_registerName("postEvent:atStart:"), e, 1);
+ }
+
+ objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
+}
+
+RGFW_key RGFW_physicalToMappedKey(RGFW_key key) {
+ u16 keycode = (u16)RGFW_rgfwToApiKey(key);
+ TISInputSourceRef source = TISCopyCurrentKeyboardLayoutInputSource();
+ if (source == NULL)
+ return key;
+
+ CFDataRef layoutData = TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutDataSrc);
+
+ if (layoutData == NULL) {
+ CFRelease(source);
+ return key;
+ }
+
+ UCKeyboardLayout *layout = (UCKeyboardLayout*)(void*)CFDataGetBytePtr(layoutData);
+
+ UInt32 deadKeyState = 0;
+ UniChar chars[4];
+ UniCharCount len = 0;
+ u32 type = LMGetKbdType();
+ OSStatus status = UCKeyTranslate(layout, keycode, kUCKeyActionDown, 0, type, kUCKeyTranslateNoDeadKeysBit, &deadKeyState, 4, &len, chars );
+
+ CFRelease(source);
+
+ if (status == noErr && len == 1 && chars[0] < 256) {
+ return (RGFW_key)chars[0];
+ }
+
+ return key;
+}
+
+RGFW_bool RGFW_window_fetchSize(RGFW_window* win, i32* w, i32* h) {
+ NSRect content = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame"));
+
+ win->w = (i32)content.size.width;
+ win->h = (i32)content.size.height;
+
+ return RGFW_window_getSize(win, w, h);
+}
+
+void RGFW_pollEvents(void) {
+ RGFW_resetPrevState();
+
+ id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ eventPool = objc_msgSend_id(eventPool, sel_registerName("init"));
+ SEL eventFunc = sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:");
+
+ while (1) {
+ void* date = NULL;
+ id e = (id) ((id(*)(id, SEL, NSEventMask, void*, id, bool))objc_msgSend)
+ ((id)_RGFW->NSApp, eventFunc, ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true);
+
+ if (e == NULL) {
+ break;
+ }
+
+ objc_msgSend_void_id((id)_RGFW->NSApp, sel_registerName("sendEvent:"), e);
+ }
+
+ objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
+}
+
+
+void RGFW_window_move(RGFW_window* win, i32 x, i32 y) {
+ RGFW_ASSERT(win != NULL);
+
+ NSRect content = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame"));
+
+ win->x = x;
+ win->y = (i32)RGFW_cocoaYTransform((float)y + (float)content.size.height - 1.0f);
+
+ ((void(*)(id,SEL,NSPoint))objc_msgSend)((id)win->src.window, sel_registerName("setFrameOrigin:"), (NSPoint){(double)x, (double)y});
+}
+
+void RGFW_window_resize(RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+
+ NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame"));
+ NSRect content = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame"));
+ float offset = (float)(frame.size.height - content.size.height);
+
+ win->w = w;
+ win->h = h;
+
+
+ ((void(*)(id, SEL, CGRect))objc_msgSend)((id)win->src.view, sel_registerName("setFrame:"), (NSRect){{0, 0}, {(double)win->w, (double)win->h}});
+ ((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend)
+ ((id)win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{(double)win->x, (double)win->y}, {(double)win->w, (double)win->h + (double)offset}}, true, true);
+}
+
+void RGFW_window_focus(RGFW_window* win) {
+ RGFW_ASSERT(win);
+ objc_msgSend_void_bool((id)_RGFW->NSApp, sel_registerName("activateIgnoringOtherApps:"), true);
+ ((void (*)(id, SEL))objc_msgSend)((id)win->src.window, sel_registerName("makeKeyWindow"));
+}
+
+void RGFW_window_raise(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("orderFront:"), (SEL)NULL);
+ objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGNormalWindowLevelKey);
+}
+
+void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) {
+ RGFW_ASSERT(win != NULL);
+ if (fullscreen && (win->internal.flags & RGFW_windowFullscreen)) return;
+ if (!fullscreen && !(win->internal.flags & RGFW_windowFullscreen)) return;
+
+ if (fullscreen) {
+ win->internal.oldX = win->x;
+ win->internal.oldY = win->y;
+ win->internal.oldW = win->w;
+ win->internal.oldH = win->h;
+
+ win->internal.flags |= RGFW_windowFullscreen;
+
+ RGFW_monitor* mon = RGFW_window_getMonitor(win);
+ RGFW_monitor_scaleToWindow(mon, win);
+
+ RGFW_window_setBorder(win, RGFW_FALSE);
+
+ if (mon != NULL) {
+ win->x = mon->x;
+ win->y = mon->y;
+ win->w = mon->mode.w;
+ win->h = mon->mode.h;
+ RGFW_window_resize(win, mon->mode.w, mon->mode.h);
+ RGFW_window_move(win, mon->x, mon->y);
+ }
+
+ ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("orderFront:"), (SEL)NULL);
+ objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), 25);
+ }
+
+ objc_msgSend_void_SEL(win->src.window, sel_registerName("toggleFullScreen:"), NULL);
+
+ if (!fullscreen) {
+ win->x = win->internal.oldX;
+ win->y = win->internal.oldY;
+ win->w = win->internal.oldW;
+ win->h = win->internal.oldH;
+ win->internal.flags &= ~(u32)RGFW_windowFullscreen;
+
+ RGFW_window_resize(win, win->w, win->h);
+ RGFW_window_move(win, win->x, win->y);
+ }
+}
+
+void RGFW_window_maximize(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ if (RGFW_window_isMaximized(win)) return;
+
+ win->internal.flags |= RGFW_windowMaximize;
+ objc_msgSend_void_SEL(win->src.window, sel_registerName("zoom:"), NULL);
+ RGFW_window_fetchSize(win, NULL, NULL);
+}
+
+void RGFW_window_minimize(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ objc_msgSend_void_SEL(win->src.window, sel_registerName("performMiniaturize:"), NULL);
+}
+
+void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) {
+ RGFW_ASSERT(win != NULL);
+ if (floating) objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGFloatingWindowLevelKey);
+ else objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGNormalWindowLevelKey);
+}
+
+void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) {
+ objc_msgSend_int(win->src.window, sel_registerName("setAlphaValue:"), opacity);
+ objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), (opacity < (u8)255));
+
+ if (opacity)
+ objc_msgSend_void_id((id)win->src.window, sel_registerName("setBackgroundColor:"), NSColor_colorWithSRGB(0, 0, 0, opacity));
+
+}
+
+void RGFW_window_restore(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+
+ if (RGFW_window_isMaximized(win))
+ objc_msgSend_void_SEL(win->src.window, sel_registerName("zoom:"), NULL);
+
+ objc_msgSend_void_SEL(win->src.window, sel_registerName("deminiaturize:"), NULL);
+ RGFW_window_show(win);
+}
+
+RGFW_bool RGFW_window_isFloating(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ int level = ((int (*)(id, SEL))objc_msgSend) ((id)(win->src.window), (SEL)sel_registerName("level"));
+ return level > kCGNormalWindowLevelKey;
+}
+
+void RGFW_window_setName(RGFW_window* win, const char* name) {
+ RGFW_ASSERT(win != NULL);
+ if (name == NULL) name = "\0";
+
+ id str = NSString_stringWithUTF8String(name);
+ objc_msgSend_void_id((id)win->src.window, sel_registerName("setTitle:"), str);
+}
+
+#ifndef RGFW_NO_PASSTHROUGH
+void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) {
+ objc_msgSend_void_bool(win->src.window, sel_registerName("setIgnoresMouseEvents:"), passthrough);
+}
+#endif
+
+void RGFW_window_setAspectRatio(RGFW_window* win, i32 w, i32 h) {
+ if (w == 0 && h == 0) { w = 1; h = 1; };
+
+ ((void (*)(id, SEL, NSSize))objc_msgSend)
+ ((id)win->src.window, sel_registerName("setContentAspectRatio:"), (NSSize){(CGFloat)w, (CGFloat)h});
+}
+
+void RGFW_window_setMinSize(RGFW_window* win, i32 w, i32 h) {
+ ((void (*)(id, SEL, NSSize))objc_msgSend) ((id)win->src.window, sel_registerName("setMinSize:"), (NSSize){(CGFloat)w, (CGFloat)h});
+}
+
+void RGFW_window_setMaxSize(RGFW_window* win, i32 w, i32 h) {
+ if (w == 0 && h == 0) {
+ RGFW_monitor* mon = RGFW_window_getMonitor(win);
+ if (mon != NULL) {
+ w = mon->mode.w;
+ h = mon->mode.h;
+ }
+ }
+
+ ((void (*)(id, SEL, NSSize))objc_msgSend)
+ ((id)win->src.window, sel_registerName("setMaxSize:"), (NSSize){(CGFloat)w, (CGFloat)h});
+}
+
+RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_icon type) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_UNUSED(type);
+
+ id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ pool = objc_msgSend_id(pool, sel_registerName("init"));
+
+ if (data == NULL) {
+ objc_msgSend_void_id((id)_RGFW->NSApp, sel_registerName("setApplicationIconImage:"), NULL);
+ objc_msgSend_bool_void(pool, sel_registerName("drain"));
+ return RGFW_TRUE;
+ }
+
+ id representation = NSBitmapImageRep_initWithBitmapData(NULL, w, h, 8, (NSInteger)4, true, false, "NSCalibratedRGBColorSpace", 1 << 1, w * 4, 32);
+ RGFW_copyImageData(NSBitmapImageRep_bitmapData(representation), w, h, RGFW_formatRGBA8, data, format, NULL);
+
+ id dock_image = ((id(*)(id, SEL, NSSize))objc_msgSend) (NSAlloc((id)objc_getClass("NSImage")), sel_registerName("initWithSize:"), ((NSSize){(CGFloat)w, (CGFloat)h}));
+
+ objc_msgSend_void_id(dock_image, sel_registerName("addRepresentation:"), representation);
+
+ objc_msgSend_void_id((id)_RGFW->NSApp, sel_registerName("setApplicationIconImage:"), dock_image);
+
+ NSRelease(dock_image);
+ NSRelease(representation);
+
+ objc_msgSend_bool_void(pool, sel_registerName("drain"));
+
+ return RGFW_TRUE;
+}
+
+id NSCursor_arrowStr(const char* str);
+id NSCursor_arrowStr(const char* str) {
+ void* nclass = objc_getClass("NSCursor");
+ SEL func = sel_registerName(str);
+ id mouse = (id) objc_msgSend_id(nclass, func);
+ NSRetain(mouse);
+ return mouse;
+}
+
+RGFW_mouse* RGFW_createMouseStandard(RGFW_mouseIcon mouse) {
+ switch (mouse) {
+ case RGFW_mouseNormal: return NSCursor_arrowStr("arrowCursor");
+ case RGFW_mouseArrow: return NSCursor_arrowStr("arrowCursor");
+ case RGFW_mouseIbeam: return NSCursor_arrowStr("IBeamCursor");
+ case RGFW_mouseCrosshair: return NSCursor_arrowStr("crosshairCursor");
+ case RGFW_mousePointingHand: return NSCursor_arrowStr("pointingHandCursor");
+ case RGFW_mouseResizeEW: return NSCursor_arrowStr("resizeLeftRightCursor");
+ case RGFW_mouseResizeE: return NSCursor_arrowStr("resizeLeftRightCursor");
+ case RGFW_mouseResizeW: return NSCursor_arrowStr("resizeLeftRightCursor");
+ case RGFW_mouseResizeNS: return NSCursor_arrowStr("resizeUpDownCursor");
+ case RGFW_mouseResizeN: return NSCursor_arrowStr("resizeUpDownCursor");
+ case RGFW_mouseResizeS: return NSCursor_arrowStr("resizeUpDownCursor");
+ case RGFW_mouseResizeNWSE: return NSCursor_arrowStr("_windowResizeNorthWestSouthEastCursor");
+ case RGFW_mouseResizeNW: return NSCursor_arrowStr("_windowResizeNorthWestSouthEastCursor");
+ case RGFW_mouseResizeSE: return NSCursor_arrowStr("_windowResizeNorthWestSouthEastCursor");
+ case RGFW_mouseResizeNESW: return NSCursor_arrowStr("_windowResizeNorthEastSouthWestCursor");
+ case RGFW_mouseResizeNE: return NSCursor_arrowStr("_windowResizeNorthEastSouthWestCursor");
+ case RGFW_mouseResizeSW: return NSCursor_arrowStr("_windowResizeNorthEastSouthWestCursor");
+ case RGFW_mouseResizeAll: return NSCursor_arrowStr("openHandCursor");
+ case RGFW_mouseNotAllowed: return NSCursor_arrowStr("operationNotAllowedCursor");
+ case RGFW_mouseWait: return NSCursor_arrowStr("arrowCursor");
+ case RGFW_mouseProgress: return NSCursor_arrowStr("arrowCursor");
+ default: return NULL;
+ }
+ return NULL;
+}
+
+RGFW_mouse* RGFW_createMouse(u8* data, i32 w, i32 h, RGFW_format format) {
+ id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ pool = objc_msgSend_id(pool, sel_registerName("init"));
+
+ if (data == NULL) {
+ objc_msgSend_void(NSCursor_arrowStr("arrowCursor"), sel_registerName("set"));
+
+ objc_msgSend_bool_void(pool, sel_registerName("drain"));
+ return NULL;
+ }
+
+ id representation = (id)NSBitmapImageRep_initWithBitmapData(NULL, w, h, 8, (NSInteger)4, true, false, "NSCalibratedRGBColorSpace", 1 << 1, w * 4, 32);
+ RGFW_copyImageData(NSBitmapImageRep_bitmapData(representation), w, h, RGFW_formatRGBA8, data, format, NULL);
+
+ id cursor_image = ((id(*)(id, SEL, NSSize))objc_msgSend) (NSAlloc((id)objc_getClass("NSImage")), sel_registerName("initWithSize:"), ((NSSize){(CGFloat)w, (CGFloat)h}));
+
+ objc_msgSend_void_id(cursor_image, sel_registerName("addRepresentation:"), representation);
+
+ id cursor = (id) ((id(*)(id, SEL, id, NSPoint))objc_msgSend)
+ (NSAlloc(objc_getClass("NSCursor")), sel_registerName("initWithImage:hotSpot:"), cursor_image, (NSPoint){0.0, 0.0});
+
+ NSRelease(cursor_image);
+ NSRelease(representation);
+
+ objc_msgSend_bool_void(pool, sel_registerName("drain"));
+
+ return (void*)cursor;
+}
+
+RGFW_bool RGFW_window_setMousePlatform(RGFW_window* win, RGFW_mouse* mouse) {
+ RGFW_ASSERT(win != NULL); RGFW_ASSERT(mouse);
+ CGDisplayShowCursor(kCGDirectMainDisplay);
+ objc_msgSend_void((id)mouse, sel_registerName("set"));
+ win->src.mouse = mouse;
+ return RGFW_TRUE;
+}
+
+void RGFW_freeMouse(RGFW_mouse* mouse) {
+ RGFW_ASSERT(mouse);
+ NSRelease((id)mouse);
+}
+
+void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) {
+ RGFW_window_showMouseFlags(win, show);
+ if (show) CGDisplayShowCursor(kCGDirectMainDisplay);
+ else CGDisplayHideCursor(kCGDirectMainDisplay);
+}
+
+void RGFW_window_setRawMouseModePlatform(RGFW_window* win, RGFW_bool state) {
+ RGFW_UNUSED(win); RGFW_UNUSED(state);
+}
+
+void RGFW_window_captureMousePlatform(RGFW_window* win, RGFW_bool state) {
+ RGFW_UNUSED(win);
+ CGAssociateMouseAndMouseCursorPosition(!(state == RGFW_TRUE));
+}
+
+void RGFW_window_moveMouse(RGFW_window* win, i32 x, i32 y) {
+ RGFW_UNUSED(win);
+
+ win->internal.lastMouseX = x - win->x;
+ win->internal.lastMouseY = y - win->y;
+ CGWarpMouseCursorPosition((CGPoint){(CGFloat)x, (CGFloat)y});
+}
+
+
+void RGFW_window_hide(RGFW_window* win) {
+ objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), false);
+}
+
+void RGFW_window_show(RGFW_window* win) {
+ if (win->internal.flags & RGFW_windowFocusOnShow)
+ ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL);
+
+ ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("orderFront:"), NULL);
+ objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true);
+}
+
+void RGFW_window_flash(RGFW_window* win, RGFW_flashRequest request) {
+ if (RGFW_window_isInFocus(win) && request) {
+ return;
+ }
+
+ id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ pool = objc_msgSend_id(pool, sel_registerName("init"));
+
+ if (_RGFW->flash) {
+ ((void (*)(id, SEL, NSInteger))objc_msgSend) ((id)_RGFW->NSApp, sel_registerName("cancelUserAttentionRequest:"), _RGFW->flash);
+ }
+
+ switch (request) {
+ case RGFW_flashBriefly:
+ _RGFW->flash = ((NSInteger (*)(id, SEL, NSInteger))objc_msgSend) ((id)_RGFW->NSApp, sel_registerName("requestUserAttention:"), NSInformationalRequest);
+ break;
+ case RGFW_flashUntilFocused:
+ _RGFW->flash = ((NSInteger (*)(id, SEL, NSInteger))objc_msgSend) ((id)_RGFW->NSApp, sel_registerName("requestUserAttention:"), NSCriticalRequest);
+ break;
+ default: break;
+ }
+
+ objc_msgSend_bool_void(pool, sel_registerName("drain"));
+}
+
+RGFW_bool RGFW_window_isHidden(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+
+ bool visible = objc_msgSend_bool(win->src.window, sel_registerName("isVisible"));
+ return visible == NO && !RGFW_window_isMinimized(win);
+}
+
+RGFW_bool RGFW_window_isMinimized(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+
+ return objc_msgSend_bool(win->src.window, sel_registerName("isMiniaturized")) == YES;
+}
+
+RGFW_bool RGFW_window_isMaximized(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_bool b = (RGFW_bool)objc_msgSend_bool(win->src.window, sel_registerName("isZoomed"));
+ return b;
+}
+
+RGFWDEF id RGFW_getNSScreenForDisplayUInt(u32 uintNum);
+id RGFW_getNSScreenForDisplayUInt(u32 uintNum) {
+ Class NSScreenClass = objc_getClass("NSScreen");
+
+ id screens = objc_msgSend_id(NSScreenClass, sel_registerName("screens"));
+
+ NSUInteger count = (NSUInteger)objc_msgSend_uint(screens, sel_registerName("count"));
+ NSUInteger i;
+ for (i = 0; i < count; i++) {
+ id screen = ((id (*)(id, SEL, int))objc_msgSend) (screens, sel_registerName("objectAtIndex:"), (int)i);
+ id description = objc_msgSend_id(screen, sel_registerName("deviceDescription"));
+ id screenNumberKey = NSString_stringWithUTF8String("NSScreenNumber");
+ id screenNumber = objc_msgSend_id_id(description, sel_registerName("objectForKey:"), screenNumberKey);
+
+ if (CGDisplayUnitNumber((CGDirectDisplayID)objc_msgSend_uint(screenNumber, sel_registerName("unsignedIntValue"))) == uintNum) {
+ return screen;
+ }
+ }
+
+ return NULL;
+}
+
+float RGFW_osx_getRefreshRate(CGDirectDisplayID display, CGDisplayModeRef mode);
+float RGFW_osx_getRefreshRate(CGDirectDisplayID display, CGDisplayModeRef mode) {
+ if (mode) {
+ float refreshRate = (float)CGDisplayModeGetRefreshRate(mode);
+ if (refreshRate != 0) return refreshRate;
+ }
+
+#ifndef RGFW_NO_IOKIT
+ float res = RGFW_osx_getFallbackRefreshRate(display);
+ if (res != 0) return res;
+#else
+ RGFW_UNUSED(display);
+#endif
+ return 60;
+}
+
+void RGFW_pollMonitors(void) {
+ u32 count;
+
+ if (CGGetActiveDisplayList(0, NULL, &count) != kCGErrorSuccess) {
+ return;
+ }
+
+ CGDirectDisplayID* displays = (CGDirectDisplayID*)RGFW_ALLOC(sizeof(CGDirectDisplayID) * count);
+ if (CGGetActiveDisplayList(count, displays, &count) != kCGErrorSuccess) {
+ return;
+ }
+
+
+ for (RGFW_monitorNode* node = _RGFW->monitors.list.head; node; node = node->next) {
+ node->disconnected = RGFW_TRUE;
+ }
+
+ CGDirectDisplayID primary = CGMainDisplayID();
+
+ u32 i;
+ for (i = 0; i < count; i++) {
+ RGFW_monitor monitor;
+
+ u32 uintNum = CGDisplayUnitNumber(displays[i]);
+ id screen = RGFW_getNSScreenForDisplayUInt(uintNum);
+
+ RGFW_monitorNode* node;
+ for (node = _RGFW->monitors.list.head; node; node = node->next) {
+ if (node->uintNum == uintNum) break;
+ }
+
+ if (node) {
+ node->screen = (void*)screen;
+ node->display = displays[i];
+ node->disconnected = RGFW_FALSE;
+ if (displays[i] == primary) {
+ _RGFW->monitors.primary = node;
+ }
+ continue;
+ }
+
+ const char name[] = "MacOS\0";
+ RGFW_MEMCPY(monitor.name, name, 6);
+
+ CGRect bounds = CGDisplayBounds(displays[i]);
+ monitor.x = (i32)bounds.origin.x;
+ monitor.y = (i32)RGFW_cocoaYTransform((float)(bounds.origin.y + bounds.size.height - 1));
+
+ CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displays[i]);
+ monitor.mode.w = (i32)CGDisplayModeGetWidth(mode);
+ monitor.mode.h = (i32)CGDisplayModeGetHeight(mode);
+ monitor.mode.src = (void*)mode;
+ monitor.mode.red = 8; monitor.mode.green = 8; monitor.mode.blue = 8;
+
+ monitor.mode.refreshRate = RGFW_osx_getRefreshRate(displays[i], mode);
+ CFRelease(mode);
+
+ CGSize screenSizeMM = CGDisplayScreenSize(displays[i]);
+ monitor.physW = (float)screenSizeMM.width / 25.4f;
+ monitor.physH = (float)screenSizeMM.height / 25.4f;
+
+ float ppi_width = ((float)monitor.mode.w / monitor.physW);
+ float ppi_height = ((float)monitor.mode.h / monitor.physH);
+
+ monitor.pixelRatio = (float)((CGFloat (*)(id, SEL))abi_objc_msgSend_fpret) (screen, sel_registerName("backingScaleFactor"));
+ float dpi = 96.0f * monitor.pixelRatio;
+
+ monitor.scaleX = ((((float) (ppi_width) / dpi) * 10.0f)) / 10.0f;
+ monitor.scaleY = ((((float) (ppi_height) / dpi) * 10.0f)) / 10.0f;
+
+ node = RGFW_monitors_add(&monitor);
+
+ node->screen = (void*)screen;
+ node->uintNum = uintNum;
+ node->display = displays[i];
+
+ if (displays[i] == primary) {
+ _RGFW->monitors.primary = node;
+ }
+
+ RGFW_monitorCallback(_RGFW->root, &node->mon, RGFW_TRUE);
+ }
+
+ RGFW_FREE(displays);
+
+ RGFW_monitors_refresh();
+}
+
+RGFW_bool RGFW_monitor_getWorkarea(RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height) {
+ NSRect frameRect = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)monitor->node->screen, sel_registerName("visibleFrame"));
+
+ if (x) *x = (i32)frameRect.origin.x;
+ if (y) *y = (i32)RGFW_cocoaYTransform((float)(frameRect.origin.y + frameRect.size.height - (double)1.0f));
+ if (width) *width = (i32)frameRect.size.width;
+ if (height) *height = (i32)frameRect.size.height;
+
+ return RGFW_TRUE;
+}
+
+size_t RGFW_monitor_getGammaRampPtr(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) {
+ id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ pool = objc_msgSend_id(pool, sel_registerName("init"));
+
+ u32 size = CGDisplayGammaTableCapacity(monitor->node->display);
+ CGGammaValue* values = (CGGammaValue*)RGFW_ALLOC(size * 3 * sizeof(CGGammaValue));
+
+ CGGetDisplayTransferByTable(monitor->node->display, size, values, values + size, values + size * 2, &size);
+
+ for (u32 i = 0; ramp && i < size; i++) {
+ ramp->red[i] = (u16) (values[i] * 65535);
+ ramp->green[i] = (u16) (values[i + size] * 65535);
+ ramp->blue[i] = (u16) (values[i + size * 2] * 65535);
+ }
+
+ RGFW_FREE(values);
+
+ objc_msgSend_bool_void(pool, sel_registerName("drain"));
+ return size;
+}
+
+RGFW_bool RGFW_monitor_setGammaRamp(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) {
+ id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ pool = objc_msgSend_id(pool, sel_registerName("init"));
+
+ CGGammaValue* values = (CGGammaValue*)RGFW_ALLOC(ramp->count * 3 * sizeof(CGGammaValue));
+
+ for (u32 i = 0; i < ramp->count; i++) {
+ values[i] = ramp->red[i] / 65535.f;
+ values[i + ramp->count] = ramp->green[i] / 65535.f;
+ values[i + ramp->count * 2] = ramp->blue[i] / 65535.f;
+ }
+
+ CGSetDisplayTransferByTable(monitor->node->display, (u32)ramp->count, values, values + ramp->count, values + ramp->count * 2);
+
+ RGFW_FREE(values);
+
+ objc_msgSend_bool_void(pool, sel_registerName("drain"));
+
+ return RGFW_TRUE;
+}
+
+size_t RGFW_monitor_getModesPtr(RGFW_monitor* mon, RGFW_monitorMode** modes) {
+ CGDirectDisplayID display = mon->node->display;
+ CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, NULL);
+
+ if (allModes == NULL) {
+ return RGFW_FALSE;
+ }
+
+ size_t count = (size_t)CFArrayGetCount(allModes);
+
+ CFIndex i;
+ for (i = 0; i < (CFIndex)count && modes; i++) {
+ CGDisplayModeRef cmode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i);
+
+ RGFW_monitorMode foundMode;
+ foundMode.w = (i32)CGDisplayModeGetWidth(cmode);
+ foundMode.h = (i32)CGDisplayModeGetHeight(cmode);
+ foundMode.refreshRate = RGFW_osx_getRefreshRate(display, cmode);
+ foundMode.red = 8; foundMode.green = 8; foundMode.blue = 8;
+ foundMode.src = (void*)cmode;
+ (*modes)[i] = foundMode;
+ }
+
+ CFRelease(allModes);
+ return count;
+}
+
+RGFW_bool RGFW_monitor_setMode(RGFW_monitor* mon, RGFW_monitorMode* mode) {
+ if (CGDisplaySetDisplayMode(mon->node->display, (CGDisplayModeRef)mode->src, NULL) == kCGErrorSuccess) {
+ return RGFW_TRUE;
+ }
+
+ return RGFW_FALSE;
+}
+
+RGFW_bool RGFW_monitor_requestMode(RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request) {
+ CGDirectDisplayID display = mon->node->display;
+ CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, NULL);
+
+ if (allModes == NULL) {
+ return RGFW_FALSE;
+ }
+
+ CGDisplayModeRef native = NULL;
+
+ CFIndex i;
+ for (i = 0; i < CFArrayGetCount(allModes); i++) {
+ CGDisplayModeRef cmode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i);
+
+ RGFW_monitorMode foundMode;
+ foundMode.w = (i32)CGDisplayModeGetWidth(cmode);
+ foundMode.h = (i32)CGDisplayModeGetHeight(cmode);
+ foundMode.refreshRate = RGFW_osx_getRefreshRate(display, cmode);
+ foundMode.red = 8; foundMode.green = 8; foundMode.blue = 8;
+ foundMode.src = (void*)cmode;
+
+ if (RGFW_monitorModeCompare(mode, &foundMode, request)) {
+ native = cmode;
+ mon->mode = foundMode;
+ break;
+ }
+ }
+
+ CFRelease(allModes);
+
+ if (native) {
+ if (CGDisplaySetDisplayMode(display, native, NULL) == kCGErrorSuccess) {
+ return RGFW_TRUE;
+ }
+ }
+
+ return RGFW_FALSE;
+}
+
+RGFW_monitor* RGFW_window_getMonitor(RGFW_window* win) {
+ id screen = objc_msgSend_id(win->src.window, sel_registerName("screen"));
+ id description = objc_msgSend_id(screen, sel_registerName("deviceDescription"));
+ id screenNumberKey = NSString_stringWithUTF8String("NSScreenNumber");
+ id screenNumber = objc_msgSend_id_id(description, sel_registerName("objectForKey:"), screenNumberKey);
+
+ CGDirectDisplayID display = (CGDirectDisplayID)objc_msgSend_uint(screenNumber, sel_registerName("unsignedIntValue"));
+
+ RGFW_monitorNode* node = _RGFW->monitors.list.head;
+ for (node = _RGFW->monitors.list.head; node; node = node->next) {
+ if (node->display == display && (id)node->screen == screen) {
+ break;
+ }
+ }
+
+ if (node == NULL) {
+ node = _RGFW->monitors.primary ? _RGFW->monitors.primary : _RGFW->monitors.list.head;
+ }
+
+ if (node == NULL) return NULL;
+ return &node->mon;
+}
+
+RGFW_bool RGFW_readClipboardPtr(u8* buffer, size_t capacity, RGFW_dataTransfer* data) {
+ RGFW_ASSERT(data != NULL);
+
+ size_t length = 0;
+ char* clip = (char*)NSPasteboard_stringForType(NSPasteboard_generalPasteboard(), NSPasteboardTypeString, &length);
+ if (clip == NULL) return RGFW_FALSE;
+
+ data->type = RGFW_dataText;
+ data->length = length;
+
+ if (clip[data->length - 1] != '\0') data->length += 1;
+
+ if (buffer == NULL) return RGFW_TRUE;
+ if (capacity < data->length) return RGFW_FALSE;
+
+ RGFW_MEMCPY(buffer, clip, length);
+ buffer[data->length - 1] = '\0';
+ data->data = (const char*)buffer;
+
+ return RGFW_TRUE;
+}
+
+RGFW_bool RGFW_writeClipboard(const RGFW_dataTransfer* data) {
+ RGFW_ASSERT(data != NULL);
+
+ NSPasteboardType array[] = { NSPasteboardTypeString, NULL };
+ NSPasteBoard_declareTypes(NSPasteboard_generalPasteboard(), array, 1, NULL);
+
+ SEL func = sel_registerName("setString:forType:");
+ bool ret = ((bool (*)(id, SEL, id, id))objc_msgSend)
+ (NSPasteboard_generalPasteboard(), func, NSString_stringWithUTF8String(data->data), NSString_stringWithUTF8String((const char*)NSPasteboardTypeString));
+
+ return (ret == true) ? RGFW_TRUE : RGFW_FALSE;
+}
+
+#ifdef RGFW_OPENGL
+void NSOpenGLContext_setValues(id context, const int* vals, NSOpenGLContextParameter param);
+void NSOpenGLContext_setValues(id context, const int* vals, NSOpenGLContextParameter param) {
+ ((void (*)(id, SEL, const int*, NSOpenGLContextParameter))objc_msgSend)
+ (context, sel_registerName("setValues:forParameter:"), vals, param);
+}
+
+
+/* MacOS OpenGL API spares us yet again (there are no extensions) */
+RGFW_bool RGFW_extensionSupportedPlatform_OpenGL(const char * extension, size_t len) { RGFW_UNUSED(extension); RGFW_UNUSED(len); return RGFW_FALSE; }
+
+RGFW_proc RGFW_getProcAddress_OpenGL(const char* procname) {
+ static CFBundleRef RGFWnsglFramework = NULL;
+ if (RGFWnsglFramework == NULL)
+ RGFWnsglFramework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
+
+ CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, procname, kCFStringEncodingASCII);
+
+ RGFW_proc symbol = (RGFW_proc)CFBundleGetFunctionPointerForName(RGFWnsglFramework, symbolName);
+
+ CFRelease(symbolName);
+
+ return symbol;
+}
+
+RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) {
+ win->src.ctx.native = ctx;
+ win->src.gfxType = RGFW_gfxNativeOpenGL;
+
+ i32 attribs[40];
+ size_t render_type_index = 0;
+ {
+ RGFW_attribStack stack;
+ RGFW_attribStack_init(&stack, attribs, 40);
+
+ i32 colorBits = (i32)(hints->red + hints->green + hints->blue + hints->alpha) / 4;
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAColorSize, colorBits);
+
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAAlphaSize, hints->alpha);
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFADepthSize, hints->depth);
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAStencilSize, hints->stencil);
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAAuxBuffers, hints->auxBuffers);
+ RGFW_attribStack_pushAttrib(&stack, NSOpenGLPFAClosestPolicy);
+ if (hints->samples) {
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFASampleBuffers, 1);
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFASamples, hints->samples);
+ } else RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFASampleBuffers, 0);
+
+ if (hints->doubleBuffer)
+ RGFW_attribStack_pushAttrib(&stack, NSOpenGLPFADoubleBuffer);
+
+ #ifdef RGFW_COCOA_GRAPHICS_SWITCHING
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAAllowOfflineRenderers, kCGLPFASupportsAutomaticGraphicsSwitching);
+ #endif
+ #if MAC_OS_X_VERSION_MAX_ALLOWED < 101200
+ if (hints->stereo) RGFW_attribStack_pushAttrib(&stack, NSOpenGLPFAStereo);
+ #endif
+
+ /* macOS has the surface attribs and the OpenGL attribs connected for some reason maybe this is to give macOS more control to limit openGL/the OpenGL version? */
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAOpenGLProfile,
+ (hints->major >= 4) ? NSOpenGLProfileVersion4_1Core : (hints->major >= 3) ?
+ NSOpenGLProfileVersion3_2Core : NSOpenGLProfileVersionLegacy);
+
+ if (hints->major <= 2) {
+ i32 accumSize = (i32)(hints->accumRed + hints->accumGreen + hints->accumBlue + hints->accumAlpha) / 4;
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAAccumSize, accumSize);
+ }
+
+ if (hints->renderer == RGFW_glSoftware) {
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFARendererID, kCGLRendererGenericFloatID);
+ } else {
+ RGFW_attribStack_pushAttrib(&stack, NSOpenGLPFAAccelerated);
+ }
+ render_type_index = stack.count - 1;
+
+ RGFW_attribStack_pushAttribs(&stack, 0, 0);
+ }
+
+ void* format = (void*) ((id(*)(id, SEL, const u32*))objc_msgSend) (NSAlloc((id)objc_getClass("NSOpenGLPixelFormat")), sel_registerName("initWithAttributes:"), (u32*)attribs);
+ if (format == NULL) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errOpenGLContext, "Failed to load pixel format for OpenGL");
+
+ assert(render_type_index + 3 < (sizeof(attribs) / sizeof(attribs[0])));
+ attribs[render_type_index] = NSOpenGLPFARendererID;
+ attribs[render_type_index + 1] = kCGLRendererGenericFloatID;
+ attribs[render_type_index + 3] = 0;
+
+ format = (void*) ((id(*)(id, SEL, const u32*))objc_msgSend) (NSAlloc((id)objc_getClass("NSOpenGLPixelFormat")), sel_registerName("initWithAttributes:"), (u32*)attribs);
+ if (format == NULL)
+ RGFW_debugCallback(RGFW_typeError, RGFW_errOpenGLContext, "and loading software rendering OpenGL failed");
+ else
+ RGFW_debugCallback(RGFW_typeWarning, RGFW_warningOpenGL, "Switching to software rendering");
+ }
+
+ /* the pixel format can be passed directly to OpenGL context creation to create a context
+ this is because the format also includes information about the OpenGL version (which may be a bad thing) */
+
+ if (win->src.view)
+ NSRelease(win->src.view);
+ win->src.view = (id) ((id(*)(id, SEL, NSRect, u32*))objc_msgSend) (NSAlloc(_RGFW->customViewClasses[1]),
+ sel_registerName("initWithFrame:pixelFormat:"), (NSRect){{0, 0}, {(double)win->w, (double)win->h}}, (u32*)format);
+
+ id share = NULL;
+ if (hints->share) {
+ share = (id)hints->share->ctx;
+ }
+
+ win->src.ctx.native->ctx = ((id (*)(id, SEL, id, id))objc_msgSend)(NSAlloc(objc_getClass("NSOpenGLContext")),
+ sel_registerName("initWithFormat:shareContext:"),
+ (id)format, share);
+
+ win->src.ctx.native->format = format;
+
+ objc_msgSend_void_id(win->src.view, sel_registerName("setOpenGLContext:"), win->src.ctx.native->ctx);
+ if (win->internal.flags & RGFW_windowTransparent) {
+ i32 opacity = 0;
+ #define NSOpenGLCPSurfaceOpacity 236
+ NSOpenGLContext_setValues((id)win->src.ctx.native->ctx, &opacity, (NSOpenGLContextParameter)NSOpenGLCPSurfaceOpacity);
+
+ }
+
+ objc_msgSend_void(win->src.ctx.native->ctx, sel_registerName("makeCurrentContext"));
+
+ objc_msgSend_void_id((id)win->src.window, sel_registerName("setContentView:"), win->src.view);
+ objc_msgSend_void_bool(win->src.view, sel_registerName("setWantsLayer:"), true);
+ objc_msgSend_int((id)win->src.view, sel_registerName("setLayerContentsPlacement:"), 4);
+
+ RGFW_window_swapInterval_OpenGL(win, 0);
+
+ RGFW_debugCallback(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context initalized.");
+ return RGFW_TRUE;
+}
+
+void RGFW_window_deleteContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx) {
+ objc_msgSend_void(ctx->format, sel_registerName("release"));
+ win->src.ctx.native->format = NULL;
+
+ objc_msgSend_void(ctx->ctx, sel_registerName("release"));
+ win->src.ctx.native->ctx = NULL;
+ RGFW_debugCallback(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context freed.");
+}
+
+void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win) {
+ if (win) RGFW_ASSERT(win->src.ctx.native);
+ if (win != NULL)
+ objc_msgSend_void(win->src.ctx.native->ctx, sel_registerName("makeCurrentContext"));
+ else
+ objc_msgSend_id(objc_getClass("NSOpenGLContext"), sel_registerName("clearCurrentContext"));
+}
+void* RGFW_getCurrentContext_OpenGL(void) {
+ return objc_msgSend_id(objc_getClass("NSOpenGLContext"), sel_registerName("currentContext"));
+}
+
+void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) {
+ RGFW_ASSERT(win && win->src.ctx.native);
+ objc_msgSend_void(win->src.ctx.native->ctx, sel_registerName("flushBuffer"));
+}
+void RGFW_window_swapInterval_OpenGL(RGFW_window* win, i32 swapInterval) {
+ RGFW_ASSERT(win != NULL && win->src.ctx.native != NULL);
+ NSOpenGLContext_setValues((id)win->src.ctx.native->ctx, &swapInterval, (NSOpenGLContextParameter)222);
+}
+#endif
+
+void RGFW_deinitPlatform(void) {
+ objc_msgSend_void_id(_RGFW->NSApp, sel_registerName("setDelegate:"), NULL);
+
+ objc_msgSend_void_id(_RGFW->NSApp, sel_registerName("stop:"), NULL);
+ NSRelease(_RGFW->NSApp);
+ _RGFW->NSApp = NULL;
+
+ NSRelease(_RGFW->customNSAppDelegate);
+
+ _RGFW->customNSAppDelegate = NULL;
+
+ objc_disposeClassPair((Class)_RGFW->customViewClasses[0]);
+ objc_disposeClassPair((Class)_RGFW->customViewClasses[1]);
+ objc_disposeClassPair((Class)_RGFW->customWindowDelegateClass);
+ objc_disposeClassPair((Class)_RGFW->customNSAppDelegateClass);
+}
+
+void RGFW_window_closePlatform(RGFW_window* win) {
+ objc_msgSend_void_id((id)win->src.window, sel_registerName("setDelegate:"), NULL);
+ NSRelease((id)win->src.delegate);
+ NSRelease(win->src.view);
+
+ objc_msgSend_id(win->src.window, sel_registerName("close"));
+ NSRelease(win->src.window);
+}
+
+#ifdef RGFW_VULKAN
+VkResult RGFW_window_createSurface_Vulkan(RGFW_window* win, VkInstance instance, VkSurfaceKHR* surface) {
+ RGFW_ASSERT(win != NULL); RGFW_ASSERT(instance);
+ RGFW_ASSERT(surface != NULL);
+
+ *surface = VK_NULL_HANDLE;
+ id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ pool = objc_msgSend_id(pool, sel_registerName("init"));
+
+ id nsView = (id)win->src.view;
+ if (!nsView) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errMetal, "NSView is NULL for macOS window");
+ return -1;
+ }
+
+
+ id layer = ((id (*)(id, SEL))objc_msgSend)(nsView, sel_registerName("layer"));
+
+ void* metalLayer = RGFW_getLayer_OSX();
+ if (metalLayer == NULL) {
+ return -1;
+ }
+ ((void (*)(id, SEL, id))objc_msgSend)((id)nsView, sel_registerName("setLayer:"), (id)metalLayer);
+ ((void (*)(id, SEL, BOOL))objc_msgSend)(nsView, sel_registerName("setWantsLayer:"), YES);
+
+ VkResult result;
+/*
+ VkMetalSurfaceCreateInfoEXT macos;
+ macos.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
+ macos.slayer = metalLayer;
+ RGFW_MEMZERO(&macos, sizeof(macos));
+ result = vkCreateMacOSSurfaceMVK(instance, &macos, NULL, surface);
+*/
+
+ VkMacOSSurfaceCreateInfoMVK macos;
+ RGFW_MEMZERO(&macos, sizeof(macos));
+ macos.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
+ macos.pView = nsView;
+
+ result = vkCreateMacOSSurfaceMVK(instance, &macos, NULL, surface);
+
+ objc_msgSend_bool_void(pool, sel_registerName("drain"));
+
+ return result;
+}
+#endif
+
+#ifdef RGFW_WEBGPU
+WGPUSurface RGFW_window_createSurface_WebGPU(RGFW_window* window, WGPUInstance instance) {
+ WGPUSurfaceDescriptor surfaceDesc = {0};
+ id nsView = (id)window->src.view;
+ if (!nsView) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errMetal, "NSView is NULL for macOS window");
+ return NULL;
+ }
+
+ ((void (*)(id, SEL, BOOL))objc_msgSend)(nsView, sel_registerName("setWantsLayer:"), YES);
+ id layer = ((id (*)(id, SEL))objc_msgSend)(nsView, sel_registerName("layer"));
+
+ void* metalLayer = RGFW_getLayer_OSX();
+ if (metalLayer == NULL) {
+ return NULL;
+ }
+ ((void (*)(id, SEL, id))objc_msgSend)((id)nsView, sel_registerName("setLayer:"), (id)metalLayer);
+ layer = (id)metalLayer;
+
+ WGPUSurfaceSourceMetalLayer fromMetal = {0};
+ fromMetal.chain.sType = WGPUSType_SurfaceSourceMetalLayer;
+#ifdef __OBJC__
+ fromMetal.layer = (__bridge CAMetalLayer*)layer; /* Use __bridge for ARC compatibility if mixing C/Obj-C */
+#else
+ fromMetal.layer = layer;
+#endif
+
+ surfaceDesc.nextInChain = (WGPUChainedStruct*)&fromMetal.chain;
+ return wgpuInstanceCreateSurface(instance, &surfaceDesc);
+}
+#endif
+
+#endif /* RGFW_MACOS */
+
+/*
+ End of MaOS defines
+*/
+
+/*
+ WASM defines
+*/
+
+#ifdef RGFW_WASM
+EM_BOOL Emscripten_on_resize(int eventType, const EmscriptenUiEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+ RGFW_windowResizedCallback(_RGFW->root, E->windowInnerWidth, E->windowInnerHeight);
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_fullscreenchange(int eventType, const EmscriptenFullscreenChangeEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+ if (!(_RGFW->root->internal.enabledEvents & RGFW_windowResizedFlag)) return EM_TRUE;
+
+ static u8 fullscreen = RGFW_FALSE;
+ static i32 originalW, originalH;
+
+ if (fullscreen == RGFW_FALSE) {
+ originalW = _RGFW->root->w;
+ originalH = _RGFW->root->h;
+ }
+
+ fullscreen = !fullscreen;
+ _RGFW->root->w = E->screenWidth;
+ _RGFW->root->h = E->screenHeight;
+
+ EM_ASM("Module.canvas.focus();");
+
+ if (fullscreen == RGFW_FALSE) {
+ _RGFW->root->w = originalW;
+ _RGFW->root->h = originalH;
+ } else {
+ #if __EMSCRIPTEN_major__ >= 1 && __EMSCRIPTEN_minor__ >= 29 && __EMSCRIPTEN_tiny__ >= 0
+ EmscriptenFullscreenStrategy FSStrat = {0};
+ FSStrat.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH;
+ FSStrat.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF;
+ FSStrat.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
+ emscripten_request_fullscreen_strategy("#canvas", 1, &FSStrat);
+ #else
+ emscripten_request_fullscreen("#canvas", 1);
+ #endif
+ }
+
+ emscripten_set_canvas_element_size("#canvas", _RGFW->root->w, _RGFW->root->h);
+ RGFW_windowResizedCallback(_RGFW->root, _RGFW->root->w, _RGFW->root->h);
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_focusin(int eventType, const EmscriptenFocusEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(E);
+
+ RGFW_windowFocusCallback(_RGFW->root, 1);
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_focusout(int eventType, const EmscriptenFocusEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(E);
+
+ RGFW_windowFocusCallback(_RGFW->root, 0);
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_mousemove(int eventType, const EmscriptenMouseEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+ RGFW_mouseMotionCallback(_RGFW->root, E->targetX, E->targetY);
+ RGFW_rawMotionCallback(_RGFW->root, E->movementX, E->movementY);
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_mousedown(int eventType, const EmscriptenMouseEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+ int button = E->button;
+ if (button > 2)
+ button += 2;
+
+ RGFW_mouseButtonCallback(_RGFW->root, button, 1);
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_mouseup(int eventType, const EmscriptenMouseEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+ int button = E->button;
+ if (button > 2)
+ button += 2;
+
+ RGFW_mouseButtonCallback(_RGFW->root, button, 0);
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_wheel(int eventType, const EmscriptenWheelEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+ RGFW_mouseScrollCallback(_RGFW->root, E->deltaX, E->deltaY);
+
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_touchstart(int eventType, const EmscriptenTouchEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+ if (!(_RGFW->root->internal.enabledEvents & RGFW_mouseButtonPressedFlag)) return EM_TRUE;
+
+ size_t i;
+ for (i = 0; i < (size_t)E->numTouches; i++) {
+ RGFW_mouseMotionCallback(_RGFW->root, E->touches[i].targetX, E->touches[i].targetY);
+ RGFW_rawMotionCallback(_RGFW->root, 0, 0);
+ RGFW_mouseButtonCallback(_RGFW->root, RGFW_mouseLeft, 1);
+ }
+
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_touchmove(int eventType, const EmscriptenTouchEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+ if (!(_RGFW->root->internal.enabledEvents & RGFW_mouseMotionFlag)) return EM_TRUE;
+
+ size_t i;
+ for (i = 0; i < (size_t)E->numTouches; i++) {
+ RGFW_mouseMotionCallback(_RGFW->root, E->touches[i].targetX, E->touches[i].targetY);
+ RGFW_rawMotionCallback(_RGFW->root, 0, 0);
+ }
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_touchend(int eventType, const EmscriptenTouchEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+ if (!(_RGFW->root->internal.enabledEvents & RGFW_mouseButtonReleasedFlag)) return EM_TRUE;
+
+ size_t i;
+ for (i = 0; i < (size_t)E->numTouches; i++) {
+ RGFW_mouseMotionCallback(_RGFW->root, E->touches[i].targetX, E->touches[i].targetY);
+ RGFW_rawMotionCallback(_RGFW->root, 0, 0);
+ RGFW_mouseButtonCallback(_RGFW->root, RGFW_mouseLeft, 0);
+ }
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_touchcancel(int eventType, const EmscriptenTouchEvent* E, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); return EM_TRUE; }
+
+RGFW_key RGFW_WASMPhysicalToRGFW(u32 hash);
+
+void EMSCRIPTEN_KEEPALIVE RGFW_handleKeyEvent(char* code, u32 codepoint, RGFW_bool press) {
+ const char* iCode = code;
+
+ u32 hash = 0;
+ while(*iCode) hash = ((hash ^ 0x7E057D79U) << 3) ^ (unsigned int)*iCode++;
+
+ u32 physicalKey = RGFW_WASMPhysicalToRGFW(hash);
+
+ RGFW_keyCallback(_RGFW->root, physicalKey, _RGFW->root->internal.mod, RGFW_isKeyDown((u8)physicalKey) && press, press);
+ if (press) {
+; RGFW_keyCharCallback(_RGFW->root, codepoint);
+ }
+}
+
+void EMSCRIPTEN_KEEPALIVE RGFW_handleKeyMods(RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll) {
+ RGFW_keyUpdateKeyModsEx(_RGFW->root, capital, numlock, control, alt, shift, super, scroll);
+}
+
+void EMSCRIPTEN_KEEPALIVE Emscripten_onDrop(char* file, size_t size) {
+ RGFW_dataDropCallback(_RGFW->root, file, size, RGFW_dataFile);
+}
+
+void EMSCRIPTEN_KEEPALIVE RGFW_webFree(void* ptr) { free(ptr); }
+
+void RGFW_stopCheckEvents(void) {
+ _RGFW->stopCheckEvents_bool = RGFW_TRUE;
+}
+
+RGFW_bool RGFW_createSurfacePtr(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) {
+ surface->data = data;
+ surface->w = w;
+ surface->h = h;
+ surface->format = format;
+ return RGFW_TRUE;
+}
+
+void RGFW_window_blitSurface(RGFW_window* win, RGFW_surface* surface) {
+ /* TODO: Needs fixing. */
+ RGFW_copyImageData(surface->data, surface->w, RGFW_MIN(win->h, surface->h), RGFW_formatRGBA8, surface->data, surface->format, surface->convertFunc);
+ EM_ASM_({
+ var data = Module.HEAPU8.slice($0, $0 + $1 * $2 * 4);
+ let context = document.getElementById("canvas").getContext("2d");
+ let image = context.getImageData(0, 0, $1, $2);
+ image.data.set(data);
+ context.putImageData(image, 0, $4 - $2);
+ }, surface->data, surface->w, surface->h, RGFW_MIN(win->h, surface->w), RGFW_MIN(win->h, surface->h));
+}
+
+void RGFW_surface_freePtr(RGFW_surface* surface) { }
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <stdio.h>
+
+void EMSCRIPTEN_KEEPALIVE RGFW_mkdir(char* name) { mkdir(name, 0755); }
+
+void EMSCRIPTEN_KEEPALIVE RGFW_writeFile(const char *path, const char *data, size_t len) {
+ FILE* file = fopen(path, "w+");
+ if (file == NULL)
+ return;
+
+ fwrite(data, sizeof(char), len, file);
+ fclose(file);
+}
+
+void RGFW_initKeycodesPlatform(void) {
+ _RGFW->keycodes[DOM_VK_BACK_QUOTE] = RGFW_keyBacktick;
+ _RGFW->keycodes[DOM_VK_0] = RGFW_key0;
+ _RGFW->keycodes[DOM_VK_1] = RGFW_key1;
+ _RGFW->keycodes[DOM_VK_2] = RGFW_key2;
+ _RGFW->keycodes[DOM_VK_3] = RGFW_key3;
+ _RGFW->keycodes[DOM_VK_4] = RGFW_key4;
+ _RGFW->keycodes[DOM_VK_5] = RGFW_key5;
+ _RGFW->keycodes[DOM_VK_6] = RGFW_key6;
+ _RGFW->keycodes[DOM_VK_7] = RGFW_key7;
+ _RGFW->keycodes[DOM_VK_8] = RGFW_key8;
+ _RGFW->keycodes[DOM_VK_9] = RGFW_key9;
+ _RGFW->keycodes[DOM_VK_SPACE] = RGFW_keySpace;
+ _RGFW->keycodes[DOM_VK_A] = RGFW_keyA;
+ _RGFW->keycodes[DOM_VK_B] = RGFW_keyB;
+ _RGFW->keycodes[DOM_VK_C] = RGFW_keyC;
+ _RGFW->keycodes[DOM_VK_D] = RGFW_keyD;
+ _RGFW->keycodes[DOM_VK_E] = RGFW_keyE;
+ _RGFW->keycodes[DOM_VK_F] = RGFW_keyF;
+ _RGFW->keycodes[DOM_VK_G] = RGFW_keyG;
+ _RGFW->keycodes[DOM_VK_H] = RGFW_keyH;
+ _RGFW->keycodes[DOM_VK_I] = RGFW_keyI;
+ _RGFW->keycodes[DOM_VK_J] = RGFW_keyJ;
+ _RGFW->keycodes[DOM_VK_K] = RGFW_keyK;
+ _RGFW->keycodes[DOM_VK_L] = RGFW_keyL;
+ _RGFW->keycodes[DOM_VK_M] = RGFW_keyM;
+ _RGFW->keycodes[DOM_VK_N] = RGFW_keyN;
+ _RGFW->keycodes[DOM_VK_O] = RGFW_keyO;
+ _RGFW->keycodes[DOM_VK_P] = RGFW_keyP;
+ _RGFW->keycodes[DOM_VK_Q] = RGFW_keyQ;
+ _RGFW->keycodes[DOM_VK_R] = RGFW_keyR;
+ _RGFW->keycodes[DOM_VK_S] = RGFW_keyS;
+ _RGFW->keycodes[DOM_VK_T] = RGFW_keyT;
+ _RGFW->keycodes[DOM_VK_U] = RGFW_keyU;
+ _RGFW->keycodes[DOM_VK_V] = RGFW_keyV;
+ _RGFW->keycodes[DOM_VK_W] = RGFW_keyW;
+ _RGFW->keycodes[DOM_VK_X] = RGFW_keyX;
+ _RGFW->keycodes[DOM_VK_Y] = RGFW_keyY;
+ _RGFW->keycodes[DOM_VK_Z] = RGFW_keyZ;
+ _RGFW->keycodes[DOM_VK_PERIOD] = RGFW_keyPeriod;
+ _RGFW->keycodes[DOM_VK_COMMA] = RGFW_keyComma;
+ _RGFW->keycodes[DOM_VK_SLASH] = RGFW_keySlash;
+ _RGFW->keycodes[DOM_VK_OPEN_BRACKET] = RGFW_keyBracket;
+ _RGFW->keycodes[DOM_VK_CLOSE_BRACKET] = RGFW_keyCloseBracket;
+ _RGFW->keycodes[DOM_VK_SEMICOLON] = RGFW_keySemicolon;
+ _RGFW->keycodes[DOM_VK_QUOTE] = RGFW_keyApostrophe;
+ _RGFW->keycodes[DOM_VK_BACK_SLASH] = RGFW_keyBackSlash;
+ _RGFW->keycodes[DOM_VK_RETURN] = RGFW_keyReturn;
+ _RGFW->keycodes[DOM_VK_DELETE] = RGFW_keyDelete;
+ _RGFW->keycodes[DOM_VK_NUM_LOCK] = RGFW_keyNumLock;
+ _RGFW->keycodes[DOM_VK_DIVIDE] = RGFW_keyPadSlash;
+ _RGFW->keycodes[DOM_VK_MULTIPLY] = RGFW_keyPadMultiply;
+ _RGFW->keycodes[DOM_VK_SUBTRACT] = RGFW_keyPadMinus;
+ _RGFW->keycodes[DOM_VK_NUMPAD1] = RGFW_keyPad1;
+ _RGFW->keycodes[DOM_VK_NUMPAD2] = RGFW_keyPad2;
+ _RGFW->keycodes[DOM_VK_NUMPAD3] = RGFW_keyPad3;
+ _RGFW->keycodes[DOM_VK_NUMPAD4] = RGFW_keyPad4;
+ _RGFW->keycodes[DOM_VK_NUMPAD5] = RGFW_keyPad5;
+ _RGFW->keycodes[DOM_VK_NUMPAD6] = RGFW_keyPad6;
+ _RGFW->keycodes[DOM_VK_NUMPAD9] = RGFW_keyPad9;
+ _RGFW->keycodes[DOM_VK_NUMPAD0] = RGFW_keyPad0;
+ _RGFW->keycodes[DOM_VK_DECIMAL] = RGFW_keyPadPeriod;
+ _RGFW->keycodes[DOM_VK_RETURN] = RGFW_keyPadReturn;
+ _RGFW->keycodes[DOM_VK_HYPHEN_MINUS] = RGFW_keyMinus;
+ _RGFW->keycodes[DOM_VK_EQUALS] = RGFW_keyEquals;
+ _RGFW->keycodes[DOM_VK_BACK_SPACE] = RGFW_keyBackSpace;
+ _RGFW->keycodes[DOM_VK_TAB] = RGFW_keyTab;
+ _RGFW->keycodes[DOM_VK_CAPS_LOCK] = RGFW_keyCapsLock;
+ _RGFW->keycodes[DOM_VK_SHIFT] = RGFW_keyShiftL;
+ _RGFW->keycodes[DOM_VK_CONTROL] = RGFW_keyControlL;
+ _RGFW->keycodes[DOM_VK_ALT] = RGFW_keyAltL;
+ _RGFW->keycodes[DOM_VK_META] = RGFW_keySuperL;
+ _RGFW->keycodes[DOM_VK_F1] = RGFW_keyF1;
+ _RGFW->keycodes[DOM_VK_F2] = RGFW_keyF2;
+ _RGFW->keycodes[DOM_VK_F3] = RGFW_keyF3;
+ _RGFW->keycodes[DOM_VK_F4] = RGFW_keyF4;
+ _RGFW->keycodes[DOM_VK_F5] = RGFW_keyF5;
+ _RGFW->keycodes[DOM_VK_F6] = RGFW_keyF6;
+ _RGFW->keycodes[DOM_VK_F7] = RGFW_keyF7;
+ _RGFW->keycodes[DOM_VK_F8] = RGFW_keyF8;
+ _RGFW->keycodes[DOM_VK_F9] = RGFW_keyF9;
+ _RGFW->keycodes[DOM_VK_F10] = RGFW_keyF10;
+ _RGFW->keycodes[DOM_VK_F11] = RGFW_keyF11;
+ _RGFW->keycodes[DOM_VK_F12] = RGFW_keyF12;
+ _RGFW->keycodes[DOM_VK_UP] = RGFW_keyUp;
+ _RGFW->keycodes[DOM_VK_DOWN] = RGFW_keyDown;
+ _RGFW->keycodes[DOM_VK_LEFT] = RGFW_keyLeft;
+ _RGFW->keycodes[DOM_VK_RIGHT] = RGFW_keyRight;
+ _RGFW->keycodes[DOM_VK_INSERT] = RGFW_keyInsert;
+ _RGFW->keycodes[DOM_VK_END] = RGFW_keyEnd;
+ _RGFW->keycodes[DOM_VK_PAGE_UP] = RGFW_keyPageUp;
+ _RGFW->keycodes[DOM_VK_PAGE_DOWN] = RGFW_keyPageDown;
+ _RGFW->keycodes[DOM_VK_ESCAPE] = RGFW_keyEscape;
+ _RGFW->keycodes[DOM_VK_HOME] = RGFW_keyHome;
+ _RGFW->keycodes[DOM_VK_SCROLL_LOCK] = RGFW_keyScrollLock;
+ _RGFW->keycodes[DOM_VK_PRINTSCREEN] = RGFW_keyPrintScreen;
+ _RGFW->keycodes[DOM_VK_PAUSE] = RGFW_keyPause;
+ _RGFW->keycodes[DOM_VK_F13] = RGFW_keyF13;
+ _RGFW->keycodes[DOM_VK_F14] = RGFW_keyF14;
+ _RGFW->keycodes[DOM_VK_F15] = RGFW_keyF15;
+ _RGFW->keycodes[DOM_VK_F16] = RGFW_keyF16;
+ _RGFW->keycodes[DOM_VK_F17] = RGFW_keyF17;
+ _RGFW->keycodes[DOM_VK_F18] = RGFW_keyF18;
+ _RGFW->keycodes[DOM_VK_F19] = RGFW_keyF19;
+ _RGFW->keycodes[DOM_VK_F20] = RGFW_keyF20;
+ _RGFW->keycodes[DOM_VK_F21] = RGFW_keyF21;
+ _RGFW->keycodes[DOM_VK_F22] = RGFW_keyF22;
+ _RGFW->keycodes[DOM_VK_F23] = RGFW_keyF23;
+ _RGFW->keycodes[DOM_VK_F24] = RGFW_keyF24;
+}
+
+i32 RGFW_initPlatform(void) {
+ RGFW_monitorNode* node = NULL;
+ RGFW_monitor monitor;
+
+ monitor.name[0] = '\0';
+
+ monitor.x = 0;
+ monitor.y = 0;
+
+ monitor.pixelRatio = EM_ASM_DOUBLE({return window.devicePixelRatio || 1;});
+ monitor.mode.w = EM_ASM_INT({return window.innerWidth || 0;});
+ monitor.mode.h = EM_ASM_INT({return window.innerHeight || 0;});
+
+ monitor.physW = (float)RGFW_ROUND((float)monitor.mode.w * monitor.pixelRatio);
+ monitor.physH = (float)RGFW_ROUND((float)monitor.mode.h * monitor.pixelRatio);
+
+ float dpi = 96.0f * monitor.pixelRatio;
+ monitor.scaleX = dpi / 96.0f;
+ monitor.scaleY = dpi / 96.0f;
+
+ RGFW_splitBPP(32, &monitor.mode);
+
+ monitor.mode.refreshRate = 0;
+
+ node = RGFW_monitors_add(&monitor);
+ if (node != NULL) {
+ _RGFW->monitors.primary = node;
+ RGFW_monitorCallback(_RGFW->root, &node->mon, RGFW_TRUE);
+ }
+
+ return 0;
+}
+
+RGFW_window* RGFW_createWindowPlatform(const char* name, RGFW_windowFlags flags, RGFW_window* win) {
+ emscripten_set_canvas_element_size("#canvas", win->w, win->h);
+ emscripten_set_window_title(name);
+
+ /* load callbacks */
+ emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_resize);
+ emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, EM_FALSE, Emscripten_on_fullscreenchange);
+ emscripten_set_mousemove_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mousemove);
+ emscripten_set_touchstart_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchstart);
+ emscripten_set_touchend_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchend);
+ emscripten_set_touchmove_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchmove);
+ emscripten_set_touchcancel_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchcancel);
+ emscripten_set_mousedown_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mousedown);
+ emscripten_set_mouseup_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mouseup);
+ emscripten_set_wheel_callback("#canvas", NULL, EM_FALSE, Emscripten_on_wheel);
+ emscripten_set_focusin_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_focusin);
+ emscripten_set_focusout_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_focusout);
+
+ if (flags & RGFW_windowAllowDND) {
+ win->internal.flags |= RGFW_windowAllowDND;
+ }
+
+ EM_ASM({
+ window.addEventListener("keydown",
+ (event) => {
+ var code = stringToNewUTF8(event.code);
+ Module._RGFW_handleKeyMods(event.getModifierState("CapsLock"), event.getModifierState("NumLock"), event.getModifierState("Control"), event.getModifierState("Alt"), event.getModifierState("Shift"), event.getModifierState("Meta"), event.getModifierState("ScrollLock"));
+
+ var codepoint = event.key.charCodeAt(0);
+ if(codepoint < 0x7f && event.key.length > 1) {
+ codepoint = 0;
+ }
+
+ Module._RGFW_handleKeyEvent(code, codepoint, 1);
+ Module._RGFW_webFree(code);
+ },
+ true);
+ window.addEventListener("keyup",
+ (event) => {
+ var code = stringToNewUTF8(event.code);
+ Module._RGFW_handleKeyMods(event.getModifierState("CapsLock"), event.getModifierState("NumLock"), event.getModifierState("Control"), event.getModifierState("Alt"), event.getModifierState("Shift"), event.getModifierState("Meta"), event.getModifierState("ScrollLock"));
+ Module._RGFW_handleKeyEvent(code, 0, 0);
+ Module._RGFW_webFree(code);
+ },
+ true);
+ });
+
+ EM_ASM({
+ var canvas = document.getElementById('canvas');
+ canvas.addEventListener('drop', function(e) {
+ e.preventDefault();
+ if (e.dataTransfer.file < 0)
+ return;
+
+ var count = e.dataTransfer.files.length;
+
+ /* Read and save the files to emscripten's files */
+ var drop_dir = '.rgfw_dropped_files';
+ Module._RGFW_mkdir(drop_dir);
+
+ for (var i = 0; i < count; i++) {
+ var file = e.dataTransfer.files[i];
+
+ var path = '/' + drop_dir + '/' + file.name.replace("//", '_');
+ var reader = new FileReader();
+
+ reader.onloadend = (e) => {
+ if (reader.readyState != 2) {
+ out('failed to read dropped file: '+file.name+': '+reader.error);
+ }
+ else {
+ var data = e.target.result;
+
+ Module._RGFW_writeFile(path, new Uint8Array(data), file.size);
+ }
+ };
+
+ reader.readAsArrayBuffer(file);
+ var filename = stringToNewUTF8(path);
+
+ Module._Emscripten_onDrop(filename, path.length + 1);
+ free(filename);
+ }
+
+ }, true);
+
+ canvas.addEventListener('dragover', function(e) { e.preventDefault(); return false; }, true);
+ });
+
+ return win;
+}
+
+RGFW_key RGFW_physicalToMappedKey(RGFW_key key) {
+ return key;
+}
+
+RGFW_bool RGFW_window_fetchSize(RGFW_window* win, i32* w, i32* h) {
+ return RGFW_window_getSize(win, w, h);
+}
+
+void RGFW_pollEvents(void) {
+ static int using_asyncify = -1;
+ if (using_asyncify == -1) using_asyncify = EM_ASM_INT({ return 'Asyncify' in Module; });
+
+ RGFW_resetPrevState();
+ if (using_asyncify) {
+ emscripten_sleep(0);
+ }
+}
+
+void RGFW_window_resize(RGFW_window* win, i32 w, i32 h) {
+ RGFW_UNUSED(win);
+ emscripten_set_canvas_element_size("#canvas", w, h);
+}
+
+RGFW_mouse* RGFW_createMouseStandard(RGFW_mouseIcon mouse) {
+ char* cursorName = NULL;
+
+ switch (mouse) {
+ case RGFW_mouseNormal: cursorName = (char*)"default"; break;
+ case RGFW_mouseArrow: cursorName = (char*)"default"; break;
+ case RGFW_mouseIbeam: cursorName = (char*)"text"; break;
+ case RGFW_mouseCrosshair: cursorName = (char*)"crosshair"; break;
+ case RGFW_mousePointingHand: cursorName = (char*)"pointer"; break;
+ case RGFW_mouseResizeEW: cursorName = (char*)"ew-resize"; break;
+ case RGFW_mouseResizeNS: cursorName = (char*)"ns-resize"; break;
+ case RGFW_mouseResizeNWSE: cursorName = (char*)"nwse-resize"; break;
+ case RGFW_mouseResizeNESW: cursorName = (char*)"nesw-resize"; break;
+ case RGFW_mouseResizeNW: cursorName = (char*)"nw-resize"; break;
+ case RGFW_mouseResizeN: cursorName = (char*)"n-resize"; break;
+ case RGFW_mouseResizeNE: cursorName = (char*)"ne-resize"; break;
+ case RGFW_mouseResizeE: cursorName = (char*)"e-resize"; break;
+ case RGFW_mouseResizeSE: cursorName = (char*)"se-resize"; break;
+ case RGFW_mouseResizeS: cursorName = (char*)"s-resize"; break;
+ case RGFW_mouseResizeSW: cursorName = (char*)"sw-resize"; break;
+ case RGFW_mouseResizeW: cursorName = (char*)"w-resize"; break;
+ case RGFW_mouseResizeAll: cursorName = (char*)"move"; break;
+ case RGFW_mouseNotAllowed: cursorName = (char*)"not-allowed"; break;
+ case RGFW_mouseWait: cursorName = (char*)"wait"; break;
+ case RGFW_mouseProgress: cursorName = (char*)"progress"; break;
+ default: return NULL;
+ }
+
+ return (RGFW_mouse*)cursorName;
+}
+
+/* NOTE: I don't know if this is possible */
+void RGFW_window_moveMouse(RGFW_window* win, i32 x, i32 y) { RGFW_UNUSED(win); RGFW_UNUSED(x); RGFW_UNUSED(y); }
+/* this one might be possible but it looks iffy */
+RGFW_mouse* RGFW_createMouse(u8* data, i32 w, i32 h, RGFW_format format) { RGFW_UNUSED(data); RGFW_UNUSED(w); RGFW_UNUSED(h); RGFW_UNUSED(format); return NULL; }
+
+RGFW_bool RGFW_window_setMousePlatform(RGFW_window* win, RGFW_mouse* mouse) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_ASSERT(mouse != NULL);
+
+ EM_ASM( { document.getElementById("canvas").style.cursor = UTF8ToString($0); }, (char*)mouse);
+ return RGFW_TRUE;
+}
+void RGFW_freeMouse(RGFW_mouse* mouse) { RGFW_UNUSED(mouse); }
+
+void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) {
+ RGFW_window_showMouseFlags(win, show);
+ if (show)
+ RGFW_window_setMouseDefault(win);
+ else
+ EM_ASM(document.getElementById('canvas').style.cursor = 'none';);
+}
+
+RGFW_bool RGFW_getGlobalMouse(i32* x, i32* y) {
+ if(x) *x = EM_ASM_INT({
+ return window.mouseX || 0;
+ });
+ if (y) *y = EM_ASM_INT({
+ return window.mouseY || 0;
+ });
+ return RGFW_TRUE;
+}
+
+void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) {
+ RGFW_UNUSED(win);
+
+ EM_ASM_({
+ var canvas = document.getElementById('canvas');
+ if ($0) {
+ canvas.style.pointerEvents = 'none';
+ } else {
+ canvas.style.pointerEvents = 'auto';
+ }
+ }, passthrough);
+}
+
+RGFW_bool RGFW_writeClipboard(const RGFW_dataTransfer* data) {
+ RGFW_ASSERT(data != NULL);
+ EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, data->data);
+ return RGFW_TRUE;
+}
+
+
+RGFW_bool RGFW_readClipboardPtr(u8* buffer, size_t capacity, RGFW_dataTransfer* data) {
+ RGFW_ASSERT(data != NULL);
+ RGFW_UNUSED(buffer); RGFW_UNUSED(capacity);
+
+ /*
+ placeholder code for later
+ I'm not sure if this is possible do the the async stuff
+ */
+ return RGFW_FALSE;
+}
+
+#ifdef RGFW_OPENGL
+RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) {
+ win->src.ctx.native = ctx;
+ win->src.gfxType = RGFW_gfxNativeOpenGL;
+
+ EmscriptenWebGLContextAttributes attrs;
+ emscripten_webgl_init_context_attributes(&attrs);
+ attrs.alpha = hints->alpha;
+ attrs.depth = hints->depth;
+ attrs.stencil = hints->stencil;
+ attrs.antialias = hints->samples;
+ attrs.premultipliedAlpha = EM_TRUE;
+ attrs.preserveDrawingBuffer = EM_FALSE;
+
+ if (hints->doubleBuffer == 0)
+ attrs.renderViaOffscreenBackBuffer = 0;
+ else
+ attrs.renderViaOffscreenBackBuffer = hints->auxBuffers;
+
+ attrs.failIfMajorPerformanceCaveat = EM_FALSE;
+
+ attrs.enableExtensionsByDefault = EM_TRUE;
+
+ if (hints->profile == RGFW_glWeb) {
+ attrs.majorVersion = (hints->major == 0) ? 1 : hints->major;
+ attrs.minorVersion = hints->minor;
+ } else {
+ attrs.majorVersion = (hints->major == 0) ? 1 : ( (hints->major > 1) ? hints->major - 1 : hints->major );
+ attrs.minorVersion = hints->minor;
+ }
+
+ attrs.explicitSwapControl = EM_TRUE;
+ win->src.ctx.native->ctx = emscripten_webgl_create_context("#canvas", &attrs);
+
+ if (win->src.ctx.native->ctx == 0) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_warningOpenGL, "WebGL: Failed to create an OpenGL Context with explicit swap control.");
+ attrs.explicitSwapControl = EM_FALSE;
+ win->src.ctx.native->ctx = emscripten_webgl_create_context("#canvas", &attrs);
+ }
+
+ if (win->src.ctx.native->ctx == 0) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errOpenGLContext, "Failed to create an OpenGL Context with the requested attributes, falling back to defaults.");
+ win->src.ctx.native->ctx = emscripten_webgl_create_context("#canvas", &attrs);
+ }
+
+ if (win->src.ctx.native->ctx == 0) {
+ RGFW_debugCallback(RGFW_typeError, RGFW_errOpenGLContext, "Failed to create an OpenGL Context.");
+ return RGFW_FALSE;
+ }
+
+ emscripten_webgl_make_context_current(win->src.ctx.native->ctx);
+
+ #ifdef LEGACY_GL_EMULATION
+ EM_ASM("Module.useWebGL = true; GLImmediate.init();");
+ RGFW_debugCallback(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context initalized.");
+ #endif
+
+ RGFW_window_swapInterval_OpenGL(win, 0);
+
+ return RGFW_TRUE;
+}
+
+void RGFW_window_deleteContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx) {
+ emscripten_webgl_destroy_context(ctx->ctx);
+ win->src.ctx.native->ctx = 0;
+ RGFW_debugCallback(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context freed.");
+}
+
+void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win) {
+ if (win) RGFW_ASSERT(win->src.ctx.native);
+ if (win == NULL)
+ emscripten_webgl_make_context_current(0);
+ else
+ emscripten_webgl_make_context_current(win->src.ctx.native->ctx);
+}
+
+void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) {
+ RGFW_ASSERT(win && win->src.ctx.native);
+ emscripten_webgl_commit_frame();
+}
+void* RGFW_getCurrentContext_OpenGL(void) { return (void*)emscripten_webgl_get_current_context(); }
+
+RGFW_bool RGFW_extensionSupportedPlatform_OpenGL(const char* extension, size_t len) {
+ return EM_ASM_INT({
+ var ext = UTF8ToString($0, $1);
+ var canvas = document.querySelector('canvas');
+ var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
+ if (!gl) return 0;
+
+ var supported = gl.getSupportedExtensions();
+ return supported && supported.includes(ext) ? 1 : 0;
+ }, extension, len);
+ return RGFW_FALSE;
+}
+
+RGFW_proc RGFW_getProcAddress_OpenGL(const char* procname) {
+ return (RGFW_proc)emscripten_webgl_get_proc_address(procname);
+ return NULL;
+}
+
+#endif
+
+void RGFW_window_swapInterval_OpenGL(RGFW_window* win, i32 swapInterval) { RGFW_UNUSED(win); RGFW_UNUSED(swapInterval); }
+
+void RGFW_deinitPlatform(void) { }
+
+void RGFW_window_closePlatform(RGFW_window* win) { }
+
+int RGFW_innerWidth(void) { return EM_ASM_INT({ return window.innerWidth; }); }
+int RGFW_innerHeight(void) { return EM_ASM_INT({ return window.innerHeight; }); }
+
+void RGFW_window_setRawMouseModePlatform(RGFW_window* win, RGFW_bool state) {
+ RGFW_UNUSED(win); RGFW_UNUSED(state);
+}
+
+void RGFW_window_captureMousePlatform(RGFW_window* win, RGFW_bool state) {
+ RGFW_UNUSED(win);
+ if (state) {
+ emscripten_request_pointerlock("#canvas", 1);
+ } else {
+ emscripten_exit_pointerlock();
+ }
+}
+
+void RGFW_window_setName(RGFW_window* win, const char* name) {
+ RGFW_UNUSED(win);
+ if (name == NULL) name = "\0";
+
+ emscripten_set_window_title(name);
+}
+
+void RGFW_window_maximize(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+
+ RGFW_monitor* mon = RGFW_window_getMonitor(win);
+ if (mon != NULL) {
+ RGFW_window_resize(win, mon->mode.w, mon->mode.h);
+ }
+
+ RGFW_window_move(win, 0, 0);
+ RGFW_window_fetchSize(win, NULL, NULL);
+}
+
+void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) {
+ RGFW_ASSERT(win != NULL);
+ if (fullscreen) {
+ win->internal.flags |= RGFW_windowFullscreen;
+ EM_ASM( Module.requestFullscreen(false, true); );
+ return;
+ }
+ win->internal.flags &= ~(u32)RGFW_windowFullscreen;
+ EM_ASM( Module.exitFullscreen(false, true); );
+}
+
+void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) {
+ RGFW_UNUSED(win);
+ EM_ASM({
+ var element = document.getElementById("canvas");
+ if (element)
+ element.style.opacity = $1;
+ }, "elementId", opacity);
+}
+
+#ifdef RGFW_WEBGPU
+WGPUSurface RGFW_window_createSurface_WebGPU(RGFW_window* window, WGPUInstance instance) {
+ WGPUSurfaceDescriptor surfaceDesc = {0};
+ WGPUEmscriptenSurfaceSourceCanvasHTMLSelector canvasDesc = {0};
+ canvasDesc.chain.sType = WGPUSType_EmscriptenSurfaceSourceCanvasHTMLSelector;
+ canvasDesc.selector = (WGPUStringView){.data = "#canvas", .length = 7};
+
+ surfaceDesc.nextInChain = &canvasDesc.chain;
+ return wgpuInstanceCreateSurface(instance, &surfaceDesc);
+}
+#endif
+
+RGFW_key RGFW_WASMPhysicalToRGFW(u32 hash) {
+ switch(hash) { /* 0x0000 */
+ case 0x67243A2DU /* Escape */: return RGFW_keyEscape; /* 0x0001 */
+ case 0x67251058U /* Digit0 */: return RGFW_key0; /* 0x0002 */
+ case 0x67251059U /* Digit1 */: return RGFW_key1; /* 0x0003 */
+ case 0x6725105AU /* Digit2 */: return RGFW_key2; /* 0x0004 */
+ case 0x6725105BU /* Digit3 */: return RGFW_key3; /* 0x0005 */
+ case 0x6725105CU /* Digit4 */: return RGFW_key4; /* 0x0006 */
+ case 0x6725105DU /* Digit5 */: return RGFW_key5; /* 0x0007 */
+ case 0x6725105EU /* Digit6 */: return RGFW_key6; /* 0x0008 */
+ case 0x6725105FU /* Digit7 */: return RGFW_key7; /* 0x0009 */
+ case 0x67251050U /* Digit8 */: return RGFW_key8; /* 0x000A */
+ case 0x67251051U /* Digit9 */: return RGFW_key9; /* 0x000B */
+ case 0x92E14DD3U /* Minus */: return RGFW_keyMinus; /* 0x000C */
+ case 0x92E1FBACU /* Equal */: return RGFW_keyEquals; /* 0x000D */
+ case 0x36BF1CB5U /* Backspace */: return RGFW_keyBackSpace; /* 0x000E */
+ case 0x7B8E51E2U /* Tab */: return RGFW_keyTab; /* 0x000F */
+ case 0x2C595B51U /* KeyQ */: return RGFW_keyQ; /* 0x0010 */
+ case 0x2C595B57U /* KeyW */: return RGFW_keyW; /* 0x0011 */
+ case 0x2C595B45U /* KeyE */: return RGFW_keyE; /* 0x0012 */
+ case 0x2C595B52U /* KeyR */: return RGFW_keyR; /* 0x0013 */
+ case 0x2C595B54U /* KeyT */: return RGFW_keyT; /* 0x0014 */
+ case 0x2C595B59U /* KeyY */: return RGFW_keyY; /* 0x0015 */
+ case 0x2C595B55U /* KeyU */: return RGFW_keyU; /* 0x0016 */
+ case 0x2C595B4FU /* KeyO */: return RGFW_keyO; /* 0x0018 */
+ case 0x2C595B50U /* KeyP */: return RGFW_keyP; /* 0x0019 */
+ case 0x45D8158CU /* BracketLeft */: return RGFW_keyCloseBracket; /* 0x001A */
+ case 0xDEEABF7CU /* BracketRight */: return RGFW_keyBracket; /* 0x001B */
+ case 0x92E1C5D2U /* Enter */: return RGFW_keyReturn; /* 0x001C */
+ case 0xE058958CU /* ControlLeft */: return RGFW_keyControlL; /* 0x001D */
+ case 0x2C595B41U /* KeyA */: return RGFW_keyA; /* 0x001E */
+ case 0x2C595B53U /* KeyS */: return RGFW_keyS; /* 0x001F */
+ case 0x2C595B44U /* KeyD */: return RGFW_keyD; /* 0x0020 */
+ case 0x2C595B46U /* KeyF */: return RGFW_keyF; /* 0x0021 */
+ case 0x2C595B47U /* KeyG */: return RGFW_keyG; /* 0x0022 */
+ case 0x2C595B48U /* KeyH */: return RGFW_keyH; /* 0x0023 */
+ case 0x2C595B4AU /* KeyJ */: return RGFW_keyJ; /* 0x0024 */
+ case 0x2C595B4BU /* KeyK */: return RGFW_keyK; /* 0x0025 */
+ case 0x2C595B4CU /* KeyL */: return RGFW_keyL; /* 0x0026 */
+ case 0x2707219EU /* Semicolon */: return RGFW_keySemicolon; /* 0x0027 */
+ case 0x92E0B58DU /* Quote */: return RGFW_keyApostrophe; /* 0x0028 */
+ case 0x36BF358DU /* Backquote */: return RGFW_keyBacktick; /* 0x0029 */
+ case 0x26B1958CU /* ShiftLeft */: return RGFW_keyShiftL; /* 0x002A */
+ case 0x36BF2438U /* Backslash */: return RGFW_keyBackSlash; /* 0x002B */
+ case 0x2C595B5AU /* KeyZ */: return RGFW_keyZ; /* 0x002C */
+ case 0x2C595B58U /* KeyX */: return RGFW_keyX; /* 0x002D */
+ case 0x2C595B43U /* KeyC */: return RGFW_keyC; /* 0x002E */
+ case 0x2C595B56U /* KeyV */: return RGFW_keyV; /* 0x002F */
+ case 0x2C595B42U /* KeyB */: return RGFW_keyB; /* 0x0030 */
+ case 0x2C595B4EU /* KeyN */: return RGFW_keyN; /* 0x0031 */
+ case 0x2C595B4DU /* KeyM */: return RGFW_keyM; /* 0x0032 */
+ case 0x92E1A1C1U /* Comma */: return RGFW_keyComma; /* 0x0033 */
+ case 0x672FFAD4U /* Period */: return RGFW_keyPeriod; /* 0x0034 */
+ case 0x92E0A438U /* Slash */: return RGFW_keySlash; /* 0x0035 */
+ case 0xC5A6BF7CU /* ShiftRight */: return RGFW_keyShiftR;
+ case 0x5D64DA91U /* NumpadMultiply */: return RGFW_keyPadMultiply;
+ case 0xC914958CU /* AltLeft */: return RGFW_keyAltL; /* 0x0038 */
+ case 0x92E09CB5U /* Space */: return RGFW_keySpace; /* 0x0039 */
+ case 0xB8FAE73BU /* CapsLock */: return RGFW_keyCapsLock; /* 0x003A */
+ case 0x7174B789U /* F1 */: return RGFW_keyF1; /* 0x003B */
+ case 0x7174B78AU /* F2 */: return RGFW_keyF2; /* 0x003C */
+ case 0x7174B78BU /* F3 */: return RGFW_keyF3; /* 0x003D */
+ case 0x7174B78CU /* F4 */: return RGFW_keyF4; /* 0x003E */
+ case 0x7174B78DU /* F5 */: return RGFW_keyF5; /* 0x003F */
+ case 0x7174B78EU /* F6 */: return RGFW_keyF6; /* 0x0040 */
+ case 0x7174B78FU /* F7 */: return RGFW_keyF7; /* 0x0041 */
+ case 0x7174B780U /* F8 */: return RGFW_keyF8; /* 0x0042 */
+ case 0x7174B781U /* F9 */: return RGFW_keyF9; /* 0x0043 */
+ case 0x7B8E57B0U /* F10 */: return RGFW_keyF10; /* 0x0044 */
+ case 0xC925FCDFU /* Numpad7 */: return RGFW_keyPadMultiply; /* 0x0047 */
+ case 0xC925FCD0U /* Numpad8 */: return RGFW_keyPad8; /* 0x0048 */
+ case 0xC925FCD1U /* Numpad9 */: return RGFW_keyPad9; /* 0x0049 */
+ case 0x5EA3E8A4U /* NumpadSubtract */: return RGFW_keyMinus; /* 0x004A */
+ case 0xC925FCDCU /* Numpad4 */: return RGFW_keyPad4; /* 0x004B */
+ case 0xC925FCDDU /* Numpad5 */: return RGFW_keyPad5; /* 0x004C */
+ case 0xC925FCDEU /* Numpad6 */: return RGFW_keyPad6; /* 0x004D */
+ case 0xC925FCD9U /* Numpad1 */: return RGFW_keyPad1; /* 0x004F */
+ case 0xC925FCDAU /* Numpad2 */: return RGFW_keyPad2; /* 0x0050 */
+ case 0xC925FCDBU /* Numpad3 */: return RGFW_keyPad3; /* 0x0051 */
+ case 0xC925FCD8U /* Numpad0 */: return RGFW_keyPad0; /* 0x0052 */
+ case 0x95852DACU /* NumpadDecimal */: return RGFW_keyPeriod; /* 0x0053 */
+ case 0x7B8E57B1U /* F11 */: return RGFW_keyF11; /* 0x0057 */
+ case 0x7B8E57B2U /* F12 */: return RGFW_keyF12; /* 0x0058 */
+ case 0x7B8E57B3U /* F13 */: return DOM_PK_F13; /* 0x0064 */
+ case 0x7B8E57B4U /* F14 */: return DOM_PK_F14; /* 0x0065 */
+ case 0x7B8E57B5U /* F15 */: return DOM_PK_F15; /* 0x0066 */
+ case 0x7B8E57B6U /* F16 */: return DOM_PK_F16; /* 0x0067 */
+ case 0x7B8E57B7U /* F17 */: return DOM_PK_F17; /* 0x0068 */
+ case 0x7B8E57B8U /* F18 */: return DOM_PK_F18; /* 0x0069 */
+ case 0x7B8E57B9U /* F19 */: return DOM_PK_F19; /* 0x006A */
+ case 0x7B8E57A8U /* F20 */: return DOM_PK_F20; /* 0x006B */
+ case 0x7B8E57A9U /* F21 */: return DOM_PK_F21; /* 0x006C */
+ case 0x7B8E57AAU /* F22 */: return DOM_PK_F22; /* 0x006D */
+ case 0x7B8E57ABU /* F23 */: return DOM_PK_F23; /* 0x006E */
+ case 0x7393FBACU /* NumpadEqual */: return RGFW_keyPadReturn;
+ case 0xB88EBF7CU /* AltRight */: return RGFW_keyAltR; /* 0xE038 */
+ case 0xC925873BU /* NumLock */: return RGFW_keyNumLock; /* 0xE045 */
+ case 0x2C595F45U /* Home */: return RGFW_keyHome; /* 0xE047 */
+ case 0xC91BB690U /* ArrowUp */: return RGFW_keyUp; /* 0xE048 */
+ case 0x672F9210U /* PageUp */: return RGFW_keyPageUp; /* 0xE049 */
+ case 0x3799258CU /* ArrowLeft */: return RGFW_keyLeft; /* 0xE04B */
+ case 0x4CE33F7CU /* ArrowRight */: return RGFW_keyRight; /* 0xE04D */
+ case 0x7B8E55DCU /* End */: return RGFW_keyEnd; /* 0xE04F */
+ case 0x3799379EU /* ArrowDown */: return RGFW_keyDown; /* 0xE050 */
+ case 0xBA90179EU /* PageDown */: return RGFW_keyPageDown; /* 0xE051 */
+ case 0x6723CB2CU /* Insert */: return RGFW_keyInsert; /* 0xE052 */
+ case 0x6725C50DU /* Delete */: return RGFW_keyDelete; /* 0xE053 */
+ case 0x6723658CU /* OSLeft */: return RGFW_keySuperL; /* 0xE05B */
+ case 0x39643F7CU /* MetaRight */: return RGFW_keySuperR; /* 0xE05C */
+ case 0x380B9C8CU /* NumpadAdd */: return DOM_PK_NUMPAD_ADD; /* 0x004E */
+ default: return DOM_PK_UNKNOWN;
+ }
+
+ return 0;
+}
+
+RGFW_monitor* RGFW_window_getMonitor(RGFW_window* win) {
+ RGFW_UNUSED(win);
+ return RGFW_getPrimaryMonitor();
+}
+
+/* unsupported functions */
+void RGFW_pollMonitors(void) { }
+void RGFW_window_focus(RGFW_window* win) { RGFW_UNUSED(win); }
+void RGFW_window_raise(RGFW_window* win) { RGFW_UNUSED(win); }
+RGFW_bool RGFW_monitor_requestMode(RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request) { RGFW_UNUSED(mon); RGFW_UNUSED(mode); RGFW_UNUSED(request); return RGFW_FALSE; }
+RGFW_bool RGFW_monitor_getWorkarea(RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height) { RGFW_UNUSED(monitor); RGFW_UNUSED(x); RGFW_UNUSED(width); RGFW_UNUSED(height); return RGFW_FALSE; }
+size_t RGFW_monitor_getGammaRampPtr(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) { RGFW_UNUSED(monitor); RGFW_UNUSED(ramp); return 0; }
+RGFW_bool RGFW_monitor_setGammaRamp(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) { RGFW_UNUSED(monitor); RGFW_UNUSED(ramp); return RGFW_FALSE; }
+size_t RGFW_monitor_getModesPtr(RGFW_monitor* mon, RGFW_monitorMode** modes) { RGFW_UNUSED(mon); RGFW_UNUSED(modes); return 0; }
+RGFW_bool RGFW_monitor_setMode(RGFW_monitor* mon, RGFW_monitorMode* mode) { RGFW_UNUSED(mon); RGFW_UNUSED(mode); return RGFW_FALSE; }
+void RGFW_window_move(RGFW_window* win, i32 x, i32 y) { RGFW_UNUSED(win); RGFW_UNUSED(x); RGFW_UNUSED(y); }
+void RGFW_window_setAspectRatio(RGFW_window* win, i32 w, i32 h) { RGFW_UNUSED(win); RGFW_UNUSED(w); RGFW_UNUSED(h); }
+void RGFW_window_setMinSize(RGFW_window* win, i32 w, i32 h) { RGFW_UNUSED(win); RGFW_UNUSED(w); RGFW_UNUSED(h); }
+void RGFW_window_setMaxSize(RGFW_window* win, i32 w, i32 h) { RGFW_UNUSED(win); RGFW_UNUSED(w); RGFW_UNUSED(h); }
+void RGFW_window_minimize(RGFW_window* win) { RGFW_UNUSED(win); }
+void RGFW_window_restore(RGFW_window* win) { RGFW_UNUSED(win); }
+void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) { RGFW_UNUSED(win); RGFW_UNUSED(floating); }
+void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { RGFW_UNUSED(win); RGFW_UNUSED(border); }
+RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_icon type) { RGFW_UNUSED(win); RGFW_UNUSED(data); RGFW_UNUSED(w); RGFW_UNUSED(h); RGFW_UNUSED(format); RGFW_UNUSED(type); return RGFW_FALSE; }
+void RGFW_window_hide(RGFW_window* win) { RGFW_UNUSED(win); }
+void RGFW_window_show(RGFW_window* win) {RGFW_UNUSED(win); }
+void RGFW_window_flash(RGFW_window* win, RGFW_flashRequest request) { RGFW_UNUSED(win); RGFW_UNUSED(request); }
+RGFW_bool RGFW_window_isHidden(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; }
+RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; }
+RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; }
+RGFW_bool RGFW_window_isFloating(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; }
+void RGFW_waitForEvent(i32 waitMS) { RGFW_UNUSED(waitMS); }
+#endif
+
+/* end of web asm defines */
+
+/*
+ * RGFW function pointer backend, made to allow you to compile for Wayland but fallback to X11
+*/
+#ifdef RGFW_DYNAMIC
+typedef RGFW_window* (*RGFW_createWindowPlatform_ptr)(const char* name, RGFW_windowFlags flags, RGFW_window* win);
+typedef RGFW_bool (*RGFW_getMouse_ptr)(i32* x, i32* y);
+typedef RGFW_key (*RGFW_physicalToMappedKey_ptr)(RGFW_key key);
+typedef void (*RGFW_pollEvents_ptr)(void);
+typedef RGFW_bool (*RGFW_window_fetchSize_ptr)(RGFW_window* win, i32* w, i32* h);
+typedef void (*RGFW_pollMonitors_ptr)(void);
+typedef void (*RGFW_window_move_ptr)(RGFW_window* win, i32 x, i32 y);
+typedef void (*RGFW_window_resize_ptr)(RGFW_window* win, i32 w, i32 h);
+typedef void (*RGFW_window_setAspectRatio_ptr)(RGFW_window* win, i32 w, i32 h);
+typedef void (*RGFW_window_setMinSize_ptr)(RGFW_window* win, i32 w, i32 h);
+typedef void (*RGFW_window_setMaxSize_ptr)(RGFW_window* win, i32 w, i32 h);
+typedef void (*RGFW_window_maximize_ptr)(RGFW_window* win);
+typedef void (*RGFW_window_focus_ptr)(RGFW_window* win);
+typedef void (*RGFW_window_raise_ptr)(RGFW_window* win);
+typedef void (*RGFW_window_setFullscreen_ptr)(RGFW_window* win, RGFW_bool fullscreen);
+typedef void (*RGFW_window_setFloating_ptr)(RGFW_window* win, RGFW_bool floating);
+typedef void (*RGFW_window_setOpacity_ptr)(RGFW_window* win, u8 opacity);
+typedef void (*RGFW_window_minimize_ptr)(RGFW_window* win);
+typedef void (*RGFW_window_restore_ptr)(RGFW_window* win);
+typedef RGFW_bool (*RGFW_window_isFloating_ptr)(RGFW_window* win);
+typedef void (*RGFW_window_setName_ptr)(RGFW_window* win, const char* name);
+typedef void (*RGFW_window_setMousePassthrough_ptr)(RGFW_window* win, RGFW_bool passthrough);
+typedef RGFW_bool (*RGFW_window_setIconEx_ptr)(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, u8 type);
+typedef RGFW_mouse* (*RGFW_createMouse_ptr)(u8* data, i32 w, i32 h, RGFW_format format);
+typedef RGFW_mouse* (*RGFW_createMouseStandard_ptr)(RGFW_mouseIcon icons);
+typedef RGFW_bool (*RGFW_window_setMousePlatform_ptr)(RGFW_window* win, RGFW_mouse* mouse);
+typedef void (*RGFW_window_moveMouse_ptr)(RGFW_window* win, i32 x, i32 y);
+typedef void (*RGFW_window_hide_ptr)(RGFW_window* win);
+typedef void (*RGFW_window_show_ptr)(RGFW_window* win);
+typedef void (*RGFW_window_flash_ptr)(RGFW_window* win, RGFW_flashRequest request);
+typedef RGFW_bool (*RGFW_readClipboardPtr_ptr)(u8* buffer, size_t capacity, RGFW_dataTransfer* data);
+typedef RGFW_bool (*RGFW_writeClipboard_ptr)(const RGFW_dataTransfer* data);
+typedef RGFW_bool (*RGFW_window_isHidden_ptr)(RGFW_window* win);
+typedef RGFW_bool (*RGFW_window_isMinimized_ptr)(RGFW_window* win);
+typedef RGFW_bool (*RGFW_window_isMaximized_ptr)(RGFW_window* win);
+typedef RGFW_bool (*RGFW_monitor_requestMode_ptr)(RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request);
+typedef RGFW_bool (*RGFW_monitor_getWorkarea_ptr)(RGFW_monitor* mon, i32* x, i32* y, i32* w, i32* h);
+typedef size_t (*RGFW_monitor_getModesPtr_ptr)(RGFW_monitor* mon, RGFW_monitorMode** modes);
+typedef size_t (*RGFW_monitor_getGammaRampPtr_ptr) (RGFW_monitor* monitor, RGFW_gammaRamp* ramp);
+typedef RGFW_bool (*RGFW_monitor_setGammaRamp_ptr) (RGFW_monitor* monitor, RGFW_gammaRamp* ramp);
+typedef RGFW_bool (*RGFW_monitor_setMode_ptr)(RGFW_monitor* mon, RGFW_monitorMode* mode);
+typedef RGFW_monitor* (*RGFW_window_getMonitor_ptr)(RGFW_window* win);
+typedef void (*RGFW_window_closePlatform_ptr)(RGFW_window* win);
+typedef RGFW_format (*RGFW_nativeFormat_ptr)(void);
+typedef RGFW_bool (*RGFW_createSurfacePtr_ptr)(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface);
+typedef void (*RGFW_window_blitSurface_ptr)(RGFW_window* win, RGFW_surface* surface);
+typedef void (*RGFW_surface_freePtr_ptr)(RGFW_surface* surface);
+typedef void (*RGFW_freeMouse_ptr)(RGFW_mouse* mouse);
+typedef void (*RGFW_window_setBorder_ptr)(RGFW_window* win, RGFW_bool border);
+typedef void (*RGFW_window_captureMousePlatform_ptr)(RGFW_window* win, RGFW_bool state);
+typedef void (*RGFW_window_setRawMouseModePlatform_ptr)(RGFW_window* win, RGFW_bool state);
+#ifdef RGFW_OPENGL
+typedef void (*RGFW_window_makeCurrentContext_OpenGL_ptr)(RGFW_window* win);
+typedef void* (*RGFW_getCurrentContext_OpenGL_ptr)(void);
+typedef void (*RGFW_window_swapBuffers_OpenGL_ptr)(RGFW_window* win);
+typedef void (*RGFW_window_swapInterval_OpenGL_ptr)(RGFW_window* win, i32 swapInterval);
+typedef RGFW_bool (*RGFW_extensionSupportedPlatform_OpenGL_ptr)(const char* extension, size_t len);
+typedef RGFW_proc (*RGFW_getProcAddress_OpenGL_ptr)(const char* procname);
+typedef RGFW_bool (*RGFW_window_createContextPtr_OpenGL_ptr)(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints);
+typedef void (*RGFW_window_deleteContextPtr_OpenGL_ptr)(RGFW_window* win, RGFW_glContext* ctx);
+#endif
+#ifdef RGFW_WEBGPU
+typedef WGPUSurface (*RGFW_window_createSurface_WebGPU_ptr)(RGFW_window* window, WGPUInstance instance);
+#endif
+
+/* Structure to hold all function pointers */
+typedef struct RGFW_FunctionPointers {
+ RGFW_nativeFormat_ptr nativeFormat;
+ RGFW_createSurfacePtr_ptr createSurfacePtr;
+ RGFW_window_blitSurface_ptr window_blitSurface;
+ RGFW_surface_freePtr_ptr surface_freePtr;
+ RGFW_freeMouse_ptr freeMouse;
+ RGFW_window_setBorder_ptr window_setBorder;
+ RGFW_window_captureMousePlatform_ptr window_captureMousePlatform;
+ RGFW_window_setRawMouseModePlatform_ptr window_setRawMouseModePlatform;
+ RGFW_createWindowPlatform_ptr createWindowPlatform;
+ RGFW_getMouse_ptr getGlobalMouse;
+ RGFW_physicalToMappedKey_ptr physicalToMappedKey;
+ RGFW_window_fetchSize_ptr window_fetchSize;
+ RGFW_pollEvents_ptr pollEvents;
+ RGFW_pollMonitors_ptr pollMonitors;
+ RGFW_window_move_ptr window_move;
+ RGFW_window_resize_ptr window_resize;
+ RGFW_window_setAspectRatio_ptr window_setAspectRatio;
+ RGFW_window_setMinSize_ptr window_setMinSize;
+ RGFW_window_setMaxSize_ptr window_setMaxSize;
+ RGFW_window_maximize_ptr window_maximize;
+ RGFW_window_focus_ptr window_focus;
+ RGFW_window_raise_ptr window_raise;
+ RGFW_window_setFullscreen_ptr window_setFullscreen;
+ RGFW_window_setFloating_ptr window_setFloating;
+ RGFW_window_setOpacity_ptr window_setOpacity;
+ RGFW_window_minimize_ptr window_minimize;
+ RGFW_window_restore_ptr window_restore;
+ RGFW_window_isFloating_ptr window_isFloating;
+ RGFW_window_setName_ptr window_setName;
+ RGFW_window_setMousePassthrough_ptr window_setMousePassthrough;
+ RGFW_window_setIconEx_ptr window_setIconEx;
+ RGFW_createMouse_ptr createMouse;
+ RGFW_createMouseStandard_ptr createMouseStandard;
+ RGFW_window_setMousePlatform_ptr window_setMousePlatform;
+ RGFW_window_moveMouse_ptr window_moveMouse;
+ RGFW_window_hide_ptr window_hide;
+ RGFW_window_show_ptr window_show;
+ RGFW_window_flash_ptr window_flash;
+ RGFW_readClipboardPtr_ptr readClipboardPtr;
+ RGFW_writeClipboard_ptr writeClipboard;
+ RGFW_window_isHidden_ptr window_isHidden;
+ RGFW_window_isMinimized_ptr window_isMinimized;
+ RGFW_window_isMaximized_ptr window_isMaximized;
+ RGFW_monitor_requestMode_ptr monitor_requestMode;
+ RGFW_monitor_getWorkarea_ptr monitor_getWorkarea;
+ RGFW_monitor_getModesPtr_ptr monitor_getModesPtr;
+ RGFW_monitor_getGammaRampPtr_ptr monitor_getGammaRampPtr;
+ RGFW_monitor_setGammaRamp_ptr monitor_setGammaRamp;
+ RGFW_monitor_setMode_ptr monitor_setMode;
+ RGFW_window_getMonitor_ptr window_getMonitor;
+ RGFW_window_closePlatform_ptr window_closePlatform;
+#ifdef RGFW_OPENGL
+ RGFW_extensionSupportedPlatform_OpenGL_ptr extensionSupportedPlatform_OpenGL;
+ RGFW_getProcAddress_OpenGL_ptr getProcAddress_OpenGL;
+ RGFW_window_createContextPtr_OpenGL_ptr window_createContextPtr_OpenGL;
+ RGFW_window_deleteContextPtr_OpenGL_ptr window_deleteContextPtr_OpenGL;
+ RGFW_window_makeCurrentContext_OpenGL_ptr window_makeCurrentContext_OpenGL;
+ RGFW_getCurrentContext_OpenGL_ptr getCurrentContext_OpenGL;
+ RGFW_window_swapBuffers_OpenGL_ptr window_swapBuffers_OpenGL;
+ RGFW_window_swapInterval_OpenGL_ptr window_swapInterval_OpenGL;
+#endif
+#ifdef RGFW_WEBGPU
+ RGFW_window_createSurface_WebGPU_ptr window_createSurface_WebGPU;
+#endif
+} RGFW_functionPointers;
+
+RGFW_functionPointers RGFW_api;
+
+RGFW_format RGFW_nativeFormat(void) { return RGFW_api.nativeFormat(); }
+RGFW_bool RGFW_createSurfacePtr(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) { return RGFW_api.createSurfacePtr(data, w, h, format, surface); }
+void RGFW_surface_freePtr(RGFW_surface* surface) { RGFW_api.surface_freePtr(surface); }
+void RGFW_freeMouse(RGFW_mouse* mouse) { RGFW_api.freeMouse(mouse); }
+void RGFW_window_blitSurface(RGFW_window* win, RGFW_surface* surface) { RGFW_api.window_blitSurface(win, surface); }
+void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { RGFW_api.window_setBorder(win, border); }
+void RGFW_window_captureMousePlatform(RGFW_window* win, RGFW_bool state) { RGFW_api.window_captureMousePlatform(win, state); }
+void RGFW_window_setRawMouseModePlatform(RGFW_window* win, RGFW_bool state) { RGFW_api.window_setRawMouseModePlatform(win, state); }
+RGFW_window* RGFW_createWindowPlatform(const char* name, RGFW_windowFlags flags, RGFW_window* win) { RGFW_init(); return RGFW_api.createWindowPlatform(name, flags, win); }
+RGFW_bool RGFW_getGlobalMouse(i32* x, i32* y) { return RGFW_api.getGlobalMouse(x, y); }
+RGFW_key RGFW_physicalToMappedKey(RGFW_key key) { return RGFW_api.physicalToMappedKey(key); }
+void RGFW_pollEvents(void) { RGFW_api.pollEvents(); }
+RGFW_bool RGFW_window_fetchSize(RGFW_window* win, i32* w, i32* h) { return RGFW_api.window_fetchSize(win, w, h); }
+void RGFW_pollMonitors(void) { RGFW_api.pollMonitors(); }
+void RGFW_window_move(RGFW_window* win, i32 x, i32 y) { RGFW_api.window_move(win, x, y); }
+void RGFW_window_resize(RGFW_window* win, i32 w, i32 h) { RGFW_api.window_resize(win, w, h); }
+void RGFW_window_setAspectRatio(RGFW_window* win, i32 w, i32 h) { RGFW_api.window_setAspectRatio(win, w, h); }
+void RGFW_window_setMinSize(RGFW_window* win, i32 w, i32 h) { RGFW_api.window_setMinSize(win, w, h); }
+void RGFW_window_setMaxSize(RGFW_window* win, i32 w, i32 h) { RGFW_api.window_setMaxSize(win, w, h); }
+void RGFW_window_maximize(RGFW_window* win) { RGFW_api.window_maximize(win); }
+void RGFW_window_focus(RGFW_window* win) { RGFW_api.window_focus(win); }
+void RGFW_window_raise(RGFW_window* win) { RGFW_api.window_raise(win); }
+void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) { RGFW_api.window_setFullscreen(win, fullscreen); }
+void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) { RGFW_api.window_setFloating(win, floating); }
+void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) { RGFW_api.window_setOpacity(win, opacity); }
+void RGFW_window_minimize(RGFW_window* win) { RGFW_api.window_minimize(win); }
+void RGFW_window_restore(RGFW_window* win) { RGFW_api.window_restore(win); }
+RGFW_bool RGFW_window_isFloating(RGFW_window* win) { return RGFW_api.window_isFloating(win); }
+void RGFW_window_setName(RGFW_window* win, const char* name) { RGFW_api.window_setName(win, name); }
+
+#ifndef RGFW_NO_PASSTHROUGH
+void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) { RGFW_api.window_setMousePassthrough(win, passthrough); }
+#endif
+
+RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, u8 type) { return RGFW_api.window_setIconEx(win, data, w, h, format, type); }
+RGFW_mouse* RGFW_createMouse(u8* data, i32 w, i32 h, RGFW_format format) { return RGFW_api.createMouse(data, w, h, format); }
+RGFW_mouse* RGFW_createMouseStandard(RGFW_mouseIcon icon) { return RGFW_api.createMouseStandard(icon); }
+RGFW_bool RGFW_window_setMousePlatform(RGFW_window* win, RGFW_mouse* mouse) { return RGFW_api.window_setMousePlatform(win, mouse); }
+void RGFW_window_moveMouse(RGFW_window* win, i32 x, i32 y) { RGFW_api.window_moveMouse(win, x, y); }
+void RGFW_window_hide(RGFW_window* win) { RGFW_api.window_hide(win); }
+void RGFW_window_show(RGFW_window* win) { RGFW_api.window_show(win); }
+void RGFW_window_flash(RGFW_window* win, RGFW_flashRequest request) { RGFW_api.window_flash(win, request); }
+RGFW_bool RGFW_readClipboardPtr(u8* buffer, size_t capacity, RGFW_dataTransfer* data) { return RGFW_api.readClipboardPtr(buffer, capacity, data); }
+RGFW_bool RGFW_writeClipboard(const RGFW_dataTransfer* data) { return RGFW_api.writeClipboard(data); }
+RGFW_bool RGFW_window_isHidden(RGFW_window* win) { return RGFW_api.window_isHidden(win); }
+RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { return RGFW_api.window_isMinimized(win); }
+RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { return RGFW_api.window_isMaximized(win); }
+RGFW_bool RGFW_monitor_requestMode(RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request) { return RGFW_api.monitor_requestMode(mon, mode, request); }
+RGFW_bool RGFW_monitor_getWorkarea(RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height) { return RGFW_api.monitor_getWorkarea(monitor, x, y, width, height); }
+size_t RGFW_monitor_getGammaRampPtr(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) { return RGFW_api.monitor_getGammaRampPtr(monitor, ramp); }
+RGFW_bool RGFW_monitor_setGammaRamp(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) { return RGFW_api.monitor_setGammaRamp(monitor, ramp); }
+size_t RGFW_monitor_getModesPtr(RGFW_monitor* mon, RGFW_monitorMode** modes) { return RGFW_api.monitor_getModesPtr(mon, modes); }
+RGFW_bool RGFW_monitor_setMode(RGFW_monitor* mon, RGFW_monitorMode* mode) { return RGFW_api.monitor_setMode(mon, mode); }
+RGFW_monitor* RGFW_window_getMonitor(RGFW_window* win) { return RGFW_api.window_getMonitor(win); }
+void RGFW_window_closePlatform(RGFW_window* win) { RGFW_api.window_closePlatform(win); }
+
+#ifdef RGFW_OPENGL
+RGFW_bool RGFW_extensionSupportedPlatform_OpenGL(const char* extension, size_t len) { return RGFW_api.extensionSupportedPlatform_OpenGL(extension, len); }
+RGFW_proc RGFW_getProcAddress_OpenGL(const char* procname) { return RGFW_api.getProcAddress_OpenGL(procname); }
+RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) { return RGFW_api.window_createContextPtr_OpenGL(win, ctx, hints); }
+void RGFW_window_deleteContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx) { RGFW_api.window_deleteContextPtr_OpenGL(win, ctx); }
+void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win) { RGFW_api.window_makeCurrentContext_OpenGL(win); }
+void* RGFW_getCurrentContext_OpenGL(void) { return RGFW_api.getCurrentContext_OpenGL(); }
+void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) { RGFW_api.window_swapBuffers_OpenGL(win); }
+void RGFW_window_swapInterval_OpenGL(RGFW_window* win, i32 swapInterval) { RGFW_api.window_swapInterval_OpenGL(win, swapInterval); }
+#endif
+
+#ifdef RGFW_WEBGPU
+WGPUSurface RGFW_window_createSurface_WebGPU(RGFW_window* window, WGPUInstance instance) { return RGFW_api.window_createSurface_WebGPU(window, instance); }
+#endif
+#endif /* RGFW_DYNAMIC */
+
+/*
+ * start of X11 AND wayland defines
+ * this allows a single executable to support x11 AND wayland
+ * falling back to x11 if wayland fails to initalize
+*/
+#if defined(RGFW_WAYLAND) && defined(RGFW_X11)
+RGFW_bool RGFW_useWaylandBool = RGFW_TRUE;
+void RGFW_useWayland(RGFW_bool wayland) { RGFW_useWaylandBool = RGFW_BOOL(wayland); }
+RGFW_bool RGFW_usingWayland(void) { return RGFW_useWaylandBool; }
+
+void RGFW_load_X11(void) {
+ RGFW_api.nativeFormat = RGFW_nativeFormat_X11;
+ RGFW_api.createSurfacePtr = RGFW_createSurfacePtr_X11;
+ RGFW_api.window_blitSurface = RGFW_window_blitSurface_X11;
+ RGFW_api.surface_freePtr = RGFW_surface_freePtr_X11;
+ RGFW_api.freeMouse = RGFW_freeMouse_X11;
+ RGFW_api.window_setBorder = RGFW_window_setBorder_X11;
+ RGFW_api.window_captureMousePlatform = RGFW_window_captureMousePlatform_X11;
+ RGFW_api.window_setRawMouseModePlatform = RGFW_window_setRawMouseModePlatform_X11;
+ RGFW_api.createWindowPlatform = RGFW_createWindowPlatform_X11;
+ RGFW_api.getGlobalMouse = RGFW_getGlobalMouse_X11;
+ RGFW_api.physicalToMappedKey = RGFW_physicalToMappedKey_X11;
+ RGFW_api.pollEvents = RGFW_pollEvents_X11;
+ RGFW_api.window_fetchSize = RGFW_window_fetchSize_X11;
+ RGFW_api.pollMonitors = RGFW_pollMonitors_X11;
+ RGFW_api.window_move = RGFW_window_move_X11;
+ RGFW_api.window_resize = RGFW_window_resize_X11;
+ RGFW_api.window_setAspectRatio = RGFW_window_setAspectRatio_X11;
+ RGFW_api.window_setMinSize = RGFW_window_setMinSize_X11;
+ RGFW_api.window_setMaxSize = RGFW_window_setMaxSize_X11;
+ RGFW_api.window_maximize = RGFW_window_maximize_X11;
+ RGFW_api.window_focus = RGFW_window_focus_X11;
+ RGFW_api.window_raise = RGFW_window_raise_X11;
+ RGFW_api.window_setFullscreen = RGFW_window_setFullscreen_X11;
+ RGFW_api.window_setFloating = RGFW_window_setFloating_X11;
+ RGFW_api.window_setOpacity = RGFW_window_setOpacity_X11;
+ RGFW_api.window_minimize = RGFW_window_minimize_X11;
+ RGFW_api.window_restore = RGFW_window_restore_X11;
+ RGFW_api.window_isFloating = RGFW_window_isFloating_X11;
+ RGFW_api.window_setName = RGFW_window_setName_X11;
+#ifndef RGFW_NO_PASSTHROUGH
+ RGFW_api.window_setMousePassthrough = RGFW_window_setMousePassthrough_X11;
+#endif
+ RGFW_api.window_setIconEx = RGFW_window_setIconEx_X11;
+ RGFW_api.createMouse = RGFW_createMouse_X11;
+ RGFW_api.createMouseStandard = RGFW_createMouseStandard_X11;
+ RGFW_api.window_setMousePlatform = RGFW_window_setMousePlatform_X11;
+ RGFW_api.window_moveMouse = RGFW_window_moveMouse_X11;
+ RGFW_api.window_hide = RGFW_window_hide_X11;
+ RGFW_api.window_show = RGFW_window_show_X11;
+ RGFW_api.window_flash = RGFW_window_flash_X11;
+ RGFW_api.readClipboardPtr = RGFW_readClipboardPtr_X11;
+ RGFW_api.writeClipboard = RGFW_writeClipboard_X11;
+ RGFW_api.window_isHidden = RGFW_window_isHidden_X11;
+ RGFW_api.window_isMinimized = RGFW_window_isMinimized_X11;
+ RGFW_api.window_isMaximized = RGFW_window_isMaximized_X11;
+ RGFW_api.monitor_requestMode = RGFW_monitor_requestMode_X11;
+ RGFW_api.monitor_getModesPtr = RGFW_monitor_getModesPtr_X11;
+ RGFW_api.monitor_setGammaRamp = RGFW_monitor_setGammaRamp_X11;
+ RGFW_api.monitor_getGammaRampPtr = RGFW_monitor_getGammaRampPtr_X11;
+ RGFW_api.monitor_setMode = RGFW_monitor_setMode_X11;
+ RGFW_api.window_getMonitor = RGFW_window_getMonitor_X11;
+ RGFW_api.window_closePlatform = RGFW_window_closePlatform_X11;
+#ifdef RGFW_OPENGL
+ RGFW_api.extensionSupportedPlatform_OpenGL = RGFW_extensionSupportedPlatform_OpenGL_X11;
+ RGFW_api.getProcAddress_OpenGL = RGFW_getProcAddress_OpenGL_X11;
+ RGFW_api.window_createContextPtr_OpenGL = RGFW_window_createContextPtr_OpenGL_X11;
+ RGFW_api.window_deleteContextPtr_OpenGL = RGFW_window_deleteContextPtr_OpenGL_X11;
+ RGFW_api.window_makeCurrentContext_OpenGL = RGFW_window_makeCurrentContext_OpenGL_X11;
+ RGFW_api.getCurrentContext_OpenGL = RGFW_getCurrentContext_OpenGL_X11;
+ RGFW_api.window_swapBuffers_OpenGL = RGFW_window_swapBuffers_OpenGL_X11;
+ RGFW_api.window_swapInterval_OpenGL = RGFW_window_swapInterval_OpenGL_X11;
+#endif
+#ifdef RGFW_WEBGPU
+ RGFW_api.window_createSurface_WebGPU = RGFW_window_createSurface_WebGPU_X11;
+#endif
+}
+
+void RGFW_load_Wayland(void) {
+ RGFW_api.nativeFormat = RGFW_nativeFormat_Wayland;
+ RGFW_api.createSurfacePtr = RGFW_createSurfacePtr_Wayland;
+ RGFW_api.window_blitSurface = RGFW_window_blitSurface_Wayland;
+ RGFW_api.surface_freePtr = RGFW_surface_freePtr_Wayland;
+ RGFW_api.freeMouse = RGFW_freeMouse_Wayland;
+ RGFW_api.window_setBorder = RGFW_window_setBorder_Wayland;
+ RGFW_api.window_captureMousePlatform = RGFW_window_captureMousePlatform_Wayland;
+ RGFW_api.window_setRawMouseModePlatform = RGFW_window_setRawMouseModePlatform_Wayland;
+ RGFW_api.createWindowPlatform = RGFW_createWindowPlatform_Wayland;
+ RGFW_api.getGlobalMouse = RGFW_getGlobalMouse_Wayland;
+ RGFW_api.physicalToMappedKey = RGFW_physicalToMappedKey_Wayland;
+ RGFW_api.pollEvents = RGFW_pollEvents_Wayland;
+ RGFW_api.window_fetchSize = RGFW_window_fetchSize_Wayland;
+ RGFW_api.pollMonitors = RGFW_pollMonitors_Wayland;
+ RGFW_api.window_move = RGFW_window_move_Wayland;
+ RGFW_api.window_resize = RGFW_window_resize_Wayland;
+ RGFW_api.window_setAspectRatio = RGFW_window_setAspectRatio_Wayland;
+ RGFW_api.window_setMinSize = RGFW_window_setMinSize_Wayland;
+ RGFW_api.window_setMaxSize = RGFW_window_setMaxSize_Wayland;
+ RGFW_api.window_maximize = RGFW_window_maximize_Wayland;
+ RGFW_api.window_focus = RGFW_window_focus_Wayland;
+ RGFW_api.window_raise = RGFW_window_raise_Wayland;
+ RGFW_api.window_setFullscreen = RGFW_window_setFullscreen_Wayland;
+ RGFW_api.window_setFloating = RGFW_window_setFloating_Wayland;
+ RGFW_api.window_setOpacity = RGFW_window_setOpacity_Wayland;
+ RGFW_api.window_minimize = RGFW_window_minimize_Wayland;
+ RGFW_api.window_restore = RGFW_window_restore_Wayland;
+ RGFW_api.window_isFloating = RGFW_window_isFloating_Wayland;
+ RGFW_api.window_setName = RGFW_window_setName_Wayland;
+#ifndef RGFW_NO_PASSTHROUGH
+ RGFW_api.window_setMousePassthrough = RGFW_window_setMousePassthrough_Wayland;
+#endif
+ RGFW_api.window_setIconEx = RGFW_window_setIconEx_Wayland;
+ RGFW_api.createMouse = RGFW_createMouse_Wayland;
+ RGFW_api.createMouseStandard = RGFW_createMouseStandard_Wayland;
+ RGFW_api.window_setMousePlatform = RGFW_window_setMousePlatform_Wayland;
+ RGFW_api.window_moveMouse = RGFW_window_moveMouse_Wayland;
+ RGFW_api.window_hide = RGFW_window_hide_Wayland;
+ RGFW_api.window_show = RGFW_window_show_Wayland;
+ RGFW_api.window_flash = RGFW_window_flash_X11;
+ RGFW_api.readClipboardPtr = RGFW_readClipboardPtr_Wayland;
+ RGFW_api.writeClipboard = RGFW_writeClipboard_Wayland;
+ RGFW_api.window_isHidden = RGFW_window_isHidden_Wayland;
+ RGFW_api.window_isMinimized = RGFW_window_isMinimized_Wayland;
+ RGFW_api.window_isMaximized = RGFW_window_isMaximized_Wayland;
+ RGFW_api.monitor_requestMode = RGFW_monitor_requestMode_Wayland;
+ RGFW_api.monitor_getModesPtr = RGFW_monitor_getModesPtr_Wayland;
+ RGFW_api.monitor_setGammaRamp = RGFW_monitor_setGammaRamp_Wayland;
+ RGFW_api.monitor_getGammaRampPtr = RGFW_monitor_getGammaRampPtr_Wayland;
+ RGFW_api.monitor_setMode = RGFW_monitor_setMode_Wayland;
+ RGFW_api.window_getMonitor = RGFW_window_getMonitor_Wayland;
+ RGFW_api.window_closePlatform = RGFW_window_closePlatform_Wayland;
+#ifdef RGFW_OPENGL
+ RGFW_api.extensionSupportedPlatform_OpenGL = RGFW_extensionSupportedPlatform_OpenGL_Wayland;
+ RGFW_api.getProcAddress_OpenGL = RGFW_getProcAddress_OpenGL_Wayland;
+ RGFW_api.window_createContextPtr_OpenGL = RGFW_window_createContextPtr_OpenGL_Wayland;
+ RGFW_api.window_deleteContextPtr_OpenGL = RGFW_window_deleteContextPtr_OpenGL_Wayland;
+ RGFW_api.window_makeCurrentContext_OpenGL = RGFW_window_makeCurrentContext_OpenGL_Wayland;
+ RGFW_api.getCurrentContext_OpenGL = RGFW_getCurrentContext_OpenGL_Wayland;
+ RGFW_api.window_swapBuffers_OpenGL = RGFW_window_swapBuffers_OpenGL_Wayland;
+ RGFW_api.window_swapInterval_OpenGL = RGFW_window_swapInterval_OpenGL_Wayland;
+#endif
+#ifdef RGFW_WEBGPU
+ RGFW_api.window_createSurface_WebGPU = RGFW_window_createSurface_WebGPU_Wayland;
+#endif
+}
+#endif /* wayland AND x11 */
+/* end of X11 AND wayland defines */
+
+#endif /* RGFW_IMPLEMENTATION */
+
+#if defined(__cplusplus) && !defined(__EMSCRIPTEN__)
+}
+#endif
+
+#if _MSC_VER
+ #pragma warning( pop )
+#endif
+
diff --git a/include/RSGL.h b/include/RSGL.h
new file mode 100644
index 0000000..cfd961b
--- /dev/null
+++ b/include/RSGL.h
@@ -0,0 +1,1926 @@
+/*
+*
+* Copyright (c) 2021-26 ColleagueRiley ColleagueRiley@gmail.com
+*
+* 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.
+*
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked as such, and must not be
+* misrepresented as being the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*
+*
+*/
+
+/*
+ define args
+ (MAKE SURE RSGL_IMPLEMENTATION is in exactly one header or you use -DRSGL_IMPLEMENTATION)
+ #define RSGL_IMPLEMENTATION - makes it so source code is included with header
+
+ #define RSGL_RFONT - do include functions to help with integrating RFont and RSGL
+ #define RSGL_MAX_BATCHES [number of batches] - set max number of batches to be allocated
+ #define RSGL_MAX_VERTS [number of verts] - set max number of verts to be allocated (global, not per batch)
+*/
+#include <stdint.h>
+#ifndef RSGL_MAX_BATCHES
+#define RSGL_MAX_BATCHES 2028
+#endif
+#ifndef RSGL_MAX_VERTS
+#define RSGL_MAX_VERTS 8192
+#endif
+
+#ifndef RSGL_MALLOC
+#include <stdlib.h>
+#define RSGL_MALLOC malloc
+#define RSGL_REALLOC realloc
+#define RSGL_FREE free
+#endif
+
+#ifndef RSGL_SIN
+#include <math.h>
+#define RSGL_SIN sinf
+#define RSGL_COS cosf
+#endif
+
+#ifndef RSGL_UNUSED
+#define RSGL_UNUSED(x) (void) (x);
+#endif
+
+#ifndef RSGL_MEMCPY
+ #include <string.h>
+ #define RSGL_MEMCPY(dest, src, count) memcpy(dest, src, count)
+ #define RSGL_MEMSET(ptr, value, num) memset(ptr, value, num)
+#endif
+
+#ifndef RSGL_H
+#define RSGL_H
+#ifndef RSGLDEF
+#ifdef __APPLE__
+#define RSGLDEF extern inline
+#else
+#define RSGLDEF inline
+#endif
+#endif
+
+#ifndef RSGL_INT_DEFINED
+ #define RSGL_INT_DEFINED
+ #if defined(_MSC_VER) || defined(__SYMBIAN32__)
+ typedef unsigned char u8;
+ typedef signed char i8;
+ typedef unsigned short u16;
+ typedef signed short i16;
+ typedef unsigned int u32;
+ typedef signed int i32;
+ typedef unsigned long long u64;
+ typedef signed long long i64;
+ #else
+ #include <stdint.h>
+
+ typedef uint8_t u8;
+ typedef int8_t i8;
+ typedef uint16_t u16;
+ typedef int16_t i16;
+ typedef uint32_t u32;
+ typedef int32_t i32;
+ typedef uint64_t u64;
+ typedef int64_t i64;
+ #endif
+#endif
+
+#ifndef RSGL_BOOL_DEFINED
+#define RSGL_BOOL_DEFINED
+
+#include <stdbool.h>
+typedef bool RSGL_bool;
+#endif
+
+#define RSGL_TRUE (RSGL_bool)1
+#define RSGL_FALSE (RSGL_bool)0
+
+typedef enum RSGL_textureFormat {
+ RSGL_formatNone = 0,
+ RSGL_formatRGB, /*!< 8-bit RGB (3 channels) */
+ RSGL_formatBGR, /*!< 8-bit BGR (3 channels) */
+ RSGL_formatRGBA, /*!< 8-bit RGBA (4 channels) */
+ RSGL_formatBGRA, /*!< 8-bit BGRA (4 channels) */
+ RSGL_formatRed, /*!< 8-bit RED (1 channel) */
+ RSGL_formatGrayscale, /*!< 8-bit grayscale (1 channel) */
+ RSGL_formatGrayscaleAlpha, /*!< 8-bit grayscale alpha (1 channel) */
+ RSGL_formatCount
+} RSGL_textureFormat;
+
+typedef enum RSGL_textureDataType {
+ RSGL_textureDataInt = 0,
+ RSGL_textureDataFloat
+} RSGL_textureDataType;
+
+typedef enum RSGL_textureFilter {
+ RSGL_filterNearest = 0,
+ RSGL_filterLinear
+} RSGL_textureFilter;
+
+typedef struct RSGL_textureBlob {
+ void* data; /* input data */
+ size_t width; /* width of the texture */
+ size_t height; /* height of the texture */
+ RSGL_textureDataType dataType;
+ RSGL_textureFormat dataFormat; /* format of the input data */
+ RSGL_textureFormat textureFormat; /* final format for the texture */
+ RSGL_textureFilter minFilter; /* filter used when rendering a surface smaller than the base texture */
+ RSGL_textureFilter magFilter; /* filter used when rendering a surface bigger than the base texture */
+} RSGL_textureBlob;
+
+#ifndef RSGL_texture
+#define RSGL_texture size_t
+#endif
+
+#ifndef RSGL_framebuffer
+#define RSGL_framebuffer size_t
+#endif
+
+/*
+*******
+RSGL shapes
+*******
+*/
+
+#ifndef RSGL_rect
+typedef struct RSGL_rect { float x, y, w, h; } RSGL_rect;
+#endif
+
+#define RSGL_RECT(x, y, w, h) (RSGL_rect){(float)x, (float)y, (float)w, (float)h}
+
+#ifndef RSGL_cube
+typedef struct RSGL_cube { float x, y, z, w, h, l; } RSGL_cube;
+#endif
+
+#define RSGL_CUBE(x, y, z, w, h, l) (RSGL_cube){(float)x, (float)y, (float)z, (float)w, (float)h, (float)l}
+
+#ifndef RSGL_vec2D
+typedef struct RSGL_vec2D { float x, y; } RSGL_vec2D;
+#endif
+#define RSGL_VEC2D(x, y) (RSGL_vec2D){(float)x, (float)y}
+
+#ifndef RSGL_vec3D
+typedef struct RSGL_vec3D { float x, y, z; } RSGL_vec3D;
+#endif
+
+#define RSGL_VEC3D(x, y, z) (RSGL_vec3D){(float)x, (float)y, (float)z}
+
+#define RSGL_TRIANGLE(p1, p2, p3) (RSGL_vec3D[3]){p1, p2, p3}
+#define RSGL_createTriangle(p1x, p1y, p1z, p2x, p2y, p2z, p3x, p3y, p3z) RSGL_TRIANGLE3D(RSGL_VEC3D(p1x, p1y, p1z), RSGL_VEC3D(p2x, p2y, p2z), RSGL_VEC3D(p3x, p3y, p3z))
+
+/*
+the color stucture is in
+ABGR by default for performance reasons
+(converting color to hex for example)
+*/
+#ifndef RSGL_color
+typedef struct RSGL_color {
+ u8 a, b, g, r;
+} RSGL_color;
+#endif
+
+#define RSGL_RGBA(r, g, b, a) ((RSGL_color){(u8)(a), (u8)(b), (u8)(g), (u8)(r)})
+#define RSGL_RGB(r, g, b) ((RSGL_color){255, (u8)(b), (u8)(g), (u8)(r)})
+
+#define RSGL_COLOR_TO_HEX(color) ((u32)(color) & 0xFFFFFF00)
+#define RSGL_RGB_TO_HEX(r, g, b, a) (RSGL_COLOR_TO_HEX(RSGL_RGBA(r, g, b, a)))
+#define RSGL_RGBA_TO_HEX(r, g, b) (RSGL_COLOR_TO_HEX(RSGL_RGB(r, g, b, a)))
+
+/*
+*********************
+RSGL matrix math
+*********************
+*/
+
+#ifndef RSGL_mat4
+typedef struct RSGL_mat4 {
+ float m[16];
+} RSGL_mat4;
+#endif
+
+RSGLDEF RSGL_mat4 RSGL_mat4_loadIdentity(void);
+RSGLDEF RSGL_mat4 RSGL_mat4_scale(float matrix[16], float x, float y, float z);
+RSGLDEF RSGL_mat4 RSGL_mat4_rotate(float matrix[16], float angle, float x, float y, float z);
+RSGLDEF RSGL_mat4 RSGL_mat4_translate(float matrix[16], float x, float y, float z);
+RSGLDEF RSGL_mat4 RSGL_mat4_perspective(float matrix[16], float fovY, float aspect, float zNear, float zFar);
+RSGLDEF RSGL_mat4 RSGL_mat4_ortho(float matrix[16], float left, float right, float bottom, float top, float znear, float zfar);
+RSGLDEF RSGL_mat4 RSGL_mat4_lookAt(float matrix[16], float eyeX, float eyeY, float eyeZ, float targetX, float targetY, float targetZ, float upX, float upY, float upZ);
+
+RSGLDEF RSGL_mat4 RSGL_mat4_multiply(float left[16], float right[16]);
+
+RSGLDEF RSGL_vec3D RSGL_mat4_multiplyPoint(RSGL_mat4 matrix, RSGL_vec3D point);
+
+/*
+*******
+RSGL_perspective
+*******
+*/
+
+typedef enum RSGL_projectionType {
+ RSGL_projectionOrtho2D = 0,
+ RSGL_projectionOrtho3D,
+ RSGL_projectionPerspective3D,
+} RSGL_projectionType;
+
+typedef struct RSGL_projection2D {
+ RSGL_projectionType type;
+ u32 width;
+ u32 height;
+} RSGL_projection2D;
+
+typedef struct RSGL_projection3D {
+ RSGL_projectionType type;
+ float fov;
+ float ratio;
+ float pNear;
+ float pFar;
+} RSGL_projection3D;
+
+typedef union RSGL_projection {
+ RSGL_projectionType type;
+ RSGL_projection2D p2D;
+ RSGL_projection3D p3D;
+} RSGL_projection;
+
+RSGLDEF RSGL_mat4 RSGL_projection_getMatrix(const RSGL_projection* projection);
+
+/*
+*********************
+RSGL renderer
+*********************
+*/
+
+/* used internally for RSGL_deleteProgram */
+typedef enum RSGL_shaderType {
+ RSGL_shaderTypeNone = 0,
+ RSGL_shaderTypeStandard = 1, /* standard vertex+fragment shader */
+ RSGL_shaderTypeCompute = 2,
+ RSGL_shaderTypeGeometry = 4, /* unimplemented as of now */
+} RSGL_shaderType;
+
+/* shader program and blob */
+typedef struct RSGL_programBlob {
+ const char* vertex;
+ size_t vertexLen;
+ const char* fragment;
+ size_t fragmentLen;
+} RSGL_programBlob;
+
+typedef struct RSGL_programInfo {
+ size_t program;
+ size_t perspectiveView;
+ size_t model;
+ size_t vertexPosition;
+ size_t vertexTexCoord;
+ size_t vertexColor;
+
+ RSGL_shaderType type;
+} RSGL_programInfo;
+
+typedef struct RSGL_BATCH {
+ size_t start, len; /* when batch starts and it's length */
+ size_t elmStart, elmCount; /* when element batch starts and it's length */
+ u32 type;
+ RSGL_texture tex;
+ float lineWidth;
+ RSGL_mat4 matrix;
+} RSGL_BATCH; /* batch data type for rendering */
+
+typedef struct RSGL_renderData {
+ float* verts;
+ float* texCoords;
+ float* colors;
+ u16* elements;
+ size_t elements_count;
+ size_t len; /* number of verts */
+
+ RSGL_mat4 perspective; /* perspective matrix */
+} RSGL_renderData;
+
+typedef enum RSGL_bufferType {
+ RSGL_arrayBuffer = 0,
+ RSGL_elementArrayBuffer,
+ RSGL_shaderStorageBuffer,
+ RSGL_textureBuffer,
+ RSGL_uniformBuffer
+} RSGL_bufferType;
+
+typedef struct RSGL_renderBuffers {
+ size_t vertex, color, texture, elements;
+ size_t maxVerts;
+
+ RSGL_BATCH batches[RSGL_MAX_BATCHES];
+ size_t batchCount;
+} RSGL_renderBuffers;
+
+typedef struct RSGL_renderState {
+ float* gradient; /* does not allocate any memory */
+
+ RSGL_rect source;
+ RSGL_texture texture;
+ u32 gradient_len;
+
+ RSGL_color color;
+
+ RSGL_vec3D rotate;
+ RSGL_programInfo* program;
+ RSGL_renderBuffers* buffers;
+
+ RSGL_vec3D center;
+ float lineWidth;
+ RSGL_mat4 modelMatrix;
+ RSGL_mat4 viewMatrix;
+ RSGL_mat4 perspectiveMatrix;
+ RSGL_bool forceBatch;
+ RSGL_bool overflow;
+ RSGL_framebuffer framebuffer;
+} RSGL_renderState;
+
+typedef struct RSGL_renderPass {
+ RSGL_programInfo* program;
+ float* matrix;
+ RSGL_renderBuffers* buffers;
+ RSGL_framebuffer framebuffer;
+} RSGL_renderPass;
+
+typedef struct RSGL_rendererProc {
+ size_t (*size)(void); /* get the size of the renderer's internal struct */
+ RSGL_programBlob (*defaultBlob)(void* ctx);
+ void (*initPtr)(void* ctx, void* proc); /* init render backend */
+ void (*freePtr)(void* ctx); /* free render backend */
+ void (*render)(void* ctx, const RSGL_renderPass* pass);
+ void (*clear)(void* ctx, RSGL_framebuffer framebuffer, float r, float g, float b, float a);
+ void (*viewport)(void* ctx, i32 x, i32 y, i32 w, i32 h);
+ void (*setSurface)(void* ctx, void* surface);
+ RSGL_texture (*createTexture)(void* ctx, const RSGL_textureBlob* blob);
+ void (*copyToTexture)(void* ctx, RSGL_texture texture, size_t x, size_t y, const RSGL_textureBlob* blob);
+ void (*deleteTexture)(void* ctx, RSGL_texture tex);
+ void (*scissorStart)(void* ctx, float x, float y, float w, float h, float renderer_height);
+ void (*scissorEnd)(void* ctx);
+ RSGL_programInfo (*createProgram)(void* ctx, RSGL_programBlob* blob);
+ void (*deleteProgram)(void* ctx, const RSGL_programInfo* program);
+ size_t (*findShaderVariable)(void*, const RSGL_programInfo*, const char*, size_t);
+ void (*updateShaderVariable)(void*, const RSGL_programInfo*, size_t, const float[], u8);
+ RSGL_programInfo (*createComputeProgram)(void* ctx, const char* CShaderCode);
+ void (*dispatchComputeProgram)(void* ctx, const RSGL_programInfo* program, u32 groups_x, u32 groups_y, u32 groups_z);
+ void (*bindComputeTexture)(void* ctx, u32 texture, u8 format);
+ void (*createBuffer)(void* ctx, RSGL_bufferType type, size_t size, const void* data, size_t* buffer);
+ void (*updateBuffer)(void* ctx, RSGL_bufferType type, size_t buffer, void* data, size_t start, size_t len);
+ void (*deleteBuffer)(void* ctx, size_t buffer);
+ RSGL_framebuffer (*createFramebuffer)(void* ctx, size_t width, size_t height);
+ void (*attachFramebuffer)(void* ctx, RSGL_framebuffer fbo, RSGL_texture tex, u8 attachType, u8 mipLevel);
+ void (*deleteFramebuffer)(void* ctx, RSGL_framebuffer fbo);
+} RSGL_rendererProc;
+
+typedef struct RSGL_renderer {
+ RSGL_renderData data;
+ RSGL_renderState state;
+ RSGL_rendererProc proc;
+ void* userPtr;
+ void* ctx; /* pointer for the renderer backend to store any internal data it wants/needs */
+
+ RSGL_texture defaultTexture;
+ RSGL_programInfo defaultProgram;
+ RSGL_mat4 defaultPerspectiveMatrix;
+
+ float verts[RSGL_MAX_VERTS * 3];
+ float texCoords[RSGL_MAX_VERTS * 2];
+ float colors[RSGL_MAX_VERTS * 4];
+ u16 elements[RSGL_MAX_VERTS * 6];
+ RSGL_renderBuffers buffers;
+} RSGL_renderer;
+
+RSGLDEF void RSGL_renderer_getRenderState(RSGL_renderer* renderer, RSGL_renderState* state);
+
+RSGLDEF size_t RSGL_renderer_size(RSGL_renderer* renderer);
+
+RSGLDEF void RSGL_renderer_initPtr(RSGL_rendererProc proc,
+ void* loader, /* opengl prozc address ex. wglProcAddress */
+ void* ptr, /* pointer to allocate backend data */
+ RSGL_renderer* renderer
+ );
+
+RSGLDEF RSGL_renderer* RSGL_renderer_init(RSGL_rendererProc proc, void* loader);
+RSGLDEF void RSGL_renderer_updateSize(RSGL_renderer* renderer, size_t width, size_t height);
+RSGLDEF void RSGL_renderer_freePtr(RSGL_renderer* renderer);
+
+RSGLDEF void RSGL_renderer_setSurface(RSGL_renderer* renderer, void* surface);
+
+RSGLDEF void RSGL_renderer_createBuffer(RSGL_renderer* renderer, RSGL_bufferType type, size_t size, const void* data, size_t* buffer);
+RSGLDEF void RSGL_renderer_updateBuffer(RSGL_renderer* renderer, RSGL_bufferType type, size_t buffer, void* data, size_t start, size_t len);
+RSGLDEF void RSGL_renderer_deleteBuffer(RSGL_renderer* renderer, size_t buffer);
+
+RSGLDEF void RSGL_renderer_createRenderBuffers(RSGL_renderer* renderer, size_t size, RSGL_renderBuffers* buffers);
+RSGLDEF void RSGL_renderer_deleteRenderBuffers(RSGL_renderer* renderer, RSGL_renderBuffers* buffers);
+
+RSGLDEF void RSGL_renderer_render(RSGL_renderer* renderer); /* draw current batch */
+RSGLDEF void RSGL_renderer_updateRenderBuffers(RSGL_renderer* renderer);
+RSGLDEF void RSGL_renderer_renderBuffers(RSGL_renderer* renderer);
+
+RSGLDEF void RSGL_renderer_free(RSGL_renderer* renderer);
+
+RSGLDEF void RSGL_renderer_setRotate(RSGL_renderer* renderer, RSGL_vec3D rotate); /* apply rotation to drawing */
+RSGLDEF void RSGL_renderer_setTexture(RSGL_renderer* renderer, RSGL_texture texture); /* apply texture to drawing */
+RSGLDEF void RSGL_renderer_setTextureSource(RSGL_renderer* renderer, RSGL_texture texture, RSGL_rect rect); /* apply texture to drawing (limited to the given rect) */
+RSGLDEF void RSGL_renderer_setColor(RSGL_renderer* renderer, RSGL_color color); /* apply color to drawing */
+RSGLDEF void RSGL_renderer_setProgram(RSGL_renderer* renderer, RSGL_programInfo* program); /* use shader program for drawing */
+RSGLDEF void RSGL_renderer_setFramebuffer(RSGL_renderer* renderer, RSGL_framebuffer framebuffer);
+RSGLDEF void RSGL_renderer_setRenderBuffers(RSGL_renderer* renderer, RSGL_renderBuffers* buffers);
+RSGLDEF void RSGL_renderer_setGradient(RSGL_renderer* renderer,
+ float* gradient, /* array of gradients */
+ size_t len /* length of array */
+ ); /* apply gradient to drawing, based on color list*/
+RSGLDEF void RSGL_renderer_setCenter(RSGL_renderer* renderer, RSGL_vec3D center); /* the center of the drawing (or shape), this is used for rotation */
+RSGLDEF void RSGL_renderer_setOverflow(RSGL_renderer* renderer, RSGL_bool overflow);
+/* args clear after a draw function by default, this toggles that */
+RSGLDEF void RSGL_renderer_clearArgs(RSGL_renderer* renderer); /* clears the args */
+
+RSGLDEF RSGL_mat4 RSGL_renderer_initDrawMatrix(RSGL_renderer* renderer, RSGL_vec3D center);
+
+/* renders the current batches */
+RSGLDEF void RSGL_renderer_clear(RSGL_renderer* renderer, RSGL_color color);
+RSGLDEF void RSGL_renderer_viewport(RSGL_renderer* renderer, RSGL_rect rect);
+/* create a texture based on a given bitmap, this must be freed later using RSGL_deleteTexture or opengl*/
+RSGLDEF RSGL_texture RSGL_renderer_createTexture(RSGL_renderer* renderer, const RSGL_textureBlob* blob);
+/* updates an existing texture wiht a new bitmap */
+RSGLDEF void RSGL_renderer_copyToTexture(RSGL_renderer* renderer, RSGL_texture texture, size_t x, size_t y, const RSGL_textureBlob* blob);
+/* delete a texture */
+RSGLDEF void RSGL_renderer_deleteTexture(RSGL_renderer* renderer, RSGL_texture tex);
+RSGLDEF RSGL_framebuffer RSGL_renderer_createFramebuffer(RSGL_renderer* renderer, size_t width, size_t height);
+RSGLDEF void RSGL_renderer_attachFramebuffer(RSGL_renderer* renderer, RSGL_framebuffer fbo, RSGL_texture tex, u8 attachType, u8 mipLevel);
+RSGLDEF void RSGL_renderer_deleteFramebuffer(RSGL_renderer* renderer, RSGL_framebuffer fbo);
+/* starts scissoring */
+RSGLDEF void RSGL_renderer_scissorStart(RSGL_renderer* renderer, RSGL_rect scissor, i32 height);
+/* stops scissoring */
+RSGLDEF void RSGL_renderer_scissorEnd(RSGL_renderer* renderer);
+/* custom shader program */
+RSGLDEF RSGL_programBlob RSGL_renderer_defaultBlob(RSGL_renderer* ctx);
+RSGLDEF RSGL_programInfo RSGL_renderer_createProgram(RSGL_renderer* renderer, RSGL_programBlob* blob);
+RSGLDEF void RSGL_renderer_deleteProgram(RSGL_renderer* renderer, const RSGL_programInfo* program);
+RSGLDEF size_t RSGL_renderer_findShaderVariable(RSGL_renderer* renderer, const RSGL_programInfo* program, const char* var, size_t len);
+RSGLDEF void RSGL_renderer_updateShaderVariable(RSGL_renderer* renderer, const RSGL_programInfo* program, size_t var, const float value[], u8 len);
+RSGLDEF void RSGL_renderer_forceBatch(RSGL_renderer* renderer);
+
+RSGLDEF void RSGL_renderer_setPerspectiveMatrix(RSGL_renderer* renderer, RSGL_mat4 matrix);
+RSGLDEF void RSGL_renderer_setDefaultPerspectiveMatrix(RSGL_renderer* renderer, RSGL_mat4 matrix);
+
+RSGLDEF void RSGL_renderer_setModelMatrix(RSGL_renderer* renderer, RSGL_mat4 matrix);
+RSGLDEF void RSGL_renderer_resetModelMatrix(RSGL_renderer* renderer);
+
+RSGLDEF RSGL_programInfo RSGL_renderer_createComputeProgram(RSGL_renderer* renderer, const char *CShaderCode);
+RSGLDEF void RSGL_renderer_dispatchComputeProgram(RSGL_renderer* renderer, const RSGL_programInfo* program, u32 groups_x, u32 groups_y, u32 groups_z);
+RSGLDEF void RSGL_renderer_bindComputeTexture(RSGL_renderer* renderer, u32 texture, u8 format);
+
+/*
+*******
+RSGL_draw low level
+*******
+RSGL_drawRawVerts is a function used internally by RSGL, but you can use it yourself
+RSGL_drawRawVerts batches a given set of points based on th data to be rendered
+*/
+
+typedef enum RSGL_drawType {
+ RSGL_TRIANGLES = 0,
+ RSGL_POINTS = 1,
+ RSGL_LINES = 2
+} RSGL_drawType;
+
+typedef struct RSGL_rawVerts {
+ RSGL_drawType type;
+ float* verts;
+ float* texCoords;
+ u16* elements;
+ size_t elmCount;
+ size_t vert_count;
+} RSGL_rawVerts;
+
+RSGLDEF i32 RSGL_drawRawVerts(RSGL_renderer* renderer, const RSGL_rawVerts* data);
+
+/*
+ ******
+ * Rfont_RSGL RFont integration *
+ ******
+ * */
+
+#ifdef RSGL_RFONT
+RSGLDEF struct RFont_renderer_proc RFont_RSGL_renderer_proc(void);
+
+RSGLDEF struct RFont_renderer* RFont_RSGL_renderer_init(struct RSGL_renderer* ptr);
+RSGLDEF void RFont_RSGL_renderer_initPtr(struct RSGL_renderer* ptr, struct RFont_renderer* renderer);
+RSGLDEF void RFont_RSGL_renderer_free(struct RFont_renderer* renderer);
+#endif
+
+#endif /* ndef RSGL_H */
+
+/*
+*******
+RSGL high level API
+*******
+*/
+
+#if !defined(RSGL_NO_HIGHLEVEL) && !defined(RSGL_HIGHLEVEL_H)
+
+
+/*
+*******
+RSGL_draw primitives
+*******
+*/
+
+/* 2D shape drawing */
+/* in the function names, F means float */
+
+RSGLDEF i32 RSGL_drawPoint(RSGL_renderer* renderer, RSGL_vec2D p);
+
+RSGLDEF i32 RSGL_drawRect(RSGL_renderer* renderer, RSGL_rect r);
+
+RSGLDEF i32 RSGL_drawRoundRect(RSGL_renderer* renderer, RSGL_rect r, RSGL_vec2D rounding);
+
+RSGLDEF i32 RSGL_drawPolygon(RSGL_renderer* renderer, RSGL_rect r, u32 sides);
+
+RSGLDEF i32 RSGL_drawArc(RSGL_renderer* renderer, RSGL_rect o, RSGL_vec2D arc);
+
+RSGLDEF i32 RSGL_drawOval(RSGL_renderer* renderer, RSGL_rect o);
+
+RSGLDEF i32 RSGL_drawLine(RSGL_renderer* renderer, RSGL_vec2D p1, RSGL_vec2D p2, u32 thickness);
+
+/* 3D objects */
+RSGLDEF i32 RSGL_drawTriangle(RSGL_renderer* renderer, RSGL_vec3D[3]);
+RSGLDEF i32 RSGL_drawPoint3D(RSGL_renderer* renderer, RSGL_vec3D p);
+RSGLDEF i32 RSGL_drawLine3D(RSGL_renderer* renderer, RSGL_vec3D p1, RSGL_vec3D p2, u32 thickness);
+RSGLDEF i32 RSGL_drawCube(RSGL_renderer* renderer, RSGL_cube cube);
+
+/* 2D outlines */
+
+/* thickness means the thickness of the line */
+RSGLDEF i32 RSGL_drawTriangleOutline(RSGL_renderer* renderer, RSGL_vec3D triangle[3], u32 thickness);
+
+RSGLDEF i32 RSGL_drawRectOutline(RSGL_renderer* renderer, RSGL_rect r, u32 thickness);
+RSGLDEF i32 RSGL_drawRectOutline(RSGL_renderer* renderer, RSGL_rect r, u32 thickness);
+
+RSGLDEF i32 RSGL_drawRoundRectOutline(RSGL_renderer* renderer, RSGL_rect r, RSGL_vec2D rounding, u32 thickness);
+RSGLDEF i32 RSGL_drawRoundRectOutline(RSGL_renderer* renderer, RSGL_rect r, RSGL_vec2D rounding, u32 thickness);
+
+RSGLDEF i32 RSGL_drawPolygonOutline(RSGL_renderer* renderer, RSGL_rect r, u32 sides, u32 thickness);
+RSGLDEF i32 RSGL_drawPolygonOutline(RSGL_renderer* renderer, RSGL_rect r, u32 sides, u32 thickness);
+
+RSGLDEF i32 RSGL_drawArcOutline(RSGL_renderer* renderer, RSGL_rect o, RSGL_vec2D arc, u32 thickness);
+RSGLDEF i32 RSGL_drawArcOutline(RSGL_renderer* renderer, RSGL_rect o, RSGL_vec2D arc, u32 thickness);
+
+RSGLDEF i32 RSGL_drawOvalOutline(RSGL_renderer* renderer, RSGL_rect o, u32 thickness);
+
+/*
+*******
+RSGL_view
+*******
+*/
+
+typedef enum RSGL_viewType {
+ RSGL_viewTypeNone = 0,
+ RSGL_viewType2D,
+ RSGL_viewType3D,
+} RSGL_viewType;
+
+typedef struct RSGL_view2D {
+ RSGL_viewType type;
+ RSGL_vec3D offset;
+ RSGL_vec3D target;
+ float rotation;
+ float zoom;
+} RSGL_view2D;
+
+/* RSGL translation */
+typedef struct RSGL_view3D {
+ RSGL_viewType type;
+ RSGL_vec3D pos;
+ RSGL_vec3D target;
+ RSGL_vec3D up;
+} RSGL_view3D;
+
+typedef union RSGL_view {
+ RSGL_viewType type;
+ RSGL_view2D view2D;
+ RSGL_view3D view3D;
+} RSGL_view;
+
+RSGLDEF RSGL_mat4 RSGL_view_getMatrix(const RSGL_view* view);
+
+#endif /* ndef RSGL_HIGHLEVEL_H && ndef RSGL_NO_HIGHLEVEL */
+
+#ifdef RSGL_IMPLEMENTATION
+
+#ifndef M_PI
+ #define M_PI 3.14159265358979323846f
+#endif
+#ifndef DEG2RAD
+ #define DEG2RAD (float)(M_PI / 180.0f)
+#endif
+#ifndef RAD2DEG
+ #define RAD2DEG (float)(180.0f / M_PI)
+#endif
+
+#define RSGL_GET_MATRIX_X(x, y, z) (float)(matrix.m[0] * x + matrix.m[4] * y + matrix.m[8] * z + matrix.m[12])
+#define RSGL_GET_MATRIX_Y(x, y, z) (float)(matrix.m[1] * x + matrix.m[5] * y + matrix.m[9] * z + matrix.m[13])
+#define RSGL_GET_MATRIX_Z(x, y, z) (float)(matrix.m[2] * x + matrix.m[6] * y + matrix.m[10] * z + matrix.m[14])
+#define RSGL_GET_MATRIX_W(x, y, z) (float)(matrix.m[2] * x + matrix.m[7] * y + matrix.m[11] * z + matrix.m[15])
+
+#define RSGL_GET_MATRIX_POINT(x, y, z) \
+ RSGL_GET_MATRIX_X((x), (y), (z)) / RSGL_GET_MATRIX_W((x), (y), (z)), \
+ RSGL_GET_MATRIX_Y((x), (y), (z)) / RSGL_GET_MATRIX_W((x), (y), (z)), \
+ RSGL_GET_MATRIX_Z((x), (y), (z)) / RSGL_GET_MATRIX_W((x), (y), (z))
+
+#define RSGL_GET_MATRIX_POINTW(x, y, z) \
+ RSGL_GET_MATRIX_X(x, y, z), \
+ RSGL_GET_MATRIX_Y(x, y, z), \
+ RSGL_GET_MATRIX_Z(x, y, z), \
+ RSGL_GET_MATRIX_W(x, y, z) \
+
+void RSGL_renderer_forceBatch(RSGL_renderer* renderer) {
+ renderer->state.forceBatch = RSGL_TRUE;
+}
+
+void RSGL_renderer_setPerspectiveMatrix(RSGL_renderer* renderer, RSGL_mat4 matrix) {
+ renderer->state.perspectiveMatrix = matrix;
+}
+
+void RSGL_renderer_setDefaultPerspectiveMatrix(RSGL_renderer* renderer, RSGL_mat4 matrix) {
+ renderer->defaultPerspectiveMatrix = matrix;
+}
+
+void RSGL_renderer_setViewMatrix(RSGL_renderer* renderer, RSGL_mat4 matrix) {
+ renderer->state.viewMatrix = matrix;
+}
+void RSGL_renderer_resetViewMatrix(RSGL_renderer* renderer) {
+ RSGL_renderer_setViewMatrix(renderer, RSGL_mat4_loadIdentity());
+}
+
+void RSGL_renderer_setModelMatrix(RSGL_renderer* renderer, RSGL_mat4 matrix) {
+ renderer->state.modelMatrix = matrix;
+ renderer->state.forceBatch = RSGL_TRUE;
+}
+
+
+void RSGL_renderer_resetModelMatrix(RSGL_renderer* renderer) {
+ RSGL_renderer_setModelMatrix(renderer, RSGL_mat4_loadIdentity());
+}
+
+void RSGL_renderer_getRenderState(RSGL_renderer* renderer, RSGL_renderState* state) {
+ if (state) *state = renderer->state;
+}
+
+void RSGL_renderer_setOverflow(RSGL_renderer* renderer, RSGL_bool overflow) {
+ renderer->state.overflow = overflow;
+}
+#include <stdio.h>
+i32 RSGL_drawRawVerts(RSGL_renderer* renderer, const RSGL_rawVerts* data) {
+ if ((renderer->state.buffers->batchCount + 1 >= RSGL_MAX_BATCHES || renderer->data.len + data->vert_count >= renderer->state.buffers->maxVerts) && renderer->state.overflow) {
+ RSGL_renderer_render(renderer);
+ }
+
+ RSGL_BATCH* batch = NULL;
+ RSGL_color c = renderer->state.color;
+
+ if (
+ renderer->state.buffers->batchCount == 0 ||
+ renderer->state.buffers->batches[renderer->state.buffers->batchCount - 1].tex != renderer->state.texture ||
+ renderer->state.buffers->batches[renderer->state.buffers->batchCount - 1].lineWidth != renderer->state.lineWidth ||
+ renderer->state.buffers->batches[renderer->state.buffers->batchCount - 1].type != data->type ||
+ renderer->state.forceBatch
+ ) {
+ renderer->state.forceBatch = RSGL_FALSE;
+ renderer->state.buffers->batchCount += 1;
+
+ batch = &renderer->state.buffers->batches[renderer->state.buffers->batchCount - 1];
+ batch->start = renderer->data.len;
+ batch->len = 0;
+ batch->elmStart = renderer->data.elements_count;
+ batch->elmCount = 0;
+ batch->type = data->type;
+ batch->tex = renderer->state.texture;
+ batch->lineWidth = renderer->state.lineWidth;
+ batch->matrix = renderer->state.modelMatrix;
+ } else {
+ batch = &renderer->state.buffers->batches[renderer->state.buffers->batchCount - 1];
+ }
+
+ if (batch == NULL) {
+ return -1;
+ }
+
+ batch->elmCount += data->elmCount;
+ batch->len += data->vert_count;
+
+ RSGL_MEMCPY(&renderer->data.verts[renderer->data.len * 3], data->verts, data->vert_count * sizeof(float) * 3);
+ RSGL_MEMCPY(&renderer->data.texCoords[renderer->data.len * 2], data->texCoords, data->vert_count * sizeof(float) * 2);
+
+ size_t i;
+ for (i = 0; i < data->elmCount; i++) {
+ size_t index = renderer->data.elements_count + i;
+ u16 elm = data->elements[i] + (u16)renderer->data.len;
+ renderer->data.elements[index] = elm;
+ }
+
+ renderer->data.elements_count += data->elmCount;
+
+ float color[4] = {c.r / 255.0f, c.g / 255.0f, c.b / 255.0f, c.a / 255.0f};
+
+ if (renderer->state.gradient_len && renderer->state.gradient && (i64)(data->vert_count - 1) > 0) {
+ RSGL_MEMCPY(&renderer->data.colors[renderer->data.len * 4], color, sizeof(float) * 4);
+ RSGL_MEMCPY(&renderer->data.colors[renderer->data.len * 4 + 4], renderer->state.gradient, (data->vert_count - 1) * sizeof(float) * 4);
+ }
+ else {
+ size_t i;
+ for (i = 0; i < data->vert_count * 4; i += 4)
+ RSGL_MEMCPY(&renderer->data.colors[(renderer->data.len * 4) + i], color, sizeof(float) * 4);
+ }
+
+ renderer->data.len += data->vert_count;
+ return renderer->state.buffers->batchCount - 1;
+}
+
+/*
+*********************
+RSGL_GRAPHICS_CONTEXT
+*********************
+*/
+
+void RSGL_renderer_setSurface(RSGL_renderer* renderer, void* surface) {
+ renderer->proc.setSurface(renderer->ctx, surface);
+}
+
+void RSGL_renderer_createBuffer(RSGL_renderer* renderer, RSGL_bufferType type, size_t size, const void* data, size_t* buffer) {
+ if (renderer->proc.createBuffer) {
+ renderer->proc.createBuffer(renderer->ctx, type, size, data, buffer);
+ }
+}
+
+void RSGL_renderer_deleteRenderBuffers(RSGL_renderer* renderer, RSGL_renderBuffers* buffers) {
+ RSGL_renderer_deleteBuffer(renderer, buffers->elements);
+ RSGL_renderer_deleteBuffer(renderer, buffers->vertex);
+ RSGL_renderer_deleteBuffer(renderer, buffers->color);
+ RSGL_renderer_deleteBuffer(renderer, buffers->texture);
+}
+
+void RSGL_renderer_createRenderBuffers(RSGL_renderer* renderer, size_t size, RSGL_renderBuffers* buffers) {
+ buffers->maxVerts = size;
+ renderer->proc.createBuffer(renderer->ctx, RSGL_arrayBuffer, size * 3 * sizeof(float), NULL, &buffers->vertex);
+ renderer->proc.createBuffer(renderer->ctx, RSGL_arrayBuffer, size * 4 * sizeof(float), NULL, &buffers->color);
+ renderer->proc.createBuffer(renderer->ctx, RSGL_arrayBuffer, size * 2 * sizeof(float), NULL, &buffers->texture);
+ renderer->proc.createBuffer(renderer->ctx, RSGL_elementArrayBuffer, size * 6 * sizeof(u16), NULL, &buffers->elements);
+}
+
+void RSGL_renderer_updateBuffer(RSGL_renderer* renderer, RSGL_bufferType type, size_t buffer, void* data, size_t start, size_t len) {
+ if (renderer->proc.updateBuffer)
+ renderer->proc.updateBuffer(renderer->ctx, type, buffer, data, start, len);
+}
+
+void RSGL_renderer_deleteBuffer(RSGL_renderer* renderer, size_t buffer) {
+ if (renderer->proc.deleteBuffer)
+ renderer->proc.deleteBuffer(renderer->ctx, buffer);
+}
+
+void RSGL_renderer_updateRenderBuffers(RSGL_renderer* renderer) {
+ RSGL_renderer_updateBuffer(renderer, RSGL_arrayBuffer, renderer->state.buffers->vertex, renderer->data.verts, 0, renderer->data.len * 3 * sizeof(float));
+ RSGL_renderer_updateBuffer(renderer, RSGL_arrayBuffer, renderer->state.buffers->color, renderer->data.colors, 0, renderer->data.len * 4 * sizeof(float));
+ RSGL_renderer_updateBuffer(renderer, RSGL_arrayBuffer, renderer->state.buffers->texture, renderer->data.texCoords, 0, renderer->data.len * 2 * sizeof(float));
+ RSGL_renderer_updateBuffer(renderer, RSGL_elementArrayBuffer, renderer->state.buffers->elements, renderer->data.elements, 0, renderer->data.elements_count * sizeof(u16));
+}
+
+void RSGL_renderer_renderBuffers(RSGL_renderer* renderer) {
+ RSGL_mat4 matrix = RSGL_mat4_multiply(renderer->defaultPerspectiveMatrix.m, renderer->state.perspectiveMatrix.m);
+ matrix = RSGL_mat4_multiply(matrix.m, renderer->state.viewMatrix.m);
+
+
+ RSGL_renderPass pass;
+ pass.program = renderer->state.program;
+ pass.matrix = matrix.m;
+ pass.buffers = renderer->state.buffers;
+ pass.framebuffer = renderer->state.framebuffer;
+
+ if (renderer->proc.render)
+ renderer->proc.render(renderer->ctx, &pass);
+
+ renderer->data.len = 0;
+ renderer->data.elements_count = 0;
+}
+
+void RSGL_renderer_render(RSGL_renderer* renderer) {
+ if (renderer->data.len && renderer->state.buffers->batchCount) {
+ RSGL_renderer_updateRenderBuffers(renderer);
+ }
+
+ RSGL_renderer_renderBuffers(renderer);
+
+ renderer->state.buffers->batchCount = 0;
+}
+
+size_t RSGL_renderer_size(RSGL_renderer* renderer) {
+ if (renderer->proc.size) return renderer->proc.size();
+ return 0;
+}
+
+void RSGL_renderer_initPtr(RSGL_rendererProc proc, void* loader, void* data, RSGL_renderer* renderer) {
+ renderer->ctx = data;
+ renderer->proc = proc;
+ RSGL_renderer_clearArgs(renderer);
+ renderer->state.color = RSGL_RGBA(0, 0, 0, 255);
+
+ renderer->state.modelMatrix = RSGL_mat4_loadIdentity();
+ renderer->data.verts = renderer->verts;
+ renderer->data.texCoords = renderer->texCoords;
+ renderer->data.elements = renderer->elements;
+ renderer->data.colors = renderer->colors;
+ renderer->data.len = 0;
+ renderer->data.elements_count = 0;
+
+ if (renderer->proc.initPtr) {
+ renderer->proc.initPtr(renderer->ctx, loader);
+ }
+
+ RSGL_renderer_setFramebuffer(renderer, 0);
+
+ RSGL_programBlob pBlob = RSGL_renderer_defaultBlob(renderer);
+ renderer->defaultProgram = RSGL_renderer_createProgram(renderer, &pBlob);
+ RSGL_renderer_setProgram(renderer, &renderer->defaultProgram);
+
+ u8 white[4] = {255, 255, 255, 255};
+ RSGL_textureBlob blob;
+ blob.data = white;
+ blob.width = 1;
+ blob.height = 1;
+ blob.dataType = RSGL_textureDataInt;
+ blob.dataFormat = RSGL_formatRGBA;
+ blob.textureFormat = RSGL_formatRGBA;
+ renderer->defaultTexture = RSGL_renderer_createTexture(renderer, &blob);
+
+ RSGL_renderer_setTexture(renderer, renderer->defaultTexture);
+
+ RSGL_renderer_createRenderBuffers(renderer, RSGL_MAX_VERTS, &renderer->buffers);
+ renderer->buffers.batchCount = 0;
+ renderer->state.buffers = &renderer->buffers;
+
+ RSGL_renderer_setModelMatrix(renderer, RSGL_mat4_loadIdentity());
+ RSGL_renderer_resetViewMatrix(renderer);
+ RSGL_renderer_setPerspectiveMatrix(renderer, RSGL_mat4_loadIdentity());
+}
+
+
+RSGL_renderer* RSGL_renderer_init(RSGL_rendererProc proc, void* loader) {
+ RSGL_renderer* renderer = (RSGL_renderer*)RSGL_MALLOC(sizeof(RSGL_renderer));
+ void* data = RSGL_MALLOC(proc.size());
+ RSGL_renderer_initPtr(proc, loader, data, renderer);
+ return renderer;
+}
+
+void RSGL_renderer_freePtr(RSGL_renderer* renderer) {
+ RSGL_renderer_deleteRenderBuffers(renderer, &renderer->buffers);
+
+ if (renderer->proc.freePtr)
+ renderer->proc.freePtr(renderer->ctx);
+
+ renderer->state.buffers->batchCount = 0;
+ renderer->data.len = 0;
+ renderer->data.elements_count = 0;
+}
+
+void RSGL_renderer_free(RSGL_renderer* renderer) {
+ RSGL_renderer_freePtr(renderer);
+ if (renderer->ctx)
+ RSGL_FREE(renderer->ctx);
+ RSGL_FREE(renderer);
+}
+
+void RSGL_renderer_clear(RSGL_renderer* renderer, RSGL_color color) {
+ if (renderer->proc.clear)
+ renderer->proc.clear(renderer->ctx, renderer->state.framebuffer, ((float)color.r) / 255.0f, ((float)color.g) / 255.0f, ((float)color.b) / 255.0f, ((float)color.a) / 255.0f);
+}
+void RSGL_renderer_viewport(RSGL_renderer* renderer, RSGL_rect rect) { renderer->proc.viewport(renderer->ctx, rect.x, rect.y, rect.w, rect.h); }
+RSGL_texture RSGL_renderer_createTexture(RSGL_renderer* renderer, const RSGL_textureBlob* blob) {
+ RSGL_texture tex = 0;
+ if (renderer->proc.createTexture) tex = renderer->proc.createTexture(renderer->ctx, blob);
+ return tex;
+}
+void RSGL_renderer_copyToTexture(RSGL_renderer* renderer, RSGL_texture texture, size_t x, size_t y, const RSGL_textureBlob* blob) {
+ return renderer->proc.copyToTexture(renderer->ctx, texture, x, y, blob);
+}
+void RSGL_renderer_deleteTexture(RSGL_renderer* renderer, RSGL_texture tex) { renderer->proc.deleteTexture(renderer->ctx, tex); }
+void RSGL_renderer_scissorStart(RSGL_renderer* renderer, RSGL_rect scissor, i32 height) {
+ renderer->proc.scissorStart(renderer->ctx, scissor.x, scissor.y, scissor.w, scissor.h, height);
+}
+
+RSGL_framebuffer RSGL_renderer_createFramebuffer(RSGL_renderer* renderer, size_t width, size_t height) {
+ RSGL_framebuffer framebuffer = 0;
+ if (renderer->proc.createFramebuffer) {
+ framebuffer = renderer->proc.createFramebuffer(renderer->ctx, width, height);
+ }
+ return framebuffer;
+}
+
+void RSGL_renderer_attachFramebuffer(RSGL_renderer* renderer, RSGL_framebuffer fbo, RSGL_texture tex, u8 attachType, u8 mipLevel) {
+ if (renderer->proc.attachFramebuffer)
+ renderer->proc.attachFramebuffer(renderer->ctx, fbo, tex, attachType, mipLevel);
+}
+
+void RSGL_renderer_deleteFramebuffer(RSGL_renderer* renderer, RSGL_framebuffer fbo) {
+ if (renderer->proc.deleteFramebuffer)
+ renderer->proc.deleteFramebuffer(renderer->ctx, fbo);
+}
+
+void RSGL_renderer_scissorEnd(RSGL_renderer* renderer) {
+ renderer->proc.scissorEnd(renderer->ctx);
+}
+RSGL_programBlob RSGL_renderer_defaultBlob(RSGL_renderer* renderer) {
+ RSGL_programBlob blob;
+ RSGL_MEMSET(&blob, 0, sizeof(blob));
+
+ if (renderer->proc.defaultBlob) {
+ blob = renderer->proc.defaultBlob(renderer);
+ }
+
+ return blob;
+}
+RSGL_programInfo RSGL_renderer_createProgram(RSGL_renderer* renderer, RSGL_programBlob* blob) {
+ RSGL_programInfo info;
+ RSGL_MEMSET(&info, 0, sizeof(info));
+
+ if (blob->fragment == NULL || blob->vertex == NULL) {
+ RSGL_programBlob pBlob = RSGL_renderer_defaultBlob(renderer);
+ renderer->defaultProgram = RSGL_renderer_createProgram(renderer, &pBlob);
+
+ if (blob->vertex == NULL) {
+ blob->vertex = pBlob.vertex;
+ blob->vertexLen = pBlob.vertexLen;
+ }
+
+ if (blob->fragment == NULL) {
+ blob->fragment = pBlob.fragment;
+ blob->fragmentLen = pBlob.fragmentLen;
+ }
+ }
+
+ if (renderer->proc.createProgram) {
+ info = renderer->proc.createProgram(renderer->ctx, blob);
+ }
+
+ return info;
+}
+void RSGL_renderer_deleteProgram(RSGL_renderer* renderer, const RSGL_programInfo* program) { return renderer->proc.deleteProgram(renderer->ctx, program); }
+
+size_t RSGL_renderer_findShaderVariable(RSGL_renderer* renderer, const RSGL_programInfo* program, const char* var, size_t len) {
+ return renderer->proc.findShaderVariable(renderer->ctx, program, var, len);
+}
+
+void RSGL_renderer_updateShaderVariable(RSGL_renderer* renderer, const RSGL_programInfo* program, size_t var, const float value[], u8 len) {
+ if (renderer->proc.updateShaderVariable) {
+ renderer->proc.updateShaderVariable(renderer->ctx, program, var, value, len);
+ }
+}
+
+RSGL_programInfo RSGL_renderer_createComputeProgram(RSGL_renderer* renderer, const char* CShaderCode) {
+ return renderer->proc.createComputeProgram(renderer->ctx, CShaderCode);
+}
+
+void RSGL_renderer_dispatchComputeProgram(RSGL_renderer* renderer, const RSGL_programInfo* program, u32 groups_x, u32 groups_y, u32 groups_z) {
+ renderer->proc.dispatchComputeProgram(renderer->ctx, program, groups_x, groups_y, groups_z);
+}
+
+void RSGL_renderer_bindComputeTexture(RSGL_renderer* renderer, u32 texture, u8 format) {
+ renderer->proc.bindComputeTexture(renderer->ctx, texture, format);
+}
+
+void RSGL_renderer_updateSize(RSGL_renderer* renderer, size_t width, size_t height) {
+ RSGL_projection projection;
+ projection.p2D.type = RSGL_projectionOrtho2D;
+ projection.p2D.width = width;
+ projection.p2D.height = height;
+
+ RSGL_mat4 matrix = RSGL_projection_getMatrix(&projection);
+ RSGL_renderer_setDefaultPerspectiveMatrix(renderer, matrix);
+}
+
+void RSGL_renderer_setRotate(RSGL_renderer* renderer, RSGL_vec3D rotate){
+ renderer->state.rotate = RSGL_VEC3D(rotate.x * DEG2RAD, rotate.y * DEG2RAD, rotate.z * DEG2RAD);
+}
+void RSGL_renderer_setTexture(RSGL_renderer* renderer, RSGL_texture texture) {
+ if (texture == 0)
+ renderer->state.texture = renderer->defaultTexture;
+ else
+ renderer->state.texture = texture;
+
+ renderer->state.source = RSGL_RECT(0, 0, 1, 1);
+}
+
+void RSGL_renderer_setTextureSource(RSGL_renderer* renderer, RSGL_texture texture, RSGL_rect rect) {
+ RSGL_renderer_setTexture(renderer, texture);
+ renderer->state.source = rect;
+}
+
+void RSGL_renderer_setColor(RSGL_renderer* renderer, RSGL_color color) {
+ renderer->state.color = color;
+}
+
+void RSGL_renderer_setRenderBuffers(RSGL_renderer* renderer, RSGL_renderBuffers* buffers) {
+ if (buffers == NULL)
+ renderer->state.buffers = &renderer->buffers;
+ else
+ renderer->state.buffers = buffers;
+}
+
+void RSGL_renderer_setProgram(RSGL_renderer* renderer, RSGL_programInfo* program) {
+ if (program == NULL)
+ renderer->state.program = &renderer->defaultProgram;
+ else
+ renderer->state.program = program;
+}
+
+void RSGL_renderer_setFramebuffer(RSGL_renderer* renderer, RSGL_framebuffer framebuffer) {
+ renderer->state.framebuffer = framebuffer;
+}
+
+void RSGL_renderer_setGradient(RSGL_renderer* renderer, float gradient[], size_t len) {
+ renderer->state.gradient_len = len;
+ renderer->state.gradient = gradient;
+}
+void RSGL_renderer_setCenter(RSGL_renderer* renderer, RSGL_vec3D center) {
+ renderer->state.center = center;
+}
+
+RSGL_mat4 RSGL_renderer_initDrawMatrix(RSGL_renderer* renderer, RSGL_vec3D center) {
+ RSGL_mat4 matrix = RSGL_mat4_loadIdentity();
+
+ if (renderer->state.rotate.x || renderer->state.rotate.y || renderer->state.rotate.z) {
+ if (renderer->state.center.x != -1 && renderer->state.center.y != -1 && renderer->state.center.z != -1)
+ center = renderer->state.center;
+
+ matrix = RSGL_mat4_translate(matrix.m, center.x, center.y, center.z);
+ matrix = RSGL_mat4_rotate(matrix.m, renderer->state.rotate.z, 0, 0, 1);
+ matrix = RSGL_mat4_rotate(matrix.m, renderer->state.rotate.y, 0, 1, 0);
+ matrix = RSGL_mat4_rotate(matrix.m, renderer->state.rotate.x, 1, 0, 0);
+ matrix = RSGL_mat4_translate(matrix.m, -center.x, -center.y, -center.z);
+ }
+
+ return matrix;
+}
+
+void RSGL_renderer_clearArgs(RSGL_renderer* renderer) {
+ RSGL_MEMSET(&renderer->state, 0, sizeof(renderer->state));
+ renderer->state.center = RSGL_VEC3D(-1, -1, -1);
+ renderer->state.overflow = RSGL_TRUE;
+}
+
+/*
+****
+RFont_RSGL integration
+****
+*/
+
+#ifdef RSGL_RFONT
+
+#ifndef RFONT_H
+
+#ifdef RSGL_INT_DEFINED
+ #ifndef RFONT_INT_DEFINED
+ #define RFONT_INT_DEFINED
+ #endif
+#endif
+
+#include "RFont.h"
+#endif
+
+struct RFont_renderer* RFont_RSGL_renderer_init(struct RSGL_renderer* ptr) {
+ RFont_renderer* renderer = (RFont_renderer*)RFONT_MALLOC(sizeof(RFont_renderer));
+ RFont_renderer_initPtr(RFont_RSGL_renderer_proc(), ptr, renderer);
+ return renderer;
+}
+void RFont_RSGL_renderer_initPtr(struct RSGL_renderer* ptr, struct RFont_renderer* renderer) { RFont_renderer_initPtr(RFont_RSGL_renderer_proc(), ptr, renderer); }
+
+void RFont_RSGL_renderer_free(struct RFont_renderer* renderer) {
+ RFONT_FREE(renderer);
+}
+
+void RFont_RSGL_render_text(RSGL_renderer* renderer, const RFont_render_data* src) {
+ RSGL_texture save = renderer->state.texture;
+ RSGL_renderer_setTexture(renderer, src->atlas);
+
+ RSGL_rawVerts data;
+ data.type = RSGL_TRIANGLES;
+ data.verts = src->verts;
+ data.texCoords = src->tcoords;
+ data.elements = src->elements;
+ data.elmCount = src->nelements;
+ data.vert_count = src->nverts;
+ i32 batch = RSGL_drawRawVerts(renderer, &data);
+ RSGL_UNUSED(batch);
+
+ RSGL_renderer_setTexture(renderer, save);
+}
+
+RFont_texture RFont_RSGL_createAtlas(RSGL_renderer* renderer, u32 atlasWidth, u32 atlasHeight) {
+ RSGL_textureBlob blob;
+ blob.data = NULL;
+ blob.width = atlasWidth;
+ blob.height = atlasWidth;
+ blob.dataType = RSGL_textureDataInt;
+ blob.dataFormat = RSGL_formatRGBA; //RSGL_formatRGBA;
+ blob.textureFormat = RSGL_formatRGBA;//RSGL_formatRGBA;
+ RFont_texture id = RSGL_renderer_createTexture(renderer, &blob);
+ return id;
+}
+
+void RFont_RSGL_deleteAtlas(RSGL_renderer* renderer, RFont_texture atlas) {
+ RSGL_renderer_deleteTexture(renderer, atlas);
+}
+
+void RFont_RSGL_bitmapToAtlas(RSGL_renderer* renderer, RFont_texture atlas, u32 atlasWidth, u32 atlasHeight, u32 maxHeight, u8* bitmap, float w, float h, float* x, float* y) {
+ RSGL_UNUSED(atlasHeight);
+ if (((*x) + w) >= atlasWidth) {
+ *x = 0;
+ *y += (float)maxHeight;
+ }
+
+ RSGL_textureBlob blob;
+ blob.width = w;
+ blob.height = h;
+ blob.dataType = RSGL_textureDataInt;
+ blob.dataFormat = RSGL_formatRGBA;
+ blob.textureFormat = blob.dataFormat;
+
+ u8* newBitmap = (u8*)RSGL_MALLOC(w * h * 4);
+
+ for (size_t indexY = 0; indexY < (size_t)h; indexY++) {
+ for (size_t indexX = 0; indexX < (size_t)w; indexX++) {
+ size_t index = ((indexY * (size_t)w * 4) + indexX * 4);
+ size_t oIndex = ((indexY * (size_t)w) + indexX);
+
+ u8 value = bitmap[oIndex];
+
+ newBitmap[index + 0] = value;
+ newBitmap[index + 1] = value;
+ newBitmap[index + 2] = value;
+ newBitmap[index + 3] = value;
+ }
+ }
+
+ blob.data = newBitmap;
+
+ RSGL_renderer_copyToTexture(renderer, atlas, (size_t)(*x), (size_t)(*y), &blob);
+
+ RSGL_FREE(newBitmap);
+
+ *x += w;
+}
+
+void RFont_RSGL_setFrameBuffer(RSGL_renderer* renderer, u32 width, u32 height) {
+ RSGL_renderer_updateSize(renderer, width, height);
+}
+
+void RFont_RSGL_setColor(RSGL_renderer* renderer, float r, float g, float b, float a) {
+ RSGL_renderer_setColor(renderer, RSGL_RGBA(r * 255.0f, g * 255.0f, b * 255.0f, a * 255.0f));
+}
+
+RFont_renderer_proc RFont_RSGL_renderer_proc(void) {
+ RFont_renderer_proc proc;
+ RSGL_MEMSET(&proc, 0, sizeof(proc));
+ proc.create_atlas = (RFont_texture (*)(void* ctx, u32 atlasWidth, u32 atlasHeight))RFont_RSGL_createAtlas;
+ proc.free_atlas = (void (*)(void*, RSGL_texture))RFont_RSGL_deleteAtlas;
+ proc.bitmap_to_atlas = (void(*)(void*, RFont_texture, u32, u32, u32, u8*, float, float, float*, float*))RFont_RSGL_bitmapToAtlas;
+ proc.render = (void (*)(void*, const RFont_render_data* data))RFont_RSGL_render_text;
+ proc.set_framebuffer = (void (*)(void*, u32, u32))RFont_RSGL_setFrameBuffer;
+ proc.set_color = (void (*)(void*, float, float, float, float))RFont_RSGL_setColor;
+ return proc;
+}
+
+#endif
+
+#if !defined(RSGL_NO_HIGHLEVEL)
+
+/*
+****
+RSGL_draw
+****
+*/
+
+i32 RSGL_drawPoint(RSGL_renderer* renderer, RSGL_vec2D p) {
+ RSGL_mat4 matrix = RSGL_renderer_initDrawMatrix(renderer, RSGL_VEC3D(p.x, p.y, 0.0f));
+
+ float points[] = {RSGL_GET_MATRIX_POINT((float)p.x, (float)p.y, 0.0f)};
+ float texPoints[] = { renderer->state.source.x, renderer->state.source.y };
+ u16 elements[] = { 0 };
+
+ RSGL_rawVerts data;
+ data.type = RSGL_POINTS;
+ data.verts = points;
+ data.texCoords = texPoints;
+ data.elements = elements;
+ data.elmCount = 1;
+ data.vert_count = 1;
+
+ return RSGL_drawRawVerts(renderer, &data);
+}
+
+i32 RSGL_drawRect(RSGL_renderer* renderer, RSGL_rect r) {
+ float texPoints[] = {
+ renderer->state.source.x, renderer->state.source.y,
+ renderer->state.source.x, renderer->state.source.y + renderer->state.source.h,
+ renderer->state.source.x + renderer->state.source.w, renderer->state.source.y,
+ renderer->state.source.x + renderer->state.source.w, renderer->state.source.y + renderer->state.source.h,
+ };
+
+ RSGL_vec3D center = (RSGL_vec3D){r.x + (r.w / 2.0f), r.y + (r.h / 2.0f), 0.0f};
+ RSGL_mat4 matrix = RSGL_renderer_initDrawMatrix(renderer, center);
+
+ float points[] = {
+ RSGL_GET_MATRIX_POINT(r.x, r.y, 0.0f),
+ RSGL_GET_MATRIX_POINT(r.x, r.y + r.h, 0.0f),
+ RSGL_GET_MATRIX_POINT(r.x + r.w, r.y, 0.0f),
+ RSGL_GET_MATRIX_POINT(r.x + r.w, r.y + r.h, 0.0f),
+ };
+
+ u16 elements[] = {
+ 0, 1, 2,
+ 3, 2, 1
+ };
+
+ RSGL_rawVerts data;
+ data.type = RSGL_TRIANGLES;
+ data.verts = points;
+ data.texCoords = texPoints;
+ data.elements = elements;
+ data.elmCount = 6;
+ data.vert_count = 4;
+
+ return RSGL_drawRawVerts(renderer, &data);
+}
+
+i32 RSGL_drawRoundRect(RSGL_renderer* renderer, RSGL_rect r, RSGL_vec2D rounding) {
+ RSGL_drawRect(renderer, RSGL_RECT(r.x + (rounding.x / 2), r.y, r.w - rounding.x, r.h));
+ RSGL_drawRect(renderer, RSGL_RECT(r.x, r.y + (rounding.y / 2), r.w, r.h - rounding.y));
+
+ RSGL_drawArc(renderer, RSGL_RECT(r.x, r.y, rounding.x, rounding.y), (RSGL_vec2D){180, 270});
+ RSGL_drawArc(renderer, RSGL_RECT(r.x + (r.w - rounding.x), r.y, rounding.x, rounding.y), (RSGL_vec2D){90, 180});
+ RSGL_drawArc(renderer, RSGL_RECT(r.x + (r.w - rounding.x), r.y + (r.h - rounding.y), rounding.x, rounding.y), (RSGL_vec2D){0, 90});
+ return RSGL_drawArc(renderer, RSGL_RECT(r.x, r.y + (r.h - rounding.y), rounding.x, rounding.y), (RSGL_vec2D){270, 360});
+}
+
+i32 RSGL_drawPolygonOutlineEx(RSGL_renderer* renderer, RSGL_rect o, u32 sides, RSGL_vec2D arc);
+
+i32 RSGL_drawPolygonEx(RSGL_renderer* renderer, RSGL_rect o, u32 sides, RSGL_vec2D arc) {
+ static float verts[360 * 3];
+ static float texcoords[360 * 2];
+ static u16 elements[360 * 6];
+
+ RSGL_vec3D center = (RSGL_vec3D){o.x + (o.w / 2.0f), o.y + (o.h / 2.0f), 0};
+
+ o = (RSGL_rect){o.x, o.y, o.w / 2, o.h / 2};
+ RSGL_mat4 matrix = RSGL_renderer_initDrawMatrix(renderer, center);
+
+ float displacement = 360.0f / (float)sides;
+ float angle = displacement * arc.x;
+
+ size_t vIndex = 0;
+ size_t tIndex = 0;
+ size_t iIndex = 0;
+
+ float texCenterX = (0.5 * (renderer->state.source.w));
+ float texCenterY = (0.5 * (renderer->state.source.h));
+
+ RSGL_UNUSED(texCenterX);
+ RSGL_UNUSED(texCenterY);
+
+ {
+ RSGL_vec2D p = {center.x, center.y};
+
+ texcoords[tIndex] = (renderer->state.source.x) + texCenterX;
+ texcoords[tIndex + 1] = (renderer->state.source.y) + texCenterY;
+ float temp[3] = { RSGL_GET_MATRIX_POINT(p.x, p.y, 0.0) };
+ memcpy(verts + vIndex, temp, 3 * sizeof(float));
+
+ angle += displacement;
+ tIndex += 2;
+ vIndex += 3;
+ }
+
+ u32 i;
+ for (i = 0; i < sides + 1; i++) {
+ RSGL_vec2D p = {RSGL_COS(angle * DEG2RAD), RSGL_SIN(angle * DEG2RAD)};
+
+ texcoords[tIndex] = ((p.x + 1.0f) * texCenterX) + (renderer->state.source.x) ;
+ texcoords[tIndex + 1] = ((p.y + 1.0f) * texCenterY) + (renderer->state.source.y);
+
+ float temp[3] = { RSGL_GET_MATRIX_POINT(o.x + o.w + (p.x * o.w), o.y + o.h + (p.y * o.h), 0.0) };
+ memcpy(&verts[vIndex], temp, 3 * sizeof(float));
+
+ elements[iIndex + 0] = i;
+
+ if (i < sides)
+ elements[iIndex + 1] = i + 1;
+ else
+ elements[iIndex + 1] = 1;
+
+ elements[iIndex + 2] = 0;
+
+ angle += displacement;
+ tIndex += 2;
+ vIndex += 3;
+ iIndex += 3;
+ }
+
+ RSGL_rawVerts data;
+ data.type = RSGL_TRIANGLES;
+ data.verts = verts;
+ data.texCoords = texcoords;
+ data.elements = elements;
+ data.elmCount = iIndex;
+ data.vert_count = (vIndex / 3);
+
+ i32 out = RSGL_drawRawVerts(renderer, &data);
+ return out;
+}
+
+i32 RSGL_drawPolygon(RSGL_renderer* renderer, RSGL_rect o, u32 sides) { return RSGL_drawPolygonEx(renderer, o, sides, (RSGL_vec2D){0, (float)sides}); }
+
+
+i32 RSGL_drawArc(RSGL_renderer* renderer, RSGL_rect o, RSGL_vec2D arc) {
+ u32 verts = (u32)((float)((2 * M_PI * ((o.w + o.h) / 2.0f)) / 10) + 0.5);
+ verts %= 360;
+
+ return RSGL_drawPolygonEx(renderer, o, verts, arc);
+}
+
+i32 RSGL_drawOval(RSGL_renderer* renderer, RSGL_rect o) {
+ float verts = ((2 * M_PI * ((o.w + o.h) / 2.0f)) / 10);
+ verts = (verts > 360 ? 360 : verts);
+
+ return RSGL_drawPolygonEx(renderer, o, verts, (RSGL_vec2D){0, verts});
+}
+
+/*
+ 3D
+*/
+
+i32 RSGL_drawPoint3D(RSGL_renderer* renderer, RSGL_vec3D p) {
+ RSGL_mat4 matrix = RSGL_renderer_initDrawMatrix(renderer, p);
+
+ float points[] = {RSGL_GET_MATRIX_POINT((float)p.x, (float)p.y, (float)p.z)};
+ float texPoints[] = { renderer->state.source.x, renderer->state.source.y };
+ u16 elements[] = { 0, };
+
+ RSGL_rawVerts data;
+ data.type = RSGL_POINTS;
+ data.verts = points;
+ data.texCoords = texPoints;
+ data.elements = elements;
+ data.elmCount = 1;
+ data.vert_count = 1;
+
+ return RSGL_drawRawVerts(renderer, &data);
+}
+
+i32 RSGL_drawLine3D(RSGL_renderer* renderer, RSGL_vec3D p1, RSGL_vec3D p2, u32 thickness) {
+ renderer->state.lineWidth = thickness;
+
+ RSGL_vec3D center = {(p1.x + p2.x) / 2.0f, (p1.y + p2.y) / 2.0f, (p1.z + p2.z) / 2.0f};
+ RSGL_mat4 matrix = RSGL_renderer_initDrawMatrix(renderer, center);
+
+ float points[] = {RSGL_GET_MATRIX_POINT(p1.x, p1.y, p1.z), RSGL_GET_MATRIX_POINT(p2.x, p2.y, p2.z)};
+ float texPoints[] = { renderer->state.source.x, renderer->state.source.y, renderer->state.source.x + renderer->state.source.w, renderer->state.source.y + renderer->state.source.h};
+ u16 elements[] = { 0, 1 };
+
+ RSGL_rawVerts data;
+ data.type = RSGL_LINES;
+ data.verts = points;
+ data.texCoords = texPoints;
+ data.elements = elements;
+ data.elmCount = 4;
+ data.vert_count = 2;
+
+ return RSGL_drawRawVerts(renderer, &data);
+}
+
+i32 RSGL_drawTriangle(RSGL_renderer* renderer, RSGL_vec3D t[3]) {
+ RSGL_vec3D center = {t[2].x, (t[2].y + t[0].y) / 2.0f, t[1].z};
+ RSGL_mat4 matrix = RSGL_renderer_initDrawMatrix(renderer, center);
+
+ float points[] = {
+ RSGL_GET_MATRIX_POINT(t[0].x, t[0].y, t[0].z),
+ RSGL_GET_MATRIX_POINT(t[1].x, t[1].y, t[1].z),
+ RSGL_GET_MATRIX_POINT(t[2].x, t[2].y, t[2].z)
+ };
+
+ float texPoints[] = {
+ renderer->state.source.x, renderer->state.source.y + renderer->state.source.h,
+ renderer->state.source.x + renderer->state.source.w, renderer->state.source.y + renderer->state.source.h,
+ ((float)(t[2].x - t[0].x)/t[1].x < 1) ? (renderer->state.source.x + (float)(t[2].x - t[0].x) / t[1].x) : renderer->state.source.x, renderer->state.source.y,
+ };
+
+ u16 elements[] = {
+ 0, 1, 2,
+ };
+
+ RSGL_rawVerts data;
+ data.type = RSGL_TRIANGLES;
+ data.verts = points;
+ data.texCoords = texPoints;
+ data.elements = elements;
+ data.elmCount = 3;
+ data.vert_count = 3;
+
+ return RSGL_drawRawVerts(renderer, &data);
+}
+
+i32 RSGL_drawCube(RSGL_renderer* renderer, RSGL_cube cube) {
+ float texPoints[] = {
+ renderer->state.source.x, renderer->state.source.y,
+ renderer->state.source.x, renderer->state.source.y + renderer->state.source.h,
+ renderer->state.source.x + renderer->state.source.w, renderer->state.source.y,
+ renderer->state.source.x + renderer->state.source.w, renderer->state.source.y + renderer->state.source.h,
+
+ renderer->state.source.x, renderer->state.source.y,
+ renderer->state.source.x, renderer->state.source.y + renderer->state.source.h,
+ renderer->state.source.x + renderer->state.source.w, renderer->state.source.y,
+ renderer->state.source.x + renderer->state.source.w, renderer->state.source.y + renderer->state.source.h,
+
+ renderer->state.source.x, renderer->state.source.y,
+ renderer->state.source.x, renderer->state.source.y + renderer->state.source.h,
+ renderer->state.source.x + renderer->state.source.w, renderer->state.source.y,
+ renderer->state.source.x + renderer->state.source.w, renderer->state.source.y + renderer->state.source.h,
+
+ renderer->state.source.x, renderer->state.source.y,
+ renderer->state.source.x, renderer->state.source.y + renderer->state.source.h,
+ renderer->state.source.x + renderer->state.source.w, renderer->state.source.y,
+ renderer->state.source.x + renderer->state.source.w, renderer->state.source.y + renderer->state.source.h,
+
+ renderer->state.source.x, renderer->state.source.y,
+ renderer->state.source.x, renderer->state.source.y + renderer->state.source.h,
+ renderer->state.source.x + renderer->state.source.w, renderer->state.source.y,
+ renderer->state.source.x + renderer->state.source.w, renderer->state.source.y + renderer->state.source.h,
+
+ renderer->state.source.x, renderer->state.source.y,
+ renderer->state.source.x, renderer->state.source.y + renderer->state.source.h,
+ renderer->state.source.x + renderer->state.source.w, renderer->state.source.y,
+ renderer->state.source.x + renderer->state.source.w, renderer->state.source.y + renderer->state.source.h,
+ };
+
+ RSGL_vec3D center = {
+ cube.x + (cube.w / 2.0f),
+ cube.y + (cube.h / 2.0f),
+ cube.z
+ };
+
+ RSGL_mat4 matrix = RSGL_renderer_initDrawMatrix(renderer, center);
+
+ float points[] = {
+ // Front face
+ RSGL_GET_MATRIX_POINT(cube.x, cube.y, cube.z),
+ RSGL_GET_MATRIX_POINT(cube.x, cube.y + cube.h, cube.z),
+ RSGL_GET_MATRIX_POINT(cube.x + cube.w, cube.y, cube.z),
+ RSGL_GET_MATRIX_POINT(cube.x + cube.w, cube.y + cube.h, cube.z),
+ // Back face
+ RSGL_GET_MATRIX_POINT(cube.x + cube.w, cube.y, cube.z + cube.l),
+ RSGL_GET_MATRIX_POINT(cube.x + cube.w, cube.y + cube.h, cube.z + cube.l),
+ RSGL_GET_MATRIX_POINT(cube.x, cube.y, cube.z + cube.l),
+ RSGL_GET_MATRIX_POINT(cube.x, cube.y + cube.h, cube.z + cube.l),
+ // Left face
+ RSGL_GET_MATRIX_POINT(cube.x, cube.y, cube.z + cube.l),
+ RSGL_GET_MATRIX_POINT(cube.x, cube.y + cube.h, cube.z + cube.l),
+ RSGL_GET_MATRIX_POINT(cube.x, cube.y, cube.z),
+ RSGL_GET_MATRIX_POINT(cube.x, cube.y + cube.h, cube.z),
+ // Right face
+ RSGL_GET_MATRIX_POINT(cube.x + cube.w, cube.y, cube.z),
+ RSGL_GET_MATRIX_POINT(cube.x + cube.w, cube.y + cube.h, cube.z),
+ RSGL_GET_MATRIX_POINT(cube.x + cube.w, cube.y, cube.z + cube.l),
+ RSGL_GET_MATRIX_POINT(cube.x + cube.w, cube.y + cube.h, cube.z + cube.l),
+ // Top face
+ RSGL_GET_MATRIX_POINT(cube.x, cube.y + cube.h, cube.z),
+ RSGL_GET_MATRIX_POINT(cube.x, cube.y + cube.h, cube.z + cube.l),
+ RSGL_GET_MATRIX_POINT(cube.x + cube.w, cube.y + cube.h, cube.z),
+ RSGL_GET_MATRIX_POINT(cube.x + cube.w, cube.y + cube.h, cube.z + cube.l),
+ // Bottom face
+ RSGL_GET_MATRIX_POINT(cube.x, cube.y, cube.z + cube.l),
+ RSGL_GET_MATRIX_POINT(cube.x, cube.y, cube.z),
+ RSGL_GET_MATRIX_POINT(cube.x + cube.w, cube.y, cube.z + cube.l),
+ RSGL_GET_MATRIX_POINT(cube.x + cube.w, cube.y, cube.z),
+ };
+
+ u16 elements[] = {
+ 0, 1, 2,
+ 3, 2, 1,
+
+ 4, 5, 6,
+ 7, 6, 5,
+
+ 8, 9, 10,
+ 11, 10, 9,
+
+ 12, 13, 14,
+ 15, 14, 13,
+
+ 16, 17, 18,
+ 19, 18, 17,
+
+ 20, 21, 22,
+ 23, 21, 22,
+ };
+
+ RSGL_rawVerts data;
+ data.type = RSGL_TRIANGLES;
+ data.verts = points;
+ data.texCoords = texPoints;
+ data.elements = elements;
+ data.elmCount = sizeof(elements) / sizeof(u16);
+ data.vert_count = sizeof(points) / sizeof(float) / 3;
+
+ return RSGL_drawRawVerts(renderer, &data);
+}
+
+/*
+outlines
+*/
+
+i32 RSGL_drawLine(RSGL_renderer* renderer, RSGL_vec2D p1, RSGL_vec2D p2, u32 thickness) {
+ renderer->state.lineWidth = thickness;
+
+ RSGL_vec3D center = {(p1.x + p2.x) / 2.0f, (p1.y + p2.y) / 2.0f, 0.0f};
+ RSGL_mat4 matrix = RSGL_renderer_initDrawMatrix(renderer, center);
+
+ float points[] = {RSGL_GET_MATRIX_POINT(p1.x, p1.y, 0.0f), RSGL_GET_MATRIX_POINT(p2.x, p2.y, 0.0f)};
+ float texPoints[] = { renderer->state.source.x, renderer->state.source.y, renderer->state.source.x + renderer->state.source.w, renderer->state.source.y + renderer->state.source.h};
+
+ u16 elements[] = { 0, 1 };
+
+ RSGL_rawVerts data;
+ data.type = RSGL_LINES;
+ data.verts = points;
+ data.texCoords = texPoints;
+ data.elements = elements;
+ data.elmCount = 4;
+ data.vert_count = 2;
+
+ return RSGL_drawRawVerts(renderer, &data);
+}
+
+i32 RSGL_drawTriangleOutline(RSGL_renderer* renderer, RSGL_vec3D t[3], u32 thickness) {
+ renderer->state.lineWidth = thickness;
+ RSGL_vec3D center = {t[2].x, (t[2].y + t[0].y) / 2.0f, 0};
+ RSGL_mat4 matrix = RSGL_renderer_initDrawMatrix(renderer, center);
+
+ float points[] = {RSGL_GET_MATRIX_POINT(t[2].x, t[2].y, t[2].z),
+ RSGL_GET_MATRIX_POINT(t[0].x, t[0].y,t[0].z),
+ RSGL_GET_MATRIX_POINT(t[0].x, t[0].y, t[0].z),
+ RSGL_GET_MATRIX_POINT(t[1].x, t[1].y,t[1].z),
+ RSGL_GET_MATRIX_POINT(t[1].x, t[1].y, t[1].z),
+ RSGL_GET_MATRIX_POINT(t[2].x, t[2].y, t[2].z)};
+
+ float texCoords[18];
+
+ u16 elements[] = {
+ 0, 1, 2,
+ 3, 2, 1
+ };
+
+ RSGL_rawVerts data;
+ data.type = RSGL_LINES;
+ data.verts = points;
+ data.texCoords = texCoords;
+ data.elements = elements;
+ data.elmCount = 6;
+ data.vert_count = 6;
+
+ return RSGL_drawRawVerts(renderer, &data);
+}
+i32 RSGL_drawRectOutline(RSGL_renderer* renderer, RSGL_rect r, u32 thickness) {
+ RSGL_renderer_setCenter(renderer, (RSGL_vec3D){r.x + (r.w / 2.0f), r.y + (r.h / 2.0f), 0.0f});
+ RSGL_drawLine(renderer, (RSGL_vec2D){r.x, r.y}, (RSGL_vec2D){r.x + r.w, r.y}, thickness);
+
+ RSGL_renderer_setCenter(renderer, (RSGL_vec3D){r.x + (r.w / 2.0f), r.y + (r.h / 2.0f), 0.0f});
+ RSGL_drawLine(renderer, (RSGL_vec2D){r.x, r.y}, (RSGL_vec2D){r.x, r.y + r.h}, thickness);
+
+ RSGL_renderer_setCenter(renderer, (RSGL_vec3D){r.x + (r.w / 2.0f), r.y + (r.h / 2.0f), 0.0f});
+ RSGL_drawLine(renderer, (RSGL_vec2D){r.x, r.y + r.h}, (RSGL_vec2D){r.x + r.w, r.y + r.h}, thickness);
+
+ RSGL_renderer_setCenter(renderer, (RSGL_vec3D){r.x + (r.w / 2.0f), r.y + (r.h / 2.0f), 0.0f});
+ return RSGL_drawLine(renderer, (RSGL_vec2D){r.x + r.w, r.y}, (RSGL_vec2D){r.x + r.w, r.y + r.h}, thickness);
+}
+i32 RSGL_drawRoundRectOutline(RSGL_renderer* renderer, RSGL_rect r, RSGL_vec2D rounding, u32 thickness) {
+ RSGL_drawRect(renderer, RSGL_RECT(r.x + (rounding.x/2), r.y, r.w - rounding.x, (int)(thickness + !thickness)));
+ RSGL_drawRect(renderer, RSGL_RECT(r.x + (rounding.x/2), r.y + r.h, r.w - rounding.x, (int)(thickness + !thickness)));
+ RSGL_drawRect(renderer, RSGL_RECT(r.x, r.y + (rounding.y/2), (int)(thickness + !thickness), r.h - rounding.y));
+ RSGL_drawRect(renderer, RSGL_RECT(r.x + r.w, r.y + (rounding.y/2), (int)(thickness + !thickness), r.h - rounding.y));
+
+ RSGL_drawArcOutline(renderer, RSGL_RECT(r.x, r.y, rounding.x, rounding.y), (RSGL_vec2D){180, 270}, thickness);
+ RSGL_drawArcOutline(renderer, RSGL_RECT(r.x + (r.w - rounding.x), r.y, rounding.x, rounding.y), (RSGL_vec2D){90, 180}, thickness);
+ RSGL_drawArcOutline(renderer, RSGL_RECT(r.x + (r.w - rounding.x), r.y + (r.h - rounding.y) - 1, rounding.x, rounding.y + 2), (RSGL_vec2D){0, 90}, thickness);
+ return RSGL_drawArcOutline(renderer, RSGL_RECT(r.x + 1, r.y + (r.h - rounding.y) - 1, rounding.x, rounding.y + 2), (RSGL_vec2D){270, 360}, thickness);
+}
+
+i32 RSGL_drawPolygonOutlineEx(RSGL_renderer* renderer, RSGL_rect o, u32 sides, RSGL_vec2D arc) {
+ static float verts[360 * 2 * 3];
+ static float texCoords[360 * 2 * 2];
+
+ RSGL_vec3D center = (RSGL_vec3D) {o.x + (o.w / 2.0f), o.y + (o.h / 2.0f), 0.0f};
+ RSGL_mat4 matrix = RSGL_renderer_initDrawMatrix(renderer, center);
+
+ o = (RSGL_rect){o.x + (o.w / 2), o.y + (o.h / 2), o.w / 2, o.h / 2};
+
+ float displacement = 360.0f / (float)sides;
+ float centralAngle = displacement * arc.x;
+
+ i32 i;
+ u32 j;
+ size_t index = 0;
+
+ for (i = arc.x; i < arc.y; i++) {
+ for (j = 0; j < 2; j++) {
+ float temp[3] = {
+ RSGL_GET_MATRIX_POINT(
+ o.x + (RSGL_SIN(DEG2RAD * centralAngle) * o.w),
+ o.y + (RSGL_COS(DEG2RAD * centralAngle) * o.h),
+ (0.0)
+ )
+ };
+ memcpy(verts + index, temp, sizeof(float) * 3);
+
+ if (!j) centralAngle += displacement;
+ index += 3;
+ }
+ }
+
+ u16 elements[] = {
+ 0, 1, 2,
+ 3, 2, 1
+ };
+
+ RSGL_rawVerts data;
+ data.type = RSGL_LINES;
+ data.verts = verts;
+ data.texCoords = texCoords;
+ data.elements = elements;
+ data.elmCount = 6;
+ data.vert_count = 6;
+
+ return RSGL_drawRawVerts(renderer, &data);
+}
+
+i32 RSGL_drawPolygonOutline(RSGL_renderer* renderer, RSGL_rect o, u32 sides, u32 thickness) {
+ renderer->state.lineWidth = thickness;
+ return RSGL_drawPolygonOutlineEx(renderer, o, sides, (RSGL_vec2D){0, (float)sides});
+}
+i32 RSGL_drawArcOutline(RSGL_renderer* renderer, RSGL_rect o, RSGL_vec2D arc, u32 thickness) {
+ float verts = ((2 * M_PI * ((o.w + o.h) / 2.0f)) / 10);
+ verts = (verts > 360 ? 360 : verts);
+
+ renderer->state.lineWidth = thickness;
+ return RSGL_drawPolygonOutlineEx(renderer, o, verts, arc);
+}
+
+i32 RSGL_drawOvalOutline(RSGL_renderer* renderer, RSGL_rect o, u32 thickness) {
+ float verts = ((2 * M_PI * ((o.w + o.h) / 2.0f)) / 10);
+ verts = (verts > 360 ? 360 : verts);
+
+ renderer->state.lineWidth = thickness;
+ return RSGL_drawPolygonOutlineEx(renderer, o, verts, (RSGL_vec2D){0, verts});
+}
+
+/*
+******
+RSGL_view
+******
+*/
+
+/* Multiply the current matrix by a rotation matrix */
+RSGL_mat4 RSGL_projection_getMatrix(const RSGL_projection* projection) {
+ RSGL_mat4 matrix = RSGL_mat4_loadIdentity();
+ switch (projection->type) {
+ case RSGL_projectionPerspective3D:
+ matrix = RSGL_mat4_perspective(matrix.m, projection->p3D.fov, projection->p3D.ratio, projection->p3D.pNear, projection->p3D.pFar);
+ break;
+ case RSGL_projectionOrtho3D: {
+ double top = projection->p3D.fov / 2.0;
+ double right = top * projection->p3D.ratio;
+
+ matrix = RSGL_mat4_ortho(matrix.m, -right, right, -top, top, projection->p3D.pNear, projection->p3D.pFar);
+ break;
+ }
+ case RSGL_projectionOrtho2D:
+ matrix = RSGL_mat4_ortho(matrix.m, 0, projection->p2D.width, projection->p2D.height, 0, 0, 1.0);
+ break;
+ default: break;
+ }
+
+ return matrix;
+}
+
+
+RSGL_mat4 RSGL_view_getMatrix(const RSGL_view* view) {
+ RSGL_mat4 matrix = RSGL_mat4_loadIdentity();
+ switch (view->type) {
+ case RSGL_viewType2D:
+ matrix = RSGL_mat4_translate(matrix.m, -view->view2D.target.x, -view->view2D.target.y, -view->view2D.target.z);
+ matrix = RSGL_mat4_rotate(matrix.m, view->view2D.rotation, 0, 0, 1);
+ matrix = RSGL_mat4_scale(matrix.m, view->view2D.zoom, view->view2D.zoom, 1.0f);
+ matrix = RSGL_mat4_translate(matrix.m, view->view2D.offset.x, view->view2D.offset.y, view->view2D.offset.z);
+ break;
+ case RSGL_viewType3D:
+ matrix = RSGL_mat4_lookAt(matrix.m, view->view3D.pos.x, view->view3D.pos.y, view->view3D.pos.z, view->view3D.target.x, view->view3D.target.y, view->view3D.target.z,
+ view->view3D.up.x, view->view3D.up.y, view->view3D.up.z);
+ break;
+ default: break;
+ }
+
+ return matrix;
+}
+
+#endif
+
+/*
+******
+RSGL_Matrix
+******
+*/
+
+
+RSGL_mat4 RSGL_mat4_lookAt(float matrix[16], float eyeX, float eyeY, float eyeZ, float targetX, float targetY, float targetZ, float upX, float upY, float upZ) {
+ float matLookAt[16];
+
+ float length = 0.0f;
+ float ilength = 0.0f;
+
+ RSGL_vec3D vz = { eyeX - targetX, eyeY - targetY, eyeZ - targetZ };
+
+ RSGL_vec3D v = vz;
+ length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);
+ if (length == 0.0f) length = 1.0f;
+ ilength = 1.0f/length;
+ vz.x *= ilength;
+ vz.y *= ilength;
+ vz.z *= ilength;
+
+ RSGL_vec3D vx = { upY*vz.z - upZ*vz.y, upZ*vz.x - upX*vz.z, upX*vz.y - upY*vz.x };
+
+ v = vx;
+ length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);
+ if (length == 0.0f) length = 1.0f;
+ ilength = 1.0f/length;
+ vx.x *= ilength;
+ vx.y *= ilength;
+ vx.z *= ilength;
+
+ RSGL_vec3D vy = { vz.y*vx.z - vz.z*vx.y, vz.z*vx.x - vz.x*vx.z, vz.x*vx.y - vz.y*vx.x };
+
+ matLookAt[0] = vx.x;
+ matLookAt[1] = vy.x;
+ matLookAt[2] = vz.x;
+ matLookAt[3] = 0.0f;
+ matLookAt[4] = vx.y;
+ matLookAt[5] = vy.y;
+ matLookAt[6] = vz.y;
+ matLookAt[7] = 0.0f;
+ matLookAt[8] = vx.z;
+ matLookAt[9] = vy.z;
+ matLookAt[10] = vz.z;
+ matLookAt[11] = 0.0f;
+ matLookAt[12] = -(vx.x*eyeX + vx.y*eyeY + vx.z*eyeZ);
+ matLookAt[13] = -(vy.x*eyeX + vy.y*eyeY + vy.z*eyeZ);
+ matLookAt[14] = -(vz.x*eyeX + vz.y*eyeY + vz.z*eyeZ);
+ matLookAt[15] = 1.0f;
+
+ return RSGL_mat4_multiply(matrix, matLookAt);
+}
+
+RSGL_mat4 RSGL_mat4_ortho(float matrix[16], float left, float right, float bottom, float top, float znear, float zfar) {
+ float rl = (float)(right - left);
+ float tb = (float)(top - bottom);
+ float fn = (float)(zfar - znear);
+
+ float matOrtho[16] = {
+ (2.0f / rl), 0.0f, 0.0f, 0.0f,
+ 0.0f, (2.0f / tb), 0.0f, 0.00,
+ 0.0f, 0.0f, (-2.0f / fn), 0.0f,
+ (-((float)left + (float)right) / rl), -((float)top + (float)bottom)/tb, (-((float)zfar + (float)znear) / fn), 1.0f
+ };
+
+ return RSGL_mat4_multiply(matrix, matOrtho);
+}
+
+RSGL_mat4 RSGL_mat4_scale(float matrix[16], float x, float y, float z) {
+ RSGL_mat4 result;
+
+ for (int i = 0; i < 16; ++i) {
+ result.m[i] = matrix[i];
+ }
+
+ result.m[0] += matrix[0]*x + matrix[4]*y + matrix[8]*z;
+ result.m[13] += matrix[1]*x + matrix[5]*y + matrix[9]*z;
+ result.m[10] += matrix[2]*x + matrix[6]*y + matrix[10]*z;
+ result.m[11] += matrix[2] + matrix[6] + matrix[10];
+
+ return result;
+}
+
+
+
+/* Multiply the current matrix by a translation matrix */
+RSGL_mat4 RSGL_mat4_translate(float matrix[16], float x, float y, float z) {
+ RSGL_mat4 result;
+
+ for (int i = 0; i < 16; ++i) {
+ result.m[i] = matrix[i];
+ }
+
+ result.m[12] += matrix[0]*x + matrix[4]*y + matrix[8]*z;
+ result.m[13] += matrix[1]*x + matrix[5]*y + matrix[9]*z;
+ result.m[14] += matrix[2]*x + matrix[6]*y + matrix[10]*z;
+
+ return result;
+}
+
+RSGL_mat4 RSGL_mat4_loadIdentity(void) {
+ RSGL_mat4 matrix = (RSGL_mat4) {
+ {
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f
+ }
+ };
+
+ return matrix;
+}
+
+RSGL_mat4 RSGL_mat4_rotate(float matrix[16], float angle, float x, float y, float z) {
+ /* Axis vector (x, y, z) normalization */
+ float lengthSquared = x * x + y * y + z * z;
+ if ((lengthSquared != 1.0f) && (lengthSquared != 0.0f)) {
+ float inverseLength = 1.0f / sqrtf(lengthSquared);
+ x *= inverseLength;
+ y *= inverseLength;
+ z *= inverseLength;
+ }
+
+ /* Rotation matrix generation */
+ float sinres = RSGL_SIN(angle);
+ float cosres = RSGL_COS(angle);
+ float t = 1.0f - cosres;
+
+ float matRotation[16] = {
+ x * x * t + cosres, y * x * t + z * sinres, z * x * t - y * sinres, 0.0f,
+ x * y * t - z * sinres, y * y * t + cosres, z * y * t + x * sinres, 0.0f,
+ x * z * t + y * sinres, y * z * t - x * sinres, z * z * t + cosres, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f
+ };
+
+ return RSGL_mat4_multiply(matRotation, matrix);
+}
+
+RSGL_mat4 RSGL_mat4_perspective(float matrix[16], float fovY, float aspect, float zNear, float zFar) {
+ fovY = (fovY * DEG2RAD) / 2.0f;
+ const float f = (RSGL_COS(fovY) / RSGL_SIN(fovY));
+
+ float perspective[16] = {
+ (f / aspect), 0.0f, 0.0f, 0.0f,
+ 0, f, 0.0f, 0.0f,
+ 0.0f, 0.0f, (zFar + zNear) / (zNear - zFar), -1.0f,
+ 0.0f, 0.0f, (2.0f * zFar * zNear) / (zNear - zFar), 0.0f
+ };
+
+ return RSGL_mat4_multiply(matrix, perspective);
+}
+
+RSGL_vec3D RSGL_mat4_multiplyPoint(RSGL_mat4 matrix, RSGL_vec3D point) {
+ return (RSGL_vec3D){RSGL_GET_MATRIX_POINT(point.x, point.y, point.z)};
+}
+
+RSGL_mat4 RSGL_mat4_multiply(float left[16], float right[16]) {
+ return (RSGL_mat4) {
+ {
+ left[0] * right[0] + left[1] * right[4] + left[2] * right[8] + left[3] * right[12],
+ left[0] * right[1] + left[1] * right[5] + left[2] * right[9] + left[3] * right[13],
+ left[0] * right[2] + left[1] * right[6] + left[2] * right[10] + left[3] * right[14],
+ left[0] * right[3] + left[1] * right[7] + left[2] * right[11] + left[3] * right[15],
+ left[4] * right[0] + left[5] * right[4] + left[6] * right[8] + left[7] * right[12],
+ left[4] * right[1] + left[5] * right[5] + left[6] * right[9] + left[7] * right[13],
+ left[4] * right[2] + left[5] * right[6] + left[6] * right[10] + left[7] * right[14],
+ left[4] * right[3] + left[5] * right[7] + left[6] * right[11] + left[7] * right[15],
+ left[8] * right[0] + left[9] * right[4] + left[10] * right[8] + left[11] * right[12],
+ left[8] * right[1] + left[9] * right[5] + left[10] * right[9] + left[11] * right[13],
+ left[8] * right[2] + left[9] * right[6] + left[10] * right[10] + left[11] * right[14],
+ left[8] * right[3] + left[9] * right[7] + left[10] * right[11] + left[11] * right[15],
+ left[12] * right[0] + left[13] * right[4] + left[14] * right[8] + left[15] * right[12],
+ left[12] * right[1] + left[13] * right[5] + left[14] * right[9] + left[15] * right[13],
+ left[12] * right[2] + left[13] * right[6] + left[14] * right[10] + left[15] * right[14],
+ left[12] * right[3] + left[13] * right[7] + left[14] * right[11] + left[15] * right[15]
+ }
+ };
+}
+#endif /* RSGL_IMPLEMENTATION */
diff --git a/include/RSGL_gl.h b/include/RSGL_gl.h
new file mode 100644
index 0000000..b1d1d05
--- /dev/null
+++ b/include/RSGL_gl.h
@@ -0,0 +1,1013 @@
+#ifndef RSGL_H
+#include "RSGL.h"
+#endif
+
+// WebGL doesn't support compute shaders iirc so yeah
+#if defined(__EMSCRIPTEN__) && defined(RSGL_USE_COMPUTE)
+#undef RSGL_USE_COMPUTE
+#endif
+
+#ifdef __EMSCRIPTEN__
+ #if defined(RSGL_GL2)
+ #undef RSGL_GL2
+ #define RSGL_GLES2
+ #endif
+ #if defined(RSGL_GL3)
+ #undef RSGL_GL3
+ #define RSGL_GLES3
+ #endif
+#endif
+
+
+#if !defined(RSGL_GLES3) && !defined(RSGL_GLES2) && !defined(RSGL_GL2) && !defined(RSGL_GL3)
+ #ifndef __EMSCRIPTEN__
+ #define RSGL_GL3
+ #else
+ #define RSGL_GLES3
+ #endif
+#endif
+
+#if defined(RSGL_GLES3) || defined(RSGL_GLES2)
+ #ifndef RSGL_NO_GL_LOADER
+ #define RSGL_NO_GL_LOADER
+ #endif
+#endif
+
+#ifndef RSGL_GL_H
+#define RSGL_GL_H
+
+typedef struct RSGL_glRenderer {
+ u32 vao;
+} RSGL_glRenderer;
+
+RSGLDEF RSGL_rendererProc RSGL_GL_rendererProc(void);
+RSGLDEF size_t RSGL_GL_size(void);
+
+RSGLDEF RSGL_renderer* RSGL_GL_renderer_init(void* loader);
+RSGLDEF void RSGL_GL_renderer_initPtr(void* loader, RSGL_glRenderer* ptr, RSGL_renderer* renderer);
+RSGLDEF void RSGL_GL_render(RSGL_glRenderer* ctx, const RSGL_renderPass* pass);
+RSGLDEF void RSGL_GL_initPtr(RSGL_glRenderer* ctx, void* proc); /* init render backend */
+RSGLDEF void RSGL_GL_freePtr(RSGL_glRenderer* ctx); /* free render backend */
+RSGLDEF void RSGL_GL_clear(RSGL_glRenderer* ctx, RSGL_framebuffer framebuffer, float r, float g, float b, float a);
+RSGLDEF void RSGL_GL_viewport(RSGL_glRenderer* ctx, i32 x, i32 y, i32 w, i32 h);
+RSGLDEF void RSGL_GL_createBuffer(RSGL_glRenderer* ctx, RSGL_bufferType type, size_t size, const void* data, size_t* buffer);
+RSGLDEF void RSGL_GL_updateBuffer(RSGL_glRenderer* ctx, RSGL_bufferType type, size_t buffer, const void* data, size_t start, size_t end);
+RSGLDEF void RSGL_GL_deleteBuffer(RSGL_glRenderer* ctx, size_t buffer);
+RSGLDEF RSGL_programBlob RSGL_GL_defaultBlob(RSGL_glRenderer* ctx);
+/* create a texture based on a given bitmap, this must be freed later using RSGL_deleteTexture or opengl*/
+RSGLDEF RSGL_texture RSGL_GL_createTexture(RSGL_glRenderer* ctx, const RSGL_textureBlob* blob);
+/* updates an existing texture wiht a new bitmap */
+RSGLDEF void RSGL_GL_copyToTexture(RSGL_glRenderer* ctx, RSGL_texture texture, size_t x, size_t y, const RSGL_textureBlob* blob);
+/* delete a texture */
+RSGLDEF void RSGL_GL_deleteTexture(RSGL_glRenderer* ctx, RSGL_texture tex);
+/* starts scissoring */
+RSGLDEF void RSGL_GL_scissorStart(RSGL_glRenderer* ctx, float x, float y, float w, float h, float renderer_height);
+/* stops scissoring */
+RSGLDEF void RSGL_GL_scissorEnd(RSGL_glRenderer* ctx);
+/* program loading */
+RSGLDEF RSGL_programInfo RSGL_GL_createProgram(RSGL_glRenderer* ctx, RSGL_programBlob* blob);
+RSGLDEF void RSGL_GL_deleteProgram(RSGL_glRenderer* ctx, const RSGL_programInfo* program);
+RSGLDEF size_t RSGL_GL_findShaderVariable(RSGL_glRenderer* ctx, const RSGL_programInfo* program, const char* var, const size_t len);
+RSGLDEF void RSGL_GL_updateShaderVariable(RSGL_glRenderer* ctx, const RSGL_programInfo* program, size_t var, const float value[], u8 len);
+
+RSGLDEF RSGL_framebuffer RSGL_GL_createFramebuffer(RSGL_glRenderer* renderer, size_t width, size_t height);
+RSGLDEF void RSGL_GL_attachFramebuffer(RSGL_glRenderer* renderer, RSGL_framebuffer fbo, RSGL_texture tex, u8 attachType, u8 mipLevel);
+RSGLDEF void RSGL_GL_deleteFramebuffer(RSGL_glRenderer* renderer, RSGL_framebuffer fbo);
+
+#ifdef RSGL_USE_COMPUTE
+RSGLDEF RSGL_programInfo RSGL_GL_createComputeProgram(RSGL_glRenderer* ctx, const char* CShaderCode);
+RSGLDEF void RSGL_GL_dispatchComputeProgram(RSGL_glRenderer* ctx, RSGL_programInfo program, u32 groups_x, u32 groups_y, u32 groups_z);
+RSGLDEF void RSGL_GL_bindComputeTexture(RSGL_glRenderer* ctx, u32 texture, u8 format);
+#endif
+#endif
+
+#ifdef RSGL_IMPLEMENTATION
+
+RSGL_renderer* RSGL_GL_renderer_init(void* loader) { return RSGL_renderer_init(RSGL_GL_rendererProc(), loader); }
+void RSGL_GL_renderer_initPtr(void* loader, RSGL_glRenderer* ptr, RSGL_renderer* renderer) { return RSGL_renderer_initPtr(RSGL_GL_rendererProc(), loader, ptr, renderer); }
+
+
+/* prevent winapi conflicts (opengl includes windows.h for some reason) */
+#define OEMRESOURCE
+
+#define GL_GLEXT_PROTOTYPES
+
+#ifndef __APPLE__
+#include <GL/gl.h>
+#else
+#include <OpenGL/gl.h>
+#include <OpenGL/glext.h>
+#endif
+
+#if defined(RSGL_GLES3)
+ #include <GLES3/gl3.h>
+#elif defined(RSGL_GLES2)
+ #include <GLES2/gl2.h>
+#endif
+
+#if defined(_WIN32)
+typedef char GLchar;
+typedef int GLsizei;
+#include <GL/glext.h>
+#endif
+
+#ifndef RSGL_NO_GL_LOADER
+
+typedef void (*RSGL_gl_proc)(void); // function pointer equivalent of void*
+#define RSGL_PROC_DEF(proc, name) name##SRC = (name##PROC)(RSGL_gl_proc)proc(#name)
+
+typedef void (*RSGLapiproc)(void);
+typedef RSGLapiproc (*RSGLloadfunc)(const char *name);
+
+typedef void (*glShaderSourcePROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
+typedef GLuint (*glCreateShaderPROC) (GLenum type);
+typedef void (*glCompileShaderPROC) (GLuint shader);
+typedef GLuint (*glCreateProgramPROC) (void);
+typedef void (*glAttachShaderPROC) (GLuint program, GLuint shader);
+typedef void (*glBindAttribLocationPROC) (GLuint program, GLuint index, const GLchar *name);
+typedef void (*glLinkProgramPROC) (GLuint program);
+typedef void (*glBindBufferPROC) (GLenum target, GLuint buffer);
+typedef void (*glBufferDataPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage);
+typedef void (*glEnableVertexAttribArrayPROC) (GLuint index);
+typedef void (*glVertexAttribPointerPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
+typedef void (*glDisableVertexAttribArrayPROC) (GLuint index);
+typedef void (*glDeleteBuffersPROC) (GLsizei n, const GLuint *buffers);
+typedef void (*glUseProgramPROC) (GLuint program);
+typedef void (*glDetachShaderPROC) (GLuint program, GLuint shader);
+typedef void (*glDeleteShaderPROC) (GLuint shader);
+typedef void (*glDeleteProgramPROC) (GLuint program);
+typedef void (*glBufferSubDataPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data);
+typedef void (*glGetShaderivPROC)(GLuint shader, GLenum pname, GLint *params);
+typedef void (*glGetShaderInfoLogPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
+typedef void (*glGetProgramivPROC)(GLuint program, GLenum pname, GLint *params);
+typedef void (*glGetProgramInfoLogPROC)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
+typedef void (*glGenBuffersPROC)(GLsizei n, GLuint *buffers);
+typedef GLint (*glGetUniformLocationPROC)(GLuint program, const GLchar *name);
+typedef GLint (*glGetAttribLocationPROC)(GLuint program, const GLchar *name);
+typedef void (*glUniformMatrix4fvPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+typedef void (*glTexImage2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);
+typedef void (*glActiveTexturePROC) (GLenum texture);
+typedef void (*glUniform1fPROC) (GLint location, GLfloat v0);
+typedef void (*glUniform2fPROC) (GLint location, GLfloat v0, GLfloat v1);
+typedef void (*glUniform3fPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
+typedef void (*glUniform4fPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
+
+#if defined(RSGL_GLES3) || defined(RSGL_GL3)
+ typedef void (*glGenVertexArraysPROC)(GLsizei n, GLuint *arrays);
+ typedef void (*glBindVertexArrayPROC)(GLuint array);
+ typedef void (*glDeleteVertexArraysPROC) (GLsizei n, const GLuint *arrays);
+
+ glGenVertexArraysPROC glGenVertexArraysSRC = NULL;
+ glBindVertexArrayPROC glBindVertexArraySRC = NULL;
+ glDeleteVertexArraysPROC glDeleteVertexArraysSRC = NULL;
+#endif
+
+glShaderSourcePROC glShaderSourceSRC = NULL;
+glCreateShaderPROC glCreateShaderSRC = NULL;
+glCompileShaderPROC glCompileShaderSRC = NULL;
+glCreateProgramPROC glCreateProgramSRC = NULL;
+glAttachShaderPROC glAttachShaderSRC = NULL;
+glBindAttribLocationPROC glBindAttribLocationSRC = NULL;
+glLinkProgramPROC glLinkProgramSRC = NULL;
+glBindBufferPROC glBindBufferSRC = NULL;
+glBufferDataPROC glBufferDataSRC = NULL;
+glEnableVertexAttribArrayPROC glEnableVertexAttribArraySRC = NULL;
+glVertexAttribPointerPROC glVertexAttribPointerSRC = NULL;
+glDisableVertexAttribArrayPROC glDisableVertexAttribArraySRC = NULL;
+glDeleteBuffersPROC glDeleteBuffersSRC = NULL;
+glUseProgramPROC glUseProgramSRC = NULL;
+glDetachShaderPROC glDetachShaderSRC = NULL;
+glDeleteShaderPROC glDeleteShaderSRC = NULL;
+glDeleteProgramPROC glDeleteProgramSRC = NULL;
+glBufferSubDataPROC glBufferSubDataSRC = NULL;
+glGetShaderivPROC glGetShaderivSRC = NULL;
+glGetShaderInfoLogPROC glGetShaderInfoLogSRC = NULL;
+glGetProgramivPROC glGetProgramivSRC = NULL;
+glGetProgramInfoLogPROC glGetProgramInfoLogSRC = NULL;
+glGenBuffersPROC glGenBuffersSRC = NULL;
+glGetUniformLocationPROC glGetUniformLocationSRC = NULL;
+glGetAttribLocationPROC glGetAttribLocationSRC = NULL;
+glUniformMatrix4fvPROC glUniformMatrix4fvSRC = NULL;
+glActiveTexturePROC glActiveTextureSRC = NULL;
+glUniform1fPROC glUniform1fSRC = NULL;
+glUniform2fPROC glUniform2fSRC = NULL;
+glUniform3fPROC glUniform3fSRC = NULL;
+glUniform4fPROC glUniform4fSRC = NULL;
+
+typedef void (*glBindFramebufferPROC) (GLenum target, GLuint framebuffer);
+typedef void (*glGenFramebuffersPROC) (GLsizei n, GLuint *ids);
+typedef void (*glDeleteFramebuffersPROC) (GLsizei n, GLuint *framebuffers);
+typedef void (*glFramebufferTexture2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
+
+#ifdef RSGL_USE_COMPUTE
+typedef void (*glDispatchComputePROC)(GLuint x, GLuint y, GLuint z);
+glDispatchComputePROC glDispatchComputeSRC = NULL;
+
+typedef void (*glMemoryBarrierPROC)(GLenum e);
+glMemoryBarrierPROC glMemoryBarrierSRC = NULL;
+
+typedef void (*glBindImageTexturePROC)(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format);
+glBindImageTexturePROC glBindImageTextureSRC = NULL;
+
+#endif
+
+glBindFramebufferPROC glBindFramebufferSRC = NULL;
+glGenFramebuffersPROC glGenFramebuffersSRC = NULL;
+glDeleteFramebuffersPROC glDeleteFramebuffersSRC = NULL;
+glFramebufferTexture2DPROC glFramebufferTexture2DSRC = NULL;
+
+#define glUniform1f glUniform1fSRC
+#define glUniform2f glUniform2fSRC
+#define glUniform3f glUniform3fSRC
+#define glUniform4f glUniform4fSRC
+#define glActiveTexture glActiveTextureSRC
+#define glShaderSource glShaderSourceSRC
+#define glCreateShader glCreateShaderSRC
+#define glCompileShader glCompileShaderSRC
+#define glCreateProgram glCreateProgramSRC
+#define glAttachShader glAttachShaderSRC
+#define glBindAttribLocation glBindAttribLocationSRC
+#define glLinkProgram glLinkProgramSRC
+#define glBindBuffer glBindBufferSRC
+#define glBufferData glBufferDataSRC
+#define glEnableVertexAttribArray glEnableVertexAttribArraySRC
+#define glVertexAttribPointer glVertexAttribPointerSRC
+#define glDisableVertexAttribArray glDisableVertexAttribArraySRC
+#define glDeleteBuffers glDeleteBuffersSRC
+#define glUseProgram glUseProgramSRC
+#define glDetachShader glDetachShaderSRC
+#define glDeleteShader glDeleteShaderSRC
+#define glDeleteProgram glDeleteProgramSRC
+#define glBufferSubData glBufferSubDataSRC
+#define glGetShaderiv glGetShaderivSRC
+#define glGetShaderInfoLog glGetShaderInfoLogSRC
+#define glGetProgramiv glGetProgramivSRC
+#define glGetProgramInfoLog glGetProgramInfoLogSRC
+#define glGenBuffers glGenBuffersSRC
+#define glGetUniformLocation glGetUniformLocationSRC
+#define glGetAttribLocation glGetAttribLocationSRC
+#define glUniformMatrix4fv glUniformMatrix4fvSRC
+#define glBindFramebuffer glBindFramebufferSRC
+#define glGenFramebuffers glGenFramebuffersSRC
+#define glDeleteFramebuffers glDeleteFramebuffersSRC
+#define glFramebufferTexture2D glFramebufferTexture2DSRC
+
+#if defined(RSGL_GLES3) || defined(RSGL_GL3)
+ #define glGenVertexArrays glGenVertexArraysSRC
+ #define glBindVertexArray glBindVertexArraySRC
+ #define glDeleteVertexArrays glDeleteVertexArraysSRC
+#endif
+
+#ifdef RSGL_USE_COMPUTE
+#define glMemoryBarrier glMemoryBarrierSRC
+#define glDispatchCompute glDispatchComputeSRC
+#define glBindImageTexture glBindImageTextureSRC
+#endif
+
+#ifndef GL_TEXTURE_SWIZZLE_RGBA
+ #define GL_TEXTURE_SWIZZLE_RGBA 0x8E46
+#endif
+
+extern int RSGL_loadGLModern(RSGLloadfunc proc);
+#endif
+
+#define RSGL_MULTILINE_STR(...) #__VA_ARGS__
+size_t RSGL_GL_size(void) {
+ return sizeof(RSGL_glRenderer);
+}
+
+RSGL_rendererProc RSGL_GL_rendererProc() {
+ RSGL_rendererProc proc;
+ RSGL_MEMSET(&proc, 0, sizeof(proc));
+
+ proc.render = (void (*)(void*, const RSGL_renderPass* pass))RSGL_GL_render;
+ proc.size = (size_t (*)(void))RSGL_GL_size;
+ proc.initPtr = (void (*)(void*, void*))RSGL_GL_initPtr;
+ proc.freePtr = (void (*)(void*))RSGL_GL_freePtr;
+ proc.clear = (void (*)(void*, RSGL_framebuffer, float, float, float, float))RSGL_GL_clear;
+ proc.viewport = (void (*)(void*, i32, i32, i32, i32))RSGL_GL_viewport;
+ proc.createTexture = (RSGL_texture (*)(void*, const RSGL_textureBlob* blob))RSGL_GL_createTexture;
+ proc.copyToTexture = (void (*)(void*, RSGL_texture, size_t, size_t, const RSGL_textureBlob* blob))RSGL_GL_copyToTexture;
+ proc.deleteTexture = (void (*)(void*, RSGL_texture))RSGL_GL_deleteTexture;
+ proc.scissorStart = (void (*)(void*, float, float, float, float, float))RSGL_GL_scissorStart;
+ proc.scissorEnd = (void (*)(void*))RSGL_GL_scissorEnd;
+ proc.createProgram = (RSGL_programInfo (*)(void*, RSGL_programBlob* blob))RSGL_GL_createProgram;
+ proc.deleteProgram = (void (*)(void*, const RSGL_programInfo*))RSGL_GL_deleteProgram;
+ proc.findShaderVariable = (size_t (*)(void*, const RSGL_programInfo*, const char*, size_t))RSGL_GL_findShaderVariable;
+ proc.updateShaderVariable = (void (*)(void*, const RSGL_programInfo*, size_t, const float[], u8))RSGL_GL_updateShaderVariable;
+ proc.createBuffer = (void (*)(void*, RSGL_bufferType, size_t, const void*, size_t*))RSGL_GL_createBuffer;
+ proc.updateBuffer = (void (*)(void*, RSGL_bufferType, size_t, void*, size_t, size_t))RSGL_GL_updateBuffer;
+ proc.deleteBuffer = (void (*)(void*, size_t))RSGL_GL_deleteBuffer;
+ proc.defaultBlob = (RSGL_programBlob (*)(void*))RSGL_GL_defaultBlob;
+ proc.createFramebuffer = (RSGL_framebuffer (*)(void*, size_t, size_t))RSGL_GL_createFramebuffer;
+ proc.attachFramebuffer = (void (*)(void*, RSGL_framebuffer, RSGL_texture, u8, u8))RSGL_GL_attachFramebuffer;
+ proc.deleteFramebuffer = (void (*)(void*, RSGL_framebuffer))RSGL_GL_deleteFramebuffer;
+
+
+// proc.setSurface = (void (*)(void*, void*))RSGL_GL_setSurface;
+#ifdef RSGL_USE_COMPUTE
+ proc.createComputeProgram = (RSGL_programInfo (*)(void*, const char*))RSGL_GL_createComputeProgram;
+ proc.dispatchComputeProgram = (void (*)(void*, const RSGL_programInfo*, u32, u32, u32))RSGL_GL_dispatchComputeProgram;
+ proc.bindComputeTexture = (void (*)(void*, u32, u8))RSGL_GL_bindComputeTexture;
+#else
+ proc.createComputeProgram = NULL;
+ proc.dispatchComputeProgram = NULL;
+ proc.bindComputeTexture = NULL;
+#endif
+ return proc;
+}
+
+void RSGL_GL_deleteTexture(RSGL_glRenderer* ctx, RSGL_texture tex) { glDeleteTextures(1, (u32*)&tex); }
+void RSGL_GL_viewport(RSGL_glRenderer* ctx, i32 x, i32 y, i32 w, i32 h) { glViewport(x, y, w ,h); }
+
+void RSGL_GL_clear(RSGL_glRenderer* ctx, RSGL_framebuffer framebuffer, float r, float g, float b, float a) {
+ glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+
+ glClearColor(r, g, b, a);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+GLuint RSGL_GL_bufferTypeToNative(RSGL_bufferType type) {
+ switch (type) {
+ case RSGL_arrayBuffer: return GL_ARRAY_BUFFER;
+ case RSGL_elementArrayBuffer: return GL_ELEMENT_ARRAY_BUFFER;
+ #if !defined(__APPLE__)
+ case RSGL_shaderStorageBuffer: return GL_SHADER_STORAGE_BUFFER;
+ #endif
+ #if !defined(__APPLE__) || defined(RSGL_GLES3)
+ case RSGL_textureBuffer: return GL_TEXTURE_BUFFER;
+ case RSGL_uniformBuffer: return GL_UNIFORM_BUFFER;
+ #endif
+ default: break;
+ }
+
+ return GL_ARRAY_BUFFER;
+}
+
+void RSGL_GL_createBuffer(RSGL_glRenderer* ctx, RSGL_bufferType type, size_t size, const void* data, size_t* buffer) {
+ glGenBuffers(1, (u32*)buffer);
+
+ GLenum usage = GL_STATIC_DRAW;
+ if (data == NULL)
+ usage = GL_DYNAMIC_DRAW;
+
+ glBindBuffer(RSGL_GL_bufferTypeToNative(type), *(u32*)buffer);
+ glBufferData(RSGL_GL_bufferTypeToNative(type), size, data, usage);
+}
+
+void RSGL_GL_updateBuffer(RSGL_glRenderer* ctx, RSGL_bufferType type, size_t buffer, const void* data, size_t start, size_t end) {
+ glBindBuffer(RSGL_GL_bufferTypeToNative(type), *(u32*)&buffer);
+ glBufferSubData(RSGL_GL_bufferTypeToNative(type), start, end, data);
+}
+
+void RSGL_GL_deleteBuffer(RSGL_glRenderer* ctx, size_t buffer) {
+ glDeleteBuffers(1, (u32*)&buffer);
+}
+
+RSGL_programBlob RSGL_GL_defaultBlob(RSGL_glRenderer* ctx) {
+#ifdef RSGL_GL3
+
+ static const char *defaultVShaderCode = RSGL_MULTILINE_STR(
+ \x23version 330 \n
+ in vec3 vertexPosition; \n
+ in vec2 vertexTexCoord; \n
+ in vec4 vertexColor; \n
+ out vec2 fragTexCoord; \n
+ out vec4 fragColor; \n
+ uniform mat4 model; \n
+ uniform mat4 pv; \n
+ void main() {
+ fragTexCoord = vertexTexCoord;
+ fragColor = vertexColor;
+ gl_Position = pv * model * vec4(vertexPosition, 1.0);
+ }
+ );
+
+ static const char* defaultFShaderCode = RSGL_MULTILINE_STR(
+ \x23version 330 \n
+ in vec2 fragTexCoord;
+ in vec4 fragColor;
+ out vec4 finalColor;
+ uniform sampler2D texture0;
+ void main() {
+ finalColor = texture(texture0, fragTexCoord) * fragColor;
+ }
+ );
+
+#elif defined(RSGL_GLES3)
+
+ static const char *defaultVShaderCode = RSGL_MULTILINE_STR(
+ \x23version 300 es \n
+ in vec3 vertexPosition; \n
+ in vec2 vertexTexCoord; \n
+ in vec4 vertexColor; \n
+ out vec2 fragTexCoord; \n
+ out vec4 fragColor; \n
+ uniform mat4 model; \n
+ uniform mat4 pv; \n
+ void main() {
+ fragTexCoord = vertexTexCoord;
+ fragColor = vertexColor;
+ gl_Position = pv * model * vec4(vertexPosition, 1.0);
+ }
+ );
+
+ static const char* defaultFShaderCode = RSGL_MULTILINE_STR(
+ \x23version 300 es \n
+ precision mediump float; \n
+ in vec2 fragTexCoord;
+ in vec4 fragColor;
+ out vec4 finalColor;
+ uniform sampler2D texture0;
+ void main() {
+ finalColor = texture(texture0, fragTexCoord) * fragColor;
+ }
+ );
+
+#elif defined(RSGL_GLES2) || defined(RSGL_GL2)
+
+ static const char *defaultVShaderCode = RSGL_MULTILINE_STR(
+ \x23version 100 \n
+ attribute vec3 vertexPosition; \n
+ attribute vec2 vertexTexCoord; \n
+ attribute vec4 vertexColor; \n
+ varying vec2 fragTexCoord; \n
+ varying vec4 fragColor; \n
+ uniform mat4 model; \n
+ uniform mat4 pv; \n
+ void main() {
+ fragTexCoord = vertexTexCoord;
+ fragColor = vertexColor;
+ gl_Position = pv * model * vec4(vertexPosition, 1.0);
+ }
+ );
+
+ static const char* defaultFShaderCode = RSGL_MULTILINE_STR(
+ \x23version 100 \n
+ precision mediump float; \n
+ varying vec2 fragTexCoord; \n
+ varying vec4 fragColor; \n
+ vec4 finalColor;
+ uniform sampler2D texture0;
+ void main() {
+ gl_FragColor= texture2D(texture0, fragTexCoord) * fragColor;
+ }
+ );
+#endif
+
+ RSGL_programBlob blob;
+ blob.vertex = defaultVShaderCode;
+ blob.vertexLen = sizeof(defaultVShaderCode);
+ blob.fragment = defaultFShaderCode;
+ blob.fragmentLen = sizeof(defaultFShaderCode );
+
+ return blob;
+}
+
+/*
+print matrix array code snippet
+ for (size_t iy = 0; iy < 4; iy++) {
+ for (size_t ix = 0; ix < 4; ix++) {
+ printf("%f, ", matrix[(iy * 4) + ix]);
+ }
+ printf("\n");
+ }
+
+ printf("\n\n");
+*/
+
+
+
+void RSGL_GL_initPtr(RSGL_glRenderer* ctx, void* proc) {
+ #if !defined(__EMSCRIPTEN__) && !defined(RSGL_NO_GL_LOADER)
+ if (RSGL_loadGLModern((RSGLloadfunc)proc)) {
+ #ifdef RSGL_DEBUG
+ printf("Failed to load an OpenGL 3.3 Context, reverting to OpenGL Legacy\n");
+ #endif
+ return;
+ }
+ #else
+ RSGL_UNUSED(proc);
+ #endif
+
+#ifdef RSGL_DEBUG
+ printf("OpenGL Vendor: %s\n", glGetString(GL_VENDOR));
+ printf("OpenGL Renderer: %s\n", glGetString(GL_RENDERER));
+ printf("OpenGL Version: %s\n", glGetString(GL_VERSION));
+ printf("GLSL Version: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
+#endif
+
+#if defined(RSGL_GLES3) || defined(RSGL_GL3)
+ glGenVertexArrays(1, &ctx->vao);
+#endif
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+}
+
+void RSGL_GL_freePtr(RSGL_glRenderer* ctx) {
+#if defined(RSGL_GLES3) || defined(RSGL_GL3)
+ glDeleteVertexArrays(0, &ctx->vao);
+#endif
+}
+
+void RSGL_GL_render(RSGL_glRenderer* ctx, const RSGL_renderPass* pass) {
+ glBindFramebuffer(GL_FRAMEBUFFER, pass->framebuffer);
+
+#if defined(RSGL_GLES3) || defined(RSGL_GL3)
+ glBindVertexArray(ctx->vao);
+#endif
+
+ glBindBuffer(GL_ARRAY_BUFFER, pass->buffers->vertex);
+ glEnableVertexAttribArray(pass->program->vertexPosition);
+ glVertexAttribPointer(pass->program->vertexPosition, 3, GL_FLOAT, 0, 0, 0);
+
+ glBindBuffer(GL_ARRAY_BUFFER, pass->buffers->texture);
+ glEnableVertexAttribArray(pass->program->vertexTexCoord);
+ glVertexAttribPointer(pass->program->vertexTexCoord, 2, GL_FLOAT, 0, 0, 0);
+
+ glBindBuffer(GL_ARRAY_BUFFER, pass->buffers->color);
+ glEnableVertexAttribArray(pass->program->vertexColor);
+ glVertexAttribPointer(pass->program->vertexColor, 4, GL_FLOAT, 0, 0, 0);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pass->buffers->elements);
+
+ glUseProgram(pass->program->program);
+ glUniformMatrix4fv(pass->program->perspectiveView, 1, GL_FALSE, pass->matrix);
+
+ u32 i;
+ for (i = 0; i < pass->buffers->batchCount; i++) {
+ GLenum mode = GL_TRIANGLES;
+ glBindTexture(GL_TEXTURE_2D, pass->buffers->batches[i].tex);
+
+ if (pass->buffers->batches[i].lineWidth)
+ glLineWidth(pass->buffers->batches[i].lineWidth);
+
+ glUniformMatrix4fv(pass->program->model, 1, GL_FALSE, pass->buffers->batches[i].matrix.m);
+
+ switch (pass->buffers->batches[i].type) {
+ case RSGL_TRIANGLES: mode = GL_TRIANGLES; break;
+ case RSGL_POINTS: mode = GL_POINTS; break;
+ case RSGL_LINES: mode = GL_LINES; break;
+ default: break;
+ }
+
+ glDrawElements(mode, (i32)pass->buffers->batches[i].elmCount, GL_UNSIGNED_SHORT, (void*)(pass->buffers->batches[i].elmStart * sizeof(u16)));
+ }
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glUseProgram(0);
+
+#if defined(RSGL_GLES3) || defined(RSGL_GL3)
+ glBindVertexArray(0);
+#endif
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+void RSGL_GL_scissorStart(RSGL_glRenderer* ctx, float x, float y, float w, float h, float renderer_height) {
+ glEnable(GL_SCISSOR_TEST);
+
+ glScissor(x, renderer_height - (y + h), w, h);
+}
+
+void RSGL_GL_scissorEnd(RSGL_glRenderer* ctx) {
+ glDisable(GL_SCISSOR_TEST);
+}
+
+GLuint RSGL_GL_textureFormatToNative(RSGL_textureFormat format) {
+ switch (format) {
+ case RSGL_formatRGB: return GL_RGB;
+ case RSGL_formatBGR: return GL_BGR;
+ case RSGL_formatRGBA: return GL_RGBA;
+ case RSGL_formatBGRA: return GL_BGRA;
+ case RSGL_formatRed: return GL_RED;
+ case RSGL_formatGrayscale: return GL_RED;
+ case RSGL_formatGrayscaleAlpha: return GL_RED;
+ default: break;
+ }
+
+ return GL_RGBA;
+}
+
+GLuint RSGL_GL_textureDataTypeToNative(RSGL_textureDataType type) {
+ switch (type) {
+ case RSGL_textureDataInt: return GL_UNSIGNED_BYTE;
+ case RSGL_textureDataFloat: return GL_FLOAT;
+ default: break;
+ }
+
+ return GL_UNSIGNED_BYTE;
+}
+
+GLuint RSGL_GL_textureFilterToNative(RSGL_textureFilter filter) {
+ switch (filter) {
+ case RSGL_filterNearest: return GL_NEAREST;
+ case RSGL_filterLinear: return GL_LINEAR;
+ default: break;
+ }
+
+ return GL_LINEAR;
+}
+
+/* textures / images */
+RSGL_texture RSGL_GL_createTexture(RSGL_glRenderer* ctx, const RSGL_textureBlob* blob) {
+ unsigned int id = 0;
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glGenTextures(1, &id);
+ glBindTexture(GL_TEXTURE_2D, id);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, RSGL_GL_textureFilterToNative(blob->minFilter));
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, RSGL_GL_textureFilterToNative(blob->magFilter));
+
+#ifndef RSGL_GLES2
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, blob->width);
+#endif
+
+#if defined(RSGL_GLES2)
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+#else
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+#endif
+
+ u32 dataFormat = RSGL_GL_textureFormatToNative(blob->dataFormat);
+ u32 textureFormat = RSGL_GL_textureFormatToNative(blob->textureFormat);
+ u32 dataType = RSGL_GL_textureDataTypeToNative(blob->dataType);
+
+#ifndef RSGL_GLES2
+ if (blob->dataFormat == RSGL_formatGrayscale) {
+ static GLint swizzleRgbaParams[4] = { GL_RED, GL_RED, GL_RED, GL_ONE };
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleRgbaParams);
+ } else if (blob->dataFormat == RSGL_formatGrayscaleAlpha) {
+ static GLint swizzleRgbaParams[4] = { GL_ONE, GL_ONE, GL_ONE, GL_RED };
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleRgbaParams);
+ }
+#endif
+
+ glTexImage2D(GL_TEXTURE_2D, 0, dataFormat, blob->width, blob->height, 0, textureFormat, dataType, blob->data);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ return id;
+}
+
+void RSGL_GL_copyToTexture(RSGL_glRenderer* ctx, RSGL_texture texture, size_t x, size_t y, const RSGL_textureBlob* blob) {
+ glBindTexture(GL_TEXTURE_2D, texture);
+
+#ifndef RSGL_GLES2
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, blob->width);
+#endif
+
+ u32 dataFormat = RSGL_GL_textureFormatToNative(blob->dataFormat);
+
+ u32 dataType = RSGL_GL_textureDataTypeToNative(blob->dataType);
+
+#ifndef RSGL_GLES2
+ if (blob->dataFormat == RSGL_formatGrayscale) {
+ static GLint swizzleRgbaParams[4] = { GL_RED, GL_RED, GL_RED, GL_ONE };
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleRgbaParams);
+ } else if (blob->dataFormat == RSGL_formatGrayscaleAlpha) {
+ static GLint swizzleRgbaParams[4] = { GL_ONE, GL_ONE, GL_ONE, GL_RED };
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleRgbaParams);
+ }
+#endif
+
+ glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, (i32)blob->width, (i32)blob->height, dataFormat, dataType, blob->data);
+ glBindTexture(GL_TEXTURE_2D, 0);
+}
+
+#ifndef GL_DEBUG_TYPE_ERROR
+#define GL_DEBUG_TYPE_ERROR 0x824C
+#define GL_DEBUG_OUTPUT 0x92E0
+#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242
+#define GL_COMPILE_STATUS 0x8B81
+#define GL_LINK_STATUS 0x8B82
+#define GL_INFO_LOG_LENGTH 0x8B84
+#endif
+
+#ifdef RSGL_DEBUG
+void RSGL_opengl_getError(void) {
+ GLenum err;
+ while ((err = glGetError()) != GL_NO_ERROR) {
+ switch (err) {
+ case GL_INVALID_ENUM:
+ printf("OpenGL error: GL_INVALID_ENUM\n");
+ break;
+ case GL_INVALID_VALUE:
+ printf("OpenGL error: GL_INVALID_VALUE\n");
+ break;
+ case GL_INVALID_OPERATION:
+ printf("OpenGL error: GL_INVALID_OPERATION\n");
+ break;
+ case GL_STACK_OVERFLOW:
+ printf("OpenGL error: GL_STACK_OVERFLOW\n");
+ break;
+ case GL_STACK_UNDERFLOW:
+ printf("OpenGL error: GL_STACK_UNDERFLOW\n");
+ break;
+ default:
+ printf("OpenGL error: Unknown error code 0x%x\n", err);
+ break;
+ }
+ }
+}
+
+
+void RSGL_debug_shader(u32 src, const char *shader, const char *action) {
+ GLint status;
+ if (action[0] == 'l')
+ glGetProgramiv(src, GL_LINK_STATUS, &status);
+ else
+ glGetShaderiv(src, GL_COMPILE_STATUS, &status);
+
+ if (status == GL_TRUE)
+ printf("%s Shader %s successfully.\n", shader, action);
+ else {
+ printf("%s Shader failed to %s.\n", shader, action);
+
+ GLchar infoLog[512];
+ if (action[0] == 'c') {
+ glGetShaderInfoLog(src, 512, NULL, infoLog);
+ printf("%s Shader info log:\n%s\n", shader, infoLog);
+ } else {
+ glGetProgramInfoLog(src, 512, NULL, infoLog);
+ printf("%s info log:\n%s\n", shader, infoLog);
+ }
+
+ RSGL_opengl_getError();
+ }
+}
+#endif
+
+RSGL_programInfo RSGL_GL_createProgram(RSGL_glRenderer* ctx, RSGL_programBlob* blob) {
+ RSGL_programInfo program;
+ u32 vShader, fShader;
+
+ /* compile vertex shader */
+ vShader = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(vShader, 1, &blob->vertex, NULL);
+ glCompileShader(vShader);
+
+#ifdef RSGL_DEBUG
+ RSGL_debug_shader(vShader, "Vertex", "compile");
+#endif
+
+ /* compile fragment shader */
+ fShader = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(fShader, 1, &blob->fragment, NULL);
+ glCompileShader(fShader);
+
+#ifdef RSGL_DEBUG
+ RSGL_debug_shader(fShader, "Fragment", "compile");
+#endif
+
+ /* create program and link vertex and fragment shaders */
+ program.program = glCreateProgram();
+
+ glAttachShader(program.program, vShader);
+ glAttachShader(program.program, fShader);
+ glLinkProgram(program.program);
+
+#ifdef RSGL_DEBUG
+ RSGL_debug_shader(program.program, "Program", "link");
+#endif
+
+ glDeleteShader(vShader);
+ glDeleteShader(fShader);
+
+ glUseProgram(program.program);
+
+ program.vertexPosition = glGetAttribLocation(program.program, "vertexPosition");
+ program.vertexTexCoord = glGetAttribLocation(program.program, "vertexTexCoord");
+ program.vertexColor = glGetAttribLocation(program.program, "vertexColor");
+
+ program.perspectiveView = glGetUniformLocation(program.program, "pv");
+ program.model = glGetUniformLocation(program.program, "model");
+
+ #ifdef RSGL_DEBUG
+ if (program.perspectiveView < 0 || program.model < 0) {
+ printf("Failed to locate the shader variables\n");
+ }
+ #endif
+
+ glUseProgram(0);
+
+ program.type = RSGL_shaderTypeStandard;
+ return program;
+}
+
+void RSGL_GL_deleteProgram(RSGL_glRenderer* ctx, const RSGL_programInfo* program) {
+ glUseProgram(0);
+ glDeleteProgram(program->program);
+}
+
+size_t RSGL_GL_findShaderArray(RSGL_glRenderer* ctx, const RSGL_programInfo* program, const char* var, const size_t len) {
+ glUseProgram(program->program);
+ int loc = glGetAttribLocation(program->program, var);
+ glUseProgram(0);
+ return loc;
+}
+
+size_t RSGL_GL_findShaderVariable(RSGL_glRenderer* ctx, const RSGL_programInfo* program, const char* var, const size_t len) {
+ glUseProgram(program->program);
+ int loc = glGetUniformLocation(program->program, var);
+ glUseProgram(0);
+ return loc;
+}
+
+void RSGL_GL_updateShaderVariable(RSGL_glRenderer* ctx, const RSGL_programInfo* program, size_t var, const float value[], u8 len) {
+ glUseProgram(program->program);
+ int loc = (int)var;
+
+ switch (len) {
+ case 1: glUniform1f(loc, value[0]); break;
+ case 2: glUniform2f(loc, value[0], value[1]); break;
+ case 3: glUniform3f(loc, value[0], value[1], value[2]); break;
+ case 4: glUniform4f(loc, value[0], value[1], value[2], value[3]); break;
+ case 16: glUniformMatrix4fv(loc, 1, GL_FALSE, value); break;
+ default: break;
+ }
+
+ glUseProgram(0);
+}
+
+RSGL_framebuffer RSGL_GL_createFramebuffer(RSGL_glRenderer* ctx, size_t width, size_t height) {
+ u32 result = 0;
+ glGenFramebuffers(1, &result);
+ return (RSGL_framebuffer)result;
+}
+
+void RSGL_GL_attachFramebuffer(RSGL_glRenderer* ctx, RSGL_framebuffer fbo, RSGL_texture tex, u8 attachType, u8 mipLevel) {
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+ if (attachType < 8)
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_TEXTURE_2D, tex, mipLevel);
+
+ if (attachType == 100)
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, tex, mipLevel);
+
+ if (attachType == 200)
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, tex, mipLevel);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+void RSGL_GL_deleteFramebuffer(RSGL_glRenderer* ctx, RSGL_framebuffer fbo) {
+ u32 value = fbo;
+ glDeleteFramebuffers(1, &value);
+}
+
+#ifdef RSGL_USE_COMPUTE
+
+#ifndef GL_RG8
+#define GL_RG8 0x822B
+#endif
+
+#ifndef GL_READ_WRITE
+#define GL_READ_WRITE 0x88BA
+#endif
+
+#ifndef GL_COMPUTE_SHADER
+#define GL_COMPUTE_SHADER 0x91B9
+#endif
+
+#ifndef GL_SHADER_IMAGE_ACCESS_BARRIER_BIT
+#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020
+#endif
+
+RSGL_programInfo RSGL_GL_createComputeProgram(RSGL_glRenderer* ctx, const char* CShaderCode) {
+ RSGL_programInfo program;
+ program.type = RSGL_shaderTypeCompute;
+
+ u32 compute = glCreateShader(GL_COMPUTE_SHADER);
+ glShaderSource(compute, 1, &CShaderCode, NULL);
+ glCompileShader(compute);
+
+#ifdef RSGL_DEBUG
+ RSGL_debug_shader(compute, "Compute", "compile");
+#endif
+
+ program.program = glCreateProgram();
+ glAttachShader(program.program, compute);
+ glLinkProgram(program.program);
+#ifdef RSGL_DEBUG
+ RSGL_debug_shader(program.program, "Program", "link");
+#endif
+
+ glDeleteShader(compute);
+
+ return program;
+}
+
+void RSGL_GL_dispatchComputeProgram(RSGL_glRenderer* ctx, RSGL_programInfo program, u32 groups_x, u32 groups_y, u32 groups_z) {
+ glUseProgram(program.program);
+ glDispatchCompute(groups_x, groups_y, groups_z);
+ glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
+}
+
+
+void RSGL_GL_bindComputeTexture(RSGL_glRenderer* ctx, u32 texture, u8 format) {
+ u16 c = 0;
+ switch (format) {
+ case 2: c = GL_RG8; break;
+ case 3: c = GL_RGB8; break;
+ case 4: c = GL_RGBA8; break;
+ default: break;
+ }
+ glBindImageTexture(0, texture, 0, GL_FALSE, 0, GL_READ_WRITE, c);
+}
+
+#endif
+
+#ifndef RSGL_NO_GL_LOADER
+int RSGL_loadGLModern(RSGLloadfunc proc) {
+ RSGL_PROC_DEF(proc, glShaderSource);
+ RSGL_PROC_DEF(proc, glCreateShader);
+ RSGL_PROC_DEF(proc, glCompileShader);
+ RSGL_PROC_DEF(proc, glCreateProgram);
+ RSGL_PROC_DEF(proc, glAttachShader);
+ RSGL_PROC_DEF(proc, glBindAttribLocation);
+ RSGL_PROC_DEF(proc, glLinkProgram);
+ RSGL_PROC_DEF(proc, glBindBuffer);
+ RSGL_PROC_DEF(proc, glBufferData);
+ RSGL_PROC_DEF(proc, glEnableVertexAttribArray);
+ RSGL_PROC_DEF(proc, glVertexAttribPointer);
+ RSGL_PROC_DEF(proc, glDisableVertexAttribArray);
+ RSGL_PROC_DEF(proc, glDeleteBuffers);
+ RSGL_PROC_DEF(proc, glUseProgram);
+ RSGL_PROC_DEF(proc, glDetachShader);
+ RSGL_PROC_DEF(proc, glDeleteShader);
+ RSGL_PROC_DEF(proc, glDeleteProgram);
+ RSGL_PROC_DEF(proc, glBufferSubData);
+ RSGL_PROC_DEF(proc, glGetShaderiv);
+ RSGL_PROC_DEF(proc, glGetShaderInfoLog);
+ RSGL_PROC_DEF(proc, glGetProgramiv);
+ RSGL_PROC_DEF(proc, glGetProgramInfoLog);
+ RSGL_PROC_DEF(proc, glGenBuffers);
+ RSGL_PROC_DEF(proc, glGetUniformLocation);
+ RSGL_PROC_DEF(proc, glGetAttribLocation);
+ RSGL_PROC_DEF(proc, glUniformMatrix4fv);
+ RSGL_PROC_DEF(proc, glActiveTexture);
+ RSGL_PROC_DEF(proc, glUniform1f);
+ RSGL_PROC_DEF(proc, glUniform2f);
+ RSGL_PROC_DEF(proc, glUniform3f);
+ RSGL_PROC_DEF(proc, glUniform4f);
+ RSGL_PROC_DEF(proc, glBindFramebuffer);
+ RSGL_PROC_DEF(proc, glGenFramebuffers);
+ RSGL_PROC_DEF(proc, glDeleteFramebuffers);
+ RSGL_PROC_DEF(proc, glFramebufferTexture2D);
+#if defined(RSGL_GLES3) || defined(RSGL_GL3)
+ RSGL_PROC_DEF(proc, glBindVertexArray);
+ RSGL_PROC_DEF(proc, glGenVertexArrays);
+ RSGL_PROC_DEF(proc, glDeleteVertexArrays);
+#endif
+#ifdef RSGL_USE_COMPUTE
+ RSGL_PROC_DEF(proc, glDispatchCompute);
+ RSGL_PROC_DEF(proc, glMemoryBarrier);
+ RSGL_PROC_DEF(proc, glBindImageTexture);
+#endif
+
+ if (
+ glShaderSourceSRC == NULL ||
+ glCreateShaderSRC == NULL ||
+ glCompileShaderSRC == NULL ||
+ glCreateProgramSRC == NULL ||
+ glAttachShaderSRC == NULL ||
+ glBindAttribLocationSRC == NULL ||
+ glLinkProgramSRC == NULL ||
+ glBindBufferSRC == NULL ||
+ glBufferDataSRC == NULL ||
+ glVertexAttribPointerSRC == NULL ||
+ glDisableVertexAttribArraySRC == NULL ||
+ glDeleteBuffersSRC == NULL ||
+ glUseProgramSRC == NULL ||
+ glDetachShaderSRC == NULL ||
+ glDeleteShaderSRC == NULL ||
+ glDeleteProgramSRC == NULL ||
+ glBufferSubDataSRC == NULL ||
+ glGetShaderivSRC == NULL ||
+ glGetShaderInfoLogSRC == NULL ||
+ glGetProgramivSRC == NULL ||
+ glGetProgramInfoLogSRC == NULL ||
+ glGenBuffersSRC == NULL ||
+ glGetUniformLocationSRC == NULL ||
+ glUniformMatrix4fvSRC == NULL ||
+ glBindFramebufferSRC == NULL ||
+ glGenFramebuffersSRC == NULL ||
+ glDeleteFramebuffersSRC == NULL ||
+ glFramebufferTexture2DSRC == NULL
+ )
+ return 1;
+ return 0;
+}
+#endif
+
+#endif /* RSGL_IMPLEMENTATION */
diff --git a/include/stb_image.h b/include/stb_image.h
new file mode 100644
index 0000000..9eedabe
--- /dev/null
+++ b/include/stb_image.h
@@ -0,0 +1,7988 @@
+/* stb_image - v2.30 - public domain image loader - http://nothings.org/stb
+ no warranty implied; use at your own risk
+
+ Do this:
+ #define STB_IMAGE_IMPLEMENTATION
+ before you include this file in *one* C or C++ file to create the implementation.
+
+ // i.e. it should look like this:
+ #include ...
+ #include ...
+ #include ...
+ #define STB_IMAGE_IMPLEMENTATION
+ #include "stb_image.h"
+
+ You can #define STBI_ASSERT(x) before the #include to avoid using assert.h.
+ And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free
+
+
+ QUICK NOTES:
+ Primarily of interest to game developers and other people who can
+ avoid problematic images and only need the trivial interface
+
+ JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib)
+ PNG 1/2/4/8/16-bit-per-channel
+
+ TGA (not sure what subset, if a subset)
+ BMP non-1bpp, non-RLE
+ PSD (composited view only, no extra channels, 8/16 bit-per-channel)
+
+ GIF (*comp always reports as 4-channel)
+ HDR (radiance rgbE format)
+ PIC (Softimage PIC)
+ PNM (PPM and PGM binary only)
+
+ Animated GIF still needs a proper API, but here's one way to do it:
+ http://gist.github.com/urraka/685d9a6340b26b830d49
+
+ - decode from memory or through FILE (define STBI_NO_STDIO to remove code)
+ - decode from arbitrary I/O callbacks
+ - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON)
+
+ Full documentation under "DOCUMENTATION" below.
+
+
+LICENSE
+
+ See end of file for license information.
+
+RECENT REVISION HISTORY:
+
+ 2.30 (2024-05-31) avoid erroneous gcc warning
+ 2.29 (2023-05-xx) optimizations
+ 2.28 (2023-01-29) many error fixes, security errors, just tons of stuff
+ 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes
+ 2.26 (2020-07-13) many minor fixes
+ 2.25 (2020-02-02) fix warnings
+ 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically
+ 2.23 (2019-08-11) fix clang static analysis warning
+ 2.22 (2019-03-04) gif fixes, fix warnings
+ 2.21 (2019-02-25) fix typo in comment
+ 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs
+ 2.19 (2018-02-11) fix warning
+ 2.18 (2018-01-30) fix warnings
+ 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings
+ 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes
+ 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC
+ 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs
+ 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes
+ 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes
+ 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64
+ RGB-format JPEG; remove white matting in PSD;
+ allocate large structures on the stack;
+ correct channel count for PNG & BMP
+ 2.10 (2016-01-22) avoid warning introduced in 2.09
+ 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED
+
+ See end of file for full revision history.
+
+
+ ============================ Contributors =========================
+
+ Image formats Extensions, features
+ Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info)
+ Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info)
+ Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG)
+ Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks)
+ Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG)
+ Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip)
+ Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD)
+ github:urraka (animated gif) Junggon Kim (PNM comments)
+ Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA)
+ socks-the-fox (16-bit PNG)
+ Jeremy Sawicki (handle all ImageNet JPGs)
+ Optimizations & bugfixes Mikhail Morozov (1-bit BMP)
+ Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query)
+ Arseny Kapoulkine Simon Breuss (16-bit PNM)
+ John-Mark Allen
+ Carmelo J Fdez-Aguera
+
+ Bug & warning fixes
+ Marc LeBlanc David Woo Guillaume George Martins Mozeiko
+ Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski
+ Phil Jordan Dave Moore Roy Eltham
+ Hayaki Saito Nathan Reed Won Chun
+ Luke Graham Johan Duparc Nick Verigakis the Horde3D community
+ Thomas Ruf Ronny Chevalier github:rlyeh
+ Janez Zemva John Bartholomew Michal Cichon github:romigrou
+ Jonathan Blow Ken Hamada Tero Hanninen github:svdijk
+ Eugene Golushkov Laurent Gomila Cort Stratton github:snagar
+ Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex
+ Cass Everitt Ryamond Barbiero github:grim210
+ Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw
+ Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus
+ Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo
+ Julian Raschke Gregory Mullen Christian Floisand github:darealshinji
+ Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007
+ Brad Weinberger Matvey Cherevko github:mosra
+ Luca Sas Alexander Veselov Zack Middleton [reserved]
+ Ryan C. Gordon [reserved] [reserved]
+ DO NOT ADD YOUR NAME HERE
+
+ Jacko Dirks
+
+ To add your name to the credits, pick a random blank space in the middle and fill it.
+ 80% of merge conflicts on stb PRs are due to people adding their name at the end
+ of the credits.
+*/
+
+#ifndef STBI_INCLUDE_STB_IMAGE_H
+#define STBI_INCLUDE_STB_IMAGE_H
+
+// DOCUMENTATION
+//
+// Limitations:
+// - no 12-bit-per-channel JPEG
+// - no JPEGs with arithmetic coding
+// - GIF always returns *comp=4
+//
+// Basic usage (see HDR discussion below for HDR usage):
+// int x,y,n;
+// unsigned char *data = stbi_load(filename, &x, &y, &n, 0);
+// // ... process data if not NULL ...
+// // ... x = width, y = height, n = # 8-bit components per pixel ...
+// // ... replace '0' with '1'..'4' to force that many components per pixel
+// // ... but 'n' will always be the number that it would have been if you said 0
+// stbi_image_free(data);
+//
+// Standard parameters:
+// int *x -- outputs image width in pixels
+// int *y -- outputs image height in pixels
+// int *channels_in_file -- outputs # of image components in image file
+// int desired_channels -- if non-zero, # of image components requested in result
+//
+// The return value from an image loader is an 'unsigned char *' which points
+// to the pixel data, or NULL on an allocation failure or if the image is
+// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels,
+// with each pixel consisting of N interleaved 8-bit components; the first
+// pixel pointed to is top-left-most in the image. There is no padding between
+// image scanlines or between pixels, regardless of format. The number of
+// components N is 'desired_channels' if desired_channels is non-zero, or
+// *channels_in_file otherwise. If desired_channels is non-zero,
+// *channels_in_file has the number of components that _would_ have been
+// output otherwise. E.g. if you set desired_channels to 4, you will always
+// get RGBA output, but you can check *channels_in_file to see if it's trivially
+// opaque because e.g. there were only 3 channels in the source image.
+//
+// An output image with N components has the following components interleaved
+// in this order in each pixel:
+//
+// N=#comp components
+// 1 grey
+// 2 grey, alpha
+// 3 red, green, blue
+// 4 red, green, blue, alpha
+//
+// If image loading fails for any reason, the return value will be NULL,
+// and *x, *y, *channels_in_file will be unchanged. The function
+// stbi_failure_reason() can be queried for an extremely brief, end-user
+// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS
+// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly
+// more user-friendly ones.
+//
+// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized.
+//
+// To query the width, height and component count of an image without having to
+// decode the full file, you can use the stbi_info family of functions:
+//
+// int x,y,n,ok;
+// ok = stbi_info(filename, &x, &y, &n);
+// // returns ok=1 and sets x, y, n if image is a supported format,
+// // 0 otherwise.
+//
+// Note that stb_image pervasively uses ints in its public API for sizes,
+// including sizes of memory buffers. This is now part of the API and thus
+// hard to change without causing breakage. As a result, the various image
+// loaders all have certain limits on image size; these differ somewhat
+// by format but generally boil down to either just under 2GB or just under
+// 1GB. When the decoded image would be larger than this, stb_image decoding
+// will fail.
+//
+// Additionally, stb_image will reject image files that have any of their
+// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS,
+// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit,
+// the only way to have an image with such dimensions load correctly
+// is for it to have a rather extreme aspect ratio. Either way, the
+// assumption here is that such larger images are likely to be malformed
+// or malicious. If you do need to load an image with individual dimensions
+// larger than that, and it still fits in the overall size limit, you can
+// #define STBI_MAX_DIMENSIONS on your own to be something larger.
+//
+// ===========================================================================
+//
+// UNICODE:
+//
+// If compiling for Windows and you wish to use Unicode filenames, compile
+// with
+// #define STBI_WINDOWS_UTF8
+// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert
+// Windows wchar_t filenames to utf8.
+//
+// ===========================================================================
+//
+// Philosophy
+//
+// stb libraries are designed with the following priorities:
+//
+// 1. easy to use
+// 2. easy to maintain
+// 3. good performance
+//
+// Sometimes I let "good performance" creep up in priority over "easy to maintain",
+// and for best performance I may provide less-easy-to-use APIs that give higher
+// performance, in addition to the easy-to-use ones. Nevertheless, it's important
+// to keep in mind that from the standpoint of you, a client of this library,
+// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all.
+//
+// Some secondary priorities arise directly from the first two, some of which
+// provide more explicit reasons why performance can't be emphasized.
+//
+// - Portable ("ease of use")
+// - Small source code footprint ("easy to maintain")
+// - No dependencies ("ease of use")
+//
+// ===========================================================================
+//
+// I/O callbacks
+//
+// I/O callbacks allow you to read from arbitrary sources, like packaged
+// files or some other source. Data read from callbacks are processed
+// through a small internal buffer (currently 128 bytes) to try to reduce
+// overhead.
+//
+// The three functions you must define are "read" (reads some bytes of data),
+// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end).
+//
+// ===========================================================================
+//
+// SIMD support
+//
+// The JPEG decoder will try to automatically use SIMD kernels on x86 when
+// supported by the compiler. For ARM Neon support, you must explicitly
+// request it.
+//
+// (The old do-it-yourself SIMD API is no longer supported in the current
+// code.)
+//
+// On x86, SSE2 will automatically be used when available based on a run-time
+// test; if not, the generic C versions are used as a fall-back. On ARM targets,
+// the typical path is to have separate builds for NEON and non-NEON devices
+// (at least this is true for iOS and Android). Therefore, the NEON support is
+// toggled by a build flag: define STBI_NEON to get NEON loops.
+//
+// If for some reason you do not want to use any of SIMD code, or if
+// you have issues compiling it, you can disable it entirely by
+// defining STBI_NO_SIMD.
+//
+// ===========================================================================
+//
+// HDR image support (disable by defining STBI_NO_HDR)
+//
+// stb_image supports loading HDR images in general, and currently the Radiance
+// .HDR file format specifically. You can still load any file through the existing
+// interface; if you attempt to load an HDR file, it will be automatically remapped
+// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1;
+// both of these constants can be reconfigured through this interface:
+//
+// stbi_hdr_to_ldr_gamma(2.2f);
+// stbi_hdr_to_ldr_scale(1.0f);
+//
+// (note, do not use _inverse_ constants; stbi_image will invert them
+// appropriately).
+//
+// Additionally, there is a new, parallel interface for loading files as
+// (linear) floats to preserve the full dynamic range:
+//
+// float *data = stbi_loadf(filename, &x, &y, &n, 0);
+//
+// If you load LDR images through this interface, those images will
+// be promoted to floating point values, run through the inverse of
+// constants corresponding to the above:
+//
+// stbi_ldr_to_hdr_scale(1.0f);
+// stbi_ldr_to_hdr_gamma(2.2f);
+//
+// Finally, given a filename (or an open file or memory block--see header
+// file for details) containing image data, you can query for the "most
+// appropriate" interface to use (that is, whether the image is HDR or
+// not), using:
+//
+// stbi_is_hdr(char *filename);
+//
+// ===========================================================================
+//
+// iPhone PNG support:
+//
+// We optionally support converting iPhone-formatted PNGs (which store
+// premultiplied BGRA) back to RGB, even though they're internally encoded
+// differently. To enable this conversion, call
+// stbi_convert_iphone_png_to_rgb(1).
+//
+// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per
+// pixel to remove any premultiplied alpha *only* if the image file explicitly
+// says there's premultiplied data (currently only happens in iPhone images,
+// and only if iPhone convert-to-rgb processing is on).
+//
+// ===========================================================================
+//
+// ADDITIONAL CONFIGURATION
+//
+// - You can suppress implementation of any of the decoders to reduce
+// your code footprint by #defining one or more of the following
+// symbols before creating the implementation.
+//
+// STBI_NO_JPEG
+// STBI_NO_PNG
+// STBI_NO_BMP
+// STBI_NO_PSD
+// STBI_NO_TGA
+// STBI_NO_GIF
+// STBI_NO_HDR
+// STBI_NO_PIC
+// STBI_NO_PNM (.ppm and .pgm)
+//
+// - You can request *only* certain decoders and suppress all other ones
+// (this will be more forward-compatible, as addition of new decoders
+// doesn't require you to disable them explicitly):
+//
+// STBI_ONLY_JPEG
+// STBI_ONLY_PNG
+// STBI_ONLY_BMP
+// STBI_ONLY_PSD
+// STBI_ONLY_TGA
+// STBI_ONLY_GIF
+// STBI_ONLY_HDR
+// STBI_ONLY_PIC
+// STBI_ONLY_PNM (.ppm and .pgm)
+//
+// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still
+// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB
+//
+// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater
+// than that size (in either width or height) without further processing.
+// This is to let programs in the wild set an upper bound to prevent
+// denial-of-service attacks on untrusted data, as one could generate a
+// valid image of gigantic dimensions and force stb_image to allocate a
+// huge block of memory and spend disproportionate time decoding it. By
+// default this is set to (1 << 24), which is 16777216, but that's still
+// very big.
+
+#ifndef STBI_NO_STDIO
+#include <stdio.h>
+#endif // STBI_NO_STDIO
+
+#define STBI_VERSION 1
+
+enum
+{
+ STBI_default = 0, // only used for desired_channels
+
+ STBI_grey = 1,
+ STBI_grey_alpha = 2,
+ STBI_rgb = 3,
+ STBI_rgb_alpha = 4
+};
+
+#include <stdlib.h>
+typedef unsigned char stbi_uc;
+typedef unsigned short stbi_us;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef STBIDEF
+#ifdef STB_IMAGE_STATIC
+#define STBIDEF static
+#else
+#define STBIDEF extern
+#endif
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// PRIMARY API - works on images of any type
+//
+
+//
+// load image by filename, open file, or memory buffer
+//
+
+typedef struct
+{
+ int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read
+ void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative
+ int (*eof) (void *user); // returns nonzero if we are at end of file/data
+} stbi_io_callbacks;
+
+////////////////////////////////////
+//
+// 8-bits-per-channel interface
+//
+
+STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels);
+STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels);
+
+#ifndef STBI_NO_STDIO
+STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);
+STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);
+// for stbi_load_from_file, file pointer is left pointing immediately after image
+#endif
+
+#ifndef STBI_NO_GIF
+STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp);
+#endif
+
+#ifdef STBI_WINDOWS_UTF8
+STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input);
+#endif
+
+////////////////////////////////////
+//
+// 16-bits-per-channel interface
+//
+
+STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels);
+STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels);
+
+#ifndef STBI_NO_STDIO
+STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);
+STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);
+#endif
+
+////////////////////////////////////
+//
+// float-per-channel interface
+//
+#ifndef STBI_NO_LINEAR
+ STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels);
+ STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels);
+
+ #ifndef STBI_NO_STDIO
+ STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);
+ STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);
+ #endif
+#endif
+
+#ifndef STBI_NO_HDR
+ STBIDEF void stbi_hdr_to_ldr_gamma(float gamma);
+ STBIDEF void stbi_hdr_to_ldr_scale(float scale);
+#endif // STBI_NO_HDR
+
+#ifndef STBI_NO_LINEAR
+ STBIDEF void stbi_ldr_to_hdr_gamma(float gamma);
+ STBIDEF void stbi_ldr_to_hdr_scale(float scale);
+#endif // STBI_NO_LINEAR
+
+// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR
+STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user);
+STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len);
+#ifndef STBI_NO_STDIO
+STBIDEF int stbi_is_hdr (char const *filename);
+STBIDEF int stbi_is_hdr_from_file(FILE *f);
+#endif // STBI_NO_STDIO
+
+
+// get a VERY brief reason for failure
+// on most compilers (and ALL modern mainstream compilers) this is threadsafe
+STBIDEF const char *stbi_failure_reason (void);
+
+// free the loaded image -- this is just free()
+STBIDEF void stbi_image_free (void *retval_from_stbi_load);
+
+// get image dimensions & components without fully decoding
+STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
+STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp);
+STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len);
+STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user);
+
+#ifndef STBI_NO_STDIO
+STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp);
+STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp);
+STBIDEF int stbi_is_16_bit (char const *filename);
+STBIDEF int stbi_is_16_bit_from_file(FILE *f);
+#endif
+
+
+
+// for image formats that explicitly notate that they have premultiplied alpha,
+// we just return the colors as stored in the file. set this flag to force
+// unpremultiplication. results are undefined if the unpremultiply overflow.
+STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply);
+
+// indicate whether we should process iphone images back to canonical format,
+// or just pass them through "as-is"
+STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert);
+
+// flip the image vertically, so the first pixel in the output array is the bottom left
+STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip);
+
+// as above, but only applies to images loaded on the thread that calls the function
+// this function is only available if your compiler supports thread-local variables;
+// calling it will fail to link if your compiler doesn't
+STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply);
+STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert);
+STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip);
+
+// ZLIB client - used by PNG, available for other purposes
+
+STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen);
+STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header);
+STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen);
+STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
+
+STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen);
+STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+//
+//
+//// end header file /////////////////////////////////////////////////////
+#endif // STBI_INCLUDE_STB_IMAGE_H
+
+#ifdef STB_IMAGE_IMPLEMENTATION
+
+#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \
+ || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \
+ || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \
+ || defined(STBI_ONLY_ZLIB)
+ #ifndef STBI_ONLY_JPEG
+ #define STBI_NO_JPEG
+ #endif
+ #ifndef STBI_ONLY_PNG
+ #define STBI_NO_PNG
+ #endif
+ #ifndef STBI_ONLY_BMP
+ #define STBI_NO_BMP
+ #endif
+ #ifndef STBI_ONLY_PSD
+ #define STBI_NO_PSD
+ #endif
+ #ifndef STBI_ONLY_TGA
+ #define STBI_NO_TGA
+ #endif
+ #ifndef STBI_ONLY_GIF
+ #define STBI_NO_GIF
+ #endif
+ #ifndef STBI_ONLY_HDR
+ #define STBI_NO_HDR
+ #endif
+ #ifndef STBI_ONLY_PIC
+ #define STBI_NO_PIC
+ #endif
+ #ifndef STBI_ONLY_PNM
+ #define STBI_NO_PNM
+ #endif
+#endif
+
+#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB)
+#define STBI_NO_ZLIB
+#endif
+
+
+#include <stdarg.h>
+#include <stddef.h> // ptrdiff_t on osx
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
+#include <math.h> // ldexp, pow
+#endif
+
+#ifndef STBI_NO_STDIO
+#include <stdio.h>
+#endif
+
+#ifndef STBI_ASSERT
+#include <assert.h>
+#define STBI_ASSERT(x) assert(x)
+#endif
+
+#ifdef __cplusplus
+#define STBI_EXTERN extern "C"
+#else
+#define STBI_EXTERN extern
+#endif
+
+
+#ifndef _MSC_VER
+ #ifdef __cplusplus
+ #define stbi_inline inline
+ #else
+ #define stbi_inline
+ #endif
+#else
+ #define stbi_inline __forceinline
+#endif
+
+#ifndef STBI_NO_THREAD_LOCALS
+ #if defined(__cplusplus) && __cplusplus >= 201103L
+ #define STBI_THREAD_LOCAL thread_local
+ #elif defined(__GNUC__) && __GNUC__ < 5
+ #define STBI_THREAD_LOCAL __thread
+ #elif defined(_MSC_VER)
+ #define STBI_THREAD_LOCAL __declspec(thread)
+ #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__)
+ #define STBI_THREAD_LOCAL _Thread_local
+ #endif
+
+ #ifndef STBI_THREAD_LOCAL
+ #if defined(__GNUC__)
+ #define STBI_THREAD_LOCAL __thread
+ #endif
+ #endif
+#endif
+
+#if defined(_MSC_VER) || defined(__SYMBIAN32__)
+typedef unsigned short stbi__uint16;
+typedef signed short stbi__int16;
+typedef unsigned int stbi__uint32;
+typedef signed int stbi__int32;
+#else
+#include <stdint.h>
+typedef uint16_t stbi__uint16;
+typedef int16_t stbi__int16;
+typedef uint32_t stbi__uint32;
+typedef int32_t stbi__int32;
+#endif
+
+// should produce compiler error if size is wrong
+typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];
+
+#ifdef _MSC_VER
+#define STBI_NOTUSED(v) (void)(v)
+#else
+#define STBI_NOTUSED(v) (void)sizeof(v)
+#endif
+
+#ifdef _MSC_VER
+#define STBI_HAS_LROTL
+#endif
+
+#ifdef STBI_HAS_LROTL
+ #define stbi_lrot(x,y) _lrotl(x,y)
+#else
+ #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31)))
+#endif
+
+#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED))
+// ok
+#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED)
+// ok
+#else
+#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)."
+#endif
+
+#ifndef STBI_MALLOC
+#define STBI_MALLOC(sz) malloc(sz)
+#define STBI_REALLOC(p,newsz) realloc(p,newsz)
+#define STBI_FREE(p) free(p)
+#endif
+
+#ifndef STBI_REALLOC_SIZED
+#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz)
+#endif
+
+// x86/x64 detection
+#if defined(__x86_64__) || defined(_M_X64)
+#define STBI__X64_TARGET
+#elif defined(__i386) || defined(_M_IX86)
+#define STBI__X86_TARGET
+#endif
+
+#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD)
+// gcc doesn't support sse2 intrinsics unless you compile with -msse2,
+// which in turn means it gets to use SSE2 everywhere. This is unfortunate,
+// but previous attempts to provide the SSE2 functions with runtime
+// detection caused numerous issues. The way architecture extensions are
+// exposed in GCC/Clang is, sadly, not really suited for one-file libs.
+// New behavior: if compiled with -msse2, we use SSE2 without any
+// detection; if not, we don't use it at all.
+#define STBI_NO_SIMD
+#endif
+
+#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD)
+// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET
+//
+// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the
+// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant.
+// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not
+// simultaneously enabling "-mstackrealign".
+//
+// See https://github.com/nothings/stb/issues/81 for more information.
+//
+// So default to no SSE2 on 32-bit MinGW. If you've read this far and added
+// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2.
+#define STBI_NO_SIMD
+#endif
+
+#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET))
+#define STBI_SSE2
+#include <emmintrin.h>
+
+#ifdef _MSC_VER
+
+#if _MSC_VER >= 1400 // not VC6
+#include <intrin.h> // __cpuid
+static int stbi__cpuid3(void)
+{
+ int info[4];
+ __cpuid(info,1);
+ return info[3];
+}
+#else
+static int stbi__cpuid3(void)
+{
+ int res;
+ __asm {
+ mov eax,1
+ cpuid
+ mov res,edx
+ }
+ return res;
+}
+#endif
+
+#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name
+
+#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2)
+static int stbi__sse2_available(void)
+{
+ int info3 = stbi__cpuid3();
+ return ((info3 >> 26) & 1) != 0;
+}
+#endif
+
+#else // assume GCC-style if not VC++
+#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
+
+#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2)
+static int stbi__sse2_available(void)
+{
+ // If we're even attempting to compile this on GCC/Clang, that means
+ // -msse2 is on, which means the compiler is allowed to use SSE2
+ // instructions at will, and so are we.
+ return 1;
+}
+#endif
+
+#endif
+#endif
+
+// ARM NEON
+#if defined(STBI_NO_SIMD) && defined(STBI_NEON)
+#undef STBI_NEON
+#endif
+
+#ifdef STBI_NEON
+#include <arm_neon.h>
+#ifdef _MSC_VER
+#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name
+#else
+#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
+#endif
+#endif
+
+#ifndef STBI_SIMD_ALIGN
+#define STBI_SIMD_ALIGN(type, name) type name
+#endif
+
+#ifndef STBI_MAX_DIMENSIONS
+#define STBI_MAX_DIMENSIONS (1 << 24)
+#endif
+
+///////////////////////////////////////////////
+//
+// stbi__context struct and start_xxx functions
+
+// stbi__context structure is our basic context used by all images, so it
+// contains all the IO context, plus some basic image information
+typedef struct
+{
+ stbi__uint32 img_x, img_y;
+ int img_n, img_out_n;
+
+ stbi_io_callbacks io;
+ void *io_user_data;
+
+ int read_from_callbacks;
+ int buflen;
+ stbi_uc buffer_start[128];
+ int callback_already_read;
+
+ stbi_uc *img_buffer, *img_buffer_end;
+ stbi_uc *img_buffer_original, *img_buffer_original_end;
+} stbi__context;
+
+
+static void stbi__refill_buffer(stbi__context *s);
+
+// initialize a memory-decode context
+static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len)
+{
+ s->io.read = NULL;
+ s->read_from_callbacks = 0;
+ s->callback_already_read = 0;
+ s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer;
+ s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len;
+}
+
+// initialize a callback-based context
+static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user)
+{
+ s->io = *c;
+ s->io_user_data = user;
+ s->buflen = sizeof(s->buffer_start);
+ s->read_from_callbacks = 1;
+ s->callback_already_read = 0;
+ s->img_buffer = s->img_buffer_original = s->buffer_start;
+ stbi__refill_buffer(s);
+ s->img_buffer_original_end = s->img_buffer_end;
+}
+
+#ifndef STBI_NO_STDIO
+
+static int stbi__stdio_read(void *user, char *data, int size)
+{
+ return (int) fread(data,1,size,(FILE*) user);
+}
+
+static void stbi__stdio_skip(void *user, int n)
+{
+ int ch;
+ fseek((FILE*) user, n, SEEK_CUR);
+ ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */
+ if (ch != EOF) {
+ ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */
+ }
+}
+
+static int stbi__stdio_eof(void *user)
+{
+ return feof((FILE*) user) || ferror((FILE *) user);
+}
+
+static stbi_io_callbacks stbi__stdio_callbacks =
+{
+ stbi__stdio_read,
+ stbi__stdio_skip,
+ stbi__stdio_eof,
+};
+
+static void stbi__start_file(stbi__context *s, FILE *f)
+{
+ stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f);
+}
+
+//static void stop_file(stbi__context *s) { }
+
+#endif // !STBI_NO_STDIO
+
+static void stbi__rewind(stbi__context *s)
+{
+ // conceptually rewind SHOULD rewind to the beginning of the stream,
+ // but we just rewind to the beginning of the initial buffer, because
+ // we only use it after doing 'test', which only ever looks at at most 92 bytes
+ s->img_buffer = s->img_buffer_original;
+ s->img_buffer_end = s->img_buffer_original_end;
+}
+
+enum
+{
+ STBI_ORDER_RGB,
+ STBI_ORDER_BGR
+};
+
+typedef struct
+{
+ int bits_per_channel;
+ int num_channels;
+ int channel_order;
+} stbi__result_info;
+
+#ifndef STBI_NO_JPEG
+static int stbi__jpeg_test(stbi__context *s);
+static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
+static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp);
+#endif
+
+#ifndef STBI_NO_PNG
+static int stbi__png_test(stbi__context *s);
+static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
+static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp);
+static int stbi__png_is16(stbi__context *s);
+#endif
+
+#ifndef STBI_NO_BMP
+static int stbi__bmp_test(stbi__context *s);
+static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
+static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp);
+#endif
+
+#ifndef STBI_NO_TGA
+static int stbi__tga_test(stbi__context *s);
+static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
+static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp);
+#endif
+
+#ifndef STBI_NO_PSD
+static int stbi__psd_test(stbi__context *s);
+static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc);
+static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp);
+static int stbi__psd_is16(stbi__context *s);
+#endif
+
+#ifndef STBI_NO_HDR
+static int stbi__hdr_test(stbi__context *s);
+static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
+static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp);
+#endif
+
+#ifndef STBI_NO_PIC
+static int stbi__pic_test(stbi__context *s);
+static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
+static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp);
+#endif
+
+#ifndef STBI_NO_GIF
+static int stbi__gif_test(stbi__context *s);
+static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
+static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp);
+static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp);
+#endif
+
+#ifndef STBI_NO_PNM
+static int stbi__pnm_test(stbi__context *s);
+static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
+static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp);
+static int stbi__pnm_is16(stbi__context *s);
+#endif
+
+static
+#ifdef STBI_THREAD_LOCAL
+STBI_THREAD_LOCAL
+#endif
+const char *stbi__g_failure_reason;
+
+STBIDEF const char *stbi_failure_reason(void)
+{
+ return stbi__g_failure_reason;
+}
+
+#ifndef STBI_NO_FAILURE_STRINGS
+static int stbi__err(const char *str)
+{
+ stbi__g_failure_reason = str;
+ return 0;
+}
+#endif
+
+static void *stbi__malloc(size_t size)
+{
+ return STBI_MALLOC(size);
+}
+
+// stb_image uses ints pervasively, including for offset calculations.
+// therefore the largest decoded image size we can support with the
+// current code, even on 64-bit targets, is INT_MAX. this is not a
+// significant limitation for the intended use case.
+//
+// we do, however, need to make sure our size calculations don't
+// overflow. hence a few helper functions for size calculations that
+// multiply integers together, making sure that they're non-negative
+// and no overflow occurs.
+
+// return 1 if the sum is valid, 0 on overflow.
+// negative terms are considered invalid.
+static int stbi__addsizes_valid(int a, int b)
+{
+ if (b < 0) return 0;
+ // now 0 <= b <= INT_MAX, hence also
+ // 0 <= INT_MAX - b <= INTMAX.
+ // And "a + b <= INT_MAX" (which might overflow) is the
+ // same as a <= INT_MAX - b (no overflow)
+ return a <= INT_MAX - b;
+}
+
+// returns 1 if the product is valid, 0 on overflow.
+// negative factors are considered invalid.
+static int stbi__mul2sizes_valid(int a, int b)
+{
+ if (a < 0 || b < 0) return 0;
+ if (b == 0) return 1; // mul-by-0 is always safe
+ // portable way to check for no overflows in a*b
+ return a <= INT_MAX/b;
+}
+
+#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR)
+// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow
+static int stbi__mad2sizes_valid(int a, int b, int add)
+{
+ return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add);
+}
+#endif
+
+// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow
+static int stbi__mad3sizes_valid(int a, int b, int c, int add)
+{
+ return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) &&
+ stbi__addsizes_valid(a*b*c, add);
+}
+
+// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow
+#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM)
+static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add)
+{
+ return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) &&
+ stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add);
+}
+#endif
+
+#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR)
+// mallocs with size overflow checking
+static void *stbi__malloc_mad2(int a, int b, int add)
+{
+ if (!stbi__mad2sizes_valid(a, b, add)) return NULL;
+ return stbi__malloc(a*b + add);
+}
+#endif
+
+static void *stbi__malloc_mad3(int a, int b, int c, int add)
+{
+ if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL;
+ return stbi__malloc(a*b*c + add);
+}
+
+#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM)
+static void *stbi__malloc_mad4(int a, int b, int c, int d, int add)
+{
+ if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL;
+ return stbi__malloc(a*b*c*d + add);
+}
+#endif
+
+// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow.
+static int stbi__addints_valid(int a, int b)
+{
+ if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow
+ if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0.
+ return a <= INT_MAX - b;
+}
+
+// returns 1 if the product of two ints fits in a signed short, 0 on overflow.
+static int stbi__mul2shorts_valid(int a, int b)
+{
+ if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow
+ if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid
+ if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN
+ return a >= SHRT_MIN / b;
+}
+
+// stbi__err - error
+// stbi__errpf - error returning pointer to float
+// stbi__errpuc - error returning pointer to unsigned char
+
+#ifdef STBI_NO_FAILURE_STRINGS
+ #define stbi__err(x,y) 0
+#elif defined(STBI_FAILURE_USERMSG)
+ #define stbi__err(x,y) stbi__err(y)
+#else
+ #define stbi__err(x,y) stbi__err(x)
+#endif
+
+#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL))
+#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL))
+
+STBIDEF void stbi_image_free(void *retval_from_stbi_load)
+{
+ STBI_FREE(retval_from_stbi_load);
+}
+
+#ifndef STBI_NO_LINEAR
+static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp);
+#endif
+
+#ifndef STBI_NO_HDR
+static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp);
+#endif
+
+static int stbi__vertically_flip_on_load_global = 0;
+
+STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip)
+{
+ stbi__vertically_flip_on_load_global = flag_true_if_should_flip;
+}
+
+#ifndef STBI_THREAD_LOCAL
+#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global
+#else
+static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set;
+
+STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip)
+{
+ stbi__vertically_flip_on_load_local = flag_true_if_should_flip;
+ stbi__vertically_flip_on_load_set = 1;
+}
+
+#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \
+ ? stbi__vertically_flip_on_load_local \
+ : stbi__vertically_flip_on_load_global)
+#endif // STBI_THREAD_LOCAL
+
+static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc)
+{
+ memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields
+ ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed
+ ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order
+ ri->num_channels = 0;
+
+ // test the formats with a very explicit header first (at least a FOURCC
+ // or distinctive magic number first)
+ #ifndef STBI_NO_PNG
+ if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri);
+ #endif
+ #ifndef STBI_NO_BMP
+ if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri);
+ #endif
+ #ifndef STBI_NO_GIF
+ if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri);
+ #endif
+ #ifndef STBI_NO_PSD
+ if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc);
+ #else
+ STBI_NOTUSED(bpc);
+ #endif
+ #ifndef STBI_NO_PIC
+ if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri);
+ #endif
+
+ // then the formats that can end up attempting to load with just 1 or 2
+ // bytes matching expectations; these are prone to false positives, so
+ // try them later
+ #ifndef STBI_NO_JPEG
+ if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri);
+ #endif
+ #ifndef STBI_NO_PNM
+ if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri);
+ #endif
+
+ #ifndef STBI_NO_HDR
+ if (stbi__hdr_test(s)) {
+ float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri);
+ return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);
+ }
+ #endif
+
+ #ifndef STBI_NO_TGA
+ // test tga last because it's a crappy test!
+ if (stbi__tga_test(s))
+ return stbi__tga_load(s,x,y,comp,req_comp, ri);
+ #endif
+
+ return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt");
+}
+
+static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels)
+{
+ int i;
+ int img_len = w * h * channels;
+ stbi_uc *reduced;
+
+ reduced = (stbi_uc *) stbi__malloc(img_len);
+ if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory");
+
+ for (i = 0; i < img_len; ++i)
+ reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling
+
+ STBI_FREE(orig);
+ return reduced;
+}
+
+static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels)
+{
+ int i;
+ int img_len = w * h * channels;
+ stbi__uint16 *enlarged;
+
+ enlarged = (stbi__uint16 *) stbi__malloc(img_len*2);
+ if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory");
+
+ for (i = 0; i < img_len; ++i)
+ enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff
+
+ STBI_FREE(orig);
+ return enlarged;
+}
+
+static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel)
+{
+ int row;
+ size_t bytes_per_row = (size_t)w * bytes_per_pixel;
+ stbi_uc temp[2048];
+ stbi_uc *bytes = (stbi_uc *)image;
+
+ for (row = 0; row < (h>>1); row++) {
+ stbi_uc *row0 = bytes + row*bytes_per_row;
+ stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row;
+ // swap row0 with row1
+ size_t bytes_left = bytes_per_row;
+ while (bytes_left) {
+ size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp);
+ memcpy(temp, row0, bytes_copy);
+ memcpy(row0, row1, bytes_copy);
+ memcpy(row1, temp, bytes_copy);
+ row0 += bytes_copy;
+ row1 += bytes_copy;
+ bytes_left -= bytes_copy;
+ }
+ }
+}
+
+#ifndef STBI_NO_GIF
+static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel)
+{
+ int slice;
+ int slice_size = w * h * bytes_per_pixel;
+
+ stbi_uc *bytes = (stbi_uc *)image;
+ for (slice = 0; slice < z; ++slice) {
+ stbi__vertical_flip(bytes, w, h, bytes_per_pixel);
+ bytes += slice_size;
+ }
+}
+#endif
+
+static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp)
+{
+ stbi__result_info ri;
+ void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8);
+
+ if (result == NULL)
+ return NULL;
+
+ // it is the responsibility of the loaders to make sure we get either 8 or 16 bit.
+ STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16);
+
+ if (ri.bits_per_channel != 8) {
+ result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp);
+ ri.bits_per_channel = 8;
+ }
+
+ // @TODO: move stbi__convert_format to here
+
+ if (stbi__vertically_flip_on_load) {
+ int channels = req_comp ? req_comp : *comp;
+ stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc));
+ }
+
+ return (unsigned char *) result;
+}
+
+static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp)
+{
+ stbi__result_info ri;
+ void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16);
+
+ if (result == NULL)
+ return NULL;
+
+ // it is the responsibility of the loaders to make sure we get either 8 or 16 bit.
+ STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16);
+
+ if (ri.bits_per_channel != 16) {
+ result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp);
+ ri.bits_per_channel = 16;
+ }
+
+ // @TODO: move stbi__convert_format16 to here
+ // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision
+
+ if (stbi__vertically_flip_on_load) {
+ int channels = req_comp ? req_comp : *comp;
+ stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16));
+ }
+
+ return (stbi__uint16 *) result;
+}
+
+#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR)
+static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp)
+{
+ if (stbi__vertically_flip_on_load && result != NULL) {
+ int channels = req_comp ? req_comp : *comp;
+ stbi__vertical_flip(result, *x, *y, channels * sizeof(float));
+ }
+}
+#endif
+
+#ifndef STBI_NO_STDIO
+
+#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
+STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide);
+STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default);
+#endif
+
+#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
+STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)
+{
+ return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);
+}
+#endif
+
+static FILE *stbi__fopen(char const *filename, char const *mode)
+{
+ FILE *f;
+#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
+ wchar_t wMode[64];
+ wchar_t wFilename[1024];
+ if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename)))
+ return 0;
+
+ if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode)))
+ return 0;
+
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+ if (0 != _wfopen_s(&f, wFilename, wMode))
+ f = 0;
+#else
+ f = _wfopen(wFilename, wMode);
+#endif
+
+#elif defined(_MSC_VER) && _MSC_VER >= 1400
+ if (0 != fopen_s(&f, filename, mode))
+ f=0;
+#else
+ f = fopen(filename, mode);
+#endif
+ return f;
+}
+
+
+STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ FILE *f = stbi__fopen(filename, "rb");
+ unsigned char *result;
+ if (!f) return stbi__errpuc("can't fopen", "Unable to open file");
+ result = stbi_load_from_file(f,x,y,comp,req_comp);
+ fclose(f);
+ return result;
+}
+
+STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ unsigned char *result;
+ stbi__context s;
+ stbi__start_file(&s,f);
+ result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);
+ if (result) {
+ // need to 'unget' all the characters in the IO buffer
+ fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);
+ }
+ return result;
+}
+
+STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ stbi__uint16 *result;
+ stbi__context s;
+ stbi__start_file(&s,f);
+ result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp);
+ if (result) {
+ // need to 'unget' all the characters in the IO buffer
+ fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);
+ }
+ return result;
+}
+
+STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ FILE *f = stbi__fopen(filename, "rb");
+ stbi__uint16 *result;
+ if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file");
+ result = stbi_load_from_file_16(f,x,y,comp,req_comp);
+ fclose(f);
+ return result;
+}
+
+
+#endif //!STBI_NO_STDIO
+
+STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels)
+{
+ stbi__context s;
+ stbi__start_mem(&s,buffer,len);
+ return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels);
+}
+
+STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels)
+{
+ stbi__context s;
+ stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user);
+ return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels);
+}
+
+STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ stbi__context s;
+ stbi__start_mem(&s,buffer,len);
+ return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);
+}
+
+STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
+{
+ stbi__context s;
+ stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
+ return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);
+}
+
+#ifndef STBI_NO_GIF
+STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp)
+{
+ unsigned char *result;
+ stbi__context s;
+ stbi__start_mem(&s,buffer,len);
+
+ result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp);
+ if (stbi__vertically_flip_on_load) {
+ stbi__vertical_flip_slices( result, *x, *y, *z, *comp );
+ }
+
+ return result;
+}
+#endif
+
+#ifndef STBI_NO_LINEAR
+static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp)
+{
+ unsigned char *data;
+ #ifndef STBI_NO_HDR
+ if (stbi__hdr_test(s)) {
+ stbi__result_info ri;
+ float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri);
+ if (hdr_data)
+ stbi__float_postprocess(hdr_data,x,y,comp,req_comp);
+ return hdr_data;
+ }
+ #endif
+ data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp);
+ if (data)
+ return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);
+ return stbi__errpf("unknown image type", "Image not of any known type, or corrupt");
+}
+
+STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
+{
+ stbi__context s;
+ stbi__start_mem(&s,buffer,len);
+ return stbi__loadf_main(&s,x,y,comp,req_comp);
+}
+
+STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
+{
+ stbi__context s;
+ stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
+ return stbi__loadf_main(&s,x,y,comp,req_comp);
+}
+
+#ifndef STBI_NO_STDIO
+STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp)
+{
+ float *result;
+ FILE *f = stbi__fopen(filename, "rb");
+ if (!f) return stbi__errpf("can't fopen", "Unable to open file");
+ result = stbi_loadf_from_file(f,x,y,comp,req_comp);
+ fclose(f);
+ return result;
+}
+
+STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
+{
+ stbi__context s;
+ stbi__start_file(&s,f);
+ return stbi__loadf_main(&s,x,y,comp,req_comp);
+}
+#endif // !STBI_NO_STDIO
+
+#endif // !STBI_NO_LINEAR
+
+// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is
+// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always
+// reports false!
+
+STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len)
+{
+ #ifndef STBI_NO_HDR
+ stbi__context s;
+ stbi__start_mem(&s,buffer,len);
+ return stbi__hdr_test(&s);
+ #else
+ STBI_NOTUSED(buffer);
+ STBI_NOTUSED(len);
+ return 0;
+ #endif
+}
+
+#ifndef STBI_NO_STDIO
+STBIDEF int stbi_is_hdr (char const *filename)
+{
+ FILE *f = stbi__fopen(filename, "rb");
+ int result=0;
+ if (f) {
+ result = stbi_is_hdr_from_file(f);
+ fclose(f);
+ }
+ return result;
+}
+
+STBIDEF int stbi_is_hdr_from_file(FILE *f)
+{
+ #ifndef STBI_NO_HDR
+ long pos = ftell(f);
+ int res;
+ stbi__context s;
+ stbi__start_file(&s,f);
+ res = stbi__hdr_test(&s);
+ fseek(f, pos, SEEK_SET);
+ return res;
+ #else
+ STBI_NOTUSED(f);
+ return 0;
+ #endif
+}
+#endif // !STBI_NO_STDIO
+
+STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user)
+{
+ #ifndef STBI_NO_HDR
+ stbi__context s;
+ stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
+ return stbi__hdr_test(&s);
+ #else
+ STBI_NOTUSED(clbk);
+ STBI_NOTUSED(user);
+ return 0;
+ #endif
+}
+
+#ifndef STBI_NO_LINEAR
+static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f;
+
+STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; }
+STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; }
+#endif
+
+static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f;
+
+STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; }
+STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; }
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Common code used by all image loaders
+//
+
+enum
+{
+ STBI__SCAN_load=0,
+ STBI__SCAN_type,
+ STBI__SCAN_header
+};
+
+static void stbi__refill_buffer(stbi__context *s)
+{
+ int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen);
+ s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original);
+ if (n == 0) {
+ // at end of file, treat same as if from memory, but need to handle case
+ // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file
+ s->read_from_callbacks = 0;
+ s->img_buffer = s->buffer_start;
+ s->img_buffer_end = s->buffer_start+1;
+ *s->img_buffer = 0;
+ } else {
+ s->img_buffer = s->buffer_start;
+ s->img_buffer_end = s->buffer_start + n;
+ }
+}
+
+stbi_inline static stbi_uc stbi__get8(stbi__context *s)
+{
+ if (s->img_buffer < s->img_buffer_end)
+ return *s->img_buffer++;
+ if (s->read_from_callbacks) {
+ stbi__refill_buffer(s);
+ return *s->img_buffer++;
+ }
+ return 0;
+}
+
+#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM)
+// nothing
+#else
+stbi_inline static int stbi__at_eof(stbi__context *s)
+{
+ if (s->io.read) {
+ if (!(s->io.eof)(s->io_user_data)) return 0;
+ // if feof() is true, check if buffer = end
+ // special case: we've only got the special 0 character at the end
+ if (s->read_from_callbacks == 0) return 1;
+ }
+
+ return s->img_buffer >= s->img_buffer_end;
+}
+#endif
+
+#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC)
+// nothing
+#else
+static void stbi__skip(stbi__context *s, int n)
+{
+ if (n == 0) return; // already there!
+ if (n < 0) {
+ s->img_buffer = s->img_buffer_end;
+ return;
+ }
+ if (s->io.read) {
+ int blen = (int) (s->img_buffer_end - s->img_buffer);
+ if (blen < n) {
+ s->img_buffer = s->img_buffer_end;
+ (s->io.skip)(s->io_user_data, n - blen);
+ return;
+ }
+ }
+ s->img_buffer += n;
+}
+#endif
+
+#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM)
+// nothing
+#else
+static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n)
+{
+ if (s->io.read) {
+ int blen = (int) (s->img_buffer_end - s->img_buffer);
+ if (blen < n) {
+ int res, count;
+
+ memcpy(buffer, s->img_buffer, blen);
+
+ count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen);
+ res = (count == (n-blen));
+ s->img_buffer = s->img_buffer_end;
+ return res;
+ }
+ }
+
+ if (s->img_buffer+n <= s->img_buffer_end) {
+ memcpy(buffer, s->img_buffer, n);
+ s->img_buffer += n;
+ return 1;
+ } else
+ return 0;
+}
+#endif
+
+#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC)
+// nothing
+#else
+static int stbi__get16be(stbi__context *s)
+{
+ int z = stbi__get8(s);
+ return (z << 8) + stbi__get8(s);
+}
+#endif
+
+#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC)
+// nothing
+#else
+static stbi__uint32 stbi__get32be(stbi__context *s)
+{
+ stbi__uint32 z = stbi__get16be(s);
+ return (z << 16) + stbi__get16be(s);
+}
+#endif
+
+#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF)
+// nothing
+#else
+static int stbi__get16le(stbi__context *s)
+{
+ int z = stbi__get8(s);
+ return z + (stbi__get8(s) << 8);
+}
+#endif
+
+#ifndef STBI_NO_BMP
+static stbi__uint32 stbi__get32le(stbi__context *s)
+{
+ stbi__uint32 z = stbi__get16le(s);
+ z += (stbi__uint32)stbi__get16le(s) << 16;
+ return z;
+}
+#endif
+
+#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings
+
+#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM)
+// nothing
+#else
+//////////////////////////////////////////////////////////////////////////////
+//
+// generic converter from built-in img_n to req_comp
+// individual types do this automatically as much as possible (e.g. jpeg
+// does all cases internally since it needs to colorspace convert anyway,
+// and it never has alpha, so very few cases ). png can automatically
+// interleave an alpha=255 channel, but falls back to this for other cases
+//
+// assume data buffer is malloced, so malloc a new one and free that one
+// only failure mode is malloc failing
+
+static stbi_uc stbi__compute_y(int r, int g, int b)
+{
+ return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8);
+}
+#endif
+
+#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM)
+// nothing
+#else
+static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y)
+{
+ int i,j;
+ unsigned char *good;
+
+ if (req_comp == img_n) return data;
+ STBI_ASSERT(req_comp >= 1 && req_comp <= 4);
+
+ good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0);
+ if (good == NULL) {
+ STBI_FREE(data);
+ return stbi__errpuc("outofmem", "Out of memory");
+ }
+
+ for (j=0; j < (int) y; ++j) {
+ unsigned char *src = data + j * x * img_n ;
+ unsigned char *dest = good + j * x * req_comp;
+
+ #define STBI__COMBO(a,b) ((a)*8+(b))
+ #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)
+ // convert source image with img_n components to one with req_comp components;
+ // avoid switch per pixel, so use switch per scanline and massive macros
+ switch (STBI__COMBO(img_n, req_comp)) {
+ STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break;
+ STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break;
+ STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break;
+ STBI__CASE(2,1) { dest[0]=src[0]; } break;
+ STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break;
+ STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break;
+ STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break;
+ STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break;
+ STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break;
+ STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break;
+ STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break;
+ STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break;
+ default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion");
+ }
+ #undef STBI__CASE
+ }
+
+ STBI_FREE(data);
+ return good;
+}
+#endif
+
+#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD)
+// nothing
+#else
+static stbi__uint16 stbi__compute_y_16(int r, int g, int b)
+{
+ return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8);
+}
+#endif
+
+#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD)
+// nothing
+#else
+static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y)
+{
+ int i,j;
+ stbi__uint16 *good;
+
+ if (req_comp == img_n) return data;
+ STBI_ASSERT(req_comp >= 1 && req_comp <= 4);
+
+ good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2);
+ if (good == NULL) {
+ STBI_FREE(data);
+ return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory");
+ }
+
+ for (j=0; j < (int) y; ++j) {
+ stbi__uint16 *src = data + j * x * img_n ;
+ stbi__uint16 *dest = good + j * x * req_comp;
+
+ #define STBI__COMBO(a,b) ((a)*8+(b))
+ #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)
+ // convert source image with img_n components to one with req_comp components;
+ // avoid switch per pixel, so use switch per scanline and massive macros
+ switch (STBI__COMBO(img_n, req_comp)) {
+ STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break;
+ STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break;
+ STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break;
+ STBI__CASE(2,1) { dest[0]=src[0]; } break;
+ STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break;
+ STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break;
+ STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break;
+ STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break;
+ STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break;
+ STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break;
+ STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break;
+ STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break;
+ default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion");
+ }
+ #undef STBI__CASE
+ }
+
+ STBI_FREE(data);
+ return good;
+}
+#endif
+
+#ifndef STBI_NO_LINEAR
+static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp)
+{
+ int i,k,n;
+ float *output;
+ if (!data) return NULL;
+ output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0);
+ if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); }
+ // compute number of non-alpha components
+ if (comp & 1) n = comp; else n = comp-1;
+ for (i=0; i < x*y; ++i) {
+ for (k=0; k < n; ++k) {
+ output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale);
+ }
+ }
+ if (n < comp) {
+ for (i=0; i < x*y; ++i) {
+ output[i*comp + n] = data[i*comp + n]/255.0f;
+ }
+ }
+ STBI_FREE(data);
+ return output;
+}
+#endif
+
+#ifndef STBI_NO_HDR
+#define stbi__float2int(x) ((int) (x))
+static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp)
+{
+ int i,k,n;
+ stbi_uc *output;
+ if (!data) return NULL;
+ output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0);
+ if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); }
+ // compute number of non-alpha components
+ if (comp & 1) n = comp; else n = comp-1;
+ for (i=0; i < x*y; ++i) {
+ for (k=0; k < n; ++k) {
+ float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f;
+ if (z < 0) z = 0;
+ if (z > 255) z = 255;
+ output[i*comp + k] = (stbi_uc) stbi__float2int(z);
+ }
+ if (k < comp) {
+ float z = data[i*comp+k] * 255 + 0.5f;
+ if (z < 0) z = 0;
+ if (z > 255) z = 255;
+ output[i*comp + k] = (stbi_uc) stbi__float2int(z);
+ }
+ }
+ STBI_FREE(data);
+ return output;
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// "baseline" JPEG/JFIF decoder
+//
+// simple implementation
+// - doesn't support delayed output of y-dimension
+// - simple interface (only one output format: 8-bit interleaved RGB)
+// - doesn't try to recover corrupt jpegs
+// - doesn't allow partial loading, loading multiple at once
+// - still fast on x86 (copying globals into locals doesn't help x86)
+// - allocates lots of intermediate memory (full size of all components)
+// - non-interleaved case requires this anyway
+// - allows good upsampling (see next)
+// high-quality
+// - upsampled channels are bilinearly interpolated, even across blocks
+// - quality integer IDCT derived from IJG's 'slow'
+// performance
+// - fast huffman; reasonable integer IDCT
+// - some SIMD kernels for common paths on targets with SSE2/NEON
+// - uses a lot of intermediate memory, could cache poorly
+
+#ifndef STBI_NO_JPEG
+
+// huffman decoding acceleration
+#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache
+
+typedef struct
+{
+ stbi_uc fast[1 << FAST_BITS];
+ // weirdly, repacking this into AoS is a 10% speed loss, instead of a win
+ stbi__uint16 code[256];
+ stbi_uc values[256];
+ stbi_uc size[257];
+ unsigned int maxcode[18];
+ int delta[17]; // old 'firstsymbol' - old 'firstcode'
+} stbi__huffman;
+
+typedef struct
+{
+ stbi__context *s;
+ stbi__huffman huff_dc[4];
+ stbi__huffman huff_ac[4];
+ stbi__uint16 dequant[4][64];
+ stbi__int16 fast_ac[4][1 << FAST_BITS];
+
+// sizes for components, interleaved MCUs
+ int img_h_max, img_v_max;
+ int img_mcu_x, img_mcu_y;
+ int img_mcu_w, img_mcu_h;
+
+// definition of jpeg image component
+ struct
+ {
+ int id;
+ int h,v;
+ int tq;
+ int hd,ha;
+ int dc_pred;
+
+ int x,y,w2,h2;
+ stbi_uc *data;
+ void *raw_data, *raw_coeff;
+ stbi_uc *linebuf;
+ short *coeff; // progressive only
+ int coeff_w, coeff_h; // number of 8x8 coefficient blocks
+ } img_comp[4];
+
+ stbi__uint32 code_buffer; // jpeg entropy-coded buffer
+ int code_bits; // number of valid bits
+ unsigned char marker; // marker seen while filling entropy buffer
+ int nomore; // flag if we saw a marker so must stop
+
+ int progressive;
+ int spec_start;
+ int spec_end;
+ int succ_high;
+ int succ_low;
+ int eob_run;
+ int jfif;
+ int app14_color_transform; // Adobe APP14 tag
+ int rgb;
+
+ int scan_n, order[4];
+ int restart_interval, todo;
+
+// kernels
+ void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]);
+ void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step);
+ stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs);
+} stbi__jpeg;
+
+static int stbi__build_huffman(stbi__huffman *h, int *count)
+{
+ int i,j,k=0;
+ unsigned int code;
+ // build size list for each symbol (from JPEG spec)
+ for (i=0; i < 16; ++i) {
+ for (j=0; j < count[i]; ++j) {
+ h->size[k++] = (stbi_uc) (i+1);
+ if(k >= 257) return stbi__err("bad size list","Corrupt JPEG");
+ }
+ }
+ h->size[k] = 0;
+
+ // compute actual symbols (from jpeg spec)
+ code = 0;
+ k = 0;
+ for(j=1; j <= 16; ++j) {
+ // compute delta to add to code to compute symbol id
+ h->delta[j] = k - code;
+ if (h->size[k] == j) {
+ while (h->size[k] == j)
+ h->code[k++] = (stbi__uint16) (code++);
+ if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG");
+ }
+ // compute largest code + 1 for this size, preshifted as needed later
+ h->maxcode[j] = code << (16-j);
+ code <<= 1;
+ }
+ h->maxcode[j] = 0xffffffff;
+
+ // build non-spec acceleration table; 255 is flag for not-accelerated
+ memset(h->fast, 255, 1 << FAST_BITS);
+ for (i=0; i < k; ++i) {
+ int s = h->size[i];
+ if (s <= FAST_BITS) {
+ int c = h->code[i] << (FAST_BITS-s);
+ int m = 1 << (FAST_BITS-s);
+ for (j=0; j < m; ++j) {
+ h->fast[c+j] = (stbi_uc) i;
+ }
+ }
+ }
+ return 1;
+}
+
+// build a table that decodes both magnitude and value of small ACs in
+// one go.
+static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h)
+{
+ int i;
+ for (i=0; i < (1 << FAST_BITS); ++i) {
+ stbi_uc fast = h->fast[i];
+ fast_ac[i] = 0;
+ if (fast < 255) {
+ int rs = h->values[fast];
+ int run = (rs >> 4) & 15;
+ int magbits = rs & 15;
+ int len = h->size[fast];
+
+ if (magbits && len + magbits <= FAST_BITS) {
+ // magnitude code followed by receive_extend code
+ int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits);
+ int m = 1 << (magbits - 1);
+ if (k < m) k += (~0U << magbits) + 1;
+ // if the result is small enough, we can fit it in fast_ac table
+ if (k >= -128 && k <= 127)
+ fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits));
+ }
+ }
+ }
+}
+
+static void stbi__grow_buffer_unsafe(stbi__jpeg *j)
+{
+ do {
+ unsigned int b = j->nomore ? 0 : stbi__get8(j->s);
+ if (b == 0xff) {
+ int c = stbi__get8(j->s);
+ while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes
+ if (c != 0) {
+ j->marker = (unsigned char) c;
+ j->nomore = 1;
+ return;
+ }
+ }
+ j->code_buffer |= b << (24 - j->code_bits);
+ j->code_bits += 8;
+ } while (j->code_bits <= 24);
+}
+
+// (1 << n) - 1
+static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535};
+
+// decode a jpeg huffman value from the bitstream
+stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h)
+{
+ unsigned int temp;
+ int c,k;
+
+ if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
+
+ // look at the top FAST_BITS and determine what symbol ID it is,
+ // if the code is <= FAST_BITS
+ c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);
+ k = h->fast[c];
+ if (k < 255) {
+ int s = h->size[k];
+ if (s > j->code_bits)
+ return -1;
+ j->code_buffer <<= s;
+ j->code_bits -= s;
+ return h->values[k];
+ }
+
+ // naive test is to shift the code_buffer down so k bits are
+ // valid, then test against maxcode. To speed this up, we've
+ // preshifted maxcode left so that it has (16-k) 0s at the
+ // end; in other words, regardless of the number of bits, it
+ // wants to be compared against something shifted to have 16;
+ // that way we don't need to shift inside the loop.
+ temp = j->code_buffer >> 16;
+ for (k=FAST_BITS+1 ; ; ++k)
+ if (temp < h->maxcode[k])
+ break;
+ if (k == 17) {
+ // error! code not found
+ j->code_bits -= 16;
+ return -1;
+ }
+
+ if (k > j->code_bits)
+ return -1;
+
+ // convert the huffman code to the symbol id
+ c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k];
+ if(c < 0 || c >= 256) // symbol id out of bounds!
+ return -1;
+ STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]);
+
+ // convert the id to a symbol
+ j->code_bits -= k;
+ j->code_buffer <<= k;
+ return h->values[c];
+}
+
+// bias[n] = (-1<<n) + 1
+static const int stbi__jbias[16] = {0,-1,-3,-7,-15,-31,-63,-127,-255,-511,-1023,-2047,-4095,-8191,-16383,-32767};
+
+// combined JPEG 'receive' and JPEG 'extend', since baseline
+// always extends everything it receives.
+stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n)
+{
+ unsigned int k;
+ int sgn;
+ if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
+ if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing
+
+ sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative)
+ k = stbi_lrot(j->code_buffer, n);
+ j->code_buffer = k & ~stbi__bmask[n];
+ k &= stbi__bmask[n];
+ j->code_bits -= n;
+ return k + (stbi__jbias[n] & (sgn - 1));
+}
+
+// get some unsigned bits
+stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n)
+{
+ unsigned int k;
+ if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
+ if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing
+ k = stbi_lrot(j->code_buffer, n);
+ j->code_buffer = k & ~stbi__bmask[n];
+ k &= stbi__bmask[n];
+ j->code_bits -= n;
+ return k;
+}
+
+stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j)
+{
+ unsigned int k;
+ if (j->code_bits < 1) stbi__grow_buffer_unsafe(j);
+ if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing
+ k = j->code_buffer;
+ j->code_buffer <<= 1;
+ --j->code_bits;
+ return k & 0x80000000;
+}
+
+// given a value that's at position X in the zigzag stream,
+// where does it appear in the 8x8 matrix coded as row-major?
+static const stbi_uc stbi__jpeg_dezigzag[64+15] =
+{
+ 0, 1, 8, 16, 9, 2, 3, 10,
+ 17, 24, 32, 25, 18, 11, 4, 5,
+ 12, 19, 26, 33, 40, 48, 41, 34,
+ 27, 20, 13, 6, 7, 14, 21, 28,
+ 35, 42, 49, 56, 57, 50, 43, 36,
+ 29, 22, 15, 23, 30, 37, 44, 51,
+ 58, 59, 52, 45, 38, 31, 39, 46,
+ 53, 60, 61, 54, 47, 55, 62, 63,
+ // let corrupt input sample past end
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63
+};
+
+// decode one 64-entry block--
+static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant)
+{
+ int diff,dc,k;
+ int t;
+
+ if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
+ t = stbi__jpeg_huff_decode(j, hdc);
+ if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG");
+
+ // 0 all the ac values now so we can do it 32-bits at a time
+ memset(data,0,64*sizeof(data[0]));
+
+ diff = t ? stbi__extend_receive(j, t) : 0;
+ if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG");
+ dc = j->img_comp[b].dc_pred + diff;
+ j->img_comp[b].dc_pred = dc;
+ if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
+ data[0] = (short) (dc * dequant[0]);
+
+ // decode AC components, see JPEG spec
+ k = 1;
+ do {
+ unsigned int zig;
+ int c,r,s;
+ if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
+ c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);
+ r = fac[c];
+ if (r) { // fast-AC path
+ k += (r >> 4) & 15; // run
+ s = r & 15; // combined length
+ if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available");
+ j->code_buffer <<= s;
+ j->code_bits -= s;
+ // decode into unzigzag'd location
+ zig = stbi__jpeg_dezigzag[k++];
+ data[zig] = (short) ((r >> 8) * dequant[zig]);
+ } else {
+ int rs = stbi__jpeg_huff_decode(j, hac);
+ if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG");
+ s = rs & 15;
+ r = rs >> 4;
+ if (s == 0) {
+ if (rs != 0xf0) break; // end block
+ k += 16;
+ } else {
+ k += r;
+ // decode into unzigzag'd location
+ zig = stbi__jpeg_dezigzag[k++];
+ data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]);
+ }
+ }
+ } while (k < 64);
+ return 1;
+}
+
+static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b)
+{
+ int diff,dc;
+ int t;
+ if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
+
+ if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
+
+ if (j->succ_high == 0) {
+ // first scan for DC coefficient, must be first
+ memset(data,0,64*sizeof(data[0])); // 0 all the ac values now
+ t = stbi__jpeg_huff_decode(j, hdc);
+ if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
+ diff = t ? stbi__extend_receive(j, t) : 0;
+
+ if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG");
+ dc = j->img_comp[b].dc_pred + diff;
+ j->img_comp[b].dc_pred = dc;
+ if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
+ data[0] = (short) (dc * (1 << j->succ_low));
+ } else {
+ // refinement scan for DC coefficient
+ if (stbi__jpeg_get_bit(j))
+ data[0] += (short) (1 << j->succ_low);
+ }
+ return 1;
+}
+
+// @OPTIMIZE: store non-zigzagged during the decode passes,
+// and only de-zigzag when dequantizing
+static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac)
+{
+ int k;
+ if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
+
+ if (j->succ_high == 0) {
+ int shift = j->succ_low;
+
+ if (j->eob_run) {
+ --j->eob_run;
+ return 1;
+ }
+
+ k = j->spec_start;
+ do {
+ unsigned int zig;
+ int c,r,s;
+ if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
+ c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);
+ r = fac[c];
+ if (r) { // fast-AC path
+ k += (r >> 4) & 15; // run
+ s = r & 15; // combined length
+ if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available");
+ j->code_buffer <<= s;
+ j->code_bits -= s;
+ zig = stbi__jpeg_dezigzag[k++];
+ data[zig] = (short) ((r >> 8) * (1 << shift));
+ } else {
+ int rs = stbi__jpeg_huff_decode(j, hac);
+ if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG");
+ s = rs & 15;
+ r = rs >> 4;
+ if (s == 0) {
+ if (r < 15) {
+ j->eob_run = (1 << r);
+ if (r)
+ j->eob_run += stbi__jpeg_get_bits(j, r);
+ --j->eob_run;
+ break;
+ }
+ k += 16;
+ } else {
+ k += r;
+ zig = stbi__jpeg_dezigzag[k++];
+ data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift));
+ }
+ }
+ } while (k <= j->spec_end);
+ } else {
+ // refinement scan for these AC coefficients
+
+ short bit = (short) (1 << j->succ_low);
+
+ if (j->eob_run) {
+ --j->eob_run;
+ for (k = j->spec_start; k <= j->spec_end; ++k) {
+ short *p = &data[stbi__jpeg_dezigzag[k]];
+ if (*p != 0)
+ if (stbi__jpeg_get_bit(j))
+ if ((*p & bit)==0) {
+ if (*p > 0)
+ *p += bit;
+ else
+ *p -= bit;
+ }
+ }
+ } else {
+ k = j->spec_start;
+ do {
+ int r,s;
+ int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh
+ if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG");
+ s = rs & 15;
+ r = rs >> 4;
+ if (s == 0) {
+ if (r < 15) {
+ j->eob_run = (1 << r) - 1;
+ if (r)
+ j->eob_run += stbi__jpeg_get_bits(j, r);
+ r = 64; // force end of block
+ } else {
+ // r=15 s=0 should write 16 0s, so we just do
+ // a run of 15 0s and then write s (which is 0),
+ // so we don't have to do anything special here
+ }
+ } else {
+ if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG");
+ // sign bit
+ if (stbi__jpeg_get_bit(j))
+ s = bit;
+ else
+ s = -bit;
+ }
+
+ // advance by r
+ while (k <= j->spec_end) {
+ short *p = &data[stbi__jpeg_dezigzag[k++]];
+ if (*p != 0) {
+ if (stbi__jpeg_get_bit(j))
+ if ((*p & bit)==0) {
+ if (*p > 0)
+ *p += bit;
+ else
+ *p -= bit;
+ }
+ } else {
+ if (r == 0) {
+ *p = (short) s;
+ break;
+ }
+ --r;
+ }
+ }
+ } while (k <= j->spec_end);
+ }
+ }
+ return 1;
+}
+
+// take a -128..127 value and stbi__clamp it and convert to 0..255
+stbi_inline static stbi_uc stbi__clamp(int x)
+{
+ // trick to use a single test to catch both cases
+ if ((unsigned int) x > 255) {
+ if (x < 0) return 0;
+ if (x > 255) return 255;
+ }
+ return (stbi_uc) x;
+}
+
+#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5)))
+#define stbi__fsh(x) ((x) * 4096)
+
+// derived from jidctint -- DCT_ISLOW
+#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \
+ int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \
+ p2 = s2; \
+ p3 = s6; \
+ p1 = (p2+p3) * stbi__f2f(0.5411961f); \
+ t2 = p1 + p3*stbi__f2f(-1.847759065f); \
+ t3 = p1 + p2*stbi__f2f( 0.765366865f); \
+ p2 = s0; \
+ p3 = s4; \
+ t0 = stbi__fsh(p2+p3); \
+ t1 = stbi__fsh(p2-p3); \
+ x0 = t0+t3; \
+ x3 = t0-t3; \
+ x1 = t1+t2; \
+ x2 = t1-t2; \
+ t0 = s7; \
+ t1 = s5; \
+ t2 = s3; \
+ t3 = s1; \
+ p3 = t0+t2; \
+ p4 = t1+t3; \
+ p1 = t0+t3; \
+ p2 = t1+t2; \
+ p5 = (p3+p4)*stbi__f2f( 1.175875602f); \
+ t0 = t0*stbi__f2f( 0.298631336f); \
+ t1 = t1*stbi__f2f( 2.053119869f); \
+ t2 = t2*stbi__f2f( 3.072711026f); \
+ t3 = t3*stbi__f2f( 1.501321110f); \
+ p1 = p5 + p1*stbi__f2f(-0.899976223f); \
+ p2 = p5 + p2*stbi__f2f(-2.562915447f); \
+ p3 = p3*stbi__f2f(-1.961570560f); \
+ p4 = p4*stbi__f2f(-0.390180644f); \
+ t3 += p1+p4; \
+ t2 += p2+p3; \
+ t1 += p2+p4; \
+ t0 += p1+p3;
+
+static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64])
+{
+ int i,val[64],*v=val;
+ stbi_uc *o;
+ short *d = data;
+
+ // columns
+ for (i=0; i < 8; ++i,++d, ++v) {
+ // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing
+ if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0
+ && d[40]==0 && d[48]==0 && d[56]==0) {
+ // no shortcut 0 seconds
+ // (1|2|3|4|5|6|7)==0 0 seconds
+ // all separate -0.047 seconds
+ // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds
+ int dcterm = d[0]*4;
+ v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm;
+ } else {
+ STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56])
+ // constants scaled things up by 1<<12; let's bring them back
+ // down, but keep 2 extra bits of precision
+ x0 += 512; x1 += 512; x2 += 512; x3 += 512;
+ v[ 0] = (x0+t3) >> 10;
+ v[56] = (x0-t3) >> 10;
+ v[ 8] = (x1+t2) >> 10;
+ v[48] = (x1-t2) >> 10;
+ v[16] = (x2+t1) >> 10;
+ v[40] = (x2-t1) >> 10;
+ v[24] = (x3+t0) >> 10;
+ v[32] = (x3-t0) >> 10;
+ }
+ }
+
+ for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) {
+ // no fast case since the first 1D IDCT spread components out
+ STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7])
+ // constants scaled things up by 1<<12, plus we had 1<<2 from first
+ // loop, plus horizontal and vertical each scale by sqrt(8) so together
+ // we've got an extra 1<<3, so 1<<17 total we need to remove.
+ // so we want to round that, which means adding 0.5 * 1<<17,
+ // aka 65536. Also, we'll end up with -128 to 127 that we want
+ // to encode as 0..255 by adding 128, so we'll add that before the shift
+ x0 += 65536 + (128<<17);
+ x1 += 65536 + (128<<17);
+ x2 += 65536 + (128<<17);
+ x3 += 65536 + (128<<17);
+ // tried computing the shifts into temps, or'ing the temps to see
+ // if any were out of range, but that was slower
+ o[0] = stbi__clamp((x0+t3) >> 17);
+ o[7] = stbi__clamp((x0-t3) >> 17);
+ o[1] = stbi__clamp((x1+t2) >> 17);
+ o[6] = stbi__clamp((x1-t2) >> 17);
+ o[2] = stbi__clamp((x2+t1) >> 17);
+ o[5] = stbi__clamp((x2-t1) >> 17);
+ o[3] = stbi__clamp((x3+t0) >> 17);
+ o[4] = stbi__clamp((x3-t0) >> 17);
+ }
+}
+
+#ifdef STBI_SSE2
+// sse2 integer IDCT. not the fastest possible implementation but it
+// produces bit-identical results to the generic C version so it's
+// fully "transparent".
+static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64])
+{
+ // This is constructed to match our regular (generic) integer IDCT exactly.
+ __m128i row0, row1, row2, row3, row4, row5, row6, row7;
+ __m128i tmp;
+
+ // dot product constant: even elems=x, odd elems=y
+ #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y))
+
+ // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit)
+ // out(1) = c1[even]*x + c1[odd]*y
+ #define dct_rot(out0,out1, x,y,c0,c1) \
+ __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \
+ __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \
+ __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \
+ __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \
+ __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \
+ __m128i out1##_h = _mm_madd_epi16(c0##hi, c1)
+
+ // out = in << 12 (in 16-bit, out 32-bit)
+ #define dct_widen(out, in) \
+ __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \
+ __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4)
+
+ // wide add
+ #define dct_wadd(out, a, b) \
+ __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \
+ __m128i out##_h = _mm_add_epi32(a##_h, b##_h)
+
+ // wide sub
+ #define dct_wsub(out, a, b) \
+ __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \
+ __m128i out##_h = _mm_sub_epi32(a##_h, b##_h)
+
+ // butterfly a/b, add bias, then shift by "s" and pack
+ #define dct_bfly32o(out0, out1, a,b,bias,s) \
+ { \
+ __m128i abiased_l = _mm_add_epi32(a##_l, bias); \
+ __m128i abiased_h = _mm_add_epi32(a##_h, bias); \
+ dct_wadd(sum, abiased, b); \
+ dct_wsub(dif, abiased, b); \
+ out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \
+ out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \
+ }
+
+ // 8-bit interleave step (for transposes)
+ #define dct_interleave8(a, b) \
+ tmp = a; \
+ a = _mm_unpacklo_epi8(a, b); \
+ b = _mm_unpackhi_epi8(tmp, b)
+
+ // 16-bit interleave step (for transposes)
+ #define dct_interleave16(a, b) \
+ tmp = a; \
+ a = _mm_unpacklo_epi16(a, b); \
+ b = _mm_unpackhi_epi16(tmp, b)
+
+ #define dct_pass(bias,shift) \
+ { \
+ /* even part */ \
+ dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \
+ __m128i sum04 = _mm_add_epi16(row0, row4); \
+ __m128i dif04 = _mm_sub_epi16(row0, row4); \
+ dct_widen(t0e, sum04); \
+ dct_widen(t1e, dif04); \
+ dct_wadd(x0, t0e, t3e); \
+ dct_wsub(x3, t0e, t3e); \
+ dct_wadd(x1, t1e, t2e); \
+ dct_wsub(x2, t1e, t2e); \
+ /* odd part */ \
+ dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \
+ dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \
+ __m128i sum17 = _mm_add_epi16(row1, row7); \
+ __m128i sum35 = _mm_add_epi16(row3, row5); \
+ dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \
+ dct_wadd(x4, y0o, y4o); \
+ dct_wadd(x5, y1o, y5o); \
+ dct_wadd(x6, y2o, y5o); \
+ dct_wadd(x7, y3o, y4o); \
+ dct_bfly32o(row0,row7, x0,x7,bias,shift); \
+ dct_bfly32o(row1,row6, x1,x6,bias,shift); \
+ dct_bfly32o(row2,row5, x2,x5,bias,shift); \
+ dct_bfly32o(row3,row4, x3,x4,bias,shift); \
+ }
+
+ __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f));
+ __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f));
+ __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f));
+ __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f));
+ __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f));
+ __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f));
+ __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f));
+ __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f));
+
+ // rounding biases in column/row passes, see stbi__idct_block for explanation.
+ __m128i bias_0 = _mm_set1_epi32(512);
+ __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17));
+
+ // load
+ row0 = _mm_load_si128((const __m128i *) (data + 0*8));
+ row1 = _mm_load_si128((const __m128i *) (data + 1*8));
+ row2 = _mm_load_si128((const __m128i *) (data + 2*8));
+ row3 = _mm_load_si128((const __m128i *) (data + 3*8));
+ row4 = _mm_load_si128((const __m128i *) (data + 4*8));
+ row5 = _mm_load_si128((const __m128i *) (data + 5*8));
+ row6 = _mm_load_si128((const __m128i *) (data + 6*8));
+ row7 = _mm_load_si128((const __m128i *) (data + 7*8));
+
+ // column pass
+ dct_pass(bias_0, 10);
+
+ {
+ // 16bit 8x8 transpose pass 1
+ dct_interleave16(row0, row4);
+ dct_interleave16(row1, row5);
+ dct_interleave16(row2, row6);
+ dct_interleave16(row3, row7);
+
+ // transpose pass 2
+ dct_interleave16(row0, row2);
+ dct_interleave16(row1, row3);
+ dct_interleave16(row4, row6);
+ dct_interleave16(row5, row7);
+
+ // transpose pass 3
+ dct_interleave16(row0, row1);
+ dct_interleave16(row2, row3);
+ dct_interleave16(row4, row5);
+ dct_interleave16(row6, row7);
+ }
+
+ // row pass
+ dct_pass(bias_1, 17);
+
+ {
+ // pack
+ __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7
+ __m128i p1 = _mm_packus_epi16(row2, row3);
+ __m128i p2 = _mm_packus_epi16(row4, row5);
+ __m128i p3 = _mm_packus_epi16(row6, row7);
+
+ // 8bit 8x8 transpose pass 1
+ dct_interleave8(p0, p2); // a0e0a1e1...
+ dct_interleave8(p1, p3); // c0g0c1g1...
+
+ // transpose pass 2
+ dct_interleave8(p0, p1); // a0c0e0g0...
+ dct_interleave8(p2, p3); // b0d0f0h0...
+
+ // transpose pass 3
+ dct_interleave8(p0, p2); // a0b0c0d0...
+ dct_interleave8(p1, p3); // a4b4c4d4...
+
+ // store
+ _mm_storel_epi64((__m128i *) out, p0); out += out_stride;
+ _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride;
+ _mm_storel_epi64((__m128i *) out, p2); out += out_stride;
+ _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride;
+ _mm_storel_epi64((__m128i *) out, p1); out += out_stride;
+ _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride;
+ _mm_storel_epi64((__m128i *) out, p3); out += out_stride;
+ _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e));
+ }
+
+#undef dct_const
+#undef dct_rot
+#undef dct_widen
+#undef dct_wadd
+#undef dct_wsub
+#undef dct_bfly32o
+#undef dct_interleave8
+#undef dct_interleave16
+#undef dct_pass
+}
+
+#endif // STBI_SSE2
+
+#ifdef STBI_NEON
+
+// NEON integer IDCT. should produce bit-identical
+// results to the generic C version.
+static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64])
+{
+ int16x8_t row0, row1, row2, row3, row4, row5, row6, row7;
+
+ int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f));
+ int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f));
+ int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f));
+ int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f));
+ int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f));
+ int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f));
+ int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f));
+ int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f));
+ int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f));
+ int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f));
+ int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f));
+ int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f));
+
+#define dct_long_mul(out, inq, coeff) \
+ int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \
+ int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff)
+
+#define dct_long_mac(out, acc, inq, coeff) \
+ int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \
+ int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff)
+
+#define dct_widen(out, inq) \
+ int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \
+ int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12)
+
+// wide add
+#define dct_wadd(out, a, b) \
+ int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \
+ int32x4_t out##_h = vaddq_s32(a##_h, b##_h)
+
+// wide sub
+#define dct_wsub(out, a, b) \
+ int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \
+ int32x4_t out##_h = vsubq_s32(a##_h, b##_h)
+
+// butterfly a/b, then shift using "shiftop" by "s" and pack
+#define dct_bfly32o(out0,out1, a,b,shiftop,s) \
+ { \
+ dct_wadd(sum, a, b); \
+ dct_wsub(dif, a, b); \
+ out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \
+ out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \
+ }
+
+#define dct_pass(shiftop, shift) \
+ { \
+ /* even part */ \
+ int16x8_t sum26 = vaddq_s16(row2, row6); \
+ dct_long_mul(p1e, sum26, rot0_0); \
+ dct_long_mac(t2e, p1e, row6, rot0_1); \
+ dct_long_mac(t3e, p1e, row2, rot0_2); \
+ int16x8_t sum04 = vaddq_s16(row0, row4); \
+ int16x8_t dif04 = vsubq_s16(row0, row4); \
+ dct_widen(t0e, sum04); \
+ dct_widen(t1e, dif04); \
+ dct_wadd(x0, t0e, t3e); \
+ dct_wsub(x3, t0e, t3e); \
+ dct_wadd(x1, t1e, t2e); \
+ dct_wsub(x2, t1e, t2e); \
+ /* odd part */ \
+ int16x8_t sum15 = vaddq_s16(row1, row5); \
+ int16x8_t sum17 = vaddq_s16(row1, row7); \
+ int16x8_t sum35 = vaddq_s16(row3, row5); \
+ int16x8_t sum37 = vaddq_s16(row3, row7); \
+ int16x8_t sumodd = vaddq_s16(sum17, sum35); \
+ dct_long_mul(p5o, sumodd, rot1_0); \
+ dct_long_mac(p1o, p5o, sum17, rot1_1); \
+ dct_long_mac(p2o, p5o, sum35, rot1_2); \
+ dct_long_mul(p3o, sum37, rot2_0); \
+ dct_long_mul(p4o, sum15, rot2_1); \
+ dct_wadd(sump13o, p1o, p3o); \
+ dct_wadd(sump24o, p2o, p4o); \
+ dct_wadd(sump23o, p2o, p3o); \
+ dct_wadd(sump14o, p1o, p4o); \
+ dct_long_mac(x4, sump13o, row7, rot3_0); \
+ dct_long_mac(x5, sump24o, row5, rot3_1); \
+ dct_long_mac(x6, sump23o, row3, rot3_2); \
+ dct_long_mac(x7, sump14o, row1, rot3_3); \
+ dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \
+ dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \
+ dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \
+ dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \
+ }
+
+ // load
+ row0 = vld1q_s16(data + 0*8);
+ row1 = vld1q_s16(data + 1*8);
+ row2 = vld1q_s16(data + 2*8);
+ row3 = vld1q_s16(data + 3*8);
+ row4 = vld1q_s16(data + 4*8);
+ row5 = vld1q_s16(data + 5*8);
+ row6 = vld1q_s16(data + 6*8);
+ row7 = vld1q_s16(data + 7*8);
+
+ // add DC bias
+ row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0));
+
+ // column pass
+ dct_pass(vrshrn_n_s32, 10);
+
+ // 16bit 8x8 transpose
+ {
+// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively.
+// whether compilers actually get this is another story, sadly.
+#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; }
+#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); }
+#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); }
+
+ // pass 1
+ dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6
+ dct_trn16(row2, row3);
+ dct_trn16(row4, row5);
+ dct_trn16(row6, row7);
+
+ // pass 2
+ dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4
+ dct_trn32(row1, row3);
+ dct_trn32(row4, row6);
+ dct_trn32(row5, row7);
+
+ // pass 3
+ dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0
+ dct_trn64(row1, row5);
+ dct_trn64(row2, row6);
+ dct_trn64(row3, row7);
+
+#undef dct_trn16
+#undef dct_trn32
+#undef dct_trn64
+ }
+
+ // row pass
+ // vrshrn_n_s32 only supports shifts up to 16, we need
+ // 17. so do a non-rounding shift of 16 first then follow
+ // up with a rounding shift by 1.
+ dct_pass(vshrn_n_s32, 16);
+
+ {
+ // pack and round
+ uint8x8_t p0 = vqrshrun_n_s16(row0, 1);
+ uint8x8_t p1 = vqrshrun_n_s16(row1, 1);
+ uint8x8_t p2 = vqrshrun_n_s16(row2, 1);
+ uint8x8_t p3 = vqrshrun_n_s16(row3, 1);
+ uint8x8_t p4 = vqrshrun_n_s16(row4, 1);
+ uint8x8_t p5 = vqrshrun_n_s16(row5, 1);
+ uint8x8_t p6 = vqrshrun_n_s16(row6, 1);
+ uint8x8_t p7 = vqrshrun_n_s16(row7, 1);
+
+ // again, these can translate into one instruction, but often don't.
+#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; }
+#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); }
+#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); }
+
+ // sadly can't use interleaved stores here since we only write
+ // 8 bytes to each scan line!
+
+ // 8x8 8-bit transpose pass 1
+ dct_trn8_8(p0, p1);
+ dct_trn8_8(p2, p3);
+ dct_trn8_8(p4, p5);
+ dct_trn8_8(p6, p7);
+
+ // pass 2
+ dct_trn8_16(p0, p2);
+ dct_trn8_16(p1, p3);
+ dct_trn8_16(p4, p6);
+ dct_trn8_16(p5, p7);
+
+ // pass 3
+ dct_trn8_32(p0, p4);
+ dct_trn8_32(p1, p5);
+ dct_trn8_32(p2, p6);
+ dct_trn8_32(p3, p7);
+
+ // store
+ vst1_u8(out, p0); out += out_stride;
+ vst1_u8(out, p1); out += out_stride;
+ vst1_u8(out, p2); out += out_stride;
+ vst1_u8(out, p3); out += out_stride;
+ vst1_u8(out, p4); out += out_stride;
+ vst1_u8(out, p5); out += out_stride;
+ vst1_u8(out, p6); out += out_stride;
+ vst1_u8(out, p7);
+
+#undef dct_trn8_8
+#undef dct_trn8_16
+#undef dct_trn8_32
+ }
+
+#undef dct_long_mul
+#undef dct_long_mac
+#undef dct_widen
+#undef dct_wadd
+#undef dct_wsub
+#undef dct_bfly32o
+#undef dct_pass
+}
+
+#endif // STBI_NEON
+
+#define STBI__MARKER_none 0xff
+// if there's a pending marker from the entropy stream, return that
+// otherwise, fetch from the stream and get a marker. if there's no
+// marker, return 0xff, which is never a valid marker value
+static stbi_uc stbi__get_marker(stbi__jpeg *j)
+{
+ stbi_uc x;
+ if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; }
+ x = stbi__get8(j->s);
+ if (x != 0xff) return STBI__MARKER_none;
+ while (x == 0xff)
+ x = stbi__get8(j->s); // consume repeated 0xff fill bytes
+ return x;
+}
+
+// in each scan, we'll have scan_n components, and the order
+// of the components is specified by order[]
+#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7)
+
+// after a restart interval, stbi__jpeg_reset the entropy decoder and
+// the dc prediction
+static void stbi__jpeg_reset(stbi__jpeg *j)
+{
+ j->code_bits = 0;
+ j->code_buffer = 0;
+ j->nomore = 0;
+ j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0;
+ j->marker = STBI__MARKER_none;
+ j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff;
+ j->eob_run = 0;
+ // no more than 1<<31 MCUs if no restart_interal? that's plenty safe,
+ // since we don't even allow 1<<30 pixels
+}
+
+static int stbi__parse_entropy_coded_data(stbi__jpeg *z)
+{
+ stbi__jpeg_reset(z);
+ if (!z->progressive) {
+ if (z->scan_n == 1) {
+ int i,j;
+ STBI_SIMD_ALIGN(short, data[64]);
+ int n = z->order[0];
+ // non-interleaved data, we just need to process one block at a time,
+ // in trivial scanline order
+ // number of blocks to do just depends on how many actual "pixels" this
+ // component has, independent of interleaved MCU blocking and such
+ int w = (z->img_comp[n].x+7) >> 3;
+ int h = (z->img_comp[n].y+7) >> 3;
+ for (j=0; j < h; ++j) {
+ for (i=0; i < w; ++i) {
+ int ha = z->img_comp[n].ha;
+ if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0;
+ z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data);
+ // every data block is an MCU, so countdown the restart interval
+ if (--z->todo <= 0) {
+ if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
+ // if it's NOT a restart, then just bail, so we get corrupt data
+ // rather than no data
+ if (!STBI__RESTART(z->marker)) return 1;
+ stbi__jpeg_reset(z);
+ }
+ }
+ }
+ return 1;
+ } else { // interleaved
+ int i,j,k,x,y;
+ STBI_SIMD_ALIGN(short, data[64]);
+ for (j=0; j < z->img_mcu_y; ++j) {
+ for (i=0; i < z->img_mcu_x; ++i) {
+ // scan an interleaved mcu... process scan_n components in order
+ for (k=0; k < z->scan_n; ++k) {
+ int n = z->order[k];
+ // scan out an mcu's worth of this component; that's just determined
+ // by the basic H and V specified for the component
+ for (y=0; y < z->img_comp[n].v; ++y) {
+ for (x=0; x < z->img_comp[n].h; ++x) {
+ int x2 = (i*z->img_comp[n].h + x)*8;
+ int y2 = (j*z->img_comp[n].v + y)*8;
+ int ha = z->img_comp[n].ha;
+ if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0;
+ z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data);
+ }
+ }
+ }
+ // after all interleaved components, that's an interleaved MCU,
+ // so now count down the restart interval
+ if (--z->todo <= 0) {
+ if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
+ if (!STBI__RESTART(z->marker)) return 1;
+ stbi__jpeg_reset(z);
+ }
+ }
+ }
+ return 1;
+ }
+ } else {
+ if (z->scan_n == 1) {
+ int i,j;
+ int n = z->order[0];
+ // non-interleaved data, we just need to process one block at a time,
+ // in trivial scanline order
+ // number of blocks to do just depends on how many actual "pixels" this
+ // component has, independent of interleaved MCU blocking and such
+ int w = (z->img_comp[n].x+7) >> 3;
+ int h = (z->img_comp[n].y+7) >> 3;
+ for (j=0; j < h; ++j) {
+ for (i=0; i < w; ++i) {
+ short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w);
+ if (z->spec_start == 0) {
+ if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n))
+ return 0;
+ } else {
+ int ha = z->img_comp[n].ha;
+ if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha]))
+ return 0;
+ }
+ // every data block is an MCU, so countdown the restart interval
+ if (--z->todo <= 0) {
+ if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
+ if (!STBI__RESTART(z->marker)) return 1;
+ stbi__jpeg_reset(z);
+ }
+ }
+ }
+ return 1;
+ } else { // interleaved
+ int i,j,k,x,y;
+ for (j=0; j < z->img_mcu_y; ++j) {
+ for (i=0; i < z->img_mcu_x; ++i) {
+ // scan an interleaved mcu... process scan_n components in order
+ for (k=0; k < z->scan_n; ++k) {
+ int n = z->order[k];
+ // scan out an mcu's worth of this component; that's just determined
+ // by the basic H and V specified for the component
+ for (y=0; y < z->img_comp[n].v; ++y) {
+ for (x=0; x < z->img_comp[n].h; ++x) {
+ int x2 = (i*z->img_comp[n].h + x);
+ int y2 = (j*z->img_comp[n].v + y);
+ short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w);
+ if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n))
+ return 0;
+ }
+ }
+ }
+ // after all interleaved components, that's an interleaved MCU,
+ // so now count down the restart interval
+ if (--z->todo <= 0) {
+ if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
+ if (!STBI__RESTART(z->marker)) return 1;
+ stbi__jpeg_reset(z);
+ }
+ }
+ }
+ return 1;
+ }
+ }
+}
+
+static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant)
+{
+ int i;
+ for (i=0; i < 64; ++i)
+ data[i] *= dequant[i];
+}
+
+static void stbi__jpeg_finish(stbi__jpeg *z)
+{
+ if (z->progressive) {
+ // dequantize and idct the data
+ int i,j,n;
+ for (n=0; n < z->s->img_n; ++n) {
+ int w = (z->img_comp[n].x+7) >> 3;
+ int h = (z->img_comp[n].y+7) >> 3;
+ for (j=0; j < h; ++j) {
+ for (i=0; i < w; ++i) {
+ short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w);
+ stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]);
+ z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data);
+ }
+ }
+ }
+ }
+}
+
+static int stbi__process_marker(stbi__jpeg *z, int m)
+{
+ int L;
+ switch (m) {
+ case STBI__MARKER_none: // no marker found
+ return stbi__err("expected marker","Corrupt JPEG");
+
+ case 0xDD: // DRI - specify restart interval
+ if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG");
+ z->restart_interval = stbi__get16be(z->s);
+ return 1;
+
+ case 0xDB: // DQT - define quantization table
+ L = stbi__get16be(z->s)-2;
+ while (L > 0) {
+ int q = stbi__get8(z->s);
+ int p = q >> 4, sixteen = (p != 0);
+ int t = q & 15,i;
+ if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG");
+ if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG");
+
+ for (i=0; i < 64; ++i)
+ z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s));
+ L -= (sixteen ? 129 : 65);
+ }
+ return L==0;
+
+ case 0xC4: // DHT - define huffman table
+ L = stbi__get16be(z->s)-2;
+ while (L > 0) {
+ stbi_uc *v;
+ int sizes[16],i,n=0;
+ int q = stbi__get8(z->s);
+ int tc = q >> 4;
+ int th = q & 15;
+ if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG");
+ for (i=0; i < 16; ++i) {
+ sizes[i] = stbi__get8(z->s);
+ n += sizes[i];
+ }
+ if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values!
+ L -= 17;
+ if (tc == 0) {
+ if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0;
+ v = z->huff_dc[th].values;
+ } else {
+ if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0;
+ v = z->huff_ac[th].values;
+ }
+ for (i=0; i < n; ++i)
+ v[i] = stbi__get8(z->s);
+ if (tc != 0)
+ stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th);
+ L -= n;
+ }
+ return L==0;
+ }
+
+ // check for comment block or APP blocks
+ if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) {
+ L = stbi__get16be(z->s);
+ if (L < 2) {
+ if (m == 0xFE)
+ return stbi__err("bad COM len","Corrupt JPEG");
+ else
+ return stbi__err("bad APP len","Corrupt JPEG");
+ }
+ L -= 2;
+
+ if (m == 0xE0 && L >= 5) { // JFIF APP0 segment
+ static const unsigned char tag[5] = {'J','F','I','F','\0'};
+ int ok = 1;
+ int i;
+ for (i=0; i < 5; ++i)
+ if (stbi__get8(z->s) != tag[i])
+ ok = 0;
+ L -= 5;
+ if (ok)
+ z->jfif = 1;
+ } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment
+ static const unsigned char tag[6] = {'A','d','o','b','e','\0'};
+ int ok = 1;
+ int i;
+ for (i=0; i < 6; ++i)
+ if (stbi__get8(z->s) != tag[i])
+ ok = 0;
+ L -= 6;
+ if (ok) {
+ stbi__get8(z->s); // version
+ stbi__get16be(z->s); // flags0
+ stbi__get16be(z->s); // flags1
+ z->app14_color_transform = stbi__get8(z->s); // color transform
+ L -= 6;
+ }
+ }
+
+ stbi__skip(z->s, L);
+ return 1;
+ }
+
+ return stbi__err("unknown marker","Corrupt JPEG");
+}
+
+// after we see SOS
+static int stbi__process_scan_header(stbi__jpeg *z)
+{
+ int i;
+ int Ls = stbi__get16be(z->s);
+ z->scan_n = stbi__get8(z->s);
+ if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG");
+ if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG");
+ for (i=0; i < z->scan_n; ++i) {
+ int id = stbi__get8(z->s), which;
+ int q = stbi__get8(z->s);
+ for (which = 0; which < z->s->img_n; ++which)
+ if (z->img_comp[which].id == id)
+ break;
+ if (which == z->s->img_n) return 0; // no match
+ z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG");
+ z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG");
+ z->order[i] = which;
+ }
+
+ {
+ int aa;
+ z->spec_start = stbi__get8(z->s);
+ z->spec_end = stbi__get8(z->s); // should be 63, but might be 0
+ aa = stbi__get8(z->s);
+ z->succ_high = (aa >> 4);
+ z->succ_low = (aa & 15);
+ if (z->progressive) {
+ if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13)
+ return stbi__err("bad SOS", "Corrupt JPEG");
+ } else {
+ if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG");
+ if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG");
+ z->spec_end = 63;
+ }
+ }
+
+ return 1;
+}
+
+static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why)
+{
+ int i;
+ for (i=0; i < ncomp; ++i) {
+ if (z->img_comp[i].raw_data) {
+ STBI_FREE(z->img_comp[i].raw_data);
+ z->img_comp[i].raw_data = NULL;
+ z->img_comp[i].data = NULL;
+ }
+ if (z->img_comp[i].raw_coeff) {
+ STBI_FREE(z->img_comp[i].raw_coeff);
+ z->img_comp[i].raw_coeff = 0;
+ z->img_comp[i].coeff = 0;
+ }
+ if (z->img_comp[i].linebuf) {
+ STBI_FREE(z->img_comp[i].linebuf);
+ z->img_comp[i].linebuf = NULL;
+ }
+ }
+ return why;
+}
+
+static int stbi__process_frame_header(stbi__jpeg *z, int scan)
+{
+ stbi__context *s = z->s;
+ int Lf,p,i,q, h_max=1,v_max=1,c;
+ Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG
+ p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline
+ s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG
+ s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires
+ if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)");
+ if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)");
+ c = stbi__get8(s);
+ if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG");
+ s->img_n = c;
+ for (i=0; i < c; ++i) {
+ z->img_comp[i].data = NULL;
+ z->img_comp[i].linebuf = NULL;
+ }
+
+ if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG");
+
+ z->rgb = 0;
+ for (i=0; i < s->img_n; ++i) {
+ static const unsigned char rgb[3] = { 'R', 'G', 'B' };
+ z->img_comp[i].id = stbi__get8(s);
+ if (s->img_n == 3 && z->img_comp[i].id == rgb[i])
+ ++z->rgb;
+ q = stbi__get8(s);
+ z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG");
+ z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG");
+ z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG");
+ }
+
+ if (scan != STBI__SCAN_load) return 1;
+
+ if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode");
+
+ for (i=0; i < s->img_n; ++i) {
+ if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h;
+ if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v;
+ }
+
+ // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios
+ // and I've never seen a non-corrupted JPEG file actually use them
+ for (i=0; i < s->img_n; ++i) {
+ if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG");
+ if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG");
+ }
+
+ // compute interleaved mcu info
+ z->img_h_max = h_max;
+ z->img_v_max = v_max;
+ z->img_mcu_w = h_max * 8;
+ z->img_mcu_h = v_max * 8;
+ // these sizes can't be more than 17 bits
+ z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w;
+ z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h;
+
+ for (i=0; i < s->img_n; ++i) {
+ // number of effective pixels (e.g. for non-interleaved MCU)
+ z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max;
+ z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max;
+ // to simplify generation, we'll allocate enough memory to decode
+ // the bogus oversized data from using interleaved MCUs and their
+ // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't
+ // discard the extra data until colorspace conversion
+ //
+ // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier)
+ // so these muls can't overflow with 32-bit ints (which we require)
+ z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8;
+ z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8;
+ z->img_comp[i].coeff = 0;
+ z->img_comp[i].raw_coeff = 0;
+ z->img_comp[i].linebuf = NULL;
+ z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15);
+ if (z->img_comp[i].raw_data == NULL)
+ return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory"));
+ // align blocks for idct using mmx/sse
+ z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15);
+ if (z->progressive) {
+ // w2, h2 are multiples of 8 (see above)
+ z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8;
+ z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8;
+ z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15);
+ if (z->img_comp[i].raw_coeff == NULL)
+ return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory"));
+ z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15);
+ }
+ }
+
+ return 1;
+}
+
+// use comparisons since in some cases we handle more than one case (e.g. SOF)
+#define stbi__DNL(x) ((x) == 0xdc)
+#define stbi__SOI(x) ((x) == 0xd8)
+#define stbi__EOI(x) ((x) == 0xd9)
+#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2)
+#define stbi__SOS(x) ((x) == 0xda)
+
+#define stbi__SOF_progressive(x) ((x) == 0xc2)
+
+static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan)
+{
+ int m;
+ z->jfif = 0;
+ z->app14_color_transform = -1; // valid values are 0,1,2
+ z->marker = STBI__MARKER_none; // initialize cached marker to empty
+ m = stbi__get_marker(z);
+ if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG");
+ if (scan == STBI__SCAN_type) return 1;
+ m = stbi__get_marker(z);
+ while (!stbi__SOF(m)) {
+ if (!stbi__process_marker(z,m)) return 0;
+ m = stbi__get_marker(z);
+ while (m == STBI__MARKER_none) {
+ // some files have extra padding after their blocks, so ok, we'll scan
+ if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG");
+ m = stbi__get_marker(z);
+ }
+ }
+ z->progressive = stbi__SOF_progressive(m);
+ if (!stbi__process_frame_header(z, scan)) return 0;
+ return 1;
+}
+
+static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j)
+{
+ // some JPEGs have junk at end, skip over it but if we find what looks
+ // like a valid marker, resume there
+ while (!stbi__at_eof(j->s)) {
+ stbi_uc x = stbi__get8(j->s);
+ while (x == 0xff) { // might be a marker
+ if (stbi__at_eof(j->s)) return STBI__MARKER_none;
+ x = stbi__get8(j->s);
+ if (x != 0x00 && x != 0xff) {
+ // not a stuffed zero or lead-in to another marker, looks
+ // like an actual marker, return it
+ return x;
+ }
+ // stuffed zero has x=0 now which ends the loop, meaning we go
+ // back to regular scan loop.
+ // repeated 0xff keeps trying to read the next byte of the marker.
+ }
+ }
+ return STBI__MARKER_none;
+}
+
+// decode image to YCbCr format
+static int stbi__decode_jpeg_image(stbi__jpeg *j)
+{
+ int m;
+ for (m = 0; m < 4; m++) {
+ j->img_comp[m].raw_data = NULL;
+ j->img_comp[m].raw_coeff = NULL;
+ }
+ j->restart_interval = 0;
+ if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0;
+ m = stbi__get_marker(j);
+ while (!stbi__EOI(m)) {
+ if (stbi__SOS(m)) {
+ if (!stbi__process_scan_header(j)) return 0;
+ if (!stbi__parse_entropy_coded_data(j)) return 0;
+ if (j->marker == STBI__MARKER_none ) {
+ j->marker = stbi__skip_jpeg_junk_at_end(j);
+ // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0
+ }
+ m = stbi__get_marker(j);
+ if (STBI__RESTART(m))
+ m = stbi__get_marker(j);
+ } else if (stbi__DNL(m)) {
+ int Ld = stbi__get16be(j->s);
+ stbi__uint32 NL = stbi__get16be(j->s);
+ if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG");
+ if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG");
+ m = stbi__get_marker(j);
+ } else {
+ if (!stbi__process_marker(j, m)) return 1;
+ m = stbi__get_marker(j);
+ }
+ }
+ if (j->progressive)
+ stbi__jpeg_finish(j);
+ return 1;
+}
+
+// static jfif-centered resampling (across block boundaries)
+
+typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1,
+ int w, int hs);
+
+#define stbi__div4(x) ((stbi_uc) ((x) >> 2))
+
+static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
+{
+ STBI_NOTUSED(out);
+ STBI_NOTUSED(in_far);
+ STBI_NOTUSED(w);
+ STBI_NOTUSED(hs);
+ return in_near;
+}
+
+static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
+{
+ // need to generate two samples vertically for every one in input
+ int i;
+ STBI_NOTUSED(hs);
+ for (i=0; i < w; ++i)
+ out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2);
+ return out;
+}
+
+static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
+{
+ // need to generate two samples horizontally for every one in input
+ int i;
+ stbi_uc *input = in_near;
+
+ if (w == 1) {
+ // if only one sample, can't do any interpolation
+ out[0] = out[1] = input[0];
+ return out;
+ }
+
+ out[0] = input[0];
+ out[1] = stbi__div4(input[0]*3 + input[1] + 2);
+ for (i=1; i < w-1; ++i) {
+ int n = 3*input[i]+2;
+ out[i*2+0] = stbi__div4(n+input[i-1]);
+ out[i*2+1] = stbi__div4(n+input[i+1]);
+ }
+ out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2);
+ out[i*2+1] = input[w-1];
+
+ STBI_NOTUSED(in_far);
+ STBI_NOTUSED(hs);
+
+ return out;
+}
+
+#define stbi__div16(x) ((stbi_uc) ((x) >> 4))
+
+static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
+{
+ // need to generate 2x2 samples for every one in input
+ int i,t0,t1;
+ if (w == 1) {
+ out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2);
+ return out;
+ }
+
+ t1 = 3*in_near[0] + in_far[0];
+ out[0] = stbi__div4(t1+2);
+ for (i=1; i < w; ++i) {
+ t0 = t1;
+ t1 = 3*in_near[i]+in_far[i];
+ out[i*2-1] = stbi__div16(3*t0 + t1 + 8);
+ out[i*2 ] = stbi__div16(3*t1 + t0 + 8);
+ }
+ out[w*2-1] = stbi__div4(t1+2);
+
+ STBI_NOTUSED(hs);
+
+ return out;
+}
+
+#if defined(STBI_SSE2) || defined(STBI_NEON)
+static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
+{
+ // need to generate 2x2 samples for every one in input
+ int i=0,t0,t1;
+
+ if (w == 1) {
+ out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2);
+ return out;
+ }
+
+ t1 = 3*in_near[0] + in_far[0];
+ // process groups of 8 pixels for as long as we can.
+ // note we can't handle the last pixel in a row in this loop
+ // because we need to handle the filter boundary conditions.
+ for (; i < ((w-1) & ~7); i += 8) {
+#if defined(STBI_SSE2)
+ // load and perform the vertical filtering pass
+ // this uses 3*x + y = 4*x + (y - x)
+ __m128i zero = _mm_setzero_si128();
+ __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i));
+ __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i));
+ __m128i farw = _mm_unpacklo_epi8(farb, zero);
+ __m128i nearw = _mm_unpacklo_epi8(nearb, zero);
+ __m128i diff = _mm_sub_epi16(farw, nearw);
+ __m128i nears = _mm_slli_epi16(nearw, 2);
+ __m128i curr = _mm_add_epi16(nears, diff); // current row
+
+ // horizontal filter works the same based on shifted vers of current
+ // row. "prev" is current row shifted right by 1 pixel; we need to
+ // insert the previous pixel value (from t1).
+ // "next" is current row shifted left by 1 pixel, with first pixel
+ // of next block of 8 pixels added in.
+ __m128i prv0 = _mm_slli_si128(curr, 2);
+ __m128i nxt0 = _mm_srli_si128(curr, 2);
+ __m128i prev = _mm_insert_epi16(prv0, t1, 0);
+ __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7);
+
+ // horizontal filter, polyphase implementation since it's convenient:
+ // even pixels = 3*cur + prev = cur*4 + (prev - cur)
+ // odd pixels = 3*cur + next = cur*4 + (next - cur)
+ // note the shared term.
+ __m128i bias = _mm_set1_epi16(8);
+ __m128i curs = _mm_slli_epi16(curr, 2);
+ __m128i prvd = _mm_sub_epi16(prev, curr);
+ __m128i nxtd = _mm_sub_epi16(next, curr);
+ __m128i curb = _mm_add_epi16(curs, bias);
+ __m128i even = _mm_add_epi16(prvd, curb);
+ __m128i odd = _mm_add_epi16(nxtd, curb);
+
+ // interleave even and odd pixels, then undo scaling.
+ __m128i int0 = _mm_unpacklo_epi16(even, odd);
+ __m128i int1 = _mm_unpackhi_epi16(even, odd);
+ __m128i de0 = _mm_srli_epi16(int0, 4);
+ __m128i de1 = _mm_srli_epi16(int1, 4);
+
+ // pack and write output
+ __m128i outv = _mm_packus_epi16(de0, de1);
+ _mm_storeu_si128((__m128i *) (out + i*2), outv);
+#elif defined(STBI_NEON)
+ // load and perform the vertical filtering pass
+ // this uses 3*x + y = 4*x + (y - x)
+ uint8x8_t farb = vld1_u8(in_far + i);
+ uint8x8_t nearb = vld1_u8(in_near + i);
+ int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb));
+ int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2));
+ int16x8_t curr = vaddq_s16(nears, diff); // current row
+
+ // horizontal filter works the same based on shifted vers of current
+ // row. "prev" is current row shifted right by 1 pixel; we need to
+ // insert the previous pixel value (from t1).
+ // "next" is current row shifted left by 1 pixel, with first pixel
+ // of next block of 8 pixels added in.
+ int16x8_t prv0 = vextq_s16(curr, curr, 7);
+ int16x8_t nxt0 = vextq_s16(curr, curr, 1);
+ int16x8_t prev = vsetq_lane_s16(t1, prv0, 0);
+ int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7);
+
+ // horizontal filter, polyphase implementation since it's convenient:
+ // even pixels = 3*cur + prev = cur*4 + (prev - cur)
+ // odd pixels = 3*cur + next = cur*4 + (next - cur)
+ // note the shared term.
+ int16x8_t curs = vshlq_n_s16(curr, 2);
+ int16x8_t prvd = vsubq_s16(prev, curr);
+ int16x8_t nxtd = vsubq_s16(next, curr);
+ int16x8_t even = vaddq_s16(curs, prvd);
+ int16x8_t odd = vaddq_s16(curs, nxtd);
+
+ // undo scaling and round, then store with even/odd phases interleaved
+ uint8x8x2_t o;
+ o.val[0] = vqrshrun_n_s16(even, 4);
+ o.val[1] = vqrshrun_n_s16(odd, 4);
+ vst2_u8(out + i*2, o);
+#endif
+
+ // "previous" value for next iter
+ t1 = 3*in_near[i+7] + in_far[i+7];
+ }
+
+ t0 = t1;
+ t1 = 3*in_near[i] + in_far[i];
+ out[i*2] = stbi__div16(3*t1 + t0 + 8);
+
+ for (++i; i < w; ++i) {
+ t0 = t1;
+ t1 = 3*in_near[i]+in_far[i];
+ out[i*2-1] = stbi__div16(3*t0 + t1 + 8);
+ out[i*2 ] = stbi__div16(3*t1 + t0 + 8);
+ }
+ out[w*2-1] = stbi__div4(t1+2);
+
+ STBI_NOTUSED(hs);
+
+ return out;
+}
+#endif
+
+static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
+{
+ // resample with nearest-neighbor
+ int i,j;
+ STBI_NOTUSED(in_far);
+ for (i=0; i < w; ++i)
+ for (j=0; j < hs; ++j)
+ out[i*hs+j] = in_near[i];
+ return out;
+}
+
+// this is a reduced-precision calculation of YCbCr-to-RGB introduced
+// to make sure the code produces the same results in both SIMD and scalar
+#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8)
+static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step)
+{
+ int i;
+ for (i=0; i < count; ++i) {
+ int y_fixed = (y[i] << 20) + (1<<19); // rounding
+ int r,g,b;
+ int cr = pcr[i] - 128;
+ int cb = pcb[i] - 128;
+ r = y_fixed + cr* stbi__float2fixed(1.40200f);
+ g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000);
+ b = y_fixed + cb* stbi__float2fixed(1.77200f);
+ r >>= 20;
+ g >>= 20;
+ b >>= 20;
+ if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; }
+ if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; }
+ if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; }
+ out[0] = (stbi_uc)r;
+ out[1] = (stbi_uc)g;
+ out[2] = (stbi_uc)b;
+ out[3] = 255;
+ out += step;
+ }
+}
+
+#if defined(STBI_SSE2) || defined(STBI_NEON)
+static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step)
+{
+ int i = 0;
+
+#ifdef STBI_SSE2
+ // step == 3 is pretty ugly on the final interleave, and i'm not convinced
+ // it's useful in practice (you wouldn't use it for textures, for example).
+ // so just accelerate step == 4 case.
+ if (step == 4) {
+ // this is a fairly straightforward implementation and not super-optimized.
+ __m128i signflip = _mm_set1_epi8(-0x80);
+ __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f));
+ __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f));
+ __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f));
+ __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f));
+ __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128);
+ __m128i xw = _mm_set1_epi16(255); // alpha channel
+
+ for (; i+7 < count; i += 8) {
+ // load
+ __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i));
+ __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i));
+ __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i));
+ __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128
+ __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128
+
+ // unpack to short (and left-shift cr, cb by 8)
+ __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes);
+ __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased);
+ __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased);
+
+ // color transform
+ __m128i yws = _mm_srli_epi16(yw, 4);
+ __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw);
+ __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw);
+ __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1);
+ __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1);
+ __m128i rws = _mm_add_epi16(cr0, yws);
+ __m128i gwt = _mm_add_epi16(cb0, yws);
+ __m128i bws = _mm_add_epi16(yws, cb1);
+ __m128i gws = _mm_add_epi16(gwt, cr1);
+
+ // descale
+ __m128i rw = _mm_srai_epi16(rws, 4);
+ __m128i bw = _mm_srai_epi16(bws, 4);
+ __m128i gw = _mm_srai_epi16(gws, 4);
+
+ // back to byte, set up for transpose
+ __m128i brb = _mm_packus_epi16(rw, bw);
+ __m128i gxb = _mm_packus_epi16(gw, xw);
+
+ // transpose to interleave channels
+ __m128i t0 = _mm_unpacklo_epi8(brb, gxb);
+ __m128i t1 = _mm_unpackhi_epi8(brb, gxb);
+ __m128i o0 = _mm_unpacklo_epi16(t0, t1);
+ __m128i o1 = _mm_unpackhi_epi16(t0, t1);
+
+ // store
+ _mm_storeu_si128((__m128i *) (out + 0), o0);
+ _mm_storeu_si128((__m128i *) (out + 16), o1);
+ out += 32;
+ }
+ }
+#endif
+
+#ifdef STBI_NEON
+ // in this version, step=3 support would be easy to add. but is there demand?
+ if (step == 4) {
+ // this is a fairly straightforward implementation and not super-optimized.
+ uint8x8_t signflip = vdup_n_u8(0x80);
+ int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f));
+ int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f));
+ int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f));
+ int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f));
+
+ for (; i+7 < count; i += 8) {
+ // load
+ uint8x8_t y_bytes = vld1_u8(y + i);
+ uint8x8_t cr_bytes = vld1_u8(pcr + i);
+ uint8x8_t cb_bytes = vld1_u8(pcb + i);
+ int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip));
+ int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip));
+
+ // expand to s16
+ int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4));
+ int16x8_t crw = vshll_n_s8(cr_biased, 7);
+ int16x8_t cbw = vshll_n_s8(cb_biased, 7);
+
+ // color transform
+ int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0);
+ int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0);
+ int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1);
+ int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1);
+ int16x8_t rws = vaddq_s16(yws, cr0);
+ int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1);
+ int16x8_t bws = vaddq_s16(yws, cb1);
+
+ // undo scaling, round, convert to byte
+ uint8x8x4_t o;
+ o.val[0] = vqrshrun_n_s16(rws, 4);
+ o.val[1] = vqrshrun_n_s16(gws, 4);
+ o.val[2] = vqrshrun_n_s16(bws, 4);
+ o.val[3] = vdup_n_u8(255);
+
+ // store, interleaving r/g/b/a
+ vst4_u8(out, o);
+ out += 8*4;
+ }
+ }
+#endif
+
+ for (; i < count; ++i) {
+ int y_fixed = (y[i] << 20) + (1<<19); // rounding
+ int r,g,b;
+ int cr = pcr[i] - 128;
+ int cb = pcb[i] - 128;
+ r = y_fixed + cr* stbi__float2fixed(1.40200f);
+ g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000);
+ b = y_fixed + cb* stbi__float2fixed(1.77200f);
+ r >>= 20;
+ g >>= 20;
+ b >>= 20;
+ if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; }
+ if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; }
+ if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; }
+ out[0] = (stbi_uc)r;
+ out[1] = (stbi_uc)g;
+ out[2] = (stbi_uc)b;
+ out[3] = 255;
+ out += step;
+ }
+}
+#endif
+
+// set up the kernels
+static void stbi__setup_jpeg(stbi__jpeg *j)
+{
+ j->idct_block_kernel = stbi__idct_block;
+ j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row;
+ j->resample_row_hv_2_kernel = stbi__resample_row_hv_2;
+
+#ifdef STBI_SSE2
+ if (stbi__sse2_available()) {
+ j->idct_block_kernel = stbi__idct_simd;
+ j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd;
+ j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd;
+ }
+#endif
+
+#ifdef STBI_NEON
+ j->idct_block_kernel = stbi__idct_simd;
+ j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd;
+ j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd;
+#endif
+}
+
+// clean up the temporary component buffers
+static void stbi__cleanup_jpeg(stbi__jpeg *j)
+{
+ stbi__free_jpeg_components(j, j->s->img_n, 0);
+}
+
+typedef struct
+{
+ resample_row_func resample;
+ stbi_uc *line0,*line1;
+ int hs,vs; // expansion factor in each axis
+ int w_lores; // horizontal pixels pre-expansion
+ int ystep; // how far through vertical expansion we are
+ int ypos; // which pre-expansion row we're on
+} stbi__resample;
+
+// fast 0..255 * 0..255 => 0..255 rounded multiplication
+static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y)
+{
+ unsigned int t = x*y + 128;
+ return (stbi_uc) ((t + (t >>8)) >> 8);
+}
+
+static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp)
+{
+ int n, decode_n, is_rgb;
+ z->s->img_n = 0; // make stbi__cleanup_jpeg safe
+
+ // validate req_comp
+ if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error");
+
+ // load a jpeg image from whichever source, but leave in YCbCr format
+ if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; }
+
+ // determine actual number of components to generate
+ n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1;
+
+ is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif));
+
+ if (z->s->img_n == 3 && n < 3 && !is_rgb)
+ decode_n = 1;
+ else
+ decode_n = z->s->img_n;
+
+ // nothing to do if no components requested; check this now to avoid
+ // accessing uninitialized coutput[0] later
+ if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; }
+
+ // resample and color-convert
+ {
+ int k;
+ unsigned int i,j;
+ stbi_uc *output;
+ stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL };
+
+ stbi__resample res_comp[4];
+
+ for (k=0; k < decode_n; ++k) {
+ stbi__resample *r = &res_comp[k];
+
+ // allocate line buffer big enough for upsampling off the edges
+ // with upsample factor of 4
+ z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3);
+ if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); }
+
+ r->hs = z->img_h_max / z->img_comp[k].h;
+ r->vs = z->img_v_max / z->img_comp[k].v;
+ r->ystep = r->vs >> 1;
+ r->w_lores = (z->s->img_x + r->hs-1) / r->hs;
+ r->ypos = 0;
+ r->line0 = r->line1 = z->img_comp[k].data;
+
+ if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1;
+ else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2;
+ else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2;
+ else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel;
+ else r->resample = stbi__resample_row_generic;
+ }
+
+ // can't error after this so, this is safe
+ output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1);
+ if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); }
+
+ // now go ahead and resample
+ for (j=0; j < z->s->img_y; ++j) {
+ stbi_uc *out = output + n * z->s->img_x * j;
+ for (k=0; k < decode_n; ++k) {
+ stbi__resample *r = &res_comp[k];
+ int y_bot = r->ystep >= (r->vs >> 1);
+ coutput[k] = r->resample(z->img_comp[k].linebuf,
+ y_bot ? r->line1 : r->line0,
+ y_bot ? r->line0 : r->line1,
+ r->w_lores, r->hs);
+ if (++r->ystep >= r->vs) {
+ r->ystep = 0;
+ r->line0 = r->line1;
+ if (++r->ypos < z->img_comp[k].y)
+ r->line1 += z->img_comp[k].w2;
+ }
+ }
+ if (n >= 3) {
+ stbi_uc *y = coutput[0];
+ if (z->s->img_n == 3) {
+ if (is_rgb) {
+ for (i=0; i < z->s->img_x; ++i) {
+ out[0] = y[i];
+ out[1] = coutput[1][i];
+ out[2] = coutput[2][i];
+ out[3] = 255;
+ out += n;
+ }
+ } else {
+ z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
+ }
+ } else if (z->s->img_n == 4) {
+ if (z->app14_color_transform == 0) { // CMYK
+ for (i=0; i < z->s->img_x; ++i) {
+ stbi_uc m = coutput[3][i];
+ out[0] = stbi__blinn_8x8(coutput[0][i], m);
+ out[1] = stbi__blinn_8x8(coutput[1][i], m);
+ out[2] = stbi__blinn_8x8(coutput[2][i], m);
+ out[3] = 255;
+ out += n;
+ }
+ } else if (z->app14_color_transform == 2) { // YCCK
+ z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
+ for (i=0; i < z->s->img_x; ++i) {
+ stbi_uc m = coutput[3][i];
+ out[0] = stbi__blinn_8x8(255 - out[0], m);
+ out[1] = stbi__blinn_8x8(255 - out[1], m);
+ out[2] = stbi__blinn_8x8(255 - out[2], m);
+ out += n;
+ }
+ } else { // YCbCr + alpha? Ignore the fourth channel for now
+ z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
+ }
+ } else
+ for (i=0; i < z->s->img_x; ++i) {
+ out[0] = out[1] = out[2] = y[i];
+ out[3] = 255; // not used if n==3
+ out += n;
+ }
+ } else {
+ if (is_rgb) {
+ if (n == 1)
+ for (i=0; i < z->s->img_x; ++i)
+ *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]);
+ else {
+ for (i=0; i < z->s->img_x; ++i, out += 2) {
+ out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]);
+ out[1] = 255;
+ }
+ }
+ } else if (z->s->img_n == 4 && z->app14_color_transform == 0) {
+ for (i=0; i < z->s->img_x; ++i) {
+ stbi_uc m = coutput[3][i];
+ stbi_uc r = stbi__blinn_8x8(coutput[0][i], m);
+ stbi_uc g = stbi__blinn_8x8(coutput[1][i], m);
+ stbi_uc b = stbi__blinn_8x8(coutput[2][i], m);
+ out[0] = stbi__compute_y(r, g, b);
+ out[1] = 255;
+ out += n;
+ }
+ } else if (z->s->img_n == 4 && z->app14_color_transform == 2) {
+ for (i=0; i < z->s->img_x; ++i) {
+ out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]);
+ out[1] = 255;
+ out += n;
+ }
+ } else {
+ stbi_uc *y = coutput[0];
+ if (n == 1)
+ for (i=0; i < z->s->img_x; ++i) out[i] = y[i];
+ else
+ for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; }
+ }
+ }
+ }
+ stbi__cleanup_jpeg(z);
+ *out_x = z->s->img_x;
+ *out_y = z->s->img_y;
+ if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output
+ return output;
+ }
+}
+
+static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
+{
+ unsigned char* result;
+ stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg));
+ if (!j) return stbi__errpuc("outofmem", "Out of memory");
+ memset(j, 0, sizeof(stbi__jpeg));
+ STBI_NOTUSED(ri);
+ j->s = s;
+ stbi__setup_jpeg(j);
+ result = load_jpeg_image(j, x,y,comp,req_comp);
+ STBI_FREE(j);
+ return result;
+}
+
+static int stbi__jpeg_test(stbi__context *s)
+{
+ int r;
+ stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg));
+ if (!j) return stbi__err("outofmem", "Out of memory");
+ memset(j, 0, sizeof(stbi__jpeg));
+ j->s = s;
+ stbi__setup_jpeg(j);
+ r = stbi__decode_jpeg_header(j, STBI__SCAN_type);
+ stbi__rewind(s);
+ STBI_FREE(j);
+ return r;
+}
+
+static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp)
+{
+ if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) {
+ stbi__rewind( j->s );
+ return 0;
+ }
+ if (x) *x = j->s->img_x;
+ if (y) *y = j->s->img_y;
+ if (comp) *comp = j->s->img_n >= 3 ? 3 : 1;
+ return 1;
+}
+
+static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
+{
+ int result;
+ stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg)));
+ if (!j) return stbi__err("outofmem", "Out of memory");
+ memset(j, 0, sizeof(stbi__jpeg));
+ j->s = s;
+ result = stbi__jpeg_info_raw(j, x, y, comp);
+ STBI_FREE(j);
+ return result;
+}
+#endif
+
+// public domain zlib decode v0.2 Sean Barrett 2006-11-18
+// simple implementation
+// - all input must be provided in an upfront buffer
+// - all output is written to a single output buffer (can malloc/realloc)
+// performance
+// - fast huffman
+
+#ifndef STBI_NO_ZLIB
+
+// fast-way is faster to check than jpeg huffman, but slow way is slower
+#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables
+#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1)
+#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet
+
+// zlib-style huffman encoding
+// (jpegs packs from left, zlib from right, so can't share code)
+typedef struct
+{
+ stbi__uint16 fast[1 << STBI__ZFAST_BITS];
+ stbi__uint16 firstcode[16];
+ int maxcode[17];
+ stbi__uint16 firstsymbol[16];
+ stbi_uc size[STBI__ZNSYMS];
+ stbi__uint16 value[STBI__ZNSYMS];
+} stbi__zhuffman;
+
+stbi_inline static int stbi__bitreverse16(int n)
+{
+ n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1);
+ n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2);
+ n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4);
+ n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8);
+ return n;
+}
+
+stbi_inline static int stbi__bit_reverse(int v, int bits)
+{
+ STBI_ASSERT(bits <= 16);
+ // to bit reverse n bits, reverse 16 and shift
+ // e.g. 11 bits, bit reverse and shift away 5
+ return stbi__bitreverse16(v) >> (16-bits);
+}
+
+static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num)
+{
+ int i,k=0;
+ int code, next_code[16], sizes[17];
+
+ // DEFLATE spec for generating codes
+ memset(sizes, 0, sizeof(sizes));
+ memset(z->fast, 0, sizeof(z->fast));
+ for (i=0; i < num; ++i)
+ ++sizes[sizelist[i]];
+ sizes[0] = 0;
+ for (i=1; i < 16; ++i)
+ if (sizes[i] > (1 << i))
+ return stbi__err("bad sizes", "Corrupt PNG");
+ code = 0;
+ for (i=1; i < 16; ++i) {
+ next_code[i] = code;
+ z->firstcode[i] = (stbi__uint16) code;
+ z->firstsymbol[i] = (stbi__uint16) k;
+ code = (code + sizes[i]);
+ if (sizes[i])
+ if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG");
+ z->maxcode[i] = code << (16-i); // preshift for inner loop
+ code <<= 1;
+ k += sizes[i];
+ }
+ z->maxcode[16] = 0x10000; // sentinel
+ for (i=0; i < num; ++i) {
+ int s = sizelist[i];
+ if (s) {
+ int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s];
+ stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i);
+ z->size [c] = (stbi_uc ) s;
+ z->value[c] = (stbi__uint16) i;
+ if (s <= STBI__ZFAST_BITS) {
+ int j = stbi__bit_reverse(next_code[s],s);
+ while (j < (1 << STBI__ZFAST_BITS)) {
+ z->fast[j] = fastv;
+ j += (1 << s);
+ }
+ }
+ ++next_code[s];
+ }
+ }
+ return 1;
+}
+
+// zlib-from-memory implementation for PNG reading
+// because PNG allows splitting the zlib stream arbitrarily,
+// and it's annoying structurally to have PNG call ZLIB call PNG,
+// we require PNG read all the IDATs and combine them into a single
+// memory buffer
+
+typedef struct
+{
+ stbi_uc *zbuffer, *zbuffer_end;
+ int num_bits;
+ int hit_zeof_once;
+ stbi__uint32 code_buffer;
+
+ char *zout;
+ char *zout_start;
+ char *zout_end;
+ int z_expandable;
+
+ stbi__zhuffman z_length, z_distance;
+} stbi__zbuf;
+
+stbi_inline static int stbi__zeof(stbi__zbuf *z)
+{
+ return (z->zbuffer >= z->zbuffer_end);
+}
+
+stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z)
+{
+ return stbi__zeof(z) ? 0 : *z->zbuffer++;
+}
+
+static void stbi__fill_bits(stbi__zbuf *z)
+{
+ do {
+ if (z->code_buffer >= (1U << z->num_bits)) {
+ z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */
+ return;
+ }
+ z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits;
+ z->num_bits += 8;
+ } while (z->num_bits <= 24);
+}
+
+stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n)
+{
+ unsigned int k;
+ if (z->num_bits < n) stbi__fill_bits(z);
+ k = z->code_buffer & ((1 << n) - 1);
+ z->code_buffer >>= n;
+ z->num_bits -= n;
+ return k;
+}
+
+static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z)
+{
+ int b,s,k;
+ // not resolved by fast table, so compute it the slow way
+ // use jpeg approach, which requires MSbits at top
+ k = stbi__bit_reverse(a->code_buffer, 16);
+ for (s=STBI__ZFAST_BITS+1; ; ++s)
+ if (k < z->maxcode[s])
+ break;
+ if (s >= 16) return -1; // invalid code!
+ // code size is s, so:
+ b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s];
+ if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere!
+ if (z->size[b] != s) return -1; // was originally an assert, but report failure instead.
+ a->code_buffer >>= s;
+ a->num_bits -= s;
+ return z->value[b];
+}
+
+stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z)
+{
+ int b,s;
+ if (a->num_bits < 16) {
+ if (stbi__zeof(a)) {
+ if (!a->hit_zeof_once) {
+ // This is the first time we hit eof, insert 16 extra padding btis
+ // to allow us to keep going; if we actually consume any of them
+ // though, that is invalid data. This is caught later.
+ a->hit_zeof_once = 1;
+ a->num_bits += 16; // add 16 implicit zero bits
+ } else {
+ // We already inserted our extra 16 padding bits and are again
+ // out, this stream is actually prematurely terminated.
+ return -1;
+ }
+ } else {
+ stbi__fill_bits(a);
+ }
+ }
+ b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
+ if (b) {
+ s = b >> 9;
+ a->code_buffer >>= s;
+ a->num_bits -= s;
+ return b & 511;
+ }
+ return stbi__zhuffman_decode_slowpath(a, z);
+}
+
+static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes
+{
+ char *q;
+ unsigned int cur, limit, old_limit;
+ z->zout = zout;
+ if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG");
+ cur = (unsigned int) (z->zout - z->zout_start);
+ limit = old_limit = (unsigned) (z->zout_end - z->zout_start);
+ if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory");
+ while (cur + n > limit) {
+ if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory");
+ limit *= 2;
+ }
+ q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit);
+ STBI_NOTUSED(old_limit);
+ if (q == NULL) return stbi__err("outofmem", "Out of memory");
+ z->zout_start = q;
+ z->zout = q + cur;
+ z->zout_end = q + limit;
+ return 1;
+}
+
+static const int stbi__zlength_base[31] = {
+ 3,4,5,6,7,8,9,10,11,13,
+ 15,17,19,23,27,31,35,43,51,59,
+ 67,83,99,115,131,163,195,227,258,0,0 };
+
+static const int stbi__zlength_extra[31]=
+{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
+
+static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,
+257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};
+
+static const int stbi__zdist_extra[32] =
+{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
+
+static int stbi__parse_huffman_block(stbi__zbuf *a)
+{
+ char *zout = a->zout;
+ for(;;) {
+ int z = stbi__zhuffman_decode(a, &a->z_length);
+ if (z < 256) {
+ if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes
+ if (zout >= a->zout_end) {
+ if (!stbi__zexpand(a, zout, 1)) return 0;
+ zout = a->zout;
+ }
+ *zout++ = (char) z;
+ } else {
+ stbi_uc *p;
+ int len,dist;
+ if (z == 256) {
+ a->zout = zout;
+ if (a->hit_zeof_once && a->num_bits < 16) {
+ // The first time we hit zeof, we inserted 16 extra zero bits into our bit
+ // buffer so the decoder can just do its speculative decoding. But if we
+ // actually consumed any of those bits (which is the case when num_bits < 16),
+ // the stream actually read past the end so it is malformed.
+ return stbi__err("unexpected end","Corrupt PNG");
+ }
+ return 1;
+ }
+ if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data
+ z -= 257;
+ len = stbi__zlength_base[z];
+ if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);
+ z = stbi__zhuffman_decode(a, &a->z_distance);
+ if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data
+ dist = stbi__zdist_base[z];
+ if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
+ if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
+ if (len > a->zout_end - zout) {
+ if (!stbi__zexpand(a, zout, len)) return 0;
+ zout = a->zout;
+ }
+ p = (stbi_uc *) (zout - dist);
+ if (dist == 1) { // run of one byte; common in images.
+ stbi_uc v = *p;
+ if (len) { do *zout++ = v; while (--len); }
+ } else {
+ if (len) { do *zout++ = *p++; while (--len); }
+ }
+ }
+ }
+}
+
+static int stbi__compute_huffman_codes(stbi__zbuf *a)
+{
+ static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
+ stbi__zhuffman z_codelength;
+ stbi_uc lencodes[286+32+137];//padding for maximum single op
+ stbi_uc codelength_sizes[19];
+ int i,n;
+
+ int hlit = stbi__zreceive(a,5) + 257;
+ int hdist = stbi__zreceive(a,5) + 1;
+ int hclen = stbi__zreceive(a,4) + 4;
+ int ntot = hlit + hdist;
+
+ memset(codelength_sizes, 0, sizeof(codelength_sizes));
+ for (i=0; i < hclen; ++i) {
+ int s = stbi__zreceive(a,3);
+ codelength_sizes[length_dezigzag[i]] = (stbi_uc) s;
+ }
+ if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0;
+
+ n = 0;
+ while (n < ntot) {
+ int c = stbi__zhuffman_decode(a, &z_codelength);
+ if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG");
+ if (c < 16)
+ lencodes[n++] = (stbi_uc) c;
+ else {
+ stbi_uc fill = 0;
+ if (c == 16) {
+ c = stbi__zreceive(a,2)+3;
+ if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG");
+ fill = lencodes[n-1];
+ } else if (c == 17) {
+ c = stbi__zreceive(a,3)+3;
+ } else if (c == 18) {
+ c = stbi__zreceive(a,7)+11;
+ } else {
+ return stbi__err("bad codelengths", "Corrupt PNG");
+ }
+ if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG");
+ memset(lencodes+n, fill, c);
+ n += c;
+ }
+ }
+ if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG");
+ if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0;
+ if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0;
+ return 1;
+}
+
+static int stbi__parse_uncompressed_block(stbi__zbuf *a)
+{
+ stbi_uc header[4];
+ int len,nlen,k;
+ if (a->num_bits & 7)
+ stbi__zreceive(a, a->num_bits & 7); // discard
+ // drain the bit-packed data into header
+ k = 0;
+ while (a->num_bits > 0) {
+ header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check
+ a->code_buffer >>= 8;
+ a->num_bits -= 8;
+ }
+ if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG");
+ // now fill header the normal way
+ while (k < 4)
+ header[k++] = stbi__zget8(a);
+ len = header[1] * 256 + header[0];
+ nlen = header[3] * 256 + header[2];
+ if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG");
+ if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG");
+ if (a->zout + len > a->zout_end)
+ if (!stbi__zexpand(a, a->zout, len)) return 0;
+ memcpy(a->zout, a->zbuffer, len);
+ a->zbuffer += len;
+ a->zout += len;
+ return 1;
+}
+
+static int stbi__parse_zlib_header(stbi__zbuf *a)
+{
+ int cmf = stbi__zget8(a);
+ int cm = cmf & 15;
+ /* int cinfo = cmf >> 4; */
+ int flg = stbi__zget8(a);
+ if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec
+ if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec
+ if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png
+ if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png
+ // window = 1 << (8 + cinfo)... but who cares, we fully buffer output
+ return 1;
+}
+
+static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] =
+{
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+ 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+ 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+ 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8
+};
+static const stbi_uc stbi__zdefault_distance[32] =
+{
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5
+};
+/*
+Init algorithm:
+{
+ int i; // use <= to match clearly with spec
+ for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8;
+ for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9;
+ for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7;
+ for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8;
+
+ for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5;
+}
+*/
+
+static int stbi__parse_zlib(stbi__zbuf *a, int parse_header)
+{
+ int final, type;
+ if (parse_header)
+ if (!stbi__parse_zlib_header(a)) return 0;
+ a->num_bits = 0;
+ a->code_buffer = 0;
+ a->hit_zeof_once = 0;
+ do {
+ final = stbi__zreceive(a,1);
+ type = stbi__zreceive(a,2);
+ if (type == 0) {
+ if (!stbi__parse_uncompressed_block(a)) return 0;
+ } else if (type == 3) {
+ return 0;
+ } else {
+ if (type == 1) {
+ // use fixed code lengths
+ if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0;
+ if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0;
+ } else {
+ if (!stbi__compute_huffman_codes(a)) return 0;
+ }
+ if (!stbi__parse_huffman_block(a)) return 0;
+ }
+ } while (!final);
+ return 1;
+}
+
+static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header)
+{
+ a->zout_start = obuf;
+ a->zout = obuf;
+ a->zout_end = obuf + olen;
+ a->z_expandable = exp;
+
+ return stbi__parse_zlib(a, parse_header);
+}
+
+STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen)
+{
+ stbi__zbuf a;
+ char *p = (char *) stbi__malloc(initial_size);
+ if (p == NULL) return NULL;
+ a.zbuffer = (stbi_uc *) buffer;
+ a.zbuffer_end = (stbi_uc *) buffer + len;
+ if (stbi__do_zlib(&a, p, initial_size, 1, 1)) {
+ if (outlen) *outlen = (int) (a.zout - a.zout_start);
+ return a.zout_start;
+ } else {
+ STBI_FREE(a.zout_start);
+ return NULL;
+ }
+}
+
+STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen)
+{
+ return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen);
+}
+
+STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header)
+{
+ stbi__zbuf a;
+ char *p = (char *) stbi__malloc(initial_size);
+ if (p == NULL) return NULL;
+ a.zbuffer = (stbi_uc *) buffer;
+ a.zbuffer_end = (stbi_uc *) buffer + len;
+ if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) {
+ if (outlen) *outlen = (int) (a.zout - a.zout_start);
+ return a.zout_start;
+ } else {
+ STBI_FREE(a.zout_start);
+ return NULL;
+ }
+}
+
+STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen)
+{
+ stbi__zbuf a;
+ a.zbuffer = (stbi_uc *) ibuffer;
+ a.zbuffer_end = (stbi_uc *) ibuffer + ilen;
+ if (stbi__do_zlib(&a, obuffer, olen, 0, 1))
+ return (int) (a.zout - a.zout_start);
+ else
+ return -1;
+}
+
+STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen)
+{
+ stbi__zbuf a;
+ char *p = (char *) stbi__malloc(16384);
+ if (p == NULL) return NULL;
+ a.zbuffer = (stbi_uc *) buffer;
+ a.zbuffer_end = (stbi_uc *) buffer+len;
+ if (stbi__do_zlib(&a, p, 16384, 1, 0)) {
+ if (outlen) *outlen = (int) (a.zout - a.zout_start);
+ return a.zout_start;
+ } else {
+ STBI_FREE(a.zout_start);
+ return NULL;
+ }
+}
+
+STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen)
+{
+ stbi__zbuf a;
+ a.zbuffer = (stbi_uc *) ibuffer;
+ a.zbuffer_end = (stbi_uc *) ibuffer + ilen;
+ if (stbi__do_zlib(&a, obuffer, olen, 0, 0))
+ return (int) (a.zout - a.zout_start);
+ else
+ return -1;
+}
+#endif
+
+// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18
+// simple implementation
+// - only 8-bit samples
+// - no CRC checking
+// - allocates lots of intermediate memory
+// - avoids problem of streaming data between subsystems
+// - avoids explicit window management
+// performance
+// - uses stb_zlib, a PD zlib implementation with fast huffman decoding
+
+#ifndef STBI_NO_PNG
+typedef struct
+{
+ stbi__uint32 length;
+ stbi__uint32 type;
+} stbi__pngchunk;
+
+static stbi__pngchunk stbi__get_chunk_header(stbi__context *s)
+{
+ stbi__pngchunk c;
+ c.length = stbi__get32be(s);
+ c.type = stbi__get32be(s);
+ return c;
+}
+
+static int stbi__check_png_header(stbi__context *s)
+{
+ static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 };
+ int i;
+ for (i=0; i < 8; ++i)
+ if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG");
+ return 1;
+}
+
+typedef struct
+{
+ stbi__context *s;
+ stbi_uc *idata, *expanded, *out;
+ int depth;
+} stbi__png;
+
+
+enum {
+ STBI__F_none=0,
+ STBI__F_sub=1,
+ STBI__F_up=2,
+ STBI__F_avg=3,
+ STBI__F_paeth=4,
+ // synthetic filter used for first scanline to avoid needing a dummy row of 0s
+ STBI__F_avg_first
+};
+
+static stbi_uc first_row_filter[5] =
+{
+ STBI__F_none,
+ STBI__F_sub,
+ STBI__F_none,
+ STBI__F_avg_first,
+ STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub
+};
+
+static int stbi__paeth(int a, int b, int c)
+{
+ // This formulation looks very different from the reference in the PNG spec, but is
+ // actually equivalent and has favorable data dependencies and admits straightforward
+ // generation of branch-free code, which helps performance significantly.
+ int thresh = c*3 - (a + b);
+ int lo = a < b ? a : b;
+ int hi = a < b ? b : a;
+ int t0 = (hi <= thresh) ? lo : c;
+ int t1 = (thresh <= lo) ? hi : t0;
+ return t1;
+}
+
+static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };
+
+// adds an extra all-255 alpha channel
+// dest == src is legal
+// img_n must be 1 or 3
+static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n)
+{
+ int i;
+ // must process data backwards since we allow dest==src
+ if (img_n == 1) {
+ for (i=x-1; i >= 0; --i) {
+ dest[i*2+1] = 255;
+ dest[i*2+0] = src[i];
+ }
+ } else {
+ STBI_ASSERT(img_n == 3);
+ for (i=x-1; i >= 0; --i) {
+ dest[i*4+3] = 255;
+ dest[i*4+2] = src[i*3+2];
+ dest[i*4+1] = src[i*3+1];
+ dest[i*4+0] = src[i*3+0];
+ }
+ }
+}
+
+// create the png data from post-deflated data
+static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)
+{
+ int bytes = (depth == 16 ? 2 : 1);
+ stbi__context *s = a->s;
+ stbi__uint32 i,j,stride = x*out_n*bytes;
+ stbi__uint32 img_len, img_width_bytes;
+ stbi_uc *filter_buf;
+ int all_ok = 1;
+ int k;
+ int img_n = s->img_n; // copy it into a local for later
+
+ int output_bytes = out_n*bytes;
+ int filter_bytes = img_n*bytes;
+ int width = x;
+
+ STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1);
+ a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into
+ if (!a->out) return stbi__err("outofmem", "Out of memory");
+
+ // note: error exits here don't need to clean up a->out individually,
+ // stbi__do_png always does on error.
+ if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG");
+ img_width_bytes = (((img_n * x * depth) + 7) >> 3);
+ if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) return stbi__err("too large", "Corrupt PNG");
+ img_len = (img_width_bytes + 1) * y;
+
+ // we used to check for exact match between raw_len and img_len on non-interlaced PNGs,
+ // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros),
+ // so just check for raw_len < img_len always.
+ if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG");
+
+ // Allocate two scan lines worth of filter workspace buffer.
+ filter_buf = (stbi_uc *) stbi__malloc_mad2(img_width_bytes, 2, 0);
+ if (!filter_buf) return stbi__err("outofmem", "Out of memory");
+
+ // Filtering for low-bit-depth images
+ if (depth < 8) {
+ filter_bytes = 1;
+ width = img_width_bytes;
+ }
+
+ for (j=0; j < y; ++j) {
+ // cur/prior filter buffers alternate
+ stbi_uc *cur = filter_buf + (j & 1)*img_width_bytes;
+ stbi_uc *prior = filter_buf + (~j & 1)*img_width_bytes;
+ stbi_uc *dest = a->out + stride*j;
+ int nk = width * filter_bytes;
+ int filter = *raw++;
+
+ // check filter type
+ if (filter > 4) {
+ all_ok = stbi__err("invalid filter","Corrupt PNG");
+ break;
+ }
+
+ // if first row, use special filter that doesn't sample previous row
+ if (j == 0) filter = first_row_filter[filter];
+
+ // perform actual filtering
+ switch (filter) {
+ case STBI__F_none:
+ memcpy(cur, raw, nk);
+ break;
+ case STBI__F_sub:
+ memcpy(cur, raw, filter_bytes);
+ for (k = filter_bytes; k < nk; ++k)
+ cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]);
+ break;
+ case STBI__F_up:
+ for (k = 0; k < nk; ++k)
+ cur[k] = STBI__BYTECAST(raw[k] + prior[k]);
+ break;
+ case STBI__F_avg:
+ for (k = 0; k < filter_bytes; ++k)
+ cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1));
+ for (k = filter_bytes; k < nk; ++k)
+ cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1));
+ break;
+ case STBI__F_paeth:
+ for (k = 0; k < filter_bytes; ++k)
+ cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0)
+ for (k = filter_bytes; k < nk; ++k)
+ cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes], prior[k], prior[k-filter_bytes]));
+ break;
+ case STBI__F_avg_first:
+ memcpy(cur, raw, filter_bytes);
+ for (k = filter_bytes; k < nk; ++k)
+ cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1));
+ break;
+ }
+
+ raw += nk;
+
+ // expand decoded bits in cur to dest, also adding an extra alpha channel if desired
+ if (depth < 8) {
+ stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range
+ stbi_uc *in = cur;
+ stbi_uc *out = dest;
+ stbi_uc inb = 0;
+ stbi__uint32 nsmp = x*img_n;
+
+ // expand bits to bytes first
+ if (depth == 4) {
+ for (i=0; i < nsmp; ++i) {
+ if ((i & 1) == 0) inb = *in++;
+ *out++ = scale * (inb >> 4);
+ inb <<= 4;
+ }
+ } else if (depth == 2) {
+ for (i=0; i < nsmp; ++i) {
+ if ((i & 3) == 0) inb = *in++;
+ *out++ = scale * (inb >> 6);
+ inb <<= 2;
+ }
+ } else {
+ STBI_ASSERT(depth == 1);
+ for (i=0; i < nsmp; ++i) {
+ if ((i & 7) == 0) inb = *in++;
+ *out++ = scale * (inb >> 7);
+ inb <<= 1;
+ }
+ }
+
+ // insert alpha=255 values if desired
+ if (img_n != out_n)
+ stbi__create_png_alpha_expand8(dest, dest, x, img_n);
+ } else if (depth == 8) {
+ if (img_n == out_n)
+ memcpy(dest, cur, x*img_n);
+ else
+ stbi__create_png_alpha_expand8(dest, cur, x, img_n);
+ } else if (depth == 16) {
+ // convert the image data from big-endian to platform-native
+ stbi__uint16 *dest16 = (stbi__uint16*)dest;
+ stbi__uint32 nsmp = x*img_n;
+
+ if (img_n == out_n) {
+ for (i = 0; i < nsmp; ++i, ++dest16, cur += 2)
+ *dest16 = (cur[0] << 8) | cur[1];
+ } else {
+ STBI_ASSERT(img_n+1 == out_n);
+ if (img_n == 1) {
+ for (i = 0; i < x; ++i, dest16 += 2, cur += 2) {
+ dest16[0] = (cur[0] << 8) | cur[1];
+ dest16[1] = 0xffff;
+ }
+ } else {
+ STBI_ASSERT(img_n == 3);
+ for (i = 0; i < x; ++i, dest16 += 4, cur += 6) {
+ dest16[0] = (cur[0] << 8) | cur[1];
+ dest16[1] = (cur[2] << 8) | cur[3];
+ dest16[2] = (cur[4] << 8) | cur[5];
+ dest16[3] = 0xffff;
+ }
+ }
+ }
+ }
+ }
+
+ STBI_FREE(filter_buf);
+ if (!all_ok) return 0;
+
+ return 1;
+}
+
+static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced)
+{
+ int bytes = (depth == 16 ? 2 : 1);
+ int out_bytes = out_n * bytes;
+ stbi_uc *final;
+ int p;
+ if (!interlaced)
+ return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color);
+
+ // de-interlacing
+ final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0);
+ if (!final) return stbi__err("outofmem", "Out of memory");
+ for (p=0; p < 7; ++p) {
+ int xorig[] = { 0,4,0,2,0,1,0 };
+ int yorig[] = { 0,0,4,0,2,0,1 };
+ int xspc[] = { 8,8,4,4,2,2,1 };
+ int yspc[] = { 8,8,8,4,4,2,2 };
+ int i,j,x,y;
+ // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1
+ x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p];
+ y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p];
+ if (x && y) {
+ stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y;
+ if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) {
+ STBI_FREE(final);
+ return 0;
+ }
+ for (j=0; j < y; ++j) {
+ for (i=0; i < x; ++i) {
+ int out_y = j*yspc[p]+yorig[p];
+ int out_x = i*xspc[p]+xorig[p];
+ memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes,
+ a->out + (j*x+i)*out_bytes, out_bytes);
+ }
+ }
+ STBI_FREE(a->out);
+ image_data += img_len;
+ image_data_len -= img_len;
+ }
+ }
+ a->out = final;
+
+ return 1;
+}
+
+static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n)
+{
+ stbi__context *s = z->s;
+ stbi__uint32 i, pixel_count = s->img_x * s->img_y;
+ stbi_uc *p = z->out;
+
+ // compute color-based transparency, assuming we've
+ // already got 255 as the alpha value in the output
+ STBI_ASSERT(out_n == 2 || out_n == 4);
+
+ if (out_n == 2) {
+ for (i=0; i < pixel_count; ++i) {
+ p[1] = (p[0] == tc[0] ? 0 : 255);
+ p += 2;
+ }
+ } else {
+ for (i=0; i < pixel_count; ++i) {
+ if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
+ p[3] = 0;
+ p += 4;
+ }
+ }
+ return 1;
+}
+
+static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n)
+{
+ stbi__context *s = z->s;
+ stbi__uint32 i, pixel_count = s->img_x * s->img_y;
+ stbi__uint16 *p = (stbi__uint16*) z->out;
+
+ // compute color-based transparency, assuming we've
+ // already got 65535 as the alpha value in the output
+ STBI_ASSERT(out_n == 2 || out_n == 4);
+
+ if (out_n == 2) {
+ for (i = 0; i < pixel_count; ++i) {
+ p[1] = (p[0] == tc[0] ? 0 : 65535);
+ p += 2;
+ }
+ } else {
+ for (i = 0; i < pixel_count; ++i) {
+ if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
+ p[3] = 0;
+ p += 4;
+ }
+ }
+ return 1;
+}
+
+static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n)
+{
+ stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y;
+ stbi_uc *p, *temp_out, *orig = a->out;
+
+ p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0);
+ if (p == NULL) return stbi__err("outofmem", "Out of memory");
+
+ // between here and free(out) below, exitting would leak
+ temp_out = p;
+
+ if (pal_img_n == 3) {
+ for (i=0; i < pixel_count; ++i) {
+ int n = orig[i]*4;
+ p[0] = palette[n ];
+ p[1] = palette[n+1];
+ p[2] = palette[n+2];
+ p += 3;
+ }
+ } else {
+ for (i=0; i < pixel_count; ++i) {
+ int n = orig[i]*4;
+ p[0] = palette[n ];
+ p[1] = palette[n+1];
+ p[2] = palette[n+2];
+ p[3] = palette[n+3];
+ p += 4;
+ }
+ }
+ STBI_FREE(a->out);
+ a->out = temp_out;
+
+ STBI_NOTUSED(len);
+
+ return 1;
+}
+
+static int stbi__unpremultiply_on_load_global = 0;
+static int stbi__de_iphone_flag_global = 0;
+
+STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply)
+{
+ stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply;
+}
+
+STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)
+{
+ stbi__de_iphone_flag_global = flag_true_if_should_convert;
+}
+
+#ifndef STBI_THREAD_LOCAL
+#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global
+#define stbi__de_iphone_flag stbi__de_iphone_flag_global
+#else
+static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set;
+static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set;
+
+STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply)
+{
+ stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply;
+ stbi__unpremultiply_on_load_set = 1;
+}
+
+STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert)
+{
+ stbi__de_iphone_flag_local = flag_true_if_should_convert;
+ stbi__de_iphone_flag_set = 1;
+}
+
+#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \
+ ? stbi__unpremultiply_on_load_local \
+ : stbi__unpremultiply_on_load_global)
+#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \
+ ? stbi__de_iphone_flag_local \
+ : stbi__de_iphone_flag_global)
+#endif // STBI_THREAD_LOCAL
+
+static void stbi__de_iphone(stbi__png *z)
+{
+ stbi__context *s = z->s;
+ stbi__uint32 i, pixel_count = s->img_x * s->img_y;
+ stbi_uc *p = z->out;
+
+ if (s->img_out_n == 3) { // convert bgr to rgb
+ for (i=0; i < pixel_count; ++i) {
+ stbi_uc t = p[0];
+ p[0] = p[2];
+ p[2] = t;
+ p += 3;
+ }
+ } else {
+ STBI_ASSERT(s->img_out_n == 4);
+ if (stbi__unpremultiply_on_load) {
+ // convert bgr to rgb and unpremultiply
+ for (i=0; i < pixel_count; ++i) {
+ stbi_uc a = p[3];
+ stbi_uc t = p[0];
+ if (a) {
+ stbi_uc half = a / 2;
+ p[0] = (p[2] * 255 + half) / a;
+ p[1] = (p[1] * 255 + half) / a;
+ p[2] = ( t * 255 + half) / a;
+ } else {
+ p[0] = p[2];
+ p[2] = t;
+ }
+ p += 4;
+ }
+ } else {
+ // convert bgr to rgb
+ for (i=0; i < pixel_count; ++i) {
+ stbi_uc t = p[0];
+ p[0] = p[2];
+ p[2] = t;
+ p += 4;
+ }
+ }
+ }
+}
+
+#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d))
+
+static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
+{
+ stbi_uc palette[1024], pal_img_n=0;
+ stbi_uc has_trans=0, tc[3]={0};
+ stbi__uint16 tc16[3];
+ stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0;
+ int first=1,k,interlace=0, color=0, is_iphone=0;
+ stbi__context *s = z->s;
+
+ z->expanded = NULL;
+ z->idata = NULL;
+ z->out = NULL;
+
+ if (!stbi__check_png_header(s)) return 0;
+
+ if (scan == STBI__SCAN_type) return 1;
+
+ for (;;) {
+ stbi__pngchunk c = stbi__get_chunk_header(s);
+ switch (c.type) {
+ case STBI__PNG_TYPE('C','g','B','I'):
+ is_iphone = 1;
+ stbi__skip(s, c.length);
+ break;
+ case STBI__PNG_TYPE('I','H','D','R'): {
+ int comp,filter;
+ if (!first) return stbi__err("multiple IHDR","Corrupt PNG");
+ first = 0;
+ if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG");
+ s->img_x = stbi__get32be(s);
+ s->img_y = stbi__get32be(s);
+ if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)");
+ if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)");
+ z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only");
+ color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG");
+ if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG");
+ if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG");
+ comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG");
+ filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG");
+ interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG");
+ if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG");
+ if (!pal_img_n) {
+ s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
+ if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode");
+ } else {
+ // if paletted, then pal_n is our final components, and
+ // img_n is # components to decompress/filter.
+ s->img_n = 1;
+ if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG");
+ }
+ // even with SCAN_header, have to scan to see if we have a tRNS
+ break;
+ }
+
+ case STBI__PNG_TYPE('P','L','T','E'): {
+ if (first) return stbi__err("first not IHDR", "Corrupt PNG");
+ if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG");
+ pal_len = c.length / 3;
+ if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG");
+ for (i=0; i < pal_len; ++i) {
+ palette[i*4+0] = stbi__get8(s);
+ palette[i*4+1] = stbi__get8(s);
+ palette[i*4+2] = stbi__get8(s);
+ palette[i*4+3] = 255;
+ }
+ break;
+ }
+
+ case STBI__PNG_TYPE('t','R','N','S'): {
+ if (first) return stbi__err("first not IHDR", "Corrupt PNG");
+ if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG");
+ if (pal_img_n) {
+ if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; }
+ if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG");
+ if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG");
+ pal_img_n = 4;
+ for (i=0; i < c.length; ++i)
+ palette[i*4+3] = stbi__get8(s);
+ } else {
+ if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG");
+ if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG");
+ has_trans = 1;
+ // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now.
+ if (scan == STBI__SCAN_header) { ++s->img_n; return 1; }
+ if (z->depth == 16) {
+ for (k = 0; k < s->img_n && k < 3; ++k) // extra loop test to suppress false GCC warning
+ tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
+ } else {
+ for (k = 0; k < s->img_n && k < 3; ++k)
+ tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
+ }
+ }
+ break;
+ }
+
+ case STBI__PNG_TYPE('I','D','A','T'): {
+ if (first) return stbi__err("first not IHDR", "Corrupt PNG");
+ if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG");
+ if (scan == STBI__SCAN_header) {
+ // header scan definitely stops at first IDAT
+ if (pal_img_n)
+ s->img_n = pal_img_n;
+ return 1;
+ }
+ if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes");
+ if ((int)(ioff + c.length) < (int)ioff) return 0;
+ if (ioff + c.length > idata_limit) {
+ stbi__uint32 idata_limit_old = idata_limit;
+ stbi_uc *p;
+ if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096;
+ while (ioff + c.length > idata_limit)
+ idata_limit *= 2;
+ STBI_NOTUSED(idata_limit_old);
+ p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory");
+ z->idata = p;
+ }
+ if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG");
+ ioff += c.length;
+ break;
+ }
+
+ case STBI__PNG_TYPE('I','E','N','D'): {
+ stbi__uint32 raw_len, bpl;
+ if (first) return stbi__err("first not IHDR", "Corrupt PNG");
+ if (scan != STBI__SCAN_load) return 1;
+ if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG");
+ // initial guess for decoded data size to avoid unnecessary reallocs
+ bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component
+ raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */;
+ z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone);
+ if (z->expanded == NULL) return 0; // zlib should set error
+ STBI_FREE(z->idata); z->idata = NULL;
+ if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans)
+ s->img_out_n = s->img_n+1;
+ else
+ s->img_out_n = s->img_n;
+ if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0;
+ if (has_trans) {
+ if (z->depth == 16) {
+ if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0;
+ } else {
+ if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0;
+ }
+ }
+ if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2)
+ stbi__de_iphone(z);
+ if (pal_img_n) {
+ // pal_img_n == 3 or 4
+ s->img_n = pal_img_n; // record the actual colors we had
+ s->img_out_n = pal_img_n;
+ if (req_comp >= 3) s->img_out_n = req_comp;
+ if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n))
+ return 0;
+ } else if (has_trans) {
+ // non-paletted image with tRNS -> source image has (constant) alpha
+ ++s->img_n;
+ }
+ STBI_FREE(z->expanded); z->expanded = NULL;
+ // end of PNG chunk, read and skip CRC
+ stbi__get32be(s);
+ return 1;
+ }
+
+ default:
+ // if critical, fail
+ if (first) return stbi__err("first not IHDR", "Corrupt PNG");
+ if ((c.type & (1 << 29)) == 0) {
+ #ifndef STBI_NO_FAILURE_STRINGS
+ // not threadsafe
+ static char invalid_chunk[] = "XXXX PNG chunk not known";
+ invalid_chunk[0] = STBI__BYTECAST(c.type >> 24);
+ invalid_chunk[1] = STBI__BYTECAST(c.type >> 16);
+ invalid_chunk[2] = STBI__BYTECAST(c.type >> 8);
+ invalid_chunk[3] = STBI__BYTECAST(c.type >> 0);
+ #endif
+ return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type");
+ }
+ stbi__skip(s, c.length);
+ break;
+ }
+ // end of PNG chunk, read and skip CRC
+ stbi__get32be(s);
+ }
+}
+
+static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri)
+{
+ void *result=NULL;
+ if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error");
+ if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) {
+ if (p->depth <= 8)
+ ri->bits_per_channel = 8;
+ else if (p->depth == 16)
+ ri->bits_per_channel = 16;
+ else
+ return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth");
+ result = p->out;
+ p->out = NULL;
+ if (req_comp && req_comp != p->s->img_out_n) {
+ if (ri->bits_per_channel == 8)
+ result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);
+ else
+ result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);
+ p->s->img_out_n = req_comp;
+ if (result == NULL) return result;
+ }
+ *x = p->s->img_x;
+ *y = p->s->img_y;
+ if (n) *n = p->s->img_n;
+ }
+ STBI_FREE(p->out); p->out = NULL;
+ STBI_FREE(p->expanded); p->expanded = NULL;
+ STBI_FREE(p->idata); p->idata = NULL;
+
+ return result;
+}
+
+static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
+{
+ stbi__png p;
+ p.s = s;
+ return stbi__do_png(&p, x,y,comp,req_comp, ri);
+}
+
+static int stbi__png_test(stbi__context *s)
+{
+ int r;
+ r = stbi__check_png_header(s);
+ stbi__rewind(s);
+ return r;
+}
+
+static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp)
+{
+ if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) {
+ stbi__rewind( p->s );
+ return 0;
+ }
+ if (x) *x = p->s->img_x;
+ if (y) *y = p->s->img_y;
+ if (comp) *comp = p->s->img_n;
+ return 1;
+}
+
+static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp)
+{
+ stbi__png p;
+ p.s = s;
+ return stbi__png_info_raw(&p, x, y, comp);
+}
+
+static int stbi__png_is16(stbi__context *s)
+{
+ stbi__png p;
+ p.s = s;
+ if (!stbi__png_info_raw(&p, NULL, NULL, NULL))
+ return 0;
+ if (p.depth != 16) {
+ stbi__rewind(p.s);
+ return 0;
+ }
+ return 1;
+}
+#endif
+
+// Microsoft/Windows BMP image
+
+#ifndef STBI_NO_BMP
+static int stbi__bmp_test_raw(stbi__context *s)
+{
+ int r;
+ int sz;
+ if (stbi__get8(s) != 'B') return 0;
+ if (stbi__get8(s) != 'M') return 0;
+ stbi__get32le(s); // discard filesize
+ stbi__get16le(s); // discard reserved
+ stbi__get16le(s); // discard reserved
+ stbi__get32le(s); // discard data offset
+ sz = stbi__get32le(s);
+ r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124);
+ return r;
+}
+
+static int stbi__bmp_test(stbi__context *s)
+{
+ int r = stbi__bmp_test_raw(s);
+ stbi__rewind(s);
+ return r;
+}
+
+
+// returns 0..31 for the highest set bit
+static int stbi__high_bit(unsigned int z)
+{
+ int n=0;
+ if (z == 0) return -1;
+ if (z >= 0x10000) { n += 16; z >>= 16; }
+ if (z >= 0x00100) { n += 8; z >>= 8; }
+ if (z >= 0x00010) { n += 4; z >>= 4; }
+ if (z >= 0x00004) { n += 2; z >>= 2; }
+ if (z >= 0x00002) { n += 1;/* >>= 1;*/ }
+ return n;
+}
+
+static int stbi__bitcount(unsigned int a)
+{
+ a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2
+ a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4
+ a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits
+ a = (a + (a >> 8)); // max 16 per 8 bits
+ a = (a + (a >> 16)); // max 32 per 8 bits
+ return a & 0xff;
+}
+
+// extract an arbitrarily-aligned N-bit value (N=bits)
+// from v, and then make it 8-bits long and fractionally
+// extend it to full full range.
+static int stbi__shiftsigned(unsigned int v, int shift, int bits)
+{
+ static unsigned int mul_table[9] = {
+ 0,
+ 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/,
+ 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/,
+ };
+ static unsigned int shift_table[9] = {
+ 0, 0,0,1,0,2,4,6,0,
+ };
+ if (shift < 0)
+ v <<= -shift;
+ else
+ v >>= shift;
+ STBI_ASSERT(v < 256);
+ v >>= (8-bits);
+ STBI_ASSERT(bits >= 0 && bits <= 8);
+ return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits];
+}
+
+typedef struct
+{
+ int bpp, offset, hsz;
+ unsigned int mr,mg,mb,ma, all_a;
+ int extra_read;
+} stbi__bmp_data;
+
+static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress)
+{
+ // BI_BITFIELDS specifies masks explicitly, don't override
+ if (compress == 3)
+ return 1;
+
+ if (compress == 0) {
+ if (info->bpp == 16) {
+ info->mr = 31u << 10;
+ info->mg = 31u << 5;
+ info->mb = 31u << 0;
+ } else if (info->bpp == 32) {
+ info->mr = 0xffu << 16;
+ info->mg = 0xffu << 8;
+ info->mb = 0xffu << 0;
+ info->ma = 0xffu << 24;
+ info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0
+ } else {
+ // otherwise, use defaults, which is all-0
+ info->mr = info->mg = info->mb = info->ma = 0;
+ }
+ return 1;
+ }
+ return 0; // error
+}
+
+static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
+{
+ int hsz;
+ if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP");
+ stbi__get32le(s); // discard filesize
+ stbi__get16le(s); // discard reserved
+ stbi__get16le(s); // discard reserved
+ info->offset = stbi__get32le(s);
+ info->hsz = hsz = stbi__get32le(s);
+ info->mr = info->mg = info->mb = info->ma = 0;
+ info->extra_read = 14;
+
+ if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP");
+
+ if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown");
+ if (hsz == 12) {
+ s->img_x = stbi__get16le(s);
+ s->img_y = stbi__get16le(s);
+ } else {
+ s->img_x = stbi__get32le(s);
+ s->img_y = stbi__get32le(s);
+ }
+ if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP");
+ info->bpp = stbi__get16le(s);
+ if (hsz != 12) {
+ int compress = stbi__get32le(s);
+ if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE");
+ if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes
+ if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel
+ stbi__get32le(s); // discard sizeof
+ stbi__get32le(s); // discard hres
+ stbi__get32le(s); // discard vres
+ stbi__get32le(s); // discard colorsused
+ stbi__get32le(s); // discard max important
+ if (hsz == 40 || hsz == 56) {
+ if (hsz == 56) {
+ stbi__get32le(s);
+ stbi__get32le(s);
+ stbi__get32le(s);
+ stbi__get32le(s);
+ }
+ if (info->bpp == 16 || info->bpp == 32) {
+ if (compress == 0) {
+ stbi__bmp_set_mask_defaults(info, compress);
+ } else if (compress == 3) {
+ info->mr = stbi__get32le(s);
+ info->mg = stbi__get32le(s);
+ info->mb = stbi__get32le(s);
+ info->extra_read += 12;
+ // not documented, but generated by photoshop and handled by mspaint
+ if (info->mr == info->mg && info->mg == info->mb) {
+ // ?!?!?
+ return stbi__errpuc("bad BMP", "bad BMP");
+ }
+ } else
+ return stbi__errpuc("bad BMP", "bad BMP");
+ }
+ } else {
+ // V4/V5 header
+ int i;
+ if (hsz != 108 && hsz != 124)
+ return stbi__errpuc("bad BMP", "bad BMP");
+ info->mr = stbi__get32le(s);
+ info->mg = stbi__get32le(s);
+ info->mb = stbi__get32le(s);
+ info->ma = stbi__get32le(s);
+ if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs
+ stbi__bmp_set_mask_defaults(info, compress);
+ stbi__get32le(s); // discard color space
+ for (i=0; i < 12; ++i)
+ stbi__get32le(s); // discard color space parameters
+ if (hsz == 124) {
+ stbi__get32le(s); // discard rendering intent
+ stbi__get32le(s); // discard offset of profile data
+ stbi__get32le(s); // discard size of profile data
+ stbi__get32le(s); // discard reserved
+ }
+ }
+ }
+ return (void *) 1;
+}
+
+
+static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
+{
+ stbi_uc *out;
+ unsigned int mr=0,mg=0,mb=0,ma=0, all_a;
+ stbi_uc pal[256][4];
+ int psize=0,i,j,width;
+ int flip_vertically, pad, target;
+ stbi__bmp_data info;
+ STBI_NOTUSED(ri);
+
+ info.all_a = 255;
+ if (stbi__bmp_parse_header(s, &info) == NULL)
+ return NULL; // error code already set
+
+ flip_vertically = ((int) s->img_y) > 0;
+ s->img_y = abs((int) s->img_y);
+
+ if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)");
+ if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)");
+
+ mr = info.mr;
+ mg = info.mg;
+ mb = info.mb;
+ ma = info.ma;
+ all_a = info.all_a;
+
+ if (info.hsz == 12) {
+ if (info.bpp < 24)
+ psize = (info.offset - info.extra_read - 24) / 3;
+ } else {
+ if (info.bpp < 16)
+ psize = (info.offset - info.extra_read - info.hsz) >> 2;
+ }
+ if (psize == 0) {
+ // accept some number of extra bytes after the header, but if the offset points either to before
+ // the header ends or implies a large amount of extra data, reject the file as malformed
+ int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original);
+ int header_limit = 1024; // max we actually read is below 256 bytes currently.
+ int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size.
+ if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) {
+ return stbi__errpuc("bad header", "Corrupt BMP");
+ }
+ // we established that bytes_read_so_far is positive and sensible.
+ // the first half of this test rejects offsets that are either too small positives, or
+ // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn
+ // ensures the number computed in the second half of the test can't overflow.
+ if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) {
+ return stbi__errpuc("bad offset", "Corrupt BMP");
+ } else {
+ stbi__skip(s, info.offset - bytes_read_so_far);
+ }
+ }
+
+ if (info.bpp == 24 && ma == 0xff000000)
+ s->img_n = 3;
+ else
+ s->img_n = ma ? 4 : 3;
+ if (req_comp && req_comp >= 3) // we can directly decode 3 or 4
+ target = req_comp;
+ else
+ target = s->img_n; // if they want monochrome, we'll post-convert
+
+ // sanity-check size
+ if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0))
+ return stbi__errpuc("too large", "Corrupt BMP");
+
+ out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0);
+ if (!out) return stbi__errpuc("outofmem", "Out of memory");
+ if (info.bpp < 16) {
+ int z=0;
+ if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); }
+ for (i=0; i < psize; ++i) {
+ pal[i][2] = stbi__get8(s);
+ pal[i][1] = stbi__get8(s);
+ pal[i][0] = stbi__get8(s);
+ if (info.hsz != 12) stbi__get8(s);
+ pal[i][3] = 255;
+ }
+ stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4));
+ if (info.bpp == 1) width = (s->img_x + 7) >> 3;
+ else if (info.bpp == 4) width = (s->img_x + 1) >> 1;
+ else if (info.bpp == 8) width = s->img_x;
+ else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); }
+ pad = (-width)&3;
+ if (info.bpp == 1) {
+ for (j=0; j < (int) s->img_y; ++j) {
+ int bit_offset = 7, v = stbi__get8(s);
+ for (i=0; i < (int) s->img_x; ++i) {
+ int color = (v>>bit_offset)&0x1;
+ out[z++] = pal[color][0];
+ out[z++] = pal[color][1];
+ out[z++] = pal[color][2];
+ if (target == 4) out[z++] = 255;
+ if (i+1 == (int) s->img_x) break;
+ if((--bit_offset) < 0) {
+ bit_offset = 7;
+ v = stbi__get8(s);
+ }
+ }
+ stbi__skip(s, pad);
+ }
+ } else {
+ for (j=0; j < (int) s->img_y; ++j) {
+ for (i=0; i < (int) s->img_x; i += 2) {
+ int v=stbi__get8(s),v2=0;
+ if (info.bpp == 4) {
+ v2 = v & 15;
+ v >>= 4;
+ }
+ out[z++] = pal[v][0];
+ out[z++] = pal[v][1];
+ out[z++] = pal[v][2];
+ if (target == 4) out[z++] = 255;
+ if (i+1 == (int) s->img_x) break;
+ v = (info.bpp == 8) ? stbi__get8(s) : v2;
+ out[z++] = pal[v][0];
+ out[z++] = pal[v][1];
+ out[z++] = pal[v][2];
+ if (target == 4) out[z++] = 255;
+ }
+ stbi__skip(s, pad);
+ }
+ }
+ } else {
+ int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0;
+ int z = 0;
+ int easy=0;
+ stbi__skip(s, info.offset - info.extra_read - info.hsz);
+ if (info.bpp == 24) width = 3 * s->img_x;
+ else if (info.bpp == 16) width = 2*s->img_x;
+ else /* bpp = 32 and pad = 0 */ width=0;
+ pad = (-width) & 3;
+ if (info.bpp == 24) {
+ easy = 1;
+ } else if (info.bpp == 32) {
+ if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000)
+ easy = 2;
+ }
+ if (!easy) {
+ if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); }
+ // right shift amt to put high bit in position #7
+ rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr);
+ gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg);
+ bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb);
+ ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma);
+ if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); }
+ }
+ for (j=0; j < (int) s->img_y; ++j) {
+ if (easy) {
+ for (i=0; i < (int) s->img_x; ++i) {
+ unsigned char a;
+ out[z+2] = stbi__get8(s);
+ out[z+1] = stbi__get8(s);
+ out[z+0] = stbi__get8(s);
+ z += 3;
+ a = (easy == 2 ? stbi__get8(s) : 255);
+ all_a |= a;
+ if (target == 4) out[z++] = a;
+ }
+ } else {
+ int bpp = info.bpp;
+ for (i=0; i < (int) s->img_x; ++i) {
+ stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s));
+ unsigned int a;
+ out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount));
+ out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount));
+ out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount));
+ a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255);
+ all_a |= a;
+ if (target == 4) out[z++] = STBI__BYTECAST(a);
+ }
+ }
+ stbi__skip(s, pad);
+ }
+ }
+
+ // if alpha channel is all 0s, replace with all 255s
+ if (target == 4 && all_a == 0)
+ for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4)
+ out[i] = 255;
+
+ if (flip_vertically) {
+ stbi_uc t;
+ for (j=0; j < (int) s->img_y>>1; ++j) {
+ stbi_uc *p1 = out + j *s->img_x*target;
+ stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target;
+ for (i=0; i < (int) s->img_x*target; ++i) {
+ t = p1[i]; p1[i] = p2[i]; p2[i] = t;
+ }
+ }
+ }
+
+ if (req_comp && req_comp != target) {
+ out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y);
+ if (out == NULL) return out; // stbi__convert_format frees input on failure
+ }
+
+ *x = s->img_x;
+ *y = s->img_y;
+ if (comp) *comp = s->img_n;
+ return out;
+}
+#endif
+
+// Targa Truevision - TGA
+// by Jonathan Dummer
+#ifndef STBI_NO_TGA
+// returns STBI_rgb or whatever, 0 on error
+static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16)
+{
+ // only RGB or RGBA (incl. 16bit) or grey allowed
+ if (is_rgb16) *is_rgb16 = 0;
+ switch(bits_per_pixel) {
+ case 8: return STBI_grey;
+ case 16: if(is_grey) return STBI_grey_alpha;
+ // fallthrough
+ case 15: if(is_rgb16) *is_rgb16 = 1;
+ return STBI_rgb;
+ case 24: // fallthrough
+ case 32: return bits_per_pixel/8;
+ default: return 0;
+ }
+}
+
+static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp)
+{
+ int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp;
+ int sz, tga_colormap_type;
+ stbi__get8(s); // discard Offset
+ tga_colormap_type = stbi__get8(s); // colormap type
+ if( tga_colormap_type > 1 ) {
+ stbi__rewind(s);
+ return 0; // only RGB or indexed allowed
+ }
+ tga_image_type = stbi__get8(s); // image type
+ if ( tga_colormap_type == 1 ) { // colormapped (paletted) image
+ if (tga_image_type != 1 && tga_image_type != 9) {
+ stbi__rewind(s);
+ return 0;
+ }
+ stbi__skip(s,4); // skip index of first colormap entry and number of entries
+ sz = stbi__get8(s); // check bits per palette color entry
+ if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) {
+ stbi__rewind(s);
+ return 0;
+ }
+ stbi__skip(s,4); // skip image x and y origin
+ tga_colormap_bpp = sz;
+ } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE
+ if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) {
+ stbi__rewind(s);
+ return 0; // only RGB or grey allowed, +/- RLE
+ }
+ stbi__skip(s,9); // skip colormap specification and image x/y origin
+ tga_colormap_bpp = 0;
+ }
+ tga_w = stbi__get16le(s);
+ if( tga_w < 1 ) {
+ stbi__rewind(s);
+ return 0; // test width
+ }
+ tga_h = stbi__get16le(s);
+ if( tga_h < 1 ) {
+ stbi__rewind(s);
+ return 0; // test height
+ }
+ tga_bits_per_pixel = stbi__get8(s); // bits per pixel
+ stbi__get8(s); // ignore alpha bits
+ if (tga_colormap_bpp != 0) {
+ if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) {
+ // when using a colormap, tga_bits_per_pixel is the size of the indexes
+ // I don't think anything but 8 or 16bit indexes makes sense
+ stbi__rewind(s);
+ return 0;
+ }
+ tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL);
+ } else {
+ tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL);
+ }
+ if(!tga_comp) {
+ stbi__rewind(s);
+ return 0;
+ }
+ if (x) *x = tga_w;
+ if (y) *y = tga_h;
+ if (comp) *comp = tga_comp;
+ return 1; // seems to have passed everything
+}
+
+static int stbi__tga_test(stbi__context *s)
+{
+ int res = 0;
+ int sz, tga_color_type;
+ stbi__get8(s); // discard Offset
+ tga_color_type = stbi__get8(s); // color type
+ if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed
+ sz = stbi__get8(s); // image type
+ if ( tga_color_type == 1 ) { // colormapped (paletted) image
+ if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9
+ stbi__skip(s,4); // skip index of first colormap entry and number of entries
+ sz = stbi__get8(s); // check bits per palette color entry
+ if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd;
+ stbi__skip(s,4); // skip image x and y origin
+ } else { // "normal" image w/o colormap
+ if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE
+ stbi__skip(s,9); // skip colormap specification and image x/y origin
+ }
+ if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width
+ if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height
+ sz = stbi__get8(s); // bits per pixel
+ if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index
+ if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd;
+
+ res = 1; // if we got this far, everything's good and we can return 1 instead of 0
+
+errorEnd:
+ stbi__rewind(s);
+ return res;
+}
+
+// read 16bit value and convert to 24bit RGB
+static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out)
+{
+ stbi__uint16 px = (stbi__uint16)stbi__get16le(s);
+ stbi__uint16 fiveBitMask = 31;
+ // we have 3 channels with 5bits each
+ int r = (px >> 10) & fiveBitMask;
+ int g = (px >> 5) & fiveBitMask;
+ int b = px & fiveBitMask;
+ // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later
+ out[0] = (stbi_uc)((r * 255)/31);
+ out[1] = (stbi_uc)((g * 255)/31);
+ out[2] = (stbi_uc)((b * 255)/31);
+
+ // some people claim that the most significant bit might be used for alpha
+ // (possibly if an alpha-bit is set in the "image descriptor byte")
+ // but that only made 16bit test images completely translucent..
+ // so let's treat all 15 and 16bit TGAs as RGB with no alpha.
+}
+
+static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
+{
+ // read in the TGA header stuff
+ int tga_offset = stbi__get8(s);
+ int tga_indexed = stbi__get8(s);
+ int tga_image_type = stbi__get8(s);
+ int tga_is_RLE = 0;
+ int tga_palette_start = stbi__get16le(s);
+ int tga_palette_len = stbi__get16le(s);
+ int tga_palette_bits = stbi__get8(s);
+ int tga_x_origin = stbi__get16le(s);
+ int tga_y_origin = stbi__get16le(s);
+ int tga_width = stbi__get16le(s);
+ int tga_height = stbi__get16le(s);
+ int tga_bits_per_pixel = stbi__get8(s);
+ int tga_comp, tga_rgb16=0;
+ int tga_inverted = stbi__get8(s);
+ // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?)
+ // image data
+ unsigned char *tga_data;
+ unsigned char *tga_palette = NULL;
+ int i, j;
+ unsigned char raw_data[4] = {0};
+ int RLE_count = 0;
+ int RLE_repeating = 0;
+ int read_next_pixel = 1;
+ STBI_NOTUSED(ri);
+ STBI_NOTUSED(tga_x_origin); // @TODO
+ STBI_NOTUSED(tga_y_origin); // @TODO
+
+ if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)");
+ if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)");
+
+ // do a tiny bit of precessing
+ if ( tga_image_type >= 8 )
+ {
+ tga_image_type -= 8;
+ tga_is_RLE = 1;
+ }
+ tga_inverted = 1 - ((tga_inverted >> 5) & 1);
+
+ // If I'm paletted, then I'll use the number of bits from the palette
+ if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16);
+ else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16);
+
+ if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency
+ return stbi__errpuc("bad format", "Can't find out TGA pixelformat");
+
+ // tga info
+ *x = tga_width;
+ *y = tga_height;
+ if (comp) *comp = tga_comp;
+
+ if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0))
+ return stbi__errpuc("too large", "Corrupt TGA");
+
+ tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0);
+ if (!tga_data) return stbi__errpuc("outofmem", "Out of memory");
+
+ // skip to the data's starting position (offset usually = 0)
+ stbi__skip(s, tga_offset );
+
+ if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) {
+ for (i=0; i < tga_height; ++i) {
+ int row = tga_inverted ? tga_height -i - 1 : i;
+ stbi_uc *tga_row = tga_data + row*tga_width*tga_comp;
+ stbi__getn(s, tga_row, tga_width * tga_comp);
+ }
+ } else {
+ // do I need to load a palette?
+ if ( tga_indexed)
+ {
+ if (tga_palette_len == 0) { /* you have to have at least one entry! */
+ STBI_FREE(tga_data);
+ return stbi__errpuc("bad palette", "Corrupt TGA");
+ }
+
+ // any data to skip? (offset usually = 0)
+ stbi__skip(s, tga_palette_start );
+ // load the palette
+ tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0);
+ if (!tga_palette) {
+ STBI_FREE(tga_data);
+ return stbi__errpuc("outofmem", "Out of memory");
+ }
+ if (tga_rgb16) {
+ stbi_uc *pal_entry = tga_palette;
+ STBI_ASSERT(tga_comp == STBI_rgb);
+ for (i=0; i < tga_palette_len; ++i) {
+ stbi__tga_read_rgb16(s, pal_entry);
+ pal_entry += tga_comp;
+ }
+ } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) {
+ STBI_FREE(tga_data);
+ STBI_FREE(tga_palette);
+ return stbi__errpuc("bad palette", "Corrupt TGA");
+ }
+ }
+ // load the data
+ for (i=0; i < tga_width * tga_height; ++i)
+ {
+ // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk?
+ if ( tga_is_RLE )
+ {
+ if ( RLE_count == 0 )
+ {
+ // yep, get the next byte as a RLE command
+ int RLE_cmd = stbi__get8(s);
+ RLE_count = 1 + (RLE_cmd & 127);
+ RLE_repeating = RLE_cmd >> 7;
+ read_next_pixel = 1;
+ } else if ( !RLE_repeating )
+ {
+ read_next_pixel = 1;
+ }
+ } else
+ {
+ read_next_pixel = 1;
+ }
+ // OK, if I need to read a pixel, do it now
+ if ( read_next_pixel )
+ {
+ // load however much data we did have
+ if ( tga_indexed )
+ {
+ // read in index, then perform the lookup
+ int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s);
+ if ( pal_idx >= tga_palette_len ) {
+ // invalid index
+ pal_idx = 0;
+ }
+ pal_idx *= tga_comp;
+ for (j = 0; j < tga_comp; ++j) {
+ raw_data[j] = tga_palette[pal_idx+j];
+ }
+ } else if(tga_rgb16) {
+ STBI_ASSERT(tga_comp == STBI_rgb);
+ stbi__tga_read_rgb16(s, raw_data);
+ } else {
+ // read in the data raw
+ for (j = 0; j < tga_comp; ++j) {
+ raw_data[j] = stbi__get8(s);
+ }
+ }
+ // clear the reading flag for the next pixel
+ read_next_pixel = 0;
+ } // end of reading a pixel
+
+ // copy data
+ for (j = 0; j < tga_comp; ++j)
+ tga_data[i*tga_comp+j] = raw_data[j];
+
+ // in case we're in RLE mode, keep counting down
+ --RLE_count;
+ }
+ // do I need to invert the image?
+ if ( tga_inverted )
+ {
+ for (j = 0; j*2 < tga_height; ++j)
+ {
+ int index1 = j * tga_width * tga_comp;
+ int index2 = (tga_height - 1 - j) * tga_width * tga_comp;
+ for (i = tga_width * tga_comp; i > 0; --i)
+ {
+ unsigned char temp = tga_data[index1];
+ tga_data[index1] = tga_data[index2];
+ tga_data[index2] = temp;
+ ++index1;
+ ++index2;
+ }
+ }
+ }
+ // clear my palette, if I had one
+ if ( tga_palette != NULL )
+ {
+ STBI_FREE( tga_palette );
+ }
+ }
+
+ // swap RGB - if the source data was RGB16, it already is in the right order
+ if (tga_comp >= 3 && !tga_rgb16)
+ {
+ unsigned char* tga_pixel = tga_data;
+ for (i=0; i < tga_width * tga_height; ++i)
+ {
+ unsigned char temp = tga_pixel[0];
+ tga_pixel[0] = tga_pixel[2];
+ tga_pixel[2] = temp;
+ tga_pixel += tga_comp;
+ }
+ }
+
+ // convert to target component count
+ if (req_comp && req_comp != tga_comp)
+ tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height);
+
+ // the things I do to get rid of an error message, and yet keep
+ // Microsoft's C compilers happy... [8^(
+ tga_palette_start = tga_palette_len = tga_palette_bits =
+ tga_x_origin = tga_y_origin = 0;
+ STBI_NOTUSED(tga_palette_start);
+ // OK, done
+ return tga_data;
+}
+#endif
+
+// *************************************************************************************************
+// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB
+
+#ifndef STBI_NO_PSD
+static int stbi__psd_test(stbi__context *s)
+{
+ int r = (stbi__get32be(s) == 0x38425053);
+ stbi__rewind(s);
+ return r;
+}
+
+static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount)
+{
+ int count, nleft, len;
+
+ count = 0;
+ while ((nleft = pixelCount - count) > 0) {
+ len = stbi__get8(s);
+ if (len == 128) {
+ // No-op.
+ } else if (len < 128) {
+ // Copy next len+1 bytes literally.
+ len++;
+ if (len > nleft) return 0; // corrupt data
+ count += len;
+ while (len) {
+ *p = stbi__get8(s);
+ p += 4;
+ len--;
+ }
+ } else if (len > 128) {
+ stbi_uc val;
+ // Next -len+1 bytes in the dest are replicated from next source byte.
+ // (Interpret len as a negative 8-bit int.)
+ len = 257 - len;
+ if (len > nleft) return 0; // corrupt data
+ val = stbi__get8(s);
+ count += len;
+ while (len) {
+ *p = val;
+ p += 4;
+ len--;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc)
+{
+ int pixelCount;
+ int channelCount, compression;
+ int channel, i;
+ int bitdepth;
+ int w,h;
+ stbi_uc *out;
+ STBI_NOTUSED(ri);
+
+ // Check identifier
+ if (stbi__get32be(s) != 0x38425053) // "8BPS"
+ return stbi__errpuc("not PSD", "Corrupt PSD image");
+
+ // Check file type version.
+ if (stbi__get16be(s) != 1)
+ return stbi__errpuc("wrong version", "Unsupported version of PSD image");
+
+ // Skip 6 reserved bytes.
+ stbi__skip(s, 6 );
+
+ // Read the number of channels (R, G, B, A, etc).
+ channelCount = stbi__get16be(s);
+ if (channelCount < 0 || channelCount > 16)
+ return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image");
+
+ // Read the rows and columns of the image.
+ h = stbi__get32be(s);
+ w = stbi__get32be(s);
+
+ if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)");
+ if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)");
+
+ // Make sure the depth is 8 bits.
+ bitdepth = stbi__get16be(s);
+ if (bitdepth != 8 && bitdepth != 16)
+ return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit");
+
+ // Make sure the color mode is RGB.
+ // Valid options are:
+ // 0: Bitmap
+ // 1: Grayscale
+ // 2: Indexed color
+ // 3: RGB color
+ // 4: CMYK color
+ // 7: Multichannel
+ // 8: Duotone
+ // 9: Lab color
+ if (stbi__get16be(s) != 3)
+ return stbi__errpuc("wrong color format", "PSD is not in RGB color format");
+
+ // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.)
+ stbi__skip(s,stbi__get32be(s) );
+
+ // Skip the image resources. (resolution, pen tool paths, etc)
+ stbi__skip(s, stbi__get32be(s) );
+
+ // Skip the reserved data.
+ stbi__skip(s, stbi__get32be(s) );
+
+ // Find out if the data is compressed.
+ // Known values:
+ // 0: no compression
+ // 1: RLE compressed
+ compression = stbi__get16be(s);
+ if (compression > 1)
+ return stbi__errpuc("bad compression", "PSD has an unknown compression format");
+
+ // Check size
+ if (!stbi__mad3sizes_valid(4, w, h, 0))
+ return stbi__errpuc("too large", "Corrupt PSD");
+
+ // Create the destination image.
+
+ if (!compression && bitdepth == 16 && bpc == 16) {
+ out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0);
+ ri->bits_per_channel = 16;
+ } else
+ out = (stbi_uc *) stbi__malloc(4 * w*h);
+
+ if (!out) return stbi__errpuc("outofmem", "Out of memory");
+ pixelCount = w*h;
+
+ // Initialize the data to zero.
+ //memset( out, 0, pixelCount * 4 );
+
+ // Finally, the image data.
+ if (compression) {
+ // RLE as used by .PSD and .TIFF
+ // Loop until you get the number of unpacked bytes you are expecting:
+ // Read the next source byte into n.
+ // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally.
+ // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times.
+ // Else if n is 128, noop.
+ // Endloop
+
+ // The RLE-compressed data is preceded by a 2-byte data count for each row in the data,
+ // which we're going to just skip.
+ stbi__skip(s, h * channelCount * 2 );
+
+ // Read the RLE data by channel.
+ for (channel = 0; channel < 4; channel++) {
+ stbi_uc *p;
+
+ p = out+channel;
+ if (channel >= channelCount) {
+ // Fill this channel with default data.
+ for (i = 0; i < pixelCount; i++, p += 4)
+ *p = (channel == 3 ? 255 : 0);
+ } else {
+ // Read the RLE data.
+ if (!stbi__psd_decode_rle(s, p, pixelCount)) {
+ STBI_FREE(out);
+ return stbi__errpuc("corrupt", "bad RLE data");
+ }
+ }
+ }
+
+ } else {
+ // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...)
+ // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image.
+
+ // Read the data by channel.
+ for (channel = 0; channel < 4; channel++) {
+ if (channel >= channelCount) {
+ // Fill this channel with default data.
+ if (bitdepth == 16 && bpc == 16) {
+ stbi__uint16 *q = ((stbi__uint16 *) out) + channel;
+ stbi__uint16 val = channel == 3 ? 65535 : 0;
+ for (i = 0; i < pixelCount; i++, q += 4)
+ *q = val;
+ } else {
+ stbi_uc *p = out+channel;
+ stbi_uc val = channel == 3 ? 255 : 0;
+ for (i = 0; i < pixelCount; i++, p += 4)
+ *p = val;
+ }
+ } else {
+ if (ri->bits_per_channel == 16) { // output bpc
+ stbi__uint16 *q = ((stbi__uint16 *) out) + channel;
+ for (i = 0; i < pixelCount; i++, q += 4)
+ *q = (stbi__uint16) stbi__get16be(s);
+ } else {
+ stbi_uc *p = out+channel;
+ if (bitdepth == 16) { // input bpc
+ for (i = 0; i < pixelCount; i++, p += 4)
+ *p = (stbi_uc) (stbi__get16be(s) >> 8);
+ } else {
+ for (i = 0; i < pixelCount; i++, p += 4)
+ *p = stbi__get8(s);
+ }
+ }
+ }
+ }
+ }
+
+ // remove weird white matte from PSD
+ if (channelCount >= 4) {
+ if (ri->bits_per_channel == 16) {
+ for (i=0; i < w*h; ++i) {
+ stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i;
+ if (pixel[3] != 0 && pixel[3] != 65535) {
+ float a = pixel[3] / 65535.0f;
+ float ra = 1.0f / a;
+ float inv_a = 65535.0f * (1 - ra);
+ pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a);
+ pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a);
+ pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a);
+ }
+ }
+ } else {
+ for (i=0; i < w*h; ++i) {
+ unsigned char *pixel = out + 4*i;
+ if (pixel[3] != 0 && pixel[3] != 255) {
+ float a = pixel[3] / 255.0f;
+ float ra = 1.0f / a;
+ float inv_a = 255.0f * (1 - ra);
+ pixel[0] = (unsigned char) (pixel[0]*ra + inv_a);
+ pixel[1] = (unsigned char) (pixel[1]*ra + inv_a);
+ pixel[2] = (unsigned char) (pixel[2]*ra + inv_a);
+ }
+ }
+ }
+ }
+
+ // convert to desired output format
+ if (req_comp && req_comp != 4) {
+ if (ri->bits_per_channel == 16)
+ out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h);
+ else
+ out = stbi__convert_format(out, 4, req_comp, w, h);
+ if (out == NULL) return out; // stbi__convert_format frees input on failure
+ }
+
+ if (comp) *comp = 4;
+ *y = h;
+ *x = w;
+
+ return out;
+}
+#endif
+
+// *************************************************************************************************
+// Softimage PIC loader
+// by Tom Seddon
+//
+// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format
+// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/
+
+#ifndef STBI_NO_PIC
+static int stbi__pic_is4(stbi__context *s,const char *str)
+{
+ int i;
+ for (i=0; i<4; ++i)
+ if (stbi__get8(s) != (stbi_uc)str[i])
+ return 0;
+
+ return 1;
+}
+
+static int stbi__pic_test_core(stbi__context *s)
+{
+ int i;
+
+ if (!stbi__pic_is4(s,"\x53\x80\xF6\x34"))
+ return 0;
+
+ for(i=0;i<84;++i)
+ stbi__get8(s);
+
+ if (!stbi__pic_is4(s,"PICT"))
+ return 0;
+
+ return 1;
+}
+
+typedef struct
+{
+ stbi_uc size,type,channel;
+} stbi__pic_packet;
+
+static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest)
+{
+ int mask=0x80, i;
+
+ for (i=0; i<4; ++i, mask>>=1) {
+ if (channel & mask) {
+ if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short");
+ dest[i]=stbi__get8(s);
+ }
+ }
+
+ return dest;
+}
+
+static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src)
+{
+ int mask=0x80,i;
+
+ for (i=0;i<4; ++i, mask>>=1)
+ if (channel&mask)
+ dest[i]=src[i];
+}
+
+static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result)
+{
+ int act_comp=0,num_packets=0,y,chained;
+ stbi__pic_packet packets[10];
+
+ // this will (should...) cater for even some bizarre stuff like having data
+ // for the same channel in multiple packets.
+ do {
+ stbi__pic_packet *packet;
+
+ if (num_packets==sizeof(packets)/sizeof(packets[0]))
+ return stbi__errpuc("bad format","too many packets");
+
+ packet = &packets[num_packets++];
+
+ chained = stbi__get8(s);
+ packet->size = stbi__get8(s);
+ packet->type = stbi__get8(s);
+ packet->channel = stbi__get8(s);
+
+ act_comp |= packet->channel;
+
+ if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)");
+ if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp");
+ } while (chained);
+
+ *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel?
+
+ for(y=0; y<height; ++y) {
+ int packet_idx;
+
+ for(packet_idx=0; packet_idx < num_packets; ++packet_idx) {
+ stbi__pic_packet *packet = &packets[packet_idx];
+ stbi_uc *dest = result+y*width*4;
+
+ switch (packet->type) {
+ default:
+ return stbi__errpuc("bad format","packet has bad compression type");
+
+ case 0: {//uncompressed
+ int x;
+
+ for(x=0;x<width;++x, dest+=4)
+ if (!stbi__readval(s,packet->channel,dest))
+ return 0;
+ break;
+ }
+
+ case 1://Pure RLE
+ {
+ int left=width, i;
+
+ while (left>0) {
+ stbi_uc count,value[4];
+
+ count=stbi__get8(s);
+ if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)");
+
+ if (count > left)
+ count = (stbi_uc) left;
+
+ if (!stbi__readval(s,packet->channel,value)) return 0;
+
+ for(i=0; i<count; ++i,dest+=4)
+ stbi__copyval(packet->channel,dest,value);
+ left -= count;
+ }
+ }
+ break;
+
+ case 2: {//Mixed RLE
+ int left=width;
+ while (left>0) {
+ int count = stbi__get8(s), i;
+ if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)");
+
+ if (count >= 128) { // Repeated
+ stbi_uc value[4];
+
+ if (count==128)
+ count = stbi__get16be(s);
+ else
+ count -= 127;
+ if (count > left)
+ return stbi__errpuc("bad file","scanline overrun");
+
+ if (!stbi__readval(s,packet->channel,value))
+ return 0;
+
+ for(i=0;i<count;++i, dest += 4)
+ stbi__copyval(packet->channel,dest,value);
+ } else { // Raw
+ ++count;
+ if (count>left) return stbi__errpuc("bad file","scanline overrun");
+
+ for(i=0;i<count;++i, dest+=4)
+ if (!stbi__readval(s,packet->channel,dest))
+ return 0;
+ }
+ left-=count;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri)
+{
+ stbi_uc *result;
+ int i, x,y, internal_comp;
+ STBI_NOTUSED(ri);
+
+ if (!comp) comp = &internal_comp;
+
+ for (i=0; i<92; ++i)
+ stbi__get8(s);
+
+ x = stbi__get16be(s);
+ y = stbi__get16be(s);
+
+ if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)");
+ if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)");
+
+ if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)");
+ if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode");
+
+ stbi__get32be(s); //skip `ratio'
+ stbi__get16be(s); //skip `fields'
+ stbi__get16be(s); //skip `pad'
+
+ // intermediate buffer is RGBA
+ result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0);
+ if (!result) return stbi__errpuc("outofmem", "Out of memory");
+ memset(result, 0xff, x*y*4);
+
+ if (!stbi__pic_load_core(s,x,y,comp, result)) {
+ STBI_FREE(result);
+ result=0;
+ }
+ *px = x;
+ *py = y;
+ if (req_comp == 0) req_comp = *comp;
+ result=stbi__convert_format(result,4,req_comp,x,y);
+
+ return result;
+}
+
+static int stbi__pic_test(stbi__context *s)
+{
+ int r = stbi__pic_test_core(s);
+ stbi__rewind(s);
+ return r;
+}
+#endif
+
+// *************************************************************************************************
+// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb
+
+#ifndef STBI_NO_GIF
+typedef struct
+{
+ stbi__int16 prefix;
+ stbi_uc first;
+ stbi_uc suffix;
+} stbi__gif_lzw;
+
+typedef struct
+{
+ int w,h;
+ stbi_uc *out; // output buffer (always 4 components)
+ stbi_uc *background; // The current "background" as far as a gif is concerned
+ stbi_uc *history;
+ int flags, bgindex, ratio, transparent, eflags;
+ stbi_uc pal[256][4];
+ stbi_uc lpal[256][4];
+ stbi__gif_lzw codes[8192];
+ stbi_uc *color_table;
+ int parse, step;
+ int lflags;
+ int start_x, start_y;
+ int max_x, max_y;
+ int cur_x, cur_y;
+ int line_size;
+ int delay;
+} stbi__gif;
+
+static int stbi__gif_test_raw(stbi__context *s)
+{
+ int sz;
+ if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0;
+ sz = stbi__get8(s);
+ if (sz != '9' && sz != '7') return 0;
+ if (stbi__get8(s) != 'a') return 0;
+ return 1;
+}
+
+static int stbi__gif_test(stbi__context *s)
+{
+ int r = stbi__gif_test_raw(s);
+ stbi__rewind(s);
+ return r;
+}
+
+static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp)
+{
+ int i;
+ for (i=0; i < num_entries; ++i) {
+ pal[i][2] = stbi__get8(s);
+ pal[i][1] = stbi__get8(s);
+ pal[i][0] = stbi__get8(s);
+ pal[i][3] = transp == i ? 0 : 255;
+ }
+}
+
+static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info)
+{
+ stbi_uc version;
+ if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8')
+ return stbi__err("not GIF", "Corrupt GIF");
+
+ version = stbi__get8(s);
+ if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF");
+ if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF");
+
+ stbi__g_failure_reason = "";
+ g->w = stbi__get16le(s);
+ g->h = stbi__get16le(s);
+ g->flags = stbi__get8(s);
+ g->bgindex = stbi__get8(s);
+ g->ratio = stbi__get8(s);
+ g->transparent = -1;
+
+ if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)");
+ if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)");
+
+ if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments
+
+ if (is_info) return 1;
+
+ if (g->flags & 0x80)
+ stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1);
+
+ return 1;
+}
+
+static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp)
+{
+ stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif));
+ if (!g) return stbi__err("outofmem", "Out of memory");
+ if (!stbi__gif_header(s, g, comp, 1)) {
+ STBI_FREE(g);
+ stbi__rewind( s );
+ return 0;
+ }
+ if (x) *x = g->w;
+ if (y) *y = g->h;
+ STBI_FREE(g);
+ return 1;
+}
+
+static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code)
+{
+ stbi_uc *p, *c;
+ int idx;
+
+ // recurse to decode the prefixes, since the linked-list is backwards,
+ // and working backwards through an interleaved image would be nasty
+ if (g->codes[code].prefix >= 0)
+ stbi__out_gif_code(g, g->codes[code].prefix);
+
+ if (g->cur_y >= g->max_y) return;
+
+ idx = g->cur_x + g->cur_y;
+ p = &g->out[idx];
+ g->history[idx / 4] = 1;
+
+ c = &g->color_table[g->codes[code].suffix * 4];
+ if (c[3] > 128) { // don't render transparent pixels;
+ p[0] = c[2];
+ p[1] = c[1];
+ p[2] = c[0];
+ p[3] = c[3];
+ }
+ g->cur_x += 4;
+
+ if (g->cur_x >= g->max_x) {
+ g->cur_x = g->start_x;
+ g->cur_y += g->step;
+
+ while (g->cur_y >= g->max_y && g->parse > 0) {
+ g->step = (1 << g->parse) * g->line_size;
+ g->cur_y = g->start_y + (g->step >> 1);
+ --g->parse;
+ }
+ }
+}
+
+static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g)
+{
+ stbi_uc lzw_cs;
+ stbi__int32 len, init_code;
+ stbi__uint32 first;
+ stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear;
+ stbi__gif_lzw *p;
+
+ lzw_cs = stbi__get8(s);
+ if (lzw_cs > 12) return NULL;
+ clear = 1 << lzw_cs;
+ first = 1;
+ codesize = lzw_cs + 1;
+ codemask = (1 << codesize) - 1;
+ bits = 0;
+ valid_bits = 0;
+ for (init_code = 0; init_code < clear; init_code++) {
+ g->codes[init_code].prefix = -1;
+ g->codes[init_code].first = (stbi_uc) init_code;
+ g->codes[init_code].suffix = (stbi_uc) init_code;
+ }
+
+ // support no starting clear code
+ avail = clear+2;
+ oldcode = -1;
+
+ len = 0;
+ for(;;) {
+ if (valid_bits < codesize) {
+ if (len == 0) {
+ len = stbi__get8(s); // start new block
+ if (len == 0)
+ return g->out;
+ }
+ --len;
+ bits |= (stbi__int32) stbi__get8(s) << valid_bits;
+ valid_bits += 8;
+ } else {
+ stbi__int32 code = bits & codemask;
+ bits >>= codesize;
+ valid_bits -= codesize;
+ // @OPTIMIZE: is there some way we can accelerate the non-clear path?
+ if (code == clear) { // clear code
+ codesize = lzw_cs + 1;
+ codemask = (1 << codesize) - 1;
+ avail = clear + 2;
+ oldcode = -1;
+ first = 0;
+ } else if (code == clear + 1) { // end of stream code
+ stbi__skip(s, len);
+ while ((len = stbi__get8(s)) > 0)
+ stbi__skip(s,len);
+ return g->out;
+ } else if (code <= avail) {
+ if (first) {
+ return stbi__errpuc("no clear code", "Corrupt GIF");
+ }
+
+ if (oldcode >= 0) {
+ p = &g->codes[avail++];
+ if (avail > 8192) {
+ return stbi__errpuc("too many codes", "Corrupt GIF");
+ }
+
+ p->prefix = (stbi__int16) oldcode;
+ p->first = g->codes[oldcode].first;
+ p->suffix = (code == avail) ? p->first : g->codes[code].first;
+ } else if (code == avail)
+ return stbi__errpuc("illegal code in raster", "Corrupt GIF");
+
+ stbi__out_gif_code(g, (stbi__uint16) code);
+
+ if ((avail & codemask) == 0 && avail <= 0x0FFF) {
+ codesize++;
+ codemask = (1 << codesize) - 1;
+ }
+
+ oldcode = code;
+ } else {
+ return stbi__errpuc("illegal code in raster", "Corrupt GIF");
+ }
+ }
+ }
+}
+
+// this function is designed to support animated gifs, although stb_image doesn't support it
+// two back is the image from two frames ago, used for a very specific disposal format
+static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back)
+{
+ int dispose;
+ int first_frame;
+ int pi;
+ int pcount;
+ STBI_NOTUSED(req_comp);
+
+ // on first frame, any non-written pixels get the background colour (non-transparent)
+ first_frame = 0;
+ if (g->out == 0) {
+ if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header
+ if (!stbi__mad3sizes_valid(4, g->w, g->h, 0))
+ return stbi__errpuc("too large", "GIF image is too large");
+ pcount = g->w * g->h;
+ g->out = (stbi_uc *) stbi__malloc(4 * pcount);
+ g->background = (stbi_uc *) stbi__malloc(4 * pcount);
+ g->history = (stbi_uc *) stbi__malloc(pcount);
+ if (!g->out || !g->background || !g->history)
+ return stbi__errpuc("outofmem", "Out of memory");
+
+ // image is treated as "transparent" at the start - ie, nothing overwrites the current background;
+ // background colour is only used for pixels that are not rendered first frame, after that "background"
+ // color refers to the color that was there the previous frame.
+ memset(g->out, 0x00, 4 * pcount);
+ memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent)
+ memset(g->history, 0x00, pcount); // pixels that were affected previous frame
+ first_frame = 1;
+ } else {
+ // second frame - how do we dispose of the previous one?
+ dispose = (g->eflags & 0x1C) >> 2;
+ pcount = g->w * g->h;
+
+ if ((dispose == 3) && (two_back == 0)) {
+ dispose = 2; // if I don't have an image to revert back to, default to the old background
+ }
+
+ if (dispose == 3) { // use previous graphic
+ for (pi = 0; pi < pcount; ++pi) {
+ if (g->history[pi]) {
+ memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 );
+ }
+ }
+ } else if (dispose == 2) {
+ // restore what was changed last frame to background before that frame;
+ for (pi = 0; pi < pcount; ++pi) {
+ if (g->history[pi]) {
+ memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 );
+ }
+ }
+ } else {
+ // This is a non-disposal case eithe way, so just
+ // leave the pixels as is, and they will become the new background
+ // 1: do not dispose
+ // 0: not specified.
+ }
+
+ // background is what out is after the undoing of the previou frame;
+ memcpy( g->background, g->out, 4 * g->w * g->h );
+ }
+
+ // clear my history;
+ memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame
+
+ for (;;) {
+ int tag = stbi__get8(s);
+ switch (tag) {
+ case 0x2C: /* Image Descriptor */
+ {
+ stbi__int32 x, y, w, h;
+ stbi_uc *o;
+
+ x = stbi__get16le(s);
+ y = stbi__get16le(s);
+ w = stbi__get16le(s);
+ h = stbi__get16le(s);
+ if (((x + w) > (g->w)) || ((y + h) > (g->h)))
+ return stbi__errpuc("bad Image Descriptor", "Corrupt GIF");
+
+ g->line_size = g->w * 4;
+ g->start_x = x * 4;
+ g->start_y = y * g->line_size;
+ g->max_x = g->start_x + w * 4;
+ g->max_y = g->start_y + h * g->line_size;
+ g->cur_x = g->start_x;
+ g->cur_y = g->start_y;
+
+ // if the width of the specified rectangle is 0, that means
+ // we may not see *any* pixels or the image is malformed;
+ // to make sure this is caught, move the current y down to
+ // max_y (which is what out_gif_code checks).
+ if (w == 0)
+ g->cur_y = g->max_y;
+
+ g->lflags = stbi__get8(s);
+
+ if (g->lflags & 0x40) {
+ g->step = 8 * g->line_size; // first interlaced spacing
+ g->parse = 3;
+ } else {
+ g->step = g->line_size;
+ g->parse = 0;
+ }
+
+ if (g->lflags & 0x80) {
+ stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1);
+ g->color_table = (stbi_uc *) g->lpal;
+ } else if (g->flags & 0x80) {
+ g->color_table = (stbi_uc *) g->pal;
+ } else
+ return stbi__errpuc("missing color table", "Corrupt GIF");
+
+ o = stbi__process_gif_raster(s, g);
+ if (!o) return NULL;
+
+ // if this was the first frame,
+ pcount = g->w * g->h;
+ if (first_frame && (g->bgindex > 0)) {
+ // if first frame, any pixel not drawn to gets the background color
+ for (pi = 0; pi < pcount; ++pi) {
+ if (g->history[pi] == 0) {
+ g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be;
+ memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 );
+ }
+ }
+ }
+
+ return o;
+ }
+
+ case 0x21: // Comment Extension.
+ {
+ int len;
+ int ext = stbi__get8(s);
+ if (ext == 0xF9) { // Graphic Control Extension.
+ len = stbi__get8(s);
+ if (len == 4) {
+ g->eflags = stbi__get8(s);
+ g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths.
+
+ // unset old transparent
+ if (g->transparent >= 0) {
+ g->pal[g->transparent][3] = 255;
+ }
+ if (g->eflags & 0x01) {
+ g->transparent = stbi__get8(s);
+ if (g->transparent >= 0) {
+ g->pal[g->transparent][3] = 0;
+ }
+ } else {
+ // don't need transparent
+ stbi__skip(s, 1);
+ g->transparent = -1;
+ }
+ } else {
+ stbi__skip(s, len);
+ break;
+ }
+ }
+ while ((len = stbi__get8(s)) != 0) {
+ stbi__skip(s, len);
+ }
+ break;
+ }
+
+ case 0x3B: // gif stream termination code
+ return (stbi_uc *) s; // using '1' causes warning on some compilers
+
+ default:
+ return stbi__errpuc("unknown code", "Corrupt GIF");
+ }
+ }
+}
+
+static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays)
+{
+ STBI_FREE(g->out);
+ STBI_FREE(g->history);
+ STBI_FREE(g->background);
+
+ if (out) STBI_FREE(out);
+ if (delays && *delays) STBI_FREE(*delays);
+ return stbi__errpuc("outofmem", "Out of memory");
+}
+
+static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp)
+{
+ if (stbi__gif_test(s)) {
+ int layers = 0;
+ stbi_uc *u = 0;
+ stbi_uc *out = 0;
+ stbi_uc *two_back = 0;
+ stbi__gif g;
+ int stride;
+ int out_size = 0;
+ int delays_size = 0;
+
+ STBI_NOTUSED(out_size);
+ STBI_NOTUSED(delays_size);
+
+ memset(&g, 0, sizeof(g));
+ if (delays) {
+ *delays = 0;
+ }
+
+ do {
+ u = stbi__gif_load_next(s, &g, comp, req_comp, two_back);
+ if (u == (stbi_uc *) s) u = 0; // end of animated gif marker
+
+ if (u) {
+ *x = g.w;
+ *y = g.h;
+ ++layers;
+ stride = g.w * g.h * 4;
+
+ if (out) {
+ void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride );
+ if (!tmp)
+ return stbi__load_gif_main_outofmem(&g, out, delays);
+ else {
+ out = (stbi_uc*) tmp;
+ out_size = layers * stride;
+ }
+
+ if (delays) {
+ int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers );
+ if (!new_delays)
+ return stbi__load_gif_main_outofmem(&g, out, delays);
+ *delays = new_delays;
+ delays_size = layers * sizeof(int);
+ }
+ } else {
+ out = (stbi_uc*)stbi__malloc( layers * stride );
+ if (!out)
+ return stbi__load_gif_main_outofmem(&g, out, delays);
+ out_size = layers * stride;
+ if (delays) {
+ *delays = (int*) stbi__malloc( layers * sizeof(int) );
+ if (!*delays)
+ return stbi__load_gif_main_outofmem(&g, out, delays);
+ delays_size = layers * sizeof(int);
+ }
+ }
+ memcpy( out + ((layers - 1) * stride), u, stride );
+ if (layers >= 2) {
+ two_back = out - 2 * stride;
+ }
+
+ if (delays) {
+ (*delays)[layers - 1U] = g.delay;
+ }
+ }
+ } while (u != 0);
+
+ // free temp buffer;
+ STBI_FREE(g.out);
+ STBI_FREE(g.history);
+ STBI_FREE(g.background);
+
+ // do the final conversion after loading everything;
+ if (req_comp && req_comp != 4)
+ out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h);
+
+ *z = layers;
+ return out;
+ } else {
+ return stbi__errpuc("not GIF", "Image was not as a gif type.");
+ }
+}
+
+static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
+{
+ stbi_uc *u = 0;
+ stbi__gif g;
+ memset(&g, 0, sizeof(g));
+ STBI_NOTUSED(ri);
+
+ u = stbi__gif_load_next(s, &g, comp, req_comp, 0);
+ if (u == (stbi_uc *) s) u = 0; // end of animated gif marker
+ if (u) {
+ *x = g.w;
+ *y = g.h;
+
+ // moved conversion to after successful load so that the same
+ // can be done for multiple frames.
+ if (req_comp && req_comp != 4)
+ u = stbi__convert_format(u, 4, req_comp, g.w, g.h);
+ } else if (g.out) {
+ // if there was an error and we allocated an image buffer, free it!
+ STBI_FREE(g.out);
+ }
+
+ // free buffers needed for multiple frame loading;
+ STBI_FREE(g.history);
+ STBI_FREE(g.background);
+
+ return u;
+}
+
+static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp)
+{
+ return stbi__gif_info_raw(s,x,y,comp);
+}
+#endif
+
+// *************************************************************************************************
+// Radiance RGBE HDR loader
+// originally by Nicolas Schulz
+#ifndef STBI_NO_HDR
+static int stbi__hdr_test_core(stbi__context *s, const char *signature)
+{
+ int i;
+ for (i=0; signature[i]; ++i)
+ if (stbi__get8(s) != signature[i])
+ return 0;
+ stbi__rewind(s);
+ return 1;
+}
+
+static int stbi__hdr_test(stbi__context* s)
+{
+ int r = stbi__hdr_test_core(s, "#?RADIANCE\n");
+ stbi__rewind(s);
+ if(!r) {
+ r = stbi__hdr_test_core(s, "#?RGBE\n");
+ stbi__rewind(s);
+ }
+ return r;
+}
+
+#define STBI__HDR_BUFLEN 1024
+static char *stbi__hdr_gettoken(stbi__context *z, char *buffer)
+{
+ int len=0;
+ char c = '\0';
+
+ c = (char) stbi__get8(z);
+
+ while (!stbi__at_eof(z) && c != '\n') {
+ buffer[len++] = c;
+ if (len == STBI__HDR_BUFLEN-1) {
+ // flush to end of line
+ while (!stbi__at_eof(z) && stbi__get8(z) != '\n')
+ ;
+ break;
+ }
+ c = (char) stbi__get8(z);
+ }
+
+ buffer[len] = 0;
+ return buffer;
+}
+
+static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp)
+{
+ if ( input[3] != 0 ) {
+ float f1;
+ // Exponent
+ f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8));
+ if (req_comp <= 2)
+ output[0] = (input[0] + input[1] + input[2]) * f1 / 3;
+ else {
+ output[0] = input[0] * f1;
+ output[1] = input[1] * f1;
+ output[2] = input[2] * f1;
+ }
+ if (req_comp == 2) output[1] = 1;
+ if (req_comp == 4) output[3] = 1;
+ } else {
+ switch (req_comp) {
+ case 4: output[3] = 1; /* fallthrough */
+ case 3: output[0] = output[1] = output[2] = 0;
+ break;
+ case 2: output[1] = 1; /* fallthrough */
+ case 1: output[0] = 0;
+ break;
+ }
+ }
+}
+
+static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
+{
+ char buffer[STBI__HDR_BUFLEN];
+ char *token;
+ int valid = 0;
+ int width, height;
+ stbi_uc *scanline;
+ float *hdr_data;
+ int len;
+ unsigned char count, value;
+ int i, j, k, c1,c2, z;
+ const char *headerToken;
+ STBI_NOTUSED(ri);
+
+ // Check identifier
+ headerToken = stbi__hdr_gettoken(s,buffer);
+ if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0)
+ return stbi__errpf("not HDR", "Corrupt HDR image");
+
+ // Parse header
+ for(;;) {
+ token = stbi__hdr_gettoken(s,buffer);
+ if (token[0] == 0) break;
+ if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
+ }
+
+ if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format");
+
+ // Parse width and height
+ // can't use sscanf() if we're not using stdio!
+ token = stbi__hdr_gettoken(s,buffer);
+ if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format");
+ token += 3;
+ height = (int) strtol(token, &token, 10);
+ while (*token == ' ') ++token;
+ if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format");
+ token += 3;
+ width = (int) strtol(token, NULL, 10);
+
+ if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)");
+ if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)");
+
+ *x = width;
+ *y = height;
+
+ if (comp) *comp = 3;
+ if (req_comp == 0) req_comp = 3;
+
+ if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0))
+ return stbi__errpf("too large", "HDR image is too large");
+
+ // Read data
+ hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0);
+ if (!hdr_data)
+ return stbi__errpf("outofmem", "Out of memory");
+
+ // Load image data
+ // image data is stored as some number of sca
+ if ( width < 8 || width >= 32768) {
+ // Read flat data
+ for (j=0; j < height; ++j) {
+ for (i=0; i < width; ++i) {
+ stbi_uc rgbe[4];
+ main_decode_loop:
+ stbi__getn(s, rgbe, 4);
+ stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp);
+ }
+ }
+ } else {
+ // Read RLE-encoded data
+ scanline = NULL;
+
+ for (j = 0; j < height; ++j) {
+ c1 = stbi__get8(s);
+ c2 = stbi__get8(s);
+ len = stbi__get8(s);
+ if (c1 != 2 || c2 != 2 || (len & 0x80)) {
+ // not run-length encoded, so we have to actually use THIS data as a decoded
+ // pixel (note this can't be a valid pixel--one of RGB must be >= 128)
+ stbi_uc rgbe[4];
+ rgbe[0] = (stbi_uc) c1;
+ rgbe[1] = (stbi_uc) c2;
+ rgbe[2] = (stbi_uc) len;
+ rgbe[3] = (stbi_uc) stbi__get8(s);
+ stbi__hdr_convert(hdr_data, rgbe, req_comp);
+ i = 1;
+ j = 0;
+ STBI_FREE(scanline);
+ goto main_decode_loop; // yes, this makes no sense
+ }
+ len <<= 8;
+ len |= stbi__get8(s);
+ if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); }
+ if (scanline == NULL) {
+ scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0);
+ if (!scanline) {
+ STBI_FREE(hdr_data);
+ return stbi__errpf("outofmem", "Out of memory");
+ }
+ }
+
+ for (k = 0; k < 4; ++k) {
+ int nleft;
+ i = 0;
+ while ((nleft = width - i) > 0) {
+ count = stbi__get8(s);
+ if (count > 128) {
+ // Run
+ value = stbi__get8(s);
+ count -= 128;
+ if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
+ for (z = 0; z < count; ++z)
+ scanline[i++ * 4 + k] = value;
+ } else {
+ // Dump
+ if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
+ for (z = 0; z < count; ++z)
+ scanline[i++ * 4 + k] = stbi__get8(s);
+ }
+ }
+ }
+ for (i=0; i < width; ++i)
+ stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp);
+ }
+ if (scanline)
+ STBI_FREE(scanline);
+ }
+
+ return hdr_data;
+}
+
+static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp)
+{
+ char buffer[STBI__HDR_BUFLEN];
+ char *token;
+ int valid = 0;
+ int dummy;
+
+ if (!x) x = &dummy;
+ if (!y) y = &dummy;
+ if (!comp) comp = &dummy;
+
+ if (stbi__hdr_test(s) == 0) {
+ stbi__rewind( s );
+ return 0;
+ }
+
+ for(;;) {
+ token = stbi__hdr_gettoken(s,buffer);
+ if (token[0] == 0) break;
+ if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
+ }
+
+ if (!valid) {
+ stbi__rewind( s );
+ return 0;
+ }
+ token = stbi__hdr_gettoken(s,buffer);
+ if (strncmp(token, "-Y ", 3)) {
+ stbi__rewind( s );
+ return 0;
+ }
+ token += 3;
+ *y = (int) strtol(token, &token, 10);
+ while (*token == ' ') ++token;
+ if (strncmp(token, "+X ", 3)) {
+ stbi__rewind( s );
+ return 0;
+ }
+ token += 3;
+ *x = (int) strtol(token, NULL, 10);
+ *comp = 3;
+ return 1;
+}
+#endif // STBI_NO_HDR
+
+#ifndef STBI_NO_BMP
+static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp)
+{
+ void *p;
+ stbi__bmp_data info;
+
+ info.all_a = 255;
+ p = stbi__bmp_parse_header(s, &info);
+ if (p == NULL) {
+ stbi__rewind( s );
+ return 0;
+ }
+ if (x) *x = s->img_x;
+ if (y) *y = s->img_y;
+ if (comp) {
+ if (info.bpp == 24 && info.ma == 0xff000000)
+ *comp = 3;
+ else
+ *comp = info.ma ? 4 : 3;
+ }
+ return 1;
+}
+#endif
+
+#ifndef STBI_NO_PSD
+static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp)
+{
+ int channelCount, dummy, depth;
+ if (!x) x = &dummy;
+ if (!y) y = &dummy;
+ if (!comp) comp = &dummy;
+ if (stbi__get32be(s) != 0x38425053) {
+ stbi__rewind( s );
+ return 0;
+ }
+ if (stbi__get16be(s) != 1) {
+ stbi__rewind( s );
+ return 0;
+ }
+ stbi__skip(s, 6);
+ channelCount = stbi__get16be(s);
+ if (channelCount < 0 || channelCount > 16) {
+ stbi__rewind( s );
+ return 0;
+ }
+ *y = stbi__get32be(s);
+ *x = stbi__get32be(s);
+ depth = stbi__get16be(s);
+ if (depth != 8 && depth != 16) {
+ stbi__rewind( s );
+ return 0;
+ }
+ if (stbi__get16be(s) != 3) {
+ stbi__rewind( s );
+ return 0;
+ }
+ *comp = 4;
+ return 1;
+}
+
+static int stbi__psd_is16(stbi__context *s)
+{
+ int channelCount, depth;
+ if (stbi__get32be(s) != 0x38425053) {
+ stbi__rewind( s );
+ return 0;
+ }
+ if (stbi__get16be(s) != 1) {
+ stbi__rewind( s );
+ return 0;
+ }
+ stbi__skip(s, 6);
+ channelCount = stbi__get16be(s);
+ if (channelCount < 0 || channelCount > 16) {
+ stbi__rewind( s );
+ return 0;
+ }
+ STBI_NOTUSED(stbi__get32be(s));
+ STBI_NOTUSED(stbi__get32be(s));
+ depth = stbi__get16be(s);
+ if (depth != 16) {
+ stbi__rewind( s );
+ return 0;
+ }
+ return 1;
+}
+#endif
+
+#ifndef STBI_NO_PIC
+static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp)
+{
+ int act_comp=0,num_packets=0,chained,dummy;
+ stbi__pic_packet packets[10];
+
+ if (!x) x = &dummy;
+ if (!y) y = &dummy;
+ if (!comp) comp = &dummy;
+
+ if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) {
+ stbi__rewind(s);
+ return 0;
+ }
+
+ stbi__skip(s, 88);
+
+ *x = stbi__get16be(s);
+ *y = stbi__get16be(s);
+ if (stbi__at_eof(s)) {
+ stbi__rewind( s);
+ return 0;
+ }
+ if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) {
+ stbi__rewind( s );
+ return 0;
+ }
+
+ stbi__skip(s, 8);
+
+ do {
+ stbi__pic_packet *packet;
+
+ if (num_packets==sizeof(packets)/sizeof(packets[0]))
+ return 0;
+
+ packet = &packets[num_packets++];
+ chained = stbi__get8(s);
+ packet->size = stbi__get8(s);
+ packet->type = stbi__get8(s);
+ packet->channel = stbi__get8(s);
+ act_comp |= packet->channel;
+
+ if (stbi__at_eof(s)) {
+ stbi__rewind( s );
+ return 0;
+ }
+ if (packet->size != 8) {
+ stbi__rewind( s );
+ return 0;
+ }
+ } while (chained);
+
+ *comp = (act_comp & 0x10 ? 4 : 3);
+
+ return 1;
+}
+#endif
+
+// *************************************************************************************************
+// Portable Gray Map and Portable Pixel Map loader
+// by Ken Miller
+//
+// PGM: http://netpbm.sourceforge.net/doc/pgm.html
+// PPM: http://netpbm.sourceforge.net/doc/ppm.html
+//
+// Known limitations:
+// Does not support comments in the header section
+// Does not support ASCII image data (formats P2 and P3)
+
+#ifndef STBI_NO_PNM
+
+static int stbi__pnm_test(stbi__context *s)
+{
+ char p, t;
+ p = (char) stbi__get8(s);
+ t = (char) stbi__get8(s);
+ if (p != 'P' || (t != '5' && t != '6')) {
+ stbi__rewind( s );
+ return 0;
+ }
+ return 1;
+}
+
+static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
+{
+ stbi_uc *out;
+ STBI_NOTUSED(ri);
+
+ ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n);
+ if (ri->bits_per_channel == 0)
+ return 0;
+
+ if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)");
+ if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)");
+
+ *x = s->img_x;
+ *y = s->img_y;
+ if (comp) *comp = s->img_n;
+
+ if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0))
+ return stbi__errpuc("too large", "PNM too large");
+
+ out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0);
+ if (!out) return stbi__errpuc("outofmem", "Out of memory");
+ if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) {
+ STBI_FREE(out);
+ return stbi__errpuc("bad PNM", "PNM file truncated");
+ }
+
+ if (req_comp && req_comp != s->img_n) {
+ if (ri->bits_per_channel == 16) {
+ out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y);
+ } else {
+ out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
+ }
+ if (out == NULL) return out; // stbi__convert_format frees input on failure
+ }
+ return out;
+}
+
+static int stbi__pnm_isspace(char c)
+{
+ return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
+}
+
+static void stbi__pnm_skip_whitespace(stbi__context *s, char *c)
+{
+ for (;;) {
+ while (!stbi__at_eof(s) && stbi__pnm_isspace(*c))
+ *c = (char) stbi__get8(s);
+
+ if (stbi__at_eof(s) || *c != '#')
+ break;
+
+ while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' )
+ *c = (char) stbi__get8(s);
+ }
+}
+
+static int stbi__pnm_isdigit(char c)
+{
+ return c >= '0' && c <= '9';
+}
+
+static int stbi__pnm_getinteger(stbi__context *s, char *c)
+{
+ int value = 0;
+
+ while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) {
+ value = value*10 + (*c - '0');
+ *c = (char) stbi__get8(s);
+ if((value > 214748364) || (value == 214748364 && *c > '7'))
+ return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int");
+ }
+
+ return value;
+}
+
+static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp)
+{
+ int maxv, dummy;
+ char c, p, t;
+
+ if (!x) x = &dummy;
+ if (!y) y = &dummy;
+ if (!comp) comp = &dummy;
+
+ stbi__rewind(s);
+
+ // Get identifier
+ p = (char) stbi__get8(s);
+ t = (char) stbi__get8(s);
+ if (p != 'P' || (t != '5' && t != '6')) {
+ stbi__rewind(s);
+ return 0;
+ }
+
+ *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm
+
+ c = (char) stbi__get8(s);
+ stbi__pnm_skip_whitespace(s, &c);
+
+ *x = stbi__pnm_getinteger(s, &c); // read width
+ if(*x == 0)
+ return stbi__err("invalid width", "PPM image header had zero or overflowing width");
+ stbi__pnm_skip_whitespace(s, &c);
+
+ *y = stbi__pnm_getinteger(s, &c); // read height
+ if (*y == 0)
+ return stbi__err("invalid width", "PPM image header had zero or overflowing width");
+ stbi__pnm_skip_whitespace(s, &c);
+
+ maxv = stbi__pnm_getinteger(s, &c); // read max value
+ if (maxv > 65535)
+ return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images");
+ else if (maxv > 255)
+ return 16;
+ else
+ return 8;
+}
+
+static int stbi__pnm_is16(stbi__context *s)
+{
+ if (stbi__pnm_info(s, NULL, NULL, NULL) == 16)
+ return 1;
+ return 0;
+}
+#endif
+
+static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp)
+{
+ #ifndef STBI_NO_JPEG
+ if (stbi__jpeg_info(s, x, y, comp)) return 1;
+ #endif
+
+ #ifndef STBI_NO_PNG
+ if (stbi__png_info(s, x, y, comp)) return 1;
+ #endif
+
+ #ifndef STBI_NO_GIF
+ if (stbi__gif_info(s, x, y, comp)) return 1;
+ #endif
+
+ #ifndef STBI_NO_BMP
+ if (stbi__bmp_info(s, x, y, comp)) return 1;
+ #endif
+
+ #ifndef STBI_NO_PSD
+ if (stbi__psd_info(s, x, y, comp)) return 1;
+ #endif
+
+ #ifndef STBI_NO_PIC
+ if (stbi__pic_info(s, x, y, comp)) return 1;
+ #endif
+
+ #ifndef STBI_NO_PNM
+ if (stbi__pnm_info(s, x, y, comp)) return 1;
+ #endif
+
+ #ifndef STBI_NO_HDR
+ if (stbi__hdr_info(s, x, y, comp)) return 1;
+ #endif
+
+ // test tga last because it's a crappy test!
+ #ifndef STBI_NO_TGA
+ if (stbi__tga_info(s, x, y, comp))
+ return 1;
+ #endif
+ return stbi__err("unknown image type", "Image not of any known type, or corrupt");
+}
+
+static int stbi__is_16_main(stbi__context *s)
+{
+ #ifndef STBI_NO_PNG
+ if (stbi__png_is16(s)) return 1;
+ #endif
+
+ #ifndef STBI_NO_PSD
+ if (stbi__psd_is16(s)) return 1;
+ #endif
+
+ #ifndef STBI_NO_PNM
+ if (stbi__pnm_is16(s)) return 1;
+ #endif
+ return 0;
+}
+
+#ifndef STBI_NO_STDIO
+STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp)
+{
+ FILE *f = stbi__fopen(filename, "rb");
+ int result;
+ if (!f) return stbi__err("can't fopen", "Unable to open file");
+ result = stbi_info_from_file(f, x, y, comp);
+ fclose(f);
+ return result;
+}
+
+STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp)
+{
+ int r;
+ stbi__context s;
+ long pos = ftell(f);
+ stbi__start_file(&s, f);
+ r = stbi__info_main(&s,x,y,comp);
+ fseek(f,pos,SEEK_SET);
+ return r;
+}
+
+STBIDEF int stbi_is_16_bit(char const *filename)
+{
+ FILE *f = stbi__fopen(filename, "rb");
+ int result;
+ if (!f) return stbi__err("can't fopen", "Unable to open file");
+ result = stbi_is_16_bit_from_file(f);
+ fclose(f);
+ return result;
+}
+
+STBIDEF int stbi_is_16_bit_from_file(FILE *f)
+{
+ int r;
+ stbi__context s;
+ long pos = ftell(f);
+ stbi__start_file(&s, f);
+ r = stbi__is_16_main(&s);
+ fseek(f,pos,SEEK_SET);
+ return r;
+}
+#endif // !STBI_NO_STDIO
+
+STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp)
+{
+ stbi__context s;
+ stbi__start_mem(&s,buffer,len);
+ return stbi__info_main(&s,x,y,comp);
+}
+
+STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp)
+{
+ stbi__context s;
+ stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user);
+ return stbi__info_main(&s,x,y,comp);
+}
+
+STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len)
+{
+ stbi__context s;
+ stbi__start_mem(&s,buffer,len);
+ return stbi__is_16_main(&s);
+}
+
+STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user)
+{
+ stbi__context s;
+ stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user);
+ return stbi__is_16_main(&s);
+}
+
+#endif // STB_IMAGE_IMPLEMENTATION
+
+/*
+ revision history:
+ 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs
+ 2.19 (2018-02-11) fix warning
+ 2.18 (2018-01-30) fix warnings
+ 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug
+ 1-bit BMP
+ *_is_16_bit api
+ avoid warnings
+ 2.16 (2017-07-23) all functions have 16-bit variants;
+ STBI_NO_STDIO works again;
+ compilation fixes;
+ fix rounding in unpremultiply;
+ optimize vertical flip;
+ disable raw_len validation;
+ documentation fixes
+ 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode;
+ warning fixes; disable run-time SSE detection on gcc;
+ uniform handling of optional "return" values;
+ thread-safe initialization of zlib tables
+ 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs
+ 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now
+ 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes
+ 2.11 (2016-04-02) allocate large structures on the stack
+ remove white matting for transparent PSD
+ fix reported channel count for PNG & BMP
+ re-enable SSE2 in non-gcc 64-bit
+ support RGB-formatted JPEG
+ read 16-bit PNGs (only as 8-bit)
+ 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED
+ 2.09 (2016-01-16) allow comments in PNM files
+ 16-bit-per-pixel TGA (not bit-per-component)
+ info() for TGA could break due to .hdr handling
+ info() for BMP to shares code instead of sloppy parse
+ can use STBI_REALLOC_SIZED if allocator doesn't support realloc
+ code cleanup
+ 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA
+ 2.07 (2015-09-13) fix compiler warnings
+ partial animated GIF support
+ limited 16-bpc PSD support
+ #ifdef unused functions
+ bug with < 92 byte PIC,PNM,HDR,TGA
+ 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value
+ 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning
+ 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit
+ 2.03 (2015-04-12) extra corruption checking (mmozeiko)
+ stbi_set_flip_vertically_on_load (nguillemot)
+ fix NEON support; fix mingw support
+ 2.02 (2015-01-19) fix incorrect assert, fix warning
+ 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2
+ 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG
+ 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg)
+ progressive JPEG (stb)
+ PGM/PPM support (Ken Miller)
+ STBI_MALLOC,STBI_REALLOC,STBI_FREE
+ GIF bugfix -- seemingly never worked
+ STBI_NO_*, STBI_ONLY_*
+ 1.48 (2014-12-14) fix incorrectly-named assert()
+ 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb)
+ optimize PNG (ryg)
+ fix bug in interlaced PNG with user-specified channel count (stb)
+ 1.46 (2014-08-26)
+ fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG
+ 1.45 (2014-08-16)
+ fix MSVC-ARM internal compiler error by wrapping malloc
+ 1.44 (2014-08-07)
+ various warning fixes from Ronny Chevalier
+ 1.43 (2014-07-15)
+ fix MSVC-only compiler problem in code changed in 1.42
+ 1.42 (2014-07-09)
+ don't define _CRT_SECURE_NO_WARNINGS (affects user code)
+ fixes to stbi__cleanup_jpeg path
+ added STBI_ASSERT to avoid requiring assert.h
+ 1.41 (2014-06-25)
+ fix search&replace from 1.36 that messed up comments/error messages
+ 1.40 (2014-06-22)
+ fix gcc struct-initialization warning
+ 1.39 (2014-06-15)
+ fix to TGA optimization when req_comp != number of components in TGA;
+ fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite)
+ add support for BMP version 5 (more ignored fields)
+ 1.38 (2014-06-06)
+ suppress MSVC warnings on integer casts truncating values
+ fix accidental rename of 'skip' field of I/O
+ 1.37 (2014-06-04)
+ remove duplicate typedef
+ 1.36 (2014-06-03)
+ convert to header file single-file library
+ if de-iphone isn't set, load iphone images color-swapped instead of returning NULL
+ 1.35 (2014-05-27)
+ various warnings
+ fix broken STBI_SIMD path
+ fix bug where stbi_load_from_file no longer left file pointer in correct place
+ fix broken non-easy path for 32-bit BMP (possibly never used)
+ TGA optimization by Arseny Kapoulkine
+ 1.34 (unknown)
+ use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case
+ 1.33 (2011-07-14)
+ make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements
+ 1.32 (2011-07-13)
+ support for "info" function for all supported filetypes (SpartanJ)
+ 1.31 (2011-06-20)
+ a few more leak fixes, bug in PNG handling (SpartanJ)
+ 1.30 (2011-06-11)
+ added ability to load files via callbacks to accomidate custom input streams (Ben Wenger)
+ removed deprecated format-specific test/load functions
+ removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway
+ error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha)
+ fix inefficiency in decoding 32-bit BMP (David Woo)
+ 1.29 (2010-08-16)
+ various warning fixes from Aurelien Pocheville
+ 1.28 (2010-08-01)
+ fix bug in GIF palette transparency (SpartanJ)
+ 1.27 (2010-08-01)
+ cast-to-stbi_uc to fix warnings
+ 1.26 (2010-07-24)
+ fix bug in file buffering for PNG reported by SpartanJ
+ 1.25 (2010-07-17)
+ refix trans_data warning (Won Chun)
+ 1.24 (2010-07-12)
+ perf improvements reading from files on platforms with lock-heavy fgetc()
+ minor perf improvements for jpeg
+ deprecated type-specific functions so we'll get feedback if they're needed
+ attempt to fix trans_data warning (Won Chun)
+ 1.23 fixed bug in iPhone support
+ 1.22 (2010-07-10)
+ removed image *writing* support
+ stbi_info support from Jetro Lauha
+ GIF support from Jean-Marc Lienher
+ iPhone PNG-extensions from James Brown
+ warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva)
+ 1.21 fix use of 'stbi_uc' in header (reported by jon blow)
+ 1.20 added support for Softimage PIC, by Tom Seddon
+ 1.19 bug in interlaced PNG corruption check (found by ryg)
+ 1.18 (2008-08-02)
+ fix a threading bug (local mutable static)
+ 1.17 support interlaced PNG
+ 1.16 major bugfix - stbi__convert_format converted one too many pixels
+ 1.15 initialize some fields for thread safety
+ 1.14 fix threadsafe conversion bug
+ header-file-only version (#define STBI_HEADER_FILE_ONLY before including)
+ 1.13 threadsafe
+ 1.12 const qualifiers in the API
+ 1.11 Support installable IDCT, colorspace conversion routines
+ 1.10 Fixes for 64-bit (don't use "unsigned long")
+ optimized upsampling by Fabian "ryg" Giesen
+ 1.09 Fix format-conversion for PSD code (bad global variables!)
+ 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz
+ 1.07 attempt to fix C++ warning/errors again
+ 1.06 attempt to fix C++ warning/errors again
+ 1.05 fix TGA loading to return correct *comp and use good luminance calc
+ 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free
+ 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR
+ 1.02 support for (subset of) HDR files, float interface for preferred access to them
+ 1.01 fix bug: possible bug in handling right-side up bmps... not sure
+ fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all
+ 1.00 interface to zlib that skips zlib header
+ 0.99 correct handling of alpha in palette
+ 0.98 TGA loader by lonesock; dynamically add loaders (untested)
+ 0.97 jpeg errors on too large a file; also catch another malloc failure
+ 0.96 fix detection of invalid v value - particleman@mollyrocket forum
+ 0.95 during header scan, seek to markers in case of padding
+ 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same
+ 0.93 handle jpegtran output; verbose errors
+ 0.92 read 4,8,16,24,32-bit BMP files of several formats
+ 0.91 output 24-bit Windows 3.0 BMP files
+ 0.90 fix a few more warnings; bump version number to approach 1.0
+ 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd
+ 0.60 fix compiling as c++
+ 0.59 fix warnings: merge Dave Moore's -Wall fixes
+ 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian
+ 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available
+ 0.56 fix bug: zlib uncompressed mode len vs. nlen
+ 0.55 fix bug: restart_interval not initialized to 0
+ 0.54 allow NULL for 'int *comp'
+ 0.53 fix bug in png 3->4; speedup png decoding
+ 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments
+ 0.51 obey req_comp requests, 1-component jpegs return as 1-component,
+ on 'test' only check type, not whether we support this variant
+ 0.50 (2006-11-19)
+ first released version
+*/
+
+
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2017 Sean Barrett
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------------------
+*/
diff --git a/rsgl.zig b/rsgl.zig
new file mode 100644
index 0000000..c65780d
--- /dev/null
+++ b/rsgl.zig
@@ -0,0 +1,411 @@
+const __root = @This();
+const raw = @import("rsgl_raw");
+const zeroes = @import("std").mem.zeroes;
+const MAX_BATCHES = 2028;
+const MAX_VERTS = 8192;
+
+pub const textureFormat = enum(c_uint) {
+ none = raw.RSGL_formatNone,
+ RGB = raw.RSGL_formatRGB,
+ BGR = raw.RSGL_formatBGR,
+ RGBA = raw.RSGL_formatRGBA,
+ BGRA = raw.RSGL_formatBGRA,
+ red = raw.RSGL_formatRed,
+ grayscale = raw.RSGL_formatGrayscale,
+};
+
+pub const textureDataType = enum(c_uint) {
+ int = raw.RSGL_textureDataInt,
+ float = raw.RSGL_textureDataFloat,
+};
+
+pub const textureFilter = enum(c_uint) {
+ nearest = raw.RSGL_filterNearest,
+ linear = raw.RSGL_filterLinear,
+};
+
+pub const textureBlob = extern struct {
+ data: ?*anyopaque = null,
+ width: usize = 0,
+ height: usize = 0,
+ dataType: textureDataType = zeroes(textureDataType),
+ dataFormat: textureFormat = zeroes(textureFormat),
+ textureFormat: textureFormat = zeroes(textureFormat),
+ minFilter: textureFilter = zeroes(textureFilter),
+ magFilter: textureFilter = zeroes(textureFilter),
+};
+
+pub const rect = raw.RSGL_rect;
+pub const cube = raw.RSGL_cube;
+pub const vec2 = raw.RSGL_vec2D;
+pub const vec3 = raw.RSGL_vec3D;
+pub const color = raw.RSGL_color;
+pub const mat4 = extern struct {
+ m: [16]f32 = zeroes([16]f32),
+ pub fn loadIdentity() mat4 {return RSGL_mat4_loadIdentity();}
+ pub fn scale(self: *mat4, x: f32, y: f32, z: f32) void {self.* = RSGL_mat4_scale(self.m, x, y, z);}
+ pub fn rotate(self: *mat4, angle: f32, x: f32, y: f32, z: f32) void {self.* = RSGL_mat4_rotate(self.m, angle, x, y, z);}
+ pub fn translate(self: *mat4, x: f32, y: f32, z: f32) void {self.* = RSGL_mat4_translate(self.m, x, y, z);}
+ pub fn perspective(self: *mat4, fovY: f32, aspect: f32, znear: f32, zfar: f32) void {self.* = RSGL_mat4_perspective(self.m, fovY, aspect, znear, zfar);}
+ pub fn ortho(self: *mat4, left: f32, right: f32, bottom: f32, top: f32, znear: f32, zfar: f32) void {self.* = RSGL_mat4_ortho(self.m, left, right, bottom, top, znear, zfar);}
+ pub fn lookAt(self: *mat4, eyeX: f32, eyeY: f32, eyeZ: f32, targetX: f32, targetY: f32, targetZ: f32, upX: f32, upY: f32, upZ: f32) void {self.* = RSGL_mat4_lookAt(self.m, eyeX, eyeY, eyeZ, targetX, targetY, targetZ, upX, upY, upZ);}
+ pub fn multiply(self: *mat4, right: *mat4) void {self.* = RSGL_mat4_multiply(self.m, right.m);}
+ pub fn multiplyPoint(self: mat4, point: vec3) vec3 {return RSGL_mat4_multiplyPoint(self, point);}
+};
+extern fn RSGL_mat4_loadIdentity() mat4;
+extern fn RSGL_mat4_scale(matrix: [*]f32, x: f32, y: f32, z: f32) mat4;
+extern fn RSGL_mat4_rotate(matrix: [*]f32, angle: f32, x: f32, y: f32, z: f32) mat4;
+extern fn RSGL_mat4_translate(matrix: [*]f32, x: f32, y: f32, z: f32) mat4;
+extern fn RSGL_mat4_perspective(matrix: [*]f32, fovY: f32, aspect: f32, zNear: f32, zFar: f32) mat4;
+extern fn RSGL_mat4_ortho(matrix: [*]f32, left: f32, right: f32, bottom: f32, top: f32, znear: f32, zfar: f32) mat4;
+extern fn RSGL_mat4_lookAt(matrix: [*]f32, eyeX: f32, eyeY: f32, eyeZ: f32, targetX: f32, targetY: f32, targetZ: f32, upX: f32, upY: f32, upZ: f32) mat4;
+extern fn RSGL_mat4_multiply(left: [*]f32, right: [*]f32) mat4;
+extern fn RSGL_mat4_multiplyPoint(matrix: mat4, point: vec3) vec3;
+
+pub const projectionType = enum(c_uint) {
+ ortho2 = raw.RSGL_projectionOrtho2D,
+ ortho3 = raw.RSGL_projectionOrtho3D,
+ perspective3 = raw.RSGL_projectionPerspective3D,
+};
+
+pub const projection2 = extern struct {
+ type: projectionType = zeroes(projectionType),
+ width: u32 = 0,
+ height: u32 = 0,
+};
+
+pub const projection3 = extern struct {
+ type: projectionType = zeroes(projectionType),
+ fov: f32 = 0,
+ ratio: f32 = 0,
+ pNear: f32 = 0,
+ pFar: f32 = 0,
+};
+
+pub const projection = extern union {
+ type: projectionType,
+ p2: projection2,
+ p3: projection3,
+ pub fn matrix(self: *const projection) mat4 {return RSGL_projection_getMatrix(self);}
+};
+extern fn RSGL_projection_getMatrix(projection: [*c]const projection) mat4;
+
+pub const shaderType = enum(c_uint) {
+ none = raw.RSGL_shaderTypeNone,
+ standard = raw.RSGL_shaderTypeStandard,
+ compute = raw.RSGL_shaderTypeCompute,
+ geometry = raw.RSGL_shaderTypeGeometry,
+};
+
+pub const programBlob = extern struct {
+ vertex: ?[*]const u8 = null,
+ vertexLen: usize = 0,
+ fragment: ?[*]const u8 = null,
+ fragmentLen: usize = 0,
+};
+
+pub const programInfo = extern struct {
+ program: usize = 0,
+ perspectiveView: usize = 0,
+ model: usize = 0,
+ vertexPosition: usize = 0,
+ vertexTexCoord: usize = 0,
+ vertexColor: usize = 0,
+ type: shaderType = zeroes(shaderType),
+};
+
+pub const batch = extern struct {
+ start: usize = 0,
+ len: usize = 0,
+ elmStart: usize = 0,
+ elmCount: usize = 0,
+ type: u32 = 0,
+ tex: usize = 0,
+ lineWidth: f32 = 0,
+ matrix: mat4 = zeroes(mat4),
+};
+
+pub const renderData = extern struct {
+ verts: ?[*]f32 = null,
+ texCoords: ?[*]f32 = null,
+ colors: ?[*]f32 = null,
+ elements: ?[*]u16 = null,
+ elements_count: usize = 0,
+ len: usize = 0,
+ perspective: mat4 = zeroes(mat4),
+};
+
+pub const bufferType = enum(@TypeOf(raw.RSGL_bufferType)) {
+ array = raw.RSGL_arrayBuffer,
+ elementArray = raw.RSGL_elementArrayBuffer,
+ shaderStorage = raw.RSGL_shaderStorageBuffer,
+ texture = raw.RSGL_textureBuffer,
+ uniform = raw.RSGL_uniformBuffer,
+};
+
+pub const renderBuffers = extern struct {
+ vertex: usize = 0,
+ color: usize = 0,
+ texture: usize = 0,
+ elements: usize = 0,
+ maxVerts: usize = 0,
+ batches: [MAX_BATCHES]batch = zeroes([MAX_BATCHES]batch),
+ batchCount: usize = 0,
+};
+
+pub const renderState = extern struct {
+ gradient: ?[*]f32 = null,
+ source: rect = zeroes(rect),
+ texture: usize = 0,
+ gradient_len: u32 = 0,
+ color: color = zeroes(color),
+ rotate: vec3 = zeroes(vec3),
+ program: ?*programInfo = null,
+ buffers: ?[*]renderBuffers = null,
+ center: vec3 = zeroes(vec3),
+ lineWidth: f32 = 0,
+ modelMatrix: mat4 = zeroes(mat4),
+ viewMatrix: mat4 = zeroes(mat4),
+ perspectiveMatrix: mat4 = zeroes(mat4),
+ forceBatch: bool = false,
+ overflow: bool = false,
+ framebuffer: usize = 0,
+};
+
+pub const renderPass = extern struct {
+ program: ?*programInfo = null,
+ matrix: ?[*]f32 = null,
+ buffers: ?[*]renderBuffers = null,
+ framebuffer: usize = 0,
+};
+
+pub const rendererProc = extern struct {
+ size: ?*const fn () callconv(.c) usize = null,
+ defaultBlob: ?*const fn (ctx: ?*anyopaque) callconv(.c) programBlob = null,
+ initPtrCallback: ?*const fn (ctx: ?*anyopaque, proc: ?*anyopaque) callconv(.c) void = null,
+ freePtr: ?*const fn (ctx: ?*anyopaque) callconv(.c) void = null,
+ render: ?*const fn (ctx: ?*anyopaque, pass: *const renderPass) callconv(.c) void = null,
+ clear: ?*const fn (ctx: ?*anyopaque, framebuffer: usize, r: f32, g: f32, b: f32, a: f32) callconv(.c) void = null,
+ viewport: ?*const fn (ctx: ?*anyopaque, x: i32, y: i32, w: i32, h: i32) callconv(.c) void = null,
+ setSurface: ?*const fn (ctx: ?*anyopaque, surface: ?*anyopaque) callconv(.c) void = null,
+ createTexture: ?*const fn (ctx: ?*anyopaque, blob: *const textureBlob) callconv(.c) usize = null,
+ copyToTexture: ?*const fn (ctx: ?*anyopaque, texture: usize, x: usize, y: usize, blob: *const textureBlob) callconv(.c) void = null,
+ deleteTexture: ?*const fn (ctx: ?*anyopaque, tex: usize) callconv(.c) void = null,
+ scissorStart: ?*const fn (ctx: ?*anyopaque, x: f32, y: f32, w: f32, h: f32, renderer_height: f32) callconv(.c) void = null,
+ scissorEnd: ?*const fn (ctx: ?*anyopaque) callconv(.c) void = null,
+ createProgram: ?*const fn (ctx: ?*anyopaque, blob: *programBlob) callconv(.c) programInfo = null,
+ deleteProgram: ?*const fn (ctx: ?*anyopaque, program: *const programInfo) callconv(.c) void = null,
+ findShaderVariable: ?*const fn (?*anyopaque, *const programInfo, [*]const u8, usize) callconv(.c) usize = null,
+ updateShaderVariable: ?*const fn (?*anyopaque, *const programInfo, usize, [*]const f32, u8) callconv(.c) void = null,
+ createComputeProgram: ?*const fn (ctx: ?*anyopaque, CShaderCode: [*]const u8) callconv(.c) programInfo = null,
+ dispatchComputeProgram: ?*const fn (ctx: ?*anyopaque, program: *const programInfo, groups_x: u32, groups_y: u32, groups_z: u32) callconv(.c) void = null,
+ bindComputeTexture: ?*const fn (ctx: ?*anyopaque, texture: u32, format: u8) callconv(.c) void = null,
+ createBuffer: ?*const fn (ctx: ?*anyopaque, @"type": bufferType, size: usize, data: ?*const anyopaque, buffer: [*]usize) callconv(.c) void = null,
+ updateBuffer: ?*const fn (ctx: ?*anyopaque, @"type": bufferType, buffer: usize, data: ?*anyopaque, start: usize, len: usize) callconv(.c) void = null,
+ deleteBuffer: ?*const fn (ctx: ?*anyopaque, buffer: usize) callconv(.c) void = null,
+ createFramebuffer: ?*const fn (ctx: ?*anyopaque, width: usize, height: usize) callconv(.c) usize = null,
+ attachFramebuffer: ?*const fn (ctx: ?*anyopaque, fbo: usize, tex: usize, attachType: u8, mipLevel: u8) callconv(.c) void = null,
+ deleteFramebuffer: ?*const fn (ctx: ?*anyopaque, fbo: usize) callconv(.c) void = null,
+ pub fn initPtr(self: rendererProc, loader: ?*anyopaque, ptr: ?*anyopaque, r: *renderer) void {r = RSGL_renderer_initPtr(self, loader, ptr, r);}
+ pub fn init(self: rendererProc, loader: ?*anyopaque) *renderer {return RSGL_renderer_init(self, loader);}
+};
+extern fn RSGL_renderer_initPtr(proc: rendererProc, loader: ?*anyopaque, ptr: ?*anyopaque, renderer: *renderer) void;
+extern fn RSGL_renderer_init(proc: rendererProc, loader: ?*anyopaque) *renderer;
+
+pub const renderer = extern struct {
+ data: renderData = zeroes(renderData),
+ state: renderState = zeroes(renderState),
+ proc: rendererProc =zeroes(rendererProc),
+ userPtr: ?*anyopaque = null,
+ ctx: ?*anyopaque = null,
+ defaultTexture: usize = 0,
+ defaultProgram: programInfo = zeroes(programInfo),
+ defaultPerspectiveMatrix: mat4 = zeroes(mat4),
+ verts: [MAX_VERTS * 3]f32 = zeroes([MAX_VERTS * 3]f32),
+ texCoords: [MAX_VERTS * 2]f32 = zeroes([MAX_VERTS * 2]f32),
+ colors: [MAX_VERTS * 4]f32 = zeroes([MAX_VERTS * 4]f32),
+ elements: [MAX_VERTS * 6]u16 = zeroes([MAX_VERTS * 6]u16),
+ buffers: renderBuffers = zeroes(renderBuffers),
+ pub fn getRenderState(self: *renderer, state: *renderState) void {RSGL_renderer_getRenderState(self, state);}
+ pub fn size(self: *renderer) usize {return RSGL_renderer_size(self);}
+ pub fn updateSize(self: *renderer, width: usize, height: usize) void {RSGL_renderer_updateSize(self, width, height);}
+ pub fn freePtr(self: *renderer) void {RSGL_renderer_freePtr(self);}
+ pub fn setSurface(self: *renderer, surface: ?*anyopaque) void {RSGL_renderer_setSurface(self, surface);}
+ pub fn createBuffer(self: *renderer, @"type": bufferType, sz: usize, data: ?*const anyopaque, buffer: [*]usize) void {RSGL_renderer_createBuffer(self, @"type", sz, data, buffer);}
+ pub fn updateBuffer(self: *renderer, @"type": bufferType, buffer: usize, data: ?*anyopaque, start: usize, len: usize) void {RSGL_renderer_updateBuffer(self, @"type", buffer, data, start, len);}
+ pub fn deleteBuffer(self: *renderer, buffer: usize) void {RSGL_renderer_deleteBuffer(self, buffer);}
+ pub fn createRenderBuffers(self: *renderer, sz: usize, buffers: *renderBuffers) void {RSGL_renderer_createRenderBuffers(self, sz, buffers);}
+ pub fn deleteRenderBuffers(self: *renderer, buffers: *renderBuffers) void {RSGL_renderer_deleteRenderBuffers(self, buffers);}
+ pub fn render(self: *renderer) void {RSGL_renderer_render(self);}
+ pub fn updateRenderBuffers(self: *renderer) void {RSGL_renderer_updateRenderBuffers(self);}
+ pub fn renderBuffers_(self: *renderer) void {RSGL_renderer_renderBuffers(self);}
+ pub fn free(self: *renderer) void {RSGL_renderer_free(self);}
+ pub fn setRotate(self: *renderer, rotate: vec3) void {RSGL_renderer_setRotate(self, rotate);}
+ pub fn setTexture(self: *renderer, texture: usize) void {RSGL_renderer_setTexture(self, texture);}
+ pub fn setTextureSource(self: *renderer, texture: usize, r: rect) void {RSGL_renderer_setTextureSource(self, texture, r);}
+ pub fn setColor(self: *renderer, c: color) void {RSGL_renderer_setColor(self, c);}
+ pub fn setProgram(self: *renderer, program: *programInfo) void {RSGL_renderer_setProgram(self, program);}
+ pub fn setFramebuffer(self: *renderer, framebuffer: usize) void {RSGL_renderer_setFramebuffer(self, framebuffer);}
+ pub fn setRenderBuffers(self: *renderer, buffers: *renderBuffers) void {RSGL_renderer_setRenderBuffers(self, buffers);}
+ pub fn setGradient(self: *renderer, gradient: [*]f32, len: usize) void {RSGL_renderer_setGradient(self, gradient, len);}
+ pub fn setCenter(self: *renderer, center: vec3) void {RSGL_renderer_setCenter(self, center);}
+ pub fn setOverflow(self: *renderer, overflow: bool) void {RSGL_renderer_setOverflow(self, overflow);}
+ pub fn clearArgs(self: *renderer) void {RSGL_renderer_clearArgs(self);}
+ pub fn initDrawMatrix(self: *renderer, center: vec3) mat4 {return RSGL_renderer_initDrawMatrix(self, center);}
+ pub fn clear(self: *renderer, c: color) void {RSGL_renderer_clear(self, c);}
+ pub fn viewport(self: *renderer, r: rect) void {RSGL_renderer_viewport(self, r);}
+ pub fn createTexture(self: *renderer, blob: *const textureBlob) usize {return RSGL_renderer_createTexture(self, blob);}
+ pub fn copyToTexture(self: *renderer, texture: usize, x: usize, y: usize, blob: *const textureBlob) void {RSGL_renderer_copyToTexture(self, texture, x, y, blob);}
+ pub fn deleteTexture(self: *renderer, tex: usize) void {RSGL_renderer_deleteTexture(self, tex);}
+ pub fn createFramebuffer(self: *renderer, width: usize, height: usize) usize {return RSGL_renderer_createFramebuffer(self, width, height);}
+ pub fn attachFramebuffer(self: *renderer, fbo: usize, tex: usize, attachType: u8, mipLevel: u8) void {RSGL_renderer_attachFramebuffer(self, fbo, tex, attachType, mipLevel);}
+ pub fn deleteFramebuffer(self: *renderer, fbo: usize) void {RSGL_renderer_deleteFramebuffer(self, fbo);}
+ pub fn scissorStart(self: *renderer, scissor: rect, height: i32) void {RSGL_renderer_scissorStart(self, scissor, height);}
+ pub fn scissorEnd(self: *renderer) void {RSGL_renderer_scissorEnd(self);}
+ pub fn defaultBlob(self: *renderer) programBlob {return RSGL_renderer_defaultBlob(self);}
+ pub fn createProgram(self: *renderer, blob: *programBlob) programInfo {return RSGL_renderer_createProgram(self, blob);}
+ pub fn deleteProgram(self: *renderer, program: *const programInfo) void {RSGL_renderer_deleteProgram(self, program);}
+ pub fn findShaderVariable(self: *renderer, program: *const programInfo, @"var": *const u8, len: usize) usize {return RSGL_renderer_findShaderVariable(self, program, @"var", len);}
+ pub fn updateShaderVariable(self: *renderer, program: *const programInfo, @"var": usize, value: *const f32, len: u8) void {RSGL_renderer_updateShaderVariable(self, program, @"var", value, len);}
+ pub fn forceBatch(self: *renderer) void {RSGL_renderer_forceBatch(self);}
+ pub fn setPerspectiveMatrix(self: *renderer, matrix: mat4) void {RSGL_renderer_setPerspectiveMatrix(self, matrix);}
+ pub fn setDefaultPerspectiveMatrix(self: *renderer, matrix: mat4) void {RSGL_renderer_setDefaultPerspectiveMatrix(self, matrix);}
+ pub fn setModelMatrix(self: *renderer, matrix: mat4) void {RSGL_renderer_setModelMatrix(self, matrix);}
+ pub fn resetModelMatrix(self: *renderer) void {RSGL_renderer_resetModelMatrix(self);}
+ pub fn createComputeProgram(self: *renderer, CShaderCode: [*]const u8) programInfo {return RSGL_renderer_createComputeProgram(self, CShaderCode);}
+ pub fn dispatchComputeProgram(self: *renderer, program: *const programInfo, groups_x: u32, groups_y: u32, groups_z: u32) void {RSGL_renderer_dispatchComputeProgram(self, program, groups_x, groups_y, groups_z);}
+ pub fn bindComputeTexture(self: *renderer, texture: u32, format: u8) void {RSGL_renderer_bindComputeTexture(self, texture, format);}
+ pub fn drawRawVerts(self: *renderer, data: *const rawVerts) i32 {return RSGL_drawRawVerts(self, data);}
+ pub fn drawPoint(self: *renderer, p: vec2) i32 {return RSGL_drawPoint(self, p);}
+ pub fn drawRect(self: *renderer, r: rect) i32 {return RSGL_drawRect(self, r);}
+ pub fn drawRoundRect(self: *renderer, r: rect, rounding: vec2) i32 {return RSGL_drawRoundRect(self, r, rounding);}
+ pub fn drawPolygon(self: *renderer, r: rect, sides: u32) i32 {return RSGL_drawPolygon(self, r, sides);}
+ pub fn drawArc(self: *renderer, o: rect, arc: vec2) i32 {return RSGL_drawArc(self, o, arc);}
+ pub fn drawOval(self: *renderer, o: rect) i32 {return RSGL_drawOval(self, o);}
+ pub fn drawLine(self: *renderer, p1: vec2, p2: vec2, thickness: u32) i32 {return RSGL_drawLine(self, p1, p2, thickness);}
+ pub fn drawTriangle(self: *renderer, t: *vec3) i32 {return RSGL_drawTriangle(self, t);}
+ pub fn drawPoint3D(self: *renderer, p: vec3) i32 {return RSGL_drawPoint3D(self, p);}
+ pub fn drawLine3D(self: *renderer, p1: vec3, p2: vec3, thickness: u32) i32 {return RSGL_drawLine3D(self, p1, p2, thickness);}
+ pub fn drawCube(self: *renderer, c: cube) i32 {return RSGL_drawCube(self, c);}
+ pub fn drawTriangleOutline(self: *renderer, triangle: *vec3, thickness: u32) i32 {return RSGL_drawTriangleOutline(self, triangle, thickness);}
+ pub fn drawRoundRectOutline(self: *renderer, r: rect, rounding: vec2, thickness: u32) i32 {return RSGL_drawRoundRectOutline(self, r, rounding, thickness);}
+ pub fn drawPolygonOutline(self: *renderer, r: rect, sides: u32, thickness: u32) i32 {return RSGL_drawPolygonOutline(self, r, sides, thickness);}
+ pub fn drawArcOutline(self: *renderer, o: rect, arc: vec2, thickness: u32) i32 {return RSGL_drawArcOutline(self, o, arc, thickness);}
+ pub fn drawOvalOutline(self: *renderer, o: rect, thickness: u32) i32 {return RSGL_drawOvalOutline(self, o, thickness);}
+};
+extern fn RSGL_renderer_getRenderState(renderer: *renderer, state: *renderState) void;
+extern fn RSGL_renderer_size(renderer: *renderer) usize;
+extern fn RSGL_renderer_updateSize(renderer: *renderer, width: usize, height: usize) void;
+extern fn RSGL_renderer_freePtr(renderer: *renderer) void;
+extern fn RSGL_renderer_setSurface(renderer: *renderer, surface: ?*anyopaque) void;
+extern fn RSGL_renderer_createBuffer(renderer: *renderer, @"type": bufferType, size: usize, data: ?*const anyopaque, buffer: [*]usize) void;
+extern fn RSGL_renderer_updateBuffer(renderer: *renderer, @"type": bufferType, buffer: usize, data: ?*anyopaque, start: usize, len: usize) void;
+extern fn RSGL_renderer_deleteBuffer(renderer: *renderer, buffer: usize) void;
+extern fn RSGL_renderer_createRenderBuffers(renderer: *renderer, size: usize, buffers: *renderBuffers) void;
+extern fn RSGL_renderer_deleteRenderBuffers(renderer: *renderer, buffers: *renderBuffers) void;
+extern fn RSGL_renderer_render(renderer: *renderer) void;
+extern fn RSGL_renderer_updateRenderBuffers(renderer: *renderer) void;
+extern fn RSGL_renderer_renderBuffers(renderer: *renderer) void;
+extern fn RSGL_renderer_free(renderer: *renderer) void;
+extern fn RSGL_renderer_setRotate(renderer: *renderer, rotate: vec3) void;
+extern fn RSGL_renderer_setTexture(renderer: *renderer, texture: usize) void;
+extern fn RSGL_renderer_setTextureSource(renderer: *renderer, texture: usize, rect: rect) void;
+extern fn RSGL_renderer_setColor(renderer: *renderer, color: color) void;
+extern fn RSGL_renderer_setProgram(renderer: *renderer, program: *programInfo) void;
+extern fn RSGL_renderer_setFramebuffer(renderer: *renderer, framebuffer: usize) void;
+extern fn RSGL_renderer_setRenderBuffers(renderer: *renderer, buffers: *renderBuffers) void;
+extern fn RSGL_renderer_setGradient(renderer: *renderer, gradient: [*]f32, len: usize) void;
+extern fn RSGL_renderer_setCenter(renderer: *renderer, center: vec3) void;
+extern fn RSGL_renderer_setOverflow(renderer: *renderer, overflow: bool) void;
+extern fn RSGL_renderer_clearArgs(renderer: *renderer) void;
+extern fn RSGL_renderer_initDrawMatrix(renderer: *renderer, center: vec3) mat4;
+extern fn RSGL_renderer_clear(renderer: *renderer, color: color) void;
+extern fn RSGL_renderer_viewport(renderer: *renderer, rect: rect) void;
+extern fn RSGL_renderer_createTexture(renderer: *renderer, blob: *const textureBlob) usize;
+extern fn RSGL_renderer_copyToTexture(renderer: *renderer, texture: usize, x: usize, y: usize, blob: *const textureBlob) void;
+extern fn RSGL_renderer_deleteTexture(renderer: *renderer, tex: usize) void;
+extern fn RSGL_renderer_createFramebuffer(renderer: *renderer, width: usize, height: usize) usize;
+extern fn RSGL_renderer_attachFramebuffer(renderer: *renderer, fbo: usize, tex: usize, attachType: u8, mipLevel: u8) void;
+extern fn RSGL_renderer_deleteFramebuffer(renderer: *renderer, fbo: usize) void;
+extern fn RSGL_renderer_scissorStart(renderer: *renderer, scissor: rect, height: i32) void;
+extern fn RSGL_renderer_scissorEnd(renderer: *renderer) void;
+extern fn RSGL_renderer_defaultBlob(ctx: *renderer) programBlob;
+extern fn RSGL_renderer_createProgram(renderer: *renderer, blob: *programBlob) programInfo;
+extern fn RSGL_renderer_deleteProgram(renderer: *renderer, program: *const programInfo) void;
+extern fn RSGL_renderer_findShaderVariable(renderer: *renderer, program: *const programInfo, @"var": *const u8, len: usize) usize;
+extern fn RSGL_renderer_updateShaderVariable(renderer: *renderer, program: *const programInfo, @"var": usize, value: *const f32, len: u8) void;
+extern fn RSGL_renderer_forceBatch(renderer: *renderer) void;
+extern fn RSGL_renderer_setPerspectiveMatrix(renderer: *renderer, matrix: mat4) void;
+extern fn RSGL_renderer_setDefaultPerspectiveMatrix(renderer: *renderer, matrix: mat4) void;
+extern fn RSGL_renderer_setModelMatrix(renderer: *renderer, matrix: mat4) void;
+extern fn RSGL_renderer_resetModelMatrix(renderer: *renderer) void;
+extern fn RSGL_renderer_createComputeProgram(renderer: *renderer, CShaderCode: [*]const u8) programInfo;
+extern fn RSGL_renderer_dispatchComputeProgram(renderer: *renderer, program: *const programInfo, groups_x: u32, groups_y: u32, groups_z: u32) void;
+extern fn RSGL_renderer_bindComputeTexture(renderer: *renderer, texture: u32, format: u8) void;
+extern fn RSGL_drawRawVerts(renderer: *renderer, data: *const rawVerts) i32;
+extern fn RSGL_drawPoint(renderer: *renderer, p: vec2) i32;
+extern fn RSGL_drawRect(renderer: *renderer, r: rect) i32;
+extern fn RSGL_drawRoundRect(renderer: *renderer, r: rect, rounding: vec2) i32;
+extern fn RSGL_drawPolygon(renderer: *renderer, r: rect, sides: u32) i32;
+extern fn RSGL_drawArc(renderer: *renderer, o: rect, arc: vec2) i32;
+extern fn RSGL_drawOval(renderer: *renderer, o: rect) i32;
+extern fn RSGL_drawLine(renderer: *renderer, p1: vec2, p2: vec2, thickness: u32) i32;
+extern fn RSGL_drawTriangle(renderer: *renderer, *vec3) i32;
+extern fn RSGL_drawPoint3D(renderer: *renderer, p: vec3) i32;
+extern fn RSGL_drawLine3D(renderer: *renderer, p1: vec3, p2: vec3, thickness: u32) i32;
+extern fn RSGL_drawCube(renderer: *renderer, cube: cube) i32;
+extern fn RSGL_drawTriangleOutline(renderer: *renderer, triangle: *vec3, thickness: u32) i32;
+extern fn RSGL_drawRectOutline(renderer: *renderer, r: rect, thickness: u32) i32;
+extern fn RSGL_drawRoundRectOutline(renderer: *renderer, r: rect, rounding: vec2, thickness: u32) i32;
+extern fn RSGL_drawPolygonOutline(renderer: *renderer, r: rect, sides: u32, thickness: u32) i32;
+extern fn RSGL_drawArcOutline(renderer: *renderer, o: rect, arc: vec2, thickness: u32) i32;
+extern fn RSGL_drawOvalOutline(renderer: *renderer, o: rect, thickness: u32) i32;
+
+pub const drawType = enum(c_uint) {
+ triangles = raw.RSGL_TRIANGLES,
+ points = raw.RSGL_POINTS,
+ lines = raw.RSGL_LINES,
+};
+
+pub const rawVerts = extern struct {
+ type: drawType = zeroes(drawType),
+ verts: [*]f32 = null,
+ texCoords: [*]f32 = null,
+ elements: [*]u16 = null,
+ elmCount: usize = 0,
+ vert_count: usize = 0,
+};
+
+pub const viewType = enum(c_uint) {
+ none = raw.RSGL_viewTypeNone,
+ @"2D" = raw.RSGL_viewType2D,
+ @"3D" = raw.RSGL_viewType3D,
+};
+
+pub const view2 = extern struct {
+ type: viewType = zeroes(viewType),
+ offset: vec3 = zeroes(vec3),
+ target: vec3 = zeroes(vec3),
+ rotation: f32 = 0,
+ zoom: f32 = 0,
+};
+
+pub const view3 = extern struct {
+ type: viewType = zeroes(viewType),
+ pos: vec3 = zeroes(vec3),
+ target: vec3 = zeroes(vec3),
+ up: vec3 = zeroes(vec3),
+};
+
+pub const view = extern union {
+ type: viewType,
+ v2: view2,
+ v3: view3,
+ pub fn getMatrix(self: *const view) mat4 {return RSGL_view_getMatrix(self);}
+};
+extern fn RSGL_view_getMatrix(view: *const view) mat4;
+
+pub const gl = struct {
+ pub fn rendererProc() __root.rendererProc {return RSGL_GL_rendererProc();}
+};
+extern fn RSGL_GL_rendererProc() rendererProc; \ No newline at end of file
diff --git a/shell.nix b/shell.nix
new file mode 100644
index 0000000..1d53317
--- /dev/null
+++ b/shell.nix
@@ -0,0 +1,12 @@
+{pkgs ? import <nixpkgs> {}}: pkgs.mkShell {
+ packages = [
+ pkgs.zig
+
+ pkgs.libXi
+ pkgs.libX11
+ pkgs.libXrandr
+ pkgs.libXcursor
+ pkgs.libxkbcommon
+ pkgs.libGL
+ ];
+}