diff options
| -rw-r--r-- | flake.nix | 23 | ||||
| -rw-r--r-- | include/RGFW.h | 15955 | ||||
| -rw-r--r-- | main.c3 | 82 | ||||
| -rw-r--r-- | project.json | 5 | ||||
| -rw-r--r-- | shim.c | 12 | ||||
| -rw-r--r-- | tigr.c | 6610 | ||||
| -rw-r--r-- | tigr.h | 366 |
7 files changed, 16030 insertions, 7023 deletions
@@ -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, ®istry_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 + @@ -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" } @@ -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" @@ -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 //////// - - @@ -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 |
