summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--flake.nix23
-rw-r--r--include/RGFW.h15955
-rw-r--r--main.c382
-rw-r--r--project.json5
-rw-r--r--shim.c12
-rw-r--r--tigr.c6610
-rw-r--r--tigr.h366
7 files changed, 16030 insertions, 7023 deletions
diff --git a/flake.nix b/flake.nix
index 8703c5a..ed67867 100644
--- a/flake.nix
+++ b/flake.nix
@@ -8,7 +8,6 @@
};
};
outputs = {
- self,
nixpkgs,
c3c,
...
@@ -24,9 +23,29 @@
src = ./.;
buildInputs = [
pkgs.c3c
- pkgs.libX11
+ pkgs.wayland-scanner
+ ];
+ nativeBuildInputs = [
+ pkgs.libxkbcommon
pkgs.libGL
+ pkgs.wayland
];
+ configurePhase = ''
+ wayland-scanner client-header ${pkgs.wayland-protocols}/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml include/xdg-shell.h
+ wayland-scanner client-header ${pkgs.wayland-protocols}/share/wayland-protocols/staging/pointer-warp/pointer-warp-v1.xml include/pointer-warp-v1.h
+ wayland-scanner client-header ${pkgs.wayland-protocols}/share/wayland-protocols/staging/xdg-toplevel-icon/xdg-toplevel-icon-v1.xml include/xdg-toplevel-icon-v1.h
+ wayland-scanner client-header ${pkgs.wayland-protocols}/share/wayland-protocols/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml include/xdg-decoration-unstable-v1.h
+ wayland-scanner client-header ${pkgs.wayland-protocols}/share/wayland-protocols/unstable/relative-pointer/relative-pointer-unstable-v1.xml include/relative-pointer-unstable-v1.h
+ wayland-scanner client-header ${pkgs.wayland-protocols}/share/wayland-protocols/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml include/pointer-constraints-unstable-v1.h
+ wayland-scanner client-header ${pkgs.wayland-protocols}/share/wayland-protocols/unstable/xdg-output/xdg-output-unstable-v1.xml include/xdg-output-unstable-v1.h
+ wayland-scanner private-code ${pkgs.wayland-protocols}/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml xdg-shell.c
+ wayland-scanner private-code ${pkgs.wayland-protocols}/share/wayland-protocols/staging/pointer-warp/pointer-warp-v1.xml pointer-warp-v1.c
+ wayland-scanner private-code ${pkgs.wayland-protocols}/share/wayland-protocols/staging/xdg-toplevel-icon/xdg-toplevel-icon-v1.xml xdg-toplevel-icon-v1.c
+ wayland-scanner private-code ${pkgs.wayland-protocols}/share/wayland-protocols/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml xdg-decoration-unstable-v1.c
+ wayland-scanner private-code ${pkgs.wayland-protocols}/share/wayland-protocols/unstable/relative-pointer/relative-pointer-unstable-v1.xml relative-pointer-unstable-v1.c
+ wayland-scanner private-code ${pkgs.wayland-protocols}/share/wayland-protocols/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml pointer-constraints-unstable-v1.c
+ wayland-scanner private-code ${pkgs.wayland-protocols}/share/wayland-protocols/unstable/xdg-output/xdg-output-unstable-v1.xml xdg-output-unstable-v1.c
+ '';
buildPhase = ''
c3c build
'';
diff --git a/include/RGFW.h b/include/RGFW.h
new file mode 100644
index 0000000..8016f02
--- /dev/null
+++ b/include/RGFW.h
@@ -0,0 +1,15955 @@
+/*
+*
+* 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_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_NO_XRANDR - do use not XRandr (X11)
+ #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_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
+*/
+
+/*
+Example to get you started :
+
+linux : 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
+ linux:
+ 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 : 4995 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
+
+#ifndef RGFW_ASSERT
+ #include <assert.h>
+ #define RGFW_ASSERT assert
+#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_MAX_MONITORS
+ #define RGFW_MAX_MONITORS 6
+#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 */
+ typedef unsigned char u8;
+ typedef signed char i8;
+ typedef unsigned short u16;
+ typedef signed short i16;
+ typedef unsigned long int u32;
+ typedef signed long int i32;
+ typedef unsigned long long u64;
+ typedef signed long long i64;
+ #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
+
+typedef ptrdiff_t RGFW_ssize_t;
+
+#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
+#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 is pressed */
+ RGFW_mouseMiddle, /*!< mouse-wheel-button is pressed */
+ RGFW_mouseRight, /*!< right mouse button is pressed */
+ 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 */
+};
+
+/*! internal node for a individual data drop */
+typedef struct RGFW_dataDropNode {
+ const char* data; /*!< dropped data */
+ size_t size; /*!< 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_mousePosChanged, /*!< the position of the mouse has been changed */
+ 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 */
+};
+
+/*! @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_mousePosChangedFlag = RGFW_BIT(RGFW_mousePosChanged),
+ 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_keyEventsFlag = RGFW_keyPressedFlag | RGFW_keyReleasedFlag | RGFW_keyCharFlag,
+ RGFW_mouseEventsFlag = RGFW_mouseButtonPressedFlag | RGFW_mouseButtonReleasedFlag | RGFW_mousePosChangedFlag | 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_mousePosChanged) */
+typedef struct RGFW_mousePosEvent {
+ 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_mousePosEvent;
+
+/*! @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_mousePosEvent 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 */
+};
+
+/*! 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
+
+/**!
+ * @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 Retrieves an array of all available monitors.
+ * @param len [OUTPUT] A pointer to store the number of monitors found (maximum of RGFW_MAX_MONITORS [6 by default]).
+ * @return An array of pointers to RGFW_monitor structures.
+*/
+RGFWDEF RGFW_monitor** RGFW_getMonitors(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 datta 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.
+ * @param size [OUTPUT] A pointer that will be filled with the size of the clipboard data.
+ * @return A pointer to the clipboard data as a string.
+*/
+RGFWDEF const char* RGFW_readClipboard(size_t* size);
+
+/**!
+ * @brief Reads clipboard data into a provided buffer, or returns the required length if str is NULL.
+ * @param str [OUTPUT] A pointer to the buffer that will receive the clipboard data (or NULL to get required size).
+ * @param strCapacity The capacity of the provided buffer.
+ * @return The number of bytes read or required length of clipboard data.
+*/
+RGFWDEF RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity);
+
+/**!
+ * @brief Writes text to the clipboard.
+ * @param text The text to be written to the clipboard.
+ * @param textLen The length of the text being written.
+*/
+RGFWDEF void RGFW_writeClipboard(const char* text, u32 textLen);
+/** @} */
+
+
+
+/** * @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
+#include <vulkan/vulkan.h>
+
+/* 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.
+ * @return 0 on success, or a negative error code on failure.
+ * @note This is automatically called when the first window is created.
+*/
+RGFWDEF i32 RGFW_init(void);
+
+/**!
+ * @brief Deinitializes 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, or a negative error code on failure.
+*/
+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>
+
+ #ifndef RGFW_NO_XRANDR
+ #include <X11/extensions/Xrandr.h>
+ #include <X11/Xresource.h>
+ #endif
+
+ #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 dataSize;
+ 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) && !defined(RGFW_NO_XRANDR)
+ 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;
+ RGFW_monitorList freeList;
+ size_t count;
+
+ RGFW_monitorNode* primary;
+ RGFW_monitorNode data[RGFW_MAX_MONITORS];
+} RGFW_monitors;
+
+RGFWDEF RGFW_monitorNode* RGFW_monitors_add(const RGFW_monitor* mon);
+RGFWDEF void RGFW_monitors_remove(RGFW_monitorNode* node, RGFW_monitorNode* prev);
+
+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
+
+ const char* className;
+ RGFW_bool useWaylandBool;
+ RGFW_bool stopCheckEvents_bool ;
+ u64 timerOffset;
+
+ char* clipboard_data;
+ char* clipboard; /* for writing to the clipboard selection */
+ size_t clipboard_len;
+
+ 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
+
+ RGFW_monitors monitors;
+
+ #ifdef RGFW_UNIX
+ int eventWait_forceStop[3];
+ i32 clock;
+ #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 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_mousePosCallback(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_useWayland(RGFW_bool wayland) { RGFW_init(); _RGFW->useWaylandBool = RGFW_BOOL(wayland); }
+RGFW_bool RGFW_usingWayland(void) { return _RGFW->useWaylandBool; }
+
+void RGFW_setRawMouseMode(RGFW_bool state) {
+ _RGFW->rawMouse = state;
+ RGFW_window_setRawMouseModePlatform(_RGFW->root, state);
+}
+
+void RGFW_clipboard_switch(char* newstr);
+void RGFW_clipboard_switch(char* newstr) {
+ if (_RGFW->clipboard_data != NULL)
+ RGFW_FREE(_RGFW->clipboard_data);
+ _RGFW->clipboard_data = newstr;
+}
+
+#define RGFW_CHECK_CLIPBOARD() \
+ if (size <= 0 && _RGFW->clipboard_data != NULL) \
+ return (const char*)_RGFW->clipboard_data; \
+ else if (size <= 0) \
+ return "\0";
+
+const char* RGFW_readClipboard(size_t* len) {
+ RGFW_ssize_t size = RGFW_readClipboardPtr(NULL, 0);
+ RGFW_CHECK_CLIPBOARD();
+ char* str = (char*)RGFW_ALLOC((size_t)size);
+ RGFW_ASSERT(str != NULL);
+ str[0] = '\0';
+
+ size = RGFW_readClipboardPtr(str, (size_t)size);
+
+ RGFW_CHECK_CLIPBOARD();
+
+ if (len != NULL) *len = (size_t)size;
+
+ RGFW_clipboard_switch(str);
+ return (const char*)str;
+}
+
+/* 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.x = 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_mousePosCallback(RGFW_window* win, i32 x, i32 y) {
+ win->internal.lastMouseX = x;
+ win->internal.lastMouseY = y;
+
+ if (!(win->internal.enabledEvents & RGFW_mousePosChangedFlag)) return;
+
+ RGFW_event event;
+ event.type = RGFW_mousePosChanged;
+ 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 size, 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.dataSize = size;
+
+ RGFW_dataDropNode node;
+ RGFW_MEMZERO(&node, sizeof(node));
+ node.data = data;
+ node.size = size;
+ 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(size);
+ RGFW_MEMCPY(dataCopy, data, size);
+ 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 press) {
+ RGFW_event event;
+
+ if (press) {
+ 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 = press;
+
+ event.key.value = key;
+ event.key.repeat = repeat;
+ event.key.mod = mod;
+ event.key.state = press;
+ 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_FORCE_INIT
+RGFW_info _rgfwGlobal;
+#endif
+
+i32 RGFW_init(void) { return RGFW_init_ptr(&_rgfwGlobal); }
+void RGFW_deinit(void) { RGFW_deinit_ptr(&_rgfwGlobal); }
+
+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;
+#ifdef RGFW_WAYLAND
+ _RGFW->useWaylandBool = RGFW_TRUE;
+#endif
+
+ _RGFW->monitors.freeList.head = &_RGFW->monitors.data[0];
+ _RGFW->monitors.freeList.cur = _RGFW->monitors.freeList.head;
+
+ for (size_t i = 1; i < RGFW_MAX_MONITORS; i++) {
+ RGFW_monitorNode* newNode = &_RGFW->monitors.data[i];
+ _RGFW->monitors.freeList.cur->next = newNode;
+ _RGFW->monitors.freeList.cur = _RGFW->monitors.freeList.cur->next;
+ }
+
+ _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);
+
+ RGFW_clipboard_switch(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
+}
+
+void RGFW_setClassName(const char* name) { RGFW_init(); _RGFW->className = name; }
+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; }
+#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->monitors.freeList.head == NULL) return node;
+
+ node = _RGFW->monitors.freeList.head;
+
+ _RGFW->monitors.freeList.head = node->next;
+ if (_RGFW->monitors.freeList.head == NULL) {
+ _RGFW->monitors.freeList.cur = 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;
+
+ /* 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;
+}
+
+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) {
+ static RGFW_monitor* monitors[RGFW_MAX_MONITORS];
+ RGFW_init();
+ if (len != NULL) {
+ *len = _RGFW->monitors.count;
+ }
+
+ u8 i = 0;
+ RGFW_monitorNode* cur_node = _RGFW->monitors.list.head;
+ while (cur_node != NULL) {
+ monitors[i] = &cur_node->mon;
+ i++;
+ cur_node = cur_node->next;
+ }
+ return monitors;
+}
+
+RGFW_monitor* RGFW_getPrimaryMonitor(void) {
+ 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_X11) || defined(RGFW_MACOS) || defined(RGFW_WASM) || defined(RGFW_WAYLAND)
+#ifndef __USE_POSIX199309
+ #define __USE_POSIX199309
+#endif
+#include <time.h>
+struct timespec;
+#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_bool RGFW_extensionSupported_base(const char* extension, size_t len) {
+ #ifdef GL_NUM_EXTENSIONS
+ if (RGFW_globalHints_OpenGL->major >= 3) {
+ i32 i;
+
+ GLint count = 0;
+
+ RGFW_proc RGFW_glGetStringi = RGFW_getProcAddress_OpenGL("glGetStringi");
+ RGFW_proc RGFW_glGetIntegerv = RGFW_getProcAddress_OpenGL("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 = RGFW_getProcAddress_OpenGL("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)) 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)
+ /* Linux and 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->useWaylandBool)
+ _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->useWaylandBool)
+ 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->useWaylandBool == 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->useWaylandBool == 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->useWaylandBool)
+ 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->useWaylandBool == 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)) 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, GetModuleHandle(NULL), (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>
+
+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_linux_getTimeNS(void);
+u64 RGFW_linux_getTimeNS(void) {
+ struct timespec ts;
+ const u64 scale_factor = 1000000000;
+ clock_gettime(_RGFW->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_linux_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_linux_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_linux_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) {
+ #if defined(_POSIX_MONOTONIC_CLOCK)
+ struct timespec ts;
+ RGFW_MEMZERO(&ts, sizeof(struct timespec));
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
+ _RGFW->clock = CLOCK_MONOTONIC;
+ #else
+ _RGFW->clock = CLOCK_REALTIME;
+ #endif
+
+#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->useWaylandBool) {
+ 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 Linux / Unix 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); RGFW_UNUSED(state);
+}
+
+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 */
+ if (_RGFW->className == NULL)
+ _RGFW->className = (char*)name;
+
+ 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->clipboard, (i32)_RGFW->clipboard_len);
+ 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->clipboard, (i32)_RGFW->clipboard_len);
+ }
+ }
+
+ 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;
+ }
+
+#ifndef RGFW_NO_XRANDR
+ if (E.type == _RGFW->xrandrEventBase + RRNotify) {
+ RGFW_pollMonitors();
+ return;
+ }
+#endif
+
+ 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_linux_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_mousePosCallback(win, E.xmotion.x, E.xmotion.y);
+ break;
+
+ case Expose: {
+ RGFW_windowRefreshCallback(win, 0, 0, win->w, win->h);
+
+#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_mousePosCallback(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_linux_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_ssize_t RGFW_FUNC(RGFW_readClipboardPtr)(char* str, size_t strCapacity) {
+ RGFW_init();
+ RGFW_LOAD_ATOM(XSEL_DATA); RGFW_LOAD_ATOM(UTF8_STRING); RGFW_LOAD_ATOM(CLIPBOARD);
+ if (XGetSelectionOwner(_RGFW->display, CLIPBOARD) == _RGFW->helperWindow) {
+ if (str != NULL)
+ RGFW_STRNCPY(str, _RGFW->clipboard, _RGFW->clipboard_len - 1);
+ _RGFW->clipboard[_RGFW->clipboard_len - 1] = '\0';
+ return (RGFW_ssize_t)_RGFW->clipboard_len - 1;
+ }
+
+ XEvent event;
+ int format;
+ unsigned long N, sizeN;
+ 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 -1;
+ break;
+ }
+
+ XGetWindowProperty(event.xselection.display, event.xselection.requestor,
+ event.xselection.property, 0L, (~0L), 0, AnyPropertyType, &target,
+ &format, &sizeN, &N, (u8**) &data);
+
+ RGFW_ssize_t size;
+ if (sizeN > strCapacity && str != NULL)
+ size = -1;
+
+ if ((target == UTF8_STRING || target == XA_STRING) && str != NULL) {
+ RGFW_MEMCPY(str, data, sizeN);
+ str[sizeN] = '\0';
+ XFree(data);
+ } else if (str != NULL) size = -1;
+
+ XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property);
+ size = (RGFW_ssize_t)sizeN;
+
+ return size;
+}
+
+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;
+}
+
+void RGFW_FUNC(RGFW_writeClipboard)(const char* text, u32 textLen) {
+ 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;
+ }
+
+ if (_RGFW->clipboard)
+ RGFW_FREE(_RGFW->clipboard);
+
+ _RGFW->clipboard = (char*)RGFW_ALLOC(textLen);
+ RGFW_ASSERT(_RGFW->clipboard != NULL);
+
+ RGFW_STRNCPY(_RGFW->clipboard, text, textLen - 1);
+ _RGFW->clipboard[textLen - 1] = '\0';
+ _RGFW->clipboard_len = textLen;
+ return;
+}
+
+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;
+
+ #ifndef RGFW_NO_XRANDR
+ 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);
+ #endif
+
+ 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);
+#ifndef RGFW_NO_XRANDR
+ 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;
+#endif
+
+ return 0;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_monitor_setGammaRamp) (RGFW_monitor* monitor, RGFW_gammaRamp* ramp) {
+ RGFW_UNUSED(monitor); RGFW_UNUSED(ramp);
+
+#ifndef RGFW_NO_XRANDR
+ 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;
+#endif
+ return RGFW_FALSE;
+}
+
+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) {
+ #ifndef RGFW_NO_XRANDR
+ 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;
+#endif
+ return RGFW_FALSE;
+}
+
+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->clipboard = 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);
+
+ unsigned char mask[XIMaskLen(XI_RawMotion)];
+ RGFW_MEMZERO(mask, sizeof(mask));
+ 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);
+
+#ifndef RGFW_NO_XRANDR
+ i32 errorBase;
+ if (XRRQueryExtension(_RGFW->display, &_RGFW->xrandrEventBase, &errorBase)) {
+ XRRSelectInput(_RGFW->display, RootWindow(_RGFW->display, DefaultScreen(_RGFW->display)), RROutputChangeNotifyMask);
+ }
+#endif
+
+ 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->clipboard) {
+ RGFW_FREE(_RGFW->clipboard);
+ _RGFW->clipboard = 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 X11 linux / wayland / unix defines
+*/
+
+/*
+
+ Start of Wayland defayland
+*/
+
+#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
+
+/*
+Wayland TODO: (out of date)
+- fix RGFW_keyPressed lock state
+
+ RGFW_windowMoved, the window was moved (by the user)
+ RGFW_windowRefresh The window content needs to be refreshed
+
+ RGFW_dataDrop a file has been dropped into the window
+ RGFW_dataDrag
+
+- window args:
+ #define RGFW_windowNoResize the window cannot be resized by the user
+ #define RGFW_windowAllowDND the window supports drag and drop
+ #define RGFW_scaleToMonitor scale the window to the screen
+
+- other missing functions functions ("TODO wayland") (~30 functions)
+- fix buffer rendering weird behavior
+*/
+#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>
+
+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_mousePosCallback(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-%li", _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
+ write(fd, _RGFW->clipboard, _RGFW->clipboard_len - 1);
+ }
+
+ 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->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;charset=utf-8", pfds[1]);
+ close(pfds[1]);
+
+ wl_display_roundtrip(_RGFW->wl_display);
+
+ char buf[1024];
+
+ ssize_t n = read(pfds[0], buf, sizeof(buf));
+
+ _RGFW->clipboard = (char*)RGFW_ALLOC((size_t)n);
+ RGFW_ASSERT(_RGFW->clipboard != NULL);
+ RGFW_STRNCPY(_RGFW->clipboard, buf, (size_t)n);
+
+ _RGFW->clipboard_len = (size_t)n + 1;
+
+ 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->clipboard) {
+ RGFW_FREE(_RGFW->clipboard);
+ _RGFW->clipboard = 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);
+
+ if (_RGFW->className == NULL)
+ _RGFW->className = (char*)name;
+
+ 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_linux_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 */
+ if (wl_display_dispatch(_RGFW->wl_display) == -1) {
+ return;
+ }
+
+}
+
+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_ssize_t RGFW_FUNC(RGFW_readClipboardPtr) (char* str, size_t strCapacity) {
+
+ RGFW_UNUSED(strCapacity);
+
+ if (str != NULL)
+ RGFW_STRNCPY(str, _RGFW->clipboard, _RGFW->clipboard_len - 1);
+ _RGFW->clipboard[_RGFW->clipboard_len - 1] = '\0';
+ return (RGFW_ssize_t)_RGFW->clipboard_len - 1;
+}
+
+void RGFW_FUNC(RGFW_writeClipboard) (const char* text, u32 textLen) {
+
+ // compositor does not support wl_data_device_manager
+ // clients cannot read rgfw's clipboard
+ if (_RGFW->data_device_manager == NULL) return;
+ // clear the clipboard
+ if (_RGFW->clipboard)
+ RGFW_FREE(_RGFW->clipboard);
+
+ // set the contents
+ _RGFW->clipboard = (char*)RGFW_ALLOC(textLen);
+ RGFW_ASSERT(_RGFW->clipboard != NULL);
+ RGFW_STRNCPY(_RGFW->clipboard, text, textLen - 1);
+ _RGFW->clipboard[textLen - 1] = '\0';
+ _RGFW->clipboard_len = textLen;
+
+ // 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;
+ }
+ wl_data_source_offer(win->src.data_source , "text/plain;charset=utf-8");
+
+ // 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);
+
+}
+
+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) {
+ 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_DISPLAYCHANGE:
+ RGFW_pollMonitors();
+ break;
+ 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: {
+ 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_mousePosCallback(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
+
+ u8 RGFW_blk[] = { 0, 0, 0, 0 };
+ _RGFW->hiddenMouse = RGFW_createMouse(RGFW_blk, 1, 1, RGFW_formatRGBA8);
+ return 1;
+}
+
+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;
+
+ HINSTANCE inh = GetModuleHandleA(NULL);
+
+ #ifndef __cplusplus
+ WNDCLASSW Class = {0}; /*!< Setup the Window class. */
+ #else
+ WNDCLASSW Class = {};
+ #endif
+
+ if (_RGFW->className == NULL)
+ _RGFW->className = (char*)name;
+
+ wchar_t wide_class[256];
+ MultiByteToWideChar(CP_UTF8, 0, _RGFW->className, -1, wide_class, 255);
+
+ Class.lpszClassName = wide_class;
+ Class.hInstance = inh;
+ Class.hCursor = LoadCursor(NULL, IDC_ARROW);
+ Class.lpfnWndProc = WndProcW;
+ Class.cbClsExtra = sizeof(RGFW_window*);
+
+ Class.hIcon = (HICON)LoadImageA(GetModuleHandleW(NULL), "RGFW_ICON", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+ if (Class.hIcon == NULL)
+ Class.hIcon = (HICON)LoadImageA(NULL, (LPCSTR)IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+
+ RegisterClassW(&Class);
+
+ 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(Class.lpszClassName, (wchar_t*)wide_name, window_style, win->x, win->y, win->w, win->h, 0, 0, inh, 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(Class.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, inh, 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->root->src.window, 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);
+
+ #ifndef __cplusplus
+ WINDOWPLACEMENT placement = {0};
+ #else
+ WINDOWPLACEMENT placement = {};
+ #endif
+ GetWindowPlacement(win->src.window, &placement);
+ return placement.showCmd == SW_SHOWMINIMIZED;
+}
+
+RGFW_bool RGFW_window_isMaximized(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+
+ #ifndef __cplusplus
+ WINDOWPLACEMENT placement = {0};
+ #else
+ WINDOWPLACEMENT placement = {};
+ #endif
+ 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);
+ /* get pixels per inch */
+ float dpiX = (float)GetDeviceCaps(hdc, LOGPIXELSX);
+ float dpiY = (float)GetDeviceCaps(hdc, LOGPIXELSX);
+
+ 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);
+
+ 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_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) {
+ /* Open the clipboard */
+ if (OpenClipboard(NULL) == 0)
+ return -1;
+
+ /* Get the clipboard data as a Unicode string */
+ HANDLE hData = GetClipboardData(CF_UNICODETEXT);
+ if (hData == NULL) {
+ CloseClipboard();
+ return -1;
+ }
+
+ wchar_t* wstr = (wchar_t*) GlobalLock(hData);
+
+ RGFW_ssize_t textLen = 0;
+
+ {
+ setlocale(LC_ALL, "en_US.UTF-8");
+
+ textLen = (RGFW_ssize_t)wcstombs(NULL, wstr, 0) + 1;
+ if (str != NULL && (RGFW_ssize_t)strCapacity <= textLen - 1)
+ textLen = 0;
+
+ if (str != NULL && textLen) {
+ if (textLen > 1)
+ wcstombs(str, wstr, (size_t)(textLen));
+
+ str[textLen - 1] = '\0';
+ }
+ }
+
+ /* Release the clipboard data */
+ GlobalUnlock(hData);
+ CloseClipboard();
+
+ return textLen;
+}
+
+void RGFW_writeClipboard(const char* text, u32 textLen) {
+ HANDLE object;
+ WCHAR* buffer;
+
+ object = GlobalAlloc(GMEM_MOVEABLE, (1 + textLen) * sizeof(WCHAR));
+ if (!object)
+ return;
+
+ buffer = (WCHAR*) GlobalLock(object);
+ if (!buffer) {
+ GlobalFree(object);
+ return;
+ }
+
+ MultiByteToWideChar(CP_UTF8, 0, text, -1, buffer, (i32)textLen);
+ GlobalUnlock(object);
+
+ if (!OpenClipboard(_RGFW->root->src.window)) {
+ GlobalFree(object);
+ return;
+ }
+
+ EmptyClipboard();
+ SetClipboardData(CF_UNICODETEXT, object);
+ CloseClipboard();
+}
+
+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 = GetModuleHandle(NULL);
+
+ 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;
+
+ RGFW_windowRefreshCallback(win, 0, 0, win->w, win->h);
+}
+
+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_mousePosCallback(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) {
+ static CGDirectDisplayID displays[RGFW_MAX_MONITORS];
+ u32 count;
+
+ if (CGGetActiveDisplayList(RGFW_MAX_MONITORS, displays, &count) != kCGErrorSuccess) {
+ return;
+ }
+
+ if (count > RGFW_MAX_MONITORS) count = RGFW_MAX_MONITORS;
+
+ 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_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_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) {
+ size_t clip_len;
+ char* clip = (char*)NSPasteboard_stringForType(NSPasteboard_generalPasteboard(), NSPasteboardTypeString, &clip_len);
+ if (clip == NULL) return -1;
+
+ if (str != NULL) {
+ if (strCapacity < clip_len)
+ return 0;
+
+ RGFW_MEMCPY(str, clip, clip_len);
+
+ str[clip_len] = '\0';
+ }
+
+ return (RGFW_ssize_t)clip_len;
+}
+
+void RGFW_writeClipboard(const char* text, u32 textLen) {
+ RGFW_UNUSED(textLen);
+
+ NSPasteboardType array[] = { NSPasteboardTypeString, NULL };
+ NSPasteBoard_declareTypes(NSPasteboard_generalPasteboard(), array, 1, NULL);
+
+ SEL func = sel_registerName("setString:forType:");
+ ((bool (*)(id, SEL, id, id))objc_msgSend)
+ (NSPasteboard_generalPasteboard(), func, NSString_stringWithUTF8String(text), NSString_stringWithUTF8String((const char*)NSPasteboardTypeString));
+}
+
+#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:"), 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:"), metalLayer);
+ layer = 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_mousePosCallback(_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_mousePosCallback(_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_mousePosChangedFlag)) return EM_TRUE;
+
+ size_t i;
+ for (i = 0; i < (size_t)E->numTouches; i++) {
+ RGFW_mousePosCallback(_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_mousePosCallback(_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 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);
+ _free(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);
+ _free(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) {
+ RGFW_resetPrevState();
+ 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);
+}
+
+void RGFW_writeClipboard(const char* text, u32 textLen) {
+ RGFW_UNUSED(textLen);
+ EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, text);
+}
+
+
+RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) {
+ RGFW_UNUSED(str); RGFW_UNUSED(strCapacity);
+ /*
+ placeholder code for later
+ I'm not sure if this is possible do the the async stuff
+ */
+ return 0;
+}
+
+#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;
+ 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.majorVersion = (hints->major == 0) ? 1 : hints->major;
+ attrs.minorVersion = hints->minor;
+
+ attrs.enableExtensionsByDefault = EM_TRUE;
+ attrs.explicitSwapControl = EM_TRUE;
+
+ emscripten_webgl_init_context_attributes(&attrs);
+ win->src.ctx.native->ctx = emscripten_webgl_create_context("#canvas", &attrs);
+ 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_ssize_t (*RGFW_readClipboardPtr_ptr)(char* str, size_t strCapacity);
+typedef void (*RGFW_writeClipboard_ptr)(const char* text, u32 textLen);
+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 loadMouse;
+ RGFW_createMouseStandard_ptr loadMouseStandard;
+ 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.loadMouse(data, w, h, format); }
+RGFW_mouse* RGFW_createMouseStandard(RGFW_mouseIcon icon) { return RGFW_api.loadMouseStandard(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_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { return RGFW_api.readClipboardPtr(str, strCapacity); }
+void RGFW_writeClipboard(const char* text, u32 textLen) { RGFW_api.writeClipboard(text, textLen); }
+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)
+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.loadMouse = RGFW_createMouse_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.loadMouse = RGFW_createMouse_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/main.c3 b/main.c3
index f349205..f9763ef 100644
--- a/main.c3
+++ b/main.c3
@@ -1,57 +1,53 @@
module c3term;
import std::io;
-struct TPixel {
- char r; char g; char b; char a;
-}
-
-struct Tigr {
- CInt w; CInt h;
- CInt cx; CInt cy; CInt cw; CInt ch;
- TPixel* pix;
- void* handle;
- CInt blitMode;
-}
-
-struct TigrGlyph {
- CInt code; CInt x; CInt y; CInt w; CInt h;
-}
+alias RGFW_window = void;
-struct TigrFont {
- Tigr* bitmap;
- CInt numglyphs;
- TigrGlyph* glyphs;
+constdef RGFW_windowFlags : uint
+{
+ RGFW_WINDOW_NO_BORDER = 1 << 0,
+ RGFW_WINDOW_NO_RESIZE = 1 << 1,
+ RGFW_WINDOW_ALLOW_DND = 1 << 2,
+ RGFW_WINDOW_HIDE_MOUSE = 1 << 3,
+ RGFW_WINDOW_FULLSCREEN = 1 << 4,
+ RGFW_WINDOW_TRANSLUCENT = 1 << 5,
+ RGFW_WINDOW_TRANSPARENT = RGFW_WINDOW_TRANSLUCENT,
+ RGFW_WINDOW_CENTER = 1 << 6,
+ RGFW_WINDOW_RAW_MOUSE = 1 << 7,
+ RGFW_WINDOW_SCALE_TO_MONITOR = 1 << 8,
+ RGFW_WINDOW_HIDE = 1 << 9,
+ RGFW_WINDOW_MAXIMIZE = 1 << 10,
+ RGFW_WINDOW_CENTER_CURSOR = 1 << 11,
+ RGFW_WINDOW_FLOATING = 1 << 12,
+ RGFW_WINDOW_FOCUS_ON_SHOW = 1 << 13,
+ RGFW_WINDOW_MINIMIZE = 1 << 14,
+ RGFW_WINDOW_FOCUS = 1 << 15,
+ RGFW_WINDOW_CAPTURE_MOUSE = 1 << 16,
+ RGFW_WINDOW_OPENGL = 1 << 17,
+ RGFW_WINDOW_EGL = 1 << 18,
+ RGFW_NO_DEINIT_ON_CLOSE = 1 << 19,
+ RGFW_WINDOWED_FULLSCREEN = RGFW_WINDOW_NO_BORDER | RGFW_WINDOW_MAXIMIZE,
+ RGFW_WINDOW_CAPTURE_RAW_MOUSE = RGFW_WINDOW_CAPTURE_MOUSE | RGFW_WINDOW_RAW_MOUSE,
}
-extern fn Tigr* tigrWindow(CInt, CInt, ZString, CInt);
-extern fn CInt tigrClosed(Tigr*);
-extern fn void tigrUpdate(Tigr*);
-extern fn void tigrClear(Tigr*, TPixel);
-extern fn void tigrPrint(Tigr*, TigrFont*, CInt, CInt, TPixel, ZString);
-extern TigrFont* tfont;
+extern fn RGFW_window* rgfw_createWindow(ZString, int, int, int, int, RGFW_windowFlags) @cname("RGFW_createWindow");
+extern fn bool rgfw_window_shouldClose(RGFW_window*) @cname("RGFW_window_shouldClose");
+extern fn bool rgfw_window_close(RGFW_window*) @cname("RGFW_window_close");
+extern fn void rgfw_pollEvents() @cname("RGFW_pollEvents");
fn int main(String[] args)
{
- io::printn(*tfont);
io::printn("init");
- Tigr* screen = tigrWindow(320, 240, "title", 0);
- while (!tigrClosed(screen)) {
- TPixel p = {
- .r = 0x80,
- .g = 0x90,
- .b = 0xA0,
- .a = 0x00,
- };
- TPixel w = {
- .r = 0xFF,
- .g = 0xFF,
- .b = 0xFF,
- .a = 0xFF,
- };
- tigrClear(screen, p);
- tigrPrint(screen, tfont, 120, 110, w, "Hello, world.");
- tigrUpdate(screen);
+
+ RGFW_window* window = rgfw_createWindow("window title", 0, 0, 800, 400, RGFW_WINDOW_CENTER | RGFW_WINDOW_NO_RESIZE);
+
+ while (!rgfw_window_shouldClose(window))
+ {
+ rgfw_pollEvents();
}
+
+ rgfw_window_close(window);
+
io::printn("tschuess");
return(0);
}
diff --git a/project.json b/project.json
index 574b9df..2da8fe4 100644
--- a/project.json
+++ b/project.json
@@ -4,8 +4,9 @@
"authors": [ "Andromeda-fp" ],
"version": "0.1.0",
"sources": [ "**" ],
- "linked-libraries": [ "GL", "X11" ],
+ "linked-libraries": [ "wayland-client", "xkbcommon", "wayland-cursor", "wayland-egl", "m"],
"c-sources": [ "**" ],
+ "c-include-dirs": [ "include" ],
"build-dir": "build",
"output": "build",
"targets": {
@@ -14,5 +15,5 @@
},
},
"cpu": "generic",
- "opt": "O0"
+ "opt": "O5"
}
diff --git a/shim.c b/shim.c
new file mode 100644
index 0000000..dcf0717
--- /dev/null
+++ b/shim.c
@@ -0,0 +1,12 @@
+#define RGFW_IMPLEMENTATION
+
+#define RGFW_WAYLAND
+#define RGFW_NO_X11
+
+#define RGFW_OPENGL
+#define RGFW_IMPORT
+
+// same as in C3
+#define RGFW_bool u8
+
+#include "RGFW.h"
diff --git a/tigr.c b/tigr.c
deleted file mode 100644
index 117a5dd..0000000
--- a/tigr.c
+++ /dev/null
@@ -1,6610 +0,0 @@
-//////// Start of inlined file: tigr_amalgamated.c ////////
-
-
-#include "tigr.h"
-
-//////// Start of inlined file: tigr_internal.h ////////
-
-// can't use pragma once here because this file probably will endup in .c
-#ifndef __TIGR_INTERNAL_H__
-#define __TIGR_INTERNAL_H__
-
-#define _CRT_SECURE_NO_WARNINGS NOPE
-
-// Graphics configuration.
-#ifndef TIGR_HEADLESS
-#define TIGR_GAPI_GL
-
-// Calculates the biggest scale that a bitmap can fit into an area at.
-int tigrCalcScale(int bmpW, int bmpH, int areaW, int areaH);
-#endif
-
-// Creates a new bitmap, with extra payload bytes.
-Tigr* tigrBitmap2(int w, int h, int extra);
-
-// Resizes an existing bitmap.
-void tigrResize(Tigr* bmp, int w, int h);
-
-// Calculates a new scale, taking minimum-scale flags into account.
-int tigrEnforceScale(int scale, int flags);
-
-// Calculates the correct position for a bitmap to fit into a window.
-void tigrPosition(Tigr* bmp, int scale, int windowW, int windowH, int out[4]);
-
-// ----------------------------------------------------------
-#ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#endif
-
-#if !defined(TIGR_HEADLESS) && __linux__ && !__ANDROID__
-#include <X11/X.h>
-#include <X11/Xlib.h>
-#endif
-
-#ifdef __APPLE__
-#include "TargetConditionals.h"
-#if TARGET_OS_IPHONE
-#define __IOS__ 1
-#else
-#define __MACOS__ 1
-#endif
-#endif
-
-#ifdef TIGR_GAPI_GL
-#if __MACOS__
-#define GL_SILENCE_DEPRECATION
-#include <OpenGL/gl3.h>
-#endif
-#ifdef _WIN32
-#include <GL/gl.h>
-#endif
-#if __linux__ && !__ANDROID__
-#define GL_GLEXT_PROTOTYPES
-#include <GL/gl.h>
-#include <GL/glx.h>
-#endif
-#if __ANDROID__
-#include <EGL/egl.h>
-#include <GLES3/gl3.h>
-#endif
-#if __IOS__
-#define GLES_SILENCE_DEPRECATION
-#include <OpenGLES/ES3/gl.h>
-#endif
-
-typedef struct {
-#ifdef _WIN32
- HGLRC hglrc;
- HDC dc;
-#endif
-#ifdef __APPLE__
- void* glContext;
-#endif
- GLuint tex[2];
- GLuint vao;
- GLuint program;
- GLuint uniform_projection;
- GLuint uniform_model;
- GLuint uniform_parameters;
- int gl_legacy;
- int gl_user_opengl_rendering;
-} GLStuff;
-#endif
-
-#define MAX_TOUCH_POINTS 10
-
-typedef struct {
- int shown, closed;
-#ifdef TIGR_GAPI_GL
- GLStuff gl;
-#ifdef _WIN32
- wchar_t* wtitle;
- DWORD dwStyle;
- RECT oldPos;
-#endif
-#ifdef __linux__
-#if __ANDROID__
- EGLContext context;
-#else
- Display* dpy;
- Window win;
- GLXContext glc;
- XIC ic;
-#endif // __ANDROID__
-#endif // __linux__
-#endif // TIGR_GAPI_GL
-
- Tigr* widgets;
- int widgetsWanted;
- unsigned char widgetAlpha;
- float widgetsScale;
-
- float p1, p2, p3, p4;
-
- int flags;
- int scale;
- int pos[4];
- int lastChar;
- char keys[256], prev[256];
-#if defined(__ANDROID__)
- char released[256];
-#endif // __ANDROID__
-#if defined(__MACOS__)
- int mouseInView;
- int mouseButtons;
- double frameTime;
-#endif // __MACOS__
-#if defined(__linux__) || defined(__IOS__)
- int mouseButtons;
- int mouseX;
- int mouseY;
-#endif // __linux__ __IOS__
-#if defined(__ANDROID__) || defined(__IOS__)
- int numTouchPoints;
- TigrTouchPoint touchPoints[MAX_TOUCH_POINTS];
-#endif // __ANDROID__ __IOS__
-#if defined(_WIN32) || defined(__linux__) || defined(__MACOS__)
- float scrollDeltaX;
- float scrollDeltaY;
-#endif // _WIN32 __linux__ __MACOS__
-} TigrInternal;
-// ----------------------------------------------------------
-
-TigrInternal* tigrInternal(Tigr* bmp);
-
-void tigrGAPICreate(Tigr* bmp);
-void tigrGAPIDestroy(Tigr* bmp);
-int tigrGAPIBegin(Tigr* bmp);
-int tigrGAPIEnd(Tigr* bmp);
-void tigrGAPIPresent(Tigr* bmp, int w, int h);
-
-#endif
-
-//////// End of inlined file: tigr_internal.h ////////
-
-//////// Start of inlined file: tigr_upscale_gl_vs.h ////////
-
-#ifndef __TIGR_UPSCALE_GL_VS_H__
-#define __TIGR_UPSCALE_GL_VS_H__
-
-//////// Start of inlined file: tigr_glsl_hdr.h ////////
-
-#ifndef __TIGR_GLSL_HDR_H__
-#define __TIGR_GLSL_HDR_H__
-
-#if __ANDROID__ || __IOS__
-#define GLSL_VERSION_HEADER \
- "#version 300 es\n" \
- "precision mediump float;\n"
-#else
-#define GLSL_VERSION_HEADER "#version 330 core\n"
-#endif
-
-#endif // __TIGR_GLSL_HDR_H__
-
-//////// End of inlined file: tigr_glsl_hdr.h ////////
-
-
-// clang-format off
-const char tigr_upscale_gl_vs[] = {
- GLSL_VERSION_HEADER
- "layout (location = 0) in vec2 pos_in;"
- "layout (location = 1) in vec2 uv_in;"
- "out vec2 uv;"
- "uniform mat4 model;"
- "uniform mat4 projection;"
- "void main()"
- "{"
- " uv = uv_in;"
- " gl_Position = projection * model * vec4(pos_in, 0.0, 1.0);"
- "}"
-};
-// clang-format on
-
-const int tigr_upscale_gl_vs_size = (int)sizeof(tigr_upscale_gl_vs) - 1;
-
-#endif
-
-//////// End of inlined file: tigr_upscale_gl_vs.h ////////
-
-//////// Start of inlined file: tigr_upscale_gl_fs.h ////////
-
-#ifndef __TIGR_UPSCALE_GL_FS_H__
-#define __TIGR_UPSCALE_GL_FS_H__
-
-//#include "tigr_glsl_hdr.h"
-
-// clang-format off
-const char tigr_upscale_gl_fs[] = {
- GLSL_VERSION_HEADER
- "in vec2 uv;"
- "out vec4 color;"
- "uniform sampler2D image;"
- "uniform vec4 parameters;"
- "void fxShader(out vec4 color, in vec2 coord);"
- "void main()"
- "{"
- " fxShader(color, uv);"
- "}\n"
-};
-// clang-format on
-
-const int tigr_upscale_gl_fs_size = (int)sizeof(tigr_upscale_gl_fs) - 1;
-
-// clang-format off
-const char tigr_default_fx_gl_fs[] = {
- "void fxShader(out vec4 color, in vec2 uv) {"
- " vec2 tex_size = vec2(textureSize(image, 0));"
- " vec2 uv_blur = mix(floor(uv * tex_size) + 0.5, uv * tex_size, parameters.xy) / tex_size;"
- " vec4 c = texture(image, uv_blur);"
- " c.rgb *= mix(0.5, 1.0 - fract(uv.y * tex_size.y), parameters.z) * 2.0; //scanline\n"
- " c = mix(vec4(0.5), c, parameters.w); //contrast\n"
- " color = c;"
- "}"
-};
-// clang-format on
-
-const int tigr_default_fx_gl_fs_size = (int)sizeof(tigr_default_fx_gl_fs) - 1;
-
-#endif
-
-//////// End of inlined file: tigr_upscale_gl_fs.h ////////
-
-
-//////// Start of inlined file: tigr_bitmaps.c ////////
-
-//#include "tigr_internal.h"
-#include <stdlib.h>
-#include <string.h>
-
-// Expands 0-255 into 0-256
-#define EXPAND(X) ((X) + ((X) > 0))
-
-#define CLIP0(CX, X, X2, W) \
- if (X < CX) { \
- int D = CX - X; \
- W -= D; \
- X2 += D; \
- X += D; \
- }
-#define CLIP1(X, DW, W) \
- if (X + W > DW) \
- W = DW - X;
-#define CLIP() \
- CLIP0(dst->cx, dx, sx, w); \
- CLIP0(dst->cy, dy, sy, h); \
- CLIP0(0, sx, dx, w); \
- CLIP0(0, sy, dy, h); \
- CLIP1(dx, dst->cx + cw, w); \
- CLIP1(dy, dst->cy + ch, h); \
- CLIP1(sx, src->w, w); \
- CLIP1(sy, src->h, h); \
- if (w <= 0 || h <= 0) \
- return
-
-Tigr* tigrBitmap2(int w, int h, int extra) {
- Tigr* tigr = (Tigr*)calloc(1, sizeof(Tigr) + extra);
- tigr->w = w;
- tigr->h = h;
- tigr->cw = -1;
- tigr->ch = -1;
- tigr->pix = (TPixel*)calloc(w * h, sizeof(TPixel));
- tigr->blitMode = TIGR_BLEND_ALPHA;
- return tigr;
-}
-
-Tigr* tigrBitmap(int w, int h) {
- return tigrBitmap2(w, h, 0);
-}
-
-#ifdef TIGR_HEADLESS
-void tigrFree(Tigr* bmp) {
- free(bmp->pix);
- free(bmp);
-}
-#endif // TIGR_HEADLESS
-
-
-void tigrResize(Tigr* bmp, int w, int h) {
- if (bmp->w == w && bmp->h == h) {
- return;
- }
-
- int y, cw, ch;
- TPixel* newpix = (TPixel*)calloc(w * h, sizeof(TPixel));
- cw = (w < bmp->w) ? w : bmp->w;
- ch = (h < bmp->h) ? h : bmp->h;
-
- // Copy any old data across.
- for (y = 0; y < ch; y++)
- memcpy(newpix + y * w, bmp->pix + y * bmp->w, cw * sizeof(TPixel));
-
- free(bmp->pix);
- bmp->pix = newpix;
- bmp->w = w;
- bmp->h = h;
-}
-
-#ifndef TIGR_HEADLESS
-int tigrCalcScale(int bmpW, int bmpH, int areaW, int areaH) {
- if (bmpW == 0 || bmpH == 0) {
- tigrError(0, "Invalid bitmap size, (%dx%d)", bmpW, bmpH);
- }
- // We want it as big as possible in the window, but still
- // maintaining the correct aspect ratio, and always
- // having an integer pixel size.
- int scale = 0;
- for (;;) {
- scale++;
- if (bmpW * scale > areaW || bmpH * scale > areaH) {
- scale--;
- break;
- }
- }
- return (scale > 1) ? scale : 1;
-}
-#endif // TIGR_HEADLESS
-
-int tigrEnforceScale(int scale, int flags) {
- if ((flags & TIGR_4X) && scale < 4)
- scale = 4;
- if ((flags & TIGR_3X) && scale < 3)
- scale = 3;
- if ((flags & TIGR_2X) && scale < 2)
- scale = 2;
- return scale;
-}
-
-void tigrPosition(Tigr* bmp, int scale, int windowW, int windowH, int out[4]) {
- // Center the image on screen at this scale.
- out[0] = (windowW - bmp->w * scale) / 2;
- out[1] = (windowH - bmp->h * scale) / 2;
- out[2] = out[0] + bmp->w * scale;
- out[3] = out[1] + bmp->h * scale;
-}
-
-void tigrClear(Tigr* bmp, TPixel color) {
- int count = bmp->w * bmp->h;
- int n;
- for (n = 0; n < count; n++)
- bmp->pix[n] = color;
-}
-
-void tigrFill(Tigr* bmp, int x, int y, int w, int h, TPixel color) {
- TPixel* td;
- int dt, i;
-
- if (x < 0) {
- w += x;
- x = 0;
- }
- if (y < 0) {
- h += y;
- y = 0;
- }
- if (x + w > bmp->w) {
- w = bmp->w - x;
- }
- if (y + h > bmp->h) {
- h = bmp->h - y;
- }
- if (w <= 0 || h <= 0)
- return;
-
- td = &bmp->pix[y * bmp->w + x];
- dt = bmp->w;
- do {
- for (i = 0; i < w; i++)
- td[i] = color;
- td += dt;
- } while (--h);
-}
-
-void tigrLine(Tigr* bmp, int x0, int y0, int x1, int y1, TPixel color) {
- int sx, sy, dx, dy, err, e2;
- dx = abs(x1 - x0);
- dy = abs(y1 - y0);
- if (x0 < x1)
- sx = 1;
- else
- sx = -1;
- if (y0 < y1)
- sy = 1;
- else
- sy = -1;
- err = dx - dy;
-
- do {
- tigrPlot(bmp, x0, y0, color);
- e2 = 2 * err;
- if (e2 > -dy) {
- err -= dy;
- x0 += sx;
- }
- if (e2 < dx) {
- err += dx;
- y0 += sy;
- }
- } while (x0 != x1 || y0 != y1);
-}
-
-void tigrFillRect(Tigr* bmp, int x, int y, int w, int h, TPixel color) {
- x += 1;
- y += 1;
- w -= 2;
- h -= 2;
-
- int cx = bmp->cx;
- int cy = bmp->cy;
- int cw = bmp->cw >= 0 ? bmp->cw : bmp->w;
- int ch = bmp->ch >= 0 ? bmp->ch : bmp->h;
-
- if (x < cx) {
- w += (x - cx);
- x = cx;
- }
- if (y < cy) {
- h += (y - cy);
- y = cy;
- }
- if (x + w > cx + cw) {
- w -= (x + w) - (cx + cw);
- }
- if (y + h > cy + ch) {
- h -= (y + h) - (cy + ch);
- }
- if (w <= 0 || h <= 0)
- return;
-
- TPixel* td = &bmp->pix[y * bmp->w + x];
- int dt = bmp->w;
- int xa = EXPAND(color.a);
- int a = xa * xa;
-
- do {
- for (int i = 0; i < w; i++) {
- td[i].r += (unsigned char)((color.r - td[i].r) * a >> 16);
- td[i].g += (unsigned char)((color.g - td[i].g) * a >> 16);
- td[i].b += (unsigned char)((color.b - td[i].b) * a >> 16);
- td[i].a += (bmp->blitMode) * (unsigned char)((color.a - td[i].a) * a >> 16);
- }
- td += dt;
- } while (--h);
-}
-
-void tigrRect(Tigr* bmp, int x, int y, int w, int h, TPixel color) {
- int x1, y1;
- if (w <= 0 || h <= 0) {
- return;
- }
-
- if (w == 1) {
- tigrLine(bmp, x, y, x, y + h, color);
- } else if (h == 1) {
- tigrLine(bmp, x, y, x + w, y, color);
- } else {
- x1 = x + w - 1;
- y1 = y + h - 1;
- tigrLine(bmp, x, y, x1, y, color);
- tigrLine(bmp, x1, y, x1, y1, color);
- tigrLine(bmp, x1, y1, x, y1, color);
- tigrLine(bmp, x, y1, x, y, color);
- }
-}
-
-void tigrFillCircle(Tigr* bmp, int x0, int y0, int r, TPixel color) {
- if (r <= 0) {
- return;
- }
-
- int E = 1 - r;
- int dx = 0;
- int dy = -2 * r;
- int x = 0;
- int y = r;
-
- tigrLine(bmp, x0 - r + 1, y0, x0 + r, y0, color);
-
- while (x < y - 1) {
- x++;
-
- if (E >= 0) {
- y--;
- dy += 2;
- E += dy;
- tigrLine(bmp, x0 - x + 1, y0 + y, x0 + x, y0 + y, color);
- tigrLine(bmp, x0 - x + 1, y0 - y, x0 + x, y0 - y, color);
- }
-
- dx += 2;
- E += dx + 1;
-
- if (x != y) {
- tigrLine(bmp, x0 - y + 1, y0 + x, x0 + y, y0 + x, color);
- tigrLine(bmp, x0 - y + 1, y0 - x, x0 + y, y0 - x, color);
- }
- }
-}
-
-void tigrCircle(Tigr* bmp, int x0, int y0, int r, TPixel color) {
- int E = 1 - r;
- int dx = 0;
- int dy = -2 * r;
- int x = 0;
- int y = r;
-
- tigrPlot(bmp, x0, y0 + r, color);
- tigrPlot(bmp, x0, y0 - r, color);
- tigrPlot(bmp, x0 + r, y0, color);
- tigrPlot(bmp, x0 - r, y0, color);
-
- while (x < y - 1) {
- x++;
-
- if (E >= 0) {
- y--;
- dy += 2;
- E += dy;
- }
-
- dx += 2;
- E += dx + 1;
-
- tigrPlot(bmp, x0 + x, y0 + y, color);
- tigrPlot(bmp, x0 - x, y0 + y, color);
- tigrPlot(bmp, x0 + x, y0 - y, color);
- tigrPlot(bmp, x0 - x, y0 - y, color);
-
- if (x != y) {
- tigrPlot(bmp, x0 + y, y0 + x, color);
- tigrPlot(bmp, x0 - y, y0 + x, color);
- tigrPlot(bmp, x0 + y, y0 - x, color);
- tigrPlot(bmp, x0 - y, y0 - x, color);
- }
- }
-}
-
-TPixel tigrGet(Tigr* bmp, int x, int y) {
- TPixel empty = { 0, 0, 0, 0 };
- if (x >= 0 && y >= 0 && x < bmp->w && y < bmp->h)
- return bmp->pix[y * bmp->w + x];
- return empty;
-}
-
-void tigrPlot(Tigr* bmp, int x, int y, TPixel pix) {
- int xa, i, a;
-
- int cx = bmp->cx;
- int cy = bmp->cy;
- int cw = bmp->cw >= 0 ? bmp->cw : bmp->w;
- int ch = bmp->ch >= 0 ? bmp->ch : bmp->h;
-
- if (x >= cx && y >= cy && x < cx + cw && y < cy + ch) {
- xa = EXPAND(pix.a);
- a = xa * xa;
- i = y * bmp->w + x;
-
- bmp->pix[i].r += (unsigned char)((pix.r - bmp->pix[i].r) * a >> 16);
- bmp->pix[i].g += (unsigned char)((pix.g - bmp->pix[i].g) * a >> 16);
- bmp->pix[i].b += (unsigned char)((pix.b - bmp->pix[i].b) * a >> 16);
- bmp->pix[i].a += (bmp->blitMode) * (unsigned char)((pix.a - bmp->pix[i].a) * a >> 16);
- }
-}
-
-void tigrClip(Tigr* bmp, int cx, int cy, int cw, int ch) {
- bmp->cx = cx;
- bmp->cy = cy;
- bmp->cw = cw;
- bmp->ch = ch;
-}
-
-void tigrBlit(Tigr* dst, Tigr* src, int dx, int dy, int sx, int sy, int w, int h) {
- int cw = dst->cw >= 0 ? dst->cw : dst->w;
- int ch = dst->ch >= 0 ? dst->ch : dst->h;
-
- CLIP();
-
- TPixel* ts = &src->pix[sy * src->w + sx];
- TPixel* td = &dst->pix[dy * dst->w + dx];
- int st = src->w;
- int dt = dst->w;
- do {
- memcpy(td, ts, w * sizeof(TPixel));
- ts += st;
- td += dt;
- } while (--h);
-}
-
-void tigrBlitTint(Tigr* dst, Tigr* src, int dx, int dy, int sx, int sy, int w, int h, TPixel tint) {
- int cw = dst->cw >= 0 ? dst->cw : dst->w;
- int ch = dst->ch >= 0 ? dst->ch : dst->h;
-
- CLIP();
-
- int xr = EXPAND(tint.r);
- int xg = EXPAND(tint.g);
- int xb = EXPAND(tint.b);
- int xa = EXPAND(tint.a);
-
- TPixel* ts = &src->pix[sy * src->w + sx];
- TPixel* td = &dst->pix[dy * dst->w + dx];
- int st = src->w;
- int dt = dst->w;
- do {
- for (int x = 0; x < w; x++) {
- unsigned r = (xr * ts[x].r) >> 8;
- unsigned g = (xg * ts[x].g) >> 8;
- unsigned b = (xb * ts[x].b) >> 8;
- unsigned a = xa * EXPAND(ts[x].a);
- td[x].r += (unsigned char)((r - td[x].r) * a >> 16);
- td[x].g += (unsigned char)((g - td[x].g) * a >> 16);
- td[x].b += (unsigned char)((b - td[x].b) * a >> 16);
- td[x].a += (dst->blitMode) * (unsigned char)((ts[x].a - td[x].a) * a >> 16);
- }
- ts += st;
- td += dt;
- } while (--h);
-}
-
-void tigrBlitAlpha(Tigr* dst, Tigr* src, int dx, int dy, int sx, int sy, int w, int h, float alpha) {
- alpha = (alpha < 0) ? 0 : (alpha > 1 ? 1 : alpha);
- tigrBlitTint(dst, src, dx, dy, sx, sy, w, h, tigrRGBA(0xff, 0xff, 0xff, (unsigned char)(alpha * 255)));
-}
-
-void tigrBlitMode(Tigr* dst, int mode) {
- dst->blitMode = mode;
-}
-
-#undef CLIP0
-#undef CLIP1
-#undef CLIP
-
-//////// End of inlined file: tigr_bitmaps.c ////////
-
-//////// Start of inlined file: tigr_loadpng.c ////////
-
-//#include "tigr_internal.h"
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-typedef struct {
- const unsigned char *p, *end;
-} PNG;
-
-static unsigned get32(const unsigned char* v) {
- return (v[0] << 24) | (v[1] << 16) | (v[2] << 8) | v[3];
-}
-
-static const unsigned char* find(PNG* png, const char* chunk, unsigned minlen) {
- const unsigned char* start;
- while (png->p < png->end) {
- unsigned len = get32(png->p + 0);
- start = png->p;
- png->p += len + 12;
- if (memcmp(start + 4, chunk, 4) == 0 && len >= minlen && png->p <= png->end)
- return start + 8;
- }
-
- return NULL;
-}
-
-static unsigned char paeth(unsigned char a, unsigned char b, unsigned char c) {
- int p = a + b - c;
- int pa = abs(p - a), pb = abs(p - b), pc = abs(p - c);
- return (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c;
-}
-
-static int rowBytes(int w, int bipp) {
- int rowBits = w * bipp;
- return rowBits / 8 + ((rowBits % 8) ? 1 : 0);
-}
-
-static int unfilter(int w, int h, int bipp, unsigned char* raw) {
- int len = rowBytes(w, bipp);
- int bpp = rowBytes(1, bipp);
- int x, y;
- unsigned char* first = (unsigned char*)malloc(len + 1);
- memset(first, 0, len + 1);
- unsigned char* prev = first;
- for (y = 0; y < h; y++, prev = raw, raw += len) {
-#define LOOP(A, B) \
- for (x = 0; x < bpp; x++) \
- raw[x] += A; \
- for (; x < len; x++) \
- raw[x] += B; \
- break
- switch (*raw++) {
- case 0:
- break;
- case 1:
- LOOP(0, raw[x - bpp]);
- case 2:
- LOOP(prev[x], prev[x]);
- case 3:
- LOOP(prev[x] / 2, (raw[x - bpp] + prev[x]) / 2);
- case 4:
- LOOP(prev[x], paeth(raw[x - bpp], prev[x], prev[x - bpp]));
- default:
- return 0;
- }
-#undef LOOP
- }
- free(first);
- return 1;
-}
-
-static void convert(int bypp, int w, int h, const unsigned char* src, TPixel* dest, const unsigned char* trns) {
- int x, y;
- for (y = 0; y < h; y++) {
- src++; // skip filter byte
- for (x = 0; x < w; x++, src += bypp) {
- switch (bypp) {
- case 1: {
- unsigned char c = src[0];
- if (trns && c == *trns) {
- *dest++ = tigrRGBA(c, c, c, 0);
- break;
- } else {
- *dest++ = tigrRGB(c, c, c);
- break;
- }
- }
- case 2:
- *dest++ = tigrRGBA(src[0], src[0], src[0], src[1]);
- break;
- case 3: {
- unsigned char r = src[0];
- unsigned char g = src[1];
- unsigned char b = src[2];
- if (trns && trns[1] == r && trns[3] == g && trns[5] == b) {
- *dest++ = tigrRGBA(r, g, b, 0);
- break;
- } else {
- *dest++ = tigrRGB(r, g, b);
- break;
- }
- }
- case 4:
- *dest++ = tigrRGBA(src[0], src[1], src[2], src[3]);
- break;
- }
- }
- }
-}
-
-static void depalette(int w,
- int h,
- unsigned char* src,
- TPixel* dest,
- int bipp,
- const unsigned char* plte,
- const unsigned char* trns,
- int trnsSize) {
- int x, y, c;
- unsigned char alpha;
- int mask = 0, len = 0;
-
- switch (bipp) {
- case 4:
- mask = 15;
- len = 1;
- break;
- case 2:
- mask = 3;
- len = 3;
- break;
- case 1:
- mask = 1;
- len = 7;
- }
-
- for (y = 0; y < h; y++) {
- src++; // skip filter byte
- for (x = 0; x < w; x++) {
- if (bipp == 8) {
- c = *src++;
- } else {
- int pos = x & len;
- c = (src[0] >> ((len - pos) * bipp)) & mask;
- if (pos == len) {
- src++;
- }
- }
- alpha = 255;
- if (c < trnsSize) {
- alpha = trns[c];
- }
- *dest++ = tigrRGBA(plte[c * 3 + 0], plte[c * 3 + 1], plte[c * 3 + 2], alpha);
- }
- }
-}
-
-#define FAIL() \
- { \
- errno = EINVAL; \
- goto err; \
- }
-#define CHECK(X) \
- if (!(X)) \
- FAIL()
-
-static int outsize(Tigr* bmp, int bipp) {
- return (rowBytes(bmp->w, bipp) + 1) * bmp->h;
-}
-
-static Tigr* tigrLoadPng(PNG* png) {
- const unsigned char *ihdr, *idat, *plte, *trns, *first;
- int trnsSize = 0;
- int depth, ctype, bipp;
- int datalen = 0;
- unsigned char *data = NULL, *out;
- Tigr* bmp = NULL;
-
- CHECK(memcmp(png->p, "\211PNG\r\n\032\n", 8) == 0); // PNG signature
- png->p += 8;
- first = png->p;
-
- // Read IHDR
- ihdr = find(png, "IHDR", 13);
- CHECK(ihdr);
- depth = ihdr[8];
- ctype = ihdr[9];
- switch (ctype) {
- case 0:
- bipp = depth;
- break; // greyscale
- case 2:
- bipp = 3 * depth;
- break; // RGB
- case 3:
- bipp = depth;
- break; // paletted
- case 4:
- bipp = 2 * depth;
- break; // grey+alpha
- case 6:
- bipp = 4 * depth;
- break; // RGBA
- default:
- FAIL();
- }
-
- // Allocate bitmap (+1 width to save room for stupid PNG filter bytes)
- bmp = tigrBitmap(get32(ihdr + 0) + 1, get32(ihdr + 4));
- CHECK(bmp);
- bmp->w--;
-
- // We support 8-bit color components and 1, 2, 4 and 8 bit palette formats.
- // No interlacing, or wacky filter types.
- CHECK((depth != 16) && ihdr[10] == 0 && ihdr[11] == 0 && ihdr[12] == 0);
-
- // Join IDAT chunks.
- for (idat = find(png, "IDAT", 0); idat; idat = find(png, "IDAT", 0)) {
- unsigned len = get32(idat - 8);
- data = (unsigned char*)realloc(data, datalen + len);
- if (!data)
- break;
-
- memcpy(data + datalen, idat, len);
- datalen += len;
- }
-
- // Find palette.
- png->p = first;
- plte = find(png, "PLTE", 0);
-
- // Find transparency info.
- png->p = first;
- trns = find(png, "tRNS", 0);
- if (trns) {
- trnsSize = get32(trns - 8);
- }
-
- CHECK(data && datalen >= 6);
- CHECK((data[0] & 0x0f) == 0x08 // compression method (RFC 1950)
- && (data[0] & 0xf0) <= 0x70 // window size
- && (data[1] & 0x20) == 0); // preset dictionary present
-
- out = (unsigned char*)bmp->pix + outsize(bmp, 32) - outsize(bmp, bipp);
- CHECK(tigrInflate(out, outsize(bmp, bipp), data + 2, datalen - 6));
- CHECK(unfilter(bmp->w, bmp->h, bipp, out));
-
- if (ctype == 3) {
- CHECK(plte);
- depalette(bmp->w, bmp->h, out, bmp->pix, bipp, plte, trns, trnsSize);
- } else {
- CHECK(bipp % 8 == 0);
- convert(bipp / 8, bmp->w, bmp->h, out, bmp->pix, trns);
- }
-
- free(data);
- return bmp;
-
-err:
- if (data)
- free(data);
- if (bmp)
- tigrFree(bmp);
- return NULL;
-}
-
-#undef CHECK
-#undef FAIL
-
-Tigr* tigrLoadImageMem(const void* data, int length) {
- PNG png;
- png.p = (unsigned char*)data;
- png.end = (unsigned char*)data + length;
- return tigrLoadPng(&png);
-}
-
-Tigr* tigrLoadImage(const char* fileName) {
- int len;
- void* data;
- Tigr* bmp;
-
- data = tigrReadFile(fileName, &len);
- if (!data)
- return NULL;
-
- bmp = tigrLoadImageMem(data, len);
- free(data);
- return bmp;
-}
-
-//////// End of inlined file: tigr_loadpng.c ////////
-
-//////// Start of inlined file: tigr_savepng.c ////////
-
-//#include "tigr_internal.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-typedef struct {
- unsigned crc, adler, bits, prev, runlen;
- FILE* out;
- unsigned crcTable[256];
-} Save;
-
-static const unsigned crctable[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4,
- 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
- 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c };
-
-static void put(Save* s, unsigned v) {
- fputc(v, s->out);
- s->crc = (s->crc >> 4) ^ crctable[(s->crc & 15) ^ (v & 15)];
- s->crc = (s->crc >> 4) ^ crctable[(s->crc & 15) ^ (v >> 4)];
-}
-
-static void updateAdler(Save* s, unsigned v) {
- unsigned s1 = s->adler & 0xffff, s2 = (s->adler >> 16) & 0xffff;
- s1 = (s1 + v) % 65521;
- s2 = (s2 + s1) % 65521;
- s->adler = (s2 << 16) + s1;
-}
-
-static void put32(Save* s, unsigned v) {
- put(s, (v >> 24) & 0xff);
- put(s, (v >> 16) & 0xff);
- put(s, (v >> 8) & 0xff);
- put(s, v & 0xff);
-}
-
-void putbits(Save* s, unsigned data, unsigned bitcount) {
- while (bitcount--) {
- unsigned prev = s->bits;
- s->bits = (s->bits >> 1) | ((data & 1) << 7);
- data >>= 1;
- if (prev & 1) {
- put(s, s->bits);
- s->bits = 0x80;
- }
- }
-}
-
-void putbitsr(Save* s, unsigned data, unsigned bitcount) {
- while (bitcount--)
- putbits(s, data >> bitcount, 1);
-}
-
-static void begin(Save* s, const char* id, unsigned len) {
- put32(s, len);
- s->crc = 0xffffffff;
- put(s, id[0]);
- put(s, id[1]);
- put(s, id[2]);
- put(s, id[3]);
-}
-
-static void literal(Save* s, unsigned v) {
- // Encode a literal/length using the built-in tables.
- // Could do better with a custom table but whatever.
- if (v < 144)
- putbitsr(s, 0x030 + v - 0, 8);
- else if (v < 256)
- putbitsr(s, 0x190 + v - 144, 9);
- else if (v < 280)
- putbitsr(s, 0x000 + v - 256, 7);
- else
- putbitsr(s, 0x0c0 + v - 280, 8);
-}
-
-static void encodelen(Save* s, unsigned code, unsigned bits, unsigned len) {
- literal(s, code + (len >> bits));
- putbits(s, len, bits);
- putbits(s, 0, 5);
-}
-
-static void endrun(Save* s) {
- s->runlen--;
- literal(s, s->prev);
-
- if (s->runlen >= 67)
- encodelen(s, 277, 4, s->runlen - 67);
- else if (s->runlen >= 35)
- encodelen(s, 273, 3, s->runlen - 35);
- else if (s->runlen >= 19)
- encodelen(s, 269, 2, s->runlen - 19);
- else if (s->runlen >= 11)
- encodelen(s, 265, 1, s->runlen - 11);
- else if (s->runlen >= 3)
- encodelen(s, 257, 0, s->runlen - 3);
- else
- while (s->runlen--)
- literal(s, s->prev);
-}
-
-static void encodeByte(Save* s, unsigned char v) {
- updateAdler(s, v);
-
- // Simple RLE compression. We could do better by doing a search
- // to find matches, but this works pretty well TBH.
- if (s->prev == v && s->runlen < 115) {
- s->runlen++;
- } else {
- if (s->runlen)
- endrun(s);
-
- s->prev = v;
- s->runlen = 1;
- }
-}
-
-static void savePngHeader(Save* s, Tigr* bmp) {
- fwrite("\211PNG\r\n\032\n", 8, 1, s->out);
- begin(s, "IHDR", 13);
- put32(s, bmp->w);
- put32(s, bmp->h);
- put(s, 8); // bit depth
- put(s, 6); // RGBA
- put(s, 0); // compression (deflate)
- put(s, 0); // filter (standard)
- put(s, 0); // interlace off
- put32(s, ~s->crc);
-}
-
-static long savePngData(Save* s, Tigr* bmp, long dataPos) {
- int x, y;
- long dataSize;
- begin(s, "IDAT", 0);
- put(s, 0x08); // zlib compression method
- put(s, 0x1d); // zlib compression flags
- putbits(s, 3, 3); // zlib last block + fixed dictionary
- for (y = 0; y < bmp->h; y++) {
- TPixel* row = &bmp->pix[y * bmp->w];
- TPixel prev = tigrRGBA(0, 0, 0, 0);
-
- encodeByte(s, 1); // sub filter
- for (x = 0; x < bmp->w; x++) {
- encodeByte(s, row[x].r - prev.r);
- encodeByte(s, row[x].g - prev.g);
- encodeByte(s, row[x].b - prev.b);
- encodeByte(s, row[x].a - prev.a);
- prev = row[x];
- }
- }
- endrun(s);
- literal(s, 256); // terminator
- while (s->bits != 0x80)
- putbits(s, 0, 1);
- put32(s, s->adler);
- dataSize = (ftell(s->out) - dataPos) - 8;
- put32(s, ~s->crc);
- return dataSize;
-}
-
-int tigrSaveImage(const char* fileName, Tigr* bmp) {
- Save s;
- long dataPos, dataSize, err;
-
- // TODO - unicode?
- FILE* out = fopen(fileName, "wb");
- if (!out)
- return 1;
-
- s.out = out;
- s.adler = 1;
- s.bits = 0x80;
- s.prev = 0xffff;
- s.runlen = 0;
-
- savePngHeader(&s, bmp);
- dataPos = ftell(s.out);
- dataSize = savePngData(&s, bmp, dataPos);
-
- // End chunk.
- begin(&s, "IEND", 0);
- put32(&s, ~s.crc);
-
- // Write back payload size.
- fseek(out, dataPos, SEEK_SET);
- put32(&s, dataSize);
-
- err = ferror(out);
- fclose(out);
- return !err;
-}
-
-//////// End of inlined file: tigr_savepng.c ////////
-
-//////// Start of inlined file: tigr_inflate.c ////////
-
-//#include "tigr_internal.h"
-#include <stdlib.h>
-#include <setjmp.h>
-
-typedef struct {
- unsigned bits, count;
- const unsigned char *in, *inend;
- unsigned char *out, *outend;
- jmp_buf jmp;
- unsigned litcodes[288], distcodes[32], lencodes[19];
- int tlit, tdist, tlen;
-} State;
-
-#define FAIL() longjmp(s->jmp, 1)
-#define CHECK(X) \
- if (!(X)) \
- FAIL()
-
-// Built-in DEFLATE standard tables.
-static char order[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
-static char lenBits[29 + 2] = { 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 int lenBase[29 + 2] = { 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 char distBits[30 + 2] = { 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, 0, 0 };
-static int distBase[30 + 2] = {
- 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
-};
-
-// Table to bit-reverse a byte.
-static const unsigned char reverseTable[256] = {
-#define R2(n) n, n + 128, n + 64, n + 192
-#define R4(n) R2(n), R2(n + 32), R2(n + 16), R2(n + 48)
-#define R6(n) R4(n), R4(n + 8), R4(n + 4), R4(n + 12)
- R6(0), R6(2), R6(1), R6(3)
-};
-
-static unsigned rev16(unsigned n) {
- return (reverseTable[n & 0xff] << 8) | reverseTable[(n >> 8) & 0xff];
-}
-
-static int bits(State* s, int n) {
- int v = s->bits & ((1 << n) - 1);
- s->bits >>= n;
- s->count -= n;
- while (s->count < 16) {
- CHECK(s->in != s->inend);
- s->bits |= (*s->in++) << s->count;
- s->count += 8;
- }
- return v;
-}
-
-static unsigned char* emit(State* s, int len) {
- s->out += len;
- CHECK(s->out <= s->outend);
- return s->out - len;
-}
-
-static void copy(State* s, const unsigned char* src, int len) {
- unsigned char* dest = emit(s, len);
- while (len--)
- *dest++ = *src++;
-}
-
-static int build(State* s, unsigned* tree, unsigned char* lens, unsigned int symcount) {
- unsigned int codes[16], first[16], counts[16] = { 0 };
-
- // Frequency count.
- for (unsigned int n = 0; n < symcount; n++)
- counts[lens[n]]++;
-
- // Distribute codes.
- counts[0] = codes[0] = first[0] = 0;
- for (unsigned int n = 1; n <= 15; n++) {
- codes[n] = (codes[n - 1] + counts[n - 1]) << 1;
- first[n] = first[n - 1] + counts[n - 1];
- }
- CHECK(first[15] + counts[15] <= symcount);
-
- // Insert keys into the tree for each symbol.
- for (unsigned int n = 0; n < symcount; n++) {
- int len = lens[n];
- if (len != 0) {
- unsigned code = codes[len]++, slot = first[len]++;
- tree[slot] = (code << (32 - len)) | (n << 4) | len;
- }
- }
-
- return first[15];
-}
-
-static int decode(State* s, unsigned tree[], int max) {
- // Find the next prefix code.
- unsigned lo = 0, hi = max, key;
- unsigned search = (rev16(s->bits) << 16) | 0xffff;
- while (lo < hi) {
- unsigned guess = (lo + hi) / 2;
- if (search < tree[guess])
- hi = guess;
- else
- lo = guess + 1;
- }
-
- // Pull out the key and check it.
- key = tree[lo - 1];
- CHECK(((search ^ key) >> (32 - (key & 0xf))) == 0);
-
- bits(s, key & 0xf);
- return (key >> 4) & 0xfff;
-}
-
-static void run(State* s, int sym) {
- int length = bits(s, lenBits[sym]) + lenBase[sym];
- int dsym = decode(s, s->distcodes, s->tdist);
- int offs = bits(s, distBits[dsym]) + distBase[dsym];
- copy(s, s->out - offs, length);
-}
-
-static void block(State* s) {
- for (;;) {
- int sym = decode(s, s->litcodes, s->tlit);
- if (sym < 256)
- *emit(s, 1) = (unsigned char)sym;
- else if (sym > 256)
- run(s, sym - 257);
- else
- break;
- }
-}
-
-static void stored(State* s) {
- // Uncompressed data block.
- int len;
- bits(s, s->count & 7);
- len = bits(s, 16);
- CHECK(((len ^ s->bits) & 0xffff) == 0xffff);
- CHECK(s->in + len <= s->inend);
-
- copy(s, s->in, len);
- s->in += len;
- bits(s, 16);
-}
-
-static void fixed(State* s) {
- // Fixed set of Huffman codes.
- int n;
- unsigned char lens[288 + 32];
- for (n = 0; n <= 143; n++)
- lens[n] = 8;
- for (n = 144; n <= 255; n++)
- lens[n] = 9;
- for (n = 256; n <= 279; n++)
- lens[n] = 7;
- for (n = 280; n <= 287; n++)
- lens[n] = 8;
- for (n = 0; n < 32; n++)
- lens[288 + n] = 5;
-
- // Build lit/dist trees.
- s->tlit = build(s, s->litcodes, lens, 288);
- s->tdist = build(s, s->distcodes, lens + 288, 32);
-}
-
-static void dynamic(State* s) {
- int n, i, nlit, ndist, nlen;
- unsigned char lenlens[19] = { 0 }, lens[288 + 32];
- nlit = 257 + bits(s, 5);
- ndist = 1 + bits(s, 5);
- nlen = 4 + bits(s, 4);
- for (n = 0; n < nlen; n++)
- lenlens[(int) order[n]] = (unsigned char)bits(s, 3);
-
- // Build the tree for decoding code lengths.
- s->tlen = build(s, s->lencodes, lenlens, 19);
-
- // Decode code lengths.
- for (n = 0; n < nlit + ndist;) {
- int sym = decode(s, s->lencodes, s->tlen);
- switch (sym) {
- case 16:
- for (i = 3 + bits(s, 2); i; i--, n++)
- lens[n] = lens[n - 1];
- break;
- case 17:
- for (i = 3 + bits(s, 3); i; i--, n++)
- lens[n] = 0;
- break;
- case 18:
- for (i = 11 + bits(s, 7); i; i--, n++)
- lens[n] = 0;
- break;
- default:
- lens[n++] = (unsigned char)sym;
- break;
- }
- }
-
- // Build lit/dist trees.
- s->tlit = build(s, s->litcodes, lens, nlit);
- s->tdist = build(s, s->distcodes, lens + nlit, ndist);
-}
-
-int tigrInflate(void* out, unsigned outlen, const void* in, unsigned inlen) {
- int last;
- State* s = (State*)calloc(1, sizeof(State));
-
- // We assume we can buffer 2 extra bytes from off the end of 'in'.
- s->in = (unsigned char*)in;
- s->inend = s->in + inlen + 2;
- s->out = (unsigned char*)out;
- s->outend = s->out + outlen;
- s->bits = 0;
- s->count = 0;
- bits(s, 0);
-
- if (setjmp(s->jmp) == 1) {
- free(s);
- return 0;
- }
-
- do {
- last = bits(s, 1);
- switch (bits(s, 2)) {
- case 0:
- stored(s);
- break;
- case 1:
- fixed(s);
- block(s);
- break;
- case 2:
- dynamic(s);
- block(s);
- break;
- case 3:
- FAIL();
- }
- } while (!last);
-
- free(s);
- return 1;
-}
-
-#undef CHECK
-#undef FAIL
-
-//////// End of inlined file: tigr_inflate.c ////////
-
-//////// Start of inlined file: tigr_print.c ////////
-
-//#include "tigr_internal.h"
-//////// Start of inlined file: tigr_font.h ////////
-
-// Auto-generated by incbin.pl from font.png
-
-const unsigned char tigr_font[] = {
- 0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52,
- 0x00,0x00,0x00,0xfd,0x00,0x00,0x00,0x5c,0x08,0x03,0x00,0x00,0x00,0x92,0xab,0x43,
- 0x85,0x00,0x00,0x03,0x00,0x50,0x4c,0x54,0x45,0x5f,0x53,0x87,0x00,0x00,0x00,0x54,
- 0x54,0x54,0xff,0xff,0xfa,0x31,0x2a,0x42,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,
- 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,
- 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,
- 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x4a,0x44,0xb7,0x5a,0x00,0x00,0x00,
- 0x01,0x74,0x52,0x4e,0x53,0x00,0x40,0xe6,0xd8,0x66,0x00,0x00,0x00,0x01,0x62,0x4b,
- 0x47,0x44,0x00,0x88,0x05,0x1d,0x48,0x00,0x00,0x00,0x09,0x70,0x48,0x59,0x73,0x00,
- 0x00,0x0b,0x13,0x00,0x00,0x0b,0x13,0x01,0x00,0x9a,0x9c,0x18,0x00,0x00,0x00,0x07,
- 0x74,0x49,0x4d,0x45,0x07,0xe5,0x04,0x1c,0x16,0x0f,0x2b,0x56,0x9d,0x54,0x3b,0x00,
- 0x00,0x0b,0x7c,0x49,0x44,0x41,0x54,0x78,0xda,0xed,0x5c,0x89,0x9a,0xec,0xac,0x0a,
- 0x04,0xf5,0xfd,0x9f,0xf9,0x4e,0x94,0xa5,0x40,0x93,0x98,0xee,0x3e,0x73,0xe7,0xfb,
- 0x67,0xce,0xd2,0x66,0x31,0x6a,0x29,0x51,0x4a,0x20,0xd4,0x7e,0xf3,0x1f,0x6a,0x34,
- 0xfe,0xb4,0x5a,0xf9,0xf8,0x7f,0x24,0x54,0xf9,0xf8,0xf7,0x75,0xeb,0xeb,0x97,0xe0,
- 0xd4,0x73,0x92,0x64,0xd7,0xab,0x90,0xa4,0xc3,0x91,0x59,0xb2,0x56,0x29,0x50,0xce,
- 0xed,0xfa,0x71,0x74,0x94,0xd7,0x2f,0xd8,0xf5,0x7c,0x0e,0xf9,0xb1,0x74,0x69,0x15,
- 0x13,0x14,0xfd,0x09,0xf4,0xd2,0x08,0x3e,0xfe,0xf6,0x53,0x86,0x9c,0xa5,0x8d,0xcc,
- 0xd2,0x45,0x9a,0x2c,0xd0,0x7f,0x65,0x63,0x45,0xc1,0xa3,0x00,0xae,0x78,0x7d,0xb4,
- 0xb9,0x9f,0xf3,0x57,0xb9,0x7a,0xfd,0x38,0xc3,0x7c,0x90,0x36,0xad,0xba,0x68,0x75,
- 0x1c,0x8a,0x7e,0x05,0x3d,0x1f,0xcf,0xfa,0x18,0x0c,0xd8,0xad,0xff,0x96,0x9e,0xa3,
- 0xd7,0xc0,0x36,0xec,0x90,0xe9,0x0c,0x7d,0x1d,0x68,0x6c,0x6c,0x7b,0x01,0x5f,0xa5,
- 0x0d,0x74,0x23,0x25,0xe9,0xf5,0xd1,0xbb,0xbd,0x22,0x62,0xc1,0xa5,0xd7,0x53,0x2a,
- 0x02,0x38,0x9a,0x35,0x04,0xa4,0x8f,0x90,0x16,0xf9,0x0a,0x7a,0xfa,0x7a,0xb6,0x8f,
- 0xe9,0xd1,0x09,0x63,0x6c,0x8f,0x46,0x70,0xd1,0x7e,0x1d,0x08,0x07,0x20,0x41,0xdf,
- 0x81,0x15,0x6b,0x03,0x11,0x1e,0xf6,0x3e,0xb1,0xde,0xec,0xc3,0x22,0x12,0x31,0x46,
- 0x4d,0x9f,0xfd,0xaa,0x41,0x7b,0x5d,0xf3,0xb3,0xe7,0xe3,0x5a,0x3d,0x1d,0xbd,0xd6,
- 0xff,0x1f,0xe0,0xa9,0x4b,0x48,0x2f,0x6c,0x0c,0xc2,0x78,0xe4,0x25,0xf4,0x32,0x6a,
- 0x2a,0xf9,0xac,0xaf,0xeb,0xa8,0xa6,0x8e,0x54,0x87,0x27,0x8c,0xfd,0xd1,0x5f,0x63,
- 0xb8,0xbc,0x39,0xe4,0xa3,0x63,0x12,0x2e,0x6f,0x49,0xbf,0xfe,0xd5,0x60,0xc6,0xf7,
- 0x3d,0x4b,0xba,0xe6,0xb3,0x73,0xd6,0xeb,0xa3,0x11,0x2a,0xec,0x15,0x5f,0x83,0xf1,
- 0xc8,0x53,0xf4,0xec,0xaf,0x50,0x92,0xfc,0xa3,0xdc,0x31,0x24,0x7a,0x8e,0xe8,0x0f,
- 0xc1,0x18,0xd5,0xae,0x24,0x5f,0x85,0xa6,0x04,0xf4,0xde,0x8d,0x84,0xa8,0xb3,0xa4,
- 0x6b,0x3e,0x3b,0xf7,0xfb,0x0d,0x85,0x7d,0x74,0xb6,0x74,0xe8,0xa8,0xe2,0x8d,0xb1,
- 0x07,0xc9,0xaf,0xa3,0xdc,0xca,0x3a,0x24,0x32,0xa8,0x8e,0x9e,0x48,0x90,0x68,0x43,
- 0x1b,0x4e,0xa3,0xa9,0xf5,0x36,0xeb,0x15,0x5a,0xa3,0xca,0x69,0xc8,0x37,0xbd,0xf7,
- 0x2a,0xec,0x63,0xce,0x97,0x66,0xf4,0xf4,0x33,0x92,0xdf,0xfb,0x13,0x3b,0x43,0xc6,
- 0x9e,0x6d,0xe2,0x8f,0x2b,0xde,0x84,0x5e,0x32,0xfb,0xdc,0x80,0x92,0x2e,0xd7,0x4d,
- 0x06,0xf0,0xfd,0x96,0xc9,0x05,0x9f,0xb7,0x74,0xcc,0x4d,0xc7,0x73,0x30,0xe7,0x17,
- 0xa2,0xd7,0xc7,0x1e,0x24,0x7f,0x4c,0x7e,0x36,0x9e,0x76,0x7a,0x48,0x98,0xbe,0xd0,
- 0x32,0xf3,0xe4,0xf5,0x1e,0x06,0xa3,0xe7,0x28,0x92,0xb9,0x67,0x65,0x11,0x52,0x4d,
- 0xf5,0xfa,0x91,0x3d,0x9c,0x5b,0xfe,0x42,0xf8,0x3c,0xe4,0x6f,0xd6,0xf1,0xa1,0x01,
- 0x2f,0xa3,0xcf,0x05,0x9d,0x9d,0x7a,0xce,0x36,0x67,0x6a,0x51,0x1a,0xda,0x69,0x01,
- 0xef,0xa6,0xcd,0x3a,0x3e,0xa3,0x7f,0x4d,0xf2,0x3f,0x83,0x3e,0x4a,0xc3,0x3f,0x44,
- 0x9f,0x4b,0x4f,0x55,0xfd,0x7f,0xd0,0xaf,0x0e,0x7f,0x32,0xfa,0xdf,0xce,0x72,0x54,
- 0x01,0xa5,0xac,0x6a,0xa5,0xf3,0x3c,0x65,0xfb,0x7d,0x5d,0x88,0xe0,0x7a,0x5a,0xc0,
- 0x74,0xe1,0xca,0xeb,0xf9,0x65,0x85,0x34,0x55,0x3c,0xad,0x7c,0x34,0x1f,0x60,0x53,
- 0xaa,0x2f,0x3c,0x35,0x90,0x92,0x21,0x20,0x63,0x3d,0x63,0xd5,0x66,0xce,0x48,0x05,
- 0x67,0x12,0x82,0x64,0x44,0xab,0x64,0xd3,0x8c,0xd9,0x16,0xac,0x88,0xde,0xf5,0xfd,
- 0xdb,0x8a,0x30,0xd5,0x82,0xf7,0xd1,0x83,0xd6,0x58,0x7c,0xd9,0xe5,0x26,0x7a,0x9b,
- 0xa3,0xef,0x7a,0x98,0x6a,0x33,0x53,0xe1,0xbc,0x44,0x11,0xc9,0x48,0x56,0x66,0xc6,
- 0xea,0x4d,0xc2,0x18,0x0b,0xa2,0x8f,0xc2,0xb3,0xaa,0x30,0x8d,0xa0,0xe8,0xcc,0xa6,
- 0x25,0xad,0xd1,0x33,0x56,0x55,0x55,0xdd,0x57,0x32,0xc6,0xa6,0x60,0x1e,0x60,0x61,
- 0x4d,0x6e,0x83,0xbf,0xaa,0x2e,0x07,0x8d,0x0b,0xad,0x76,0x71,0x52,0xc5,0x9a,0x43,
- 0x2f,0x98,0xa6,0x9d,0xd0,0x9b,0x9a,0xae,0xe5,0xd8,0x98,0xdf,0xa0,0x77,0xed,0x86,
- 0x61,0x11,0x3f,0x45,0x4f,0x2c,0x55,0xb9,0xfe,0xe4,0x10,0x1a,0x39,0xfa,0x63,0x35,
- 0xf4,0x35,0x59,0xd0,0x93,0x6b,0xb2,0x31,0x4d,0xe8,0x59,0x15,0x57,0x4d,0xeb,0x44,
- 0x4a,0xa6,0xb1,0x0f,0xe8,0x95,0xcb,0xc5,0x57,0x06,0xbb,0x3b,0x14,0xc8,0x71,0x57,
- 0x64,0x81,0xde,0xe9,0x93,0xd0,0x4f,0xe1,0x4f,0xd0,0xf1,0x42,0x07,0x08,0x95,0x59,
- 0x40,0xdf,0xe5,0xe6,0xec,0xb5,0x4a,0x95,0x26,0xc9,0x0f,0x20,0x16,0xe8,0xf9,0x72,
- 0xd6,0x43,0xb4,0xda,0xd6,0x58,0x20,0xd7,0xc4,0x0f,0x16,0x73,0x9b,0x6f,0x10,0x60,
- 0x3f,0x39,0xc3,0x10,0x9a,0xbd,0x46,0xcf,0x75,0x28,0xb0,0xb4,0xf1,0x1a,0xce,0xef,
- 0xfd,0x0a,0x3d,0x8e,0xf9,0x2e,0x7a,0x7b,0x35,0x72,0xaf,0xac,0xd0,0x03,0x6f,0xb0,
- 0x07,0x0a,0x9f,0xf1,0x28,0x25,0x43,0x4b,0xf4,0xc4,0x75,0xb5,0x72,0x05,0xb2,0x91,
- 0x49,0x4a,0x24,0x1f,0x79,0x99,0x61,0x10,0xbb,0x3c,0xc9,0xc5,0x99,0xf9,0x84,0xcd,
- 0xe4,0xfb,0x8a,0xc9,0xce,0xd9,0x2a,0x28,0x1c,0x27,0x00,0x1f,0x7b,0x5f,0xf1,0x74,
- 0x7b,0x0d,0x09,0x58,0x3f,0x6b,0x43,0x33,0x76,0x52,0x61,0x24,0xa4,0x24,0x72,0x12,
- 0x48,0x4a,0x24,0x1f,0xe1,0x39,0x21,0x2b,0x85,0xe6,0xeb,0xfd,0xaa,0x1e,0x20,0xab,
- 0xc1,0x02,0x75,0xc7,0x2b,0xb2,0x20,0x16,0xba,0x15,0xcf,0x43,0x81,0xb2,0x17,0xc6,
- 0xbe,0xdf,0x33,0xaa,0x2e,0x42,0x05,0x19,0x68,0x9c,0xdc,0x75,0x4d,0xf7,0xdf,0x28,
- 0xa4,0xab,0xf4,0x3a,0x83,0xb7,0xf1,0x79,0x81,0xeb,0x1c,0x8d,0xce,0xd4,0xf2,0x9f,
- 0x87,0xfe,0x9d,0x02,0xff,0xd0,0x3f,0x44,0xff,0xbb,0x59,0x8e,0x4f,0x85,0xbe,0x96,
- 0x41,0x17,0xe1,0x6d,0x51,0x1f,0x73,0x6e,0x53,0x2d,0xe3,0xe2,0xf6,0xea,0x58,0x37,
- 0x57,0x1b,0x9e,0x88,0x4e,0x0b,0x3b,0x9e,0x78,0xf7,0x53,0xe8,0x6b,0xc5,0x49,0xc9,
- 0x16,0x2a,0x99,0x57,0x93,0xc2,0xf4,0xed,0xe8,0x75,0xb5,0x6c,0x1c,0x87,0xec,0x12,
- 0x7d,0xdf,0xb5,0x5d,0x51,0x4b,0xa3,0xaa,0xf1,0x36,0x30,0x58,0xd3,0x13,0x60,0xec,
- 0x51,0xbf,0x36,0xb5,0xe7,0x82,0xc1,0x42,0x41,0x14,0xaa,0xb9,0x64,0x74,0xb3,0xa2,
- 0xdc,0xc8,0x1b,0xe3,0x64,0xf6,0x0e,0x7d,0x57,0x1b,0x16,0x06,0xb3,0x48,0x61,0x59,
- 0xfb,0x74,0x71,0x39,0xa0,0x07,0xfd,0x1a,0x38,0x1d,0xef,0x33,0x5a,0xac,0x66,0x0f,
- 0x3d,0xb0,0x59,0x35,0x49,0xd5,0x3a,0xec,0x6f,0x9d,0xd6,0xdd,0xec,0x6c,0x99,0x89,
- 0x28,0xaa,0x9e,0xab,0x6d,0x8b,0x13,0xb2,0x01,0xe8,0x5d,0xbf,0x46,0x4e,0x77,0xab,
- 0x42,0x33,0x58,0xce,0xee,0xf9,0x6c,0xa2,0xb3,0x66,0x53,0x85,0xb1,0x2f,0x9d,0xcc,
- 0x1e,0xdb,0xca,0x77,0xe8,0x67,0xd2,0xc5,0x99,0xdc,0x5d,0x37,0x2b,0x8c,0x3d,0xa0,
- 0x37,0x4e,0xb7,0x45,0x20,0xd4,0x72,0x76,0xc7,0xe8,0x92,0x66,0x4e,0xc6,0xeb,0xa2,
- 0xe4,0x93,0x6c,0xcb,0x6f,0xa0,0xa7,0x6b,0xf4,0xb4,0x3b,0xf6,0x4e,0x70,0xae,0x59,
- 0xcd,0x92,0xe5,0x98,0xa1,0xfb,0x84,0xd3,0x4c,0x2f,0x12,0x98,0x7c,0x56,0xe8,0xef,
- 0xdf,0xfb,0x28,0xf9,0xc8,0x6f,0xdc,0x66,0xaa,0xec,0x02,0xc8,0xc6,0x29,0x7a,0x9b,
- 0x21,0xae,0x58,0x4d,0xb4,0xc9,0x22,0xcb,0x11,0x12,0x83,0xb5,0xb2,0xed,0x5c,0xf0,
- 0x4c,0x82,0x60,0xd6,0x43,0xdb,0x7e,0x79,0x80,0x9e,0xd0,0xc8,0x62,0xfc,0xc6,0x2e,
- 0x83,0x2d,0xa5,0x2c,0xc8,0x88,0xac,0x78,0x89,0x9f,0x5c,0xb0,0x1a,0xe5,0xf3,0x58,
- 0x90,0x36,0x43,0x48,0xcc,0x9a,0xe3,0x94,0xc0,0xaa,0xfc,0x71,0x36,0x86,0xd3,0x92,
- 0x8d,0x69,0x0b,0xfd,0xf6,0x46,0x7e,0x36,0x3e,0x92,0x5b,0xb4,0xd3,0xa5,0x8b,0x05,
- 0x1a,0xd5,0x8a,0xfc,0xd4,0x5d,0xed,0xa9,0x98,0x4c,0xda,0x5b,0xae,0xfd,0xe3,0xe8,
- 0xd1,0xf8,0xe8,0xa6,0xf4,0x7c,0xe9,0xdf,0xa3,0x8f,0xed,0xf8,0x2e,0xf4,0x68,0x7c,
- 0xb4,0x0a,0xa7,0x4b,0x17,0xe8,0x4d,0xd2,0x97,0x8c,0x76,0x1b,0x7d,0x6c,0xc7,0x6b,
- 0xe8,0xff,0x3c,0xd6,0xa4,0x8f,0x80,0x2c,0xa4,0xfe,0x86,0x6d,0x65,0x77,0xc0,0x60,
- 0xb3,0xf4,0x8f,0x53,0xd5,0xed,0x45,0x83,0x1d,0xc5,0xf8,0x8d,0x16,0x1d,0x5e,0x2a,
- 0xa3,0x3a,0xbc,0x30,0x23,0xca,0x56,0xb4,0x39,0xbe,0x24,0x6b,0x04,0xb4,0x2d,0x0c,
- 0xb5,0xfa,0x97,0x0d,0x17,0x84,0x2d,0x4b,0x16,0x03,0xfa,0xd9,0x27,0x61,0x89,0x5e,
- 0xd4,0xc8,0xe1,0xc9,0xe4,0x9e,0x0e,0x3e,0x05,0x0e,0x99,0xf6,0x1b,0xcf,0xd1,0xbb,
- 0xcf,0x58,0x51,0xa7,0x19,0xa8,0xe5,0x04,0xbd,0xe4,0x10,0x9f,0x83,0x5b,0xf4,0x5d,
- 0xc9,0xe7,0x80,0x9e,0x16,0xe8,0x27,0xc7,0x8b,0x32,0x3c,0x28,0x64,0xf7,0xd4,0xfa,
- 0xd0,0x37,0x75,0x15,0xb0,0x49,0x01,0xba,0x7c,0x70,0x05,0xcb,0x86,0xba,0x44,0x24,
- 0xf4,0xa2,0xab,0x8e,0x71,0x3c,0x7a,0x20,0xd4,0x32,0x54,0xbe,0x68,0x64,0x9c,0x28,
- 0xd2,0x3d,0xbf,0x17,0x52,0xc1,0xcd,0x50,0xae,0x24,0x5f,0x8d,0x69,0x66,0xad,0x13,
- 0x2f,0x0f,0xdc,0xdc,0x1f,0xcc,0x4a,0x76,0xa6,0x87,0xa3,0xa3,0x71,0x90,0xfe,0x0f,
- 0x5c,0x3e,0xc6,0xea,0xdd,0x74,0x11,0x07,0xb5,0x05,0xde,0x1b,0x82,0x71,0x24,0x25,
- 0x58,0xc3,0x2e,0x29,0x9a,0x95,0x29,0x7e,0xc3,0x1a,0x09,0x06,0xbc,0x2d,0xf4,0x2c,
- 0x96,0x19,0x05,0xc4,0x9b,0x92,0x4f,0xe6,0x8d,0x68,0x8f,0x89,0xe7,0x94,0x4a,0x7e,
- 0x91,0x42,0xab,0x90,0x0d,0xeb,0xb2,0x93,0x55,0xb0,0x05,0xc7,0x96,0x65,0x23,0x74,
- 0x04,0x5c,0xcd,0x77,0xff,0xb2,0xbe,0x71,0xcd,0xc9,0xc5,0x6f,0x6b,0xec,0x89,0x6e,
- 0x25,0x3f,0x6f,0x95,0x1b,0x7a,0x99,0x66,0x44,0xc1,0x57,0xa7,0x4e,0x93,0x7c,0xf3,
- 0xcd,0x6b,0x0b,0xa7,0x87,0xb5,0xdb,0xd0,0x7a,0xe1,0x45,0x1e,0xe4,0x46,0x57,0x33,
- 0x28,0x2b,0x7a,0xa9,0x8e,0x77,0xfc,0xf6,0x64,0xec,0x75,0x7a,0xb6,0x4d,0x82,0x31,
- 0x19,0xe9,0xdb,0x89,0x86,0x82,0x28,0xf9,0xd5,0x69,0xb4,0x28,0xf8,0x3c,0xbd,0xf7,
- 0xe2,0x4a,0x76,0x8d,0x7e,0x39,0xe7,0xcf,0x93,0x8f,0xf2,0x20,0x53,0xf7,0x8d,0x6c,
- 0x08,0x7a,0xab,0x6e,0xc7,0x7b,0x89,0xd4,0xfe,0xb9,0x8d,0x3e,0x4a,0xbe,0xd8,0x7f,
- 0x0b,0x30,0x14,0xa5,0x1f,0x32,0xd7,0x85,0x59,0xef,0x5d,0xf4,0xea,0x16,0xeb,0xf6,
- 0xda,0xba,0xf2,0x86,0xe3,0x7d,0xf4,0xa6,0xa6,0x35,0x73,0x9a,0x35,0xbd,0xbd,0x8c,
- 0x49,0x09,0xcd,0x24,0x9e,0x9a,0x33,0x9b,0xb9,0xb3,0x0d,0x27,0x37,0x61,0x47,0xb2,
- 0xe2,0xf9,0xd1,0x07,0xd0,0x4b,0x2d,0x60,0xd9,0x09,0xc4,0xcc,0x88,0xda,0xa6,0xef,
- 0x56,0xd0,0x76,0xdc,0xac,0xd4,0x0c,0xf2,0xc6,0x56,0x79,0x6e,0x69,0xb8,0xda,0xf0,
- 0xe8,0x7d,0xf4,0xab,0x5a,0xae,0x94,0xf4,0xe7,0xba,0xde,0x55,0xc5,0x7f,0xe8,0xff,
- 0x4b,0xe8,0xff,0x58,0x0e,0x86,0x52,0x10,0xaa,0xfe,0x94,0xdd,0x14,0xec,0x9e,0xfa,
- 0xae,0xc3,0x2e,0x66,0x5f,0x72,0x2c,0x52,0xa6,0xa1,0xfd,0x87,0x28,0x6e,0x0c,0xe3,
- 0x46,0x49,0xf0,0xf7,0x26,0xa0,0x49,0xdc,0x12,0x7d,0x91,0x1f,0x64,0x56,0x44,0xee,
- 0x42,0x94,0x8e,0x36,0x86,0x7f,0x8d,0x5e,0x75,0x70,0xb6,0xb8,0x1a,0x82,0x68,0x90,
- 0xb1,0xe9,0x5e,0x86,0x66,0xc7,0xe2,0x23,0xaf,0xf1,0x01,0xc9,0x7b,0x18,0xb3,0xa8,
- 0x5a,0x02,0x81,0x2c,0xac,0x81,0x0d,0x11,0x3d,0x46,0x02,0x50,0xe8,0x28,0xb6,0xf8,
- 0x99,0x80,0x94,0x59,0xba,0x04,0x8e,0x1e,0xa1,0xaf,0x40,0x22,0x84,0xa1,0xcd,0xce,
- 0x19,0x83,0xd5,0xa9,0xdb,0xd3,0xf0,0x76,0x63,0x73,0x8c,0x18,0xe0,0xc3,0xd4,0xd1,
- 0x86,0xd2,0x8b,0xc8,0x44,0x3c,0x94,0x26,0xbb,0x76,0x5c,0x2e,0xd1,0xb3,0x4b,0x9c,
- 0xc0,0x6f,0x03,0x68,0x91,0x72,0xa6,0xa3,0xfb,0x25,0xff,0x24,0x26,0xcb,0x76,0xb4,
- 0x39,0x99,0x0f,0x1a,0x6b,0x70,0x8e,0x52,0xd0,0xae,0x28,0x8a,0xba,0x57,0xd4,0x96,
- 0x61,0x7b,0xfb,0xcd,0x94,0x41,0x54,0xd5,0x2d,0x8c,0x47,0xb5,0xd2,0xb1,0xb3,0x99,
- 0xb6,0xee,0xb9,0xc5,0xb7,0xc1,0xdf,0x37,0x89,0x9e,0xf2,0xdd,0x85,0xa3,0x1c,0x67,
- 0xda,0xaa,0x67,0x3e,0x40,0x6f,0x5e,0x98,0xa5,0xd5,0x13,0x17,0x54,0x16,0x65,0xce,
- 0xa9,0x2c,0x0f,0xd9,0x6b,0xb5,0xa2,0x44,0xca,0xbb,0x21,0x82,0xde,0xe3,0x52,0x18,
- 0xf7,0xa3,0x0a,0x69,0x18,0x4f,0x0d,0x1b,0xd0,0x61,0xaf,0x3a,0xa3,0xd7,0x2a,0x8a,
- 0xbf,0x61,0x43,0xd9,0x53,0x57,0x43,0xeb,0x30,0xeb,0xee,0x5b,0x85,0x67,0x1a,0x7b,
- 0x67,0x52,0x34,0xb9,0xa0,0x5a,0x34,0x88,0xb1,0x61,0xf1,0x76,0x13,0xa7,0x18,0x95,
- 0x48,0x88,0x12,0x9c,0xc7,0xde,0x26,0x43,0xe9,0x27,0xd8,0x80,0xf6,0x5d,0x5a,0xd6,
- 0x50,0xa7,0x02,0xa3,0x33,0xc2,0xc3,0x5c,0xb8,0x47,0x2f,0x6b,0xfd,0x76,0x54,0x3c,
- 0x9c,0xe8,0x2d,0xc9,0x9f,0x23,0x4a,0xfa,0x4c,0xc8,0xd5,0xd9,0x30,0xb3,0x9f,0xb9,
- 0x44,0xba,0x43,0x64,0x1f,0x2d,0xce,0xdb,0xbd,0x1e,0xf6,0x11,0xbd,0xdd,0xd1,0xd1,
- 0x34,0xbf,0xf7,0x90,0x0b,0xde,0xec,0xe1,0x66,0x7b,0x76,0xf4,0x0c,0x3d,0x27,0xb4,
- 0xac,0x32,0xec,0x63,0xaf,0xee,0x68,0x46,0x38,0xc3,0x56,0xd3,0x88,0xe7,0x0b,0x30,
- 0x31,0x0b,0x4c,0x86,0x81,0x26,0x3b,0x7a,0x70,0x51,0x3b,0x45,0x2f,0x9b,0x1d,0x22,
- 0x40,0xbe,0x84,0xe4,0x23,0xa1,0x7b,0x0f,0xa2,0x11,0x6b,0x70,0x4d,0x2b,0x3a,0x8a,
- 0xec,0x01,0x57,0x8c,0x24,0x27,0x9d,0xcd,0x81,0x02,0x31,0x8b,0x5f,0x86,0xfd,0x8b,
- 0x38,0xf6,0x6e,0xbe,0x09,0xdb,0x5d,0x0b,0x35,0x51,0x05,0x88,0x2c,0x30,0x2c,0x1e,
- 0xa9,0xc7,0xe2,0x83,0x98,0xac,0x2b,0xaf,0x89,0xc6,0x97,0xf6,0x8a,0x33,0xf4,0xab,
- 0xc2,0xda,0x12,0x52,0xf2,0x1d,0x41,0x1d,0xe4,0x04,0x3d,0x11,0xc6,0x65,0xc5,0xa3,
- 0x8d,0xcd,0xad,0x8c,0x7e,0x1e,0xa6,0xa5,0x2e,0xfe,0x8f,0xd0,0xa3,0x99,0xc3,0x03,
- 0x1a,0xef,0xd0,0x9f,0x1c,0x6d,0x6c,0x6e,0x5d,0x6d,0x2d,0x7d,0x3f,0xfa,0x65,0x85,
- 0xac,0x2f,0xf9,0x43,0xf4,0x6a,0xea,0xf9,0x63,0x39,0x57,0xd6,0x0c,0xb6,0x50,0xf9,
- 0x90,0xd6,0xf4,0xed,0x81,0xe0,0x4a,0x45,0x18,0xeb,0x1f,0x1e,0x05,0x2d,0x16,0x62,
- 0x03,0xfc,0x8e,0xea,0xba,0x35,0x7f,0x93,0x60,0xbb,0x21,0xd3,0x70,0x9f,0x3c,0xe8,
- 0x0f,0x60,0x54,0x05,0xb6,0x7b,0xac,0xd2,0xbe,0x39,0x6d,0x31,0xc7,0x6e,0x36,0xd7,
- 0xc0,0xcc,0x19,0xbd,0xbb,0x90,0xb3,0x07,0x6b,0x1b,0x6c,0xdd,0xf2,0x92,0x3e,0xd0,
- 0x3b,0x4d,0x96,0x45,0x78,0x58,0xb7,0xcc,0x36,0x1b,0x32,0xa1,0x3f,0x79,0xd0,0x5b,
- 0x8e,0x31,0xe4,0xe4,0x5e,0x5a,0x8b,0xcf,0x40,0xdc,0xa5,0x11,0x3d,0x31,0x68,0xed,
- 0x67,0xc9,0xea,0x97,0x16,0x7e,0x31,0x5b,0x0d,0x10,0xe5,0x5a,0x62,0xc9,0xa2,0x27,
- 0x4b,0x49,0x29,0xe1,0xb9,0x4a,0x9b,0x14,0xd1,0xd1,0x3f,0x72,0x27,0xb3,0x32,0xa2,
- 0xcb,0x09,0x78,0xb8,0x4d,0x09,0x9b,0xe1,0x2a,0xfe,0x56,0x8f,0xcc,0x7a,0x18,0xa1,
- 0x55,0xb9,0x82,0xec,0x94,0x2d,0xb7,0x20,0xed,0x6f,0xb5,0x3a,0x88,0xb5,0x72,0xfb,
- 0xd9,0x14,0x30,0xa1,0x00,0xdd,0xfa,0x75,0x92,0xd0,0xc9,0x2f,0x87,0x2f,0x2e,0x3c,
- 0x68,0x80,0x45,0x86,0x57,0xae,0x53,0x0c,0xc8,0xd2,0x9f,0x0f,0xdd,0x8e,0x14,0x3d,
- 0xeb,0xd8,0xe7,0x30,0xb1,0x9d,0x34,0x47,0x65,0x99,0x3f,0xce,0x94,0xd0,0x55,0x1f,
- 0xec,0x45,0x68,0xe5,0x21,0x64,0x73,0xec,0xdc,0x42,0x8f,0xa1,0x4a,0xb2,0x5f,0xff,
- 0xb2,0xe4,0xeb,0xd8,0x97,0x29,0x5e,0x69,0x9d,0xec,0xa0,0xe7,0x47,0xc2,0x27,0x31,
- 0x75,0x22,0x3b,0x1b,0xe8,0xe1,0x93,0x0f,0xd2,0x6e,0x44,0xff,0x54,0xf2,0x29,0x7f,
- 0x3e,0xe0,0xc5,0x59,0x6f,0x1d,0x85,0x73,0x9b,0x8a,0x39,0x97,0x6b,0x0a,0xc2,0x3b,
- 0x4b,0xd1,0x02,0xc9,0xb6,0xe1,0x34,0xde,0x7b,0xf0,0x3b,0x7b,0x90,0x9a,0x2b,0x9b,
- 0xd3,0xa2,0x72,0x9e,0xac,0x7e,0xa7,0x70,0xa0,0xe9,0x1b,0x03,0x67,0xa9,0x19,0xf7,
- 0x39,0x85,0xeb,0x9c,0x3e,0xe0,0xc1,0x3a,0xc5,0x9f,0x2d,0xd4,0x5e,0x0f,0x88,0x20,
- 0xca,0x3a,0xc0,0x45,0xd2,0x76,0x55,0xe0,0x07,0x7e,0xec,0xf4,0x72,0xcb,0xfd,0xca,
- 0x9b,0xe8,0x91,0xbd,0xfe,0x3e,0xf4,0x77,0xb0,0x7f,0x3a,0xfa,0x3f,0x96,0x73,0x49,
- 0x2e,0x4c,0xdd,0x5e,0xb0,0x9c,0x4d,0x9e,0x21,0x9a,0x7e,0xa3,0xe0,0x6e,0x66,0x04,
- 0xc9,0xa9,0xd1,0x37,0x70,0x1c,0x74,0x66,0xdb,0x20,0x17,0xca,0x56,0x2e,0xd1,0x5f,
- 0xf3,0x0c,0xd9,0xed,0x77,0x2f,0x37,0x09,0x90,0x57,0xdd,0xa5,0x3c,0x22,0x5b,0xfc,
- 0x16,0xc7,0x41,0x67,0xb6,0x17,0x58,0x8e,0x2b,0x7b,0xfb,0xcf,0x8c,0x65,0x5a,0xbc,
- 0xdc,0x64,0xc7,0x4e,0xdc,0xa3,0x7a,0xdf,0x3e,0x21,0x59,0x66,0xa8,0x79,0x8d,0xe3,
- 0xe0,0x8e,0x17,0xc4,0xe4,0x3c,0x4b,0x9f,0xea,0x87,0x1e,0x2a,0x86,0x01,0x49,0xf8,
- 0x51,0x8f,0xdd,0xa2,0x2a,0x32,0x96,0xe7,0x1c,0x27,0xa2,0x0f,0x21,0x08,0xdb,0xa9,
- 0x13,0x13,0x5e,0x7c,0x42,0x6a,0xf1,0x89,0x89,0x84,0xde,0xe9,0xd6,0x43,0x3d,0x97,
- 0x3d,0x48,0x66,0x8f,0xe3,0x84,0xc0,0x92,0x09,0xfd,0xab,0x7a,0x7e,0xe4,0x78,0xf3,
- 0xf7,0x48,0x52,0x3a,0xa3,0xa7,0x47,0x8d,0x07,0xa5,0x5d,0xbf,0x0f,0xc3,0x9f,0x41,
- 0xff,0x82,0x9e,0xff,0xf8,0x99,0xcf,0xa1,0xb7,0x20,0x9c,0xf2,0x01,0xf4,0x2f,0x4b,
- 0xfe,0x0e,0x21,0x01,0xe3,0x6f,0x44,0x3f,0x7d,0xf2,0x60,0xb7,0x28,0x77,0x60,0xde,
- 0xe4,0x38,0x5c,0x13,0xab,0xd2,0x0b,0xe2,0xaf,0xb7,0x08,0xc2,0xb9,0x4d,0x8d,0x47,
- 0xd4,0x1d,0x62,0x12,0x02,0x66,0xd0,0xc1,0xec,0x3b,0x38,0x0e,0x7b,0xa4,0x57,0xb8,
- 0x90,0x7d,0x37,0xde,0x64,0x39,0x7b,0x1f,0xc6,0x6a,0xdf,0xac,0xe7,0xc6,0x70,0x3a,
- 0x4a,0x2b,0xde,0xe7,0x58,0xce,0xcf,0x44,0x8f,0x7b,0x10,0x9c,0x6c,0x3c,0x9f,0x64,
- 0x39,0x3f,0x13,0x3d,0x7e,0x3d,0xa4,0x24,0x1b,0xcf,0xef,0x66,0x39,0xff,0x03,0xca,
- 0x1b,0xaa,0xdf,0x6a,0xd1,0xba,0xa4,0x00,0x00,0x00,0x00,0x49,0x45,0x4e,0x44,0xae,
- 0x42,0x60,0x82 };
-
-int tigr_font_size = (int)sizeof(tigr_font);
-
-
-//////// End of inlined file: tigr_font.h ////////
-
-#include <stdlib.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <stdio.h>
-
-#ifdef _MSC_VER
-#define vsnprintf _vsnprintf
-#endif
-
-TigrFont tigrStockFont;
-TigrFont* tfont = &tigrStockFont;
-
-// Converts 8-bit codepage entries into Unicode code points.
-static int cp1252[] = {
- 0x20ac, 0xfffd, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152,
- 0xfffd, 0x017d, 0xfffd, 0xfffd, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 0x02dc, 0x2122,
- 0x0161, 0x203a, 0x0153, 0xfffd, 0x017e, 0x0178, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6,
- 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3,
- 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00c0,
- 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd,
- 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da,
- 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
- 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4,
- 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff,
-};
-
-static int border(Tigr* bmp, int x, int y) {
- TPixel top = tigrGet(bmp, 0, 0);
- TPixel c = tigrGet(bmp, x, y);
- return (c.r == top.r && c.g == top.g && c.b == top.b) || x >= bmp->w || y >= bmp->h;
-}
-
-static void scan(Tigr* bmp, int* x, int* y, int* rowh) {
- while (*y < bmp->h) {
- if (*x >= bmp->w) {
- *x = 0;
- (*y) += *rowh;
- *rowh = 1;
- }
- if (!border(bmp, *x, *y))
- return;
- (*x)++;
- }
-}
-
-/*
- * Watermarks are encoded vertically in the alpha channel using seven pixels
- * starting at x, y. The first and last alpha values contain the magic values
- * 0b10101010 and 0b01010101 respectively.
- */
-static int readWatermark(Tigr* bmp, int x, int y, int* big, int* small) {
- const int magicHeader = 0xAA;
- const int magicFooter = 0x55;
-
- unsigned char watermark[7];
-
- for (int i = 0; i < 7; i++) {
- TPixel c = tigrGet(bmp, x, y + i);
- watermark[i] = c.a;
- }
-
- if (watermark[0] != magicHeader || watermark[6] != magicFooter) {
- return 0;
- }
-
- *big = watermark[1] | (watermark[2] << 8) | (watermark[3] << 16) | (watermark[4] << 24);
- *small = watermark[5];
-
- return 1;
-}
-
-int tigrLoadGlyphs(TigrFont* font, int codepage) {
- int x = 0;
- int y = 0;
- int w = 0;
- int h = 0;
- int rowh = 1;
-
- TigrGlyph* g;
- switch (codepage) {
- case TCP_ASCII:
- font->numGlyphs = 128 - 32;
- break;
- case TCP_1252:
- font->numGlyphs = 256 - 32;
- break;
- case TCP_UTF32:
- if (!readWatermark(font->bitmap, 0, 0, &font->numGlyphs, &rowh)) {
- return 0;
- }
- h = rowh;
- x = 1;
- break;
- default:
- errno = EINVAL;
- return 0;
- }
-
- font->glyphs = (TigrGlyph*)calloc(font->numGlyphs, sizeof(TigrGlyph));
-
- for (int index = 0; index < font->numGlyphs; index++) {
- // Look up the Unicode code point.
- g = &font->glyphs[index];
-
- if (codepage != TCP_UTF32) {
- // Find the next glyph.
- scan(font->bitmap, &x, &y, &rowh);
-
- if (y >= font->bitmap->h) {
- errno = EINVAL;
- return 0;
- }
-
- // Scan the width and height
- w = h = 0;
- while (!border(font->bitmap, x + w, y)) {
- w++;
- }
-
- while (!border(font->bitmap, x, y + h)) {
- h++;
- }
- }
-
- switch (codepage) {
- case TCP_ASCII:
- g->code = index + 32;
- break;
- case TCP_1252:
- if (index < 96) {
- g->code = index + 32;
- } else {
- g->code = cp1252[index - 96];
- }
- break;
- case TCP_UTF32:
- if (!readWatermark(font->bitmap, x, y, &g->code, &w)) {
- // Maybe we are at the end of a row?
- x = 0;
- y += rowh;
- if (!readWatermark(font->bitmap, x, y, &g->code, &w)) {
- return 0;
- }
- }
- x++;
- break;
- default:
- return 0;
- }
-
- g->x = x;
- g->y = y;
- g->w = w;
- g->h = h;
- x += w;
- if (h != font->glyphs[0].h) {
- errno = EINVAL;
- return 0;
- }
-
- if (h > rowh) {
- rowh = h;
- }
- }
-
- // Sort by code point.
- for (int i = 1; i < font->numGlyphs; i++) {
- int j = i;
- TigrGlyph g = font->glyphs[i];
- while (j > 0 && font->glyphs[j - 1].code > g.code) {
- font->glyphs[j] = font->glyphs[j - 1];
- j--;
- }
- font->glyphs[j] = g;
- }
-
- return 1;
-}
-
-TigrFont* tigrLoadFont(Tigr* bitmap, int codepage) {
- TigrFont* font = (TigrFont*)calloc(1, sizeof(TigrFont));
- font->bitmap = bitmap;
- if (!tigrLoadGlyphs(font, codepage)) {
- tigrFreeFont(font);
- return NULL;
- }
- return font;
-}
-
-void tigrFreeFont(TigrFont* font) {
- tigrFree(font->bitmap);
- free(font->glyphs);
- free(font);
-}
-
-static TigrGlyph* get(TigrFont* font, int code) {
- unsigned lo = 0, hi = font->numGlyphs;
- while (lo < hi) {
- unsigned guess = (lo + hi) / 2;
- if (code < font->glyphs[guess].code)
- hi = guess;
- else
- lo = guess + 1;
- }
-
- if (lo == 0 || font->glyphs[lo - 1].code != code)
- return &font->glyphs['?' - 32];
- else
- return &font->glyphs[lo - 1];
-}
-
-void tigrSetupFont(TigrFont* font) {
- // Load the stock font if needed.
- if (font == tfont && !tfont->bitmap) {
- tfont->bitmap = tigrLoadImageMem(tigr_font, tigr_font_size);
- tigrLoadGlyphs(tfont, 1252);
- }
-}
-
-void tigrPrint(Tigr* dest, TigrFont* font, int x, int y, TPixel color, const char* text, ...) {
- char tmp[1024];
- TigrGlyph* g;
- va_list args;
- const char* p;
- int start = x, c;
-
- tigrSetupFont(font);
-
- // Expand the formatting string.
- va_start(args, text);
- vsnprintf(tmp, sizeof(tmp), text, args);
- tmp[sizeof(tmp) - 1] = 0;
- va_end(args);
-
- // Print each glyph.
- p = tmp;
- while (*p) {
- p = tigrDecodeUTF8(p, &c);
- if (c == '\r')
- continue;
- if (c == '\n') {
- x = start;
- y += tigrTextHeight(font, "");
- continue;
- }
- g = get(font, c);
- tigrBlitTint(dest, font->bitmap, x, y, g->x, g->y, g->w, g->h, color);
- x += g->w;
- }
-}
-
-int tigrTextWidth(TigrFont* font, const char* text) {
- int x = 0, w = 0, c;
- tigrSetupFont(font);
-
- while (*text) {
- text = tigrDecodeUTF8(text, &c);
- if (c == '\n' || c == '\r') {
- x = 0;
- } else {
- x += get(font, c)->w;
- w = (x > w) ? x : w;
- }
- }
- return w;
-}
-
-int tigrTextHeight(TigrFont* font, const char* text) {
- int rowh, h, c;
- tigrSetupFont(font);
-
- h = rowh = get(font, 0)->h;
- while (*text) {
- text = tigrDecodeUTF8(text, &c);
- if (c == '\n' && *text)
- h += rowh;
- }
- return h;
-}
-
-//////// End of inlined file: tigr_print.c ////////
-
-//////// Start of inlined file: tigr_win.c ////////
-
-#ifndef TIGR_HEADLESS
-
-//#include "tigr_internal.h"
-#include <assert.h>
-
-// not really windows stuff
-TigrInternal* tigrInternal(Tigr* bmp) {
- assert(bmp->handle);
- return (TigrInternal*)(bmp + 1);
-}
-
-#ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <shellapi.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stddef.h>
-
-#ifdef _MSC_VER
-#pragma comment(lib, "opengl32") // glViewport
-#pragma comment(lib, "shell32") // CommandLineToArgvW
-#pragma comment(lib, "user32") // SetWindowLong
-#pragma comment(lib, "gdi32") // ChoosePixelFormat
-#pragma comment(lib, "advapi32") // RegSetValueEx
-#endif
-
-#define WIDGET_SCALE 3
-#define WIDGET_FADE 16
-
-int main(int argc, char* argv[]);
-
-#ifndef TIGR_DO_NOT_PRESERVE_WINDOW_POSITION
-HKEY tigrRegKey;
-#endif
-
-#ifdef __TINYC__
-#define CP_UTF8 65001
-int WINAPI MultiByteToWideChar();
-int WINAPI WideCharToMultiByte();
-#endif
-
-static wchar_t* unicode(const char* str) {
- int len = MultiByteToWideChar(CP_UTF8, 0, str, -1, 0, 0);
- wchar_t* dest = (wchar_t*)malloc(sizeof(wchar_t) * len);
- MultiByteToWideChar(CP_UTF8, 0, str, -1, dest, len);
- return dest;
-}
-
-void tigrError(Tigr* bmp, const char* message, ...) {
- char tmp[1024];
-
- va_list args;
- va_start(args, message);
- _vsnprintf(tmp, sizeof(tmp), message, args);
- tmp[sizeof(tmp) - 1] = 0;
- va_end(args);
-
- MessageBoxW(bmp ? (HWND)bmp->handle : NULL, unicode(tmp), bmp ? tigrInternal(bmp)->wtitle : L"Error",
- MB_OK | MB_ICONERROR);
- exit(1);
-}
-
-void tigrEnterBorderlessWindowed(Tigr* bmp) {
- // Enter borderless windowed mode.
- MONITORINFO mi = { sizeof(mi) };
- TigrInternal* win = tigrInternal(bmp);
-
- GetWindowRect((HWND)bmp->handle, &win->oldPos);
-
- GetMonitorInfo(MonitorFromWindow((HWND)bmp->handle, MONITOR_DEFAULTTONEAREST), &mi);
- win->dwStyle = WS_VISIBLE | WS_POPUP;
- SetWindowLong((HWND)bmp->handle, GWL_STYLE, win->dwStyle);
- SetWindowPos((HWND)bmp->handle, HWND_TOP, mi.rcMonitor.left, mi.rcMonitor.top,
- mi.rcMonitor.right - mi.rcMonitor.left, mi.rcMonitor.bottom - mi.rcMonitor.top, 0);
-}
-
-void tigrLeaveBorderlessWindowed(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
-
- win->dwStyle = WS_VISIBLE | WS_OVERLAPPEDWINDOW;
- SetWindowLong((HWND)bmp->handle, GWL_STYLE, win->dwStyle);
-
- SetWindowPos((HWND)bmp->handle, NULL, win->oldPos.left, win->oldPos.top, win->oldPos.right - win->oldPos.left,
- win->oldPos.bottom - win->oldPos.top, 0);
-}
-
-void tigrWinUpdateWidgets(Tigr* bmp, int dw, int dh) {
- POINT pt;
- int i, x, clicked = 0;
- char str[8];
- TPixel col;
- TPixel off = tigrRGB(255, 255, 255);
- TPixel on = tigrRGB(0, 200, 255);
- TigrInternal* win = tigrInternal(bmp);
- (void)dh;
-
- tigrClear(win->widgets, tigrRGBA(0, 0, 0, 0));
-
- if (!(win->dwStyle & WS_POPUP)) {
- win->widgetsWanted = 0;
- win->widgetAlpha = 0;
- return;
- }
-
- // See if we want to be showing widgets or not.
- GetCursorPos(&pt);
- ScreenToClient((HWND)bmp->handle, &pt);
- if (pt.y == 0)
- win->widgetsWanted = 1;
- if (pt.y > win->widgets->h * WIDGET_SCALE)
- win->widgetsWanted = 0;
-
- // Track the alpha.
- if (win->widgetsWanted)
- win->widgetAlpha = (win->widgetAlpha <= 255 - WIDGET_FADE) ? win->widgetAlpha + WIDGET_FADE : 255;
- else
- win->widgetAlpha = (win->widgetAlpha >= WIDGET_FADE) ? win->widgetAlpha - WIDGET_FADE : 0;
-
- // Get relative coords.
- pt.x -= (dw - win->widgets->w * WIDGET_SCALE);
- pt.x /= WIDGET_SCALE;
- pt.y /= WIDGET_SCALE;
-
- tigrClear(win->widgets, tigrRGBA(0, 0, 0, win->widgetAlpha));
-
- // Render it.
- for (i = 0; i < 3; i++) {
- switch (i) {
- case 0:
- str[0] = '_';
- str[1] = 0;
- break; // "_" (minimize)
- case 1:
- str[0] = (char)0xEF;
- str[1] = (char)0xBF;
- str[2] = (char)0xBD;
- str[3] = 0;
- break; // "[]" (maximize)
- case 2:
- str[0] = (char)0xC3;
- str[1] = (char)0x97;
- str[2] = 0;
- break; // "x" (close)
- }
- x = win->widgets->w + (i - 3) * 12;
- if (i == 2)
- off = tigrRGB(255, 0, 0);
- if (pt.x >= x && pt.x < x + 10 && pt.y < win->widgets->h) {
- col = on;
- if (GetAsyncKeyState(VK_LBUTTON) & 0x8000)
- clicked |= 1 << i;
- } else {
- col = off;
- }
- col.a = win->widgetAlpha;
- tigrPrint(win->widgets, tfont, x, 2, col, str);
- }
-
- if (clicked & 1)
- ShowWindow((HWND)bmp->handle, SW_MINIMIZE);
- if (clicked & 2)
- tigrLeaveBorderlessWindowed(bmp);
- if (clicked & 4)
- SendMessage((HWND)bmp->handle, WM_CLOSE, 0, 0);
-}
-
-void tigrUpdate(Tigr* bmp) {
- MSG msg;
- RECT rc;
- int dw, dh;
- TigrInternal* win = tigrInternal(bmp);
-
- if (!win->shown) {
- win->shown = 1;
- UpdateWindow((HWND)bmp->handle);
- ShowWindow((HWND)bmp->handle, SW_SHOW);
- }
-
- // Get the window size.
- GetClientRect((HWND)bmp->handle, &rc);
- dw = rc.right - rc.left;
- dh = rc.bottom - rc.top;
-
- // Update the widget overlay.
- tigrWinUpdateWidgets(bmp, dw, dh);
-
- if (!tigrGAPIBegin(bmp)) {
- tigrGAPIPresent(bmp, dw, dh);
- SwapBuffers(win->gl.dc);
- tigrGAPIEnd(bmp);
- }
-
- memcpy(win->prev, win->keys, 256);
- win->scrollDeltaX = 0;
- win->scrollDeltaY = 0;
-
- // Run the message pump.
- while (PeekMessage(&msg, (HWND)bmp->handle, 0, 0, PM_REMOVE)) {
- if (msg.message == WM_QUIT)
- break;
-
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
-}
-
-typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALFARPROC_)(int);
-static PFNWGLSWAPINTERVALFARPROC_ wglSwapIntervalEXT_ = 0;
-
-int tigrGAPIBegin(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
-
- return wglMakeCurrent(win->gl.dc, win->gl.hglrc) ? 0 : -1;
-}
-
-int tigrGAPIEnd(Tigr* bmp) {
- (void)bmp;
- return wglMakeCurrent(NULL, NULL) ? 0 : -1;
-}
-
-static BOOL UnadjustWindowRectEx(LPRECT prc, DWORD dwStyle, BOOL fMenu, DWORD dwExStyle) {
- BOOL fRc;
- RECT rc;
- SetRectEmpty(&rc);
- fRc = AdjustWindowRectEx(&rc, dwStyle, fMenu, dwExStyle);
- if (fRc) {
- prc->left -= rc.left;
- prc->top -= rc.top;
- prc->right -= rc.right;
- prc->bottom -= rc.bottom;
- }
- return fRc;
-}
-
-LRESULT CALLBACK tigrWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
- Tigr* bmp;
- TigrInternal* win = NULL;
- RECT rc;
- int dw, dh;
-
- GetClientRect(hWnd, &rc);
- dw = rc.right - rc.left;
- dh = rc.bottom - rc.top;
-
- bmp = (Tigr*)GetPropW(hWnd, L"Tigr");
- if (bmp)
- win = tigrInternal(bmp);
-
- switch (message) {
- case WM_PAINT:
- if (!tigrGAPIBegin(bmp)) {
- tigrGAPIPresent(bmp, dw, dh);
- SwapBuffers(win->gl.dc);
- tigrGAPIEnd(bmp);
- }
- ValidateRect(hWnd, NULL);
- break;
- case WM_CLOSE:
- if (win)
- win->closed = 1;
- break;
- case WM_GETMINMAXINFO:
- if (bmp) {
- MINMAXINFO* info = (MINMAXINFO*)lParam;
- RECT rc;
- rc.left = 0;
- rc.top = 0;
- if (win->flags & TIGR_AUTO) {
- rc.right = 32;
- rc.bottom = 32;
- } else {
- int minscale = tigrEnforceScale(1, win->flags);
- rc.right = bmp->w * minscale;
- rc.bottom = bmp->h * minscale;
- }
- AdjustWindowRectEx(&rc, win->dwStyle, FALSE, 0);
- info->ptMinTrackSize.x = rc.right - rc.left;
- info->ptMinTrackSize.y = rc.bottom - rc.top;
- }
- return 0;
- case WM_SIZING:
- if (win) {
- // Calculate scale-constrained sizes.
- RECT* rc = (RECT*)lParam;
- int dx, dy;
- UnadjustWindowRectEx(rc, win->dwStyle, FALSE, 0);
- dx = (rc->right - rc->left) % win->scale;
- dy = (rc->bottom - rc->top) % win->scale;
- switch (wParam) {
- case WMSZ_LEFT:
- rc->left += dx;
- break;
- case WMSZ_RIGHT:
- rc->right -= dx;
- break;
- case WMSZ_TOP:
- rc->top += dy;
- break;
- case WMSZ_TOPLEFT:
- rc->left += dx;
- rc->top += dy;
- break;
- case WMSZ_TOPRIGHT:
- rc->right -= dx;
- rc->top += dy;
- break;
- case WMSZ_BOTTOM:
- rc->bottom -= dy;
- break;
- case WMSZ_BOTTOMLEFT:
- rc->left += dx;
- rc->bottom -= dy;
- break;
- case WMSZ_BOTTOMRIGHT:
- rc->right -= dx;
- rc->bottom -= dy;
- break;
- }
- AdjustWindowRectEx(rc, win->dwStyle, FALSE, 0);
- }
- return TRUE;
- case WM_SIZE:
- if (win) {
- if (wParam != SIZE_MINIMIZED) {
- // Detect window size changes and update our bitmap accordingly.
- dw = LOWORD(lParam);
- dh = HIWORD(lParam);
- if (win->flags & TIGR_AUTO) {
- tigrResize(bmp, dw / win->scale, dh / win->scale);
- } else {
- win->scale = tigrEnforceScale(tigrCalcScale(bmp->w, bmp->h, dw, dh), win->flags);
- }
- tigrPosition(bmp, win->scale, dw, dh, win->pos);
- }
-
- // If someone tried to maximize us (e.g. via shortcut launch options),
- // prefer instead to be borderless.
- if (wParam == SIZE_MAXIMIZED) {
- ShowWindow((HWND)bmp->handle, SW_NORMAL);
- tigrEnterBorderlessWindowed(bmp);
- }
- }
- return 0;
-#ifndef TIGR_DO_NOT_PRESERVE_WINDOW_POSITION
- case WM_WINDOWPOSCHANGED: {
- // Save our position.
- WINDOWPLACEMENT wp = { sizeof(WINDOWPLACEMENT) };
- GetWindowPlacement(hWnd, &wp);
- if (win->dwStyle & WS_POPUP)
- wp.showCmd = SW_MAXIMIZE;
- RegSetValueExW(tigrRegKey, win->wtitle, 0, REG_BINARY, (BYTE*)&wp, sizeof(wp));
- return DefWindowProcW(hWnd, message, wParam, lParam);
- }
-#endif
- case WM_ACTIVATE:
- if (win) {
- memset(win->keys, 0, 256);
- memset(win->prev, 0, 256);
- win->lastChar = 0;
- }
- return 0;
- case WM_CHAR:
- if (win) {
- if (wParam == '\r') {
- wParam = '\n';
- }
- int repeating = (HIWORD(lParam) & KF_REPEAT) == KF_REPEAT;
- if (!repeating) {
- win->lastChar = wParam;
- }
- }
- return DefWindowProcW(hWnd, message, wParam, lParam);
- case WM_MENUCHAR:
- // Disable beep on Alt+Enter
- if (LOWORD(wParam) == VK_RETURN)
- return MNC_CLOSE << 16;
- return DefWindowProcW(hWnd, message, wParam, lParam);
- case WM_SYSKEYDOWN:
- if (win) {
- if (wParam == VK_RETURN) {
- // Alt+Enter
- if (win->dwStyle & WS_POPUP)
- tigrLeaveBorderlessWindowed(bmp);
- else
- tigrEnterBorderlessWindowed(bmp);
- return 0;
- }
- }
- // fall-thru
- case WM_KEYDOWN:
- if (win)
- win->keys[wParam] = 1;
- return DefWindowProcW(hWnd, message, wParam, lParam);
- case WM_SYSKEYUP:
- // fall-thru
- case WM_KEYUP:
- if (win)
- win->keys[wParam] = 0;
- return DefWindowProcW(hWnd, message, wParam, lParam);
- case WM_MOUSEWHEEL:
- if (win)
- win->scrollDeltaY += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
- return DefWindowProcW(hWnd, message, wParam, lParam);
- case WM_MOUSEHWHEEL:
- if (win)
- win->scrollDeltaX += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
- return DefWindowProcW(hWnd, message, wParam, lParam);
- default:
- return DefWindowProcW(hWnd, message, wParam, lParam);
- }
- return 0;
-}
-
-Tigr* tigrWindow(int w, int h, const char* title, int flags) {
- WNDCLASSEXW wcex = { 0 };
- int maxW, maxH, scale;
- HWND hWnd;
- DWORD dwStyle;
- RECT rc;
- Tigr* bmp;
- TigrInternal* win;
-#ifndef TIGR_DO_NOT_PRESERVE_WINDOW_POSITION
- WINDOWPLACEMENT wp;
- DWORD wpsize = sizeof(wp);
-#endif
-
- wchar_t* wtitle = unicode(title);
-
-// Find our registry key.
-#ifndef TIGR_DO_NOT_PRESERVE_WINDOW_POSITION
- RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\TIGR", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
- &tigrRegKey, NULL);
-#endif
-
- // Register a window class.
- wcex.cbSize = sizeof(WNDCLASSEXW);
- wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
- wcex.lpfnWndProc = tigrWndProc;
- wcex.hInstance = GetModuleHandle(NULL);
- wcex.hIcon = NULL;
- wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
- wcex.lpszClassName = L"TIGR";
- RegisterClassExW(&wcex);
-
- if (flags & TIGR_AUTO) {
- // Always use a 1:1 pixel size.
- scale = 1;
- } else {
- // See how big we can make it and still fit on-screen.
- maxW = GetSystemMetrics(SM_CXSCREEN) * 3 / 4;
- maxH = GetSystemMetrics(SM_CYSCREEN) * 3 / 4;
- scale = tigrCalcScale(w, h, maxW, maxH);
- }
-
- scale = tigrEnforceScale(scale, flags);
-
- // Get the final window size.
- dwStyle = WS_OVERLAPPEDWINDOW;
- rc.left = 0;
- rc.top = 0;
- rc.right = w * scale;
- rc.bottom = h * scale;
- AdjustWindowRect(&rc, dwStyle, FALSE);
-
- // Make a window.
- hWnd = CreateWindowW(L"TIGR", wtitle, dwStyle, CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top,
- NULL, NULL, wcex.hInstance, NULL);
- if (!hWnd)
- ExitProcess(1);
-
- if (flags & TIGR_NOCURSOR) {
- ShowCursor(FALSE);
- }
-
- // Wrap a bitmap around it.
- bmp = tigrBitmap2(w, h, sizeof(TigrInternal));
- bmp->handle = hWnd;
-
- // Set up the Windows parts.
- win = tigrInternal(bmp);
- win->dwStyle = dwStyle;
- win->wtitle = wtitle;
- win->shown = 0;
- win->closed = 0;
- win->scale = scale;
- win->lastChar = 0;
- win->flags = flags;
-
- win->p1 = win->p2 = win->p3 = 0;
- win->p4 = 1;
-
- win->widgetsWanted = 0;
- win->widgetAlpha = 0;
- win->widgetsScale = WIDGET_SCALE;
- win->widgets = tigrBitmap(40, 14);
-
- SetPropW(hWnd, L"Tigr", bmp);
-
- tigrGAPICreate(bmp);
-
- if (flags & TIGR_FULLSCREEN) {
- tigrEnterBorderlessWindowed(bmp);
- } else {
-// Try and restore our window position.
-#ifndef TIGR_DO_NOT_PRESERVE_WINDOW_POSITION
- if (RegQueryValueExW(tigrRegKey, wtitle, NULL, NULL, (BYTE*)&wp, &wpsize) == ERROR_SUCCESS) {
- if (wp.showCmd == SW_MAXIMIZE)
- tigrEnterBorderlessWindowed(bmp);
- else
- SetWindowPlacement(hWnd, &wp);
- }
-#endif
- }
-
- wglSwapIntervalEXT_ = (PFNWGLSWAPINTERVALFARPROC_)wglGetProcAddress("wglSwapIntervalEXT");
- if (wglSwapIntervalEXT_)
- wglSwapIntervalEXT_(1);
-
- return bmp;
-}
-
-void tigrFree(Tigr* bmp) {
- if (bmp->handle) {
- TigrInternal* win = tigrInternal(bmp);
- tigrGAPIDestroy(bmp);
-
- if (win->gl.hglrc && !wglDeleteContext(win->gl.hglrc)) {
- tigrError(bmp, "Cannot delete OpenGL context.\n");
- }
- win->gl.hglrc = NULL;
-
- if (win->gl.dc && !ReleaseDC((HWND)bmp->handle, win->gl.dc)) {
- tigrError(bmp, "Cannot release OpenGL device context.\n");
- }
- win->gl.dc = NULL;
-
- DestroyWindow((HWND)bmp->handle);
- free(win->wtitle);
- tigrFree(win->widgets);
- }
- free(bmp->pix);
- free(bmp);
-}
-
-int tigrClosed(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- int val = win->closed;
- win->closed = 0;
- return val;
-}
-
-float tigrTime(void) {
- static int first = 1;
- static LARGE_INTEGER prev;
-
- LARGE_INTEGER cnt, freq;
- ULONGLONG diff;
- QueryPerformanceCounter(&cnt);
- QueryPerformanceFrequency(&freq);
-
- if (first) {
- first = 0;
- prev = cnt;
- }
-
- diff = cnt.QuadPart - prev.QuadPart;
- prev = cnt;
- return (float)(diff / (double)freq.QuadPart);
-}
-
-void tigrMouse(Tigr* bmp, int* x, int* y, int* buttons) {
- POINT pt;
- TigrInternal* win;
-
- win = tigrInternal(bmp);
- GetCursorPos(&pt);
- ScreenToClient((HWND)bmp->handle, &pt);
- *x = (pt.x - win->pos[0]) / win->scale;
- *y = (pt.y - win->pos[1]) / win->scale;
- *buttons = 0;
- if (GetFocus() != bmp->handle)
- return;
- if (GetAsyncKeyState(VK_LBUTTON) & 0x8000)
- *buttons |= 1;
- if (GetAsyncKeyState(VK_MBUTTON) & 0x8000)
- *buttons |= 2;
- if (GetAsyncKeyState(VK_RBUTTON) & 0x8000)
- *buttons |= 4;
-}
-
-int tigrTouch(Tigr* bmp, TigrTouchPoint* points, int maxPoints) {
- int buttons = 0;
- if (maxPoints > 0) {
- tigrMouse(bmp, &points[0].x, &points[1].y, &buttons);
- }
- return buttons ? 1 : 0;
-}
-
-void tigrScrollWheel(Tigr* bmp, float* x, float* y) {
- TigrInternal* win;
- win = tigrInternal(bmp);
- if (x) {
- *x = win->scrollDeltaX;
- }
- if (y) {
- *y = win->scrollDeltaY;
- }
-}
-
-static int tigrWinVK(int key) {
- if (key >= 'A' && key <= 'Z')
- return key;
- if (key >= '0' && key <= '9')
- return key;
- switch (key) {
- case TK_BACKSPACE:
- return VK_BACK;
- case TK_TAB:
- return VK_TAB;
- case TK_RETURN:
- return VK_RETURN;
- case TK_SHIFT:
- return VK_SHIFT;
- case TK_CONTROL:
- return VK_CONTROL;
- case TK_ALT:
- return VK_MENU;
- case TK_PAUSE:
- return VK_PAUSE;
- case TK_CAPSLOCK:
- return VK_CAPITAL;
- case TK_ESCAPE:
- return VK_ESCAPE;
- case TK_SPACE:
- return VK_SPACE;
- case TK_PAGEUP:
- return VK_PRIOR;
- case TK_PAGEDN:
- return VK_NEXT;
- case TK_END:
- return VK_END;
- case TK_HOME:
- return VK_HOME;
- case TK_LEFT:
- return VK_LEFT;
- case TK_UP:
- return VK_UP;
- case TK_RIGHT:
- return VK_RIGHT;
- case TK_DOWN:
- return VK_DOWN;
- case TK_INSERT:
- return VK_INSERT;
- case TK_DELETE:
- return VK_DELETE;
- case TK_LWIN:
- return VK_LWIN;
- case TK_RWIN:
- return VK_RWIN;
- // case TK_APPS: return VK_APPS; // this key doesn't exist on OS X
- case TK_PAD0:
- return VK_NUMPAD0;
- case TK_PAD1:
- return VK_NUMPAD1;
- case TK_PAD2:
- return VK_NUMPAD2;
- case TK_PAD3:
- return VK_NUMPAD3;
- case TK_PAD4:
- return VK_NUMPAD4;
- case TK_PAD5:
- return VK_NUMPAD5;
- case TK_PAD6:
- return VK_NUMPAD6;
- case TK_PAD7:
- return VK_NUMPAD7;
- case TK_PAD8:
- return VK_NUMPAD8;
- case TK_PAD9:
- return VK_NUMPAD9;
- case TK_PADMUL:
- return VK_MULTIPLY;
- case TK_PADADD:
- return VK_ADD;
- case TK_PADENTER:
- return VK_SEPARATOR;
- case TK_PADSUB:
- return VK_SUBTRACT;
- case TK_PADDOT:
- return VK_DECIMAL;
- case TK_PADDIV:
- return VK_DIVIDE;
- case TK_F1:
- return VK_F1;
- case TK_F2:
- return VK_F2;
- case TK_F3:
- return VK_F3;
- case TK_F4:
- return VK_F4;
- case TK_F5:
- return VK_F5;
- case TK_F6:
- return VK_F6;
- case TK_F7:
- return VK_F7;
- case TK_F8:
- return VK_F8;
- case TK_F9:
- return VK_F9;
- case TK_F10:
- return VK_F10;
- case TK_F11:
- return VK_F11;
- case TK_F12:
- return VK_F12;
- case TK_NUMLOCK:
- return VK_NUMLOCK;
- case TK_SCROLL:
- return VK_SCROLL;
- case TK_LSHIFT:
- return VK_LSHIFT;
- case TK_RSHIFT:
- return VK_RSHIFT;
- case TK_LCONTROL:
- return VK_LCONTROL;
- case TK_RCONTROL:
- return VK_RCONTROL;
- case TK_LALT:
- return VK_LMENU;
- case TK_RALT:
- return VK_RMENU;
- case TK_SEMICOLON:
- return VK_OEM_1;
- case TK_EQUALS:
- return VK_OEM_PLUS;
- case TK_COMMA:
- return VK_OEM_COMMA;
- case TK_MINUS:
- return VK_OEM_MINUS;
- case TK_DOT:
- return VK_OEM_PERIOD;
- case TK_SLASH:
- return VK_OEM_2;
- case TK_BACKTICK:
- return VK_OEM_3;
- case TK_LSQUARE:
- return VK_OEM_4;
- case TK_BACKSLASH:
- return VK_OEM_5;
- case TK_RSQUARE:
- return VK_OEM_6;
- case TK_TICK:
- return VK_OEM_7;
- }
- return 0;
-}
-
-int tigrKeyDown(Tigr* bmp, int key) {
- TigrInternal* win;
- int k = tigrWinVK(key);
- if (GetFocus() != bmp->handle)
- return 0;
- win = tigrInternal(bmp);
- return win->keys[k] && !win->prev[k];
-}
-
-int tigrKeyHeld(Tigr* bmp, int key) {
- TigrInternal* win;
- int k = tigrWinVK(key);
- if (GetFocus() != bmp->handle)
- return 0;
- win = tigrInternal(bmp);
- return win->keys[k];
-}
-
-int tigrReadChar(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- int c = win->lastChar;
- win->lastChar = 0;
- return c;
-}
-
-// We supply our own WinMain and just chain through to the user's
-// real entry point.
-#ifdef UNICODE
-int CALLBACK wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
-#else
-int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
-#endif
-{
- int n, argc;
- LPWSTR* wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
- char** argv = (char**)calloc(argc + 1, sizeof(int));
-
- (void)hInstance;
- (void)hPrevInstance;
- (void)lpCmdLine;
- (void)nCmdShow;
-
- for (n = 0; n < argc; n++) {
- int len = WideCharToMultiByte(CP_UTF8, 0, wargv[n], -1, 0, 0, NULL, NULL);
- argv[n] = (char*)malloc(len);
- WideCharToMultiByte(CP_UTF8, 0, wargv[n], -1, argv[n], len, NULL, NULL);
- }
- return main(argc, argv);
-}
-#endif
-
-#endif // #ifndef TIGR_HEADLESS
-//////// End of inlined file: tigr_win.c ////////
-
-//////// Start of inlined file: tigr_osx.c ////////
-
-#ifndef TIGR_HEADLESS
-
-// originally based on https://github.com/jimon/osx_app_in_plain_c
-
-//#include "tigr_internal.h"
-
-#if __MACOS__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
-//////// Start of inlined file: tigr_objc.h ////////
-
-#ifndef TIGR_OBJC_H
-#define TIGR_OBJC_H
-
-#if defined(__IOS__) || defined(__MACOS__)
-
-#if defined(__OBJC__) && __has_feature(objc_arc)
-#error "Can't compile as objective-c code!"
-#endif
-
-// ABI is a bit different between platforms
-#ifdef __arm64__
-#define abi_objc_msgSend_stret objc_msgSend
-#else
-#define abi_objc_msgSend_stret objc_msgSend_stret
-#endif
-#ifdef __i386__
-#define abi_objc_msgSend_fpret objc_msgSend_fpret
-#else
-#define abi_objc_msgSend_fpret objc_msgSend
-#endif
-
-#define objc_msgSendSuper_t(RET, ...) ((RET(*)(struct objc_super*, SEL, ##__VA_ARGS__))objc_msgSendSuper)
-#define objc_msgSend_t(RET, ...) ((RET(*)(id, SEL, ##__VA_ARGS__))objc_msgSend)
-#define objc_msgSend_stret_t(RET, ...) ((RET(*)(id, SEL, ##__VA_ARGS__))abi_objc_msgSend_stret)
-#define objc_msgSend_id objc_msgSend_t(id)
-#define objc_msgSend_void objc_msgSend_t(void)
-#define objc_msgSend_void_id objc_msgSend_t(void, id)
-#define objc_msgSend_void_bool objc_msgSend_t(void, bool)
-
-#define sel(NAME) sel_registerName(NAME)
-#define class(NAME) ((id)objc_getClass(NAME))
-#define makeClass(NAME, SUPER) objc_allocateClassPair((Class)objc_getClass(SUPER), NAME, 0)
-
-// Check here to get the signature right: https://nshipster.com/type-encodings/
-#define addMethod(CLASS, NAME, IMPL, SIGNATURE) \
- if (!class_addMethod(CLASS, sel(NAME), (IMP)(IMPL), (SIGNATURE))) \
- assert(false)
-
-#define addIvar(CLASS, NAME, SIZE, SIGNATURE) \
- if (!class_addIvar(CLASS, NAME, SIZE, rint(log2(SIZE)), SIGNATURE)) \
- assert(false)
-
-#define objc_alloc(CLASS) objc_msgSend_id(class(CLASS), sel("alloc"))
-
-#if __LP64__ || NS_BUILD_32_LIKE_64
-#define NSIntegerEncoding "q"
-#define NSUIntegerEncoding "L"
-#else
-#define NSIntegerEncoding "i"
-#define NSUIntegerEncoding "I"
-#endif
-
-#endif // defined(__IOS__) || defined (__MACOS__)
-#endif // TIGR_OBJC_H
-
-//////// End of inlined file: tigr_objc.h ////////
-
-#pragma clang diagnostic pop
-
-#include <assert.h>
-#include <limits.h>
-#include <math.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <CoreFoundation/CoreFoundation.h>
-#include <CoreGraphics/CoreGraphics.h>
-#include <mach/mach_time.h>
-#include <objc/NSObjCRuntime.h>
-#include <objc/message.h>
-#include <objc/objc.h>
-#include <objc/runtime.h>
-
-#include <CoreGraphics/CGBase.h>
-#include <CoreGraphics/CGGeometry.h>
-
-typedef CGPoint NSPoint;
-typedef CGSize NSSize;
-typedef CGRect NSRect;
-
-enum {
- NSKeyDown = 10,
- NSKeyDownMask = 1 << NSKeyDown,
- NSKeyUp = 11,
- NSKeyUpMask = 1 << NSKeyUp,
-};
-
-NSUInteger NSAllEventMask = NSUIntegerMax;
-
-extern id NSApp;
-extern id const NSDefaultRunLoopMode;
-
-#define NSApplicationActivationPolicyRegular 0
-
-bool terminated = false;
-
-static double _tigrTimestamp = 0;
-
-double _currentMediaTime(void) {
- uint64_t now = clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
- double current = now * 1.0E-9;
- return current;
-}
-
-TigrInternal* _tigrInternalFromWindowDelegate(id wdg) {
- Tigr* bmp = 0;
- object_getInstanceVariable(wdg, "tigrHandle", (void**)&bmp);
- return bmp ? tigrInternal(bmp) : NULL;
-}
-
-TigrInternal* _tigrInternalFromWindow(id window) {
- if (!window)
- return NULL;
-
- id wdg = objc_msgSend_id(window, sel("delegate"));
- if (!wdg)
- return NULL;
-
- return _tigrInternalFromWindowDelegate(wdg);
-}
-
-NSUInteger applicationShouldTerminate(id self, SEL sel, id sender) {
- terminated = true;
- return 0;
-}
-
-void windowWillClose(id self, SEL _sel, id notification) {
- NSUInteger value = true;
- object_setInstanceVariable(self, "closed", (void*)value);
- object_setInstanceVariable(self, "tigrHandle", (void*)0);
-}
-
-void windowDidEnterFullScreen(id self, SEL _sel, id notification) {
- NSUInteger value = true;
- object_setInstanceVariable(self, "visible", (void*)value);
-}
-
-void windowDidResize(id self, SEL _sel, id notification) {
- TigrInternal* win;
- Tigr* bmp = 0;
- object_getInstanceVariable(self, "tigrHandle", (void**)&bmp);
- win = bmp ? tigrInternal(bmp) : NULL;
- if (win) {
- win->mouseButtons = 0;
- }
-}
-
-void windowDidBecomeKey(id self, SEL _sel, id notification) {
- TigrInternal* win;
- Tigr* bmp = 0;
- object_getInstanceVariable(self, "tigrHandle", (void**)&bmp);
- win = bmp ? tigrInternal(bmp) : NULL;
-
- if (win) {
- memset(win->keys, 0, 256);
- memset(win->prev, 0, 256);
- win->lastChar = 0;
- win->mouseButtons = 0;
- }
-}
-
-void onFrame(id self, SEL _sel, id displayLink) {
- double timestamp = objc_msgSend_t(double)(displayLink, sel("timestamp"));
- TigrInternal* win = _tigrInternalFromWindowDelegate(self);
- if (win == NULL) {
- return;
- }
- win->frameTime = timestamp;
-}
-
-void mouseEntered(id self, SEL _sel, id event) {
- id window = objc_msgSend_id(event, sel("window"));
- TigrInternal* win = _tigrInternalFromWindow(window);
- if (win) {
- win->mouseInView = 1;
- if (win->flags & TIGR_NOCURSOR) {
- objc_msgSend_id(class("NSCursor"), sel("hide"));
- }
- }
-}
-
-void mouseExited(id self, SEL _sel, id event) {
- id window = objc_msgSend_id(event, sel("window"));
- TigrInternal* win = _tigrInternalFromWindow(window);
- if (win) {
- win->mouseInView = 0;
- if (win->flags & TIGR_NOCURSOR) {
- objc_msgSend_id(class("NSCursor"), sel("unhide"));
- }
- }
-}
-
-bool _tigrIsWindowClosed(id window) {
- id wdg = objc_msgSend_id(window, sel("delegate"));
- if (!wdg)
- return false;
- NSUInteger value = 0;
- object_getInstanceVariable(wdg, "closed", (void**)&value);
- return value ? true : false;
-}
-
-bool _tigrIsWindowVisible(id window) {
- id wdg = objc_msgSend_id(window, sel("delegate"));
- if (!wdg)
- return false;
- NSUInteger value = 0;
- object_getInstanceVariable(wdg, "visible", (void**)&value);
- return value ? true : false;
-}
-
-static bool tigrOSXInited = false;
-static id autoreleasePool = NULL;
-
-#ifdef DEBUG
-static void _showPools(const char* context) {
- fprintf(stderr, "NSAutoreleasePool@%s:\n", context);
- objc_msgSend(class("NSAutoreleasePool"), sel("showPools"));
-}
-#define showPools(x) _showPools((x))
-#else
-#define showPools(x)
-#endif
-
-static id pushPool(void) {
- id pool = objc_msgSend_id(class("NSAutoreleasePool"), sel("alloc"));
- return objc_msgSend_id(pool, sel("init"));
-}
-
-static void popPool(id pool) {
- objc_msgSend_void(pool, sel("drain"));
-}
-
-void _tigrCleanupOSX(void) {
- showPools("cleanup");
- popPool(autoreleasePool);
-}
-
-void tigrInitOSX(void) {
- if (tigrOSXInited)
- return;
-
- atexit(&_tigrCleanupOSX);
-
- autoreleasePool = pushPool();
-
- showPools("init start");
-
- objc_msgSend_id(class("NSApplication"), sel("sharedApplication"));
- objc_msgSend_t(void, NSInteger)(NSApp, sel("setActivationPolicy:"), NSApplicationActivationPolicyRegular);
-
- Class appDelegateClass = makeClass("AppDelegate", "NSObject");
- addMethod(appDelegateClass, "applicationShouldTerminate", applicationShouldTerminate, NSUIntegerEncoding "@:@");
- id dgAlloc = objc_msgSend_id((id)appDelegateClass, sel("alloc"));
- id dg = objc_msgSend_id(dgAlloc, sel("init"));
-
- objc_msgSend_void_id(NSApp, sel("setDelegate:"), dg);
- objc_msgSend_void(NSApp, sel("finishLaunching"));
-
- id menuBar = objc_alloc("NSMenu");
- menuBar = objc_msgSend_id(menuBar, sel("init"));
-
- id appMenuItem = objc_alloc("NSMenuItem");
- appMenuItem = objc_msgSend_id(appMenuItem, sel("init"));
-
- objc_msgSend_void_id(menuBar, sel("addItem:"), appMenuItem);
- objc_msgSend_t(id, id)(NSApp, sel("setMainMenu:"), menuBar);
-
- id processInfo = objc_msgSend_id(class("NSProcessInfo"), sel("processInfo"));
- id appName = objc_msgSend_id(processInfo, sel("processName"));
-
- id appMenu = objc_alloc("NSMenu");
- appMenu = objc_msgSend_t(id, id)(appMenu, sel("initWithTitle:"), appName);
-
- id quitTitlePrefixString =
- objc_msgSend_t(id, const char*)(class("NSString"), sel("stringWithUTF8String:"), "Quit ");
- id quitTitle = objc_msgSend_t(id, id)(quitTitlePrefixString, sel("stringByAppendingString:"), appName);
-
- id quitMenuItemKey = objc_msgSend_t(id, const char*)(class("NSString"), sel("stringWithUTF8String:"), "q");
- id quitMenuItem = objc_alloc("NSMenuItem");
- quitMenuItem = objc_msgSend_t(id, id, SEL, id)(quitMenuItem, sel("initWithTitle:action:keyEquivalent:"), quitTitle,
- sel("terminate:"), quitMenuItemKey);
-
- objc_msgSend_void_id(appMenu, sel("addItem:"), quitMenuItem);
- objc_msgSend_void_id(appMenuItem, sel("setSubmenu:"), appMenu);
-
- tigrOSXInited = true;
-
- showPools("init end");
-}
-
-void tigrError(Tigr* bmp, const char* message, ...) {
- char tmp[1024];
-
- va_list args;
- va_start(args, message);
- vsnprintf(tmp, sizeof(tmp), message, args);
- tmp[sizeof(tmp) - 1] = 0;
- va_end(args);
-
- printf("tigr fatal error: %s\n", tmp);
-
- exit(1);
-}
-
-NSSize _tigrContentBackingSize(id window) {
- id contentView = objc_msgSend_id(window, sel("contentView"));
- NSRect rect = objc_msgSend_stret_t(NSRect)(contentView, sel("frame"));
- rect = objc_msgSend_stret_t(NSRect, NSRect)(contentView, sel("convertRectToBacking:"), rect);
-
- return rect.size;
-}
-
-enum {
- NSWindowStyleMaskTitled = 1 << 0,
- NSWindowStyleMaskClosable = 1 << 1,
- NSWindowStyleMaskMiniaturizable = 1 << 2,
- NSWindowStyleMaskResizable = 1 << 3,
- NSWindowStyleRegular = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable |
- NSWindowStyleMaskResizable,
- NSWindowStyleMaskFullSizeContentView = 1 << 15
-};
-
-Tigr* tigrWindow(int w, int h, const char* title, int flags) {
- Tigr* bmp;
- TigrInternal* win;
-
- tigrInitOSX();
-
- NSUInteger windowStyleMask = NSWindowStyleRegular & ~NSWindowStyleMaskMiniaturizable;
-
- // In AUTO mode, window follows requested size, unless downscaled by tigrEnforceScale below.
- int windowScale = 1;
-
- // In non-AUTO mode, see how big we can make it and still fit on-screen.
- if ((flags & TIGR_AUTO) == 0) {
- CGRect mainMonitor = CGDisplayBounds(CGMainDisplayID());
- int maxW = CGRectGetWidth(mainMonitor);
- int maxH = CGRectGetHeight(mainMonitor);
- NSRect screen = { { 0, 0 }, { maxW, maxH } };
- NSRect content = objc_msgSend_stret_t(NSRect, NSRect, NSUInteger)(
- class("NSWindow"), sel("contentRectForFrameRect:styleMask:"), screen, windowStyleMask);
- windowScale = tigrCalcScale(w, h, content.size.width, content.size.height);
- }
-
- windowScale = tigrEnforceScale(windowScale, flags);
-
- NSRect rect = { { 0, 0 }, { w * windowScale, h * windowScale } };
- id windowAlloc = objc_msgSend_id(class("NSWindow"), sel("alloc"));
- id window = ((id(*)(id, SEL, NSRect, NSUInteger, NSUInteger, BOOL))objc_msgSend)(
- windowAlloc, sel("initWithContentRect:styleMask:backing:defer:"), rect, windowStyleMask, 2, NO);
-
- objc_msgSend_void_bool(window, sel("setReleasedWhenClosed:"), NO);
-
- Class WindowDelegateClass = objc_allocateClassPair((Class)objc_getClass("NSObject"), "WindowDelegate", 0);
- addIvar(WindowDelegateClass, "closed", sizeof(NSUInteger), NSUIntegerEncoding);
- addIvar(WindowDelegateClass, "visible", sizeof(NSUInteger), NSUIntegerEncoding);
- addIvar(WindowDelegateClass, "tigrHandle", sizeof(void*), "ˆv");
- addMethod(WindowDelegateClass, "windowWillClose:", windowWillClose, "v@:@");
- addMethod(WindowDelegateClass, "windowDidEnterFullScreen:", windowDidEnterFullScreen, "v@:@");
- addMethod(WindowDelegateClass, "windowDidResize:", windowDidResize, "v@:@");
- addMethod(WindowDelegateClass, "windowDidBecomeKey:", windowDidBecomeKey, "v@:@");
- addMethod(WindowDelegateClass, "mouseEntered:", mouseEntered, "v@:@");
- addMethod(WindowDelegateClass, "mouseExited:", mouseExited, "v@:@");
- addMethod(WindowDelegateClass, "onFrame:", onFrame, "v@:@");
-
- id wdgAlloc = objc_msgSend_id((id)WindowDelegateClass, sel("alloc"));
- id wdg = objc_msgSend_id(wdgAlloc, sel("init"));
-
- if (flags & TIGR_FULLSCREEN) {
- objc_msgSend_void_id(window, sel("toggleFullScreen:"), window);
- if (flags & TIGR_NOCURSOR) {
- objc_msgSend_id(class("NSCursor"), sel("hide"));
- }
- } else {
- NSUInteger value = true;
- object_setInstanceVariable(wdg, "visible", (void*)value);
- }
-
- objc_msgSend_void_id(window, sel("setDelegate:"), wdg);
-
- id contentView = objc_msgSend_id(window, sel("contentView"));
-
- objc_msgSend_void_bool(contentView, sel("setWantsLayer:"), NO);
-
- bool useDisplayLink = objc_msgSend_t(bool, SEL)(class("NSView"), sel("instancesRespondToSelector:"),
- sel("displayLinkWithTarget:selector:"));
-
- if (useDisplayLink) {
- // Use display link to pace frames, if possible
- id link =
- objc_msgSend_t(id, id, SEL)(contentView, sel("displayLinkWithTarget:selector:"), wdg, sel("onFrame:"));
- id runLoop = objc_msgSend_id(class("NSRunLoop"), sel("currentRunLoop"));
- objc_msgSend_t(void, id, id)(link, sel("addToRunLoop:forMode:"), runLoop, NSDefaultRunLoopMode);
- objc_msgSend_void_bool(link, sel("setPaused:"), NO);
- }
-
- int wantsHighRes = (flags & TIGR_RETINA);
- objc_msgSend_void_bool(contentView, sel("setWantsBestResolutionOpenGLSurface:"), wantsHighRes);
-
- NSPoint point = { 20, 20 };
- ((void (*)(id, SEL, NSPoint))objc_msgSend)(window, sel("cascadeTopLeftFromPoint:"), point);
-
- id titleString = objc_msgSend_t(id, const char*)(class("NSString"), sel("stringWithUTF8String:"), title);
- objc_msgSend_void_id(window, sel("setTitle:"), titleString);
-
- uint32_t glAttributes[] = { 8, 24, // NSOpenGLPFAColorSize, 24,
- 11, 8, // NSOpenGLPFAAlphaSize, 8,
- 5, // NSOpenGLPFADoubleBuffer,
- 73, // NSOpenGLPFAAccelerated,
- // 72, // NSOpenGLPFANoRecovery,
- // 55, 1, // NSOpenGLPFASampleBuffers, 1,
- // 56, 4, // NSOpenGLPFASamples, 4,
- 99, 0x3200, // NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
- // 70, 0x00020400, // NSOpenGLPFARendererID, kCGLRendererGenericFloatID
- 0 };
-
- id pixelFormat = objc_alloc("NSOpenGLPixelFormat");
- pixelFormat = objc_msgSend_t(id, const uint32_t*)(pixelFormat, sel("initWithAttributes:"), glAttributes);
- objc_msgSend_void(pixelFormat, sel("autorelease"));
-
- id openGLContext = objc_alloc("NSOpenGLContext");
- openGLContext = objc_msgSend_t(id, id, id)(openGLContext, sel("initWithFormat:shareContext:"), pixelFormat, nil);
-
- objc_msgSend_void_id(openGLContext, sel("setView:"), contentView);
- objc_msgSend_void_id(window, sel("makeKeyAndOrderFront:"), window);
- objc_msgSend_void_bool(window, sel("setAcceptsMouseMovedEvents:"), YES);
-
- id blackColor = objc_msgSend_id(class("NSColor"), sel("blackColor"));
- objc_msgSend_void_id(window, sel("setBackgroundColor:"), blackColor);
-
- objc_msgSend_void_bool(NSApp, sel("activateIgnoringOtherApps:"), YES);
-
- NSSize windowContentSize = _tigrContentBackingSize(window);
-
- // In AUTO mode, always use a 1:1 pixel size, unless downscaled by tigrEnforceScale below.
- int bitmapScale = 1;
-
- // In non-AUTO mode, scale based on backing size
- if ((flags & TIGR_AUTO) == 0) {
- bitmapScale = tigrEnforceScale(tigrCalcScale(w, h, windowContentSize.width, windowContentSize.height), flags);
- } else {
- // In AUTO mode, bitmap size follows window size
- w = windowContentSize.width / windowScale;
- h = windowContentSize.height / windowScale;
- bitmapScale = tigrEnforceScale(bitmapScale, flags);
- }
-
- bmp = tigrBitmap2(w, h, sizeof(TigrInternal));
- bmp->handle = window;
-
- // Set the handle
- object_setInstanceVariable(wdg, "tigrHandle", (void*)bmp);
-
- {
-#define NSTrackingMouseEnteredAndExited 1
-#define NSTrackingActiveInKeyWindow 0x20
-#define NSTrackingInVisibleRect 0x200
-
- int trackingFlags = NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect;
- id trackingArea = objc_msgSend_id(class("NSTrackingArea"), sel("alloc"));
- trackingArea = objc_msgSend_t(id, NSRect, int, id, id)(
- trackingArea, sel("initWithRect:options:owner:userInfo:"), rect, trackingFlags, wdg, 0);
- objc_msgSend_void_id(contentView, sel("addTrackingArea:"), trackingArea);
- }
-
- // Set up the Windows parts.
- win = tigrInternal(bmp);
- win->shown = 0;
- win->closed = 0;
- win->scale = bitmapScale;
- win->lastChar = 0;
- win->flags = flags;
- win->p1 = win->p2 = win->p3 = 0;
- win->p4 = 1;
- win->widgetsWanted = 0;
- win->widgetAlpha = 0;
- win->widgetsScale = 0;
- win->widgets = 0;
- win->gl.gl_legacy = 0;
- win->gl.glContext = openGLContext;
- win->mouseButtons = 0;
- win->mouseInView = 0;
- win->frameTime = 0;
-
- tigrPosition(bmp, win->scale, bmp->w, bmp->h, win->pos);
-
- GLint swapInterval = 1;
- NSInteger NSOpenGLCPSwapInterval = 222;
-
- objc_msgSend_t(void, GLint*, NSInteger)(openGLContext, sel("setValues:forParameter:"), &swapInterval,
- NSOpenGLCPSwapInterval);
-
- objc_msgSend_void(openGLContext, sel("makeCurrentContext"));
-
- tigrGAPICreate(bmp);
-
- return bmp;
-}
-
-void tigrFree(Tigr* bmp) {
- if (bmp->handle) {
- TigrInternal* win = tigrInternal(bmp);
- tigrGAPIDestroy(bmp);
-
- id window = (id)bmp->handle;
-
- if (!_tigrIsWindowClosed(window) && !terminated) {
- objc_msgSend_void(window, sel("close"));
- }
-
- if (win->flags & TIGR_NOCURSOR) {
- objc_msgSend_id(class("NSCursor"), sel("unhide"));
- }
-
- id wdg = objc_msgSend_id(window, sel("delegate"));
- objc_msgSend_void(wdg, sel("release"));
- objc_msgSend_void((id)win->gl.glContext, sel("release"));
- objc_msgSend_void(window, sel("release"));
- }
- free(bmp->pix);
- free(bmp);
-}
-
-uint8_t _tigrKeyFromOSX(uint16_t key) {
- // from Carbon HIToolbox/Events.h
- enum {
- kVK_ANSI_A = 0x00,
- kVK_ANSI_S = 0x01,
- kVK_ANSI_D = 0x02,
- kVK_ANSI_F = 0x03,
- kVK_ANSI_H = 0x04,
- kVK_ANSI_G = 0x05,
- kVK_ANSI_Z = 0x06,
- kVK_ANSI_X = 0x07,
- kVK_ANSI_C = 0x08,
- kVK_ANSI_V = 0x09,
- kVK_ANSI_B = 0x0B,
- kVK_ANSI_Q = 0x0C,
- kVK_ANSI_W = 0x0D,
- kVK_ANSI_E = 0x0E,
- kVK_ANSI_R = 0x0F,
- kVK_ANSI_Y = 0x10,
- kVK_ANSI_T = 0x11,
- kVK_ANSI_1 = 0x12,
- kVK_ANSI_2 = 0x13,
- kVK_ANSI_3 = 0x14,
- kVK_ANSI_4 = 0x15,
- kVK_ANSI_6 = 0x16,
- kVK_ANSI_5 = 0x17,
- kVK_ANSI_Equal = 0x18,
- kVK_ANSI_9 = 0x19,
- kVK_ANSI_7 = 0x1A,
- kVK_ANSI_Minus = 0x1B,
- kVK_ANSI_8 = 0x1C,
- kVK_ANSI_0 = 0x1D,
- kVK_ANSI_RightBracket = 0x1E,
- kVK_ANSI_O = 0x1F,
- kVK_ANSI_U = 0x20,
- kVK_ANSI_LeftBracket = 0x21,
- kVK_ANSI_I = 0x22,
- kVK_ANSI_P = 0x23,
- kVK_ANSI_L = 0x25,
- kVK_ANSI_J = 0x26,
- kVK_ANSI_Quote = 0x27,
- kVK_ANSI_K = 0x28,
- kVK_ANSI_Semicolon = 0x29,
- kVK_ANSI_Backslash = 0x2A,
- kVK_ANSI_Comma = 0x2B,
- kVK_ANSI_Slash = 0x2C,
- kVK_ANSI_N = 0x2D,
- kVK_ANSI_M = 0x2E,
- kVK_ANSI_Period = 0x2F,
- kVK_ANSI_Grave = 0x32,
- kVK_ANSI_KeypadDecimal = 0x41,
- kVK_ANSI_KeypadMultiply = 0x43,
- kVK_ANSI_KeypadPlus = 0x45,
- kVK_ANSI_KeypadClear = 0x47,
- kVK_ANSI_KeypadDivide = 0x4B,
- kVK_ANSI_KeypadEnter = 0x4C,
- kVK_ANSI_KeypadMinus = 0x4E,
- kVK_ANSI_KeypadEquals = 0x51,
- kVK_ANSI_Keypad0 = 0x52,
- kVK_ANSI_Keypad1 = 0x53,
- kVK_ANSI_Keypad2 = 0x54,
- kVK_ANSI_Keypad3 = 0x55,
- kVK_ANSI_Keypad4 = 0x56,
- kVK_ANSI_Keypad5 = 0x57,
- kVK_ANSI_Keypad6 = 0x58,
- kVK_ANSI_Keypad7 = 0x59,
- kVK_ANSI_Keypad8 = 0x5B,
- kVK_ANSI_Keypad9 = 0x5C,
- kVK_Return = 0x24,
- kVK_Tab = 0x30,
- kVK_Space = 0x31,
- kVK_Delete = 0x33,
- kVK_Escape = 0x35,
- kVK_Command = 0x37,
- kVK_Shift = 0x38,
- kVK_CapsLock = 0x39,
- kVK_Option = 0x3A,
- kVK_Control = 0x3B,
- kVK_RightShift = 0x3C,
- kVK_RightOption = 0x3D,
- kVK_RightControl = 0x3E,
- kVK_Function = 0x3F,
- kVK_F17 = 0x40,
- kVK_VolumeUp = 0x48,
- kVK_VolumeDown = 0x49,
- kVK_Mute = 0x4A,
- kVK_F18 = 0x4F,
- kVK_F19 = 0x50,
- kVK_F20 = 0x5A,
- kVK_F5 = 0x60,
- kVK_F6 = 0x61,
- kVK_F7 = 0x62,
- kVK_F3 = 0x63,
- kVK_F8 = 0x64,
- kVK_F9 = 0x65,
- kVK_F11 = 0x67,
- kVK_F13 = 0x69,
- kVK_F16 = 0x6A,
- kVK_F14 = 0x6B,
- kVK_F10 = 0x6D,
- kVK_F12 = 0x6F,
- kVK_F15 = 0x71,
- kVK_Help = 0x72,
- kVK_Home = 0x73,
- kVK_PageUp = 0x74,
- kVK_ForwardDelete = 0x75,
- kVK_F4 = 0x76,
- kVK_End = 0x77,
- kVK_F2 = 0x78,
- kVK_PageDown = 0x79,
- kVK_F1 = 0x7A,
- kVK_LeftArrow = 0x7B,
- kVK_RightArrow = 0x7C,
- kVK_DownArrow = 0x7D,
- kVK_UpArrow = 0x7E
- };
-
- switch (key) {
- case kVK_ANSI_Q:
- return 'Q';
- case kVK_ANSI_W:
- return 'W';
- case kVK_ANSI_E:
- return 'E';
- case kVK_ANSI_R:
- return 'R';
- case kVK_ANSI_T:
- return 'T';
- case kVK_ANSI_Y:
- return 'Y';
- case kVK_ANSI_U:
- return 'U';
- case kVK_ANSI_I:
- return 'I';
- case kVK_ANSI_O:
- return 'O';
- case kVK_ANSI_P:
- return 'P';
- case kVK_ANSI_A:
- return 'A';
- case kVK_ANSI_S:
- return 'S';
- case kVK_ANSI_D:
- return 'D';
- case kVK_ANSI_F:
- return 'F';
- case kVK_ANSI_G:
- return 'G';
- case kVK_ANSI_H:
- return 'H';
- case kVK_ANSI_J:
- return 'J';
- case kVK_ANSI_K:
- return 'K';
- case kVK_ANSI_L:
- return 'L';
- case kVK_ANSI_Z:
- return 'Z';
- case kVK_ANSI_X:
- return 'X';
- case kVK_ANSI_C:
- return 'C';
- case kVK_ANSI_V:
- return 'V';
- case kVK_ANSI_B:
- return 'B';
- case kVK_ANSI_N:
- return 'N';
- case kVK_ANSI_M:
- return 'M';
- case kVK_ANSI_0:
- return '0';
- case kVK_ANSI_1:
- return '1';
- case kVK_ANSI_2:
- return '2';
- case kVK_ANSI_3:
- return '3';
- case kVK_ANSI_4:
- return '4';
- case kVK_ANSI_5:
- return '5';
- case kVK_ANSI_6:
- return '6';
- case kVK_ANSI_7:
- return '7';
- case kVK_ANSI_8:
- return '8';
- case kVK_ANSI_9:
- return '9';
- case kVK_ANSI_Keypad0:
- return TK_PAD0;
- case kVK_ANSI_Keypad1:
- return TK_PAD1;
- case kVK_ANSI_Keypad2:
- return TK_PAD2;
- case kVK_ANSI_Keypad3:
- return TK_PAD3;
- case kVK_ANSI_Keypad4:
- return TK_PAD4;
- case kVK_ANSI_Keypad5:
- return TK_PAD5;
- case kVK_ANSI_Keypad6:
- return TK_PAD6;
- case kVK_ANSI_Keypad7:
- return TK_PAD7;
- case kVK_ANSI_Keypad8:
- return TK_PAD8;
- case kVK_ANSI_Keypad9:
- return TK_PAD9;
- case kVK_ANSI_KeypadMultiply:
- return TK_PADMUL;
- case kVK_ANSI_KeypadPlus:
- return TK_PADADD;
- case kVK_ANSI_KeypadEnter:
- return TK_PADENTER;
- case kVK_ANSI_KeypadMinus:
- return TK_PADSUB;
- case kVK_ANSI_KeypadDecimal:
- return TK_PADDOT;
- case kVK_ANSI_KeypadDivide:
- return TK_PADDIV;
- case kVK_F1:
- return TK_F1;
- case kVK_F2:
- return TK_F2;
- case kVK_F3:
- return TK_F3;
- case kVK_F4:
- return TK_F4;
- case kVK_F5:
- return TK_F5;
- case kVK_F6:
- return TK_F6;
- case kVK_F7:
- return TK_F7;
- case kVK_F8:
- return TK_F8;
- case kVK_F9:
- return TK_F9;
- case kVK_F10:
- return TK_F10;
- case kVK_F11:
- return TK_F11;
- case kVK_F12:
- return TK_F12;
- case kVK_Shift:
- return TK_LSHIFT;
- case kVK_Control:
- return TK_LCONTROL;
- case kVK_Option:
- return TK_LALT;
- case kVK_CapsLock:
- return TK_CAPSLOCK;
- case kVK_Command:
- return TK_LWIN;
- case kVK_Command - 1:
- return TK_RWIN;
- case kVK_RightShift:
- return TK_RSHIFT;
- case kVK_RightControl:
- return TK_RCONTROL;
- case kVK_RightOption:
- return TK_RALT;
- case kVK_Delete:
- return TK_BACKSPACE;
- case kVK_Tab:
- return TK_TAB;
- case kVK_Return:
- return TK_RETURN;
- case kVK_Escape:
- return TK_ESCAPE;
- case kVK_Space:
- return TK_SPACE;
- case kVK_PageUp:
- return TK_PAGEUP;
- case kVK_PageDown:
- return TK_PAGEDN;
- case kVK_End:
- return TK_END;
- case kVK_Home:
- return TK_HOME;
- case kVK_LeftArrow:
- return TK_LEFT;
- case kVK_UpArrow:
- return TK_UP;
- case kVK_RightArrow:
- return TK_RIGHT;
- case kVK_DownArrow:
- return TK_DOWN;
- case kVK_Help:
- return TK_INSERT;
- case kVK_ForwardDelete:
- return TK_DELETE;
- case kVK_F14:
- return TK_SCROLL;
- case kVK_F15:
- return TK_PAUSE;
- case kVK_ANSI_KeypadClear:
- return TK_NUMLOCK;
- case kVK_ANSI_Semicolon:
- return TK_SEMICOLON;
- case kVK_ANSI_Equal:
- return TK_EQUALS;
- case kVK_ANSI_Comma:
- return TK_COMMA;
- case kVK_ANSI_Minus:
- return TK_MINUS;
- case kVK_ANSI_Slash:
- return TK_SLASH;
- case kVK_ANSI_Backslash:
- return TK_BACKSLASH;
- case kVK_ANSI_Grave:
- return TK_BACKTICK;
- case kVK_ANSI_Quote:
- return TK_TICK;
- case kVK_ANSI_LeftBracket:
- return TK_LSQUARE;
- case kVK_ANSI_RightBracket:
- return TK_RSQUARE;
- case kVK_ANSI_Period:
- return TK_DOT;
- default:
- return 0;
- }
-}
-
-void _tigrOnCocoaEvent(id event, id window) {
- if (!event)
- return;
-
- TigrInternal* win = _tigrInternalFromWindow(window);
- if (!win) // just pipe the event
- {
- objc_msgSend_void_id(NSApp, sel("sendEvent:"), event);
- return;
- }
-
- NSUInteger eventType = objc_msgSend_t(NSUInteger)(event, sel("type"));
- switch (eventType) {
- case 1: // NSLeftMouseDown
- if (win->mouseInView) {
- win->mouseButtons |= 1;
- }
- break;
- case 2: // NSLeftMouseUp
- win->mouseButtons &= ~1;
- break;
- case 3: // NSRightMouseDown
- if (win->mouseInView) {
- win->mouseButtons |= 2;
- }
- break;
- case 4: // NSRightMouseUp
- win->mouseButtons &= ~2;
- break;
- case 25: // NSOtherMouseDown
- {
- // number == 2 is a middle button
- NSInteger number = objc_msgSend_t(NSInteger)(event, sel("buttonNumber"));
- if (number == 2 && win->mouseInView) {
- win->mouseButtons |= 4;
- }
- break;
- }
- case 26: // NSOtherMouseUp
- {
- NSInteger number = objc_msgSend_t(NSInteger)(event, sel("buttonNumber"));
- if (number == 2)
- win->mouseButtons &= ~4;
- break;
- }
- case 22: // NSScrollWheel
- {
- CGFloat deltaX = objc_msgSend_t(CGFloat)(event, sel("deltaX"));
- win->scrollDeltaX += deltaX;
- CGFloat deltaY = objc_msgSend_t(CGFloat)(event, sel("deltaY"));
- win->scrollDeltaY += deltaY;
- break;
- }
- case 12: // NSFlagsChanged
- {
- NSUInteger modifiers = objc_msgSend_t(NSUInteger)(event, sel("modifierFlags"));
-
- // based on NSEventModifierFlags and
- // NSDeviceIndependentModifierFlagsMask
- struct {
- union {
- struct {
- uint8_t alpha_shift : 1;
- uint8_t shift : 1;
- uint8_t control : 1;
- uint8_t alternate : 1;
- uint8_t command : 1;
- uint8_t numeric_pad : 1;
- uint8_t help : 1;
- uint8_t function : 1;
- };
- uint8_t mask;
- };
- } keys;
-
- keys.mask = (modifiers & 0xffff0000UL) >> 16;
-
- // TODO L,R variation of keys?
- win->keys[TK_CONTROL] = keys.alpha_shift;
- win->keys[TK_SHIFT] = keys.shift;
- win->keys[TK_CONTROL] = keys.control;
- win->keys[TK_ALT] = keys.alternate;
- win->keys[TK_LWIN] = keys.command;
- win->keys[TK_RWIN] = keys.command;
- break;
- }
- case 10: // NSKeyDown
- {
- uint16_t keyCode = objc_msgSend_t(unsigned short)(event, sel("keyCode"));
- int tigrKey = _tigrKeyFromOSX(keyCode);
-
- // Ignore keyboard repeats
- if (!win->keys[tigrKey]) {
- win->keys[tigrKey] = 1;
- id inputText = objc_msgSend_id(event, sel("characters"));
- const char* inputTextUTF8 = objc_msgSend_t(const char*)(inputText, sel("UTF8String"));
-
- int decoded = 0;
- tigrDecodeUTF8(inputTextUTF8, &decoded);
- if (decoded < 0xe000 || decoded > 0xf8ff) {
- win->lastChar = decoded;
- }
- }
-
- // Pass through cmd+key
- if (win->keys[TK_LWIN]) {
- break;
- }
- return;
- }
- case 11: // NSKeyUp
- {
- uint16_t keyCode = objc_msgSend_t(unsigned short)(event, sel("keyCode"));
- win->keys[_tigrKeyFromOSX(keyCode)] = 0;
- return;
- }
- default:
- break;
- }
-
- objc_msgSend_void_id(NSApp, sel("sendEvent:"), event);
-}
-
-void tigrUpdate(Tigr* bmp) {
- popPool(autoreleasePool);
- autoreleasePool = pushPool();
-
- TigrInternal* win;
- id openGLContext;
- id window;
- win = tigrInternal(bmp);
- window = (id)bmp->handle;
- openGLContext = (id)win->gl.glContext;
-
- if (terminated || _tigrIsWindowClosed(window)) {
- return;
- }
-
- id keyWindow = objc_msgSend_id(NSApp, sel("keyWindow"));
- unsigned long long eventMask = NSAllEventMask;
-
- if (keyWindow == window) {
- memcpy(win->prev, win->keys, 256);
- } else {
- eventMask &= ~(NSKeyDownMask | NSKeyUpMask);
- }
-
- win->scrollDeltaX = 0;
- win->scrollDeltaY = 0;
-
- id event = 0;
- BOOL visible = 0;
-
- double now = _currentMediaTime();
- double passed = now - _tigrTimestamp;
-
- do {
- event = objc_msgSend_t(id, NSUInteger, id, id, BOOL)(
- NSApp, sel("nextEventMatchingMask:untilDate:inMode:dequeue:"), eventMask, nil, NSDefaultRunLoopMode, YES);
-
- if (event != 0) {
- _tigrOnCocoaEvent(event, window);
- } else {
- visible = _tigrIsWindowVisible(window);
- }
- } while (event != 0 || !visible);
-
- // The event processing loop above blocks during resize, which causes updates to freeze
- // but real time keeps ticking. We pretend that the event processing took no time
- // to avoid huge jumps in tigrTime.
- if (_tigrTimestamp != 0) {
- _tigrTimestamp = _currentMediaTime() - passed;
- }
-
- objc_msgSend_void(NSApp, sel("updateWindows"));
- objc_msgSend_void(openGLContext, sel("update"));
- tigrGAPIBegin(bmp);
-
- NSSize windowSize = _tigrContentBackingSize(window);
-
- if (win->flags & TIGR_AUTO)
- tigrResize(bmp, windowSize.width / win->scale, windowSize.height / win->scale);
- else
- win->scale = tigrEnforceScale(tigrCalcScale(bmp->w, bmp->h, windowSize.width, windowSize.height), win->flags);
-
- tigrPosition(bmp, win->scale, windowSize.width, windowSize.height, win->pos);
- tigrGAPIPresent(bmp, windowSize.width, windowSize.height);
- objc_msgSend_void(openGLContext, sel("flushBuffer"));
- tigrGAPIEnd(bmp);
-
- id runLoop = objc_msgSend_id(class("NSRunLoop"), sel("currentRunLoop"));
- id until = objc_msgSend_t(id, float)(class("NSDate"), sel("dateWithTimeIntervalSinceNow:"), 0.005f);
- double lastFrameTime = win->frameTime;
- static const double cap = 1.0 / 60;
-
- while (win->frameTime <= lastFrameTime) {
- // Run the event loop for 5ms at a time..
- objc_msgSend_t(void, id, id)(runLoop, sel("runMode:beforeDate:"), NSDefaultRunLoopMode, until);
- // ..until we get a frame callback..
- if (win->frameTime > lastFrameTime) {
- break;
- }
- double now = _currentMediaTime();
- double passed = now - lastFrameTime;
- // ..or it's time to cap the wait at 60 fps
- if (passed > cap) {
- win->frameTime = now;
- break;
- }
- }
-}
-
-int tigrGAPIBegin(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- objc_msgSend_void((id)win->gl.glContext, sel("makeCurrentContext"));
- return 0;
-}
-
-int tigrGAPIEnd(Tigr* bmp) {
- (void)bmp;
- objc_msgSend_void(class("NSOpenGLContext"), sel("clearCurrentContext"));
- return 0;
-}
-
-int tigrClosed(Tigr* bmp) {
- return (terminated || _tigrIsWindowClosed((id)bmp->handle)) ? 1 : 0;
-}
-
-void tigrMouse(Tigr* bmp, int* x, int* y, int* buttons) {
- TigrInternal* win;
- id window;
- win = tigrInternal(bmp);
- window = (id)bmp->handle;
-
- id windowContentView = objc_msgSend_id(window, sel("contentView"));
- NSRect adjustFrame = objc_msgSend_stret_t(NSRect)(windowContentView, sel("frame"));
-
- // NSPoint is small enough to fit a register, so no need for
- // objc_msgSend_stret
- NSPoint p = objc_msgSend_t(NSPoint)(window, sel("mouseLocationOutsideOfEventStream"));
-
- // map input to content view rect
- if (p.x < 0)
- p.x = 0;
- else if (p.x > adjustFrame.size.width)
- p.x = adjustFrame.size.width;
- if (p.y < 0)
- p.y = 0;
- else if (p.y > adjustFrame.size.height)
- p.y = adjustFrame.size.height;
-
- // map input to pixels
- NSRect r = { p, { 0, 0 } };
- r = objc_msgSend_stret_t(NSRect, NSRect)(windowContentView, sel("convertRectToBacking:"), r);
- p = r.origin;
-
- p.x = (p.x - win->pos[0]) / win->scale;
- p.y = bmp->h - (p.y - win->pos[1]) / win->scale;
-
- if (x)
- *x = p.x;
- if (y)
- *y = p.y;
-
- if (buttons) {
- id keyWindow = objc_msgSend_id(NSApp, sel("keyWindow"));
- *buttons = keyWindow != bmp->handle ? 0 : win->mouseButtons;
- }
-}
-
-int tigrTouch(Tigr* bmp, TigrTouchPoint* points, int maxPoints) {
- int buttons = 0;
- if (maxPoints > 0) {
- tigrMouse(bmp, &points[0].x, &points[1].y, &buttons);
- }
- return buttons ? 1 : 0;
-}
-
-void tigrScrollWheel(Tigr* bmp, float* x, float* y) {
- TigrInternal* win;
- win = tigrInternal(bmp);
- if (x) {
- *x = win->scrollDeltaX;
- }
- if (y) {
- *y = win->scrollDeltaY;
- }
-}
-
-int tigrKeyDown(Tigr* bmp, int key) {
- TigrInternal* win;
- assert(key < 256);
- win = tigrInternal(bmp);
- return (win->keys[key] != 0) && (win->prev[key] == 0);
-}
-
-int tigrKeyHeld(Tigr* bmp, int key) {
- TigrInternal* win;
- assert(key < 256);
- win = tigrInternal(bmp);
- return win->keys[key];
-}
-
-int tigrReadChar(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- int c = win->lastChar;
- win->lastChar = 0;
- return c;
-}
-
-float tigrTime(void) {
- double elapsed = 0;
-
- double now = _currentMediaTime();
- if (_tigrTimestamp != 0) {
- elapsed = now - _tigrTimestamp;
- }
- _tigrTimestamp = now;
-
- return (float)elapsed;
-}
-
-#endif // __MACOS__
-#endif // #ifndef TIGR_HEADLESS
-
-//////// End of inlined file: tigr_osx.c ////////
-
-//////// Start of inlined file: tigr_ios.c ////////
-
-#ifndef TIGR_HEADLESS
-
-//#include "tigr_internal.h"
-//#include "tigr_objc.h"
-
-#ifdef __IOS__
-
-#include <CoreGraphics/CoreGraphics.h>
-#include <objc/message.h>
-#include <objc/objc.h>
-#include <objc/runtime.h>
-#include <dispatch/dispatch.h>
-#include <os/log.h>
-#include <time.h>
-#include <stdatomic.h>
-
-id makeNSString(const char* str) {
- return objc_msgSend_t(id, const char*)(class("NSString"), sel("stringWithUTF8String:"), str);
-}
-
-id joinNSStrings(id a, id b) {
- return objc_msgSend_t(id, id)(a, sel("stringByAppendingString:"), b);
-}
-
-const char* UTF8StringFromNSString(id a) {
- return objc_msgSend_t(const char*)(a, sel("UTF8String"));
-}
-
-extern id UIApplication;
-static int NSQualityOfServiceUserInteractive = 0x21;
-
-typedef struct {
- TigrTouchPoint points[MAX_TOUCH_POINTS];
- int numPoints;
-} InputState;
-
-typedef struct {
- int keyCode;
- int codePoint;
-} KeyEvent;
-
-enum {
- KBD_HIDDEN = 0,
- KBD_SHOWREQ,
- KBD_HIDEREQ,
- KBD_SHOWN,
-};
-
-/// Global state
-static struct {
- InputState inputState;
- id viewController;
- id view;
- id context;
- id frameCondition;
- int screenW;
- int screenH;
- double scaleFactor;
- double timeSinceLastDraw;
- int renderReadFd;
- int mainWriteFd;
- _Atomic(int) keyboardState;
-} gState = {
- .inputState = {
- .numPoints = 0,
- },
- .viewController = 0,
- .view = 0,
- .context = 0,
- .frameCondition = 0,
- .screenW = 0,
- .screenH = 0,
- .scaleFactor = 1,
- .timeSinceLastDraw = 0,
- .renderReadFd = 0,
- .mainWriteFd = 0,
- .keyboardState = ATOMIC_VAR_INIT(KBD_HIDDEN),
-};
-
-typedef enum {
- SET_INPUT,
- KEY_EVENT,
-} TigrMessage;
-
-typedef struct TigrMessageData {
- TigrMessage message;
- union {
- InputState inputState;
- KeyEvent keyEvent;
- };
-} TigrMessageData;
-
-static id autoreleasePool = NULL;
-
-void writeToRenderThread(const TigrMessageData* message) {
- if (write(gState.mainWriteFd, message, sizeof(TigrMessageData)) != sizeof(TigrMessageData)) {
- os_log_error(OS_LOG_DEFAULT, "Failed to write message to render thread: %{public}s", strerror(errno));
- }
-}
-
-int readFromMainThread(TigrMessageData* message) {
- int result = read(gState.renderReadFd, message, sizeof(TigrMessageData));
- if (result == -1 && errno == EAGAIN) {
- return 0;
- }
- if (result != sizeof(TigrMessageData)) {
- os_log_error(OS_LOG_DEFAULT, "Failed to read message from main thread: %{public}s", strerror(errno));
- return 0;
- }
- return 1;
-}
-
-void viewWillTransitionToSize(id self, SEL _sel, CGSize size, id transitionCoordinator) {
- // No animation, just set them
- gState.screenW = size.width * gState.scaleFactor;
- gState.screenH = size.height * gState.scaleFactor;
- struct objc_super super = {
- self,
- objc_getClass("GLKViewController"),
- };
- objc_msgSendSuper_t(void, CGSize, id)(&super, _sel, size, transitionCoordinator);
-}
-
-BOOL prefersStatusBarHidden(id self, SEL _sel) {
- return YES;
-}
-
-BOOL hasText(id self, SEL _sel) {
- return NO;
-}
-
-void tigrShowKeyboard(int show) {
- int expected = show ? KBD_HIDDEN : KBD_SHOWN;
- int desired = show ? KBD_SHOWREQ : KBD_HIDEREQ;
- atomic_compare_exchange_weak(&gState.keyboardState, &expected, desired);
-}
-
-void insertText(id self, SEL _sel, id text) {
- const char* inserted = UTF8StringFromNSString(text);
- int codePoint = 0;
-
- do {
- inserted = tigrDecodeUTF8(inserted, &codePoint);
- if (codePoint != 0) {
- KeyEvent event;
- event.codePoint = codePoint;
- event.keyCode = (codePoint < 128) ? codePoint : 0;
-
- TigrMessageData message = {
- .message = KEY_EVENT,
- .keyEvent = event,
- };
- writeToRenderThread(&message);
- }
- } while (*inserted != 0);
-}
-
-void deleteBackward(id self, SEL _sel) {
- KeyEvent event;
- event.codePoint = 0;
- event.keyCode = 8; // BS
-
- TigrMessageData message = { .message = KEY_EVENT,
- .keyEvent = {
- .codePoint = 0,
- .keyCode = 8,
- } };
- writeToRenderThread(&message);
-}
-
-BOOL canBecomeFirstResponder(id self, SEL _sel) {
- return YES;
-}
-
-BOOL canResignFirstResponder(id self, SEL _sel) {
- return YES;
-}
-
-enum RenderState { SWAPPED = 5150, RENDERED };
-
-BOOL didFinishLaunchingWithOptions(id self, SEL _sel, id application, id options) {
- id screen = objc_msgSend_id(class("UIScreen"), sel("mainScreen"));
- CGRect bounds = objc_msgSend_t(CGRect)(screen, sel("bounds"));
- CGSize size = bounds.size;
-
- id window = objc_alloc("UIWindow");
- window = objc_msgSend_t(id, CGRect)(window, sel("initWithFrame:"), bounds);
-
- Class ViewController = makeClass("TigrViewController", "GLKViewController");
- addMethod(ViewController, "viewWillTransitionToSize:withTransitionCoordinator:", viewWillTransitionToSize,
- "v@:{CGSize}@");
- addMethod(ViewController, "prefersStatusBarHidden", prefersStatusBarHidden, "c@:");
- id vc = objc_msgSend_t(id)((id)ViewController, sel("alloc"));
- vc = objc_msgSend_id(vc, sel("init"));
- gState.viewController = vc;
- objc_msgSend_t(void, int)(vc, sel("setPreferredFramesPerSecond:"), 60);
- int framesPerSecond = objc_msgSend_t(int)(vc, sel("framesPerSecond"));
-
- id context = objc_alloc("EAGLContext");
- static int kEAGLRenderingAPIOpenGLES3 = 3;
- context = objc_msgSend_t(id, int)(context, sel("initWithAPI:"), kEAGLRenderingAPIOpenGLES3);
- gState.context = context;
-
- Class View = makeClass("TigrView", "GLKView");
- addMethod(View, "insertText:", insertText, "v@:@");
- addMethod(View, "deleteBackward", deleteBackward, "v@:");
- addMethod(View, "hasText", hasText, "c@:");
- addMethod(View, "canBecomeFirstResponder", canBecomeFirstResponder, "c@:");
- addMethod(View, "canResignFirstResponder", canResignFirstResponder, "c@:");
-
- Protocol* UIKeyInput = objc_getProtocol("UIKeyInput");
- class_addProtocol(View, UIKeyInput);
-
- id view = objc_msgSend_id((id)View, sel("alloc"));
- view = objc_msgSend_t(id, CGRect, id)(view, sel("initWithFrame:context:"), bounds, context);
- gState.view = view;
- objc_msgSend_t(void, BOOL)(view, sel("setMultipleTouchEnabled:"), YES);
- objc_msgSend_t(void, id)(view, sel("setDelegate:"), self);
- objc_msgSend_t(void, id)(vc, sel("setView:"), view);
- objc_msgSend_t(void, id)(vc, sel("setDelegate:"), self);
- objc_msgSend_t(void, id)(window, sel("setRootViewController:"), vc);
- objc_msgSend_t(void)(window, sel("makeKeyAndVisible"));
-
- gState.scaleFactor = objc_msgSend_t(double)(view, sel("contentScaleFactor"));
- gState.screenW = size.width * gState.scaleFactor;
- gState.screenH = size.height * gState.scaleFactor;
-
- gState.frameCondition = objc_msgSend_t(id, int)(objc_alloc("NSConditionLock"), sel("initWithCondition:"), RENDERED);
- objc_msgSend_t(void, int)(gState.frameCondition, sel("lockWhenCondition:"), RENDERED);
-
- id renderThread = objc_msgSend_t(id, id, SEL, id)(objc_alloc("NSThread"), sel("initWithTarget:selector:object:"),
- self, sel("renderMain"), NULL);
- objc_msgSend_t(void, int)(renderThread, sel("setQualityOfService:"), NSQualityOfServiceUserInteractive);
- objc_msgSend_t(void, id)(renderThread, sel("setName:"), makeNSString("Tigr Render Thread"));
- objc_msgSend_void(renderThread, sel("start"));
-
- return YES;
-}
-
-void waitForFrame() {
- objc_msgSend_t(void, id)(class("EAGLContext"), sel("setCurrentContext:"), 0);
- objc_msgSend_t(void, int)(gState.frameCondition, sel("unlockWithCondition:"), RENDERED);
- objc_msgSend_t(void, int)(gState.frameCondition, sel("lockWhenCondition:"), SWAPPED);
- objc_msgSend_t(void, id)(class("EAGLContext"), sel("setCurrentContext:"), gState.context);
-}
-
-void processKeyboardRequest() {
- int showReq = KBD_SHOWREQ;
- int hideReq = KBD_HIDEREQ;
-
- if (atomic_compare_exchange_weak(&gState.keyboardState, &showReq, KBD_SHOWN)) {
- objc_msgSend_t(BOOL)(gState.view, sel("becomeFirstResponder"));
- } else if (atomic_compare_exchange_weak(&gState.keyboardState, &hideReq, KBD_HIDDEN)) {
- objc_msgSend_t(BOOL)(gState.view, sel("resignFirstResponder"));
- }
-}
-
-void drawInRect(id _self, SEL _sel, id view, CGRect rect) {
- gState.timeSinceLastDraw = objc_msgSend_t(double)(gState.viewController, sel("timeSinceLastDraw"));
- objc_msgSend_t(void, int)(gState.frameCondition, sel("unlockWithCondition:"), SWAPPED);
- objc_msgSend_t(void, int)(gState.frameCondition, sel("lockWhenCondition:"), RENDERED);
- objc_msgSend_t(void, id)(class("EAGLContext"), sel("setCurrentContext:"), gState.context);
-
- processKeyboardRequest();
-}
-
-extern void tigrMain();
-
-void renderMain(id _self, SEL _sel) {
- objc_msgSend_t(void, int)(gState.frameCondition, sel("lockWhenCondition:"), SWAPPED);
- objc_msgSend_t(void, id)(class("EAGLContext"), sel("setCurrentContext:"), gState.context);
- tigrMain();
-}
-
-enum {
- UITouchPhaseBegan,
- UITouchPhaseMoved,
- UITouchPhaseStationary,
- UITouchPhaseEnded,
- UITouchPhaseCancelled,
-};
-
-void touches(id self, SEL sel, id touches, id event) {
- id allTouches = objc_msgSend_t(id)(event, sel("allTouches"));
- id enumerator = objc_msgSend_t(id)(allTouches, sel("objectEnumerator"));
- id touch = 0;
- InputState input = {
- .numPoints = 0,
- };
- while ((touch = objc_msgSend_t(id)(enumerator, sel("nextObject")))) {
- CGPoint location = objc_msgSend_t(CGPoint, id)(touch, sel("locationInView:"), NULL);
- int phase = objc_msgSend_t(int)(touch, sel("phase"));
- switch (phase) {
- case UITouchPhaseBegan:
- case UITouchPhaseMoved:
- case UITouchPhaseStationary:
- input.points[input.numPoints].x = location.x * gState.scaleFactor;
- input.points[input.numPoints].y = location.y * gState.scaleFactor;
- input.numPoints++;
- break;
- }
- if (input.numPoints >= MAX_TOUCH_POINTS) {
- break;
- }
- }
- TigrMessageData message = {
- .message = SET_INPUT,
- .inputState = input,
- };
- writeToRenderThread(&message);
-}
-
-Class tigrAppDelegate() {
- static Class delegateClass = 0;
- if (delegateClass != 0) {
- return delegateClass;
- }
-
- id application = objc_msgSend_id(class("UIApplication"), sel("sharedApplication"));
- delegateClass = makeClass("TigrAppDelegate", "UIResponder");
- addMethod(delegateClass, "application:didFinishLaunchingWithOptions:", didFinishLaunchingWithOptions, "c@:@@");
- addMethod(delegateClass, "touchesBegan:withEvent:", touches, "v@:@@");
- addMethod(delegateClass, "touchesMoved:withEvent:", touches, "v@:@@");
- addMethod(delegateClass, "touchesEnded:withEvent:", touches, "v@:@@");
- addMethod(delegateClass, "touchesCancelled:withEvent:", touches, "v@:@@");
- addMethod(delegateClass, "glkView:drawInRect:", drawInRect, "v@:@{CGRect}");
- addMethod(delegateClass, "renderMain", renderMain, "v@:");
- objc_registerClassPair(delegateClass);
-
- int fds[2];
- if (pipe(fds) != 0) {
- tigrError(0, "Failed to create message pipe");
- }
- int flags = fcntl(fds[0], F_GETFL, 0);
- fcntl(fds[0], F_SETFL, flags | O_NONBLOCK);
-
- gState.renderReadFd = fds[0];
- gState.mainWriteFd = fds[1];
- return delegateClass;
-}
-
-Tigr* tigrWindow(int w, int h, const char* title, int flags) {
- int scale = 1;
- if (flags & TIGR_AUTO) {
- // Always use a 1:1 pixel size.
- scale = 1;
- } else {
- // See how big we can make it and still fit on-screen.
- scale = tigrCalcScale(w, h, gState.screenW, gState.screenH);
- }
-
- scale = tigrEnforceScale(scale, flags);
- Tigr* bmp = tigrBitmap2(w, h, sizeof(TigrInternal));
- bmp->handle = (void*)4711;
- TigrInternal* win = tigrInternal(bmp);
- win->shown = 0;
- win->closed = 0;
- win->scale = scale;
-
- win->lastChar = 0;
- win->flags = flags;
- win->p1 = win->p2 = win->p3 = 0;
- win->p4 = 1;
- win->widgetsWanted = 0;
- win->widgetAlpha = 0;
- win->widgetsScale = 0;
- win->widgets = 0;
- win->gl.gl_legacy = 0;
-
- tigrPosition(bmp, win->scale, bmp->w, bmp->h, win->pos);
- tigrGAPICreate(bmp);
-
- return bmp;
-}
-
-void processEvents(TigrInternal* win) {
- memset(win->keys, 0, 255);
-
- TigrMessageData data;
-
- while (readFromMainThread(&data)) {
- switch (data.message) {
- case SET_INPUT:
- gState.inputState = data.inputState;
- break;
- case KEY_EVENT:
- win->keys[data.keyEvent.keyCode] = 1;
- win->lastChar = data.keyEvent.codePoint;
- break;
- }
- }
-}
-
-static int toWindowX(TigrInternal* win, int x) {
- return (x - win->pos[0]) / win->scale;
-}
-
-static int toWindowY(TigrInternal* win, int y) {
- return (y - win->pos[1]) / win->scale;
-}
-
-void tigrUpdate(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
-
- processEvents(win);
-
- win->numTouchPoints = gState.inputState.numPoints;
- for (int i = 0; i < win->numTouchPoints; i++) {
- win->touchPoints[i].x = toWindowX(win, gState.inputState.points[i].x);
- win->touchPoints[i].y = toWindowY(win, gState.inputState.points[i].y);
- }
-
- win->mouseButtons = win->numTouchPoints;
- if (win->mouseButtons > 0) {
- win->mouseX = win->touchPoints[0].x;
- win->mouseY = win->touchPoints[0].y;
- }
-
- if (win->flags & TIGR_AUTO) {
- tigrResize(bmp, gState.screenW / win->scale, gState.screenH / win->scale);
- } else {
- win->scale = tigrEnforceScale(tigrCalcScale(bmp->w, bmp->h, gState.screenW, gState.screenH), win->flags);
- }
-
- tigrPosition(bmp, win->scale, gState.screenW, gState.screenH, win->pos);
- tigrGAPIPresent(bmp, gState.screenW, gState.screenH);
- waitForFrame();
-}
-
-int tigrClosed(Tigr* bmp) {
- return 0;
-}
-
-void tigrError(Tigr* bmp, const char* message, ...) {
- char tmp[1024];
-
- va_list args;
- va_start(args, message);
- vsnprintf(tmp, sizeof(tmp), message, args);
- tmp[sizeof(tmp) - 1] = 0;
- va_end(args);
-
- os_log_error(OS_LOG_DEFAULT, "tigr fatal error: %{public}s\n", tmp);
-
- exit(1);
-}
-
-float tigrTime() {
- return (float)gState.timeSinceLastDraw;
-}
-
-void tigrMouse(Tigr* bmp, int* x, int* y, int* buttons) {
- TigrInternal* win = tigrInternal(bmp);
- if (x) {
- *x = win->mouseX;
- }
- if (y) {
- *y = win->mouseY;
- }
- if (buttons) {
- *buttons = win->mouseButtons;
- }
-}
-
-int tigrTouch(Tigr* bmp, TigrTouchPoint* points, int maxPoints) {
- TigrInternal* win = tigrInternal(bmp);
- for (int i = 0; i < maxPoints && i < win->numTouchPoints; i++) {
- points[i] = win->touchPoints[i];
- }
- return maxPoints < win->numTouchPoints ? maxPoints : win->numTouchPoints;
-}
-
-void tigrMouseWheel(Tigr* bmp, float* x, float* y) {
- if (x) {
- *x = 0;
- }
- if (y) {
- *y = 0;
- }
-}
-
-void tigrFree(Tigr* bmp) {
- if (bmp->handle) {
- TigrInternal* win = tigrInternal(bmp);
- }
- free(bmp->pix);
- free(bmp);
-}
-
-int tigrGAPIBegin(Tigr* bmp) {
- (void)bmp;
- return 0;
-}
-
-int tigrGAPIEnd(Tigr* bmp) {
- (void)bmp;
- return 0;
-}
-
-int tigrKeyDown(Tigr* bmp, int key) {
- TigrInternal* win;
- assert(key < 256);
- win = tigrInternal(bmp);
- return win->keys[key];
-}
-
-int tigrKeyHeld(Tigr* bmp, int key) {
- return tigrKeyDown(bmp, key);
-}
-
-int tigrReadChar(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- int c = win->lastChar;
- win->lastChar = 0;
- return c;
-}
-
-extern void* _tigrReadFile(const char* fileName, int* length);
-
-void* tigrReadFile(const char* fileName, int* length) {
- id mainBundle = objc_msgSend_id(class("NSBundle"), sel("mainBundle"));
- id resourcePath = objc_msgSend_id(mainBundle, sel("resourcePath"));
- resourcePath = joinNSStrings(resourcePath, makeNSString("/"));
- resourcePath = joinNSStrings(resourcePath, makeNSString(fileName));
- return _tigrReadFile(UTF8StringFromNSString(resourcePath), length);
-}
-
-#endif // __IOS__
-
-#endif // #ifndef TIGR_HEADLESS
-
-//////// End of inlined file: tigr_ios.c ////////
-
-//////// Start of inlined file: tigr_android.h ////////
-
-#ifndef TIGR_ANDROID_H
-#define TIGR_ANDROID_H
-#ifdef __ANDROID__
-
-#include <android/input.h>
-#include <android/native_window.h>
-#include <EGL/egl.h>
-
-typedef enum {
- AE_INPUT,
- AE_WINDOW_CREATED,
- AE_WINDOW_DESTROYED,
- AE_RESUME,
- AE_CLOSE,
-} AndroidEventType;
-
-typedef struct {
- AndroidEventType type;
- AInputEvent* inputEvent;
- ANativeWindow* window;
- double time;
-} AndroidEvent;
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/// Calls from TIGR to Android side, render thread
-extern int android_pollEvent(int (*eventHandler)(AndroidEvent, void*), void*);
-extern void android_swap(EGLDisplay display, EGLSurface surface);
-extern void* android_loadAsset(const char* filename, int* outLength);
-extern void android_showKeyboard(int show);
-
-/// Calls from Android to TIGR side, main thread
-void tigr_android_create();
-void tigr_android_destroy();
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // __ANDROID__
-#endif // TIGR_ANDROID_H
-
-//////// End of inlined file: tigr_android.h ////////
-
-//////// Start of inlined file: tigr_linux.c ////////
-
-#ifndef TIGR_HEADLESS
-
-//#include "tigr_internal.h"
-
-#if __linux__ && !__ANDROID__
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <string.h>
-#include <stdarg.h>
-#include <sys/time.h>
-#include <X11/X.h>
-#include <X11/Xlib.h>
-#include <X11/Xlocale.h>
-#include <X11/XKBlib.h>
-#include <X11/Xatom.h>
-#include <GL/glx.h>
-
-static Display* dpy;
-static Window root;
-static XVisualInfo* vi;
-static Atom wmDeleteMessage;
-static XIM inputMethod;
-static GLXFBConfig fbConfig;
-
-static PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = 0;
-
-static void tearDownX11Stuff() {
- XCloseDisplay(dpy);
-}
-
-static void initX11Stuff() {
- static int done = 0;
- if (!done) {
- dpy = XOpenDisplay(NULL);
- if (dpy == NULL) {
- tigrError(0, "Cannot connect to X server");
- }
-
- root = DefaultRootWindow(dpy);
-
- static int attribList[] = { GLX_RENDER_TYPE,
- GLX_RGBA_BIT,
- GLX_DRAWABLE_TYPE,
- GLX_WINDOW_BIT,
- GLX_DOUBLEBUFFER,
- 1,
- GLX_RED_SIZE,
- 1,
- GLX_GREEN_SIZE,
- 1,
- GLX_BLUE_SIZE,
- 1,
- None };
-
- int fbcCount = 0;
- GLXFBConfig* fbc = glXChooseFBConfig(dpy, DefaultScreen(dpy), attribList, &fbcCount);
- if (!fbc) {
- tigrError(0, "Failed to choose FB config");
- }
- fbConfig = fbc[0];
- XFree(fbc);
-
- vi = glXGetVisualFromFBConfig(dpy, fbConfig);
- if (vi == NULL) {
- tigrError(0, "No appropriate visual found");
- }
-
- GLXContext tmpCtx = glXCreateContext(dpy, vi, 0, GL_TRUE);
- glXCreateContextAttribsARB =
- (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB");
- glXDestroyContext(dpy, tmpCtx);
- if (!glXCreateContextAttribsARB) {
- tigrError(0, "Failed to get glXCreateContextAttribsARB");
- }
-
- inputMethod = XOpenIM(dpy, NULL, NULL, NULL);
- if (inputMethod == NULL) {
- tigrError(0, "Failed to create input method");
- }
-
- wmDeleteMessage = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
-
- atexit(tearDownX11Stuff);
-
- done = 1;
- }
-}
-
-static int hasGLXExtension(Display* display, const char* wanted) {
- const char* extensions = glXQueryExtensionsString(display, DefaultScreen(display));
- char* dup = strdup(extensions);
- char* found = 0;
-
- for (char* start = dup;; start = 0) {
- found = strtok(start, " ");
- if (found == 0 || strcmp(found, wanted) == 0) {
- break;
- }
- }
-
- free(dup);
- return found != 0;
-}
-
-static void setupVSync(Display* display, Window win) {
- if (hasGLXExtension(display, "GLX_EXT_swap_control")) {
- PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT =
- (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalEXT");
- if (glXSwapIntervalEXT) {
- glXSwapIntervalEXT(display, win, 1);
- }
- } else if (hasGLXExtension(display, "GLX_MESA_swap_control")) {
- PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESA =
- (PFNGLXSWAPINTERVALMESAPROC)glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalMESA");
- if (glXSwapIntervalMESA) {
- glXSwapIntervalMESA(1);
- }
- } else if (hasGLXExtension(display, "GLX_SGI_swap_control")) {
- PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI =
- (PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalSGI");
- if (glXSwapIntervalSGI) {
- glXSwapIntervalSGI(1);
- }
- }
-}
-
-static void tigrHideCursor(TigrInternal* win) {
- Cursor invisibleCursor;
- Pixmap bitmapNoData;
- XColor black;
- static char noData[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
- black.red = black.green = black.blue = 0;
-
- bitmapNoData = XCreateBitmapFromData(win->dpy, win->win, noData, 8, 8);
- invisibleCursor = XCreatePixmapCursor(win->dpy, bitmapNoData, bitmapNoData, &black, &black, 0, 0);
- XDefineCursor(win->dpy, win->win, invisibleCursor);
- XFreeCursor(win->dpy, invisibleCursor);
- XFreePixmap(win->dpy, bitmapNoData);
-}
-
-typedef struct {
- unsigned long flags;
- unsigned long functions;
- unsigned long decorations;
- long inputMode;
- unsigned long status;
-} WindowHints;
-
-Tigr* tigrWindow(int w, int h, const char* title, int flags) {
- Tigr* bmp = 0;
- Colormap cmap;
- XSetWindowAttributes swa;
- Window xwin;
- GLXContext glc;
- XIC ic;
- int scale;
-
- initX11Stuff();
-
- if (flags & TIGR_AUTO) {
- // Always use a 1:1 pixel size, unless downscaled by tigrEnforceScale below.
- scale = 1;
- } else {
- // See how big we can make it and still fit on-screen.
- Screen* screen = DefaultScreenOfDisplay(dpy);
- int maxW = WidthOfScreen(screen);
- int maxH = HeightOfScreen(screen);
- scale = tigrCalcScale(w, h, maxW, maxH);
- }
-
- scale = tigrEnforceScale(scale, flags);
-
- cmap = XCreateColormap(dpy, root, vi->visual, AllocNone);
- swa.colormap = cmap;
- swa.event_mask = StructureNotifyMask;
-
- // Create window of wanted size
- xwin = XCreateWindow(dpy, root, 0, 0, w * scale, h * scale, 0, vi->depth, InputOutput, vi->visual,
- CWColormap | CWEventMask, &swa);
- XMapWindow(dpy, xwin);
-
- if (flags & TIGR_FULLSCREEN) {
- // https://superuser.com/questions/1680077/does-x11-actually-have-a-native-fullscreen-mode
- Atom wm_state = XInternAtom(dpy, "_NET_WM_STATE", true);
- Atom wm_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", true);
- XChangeProperty(dpy, xwin, wm_state, XA_ATOM, 32, PropModeReplace, (unsigned char*)&wm_fullscreen, 1);
- } else {
- // Wait for window to get mapped
- for (;;) {
- XEvent e;
- XNextEvent(dpy, &e);
- if (e.type == MapNotify) {
- break;
- }
- }
-
- // Reset size if we did not get the window size we wanted above.
- XWindowAttributes wa;
- XGetWindowAttributes(dpy, xwin, &wa);
- scale = tigrCalcScale(w, h, wa.width, wa.height);
- scale = tigrEnforceScale(scale, flags);
- XResizeWindow(dpy, xwin, w * scale, h * scale);
- }
-
- // Enable mouse events reporting (needed for mouse wheel events)
- XSelectInput(dpy, xwin, ButtonPressMask);
-
- XTextProperty prop;
- int result = Xutf8TextListToTextProperty(dpy, (char**)&title, 1, XUTF8StringStyle, &prop);
- if (result == Success) {
- Atom wmName = XInternAtom(dpy, "_NET_WM_NAME", 0);
- XSetTextProperty(dpy, xwin, &prop, wmName);
- XFree(prop.value);
- }
-
- ic = XCreateIC(inputMethod, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, xwin, NULL);
- if (ic == NULL) {
- printf("Failed to create input context\n");
- exit(0);
- }
- XSetICFocus(ic);
-
- XSetWMProtocols(dpy, xwin, &wmDeleteMessage, 1);
-
- int contextAttributes[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, 3, None };
- glc = glXCreateContextAttribsARB(dpy, fbConfig, NULL, GL_TRUE, contextAttributes);
- glXMakeCurrent(dpy, xwin, glc);
-
- setupVSync(dpy, xwin);
-
- bmp = tigrBitmap2(w, h, sizeof(TigrInternal));
- bmp->handle = (void*)xwin;
-
- TigrInternal* win = tigrInternal(bmp);
- win->win = xwin;
- win->dpy = dpy;
- win->glc = glc;
- win->ic = ic;
-
- win->shown = 0;
- win->closed = 0;
- win->scale = scale;
-
- win->lastChar = 0;
- win->flags = flags;
- win->p1 = win->p2 = win->p3 = 0;
- win->p4 = 1;
- win->widgetsWanted = 0;
- win->widgetAlpha = 0;
- win->widgetsScale = 0;
- win->widgets = 0;
- win->gl.gl_legacy = 0;
-
- memset(win->keys, 0, 256);
- memset(win->prev, 0, 256);
-
- if (flags & TIGR_NOCURSOR) {
- tigrHideCursor(win);
- }
-
- tigrPosition(bmp, win->scale, bmp->w, bmp->h, win->pos);
- tigrGAPICreate(bmp);
- tigrGAPIBegin(bmp);
-
- return bmp;
-}
-
-int tigrClosed(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- return win->win == 0;
-}
-
-int tigrGAPIBegin(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- return glXMakeCurrent(win->dpy, win->win, win->glc) ? 0 : -1;
-}
-
-int tigrGAPIEnd(Tigr* bmp) {
- (void)bmp;
- return glXMakeCurrent(NULL, 0, 0) ? 0 : -1;
-}
-
-int tigrKeyDown(Tigr* bmp, int key) {
- TigrInternal* win;
- assert(key < 256);
- win = tigrInternal(bmp);
- return win->keys[key] && !win->prev[key];
-}
-
-int tigrKeyHeld(Tigr* bmp, int key) {
- TigrInternal* win;
- assert(key < 256);
- win = tigrInternal(bmp);
- return win->keys[key];
-}
-
-int tigrReadChar(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- int c = win->lastChar;
- win->lastChar = 0;
- return c;
-}
-
-uint8_t tigrKeyFromX11(KeySym sym) {
- if (sym >= 'a' && sym <= 'z') {
- return (uint8_t)sym - ('a' - 'A');
- }
-
- if (sym >= '0' && sym <= '9') {
- return (uint8_t)sym;
- }
-
- switch (sym) {
- case XK_KP_0:
- return TK_PAD0;
- case XK_KP_1:
- return TK_PAD1;
- case XK_KP_2:
- return TK_PAD2;
- case XK_KP_3:
- return TK_PAD3;
- case XK_KP_4:
- return TK_PAD4;
- case XK_KP_5:
- return TK_PAD5;
- case XK_KP_6:
- return TK_PAD6;
- case XK_KP_7:
- return TK_PAD7;
- case XK_KP_8:
- return TK_PAD8;
- case XK_KP_9:
- return TK_PAD9;
-
- case XK_KP_Multiply:
- return TK_PADMUL;
- case XK_KP_Divide:
- return TK_PADDIV;
- case XK_KP_Add:
- return TK_PADADD;
- case XK_KP_Subtract:
- return TK_PADSUB;
- case XK_KP_Decimal:
- return TK_PADDOT;
- case XK_KP_Enter:
- return TK_PADENTER;
-
- case XK_F1:
- return TK_F1;
- case XK_F2:
- return TK_F2;
- case XK_F3:
- return TK_F3;
- case XK_F4:
- return TK_F4;
- case XK_F5:
- return TK_F5;
- case XK_F6:
- return TK_F6;
- case XK_F7:
- return TK_F7;
- case XK_F8:
- return TK_F8;
- case XK_F9:
- return TK_F9;
- case XK_F10:
- return TK_F10;
- case XK_F11:
- return TK_F11;
- case XK_F12:
- return TK_F12;
-
- case XK_BackSpace:
- return TK_BACKSPACE;
- case XK_Tab:
- return TK_TAB;
- case XK_Return:
- return TK_RETURN;
- case XK_Pause:
- return TK_PAUSE;
- case XK_Caps_Lock:
- return TK_CAPSLOCK;
- case XK_Escape:
- return TK_ESCAPE;
- case XK_space:
- return TK_SPACE;
-
- case XK_Page_Up:
- return TK_PAGEUP;
- case XK_Page_Down:
- return TK_PAGEDN;
- case XK_End:
- return TK_END;
- case XK_Home:
- return TK_HOME;
- case XK_Left:
- return TK_LEFT;
- case XK_Up:
- return TK_UP;
- case XK_Right:
- return TK_RIGHT;
- case XK_Down:
- return TK_DOWN;
- case XK_Insert:
- return TK_INSERT;
- case XK_Delete:
- return TK_DELETE;
-
- case XK_Meta_L:
- return TK_LWIN;
- case XK_Meta_R:
- return TK_RWIN;
- case XK_Num_Lock:
- return TK_NUMLOCK;
- case XK_Scroll_Lock:
- return TK_SCROLL;
- case XK_Shift_L:
- return TK_LSHIFT;
- case XK_Shift_R:
- return TK_RSHIFT;
- case XK_Control_L:
- return TK_LCONTROL;
- case XK_Control_R:
- return TK_RCONTROL;
- case XK_Alt_L:
- return TK_LALT;
- case XK_Alt_R:
- return TK_RALT;
-
- case XK_semicolon:
- return TK_SEMICOLON;
- case XK_equal:
- return TK_EQUALS;
- case XK_comma:
- return TK_COMMA;
- case XK_minus:
- return TK_MINUS;
- case XK_period:
- return TK_DOT;
- case XK_slash:
- return TK_SLASH;
- case XK_grave:
- return TK_BACKTICK;
- case XK_bracketleft:
- return TK_LSQUARE;
- case XK_backslash:
- return TK_BACKSLASH;
- case XK_bracketright:
- return TK_RSQUARE;
- case XK_apostrophe:
- return TK_TICK;
- }
- return 0;
-}
-
-static void tigrUpdateModifiers(TigrInternal* win) {
- win->keys[TK_SHIFT] = win->keys[TK_LSHIFT] || win->keys[TK_RSHIFT];
- win->keys[TK_CONTROL] = win->keys[TK_LCONTROL] || win->keys[TK_RCONTROL];
- win->keys[TK_ALT] = win->keys[TK_LALT] || win->keys[TK_RALT];
-}
-
-static void tigrInterpretChar(TigrInternal* win, Window root, unsigned int keycode, unsigned int mask) {
- XKeyEvent event;
- memset(&event, 0, sizeof(event));
- event.type = KeyPress;
- event.display = win->dpy;
- event.root = root;
- event.window = win->win;
- event.state = mask;
- event.keycode = keycode;
- char inputTextUTF8[10];
- Status status = 0;
- Xutf8LookupString(win->ic, &event, inputTextUTF8, sizeof(inputTextUTF8), NULL, &status);
-
- if (status == XLookupChars) {
- tigrDecodeUTF8(inputTextUTF8, &win->lastChar);
- }
-}
-
-static void tigrProcessInput(TigrInternal* win, int winWidth, int winHeight) {
- {
- Window focused;
- int revertTo;
- XGetInputFocus(win->dpy, &focused, &revertTo);
-
- if (win->win != focused) {
- return;
- }
- }
-
- Window root;
- Window child;
- int rootX;
- int rootY;
- int winX;
- int winY;
- unsigned int mask;
-
- if (XQueryPointer(win->dpy, win->win, &root, &child, &rootX, &rootY, &winX, &winY, &mask)) {
- static unsigned int prevButtons;
- unsigned int buttons = mask & (Button1Mask | Button2Mask | Button3Mask);
-
- win->mouseX = (winX - win->pos[0]) / win->scale;
- win->mouseY = (winY - win->pos[1]) / win->scale;
-
- if (buttons != prevButtons && (winX > 0 && winX < winWidth) && (winY > 0 && winY < winHeight)) {
- win->mouseButtons = (buttons & Button1Mask) ? 1
- : 0 | (buttons & Button3Mask) ? 2
- : 0 | (buttons & Button2Mask) ? 4
- : 0;
- }
- prevButtons = buttons;
- }
-
- // Retrieve mouse wheel events - Cannot be done in the XQueryPointer call
- // -> https://comp.unix.programmer.narkive.com/eOnjkQ6L/xlib-xquerypointer-and-button4mask-button5mask#post2
- // Button4 = WheelUp / Button5 = WheelDown
- XEvent mouseButtonEvent;
- while (XPending(win->dpy)) {
- XPeekEvent(win->dpy, &mouseButtonEvent);
- if (mouseButtonEvent.xany.type == ButtonPress) {
- switch (mouseButtonEvent.xbutton.button) {
- case Button4:
- win->scrollDeltaY += 1;
- break;
- case Button5:
- win->scrollDeltaY -= 1;
- break;
- case 6:
- win->scrollDeltaX += 1;
- break;
- case 7:
- win->scrollDeltaX -= 1;
- break;
- }
- XNextEvent(win->dpy, &mouseButtonEvent);
- } else {
- break;
- }
- }
-
- static char prevKeys[32];
- char keys[32];
- XQueryKeymap(win->dpy, keys);
- for (int i = 0; i < 32; i++) {
- char thisBlock = keys[i];
- char prevBlock = prevKeys[i];
- if (thisBlock != prevBlock) {
- for (int j = 0; j < 8; j++) {
- int thisBit = thisBlock & 1;
- int prevBit = prevBlock & 1;
- thisBlock >>= 1;
- prevBlock >>= 1;
- if (thisBit != prevBit) {
- int keyCode = 8 * i + j;
- KeySym keySym = XkbKeycodeToKeysym(win->dpy, keyCode, 0, 0);
- if (keySym != NoSymbol) {
- int key = tigrKeyFromX11(keySym);
- win->keys[key] = thisBit;
- tigrUpdateModifiers(win);
-
- if (thisBit) {
- tigrInterpretChar(win, root, keyCode, mask);
- }
- }
- }
- }
- }
- }
- memcpy(prevKeys, keys, 32);
-
- XEvent event;
- while (XCheckTypedWindowEvent(win->dpy, win->win, ClientMessage, &event)) {
- if (event.xclient.data.l[0] == wmDeleteMessage) {
- glXMakeCurrent(win->dpy, None, NULL);
- glXDestroyContext(win->dpy, win->glc);
- XDestroyWindow(win->dpy, win->win);
- win->win = 0;
- }
- }
- XFlush(win->dpy);
-}
-
-void tigrUpdate(Tigr* bmp) {
- XWindowAttributes gwa;
-
- TigrInternal* win = tigrInternal(bmp);
-
- memcpy(win->prev, win->keys, 256);
- win->scrollDeltaX = 0;
- win->scrollDeltaY = 0;
-
- XGetWindowAttributes(win->dpy, win->win, &gwa);
-
- if (win->flags & TIGR_AUTO)
- tigrResize(bmp, gwa.width / win->scale, gwa.height / win->scale);
- else
- win->scale = tigrEnforceScale(tigrCalcScale(bmp->w, bmp->h, gwa.width, gwa.height), win->flags);
-
- tigrPosition(bmp, win->scale, gwa.width, gwa.height, win->pos);
- glXMakeCurrent(win->dpy, win->win, win->glc);
- tigrGAPIPresent(bmp, gwa.width, gwa.height);
- glXSwapBuffers(win->dpy, win->win);
-
- tigrProcessInput(win, gwa.width, gwa.height);
-}
-
-void tigrFree(Tigr* bmp) {
- if (bmp->handle) {
- TigrInternal* win = tigrInternal(bmp);
- if (win->win) {
- glXMakeCurrent(win->dpy, None, NULL);
- glXDestroyContext(win->dpy, win->glc);
- XDestroyWindow(win->dpy, win->win);
- win->win = 0;
- }
- }
- free(bmp->pix);
- free(bmp);
-}
-
-void tigrError(Tigr* bmp, const char* message, ...) {
- char tmp[1024];
-
- va_list args;
- va_start(args, message);
- vsnprintf(tmp, sizeof(tmp), message, args);
- tmp[sizeof(tmp) - 1] = 0;
- va_end(args);
-
- printf("tigr fatal error: %s\n", tmp);
-
- exit(1);
-}
-
-float tigrTime() {
- static double lastTime = 0;
-
- struct timeval tv;
- gettimeofday(&tv, NULL);
-
- double now = (double)tv.tv_sec + (tv.tv_usec / 1000000.0);
- double elapsed = lastTime == 0 ? 0 : now - lastTime;
- lastTime = now;
-
- return (float)elapsed;
-}
-
-void tigrMouse(Tigr* bmp, int* x, int* y, int* buttons) {
- TigrInternal* win = tigrInternal(bmp);
- if (x) {
- *x = win->mouseX;
- }
- if (y) {
- *y = win->mouseY;
- }
- if (buttons) {
- *buttons = win->mouseButtons;
- }
-}
-
-int tigrTouch(Tigr* bmp, TigrTouchPoint* points, int maxPoints) {
- int buttons = 0;
- if (maxPoints > 0) {
- tigrMouse(bmp, &points->x, &points->y, &buttons);
- }
- return buttons ? 1 : 0;
-}
-
-void tigrScrollWheel(Tigr* bmp, float* x, float* y) {
- TigrInternal* win;
- win = tigrInternal(bmp);
- if (x) {
- *x = win->scrollDeltaX;
- }
- if (y) {
- *y = win->scrollDeltaY;
- }
-}
-
-#endif // __linux__ && !__ANDROID__
-
-#endif // #ifndef TIGR_HEADLESS
-
-//////// End of inlined file: tigr_linux.c ////////
-
-//////// Start of inlined file: tigr_android.c ////////
-
-#ifndef TIGR_HEADLESS
-
-//#include "tigr_internal.h"
-
-#ifdef __ANDROID__
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#include <android/log.h>
-#include <android/native_window.h>
-#include <android/input.h>
-
-#ifndef NDEBUG
-#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "tigr", __VA_ARGS__))
-#else
-#define LOGD(...) ((void)0)
-#endif
-#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "tigr", __VA_ARGS__))
-#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "tigr", __VA_ARGS__))
-
-typedef struct {
- TigrTouchPoint points[MAX_TOUCH_POINTS];
- int numPoints;
-} InputState;
-
-/// Global state
-static struct {
- ANativeWindow* window;
- InputState inputState;
- EGLDisplay display;
- EGLSurface surface;
- EGLint screenW;
- EGLint screenH;
- EGLConfig config;
- double lastTime;
- int closed;
-} gState = {
- .window = 0,
- .inputState = {
- .numPoints = 0,
- },
- .display = EGL_NO_DISPLAY,
- .surface = EGL_NO_SURFACE,
- .screenW = 0,
- .screenH = 0,
- .config = 0,
- .lastTime = 0,
- .closed = 0,
-};
-
-static const EGLint contextAttribs[] = { EGL_CONTEXT_MAJOR_VERSION, 3, EGL_CONTEXT_MINOR_VERSION, 0, EGL_NONE };
-
-static EGLConfig getGLConfig(EGLDisplay display) {
- EGLConfig config = 0;
-
- const EGLint attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8,
- EGL_NONE };
- EGLint numConfigs;
-
- eglChooseConfig(display, attribs, NULL, 0, &numConfigs);
- EGLConfig* supportedConfigs = (EGLConfig*)malloc(sizeof(EGLConfig) * numConfigs);
- eglChooseConfig(display, attribs, supportedConfigs, numConfigs, &numConfigs);
-
- int i = 0;
- for (; i < numConfigs; i++) {
- EGLConfig* cfg = supportedConfigs[i];
- EGLint r, g, b, d;
- if (eglGetConfigAttrib(display, cfg, EGL_RED_SIZE, &r) &&
- eglGetConfigAttrib(display, cfg, EGL_GREEN_SIZE, &g) &&
- eglGetConfigAttrib(display, cfg, EGL_BLUE_SIZE, &b) &&
- eglGetConfigAttrib(display, cfg, EGL_DEPTH_SIZE, &d) && r == 8 && g == 8 && b == 8 && d == 0) {
- config = supportedConfigs[i];
- break;
- }
- }
-
- if (i == numConfigs) {
- config = supportedConfigs[0];
- }
-
- if (config == NULL) {
- tigrError(NULL, "Unable to initialize EGLConfig");
- }
-
- free(supportedConfigs);
-
- return config;
-}
-
-/// Android interface, called from main thread
-
-void tigr_android_create() {
- gState.closed = 0;
- gState.window = 0;
- gState.inputState.numPoints = 0;
- gState.surface = EGL_NO_SURFACE;
- gState.lastTime = 0;
-
- if (gState.display == EGL_NO_DISPLAY) {
- gState.display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- EGLBoolean status = eglInitialize(gState.display, NULL, NULL);
- if (!status) {
- tigrError(NULL, "Failed to init EGL");
- }
- gState.config = getGLConfig(gState.display);
- }
-}
-
-void tigr_android_destroy() {
- eglTerminate(gState.display);
- gState.display = EGL_NO_DISPLAY;
-}
-
-/// Internals ///
-
-static void logEglError() {
- int error = eglGetError();
- switch (error) {
- case EGL_BAD_DISPLAY:
- LOGE("EGL error: Bad display");
- break;
- case EGL_NOT_INITIALIZED:
- LOGE("EGL error: Not initialized");
- break;
- case EGL_BAD_NATIVE_WINDOW:
- LOGE("EGL error: Bad native window");
- break;
- case EGL_BAD_ALLOC:
- LOGE("EGL error: Bad alloc");
- break;
- case EGL_BAD_MATCH:
- LOGE("EGL error: Bad match");
- break;
- default:
- LOGE("EGL error: %d", error);
- }
-}
-
-static void setupOpenGL() {
- LOGD("setupOpenGL");
- assert(gState.surface == EGL_NO_SURFACE);
- assert(gState.window != 0);
-
- gState.surface = eglCreateWindowSurface(gState.display, gState.config, gState.window, NULL);
- if (gState.surface == EGL_NO_SURFACE) {
- logEglError();
- }
- assert(gState.surface != EGL_NO_SURFACE);
- eglQuerySurface(gState.display, gState.surface, EGL_WIDTH, &gState.screenW);
- eglQuerySurface(gState.display, gState.surface, EGL_HEIGHT, &gState.screenH);
- LOGD("Screen is %d x %d", gState.screenW, gState.screenH);
-}
-
-static void tearDownOpenGL() {
- LOGD("tearDownOpenGL");
- if (gState.display != EGL_NO_DISPLAY) {
- eglMakeCurrent(gState.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-
- if (gState.surface != EGL_NO_SURFACE) {
- LOGD("eglDestroySurface");
- if (!eglDestroySurface(gState.display, gState.surface)) {
- logEglError();
- }
- gState.surface = EGL_NO_SURFACE;
- }
- }
-}
-
-static int tigrKeyFromAndroidKey(int key) {
- switch (key) {
- case AKEYCODE_Q:
- return 'Q';
- case AKEYCODE_W:
- return 'W';
- case AKEYCODE_E:
- return 'E';
- case AKEYCODE_R:
- return 'R';
- case AKEYCODE_T:
- return 'T';
- case AKEYCODE_Y:
- return 'Y';
- case AKEYCODE_U:
- return 'U';
- case AKEYCODE_I:
- return 'I';
- case AKEYCODE_O:
- return 'O';
- case AKEYCODE_P:
- return 'P';
-
- case AKEYCODE_A:
- return 'A';
- case AKEYCODE_S:
- return 'S';
- case AKEYCODE_D:
- return 'D';
- case AKEYCODE_F:
- return 'F';
- case AKEYCODE_G:
- return 'G';
- case AKEYCODE_H:
- return 'H';
- case AKEYCODE_J:
- return 'J';
- case AKEYCODE_K:
- return 'K';
- case AKEYCODE_L:
- return 'L';
-
- case AKEYCODE_Z:
- return 'Z';
- case AKEYCODE_X:
- return 'X';
- case AKEYCODE_C:
- return 'C';
- case AKEYCODE_V:
- return 'V';
- case AKEYCODE_B:
- return 'B';
- case AKEYCODE_N:
- return 'N';
- case AKEYCODE_M:
- return 'M';
-
- case AKEYCODE_0:
- return '0';
- case AKEYCODE_1:
- return '1';
- case AKEYCODE_2:
- return '2';
- case AKEYCODE_3:
- return '3';
- case AKEYCODE_4:
- return '4';
- case AKEYCODE_5:
- return '5';
- case AKEYCODE_6:
- return '6';
- case AKEYCODE_7:
- return '7';
- case AKEYCODE_8:
- return '8';
- case AKEYCODE_9:
- return '9';
-
- case AKEYCODE_NUMPAD_0:
- return TK_PAD0;
- case AKEYCODE_NUMPAD_1:
- return TK_PAD1;
- case AKEYCODE_NUMPAD_2:
- return TK_PAD2;
- case AKEYCODE_NUMPAD_3:
- return TK_PAD3;
- case AKEYCODE_NUMPAD_4:
- return TK_PAD4;
- case AKEYCODE_NUMPAD_5:
- return TK_PAD5;
- case AKEYCODE_NUMPAD_6:
- return TK_PAD6;
- case AKEYCODE_NUMPAD_7:
- return TK_PAD7;
- case AKEYCODE_NUMPAD_8:
- return TK_PAD8;
- case AKEYCODE_NUMPAD_9:
- return TK_PAD9;
-
- case AKEYCODE_NUMPAD_MULTIPLY:
- return TK_PADMUL;
- case AKEYCODE_NUMPAD_DIVIDE:
- return TK_PADDIV;
- case AKEYCODE_NUMPAD_ADD:
- return TK_PADADD;
- case AKEYCODE_NUMPAD_SUBTRACT:
- return TK_PADSUB;
- case AKEYCODE_NUMPAD_ENTER:
- return TK_PADENTER;
- case AKEYCODE_NUMPAD_DOT:
- return TK_PADDOT;
-
- case AKEYCODE_F1:
- return TK_F1;
- case AKEYCODE_F2:
- return TK_F2;
- case AKEYCODE_F3:
- return TK_F3;
- case AKEYCODE_F4:
- return TK_F4;
- case AKEYCODE_F5:
- return TK_F5;
- case AKEYCODE_F6:
- return TK_F6;
- case AKEYCODE_F7:
- return TK_F7;
- case AKEYCODE_F8:
- return TK_F8;
- case AKEYCODE_F9:
- return TK_F9;
- case AKEYCODE_F10:
- return TK_F10;
- case AKEYCODE_F11:
- return TK_F11;
- case AKEYCODE_F12:
- return TK_F12;
-
- case AKEYCODE_SHIFT_LEFT:
- return TK_LSHIFT;
- case AKEYCODE_SHIFT_RIGHT:
- return TK_RSHIFT;
- case AKEYCODE_CTRL_LEFT:
- return TK_LCONTROL;
- case AKEYCODE_CTRL_RIGHT:
- return TK_RCONTROL;
- case AKEYCODE_ALT_LEFT:
- return TK_LALT;
- case AKEYCODE_ALT_RIGHT:
- return TK_RALT;
- case AKEYCODE_META_LEFT:
- return TK_LWIN;
- case AKEYCODE_META_RIGHT:
- return TK_RWIN;
-
- case AKEYCODE_DEL:
- return TK_BACKSPACE;
- case AKEYCODE_TAB:
- return TK_TAB;
- case AKEYCODE_ENTER:
- return TK_RETURN;
- case AKEYCODE_CAPS_LOCK:
- return TK_CAPSLOCK;
- case AKEYCODE_ESCAPE:
- case AKEYCODE_BACK:
- return TK_ESCAPE;
- case AKEYCODE_SPACE:
- return TK_SPACE;
-
- case AKEYCODE_PAGE_UP:
- return TK_PAGEUP;
- case AKEYCODE_PAGE_DOWN:
- return TK_PAGEDN;
- case AKEYCODE_MOVE_END:
- return TK_END;
- case AKEYCODE_MOVE_HOME:
- return TK_HOME;
- case AKEYCODE_DPAD_LEFT:
- return TK_LEFT;
- case AKEYCODE_DPAD_RIGHT:
- return TK_RIGHT;
- case AKEYCODE_DPAD_UP:
- return TK_UP;
- case AKEYCODE_DPAD_DOWN:
- return TK_DOWN;
-
- case AKEYCODE_INSERT:
- return TK_INSERT;
- case AKEYCODE_FORWARD_DEL:
- return TK_DELETE;
- case AKEYCODE_NUM_LOCK:
- return TK_NUMLOCK;
- case AKEYCODE_SCROLL_LOCK:
- return TK_SCROLL;
-
- case AKEYCODE_SEMICOLON:
- return TK_SEMICOLON;
- case AKEYCODE_EQUALS:
- return TK_EQUALS;
- case AKEYCODE_COMMA:
- return TK_COMMA;
- case AKEYCODE_MINUS:
- return TK_MINUS;
- case AKEYCODE_PERIOD:
- return TK_DOT;
- case AKEYCODE_SLASH:
- return TK_SLASH;
- case AKEYCODE_BACKSLASH:
- return TK_BACKSLASH;
- case AKEYCODE_GRAVE:
- return TK_BACKTICK;
- case AKEYCODE_APOSTROPHE:
- return TK_TICK;
- case AKEYCODE_LEFT_BRACKET:
- return TK_LSQUARE;
- case AKEYCODE_RIGHT_BRACKET:
- return TK_RSQUARE;
-
- default:
- return 0;
- }
-}
-
-static int processInputEvent(AInputEvent* event, TigrInternal* win) {
- if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
- int32_t action = AMotionEvent_getAction(event);
- int32_t actionCode = action & AMOTION_EVENT_ACTION_MASK;
-
- size_t touchPoints = AMotionEvent_getPointerCount(event);
- size_t releasedIndex = -1;
- if (actionCode == AMOTION_EVENT_ACTION_POINTER_UP) {
- releasedIndex =
- (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
- }
- size_t targetPointCount = 0;
-
- for (size_t i = 0; i < touchPoints && i < MAX_TOUCH_POINTS; i++) {
- if (i == releasedIndex) {
- continue;
- }
- gState.inputState.points[targetPointCount].x = AMotionEvent_getX(event, i);
- gState.inputState.points[targetPointCount].y = AMotionEvent_getY(event, i);
- targetPointCount++;
- }
-
- if (actionCode == AMOTION_EVENT_ACTION_UP || actionCode == AMOTION_EVENT_ACTION_CANCEL) {
- gState.inputState.numPoints = 0;
- } else {
- gState.inputState.numPoints = targetPointCount;
- }
- return 1;
- } else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) {
- if (!win) {
- return 1;
- }
- int32_t deviceID = AInputEvent_getDeviceId(event);
- // KeyCharacterMap#VIRTUAL_KEYBOARD == -1
- if (deviceID != -1) {
- return 1;
- }
- int32_t action = AKeyEvent_getAction(event);
- int32_t keyCode = AKeyEvent_getKeyCode(event);
- int key = tigrKeyFromAndroidKey(keyCode);
- // We pass the character in the scancode field from the Java side
- int32_t unicodeChar = AKeyEvent_getScanCode(event);
-
- if (action == AKEY_EVENT_ACTION_DOWN) {
- win->keys[key] = 1;
- win->lastChar = unicodeChar;
- } else if (action == AKEY_EVENT_ACTION_UP) {
- win->released[key] = 1;
- }
- return 1;
- }
- return 0;
-}
-
-static int handleEvent(AndroidEvent event, void* userData) {
- switch (event.type) {
- case AE_WINDOW_CREATED:
- gState.window = event.window;
- setupOpenGL();
- break;
-
- case AE_WINDOW_DESTROYED:
- tearDownOpenGL();
- gState.window = 0;
- break;
-
- case AE_INPUT:
- return processInputEvent(event.inputEvent, (TigrInternal*)userData);
-
- case AE_RESUME:
- gState.lastTime = event.time;
- break;
-
- case AE_CLOSE:
- gState.closed = 1;
- break;
-
- default:
- LOGE("Unhandled event type: %d", event.type);
- return 0;
- }
-
- return 1;
-}
-
-static int processEvents(TigrInternal* win) {
- if (gState.closed) {
- return 0;
- }
-
- while (android_pollEvent(handleEvent, win)) {
- if (gState.closed) {
- return 0;
- }
- }
-
- return 1;
-}
-
-static Tigr* refreshWindow(Tigr* bmp) {
- if (bmp->handle == gState.window) {
- return bmp;
- }
-
- bmp->handle = gState.window;
- if (gState.window == 0) {
- return 0;
- }
-
- TigrInternal* win = tigrInternal(bmp);
-
- int scale = 1;
- if (win->flags & TIGR_AUTO) {
- // Always use a 1:1 pixel size.
- scale = 1;
- } else {
- // See how big we can make it and still fit on-screen.
- scale = tigrCalcScale(bmp->w, bmp->h, gState.screenW, gState.screenH);
- }
-
- win->scale = tigrEnforceScale(scale, win->flags);
-
- return bmp;
-}
-
-/// TIGR interface implementation, called from render thread ///
-
-Tigr* tigrWindow(int w, int h, const char* title, int flags) {
- while (gState.window == NULL) {
- if (!processEvents(0)) {
- return NULL;
- }
- }
-
- EGLContext context = eglCreateContext(gState.display, gState.config, NULL, contextAttribs);
-
- int scale = 1;
- if (flags & TIGR_AUTO) {
- // Always use a 1:1 pixel size.
- scale = 1;
- } else {
- // See how big we can make it and still fit on-screen.
- scale = tigrCalcScale(w, h, gState.screenW, gState.screenH);
- }
-
- scale = tigrEnforceScale(scale, flags);
-
- Tigr* bmp = tigrBitmap2(w, h, sizeof(TigrInternal));
- bmp->handle = (void*)gState.window;
-
- TigrInternal* win = tigrInternal(bmp);
- win->context = context;
-
- win->shown = 0;
- win->closed = 0;
- win->scale = scale;
-
- win->lastChar = 0;
- win->flags = flags;
- win->p1 = win->p2 = win->p3 = 0;
- win->p4 = 1;
- win->widgetsWanted = 0;
- win->widgetAlpha = 0;
- win->widgetsScale = 0;
- win->widgets = 0;
- win->gl.gl_legacy = 0;
-
- memset(win->keys, 0, 256);
- memset(win->prev, 0, 256);
- memset(win->released, 0, 256);
-
- tigrPosition(bmp, win->scale, bmp->w, bmp->h, win->pos);
-
- if (eglMakeCurrent(gState.display, gState.surface, gState.surface, context) == EGL_FALSE) {
- LOGE("Unable to eglMakeCurrent");
- return 0;
- }
-
- tigrGAPICreate(bmp);
-
- return bmp;
-}
-
-int tigrClosed(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- return win->closed;
-}
-
-int tigrGAPIBegin(Tigr* bmp) {
- assert(gState.display != EGL_NO_DISPLAY);
- assert(gState.surface != EGL_NO_SURFACE);
-
- TigrInternal* win = tigrInternal(bmp);
- if (eglMakeCurrent(gState.display, gState.surface, gState.surface, win->context) == EGL_FALSE) {
- return -1;
- }
- return 0;
-}
-
-int tigrGAPIEnd(Tigr* bmp) {
- (void)bmp;
- eglMakeCurrent(gState.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- return 0;
-}
-
-int tigrKeyDown(Tigr* bmp, int key) {
- TigrInternal* win;
- assert(key < 256);
- win = tigrInternal(bmp);
- return win->keys[key] && !win->prev[key];
-}
-
-int tigrKeyHeld(Tigr* bmp, int key) {
- TigrInternal* win;
- assert(key < 256);
- win = tigrInternal(bmp);
- return win->keys[key];
-}
-
-int tigrReadChar(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- int c = win->lastChar;
- win->lastChar = 0;
- return c;
-}
-
-static void tigrUpdateModifiers(TigrInternal* win) {
- win->keys[TK_SHIFT] = win->keys[TK_LSHIFT] || win->keys[TK_RSHIFT];
- win->keys[TK_CONTROL] = win->keys[TK_LCONTROL] || win->keys[TK_RCONTROL];
- win->keys[TK_ALT] = win->keys[TK_LALT] || win->keys[TK_RALT];
-}
-
-static int toWindowX(TigrInternal* win, int x) {
- return (x - win->pos[0]) / win->scale;
-}
-
-static int toWindowY(TigrInternal* win, int y) {
- return (y - win->pos[1]) / win->scale;
-}
-
-void tigrUpdate(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- memcpy(win->prev, win->keys, 256);
- for (int i = 0; i < 256; i++) {
- win->keys[i] ^= win->released[i];
- win->released[i] = 0;
- }
-
- if (!processEvents(win)) {
- win->closed = 1;
- return;
- }
-
- tigrUpdateModifiers(win);
-
- if (gState.window == 0) {
- return;
- }
-
- bmp = refreshWindow(bmp);
- if (bmp == 0) {
- return;
- }
-
- win->numTouchPoints = gState.inputState.numPoints;
- for (int i = 0; i < win->numTouchPoints; i++) {
- win->touchPoints[i].x = toWindowX(win, gState.inputState.points[i].x);
- win->touchPoints[i].y = toWindowY(win, gState.inputState.points[i].y);
- }
-
- win->mouseButtons = win->numTouchPoints;
- if (win->mouseButtons > 0) {
- win->mouseX = win->touchPoints[0].x;
- win->mouseY = win->touchPoints[0].y;
- }
-
- if (win->flags & TIGR_AUTO) {
- tigrResize(bmp, gState.screenW / win->scale, gState.screenH / win->scale);
- } else {
- win->scale = tigrEnforceScale(tigrCalcScale(bmp->w, bmp->h, gState.screenW, gState.screenH), win->flags);
- }
-
- tigrPosition(bmp, win->scale, gState.screenW, gState.screenH, win->pos);
- tigrGAPIBegin(bmp);
- tigrGAPIPresent(bmp, gState.screenW, gState.screenH);
- android_swap(gState.display, gState.surface);
- tigrGAPIEnd(bmp);
-}
-
-void tigrFree(Tigr* bmp) {
- if (bmp->handle) {
- TigrInternal* win = tigrInternal(bmp);
-
- eglMakeCurrent(gState.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- if (win->context != EGL_NO_CONTEXT) {
- // Win closed means app windows has closed, and the call would fail.
- if (!win->closed) {
- tigrGAPIDestroy(bmp);
- }
- eglDestroyContext(gState.display, win->context);
- }
-
- win->context = EGL_NO_CONTEXT;
- }
- free(bmp->pix);
- free(bmp);
-}
-
-void tigrError(Tigr* bmp, const char* message, ...) {
- char tmp[1024];
-
- va_list args;
- va_start(args, message);
- vsnprintf(tmp, sizeof(tmp), message, args);
- tmp[sizeof(tmp) - 1] = 0;
- va_end(args);
-
- LOGE("tigr fatal error: %s\n", tmp);
-
- exit(1);
-}
-
-float tigrTime() {
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
-
- double now = (double)ts.tv_sec + (ts.tv_nsec / 1000000000.0);
- double elapsed = gState.lastTime == 0 ? 0 : now - gState.lastTime;
- gState.lastTime = now;
-
- return (float)elapsed;
-}
-
-void tigrMouse(Tigr* bmp, int* x, int* y, int* buttons) {
- TigrInternal* win = tigrInternal(bmp);
- if (x) {
- *x = win->mouseX;
- }
- if (y) {
- *y = win->mouseY;
- }
- if (buttons) {
- *buttons = win->mouseButtons;
- }
-}
-
-int tigrTouch(Tigr* bmp, TigrTouchPoint* points, int maxPoints) {
- TigrInternal* win = tigrInternal(bmp);
- for (int i = 0; i < maxPoints && i < win->numTouchPoints; i++) {
- points[i] = win->touchPoints[i];
- }
- return maxPoints < win->numTouchPoints ? maxPoints : win->numTouchPoints;
-}
-
-void tigrMouseWheel(Tigr* bmp, float* x, float* y) {
- if (x) {
- *x = 0;
- }
- if (y) {
- *y = 0;
- }
-}
-
-void* tigrReadFile(const char* fileName, int* length) {
- if (length != 0) {
- *length = 0;
- }
-
- void* asset = android_loadAsset(fileName, length);
- return asset;
-}
-
-#endif // __ANDROID__
-#endif // #ifndef TIGR_HEADLESS
-
-//////// End of inlined file: tigr_android.c ////////
-
-//////// Start of inlined file: tigr_gl.c ////////
-
-#ifndef TIGR_HEADLESS
-
-//#include "tigr_internal.h"
-#include <assert.h>
-
-#ifdef TIGR_GAPI_GL
-#if __linux__
-#if __ANDROID__
-#include <GLES3/gl3.h>
-#else
-#define GLX_GLXEXT_PROTOTYPES
-#include <GL/glext.h>
-#endif
-#endif
-extern const char tigr_upscale_gl_vs[], tigr_upscale_gl_fs[], tigr_default_fx_gl_fs[];
-extern const int tigr_upscale_gl_vs_size, tigr_upscale_gl_fs_size, tigr_default_fx_gl_fs_size;
-
-#ifdef _WIN32
-
-#ifdef TIGR_GAPI_GL_WIN_USE_GLEXT
-#include <glext.h>
-#include <wglext.h>
-#else // short version of glext.h and wglext.h so we don't need to depend on them
-#ifndef APIENTRY
-#define APIENTRY
-#endif
-#ifndef APIENTRYP
-#define APIENTRYP APIENTRY*
-#endif
-typedef ptrdiff_t GLsizeiptr;
-#define GL_COMPILE_STATUS 0x8B81
-#define GL_LINK_STATUS 0x8B82
-#define GL_ARRAY_BUFFER 0x8892
-#define GL_STATIC_DRAW 0x88E4
-#define GL_VERTEX_SHADER 0x8B31
-#define GL_FRAGMENT_SHADER 0x8B30
-#define GL_BGRA 0x80E1
-#define GL_TEXTURE0 0x84C0
-typedef void(APIENTRYP PFNGLGENVERTEXARRAYSPROC)(GLsizei n, GLuint* arrays);
-typedef void(APIENTRYP PFNGLGENBUFFERSARBPROC)(GLsizei n, GLuint* buffers);
-typedef void(APIENTRYP PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer);
-typedef void(APIENTRYP PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
-typedef void(APIENTRYP PFNGLBINDVERTEXARRAYPROC)(GLuint array);
-typedef void(APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index);
-typedef void(APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index,
- GLint size,
- GLenum type,
- GLboolean normalized,
- GLsizei stride,
- const void* pointer);
-typedef GLuint(APIENTRYP PFNGLCREATESHADERPROC)(GLenum type);
-typedef char GLchar;
-typedef void(APIENTRYP PFNGLSHADERSOURCEPROC)(GLuint shader,
- GLsizei count,
- const GLchar* const* string,
- const GLint* length);
-typedef void(APIENTRYP PFNGLCOMPILESHADERPROC)(GLuint shader);
-typedef GLuint(APIENTRYP PFNGLCREATEPROGRAMPROC)(void);
-typedef void(APIENTRYP PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader);
-typedef void(APIENTRYP PFNGLLINKPROGRAMPROC)(GLuint program);
-typedef void(APIENTRYP PFNGLDELETESHADERPROC)(GLuint shader);
-typedef void(APIENTRYP PFNGLDELETEPROGRAMPROC)(GLuint program);
-typedef void(APIENTRYP PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint* params);
-typedef void(APIENTRYP PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
-typedef void(APIENTRYP PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint* params);
-typedef void(APIENTRYP PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
-typedef void(APIENTRYP PFNGLUSEPROGRAMPROC)(GLuint program);
-typedef GLint(APIENTRYP PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar* name);
-typedef void(APIENTRYP PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
-typedef void(APIENTRYP PFNGLUNIFORMMATRIX4FVPROC)(GLint location,
- GLsizei count,
- GLboolean transpose,
- const GLfloat* value);
-typedef void(APIENTRYP PFNGLACTIVETEXTUREPROC)(GLenum texture);
-#define WGL_DRAW_TO_WINDOW_ARB 0x2001
-#define WGL_SUPPORT_OPENGL_ARB 0x2010
-#define WGL_DOUBLE_BUFFER_ARB 0x2011
-#define WGL_PIXEL_TYPE_ARB 0x2013
-#define WGL_COLOR_BITS_ARB 0x2014
-#define WGL_DEPTH_BITS_ARB 0x2022
-#define WGL_STENCIL_BITS_ARB 0x2023
-#define WGL_TYPE_RGBA_ARB 0x202B
-#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
-#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
-typedef BOOL(WINAPI* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc,
- const int* piAttribIList,
- const FLOAT* pfAttribFList,
- UINT nMaxFormats,
- int* piFormats,
- UINT* nNumFormats);
-typedef HGLRC(WINAPI* PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC, HGLRC hShareContext, const int* attribList);
-#endif
-
-PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat;
-PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs;
-PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
-PFNGLGENBUFFERSARBPROC glGenBuffers;
-PFNGLBINDBUFFERPROC glBindBuffer;
-PFNGLBUFFERDATAPROC glBufferData;
-PFNGLBINDVERTEXARRAYPROC glBindVertexArray;
-PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
-PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
-PFNGLCREATESHADERPROC glCreateShader;
-PFNGLSHADERSOURCEPROC glShaderSource;
-PFNGLCOMPILESHADERPROC glCompileShader;
-PFNGLCREATEPROGRAMPROC glCreateProgram;
-PFNGLATTACHSHADERPROC glAttachShader;
-PFNGLLINKPROGRAMPROC glLinkProgram;
-PFNGLDELETESHADERPROC glDeleteShader;
-PFNGLDELETEPROGRAMPROC glDeleteProgram;
-PFNGLGETSHADERIVPROC glGetShaderiv;
-PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
-PFNGLGETPROGRAMIVPROC glGetProgramiv;
-PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
-PFNGLUSEPROGRAMPROC glUseProgram;
-PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
-PFNGLUNIFORM4FPROC glUniform4f;
-PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv;
-PFNGLACTIVETEXTUREPROC glActiveTexture;
-int tigrGL11Init(Tigr* bmp) {
- int pixel_format;
- TigrInternal* win = tigrInternal(bmp);
- GLStuff* gl = &win->gl;
- PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR),
- 1,
- PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_SWAP_EXCHANGE,
- PFD_TYPE_RGBA,
- 32, // color bits
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 24, // depth
- 8, // stencil
- 0,
- PFD_MAIN_PLANE, // is it ignored ?
- 0,
- 0,
- 0,
- 0 };
- if (!(gl->dc = GetDC((HWND)bmp->handle))) {
- tigrError(bmp, "Cannot create OpenGL device context.\n");
- return -1;
- }
- if (!(pixel_format = ChoosePixelFormat(gl->dc, &pfd))) {
- tigrError(bmp, "Cannot choose OpenGL pixel format.\n");
- return -1;
- }
- if (!SetPixelFormat(gl->dc, pixel_format, &pfd)) {
- tigrError(bmp, "Cannot set OpenGL pixel format.\n");
- return -1;
- }
- if (!(gl->hglrc = wglCreateContext(gl->dc))) {
- tigrError(bmp, "Cannot create OpenGL context.\n");
- return -1;
- }
- if (!wglMakeCurrent(gl->dc, gl->hglrc)) {
- tigrError(bmp, "Cannot activate OpenGL context.\n");
- return -1;
- }
- gl->gl_legacy = 1;
- return 0;
-}
-int tigrGL33Init(Tigr* bmp) {
- int pixel_format;
- UINT num_formats;
- TigrInternal* win = tigrInternal(bmp);
- GLStuff* gl = &win->gl;
-
- wglChoosePixelFormat = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
- wglCreateContextAttribs = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
- glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)wglGetProcAddress("glGenVertexArrays");
- glGenBuffers = (PFNGLGENBUFFERSARBPROC)wglGetProcAddress("glGenBuffers");
- glBindBuffer = (PFNGLBINDBUFFERPROC)wglGetProcAddress("glBindBuffer");
- glBufferData = (PFNGLBUFFERDATAPROC)wglGetProcAddress("glBufferData");
- glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)wglGetProcAddress("glBindVertexArray");
- glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)wglGetProcAddress("glEnableVertexAttribArray");
- glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)wglGetProcAddress("glVertexAttribPointer");
- glCreateShader = (PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader");
- glShaderSource = (PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource");
- glCompileShader = (PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader");
- glCreateProgram = (PFNGLCREATEPROGRAMPROC)wglGetProcAddress("glCreateProgram");
- glAttachShader = (PFNGLATTACHSHADERPROC)wglGetProcAddress("glAttachShader");
- glLinkProgram = (PFNGLLINKPROGRAMPROC)wglGetProcAddress("glLinkProgram");
- glDeleteShader = (PFNGLDELETESHADERPROC)wglGetProcAddress("glDeleteShader");
- glDeleteProgram = (PFNGLDELETEPROGRAMPROC)wglGetProcAddress("glDeleteProgram");
- glGetShaderiv = (PFNGLGETSHADERIVPROC)wglGetProcAddress("glGetShaderiv");
- glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)wglGetProcAddress("glGetShaderInfoLog");
- glGetProgramiv = (PFNGLGETPROGRAMIVPROC)wglGetProcAddress("glGetProgramiv");
- glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)wglGetProcAddress("glGetProgramInfoLog");
- glUseProgram = (PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram");
- glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)wglGetProcAddress("glGetUniformLocation");
- glUniform4f = (PFNGLUNIFORM4FPROC)wglGetProcAddress("glUniform4f");
- glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)wglGetProcAddress("glUniformMatrix4fv");
- glActiveTexture = (PFNGLACTIVETEXTUREPROC)wglGetProcAddress("glActiveTexture");
-
- if (!wglChoosePixelFormat || !wglCreateContextAttribs) {
- tigrError(bmp, "Cannot create OpenGL context.\n");
- return -1;
- }
- const int attribList[] = { WGL_DRAW_TO_WINDOW_ARB,
- GL_TRUE,
- WGL_SUPPORT_OPENGL_ARB,
- GL_TRUE,
- WGL_DOUBLE_BUFFER_ARB,
- GL_TRUE,
- WGL_PIXEL_TYPE_ARB,
- WGL_TYPE_RGBA_ARB,
- WGL_COLOR_BITS_ARB,
- 32,
- WGL_DEPTH_BITS_ARB,
- 24,
- WGL_STENCIL_BITS_ARB,
- 8,
- 0 };
- int attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 3, 0 };
- if (!wglChoosePixelFormat(gl->dc, attribList, NULL, 1, &pixel_format, &num_formats)) {
- tigrError(bmp, "Cannot choose OpenGL pixel format.\n");
- return -1;
- }
- if (!(gl->hglrc = wglCreateContextAttribs(gl->dc, gl->hglrc, attribs))) {
- tigrError(bmp, "Cannot create OpenGL context attribs.\n");
- return -1;
- }
- if (!wglMakeCurrent(gl->dc, gl->hglrc)) {
- tigrError(bmp, "Cannot activate OpenGL context.\n");
- return -1;
- }
- gl->gl_legacy = 0;
- return 0;
-}
-#endif
-
-void tigrCheckGLError(const char* state) {
- GLenum err = glGetError();
- if (err != GL_NO_ERROR) {
- tigrError(NULL, "got GL error %x when doing %s\n", err, state);
- }
-}
-
-void tigrCheckShaderErrors(GLuint object) {
- GLint success;
- GLchar info[2048];
- glGetShaderiv(object, GL_COMPILE_STATUS, &success);
- if (!success) {
- glGetShaderInfoLog(object, sizeof(info), NULL, info);
- tigrError(NULL, "shader compile error : %s\n", info);
- }
-}
-
-void tigrCheckProgramErrors(GLuint object) {
- GLint success;
- GLchar info[2048];
- glGetProgramiv(object, GL_LINK_STATUS, &success);
- if (!success) {
- glGetProgramInfoLog(object, sizeof(info), NULL, info);
- tigrError(NULL, "shader link error : %s\n", info);
- }
-}
-
-void tigrCreateShaderProgram(GLStuff* gl, const char* fxSource, int fxSize) {
- if (gl->program != 0) {
- glDeleteProgram(gl->program);
- gl->program = 0;
- }
-
- GLuint vs = glCreateShader(GL_VERTEX_SHADER);
- const char* vs_source = (const char*)&tigr_upscale_gl_vs;
- glShaderSource(vs, 1, &vs_source, &tigr_upscale_gl_vs_size);
- glCompileShader(vs);
- tigrCheckShaderErrors(vs);
-
- GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
- const char* fs_sources[] = {
- (const char*)tigr_upscale_gl_fs,
- fxSource,
- };
- const int fs_lengths[] = {
- tigr_upscale_gl_fs_size,
- fxSize,
- };
- glShaderSource(fs, 2, fs_sources, fs_lengths);
- glCompileShader(fs);
- tigrCheckShaderErrors(fs);
-
- gl->program = glCreateProgram();
- glAttachShader(gl->program, vs);
- glAttachShader(gl->program, fs);
- glLinkProgram(gl->program);
- tigrCheckProgramErrors(gl->program);
- glDeleteShader(vs);
- glDeleteShader(fs);
-
- gl->uniform_projection = glGetUniformLocation(gl->program, "projection");
- gl->uniform_model = glGetUniformLocation(gl->program, "model");
- gl->uniform_parameters = glGetUniformLocation(gl->program, "parameters");
-}
-
-void tigrGAPICreate(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- GLStuff* gl = &win->gl;
- GLuint VBO;
- GLfloat vertices[] = { // pos uv
- 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
- 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f
- };
-
-#ifdef _WIN32
- if (tigrGL11Init(bmp))
- return;
- tigrGL33Init(bmp);
-#endif
-
- if (!gl->gl_legacy) {
- // create vao
- glGenVertexArrays(1, &gl->vao);
- glGenBuffers(1, &VBO);
- glBindBuffer(GL_ARRAY_BUFFER, VBO);
- glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
- glBindVertexArray(gl->vao);
- glEnableVertexAttribArray(0);
- glEnableVertexAttribArray(1);
- glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), NULL);
- glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), NULL);
-
- // create program
- tigrCreateShaderProgram(gl, tigr_default_fx_gl_fs, tigr_default_fx_gl_fs_size);
- }
-
- // create textures
- if (gl->gl_legacy) {
- glEnable(GL_TEXTURE_2D);
- }
- glGenTextures(2, gl->tex);
- for (int i = 0; i < 2; ++i) {
- glBindTexture(GL_TEXTURE_2D, gl->tex[i]);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl->gl_legacy ? GL_NEAREST : GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl->gl_legacy ? GL_NEAREST : GL_LINEAR);
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- }
-
- tigrCheckGLError("initialization");
-}
-
-void tigrGAPIDestroy(Tigr* bmp) {
- TigrInternal* win = tigrInternal(bmp);
- GLStuff* gl = &win->gl;
-
- if (tigrGAPIBegin(bmp) < 0) {
- tigrError(bmp, "Cannot activate OpenGL context.\n");
- return;
- }
-
- if (!gl->gl_legacy) {
- glDeleteTextures(2, gl->tex);
- glDeleteProgram(gl->program);
- }
-
- tigrCheckGLError("destroy");
-
- if (tigrGAPIEnd(bmp) < 0) {
- tigrError(bmp, "Cannot deactivate OpenGL context.\n");
- return;
- }
-}
-
-void tigrGAPIDraw(int legacy, GLuint uniform_model, GLuint tex, Tigr* bmp, int x1, int y1, int x2, int y2) {
- glBindTexture(GL_TEXTURE_2D, tex);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, bmp->w, bmp->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, bmp->pix);
-
- if (!legacy) {
- float sx = (float)(x2 - x1);
- float sy = (float)(y2 - y1);
- float tx = (float)x1;
- float ty = (float)y1;
-
- float model[16] = { sx, 0.0f, 0.0f, 0.0f, 0.0f, sy, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, tx, ty, 0.0f, 1.0f };
-
- glUniformMatrix4fv(uniform_model, 1, GL_FALSE, model);
- glDrawArrays(GL_TRIANGLES, 0, 6);
- } else {
-#if !(__APPLE__ || __ANDROID__)
- glBegin(GL_QUADS);
- glTexCoord2f(1.0f, 0.0f);
- glVertex2i(x2, y1);
- glTexCoord2f(0.0f, 0.0f);
- glVertex2i(x1, y1);
- glTexCoord2f(0.0f, 1.0f);
- glVertex2i(x1, y2);
- glTexCoord2f(1.0f, 1.0f);
- glVertex2i(x2, y2);
- glEnd();
-#else
- assert(0);
-#endif
- }
-}
-
-void tigrGAPIPresent(Tigr* bmp, int w, int h) {
- TigrInternal* win = tigrInternal(bmp);
- GLStuff* gl = &win->gl;
-
- glViewport(0, 0, w, h);
- if (!gl->gl_user_opengl_rendering) {
- glClearColor(0, 0, 0, 1);
- glClear(GL_COLOR_BUFFER_BIT);
- }
-
- if (!gl->gl_legacy) {
- float projection[16] = { 2.0f / w, 0.0f, 0.0f, 0.0f, 0.0f, -2.0f / h, 0.0f, 0.0f,
- 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f, 1.0f };
-
- glActiveTexture(GL_TEXTURE0);
- glBindVertexArray(gl->vao);
- glUseProgram(gl->program);
- glUniformMatrix4fv(gl->uniform_projection, 1, GL_FALSE, projection);
- glUniform4f(gl->uniform_parameters, win->p1, win->p2, win->p3, win->p4);
- } else {
-#if !(__APPLE__ || __ANDROID__)
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrtho(0, w, h, 0, -1.0f, 1.0f);
- glEnable(GL_TEXTURE_2D);
-#else
- assert(0);
-#endif
- }
-
- if (gl->gl_user_opengl_rendering) {
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- } else {
- glDisable(GL_BLEND);
- }
- tigrGAPIDraw(gl->gl_legacy, gl->uniform_model, gl->tex[0], bmp, win->pos[0], win->pos[1], win->pos[2], win->pos[3]);
-
- if (win->widgetsScale > 0) {
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- tigrGAPIDraw(gl->gl_legacy, gl->uniform_model, gl->tex[1], win->widgets,
- (int)(w - win->widgets->w * win->widgetsScale), 0, w, (int)(win->widgets->h * win->widgetsScale));
- }
-
- tigrCheckGLError("present");
-
- gl->gl_user_opengl_rendering = 0;
-}
-
-#endif
-#endif // #ifndef TIGR_HEADLESS
-//////// End of inlined file: tigr_gl.c ////////
-
-//////// Start of inlined file: tigr_utils.c ////////
-
-//#include "tigr_internal.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-#ifndef __ANDROID__
-
-#ifdef __IOS__
-void* _tigrReadFile(const char* fileName, int* length) {
-#else
-void* tigrReadFile(const char* fileName, int* length) {
-#endif
- // TODO - unicode?
- FILE* file;
- char* data;
- size_t len;
-
- if (length)
- *length = 0;
-
- file = fopen(fileName, "rb");
- if (!file)
- return NULL;
-
- fseek(file, 0, SEEK_END);
- len = ftell(file);
- fseek(file, 0, SEEK_SET);
-
- data = (char*)malloc(len + 1);
- if (!data) {
- fclose(file);
- return NULL;
- }
-
- if (fread(data, 1, len, file) != len) {
- free(data);
- fclose(file);
- return NULL;
- }
- data[len] = '\0';
- fclose(file);
-
- if (length)
- *length = len;
-
- return data;
-}
-
-#endif // __ANDROID__
-
-// Reads a single UTF8 codepoint.
-const char* tigrDecodeUTF8(const char* text, int* cp) {
- unsigned char c = *text++;
- int extra = 0, min = 0;
- *cp = 0;
- if (c >= 0xf0) {
- *cp = c & 0x07;
- extra = 3;
- min = 0x10000;
- } else if (c >= 0xe0) {
- *cp = c & 0x0f;
- extra = 2;
- min = 0x800;
- } else if (c >= 0xc0) {
- *cp = c & 0x1f;
- extra = 1;
- min = 0x80;
- } else if (c >= 0x80) {
- *cp = 0xfffd;
- } else {
- *cp = c;
- }
- while (extra--) {
- c = *text++;
- if ((c & 0xc0) != 0x80) {
- *cp = 0xfffd;
- break;
- }
- (*cp) = ((*cp) << 6) | (c & 0x3f);
- }
- if (*cp < min) {
- *cp = 0xfffd;
- }
- return text;
-}
-
-char* tigrEncodeUTF8(char* text, int cp) {
- if (cp < 0 || cp > 0x10ffff) {
- cp = 0xfffd;
- }
-
-#define EMIT(X, Y, Z) *text++ = X | ((cp >> Y) & Z)
- if (cp < 0x80) {
- EMIT(0x00, 0, 0x7f);
- } else if (cp < 0x800) {
- EMIT(0xc0, 6, 0x1f);
- EMIT(0x80, 0, 0x3f);
- } else if (cp < 0x10000) {
- EMIT(0xe0, 12, 0xf);
- EMIT(0x80, 6, 0x3f);
- EMIT(0x80, 0, 0x3f);
- } else {
- EMIT(0xf0, 18, 0x7);
- EMIT(0x80, 12, 0x3f);
- EMIT(0x80, 6, 0x3f);
- EMIT(0x80, 0, 0x3f);
- }
- return text;
-#undef EMIT
-}
-
-#ifndef TIGR_HEADLESS
-
-int tigrBeginOpenGL(Tigr* bmp) {
-#ifdef TIGR_GAPI_GL
- TigrInternal* win = tigrInternal(bmp);
- win->gl.gl_user_opengl_rendering = 1;
- return tigrGAPIBegin(bmp) == 0;
-#else
- return 0;
-#endif
-}
-
-void tigrSetPostShader(Tigr* bmp, const char* code, int size) {
-#ifdef TIGR_GAPI_GL
- tigrGAPIBegin(bmp);
- TigrInternal* win = tigrInternal(bmp);
- GLStuff* gl = &win->gl;
- tigrCreateShaderProgram(gl, code, size);
- tigrGAPIEnd(bmp);
-#endif
-}
-
-void tigrSetPostFX(Tigr* bmp, float p1, float p2, float p3, float p4) {
- TigrInternal* win = tigrInternal(bmp);
- win->p1 = p1;
- win->p2 = p2;
- win->p3 = p3;
- win->p4 = p4;
-}
-
-#endif // TIGR_HEADLESS
-
-//////// End of inlined file: tigr_utils.c ////////
-
-
-//////// End of inlined file: tigr_amalgamated.c ////////
-
-
diff --git a/tigr.h b/tigr.h
deleted file mode 100644
index 0e35f54..0000000
--- a/tigr.h
+++ /dev/null
@@ -1,366 +0,0 @@
-// TIGR - TIny GRaphics Library - v3.2
-// ^^ ^^
-//
-// rawr.
-
-/*
-This is free and unencumbered software released into the public domain.
-
-Our intent is that anyone is free to copy and use this software,
-for any purpose, in any form, and by any means.
-
-The authors dedicate any and all copyright interest in the software
-to the public domain, at their own expense for the betterment of mankind.
-
-The software is provided "as is", without any kind of warranty, including
-any implied warranty. If it breaks, you get to keep both pieces.
-*/
-
-#pragma once
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Compiler configuration.
-#ifdef _MSC_VER
-#define TIGR_INLINE static __forceinline
-#else
-#define TIGR_INLINE static inline
-#endif
-
-// Bitmaps ----------------------------------------------------------------
-
-// This struct contains one pixel.
-typedef struct {
- unsigned char r, g, b, a;
-} TPixel;
-
-// Window flags.
-#define TIGR_FIXED 0 // window's bitmap is a fixed size (default)
-#define TIGR_AUTO 1 // window's bitmap is scaled with the window
-#define TIGR_2X 2 // always enforce (at least) 2X pixel scale
-#define TIGR_3X 4 // always enforce (at least) 3X pixel scale
-#define TIGR_4X 8 // always enforce (at least) 4X pixel scale
-#define TIGR_RETINA 16 // enable retina support on OS X
-#define TIGR_NOCURSOR 32 // hide cursor
-#define TIGR_FULLSCREEN 64 // start in full-screen mode
-
-// A Tigr bitmap.
-typedef struct Tigr {
- int w, h; // width/height (unscaled)
- int cx, cy, cw, ch; // clip rect
- TPixel *pix; // pixel data
- void *handle; // OS window handle, NULL for off-screen bitmaps.
- int blitMode; // Target bitmap blit mode
-} Tigr;
-
-// Creates a new empty window with a given bitmap size.
-//
-// Title is UTF-8.
-//
-// In TIGR_FIXED mode, the window is made as large as possible to contain an integer-scaled
-// version of the bitmap while still fitting on the screen. Resizing the window will adapt
-// the scale in integer steps to fit the bitmap.
-//
-// In TIGR_AUTO mode, the initial window size is set to the bitmap size times the pixel
-// scale. Resizing the window will resize the bitmap using the specified scale.
-// For example, in forced 2X mode, the window will be twice as wide (and high) as the bitmap.
-//
-// Turning on TIGR_RETINA mode will request full backing resolution on OSX, meaning that
-// the effective window size might be integer scaled to a larger size. In TIGR_AUTO mode,
-// this means that the Tigr bitmap will change size if the window is moved between
-// retina and non-retina screens.
-//
-Tigr *tigrWindow(int w, int h, const char *title, int flags);
-
-// Creates an empty off-screen bitmap.
-Tigr *tigrBitmap(int w, int h);
-
-// Deletes a window/bitmap.
-void tigrFree(Tigr *bmp);
-
-// Returns non-zero if the user requested to close a window.
-int tigrClosed(Tigr *bmp);
-
-// Displays a window's contents on-screen and updates input.
-void tigrUpdate(Tigr *bmp);
-
-// Called before doing direct OpenGL calls and before tigrUpdate.
-// Returns non-zero if OpenGL is available.
-int tigrBeginOpenGL(Tigr *bmp);
-
-// Sets post shader for a window.
-// This replaces the built-in post-FX shader.
-void tigrSetPostShader(Tigr *bmp, const char* code, int size);
-
-// Sets post-FX properties for a window.
-//
-// The built-in post-FX shader uses the following parameters:
-// p1: hblur - use bilinear filtering along the x-axis (pixels)
-// p2: vblur - use bilinear filtering along the y-axis (pixels)
-// p3: scanlines - CRT scanlines effect (0-1)
-// p4: contrast - contrast boost (1 = no change, 2 = 2X contrast, etc)
-void tigrSetPostFX(Tigr *bmp, float p1, float p2, float p3, float p4);
-
-
-// Drawing ----------------------------------------------------------------
-
-// Helper for reading pixels.
-// For high performance, just access bmp->pix directly.
-TPixel tigrGet(Tigr *bmp, int x, int y);
-
-// Plots a pixel.
-// Clips and blends.
-// For high performance, just access bmp->pix directly.
-void tigrPlot(Tigr *bmp, int x, int y, TPixel pix);
-
-// Clears a bitmap to a color.
-// No blending, no clipping.
-void tigrClear(Tigr *bmp, TPixel color);
-
-// Fills a rectangular area.
-// No blending, no clipping.
-void tigrFill(Tigr *bmp, int x, int y, int w, int h, TPixel color);
-
-// Draws a line.
-// Start pixel is drawn, end pixel is not.
-// Clips and blends.
-void tigrLine(Tigr *bmp, int x0, int y0, int x1, int y1, TPixel color);
-
-// Draws an empty rectangle.
-// Drawing a 1x1 rectangle yields the same result as calling tigrPlot.
-// Clips and blends.
-void tigrRect(Tigr *bmp, int x, int y, int w, int h, TPixel color);
-
-// Fills a rectangle.
-// Fills the inside of the specified rectangular area.
-// Calling tigrRect followed by tigrFillRect using the same arguments
-// causes no overdrawing.
-// Clips and blends.
-void tigrFillRect(Tigr *bmp, int x, int y, int w, int h, TPixel color);
-
-// Draws a circle.
-// Drawing a zero radius circle yields the same result as calling tigrPlot.
-// Drawing a circle with radius one draws a circle three pixels wide.
-// Clips and blends.
-void tigrCircle(Tigr *bmp, int x, int y, int r, TPixel color);
-
-// Fills a circle.
-// Fills the inside of the specified circle.
-// Calling tigrCircle followed by tigrFillCircle using the same arguments
-// causes no overdrawing.
-// Filling a circle with zero radius has no effect.
-// Clips and blends.
-void tigrFillCircle(Tigr *bmp, int x, int y, int r, TPixel color);
-
-// Sets clip rect.
-// Set to (0, 0, -1, -1) to reset clipping to full bitmap.
-void tigrClip(Tigr *bmp, int cx, int cy, int cw, int ch);
-
-// Copies bitmap data.
-// dx/dy = dest co-ordinates
-// sx/sy = source co-ordinates
-// w/h = width/height
-//
-// RGBAdest = RGBAsrc
-// Clips, does not blend.
-void tigrBlit(Tigr *dest, Tigr *src, int dx, int dy, int sx, int sy, int w, int h);
-
-// Same as tigrBlit, but alpha blends the source bitmap with the
-// target using per pixel alpha and the specified global alpha.
-//
-// Ablend = Asrc * alpha
-// RGBdest = RGBsrc * Ablend + RGBdest * (1 - Ablend)
-//
-// Blit mode == TIGR_KEEP_ALPHA:
-// Adest = Adest
-//
-// Blit mode == TIGR_BLEND_ALPHA:
-// Adest = Asrc * Ablend + Adest * (1 - Ablend)
-// Clips and blends.
-void tigrBlitAlpha(Tigr *dest, Tigr *src, int dx, int dy, int sx, int sy, int w, int h, float alpha);
-
-// Same as tigrBlit, but tints the source bitmap with a color
-// and alpha blends the resulting source with the destination.
-//
-// Rblend = Rsrc * Rtint
-// Gblend = Gsrc * Gtint
-// Bblend = Bsrc * Btint
-// Ablend = Asrc * Atint
-//
-// RGBdest = RGBblend * Ablend + RGBdest * (1 - Ablend)
-//
-// Blit mode == TIGR_KEEP_ALPHA:
-// Adest = Adest
-//
-// Blit mode == TIGR_BLEND_ALPHA:
-// Adest = Ablend * Ablend + Adest * (1 - Ablend)
-// Clips and blends.
-void tigrBlitTint(Tigr *dest, Tigr *src, int dx, int dy, int sx, int sy, int w, int h, TPixel tint);
-
-enum TIGRBlitMode {
- TIGR_KEEP_ALPHA = 0, // Keep destination alpha value
- TIGR_BLEND_ALPHA = 1, // Blend destination alpha (default)
-};
-
-// Set destination bitmap blend mode for blit operations.
-void tigrBlitMode(Tigr *dest, int mode);
-
-// Helper for making colors.
-TIGR_INLINE TPixel tigrRGB(unsigned char r, unsigned char g, unsigned char b)
-{
- TPixel p; p.r = r; p.g = g; p.b = b; p.a = 0xff; return p;
-}
-
-// Helper for making colors.
-TIGR_INLINE TPixel tigrRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
-{
- TPixel p; p.r = r; p.g = g; p.b = b; p.a = a; return p;
-}
-
-
-// Font printing ----------------------------------------------------------
-
-typedef struct {
- int code, x, y, w, h;
-} TigrGlyph;
-
-typedef struct {
- Tigr *bitmap;
- int numGlyphs;
- TigrGlyph *glyphs;
-} TigrFont;
-
-typedef enum {
- TCP_ASCII = 0,
- TCP_1252 = 1252,
- TCP_UTF32 = 12001
-} TCodepage;
-
-// Loads a font from a bitmap font sheet.
-// The loaded font takes ownership of the provided bitmap.
-//
-// Codepages:
-//
-// TCP_ASCII - Regular 7-bit ASCII
-// TCP_1252 - Windows 1252
-// TCP_UTF32 - Unicode subset
-//
-// For ASCII and 1252, the font bitmap should contain all characters
-// for the given codepage, excluding the first 32 control codes.
-//
-// For UTF32 - the font bitmap contains a subset of Unicode characters
-// and must be in the format generated by tigrFont for UTF32.
-//
-TigrFont *tigrLoadFont(Tigr *bitmap, int codepage);
-
-// Frees a font and associated font sheet.
-void tigrFreeFont(TigrFont *font);
-
-// Prints UTF-8 text onto a bitmap.
-// NOTE:
-// This uses the target bitmap blit mode.
-// See tigrBlitTint for details.
-void tigrPrint(Tigr *dest, TigrFont *font, int x, int y, TPixel color, const char *text, ...);
-
-// Returns the width/height of a string.
-int tigrTextWidth(TigrFont *font, const char *text);
-int tigrTextHeight(TigrFont *font, const char *text);
-
-// The built-in font.
-extern TigrFont *tfont;
-
-
-// User Input -------------------------------------------------------------
-
-// Key scancodes. For letters/numbers, use ASCII ('A'-'Z' and '0'-'9').
-typedef enum {
- TK_PAD0=128,TK_PAD1,TK_PAD2,TK_PAD3,TK_PAD4,TK_PAD5,TK_PAD6,TK_PAD7,TK_PAD8,TK_PAD9,
- TK_PADMUL,TK_PADADD,TK_PADENTER,TK_PADSUB,TK_PADDOT,TK_PADDIV,
- TK_F1,TK_F2,TK_F3,TK_F4,TK_F5,TK_F6,TK_F7,TK_F8,TK_F9,TK_F10,TK_F11,TK_F12,
- TK_BACKSPACE,TK_TAB,TK_RETURN,TK_SHIFT,TK_CONTROL,TK_ALT,TK_PAUSE,TK_CAPSLOCK,
- TK_ESCAPE,TK_SPACE,TK_PAGEUP,TK_PAGEDN,TK_END,TK_HOME,TK_LEFT,TK_UP,TK_RIGHT,TK_DOWN,
- TK_INSERT,TK_DELETE,TK_LWIN,TK_RWIN,TK_NUMLOCK,TK_SCROLL,TK_LSHIFT,TK_RSHIFT,
- TK_LCONTROL,TK_RCONTROL,TK_LALT,TK_RALT,TK_SEMICOLON,TK_EQUALS,TK_COMMA,TK_MINUS,
- TK_DOT,TK_SLASH,TK_BACKTICK,TK_LSQUARE,TK_BACKSLASH,TK_RSQUARE,TK_TICK
-} TKey;
-
-// Returns mouse input for a window.
-// The value set to "buttons" is a bit set where bits 0, 1 and 2
-// corresponds to the left, right and middle buttons.
-// A set bit indicates that a button is held.
-void tigrMouse(Tigr *bmp, int *x, int *y, int *buttons);
-
-typedef struct {
- int x;
- int y;
-} TigrTouchPoint;
-
-// Reads touch input for a window.
-// Returns number of touch points read.
-int tigrTouch(Tigr *bmp, TigrTouchPoint* points, int maxPoints);
-
-// Reads the delta of the scroll "wheel" in somewhat platform neutral
-// units where 1.0 corresponds to a "notch". The actual correlation between
-// physical movement and this number varies between platforms, input methods
-// and settings.
-void tigrScrollWheel(Tigr* bmp, float* x, float *y);
-
-// Reads the keyboard for a window.
-// Returns non-zero if a key is pressed/held.
-// tigrKeyDown tests for the initial press, tigrKeyHeld repeats each frame.
-int tigrKeyDown(Tigr *bmp, int key);
-int tigrKeyHeld(Tigr *bmp, int key);
-
-// Reads character input for a window.
-// Returns the Unicode value of the last key pressed, or 0 if none.
-int tigrReadChar(Tigr *bmp);
-
-// Show / hide virtual keyboard.
-// (Only available on iOS / Android)
-void tigrShowKeyboard(int show);
-
-
-// Bitmap I/O -------------------------------------------------------------
-
-// Loads a PNG, from either a file or memory. (fileName is UTF-8)
-// On error, returns NULL and sets errno.
-Tigr *tigrLoadImage(const char *fileName);
-Tigr *tigrLoadImageMem(const void *data, int length);
-
-// Saves a PNG to a file. (fileName is UTF-8)
-// On error, returns zero and sets errno.
-int tigrSaveImage(const char *fileName, Tigr *bmp);
-
-
-// Helpers ----------------------------------------------------------------
-
-// Returns the amount of time elapsed since tigrTime was last called,
-// or zero on the first call.
-float tigrTime(void);
-
-// Displays an error message and quits. (UTF-8)
-// 'bmp' can be NULL.
-void tigrError(Tigr *bmp, const char *message, ...);
-
-// Reads an entire file into memory. (fileName is UTF-8)
-// Free it yourself after with 'free'.
-// On error, returns NULL and sets errno.
-// TIGR will automatically append a NUL terminator byte
-// to the end (not included in the length)
-void *tigrReadFile(const char *fileName, int *length);
-
-// Decompresses DEFLATEd zip/zlib data into a buffer.
-// Returns non-zero on success.
-int tigrInflate(void *out, unsigned outlen, const void *in, unsigned inlen);
-
-// Decodes a single UTF8 codepoint and returns the next pointer.
-const char *tigrDecodeUTF8(const char *text, int *cp);
-
-// Encodes a single UTF8 codepoint and returns the next pointer.
-char *tigrEncodeUTF8(char *text, int cp);
-
-#ifdef __cplusplus
-}
-#endif