From 34d00d52177230cb3434240c263a0529aa0b4891 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 May 2024 19:46:31 +0200 Subject: [PATCH 01/11] Minor formatting tweaks --- .../resources/shaders/glsl100/palette_switch.fs | 16 +++++++++------- .../resources/shaders/glsl330/palette_switch.fs | 13 ++++++++----- src/external/rl_gputex.h | 4 ++-- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/examples/shaders/resources/shaders/glsl100/palette_switch.fs b/examples/shaders/resources/shaders/glsl100/palette_switch.fs index 25b496321..d8d696d4f 100644 --- a/examples/shaders/resources/shaders/glsl100/palette_switch.fs +++ b/examples/shaders/resources/shaders/glsl100/palette_switch.fs @@ -2,7 +2,7 @@ precision mediump float; -const int colors = 8; +const int MAX_INDEXED_COLORS = 8; // Input vertex attributes (from vertex shader) varying vec2 fragTexCoord; @@ -10,7 +10,8 @@ varying vec4 fragColor; // Input uniform values uniform sampler2D texture0; -uniform ivec3 palette[colors]; +uniform ivec3 palette[MAX_INDEXED_COLORS]; +//uniform sampler2D palette; // Alternative to ivec3, palette provided as a 256x1 texture void main() { @@ -18,13 +19,13 @@ void main() vec4 texelColor = texture2D(texture0, fragTexCoord)*fragColor; // Convert the (normalized) texel color RED component (GB would work, too) - // to the palette index by scaling up from [0, 1] to [0, 255]. + // to the palette index by scaling up from [0..1] to [0..255] int index = int(texelColor.r*255.0); - + ivec3 color = ivec3(0); // NOTE: On GLSL 100 we are not allowed to index a uniform array by a variable value, - // a constantmust be used, so this logic... + // a constant must be used, so this logic... if (index == 0) color = palette[0]; else if (index == 1) color = palette[1]; else if (index == 2) color = palette[2]; @@ -33,9 +34,10 @@ void main() else if (index == 5) color = palette[5]; else if (index == 6) color = palette[6]; else if (index == 7) color = palette[7]; + + //gl_FragColor = texture2D(palette, texelColor.xy); // Alternative to ivec3 // Calculate final fragment color. Note that the palette color components - // are defined in the range [0, 255] and need to be normalized to [0, 1] - // for OpenGL to work. + // are defined in the range [0..255] and need to be normalized to [0..1] gl_FragColor = vec4(float(color.x)/255.0, float(color.y)/255.0, float(color.z)/255.0, texelColor.a); } diff --git a/examples/shaders/resources/shaders/glsl330/palette_switch.fs b/examples/shaders/resources/shaders/glsl330/palette_switch.fs index 7c8a488cd..6a82529b1 100644 --- a/examples/shaders/resources/shaders/glsl330/palette_switch.fs +++ b/examples/shaders/resources/shaders/glsl330/palette_switch.fs @@ -1,6 +1,6 @@ #version 330 -const int colors = 8; +const int MAX_INDEXED_COLORS = 8; // Input fragment attributes (from fragment shader) in vec2 fragTexCoord; @@ -8,7 +8,8 @@ in vec4 fragColor; // Input uniform values uniform sampler2D texture0; -uniform ivec3 palette[colors]; +uniform ivec3 palette[MAX_INDEXED_COLORS]; +//uniform sampler2D palette; // Alternative to ivec3, palette provided as a 256x1 texture // Output fragment color out vec4 finalColor; @@ -16,15 +17,17 @@ out vec4 finalColor; void main() { // Texel color fetching from texture sampler + // NOTE: The texel is actually the a GRAYSCALE index color vec4 texelColor = texture(texture0, fragTexCoord)*fragColor; // Convert the (normalized) texel color RED component (GB would work, too) - // to the palette index by scaling up from [0, 1] to [0, 255]. + // to the palette index by scaling up from [0..1] to [0..255] int index = int(texelColor.r*255.0); ivec3 color = palette[index]; + + //finalColor = texture(palette, texelColor.xy); // Alternative to ivec3 // Calculate final fragment color. Note that the palette color components - // are defined in the range [0, 255] and need to be normalized to [0, 1] - // for OpenGL to work. + // are defined in the range [0..255] and need to be normalized to [0..1] finalColor = vec4(color/255.0, texelColor.a); } diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index 2cbe59636..c57730552 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -229,7 +229,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ } } } - else if (header->ddspf.flags == 0x40 && header->ddspf.rgb_bit_count == 24) // DDS_RGB, no compressed + else if ((header->ddspf.flags == 0x40) && (header->ddspf.rgb_bit_count == 24)) // DDS_RGB, no compressed { int data_size = image_pixel_size*3*sizeof(unsigned char); image_data = RL_MALLOC(data_size); @@ -238,7 +238,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8; } - else if (header->ddspf.flags == 0x41 && header->ddspf.rgb_bit_count == 32) // DDS_RGBA, no compressed + else if ((header->ddspf.flags == 0x41) && (header->ddspf.rgb_bit_count == 32)) // DDS_RGBA, no compressed { int data_size = image_pixel_size*4*sizeof(unsigned char); image_data = RL_MALLOC(data_size); From 23385231c6c5ed6e9ace97e7305d3b7002c62171 Mon Sep 17 00:00:00 2001 From: bohonghuang <1281299809@qq.com> Date: Mon, 6 May 2024 02:19:25 +0800 Subject: [PATCH 02/11] [rlgl] Fix incorrect matrix multiplication order in `rlMultMatrixf` (#3935) --- src/rlgl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rlgl.h b/src/rlgl.h index ae53b4efb..513dd3ed7 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1296,7 +1296,7 @@ void rlMultMatrixf(const float *matf) matf[2], matf[6], matf[10], matf[14], matf[3], matf[7], matf[11], matf[15] }; - *RLGL.State.currentMatrix = rlMatrixMultiply(*RLGL.State.currentMatrix, mat); + *RLGL.State.currentMatrix = rlMatrixMultiply(mat, *RLGL.State.currentMatrix); } // Multiply the current matrix by a perspective matrix generated by parameters From 3d9aafed3b5d183f35a1e8060d0985475101f6db Mon Sep 17 00:00:00 2001 From: Christian Haas Date: Sun, 5 May 2024 20:22:58 +0200 Subject: [PATCH 03/11] [rcore_desktop] Fix 3693 initial window geometry (#3950) * Rework window placement and dimensions for multi-monitor setups; - fullscreen apps use primary monitor, exclusively - non-fullscreen apps come in two variants: a) pre-determined window size by user b) use-active-monitor dimensions by user specifying 0x0 Either way, the window shall be centred at the monitor where it was created This may have been the original intent, yet the primary monitor was used also for the second case, regardless where the window opened. * Clean up code, handle error, fix integer-overflow for centering windowed fullscreen --- src/platforms/rcore_desktop.c | 95 +++++++++++++++++++++++++---------- 1 file changed, 69 insertions(+), 26 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index de289ca0a..f08774165 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -1218,6 +1218,19 @@ void PollInputEvents(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- +static void SetDimensionsFromMonitor(GLFWmonitor *monitor) +{ + const GLFWvidmode *mode = glfwGetVideoMode(monitor); + + // Default display resolution to that of the current mode + CORE.Window.display.width = mode->width; + CORE.Window.display.height = mode->height; + + // Set screen width/height to the display width/height if they are 0 + if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width; + if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height; +} + // Initialize platform: graphics, inputs and more int InitPlatform(void) { @@ -1358,26 +1371,22 @@ int InitPlatform(void) // REF: https://github.com/raysan5/raylib/issues/1554 glfwSetJoystickCallback(NULL); - // Find monitor resolution - GLFWmonitor *monitor = glfwGetPrimaryMonitor(); - if (!monitor) - { - TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor"); - return -1; - } - - const GLFWvidmode *mode = glfwGetVideoMode(monitor); - - CORE.Window.display.width = mode->width; - CORE.Window.display.height = mode->height; - - // Set screen width/height to the display width/height if they are 0 - if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width; - if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height; - + GLFWmonitor *monitor = NULL; if (CORE.Window.fullscreen) { - // Remember center for switchinging from fullscreen to window + // According to glfwCreateWindow(), if the user does not have a choice, fullscreen applications + // should default to the primary monitor. + + monitor = glfwGetPrimaryMonitor(); + if (!monitor) + { + TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor"); + return -1; + } + + SetDimensionsFromMonitor(monitor); + + // Remember center for switching from fullscreen to window if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) { // If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed. @@ -1396,7 +1405,7 @@ int InitPlatform(void) // Obtain recommended CORE.Window.display.width/CORE.Window.display.height from a valid videomode for the monitor int count = 0; - const GLFWvidmode *modes = glfwGetVideoModes(glfwGetPrimaryMonitor(), &count); + const GLFWvidmode *modes = glfwGetVideoModes(monitor, &count); // Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height for (int i = 0; i < count; i++) @@ -1426,21 +1435,55 @@ int InitPlatform(void) // HighDPI monitors are properly considered in a following similar function: SetupViewport() SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); - platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL); + platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", monitor, NULL); // NOTE: Full-screen change, not working properly... //glfwSetWindowMonitor(platform.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } else { - // If we are windowed fullscreen, ensures that window does not minimize when focus is lost - if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) + // No-fullscreen window creation + bool wantWindowedFullscreen = (CORE.Window.screen.height == 0) && (CORE.Window.screen.width == 0); + + // If we are windowed fullscreen, ensures that window does not minimize when focus is lost. + // This hinting code will not work if the user already specified the correct monitor dimensions; + // at this point we don't know the monitor's dimensions. (Though, how did the user then?) + if (wantWindowedFullscreen) { glfwWindowHint(GLFW_AUTO_ICONIFY, 0); } - // No-fullscreen window creation - platform.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); + // Default to at least one pixel in size, as creation with a zero dimension is not allowed. + int creationWidth = CORE.Window.screen.width != 0 ? CORE.Window.screen.width : 1; + int creationHeight = CORE.Window.screen.height != 0 ? CORE.Window.screen.height : 1; + + platform.handle = glfwCreateWindow(creationWidth, creationHeight, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); + + // After the window was created, determine the monitor that the window manager assigned. + // Derive display sizes, and, if possible, window size in case it was zero at beginning. + + int monitorCount = 0; + int monitorIndex = GetCurrentMonitor(); + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if (monitorIndex < monitorCount) + { + monitor = monitors[monitorIndex]; + SetDimensionsFromMonitor(monitor); + + TRACELOG(LOG_INFO, "wantWindowed: %d, size: %dx%d", wantWindowedFullscreen, CORE.Window.screen.width, CORE.Window.screen.height); + if (wantWindowedFullscreen) + { + glfwSetWindowSize(platform.handle, CORE.Window.screen.width, CORE.Window.screen.height); + } + } + else + { + // The monitor for the window-manager-created window can not be determined, so it can not be centered. + glfwTerminate(); + TRACELOG(LOG_WARNING, "GLFW: Failed to determine Monitor to center Window"); + return -1; + } if (platform.handle) { @@ -1524,8 +1567,8 @@ int InitPlatform(void) int monitorHeight = 0; glfwGetMonitorWorkarea(monitor, &monitorX, &monitorY, &monitorWidth, &monitorHeight); - int posX = monitorX + (monitorWidth - CORE.Window.screen.width)/2; - int posY = monitorY + (monitorHeight - CORE.Window.screen.height)/2; + int posX = monitorX + (monitorWidth - (int)CORE.Window.screen.width)/2; + int posY = monitorY + (monitorHeight - (int)CORE.Window.screen.height)/2; if (posX < monitorX) posX = monitorX; if (posY < monitorY) posY = monitorY; SetWindowPosition(posX, posY); From f69ae583040060ece2e6e4f813e1408c8fd04900 Mon Sep 17 00:00:00 2001 From: MrScautHD <65916181+MrScautHD@users.noreply.github.com> Date: Mon, 6 May 2024 12:50:05 +0200 Subject: [PATCH 04/11] Update BINDINGS.md (#3963) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index 178fd987c..93ae18792 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -82,6 +82,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | Raylib.lean | 4.5 | [Lean4](https://lean-lang.org/) | BSD-3-Clause | https://github.com/KislyjKisel/Raylib.lean | | Raylib-CSharp-Vinculum | 5.0 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/ZeroElectric/Raylib-CSharp-Vinculum | | raylib-cobol | **auto** | [COBOL](https://gnucobol.sourceforge.io) | Public domain | https://codeberg.org/glowiak/raylib-cobol | +| Raylib-CSharp | 5.0 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MIT | https://github.com/MrScautHD/Raylib-CSharp | ### Utility Wrapers These are utility wrappers for specific languages, they are not required to use raylib in the language but may adapt the raylib API to be more inline with the language's pardigm. From fa2b1c8f094db18466079fcd2f8e488bf89095c2 Mon Sep 17 00:00:00 2001 From: gabriel-marques Date: Tue, 7 May 2024 10:33:56 +0200 Subject: [PATCH 05/11] Implementing GetMonitorWidth/Height for DRM (#3956) * Implementing GetMonitorWidth/Height and GetMonitorPhysicalWidth/Height for drm Added implementation for DRM for functions : - GetMonitorWidth() - GetMonitorHeight() - GetMonitorPhysicalWidth() - GetMonitorPhysicalHeight() - GetMonnitorName() These functions take an argument but only the value 0 is accepted. This is because the DRM platform implementation manages only one screen for now * Refactor "GetMonitor" properties for DRM Platform Refactored GetMonitorHeight, GetMonitorWidth, GetMonitorPhysicalHeight, GetMonitorPhysicalWidth and GetMonitorName to accept only argument "0" as more than one screen is not supported in DRM platform. --- src/platforms/rcore_drm.c | 70 +++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 10 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index a07ba27f1..ba6071460 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -388,29 +388,69 @@ Vector2 GetMonitorPosition(int monitor) // Get selected monitor width (currently used by monitor) int GetMonitorWidth(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on target platform"); - return 0; + int width = 0; + + if (monitor != 0) + { + TRACELOG(LOG_WARNING, "GetMonitorWidth() implemented for first monitor only"); + } + else if ((platform.connector) && (platform.modeIndex >= 0)) + { + width = platform.connector->modes[platform.modeIndex].hdisplay; + } + + return width; } // Get selected monitor height (currently used by monitor) int GetMonitorHeight(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on target platform"); - return 0; + int height = 0; + + if (monitor != 0) + { + TRACELOG(LOG_WARNING, "GetMonitorHeight() implemented for first monitor only"); + } + else if ((platform.connector) && (platform.modeIndex >= 0)) + { + height = platform.connector->modes[platform.modeIndex].vdisplay; + } + + return height; } // Get selected monitor physical width in millimetres int GetMonitorPhysicalWidth(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform"); - return 0; + int physicalWidth = 0; + + if (monitor != 0) + { + TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() implemented for first monitor only"); + } + else if ((platform.connector) && (platform.modeIndex >= 0)) + { + physicalWidth = platform.connector->mmWidth; + } + + return physicalWidth; } // Get selected monitor physical height in millimetres int GetMonitorPhysicalHeight(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform"); - return 0; + int physicalHeight = 0; + + if (monitor != 0) + { + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() implemented for first monitor only"); + } + else if ((platform.connector) && (platform.modeIndex >= 0)) + { + physicalHeight = platform.connector->mmHeight; + } + + return physicalHeight; } // Get selected monitor refresh rate @@ -429,8 +469,18 @@ int GetMonitorRefreshRate(int monitor) // Get the human-readable, UTF-8 encoded name of the selected monitor const char *GetMonitorName(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform"); - return ""; + const char *name = ""; + + if (monitor != 0) + { + TRACELOG(LOG_WARNING, "GetMonitorName() implemented for first monitor only"); + } + else if ((platform.connector) && (platform.modeIndex >= 0)) + { + name = platform.connector->modes[platform.modeIndex].name; + } + + return name; } // Get window position XY on monitor From 9805fe0b1afe8a25a5c7be92468a58bc2fbf8e4a Mon Sep 17 00:00:00 2001 From: Colleague Riley <50218871+ColleagueRiley@users.noreply.github.com> Date: Tue, 7 May 2024 14:30:52 -0400 Subject: [PATCH 06/11] make RGFW a custom platform (#3941) * fix minor bugs and errors (x86) for the RGFW platform, update RGFW platform license, update makefile info for RGFW platform * revert * (RGFW) (winapi) fix mouse hide bug when cursor changes * ensure PLATFORM_SHELL is defined (PLATFORM_RGFW) --------- Co-authored-by: ColleagueRiley --- examples/Makefile | 34 +- src/Makefile | 45 +- src/external/RGFW.h | 6349 ++++++++++++++++++++++++++++ src/platforms/rcore_desktop_rgfw.c | 1519 +++++++ src/rcore.c | 8 +- 5 files changed, 7948 insertions(+), 7 deletions(-) create mode 100644 src/external/RGFW.h create mode 100644 src/platforms/rcore_desktop_rgfw.c diff --git a/examples/Makefile b/examples/Makefile index 9ce05da54..52945c4b4 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -13,6 +13,11 @@ # - Windows (Win32, Win64) # - Linux (X11/Wayland desktop mode) # - Others (not tested) +# > PLATFORM_DESKTOP_RGFW (RGFW backend): +# - Windows (Win32, Win64) +# - Linux (X11 desktop mode) +# - macOS/OSX (x64, arm64 (not tested)) +# - Others (not tested) # > PLATFORM_WEB: # - HTML5 (WebAssembly) # > PLATFORM_DRM: @@ -45,7 +50,7 @@ # Define required environment variables #------------------------------------------------------------------------------------------------ # Define target platform: PLATFORM_DESKTOP, PLATFORM_DESKTOP_SDL, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB -PLATFORM ?= PLATFORM_DESKTOP +PLATFORM ?= PLATFORM_DESKTOP_RGFW # Define required raylib variables PROJECT_NAME ?= raylib_examples @@ -86,7 +91,7 @@ BUILD_WEB_RESOURCES ?= TRUE BUILD_WEB_RESOURCES_PATH ?= $(dir $<)resources@resources # Determine PLATFORM_OS when required -ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL PLATFORM_WEB)) +ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL PLATFORM_WEB PLATFORM_DESKTOP_RGFW)) # No uname.exe on MinGW!, but OS=Windows_NT on Windows! # ifeq ($(UNAME),Msys) -> Windows ifeq ($(OS),Windows_NT) @@ -416,6 +421,31 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) LDLIBS += -latomic endif endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_RGFW) + ifeq ($(PLATFORM_OS),WINDOWS) + # Libraries for Windows desktop compilation + LDLIBS = ..\src\libraylib.a -lgdi32 -lwinmm -lopengl32 + endif + ifeq ($(PLATFORM_OS),LINUX) + # Libraries for Debian GNU/Linux desktop compipling + # NOTE: Required packages: libegl1-mesa-dev + LDLIBS = ../src/libraylib.a -lGL -lX11 -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor -lm -lpthread -ldl -lrt + + # Explicit link to libc + ifeq ($(RAYLIB_LIBTYPE),SHARED) + LDLIBS += -lc + endif + + # NOTE: On ARM 32bit arch, miniaudio requires atomics library + LDLIBS += -latomic + endif + ifeq ($(PLATFORM_OS),OSX) + # Libraries for Debian GNU/Linux desktop compiling + # NOTE: Required packages: libegl1-mesa-dev + LDLIBS = ../src/libraylib.a -lm + LDLIBS += -framework Foundation -framework AppKit -framework OpenGL -framework CoreVideo + endif +endif ifeq ($(PLATFORM),PLATFORM_DRM) # Libraries for DRM compiling # NOTE: Required packages: libasound2-dev (ALSA) diff --git a/src/Makefile b/src/Makefile index 7f5dd335d..89d81627a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -13,6 +13,11 @@ # - Windows (Win32, Win64) # - Linux (X11/Wayland desktop mode) # - Others (not tested) +# > PLATFORM_DESKTOP_RGFW (RGFW backend): +# - Windows (Win32, Win64) +# - Linux (X11 desktop mode) +# - macOS/OSX (x64, arm64 (not tested)) +# - Others (not tested) # > PLATFORM_WEB: # - HTML5 (WebAssembly) # > PLATFORM_DRM: @@ -114,7 +119,7 @@ HOST_PLATFORM_OS ?= WINDOWS PLATFORM_OS ?= WINDOWS # Determine PLATFORM_OS when required -ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL PLATFORM_WEB PLATFORM_ANDROID)) +ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL PLATFORM_WEB PLATFORM_ANDROID PLATFORM_DESKTOP_RGFW)) # No uname.exe on MinGW!, but OS=Windows_NT on Windows! # ifeq ($(UNAME),Msys) -> Windows ifeq ($(OS),Windows_NT) @@ -224,6 +229,14 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_RGFW) + # By default use OpenGL 3.3 on desktop platforms + GRAPHICS ?= GRAPHICS_API_OPENGL_33 + #GRAPHICS = GRAPHICS_API_OPENGL_11 # Uncomment to use OpenGL 1.1 + #GRAPHICS = GRAPHICS_API_OPENGL_21 # Uncomment to use OpenGL 2.1 + #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 + #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) +endif ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) # By default use OpenGL 3.3 on desktop platform with SDL backend GRAPHICS ?= GRAPHICS_API_OPENGL_33 @@ -576,6 +589,31 @@ endif ifeq ($(PLATFORM),PLATFORM_ANDROID) LDLIBS = -llog -landroid -lEGL -lGLESv2 -lOpenSLES -lc -lm endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_RGFW) + ifeq ($(PLATFORM_OS),WINDOWS) + # Libraries for Windows desktop compilation + LDLIBS = ..\src\libraylib.a -lgdi32 -lwinmm -lopengl32 + endif + ifeq ($(PLATFORM_OS),LINUX) + # Libraries for Debian GNU/Linux desktop compipling + # NOTE: Required packages: libegl1-mesa-dev + LDLIBS = ../src/libraylib.a -lGL -lX11 -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor -lm -lpthread -ldl -lrt + + # Explicit link to libc + ifeq ($(RAYLIB_LIBTYPE),SHARED) + LDLIBS += -lc + endif + + # NOTE: On ARM 32bit arch, miniaudio requires atomics library + LDLIBS += -latomic + endif + ifeq ($(PLATFORM_OS),OSX) + # Libraries for Debian GNU/Linux desktop compiling + # NOTE: Required packages: libegl1-mesa-dev + LDLIBS = ../src/libraylib.a -lm + LDLIBS += -framework Foundation -framework AppKit -framework OpenGL -framework CoreVideo + endif +endif # Define source code object files required #------------------------------------------------------------------------------------------------ @@ -583,7 +621,8 @@ OBJS = rcore.o \ rshapes.o \ rtextures.o \ rtext.o \ - utils.o + utils.o + ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(USE_EXTERNAL_GLFW),FALSE) @@ -619,7 +658,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) @echo "raylib library generated (lib$(RAYLIB_LIB_NAME).a)!" else ifeq ($(RAYLIB_LIBTYPE),SHARED) - ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL)) + ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL PLATFORM_DESKTOP_RGFW)) ifeq ($(PLATFORM_OS),WINDOWS) # NOTE: Linking with provided resource file $(CC) -shared -o $(RAYLIB_RELEASE_PATH)/$(RAYLIB_LIB_NAME).dll $(OBJS) $(RAYLIB_RES_FILE) $(LDFLAGS) $(LDLIBS) diff --git a/src/external/RGFW.h b/src/external/RGFW.h new file mode 100644 index 000000000..13b68d98a --- /dev/null +++ b/src/external/RGFW.h @@ -0,0 +1,6349 @@ +/* +* Copyright (C) 2023-24 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_PRINT_ERRORS - (optional) makes it so RGFW prints errors when they're found + #define RGFW_OSMESA - (optional) use OSmesa as backend (instead of system's opengl api + regular opengl) + #define RGFW_BUFFER - (optional) just draw directly to (RGFW) window pixel buffer that is drawn to screen (the buffer is in the RGBA format) + #define RGFW_EGL - (optional) use EGL for loading an OpenGL context (instead of the system's opengl api) + #define RGFW_OPENGL_ES1 - (optional) use EGL to load and use Opengl ES (version 1) for backend rendering (instead of the system's opengl api) + #define RGFW_OPENGL_ES2 - (optional) use OpenGL ES (version 2) + #define RGFW_VULKAN - (optional) use vulkan for the rendering backend (rather than opengl) + #define RGFW_DIRECTX - (optional) use directX for the rendering backend (rather than opengl) (windows only, defaults to opengl for unix) + #define RGFW_NO_API - (optional) don't use any rendering API (no opengl, no vulkan, no directX) + + #define RGFW_LINK_EGL (optional) (windows only) if EGL is being used, if EGL functions should be defined dymanically (using GetProcAddress) + #define RGFW_LINK_OSMESA (optional) (windows only) if EGL is being used, if OS Mesa functions should be defined dymanically (using GetProcAddress) + + #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_WGL_LOAD (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_DPI - Do not include calculate DPI (no XRM nor libShcore included) + + #define RGFW_ALLOC_DROPFILES (optional) if room should be allocating for drop files (by default it's global data) + #define RGFW_MALLOC x - choose what function to use to allocate, by default the standard malloc is used + #define RGFW_CALLOC x - choose what function to use to allocate (calloc), by default the standard calloc is used + #define RGFW_FREE x - choose what function to use to allocated memory, by default the standard free is used +*/ + +/* + Credits : + EimaMei/Sacode : Much of the code for creating windows using winapi, Wrote the Silicon library, helped with MacOS Support + + stb - This project is heavily inspired by the stb single header files + + GLFW: + certain parts of winapi and X11 are very poorly documented, + GLFW's source code was referenced and used throughout the project (used code is marked in some way), + this mainly includes, code for drag and drops, code for setting the icon to a bitmap and the code for managing the clipboard for X11 (as these parts are not documented very well) + + GLFW Copyright, https::/github.com/GLFW/GLFW + + Copyright (c) 2002-2006 Marcus Geelnard + Copyright (c) 2006-2019 Camilla Löwy +*/ + +#ifndef RGFW_MALLOC +#include +#include +#define RGFW_MALLOC malloc +#define RGFW_CALLOC calloc +#define RGFW_FREE free +#endif + +#if !_MSC_VER +#ifndef inline +#ifndef __APPLE__ +#define inline __inline +#endif +#endif +#endif + +#ifndef RGFWDEF +#ifdef __APPLE__ +#define RGFWDEF extern inline +#else +#define RGFWDEF inline +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + /* makes sure the header file part is only defined once by default */ +#ifndef RGFW_HEADER + +#define RGFW_HEADER + +#if !defined(u8) +#include + + 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 + +#if defined(RGFW_X11) && defined(__APPLE__) +#define RGFW_MACOS_X11 +#undef __APPLE__ +#endif + +#if defined(_WIN32) && !defined(RGFW_X11) /* (if you're using X11 on windows some how) */ + + /* this name looks better */ + /* plus it helps with cross-compiling because RGFW_X11 won't be accidently defined */ + +#define RGFW_WINDOWS + +#if defined(_WIN32) +#define WIN32 +#endif + +#if defined(_WIN64) +#define WIN64 +#define _AMD64_ +#undef _X86_ +#else +#undef _AMD64_ +#define _X86_ +#endif + +#include + +#ifdef __MINGW32__ +#include +#else +#include +#endif + +#else +#if defined(__unix__) || defined(RGFW_MACOS_X11) || defined(RGFW_X11) +#define RGFW_MACOS_X11 +#define RGFW_X11 +#include +#endif +#endif + +#if defined(__APPLE__) && !defined(RGFW_MACOS_X11) && !defined(RGFW_X11) +#define RGFW_MACOS +#endif + +#if (defined(RGFW_OPENGL_ES1) || defined(RGFW_OPENGL_ES2)) && !defined(RGFW_EGL) +#define RGFW_EGL +#endif +#if defined(RGFW_EGL) && defined(__APPLE__) + #warning EGL is not supported for Cocoa, switching back to the native opengl api +#undef RGFW_EGL +#endif + +#if !defined(RGFW_OSMESA) && !defined(RGFW_EGL) && !defined(RGFW_OPENGL) && !defined (RGFW_VULKAN) && !defined(RGFW_DIRECTX) && !defined(RGFW_BUFFER) && !defined(RGFW_NO_API) +#define RGFW_OPENGL +#endif + +#ifdef RGFW_VULKAN +#ifndef RGFW_MAX_FRAMES_IN_FLIGHT +#define RGFW_MAX_FRAMES_IN_FLIGHT 2 +#endif + +#ifdef RGFW_X11 +#define VK_USE_PLATFORM_XLIB_KHR +#endif +#ifdef RGFW_WINDOWS +#define VK_USE_PLATFORM_WIN32_KHR +#endif +#ifdef RGFW_MACOS +#define VK_USE_PLATFORM_MACOS_MVK +#endif + +#include +#endif + +#if defined(RGFW_X11) && defined(RGFW_OPENGL) +#ifndef GLX_MESA_swap_control +#define GLX_MESA_swap_control +#endif +#include /* GLX defs, xlib.h, gl.h */ +#endif + +#ifdef RGFW_EGL +#include +#endif + +#ifdef RGFW_OSMESA +#ifndef __APPLE__ +#include +#else +#include +#endif +#endif + +#if defined(RGFW_DIRECTX) && defined(RGFW_WINDOWS) +#include +#include +#include +#include + +#ifndef __cplusplus +#define __uuidof(T) IID_##T +#endif +#endif + + /*! Optional arguments for making a windows */ +#define RGFW_TRANSPARENT_WINDOW (1L<<9) /*!< the window is transparent */ +#define RGFW_NO_BORDER (1L<<3) /*!< the window doesn't have border */ +#define RGFW_NO_RESIZE (1L<<4) /*!< the window cannot be resized by the user */ +#define RGFW_ALLOW_DND (1L<<5) /*!< the window supports drag and drop*/ +#define RGFW_HIDE_MOUSE (1L<<6) /*! the window should hide the mouse or not (can be toggled later on) using `RGFW_window_mouseShow*/ +#define RGFW_FULLSCREEN (1L<<8) /* the window is fullscreen by default or not */ +#define RGFW_CENTER (1L<<10) /*! center the window on the screen */ +#define RGFW_OPENGL_SOFTWARE (1L<<11) /*! use OpenGL software rendering */ +#define RGFW_COCOA_MOVE_TO_RESOURCE_DIR (1L << 12) /* (cocoa only), move to resource folder */ +#define RGFW_SCALE_TO_MONITOR (1L << 13) /* scale the window to the screen */ + +#define RGFW_NO_GPU_RENDER (1L<<14) /* don't render (using the GPU based API)*/ +#define RGFW_NO_CPU_RENDER (1L<<15) /* don't render (using the CPU based buffer rendering)*/ + + +/*! event codes */ +#define RGFW_keyPressed 2 /* a key has been pressed */ +#define RGFW_keyReleased 3 /*!< a key has been released*/ +/*! key event note + the code of the key pressed is stored in + RGFW_Event.keyCode + !!Keycodes defined at the bottom of the header file!! + + while a string version is stored in + RGFW_Event.KeyString + + RGFW_Event.lockState holds the current lockState + this means if CapsLock, NumLock are active or not +*/ +#define RGFW_mouseButtonPressed 4 /*!< a mouse button has been pressed (left,middle,right)*/ +#define RGFW_mouseButtonReleased 5 /*!< a mouse button has been released (left,middle,right)*/ +#define RGFW_mousePosChanged 6 /*!< the position of the mouse has been changed*/ +/*! mouse event note + the x and y of the mouse can be found in the vector, RGFW_Event.point + + RGFW_Event.button holds which mouse button was pressed +*/ +#define RGFW_jsButtonPressed 7 /*!< a joystick button was pressed */ +#define RGFW_jsButtonReleased 8 /*!< a joystick button was released */ +#define RGFW_jsAxisMove 9 /*!< an axis of a joystick was moved*/ +/*! joystick event note + RGFW_Event.joystick holds which joystick was altered, if any + RGFW_Event.button holds which joystick button was pressed + + RGFW_Event.axis holds the data of all the axis + RGFW_Event.axisCount says how many axis there are +*/ +#define RGFW_windowAttribsChange 10 /*!< the window was moved or resized (by the user) */ +/* attribs change event note + The event data is sent straight to the window structure + with win->r.x, win->r.y, win->r.w and win->r.h +*/ +#define RGFW_quit 33 /*!< the user clicked the quit button*/ +#define RGFW_dnd 34 /*!< a file has been dropped into the window*/ +#define RGFW_dnd_init 35 /*!< the start of a dnd event, when the place where the file drop is known */ +/* dnd data note + The x and y coords of the drop are stored in the vector RGFW_Event.point + + RGFW_Event.droppedFilesCount holds how many files were dropped + + This is also the size of the array which stores all the dropped file string, + RGFW_Event.droppedFiles +*/ + +/*! mouse button codes (RGFW_Event.button) */ +#define RGFW_mouseLeft 1 /*!< left mouse button is pressed*/ +#define RGFW_mouseMiddle 2 /*!< mouse-wheel-button is pressed*/ +#define RGFW_mouseRight 3 /*!< right mouse button is pressed*/ +#define RGFW_mouseScrollUp 4 /*!< mouse wheel is scrolling up*/ +#define RGFW_mouseScrollDown 5 /*!< mouse wheel is scrolling down*/ + +#ifndef RGFW_MAX_PATH +#define RGFW_MAX_PATH 260 /* max length of a path (for dnd) */ +#endif +#ifndef RGFW_MAX_DROPS +#define RGFW_MAX_DROPS 260 /* max items you can drop at once */ +#endif + + +/* for RGFW_Event.lockstate */ +#define RGFW_CAPSLOCK (1L << 1) +#define RGFW_NUMLOCK (1L << 2) + +/*! joystick button codes (based on xbox/playstation), you may need to change these values per controller */ +#ifndef RGFW_JS_A + +#define RGFW_JS_A 0 /* or PS X button */ +#define RGFW_JS_B 1 /* or PS circle button */ +#define RGFW_JS_Y 2 /* or PS triangle button */ +#define RGFW_JS_X 3 /* or PS square button */ +#define RGFW_JS_START 9 /* start button */ +#define RGFW_JS_SELECT 8 /* select button */ +#define RGFW_JS_HOME 10 /* home button */ +#define RGFW_JS_UP 13 /* dpad up */ +#define RGFW_JS_DOWN 14 /* dpad down*/ +#define RGFW_JS_LEFT 15 /* dpad left */ +#define RGFW_JS_RIGHT 16 /* dpad right */ +#define RGFW_JS_L1 4 /* left bump */ +#define RGFW_JS_L2 5 /* left trigger*/ +#define RGFW_JS_R1 6 /* right bumper */ +#define RGFW_JS_R2 7 /* right trigger */ + +#endif + +/* basic vector type, if there's not already a point/vector type of choice */ +#ifndef RGFW_vector +typedef struct { i32 x, y; } RGFW_vector; +#endif + + /* basic rect type, if there's not already a rect type of choice */ +#ifndef RGFW_rect + typedef struct { i32 x, y, w, h; } RGFW_rect; +#endif + + /* basic area type, if there's not already a area type of choice */ +#ifndef RGFW_area + typedef struct { u32 w, h; } RGFW_area; +#endif + +#define RGFW_VECTOR(x, y) (RGFW_vector){x, y} +#define RGFW_RECT(x, y, w, h) (RGFW_rect){x, y, w, h} +#define RGFW_AREA(w, h) (RGFW_area){w, h} + + typedef struct RGFW_monitor { + char name[128]; /* monitor name */ + RGFW_rect rect; /* monitor Workarea */ + float scaleX, scaleY; /* monitor content scale*/ + float physW, physH; /* monitor physical size */ + } RGFW_monitor; + + /* + NOTE : Monitor functions should be ran only as many times as needed (not in a loop) + */ + + /* get an array of all the monitors (max 6) */ + RGFWDEF RGFW_monitor* RGFW_getMonitors(void); + /* get the primary monitor */ + RGFWDEF RGFW_monitor RGFW_getPrimaryMonitor(void); + + /* NOTE: some parts of the data can represent different things based on the event (read comments in RGFW_Event struct) */ + typedef struct RGFW_Event { +#ifdef RGFW_WINDOWS + char keyName[16]; /* key name of event*/ +#else + char* keyName; /*!< key name of event */ +#endif + + /*! drag and drop data */ + /* 260 max paths with a max length of 260 */ +#ifdef RGFW_ALLOC_DROPFILES + char** droppedFiles; +#else + char droppedFiles[RGFW_MAX_DROPS][RGFW_MAX_PATH]; /*!< dropped files*/ +#endif + u32 droppedFilesCount; /*!< house many files were dropped */ + + u32 type; /*!< which event has been sent?*/ + RGFW_vector point; /*!< mouse x, y of event (or drop point) */ + u32 keyCode; /*!< keycode of event !!Keycodes defined at the bottom of the header file!! */ + + u32 inFocus; /*if the window is in focus or not*/ + + u32 fps; /*the current fps of the window [the fps is checked when events are checked]*/ + u32 current_ticks, frames; /* this is used for counting the fps */ + + u8 lockState; + + u16 joystick; /* which joystick this event applies to (if applicable to any) */ + + u8 button; /*!< which mouse button has been clicked (0) left (1) middle (2) right OR which joystick button was pressed*/ + double scroll; /* the raw mouse scroll value */ + + u8 axisesCount; /* number of axises */ + RGFW_vector axis[2]; /* x, y of axises (-100 to 100) */ + } RGFW_Event; /*!< Event structure for checking/getting events */ + + /* source data for the window (used by the APIs) */ + typedef struct RGFW_window_src { +#ifdef RGFW_WINDOWS + HWND window; /*!< source window */ + HDC hdc; /*!< source HDC */ + u32 hOffset; /*!< height offset for window */ +#endif +#ifdef RGFW_X11 + Display* display; /*!< source display */ + Window window; /*!< source window */ + Cursor cursor; +#endif +#ifdef RGFW_MACOS + u32 display; + void* displayLink; + void* window; +#endif + +#if defined(RGFW_OPENGL) && !defined(RGFW_OSMESA) +#ifdef RGFW_MACOS + void* rSurf; /*!< source graphics context */ +#endif +#ifdef RGFW_WINDOWS + HGLRC rSurf; /*!< source graphics context */ +#endif +#ifdef RGFW_X11 + GLXContext rSurf; /*!< source graphics context */ +#endif +#else +#ifdef RGFW_VULKAN + VkSurfaceKHR rSurf; /*!< source graphics context */ + + /* vulkan data */ + VkSwapchainKHR swapchain; + u32 image_count; + VkImage* swapchain_images; + VkImageView* swapchain_image_views; +#endif + +#ifdef RGFW_OSMESA + OSMesaContext rSurf; +#endif +#endif + +#ifdef RGFW_WINDOWS + RGFW_area maxSize, minSize; +#if defined(RGFW_DIRECTX) + IDXGISwapChain* swapchain; + ID3D11RenderTargetView* renderTargetView; + ID3D11DepthStencilView* pDepthStencilView; +#endif +#endif + +#if defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11) + void* view; /*apple viewpoint thingy*/ +#endif + +#ifdef RGFW_EGL + EGLSurface EGL_surface; + EGLDisplay EGL_display; +#endif + +#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) +#ifdef RGFW_WINDOWS + HBITMAP bitmap; +#endif +#ifdef RGFW_X11 + XImage* bitmap; +#endif +#ifdef RGFW_MACOS + void* bitmap; /* API's bitmap for storing or managing */ + void* image; +#endif +#if defined(RGFW_BUFFER) && defined(RGFW_WINDOWS) + HDC hdcMem; /* window stored in memory that winapi needs to render buffers */ +#endif +#endif + + u8 jsPressed[4][16]; /* if a key is currently pressed or not (per joystick) */ + + i32 joysticks[4]; /* limit of 4 joysticks at a time */ + u16 joystickCount; /* the actual amount of joysticks */ + + RGFW_area scale; /* window scaling */ + +#ifdef RGFW_MACOS + u8 cursorChanged; /* for steve jobs */ +#endif + + u32 winArgs; /* windows args (for RGFW to check) */ + /* + !< if dnd is enabled or on (based on window creating args) + cursorChanged + */ + } RGFW_window_src; + + typedef struct RGFW_window { + RGFW_window_src src; + +#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + u8* buffer; /* buffer for non-GPU systems (OSMesa, basic software rendering) */ + /* when rendering using RGFW_BUFFER, the buffer is in the RGBA format */ +#endif + + RGFW_Event event; /*!< current event */ + + RGFW_rect r; /* the x, y, w and h of the struct */ + + u8 fpsCap; /*!< the fps cap of the window should run at (change this var to change the fps cap, 0 = no limit)*/ + /*[the fps is capped when events are checked]*/ + } RGFW_window; /*!< Window structure for managing the window */ + +#if defined(RGFW_X11) || defined(RGFW_MACOS) + typedef u64 RGFW_thread; /* thread type unix */ +#else + typedef void* RGFW_thread; /* thread type for window */ +#endif + + RGFW_window* RGFW_createWindow( + const char* name, /* name of the window */ + RGFW_rect rect, /* rect of window */ + u16 args /* extra arguments (NULL / (u16)0 means no args used)*/ + ); /*!< function to create a window struct */ + + /* get the size of the screen to an area struct */ + RGFWDEF RGFW_area RGFW_getScreenSize(void); + + /* + this function checks an *individual* event (and updates window structure attributes) + this means, using this function without a while loop may cause event lag + + ex. + + while (RGFW_window_checkEvent(win) != NULL) [this keeps checking events until it reaches the last one] + */ + + RGFW_Event* RGFW_window_checkEvent(RGFW_window* win); /*!< check events (returns a pointer to win->event or NULL if there is no event)*/ + + /*! window managment functions*/ + RGFWDEF void RGFW_window_close(RGFW_window* win); /*!< close the window and free leftover data */ + + RGFWDEF void RGFW_window_move(RGFW_window* win, + RGFW_vector v/* new pos*/ + ); + + /* move to a specific monitor */ + RGFWDEF void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m); + + RGFWDEF void RGFW_window_resize(RGFW_window* win, + RGFW_area a/* new size*/ + ); + + /* set the minimum size a user can shrink a window */ + RGFWDEF void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a); + /* set the minimum size a user can extend a window */ + RGFWDEF void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a); + + RGFWDEF void RGFW_window_maximize(RGFW_window* win); /* maximize the window size */ + RGFWDEF void RGFW_window_minimize(RGFW_window* win); /* minimize the window (in taskbar (per OS))*/ + RGFWDEF void RGFW_window_restore(RGFW_window* win); /* restore the window from minimized (per OS)*/ + + RGFWDEF void RGFW_window_setName(RGFW_window* win, + char* name + ); + + void RGFW_window_setIcon(RGFW_window* win, /*!< source window */ + u8* icon /*!< icon bitmap */, + RGFW_area a /*!< width and height of the bitmap*/, + i32 channels /*!< how many channels the bitmap has (rgb : 3, rgba : 4) */ + ); /*!< image resized by default */ + + /*!< sets mouse to bitmap (very simular to RGFW_window_setIcon), image NOT resized by default*/ + RGFWDEF void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels); + + /*!< sets the mouse to a standard API cursor (based on RGFW_MOUSE, as seen at the end of the file) */ +#ifdef RGFW_MACOS + RGFWDEF void RGFW_window_setMouseStandard(RGFW_window* win, void* mouse); +#else + RGFWDEF void RGFW_window_setMouseStandard(RGFW_window* win, i32 mouse); +#endif + + RGFWDEF void RGFW_window_setMouseDefault(RGFW_window* win); /* sets the mouse to1` the default mouse image */ + /* + holds the mouse in place by moving the mouse back each time it moves + you can still use win->event.point to see how much it moved before it was put back in place + + this is useful for a 3D camera + */ + RGFWDEF void RGFW_window_mouseHold(RGFW_window* win); + /* undo hold */ + RGFWDEF void RGFW_window_mouseUnhold(RGFW_window* win); + + /* hide the window */ + RGFWDEF void RGFW_window_hide(RGFW_window* win); + /* show the window */ + RGFWDEF void RGFW_window_show(RGFW_window* win); + + /* + makes it so `RGFW_window_shouldClose` returns true + by setting the window event.type to RGFW_quit + */ + RGFWDEF void RGFW_window_setShouldClose(RGFW_window* win); + + /* where the mouse is on the screen */ + RGFWDEF RGFW_vector RGFW_getGlobalMousePoint(void); + + /* show the mouse or hide the mouse*/ + RGFWDEF void RGFW_window_showMouse(RGFW_window* win, i8 show); + /* move the mouse to a set x, y pos*/ + RGFWDEF void RGFW_window_moveMouse(RGFW_window* win, RGFW_vector v); + + /* if the window should close (RGFW_close was sent or escape was pressed) */ + RGFWDEF u8 RGFW_window_shouldClose(RGFW_window* win); + /* if window is fullscreen'd */ + RGFWDEF u8 RGFW_window_isFullscreen(RGFW_window* win); + /* if window is hidden */ + RGFWDEF u8 RGFW_window_isHidden(RGFW_window* win); + /* if window is minimized */ + RGFWDEF u8 RGFW_window_isMinimized(RGFW_window* win); + /* if window is maximized */ + RGFWDEF u8 RGFW_window_isMaximized(RGFW_window* win); + + /* + scale the window to the monitor, + this is run by default if the user uses the arg `RGFW_SCALE_TO_MONITOR` during window creation + */ + RGFWDEF void RGFW_window_scaleToMonitor(RGFW_window* win); + + /* get the struct of the window's monitor */ + RGFWDEF RGFW_monitor RGFW_window_getMonitor(RGFW_window* win); + + /*!< make the window the current opengl drawing context */ + RGFWDEF void RGFW_window_makeCurrent(RGFW_window* win); + + /*error handling*/ + RGFWDEF u8 RGFW_Error(); /* returns true if an error has occurred (doesn't print errors itself) */ + + /*!< if window == NULL, it checks if the key is pressed globally. Otherwise, it checks only if the key is pressed while the window in focus.*/ + RGFWDEF u8 RGFW_isPressedI(RGFW_window* win, u32 key); /*!< if key is pressed (key code)*/ + + /* + !!Keycodes defined at the bottom of the header file!! + */ + /*!< converts a key code to it's key string */ + RGFWDEF char* RGFW_keyCodeTokeyStr(u64 key); + /*!< converts a string of a key to it's key code */ + RGFWDEF u32 RGFW_keyStrToKeyCode(char* key); + /*!< if key is pressed (key string) */ +#define RGFW_isPressedS(win, key) RGFW_isPressedI(win, RGFW_keyStrToKeyCode(key)) + +/*! clipboard functions*/ + RGFWDEF char* RGFW_readClipboard(size_t* size); /*!< read clipboard data */ +#define RGFW_clipboardFree free /* the string returned from RGFW_readClipboard must be freed */ + + RGFWDEF void RGFW_writeClipboard(const char* text, u32 textLen); /*!< write text to the clipboard */ + + /* + convert a keyString to a char version + */ + RGFWDEF char RGFW_keystrToChar(const char*); + /* + ex. + "parenleft" -> '(' + "A" -> 'A', + "Return" -> "\n" + */ + +#ifndef RGFW_NO_THREADS + /*! threading functions*/ + + /*! NOTE! (for X11/linux) : if you define a window in a thread, it must be run after the original thread's window is created or else there will be a memory error */ + /* + I'd suggest you use sili's threading functions instead + if you're going to use sili + which is a good idea generally + */ + RGFWDEF RGFW_thread RGFW_createThread(void* (*function_ptr)(void*), void* args); /*!< create a thread*/ + RGFWDEF void RGFW_cancelThread(RGFW_thread thread); /*!< cancels a thread*/ + RGFWDEF void RGFW_joinThread(RGFW_thread thread); /*!< join thread to current thread */ + RGFWDEF void RGFW_setThreadPriority(RGFW_thread thread, u8 priority); /*!< sets the priority priority */ +#endif + + /*! gamepad/joystick functions (linux-only currently) */ + + /*! joystick count starts at 0*/ + /*!< register joystick to window based on a number (the number is based on when it was connected eg. /dev/js0)*/ + RGFWDEF u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber); + RGFWDEF u16 RGFW_registerJoystickF(RGFW_window* win, char* file); + + RGFWDEF u32 RGFW_isPressedJS(RGFW_window* win, u16 controller, u8 button); + + /*! native opengl functions */ +#ifdef RGFW_OPENGL +/*! Get max OpenGL version */ + RGFWDEF u8* RGFW_getMaxGLVersion(); + /* OpenGL init hints */ + RGFWDEF void RGFW_setGLStencil(i32 stencil); /* set stencil buffer bit size (8 by default) */ + RGFWDEF void RGFW_setGLSamples(i32 samples); /* set number of sampiling buffers (4 by default) */ + RGFWDEF void RGFW_setGLStereo(i32 stereo); /* use GL_STEREO (GL_FALSE by default) */ + RGFWDEF void RGFW_setGLAuxBuffers(i32 auxBuffers); /* number of aux buffers (0 by default) */ + + /*! Set OpenGL version hint */ + RGFWDEF void RGFW_setGLVersion(i32 major, i32 minor); + RGFWDEF void* RGFW_getProcAddress(const char* procname); /* get native opengl proc address */ +#endif + /* supports openGL, directX, OSMesa, EGL and software rendering */ + RGFWDEF void RGFW_window_swapBuffers(RGFW_window* win); /* swap the rendering buffer */ + RGFWDEF void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval); + + RGFWDEF void RGFW_window_setGPURender(RGFW_window* win, i8 set); + +#ifdef RGFW_VULKAN + typedef struct { + VkInstance instance; + VkPhysicalDevice physical_device; + VkDevice device; + + VkDebugUtilsMessengerEXT debugMessenger; + + VkQueue graphics_queue; + VkQueue present_queue; + + VkFramebuffer* framebuffers; + + VkRenderPass render_pass; + VkPipelineLayout pipeline_layout; + VkPipeline graphics_pipeline; + + VkCommandPool command_pool; + VkCommandBuffer* command_buffers; + + VkSemaphore* available_semaphores; + VkSemaphore* finished_semaphore; + VkFence* in_flight_fences; + VkFence* image_in_flight; + size_t current_frame; + } RGFW_vulkanInfo; + + /*! initializes a vulkan rendering context for the RGFW window, + this outputs the vulkan surface into wwin->src.rSurf + other vulkan data is stored in the global instance of the RGFW_vulkanInfo structure which is returned + by the initVulkan() function + RGFW_VULKAN must be defined for this function to be defined + + */ + RGFWDEF RGFW_vulkanInfo* RGFW_initVulkan(RGFW_window* win); + RGFWDEF void RGFW_freeVulkan(void); + + RGFWDEF RGFW_vulkanInfo* RGFW_getVulkanInfo(void); + + RGFWDEF int RGFW_initData(RGFW_window* win); + RGFWDEF void RGFW_createSurface(VkInstance instance, RGFW_window* win); + int RGFW_deviceInitialization(RGFW_window* win); + int RGFW_createSwapchain(RGFW_window* win); + RGFWDEF int RGFW_createRenderPass(); + int RGFW_createCommandPool(); + int RGFW_createCommandBuffers(RGFW_window* win); + int RGFW_createSyncObjects(RGFW_window* win); + RGFWDEF int RGFW_createFramebuffers(RGFW_window* win); +#endif + +#ifdef RGFW_DIRECTX + typedef struct { + IDXGIFactory* pFactory; + IDXGIAdapter* pAdapter; + ID3D11Device* pDevice; + ID3D11DeviceContext* pDeviceContext; + } RGFW_directXinfo; + + /* + RGFW stores a global instance of RGFW_directXinfo, + you can use this function to get a pointer the instance + */ + RGFWDEF RGFW_directXinfo* RGFW_getDirectXInfo(void); +#endif + + /*! Supporting functions */ + RGFWDEF void RGFW_window_checkFPS(RGFW_window* win); /*!< updates fps / sets fps to cap (ran by RGFW_window_checkEvent)*/ + RGFWDEF u64 RGFW_getTime(void); /* get time in seconds */ + RGFWDEF u64 RGFW_getTimeNS(void); /* get time in nanoseconds */ + RGFWDEF u32 RGFW_getFPS(void); /* get current FPS (win->event.fps) */ + RGFWDEF void RGFW_sleep(u32 microsecond); /* sleep for a set time */ +#endif /* RGFW_HEADER */ + + /* + Example to get you started : + + linux : gcc main.c -lX11 -lXcursor -lGL + windows : gcc main.c -lopengl32 -lshell32 -lgdi32 + macos : gcc main.c -framework Foundation -framework AppKit -framework OpenGL -framework CoreVideo + + #define RGFW_IMPLEMENTATION + #include "RGFW.h" + + u8 icon[4 * 3 * 3] = {0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF}; + + int main() { + RGFW_window* win = RGFW_createWindow("name", RGFW_RECT(500, 500, 500, 500), (u64)0); + + RGFW_window_setIcon(win, icon, RGFW_AREA(3, 3), 4); + + for (;;) { + RGFW_window_checkEvent(win); // NOTE: checking events outside of a while loop may cause input lag + if (win->event.type == RGFW_quit || RGFW_isPressedI(win, RGFW_Escape)) + break; + + RGFW_window_swapBuffers(win); + + glClearColor(0xFF, 0XFF, 0xFF, 0xFF); + glClear(GL_COLOR_BUFFER_BIT); + } + + 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" + + 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 -lshell32 -lgdi32 -o RGFW.dll + linux: + gcc -shared RGFW.o -lX11 -lXcursor -lGL -o RGFW.so + macos: + gcc -shared RGFW.o -framework Foundation -framework AppKit -framework OpenGL -framework CoreVideo + */ + +#ifdef RGFW_X11 +#define RGFW_OS_BASED_VALUE(l, w, m) l +#endif +#ifdef RGFW_WINDOWS +#define RGFW_OS_BASED_VALUE(l, w, m) w +#endif +#ifdef RGFW_MACOS +#define RGFW_OS_BASED_VALUE(l, w, m) m +#endif + +#ifdef RGFW_IMPLEMENTATION + +#include +#include +#include +#include + +#ifdef RGFW_WINDOWS + +#define WIN32_LEAN_AND_MEAN + +#include + +#endif + +#ifdef RGFW_MACOS +#include + + /* + based on silicon.h + */ + +#define GL_SILENCE_DEPRECATION +#include +#include +#include +#include + + typedef CGRect NSRect; + typedef CGPoint NSPoint; + typedef CGSize NSSize; + + typedef void NSBitmapImageRep; + typedef void NSCursor; + typedef void NSDraggingInfo; + typedef void NSWindow; + typedef void NSApplication; + typedef void NSScreen; + typedef void NSEvent; + typedef void NSString; + typedef void NSOpenGLContext; + typedef void NSPasteboard; + typedef void NSColor; + typedef void NSArray; + typedef void NSImageRep; + typedef void NSImage; + typedef void NSOpenGLView; + + + typedef const char* NSPasteboardType; + typedef unsigned long NSUInteger; + typedef long NSInteger; + typedef NSInteger NSModalResponse; + +#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(nsclass, sel_registerName("alloc")) +#define objc_msgSend_bool ((BOOL (*)(id, SEL))objc_msgSend) +#define objc_msgSend_void ((void (*)(id, SEL))objc_msgSend) +#define objc_msgSend_void_id ((void (*)(id, SEL, id))objc_msgSend) +#define objc_msgSend_uint ((NSUInteger (*)(id, SEL))objc_msgSend) +#define objc_msgSend_void_bool ((void (*)(id, SEL, BOOL))objc_msgSend) +#define objc_msgSend_void_SEL ((void (*)(id, SEL, SEL))objc_msgSend) +#define objc_msgSend_id ((id (*)(id, SEL))objc_msgSend) + +#define si_declare_single(class, name, func) \ + void class##_##name(class* obj) { \ + return objc_msgSend_void(obj, sel_registerName(func)); \ + } + + +#define loadFunc(funcName) \ + static void* func = NULL;\ + if (func == NULL) \ + func = sel_registerName(funcName); + + void NSRelease(id obj) { + loadFunc("release"); + objc_msgSend_void(obj, func); + } + +#define release NSRelease + + si_declare_single(NSApplication, finishLaunching, "finishLaunching") + si_declare_single(NSOpenGLContext, flushBuffer, "flushBuffer") + + NSString* NSString_stringWithUTF8String(const char* str) { + loadFunc("stringWithUTF8String:"); + + return ((id(*)(id, SEL, const char*))objc_msgSend) + (objc_getClass("NSString"), func, str); + } + + const char* NSString_to_char(NSString* str) { + return ((const char* (*)(id, SEL)) objc_msgSend) (str, sel_registerName("UTF8String")); + } + + void si_impl_func_to_SEL_with_name(const char* class_name, const char* register_name, void* function) { + Class selected_class; + + if (strcmp(class_name, "NSView") == 0) { + selected_class = objc_getClass("ViewClass"); + } else if (strcmp(class_name, "NSWindow") == 0) { + selected_class = objc_getClass("WindowClass"); + } else { + selected_class = objc_getClass(class_name); + } + + class_addMethod(selected_class, sel_registerName(register_name), (IMP) function, 0); + } + + /* Header for the array. */ + typedef struct siArrayHeader { + size_t count; + /* TODO(EimaMei): Add a `type_width` later on. */ + } siArrayHeader; + + /* Gets the header of the siArray. */ +#define SI_ARRAY_HEADER(s) ((siArrayHeader*)s - 1) + + void* si_array_init_reserve(size_t sizeof_element, size_t count) { + siArrayHeader* ptr = malloc(sizeof(siArrayHeader) + (sizeof_element * count)); + void* array = ptr + sizeof(siArrayHeader); + + siArrayHeader* header = SI_ARRAY_HEADER(array); + header->count = count; + + return array; + } + +#define si_array_len(array) (SI_ARRAY_HEADER(array)->count) +#define si_func_to_SEL(class_name, function) si_impl_func_to_SEL_with_name(class_name, #function":", function) + /* Creates an Objective-C method (SEL) from a regular C function with the option to set the register name.*/ +#define si_func_to_SEL_with_name(class_name, register_name, function) si_impl_func_to_SEL_with_name(class_name, register_name":", function) + + NSRect NSMakeRect(double x, double y, double width, double height) { + NSRect r; + r.origin.x = x; + r.origin.y = y; + r.size.width = width; + r.size.height = height; + + return r; + } + + NSPoint NSMakePoint(double x, double y) { + NSPoint point; + point.x = x; + point.y = y; + return point; + } + + NSSize NSMakeSize(double w, double h) { + NSSize size; + size.width = w; + size.height = h; + return size; + } + + void* si_array_init(void* allocator, size_t sizeof_element, size_t count) { + void* array = si_array_init_reserve(sizeof_element, count); + memcpy(array, allocator, sizeof_element * count); + + return array; + } + + void si_array_free(void* array) { + if (array == NULL) + return; + + free(SI_ARRAY_HEADER(array)); + } + + unsigned char* NSBitmapImageRep_bitmapData(NSBitmapImageRep* imageRep) { + return ((unsigned char* (*)(id, SEL))objc_msgSend) + (imageRep, sel_registerName("bitmapData")); + } + +#define NS_ENUM(type, name) type name; enum + + typedef NS_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 API_AVAILABLE(macos(10.10)) = (1 << 8), + NSBitmapFormatThirtyTwoBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 9), + NSBitmapFormatSixteenBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 10), + NSBitmapFormatThirtyTwoBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 11) + }; + + NSBitmapImageRep* 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) { + void* func = sel_registerName("initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bitmapFormat:bytesPerRow:bitsPerPixel:"); + + return (NSBitmapImageRep*) ((id(*)(id, SEL, unsigned char**, NSInteger, NSInteger, NSInteger, NSInteger, bool, bool, const char*, NSBitmapFormat, NSInteger, NSInteger))objc_msgSend) + (NSAlloc(objc_getClass("NSBitmapImageRep")), func, planes, width, height, bps, spp, alpha, isPlanar, NSString_stringWithUTF8String(colorSpaceName), bitmapFormat, rowBytes, pixelBits); + } + + NSColor* NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha) { + void* nsclass = objc_getClass("NSColor"); + void* func = sel_registerName("colorWithSRGBRed:green:blue:alpha:"); + return ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend) + (nsclass, func, red, green, blue, alpha); + } + + NSCursor* NSCursor_initWithImage(NSImage* newImage, NSPoint aPoint) { + void* func = sel_registerName("initWithImage:hotSpot:"); + void* nsclass = objc_getClass("NSCursor"); + + return (NSCursor*) ((id(*)(id, SEL, id, NSPoint))objc_msgSend) + (NSAlloc(nsclass), func, newImage, aPoint); + } + + void NSImage_addRepresentation(NSImage* image, NSImageRep* imageRep) { + void* func = sel_registerName("addRepresentation:"); + objc_msgSend_void_id(image, func, imageRep); + } + + NSImage* NSImage_initWithSize(NSSize size) { + void* func = sel_registerName("initWithSize:"); + return ((id(*)(id, SEL, NSSize))objc_msgSend) + (NSAlloc(objc_getClass("NSImage")), func, size); + } +#define NS_OPENGL_ENUM_DEPRECATED(minVers, maxVers) API_AVAILABLE(macos(minVers)) + typedef NS_ENUM(NSInteger, NSOpenGLContextParameter) { + NSOpenGLContextParameterSwapInterval NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 222, /* 1 param. 0 -> Don't sync, 1 -> Sync to vertical retrace */ + NSOpenGLContextParameterSurfaceOrder NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 235, /* 1 param. 1 -> Above Window (default), -1 -> Below Window */ + NSOpenGLContextParameterSurfaceOpacity NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 236, /* 1 param. 1-> Surface is opaque (default), 0 -> non-opaque */ + NSOpenGLContextParameterSurfaceBackingSize NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 304, /* 2 params. Width/height of surface backing size */ + NSOpenGLContextParameterReclaimResources NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 308, /* 0 params. */ + NSOpenGLContextParameterCurrentRendererID NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 309, /* 1 param. Retrieves the current renderer ID */ + NSOpenGLContextParameterGPUVertexProcessing NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 310, /* 1 param. Currently processing vertices with GPU (get) */ + NSOpenGLContextParameterGPUFragmentProcessing NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 311, /* 1 param. Currently processing fragments with GPU (get) */ + NSOpenGLContextParameterHasDrawable NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 314, /* 1 param. Boolean returned if drawable is attached */ + NSOpenGLContextParameterMPSwapsInFlight NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 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 */ + NSOpenGLContextParameterSurfaceSurfaceVolatile API_DEPRECATED("", macos(10.0, 10.14)) = 306, /* 1 param. Surface volatile state */ + }; + + + void NSOpenGLContext_setValues(NSOpenGLContext* context, const int* vals, NSOpenGLContextParameter param) { + void* func = sel_registerName("setValues:forParameter:"); + ((void (*)(id, SEL, const int*, NSOpenGLContextParameter))objc_msgSend) + (context, func, vals, param); + } + + void* NSOpenGLPixelFormat_initWithAttributes(const uint32_t* attribs) { + void* func = sel_registerName("initWithAttributes:"); + return (void*) ((id(*)(id, SEL, const uint32_t*))objc_msgSend) + (NSAlloc(objc_getClass("NSOpenGLPixelFormat")), func, attribs); + } + + NSOpenGLView* NSOpenGLView_initWithFrame(NSRect frameRect, uint32_t* format) { + void* func = sel_registerName("initWithFrame:pixelFormat:"); + return (NSOpenGLView*) ((id(*)(id, SEL, NSRect, uint32_t*))objc_msgSend) + (NSAlloc(objc_getClass("NSOpenGLView")), func, frameRect, format); + } + + void NSCursor_performSelector(NSCursor* cursor, void* selector) { + void* func = sel_registerName("performSelector:"); + objc_msgSend_void_SEL(cursor, func, selector); + } + + NSPasteboard* NSPasteboard_generalPasteboard(void) { + return (NSPasteboard*) objc_msgSend_id(objc_getClass("NSPasteboard"), sel_registerName("generalPasteboard")); + } + + NSString** cstrToNSStringArray(char** strs, size_t len) { + static NSString* nstrs[6]; + size_t i; + for (i = 0; i < len; i++) + nstrs[i] = NSString_stringWithUTF8String(strs[i]); + + return nstrs; + } + + const char* NSPasteboard_stringForType(NSPasteboard* pasteboard, NSPasteboardType dataType) { + void* func = sel_registerName("stringForType:"); + return (const char*) NSString_to_char(((id(*)(id, SEL, const char*))objc_msgSend)(pasteboard, func, NSString_stringWithUTF8String(dataType))); + } + + NSArray* c_array_to_NSArray(void* array, size_t len) { + void* func = sel_registerName("initWithObjects:count:"); + void* nsclass = objc_getClass("NSArray"); + + return ((id(*)(id, SEL, void*, NSUInteger))objc_msgSend) + (NSAlloc(nsclass), func, array, len); + } + + void NSregisterForDraggedTypes(void* view, NSPasteboardType* newTypes, size_t len) { + NSString** ntypes = cstrToNSStringArray(newTypes, len); + + void* func = sel_registerName("registerForDraggedTypes:"); + + NSArray* array = c_array_to_NSArray(ntypes, len); + + objc_msgSend_void_id(view, sel_registerName("registerForDraggedTypes:"), array); + + NSRelease(array); + } + + NSInteger NSPasteBoard_declareTypes(NSPasteboard* pasteboard, NSPasteboardType* newTypes, size_t len, void* owner) { + NSString** ntypes = cstrToNSStringArray(newTypes, len); + + void* func = sel_registerName("declareTypes:owner:"); + + NSArray* array = c_array_to_NSArray(ntypes, len); + + NSInteger output = ((NSInteger(*)(id, SEL, id, void*))objc_msgSend) + (pasteboard, func, array, owner); + NSRelease(array); + + return output; + } + + bool NSPasteBoard_setString(NSPasteboard* pasteboard, const char* stringToWrite, NSPasteboardType dataType) { + void* func = sel_registerName("setString:forType:"); + return ((bool (*)(id, SEL, id, NSPasteboardType))objc_msgSend) + (pasteboard, func, NSString_stringWithUTF8String(stringToWrite), NSString_stringWithUTF8String(dataType)); + } + + void NSRetain(id obj) { objc_msgSend_void(obj, sel_registerName("retain")); } + + typedef enum NSApplicationActivationPolicy { + NSApplicationActivationPolicyRegular, + NSApplicationActivationPolicyAccessory, + NSApplicationActivationPolicyProhibited + } NSApplicationActivationPolicy; + + typedef NS_ENUM(u32, NSBackingStoreType) { + NSBackingStoreRetained = 0, + NSBackingStoreNonretained = 1, + NSBackingStoreBuffered = 2 + }; + + typedef NS_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 + }; + + typedef const char* NSPasteboardType; + NSPasteboardType const NSPasteboardTypeString = "public.utf8-plain-text"; // Replaces NSStringPboardType + + + + typedef NS_ENUM(i32, NSDragOperation) { + NSDragOperationNone = 0, + NSDragOperationCopy = 1, + NSDragOperationLink = 2, + NSDragOperationGeneric = 4, + NSDragOperationPrivate = 8, + NSDragOperationMove = 16, + NSDragOperationDelete = 32, + NSDragOperationEvery = ULONG_MAX, + + //NSDragOperationAll_Obsolete API_DEPRECATED("", macos(10.0,10.10)) = 15, // Use NSDragOperationEvery + //NSDragOperationAll API_DEPRECATED("", macos(10.0,10.10)) = NSDragOperationAll_Obsolete, // Use NSDragOperationEvery + }; + + + NSUInteger NSArray_count(NSArray* array) { + void* func = sel_registerName("count"); + return ((NSUInteger(*)(id, SEL))objc_msgSend)(array, func); + } + + void* NSArray_objectAtIndex(NSArray* array, NSUInteger index) { + void* func = sel_registerName("objectAtIndex:"); + return ((id(*)(id, SEL, NSUInteger))objc_msgSend)(array, func, index); + } + + const char** NSPasteboard_readObjectsForClasses(NSPasteboard* pasteboard, Class* classArray, size_t len, void* options) { + void* func = sel_registerName("readObjectsForClasses:options:"); + + NSArray* array = c_array_to_NSArray(classArray, len); + + NSArray* output = (NSArray*) ((id(*)(id, SEL, id, void*))objc_msgSend) + (pasteboard, func, array, options); + + NSRelease(array); + + NSUInteger count = NSArray_count(output); + + const char** res = si_array_init_reserve(sizeof(const char*), count); + + for (NSUInteger i = 0; i < count; i++) + res[i] = NSString_to_char(NSArray_objectAtIndex(output, i)); + + return res; + } + + void* NSWindow_contentView(NSWindow* window) { + void* func = sel_registerName("contentView"); + return objc_msgSend_id(window, func); + } +#endif + + +#define RGFW_ASSERT(check, str) {\ + if (!(check)) { \ + printf(str); \ + assert(check); \ + } \ +} + + u8 RGFW_error = 0; + u8 RGFW_Error() { return RGFW_error; } + +#define SET_ATTRIB(a, v) { \ + assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[index++] = a; \ + attribs[index++] = v; \ +} + +#define ADD_ATTRIB(a) { \ + assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ + attribs[index++] = a; \ +} + +#if defined(RGFW_X11) || defined(RGFW_WINDOWS) + void RGFW_window_showMouse(RGFW_window* win, i8 show) { + static u8 RGFW_blk[] = { 0, 0, 0, 0 }; + if (show == 0) + RGFW_window_setMouse(win, RGFW_blk, RGFW_AREA(1, 1), 4); + else + RGFW_window_setMouseDefault(win); + } +#endif + + RGFWDEF RGFW_window* RGFW_window_basic_init(RGFW_rect rect, u16 args); + RGFWDEF void RGFW_init_buffer(RGFW_window* win); + + RGFW_window* RGFW_window_basic_init(RGFW_rect rect, u16 args) { + RGFW_window* win = (RGFW_window*) RGFW_MALLOC(sizeof(RGFW_window)); /* make a new RGFW struct */ + +#ifdef RGFW_ALLOC_DROPFILES + win->event.droppedFiles = (char**) RGFW_MALLOC(sizeof(char*) * RGFW_MAX_DROPS); + u32 i; + for (i = 0; i < RGFW_MAX_DROPS; i++) + win->event.droppedFiles[i] = (char*) RGFW_CALLOC(RGFW_MAX_PATH, sizeof(char)); +#endif + +#ifdef RGFW_X11 + /* open X11 display */ + /* this is done here so the screen size can be accessed */ + win->src.display = XOpenDisplay(NULL); + assert(win->src.display != NULL); +#endif + + #ifndef RGFW_X11 + RGFW_area screenR = RGFW_getScreenSize(); + #else + win->src.display = XOpenDisplay(NULL); + assert(win->src.display != NULL); + + Screen* scrn = DefaultScreenOfDisplay((Display*)win->src.display); + RGFW_area screenR = RGFW_AREA(scrn->width, scrn->height); + #endif + + if (args & RGFW_FULLSCREEN) + rect = RGFW_RECT(0, 0, screenR.w, screenR.h); + + if (args & RGFW_CENTER) + rect = RGFW_RECT((screenR.w - rect.w) / 2, (screenR.h - rect.h) / 2, rect.w, rect.h); + + /* set and init the new window's data */ + win->r = rect; + win->fpsCap = 0; + win->event.inFocus = 1; + win->event.droppedFilesCount = 0; + win->src.joystickCount = 0; +#ifdef RGFW_X11 + win->src.cursor = 0; +#endif +#ifdef RGFW_MACOS + RGFW_window_setMouseDefault(win); +#endif +#ifdef RGFW_WINDOWS + win->src.maxSize = RGFW_AREA(0, 0); + win->src.minSize = RGFW_AREA(0, 0); +#endif + win->src.winArgs = 0; + + return win; + } + + void RGFW_window_scaleToMonitor(RGFW_window* win) { + RGFW_monitor monitor = RGFW_window_getMonitor(win); + + RGFW_window_resize(win, RGFW_AREA(((u32) monitor.scaleX) * win->r.w, ((u32) monitor.scaleX) * win->r.h)); + } + + void RGFW_init_buffer(RGFW_window* win) { +#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + RGFW_area area = RGFW_getScreenSize(); +#if !(defined(RGFW_WINDOWS)) || defined(RGFW_OSMESA) + win->buffer = RGFW_MALLOC(area.w * area.h * 4); +#endif + +#ifdef RGFW_OSMESA + win->src.rSurf = OSMesaCreateContext(OSMESA_RGBA, NULL); + OSMesaMakeCurrent(win->src.rSurf, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h); +#endif +#ifdef RGFW_X11 + win->src.bitmap = XCreateImage( + win->src.display, DefaultVisual(win->src.display, XDefaultScreen(win->src.display)), + DefaultDepth(win->src.display, XDefaultScreen(win->src.display)), + ZPixmap, 0, NULL, area.w, area.h, + 32, 0 + ); +#endif +#ifdef RGFW_WINDOWS + BITMAPV5HEADER bi = { 0 }; + ZeroMemory(&bi, sizeof(bi)); + bi.bV5Size = sizeof(bi); + bi.bV5Width = area.w; + bi.bV5Height = -((LONG) area.h); + bi.bV5Planes = 1; + bi.bV5BitCount = 32; + bi.bV5Compression = BI_BITFIELDS; + bi.bV5BlueMask = 0x00ff0000; + bi.bV5GreenMask = 0x0000ff00; + bi.bV5RedMask = 0x000000ff; + bi.bV5AlphaMask = 0xff000000; + + win->src.bitmap = CreateDIBSection(win->src.hdc, + (BITMAPINFO*) &bi, + DIB_RGB_COLORS, + (void**) &win->buffer, + NULL, + (DWORD) 0); + + win->src.hdcMem = CreateCompatibleDC(win->src.hdc); +#endif +#endif + } + +#if defined(RGFW_OPENGL) || defined(RGFW_EGL) || defined(RGFW_OSMESA) +#ifndef __APPLE__ +#include +#else +#include +#endif +#endif + +#ifdef RGFW_VULKAN + RGFW_vulkanInfo RGFW_vulkan_info; + + RGFW_vulkanInfo* RGFW_initVulkan(RGFW_window* win) { + assert(win != NULL); + + if ( + RGFW_initData(win) || + RGFW_deviceInitialization(win) || + RGFW_createSwapchain(win) + ) + return NULL; + + u32 graphics_family_index = 0; + u32 present_family_index = 0; + + vkGetDeviceQueue(RGFW_vulkan_info.device, graphics_family_index, 0, &RGFW_vulkan_info.graphics_queue); + vkGetDeviceQueue(RGFW_vulkan_info.device, present_family_index, 0, &RGFW_vulkan_info.present_queue); + + if ( + RGFW_createRenderPass() || + RGFW_createFramebuffers(win) || + RGFW_createCommandPool() || + RGFW_createCommandBuffers(win) || + RGFW_createSyncObjects(win) + ) + return NULL; + + return &RGFW_vulkan_info; + } + + int RGFW_initData(RGFW_window* win) { + assert(win != NULL); + + win->src.swapchain = VK_NULL_HANDLE; + win->src.image_count = 0; + RGFW_vulkan_info.current_frame = 0; + + return 0; + } + + void RGFW_createSurface(VkInstance instance, RGFW_window* win) { + assert(win != NULL); + assert(instance); + + win->src.rSurf = VK_NULL_HANDLE; + +#ifdef RGFW_X11 + VkXlibSurfaceCreateInfoKHR x11 = { VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, 0, 0, (Display*) win->src.display, (Window) win->src.window }; + + vkCreateXlibSurfaceKHR(RGFW_vulkan_info.instance, &x11, NULL, &win->src.rSurf); +#endif +#ifdef RGFW_WINDOWS + VkWin32SurfaceCreateInfoKHR win32 = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, 0, 0, GetModuleHandle(NULL), win->src.window }; + + vkCreateWin32SurfaceKHR(RGFW_vulkan_info.instance, &win32, NULL, &win->src.rSurf); +#endif +#if defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11) + VkMacOSSurfaceCreateFlagsMVK macos = { VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK, 0, 0, win->src.display, win->src.window }; + + vkCreateMacOSSurfaceMVK(RGFW_vulkan_info.instance, &macos, NULL, &win->src.rSurf); +#endif + } + + RGFW_vulkanInfo* RGFW_getVulkanInfo(void) { + return &RGFW_vulkan_info; + } + + int RGFW_deviceInitialization(RGFW_window* win) { + assert(win != NULL); + + VkApplicationInfo appInfo = { 0 }; + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.pApplicationName = "RGFW app"; + appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 0); + + char* extension = +#ifdef RGFW_WINDOWS + "VK_KHR_win32_surface"; +#elif defined(RGFW_X11) + VK_KHR_XLIB_SURFACE_EXTENSION_NAME; +#elif defined(RGFW_MACOS) + "VK_MVK_macos_surface"; +#else + NULL; +#endif + + VkInstanceCreateInfo instance_create_info = { 0 }; + instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instance_create_info.pApplicationInfo = &appInfo; + instance_create_info.enabledExtensionCount = extension ? 2 : 0, + instance_create_info.ppEnabledExtensionNames = (const char* [2]){ + VK_KHR_SURFACE_EXTENSION_NAME, + extension + }; + + if (vkCreateInstance(&instance_create_info, NULL, &RGFW_vulkan_info.instance) != VK_SUCCESS) { + fprintf(stderr, "failed to create instance!\n"); + return -1; + } + + + RGFW_createSurface(RGFW_vulkan_info.instance, win); + + u32 deviceCount = 0; + vkEnumeratePhysicalDevices(RGFW_vulkan_info.instance, &deviceCount, NULL); + VkPhysicalDevice* devices = (VkPhysicalDevice*) malloc(sizeof(VkPhysicalDevice) * deviceCount); + vkEnumeratePhysicalDevices(RGFW_vulkan_info.instance, &deviceCount, devices); + + RGFW_vulkan_info.physical_device = devices[0]; + + u32 queue_family_count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(RGFW_vulkan_info.physical_device, &queue_family_count, NULL); + VkQueueFamilyProperties* queueFamilies = (VkQueueFamilyProperties*) malloc(sizeof(VkQueueFamilyProperties) * queue_family_count); + vkGetPhysicalDeviceQueueFamilyProperties(RGFW_vulkan_info.physical_device, &queue_family_count, queueFamilies); + + float queuePriority = 1.0f; + + VkPhysicalDeviceFeatures device_features = { 0 }; + + VkDeviceCreateInfo device_create_info = { 0 }; + device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + VkDeviceQueueCreateInfo queue_create_infos[2] = { + {0}, + {0}, + }; + queue_create_infos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_create_infos[0].queueCount = 1; + queue_create_infos[0].pQueuePriorities = &queuePriority; + queue_create_infos[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_create_infos[1].queueCount = 1; + queue_create_infos[1].pQueuePriorities = &queuePriority; + device_create_info.queueCreateInfoCount = 2; + device_create_info.pQueueCreateInfos = queue_create_infos; + + device_create_info.enabledExtensionCount = 1; + + const char* device_extensions[] = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME + }; + + device_create_info.ppEnabledExtensionNames = device_extensions; + device_create_info.pEnabledFeatures = &device_features; + + if (vkCreateDevice(RGFW_vulkan_info.physical_device, &device_create_info, NULL, &RGFW_vulkan_info.device) != VK_SUCCESS) { + fprintf(stderr, "failed to create logical device!\n"); + return -1; + } + + return 0; + } + + int RGFW_createSwapchain(RGFW_window* win) { + assert(win != NULL); + + VkSurfaceFormatKHR surfaceFormat = { VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR }; + VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; + + VkSurfaceCapabilitiesKHR capabilities = { 0 }; + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(RGFW_vulkan_info.physical_device, win->src.rSurf, &capabilities); + + win->src.image_count = capabilities.minImageCount + 1; + if (capabilities.maxImageCount > 0 && win->src.image_count > capabilities.maxImageCount) { + win->src.image_count = capabilities.maxImageCount; + } + + VkSwapchainCreateInfoKHR swapchain_create_info = { 0 }; + swapchain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchain_create_info.surface = win->src.rSurf; + swapchain_create_info.minImageCount = win->src.image_count; + swapchain_create_info.imageFormat = surfaceFormat.format; + swapchain_create_info.imageColorSpace = surfaceFormat.colorSpace; + swapchain_create_info.imageExtent = (VkExtent2D){ win->r.w, win->r.h }; + swapchain_create_info.imageArrayLayers = 1; + swapchain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchain_create_info.queueFamilyIndexCount = 2; + swapchain_create_info.preTransform = capabilities.currentTransform; + swapchain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swapchain_create_info.presentMode = presentMode; + swapchain_create_info.clipped = VK_TRUE; + swapchain_create_info.oldSwapchain = VK_NULL_HANDLE; + + if (vkCreateSwapchainKHR(RGFW_vulkan_info.device, &swapchain_create_info, NULL, &win->src.swapchain) != VK_SUCCESS) { + fprintf(stderr, "failed to create swap chain!\n"); + return -1; + } + + u32 imageCount; + vkGetSwapchainImagesKHR(RGFW_vulkan_info.device, win->src.swapchain, &imageCount, NULL); + win->src.swapchain_images = (VkImage*) malloc(sizeof(VkImage) * imageCount); + vkGetSwapchainImagesKHR(RGFW_vulkan_info.device, win->src.swapchain, &imageCount, win->src.swapchain_images); + + win->src.swapchain_image_views = (VkImageView*) malloc(sizeof(VkImageView) * imageCount); + for (u32 i = 0; i < imageCount; i++) { + VkImageViewCreateInfo image_view_cre_infos = { 0 }; + image_view_cre_infos.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_cre_infos.image = win->src.swapchain_images[i]; + image_view_cre_infos.viewType = VK_IMAGE_VIEW_TYPE_2D; + image_view_cre_infos.format = VK_FORMAT_B8G8R8A8_SRGB; + image_view_cre_infos.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_cre_infos.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_cre_infos.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_cre_infos.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + image_view_cre_infos.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_view_cre_infos.subresourceRange.baseMipLevel = 0; + image_view_cre_infos.subresourceRange.levelCount = 1; + image_view_cre_infos.subresourceRange.baseArrayLayer = 0; + image_view_cre_infos.subresourceRange.layerCount = 1; + if (vkCreateImageView(RGFW_vulkan_info.device, &image_view_cre_infos, NULL, &win->src.swapchain_image_views[i]) != VK_SUCCESS) { + fprintf(stderr, "failed to create image views!"); + return -1; + } + } + + return 0; + } + + int RGFW_createRenderPass(void) { + VkAttachmentDescription color_attachment = { 0 }; + color_attachment.format = VK_FORMAT_B8G8R8A8_SRGB; + color_attachment.samples = VK_SAMPLE_COUNT_1_BIT; + color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference color_attachment_ref = { 0 }; + color_attachment_ref.attachment = 0; + color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = { 0 }; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_attachment_ref; + + VkSubpassDependency dependency = { 0 }; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + VkRenderPassCreateInfo render_pass_info = { 0 }; + render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + render_pass_info.attachmentCount = 1; + render_pass_info.pAttachments = &color_attachment; + render_pass_info.subpassCount = 1; + render_pass_info.pSubpasses = &subpass; + render_pass_info.dependencyCount = 1; + render_pass_info.pDependencies = &dependency; + + if (vkCreateRenderPass(RGFW_vulkan_info.device, &render_pass_info, NULL, &RGFW_vulkan_info.render_pass) != VK_SUCCESS) { + fprintf(stderr, "failed to create render pass\n"); + return -1; // failed to create render pass! + } + return 0; + } + + int RGFW_createCommandPool(void) { + VkCommandPoolCreateInfo pool_info = { 0 }; + pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + pool_info.queueFamilyIndex = 0; + + if (vkCreateCommandPool(RGFW_vulkan_info.device, &pool_info, NULL, &RGFW_vulkan_info.command_pool) != VK_SUCCESS) { + fprintf(stderr, "failed to create command pool\n"); + return -1; // failed to create command pool + } + return 0; + } + + int RGFW_createCommandBuffers(RGFW_window* win) { + assert(win != NULL); + + RGFW_vulkan_info.command_buffers = (VkCommandBuffer*) malloc(sizeof(VkCommandBuffer) * win->src.image_count); + + VkCommandBufferAllocateInfo allocInfo = { 0 }; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = RGFW_vulkan_info.command_pool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = (u32) win->src.image_count; + + if (vkAllocateCommandBuffers(RGFW_vulkan_info.device, &allocInfo, RGFW_vulkan_info.command_buffers) != VK_SUCCESS) { + return -1; // failed to allocate command buffers; + } + + return 0; + } + + int RGFW_createSyncObjects(RGFW_window* win) { + assert(win != NULL); + + RGFW_vulkan_info.available_semaphores = (VkSemaphore*) malloc(sizeof(VkSemaphore) * RGFW_MAX_FRAMES_IN_FLIGHT); + RGFW_vulkan_info.finished_semaphore = (VkSemaphore*) malloc(sizeof(VkSemaphore) * RGFW_MAX_FRAMES_IN_FLIGHT); + RGFW_vulkan_info.in_flight_fences = (VkFence*) malloc(sizeof(VkFence) * RGFW_MAX_FRAMES_IN_FLIGHT); + RGFW_vulkan_info.image_in_flight = (VkFence*) malloc(sizeof(VkFence) * win->src.image_count); + + VkSemaphoreCreateInfo semaphore_info = { 0 }; + semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + VkFenceCreateInfo fence_info = { 0 }; + fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + for (size_t i = 0; i < RGFW_MAX_FRAMES_IN_FLIGHT; i++) { + if (vkCreateSemaphore(RGFW_vulkan_info.device, &semaphore_info, NULL, &RGFW_vulkan_info.available_semaphores[i]) != VK_SUCCESS || + vkCreateSemaphore(RGFW_vulkan_info.device, &semaphore_info, NULL, &RGFW_vulkan_info.finished_semaphore[i]) != VK_SUCCESS || + vkCreateFence(RGFW_vulkan_info.device, &fence_info, NULL, &RGFW_vulkan_info.in_flight_fences[i]) != VK_SUCCESS) { + fprintf(stderr, "failed to create sync objects\n"); + return -1; // failed to create synchronization objects for a frame + } + } + + for (size_t i = 0; i < win->src.image_count; i++) { + RGFW_vulkan_info.image_in_flight[i] = VK_NULL_HANDLE; + } + + return 0; + } + + int RGFW_createFramebuffers(RGFW_window* win) { + assert(win != NULL); + + RGFW_vulkan_info.framebuffers = (VkFramebuffer*) malloc(sizeof(VkFramebuffer) * win->src.image_count); + + for (size_t i = 0; i < win->src.image_count; i++) { + VkImageView attachments[] = { win->src.swapchain_image_views[i] }; + + VkFramebufferCreateInfo framebuffer_info = { 0 }; + framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebuffer_info.renderPass = RGFW_vulkan_info.render_pass; + framebuffer_info.attachmentCount = 1; + framebuffer_info.pAttachments = attachments; + framebuffer_info.width = win->r.w; + framebuffer_info.height = win->r.h; + framebuffer_info.layers = 1; + + if (vkCreateFramebuffer(RGFW_vulkan_info.device, &framebuffer_info, NULL, &RGFW_vulkan_info.framebuffers[i]) != VK_SUCCESS) { + return -1; // failed to create framebuffer + } + } + return 0; + } + + void RGFW_freeVulkan(void) { + vkDeviceWaitIdle(RGFW_vulkan_info.device); + + for (size_t i = 0; i < RGFW_MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroySemaphore(RGFW_vulkan_info.device, RGFW_vulkan_info.finished_semaphore[i], NULL); + vkDestroySemaphore(RGFW_vulkan_info.device, RGFW_vulkan_info.available_semaphores[i], NULL); + vkDestroyFence(RGFW_vulkan_info.device, RGFW_vulkan_info.in_flight_fences[i], NULL); + } + + vkDestroyCommandPool(RGFW_vulkan_info.device, RGFW_vulkan_info.command_pool, NULL); + + vkDestroyPipeline(RGFW_vulkan_info.device, RGFW_vulkan_info.graphics_pipeline, NULL); + vkDestroyPipelineLayout(RGFW_vulkan_info.device, RGFW_vulkan_info.pipeline_layout, NULL); + vkDestroyRenderPass(RGFW_vulkan_info.device, RGFW_vulkan_info.render_pass, NULL); + +#ifdef RGFW_DEBUG + PFN_vkDestroyDebugUtilsMessengerEXT func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(RGFW_vulkan_info.instance, "vkDestroyDebugUtilsMessengerEXT"); + if (func != NULL) { + func(RGFW_vulkan_info.instance, RGFW_vulkan_info.debugMessenger, NULL); + } +#endif + + vkDestroyDevice(RGFW_vulkan_info.device, NULL); + vkDestroyInstance(RGFW_vulkan_info.instance, NULL); + + free(RGFW_vulkan_info.framebuffers); + free(RGFW_vulkan_info.command_buffers); + free(RGFW_vulkan_info.available_semaphores); + free(RGFW_vulkan_info.finished_semaphore); + free(RGFW_vulkan_info.in_flight_fences); + free(RGFW_vulkan_info.image_in_flight); + } + +#endif /* RGFW_VULKAN */ + + RGFW_window* RGFW_root = NULL; + +#ifdef RGFW_X11 +#include +#ifndef RGFW_NO_X11_CURSOR +#include +#endif +#include + +#ifndef RGFW_NO_DPI +#include +#include +#endif +#endif + +#define RGFW_MOUSE_CHANGED (1L<<1) /*!< mouse change (for winargs)*/ +#define RGFW_HOLD_MOUSE (1L<<2) /*!< hold the moues still */ + +#ifdef RGFW_WINDOWS +#include +#include +#include +#include +#include +#include +#endif + +#if defined(RGFW_MACOS) + u8 RGFW_keyMap[128] = { 0 }; +#endif + + char* RGFW_keyCodeTokeyStr(u64 key) { +#if defined(RGFW_MACOS) + static char* keyStrs[128] = { "a", "s", "d", "f", "h", "g", "z", "x", "c", "v", "0", "b", "q", "w", "e", "r", "y", "t", "1", "2", "3", "4", "6", "5", "Equals", "9", "7", "Minus", "8", "0", "CloseBracket", "o", "u", "Bracket", "i", "p", "Return", "l", "j", "Apostrophe", "k", "Semicolon", "BackSlash", "Comma", "Slash", "n", "m", "Period", "Tab", "Space", "Backtick", "BackSpace", "0", "Escape", "0", "Super", "Shift", "CapsLock", "Alt", "Control", "0", "0", "0", "0", "0", "KP_Period", "0", "KP_Minus", "0", "0", "0", "0", "Numlock", "0", "0", "0", "KP_Multiply", "KP_Return", "0", "0", "0", "0", "KP_Slash", "KP_0", "KP_1", "KP_2", "KP_3", "KP_4", "KP_5", "KP_6", "KP_7", "0", "KP_8", "KP_9", "0", "0", "0", "F5", "F6", "F7", "F3", "F8", "F9", "0", "F11", "0", "F13", "0", "F14", "0", "F10", "0", "F12", "0", "F15", "Insert", "Home", "PageUp", "Delete", "F4", "End", "F2", "PageDown", "Left", "Right", "Down", "Up", "F1" }; + + return keyStrs[key]; +#endif +#ifdef RGFW_X11 + return XKeysymToString(key); +#endif +#ifdef RGFW_WINDOWS + static char keyName[16]; + GetKeyNameTextA((LONG) key, keyName, 16); + + if ((!(GetKeyState(VK_CAPITAL) & 0x0001) && !(GetKeyState(VK_SHIFT) & 0x8000)) || + ((GetKeyState(VK_CAPITAL) & 0x0001) && (GetKeyState(VK_SHIFT) & 0x8000))) { + CharLowerBuffA(keyName, 16); + } + + return keyName; +#endif + } + + u32 RGFW_keyStrToKeyCode(char* key) { +#if defined(RGFW_MACOS) + static char* keyStrs[128] = { "a", "s", "d", "f", "h", "g", "z", "x", "c", "v", "0", "b", "q", "w", "e", "r", "y", "t", "1", "2", "3", "4", "6", "5", "Equals", "9", "7", "Minus", "8", "0", "CloseBracket", "o", "u", "Bracket", "i", "p", "Return", "l", "j", "Apostrophe", "k", "Semicolon", "BackSlash", "Comma", "Slash", "n", "m", "Period", "Tab", "Space", "Backtick", "BackSpace", "0", "Escape", "0", "Super", "Shift", "CapsLock", "Alt", "Control", "0", "0", "0", "0", "0", "KP_Period", "0", "KP_Minus", "0", "0", "0", "0", "Numlock", "0", "0", "0", "KP_Multiply", "KP_Return", "0", "0", "0", "0", "KP_Slash", "KP_0", "KP_1", "KP_2", "KP_3", "KP_4", "KP_5", "KP_6", "KP_7", "0", "KP_8", "KP_9", "0", "0", "0", "F5", "F6", "F7", "F3", "F8", "F9", "0", "F11", "0", "F13", "0", "F14", "0", "F10", "0", "F12", "0", "F15", "Insert", "Home", "PageUp", "Delete", "F4", "End", "F2", "PageDown", "Left", "Right", "Down", "Up", "F1" }; + + key--; + while (key++) { + u32 i; + for (i = 0; i < 128; i++) { + if (*keyStrs[i] == '\1') + continue; + + if (*keyStrs[i] != *key) { + keyStrs[i] = "\1"; + continue; + } + + if (*keyStrs[i] == '\0' && *key == '\0') + return i; + + else + keyStrs[i]++; + } + + if (*key == '\0') + break; + } + +#endif +#ifdef RGFW_X11 + if (strcmp(key, "Space") == 0) key = (char*) "space"; + + return XStringToKeysym(key); +#endif +#ifdef RGFW_WINDOWS + if (key[1]) { + struct { char* key; i32 code; } keyStrs[] = { + {"Super_L", VK_LWIN}, + {"Super_R", VK_RWIN}, + {"Space", VK_SPACE}, + {"Return", VK_RETURN}, + {"Caps_Lock", VK_CAPITAL}, + {"Tab", VK_TAB}, + {"Right", VK_RIGHT}, + {"Left", VK_LEFT}, + {"Up", VK_UP}, + {"Down", VK_DOWN}, + {"ShiftL", VK_LSHIFT}, + {"ShiftR", VK_RSHIFT}, + {"ControlL", VK_RCONTROL}, + {"ControlR", VK_RCONTROL} + }; + + + while (key++) { + u32 i; + for (i = 0; i < 14; i++) { + if (*keyStrs[i].key != '\0' && *keyStrs[i].key != '\1') + keyStrs[i].key++; + + if (*keyStrs[i].key != *key) { + keyStrs[i].key = "\1"; + continue; + } + + if (*keyStrs[i].key == '\0' && *key == '\0') + return keyStrs[i].code; + } + + if (*key == '\0') + break; + } + } + + i32 vKey = VkKeyScan(key[0]); + + return vKey; +#endif /* RGFW_WINDOWS */ + + return 0; + } + + + char RGFW_keystrToChar(const char* str) { + if (str[1] == 0) + return str[0]; + + static const char* map[] = { + "asciitilde", "`", + "grave", "~", + "exclam", "!", + "at", "@", + "numbersign", "#", + "dollar", "$", + "percent", "%%", + "asciicircum", "^", + "ampersand", "&", + "asterisk", "*", + "parenleft", "(", + "parenright", ")", + "underscore", "_", + "minus", "-", + "plus", "+", + "equal", "=", + "braceleft", "{", + "bracketleft", "[", + "bracketright", "]", + "braceright", "}", + "colon", ":", + "semicolon", ";", + "quotedbl", "\"", + "apostrophe", "'", + "bar", "|", + "backslash", "\'", + "less", "<", + "comma", ",", + "greater", ">", + "period", ".", + "question", "?", + "slash", "/", + "space", " ", + "Return", "\n", + "Enter", "\n", + "enter", "\n", + }; + + u8 i = 0; + for (i = 0; i < (sizeof(map) / sizeof(char*)); i += 2) + if (strcmp(map[i], str) == 0) + return *map[i + 1]; + + return '\0'; + } + +#ifndef M_PI +#define M_PI 3.14159265358979323846 /* pi */ +#endif + + typedef struct RGFW_Timespec { + time_t tv_sec; /* Seconds. */ + u32 tv_nsec; /* Nanoseconds. */ + } RGFW_Timespec; /*time struct for fps functions*/ + +#ifndef RGFW_WINDOWS + u32 RGFW_isPressedJS(RGFW_window* win, u16 c, u8 button) { return win->src.jsPressed[c][button]; } +#else + + typedef DWORD (WINAPI * PFN_XInputGetState)(DWORD,XINPUT_STATE*); + PFN_XInputGetState XInputGetStateSRC = NULL; + #define XInputGetState XInputGetStateSRC + static HMODULE RGFW_XInput_dll = NULL; + + u32 RGFW_isPressedJS(RGFW_window* win, u16 c, u8 button) { + XINPUT_STATE state; + if (XInputGetState == NULL || XInputGetState(c, &state) == ERROR_DEVICE_NOT_CONNECTED) + return 0; + + if (button == RGFW_JS_A) return state.Gamepad.wButtons & XINPUT_GAMEPAD_A; + else if (button == RGFW_JS_B) return state.Gamepad.wButtons & XINPUT_GAMEPAD_B; + else if (button == RGFW_JS_Y) return state.Gamepad.wButtons & XINPUT_GAMEPAD_Y; + else if (button == RGFW_JS_X) return state.Gamepad.wButtons & XINPUT_GAMEPAD_X; + else if (button == RGFW_JS_START) return state.Gamepad.wButtons & XINPUT_GAMEPAD_START; + else if (button == RGFW_JS_SELECT) return state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK; + else if (button == RGFW_JS_UP) return state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP; + else if (button == RGFW_JS_DOWN) return state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN; + else if (button == RGFW_JS_LEFT) return state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT; + else if (button == RGFW_JS_RIGHT) return state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT; + else if (button == RGFW_JS_L1) return state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER; + else if (button == RGFW_JS_R1) return state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER; + else if (button == RGFW_JS_L2 && state.Gamepad.bLeftTrigger) return 1; + else if (button == RGFW_JS_R2 && state.Gamepad.bRightTrigger) return 1; + + return 0; + } +#endif + +#ifdef RGFW_OPENGL + i32 RGFW_majorVersion = 0, RGFW_minorVersion = 0; + i32 RGFW_STENCIL = 8, RGFW_SAMPLES = 4, RGFW_STEREO = GL_FALSE, RGFW_AUX_BUFFERS = 0; + + void RGFW_setGLStencil(i32 stencil) { RGFW_STENCIL = stencil; } + void RGFW_setGLSamples(i32 samples) { RGFW_SAMPLES = samples; } + void RGFW_setGLStereo(i32 stereo) { RGFW_STEREO = stereo; } + void RGFW_setGLAuxBuffers(i32 auxBuffers) { RGFW_AUX_BUFFERS = auxBuffers; } + + void RGFW_setGLVersion(i32 major, i32 minor) { + RGFW_majorVersion = major; + RGFW_minorVersion = minor; + } + + u8* RGFW_getMaxGLVersion() { + RGFW_window* dummy = RGFW_createWindow("dummy", RGFW_RECT(0, 0, 1, 1), 0); + + const char* versionStr = (const char*) glGetString(GL_VERSION); + + static u8 version[2]; + version[0] = versionStr[0] - '0', + version[1] = versionStr[2] - '0'; + + RGFW_window_close(dummy); + + return version; + } + +#define RGFW_GL_RENDER_TYPE RGFW_OS_BASED_VALUE(GLX_X_VISUAL_TYPE, 0x2003, 73) +#define RGFW_GL_ALPHA_SIZE RGFW_OS_BASED_VALUE(GLX_ALPHA_SIZE, 0x201b, 11) +#define RGFW_GL_DEPTH_SIZE RGFW_OS_BASED_VALUE(GLX_DEPTH_SIZE, 0x2022, 12) +#define RGFW_GL_DOUBLEBUFFER RGFW_OS_BASED_VALUE(GLX_DOUBLEBUFFER, 0x2011, 5) +#define RGFW_GL_STENCIL_SIZE RGFW_OS_BASED_VALUE(GLX_STENCIL_SIZE, 0x2023, 13) +#define RGFW_GL_SAMPLES RGFW_OS_BASED_VALUE(GLX_SAMPLES, 0x2042, 55) +#define RGFW_GL_STEREO RGFW_OS_BASED_VALUE(GLX_STEREO, 0x2012, 6) +#define RGFW_GL_AUX_BUFFERS RGFW_OS_BASED_VALUE(GLX_AUX_BUFFERS, 0x2024, 7) + +#if defined(RGFW_X11) || defined(RGFW_WINDOWS) +#define RGFW_GL_DRAW RGFW_OS_BASED_VALUE(GLX_X_RENDERABLE, 0x2001, 0) +#define RGFW_GL_DRAW_TYPE RGFW_OS_BASED_VALUE(GLX_RENDER_TYPE, 0x2013, 0) +#define RGFW_GL_USE_OPENGL RGFW_OS_BASED_VALUE(GLX_USE_GL, 0x2010, 0) +#define RGFW_GL_FULL_FORMAT RGFW_OS_BASED_VALUE(GLX_TRUE_COLOR, 0x2027, 0) +#define RGFW_GL_RED_SIZE RGFW_OS_BASED_VALUE(GLX_RED_SIZE, 0x2015, 0) +#define RGFW_GL_GREEN_SIZE RGFW_OS_BASED_VALUE(GLX_GREEN_SIZE, 0x2017, 0) +#define RGFW_GL_BLUE_SIZE RGFW_OS_BASED_VALUE(GLX_BLUE_SIZE, 0x2019, 0) +#define RGFW_GL_USE_RGBA RGFW_OS_BASED_VALUE(GLX_RGBA_BIT, 0x202B, 0) +#endif + +#ifdef RGFW_WINDOWS +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define WGL_SAMPLE_BUFFERS_ARB 0x2041 +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9 +#endif + + static u32* RGFW_initAttribs(u32 useSoftware) { + static u32 attribs[] = { + #ifndef RGFW_MACOS + RGFW_GL_RENDER_TYPE, + RGFW_GL_FULL_FORMAT, + #endif + RGFW_GL_ALPHA_SIZE , 8, + RGFW_GL_DEPTH_SIZE , 24, + RGFW_GL_DOUBLEBUFFER , + #ifndef RGFW_MACOS + 1, + #endif + + #if defined(RGFW_X11) || defined(RGFW_WINDOWS) + RGFW_GL_USE_OPENGL, 1, + RGFW_GL_DRAW, 1, + RGFW_GL_RED_SIZE , 8, + RGFW_GL_GREEN_SIZE , 8, + RGFW_GL_BLUE_SIZE , 8, + RGFW_GL_DRAW_TYPE , RGFW_GL_USE_RGBA, + #endif + + #ifdef RGFW_X11 + GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, + #endif + + #ifdef RGFW_MACOS + 72, + 8, 24, + #endif + + #ifdef RGFW_WINDOWS + WGL_COLOR_BITS_ARB, 32, + #endif + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + size_t index = (sizeof(attribs) / sizeof(attribs[0])) - 13; + +#define RGFW_GL_ADD_ATTRIB(attrib, attVal) \ + if (attVal) { \ + attribs[index] = attrib;\ + attribs[index + 1] = attVal;\ + index += 2;\ + } + + RGFW_GL_ADD_ATTRIB(RGFW_GL_STENCIL_SIZE, RGFW_STENCIL); + RGFW_GL_ADD_ATTRIB(RGFW_GL_STEREO, RGFW_STEREO); + RGFW_GL_ADD_ATTRIB(RGFW_GL_AUX_BUFFERS, RGFW_AUX_BUFFERS); + +#ifndef RGFW_X11 + RGFW_GL_ADD_ATTRIB(RGFW_GL_SAMPLES, RGFW_SAMPLES); +#endif + +#ifdef RGFW_MACOS + if (useSoftware) { + RGFW_GL_ADD_ATTRIB(70, kCGLRendererGenericFloatID); + } else { + attribs[index] = RGFW_GL_RENDER_TYPE; + index += 1; + } +#endif + +#ifdef RGFW_MACOS + attribs[index] = 99; + attribs[index + 1] = 0x1000; + + if (RGFW_majorVersion >= 4 || RGFW_majorVersion >= 3) { + attribs[index + 1] = (u32) ((RGFW_majorVersion >= 4) ? 0x4100 : 0x3200); + } + +#endif + + RGFW_GL_ADD_ATTRIB(0, 0); + + return attribs; + } + +#endif + +#ifdef RGFW_EGL + +#if defined(RGFW_LINK_EGL) + typedef EGLBoolean(EGLAPIENTRY* PFN_eglInitialize)(EGLDisplay, EGLint*, EGLint*); + + PFNEGLINITIALIZEPROC eglInitializeSource; + PFNEGLGETCONFIGSPROC eglGetConfigsSource; + PFNEGLCHOOSECONFIGPROC eglChooseConfigSource; + PFNEGLCREATEWINDOWSURFACEPROC eglCreateWindowSurfaceSource; + PFNEGLCREATECONTEXTPROC eglCreateContextSource; + PFNEGLMAKECURRENTPROC eglMakeCurrentSource; + PFNEGLGETDISPLAYPROC eglGetDisplaySource; + PFNEGLSWAPBUFFERSPROC eglSwapBuffersSource; + PFNEGLSWAPINTERVALPROC eglSwapIntervalSource; + PFNEGLBINDAPIPROC eglBindAPISource; + PFNEGLDESTROYCONTEXTPROC eglDestroyContextSource; + PFNEGLTERMINATEPROC eglTerminateSource; + PFNEGLDESTROYSURFACEPROC eglDestroySurfaceSource; + +#define eglInitialize eglInitializeSource +#define eglGetConfigs eglGetConfigsSource +#define eglChooseConfig eglChooseConfigSource +#define eglCreateWindowSurface eglCreateWindowSurfaceSource +#define eglCreateContext eglCreateContextSource +#define eglMakeCurrent eglMakeCurrentSource +#define eglGetDisplay eglGetDisplaySource +#define eglSwapBuffers eglSwapBuffersSource +#define eglSwapInterval eglSwapIntervalSource +#define eglBindAPI eglBindAPISource +#define eglDestroyContext eglDestroyContextSource +#define eglTerminate eglTerminateSource +#define eglDestroySurface eglDestroySurfaceSource; +#endif + + +#define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098 +#define EGL_CONTEXT_MINOR_VERSION_KHR 0x30fb + +#ifndef RGFW_GL_ADD_ATTRIB +#define RGFW_GL_ADD_ATTRIB(attrib, attVal) \ + if (attVal) { \ + attribs[index] = attrib;\ + attribs[index + 1] = attVal;\ + index += 2;\ + } +#endif + + void RGFW_createOpenGLContext(RGFW_window* win) { + static EGLContext globalCtx = EGL_NO_CONTEXT; + +#if defined(RGFW_LINK_EGL) + eglInitializeSource = (PFNEGLINITIALIZEPROC) eglGetProcAddress("eglInitialize"); + eglGetConfigsSource = (PFNEGLGETCONFIGSPROC) eglGetProcAddress("eglGetConfigs"); + eglChooseConfigSource = (PFNEGLCHOOSECONFIGPROC) eglGetProcAddress("eglChooseConfig"); + eglCreateWindowSurfaceSource = (PFNEGLCREATEWINDOWSURFACEPROC) eglGetProcAddress("eglCreateWindowSurface"); + eglCreateContextSource = (PFNEGLCREATECONTEXTPROC) eglGetProcAddress("eglCreateContext"); + eglMakeCurrentSource = (PFNEGLMAKECURRENTPROC) eglGetProcAddress("eglMakeCurrent"); + eglGetDisplaySource = (PFNEGLGETDISPLAYPROC) eglGetProcAddress("eglGetDisplay"); + eglSwapBuffersSource = (PFNEGLSWAPBUFFERSPROC) eglGetProcAddress("eglSwapBuffers"); + eglSwapIntervalSource = (PFNEGLSWAPINTERVALPROC) eglGetProcAddress("eglSwapInterval"); + eglBindAPISource = (PFNEGLBINDAPIPROC) eglGetProcAddress("eglBindAPI"); + eglDestroyContextSource = (PFNEGLDESTROYCONTEXTPROC) eglGetProcAddress("eglDestroyContext"); + eglTerminateSource = (PFNEGLTERMINATEPROC) eglGetProcAddress("eglTerminate"); + eglDestroySurfaceSource = (PFNEGLDESTROYSURFACEPROC) eglGetProcAddress("eglDestroySurface"); +#endif /* RGFW_LINK_EGL */ + + win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.display); + + EGLint major, minor; + + eglInitialize(win->src.EGL_display, &major, &minor); + + EGLint config_attribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, + #ifdef RGFW_OPENGL_ES1 + EGL_OPENGL_ES1_BIT, + #endif + #ifdef RGFW_OPENGL_ES2 + EGL_OPENGL_ES2_BIT, + #else + EGL_OPENGL_BIT, + #endif + EGL_NONE + }; + + EGLConfig config; + EGLint num_configs; + eglChooseConfig(win->src.EGL_display, config_attribs, &config, 1, &num_configs); + +#if defined(RGFW_OPENGL_ES2) || defined(RGFW_OPENGL_ES1) + eglBindAPI(EGL_OPENGL_ES_API); +#else + eglBindAPI(EGL_OPENGL_API); +#endif + + EGLint attribs[]{ + EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT, + 0, 0, 0, 0 + }; + + size_t index = 2; + RGFW_GL_ADD_ATTRIB(EGL_STENCIL_SIZE, RGFW_STENCIL); + RGFW_GL_ADD_ATTRIB(EGL_SAMPLES, RGFW_SAMPLES); + RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MAJOR_VERSION, RGFW_majorVersion); + RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MINOR_VERSION, RGFW_minorVersion); + + win->src.rSurf = eglCreateContext(win->src.EGL_display, config, globalCtx, attribs); + win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL); + + if (globalCtx == EGL_NO_CONTEXT) + RGFW_EGLglobalContext = win->src.rSurf; + + eglMakeCurrent(win->src.EGL_display, win->src.EGL_surface, win->src.EGL_surface, win->src.rSurf); + eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); + + eglSwapInterval(win->src.EGL_display, 1); + } + + void* RGFW_getProcAddress(const char* procname) { return (void*) eglGetProcAddress(procname); } + + void RGFW_closeEGL(RGFW_window* win) { + eglDestroySurface(win->src.EGL_display, win->src.EGL_surface); + eglDestroyContext(win->src.EGL_display, win->src.rSurf); + + eglTerminate(win->src.EGL_display); + } + +#endif /* RGFW_EGL */ + + /* + This is where OS specific stuff starts + */ + +#ifdef RGFW_X11 +#include +#include +#include +#include + +#include /* for converting keycode to string */ +#include /* for hiding */ + +#include /* for data limits (mainly used in drag and drop functions) */ +#include + +#ifdef __linux__ +#include +#endif + + /*atoms needed for drag and drop*/ + Atom XdndAware, XdndTypeList, XdndSelection, XdndEnter, XdndPosition, XdndStatus, XdndLeave, XdndDrop, XdndFinished, XdndActionCopy, XdndActionMove, XdndActionLink, XdndActionAsk, XdndActionPrivate; + + Atom wm_delete_window = 0; + +#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 +#ifdef RGFW_OPENGL + typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); +#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 + + u32 RGFW_windowsOpen = 0; + +#ifdef RGFW_OPENGL + void* RGFW_getProcAddress(const char* procname) { return (void*) glXGetProcAddress((GLubyte*) procname); } +#endif + + RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) { +#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) + if (X11Cursorhandle == NULL) { +#if defined(__CYGWIN__) + X11Cursorhandle = dlopen("libXcursor-1.so", RTLD_LAZY | RTLD_LOCAL); +#elif defined(__OpenBSD__) || defined(__NetBSD__) + X11Cursorhandle = dlopen("libXcursor.so", RTLD_LAZY | RTLD_LOCAL); +#else + X11Cursorhandle = dlopen("libXcursor.so.1", RTLD_LAZY | RTLD_LOCAL); +#endif + + XcursorImageCreateSrc = (PFN_XcursorImageCreate) dlsym(X11Cursorhandle, "XcursorImageCreate"); + XcursorImageDestroySrc = (PFN_XcursorImageDestroy) dlsym(X11Cursorhandle, "XcursorImageDestroy"); + XcursorImageLoadCursorSrc = (PFN_XcursorImageLoadCursor) dlsym(X11Cursorhandle, "XcursorImageLoadCursor"); + } +#endif + + XInitThreads(); /* init X11 threading*/ + + if (args & RGFW_OPENGL_SOFTWARE) + setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1); + + RGFW_window* win = RGFW_window_basic_init(rect, args); + + u64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask; /* X11 events accepted*/ + +#ifdef RGFW_OPENGL + u32* visual_attribs = RGFW_initAttribs(args & RGFW_OPENGL_SOFTWARE); + i32 fbcount; + GLXFBConfig* fbc = glXChooseFBConfig((Display*) win->src.display, DefaultScreen(win->src.display), (i32*) visual_attribs, &fbcount); + + i32 best_fbc = -1; + + if (fbcount == 0) { + printf("Failed to find any valid GLX configs\n"); + return NULL; + } + + u32 i; + for (i = 0; i < fbcount; i++) { + XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) win->src.display, fbc[i]); + if (vi == NULL) + continue; + + XFree(vi); + + i32 samp_buf, samples; + glXGetFBConfigAttrib((Display*) win->src.display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf); + glXGetFBConfigAttrib((Display*) win->src.display, fbc[i], GLX_SAMPLES, &samples); + if ((best_fbc < 0 || samp_buf) && (samples == RGFW_SAMPLES)) + best_fbc = i; + } + + if (best_fbc == -1) { + printf("Failed to get a valid GLX visual\n"); + return NULL; + } + + GLXFBConfig bestFbc = fbc[best_fbc]; + + /* Get a visual */ + XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) win->src.display, bestFbc); + + XFree(fbc); + + u32 valuemask = CWBorderPixel | CWColormap; +#else + XVisualInfo* vi = (XVisualInfo*) malloc(sizeof(XVisualInfo)); + vi->screen = DefaultScreen((Display*) win->src.display); + vi->visual = DefaultVisual((Display*) win->src.display, vi->screen); + + vi->depth = 0; + u32 valuemask = 0; +#endif + + /* make X window attrubutes*/ + XSetWindowAttributes swa; + Colormap cmap; + + swa.colormap = cmap = XCreateColormap((Display*) win->src.display, + RootWindow(win->src.display, vi->screen), + vi->visual, AllocNone); + + swa.background_pixmap = None; + swa.border_pixel = 0; + swa.event_mask = event_mask; + + /* create the window*/ + win->src.window = XCreateWindow((Display*) win->src.display, RootWindow((Display*) win->src.display, vi->screen), win->r.x, win->r.y, win->r.w, win->r.h, + 0, vi->depth, InputOutput, vi->visual, + valuemask | CWEventMask, &swa); + + + XFreeColors((Display*) win->src.display, cmap, NULL, 0, 0); + if (args & RGFW_TRANSPARENT_WINDOW) + XMatchVisualInfo((Display*) win->src.display, DefaultScreen((Display*) win->src.display), 32, TrueColor, vi); /* for RGBA backgrounds*/ + + XFree(vi); + +#ifdef RGFW_OPENGL + i32 context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 }; + context_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB; + context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + + if (RGFW_majorVersion || RGFW_minorVersion) { + context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB; + context_attribs[3] = RGFW_majorVersion; + context_attribs[4] = GLX_CONTEXT_MINOR_VERSION_ARB; + context_attribs[5] = RGFW_minorVersion; + } + + glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; + glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) + glXGetProcAddressARB((GLubyte*) "glXCreateContextAttribsARB"); + + GLXContext ctx = NULL; + + if (RGFW_root != NULL) + ctx = RGFW_root->src.rSurf; + + win->src.rSurf = glXCreateContextAttribsARB((Display*) win->src.display, bestFbc, ctx, True, context_attribs); +#endif + +#ifdef RGFW_EGL + RGFW_createOpenGLContext(win); +#endif + + if (RGFW_root == NULL) + RGFW_root = win; + + RGFW_init_buffer(win); + +#ifdef RGFW_VULKAN + RGFW_initVulkan(win); +#endif + + if (args & RGFW_SCALE_TO_MONITOR) + RGFW_window_scaleToMonitor(win); + + if (args & RGFW_NO_RESIZE) { /* make it so the user can't resize the window*/ + XSizeHints* sh = XAllocSizeHints(); + sh->flags = (1L << 4) | (1L << 5); + sh->min_width = sh->max_width = win->r.w; + sh->min_height = sh->max_height = win->r.h; + + XSetWMSizeHints((Display*) win->src.display, (Drawable) win->src.window, sh, XA_WM_NORMAL_HINTS); + XFree(sh); + } + + if (args & RGFW_NO_BORDER) { + /* Atom vars for no-border*/ + static Atom window_type = 0; + static Atom value = 0; + + if (window_type == 0) { + window_type = XInternAtom((Display*) win->src.display, "_NET_WM_WINDOW_TYPE", False); + value = XInternAtom((Display*) win->src.display, "_NET_WM_WINDOW_TYPE_DOCK", False); + } + + XChangeProperty((Display*) win->src.display, (Drawable) win->src.window, window_type, XA_ATOM, 32, PropModeReplace, (u8*) &value, 1); /* toggle border*/ + } + + XSelectInput((Display*) win->src.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*/ + if (wm_delete_window == 0) + wm_delete_window = XInternAtom((Display*) win->src.display, "WM_DELETE_WINDOW", 1); + + XSetWMProtocols((Display*) win->src.display, (Drawable) win->src.window, &wm_delete_window, 1); + + /* connect the context to the window*/ +#ifdef RGFW_OPENGL + glXMakeCurrent((Display*) win->src.display, (Drawable) win->src.window, (GLXContext) win->src.rSurf); +#endif + + /* set the background*/ + XStoreName((Display*) win->src.display, (Drawable) win->src.window, name); /* set the name*/ + + XMapWindow((Display*) win->src.display, (Drawable) win->src.window); /* draw the window*/ + XMoveWindow((Display*) win->src.display, (Drawable) win->src.window, win->r.x, win->r.y); /* move the window to it's proper cords*/ + + if (args & RGFW_ALLOW_DND) { /* init drag and drop atoms and turn on drag and drop for this window */ + win->src.winArgs |= RGFW_ALLOW_DND; + + XdndAware = XInternAtom((Display*) win->src.display, "XdndAware", False); + XdndTypeList = XInternAtom((Display*) win->src.display, "XdndTypeList", False); + XdndSelection = XInternAtom((Display*) win->src.display, "XdndSelection", False); + + /* client messages */ + XdndEnter = XInternAtom((Display*) win->src.display, "XdndEnter", False); + XdndPosition = XInternAtom((Display*) win->src.display, "XdndPosition", False); + XdndStatus = XInternAtom((Display*) win->src.display, "XdndStatus", False); + XdndLeave = XInternAtom((Display*) win->src.display, "XdndLeave", False); + XdndDrop = XInternAtom((Display*) win->src.display, "XdndDrop", False); + XdndFinished = XInternAtom((Display*) win->src.display, "XdndFinished", False); + + /* actions */ + XdndActionCopy = XInternAtom((Display*) win->src.display, "XdndActionCopy", False); + XdndActionMove = XInternAtom((Display*) win->src.display, "XdndActionMove", False); + XdndActionLink = XInternAtom((Display*) win->src.display, "XdndActionLink", False); + XdndActionAsk = XInternAtom((Display*) win->src.display, "XdndActionAsk", False); + XdndActionPrivate = XInternAtom((Display*) win->src.display, "XdndActionPrivate", False); + const Atom version = 5; + + XChangeProperty((Display*) win->src.display, (Window) win->src.window, + XdndAware, 4, 32, + PropModeReplace, (u8*) &version, 1); /* turns on drag and drop */ + } + + RGFW_window_setMouseDefault(win); + + RGFW_windowsOpen++; + + return win; /*return newly created window*/ + } + + RGFW_area RGFW_getScreenSize(void) { + assert(RGFW_root != NULL); + + Screen* scrn = DefaultScreenOfDisplay((Display*) RGFW_root->src.display); + return RGFW_AREA(scrn->width, scrn->height); + } + + RGFW_vector RGFW_getGlobalMousePoint(void) { + assert(RGFW_root != NULL); + + RGFW_vector RGFWMouse; + + i32 x, y; + u32 z; + Window window1, window2; + XQueryPointer((Display*) RGFW_root->src.display, XDefaultRootWindow((Display*) RGFW_root->src.display), &window1, &window2, &x, &RGFWMouse.x, &RGFWMouse.y, &y, &z); + + return RGFWMouse; + } + + typedef struct XDND { + long source, version; + i32 format; + } XDND; /* data structure for xdnd events */ + XDND xdnd; + + int xAxis = 0, yAxis = 0; + + RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) { + assert(win != NULL); + + win->event.type = 0; + +#ifdef __linux__ + { + u8 i; + for (i = 0; i < win->src.joystickCount; i++) { + struct js_event e; + + + if (!win->src.joysticks[i]) + continue; + + i32 flags = fcntl(win->src.joysticks[i], F_GETFL, 0); + fcntl(win->src.joysticks[i], F_SETFL, flags | O_NONBLOCK); + + ssize_t bytes; + while ((bytes = read(win->src.joysticks[i], &e, sizeof(e))) > 0) { + switch (e.type) { + case JS_EVENT_BUTTON: + win->event.type = e.value ? RGFW_jsButtonPressed : RGFW_jsButtonReleased; + win->event.button = e.number; + win->src.jsPressed[i][e.number] = e.value; + return &win->event; + case JS_EVENT_AXIS: + ioctl(win->src.joysticks[i], JSIOCGAXES, &win->event.axisesCount); + + if ((e.number == 0 || e.number % 2) && e.number != 1) + xAxis = e.value; + else + yAxis = e.value; + + win->event.axis[e.number / 2].x = xAxis; + win->event.axis[e.number / 2].y = yAxis; + win->event.type = RGFW_jsAxisMove; + win->event.joystick = e.number / 2; + return &win->event; + + default: break; + } + } + } + } +#endif + + XEvent E; /* raw X11 event */ + + /* if there is no unread qued events, get a new one */ + if (XEventsQueued((Display*) win->src.display, QueuedAlready) + XEventsQueued((Display*) win->src.display, QueuedAfterReading) && win->event.type != RGFW_quit) + XNextEvent((Display*) win->src.display, &E); + else { + return NULL; + } + + u32 i; + + if (win->event.droppedFilesCount) { + for (i = 0; i < win->event.droppedFilesCount; i++) + win->event.droppedFiles[i][0] = '\0'; + } + + win->event.droppedFilesCount = 0; + win->event.type = 0; + + + switch (E.type) { + case KeyPress: + case KeyRelease: + /* check if it's a real key release */ + if (E.type == KeyRelease && XEventsQueued((Display*) win->src.display, QueuedAfterReading)) { /* get next event if there is one*/ + XEvent NE; + XPeekEvent((Display*) win->src.display, &NE); + + if (E.xkey.time == NE.xkey.time && E.xkey.keycode == NE.xkey.keycode) /* check if the current and next are both the same*/ + break; + } + + /* set event key data */ + win->event.keyCode = XkbKeycodeToKeysym((Display*) win->src.display, E.xkey.keycode, 0, E.xkey.state & ShiftMask ? 1 : 0); + win->event.keyName = XKeysymToString(win->event.keyCode); /* convert to string */ + + /* get keystate data */ + win->event.type = (E.type == KeyPress) ? RGFW_keyPressed : RGFW_keyReleased; + + if (win->event.type == RGFW_keyReleased) { + if (win->event.keyCode == XK_Caps_Lock && win->event.lockState & RGFW_CAPSLOCK) + win->event.lockState ^= RGFW_CAPSLOCK; + else if (win->event.keyCode == XK_Caps_Lock) + win->event.lockState |= RGFW_CAPSLOCK; + + else if (win->event.keyCode == XK_Num_Lock && win->event.lockState & RGFW_NUMLOCK) + win->event.lockState ^= RGFW_NUMLOCK; + else if (win->event.keyCode == XK_Num_Lock) + win->event.lockState |= RGFW_NUMLOCK; + } + break; + + case ButtonPress: + case ButtonRelease: + win->event.type = (E.type == ButtonPress) ? RGFW_mouseButtonPressed : RGFW_mouseButtonReleased; + + if (win->event.button == RGFW_mouseScrollUp) { + win->event.scroll = 1; + } + else if (win->event.button == RGFW_mouseScrollDown) { + win->event.scroll = -1; + } + + win->event.button = E.xbutton.button; + break; + + case MotionNotify: + win->event.point.x = E.xmotion.x; + win->event.point.y = E.xmotion.y; + win->event.type = RGFW_mousePosChanged; + break; + + case ClientMessage: + /* if the client closed the window*/ + if (E.xclient.data.l[0] == (i64) wm_delete_window) { + win->event.type = RGFW_quit; + break; + } + + /* + much of this event (drag and drop code) is source from glfw + */ + + if ((win->src.winArgs & RGFW_ALLOW_DND) == 0) + break; + + u8 formFree = 0; + if (E.xclient.message_type == XdndEnter) { + u64 count; + Atom* formats = (Atom*) 0; + Bool list = E.xclient.data.l[1] & 1; + + xdnd.source = E.xclient.data.l[0]; + xdnd.version = E.xclient.data.l[1] >> 24; + xdnd.format = None; + + if (xdnd.version > 5) + break; + + if (list) { + Atom actualType; + i32 actualFormat; + u64 bytesAfter; + + XGetWindowProperty((Display*) win->src.display, + xdnd.source, + XdndTypeList, + 0, + LONG_MAX, + False, + 4, + &actualType, + &actualFormat, + &count, + &bytesAfter, + (u8**) &formats); + } else { + formats = (Atom*) RGFW_MALLOC(E.xclient.data.l[2] + E.xclient.data.l[3] + E.xclient.data.l[4]); + formFree = 1; + + count = 0; + + if (E.xclient.data.l[2] != None) + formats[count++] = E.xclient.data.l[2]; + if (E.xclient.data.l[3] != None) + formats[count++] = E.xclient.data.l[3]; + if (E.xclient.data.l[4] != None) + formats[count++] = E.xclient.data.l[4]; + } + + u32 i; + for (i = 0; i < count; i++) { + char* name = XGetAtomName((Display*) win->src.display, formats[i]); + + char* links[2] = { (char*) (const char*) "text/uri-list", (char*) (const char*) "text/plain" }; + for (; 1; name++) { + u32 j; + for (j = 0; j < 2; j++) { + if (*links[j] != *name) { + links[j] = (char*) (const char*) "\1"; + continue; + } + + if (*links[j] == '\0' && *name == '\0') + xdnd.format = formats[i]; + + if (*links[j] != '\0' && *links[j] != '\1') + links[j]++; + } + + if (*name == '\0') + break; + } + } + + if (list && formats) { + XFree(formats); + formats = (Atom*) 0; + } else if (formFree && formats != (Atom*) 0) { + RGFW_FREE(formats); + + formats = (Atom*) 0; + formFree = 1; + } + + break; + } + 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; + + if (xdnd.version > 5) + break; + + XTranslateCoordinates((Display*) win->src.display, + XDefaultRootWindow((Display*) win->src.display), + (Window) win->src.window, + xabs, yabs, + &xpos, &ypos, + &dummy); + + win->event.point.x = xpos; + win->event.point.y = ypos; + + XEvent reply = { ClientMessage }; + reply.xclient.window = xdnd.source; + 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 (xdnd.format) { + reply.xclient.data.l[1] = 1; + if (xdnd.version >= 2) + reply.xclient.data.l[4] = XdndActionCopy; + } + + XSendEvent((Display*) win->src.display, xdnd.source, False, NoEventMask, &reply); + XFlush((Display*) win->src.display); + break; + } + + if (E.xclient.message_type != XdndDrop) + break; + + if (xdnd.version > 5) + break; + + win->event.type = RGFW_dnd_init; + + if (xdnd.format) { + Time time = CurrentTime; + + if (xdnd.version >= 1) + time = E.xclient.data.l[2]; + + XConvertSelection((Display*) win->src.display, + XdndSelection, + xdnd.format, + XdndSelection, + (Window) win->src.window, + time); + } else if (xdnd.version >= 2) { + XEvent reply = { ClientMessage }; + reply.xclient.window = xdnd.source; + 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((Display*) win->src.display, xdnd.source, + False, NoEventMask, &reply); + XFlush((Display*) win->src.display); + } + break; + case SelectionNotify: + /* this is only for checking for xdnd drops */ + if (E.xselection.property != XdndSelection || !(win->src.winArgs | RGFW_ALLOW_DND)) + break; + + char* data; + u64 result; + + Atom actualType; + i32 actualFormat; + u64 bytesAfter; + + XGetWindowProperty((Display*) win->src.display, E.xselection.requestor, E.xselection.property, 0, LONG_MAX, False, E.xselection.target, &actualType, &actualFormat, &result, &bytesAfter, (u8**) &data); + + if (result == 0) + break; + + /* + SOURCED FROM GLFW _glfwParseUriList + Copyright (c) 2002-2006 Marcus Geelnard + Copyright (c) 2006-2019 Camilla Löwy + */ + + const char* prefix = "file://"; + + char* line; + + win->event.droppedFilesCount = 0; + + win->event.type = RGFW_dnd; + + while ((line = strtok(data, "\r\n"))) { + char path[RGFW_MAX_PATH]; + + 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; + } + + win->event.droppedFilesCount++; + + size_t index = 0; + while (*line) { + if (line[0] == '%' && line[1] && line[2]) { + const char digits[3] = { line[1], line[2], '\0' }; + path[index] = (char) strtol(digits, NULL, 16); + line += 2; + } else + path[index] = *line; + + index++; + line++; + } + + strcpy(win->event.droppedFiles[win->event.droppedFilesCount - 1], path); + } + + if (data) + XFree(data); + + if (xdnd.version >= 2) { + XEvent reply = { ClientMessage }; + reply.xclient.window = xdnd.source; + reply.xclient.message_type = XdndFinished; + reply.xclient.format = 32; + reply.xclient.data.l[0] = (long) win->src.window; + reply.xclient.data.l[1] = result; + reply.xclient.data.l[2] = XdndActionCopy; + + XSendEvent((Display*) win->src.display, xdnd.source, False, NoEventMask, &reply); + XFlush((Display*) win->src.display); + } + + break; + + case FocusIn: + win->event.inFocus = 1; + + XKeyboardState keystate; + XGetKeyboardControl((Display*) win->src.display, &keystate); + win->event.lockState = keystate.led_mask; + + break; + case FocusOut: + win->event.inFocus = 0; + RGFW_window_setMouseDefault(win); + break; + case ConfigureNotify: { +#ifndef RGFW_NO_X11_WINDOW_ATTRIB + XWindowAttributes a; + XGetWindowAttributes((Display*) win->src.display, (Window) win->src.window, &a); + win->r = RGFW_RECT(E.xconfigure.x, E.xconfigure.y, E.xconfigure.width, E.xconfigure.height); +#endif + + win->event.type = RGFW_windowAttribsChange; + break; + } + default: { + break; + } + } + + if (win->event.inFocus && (win->src.winArgs & RGFW_MOUSE_CHANGED)) { + XDefineCursor((Display*) win->src.display, (Window) win->src.window, (Cursor) win->src.cursor); + + win->src.winArgs &= ~RGFW_MOUSE_CHANGED; + } + + RGFW_vector mouse = RGFW_getGlobalMousePoint(); + if (win->src.winArgs & RGFW_HOLD_MOUSE && win->event.inFocus && win->event.type == RGFW_mousePosChanged) { + RGFW_window_moveMouse(win, RGFW_VECTOR(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2))); + + if (XEventsQueued((Display*) win->src.display, QueuedAfterReading) <= 1) + XSync(win->src.display, True); + } + + + XFlush((Display*) win->src.display); + + if (win->event.type) + return &win->event; + else + return NULL; + } + + void RGFW_window_close(RGFW_window* win) { + assert(win != NULL); + +#ifdef RGFW_VULKAN + for (int i = 0; i < win->src.image_count; i++) { + vkDestroyImageView(RGFW_vulkan_info.device, win->src.swapchain_image_views[i], NULL); + } + + vkDestroySwapchainKHR(RGFW_vulkan_info.device, win->src.swapchain, NULL); + vkDestroySurfaceKHR(RGFW_vulkan_info.instance, win->src.rSurf, NULL); + free(win->src.swapchain_image_views); + free(win->src.swapchain_images); +#endif + +#ifdef RGFW_EGL + RGFW_closeEGL(win); +#endif + + XFreeCursor((Display*) win->src.display, (Cursor) win->src.cursor); + +#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + if (win->buffer != NULL) { + XDestroyImage((XImage*) win->src.bitmap); + } +#endif + + if ((Display*) win->src.display) { +#ifdef RGFW_OPENGL + glXDestroyContext((Display*) win->src.display, win->src.rSurf); +#endif + + if (win == RGFW_root) + RGFW_root = NULL; + + if ((Drawable) win->src.window) + XDestroyWindow((Display*) win->src.display, (Drawable) win->src.window); /* close the window*/ + + if (win->src.display) + XCloseDisplay((Display*) win->src.display); /* kill the display*/ + } + +#ifdef RGFW_ALLOC_DROPFILES + { + u32 i; + for (i = 0; i < RGFW_MAX_DROPS; i++) + RGFW_FREE(win->event.droppedFiles[i]); + + + RGFW_FREE(win->event.droppedFiles); + } +#endif + + RGFW_windowsOpen--; +#if !defined(RGFW_NO_X11_CURSOR_PRELOAD) && !defined(RGFW_NO_X11_CURSOR) + if (X11Cursorhandle != NULL && RGFW_windowsOpen <= 0) { + dlclose(X11Cursorhandle); + + X11Cursorhandle = NULL; + } +#endif + + /* set cleared display / window to NULL for error checking */ + win->src.display = (Display*) 0; + win->src.window = (Window) 0; + + u8 i; + for (i = 0; i < win->src.joystickCount; i++) + close(win->src.joysticks[i]); + + RGFW_FREE(win); /* free collected window data */ + } + + void RGFW_window_move(RGFW_window* win, RGFW_vector v) { + assert(win != NULL); + win->r.x = v.x; + win->r.y = v.y; + + XMoveWindow((Display*) win->src.display, (Window) win->src.window, v.x, v.y); + } + + + void RGFW_window_resize(RGFW_window* win, RGFW_area a) { + assert(win != NULL); + win->r.w = a.w; + win->r.h = a.h; + + XResizeWindow((Display*) win->src.display, (Window) win->src.window, a.w, a.h); + } + + void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { + assert(win != NULL); + + XSizeHints hints; + long flags; + + XGetWMNormalHints(win->src.display, (Window) win->src.window, &hints, &flags); + + hints.flags |= PMinSize; + + hints.min_width = a.w; + hints.min_height = a.h; + + XSetWMNormalHints(win->src.display, (Window) win->src.window, &hints); + } + + void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { + assert(win != NULL); + + XSizeHints hints; + long flags; + + XGetWMNormalHints(win->src.display, (Window) win->src.window, &hints, &flags); + + hints.flags |= PMaxSize; + + hints.max_width = a.w; + hints.max_height = a.h; + + XSetWMNormalHints(win->src.display, (Window) win->src.window, &hints); + } + + + void RGFW_window_minimize(RGFW_window* win) { + assert(win != NULL); + + XIconifyWindow(win->src.display, (Window) win->src.window, DefaultScreen(win->src.display)); + XFlush(win->src.display); + } + + void RGFW_window_restore(RGFW_window* win) { + assert(win != NULL); + + XMapWindow(win->src.display, (Window) win->src.window); + XFlush(win->src.display); + } + + void RGFW_window_setName(RGFW_window* win, char* name) { + assert(win != NULL); + + XStoreName((Display*) win->src.display, (Window) win->src.window, name); + } + + /* + the majority function is sourced from GLFW + */ + + void RGFW_window_setIcon(RGFW_window* win, u8* icon, RGFW_area a, i32 channels) { + assert(win != NULL); + + i32 longCount = 2 + a.w * a.h; + + u64* X11Icon = (u64*) RGFW_MALLOC(longCount * sizeof(u64)); + u64* target = X11Icon; + + *target++ = a.w; + *target++ = a.h; + + u32 i; + + for (i = 0; i < a.w * a.h; i++) { + if (channels == 3) + *target++ = ((icon[i * 3 + 0]) << 16) | + ((icon[i * 3 + 1]) << 8) | + ((icon[i * 3 + 2]) << 0) | + (0xFF << 24); + + else if (channels == 4) + *target++ = ((icon[i * 4 + 0]) << 16) | + ((icon[i * 4 + 1]) << 8) | + ((icon[i * 4 + 2]) << 0) | + ((icon[i * 4 + 3]) << 24); + } + + static Atom NET_WM_ICON = 0; + if (NET_WM_ICON == 0) + NET_WM_ICON = XInternAtom((Display*) win->src.display, "_NET_WM_ICON", False); + + XChangeProperty((Display*) win->src.display, (Window) win->src.window, + NET_WM_ICON, + 6, 32, + PropModeReplace, + (u8*) X11Icon, + longCount); + + RGFW_FREE(X11Icon); + + XFlush((Display*) win->src.display); + } + + void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) { + assert(win != NULL); + +#ifndef RGFW_NO_X11_CURSOR + /* free the previous cursor */ + if (win->src.cursor && win->src.cursor != -1) + XFreeCursor((Display*) win->src.display, (Cursor) win->src.cursor); + + XcursorImage* native = XcursorImageCreate(a.w, a.h); + native->xhot = 0; + native->yhot = 0; + + u8* source = (u8*) image; + XcursorPixel* target = native->pixels; + + u32 i; + for (i = 0; i < a.w * a.h; i++, target++, source += 4) { + u8 alpha = 0xFF; + if (channels == 4) + alpha = source[3]; + + *target = (alpha << 24) | (((source[0] * alpha) / 255) << 16) | (((source[1] * alpha) / 255) << 8) | (((source[2] * alpha) / 255) << 0); + } + + win->src.winArgs |= RGFW_MOUSE_CHANGED; + win->src.cursor = XcursorImageLoadCursor((Display*) win->src.display, native); + + XcursorImageDestroy(native); +#endif + } + + void RGFW_window_moveMouse(RGFW_window* win, RGFW_vector v) { + assert(win != NULL); + + XEvent event; + XQueryPointer(win->src.display, DefaultRootWindow(win->src.display), + &event.xbutton.root, &event.xbutton.window, + &event.xbutton.x_root, &event.xbutton.y_root, + &event.xbutton.x, &event.xbutton.y, + &event.xbutton.state); + + if (event.xbutton.x == v.x && event.xbutton.y == v.y) + return; + + XWarpPointer(win->src.display, None, None, 0, 0, 0, 0, -event.xbutton.x, -event.xbutton.y); + XWarpPointer(win->src.display, None, None, 0, 0, 0, 0, v.x, v.y); + } + + RGFWDEF void RGFW_window_disableMouse(RGFW_window* win) { + + } + + void RGFW_window_setMouseDefault(RGFW_window* win) { + RGFW_window_setMouseStandard(win, XC_left_ptr); + } + + void RGFW_window_setMouseStandard(RGFW_window* win, i32 mouse) { + assert(win != NULL); + + /* free the previous cursor */ + if (win->src.cursor && win->src.cursor != -1) + XFreeCursor((Display*) win->src.display, (Cursor) win->src.cursor); + + win->src.winArgs |= RGFW_MOUSE_CHANGED; + win->src.cursor = XCreateFontCursor((Display*) win->src.display, mouse); + } + + void RGFW_window_hide(RGFW_window* win) { + XMapWindow(win->src.display, win->src.window); + } + + void RGFW_window_show(RGFW_window* win) { + XUnmapWindow(win->src.display, win->src.window); + } + + /* + the majority function is sourced from GLFW + */ + char* RGFW_readClipboard(size_t* size) { + static Atom UTF8 = 0; + if (UTF8 == 0) + UTF8 = XInternAtom(RGFW_root->src.display, "UTF8_STRING", True); + + XEvent event; + int format; + unsigned long N, sizeN; + char* data, * s = NULL; + Atom target; + Atom CLIPBOARD = 0, XSEL_DATA = 0; + + if (CLIPBOARD == 0) { + CLIPBOARD = XInternAtom(RGFW_root->src.display, "CLIPBOARD", 0); + XSEL_DATA = XInternAtom(RGFW_root->src.display, "XSEL_DATA", 0); + } + + XConvertSelection(RGFW_root->src.display, CLIPBOARD, UTF8, XSEL_DATA, RGFW_root->src.window, CurrentTime); + XSync(RGFW_root->src.display, 0); + XNextEvent(RGFW_root->src.display, &event); + + if (event.type != SelectionNotify || event.xselection.selection != CLIPBOARD || event.xselection.property == 0) + return NULL; + + XGetWindowProperty(event.xselection.display, event.xselection.requestor, + event.xselection.property, 0L, (~0L), 0, AnyPropertyType, &target, + &format, &sizeN, &N, (unsigned char**) &data); + + if (target == UTF8 || target == XA_STRING) { + s = strndup(data, sizeN); + XFree(data); + } + + XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property); + + if (s != NULL && size != NULL) + *size = sizeN; + + return s; + } + + /* + almost all of this function is sourced from GLFW + */ + void RGFW_writeClipboard(const char* text, u32 textLen) { + static Atom CLIPBOARD = 0, + UTF8_STRING = 0, + SAVE_TARGETS = 0, + TARGETS = 0, + MULTIPLE = 0, + ATOM_PAIR = 0, + CLIPBOARD_MANAGER = 0; + + if (CLIPBOARD == 0) { + CLIPBOARD = XInternAtom((Display*) RGFW_root->src.display, "CLIPBOARD", False); + UTF8_STRING = XInternAtom((Display*) RGFW_root->src.display, "UTF8_STRING", False); + SAVE_TARGETS = XInternAtom((Display*) RGFW_root->src.display, "SAVE_TARGETS", False); + TARGETS = XInternAtom((Display*) RGFW_root->src.display, "TARGETS", False); + MULTIPLE = XInternAtom((Display*) RGFW_root->src.display, "MULTIPLE", False); + ATOM_PAIR = XInternAtom((Display*) RGFW_root->src.display, "ATOM_PAIR", False); + CLIPBOARD_MANAGER = XInternAtom((Display*) RGFW_root->src.display, "CLIPBOARD_MANAGER", False); + } + + XSetSelectionOwner((Display*) RGFW_root->src.display, CLIPBOARD, (Window) RGFW_root->src.window, CurrentTime); + + XConvertSelection((Display*) RGFW_root->src.display, CLIPBOARD_MANAGER, SAVE_TARGETS, None, (Window) RGFW_root->src.window, CurrentTime); + + for (;;) { + XEvent event; + + XNextEvent((Display*) RGFW_root->src.display, &event); + if (event.type != SelectionRequest) + return; + + const XSelectionRequestEvent* request = &event.xselectionrequest; + + XEvent reply = { SelectionNotify }; + + char* selectionString = NULL; + const Atom formats[] = { UTF8_STRING, XA_STRING }; + const i32 formatCount = sizeof(formats) / sizeof(formats[0]); + + selectionString = (char*) text; + + if (request->target == TARGETS) { + const Atom targets[] = { TARGETS, + MULTIPLE, + UTF8_STRING, + XA_STRING }; + + XChangeProperty((Display*) RGFW_root->src.display, + request->requestor, + request->property, + 4, + 32, + PropModeReplace, + (u8*) targets, + sizeof(targets) / sizeof(targets[0])); + + reply.xselection.property = request->property; + } + + if (request->target == MULTIPLE) { + + Atom* targets; + + Atom actualType; + i32 actualFormat; + u64 count, bytesAfter; + + XGetWindowProperty((Display*) RGFW_root->src.display, request->requestor, request->property, 0, LONG_MAX, False, ATOM_PAIR, &actualType, &actualFormat, &count, &bytesAfter, (u8**) &targets); + + u64 i; + for (i = 0; i < count; i += 2) { + i32 j; + + for (j = 0; j < formatCount; j++) { + if (targets[i] == formats[j]) + break; + } + + if (j < formatCount) + { + XChangeProperty((Display*) RGFW_root->src.display, + request->requestor, + targets[i + 1], + targets[i], + 8, + PropModeReplace, + (u8*) selectionString, + textLen); + } else + targets[i + 1] = None; + } + + XChangeProperty((Display*) RGFW_root->src.display, + request->requestor, + request->property, + ATOM_PAIR, + 32, + PropModeReplace, + (u8*) targets, + count); + + XFree(targets); + + 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((Display*) RGFW_root->src.display, request->requestor, False, 0, &reply); + } + } + + u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber) { + assert(win != NULL); + +#ifdef __linux__ + char file[15]; + sprintf(file, "/dev/input/js%i", jsNumber); + + return RGFW_registerJoystickF(win, file); +#endif + } + + u16 RGFW_registerJoystickF(RGFW_window* win, char* file) { + assert(win != NULL); + +#ifdef __linux__ + + i32 js = open(file, O_RDONLY); + + if (js && win->src.joystickCount < 4) { + win->src.joystickCount++; + + win->src.joysticks[win->src.joystickCount - 1] = open(file, O_RDONLY); + + u8 i; + for (i = 0; i < 16; i++) + win->src.jsPressed[win->src.joystickCount - 1][i] = 0; + + } + + else { +#ifdef RGFW_PRINT_ERRORS + RGFW_error = 1; + fprintf(stderr, "Error RGFW_registerJoystickF : Cannot open file %s\n", file); +#endif + } + + return win->src.joystickCount - 1; +#endif + } + + u8 RGFW_window_isFullscreen(RGFW_window* win) { + assert(win != NULL); + + XWindowAttributes windowAttributes; + XGetWindowAttributes(win->src.display, (Window) win->src.window, &windowAttributes); + + /* check if the window is visable */ + if (windowAttributes.map_state != IsViewable) + return 0; + + /* check if the window covers the full screen */ + return (windowAttributes.x == 0 && windowAttributes.y == 0 && + windowAttributes.width == XDisplayWidth(win->src.display, DefaultScreen(win->src.display)) && + windowAttributes.height == XDisplayHeight(win->src.display, DefaultScreen(win->src.display))); + } + + u8 RGFW_window_isHidden(RGFW_window* win) { + assert(win != NULL); + + XWindowAttributes windowAttributes; + XGetWindowAttributes(win->src.display, (Window) win->src.window, &windowAttributes); + + return (windowAttributes.map_state == IsUnmapped && !RGFW_window_isMinimized(win)); + } + + u8 RGFW_window_isMinimized(RGFW_window* win) { + assert(win != NULL); + + static Atom prop = 0; + if (prop == 0) + prop = XInternAtom(win->src.display, "WM_STATE", False); + + Atom actual_type; + i32 actual_format; + u64 nitems, bytes_after; + unsigned char* prop_data; + + i16 status = XGetWindowProperty(win->src.display, (Window) win->src.window, prop, 0, 2, False, + AnyPropertyType, &actual_type, &actual_format, + &nitems, &bytes_after, &prop_data); + + if (status == Success && nitems >= 1 && *((int*) prop_data) == IconicState) { + XFree(prop_data); + return 1; + } + + if (prop_data != NULL) + XFree(prop_data); + + return 0; + } + + u8 RGFW_window_isMaximized(RGFW_window* win) { + assert(win != NULL); + + static Atom net_wm_state = 0; + static Atom net_wm_state_maximized_horz = 0; + static Atom net_wm_state_maximized_vert = 0; + + if (net_wm_state == 0) { + net_wm_state = XInternAtom(win->src.display, "_NET_WM_STATE", False); + net_wm_state_maximized_vert = XInternAtom(win->src.display, "_NET_WM_STATE_MAXIMIZED_VERT", False); + net_wm_state_maximized_horz = XInternAtom(win->src.display, "_NET_WM_STATE_MAXIMIZED_HORZ", False); + } + + Atom actual_type; + i32 actual_format; + u64 nitems, bytes_after; + unsigned char* prop_data; + + i16 status = XGetWindowProperty(win->src.display, (Window) 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 0; + } + + Atom* atoms = (Atom*) prop_data; + u64 i; + for (i = 0; i < nitems; ++i) { + if (atoms[i] == net_wm_state_maximized_horz || + atoms[i] == net_wm_state_maximized_vert) { + XFree(prop_data); + return 1; + } + } + + return 0; + } + + static void XGetSystemContentScale(Display* display, float* xscale, float* yscale) { + float xdpi = 96.f, ydpi = 96.f; + +#ifndef RGFW_NO_DPI + char* rms = XResourceManagerString(display); + XrmDatabase db = NULL; + + if (rms && db) + db = XrmGetStringDatabase(rms); + + if (db == 0) { + *xscale = xdpi / 96.f; + *yscale = ydpi / 96.f; + return; + } + + XrmValue value; + char* type = NULL; + + if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value) && type && strcmp(type, "String") == 0) + xdpi = ydpi = atof(value.addr); + XrmDestroyDatabase(db); +#endif + + * xscale = xdpi / 96.f; + *yscale = ydpi / 96.f; + } + + RGFW_monitor RGFW_XCreateMonitor(i32 screen) { + RGFW_monitor monitor; + + Display* display = XOpenDisplay(NULL); + + monitor.rect = RGFW_RECT(0, 0, DisplayWidth(display, screen), DisplayHeight(display, screen)); + monitor.physW = (monitor.rect.w * 25.4f / 96.f); + monitor.physH = (monitor.rect.h * 25.4f / 96.f); + + strncpy(monitor.name, DisplayString(display), 128); + + XGetSystemContentScale(display, &monitor.scaleX, &monitor.scaleY); + + XRRScreenResources* sr = XRRGetScreenResourcesCurrent(display, RootWindow(display, screen)); + + XRRCrtcInfo* ci = NULL; + int crtc = 0; + + if (sr->ncrtc > crtc) { + ci = XRRGetCrtcInfo(display, sr, sr->crtcs[crtc]); + } + + if (ci == NULL) { + XRRFreeScreenResources(sr); + XCloseDisplay(display); + return monitor; + } + + monitor.rect.x = ci->x; + monitor.rect.y = ci->y; + + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(sr); + + XCloseDisplay(display); + + return monitor; + } + + RGFW_monitor RGFW_monitors[6]; + RGFW_monitor* RGFW_getMonitors(void) { + size_t i; + for (i = 0; i < ScreenCount(RGFW_root->src.display) && i < 6; i++) + RGFW_monitors[i] = RGFW_XCreateMonitor(i); + + return RGFW_monitors; + } + + RGFW_monitor RGFW_getPrimaryMonitor(void) { + assert(RGFW_root != NULL); + + i32 primary = -1; + Window root = DefaultRootWindow(RGFW_root->src.display); + XRRScreenResources* res = XRRGetScreenResources(RGFW_root->src.display, root); + + for (int i = 0; i < res->noutput; i++) { + XRROutputInfo* output_info = XRRGetOutputInfo(RGFW_root->src.display, res, res->outputs[i]); + if (output_info->connection == RR_Connected && output_info->crtc) { + XRRCrtcInfo* crtc_info = XRRGetCrtcInfo(RGFW_root->src.display, res, output_info->crtc); + if (crtc_info->mode != None && crtc_info->x == 0 && crtc_info->y == 0) { + primary = i; + XRRFreeCrtcInfo(crtc_info); + XRRFreeOutputInfo(output_info); + break; + } + XRRFreeCrtcInfo(crtc_info); + } + XRRFreeOutputInfo(output_info); + } + + XRRFreeScreenResources(res); + + return RGFW_XCreateMonitor(primary); + } + + RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { + return RGFW_XCreateMonitor(DefaultScreen(win->src.display)); + } + + char keyboard[32]; + + u8 RGFW_isPressedI(RGFW_window* win, u32 key) { + Display* d; + if (win == (RGFW_window*) 0) + d = RGFW_root->src.display; + else if (!win->event.inFocus) + return 0; + else + d = (Display*) win->src.display; + + XQueryKeymap(d, keyboard); /* query the keymap */ + + KeyCode kc2 = XKeysymToKeycode(d, key); /* convert the key to a keycode */ + return !!(keyboard[kc2 >> 3] & (1 << (kc2 & 7))); /* check if the key is pressed */ + } + +#endif + +#ifdef RGFW_WINDOWS + char* createUTF8FromWideStringWin32(const WCHAR* source); + +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 +#define GL_LEFT 0x0406 +#define GL_RIGHT 0x0407 + +#if defined(RGFW_OSMESA) && defined(RGFW_LINK_OSMESA) + + typedef void (GLAPIENTRY* PFN_OSMesaDestroyContext)(OSMesaContext); + typedef i32(GLAPIENTRY* PFN_OSMesaMakeCurrent)(OSMesaContext, void*, int, int, int); + typedef OSMesaContext(GLAPIENTRY* PFN_OSMesaCreateContext)(GLenum, OSMesaContext); + + PFN_OSMesaMakeCurrent OSMesaMakeCurrentSource; + PFN_OSMesaCreateContext OSMesaCreateContextSource; + PFN_OSMesaDestroyContext OSMesaDestroyContextSource; + +#define OSMesaCreateContext OSMesaCreateContextSource +#define OSMesaMakeCurrent OSMesaMakeCurrentSource +#define OSMesaDestroyContext OSMesaDestroyContextSource +#endif + + typedef int (*PFN_wglGetSwapIntervalEXT)(void); + PFN_wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc = NULL; +#define wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc + + + +#if defined(RGFW_DIRECTX) + RGFW_directXinfo RGFW_dxInfo; + + RGFW_directXinfo* RGFW_getDirectXInfo(void) { return &RGFW_dxInfo; } +#endif + + void* RGFWjoystickApi = NULL; + + /* these two wgl functions need to be preloaded */ + typedef HGLRC(WINAPI* wglCreateContextAttribsARB_type)(HDC hdc, HGLRC hShareContext, + const i32* attribList); + wglCreateContextAttribsARB_type wglCreateContextAttribsARB = NULL; + + /* defines for creating ARB attributes */ +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_TRANSPARENT_ARB 0x200A +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_NO_ACCELERATION_ARB 0x2025 +#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_RED_BITS_ARB 0x2015 +#define WGL_RED_SHIFT_ARB 0x2016 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_GREEN_SHIFT_ARB 0x2018 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_BLUE_SHIFT_ARB 0x201a +#define WGL_ALPHA_BITS_ARB 0x201b +#define WGL_ALPHA_SHIFT_ARB 0x201c +#define WGL_ACCUM_BITS_ARB 0x201d +#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_DEPTH_BITS_ARB 0x2022 +#define WGL_AUX_BUFFERS_ARB 0x2024 +#define WGL_STEREO_ARB 0x2012 +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_FULL_ACCELERATION_ARB 0x2027 +#define WGL_TYPE_RGBA_ARB 0x202B +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define WGL_SAMPLE_BUFFERS_ARB 0x2041 +#define WGL_SAMPLES_ARB 0x2042 +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9 + +static HMODULE wglinstance = NULL; + +#ifdef RGFW_WGL_LOAD + 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)(); + typedef HGLRC(WINAPI* PFN_wglGetCurrentContext)(); + + PFN_wglCreateContext wglCreateContextSRC; + PFN_wglDeleteContext wglDeleteContextSRC; + PFN_wglGetProcAddress wglGetProcAddressSRC; + PFN_wglMakeCurrent wglMakeCurrentSRC; + PFN_wglGetCurrentDC wglGetCurrentDCSRC; + PFN_wglGetCurrentContext wglGetCurrentContextSRC; + +#define wglCreateContext wglCreateContextSRC +#define wglDeleteContext wglDeleteContextSRC +#define wglGetProcAddress wglGetProcAddressSRC +#define wglMakeCurrent wglMakeCurrentSRC + +#define wglGetCurrentDC wglGetCurrentDCSRC +#define wglGetCurrentContext wglGetCurrentContextSRC +#endif + +#ifdef RGFW_OPENGL + void* RGFW_getProcAddress(const char* procname) { + void* proc = (void*) wglGetProcAddress(procname); + if (proc) + return proc; + + return (void*) GetProcAddress(wglinstance, procname); + } + + typedef BOOL(APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats); + static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL; +#endif + + RGFW_window RGFW_eventWindow = { {NULL} }; + + LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { + switch (message) { + case WM_MOVE: + RGFW_eventWindow.r.x = LOWORD(lParam); + RGFW_eventWindow.r.y = HIWORD(lParam); + RGFW_eventWindow.src.window = hWnd; + return DefWindowProcA(hWnd, message, wParam, lParam); + case WM_SIZE: + RGFW_eventWindow.r.w = LOWORD(lParam); + RGFW_eventWindow.r.h = HIWORD(lParam); + RGFW_eventWindow.src.window = hWnd; + return DefWindowProcA(hWnd, message, wParam, lParam); // Call DefWindowProc after handling + default: + return DefWindowProcA(hWnd, message, wParam, lParam); + } + } + + #ifndef RGFW_NO_DPI + static HMODULE RGFW_Shcore_dll = NULL; + typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*); + PFN_GetDpiForMonitor GetDpiForMonitorSRC = NULL; + #define GetDpiForMonitor GetDpiForMonitorSRC + #endif + + void RGFW_loadXInput(void) { + u32 i; + static const char* names[] = { + "xinput1_4.dll", + "xinput1_3.dll", + "xinput9_1_0.dll", + "xinput1_2.dll", + "xinput1_1.dll" + }; + + for (i = 0; i < sizeof(names) / sizeof(const char*); i++) { + RGFW_XInput_dll = LoadLibraryA(names[i]); + + if (RGFW_XInput_dll) { + XInputGetStateSRC = (PFN_XInputGetState)GetProcAddress(RGFW_XInput_dll, "XInputGetState"); + + if (XInputGetStateSRC == NULL) + printf("Failed to load XInputGetState"); + } + } + } + + RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) { + if (RGFW_XInput_dll == NULL) + RGFW_loadXInput(); + + #ifndef RGFW_NO_DPI + if (RGFW_Shcore_dll == NULL) { + RGFW_Shcore_dll = LoadLibraryA("shcore.dll"); + GetDpiForMonitorSRC = (PFN_GetDpiForMonitor)GetProcAddress(RGFW_Shcore_dll, "GetDpiForMonitor"); + } + #endif + + if (wglinstance == NULL) { + wglinstance = LoadLibraryA("opengl32.dll"); +#ifdef RGFW_WGL_LOAD + wglCreateContextSRC = (PFN_wglCreateContext) GetProcAddress(wglinstance, "wglCreateContext"); + wglDeleteContextSRC = (PFN_wglDeleteContext) GetProcAddress(wglinstance, "wglDeleteContext"); + wglGetProcAddressSRC = (PFN_wglGetProcAddress) GetProcAddress(wglinstance, "wglGetProcAddress"); + wglMakeCurrentSRC = (PFN_wglMakeCurrent) GetProcAddress(wglinstance, "wglMakeCurrent"); + wglGetCurrentDCSRC = (PFN_wglGetCurrentDC) GetProcAddress(wglinstance, "wglGetCurrentDC"); + wglGetCurrentContextSRC = (PFN_wglGetCurrentContext) GetProcAddress(wglinstance, "wglGetCurrentContext"); +#endif + } + + if (name[0] == 0) name = (char*) " "; + + RGFW_eventWindow.r = RGFW_RECT(-1, -1, -1, -1); + + RGFW_window* win = RGFW_window_basic_init(rect, args); + + if (RGFW_root == NULL) { + RGFW_root = win; + } + + HINSTANCE inh = GetModuleHandleA(NULL); + + WNDCLASSA Class = { 0 }; /* Setup the Window class. */ + Class.lpszClassName = name; + Class.hInstance = inh; + Class.hCursor = LoadCursor(NULL, IDC_ARROW); + Class.lpfnWndProc = WndProc; + + RegisterClassA(&Class); + + DWORD window_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + + RECT windowRect, clientRect; + + if (!(args & RGFW_NO_BORDER)) { + window_style |= WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_VISIBLE | WS_MINIMIZEBOX; + + if (!(args & RGFW_NO_RESIZE)) + window_style |= WS_SIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME; + } else + window_style |= WS_POPUP | WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX; + + HWND dummyWin = CreateWindowA(Class.lpszClassName, name, window_style, win->r.x, win->r.y, win->r.w, win->r.h, 0, 0, inh, 0); + + GetWindowRect(dummyWin, &windowRect); + GetClientRect(dummyWin, &clientRect); + + win->src.hOffset = (windowRect.bottom - windowRect.top) - (clientRect.bottom - clientRect.top); + win->src.window = CreateWindowA(Class.lpszClassName, name, window_style, win->r.x, win->r.y, win->r.w, win->r.h + win->src.hOffset, 0, 0, inh, 0); + + if (args & RGFW_TRANSPARENT_WINDOW) { + SetWindowLongA(win->src.window, GWL_EXSTYLE, GetWindowLongA(win->src.window, GWL_EXSTYLE) | WS_EX_LAYERED); + } + if (args & RGFW_ALLOW_DND) { + win->src.winArgs |= RGFW_ALLOW_DND; + DragAcceptFiles(win->src.window, TRUE); + } + win->src.hdc = GetDC(win->src.window); + +#ifdef RGFW_DIRECTX + assert(FAILED(CreateDXGIFactory(&__uuidof(IDXGIFactory), (void**) &RGFW_dxInfo.pFactory)) == 0); + + if (FAILED(RGFW_dxInfo.pFactory->lpVtbl->EnumAdapters(RGFW_dxInfo.pFactory, 0, &RGFW_dxInfo.pAdapter))) { + fprintf(stderr, "Failed to enumerate DXGI adapters\n"); + RGFW_dxInfo.pFactory->lpVtbl->Release(RGFW_dxInfo.pFactory); + return NULL; + } + + D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0 }; + + if (FAILED(D3D11CreateDevice(RGFW_dxInfo.pAdapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, featureLevels, 1, D3D11_SDK_VERSION, &RGFW_dxInfo.pDevice, NULL, &RGFW_dxInfo.pDeviceContext))) { + fprintf(stderr, "Failed to create Direct3D device\n"); + RGFW_dxInfo.pAdapter->lpVtbl->Release(RGFW_dxInfo.pAdapter); + RGFW_dxInfo.pFactory->lpVtbl->Release(RGFW_dxInfo.pFactory); + return NULL; + } + + DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 }; + swapChainDesc.BufferCount = 1; + swapChainDesc.BufferDesc.Width = win->r.w; + swapChainDesc.BufferDesc.Height = win->r.h; + swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.OutputWindow = win->src.window; + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.SampleDesc.Quality = 0; + swapChainDesc.Windowed = TRUE; + RGFW_dxInfo.pFactory->lpVtbl->CreateSwapChain(RGFW_dxInfo.pFactory, (IUnknown*) RGFW_dxInfo.pDevice, &swapChainDesc, &win->src.swapchain); + + ID3D11Texture2D* pBackBuffer; + win->src.swapchain->lpVtbl->GetBuffer(win->src.swapchain, 0, &__uuidof(ID3D11Texture2D), (LPVOID*) &pBackBuffer); + RGFW_dxInfo.pDevice->lpVtbl->CreateRenderTargetView(RGFW_dxInfo.pDevice, (ID3D11Resource*) pBackBuffer, NULL, &win->src.renderTargetView); + pBackBuffer->lpVtbl->Release(pBackBuffer); + + D3D11_TEXTURE2D_DESC depthStencilDesc = { 0 }; + depthStencilDesc.Width = win->r.w; + depthStencilDesc.Height = win->r.h; + depthStencilDesc.MipLevels = 1; + depthStencilDesc.ArraySize = 1; + depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; + depthStencilDesc.SampleDesc.Count = 1; + depthStencilDesc.SampleDesc.Quality = 0; + depthStencilDesc.Usage = D3D11_USAGE_DEFAULT; + depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; + + ID3D11Texture2D* pDepthStencilTexture = NULL; + RGFW_dxInfo.pDevice->lpVtbl->CreateTexture2D(RGFW_dxInfo.pDevice, &depthStencilDesc, NULL, &pDepthStencilTexture); + + D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc = { 0 }; + depthStencilViewDesc.Format = depthStencilDesc.Format; + depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; + depthStencilViewDesc.Texture2D.MipSlice = 0; + + RGFW_dxInfo.pDevice->lpVtbl->CreateDepthStencilView(RGFW_dxInfo.pDevice, (ID3D11Resource*) pDepthStencilTexture, &depthStencilViewDesc, &win->src.pDepthStencilView); + + pDepthStencilTexture->lpVtbl->Release(pDepthStencilTexture); + + RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, &win->src.renderTargetView, win->src.pDepthStencilView); +#endif + +#ifdef RGFW_OPENGL + HDC dummy_dc = GetDC(dummyWin); + + PIXELFORMATDESCRIPTOR pfd = { + .nSize = sizeof(pfd), + .nVersion = 1, + .iPixelType = PFD_TYPE_RGBA, + .dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, + .cColorBits = 32, + .cAlphaBits = 8, + .iLayerType = PFD_MAIN_PLANE, + .cDepthBits = 24, + .cStencilBits = 8, + }; + + int pixel_format = ChoosePixelFormat(dummy_dc, &pfd); + SetPixelFormat(dummy_dc, pixel_format, &pfd); + + HGLRC dummy_context = wglCreateContext(dummy_dc); + wglMakeCurrent(dummy_dc, dummy_context); + + if (wglChoosePixelFormatARB == NULL) { + wglCreateContextAttribsARB = (wglCreateContextAttribsARB_type) wglGetProcAddress("wglCreateContextAttribsARB"); + wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) wglGetProcAddress("wglChoosePixelFormatARB"); + } + + wglMakeCurrent(dummy_dc, 0); + wglDeleteContext(dummy_context); + ReleaseDC(dummyWin, dummy_dc); + + if (wglCreateContextAttribsARB != NULL) { + PIXELFORMATDESCRIPTOR pfd = { sizeof(pfd), 1, PFD_TYPE_RGBA, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, 32, 8, PFD_MAIN_PLANE, 24, 8 }; + + if (args & RGFW_OPENGL_SOFTWARE) + pfd.dwFlags |= PFD_GENERIC_FORMAT | PFD_GENERIC_ACCELERATED; + + if (wglChoosePixelFormatARB != NULL) { + i32* pixel_format_attribs = (i32*)RGFW_initAttribs(args & RGFW_OPENGL_SOFTWARE); + + int pixel_format; + UINT num_formats; + wglChoosePixelFormatARB(win->src.hdc, pixel_format_attribs, 0, 1, &pixel_format, &num_formats); + if (!num_formats) { + printf("Failed to set the OpenGL 3.3 pixel format.\n"); + } + + DescribePixelFormat(win->src.hdc, pixel_format, sizeof(pfd), &pfd); + if (!SetPixelFormat(win->src.hdc, pixel_format, &pfd)) { + printf("Failed to set the OpenGL 3.3 pixel format.\n"); + } + } + + u32 index = 0; + i32 attribs[40]; +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 + SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB); + + if (RGFW_majorVersion || RGFW_minorVersion) { + SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_majorVersion); + SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_minorVersion); + } + + SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB); + + if (RGFW_majorVersion || RGFW_minorVersion) { + SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_majorVersion); + SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_minorVersion); + } + + SET_ATTRIB(0, 0); + + win->src.rSurf = wglCreateContextAttribsARB(win->src.hdc, NULL, attribs); + } else { + fprintf(stderr, "Failed to create an accelerated OpenGL Context\n"); + + int pixel_format = ChoosePixelFormat(win->src.hdc, &pfd); + SetPixelFormat(win->src.hdc, pixel_format, &pfd); + + win->src.rSurf = wglCreateContext(win->src.hdc); + } + + wglMakeCurrent(win->src.hdc, win->src.rSurf); + wglShareLists(RGFW_root->src.rSurf, win->src.rSurf); +#endif + +#ifdef RGFW_OSMESA +#ifdef RGFW_LINK_OSM ESA + OSMesaMakeCurrentSource = (PFN_OSMesaMakeCurrent) GetProcAddress(win->src.hdc, "OSMesaMakeCurrent"); + OSMesaCreateContextSource = (PFN_OSMesaCreateContext) GetProcAddress(win->src.hdc, "OSMesaCreateContext"); + OSMesaDestroyContextSource = (PFN_OSMesaDestroyContext) GetProcAddress(win->src.hdc, "OSMesaDestroyContext"); +#endif +#endif + +#ifdef RGFW_OPENGL + ReleaseDC(win->src.window, win->src.hdc); + win->src.hdc = GetDC(win->src.window); + wglMakeCurrent(win->src.hdc, win->src.rSurf); +#endif + + DestroyWindow(dummyWin); + RGFW_init_buffer(win); + +#ifdef RGFW_VULKAN + RGFW_initVulkan(win); +#endif + + if (args & RGFW_SCALE_TO_MONITOR) + RGFW_window_scaleToMonitor(win); + +#ifdef RGFW_EGL + RGFW_createOpenGLContext(win); +#endif + + if (args & RGFW_HIDE_MOUSE) + RGFW_window_showMouse(win, 0); + + ShowWindow(win->src.window, SW_SHOWNORMAL); + + return win; + } + + + RGFW_area RGFW_getScreenSize(void) { + return RGFW_AREA(GetDeviceCaps(GetDC(NULL), HORZRES), GetDeviceCaps(GetDC(NULL), VERTRES)); + } + + RGFW_vector RGFW_getGlobalMousePoint(void) { + POINT p; + GetCursorPos(&p); + + return RGFW_VECTOR(p.x, p.y); + } + + void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { + assert(win != NULL); + win->src.minSize = a; + } + + void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { + assert(win != NULL); + win->src.maxSize = a; + } + + + void RGFW_window_minimize(RGFW_window* win) { + assert(win != NULL); + + ShowWindow(win->src.window, SW_MINIMIZE); + } + + void RGFW_window_restore(RGFW_window* win) { + assert(win != NULL); + + ShowWindow(win->src.window, SW_RESTORE); + } + + static i32 RGFW_checkXInput(RGFW_Event* e) { + static WORD buttons[4]; + static BYTE triggers[4][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} }; + + size_t i; + for (i = 0; i < 4; i++) { + XINPUT_STATE state; + if (XInputGetState == NULL || + XInputGetState((DWORD) i, &state) == ERROR_DEVICE_NOT_CONNECTED + ) + return 0; + + e->button = 0; + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_A && !(buttons[i] & XINPUT_GAMEPAD_A)) { + e->button = RGFW_JS_A; + e->type = RGFW_jsButtonPressed; + buttons[i] = state.Gamepad.wButtons; + return 1; + } else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_B && !(buttons[i] & XINPUT_GAMEPAD_B)) + e->button = RGFW_JS_B; + else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_Y && !(buttons[i] & XINPUT_GAMEPAD_Y)) + e->button = RGFW_JS_Y; + else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_X && !(buttons[i] & XINPUT_GAMEPAD_X)) + e->button = RGFW_JS_X; + else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_START && !(buttons[i] & XINPUT_GAMEPAD_START)) + e->button = RGFW_JS_START; + else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK && !(buttons[i] & XINPUT_GAMEPAD_BACK)) + e->button = RGFW_JS_SELECT; + else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP && !(buttons[i] & XINPUT_GAMEPAD_DPAD_UP)) + e->button = RGFW_JS_UP; + else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN && !(buttons[i] & XINPUT_GAMEPAD_DPAD_DOWN)) + e->button = RGFW_JS_DOWN; + else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT && !(buttons[i] & XINPUT_GAMEPAD_DPAD_LEFT)) + e->button = RGFW_JS_LEFT; + else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT && !(buttons[i] & XINPUT_GAMEPAD_DPAD_RIGHT)) + e->button = RGFW_JS_RIGHT; + else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER && !(buttons[i] & XINPUT_GAMEPAD_LEFT_SHOULDER)) + e->button = RGFW_JS_L1; + else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER && !(buttons[i] & XINPUT_GAMEPAD_RIGHT_SHOULDER)) + e->button = RGFW_JS_R1; + else if (state.Gamepad.bLeftTrigger && triggers[i][0] == 0) + e->button = RGFW_JS_L2; + else if (state.Gamepad.bRightTrigger && triggers[i][1] == 0) + e->button = RGFW_JS_R2; + + triggers[i][0] = state.Gamepad.bLeftTrigger; + triggers[i][1] = state.Gamepad.bRightTrigger; + + if (e->button) { + buttons[i] = state.Gamepad.wButtons; + e->type = RGFW_jsButtonPressed; + return 1; + } + + if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_A) && (buttons[i] & XINPUT_GAMEPAD_A)) { + e->button = RGFW_JS_A; + e->type = RGFW_jsButtonReleased; + buttons[i] = state.Gamepad.wButtons; + return 1; + } else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_B) && (buttons[i] & XINPUT_GAMEPAD_B)) + e->button = RGFW_JS_B; + else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_Y) && (buttons[i] & XINPUT_GAMEPAD_Y)) + e->button = RGFW_JS_Y; + else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_X) && (buttons[i] & XINPUT_GAMEPAD_X)) + e->button = RGFW_JS_X; + else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_START) && (buttons[i] & XINPUT_GAMEPAD_START)) + e->button = RGFW_JS_START; + else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) && (buttons[i] & XINPUT_GAMEPAD_BACK)) + e->button = RGFW_JS_SELECT; + else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) && (buttons[i] & XINPUT_GAMEPAD_DPAD_UP)) + e->button = RGFW_JS_UP; + else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) && (buttons[i] & XINPUT_GAMEPAD_DPAD_DOWN)) + e->button = RGFW_JS_DOWN; + else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) && (buttons[i] & XINPUT_GAMEPAD_DPAD_LEFT)) + e->button = RGFW_JS_LEFT; + else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) && (buttons[i] & XINPUT_GAMEPAD_DPAD_RIGHT)) + e->button = RGFW_JS_RIGHT; + else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) && (buttons[i] & XINPUT_GAMEPAD_LEFT_SHOULDER)) + e->button = RGFW_JS_L1; + else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) && (buttons[i] & XINPUT_GAMEPAD_RIGHT_SHOULDER)) + e->button = RGFW_JS_R1; + else if (state.Gamepad.bLeftTrigger == 0 && triggers[i][0] != 0) + e->button = RGFW_JS_L2; + else if (state.Gamepad.bRightTrigger == 0 && triggers[i][1] != 0) + e->button = RGFW_JS_R2; + + buttons[i] = state.Gamepad.wButtons; + + if (e->button) { + e->type = RGFW_jsButtonReleased; + return 1; + } +#define INPUT_DEADZONE ( 0.24f * (float)(0x7FFF) ) // Default to 24% of the +/- 32767 range. This is a reasonable default value but can be altered if needed. + + if ((state.Gamepad.sThumbLX < INPUT_DEADZONE && + state.Gamepad.sThumbLX > -INPUT_DEADZONE) && + (state.Gamepad.sThumbLY < INPUT_DEADZONE && + state.Gamepad.sThumbLY > -INPUT_DEADZONE)) + { + state.Gamepad.sThumbLX = 0; + state.Gamepad.sThumbLY = 0; + } + + if ((state.Gamepad.sThumbRX < INPUT_DEADZONE && + state.Gamepad.sThumbRX > -INPUT_DEADZONE) && + (state.Gamepad.sThumbRY < INPUT_DEADZONE && + state.Gamepad.sThumbRY > -INPUT_DEADZONE)) + { + state.Gamepad.sThumbRX = 0; + state.Gamepad.sThumbRY = 0; + } + + e->axisesCount = 2; + RGFW_vector axis1 = RGFW_VECTOR(state.Gamepad.sThumbLX, state.Gamepad.sThumbLY); + RGFW_vector axis2 = RGFW_VECTOR(state.Gamepad.sThumbRX, state.Gamepad.sThumbRY); + + if (axis1.x != e->axis[0].x || axis1.y != e->axis[0].y || axis2.x != e->axis[1].x || axis2.y != e->axis[1].y) { + e->type = RGFW_jsAxisMove; + + e->axis[0] = axis1; + e->axis[1] = axis2; + + return 1; + } + + e->axis[0] = axis1; + e->axis[1] = axis2; + } + + return 0; + } + + RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) { + assert(win != NULL); + + MSG msg; + + if (RGFW_eventWindow.src.window == win->src.window) { + if (RGFW_eventWindow.r.x != -1) { + win->r.x = RGFW_eventWindow.r.x; + win->r.y = RGFW_eventWindow.r.y; + } + + if (RGFW_eventWindow.r.w != -1) { + win->r.w = RGFW_eventWindow.r.w; + win->r.h = RGFW_eventWindow.r.h; + } + + win->event.type = RGFW_windowAttribsChange; + + RGFW_eventWindow.src.window = NULL; + RGFW_eventWindow.r = RGFW_RECT(-1, -1, -1, -1); + + return &win->event; + } + + if (win->event.droppedFilesCount) { + u32 i; + for (i = 0; i < win->event.droppedFilesCount; i++) + win->event.droppedFiles[i][0] = '\0'; + } + + win->event.droppedFilesCount = 0; + + win->event.inFocus = (GetForegroundWindow() == win->src.window); + + if (RGFW_checkXInput(&win->event)) + return &win->event; + + if (win->event.type == RGFW_quit) + return NULL; + + static BYTE keyboardState[256]; + GetKeyboardState(keyboardState); + + if (PeekMessageA(&msg, win->src.window, 0u, 0u, PM_REMOVE)) { + switch (msg.message) { + case WM_CLOSE: + case WM_QUIT: + win->event.type = RGFW_quit; + break; + + case WM_KEYUP: + win->event.keyCode = (u32) msg.wParam; + strncpy(win->event.keyName, RGFW_keyCodeTokeyStr(msg.lParam), 16); + if (GetKeyState(VK_SHIFT) & 0x8000) { + ToAscii((UINT) msg.wParam, MapVirtualKey((UINT) msg.wParam, MAPVK_VK_TO_CHAR), + keyboardState, (LPWORD) win->event.keyName, 0); + } + + win->event.type = RGFW_keyReleased; + break; + + case WM_KEYDOWN: + win->event.keyCode = (u32) msg.wParam; + strncpy(win->event.keyName, RGFW_keyCodeTokeyStr(msg.lParam), 16); + if (GetKeyState(VK_SHIFT) & 0x8000) { + ToAscii((UINT) msg.wParam, MapVirtualKey((UINT) msg.wParam, MAPVK_VK_TO_CHAR), + keyboardState, (LPWORD) win->event.keyName, 0); + } + + win->event.type = RGFW_keyPressed; + break; + + case WM_MOUSEMOVE: + win->event.point.x = GET_X_LPARAM(msg.lParam); + win->event.point.y = GET_Y_LPARAM(msg.lParam); + + win->event.type = RGFW_mousePosChanged; + break; + + case WM_LBUTTONDOWN: + win->event.button = RGFW_mouseLeft; + win->event.type = RGFW_mouseButtonPressed; + break; + case WM_RBUTTONDOWN: + win->event.button = RGFW_mouseRight; + win->event.type = RGFW_mouseButtonPressed; + break; + case WM_MBUTTONDOWN: + win->event.button = RGFW_mouseMiddle; + win->event.type = RGFW_mouseButtonPressed; + break; + + case WM_MOUSEWHEEL: + if (msg.wParam > 0) + win->event.button = RGFW_mouseScrollUp; + else + win->event.button = RGFW_mouseScrollDown; + + win->event.scroll = (SHORT) HIWORD(msg.wParam) / (double) WHEEL_DELTA; + + win->event.type = RGFW_mouseButtonPressed; + break; + + case WM_LBUTTONUP: + win->event.button = RGFW_mouseLeft; + win->event.type = RGFW_mouseButtonReleased; + break; + case WM_RBUTTONUP: + win->event.button = RGFW_mouseRight; + win->event.type = RGFW_mouseButtonReleased; + break; + case WM_MBUTTONUP: + win->event.button = RGFW_mouseMiddle; + win->event.type = RGFW_mouseButtonReleased; + break; + + /* + much of this event is source from glfw + */ + case WM_DROPFILES: { + win->event.type = RGFW_dnd; + + HDROP drop = (HDROP) msg.wParam; + POINT pt; + u32 i; + + win->event.droppedFilesCount = DragQueryFileW(drop, 0xffffffff, NULL, 0); + //win->event.droppedFiles = (char**)RGFW_CALLOC(win->event.droppedFilesCount, sizeof(char*)); + + /* Move the mouse to the position of the drop */ + DragQueryPoint(drop, &pt); + + win->event.point.x = pt.x; + win->event.point.y = pt.y; + + for (i = 0; i < win->event.droppedFilesCount; i++) { + const UINT length = DragQueryFileW(drop, i, NULL, 0); + WCHAR* buffer = (WCHAR*) RGFW_CALLOC((size_t) length + 1, sizeof(WCHAR)); + + DragQueryFileW(drop, i, buffer, length + 1); + strcpy(win->event.droppedFiles[i], createUTF8FromWideStringWin32(buffer)); + + RGFW_FREE(buffer); + } + + DragFinish(drop); + } + break; + case WM_GETMINMAXINFO: + { + if (win->src.maxSize.w == 0 && win->src.maxSize.h == 0) + break; + + MINMAXINFO* mmi = (MINMAXINFO*) msg.lParam; + mmi->ptMinTrackSize.x = win->src.minSize.w; + mmi->ptMinTrackSize.y = win->src.minSize.h; + mmi->ptMaxTrackSize.x = win->src.maxSize.w; + mmi->ptMaxTrackSize.y = win->src.maxSize.h; + return 0; + } + default: + win->event.type = 0; + break; + } + + TranslateMessage(&msg); + DispatchMessageA(&msg); + } + + else + win->event.type = 0; + + RGFW_vector mouse = RGFW_getGlobalMousePoint(); + if (win->src.winArgs & RGFW_HOLD_MOUSE && win->event.inFocus && + (mouse.x != win->r.x + (win->r.w / 2) || mouse.y != win->r.y + (win->r.h / 2))) { + RGFW_window_moveMouse(win, RGFW_VECTOR(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2))); + } + + win->event.lockState = 0; + + if ((GetKeyState(VK_CAPITAL) & 0x0001) != 0) + win->event.lockState |= RGFW_CAPSLOCK; + if ((GetKeyState(VK_NUMLOCK) & 0x0001) != 0) + win->event.lockState |= RGFW_NUMLOCK; + if ((GetKeyState(VK_SCROLL) & 0x0001) != 0) + win->event.lockState |= 3; + + + if (!IsWindow(win->src.window)) + win->event.type = RGFW_quit; + + if (win->event.type) + return &win->event; + else + return NULL; + } + + u8 RGFW_window_isFullscreen(RGFW_window* win) { + assert(win != NULL); + + WINDOWPLACEMENT placement = { 0 }; + GetWindowPlacement(win->src.window, &placement); + return placement.showCmd == SW_SHOWMAXIMIZED; + } + + u8 RGFW_window_isHidden(RGFW_window* win) { + assert(win != NULL); + + return IsWindowVisible(win->src.window) == 0 && !RGFW_window_isMinimized(win); + } + + u8 RGFW_window_isMinimized(RGFW_window* win) { + assert(win != NULL); + + WINDOWPLACEMENT placement = { 0 }; + GetWindowPlacement(win->src.window, &placement); + return placement.showCmd == SW_SHOWMINIMIZED; + } + + u8 RGFW_window_isMaximized(RGFW_window* win) { + assert(win != NULL); + + WINDOWPLACEMENT placement = { 0 }; + GetWindowPlacement(win->src.window, &placement); + return placement.showCmd == SW_SHOWMAXIMIZED; + } + + typedef struct { int iIndex; HMONITOR hMonitor; } RGFW_mInfo; + BOOL CALLBACK GetMonitorByHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { + RGFW_mInfo* info = (RGFW_mInfo*) dwData; + if (info->hMonitor == hMonitor) + return FALSE; + + info->iIndex++; + return TRUE; + } + + RGFW_monitor win32CreateMonitor(HMONITOR src) { + RGFW_monitor monitor; + MONITORINFO monitorInfo; + + monitorInfo.cbSize = sizeof(MONITORINFO); + GetMonitorInfoA(src, &monitorInfo); + + RGFW_mInfo info; + info.iIndex = 0; + info.hMonitor = src; + + /* get the monitor's index */ + if (EnumDisplayMonitors(NULL, NULL, GetMonitorByHandle, (LPARAM) &info)) { + DISPLAY_DEVICEA dd; + dd.cb = sizeof(dd); + + /* loop through the devices until you find a device with the monitor's index */ + size_t deviceIndex; + for (deviceIndex = 0; EnumDisplayDevicesA(0, (DWORD) deviceIndex, &dd, 0); deviceIndex++) { + char* deviceName = dd.DeviceName; + if (EnumDisplayDevicesA(deviceName, info.iIndex, &dd, 0)) { + strcpy(monitor.name, dd.DeviceString); /* copy the monitor's name */ + break; + } + } + } + + monitor.rect.x = monitorInfo.rcWork.left; + monitor.rect.y = monitorInfo.rcWork.top; + monitor.rect.w = monitorInfo.rcWork.right - monitorInfo.rcWork.left; + monitor.rect.h = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top; + +#ifndef RGFW_NO_DPI + if (GetDpiForMonitor != NULL) { + u32 x, y; + GetDpiForMonitor(src, MDT_ANGULAR_DPI, &x, &y); + monitor.scaleX = (float) (x) / (float) USER_DEFAULT_SCREEN_DPI; + monitor.scaleY = (float) (y) / (float) USER_DEFAULT_SCREEN_DPI; + } +#endif + + HDC hdc = GetDC(NULL); + /* get pixels per inch */ + i32 ppiX = GetDeviceCaps(hdc, LOGPIXELSX); + i32 ppiY = GetDeviceCaps(hdc, LOGPIXELSY); + ReleaseDC(NULL, hdc); + + /* Calculate physical height in inches */ + monitor.physW = GetSystemMetrics(SM_CYSCREEN) / (float) ppiX; + monitor.physH = GetSystemMetrics(SM_CXSCREEN) / (float) ppiY; + + return monitor; + } + + RGFW_monitor RGFW_monitors[6]; + BOOL CALLBACK GetMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { + RGFW_mInfo* info = (RGFW_mInfo*) dwData; + + if (info->iIndex >= 6) + return FALSE; + + RGFW_monitors[info->iIndex] = win32CreateMonitor(hMonitor); + info->iIndex++; + + return TRUE; + } + + RGFW_monitor RGFW_getPrimaryMonitor(void) { + return win32CreateMonitor(MonitorFromPoint((POINT) { 0, 0 }, MONITOR_DEFAULTTOPRIMARY)); + } + + RGFW_monitor* RGFW_getMonitors(void) { + RGFW_mInfo info; + info.iIndex = 0; + while (EnumDisplayMonitors(NULL, NULL, GetMonitorHandle, (LPARAM) &info)); + + return RGFW_monitors; + } + + RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { + HMONITOR src = MonitorFromWindow(win->src.window, MONITOR_DEFAULTTOPRIMARY); + return win32CreateMonitor(src); + } + + u8 RGFW_isPressedI(RGFW_window* win, u32 key) { + if (win != NULL && !win->event.inFocus) + return 0; + + if (GetAsyncKeyState(key) & 0x8000) + return 1; + else return 0; + } + + HICON RGFW_loadHandleImage(RGFW_window* win, u8* src, RGFW_area a, BOOL icon) { + assert(win != NULL); + + u32 i; + HDC dc; + HICON handle; + HBITMAP color, mask; + BITMAPV5HEADER bi; + ICONINFO ii; + u8* target = NULL; + u8* source = src; + + ZeroMemory(&bi, sizeof(bi)); + bi.bV5Size = sizeof(bi); + bi.bV5Width = a.w; + bi.bV5Height = -((LONG) a.h); + bi.bV5Planes = 1; + bi.bV5BitCount = 32; + bi.bV5Compression = BI_BITFIELDS; + bi.bV5RedMask = 0x00ff0000; + bi.bV5GreenMask = 0x0000ff00; + bi.bV5BlueMask = 0x000000ff; + bi.bV5AlphaMask = 0xff000000; + + dc = GetDC(NULL); + color = CreateDIBSection(dc, + (BITMAPINFO*) &bi, + DIB_RGB_COLORS, + (void**) &target, + NULL, + (DWORD) 0); + ReleaseDC(NULL, dc); + + mask = CreateBitmap(a.w, a.h, 1, 1, NULL); + + for (i = 0; i < a.w * a.h; i++) { + target[0] = source[2]; + target[1] = source[1]; + target[2] = source[0]; + target[3] = source[3]; + target += 4; + source += 4; + } + + ZeroMemory(&ii, sizeof(ii)); + ii.fIcon = icon; + ii.xHotspot = 0; + ii.yHotspot = 0; + ii.hbmMask = mask; + ii.hbmColor = color; + + handle = CreateIconIndirect(&ii); + + DeleteObject(color); + DeleteObject(mask); + + return handle; + } + + void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) { + assert(win != NULL); + + HCURSOR cursor = (HCURSOR) RGFW_loadHandleImage(win, image, a, FALSE); + SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) cursor); + SetCursor(cursor); + DestroyCursor(cursor); + } + + void RGFW_window_setMouseDefault(RGFW_window* win) { + RGFW_window_setMouseStandard(win, 32512); + } + + void RGFW_window_setMouseStandard(RGFW_window* win, i32 mouse) { + assert(win != NULL); + + char* icon = MAKEINTRESOURCEA(mouse); + + SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) LoadCursorA(NULL, icon)); + SetCursor(LoadCursorA(NULL, icon)); + } + + void RGFW_window_hide(RGFW_window* win) { + ShowWindow(win->src.window, SW_HIDE); + } + + void RGFW_window_show(RGFW_window* win) { + ShowWindow(win->src.window, SW_RESTORE); + } + + void RGFW_window_close(RGFW_window* win) { + assert(win != NULL); + +#ifdef RGFW_VULKAN + for (u32 i = 0; i < win->src.image_count; i++) { + vkDestroyFramebuffer(RGFW_vulkan_info.device, RGFW_vulkan_info.framebuffers[i], NULL); + } + + for (u32 i = 0; i < win->src.image_count; i++) { + vkDestroyImageView(RGFW_vulkan_info.device, win->src.swapchain_image_views[i], NULL); + } + + vkDestroySwapchainKHR(RGFW_vulkan_info.device, win->src.swapchain, NULL); + vkDestroySurfaceKHR(RGFW_vulkan_info.instance, win->src.rSurf, NULL); + free(win->src.swapchain_image_views); + free(win->src.swapchain_images); +#endif + +#ifdef RGFW_EGL + RGFW_closeEGL(win); +#endif + + if (win == RGFW_root) { +#ifdef RGFW_DIRECTX + RGFW_dxInfo.pDeviceContext->lpVtbl->Release(RGFW_dxInfo.pDeviceContext); + RGFW_dxInfo.pDevice->lpVtbl->Release(RGFW_dxInfo.pDevice); + RGFW_dxInfo.pAdapter->lpVtbl->Release(RGFW_dxInfo.pAdapter); + RGFW_dxInfo.pFactory->lpVtbl->Release(RGFW_dxInfo.pFactory); +#endif + + if (RGFW_XInput_dll != NULL) { + FreeLibrary(RGFW_XInput_dll); + RGFW_XInput_dll = NULL; + } + + #ifndef RGFW_NO_DPI + if (RGFW_Shcore_dll != NULL) { + FreeLibrary(RGFW_Shcore_dll); + RGFW_Shcore_dll = NULL; + } + #endif + + if (wglinstance != NULL) { + FreeLibrary(wglinstance); + wglinstance = NULL; + } + + RGFW_root = NULL; + } + +#ifdef RGFW_DIRECTX + win->src.swapchain->lpVtbl->Release(win->src.swapchain); + win->src.renderTargetView->lpVtbl->Release(win->src.renderTargetView); + win->src.pDepthStencilView->lpVtbl->Release(win->src.pDepthStencilView); +#endif + +#ifdef RGFW_BUFFER + DeleteDC(win->src.hdcMem); + DeleteObject(win->src.bitmap); +#endif + +#ifdef RGFW_OPENGL + wglDeleteContext((HGLRC) win->src.rSurf); /* delete opengl context */ +#endif + DeleteDC(win->src.hdc); /* delete device context */ + DestroyWindow(win->src.window); /* delete window */ + +#if defined(RGFW_OSMESA) + if (win->buffer != NULL) + RGFW_FREE(win->buffer); +#endif + +#ifdef RGFW_ALLOC_DROPFILES + { + u32 i; + for (i = 0; i < RGFW_MAX_DROPS; i++) + RGFW_FREE(win->event.droppedFiles[i]); + + + RGFW_FREE(win->event.droppedFiles); + } +#endif + + RGFW_FREE(win); + } + + void RGFW_window_move(RGFW_window* win, RGFW_vector v) { + assert(win != NULL); + + win->r.x = v.x; + win->r.y = v.y; + SetWindowPos(win->src.window, HWND_TOP, win->r.x, win->r.y, 0, 0, SWP_NOSIZE); + } + + void RGFW_window_resize(RGFW_window* win, RGFW_area a) { + assert(win != NULL); + + win->r.w = a.w; + win->r.h = a.h; + SetWindowPos(win->src.window, HWND_TOP, 0, 0, win->r.w, win->r.h + win->src.hOffset, SWP_NOMOVE); + } + + + void RGFW_window_setName(RGFW_window* win, char* name) { + assert(win != NULL); + + SetWindowTextA(win->src.window, name); + } + + /* much of this function is sourced from GLFW */ + void RGFW_window_setIcon(RGFW_window* win, u8* src, RGFW_area a, i32 channels) { + assert(win != NULL); + + HICON handle = RGFW_loadHandleImage(win, src, a, TRUE); + + SetClassLongPtrA(win->src.window, GCLP_HICON, (LPARAM) handle); + + DestroyIcon(handle); + } + + char* RGFW_readClipboard(size_t* size) { + /* Open the clipboard */ + if (OpenClipboard(NULL) == 0) + return (char*) ""; + + /* Get the clipboard data as a Unicode string */ + HANDLE hData = GetClipboardData(CF_UNICODETEXT); + if (hData == NULL) { + CloseClipboard(); + return (char*) ""; + } + + wchar_t* wstr = (wchar_t*) GlobalLock(hData); + + char* text; + + { + setlocale(LC_ALL, "en_US.UTF-8"); + + size_t textLen = wcstombs(NULL, wstr, 0); + if (textLen == 0) + return (char*) ""; + + text = (char*) malloc((textLen * sizeof(char)) + 1); + + wcstombs(text, wstr, (textLen) +1); + + if (size != NULL) + *size = textLen + 1; + } + + /* Release the clipboard data */ + GlobalUnlock(hData); + CloseClipboard(); + + return text; + } + + 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, textLen); + GlobalUnlock(object); + + if (!OpenClipboard(RGFW_root->src.window)) { + GlobalFree(object); + return; + } + + EmptyClipboard(); + SetClipboardData(CF_UNICODETEXT, object); + CloseClipboard(); + } + + u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber) { + assert(win != NULL); + + return RGFW_registerJoystickF(win, (char*) ""); + } + + u16 RGFW_registerJoystickF(RGFW_window* win, char* file) { + assert(win != NULL); + + + return win->src.joystickCount - 1; + } + + void RGFW_window_moveMouse(RGFW_window* win, RGFW_vector p) { + assert(win != NULL); + + SetCursorPos(p.x, p.y); + } + + char* createUTF8FromWideStringWin32(const WCHAR* source) { + char* target; + i32 size; + + size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL); + if (!size) { + return NULL; + } + + target = (char*) RGFW_CALLOC(size, 1); + + if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, size, NULL, NULL)) { + RGFW_FREE(target); + return NULL; + } + + return target; + } + +#ifndef RGFW_NO_THREADS + RGFW_thread RGFW_createThread(void* (*function_ptr)(void*), void* args) { return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) *function_ptr, args, 0, NULL); } + void RGFW_cancelThread(RGFW_thread thread) { CloseHandle((HANDLE) thread); } + void RGFW_joinThread(RGFW_thread thread) { WaitForSingleObject((HANDLE) thread, INFINITE); } + void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { SetThreadPriority((HANDLE) thread, priority); } +#endif +#endif + +#if defined(RGFW_MACOS) + + void* RGFWnsglFramework = NULL; + +#ifdef RGFW_OPENGL + void* RGFW_getProcAddress(const char* procname) { + if (RGFWnsglFramework == NULL) + RGFWnsglFramework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); + + CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, procname, kCFStringEncodingASCII); + + void* symbol = CFBundleGetFunctionPointerForName(RGFWnsglFramework, symbolName); + + CFRelease(symbolName); + + return symbol; + } +#endif + + CVReturn displayCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) { return kCVReturnSuccess; } + + RGFW_window* RGFW_windows[10]; + u32 RGFW_windows_size = 0; + + id NSWindow_delegate(RGFW_window* win) { + return (id) objc_msgSend_id(win->src.window, sel_registerName("delegate")); + } + + u32 RGFW_OnClose(void* self) { + u32 i; + for (i = 0; i < RGFW_windows_size; i++) + if (RGFW_windows[i] && NSWindow_delegate(RGFW_windows[i]) == self) { + RGFW_windows[i]->event.type = RGFW_quit; + return true; + } + + return true; + } + + /* NOTE(EimaMei): Fixes the constant clicking when the app is running under a terminal. */ + bool acceptsFirstResponder() { return true; } + bool performKeyEquivalent(NSEvent* event) { return true; } + + NSDragOperation draggingEntered(id self, SEL sel, id sender) { return NSDragOperationCopy; } + NSDragOperation draggingUpdated(id self, SEL sel, id sender) { return NSDragOperationCopy; } + bool prepareForDragOperation(void) { return true; } + + void RGFW__osxDraggingEnded(id self, SEL sel, id sender) { return; } + + /* NOTE(EimaMei): Usually, you never need 'id self, SEL cmd' for C -> Obj-C methods. This isn't the case. */ + bool performDragOperation(id self, SEL sel, id sender) { + NSWindow* window = objc_msgSend_id(sender, sel_registerName("draggingDestinationWindow")); + u32 i; + bool found = false; + + for (i = 0; i < RGFW_windows_size; i++) + if (RGFW_windows[i]->src.window == window) { + found = true; + break; + } + + if (!found) + i = 0; + + Class* array[] = { objc_getClass("NSURL"), NULL }; + char** droppedFiles = (char**) NSPasteboard_readObjectsForClasses( + (NSPasteboard*) objc_msgSend_id(sender, sel_registerName("draggingPasteboard")), + array, 1, NULL); + + RGFW_windows[i]->event.droppedFilesCount = si_array_len(droppedFiles); + + u32 y; + + for (y = 0; y < RGFW_windows[i]->event.droppedFilesCount; y++) + strcpy(RGFW_windows[i]->event.droppedFiles[y], droppedFiles[y]); + + RGFW_windows[i]->event.type = RGFW_dnd; + + NSPoint p = *(NSPoint*) objc_msgSend_id(sender, sel_registerName("draggingLocation")); + RGFW_windows[i]->event.point.x = p.x; + RGFW_windows[i]->event.point.x = p.y; + + return true; + } + + + NSApplication* NSApp = NULL; + + static void NSMoveToResourceDir(void) { + /* sourced from glfw */ + char resourcesPath[255]; + + 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); + } + + + NSSize RGFW__osxWindowResize(void* self, SEL sel, NSSize frameSize) { + u32 i; + for (i = 0; i < RGFW_windows_size; i++) { + if (RGFW_windows[i] && NSWindow_delegate(RGFW_windows[i]) == self) { + RGFW_windows[i]->r.w = frameSize.width; + RGFW_windows[i]->r.h = frameSize.height; + RGFW_windows[i]->event.type = RGFW_windowAttribsChange; + + return frameSize; + } + } + + return frameSize; + } + + void RGFW__osxWindowMove(void* self, SEL sel) { + u32 i; + for (i = 0; i < RGFW_windows_size; i++) { + if (RGFW_windows[i] && NSWindow_delegate(RGFW_windows[i]) == self) { + NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)(RGFW_windows[i]->src.window, sel_registerName("frame")); + RGFW_windows[i]->r.x = (i32) frame.origin.x; + RGFW_windows[i]->r.y = (i32) frame.origin.y; + + RGFW_windows[i]->event.type = RGFW_windowAttribsChange; + return; + } + } + } + + #ifdef __cplusplus + #define APPKIT_EXTERN extern "C" + #else + #define APPKIT_EXTERN extern + #endif + + APPKIT_EXTERN NSPasteboardType const NSPasteboardTypeURL = "public.url"; API_AVAILABLE(macos(10.13)); // Equivalent to kUTTypeURL + APPKIT_EXTERN NSPasteboardType const NSPasteboardTypeFileURL = "public.file-url"; API_AVAILABLE(macos(10.13)); // Equivalent to kUTTypeFileURL + + RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) { + static u8 RGFW_loaded = 0; + + /* NOTE(EimaMei): Why does Apple hate good code? Like wtf, who thought of methods being a great idea??? + Imagine a universe, where MacOS had a proper system API (we would probably have like 20% better performance). + */ + si_func_to_SEL_with_name("NSObject", "windowShouldClose", RGFW_OnClose); + + /* NOTE(EimaMei): Fixes the 'Boop' sfx from constantly playing each time you click a key. Only a problem when running in the terminal. */ + si_func_to_SEL("NSWindow", acceptsFirstResponder); + si_func_to_SEL("NSWindow", performKeyEquivalent); + + if (NSApp == NULL) { + NSApp = objc_msgSend_id(objc_getClass("NSApplication"), sel_registerName("sharedApplication")); + + ((void (*)(id, SEL, NSUInteger))objc_msgSend) + (NSApp, sel_registerName("setActivationPolicy:"), NSApplicationActivationPolicyRegular); + } + + RGFW_window* win = RGFW_window_basic_init(rect, args); + + NSRect windowRect; + windowRect.origin.x = win->r.x; + windowRect.origin.y = win->r.y; + windowRect.size.width = win->r.w; + windowRect.size.height = win->r.h; + + NSBackingStoreType macArgs = NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSBackingStoreBuffered | NSWindowStyleMaskTitled; + + if (!(args & RGFW_NO_RESIZE)) + macArgs |= NSWindowStyleMaskResizable; + if (!(args & RGFW_NO_BORDER)) + macArgs |= NSWindowStyleMaskTitled; + else + macArgs = NSWindowStyleMaskBorderless; + { + void* nsclass = objc_getClass("NSWindow"); + void* func = sel_registerName("initWithContentRect:styleMask:backing:defer:"); + + win->src.window = ((id(*)(id, SEL, NSRect, NSWindowStyleMask, NSBackingStoreType, bool))objc_msgSend) + (NSAlloc(nsclass), func, windowRect, macArgs, macArgs, false); + } + + NSString* str = NSString_stringWithUTF8String(name); + objc_msgSend_void_id(win->src.window, sel_registerName("setTitle:"), str); + +#ifdef RGFW_OPENGL + void* attrs = RGFW_initAttribs(args & RGFW_OPENGL_SOFTWARE); + void* format = NSOpenGLPixelFormat_initWithAttributes(attrs); + + if (format == NULL) { + printf("Failed to load pixel format "); + + void* attrs = RGFW_initAttribs(1); + format = NSOpenGLPixelFormat_initWithAttributes(attrs); + if (format == NULL) + printf("and loading software rendering OpenGL failed\n"); + else + printf("Switching to software rendering\n"); + } + + win->src.view = NSOpenGLView_initWithFrame(NSMakeRect(0, 0, win->r.w, win->r.h), format); + objc_msgSend_void(win->src.view, sel_registerName("prepareOpenGL")); + win->src.rSurf = objc_msgSend_id(win->src.view, sel_registerName("openGLContext")); + +#else + NSRect contentRect = NSMakeRect(0, 0, win->r.w, win->r.h); + win->src.view = ((id(*)(id, SEL, NSRect))objc_msgSend) + (NSAlloc(objc_getClass("NSView")), sel_registerName("initWithFrame:"), + contentRect); +#endif + + + void* contentView = NSWindow_contentView(win->src.window); + objc_msgSend_void_bool(contentView, sel_registerName("setWantsLayer:"), true); + + objc_msgSend_void_id(win->src.window, sel_registerName("setContentView:"), win->src.view); + +#ifdef RGFW_OPENGL + objc_msgSend_void(win->src.rSurf, sel_registerName("makeCurrentContext")); +#endif + if (args & RGFW_TRANSPARENT_WINDOW) { +#ifdef RGFW_OPENGL + i32 opacity = 0; + NSOpenGLContext_setValues(win->src.rSurf, &opacity, 304); +#endif + + objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), false); + + objc_msgSend_void_id(win->src.window, sel_registerName("setBackgroundColor:"), + NSColor_colorWithSRGB(0, 0, 0, 0)); + + ((void (*)(id, SEL, CGFloat))objc_msgSend) + (win->src.window, sel_registerName("setAlphaValue:"), 0x00); + } + + win->src.display = CGMainDisplayID(); + CVDisplayLinkCreateWithCGDisplay(win->src.display, &win->src.displayLink); + CVDisplayLinkSetOutputCallback(win->src.displayLink, displayCallback, win); + CVDisplayLinkStart(win->src.displayLink); + + RGFW_init_buffer(win); + +#ifdef RGFW_VULKAN + RGFW_initVulkan(win); +#endif + + if (args & RGFW_SCALE_TO_MONITOR) + RGFW_window_scaleToMonitor(win); + + if (args & RGFW_HIDE_MOUSE) + RGFW_window_showMouse(win, 0); + + if (args & RGFW_COCOA_MOVE_TO_RESOURCE_DIR) + NSMoveToResourceDir(); + + Class delegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "WindowDelegate", 0); + class_addMethod(delegateClass, sel_registerName("windowWillResize:toSize:"), (IMP) RGFW__osxWindowResize, "{NSSize=ff}@:{NSSize=ff}"); + class_addMethod(delegateClass, sel_registerName("windowWillMove:"), (IMP) RGFW__osxWindowMove, ""); + class_addMethod(delegateClass, sel_registerName("windowDidMove:"), (IMP) RGFW__osxWindowMove, ""); + + + if (args & RGFW_ALLOW_DND) { + win->src.winArgs |= RGFW_ALLOW_DND; + +/* + NSPasteboardType types[] = {NSPasteboardTypeURL, NSPasteboardTypeFileURL, NSPasteboardTypeString}; + + siArray(NSPasteboardType) array = sic_arrayInit(types, sizeof(id), countof(types)); + NSWindow_registerForDraggedTypes(win->hwnd, array); + + win->dndHead = win->dndPrev = out; +*/ + + NSPasteboardType array[] = {NSPasteboardTypeURL, NSPasteboardTypeFileURL, NSPasteboardTypeString}; + NSregisterForDraggedTypes(win->src.window, array, 3); + + /* NOTE(EimaMei): Drag 'n Drop requires too many damn functions for just a Drag 'n Drop event. */ + class_addMethod(delegateClass, "draggingEntered:", draggingEntered, "l@:@"); + class_addMethod(delegateClass, "draggingUpdated:", draggingUpdated, "l@:@"); + class_addMethod(delegateClass, "draggingExited:", RGFW__osxDraggingEnded, "v@:@"); + class_addMethod(delegateClass, "draggingEnded:", RGFW__osxDraggingEnded, "v@:@"); + class_addMethod(delegateClass, "prepareForDragOperation:", prepareForDragOperation, "B@:@"); + class_addMethod(delegateClass, "performDragOperation:", performDragOperation, "B@:@"); + + } + + id delegate = objc_msgSend_id(NSAlloc(delegateClass), sel_registerName("init")); + + object_setInstanceVariable(delegate, "RGFW_window", win); + + objc_msgSend_void_id(win->src.window, sel_registerName("setDelegate:"), delegate); + + // Show the window + ((id(*)(id, SEL, SEL))objc_msgSend)(win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL); + objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true); + + if (!RGFW_loaded) { + objc_msgSend_void(win->src.window, sel_registerName("makeMainWindow")); + + RGFW_loaded = 1; + } + + NSApplication_finishLaunching(NSApp); + + RGFW_windows_size++; + + size_t i; + for (i = 0; i < RGFW_windows_size; i++) + if (!RGFW_windows[i]) { + RGFW_windows[i] = win; + break; + } + + if (RGFW_root == NULL) + RGFW_root = win; + + NSRetain(win->src.window); + NSRetain(NSApp); + + return win; + } + + + RGFW_area RGFW_getScreenSize(void) { + static CGDirectDisplayID display = 0; + + if (display == 0) + display = CGMainDisplayID(); + + return RGFW_AREA(CGDisplayPixelsWide(display), CGDisplayPixelsHigh(display)); + } + + RGFW_vector RGFW_getGlobalMousePoint(void) { + assert(RGFW_root != NULL); + + CGEventRef e = CGEventCreate(NULL); + CGPoint point = CGEventGetLocation(e); + CFRelease(e); + + return RGFW_VECTOR((u32) point.x, (u32) point.y); /* the point is loaded during event checks */ + } + + u32 RGFW_keysPressed[10]; /*10 keys at a time*/ + typedef NS_ENUM(u32, NSEventType) { /* various types of events */ + NSEventTypeLeftMouseDown = 1, + NSEventTypeLeftMouseUp = 2, + NSEventTypeRightMouseDown = 3, + NSEventTypeRightMouseUp = 4, + NSEventTypeMouseMoved = 5, + NSEventTypeLeftMouseDragged = 6, + NSEventTypeRightMouseDragged = 7, + NSEventTypeMouseEntered = 8, + NSEventTypeMouseExited = 9, + NSEventTypeKeyDown = 10, + NSEventTypeKeyUp = 11, + NSEventTypeFlagsChanged = 12, + NSEventTypeAppKitDefined = 13, + NSEventTypeSystemDefined = 14, + NSEventTypeApplicationDefined = 15, + NSEventTypePeriodic = 16, + NSEventTypeCursorUpdate = 17, + NSEventTypeScrollWheel = 22, + NSEventTypeTabletPoint = 23, + NSEventTypeTabletProximity = 24, + NSEventTypeOtherMouseDown = 25, + NSEventTypeOtherMouseUp = 26, + NSEventTypeOtherMouseDragged = 27, + /* The following event types are available on some hardware on 10.5.2 and later */ + NSEventTypeGesture API_AVAILABLE(macos(10.5)) = 29, + NSEventTypeMagnify API_AVAILABLE(macos(10.5)) = 30, + NSEventTypeSwipe API_AVAILABLE(macos(10.5)) = 31, + NSEventTypeRotate API_AVAILABLE(macos(10.5)) = 18, + NSEventTypeBeginGesture API_AVAILABLE(macos(10.5)) = 19, + NSEventTypeEndGesture API_AVAILABLE(macos(10.5)) = 20, + + NSEventTypeSmartMagnify API_AVAILABLE(macos(10.8)) = 32, + NSEventTypeQuickLook API_AVAILABLE(macos(10.8)) = 33, + + NSEventTypePressure API_AVAILABLE(macos(10.10.3)) = 34, + NSEventTypeDirectTouch API_AVAILABLE(macos(10.10)) = 37, + + NSEventTypeChangeMode API_AVAILABLE(macos(10.15)) = 38, + }; + + typedef NS_ENUM(unsigned long long, NSEventMask) { /* masks for the types of events */ + NSEventMaskLeftMouseDown = 1ULL << NSEventTypeLeftMouseDown, + NSEventMaskLeftMouseUp = 1ULL << NSEventTypeLeftMouseUp, + NSEventMaskRightMouseDown = 1ULL << NSEventTypeRightMouseDown, + NSEventMaskRightMouseUp = 1ULL << NSEventTypeRightMouseUp, + NSEventMaskMouseMoved = 1ULL << NSEventTypeMouseMoved, + NSEventMaskLeftMouseDragged = 1ULL << NSEventTypeLeftMouseDragged, + NSEventMaskRightMouseDragged = 1ULL << NSEventTypeRightMouseDragged, + NSEventMaskMouseEntered = 1ULL << NSEventTypeMouseEntered, + NSEventMaskMouseExited = 1ULL << NSEventTypeMouseExited, + NSEventMaskKeyDown = 1ULL << NSEventTypeKeyDown, + NSEventMaskKeyUp = 1ULL << NSEventTypeKeyUp, + NSEventMaskFlagsChanged = 1ULL << NSEventTypeFlagsChanged, + NSEventMaskAppKitDefined = 1ULL << NSEventTypeAppKitDefined, + NSEventMaskSystemDefined = 1ULL << NSEventTypeSystemDefined, + NSEventMaskApplicationDefined = 1ULL << NSEventTypeApplicationDefined, + NSEventMaskPeriodic = 1ULL << NSEventTypePeriodic, + NSEventMaskCursorUpdate = 1ULL << NSEventTypeCursorUpdate, + NSEventMaskScrollWheel = 1ULL << NSEventTypeScrollWheel, + NSEventMaskTabletPoint = 1ULL << NSEventTypeTabletPoint, + NSEventMaskTabletProximity = 1ULL << NSEventTypeTabletProximity, + NSEventMaskOtherMouseDown = 1ULL << NSEventTypeOtherMouseDown, + NSEventMaskOtherMouseUp = 1ULL << NSEventTypeOtherMouseUp, + NSEventMaskOtherMouseDragged = 1ULL << NSEventTypeOtherMouseDragged, + /* The following event masks are available on some hardware on 10.5.2 and later */ + NSEventMaskGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeGesture, + NSEventMaskMagnify API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeMagnify, + NSEventMaskSwipe API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeSwipe, + NSEventMaskRotate API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeRotate, + NSEventMaskBeginGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeBeginGesture, + NSEventMaskEndGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeEndGesture, + + /* Note: You can only use these event masks on 64 bit. In other words, you cannot setup a local, nor global, event monitor for these event types on 32 bit. Also, you cannot search the event queue for them (nextEventMatchingMask:...) on 32 bit. + */ + NSEventMaskSmartMagnify API_AVAILABLE(macos(10.8)) = 1ULL << NSEventTypeSmartMagnify, + NSEventMaskPressure API_AVAILABLE(macos(10.10.3)) = 1ULL << NSEventTypePressure, + NSEventMaskDirectTouch API_AVAILABLE(macos(10.12.2)) = 1ULL << NSEventTypeDirectTouch, + + NSEventMaskChangeMode API_AVAILABLE(macos(10.15)) = 1ULL << NSEventTypeChangeMode, + + NSEventMaskAny = ULONG_MAX, + + }; + + RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) { + assert(win != NULL); + + if (win->event.type == RGFW_quit) + return &win->event; + + static void* eventFunc = NULL; + if (eventFunc == NULL) + eventFunc = sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"); + + if (win->event.type == RGFW_windowAttribsChange && win->event.keyCode != 120) { + win->event.keyCode = 120; + return &win->event; + } + + NSEvent* e = (NSEvent*) ((id(*)(id, SEL, NSEventMask, void*, NSString*, bool))objc_msgSend) + (NSApp, eventFunc, ULONG_MAX, NULL, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true); + + + if (e == NULL) + return NULL; + + if (objc_msgSend_id(e, sel_registerName("window")) != win->src.window) { + ((void (*)(id, SEL, id, bool))objc_msgSend) + (NSApp, sel_registerName("postEvent:atStart:"), e, 0); + + return NULL; + } + + if (win->event.droppedFilesCount) { + u32 i; + for (i = 0; i < win->event.droppedFilesCount; i++) + win->event.droppedFiles[i][0] = '\0'; + } + + win->event.droppedFilesCount = 0; + win->event.type = 0; + + win->event.inFocus = (bool) objc_msgSend_bool(win->src.window, sel_registerName("isKeyWindow")); + + switch (objc_msgSend_uint(e, sel_registerName("type"))) { + case NSEventTypeKeyDown: + win->event.type = RGFW_keyPressed; + win->event.keyCode = (u16) objc_msgSend_uint(e, sel_registerName("keyCode")); + win->event.keyName = (const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("characters"))); + + RGFW_keyMap[win->event.keyCode] = 1; + break; + + case NSEventTypeKeyUp: + win->event.type = RGFW_keyReleased; + win->event.keyCode = (u16) objc_msgSend_uint(e, sel_registerName("keyCode")); + win->event.keyName = (const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("characters"))); + + RGFW_keyMap[win->event.keyCode] = 0; + break; + + case NSEventTypeLeftMouseDragged: + case NSEventTypeOtherMouseDragged: + case NSEventTypeRightMouseDragged: + case NSEventTypeMouseMoved: + win->event.type = RGFW_mousePosChanged; + NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow")); + + win->event.point = RGFW_VECTOR((u32) p.x, (u32) (win->r.h - p.y)); + + if (win->src.winArgs & RGFW_HOLD_MOUSE) { + RGFW_vector mouse = RGFW_getGlobalMousePoint(); + if ((mouse.x != win->r.x + (win->r.w / 2) || mouse.y != win->r.y + (win->r.h / 2))) { + RGFW_window_moveMouse(win, RGFW_VECTOR(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2))); + } + } + break; + + case NSEventTypeLeftMouseDown: + win->event.button = RGFW_mouseLeft; + win->event.type = RGFW_mouseButtonPressed; + break; + + case NSEventTypeOtherMouseDown: + win->event.button = RGFW_mouseMiddle; + win->event.type = RGFW_mouseButtonPressed; + break; + + case NSEventTypeRightMouseDown: + win->event.button = RGFW_mouseRight; + win->event.type = RGFW_mouseButtonPressed; + break; + + case NSEventTypeLeftMouseUp: + win->event.button = RGFW_mouseLeft; + win->event.type = RGFW_mouseButtonReleased; + break; + + case NSEventTypeOtherMouseUp: + win->event.button = RGFW_mouseMiddle; + win->event.type = RGFW_mouseButtonReleased; + break; + + case NSEventTypeScrollWheel: { + double deltaY = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaY")); + + if (deltaY > 0) + win->event.button = RGFW_mouseScrollUp; + + else if (deltaY < 0) + win->event.button = RGFW_mouseScrollDown; + + win->event.scroll = deltaY; + + win->event.type = RGFW_mouseButtonReleased; + break; + } + case NSEventTypeRightMouseUp: + win->event.button = RGFW_mouseRight; + win->event.type = RGFW_mouseButtonReleased; + break; + + default: + break; + } + + objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e); + + return &win->event; + } + + + void RGFW_window_move(RGFW_window* win, RGFW_vector v) { + assert(win != NULL); + + win->r.x = v.x; + win->r.y = v.y; + ((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend) + (win->src.window, sel_registerName("setFrame:display:animate:"), NSMakeRect(win->r.x, win->r.y, win->r.w, win->r.h), true, true); + } + + void RGFW_window_resize(RGFW_window* win, RGFW_area a) { + assert(win != NULL); + + win->r.w = a.w; + win->r.h = a.h; + ((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend) + (win->src.window, sel_registerName("setFrame:display:animate:"), NSMakeRect(win->r.x, win->r.y, win->r.w, win->r.h), true, true); + } + + void RGFW_window_minimize(RGFW_window* win) { + assert(win != NULL); + + objc_msgSend_void_SEL(win->src.window, sel_registerName("performMiniaturize:"), NULL); + } + + void RGFW_window_restore(RGFW_window* win) { + assert(win != NULL); + + objc_msgSend_void_SEL(win->src.window, sel_registerName("deminiaturize:"), NULL); + } + + void RGFW_window_setName(RGFW_window* win, char* name) { + assert(win != NULL); + + NSString* str = NSString_stringWithUTF8String(name); + objc_msgSend_void_id(win->src.window, sel_registerName("setTitle:"), str); + } + + void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { + ((void (*)(id, SEL, NSSize))objc_msgSend) + (win->src.window, sel_registerName("setMinSize:"), NSMakeSize(a.w, a.h)); + } + + void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { + ((void (*)(id, SEL, NSSize))objc_msgSend) + (win->src.window, sel_registerName("setMaxSize:"), NSMakeSize(a.w, a.h)); + } + + void RGFW_window_setIcon(RGFW_window* win, u8* data, RGFW_area area, i32 channels) { + assert(win != NULL); + + /* code by EimaMei */ + // Make a bitmap representation, then copy the loaded image into it. + void* representation = NSBitmapImageRep_initWithBitmapData(NULL, area.w, area.h, 8, channels, (channels == 4), false, "NSCalibratedRGBColorSpace", 1 << 1, area.w * channels, 8 * channels); + memcpy(NSBitmapImageRep_bitmapData(representation), data, area.w * area.h * channels); + + // Add ze representation. + void* dock_image = NSImage_initWithSize(NSMakeSize(area.w, area.h)); + NSImage_addRepresentation(dock_image, (void*) representation); + + // Finally, set the dock image to it. + objc_msgSend_void_id(NSApp, sel_registerName("setApplicationIconImage:"), dock_image); + // Free the garbage. + release(dock_image); + release(representation); + } + + NSCursor* NSCursor_arrowStr(char* str) { + void* nclass = objc_getClass("NSCursor"); + void* func = sel_registerName(str); + return (NSCursor*) objc_msgSend_id(nclass, func); + } + + void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) { + assert(win != NULL); + + if (image == NULL) { + objc_msgSend_void(NSCursor_arrowStr("arrowCursor"), sel_registerName("set")); + return; + } + + /* NOTE(EimaMei): Code by yours truly. */ + // Make a bitmap representation, then copy the loaded image into it. + void* representation = NSBitmapImageRep_initWithBitmapData(NULL, a.w, a.h, 8, channels, (channels == 4), false, "NSCalibratedRGBColorSpace", 1 << 1, a.w * channels, 8 * channels); + memcpy(NSBitmapImageRep_bitmapData(representation), image, a.w * a.h * channels); + + // Add ze representation. + void* cursor_image = NSImage_initWithSize(NSMakeSize(a.w, a.h)); + NSImage_addRepresentation(cursor_image, representation); + + // Finally, set the cursor image. + void* cursor = NSCursor_initWithImage(cursor_image, NSMakePoint(0, 0)); + + objc_msgSend_void(cursor, sel_registerName("set")); + + // Free the garbage. + release(cursor_image); + release(representation); + } + + void RGFW_window_setMouseDefault(RGFW_window* win) { + RGFW_window_setMouseStandard(win, NSCursor_arrowStr("arrowCursor")); + } + + void RGFW_window_showMouse(RGFW_window* win, i8 show) { + if (show) { + CGDisplayShowCursor(kCGDirectMainDisplay); + } + else { + CGDisplayHideCursor(kCGDirectMainDisplay); + } + } + + void RGFW_window_setMouseStandard(RGFW_window* win, void* mouse) { + CGDisplayShowCursor(kCGDirectMainDisplay); + objc_msgSend_void(mouse, sel_registerName("set")); + } + + void RGFW_window_moveMouse(RGFW_window* win, RGFW_vector v) { + assert(win != NULL); + + CGWarpMouseCursorPosition(CGPointMake(v.x, v.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) { + ((id(*)(id, SEL, SEL))objc_msgSend)(win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL); + objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true); + } + + u8 RGFW_window_isFullscreen(RGFW_window* win) { + assert(win != NULL); + + NSWindowStyleMask mask = (NSWindowStyleMask) objc_msgSend_uint(win->src.window, sel_registerName("styleMask")); + return (mask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen; + } + + u8 RGFW_window_isHidden(RGFW_window* win) { + assert(win != NULL); + + bool visible = objc_msgSend_bool(win->src.window, sel_registerName("isVisible")); + return visible == NO && !RGFW_window_isMinimized(win); + } + + u8 RGFW_window_isMinimized(RGFW_window* win) { + assert(win != NULL); + + return objc_msgSend_bool(win->src.window, sel_registerName("isMiniaturized")) == YES; + } + + u8 RGFW_window_isMaximized(RGFW_window* win) { + assert(win != NULL); + + return objc_msgSend_bool(win->src.window, sel_registerName("isZoomed")); + } + + static RGFW_monitor RGFW_NSCreateMonitor(CGDirectDisplayID display) { + RGFW_monitor monitor; + + CGRect bounds = CGDisplayBounds(display); + monitor.rect = RGFW_RECT((int) bounds.origin.x, (int) bounds.origin.y, (int) bounds.size.width, (int) bounds.size.height); + + CGSize screenSizeMM = CGDisplayScreenSize(display); + monitor.physW = screenSizeMM.width / 25.4; + monitor.physH = screenSizeMM.height / 25.4; + + monitor.scaleX = (monitor.rect.w / (screenSizeMM.width)) / 2.6; + monitor.scaleY = (monitor.rect.h / (screenSizeMM.height)) / 2.6; + + snprintf(monitor.name, 128, "%i %i %i", CGDisplayModelNumber(display), CGDisplayVendorNumber(display), CGDisplaySerialNumber(display)); + + return monitor; + } + + + static RGFW_monitor RGFW_monitors[7]; + + RGFW_monitor* RGFW_getMonitors(void) { + static CGDirectDisplayID displays[7]; + u32 count; + + if (CGGetActiveDisplayList(6, displays, &count) != kCGErrorSuccess) + return NULL; + + for (u32 i = 0; i < count; i++) + RGFW_monitors[i] = RGFW_NSCreateMonitor(displays[i]); + + return RGFW_monitors; + } + + RGFW_monitor RGFW_getPrimaryMonitor(void) { + CGDirectDisplayID primary = CGMainDisplayID(); + return RGFW_NSCreateMonitor(primary); + } + + RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { + return RGFW_NSCreateMonitor(win->src.display); + } + + u8 RGFW_isPressedI(RGFW_window* win, u32 key) { + if (key >= 128) { +#ifdef RGFW_PRINT_ERRORS + fprintf(stderr, "RGFW_isPressedI : invalid keycode\n"); +#endif + RGFW_error = 1; + } + + return RGFW_keyMap[key]; + } + +#ifdef __cplusplus +#define APPKIT_EXTERN extern "C" +#else +#define APPKIT_EXTERN extern +#endif + + char* RGFW_readClipboard(size_t* size) { + char* str = strdup((char*) NSPasteboard_stringForType(NSPasteboard_generalPasteboard(), NSPasteboardTypeString)); + if (size != NULL) + *size = strlen(str); + return str; + } + + void RGFW_writeClipboard(const char* text, u32 textLen) { + NSPasteboardType array[] = { NSPasteboardTypeString, NULL }; + NSPasteBoard_declareTypes(NSPasteboard_generalPasteboard(), array, 1, NULL); + + NSPasteBoard_setString(NSPasteboard_generalPasteboard(), text, NSPasteboardTypeString); + } + + u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber) { + assert(win != NULL); + + return RGFW_registerJoystickF(win, (char*) ""); + } + + u16 RGFW_registerJoystickF(RGFW_window* win, char* file) { + assert(win != NULL); + + return win->src.joystickCount - 1; + } + + void RGFW_window_close(RGFW_window* win) { + assert(win != NULL); + +#ifdef RGFW_VULKAN + for (int i = 0; i < win->src.image_count; i++) { + vkDestroyFramebuffer(RGFW_vulkan_info.device, RGFW_vulkan_info.framebuffers[i], NULL); + } + + for (int i = 0; i < win->src.image_count; i++) { + vkDestroyImageView(RGFW_vulkan_info.device, win->src.swapchain_image_views[i], NULL); + } + + vkDestroySwapchainKHR(RGFW_vulkan_info.device, win->src.swapchain, NULL); + vkDestroySurfaceKHR(RGFW_vulkan_info.instance, win->src.rSurf, NULL); + free(win->src.swapchain_image_views); + free(win->src.swapchain_images); +#endif + + release(win->src.view); + +#ifdef RGFW_ALLOC_DROPFILES + { + u32 i; + for (i = 0; i < RGFW_MAX_DROPS; i++) + RGFW_FREE(win->event.droppedFiles[i]); + + + RGFW_FREE(win->event.droppedFiles); + } +#endif + + u32 i; + for (i = 0; i < RGFW_windows_size; i++) + if (RGFW_windows[i]->src.window == win->src.window) { + RGFW_windows[i] = NULL; + break; + } + + if (!i) { + RGFW_windows_size = 0; + + objc_msgSend_void_id(NSApp, sel_registerName("terminate:"), (id) win->src.window); + NSApp = NULL; + } + +#ifdef RGFW_BUFFER + release(win->src.bitmap); + release(win->src.image); +#endif + + CVDisplayLinkStop(win->src.displayLink); + CVDisplayLinkRelease(win->src.displayLink); + + free(win); + } +#endif + +#if defined(RGFW_X11) || defined(RGFW_MACOS) + +#ifndef RGFW_NO_THREADS +#include + + RGFW_thread RGFW_createThread(void* (*function_ptr)(void*), void* args) { + RGFW_thread t; + pthread_create((pthread_t*) &t, NULL, *function_ptr, NULL); + return t; + } + void RGFW_cancelThread(RGFW_thread thread) { pthread_cancel((pthread_t) thread); } + void RGFW_joinThread(RGFW_thread thread) { pthread_join((pthread_t) thread, NULL); } +#ifdef __linux__ + void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { pthread_setschedprio(thread, priority); } +#endif +#endif +#endif + + void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { + assert(win != NULL); + +#ifdef RGFW_OPENGL +#ifdef RGFW_X11 + glXMakeCurrent((Display*) win->src.display, (Drawable) win->src.window, (GLXContext) win->src.rSurf); +#endif +#ifdef RGFW_WINDOWS + wglMakeCurrent(win->src.hdc, (HGLRC) win->src.rSurf); +#endif +#if defined(RGFW_MACOS) + objc_msgSend_void(win->src.rSurf, sel_registerName("makeCurrentContext")); +#endif +#else +#ifdef RGFW_EGL + eglMakeCurrent(win->src.EGL_display, win->src.EGL_surface, win->src.EGL_surface, win->src.rSurf); +#endif +#endif + + } + + void RGFW_window_makeCurrent(RGFW_window* win) { + assert(win != NULL); + +#if defined(RGFW_WINDOWS) && defined(RGFW_DIRECTX) + RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, &win->src.renderTargetView, NULL); +#endif + +#ifdef RGFW_OPENGL + RGFW_window_makeCurrent_OpenGL(win); +#endif + } + + void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { + assert(win != NULL); + +#ifdef RGFW_OPENGL +#ifdef RGFW_X11 + ((PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress((GLubyte*) "glXSwapIntervalEXT"))((Display*) win->src.display, (Window) win->src.window, swapInterval); +#endif +#ifdef RGFW_WINDOWS + + typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval); + static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; + static void* loadSwapFunc = (void*) 1; + + if (loadSwapFunc == NULL) { + fprintf(stderr, "wglSwapIntervalEXT not supported\n"); + win->fpsCap = (swapInterval == 1) ? 0 : swapInterval; + return; + } + + if (wglSwapIntervalEXT == NULL) { + loadSwapFunc = (void*) wglGetProcAddress("wglSwapIntervalEXT"); + wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) loadSwapFunc; + } + + if (wglSwapIntervalEXT(swapInterval) == FALSE) + fprintf(stderr, "Failed to set swap interval\n"); + +#endif +#if defined(RGFW_MACOS) + NSOpenGLContext_setValues(win->src.rSurf, &swapInterval, 222); +#endif +#endif + +#ifdef RGFW_EGL + eglSwapInterval(win->src.EGL_display, swapInterval); +#endif + + win->fpsCap = (swapInterval == 1) ? 0 : swapInterval; + + } + + void RGFW_window_setGPURender(RGFW_window* win, i8 set) { + if (!set && !(win->src.winArgs & RGFW_NO_GPU_RENDER)) + win->src.winArgs |= RGFW_NO_GPU_RENDER; + + else if (set && win->src.winArgs & RGFW_NO_GPU_RENDER) + win->src.winArgs ^= RGFW_NO_GPU_RENDER; + } + + void RGFW_window_setCPURender(RGFW_window* win, i8 set) { + if (!set && !(win->src.winArgs & RGFW_NO_CPU_RENDER)) + win->src.winArgs |= RGFW_NO_CPU_RENDER; + + else if (set && win->src.winArgs & RGFW_NO_CPU_RENDER) + win->src.winArgs ^= RGFW_NO_CPU_RENDER; + } + + + void RGFW_window_swapBuffers(RGFW_window* win) { + assert(win != NULL); + + win->event.frames++; + RGFW_window_checkFPS(win); + + RGFW_window_makeCurrent(win); + + /* clear the window*/ + + if (!(win->src.winArgs & RGFW_NO_CPU_RENDER)) { +#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) +#ifdef RGFW_OSMESA + u8* row = (u8*) RGFW_MALLOC(win->r.w * 3); + + i32 half_height = win->r.h / 2; + i32 stride = win->r.w * 3; + + i32 y; + for (y = 0; y < half_height; ++y) { + i32 top_offset = y * stride; + i32 bottom_offset = (win->r.h - y - 1) * stride; + memcpy(row, win->buffer + top_offset, stride); + memcpy(win->buffer + top_offset, win->buffer + bottom_offset, stride); + memcpy(win->buffer + bottom_offset, row, stride); + } + + RGFW_FREE(row); +#endif + +#ifdef RGFW_X11 + RGFW_area area = RGFW_getScreenSize(); + +#ifndef RGFW_X11_DONT_CONVERT_BGR + win->src.bitmap->data = (const char*) win->buffer; + u32 x, y; + for (y = 0; y < win->r.h; y++) { + for (x = 0; x < win->r.w; x++) { + u32 index = (y * 4 * area.w) + x * 4; + + u8 red = win->src.bitmap->data[index]; + win->src.bitmap->data[index] = win->buffer[index + 2]; + win->src.bitmap->data[index + 2] = red; + } + } +#endif + + XPutImage(win->src.display, (Window) win->src.window, XDefaultGC(win->src.display, XDefaultScreen(win->src.display)), win->src.bitmap, 0, 0, 0, 0, win->r.w, win->r.h); +#endif +#ifdef RGFW_WINDOWS + HGDIOBJ oldbmp = SelectObject(win->src.hdcMem, win->src.bitmap); + BitBlt(win->src.hdc, 0, 0, win->r.w, win->r.h, win->src.hdcMem, 0, 0, SRCCOPY); + SelectObject(win->src.hdcMem, oldbmp); +#endif +#if defined(RGFW_MACOS) + RGFW_area area = RGFW_getScreenSize(); + void* view = NSWindow_contentView(win->src.window); + void* layer = objc_msgSend_id(view, sel_registerName("layer")); + + ((void(*)(id, SEL, NSRect))objc_msgSend)(layer, + sel_registerName("setFrame:"), + NSMakeRect(0, 0, win->r.w, win->r.h)); + + NSBitmapImageRep* rep = NSBitmapImageRep_initWithBitmapData( + &win->buffer, win->r.w, win->r.h, 8, 4, true, false, + "NSDeviceRGBColorSpace", 0, + area.w * 4, 32 + ); + id image = NSAlloc(objc_getClass("NSImage")); + NSImage_addRepresentation(image, rep); + objc_msgSend_void_id(layer, sel_registerName("setContents:"), (id) image); + + release(image); + release(rep); +#endif +#endif + +#ifdef RGFW_VULKAN +#ifdef RGFW_PRINT_ERRORS + fprintf(stderr, "RGFW_window_swapBuffers %s\n", "RGFW_window_swapBuffers is not yet supported for Vulkan"); + RGFW_error = 1; +#endif +#endif + } + + if (win->src.winArgs & RGFW_NO_GPU_RENDER) + return; + +#ifdef RGFW_OPENGL +#ifdef RGFW_EGL + eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); +#else +#if defined(RGFW_X11) && defined(RGFW_OPENGL) + glXSwapBuffers((Display*) win->src.display, (Window) win->src.window); +#endif +#ifdef RGFW_WINDOWS + SwapBuffers(win->src.hdc); +#endif +#if defined(RGFW_MACOS) + NSOpenGLContext_flushBuffer(win->src.rSurf); +#endif +#endif +#endif + +#if defined(RGFW_WINDOWS) && defined(RGFW_DIRECTX) + win->src.swapchain->lpVtbl->Present(win->src.swapchain, 0, 0); +#endif + } + + void RGFW_window_maximize(RGFW_window* win) { + assert(win != NULL); + + RGFW_area screen = RGFW_getScreenSize(); + + RGFW_window_move(win, RGFW_VECTOR(0, 0)); + RGFW_window_resize(win, screen); + } + + u8 RGFW_window_shouldClose(RGFW_window* win) { + assert(win != NULL); + + /* || RGFW_isPressedI(win, RGFW_Escape) */ + return (win->event.type == RGFW_quit || RGFW_isPressedI(win, RGFW_OS_BASED_VALUE(0xff1b, 0x1B, 53))); + } + + void RGFW_window_setShouldClose(RGFW_window* win) { win->event.type = RGFW_quit; } + + void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m) { + RGFW_window_move(win, RGFW_VECTOR(m.rect.x + win->r.x, m.rect.y + win->r.y)); + } + + void RGFW_window_mouseHold(RGFW_window* win) { + win->src.winArgs |= RGFW_HOLD_MOUSE; + + #ifdef RGFW_WINDOWS + RECT rect = {win->r.x, win->r.y, win->r.x + win->r.w, win->r.y + win->r.h}; + ClipCursor(&rect); + #endif + } + + void RGFW_window_mouseUnhold(RGFW_window* win) { + win->src.winArgs ^= RGFW_HOLD_MOUSE; + + #ifdef RGFW_WINDOWS + ClipCursor(NULL); + #endif + } + + void RGFW_sleep(u32 ms) { +#ifndef _MSC_VER + struct timespec time; + time.tv_sec = 0; + time.tv_nsec = ms * 1000; + + nanosleep(&time, NULL); +#else + Sleep(ms); +#endif + } + + static float currentFrameTime = 0; + + void RGFW_window_checkFPS(RGFW_window* win) { + assert(win != NULL); + + win->event.fps = RGFW_getFPS(); + + if (win->fpsCap == 0) + return; + + double targetFrameTime = 1.0 / win->fpsCap; + double elapsedTime = RGFW_getTime() - currentFrameTime; + + if (elapsedTime < targetFrameTime) { + u32 sleepTime = (u32) ((targetFrameTime - elapsedTime) * 1e3); + RGFW_sleep(sleepTime); + } + + currentFrameTime = (float) RGFW_getTime(); + + if (elapsedTime < targetFrameTime) { + u32 sleepTime = (u32) ((targetFrameTime - elapsedTime) * 1e3); + RGFW_sleep(sleepTime); + } + + currentFrameTime = (float) RGFW_getTime(); + } + +#ifdef __APPLE__ +#include +#endif + + u64 RGFW_getTimeNS(void) { +#ifdef RGFW_WINDOWS + LARGE_INTEGER frequency; + QueryPerformanceFrequency(&frequency); + + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + + return (u64) (counter.QuadPart * 1e9 / frequency.QuadPart); +#elif defined(__unix__) + struct timespec ts = { 0 }; + clock_gettime(CLOCK_MONOTONIC, &ts); + unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; + + return nanoSeconds; +#elif defined(__APPLE__) + static mach_timebase_info_data_t timebase_info; + if (timebase_info.denom == 0) { + mach_timebase_info(&timebase_info); + } + return mach_absolute_time() * timebase_info.numer / timebase_info.denom; +#endif + return 0; + } + + u64 RGFW_getTime(void) { +#ifdef RGFW_WINDOWS + LARGE_INTEGER frequency; + QueryPerformanceFrequency(&frequency); + + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + return (u64) (counter.QuadPart / (double) frequency.QuadPart); +#elif defined(__unix__) + struct timespec ts = { 0 }; + clock_gettime(CLOCK_MONOTONIC, &ts); + unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; + + return (double)(nanoSeconds) * 1e-9; +#elif defined(__APPLE__) + static mach_timebase_info_data_t timebase_info; + if (timebase_info.denom == 0) { + mach_timebase_info(&timebase_info); + } + return (double) mach_absolute_time() * (double) timebase_info.numer / ((double) timebase_info.denom * 1e9); +#endif + return 0; + } + + u32 RGFW_getFPS(void) { + static double previousSeconds = 0.0; + if (previousSeconds == 0.0) + previousSeconds = (double) RGFW_getTime();//glfwGetTime(); + + static i16 frameCount; + double currentSeconds = (double) RGFW_getTime();//glfwGetTime(); + double elapsedSeconds = currentSeconds - previousSeconds; + static double fps = 0; + + if (elapsedSeconds > 0.25) { + previousSeconds = currentSeconds; + fps = (double) frameCount / elapsedSeconds; + frameCount = 0; + } + + frameCount++; + + return (u32) fps; + } + +#endif /*RGFW_IMPLEMENTATION*/ + +#define RGFW_Escape RGFW_OS_BASED_VALUE(0xff1b, 0x1B, 53) +#define RGFW_F1 RGFW_OS_BASED_VALUE(0xffbe, 0x70, 127) +#define RGFW_F2 RGFW_OS_BASED_VALUE(0xffbf, 0x71, 121) +#define RGFW_F3 RGFW_OS_BASED_VALUE(0xffc0, 0x72, 100) +#define RGFW_F4 RGFW_OS_BASED_VALUE(0xffc1, 0x73, 119) +#define RGFW_F5 RGFW_OS_BASED_VALUE(0xffc2, 0x74, 97) +#define RGFW_F6 RGFW_OS_BASED_VALUE(0xffc3, 0x75, 98) +#define RGFW_F7 RGFW_OS_BASED_VALUE(0xffc4, 0x76, 99) +#define RGFW_F8 RGFW_OS_BASED_VALUE(0xffc5, 0x77, 101) +#define RGFW_F9 RGFW_OS_BASED_VALUE(0xffc6, 0x78, 102) +#define RGFW_F10 RGFW_OS_BASED_VALUE(0xffc7, 0x79, 110) +#define RGFW_F11 RGFW_OS_BASED_VALUE(0xffc8, 0x7A, 104) +#define RGFW_F12 RGFW_OS_BASED_VALUE(0xffc9, 0x7B, 112) +#define RGFW_F13 RGFW_OS_BASED_VALUE(0xffca, 0x7C, 106) +#define RGFW_F14 RGFW_OS_BASED_VALUE(0xffcb, 0x7D, 108) +#define RGFW_F15 RGFW_OS_BASED_VALUE(0xffcc, 0x7E, 114) + +#define RGFW_Backtick RGFW_OS_BASED_VALUE(96 , 192, 50) + +#define RGFW_0 RGFW_OS_BASED_VALUE(0x0030, 0x30, 29) +#define RGFW_1 RGFW_OS_BASED_VALUE(0x0031, 0x31, 18) +#define RGFW_2 RGFW_OS_BASED_VALUE(0x0032, 0x32, 19) +#define RGFW_3 RGFW_OS_BASED_VALUE(0x0033, 0x33, 20) +#define RGFW_4 RGFW_OS_BASED_VALUE(0x0034, 0x34, 21) +#define RGFW_5 RGFW_OS_BASED_VALUE(0x0035, 0x35, 23) +#define RGFW_6 RGFW_OS_BASED_VALUE(0x0036, 0x36, 22) +#define RGFW_7 RGFW_OS_BASED_VALUE(0x0037, 0x37, 26) +#define RGFW_8 RGFW_OS_BASED_VALUE(0x0038, 0x38, 28) +#define RGFW_9 RGFW_OS_BASED_VALUE(0x0039, 0x39, 25) + +#define RGFW_Minus RGFW_OS_BASED_VALUE(0x002d, 189, 27) +#define RGFW_Equals RGFW_OS_BASED_VALUE(0x003d, 187, 24) +#define RGFW_BackSpace RGFW_OS_BASED_VALUE(0xff08, 8, 51) +#define RGFW_Tab RGFW_OS_BASED_VALUE(0xff89, 0x09, 48) +#define RGFW_CapsLock RGFW_OS_BASED_VALUE(0xffe5, 20, 57) +#define RGFW_ShiftL RGFW_OS_BASED_VALUE(0xffe1, 0xA0, 56) +#define RGFW_ControlL RGFW_OS_BASED_VALUE(0xffe3, 0x11, 59) +#define RGFW_AltL RGFW_OS_BASED_VALUE(0xffe9, 164, 58) +#define RGFW_SuperL RGFW_OS_BASED_VALUE(0xffeb, 0x5B, 55) +#define RGFW_ShiftR RGFW_OS_BASED_VALUE(0xffe2, 0x5C, 56) +#define RGFW_ControlR RGFW_OS_BASED_VALUE(0xffe4, 0x11, 59) +#define RGFW_AltR RGFW_OS_BASED_VALUE(0xffea, 165, 58) +#define RGFW_SuperR RGFW_OS_BASED_VALUE(0xffec, 0xA4, 55) +#define RGFW_Space RGFW_OS_BASED_VALUE(0x0020, 0x20, 49) + +#define RGFW_A RGFW_OS_BASED_VALUE(0x0041, 0x41, 0) +#define RGFW_B RGFW_OS_BASED_VALUE(0x0042, 0x42, 11) +#define RGFW_C RGFW_OS_BASED_VALUE(0x0043, 0x43, 8) +#define RGFW_D RGFW_OS_BASED_VALUE(0x0044, 0x44, 2) +#define RGFW_E RGFW_OS_BASED_VALUE(0x0045, 0x45, 14) +#define RGFW_F RGFW_OS_BASED_VALUE(0x0046, 0x46, 3) +#define RGFW_G RGFW_OS_BASED_VALUE(0x0047, 0x47, 5) +#define RGFW_H RGFW_OS_BASED_VALUE(0x0048, 0x48, 4) +#define RGFW_I RGFW_OS_BASED_VALUE(0x0049, 0x49, 34) +#define RGFW_J RGFW_OS_BASED_VALUE(0x004a, 0x4A, 38) +#define RGFW_K RGFW_OS_BASED_VALUE(0x004b, 0x4B, 40) +#define RGFW_L RGFW_OS_BASED_VALUE(0x004c, 0x4C, 37) +#define RGFW_M RGFW_OS_BASED_VALUE(0x004d, 0x4D, 46) +#define RGFW_N RGFW_OS_BASED_VALUE(0x004e, 0x4E, 45) +#define RGFW_O RGFW_OS_BASED_VALUE(0x004f, 0x4F, 31) +#define RGFW_P RGFW_OS_BASED_VALUE(0x0050, 0x50, 35) +#define RGFW_Q RGFW_OS_BASED_VALUE(0x0051, 0x51, 12) +#define RGFW_R RGFW_OS_BASED_VALUE(0x0052, 0x52, 15) +#define RGFW_S RGFW_OS_BASED_VALUE(0x0053, 0x53, 1) +#define RGFW_T RGFW_OS_BASED_VALUE(0x0054, 0x54, 17) +#define RGFW_U RGFW_OS_BASED_VALUE(0x0055, 0x55, 32) +#define RGFW_V RGFW_OS_BASED_VALUE(0x0056, 0x56, 9) +#define RGFW_W RGFW_OS_BASED_VALUE(0x0057, 0x57, 13) +#define RGFW_X RGFW_OS_BASED_VALUE(0x0058, 0x58, 7) +#define RGFW_Y RGFW_OS_BASED_VALUE(0x0059, 0x59, 16) +#define RGFW_Z RGFW_OS_BASED_VALUE(0x005a, 0x5A, 6) + +#define RGFW_a RGFW_OS_BASED_VALUE(0x0061, 0x41, 0) +#define RGFW_b RGFW_OS_BASED_VALUE(0x0062, 0x42, 11) +#define RGFW_c RGFW_OS_BASED_VALUE(0x0063, 0x43, 8) +#define RGFW_d RGFW_OS_BASED_VALUE(0x0064, 0x44, 2) +#define RGFW_e RGFW_OS_BASED_VALUE(0x0065, 0x45, 14) +#define RGFW_f RGFW_OS_BASED_VALUE(0x0066, 0x46, 3) +#define RGFW_g RGFW_OS_BASED_VALUE(0x0067, 0x47, 5) +#define RGFW_h RGFW_OS_BASED_VALUE(0x0068, 0x48, 4) +#define RGFW_i RGFW_OS_BASED_VALUE(0x0069, 0x49, 34) +#define RGFW_j RGFW_OS_BASED_VALUE(0x006a, 0x4a, 38) +#define RGFW_k RGFW_OS_BASED_VALUE(0x006b, 0x4b, 40) +#define RGFW_l RGFW_OS_BASED_VALUE(0x006c, 0x4c, 37) +#define RGFW_m RGFW_OS_BASED_VALUE(0x006d, 0x4d, 46) +#define RGFW_n RGFW_OS_BASED_VALUE(0x006e, 0x4e, 45) +#define RGFW_o RGFW_OS_BASED_VALUE(0x006f, 0x4f, 31) +#define RGFW_p RGFW_OS_BASED_VALUE(0x0070, 0x50, 35) +#define RGFW_q RGFW_OS_BASED_VALUE(0x0071, 0x51, 12) +#define RGFW_r RGFW_OS_BASED_VALUE(0x0072, 0x52, 15) +#define RGFW_s RGFW_OS_BASED_VALUE(0x0073, 0x53, 1) +#define RGFW_t RGFW_OS_BASED_VALUE(0x0074, 0x54, 17) +#define RGFW_u RGFW_OS_BASED_VALUE(0x0075, 0x55, 32) +#define RGFW_v RGFW_OS_BASED_VALUE(0x0076, 0x56, 9) +#define RGFW_w RGFW_OS_BASED_VALUE(0x0077, 0x57, 13) +#define RGFW_x RGFW_OS_BASED_VALUE(0x0078, 0x58, 7) +#define RGFW_y RGFW_OS_BASED_VALUE(0x0079, 0x59, 16) +#define RGFW_z RGFW_OS_BASED_VALUE(0x007a, 0x5A, 6) + +#define RGFW_Period RGFW_OS_BASED_VALUE(0x002e, 190, 47) +#define RGFW_Comma RGFW_OS_BASED_VALUE(0x002c, 188, 43) +#define RGFW_Slash RGFW_OS_BASED_VALUE(0x002f, 191, 44) +#define RGFW_Bracket RGFW_OS_BASED_VALUE(0x005b, 219, 33) +#define RGFW_CloseBracket RGFW_OS_BASED_VALUE(0x005d, 221, 30) +#define RGFW_Semicolon RGFW_OS_BASED_VALUE(0x003b, 186, 41) +#define RGFW_Return RGFW_OS_BASED_VALUE(0xff0d, 0x0D, 36) +#define RGFW_Quote RGFW_OS_BASED_VALUE(0x0022, 222, 39) +#define RGFW_BackSlash RGFW_OS_BASED_VALUE(0x005c, 322, 42) + +#define RGFW_Up RGFW_OS_BASED_VALUE(0xff52, 0x26, 126) +#define RGFW_Down RGFW_OS_BASED_VALUE(0xff54, 0x28, 125) +#define RGFW_Left RGFW_OS_BASED_VALUE(0xff51, 0x25, 123) +#define RGFW_Right RGFW_OS_BASED_VALUE(0xff53, 0x27, 124) + +#define RGFW_Delete RGFW_OS_BASED_VALUE(0xffff, 0x2E, 118) +#define RGFW_Insert RGFW_OS_BASED_VALUE(0xff63, 0x2D, 115) +#define RGFW_End RGFW_OS_BASED_VALUE(0xff57, 0x23, 120) +#define RGFW_Home RGFW_OS_BASED_VALUE(0xff50, 0x24, 116) +#define RGFW_PageUp RGFW_OS_BASED_VALUE(0xff55, 336, 117) +#define RGFW_PageDown RGFW_OS_BASED_VALUE(0xff56, 325, 122) + +#define RGFW_Numlock RGFW_OS_BASED_VALUE(0xff7f, 0x90, 72) +#define RGFW_KP_Slash RGFW_OS_BASED_VALUE(0xffaf, 0x6F, 82) +#define RGFW_Multiply RGFW_OS_BASED_VALUE(0xffaa, 0x6A, 76) +#define RGFW_KP_Minus RGFW_OS_BASED_VALUE(0xffad, 0x6D, 67) +#define RGFW_KP_1 RGFW_OS_BASED_VALUE(0xffb1, 0x61, 84) +#define RGFW_KP_2 RGFW_OS_BASED_VALUE(0xffb2, 0x62, 85) +#define RGFW_KP_3 RGFW_OS_BASED_VALUE(0xffb3, 0x63, 86) +#define RGFW_KP_4 RGFW_OS_BASED_VALUE(0xffb4, 0x64, 87) +#define RGFW_KP_5 RGFW_OS_BASED_VALUE(0xffb5, 0x65, 88) +#define RGFW_KP_6 RGFW_OS_BASED_VALUE(0xffb6, 0x66, 89) +#define RGFW_KP_7 RGFW_OS_BASED_VALUE(0xffb7, 0x67, 90) +#define RGFW_KP_8 RGFW_OS_BASED_VALUE(0xffb8, 0x68, 92) +#define RGFW_KP_9 RGFW_OS_BASED_VALUE(0xffb9, 0x619, 93) +#define RGFW_KP_0 RGFW_OS_BASED_VALUE(0xffb0, 0x60, 83) +#define RGFW_KP_Period RGFW_OS_BASED_VALUE(0xffae, 0x6E, 65) +#define RGFW_KP_Return RGFW_OS_BASED_VALUE(0xff8d, 0x92, 77) + +#ifdef __APPLE__ + void* NSCursor_arrowStr(char* str); + void NSCursor_performSelector(void* cursor, void* selector); +#endif + + /* mouse icons */ +#define RGFW_MOUSE_ARROW RGFW_OS_BASED_VALUE(68, 32512, NSCursor_arrowStr("arrowCursor")) +#define RGFW_MOUSE_IBEAM RGFW_OS_BASED_VALUE(152, 32513, NSCursor_arrowStr("IBeamCursor")) +#define RGFW_MOUSE_CROSSHAIR RGFW_OS_BASED_VALUE(34, 32515, NSCursor_arrowStr("crosshairCursor")) +#define RGFW_MOUSE_POINTING_HAND RGFW_OS_BASED_VALUE(60, 32649, NSCursor_arrowStr("pointingHandCursor")) +#define RGFW_MOUSE_RESIZE_EW RGFW_OS_BASED_VALUE(108, 32644, NSCursor_arrowStr("resizeLeftRightCursor")) +#define RGFW_MOUSE_RESIZE_NS RGFW_OS_BASED_VALUE(116, 32645, NSCursor_arrowStr("resizeUpDownCursor")) +#define RGFW_MOUSE_RESIZE_ALL RGFW_OS_BASED_VALUE(52, 32646, NSCursor_arrowStr("closedHandCursor")) +#define RGFW_MOUSE_RESIZE_NWSE RGFW_OS_BASED_VALUE(12, 32642, NSCursor_performSelector(selector("_windowResizeNorthWestSouthEastCursor"))) +#define RGFW_MOUSE_RESIZE_NESW RGFW_OS_BASED_VALUE(14, 32643, NSCursor_performSelector(selector("_windowResizeNorthEastSouthWestCursor"))) +#define RGFW_MOUSE_NOT_ALLOWED RGFW_OS_BASED_VALUE(0, 32648, NSCursor_arrowStr("operationNotAllowedCursor")) + +#ifdef __cplusplus +} +#endif diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c new file mode 100644 index 000000000..e4f74938d --- /dev/null +++ b/src/platforms/rcore_desktop_rgfw.c @@ -0,0 +1,1519 @@ +/********************************************************************************************** +* +* rcore_desktop_rgfw template - Functions to manage window, graphics device and inputs +* +* PLATFORM: RGFW +* - Windows (Win32, Win64) +* - Linux (X11/Wayland desktop mode) +* - MacOS (Cocoa) +* +* LIMITATIONS: +* - Limitation 01 +* - Limitation 02 +* +* POSSIBLE IMPROVEMENTS: +* - Improvement 01 +* - Improvement 02 +* +* ADDITIONAL NOTES: +* - TRACELOG() function is located in raylib [utils] module +* +* CONFIGURATION: +* #define RCORE_PLATFORM_CUSTOM_FLAG +* Custom flag for rcore on target platform -not used- +* +* DEPENDENCIES: +* - RGFW.h (main library): Windowing and inputs management +* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5), Colleague Riley and contributors +* +* 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. +* +**********************************************************************************************/ + +#ifdef GRAPHICS_API_OPENGL_ES2 +#define RGFW_OPENGL_ES2 +#endif + +void ShowCursor(void); +void CloseWindow(void); + +#define _INPUT_EVENT_CODES_H +#define _APISETSTRING_ +#define RGFWDEF +#define _XTYPEDEF_FONT +#define RGFW_IMPLEMENTATION + +#define WIN32_LEAN_AND_MEAN +#define Rectangle rectangle_win32 +#define CloseWindow CloseWindow_win32 +#define ShowCursor __imp_ShowCursor + +#define Point NSPOINT +#define Size NSSIZE + +#ifdef _MSC_VER +__declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int CodePage, unsigned long dwFlags, const char* lpMultiByteStr, int cbMultiByte, wchar_t* lpWideCharStr, int cchWideChar); +#endif + +#include "../external/RGFW.h" +#undef DrawText +#undef ShowCursor +#undef CloseWindow +#undef Point +#undef Size + +#define Rectangle struct Rectangle +void CloseWindow(void); +void ShowCursor(void); + + +#include + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef struct { + // TODO: Define the platform specific variables required + + RGFW_window* window; // Native display device (physical screen connection) +} PlatformData; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; // Global CORE state context + +static PlatformData platform = { NULL }; // Platform specific + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +int InitPlatform(void); // Initialize platform (graphics, inputs and more) +bool InitGraphicsDevice(void); // Initialize graphics device + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +// NOTE: Functions declaration is provided by raylib.h + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Window and Graphics Device +//---------------------------------------------------------------------------------- + +// Check if application should close +bool WindowShouldClose(void) +{ + if (CORE.Window.shouldClose == false) + CORE.Window.shouldClose = RGFW_window_shouldClose(platform.window); + if (CORE.Window.ready) return CORE.Window.shouldClose; + else return true; +} + +// Toggle fullscreen mode +void ToggleFullscreen(void) +{ + RGFW_window_maximize(platform.window); + ToggleBorderlessWindowed(); +} + +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + CORE.Window.flags & FLAG_WINDOW_UNDECORATED; + + if (platform.window != NULL) + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() after window creation not available on target platform"); +} + +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + RGFW_window_maximize(platform.window); +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + RGFW_window_minimize(platform.window); +} + +// Set window state: not minimized/maximized +void RestoreWindow(void) +{ + RGFW_window_restore(platform.window); +} + +// Set window configuration state using flags +void SetWindowState(unsigned int flags) +{ + CORE.Window.flags |= flags; + + if (flags & FLAG_VSYNC_HINT) + { + RGFW_window_swapInterval(platform.window, 1); + } + if (flags & FLAG_FULLSCREEN_MODE) + { + RGFW_window_maximize(platform.window); + ToggleBorderlessWindowed(); + } + if (flags & FLAG_WINDOW_RESIZABLE) + { + RGFW_window_setMaxSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h)); + RGFW_window_setMinSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h)); + } + if (flags & FLAG_WINDOW_UNDECORATED) + { + ToggleBorderlessWindowed(); + } + if (flags & FLAG_WINDOW_HIDDEN) + { + RGFW_window_hide(platform.window); + } + if (flags & FLAG_WINDOW_MINIMIZED) + { + RGFW_window_minimize(platform.window); + } + if (flags & FLAG_WINDOW_MAXIMIZED) + { + RGFW_window_maximize(platform.window); + } + if (flags & FLAG_WINDOW_UNFOCUSED) + { + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_WINDOW_TOPMOST) + { + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_TOPMOST is not supported on PLATFORM_DESKTOP_RGFW"); + } + if (flags & FLAG_WINDOW_ALWAYS_RUN) + { + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_ALWAYS_RUN is not supported on PLATFORM_DESKTOP_RGFW"); + } + if (flags & FLAG_WINDOW_TRANSPARENT) + { + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_TRANSPARENT is not supported on PLATFORM_DESKTOP_RGFW"); + } + if (flags & FLAG_WINDOW_HIGHDPI) + { + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_RGFW"); + } + if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) + { + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_RGFW"); + } + if (flags & FLAG_BORDERLESS_WINDOWED_MODE) + { + ToggleBorderlessWindowed(); + } + if (flags & FLAG_MSAA_4X_HINT) + { + RGFW_setGLSamples(4); + } + if (flags & FLAG_INTERLACED_HINT) + { + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_RGFW"); + } +} + +// Clear window configuration state flags +void ClearWindowState(unsigned int flags) +{ + CORE.Window.flags &= ~flags; + + if (flags & FLAG_VSYNC_HINT) + { + RGFW_window_swapInterval(platform.window, 0); + } + if (flags & FLAG_FULLSCREEN_MODE) + { + ToggleBorderlessWindowed(); + RGFW_window_restore(platform.window); + CORE.Window.fullscreen = false; + } + if (flags & FLAG_WINDOW_RESIZABLE) + { + RGFW_window_setMaxSize(platform.window, RGFW_AREA(0, 0)); + RGFW_window_setMinSize(platform.window, RGFW_AREA(0, 0)); + } + if (flags & FLAG_WINDOW_UNDECORATED) + { + ToggleBorderlessWindowed(); + } + if (flags & FLAG_WINDOW_HIDDEN) + { + RGFW_window_show(platform.window); + } + if (flags & FLAG_WINDOW_MINIMIZED) + { + RGFW_window_restore(platform.window); + } + if (flags & FLAG_WINDOW_MAXIMIZED) + { + RGFW_window_restore(platform.window); + } + if (flags & FLAG_WINDOW_UNFOCUSED) + { + TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_RGFW"); + } + if (flags & FLAG_WINDOW_TOPMOST) + { + TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_TOPMOST is not supported on PLATFORM_DESKTOP_RGFW"); + } + if (flags & FLAG_WINDOW_ALWAYS_RUN) + { + TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_ALWAYS_RUN is not supported on PLATFORM_DESKTOP_RGFW"); + } + if (flags & FLAG_WINDOW_TRANSPARENT) + { + TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_TRANSPARENT is not supported on PLATFORM_DESKTOP_RGFW"); + } + if (flags & FLAG_WINDOW_HIGHDPI) + { + // NOTE: There also doesn't seem to be a feature to disable high DPI once enabled + TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_RGFW"); + } + if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) + { + //SDL_SetWindowGrab(platform.window, SDL_TRUE); + TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_RGFW"); + } + if (flags & FLAG_BORDERLESS_WINDOWED_MODE) + { + ToggleFullscreen(); + } + if (flags & FLAG_MSAA_4X_HINT) + { + RGFW_setGLSamples(0); + } + if (flags & FLAG_INTERLACED_HINT) + { + TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_RGFW"); + } +} + +// Set icon for window +void SetWindowIcon(Image image) +{ + i32 channels = 4; + + switch (image.format) { + case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: + case PIXELFORMAT_UNCOMPRESSED_R16: // 16 bpp (1 channel - half float) + case PIXELFORMAT_UNCOMPRESSED_R32: // 32 bpp (1 channel - float) + channels = 1; + break; + + case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: // 8*2 bpp (2 channels) + case PIXELFORMAT_UNCOMPRESSED_R5G6B5: // 16 bpp + case PIXELFORMAT_UNCOMPRESSED_R8G8B8: // 24 bpp + case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: // 16 bpp (1 bit alpha) + case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: // 16 bpp (4 bit alpha) + case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: // 32 bpp + channels = 2; + break; + + case PIXELFORMAT_UNCOMPRESSED_R32G32B32: // 32*3 bpp (3 channels - float) + case PIXELFORMAT_UNCOMPRESSED_R16G16B16: // 16*3 bpp (3 channels - half float) + case PIXELFORMAT_COMPRESSED_DXT1_RGB: // 4 bpp (no alpha) + case PIXELFORMAT_COMPRESSED_ETC1_RGB: // 4 bpp + case PIXELFORMAT_COMPRESSED_ETC2_RGB: // 4 bpp + case PIXELFORMAT_COMPRESSED_PVRT_RGB: // 4 bpp + channels = 3; + break; + + case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: // 32*4 bpp (4 channels - float) + case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: // 16*4 bpp (4 channels - half float) + case PIXELFORMAT_COMPRESSED_DXT1_RGBA: // 4 bpp (1 bit alpha) + case PIXELFORMAT_COMPRESSED_DXT3_RGBA: // 8 bpp + case PIXELFORMAT_COMPRESSED_DXT5_RGBA: // 8 bpp + case PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: // 8 bpp + case PIXELFORMAT_COMPRESSED_PVRT_RGBA: // 4 bpp + case PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: // 8 bpp + case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: // 2 bpp + channels = 4; + break; + + default: break; + } + + RGFW_window_setIcon(platform.window, image.data, RGFW_AREA(image.width, image.height), channels); +} + +// Set icon for window +void SetWindowIcons(Image *images, int count) +{ + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform"); +} + +// Set title for window +void SetWindowTitle(const char *title) +{ + RGFW_window_setName(platform.window, title); + CORE.Window.title = title; +} + +// Set window position on screen (windowed mode) +void SetWindowPosition(int x, int y) +{ + RGFW_window_move(platform.window, RGFW_VECTOR(x, y)); +} + +// Set monitor for the current window +void SetWindowMonitor(int monitor) +{ + RGFW_window_moveToMonitor(platform.window, RGFW_getMonitors()[monitor]); +} + +// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMinSize(int width, int height) +{ + RGFW_window_setMinSize(platform.window, RGFW_AREA(width, height)); + CORE.Window.screenMin.width = width; + CORE.Window.screenMin.height = height; +} + +// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMaxSize(int width, int height) +{ + RGFW_window_setMaxSize(platform.window, RGFW_AREA(width, height)); + CORE.Window.screenMax.width = width; + CORE.Window.screenMax.height = height; +} + +// Set window dimensions +void SetWindowSize(int width, int height) +{ + RGFW_window_resize(platform.window, RGFW_AREA(width, height)); +} + +// Set window opacity, value opacity is between 0.0 and 1.0 +void SetWindowOpacity(float opacity) +{ + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform"); +} + +// Set window focused +void SetWindowFocused(void) +{ + RGFW_window_show(platform.window); +} + +// Get native window handle +void *GetWindowHandle(void) +{ + return platform.window->src.window; +} + +// Get number of monitors +int GetMonitorCount(void) +{ + RGFW_monitor* mons = RGFW_getMonitors(); + u32 i; + for (i = 0; i < 6; i++) { + if (!mons[i].rect.x && !mons[i].rect.y && !mons[i].rect.w && mons[i].rect.h) + return i; + } + + return 6; +} + +// Get number of monitors +int GetCurrentMonitor(void) +{ + RGFW_monitor* mons = RGFW_getMonitors(); + RGFW_monitor mon = RGFW_window_getMonitor(platform.window); + + u32 i; + for (i = 0; i < 6; i++) { + if (mons[i].rect.x == mon.rect.x && + mons[i].rect.y == mon.rect.y) + return i; + } + + return 0; +} + +// Get selected monitor position +Vector2 GetMonitorPosition(int monitor) +{ + RGFW_monitor* mons = RGFW_getMonitors(); + + return (Vector2){mons[monitor].rect.x, mons[monitor].rect.y}; +} + +// Get selected monitor width (currently used by monitor) +int GetMonitorWidth(int monitor) +{ + RGFW_monitor* mons = RGFW_getMonitors(); + + return mons[monitor].rect.w; +} + +// Get selected monitor height (currently used by monitor) +int GetMonitorHeight(int monitor) +{ + RGFW_monitor* mons = RGFW_getMonitors(); + + return mons[monitor].rect.h; + return 0; +} + +// Get selected monitor physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ + RGFW_monitor* mons = RGFW_getMonitors(); + + return mons[monitor].physW; +} + +// Get selected monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ + RGFW_monitor* mons = RGFW_getMonitors(); + + return mons[monitor].physH; +} + +// Get selected monitor refresh rate +int GetMonitorRefreshRate(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform"); + return 0; +} + +// Get the human-readable, UTF-8 encoded name of the selected monitor +const char *GetMonitorName(int monitor) +{ + RGFW_monitor* mons = RGFW_getMonitors(); + + return mons[monitor].name; +} + +// Get window position XY on monitor +Vector2 GetWindowPosition(void) +{ + return (Vector2){ platform.window->r.x, platform.window->r.y }; +} + +// Get window scale DPI factor for current monitor +Vector2 GetWindowScaleDPI(void) +{ + RGFW_monitor monitor = RGFW_window_getMonitor(platform.window); + + return (Vector2){((u32)monitor.scaleX) * platform.window->r.w, ((u32) monitor.scaleX) * platform.window->r.h}; +} + +// Set clipboard text content +void SetClipboardText(const char *text) +{ + RGFW_writeClipboard(text, strlen(text)); +} + +// Get clipboard text content +// NOTE: returned string is allocated and freed by GLFW +const char *GetClipboardText(void) +{ + return RGFW_readClipboard(NULL); +} + +// Show mouse cursor +void ShowCursor(void) +{ + RGFW_window_showMouse(platform.window, true); + CORE.Input.Mouse.cursorHidden = false; +} + +// Hides mouse cursor +void HideCursor(void) +{ + RGFW_window_showMouse(platform.window, false); + CORE.Input.Mouse.cursorHidden = true; +} + +// Enables cursor (unlock cursor) +void EnableCursor(void) +{ + RGFW_window_mouseUnhold(platform.window); + + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + RGFW_window_showMouse(platform.window, true); + CORE.Input.Mouse.cursorHidden = false; +} + +// Disables cursor (lock cursor) +void DisableCursor(void) +{ + RGFW_window_mouseHold(platform.window); + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + + HideCursor(); +} + +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + RGFW_window_swapBuffers(platform.window); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + +// Get elapsed time measure in seconds since InitTimer() +double GetTime(void) +{ + double time = 0.0; + unsigned long long int nanoSeconds = RGFW_getTimeNS(); + time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer() + + return time; +} + +// Open URL with default system browser (if available) +// NOTE: This function is only safe to use if you control the URL given. +// A user could craft a malicious string performing another action. +// Only call this function yourself not with user input or make sure to check the string yourself. +// Ref: https://github.com/raysan5/raylib/issues/686 +void OpenURL(const char *url) +{ + // Security check to (partially) avoid malicious code on target platform + if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); + else + { + // TODO: + } +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Inputs +//---------------------------------------------------------------------------------- + +// Set internal gamepad mappings +int SetGamepadMappings(const char *mappings) +{ + TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform"); + return 0; +} + +// Set mouse position XY +void SetMousePosition(int x, int y) +{ + RGFW_window_moveMouse(platform.window, RGFW_VECTOR(x, y)); + CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; +} + +// Set mouse cursor +void SetMouseCursor(int cursor) +{ + switch (cursor) { + case MOUSE_CURSOR_DEFAULT: + return RGFW_window_setMouseDefault(platform.window); + case MOUSE_CURSOR_ARROW: + return RGFW_window_setMouseStandard(platform.window, RGFW_MOUSE_ARROW); + case MOUSE_CURSOR_IBEAM: + return RGFW_window_setMouseStandard(platform.window, RGFW_MOUSE_IBEAM); + case MOUSE_CURSOR_CROSSHAIR: + return RGFW_window_setMouseStandard(platform.window, RGFW_MOUSE_CROSSHAIR); + case MOUSE_CURSOR_POINTING_HAND: + return RGFW_window_setMouseStandard(platform.window, RGFW_MOUSE_POINTING_HAND); + case MOUSE_CURSOR_RESIZE_EW: + return RGFW_window_setMouseStandard(platform.window, RGFW_MOUSE_RESIZE_EW); + case MOUSE_CURSOR_RESIZE_NS: + return RGFW_window_setMouseStandard(platform.window, RGFW_MOUSE_RESIZE_NS); + #ifndef RGFW_MACOS + case MOUSE_CURSOR_RESIZE_NWSE: + return RGFW_window_setMouseStandard(platform.window, RGFW_MOUSE_RESIZE_NWSE); + case MOUSE_CURSOR_RESIZE_NESW: + return RGFW_window_setMouseStandard(platform.window, RGFW_MOUSE_RESIZE_NESW); + #endif + case MOUSE_CURSOR_RESIZE_ALL: + return RGFW_window_setMouseStandard(platform.window, RGFW_MOUSE_RESIZE_ALL); + case MOUSE_CURSOR_NOT_ALLOWED: + return RGFW_window_setMouseStandard(platform.window, RGFW_MOUSE_NOT_ALLOWED); + default: + break; + } +} + +static KeyboardKey ConvertScancodeToKey(u32 keycode); + +// Register all input events +void PollInputEvents(void) +{ +#if defined(SUPPORT_GESTURES_SYSTEM) + // NOTE: Gestures update must be called every frame to reset gestures correctly + // because ProcessGestureEvent() is just called on an event, not every frame + UpdateGestures(); +#endif + + // Reset keys/chars pressed registered + CORE.Input.Keyboard.keyPressedQueueCount = 0; + CORE.Input.Keyboard.charPressedQueueCount = 0; + + // Reset mouse wheel + CORE.Input.Mouse.currentWheelMove.x = 0; + CORE.Input.Mouse.currentWheelMove.y = 0; + + // Register previous mouse position + + // Reset last gamepad button/axis registered state + + for (int i = 0; (i < 4) && (i < MAX_GAMEPADS); i++) + { + // Check if gamepad is available + if (CORE.Input.Gamepad.ready[i]) + { + // Register previous gamepad button states + for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) + { + CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k]; + } + } + } + + // Register previous touch states + for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; + + // Map touch position to mouse position for convenience + CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; + + int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE + bool realTouch = false; // Flag to differentiate real touch gestures from mouse ones + + // Register previous keys states + // NOTE: Android supports up to 260 keys + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) + { + CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } + + // Register previous mouse states + for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) + CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; + + // Poll input events for current platform + //----------------------------------------------------------------------------- + CORE.Window.resizedLastFrame = false; + + + #define RGFW_HOLD_MOUSE (1L<<2) + #if defined(RGFW_X11) //|| defined(RGFW_MACOS) + if (platform.window->src.winArgs & RGFW_HOLD_MOUSE) + { + CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f }; + CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f }; + } + else { + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + } + #endif + + while (RGFW_window_checkEvent(platform.window)) + { + + if (platform.window->event.type >= RGFW_jsButtonPressed && platform.window->event.type <= RGFW_jsAxisMove) { + if (!CORE.Input.Gamepad.ready[platform.window->event.joystick]) + { + CORE.Input.Gamepad.ready[platform.window->event.joystick] = true; + CORE.Input.Gamepad.axisCount[platform.window->event.joystick] = platform.window->event.axisesCount; + CORE.Input.Gamepad.name[platform.window->event.joystick][0] = '\0'; + CORE.Input.Gamepad.axisState[platform.window->event.joystick][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f; + CORE.Input.Gamepad.axisState[platform.window->event.joystick][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f; + } + } + + RGFW_Event* event = &platform.window->event; + + // All input events can be processed after polling + switch (event->type) + { + case RGFW_quit: CORE.Window.shouldClose = true; break; + + case RGFW_dnd: // Dropped file + { + size_t i; + for (i = 0; i < event->droppedFilesCount; i++) { + if (CORE.Window.dropFileCount == 0) + { + // When a new file is dropped, we reserve a fixed number of slots for all possible dropped files + // at the moment we limit the number of drops at once to 1024 files but this behaviour should probably be reviewed + // TODO: Pointers should probably be reallocated for any new file added... + CORE.Window.dropFilepaths = (char **)RL_CALLOC(1024, sizeof(char *)); + + CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); + strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event->droppedFiles[i]); + + CORE.Window.dropFileCount++; + } + else if (CORE.Window.dropFileCount < 1024) + { + CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); + strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event->droppedFiles[i]); + + CORE.Window.dropFileCount++; + } + else TRACELOG(LOG_WARNING, "FILE: Maximum drag and drop files at once is limited to 1024 files!"); + } + } break; + + // Window events are also polled (Minimized, maximized, close...) + case RGFW_windowAttribsChange: + { + SetupViewport(platform.window->r.w, platform.window->r.h); + CORE.Window.position.x = platform.window->r.x; + CORE.Window.position.y = platform.window->r.x; + CORE.Window.screen.width = platform.window->r.w; + CORE.Window.screen.height = platform.window->r.h; + CORE.Window.currentFbo.width = platform.window->r.w;; + CORE.Window.currentFbo.height = platform.window->r.h; + CORE.Window.resizedLastFrame = true; + } break; + + // Keyboard events + case RGFW_keyPressed: + { + KeyboardKey key = ConvertScancodeToKey(event->keyCode); + + if (key != KEY_NULL) { + // If key was up, add it to the key pressed queue + if ((CORE.Input.Keyboard.currentKeyState[key] == 0) && (CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE)) + { + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key; + CORE.Input.Keyboard.keyPressedQueueCount++; + } + + CORE.Input.Keyboard.currentKeyState[key] = 1; + } + + // TODO: Put exitKey verification outside the switch? + if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey]) + { + CORE.Window.shouldClose = true; + } + + // NOTE: event.text.text data comes an UTF-8 text sequence but we register codepoints (int) + // Check if there is space available in the queue + if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) + { + // Add character (codepoint) to the queue + CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = RGFW_keystrToChar(event->keyName); + CORE.Input.Keyboard.charPressedQueueCount++; + } + } break; + + case RGFW_keyReleased: + { + KeyboardKey key = ConvertScancodeToKey(event->keyCode); + if (key != KEY_NULL) CORE.Input.Keyboard.currentKeyState[key] = 0; + } break; + + // Check mouse events + case RGFW_mouseButtonPressed: + { + if (event->button == RGFW_mouseScrollUp || event->button == RGFW_mouseScrollDown) { + CORE.Input.Mouse.currentWheelMove.y = event->scroll; + break; + } + + int btn = event->button; + if (btn == RGFW_mouseLeft) btn = 1; + else if (btn == RGFW_mouseRight) btn = 2; + else if (btn == RGFW_mouseMiddle) btn = 3; + + CORE.Input.Mouse.currentButtonState[btn - 1] = 1; + CORE.Input.Touch.currentTouchState[btn - 1] = 1; + + touchAction = 1; + } break; + case RGFW_mouseButtonReleased: + { + + if (event->button == RGFW_mouseScrollUp || event->button == RGFW_mouseScrollDown) { + CORE.Input.Mouse.currentWheelMove.y = event->scroll; + break; + } + + int btn = event->button; + if (btn == RGFW_mouseLeft) btn = 1; + else if (btn == RGFW_mouseRight) btn = 2; + else if (btn == RGFW_mouseMiddle) btn = 3; + + CORE.Input.Mouse.currentButtonState[btn - 1] = 0; + CORE.Input.Touch.currentTouchState[btn - 1] = 0; + + touchAction = 0; + } break; + case RGFW_mousePosChanged: + { + if (platform.window->src.winArgs & RGFW_HOLD_MOUSE) { + + CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f }; + + if ((event->point.x - (platform.window->r.w / 2)) * 2) + CORE.Input.Mouse.previousPosition.x = CORE.Input.Mouse.currentPosition.x; + if ((event->point.y - (platform.window->r.h / 2)) * 2) + CORE.Input.Mouse.previousPosition.y = CORE.Input.Mouse.currentPosition.y; + + CORE.Input.Mouse.currentPosition.x = (event->point.x - (platform.window->r.w / 2)) * 2; + CORE.Input.Mouse.currentPosition.y = (event->point.y - (platform.window->r.h / 2)) * 2; + + RGFW_window_showMouse(platform.window, 1); + } + else { + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + CORE.Input.Mouse.currentPosition.x = (float)event->point.x; + CORE.Input.Mouse.currentPosition.y = (float)event->point.y; + } + + CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; + touchAction = 2; + } break; + + case RGFW_jsButtonPressed: + { + int button = -1; + + switch (event->button) + { + case RGFW_JS_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; + case RGFW_JS_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; + case RGFW_JS_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; + case RGFW_JS_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; + + case RGFW_JS_L1: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; + case RGFW_JS_R1: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; + + case RGFW_JS_L2: button = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break; + case RGFW_JS_R2: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break; + + case RGFW_JS_SELECT: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break; + case RGFW_JS_HOME: button = GAMEPAD_BUTTON_MIDDLE; break; + case RGFW_JS_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; + + case RGFW_JS_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break; + case RGFW_JS_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; + case RGFW_JS_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; + case RGFW_JS_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; + + default: break; + } + + if (button >= 0) + { + CORE.Input.Gamepad.currentButtonState[event->joystick][button] = 1; + CORE.Input.Gamepad.lastButtonPressed = button; + } + } break; + case RGFW_jsButtonReleased: + { + int button = -1; + switch (event->button) + { + case RGFW_JS_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; + case RGFW_JS_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; + case RGFW_JS_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; + case RGFW_JS_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; + + case RGFW_JS_L1: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; + case RGFW_JS_R1: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; + + case RGFW_JS_L2: button = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break; + case RGFW_JS_R2: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break; + + case RGFW_JS_SELECT: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break; + case RGFW_JS_HOME: button = GAMEPAD_BUTTON_MIDDLE; break; + case RGFW_JS_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; + + case RGFW_JS_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break; + case RGFW_JS_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; + case RGFW_JS_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; + case RGFW_JS_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; + default: break; + } + + if (button >= 0) + { + CORE.Input.Gamepad.currentButtonState[event->joystick][button] = 0; + if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0; + } + } break; + case RGFW_jsAxisMove: + { + int axis = -1; + + size_t i; + for (i = 0; i < event->axisesCount; i++) + { + switch(i) { + case 0: + if (abs(event->axis[i].x) > abs(event->axis[i].y)) { + axis = GAMEPAD_AXIS_LEFT_X; + break; + } + + axis = GAMEPAD_AXIS_LEFT_Y; + break; + case 1: + if (abs(event->axis[i].x) > abs(event->axis[i].y)) { + axis = GAMEPAD_AXIS_RIGHT_X; break; + } + + axis = GAMEPAD_AXIS_RIGHT_Y; break; + case 2: axis = GAMEPAD_AXIS_LEFT_TRIGGER; break; + case 3: axis = GAMEPAD_AXIS_RIGHT_TRIGGER; break; + default: break; + } + + #ifdef __linux__ + float value = (event->axis[i].x + event->axis[i].y) / (float) 32767; + #else + float value = (event->axis[i].x + -event->axis[i].y) / (float) 32767; + #endif + CORE.Input.Gamepad.axisState[event->joystick][axis] = value; + + // Register button state for triggers in addition to their axes + if ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER)) + { + int button = (axis == GAMEPAD_AXIS_LEFT_TRIGGER) ? GAMEPAD_BUTTON_LEFT_TRIGGER_2 : GAMEPAD_BUTTON_RIGHT_TRIGGER_2; + int pressed = (value > 0.1f); + CORE.Input.Gamepad.currentButtonState[event->joystick][button] = pressed; + if (pressed) CORE.Input.Gamepad.lastButtonPressed = button; + else if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0; + } + } + } break; + default: break; + } + +#if defined(SUPPORT_GESTURES_SYSTEM) + if (touchAction > -1) + { + // Process mouse events as touches to be able to use mouse-gestures + GestureEvent gestureEvent = { 0 }; + + // Register touch actions + gestureEvent.touchAction = touchAction; + + // Assign a pointer ID + gestureEvent.pointId[0] = 0; + + // Register touch points count + gestureEvent.pointCount = 1; + + // Register touch points position, only one point registered + if (touchAction == 2 || realTouch) gestureEvent.position[0] = CORE.Input.Touch.position[0]; + else gestureEvent.position[0] = GetMousePosition(); + + // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height + gestureEvent.position[0].x /= (float)GetScreenWidth(); + gestureEvent.position[0].y /= (float)GetScreenHeight(); + + // Gesture data is sent to gestures-system for processing + ProcessGestureEvent(gestureEvent); + + touchAction = -1; + } +#endif + } + //----------------------------------------------------------------------------- +} + + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize platform: graphics, inputs and more +int InitPlatform(void) +{ + // TODO: Initialize graphic device: display/window + // It usually requires setting up the platform display system configuration + // and connexion with the GPU through some system graphic API + // raylib uses OpenGL so, platform should create that kind of connection + // Below example illustrates that process using EGL library + //---------------------------------------------------------------------------- + // Initialize RGFW internal global state, only required systems + // Initialize graphic device: display/window and graphic context + //---------------------------------------------------------------------------- + unsigned int flags = RGFW_CENTER | RGFW_ALLOW_DND; + + // Check window creation flags + if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) + { + CORE.Window.fullscreen = true; + flags |= RGFW_FULLSCREEN; + } + + if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) flags |= RGFW_NO_BORDER; + if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) flags |= RGFW_NO_RESIZE; + + if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) flags |= RGFW_TRANSPARENT_WINDOW; + + if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) flags |= RGFW_FULLSCREEN; + + // NOTE: Some OpenGL context attributes must be set before window creation + + // Check selection OpenGL version + if (rlGetVersion() == RL_OPENGL_21) + { + RGFW_setGLVersion(2, 1); + } + else if (rlGetVersion() == RL_OPENGL_33) + { + RGFW_setGLVersion(3, 3); + } + else if (rlGetVersion() == RL_OPENGL_43) + { + RGFW_setGLVersion(4, 1); + } + + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + RGFW_setGLSamples(4); + } + + platform.window = RGFW_createWindow(CORE.Window.title, RGFW_RECT(0, 0, CORE.Window.screen.width, CORE.Window.screen.height), flags); + + if (CORE.Window.flags & FLAG_VSYNC_HINT) + RGFW_window_swapInterval(platform.window, 1); + + RGFW_window_makeCurrent(platform.window); + + // Check surface and context activation + if (platform.window != NULL) + { + CORE.Window.ready = true; + + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + } + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + return -1; + } + //---------------------------------------------------------------------------- + + // If everything work as expected, we can continue + CORE.Window.position.x = platform.window->r.x; + CORE.Window.position.y = platform.window->r.y; + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + + // TODO: Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + //---------------------------------------------------------------------------- + rlLoadExtensions((void*)RGFW_getProcAddress); + //---------------------------------------------------------------------------- + + // TODO: Initialize input events system + // It could imply keyboard, mouse, gamepad, touch... + // Depending on the platform libraries/SDK it could use a callback mechanism + // For system events and inputs evens polling on a per-frame basis, use PollInputEvents() + //---------------------------------------------------------------------------- + // ... + //---------------------------------------------------------------------------- + + // TODO: Initialize timing system + //---------------------------------------------------------------------------- + InitTimer(); + //---------------------------------------------------------------------------- + + // TODO: Initialize storage system + //---------------------------------------------------------------------------- + CORE.Storage.basePath = GetWorkingDirectory(); + //---------------------------------------------------------------------------- + + #ifdef RGFW_X11 + for (int i = 0; (i < 4) && (i < MAX_GAMEPADS); i++) + { + RGFW_registerJoystick(platform.window, i); + } + #endif + + TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Initialized successfully"); + + return 0; +} + +// Close platform +void ClosePlatform(void) +{ + RGFW_window_close(platform.window); + // TODO: De-initialize graphics, inputs and more +} + + +static KeyboardKey ConvertScancodeToKey(u32 keycode) { + switch (keycode) { + case RGFW_Quote: + return KEY_APOSTROPHE; + case RGFW_Comma: + return KEY_COMMA; + case RGFW_Minus: + return KEY_MINUS; + case RGFW_Period: + return KEY_PERIOD; + case RGFW_Slash: + return KEY_SLASH; + case RGFW_Escape: + return KEY_ESCAPE; + case RGFW_F1: + return KEY_F1; + case RGFW_F2: + return KEY_F2; + case RGFW_F3: + return KEY_F3; + case RGFW_F4: + return KEY_F4; + case RGFW_F5: + return KEY_F5; + case RGFW_F6: + return KEY_F6; + case RGFW_F7: + return KEY_F7; + case RGFW_F8: + return KEY_F8; + case RGFW_F9: + return KEY_F9; + case RGFW_F10: + return KEY_F10; + case RGFW_F11: + return KEY_F11; + case RGFW_F12: + return KEY_F12; + case RGFW_Backtick: + return KEY_GRAVE; + case RGFW_0: + return KEY_ZERO; + case RGFW_1: + return KEY_ONE; + case RGFW_2: + return KEY_TWO; + case RGFW_3: + return KEY_THREE; + case RGFW_4: + return KEY_FOUR; + case RGFW_5: + return KEY_FIVE; + case RGFW_6: + return KEY_SIX; + case RGFW_7: + return KEY_SEVEN; + case RGFW_8: + return KEY_EIGHT; + case RGFW_9: + return KEY_NINE; + case RGFW_Equals: + return KEY_EQUAL; + case RGFW_BackSpace: + return KEY_BACKSPACE; + case RGFW_Tab: + return KEY_TAB; + case RGFW_CapsLock: + return KEY_CAPS_LOCK; + case RGFW_ShiftL: + return KEY_LEFT_SHIFT; + case RGFW_ControlL: + return KEY_LEFT_CONTROL; + case RGFW_AltL: + return KEY_LEFT_ALT; + case RGFW_SuperL: + return KEY_LEFT_SUPER; + #ifndef RGFW_MACOS + case RGFW_ShiftR: + return KEY_RIGHT_SHIFT; + + case RGFW_AltR: + return KEY_RIGHT_ALT; + #endif + case RGFW_Space: + return KEY_SPACE; + + #ifdef RGFW_X11 + case RGFW_a: + #endif + + case RGFW_A: + return KEY_A; + + #ifdef RGFW_X11 + case RGFW_b: + #endif + + case RGFW_B: + return KEY_B; + + #ifdef RGFW_X11 + case RGFW_c: + #endif + + case RGFW_C: + return KEY_C; + + #ifdef RGFW_X11 + case RGFW_d: + #endif + + case RGFW_D: + return KEY_D; + + #ifdef RGFW_X11 + case RGFW_e: + #endif + + case RGFW_E: + return KEY_E; + + #ifdef RGFW_X11 + case RGFW_f: + #endif + + case RGFW_F: + return KEY_F; + + #ifdef RGFW_X11 + case RGFW_g: + #endif + + case RGFW_G: + return KEY_G; + + #ifdef RGFW_X11 + case RGFW_h: + #endif + + case RGFW_H: + return KEY_H; + + #ifdef RGFW_X11 + case RGFW_i: + #endif + + case RGFW_I: + return KEY_I; + + #ifdef RGFW_X11 + case RGFW_j: + #endif + + case RGFW_J: + return KEY_J; + + #ifdef RGFW_X11 + case RGFW_k: + #endif + + case RGFW_K: + return KEY_K; + + #ifdef RGFW_X11 + case RGFW_l: + #endif + + case RGFW_L: + return KEY_L; + + #ifdef RGFW_X11 + case RGFW_m: + #endif + + case RGFW_M: + return KEY_M; + + #ifdef RGFW_X11 + case RGFW_n: + #endif + + case RGFW_N: + return KEY_N; + + #ifdef RGFW_X11 + case RGFW_o: + #endif + + case RGFW_O: + return KEY_O; + + #ifdef RGFW_X11 + case RGFW_p: + #endif + + case RGFW_P: + return KEY_P; + + #ifdef RGFW_X11 + case RGFW_q: + #endif + + case RGFW_Q: + return KEY_Q; + + #ifdef RGFW_X11 + case RGFW_r: + #endif + + case RGFW_R: + return KEY_R; + + #ifdef RGFW_X11 + case RGFW_s: + #endif + + case RGFW_S: + return KEY_S; + + #ifdef RGFW_X11 + case RGFW_t: + #endif + + case RGFW_T: + return KEY_T; + + #ifdef RGFW_X11 + case RGFW_u: + #endif + + case RGFW_U: + return KEY_U; + + #ifdef RGFW_X11 + case RGFW_v: + #endif + + case RGFW_V: + return KEY_V; + + #ifdef RGFW_X11 + case RGFW_w: + #endif + + case RGFW_W: + return KEY_W; + + #ifdef RGFW_X11 + case RGFW_x: + #endif + + case RGFW_X: + return KEY_X; + + #ifdef RGFW_X11 + case RGFW_y: + #endif + + case RGFW_Y: + return KEY_Y; + + #ifdef RGFW_X11 + case RGFW_z: + #endif + + case RGFW_Z: + return KEY_Z; + case RGFW_Bracket: + return KEY_LEFT_BRACKET; + case RGFW_BackSlash: + return KEY_BACKSLASH; + case RGFW_CloseBracket: + return KEY_RIGHT_BRACKET; + case RGFW_Semicolon: + return KEY_SEMICOLON; + case RGFW_Insert: + return KEY_INSERT; + case RGFW_Home: + return KEY_HOME; + case RGFW_PageUp: + return KEY_PAGE_UP; + case RGFW_Delete: + return KEY_DELETE; + case RGFW_End: + return KEY_END; + case RGFW_PageDown: + return KEY_PAGE_DOWN; + case RGFW_Right: + return KEY_RIGHT; + case RGFW_Left: + return KEY_LEFT; + case RGFW_Down: + return KEY_DOWN; + case RGFW_Up: + return KEY_UP; + case RGFW_Numlock: + return KEY_NUM_LOCK; + case RGFW_KP_Slash: + return KEY_KP_DIVIDE; + case RGFW_Multiply: + return KEY_KP_MULTIPLY; + case RGFW_KP_Minus: + return KEY_KP_SUBTRACT; + case RGFW_KP_Return: + return KEY_KP_ENTER; + case RGFW_KP_1: + return KEY_KP_1; + case RGFW_KP_2: + return KEY_KP_2; + case RGFW_KP_3: + return KEY_KP_3; + case RGFW_KP_4: + return KEY_KP_4; + case RGFW_KP_5: + return KEY_KP_5; + case RGFW_KP_6: + return KEY_KP_6; + case RGFW_KP_7: + return KEY_KP_7; + case RGFW_KP_8: + return KEY_KP_8; + case RGFW_KP_9: + return KEY_KP_9; + case RGFW_KP_0: + return KEY_KP_0; + case RGFW_KP_Period: + return KEY_KP_DECIMAL; + default: + return 0; + } + + return 0; +} +// EOF \ No newline at end of file diff --git a/src/rcore.c b/src/rcore.c index b229589f7..1db865187 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -154,13 +154,15 @@ #endif // Platform specific defines to handle GetApplicationDirectory() -#if defined(_WIN32) +#if (defined(_WIN32) && !defined(PLATFORM_DESKTOP_RGFW)) || (defined(_MSC_VER) && defined(PLATFORM_DESKTOP_RGFW)) #ifndef MAX_PATH #define MAX_PATH 1025 #endif __declspec(dllimport) unsigned long __stdcall GetModuleFileNameA(void *hModule, void *lpFilename, unsigned long nSize); __declspec(dllimport) unsigned long __stdcall GetModuleFileNameW(void *hModule, void *lpFilename, unsigned long nSize); __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, void *widestr, int cchwide, void *str, int cbmb, void *defchar, int *used_default); +unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod); +unsigned int __stdcall timeEndPeriod(unsigned int uPeriod); #elif defined(__linux__) #include #elif defined(__APPLE__) @@ -482,7 +484,7 @@ static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *li static void RecordAutomationEvent(void); // Record frame events (to internal events array) #endif -#if defined(_WIN32) +#if defined(_WIN32) && !defined(PLATFORM_DESKTOP_RGFW) // NOTE: We declare Sleep() function symbol to avoid including windows.h (kernel32.lib linkage required) void __stdcall Sleep(unsigned long msTimeout); // Required for: WaitTime() #endif @@ -496,6 +498,8 @@ const char *TextFormat(const char *text, ...); // Formatting of tex #include "platforms/rcore_desktop.c" #elif defined(PLATFORM_DESKTOP_SDL) #include "platforms/rcore_desktop_sdl.c" +#elif defined(PLATFORM_DESKTOP_RGFW) + #include "platforms/rcore_desktop_rgfw.c" #elif defined(PLATFORM_WEB) #include "platforms/rcore_web.c" #elif defined(PLATFORM_DRM) From 08ae888722e6ee6c54555ed33c0db9f9e691b2e1 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 7 May 2024 20:40:41 +0200 Subject: [PATCH 07/11] Update Makefile --- examples/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Makefile b/examples/Makefile index 52945c4b4..3c39e09e5 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -50,7 +50,7 @@ # Define required environment variables #------------------------------------------------------------------------------------------------ # Define target platform: PLATFORM_DESKTOP, PLATFORM_DESKTOP_SDL, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB -PLATFORM ?= PLATFORM_DESKTOP_RGFW +PLATFORM ?= PLATFORM_DESKTOP # Define required raylib variables PROJECT_NAME ?= raylib_examples From 1aa35591555ff3d0338e57d4ad320b437f52531b Mon Sep 17 00:00:00 2001 From: veins1 <19636663+veins1@users.noreply.github.com> Date: Tue, 7 May 2024 23:44:41 +0500 Subject: [PATCH 08/11] Fixes for loading Music (#3966) * Fixes for loading Music Fix for #3889 Fixes for QOA crashes. Memory leak FIX on unsuccessful .wav loading. * Added comments --- src/raudio.c | 193 +++++++++++++++++++++++++-------------------------- 1 file changed, 96 insertions(+), 97 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 032cfadeb..9d7c39a5b 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1334,11 +1334,10 @@ Music LoadMusicStream(const char *fileName) drwav *ctxWav = RL_CALLOC(1, sizeof(drwav)); bool success = drwav_init_file(ctxWav, fileName, NULL); - music.ctxType = MUSIC_AUDIO_WAV; - music.ctxData = ctxWav; - if (success) { + music.ctxType = MUSIC_AUDIO_WAV; + music.ctxData = ctxWav; int sampleSize = ctxWav->bitsPerSample; if (ctxWav->bitsPerSample == 24) sampleSize = 16; // Forcing conversion to s16 on UpdateMusicStream() @@ -1347,17 +1346,23 @@ Music LoadMusicStream(const char *fileName) music.looping = true; // Looping enabled by default musicLoaded = true; } + else + { + drwav_uninit(ctxWav); + RL_FREE(ctxWav); + } } #endif #if defined(SUPPORT_FILEFORMAT_OGG) else if (IsFileExtension(fileName, ".ogg")) { // Open ogg audio stream - music.ctxType = MUSIC_AUDIO_OGG; - music.ctxData = stb_vorbis_open_filename(fileName, NULL, NULL); + stb_vorbis *ctxOgg = stb_vorbis_open_filename(fileName, NULL, NULL); - if (music.ctxData != NULL) + if (ctxOgg != NULL) { + music.ctxType = MUSIC_AUDIO_OGG; + music.ctxData = ctxOgg; stb_vorbis_info info = stb_vorbis_get_info((stb_vorbis *)music.ctxData); // Get Ogg file info // OGG bit rate defaults to 16 bit, it's enough for compressed format @@ -1368,6 +1373,10 @@ Music LoadMusicStream(const char *fileName) music.looping = true; // Looping enabled by default musicLoaded = true; } + else + { + stb_vorbis_close(ctxOgg); + } } #endif #if defined(SUPPORT_FILEFORMAT_MP3) @@ -1376,27 +1385,30 @@ Music LoadMusicStream(const char *fileName) drmp3 *ctxMp3 = RL_CALLOC(1, sizeof(drmp3)); int result = drmp3_init_file(ctxMp3, fileName, NULL); - music.ctxType = MUSIC_AUDIO_MP3; - music.ctxData = ctxMp3; - if (result > 0) { + music.ctxType = MUSIC_AUDIO_MP3; + music.ctxData = ctxMp3; music.stream = LoadAudioStream(ctxMp3->sampleRate, 32, ctxMp3->channels); music.frameCount = (unsigned int)drmp3_get_pcm_frame_count(ctxMp3); music.looping = true; // Looping enabled by default musicLoaded = true; } + else + { + RL_FREE(ctxMp3); + } } #endif #if defined(SUPPORT_FILEFORMAT_QOA) else if (IsFileExtension(fileName, ".qoa")) { qoaplay_desc *ctxQoa = qoaplay_open(fileName); - music.ctxType = MUSIC_AUDIO_QOA; - music.ctxData = ctxQoa; - if (ctxQoa->file != NULL) + if (ctxQoa != NULL) { + music.ctxType = MUSIC_AUDIO_QOA; + music.ctxData = ctxQoa; // NOTE: We are loading samples are 32bit float normalized data, so, // we configure the output audio stream to also use float 32bit music.stream = LoadAudioStream(ctxQoa->info.samplerate, 32, ctxQoa->info.channels); @@ -1404,23 +1416,27 @@ Music LoadMusicStream(const char *fileName) music.looping = true; // Looping enabled by default musicLoaded = true; } + else{} //No uninit required } #endif #if defined(SUPPORT_FILEFORMAT_FLAC) else if (IsFileExtension(fileName, ".flac")) { - music.ctxType = MUSIC_AUDIO_FLAC; - music.ctxData = drflac_open_file(fileName, NULL); + drflac *ctxFlac = drflac_open_file(fileName, NULL); - if (music.ctxData != NULL) + if (ctxFlac != NULL) { - drflac *ctxFlac = (drflac *)music.ctxData; - + music.ctxType = MUSIC_AUDIO_FLAC; + music.ctxData = ctxFlac; music.stream = LoadAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels); music.frameCount = (unsigned int)ctxFlac->totalPCMFrameCount; music.looping = true; // Looping enabled by default musicLoaded = true; } + else + { + drflac_free(ctxFlac, NULL); + } } #endif #if defined(SUPPORT_FILEFORMAT_XM) @@ -1429,11 +1445,10 @@ Music LoadMusicStream(const char *fileName) jar_xm_context_t *ctxXm = NULL; int result = jar_xm_create_context_from_file(&ctxXm, AUDIO.System.device.sampleRate, fileName); - music.ctxType = MUSIC_MODULE_XM; - music.ctxData = ctxXm; - if (result == 0) // XM AUDIO.System.context created successfully { + music.ctxType = MUSIC_MODULE_XM; + music.ctxData = ctxXm; jar_xm_set_max_loop_count(ctxXm, 0); // Set infinite number of loops unsigned int bits = 32; @@ -1447,6 +1462,10 @@ Music LoadMusicStream(const char *fileName) jar_xm_reset(ctxXm); // Make sure we start at the beginning of the song musicLoaded = true; } + else + { + jar_xm_free_context(ctxXm); + } } #endif #if defined(SUPPORT_FILEFORMAT_MOD) @@ -1456,48 +1475,27 @@ Music LoadMusicStream(const char *fileName) jar_mod_init(ctxMod); int result = jar_mod_load_file(ctxMod, fileName); - music.ctxType = MUSIC_MODULE_MOD; - music.ctxData = ctxMod; - if (result > 0) { + music.ctxType = MUSIC_MODULE_MOD; + music.ctxData = ctxMod; // NOTE: Only stereo is supported for MOD music.stream = LoadAudioStream(AUDIO.System.device.sampleRate, 16, AUDIO_DEVICE_CHANNELS); music.frameCount = (unsigned int)jar_mod_max_samples(ctxMod); // NOTE: Always 2 channels (stereo) music.looping = true; // Looping enabled by default musicLoaded = true; } + else + { + jar_mod_unload(ctxMod); + RL_FREE(ctxMod); + } } #endif else TRACELOG(LOG_WARNING, "STREAM: [%s] File format not supported", fileName); if (!musicLoaded) { - if (false) { } - #if defined(SUPPORT_FILEFORMAT_WAV) - else if (music.ctxType == MUSIC_AUDIO_WAV) drwav_uninit((drwav *)music.ctxData); - #endif - #if defined(SUPPORT_FILEFORMAT_OGG) - else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData); - #endif - #if defined(SUPPORT_FILEFORMAT_MP3) - else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); } - #endif - #if defined(SUPPORT_FILEFORMAT_QOA) - else if (music.ctxType == MUSIC_AUDIO_QOA) qoaplay_close((qoaplay_desc *)music.ctxData); - #endif - #if defined(SUPPORT_FILEFORMAT_FLAC) - else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL); - #endif - #if defined(SUPPORT_FILEFORMAT_XM) - else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData); - #endif - #if defined(SUPPORT_FILEFORMAT_MOD) - else if (music.ctxType == MUSIC_MODULE_MOD) { jar_mod_unload((jar_mod_context_t *)music.ctxData); RL_FREE(music.ctxData); } - #endif - - music.ctxData = NULL; - music.ctxType = MUSIC_AUDIO_NONE; TRACELOG(LOG_WARNING, "FILEIO: [%s] Music file could not be opened", fileName); } else @@ -1528,11 +1526,10 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, bool success = drwav_init_memory(ctxWav, (const void *)data, dataSize, NULL); - music.ctxType = MUSIC_AUDIO_WAV; - music.ctxData = ctxWav; - if (success) { + music.ctxType = MUSIC_AUDIO_WAV; + music.ctxData = ctxWav; int sampleSize = ctxWav->bitsPerSample; if (ctxWav->bitsPerSample == 24) sampleSize = 16; // Forcing conversion to s16 on UpdateMusicStream() @@ -1541,18 +1538,22 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, music.looping = true; // Looping enabled by default musicLoaded = true; } + else { + drwav_uninit(ctxWav); + RL_FREE(ctxWav); + } } #endif #if defined(SUPPORT_FILEFORMAT_OGG) else if ((strcmp(fileType, ".ogg") == 0) || (strcmp(fileType, ".OGG") == 0)) { // Open ogg audio stream - music.ctxType = MUSIC_AUDIO_OGG; - //music.ctxData = stb_vorbis_open_filename(fileName, NULL, NULL); - music.ctxData = stb_vorbis_open_memory((const unsigned char *)data, dataSize, NULL, NULL); + stb_vorbis* ctxOgg = stb_vorbis_open_memory((const unsigned char*)data, dataSize, NULL, NULL); - if (music.ctxData != NULL) + if (ctxOgg != NULL) { + music.ctxType = MUSIC_AUDIO_OGG; + music.ctxData = ctxOgg; stb_vorbis_info info = stb_vorbis_get_info((stb_vorbis *)music.ctxData); // Get Ogg file info // OGG bit rate defaults to 16 bit, it's enough for compressed format @@ -1563,6 +1564,10 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, music.looping = true; // Looping enabled by default musicLoaded = true; } + else + { + stb_vorbis_close(ctxOgg); + } } #endif #if defined(SUPPORT_FILEFORMAT_MP3) @@ -1571,27 +1576,35 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, drmp3 *ctxMp3 = RL_CALLOC(1, sizeof(drmp3)); int success = drmp3_init_memory(ctxMp3, (const void*)data, dataSize, NULL); - music.ctxType = MUSIC_AUDIO_MP3; - music.ctxData = ctxMp3; - if (success) { + music.ctxType = MUSIC_AUDIO_MP3; + music.ctxData = ctxMp3; music.stream = LoadAudioStream(ctxMp3->sampleRate, 32, ctxMp3->channels); music.frameCount = (unsigned int)drmp3_get_pcm_frame_count(ctxMp3); music.looping = true; // Looping enabled by default musicLoaded = true; } + else + { + drmp3_uninit(ctxMp3); + RL_FREE(ctxMp3); + } } #endif #if defined(SUPPORT_FILEFORMAT_QOA) else if ((strcmp(fileType, ".qoa") == 0) || (strcmp(fileType, ".QOA") == 0)) { - qoaplay_desc *ctxQoa = qoaplay_open_memory(data, dataSize); - music.ctxType = MUSIC_AUDIO_QOA; - music.ctxData = ctxQoa; - - if ((ctxQoa->file_data != NULL) && (ctxQoa->file_data_size != 0)) + qoaplay_desc *ctxQoa = NULL; + if ((data != NULL) && (dataSize > 0)) { + ctxQoa = qoaplay_open_memory(data, dataSize); + } + + if (ctxQoa != NULL) + { + music.ctxType = MUSIC_AUDIO_QOA; + music.ctxData = ctxQoa; // NOTE: We are loading samples are 32bit float normalized data, so, // we configure the output audio stream to also use float 32bit music.stream = LoadAudioStream(ctxQoa->info.samplerate, 32, ctxQoa->info.channels); @@ -1599,23 +1612,27 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, music.looping = true; // Looping enabled by default musicLoaded = true; } + else{} //No uninit required } #endif #if defined(SUPPORT_FILEFORMAT_FLAC) else if ((strcmp(fileType, ".flac") == 0) || (strcmp(fileType, ".FLAC") == 0)) { - music.ctxType = MUSIC_AUDIO_FLAC; - music.ctxData = drflac_open_memory((const void*)data, dataSize, NULL); + drflac *ctxFlac = drflac_open_memory((const void*)data, dataSize, NULL); - if (music.ctxData != NULL) + if (ctxFlac != NULL) { - drflac *ctxFlac = (drflac *)music.ctxData; - + music.ctxType = MUSIC_AUDIO_FLAC; + music.ctxData = ctxFlac; music.stream = LoadAudioStream(ctxFlac->sampleRate, ctxFlac->bitsPerSample, ctxFlac->channels); music.frameCount = (unsigned int)ctxFlac->totalPCMFrameCount; music.looping = true; // Looping enabled by default musicLoaded = true; } + else + { + drflac_free(ctxFlac, NULL); + } } #endif #if defined(SUPPORT_FILEFORMAT_XM) @@ -1626,6 +1643,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, if (result == 0) // XM AUDIO.System.context created successfully { music.ctxType = MUSIC_MODULE_XM; + music.ctxData = ctxXm; jar_xm_set_max_loop_count(ctxXm, 0); // Set infinite number of loops unsigned int bits = 32; @@ -1638,9 +1656,12 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, music.looping = true; // Looping enabled by default jar_xm_reset(ctxXm); // Make sure we start at the beginning of the song - music.ctxData = ctxXm; musicLoaded = true; } + else + { + jar_xm_free_context(ctxXm); + } } #endif #if defined(SUPPORT_FILEFORMAT_MOD) @@ -1667,15 +1688,18 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, if (result > 0) { music.ctxType = MUSIC_MODULE_MOD; + music.ctxData = ctxMod; // NOTE: Only stereo is supported for MOD music.stream = LoadAudioStream(AUDIO.System.device.sampleRate, 16, 2); music.frameCount = (unsigned int)jar_mod_max_samples(ctxMod); // NOTE: Always 2 channels (stereo) music.looping = true; // Looping enabled by default musicLoaded = true; - - music.ctxData = ctxMod; - musicLoaded = true; + } + else + { + jar_mod_unload(ctxMod); + RL_FREE(ctxMod); } } #endif @@ -1683,31 +1707,6 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, if (!musicLoaded) { - if (false) { } -#if defined(SUPPORT_FILEFORMAT_WAV) - else if (music.ctxType == MUSIC_AUDIO_WAV) drwav_uninit((drwav *)music.ctxData); -#endif -#if defined(SUPPORT_FILEFORMAT_OGG) - else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData); -#endif -#if defined(SUPPORT_FILEFORMAT_MP3) - else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); } -#endif -#if defined(SUPPORT_FILEFORMAT_QOA) - else if (music.ctxType == MUSIC_AUDIO_QOA) qoaplay_close((qoaplay_desc *)music.ctxData); -#endif -#if defined(SUPPORT_FILEFORMAT_FLAC) - else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL); -#endif -#if defined(SUPPORT_FILEFORMAT_XM) - else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData); -#endif -#if defined(SUPPORT_FILEFORMAT_MOD) - else if (music.ctxType == MUSIC_MODULE_MOD) { jar_mod_unload((jar_mod_context_t *)music.ctxData); RL_FREE(music.ctxData); } -#endif - - music.ctxData = NULL; - music.ctxType = MUSIC_AUDIO_NONE; TRACELOG(LOG_WARNING, "FILEIO: Music data could not be loaded"); } else From 6ec925554cc5c5297d74b9867331fae421966519 Mon Sep 17 00:00:00 2001 From: Colleague Riley <50218871+ColleagueRiley@users.noreply.github.com> Date: Thu, 9 May 2024 08:18:18 -0400 Subject: [PATCH 09/11] Fix Makefile issues (RGFW) (linux) (macOS) (#3969) Co-authored-by: ColleagueRiley --- src/Makefile | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Makefile b/src/Makefile index 89d81627a..23742565d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -592,12 +592,12 @@ endif ifeq ($(PLATFORM),PLATFORM_DESKTOP_RGFW) ifeq ($(PLATFORM_OS),WINDOWS) # Libraries for Windows desktop compilation - LDLIBS = ..\src\libraylib.a -lgdi32 -lwinmm -lopengl32 + LDLIBS = -lgdi32 -lwinmm -lopengl32 endif ifeq ($(PLATFORM_OS),LINUX) # Libraries for Debian GNU/Linux desktop compipling # NOTE: Required packages: libegl1-mesa-dev - LDLIBS = ../src/libraylib.a -lGL -lX11 -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor -lm -lpthread -ldl -lrt + LDLIBS = -lGL -lX11 -lXrandr -lXinerama -lXi -lXcursor -lm -lpthread -ldl -lrt # Explicit link to libc ifeq ($(RAYLIB_LIBTYPE),SHARED) @@ -608,10 +608,9 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP_RGFW) LDLIBS += -latomic endif ifeq ($(PLATFORM_OS),OSX) - # Libraries for Debian GNU/Linux desktop compiling + # Libraries for Debian MacOS desktop compiling # NOTE: Required packages: libegl1-mesa-dev - LDLIBS = ../src/libraylib.a -lm - LDLIBS += -framework Foundation -framework AppKit -framework OpenGL -framework CoreVideo + LDLIBS += -lm -framework Foundation -framework AppKit -framework OpenGL -framework CoreVideo endif endif From 8a5fd3ac1d481ff978363b80298770ed8eeca9f8 Mon Sep 17 00:00:00 2001 From: Colleague Riley <50218871+ColleagueRiley@users.noreply.github.com> Date: Thu, 9 May 2024 10:35:26 -0400 Subject: [PATCH 10/11] Do not use "nanosleep" on windows at all (PLATFORM_DESKTOP_RGFW) (#3970) * Fix Makefile issues (RGFW) (linux) (macOS) * Do not use nanosleep on windows at all (PLATFORM_DESKTOP_RGFW) --------- Co-authored-by: ColleagueRiley --- src/external/RGFW.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/external/RGFW.h b/src/external/RGFW.h index 13b68d98a..67c67e91c 100644 --- a/src/external/RGFW.h +++ b/src/external/RGFW.h @@ -2753,8 +2753,8 @@ typedef struct { i32 x, y; } RGFW_vector; 4, &actualType, &actualFormat, - &count, - &bytesAfter, + (unsigned long*) &count, + (unsigned long*) &bytesAfter, (u8**) &formats); } else { formats = (Atom*) RGFW_MALLOC(E.xclient.data.l[2] + E.xclient.data.l[3] + E.xclient.data.l[4]); @@ -3002,7 +3002,6 @@ typedef struct { i32 x, y; } RGFW_vector; win->src.winArgs &= ~RGFW_MOUSE_CHANGED; } - RGFW_vector mouse = RGFW_getGlobalMousePoint(); if (win->src.winArgs & RGFW_HOLD_MOUSE && win->event.inFocus && win->event.type == RGFW_mousePosChanged) { RGFW_window_moveMouse(win, RGFW_VECTOR(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2))); @@ -6074,7 +6073,7 @@ static HMODULE wglinstance = NULL; } void RGFW_sleep(u32 ms) { -#ifndef _MSC_VER +#ifndef RGFW_WINDOWS struct timespec time; time.tv_sec = 0; time.tv_nsec = ms * 1000; From f62202198e35161a9fe394a14bbd1a7c655107ad Mon Sep 17 00:00:00 2001 From: Colleague Riley Date: Fri, 10 May 2024 15:09:22 -0400 Subject: [PATCH 11/11] remove #define RGFWDEF and make the #undefs only happen for their OS (RGFW) (#3971) * Fix Makefile issues (RGFW) (linux) (macOS) * Do not use nanosleep on windows at all (PLATFORM_DESKTOP_RGFW) * remove #define RGFWDEF and make the #undefs only happen for their OS --- src/platforms/rcore_desktop_rgfw.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index e4f74938d..623dc0e76 100644 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -55,35 +55,46 @@ void ShowCursor(void); void CloseWindow(void); +#ifdef __linux__ #define _INPUT_EVENT_CODES_H -#define _APISETSTRING_ -#define RGFWDEF +#endif + +#if defined(__unix__) || defined(__linux__) #define _XTYPEDEF_FONT +#endif + #define RGFW_IMPLEMENTATION +#if defined(__WIN32) || defined(__WIN64) #define WIN32_LEAN_AND_MEAN #define Rectangle rectangle_win32 #define CloseWindow CloseWindow_win32 #define ShowCursor __imp_ShowCursor +#define _APISETSTRING_ +#endif +#ifdef __APPLE__ #define Point NSPOINT #define Size NSSIZE +#endif #ifdef _MSC_VER __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int CodePage, unsigned long dwFlags, const char* lpMultiByteStr, int cbMultiByte, wchar_t* lpWideCharStr, int cchWideChar); #endif #include "../external/RGFW.h" + +#if defined(__WIN32) || defined(__WIN64) #undef DrawText #undef ShowCursor #undef CloseWindow +#undef Rectangle +#endif + +#ifdef __APPLE__ #undef Point #undef Size - -#define Rectangle struct Rectangle -void CloseWindow(void); -void ShowCursor(void); - +#endif #include