diff --git a/.gitignore b/.gitignore
index f7b2cccf6..00a751a73 100644
--- a/.gitignore
+++ b/.gitignore
@@ -132,3 +132,8 @@ tools/rexm/rexm
# CI
emsdk-cache/
raylib.com/
+
+# Wayland files
+src/*protocol.h
+src/*protocol-code.h
+src/*protocol-code.c
diff --git a/BINDINGS.md b/BINDINGS.md
index 6c405ab66..4a7680964 100644
--- a/BINDINGS.md
+++ b/BINDINGS.md
@@ -32,7 +32,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers
| [DenoRaylib550](https://github.com/JJLDonley/DenoRaylib550) | **5.5** | [Deno](https://deno.land) | MIT |
| [rayex](https://github.com/shiryel/rayex) | 3.7 | [elixir](https://elixir-lang.org) | Apache-2.0 |
| [raylib-elle](https://github.com/acquitelol/elle/blob/rewrite/std/raylib.le) | **5.5** | [Elle](https://github.com/acquitelol/elle) | GPL-3.0 |
-| [raylib-factor](https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor) | 4.5 | [Factor](https://factorcode.org) | BSD |
+| [raylib-factor](https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor) | 5.5 | [Factor](https://factorcode.org) | BSD |
| [raylib-freebasic](https://github.com/WIITD/raylib-freebasic) | **5.0** | [FreeBASIC](https://www.freebasic.net) | MIT |
| [raylib.f](https://github.com/cthulhuology/raylib.f) | **5.5** | [Forth](https://forth.com) | Zlib |
| [fortran-raylib](https://github.com/interkosmos/fortran-raylib) | **5.5** | [Fortran](https://fortran-lang.org) | ISC |
@@ -59,7 +59,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers
| [raylib-bindings](https://github.com/vaiorabbit/raylib-bindings) | 5.6-dev | [Ruby](https://www.ruby-lang.org/en) | Zlib |
| [naylib](https://github.com/planetis-m/naylib) | **5.6-dev** | [Nim](https://nim-lang.org) | MIT |
| [node-raylib](https://github.com/RobLoach/node-raylib) | 4.5 | [Node.js](https://nodejs.org/en) | Zlib |
-| [raylib-odin](https://github.com/odin-lang/Odin/tree/master/vendor/raylib) | **5.5** | [Odin](https://odin-lang.org) | BSD-3Clause |
+| [raylib-odin](https://github.com/odin-lang/Odin/tree/master/vendor/raylib) | **5.5** | [Odin](https://odin-lang.org) | Zlib |
| [raylib_odin_bindings](https://github.com/Deathbat2190/raylib_odin_bindings) | 4.0-dev | [Odin](https://odin-lang.org) | MIT |
| [raylib-ocaml](https://github.com/tjammer/raylib-ocaml) | **5.0** | [OCaml](https://ocaml.org) | MIT |
| [TurboRaylib](https://github.com/turborium/TurboRaylib) | 4.5 | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT |
@@ -103,6 +103,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers
| [raylib-jai](https://github.com/ahmedqarmout2/raylib-jai) | **5.5** | [Jai](https://github.com/BSVino/JaiPrimer/blob/master/JaiPrimer.md) | MIT |
| [fnl-raylib](https://github.com/0riginaln0/fnl-raylib) | **5.5** | [Fennel](https://fennel-lang.org/) | MIT |
| [Rayua](https://github.com/uiua-lang/rayua) | **5.5** | [Uiua](https://www.uiua.org/) | **???** |
+| [Target](https://github.com/FinnDemonCat/Target/tree/main/libs/raylib) | **5.5** | [Dart](https://dart.dev/) | Apache-2.0 license |
### Utility Wrapers
diff --git a/CHANGELOG b/CHANGELOG
index 29e4d8174..9bfdbe484 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -3,6 +3,25 @@ changelog
Current Release: raylib 5.5 (18 November 2024)
+-------------------------------------------------------------------------
+Release: raylib 6.0 (?? March 2026)
+-------------------------------------------------------------------------
+KEY CHANGES:
+ - New Software Renderer backend [rlsw]
+ - New rcore platform backend: Memory (memory buffer)
+ - New rcore platform backend: Win32 (experimental)
+ - New rcore platform backend: Emscripten (experimental)
+ - Redesigned Skeletal Animation System
+ - Redesigned fullscreen modes and high-dpi content scaling
+ - Redesigned Build Config System [config.h]
+ - New File System API
+ - New Text Management API
+ - New tool: [rexm] raylib examples manager
+
+Detailed changes:
+
+TODO...
+
-------------------------------------------------------------------------
Release: raylib 5.5 (18 November 2024)
-------------------------------------------------------------------------
@@ -15,36 +34,34 @@ KEY CHANGES:
Detailed changes:
-WIP: Last update with commit from 02-Nov-2024
-
-[rcore] ADDED: Working directory info at initialization by @Ray
+[rcore] ADDED: Working directory info at initialization by @raysan5
[rcore] ADDED: `GetClipboardImage()`, supported by multiple backends (#4459) by @evertonse
-[rcore] ADDED: `MakeDirectory()`, supporting recursive directory creation by @Ray
+[rcore] ADDED: `MakeDirectory()`, supporting recursive directory creation by @raysan5
[rcore] ADDED: `ComputeSHA1()` (#4390) by @Anthony Carbajal
-[rcore] ADDED: `ComputeCRC32()` and `ComputeMD5()` by @Ray
+[rcore] ADDED: `ComputeCRC32()` and `ComputeMD5()` by @raysan5
[rcore] ADDED: `GetKeyName()` (#4161) by @MrScautHD
-[rcore] ADDED: `IsFileNameValid()` by @Ray
-[rcore] ADDED: `GetViewRay()`, viewport independent raycast (#3709) by @Luís Almeida
-[rcore] RENAMED: `GetMouseRay()` to `GetScreenToWorldRay()` (#3830) by @Ray
-[rcore] RENAMED: `GetViewRay()` to `GetScreenToWorldRayEx()` (#3830) by @Ray
-[rcore] REVIEWED: `GetApplicationDirectory()` for FreeBSD (#4318) by @base
+[rcore] ADDED: `IsFileNameValid()` by @raysan5
+[rcore] ADDED: `GetViewRay()`, viewport independent raycast (#3709) by @luis605
+[rcore] RENAMED: `GetMouseRay()` to `GetScreenToWorldRay()` (#3830) by @raysan5
+[rcore] RENAMED: `GetViewRay()` to `GetScreenToWorldRayEx()` (#3830) by @raysan5
+[rcore] REVIEWED: `GetApplicationDirectory()` for FreeBSD (#4318) by @git
[rcore] REVIEWED: `LoadDirectoryFilesEx()`/`ScanDirectoryFiles()`, support directory on filter (#4302) by @foxblock
-[rcore] REVIEWED: Update comments on fullscreen and boderless window to describe what they do (#4280) by @Jeffery Myers
-[rcore] REVIEWED: Correct processing of mouse wheel on Automation events #4263 by @Ray
+[rcore] REVIEWED: Update comments on fullscreen and boderless window to describe what they do (#4280) by @Jeffm2501
+[rcore] REVIEWED: Correct processing of mouse wheel on Automation events #4263 by @raysan5
[rcore] REVIEWED: Fix gamepad axis movement and its automation event recording (#4184) by @maxmutant
[rcore] REVIEWED: Do not set RL_TEXTURE_FILTER_LINEAR when high dpi flag is enabled (#4189) by @Dave Green
[rcore] REVIEWED: `GetScreenWidth()`/`GetScreenHeight()` (#4074) by @Anthony Carbajal
[rcore] REVIEWED: Initial window dimensions checks (#3950) by @Christian Haas
-[rcore] REVIEWED: Set default init values for random #3954 by @Ray
-[rcore] REVIEWED: Window positioning, avoid out-of-screen window-bar by @Ray
-[rcore] REVIEWED: Fix framerate recording for .gif (#3894) by @Rob Loach
+[rcore] REVIEWED: Set default init values for random #3954 by @raysan5
+[rcore] REVIEWED: Window positioning, avoid out-of-screen window-bar by @raysan5
+[rcore] REVIEWED: Fix framerate recording for .gif (#3894) by @RobLoach
[rcore] REVIEWED: Screen space related functions consistency (#3830) by @aiafrasinei
[rcore] REVIEWED: `GetFileNameWithoutExt()` (#3771) by @oblerion
-[rcore] REVIEWED: `GetWindowScaleDPI()`, simplified (#3701) by @Karl Zylinski
-[rcore] REVIEWED: `UnloadAutomationEventList()` (#3658) by @Antonis Geralis
+[rcore] REVIEWED: `GetWindowScaleDPI()`, simplified (#3701) by @karl
+[rcore] REVIEWED: `UnloadAutomationEventList()` (#3658) by @planetis-m
[rcore] REVIEWED: Flip VR screens (#3633) by @Matthew Oros
[rcore] REVIEWED: Remove unused vScreenCenter (#3632) by @Matthew Oros
-[rcore] REVIEWED: `LoadRandomSequence()`, issue in sequence generation #3612 by @Ray
+[rcore] REVIEWED: `LoadRandomSequence()`, issue in sequence generation #3612 by @raysan5
[rcore] REVIEWED: `IsMouseButtonUp()` (#3609) by @Kenneth M
[rcore] REVIEWED: Fix typos in src/platforms/rcore_*.c (#3581) by @RadsammyT
[rcore] REVIEWED: `ExportDataAsCode()`, change sanitization check (#3837) by @Laurentino Luna
@@ -52,17 +69,17 @@ WIP: Last update with commit from 02-Nov-2024
[rcore] REVIEWED: `GetScreenWidth()`/`GetScreenHeight()` align with all platforms (#4451) by @Arche Washi
[rcore] REVIEWED: `SetGamepadVibration()`, added duration parameter (#4410) by @Asdqwe -WARNING-
[rcore] REVIEWED: `GetGamepadAxisMovement()`, fix #4405 (#4420) by @Asdqwe
-[rcore] REVIEWED: `GetGestureHoldDuration()` comments by @Ray
-[rcore][rlgl] REVIEWED: Fix scale issues when ending a view mode (#3746) by @Jeffery Myers
+[rcore] REVIEWED: `GetGestureHoldDuration()` comments by @raysan5
+[rcore][rlgl] REVIEWED: Fix scale issues when ending a view mode (#3746) by @Jeffm2501
[rcore][GLFW] REVIEWED: Keep CORE.Window.position properly in sync with glfw window position (#4190) by @Dave Green
[rcore][GLFW] REVIEWED: Set AUTO_ICONIFY flag to false per default (#4188) by @Dave Green
[rcore][GLFW] REVIEWED: `InitPlatform()`, add workaround for NetBSD (#4139) by @NishiOwO
[rcore][GLFW] REVIEWED: Fix window not initializing on primary monitor (#3923) by @Rafael Bordoni
-[rcore][GLFW] REVIEWED: Set relative mouse mode when the cursor is disabled (#3874) by @Jeffery Myers
-[rcore][GLFW] REVIEWED: Remove GLFW mouse passthrough hack and increase GLFW version in CMake (#3852) by @Alexandre Almeida
-[rcore][GLFW] REVIEWED: Updated GLFW to 3.4 (#3827) by @Alexandre Almeida
+[rcore][GLFW] REVIEWED: Set relative mouse mode when the cursor is disabled (#3874) by @Jeffm2501
+[rcore][GLFW] REVIEWED: Remove GLFW mouse passthrough hack and increase GLFW version in CMake (#3852) by @M374LX
+[rcore][GLFW] REVIEWED: Updated GLFW to 3.4 (#3827) by @M374LX
[rcore][GLFW] REVIEWED: Feature test macros before include (#3737) by @John
-[rcore][GLFW] REVIEWED: Fix inconsistent dll linkage warning on windows (#4447) by @Jeffery Myers
+[rcore][GLFW] REVIEWED: Fix inconsistent dll linkage warning on windows (#4447) by @Jeffm2501
[rcore][Web] ADDED: `SetWindowOpacity()` implementation (#4403) by @Asdqwe
[rcore][Web] ADDED: `MaximizeWindow()` and `RestoreWindow()` implementations (#4397) by @Asdqwe
[rcore][Web] ADDED: `ToggleFullscreen()` implementation (#3634) by @ubkp
@@ -75,21 +92,21 @@ WIP: Last update with commit from 02-Nov-2024
[rcore][Web] REVIEWED: `ShowCursor()`, `HideCursor()` and `SetMouseCursor()` (#3647) by @ubkp
[rcore][Web] REVIEWED: Fix CORE.Input.Mouse.cursorHidden with callbacks (#3644) by @ubkp
[rcore][Web] REVIEWED: Fix `IsMouseButtonUp()` (#3611) by @ubkp
-[rcore][Web] REVIEWED: HighDPI support #3372 by @Ray
+[rcore][Web] REVIEWED: HighDPI support #3372 by @raysan5
[rcore][Web] REVIEWED: `SetWindowSize()` (#4452) by @Asdqwe
[rcore][Web] REVIEWED: `EmscriptenResizeCallback()`, simplified (#4415) by @Asdqwe
[rcore][SDL] ADDED: `IsCursorOnScreen()` (#3862) by @Peter0x44
-[rcore][SDL] ADDED: Gamepad rumble/vibration support (#3819) by @GideonSerf
+[rcore][SDL] ADDED: Gamepad rumble/vibration support (#3819) by @gdserf.gs
[rcore][SDL] REVIEWED: Gamepad support (#3776) by @A
[rcore][SDL] REVIEWED: `GetWorkingDirectory()`, return correct path (#4392) by @Asdqwe
[rcore][SDL] REVIEWED: `GetClipboardText()`, fix memory leak (#4354) by @Asdqwe
[rcore][SDL] REVIEWED: Change SDL_Joystick to SDL_GameController (#4129) by @Frank Kartheuser
-[rcore][SDL] REVIEWED: Update storage base path, use provided SDL base path by @Ray
+[rcore][SDL] REVIEWED: Update storage base path, use provided SDL base path by @raysan5
[rcore][SDL] REVIEWED: Call SDL_GL_SetSwapInterval() after GL context creation (#3997) by @JupiterRider
[rcore][SDL] REVIEWED: `GetKeyPressed()` (#3869) by @Arthur
[rcore][SDL] REVIEWED: Fix SDL multitouch tracking (#3810) by @mooff
[rcore][SDL] REVIEWED: Fix `SUPPORT_WINMM_HIGHRES_TIMER` (#3679) by @ubkp
-[rcore][SDL] REVIEWED: SDL text input to Unicode codepoints #3650 by @Ray
+[rcore][SDL] REVIEWED: SDL text input to Unicode codepoints #3650 by @raysan5
[rcore][SDL] REVIEWED: `IsMouseButtonUp()` and add touch events (#3610) by @ubkp
[rcore][SDL] REVIEWED: Fix real touch gestures (#3614) by @ubkp
[rcore][SDL] REVIEWED: `IsKeyPressedRepeat()` (#3605) by @ubkp
@@ -101,7 +118,7 @@ WIP: Last update with commit from 02-Nov-2024
[rcore][Android] REVIEWED: Allow main() to return it its caller on configuration changes (#4288) by @Hesham Abourgheba
[rcore][Android] REVIEWED: Replace deprecated Android function ALooper_pollAll with ALooper_pollOnce (#4275) by @Menno van der Graaf
[rcore][Android] REVIEWED: `PollInputEvents()`, register previous gamepad events (#3910) by @Aria
-[rcore][Android] REVIEWED: Fix Android keycode translation and duplicate key constants (#3733) by @Alexandre Almeida
+[rcore][Android] REVIEWED: Fix Android keycode translation and duplicate key constants (#3733) by @M374LX
[rcore][DRM] ADDED: uConsole keys mapping (#4297) by @carverdamien
[rcore][DRM] ADDED: `GetMonitorWidth/Height()` (#3956) by @gabriel-marques
[rcore][DRM] REVIEWED: `IsMouseButtonUp()` (#3611) by @ubkp
@@ -111,124 +128,124 @@ WIP: Last update with commit from 02-Nov-2024
[rcore][DRM] REVIEWED: DRM backend to only use one api to allow for more devices (#3879) by @MrMugame
[rcore][DRM] REVIEWED: Avoid separate thread when polling for gamepad events (#3641) by @Cinghy Creations
[rcore][DRM] REVIEWED: Connector status reported as UNKNOWN but should be considered as CONNECTED (#4305) by @Michał Jaskólski
-[rcore][RGFW] ADDED: RGFW, new rcore backend platform (#3941) by @Colleague Riley
-[rcore][RGFW] REVIEWED: RGFW 1.0 (#4144) by @Colleague Riley
-[rcore][RGFW] REVIEWED: Fix errors when compiling with mingw (#4282) by @Colleague Riley
-[rcore][RGFW] REVIEWED: Replace long switch with a lookup table (#4108) by @Colleague Riley
-[rcore][RGFW] REVIEWED: Fix MSVC build errors (#4441) by @Colleague Riley
-[rlgl] ADDED: More uniform data type options #4137 by @Ray
+[rcore][RGFW] ADDED: RGFW, new rcore backend platform (#3941) by @colleagueRiley
+[rcore][RGFW] REVIEWED: RGFW 1.0 (#4144) by @colleagueRiley
+[rcore][RGFW] REVIEWED: Fix errors when compiling with mingw (#4282) by @colleagueRiley
+[rcore][RGFW] REVIEWED: Replace long switch with a lookup table (#4108) by @colleagueRiley
+[rcore][RGFW] REVIEWED: Fix MSVC build errors (#4441) by @colleagueRiley
+[rlgl] ADDED: More uniform data type options #4137 by @raysan5
[rlgl] ADDED: Vertex normals for RLGL immediate drawing mode (#3866) by @bohonghuang -WARNING-
[rlgl] ADDED: `rlCullDistance*()` variables and getters (#3912) by @KotzaBoss
[rlgl] ADDED: `rlSetClipPlanes()` function (#3912) by @KotzaBoss
[rlgl] ADDED: `isGpuReady` flag, allow font loading with no GPU acceleration by @Ray -WARNING-
-[rlgl] REVIEWED: Changed RLGL_VERSION from 4.5 to 5.0 (#3914) by @Mute
+[rlgl] REVIEWED: Changed RLGL_VERSION from 4.5 to 5.0 (#3914) by @rsteinke1111
[rlgl] REVIEWED: Shader load failing returns 0, instead of fallback by @Ray -WARNING-
[rlgl] REVIEWED: Standalone mode default flags (#4334) by @Asdqwe
-[rlgl] REVIEWED: Fix hardcoded index values in vboID array (#4312) by @Jett
+[rlgl] REVIEWED: Fix hardcoded index values in vboID array (#4312) by @JettMonstersGoBoom
[rlgl] REVIEWED: GLint64 did not exist before OpenGL 3.2 (#4284) by @Tchan0
[rlgl] REVIEWED: Extra warnings in case OpenGL 4.3 is not enabled (#4202) by @Maxim Knyazkin
[rlgl] REVIEWED: Using GLint64 for glGetBufferParameteri64v() (#4197) by @Randy Palamar
[rlgl] REVIEWED: Replace `glGetInteger64v()` with `glGetBufferParameteri64v()` (#4154) by @Kai Kitagawa-Jones
[rlgl] REVIEWED: `rlMultMatrixf()`, fix matrix multiplication order (#3935) by @bohonghuang
-[rlgl] REVIEWED: `rlSetVertexAttribute()`, define last parameter as offset #3800 by @Ray
-[rlgl] REVIEWED: `rlDisableVertexAttribute()`, remove redundat calls for SHADER_LOC_VERTEX_COLOR (#3871) by @Kacper Zybała
-[rlgl] REVIEWED: `rlLoadTextureCubemap()`, load mipmaps for cubemaps (#4429) by @Nikolas
-[rlgl] REVIEWED: `rlLoadFramebuffer()`, parameters not required by @Ray
+[rlgl] REVIEWED: `rlSetVertexAttribute()`, define last parameter as offset #3800 by @raysan5
+[rlgl] REVIEWED: `rlDisableVertexAttribute()`, remove redundat calls for SHADER_LOC_VERTEX_COLOR (#3871) by @zyperpl
+[rlgl] REVIEWED: `rlLoadTextureCubemap()`, load mipmaps for cubemaps (#4429) by @Not-Nik
+[rlgl] REVIEWED: `rlLoadFramebuffer()`, parameters not required by @raysan5
[rlgl] REVIEWED: `rlSetUniformSampler()` (#3759) by @veins1
[rlgl] REVIEWED: Renamed near/far variables (#4039) by @jgabaut
[rlgl] REVIEWED: Expose OpenGL symbols (#3588) by @Peter0x44
-[rlgl] REVIEWED: Fix OpenGL 1.1 build issues (#3876) by @Ray
+[rlgl] REVIEWED: Fix OpenGL 1.1 build issues (#3876) by @raysan5
[rlgl] REVIEWED: Fixed compilation for OpenGL ES (#4243) by @Maxim Knyazkin
-[rlgl] REVIEWED: rlgl function description and comments by @Ray
+[rlgl] REVIEWED: rlgl function description and comments by @raysan5
[rlgl] REVIEWED: Expose glad functions when building raylib as a shared lib (#3572) by @Peter0x44
-[rlgl] REVIEWED: Fix version info in rlgl.h (#3558) by @Steven Schveighoffer
-[rlgl] REVIEWED: Use the vertex color to the base shader in GLSL330 (#4431) by @Jeffery Myers
+[rlgl] REVIEWED: Fix version info in rlgl.h (#3558) by @schveiguy
+[rlgl] REVIEWED: Use the vertex color to the base shader in GLSL330 (#4431) by @Jeffm2501
[rcamera] REVIEWED: Make camera movement independant of framerate (#4247) by @hanaxars -WARNING-
[rcamera] REVIEWED: Updated camera speeds with GetFrameTime() (#4362) by @Anthony Carbajal
[rcamera] REVIEWED: `UpdateCamera()`, added CAMERA_CUSTOM check (#3938) by @Tomas Fabrizio Orsi
[rcamera] REVIEWED: Support mouse/keyboard and gamepad coexistence for input (#3579) by @ubkp
-[rcamera] REVIEWED: Cleaned away unused macros(#3762) by @Brian E
+[rcamera] REVIEWED: Cleaned away unused macros(#3762) by @Brian-ED
[rcamera] REVIEWED: Fix for free camera mode (#3603) by @lesleyrs
[rcamera] REVIEWED: `GetCameraRight()` (#3784) by @Danil
[raymath] ADDED: C++ operator overloads for common math function (#4385) by @Jeffery Myers -WARNING-
[raymath] ADDED: Vector4 math functions and Vector2 variants of some Vector3 functions (#3828) by @Bowserinator
-[raymath] REVIEWED: Fix MSVC warnings/errors in C++ (#4125) by @Jeffery Myers
-[raymath] REVIEWED: Add extern "C" to raymath header for C++ (#3978) by @Jeffery Myers
+[raymath] REVIEWED: Fix MSVC warnings/errors in C++ (#4125) by @Jeffm2501
+[raymath] REVIEWED: Add extern "C" to raymath header for C++ (#3978) by @Jeffm2501
[raymath] REVIEWED: `QuaternionFromAxisAngle()`, remove redundant axis length calculation (#3900) by @jtainer
[raymath] REVIEWED: `Vector3Perpendicular()`, avoid implicit conversion from float to double (#3799) by @João Foscarini
[raymath] REVIEWED: `MatrixDecompose()`, incorrect output for certain scale and rotations (#4461) by @waveydave
[raymath] REVIEWED: Small code refactor (#3753) by @Idir Carlos Aliane
[rshapes] ADDED: `CheckCollisionCircleLine()` (#4018) by @kai-z99
[rshapes] REVIEWED: Multisegment Bezier splines (#3744) by @Santiago Pelufo
-[rshapes] REVIEWED: Expose shapes drawing texture and rectangle (#3677) by @Jeffery Myers
-[rshapes] REVIEWED: `DrawLine()` #4075 by @Ray
-[rshapes] REVIEWED: `DrawPixel()` drawing by @Ray
-[rshapes] REVIEWED: `DrawLine()` to avoid pixel rounding issues #3931 by @Ray
-[rshapes] REVIEWED: `DrawRectangleLines()`, considering view matrix for lines "alignment" by @Ray
+[rshapes] REVIEWED: Expose shapes drawing texture and rectangle (#3677) by @Jeffm2501
+[rshapes] REVIEWED: `DrawLine()` #4075 by @raysan5
+[rshapes] REVIEWED: `DrawPixel()` drawing by @raysan5
+[rshapes] REVIEWED: `DrawLine()` to avoid pixel rounding issues #3931 by @raysan5
+[rshapes] REVIEWED: `DrawRectangleLines()`, considering view matrix for lines "alignment" by @raysan5
[rshapes] REVIEWED: `DrawRectangleLines()`, pixel offset (#4261) by @RadsammyT
-[rshapes] REVIEWED: `DrawRectangleLines()`, pixel offset when scaling (#3884) by @Ray
+[rshapes] REVIEWED: `DrawRectangleLines()`, pixel offset when scaling (#3884) by @raysan5
[rshapes] REVIEWED: `DrawRectangleLinesEx()`, make sure accounts for square tiles (#4382) by @Jojaby
[rshapes] REVIEWED: `Draw*Gradient()` color parameter names (#4270) by @Paperdomo101
-[rshapes] REVIEWED: `DrawGrid()`, remove duplicate color calls (#4148) by @Jeffery Myers
-[rshapes] REVIEWED: `DrawSplineLinear()` to `SUPPORT_SPLINE_MITERS` by @Ray
+[rshapes] REVIEWED: `DrawGrid()`, remove duplicate color calls (#4148) by @Jeffm2501
+[rshapes] REVIEWED: `DrawSplineLinear()` to `SUPPORT_SPLINE_MITERS` by @raysan5
[rshapes] REVIEWED: `DrawSplineLinear()`, implement miters (#3585) by @Toctave
-[rshapes] REVIEWED: `CheckCollisionPointRec()` by @Ray
+[rshapes] REVIEWED: `CheckCollisionPointRec()` by @raysan5
[rshapes] REVIEWED: `CheckCollisionPointCircle()`, new implementation (#4135) by @kai-z99
[rshapes] REVIEWED: `CheckCollisionCircles()`, optimized (#4065) by @kai-z99
[rshapes] REVIEWED: `CheckCollisionPointPoly()` (#3750) by @Antonio Raúl
[rshapes] REVIEWED: `CheckCollisionCircleRec()` (#3584) by @ubkp
-[rshapes] REVIEWED: Add more detail to function comment (#4344) by @Jeffery Myers
-[rshapes] REVIEWED: Functions that draw point arrays take them as const (#4051) by @Jeffery Myers
-[rtextures] ADDED: `ColorIsEqual()` by @Ray
+[rshapes] REVIEWED: Add more detail to function comment (#4344) by @Jeffm2501
+[rshapes] REVIEWED: Functions that draw point arrays take them as const (#4051) by @Jeffm2501
+[rtextures] ADDED: `ColorIsEqual()` by @raysan5
[rtextures] ADDED: `ColorLerp()`, to mix 2 colors together (#4310) by @SusgUY446
[rtextures] ADDED: `LoadImageAnimFromMemory()` (#3681) by @IoIxD
[rtextures] ADDED: `ImageKernelConvolution()` (#3528) by @Karim
-[rtextures] ADDED: `ImageFromChannel()` (#4105) by @Bruno Cabral
-[rtextures] ADDED: `ImageDrawLineEx()` (#4097) by @Le Juez Victor
-[rtextures] ADDED: `ImageDrawTriangle()` (#4094) by @Le Juez Victor
+[rtextures] ADDED: `ImageFromChannel()` (#4105) by @brccabral
+[rtextures] ADDED: `ImageDrawLineEx()` (#4097) by @Bigfoot71
+[rtextures] ADDED: `ImageDrawTriangle()` (#4094) by @Bigfoot71
[rtextures] REMOVED: SVG files loading and drawing, moving it to raylib-extras by @Ray -WARNING-
[rtextures] REVIEWED: `LoadImage()`, added support for 3-channel QOI images (#4384) by @R-YaTian
-[rtextures] REVIEWED: `LoadImageRaw()` #3926 by @Ray
-[rtextures] REVIEWED: `LoadImageColors()`, advance k in loop (#4120) by @Bruno Cabral
-[rtextures] REVIEWED: `LoadTextureCubemap()`, added `mipmaps` #3665 by @Ray
+[rtextures] REVIEWED: `LoadImageRaw()` #3926 by @raysan5
+[rtextures] REVIEWED: `LoadImageColors()`, advance k in loop (#4120) by @brccabral
+[rtextures] REVIEWED: `LoadTextureCubemap()`, added `mipmaps` #3665 by @raysan5
[rtextures] REVIEWED: `LoadTextureCubemap()`, assign format to cubemap (#3823) by @Gary M
-[rtextures] REVIEWED: `LoadTextureCubemap()`, load mipmaps for cubemaps (#4429) by @Nikolas
-[rtextures] REVIEWED: `LoadTextureCubemap()`, avoid dangling re-allocated pointers (#4439) by @Nikolas
+[rtextures] REVIEWED: `LoadTextureCubemap()`, load mipmaps for cubemaps (#4429) by @Not-Nik
+[rtextures] REVIEWED: `LoadTextureCubemap()`, avoid dangling re-allocated pointers (#4439) by @Not-Nik
[rtextures] REVIEWED: `LoadImageFromScreen()`, fix scaling (#3881) by @proberge-dev
[rtextures] REVIEWED: `LoadImageFromMemory()`, warnings on invalid image data (#4179) by @Jutastre
-[rtextures] REVIEWED: `LoadImageAnimFromMemory()`, added security checks (#3924) by @Ray
-[rtextures] REVIEWED: `ImageColorTint()` and `ColorTint()`, optimized (#4015) by @Le Juez Victor
-[rtextures] REVIEWED: `ImageKernelConvolution()`, formating and warnings by @Ray
+[rtextures] REVIEWED: `LoadImageAnimFromMemory()`, added security checks (#3924) by @raysan5
+[rtextures] REVIEWED: `ImageColorTint()` and `ColorTint()`, optimized (#4015) by @Bigfoot71
+[rtextures] REVIEWED: `ImageKernelConvolution()`, formating and warnings by @raysan5
[rtextures] REVIEWED: `ImageDrawRectangleRec`, fix bounds check (#3732) by @Blockguy24
[rtextures] REVIEWED: `ImageResizeCanvas()`, implemented fill color (#3720) by @Lieven Petersen
-[rtextures] REVIEWED: `ImageDrawRectangleRec()` (#3721) by @Le Juez Victor
-[rtextures] REVIEWED: `ImageDraw()`, don't try to blend images without alpha (#4395) by @Nikolas
+[rtextures] REVIEWED: `ImageDrawRectangleRec()` (#3721) by @Bigfoot71
+[rtextures] REVIEWED: `ImageDraw()`, don't try to blend images without alpha (#4395) by @Not-Nik
[rtextures] REVIEWED: `GenImagePerlinNoise()` being stretched (#4276) by @Bugsia
[rtextures] REVIEWED: `GenImageGradientLinear()`, fix some angles (#4462) by @decromo
-[rtextures] REVIEWED: `DrawTexturePro()` to avoid negative dest rec #4316 by @Ray
+[rtextures] REVIEWED: `DrawTexturePro()` to avoid negative dest rec #4316 by @raysan5
[rtextures] REVIEWED: `ColorToInt()`, fix undefined behaviour (#3996) by @OetkenPurveyorOfCode
-[rtextures] REVIEWED: Remove panorama cubemap layout option (#4425) by @Jeffery Myers
-[rtextures] REVIEWED: Removed unneeded module check, `rtextures` should not depend on `rtext` by @Ray
+[rtextures] REVIEWED: Remove panorama cubemap layout option (#4425) by @Jeffm2501
+[rtextures] REVIEWED: Removed unneeded module check, `rtextures` should not depend on `rtext` by @raysan5
[rtextures] REVIEWED: Simplified for loop for some image manipulation functions (#3712) by @Alice Nyaa
[rtext] ADDED: BDF fonts support (#3735) by @Stanley Fuller -WARNING-
[rtext] ADDED: `TextToCamel()` (#4033) by @IoIxD
[rtext] ADDED: `TextToSnake()` (#4033) by @IoIxD
[rtext] ADDED: `TextToFloat()` (#3627) by @Benjamin Schmid Ties
[rtext] REDESIGNED: `SetTextLineSpacing()` by @Ray -WARNING-
-[rtext] REVIEWED: `LoadFontDataBDF()` name and formating by @Ray
-[rtext] REVIEWED: `LoadFontDefault()`, initialize glyphs and recs to zero #4319 by @Ray
+[rtext] REVIEWED: `LoadFontDataBDF()` name and formating by @raysan5
+[rtext] REVIEWED: `LoadFontDefault()`, initialize glyphs and recs to zero #4319 by @raysan5
[rtext] REVIEWED: `LoadFontEx()`, avoid default font fallback (#4077) by @Peter0x44 -WARNING-
[rtext] REVIEWED: `LoadBMFont()`, extended functionality (#3536) by @Dongkun Lee
-[rtext] REVIEWED: `LoadBMFont()`, issue on not glyph data initialized by @Ray
+[rtext] REVIEWED: `LoadBMFont()`, issue on not glyph data initialized by @raysan5
[rtext] REVIEWED: `LoadFontFromMemory()`, use strncpy() to fix buffer overflow (#3795) by @Mingjie Shen
[rtext] REVIEWED: `LoadCodepoints()` returning a freed ptr when count is 0 (#4089) by @Alice Nyaa
[rtext] REVIEWED: `LoadFontData()` avoid fallback glyphs by @Ray -WARNING-
-[rtext] REVIEWED: `LoadFontData()`, load image only if glyph has been found in font by @Ray
+[rtext] REVIEWED: `LoadFontData()`, load image only if glyph has been found in font by @raysan5
[rtext] REVIEWED: `ExportFontAsCode()`, fix C++ compiler errors (#4013) by @DarkAssassin23
[rtext] REVIEWED: `MeasureTextEx()` height calculation (#3770) by @Marrony Neris
[rtext] REVIEWED: `MeasureTextEx()`, additional check for empty input string (#4448) by @mpv-enjoyer
-[rtext] REVIEWED: `CodepointToUTF8()`, clean static buffer #4379 by @Ray
-[rtext] REVIEWED: `TextToFloat()`, always multiply by sign (#4273) by @listeria
+[rtext] REVIEWED: `CodepointToUTF8()`, clean static buffer #4379 by @raysan5
+[rtext] REVIEWED: `TextToFloat()`, always multiply by sign (#4273) by @ListeriaM
[rtext] REVIEWED: `TextReplace()` const correctness (#3678) by @maverikou
[rtext] REVIEWED: `TextToFloat()`, coding style (#3627) by @Benjamin Schmid Ties
[rtext] REVIEWED: Some comments to align to style (#3756) by @Idir Carlos Aliane
@@ -237,23 +254,23 @@ WIP: Last update with commit from 02-Nov-2024
[rmodels] ADDED: Support 16-bit unsigned short vec4 format for gltf joint loading (#3821) by @Gary M
[rmodels] ADDED: Support animation names for the m3d model format (#3714) by @kolunmi
[rmodels] ADDED: `DrawModelPoints()`, more performant point cloud rendering (#4203) by @Reese Gallagher
-[rmodels] ADDED: `ExportMeshAsCode()` by @Ray
+[rmodels] ADDED: `ExportMeshAsCode()` by @raysan5
[rmodels] REVIEWED: Multiple updates to gltf loading, improved macro (#4373) by @Harald Scheirich
-[rmodels] REVIEWED: `LoadOBJ()`, correctly split obj meshes by material (#4285) by @Jeffery Myers
-[rmodels] REVIEWED: `LoadOBJ()`, add warning when loading an OBJ with multiple materials (#4271) by @Jeffery Myers
-[rmodels] REVIEWED: `LoadOBJ()`, fix bug that fragmented the loaded meshes (#4494) by @Eike Decker
+[rmodels] REVIEWED: `LoadOBJ()`, correctly split obj meshes by material (#4285) by @Jeffm2501
+[rmodels] REVIEWED: `LoadOBJ()`, add warning when loading an OBJ with multiple materials (#4271) by @Jeffm2501
+[rmodels] REVIEWED: `LoadOBJ()`, fix bug that fragmented the loaded meshes (#4494) by @zet23t
[rmodels] REVIEWED: `LoadIQM()`, set model.meshMaterial[] (#4092) by @SuperUserNameMan
-[rmodels] REVIEWED: `LoadIQM()`, attempt to load texture from IQM at loadtime (#4029) by @Jett
-[rmodels] REVIEWED: `LoadM3D(), fix vertex colors for m3d files (#3859) by @Jeffery Myers
+[rmodels] REVIEWED: `LoadIQM()`, attempt to load texture from IQM at loadtime (#4029) by @JettMonstersGoBoom
+[rmodels] REVIEWED: `LoadM3D(), fix vertex colors for m3d files (#3859) by @Jeffm2501
[rmodels] REVIEWED: `LoadGLTF()`, supporting additional vertex data formats (#3546) by @MrScautHD
[rmodels] REVIEWED: `LoadGLTF()`, correctly handle the node hierarchy in a glTF file (#4037) by @Paul Melis
[rmodels] REVIEWED: `LoadGLTF()`, replaced SQUAD quat interpolation with cubic hermite (gltf 2.0 specs) (#3920) by @Benji
-[rmodels] REVIEWED: `LoadGLTF()`, support 2nd texture coordinates loading by @Ray
-[rmodels] REVIEWED: `LoadGLTF()`, support additional vertex attributes data formats #3890 by @Ray
+[rmodels] REVIEWED: `LoadGLTF()`, support 2nd texture coordinates loading by @raysan5
+[rmodels] REVIEWED: `LoadGLTF()`, support additional vertex attributes data formats #3890 by @raysan5
[rmodels] REVIEWED: `LoadGLTF()`, set cgltf callbacks to use `LoadFileData()` and `UnloadFileData()` (#3652) by @kolunmi
-[rmodels] REVIEWED: `LoadGLTF()`, JOINTS loading #3836 by @Ray
+[rmodels] REVIEWED: `LoadGLTF()`, JOINTS loading #3836 by @raysan5
[rmodels] REVIEWED: `LoadImageFromCgltfImage()`, fix base64 padding support (#4112) by @SuperUserNameMan
-[rmodels] REVIEWED: `LoadModelAnimationsIQM()`, fix corrupted animation names (#4026) by @Jett
+[rmodels] REVIEWED: `LoadModelAnimationsIQM()`, fix corrupted animation names (#4026) by @JettMonstersGoBoom
[rmodels] REVIEWED: `LoadModelAnimationsGLTF()`, load animations with 1 frame (#3804) by @Nikita Blizniuk
[rmodels] REVIEWED: `LoadModelAnimationsGLTF()`, added missing interpolation types (#3919) by @Benji
[rmodels] REVIEWED: `LoadModelAnimationsGLTF()` (#4107) by @VitoTringolo
@@ -266,19 +283,19 @@ WIP: Last update with commit from 02-Nov-2024
[rmodels] REVIEWED: `DrawCylinder()`, fix drawing of cap (#4478) by @JeffM2501
[rmodels] REVIEWED: `DrawMesh()`, send full matModel to shader in DrawMesh (#4005) (#4022) by @David Holland
[rmodels] REVIEWED: `DrawMesh()`, fix material specular map retrieval (#3758) by @Victor Gallet
-[rmodels] REVIEWED: `DrawModelEx()`, simplified multiplication of colors (#4002) by @Le Juez Victor
+[rmodels] REVIEWED: `DrawModelEx()`, simplified multiplication of colors (#4002) by @Bigfoot71
[rmodels] REVIEWED: `DrawBillboardPro()`, to be consistend with `DrawTexturePro()` (#4132) by @bohonghuang
[rmodels] REVIEWED: `DrawSphereEx()` optimization (#4106) by @smalltimewizard
[raudio] REVIEWED: `LoadMusicStreamFromMemory()`, support 24-bit FLACs (#4279) by @konstruktor227
-[raudio] REVIEWED: `LoadWaveSamples()`, fix mapping of wave data (#4062) by @listeria
+[raudio] REVIEWED: `LoadWaveSamples()`, fix mapping of wave data (#4062) by @ListeriaM
[raudio] REVIEWED: `LoadMusicStream()`, remove drwav_uninit() (#3986) by @FishingHacks
[raudio] REVIEWED: `LoadMusicStream()` qoa and wav loading (#3966) by @veins1
[raudio] REVIEWED: `ExportWaveAsCode()`, segfault (#3769) by @IoIxD
-[raudio] REVIEWED: `WaveCrop()`, fix issues and use frames instead of samples (#3994) by @listeria
+[raudio] REVIEWED: `WaveCrop()`, fix issues and use frames instead of samples (#3994) by @ListeriaM
[raudio] REVIEWED: Crash from multithreading issues (#3907) by @Christian Haas
[raudio] REVIEWED: Reset music.ctxType if loading wasn't succesful (#3917) by @veins1
[raudio] REVIEWED: Added missing functions in "standalone" mode (#3760) by @Alessandro Nikolaev
-[raudio] REVIEWED: Disable unused miniaudio features (#3544) by @Alexandre Almeida
+[raudio] REVIEWED: Disable unused miniaudio features (#3544) by @M374LX
[raudio] REVIEWED: Fix crash when switching playback device at runtime (#4102) by @jkaup
[raudio] REVIEWED: Support 24 bits samples for FLAC format (#4058) by @Alexey Kutepov
[examples] ADDED: `core_random_sequence` (#3846) by @Dalton Overmyer
@@ -287,36 +304,36 @@ WIP: Last update with commit from 02-Nov-2024
[examples] ADDED: `models_bone_socket` (#3833) by @iP
[examples] ADDED: `shaders_vertex_displacement` (#4186) by @Alex ZH
[examples] ADDED: `shaders_shadowmap` (#3653) by @TheManTheMythTheGameDev
-[examples] REVIEWED: `core_2d_camera_platformer` by @Ray
-[examples] REVIEWED: `core_2d_camera_mouse_zoom`, use logarithmic scaling for a 2d zoom functionality (#3977) by @Mike Will
+[examples] REVIEWED: `core_2d_camera_platformer` by @raysan5
+[examples] REVIEWED: `core_2d_camera_mouse_zoom`, use logarithmic scaling for a 2d zoom functionality (#3977) by @myQwil
[examples] REVIEWED: `core_input_gamepad_info`, all buttons displayed within the window (#4241) by @Asdqwe
[examples] REVIEWED: `core_input_gamepad_info`, show ps3 controller (#4040) by @Konrad Gutvik Grande
[examples] REVIEWED: `core_input_gamepad`, add drawing for generic gamepad (#4424) by @Asdqwe
[examples] REVIEWED: `core_input_gamepad`, add deadzone handling (#4422) by @Asdqwe
[examples] REVIEWED: `shapes_bouncing_ball` (#4226) by @Anthony Carbajal
[examples] REVIEWED: `shapes_following_eyes` (#3710) by @Hongyu Ouyang
-[examples] REVIEWED: `shapes_draw_rectangle_rounded` by @Ray
+[examples] REVIEWED: `shapes_draw_rectangle_rounded` by @raysan5
[examples] REVIEWED: `shapes_draw_ring`, fix other examples (#4211) by @kai-z99
-[examples] REVIEWED: `shapes_lines_bezier` by @Ray
-[examples] REVIEWED: `textures_image_kernel` #3556 by @Ray
+[examples] REVIEWED: `shapes_lines_bezier` by @raysan5
+[examples] REVIEWED: `textures_image_kernel` #3556 by @raysan5
[examples] REVIEWED: `text_input_box` (#4229) by @Anthony Carbajal
[examples] REVIEWED: `text_writing_anim` (#4230) by @Anthony Carbajal
-[examples] REVIEWED: `models_billboard` by @Ray
-[examples] REVIEWED: `models_cubicmap` by @Ray
-[examples] REVIEWED: `models_point_rendering` by @Ray
+[examples] REVIEWED: `models_billboard` by @raysan5
+[examples] REVIEWED: `models_cubicmap` by @raysan5
+[examples] REVIEWED: `models_point_rendering` by @raysan5
[examples] REVIEWED: `models_box_collisions` (#4224) by @Anthony Carbajal
-[examples] REVIEWED: `models_skybox`, do not use HDR by default (#4115) by @Jeffery Myers
+[examples] REVIEWED: `models_skybox`, do not use HDR by default (#4115) by @Jeffm2501
[examples] REVIEWED: `shaders_basic_pbr` (#4225) by @Anthony Carbajal
-[examples] REVIEWED: `shaders_palette_switch` by @Ray
+[examples] REVIEWED: `shaders_palette_switch` by @raysan5
[examples] REVIEWED: `shaders_hybrid_render` (#3908) by @Yousif
-[examples] REVIEWED: `shaders_lighting_instancing`, fix vertex shader (#4056) by @Karl Zylinski
+[examples] REVIEWED: `shaders_lighting_instancing`, fix vertex shader (#4056) by @karl
[examples] REVIEWED: `shaders_raymarching`, add `raymarching.fs` for GLSL120 (#4183) by @CDM15y
[examples] REVIEWED: `shaders_shadowmap`, fix shaders for GLSL 1.20 (#4167) by @CDM15y
-[examples] REVIEWED: `shaders_deferred_render` (#3655) by @Jett
-[examples] REVIEWED: `shaders_basic_pbr` (#3621) by @devdad
+[examples] REVIEWED: `shaders_deferred_render` (#3655) by @JettMonstersGoBoom
+[examples] REVIEWED: `shaders_basic_pbr` (#3621) by @afanolovcic
[examples] REVIEWED: `shaders_basic_pbr`, remove dependencies (#3649) by @TheManTheMythTheGameDev
-[examples] REVIEWED: `shaders_basic_pbr`, added more comments by @Ray
-[examples] REVIEWED: `shaders_gpu_skinning`, to work with OpenGL ES 2.0 #4412 by @Ray
+[examples] REVIEWED: `shaders_basic_pbr`, added more comments by @raysan5
+[examples] REVIEWED: `shaders_gpu_skinning`, to work with OpenGL ES 2.0 #4412 by @raysan5
[examples] REVIEWED: `shaders_model_shader`, use free camera (#4428) by @IcyLeave6109
[examples] REVIEWED: `audio_stream_effects` (#3618) by @lipx
[examples] REVIEWED: `audio_raw_stream` (#3624) by @riadbettole
@@ -325,21 +342,21 @@ WIP: Last update with commit from 02-Nov-2024
[examples] REVIEWED: Update examples missing UnloadTexture() calls (#4234) by @Anthony Carbajal
[examples] REVIEWED: Added GLSL 100 and 120 shaders to lightmap example (#3543) by @Jussi Viitala
[examples] REVIEWED: Set FPS to always 60 in all exampels (#4235) by @Anthony Carbajal
-[build] REVIEWED: Makefile by @Ray
-[build] REVIEWED: Makefile, fix wrong flag #3593 by @Ray
+[build] REVIEWED: Makefile by @raysan5
+[build] REVIEWED: Makefile, fix wrong flag #3593 by @raysan5
[build] REVIEWED: Makefile, disable wayland by default (#4369) by @Anthony Carbajal
[build] REVIEWED: Makefile, VSCode, fix to support multiple .c files (#4391) by @Alan Arrecis
[build] REVIEWED: Makefile, fix -Wstringop-truncation warning (#4096) by @Peter0x44
-[build] REVIEWED: Makefile, fix issues for RGFW on Linux/macOS (#3969) by @Colleague Riley
+[build] REVIEWED: Makefile, fix issues for RGFW on Linux/macOS (#3969) by @colleagueRiley
[build] REVIEWED: Makefile, update RAYLIB_VERSION (#3901) by @Belllg
[build] REVIEWED: Makefile, use mingw32-make for Windows (#4436) by @Asdqwe
[build] REVIEWED: Makefile, move CUSTOM_CFLAGS for better visibility (#4054) by @Lázaro Albuquerque
-[build] REVIEWED: Makefile, update emsdk paths to latest versions by @Ray
+[build] REVIEWED: Makefile, update emsdk paths to latest versions by @raysan5
[build] REVIEWED: Makefile examples, align /usr/local with /src Makefile (#4286) by @Tchan0
[build] REVIEWED: Makefile examples, added `textures_image_kernel` (#3555) by @Sergey Zapunidi
[build] REVIEWED: Makefile examples (#4209) by @Anthony Carbajal
[build] REVIEWED: Makefile examples, to work on NetBSD (#4438) by @NishiOwO
-[build] REVIEWED: Makefile examples, WebGL2 (OpenGL ES 3.0) backend flags #4330 by @Ray
+[build] REVIEWED: Makefile examples, WebGL2 (OpenGL ES 3.0) backend flags #4330 by @raysan5
[build] REVIEWED: Makefile examples, web building (#4434) by @Asdqwe
[build] REVIEWED: build.zig, fix various issues around `-Dconfig` (#4398) by @Sage Hane
[build] REVIEWED: build.zig, fix type mismatch (#4383) by @yuval_dev
@@ -348,15 +365,15 @@ WIP: Last update with commit from 02-Nov-2024
[build] REVIEWED: build.zig, improve logic (#4375) by @Sage Hane
[build] REVIEWED: build.zig, issues (#4374) by @William Culver
[build] REVIEWED: build.zig, issues (#4366) by @Visen
-[build] REVIEWED: build.zig, support desktop backend change (#4358) by @Nikolas
+[build] REVIEWED: build.zig, support desktop backend change (#4358) by @Not-Nik
[build] REVIEWED: build.zig, use zig fmt (#4242) by @freakmangd
[build] REVIEWED: build.zig, check if wayland-scanner is installed (#4217) by @lnc3l0t
[build] REVIEWED: build.zig, override config.h definitions (#4193) by @lnc3l0t
[build] REVIEWED: build.zig, support GLFW platform detection (#4150) by @InventorXtreme
-[build] REVIEWED: build.zig, make emscripten build compatible with Zig 0.13.0 (#4121) by @Mike Will
+[build] REVIEWED: build.zig, make emscripten build compatible with Zig 0.13.0 (#4121) by @myQwil
[build] REVIEWED: build.zig, pass the real build.zig file (#4113) by @InKryption
[build] REVIEWED: build.zig, leverage `dependencyFromBuildZig` (#4109) by @InKryption
-[build] REVIEWED: build.zig, run examples from their directories (#4063) by @Mike Will
+[build] REVIEWED: build.zig, run examples from their directories (#4063) by @myQwil
[build] REVIEWED: build.zig, fix raygui build when using addRaygui externally (#4027) by @Viktor Pocedulić
[build] REVIEWED: build.zig, fix emscripten build (#4012) by @Dylan
[build] REVIEWED: build.zig, update to zig 0.12.0dev while keeping 0.11.0 compatibility (#3715) by @freakmangd
@@ -372,7 +389,7 @@ WIP: Last update with commit from 02-Nov-2024
[build] REVIEWED: build.zig, improve cross-compilation (#4468) by @deathbeam
[build] REVIEWED: CMake, update to raylib 5.0 (#3623) by @Peter0x44
[build] REVIEWED: CMake, added PLATFORM option for Desktop SDL (#3809) by @mooff
-[build] REVIEWED: CMake, fix GRAPHICS_* check (#4359) by @Kacper Zybała
+[build] REVIEWED: CMake, fix GRAPHICS_* check (#4359) by @zyperpl
[build] REVIEWED: CMake, examples projects (#4332) by @Ridge3Dproductions
[build] REVIEWED: CMake, fix warnings in projects/CMake/CMakeLists.txt (#4278) by @Peter0x44
[build] REVIEWED: CMake, delete BuildOptions.cmake (#4277) by @Peter0x44
@@ -381,54 +398,54 @@ WIP: Last update with commit from 02-Nov-2024
[build] REVIEWED: CMake, support OpenGL ES3 in `LibraryConfigurations.cmake` (#4079) by @manuel5975p
[build] REVIEWED: CMake, `config.h` fully available to users (#4044) by @Lázaro Albuquerque
[build] REVIEWED: CMake, pass -sFULL_ES3 instead of -sFULL_ES3=1 (#4090) by @manuel5975p
-[build] REVIEWED: CMake, SDL build link the glfw dependency (#3860) by @Rob Loach
+[build] REVIEWED: CMake, SDL build link the glfw dependency (#3860) by @RobLoach
[build] REVIEWED: CMake, infer CMAKE_MODULE_PATH in super-build (#4042) by @fruzitent
-[build] REVIEWED: CMake, remove USE_WAYLAND option (#3851) by @Alexandre Almeida
-[build] REVIEWED: CMake, disable SDL rlgl_standalone example (#3861) by @Rob Loach
-[build] REVIEWED: CMake, bump version required to avoid deprecated #3639 by @Ray
+[build] REVIEWED: CMake, remove USE_WAYLAND option (#3851) by @M374LX
+[build] REVIEWED: CMake, disable SDL rlgl_standalone example (#3861) by @RobLoach
+[build] REVIEWED: CMake, bump version required to avoid deprecated #3639 by @raysan5
[build] REVIEWED: CMake, fix examples linking -DPLATFORM=SDL (#3825) by @Peter0x44
[build] REVIEWED: CMake, don't build for wayland by default (#4432) by @Peter0x44
-[build] REVIEWED: VS2022, misc improvements by @Ray
-[build] REVIEWED: VS2022, fix build warnings (#4095) by @Jeffery Myers
-[build] REVIEWED: VS2022, added new examples (#4492) by @Jeffery Myers
+[build] REVIEWED: VS2022, misc improvements by @raysan5
+[build] REVIEWED: VS2022, fix build warnings (#4095) by @Jeffm2501
+[build] REVIEWED: VS2022, added new examples (#4492) by @Jeffm2501
[build] REVIEWED: Fix fix-build-paths (#3849) by @Caleb Barger
[build] REVIEWED: Fix build paths (#3835) by @Steve Biedermann
[build] REVIEWED: Fix VSCode sample project for macOS (#3666) by @Tim Romero
[build] REVIEWED: Fix some warnings on web builds and remove some redundant flags (#4069) by @Lázaro Albuquerque
[build] REVIEWED: Fix examples not building with gestures system disabled (#4020) by @Sprix
-[build] REVIEWED: Fix GLFW runtime platform detection (#3863) by @Alexandre Almeida
+[build] REVIEWED: Fix GLFW runtime platform detection (#3863) by @M374LX
[build] REVIEWED: Fix DRM cross-compile without sysroot (#3839) by @Christian W. Zuckschwerdt
[build] REVIEWED: Fix cmake-built libraylib.a to properly include GLFW's object files (#3598) by @Peter0x44
[build] REVIEWED: Hide unneeded internal symbols when building raylib as an so or dylib (#3573) by @Peter0x44
[build] REVIEWED: Corrected the path of android ndk toolchains for OSX platforms (#3574) by @Emmanuel Méra
[build][CI] ADDED: Automatic update for raylib_api.* (#3692) by @seiren
-[build][CI] REVIEWED: Update workflows to use latest actions/upload-artifact by @Ray
-[build][CI] REVIEWED: CodeQL minor tweaks to avoid some warnings by @Ray
-[build][CI] REVIEWED: Update linux_examples.yml by @Ray
-[build][CI] REVIEWED: Update linux.yml by @Ray
-[build][CI] REVIEWED: Update webassembly.yml by @Ray
-[build][CI] REVIEWED: Update cmake.yml by @Ray
-[build][CI] REVIEWED: Update codeql.yml, exclude src/external files by @Ray
-[bindings] ADDED: raylib-APL (#4253) by @Brian E
-[bindings] ADDED: raylib-bqn, moved rayed-bqn (#4331) by @Brian E
+[build][CI] REVIEWED: Update workflows to use latest actions/upload-artifact by @raysan5
+[build][CI] REVIEWED: CodeQL minor tweaks to avoid some warnings by @raysan5
+[build][CI] REVIEWED: Update linux_examples.yml by @raysan5
+[build][CI] REVIEWED: Update linux.yml by @raysan5
+[build][CI] REVIEWED: Update webassembly.yml by @raysan5
+[build][CI] REVIEWED: Update cmake.yml by @raysan5
+[build][CI] REVIEWED: Update codeql.yml, exclude src/external files by @raysan5
+[bindings] ADDED: raylib-APL (#4253) by @Brian-ED
+[bindings] ADDED: raylib-bqn, moved rayed-bqn (#4331) by @Brian-ED
[bindings] ADDED: brainfuck binding (#4169) by @_Tradam
[bindings] ADDED: raylib-zig-bindings (#4004) by @Lionel Briand
[bindings] ADDED: Raylib-CSharp wrapper (#3963) by @MrScautHD
[bindings] ADDED: COBOL binding (#3661) by @glowiak
[bindings] ADDED: raylib-beef binding (#3640) by @Braedon Lewis
[bindings] ADDED: Raylib-CSharp-Vinculum (#3571) by @Danil
-[bindings] REVIEWED: Remove broken-link bindings #3899 by @Ray
-[bindings] REVIEWED: Updated some versions on BINDINGS.md by @Ray
-[bindings] REVIEWED: Removed umaintained repos (#3999) by @Antonis Geralis
+[bindings] REVIEWED: Remove broken-link bindings #3899 by @raysan5
+[bindings] REVIEWED: Updated some versions on BINDINGS.md by @raysan5
+[bindings] REVIEWED: Removed umaintained repos (#3999) by @planetis-m
[bindings] REDESIGNED: Add binding link to name, instead of separate column (#3995) by @Carmine Pietroluongo
[bindings] UPDATED: h-raylib (#4378) by @Anand Swaroop
[bindings] UPDATED: Raylib.lean, to master version (#4337) by @Daniil Kisel
[bindings] UPDATED: raybit, to latest master (#4311) by @Alex
[bindings] UPDATED: dray binding (#4163) by @red thing
[bindings] UPDATED: Julia (#4068) by @ShalokShalom
-[bindings] UPDATED: nim to latest master (#3999) by @Antonis Geralis
+[bindings] UPDATED: nim to latest master (#3999) by @planetis-m
[bindings] UPDATED: raylib-rs (#3991) by @IoIxD
-[bindings] UPDATED: raylib-zig version (#3902) by @Nikolas
+[bindings] UPDATED: raylib-zig version (#3902) by @Not-Nik
[bindings] UPDATED: raylib-odin (#3868) by @joyousblunder
[bindings] UPDATED: Raylib VAPI (#3829) by @Alex Macafee
[bindings] UPDATED: Raylib-cs (#3774) by @Brandon Baker
@@ -438,41 +455,41 @@ WIP: Last update with commit from 02-Nov-2024
[bindings] UPDATED: ray-cyber to 5.0 (#3654) by @fubark
[bindings] UPDATED: raylib-freebasic binding (#3591) by @WIITD
[bindings] UPDATED: SmallBASIC (#3562) by @Chris Warren-Smith
-[bindings] UPDATED: Python raylib-py v5.0.0beta1 (#3557) by @Jorge A. Gomes
-[bindings] UPDATED: raylib-d binding (#3561) by @Steven Schveighoffer
+[bindings] UPDATED: Python raylib-py v5.0.0beta1 (#3557) by @jorgegomes83
+[bindings] UPDATED: raylib-d binding (#3561) by @schveiguy
[bindings] UPDATED: Janet (#3553) by @Dmitry Matveyev
-[bindings] UPDATED: Raylib.nelua (#3552) by @Auz
-[bindings] UPDATED: raylib-cpp to 5.0 (#3551) by @Rob Loach
+[bindings] UPDATED: Raylib.nelua (#3552) by @guy.smith764
+[bindings] UPDATED: raylib-cpp to 5.0 (#3551) by @RobLoach
[bindings] UPDATED: Pascal binding (#3548) by @Gunko Vadim
-[external] UPDATED: stb_truetype.h to latest version by @Ray
-[external] UPDATED: stb_image_resize2.h to latest version by @Ray
-[external] UPDATED: stb_image.h to latest version by @Ray
-[external] UPDATED: qoa.h to latest version by @Ray
-[external] UPDATED: dr_wav.h to latest version by @Ray
-[external] UPDATED: dr_mp3.h to latest version by @Ray
-[external] UPDATED: cgltf.h to latest version by @Ray
-[external] REVIEWED: rl_gputex, correctly load mipmaps from DDS files (#4399) by @Nikolas
+[external] UPDATED: stb_truetype.h to latest version by @raysan5
+[external] UPDATED: stb_image_resize2.h to latest version by @raysan5
+[external] UPDATED: stb_image.h to latest version by @raysan5
+[external] UPDATED: qoa.h to latest version by @raysan5
+[external] UPDATED: dr_wav.h to latest version by @raysan5
+[external] UPDATED: dr_mp3.h to latest version by @raysan5
+[external] UPDATED: cgltf.h to latest version by @raysan5
+[external] REVIEWED: rl_gputex, correctly load mipmaps from DDS files (#4399) by @Not-Nik
[external] REVIEWED: stb_image_resize2, dix vld1q_f16 undeclared in arm (#4309) by @masnm
[external] REVIEWED: miniaudio, fix library and Makefile for NetBSD (#4212) by @NishiOwO
[external] REVIEWED: raygui, update to latest version 4.5-dev (#4238) by @Anthony Carbajal
-[external] REVIEWED: jar_xml, replace unicode characters by ascii characters to avoid warning in MSVC (#4196) by @Rico P
+[external] REVIEWED: jar_xml, replace unicode characters by ascii characters to avoid warning in MSVC (#4196) by @RicoP
[external] REVIEWED: vox_loader, normals and new voxels shader (#3843) by @johann nadalutti
[parser] REVIEWED: README.md, to mirror fixed help text (#4336) by @Daniil Kisel
[parser] REVIEWED: Fix seg fault with long comment lines (#4306) by @Chris Warren-Smith
[parser] REVIEWED: Don't crash for files that don't end in newlines (#3981) by @Peter0x44
[parser] REVIEWED: Issues in usage example help text (#4084) by @Peter0x44
[parser] REVIEWED: Fix parsing of empty parentheses (#3974) by @Filyus
-[parser] REVIEWED: Address parsing issue when generating XML #3893 by @Ray
+[parser] REVIEWED: Address parsing issue when generating XML #3893 by @raysan5
[parser] REVIEWED: `MemoryCopy()`, prevent buffer overflow by replacing hard-coded arguments (#4011) by @avx0
-[misc] ADDED: Create logo/raylib.icns by @Ray
-[misc] ADDED: Create logo/raylib_1024x1024.png by @Ray
+[misc] ADDED: Create logo/raylib.icns by @raysan5
+[misc] ADDED: Create logo/raylib_1024x1024.png by @raysan5
[misc] ADDED: Default vertex/fragment shader for OpenGL ES 3.0 (#4178) by @Lázaro Albuquerque
[misc] REVIEWED: README.md, fix Reddit badge (#4136) by @Ninad Sachania
[misc] REVIEWED: .gitignore, ignore compiled dll binaries (#3628) by @2Bear
[misc] REVIEWED: Fix undesired scrollbars on web shell files (#4104) by @jspast
[misc] REVIEWED: Made comments on raylib.h match comments in rcamera.h (#3942) by @Tomas Fabrizio Orsi
[misc] REVIEWED: Make raylib/raygui work better on touchscreen (#3728) by @Hongyu Ouyang
-[misc] REVIEWED: Update config.h by @Ray
+[misc] REVIEWED: Update config.h by @raysan5
-------------------------------------------------------------------------
Release: raylib 5.0 - 10th Anniversary Edition (18 November 2023)
diff --git a/CMakeOptions.txt b/CMakeOptions.txt
index cb1b95b0c..0a453917d 100644
--- a/CMakeOptions.txt
+++ b/CMakeOptions.txt
@@ -6,7 +6,7 @@ if(EMSCRIPTEN)
# When configuring web builds with "emcmake cmake -B build -S .", set PLATFORM to Web by default
SET(PLATFORM Web CACHE STRING "Platform to build for.")
endif()
-enum_option(PLATFORM "Desktop;Web;Android;Raspberry Pi;DRM;SDL;RGFW" "Platform to build for.")
+enum_option(PLATFORM "Desktop;Web;WebRGFW;Android;Raspberry Pi;DRM;SDL;RGFW" "Platform to build for.")
enum_option(OPENGL_VERSION "OFF;4.3;3.3;2.1;1.1;ES 2.0;ES 3.0;Software" "Force a specific OpenGL Version?")
diff --git a/HISTORY.md b/HISTORY.md
index 02e226437..7f5539816 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -449,13 +449,13 @@ Some numbers for this release:
Highlights for `raylib 5.0`:
- **`rcore` module platform-split**: Probably the biggest raylib redesign in the last 10 years. raylib started as a library targeting 3 desktop platforms: `Windows`, `Linux` and `macOS` (thanks to `GLFW` underlying library) but with the years support for several new platforms has been added (`Android`, `Web`, `Rapsberry Pi`, `RPI native`...); lot of the platform code was shared so the logic was all together on `rcore.c` module, separated by compilation flags. This approach was very handy but also made it very difficult to support new platforms and specially painful for contributors not familiar with the module, navigating +8000 lines of code in a single file. A big redesign was really needed but the amount of work required was humungous and quite scary for a solo-developer like me, moreover considering that everything was working and the chances to break things were really high. Fortunately, some contributors were ready for the task (@ubkp, @michaelfiber, @Bigfoot71) and thanks to their initiative and super-hard work, the `rcore` [platform split](https://github.com/raysan5/raylib/blob/master/src/platforms) has been possible! This new raylib architecture greatly improves the platforms maintenance but also greatly simplifies the addition of new platforms. A [`platforms/rcore_template.c`](https://github.com/raysan5/raylib/blob/master/src/platforms/rcore_template.c) file is provided with the required structure and functions to be filled for the addition of new platforms, actually it has been simplified to mostly filling some pre-defined functions: `InitPlatform()`, `ClosePlatform`, `PollInputEvents`... Undoubtedly, **this redesign opens the doors to a new era for raylib**, letting the users to plug new platforms as desired.
-
+
- **`NEW` Platform backend supported: SDL**: Thanks to the new `rcore` platform-split, the addition of new platforms/backends to raylib has been greatly simplified. As a proof of concept, [`SDL2`](https://libsdl.org/) platform backend has been added to raylib as an alternative for `GLFW` library for desktop builds: [`platforms/rcore_desktop_sdl`](https://github.com/raysan5/raylib/blob/master/src/platforms/rcore_desktop_sdl.c). Lot of work has been put to provide exactly the same features as the other platforms and carefully test the new implementation. Now `SDL2` fans can use this new backend, just providing the required include libraries on compilation and linkage (not included in raylib, like `GLFW`). `SDL` backend support also **eases the process of supporting a wider range of platforms** that already support `SDL`.
- **`NEW` Platform backend supported: Nintendo Switch (closed source)**: The addition of the `SDL` backend was quite a challenge but to really verify the robustness and ease of the new platform plugin system, adding support for a console was a more demanding adventure. Surprisingly, only two days of work were required to add support for `Nintendo Switch` to raylib! Implementation result showed an outstanding level of simplicity, with a **self-contained module** (`rcore_swith.cpp`) supporting graphics and inputs. Unfortunately this module can not be open-sourced due to licensing restrictions.
- **`NEW` Splines drawing and evaluation API**: A complete set of functions has been added to [draw](https://github.com/raysan5/raylib/blob/master/src/raylib.h#L1258) and [evaluate](https://github.com/raysan5/raylib/blob/master/src/raylib.h#L1270) different types of splines: `Linear`, `Basis`, `Catmull-Rom`, `Quadratic Bezier` and `Cubic Bezier`. Splines are extremely useful for game development (describe paths, control NPC movement...) but they can also be very useful on tools development (node-conections, elements-movement, 3d modelling, animations...). This was the missing feature on the raylib [`rshapes`](https://github.com/raysan5/raylib/blob/master/src/rshapes.h) module to make it complete! Note that `rshapes` module can also be used independently of raylib just providing the **only 6 functions required for vertex definition and drawing**.
-
+
- **`NEW` Pseudo-random numbers generator: rprand**: After several years of users asking for this missing piece, a brand new pseudo-random generator module has been added to raylib. [`rprand`](https://github.com/raysan5/raylib/blob/master/src/external/rprand.h) implements the `Xoshiro128**` algorithm combined with `SplitMix64`, specially suited for **fast software pseudo-random numbers generation**. The module also implies some useful functions to generate non-repetitive random numbers sequences, functionality exposed by raylib. usage of this module can be controlled by a compilation flag, in case the default libc `rand()` function was preferred.
- **`NEW` Automation Events System API**: This new system was first added in `raylib 4.0` as an experimental feature but it was a bit clumsy and there was no API exposed to users. For the new `raylib 5.0` the system has been redesigned and [proper API](https://github.com/raysan5/raylib/blob/master/src/raylib.h#L1135) added for the users. With this new events automation system, users can **record input events for later replay**, very useful feature for testing automation, tutorials generation, assisted game playing, in-game cinematics, speedruns saving or even AI assited game playing!
@@ -464,8 +464,8 @@ Highlights for `raylib 5.0`:
- **`NEW` raylib web examples functionality**: Beside the addition of several new examples, the web examples functionality has been improved. Examples have been organized by [complexity level](https://www.raylib.com/examples.html), marked with one star for simple examples and up to 4 stars for more complex ones. A new option has been added to web to allow to **filter examples by function-name** usage, to ease the learning process when looking for an usage example of some function. Finally, **open-graph metadata** information has been added to all examples individual webpages, improving a the visuals and information when sharing those webpages on social networks, sharing the example screenshot and details.
-As always, those are only some highlights of the new `raylib 5.0` but there is many more improvements! Support for 16-bit HDR images/textures, SVG loading and scaling support, new OpenGL ES 3.0 graphic backend, new image gradient generators, sound alias loading, improved 3d models loading, multiple optimizations, new bindings, CodeQL integration and much more!
-
+As always, those are only some highlights of the new `raylib 5.0` but there is many more improvements! Support for 16-bit HDR images/textures, SVG loading and scaling support, new OpenGL ES 3.0 graphic backend, new image gradient generators, sound alias loading, improved 3d models loading, multiple optimizations, new bindings, CodeQL integration and much more!
+
Make sure to check raylib [CHANGELOG]([CHANGELOG](https://github.com/raysan5/raylib/blob/master/CHANGELOG)) for a detailed list of changes!
Undoubtedly, this is the **biggest raylib update in 10 years**. Many new features and improvements with a special focus on maintainability and long-term sustainability. **Undoubtedly, this is the raylib of the future**.
@@ -492,16 +492,16 @@ Highlights for `raylib 5.5`:
- **`NEW` raylib project creator tool**: A brand new tool developed to help raylib users to **setup new projects in a professional way**. `raylib project creator` generates a complete project structure with **multiple build systems ready-to-use** and **GitHub CI/CD actions pre-configured**. It only requires providing some C files and basic project parameters! The tools is [free and open-source](https://raysan5.itch.io/raylib-project-creator), and [it can be used online](https://raysan5.itch.io/raylib-project-creator)!.
- **`NEW` Platform backend supported: RGFW**: Thanks to the `rcore` platform-split implemented in `raylib 5.0`, **adding new platforms backends has been greatly simplified**, new backends can be added using provided template, self-contained in a single C module, completely portable. A new platform backend has been added: [`RGFW`](https://github.com/raysan5/raylib/blob/master/src/platforms/rcore_desktop_rgfw.c). `RGFW` is a **new single-file header-only portable library** ([`RGFW.h`](https://github.com/ColleagueRiley/RGFW)) intended for platform-functionality management (windowing and inputs); in this case for **desktop platforms** (Windows, Linux, macOS) but also for **Web platform**. It adds a new alternative to the already existing `GLFW` and `SDL` platform backends.
-
+
- **`NEW` Platform backend version supported: SDL3**: Previous `raylib 5.0` added support for `SDL2` library, and `raylib 5.5` not only improves SDL2 functionality, with several issues reviewed, but also adds support for the recently released big SDL update in years: [`SDL3`](https://wiki.libsdl.org/SDL3/FrontPage). Now users can **select at compile time the desired SDL version to use**, increasing the number of potential platforms supported in the future!
-
+
- **`NEW` Retro-console platforms supported: Dreamcast, N64, PSP, PSVita, PS4**: Thanks to the platform-split on `raylib 5.0`, **supporting new platform backends is easier than ever!** Along the raylib `rlgl` module support for the `OpenGL 1.1` graphics API, it opened the door to [**multiple homebrew retro-consoles backend implementations!**](https://github.com/raylib4Consoles) It's amazing to see raylib running on +20 year old consoles like [Dreamcast](https://github.com/raylib4Consoles/raylib4Dreamcast), [PSP](https://github.com/raylib4Consoles/raylib4Psp) or [PSVita](https://github.com/psp2dev/raylib4Vita), considering the hardware constraints of those platforms and proves **raylib outstanding versability!** Those additional platforms can be found in separate repositories and have been created by the amazing programmer Antonio Jose Ramos Marquez (@psxdev).
-
+
- **`NEW` GPU Skinning support**: After lots of requests for this feature, it has been finally added to raylib thanks to the contributor Daniel Holden (@orangeduck), probably the developer that has further pushed models animations with raylib, developing two amazing tools to visualize and test animations: [GenoView](https://github.com/orangeduck/GenoView) and [BVHView](https://github.com/orangeduck/BVHView). Adding GPU skinning was a tricky feature, considering it had to be **available for all raylib supported platforms**, including limited ones like Raspberry Pi with OpenGL ES 2.0, where some advance OpenGL features are not available (UBO, SSBO, Transform Feedback) but a multi-platform solution was found to make it possible. A new example, [`models_gpu_skinning`](https://github.com/raysan5/raylib/blob/master/examples/models/models_gpu_skinning.c) has been added to illustrate this new functionality. As an extra, previous existing CPU animation system has been greatly improved, multiplying performance by a factor (simplifiying required maths).
-
+
- **`NEW` [`raymath`](https://github.com/raysan5/raylib/blob/master/src/raymath.h) C++ operators**: After several requested for this feature, C++ math operators for `Vector2`, `Vector3`, `Vector4`, `Quaternion` and `Matrix` has been added to `raymath` as an extension to current implementation. Despite being only available for C++ because C does not support it, these operators **simplify C++ code when doing math operations**.
-Beside those new big features, `raylib 5.5` comes with MANY other improvements:
+Beside those new big features, `raylib 5.5` comes with MANY other improvements:
- Normals support on batching system
- Clipboard images reading support
@@ -512,7 +512,7 @@ Beside those new big features, `raylib 5.5` comes with MANY other improvements:
- Improved GLTF animations loading
...and [much much more](https://github.com/raysan5/raylib/blob/master/CHANGELOG), including **many functions reviews and new functions added!**
-
+
Make sure to check raylib [CHANGELOG](https://github.com/raysan5/raylib/blob/master/CHANGELOG) for a detailed list of changes!
To end with, I want to **thank all the contributors (+640!**) that along the years have **greatly improved raylib** and pushed it further and better day after day. Thanks to all of them, raylib is the amazing library it is today.
@@ -522,3 +522,30 @@ Last but not least, I want to thank **raylib sponsors and all the raylib communi
**After 11 years of development, `raylib 5.5` is the best raylib ever.**
**Enjoy programming with raylib!** :)
+
+notes on raylib 6.0
+-------------------
+
+A new raylib release is finally ready, after almost 1.5 years since last release. This is the biggest release ever with many improvements to the library, thanks to the support of many amazing contributors and also thanks to the financial support of NLnet/NGI0 funds.
+
+Some astonishing numbers for this release:
+
+ - **+320** closed issues (for a TOTAL of **+2130**!)
+ - **+1800** commits since previous RELEASE (for a TOTAL of **+9600**!)
+ - **18** functions ADDED to raylib API (for a TOTAL of **599**!)
+ - **??** functions REVIEWED with fixes and improvements
+ - **+200** new contributors (for a TOTAL of **+840**!)
+
+Highlights for `raylib 6.0`:
+
+ - New Software Renderer backend [rlsw]
+ - New rcore platform backend: Memory (memory buffer)
+ - New rcore platform backend: Win32 (experimental)
+ - New rcore platform backend: Emscripten (experimental)
+ - Redesigned Skeletal Animation System
+ - Redesigned fullscreen modes and high-dpi content scaling
+ - Redesigned Build Config System [config.h]
+ - New File System API
+ - New Text Management API
+ - New tool: [rexm] raylib examples manager
+
diff --git a/README.md b/README.md
index 37e37c7c4..0bc503cd6 100644
--- a/README.md
+++ b/README.md
@@ -42,7 +42,7 @@ features
- **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.h)
- Multiple **Fonts** formats supported (TTF, OTF, FNT, BDF, sprite fonts)
- Multiple texture formats supported, including **compressed formats** (DXT, ETC, ASTC)
- - **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more!
+ - **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more!
- Flexible Materials system, supporting classic maps and **PBR maps**
- **Animated 3D models** supported (skeletal bones animation) (IQM, M3D, glTF)
- Shaders support, including model shaders and **postprocessing** shaders
@@ -111,7 +111,7 @@ raylib has been developed on Windows platform using [Notepad++](https://notepad-
learning and docs
------------------
-raylib is designed to be learned using [the examples](https://github.com/raysan5/raylib/tree/master/examples) as the main reference. There is no standard API documentation but there is a [**cheatsheet**](https://www.raylib.com/cheatsheet/cheatsheet.html) containing all the functions available on the library a short description of each one of them, input parameters and result value names should be intuitive enough to understand how each function works.
+raylib is designed to be learned using [the examples](https://github.com/raysan5/raylib/tree/master/examples) as the main reference. There is no standard API documentation but there is a [**cheatsheet**](https://www.raylib.com/cheatsheet/cheatsheet.html) containing all the functions available on the library a short description of each one of them, input parameters and result value names should be intuitive enough to understand how each function works.
Some additional documentation about raylib design can be found in [raylib GitHub Wiki](https://github.com/raysan5/raylib/wiki). Here are the relevant links:
diff --git a/ROADMAP.md b/ROADMAP.md
index a49cdbfd7..9d333089a 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -10,16 +10,15 @@ Here is a wishlist with features and ideas to improve the library. Note that fea
- [raylib 5.0 wishlist](https://github.com/raysan5/raylib/discussions/2952)
- [raylib wishlist 2022](https://github.com/raysan5/raylib/discussions/2272)
- [raylib wishlist 2021](https://github.com/raysan5/raylib/discussions/1502)
-
+
_Current version of raylib is complete and functional but there is always room for improvements._
**raylib 5.x**
- [ ] `rcore`: Support additional platforms: iOS, consoles?
- [x] `rcore_web`: Avoid GLFW dependency, functionality can be directly implemented using emscripten SDK
- - [ ] `rlgl`: Review GLSL shaders naming conventions for consistency
+ - [x] `rlgl`: Review GLSL shaders naming conventions for consistency
- [ ] `textures`: Improve compressed textures support, loading and saving
- - [ ] `rmodels`: Improve 3d objects loading, specially animations (obj, gltf)
- - [ ] `raudio`: Implement miniaudio high-level provided features
+ - [x] `rmodels`: Improve 3d objects loading, specially animations (obj, gltf)
- [x] `examples`: Review all examples, add more and better code explanations
- [x] Software renderer backend? Maybe using `Image` provided API
diff --git a/build.zig b/build.zig
index 7eb46699e..3dc95bc83 100644
--- a/build.zig
+++ b/build.zig
@@ -90,41 +90,7 @@ fn setDesktopPlatform(raylib: *std.Build.Step.Compile, platform: PlatformBackend
}
}
-/// A list of all flags from `src/config.h` that one may override
-const config_h_flags = outer: {
- // Set this value higher if compile errors happen as `src/config.h` gets larger
- @setEvalBranchQuota(1 << 20);
-
- const config_h = @embedFile("src/config.h");
- var flags: [std.mem.count(u8, config_h, "\n") + 1][]const u8 = undefined;
-
- var i = 0;
- var lines = std.mem.tokenizeScalar(u8, config_h, '\n');
- while (lines.next()) |line| {
- if (!std.mem.containsAtLeast(u8, line, 1, "SUPPORT")) continue;
- if (std.mem.containsAtLeast(u8, line, 1, "MODULE")) continue;
- if (std.mem.startsWith(u8, line, "//")) continue;
- if (std.mem.startsWith(u8, line, "#if")) continue;
-
- var flag = std.mem.trimStart(u8, line, " \t"); // Trim whitespace
- flag = flag["#define ".len - 1 ..]; // Remove #define
- flag = std.mem.trimStart(u8, flag, " \t"); // Trim whitespace
- flag = flag[0 .. std.mem.indexOf(u8, flag, " ") orelse continue]; // Flag is only one word, so capture till space
- flag = "-D" ++ flag; // Prepend with -D
-
- flags[i] = flag;
- i += 1;
- }
-
- // Uncomment this to check what flags normally get passed
- //@compileLog(flags[0..i].*);
- break :outer flags[0..i].*;
-};
-
fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, options: Options) !*std.Build.Step.Compile {
- var raylib_flags_arr: std.ArrayList([]const u8) = .empty;
- defer raylib_flags_arr.deinit(b.allocator);
-
const raylib = b.addLibrary(.{
.name = "raylib",
.linkage = options.linkage,
@@ -135,29 +101,27 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
}),
});
- try raylib_flags_arr.appendSlice(
+ raylib.root_module.addCMacro("_GNU_SOURCE", "");
+ raylib.root_module.addCMacro("GL_SILENCE_DEPRECATION", "199309L");
+
+ var raylib_flags_arr: std.ArrayList([]const u8) = .empty;
+ defer raylib_flags_arr.deinit(b.allocator);
+
+ try raylib_flags_arr.append(
b.allocator,
- &[_][]const u8{
- "-std=gnu99",
- "-D_GNU_SOURCE",
- "-DGL_SILENCE_DEPRECATION=199309L",
- "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/3674
- },
+ "-std=gnu99",
);
if (options.linkage == .dynamic) {
- try raylib_flags_arr.appendSlice(
+ try raylib_flags_arr.append(
b.allocator,
- &[_][]const u8{
- "-fPIC",
- "-DBUILD_LIBTYPE_SHARED",
- },
+ "-fPIC",
);
+
+ raylib.root_module.addCMacro("BUILD_LIBTYPE_SHARED", "");
}
if (options.config.len > 0) {
- // Sets a flag indicating the use of a custom `config.h`
- try raylib_flags_arr.append(b.allocator, "-DEXTERNAL_CONFIG_FLAGS");
// Splits a space-separated list of config flags into multiple flags
//
// Note: This means certain flags like `-x c++` won't be processed properly.
@@ -165,30 +129,9 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
var config_iter = std.mem.tokenizeScalar(u8, options.config, ' ');
// Apply config flags supplied by the user
- while (config_iter.next()) |config_flag|
+ while (config_iter.next()) |config_flag| {
try raylib_flags_arr.append(b.allocator, config_flag);
-
- // Apply all relevant configs from `src/config.h` *except* the user-specified ones
- //
- // Note: Currently using a suboptimal `O(m*n)` time algorithm where:
- // `m` corresponds roughly to the number of lines in `src/config.h`
- // `n` corresponds to the number of user-specified flags
- outer: for (config_h_flags) |flag| {
- // If a user already specified the flag, skip it
- config_iter.reset();
- while (config_iter.next()) |config_flag| {
- // For a user-specified flag to match, it must share the same prefix and have the
- // same length or be followed by an equals sign
- if (!std.mem.startsWith(u8, config_flag, flag)) continue;
- if (config_flag.len == flag.len or config_flag[flag.len] == '=') continue :outer;
- }
-
- // Otherwise, append default value from config.h to compile flags
- try raylib_flags_arr.append(b.allocator, flag);
}
- } else {
- // Set default config if no custom config got set
- try raylib_flags_arr.appendSlice(b.allocator, &config_h_flags);
}
// No GLFW required on PLATFORM_DRM
@@ -200,24 +143,38 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
c_source_files.appendSliceAssumeCapacity(&.{"src/rcore.c"});
if (options.rshapes) {
+ raylib.root_module.addCMacro("SUPPORT_MODULE_RSHAPES", "1");
try c_source_files.append(b.allocator, "src/rshapes.c");
- try raylib_flags_arr.append(b.allocator, "-DSUPPORT_MODULE_RSHAPES");
+ } else {
+ raylib.root_module.addCMacro("SUPPORT_MODULE_RSHAPES", "0");
}
+
if (options.rtextures) {
+ raylib.root_module.addCMacro("SUPPORT_MODULE_RTEXTURES", "1");
try c_source_files.append(b.allocator, "src/rtextures.c");
- try raylib_flags_arr.append(b.allocator, "-DSUPPORT_MODULE_RTEXTURES");
+ } else {
+ raylib.root_module.addCMacro("SUPPORT_MODULE_RTEXTURES", "0");
}
+
if (options.rtext) {
+ raylib.root_module.addCMacro("SUPPORT_MODULE_RTEXT", "1");
try c_source_files.append(b.allocator, "src/rtext.c");
- try raylib_flags_arr.append(b.allocator, "-DSUPPORT_MODULE_RTEXT");
+ } else {
+ raylib.root_module.addCMacro("SUPPORT_MODULE_RTEXT", "0");
}
+
if (options.rmodels) {
+ raylib.root_module.addCMacro("SUPPORT_MODULE_RMODELS", "1");
try c_source_files.append(b.allocator, "src/rmodels.c");
- try raylib_flags_arr.append(b.allocator, "-DSUPPORT_MODULE_RMODELS");
+ } else {
+ raylib.root_module.addCMacro("SUPPORT_MODULE_RMODELS", "0");
}
+
if (options.raudio) {
+ raylib.root_module.addCMacro("SUPPORT_MODULE_RAUDIO", "1");
try c_source_files.append(b.allocator, "src/raudio.c");
- try raylib_flags_arr.append(b.allocator, "-DSUPPORT_MODULE_RAUDIO");
+ } else {
+ raylib.root_module.addCMacro("SUPPORT_MODULE_RAUDIO", "0");
}
if (options.opengl_version != .auto) {
diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake
index ffc12edda..2fe05827a 100644
--- a/cmake/LibraryConfigurations.cmake
+++ b/cmake/LibraryConfigurations.cmake
@@ -143,7 +143,7 @@ elseif ("${PLATFORM}" MATCHES "SDL")
message(STATUS "Found SDL2 via find_package()")
set(PLATFORM_CPP "PLATFORM_DESKTOP_SDL")
set(LIBS_PUBLIC SDL2::SDL2)
- set(RAYLIB_DEPENDENCIES "${RAYLIB_DEPENDENCIES}\nfind_dependency(SDL3 REQUIRED)")
+ set(RAYLIB_DEPENDENCIES "${RAYLIB_DEPENDENCIES}\nfind_dependency(SDL2 REQUIRED)")
add_compile_definitions(USING_SDL2_PACKAGE)
endif()
endif()
@@ -167,6 +167,11 @@ elseif ("${PLATFORM}" MATCHES "RGFW")
set(LIBS_PRIVATE ${X11_LIBRARIES} ${OPENGL_LIBRARIES})
endif ()
+
+elseif ("${PLATFORM}" MATCHES "WebRGFW")
+ set(PLATFORM_CPP "PLATFORM_WEB_RGFW")
+ set(GRAPHICS "GRAPHICS_API_OPENGL_ES2")
+ set(CMAKE_STATIC_LIBRARY_SUFFIX ".a")
endif ()
if (NOT ${OPENGL_VERSION} MATCHES "OFF")
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index b482df0fc..6554d1327 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -189,6 +189,18 @@ foreach (example_source ${example_sources})
target_link_libraries(${example_name} "-framework OpenGL")
endif()
+ elseif (${PLATFORM} MATCHES "RGFW")
+ if (APPLE)
+ target_link_libraries(${example_name} "-framework IOKit")
+ target_link_libraries(${example_name} "-framework Cocoa")
+ target_link_libraries(${example_name} "-framework OpenGL")
+ elseif (WIN32)
+ target_link_libraries(${example_name} "-lgdi32")
+ target_link_libraries(${example_name} "-lwinmm")
+ elseif (UNIX)
+ target_link_libraries(${example_name} "-lX11")
+ target_link_libraries(${example_name} "-lXrandr")
+ endif()
endif ()
endforeach ()
diff --git a/examples/Makefile b/examples/Makefile
index 40906ebfc..899d040e3 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -69,7 +69,7 @@ endif
# Define required raylib variables
PROJECT_NAME ?= raylib_examples
-RAYLIB_VERSION ?= 5.5.0
+RAYLIB_VERSION ?= 6.0.0
RAYLIB_PATH ?= ..
# Define raylib source code path
@@ -90,6 +90,16 @@ BUILD_MODE ?= RELEASE
# Use external GLFW library instead of rglfw module
USE_EXTERNAL_GLFW ?= FALSE
+# Enable support for X11 by default on Linux when using GLFW
+# NOTE: Wayland is disabled by default, only enable if you are sure
+GLFW_LINUX_ENABLE_WAYLAND ?= FALSE
+GLFW_LINUX_ENABLE_X11 ?= TRUE
+
+# Enable support for X11 by default on Linux when using GLFW
+# NOTE: Wayland is disabled by default, only enable if you are sure
+RGFW_LINUX_ENABLE_WAYLAND ?= FALSE
+RGFW_LINUX_ENABLE_X11 ?= TRUE
+
# PLATFORM_DESKTOP_SDL: It requires SDL library to be provided externally
# WARNING: Library is not included in raylib, it MUST be configured by users
SDL_INCLUDE_PATH ?= $(RAYLIB_SRC_PATH)/external/SDL2/include
@@ -145,15 +155,6 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DRM)
endif
endif
-# RAYLIB_PATH adjustment for LINUX platform
-# TODO: Do we really need this?
-ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW)
- ifeq ($(PLATFORM_OS),LINUX)
- RAYLIB_PREFIX ?= ..
- RAYLIB_PATH = $(realpath $(RAYLIB_PREFIX))
- endif
-endif
-
# Default path for raylib on Raspberry Pi
ifeq ($(TARGET_PLATFORM),PLATFORM_DRM)
RAYLIB_PATH ?= /home/pi/raylib
@@ -294,7 +295,7 @@ endif
# Define library paths containing required libs: LDFLAGS
#------------------------------------------------------------------------------------------------
-LDFLAGS = -L. -L$(RAYLIB_RELEASE_PATH) -L$(RAYLIB_PATH)/src
+LDFLAGS = -L. -L$(RAYLIB_RELEASE_PATH)
ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW)
ifeq ($(PLATFORM_OS),WINDOWS)
@@ -394,9 +395,11 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW)
LDLIBS = -lraylib -lGL -lm -lpthread -ldl -lrt
# On Wayland, additional libraries requires
- ifeq ($(USE_WAYLAND_DISPLAY),TRUE)
+ ifeq ($(GLFW_LINUX_ENABLE_WAYLAND),TRUE)
LDLIBS += -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon
- else
+ endif
+
+ ifeq ($(GLFW_LINUX_ENABLE_X11),TRUE)
# On X11, additional libraries required
LDLIBS += -lX11
# NOTE: It seems additional libraries are not required any more, latest GLFW just dlopen them
@@ -468,7 +471,15 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW)
# Libraries for Debian GNU/Linux desktop compipling
# NOTE: Required packages: libegl1-mesa-dev
LDFLAGS += -L../src
- LDLIBS = -lraylib -lGL -lX11 -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor -lm -lpthread -ldl -lrt
+ LDLIBS = -lraylib -lm
+
+ ifeq ($(RGFW_LINUX_ENABLE_X11),TRUE)
+ LDLIBS += -lGL -lX11 -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor -lpthread -ldl -lrt
+ endif
+
+ ifeq ($(RGFW_LINUX_ENABLE_WAYLAND),TRUE)
+ LDLIBS += -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon
+ endif
# Explicit link to libc
ifeq ($(RAYLIB_LIBTYPE),SHARED)
@@ -657,10 +668,10 @@ TEXT = \
text/text_writing_anim
MODELS = \
+ models/models_animation_blend_custom \
models/models_animation_blending \
- models/models_animation_bone_blending \
models/models_animation_gpu_skinning \
- models/models_animation_playing \
+ models/models_animation_timing \
models/models_basic_voxel \
models/models_billboard_rendering \
models/models_bone_socket \
@@ -673,6 +684,7 @@ MODELS = \
models/models_heightmap_rendering \
models/models_loading \
models/models_loading_gltf \
+ models/models_loading_iqm \
models/models_loading_m3d \
models/models_loading_vox \
models/models_mesh_generation \
diff --git a/examples/Makefile.Web b/examples/Makefile.Web
index b91f59267..648e5eef4 100644
--- a/examples/Makefile.Web
+++ b/examples/Makefile.Web
@@ -69,7 +69,7 @@ endif
# Define required raylib variables
PROJECT_NAME ?= raylib_examples
-RAYLIB_VERSION ?= 5.5.0
+RAYLIB_VERSION ?= 6.0.0
RAYLIB_PATH ?= ..
# Define raylib source code path
@@ -642,10 +642,10 @@ TEXT = \
text/text_writing_anim
MODELS = \
+ models/models_animation_blend_custom \
models/models_animation_blending \
- models/models_animation_bone_blending \
models/models_animation_gpu_skinning \
- models/models_animation_playing \
+ models/models_animation_timing \
models/models_basic_voxel \
models/models_billboard_rendering \
models/models_bone_socket \
@@ -658,6 +658,7 @@ MODELS = \
models/models_heightmap_rendering \
models/models_loading \
models/models_loading_gltf \
+ models/models_loading_iqm \
models/models_loading_m3d \
models/models_loading_vox \
models/models_mesh_generation \
@@ -1199,15 +1200,15 @@ text/text_writing_anim: text/text_writing_anim.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
# Compile MODELS examples
-models/models_animation_blending: models/models_animation_blending.c
+models/models_animation_blend_custom: models/models_animation_blend_custom.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
- --preload-file models/resources/models/gltf/robot.glb@resources/models/gltf/robot.glb \
+ --preload-file models/resources/models/gltf/greenman.glb@resources/models/gltf/greenman.glb \
--preload-file models/resources/shaders/glsl100/skinning.vs@resources/shaders/glsl100/skinning.vs \
--preload-file models/resources/shaders/glsl100/skinning.fs@resources/shaders/glsl100/skinning.fs
-models/models_animation_bone_blending: models/models_animation_bone_blending.c
+models/models_animation_blending: models/models_animation_blending.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
- --preload-file models/resources/models/gltf/greenman.glb@resources/models/gltf/greenman.glb \
+ --preload-file models/resources/models/gltf/robot.glb@resources/models/gltf/robot.glb \
--preload-file models/resources/shaders/glsl100/skinning.vs@resources/shaders/glsl100/skinning.vs \
--preload-file models/resources/shaders/glsl100/skinning.fs@resources/shaders/glsl100/skinning.fs
@@ -1217,11 +1218,9 @@ models/models_animation_gpu_skinning: models/models_animation_gpu_skinning.c
--preload-file models/resources/shaders/glsl100/skinning.vs@resources/shaders/glsl100/skinning.vs \
--preload-file models/resources/shaders/glsl100/skinning.fs@resources/shaders/glsl100/skinning.fs
-models/models_animation_playing: models/models_animation_playing.c
+models/models_animation_timing: models/models_animation_timing.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
- --preload-file models/resources/models/iqm/guy.iqm@resources/models/iqm/guy.iqm \
- --preload-file models/resources/models/iqm/guytex.png@resources/models/iqm/guytex.png \
- --preload-file models/resources/models/iqm/guyanim.iqm@resources/models/iqm/guyanim.iqm
+ --preload-file models/resources/models/gltf/robot.glb@resources/models/gltf/robot.glb
models/models_basic_voxel: models/models_basic_voxel.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
@@ -1276,6 +1275,12 @@ models/models_loading_gltf: models/models_loading_gltf.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
--preload-file models/resources/models/gltf/robot.glb@resources/models/gltf/robot.glb
+models/models_loading_iqm: models/models_loading_iqm.c
+ $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
+ --preload-file models/resources/models/iqm/guy.iqm@resources/models/iqm/guy.iqm \
+ --preload-file models/resources/models/iqm/guytex.png@resources/models/iqm/guytex.png \
+ --preload-file models/resources/models/iqm/guyanim.iqm@resources/models/iqm/guyanim.iqm
+
models/models_loading_m3d: models/models_loading_m3d.c
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
--preload-file models/resources/models/m3d/cesium_man.m3d@resources/models/m3d/cesium_man.m3d
diff --git a/examples/README.md b/examples/README.md
index 2b8b9dd98..a4e65b9f5 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -17,7 +17,7 @@ You may find it easier to use than other toolchains, especially when it comes to
- `zig build [module]` to compile all examples for a module (e.g. `zig build core`)
- `zig build [example]` to compile _and run_ a particular example (e.g. `zig build core_basic_window`)
-## EXAMPLES COLLECTION [TOTAL: 207]
+## EXAMPLES COLLECTION [TOTAL: 208]
### category: core [49]
@@ -182,13 +182,13 @@ Examples using raylib text functionality, including sprite fonts loading/generat
| [text_words_alignment](text/text_words_alignment.c) |
| ⭐☆☆☆ | 5.6-dev | 5.6-dev | [JP Mortiboys](https://github.com/themushroompirates) |
| [text_strings_management](text/text_strings_management.c) |
| ⭐⭐⭐☆ | 5.6-dev | 5.6-dev | [David Buzatto](https://github.com/davidbuzatto) |
-### category: models [29]
+### category: models [30]
Examples using raylib models functionality, including models loading/generation and drawing, provided by raylib [models](../src/rmodels.c) module.
| example | image | difficulty
level | version
created | last version
updated | original
developer |
|-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------|
-| [models_animation_playing](models/models_animation_playing.c) |
| ⭐⭐☆☆ | 2.5 | 3.5 | [Culacant](https://github.com/culacant) |
+| [models_loading_iqm](models/models_loading_iqm.c) |
| ⭐⭐☆☆ | 2.5 | 3.5 | [Culacant](https://github.com/culacant) |
| [models_billboard_rendering](models/models_billboard_rendering.c) |
| ⭐⭐⭐☆ | 1.3 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) |
| [models_box_collisions](models/models_box_collisions.c) |
| ⭐☆☆☆ | 1.3 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) |
| [models_cubicmap_rendering](models/models_cubicmap_rendering.c) |
| ⭐⭐☆☆ | 1.8 | 3.5 | [Ramon Santamaria](https://github.com/raysan5) |
@@ -215,8 +215,9 @@ Examples using raylib models functionality, including models loading/generation
| [models_rotating_cube](models/models_rotating_cube.c) |
| ⭐☆☆☆ | 5.6-dev | 5.6-dev | [Jopestpe](https://github.com/jopestpe) |
| [models_decals](models/models_decals.c) |
| ⭐⭐⭐⭐️ | 5.6-dev | 5.6-dev | [JP Mortiboys](https://github.com/themushroompirates) |
| [models_directional_billboard](models/models_directional_billboard.c) |
| ⭐⭐☆☆ | 5.6-dev | 5.6 | [Robin](https://github.com/RobinsAviary) |
-| [models_animation_bone_blending](models/models_animation_bone_blending.c) |
| ⭐⭐⭐⭐️ | 5.5 | 5.5 | [dmitrii-brand](https://github.com/dmitrii-brand) |
+| [models_animation_blend_custom](models/models_animation_blend_custom.c) |
| ⭐⭐⭐⭐️ | 5.5 | 5.5 | [dmitrii-brand](https://github.com/dmitrii-brand) |
| [models_animation_blending](models/models_animation_blending.c) |
| ☆☆☆☆ | 5.5 | 5.6-dev | [Kirandeep](https://github.com/Kirandeep-Singh-Khehra) |
+| [models_animation_timming](models/models_animation_timming.c) |
| ⭐⭐☆☆ | 5.6 | 5.6 | [Ramon Santamaria](https://github.com/raysan5) |
### category: shaders [34]
diff --git a/examples/audio/audio_raw_stream.c b/examples/audio/audio_raw_stream.c
index b327e92af..64c9b5a50 100644
--- a/examples/audio/audio_raw_stream.c
+++ b/examples/audio/audio_raw_stream.c
@@ -4,53 +4,22 @@
*
* Example complexity rating: [★★★☆] 3/4
*
-* Example originally created with raylib 1.6, last time updated with raylib 4.2
+* Example originally created with raylib 1.6, last time updated with raylib 6.0
*
* Example created by Ramon Santamaria (@raysan5) and reviewed by James Hofmann (@triplefox)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
-* Copyright (c) 2015-2025 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox)
+* Copyright (c) 2015-2026 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox)
*
********************************************************************************************/
#include "raylib.h"
+#include
-#include // Required for: malloc(), free()
-#include // Required for: sinf()
-#include // Required for: memcpy()
-
-#define MAX_SAMPLES 512
-#define MAX_SAMPLES_PER_UPDATE 4096
-
-// Cycles per second (hz)
-float frequency = 440.0f;
-
-// Audio frequency, for smoothing
-float audioFrequency = 440.0f;
-
-// Previous value, used to test if sine needs to be rewritten, and to smoothly modulate frequency
-float oldFrequency = 1.0f;
-
-// Index for audio rendering
-float sineIdx = 0.0f;
-
-// Audio input processing callback
-void AudioInputCallback(void *buffer, unsigned int frames)
-{
- audioFrequency = frequency + (audioFrequency - frequency)*0.95f;
-
- float incr = audioFrequency/44100.0f;
- short *d = (short *)buffer;
-
- for (unsigned int i = 0; i < frames; i++)
- {
- d[i] = (short)(32000.0f*sinf(2*PI*sineIdx));
- sineIdx += incr;
- if (sineIdx > 1.0f) sineIdx -= 1.0f;
- }
-}
+#define BUFFER_SIZE 4096
+#define SAMPLE_RATE 44100
//------------------------------------------------------------------------------------
// Program main entry point
@@ -64,43 +33,24 @@ int main(void)
InitWindow(screenWidth, screenHeight, "raylib [audio] example - raw stream");
- InitAudioDevice(); // Initialize audio device
+ InitAudioDevice();
- SetAudioStreamBufferSizeDefault(MAX_SAMPLES_PER_UPDATE);
+ // Set the number of samples the stream will keep in memory at a time to BUFFER_SIZE
+ SetAudioStreamBufferSizeDefault(BUFFER_SIZE);
+ float buffer[BUFFER_SIZE] = {};
- // Init raw audio stream (sample rate: 44100, sample size: 16bit-short, channels: 1-mono)
- AudioStream stream = LoadAudioStream(44100, 16, 1);
+ // Init raw audio stream (sample rate: 44100, sample size: 32bit-float, channels: 1-mono)
+ AudioStream stream = LoadAudioStream(SAMPLE_RATE, 32, 1);
+ float pan = 0.0f;
+ SetAudioStreamPan(stream, pan);
+ PlayAudioStream(stream);
- SetAudioStreamCallback(stream, AudioInputCallback);
+ int sineFrequency = 440;
+ int newSineFrequency = 440;
+ int sineIndex = 0;
+ double sineStartTime = 0.0;
- // Buffer for the single cycle waveform we are synthesizing
- short *data = (short *)malloc(sizeof(short)*MAX_SAMPLES);
-
- // Frame buffer, describing the waveform when repeated over the course of a frame
- short *writeBuf = (short *)malloc(sizeof(short)*MAX_SAMPLES_PER_UPDATE);
-
- PlayAudioStream(stream); // Start processing stream buffer (no data loaded currently)
-
- // Position read in to determine next frequency
- Vector2 mousePosition = { -100.0f, -100.0f };
-
- /*
- // Cycles per second (hz)
- float frequency = 440.0f;
-
- // Previous value, used to test if sine needs to be rewritten, and to smoothly modulate frequency
- float oldFrequency = 1.0f;
-
- // Cursor to read and copy the samples of the sine wave buffer
- int readCursor = 0;
- */
-
- // Computed size in samples of the sine wave
- int waveLength = 1;
-
- Vector2 position = { 0, 0 };
-
- SetTargetFPS(30); // Set our game to run at 30 frames-per-second
+ SetTargetFPS(30);
//--------------------------------------------------------------------------------------
// Main game loop
@@ -108,91 +58,76 @@ int main(void)
{
// Update
//----------------------------------------------------------------------------------
- mousePosition = GetMousePosition();
- if (IsMouseButtonDown(MOUSE_BUTTON_LEFT))
+ if (IsKeyDown(KEY_UP))
{
- float fp = (float)(mousePosition.y);
- frequency = 40.0f + (float)(fp);
+ newSineFrequency += 10;
+ if (newSineFrequency > 12500) newSineFrequency = 12500;
+ }
- float pan = (float)(mousePosition.x)/(float)screenWidth;
+ if (IsKeyDown(KEY_DOWN))
+ {
+ newSineFrequency -= 10;
+ if (newSineFrequency < 20) newSineFrequency = 20;
+ }
+
+ if (IsKeyDown(KEY_LEFT))
+ {
+ pan -= 0.01f;
+ if (pan < -1.0f) pan = -1.0f;
SetAudioStreamPan(stream, pan);
}
- // Rewrite the sine wave
- // Compute two cycles to allow the buffer padding, simplifying any modulation, resampling, etc.
- if (frequency != oldFrequency)
+ if (IsKeyDown(KEY_RIGHT))
{
- // Compute wavelength. Limit size in both directions
- //int oldWavelength = waveLength;
- waveLength = (int)(22050/frequency);
- if (waveLength > MAX_SAMPLES/2) waveLength = MAX_SAMPLES/2;
- if (waveLength < 1) waveLength = 1;
-
- // Write sine wave
- for (int i = 0; i < waveLength*2; i++)
- {
- data[i] = (short)(sinf(((2*PI*(float)i/waveLength)))*32000);
- }
- // Make sure the rest of the line is flat
- for (int j = waveLength*2; j < MAX_SAMPLES; j++)
- {
- data[j] = (short)0;
- }
-
- // Scale read cursor's position to minimize transition artifacts
- //readCursor = (int)(readCursor*((float)waveLength/(float)oldWavelength));
- oldFrequency = frequency;
+ pan += 0.01f;
+ if (pan > 1.0f) pan = 1.0f;
+ SetAudioStreamPan(stream, pan);
}
- /*
- // Refill audio stream if required
if (IsAudioStreamProcessed(stream))
{
- // Synthesize a buffer that is exactly the requested size
- int writeCursor = 0;
-
- while (writeCursor < MAX_SAMPLES_PER_UPDATE)
+ for (int i = 0; i < BUFFER_SIZE; i++)
{
- // Start by trying to write the whole chunk at once
- int writeLength = MAX_SAMPLES_PER_UPDATE-writeCursor;
+ int wavelength = SAMPLE_RATE/sineFrequency;
+ buffer[i] = sin(2*PI*sineIndex/wavelength);
+ sineIndex++;
- // Limit to the maximum readable size
- int readLength = waveLength-readCursor;
-
- if (writeLength > readLength) writeLength = readLength;
-
- // Write the slice
- memcpy(writeBuf + writeCursor, data + readCursor, writeLength*sizeof(short));
-
- // Update cursors and loop audio
- readCursor = (readCursor + writeLength)%waveLength;
-
- writeCursor += writeLength;
+ if (sineIndex >= wavelength)
+ {
+ sineFrequency = newSineFrequency;
+ sineIndex = 0;
+ sineStartTime = GetTime();
+ }
}
- // Copy finished frame to audio stream
- UpdateAudioStream(stream, writeBuf, MAX_SAMPLES_PER_UPDATE);
+ UpdateAudioStream(stream, buffer, BUFFER_SIZE);
}
- */
+
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
-
ClearBackground(RAYWHITE);
-
- DrawText(TextFormat("sine frequency: %i",(int)frequency), GetScreenWidth() - 220, 10, 20, RED);
- DrawText("click mouse button to change frequency or pan", 10, 10, 20, DARKGRAY);
-
- // Draw the current buffer state proportionate to the screen
+
+ DrawText(TextFormat("sine frequency: %i", sineFrequency), screenWidth - 220, 10, 20, RED);
+ DrawText(TextFormat("pan: %.2f", pan), screenWidth - 220, 30, 20, RED);
+ DrawText("Up/down to change frequency", 10, 10, 20, DARKGRAY);
+ DrawText("Left/right to pan", 10, 30, 20, DARKGRAY);
+
+ int windowStart = (GetTime() - sineStartTime)*SAMPLE_RATE;
+ int windowSize = 0.1f*SAMPLE_RATE;
+ int wavelength = SAMPLE_RATE/sineFrequency;
+
+ // Draw a sine wave with the same frequency as the one being sent to the audio stream
for (int i = 0; i < screenWidth; i++)
{
- position.x = (float)i;
- position.y = 250 + 50*data[i*MAX_SAMPLES/screenWidth]/32000.0f;
-
- DrawPixelV(position, RED);
+ int t0 = windowStart + i*windowSize/screenWidth;
+ int t1 = windowStart + (i + 1)*windowSize/screenWidth;
+ Vector2 startPos = { i, 250 + 50*sin(2*PI*t0/wavelength) };
+ Vector2 endPos = { i + 1, 250 + 50*sin(2*PI*t1/wavelength) };
+ DrawLineV(startPos, endPos, RED);
}
EndDrawing();
@@ -201,9 +136,6 @@ int main(void)
// De-Initialization
//--------------------------------------------------------------------------------------
- free(data); // Unload sine wave data
- free(writeBuf); // Unload write buffer
-
UnloadAudioStream(stream); // Close raw audio stream and delete buffers from RAM
CloseAudioDevice(); // Close audio device (music streaming is automatically stopped)
diff --git a/examples/audio/audio_raw_stream.png b/examples/audio/audio_raw_stream.png
index 344f4a710..b061abd2d 100644
Binary files a/examples/audio/audio_raw_stream.png and b/examples/audio/audio_raw_stream.png differ
diff --git a/examples/audio/audio_spectrum_visualizer.c b/examples/audio/audio_spectrum_visualizer.c
index 5cde7e9bd..d2dbbbb57 100644
--- a/examples/audio/audio_spectrum_visualizer.c
+++ b/examples/audio/audio_spectrum_visualizer.c
@@ -115,7 +115,7 @@ int main(void)
.tapbackPos = 0.01f
};
- int wavCursor = 0;
+ unsigned int wavCursor = 0;
const short *wavPCM16 = wav.data;
short chunkSamples[AUDIO_STREAM_RING_BUFFER_SIZE] = { 0 };
@@ -274,8 +274,8 @@ static void CaptureFrame(FFTData *fftData, const float *audioSamples)
static void RenderFrame(const FFTData *fftData, Image *fftImage)
{
- double framesSinceTapback = floor(fftData->tapbackPos/WINDOW_TIME);
- framesSinceTapback = Clamp(framesSinceTapback, 0.0, fftData->fftHistoryLen - 1);
+ float framesSinceTapback = floorf((float)(fftData->tapbackPos/WINDOW_TIME));
+ framesSinceTapback = Clamp(framesSinceTapback, 0.0f, (float)(fftData->fftHistoryLen - 1));
int historyPosition = (fftData->historyPos - 1 - (int)framesSinceTapback)%fftData->fftHistoryLen;
if (historyPosition < 0) historyPosition += fftData->fftHistoryLen;
diff --git a/examples/audio/audio_stream_callback.c b/examples/audio/audio_stream_callback.c
new file mode 100644
index 000000000..e31e8e80b
--- /dev/null
+++ b/examples/audio/audio_stream_callback.c
@@ -0,0 +1,236 @@
+/*******************************************************************************************
+*
+* raylib [audio] example - stream callback
+*
+* Example complexity rating: [★★★☆] 3/4
+*
+* Example originally created with raylib 6.0, last time updated with raylib 6.0
+*
+* Example created by Dan Hoang (@dan-hoang) and reviewed by
+*
+* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+* BSD-like license that allows static linking with closed source software
+*
+* Copyright (c) 2015-2026
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include
+#include
+
+#define BUFFER_SIZE 4096
+#define SAMPLE_RATE 44100
+
+// This example sends a wave to the audio device
+// The user gets the choice of four waves: sine, square, triangle, and sawtooth
+// A stream is set up to play to the audio device
+// The stream is hooked to a callback that generates a wave
+// The callback used is determined by user choice
+typedef enum
+{
+ SINE,
+ SQUARE,
+ TRIANGLE,
+ SAWTOOTH
+} WaveType;
+
+int waveFrequency = 440;
+int newWaveFrequency = 440;
+int waveIndex = 0;
+
+// Buffer for keeping the last second of uploaded audio, part of which will be drawn on the screen
+float buffer[SAMPLE_RATE] = {};
+
+void SineCallback(void *framesOut, unsigned int frameCount)
+{
+ int wavelength = SAMPLE_RATE/waveFrequency;
+
+ // Synthesize the sine wave
+ for (int i = 0; i < frameCount; i++)
+ {
+ ((float *)framesOut)[i] = sin(2*PI*waveIndex/wavelength);
+
+ waveIndex++;
+
+ if (waveIndex >= wavelength)
+ {
+ waveFrequency = newWaveFrequency;
+ waveIndex = 0;
+ }
+ }
+
+ // Save the synthesized samples for later drawing
+ for (int i = 0; i < SAMPLE_RATE - frameCount; i++) buffer[i] = buffer[i + frameCount];
+ for (int i = 0; i < frameCount; i++) buffer[SAMPLE_RATE - frameCount + i] = ((float *)framesOut)[i];
+}
+
+void SquareCallback(void *framesOut, unsigned int frameCount)
+{
+ int wavelength = SAMPLE_RATE/waveFrequency;
+
+ // Synthesize the square wave
+ for (int i = 0; i < frameCount; i++)
+ {
+ ((float *)framesOut)[i] = (waveIndex < wavelength/2)? 1 : -1;
+ waveIndex++;
+
+ if (waveIndex >= wavelength)
+ {
+ waveFrequency = newWaveFrequency;
+ waveIndex = 0;
+ }
+ }
+
+ // Save the synthesized samples for later drawing
+ for (int i = 0; i < SAMPLE_RATE - frameCount; i++) buffer[i] = buffer[i + frameCount];
+ for (int i = 0; i < frameCount; i++) buffer[SAMPLE_RATE - frameCount + i] = ((float *)framesOut)[i];
+}
+
+void TriangleCallback(void *framesOut, unsigned int frameCount)
+{
+ int wavelength = SAMPLE_RATE/waveFrequency;
+
+ // Synthesize the triangle wave
+ for (int i = 0; i < frameCount; i++)
+ {
+ ((float *)framesOut)[i] = (waveIndex < wavelength/2)? (-1 + 2.0f*waveIndex/(wavelength/2)) : (1 - 2.0f*(waveIndex - wavelength/2)/(wavelength/2));
+ waveIndex++;
+
+ if (waveIndex >= wavelength)
+ {
+ waveFrequency = newWaveFrequency;
+ waveIndex = 0;
+ }
+ }
+
+ // Save the synthesized samples for later drawing
+ for (int i = 0; i < SAMPLE_RATE - frameCount; i++) buffer[i] = buffer[i + frameCount];
+ for (int i = 0; i < frameCount; i++) buffer[SAMPLE_RATE - frameCount + i] = ((float *)framesOut)[i];
+}
+
+void SawtoothCallback(void *framesOut, unsigned int frameCount)
+{
+ int wavelength = SAMPLE_RATE/waveFrequency;
+
+ // Synthesize the sawtooth wave
+ for (int i = 0; i < frameCount; i++)
+ {
+ ((float *)framesOut)[i] = -1 + 2.0f*waveIndex/wavelength;
+ waveIndex++;
+
+ if (waveIndex >= wavelength)
+ {
+ waveFrequency = newWaveFrequency;
+ waveIndex = 0;
+ }
+ }
+
+ // Save the synthesized samples for later drawing
+ for (int i = 0; i < SAMPLE_RATE - frameCount; i++) buffer[i] = buffer[i + frameCount];
+ for (int i = 0; i < frameCount; i++) buffer[SAMPLE_RATE - frameCount + i] = ((float *)framesOut)[i];
+}
+
+AudioCallback waveCallbacks[] = { SineCallback, SquareCallback, TriangleCallback, SawtoothCallback };
+char *waveTypesAsString[] = { "sine", "square", "triangle", "sawtooth" };
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ InitWindow(screenWidth, screenHeight, "raylib [audio] example - stream callback");
+
+ InitAudioDevice();
+
+ // Set the number of samples the stream will keep in memory at a time to BUFFER_SIZE
+ SetAudioStreamBufferSizeDefault(BUFFER_SIZE);
+
+ // Init raw audio stream (sample rate: 44100, sample size: 32bit-float, channels: 1-mono)
+ AudioStream stream = LoadAudioStream(SAMPLE_RATE, 32, 1);
+ PlayAudioStream(stream);
+
+ // Configure it so that waveCallbacks[waveType] is called whenever stream is out of samples
+ WaveType waveType = SINE;
+ SetAudioStreamCallback(stream, waveCallbacks[waveType]);
+
+ SetTargetFPS(30);
+ //--------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+
+ if (IsKeyDown(KEY_UP))
+ {
+ newWaveFrequency += 10;
+ if (newWaveFrequency > 12500) newWaveFrequency = 12500;
+ }
+
+ if (IsKeyDown(KEY_DOWN))
+ {
+ newWaveFrequency -= 10;
+ if (newWaveFrequency < 20) newWaveFrequency = 20;
+ }
+
+ if (IsKeyPressed(KEY_LEFT))
+ {
+ if (waveType == SINE) waveType = SAWTOOTH;
+ else if (waveType == SQUARE) waveType = SINE;
+ else if (waveType == TRIANGLE) waveType = SQUARE;
+ else waveType = TRIANGLE;
+
+ SetAudioStreamCallback(stream, waveCallbacks[waveType]);
+ }
+
+ if (IsKeyPressed(KEY_RIGHT))
+ {
+ if (waveType == SINE) waveType = SQUARE;
+ else if (waveType == SQUARE) waveType = TRIANGLE;
+ else if (waveType == TRIANGLE) waveType = SAWTOOTH;
+ else waveType = SINE;
+
+ SetAudioStreamCallback(stream, waveCallbacks[waveType]);
+ }
+
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground(RAYWHITE);
+ DrawText(TextFormat("frequency: %i", newWaveFrequency), screenWidth - 220, 10, 20, RED);
+ DrawText(TextFormat("wave type: %s", waveTypesAsString[waveType]), screenWidth - 220, 30, 20, RED);
+ DrawText("Up/down to change frequency", 10, 10, 20, DARKGRAY);
+ DrawText("Left/right to change wave type", 10, 30, 20, DARKGRAY);
+
+ // Draw the last 10 ms of uploaded audio
+ for (int i = 0; i < screenWidth; i++)
+ {
+ Vector2 startPos = { i, 250 - 50*buffer[SAMPLE_RATE - SAMPLE_RATE/100 + i*SAMPLE_RATE/100/screenWidth] };
+ Vector2 endPos = { i + 1, 250 - 50*buffer[SAMPLE_RATE - SAMPLE_RATE/100 + (i + 1)*SAMPLE_RATE/100/screenWidth] };
+ DrawLineV(startPos, endPos, RED);
+ }
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ UnloadAudioStream(stream); // Close raw audio stream and delete buffers from RAM
+ CloseAudioDevice(); // Close audio device (music streaming is automatically stopped)
+
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
diff --git a/examples/audio/audio_stream_callback.png b/examples/audio/audio_stream_callback.png
new file mode 100644
index 000000000..12305744e
Binary files /dev/null and b/examples/audio/audio_stream_callback.png differ
diff --git a/examples/core/core_compute_hash.c b/examples/core/core_compute_hash.c
index 505ffea7b..f7d3ba9ac 100644
--- a/examples/core/core_compute_hash.c
+++ b/examples/core/core_compute_hash.c
@@ -60,7 +60,7 @@ int main(void)
//----------------------------------------------------------------------------------
if (btnComputeHashes)
{
- int textInputLen = strlen(textInput);
+ int textInputLen = (int)strlen(textInput);
// Encode data to Base64 string (includes NULL terminator), memory must be MemFree()
base64Text = EncodeDataBase64((unsigned char *)textInput, textInputLen, &base64TextSize);
diff --git a/examples/core/core_directory_files.c b/examples/core/core_directory_files.c
index b0a204d37..a5b19078f 100644
--- a/examples/core/core_directory_files.c
+++ b/examples/core/core_directory_files.c
@@ -78,7 +78,7 @@ int main(void)
GuiSetStyle(LISTVIEW, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
GuiSetStyle(LISTVIEW, TEXT_PADDING, 40);
- GuiListViewEx((Rectangle){ 0, 50, GetScreenWidth(), GetScreenHeight() - 50 },
+ GuiListViewEx((Rectangle){ 0, 50, (float)GetScreenWidth(), (float)GetScreenHeight() - 50 },
files.paths, files.count, &listScrollIndex, &listItemActive, &listItemFocused);
EndDrawing();
diff --git a/examples/core/core_highdpi_testbed.c b/examples/core/core_highdpi_testbed.c
index 1c39a9d2c..cfff1f3c5 100644
--- a/examples/core/core_highdpi_testbed.c
+++ b/examples/core/core_highdpi_testbed.c
@@ -86,10 +86,10 @@ int main(void)
// Draw mouse position
DrawCircleV(GetMousePosition(), 20, MAROON);
- DrawRectangle(mousePos.x - 25, mousePos.y, 50, 2, BLACK);
- DrawRectangle(mousePos.x, mousePos.y - 25, 2, 50, BLACK);
+ DrawRectangleRec((Rectangle) { mousePos.x - 25, mousePos.y, 50, 2 }, BLACK);
+ DrawRectangleRec((Rectangle) { mousePos.x, mousePos.y - 25, 2, 50 }, BLACK);
DrawText(TextFormat("[%i,%i]", GetMouseX(), GetMouseY()), mousePos.x - 44,
- (mousePos.y > GetScreenHeight() - 60)? mousePos.y - 46 : mousePos.y + 30, 20, BLACK);
+ (mousePos.y > GetScreenHeight() - 60)? (int)mousePos.y - 46 : (int)mousePos.y + 30, 20, BLACK);
EndDrawing();
//----------------------------------------------------------------------------------
diff --git a/examples/core/core_keyboard_testbed.c b/examples/core/core_keyboard_testbed.c
index 904e9c90a..2ea5c361a 100644
--- a/examples/core/core_keyboard_testbed.c
+++ b/examples/core/core_keyboard_testbed.c
@@ -133,7 +133,7 @@ int main(void)
// ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, IMP, CLOSE
for (int i = 0, recOffsetX = 0; i < 15; i++)
{
- GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y, line01KeyWidths[i], 30 }, line01Keys[i]);
+ GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y, (float)line01KeyWidths[i], 30.0f }, line01Keys[i]);
recOffsetX += line01KeyWidths[i] + KEY_REC_SPACING;
}
@@ -141,7 +141,7 @@ int main(void)
// `, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, -, =, BACKSPACE, DEL
for (int i = 0, recOffsetX = 0; i < 15; i++)
{
- GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + KEY_REC_SPACING, line02KeyWidths[i], 38 }, line02Keys[i]);
+ GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + KEY_REC_SPACING, (float)line02KeyWidths[i], 38.0f }, line02Keys[i]);
recOffsetX += line02KeyWidths[i] + KEY_REC_SPACING;
}
@@ -149,7 +149,7 @@ int main(void)
// TAB, Q, W, E, R, T, Y, U, I, O, P, [, ], \, INS
for (int i = 0, recOffsetX = 0; i < 15; i++)
{
- GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38 + KEY_REC_SPACING*2, line03KeyWidths[i], 38 }, line03Keys[i]);
+ GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38 + KEY_REC_SPACING*2, (float)line03KeyWidths[i], 38.0f }, line03Keys[i]);
recOffsetX += line03KeyWidths[i] + KEY_REC_SPACING;
}
@@ -157,7 +157,7 @@ int main(void)
// MAYUS, A, S, D, F, G, H, J, K, L, ;, ', ENTER, REPAG
for (int i = 0, recOffsetX = 0; i < 14; i++)
{
- GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38*2 + KEY_REC_SPACING*3, line04KeyWidths[i], 38 }, line04Keys[i]);
+ GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38*2 + KEY_REC_SPACING*3, (float)line04KeyWidths[i], 38.0f }, line04Keys[i]);
recOffsetX += line04KeyWidths[i] + KEY_REC_SPACING;
}
@@ -165,7 +165,7 @@ int main(void)
// LSHIFT, Z, X, C, V, B, N, M, ,, ., /, RSHIFT, UP, AVPAG
for (int i = 0, recOffsetX = 0; i < 14; i++)
{
- GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38*3 + KEY_REC_SPACING*4, line05KeyWidths[i], 38 }, line05Keys[i]);
+ GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38*3 + KEY_REC_SPACING*4, (float)line05KeyWidths[i], 38.0f }, line05Keys[i]);
recOffsetX += line05KeyWidths[i] + KEY_REC_SPACING;
}
@@ -173,7 +173,7 @@ int main(void)
// LCTRL, WIN, LALT, SPACE, ALTGR, \, FN, RCTRL, LEFT, DOWN, RIGHT
for (int i = 0, recOffsetX = 0; i < 11; i++)
{
- GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38*4 + KEY_REC_SPACING*5, line06KeyWidths[i], 38 }, line06Keys[i]);
+ GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38*4 + KEY_REC_SPACING*5, (float)line06KeyWidths[i], 38.0f }, line06Keys[i]);
recOffsetX += line06KeyWidths[i] + KEY_REC_SPACING;
}
@@ -316,12 +316,12 @@ static void GuiKeyboardKey(Rectangle bounds, int key)
if (IsKeyDown(key))
{
DrawRectangleLinesEx(bounds, 2.0f, MAROON);
- DrawText(GetKeyText(key), bounds.x + 4, bounds.y + 4, 10, MAROON);
+ DrawText(GetKeyText(key), (int)(bounds.x + 4), (int)(bounds.y + 4), 10, MAROON);
}
else
{
DrawRectangleLinesEx(bounds, 2.0f, DARKGRAY);
- DrawText(GetKeyText(key), bounds.x + 4, bounds.y + 4, 10, DARKGRAY);
+ DrawText(GetKeyText(key), (int)(bounds.x + 4), (int)(bounds.y + 4), 10, DARKGRAY);
}
}
diff --git a/examples/core/core_viewport_scaling.c b/examples/core/core_viewport_scaling.c
index 4a608e96d..716b2f42d 100644
--- a/examples/core/core_viewport_scaling.c
+++ b/examples/core/core_viewport_scaling.c
@@ -177,10 +177,10 @@ int main(void)
DrawRectangleRec(increaseTypeButton, SKYBLUE);
DrawRectangleRec(decreaseResolutionButton, SKYBLUE);
DrawRectangleRec(increaseResolutionButton, SKYBLUE);
- DrawText("<", decreaseTypeButton.x + 3, decreaseTypeButton.y + 1, 10, BLACK);
- DrawText(">", increaseTypeButton.x + 3, increaseTypeButton.y + 1, 10, BLACK);
- DrawText("<", decreaseResolutionButton.x + 3, decreaseResolutionButton.y + 1, 10, BLACK);
- DrawText(">", increaseResolutionButton.x + 3, increaseResolutionButton.y + 1, 10, BLACK);
+ DrawText("<", (int)decreaseTypeButton.x + 3, (int)decreaseTypeButton.y + 1, 10, BLACK);
+ DrawText(">", (int)increaseTypeButton.x + 3, (int)increaseTypeButton.y + 1, 10, BLACK);
+ DrawText("<", (int)decreaseResolutionButton.x + 3, (int)decreaseResolutionButton.y + 1, 10, BLACK);
+ DrawText(">", (int)increaseResolutionButton.x + 3, (int)increaseResolutionButton.y + 1, 10, BLACK);
EndDrawing();
//----------------------------------------------------------------------------------
@@ -308,7 +308,7 @@ static void ResizeRenderSize(ViewportType viewportType, int *screenWidth, int *s
}
UnloadRenderTexture(*target);
- *target = LoadRenderTexture(sourceRect->width, -sourceRect->height);
+ *target = LoadRenderTexture((int)sourceRect->width, -(int)sourceRect->height);
}
// Example how to calculate position on RenderTexture
diff --git a/examples/core/raygui.h b/examples/core/raygui.h
index 42c5ae7bc..03e487912 100644
--- a/examples/core/raygui.h
+++ b/examples/core/raygui.h
@@ -646,6 +646,7 @@ typedef enum {
// ProgressBar
typedef enum {
PROGRESS_PADDING = 16, // ProgressBar internal padding
+ PROGRESS_SIDE, // ProgressBar increment side: 0-left->right, 1-right-left
} GuiProgressBarProperty;
// ScrollBar
@@ -3522,7 +3523,15 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight
}
// Draw slider internal progress bar (depends on state)
- GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)));
+ if (GuiGetStyle(PROGRESSBAR, PROGRESS_SIDE) == 0) // Left-->Right
+ {
+ GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)));
+ }
+ else // Right-->Left
+ {
+ progress.x = bounds.x + bounds.width - progress.width - GuiGetStyle(PROGRESSBAR, BORDER_WIDTH);
+ GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)));
+ }
}
// Draw left/right text if provided
@@ -5119,11 +5128,11 @@ static const char *GetTextIcon(const char *text, int *iconId)
// Get text divided into lines (by line-breaks '\n')
// WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator!
-static char **GetTextLines(const char *text, int *count)
+static const char **GetTextLines(const char *text, int *count)
{
#define RAYGUI_MAX_TEXT_LINES 128
- static char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 };
+ static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 };
for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings
int textLength = (int)strlen(text);
@@ -5193,7 +5202,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C
// WARNING: GuiTextSplit() function can't be used now because it can have already been used
// before the GuiDrawText() call and its buffer is static, it would be overriden :(
int lineCount = 0;
- char **lines = GetTextLines(text, &lineCount);
+ const char **lines = GetTextLines(text, &lineCount);
// Text style variables
//int alignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT);
diff --git a/examples/examples_list.txt b/examples/examples_list.txt
index 85b997ec4..dc3312893 100644
--- a/examples/examples_list.txt
+++ b/examples/examples_list.txt
@@ -144,7 +144,7 @@ text;text_codepoints_loading;★★★☆;4.2;4.2;2022;2025;"Ramon Santamaria";@
text;text_inline_styling;★★★☆;5.6-dev;5.6-dev;2025;2025;"Wagner Barongello";@SultansOfCode
text;text_words_alignment;★☆☆☆;5.6-dev;5.6-dev;2025;2025;"JP Mortiboys";@themushroompirates
text;text_strings_management;★★★☆;5.6-dev;5.6-dev;2025;2025;"David Buzatto";@davidbuzatto
-models;models_animation_playing;★★☆☆;2.5;3.5;2019;2025;"Culacant";@culacant
+models;models_loading_iqm;★★☆☆;2.5;3.5;2019;2025;"Culacant";@culacant
models;models_billboard_rendering;★★★☆;1.3;3.5;2015;2025;"Ramon Santamaria";@raysan5
models;models_box_collisions;★☆☆☆;1.3;3.5;2015;2025;"Ramon Santamaria";@raysan5
models;models_cubicmap_rendering;★★☆☆;1.8;3.5;2015;2025;"Ramon Santamaria";@raysan5
@@ -171,8 +171,9 @@ models;models_basic_voxel;★★☆☆;5.5;5.5;2025;2025;"Tim Little";@timlittle
models;models_rotating_cube;★☆☆☆;5.6-dev;5.6-dev;2025;2025;"Jopestpe";@jopestpe
models;models_decals;★★★★;5.6-dev;5.6-dev;2025;2025;"JP Mortiboys";@themushroompirates
models;models_directional_billboard;★★☆☆;5.6-dev;5.6;2025;2025;"Robin";@RobinsAviary
-models;models_animation_bone_blending;★★★★;5.5;5.5;2026;2026;"dmitrii-brand";@dmitrii-brand
+models;models_animation_blend_custom;★★★★;5.5;5.5;2026;2026;"dmitrii-brand";@dmitrii-brand
models;models_animation_blending;☆☆☆☆;5.5;5.6-dev;2024;2024;"Kirandeep";@Kirandeep-Singh-Khehra
+models;models_animation_timing;★★☆☆;5.6;5.6;2026;2026;"Ramon Santamaria";@raysan5
shaders;shaders_ascii_rendering;★★☆☆;5.5;5.6;2025;2025;"Maicon Santana";@maiconpintoabreu
shaders;shaders_basic_lighting;★★★★;3.0;4.2;2019;2025;"Chris Camacho";@chriscamacho
shaders;shaders_model_shader;★★☆☆;1.3;3.7;2014;2025;"Ramon Santamaria";@raysan5
diff --git a/examples/models/models_animation_blend_custom.c b/examples/models/models_animation_blend_custom.c
new file mode 100644
index 000000000..005982ec6
--- /dev/null
+++ b/examples/models/models_animation_blend_custom.c
@@ -0,0 +1,331 @@
+/*******************************************************************************************
+*
+* raylib [models] example - animation blend custom
+*
+* Example complexity rating: [★★★★] 4/4
+*
+* Example originally created with raylib 5.5, last time updated with raylib 5.5
+*
+* Example contributed by dmitrii-brand (@dmitrii-brand) and reviewed by Ramon Santamaria (@raysan5)
+*
+* DETAILS: Example demonstrates per-bone animation blending, allowing smooth transitions
+* between two animations by interpolating bone transforms. This is useful for:
+* - Blending movement animations (walk/run) with action animations (jump/attack)
+* - Creating smooth animation transitions
+* - Layering animations (e.g., upper body attack while lower body walks)
+*
+* WARNING: GPU skinning must be enabled in raylib with a compilation flag,
+* if not enabled, CPU skinning will be used instead
+*
+* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+* BSD-like license that allows static linking with closed source software
+*
+* Copyright (c) 2026 dmitrii-brand (@dmitrii-brand)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"
+
+#include "rlgl.h" // Requried for: rlUpdateVertexBuffer() (CPU-skinning)
+
+#include // Required for: memcpy()
+#include // Required for: NULL
+
+#if defined(PLATFORM_DESKTOP)
+ #define GLSL_VERSION 330
+#else // PLATFORM_ANDROID, PLATFORM_WEB
+ #define GLSL_VERSION 100
+#endif
+
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
+static bool IsUpperBodyBone(const char *boneName);
+static void UpdateModelAnimationBones(Model *model, ModelAnimation *anim1, int frame1,
+ ModelAnimation *anim2, int frame2, float blend, bool upperBodyBlend);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ InitWindow(screenWidth, screenHeight, "raylib [models] example - animation blend custom");
+
+ // Define the camera to look into our 3d world
+ Camera camera = { 0 };
+ camera.position = (Vector3){ 4.0f, 4.0f, 4.0f }; // Camera position
+ camera.target = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera looking at point
+ camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
+ camera.fovy = 45.0f; // Camera field-of-view Y
+ camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
+
+ // Load gltf model
+ Model model = LoadModel("resources/models/gltf/greenman.glb");
+ Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
+
+ // Load skinning shader
+ // WARNING: GPU skinning must be enabled in raylib with a compilation flag,
+ // if not enabled, CPU skinning will be used instead
+ Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION),
+ TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION));
+ model.materials[1].shader = skinningShader;
+
+ // Load gltf model animations
+ int animCount = 0;
+ ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/greenman.glb", &animCount);
+
+ // Use specific animation indices: 2-walk/move, 3-attack
+ int animIndex0 = 2; // Walk/Move animation (index 2)
+ int animIndex1 = 3; // Attack animation (index 3)
+ int animCurrentFrame0 = 0;
+ int animCurrentFrame1 = 0;
+
+ // Validate indices
+ if (animIndex0 >= animCount) animIndex0 = 0;
+ if (animIndex1 >= animCount) animIndex1 = (animCount > 1) ? 1 : 0;
+
+ bool upperBodyBlend = true; // Toggle: true = upper/lower body blending, false = uniform blending (50/50)
+
+ SetTargetFPS(60); // Set our game to run at 60 frames-per-second
+ //--------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ UpdateCamera(&camera, CAMERA_ORBITAL);
+
+ // Toggle upper/lower body blending mode (SPACE key)
+ if (IsKeyPressed(KEY_SPACE)) upperBodyBlend = !upperBodyBlend;
+
+ // Update animation frames
+ ModelAnimation anim0 = anims[animIndex0];
+ ModelAnimation anim1 = anims[animIndex1];
+
+ animCurrentFrame0 = (animCurrentFrame0 + 1)%anim0.keyframeCount;
+ animCurrentFrame1 = (animCurrentFrame1 + 1)%anim1.keyframeCount;
+
+ // Blend the two animations
+ // When upperBodyBlend is ON: upper body = attack (1.0), lower body = walk (0.0)
+ // When upperBodyBlend is OFF: uniform blend at 0.5 (50% walk, 50% attack)
+ float blendFactor = (upperBodyBlend? 1.0f : 0.5f);
+ UpdateModelAnimationBones(&model, &anim0, animCurrentFrame0,
+ &anim1, animCurrentFrame1, blendFactor, upperBodyBlend);
+
+ // raylib provided animation blending function
+ //UpdateModelAnimationEx(model, anim0, (float)animCurrentFrame0,
+ // anim1, (float)animCurrentFrame1, blendFactor);
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground(RAYWHITE);
+
+ BeginMode3D(camera);
+
+ DrawModel(model, position, 1.0f, WHITE);
+
+ DrawGrid(10, 1.0f);
+
+ EndMode3D();
+
+ // Draw UI
+ DrawText(TextFormat("ANIM 0: %s", anim0.name), 10, 10, 20, GRAY);
+ DrawText(TextFormat("ANIM 1: %s", anim1.name), 10, 40, 20, GRAY);
+ DrawText(TextFormat("[SPACE] Toggle blending mode: %s",
+ upperBodyBlend? "Upper/Lower Body Blending" : "Uniform Blending"),
+ 10, GetScreenHeight() - 30, 20, DARKGRAY);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ UnloadModelAnimations(anims, animCount); // Unload model animation
+ UnloadModel(model); // Unload model and meshes/material
+ UnloadShader(skinningShader); // Unload GPU skinning shader
+
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+// Check if a bone is part of upper body (for selective blending)
+static bool IsUpperBodyBone(const char *boneName)
+{
+ // Common upper body bone names (adjust based on your model)
+ if (TextIsEqual(boneName, "spine") || TextIsEqual(boneName, "spine1") || TextIsEqual(boneName, "spine2") ||
+ TextIsEqual(boneName, "chest") || TextIsEqual(boneName, "upperChest") ||
+ TextIsEqual(boneName, "neck") || TextIsEqual(boneName, "head") ||
+ TextIsEqual(boneName, "shoulder") || TextIsEqual(boneName, "shoulder_L") || TextIsEqual(boneName, "shoulder_R") ||
+ TextIsEqual(boneName, "upperArm") || TextIsEqual(boneName, "upperArm_L") || TextIsEqual(boneName, "upperArm_R") ||
+ TextIsEqual(boneName, "lowerArm") || TextIsEqual(boneName, "lowerArm_L") || TextIsEqual(boneName, "lowerArm_R") ||
+ TextIsEqual(boneName, "hand") || TextIsEqual(boneName, "hand_L") || TextIsEqual(boneName, "hand_R") ||
+ TextIsEqual(boneName, "clavicle") || TextIsEqual(boneName, "clavicle_L") || TextIsEqual(boneName, "clavicle_R"))
+ {
+ return true;
+ }
+
+ // Check if bone name contains upper body keywords
+ if (strstr(boneName, "spine") != NULL || strstr(boneName, "chest") != NULL ||
+ strstr(boneName, "neck") != NULL || strstr(boneName, "head") != NULL ||
+ strstr(boneName, "shoulder") != NULL || strstr(boneName, "arm") != NULL ||
+ strstr(boneName, "hand") != NULL || strstr(boneName, "clavicle") != NULL)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+// Blend two animations per-bone with selective upper/lower body blending
+static void UpdateModelAnimationBones(Model *model, ModelAnimation *anim0, int frame0,
+ ModelAnimation *anim1, int frame1, float blend, bool upperBodyBlend)
+{
+ // Validate inputs
+ if ((anim0->boneCount != 0) && (anim0->keyframePoses != NULL) &&
+ (anim1->boneCount != 0) && (anim1->keyframePoses != NULL) &&
+ (model->skeleton.boneCount != 0) && (model->skeleton.bindPose != NULL))
+ {
+ // Clamp blend factor to [0, 1]
+ blend = fminf(1.0f, fmaxf(0.0f, blend));
+
+ // Ensure frame indices are valid
+ if (frame0 >= anim0->keyframeCount) frame0 = anim0->keyframeCount - 1;
+ if (frame1 >= anim1->keyframeCount) frame1 = anim1->keyframeCount - 1;
+ if (frame0 < 0) frame0 = 0;
+ if (frame1 < 0) frame1 = 0;
+
+ // Get bone count (use minimum of all to be safe)
+ int boneCount = model->skeleton.boneCount;
+ if (anim0->boneCount < boneCount) boneCount = anim0->boneCount;
+ if (anim1->boneCount < boneCount) boneCount = anim1->boneCount;
+
+ // Blend each bone
+ for (int boneIndex = 0; boneIndex < boneCount; boneIndex++)
+ {
+ // Determine blend factor for this bone
+ float boneBlendFactor = blend;
+
+ // If upper body blending is enabled, use different blend factors for upper vs lower body
+ if (upperBodyBlend)
+ {
+ const char *boneName = model->skeleton.bones[boneIndex].name;
+ bool isUpperBody = IsUpperBodyBone(boneName);
+
+ // Upper body: use anim1 (attack), Lower body: use anim0 (walk)
+ // blend = 0.0 means full anim0 (walk), 1.0 means full anim1 (attack)
+ if (isUpperBody) boneBlendFactor = blend; // Upper body: blend towards anim1 (attack)
+ else boneBlendFactor = 1.0f - blend; // Lower body: blend towards anim0 (walk) - invert the blend
+ }
+
+ // Get transforms from both animations
+ Transform *bindTransform = &model->skeleton.bindPose[boneIndex];
+ Transform *animTransform0 = &anim0->keyframePoses[frame0][boneIndex];
+ Transform *animTransform1 = &anim1->keyframePoses[frame1][boneIndex];
+
+ // Blend the transforms
+ Transform blended = { 0 };
+ blended.translation = Vector3Lerp(animTransform0->translation, animTransform1->translation, boneBlendFactor);
+ blended.rotation = QuaternionSlerp(animTransform0->rotation, animTransform1->rotation, boneBlendFactor);
+ blended.scale = Vector3Lerp(animTransform0->scale, animTransform1->scale, boneBlendFactor);
+
+ // Convert bind pose to matrix
+ Matrix bindMatrix = MatrixMultiply(MatrixMultiply(
+ MatrixScale(bindTransform->scale.x, bindTransform->scale.y, bindTransform->scale.z),
+ QuaternionToMatrix(bindTransform->rotation)),
+ MatrixTranslate(bindTransform->translation.x, bindTransform->translation.y, bindTransform->translation.z));
+
+ // Convert blended transform to matrix
+ Matrix blendedMatrix = MatrixMultiply(MatrixMultiply(
+ MatrixScale(blended.scale.x, blended.scale.y, blended.scale.z),
+ QuaternionToMatrix(blended.rotation)),
+ MatrixTranslate(blended.translation.x, blended.translation.y, blended.translation.z));
+
+ // Calculate final bone matrix (similar to UpdateModelAnimationBones)
+ model->boneMatrices[boneIndex] = MatrixMultiply(MatrixInvert(bindMatrix), blendedMatrix);
+ }
+
+ // CPU skinning, updates CPU buffers and uploads them to GPU (if available)
+ // NOTE: Fallback in case GPU skinning is not supported or enabled
+ for (int m = 0; m < model->meshCount; m++)
+ {
+ Mesh mesh = model->meshes[m];
+ Vector3 animVertex = { 0 };
+ Vector3 animNormal = { 0 };
+ const int vertexValuesCount = mesh.vertexCount*3;
+
+ int boneIndex = 0;
+ int boneCounter = 0;
+ float boneWeight = 0.0f;
+ bool bufferUpdateRequired = false; // Flag to check when anim vertex information is updated
+
+ // Skip if missing bone data or missing anim buffers initialization
+ if ((mesh.boneWeights == NULL) || (mesh.boneIndices == NULL) ||
+ (mesh.animVertices == NULL) || (mesh.animNormals == NULL)) continue;
+
+ for (int vCounter = 0; vCounter < vertexValuesCount; vCounter += 3)
+ {
+ mesh.animVertices[vCounter] = 0;
+ mesh.animVertices[vCounter + 1] = 0;
+ mesh.animVertices[vCounter + 2] = 0;
+ if (mesh.animNormals != NULL)
+ {
+ mesh.animNormals[vCounter] = 0;
+ mesh.animNormals[vCounter + 1] = 0;
+ mesh.animNormals[vCounter + 2] = 0;
+ }
+
+ // Iterates over 4 bones per vertex
+ for (int j = 0; j < 4; j++, boneCounter++)
+ {
+ boneWeight = mesh.boneWeights[boneCounter];
+ boneIndex = mesh.boneIndices[boneCounter];
+
+ // Early stop when no transformation will be applied
+ if (boneWeight == 0.0f) continue;
+ animVertex = (Vector3){ mesh.vertices[vCounter], mesh.vertices[vCounter + 1], mesh.vertices[vCounter + 2] };
+ animVertex = Vector3Transform(animVertex, model->boneMatrices[boneIndex]);
+ mesh.animVertices[vCounter] += animVertex.x*boneWeight;
+ mesh.animVertices[vCounter + 1] += animVertex.y*boneWeight;
+ mesh.animVertices[vCounter + 2] += animVertex.z*boneWeight;
+ bufferUpdateRequired = true;
+
+ // Normals processing
+ // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals)
+ if ((mesh.normals != NULL) && (mesh.animNormals != NULL ))
+ {
+ animNormal = (Vector3){ mesh.normals[vCounter], mesh.normals[vCounter + 1], mesh.normals[vCounter + 2] };
+ animNormal = Vector3Transform(animNormal, MatrixTranspose(MatrixInvert(model->boneMatrices[boneIndex])));
+ mesh.animNormals[vCounter] += animNormal.x*boneWeight;
+ mesh.animNormals[vCounter + 1] += animNormal.y*boneWeight;
+ mesh.animNormals[vCounter + 2] += animNormal.z*boneWeight;
+ }
+ }
+ }
+
+ if (bufferUpdateRequired)
+ {
+ // Update GPU vertex buffers with updated data (position + normals)
+ rlUpdateVertexBuffer(mesh.vboId[SHADER_LOC_VERTEX_POSITION], mesh.animVertices, mesh.vertexCount*3*sizeof(float), 0);
+ if (mesh.normals != NULL) rlUpdateVertexBuffer(mesh.vboId[SHADER_LOC_VERTEX_NORMAL], mesh.animNormals, mesh.vertexCount*3*sizeof(float), 0);
+ }
+ }
+ }
+}
+
diff --git a/examples/models/models_animation_blend_custom.png b/examples/models/models_animation_blend_custom.png
new file mode 100644
index 000000000..95ea1dad2
Binary files /dev/null and b/examples/models/models_animation_blend_custom.png differ
diff --git a/examples/models/models_animation_blending.c b/examples/models/models_animation_blending.c
index b33dbe85d..e44583299 100644
--- a/examples/models/models_animation_blending.c
+++ b/examples/models/models_animation_blending.c
@@ -1,25 +1,27 @@
/*******************************************************************************************
*
* raylib [models] example - animation blending
+*
+* Example complexity rating: [★★★★] 4/4
*
* Example originally created with raylib 5.5, last time updated with raylib 5.6-dev
*
-* Example contributed by Kirandeep (@Kirandeep-Singh-Khehra)
+* Example contributed by Kirandeep (@Kirandeep-Singh-Khehra) and reviewed by Ramon Santamaria (@raysan5)
+*
+* WARNING: GPU skinning must be enabled in raylib with a compilation flag,
+* if not enabled, CPU skinning will be used instead
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
-* Copyright (c) 2024 Kirandeep (@Kirandeep-Singh-Khehra)
-*
-* Note: Due to limitations in the Apple OpenGL driver, this feature does not work on MacOS
-* Note: This example uses CPU for updating meshes.
-* For GPU skinning see comments with 'INFO:'.
+* Copyright (c) 2024-2026 Kirandeep (@Kirandeep-Singh-Khehra) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
#include "raylib.h"
-#define clamp(x,a,b) ((x < a)? a : (x > b)? b : x)
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h" // Required for: UI controls
#if defined(PLATFORM_DESKTOP)
#define GLSL_VERSION 330
@@ -41,36 +43,58 @@ int main(void)
// Define the camera to look into our 3d world
Camera camera = { 0 };
- camera.position = (Vector3){ 8.0f, 8.0f, 8.0f }; // Camera position
+ camera.position = (Vector3){ 6.0f, 6.0f, 6.0f }; // Camera position
camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; // Camera looking at point
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
camera.fovy = 45.0f; // Camera field-of-view Y
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
- // Load gltf model
- Model characterModel = LoadModel("resources/models/gltf/robot.glb"); // Load character model
-
-/* INFO: Uncomment this to use GPU skinning
+ // Load model
+ Model model = LoadModel("resources/models/gltf/robot.glb"); // Load character model
+ Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model world position
+
// Load skinning shader
+ // WARNING: It requires SUPPORT_GPU_SKINNING enabled on raylib (disabled by default)
Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION),
TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION));
+
+ // Assign skinning shader to all materials shaders
+ //for (int i = 0; i < model.materialCount; i++) model.materials[i].shader = skinningShader;
- for (int i = 0; i < characterModel.materialCount; i++)
- {
- characterModel.materials[i].shader = skinningShader;
- }
-*/
+ // Load model animations
+ int animCount = 0;
+ ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/robot.glb", &animCount);
- // Load gltf model animations
- int animsCount = 0;
- unsigned int animIndex0 = 0;
- unsigned int animIndex1 = 0;
- unsigned int animCurrentFrame = 0;
- ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/robot.glb", &animsCount);
+ // Animation playing variables
+ // NOTE: Two animations are played with a smooth transition between them
+ int currentAnimPlaying = 0; // Current animation playing (0 o 1)
+ int nextAnimToPlay = 1; // Next animation to play (to transition)
+ bool animTransition = false; // Flag to register anim transition state
- float blendFactor = 0.5f;
+ int animIndex0 = 10; // Current animation playing (walking)
+ float animCurrentFrame0 = 0.0f; // Current animation frame (supporting interpolated frames)
+ float animFrameSpeed0 = 0.5f; // Current animation play speed
+ int animIndex1 = 6; // Next animation to play (running)
+ float animCurrentFrame1 = 0.0f; // Next animation frame (supporting interpolated frames)
+ float animFrameSpeed1 = 0.5f; // Next animation play speed
- DisableCursor(); // Limit cursor to relative movement inside the window
+ float animBlendFactor = 0.0f; // Blend factor from anim0[frame0] --> anim1[frame1], [0.0f..1.0f]
+ // NOTE: 0.0f results in full anim0[] and 1.0f in full anim1[]
+
+ float animBlendTime = 2.0f; // Time to blend from one playing animation to another (in seconds)
+ float animBlendTimeCounter = 0.0f; // Time counter (delta time)
+
+ bool animPause = false; // Pause animation
+
+ // UI required variables
+ char *animNames[64] = { 0 }; // Pointers to animation names for dropdown box
+ for (int i = 0; i < animCount; i++) animNames[i] = anims[i].name;
+
+ bool dropdownEditMode0 = false;
+ bool dropdownEditMode1 = false;
+ float animFrameProgress0 = 0.0f;
+ float animFrameProgress1 = 0.0f;
+ float animBlendProgress = 0.0f;
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
@@ -80,27 +104,102 @@ int main(void)
{
// Update
//----------------------------------------------------------------------------------
- UpdateCamera(&camera, CAMERA_THIRD_PERSON);
+ UpdateCamera(&camera, CAMERA_ORBITAL);
- // Select current animation
- if (IsKeyPressed(KEY_T)) animIndex0 = (animIndex0 + 1)%animsCount;
- else if (IsKeyPressed(KEY_G)) animIndex0 = (animIndex0 + animsCount - 1)%animsCount;
- if (IsKeyPressed(KEY_Y)) animIndex1 = (animIndex1 + 1)%animsCount;
- else if (IsKeyPressed(KEY_H)) animIndex1 = (animIndex1 + animsCount - 1)%animsCount;
-
- // Select blend factor
- if (IsKeyPressed(KEY_U)) blendFactor = clamp(blendFactor - 0.1, 0.0f, 1.0f);
- else if (IsKeyPressed(KEY_J)) blendFactor = clamp(blendFactor + 0.1, 0.0f, 1.0f);
+ if (IsKeyPressed(KEY_P)) animPause = !animPause;
- // Update animation
- animCurrentFrame++;
+ if (!animPause)
+ {
+ // Start transition from anim0[] to anim1[]
+ if (IsKeyPressed(KEY_SPACE) && !animTransition)
+ {
+ if (currentAnimPlaying == 0)
+ {
+ // Transition anim0 --> anim1
+ nextAnimToPlay = 1;
+ animCurrentFrame1 = 0.0f;
+ }
+ else
+ {
+ // Transition anim1 --> anim0
+ nextAnimToPlay = 0;
+ animCurrentFrame0 = 0.0f;
+ }
- // Update bones
- // Note: Same animation frame index is used below. By default it loops both animations
- UpdateModelAnimationBonesLerp(characterModel, modelAnimations[animIndex0], animCurrentFrame, modelAnimations[animIndex1], animCurrentFrame, blendFactor);
+ // Set animation transition
+ animTransition = true;
+ animBlendTimeCounter = 0.0f;
+ animBlendFactor = 0.0f;
+ }
-// INFO: Comment the following line to use GPU skinning
- UpdateModelVertsToCurrentBones(characterModel);
+ if (animTransition)
+ {
+ // Playing anim0 and anim1 at the same time
+ animCurrentFrame0 += animFrameSpeed0;
+ if (animCurrentFrame0 >= anims[animIndex0].keyframeCount) animCurrentFrame0 = 0.0f;
+ animCurrentFrame1 += animFrameSpeed1;
+ if (animCurrentFrame1 >= anims[animIndex1].keyframeCount) animCurrentFrame1 = 0.0f;
+
+ // Increment blend factor over time to transition from anim0 --> anim1 over time
+ // NOTE: Time blending could be other than linear, using some easing
+ animBlendFactor = animBlendTimeCounter/animBlendTime;
+ animBlendTimeCounter += GetFrameTime();
+ animBlendProgress = animBlendFactor;
+
+ // Update model with animations blending
+ if (nextAnimToPlay == 1)
+ {
+ // Blend anim0 --> anim1
+ UpdateModelAnimationEx(model, anims[animIndex0], animCurrentFrame0,
+ anims[animIndex1], animCurrentFrame1, animBlendFactor);
+ }
+ else
+ {
+ // Blend anim1 --> anim0
+ UpdateModelAnimationEx(model, anims[animIndex1], animCurrentFrame1,
+ anims[animIndex0], animCurrentFrame0, animBlendFactor);
+ }
+
+ // Check if transition completed
+ if (animBlendFactor > 1.0f)
+ {
+ // Reset frame states
+ if (currentAnimPlaying == 0) animCurrentFrame0 = 0.0f;
+ else if (currentAnimPlaying == 1) animCurrentFrame1 = 0.0f;
+ currentAnimPlaying = nextAnimToPlay; // Update current animation playing
+
+ animBlendFactor = 0.0f; // Reset blend factor
+ animTransition = false; // Exit transition mode
+ animBlendTimeCounter = 0.0f;
+ }
+ }
+ else
+ {
+ // Play only one anim, the current one
+ if (currentAnimPlaying == 0)
+ {
+ // Playing anim0 at defined speed
+ animCurrentFrame0 += animFrameSpeed0;
+ if (animCurrentFrame0 >= anims[animIndex0].keyframeCount) animCurrentFrame0 = 0.0f;
+ UpdateModelAnimation(model, anims[animIndex0], animCurrentFrame0);
+ //UpdateModelAnimationEx(model, anims[animIndex0], animCurrentFrame0,
+ // anims[animIndex1], animCurrentFrame1, 0.0f); // Same as above, first animation frame blend
+ }
+ else if (currentAnimPlaying == 1)
+ {
+ // Playing anim1 at defined speed
+ animCurrentFrame1 += animFrameSpeed1;
+ if (animCurrentFrame1 >= anims[animIndex1].keyframeCount) animCurrentFrame1 = 0.0f;
+ UpdateModelAnimation(model, anims[animIndex1], animCurrentFrame1);
+ //UpdateModelAnimationEx(model, anims[animIndex0], animCurrentFrame0,
+ // anims[animIndex1], animCurrentFrame1, 1.0f); // Same as above, second animation frame blend
+ }
+ }
+ }
+
+ // Update progress bars values with current frame for each animation
+ animFrameProgress0 = animCurrentFrame0;
+ animFrameProgress1 = animCurrentFrame1;
//----------------------------------------------------------------------------------
// Draw
@@ -111,27 +210,56 @@ int main(void)
BeginMode3D(camera);
-/* INFO: Uncomment this to use GPU skinning
- // Draw character mesh, pose calculation is done in shader (GPU skinning)
- for (int i = 0; i < characterModel.meshCount; i++)
- {
- DrawMesh(characterModel.meshes[i], characterModel.materials[characterModel.meshMaterial[i]], characterModel.transform);
- }
-*/
-
-// INFO: Comment the following line to use GPU skinning
- DrawModel(characterModel, (Vector3){0.0f, 0.0f, 0.0f}, 1.0f, WHITE);
-
+ DrawModel(model, position, 1.0f, WHITE); // Draw animated model
DrawGrid(10, 1.0f);
EndMode3D();
- DrawText("Use the U/J to adjust blend factor", 10, 10, 20, GRAY);
- DrawText("Use the T/G to switch first animation", 10, 30, 20, GRAY);
- DrawText("Use the Y/H to switch second animation", 10, 50, 20, GRAY);
- DrawText(TextFormat("Animations: %s, %s", modelAnimations[animIndex0].name, modelAnimations[animIndex1].name), 10, 70, 20, BLACK);
- DrawText(TextFormat("Blend Factor: %f", blendFactor), 10, 86, 20, BLACK);
+ if (animTransition) DrawText("ANIM TRANSITION BLENDING!", 170, 50, 30, BLUE);
+
+ // Draw UI elements
+ //---------------------------------------------------------------------------------------------
+ if (dropdownEditMode0) GuiDisable();
+ GuiSlider((Rectangle){ 10, 38, 160, 12 },
+ NULL, TextFormat("x%.1f", animFrameSpeed0), &animFrameSpeed0, 0.1f, 2.0f);
+ GuiEnable();
+ if (dropdownEditMode1) GuiDisable();
+ GuiSlider((Rectangle){ GetScreenWidth() - 170.0f, 38, 160, 12 },
+ TextFormat("%.1fx", animFrameSpeed1), NULL, &animFrameSpeed1, 0.1f, 2.0f);
+ GuiEnable();
+
+ // Draw animation selectors for blending transition
+ // NOTE: Transition does not start until requested
+ GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 1);
+ if (GuiDropdownBox((Rectangle){ 10, 10, 160, 24 }, TextJoin(animNames, animCount, ";"),
+ &animIndex0, dropdownEditMode0)) dropdownEditMode0 = !dropdownEditMode0;
+
+ // Blending process progress bar
+ if (nextAnimToPlay == 1) GuiSetStyle(PROGRESSBAR, PROGRESS_SIDE, 0); // Left-->Right
+ else GuiSetStyle(PROGRESSBAR, PROGRESS_SIDE, 1); // Right-->Left
+ GuiProgressBar((Rectangle){ 180, 14, 440, 16 }, NULL, NULL, &animBlendProgress, 0.0f, 1.0f);
+ GuiSetStyle(PROGRESSBAR, PROGRESS_SIDE, 0); // Reset to Left-->Right
+
+ if (GuiDropdownBox((Rectangle){ GetScreenWidth() - 170.0f, 10, 160, 24 }, TextJoin(animNames, animCount, ";"),
+ &animIndex1, dropdownEditMode1)) dropdownEditMode1 = !dropdownEditMode1;
+
+ // Draw playing timeline with keyframes for anim0[]
+ GuiProgressBar((Rectangle){ 60, GetScreenHeight() - 60.0f, GetScreenWidth() - 180.0f, 20 }, "ANIM 0",
+ TextFormat("FRAME: %.2f / %i", animFrameProgress0, anims[animIndex0].keyframeCount),
+ &animFrameProgress0, 0.0f, (float)anims[animIndex0].keyframeCount);
+ for (int i = 0; i < anims[animIndex0].keyframeCount; i++)
+ DrawRectangle(60 + (int)(((float)(GetScreenWidth() - 180)/(float)anims[animIndex0].keyframeCount)*(float)i),
+ GetScreenHeight() - 60, 1, 20, BLUE);
+
+ // Draw playing timeline with keyframes for anim1[]
+ GuiProgressBar((Rectangle){ 60, GetScreenHeight() - 30.0f, GetScreenWidth() - 180.0f, 20 }, "ANIM 1",
+ TextFormat("FRAME: %.2f / %i", animFrameProgress1, anims[animIndex1].keyframeCount),
+ &animFrameProgress1, 0.0f, (float)anims[animIndex1].keyframeCount);
+ for (int i = 0; i < anims[animIndex1].keyframeCount; i++)
+ DrawRectangle(60 + (int)(((float)(GetScreenWidth() - 180)/(float)anims[animIndex1].keyframeCount)*(float)i),
+ GetScreenHeight() - 30, 1, 20, BLUE);
+ //---------------------------------------------------------------------------------------------
EndDrawing();
//----------------------------------------------------------------------------------
@@ -139,11 +267,9 @@ int main(void)
// De-Initialization
//--------------------------------------------------------------------------------------
- UnloadModelAnimations(modelAnimations, animsCount); // Unload model animation
- UnloadModel(characterModel); // Unload model and meshes/material
-
-// INFO: Uncomment the following line to use GPU skinning
- // UnloadShader(skinningShader); // Unload GPU skinning shader
+ UnloadModelAnimations(anims, animCount); // Unload model animation
+ UnloadModel(model); // Unload model and meshes/material
+ UnloadShader(skinningShader); // Unload GPU skinning shader
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
diff --git a/examples/models/models_animation_blending.png b/examples/models/models_animation_blending.png
index 0d70c1a88..d1ca4a51b 100644
Binary files a/examples/models/models_animation_blending.png and b/examples/models/models_animation_blending.png differ
diff --git a/examples/models/models_animation_bone_blending.c b/examples/models/models_animation_bone_blending.c
deleted file mode 100644
index 11e05a305..000000000
--- a/examples/models/models_animation_bone_blending.c
+++ /dev/null
@@ -1,296 +0,0 @@
-/*******************************************************************************************
-*
-* raylib [models] example - animation bone blending
-*
-* Example complexity rating: [★★★★] 4/4
-*
-* Example originally created with raylib 5.5, last time updated with raylib 5.5
-*
-* This example demonstrates per-bone animation blending, allowing smooth transitions
-* between two animations by interpolating bone transforms. This is useful for:
-* - Blending movement animations (walk/run) with action animations (jump/attack)
-* - Creating smooth animation transitions
-* - Layering animations (e.g., upper body attack while lower body walks)
-*
-* Example contributed by dmitrii-brand (@dmitrii-brand) and reviewed by Ramon Santamaria (@raysan5)
-*
-* NOTE: Due to limitations in the Apple OpenGL driver, this feature does not work on MacOS
-*
-* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-* BSD-like license that allows static linking with closed source software
-*
-* Copyright (c) 2026 dmitrii-brand (@dmitrii-brand)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-#include "raymath.h"
-
-#include // Required for: memcpy()
-#include // Required for: NULL
-
-#if defined(PLATFORM_DESKTOP)
- #define GLSL_VERSION 330
-#else // PLATFORM_ANDROID, PLATFORM_WEB
- #define GLSL_VERSION 100
-#endif
-
-//------------------------------------------------------------------------------------
-// Module Functions Declaration
-//------------------------------------------------------------------------------------
-static bool IsUpperBodyBone(const char *boneName);
-static void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int frame1,
- ModelAnimation *anim2, int frame2, float blendFactor, bool upperBodyBlend);
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
- // Initialization
- //--------------------------------------------------------------------------------------
- const int screenWidth = 800;
- const int screenHeight = 450;
-
- InitWindow(screenWidth, screenHeight, "raylib [models] example - animation bone blending");
-
- // Define the camera to look into our 3d world
- Camera camera = { 0 };
- camera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; // Camera position
- camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; // Camera looking at point
- camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
- camera.fovy = 45.0f; // Camera field-of-view Y
- camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
-
- // Load gltf model
- Model characterModel = LoadModel("resources/models/gltf/greenman.glb");
-
- // Load skinning shader
- Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION),
- TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION));
-
- characterModel.materials[1].shader = skinningShader;
-
- // Load gltf model animations
- int animsCount = 0;
- ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/greenman.glb", &animsCount);
-
- // Log all available animations for debugging
- TraceLog(LOG_INFO, "Found %d animations:", animsCount);
- for (int i = 0; i < animsCount; i++)
- {
- TraceLog(LOG_INFO, " Animation %d: %s (%d frames)", i, modelAnimations[i].name, modelAnimations[i].frameCount);
- }
-
- // Use specific indices: walk/move = 2, attack = 3
- unsigned int animIndex1 = 2; // Walk/Move animation (index 2)
- unsigned int animIndex2 = 3; // Attack animation (index 3)
- unsigned int animCurrentFrame1 = 0;
- unsigned int animCurrentFrame2 = 0;
-
- // Validate indices
- if (animIndex1 >= animsCount) animIndex1 = 0;
- if (animIndex2 >= animsCount) animIndex2 = (animsCount > 1) ? 1 : 0;
-
- TraceLog(LOG_INFO, "Using Walk (index %d): %s", animIndex1, modelAnimations[animIndex1].name);
- TraceLog(LOG_INFO, "Using Attack (index %d): %s", animIndex2, modelAnimations[animIndex2].name);
-
- Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
- bool upperBodyBlend = true; // Toggle: true = upper/lower body blending, false = uniform blending (50/50)
-
- DisableCursor(); // Limit cursor to relative movement inside the window
-
- SetTargetFPS(60); // Set our game to run at 60 frames-per-second
- //--------------------------------------------------------------------------------------
-
- // Main game loop
- while (!WindowShouldClose()) // Detect window close button or ESC key
- {
- // Update
- //----------------------------------------------------------------------------------
- UpdateCamera(&camera, CAMERA_THIRD_PERSON);
-
- // Toggle upper/lower body blending mode (SPACE key)
- if (IsKeyPressed(KEY_SPACE)) upperBodyBlend = !upperBodyBlend;
-
- // Update animation frames
- ModelAnimation anim1 = modelAnimations[animIndex1];
- ModelAnimation anim2 = modelAnimations[animIndex2];
-
- animCurrentFrame1 = (animCurrentFrame1 + 1)%anim1.frameCount;
- animCurrentFrame2 = (animCurrentFrame2 + 1)%anim2.frameCount;
-
- // Blend the two animations
- characterModel.transform = MatrixTranslate(position.x, position.y, position.z);
- // When upperBodyBlend is ON: upper body = attack (1.0), lower body = walk (0.0)
- // When upperBodyBlend is OFF: uniform blend at 0.5 (50% walk, 50% attack)
- float blendFactor = upperBodyBlend ? 1.0f : 0.5f;
- BlendModelAnimationsBones(&characterModel, &anim1, animCurrentFrame1, &anim2, animCurrentFrame2, blendFactor, upperBodyBlend);
- //----------------------------------------------------------------------------------
-
- // Draw
- //----------------------------------------------------------------------------------
- BeginDrawing();
-
- ClearBackground(RAYWHITE);
-
- BeginMode3D(camera);
-
- // Draw character mesh, pose calculation is done in shader (GPU skinning)
- DrawMesh(characterModel.meshes[0], characterModel.materials[1], characterModel.transform);
-
- DrawGrid(10, 1.0f);
-
- EndMode3D();
-
- // Draw UI
- DrawText("BONE BLENDING EXAMPLE", 10, 10, 20, DARKGRAY);
- DrawText(TextFormat("Walk (Animation 2): %s", anim1.name), 10, 35, 10, GRAY);
- DrawText(TextFormat("Attack (Animation 3): %s", anim2.name), 10, 50, 10, GRAY);
- DrawText(TextFormat("Mode: %s", upperBodyBlend ? "Upper/Lower Body Blending" : "Uniform Blending"), 10, 65, 10, GRAY);
- DrawText("SPACE - Toggle blending mode", 10, GetScreenHeight() - 20, 10, DARKGRAY);
-
- EndDrawing();
- //----------------------------------------------------------------------------------
- }
-
- // De-Initialization
- //--------------------------------------------------------------------------------------
- UnloadModelAnimations(modelAnimations, animsCount); // Unload model animation
- UnloadModel(characterModel); // Unload model and meshes/material
- UnloadShader(skinningShader); // Unload GPU skinning shader
-
- CloseWindow(); // Close window and OpenGL context
- //--------------------------------------------------------------------------------------
-
- return 0;
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition
-//----------------------------------------------------------------------------------
-// Check if a bone is part of upper body (for selective blending)
-static bool IsUpperBodyBone(const char *boneName)
-{
- // Common upper body bone names (adjust based on your model)
- if (TextIsEqual(boneName, "spine") || TextIsEqual(boneName, "spine1") || TextIsEqual(boneName, "spine2") ||
- TextIsEqual(boneName, "chest") || TextIsEqual(boneName, "upperChest") ||
- TextIsEqual(boneName, "neck") || TextIsEqual(boneName, "head") ||
- TextIsEqual(boneName, "shoulder") || TextIsEqual(boneName, "shoulder_L") || TextIsEqual(boneName, "shoulder_R") ||
- TextIsEqual(boneName, "upperArm") || TextIsEqual(boneName, "upperArm_L") || TextIsEqual(boneName, "upperArm_R") ||
- TextIsEqual(boneName, "lowerArm") || TextIsEqual(boneName, "lowerArm_L") || TextIsEqual(boneName, "lowerArm_R") ||
- TextIsEqual(boneName, "hand") || TextIsEqual(boneName, "hand_L") || TextIsEqual(boneName, "hand_R") ||
- TextIsEqual(boneName, "clavicle") || TextIsEqual(boneName, "clavicle_L") || TextIsEqual(boneName, "clavicle_R"))
- {
- return true;
- }
-
- // Check if bone name contains upper body keywords
- if (strstr(boneName, "spine") != NULL || strstr(boneName, "chest") != NULL ||
- strstr(boneName, "neck") != NULL || strstr(boneName, "head") != NULL ||
- strstr(boneName, "shoulder") != NULL || strstr(boneName, "arm") != NULL ||
- strstr(boneName, "hand") != NULL || strstr(boneName, "clavicle") != NULL)
- {
- return true;
- }
-
- return false;
-}
-
-// Blend two animations per-bone with selective upper/lower body blending
-static void BlendModelAnimationsBones(Model *model, ModelAnimation *anim1, int frame1,
- ModelAnimation *anim2, int frame2, float blendFactor, bool upperBodyBlend)
-{
- // Validate inputs
- if (anim1->boneCount == 0 || anim1->framePoses == NULL ||
- anim2->boneCount == 0 || anim2->framePoses == NULL ||
- model->boneCount == 0 || model->bindPose == NULL)
- {
- return;
- }
-
- // Clamp blend factor to [0, 1]
- blendFactor = fminf(1.0f, fmaxf(0.0f, blendFactor));
-
- // Ensure frame indices are valid
- if (frame1 >= anim1->frameCount) frame1 = anim1->frameCount - 1;
- if (frame2 >= anim2->frameCount) frame2 = anim2->frameCount - 1;
- if (frame1 < 0) frame1 = 0;
- if (frame2 < 0) frame2 = 0;
-
- // Find first mesh with bones
- int firstMeshWithBones = -1;
- for (int i = 0; i < model->meshCount; i++)
- {
- if (model->meshes[i].boneMatrices)
- {
- firstMeshWithBones = i;
- break;
- }
- }
-
- if (firstMeshWithBones == -1) return;
-
- // Get bone count (use minimum of all to be safe)
- int boneCount = model->boneCount;
- if (anim1->boneCount < boneCount) boneCount = anim1->boneCount;
- if (anim2->boneCount < boneCount) boneCount = anim2->boneCount;
-
- // Blend each bone
- for (int boneId = 0; boneId < boneCount; boneId++)
- {
- // Determine blend factor for this bone
- float boneBlendFactor = blendFactor;
-
- // If upper body blending is enabled, use different blend factors for upper vs lower body
- if (upperBodyBlend)
- {
- const char *boneName = model->bones[boneId].name;
- bool isUpperBody = IsUpperBodyBone(boneName);
-
- // Upper body: use anim2 (attack), Lower body: use anim1 (walk)
- // blendFactor = 0.0 means full anim1 (walk), 1.0 means full anim2 (attack)
- if (isUpperBody) boneBlendFactor = blendFactor; // Upper body: blend towards anim2 (attack)
- else boneBlendFactor = 1.0f - blendFactor; // Lower body: blend towards anim1 (walk) - invert the blend
- }
-
- // Get transforms from both animations
- Transform *bindTransform = &model->bindPose[boneId];
- Transform *anim1Transform = &anim1->framePoses[frame1][boneId];
- Transform *anim2Transform = &anim2->framePoses[frame2][boneId];
-
- // Blend the transforms
- Transform blended;
- blended.translation = Vector3Lerp(anim1Transform->translation, anim2Transform->translation, boneBlendFactor);
- blended.rotation = QuaternionSlerp(anim1Transform->rotation, anim2Transform->rotation, boneBlendFactor);
- blended.scale = Vector3Lerp(anim1Transform->scale, anim2Transform->scale, boneBlendFactor);
-
- // Convert bind pose to matrix
- Matrix bindMatrix = MatrixMultiply(MatrixMultiply(
- MatrixScale(bindTransform->scale.x, bindTransform->scale.y, bindTransform->scale.z),
- QuaternionToMatrix(bindTransform->rotation)),
- MatrixTranslate(bindTransform->translation.x, bindTransform->translation.y, bindTransform->translation.z));
-
- // Convert blended transform to matrix
- Matrix blendedMatrix = MatrixMultiply(MatrixMultiply(
- MatrixScale(blended.scale.x, blended.scale.y, blended.scale.z),
- QuaternionToMatrix(blended.rotation)),
- MatrixTranslate(blended.translation.x, blended.translation.y, blended.translation.z));
-
- // Calculate final bone matrix (similar to UpdateModelAnimationBones)
- model->meshes[firstMeshWithBones].boneMatrices[boneId] = MatrixMultiply(MatrixInvert(bindMatrix), blendedMatrix);
- }
-
- // Copy bone matrices to remaining meshes
- for (int i = firstMeshWithBones + 1; i < model->meshCount; i++)
- {
- if (model->meshes[i].boneMatrices)
- {
- memcpy(model->meshes[i].boneMatrices,
- model->meshes[firstMeshWithBones].boneMatrices,
- model->meshes[i].boneCount*sizeof(model->meshes[i].boneMatrices[0]));
- }
- }
-}
-
diff --git a/examples/models/models_animation_gpu_skinning.c b/examples/models/models_animation_gpu_skinning.c
index 337df931c..b50dd86a3 100644
--- a/examples/models/models_animation_gpu_skinning.c
+++ b/examples/models/models_animation_gpu_skinning.c
@@ -8,13 +8,14 @@
*
* Example contributed by Daniel Holden (@orangeduck) and reviewed by Ramon Santamaria (@raysan5)
*
+* WARNING: GPU skinning must be enabled in raylib with a compilation flag,
+* if not enabled, CPU skinning will be used instead
+*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2024-2025 Daniel Holden (@orangeduck)
*
-* Note: Due to limitations in the Apple OpenGL driver, this feature does not work on MacOS
-*
********************************************************************************************/
#include "raylib.h"
@@ -42,29 +43,29 @@ int main(void)
// Define the camera to look into our 3d world
Camera camera = { 0 };
camera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; // Camera position
- camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; // Camera looking at point
+ camera.target = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera looking at point
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
camera.fovy = 45.0f; // Camera field-of-view Y
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
// Load gltf model
- Model characterModel = LoadModel("resources/models/gltf/greenman.glb"); // Load character model
-
- // Load skinning shader
- Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION),
- TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION));
-
- characterModel.materials[1].shader = skinningShader;
-
- // Load gltf model animations
- int animsCount = 0;
- unsigned int animIndex = 0;
- unsigned int animCurrentFrame = 0;
- ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/greenman.glb", &animsCount);
-
+ Model model = LoadModel("resources/models/gltf/greenman.glb"); // Load character model
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
- DisableCursor(); // Limit cursor to relative movement inside the window
+ // Load skinning shader
+ // WARNING: GPU skinning must be enabled in raylib with a compilation flag,
+ // if not enabled, CPU skinning will be used instead
+ Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION),
+ TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION));
+ model.materials[1].shader = skinningShader;
+
+ // Load gltf model animations
+ int animCount = 0;
+ ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/greenman.glb", &animCount);
+
+ // Animation playing variables
+ unsigned int animIndex = 0; // Current animation playing
+ unsigned int animCurrentFrame = 0; // Current animation frame
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
@@ -74,17 +75,15 @@ int main(void)
{
// Update
//----------------------------------------------------------------------------------
- UpdateCamera(&camera, CAMERA_THIRD_PERSON);
+ UpdateCamera(&camera, CAMERA_ORBITAL);
// Select current animation
- if (IsKeyPressed(KEY_T)) animIndex = (animIndex + 1)%animsCount;
- else if (IsKeyPressed(KEY_G)) animIndex = (animIndex + animsCount - 1)%animsCount;
+ if (IsKeyPressed(KEY_RIGHT)) animIndex = (animIndex + 1)%animCount;
+ else if (IsKeyPressed(KEY_LEFT)) animIndex = (animIndex + animCount - 1)%animCount;
// Update model animation
- ModelAnimation anim = modelAnimations[animIndex];
- animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount;
- characterModel.transform = MatrixTranslate(position.x, position.y, position.z);
- UpdateModelAnimationBones(characterModel, anim, animCurrentFrame);
+ animCurrentFrame = (animCurrentFrame + 1)%anims[animIndex].keyframeCount;
+ UpdateModelAnimation(model, anims[animIndex], (float)animCurrentFrame);
//----------------------------------------------------------------------------------
// Draw
@@ -95,14 +94,14 @@ int main(void)
BeginMode3D(camera);
- // Draw character mesh, pose calculation is done in shader (GPU skinning)
- DrawMesh(characterModel.meshes[0], characterModel.materials[1], characterModel.transform);
+ DrawModel(model, position, 1.0f, WHITE);
DrawGrid(10, 1.0f);
EndMode3D();
- DrawText("Use the T/G to switch animation", 10, 10, 20, GRAY);
+ DrawText(TextFormat("Current animation: %s", anims[animIndex].name), 10, 40, 20, MAROON);
+ DrawText("Use the LEFT/RIGHT keys to switch animation", 10, 10, 20, GRAY);
EndDrawing();
//----------------------------------------------------------------------------------
@@ -110,8 +109,8 @@ int main(void)
// De-Initialization
//--------------------------------------------------------------------------------------
- UnloadModelAnimations(modelAnimations, animsCount); // Unload model animation
- UnloadModel(characterModel); // Unload model and meshes/material
+ UnloadModelAnimations(anims, animCount); // Unload model animation
+ UnloadModel(model); // Unload model and meshes/material
UnloadShader(skinningShader); // Unload GPU skinning shader
CloseWindow(); // Close window and OpenGL context
diff --git a/examples/models/models_animation_gpu_skinning.png b/examples/models/models_animation_gpu_skinning.png
index 8003c16ef..a8d102408 100644
Binary files a/examples/models/models_animation_gpu_skinning.png and b/examples/models/models_animation_gpu_skinning.png differ
diff --git a/examples/models/models_animation_playing.png b/examples/models/models_animation_playing.png
deleted file mode 100644
index 57e39dd3e..000000000
Binary files a/examples/models/models_animation_playing.png and /dev/null differ
diff --git a/examples/models/models_animation_timing.c b/examples/models/models_animation_timing.c
new file mode 100644
index 000000000..030ad81e1
--- /dev/null
+++ b/examples/models/models_animation_timing.c
@@ -0,0 +1,133 @@
+/*******************************************************************************************
+*
+* raylib [models] example - animation timing
+*
+* Example complexity rating: [★★★☆] 3/4
+*
+* Example originally created with raylib 5.6, last time updated with raylib 5.6
+*
+* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+* BSD-like license that allows static linking with closed source software
+*
+* Copyright (c) 2026 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h" // Required for: UI controls
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ InitWindow(screenWidth, screenHeight, "raylib [models] example - animation timing");
+
+ // Define the camera to look into our 3d world
+ Camera camera = { 0 };
+ camera.position = (Vector3){ 6.0f, 6.0f, 6.0f }; // Camera position
+ camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; // Camera looking at point
+ camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
+ camera.fovy = 45.0f; // Camera field-of-view Y
+ camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
+
+ // Load model
+ Model model = LoadModel("resources/models/gltf/robot.glb");
+ Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model world position
+
+ // Load model animations
+ int animCount = 0;
+ ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/robot.glb", &animCount);
+
+ // Animation playing variables
+ int animIndex = 10; // Current animation playing
+ float animCurrentFrame = 0.0f; // Current animation frame (supporting interpolated frames)
+ float animFrameSpeed = 0.5f; // Animation play speed
+ bool animPause = false; // Pause animation
+
+ // UI required variables
+ char *animNames[64] = { 0 };
+ for (int i = 0; i < animCount; i++) animNames[i] = anims[i].name;
+
+ bool dropdownEditMode = false;
+ float animFrameProgress = 0.0f;
+
+ SetTargetFPS(60); // Set our game to run at 60 frames-per-second
+ //--------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ UpdateCamera(&camera, CAMERA_ORBITAL);
+
+ if (IsKeyPressed(KEY_P)) animPause = !animPause;
+
+ if (!animPause && (animIndex < animCount))
+ {
+ // Update model animation
+ animCurrentFrame += animFrameSpeed;
+ if (animCurrentFrame >= anims[animIndex].keyframeCount) animCurrentFrame = 0.0f;
+ UpdateModelAnimation(model, anims[animIndex], animCurrentFrame);
+ }
+
+ // NOTE: Animation and playing speed selected through UI
+
+ // Update progressbar value with current frame
+ animFrameProgress = animCurrentFrame;
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground(RAYWHITE);
+
+ BeginMode3D(camera);
+
+ DrawModel(model, position, 1.0f, WHITE);
+
+ DrawGrid(10, 1.0f);
+
+ EndMode3D();
+
+ // Draw UI, select anim and playing speed
+ GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 1);
+ if (GuiDropdownBox((Rectangle){ 10, 10, 140, 24 }, TextJoin(animNames, animCount, ";"),
+ &animIndex, dropdownEditMode)) dropdownEditMode = !dropdownEditMode;
+
+ GuiSlider((Rectangle){ 260, 10, 500, 24 }, "FRAME SPEED: ", TextFormat("x%.1f", animFrameSpeed),
+ &animFrameSpeed, 0.1f, 2.0f);
+
+ // Draw playing timeline with keyframes
+ GuiLabel((Rectangle){ 10, GetScreenHeight() - 64.0f, GetScreenWidth() - 20.0f, 24 },
+ TextFormat("CURRENT FRAME: %.2f / %i", animFrameProgress, anims[animIndex].keyframeCount));
+ GuiProgressBar((Rectangle){ 10, GetScreenHeight() - 40.0f, GetScreenWidth() - 20.0f, 24 }, NULL, NULL,
+ &animFrameProgress, 0.0f, (float)anims[animIndex].keyframeCount);
+ for (int i = 0; i < anims[animIndex].keyframeCount; i++)
+ DrawRectangle(10 + (int)(((float)(GetScreenWidth() - 20)/(float)anims[animIndex].keyframeCount)*(float)i),
+ GetScreenHeight() - 40, 1, 24, BLUE);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ UnloadModelAnimations(anims, animCount); // Unload model animation
+ UnloadModel(model); // Unload model and meshes/material
+
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
+
diff --git a/examples/models/models_animation_timing.png b/examples/models/models_animation_timing.png
new file mode 100644
index 000000000..4448fec67
Binary files /dev/null and b/examples/models/models_animation_timing.png differ
diff --git a/examples/models/models_basic_voxel.c b/examples/models/models_basic_voxel.c
index 61313ecef..2e2756f2b 100644
--- a/examples/models/models_basic_voxel.c
+++ b/examples/models/models_basic_voxel.c
@@ -72,6 +72,7 @@ int main(void)
UpdateCamera(&camera, CAMERA_FIRST_PERSON);
// Handle voxel removal with mouse click
+ // This method is quite inefficient. Ray marching through the voxel grid using DDA would be faster, but more complex.
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
{
// Cast a ray from the screen center (where crosshair would be)
@@ -79,12 +80,14 @@ int main(void)
Ray ray = GetMouseRay(screenCenter, camera);
// Check ray collision with all voxels
- bool voxelRemoved = false;
- for (int x = 0; (x < WORLD_SIZE) && !voxelRemoved; x++)
+ float closestDistance = 99999.0f;
+ Vector3 closestVoxelPosition = { -1, -1, -1 };
+ bool voxelFound = false;
+ for (int x = 0; x < WORLD_SIZE; x++)
{
- for (int y = 0; (y < WORLD_SIZE) && !voxelRemoved; y++)
+ for (int y = 0; y < WORLD_SIZE; y++)
{
- for (int z = 0; (z < WORLD_SIZE) && !voxelRemoved; z++)
+ for (int z = 0; z < WORLD_SIZE; z++)
{
if (!voxels[x][y][z]) continue; // Skip empty voxels
@@ -97,14 +100,23 @@ int main(void)
// Check ray-box collision
RayCollision collision = GetRayCollisionBox(ray, box);
- if (collision.hit)
+ if (collision.hit && (collision.distance < closestDistance))
{
- voxels[x][y][z] = false; // Remove this voxel
- voxelRemoved = true; // Exit all loops
+ closestDistance = collision.distance;
+ closestVoxelPosition = (Vector3){ x, y, z };
+ voxelFound = true;
}
}
}
}
+
+ // Remove the closest voxel if one was hit
+ if (voxelFound)
+ {
+ voxels[(int)closestVoxelPosition.x]
+ [(int)closestVoxelPosition.y]
+ [(int)closestVoxelPosition.z] = false;
+ }
}
//----------------------------------------------------------------------------------
@@ -137,7 +149,7 @@ int main(void)
EndMode3D();
// Draw reference point for raycasting to delete blocks
- DrawCircle(GetRenderWidth()/2, GetScreenHeight()/2, 4, RED);
+ DrawCircle(GetScreenWidth()/2, GetScreenHeight()/2, 4, RED);
DrawText("Left-click a voxel to remove it!", 10, 10, 20, DARKGRAY);
DrawText("WASD to move, mouse to look around", 10, 35, 10, GRAY);
diff --git a/examples/models/models_bone_socket.c b/examples/models/models_bone_socket.c
index 8348aa50d..ff44671c2 100644
--- a/examples/models/models_bone_socket.c
+++ b/examples/models/models_bone_socket.c
@@ -60,25 +60,25 @@ int main(void)
unsigned int animCurrentFrame = 0;
ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/greenman.glb", &animsCount);
- // indices of bones for sockets
+ // Indices of bones for sockets
int boneSocketIndex[BONE_SOCKETS] = { -1, -1, -1 };
- // search bones for sockets
- for (int i = 0; i < characterModel.boneCount; i++)
+ // Search bones for sockets
+ for (int i = 0; i < characterModel.skeleton.boneCount; i++)
{
- if (TextIsEqual(characterModel.bones[i].name, "socket_hat"))
+ if (TextIsEqual(characterModel.skeleton.bones[i].name, "socket_hat"))
{
boneSocketIndex[BONE_SOCKET_HAT] = i;
continue;
}
- if (TextIsEqual(characterModel.bones[i].name, "socket_hand_R"))
+ if (TextIsEqual(characterModel.skeleton.bones[i].name, "socket_hand_R"))
{
boneSocketIndex[BONE_SOCKET_HAND_R] = i;
continue;
}
- if (TextIsEqual(characterModel.bones[i].name, "socket_hand_L"))
+ if (TextIsEqual(characterModel.skeleton.bones[i].name, "socket_hand_L"))
{
boneSocketIndex[BONE_SOCKET_HAND_L] = i;
continue;
@@ -115,8 +115,8 @@ int main(void)
// Update model animation
ModelAnimation anim = modelAnimations[animIndex];
- animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount;
- UpdateModelAnimation(characterModel, anim, animCurrentFrame);
+ animCurrentFrame = (animCurrentFrame + 1)%anim.keyframeCount;
+ UpdateModelAnimation(characterModel, anim, (float)animCurrentFrame);
//----------------------------------------------------------------------------------
// Draw
@@ -129,7 +129,7 @@ int main(void)
// Draw character
Quaternion characterRotate = QuaternionFromAxisAngle((Vector3){ 0.0f, 1.0f, 0.0f }, angle*DEG2RAD);
characterModel.transform = MatrixMultiply(QuaternionToMatrix(characterRotate), MatrixTranslate(position.x, position.y, position.z));
- UpdateModelAnimation(characterModel, anim, animCurrentFrame);
+ UpdateModelAnimation(characterModel, anim, (float)animCurrentFrame);
DrawMesh(characterModel.meshes[0], characterModel.materials[1], characterModel.transform);
// Draw equipments (hat, sword, shield)
@@ -137,8 +137,8 @@ int main(void)
{
if (!showEquip[i]) continue;
- Transform *transform = &anim.framePoses[animCurrentFrame][boneSocketIndex[i]];
- Quaternion inRotation = characterModel.bindPose[boneSocketIndex[i]].rotation;
+ Transform *transform = &anim.keyframePoses[animCurrentFrame][boneSocketIndex[i]];
+ Quaternion inRotation = characterModel.skeleton.bindPose[boneSocketIndex[i]].rotation;
Quaternion outRotation = transform->rotation;
// Calculate socket rotation (angle between bone in initial pose and same bone in current animation frame)
diff --git a/examples/models/models_loading.c b/examples/models/models_loading.c
index 650b3126f..0672da047 100644
--- a/examples/models/models_loading.c
+++ b/examples/models/models_loading.c
@@ -7,11 +7,11 @@
* NOTE: raylib supports multiple models file formats:
*
* - OBJ > Text file format. Must include vertex position-texcoords-normals information,
-* if files references some .mtl materials file, it will be loaded (or try to)
-* - GLTF > Text/binary file format. Includes lot of information and it could
-* also reference external files, raylib will try loading mesh and materials data
+* if .obj references some .mtl materials file, it will be tried to be loaded
+* - GLTF/GLB > Text/binary file formats. Includes lot of information and it could
+* also reference external files, mesh and materials data will be tried to be loaded
* - IQM > Binary file format. Includes mesh vertex data but also animation data,
-* raylib can load .iqm animations
+* meshes and animation data can be loaded
* - VOX > Binary file format. MagikaVoxel mesh format:
* https://github.com/ephtracy/voxel-model/blob/master/MagicaVoxel-file-format-vox.txt
* - M3D > Binary file format. Model 3D format:
@@ -43,10 +43,10 @@ int main(void)
// Define the camera to look into our 3d world
Camera camera = { 0 };
camera.position = (Vector3){ 50.0f, 50.0f, 50.0f }; // Camera position
- camera.target = (Vector3){ 0.0f, 10.0f, 0.0f }; // Camera looking at point
+ camera.target = (Vector3){ 0.0f, 12.0f, 0.0f }; // Camera looking at point
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
camera.fovy = 45.0f; // Camera field-of-view Y
- camera.projection = CAMERA_PERSPECTIVE; // Camera mode type
+ camera.projection = CAMERA_PERSPECTIVE; // Camera mode type
Model model = LoadModel("resources/models/obj/castle.obj"); // Load model
Texture2D texture = LoadTexture("resources/models/obj/castle_diffuse.png"); // Load model texture
@@ -61,8 +61,6 @@ int main(void)
bool selected = false; // Selected object flag
- DisableCursor(); // Limit cursor to relative movement inside the window
-
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
@@ -71,7 +69,7 @@ int main(void)
{
// Update
//----------------------------------------------------------------------------------
- UpdateCamera(&camera, CAMERA_FIRST_PERSON);
+ UpdateCamera(&camera, CAMERA_ORBITAL);
// Load new models/textures on drag&drop
if (IsFileDropped())
@@ -93,7 +91,10 @@ int main(void)
bounds = GetMeshBoundingBox(model.meshes[0]);
- // TODO: Move camera position from target enough distance to visualize model properly
+ // Move camera position from target enough distance to visualize model properly
+ camera.position.x = bounds.max.x + 10.0f;
+ camera.position.y = bounds.max.y + 10.0f;
+ camera.position.z = bounds.max.z + 10.0f;
}
else if (IsFileExtension(droppedFiles.paths[0], ".png")) // Texture file formats supported
{
diff --git a/examples/models/models_loading_gltf.c b/examples/models/models_loading_gltf.c
index 7729da13f..f21207ba1 100644
--- a/examples/models/models_loading_gltf.c
+++ b/examples/models/models_loading_gltf.c
@@ -42,15 +42,17 @@ int main(void)
camera.fovy = 45.0f; // Camera field-of-view Y
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
- // Load gltf model
+ // Load model
Model model = LoadModel("resources/models/gltf/robot.glb");
- Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
+ Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model world position
- // Load gltf model animations
- int animsCount = 0;
- unsigned int animIndex = 0;
- unsigned int animCurrentFrame = 0;
- ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/robot.glb", &animsCount);
+ // Load model animations
+ int animCount = 0;
+ ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/robot.glb", &animCount);
+
+ // Animation playing variables
+ unsigned int animIndex = 0; // Current animation playing
+ unsigned int animCurrentFrame = 0; // Current animation frame
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
@@ -63,13 +65,12 @@ int main(void)
UpdateCamera(&camera, CAMERA_ORBITAL);
// Select current animation
- if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) animIndex = (animIndex + 1)%animsCount;
- else if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) animIndex = (animIndex + animsCount - 1)%animsCount;
+ if (IsKeyPressed(KEY_RIGHT)) animIndex = (animIndex + 1)%animCount;
+ else if (IsKeyPressed(KEY_LEFT)) animIndex = (animIndex + animCount - 1)%animCount;
// Update model animation
- ModelAnimation anim = modelAnimations[animIndex];
- animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount;
- UpdateModelAnimation(model, anim, animCurrentFrame);
+ animCurrentFrame = (animCurrentFrame + 1)%anims[animIndex].keyframeCount;
+ UpdateModelAnimation(model, anims[animIndex], (float)animCurrentFrame);
//----------------------------------------------------------------------------------
// Draw
@@ -79,12 +80,15 @@ int main(void)
ClearBackground(RAYWHITE);
BeginMode3D(camera);
- DrawModel(model, position, 1.0f, WHITE); // Draw animated model
+
+ DrawModel(model, position, 1.0f, WHITE);
+
DrawGrid(10, 1.0f);
+
EndMode3D();
- DrawText("Use the LEFT/RIGHT mouse buttons to switch animation", 10, 10, 20, GRAY);
- DrawText(TextFormat("Animation: %s", anim.name), 10, GetScreenHeight() - 20, 10, DARKGRAY);
+ DrawText(TextFormat("Current animation: %s", anims[animIndex].name), 10, 40, 20, MAROON);
+ DrawText("Use the LEFT/RIGHT keys to switch animation", 10, 10, 20, GRAY);
EndDrawing();
//----------------------------------------------------------------------------------
@@ -92,7 +96,8 @@ int main(void)
// De-Initialization
//--------------------------------------------------------------------------------------
- UnloadModel(model); // Unload model and meshes/material
+ UnloadModelAnimations(anims, animCount); // Unload model animations data
+ UnloadModel(model); // Unload model
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
diff --git a/examples/models/models_loading_gltf.png b/examples/models/models_loading_gltf.png
index ce2a85284..9aa3e2350 100644
Binary files a/examples/models/models_loading_gltf.png and b/examples/models/models_loading_gltf.png differ
diff --git a/examples/models/models_animation_playing.c b/examples/models/models_loading_iqm.c
similarity index 69%
rename from examples/models/models_animation_playing.c
rename to examples/models/models_loading_iqm.c
index fd708b7dd..0eab898a4 100644
--- a/examples/models/models_animation_playing.c
+++ b/examples/models/models_loading_iqm.c
@@ -1,6 +1,6 @@
/*******************************************************************************************
*
-* raylib [models] example - animation playing
+* raylib [models] example - loading iqm
*
* Example complexity rating: [★★☆☆] 2/4
*
@@ -8,17 +8,15 @@
*
* Example contributed by Culacant (@culacant) and reviewed by Ramon Santamaria (@raysan5)
*
+* NOTES: To export an IQM model from blender, make sure it is not posed, the vertices need
+* to be in the same position as they would be in edit mode and the scale of the models is
+* set to 0; scaling can be set from the export menu
+*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2019-2025 Culacant (@culacant) and Ramon Santamaria (@raysan5)
*
-********************************************************************************************
-*
-* NOTE: To export a model from blender, make sure it is not posed, the vertices need to be
-* in the same position as they would be in edit mode and the scale of your models is
-* set to 0. Scaling can be done from the export menu
-*
********************************************************************************************/
#include "raylib.h"
@@ -33,12 +31,12 @@ int main(void)
const int screenWidth = 800;
const int screenHeight = 450;
- InitWindow(screenWidth, screenHeight, "raylib [models] example - animation playing");
+ InitWindow(screenWidth, screenHeight, "raylib [models] example - loading iqm");
// Define the camera to look into our 3d world
Camera camera = { 0 };
camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position
- camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point
+ camera.target = (Vector3){ 0.0f, 4.0f, 0.0f }; // Camera looking at point
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
camera.fovy = 45.0f; // Camera field-of-view Y
camera.projection = CAMERA_PERSPECTIVE; // Camera mode type
@@ -46,15 +44,16 @@ int main(void)
Model model = LoadModel("resources/models/iqm/guy.iqm"); // Load the animated model mesh and basic data
Texture2D texture = LoadTexture("resources/models/iqm/guytex.png"); // Load model texture and set material
SetMaterialTexture(&model.materials[0], MATERIAL_MAP_DIFFUSE, texture); // Set model material map texture
-
- Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
+ Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
// Load animation data
- int animsCount = 0;
- ModelAnimation *anims = LoadModelAnimations("resources/models/iqm/guyanim.iqm", &animsCount);
- int animFrameCounter = 0;
+ int animCount = 0;
+ ModelAnimation *anims = LoadModelAnimations("resources/models/iqm/guyanim.iqm", &animCount);
+
+ // Animation playing variables
+ unsigned int animIndex = 0; // Current animation playing
+ float animCurrentFrame = 0.0f; // Current animation frame (supporting interpolated frames)
- DisableCursor(); // Catch cursor
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
@@ -63,15 +62,12 @@ int main(void)
{
// Update
//----------------------------------------------------------------------------------
- UpdateCamera(&camera, CAMERA_FIRST_PERSON);
+ UpdateCamera(&camera, CAMERA_ORBITAL);
// Play animation when spacebar is held down
- if (IsKeyDown(KEY_SPACE))
- {
- animFrameCounter++;
- UpdateModelAnimation(model, anims[0], animFrameCounter);
- if (animFrameCounter >= anims[0].frameCount) animFrameCounter = 0;
- }
+ animCurrentFrame += 1.0f;
+ UpdateModelAnimation(model, anims[0], animCurrentFrame);
+ if (animCurrentFrame >= anims[0].keyframeCount) animCurrentFrame = 0;
//----------------------------------------------------------------------------------
// Draw
@@ -84,16 +80,11 @@ int main(void)
DrawModelEx(model, position, (Vector3){ 1.0f, 0.0f, 0.0f }, -90.0f, (Vector3){ 1.0f, 1.0f, 1.0f }, WHITE);
- for (int i = 0; i < model.boneCount; i++)
- {
- DrawCube(anims[0].framePoses[animFrameCounter][i].translation, 0.2f, 0.2f, 0.2f, RED);
- }
-
- DrawGrid(10, 1.0f); // Draw a grid
+ DrawGrid(10, 1.0f);
EndMode3D();
- DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, 10, 20, MAROON);
+ DrawText(TextFormat("Current animation: %s", anims[animIndex].name), 10, 10, 20, MAROON);
DrawText("(c) Guy IQM 3D model by @culacant", screenWidth - 200, screenHeight - 20, 10, GRAY);
EndDrawing();
@@ -102,9 +93,9 @@ int main(void)
// De-Initialization
//--------------------------------------------------------------------------------------
- UnloadTexture(texture); // Unload texture
- UnloadModelAnimations(anims, animsCount); // Unload model animations data
- UnloadModel(model); // Unload model
+ UnloadTexture(texture); // Unload texture
+ UnloadModelAnimations(anims, animCount); // Unload model animations data
+ UnloadModel(model); // Unload model
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
diff --git a/examples/models/models_loading_iqm.png b/examples/models/models_loading_iqm.png
new file mode 100644
index 000000000..560cb3580
Binary files /dev/null and b/examples/models/models_loading_iqm.png differ
diff --git a/examples/models/models_loading_m3d.c b/examples/models/models_loading_m3d.c
index 452a21610..06911bf53 100644
--- a/examples/models/models_loading_m3d.c
+++ b/examples/models/models_loading_m3d.c
@@ -21,6 +21,8 @@
#include "raylib.h"
+static void DrawModelSkeleton(ModelSkeleton skeleton, ModelAnimPose pose, float scale, Color color);
+
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
@@ -41,22 +43,17 @@ int main(void)
camera.fovy = 45.0f; // Camera field-of-view Y
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
- Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
-
- char modelFileName[128] = "resources/models/m3d/cesium_man.m3d";
- bool drawMesh = 1;
- bool drawSkeleton = 1;
- bool animPlaying = false; // Store anim state, what to draw
-
// Load model
- Model model = LoadModel(modelFileName); // Load the bind-pose model mesh and basic data
+ Model model = LoadModel("resources/models/m3d/cesium_man.m3d"); // Load the animated model mesh and basic data
+ Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
- // Load animations
- int animsCount = 0;
- int animFrameCounter = 0, animId = 0;
- ModelAnimation *anims = LoadModelAnimations(modelFileName, &animsCount); // Load skeletal animation data
+ // Load animation data
+ int animCount = 0;
+ ModelAnimation *anims = LoadModelAnimations("resources/models/m3d/cesium_man.m3d", &animCount);
- DisableCursor(); // Limit cursor to relative movement inside the window
+ // Animation playing variables
+ unsigned int animIndex = 0; // Current animation playing
+ float animCurrentFrame = 0.0f; // Current animation frame (supporting interpolated frames)
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
@@ -66,38 +63,16 @@ int main(void)
{
// Update
//----------------------------------------------------------------------------------
- UpdateCamera(&camera, CAMERA_FIRST_PERSON);
+ UpdateCamera(&camera, CAMERA_ORBITAL);
- if (animsCount)
- {
- // Play animation when spacebar is held down (or step one frame with N)
- if (IsKeyDown(KEY_SPACE) || IsKeyPressed(KEY_N))
- {
- animFrameCounter++;
+ // Select current animation
+ if (IsKeyPressed(KEY_RIGHT)) animIndex = (animIndex + 1)%animCount;
+ else if (IsKeyPressed(KEY_LEFT)) animIndex = (animIndex + animCount - 1)%animCount;
- if (animFrameCounter >= anims[animId].frameCount) animFrameCounter = 0;
-
- UpdateModelAnimation(model, anims[animId], animFrameCounter);
- animPlaying = true;
- }
-
- // Select animation by pressing C
- if (IsKeyPressed(KEY_C))
- {
- animFrameCounter = 0;
- animId++;
-
- if (animId >= (int)animsCount) animId = 0;
- UpdateModelAnimation(model, anims[animId], 0);
- animPlaying = true;
- }
- }
-
- // Toggle skeleton drawing
- if (IsKeyPressed(KEY_B)) drawSkeleton ^= 1;
-
- // Toggle mesh drawing
- if (IsKeyPressed(KEY_M)) drawMesh ^= 1;
+ // Update model animation
+ animCurrentFrame += 1.0f;
+ if (animCurrentFrame >= anims[animIndex].keyframeCount) animCurrentFrame = 0.0f;
+ UpdateModelAnimation(model, anims[animIndex], animCurrentFrame);
//----------------------------------------------------------------------------------
// Draw
@@ -109,52 +84,19 @@ int main(void)
BeginMode3D(camera);
// Draw 3d model with texture
- if (drawMesh) DrawModel(model, position, 1.0f, WHITE);
-
- // Draw the animated skeleton
- if (drawSkeleton)
+ if (!IsKeyDown(KEY_SPACE)) DrawModel(model, position, 1.0f, WHITE);
+ else
{
- // Loop to (boneCount - 1) because the last one is a special "no bone" bone,
- // needed to workaround buggy models
- // without a -1, we would always draw a cube at the origin
- for (int i = 0; i < model.boneCount - 1; i++)
- {
- // By default the model is loaded in bind-pose by LoadModel()
- // But if UpdateModelAnimation() has been called at least once
- // then the model is already in animation pose, so we need the animated skeleton
- if (!animPlaying || !animsCount)
- {
- // Display the bind-pose skeleton
- DrawCube(model.bindPose[i].translation, 0.04f, 0.04f, 0.04f, RED);
-
- if (model.bones[i].parent >= 0)
- {
- DrawLine3D(model.bindPose[i].translation,
- model.bindPose[model.bones[i].parent].translation, RED);
- }
- }
- else
- {
- // Display the frame-pose skeleton
- DrawCube(anims[animId].framePoses[animFrameCounter][i].translation, 0.05f, 0.05f, 0.05f, RED);
-
- if (anims[animId].bones[i].parent >= 0)
- {
- DrawLine3D(anims[animId].framePoses[animFrameCounter][i].translation,
- anims[animId].framePoses[animFrameCounter][anims[animId].bones[i].parent].translation, RED);
- }
- }
- }
+ // Draw the animated skeleton
+ DrawModelSkeleton(model.skeleton, anims[animIndex].keyframePoses[(int)animCurrentFrame], 1.0f, RED);
}
- DrawGrid(10, 1.0f); // Draw a grid
+ DrawGrid(10, 1.0f);
EndMode3D();
- DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, GetScreenHeight() - 80, 10, MAROON);
- DrawText("PRESS N to STEP ONE ANIMATION FRAME", 10, GetScreenHeight() - 60, 10, DARKGRAY);
- DrawText("PRESS C to CYCLE THROUGH ANIMATIONS", 10, GetScreenHeight() - 40, 10, DARKGRAY);
- DrawText("PRESS M to toggle MESH, B to toggle SKELETON DRAWING", 10, GetScreenHeight() - 20, 10, DARKGRAY);
+ DrawText(TextFormat("Current animation: %s", anims[animIndex].name), 10, 10, 20, LIGHTGRAY);
+ DrawText("Press SPACE to draw skeleton", 10, 40, 20, MAROON);
DrawText("(c) CesiumMan model by KhronosGroup", GetScreenWidth() - 210, GetScreenHeight() - 20, 10, GRAY);
EndDrawing();
@@ -163,14 +105,28 @@ int main(void)
// De-Initialization
//--------------------------------------------------------------------------------------
-
- // Unload model animations data
- UnloadModelAnimations(anims, animsCount);
-
- UnloadModel(model); // Unload model
+ UnloadModelAnimations(anims, animCount); // Unload model animations data
+ UnloadModel(model); // Unload model
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
+
+// Draw model skeleton
+static void DrawModelSkeleton(ModelSkeleton skeleton, ModelAnimPose pose, float scale, Color color)
+{
+ // Loop to (boneCount - 1) because the last one is a special "no bone" bone,
+ // needed to workaround buggy models without a -1, a cube is always drawn at the origin
+ for (int i = 0; i < skeleton.boneCount - 1; i++)
+ {
+ // Display the frame-pose skeleton
+ DrawCube(pose[i].translation, scale*0.05f, scale*0.05f, scale*0.05f, color);
+
+ if (skeleton.bones[i].parent >= 0)
+ {
+ DrawLine3D(pose[i].translation, pose[skeleton.bones[i].parent].translation, color);
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/models/models_loading_m3d.png b/examples/models/models_loading_m3d.png
index 9220419ad..101831c60 100644
Binary files a/examples/models/models_loading_m3d.png and b/examples/models/models_loading_m3d.png differ
diff --git a/examples/models/models_loading_vox.c b/examples/models/models_loading_vox.c
index 06dc651d7..04b203ad9 100644
--- a/examples/models/models_loading_vox.c
+++ b/examples/models/models_loading_vox.c
@@ -25,9 +25,9 @@
#include "rlights.h"
#if defined(PLATFORM_DESKTOP)
-#define GLSL_VERSION 330
+ #define GLSL_VERSION 330
#else // PLATFORM_ANDROID, PLATFORM_WEB
-#define GLSL_VERSION 100
+ #define GLSL_VERSION 100
#endif
//------------------------------------------------------------------------------------
@@ -130,6 +130,7 @@ int main(void)
camerarot.y = 0;
}
+ // Update camere movement, custom controls
UpdateCameraPro(&camera,
(Vector3){ (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP))*0.1f - (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN))*0.1f, // Move forward-backward
(IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT))*0.1f - (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT))*0.1f, // Move right-left
@@ -173,7 +174,7 @@ int main(void)
DrawText("- MOUSE LEFT BUTTON: CYCLE VOX MODELS", 20, 50, 10, BLUE);
DrawText("- MOUSE MIDDLE BUTTON: ZOOM OR ROTATE CAMERA", 20, 70, 10, BLUE);
DrawText("- UP-DOWN-LEFT-RIGHT KEYS: MOVE CAMERA", 20, 90, 10, BLUE);
- DrawText(TextFormat("Model file: %s", GetFileName(voxFileNames[currentModel])), 10, 10, 20, GRAY);
+ DrawText(TextFormat("VOX model file: %s", GetFileName(voxFileNames[currentModel])), 10, 10, 20, GRAY);
EndDrawing();
//----------------------------------------------------------------------------------
diff --git a/examples/models/models_loading_vox.png b/examples/models/models_loading_vox.png
index 417d887e4..2bae0ab3e 100644
Binary files a/examples/models/models_loading_vox.png and b/examples/models/models_loading_vox.png differ
diff --git a/examples/models/raygui.h b/examples/models/raygui.h
index 42c5ae7bc..58bd38bef 100644
--- a/examples/models/raygui.h
+++ b/examples/models/raygui.h
@@ -357,8 +357,10 @@
#elif defined(USE_LIBTYPE_SHARED)
#define RAYGUIAPI __declspec(dllimport) // Using the library as a Win32 shared library (.dll)
#endif
+#if !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS // Disable unsafe warnings on scanf() functions in MSVC
#endif
+#endif
// Function specifiers definition
#ifndef RAYGUIAPI
@@ -646,6 +648,7 @@ typedef enum {
// ProgressBar
typedef enum {
PROGRESS_PADDING = 16, // ProgressBar internal padding
+ PROGRESS_SIDE, // ProgressBar increment side: 0-left->right, 1-right-left
} GuiProgressBarProperty;
// ScrollBar
@@ -3522,7 +3525,15 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight
}
// Draw slider internal progress bar (depends on state)
- GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)));
+ if (GuiGetStyle(PROGRESSBAR, PROGRESS_SIDE) == 0) // Left-->Right
+ {
+ GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)));
+ }
+ else // Right-->Left
+ {
+ progress.x = bounds.x + bounds.width - progress.width - GuiGetStyle(PROGRESSBAR, BORDER_WIDTH);
+ GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)));
+ }
}
// Draw left/right text if provided
@@ -5119,11 +5130,11 @@ static const char *GetTextIcon(const char *text, int *iconId)
// Get text divided into lines (by line-breaks '\n')
// WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator!
-static char **GetTextLines(const char *text, int *count)
+static const char **GetTextLines(const char *text, int *count)
{
#define RAYGUI_MAX_TEXT_LINES 128
- static char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 };
+ static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 };
for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings
int textLength = (int)strlen(text);
@@ -5193,7 +5204,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C
// WARNING: GuiTextSplit() function can't be used now because it can have already been used
// before the GuiDrawText() call and its buffer is static, it would be overriden :(
int lineCount = 0;
- char **lines = GetTextLines(text, &lineCount);
+ const char **lines = GetTextLines(text, &lineCount);
// Text style variables
//int alignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT);
diff --git a/examples/models/resources/shaders/glsl100/skinning.vs b/examples/models/resources/shaders/glsl100/skinning.vs
index 627a7dda4..344ad3f95 100644
--- a/examples/models/resources/shaders/glsl100/skinning.vs
+++ b/examples/models/resources/shaders/glsl100/skinning.vs
@@ -6,7 +6,7 @@
attribute vec3 vertexPosition;
attribute vec2 vertexTexCoord;
attribute vec4 vertexColor;
-attribute vec4 vertexBoneIds;
+attribute vec4 vertexBoneIndices;
attribute vec4 vertexBoneWeights;
// Input uniform values
@@ -19,10 +19,10 @@ varying vec4 fragColor;
void main()
{
- int boneIndex0 = int(vertexBoneIds.x);
- int boneIndex1 = int(vertexBoneIds.y);
- int boneIndex2 = int(vertexBoneIds.z);
- int boneIndex3 = int(vertexBoneIds.w);
+ int boneIndex0 = int(vertexBoneIndices.x);
+ int boneIndex1 = int(vertexBoneIndices.y);
+ int boneIndex2 = int(vertexBoneIndices.z);
+ int boneIndex3 = int(vertexBoneIndices.w);
// WARNING: OpenGL ES 2.0 does not support automatic matrix transposing, neither transpose() function
mat4 boneMatrixTransposed0 = mat4(
diff --git a/examples/models/resources/shaders/glsl120/skinning.vs b/examples/models/resources/shaders/glsl120/skinning.vs
index c08840b54..1d9855074 100644
--- a/examples/models/resources/shaders/glsl120/skinning.vs
+++ b/examples/models/resources/shaders/glsl120/skinning.vs
@@ -6,7 +6,7 @@
attribute vec3 vertexPosition;
attribute vec2 vertexTexCoord;
attribute vec4 vertexColor;
-attribute vec4 vertexBoneIds;
+attribute vec4 vertexBoneIndices;
attribute vec4 vertexBoneWeights;
// Input uniform values
@@ -19,10 +19,10 @@ varying vec4 fragColor;
void main()
{
- int boneIndex0 = int(vertexBoneIds.x);
- int boneIndex1 = int(vertexBoneIds.y);
- int boneIndex2 = int(vertexBoneIds.z);
- int boneIndex3 = int(vertexBoneIds.w);
+ int boneIndex0 = int(vertexBoneIndices.x);
+ int boneIndex1 = int(vertexBoneIndices.y);
+ int boneIndex2 = int(vertexBoneIndices.z);
+ int boneIndex3 = int(vertexBoneIndices.w);
// WARNING: OpenGL ES 2.0 does not support automatic matrix transposing, neither transpose() function
mat4 boneMatrixTransposed0 = mat4(
diff --git a/examples/models/resources/shaders/glsl330/skinning.vs b/examples/models/resources/shaders/glsl330/skinning.vs
index 43bbca76c..9b4ffbbfa 100644
--- a/examples/models/resources/shaders/glsl330/skinning.vs
+++ b/examples/models/resources/shaders/glsl330/skinning.vs
@@ -7,7 +7,7 @@ in vec3 vertexPosition;
in vec2 vertexTexCoord;
in vec4 vertexColor;
in vec3 vertexNormal;
-in vec4 vertexBoneIds;
+in vec4 vertexBoneIndices;
in vec4 vertexBoneWeights;
// Input uniform values
@@ -22,10 +22,10 @@ out vec3 fragNormal;
void main()
{
- int boneIndex0 = int(vertexBoneIds.x);
- int boneIndex1 = int(vertexBoneIds.y);
- int boneIndex2 = int(vertexBoneIds.z);
- int boneIndex3 = int(vertexBoneIds.w);
+ int boneIndex0 = int(vertexBoneIndices.x);
+ int boneIndex1 = int(vertexBoneIndices.y);
+ int boneIndex2 = int(vertexBoneIndices.z);
+ int boneIndex3 = int(vertexBoneIndices.w);
vec4 skinnedPosition =
vertexBoneWeights.x*(boneMatrices[boneIndex0]*vec4(vertexPosition, 1.0)) +
@@ -42,7 +42,6 @@ void main()
fragTexCoord = vertexTexCoord;
fragColor = vertexColor;
-
fragNormal = normalize(vec3(matNormal*skinnedNormal));
gl_Position = mvp*skinnedPosition;
diff --git a/examples/others/raylib_opengl_interop.c b/examples/others/raylib_opengl_interop.c
index 935eb87f8..540a623ab 100644
--- a/examples/others/raylib_opengl_interop.c
+++ b/examples/others/raylib_opengl_interop.c
@@ -30,7 +30,6 @@
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL)
#if defined(GRAPHICS_API_OPENGL_ES2)
- #define GLAD_GLES2_IMPLEMENTATION
#include "glad_gles2.h" // Required for: OpenGL functionality
#define glGenVertexArrays glGenVertexArraysOES
#define glBindVertexArray glBindVertexArrayOES
diff --git a/examples/shaders/raygui.h b/examples/shaders/raygui.h
index 42c5ae7bc..03e487912 100644
--- a/examples/shaders/raygui.h
+++ b/examples/shaders/raygui.h
@@ -646,6 +646,7 @@ typedef enum {
// ProgressBar
typedef enum {
PROGRESS_PADDING = 16, // ProgressBar internal padding
+ PROGRESS_SIDE, // ProgressBar increment side: 0-left->right, 1-right-left
} GuiProgressBarProperty;
// ScrollBar
@@ -3522,7 +3523,15 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight
}
// Draw slider internal progress bar (depends on state)
- GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)));
+ if (GuiGetStyle(PROGRESSBAR, PROGRESS_SIDE) == 0) // Left-->Right
+ {
+ GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)));
+ }
+ else // Right-->Left
+ {
+ progress.x = bounds.x + bounds.width - progress.width - GuiGetStyle(PROGRESSBAR, BORDER_WIDTH);
+ GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)));
+ }
}
// Draw left/right text if provided
@@ -5119,11 +5128,11 @@ static const char *GetTextIcon(const char *text, int *iconId)
// Get text divided into lines (by line-breaks '\n')
// WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator!
-static char **GetTextLines(const char *text, int *count)
+static const char **GetTextLines(const char *text, int *count)
{
#define RAYGUI_MAX_TEXT_LINES 128
- static char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 };
+ static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 };
for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings
int textLength = (int)strlen(text);
@@ -5193,7 +5202,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C
// WARNING: GuiTextSplit() function can't be used now because it can have already been used
// before the GuiDrawText() call and its buffer is static, it would be overriden :(
int lineCount = 0;
- char **lines = GetTextLines(text, &lineCount);
+ const char **lines = GetTextLines(text, &lineCount);
// Text style variables
//int alignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT);
diff --git a/examples/shaders/resources/shaders/glsl100/cel.fs b/examples/shaders/resources/shaders/glsl100/cel.fs
new file mode 100644
index 000000000..dfc22d0c0
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl100/cel.fs
@@ -0,0 +1,58 @@
+#version 100
+
+precision mediump float;
+
+varying vec3 fragPosition;
+varying vec2 fragTexCoord;
+varying vec4 fragColor;
+varying vec3 fragNormal;
+
+uniform sampler2D texture0;
+uniform vec4 colDiffuse;
+uniform vec3 viewPos;
+uniform float numBands;
+
+struct Light {
+ int enabled;
+ int type;
+ vec3 position;
+ vec3 target;
+ vec4 color;
+};
+uniform Light lights[4];
+
+void main()
+{
+ vec4 texColor = texture2D(texture0, fragTexCoord);
+ vec3 baseColor = texColor.rgb * fragColor.rgb * colDiffuse.rgb;
+ vec3 norm = normalize(fragNormal);
+
+ float lightAccum = 0.08; // ambient floor
+
+ for (int i = 0; i < 4; i++)
+ {
+ if (lights[i].enabled == 1) // no continue in GLSL ES 1.0
+ {
+ vec3 lightDir;
+ if (lights[i].type == 0)
+ {
+ // Directional: direction is from position toward target.
+ lightDir = normalize(lights[i].position - lights[i].target);
+ }
+ else
+ {
+ // Point: direction from surface to light.
+ lightDir = normalize(lights[i].position - fragPosition);
+ }
+
+ float NdotL = max(dot(norm, lightDir), 0.0);
+
+ // Quantize NdotL into numBands discrete steps.
+ float quantized = min(floor(NdotL * numBands), numBands - 1.0) / (numBands - 1.0);
+ lightAccum += quantized * lights[i].color.r;
+ }
+ }
+
+ lightAccum = clamp(lightAccum, 0.0, 1.0);
+ gl_FragColor = vec4(baseColor * lightAccum, texColor.a * colDiffuse.a);
+}
\ No newline at end of file
diff --git a/examples/shaders/resources/shaders/glsl100/cel.vs b/examples/shaders/resources/shaders/glsl100/cel.vs
new file mode 100644
index 000000000..153842a9b
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl100/cel.vs
@@ -0,0 +1,47 @@
+#version 100
+
+attribute vec3 vertexPosition;
+attribute vec2 vertexTexCoord;
+attribute vec3 vertexNormal;
+attribute vec4 vertexColor;
+
+uniform mat4 mvp;
+uniform mat4 matModel;
+
+varying vec3 fragPosition;
+varying vec2 fragTexCoord;
+varying vec4 fragColor;
+varying vec3 fragNormal;
+
+mat3 inverse(mat3 m)
+{
+ float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2];
+ float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2];
+ float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2];
+ float b01 = a22*a11 - a12*a21;
+ float b11 = -a22*a10 + a12*a20;
+ float b21 = a21*a10 - a11*a20;
+ float det = a00*b01 + a01*b11 + a02*b21;
+ return mat3(b01, (-a22*a01 + a02*a21), ( a12*a01 - a02*a11),
+ b11, ( a22*a00 - a02*a20), (-a12*a00 + a02*a10),
+ b21, (-a21*a00 + a01*a20), ( a11*a00 - a01*a10)) / det;
+}
+
+mat3 transpose(mat3 m)
+{
+ return mat3(m[0][0], m[1][0], m[2][0],
+ m[0][1], m[1][1], m[2][1],
+ m[0][2], m[1][2], m[2][2]);
+}
+
+void main()
+{
+ fragPosition = vec3(matModel * vec4(vertexPosition, 1.0));
+ fragTexCoord = vertexTexCoord;
+ fragColor = vertexColor;
+
+ mat3 normalMatrix = transpose(inverse(mat3(matModel)));
+ fragNormal = normalize(normalMatrix * vertexNormal);
+
+ gl_Position = mvp * vec4(vertexPosition, 1.0);
+}
\ No newline at end of file
diff --git a/examples/shaders/resources/shaders/glsl100/outline_hull.fs b/examples/shaders/resources/shaders/glsl100/outline_hull.fs
new file mode 100644
index 000000000..4f82cfc87
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl100/outline_hull.fs
@@ -0,0 +1,8 @@
+#version 100
+
+precision mediump float;
+
+void main()
+{
+ gl_FragColor = vec4(0.05, 0.05, 0.05, 1.0);
+}
\ No newline at end of file
diff --git a/examples/shaders/resources/shaders/glsl100/outline_hull.vs b/examples/shaders/resources/shaders/glsl100/outline_hull.vs
new file mode 100644
index 000000000..af7be8831
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl100/outline_hull.vs
@@ -0,0 +1,15 @@
+#version 100
+
+attribute vec3 vertexPosition;
+attribute vec3 vertexNormal;
+attribute vec2 vertexTexCoord;
+attribute vec4 vertexColor;
+
+uniform mat4 mvp;
+uniform float outlineThickness;
+
+void main()
+{
+ vec3 extruded = vertexPosition + vertexNormal * outlineThickness;
+ gl_Position = mvp * vec4(extruded, 1.0);
+}
\ No newline at end of file
diff --git a/examples/shaders/resources/shaders/glsl120/cel.fs b/examples/shaders/resources/shaders/glsl120/cel.fs
new file mode 100644
index 000000000..88321e60f
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl120/cel.fs
@@ -0,0 +1,56 @@
+#version 120
+
+varying vec3 fragPosition;
+varying vec2 fragTexCoord;
+varying vec4 fragColor;
+varying vec3 fragNormal;
+
+uniform sampler2D texture0;
+uniform vec4 colDiffuse;
+uniform vec3 viewPos;
+uniform float numBands;
+
+struct Light {
+ int enabled;
+ int type;
+ vec3 position;
+ vec3 target;
+ vec4 color;
+};
+uniform Light lights[4];
+
+void main()
+{
+ vec4 texColor = texture2D(texture0, fragTexCoord);
+ vec3 baseColor = texColor.rgb * fragColor.rgb * colDiffuse.rgb;
+ vec3 norm = normalize(fragNormal);
+
+ float lightAccum = 0.08; // ambient floor
+
+ for (int i = 0; i < 4; i++)
+ {
+ if (lights[i].enabled == 1)
+ {
+ vec3 lightDir;
+ if (lights[i].type == 0)
+ {
+ // Directional: direction is from position toward target.
+ lightDir = normalize(lights[i].position - lights[i].target);
+ }
+ else
+ {
+ // Point: direction from surface to light.
+ lightDir = normalize(lights[i].position - fragPosition);
+ }
+
+ float NdotL = max(dot(norm, lightDir), 0.0);
+
+ // Quantize NdotL into numBands discrete steps.
+ float quantized = min(floor(NdotL * numBands), numBands - 1.0) / (numBands - 1.0);
+ lightAccum += quantized * lights[i].color.r;
+ }
+ }
+
+ lightAccum = clamp(lightAccum, 0.0, 1.0);
+ gl_FragColor = vec4(baseColor * lightAccum, texColor.a * colDiffuse.a);
+}
\ No newline at end of file
diff --git a/examples/shaders/resources/shaders/glsl120/cel.vs b/examples/shaders/resources/shaders/glsl120/cel.vs
new file mode 100644
index 000000000..3c736aec7
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl120/cel.vs
@@ -0,0 +1,48 @@
+#version 120
+
+attribute vec3 vertexPosition;
+attribute vec2 vertexTexCoord;
+attribute vec3 vertexNormal;
+attribute vec4 vertexColor;
+
+uniform mat4 mvp;
+uniform mat4 matModel;
+
+varying vec3 fragPosition;
+varying vec2 fragTexCoord;
+varying vec4 fragColor;
+varying vec3 fragNormal;
+
+// inverse() and transpose() are not built-in until GLSL 1.40
+mat3 inverse(mat3 m)
+{
+ float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2];
+ float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2];
+ float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2];
+ float b01 = a22*a11 - a12*a21;
+ float b11 = -a22*a10 + a12*a20;
+ float b21 = a21*a10 - a11*a20;
+ float det = a00*b01 + a01*b11 + a02*b21;
+ return mat3(b01, (-a22*a01 + a02*a21), ( a12*a01 - a02*a11),
+ b11, ( a22*a00 - a02*a20), (-a12*a00 + a02*a10),
+ b21, (-a21*a00 + a01*a20), ( a11*a00 - a01*a10)) / det;
+}
+
+mat3 transpose(mat3 m)
+{
+ return mat3(m[0][0], m[1][0], m[2][0],
+ m[0][1], m[1][1], m[2][1],
+ m[0][2], m[1][2], m[2][2]);
+}
+
+void main()
+{
+ fragPosition = vec3(matModel * vec4(vertexPosition, 1.0));
+ fragTexCoord = vertexTexCoord;
+ fragColor = vertexColor;
+
+ mat3 normalMatrix = transpose(inverse(mat3(matModel)));
+ fragNormal = normalize(normalMatrix * vertexNormal);
+
+ gl_Position = mvp * vec4(vertexPosition, 1.0);
+}
\ No newline at end of file
diff --git a/examples/shaders/resources/shaders/glsl120/outline_hull.fs b/examples/shaders/resources/shaders/glsl120/outline_hull.fs
new file mode 100644
index 000000000..b6e4fc828
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl120/outline_hull.fs
@@ -0,0 +1,6 @@
+#version 120
+
+void main()
+{
+ gl_FragColor = vec4(0.05, 0.05, 0.05, 1.0);
+}
diff --git a/examples/shaders/resources/shaders/glsl120/outline_hull.vs b/examples/shaders/resources/shaders/glsl120/outline_hull.vs
new file mode 100644
index 000000000..7ad125be1
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl120/outline_hull.vs
@@ -0,0 +1,15 @@
+#version 120
+
+attribute vec3 vertexPosition;
+attribute vec3 vertexNormal;
+attribute vec2 vertexTexCoord;
+attribute vec4 vertexColor;
+
+uniform mat4 mvp;
+uniform float outlineThickness;
+
+void main()
+{
+ vec3 extruded = vertexPosition + vertexNormal * outlineThickness;
+ gl_Position = mvp * vec4(extruded, 1.0);
+}
\ No newline at end of file
diff --git a/examples/shaders/resources/shaders/glsl330/cel.fs b/examples/shaders/resources/shaders/glsl330/cel.fs
new file mode 100644
index 000000000..78a2097c2
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl330/cel.fs
@@ -0,0 +1,60 @@
+#version 330
+
+in vec3 fragPosition;
+in vec2 fragTexCoord;
+in vec4 fragColor;
+in vec3 fragNormal;
+
+// Raylib standard uniforms
+uniform sampler2D texture0;
+uniform vec4 colDiffuse;
+
+// View position for future specular / fresnel use.
+uniform vec3 viewPos;
+
+// Number of discrete toon bands (2 = hard binary, 10 = default, 20 = near-smooth).
+uniform float numBands;
+
+// rlights.h compatible light block.
+struct Light {
+ int enabled;
+ int type; // 0 = directional, 1 = point
+ vec3 position;
+ vec3 target;
+ vec4 color;
+ float attenuation;
+};
+uniform Light lights[4];
+
+out vec4 finalColor;
+
+void main() {
+ vec4 texColor = texture(texture0, fragTexCoord);
+ vec3 baseColor = texColor.rgb * fragColor.rgb * colDiffuse.rgb;
+ vec3 norm = normalize(fragNormal);
+
+ float lightAccum = 0.08; // ambient floor
+
+ for (int i = 0; i < 4; i++) {
+ if (lights[i].enabled == 0) continue;
+
+ vec3 lightDir;
+ if (lights[i].type == 0) {
+ // Directional: direction is from position toward target.
+ lightDir = normalize(lights[i].position - lights[i].target);
+ } else {
+ // Point: direction from surface to light.
+ lightDir = normalize(lights[i].position - fragPosition);
+ }
+
+ float NdotL = max(dot(norm, lightDir), 0.0);
+
+ // Quantize NdotL into numBands discrete steps.
+ // min() guards against NdotL == 1.0 producing an out-of-range index.
+ float quantized = min(floor(NdotL * numBands), numBands - 1.0) / (numBands - 1.0);
+ lightAccum += quantized * lights[i].color.r;
+ }
+
+ lightAccum = clamp(lightAccum, 0.0, 1.0);
+ finalColor = vec4(baseColor * lightAccum, texColor.a * colDiffuse.a);
+}
diff --git a/examples/shaders/resources/shaders/glsl330/cel.vs b/examples/shaders/resources/shaders/glsl330/cel.vs
new file mode 100644
index 000000000..37a8d200d
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl330/cel.vs
@@ -0,0 +1,25 @@
+#version 330
+
+// Raylib standard attributes
+in vec3 vertexPosition;
+in vec2 vertexTexCoord;
+in vec3 vertexNormal;
+in vec4 vertexColor;
+
+// Raylib standard uniforms
+uniform mat4 mvp;
+uniform mat4 matModel;
+uniform mat4 matNormal;
+
+out vec3 fragPosition;
+out vec2 fragTexCoord;
+out vec4 fragColor;
+out vec3 fragNormal;
+
+void main() {
+ fragPosition = vec3(matModel * vec4(vertexPosition, 1.0));
+ fragTexCoord = vertexTexCoord;
+ fragColor = vertexColor;
+ fragNormal = normalize(vec3(matNormal * vec4(vertexNormal, 0.0)));
+ gl_Position = mvp * vec4(vertexPosition, 1.0);
+}
diff --git a/examples/shaders/resources/shaders/glsl330/outline_hull.fs b/examples/shaders/resources/shaders/glsl330/outline_hull.fs
new file mode 100644
index 000000000..c8a33de08
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl330/outline_hull.fs
@@ -0,0 +1,7 @@
+#version 330
+
+out vec4 finalColor;
+
+void main() {
+ finalColor = vec4(0.05, 0.05, 0.05, 1.0);
+}
diff --git a/examples/shaders/resources/shaders/glsl330/outline_hull.vs b/examples/shaders/resources/shaders/glsl330/outline_hull.vs
new file mode 100644
index 000000000..2e350201e
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl330/outline_hull.vs
@@ -0,0 +1,15 @@
+#version 330
+
+in vec3 vertexPosition;
+in vec3 vertexNormal;
+in vec2 vertexTexCoord;
+in vec4 vertexColor;
+
+uniform mat4 mvp;
+uniform float outlineThickness;
+
+void main() {
+ // Extrude vertex along its normal to create the hull.
+ vec3 extruded = vertexPosition + vertexNormal * outlineThickness;
+ gl_Position = mvp * vec4(extruded, 1.0);
+}
diff --git a/examples/shaders/shaders_cel_shading.c b/examples/shaders/shaders_cel_shading.c
new file mode 100644
index 000000000..133b8db9e
--- /dev/null
+++ b/examples/shaders/shaders_cel_shading.c
@@ -0,0 +1,175 @@
+/*******************************************************************************************
+*
+* raylib [shaders] example - cel shading
+*
+* Example complexity rating: [★★★☆] 3/4
+*
+* NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
+* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
+*
+* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3)
+*
+* Example contributed by Gleb A (@ggrizzly) and reviewed by Ramon Santamaria (@raysan5)
+*
+* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+* BSD-like license that allows static linking with closed source software
+*
+* Copyright (c) 2026 Gleb A (@ggrizzly)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include "raymath.h"
+#include "rlgl.h"
+
+#include // Required for: sinf(), cosf()
+
+#define RLIGHTS_IMPLEMENTATION
+#include "rlights.h"
+
+#if defined(PLATFORM_DESKTOP)
+ #define GLSL_VERSION 330
+#else // PLATFORM_ANDROID, PLATFORM_WEB
+ #define GLSL_VERSION 100
+#endif
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ SetConfigFlags(FLAG_MSAA_4X_HINT);
+ InitWindow(screenWidth, screenHeight, "raylib [shaders] example - cel shading");
+
+ Camera camera = { 0 };
+ camera.position = (Vector3){ 9.0f, 6.0f, 9.0f };
+ camera.target = (Vector3){ 0.0f, 1.0f, 0.0f };
+ camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
+ camera.fovy = 45.0f;
+ camera.projection = CAMERA_PERSPECTIVE;
+
+ // Load model
+ Model model = LoadModel("resources/models/old_car_new.glb");
+
+ // Load cel shader
+ Shader celShader = LoadShader(
+ TextFormat("resources/shaders/glsl%i/cel.vs", GLSL_VERSION),
+ TextFormat("resources/shaders/glsl%i/cel.fs", GLSL_VERSION));
+ celShader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(celShader, "viewPos");
+
+ // Apply cel shader to model, keep copy of default shader
+ Shader defaultShader = model.materials[0].shader;
+ model.materials[0].shader = celShader;
+
+ // numBands: controls toon quantization steps (2 = hard binary, 20 = near-smooth)
+ float numBands = 10.0f;
+ int numBandsLoc = GetShaderLocation(celShader, "numBands");
+ SetShaderValue(celShader, numBandsLoc, &numBands, SHADER_UNIFORM_FLOAT);
+
+ // Inverted-hull outline shader: draws back faces extruded along normals
+ Shader outlineShader = LoadShader(
+ TextFormat("resources/shaders/glsl%i/outline_hull.vs", GLSL_VERSION),
+ TextFormat("resources/shaders/glsl%i/outline_hull.fs", GLSL_VERSION));
+ int outlineThicknessLoc = GetShaderLocation(outlineShader, "outlineThickness");
+
+ // Single directional white light, angled so toon bands are visible on the model sides.
+ // Spins opposite to CAMERA_ORBITAL (0.5 rad/s) so lighting changes as you watch.
+ Light lights[MAX_LIGHTS] = { 0 };
+ lights[0] = CreateLight(LIGHT_DIRECTIONAL, (Vector3){ 50.0f, 50.0f, 50.0f }, Vector3Zero(), WHITE, celShader);
+
+ bool celEnabled = true;
+ bool outlineEnabled = true;
+
+ SetTargetFPS(60);
+ //--------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose())
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ UpdateCamera(&camera, CAMERA_ORBITAL);
+
+ float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z };
+ SetShaderValue(celShader, celShader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3);
+
+ // [Z] Toggle cel shading on/off
+ if (IsKeyPressed(KEY_Z))
+ {
+ celEnabled = !celEnabled;
+ if (celEnabled) model.materials[0].shader = celShader; // Apply cel shader to model
+ else model.materials[0].shader = defaultShader; // Apply default shader to model
+ }
+
+ // [C] Toggle outline on/off
+ if (IsKeyPressed(KEY_C)) outlineEnabled = !outlineEnabled;
+
+ // [Q/E] Decrease/increase toon band count (press or hold to repeat)
+ if (IsKeyPressed(KEY_E) || IsKeyPressedRepeat(KEY_E)) numBands = Clamp(numBands + 1.0f, 2.0f, 20.0f);
+ if (IsKeyPressed(KEY_Q) || IsKeyPressedRepeat(KEY_Q)) numBands = Clamp(numBands - 1.0f, 2.0f, 20.0f);
+ SetShaderValue(celShader, numBandsLoc, &numBands, SHADER_UNIFORM_FLOAT);
+
+ // Spin light opposite to CAMERA_ORBITAL (0.5 rad/s), angled 45 degrees off vertical
+ float t = (float)GetTime();
+ lights[0].position = (Vector3){ sinf(-t*0.3f)*5.0f, 5.0f, cosf(-t*0.3f)*5.0f };
+
+ for (int i = 0; i < MAX_LIGHTS; i++) UpdateLightValues(celShader, lights[i]);
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground(RAYWHITE);
+
+ BeginMode3D(camera);
+
+ if (outlineEnabled)
+ {
+ // Outline pass: cull front faces, draw extruded back faces as silhouette
+ float thickness = 0.005f;
+ SetShaderValue(outlineShader, outlineThicknessLoc, &thickness, SHADER_UNIFORM_FLOAT);
+
+ rlSetCullFace(RL_CULL_FACE_FRONT);
+
+ model.materials[0].shader = outlineShader;
+
+ DrawModel(model, Vector3Zero(), 0.75f, WHITE);
+
+ if (celEnabled) model.materials[0].shader = celShader; // Apply cel shader to model
+ else model.materials[0].shader = defaultShader; // Apply default shader to model
+
+ rlSetCullFace(RL_CULL_FACE_BACK);
+ }
+
+ DrawModel(model, Vector3Zero(), 0.75f, WHITE);
+ DrawSphereEx(lights[0].position, 0.2f, 50, 50, YELLOW); // Light position indicator
+ DrawGrid(10, 10.0f);
+
+ EndMode3D();
+
+ DrawFPS(10, 10);
+ DrawText(TextFormat("Cel: %s [Z]", celEnabled? "ON" : "OFF"), 10, 65, 20, celEnabled? DARKGREEN : DARKGRAY);
+ DrawText(TextFormat("Outline: %s [C]", outlineEnabled? "ON" : "OFF"), 10, 90, 20, outlineEnabled? DARKGREEN : DARKGRAY);
+ DrawText(TextFormat("Bands: %.0f [Q/E]", numBands), 10, 115, 20, DARKGRAY);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ UnloadModel(model);
+ UnloadShader(celShader);
+ UnloadShader(outlineShader);
+
+ CloseWindow();
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
diff --git a/examples/shaders/shaders_cel_shading.png b/examples/shaders/shaders_cel_shading.png
new file mode 100644
index 000000000..51b98225e
Binary files /dev/null and b/examples/shaders/shaders_cel_shading.png differ
diff --git a/examples/shaders/shaders_rlgl_compute.c b/examples/shaders/shaders_rlgl_compute.c
index af3bb6fe4..a92fbac6a 100644
--- a/examples/shaders/shaders_rlgl_compute.c
+++ b/examples/shaders/shaders_rlgl_compute.c
@@ -65,8 +65,8 @@ int main(void)
// Game of Life logic compute shader
char *golLogicCode = LoadFileText("resources/shaders/glsl430/gol.glsl");
- unsigned int golLogicShader = rlCompileShader(golLogicCode, RL_COMPUTE_SHADER);
- unsigned int golLogicProgram = rlLoadComputeShaderProgram(golLogicShader);
+ unsigned int golLogicShader = rlLoadShader(golLogicCode, RL_COMPUTE_SHADER);
+ unsigned int golLogicProgram = rlLoadShaderProgramCompute(golLogicShader);
UnloadFileText(golLogicCode);
// Game of Life logic render shader
@@ -75,8 +75,8 @@ int main(void)
// Game of Life transfert shader (CPU<->GPU download and upload)
char *golTransfertCode = LoadFileText("resources/shaders/glsl430/gol_transfert.glsl");
- unsigned int golTransfertShader = rlCompileShader(golTransfertCode, RL_COMPUTE_SHADER);
- unsigned int golTransfertProgram = rlLoadComputeShaderProgram(golTransfertShader);
+ unsigned int golTransfertShader = rlLoadShader(golTransfertCode, RL_COMPUTE_SHADER);
+ unsigned int golTransfertProgram = rlLoadShaderProgramCompute(golTransfertShader);
UnloadFileText(golTransfertCode);
// Load shader storage buffer object (SSBO), id returned
@@ -169,7 +169,9 @@ int main(void)
rlUnloadShaderBuffer(ssboB);
rlUnloadShaderBuffer(ssboTransfert);
- // Unload compute shader programs
+ // Unload compute shader
+ rlUnloadShader(golLogicShader);
+ rlUnloadShader(golTransfertShader);
rlUnloadShaderProgram(golTransfertProgram);
rlUnloadShaderProgram(golLogicProgram);
diff --git a/examples/shaders/shaders_shadowmap_rendering.c b/examples/shaders/shaders_shadowmap_rendering.c
index a739cdd93..3b98ab5b6 100644
--- a/examples/shaders/shaders_shadowmap_rendering.c
+++ b/examples/shaders/shaders_shadowmap_rendering.c
@@ -79,8 +79,9 @@ int main(void)
cube.materials[0].shader = shadowShader;
Model robot = LoadModel("resources/models/robot.glb");
for (int i = 0; i < robot.materialCount; i++) robot.materials[i].shader = shadowShader;
+
int animCount = 0;
- ModelAnimation *robotAnimations = LoadModelAnimations("resources/models/robot.glb", &animCount);
+ ModelAnimation *anims = LoadModelAnimations("resources/models/robot.glb", &animCount);
RenderTexture2D shadowMap = LoadShadowmapRenderTexture(SHADOWMAP_RESOLUTION, SHADOWMAP_RESOLUTION);
@@ -115,8 +116,8 @@ int main(void)
UpdateCamera(&camera, CAMERA_ORBITAL);
frameCounter++;
- frameCounter %= (robotAnimations[0].frameCount);
- UpdateModelAnimation(robot, robotAnimations[0], frameCounter);
+ frameCounter %= (anims[0].keyframeCount);
+ UpdateModelAnimation(robot, anims[0], (float)frameCounter);
// Move light with arrow keys
const float cameraSpeed = 0.05f;
@@ -190,7 +191,7 @@ int main(void)
UnloadShader(shadowShader);
UnloadModel(cube);
UnloadModel(robot);
- UnloadModelAnimations(robotAnimations, animCount);
+ UnloadModelAnimations(anims, animCount);
UnloadShadowmapRenderTexture(shadowMap);
CloseWindow(); // Close window and OpenGL context
diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h
index 42c5ae7bc..07ef3c05b 100644
--- a/examples/shapes/raygui.h
+++ b/examples/shapes/raygui.h
@@ -357,7 +357,9 @@
#elif defined(USE_LIBTYPE_SHARED)
#define RAYGUIAPI __declspec(dllimport) // Using the library as a Win32 shared library (.dll)
#endif
- #define _CRT_SECURE_NO_WARNINGS // Disable unsafe warnings on scanf() functions in MSVC
+#if !defined(_CRT_SECURE_NO_WARNINGS)
+#define _CRT_SECURE_NO_WARNINGS // Disable unsafe warnings on scanf() functions in MSVC
+#endif
#endif
// Function specifiers definition
@@ -646,6 +648,7 @@ typedef enum {
// ProgressBar
typedef enum {
PROGRESS_PADDING = 16, // ProgressBar internal padding
+ PROGRESS_SIDE, // ProgressBar increment side: 0-left->right, 1-right-left
} GuiProgressBarProperty;
// ScrollBar
@@ -3522,7 +3525,15 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight
}
// Draw slider internal progress bar (depends on state)
- GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)));
+ if (GuiGetStyle(PROGRESSBAR, PROGRESS_SIDE) == 0) // Left-->Right
+ {
+ GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)));
+ }
+ else // Right-->Left
+ {
+ progress.x = bounds.x + bounds.width - progress.width - GuiGetStyle(PROGRESSBAR, BORDER_WIDTH);
+ GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)));
+ }
}
// Draw left/right text if provided
@@ -5119,11 +5130,11 @@ static const char *GetTextIcon(const char *text, int *iconId)
// Get text divided into lines (by line-breaks '\n')
// WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator!
-static char **GetTextLines(const char *text, int *count)
+static const char **GetTextLines(const char *text, int *count)
{
#define RAYGUI_MAX_TEXT_LINES 128
- static char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 };
+ static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 };
for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings
int textLength = (int)strlen(text);
@@ -5193,7 +5204,7 @@ static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, C
// WARNING: GuiTextSplit() function can't be used now because it can have already been used
// before the GuiDrawText() call and its buffer is static, it would be overriden :(
int lineCount = 0;
- char **lines = GetTextLines(text, &lineCount);
+ const char **lines = GetTextLines(text, &lineCount);
// Text style variables
//int alignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT);
diff --git a/examples/shapes/shapes_hilbert_curve.c b/examples/shapes/shapes_hilbert_curve.c
index 3f368ca03..759fe93fd 100644
--- a/examples/shapes/shapes_hilbert_curve.c
+++ b/examples/shapes/shapes_hilbert_curve.c
@@ -43,7 +43,7 @@ int main(void)
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - hilbert curve");
int order = 2;
- float size = GetScreenHeight();
+ float size = (float)GetScreenHeight();
int strokeCount = 0;
Vector2 *hilbertPath = LoadHilbertPath(order, size, &strokeCount);
@@ -73,7 +73,7 @@ int main(void)
else counter = strokeCount;
prevOrder = order;
- prevSize = size;
+ prevSize = (int)size;
}
//----------------------------------------------------------------------------------
diff --git a/examples/shapes/shapes_math_sine_cosine.c b/examples/shapes/shapes_math_sine_cosine.c
index c3646cabd..c9964a9ff 100644
--- a/examples/shapes/shapes_math_sine_cosine.c
+++ b/examples/shapes/shapes_math_sine_cosine.c
@@ -89,7 +89,7 @@ int main(void)
// Cotangent (orange)
DrawLineEx((Vector2){ center.x , limitMin.y }, (Vector2){ cotangentPoint.x, limitMin.y }, 2.0f, ORANGE);
- DrawLineDashed(center, cotangentPoint, 10.0f, 4.0f, ORANGE);
+ DrawLineDashed(center, cotangentPoint, 10, 4, ORANGE);
// Side background
DrawLine(580, 0, 580, GetScreenHeight(), (Color){ 218, 218, 218, 255 });
@@ -106,48 +106,48 @@ int main(void)
DrawLineEx((Vector2){ start.x, start.y + start.height/2 }, (Vector2){ start.x + start.width, start.y + start.height/2 }, 2.0f, GRAY);
// Wave graph axis labels
- DrawText("1", start.x - 8, start.y, 6, GRAY);
- DrawText("0", start.x - 8, start.y + start.height/2 - 6, 6, GRAY);
- DrawText("-1", start.x - 12, start.y + start.height - 8, 6, GRAY);
- DrawText("0", start.x - 2, start.y + start.height + 4, 6, GRAY);
- DrawText("360", start.x + start.width - 8, start.y + start.height + 4, 6, GRAY);
+ DrawText("1", (int)start.x - 8, (int)start.y, 6, GRAY);
+ DrawText("0", (int)start.x - 8, (int)start.y + (int)start.height/2 - 6, 6, GRAY);
+ DrawText("-1", (int)start.x - 12, (int)start.y + (int)start.height - 8, 6, GRAY);
+ DrawText("0", (int)start.x - 2, (int)start.y + (int)start.height + 4, 6, GRAY);
+ DrawText("360", (int)start.x + (int)start.width - 8, (int)start.y + (int)start.height + 4, 6, GRAY);
// Sine (red - vertical)
DrawLineEx((Vector2){ center.x, center.y }, (Vector2){ center.x, point.y }, 2.0f, RED);
- DrawLineDashed((Vector2){ point.x, center.y }, (Vector2){ point.x, point.y }, 10.0f, 4.0f, RED);
+ DrawLineDashed((Vector2){ point.x, center.y }, (Vector2){ point.x, point.y }, 10, 4, RED);
DrawText(TextFormat("Sine %.2f", sinRad), 640, 190, 6, RED);
DrawCircleV((Vector2){ start.x + (angle/360.0f)*start.width, start.y + ((-sinRad + 1)*start.height/2.0f) }, 4.0f, RED);
DrawSplineLinear(sinePoints, WAVE_POINTS, 1.0f, RED);
// Cosine (blue - horizontal)
DrawLineEx((Vector2){ center.x, center.y }, (Vector2){ point.x, center.y }, 2.0f, BLUE);
- DrawLineDashed((Vector2){ center.x , point.y }, (Vector2){ point.x, point.y }, 10.0f, 4.0f, BLUE);
+ DrawLineDashed((Vector2){ center.x , point.y }, (Vector2){ point.x, point.y }, 10, 4, BLUE);
DrawText(TextFormat("Cosine %.2f", cosRad), 640, 210, 6, BLUE);
DrawCircleV((Vector2){ start.x + (angle/360.0f)*start.width, start.y + ((-cosRad + 1)*start.height/2.0f) }, 4.0f, BLUE);
DrawSplineLinear(cosPoints, WAVE_POINTS, 1.0f, BLUE);
// Tangent (purple)
DrawLineEx((Vector2){ limitMax.x , center.y }, (Vector2){ limitMax.x, tangentPoint.y }, 2.0f, PURPLE);
- DrawLineDashed(center, tangentPoint, 10.0f, 4.0f, PURPLE);
+ DrawLineDashed(center, tangentPoint, 10, 4, PURPLE);
DrawText(TextFormat("Tangent %.2f", tangent), 640, 230, 6, PURPLE);
// Cotangent (orange)
DrawText(TextFormat("Cotangent %.2f", cotangent), 640, 250, 6, ORANGE);
// Complementary angle (beige)
- DrawCircleSectorLines(center, radius*0.6f , -angle, -90.0f , 36.0f, BEIGE);
+ DrawCircleSectorLines(center, radius*0.6f , -angle, -90.0f , 36, BEIGE);
DrawText(TextFormat("Complementary %0.f°",complementary), 640, 150, 6, BEIGE);
// Supplementary angle (darkblue)
- DrawCircleSectorLines(center, radius*0.5f , -angle, -180.0f , 36.0f, DARKBLUE);
+ DrawCircleSectorLines(center, radius*0.5f , -angle, -180.0f , 36, DARKBLUE);
DrawText(TextFormat("Supplementary %0.f°",supplementary), 640, 130, 6, DARKBLUE);
// Explementary angle (pink)
- DrawCircleSectorLines(center, radius*0.4f , -angle, -360.0f , 36.0f, PINK);
+ DrawCircleSectorLines(center, radius*0.4f , -angle, -360.0f , 36, PINK);
DrawText(TextFormat("Explementary %0.f°",explementary), 640, 170, 6, PINK);
// Current angle - arc (lime), radius (black), endpoint (black)
- DrawCircleSectorLines(center, radius*0.7f , -angle, 0.0f, 36.0f, LIME);
+ DrawCircleSectorLines(center, radius*0.7f , -angle, 0.0f, 36, LIME);
DrawLineEx((Vector2){ center.x , center.y }, point, 2.0f, BLACK);
DrawCircleV(point, 4.0f, BLACK);
diff --git a/examples/shapes/shapes_penrose_tile.c b/examples/shapes/shapes_penrose_tile.c
index cf41852f8..f94eae763 100644
--- a/examples/shapes/shapes_penrose_tile.c
+++ b/examples/shapes/shapes_penrose_tile.c
@@ -30,7 +30,7 @@
//----------------------------------------------------------------------------------
typedef struct TurtleState {
Vector2 origin;
- double angle;
+ float angle;
} TurtleState;
typedef struct PenroseLSystem {
diff --git a/examples/shapes/shapes_pie_chart.c b/examples/shapes/shapes_pie_chart.c
index baf0a3652..5ef6802dc 100644
--- a/examples/shapes/shapes_pie_chart.c
+++ b/examples/shapes/shapes_pie_chart.c
@@ -160,7 +160,7 @@ int main(void)
// Draw inner circle to create donut effect
// TODO: This is a hacky solution, better use DrawRing()
- if (showDonut) DrawCircle(center.x, center.y, donutInnerRadius, RAYWHITE);
+ if (showDonut) DrawCircleV(center, donutInnerRadius, RAYWHITE);
startAngle += sweepAngle;
}
diff --git a/examples/shapes/shapes_starfield_effect.c b/examples/shapes/shapes_starfield_effect.c
index ca409c926..484f4fb78 100644
--- a/examples/shapes/shapes_starfield_effect.c
+++ b/examples/shapes/shapes_starfield_effect.c
@@ -47,8 +47,8 @@ int main(void)
// Setup the stars with a random position
for (int i = 0; i < STAR_COUNT; i++)
{
- stars[i].x = GetRandomValue(-screenWidth*0.5f, screenWidth*0.5f);
- stars[i].y = GetRandomValue(-screenHeight*0.5f, screenHeight*0.5f);
+ stars[i].x = (float)GetRandomValue(-screenWidth / 2, (int)screenWidth / 2);
+ stars[i].y = (float)GetRandomValue(-screenHeight / 2, (int)screenHeight / 2);
stars[i].z = 1.0f;
}
@@ -85,8 +85,8 @@ int main(void)
if ((stars[i].z < 0.0f) || (starsScreenPos[i].x < 0) || (starsScreenPos[i].y < 0.0f) ||
(starsScreenPos[i].x > screenWidth) || (starsScreenPos[i].y > screenHeight))
{
- stars[i].x = GetRandomValue(-screenWidth*0.5f, screenWidth*0.5f);
- stars[i].y = GetRandomValue(-screenHeight*0.5f, screenHeight*0.5f);
+ stars[i].x = (float)GetRandomValue(-screenWidth / 2, screenWidth / 2);
+ stars[i].y = (float)GetRandomValue(-screenHeight / 2, screenHeight / 2);
stars[i].z = 1.0f;
}
}
diff --git a/examples/text/text_words_alignment.c b/examples/text/text_words_alignment.c
index f7ffa74af..8017f33eb 100644
--- a/examples/text/text_words_alignment.c
+++ b/examples/text/text_words_alignment.c
@@ -109,7 +109,7 @@ int main(void)
DrawRectangleRec(textContainerRect, BLUE);
// Get the size of the text to draw
- Vector2 textSize = MeasureTextEx(font, words[wordIndex], fontSize, fontSize*.1f);
+ Vector2 textSize = MeasureTextEx(font, words[wordIndex], (float)fontSize, fontSize*.1f);
// Calculate the top-left text position based on the rectangle and alignment
Vector2 textPos = (Vector2){
@@ -118,7 +118,7 @@ int main(void)
};
// Draw the text
- DrawTextEx(font, words[wordIndex], textPos, fontSize, fontSize*.1f, RAYWHITE);
+ DrawTextEx(font, words[wordIndex], textPos, (float)fontSize, fontSize*.1f, RAYWHITE);
EndDrawing();
//----------------------------------------------------------------------------------
diff --git a/examples/textures/textures_bunnymark.c b/examples/textures/textures_bunnymark.c
index 3279abb8f..a04741227 100644
--- a/examples/textures/textures_bunnymark.c
+++ b/examples/textures/textures_bunnymark.c
@@ -17,7 +17,7 @@
#include // Required for: malloc(), free()
-#define MAX_BUNNIES 50000 // 50K bunnies limit
+#define MAX_BUNNIES 80000 // 80K bunnies limit
// This is the maximum amount of elements (quads) per batch
// NOTE: This value is defined in [rlgl] module and can be changed there
@@ -51,7 +51,9 @@ int main(void)
int bunniesCount = 0; // Bunnies counter
- SetTargetFPS(60); // Set our game to run at 60 frames-per-second
+ bool paused = false;
+
+ //SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
// Main game loop
@@ -67,8 +69,8 @@ int main(void)
if (bunniesCount < MAX_BUNNIES)
{
bunnies[bunniesCount].position = GetMousePosition();
- bunnies[bunniesCount].speed.x = (float)GetRandomValue(-250, 250)/60.0f;
- bunnies[bunniesCount].speed.y = (float)GetRandomValue(-250, 250)/60.0f;
+ bunnies[bunniesCount].speed.x = (float)GetRandomValue(-250, 250);
+ bunnies[bunniesCount].speed.y = (float)GetRandomValue(-250, 250);
bunnies[bunniesCount].color = (Color){ GetRandomValue(50, 240),
GetRandomValue(80, 240),
GetRandomValue(100, 240), 255 };
@@ -77,16 +79,21 @@ int main(void)
}
}
- // Update bunnies
- for (int i = 0; i < bunniesCount; i++)
- {
- bunnies[i].position.x += bunnies[i].speed.x;
- bunnies[i].position.y += bunnies[i].speed.y;
+ if (IsKeyPressed(KEY_P)) paused = !paused;
- if (((bunnies[i].position.x + (float)texBunny.width/2) > GetScreenWidth()) ||
- ((bunnies[i].position.x + (float)texBunny.width/2) < 0)) bunnies[i].speed.x *= -1;
- if (((bunnies[i].position.y + (float)texBunny.height/2) > GetScreenHeight()) ||
- ((bunnies[i].position.y + (float)texBunny.height/2 - 40) < 0)) bunnies[i].speed.y *= -1;
+ if (!paused)
+ {
+ // Update bunnies
+ for (int i = 0; i < bunniesCount; i++)
+ {
+ bunnies[i].position.x += bunnies[i].speed.x*GetFrameTime();
+ bunnies[i].position.y += bunnies[i].speed.y*GetFrameTime();
+
+ if (((bunnies[i].position.x + (float)texBunny.width/2) > GetScreenWidth()) ||
+ ((bunnies[i].position.x + (float)texBunny.width/2) < 0)) bunnies[i].speed.x *= -1;
+ if (((bunnies[i].position.y + (float)texBunny.height/2) > GetScreenHeight()) ||
+ ((bunnies[i].position.y + (float)texBunny.height/2 - 40) < 0)) bunnies[i].speed.y *= -1;
+ }
}
//----------------------------------------------------------------------------------
diff --git a/examples/textures/textures_clipboard_image.c b/examples/textures/textures_clipboard_image.c
new file mode 100644
index 000000000..aa955da2e
--- /dev/null
+++ b/examples/textures/textures_clipboard_image.c
@@ -0,0 +1,110 @@
+/*******************************************************************************************
+*
+* raylib [textures] example - clipboard image
+*
+* Example complexity rating: [★☆☆☆] 1/4
+*
+* Example originally created with raylib 6.0, last time updated with raylib 6.0
+*
+* Example contributed by Maicon Santana (@maiconpintoabreu) and reviewed by Ramon Santamaria (@raysan5)
+*
+* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+* BSD-like license that allows static linking with closed source software
+*
+* Copyright (c) 2026 Maicon Santana (@maiconpintoabreu)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#define MAX_TEXTURE_COLLECTION 20
+
+typedef struct TextureCollection {
+ Texture2D texture;
+ Vector2 position;
+} TextureCollection;
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ InitWindow(screenWidth, screenHeight, "raylib [textures] example - clipboard_image");
+
+ TextureCollection collection[MAX_TEXTURE_COLLECTION] = { 0 };
+ int currentCollectionIndex = 0;
+
+ SetTargetFPS(60);
+ //--------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ if (IsKeyPressed(KEY_R)) // Reset image collection
+ {
+ // Unload textures to avoid memory leaks
+ for (int i = 0; i < MAX_TEXTURE_COLLECTION; i++) UnloadTexture(collection[i].texture);
+
+ currentCollectionIndex = 0;
+ }
+
+ if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_V) &&
+ (currentCollectionIndex < MAX_TEXTURE_COLLECTION))
+ {
+ Image image = GetClipboardImage();
+
+ if (IsImageValid(image))
+ {
+ collection[currentCollectionIndex].texture = LoadTextureFromImage(image);
+ collection[currentCollectionIndex].position = GetMousePosition();
+ currentCollectionIndex++;
+ UnloadImage(image);
+ }
+ else TraceLog(LOG_INFO, "IMAGE: Could not retrieve image from clipboard");
+ }
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground(RAYWHITE);
+
+ for (int i = 0; i < currentCollectionIndex; i++)
+ {
+ if (IsTextureValid(collection[i].texture))
+ {
+ DrawTexturePro(collection[i].texture,
+ (Rectangle){0,0,collection[i].texture.width, collection[i].texture.height},
+ (Rectangle){collection[i].position.x,collection[i].position.y,collection[i].texture.width, collection[i].texture.height},
+ (Vector2){collection[i].texture.width*0.5f, collection[i].texture.height*0.5f},
+ 0.0f, WHITE);
+ }
+ }
+
+ DrawRectangle(0, 0, screenWidth, 40, BLACK);
+ DrawText("Clipboard Image - Ctrl+V to Paste and R to Reset ", 120, 10, 20, LIGHTGRAY);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ for (int i = 0; i < MAX_TEXTURE_COLLECTION;i ++)
+ {
+ UnloadTexture(collection[i].texture);
+ }
+
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
diff --git a/examples/textures/textures_clipboard_image.png b/examples/textures/textures_clipboard_image.png
new file mode 100644
index 000000000..5aed6fa2c
Binary files /dev/null and b/examples/textures/textures_clipboard_image.png differ
diff --git a/examples/textures/textures_framebuffer_rendering.c b/examples/textures/textures_framebuffer_rendering.c
index 097ae1f2f..84ecce0ac 100644
--- a/examples/textures/textures_framebuffer_rendering.c
+++ b/examples/textures/textures_framebuffer_rendering.c
@@ -65,7 +65,7 @@ int main(void)
// Rectangles for cropping render texture
const float captureSize = 128.0f;
Rectangle cropSource = { (subjectTarget.texture.width - captureSize)/2.0f, (subjectTarget.texture.height - captureSize)/2.0f, captureSize, -captureSize };
- Rectangle cropDest = { splitWidth + 20, 20, captureSize, captureSize};
+ Rectangle cropDest = { splitWidth + 20.0f, 20.0f, captureSize, captureSize};
SetTargetFPS(60);
DisableCursor();
@@ -115,7 +115,7 @@ int main(void)
EndMode3D();
- DrawRectangleLines((subjectTarget.texture.width - captureSize)/2, (subjectTarget.texture.height - captureSize)/2, captureSize, captureSize, GREEN);
+ DrawRectangleLines((int)((subjectTarget.texture.width - captureSize)/2.0f), (int)((subjectTarget.texture.height - captureSize)/2.0f), captureSize, captureSize, GREEN);
DrawText("Subject View", 10, subjectTarget.texture.height - 30, 20, BLACK);
EndTextureMode();
diff --git a/examples/textures/textures_magnifying_glass.c b/examples/textures/textures_magnifying_glass.c
new file mode 100644
index 000000000..12cd0cdee
--- /dev/null
+++ b/examples/textures/textures_magnifying_glass.c
@@ -0,0 +1,134 @@
+/*******************************************************************************************
+*
+* raylib textures example - magnifying glass
+*
+* Example complexity rating: [★★★☆] 3/4
+*
+* Example originally created with raylib 5.6, last time updated with raylib 5.6
+*
+* Example contributed by Luke Vaughan (@badram) and reviewed by Ramon Santamaria (@raysan5)
+*
+* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+* BSD-like license that allows static linking with closed source software
+*
+* Copyright (c) 2026 Luke Vaughan (@badram)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include "rlgl.h" // for rlSetBlendFactorsSeparate()
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ InitWindow(screenWidth, screenHeight, "raylib [textures] example - magnifying glass");
+
+ Texture2D bunny = LoadTexture("resources/raybunny.png");
+ Texture2D parrots = LoadTexture("resources/parrots.png");
+
+ // Use image draw to generate a mask texture instead of loading it from a file.
+ Image circle = GenImageColor(256, 256, BLANK);
+ ImageDrawCircle(&circle, 128, 128, 128, WHITE);
+ Texture2D mask = LoadTextureFromImage(circle); // Copy the mask image from RAM to VRAM
+ UnloadImage(circle); // Unload the image from RAM
+
+ RenderTexture2D magnifiedWorld = LoadRenderTexture(256, 256);
+
+ Camera2D camera = { 0 };
+ // Set magnifying glass zoom
+ camera.zoom = 2;
+ // Offset by half the size of the magnifying glass to counteract drawing the texture centered on the mouse position
+ camera.offset = (Vector2){128, 128};
+
+
+ SetTargetFPS(60);
+ //--------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ Vector2 mPos = GetMousePosition();
+ camera.target = mPos;
+
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground(RAYWHITE);
+
+ // Draw the normal version of the world
+ DrawTexture(parrots, 144, 33, WHITE);
+ DrawText("Use the magnifying glass to find hidden bunnies!", 154, 6, 20, BLACK);
+
+ // Render to a the magnifying glass
+ BeginTextureMode(magnifiedWorld);
+ ClearBackground(RAYWHITE);
+
+ BeginMode2D(camera);
+ // Draw the same things in the magnified world as were in the normal version
+ DrawTexture(parrots, 144, 33, WHITE);
+ DrawText("Use the magnifying glass to find hidden bunnies!", 154, 6, 20, BLACK);
+
+ // Draw bunnies only in the magnified world.
+ // BLEND_MULTIPLIED lets them take on the color of the image below them.
+ BeginBlendMode(BLEND_MULTIPLIED);
+ DrawTexture(bunny, 250, 350, WHITE);
+ DrawTexture(bunny, 500, 100, WHITE);
+ DrawTexture(bunny, 420, 300, WHITE);
+ DrawTexture(bunny, 650, 10, WHITE);
+ EndBlendMode();
+ EndMode2D();
+
+ // Mask the magnifying glass view texture to a circle
+ // To make the mask affect only alpha, a CUSTOM blend mode is used with SEPARATE color/alpha functions
+ BeginBlendMode(BLEND_CUSTOM_SEPARATE);
+ // C: Color, A: Alpha, s: source (texture to draw), d: destination (texture drawn to)
+ // glSrcRGB: RL_ZERO - Cs * 0 = 0 - discard source rgb because we don't want to draw our texture's colors at all
+ // glDstRGB: RL_ONE - Cd * 1 = Cd - use destination colors unmodified
+ // glSrcAlpha: RL_ONE - As * 1 = As - use source alpha unmodified
+ // glDstAlpha: RL_ZERO - Ad * 0 = 0 - discard destination alpha
+ // glEqRGB: RL_FUNC_ADD - Cs(0) + Cd = Cd - destination color is unmodified
+ // glEqAlpha: RL_FUNC_ADD - As + Ad(0) = As - destination alpha is set to source alpha
+ rlSetBlendFactorsSeparate(RL_ZERO, RL_ONE, RL_ONE, RL_ZERO, RL_FUNC_ADD, RL_FUNC_ADD);
+ DrawTexture(mask, 0, 0, WHITE);
+ EndBlendMode();
+ EndTextureMode();
+
+ // Draw magnifiedWorld to screen, centered on cursor
+ DrawTextureRec(magnifiedWorld.texture, (Rectangle){0, 0, 256, -256}, (Vector2){mPos.x - 128, mPos.y - 128}, WHITE);
+
+ // Draw the outer ring of the magnifying glass
+ DrawRing(mPos, 126, 130, 0, 360, 64, BLACK);
+
+ // Draw floating specular highlight on the glass
+ float rx = mPos.x/800;
+ float ry = mPos.y/800;
+ DrawCircle((int)(mPos.x - 64*rx) - 32, (int)(mPos.y - 64*ry) - 32, 4, ColorAlpha(WHITE, 0.5));
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ UnloadTexture(parrots);
+ UnloadTexture(bunny);
+ UnloadTexture(mask);
+ UnloadRenderTexture(magnifiedWorld);
+
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
diff --git a/examples/textures/textures_magnifying_glass.png b/examples/textures/textures_magnifying_glass.png
new file mode 100644
index 000000000..bc8b02aa3
Binary files /dev/null and b/examples/textures/textures_magnifying_glass.png differ
diff --git a/projects/VS2022/examples/core_compute_hash.vcxproj b/projects/VS2022/examples/core_compute_hash.vcxproj
index bbca36d86..28255f522 100644
--- a/projects/VS2022/examples/core_compute_hash.vcxproj
+++ b/projects/VS2022/examples/core_compute_hash.vcxproj
@@ -292,7 +292,7 @@
Level3
Disabled
- WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
@@ -309,7 +309,7 @@
Level3
Disabled
- WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
/FS %(AdditionalOptions)
@@ -345,7 +345,7 @@
Level3
Disabled
- WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
@@ -366,7 +366,7 @@
Level3
Disabled
- WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
@@ -410,7 +410,7 @@
MaxSpeed
true
true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
CompileAsC
true
@@ -432,7 +432,7 @@
MaxSpeed
true
true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
CompileAsC
true
@@ -476,7 +476,7 @@
MaxSpeed
true
true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
CompileAsC
true
@@ -504,7 +504,7 @@
MaxSpeed
true
true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
CompileAsC
true
diff --git a/projects/VS2022/examples/core_directory_files.vcxproj b/projects/VS2022/examples/core_directory_files.vcxproj
index 426fbfca8..f20797cff 100644
--- a/projects/VS2022/examples/core_directory_files.vcxproj
+++ b/projects/VS2022/examples/core_directory_files.vcxproj
@@ -292,7 +292,7 @@
Level3
Disabled
- WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
@@ -309,7 +309,7 @@
Level3
Disabled
- WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
/FS %(AdditionalOptions)
@@ -345,7 +345,7 @@
Level3
Disabled
- WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
@@ -366,7 +366,7 @@
Level3
Disabled
- WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
@@ -410,7 +410,7 @@
MaxSpeed
true
true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
CompileAsC
true
@@ -432,7 +432,7 @@
MaxSpeed
true
true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
CompileAsC
true
@@ -476,7 +476,7 @@
MaxSpeed
true
true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
CompileAsC
true
@@ -504,7 +504,7 @@
MaxSpeed
true
true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
CompileAsC
true
diff --git a/projects/VS2022/examples/models_animation_bone_blending.vcxproj b/projects/VS2022/examples/models_animation_blend_custom.vcxproj
similarity index 99%
rename from projects/VS2022/examples/models_animation_bone_blending.vcxproj
rename to projects/VS2022/examples/models_animation_blend_custom.vcxproj
index 475ad47f0..32fdf506b 100644
--- a/projects/VS2022/examples/models_animation_bone_blending.vcxproj
+++ b/projects/VS2022/examples/models_animation_blend_custom.vcxproj
@@ -53,9 +53,9 @@
{AC751FE1-C986-4B6A-92A8-28ED89DEE671}
Win32Proj
- models_animation_bone_blending
+ models_animation_blend_custom
10.0
- models_animation_bone_blending
+ models_animation_blend_custom
@@ -553,7 +553,7 @@
-
+
diff --git a/projects/VS2022/examples/models_animation_blending.vcxproj b/projects/VS2022/examples/models_animation_blending.vcxproj
index 3eca7a6f2..9ba155d6b 100644
--- a/projects/VS2022/examples/models_animation_blending.vcxproj
+++ b/projects/VS2022/examples/models_animation_blending.vcxproj
@@ -202,7 +202,7 @@
Level3
Disabled
- WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
@@ -219,7 +219,7 @@
Level3
Disabled
- WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
/FS %(AdditionalOptions)
@@ -237,7 +237,7 @@
Level3
Disabled
- WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
@@ -258,7 +258,7 @@
Level3
Disabled
- WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
@@ -281,7 +281,7 @@
MaxSpeed
true
true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
CompileAsC
true
@@ -303,7 +303,7 @@
MaxSpeed
true
true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
CompileAsC
true
@@ -325,7 +325,7 @@
MaxSpeed
true
true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
CompileAsC
true
@@ -353,7 +353,7 @@
MaxSpeed
true
true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
CompileAsC
true
@@ -381,6 +381,9 @@
{e89d61ac-55de-4482-afd4-df7242ebc859}
+
+
+
diff --git a/projects/VS2022/examples/models_animation_timing.vcxproj b/projects/VS2022/examples/models_animation_timing.vcxproj
new file mode 100644
index 000000000..339f5b936
--- /dev/null
+++ b/projects/VS2022/examples/models_animation_timing.vcxproj
@@ -0,0 +1,569 @@
+
+
+
+
+ Debug.DLL
+ ARM64
+
+
+ Debug.DLL
+ Win32
+
+
+ Debug.DLL
+ x64
+
+
+ Debug
+ ARM64
+
+
+ Debug
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release.DLL
+ ARM64
+
+
+ Release.DLL
+ Win32
+
+
+ Release.DLL
+ x64
+
+
+ Release
+ ARM64
+
+
+ Release
+ Win32
+
+
+ Release
+ x64
+
+
+
+ {89D5A0E9-683C-465C-BF85-A880865175C8}
+ Win32Proj
+ models_animation_timming
+ 10.0
+ models_animation_timing
+
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\models
+ WindowsLocalDebugger
+
+
+
+
+
+ Level3
+ Disabled
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+ Level3
+ Disabled
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ /FS %(AdditionalOptions)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ /FS %(AdditionalOptions)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+ Level3
+ Disabled
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+ Copy Debug DLL to output directory
+
+
+
+
+
+
+ Level3
+ Disabled
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+ Copy Debug DLL to output directory
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+ Copy Debug DLL to output directory
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+
+
+ Copy Release DLL to output directory
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+
+
+ Copy Release DLL to output directory
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+
+
+ Copy Release DLL to output directory
+
+
+
+
+
+
+
+
+
+
+ {e89d61ac-55de-4482-afd4-df7242ebc859}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/projects/VS2022/examples/models_animation_playing.vcxproj b/projects/VS2022/examples/models_loading_iqm.vcxproj
similarity index 99%
rename from projects/VS2022/examples/models_animation_playing.vcxproj
rename to projects/VS2022/examples/models_loading_iqm.vcxproj
index 292c45fb6..b5364fc37 100644
--- a/projects/VS2022/examples/models_animation_playing.vcxproj
+++ b/projects/VS2022/examples/models_loading_iqm.vcxproj
@@ -53,9 +53,9 @@
{AFDDE100-2D36-4749-817D-12E54C56312F}
Win32Proj
- models_animation_playing
+ models_loading_iqm
10.0
- models_animation_playing
+ models_loading_iqm
@@ -553,7 +553,7 @@
-
+
diff --git a/projects/VS2022/examples/shapes_pie_chart.vcxproj b/projects/VS2022/examples/shapes_pie_chart.vcxproj
index ad1cf93d1..ba88b28f7 100644
--- a/projects/VS2022/examples/shapes_pie_chart.vcxproj
+++ b/projects/VS2022/examples/shapes_pie_chart.vcxproj
@@ -292,7 +292,7 @@
Level3
Disabled
- WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
@@ -309,7 +309,7 @@
Level3
Disabled
- WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
/FS %(AdditionalOptions)
@@ -345,7 +345,7 @@
Level3
Disabled
- WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
@@ -366,7 +366,7 @@
Level3
Disabled
- WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
@@ -410,7 +410,7 @@
MaxSpeed
true
true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
CompileAsC
true
@@ -432,7 +432,7 @@
MaxSpeed
true
true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
CompileAsC
true
@@ -476,7 +476,7 @@
MaxSpeed
true
true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
CompileAsC
true
@@ -504,7 +504,7 @@
MaxSpeed
true
true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
CompileAsC
true
diff --git a/projects/VS2022/examples/shapes_rlgl_triangle.vcxproj b/projects/VS2022/examples/shapes_rlgl_triangle.vcxproj
index 780f514a0..3caacc139 100644
--- a/projects/VS2022/examples/shapes_rlgl_triangle.vcxproj
+++ b/projects/VS2022/examples/shapes_rlgl_triangle.vcxproj
@@ -292,7 +292,7 @@
Level3
Disabled
- WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
@@ -309,7 +309,7 @@
Level3
Disabled
- WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
/FS %(AdditionalOptions)
@@ -345,7 +345,7 @@
Level3
Disabled
- WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
@@ -366,7 +366,7 @@
Level3
Disabled
- WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
CompileAsC
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
@@ -410,7 +410,7 @@
MaxSpeed
true
true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
CompileAsC
true
@@ -432,7 +432,7 @@
MaxSpeed
true
true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
CompileAsC
true
@@ -476,7 +476,7 @@
MaxSpeed
true
true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
CompileAsC
true
@@ -504,7 +504,7 @@
MaxSpeed
true
true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
$(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
CompileAsC
true
diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln
index 873db235b..51782892c 100644
--- a/projects/VS2022/raylib.sln
+++ b/projects/VS2022/raylib.sln
@@ -83,7 +83,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_window_letterbox", "ex
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_world_screen", "examples\core_world_screen.vcxproj", "{79417CE2-FEEB-42F0-BC53-62D5267B19B1}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_playing", "examples\models_animation_playing.vcxproj", "{AFDDE100-2D36-4749-817D-12E54C56312F}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_loading_iqm", "examples\models_loading_iqm.vcxproj", "{AFDDE100-2D36-4749-817D-12E54C56312F}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_billboard_rendering", "examples\models_billboard_rendering.vcxproj", "{B7812167-50FB-4934-996F-DF6FE4CBBFDF}"
EndProject
@@ -429,12 +429,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shapes_easings_testbed", "e
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_rlgl_compute", "examples\shaders_rlgl_compute.vcxproj", "{AEA9D0D4-B810-4624-BC75-10A291584ED6}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_bone_blending", "examples\models_animation_bone_blending.vcxproj", "{AC751FE1-C986-4B6A-92A8-28ED89DEE671}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_blend_custom", "examples\models_animation_blend_custom.vcxproj", "{AC751FE1-C986-4B6A-92A8-28ED89DEE671}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_blending", "examples\models_animation_blending.vcxproj", "{BB9C957D-34F1-46AE-B64A-9E0499C1746D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_window_web", "examples\core_window_web.vcxproj", "{4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_animation_timing", "examples\models_animation_timing.vcxproj", "{89D5A0E9-683C-465C-BF85-A880865175C8}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug.DLL|ARM64 = Debug.DLL|ARM64
@@ -5443,6 +5445,30 @@ Global
{4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Release|x64.Build.0 = Release|x64
{4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Release|x86.ActiveCfg = Release|Win32
{4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3}.Release|x86.Build.0 = Release|Win32
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug.DLL|x64.Build.0 = Debug.DLL|x64
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug|ARM64.Build.0 = Debug|ARM64
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug|x64.ActiveCfg = Debug|x64
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug|x64.Build.0 = Debug|x64
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug|x86.ActiveCfg = Debug|Win32
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Debug|x86.Build.0 = Debug|Win32
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Release.DLL|x64.ActiveCfg = Release.DLL|x64
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Release.DLL|x64.Build.0 = Release.DLL|x64
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Release.DLL|x86.Build.0 = Release.DLL|Win32
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Release|ARM64.ActiveCfg = Release|ARM64
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Release|ARM64.Build.0 = Release|ARM64
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Release|x64.ActiveCfg = Release|x64
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Release|x64.Build.0 = Release|x64
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Release|x86.ActiveCfg = Release|Win32
+ {89D5A0E9-683C-465C-BF85-A880865175C8}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -5619,7 +5645,7 @@ Global
{52BA9067-A5FC-4CE8-82AD-7204ECFDEF9F} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C}
{8E132D5A-2C00-48D0-8747-97E41356F26F} = {278D8859-20B1-428F-8448-064F46E1F021}
{A4662163-83E7-4309-8CAA-B0BF13655FE6} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C}
- {5F4B766F-DD52-4B53-B6C3-BC7611E17F20} = {278D8859-20B1-428F-8448-064F46E1F021}
+ {5F4B766F-DD52-4B53-B6C3-BC7611E17F20} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9}
{FF5F9EE9-29C5-40EE-BBCF-AE51B001FEC3} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035}
{0C442799-B09C-4CD1-9538-711B6E85E9BF} = {278D8859-20B1-428F-8448-064F46E1F021}
{DFB40A10-F8B7-412A-BCC3-5EE49294D816} = {278D8859-20B1-428F-8448-064F46E1F021}
@@ -5662,6 +5688,7 @@ Global
{AC751FE1-C986-4B6A-92A8-28ED89DEE671} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C}
{BB9C957D-34F1-46AE-B64A-9E0499C1746D} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C}
{4E7157E0-6CDB-47AE-A19A-FEC3876FA8A3} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035}
+ {89D5A0E9-683C-465C-BF85-A880865175C8} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29}
diff --git a/projects/VSCode/Makefile b/projects/VSCode/Makefile
index 389a12cf9..219673670 100644
--- a/projects/VSCode/Makefile
+++ b/projects/VSCode/Makefile
@@ -343,7 +343,7 @@ ifeq ($(PLATFORM),PLATFORM_RPI)
endif
ifeq ($(PLATFORM),PLATFORM_WEB)
# Libraries for web (HTML5) compiling
- LDLIBS = $(RAYLIB_RELEASE_PATH)/libraylib.a
+ LDLIBS = $(RAYLIB_RELEASE_PATH)/libraylib.web.a
endif
# Define a recursive wildcard function
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 76c985969..5e5dd91ee 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,7 +1,7 @@
# Setup the project and settings
project(raylib C)
-set(PROJECT_VERSION 5.5.0)
-set(API_VERSION 550)
+set(PROJECT_VERSION 6.0.0)
+set(API_VERSION 600)
include(GNUInstallDirs)
include(JoinPaths)
@@ -75,6 +75,12 @@ if (${PLATFORM} MATCHES "Web")
endif()
endif()
+if (${PLATFORM} MATCHES "Android")
+ # Wrap fopen at link time so all code (including third-party libs) goes
+ # through __wrap_fopen, which handles Android APK asset loading
+ target_link_options(raylib PUBLIC -Wl,--wrap=fopen)
+endif()
+
set_target_properties(raylib PROPERTIES
PUBLIC_HEADER "${raylib_public_headers}"
VERSION ${PROJECT_VERSION}
@@ -111,6 +117,21 @@ target_include_directories(raylib
# Copy the header files to the build directory for convenience
file(COPY ${raylib_public_headers} DESTINATION "include")
+if (PLATFORM STREQUAL "Desktop")
+ if (UNIX AND NOT APPLE)
+ if (GLFW_BUILD_X11)
+ find_package(X11 REQUIRED)
+
+ target_compile_definitions(raylib PRIVATE _GLFW_X11)
+
+ target_link_libraries(raylib PRIVATE
+ ${X11_LIBRARIES}
+ )
+
+ message(STATUS "X11 support enabled for raylib")
+ endif()
+ endif()
+endif()
# Includes information on how the library will be installed on the system
# when cmake --install is run
diff --git a/src/Makefile b/src/Makefile
index b4ea8f0a9..e53514701 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -20,15 +20,22 @@
# - Linux (X11 desktop mode)
# - macOS/OSX (x64, arm64 (not tested))
# - Others (not tested)
-# > PLATFORM_WEB_RGFW:
+# > PLATFORM_DESKTOP_WIN32 (native Win32):
+# - Windows (Win32, Win64)
+# > PLATFORM_WEB (GLFW + Emscripten):
# - HTML5 (WebAssembly)
-# > PLATFORM_WEB:
+# > PLATFORM_WEB_EMSCRIPTEN (Emscripten):
# - HTML5 (WebAssembly)
-# > PLATFORM_DRM:
+# > PLATFORM_WEB_RGFW (Emscripten):
+# - HTML5 (WebAssembly)
+# > PLATFORM_DRM (native DRM):
# - Raspberry Pi 0-5 (DRM/KMS)
# - Linux DRM subsystem (KMS mode)
-# > PLATFORM_ANDROID:
+# - Embedded devices (with GPU)
+# > PLATFORM_ANDROID (native NDK):
# - Android (ARM, ARM64)
+# > PLATFORM_MEMORY
+# - Memory framebuffer output, using software renderer, no OS required
#
# Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline.
# Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline.
@@ -68,8 +75,8 @@ else
endif
# Define required raylib variables
-RAYLIB_VERSION = 5.5.0
-RAYLIB_API_VERSION = 550
+RAYLIB_VERSION = 6.0.0
+RAYLIB_API_VERSION = 600
# Define raylib source code path
RAYLIB_SRC_PATH ?= ../src
@@ -115,6 +122,11 @@ USE_EXTERNAL_GLFW ?= FALSE
GLFW_LINUX_ENABLE_WAYLAND ?= FALSE
GLFW_LINUX_ENABLE_X11 ?= TRUE
+# Enable support for X11 by default on Linux when using GLFW
+# NOTE: Wayland is disabled by default, only enable if you are sure
+RGFW_LINUX_ENABLE_WAYLAND ?= FALSE
+RGFW_LINUX_ENABLE_X11 ?= TRUE
+
# PLATFORM_DESKTOP_SDL: It requires SDL library to be provided externally
# WARNING: Library is not included in raylib, it MUST be configured by users
SDL_INCLUDE_PATH ?= $(RAYLIB_SRC_PATH)/external/SDL2/include
@@ -289,6 +301,17 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW)
CC = clang
endif
endif
+ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW)
+ ifeq ($(PLATFORM_OS),OSX)
+ # OSX default compiler
+ CC = clang
+ GLFW_OSX = -x objective-c
+ endif
+ ifeq ($(PLATFORM_OS),BSD)
+ # FreeBSD, OpenBSD, NetBSD, DragonFly default compiler
+ CC = clang
+ endif
+endif
ifeq ($(TARGET_PLATFORM),PLATFORM_DRM)
ifeq ($(USE_RPI_CROSSCOMPILER),TRUE)
# Define RPI cross-compiler
@@ -450,7 +473,6 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW)
endif
ifeq ($(GLFW_LINUX_ENABLE_WAYLAND),TRUE)
CFLAGS += -D_GLFW_WAYLAND
- LDFLAGS += $(shell pkg-config wayland-client wayland-cursor wayland-egl xkbcommon --libs)
WL_PROTOCOLS_DIR := external/glfw/deps/wayland
@@ -472,6 +494,33 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW)
endif
endif
endif
+# Use Wayland display on Linux desktop
+ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW)
+ ifeq ($(PLATFORM_OS), LINUX)
+ ifeq ($(RGFW_LINUX_ENABLE_X11),TRUE)
+ CFLAGS += -DRGFW_X11 -DRGFW_UNIX
+ endif
+ ifeq ($(RGFW_LINUX_ENABLE_WAYLAND),TRUE)
+ CFLAGS += -DRGFW_WAYLAND -DEGLAPIENTRY=
+
+ WL_PROTOCOLS_DIR := external/RGFW/deps/wayland
+
+ wl_generate = \
+ $(eval protocol=$(1)) \
+ $(eval basename=$(2)) \
+ $(shell wayland-scanner client-header $(protocol) $(RAYLIB_SRC_PATH)/$(basename).h) \
+ $(shell wayland-scanner private-code $(protocol) $(RAYLIB_SRC_PATH)/$(basename)-code.c)
+
+ $(call wl_generate, $(WL_PROTOCOLS_DIR)/pointer-constraints-unstable-v1.xml, pointer-constraints-unstable-v1-client-protocol)
+ $(call wl_generate, $(WL_PROTOCOLS_DIR)/pointer-warp-v1.xml, pointer-warp-v1-client-protocol)
+ $(call wl_generate, $(WL_PROTOCOLS_DIR)/relative-pointer-unstable-v1.xml, relative-pointer-unstable-v1-client-protocol)
+ $(call wl_generate, $(WL_PROTOCOLS_DIR)/xdg-decoration-unstable-v1.xml, xdg-decoration-unstable-v1-client-protocol)
+ $(call wl_generate, $(WL_PROTOCOLS_DIR)/xdg-output-unstable-v1.xml, xdg-output-unstable-v1-client-protocol)
+ $(call wl_generate, $(WL_PROTOCOLS_DIR)/xdg-shell.xml, xdg-shell-client-protocol)
+ $(call wl_generate, $(WL_PROTOCOLS_DIR)/xdg-toplevel-icon-v1.xml, xdg-toplevel-icon-v1-client-protocol)
+ endif
+ endif
+endif
CFLAGS += $(CUSTOM_CFLAGS)
@@ -565,6 +614,9 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID)
LDFLAGS += -Lsrc
# Avoid unresolved symbol pointing to external main()
LDFLAGS += -Wl,-undefined,dynamic_lookup
+ # Wrap fopen at link time so all code (including third-party libs) goes
+ # through __wrap_fopen, which handles Android APK asset loading
+ LDFLAGS += -Wl,--wrap=fopen
endif
# Define libraries required on linking: LDLIBS
@@ -583,6 +635,10 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW)
ifeq ($(GLFW_LINUX_ENABLE_X11),TRUE)
LDLIBS += -lX11
endif
+
+ ifeq ($(GLFW_LINUX_ENABLE_WAYLAND),TRUE)
+ LDFLAGS += $(shell pkg-config wayland-client wayland-cursor wayland-egl xkbcommon --libs)
+ endif
# TODO: On ARM 32bit arch, miniaudio requires atomics library
#LDLIBS += -latomic
endif
@@ -618,7 +674,15 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW)
ifeq ($(PLATFORM_OS),LINUX)
# Libraries for Debian GNU/Linux desktop compipling
# NOTE: Required packages: libegl1-mesa-dev
- LDLIBS = -lGL -lX11 -lXrandr -lXinerama -lXi -lXcursor -lm -lpthread -ldl -lrt
+ LDLIBS = -lm
+
+ ifeq ($(RGFW_LINUX_ENABLE_X11),TRUE)
+ LDLIBS += -lGL -lX11 -lXrandr -lXinerama -lXi -lXcursor -lpthread -ldl -lrt
+ endif
+
+ ifeq ($(RGFW_LINUX_ENABLE_WAYLAND),TRUE)
+ LDFLAGS += $(shell pkg-config wayland-client wayland-cursor wayland-egl xkbcommon --libs)
+ endif
# Explicit link to libc
ifeq ($(RAYLIB_LIBTYPE),SHARED)
@@ -665,6 +729,21 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW)
OBJS += rglfw.o
endif
endif
+ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW)
+ ifeq ($(RGFW_LINUX_ENABLE_WAYLAND),TRUE)
+ PROTOCOL_CODE_FILES = \
+ pointer-constraints-unstable-v1-client-protocol-code \
+ pointer-warp-v1-client-protocol-code \
+ relative-pointer-unstable-v1-client-protocol-code \
+ xdg-decoration-unstable-v1-client-protocol-code \
+ xdg-output-unstable-v1-client-protocol-code \
+ xdg-shell-client-protocol-code \
+ xdg-toplevel-icon-v1-client-protocol-code
+
+ PROTO_OBJS = $(addsuffix .o, $(PROTOCOL_CODE_FILES))
+ OBJS += $(PROTO_OBJS)
+ endif
+endif
ifeq ($(RAYLIB_MODULE_MODELS),TRUE)
OBJS += rmodels.o
endif
@@ -801,6 +880,17 @@ endif
android_native_app_glue.o : $(NATIVE_APP_GLUE)/android_native_app_glue.c
$(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS)
+# Compile Wayland files
+ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW)
+ ifeq ($(RGFW_LINUX_ENABLE_WAYLAND),TRUE)
+ %-client-protocol-code.o: %-client-protocol-code.c
+ $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS)
+
+ %-client-protocol-code.h: external/RGFW/deps/wayland/%.xml
+ wayland-scanner private-code $< $@
+ endif
+endif
+
# Install generated and needed files to desired directories.
# On GNU/Linux and BSDs, there are some standard directories that contain extra
# libraries and header files. These directories (often /usr/local/lib and
@@ -885,7 +975,7 @@ clean: clean_shell_$(PLATFORM_SHELL)
@echo "removed all generated files!"
clean_shell_sh:
- rm -fv *.o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).web.a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so* raygui.c $(RAYLIB_RELEASE_PATH)/*-protocol.h $(RAYLIB_RELEASE_PATH)/*-protocol-code.h
+ rm -fv *.o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).web.a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so* raygui.c $(RAYLIB_RELEASE_PATH)/*-protocol.h $(RAYLIB_RELEASE_PATH)/*-protocol-code.h $(RAYLIB_RELEASE_PATH)/*-protocol-code.c
ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID)
rm -fv $(NATIVE_APP_GLUE)/android_native_app_glue.o
endif
diff --git a/src/config.h b/src/config.h
index 3beaeeceb..a3502d772 100644
--- a/src/config.h
+++ b/src/config.h
@@ -2,7 +2,9 @@
*
* raylib configuration flags
*
-* This file defines all the configuration flags for the different raylib modules
+* This file defines the configuration flags for different raylib features per-module
+*
+* NOTE: Additional values are configured per-module and can be set on compile time
*
* LICENSE: zlib/libpng
*
@@ -28,283 +30,343 @@
#ifndef CONFIG_H
#define CONFIG_H
+#if !defined(EXTERNAL_CONFIG_FLAGS)
+
//------------------------------------------------------------------------------------
// Module selection - Some modules could be avoided
// Mandatory modules: rcore, rlgl
//------------------------------------------------------------------------------------
-#if !defined(EXTERNAL_CONFIG_FLAGS)
- #define SUPPORT_MODULE_RSHAPES 1
- #define SUPPORT_MODULE_RTEXTURES 1
- #define SUPPORT_MODULE_RTEXT 1 // WARNING: It requires SUPPORT_MODULE_RTEXTURES to load sprite font textures
- #define SUPPORT_MODULE_RMODELS 1
- #define SUPPORT_MODULE_RAUDIO 1
+#ifndef SUPPORT_MODULE_RSHAPES
+ #define SUPPORT_MODULE_RSHAPES 1
+#endif
+#ifndef SUPPORT_MODULE_RTEXTURES
+ #define SUPPORT_MODULE_RTEXTURES 1
+#endif
+#ifndef SUPPORT_MODULE_RTEXT
+ #define SUPPORT_MODULE_RTEXT 1 // WARNING: It requires SUPPORT_MODULE_RTEXTURES to load sprite font textures
+#endif
+#ifndef SUPPORT_MODULE_RMODELS
+ #define SUPPORT_MODULE_RMODELS 1
+#endif
+#ifndef SUPPORT_MODULE_RAUDIO
+ #define SUPPORT_MODULE_RAUDIO 1
#endif
//------------------------------------------------------------------------------------
// Module: rcore - Configuration Flags
//------------------------------------------------------------------------------------
-#if !defined(EXTERNAL_CONFIG_FLAGS)
-// Standard file io library (stdio.h) included
-#define SUPPORT_STANDARD_FILEIO 1
-// Show TRACELOG() output messages
-#define SUPPORT_TRACELOG 1
-// Camera module is included (rcamera.h) and multiple predefined cameras are available: free, 1st/3rd person, orbital
-#define SUPPORT_CAMERA_SYSTEM 1
-// Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag
-#define SUPPORT_GESTURES_SYSTEM 1
-// Include pseudo-random numbers generator (rprand.h), based on Xoshiro128** and SplitMix64
-#define SUPPORT_RPRAND_GENERATOR 1
-// Mouse gestures are directly mapped like touches and processed by gestures system
-#define SUPPORT_MOUSE_GESTURES 1
-// Reconfigure standard input to receive key inputs, works with SSH connection
-#define SUPPORT_SSH_KEYBOARD_RPI 1
-// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions
-// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often
-#define SUPPORT_WINMM_HIGHRES_TIMER 1
-// Use busy wait loop for timing sync, if not defined, a high-resolution timer is set up and used
-//#define SUPPORT_BUSY_WAIT_LOOP 1
-// Use a partial-busy wait loop, in this case frame sleeps for most of the time, but then runs a busy loop at the end for accuracy
-#define SUPPORT_PARTIALBUSY_WAIT_LOOP 1
-// Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()
-// WARNING: It also requires SUPPORT_IMAGE_EXPORT and SUPPORT_FILEFORMAT_PNG flags
-#define SUPPORT_SCREEN_CAPTURE 1
-// Support CompressData() and DecompressData() functions
-#define SUPPORT_COMPRESSION_API 1
-// Support automatic generated events, loading and recording of those events when required
-#define SUPPORT_AUTOMATION_EVENTS 1
-// Support custom frame control, only for advanced users
-// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents()
-// Enabling this flag allows manual control of the frame processes, use at your own risk
-//#define SUPPORT_CUSTOM_FRAME_CONTROL 1
-// Support for clipboard image loading
-// NOTE: Only working on SDL3, GLFW (Windows) and RGFW (Windows)
-#define SUPPORT_CLIPBOARD_IMAGE 1
+#ifndef SUPPORT_TRACELOG
+ // Show TRACELOG() output messages
+ #define SUPPORT_TRACELOG 1
#endif
-
-// NOTE: Clipboard image loading requires support for some image file formats
-// TODO: Those defines should probably be removed from here, letting the user manage them
-#if defined(SUPPORT_CLIPBOARD_IMAGE)
- #ifndef SUPPORT_MODULE_RTEXTURES
- #define SUPPORT_MODULE_RTEXTURES 1
- #endif
- #ifndef STBI_REQUIRED
- #define STBI_REQUIRED
- #endif
- #ifndef SUPPORT_FILEFORMAT_BMP // For clipboard image on Windows
- #define SUPPORT_FILEFORMAT_BMP 1
- #endif
- #ifndef SUPPORT_FILEFORMAT_PNG // Wayland uses png for prints, at least it was on 22 LTS ubuntu
- #define SUPPORT_FILEFORMAT_PNG 1
- #endif
- #ifndef SUPPORT_FILEFORMAT_JPG
- #define SUPPORT_FILEFORMAT_JPG 1
- #endif
+#ifndef SUPPORT_CAMERA_SYSTEM
+ // Camera module is included (rcamera.h) and multiple predefined
+ // cameras are available: free, 1st/3rd person, orbital
+ #define SUPPORT_CAMERA_SYSTEM 1
#endif
-
-#if defined(SUPPORT_TRACELOG)
- #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__)
-#else
- #define TRACELOG(level, ...) (void)0
+#ifndef SUPPORT_GESTURES_SYSTEM
+ // Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag
+ #define SUPPORT_GESTURES_SYSTEM 1
+#endif
+#ifndef SUPPORT_RPRAND_GENERATOR
+ // Include pseudo-random numbers generator (rprand.h), based on Xoshiro128** and SplitMix64
+ #define SUPPORT_RPRAND_GENERATOR 1
+#endif
+#ifndef SUPPORT_MOUSE_GESTURES
+ // Mouse gestures are directly mapped like touches and processed by gestures system
+ #define SUPPORT_MOUSE_GESTURES 1
+#endif
+#ifndef SUPPORT_SSH_KEYBOARD_RPI
+ // Reconfigure standard input to receive key inputs, works with SSH connection
+ #define SUPPORT_SSH_KEYBOARD_RPI 1
+#endif
+#ifndef SUPPORT_WINMM_HIGHRES_TIMER
+ // Setting a higher resolution can improve the accuracy of time-out intervals in wait functions
+ // However, it can also reduce overall system performance, because the thread scheduler switches tasks more often
+ #define SUPPORT_WINMM_HIGHRES_TIMER 1
+#endif
+#if !SUPPORT_BUSY_WAIT_LOOP && !SUPPORT_PARTIALBUSY_WAIT_LOOP
+ // Use busy wait loop for timing sync, if not defined, a high-resolution timer is set up and used
+ #define SUPPORT_BUSY_WAIT_LOOP 0 // Disabled by default
+#endif
+#if !SUPPORT_PARTIALBUSY_WAIT_LOOP && !SUPPORT_BUSY_WAIT_LOOP
+ // Use a partial-busy wait loop, in this case frame sleeps for most of the time,
+ // but then runs a busy loop at the end for accuracy
+ #define SUPPORT_PARTIALBUSY_WAIT_LOOP 1
+#endif
+#ifndef SUPPORT_SCREEN_CAPTURE
+ // Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()
+ // WARNING: It requires SUPPORT_FILEFORMAT_PNG flag
+ #define SUPPORT_SCREEN_CAPTURE 1
+#endif
+#ifndef SUPPORT_COMPRESSION_API
+ // Support CompressData() and DecompressData() functions
+ #define SUPPORT_COMPRESSION_API 1
+#endif
+#ifndef SUPPORT_AUTOMATION_EVENTS
+ // Support automatic generated events, loading and recording of those events when required
+ #define SUPPORT_AUTOMATION_EVENTS 1
+#endif
+#ifndef SUPPORT_CUSTOM_FRAME_CONTROL
+ // Support custom frame control, only for advanced users
+ // By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents()
+ // Enabling this flag allows manual control of the frame processes, use at your own risk
+ #define SUPPORT_CUSTOM_FRAME_CONTROL 0 // Disabled by default
+#endif
+#ifndef SUPPORT_CLIPBOARD_IMAGE
+ // Support for clipboard image loading
+ // NOTE: Only working on SDL3, GLFW (Windows) and RGFW (Windows)
+ // WARNING: It requires support for some additional flags:
+ // - SUPPORT_MODULE_RTEXTURES
+ // - SUPPORT_FILEFORMAT_BMP (Windows clipboard)
+ // - SUPPORT_FILEFORMAT_PNG (Wayland clipboard)
+ // - SUPPORT_FILEFORMAT_JPG
+ #define SUPPORT_CLIPBOARD_IMAGE 1
#endif
// rcore: Configuration values
+// NOTE: Below values are alread defined inside [rcore.c] so there is no need to be
+// redefined here, in case it must be done, uncomment the required line and update
+// the value; it can also be done on compilation with -DVALUE_TO_REDEFINE=128
+//------------------------------------------------------------------------------------
+//#define MAX_TRACELOG_MSG_LENGTH 256 // Max length of one trace-log message
+//#define MAX_FILEPATH_CAPACITY 8192 // Maximum file paths capacity
+//#define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value)
+//#define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported
+//#define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported
+//#define MAX_GAMEPADS 4 // Maximum number of gamepads supported
+//#define MAX_GAMEPAD_AXES 8 // Maximum number of axes supported (per gamepad)
+//#define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad)
+//#define MAX_GAMEPAD_VIBRATION_TIME 2.0f // Maximum vibration time in seconds
+//#define MAX_TOUCH_POINTS 10 // Maximum number of touch points supported
+//#define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue
+//#define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue
+//#define MAX_DECOMPRESSION_SIZE 64 // Max size allocated for decompression in MB
+//#define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record
//------------------------------------------------------------------------------------
-#define MAX_TRACELOG_MSG_LENGTH 256 // Max length of one trace-log message
-#define MAX_FILEPATH_CAPACITY 8192 // Maximum file paths capacity
-#define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value)
-
-#define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported
-#define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported
-#define MAX_GAMEPADS 4 // Maximum number of gamepads supported
-#define MAX_GAMEPAD_AXES 8 // Maximum number of axes supported (per gamepad)
-#define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad)
-#define MAX_GAMEPAD_VIBRATION_TIME 2.0f // Maximum vibration time in seconds
-#define MAX_TOUCH_POINTS 10 // Maximum number of touch points supported
-#define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue
-#define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue
-
-#define MAX_DECOMPRESSION_SIZE 64 // Max size allocated for decompression in MB
-
-#define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record
//------------------------------------------------------------------------------------
// Module: rlgl - Configuration values
//------------------------------------------------------------------------------------
-#if !defined(EXTERNAL_CONFIG_FLAGS)
-// Enable OpenGL Debug Context (only available on OpenGL 4.3)
-//#define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT 1
-
-// Show OpenGL extensions and capabilities detailed logs on init
-//#define RLGL_SHOW_GL_DETAILS_INFO 1
-
-#define RL_SUPPORT_MESH_GPU_SKINNING 1 // GPU skinning, comment if your GPU does not support more than 8 VBOs
+#ifndef RLGL_ENABLE_OPENGL_DEBUG_CONTEXT
+ // Request OpenGL debug context (only available on OpenGL 4.3)
+ #define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT 0
+#endif
+#ifndef RLGL_SHOW_GL_DETAILS_INFO
+ // Show OpenGL detailed info on initialization,
+ // supported GL extensions and GL capabilities
+ #define RLGL_SHOW_GL_DETAILS_INFO 0
#endif
-//#define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 4096 // Default internal render batch elements limits
-#define RL_DEFAULT_BATCH_BUFFERS 1 // Default number of batch buffers (multi-buffering)
-#define RL_DEFAULT_BATCH_DRAWCALLS 256 // Default number of batch draw calls (by state changes: mode, texture)
-#define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of textures units that can be activated on batch drawing (SetShaderValueTexture())
-
-#define RL_MAX_MATRIX_STACK_SIZE 32 // Maximum size of internal Matrix stack
-
-#define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported
-
-#define RL_CULL_DISTANCE_NEAR 0.05 // Default projection matrix near cull distance
-#define RL_CULL_DISTANCE_FAR 4000.0 // Default projection matrix far cull distance
+// rlgl: Configuration values
+// NOTE: Below values are alread defined inside [rlgl.h] so there is no need to be
+// redefined here, in case it must be done, uncomment the required line and update
+// the value; it can also be done on compilation with -DVALUE_TO_REDEFINE=128
+//------------------------------------------------------------------------------------
+//#define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 4096 // Default internal render batch elements limits
+//#define RL_DEFAULT_BATCH_BUFFERS 1 // Default number of batch buffers (multi-buffering)
+//#define RL_DEFAULT_BATCH_DRAWCALLS 256 // Default number of batch draw calls (by state changes: mode, texture)
+//#define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of textures units that can be activated on batch drawing (SetShaderValueTexture())
+//#define RL_MAX_MATRIX_STACK_SIZE 32 // Maximum size of internal Matrix stack
+//#define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported
+//#define RL_CULL_DISTANCE_NEAR 0.05 // Default projection matrix near cull distance
+//#define RL_CULL_DISTANCE_FAR 4000.0 // Default projection matrix far cull distance
// Default shader vertex attribute locations
-#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION 0
-#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD 1
-#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL 2
-#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR 3
-#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT 4
-#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 5
-#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES 6
-#if defined(RL_SUPPORT_MESH_GPU_SKINNING)
- #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS 7
- #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8
-#endif
-#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCE_TX 9
+//#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION 0
+//#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD 1
+//#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL 2
+//#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR 3
+//#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT 4
+//#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2 5
+//#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES 6
+//#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES 7
+//#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8
+//#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORM 9
-// Default shader vertex attribute names to set location points
-// NOTE: When a new shader is loaded, the following locations are tried to be set for convenience
-#define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION
-#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD
-#define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL
-#define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR
-#define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT
-#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2
-
-#define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix
-#define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix
-#define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix
-#define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix
-#define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView))
-#define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color)
-#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0)
-#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1)
-#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2)
+// Default shader vertex attribute/uniform names to set location points
+// NOTE: When a new shader is loaded, locations are tried to be set for convenience, looking for the names defined here
+// In case custom shader names are used, it's up to the user to set locations with GetShaderLocation*() functions
+//#define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION
+//#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD
+//#define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL
+//#define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR
+//#define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT
+//#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2
+//#define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES "vertexBoneIndices" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES
+//#define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS
+//#define RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORM "instanceTransform" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORM
+//#define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix
+//#define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix
+//#define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix
+//#define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix
+//#define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView))
+//#define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (tint color, multiplied by texture color)
+//#define RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES "boneMatrices" // bone matrices
+//#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0)
+//#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1)
+//#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2)
+//------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------
// Module: rshapes - Configuration Flags
//------------------------------------------------------------------------------------
-#if !defined(EXTERNAL_CONFIG_FLAGS)
-// Use QUADS instead of TRIANGLES for drawing when possible
-// Some lines-based shapes could still use lines
-#define SUPPORT_QUADS_DRAW_MODE 1
+#ifndef SUPPORT_QUADS_DRAW_MODE
+ // Use QUADS instead of TRIANGLES for drawing when possible
+ // Some lines-based shapes could still use lines
+ #define SUPPORT_QUADS_DRAW_MODE 1
#endif
-// rshapes: Configuration values
-//------------------------------------------------------------------------------------
-#define SPLINE_SEGMENT_DIVISIONS 24 // Spline segments subdivisions
-
//------------------------------------------------------------------------------------
// Module: rtextures - Configuration Flags
//------------------------------------------------------------------------------------
-#if !defined(EXTERNAL_CONFIG_FLAGS)
// Selected desired fileformats to be supported for image data loading
-#define SUPPORT_FILEFORMAT_PNG 1
-//#define SUPPORT_FILEFORMAT_BMP 1
-//#define SUPPORT_FILEFORMAT_TGA 1
-//#define SUPPORT_FILEFORMAT_JPG 1
-#define SUPPORT_FILEFORMAT_GIF 1
-#define SUPPORT_FILEFORMAT_QOI 1
-//#define SUPPORT_FILEFORMAT_PSD 1
-#define SUPPORT_FILEFORMAT_DDS 1
-//#define SUPPORT_FILEFORMAT_HDR 1
-//#define SUPPORT_FILEFORMAT_PIC 1
-//#define SUPPORT_FILEFORMAT_KTX 1
-//#define SUPPORT_FILEFORMAT_ASTC 1
-//#define SUPPORT_FILEFORMAT_PKM 1
-//#define SUPPORT_FILEFORMAT_PVR 1
+#ifndef SUPPORT_FILEFORMAT_PNG
+ #define SUPPORT_FILEFORMAT_PNG 1
+#endif
+#ifndef SUPPORT_FILEFORMAT_BMP
+ // NOTE: BMP support required for clipboard images on Windows
+ #define SUPPORT_FILEFORMAT_BMP 1
+#endif
+#ifndef SUPPORT_FILEFORMAT_TGA
+ #define SUPPORT_FILEFORMAT_TGA 0 // Disabled by default
+#endif
+#ifndef SUPPORT_FILEFORMAT_JPG
+ #define SUPPORT_FILEFORMAT_JPG 0 // Disabled by default
+#endif
+#ifndef SUPPORT_FILEFORMAT_GIF
+ #define SUPPORT_FILEFORMAT_GIF 1
+#endif
+#ifndef SUPPORT_FILEFORMAT_QOI
+ #define SUPPORT_FILEFORMAT_QOI 1
+#endif
+#ifndef SUPPORT_FILEFORMAT_PSD
+ #define SUPPORT_FILEFORMAT_PSD 0 // Disabled by default
+#endif
+#ifndef SUPPORT_FILEFORMAT_DDS
+ #define SUPPORT_FILEFORMAT_DDS 1
+#endif
+#ifndef SUPPORT_FILEFORMAT_HDR
+ #define SUPPORT_FILEFORMAT_HDR 0 // Disabled by default
+#endif
+#ifndef SUPPORT_FILEFORMAT_PIC
+ #define SUPPORT_FILEFORMAT_PIC 0 // Disabled by default
+#endif
+#ifndef SUPPORT_FILEFORMAT_KTX
+ #define SUPPORT_FILEFORMAT_KTX 0 // Disabled by default
+#endif
+#ifndef SUPPORT_FILEFORMAT_ASTC
+ #define SUPPORT_FILEFORMAT_ASTC 0 // Disabled by default
+#endif
+#ifndef SUPPORT_FILEFORMAT_PKM
+ #define SUPPORT_FILEFORMAT_PKM 0 // Disabled by default
+#endif
+#ifndef SUPPORT_FILEFORMAT_PVR
+ #define SUPPORT_FILEFORMAT_PVR 0 // Disabled by default
+#endif
-// Support image export functionality (.png, .bmp, .tga, .jpg, .qoi)
-#define SUPPORT_IMAGE_EXPORT 1
-// Support procedural image generation functionality (gradient, spot, perlin-noise, cellular)
-#define SUPPORT_IMAGE_GENERATION 1
-// Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop...
-// If not defined, still some functions are supported: ImageFormat(), ImageCrop(), ImageToPOT()
-#define SUPPORT_IMAGE_MANIPULATION 1
+#ifndef SUPPORT_IMAGE_EXPORT
+ // Support image export functionality (.png, .bmp, .tga, .jpg, .qoi)
+ // NOTE: Image export requires stb_image_write.h library
+ #define SUPPORT_IMAGE_EXPORT 1
+#endif
+#ifndef SUPPORT_IMAGE_GENERATION
+ // Support procedural image generation functionality: gradient, spot, perlin-noise, cellular...
+ // NOTE: Perlin noise requires stb_perlin.h library
+ #define SUPPORT_IMAGE_GENERATION 1
#endif
//------------------------------------------------------------------------------------
// Module: rtext - Configuration Flags
//------------------------------------------------------------------------------------
-#if !defined(EXTERNAL_CONFIG_FLAGS)
-// Default font is loaded on window initialization to be available for the user to render simple text
-// NOTE: If enabled, uses external module functions to load default raylib font
-#define SUPPORT_DEFAULT_FONT 1
// Selected desired font fileformats to be supported for loading
-#define SUPPORT_FILEFORMAT_TTF 1
-#define SUPPORT_FILEFORMAT_FNT 1
-//#define SUPPORT_FILEFORMAT_BDF 1
-
-// Support text management functions
-// If not defined, still some functions are supported: TextLength(), TextFormat()
-#define SUPPORT_TEXT_MANIPULATION 1
-
-// On font atlas image generation [GenImageFontAtlas()], add a 3x3 pixels white rectangle
-// at the bottom-right corner of the atlas. It can be useful to for shapes drawing, to allow
-// drawing text and shapes with a single draw call [SetShapesTexture()]
-#define SUPPORT_FONT_ATLAS_WHITE_REC 1
-
-// Support conservative font atlas size estimation
-//#define SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE 1
+#ifndef SUPPORT_FILEFORMAT_TTF
+ #define SUPPORT_FILEFORMAT_TTF 1
+#endif
+#ifndef SUPPORT_FILEFORMAT_FNT
+ #define SUPPORT_FILEFORMAT_FNT 1
+#endif
+#ifndef SUPPORT_FILEFORMAT_BDF
+ #define SUPPORT_FILEFORMAT_BDF 0 // Disabled by default
#endif
-
-// rtext: Configuration values
-//------------------------------------------------------------------------------------
-#define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions:
- // TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal(), TextSplit()
-#define MAX_TEXTSPLIT_COUNT 128 // Maximum number of substrings to split: TextSplit()
//------------------------------------------------------------------------------------
// Module: rmodels - Configuration Flags
//------------------------------------------------------------------------------------
-#if !defined(EXTERNAL_CONFIG_FLAGS)
// Selected desired model fileformats to be supported for loading
-#define SUPPORT_FILEFORMAT_OBJ 1
-#define SUPPORT_FILEFORMAT_MTL 1
-#define SUPPORT_FILEFORMAT_IQM 1
-#define SUPPORT_FILEFORMAT_GLTF 1
-#define SUPPORT_FILEFORMAT_VOX 1
-#define SUPPORT_FILEFORMAT_M3D 1
-// Support procedural mesh generation functions, uses external par_shapes.h library
-// NOTE: Some generated meshes DO NOT include generated texture coordinates
-#define SUPPORT_MESH_GENERATION 1
+#ifndef SUPPORT_FILEFORMAT_OBJ
+ #define SUPPORT_FILEFORMAT_OBJ 1
#endif
-
-// rmodels: Configuration values
-//------------------------------------------------------------------------------------
-#define MAX_MATERIAL_MAPS 12 // Maximum number of shader maps supported
-
-#ifdef RL_SUPPORT_MESH_GPU_SKINNING
-#define MAX_MESH_VERTEX_BUFFERS 9 // Maximum vertex buffers (VBO) per mesh
-#else
-#define MAX_MESH_VERTEX_BUFFERS 7 // Maximum vertex buffers (VBO) per mesh
+#ifndef SUPPORT_FILEFORMAT_MTL
+ #define SUPPORT_FILEFORMAT_MTL 1
+#endif
+#ifndef SUPPORT_FILEFORMAT_IQM
+ #define SUPPORT_FILEFORMAT_IQM 1
+#endif
+#ifndef SUPPORT_FILEFORMAT_GLTF
+ #define SUPPORT_FILEFORMAT_GLTF 1
+#endif
+#ifndef SUPPORT_FILEFORMAT_VOX
+ #define SUPPORT_FILEFORMAT_VOX 1
+#endif
+#ifndef SUPPORT_FILEFORMAT_M3D
+ #define SUPPORT_FILEFORMAT_M3D 1
+#endif
+#ifndef SUPPORT_MESH_GENERATION
+ // Support procedural mesh generation functions, uses external par_shapes.h library
+ // NOTE: Some generated meshes DO NOT include generated texture coordinates
+ #define SUPPORT_MESH_GENERATION 1
+#endif
+#ifndef SUPPORT_GPU_SKINNING
+ // GPU skinning disabled by default, some GPUs do not support more than 8 VBOs
+ #define SUPPORT_GPU_SKINNING 0
#endif
//------------------------------------------------------------------------------------
// Module: raudio - Configuration Flags
//------------------------------------------------------------------------------------
-#if !defined(EXTERNAL_CONFIG_FLAGS)
// Desired audio fileformats to be supported for loading
-#define SUPPORT_FILEFORMAT_WAV 1
-#define SUPPORT_FILEFORMAT_OGG 1
-#define SUPPORT_FILEFORMAT_MP3 1
-#define SUPPORT_FILEFORMAT_QOA 1
-//#define SUPPORT_FILEFORMAT_FLAC 1
-#define SUPPORT_FILEFORMAT_XM 1
-#define SUPPORT_FILEFORMAT_MOD 1
+#ifndef SUPPORT_FILEFORMAT_WAV
+ #define SUPPORT_FILEFORMAT_WAV 1
+#endif
+#ifndef SUPPORT_FILEFORMAT_OGG
+ #define SUPPORT_FILEFORMAT_OGG 1
+#endif
+#ifndef SUPPORT_FILEFORMAT_MP3
+ #define SUPPORT_FILEFORMAT_MP3 1
+#endif
+#ifndef SUPPORT_FILEFORMAT_QOA
+ #define SUPPORT_FILEFORMAT_QOA 1
+#endif
+#ifndef SUPPORT_FILEFORMAT_FLAC
+ #define SUPPORT_FILEFORMAT_FLAC 0 // Disabled by default
+#endif
+#ifndef SUPPORT_FILEFORMAT_XM
+ #define SUPPORT_FILEFORMAT_XM 1
+#endif
+#ifndef SUPPORT_FILEFORMAT_MOD
+ #define SUPPORT_FILEFORMAT_MOD 1
#endif
// raudio: Configuration values
+// NOTE: Below values are alread defined inside [rlgl.h] so there is no need to be
+// redefined here, in case it must be done, uncomment the required line and update
+// the value; it can also be done on compilation with -DVALUE_TO_REDEFINE=128
//------------------------------------------------------------------------------------
-#define AUDIO_DEVICE_FORMAT ma_format_f32 // Device output format (miniaudio: float-32bit)
-#define AUDIO_DEVICE_CHANNELS 2 // Device output channels: stereo
-#define AUDIO_DEVICE_SAMPLE_RATE 0 // Device sample rate (device default)
-#define AUDIO_DEVICE_PERIOD_SIZE_IN_FRAMES 0 // Device period size (controls latency, 0 defaults to 10ms)
+//#define AUDIO_DEVICE_FORMAT ma_format_f32 // Device output format (miniaudio: float-32bit)
+//#define AUDIO_DEVICE_CHANNELS 2 // Device output channels: stereo
+//#define AUDIO_DEVICE_SAMPLE_RATE 0 // Device sample rate (device default)
+//#define AUDIO_DEVICE_PERIOD_SIZE_IN_FRAMES 0 // Device period size (controls latency, 0 defaults to 10ms)
+//#define MAX_AUDIO_BUFFER_POOL_CHANNELS 16 // Maximum number of audio pool channels
+//------------------------------------------------------------------------------------
+#endif // !EXTERNAL_CONFIG_FLAGS
-#define MAX_AUDIO_BUFFER_POOL_CHANNELS 16 // Maximum number of audio pool channels
+// NOTE: Following macro depends on config flag that can
+// be externally defined, so, it needs to be outside EXTERNAL_CONFIG_FLAGS
+#if SUPPORT_TRACELOG
+ #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__)
+#else
+ #define TRACELOG(level, ...) (void)0
+#endif
#endif // CONFIG_H
diff --git a/src/external/RGFW.h b/src/external/RGFW.h
deleted file mode 100644
index c01ce1177..000000000
--- a/src/external/RGFW.h
+++ /dev/null
@@ -1,11074 +0,0 @@
-/*
-*
-* RGFW 1.7.5-dev
-
-* Copyright (C) 2022-25 ColleagueRiley
-*
-* libpng license
-*
-* This software is provided 'as-is', without any express or implied
-* warranty. In no event will the authors be held liable for any damages
-* arising from the use of this software.
-
-* Permission is granted to anyone to use this software for any purpose,
-* including commercial applications, and to alter it and redistribute it
-* freely, subject to the following restrictions:
-*
-* 1. The origin of this software must not be misrepresented; you must not
-* claim that you wrote the original software. If you use this software
-* in a product, an acknowledgment in the product documentation would be
-* appreciated but is not required.
-* 2. Altered source versions must be plainly marked as such, and must not be
-* misrepresented as being the original software.
-* 3. This notice may not be removed or altered from any source distribution.
-*
-*
-*/
-
-/*
- (MAKE SURE RGFW_IMPLEMENTATION is in exactly one header or you use -D RGFW_IMPLEMENTATION)
- #define RGFW_IMPLEMENTATION - makes it so source code is included with header
-*/
-
-/*
- #define RGFW_IMPLEMENTATION - (required) makes it so the source code is included
- #define RGFW_DEBUG - (optional) makes it so RGFW prints debug messages and errors when they're found
- #define RGFW_OSMESA - (optional) use OSmesa as backend (instead of system's opengl api + regular opengl)
- #define RGFW_BUFFER - (optional) 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)
- This version doesn't work for desktops (I'm pretty sure)
- #define RGFW_OPENGL_ES2 - (optional) use OpenGL ES (version 2)
- #define RGFW_OPENGL_ES3 - (optional) use OpenGL ES (version 3)
- #define RGFW_DIRECTX - (optional) include integration directX functions (windows only)
- #define RGFW_VULKAN - (optional) include helpful vulkan integration functions and macros
- #define RGFW_WEBGPU - (optional) use webGPU for rendering (Web ONLY)
- #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_X11 (optional) (unix only) if X11 should be used. This option is turned on by default by unix systems except for MacOS
- #define RGFW_WAYLAND (optional) (unix only) use Wayland. (This can be used with X11)
- #define RGFW_NO_X11 (optional) (unix only) don't fallback to X11 when using Wayland
- #define RGFW_NO_LOAD_WGL (optional) (windows only) if WGL should be loaded dynamically during runtime
- #define RGFW_NO_X11_CURSOR (optional) (unix only) don't use XCursor
- #define RGFW_NO_X11_CURSOR_PRELOAD (optional) (unix only) use XCursor, but don't link it in code, (you'll have to link it with -lXcursor)
- #define RGFW_NO_X11_EXT_PRELOAD (optional) (unix only) use Xext, but don't link it in code, (you'll have to link it with -lXext)
- #define RGFW_NO_LOAD_WINMM (optional) (windows only) use winmm (timeBeginPeriod), but don't link it in code, (you'll have to link it with -lwinmm)
- #define RGFW_NO_WINMM (optional) (windows only) don't use winmm
- #define RGFW_NO_IOKIT (optional) (macOS) don't use IOKit
- #define RGFW_NO_UNIX_CLOCK (optional) (unix) don't link unix clock functions
- #define RGFW_NO_DWM (windows only) - do not use or link dwmapi
- #define RGFW_USE_XDL (optional) (X11) if XDL (XLib Dynamic Loader) should be used to load X11 dynamically during runtime (must include XDL.h along with RGFW)
- #define RGFW_COCOA_GRAPHICS_SWITCHING - (optional) (cocoa) use automatic graphics switching (allow the system to choose to use GPU or iGPU)
- #define RGFW_COCOA_FRAME_NAME (optional) (cocoa) set frame name
- #define RGFW_NO_DPI - do not calculate DPI (no XRM nor libShcore included)
- #define RGFW_BUFFER_BGR - use the BGR format for bufffers instead of RGB, saves processing time
- #define RGFW_ADVANCED_SMOOTH_RESIZE - use advanced methods for smooth resizing (may result in a spike in memory usage or worse performance) (eg. WM_TIMER and XSyncValue)
-
- #define RGFW_ALLOC x - choose the default allocation function (defaults to standard malloc)
- #define RGFW_FREE x - choose the default deallocation function (defaults to standard free)
- #define RGFW_USERPTR x - choose the default userptr sent to the malloc call, (NULL by default)
-
- #define RGFW_EXPORT - use when building RGFW
- #define RGFW_IMPORT - use when linking with RGFW (not as a single-header)
-
- #define RGFW_USE_INT - force the use c-types rather than stdint.h (for systems that might not have stdint.h (msvc))
- #define RGFW_bool x - choose what type to use for bool, by default u32 is used
-*/
-
-/*
-Example to get you started :
-
-linux : gcc main.c -lX11 -lXrandr -lGL
-windows : gcc main.c -lopengl32 -lgdi32
-macos : gcc main.c -framework Cocoa -framework CoreVideo -framework OpenGL -framework IOKit
-
-#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(100, 100, 500, 500), (u64)0);
-
- RGFW_window_setIcon(win, icon, RGFW_AREA(3, 3), 4);
-
- while (RGFW_window_shouldClose(win) == RGFW_FALSE) {
- while (RGFW_window_checkEvent(win)) {
- if (win->event.type == RGFW_quit || RGFW_isPressed(win, RGFW_escape))
- break;
- }
-
- RGFW_window_swapBuffers(win);
-
- glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
- 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"
-
- You may also want to add
- `#define RGFW_EXPORT` when compiling and
- `#define RGFW_IMPORT`when linking RGFW on it's own:
- this reduces inline functions and prevents bloat in the object file
-
- then you can use gcc (or whatever compile you wish to use) to compile the library into object file
-
- ex. gcc -c RGFW.c -fPIC
-
- after you compile the library into an object file, you can also turn the object file into an static or shared library
-
- (commands ar and gcc can be replaced with whatever equivalent your system uses)
-
- static : ar rcs RGFW.a RGFW.o
- shared :
- windows:
- gcc -shared RGFW.o -lopengl32 -lgdi32 -o RGFW.dll
- linux:
- gcc -shared RGFW.o -lX11 -lGL -lXrandr -o RGFW.so
- macos:
- gcc -shared RGFW.o -framework CoreVideo -framework Cocoa -framework OpenGL -framework IOKit
-*/
-
-
-
-/*
- Credits :
- EimaMei/Sacode : Much of the code for creating windows using winapi, Wrote the Silicon library, helped with MacOS Support, siliapp.h -> referencing
-
- 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.
-
- contributors : (feel free to put yourself here if you contribute)
- krisvers -> code review
- EimaMei (SaCode) -> code review
- Code-Nycticebus -> bug fixes
- Rob Rohan -> X11 bugs and missing features, MacOS/Cocoa fixing memory issues/bugs
- AICDG (@THISISAGOODNAME) -> vulkan support (example)
- @Easymode -> support, testing/debugging, bug fixes and reviews
- Joshua Rowe (omnisci3nce) - bug fix, review (macOS)
- @lesleyrs -> bug fix, review (OpenGL)
- Nick Porcino (meshula) - testing, organization, review (MacOS, examples)
- @DarekParodia -> code review (X11) (C++)
-*/
-
-#if _MSC_VER
- #pragma comment(lib, "gdi32")
- #pragma comment(lib, "shell32")
- #pragma comment(lib, "User32")
- #pragma warning( push )
- #pragma warning( disable : 4996 4191 4127)
- #if _MSC_VER < 600
- #define RGFW_C89
- #endif
-#else
- #if defined(__STDC__) && !defined(__STDC_VERSION__)
- #define RGFW_C89
- #endif
-#endif
-
-#ifndef RGFW_USERPTR
- #define RGFW_USERPTR NULL
-#endif
-
-#ifndef RGFW_UNUSED
- #define RGFW_UNUSED(x) (void)(x)
-#endif
-
-#ifndef RGFW_ROUND
- #define RGFW_ROUND(x) (i32)((x) >= 0 ? (x) + 0.5f : (x) - 0.5f)
-#endif
-
-#ifndef RGFW_ALLOC
- #include
- #define RGFW_ALLOC malloc
- #define RGFW_FREE free
-#endif
-
-#ifndef RGFW_ASSERT
- #include
- #define RGFW_ASSERT assert
-#endif
-
-#if !defined(RGFW_MEMCPY) || !defined(RGFW_STRNCMP) || !defined(RGFW_STRNCPY) || !defined(RGFW_MEMSET)
- #include
-#endif
-
-#ifndef RGFW_MEMSET
- #define RGFW_MEMSET(ptr, value, num) memset(ptr, value, num)
-#endif
-
-#ifndef RGFW_MEMCPY
- #define RGFW_MEMCPY(dist, src, len) memcpy(dist, src, len)
-#endif
-
-#ifndef RGFW_STRNCMP
- #define RGFW_STRNCMP(s1, s2, max) strncmp(s1, s2, max)
-#endif
-
-#ifndef RGFW_STRNCPY
- #define RGFW_STRNCPY(dist, src, len) strncpy(dist, src, len)
-#endif
-
-#ifndef RGFW_STRSTR
- #define RGFW_STRSTR(str, substr) strstr(str, substr)
-#endif
-
-#ifndef RGFW_STRTOL
- /* required for X11 XDnD and X11 Monitor DPI */
- #include
- #define RGFW_STRTOL(str, endptr, base) strtol(str, endptr, base)
- #define RGFW_ATOF(num) atof(num)
-#endif
-
-#ifdef RGFW_WIN95 /* for windows 95 testing (not that it really works) */
- #define RGFW_NO_MONITOR
- #define RGFW_NO_PASSTHROUGH
-#endif
-
-#if defined(RGFW_EXPORT) || defined(RGFW_IMPORT)
- #if defined(_WIN32)
- #if defined(__TINYC__) && (defined(RGFW_EXPORT) || defined(RGFW_IMPORT))
- #define __declspec(x) __attribute__((x))
- #endif
-
- #if defined(RGFW_EXPORT)
- #define RGFWDEF __declspec(dllexport)
- #else
- #define RGFWDEF __declspec(dllimport)
- #endif
- #else
- #if defined(RGFW_EXPORT)
- #define RGFWDEF __attribute__((visibility("default")))
- #endif
- #endif
- #ifndef RGFWDEF
- #define RGFWDEF
- #endif
-#endif
-
-#ifndef RGFWDEF
- #ifdef RGFW_C89
- #define RGFWDEF __inline
- #else
- #define RGFWDEF inline
- #endif
-#endif
-
-#ifndef RGFW_ENUM
- #define RGFW_ENUM(type, name) type name; enum
-#endif
-
-
-#if defined(__cplusplus) && !defined(__EMSCRIPTEN__)
- extern "C" {
-#endif
-
- /* makes sure the header file part is only defined once by default */
-#ifndef RGFW_HEADER
-
-#define RGFW_HEADER
-
-#include
-#ifndef RGFW_INT_DEFINED
- #ifdef RGFW_USE_INT /* optional for any system that might not have stdint.h */
- typedef unsigned char u8;
- typedef signed char i8;
- typedef unsigned short u16;
- typedef signed short i16;
- typedef unsigned long int u32;
- typedef signed long int i32;
- typedef unsigned long long u64;
- typedef signed long long i64;
- #else /* use stdint standard types instead of c "standard" types */
- #include
-
- typedef uint8_t u8;
- typedef int8_t i8;
- typedef uint16_t u16;
- typedef int16_t i16;
- typedef uint32_t u32;
- typedef int32_t i32;
- typedef uint64_t u64;
- typedef int64_t i64;
- #endif
- #define RGFW_INT_DEFINED
-#endif
-
-#ifndef RGFW_BOOL_DEFINED
- #define RGFW_BOOL_DEFINED
- typedef u8 RGFW_bool;
-#endif
-
-#define RGFW_BOOL(x) (RGFW_bool)((x) ? RGFW_TRUE : RGFW_FALSE) /* force an value to be 0 or 1 */
-#define RGFW_TRUE (RGFW_bool)1
-#define RGFW_FALSE (RGFW_bool)0
-
-/* these OS macros look better & are standardized */
-/* plus it helps with cross-compiling */
-
-#ifdef __EMSCRIPTEN__
- #define RGFW_WASM
-
- #if !defined(RGFW_NO_API) && !defined(RGFW_WEBGPU)
- #define RGFW_OPENGL
- #endif
-
- #ifdef RGFW_EGL
- #undef RGFW_EGL
- #endif
-
- #include
- #include
-
- #ifdef RGFW_WEBGPU
- #include
- #endif
-#endif
-
-#if defined(RGFW_X11) && defined(__APPLE__) && !defined(RGFW_CUSTOM_BACKEND)
- #define RGFW_MACOS_X11
- #define RGFW_UNIX
- #undef __APPLE__
-#endif
-
-#if defined(_WIN32) && !defined(RGFW_X11) && !defined(RGFW_UNIX) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND) /* (if you're using X11 on windows some how) */
- #define RGFW_WINDOWS
- /* make sure the correct architecture is defined */
- #if defined(_WIN64)
- #define _AMD64_
- #undef _X86_
- #else
- #undef _AMD64_
- #ifndef _X86_
- #define _X86_
- #endif
- #endif
-
- #ifndef RGFW_NO_XINPUT
- #ifdef __MINGW32__ /* try to find the right header */
- #include
- #else
- #include
- #endif
- #endif
-#endif
-#if defined(RGFW_WAYLAND)
- #define RGFW_DEBUG /* wayland will be in debug mode by default for now */
- #if !defined(RGFW_NO_API) && (!defined(RGFW_BUFFER) || defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA)
- #define RGFW_EGL
- #define RGFW_OPENGL
- #include
- #endif
-
- #define RGFW_UNIX
- #include
-#endif
-#if !defined(RGFW_NO_X11) && (defined(__unix__) || defined(RGFW_MACOS_X11) || defined(RGFW_X11)) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND)
- #define RGFW_MACOS_X11
- #define RGFW_X11
- #define RGFW_UNIX
- #include
- #include
-#elif defined(__APPLE__) && !defined(RGFW_MACOS_X11) && !defined(RGFW_X11) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND)
- #define RGFW_MACOS
- #if !defined(RGFW_BUFFER_BGR)
- #define RGFW_BUFFER_BGR
- #else
- #undef RGFW_BUFFER_BGR
- #endif
-#endif
-
-#if (defined(RGFW_OPENGL_ES1) || defined(RGFW_OPENGL_ES2) || defined(RGFW_OPENGL_ES3)) && !defined(RGFW_EGL)
- #define RGFW_EGL
-#endif
-
-#if !defined(RGFW_OSMESA) && !defined(RGFW_EGL) && !defined(RGFW_OPENGL) && !defined(RGFW_DIRECTX) && !defined(RGFW_BUFFER) && !defined(RGFW_NO_API)
- #define RGFW_OPENGL
-#endif
-
-#ifdef RGFW_EGL
- #include
-#elif defined(RGFW_OSMESA)
- #ifdef RGFW_WINDOWS
- #define OEMRESOURCE
- #include
- #ifndef GLAPIENTRY
- #define GLAPIENTRY APIENTRY
- #endif
- #ifndef GLAPI
- #define GLAPI WINGDIAPI
- #endif
- #endif
-
- #ifndef __APPLE__
- #include
- #else
- #include
- #endif
-#endif
-
-#if (defined(RGFW_OPENGL) || defined(RGFW_WEGL)) && defined(_MSC_VER)
- #pragma comment(lib, "opengl32")
-#endif
-
-#if defined(RGFW_OPENGL) && defined(RGFW_X11)
- #ifndef GLX_MESA_swap_control
- #define GLX_MESA_swap_control
- #endif
- #include /* GLX defs, xlib.h, gl.h */
-#endif
-
-#define RGFW_COCOA_FRAME_NAME NULL
-
-/*! (unix) Toggle use of wayland. This will be on by default if you use `RGFW_WAYLAND` (if you don't use RGFW_WAYLAND, you don't expose WAYLAND functions)
- this is mostly used to allow you to force the use of XWayland
-*/
-RGFWDEF void RGFW_useWayland(RGFW_bool wayland);
-RGFWDEF RGFW_bool RGFW_usingWayland(void);
-/*
- regular RGFW stuff
-*/
-
-#define RGFW_key u8
-
-typedef RGFW_ENUM(u8, RGFW_eventType) {
- /*! event codes */
- RGFW_eventNone = 0, /*!< no event has been sent */
- RGFW_keyPressed, /* a key has been pressed */
- RGFW_keyReleased, /*!< a key has been released */
- /*! key event note
- the code of the key pressed is stored in
- RGFW_event.key
- !!Keycodes defined at the bottom of the RGFW_HEADER part of this file!!
-
- while a string version is stored in
- RGFW_event.KeyString
-
- RGFW_event.keyMod holds the current keyMod
- this means if CapsLock, NumLock are active or not
- */
- RGFW_mouseButtonPressed, /*!< a mouse button has been pressed (left,middle,right) */
- RGFW_mouseButtonReleased, /*!< a mouse button has been released (left,middle,right) */
- RGFW_mousePosChanged, /*!< 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
- */
- RGFW_gamepadConnected, /*!< a gamepad was connected */
- RGFW_gamepadDisconnected, /*!< a gamepad was disconnected */
- RGFW_gamepadButtonPressed, /*!< a gamepad button was pressed */
- RGFW_gamepadButtonReleased, /*!< a gamepad button was released */
- RGFW_gamepadAxisMove, /*!< an axis of a gamepad was moved */
- /*! gamepad event note
- RGFW_event.gamepad holds which gamepad was altered, if any
- RGFW_event.button holds which gamepad button was pressed
-
- RGFW_event.axis holds the data of all the axises
- RGFW_event.axisesCount says how many axises there are
- */
- RGFW_windowMoved, /*!< the window was moved (by the user) */
- RGFW_windowResized, /*!< the window was resized (by the user), [on WASM this means the browser was resized] */
- RGFW_focusIn, /*!< window is in focus now */
- RGFW_focusOut, /*!< window is out of focus now */
- RGFW_mouseEnter, /* mouse entered the window */
- RGFW_mouseLeave, /* mouse left the window */
- RGFW_windowRefresh, /* The window content needs to be refreshed */
-
- /* 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
- */
- RGFW_quit, /*!< the user clicked the quit button */
- RGFW_DND, /*!< a file has been dropped into the window */
- RGFW_DNDInit, /*!< 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
- */
- RGFW_windowMaximized, /*!< the window was maximized */
- RGFW_windowMinimized, /*!< the window was minimized */
- RGFW_windowRestored, /*!< the window was restored */
- RGFW_scaleUpdated /*!< content scale factor changed */
-};
-
-/*! mouse button codes (RGFW_event.button) */
-typedef RGFW_ENUM(u8, RGFW_mouseButton) {
- RGFW_mouseLeft = 0, /*!< left mouse button is pressed */
- RGFW_mouseMiddle, /*!< mouse-wheel-button is pressed */
- RGFW_mouseRight, /*!< right mouse button is pressed */
- RGFW_mouseScrollUp, /*!< mouse wheel is scrolling up */
- RGFW_mouseScrollDown, /*!< mouse wheel is scrolling down */
- RGFW_mouseMisc1, RGFW_mouseMisc2, RGFW_mouseMisc3, RGFW_mouseMisc4, RGFW_mouseMisc5,
- RGFW_mouseFinal
-};
-
-#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
-
-#define RGFW_BIT(x) (1 << x)
-
-/* for RGFW_event.lockstate */
-typedef RGFW_ENUM(u8, RGFW_keymod) {
- RGFW_modCapsLock = RGFW_BIT(0),
- RGFW_modNumLock = RGFW_BIT(1),
- RGFW_modControl = RGFW_BIT(2),
- RGFW_modAlt = RGFW_BIT(3),
- RGFW_modShift = RGFW_BIT(4),
- RGFW_modSuper = RGFW_BIT(5),
- RGFW_modScrollLock = RGFW_BIT(6)
-};
-
-/*! gamepad button codes (based on xbox/playstation), you may need to change these values per controller */
-typedef RGFW_ENUM(u8, RGFW_gamepadCodes) {
- RGFW_gamepadNone = 0, /*!< or PS X button */
- RGFW_gamepadA, /*!< or PS X button */
- RGFW_gamepadB, /*!< or PS circle button */
- RGFW_gamepadY, /*!< or PS triangle button */
- RGFW_gamepadX, /*!< or PS square button */
- RGFW_gamepadStart, /*!< start button */
- RGFW_gamepadSelect, /*!< select button */
- RGFW_gamepadHome, /*!< home button */
- RGFW_gamepadUp, /*!< dpad up */
- RGFW_gamepadDown, /*!< dpad down */
- RGFW_gamepadLeft, /*!< dpad left */
- RGFW_gamepadRight, /*!< dpad right */
- RGFW_gamepadL1, /*!< left bump */
- RGFW_gamepadL2, /*!< left trigger */
- RGFW_gamepadR1, /*!< right bumper */
- RGFW_gamepadR2, /*!< right trigger */
- RGFW_gamepadL3, /* left thumb stick */
- RGFW_gamepadR3, /*!< right thumb stick */
- RGFW_gamepadFinal
-};
-
-/*! basic vector type, if there's not already a point/vector type of choice */
-#ifndef RGFW_point
- typedef struct RGFW_point { i32 x, y; } RGFW_point;
-#endif
-
-/*! basic rect type, if there's not already a rect type of choice */
-#ifndef RGFW_rect
- typedef struct RGFW_rect { 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 RGFW_area { u32 w, h; } RGFW_area;
-#endif
-
-#if defined(__cplusplus) && !defined(__APPLE__)
-#define RGFW_POINT(x, y) {(i32)x, (i32)y}
-#define RGFW_RECT(x, y, w, h) {(i32)x, (i32)y, (i32)w, (i32)h}
-#define RGFW_AREA(w, h) {(u32)w, (u32)h}
-#else
-#define RGFW_POINT(x, y) (RGFW_point){(i32)(x), (i32)(y)}
-#define RGFW_RECT(x, y, w, h) (RGFW_rect){(i32)(x), (i32)(y), (i32)(w), (i32)(h)}
-#define RGFW_AREA(w, h) (RGFW_area){(u32)(w), (u32)(h)}
-#endif
-
-#ifndef RGFW_NO_MONITOR
- /* monitor mode data | can be changed by the user (with functions)*/
- typedef struct RGFW_monitorMode {
- RGFW_area area; /*!< monitor workarea size */
- u32 refreshRate; /*!< monitor refresh rate */
- u8 red, blue, green;
- } RGFW_monitorMode;
-
- /*! structure for monitor data */
- typedef struct RGFW_monitor {
- i32 x, y; /*!< x - y of the monitor workarea */
- char name[128]; /*!< monitor name */
- float scaleX, scaleY; /*!< monitor content scale */
- float pixelRatio; /*!< pixel ratio for monitor (1.0 for regular, 2.0 for hiDPI) */
- float physW, physH; /*!< monitor physical size in inches */
-
- RGFW_monitorMode mode;
- } RGFW_monitor;
-
- /*! get an array of all the monitors (max 6) */
- RGFWDEF RGFW_monitor* RGFW_getMonitors(size_t* len);
- /*! get the primary monitor */
- RGFWDEF RGFW_monitor RGFW_getPrimaryMonitor(void);
-
- typedef RGFW_ENUM(u8, RGFW_modeRequest) {
- RGFW_monitorScale = RGFW_BIT(0), /*!< scale the monitor size */
- RGFW_monitorRefresh = RGFW_BIT(1), /*!< change the refresh rate */
- RGFW_monitorRGB = RGFW_BIT(2), /*!< change the monitor RGB bits size */
- RGFW_monitorAll = RGFW_monitorScale | RGFW_monitorRefresh | RGFW_monitorRGB
- };
-
- /*! request a specific mode */
- RGFWDEF RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request);
- /*! check if 2 monitor modes are the same */
- RGFWDEF RGFW_bool RGFW_monitorModeCompare(RGFW_monitorMode mon, RGFW_monitorMode mon2, RGFW_modeRequest request);
-#endif
-
-/* RGFW mouse loading */
-typedef void RGFW_mouse;
-
-/*!< loads mouse icon from bitmap (similar to RGFW_window_setIcon). Icon NOT resized by default */
-RGFWDEF RGFW_mouse* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels);
-/*!< frees RGFW_mouse data */
-RGFWDEF void RGFW_freeMouse(RGFW_mouse* mouse);
-
-/* NOTE: some parts of the data can represent different things based on the event (read comments in RGFW_event struct) */
-/*! Event structure for checking/getting events */
-typedef struct RGFW_event {
- RGFW_eventType type; /*!< which event has been sent?*/
- RGFW_point point; /*!< mouse x, y of event (or drop point) */
- RGFW_point vector; /*!< raw mouse movement */
- float scaleX, scaleY; /*!< DPI scaling */
-
- RGFW_key key; /*!< the physical key of the event, refers to where key is physically !!Keycodes defined at the bottom of the RGFW_HEADER part of this file!! */
- u8 keyChar; /*!< mapped key char of the event */
-
- RGFW_bool repeat; /*!< key press event repeated (the key is being held) */
- RGFW_keymod keyMod;
-
- u8 button; /* !< which mouse (or gamepad) button was pressed */
- double scroll; /*!< the raw mouse scroll value */
-
- u16 gamepad; /*! which gamepad this event applies to (if applicable to any) */
- u8 axisesCount; /*!< number of axises */
-
- u8 whichAxis; /* which axis was effected */
- RGFW_point axis[4]; /*!< x, y of axises (-100 to 100) */
-
- /*! drag and drop data */
- /* 260 max paths with a max length of 260 */
- char** droppedFiles; /*!< dropped files */
- size_t droppedFilesCount; /*!< house many files were dropped */
-
- void* _win; /*!< the window this event applies too (for event queue events) */
-} RGFW_event;
-
-/*! source data for the window (used by the APIs) */
-#ifdef RGFW_WINDOWS
-typedef struct RGFW_window_src {
- HWND window; /*!< source window */
- HDC hdc; /*!< source HDC */
- i32 wOffset; /*!< width offset for window */
- i32 hOffset; /*!< height offset for window */
- HICON hIconSmall, hIconBig; /*!< source window icons */
- #if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL)
- HGLRC ctx; /*!< source graphics context */
- #elif defined(RGFW_OSMESA)
- OSMesaContext ctx;
- #elif defined(RGFW_EGL)
- EGLSurface EGL_surface;
- EGLDisplay EGL_display;
- EGLContext EGL_context;
- #endif
-
- #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
- HDC hdcMem;
- HBITMAP bitmap;
- u8* bitmapBits;
- #endif
- RGFW_area maxSize, minSize, aspectRatio; /*!< for setting max/min resize (RGFW_WINDOWS) */
-} RGFW_window_src;
-#elif defined(RGFW_UNIX)
-typedef struct RGFW_window_src {
-#if defined(RGFW_X11)
- Display* display; /*!< source display */
- Window window; /*!< source window */
- #if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL)
- GLXContext ctx; /*!< source graphics context */
- GLXFBConfig bestFbc;
- #elif defined(RGFW_OSMESA)
- OSMesaContext ctx;
- #elif defined(RGFW_EGL)
- EGLSurface EGL_surface;
- EGLDisplay EGL_display;
- EGLContext EGL_context;
- #endif
-
- #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
- XImage* bitmap;
- #endif
- GC gc;
- XVisualInfo visual;
- #ifdef RGFW_ADVANCED_SMOOTH_RESIZE
- i64 counter_value;
- XID counter;
- #endif
- RGFW_rect r;
-#endif /* RGFW_X11 */
-#if defined(RGFW_WAYLAND)
- struct wl_display* wl_display;
- struct wl_surface* surface;
- struct wl_buffer* wl_buffer;
- struct wl_keyboard* keyboard;
-
- struct wl_compositor* compositor;
- struct xdg_surface* xdg_surface;
- struct xdg_toplevel* xdg_toplevel;
- struct zxdg_toplevel_decoration_v1* decoration;
- struct xdg_wm_base* xdg_wm_base;
- struct wl_shm* shm;
- struct wl_seat *seat;
- u8* buffer;
- #if defined(RGFW_EGL)
- struct wl_egl_window* eglWindow;
- #endif
- #if defined(RGFW_EGL) && !defined(RGFW_X11)
- EGLSurface EGL_surface;
- EGLDisplay EGL_display;
- EGLContext EGL_context;
- #elif defined(RGFW_OSMESA) && !defined(RGFW_X11)
- OSMesaContext ctx;
- #endif
-#endif /* RGFW_WAYLAND */
-} RGFW_window_src;
-#endif /* RGFW_UNIX */
-#if defined(RGFW_MACOS)
-typedef struct RGFW_window_src {
- void* window;
-#if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL)
- void* ctx; /*!< source graphics context */
-#elif defined(RGFW_OSMESA)
- OSMesaContext ctx;
-#elif defined(RGFW_EGL)
- EGLSurface EGL_surface;
- EGLDisplay EGL_display;
- EGLContext EGL_context;
-#endif
-
- void* view; /* apple viewpoint thingy */
- void* mouse;
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
-#endif
-} RGFW_window_src;
-#elif defined(RGFW_WASM)
-typedef struct RGFW_window_src {
- #if defined(RGFW_WEBGPU)
- WGPUInstance ctx;
- WGPUDevice device;
- WGPUQueue queue;
- #elif defined(RGFW_OSMESA)
- OSMesaContext ctx;
- #else
- EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx;
- #endif
-} RGFW_window_src;
-#endif
-
-/*! Optional arguments for making a windows */
-typedef RGFW_ENUM(u32, RGFW_windowFlags) {
- RGFW_windowNoInitAPI = RGFW_BIT(0), /* do NOT init an API (including the software rendering buffer) (mostly for bindings. you can also use `#define RGFW_NO_API`) */
- RGFW_windowNoBorder = RGFW_BIT(1), /*!< the window doesn't have a border */
- RGFW_windowNoResize = RGFW_BIT(2), /*!< the window cannot be resized by the user */
- RGFW_windowAllowDND = RGFW_BIT(3), /*!< the window supports drag and drop */
- RGFW_windowHideMouse = RGFW_BIT(4), /*! the window should hide the mouse (can be toggled later on using `RGFW_window_mouseShow`) */
- RGFW_windowFullscreen = RGFW_BIT(5), /*!< the window is fullscreen by default */
- RGFW_windowTransparent = RGFW_BIT(6), /*!< the window is transparent (only properly works on X11 and MacOS, although it's meant for for windows) */
- RGFW_windowCenter = RGFW_BIT(7), /*! center the window on the screen */
- RGFW_windowOpenglSoftware = RGFW_BIT(8), /*! use OpenGL software rendering */
- RGFW_windowCocoaCHDirToRes = RGFW_BIT(9), /*! (cocoa only), change directory to resource folder */
- RGFW_windowScaleToMonitor = RGFW_BIT(10), /*! scale the window to the screen */
- RGFW_windowHide = RGFW_BIT(11), /*! the window is hidden */
- RGFW_windowMaximize = RGFW_BIT(12),
- RGFW_windowCenterCursor = RGFW_BIT(13),
- RGFW_windowFloating = RGFW_BIT(14), /*!< create a floating window */
- RGFW_windowFreeOnClose = RGFW_BIT(15), /*!< free (RGFW_window_close) the RGFW_window struct when the window is closed (by the end user) */
- RGFW_windowFocusOnShow = RGFW_BIT(16), /*!< focus the window when it's shown */
- RGFW_windowMinimize = RGFW_BIT(17), /*!< focus the window when it's shown */
- RGFW_windowFocus = RGFW_BIT(18), /*!< if the window is in focus */
- RGFW_windowedFullscreen = RGFW_windowNoBorder | RGFW_windowMaximize
-};
-
-typedef struct RGFW_window {
- RGFW_window_src src; /*!< src window data */
-
-#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 */
- RGFW_area bufferSize;
-#endif
- void* userPtr; /* ptr for usr data */
-
- RGFW_event event; /*!< current event */
-
- RGFW_rect r; /*!< the x, y, w and h of the struct */
-
- /*! which key RGFW_window_shouldClose checks. Settting this to RGFW_keyNULL disables the feature. */
- RGFW_key exitKey;
- RGFW_point _lastMousePoint; /*!< last cusor point (for raw mouse data) */
-
- u32 _flags; /*!< windows flags (for RGFW to check) */
- RGFW_rect _oldRect; /*!< rect before fullscreen */
-} 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 windows */
-#endif
-
-/*! scale monitor to window size */
-RGFWDEF RGFW_bool RGFW_monitor_scaleToWindow(RGFW_monitor mon, RGFW_window* win);
-
-/** * @defgroup Window_management
-* @{ */
-
-
-/*!
- * the class name for X11 and WinAPI. apps with the same class will be grouped by the WM
- * by default the class name will == the root window's name
-*/
-RGFWDEF void RGFW_setClassName(const char* name);
-RGFWDEF void RGFW_setXInstName(const char* name); /*!< X11 instance name (window name will by used by default) */
-
-/*! (cocoa only) change directory to resource folder */
-RGFWDEF void RGFW_moveToMacOSResourceDir(void);
-
-/* NOTE: (windows) if the executable has an icon resource named RGFW_ICON, it will be set as the initial icon for the window */
-
-RGFWDEF RGFW_window* RGFW_createWindow(
- const char* name, /* name of the window */
- RGFW_rect rect, /* rect of window */
- RGFW_windowFlags flags /* extra arguments ((u32)0 means no flags used)*/
-); /*!< function to create a window and struct */
-
-RGFWDEF RGFW_window* RGFW_createWindowPtr(
- const char* name, /* name of the window */
- RGFW_rect rect, /* rect of window */
- RGFW_windowFlags flags, /* extra arguments (NULL / (u32)0 means no flags used) */
- RGFW_window* win /* ptr to the window struct you want to use */
-); /*!< function to create a window (without allocating a window struct) */
-
-RGFWDEF void RGFW_window_initBuffer(RGFW_window* win);
-RGFWDEF void RGFW_window_initBufferSize(RGFW_window* win, RGFW_area area);
-RGFWDEF void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area);
-
-/*! set the window flags (will undo flags if they don't match the old ones) */
-RGFWDEF void RGFW_window_setFlags(RGFW_window* win, RGFW_windowFlags);
-
-/*! 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]
-
- this function is optional if you choose to use event callbacks,
- although you still need some way to tell RGFW to process events eg. `RGFW_window_checkEvents`
-*/
-
-RGFWDEF RGFW_event* RGFW_window_checkEvent(RGFW_window* win); /*!< check current event (returns a pointer to win->event or NULL if there is no event)*/
-
-/*!
- for RGFW_window_eventWait and RGFW_window_checkEvents
- waitMS -> Allows the function to keep checking for events even after `RGFW_window_checkEvent == NULL`
- if waitMS == 0, the loop will not wait for events
- if waitMS > 0, the loop will wait that many miliseconds after there are no more events until it returns
- if waitMS == -1 or waitMS == the max size of an unsigned 32-bit int, the loop will not return until it gets another event
-*/
-typedef RGFW_ENUM(i32, RGFW_eventWait) {
- RGFW_eventNoWait = 0,
- RGFW_eventWaitNext = -1
-};
-
-/*! sleep until RGFW gets an event or the timer ends (defined by OS) */
-RGFWDEF void RGFW_window_eventWait(RGFW_window* win, i32 waitMS);
-
-/*!
- check all the events until there are none left.
- This should only be used if you're using callbacks only
-*/
-RGFWDEF void RGFW_window_checkEvents(RGFW_window* win, i32 waitMS);
-
-/*!
- tell RGFW_window_eventWait to stop waiting (to be ran from another thread)
-*/
-RGFWDEF void RGFW_stopCheckEvents(void);
-
-/*! window managment functions */
-RGFWDEF void RGFW_window_close(RGFW_window* win); /*!< close the window and free leftover data */
-
-/*! move a window to a given point */
-RGFWDEF void RGFW_window_move(RGFW_window* win,
- RGFW_point v /*!< new pos */
-);
-
-#ifndef RGFW_NO_MONITOR
- /*! move window to a specific monitor */
- RGFWDEF void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m /* monitor */);
-#endif
-
-/*! resize window to a current size/area */
-RGFWDEF void RGFW_window_resize(RGFW_window* win, /*!< source window */
- RGFW_area a /*!< new size */
-);
-
-/*! set window aspect ratio */
-RGFWDEF void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a);
-/*! set the minimum dimensions of a window */
-RGFWDEF void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a);
-/*! set the maximum dimensions of a window */
-RGFWDEF void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a);
-
-RGFWDEF void RGFW_window_focus(RGFW_window* win); /*!< sets the focus to this window */
-RGFWDEF RGFW_bool RGFW_window_isInFocus(RGFW_window* win); /*!< checks the focus to this window */
-RGFWDEF void RGFW_window_raise(RGFW_window* win); /*!< raise the window (to the top) */
-RGFWDEF void RGFW_window_maximize(RGFW_window* win); /*!< maximize the window */
-RGFWDEF void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen); /*!< turn fullscreen on / off for a window */
-RGFWDEF void RGFW_window_center(RGFW_window* win); /*!< center the window */
-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_setFloating(RGFW_window* win, RGFW_bool floating); /*!< make the window a floating window */
-RGFWDEF void RGFW_window_setOpacity(RGFW_window* win, u8 opacity); /*!< sets the opacity of a window */
-
-RGFWDEF RGFW_bool RGFW_window_opengl_isSoftware(RGFW_window* win);
-
-/*! if the window should have a border or not (borderless) based on bool value of `border` */
-RGFWDEF void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border);
-RGFWDEF RGFW_bool RGFW_window_borderless(RGFW_window* win);
-
-/*! turn on / off dnd (RGFW_windowAllowDND stil must be passed to the window)*/
-RGFWDEF void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow);
-/*! check if DND is allowed */
-RGFWDEF RGFW_bool RGFW_window_allowsDND(RGFW_window* win);
-
-
-#ifndef RGFW_NO_PASSTHROUGH
- /*! turn on / off mouse passthrough */
- RGFWDEF void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough);
-#endif
-
-/*! rename window to a given string */
-RGFWDEF void RGFW_window_setName(RGFW_window* win,
- const char* name
-);
-
-RGFWDEF RGFW_bool 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 MAY be resized by default, set both the taskbar and window icon */
-
-typedef RGFW_ENUM(u8, RGFW_icon) {
- RGFW_iconTaskbar = RGFW_BIT(0),
- RGFW_iconWindow = RGFW_BIT(1),
- RGFW_iconBoth = RGFW_iconTaskbar | RGFW_iconWindow
-};
-RGFWDEF RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* icon, RGFW_area a, i32 channels, u8 type);
-
-/*!< sets mouse to RGFW_mouse icon (loaded from a bitmap struct) */
-RGFWDEF void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse);
-
-/*!< sets the mouse to a standard API cursor (based on RGFW_MOUSE, as seen at the end of the RGFW_HEADER part of this file) */
-RGFWDEF RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse);
-
-RGFWDEF RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win); /*!< sets the mouse to the default mouse icon */
-/*
- Locks cursor at the center of the window
- win->event.point becomes raw mouse movement data
-
- this is useful for a 3D camera
-*/
-RGFWDEF void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area);
-/*! if the mouse is held by RGFW */
-RGFWDEF RGFW_bool RGFW_window_mouseHeld(RGFW_window* win);
-/*! stop holding the mouse and let it move freely */
-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 or overrides a window close
- by modifying window flags
-*/
-RGFWDEF void RGFW_window_setShouldClose(RGFW_window* win, RGFW_bool shouldClose);
-
-/*! where the mouse is on the screen */
-RGFWDEF RGFW_point RGFW_getGlobalMousePoint(void);
-
-/*! where the mouse is on the window */
-RGFWDEF RGFW_point RGFW_window_getMousePoint(RGFW_window* win);
-
-/*! show the mouse or hide the mouse */
-RGFWDEF void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show);
-/*! if the mouse is hidden */
-RGFWDEF RGFW_bool RGFW_window_mouseHidden(RGFW_window* win);
-/*! move the mouse to a given point */
-RGFWDEF void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v);
-
-/*! if the window should close (RGFW_close was sent or escape was pressed) */
-RGFWDEF RGFW_bool RGFW_window_shouldClose(RGFW_window* win);
-/*! if the window is fullscreen */
-RGFWDEF RGFW_bool RGFW_window_isFullscreen(RGFW_window* win);
-/*! if the window is hidden */
-RGFWDEF RGFW_bool RGFW_window_isHidden(RGFW_window* win);
-/*! if the window is minimized */
-RGFWDEF RGFW_bool RGFW_window_isMinimized(RGFW_window* win);
-/*! if the window is maximized */
-RGFWDEF RGFW_bool RGFW_window_isMaximized(RGFW_window* win);
-/*! if the window is floating */
-RGFWDEF RGFW_bool RGFW_window_isFloating(RGFW_window* win);
-/** @} */
-
-/** * @defgroup Monitor
-* @{ */
-
-#ifndef RGFW_NO_MONITOR
-/*
- scale the window to the monitor.
- This is run by default if the user uses the arg `RGFW_scaleToMonitor` 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);
-#endif
-
-/** @} */
-
-/** * @defgroup Input
-* @{ */
-
-/*! 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 RGFW_bool RGFW_isPressed(RGFW_window* win, RGFW_key key); /*!< if key is pressed (key code)*/
-
-RGFWDEF RGFW_bool RGFW_wasPressed(RGFW_window* win, RGFW_key key); /*!< if key was pressed (checks previous state only) (key code) */
-
-RGFWDEF RGFW_bool RGFW_isHeld(RGFW_window* win, RGFW_key key); /*!< if key is held (key code) */
-RGFWDEF RGFW_bool RGFW_isReleased(RGFW_window* win, RGFW_key key); /*!< if key is released (key code) */
-
-/* if a key is pressed and then released, pretty much the same as RGFW_isReleased */
-RGFWDEF RGFW_bool RGFW_isClicked(RGFW_window* win, RGFW_key key /*!< key code */);
-
-/*! if a mouse button is pressed */
-RGFWDEF RGFW_bool RGFW_isMousePressed(RGFW_window* win, RGFW_mouseButton button /*!< mouse button code */ );
-/*! if a mouse button is held */
-RGFWDEF RGFW_bool RGFW_isMouseHeld(RGFW_window* win, RGFW_mouseButton button /*!< mouse button code */ );
-/*! if a mouse button was released */
-RGFWDEF RGFW_bool RGFW_isMouseReleased(RGFW_window* win, RGFW_mouseButton button /*!< mouse button code */ );
-/*! if a mouse button was pressed (checks previous state only) */
-RGFWDEF RGFW_bool RGFW_wasMousePressed(RGFW_window* win, RGFW_mouseButton button /*!< mouse button code */ );
-/** @} */
-
-/** * @defgroup Clipboard
-* @{ */
-typedef ptrdiff_t RGFW_ssize_t;
-
-RGFWDEF const char* RGFW_readClipboard(size_t* size); /*!< read clipboard data */
-/*! read clipboard data or send a NULL str to just get the length of the clipboard data */
-RGFWDEF RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity);
-RGFWDEF void RGFW_writeClipboard(const char* text, u32 textLen); /*!< write text to the clipboard */
-/** @} */
-
-
-
-/** * @defgroup error handling
-* @{ */
-typedef RGFW_ENUM(u8, RGFW_debugType) {
- RGFW_typeError = 0, RGFW_typeWarning, RGFW_typeInfo
-};
-
-typedef RGFW_ENUM(u8, RGFW_errorCode) {
- RGFW_noError = 0, /*!< no error */
- RGFW_errOpenglContext, RGFW_errEGLContext, /*!< error with the OpenGL context */
- RGFW_errWayland,
- RGFW_errDirectXContext,
- RGFW_errIOKit,
- RGFW_errClipboard,
- RGFW_errFailedFuncLoad,
- RGFW_errBuffer,
- RGFW_infoMonitor, RGFW_infoWindow, RGFW_infoBuffer, RGFW_infoGlobal, RGFW_infoOpenGL,
- RGFW_warningWayland, RGFW_warningOpenGL
-};
-
-typedef struct RGFW_debugContext { RGFW_window* win; RGFW_monitor* monitor; u32 srcError; } RGFW_debugContext;
-
-#if defined(__cplusplus) && !defined(__APPLE__)
-#define RGFW_DEBUG_CTX(win, err) {win, NULL, err}
-#define RGFW_DEBUG_CTX_MON(monitor) {_RGFW.root, &monitor, 0}
-#else
-#define RGFW_DEBUG_CTX(win, err) (RGFW_debugContext){win, NULL, err}
-#define RGFW_DEBUG_CTX_MON(monitor) (RGFW_debugContext){_RGFW.root, &monitor, 0}
-#endif
-
-typedef void (* RGFW_debugfunc)(RGFW_debugType type, RGFW_errorCode err, RGFW_debugContext ctx, const char* msg);
-RGFWDEF RGFW_debugfunc RGFW_setDebugCallback(RGFW_debugfunc func);
-RGFWDEF void RGFW_sendDebugInfo(RGFW_debugType type, RGFW_errorCode err, RGFW_debugContext ctx, const char* msg);
-/** @} */
-
-/**
-
-
- event callbacks.
- These are completely optional, so you can use the normal
- RGFW_checkEvent() method if you prefer that
-
-* @defgroup Callbacks
-* @{
-*/
-
-/*! RGFW_windowMoved, the window and its new rect value */
-typedef void (* RGFW_windowMovedfunc)(RGFW_window* win, RGFW_rect r);
-/*! RGFW_windowResized, the window and its new rect value */
-typedef void (* RGFW_windowResizedfunc)(RGFW_window* win, RGFW_rect r);
-/*! RGFW_windowRestored, the window and its new rect value */
-typedef void (* RGFW_windowRestoredfunc)(RGFW_window* win, RGFW_rect r);
-/*! RGFW_windowMaximized, the window and its new rect value */
-typedef void (* RGFW_windowMaximizedfunc)(RGFW_window* win, RGFW_rect r);
-/*! RGFW_windowMinimized, the window and its new rect value */
-typedef void (* RGFW_windowMinimizedfunc)(RGFW_window* win, RGFW_rect r);
-/*! RGFW_quit, the window that was closed */
-typedef void (* RGFW_windowQuitfunc)(RGFW_window* win);
-/*! RGFW_focusIn / RGFW_focusOut, the window who's focus has changed and if its in focus */
-typedef void (* RGFW_focusfunc)(RGFW_window* win, RGFW_bool inFocus);
-/*! RGFW_mouseEnter / RGFW_mouseLeave, the window that changed, the point of the mouse (enter only) and if the mouse has entered */
-typedef void (* RGFW_mouseNotifyfunc)(RGFW_window* win, RGFW_point point, RGFW_bool status);
-/*! RGFW_mousePosChanged, the window that the move happened on, and the new point of the mouse */
-typedef void (* RGFW_mousePosfunc)(RGFW_window* win, RGFW_point point, RGFW_point vector);
-/*! RGFW_DNDInit, the window, the point of the drop on the windows */
-typedef void (* RGFW_dndInitfunc)(RGFW_window* win, RGFW_point point);
-/*! RGFW_windowRefresh, the window that needs to be refreshed */
-typedef void (* RGFW_windowRefreshfunc)(RGFW_window* win);
-/*! RGFW_keyPressed / RGFW_keyReleased, the window that got the event, the mapped key, the physical key, the string version, the state of the mod keys, if it was a press (else it's a release) */
-typedef void (* RGFW_keyfunc)(RGFW_window* win, u8 key, u8 keyChar, RGFW_keymod keyMod, RGFW_bool pressed);
-/*! RGFW_mouseButtonPressed / RGFW_mouseButtonReleased, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release) */
-typedef void (* RGFW_mouseButtonfunc)(RGFW_window* win, RGFW_mouseButton button, double scroll, RGFW_bool pressed);
-/*! RGFW_gamepadButtonPressed, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release) */
-typedef void (* RGFW_gamepadButtonfunc)(RGFW_window* win, u16 gamepad, u8 button, RGFW_bool pressed);
-/*! RGFW_gamepadAxisMove, the window that got the event, the gamepad in question, the axis values and the axis count */
-typedef void (* RGFW_gamepadAxisfunc)(RGFW_window* win, u16 gamepad, RGFW_point axis[2], u8 axisesCount, u8 whichAxis);
-/*! RGFW_gamepadConnected / RGFW_gamepadDisconnected, the window that got the event, the gamepad in question, if the controller was connected (else it was disconnected) */
-typedef void (* RGFW_gamepadfunc)(RGFW_window* win, u16 gamepad, RGFW_bool connected);
-/*! RGFW_dnd, the window that had the drop, the drop data and the number of files dropped */
-typedef void (* RGFW_dndfunc)(RGFW_window* win, char** droppedFiles, size_t droppedFilesCount);
-/*! RGFW_scaleUpdated, the window the event was sent to, content scaleX, content scaleY */
-typedef void (* RGFW_scaleUpdatedfunc)(RGFW_window* win, float scaleX, float scaleY);
-
-/*! set callback for a window move event. Returns previous callback function (if it was set) */
-RGFWDEF RGFW_windowMovedfunc RGFW_setWindowMovedCallback(RGFW_windowMovedfunc func);
-/*! set callback for a window resize event. Returns previous callback function (if it was set) */
-RGFWDEF RGFW_windowResizedfunc RGFW_setWindowResizedCallback(RGFW_windowResizedfunc func);
-/*! set callback for a window quit event. Returns previous callback function (if it was set) */
-RGFWDEF RGFW_windowQuitfunc RGFW_setWindowQuitCallback(RGFW_windowQuitfunc func);
-/*! set callback for a mouse move event. Returns previous callback function (if it was set) */
-RGFWDEF RGFW_mousePosfunc RGFW_setMousePosCallback(RGFW_mousePosfunc func);
-/*! set callback for a window refresh event. Returns previous callback function (if it was set) */
-RGFWDEF RGFW_windowRefreshfunc RGFW_setWindowRefreshCallback(RGFW_windowRefreshfunc func);
-/*! set callback for a window focus change event. Returns previous callback function (if it was set) */
-RGFWDEF RGFW_focusfunc RGFW_setFocusCallback(RGFW_focusfunc func);
-/*! set callback for a mouse notify event. Returns previous callback function (if it was set) */
-RGFWDEF RGFW_mouseNotifyfunc RGFW_setMouseNotifyCallback(RGFW_mouseNotifyfunc func);
-/*! set callback for a drop event event. Returns previous callback function (if it was set) */
-RGFWDEF RGFW_dndfunc RGFW_setDndCallback(RGFW_dndfunc func);
-/*! set callback for a start of a drop event. Returns previous callback function (if it was set) */
-RGFWDEF RGFW_dndInitfunc RGFW_setDndInitCallback(RGFW_dndInitfunc func);
-/*! set callback for a key (press / release) event. Returns previous callback function (if it was set) */
-RGFWDEF RGFW_keyfunc RGFW_setKeyCallback(RGFW_keyfunc func);
-/*! set callback for a mouse button (press / release) event. Returns previous callback function (if it was set) */
-RGFWDEF RGFW_mouseButtonfunc RGFW_setMouseButtonCallback(RGFW_mouseButtonfunc func);
-/*! set callback for a controller button (press / release) event. Returns previous callback function (if it was set) */
-RGFWDEF RGFW_gamepadButtonfunc RGFW_setGamepadButtonCallback(RGFW_gamepadButtonfunc func);
-/*! set callback for a gamepad axis move event. Returns previous callback function (if it was set) */
-RGFWDEF RGFW_gamepadAxisfunc RGFW_setGamepadAxisCallback(RGFW_gamepadAxisfunc func);
-/*! set callback for when a controller is connected or disconnected. Returns the previous callback function (if it was set) */
-RGFWDEF RGFW_gamepadfunc RGFW_setGamepadCallback(RGFW_gamepadfunc func);
-/*! set call back for when window is maximized. Returns the previous callback function (if it was set) */
-RGFWDEF RGFW_windowResizedfunc RGFW_setWindowMaximizedCallback(RGFW_windowResizedfunc func);
-/*! set call back for when window is minimized. Returns the previous callback function (if it was set) */
-RGFWDEF RGFW_windowResizedfunc RGFW_setWindowMinimizedCallback(RGFW_windowResizedfunc func);
-/*! set call back for when window is restored. Returns the previous callback function (if it was set) */
-RGFWDEF RGFW_windowResizedfunc RGFW_setWindowRestoredCallback(RGFW_windowResizedfunc func);
-/*! set callback for when the DPI changes. Returns previous callback function (if it was set) */
-RGFWDEF RGFW_scaleUpdatedfunc RGFW_setScaleUpdatedCallback(RGFW_scaleUpdatedfunc func);
-/** @} */
-
-/** * @defgroup Threads
-* @{ */
-
-#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
-*/
-
-#if defined(__unix__) || defined(__APPLE__) || defined(RGFW_WASM) || defined(RGFW_CUSTOM_BACKEND)
- typedef void* (* RGFW_threadFunc_ptr)(void*);
-#else
- typedef DWORD (__stdcall *RGFW_threadFunc_ptr) (LPVOID lpThreadParameter);
-#endif
-
-RGFWDEF RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, 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
-
-/** @} */
-
-/** * @defgroup gamepad
-* @{ */
-
-typedef RGFW_ENUM(u8, RGFW_gamepadType) {
- RGFW_gamepadMicrosoft = 0, RGFW_gamepadSony, RGFW_gamepadNintendo, RGFW_gamepadLogitech, RGFW_gamepadUnknown
-};
-
-/*! gamepad count starts at 0*/
-RGFWDEF u32 RGFW_isPressedGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button);
-RGFWDEF u32 RGFW_isReleasedGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button);
-RGFWDEF u32 RGFW_isHeldGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button);
-RGFWDEF u32 RGFW_wasPressedGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button);
-RGFWDEF RGFW_point RGFW_getGamepadAxis(RGFW_window* win, u16 controller, u16 whichAxis);
-RGFWDEF const char* RGFW_getGamepadName(RGFW_window* win, u16 controller);
-RGFWDEF size_t RGFW_getGamepadCount(RGFW_window* win);
-RGFWDEF RGFW_gamepadType RGFW_getGamepadType(RGFW_window* win, u16 controller);
-
-/** @} */
-
-/** * @defgroup graphics_API
-* @{ */
-
-/*!< make the window the current opengl drawing context
-
- NOTE:
- if you want to switch the graphics context's thread,
- you have to run RGFW_window_makeCurrent(NULL); on the old thread
- then RGFW_window_makeCurrent(valid_window) on the new thread
-*/
-RGFWDEF void RGFW_window_makeCurrent(RGFW_window* win);
-
-/*! get current RGFW window graphics context */
-RGFWDEF RGFW_window* RGFW_getCurrent(void);
-
-/* 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);
-/*!< render the software rendering buffer (this is called by RGFW_window_swapInterval) */
-RGFWDEF void RGFW_window_swapBuffers_software(RGFW_window* win);
-
-typedef void (*RGFW_proc)(void); /* function pointer equivalent of void* */
-
-/*! native API functions */
-#if defined(RGFW_OPENGL) || defined(RGFW_EGL)
-/*!< create an opengl context for the RGFW window, run by createWindow by default (unless the RGFW_windowNoInitAPI is included) */
-RGFWDEF void RGFW_window_initOpenGL(RGFW_window* win);
-/*!< called by `RGFW_window_close` by default (unless the RGFW_windowNoInitAPI is set) */
-RGFWDEF void RGFW_window_freeOpenGL(RGFW_window* win);
-
-/*! OpenGL init hints */
-typedef RGFW_ENUM(u8, RGFW_glHints) {
- RGFW_glStencil = 0, /*!< set stencil buffer bit size (8 by default) */
- RGFW_glSamples, /*!< set number of sampiling buffers (4 by default) */
- RGFW_glStereo, /*!< use GL_STEREO (GL_FALSE by default) */
- RGFW_glAuxBuffers, /*!< number of aux buffers (0 by default) */
- RGFW_glDoubleBuffer, /*!< request double buffering */
- RGFW_glRed, RGFW_glGreen, RGFW_glBlue, RGFW_glAlpha, /*!< set RGBA bit sizes */
- RGFW_glDepth,
- RGFW_glAccumRed, RGFW_glAccumGreen, RGFW_glAccumBlue,RGFW_glAccumAlpha, /*!< set accumulated RGBA bit sizes */
- RGFW_glSRGB, /*!< request sRGA */
- RGFW_glRobustness, /*!< request a robust context */
- RGFW_glDebug, /*!< request opengl debugging */
- RGFW_glNoError, /*!< request no opengl errors */
- RGFW_glReleaseBehavior,
- RGFW_glProfile,
- RGFW_glMajor, RGFW_glMinor,
- RGFW_glFinalHint = 32, /*!< the final hint (not for setting) */
- RGFW_releaseFlush = 0, RGFW_glReleaseNone, /* RGFW_glReleaseBehavior options */
- RGFW_glCore = 0, RGFW_glCompatibility /*!< RGFW_glProfile options */
-};
-RGFWDEF void RGFW_setGLHint(RGFW_glHints hint, i32 value);
-RGFWDEF RGFW_bool RGFW_extensionSupported(const char* extension, size_t len); /*!< check if whether the specified API extension is supported by the current OpenGL or OpenGL ES context */
-RGFWDEF RGFW_proc RGFW_getProcAddress(const char* procname); /*!< get native opengl proc address */
-RGFWDEF void RGFW_window_makeCurrent_OpenGL(RGFW_window* win); /*!< to be called by RGFW_window_makeCurrent */
-RGFWDEF void RGFW_window_swapBuffers_OpenGL(RGFW_window* win); /*!< swap opengl buffer (only) called by RGFW_window_swapInterval */
-void* RGFW_getCurrent_OpenGL(void); /*!< get the current context (OpenGL backend (GLX) (WGL) (EGL) (cocoa) (webgl))*/
-
-RGFWDEF RGFW_bool RGFW_extensionSupportedPlatform(const char* extension, size_t len); /*!< check if whether the specified platform-specific API extension is supported by the current OpenGL or OpenGL ES context */
-#endif
-#ifdef RGFW_VULKAN
- #if defined(RGFW_WAYLAND) && defined(RGFW_X11)
- #define VK_USE_PLATFORM_WAYLAND_KHR
- #define VK_USE_PLATFORM_XLIB_KHR
- #define RGFW_VK_SURFACE ((RGFW_usingWayland()) ? ("VK_KHR_wayland_surface") : ("VK_KHR_xlib_surface"))
- #elif defined(RGFW_WAYLAND)
- #define VK_USE_PLATFORM_WAYLAND_KHR
- #define VK_USE_PLATFORM_XLIB_KHR
- #define RGFW_VK_SURFACE "VK_KHR_wayland_surface"
- #elif defined(RGFW_X11)
- #define VK_USE_PLATFORM_XLIB_KHR
- #define RGFW_VK_SURFACE "VK_KHR_xlib_surface"
- #elif defined(RGFW_WINDOWS)
- #define VK_USE_PLATFORM_WIN32_KHR
- #define OEMRESOURCE
- #define RGFW_VK_SURFACE "VK_KHR_win32_surface"
- #elif defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11)
- #define VK_USE_PLATFORM_MACOS_MVK
- #define RGFW_VK_SURFACE "VK_MVK_macos_surface"
- #else
- #define RGFW_VK_SURFACE NULL
- #endif
-
-/* if you don't want to use the above macros */
-RGFWDEF const char** RGFW_getVKRequiredInstanceExtensions(size_t* count); /*!< gets (static) extension array (and size (which will be 2)) */
-
-#include
-
-RGFWDEF VkResult RGFW_window_createVKSurface(RGFW_window* win, VkInstance instance, VkSurfaceKHR* surface);
-RGFWDEF RGFW_bool RGFW_getVKPresentationSupport(VkInstance instance, VkPhysicalDevice physicalDevice, u32 queueFamilyIndex);
-#endif
-#ifdef RGFW_DIRECTX
-#ifndef RGFW_WINDOWS
- #undef RGFW_DIRECTX
-#else
- #define OEMRESOURCE
- #include
-
- #ifndef __cplusplus
- #define __uuidof(T) IID_##T
- #endif
-RGFWDEF int RGFW_window_createDXSwapChain(RGFW_window* win, IDXGIFactory* pFactory, IUnknown* pDevice, IDXGISwapChain** swapchain);
-#endif
-#endif
-
-/** @} */
-
-/** * @defgroup Supporting
-* @{ */
-
-/*! optional init/deinit function */
-RGFWDEF i32 RGFW_init(void); /*!< is called by default when the first window is created by default */
-RGFWDEF void RGFW_deinit(void); /*!< is called by default when the last open window is closed */
-
-RGFWDEF double RGFW_getTime(void); /*!< get time in seconds since RGFW_setTime, which ran when the first window is open */
-RGFWDEF u64 RGFW_getTimeNS(void); /*!< get time in nanoseconds RGFW_setTime, which ran when the first window is open */
-RGFWDEF void RGFW_sleep(u64 milisecond); /*!< sleep for a set time */
-RGFWDEF void RGFW_setTime(double time); /*!< set timer in seconds */
-RGFWDEF u64 RGFW_getTimerValue(void); /*!< get API timer value */
-RGFWDEF u64 RGFW_getTimerFreq(void); /*!< get API time freq */
-
-/*< updates fps / sets fps to cap (must by ran manually by the user at the end of a frame), returns current fps */
-RGFWDEF u32 RGFW_checkFPS(double startTime, u32 frameCount, u32 fpsCap);
-
-/*!< change which window is the root window */
-RGFWDEF void RGFW_setRootWindow(RGFW_window* win);
-RGFWDEF RGFW_window* RGFW_getRootWindow(void);
-
-/*! standard event queue, used for injecting events and returning source API callback events like any other queue check */
-/* these are all used internally by RGFW */
-void RGFW_eventQueuePush(RGFW_event event);
-RGFW_event* RGFW_eventQueuePop(RGFW_window* win);
-
-/* for C++ / C89 */
-#define RGFW_eventQueuePushEx(eventInit) { RGFW_event e; eventInit; RGFW_eventQueuePush(e); }
-
-/*!
- key codes and mouse icon enums
-*/
-#undef RGFW_key
-typedef RGFW_ENUM(u8, RGFW_key) {
- RGFW_keyNULL = 0,
- RGFW_escape = '\033',
- RGFW_backtick = '`',
- RGFW_0 = '0',
- RGFW_1 = '1',
- RGFW_2 = '2',
- RGFW_3 = '3',
- RGFW_4 = '4',
- RGFW_5 = '5',
- RGFW_6 = '6',
- RGFW_7 = '7',
- RGFW_8 = '8',
- RGFW_9 = '9',
-
- RGFW_minus = '-',
- RGFW_equals = '=',
- RGFW_backSpace = '\b',
- RGFW_tab = '\t',
- RGFW_space = ' ',
-
- RGFW_a = 'a',
- RGFW_b = 'b',
- RGFW_c = 'c',
- RGFW_d = 'd',
- RGFW_e = 'e',
- RGFW_f = 'f',
- RGFW_g = 'g',
- RGFW_h = 'h',
- RGFW_i = 'i',
- RGFW_j = 'j',
- RGFW_k = 'k',
- RGFW_l = 'l',
- RGFW_m = 'm',
- RGFW_n = 'n',
- RGFW_o = 'o',
- RGFW_p = 'p',
- RGFW_q = 'q',
- RGFW_r = 'r',
- RGFW_s = 's',
- RGFW_t = 't',
- RGFW_u = 'u',
- RGFW_v = 'v',
- RGFW_w = 'w',
- RGFW_x = 'x',
- RGFW_y = 'y',
- RGFW_z = 'z',
-
- RGFW_period = '.',
- RGFW_comma = ',',
- RGFW_slash = '/',
- RGFW_bracket = '[',
- RGFW_closeBracket = ']',
- RGFW_semicolon = ';',
- RGFW_apostrophe = '\'',
- RGFW_backSlash = '\\',
- RGFW_return = '\n',
- RGFW_enter = RGFW_return,
-
- RGFW_delete = '\177', /* 127 */
-
- RGFW_F1,
- RGFW_F2,
- RGFW_F3,
- RGFW_F4,
- RGFW_F5,
- RGFW_F6,
- RGFW_F7,
- RGFW_F8,
- RGFW_F9,
- RGFW_F10,
- RGFW_F11,
- RGFW_F12,
-
- RGFW_capsLock,
- RGFW_shiftL,
- RGFW_controlL,
- RGFW_altL,
- RGFW_superL,
- RGFW_shiftR,
- RGFW_controlR,
- RGFW_altR,
- RGFW_superR,
- RGFW_up,
- RGFW_down,
- RGFW_left,
- RGFW_right,
- RGFW_insert,
- RGFW_end,
- RGFW_home,
- RGFW_pageUp,
- RGFW_pageDown,
-
- RGFW_numLock,
- RGFW_KP_Slash,
- RGFW_multiply,
- RGFW_KP_Minus,
- RGFW_KP_1,
- RGFW_KP_2,
- RGFW_KP_3,
- RGFW_KP_4,
- RGFW_KP_5,
- RGFW_KP_6,
- RGFW_KP_7,
- RGFW_KP_8,
- RGFW_KP_9,
- RGFW_KP_0,
- RGFW_KP_Period,
- RGFW_KP_Return,
- RGFW_scrollLock,
- RGFW_printScreen,
- RGFW_pause,
- RGFW_keyLast = 256 /* padding for alignment ~(175 by default) */
- };
-
-
-/*! converts api keycode to the RGFW unmapped/physical key */
-RGFWDEF u32 RGFW_apiKeyToRGFW(u32 keycode);
-/*! converts RGFW keycode to the unmapped/physical api key */
-RGFWDEF u32 RGFW_rgfwToApiKey(u32 keycode);
-/*! converts RGFW keycode to the mapped keychar */
-RGFWDEF u8 RGFW_rgfwToKeyChar(u32 keycode);
-
-typedef RGFW_ENUM(u8, RGFW_mouseIcons) {
- RGFW_mouseNormal = 0,
- RGFW_mouseArrow,
- RGFW_mouseIbeam,
- RGFW_mouseCrosshair,
- RGFW_mousePointingHand,
- RGFW_mouseResizeEW,
- RGFW_mouseResizeNS,
- RGFW_mouseResizeNWSE,
- RGFW_mouseResizeNESW,
- RGFW_mouseResizeAll,
- RGFW_mouseNotAllowed,
- RGFW_mouseIconFinal = 16 /* padding for alignment */
-};
-/** @} */
-
-#endif /* RGFW_HEADER */
-#if defined(RGFW_X11) || defined(RGFW_WAYLAND)
- #define RGFW_OS_BASED_VALUE(l, w, m, h) l
-#elif defined(RGFW_WINDOWS)
- #define RGFW_OS_BASED_VALUE(l, w, m, h) w
-#elif defined(RGFW_MACOS)
- #define RGFW_OS_BASED_VALUE(l, w, m, h) m
-#elif defined(RGFW_WASM)
- #define RGFW_OS_BASED_VALUE(l, w, m, h) h
-#endif
-
-
-#ifdef RGFW_IMPLEMENTATION
-RGFW_bool RGFW_useWaylandBool = 1;
-void RGFW_useWayland(RGFW_bool wayland) { RGFW_useWaylandBool = wayland; }
-RGFW_bool RGFW_usingWayland(void) { return RGFW_useWaylandBool; }
-
-#if !defined(RGFW_NO_X11) && defined(RGFW_WAYLAND)
-#define RGFW_GOTO_WAYLAND(fallback) if (RGFW_useWaylandBool && fallback == 0) goto wayland
-#define RGFW_WAYLAND_LABEL wayland:;
-#else
-#define RGFW_GOTO_WAYLAND(fallback)
-#define RGFW_WAYLAND_LABEL
-#endif
-
-char* RGFW_clipboard_data;
-void RGFW_clipboard_switch(char* newstr);
-void RGFW_clipboard_switch(char* newstr) {
- if (RGFW_clipboard_data != NULL)
- RGFW_FREE(RGFW_clipboard_data);
- RGFW_clipboard_data = newstr;
-}
-
-#define RGFW_CHECK_CLIPBOARD() \
- if (size <= 0 && RGFW_clipboard_data != NULL) \
- return (const char*)RGFW_clipboard_data; \
- else if (size <= 0) \
- return "\0";
-
-const char* RGFW_readClipboard(size_t* len) {
- RGFW_ssize_t size = RGFW_readClipboardPtr(NULL, 0);
- RGFW_CHECK_CLIPBOARD();
- char* str = (char*)RGFW_ALLOC((size_t)size);
- RGFW_ASSERT(str != NULL);
- str[0] = '\0';
-
- size = RGFW_readClipboardPtr(str, (size_t)size);
-
- RGFW_CHECK_CLIPBOARD();
-
- if (len != NULL) *len = (size_t)size;
-
- RGFW_clipboard_switch(str);
- return (const char*)str;
-}
-
-RGFW_debugfunc RGFW_debugCallback = NULL;
-RGFW_debugfunc RGFW_setDebugCallback(RGFW_debugfunc func) {
- RGFW_debugfunc RGFW_debugCallbackPrev = RGFW_debugCallback;
- RGFW_debugCallback = func;
- return RGFW_debugCallbackPrev;
-}
-
-#ifdef RGFW_DEBUG
-#include
-#endif
-
-void RGFW_sendDebugInfo(RGFW_debugType type, RGFW_errorCode err, RGFW_debugContext ctx, const char* msg) {
- if (RGFW_debugCallback) RGFW_debugCallback(type, err, ctx, msg);
- #ifdef RGFW_DEBUG
- switch (type) {
- case RGFW_typeInfo: printf("RGFW INFO (%i %i): %s", type, err, msg); break;
- case RGFW_typeError: printf("RGFW DEBUG (%i %i): %s", type, err, msg); break;
- case RGFW_typeWarning: printf("RGFW WARNING (%i %i): %s", type, err, msg); break;
- default: break;
- }
-
- switch (err) {
- #ifdef RGFW_BUFFER
- case RGFW_errBuffer: case RGFW_infoBuffer: printf(" buffer size: %i %i\n", ctx.win->bufferSize.w, ctx.win->bufferSize.h); break;
- #endif
- case RGFW_infoMonitor: printf(": scale (%s):\n rect: {%i, %i, %i, %i}\n physical size:%f %f\n scale: %f %f\n pixelRatio: %f\n refreshRate: %i\n depth: %i\n", ctx.monitor->name, ctx.monitor->x, ctx.monitor->y, ctx.monitor->mode.area.w, ctx.monitor->mode.area.h, ctx.monitor->physW, ctx.monitor->physH, ctx.monitor->scaleX, ctx.monitor->scaleY, ctx.monitor->pixelRatio, ctx.monitor->mode.refreshRate, ctx.monitor->mode.red + ctx.monitor->mode.green + ctx.monitor->mode.blue); break;
- case RGFW_infoWindow: printf(" with rect of {%i, %i, %i, %i} \n", ctx.win->r.x, ctx.win->r.y,ctx. win->r.w, ctx.win->r.h); break;
- case RGFW_errDirectXContext: printf(" srcError %i\n", ctx.srcError); break;
- default: printf("\n");
- }
- #endif
-}
-
-u64 RGFW_timerOffset = 0;
-void RGFW_setTime(double time) {
- RGFW_timerOffset = RGFW_getTimerValue() - (u64)(time * (double)RGFW_getTimerFreq());
-}
-
-double RGFW_getTime(void) {
- return (double) ((double)(RGFW_getTimerValue() - RGFW_timerOffset) / (double)RGFW_getTimerFreq());
-}
-
-u64 RGFW_getTimeNS(void) {
- return (u64)(((double)((RGFW_getTimerValue() - RGFW_timerOffset)) * 1e9) / (double)RGFW_getTimerFreq());
-}
-
-/*
-RGFW_IMPLEMENTATION starts with generic RGFW defines
-
-This is the start of keycode data
-*/
-
-
-
-/*
- the c++ compiler doesn't support setting up an array like,
- we'll have to do it during runtime using a function & this messy setup
-*/
-
-#ifndef RGFW_CUSTOM_BACKEND
-
-#if !defined(__cplusplus) && !defined(RGFW_C89)
-#define RGFW_NEXT ,
-#define RGFW_MAP
-#else
-#define RGFW_NEXT ;
-#define RGFW_MAP RGFW_keycodes
-#endif
-
-u32 RGFW_apiKeycodes[RGFW_keyLast] = { 0 };
-
-u8 RGFW_keycodes [RGFW_OS_BASED_VALUE(256, 512, 128, 256)] = {
-#if defined(__cplusplus) || defined(RGFW_C89)
- 0
-};
-void RGFW_init_keys(void);
-void RGFW_init_keys(void) {
-#endif
- RGFW_MAP [RGFW_OS_BASED_VALUE(49, 0x029, 50, DOM_VK_BACK_QUOTE)] = RGFW_backtick RGFW_NEXT
-
- RGFW_MAP [RGFW_OS_BASED_VALUE(19, 0x00B, 29, DOM_VK_0)] = RGFW_0 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(10, 0x002, 18, DOM_VK_1)] = RGFW_1 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(11, 0x003, 19, DOM_VK_2)] = RGFW_2 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(12, 0x004, 20, DOM_VK_3)] = RGFW_3 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(13, 0x005, 21, DOM_VK_4)] = RGFW_4 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(14, 0x006, 23, DOM_VK_5)] = RGFW_5 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(15, 0x007, 22, DOM_VK_6)] = RGFW_6 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(16, 0x008, 26, DOM_VK_7)] = RGFW_7 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(17, 0x009, 28, DOM_VK_8)] = RGFW_8 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(18, 0x00A, 25, DOM_VK_9)] = RGFW_9,
- RGFW_MAP [RGFW_OS_BASED_VALUE(65, 0x039, 49, DOM_VK_SPACE)] = RGFW_space,
- RGFW_MAP [RGFW_OS_BASED_VALUE(38, 0x01E, 0, DOM_VK_A)] = RGFW_a RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(56, 0x030, 11, DOM_VK_B)] = RGFW_b RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(54, 0x02E, 8, DOM_VK_C)] = RGFW_c RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(40, 0x020, 2, DOM_VK_D)] = RGFW_d RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(26, 0x012, 14, DOM_VK_E)] = RGFW_e RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(41, 0x021, 3, DOM_VK_F)] = RGFW_f RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(42, 0x022, 5, DOM_VK_G)] = RGFW_g RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(43, 0x023, 4, DOM_VK_H)] = RGFW_h RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(31, 0x017, 34, DOM_VK_I)] = RGFW_i RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(44, 0x024, 38, DOM_VK_J)] = RGFW_j RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(45, 0x025, 40, DOM_VK_K)] = RGFW_k RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(46, 0x026, 37, DOM_VK_L)] = RGFW_l RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(58, 0x032, 46, DOM_VK_M)] = RGFW_m RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(57, 0x031, 45, DOM_VK_N)] = RGFW_n RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(32, 0x018, 31, DOM_VK_O)] = RGFW_o RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(33, 0x019, 35, DOM_VK_P)] = RGFW_p RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(24, 0x010, 12, DOM_VK_Q)] = RGFW_q RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(27, 0x013, 15, DOM_VK_R)] = RGFW_r RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(39, 0x01F, 1, DOM_VK_S)] = RGFW_s RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(28, 0x014, 17, DOM_VK_T)] = RGFW_t RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(30, 0x016, 32, DOM_VK_U)] = RGFW_u RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(55, 0x02F, 9, DOM_VK_V)] = RGFW_v RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(25, 0x011, 13, DOM_VK_W)] = RGFW_w RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(53, 0x02D, 7, DOM_VK_X)] = RGFW_x RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(29, 0x015, 16, DOM_VK_Y)] = RGFW_y RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(52, 0x02C, 6, DOM_VK_Z)] = RGFW_z,
- RGFW_MAP [RGFW_OS_BASED_VALUE(60, 0x034, 47, DOM_VK_PERIOD)] = RGFW_period RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(59, 0x033, 43, DOM_VK_COMMA)] = RGFW_comma RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(61, 0x035, 44, DOM_VK_SLASH)] = RGFW_slash RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(34, 0x01A, 33, DOM_VK_OPEN_BRACKET)] = RGFW_bracket RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(35, 0x01B, 30, DOM_VK_CLOSE_BRACKET)] = RGFW_closeBracket RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(47, 0x027, 41, DOM_VK_SEMICOLON)] = RGFW_semicolon RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(48, 0x028, 39, DOM_VK_QUOTE)] = RGFW_apostrophe RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(51, 0x02B, 42, DOM_VK_BACK_SLASH)] = RGFW_backSlash,
- RGFW_MAP [RGFW_OS_BASED_VALUE(36, 0x01C, 36, DOM_VK_RETURN)] = RGFW_return RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(119, 0x153, 118, DOM_VK_DELETE)] = RGFW_delete RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(77, 0x145, 72, DOM_VK_NUM_LOCK)] = RGFW_numLock RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(106, 0x135, 82, DOM_VK_DIVIDE)] = RGFW_KP_Slash RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(63, 0x037, 76, DOM_VK_MULTIPLY)] = RGFW_multiply RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(82, 0x04A, 67, DOM_VK_SUBTRACT)] = RGFW_KP_Minus RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(87, 0x04F, 84, DOM_VK_NUMPAD1)] = RGFW_KP_1 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(88, 0x050, 85, DOM_VK_NUMPAD2)] = RGFW_KP_2 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(89, 0x051, 86, DOM_VK_NUMPAD3)] = RGFW_KP_3 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(83, 0x04B, 87, DOM_VK_NUMPAD4)] = RGFW_KP_4 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(84, 0x04C, 88, DOM_VK_NUMPAD5)] = RGFW_KP_5 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(85, 0x04D, 89, DOM_VK_NUMPAD6)] = RGFW_KP_6 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(79, 0x047, 90, DOM_VK_NUMPAD7)] = RGFW_KP_7 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(80, 0x048, 92, DOM_VK_NUMPAD8)] = RGFW_KP_8 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(81, 0x049, 93, DOM_VK_NUMPAD9)] = RGFW_KP_9 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(90, 0x052, 83, DOM_VK_NUMPAD0)] = RGFW_KP_0 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(91, 0x053, 65, DOM_VK_DECIMAL)] = RGFW_KP_Period RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(104, 0x11C, 77, 0)] = RGFW_KP_Return,
- RGFW_MAP [RGFW_OS_BASED_VALUE(20, 0x00C, 27, DOM_VK_HYPHEN_MINUS)] = RGFW_minus RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(21, 0x00D, 24, DOM_VK_EQUALS)] = RGFW_equals RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(22, 0x00E, 51, DOM_VK_BACK_SPACE)] = RGFW_backSpace RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(23, 0x00F, 48, DOM_VK_TAB)] = RGFW_tab RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(66, 0x03A, 57, DOM_VK_CAPS_LOCK)] = RGFW_capsLock RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(50, 0x02A, 56, DOM_VK_SHIFT)] = RGFW_shiftL RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(37, 0x01D, 59, DOM_VK_CONTROL)] = RGFW_controlL RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(64, 0x038, 58, DOM_VK_ALT)] = RGFW_altL RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(133, 0x15B, 55, DOM_VK_WIN)] = RGFW_superL,
- #if !defined(RGFW_MACOS) && !defined(RGFW_WASM)
- RGFW_MAP [RGFW_OS_BASED_VALUE(105, 0x11D, 59, 0)] = RGFW_controlR RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(134, 0x15C, 55, 0)] = RGFW_superR,
- RGFW_MAP [RGFW_OS_BASED_VALUE(62, 0x036, 56, 0)] = RGFW_shiftR RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(108, 0x138, 58, 0)] = RGFW_altR,
- #endif
- RGFW_MAP [RGFW_OS_BASED_VALUE(67, 0x03B, 127, DOM_VK_F1)] = RGFW_F1 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(68, 0x03C, 121, DOM_VK_F2)] = RGFW_F2 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(69, 0x03D, 100, DOM_VK_F3)] = RGFW_F3 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(70, 0x03E, 119, DOM_VK_F4)] = RGFW_F4 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(71, 0x03F, 97, DOM_VK_F5)] = RGFW_F5 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(72, 0x040, 98, DOM_VK_F6)] = RGFW_F6 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(73, 0x041, 99, DOM_VK_F7)] = RGFW_F7 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(74, 0x042, 101, DOM_VK_F8)] = RGFW_F8 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(75, 0x043, 102, DOM_VK_F9)] = RGFW_F9 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(76, 0x044, 110, DOM_VK_F10)] = RGFW_F10 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(95, 0x057, 104, DOM_VK_F11)] = RGFW_F11 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(96, 0x058, 111, DOM_VK_F12)] = RGFW_F12 RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(111, 0x148, 126, DOM_VK_UP)] = RGFW_up RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(116, 0x150, 125, DOM_VK_DOWN)] = RGFW_down RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(113, 0x14B, 123, DOM_VK_LEFT)] = RGFW_left RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(114, 0x14D, 124, DOM_VK_RIGHT)] = RGFW_right RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(118, 0x152, 115, DOM_VK_INSERT)] = RGFW_insert RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(115, 0x14F, 120, DOM_VK_END)] = RGFW_end RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(112, 0x149, 117, DOM_VK_PAGE_UP)] = RGFW_pageUp RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(117, 0x151, 122, DOM_VK_PAGE_DOWN)] = RGFW_pageDown RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(9, 0x001, 53, DOM_VK_ESCAPE)] = RGFW_escape RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(110, 0x147, 116, DOM_VK_HOME)] = RGFW_home RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(78, 0x046, 107, DOM_VK_SCROLL_LOCK)] = RGFW_scrollLock RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(107, 0x137, 105, DOM_VK_PRINTSCREEN)] = RGFW_printScreen RGFW_NEXT
- RGFW_MAP [RGFW_OS_BASED_VALUE(128, 0x045, 113, DOM_VK_PAUSE)] = RGFW_pause RGFW_NEXT
-#if defined(__cplusplus) || defined(RGFW_C89)
-}
-#else
-};
-#endif
-
-#undef RGFW_NEXT
-#undef RGFW_MAP
-
-u32 RGFW_apiKeyToRGFW(u32 keycode) {
- #if defined(__cplusplus) || defined(RGFW_C89)
- if (RGFW_keycodes[RGFW_OS_BASED_VALUE(49, 0x029, 50, DOM_VK_BACK_QUOTE)] != RGFW_backtick) {
- RGFW_init_keys();
- }
- #endif
-
- /* make sure the key isn't out of bounds */
- if (keycode > sizeof(RGFW_keycodes) / sizeof(u8))
- return 0;
-
- return RGFW_keycodes[keycode];
-}
-
-u32 RGFW_rgfwToApiKey(u32 keycode) {
- if (RGFW_apiKeycodes[RGFW_backtick] != RGFW_OS_BASED_VALUE(49, 0x029, 50, DOM_VK_BACK_QUOTE)) {
- for (u32 i = 0; i < RGFW_keyLast; i++) {
- for (u32 y = 0; y < sizeof(RGFW_keycodes); y++) {
- if (RGFW_keycodes[y] == i) {
- RGFW_apiKeycodes[i] = y;
- break;
- }
- }
- }
- }
-
- /* make sure the key isn't out of bounds */
- if (keycode > sizeof(RGFW_apiKeycodes) / sizeof(u32))
- return 0;
-
- return RGFW_apiKeycodes[keycode];
-}
-#endif /* RGFW_CUSTOM_BACKEND */
-
-typedef struct {
- RGFW_bool current : 1;
- RGFW_bool prev : 1;
-} RGFW_keyState;
-
-RGFW_keyState RGFW_keyboard[RGFW_keyLast] = { {0, 0} };
-
-RGFWDEF void RGFW_resetKeyPrev(void);
-void RGFW_resetKeyPrev(void) {
- size_t i; /*!< reset each previous state */
- for (i = 0; i < RGFW_keyLast; i++) RGFW_keyboard[i].prev = 0;
-}
-RGFWDEF void RGFW_resetKey(void);
-void RGFW_resetKey(void) { RGFW_MEMSET(RGFW_keyboard, 0, sizeof(RGFW_keyboard)); }
-/*
- this is the end of keycode data
-*/
-
-/* gamepad data */
-RGFW_keyState RGFW_gamepadPressed[4][32]; /*!< if a key is currently pressed or not (per gamepad) */
-RGFW_point RGFW_gamepadAxes[4][4]; /*!< if a key is currently pressed or not (per gamepad) */
-
-RGFW_gamepadType RGFW_gamepads_type[4]; /*!< if a key is currently pressed or not (per gamepad) */
-i32 RGFW_gamepads[4] = {0, 0, 0, 0}; /*!< limit of 4 gamepads at a time */
-char RGFW_gamepads_name[4][128]; /*!< gamepad names */
-u16 RGFW_gamepadCount = 0; /*!< the actual amount of gamepads */
-
-/*
- event callback defines start here
-*/
-
-
-/*
- These exist to avoid the
- if (func == NULL) check
- for (allegedly) better performance
-
- RGFW_EMPTY_DEF exists to prevent the missing-prototypes warning
-*/
-static void RGFW_windowMovedfuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); }
-static void RGFW_windowResizedfuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); }
-static void RGFW_windowRestoredfuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); }
-static void RGFW_windowMinimizedfuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); }
-static void RGFW_windowMaximizedfuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); }
-static void RGFW_windowQuitfuncEMPTY(RGFW_window* win) { RGFW_UNUSED(win); }
-static void RGFW_focusfuncEMPTY(RGFW_window* win, RGFW_bool inFocus) {RGFW_UNUSED(win); RGFW_UNUSED(inFocus);}
-static void RGFW_mouseNotifyfuncEMPTY(RGFW_window* win, RGFW_point point, RGFW_bool status) {RGFW_UNUSED(win); RGFW_UNUSED(point); RGFW_UNUSED(status);}
-static void RGFW_mousePosfuncEMPTY(RGFW_window* win, RGFW_point point, RGFW_point vector) {RGFW_UNUSED(win); RGFW_UNUSED(point); RGFW_UNUSED(vector);}
-static void RGFW_dndInitfuncEMPTY(RGFW_window* win, RGFW_point point) {RGFW_UNUSED(win); RGFW_UNUSED(point);}
-static void RGFW_windowRefreshfuncEMPTY(RGFW_window* win) {RGFW_UNUSED(win); }
-static void RGFW_keyfuncEMPTY(RGFW_window* win, RGFW_key key, u8 keyChar, RGFW_keymod keyMod, RGFW_bool pressed) {RGFW_UNUSED(win); RGFW_UNUSED(key); RGFW_UNUSED(keyChar); RGFW_UNUSED(keyMod); RGFW_UNUSED(pressed);}
-static void RGFW_mouseButtonfuncEMPTY(RGFW_window* win, RGFW_mouseButton button, double scroll, RGFW_bool pressed) {RGFW_UNUSED(win); RGFW_UNUSED(button); RGFW_UNUSED(scroll); RGFW_UNUSED(pressed);}
-static void RGFW_gamepadButtonfuncEMPTY(RGFW_window* win, u16 gamepad, u8 button, RGFW_bool pressed) {RGFW_UNUSED(win); RGFW_UNUSED(gamepad); RGFW_UNUSED(button); RGFW_UNUSED(pressed); }
-static void RGFW_gamepadAxisfuncEMPTY(RGFW_window* win, u16 gamepad, RGFW_point axis[2], u8 axisesCount, u8 whichAxis) {RGFW_UNUSED(win); RGFW_UNUSED(gamepad); RGFW_UNUSED(axis); RGFW_UNUSED(axisesCount); RGFW_UNUSED(whichAxis); }
-static void RGFW_gamepadfuncEMPTY(RGFW_window* win, u16 gamepad, RGFW_bool connected) {RGFW_UNUSED(win); RGFW_UNUSED(gamepad); RGFW_UNUSED(connected);}
-static void RGFW_dndfuncEMPTY(RGFW_window* win, char** droppedFiles, size_t droppedFilesCount) {RGFW_UNUSED(win); RGFW_UNUSED(droppedFiles); RGFW_UNUSED(droppedFilesCount);}
-static void RGFW_scaleUpdatedfuncEMPTY(RGFW_window* win, float scaleX, float scaleY) {RGFW_UNUSED(win); RGFW_UNUSED(scaleX); RGFW_UNUSED(scaleY); }
-
-#define RGFW_CALLBACK_DEFINE(x, x2) \
-RGFW_##x##func RGFW_##x##Callback = RGFW_##x##funcEMPTY; \
-RGFW_##x##func RGFW_set##x2##Callback(RGFW_##x##func func) { \
- RGFW_##x##func prev = RGFW_##x##Callback; \
- RGFW_##x##Callback = func; \
- return prev; \
-}
-RGFW_CALLBACK_DEFINE(windowMaximized, WindowMaximized)
-RGFW_CALLBACK_DEFINE(windowMinimized, WindowMinimized)
-RGFW_CALLBACK_DEFINE(windowRestored, WindowRestored)
-RGFW_CALLBACK_DEFINE(windowMoved, WindowMoved)
-RGFW_CALLBACK_DEFINE(windowResized, WindowResized)
-RGFW_CALLBACK_DEFINE(windowQuit, WindowQuit)
-RGFW_CALLBACK_DEFINE(mousePos, MousePos)
-RGFW_CALLBACK_DEFINE(windowRefresh, WindowRefresh)
-RGFW_CALLBACK_DEFINE(focus, Focus)
-RGFW_CALLBACK_DEFINE(mouseNotify, MouseNotify)
-RGFW_CALLBACK_DEFINE(dnd, Dnd)
-RGFW_CALLBACK_DEFINE(dndInit, DndInit)
-RGFW_CALLBACK_DEFINE(key, Key)
-RGFW_CALLBACK_DEFINE(mouseButton, MouseButton)
-RGFW_CALLBACK_DEFINE(gamepadButton, GamepadButton)
-RGFW_CALLBACK_DEFINE(gamepadAxis, GamepadAxis)
-RGFW_CALLBACK_DEFINE(gamepad, Gamepad)
-RGFW_CALLBACK_DEFINE(scaleUpdated, ScaleUpdated)
-#undef RGFW_CALLBACK_DEFINE
-
-void RGFW_window_checkEvents(RGFW_window* win, i32 waitMS) {
- RGFW_window_eventWait(win, waitMS);
-
- while (RGFW_window_checkEvent(win) != NULL && RGFW_window_shouldClose(win) == 0) {
- if (win->event.type == RGFW_quit) return;
- }
-
- #ifdef RGFW_WASM /* WASM needs to run the sleep function for asyncify */
- RGFW_sleep(0);
- #endif
-}
-
-void RGFW_window_checkMode(RGFW_window* win);
-void RGFW_window_checkMode(RGFW_window* win) {
- if (RGFW_window_isMinimized(win)) {
- win->_flags |= RGFW_windowMinimize;
- RGFW_windowMinimizedCallback(win, win->r);
- } else if (RGFW_window_isMaximized(win)) {
- win->_flags |= RGFW_windowMaximize;
- RGFW_eventQueuePushEx(e.type = RGFW_windowMaximized; e._win = win);
- RGFW_windowMaximizedCallback(win, win->r);
- } else if (((win->_flags & RGFW_windowMinimize) && !RGFW_window_isMaximized(win)) ||
- (win->_flags & RGFW_windowMaximize && !RGFW_window_isMaximized(win))) {
- win->_flags &= ~(u32)RGFW_windowMinimize;
- if (RGFW_window_isMaximized(win) == RGFW_FALSE) win->_flags &= ~(u32)RGFW_windowMaximize;
- RGFW_eventQueuePushEx(e.type = RGFW_windowRestored; e._win = win);
- RGFW_windowRestoredCallback(win, win->r);
- }
-}
-
-/*
-no more event call back defines
-*/
-
-#define SET_ATTRIB(a, v) { \
- RGFW_ASSERT(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
- attribs[index++] = a; \
- attribs[index++] = v; \
-}
-
-#define RGFW_EVENT_PASSED RGFW_BIT(24) /* if a queued event was passed */
-#define RGFW_EVENT_QUIT RGFW_BIT(25) /* the window close button was pressed */
-#define RGFW_HOLD_MOUSE RGFW_BIT(26) /*!< hold the moues still */
-#define RGFW_MOUSE_LEFT RGFW_BIT(27) /* if mouse left the window */
-#define RGFW_WINDOW_ALLOC RGFW_BIT(28) /* if window was allocated by RGFW */
-#define RGFW_BUFFER_ALLOC RGFW_BIT(29) /* if window.buffer was allocated by RGFW */
-#define RGFW_WINDOW_INIT RGFW_BIT(30) /* if window.buffer was allocated by RGFW */
-#define RGFW_INTERNAL_FLAGS (RGFW_EVENT_QUIT | RGFW_EVENT_PASSED | RGFW_HOLD_MOUSE | RGFW_MOUSE_LEFT | RGFW_WINDOW_ALLOC | RGFW_BUFFER_ALLOC | RGFW_windowFocus)
-
-RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, RGFW_windowFlags flags) {
- RGFW_window* win = (RGFW_window*)RGFW_ALLOC(sizeof(RGFW_window));
- RGFW_ASSERT(win != NULL);
- win->_flags = RGFW_WINDOW_ALLOC;
- return RGFW_createWindowPtr(name, rect, flags, win);
-}
-
-#if defined(RGFW_USE_XDL) && defined(RGFW_X11)
- #define XDL_IMPLEMENTATION
- #include "XDL.h"
-#endif
-
-#define RGFW_MAX_EVENTS 32
-typedef struct RGFW_globalStruct {
- RGFW_window* root;
- RGFW_window* current;
- i32 windowCount;
- i32 eventLen;
- i32 eventIndex;
-
- #ifdef RGFW_X11
- Display* display;
- Window helperWindow;
- char* clipboard; /* for writing to the clipboard selection */
- size_t clipboard_len;
- #endif
- #ifdef RGFW_WAYLAND
- struct wl_display* wl_display;
- #endif
- #if defined(RGFW_X11) || defined(RGFW_WINDOWS) || defined(RGFW_WAYLAND)
- RGFW_mouse* hiddenMouse;
- #endif
- RGFW_event events[RGFW_MAX_EVENTS];
-
-} RGFW_globalStruct;
-#if !defined(RGFW_C89) && !defined(__cplusplus)
-RGFW_globalStruct _RGFW = {.root = NULL, .current = NULL, .windowCount = -1, .eventLen = 0, .eventIndex = 0};
-#define _RGFW_init RGFW_TRUE
-#else
-RGFW_bool _RGFW_init = RGFW_FALSE;
-RGFW_globalStruct _RGFW;
-#endif
-
-void RGFW_eventQueuePush(RGFW_event event) {
- if (_RGFW.eventLen >= RGFW_MAX_EVENTS) return;
- _RGFW.events[_RGFW.eventLen] = event;
- _RGFW.eventLen++;
-}
-
-RGFW_event* RGFW_eventQueuePop(RGFW_window* win) {
- RGFW_event* ev;
- if (_RGFW.eventLen == 0) return NULL;
-
- ev = (RGFW_event*)&_RGFW.events[_RGFW.eventIndex];
-
- _RGFW.eventLen--;
- if (_RGFW.eventLen >= 0 && _RGFW.eventIndex < _RGFW.eventLen) {
- _RGFW.eventIndex++;
- } else if (_RGFW.eventLen == 0) {
- _RGFW.eventIndex = 0;
- }
-
- if (ev->_win != win && ev->_win != NULL) {
- RGFW_eventQueuePush(*ev);
- return NULL;
- }
-
- ev->droppedFilesCount = win->event.droppedFilesCount;
- ev->droppedFiles = win->event.droppedFiles;
- return ev;
-}
-
-RGFW_event* RGFW_window_checkEventCore(RGFW_window* win);
-RGFW_event* RGFW_window_checkEventCore(RGFW_window* win) {
- RGFW_event* ev;
- RGFW_ASSERT(win != NULL);
- if (win->event.type == 0 && _RGFW.eventLen == 0)
- RGFW_resetKeyPrev();
-
- if (win->event.type == RGFW_quit && win->_flags & RGFW_windowFreeOnClose) {
- static RGFW_event event;
- event = win->event;
- RGFW_window_close(win);
- return &event;
- }
-
- if (win->event.type != RGFW_DNDInit) win->event.type = 0;
-
- /* check queued events */
- ev = RGFW_eventQueuePop(win);
- if (ev != NULL) {
- if (ev->type == RGFW_quit) RGFW_window_setShouldClose(win, RGFW_TRUE);
- win->event = *ev;
- }
- else return NULL;
-
- return &win->event;
-}
-
-
-RGFWDEF void RGFW_window_basic_init(RGFW_window* win, RGFW_rect rect, RGFW_windowFlags flags);
-void RGFW_setRootWindow(RGFW_window* win) { _RGFW.root = win; }
-RGFW_window* RGFW_getRootWindow(void) { return _RGFW.root; }
-
-/* do a basic initialization for RGFW_window, this is to standard it for each OS */
-void RGFW_window_basic_init(RGFW_window* win, RGFW_rect rect, RGFW_windowFlags flags) {
- RGFW_UNUSED(flags);
- if (_RGFW.windowCount == -1 || _RGFW_init == RGFW_FALSE) RGFW_init();
- _RGFW.windowCount++;
-
- /* rect based the requested flags */
- if (_RGFW.root == NULL) {
- RGFW_setRootWindow(win);
- RGFW_setTime(0);
- }
-
- if (!(win->_flags & RGFW_WINDOW_ALLOC)) win->_flags = 0;
-
- /* set and init the new window's data */
- win->r = rect;
- win->exitKey = RGFW_escape;
- win->event.droppedFilesCount = 0;
-
- win->_flags = 0 | (win->_flags & RGFW_WINDOW_ALLOC);
- win->_flags |= flags;
- win->event.keyMod = 0;
- win->_lastMousePoint.x = 0;
- win->_lastMousePoint.y = 0;
-
- win->event.droppedFiles = (char**)RGFW_ALLOC(RGFW_MAX_PATH * RGFW_MAX_DROPS);
- RGFW_ASSERT(win->event.droppedFiles != NULL);
-
- {
- u32 i;
- for (i = 0; i < RGFW_MAX_DROPS; i++)
- win->event.droppedFiles[i] = (char*)(win->event.droppedFiles + RGFW_MAX_DROPS + (i * RGFW_MAX_PATH));
- }
-}
-
-void RGFW_window_setFlags(RGFW_window* win, RGFW_windowFlags flags) {
- RGFW_windowFlags cmpFlags = win->_flags;
- if (win->_flags & RGFW_WINDOW_INIT) cmpFlags = 0;
-
- #ifndef RGFW_NO_MONITOR
- if (flags & RGFW_windowScaleToMonitor) RGFW_window_scaleToMonitor(win);
- #endif
-
- if (flags & RGFW_windowCenter) RGFW_window_center(win);
- if (flags & RGFW_windowCenterCursor)
- RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2)));
- if (flags & RGFW_windowNoBorder) RGFW_window_setBorder(win, 0);
- else RGFW_window_setBorder(win, 1);
- if (flags & RGFW_windowFullscreen) RGFW_window_setFullscreen(win, RGFW_TRUE);
- else if (cmpFlags & RGFW_windowFullscreen) RGFW_window_setFullscreen(win, 0);
- if (flags & RGFW_windowMaximize) RGFW_window_maximize(win);
- else if (cmpFlags & RGFW_windowMaximize) RGFW_window_restore(win);
- if (flags & RGFW_windowMinimize) RGFW_window_minimize(win);
- else if (cmpFlags & RGFW_windowMinimize) RGFW_window_restore(win);
- if (flags & RGFW_windowHideMouse) RGFW_window_showMouse(win, 0);
- else if (cmpFlags & RGFW_windowHideMouse) RGFW_window_showMouse(win, 1);
- if (flags & RGFW_windowHide) RGFW_window_hide(win);
- else if (cmpFlags & RGFW_windowHide) RGFW_window_show(win);
- if (flags & RGFW_windowCocoaCHDirToRes) RGFW_moveToMacOSResourceDir();
- if (flags & RGFW_windowFloating) RGFW_window_setFloating(win, 1);
- else if (cmpFlags & RGFW_windowFloating) RGFW_window_setFloating(win, 0);
- if (flags & RGFW_windowFocus) RGFW_window_focus(win);
-
- if (flags & RGFW_windowNoResize) {
- RGFW_window_setMaxSize(win, RGFW_AREA(win->r.w, win->r.h));
- RGFW_window_setMinSize(win, RGFW_AREA(win->r.w, win->r.h));
- } else if (cmpFlags & RGFW_windowNoResize) {
- RGFW_window_setMaxSize(win, RGFW_AREA(0, 0));
- RGFW_window_setMinSize(win, RGFW_AREA(0, 0));
- }
-
- win->_flags = flags | (win->_flags & RGFW_INTERNAL_FLAGS);
-}
-
-RGFW_bool RGFW_window_opengl_isSoftware(RGFW_window* win) {
- return RGFW_BOOL(win->_flags |= RGFW_windowOpenglSoftware);
-}
-
-RGFW_bool RGFW_window_isInFocus(RGFW_window* win) {
-#ifdef RGFW_WASM
- return RGFW_TRUE;
-#else
- return RGFW_BOOL(win->_flags & RGFW_windowFocus);
-#endif
-}
-
-void RGFW_window_initBuffer(RGFW_window* win) {
- RGFW_area area = RGFW_getScreenSize();
- if ((win->_flags & RGFW_windowNoResize))
- area = RGFW_AREA(win->r.w, win->r.h);
-
- RGFW_window_initBufferSize(win, area);
-}
-
-void RGFW_window_initBufferSize(RGFW_window* win, RGFW_area area) {
-#if defined(RGFW_BUFFER) || defined(RGFW_OSMESA)
- win->_flags |= RGFW_BUFFER_ALLOC;
- #ifndef RGFW_WINDOWS
- u8* buffer = (u8*)RGFW_ALLOC(area.w * area.h * 4);
- RGFW_ASSERT(buffer != NULL);
-
- RGFW_window_initBufferPtr(win, buffer, area);
- #else /* windows's bitmap allocs memory for us */
- RGFW_window_initBufferPtr(win, (u8*)NULL, area);
- #endif
-#else
- RGFW_UNUSED(win); RGFW_UNUSED(area);
-#endif
-}
-
-#ifdef RGFW_MACOS
-RGFWDEF void RGFW_window_cocoaSetLayer(RGFW_window* win, void* layer);
-RGFWDEF void* RGFW_cocoaGetLayer(void);
-#endif
-
-const char* RGFW_className = NULL;
-void RGFW_setClassName(const char* name) { RGFW_className = name; }
-
-#ifndef RGFW_X11
-void RGFW_setXInstName(const char* name) { RGFW_UNUSED(name); }
-#endif
-
-RGFW_keyState RGFW_mouseButtons[RGFW_mouseFinal] = { {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0} };
-
-RGFW_bool RGFW_isMousePressed(RGFW_window* win, RGFW_mouseButton button) {
- return RGFW_mouseButtons[button].current && (win == NULL || RGFW_window_isInFocus(win));
-}
-RGFW_bool RGFW_wasMousePressed(RGFW_window* win, RGFW_mouseButton button) {
- return RGFW_mouseButtons[button].prev && (win != NULL || RGFW_window_isInFocus(win));
-}
-RGFW_bool RGFW_isMouseHeld(RGFW_window* win, RGFW_mouseButton button) {
- return (RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button));
-}
-RGFW_bool RGFW_isMouseReleased(RGFW_window* win, RGFW_mouseButton button) {
- return (!RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button));
-}
-
-RGFW_point RGFW_window_getMousePoint(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
- return win->_lastMousePoint;
-}
-
-RGFW_bool RGFW_isPressed(RGFW_window* win, RGFW_key key) {
- return RGFW_keyboard[key].current && (win == NULL || RGFW_window_isInFocus(win));
-}
-
-RGFW_bool RGFW_wasPressed(RGFW_window* win, RGFW_key key) {
- return RGFW_keyboard[key].prev && (win == NULL || RGFW_window_isInFocus(win));
-}
-
-RGFW_bool RGFW_isHeld(RGFW_window* win, RGFW_key key) {
- return (RGFW_isPressed(win, key) && RGFW_wasPressed(win, key));
-}
-
-RGFW_bool RGFW_isClicked(RGFW_window* win, RGFW_key key) {
- return (RGFW_wasPressed(win, key) && !RGFW_isPressed(win, key));
-}
-
-RGFW_bool RGFW_isReleased(RGFW_window* win, RGFW_key key) {
- return (!RGFW_isPressed(win, key) && RGFW_wasPressed(win, key));
-}
-
-void RGFW_window_makeCurrent(RGFW_window* win) {
- _RGFW.current = win;
-#if defined(RGFW_OPENGL) || defined(RGFW_EGL)
- RGFW_window_makeCurrent_OpenGL(win);
-#endif
-}
-
-RGFW_window* RGFW_getCurrent(void) {
- return _RGFW.current;
-}
-
-void RGFW_window_swapBuffers(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
- RGFW_window_swapBuffers_software(win);
-#if defined(RGFW_OPENGL) || defined(RGFW_EGL)
- RGFW_window_swapBuffers_OpenGL(win);
-#endif
-}
-
-RGFWDEF void RGFW_setBit(u32* data, u32 bit, RGFW_bool value);
-void RGFW_setBit(u32* data, u32 bit, RGFW_bool value) {
- if (value)
- *data |= bit;
- else if (!value && (*(data) & bit))
- *data ^= bit;
-}
-
-void RGFW_window_center(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
- RGFW_area screenR = RGFW_getScreenSize();
- RGFW_window_move(win, RGFW_POINT((i32)(screenR.w - (u32)win->r.w) / 2, (screenR.h - (u32)win->r.h) / 2));
-}
-
-RGFW_bool RGFW_monitor_scaleToWindow(RGFW_monitor mon, RGFW_window* win) {
- RGFW_monitorMode mode;
- RGFW_ASSERT(win != NULL);
-
- mode.area.w = (u32)win->r.w;
- mode.area.h = (u32)win->r.h;
- return RGFW_monitor_requestMode(mon, mode, RGFW_monitorScale);
-}
-
-void RGFW_splitBPP(u32 bpp, RGFW_monitorMode* mode);
-void RGFW_splitBPP(u32 bpp, RGFW_monitorMode* mode) {
- if (bpp == 32) bpp = 24;
- mode->red = mode->green = mode->blue = (u8)(bpp / 3);
-
- u32 delta = bpp - (mode->red * 3); /* handle leftovers */
- if (delta >= 1) mode->green = mode->green + 1;
- if (delta == 2) mode->red = mode->red + 1;
-}
-
-RGFW_bool RGFW_monitorModeCompare(RGFW_monitorMode mon, RGFW_monitorMode mon2, RGFW_modeRequest request) {
- return (((mon.area.w == mon2.area.w && mon.area.h == mon2.area.h) || !(request & RGFW_monitorScale)) &&
- ((mon.refreshRate == mon2.refreshRate) || !(request & RGFW_monitorRefresh)) &&
- ((mon.red == mon2.red && mon.green == mon2.green && mon.blue == mon2.blue) || !(request & RGFW_monitorRGB)));
-}
-
-RGFW_bool RGFW_window_shouldClose(RGFW_window* win) {
- return (win == NULL || (win->_flags & RGFW_EVENT_QUIT)|| (win->exitKey && RGFW_isPressed(win, win->exitKey)));
-}
-
-void RGFW_window_setShouldClose(RGFW_window* win, RGFW_bool shouldClose) {
- if (shouldClose) {
- win->_flags |= RGFW_EVENT_QUIT;
- RGFW_windowQuitCallback(win);
- } else {
- win->_flags &= ~(u32)RGFW_EVENT_QUIT;
- }
-}
-
-#ifndef RGFW_NO_MONITOR
-void RGFW_window_scaleToMonitor(RGFW_window* win) {
- RGFW_monitor monitor = RGFW_window_getMonitor(win);
- if (monitor.scaleX == 0 && monitor.scaleY == 0)
- return;
-
- RGFW_window_resize(win, RGFW_AREA((u32)(monitor.scaleX * (float)win->r.w), (u32)(monitor.scaleY * (float)win->r.h)));
-}
-
-void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m) {
- RGFW_window_move(win, RGFW_POINT(m.x + win->r.x, m.y + win->r.y));
-}
-#endif
-
-RGFW_bool RGFW_window_setIcon(RGFW_window* win, u8* icon, RGFW_area a, i32 channels) {
- return RGFW_window_setIconEx(win, icon, a, channels, RGFW_iconBoth);
-}
-
-RGFWDEF void RGFW_captureCursor(RGFW_window* win, RGFW_rect);
-RGFWDEF void RGFW_releaseCursor(RGFW_window* win);
-
-
-RGFW_bool RGFW_window_mouseHeld(RGFW_window* win) { return RGFW_BOOL(win->_flags & RGFW_HOLD_MOUSE); }
-
-void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area) {
- if (!area.w && !area.h)
- area = RGFW_AREA(win->r.w / 2, win->r.h / 2);
-
- win->_flags |= RGFW_HOLD_MOUSE;
- RGFW_captureCursor(win, win->r);
- RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2)));
-}
-
-void RGFW_window_mouseUnhold(RGFW_window* win) {
- win->_flags &= ~(u32)RGFW_HOLD_MOUSE;
- RGFW_releaseCursor(win);
-}
-
-u32 RGFW_checkFPS(double startTime, u32 frameCount, u32 fpsCap) {
- double deltaTime = RGFW_getTime() - startTime;
- if (deltaTime == 0) return 0;
-
- double fps = (frameCount / deltaTime); /* the numer of frames over the time it took for them to render */
- if (fpsCap && fps > fpsCap) {
- double frameTime = (double)frameCount / (double)fpsCap; /* how long it should take to finish the frames */
- double sleepTime = frameTime - deltaTime; /* subtract how long it should have taken with how long it did take */
-
- if (sleepTime > 0) RGFW_sleep((u32)(sleepTime * 1000));
- }
-
- return (u32) fps;
-}
-
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
-void RGFW_RGB_to_BGR(RGFW_window* win, u8* data) {
- #if !defined(RGFW_BUFFER_BGR) && !defined(RGFW_OSMESA)
- u32 x, y;
- for (y = 0; y < (u32)win->r.h; y++) {
- for (x = 0; x < (u32)win->r.w; x++) {
- u32 index = (y * 4 * win->bufferSize.w) + x * 4;
-
- u8 red = data[index];
- data[index] = win->buffer[index + 2];
- data[index + 2] = red;
- }
- }
- #elif defined(RGFW_OSMESA)
- u32 y;
- for(y = 0; y < (u32)win->r.h; y++){
- u32 index_from = (y + (win->bufferSize.h - win->r.h)) * 4 * win->bufferSize.w;
- u32 index_to = y * 4 * win->bufferSize.w;
- memcpy(&data[index_to], &data[index_from], 4 * win->bufferSize.w);
- }
- #else
- RGFW_UNUSED(win); RGFW_UNUSED(data);
- #endif
-}
-#endif
-
-u32 RGFW_isPressedGamepad(RGFW_window* win, u8 c, RGFW_gamepadCodes button) {
- RGFW_UNUSED(win);
- return RGFW_gamepadPressed[c][button].current;
-}
-u32 RGFW_wasPressedGamepad(RGFW_window* win, u8 c, RGFW_gamepadCodes button) {
- RGFW_UNUSED(win);
- return RGFW_gamepadPressed[c][button].prev;
-}
-u32 RGFW_isReleasedGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button) {
- RGFW_UNUSED(win);
- return !RGFW_isPressedGamepad(win, controller, button) && RGFW_wasPressedGamepad(win, controller, button);
-}
-u32 RGFW_isHeldGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button) {
- RGFW_UNUSED(win);
- return RGFW_isPressedGamepad(win, controller, button) && RGFW_wasPressedGamepad(win, controller, button);
-}
-
-RGFW_point RGFW_getGamepadAxis(RGFW_window* win, u16 controller, u16 whichAxis) {
- RGFW_UNUSED(win);
- return RGFW_gamepadAxes[controller][whichAxis];
-}
-const char* RGFW_getGamepadName(RGFW_window* win, u16 controller) {
- RGFW_UNUSED(win);
- return (const char*)RGFW_gamepads_name[controller];
-}
-
-size_t RGFW_getGamepadCount(RGFW_window* win) {
- RGFW_UNUSED(win);
- return RGFW_gamepadCount;
-}
-
-RGFW_gamepadType RGFW_getGamepadType(RGFW_window* win, u16 controller) {
- RGFW_UNUSED(win);
- return RGFW_gamepads_type[controller];
-}
-
-RGFWDEF void RGFW_updateKeyMod(RGFW_window* win, RGFW_keymod mod, RGFW_bool value);
-void RGFW_updateKeyMod(RGFW_window* win, RGFW_keymod mod, RGFW_bool value) {
- if (value) win->event.keyMod |= mod;
- else win->event.keyMod &= ~mod;
-}
-
-RGFWDEF void RGFW_updateKeyModsPro(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll);
-void RGFW_updateKeyModsPro(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll) {
- RGFW_updateKeyMod(win, RGFW_modCapsLock, capital);
- RGFW_updateKeyMod(win, RGFW_modNumLock, numlock);
- RGFW_updateKeyMod(win, RGFW_modControl, control);
- RGFW_updateKeyMod(win, RGFW_modAlt, alt);
- RGFW_updateKeyMod(win, RGFW_modShift, shift);
- RGFW_updateKeyMod(win, RGFW_modSuper, super);
- RGFW_updateKeyMod(win, RGFW_modScrollLock, scroll);
-}
-
-RGFWDEF void RGFW_updateKeyMods(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool scroll);
-void RGFW_updateKeyMods(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool scroll) {
- RGFW_updateKeyModsPro(win, capital, numlock,
- RGFW_isPressed(win, RGFW_controlL) || RGFW_isPressed(win, RGFW_controlR),
- RGFW_isPressed(win, RGFW_altL) || RGFW_isPressed(win, RGFW_altR),
- RGFW_isPressed(win, RGFW_shiftL) || RGFW_isPressed(win, RGFW_shiftR),
- RGFW_isPressed(win, RGFW_superL) || RGFW_isPressed(win, RGFW_superR),
- scroll);
-}
-
-RGFWDEF void RGFW_window_showMouseFlags(RGFW_window* win, RGFW_bool show);
-void RGFW_window_showMouseFlags(RGFW_window* win, RGFW_bool show) {
- if (show && (win->_flags & RGFW_windowHideMouse))
- win->_flags ^= RGFW_windowHideMouse;
- else if (!show && !(win->_flags & RGFW_windowHideMouse))
- win->_flags |= RGFW_windowHideMouse;
-}
-
-RGFW_bool RGFW_window_mouseHidden(RGFW_window* win) {
- return (RGFW_bool)RGFW_BOOL(win->_flags & RGFW_windowHideMouse);
-}
-
-RGFW_bool RGFW_window_borderless(RGFW_window* win) {
- return (RGFW_bool)RGFW_BOOL(win->_flags & RGFW_windowNoBorder);
-}
-
-RGFW_bool RGFW_window_isFullscreen(RGFW_window* win){ return RGFW_BOOL(win->_flags & RGFW_windowFullscreen); }
-RGFW_bool RGFW_window_allowsDND(RGFW_window* win) { return RGFW_BOOL(win->_flags & RGFW_windowAllowDND); }
-
-void RGFW_window_focusLost(RGFW_window* win) {
- /* standard routines for when a window looses focus */
- _RGFW.root->_flags &= ~(u32)RGFW_windowFocus;
- if ((win->_flags & RGFW_windowFullscreen))
- RGFW_window_minimize(win);
-
- for (size_t key = 0; key < RGFW_keyLast; key++) {
- if (RGFW_isPressed(NULL, (u8)key) == RGFW_FALSE) continue;
- RGFW_keyboard[key].current = RGFW_FALSE;
- u8 keyChar = RGFW_rgfwToKeyChar((u32)key);
- RGFW_keyCallback(win, (u8)key, keyChar, win->event.keyMod, RGFW_FALSE);
- RGFW_eventQueuePushEx(e.type = RGFW_keyReleased;
- e.key = (u8)key;
- e.keyChar = keyChar;
- e.repeat = RGFW_FALSE;
- e.keyMod = win->event.keyMod;
- e._win = win);
- }
-
- RGFW_resetKey();
-}
-
-#ifndef RGFW_WINDOWS
-void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow) {
- RGFW_setBit(&win->_flags, RGFW_windowAllowDND, allow);
-}
-#endif
-
-#if defined(RGFW_X11) || defined(RGFW_MACOS) || defined(RGFW_WASM) || defined(RGFW_WAYLAND)
-#ifndef __USE_POSIX199309
- #define __USE_POSIX199309
-#endif
-#include
-struct timespec;
-#endif
-
-#if defined(RGFW_WAYLAND) || defined(RGFW_X11) || defined(RGFW_WINDOWS)
-void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) {
- RGFW_window_showMouseFlags(win, show);
- if (show == 0)
- RGFW_window_setMouse(win, _RGFW.hiddenMouse);
- else
- RGFW_window_setMouseDefault(win);
-}
-#endif
-
-#ifndef RGFW_MACOS
-void RGFW_moveToMacOSResourceDir(void) { }
-#endif
-
-/*
- graphics API specific code (end of generic code)
- starts here
-*/
-
-
-/*
- OpenGL defines start here (Normal, EGL, OSMesa)
-*/
-
-#if defined(RGFW_OPENGL) || defined(RGFW_EGL)
-
-#ifdef RGFW_WINDOWS
- #define WIN32_LEAN_AND_MEAN
- #define OEMRESOURCE
- #include
-#endif
-
-#if !defined(__APPLE__) && !defined(RGFW_NO_GL_HEADER)
- #include
-#elif defined(__APPLE__)
- #ifndef GL_SILENCE_DEPRECATION
- #define GL_SILENCE_DEPRECATION
- #endif
- #include
- #include
-#endif
-
-/* EGL, normal OpenGL only */
-#ifndef RGFW_EGL
-i32 RGFW_GL_HINTS[RGFW_glFinalHint] = {8,
-#else
-i32 RGFW_GL_HINTS[RGFW_glFinalHint] = {0,
-#endif
- 0, 0, 0, 1, 8, 8, 8, 8, 24, 0, 0, 0, 0, 0, 0, 0, 0, RGFW_glReleaseNone, RGFW_glCore, 0, 0};
-
-void RGFW_setGLHint(RGFW_glHints hint, i32 value) {
- if (hint < RGFW_glFinalHint && hint) RGFW_GL_HINTS[hint] = value;
-}
-
-RGFW_bool RGFW_extensionSupportedStr(const char* extensions, const char* ext, size_t len) {
- const char *start = extensions;
- const char *where;
- const char* terminator;
-
- if (extensions == NULL || ext == NULL)
- return RGFW_FALSE;
-
- where = strstr(extensions, ext);
- while (where) {
- terminator = where + len;
- if ((where == start || *(where - 1) == ' ') &&
- (*terminator == ' ' || *terminator == '\0')) {
- return RGFW_TRUE;
- }
- where = RGFW_STRSTR(terminator, ext);
- }
-
- return RGFW_FALSE;
-}
-
-RGFW_bool RGFW_extensionSupported(const char* extension, size_t len) {
- #ifdef GL_NUM_EXTENSIONS
- if (RGFW_GL_HINTS[RGFW_glMajor] >= 3) {
- i32 i;
- GLint count = 0;
-
- RGFW_proc RGFW_glGetStringi = RGFW_getProcAddress("glGetStringi");
- RGFW_proc RGFW_glGetIntegerv = RGFW_getProcAddress("RGFW_glGetIntegerv");
- if (RGFW_glGetIntegerv)
- ((void(*)(GLenum, GLint*))RGFW_glGetIntegerv)(GL_NUM_EXTENSIONS, &count);
-
- for (i = 0; RGFW_glGetStringi && i < count; i++) {
- const char* en = ((const char* (*)(u32, u32))RGFW_glGetStringi)(GL_EXTENSIONS, (u32)i);
- if (en && RGFW_STRNCMP(en, extension, len) == 0)
- return RGFW_TRUE;
- }
- } else
-#endif
- {
- RGFW_proc RGFW_glGetString = RGFW_getProcAddress("glGetString");
-
- if (RGFW_glGetString) {
- const char* extensions = ((const char*(*)(u32))RGFW_glGetString)(GL_EXTENSIONS);
- if ((extensions != NULL) && RGFW_extensionSupportedStr(extensions, extension, len))
- return RGFW_TRUE;
- }
- }
-
- return RGFW_extensionSupportedPlatform(extension, len);
-}
-
-/* OPENGL normal only (no EGL / OSMesa) */
-#if defined(RGFW_OPENGL) && !defined(RGFW_EGL) && !defined(RGFW_CUSTOM_BACKEND) && !defined(RGFW_WASM)
-
-#define RGFW_GL_RENDER_TYPE RGFW_OS_BASED_VALUE(GLX_X_VISUAL_TYPE, 0x2003, 73, 0)
- #define RGFW_GL_ALPHA_SIZE RGFW_OS_BASED_VALUE(GLX_ALPHA_SIZE, 0x201b, 11, 0)
- #define RGFW_GL_DEPTH_SIZE RGFW_OS_BASED_VALUE(GLX_DEPTH_SIZE, 0x2022, 12, 0)
- #define RGFW_GL_DOUBLEBUFFER RGFW_OS_BASED_VALUE(GLX_DOUBLEBUFFER, 0x2011, 5, 0)
- #define RGFW_GL_STENCIL_SIZE RGFW_OS_BASED_VALUE(GLX_STENCIL_SIZE, 0x2023, 13, 0)
- #define RGFW_GL_SAMPLES RGFW_OS_BASED_VALUE(GLX_SAMPLES, 0x2042, 55, 0)
- #define RGFW_GL_STEREO RGFW_OS_BASED_VALUE(GLX_STEREO, 0x2012, 6, 0)
- #define RGFW_GL_AUX_BUFFERS RGFW_OS_BASED_VALUE(GLX_AUX_BUFFERS, 0x2024, 7, 0)
-
-#if defined(RGFW_X11) || defined(RGFW_WINDOWS)
- #define RGFW_GL_DRAW RGFW_OS_BASED_VALUE(GLX_X_RENDERABLE, 0x2001, 0, 0)
- #define RGFW_GL_DRAW_TYPE RGFW_OS_BASED_VALUE(GLX_RENDER_TYPE, 0x2013, 0, 0)
- #define RGFW_GL_FULL_FORMAT RGFW_OS_BASED_VALUE(GLX_TRUE_COLOR, 0x2027, 0, 0)
- #define RGFW_GL_RED_SIZE RGFW_OS_BASED_VALUE(GLX_RED_SIZE, 0x2015, 0, 0)
- #define RGFW_GL_GREEN_SIZE RGFW_OS_BASED_VALUE(GLX_GREEN_SIZE, 0x2017, 0, 0)
- #define RGFW_GL_BLUE_SIZE RGFW_OS_BASED_VALUE(GLX_BLUE_SIZE, 0x2019, 0, 0)
- #define RGFW_GL_USE_RGBA RGFW_OS_BASED_VALUE(GLX_RGBA_BIT, 0x202B, 0, 0)
- #define RGFW_GL_ACCUM_RED_SIZE RGFW_OS_BASED_VALUE(14, 0x201E, 0, 0)
- #define RGFW_GL_ACCUM_GREEN_SIZE RGFW_OS_BASED_VALUE(15, 0x201F, 0, 0)
- #define RGFW_GL_ACCUM_BLUE_SIZE RGFW_OS_BASED_VALUE(16, 0x2020, 0, 0)
- #define RGFW_GL_ACCUM_ALPHA_SIZE RGFW_OS_BASED_VALUE(17, 0x2021, 0, 0)
- #define RGFW_GL_SRGB RGFW_OS_BASED_VALUE(0x20b2, 0x3089, 0, 0)
- #define RGFW_GL_NOERROR RGFW_OS_BASED_VALUE(0x31b3, 0x31b3, 0, 0)
- #define RGFW_GL_FLAGS RGFW_OS_BASED_VALUE(GLX_CONTEXT_FLAGS_ARB, 0x2094, 0, 0)
- #define RGFW_GL_RELEASE_BEHAVIOR RGFW_OS_BASED_VALUE(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, 0x2097 , 0, 0)
- #define RGFW_GL_CONTEXT_RELEASE RGFW_OS_BASED_VALUE(GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB, 0x2098, 0, 0)
- #define RGFW_GL_CONTEXT_NONE RGFW_OS_BASED_VALUE(GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB, 0x0000, 0, 0)
- #define RGFW_GL_FLAGS RGFW_OS_BASED_VALUE(GLX_CONTEXT_FLAGS_ARB, 0x2094, 0, 0)
- #define RGFW_GL_DEBUG_BIT RGFW_OS_BASED_VALUE(GLX_CONTEXT_FLAGS_ARB, 0x2094, 0, 0)
- #define RGFW_GL_ROBUST_BIT RGFW_OS_BASED_VALUE(GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB, 0x00000004, 0, 0)
-#endif
-
-#ifdef RGFW_WINDOWS
- #define WGL_SUPPORT_OPENGL_ARB 0x2010
- #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_CORE_PROFILE_BIT_ARB 0x00000001
- #define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
- #define WGL_SAMPLE_BUFFERS_ARB 0x2041
- #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9
- #define WGL_PIXEL_TYPE_ARB 0x2013
- #define WGL_TYPE_RGBA_ARB 0x202B
-
- #define WGL_TRANSPARENT_ARB 0x200A
-#endif
-
-/* The window'ing api needs to know how to render the data we (or opengl) give it
- MacOS and Windows do this using a structure called a "pixel format"
- X11 calls it a "Visual"
- This function returns the attributes for the format we want */
-i32* RGFW_initFormatAttribs(void);
-i32* RGFW_initFormatAttribs(void) {
- static i32 attribs[] = {
- #if defined(RGFW_X11) || defined(RGFW_WINDOWS)
- RGFW_GL_RENDER_TYPE,
- RGFW_GL_FULL_FORMAT,
- RGFW_GL_DRAW, 1,
- 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_SUPPORT_OPENGL_ARB, 1,
- WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
- WGL_COLOR_BITS_ARB, 32,
- #endif
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- };
-
- size_t index = (sizeof(attribs) / sizeof(attribs[0])) - 27;
-
- #define RGFW_GL_ADD_ATTRIB(attrib, attVal) \
- if (attVal) { \
- attribs[index] = attrib;\
- attribs[index + 1] = attVal;\
- index += 2;\
- }
-
- #if defined(RGFW_MACOS) && defined(RGFW_COCOA_GRAPHICS_SWITCHING)
- RGFW_GL_ADD_ATTRIB(96, kCGLPFASupportsAutomaticGraphicsSwitching);
- #endif
-
- RGFW_GL_ADD_ATTRIB(RGFW_GL_DOUBLEBUFFER, 1);
-
- RGFW_GL_ADD_ATTRIB(RGFW_GL_ALPHA_SIZE, RGFW_GL_HINTS[RGFW_glAlpha]);
- RGFW_GL_ADD_ATTRIB(RGFW_GL_DEPTH_SIZE, RGFW_GL_HINTS[RGFW_glDepth]);
- RGFW_GL_ADD_ATTRIB(RGFW_GL_STENCIL_SIZE, RGFW_GL_HINTS[RGFW_glStencil]);
- RGFW_GL_ADD_ATTRIB(RGFW_GL_STEREO, RGFW_GL_HINTS[RGFW_glStereo]);
- RGFW_GL_ADD_ATTRIB(RGFW_GL_AUX_BUFFERS, RGFW_GL_HINTS[RGFW_glAuxBuffers]);
-
- #if defined(RGFW_X11) || defined(RGFW_WINDOWS)
- RGFW_GL_ADD_ATTRIB(RGFW_GL_RED_SIZE, RGFW_GL_HINTS[RGFW_glRed]);
- RGFW_GL_ADD_ATTRIB(RGFW_GL_GREEN_SIZE, RGFW_GL_HINTS[RGFW_glBlue]);
- RGFW_GL_ADD_ATTRIB(RGFW_GL_BLUE_SIZE, RGFW_GL_HINTS[RGFW_glGreen]);
- #endif
-
- #if defined(RGFW_X11) || defined(RGFW_WINDOWS)
- RGFW_GL_ADD_ATTRIB(RGFW_GL_ACCUM_RED_SIZE, RGFW_GL_HINTS[RGFW_glAccumRed]);
- RGFW_GL_ADD_ATTRIB(RGFW_GL_ACCUM_GREEN_SIZE, RGFW_GL_HINTS[RGFW_glAccumBlue]);
- RGFW_GL_ADD_ATTRIB(RGFW_GL_ACCUM_BLUE_SIZE, RGFW_GL_HINTS[RGFW_glAccumGreen]);
- RGFW_GL_ADD_ATTRIB(RGFW_GL_ACCUM_ALPHA_SIZE, RGFW_GL_HINTS[RGFW_glAccumAlpha]);
- RGFW_GL_ADD_ATTRIB(RGFW_GL_SRGB, RGFW_GL_HINTS[RGFW_glSRGB]);
- RGFW_GL_ADD_ATTRIB(RGFW_GL_NOERROR, RGFW_GL_HINTS[RGFW_glNoError]);
-
- if (RGFW_GL_HINTS[RGFW_glReleaseBehavior] == RGFW_releaseFlush) {
- RGFW_GL_ADD_ATTRIB(RGFW_GL_RELEASE_BEHAVIOR, RGFW_GL_CONTEXT_RELEASE);
- } else if (RGFW_GL_HINTS[RGFW_glReleaseBehavior] == RGFW_glReleaseNone) {
- RGFW_GL_ADD_ATTRIB(RGFW_GL_RELEASE_BEHAVIOR, RGFW_GL_CONTEXT_NONE);
- }
-
- i32 flags = 0;
- if (RGFW_GL_HINTS[RGFW_glDebug]) flags |= RGFW_GL_DEBUG_BIT;
- if (RGFW_GL_HINTS[RGFW_glRobustness]) flags |= RGFW_GL_ROBUST_BIT;
- RGFW_GL_ADD_ATTRIB(RGFW_GL_FLAGS, flags);
- #else
- i32 accumSize = (i32)(RGFW_GL_HINTS[RGFW_glAccumRed] + RGFW_GL_HINTS[RGFW_glAccumGreen] + RGFW_GL_HINTS[RGFW_glAccumBlue] + RGFW_GL_HINTS[RGFW_glAccumAlpha]) / 4;
- RGFW_GL_ADD_ATTRIB(14, accumSize);
- #endif
-
- #ifndef RGFW_X11
- RGFW_GL_ADD_ATTRIB(RGFW_GL_SAMPLES, RGFW_GL_HINTS[RGFW_glSamples]);
- #endif
-
- #ifdef RGFW_MACOS
- if (_RGFW.root->_flags & RGFW_windowOpenglSoftware) {
- RGFW_GL_ADD_ATTRIB(70, kCGLRendererGenericFloatID);
- } else {
- attribs[index] = RGFW_GL_RENDER_TYPE;
- index += 1;
- }
- #endif
-
- #ifdef RGFW_MACOS
- /* macOS has the surface attribs and the opengl attribs connected for some reason
- maybe this is to give macOS more control to limit openGL/the opengl version? */
-
- attribs[index] = 99;
- attribs[index + 1] = 0x1000;
-
-
- if (RGFW_GL_HINTS[RGFW_glMajor] >= 4 || RGFW_GL_HINTS[RGFW_glMajor] >= 3) {
- attribs[index + 1] = (i32) ((RGFW_GL_HINTS[RGFW_glMajor] >= 4) ? 0x4100 : 0x3200);
- }
- #endif
-
- RGFW_GL_ADD_ATTRIB(0, 0);
-
- return attribs;
-}
-
-/* EGL only (no OSMesa nor normal OPENGL) */
-#elif defined(RGFW_EGL)
-
-#include
-
-#if defined(RGFW_LINK_EGL)
- typedef EGLBoolean(EGLAPIENTRY* PFN_eglInitialize)(EGLDisplay, EGLint*, EGLint*);
-
- PFNEGLINITIALIZEPROC eglInitializeSource;
- PFNEGLGETCONFIGSPROC eglGetConfigsSource;
- PFNEGLCHOOSECONFIgamepadROC 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_SURFACE_MAJOR_VERSION_KHR 0x3098
-#define EGL_SURFACE_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_window_initOpenGL(RGFW_window* win) {
-#if defined(RGFW_LINK_EGL)
- eglInitializeSource = (PFNEGLINITIALIZEPROC) eglGetProcAddress("eglInitialize");
- eglGetConfigsSource = (PFNEGLGETCONFIGSPROC) eglGetProcAddress("eglGetConfigs");
- eglChooseConfigSource = (PFNEGLCHOOSECONFIgamepadROC) 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");
-
- RGFW_ASSERT(eglInitializeSource != NULL &&
- eglGetConfigsSource != NULL &&
- eglChooseConfigSource != NULL &&
- eglCreateWindowSurfaceSource != NULL &&
- eglCreateContextSource != NULL &&
- eglMakeCurrentSource != NULL &&
- eglGetDisplaySource != NULL &&
- eglSwapBuffersSource != NULL &&
- eglSwapIntervalsSource != NULL &&
- eglBindAPISource != NULL &&
- eglDestroyContextSource != NULL &&
- eglTerminateSource != NULL &&
- eglDestroySurfaceSource != NULL);
-#endif /* RGFW_LINK_EGL */
-
-#ifdef RGFW_WAYLAND
- if (RGFW_useWaylandBool)
- win->src.eglWindow = wl_egl_window_create(win->src.surface, win->r.w, win->r.h);
-#endif
-
- #ifdef RGFW_WINDOWS
- win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.hdc);
- #elif defined(RGFW_MACOS)
- win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType)0);
- #elif defined(RGFW_WAYLAND)
- if (RGFW_useWaylandBool)
- win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.wl_display);
- else
- #endif
- #ifdef RGFW_X11
- win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.display);
- #else
- {}
- #endif
- #if !defined(RGFW_WAYLAND) && !defined(RGFW_WINDOWS) && !defined(RGFW_X11)
- win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.display);
- #endif
-
- EGLint major, minor;
-
- eglInitialize(win->src.EGL_display, &major, &minor);
-
- #ifndef EGL_OPENGL_ES1_BIT
- #define EGL_OPENGL_ES1_BIT 0x1
- #endif
-
- EGLint egl_config[24] = {
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
- EGL_RENDERABLE_TYPE,
- #ifdef RGFW_OPENGL_ES1
- EGL_OPENGL_ES1_BIT,
- #elif defined(RGFW_OPENGL_ES3)
- EGL_OPENGL_ES3_BIT,
- #elif defined(RGFW_OPENGL_ES2)
- EGL_OPENGL_ES2_BIT,
- #else
- EGL_OPENGL_BIT,
- #endif
- EGL_NONE, EGL_NONE
- };
-
- {
- size_t index = 7;
- EGLint* attribs = egl_config;
-
- RGFW_GL_ADD_ATTRIB(EGL_RED_SIZE, RGFW_GL_HINTS[RGFW_glRed]);
- RGFW_GL_ADD_ATTRIB(EGL_GREEN_SIZE, RGFW_GL_HINTS[RGFW_glBlue]);
- RGFW_GL_ADD_ATTRIB(EGL_BLUE_SIZE, RGFW_GL_HINTS[RGFW_glGreen]);
- RGFW_GL_ADD_ATTRIB(EGL_ALPHA_SIZE, RGFW_GL_HINTS[RGFW_glAlpha]);
- RGFW_GL_ADD_ATTRIB(EGL_DEPTH_SIZE, RGFW_GL_HINTS[RGFW_glDepth]);
-
- if (RGFW_GL_HINTS[RGFW_glSRGB])
- RGFW_GL_ADD_ATTRIB(0x3089, RGFW_GL_HINTS[RGFW_glSRGB]);
-
- RGFW_GL_ADD_ATTRIB(EGL_NONE, EGL_NONE);
- }
-
- EGLConfig config;
- EGLint numConfigs;
- eglChooseConfig(win->src.EGL_display, egl_config, &config, 1, &numConfigs);
-
- #if defined(RGFW_MACOS)
- void* layer = RGFW_cocoaGetLayer();
-
- RGFW_window_cocoaSetLayer(win, layer);
-
- win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) layer, NULL);
- #elif defined(RGFW_WINDOWS)
- win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL);
- #elif defined(RGFW_WAYLAND)
- if (RGFW_useWaylandBool)
- win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.eglWindow, NULL);
- else
- #endif
- #ifdef RGFW_X11
- win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL);
- #else
- {}
- #endif
- #if !defined(RGFW_X11) && !defined(RGFW_WAYLAND) && !defined(RGFW_MACOS)
- win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL);
- #endif
-
- EGLint attribs[12];
- size_t index = 0;
-
-#ifdef RGFW_OPENGL_ES1
- RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_CLIENT_VERSION, 1);
-#elif defined(RGFW_OPENGL_ES2)
- RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_CLIENT_VERSION, 2);
-#elif defined(RGFW_OPENGL_ES3)
- RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_CLIENT_VERSION, 3);
-#endif
-
- RGFW_GL_ADD_ATTRIB(EGL_STENCIL_SIZE, RGFW_GL_HINTS[RGFW_glStencil]);
- RGFW_GL_ADD_ATTRIB(EGL_SAMPLES, RGFW_GL_HINTS[RGFW_glSamples]);
-
- if (RGFW_GL_HINTS[RGFW_glDoubleBuffer] == 0)
- RGFW_GL_ADD_ATTRIB(EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER);
-
- if (RGFW_GL_HINTS[RGFW_glMajor]) {
- RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MAJOR_VERSION, RGFW_GL_HINTS[RGFW_glMajor]);
- RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MINOR_VERSION, RGFW_GL_HINTS[RGFW_glMinor]);
-
- if (RGFW_GL_HINTS[RGFW_glProfile] == RGFW_glCore) {
- RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT);
- }
- else {
- RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT);
- }
- }
-
- RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_ROBUST_ACCESS, RGFW_GL_HINTS[RGFW_glRobustness]);
- RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_DEBUG, RGFW_GL_HINTS[RGFW_glDebug]);
- if (RGFW_GL_HINTS[RGFW_glReleaseBehavior] == RGFW_releaseFlush) {
- RGFW_GL_ADD_ATTRIB(0x2097, 0x2098);
- } else {
- RGFW_GL_ADD_ATTRIB(0x2096, 0x0000);
- }
-
- RGFW_GL_ADD_ATTRIB(EGL_NONE, EGL_NONE);
-
- #if defined(RGFW_OPENGL_ES1) || defined(RGFW_OPENGL_ES2) || defined(RGFW_OPENGL_ES3)
- eglBindAPI(EGL_OPENGL_ES_API);
- #else
- eglBindAPI(EGL_OPENGL_API);
- #endif
-
- win->src.EGL_context = eglCreateContext(win->src.EGL_display, config, EGL_NO_CONTEXT, attribs);
-
- if (win->src.EGL_context == NULL) {
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errEGLContext, RGFW_DEBUG_CTX(win, 0), "failed to create an EGL opengl context");
- return;
- }
-
- eglMakeCurrent(win->src.EGL_display, win->src.EGL_surface, win->src.EGL_surface, win->src.EGL_context);
- eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface);
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "EGL opengl context initalized");
-}
-
-void RGFW_window_freeOpenGL(RGFW_window* win) {
- if (win->src.EGL_display == NULL) return;
-
- eglDestroySurface(win->src.EGL_display, win->src.EGL_surface);
- eglDestroyContext(win->src.EGL_display, win->src.EGL_context);
- eglTerminate(win->src.EGL_display);
- win->src.EGL_display = NULL;
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "EGL opengl context freed");
-}
-
-void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
- if (win == NULL)
- eglMakeCurrent(_RGFW.root->src.EGL_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- else {
- eglMakeCurrent(win->src.EGL_display, win->src.EGL_surface, win->src.EGL_surface, win->src.EGL_context);
- }
-}
-
-void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) { eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); }
-
-void* RGFW_getCurrent_OpenGL(void) { return eglGetCurrentContext(); }
-
-#ifdef RGFW_APPLE
-void* RGFWnsglFramework = NULL;
-#elif defined(RGFW_WINDOWS)
-HMODULE RGFW_wgl_dll = NULL;
-#endif
-
-RGFW_proc RGFW_getProcAddress(const char* procname) {
- #if defined(RGFW_WINDOWS)
- RGFW_proc proc = (RGFW_proc) GetProcAddress(RGFW_wgl_dll, procname);
-
- if (proc)
- return proc;
- #endif
-
- return (RGFW_proc) eglGetProcAddress(procname);
-}
-
-RGFW_bool RGFW_extensionSupportedPlatform(const char* extension, size_t len) {
- const char* extensions = eglQueryString(_RGFW.root->src.EGL_display, EGL_EXTENSIONS);
- return extensions != NULL && RGFW_extensionSupportedStr(extensions, extension, len);
-}
-
-void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) {
- RGFW_ASSERT(win != NULL);
-
- eglSwapInterval(win->src.EGL_display, swapInterval);
-
-}
-
-#endif /* RGFW_EGL */
-
-/*
- end of RGFW_EGL defines
-*/
-#endif /* end of RGFW_GL (OpenGL, EGL, OSMesa )*/
-
-/*
- RGFW_VULKAN defines
-*/
-#ifdef RGFW_VULKAN
-#ifdef RGFW_MACOS
-#include
-#endif
-
-const char** RGFW_getVKRequiredInstanceExtensions(size_t* count) {
- static const char* arr[2] = {VK_KHR_SURFACE_EXTENSION_NAME};
- arr[1] = RGFW_VK_SURFACE;
- if (count != NULL) *count = 2;
-
- return (const char**)arr;
-}
-
-VkResult RGFW_window_createVKSurface(RGFW_window* win, VkInstance instance, VkSurfaceKHR* surface) {
- RGFW_ASSERT(win != NULL); RGFW_ASSERT(instance);
- RGFW_ASSERT(surface != NULL);
-
- *surface = VK_NULL_HANDLE;
-
-#ifdef RGFW_X11
- RGFW_GOTO_WAYLAND(0);
- VkXlibSurfaceCreateInfoKHR x11 = { VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, 0, 0, (Display*) win->src.display, (Window) win->src.window };
- return vkCreateXlibSurfaceKHR(instance, &x11, NULL, surface);
-#endif
-#if defined(RGFW_WAYLAND)
-RGFW_WAYLAND_LABEL
- VkWaylandSurfaceCreateInfoKHR wayland = { VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, 0, 0, (struct wl_display*) win->src.wl_display, (struct wl_surface*) win->src.surface };
- return vkCreateWaylandSurfaceKHR(instance, &wayland, NULL, surface);
-#elif defined(RGFW_WINDOWS)
- VkWin32SurfaceCreateInfoKHR win32 = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, 0, 0, GetModuleHandle(NULL), (HWND)win->src.window };
-
- return vkCreateWin32SurfaceKHR(instance, &win32, NULL, surface);
-#elif defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11)
- void* contentView = ((void* (*)(id, SEL))objc_msgSend)((id)win->src.window, sel_getUid("contentView"));
- VkMacOSSurfaceCreateFlagsMVK macos = { VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK, 0, 0, win->src.display, (void*)contentView };
-
- return vkCreateMacOSSurfaceMVK(instance, &macos, NULL, surface);
-#endif
-}
-
-
-RGFW_bool RGFW_getVKPresentationSupport(VkInstance instance, VkPhysicalDevice physicalDevice, u32 queueFamilyIndex) {
- RGFW_ASSERT(instance);
- if (_RGFW.windowCount == -1 || _RGFW_init == RGFW_FALSE) RGFW_init();
-#ifdef RGFW_X11
- RGFW_GOTO_WAYLAND(0);
- Visual* visual = DefaultVisual(_RGFW.display, DefaultScreen(_RGFW.display));
- if (_RGFW.root)
- visual = _RGFW.root->src.visual.visual;
-
- RGFW_bool out = vkGetPhysicalDeviceXlibPresentationSupportKHR(physicalDevice, queueFamilyIndex, _RGFW.display, XVisualIDFromVisual(visual));
- return out;
-#endif
-#if defined(RGFW_WAYLAND)
-RGFW_WAYLAND_LABEL
- RGFW_bool wlout = vkGetPhysicalDeviceWaylandPresentationSupportKHR(physicalDevice, queueFamilyIndex, _RGFW.wl_display);
- return wlout;
-#elif defined(RGFW_WINDOWS)
-#elif defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11)
- return RGFW_FALSE; /* TODO */
-#endif
-}
-#endif /* end of RGFW_vulkan */
-
-/*
-This is where OS specific stuff starts
-*/
-
-
-#if (defined(RGFW_WAYLAND) || defined(RGFW_X11)) && !defined(RGFW_NO_LINUX)
- int RGFW_eventWait_forceStop[] = {0, 0, 0}; /* for wait events */
-
- #if defined(__linux__)
- #include
- #include
- #include
- #include
-
- u32 RGFW_linux_updateGamepad(RGFW_window* win);
- u32 RGFW_linux_updateGamepad(RGFW_window* win) {
- /* check for new gamepads */
- static const char* str[] = {"/dev/input/js0", "/dev/input/js1", "/dev/input/js2", "/dev/input/js3", "/dev/input/js4", "/dev/input/js5"};
- static u8 RGFW_rawGamepads[6];
- {
- u16 i;
- for (i = 0; i < 6; i++) {
- u16 index = RGFW_gamepadCount;
- if (RGFW_rawGamepads[i]) {
- struct input_id device_info;
- if (ioctl(RGFW_rawGamepads[i], EVIOCGID, &device_info) == -2) {
- if (errno == ENODEV) {
- RGFW_rawGamepads[i] = 0;
- }
- }
- continue;
- }
-
- i32 js = open(str[i], O_RDONLY);
-
- if (js <= 0)
- break;
-
- if (RGFW_gamepadCount >= 4) {
- close(js);
- break;
- }
-
- RGFW_rawGamepads[i] = 1;
-
- int axes, buttons;
- if (ioctl(js, JSIOCGAXES, &axes) < 0 || ioctl(js, JSIOCGBUTTONS, &buttons) < 0) {
- close(js);
- continue;
- }
-
- if (buttons <= 5 || buttons >= 30) {
- close(js);
- continue;
- }
-
- RGFW_gamepadCount++;
-
- RGFW_gamepads[index] = js;
-
- ioctl(js, JSIOCGNAME(sizeof(RGFW_gamepads_name[index])), RGFW_gamepads_name[index]);
- RGFW_gamepads_name[index][sizeof(RGFW_gamepads_name[index]) - 1] = 0;
-
- u8 j;
- for (j = 0; j < 16; j++) {
- RGFW_gamepadPressed[index][j].prev = 0;
- RGFW_gamepadPressed[index][j].current = 0;
- }
-
- win->event.type = RGFW_gamepadConnected;
-
- RGFW_gamepads_type[index] = RGFW_gamepadUnknown;
- if (RGFW_STRSTR(RGFW_gamepads_name[index], "Microsoft") || RGFW_STRSTR(RGFW_gamepads_name[index], "X-Box"))
- RGFW_gamepads_type[index] = RGFW_gamepadMicrosoft;
- else if (RGFW_STRSTR(RGFW_gamepads_name[index], "PlayStation") || RGFW_STRSTR(RGFW_gamepads_name[index], "PS3") || RGFW_STRSTR(RGFW_gamepads_name[index], "PS4") || RGFW_STRSTR(RGFW_gamepads_name[index], "PS5"))
- RGFW_gamepads_type[index] = RGFW_gamepadSony;
- else if (RGFW_STRSTR(RGFW_gamepads_name[index], "Nintendo"))
- RGFW_gamepads_type[index] = RGFW_gamepadNintendo;
- else if (RGFW_STRSTR(RGFW_gamepads_name[index], "Logitech"))
- RGFW_gamepads_type[index] = RGFW_gamepadLogitech;
-
- win->event.gamepad = index;
- RGFW_gamepadCallback(win, index, 1);
- return 1;
- }
- }
- /* check gamepad events */
- u8 i;
-
- for (i = 0; i < RGFW_gamepadCount; i++) {
- struct js_event e;
- if (RGFW_gamepads[i] == 0)
- continue;
-
- i32 flags = fcntl(RGFW_gamepads[i], F_GETFL, 0);
- fcntl(RGFW_gamepads[i], F_SETFL, flags | O_NONBLOCK);
-
- ssize_t bytes;
- while ((bytes = read(RGFW_gamepads[i], &e, sizeof(e))) > 0) {
- switch (e.type) {
- case JS_EVENT_BUTTON: {
- size_t typeIndex = 0;
- if (RGFW_gamepads_type[i] == RGFW_gamepadMicrosoft) typeIndex = 1;
- else if (RGFW_gamepads_type[i] == RGFW_gamepadLogitech) typeIndex = 2;
-
- win->event.type = e.value ? RGFW_gamepadButtonPressed : RGFW_gamepadButtonReleased;
- u8 RGFW_linux2RGFW[3][RGFW_gamepadR3 + 8] = {{ /* ps */
- RGFW_gamepadA, RGFW_gamepadB, RGFW_gamepadY, RGFW_gamepadX, RGFW_gamepadL1, RGFW_gamepadR1, RGFW_gamepadL2, RGFW_gamepadR2,
- RGFW_gamepadSelect, RGFW_gamepadStart, RGFW_gamepadHome, RGFW_gamepadL3, RGFW_gamepadR3, RGFW_gamepadUp, RGFW_gamepadDown, RGFW_gamepadLeft, RGFW_gamepadRight,
- },{ /* xbox */
- RGFW_gamepadA, RGFW_gamepadB, RGFW_gamepadX, RGFW_gamepadY, RGFW_gamepadL1, RGFW_gamepadR1, RGFW_gamepadSelect, RGFW_gamepadStart,
- RGFW_gamepadHome, RGFW_gamepadL3, RGFW_gamepadR3, 255, 255, RGFW_gamepadUp, RGFW_gamepadDown, RGFW_gamepadLeft, RGFW_gamepadRight
- },{ /* Logitech */
- RGFW_gamepadA, RGFW_gamepadB, RGFW_gamepadX, RGFW_gamepadY, RGFW_gamepadL1, RGFW_gamepadR1, RGFW_gamepadL2, RGFW_gamepadR2,
- RGFW_gamepadSelect, RGFW_gamepadStart, RGFW_gamepadHome, RGFW_gamepadL3, RGFW_gamepadR3, RGFW_gamepadUp, RGFW_gamepadDown, RGFW_gamepadLeft, RGFW_gamepadRight
- }
- };
-
- win->event.button = RGFW_linux2RGFW[typeIndex][e.number];
- win->event.gamepad = i;
- if (win->event.button == 255) break;
-
- RGFW_gamepadPressed[i][win->event.button].prev = RGFW_gamepadPressed[i][win->event.button].current;
- RGFW_gamepadPressed[i][win->event.button].current = RGFW_BOOL(e.value);
- RGFW_gamepadButtonCallback(win, i, win->event.button, RGFW_BOOL(e.value));
-
- return 1;
- }
- case JS_EVENT_AXIS: {
- size_t axis = e.number / 2;
- if (axis == 2) axis = 1;
-
- ioctl(RGFW_gamepads[i], JSIOCGAXES, &win->event.axisesCount);
- win->event.axisesCount = 2;
-
- if (axis < 3) {
- if (e.number == 0 || e.number == 3)
- RGFW_gamepadAxes[i][axis].x = (i32)((e.value / 32767.0f) * 100);
- else if (e.number == 1 || e.number == 4) {
- RGFW_gamepadAxes[i][axis].y = (i32)((e.value / 32767.0f) * 100);
- }
- }
-
- win->event.axis[axis] = RGFW_gamepadAxes[i][axis];
- win->event.type = RGFW_gamepadAxisMove;
- win->event.gamepad = i;
- win->event.whichAxis = (u8)axis;
- RGFW_gamepadAxisCallback(win, i, win->event.axis, win->event.axisesCount, win->event.whichAxis);
- return 1;
- }
- default: break;
- }
- }
- if (bytes == -1 && errno == ENODEV) {
- RGFW_gamepadCount--;
- close(RGFW_gamepads[i]);
- RGFW_gamepads[i] = 0;
-
- win->event.type = RGFW_gamepadDisconnected;
- win->event.gamepad = i;
- RGFW_gamepadCallback(win, i, 0);
- return 1;
- }
- }
- return 0;
- }
-
- #endif
-#endif
-
-
-
-/*
-
- Start of Wayland defines
-
-
-*/
-
-#ifdef RGFW_WAYLAND
-/*
-Wayland TODO: (out of date)
-- fix RGFW_keyPressed lock state
-
- RGFW_windowMoved, the window was moved (by the user)
- RGFW_windowResized the window was resized (by the user), [on WASM this means the browser was resized]
- RGFW_windowRefresh The window content needs to be refreshed
-
- RGFW_DND a file has been dropped into the window
- RGFW_DNDInit
-
-- window args:
- #define RGFW_windowNoResize the window cannot be resized by the user
- #define RGFW_windowAllowDND the window supports drag and drop
- #define RGFW_scaleToMonitor scale the window to the screen
-
-- other missing functions functions ("TODO wayland") (~30 functions)
-- fix buffer rendering weird behavior
-*/
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-RGFW_window* RGFW_key_win = NULL;
-
-/* wayland global garbage (wayland bad, X11 is fine (ish) (not really)) */
-#include "xdg-shell.h"
-#include "xdg-decoration-unstable-v1.h"
-
-struct xkb_context *xkb_context;
-struct xkb_keymap *keymap = NULL;
-struct xkb_state *xkb_state = NULL;
-enum zxdg_toplevel_decoration_v1_mode client_preferred_mode, RGFW_current_mode;
-struct zxdg_decoration_manager_v1 *decoration_manager = NULL;
-
-struct wl_cursor_theme* RGFW_wl_cursor_theme = NULL;
-struct wl_surface* RGFW_cursor_surface = NULL;
-struct wl_cursor_image* RGFW_cursor_image = NULL;
-
-void xdg_wm_base_ping_handler(void *data,
- struct xdg_wm_base *wm_base, uint32_t serial)
-{
- RGFW_UNUSED(data);
- xdg_wm_base_pong(wm_base, serial);
-}
-
-const struct xdg_wm_base_listener xdg_wm_base_listener = {
- .ping = xdg_wm_base_ping_handler,
-};
-
-RGFW_bool RGFW_wl_configured = 0;
-
-void xdg_surface_configure_handler(void *data,
- struct xdg_surface *xdg_surface, uint32_t serial)
-{
- RGFW_UNUSED(data);
- xdg_surface_ack_configure(xdg_surface, serial);
- RGFW_wl_configured = 1;
-}
-
-const struct xdg_surface_listener xdg_surface_listener = {
- .configure = xdg_surface_configure_handler,
-};
-
-void xdg_toplevel_configure_handler(void *data,
- struct xdg_toplevel *toplevel, int32_t width, int32_t height,
- struct wl_array *states)
-{
- RGFW_UNUSED(data); RGFW_UNUSED(toplevel); RGFW_UNUSED(states);
- RGFW_UNUSED(width); RGFW_UNUSED(height);
-}
-
-void xdg_toplevel_close_handler(void *data,
- struct xdg_toplevel *toplevel)
-{
- RGFW_UNUSED(data);
- RGFW_window* win = (RGFW_window*)xdg_toplevel_get_user_data(toplevel);
- if (win == NULL)
- win = RGFW_key_win;
-
- RGFW_eventQueuePushEx(e.type = RGFW_quit; e._win = win);
- RGFW_windowQuitCallback(win);
-}
-
-void shm_format_handler(void *data,
- struct wl_shm *shm, uint32_t format)
-{
- RGFW_UNUSED(data); RGFW_UNUSED(shm); RGFW_UNUSED(format);
-}
-
-const struct wl_shm_listener shm_listener = {
- .format = shm_format_handler,
-};
-
-const struct xdg_toplevel_listener xdg_toplevel_listener = {
- .configure = xdg_toplevel_configure_handler,
- .close = xdg_toplevel_close_handler,
-};
-
-RGFW_window* RGFW_mouse_win = NULL;
-
-void pointer_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) {
- RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(serial); RGFW_UNUSED(surface_x); RGFW_UNUSED(surface_y);
- RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface);
- RGFW_mouse_win = win;
-
- RGFW_eventQueuePushEx(e.type = RGFW_mouseEnter;
- e.point = RGFW_POINT(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y));
- e._win = win);
-
- RGFW_mouseNotifyCallback(win, win->event.point, RGFW_TRUE);
-}
-void pointer_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface) {
- RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(serial); RGFW_UNUSED(surface);
- RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface);
- if (RGFW_mouse_win == win)
- RGFW_mouse_win = NULL;
-
- RGFW_eventQueuePushEx(e.type = RGFW_mouseLeave;
- e.point = win->event.point;
- e._win = win);
-
- RGFW_mouseNotifyCallback(win, win->event.point, RGFW_FALSE);
-}
-void pointer_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t x, wl_fixed_t y) {
- RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(x); RGFW_UNUSED(y);
-
- RGFW_ASSERT(RGFW_mouse_win != NULL);
- RGFW_eventQueuePushEx(e.type = RGFW_mousePosChanged;
- e.point = RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y));
- e._win = RGFW_mouse_win);
-
- RGFW_mousePosCallback(RGFW_mouse_win, RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y)), RGFW_mouse_win->event.vector);
-}
-void pointer_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
- RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(serial);
- RGFW_ASSERT(RGFW_mouse_win != NULL);
-
- u32 b = (button - 0x110);
-
- /* flip right and middle button codes */
- if (b == 1) b = 2;
- else if (b == 2) b = 1;
-
- RGFW_mouseButtons[b].prev = RGFW_mouseButtons[b].current;
- RGFW_mouseButtons[b].current = RGFW_BOOL(state);
-
- RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonReleased - RGFW_BOOL(state);
- e.point = RGFW_mouse_win->event.point;
- e.button = (u8)b;
- e._win = RGFW_mouse_win);
- RGFW_mouseButtonCallback(RGFW_mouse_win, (u8)b, 0, RGFW_BOOL(state));
-}
-void pointer_axis(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value) {
- RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(axis);
- RGFW_ASSERT(RGFW_mouse_win != NULL);
-
- double scroll = - wl_fixed_to_double(value);
-
- RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonPressed;
- e.point = RGFW_mouse_win->event.point;
- e.button = RGFW_mouseScrollUp + (scroll < 0);
- e.scroll = scroll;
- e._win = RGFW_mouse_win);
-
- RGFW_mouseButtonCallback(RGFW_mouse_win, RGFW_mouseScrollUp + (scroll < 0), scroll, 1);
-}
-
-void RGFW_doNothing(void) { }
-
-void keyboard_keymap (void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd, uint32_t size) {
- RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(format);
-
- char *keymap_string = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
- xkb_keymap_unref (keymap);
- keymap = xkb_keymap_new_from_string (xkb_context, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
-
- munmap (keymap_string, size);
- close (fd);
- xkb_state_unref (xkb_state);
- xkb_state = xkb_state_new (keymap);
-}
-void keyboard_enter (void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
- RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(keys);
-
- RGFW_key_win = (RGFW_window*)wl_surface_get_user_data(surface);
-
- RGFW_key_win->_flags |= RGFW_windowFocus;
- RGFW_eventQueuePushEx(e.type = RGFW_focusIn; e._win = RGFW_key_win);
- RGFW_focusCallback(RGFW_key_win, RGFW_TRUE);
-
- if ((RGFW_key_win->_flags & RGFW_HOLD_MOUSE)) RGFW_window_mouseHold(RGFW_key_win, RGFW_AREA(RGFW_key_win->r.w, RGFW_key_win->r.h));
-}
-void keyboard_leave (void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface) {
- RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial);
-
- RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface);
- if (RGFW_key_win == win)
- RGFW_key_win = NULL;
-
- RGFW_eventQueuePushEx(e.type = RGFW_focusOut; e._win = win);
- RGFW_focusCallback(win, RGFW_FALSE);
- RGFW_window_focusLost(win);
-}
-void keyboard_key (void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
- RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time);
-
- if (RGFW_key_win == NULL) return;
-
- xkb_keysym_t keysym = xkb_state_key_get_one_sym(xkb_state, key + 8);
-
- u32 RGFWkey = RGFW_apiKeyToRGFW(key + 8);
- RGFW_keyboard[RGFWkey].prev = RGFW_keyboard[RGFWkey].current;
- RGFW_keyboard[RGFWkey].current = RGFW_BOOL(state);
-
- RGFW_eventQueuePushEx(e.type = (u8)(RGFW_keyPressed + state);
- e.key = (u8)RGFWkey;
- e.keyChar = (u8)keysym;
- e.repeat = RGFW_isHeld(RGFW_key_win, (u8)RGFWkey);
- e._win = RGFW_key_win);
-
- RGFW_updateKeyMods(RGFW_key_win, RGFW_BOOL(xkb_keymap_mod_get_index(keymap, "Lock")), RGFW_BOOL(xkb_keymap_mod_get_index(keymap, "Mod2")), RGFW_BOOL(xkb_keymap_mod_get_index(keymap, "ScrollLock")));
- RGFW_keyCallback(RGFW_key_win, (u8)RGFWkey, (u8)keysym, RGFW_key_win->event.keyMod, RGFW_BOOL(state));
-}
-void keyboard_modifiers (void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) {
- RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time);
- xkb_state_update_mask (xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
-}
-struct wl_keyboard_listener keyboard_listener = {&keyboard_keymap, &keyboard_enter, &keyboard_leave, &keyboard_key, &keyboard_modifiers, (void (*)(void *, struct wl_keyboard *,
-int, int))&RGFW_doNothing};
-
-void seat_capabilities (void *data, struct wl_seat *seat, uint32_t capabilities) {
- RGFW_UNUSED(data);
- static struct wl_pointer_listener pointer_listener = {&pointer_enter, &pointer_leave, &pointer_motion, &pointer_button, &pointer_axis, (void (*)(void *, struct wl_pointer *))&RGFW_doNothing, (void (*)(void *, struct wl_pointer *, uint32_t))&RGFW_doNothing, (void (*)(void *, struct wl_pointer *, uint32_t, uint32_t))&RGFW_doNothing, (void (*)(void *, struct wl_pointer *, uint32_t, int32_t))&RGFW_doNothing, (void (*)(void *, struct wl_pointer *, uint32_t, int32_t))&RGFW_doNothing, (void (*)(void*, struct wl_pointer*, uint32_t, uint32_t))&RGFW_doNothing};
-
- if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
- struct wl_pointer *pointer = wl_seat_get_pointer (seat);
- wl_pointer_add_listener (pointer, &pointer_listener, NULL);
- }
- if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
- struct wl_keyboard *keyboard = wl_seat_get_keyboard (seat);
- wl_keyboard_add_listener (keyboard, &keyboard_listener, NULL);
- }
-}
-struct wl_seat_listener seat_listener = {&seat_capabilities, (void (*)(void *, struct wl_seat *, const char *))&RGFW_doNothing};
-
-void wl_global_registry_handler(void *data,
- struct wl_registry *registry, uint32_t id, const char *interface,
- uint32_t version)
-{
- RGFW_window* win = (RGFW_window*)data;
- RGFW_UNUSED(version);
- if (RGFW_STRNCMP(interface, "wl_compositor", 16) == 0) {
- win->src.compositor = wl_registry_bind(registry,
- id, &wl_compositor_interface, 4);
- } else if (RGFW_STRNCMP(interface, "xdg_wm_base", 12) == 0) {
- win->src.xdg_wm_base = wl_registry_bind(registry,
- id, &xdg_wm_base_interface, 1);
- } else if (RGFW_STRNCMP(interface, zxdg_decoration_manager_v1_interface.name, 255) == 0) {
- decoration_manager = wl_registry_bind(registry, id, &zxdg_decoration_manager_v1_interface, 1);
- } else if (RGFW_STRNCMP(interface, "wl_shm", 7) == 0) {
- win->src.shm = wl_registry_bind(registry,
- id, &wl_shm_interface, 1);
- wl_shm_add_listener(win->src.shm, &shm_listener, NULL);
- } else if (RGFW_STRNCMP(interface,"wl_seat", 8) == 0) {
- win->src.seat = wl_registry_bind(registry, id, &wl_seat_interface, 1);
- wl_seat_add_listener(win->src.seat, &seat_listener, NULL);
- }
-}
-
-void wl_global_registry_remove(void *data, struct wl_registry *registry, uint32_t name) { RGFW_UNUSED(data); RGFW_UNUSED(registry); RGFW_UNUSED(name); }
-const struct wl_registry_listener registry_listener = {
- .global = wl_global_registry_handler,
- .global_remove = wl_global_registry_remove,
-};
-
-void decoration_handle_configure(void *data,
- struct zxdg_toplevel_decoration_v1 *decoration,
- enum zxdg_toplevel_decoration_v1_mode mode) {
- RGFW_UNUSED(data); RGFW_UNUSED(decoration);
- RGFW_current_mode = mode;
-}
-
-const struct zxdg_toplevel_decoration_v1_listener decoration_listener = {
- .configure = decoration_handle_configure,
-};
-
-void randname(char *buf) {
- struct timespec ts;
- clock_gettime(CLOCK_REALTIME, &ts);
- long r = ts.tv_nsec;
-
- int i;
- for (i = 0; i < 6; ++i) {
- buf[i] = (char)('A'+(r&15)+(r&16)*2);
- r >>= 5;
- }
-}
-
-size_t wl_stringlen(char* name) {
- size_t i = 0;
- while (name[i]) { i++; }
- return i;
-}
-
-int anonymous_shm_open(void) {
- char name[] = "/RGFW-wayland-XXXXXX";
- int retries = 100;
-
- do {
- randname(name + wl_stringlen(name) - 6);
-
- --retries;
- /* shm_open guarantees that O_CLOEXEC is set */
- int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
- if (fd >= 0) {
- shm_unlink(name);
- return fd;
- }
- } while (retries > 0 && errno == EEXIST);
-
- return -1;
-}
-
-int create_shm_file(off_t size) {
- int fd = anonymous_shm_open();
- if (fd < 0) {
- return fd;
- }
-
- if (ftruncate(fd, size) < 0) {
- close(fd);
- return -1;
- }
-
- return fd;
-}
-
-void wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) {
- RGFW_UNUSED(data); RGFW_UNUSED(cb); RGFW_UNUSED(time);
-
- #ifdef RGFW_BUFFER
- RGFW_window* win = (RGFW_window*)data;
- wl_surface_attach(win->src.surface, win->src.wl_buffer, 0, 0);
- wl_surface_damage_buffer(win->src.surface, 0, 0, win->r.w, win->r.h);
- wl_surface_commit(win->src.surface);
- #endif
-}
-
-const struct wl_callback_listener wl_surface_frame_listener = {
- .done = wl_surface_frame_done,
-};
-#endif /* RGFW_WAYLAND */
-/*
- End of Wayland defines
-*/
-
-/*
-
-
-Start of Linux / Unix defines
-
-
-*/
-
-#ifdef RGFW_UNIX
-#if !defined(RGFW_NO_X11_CURSOR) && defined(RGFW_X11)
-#include
-#endif
-
-#include
-
-#ifndef RGFW_NO_DPI
-#include
-#include
-#endif
-
-#include
-#include
-#include
-#include
-
-#include /* for converting keycode to string */
-#include /* for hiding */
-#include
-#include
-#include
-
-#include /* for data limits (mainly used in drag and drop functions) */
-#include
-
-/* atoms needed for drag and drop */
-Atom XdndAware, XtextPlain, XtextUriList;
-Atom RGFW_XUTF8_STRING = 0;
-
-Atom wm_delete_window = 0, RGFW_XCLIPBOARD = 0;
-
-#if defined(RGFW_X11) && !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD)
- typedef XcursorImage* (*PFN_XcursorImageCreate)(int, int);
- typedef void (*PFN_XcursorImageDestroy)(XcursorImage*);
- typedef Cursor(*PFN_XcursorImageLoadCursor)(Display*, const XcursorImage*);
-#endif
-#if defined(RGFW_OPENGL) && defined(RGFW_X11)
- typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
-#endif
-
-#if !defined(RGFW_NO_X11_XI_PRELOAD) && defined(RGFW_X11)
- typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int);
- PFN_XISelectEvents XISelectEventsSRC = NULL;
- #define XISelectEvents XISelectEventsSRC
-
- void* X11Xihandle = NULL;
-#endif
-
-#if !defined(RGFW_NO_X11_EXT_PRELOAD) && defined(RGFW_X11)
- typedef void (* PFN_XSyncIntToValue)(XSyncValue*, int);
- PFN_XSyncIntToValue XSyncIntToValueSRC = NULL;
- #define XSyncIntToValue XSyncIntToValueSRC
-
- typedef Status (* PFN_XSyncSetCounter)(Display*, XSyncCounter, XSyncValue);
- PFN_XSyncSetCounter XSyncSetCounterSRC = NULL;
- #define XSyncSetCounter XSyncSetCounterSRC
-
- typedef XSyncCounter (* PFN_XSyncCreateCounter)(Display*, XSyncValue);
- PFN_XSyncCreateCounter XSyncCreateCounterSRC = NULL;
- #define XSyncCreateCounter XSyncCreateCounterSRC
-
- typedef void (* PFN_XShapeCombineMask)(Display*,Window,int,int,int,Pixmap,int);
- PFN_XShapeCombineMask XShapeCombineMaskSRC;
- #define XShapeCombineMask XShapeCombineMaskSRC
-
- typedef void (* PFN_XShapeCombineRegion)(Display*,Window,int,int,int,Region,int);
- PFN_XShapeCombineRegion XShapeCombineRegionSRC;
- #define XShapeCombineRegion XShapeCombineRegionSRC
- void* X11XEXThandle = NULL;
-#endif
-
-#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) && defined(RGFW_X11)
- 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
-
-#ifdef RGFW_X11
-const char* RGFW_instName = NULL;
-void RGFW_setXInstName(const char* name) { RGFW_instName = name; }
-#endif
-
-#if defined(RGFW_OPENGL) && !defined(RGFW_EGL)
-RGFW_bool RGFW_extensionSupportedPlatform(const char * extension, size_t len) {
- const char* extensions = glXQueryExtensionsString(_RGFW.display, XDefaultScreen(_RGFW.display));
- return (extensions != NULL) && RGFW_extensionSupportedStr(extensions, extension, len);
-}
-RGFW_proc RGFW_getProcAddress(const char* procname) { return (RGFW_proc) glXGetProcAddress((GLubyte*) procname); }
-#endif
-
-void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area) {
- RGFW_GOTO_WAYLAND(0);
-
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
- win->buffer = (u8*)buffer;
- win->bufferSize = area;
-
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoBuffer, RGFW_DEBUG_CTX(win, 0), "createing a 4 channel buffer");
- #ifdef RGFW_X11
- #ifdef RGFW_OSMESA
- win->src.ctx = OSMesaCreateContext(OSMESA_BGRA, NULL);
- OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, area.w, area.h);
- OSMesaPixelStore(OSMESA_Y_UP, 0);
- #endif
-
- win->src.bitmap = XCreateImage(
- win->src.display, win->src.visual.visual, (u32)win->src.visual.depth,
- ZPixmap, 0, NULL, area.w, area.h, 32, 0
- );
- #endif
- #ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL {}
- u32 size = (u32)(win->r.w * win->r.h * 4);
- int fd = create_shm_file(size);
- if (fd < 0) {
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, RGFW_DEBUG_CTX(win, (u32)fd),"Failed to create a buffer.");
- exit(1);
- }
-
- win->src.buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if (win->src.buffer == MAP_FAILED) {
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, RGFW_DEBUG_CTX(win, 0), "mmap failed!");
- close(fd);
- exit(1);
- }
-
- win->_flags |= RGFW_BUFFER_ALLOC;
-
- struct wl_shm_pool* pool = wl_shm_create_pool(win->src.shm, fd, (i32)size);
- win->src.wl_buffer = wl_shm_pool_create_buffer(pool, 0, win->r.w, win->r.h, win->r.w * 4,
- WL_SHM_FORMAT_ARGB8888);
- wl_shm_pool_destroy(pool);
-
- close(fd);
-
- wl_surface_attach(win->src.surface, win->src.wl_buffer, 0, 0);
- wl_surface_commit(win->src.surface);
-
- u8 color[] = {0x00, 0x00, 0x00, 0xFF};
-
- size_t i;
- for (i = 0; i < area.w * area.h * 4; i += 4) {
- RGFW_MEMCPY(&win->buffer[i], color, 4);
- }
-
- RGFW_MEMCPY(win->src.buffer, win->buffer, (size_t)(win->r.w * win->r.h * 4));
-
- #if defined(RGFW_OSMESA)
- win->src.ctx = OSMesaCreateContext(OSMESA_BGRA, NULL);
- OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, area.w, area.h);
- OSMesaPixelStore(OSMESA_Y_UP, 0);
- #endif
- #endif
-#else
- #ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL{}
- #endif
-
- RGFW_UNUSED(win); RGFW_UNUSED(buffer); RGFW_UNUSED(area);
-#endif
-}
-
-#define RGFW_LOAD_ATOM(name) \
- static Atom name = 0; \
- if (name == 0) name = XInternAtom(_RGFW.display, #name, False);
-
-void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) {
- RGFW_setBit(&win->_flags, RGFW_windowNoBorder, !border);
-
- RGFW_GOTO_WAYLAND(0);
- #ifdef RGFW_X11
- RGFW_LOAD_ATOM(_MOTIF_WM_HINTS);
-
- struct __x11WindowHints {
- unsigned long flags, functions, decorations, status;
- long input_mode;
- } hints;
- hints.flags = 2;
- hints.decorations = border;
-
- XChangeProperty(win->src.display, win->src.window, _MOTIF_WM_HINTS, _MOTIF_WM_HINTS, 32,
- PropModeReplace, (u8*)&hints, 5
- );
-
- if (RGFW_window_isHidden(win) == 0) {
- RGFW_window_hide(win);
- RGFW_window_show(win);
- }
-
- #endif
- #ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
- RGFW_UNUSED(win); RGFW_UNUSED(border);
- #endif
-}
-
-void RGFW_releaseCursor(RGFW_window* win) {
-RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- XUngrabPointer(win->src.display, CurrentTime);
-
- /* disable raw input */
- unsigned char mask[] = { 0 };
- XIEventMask em;
- em.deviceid = XIAllMasterDevices;
- em.mask_len = sizeof(mask);
- em.mask = mask;
-
- XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1);
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
- RGFW_UNUSED(win);
-#endif
-}
-
-void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) {
-RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- /* enable raw input */
- unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 };
- XISetMask(mask, XI_RawMotion);
-
- XIEventMask em;
- em.deviceid = XIAllMasterDevices;
- em.mask_len = sizeof(mask);
- em.mask = mask;
-
- XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1);
-
- XGrabPointer(win->src.display, win->src.window, True, PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
- RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (i32)(r.w / 2), win->r.y + (i32)(r.h / 2)));
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
- RGFW_UNUSED(win); RGFW_UNUSED(r);
-#endif
-}
-
-#define RGFW_LOAD_LIBRARY(x, lib) if (x == NULL) x = dlopen(lib, RTLD_LAZY | RTLD_LOCAL)
-#define RGFW_PROC_DEF(proc, name) if (name##SRC == NULL && proc != NULL) { \
- void* ptr = dlsym(proc, #name); \
- if (ptr != NULL) memcpy(&name##SRC, &ptr, sizeof(PFN_##name)); \
-}
-
-#ifdef RGFW_X11
-void RGFW_window_getVisual(RGFW_window* win) {
-#if defined(RGFW_OPENGL) && !defined(RGFW_EGL)
- i32* visual_attribs = RGFW_initFormatAttribs();
- i32 fbcount;
- GLXFBConfig* fbc = glXChooseFBConfig(win->src.display, DefaultScreen(win->src.display), visual_attribs, &fbcount);
-
- i32 best_fbc = -1;
- i32 best_depth = 0;
- i32 best_samples = 0;
-
- if (fbcount == 0) {
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to find any valid GLX visual configs");
- return;
- }
-
- i32 i;
- for (i = 0; i < fbcount; i++) {
- XVisualInfo* vi = glXGetVisualFromFBConfig(win->src.display, fbc[i]);
- if (vi == NULL)
- continue;
-
- i32 samp_buf, samples;
- glXGetFBConfigAttrib(win->src.display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
- glXGetFBConfigAttrib(win->src.display, fbc[i], GLX_SAMPLES, &samples);
-
- if (best_fbc == -1) best_fbc = i;
- if ((!(win->_flags & RGFW_windowTransparent) || vi->depth == 32) && best_depth == 0) {
- best_fbc = i;
- best_depth = vi->depth;
- }
- if ((!(win->_flags & RGFW_windowTransparent) || vi->depth == 32) && samples <= RGFW_GL_HINTS[RGFW_glSamples] && samples > best_samples) {
- best_fbc = i;
- best_depth = vi->depth;
- best_samples = samples;
- }
- XFree(vi);
- }
-
- if (best_fbc == -1) {
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to get a valid GLX visual");
- return;
- }
-
- win->src.bestFbc = fbc[best_fbc];
- XVisualInfo* vi = glXGetVisualFromFBConfig(win->src.display, win->src.bestFbc);
- if (vi->depth != 32 && (win->_flags & RGFW_windowTransparent))
- RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, RGFW_DEBUG_CTX(win, 0), "Failed to to find a matching visual with a 32-bit depth");
-
- if (best_samples < RGFW_GL_HINTS[RGFW_glSamples])
- RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, RGFW_DEBUG_CTX(win, 0), "Failed to load matching sampiling");
-
- int configCaveat;
- if (glXGetFBConfigAttrib(win->src.display, win->src.bestFbc, GLX_CONFIG_CAVEAT, &configCaveat) == Success &&
- configCaveat == GLX_SLOW_CONFIG) {
- win->_flags |= RGFW_windowOpenglSoftware;
- }
-
- XFree(fbc);
- win->src.visual = *vi;
- XFree(vi);
-#else
- win->src.visual.visual = DefaultVisual(win->src.display, DefaultScreen(win->src.display));
- win->src.visual.depth = DefaultDepth(win->src.display, DefaultScreen(win->src.display));
- if (win->_flags & RGFW_windowTransparent) {
- XMatchVisualInfo(win->src.display, DefaultScreen(win->src.display), 32, TrueColor, &win->src.visual); /*!< for RGBA backgrounds */
- if (win->src.visual.depth != 32)
- RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, RGFW_DEBUG_CTX(win, 0), "Failed to load a 32-bit depth");
- }
-#endif
-}
-#endif
-#ifndef RGFW_EGL
-void RGFW_window_initOpenGL(RGFW_window* win) {
-#ifdef RGFW_OPENGL
- i32 context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 };
- context_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB;
- if (RGFW_GL_HINTS[RGFW_glProfile] == RGFW_glCore)
- context_attribs[1] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
- else
- context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
-
- if (RGFW_GL_HINTS[RGFW_glMinor] || RGFW_GL_HINTS[RGFW_glMajor]) {
- context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB;
- context_attribs[3] = RGFW_GL_HINTS[RGFW_glMajor];
- context_attribs[4] = GLX_CONTEXT_MINOR_VERSION_ARB;
- context_attribs[5] = RGFW_GL_HINTS[RGFW_glMinor];
- }
-
- glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
- glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
- glXGetProcAddressARB((GLubyte*) "glXCreateContextAttribsARB");
-
- GLXContext ctx = NULL;
- if (_RGFW.root != NULL && _RGFW.root != win) {
- ctx = _RGFW.root->src.ctx;
- RGFW_window_makeCurrent_OpenGL(_RGFW.root);
- }
-
- if (glXCreateContextAttribsARB == NULL) {
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "failed to load proc address 'glXCreateContextAttribsARB', loading a generic opengl context");
- win->src.ctx = glXCreateContext(win->src.display, &win->src.visual, ctx, True);
- }
- else {
- win->src.ctx = glXCreateContextAttribsARB(win->src.display, win->src.bestFbc, ctx, True, context_attribs);
- XSync(win->src.display, False);
- if (win->src.ctx == NULL) {
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "failed to create an opengl context with AttribsARB, loading a generic opengl context");
- win->src.ctx = glXCreateContext(win->src.display, &win->src.visual, ctx, True);
- }
- }
-
- glXMakeCurrent(win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx);
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context initalized");
-#else
- RGFW_UNUSED(win);
-#endif
-}
-
-void RGFW_window_freeOpenGL(RGFW_window* win) {
-#ifdef RGFW_OPENGL
- if (win->src.ctx == NULL) return;
- glXDestroyContext(win->src.display, win->src.ctx);
- win->src.ctx = NULL;
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context freed");
-#else
-RGFW_UNUSED(win);
-#endif
-}
-#endif
-
-
-i32 RGFW_init(void) {
- RGFW_GOTO_WAYLAND(1);
-#if defined(RGFW_C89) || defined(__cplusplus)
- if (_RGFW_init) return 0;
- _RGFW_init = RGFW_TRUE;
- _RGFW.root = NULL; _RGFW.current = NULL; _RGFW.windowCount = -1; _RGFW.eventLen = 0; _RGFW.eventIndex = 0;
-#endif
-
-#ifdef RGFW_X11
- if (_RGFW.windowCount != -1) return 0;
- #ifdef RGFW_USE_XDL
- XDL_init();
- #endif
-
- #if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD)
- #if defined(__CYGWIN__)
- RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor-1.so");
- #elif defined(__OpenBSD__) || defined(__NetBSD__)
- RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor.so");
- #else
- RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor.so.1");
- #endif
- RGFW_PROC_DEF(X11Cursorhandle, XcursorImageCreate);
- RGFW_PROC_DEF(X11Cursorhandle, XcursorImageDestroy);
- RGFW_PROC_DEF(X11Cursorhandle, XcursorImageLoadCursor);
- #endif
-
- #if !defined(RGFW_NO_X11_XI_PRELOAD)
- #if defined(__CYGWIN__)
- RGFW_LOAD_LIBRARY(X11Xihandle, "libXi-6.so");
- #elif defined(__OpenBSD__) || defined(__NetBSD__)
- RGFW_LOAD_LIBRARY(X11Xihandle, "libXi.so");
- #else
- RGFW_LOAD_LIBRARY(X11Xihandle, "libXi.so.6");
- #endif
- RGFW_PROC_DEF(X11Xihandle, XISelectEvents);
- #endif
-
- #if !defined(RGFW_NO_X11_EXT_PRELOAD)
- #if defined(__CYGWIN__)
- RGFW_LOAD_LIBRARY(X11XEXThandle, "libXext-6.so");
- #elif defined(__OpenBSD__) || defined(__NetBSD__)
- RGFW_LOAD_LIBRARY(X11XEXThandle, "libXext.so");
- #else
- RGFW_LOAD_LIBRARY(X11XEXThandle, "libXext.so.6");
- #endif
- RGFW_PROC_DEF(X11XEXThandle, XSyncCreateCounter);
- RGFW_PROC_DEF(X11XEXThandle, XSyncIntToValue);
- RGFW_PROC_DEF(X11XEXThandle, XSyncSetCounter);
- RGFW_PROC_DEF(X11XEXThandle, XShapeCombineRegion);
- RGFW_PROC_DEF(X11XEXThandle, XShapeCombineMask);
- #endif
-
- XInitThreads(); /*!< init X11 threading */
- _RGFW.display = XOpenDisplay(0);
- XSetWindowAttributes wa;
- RGFW_MEMSET(&wa, 0, sizeof(wa));
- wa.event_mask = PropertyChangeMask;
- _RGFW.helperWindow = XCreateWindow(_RGFW.display, XDefaultRootWindow(_RGFW.display), 0, 0, 1, 1, 0, 0,
- InputOnly, DefaultVisual(_RGFW.display, DefaultScreen(_RGFW.display)), CWEventMask, &wa);
-
- _RGFW.windowCount = 0;
- u8 RGFW_blk[] = { 0, 0, 0, 0 };
- _RGFW.hiddenMouse = RGFW_loadMouse(RGFW_blk, RGFW_AREA(1, 1), 4);
- _RGFW.clipboard = NULL;
-
- XkbComponentNamesRec rec;
- XkbDescPtr desc = XkbGetMap(_RGFW.display, 0, XkbUseCoreKbd);
- XkbDescPtr evdesc;
- u8 old[sizeof(RGFW_keycodes) / sizeof(RGFW_keycodes[0])];
-
- XkbGetNames(_RGFW.display, XkbKeyNamesMask, desc);
-
- RGFW_MEMSET(&rec, 0, sizeof(rec));
- rec.keycodes = (char*)"evdev";
- evdesc = XkbGetKeyboardByName(_RGFW.display, XkbUseCoreKbd, &rec, XkbGBN_KeyNamesMask, XkbGBN_KeyNamesMask, False);
- /* memo: RGFW_keycodes[x11 keycode] = rgfw keycode */
- if(evdesc != NULL && desc != NULL){
- for(int i = 0; i < (int)sizeof(RGFW_keycodes) / (int)sizeof(RGFW_keycodes[0]); i++){
- old[i] = RGFW_keycodes[i];
- RGFW_keycodes[i] = 0;
- }
- for(int i = evdesc->min_key_code; i <= evdesc->max_key_code; i++){
- for(int j = desc->min_key_code; j <= desc->max_key_code; j++){
- if(strncmp(evdesc->names->keys[i].name, desc->names->keys[j].name, XkbKeyNameLength) == 0){
- RGFW_keycodes[j] = old[i];
- break;
- }
- }
- }
- XkbFreeKeyboard(desc, 0, True);
- XkbFreeKeyboard(evdesc, 0, True);
- }
-#endif
-#ifdef RGFW_WAYLAND
-RGFW_WAYLAND_LABEL
- _RGFW.wl_display = wl_display_connect(NULL);
-#endif
- _RGFW.windowCount = 0;
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context initialized");
- return 0;
-}
-
-
-RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) {
- RGFW_window_basic_init(win, rect, flags);
-
-#ifdef RGFW_WAYLAND
- win->src.compositor = NULL;
-#endif
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- i64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask | LeaveWindowMask | EnterWindowMask | ExposureMask; /*!< X11 events accepted */
-
- win->src.display = XOpenDisplay(NULL);
- RGFW_window_getVisual(win);
-
- /* make X window attrubutes */
- XSetWindowAttributes swa;
- RGFW_MEMSET(&swa, 0, sizeof(swa));
-
- Colormap cmap;
- swa.colormap = cmap = XCreateColormap(win->src.display,
- DefaultRootWindow(win->src.display),
- win->src.visual.visual, AllocNone);
- swa.event_mask = event_mask;
-
- /* create the window */
- win->src.window = XCreateWindow(win->src.display, DefaultRootWindow(win->src.display), win->r.x, win->r.y, (u32)win->r.w, (u32)win->r.h,
- 0, win->src.visual.depth, InputOutput, win->src.visual.visual,
- CWColormap | CWBorderPixel | CWEventMask, &swa);
-
- XFreeColors(win->src.display, cmap, NULL, 0, 0);
-
- win->src.gc = XCreateGC(win->src.display, win->src.window, 0, NULL);
-
- /* In your .desktop app, if you set the property
- StartupWMClass=RGFW that will assoicate the launcher icon
- with your application - robrohan */
- if (RGFW_className == NULL)
- RGFW_className = (char*)name;
-
- XClassHint hint;
- hint.res_class = (char*)RGFW_className;
- if (RGFW_instName == NULL) hint.res_name = (char*)name;
- else hint.res_name = (char*)RGFW_instName;
- XSetClassHint(win->src.display, win->src.window, &hint);
-
- #ifndef RGFW_NO_MONITOR
- if (flags & RGFW_windowScaleToMonitor)
- RGFW_window_scaleToMonitor(win);
- #endif
- XSelectInput(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(win->src.display, "WM_DELETE_WINDOW", False);
- RGFW_XUTF8_STRING = XInternAtom(win->src.display, "UTF8_STRING", False);
- RGFW_XCLIPBOARD = XInternAtom(win->src.display, "CLIPBOARD", False);
- }
-
- XSetWMProtocols(win->src.display, (Drawable) win->src.window, &wm_delete_window, 1);
- /* set the background */
- RGFW_window_setName(win, name);
-
- XMoveWindow(win->src.display, (Drawable) win->src.window, win->r.x, win->r.y); /*!< move the window to it's proper cords */
-
- if (flags & RGFW_windowAllowDND) { /* init drag and drop atoms and turn on drag and drop for this window */
- win->_flags |= RGFW_windowAllowDND;
-
- /* actions */
- XtextUriList = XInternAtom(win->src.display, "text/uri-list", False);
- XtextPlain = XInternAtom(win->src.display, "text/plain", False);
- XdndAware = XInternAtom(win->src.display, "XdndAware", False);
- const u8 version = 5;
-
- XChangeProperty(win->src.display, win->src.window,
- XdndAware, 4, 32,
- PropModeReplace, &version, 1); /*!< turns on drag and drop */
- }
-
-#ifdef RGFW_ADVANCED_SMOOTH_RESIZE
- RGFW_LOAD_ATOM(_NET_WM_SYNC_REQUEST_COUNTER)
- RGFW_LOAD_ATOM(_NET_WM_SYNC_REQUEST)
- Atom protcols[2] = {_NET_WM_SYNC_REQUEST, wm_delete_window};
- XSetWMProtocols(win->src.display, win->src.window, protcols, 2);
-
- XSyncValue initial_value;
- XSyncIntToValue(&initial_value, 0);
- win->src.counter = XSyncCreateCounter(win->src.display, initial_value);
-
- XChangeProperty(win->src.display, win->src.window, _NET_WM_SYNC_REQUEST_COUNTER, XA_CARDINAL, 32, PropModeReplace, (uint8_t*)&win->src.counter, 1);
-#endif
-
- if ((flags & RGFW_windowNoInitAPI) == 0) {
- RGFW_window_initOpenGL(win);
- RGFW_window_initBuffer(win);
- }
-
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new window was created");
- RGFW_window_setMouseDefault(win);
- RGFW_window_setFlags(win, flags);
-
- win->src.r = win->r;
-
- RGFW_window_show(win);
- return win; /*return newly created window */
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
- RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningWayland, RGFW_DEBUG_CTX(win, 0), "RGFW Wayland support is experimental");
-
- win->src.wl_display = _RGFW.wl_display;
- if (win->src.wl_display == NULL) {
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errWayland, RGFW_DEBUG_CTX(win, 0), "Failed to load Wayland display");
- #ifdef RGFW_X11
- RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningWayland, RGFW_DEBUG_CTX(win, 0), "Falling back to X11");
- RGFW_useWayland(0);
- return RGFW_createWindowPtr(name, rect, flags, win);
- #endif
- return NULL;
- }
-
-
- #ifdef RGFW_X11
- win->src.display = _RGFW.display;
- win->src.window = _RGFW.helperWindow;
- XMapWindow(_RGFW.display, win->src.window);
- XFlush(win->src.display);
- if (wm_delete_window == 0) {
- wm_delete_window = XInternAtom(win->src.display, "WM_DELETE_WINDOW", False);
- RGFW_XUTF8_STRING = XInternAtom(win->src.display, "UTF8_STRING", False);
- RGFW_XCLIPBOARD = XInternAtom(win->src.display, "CLIPBOARD", False);
- }
- #endif
-
- struct wl_registry *registry = wl_display_get_registry(win->src.wl_display);
- wl_registry_add_listener(registry, ®istry_listener, win);
-
- wl_display_roundtrip(win->src.wl_display);
- wl_display_dispatch(win->src.wl_display);
-
- if (win->src.compositor == NULL) {
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errWayland, RGFW_DEBUG_CTX(win, 0), "Can't find compositor.");
- return NULL;
- }
-
- if (RGFW_wl_cursor_theme == NULL) {
- RGFW_wl_cursor_theme = wl_cursor_theme_load(NULL, 24, win->src.shm);
- RGFW_cursor_surface = wl_compositor_create_surface(win->src.compositor);
-
- struct wl_cursor* cursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, "left_ptr");
- RGFW_cursor_image = cursor->images[0];
- struct wl_buffer* cursor_buffer = wl_cursor_image_get_buffer(RGFW_cursor_image);
-
- wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0);
- wl_surface_commit(RGFW_cursor_surface);
- }
-
- xdg_wm_base_add_listener(win->src.xdg_wm_base, &xdg_wm_base_listener, NULL);
-
- xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
-
- win->src.surface = wl_compositor_create_surface(win->src.compositor);
- wl_surface_set_user_data(win->src.surface, win);
-
- win->src.xdg_surface = xdg_wm_base_get_xdg_surface(win->src.xdg_wm_base, win->src.surface);
- xdg_surface_add_listener(win->src.xdg_surface, &xdg_surface_listener, NULL);
-
- xdg_wm_base_set_user_data(win->src.xdg_wm_base, win);
-
- win->src.xdg_toplevel = xdg_surface_get_toplevel(win->src.xdg_surface);
- xdg_toplevel_set_user_data(win->src.xdg_toplevel, win);
- xdg_toplevel_add_listener(win->src.xdg_toplevel, &xdg_toplevel_listener, NULL);
-
- xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->r.w, win->r.h);
-
- if (!(flags & RGFW_windowNoBorder)) {
- win->src.decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
- decoration_manager, win->src.xdg_toplevel);
- }
-
- wl_display_roundtrip(win->src.wl_display);
-
- wl_surface_commit(win->src.surface);
- RGFW_window_show(win);
-
- /* wait for the surface to be configured */
- while (wl_display_dispatch(win->src.wl_display) != -1 && !RGFW_wl_configured) { }
-
- if ((flags & RGFW_windowNoInitAPI) == 0) {
- RGFW_window_initOpenGL(win);
- RGFW_window_initBuffer(win);
- }
- struct wl_callback* callback = wl_surface_frame(win->src.surface);
- wl_callback_add_listener(callback, &wl_surface_frame_listener, win);
- wl_surface_commit(win->src.surface);
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new window was created");
-
- #ifndef RGFW_NO_MONITOR
- if (flags & RGFW_windowScaleToMonitor)
- RGFW_window_scaleToMonitor(win);
- #endif
-
- RGFW_window_setName(win, name);
- RGFW_window_setMouseDefault(win);
- RGFW_window_setFlags(win, flags);
- return win; /* return newly created window */
-#endif
-}
-
-RGFW_area RGFW_getScreenSize(void) {
- RGFW_GOTO_WAYLAND(1);
- RGFW_init();
-
- #ifdef RGFW_X11
- Screen* scrn = DefaultScreenOfDisplay(_RGFW.display);
- return RGFW_AREA(scrn->width, scrn->height);
- #endif
- #ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL return RGFW_AREA(_RGFW.root->r.w, _RGFW.root->r.h); /* TODO */
- #endif
-}
-
-RGFW_point RGFW_getGlobalMousePoint(void) {
- RGFW_init();
- RGFW_point RGFWMouse = RGFW_POINT(0, 0);
- RGFW_GOTO_WAYLAND(1);
-#ifdef RGFW_X11
- i32 x, y;
- u32 z;
- Window window1, window2;
- XQueryPointer(_RGFW.display, XDefaultRootWindow(_RGFW.display), &window1, &window2, &RGFWMouse.x, &RGFWMouse.y, &x, &y, &z);
- return RGFWMouse;
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
- return RGFWMouse;
-#endif
-}
-
-RGFWDEF void RGFW_XHandleClipboardSelection(XEvent* event);
-void RGFW_XHandleClipboardSelection(XEvent* event) { RGFW_UNUSED(event);
-#ifdef RGFW_X11
- RGFW_LOAD_ATOM(ATOM_PAIR);
- RGFW_LOAD_ATOM(MULTIPLE);
- RGFW_LOAD_ATOM(TARGETS);
- RGFW_LOAD_ATOM(SAVE_TARGETS);
-
- const XSelectionRequestEvent* request = &event->xselectionrequest;
- const Atom formats[] = { RGFW_XUTF8_STRING, XA_STRING };
- const int formatCount = sizeof(formats) / sizeof(formats[0]);
-
- if (request->target == TARGETS) {
- const Atom targets[] = { TARGETS, MULTIPLE, RGFW_XUTF8_STRING, XA_STRING };
-
- XChangeProperty(_RGFW.display, request->requestor, request->property,
- XA_ATOM, 32, PropModeReplace, (u8*) targets, sizeof(targets) / sizeof(Atom));
- } else if (request->target == MULTIPLE) {
- Atom* targets = NULL;
-
- Atom actualType = 0;
- int actualFormat = 0;
- unsigned long count = 0, bytesAfter = 0;
-
- XGetWindowProperty(_RGFW.display, request->requestor, request->property, 0, LONG_MAX,
- False, ATOM_PAIR, &actualType, &actualFormat, &count, &bytesAfter, (u8**) &targets);
-
- unsigned long i;
- for (i = 0; i < (u32)count; i += 2) {
- if (targets[i] == RGFW_XUTF8_STRING || targets[i] == XA_STRING)
- XChangeProperty(_RGFW.display, request->requestor, targets[i + 1], targets[i],
- 8, PropModeReplace, (const unsigned char *)_RGFW.clipboard, (i32)_RGFW.clipboard_len);
- else
- targets[i + 1] = None;
- }
-
- XChangeProperty(_RGFW.display,
- request->requestor, request->property, ATOM_PAIR, 32,
- PropModeReplace, (u8*) targets, (i32)count);
-
- XFlush(_RGFW.display);
- XFree(targets);
- } else if (request->target == SAVE_TARGETS)
- XChangeProperty(_RGFW.display, request->requestor, request->property, 0, 32, PropModeReplace, NULL, 0);
- else {
- int i;
- for (i = 0; i < formatCount; i++) {
- if (request->target != formats[i])
- continue;
- XChangeProperty(_RGFW.display, request->requestor, request->property, request->target,
- 8, PropModeReplace, (u8*) _RGFW.clipboard, (i32)_RGFW.clipboard_len);
- }
- }
-
- XEvent reply = { SelectionNotify };
- reply.xselection.property = request->property;
- reply.xselection.display = request->display;
- reply.xselection.requestor = request->requestor;
- reply.xselection.selection = request->selection;
- reply.xselection.target = request->target;
- reply.xselection.time = request->time;
-
- XSendEvent(_RGFW.display, request->requestor, False, 0, &reply);
-#endif
-}
-
-char* RGFW_strtok(char* str, const char* delimStr);
-char* RGFW_strtok(char* str, const char* delimStr) {
- static char* static_str = NULL;
-
- if (str != NULL)
- static_str = str;
-
- if (static_str == NULL) {
- return NULL;
- }
-
- while (*static_str != '\0') {
- RGFW_bool delim = 0;
- const char* d;
- for (d = delimStr; *d != '\0'; d++) {
- if (*static_str == *d) {
- delim = 1;
- break;
- }
- }
- if (!delim)
- break;
- static_str++;
- }
-
- if (*static_str == '\0')
- return NULL;
-
- char* token_start = static_str;
- while (*static_str != '\0') {
- int delim = 0;
- const char* d;
- for (d = delimStr; *d != '\0'; d++) {
- if (*static_str == *d) {
- delim = 1;
- break;
- }
- }
-
- if (delim) {
- *static_str = '\0';
- static_str++;
- break;
- }
- static_str++;
- }
-
- return token_start;
-}
-
-i32 RGFW_XHandleClipboardSelectionHelper(void);
-
-
-u8 RGFW_rgfwToKeyChar(u32 key) {
- u32 keycode = RGFW_rgfwToApiKey(key);
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- Window root = DefaultRootWindow(_RGFW.display);
- Window ret_root, ret_child;
- int root_x, root_y, win_x, win_y;
- unsigned int mask;
- XQueryPointer(_RGFW.display, root, &ret_root, &ret_child, &root_x, &root_y, &win_x, &win_y, &mask);
- KeySym sym = (KeySym)XkbKeycodeToKeysym(_RGFW.display, (KeyCode)keycode, 0, (KeyCode)mask & ShiftMask ? 1 : 0);
-
- if ((mask & LockMask) && sym >= XK_a && sym <= XK_z)
- sym = (mask & ShiftMask) ? sym + 32 : sym - 32;
- if ((u8)sym != (u32)sym)
- sym = 0;
-
- return (u8)sym;
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL RGFW_UNUSED(keycode);
- return (u8)key;
-#endif
-}
-
-RGFW_event* RGFW_window_checkEvent(RGFW_window* win) {
- RGFW_XHandleClipboardSelectionHelper();
-
- if (win == NULL || ((win->_flags & RGFW_windowFreeOnClose) && (win->_flags & RGFW_EVENT_QUIT))) return NULL;
- RGFW_event* ev = RGFW_window_checkEventCore(win);
- if (ev) return ev;
-
- #if defined(__linux__) && !defined(RGFW_NO_LINUX)
- if (RGFW_linux_updateGamepad(win)) return &win->event;
- #endif
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- RGFW_LOAD_ATOM(XdndTypeList);
- RGFW_LOAD_ATOM(XdndSelection);
- RGFW_LOAD_ATOM(XdndEnter);
- RGFW_LOAD_ATOM(XdndPosition);
- RGFW_LOAD_ATOM(XdndStatus);
- RGFW_LOAD_ATOM(XdndLeave);
- RGFW_LOAD_ATOM(XdndDrop);
- RGFW_LOAD_ATOM(XdndFinished);
- RGFW_LOAD_ATOM(XdndActionCopy);
- RGFW_LOAD_ATOM(_NET_WM_SYNC_REQUEST);
- RGFW_LOAD_ATOM(WM_PROTOCOLS);
- XPending(win->src.display);
-
- XEvent E; /*!< raw X11 event */
-
- /* if there is no unread qued events, get a new one */
- if ((QLength(win->src.display) || XEventsQueued(win->src.display, QueuedAlready) + XEventsQueued(win->src.display, QueuedAfterReading))
- && win->event.type != RGFW_quit
- )
- XNextEvent(win->src.display, &E);
- else {
- return NULL;
- }
-
- win->event.type = 0;
-
- /* xdnd data */
- static Window source = 0;
- static long version = 0;
- static i32 format = 0;
-
- XEvent reply = { ClientMessage };
-
- switch (E.type) {
- case KeyPress:
- case KeyRelease: {
- win->event.repeat = RGFW_FALSE;
- /* check if it's a real key release */
- if (E.type == KeyRelease && XEventsQueued(win->src.display, QueuedAfterReading)) { /* get next event if there is one */
- XEvent NE;
- XPeekEvent(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 */
- win->event.repeat = RGFW_TRUE;
- }
-
- /* set event key data */
- win->event.key = (u8)RGFW_apiKeyToRGFW(E.xkey.keycode);
- win->event.keyChar = (u8)RGFW_rgfwToKeyChar(win->event.key);
-
- RGFW_keyboard[win->event.key].prev = RGFW_keyboard[win->event.key].current;
-
- /* get keystate data */
- win->event.type = (E.type == KeyPress) ? RGFW_keyPressed : RGFW_keyReleased;
-
- XKeyboardState keystate;
- XGetKeyboardControl(win->src.display, &keystate);
-
- RGFW_keyboard[win->event.key].current = (E.type == KeyPress);
-
- XkbStateRec state;
- XkbGetState(win->src.display, XkbUseCoreKbd, &state);
- RGFW_updateKeyMods(win, (state.locked_mods & LockMask), (state.locked_mods & Mod2Mask), (state.locked_mods & Mod3Mask));
-
- RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, (E.type == KeyPress));
- break;
- }
- case ButtonPress:
- case ButtonRelease:
- if (E.xbutton.button > RGFW_mouseFinal) { /* skip this event */
- XFlush(win->src.display);
- return RGFW_window_checkEvent(win);
- }
-
- win->event.type = RGFW_mouseButtonPressed + (E.type == ButtonRelease); /* the events match */
- win->event.button = (u8)(E.xbutton.button - 1);
- switch(win->event.button) {
- case RGFW_mouseScrollUp:
- win->event.scroll = 1;
- break;
- case RGFW_mouseScrollDown:
- win->event.scroll = -1;
- break;
- default: break;
- }
-
- RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
-
- if (win->event.repeat == RGFW_FALSE)
- win->event.repeat = RGFW_isPressed(win, win->event.key);
-
- RGFW_mouseButtons[win->event.button].current = (E.type == ButtonPress);
- RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, (E.type == ButtonPress));
- break;
-
- case MotionNotify:
- win->event.point.x = E.xmotion.x;
- win->event.point.y = E.xmotion.y;
-
- win->event.vector.x = win->event.point.x - win->_lastMousePoint.x;
- win->event.vector.y = win->event.point.y - win->_lastMousePoint.y;
- win->_lastMousePoint = win->event.point;
-
- win->event.type = RGFW_mousePosChanged;
- RGFW_mousePosCallback(win, win->event.point, win->event.vector);
- break;
-
- case GenericEvent: {
- /* MotionNotify is used for mouse events if the mouse isn't held */
- if (!(win->_flags & RGFW_HOLD_MOUSE)) {
- XFreeEventData(win->src.display, &E.xcookie);
- break;
- }
-
- XGetEventData(win->src.display, &E.xcookie);
- if (E.xcookie.evtype == XI_RawMotion) {
- XIRawEvent *raw = (XIRawEvent *)E.xcookie.data;
- if (raw->valuators.mask_len == 0) {
- XFreeEventData(win->src.display, &E.xcookie);
- break;
- }
-
- double deltaX = 0.0f;
- double deltaY = 0.0f;
-
- /* check if relative motion data exists where we think it does */
- if (XIMaskIsSet(raw->valuators.mask, 0) != 0)
- deltaX += raw->raw_values[0];
- if (XIMaskIsSet(raw->valuators.mask, 1) != 0)
- deltaY += raw->raw_values[1];
-
- win->event.vector = RGFW_POINT((i32)deltaX, (i32)deltaY);
- win->event.point.x = win->_lastMousePoint.x + win->event.vector.x;
- win->event.point.y = win->_lastMousePoint.y + win->event.vector.y;
- win->_lastMousePoint = win->event.point;
-
- RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2)));
-
- win->event.type = RGFW_mousePosChanged;
- RGFW_mousePosCallback(win, win->event.point, win->event.vector);
- }
-
- XFreeEventData(win->src.display, &E.xcookie);
- break;
- }
-
- case Expose: {
- win->event.type = RGFW_windowRefresh;
- RGFW_windowRefreshCallback(win);
-
-#ifdef RGFW_ADVANCED_SMOOTH_RESIZE
- XSyncValue value;
- XSyncIntToValue(&value, (i32)win->src.counter_value);
- XSyncSetCounter(win->src.display, win->src.counter, value);
-#endif
- break;
- }
- case MapNotify: case UnmapNotify: RGFW_window_checkMode(win); break;
- case ClientMessage: {
- /* if the client closed the window */
- if (E.xclient.data.l[0] == (long)wm_delete_window) {
- win->event.type = RGFW_quit;
- RGFW_window_setShouldClose(win, RGFW_TRUE);
- RGFW_windowQuitCallback(win);
- break;
- }
-#ifdef RGFW_ADVANCED_SMOOTH_RESIZE
- if (E.xclient.message_type == WM_PROTOCOLS && (Atom)E.xclient.data.l[0] == _NET_WM_SYNC_REQUEST) {
- RGFW_windowRefreshCallback(win);
- win->src.counter_value = 0;
- win->src.counter_value |= E.xclient.data.l[2];
- win->src.counter_value |= (E.xclient.data.l[3] << 32);
-
- XSyncValue value;
- XSyncIntToValue(&value, (i32)win->src.counter_value);
- XSyncSetCounter(win->src.display, win->src.counter, value);
- break;
- }
-#endif
- if ((win->_flags & RGFW_windowAllowDND) == 0)
- break;
-
- reply.xclient.window = source;
- 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;
-
- if (E.xclient.message_type == XdndEnter) {
- if (version > 5)
- break;
-
- unsigned long count;
- Atom* formats;
- Atom real_formats[6];
- Bool list = E.xclient.data.l[1] & 1;
-
- source = (unsigned long int)E.xclient.data.l[0];
- version = E.xclient.data.l[1] >> 24;
- format = None;
- if (list) {
- Atom actualType;
- i32 actualFormat;
- unsigned long bytesAfter;
-
- XGetWindowProperty(
- win->src.display, source, XdndTypeList,
- 0, LONG_MAX, False, 4,
- &actualType, &actualFormat, &count, &bytesAfter, (u8**)&formats
- );
- } else {
- count = 0;
-
- size_t i;
- for (i = 2; i < 5; i++) {
- if (E.xclient.data.l[i] != None) {
- real_formats[count] = (unsigned long int)E.xclient.data.l[i];
- count += 1;
- }
- }
-
- formats = real_formats;
- }
-
- size_t i;
- for (i = 0; i < count; i++) {
- if (formats[i] == XtextUriList || formats[i] == XtextPlain) {
- format = (int)formats[i];
- break;
- }
- }
-
- if (list) {
- XFree(formats);
- }
-
- 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 (version > 5)
- break;
-
- XTranslateCoordinates(
- win->src.display, XDefaultRootWindow(win->src.display), win->src.window,
- xabs, yabs, &xpos, &ypos, &dummy
- );
-
- win->event.point.x = xpos;
- win->event.point.y = ypos;
-
- reply.xclient.window = source;
- reply.xclient.message_type = XdndStatus;
-
- if (format) {
- reply.xclient.data.l[1] = 1;
- if (version >= 2)
- reply.xclient.data.l[4] = (long)XdndActionCopy;
- }
-
- XSendEvent(win->src.display, source, False, NoEventMask, &reply);
- XFlush(win->src.display);
- break;
- }
- if (E.xclient.message_type != XdndDrop)
- break;
-
- if (version > 5)
- break;
-
- size_t i;
- for (i = 0; i < win->event.droppedFilesCount; i++)
- win->event.droppedFiles[i][0] = '\0';
-
- win->event.droppedFilesCount = 0;
-
-
- win->event.type = RGFW_DNDInit;
-
- if (format) {
- Time time = (version >= 1)
- ? (Time)E.xclient.data.l[2]
- : CurrentTime;
-
- XConvertSelection(
- win->src.display, XdndSelection, (Atom)format,
- XdndSelection, win->src.window, time
- );
- } else if (version >= 2) {
- XEvent new_reply = { ClientMessage };
-
- XSendEvent(win->src.display, source, False, NoEventMask, &new_reply);
- XFlush(win->src.display);
- }
-
- RGFW_dndInitCallback(win, win->event.point);
- } break;
- case SelectionRequest:
- RGFW_XHandleClipboardSelection(&E);
- XFlush(win->src.display);
- return RGFW_window_checkEvent(win);
- case SelectionNotify: {
- /* this is only for checking for xdnd drops */
- if (E.xselection.property != XdndSelection || !(win->_flags & RGFW_windowAllowDND))
- break;
- char* data;
- unsigned long result;
-
- Atom actualType;
- i32 actualFormat;
- unsigned long bytesAfter;
-
- XGetWindowProperty(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;
-
- const char* prefix = (const char*)"file://";
-
- char* line;
-
- win->event.droppedFilesCount = 0;
- win->event.type = RGFW_DND;
-
- while ((line = (char*)RGFW_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) RGFW_STRTOL(digits, NULL, 16);
- line += 2;
- } else
- path[index] = *line;
-
- index++;
- line++;
- }
- path[index] = '\0';
- RGFW_MEMCPY(win->event.droppedFiles[win->event.droppedFilesCount - 1], path, index + 1);
- }
-
- RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount);
- if (data)
- XFree(data);
-
- if (version >= 2) {
- XEvent new_reply = { ClientMessage };
- new_reply.xclient.window = source;
- new_reply.xclient.message_type = XdndFinished;
- new_reply.xclient.format = 32;
- new_reply.xclient.data.l[1] = (long int)result;
- new_reply.xclient.data.l[2] = (long int)XdndActionCopy;
- XSendEvent(win->src.display, source, False, NoEventMask, &new_reply);
- XFlush(win->src.display);
- }
- break;
- }
- case FocusIn:
- if ((win->_flags & RGFW_windowFullscreen))
- XMapRaised(win->src.display, win->src.window);
-
- win->_flags |= RGFW_windowFocus;
- win->event.type = RGFW_focusIn;
- RGFW_focusCallback(win, 1);
-
-
- if ((win->_flags & RGFW_HOLD_MOUSE)) RGFW_window_mouseHold(win, RGFW_AREA(win->r.w, win->r.h));
- break;
- case FocusOut:
- win->event.type = RGFW_focusOut;
- RGFW_focusCallback(win, 0);
- RGFW_window_focusLost(win);
- break;
- case PropertyNotify: RGFW_window_checkMode(win); break;
- case EnterNotify: {
- win->event.type = RGFW_mouseEnter;
- win->event.point.x = E.xcrossing.x;
- win->event.point.y = E.xcrossing.y;
- RGFW_mouseNotifyCallback(win, win->event.point, 1);
- break;
- }
-
- case LeaveNotify: {
- win->event.type = RGFW_mouseLeave;
- RGFW_mouseNotifyCallback(win, win->event.point, 0);
- break;
- }
-
- case ConfigureNotify: {
- /* detect resize */
- RGFW_window_checkMode(win);
- if (E.xconfigure.width != win->src.r.w || E.xconfigure.height != win->src.r.h) {
- win->event.type = RGFW_windowResized;
- win->src.r = win->r = RGFW_RECT(win->src.r.x, win->src.r.y, E.xconfigure.width, E.xconfigure.height);
- RGFW_windowResizedCallback(win, win->r);
- break;
- }
-
- /* detect move */
- if (E.xconfigure.x != win->src.r.x || E.xconfigure.y != win->src.r.y) {
- win->event.type = RGFW_windowMoved;
- win->src.r = win->r = RGFW_RECT(E.xconfigure.x, E.xconfigure.y, win->src.r.w, win->src.r.h);
- RGFW_windowMovedCallback(win, win->r);
- break;
- }
-
- break;
- }
- default:
- XFlush(win->src.display);
- return RGFW_window_checkEvent(win);
- }
- XFlush(win->src.display);
- if (win->event.type) return &win->event;
- else return NULL;
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
- if ((win->_flags & RGFW_windowHide) == 0)
- wl_display_roundtrip(win->src.wl_display);
- return NULL;
-#endif
-}
-
-void RGFW_window_move(RGFW_window* win, RGFW_point v) {
- RGFW_ASSERT(win != NULL);
- win->r.x = v.x;
- win->r.y = v.y;
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- XMoveWindow(win->src.display, win->src.window, v.x, v.y);
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
- RGFW_ASSERT(win != NULL);
-
- if (win->src.compositor) {
- struct wl_pointer *pointer = wl_seat_get_pointer(win->src.seat);
- if (!pointer) {
- return;
- }
-
- wl_display_flush(win->src.wl_display);
- }
-#endif
-}
-
-
-void RGFW_window_resize(RGFW_window* win, RGFW_area a) {
- RGFW_ASSERT(win != NULL);
- win->r.w = (i32)a.w;
- win->r.h = (i32)a.h;
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- XResizeWindow(win->src.display, win->src.window, a.w, a.h);
-
- if ((win->_flags & RGFW_windowNoResize)) {
- XSizeHints sh;
- sh.flags = (1L << 4) | (1L << 5);
- sh.min_width = sh.max_width = (i32)a.w;
- sh.min_height = sh.max_height = (i32)a.h;
-
- XSetWMSizeHints(win->src.display, (Drawable) win->src.window, &sh, XA_WM_NORMAL_HINTS);
- }
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
- if (win->src.compositor) {
- xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->r.w, win->r.h);
- #ifdef RGFW_OPENGL
- wl_egl_window_resize(win->src.eglWindow, (i32)a.w, (i32)a.h, 0, 0);
- #endif
- }
-#endif
-}
-
-void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a) {
- RGFW_ASSERT(win != NULL);
- RGFW_GOTO_WAYLAND(0);
-
- if (a.w == 0 && a.h == 0)
- return;
-#ifdef RGFW_X11
- XSizeHints hints;
- long flags;
-
- XGetWMNormalHints(win->src.display, win->src.window, &hints, &flags);
-
- hints.flags |= PAspect;
-
- hints.min_aspect.x = hints.max_aspect.x = (i32)a.w;
- hints.min_aspect.y = hints.max_aspect.y = (i32)a.h;
-
- XSetWMNormalHints(win->src.display, win->src.window, &hints);
- return;
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
-#endif
-}
-
-void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) {
- RGFW_ASSERT(win != NULL);
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- long flags;
- XSizeHints hints;
- RGFW_MEMSET(&hints, 0, sizeof(XSizeHints));
-
- XGetWMNormalHints(win->src.display, win->src.window, &hints, &flags);
-
- hints.flags |= PMinSize;
-
- hints.min_width = (i32)a.w;
- hints.min_height = (i32)a.h;
-
- XSetWMNormalHints(win->src.display, win->src.window, &hints);
- return;
-#endif
-#ifdef RGFW_WAYLAND
-RGFW_WAYLAND_LABEL RGFW_UNUSED(a);
-#endif
-}
-
-void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) {
- RGFW_ASSERT(win != NULL);
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- long flags;
- XSizeHints hints;
- RGFW_MEMSET(&hints, 0, sizeof(XSizeHints));
-
- XGetWMNormalHints(win->src.display, win->src.window, &hints, &flags);
-
- hints.flags |= PMaxSize;
-
- hints.max_width = (i32)a.w;
- hints.max_height = (i32)a.h;
-
- XSetWMNormalHints(win->src.display, win->src.window, &hints);
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL RGFW_UNUSED(a);
-#endif
-}
-
-#ifdef RGFW_X11
-void RGFW_toggleXMaximized(RGFW_window* win, RGFW_bool maximized);
-void RGFW_toggleXMaximized(RGFW_window* win, RGFW_bool maximized) {
- RGFW_ASSERT(win != NULL);
- RGFW_LOAD_ATOM(_NET_WM_STATE);
- RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
- RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
-
- XEvent xev = {0};
- xev.type = ClientMessage;
- xev.xclient.window = win->src.window;
- xev.xclient.message_type = _NET_WM_STATE;
- xev.xclient.format = 32;
- xev.xclient.data.l[0] = maximized;
- xev.xclient.data.l[1] = (long int)_NET_WM_STATE_MAXIMIZED_HORZ;
- xev.xclient.data.l[2] = (long int)_NET_WM_STATE_MAXIMIZED_VERT;
- xev.xclient.data.l[3] = 0;
- xev.xclient.data.l[4] = 0;
-
- XSendEvent(win->src.display, DefaultRootWindow(win->src.display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
-}
-#endif
-
-void RGFW_window_maximize(RGFW_window* win) {
- win->_oldRect = win->r;
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- RGFW_toggleXMaximized(win, 1);
- return;
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
- return;
-#endif
-}
-
-void RGFW_window_focus(RGFW_window* win) {
- RGFW_ASSERT(win);
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- XWindowAttributes attr;
- XGetWindowAttributes(win->src.display, win->src.window, &attr);
- if (attr.map_state != IsViewable) return;
-
- XSetInputFocus(win->src.display, win->src.window, RevertToPointerRoot, CurrentTime);
- XFlush(win->src.display);
-#endif
-#ifdef RGFW_WAYLAND
-RGFW_WAYLAND_LABEL;
-#endif
-}
-
-void RGFW_window_raise(RGFW_window* win) {
- RGFW_ASSERT(win);
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- XRaiseWindow(win->src.display, win->src.window);
- XMapRaised(win->src.display, win->src.window);
-#endif
-#ifdef RGFW_WAYLAND
-RGFW_WAYLAND_LABEL;
-#endif
-}
-
-#ifdef RGFW_X11
-void RGFW_window_setXAtom(RGFW_window* win, Atom netAtom, RGFW_bool fullscreen);
-void RGFW_window_setXAtom(RGFW_window* win, Atom netAtom, RGFW_bool fullscreen) {
- RGFW_ASSERT(win != NULL);
- RGFW_LOAD_ATOM(_NET_WM_STATE);
-
- XEvent xev = {0};
- xev.xclient.type = ClientMessage;
- xev.xclient.serial = 0;
- xev.xclient.send_event = True;
- xev.xclient.message_type = _NET_WM_STATE;
- xev.xclient.window = win->src.window;
- xev.xclient.format = 32;
- xev.xclient.data.l[0] = fullscreen;
- xev.xclient.data.l[1] = (long int)netAtom;
- xev.xclient.data.l[2] = 0;
-
- XSendEvent(win->src.display, DefaultRootWindow(win->src.display), False, SubstructureNotifyMask | SubstructureRedirectMask, &xev);
-}
-#endif
-
-void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) {
- RGFW_ASSERT(win != NULL);
- RGFW_GOTO_WAYLAND(0);
- if (fullscreen) {
- win->_flags |= RGFW_windowFullscreen;
- win->_oldRect = win->r;
- }
- else win->_flags &= ~(u32)RGFW_windowFullscreen;
-#ifdef RGFW_X11
- RGFW_LOAD_ATOM(_NET_WM_STATE_FULLSCREEN);
-
- RGFW_window_setXAtom(win, _NET_WM_STATE_FULLSCREEN, fullscreen);
-
- XRaiseWindow(win->src.display, win->src.window);
- XMapRaised(win->src.display, win->src.window);
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL;
-#endif
-}
-
-void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) {
- RGFW_ASSERT(win != NULL);
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- RGFW_LOAD_ATOM(_NET_WM_STATE_ABOVE);
- RGFW_window_setXAtom(win, _NET_WM_STATE_ABOVE, floating);
-#endif
-#ifdef RGFW_WAYLAND
-RGFW_WAYLAND_LABEL RGFW_UNUSED(floating);
-#endif
-}
-
-void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) {
- RGFW_ASSERT(win != NULL);
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- const u32 value = (u32) (0xffffffffu * (double) opacity);
- RGFW_LOAD_ATOM(NET_WM_WINDOW_OPACITY);
- XChangeProperty(win->src.display, win->src.window,
- NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &value, 1);
-#endif
-#ifdef RGFW_WAYLAND
-RGFW_WAYLAND_LABEL RGFW_UNUSED(opacity);
-#endif
-}
-
-void RGFW_window_minimize(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
- RGFW_GOTO_WAYLAND(0);
- if (RGFW_window_isMaximized(win)) return;
-
- win->_oldRect = win->r;
-#ifdef RGFW_X11
- XIconifyWindow(win->src.display, win->src.window, DefaultScreen(win->src.display));
- XFlush(win->src.display);
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL;
-#endif
-}
-
-void RGFW_window_restore(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- RGFW_toggleXMaximized(win, 0);
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
-#endif
- win->r = win->_oldRect;
- RGFW_window_move(win, RGFW_POINT(win->r.x, win->r.y));
- RGFW_window_resize(win, RGFW_AREA(win->r.w, win->r.h));
-
- RGFW_window_show(win);
-#ifdef RGFW_X11
- XFlush(win->src.display);
-#endif
-}
-
-RGFW_bool RGFW_window_isFloating(RGFW_window* win) {
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- RGFW_LOAD_ATOM(_NET_WM_STATE);
- RGFW_LOAD_ATOM(_NET_WM_STATE_ABOVE);
-
- Atom actual_type;
- int actual_format;
- unsigned long nitems, bytes_after;
- Atom* prop_return = NULL;
-
- int status = XGetWindowProperty(win->src.display, win->src.window, _NET_WM_STATE, 0, (~0L), False, XA_ATOM,
- &actual_type, &actual_format, &nitems, &bytes_after,
- (unsigned char **)&prop_return);
-
- if (status != Success || actual_type != XA_ATOM)
- return RGFW_FALSE;
-
- unsigned long i;
- for (i = 0; i < nitems; i++)
- if (prop_return[i] == _NET_WM_STATE_ABOVE) return RGFW_TRUE;
-
- if (prop_return)
- XFree(prop_return);
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL RGFW_UNUSED(win);
-#endif
- return RGFW_FALSE;
-}
-
-void RGFW_window_setName(RGFW_window* win, const char* name) {
- RGFW_ASSERT(win != NULL);
- RGFW_GOTO_WAYLAND(0);
- #ifdef RGFW_X11
- XStoreName(win->src.display, win->src.window, name);
-
- RGFW_LOAD_ATOM(_NET_WM_NAME);
-
- char buf[256];
- RGFW_MEMSET(buf, 0, sizeof(buf));
- RGFW_STRNCPY(buf, name, sizeof(buf) - 1);
-
- XChangeProperty(
- win->src.display, win->src.window, _NET_WM_NAME, RGFW_XUTF8_STRING,
- 8, PropModeReplace, (u8*)buf, sizeof(buf)
- );
- #endif
- #ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
- if (win->src.compositor)
- xdg_toplevel_set_title(win->src.xdg_toplevel, name);
- #endif
-}
-
-#ifndef RGFW_NO_PASSTHROUGH
-void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) {
- RGFW_ASSERT(win != NULL);
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- if (passthrough) {
- Region region = XCreateRegion();
- XShapeCombineRegion(win->src.display, win->src.window, ShapeInput, 0, 0, region, ShapeSet);
- XDestroyRegion(region);
-
- return;
- }
-
- XShapeCombineMask(win->src.display, win->src.window, ShapeInput, 0, 0, None, ShapeSet);
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL RGFW_UNUSED(passthrough);
-#endif
-}
-#endif /* RGFW_NO_PASSTHROUGH */
-
-RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* icon, RGFW_area a, i32 channels, u8 type) {
- RGFW_ASSERT(win != NULL);
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- RGFW_LOAD_ATOM(_NET_WM_ICON);
- if (icon == NULL || (channels != 3 && channels != 4)) {
- RGFW_bool res = (RGFW_bool)XChangeProperty(
- win->src.display, win->src.window, _NET_WM_ICON, XA_CARDINAL, 32,
- PropModeReplace, (u8*)NULL, 0
- );
- return res;
- }
-
- i32 count = (i32)(2 + (a.w * a.h));
-
- unsigned long* data = (unsigned long*) RGFW_ALLOC((u32)count * sizeof(unsigned long));
- RGFW_ASSERT(data != NULL);
-
- data[0] = (unsigned long)a.w;
- data[1] = (unsigned long)a.h;
-
- unsigned long* target = &data[2];
- u32 x, y;
-
- for (x = 0; x < a.w; x++) {
- for (y = 0; y < a.h; y++) {
- size_t i = y * a.w + x;
- u32 alpha = (channels == 4) ? icon[i * 4 + 3] : 0xFF;
-
- target[i] = (unsigned long)((icon[i * 4 + 0]) << 16) |
- (unsigned long)((icon[i * 4 + 1]) << 8) |
- (unsigned long)((icon[i * 4 + 2]) << 0) |
- (unsigned long)(alpha << 24);
- }
- }
-
- RGFW_bool res = RGFW_TRUE;
- if (type & RGFW_iconTaskbar) {
- res = (RGFW_bool)XChangeProperty(
- win->src.display, win->src.window, _NET_WM_ICON, XA_CARDINAL, 32,
- PropModeReplace, (u8*)data, count
- );
- }
-
- if (type & RGFW_iconWindow) {
- XWMHints wm_hints;
- wm_hints.flags = IconPixmapHint;
-
- i32 depth = DefaultDepth(win->src.display, DefaultScreen(win->src.display));
- XImage *image = XCreateImage(win->src.display, DefaultVisual(win->src.display, DefaultScreen(win->src.display)),
- (u32)depth, ZPixmap, 0, (char *)target, a.w, a.h, 32, 0);
-
- wm_hints.icon_pixmap = XCreatePixmap(win->src.display, win->src.window, a.w, a.h, (u32)depth);
- XPutImage(win->src.display, wm_hints.icon_pixmap, DefaultGC(win->src.display, DefaultScreen(win->src.display)), image, 0, 0, 0, 0, a.w, a.h);
- image->data = NULL;
- XDestroyImage(image);
-
- XSetWMHints(win->src.display, win->src.window, &wm_hints);
- }
-
- RGFW_FREE(data);
- XFlush(win->src.display);
- return RGFW_BOOL(res);
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL RGFW_UNUSED(icon); RGFW_UNUSED(a); RGFW_UNUSED(channels); RGFW_UNUSED(type);
- return RGFW_FALSE;
-#endif
-}
-
-RGFW_mouse* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels) {
- RGFW_ASSERT(icon);
- RGFW_ASSERT(channels == 3 || channels == 4);
- RGFW_GOTO_WAYLAND(0);
-
-#ifdef RGFW_X11
-#ifndef RGFW_NO_X11_CURSOR
- RGFW_init();
- XcursorImage* native = XcursorImageCreate((i32)a.w, (i32)a.h);
- native->xhot = 0;
- native->yhot = 0;
-
- XcursorPixel* target = native->pixels;
- size_t x, y;
- for (x = 0; x < a.w; x++) {
- for (y = 0; y < a.h; y++) {
- size_t i = y * a.w + x;
- u32 alpha = (channels == 4) ? icon[i * 4 + 3] : 0xFF;
-
- target[i] = (u32)((icon[i * 4 + 0]) << 16)
- | (u32)((icon[i * 4 + 1]) << 8)
- | (u32)((icon[i * 4 + 2]) << 0)
- | (u32)(alpha << 24);
- }
- }
-
- Cursor cursor = XcursorImageLoadCursor(_RGFW.display, native);
- XcursorImageDestroy(native);
-
- return (void*)cursor;
-#else
- RGFW_UNUSED(image); RGFW_UNUSED(a.w); RGFW_UNUSED(channels);
- return NULL;
-#endif
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
- RGFW_UNUSED(icon); RGFW_UNUSED(a); RGFW_UNUSED(channels);
- return NULL; /* TODO */
-#endif
-}
-
-void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) {
-RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- RGFW_ASSERT(win && mouse);
- XDefineCursor(win->src.display, win->src.window, (Cursor)mouse);
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
- RGFW_UNUSED(win); RGFW_UNUSED(mouse);
-#endif
-}
-
-void RGFW_freeMouse(RGFW_mouse* mouse) {
-RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- RGFW_ASSERT(mouse);
- XFreeCursor(_RGFW.display, (Cursor)mouse);
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
- RGFW_UNUSED(mouse);
-#endif
-}
-
-void RGFW_window_moveMouse(RGFW_window* win, RGFW_point p) {
-RGFW_GOTO_WAYLAND(1);
-#ifdef RGFW_X11
- RGFW_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);
-
- win->_lastMousePoint = RGFW_POINT(p.x - win->r.x, p.y - win->r.y);
- if (event.xbutton.x == p.x && event.xbutton.y == p.y)
- return;
-
- XWarpPointer(win->src.display, None, win->src.window, 0, 0, 0, 0, (int) p.x - win->r.x, (int) p.y - win->r.y);
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
- RGFW_UNUSED(win); RGFW_UNUSED(p);
-#endif
-}
-
-RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) {
- return RGFW_window_setMouseStandard(win, RGFW_mouseArrow);
-}
-
-RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) {
- RGFW_ASSERT(win != NULL);
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- static const u8 mouseIconSrc[16] = { XC_arrow, XC_left_ptr, XC_xterm, XC_crosshair, XC_hand2, XC_sb_h_double_arrow, XC_sb_v_double_arrow, XC_bottom_left_corner, XC_bottom_right_corner, XC_fleur, XC_X_cursor};
-
- if (mouse > (sizeof(mouseIconSrc) / sizeof(u8)))
- return RGFW_FALSE;
-
- mouse = mouseIconSrc[mouse];
-
- Cursor cursor = XCreateFontCursor(win->src.display, mouse);
- XDefineCursor(win->src.display, win->src.window, (Cursor) cursor);
-
- XFreeCursor(win->src.display, (Cursor) cursor);
- return RGFW_TRUE;
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL { }
- static const char* iconStrings[16] = { "left_ptr", "left_ptr", "text", "cross", "pointer", "e-resize", "n-resize", "nw-resize", "ne-resize", "all-resize", "not-allowed" };
-
- struct wl_cursor* wlcursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, iconStrings[mouse]);
- RGFW_cursor_image = wlcursor->images[0];
- struct wl_buffer* cursor_buffer = wl_cursor_image_get_buffer(RGFW_cursor_image);
-
- wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0);
- wl_surface_commit(RGFW_cursor_surface);
- return RGFW_TRUE;
-
-#endif
-}
-
-void RGFW_window_hide(RGFW_window* win) {
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- XUnmapWindow(win->src.display, win->src.window);
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
- wl_surface_attach(win->src.surface, NULL, 0, 0);
- wl_surface_commit(win->src.surface);
- win->_flags |= RGFW_windowHide;
-#endif
-}
-
-void RGFW_window_show(RGFW_window* win) {
- win->_flags &= ~(u32)RGFW_windowHide;
- if (win->_flags & RGFW_windowFocusOnShow) RGFW_window_focus(win);
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- XMapWindow(win->src.display, win->src.window);
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
- /* wl_surface_attach(win->src.surface, win->rc., 0, 0); */
- wl_surface_commit(win->src.surface);
-#endif
-}
-
-RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) {
- RGFW_GOTO_WAYLAND(1);
-#ifdef RGFW_X11
- RGFW_init();
- if (XGetSelectionOwner(_RGFW.display, RGFW_XCLIPBOARD) == _RGFW.helperWindow) {
- if (str != NULL)
- RGFW_STRNCPY(str, _RGFW.clipboard, _RGFW.clipboard_len - 1);
- _RGFW.clipboard[_RGFW.clipboard_len - 1] = '\0';
- return (RGFW_ssize_t)_RGFW.clipboard_len - 1;
- }
-
- XEvent event;
- int format;
- unsigned long N, sizeN;
- char* data;
- Atom target;
-
- RGFW_LOAD_ATOM(XSEL_DATA);
-
- XConvertSelection(_RGFW.display, RGFW_XCLIPBOARD, RGFW_XUTF8_STRING, XSEL_DATA, _RGFW.helperWindow, CurrentTime);
- XSync(_RGFW.display, 0);
- while (1) {
- XNextEvent(_RGFW.display, &event);
- if (event.type != SelectionNotify) continue;
-
- if (event.xselection.selection != RGFW_XCLIPBOARD || event.xselection.property == 0)
- return -1;
- break;
- }
-
- XGetWindowProperty(event.xselection.display, event.xselection.requestor,
- event.xselection.property, 0L, (~0L), 0, AnyPropertyType, &target,
- &format, &sizeN, &N, (u8**) &data);
-
- RGFW_ssize_t size;
- if (sizeN > strCapacity && str != NULL)
- size = -1;
-
- if ((target == RGFW_XUTF8_STRING || target == XA_STRING) && str != NULL) {
- RGFW_MEMCPY(str, data, sizeN);
- str[sizeN] = '\0';
- XFree(data);
- } else if (str != NULL) size = -1;
-
- XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property);
- size = (RGFW_ssize_t)sizeN;
-
- return size;
- #endif
- #if defined(RGFW_WAYLAND)
- RGFW_WAYLAND_LABEL RGFW_UNUSED(str); RGFW_UNUSED(strCapacity);
- return 0;
- #endif
-}
-
-i32 RGFW_XHandleClipboardSelectionHelper(void) {
-#ifdef RGFW_X11
- RGFW_LOAD_ATOM(SAVE_TARGETS);
-
- XEvent event;
- XPending(_RGFW.display);
-
- if (QLength(_RGFW.display) || XEventsQueued(_RGFW.display, QueuedAlready) + XEventsQueued(_RGFW.display, QueuedAfterReading))
- XNextEvent(_RGFW.display, &event);
- else
- return 0;
-
- switch (event.type) {
- case SelectionRequest:
- RGFW_XHandleClipboardSelection(&event);
- return 0;
- case SelectionNotify:
- if (event.xselection.target == SAVE_TARGETS)
- return 0;
- break;
- default: break;
- }
-
- return 0;
-#else
- return 1;
-#endif
-}
-
-void RGFW_writeClipboard(const char* text, u32 textLen) {
- RGFW_GOTO_WAYLAND(1);
- #ifdef RGFW_X11
- RGFW_LOAD_ATOM(SAVE_TARGETS);
- RGFW_init();
-
- /* request ownership of the clipboard section and request to convert it, this means its our job to convert it */
- XSetSelectionOwner(_RGFW.display, RGFW_XCLIPBOARD, _RGFW.helperWindow, CurrentTime);
- if (XGetSelectionOwner(_RGFW.display, RGFW_XCLIPBOARD) != _RGFW.helperWindow) {
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errClipboard, RGFW_DEBUG_CTX(_RGFW.root, 0), "X11 failed to become owner of clipboard selection");
- return;
- }
-
- if (_RGFW.clipboard)
- RGFW_FREE(_RGFW.clipboard);
-
- _RGFW.clipboard = (char*)RGFW_ALLOC(textLen);
- RGFW_ASSERT(_RGFW.clipboard != NULL);
-
- RGFW_STRNCPY(_RGFW.clipboard, text, textLen - 1);
- _RGFW.clipboard[textLen - 1] = '\0';
- _RGFW.clipboard_len = textLen;
- #endif
- #ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
- RGFW_UNUSED(text); RGFW_UNUSED(textLen);
- #endif
-}
-
-RGFW_bool RGFW_window_isHidden(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
-
- XWindowAttributes windowAttributes;
- XGetWindowAttributes(win->src.display, win->src.window, &windowAttributes);
-
- return (windowAttributes.map_state == IsUnmapped && !RGFW_window_isMinimized(win));
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
- return RGFW_FALSE;
-#endif
-}
-
-RGFW_bool RGFW_window_isMinimized(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- RGFW_LOAD_ATOM(WM_STATE);
-
- Atom actual_type;
- i32 actual_format;
- unsigned long nitems, bytes_after;
- unsigned char* prop_data;
-
- i32 status = XGetWindowProperty(win->src.display, win->src.window, WM_STATE, 0, 2, False,
- AnyPropertyType, &actual_type, &actual_format,
- &nitems, &bytes_after, &prop_data);
-
- if (status == Success && nitems >= 1 && prop_data == (unsigned char*)IconicState) {
- XFree(prop_data);
- return RGFW_TRUE;
- }
-
- if (prop_data != NULL)
- XFree(prop_data);
-
- XWindowAttributes windowAttributes;
- XGetWindowAttributes(win->src.display, win->src.window, &windowAttributes);
- return windowAttributes.map_state != IsViewable;
-#endif
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
- return RGFW_FALSE;
-#endif
-}
-
-RGFW_bool RGFW_window_isMaximized(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
- RGFW_GOTO_WAYLAND(0);
-#ifdef RGFW_X11
- RGFW_LOAD_ATOM(_NET_WM_STATE);
- RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
- RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
-
- Atom actual_type;
- i32 actual_format;
- unsigned long nitems, bytes_after;
- unsigned char* prop_data;
-
- i32 status = XGetWindowProperty(win->src.display, win->src.window, _NET_WM_STATE, 0, 1024, False,
- XA_ATOM, &actual_type, &actual_format,
- &nitems, &bytes_after, &prop_data);
-
- if (status != Success) {
- if (prop_data != NULL)
- XFree(prop_data);
-
- return RGFW_FALSE;
- }
-
- u64 i;
- for (i = 0; i < nitems; ++i) {
- if (prop_data[i] == _NET_WM_STATE_MAXIMIZED_VERT ||
- prop_data[i] == _NET_WM_STATE_MAXIMIZED_HORZ) {
- XFree(prop_data);
- return RGFW_TRUE;
- }
- }
-
- if (prop_data != NULL)
- XFree(prop_data);
-#endif
-#ifdef RGFW_WAYLAND
-RGFW_WAYLAND_LABEL;
-#endif
- return RGFW_FALSE;
-}
-
-#ifndef RGFW_NO_DPI
-u32 RGFW_XCalculateRefreshRate(XRRModeInfo mi);
-u32 RGFW_XCalculateRefreshRate(XRRModeInfo mi) {
- if (mi.hTotal == 0 || mi.vTotal == 0) return 0;
- return (u32) RGFW_ROUND((double) mi.dotClock / ((double) mi.hTotal * (double) mi.vTotal));
-}
-#endif
-
-
-#ifdef RGFW_X11
-static float XGetSystemContentDPI(Display* display, i32 screen) {
- float dpi = 96.0f;
-
- #ifndef RGFW_NO_DPI
- RGFW_UNUSED(screen);
- char* rms = XResourceManagerString(display);
- XrmDatabase db = NULL;
- if (rms) db = XrmGetStringDatabase(rms);
-
- if (rms && db) {
- XrmValue value;
- char* type = NULL;
-
- if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value) && type && RGFW_STRNCMP(type, "String", 7) == 0)
- dpi = (float)RGFW_ATOF(value.addr);
- XrmDestroyDatabase(db);
- }
- #else
- dpi = RGFW_ROUND(DisplayWidth(display, screen) / (DisplayWidthMM(display, screen) / 25.4));
- #endif
-
- return dpi;
-}
-#endif
-
-RGFW_monitor RGFW_XCreateMonitor(i32 screen);
-RGFW_monitor RGFW_XCreateMonitor(i32 screen) {
- RGFW_monitor monitor;
- RGFW_init();
-
- RGFW_GOTO_WAYLAND(1);
-#ifdef RGFW_X11
- Display* display = _RGFW.display;
-
- if (screen == -1) screen = DefaultScreen(display);
-
- Screen* scrn = DefaultScreenOfDisplay(display);
- RGFW_area size = RGFW_AREA(scrn->width, scrn->height);
-
- monitor.x = 0;
- monitor.y = 0;
- monitor.mode.area = RGFW_AREA(size.w, size.h);
- monitor.physW = (float)DisplayWidthMM(display, screen) / 25.4f;
- monitor.physH = (float)DisplayHeightMM(display, screen) / 25.4f;
-
- RGFW_splitBPP((u32)DefaultDepth(display, DefaultScreen(display)), &monitor.mode);
-
- char* name = XDisplayName((const char*)display);
- RGFW_STRNCPY(monitor.name, name, sizeof(monitor.name) - 1);
- monitor.name[sizeof(monitor.name) - 1] = '\0';
-
- float dpi = XGetSystemContentDPI(display, screen);
- monitor.pixelRatio = dpi >= 192.0f ? 2 : 1;
- monitor.scaleX = (float) (dpi) / 96.0f;
- monitor.scaleY = (float) (dpi) / 96.0f;
-
- #ifndef RGFW_NO_DPI
- XRRScreenResources* sr = XRRGetScreenResourcesCurrent(display, RootWindow(display, screen));
- monitor.mode.refreshRate = RGFW_XCalculateRefreshRate(sr->modes[screen]);
-
- XRRCrtcInfo* ci = NULL;
- int crtc = screen;
-
- if (sr->ncrtc > crtc) {
- ci = XRRGetCrtcInfo(display, sr, sr->crtcs[crtc]);
- }
- #endif
-
- #ifndef RGFW_NO_DPI
- XRROutputInfo* info = XRRGetOutputInfo (display, sr, sr->outputs[screen]);
-
- if (info == NULL || ci == NULL) {
- XRRFreeScreenResources(sr);
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, RGFW_DEBUG_CTX_MON(monitor), "monitor found");
- return monitor;
- }
-
-
- float physW = (float)info->mm_width / 25.4f;
- float physH = (float)info->mm_height / 25.4f;
-
- RGFW_STRNCPY(monitor.name, info->name, sizeof(monitor.name) - 1);
- monitor.name[sizeof(monitor.name) - 1] = '\0';
-
- if ((u8)physW && (u8)physH) {
- monitor.physW = physW;
- monitor.physH = physH;
- }
-
- monitor.x = ci->x;
- monitor.y = ci->y;
-
- if (ci->width && ci->height) {
- monitor.mode.area.w = (u32)ci->width;
- monitor.mode.area.h = (u32)ci->height;
- }
- #endif
-
- #ifndef RGFW_NO_DPI
- XRRFreeCrtcInfo(ci);
- XRRFreeScreenResources(sr);
- #endif
-
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, RGFW_DEBUG_CTX_MON(monitor), "monitor found");
- return monitor;
-#endif
-#ifdef RGFW_WAYLAND
-RGFW_WAYLAND_LABEL RGFW_UNUSED(screen);
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, RGFW_DEBUG_CTX_MON(monitor), "monitor found");
- return monitor;
-#endif
-}
-
-RGFW_monitor* RGFW_getMonitors(size_t* len) {
- static RGFW_monitor monitors[7];
-
- RGFW_GOTO_WAYLAND(1);
- #ifdef RGFW_X11
- RGFW_init();
-
- Display* display = _RGFW.display;
- i32 max = ScreenCount(display);
-
- i32 i;
- for (i = 0; i < max && i < 6; i++)
- monitors[i] = RGFW_XCreateMonitor(i);
-
- if (len != NULL) *len = (size_t)((max <= 6) ? (max) : (6));
-
- return monitors;
- #endif
- #ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL RGFW_UNUSED(len);
- return monitors; /* TODO WAYLAND */
- #endif
-}
-
-RGFW_monitor RGFW_getPrimaryMonitor(void) {
- RGFW_GOTO_WAYLAND(1);
- #ifdef RGFW_X11
- return RGFW_XCreateMonitor(-1);
- #endif
- #ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL return (RGFW_monitor){ 0 }; /* TODO WAYLAND */
- #endif
-}
-
-RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) {
- RGFW_GOTO_WAYLAND(1);
-#ifdef RGFW_X11
- #ifndef RGFW_NO_DPI
- RGFW_init();
- XRRScreenResources* screenRes = XRRGetScreenResources(_RGFW.display, DefaultRootWindow(_RGFW.display));
- if (screenRes == NULL) return RGFW_FALSE;
-
- int i;
- for (i = 0; i < screenRes->ncrtc; i++) {
- XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(_RGFW.display, screenRes, screenRes->crtcs[i]);
- if (!crtcInfo) continue;
-
- if (mon.x == crtcInfo->x && mon.y == crtcInfo->y && (u32)mon.mode.area.w == crtcInfo->width && (u32)mon.mode.area.h == crtcInfo->height) {
- RRMode rmode = None;
- int index;
- for (index = 0; index < screenRes->nmode; index++) {
- RGFW_monitorMode foundMode;
- foundMode.area = RGFW_AREA(screenRes->modes[index].width, screenRes->modes[index].height);
- foundMode.refreshRate = RGFW_XCalculateRefreshRate(screenRes->modes[index]);
- RGFW_splitBPP((u32)DefaultDepth(_RGFW.display, DefaultScreen(_RGFW.display)), &foundMode);
-
- if (RGFW_monitorModeCompare(mode, foundMode, request)) {
- rmode = screenRes->modes[index].id;
-
- RROutput output = screenRes->outputs[i];
- XRROutputInfo* info = XRRGetOutputInfo(_RGFW.display, screenRes, output);
- if (info) {
- XRRSetCrtcConfig(_RGFW.display, screenRes, screenRes->crtcs[i],
- CurrentTime, 0, 0, rmode, RR_Rotate_0, &output, 1);
- XRRFreeOutputInfo(info);
- XRRFreeCrtcInfo(crtcInfo);
- XRRFreeScreenResources(screenRes);
- return RGFW_TRUE;
- }
- }
- }
-
- XRRFreeCrtcInfo(crtcInfo);
- XRRFreeScreenResources(screenRes);
- return RGFW_FALSE;
- }
-
- XRRFreeCrtcInfo(crtcInfo);
- }
-
- XRRFreeScreenResources(screenRes);
- return RGFW_FALSE;
- #endif
-#endif
-#ifdef RGFW_WAYLAND
-RGFW_WAYLAND_LABEL RGFW_UNUSED(mon); RGFW_UNUSED(mode); RGFW_UNUSED(request);
-#endif
- return RGFW_FALSE;
-}
-
-RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) {
- RGFW_monitor mon;
- RGFW_MEMSET(&mon, 0, sizeof(mon));
-
- RGFW_ASSERT(win != NULL);
- RGFW_GOTO_WAYLAND(1);
-#ifdef RGFW_X11
- XWindowAttributes attrs;
- if (!XGetWindowAttributes(win->src.display, win->src.window, &attrs)) {
- return mon;
- }
-
- i32 i;
- for (i = 0; i < ScreenCount(win->src.display) && i < 6; i++) {
- Screen* screen = ScreenOfDisplay(win->src.display, i);
- if (attrs.x >= 0 && attrs.x < XWidthOfScreen(screen) &&
- attrs.y >= 0 && attrs.y < XHeightOfScreen(screen))
- return RGFW_XCreateMonitor(i);
- }
-#endif
-#ifdef RGFW_WAYLAND
-RGFW_WAYLAND_LABEL
-#endif
- return mon;
-
-}
-
-#if defined(RGFW_OPENGL) && !defined(RGFW_EGL)
-void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
- if (win == NULL)
- glXMakeCurrent(NULL, (Drawable)NULL, (GLXContext) NULL);
- else
- glXMakeCurrent(win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx);
-}
-void* RGFW_getCurrent_OpenGL(void) { return glXGetCurrentContext(); }
-void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) { glXSwapBuffers(win->src.display, win->src.window); }
-#endif
-
-void RGFW_window_swapBuffers_software(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
- RGFW_GOTO_WAYLAND(0);
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
- #ifdef RGFW_X11
- win->src.bitmap->data = (char*) win->buffer;
- RGFW_RGB_to_BGR(win, (u8*)win->src.bitmap->data);
- XPutImage(win->src.display, win->src.window, win->src.gc, win->src.bitmap, 0, 0, 0, 0, win->bufferSize.w, win->bufferSize.h);
- win->src.bitmap->data = NULL;
- return;
- #endif
- #ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
- #if !defined(RGFW_BUFFER_BGR) && !defined(RGFW_OSMESA)
- RGFW_RGB_to_BGR(win, win->src.buffer);
- #else
- size_t y;
- for (y = 0; y < win->r.h; y++) {
- u32 index = (y * 4 * win->r.w);
- u32 index2 = (y * 4 * win->bufferSize.w);
- RGFW_MEMCPY(&win->src.buffer[index], &win->buffer[index2], win->r.w * 4);
- }
- #endif
-
- wl_surface_frame_done(win, NULL, 0);
- wl_surface_commit(win->src.surface);
- #endif
-#else
-#ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
-#endif
- RGFW_UNUSED(win);
-#endif
-}
-
-#if !defined(RGFW_EGL)
-
-void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) {
- RGFW_ASSERT(win != NULL);
-
- #if defined(RGFW_OPENGL)
- // cached pfn to avoid calling glXGetProcAddress more than once
- static PFNGLXSWAPINTERVALEXTPROC pfn = (PFNGLXSWAPINTERVALEXTPROC)123;
- static int (*pfn2)(int) = NULL;
-
- if (pfn == (PFNGLXSWAPINTERVALEXTPROC)123) {
- pfn = ((PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress((GLubyte*) "glXSwapIntervalEXT"));
- if (pfn == NULL) {
- const char* array[] = {"GLX_MESA_swap_control", "GLX_SGI_swap_control"};
- u32 i;
- for (i = 0; i < sizeof(array) / sizeof(char*) && pfn2 == NULL; i++)
- pfn2 = ((int(*)(int))glXGetProcAddress((GLubyte*) array[i]));
-
- if (pfn2 != NULL) {
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to load swap interval function, fallingback to the native swapinterval function");
- } else {
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to load swap interval function");
- }
- }
- }
- if (pfn != NULL)
- pfn(win->src.display, win->src.window, swapInterval);
- else if (pfn2 != NULL) {
- pfn2(swapInterval);
- }
- #else
- RGFW_UNUSED(swapInterval);
- #endif
-}
-#endif
-
-void RGFW_deinit(void) {
- if (_RGFW.windowCount == -1 || _RGFW_init == RGFW_FALSE) return;
- #define RGFW_FREE_LIBRARY(x) if (x != NULL) dlclose(x); x = NULL;
-#ifdef RGFW_X11
- /* to save the clipboard on the x server after the window is closed */
- RGFW_LOAD_ATOM(CLIPBOARD_MANAGER);
- RGFW_LOAD_ATOM(SAVE_TARGETS);
- if (XGetSelectionOwner(_RGFW.display, RGFW_XCLIPBOARD) == _RGFW.helperWindow) {
- XConvertSelection(_RGFW.display, CLIPBOARD_MANAGER, SAVE_TARGETS, None, _RGFW.helperWindow, CurrentTime);
- while (RGFW_XHandleClipboardSelectionHelper());
- }
- if (_RGFW.clipboard) {
- RGFW_FREE(_RGFW.clipboard);
- _RGFW.clipboard = NULL;
- }
-
- RGFW_freeMouse(_RGFW.hiddenMouse);
-
- XDestroyWindow(_RGFW.display, (Drawable) _RGFW.helperWindow); /*!< close the window */
- XCloseDisplay(_RGFW.display); /*!< kill connection to the x server */
-
- #if !defined(RGFW_NO_X11_CURSOR_PRELOAD) && !defined(RGFW_NO_X11_CURSOR)
- RGFW_FREE_LIBRARY(X11Cursorhandle);
- #endif
- #if !defined(RGFW_NO_X11_XI_PRELOAD)
- RGFW_FREE_LIBRARY(X11Xihandle);
- #endif
-
- #ifdef RGFW_USE_XDL
- XDL_close();
- #endif
-
- #if !defined(RGFW_NO_X11_EXT_PRELOAD)
- RGFW_FREE_LIBRARY(X11XEXThandle);
- #endif
-#endif
-#ifdef RGFW_WAYLAND
- wl_display_disconnect(_RGFW.wl_display);
-#endif
- #ifndef RGFW_NO_LINUX
- if (RGFW_eventWait_forceStop[0] || RGFW_eventWait_forceStop[1]){
- close(RGFW_eventWait_forceStop[0]);
- close(RGFW_eventWait_forceStop[1]);
- }
-
- u8 i;
- for (i = 0; i < RGFW_gamepadCount; i++) {
- if(RGFW_gamepads[i])
- close(RGFW_gamepads[i]);
- }
- #endif
-
- _RGFW.root = NULL;
- _RGFW.windowCount = -1;
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context deinitialized");
-}
-
-void RGFW_window_close(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
- if ((win->_flags & RGFW_windowNoInitAPI) == 0) RGFW_window_freeOpenGL(win);
-
- RGFW_GOTO_WAYLAND(0);
- #ifdef RGFW_X11
- /* ungrab pointer if it was grabbed */
- if (win->_flags & RGFW_HOLD_MOUSE)
- XUngrabPointer(win->src.display, CurrentTime);
-
- #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
- if (win->buffer != NULL) {
- if ((win->_flags & RGFW_BUFFER_ALLOC))
- RGFW_FREE(win->buffer);
- XDestroyImage((XImage*) win->src.bitmap);
- }
- #endif
-
- XFreeGC(win->src.display, win->src.gc);
- XDestroyWindow(win->src.display, (Drawable) win->src.window); /*!< close the window */
- win->src.window = 0;
- XCloseDisplay(win->src.display);
-
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a window was freed");
- _RGFW.windowCount--;
- if (_RGFW.windowCount == 0) RGFW_deinit();
-
- RGFW_clipboard_switch(NULL);
- RGFW_FREE(win->event.droppedFiles);
- if ((win->_flags & RGFW_WINDOW_ALLOC)) {
- RGFW_FREE(win);
- win = NULL;
- }
- return;
- #endif
-
- #ifdef RGFW_WAYLAND
- RGFW_WAYLAND_LABEL
-
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a window was freed");
-
- xdg_toplevel_destroy(win->src.xdg_toplevel);
- xdg_surface_destroy(win->src.xdg_surface);
- wl_surface_destroy(win->src.surface);
-
- _RGFW.windowCount--;
- if (_RGFW.windowCount == 0) RGFW_deinit();
-
- #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
- wl_buffer_destroy(win->src.wl_buffer);
- if ((win->_flags & RGFW_BUFFER_ALLOC))
- RGFW_FREE(win->buffer);
-
- munmap(win->src.buffer, (size_t)(win->r.w * win->r.h * 4));
- #endif
-
- RGFW_clipboard_switch(NULL);
- RGFW_FREE(win->event.droppedFiles);
- if ((win->_flags & RGFW_WINDOW_ALLOC)) {
- RGFW_FREE(win);
- win = NULL;
- }
- #endif
-}
-
-
-/*
- End of X11 linux / wayland / unix defines
-*/
-
-#include
-#include
-#include
-
-void RGFW_stopCheckEvents(void) {
-
- RGFW_eventWait_forceStop[2] = 1;
- while (1) {
- const char byte = 0;
- const ssize_t result = write(RGFW_eventWait_forceStop[1], &byte, 1);
- if (result == 1 || result == -1)
- break;
- }
-}
-
-void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) {
- if (waitMS == 0) return;
-
- u8 i;
- if (RGFW_eventWait_forceStop[0] == 0 || RGFW_eventWait_forceStop[1] == 0) {
- if (pipe(RGFW_eventWait_forceStop) != -1) {
- fcntl(RGFW_eventWait_forceStop[0], F_GETFL, 0);
- fcntl(RGFW_eventWait_forceStop[0], F_GETFD, 0);
- fcntl(RGFW_eventWait_forceStop[1], F_GETFL, 0);
- fcntl(RGFW_eventWait_forceStop[1], F_GETFD, 0);
- }
- }
-
- struct pollfd fds[] = {
- #ifdef RGFW_WAYLAND
- { wl_display_get_fd(win->src.wl_display), POLLIN, 0 },
- #else
- { ConnectionNumber(win->src.display), POLLIN, 0 },
- #endif
- #ifdef RGFW_X11
- { ConnectionNumber(_RGFW.display), POLLIN, 0 },
- #endif
- { RGFW_eventWait_forceStop[0], POLLIN, 0 },
- #if defined(__linux__)
- { -1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0}
- #endif
- };
-
- u8 index = 2;
-#ifdef RGFW_X11
- index++;
-#endif
-
- #if defined(__linux__) || defined(__NetBSD__)
- for (i = 0; i < RGFW_gamepadCount; i++) {
- if (RGFW_gamepads[i] == 0)
- continue;
-
- fds[index].fd = RGFW_gamepads[i];
- index++;
- }
- #endif
-
-
- u64 start = RGFW_getTimeNS();
-
-
- #ifdef RGFW_WAYLAND
- while (wl_display_dispatch(win->src.wl_display) <= 0
- #else
- while (XPending(win->src.display) == 0
- #endif
- #ifdef RGFW_X11
- && XPending(_RGFW.display) == 0
- #endif
- ) {
- if (poll(fds, index, waitMS) <= 0)
- break;
-
- if (waitMS != RGFW_eventWaitNext)
- waitMS -= (i32)(RGFW_getTimeNS() - start) / (i32)1e+6;
- }
-
- /* drain any data in the stop request */
- if (RGFW_eventWait_forceStop[2]) {
- char data[64];
- (void)!read(RGFW_eventWait_forceStop[0], data, sizeof(data));
-
- RGFW_eventWait_forceStop[2] = 0;
- }
-}
-
-i32 RGFW_getClock(void);
-i32 RGFW_getClock(void) {
- static i32 clock = -1;
- if (clock != -1) return clock;
-
- #if defined(_POSIX_MONOTONIC_CLOCK)
- struct timespec ts;
- if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
- clock = CLOCK_MONOTONIC;
- #else
- clock = CLOCK_REALTIME;
- #endif
-
- return clock;
-}
-
-u64 RGFW_getTimerFreq(void) { return 1000000000LLU; }
-u64 RGFW_getTimerValue(void) {
- struct timespec ts;
- clock_gettime(CLOCK_REALTIME, &ts);
- return (u64)ts.tv_sec * RGFW_getTimerFreq() + (u64)ts.tv_nsec;
-}
-#endif /* end of wayland or X11 defines */
-
-
-
-/*
-
- Start of Windows defines
-
-
-*/
-
-#ifdef RGFW_WINDOWS
-#define WIN32_LEAN_AND_MEAN
-#define OEMRESOURCE
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#ifndef WM_DPICHANGED
-#define WM_DPICHANGED 0x02E0
-#endif
-
-#ifndef RGFW_NO_XINPUT
- typedef DWORD (WINAPI * PFN_XInputGetState)(DWORD,XINPUT_STATE*);
- PFN_XInputGetState XInputGetStateSRC = NULL;
- #define XInputGetState XInputGetStateSRC
-
- typedef DWORD (WINAPI * PFN_XInputGetKeystroke)(DWORD, DWORD, PXINPUT_KEYSTROKE);
- PFN_XInputGetKeystroke XInputGetKeystrokeSRC = NULL;
- #define XInputGetKeystroke XInputGetKeystrokeSRC
-
- HMODULE RGFW_XInput_dll = NULL;
-#endif
-
-char* RGFW_createUTF8FromWideStringWin32(const WCHAR* source);
-
-#define GL_FRONT 0x0404
-#define GL_BACK 0x0405
-#define GL_LEFT 0x0406
-#define GL_RIGHT 0x0407
-
-typedef int (*PFN_wglGetSwapIntervalEXT)(void);
-PFN_wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc = NULL;
-#define wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc
-
-
-void* RGFWgamepadApi = NULL;
-
-/* these two wgl functions need to be preloaded */
-typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList);
-PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
-
-#ifndef RGFW_EGL
- HMODULE RGFW_wgl_dll = NULL;
-#endif
-
-#ifndef RGFW_NO_LOAD_WGL
- typedef HGLRC(WINAPI* PFN_wglCreateContext)(HDC);
- typedef BOOL(WINAPI* PFN_wglDeleteContext)(HGLRC);
- typedef PROC(WINAPI* PFN_wglGetProcAddress)(LPCSTR);
- typedef BOOL(WINAPI* PFN_wglMakeCurrent)(HDC, HGLRC);
- typedef HDC(WINAPI* PFN_wglGetCurrentDC)(void);
- typedef HGLRC(WINAPI* PFN_wglGetCurrentContext)(void);
- typedef BOOL(WINAPI* PFN_wglShareLists)(HGLRC, HGLRC);
-
- PFN_wglCreateContext wglCreateContextSRC;
- PFN_wglDeleteContext wglDeleteContextSRC;
- PFN_wglGetProcAddress wglGetProcAddressSRC;
- PFN_wglMakeCurrent wglMakeCurrentSRC;
- PFN_wglGetCurrentDC wglGetCurrentDCSRC;
- PFN_wglGetCurrentContext wglGetCurrentContextSRC;
- PFN_wglShareLists wglShareListsSRC;
-
- #define wglCreateContext wglCreateContextSRC
- #define wglDeleteContext wglDeleteContextSRC
- #define wglGetProcAddress wglGetProcAddressSRC
- #define wglMakeCurrent wglMakeCurrentSRC
- #define wglGetCurrentDC wglGetCurrentDCSRC
- #define wglGetCurrentContext wglGetCurrentContextSRC
- #define wglShareLists wglShareListsSRC
-#endif
-
-#if defined(RGFW_OPENGL) && !defined(RGFW_EGL)
-RGFW_bool RGFW_extensionSupportedPlatform(const char * extension, size_t len) {
- const char* extensions = NULL;
-
- RGFW_proc proc = RGFW_getProcAddress("wglGetExtensionsStringARB");
- RGFW_proc proc2 = RGFW_getProcAddress("wglGetExtensionsStringEXT");
-
- if (proc)
- extensions = ((const char* (*)(HDC))proc)(wglGetCurrentDC());
- else if (proc2)
- extensions = ((const char*(*)(void))proc2)();
-
- return extensions != NULL && RGFW_extensionSupportedStr(extensions, extension, len);
-}
-
-RGFW_proc RGFW_getProcAddress(const char* procname) {
- RGFW_proc proc = (RGFW_proc)wglGetProcAddress(procname);
- if (proc)
- return proc;
-
- return (RGFW_proc) GetProcAddress(RGFW_wgl_dll, procname);
-}
-
-typedef HRESULT (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats);
-PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL;
-
-typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval);
-PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
-#endif
-
-#ifndef RGFW_NO_DWM
-HMODULE RGFW_dwm_dll = NULL;
-typedef struct { DWORD dwFlags; int fEnable; HRGN hRgnBlur; int fTransitionOnMaximized;} DWM_BLURBEHIND;
-typedef HRESULT (WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND, const DWM_BLURBEHIND*);
-PFN_DwmEnableBlurBehindWindow DwmEnableBlurBehindWindowSRC = NULL;
-#endif
-void RGFW_win32_makeWindowTransparent(RGFW_window* win);
-void RGFW_win32_makeWindowTransparent(RGFW_window* win) {
- if (!(win->_flags & RGFW_windowTransparent)) return;
-
- #ifndef RGFW_NO_DWM
- if (DwmEnableBlurBehindWindowSRC != NULL) {
- DWM_BLURBEHIND bb = {0, 0, 0, 0};
- bb.dwFlags = 0x1;
- bb.fEnable = TRUE;
- bb.hRgnBlur = NULL;
- DwmEnableBlurBehindWindowSRC(win->src.window, &bb);
-
- } else
- #endif
- {
- SetWindowLong(win->src.window, GWL_EXSTYLE, WS_EX_LAYERED);
- SetLayeredWindowAttributes(win->src.window, 0, 128, LWA_ALPHA);
- }
-}
-
-LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
-LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
- RGFW_window* win = (RGFW_window*)GetPropW(hWnd, L"RGFW");
- if (win == NULL) return DefWindowProcW(hWnd, message, wParam, lParam);
-
- RECT windowRect;
- GetWindowRect(hWnd, &windowRect);
-
- switch (message) {
- case WM_CLOSE:
- case WM_QUIT:
- RGFW_eventQueuePushEx(e.type = RGFW_quit; e._win = win);
- RGFW_windowQuitCallback(win);
- return 0;
- case WM_ACTIVATE: {
- RGFW_bool inFocus = RGFW_BOOL(LOWORD(wParam) != WA_INACTIVE);
- if (inFocus) win->_flags |= RGFW_windowFocus;
- else win->_flags &= ~ (u32)RGFW_windowFocus;
- RGFW_eventQueuePushEx(e.type = (RGFW_eventType)((u8)RGFW_focusOut - inFocus); e._win = win);
-
- RGFW_focusCallback(win, inFocus);
- RGFW_window_focusLost(win);
-
- if ((win->_flags & RGFW_windowFullscreen) == 0)
- return DefWindowProcW(hWnd, message, wParam, lParam);
-
- win->_flags &= ~(u32)RGFW_EVENT_PASSED;
- if (inFocus == RGFW_FALSE) RGFW_window_minimize(win);
- else RGFW_window_setFullscreen(win, 1);
- return DefWindowProcW(hWnd, message, wParam, lParam);
- }
- case WM_MOVE:
- win->r.x = windowRect.left;
- win->r.y = windowRect.top;
- RGFW_eventQueuePushEx(e.type = RGFW_windowMoved; e._win = win);
- RGFW_windowMovedCallback(win, win->r);
- return DefWindowProcW(hWnd, message, wParam, lParam);
- case WM_SIZE: {
- if (win->src.aspectRatio.w != 0 && win->src.aspectRatio.h != 0) {
- double aspectRatio = (double)win->src.aspectRatio.w / win->src.aspectRatio.h;
-
- int width = (windowRect.right - windowRect.left) - win->src.wOffset;
- int height = (windowRect.bottom - windowRect.top) - win->src.hOffset;
- int newHeight = (int)(width / aspectRatio);
- int newWidth = (int)(height * aspectRatio);
-
- if (win->r.w > windowRect.right - windowRect.left ||
- win->r.h > (i32)((u32)(windowRect.bottom - windowRect.top) - win->src.hOffset))
- {
- if (newHeight > height) windowRect.right = windowRect.left + newWidth;
- else windowRect.bottom = windowRect.top + newHeight;
- } else {
- if (newHeight < height) windowRect.right = windowRect.left + newWidth;
- else windowRect.bottom = windowRect.top + newHeight;
- }
-
- RGFW_window_resize(win, RGFW_AREA((u32)(windowRect.right - windowRect.left) - (u32)win->src.wOffset,
- (u32)(windowRect.bottom - windowRect.top) - (u32)win->src.hOffset));
- }
-
- win->r.w = (windowRect.right - windowRect.left) - (i32)win->src.wOffset;
- win->r.h = (windowRect.bottom - windowRect.top) - (i32)win->src.hOffset;
- RGFW_eventQueuePushEx(e.type = RGFW_windowResized; e._win = win);
- RGFW_windowResizedCallback(win, win->r);
- RGFW_window_checkMode(win);
- return DefWindowProcW(hWnd, message, wParam, lParam);
- }
- #ifndef RGFW_NO_MONITOR
- case WM_DPICHANGED: {
- if (win->_flags & RGFW_windowScaleToMonitor) RGFW_window_scaleToMonitor(win);
-
- const float scaleX = HIWORD(wParam) / (float) 96;
- const float scaleY = LOWORD(wParam) / (float) 96;
- RGFW_scaleUpdatedCallback(win, scaleX, scaleY);
- RGFW_eventQueuePushEx(e.type = RGFW_scaleUpdated; e.scaleX = scaleX; e.scaleY = scaleY; e._win = win);
- return DefWindowProcW(hWnd, message, wParam, lParam);
- }
- #endif
- case WM_GETMINMAXINFO: {
- MINMAXINFO* mmi = (MINMAXINFO*) lParam;
- mmi->ptMinTrackSize.x = (LONG)(win->src.minSize.w + win->src.wOffset);
- mmi->ptMinTrackSize.y = (LONG)(win->src.minSize.h + win->src.hOffset);
- if (win->src.maxSize.w == 0 && win->src.maxSize.h == 0)
- return DefWindowProcW(hWnd, message, wParam, lParam);
-
- mmi->ptMaxTrackSize.x = (LONG)(win->src.maxSize.w + win->src.wOffset);
- mmi->ptMaxTrackSize.y = (LONG)(win->src.maxSize.h + win->src.hOffset);
- return DefWindowProcW(hWnd, message, wParam, lParam);
- }
- case WM_PAINT: {
- PAINTSTRUCT ps;
- BeginPaint(hWnd, &ps);
- RGFW_eventQueuePushEx(e.type = RGFW_windowRefresh; e._win = win);
- RGFW_windowRefreshCallback(win);
- EndPaint(hWnd, &ps);
-
- return DefWindowProcW(hWnd, message, wParam, lParam);
- }
- #if(_WIN32_WINNT >= 0x0600)
- case WM_DWMCOMPOSITIONCHANGED:
- case WM_DWMCOLORIZATIONCOLORCHANGED:
- RGFW_win32_makeWindowTransparent(win);
- break;
- #endif
-/* based on sokol_app.h */
-#ifdef RGFW_ADVANCED_SMOOTH_RESIZE
- case WM_ENTERSIZEMOVE: SetTimer(win->src.window, 1, USER_TIMER_MINIMUM, NULL); break;
- case WM_EXITSIZEMOVE: KillTimer(win->src.window, 1); break;
- case WM_TIMER: RGFW_windowRefreshCallback(win); break;
-#endif
- case WM_NCLBUTTONDOWN: {
- /* workaround for half-second pause when starting to move window
- see: https://gamedev.net/forums/topic/672094-keeping-things-moving-during-win32-moveresize-events/5254386/
- */
- POINT point = { 0, 0 };
- if (SendMessage(win->src.window, WM_NCHITTEST, wParam, lParam) != HTCAPTION || GetCursorPos(&point) == FALSE)
- break;
-
- ScreenToClient(win->src.window, &point);
- PostMessage(win->src.window, WM_MOUSEMOVE, 0, ((uint32_t)point.x)|(((uint32_t)point.y) << 16));
- break;
- }
- default: break;
- }
- return DefWindowProcW(hWnd, message, wParam, lParam);
-}
-
-#ifndef RGFW_NO_DPI
- HMODULE RGFW_Shcore_dll = NULL;
- typedef HRESULT (WINAPI *PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*);
- PFN_GetDpiForMonitor GetDpiForMonitorSRC = NULL;
- #define GetDpiForMonitor GetDpiForMonitorSRC
-#endif
-
-#if !defined(RGFW_NO_LOAD_WINMM) && !defined(RGFW_NO_WINMM)
- HMODULE RGFW_winmm_dll = NULL;
- typedef u32 (WINAPI * PFN_timeBeginPeriod)(u32);
- typedef PFN_timeBeginPeriod PFN_timeEndPeriod;
- PFN_timeBeginPeriod timeBeginPeriodSRC, timeEndPeriodSRC;
- #define timeBeginPeriod timeBeginPeriodSRC
- #define timeEndPeriod timeEndPeriodSRC
-#elif !defined(RGFW_NO_WINMM)
- __declspec(dllimport) u32 __stdcall timeBeginPeriod(u32 uPeriod);
- __declspec(dllimport) u32 __stdcall timeEndPeriod(u32 uPeriod);
-#endif
-#define RGFW_PROC_DEF(proc, name) if (name##SRC == NULL && proc != NULL) { \
- name##SRC = (PFN_##name)(RGFW_proc)GetProcAddress((proc), (#name)); \
- RGFW_ASSERT(name##SRC != NULL); \
- }
-
-#ifndef RGFW_NO_XINPUT
-void RGFW_loadXInput(void);
-void RGFW_loadXInput(void) {
- u32 i;
- static const char* names[] = {"xinput1_4.dll", "xinput9_1_0.dll", "xinput1_2.dll", "xinput1_1.dll"};
-
- for (i = 0; i < sizeof(names) / sizeof(const char*) && (XInputGetStateSRC == NULL || XInputGetKeystrokeSRC != NULL); i++) {
- RGFW_XInput_dll = LoadLibraryA(names[i]);
- RGFW_PROC_DEF(RGFW_XInput_dll, XInputGetState);
- RGFW_PROC_DEF(RGFW_XInput_dll, XInputGetKeystroke);
- }
-
- if (XInputGetStateSRC == NULL)
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errFailedFuncLoad, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to load XInputGetState");
- if (XInputGetKeystrokeSRC == NULL)
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errFailedFuncLoad, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to load XInputGetKeystroke");
-}
-#endif
-
-void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area){
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
- win->buffer = buffer;
- win->bufferSize = area;
-
- BITMAPV5HEADER bi;
- ZeroMemory(&bi, sizeof(bi));
- bi.bV5Size = sizeof(bi);
- bi.bV5Width = (i32)area.w;
- bi.bV5Height = -((LONG) area.h);
- bi.bV5Planes = 1;
- bi.bV5BitCount = 32;
- bi.bV5Compression = BI_RGB;
-
- win->src.bitmap = CreateDIBSection(win->src.hdc,
- (BITMAPINFO*) &bi, DIB_RGB_COLORS,
- (void**) &win->src.bitmapBits,
- NULL, (DWORD) 0);
-
- if (win->buffer == NULL)
- win->buffer = win->src.bitmapBits;
-
- win->src.hdcMem = CreateCompatibleDC(win->src.hdc);
- SelectObject(win->src.hdcMem, win->src.bitmap);
-
- #if defined(RGFW_OSMESA)
- win->src.ctx = OSMesaCreateContext(OSMESA_BGRA, NULL);
- OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, area.w, area.h);
- OSMesaPixelStore(OSMESA_Y_UP, 0);
- #endif
- #else
- RGFW_UNUSED(win); RGFW_UNUSED(buffer); RGFW_UNUSED(area); /*!< if buffer rendering is not being used */
- #endif
-}
-
-void RGFW_releaseCursor(RGFW_window* win) {
- RGFW_UNUSED(win);
- ClipCursor(NULL);
- const RAWINPUTDEVICE id = { 0x01, 0x02, RIDEV_REMOVE, NULL };
- RegisterRawInputDevices(&id, 1, sizeof(id));
-}
-
-void RGFW_captureCursor(RGFW_window* win, RGFW_rect rect) {
- RGFW_UNUSED(win); RGFW_UNUSED(rect);
-
- RECT clipRect;
- GetClientRect(win->src.window, &clipRect);
- ClientToScreen(win->src.window, (POINT*) &clipRect.left);
- ClientToScreen(win->src.window, (POINT*) &clipRect.right);
- ClipCursor(&clipRect);
-
- const RAWINPUTDEVICE id = { 0x01, 0x02, 0, win->src.window };
- RegisterRawInputDevices(&id, 1, sizeof(id));
-}
-
-#define RGFW_LOAD_LIBRARY(x, lib) if (x == NULL) { x = LoadLibraryA(lib); RGFW_ASSERT(x != NULL); }
-
-#ifdef RGFW_DIRECTX
-int RGFW_window_createDXSwapChain(RGFW_window* win, IDXGIFactory* pFactory, IUnknown* pDevice, IDXGISwapChain** swapchain) {
- RGFW_ASSERT(win && pFactory && pDevice && swapchain);
-
- static DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 };
- swapChainDesc.BufferCount = 2;
- 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 = (HWND)win->src.window;
- swapChainDesc.SampleDesc.Count = 1;
- swapChainDesc.SampleDesc.Quality = 0;
- swapChainDesc.Windowed = TRUE;
- swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
-
- HRESULT hr = pFactory->lpVtbl->CreateSwapChain(pFactory, (IUnknown*)pDevice, &swapChainDesc, swapchain);
- if (FAILED(hr)) {
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errDirectXContext, RGFW_DEBUG_CTX(win, hr), "Failed to create DirectX swap chain!");
- return -2;
- }
-
- return 0;
-}
-#endif
-
-void RGFW_win32_loadOpenGLFuncs(HWND dummyWin);
-void RGFW_win32_loadOpenGLFuncs(HWND dummyWin) {
-#ifdef RGFW_OPENGL
- if (wglSwapIntervalEXT != NULL && wglChoosePixelFormatARB != NULL && wglChoosePixelFormatARB != NULL)
- return;
-
- HDC dummy_dc = GetDC(dummyWin);
- u32 pfd_flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
-
- PIXELFORMATDESCRIPTOR pfd = {sizeof(pfd), 1, pfd_flags, PFD_TYPE_RGBA, 32, 8, PFD_MAIN_PLANE, 32, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 32, 8, 0, PFD_MAIN_PLANE, 0, 0, 0, 0};
-
- int dummy_pixel_format = ChoosePixelFormat(dummy_dc, &pfd);
- SetPixelFormat(dummy_dc, dummy_pixel_format, &pfd);
-
- HGLRC dummy_context = wglCreateContext(dummy_dc);
- wglMakeCurrent(dummy_dc, dummy_context);
-
- wglCreateContextAttribsARB = ((PFNWGLCREATECONTEXTATTRIBSARBPROC(WINAPI *)(const char*)) wglGetProcAddress)("wglCreateContextAttribsARB");
- wglChoosePixelFormatARB = ((PFNWGLCHOOSEPIXELFORMATARBPROC(WINAPI *)(const char*)) wglGetProcAddress)("wglChoosePixelFormatARB");
-
- wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)(RGFW_proc)wglGetProcAddress("wglSwapIntervalEXT");
- if (wglSwapIntervalEXT == NULL) {
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to load swap interval function");
- }
-
- wglMakeCurrent(dummy_dc, 0);
- wglDeleteContext(dummy_context);
- ReleaseDC(dummyWin, dummy_dc);
-#else
- RGFW_UNUSED(dummyWin);
-#endif
-}
-
-#ifndef RGFW_EGL
-void RGFW_window_initOpenGL(RGFW_window* win) {
-#ifdef RGFW_OPENGL
- PIXELFORMATDESCRIPTOR pfd;
- pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
- pfd.nVersion = 1;
- pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
- pfd.iPixelType = PFD_TYPE_RGBA;
- pfd.iLayerType = PFD_MAIN_PLANE;
- pfd.cColorBits = 32;
- pfd.cAlphaBits = 8;
- pfd.cDepthBits = 24;
- pfd.cStencilBits = (BYTE)RGFW_GL_HINTS[RGFW_glStencil];
- pfd.cAuxBuffers = (BYTE)RGFW_GL_HINTS[RGFW_glAuxBuffers];
- if (RGFW_GL_HINTS[RGFW_glStereo]) pfd.dwFlags |= PFD_STEREO;
-
- /* try to create the pixel format we want for opengl and then try to create an opengl context for the specified version */
- if (win->_flags & RGFW_windowOpenglSoftware)
- pfd.dwFlags |= PFD_GENERIC_FORMAT | PFD_GENERIC_ACCELERATED;
-
- /* get pixel format, default to a basic pixel format */
- int pixel_format = ChoosePixelFormat(win->src.hdc, &pfd);
- if (wglChoosePixelFormatARB != NULL) {
- i32* pixel_format_attribs = (i32*)RGFW_initFormatAttribs();
-
- int new_pixel_format;
- UINT num_formats;
- wglChoosePixelFormatARB(win->src.hdc, pixel_format_attribs, 0, 1, &new_pixel_format, &num_formats);
- if (!num_formats)
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to create a pixel format for WGL");
- else pixel_format = new_pixel_format;
- }
-
- PIXELFORMATDESCRIPTOR suggested;
- if (!DescribePixelFormat(win->src.hdc, pixel_format, sizeof(suggested), &suggested) ||
- !SetPixelFormat(win->src.hdc, pixel_format, &pfd))
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to set the WGL pixel format");
-
- if (!(pfd.dwFlags & PFD_GENERIC_ACCELERATED)) {
- win->_flags |= RGFW_windowOpenglSoftware;
- }
-
- if (wglCreateContextAttribsARB != NULL) {
- /* create opengl/WGL context for the specified version */
- u32 index = 0;
- i32 attribs[40];
-
- if (RGFW_GL_HINTS[RGFW_glProfile]== RGFW_glCore) {
- SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB);
- }
- else {
- SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB);
- }
-
- if (RGFW_GL_HINTS[RGFW_glMinor] || RGFW_GL_HINTS[RGFW_glMajor]) {
- SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_GL_HINTS[RGFW_glMajor]);
- SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_GL_HINTS[RGFW_glMinor]);
- }
-
- SET_ATTRIB(0, 0);
-
- win->src.ctx = (HGLRC)wglCreateContextAttribsARB(win->src.hdc, NULL, attribs);
- } else { /* fall back to a default context (probably opengl 2 or something) */
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to create an accelerated OpenGL Context");
- win->src.ctx = wglCreateContext(win->src.hdc);
- }
-
- ReleaseDC(win->src.window, win->src.hdc);
- win->src.hdc = GetDC(win->src.window);
- wglMakeCurrent(win->src.hdc, win->src.ctx);
-
- if (_RGFW.root != win)
- wglShareLists(_RGFW.root->src.ctx, win->src.ctx);
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context initalized");
-#else
- RGFW_UNUSED(win);
-#endif
-}
-
-void RGFW_window_freeOpenGL(RGFW_window* win) {
-#ifdef RGFW_OPENGL
- if (win->src.ctx == NULL) return;
- wglDeleteContext((HGLRC) win->src.ctx); /*!< delete opengl context */
- win->src.ctx = NULL;
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context freed");
-#else
- RGFW_UNUSED(win);
-#endif
-}
-#endif
-
-
-i32 RGFW_init(void) {
-#if defined(RGFW_C89) || defined(__cplusplus)
- if (_RGFW_init) return 0;
- _RGFW_init = RGFW_TRUE;
- _RGFW.root = NULL; _RGFW.current = NULL; _RGFW.windowCount = -1; _RGFW.eventLen = 0; _RGFW.eventIndex = 0;
-#endif
-
- #ifndef RGFW_NO_XINPUT
- if (RGFW_XInput_dll == NULL)
- RGFW_loadXInput();
- #endif
-
-#ifndef RGFW_NO_DPI
- #if (_WIN32_WINNT >= 0x0600)
- SetProcessDPIAware();
- #endif
-#endif
-
- #ifndef RGFW_NO_WINMM
- #ifndef RGFW_NO_LOAD_WINMM
- RGFW_LOAD_LIBRARY(RGFW_winmm_dll, "winmm.dll");
- RGFW_PROC_DEF(RGFW_winmm_dll, timeBeginPeriod);
- RGFW_PROC_DEF(RGFW_winmm_dll, timeEndPeriod);
- #endif
- timeBeginPeriod(1);
- #endif
-
- #ifndef RGFW_NO_DWM
- RGFW_LOAD_LIBRARY(RGFW_dwm_dll, "dwmapi.dll");
- RGFW_PROC_DEF(RGFW_dwm_dll, DwmEnableBlurBehindWindow);
- #endif
-
- RGFW_LOAD_LIBRARY(RGFW_wgl_dll, "opengl32.dll");
- #ifndef RGFW_NO_LOAD_WGL
- RGFW_PROC_DEF(RGFW_wgl_dll, wglCreateContext);
- RGFW_PROC_DEF(RGFW_wgl_dll, wglDeleteContext);
- RGFW_PROC_DEF(RGFW_wgl_dll, wglGetProcAddress);
- RGFW_PROC_DEF(RGFW_wgl_dll, wglMakeCurrent);
- RGFW_PROC_DEF(RGFW_wgl_dll, wglGetCurrentDC);
- RGFW_PROC_DEF(RGFW_wgl_dll, wglGetCurrentContext);
- RGFW_PROC_DEF(RGFW_wgl_dll, wglShareLists);
- #endif
-
- u8 RGFW_blk[] = { 0, 0, 0, 0 };
- _RGFW.hiddenMouse = RGFW_loadMouse(RGFW_blk, RGFW_AREA(1, 1), 4);
-
- _RGFW.windowCount = 0;
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context initialized");
- return 1;
-}
-
-RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) {
- if (name[0] == 0) name = (char*) " ";
-
- RGFW_window_basic_init(win, rect, flags);
-
- win->src.hIconSmall = win->src.hIconBig = NULL;
- win->src.maxSize = RGFW_AREA(0, 0);
- win->src.minSize = RGFW_AREA(0, 0);
- win->src.aspectRatio = RGFW_AREA(0, 0);
-
- HINSTANCE inh = GetModuleHandleA(NULL);
-
- #ifndef __cplusplus
- WNDCLASSW Class = { 0 }; /*!< Setup the Window class. */
- #else
- WNDCLASSW Class = { };
- #endif
-
- if (RGFW_className == NULL)
- RGFW_className = (char*)name;
-
- wchar_t wide_class[256];
- MultiByteToWideChar(CP_UTF8, 0, RGFW_className, -1, wide_class, 255);
-
- Class.lpszClassName = wide_class;
- Class.hInstance = inh;
- Class.hCursor = LoadCursor(NULL, IDC_ARROW);
- Class.lpfnWndProc = WndProcW;
- Class.cbClsExtra = sizeof(RGFW_window*);
-
- Class.hIcon = (HICON)LoadImageA(GetModuleHandleW(NULL), "RGFW_ICON", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
- if (Class.hIcon == NULL)
- Class.hIcon = (HICON)LoadImageA(NULL, (LPCSTR)IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
-
- RegisterClassW(&Class);
-
- DWORD window_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
-
- RECT windowRect, clientRect;
-
- if (!(flags & RGFW_windowNoBorder)) {
- window_style |= WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_MINIMIZEBOX | WS_THICKFRAME;
-
- if (!(flags & RGFW_windowNoResize))
- window_style |= WS_SIZEBOX | WS_MAXIMIZEBOX;
- } else
- window_style |= WS_POPUP | WS_VISIBLE | WS_SYSMENU;
-
- wchar_t wide_name[256];
- MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_name, 255);
- HWND dummyWin = CreateWindowW(Class.lpszClassName, (wchar_t*)wide_name, window_style, win->r.x, win->r.y, win->r.w, win->r.h, 0, 0, inh, 0);
-
- GetWindowRect(dummyWin, &windowRect);
- GetClientRect(dummyWin, &clientRect);
-
- RGFW_win32_loadOpenGLFuncs(dummyWin);
- DestroyWindow(dummyWin);
-
- win->src.hOffset = (u32)(windowRect.bottom - windowRect.top) - (u32)(clientRect.bottom - clientRect.top);
- win->src.wOffset = (u32)(windowRect.right - windowRect.left) - (u32)(clientRect.right - clientRect.left);
- win->src.window = CreateWindowW(Class.lpszClassName, (wchar_t*)wide_name, window_style, win->r.x, win->r.y, win->r.w + (i32)win->src.wOffset, win->r.h + (i32)win->src.hOffset, 0, 0, inh, 0);
- SetPropW(win->src.window, L"RGFW", win);
- RGFW_window_resize(win, RGFW_AREA(win->r.w, win->r.h)); /* so WM_GETMINMAXINFO gets called again */
-
- if (flags & RGFW_windowAllowDND) {
- win->_flags |= RGFW_windowAllowDND;
- RGFW_window_setDND(win, 1);
- }
- win->src.hdc = GetDC(win->src.window);
-
- if ((flags & RGFW_windowNoInitAPI) == 0) {
- RGFW_window_initOpenGL(win);
- RGFW_window_initBuffer(win);
- }
-
- RGFW_window_setFlags(win, flags);
- RGFW_win32_makeWindowTransparent(win);
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new window was created");
- RGFW_window_show(win);
-
- return win;
-}
-
-void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) {
- RGFW_setBit(&win->_flags, RGFW_windowNoBorder, !border);
- LONG style = GetWindowLong(win->src.window, GWL_STYLE);
-
-
- if (border == 0) {
- SetWindowLong(win->src.window, GWL_STYLE, style & ~WS_OVERLAPPEDWINDOW);
- SetWindowPos(
- win->src.window, HWND_TOP, 0, 0, 0, 0,
- SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE
- );
- }
- else {
- style |= WS_OVERLAPPEDWINDOW;
- if (win->_flags & RGFW_windowNoResize) style &= ~WS_MAXIMIZEBOX;
- SetWindowPos(
- win->src.window, HWND_TOP, 0, 0, 0, 0,
- SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE
- );
- }
-}
-
-void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow) {
- RGFW_setBit(&win->_flags, RGFW_windowAllowDND, allow);
- DragAcceptFiles(win->src.window, allow);
-}
-
-RGFW_area RGFW_getScreenSize(void) {
- HDC dc = GetDC(NULL);
- RGFW_area area = RGFW_AREA(GetDeviceCaps(dc, HORZRES), GetDeviceCaps(dc, VERTRES));
- ReleaseDC(NULL, dc);
- return area;
-}
-
-RGFW_point RGFW_getGlobalMousePoint(void) {
- POINT p;
- GetCursorPos(&p);
-
- return RGFW_POINT(p.x, p.y);
-}
-
-void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a) {
- RGFW_ASSERT(win != NULL);
- win->src.aspectRatio = a;
-}
-
-void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) {
- RGFW_ASSERT(win != NULL);
- win->src.minSize = a;
-}
-
-void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) {
- RGFW_ASSERT(win != NULL);
- win->src.maxSize = a;
-}
-
-void RGFW_window_focus(RGFW_window* win) {
- RGFW_ASSERT(win);
- SetForegroundWindow(win->src.window);
- SetFocus(win->src.window);
-}
-
-void RGFW_window_raise(RGFW_window* win) {
- RGFW_ASSERT(win);
- BringWindowToTop(win->src.window);
- SetWindowPos(win->src.window, HWND_TOP, win->r.x, win->r.y, win->r.w, win->r.h, SWP_NOSIZE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
-}
-
-void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) {
- RGFW_ASSERT(win != NULL);
-
- if (fullscreen == RGFW_FALSE) {
- RGFW_window_setBorder(win, 1);
- SetWindowPos(win->src.window, HWND_NOTOPMOST, win->_oldRect.x, win->_oldRect.y, win->_oldRect.w + (i32)win->src.wOffset, win->_oldRect.h + (i32)win->src.hOffset,
- SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
-
- win->_flags &= ~(u32)RGFW_windowFullscreen;
- win->r = win->_oldRect;
- return;
- }
-
- win->_oldRect = win->r;
- win->_flags |= RGFW_windowFullscreen;
-
- RGFW_monitor mon = RGFW_window_getMonitor(win);
- RGFW_window_setBorder(win, 0);
-
- SetWindowPos(win->src.window, HWND_TOPMOST, 0, 0, (i32)mon.mode.area.w, (i32)mon.mode.area.h, SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW);
- RGFW_monitor_scaleToWindow(mon, win);
-
- win->r = RGFW_RECT(0, 0, mon.mode.area.w, mon.mode.area.h);
-}
-
-void RGFW_window_maximize(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
- RGFW_window_hide(win);
- ShowWindow(win->src.window, SW_MAXIMIZE);
-}
-
-void RGFW_window_minimize(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
- ShowWindow(win->src.window, SW_MINIMIZE);
-}
-
-void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) {
- RGFW_ASSERT(win != NULL);
- if (floating) SetWindowPos(win->src.window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
- else SetWindowPos(win->src.window, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
-}
-
-void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) {
- SetWindowLong(win->src.window, GWL_EXSTYLE, WS_EX_LAYERED);
- SetLayeredWindowAttributes(win->src.window, 0, opacity, LWA_ALPHA);
-}
-
-void RGFW_window_restore(RGFW_window* win) { RGFW_window_show(win); }
-
-RGFW_bool RGFW_window_isFloating(RGFW_window* win) {
- return (GetWindowLongPtr(win->src.window, GWL_EXSTYLE) & WS_EX_TOPMOST) != 0;
-}
-
-u8 RGFW_xinput2RGFW[] = {
- RGFW_gamepadA, /* or PS X button */
- RGFW_gamepadB, /* or PS circle button */
- RGFW_gamepadX, /* or PS square button */
- RGFW_gamepadY, /* or PS triangle button */
- RGFW_gamepadR1, /* right bumper */
- RGFW_gamepadL1, /* left bump */
- RGFW_gamepadL2, /* left trigger */
- RGFW_gamepadR2, /* right trigger */
- 0, 0, 0, 0, 0, 0, 0, 0,
- RGFW_gamepadUp, /* dpad up */
- RGFW_gamepadDown, /* dpad down */
- RGFW_gamepadLeft, /* dpad left */
- RGFW_gamepadRight, /* dpad right */
- RGFW_gamepadStart, /* start button */
- RGFW_gamepadSelect,/* select button */
- RGFW_gamepadL3,
- RGFW_gamepadR3,
-};
-i32 RGFW_checkXInput(RGFW_window* win, RGFW_event* e);
-i32 RGFW_checkXInput(RGFW_window* win, RGFW_event* e) {
- #ifndef RGFW_NO_XINPUT
-
- RGFW_UNUSED(win);
- u16 i;
- for (i = 0; i < 4; i++) {
- XINPUT_KEYSTROKE keystroke;
-
- if (XInputGetKeystroke == NULL)
- return 0;
-
- DWORD result = XInputGetKeystroke((DWORD)i, 0, &keystroke);
-
- if ((keystroke.Flags & XINPUT_KEYSTROKE_REPEAT) == 0 && result != ERROR_EMPTY) {
- if (result != ERROR_SUCCESS)
- return 0;
-
- if (keystroke.VirtualKey > VK_PAD_RTHUMB_PRESS)
- continue;
-
- /* gamepad + 1 = RGFW_gamepadButtonReleased */
- e->type = RGFW_gamepadButtonPressed + !(keystroke.Flags & XINPUT_KEYSTROKE_KEYDOWN);
- e->button = RGFW_xinput2RGFW[keystroke.VirtualKey - 0x5800];
- RGFW_gamepadPressed[i][e->button].prev = RGFW_gamepadPressed[i][e->button].current;
- RGFW_gamepadPressed[i][e->button].current = RGFW_BOOL(keystroke.Flags & XINPUT_KEYSTROKE_KEYDOWN);
-
- RGFW_gamepadButtonCallback(win, i, e->button, e->type == RGFW_gamepadButtonPressed);
- return 1;
- }
-
- XINPUT_STATE state;
- if (XInputGetState == NULL ||
- XInputGetState((DWORD) i, &state) == ERROR_DEVICE_NOT_CONNECTED
- ) {
- if (RGFW_gamepads[i] == 0)
- continue;
-
- RGFW_gamepads[i] = 0;
- RGFW_gamepadCount--;
-
- win->event.type = RGFW_gamepadDisconnected;
- win->event.gamepad = (u16)i;
- RGFW_gamepadCallback(win, i, 0);
- return 1;
- }
-
- if (RGFW_gamepads[i] == 0) {
- RGFW_gamepads[i] = 1;
- RGFW_gamepadCount++;
-
- char str[] = "Microsoft X-Box (XInput device)";
- RGFW_MEMCPY(RGFW_gamepads_name[i], str, sizeof(str));
- RGFW_gamepads_name[i][sizeof(RGFW_gamepads_name[i]) - 1] = '\0';
- win->event.type = RGFW_gamepadConnected;
- win->event.gamepad = i;
- RGFW_gamepads_type[i] = RGFW_gamepadMicrosoft;
-
- RGFW_gamepadCallback(win, i, 1);
- 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_point axis1 = RGFW_POINT(((float)state.Gamepad.sThumbLX / 32768.0f) * 100, ((float)state.Gamepad.sThumbLY / -32768.0f) * 100);
- RGFW_point axis2 = RGFW_POINT(((float)state.Gamepad.sThumbRX / 32768.0f) * 100, ((float)state.Gamepad.sThumbRY / -32768.0f) * 100);
-
- if (axis1.x != e->axis[0].x || axis1.y != e->axis[0].y){
- win->event.whichAxis = 0;
-
- e->type = RGFW_gamepadAxisMove;
- e->axis[0] = axis1;
- RGFW_gamepadAxes[i][0] = e->axis[0];
-
- RGFW_gamepadAxisCallback(win, e->gamepad, e->axis, e->axisesCount, e->whichAxis);
- return 1;
- }
-
- if (axis2.x != e->axis[1].x || axis2.y != e->axis[1].y) {
- win->event.whichAxis = 1;
- e->type = RGFW_gamepadAxisMove;
- e->axis[1] = axis2;
- RGFW_gamepadAxes[i][1] = e->axis[1];
-
- RGFW_gamepadAxisCallback(win, e->gamepad, e->axis, e->axisesCount, e->whichAxis);
- return 1;
- }
- }
-
- #endif
-
- return 0;
-}
-
-void RGFW_stopCheckEvents(void) {
- PostMessageW(_RGFW.root->src.window, WM_NULL, 0, 0);
-}
-
-void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) {
- RGFW_UNUSED(win);
- MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD)waitMS, QS_ALLINPUT);
-}
-
-u8 RGFW_rgfwToKeyChar(u32 rgfw_keycode) {
- UINT vsc = RGFW_rgfwToApiKey(rgfw_keycode); // Should return a Windows VK_* code
- BYTE keyboardState[256] = {0};
-
- if (!GetKeyboardState(keyboardState))
- return (u8)rgfw_keycode;
-
- UINT vk = MapVirtualKeyW(vsc, MAPVK_VSC_TO_VK);
- HKL layout = GetKeyboardLayout(0);
-
- wchar_t charBuffer[2] = {0};
- int result = ToUnicodeEx(vk, vsc, keyboardState, charBuffer, 1, 0, layout);
-
- if (result <= 0)
- return (u8)rgfw_keycode;
-
- return (u8)charBuffer[0];
-}
-
-RGFW_event* RGFW_window_checkEvent(RGFW_window* win) {
- if (win == NULL || ((win->_flags & RGFW_windowFreeOnClose) && (win->_flags & RGFW_EVENT_QUIT))) return NULL;
- RGFW_event* ev = RGFW_window_checkEventCore(win);
- if (ev) {
- return ev;
- }
-
- static HDROP drop;
- if (win->event.type == RGFW_DNDInit) {
- 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.droppedFilesCount = DragQueryFileW(drop, 0xffffffff, NULL, 0);
-
- u32 i;
- for (i = 0; i < win->event.droppedFilesCount; i++) {
- UINT length = DragQueryFileW(drop, i, NULL, 0);
- if (length == 0)
- continue;
-
- WCHAR buffer[RGFW_MAX_PATH * 2];
- if (length > (RGFW_MAX_PATH * 2) - 1)
- length = RGFW_MAX_PATH * 2;
-
- DragQueryFileW(drop, i, buffer, length + 1);
-
- char* str = RGFW_createUTF8FromWideStringWin32(buffer);
- if (str != NULL)
- RGFW_MEMCPY(win->event.droppedFiles[i], str, length + 1);
-
- win->event.droppedFiles[i][RGFW_MAX_PATH - 1] = '\0';
- }
-
- DragFinish(drop);
- RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount);
-
- win->event.type = RGFW_DND;
- return &win->event;
- }
-
- if (RGFW_checkXInput(win, &win->event))
- return &win->event;
-
- static BYTE keyboardState[256];
- GetKeyboardState(keyboardState);
-
- MSG msg;
- if (PeekMessageA(&msg, NULL, 0u, 0u, PM_REMOVE)) {
- if (msg.hwnd != win->src.window && msg.hwnd != NULL) {
- TranslateMessage(&msg);
- DispatchMessageA(&msg);
- return RGFW_window_checkEvent(win);
- }
- } else {
- return NULL;
- }
-
- switch (msg.message) {
- case WM_MOUSELEAVE:
- win->event.type = RGFW_mouseLeave;
- win->_flags |= RGFW_MOUSE_LEFT;
- RGFW_mouseNotifyCallback(win, win->event.point, 0);
- break;
- case WM_SYSKEYUP: case WM_KEYUP: {
- i32 scancode = (HIWORD(msg.lParam) & (KF_EXTENDED | 0xff));
- if (scancode == 0)
- scancode = (i32)MapVirtualKeyW((UINT)msg.wParam, MAPVK_VK_TO_VSC);
-
- switch (scancode) {
- case 0x54: scancode = 0x137; break; /* Alt+PrtS */
- case 0x146: scancode = 0x45; break; /* Ctrl+Pause */
- case 0x136: scancode = 0x36; break; /* CJK IME sets the extended bit for right Shift */
- default: break;
- }
-
- win->event.key = (u8)RGFW_apiKeyToRGFW((u32) scancode);
-
- if (msg.wParam == VK_CONTROL) {
- if (HIWORD(msg.lParam) & KF_EXTENDED)
- win->event.key = RGFW_controlR;
- else win->event.key = RGFW_controlL;
- }
-
- wchar_t charBuffer;
- ToUnicodeEx((UINT)msg.wParam, (UINT)scancode, keyboardState, (wchar_t*)&charBuffer, 1, 0, NULL);
-
- win->event.keyChar = (u8)charBuffer;
-
- RGFW_keyboard[win->event.key].prev = RGFW_keyboard[win->event.key].current;
- win->event.type = RGFW_keyReleased;
- RGFW_keyboard[win->event.key].current = 0;
-
- RGFW_updateKeyMods(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001), (GetKeyState(VK_SCROLL) & 0x0001));
-
- RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, 0);
- break;
- }
- case WM_SYSKEYDOWN: case WM_KEYDOWN: {
- i32 scancode = (HIWORD(msg.lParam) & (KF_EXTENDED | 0xff));
- if (scancode == 0)
- scancode = (i32)MapVirtualKeyW((u32)msg.wParam, MAPVK_VK_TO_VSC);
-
- switch (scancode) {
- case 0x54: scancode = 0x137; break; /* Alt+PrtS */
- case 0x146: scancode = 0x45; break; /* Ctrl+Pause */
- case 0x136: scancode = 0x36; break; /* CJK IME sets the extended bit for right Shift */
- default: break;
- }
-
- win->event.key = (u8)RGFW_apiKeyToRGFW((u32) scancode);
- if (msg.wParam == VK_CONTROL) {
- if (HIWORD(msg.lParam) & KF_EXTENDED)
- win->event.key = RGFW_controlR;
- else win->event.key = RGFW_controlL;
- }
-
- wchar_t charBuffer;
- ToUnicodeEx((UINT)msg.wParam, (UINT)scancode, keyboardState, &charBuffer, 1, 0, NULL);
- win->event.keyChar = (u8)charBuffer;
-
- RGFW_keyboard[win->event.key].prev = RGFW_keyboard[win->event.key].current;
-
- win->event.type = RGFW_keyPressed;
- win->event.repeat = RGFW_isPressed(win, win->event.key);
- RGFW_keyboard[win->event.key].current = 1;
- RGFW_updateKeyMods(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001), (GetKeyState(VK_SCROLL) & 0x0001));
-
- RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, 1);
- break;
- }
- case WM_MOUSEMOVE: {
- if ((win->_flags & RGFW_HOLD_MOUSE))
- break;
-
- win->event.type = RGFW_mousePosChanged;
-
- i32 x = GET_X_LPARAM(msg.lParam);
- i32 y = GET_Y_LPARAM(msg.lParam);
-
- RGFW_mousePosCallback(win, win->event.point, win->event.vector);
-
- if (win->_flags & RGFW_MOUSE_LEFT) {
- win->_flags &= ~(u32)RGFW_MOUSE_LEFT;
- win->event.type = RGFW_mouseEnter;
- RGFW_mouseNotifyCallback(win, win->event.point, 1);
- }
-
- win->event.point.x = x;
- win->event.point.y = y;
- win->_lastMousePoint = RGFW_POINT(x, y);
-
- break;
- }
- case WM_INPUT: {
- if (!(win->_flags & RGFW_HOLD_MOUSE))
- break;
-
- unsigned size = sizeof(RAWINPUT);
- static RAWINPUT raw;
-
- GetRawInputData((HRAWINPUT)msg.lParam, RID_INPUT, &raw, &size, sizeof(RAWINPUTHEADER));
-
- if (raw.header.dwType != RIM_TYPEMOUSE || (raw.data.mouse.lLastX == 0 && raw.data.mouse.lLastY == 0) )
- break;
-
- if (raw.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) {
- POINT pos = {0, 0};
- int width, height;
-
- if (raw.data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) {
- pos.x += GetSystemMetrics(SM_XVIRTUALSCREEN);
- pos.y += GetSystemMetrics(SM_YVIRTUALSCREEN);
- width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
- height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
- }
- else {
- width = GetSystemMetrics(SM_CXSCREEN);
- height = GetSystemMetrics(SM_CYSCREEN);
- }
-
- pos.x += (int) (((float)raw.data.mouse.lLastX / 65535.f) * (float)width);
- pos.y += (int) (((float)raw.data.mouse.lLastY / 65535.f) * (float)height);
- ScreenToClient(win->src.window, &pos);
-
- win->event.vector.x = pos.x - win->_lastMousePoint.x;
- win->event.vector.y = pos.y - win->_lastMousePoint.y;
- } else {
- win->event.vector.x = raw.data.mouse.lLastX;
- win->event.vector.y = raw.data.mouse.lLastY;
- }
-
- win->event.type = RGFW_mousePosChanged;
- win->_lastMousePoint.x += win->event.vector.x;
- win->_lastMousePoint.y += win->event.vector.y;
- win->event.point = win->_lastMousePoint;
- RGFW_mousePosCallback(win, win->event.point, win->event.vector);
- break;
- }
- case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: case WM_XBUTTONDOWN:
- if (msg.message == WM_XBUTTONDOWN)
- win->event.button = RGFW_mouseMisc1 + (GET_XBUTTON_WPARAM(msg.wParam) == XBUTTON2);
- else win->event.button = (msg.message == WM_LBUTTONDOWN) ? RGFW_mouseLeft :
- (msg.message == WM_RBUTTONDOWN) ? RGFW_mouseRight : RGFW_mouseMiddle;
-
- win->event.type = RGFW_mouseButtonPressed;
- RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
- RGFW_mouseButtons[win->event.button].current = 1;
- RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
- break;
- case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: case WM_XBUTTONUP:
- if (msg.message == WM_XBUTTONUP)
- win->event.button = RGFW_mouseMisc1 + (GET_XBUTTON_WPARAM(msg.wParam) == XBUTTON2);
- else win->event.button = (msg.message == WM_LBUTTONUP) ? RGFW_mouseLeft :
- (msg.message == WM_RBUTTONUP) ? RGFW_mouseRight : RGFW_mouseMiddle;
- win->event.type = RGFW_mouseButtonReleased;
- RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
- RGFW_mouseButtons[win->event.button].current = 0;
- RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
- break;
- case WM_MOUSEWHEEL:
- if (msg.wParam > 0)
- win->event.button = RGFW_mouseScrollUp;
- else
- win->event.button = RGFW_mouseScrollDown;
-
- RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
- RGFW_mouseButtons[win->event.button].current = 1;
-
- win->event.scroll = (SHORT) HIWORD(msg.wParam) / (double) WHEEL_DELTA;
-
- win->event.type = RGFW_mouseButtonPressed;
- RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
- break;
- case WM_DROPFILES: {
- win->event.type = RGFW_DNDInit;
-
- drop = (HDROP) msg.wParam;
- POINT pt;
-
- /* Move the mouse to the position of the drop */
- DragQueryPoint(drop, &pt);
-
- win->event.point.x = pt.x;
- win->event.point.y = pt.y;
-
- RGFW_dndInitCallback(win, win->event.point);
- }
- break;
- default:
- TranslateMessage(&msg);
- DispatchMessageA(&msg);
- return RGFW_window_checkEvent(win);
- }
-
- TranslateMessage(&msg);
- DispatchMessageA(&msg);
-
- return &win->event;
-}
-
-RGFW_bool RGFW_window_isHidden(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
-
- return IsWindowVisible(win->src.window) == 0 && !RGFW_window_isMinimized(win);
-}
-
-RGFW_bool RGFW_window_isMinimized(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
-
- #ifndef __cplusplus
- WINDOWPLACEMENT placement = { 0 };
- #else
- WINDOWPLACEMENT placement = { };
- #endif
- GetWindowPlacement(win->src.window, &placement);
- return placement.showCmd == SW_SHOWMINIMIZED;
-}
-
-RGFW_bool RGFW_window_isMaximized(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
-
- #ifndef __cplusplus
- WINDOWPLACEMENT placement = { 0 };
- #else
- WINDOWPLACEMENT placement = { };
- #endif
- GetWindowPlacement(win->src.window, &placement);
- return placement.showCmd == SW_SHOWMAXIMIZED || IsZoomed(win->src.window);
-}
-
-typedef struct { int iIndex; HMONITOR hMonitor; RGFW_monitor* monitors; } RGFW_mInfo;
-#ifndef RGFW_NO_MONITOR
-RGFW_monitor win32CreateMonitor(HMONITOR src);
-RGFW_monitor win32CreateMonitor(HMONITOR src) {
- RGFW_monitor monitor;
- MONITORINFOEX monitorInfo;
-
- monitorInfo.cbSize = sizeof(MONITORINFOEX);
- GetMonitorInfoA(src, (LPMONITORINFO)&monitorInfo);
-
- /* get the monitor's index */
- DISPLAY_DEVICEA dd;
- dd.cb = sizeof(dd);
-
- DWORD deviceNum;
- for (deviceNum = 0; EnumDisplayDevicesA(NULL, deviceNum, &dd, 0); deviceNum++) {
- if (!(dd.StateFlags & DISPLAY_DEVICE_ACTIVE))
- continue;
-
- DEVMODEA dm;
- ZeroMemory(&dm, sizeof(dm));
- dm.dmSize = sizeof(dm);
-
- if (EnumDisplaySettingsA(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm)) {
- monitor.mode.refreshRate = dm.dmDisplayFrequency;
- RGFW_splitBPP(dm.dmBitsPerPel, &monitor.mode);
- }
-
- DISPLAY_DEVICEA mdd;
- mdd.cb = sizeof(mdd);
-
- if (EnumDisplayDevicesA(dd.DeviceName, (DWORD)deviceNum, &mdd, 0)) {
- RGFW_STRNCPY(monitor.name, mdd.DeviceString, sizeof(monitor.name) - 1);
- monitor.name[sizeof(monitor.name) - 1] = '\0';
- break;
- }
- }
-
-
-
-
- monitor.x = monitorInfo.rcWork.left;
- monitor.y = monitorInfo.rcWork.top;
- monitor.mode.area.w = (u32)(monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left);
- monitor.mode.area.h = (u32)(monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top);
-
- HDC hdc = CreateDC(monitorInfo.szDevice, NULL, NULL, NULL);
- /* get pixels per inch */
- float dpiX = (float)GetDeviceCaps(hdc, LOGPIXELSX);
- float dpiY = (float)GetDeviceCaps(hdc, LOGPIXELSX);
-
- monitor.scaleX = dpiX / 96.0f;
- monitor.scaleY = dpiY / 96.0f;
- monitor.pixelRatio = dpiX >= 192.0f ? 2.0f : 1.0f;
-
- monitor.physW = (float)GetDeviceCaps(hdc, HORZSIZE) / 25.4f;
- monitor.physH = (float)GetDeviceCaps(hdc, VERTSIZE) / 25.4f;
- DeleteDC(hdc);
-
- #ifndef RGFW_NO_DPI
- RGFW_LOAD_LIBRARY(RGFW_Shcore_dll, "shcore.dll");
- RGFW_PROC_DEF(RGFW_Shcore_dll, GetDpiForMonitor);
-
- if (GetDpiForMonitor != NULL) {
- u32 x, y;
- GetDpiForMonitor(src, MDT_EFFECTIVE_DPI, &x, &y);
- monitor.scaleX = (float) (x) / (float) 96.0f;
- monitor.scaleY = (float) (y) / (float) 96.0f;
- monitor.pixelRatio = dpiX >= 192.0f ? 2.0f : 1.0f;
- }
- #endif
-
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, RGFW_DEBUG_CTX_MON(monitor), "monitor found");
- return monitor;
-}
-#endif /* RGFW_NO_MONITOR */
-
-#ifndef RGFW_NO_MONITOR
-BOOL CALLBACK GetMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData);
-BOOL CALLBACK GetMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
- RGFW_UNUSED(hdcMonitor);
- RGFW_UNUSED(lprcMonitor);
-
- RGFW_mInfo* info = (RGFW_mInfo*) dwData;
-
-
- if (info->iIndex >= 6)
- return FALSE;
-
- info->monitors[info->iIndex] = win32CreateMonitor(hMonitor);
- info->iIndex++;
-
- return TRUE;
-}
-
-RGFW_monitor RGFW_getPrimaryMonitor(void) {
- #ifdef __cplusplus
- return win32CreateMonitor(MonitorFromPoint({ 0, 0 }, MONITOR_DEFAULTTOPRIMARY));
- #else
- return win32CreateMonitor(MonitorFromPoint((POINT) { 0, 0 }, MONITOR_DEFAULTTOPRIMARY));
- #endif
-}
-
-RGFW_monitor* RGFW_getMonitors(size_t* len) {
- static RGFW_monitor monitors[6];
- RGFW_mInfo info;
- info.iIndex = 0;
- info.monitors = monitors;
-
- EnumDisplayMonitors(NULL, NULL, GetMonitorHandle, (LPARAM) &info);
-
- if (len != NULL) *len = (size_t)info.iIndex;
- return monitors;
-}
-
-RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) {
- HMONITOR src = MonitorFromWindow(win->src.window, MONITOR_DEFAULTTOPRIMARY);
- return win32CreateMonitor(src);
-}
-
-RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) {
- POINT p = { mon.x, mon.y };
- HMONITOR src = MonitorFromPoint(p, MONITOR_DEFAULTTOPRIMARY);
-
- MONITORINFOEX monitorInfo;
- monitorInfo.cbSize = sizeof(MONITORINFOEX);
- GetMonitorInfoA(src, (LPMONITORINFO)&monitorInfo);
-
- DISPLAY_DEVICEA dd;
- dd.cb = sizeof(dd);
-
- /* Enumerate display devices */
- DWORD deviceNum;
- for (deviceNum = 0; EnumDisplayDevicesA(NULL, deviceNum, &dd, 0); deviceNum++) {
- if (!(dd.StateFlags & DISPLAY_DEVICE_ACTIVE))
- continue;
-
- if (strcmp(dd.DeviceName, (const char*)monitorInfo.szDevice) != 0)
- continue;
-
- DEVMODEA dm;
- ZeroMemory(&dm, sizeof(dm));
- dm.dmSize = sizeof(dm);
-
- if (EnumDisplaySettingsA(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm)) {
- if (request & RGFW_monitorScale) {
- dm.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT;
- dm.dmPelsWidth = mode.area.w;
- dm.dmPelsHeight = mode.area.h;
- }
-
- if (request & RGFW_monitorRefresh) {
- dm.dmFields |= DM_DISPLAYFREQUENCY;
- dm.dmDisplayFrequency = mode.refreshRate;
- }
-
- if (request & RGFW_monitorRGB) {
- dm.dmFields |= DM_BITSPERPEL;
- dm.dmBitsPerPel = (DWORD)(mode.red + mode.green + mode.blue);
- }
-
- if (ChangeDisplaySettingsExA((LPCSTR)dd.DeviceName, (DEVMODE *)&dm, NULL, CDS_TEST, NULL) == DISP_CHANGE_SUCCESSFUL) {
- if (ChangeDisplaySettingsExA((LPCSTR)dd.DeviceName, (DEVMODE *)&dm, NULL, CDS_UPDATEREGISTRY, NULL) == DISP_CHANGE_SUCCESSFUL)
- return RGFW_TRUE;
- return RGFW_FALSE;
- } else return RGFW_FALSE;
- }
- }
-
- return RGFW_FALSE;
-}
-
-#endif
-HICON RGFW_loadHandleImage(u8* src, i32 c, RGFW_area a, BOOL icon);
-HICON RGFW_loadHandleImage(u8* src, i32 c, RGFW_area a, BOOL icon) {
- size_t channels = (size_t)c;
-
- BITMAPV5HEADER bi;
- ZeroMemory(&bi, sizeof(bi));
- bi.bV5Size = sizeof(bi);
- bi.bV5Width = (i32)a.w;
- bi.bV5Height = -((LONG) a.h);
- bi.bV5Planes = 1;
- bi.bV5BitCount = (WORD)(channels * 8);
- bi.bV5Compression = BI_RGB;
- HDC dc = GetDC(NULL);
- u8* target = NULL;
-
- HBITMAP color = CreateDIBSection(dc,
- (BITMAPINFO*) &bi, DIB_RGB_COLORS, (void**) &target,
- NULL, (DWORD) 0);
-
- size_t x, y;
- for (y = 0; y < a.h; y++) {
- for (x = 0; x < a.w; x++) {
- size_t index = (y * 4 * (size_t)a.w) + x * channels;
- target[index] = src[index + 2];
- target[index + 1] = src[index + 1];
- target[index + 2] = src[index];
- target[index + 3] = src[index + 3];
- }
- }
-
- ReleaseDC(NULL, dc);
-
- HBITMAP mask = CreateBitmap((i32)a.w, (i32)a.h, 1, 1, NULL);
-
- ICONINFO ii;
- ZeroMemory(&ii, sizeof(ii));
- ii.fIcon = icon;
- ii.xHotspot = a.w / 2;
- ii.yHotspot = a.h / 2;
- ii.hbmMask = mask;
- ii.hbmColor = color;
-
- HICON handle = CreateIconIndirect(&ii);
-
- DeleteObject(color);
- DeleteObject(mask);
-
- return handle;
-}
-
-void* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels) {
- HCURSOR cursor = (HCURSOR) RGFW_loadHandleImage(icon, channels, a, FALSE);
- return cursor;
-}
-
-void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) {
- RGFW_ASSERT(win && mouse);
- SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) mouse);
- SetCursor((HCURSOR)mouse);
-}
-
-void RGFW_freeMouse(RGFW_mouse* mouse) {
- RGFW_ASSERT(mouse);
- DestroyCursor((HCURSOR)mouse);
-}
-
-RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) {
- return RGFW_window_setMouseStandard(win, RGFW_mouseArrow);
-}
-
-RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) {
- RGFW_ASSERT(win != NULL);
-
- static const u32 mouseIconSrc[16] = {OCR_NORMAL, OCR_NORMAL, OCR_IBEAM, OCR_CROSS, OCR_HAND, OCR_SIZEWE, OCR_SIZENS, OCR_SIZENWSE, OCR_SIZENESW, OCR_SIZEALL, OCR_NO};
- if (mouse > (sizeof(mouseIconSrc) / sizeof(u32)))
- return RGFW_FALSE;
-
- char* icon = MAKEINTRESOURCEA(mouseIconSrc[mouse]);
-
- SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) LoadCursorA(NULL, icon));
- SetCursor(LoadCursorA(NULL, icon));
- return RGFW_TRUE;
-}
-
-void RGFW_window_hide(RGFW_window* win) {
- ShowWindow(win->src.window, SW_HIDE);
-}
-
-void RGFW_window_show(RGFW_window* win) {
- if (win->_flags & RGFW_windowFocusOnShow) RGFW_window_focus(win);
- ShowWindow(win->src.window, SW_RESTORE);
-}
-
-#define RGFW_FREE_LIBRARY(x) if (x != NULL) FreeLibrary(x); x = NULL;
-void RGFW_deinit(void) {
- #ifndef RGFW_NO_XINPUT
- RGFW_FREE_LIBRARY(RGFW_XInput_dll);
- #endif
-
- #ifndef RGFW_NO_DPI
- RGFW_FREE_LIBRARY(RGFW_Shcore_dll);
- #endif
-
- #ifndef RGFW_NO_WINMM
- timeEndPeriod(1);
- #ifndef RGFW_NO_LOAD_WINMM
- RGFW_FREE_LIBRARY(RGFW_winmm_dll);
- #endif
- #endif
-
- RGFW_FREE_LIBRARY(RGFW_wgl_dll);
- _RGFW.root = NULL;
-
- RGFW_freeMouse(_RGFW.hiddenMouse);
- _RGFW.windowCount = -1;
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context deinitialized");
-}
-
-
-void RGFW_window_close(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
- #ifdef RGFW_BUFFER
- DeleteDC(win->src.hdcMem);
- DeleteObject(win->src.bitmap);
- #endif
-
- if ((win->_flags & RGFW_windowNoInitAPI) == 0) RGFW_window_freeOpenGL(win);
- RemovePropW(win->src.window, L"RGFW");
- ReleaseDC(win->src.window, win->src.hdc); /*!< delete device context */
- DestroyWindow(win->src.window); /*!< delete window */
-
- if (win->src.hIconSmall) DestroyIcon(win->src.hIconSmall);
- if (win->src.hIconBig) DestroyIcon(win->src.hIconBig);
-
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a window was freed");
- _RGFW.windowCount--;
- if (_RGFW.windowCount == 0) RGFW_deinit();
-
- RGFW_clipboard_switch(NULL);
- RGFW_FREE(win->event.droppedFiles);
- if ((win->_flags & RGFW_WINDOW_ALLOC)) {
- RGFW_FREE(win);
- win = NULL;
- }
-}
-
-void RGFW_window_move(RGFW_window* win, RGFW_point v) {
- RGFW_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) {
- RGFW_ASSERT(win != NULL);
-
- win->r.w = (i32)a.w;
- win->r.h = (i32)a.h;
- SetWindowPos(win->src.window, HWND_TOP, 0, 0, win->r.w + (i32)win->src.wOffset, win->r.h + (i32)win->src.hOffset, SWP_NOMOVE);
-}
-
-
-void RGFW_window_setName(RGFW_window* win, const char* name) {
- RGFW_ASSERT(win != NULL);
-
- wchar_t wide_name[256];
- MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_name, 256);
- SetWindowTextW(win->src.window, wide_name);
-}
-
-#ifndef RGFW_NO_PASSTHROUGH
-void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) {
- RGFW_ASSERT(win != NULL);
- COLORREF key = 0;
- BYTE alpha = 0;
- DWORD flags = 0;
- i32 exStyle = GetWindowLongW(win->src.window, GWL_EXSTYLE);
-
- if (exStyle & WS_EX_LAYERED)
- GetLayeredWindowAttributes(win->src.window, &key, &alpha, &flags);
-
- if (passthrough)
- exStyle |= (WS_EX_TRANSPARENT | WS_EX_LAYERED);
- else {
- exStyle &= ~WS_EX_TRANSPARENT;
- if (exStyle & WS_EX_LAYERED && !(flags & LWA_ALPHA))
- exStyle &= ~WS_EX_LAYERED;
- }
-
- SetWindowLongW(win->src.window, GWL_EXSTYLE, exStyle);
-
- if (passthrough)
- SetLayeredWindowAttributes(win->src.window, key, alpha, flags);
-}
-#endif
-
-RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* src, RGFW_area a, i32 channels, u8 type) {
- RGFW_ASSERT(win != NULL);
- #ifndef RGFW_WIN95
- RGFW_UNUSED(channels);
-
- if (win->src.hIconSmall && (type & RGFW_iconWindow)) DestroyIcon(win->src.hIconSmall);
- if (win->src.hIconBig && (type & RGFW_iconTaskbar)) DestroyIcon(win->src.hIconBig);
-
- if (src == NULL) {
- HICON defaultIcon = LoadIcon(NULL, IDI_APPLICATION);
- if (type & RGFW_iconWindow)
- SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)defaultIcon);
- if (type & RGFW_iconTaskbar)
- SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)defaultIcon);
- return RGFW_TRUE;
- }
-
- if (type & RGFW_iconWindow) {
- win->src.hIconSmall = RGFW_loadHandleImage(src, channels, a, TRUE);
- SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)win->src.hIconSmall);
- }
- if (type & RGFW_iconTaskbar) {
- win->src.hIconBig = RGFW_loadHandleImage(src, channels, a, TRUE);
- SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)win->src.hIconBig);
- }
- return RGFW_TRUE;
- #else
- RGFW_UNUSED(src);
- RGFW_UNUSED(a);
- RGFW_UNUSED(channels);
- return RGFW_FALSE;
- #endif
-}
-
-RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) {
- /* Open the clipboard */
- if (OpenClipboard(NULL) == 0)
- return -1;
-
- /* Get the clipboard data as a Unicode string */
- HANDLE hData = GetClipboardData(CF_UNICODETEXT);
- if (hData == NULL) {
- CloseClipboard();
- return -1;
- }
-
- wchar_t* wstr = (wchar_t*) GlobalLock(hData);
-
- RGFW_ssize_t textLen = 0;
-
- {
- setlocale(LC_ALL, "en_US.UTF-8");
-
- textLen = (RGFW_ssize_t)wcstombs(NULL, wstr, 0) + 1;
- if (str != NULL && (RGFW_ssize_t)strCapacity <= textLen - 1)
- textLen = 0;
-
- if (str != NULL && textLen) {
- if (textLen > 1)
- wcstombs(str, wstr, (size_t)(textLen));
-
- str[textLen] = '\0';
- }
- }
-
- /* Release the clipboard data */
- GlobalUnlock(hData);
- CloseClipboard();
-
- return textLen;
-}
-
-void RGFW_writeClipboard(const char* text, u32 textLen) {
- HANDLE object;
- WCHAR* buffer;
-
- object = GlobalAlloc(GMEM_MOVEABLE, (1 + textLen) * sizeof(WCHAR));
- if (!object)
- return;
-
- buffer = (WCHAR*) GlobalLock(object);
- if (!buffer) {
- GlobalFree(object);
- return;
- }
-
- MultiByteToWideChar(CP_UTF8, 0, text, -1, buffer, (i32)textLen);
- GlobalUnlock(object);
-
- if (!OpenClipboard(_RGFW.root->src.window)) {
- GlobalFree(object);
- return;
- }
-
- EmptyClipboard();
- SetClipboardData(CF_UNICODETEXT, object);
- CloseClipboard();
-}
-
-void RGFW_window_moveMouse(RGFW_window* win, RGFW_point p) {
- RGFW_ASSERT(win != NULL);
- win->_lastMousePoint = RGFW_POINT(p.x - win->r.x, p.y - win->r.y);
- SetCursorPos(p.x, p.y);
-}
-
-#ifdef RGFW_OPENGL
-void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
- if (win == NULL)
- wglMakeCurrent(NULL, NULL);
- else
- wglMakeCurrent(win->src.hdc, (HGLRC) win->src.ctx);
-}
-void* RGFW_getCurrent_OpenGL(void) { return wglGetCurrentContext(); }
-void RGFW_window_swapBuffers_OpenGL(RGFW_window* win){ SwapBuffers(win->src.hdc); }
-#endif
-
-#ifndef RGFW_EGL
-void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) {
- RGFW_ASSERT(win != NULL);
-#if defined(RGFW_OPENGL)
- if (wglSwapIntervalEXT == NULL || wglSwapIntervalEXT(swapInterval) == FALSE)
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to set swap interval");
-#else
- RGFW_UNUSED(swapInterval);
-#endif
-}
-#endif
-
-void RGFW_window_swapBuffers_software(RGFW_window* win) {
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
- if (win->buffer != win->src.bitmapBits)
- memcpy(win->src.bitmapBits, win->buffer, win->bufferSize.w * win->bufferSize.h * 4);
-
- RGFW_RGB_to_BGR(win, win->src.bitmapBits);
- BitBlt(win->src.hdc, 0, 0, win->r.w, win->r.h, win->src.hdcMem, 0, 0, SRCCOPY);
-#else
- RGFW_UNUSED(win);
-#endif
-}
-
-char* RGFW_createUTF8FromWideStringWin32(const WCHAR* source) {
- if (source == NULL) {
- return NULL;
- }
- i32 size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL);
- if (!size) {
- return NULL;
- }
-
- static char target[RGFW_MAX_PATH * 2];
- if (size > RGFW_MAX_PATH * 2)
- size = RGFW_MAX_PATH * 2;
-
- target[size] = 0;
-
- if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, size, NULL, NULL)) {
- return NULL;
- }
-
- return target;
-}
-
-u64 RGFW_getTimerFreq(void) {
- static u64 frequency = 0;
- if (frequency == 0) QueryPerformanceFrequency((LARGE_INTEGER*)&frequency);
-
- return frequency;
-}
-
-u64 RGFW_getTimerValue(void) {
- u64 value;
- QueryPerformanceCounter((LARGE_INTEGER*)&value);
- return value;
-}
-
-void RGFW_sleep(u64 ms) {
- Sleep((u32)ms);
-}
-
-#ifndef RGFW_NO_THREADS
-
-RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args) { return CreateThread(NULL, 0, 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 /* RGFW_WINDOWS */
-
-/*
- End of Windows defines
-*/
-
-
-
-/*
-
- Start of MacOS defines
-
-
-*/
-
-#if defined(RGFW_MACOS)
-/*
- based on silicon.h
- start of cocoa wrapper
-*/
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-typedef CGRect NSRect;
-typedef CGPoint NSPoint;
-typedef CGSize NSSize;
-
-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((id)nsclass, sel_registerName("alloc"))
-#define objc_msgSend_bool(x, y) ((BOOL (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y)
-#define objc_msgSend_void(x, y) ((void (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y)
-#define objc_msgSend_void_id(x, y, z) ((void (*)(id, SEL, id))objc_msgSend) ((id)x, (SEL)y, (id)z)
-#define objc_msgSend_uint(x, y) ((NSUInteger (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y)
-#define objc_msgSend_void_bool(x, y, z) ((void (*)(id, SEL, BOOL))objc_msgSend) ((id)(x), (SEL)y, (BOOL)z)
-#define objc_msgSend_bool_void(x, y) ((BOOL (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y)
-#define objc_msgSend_void_SEL(x, y, z) ((void (*)(id, SEL, SEL))objc_msgSend) ((id)(x), (SEL)y, (SEL)z)
-#define objc_msgSend_id(x, y) ((id (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y)
-#define objc_msgSend_id_id(x, y, z) ((id (*)(id, SEL, id))objc_msgSend) ((id)(x), (SEL)y, (id)z)
-#define objc_msgSend_id_bool(x, y, z) ((BOOL (*)(id, SEL, id))objc_msgSend) ((id)(x), (SEL)y, (id)z)
-#define objc_msgSend_int(x, y, z) ((id (*)(id, SEL, int))objc_msgSend) ((id)(x), (SEL)y, (int)z)
-#define objc_msgSend_arr(x, y, z) ((id (*)(id, SEL, int))objc_msgSend) ((id)(x), (SEL)y, (int)z)
-#define objc_msgSend_ptr(x, y, z) ((id (*)(id, SEL, void*))objc_msgSend) ((id)(x), (SEL)y, (void*)z)
-#define objc_msgSend_class(x, y) ((id (*)(Class, SEL))objc_msgSend) ((Class)(x), (SEL)y)
-#define objc_msgSend_class_char(x, y, z) ((id (*)(Class, SEL, char*))objc_msgSend) ((Class)(x), (SEL)y, (char*)z)
-
-id NSApp = NULL;
-
-#define NSRelease(obj) objc_msgSend_void((id)obj, sel_registerName("release"))
-id NSString_stringWithUTF8String(const char* str);
-id NSString_stringWithUTF8String(const char* str) {
- return ((id(*)(id, SEL, const char*))objc_msgSend)
- ((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), str);
-}
-
-const char* NSString_to_char(id str);
-const char* NSString_to_char(id str) {
- return ((const char* (*)(id, SEL)) objc_msgSend) ((id)(id)str, sel_registerName("UTF8String"));
-}
-
-void si_impl_func_to_SEL_with_name(const char* class_name, const char* register_name, void* function);
-void si_impl_func_to_SEL_with_name(const char* class_name, const char* register_name, void* function) {
- Class selected_class;
-
- if (RGFW_STRNCMP(class_name, "NSView", 6) == 0) {
- selected_class = objc_getClass("ViewClass");
- } else if (RGFW_STRNCMP(class_name, "NSWindow", 8) == 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)
-#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":", (void*)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":", (void*)function)
-
-unsigned char* NSBitmapImageRep_bitmapData(id imageRep);
-unsigned char* NSBitmapImageRep_bitmapData(id imageRep) {
- return ((unsigned char* (*)(id, SEL))objc_msgSend) ((id)imageRep, sel_registerName("bitmapData"));
-}
-
-typedef RGFW_ENUM(NSUInteger, NSBitmapFormat) {
- NSBitmapFormatAlphaFirst = 1 << 0, /* 0 means is alpha last (RGBA, CMYKA, etc.) */
- NSBitmapFormatAlphaNonpremultiplied = 1 << 1, /* 0 means is premultiplied */
- NSBitmapFormatFloatingpointSamples = 1 << 2, /* 0 is integer */
-
- NSBitmapFormatSixteenBitLittleEndian = (1 << 8),
- NSBitmapFormatThirtyTwoBitLittleEndian = (1 << 9),
- NSBitmapFormatSixteenBitBigEndian = (1 << 10),
- NSBitmapFormatThirtyTwoBitBigEndian = (1 << 11)
-};
-
-id NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits);
-id NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits) {
- SEL func = sel_registerName("initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bitmapFormat:bytesPerRow:bitsPerPixel:");
-
- return (id) ((id(*)(id, SEL, unsigned char**, NSInteger, NSInteger, NSInteger, NSInteger, bool, bool, id, NSBitmapFormat, NSInteger, NSInteger))objc_msgSend)
- (NSAlloc((id)objc_getClass("NSBitmapImageRep")), func, planes, width, height, bps, spp, alpha, isPlanar, NSString_stringWithUTF8String(colorSpaceName), bitmapFormat, rowBytes, pixelBits);
-}
-
-id NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha);
-id NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha) {
- void* nsclass = objc_getClass("NSColor");
- SEL func = sel_registerName("colorWithSRGBRed:green:blue:alpha:");
- return ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend)
- ((id)nsclass, func, red, green, blue, alpha);
-}
-
-typedef RGFW_ENUM(NSInteger, NSOpenGLContextParameter) {
- NSOpenGLContextParameterSwapInterval = 222, /* 1 param. 0 -> Don't sync, 1 -> Sync to vertical retrace */
- NSOpenGLContextParametectxaceOrder = 235, /* 1 param. 1 -> Above Window (default), -1 -> Below Window */
- NSOpenGLContextParametectxaceOpacity = 236, /* 1 param. 1-> Surface is opaque (default), 0 -> non-opaque */
- NSOpenGLContextParametectxaceBackingSize = 304, /* 2 params. Width/height of surface backing size */
- NSOpenGLContextParameterReclaimResources = 308, /* 0 params. */
- NSOpenGLContextParameterCurrentRendererID = 309, /* 1 param. Retrieves the current renderer ID */
- NSOpenGLContextParameterGPUVertexProcessing = 310, /* 1 param. Currently processing vertices with GPU (get) */
- NSOpenGLContextParameterGPUFragmentProcessing = 311, /* 1 param. Currently processing fragments with GPU (get) */
- NSOpenGLContextParameterHasDrawable = 314, /* 1 param. Boolean returned if drawable is attached */
- NSOpenGLContextParameterMPSwapsInFlight = 315, /* 1 param. Max number of swaps queued by the MP GL engine */
-
- NSOpenGLContextParameterSwapRectangle API_DEPRECATED("", macos(10.0, 10.14)) = 200, /* 4 params. Set or get the swap rectangle {x, y, w, h} */
- NSOpenGLContextParameterSwapRectangleEnable API_DEPRECATED("", macos(10.0, 10.14)) = 201, /* Enable or disable the swap rectangle */
- NSOpenGLContextParameterRasterizationEnable API_DEPRECATED("", macos(10.0, 10.14)) = 221, /* Enable or disable all rasterization */
- NSOpenGLContextParameterStateValidation API_DEPRECATED("", macos(10.0, 10.14)) = 301, /* Validate state for multi-screen functionality */
- NSOpenGLContextParametectxaceSurfaceVolatile API_DEPRECATED("", macos(10.0, 10.14)) = 306, /* 1 param. Surface volatile state */
-};
-
-typedef RGFW_ENUM(NSInteger, NSWindowButton) {
- NSWindowCloseButton = 0,
- NSWindowMiniaturizeButton = 1,
- NSWindowZoomButton = 2,
- NSWindowToolbarButton = 3,
- NSWindowDocumentIconButton = 4,
- NSWindowDocumentVersionsButton = 6,
- NSWindowFullScreenButton = 7,
-};
-void NSOpenGLContext_setValues(id context, const int* vals, NSOpenGLContextParameter param);
-void NSOpenGLContext_setValues(id context, const int* vals, NSOpenGLContextParameter param) {
- ((void (*)(id, SEL, const int*, NSOpenGLContextParameter))objc_msgSend)
- (context, sel_registerName("setValues:forParameter:"), vals, param);
-}
-void* NSOpenGLPixelFormat_initWithAttributes(const uint32_t* attribs);
-void* NSOpenGLPixelFormat_initWithAttributes(const uint32_t* attribs) {
- return (void*) ((id(*)(id, SEL, const uint32_t*))objc_msgSend)
- (NSAlloc((id)objc_getClass("NSOpenGLPixelFormat")), sel_registerName("initWithAttributes:"), attribs);
-}
-
-id NSPasteboard_generalPasteboard(void);
-id NSPasteboard_generalPasteboard(void) {
- return (id) objc_msgSend_id((id)objc_getClass("NSPasteboard"), sel_registerName("generalPasteboard"));
-}
-
-id* cstrToNSStringArray(char** strs, size_t len);
-id* cstrToNSStringArray(char** strs, size_t len) {
- static id nstrs[6];
- size_t i;
- for (i = 0; i < len; i++)
- nstrs[i] = NSString_stringWithUTF8String(strs[i]);
-
- return nstrs;
-}
-
-const char* NSPasteboard_stringForType(id pasteboard, NSPasteboardType dataType, size_t* len);
-const char* NSPasteboard_stringForType(id pasteboard, NSPasteboardType dataType, size_t* len) {
- SEL func = sel_registerName("stringForType:");
- id nsstr = NSString_stringWithUTF8String(dataType);
- id nsString = ((id(*)(id, SEL, id))objc_msgSend)(pasteboard, func, nsstr);
- const char* str = NSString_to_char(nsString);
- if (len != NULL)
- *len = (size_t)((NSUInteger(*)(id, SEL, int))objc_msgSend)(nsString, sel_registerName("maximumLengthOfBytesUsingEncoding:"), 4);
- return str;
-}
-
-id c_array_to_NSArray(void* array, size_t len);
-id c_array_to_NSArray(void* array, size_t len) {
- SEL 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(id view, NSPasteboardType* newTypes, size_t len);
-void NSregisterForDraggedTypes(id view, NSPasteboardType* newTypes, size_t len) {
- id* ntypes = cstrToNSStringArray((char**)newTypes, len);
-
- id array = c_array_to_NSArray(ntypes, len);
- objc_msgSend_void_id(view, sel_registerName("registerForDraggedTypes:"), array);
- NSRelease(array);
-}
-
-NSInteger NSPasteBoard_declareTypes(id pasteboard, NSPasteboardType* newTypes, size_t len, void* owner);
-NSInteger NSPasteBoard_declareTypes(id pasteboard, NSPasteboardType* newTypes, size_t len, void* owner) {
- id* ntypes = cstrToNSStringArray((char**)newTypes, len);
-
- SEL func = sel_registerName("declareTypes:owner:");
-
- id array = c_array_to_NSArray(ntypes, len);
-
- NSInteger output = ((NSInteger(*)(id, SEL, id, void*))objc_msgSend)
- (pasteboard, func, array, owner);
- NSRelease(array);
-
- return output;
-}
-
-#define NSRetain(obj) objc_msgSend_void((id)obj, sel_registerName("retain"))
-
-typedef enum NSApplicationActivationPolicy {
- NSApplicationActivationPolicyRegular,
- NSApplicationActivationPolicyAccessory,
- NSApplicationActivationPolicyProhibited
-} NSApplicationActivationPolicy;
-
-typedef RGFW_ENUM(u32, NSBackingStoreType) {
- NSBackingStoreRetained = 0,
- NSBackingStoreNonretained = 1,
- NSBackingStoreBuffered = 2
-};
-
-typedef RGFW_ENUM(u32, NSWindowStyleMask) {
- NSWindowStyleMaskBorderless = 0,
- NSWindowStyleMaskTitled = 1 << 0,
- NSWindowStyleMaskClosable = 1 << 1,
- NSWindowStyleMaskMiniaturizable = 1 << 2,
- NSWindowStyleMaskResizable = 1 << 3,
- NSWindowStyleMaskTexturedBackground = 1 << 8, /* deprecated */
- NSWindowStyleMaskUnifiedTitleAndToolbar = 1 << 12,
- NSWindowStyleMaskFullScreen = 1 << 14,
- NSWindowStyleMaskFullSizeContentView = 1 << 15,
- NSWindowStyleMaskUtilityWindow = 1 << 4,
- NSWindowStyleMaskDocModalWindow = 1 << 6,
- NSWindowStyleMaskNonactivatingpanel = 1 << 7,
- NSWindowStyleMaskHUDWindow = 1 << 13
-};
-
-NSPasteboardType const NSPasteboardTypeString = "public.utf8-plain-text"; /* Replaces NSStringPasteboardType */
-
-
-typedef RGFW_ENUM(i32, NSDragOperation) {
- NSDragOperationNone = 0,
- NSDragOperationCopy = 1,
- NSDragOperationLink = 2,
- NSDragOperationGeneric = 4,
- NSDragOperationPrivate = 8,
- NSDragOperationMove = 16,
- NSDragOperationDelete = 32,
- NSDragOperationEvery = (int)ULONG_MAX
-};
-
-void* NSArray_objectAtIndex(id array, NSUInteger index) {
- SEL func = sel_registerName("objectAtIndex:");
- return ((id(*)(id, SEL, NSUInteger))objc_msgSend)(array, func, index);
-}
-
-id NSWindow_contentView(id window) {
- SEL func = sel_registerName("contentView");
- return objc_msgSend_id(window, func);
-}
-
-/*
- End of cocoa wrapper
-*/
-
-#ifdef RGFW_OPENGL
-/* MacOS opengl API spares us yet again (there are no extensions) */
-RGFW_bool RGFW_extensionSupportedPlatform(const char * extension, size_t len) { RGFW_UNUSED(extension); RGFW_UNUSED(len); return RGFW_FALSE; }
-CFBundleRef RGFWnsglFramework = NULL;
-
-RGFW_proc RGFW_getProcAddress(const char* procname) {
- if (RGFWnsglFramework == NULL)
- RGFWnsglFramework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
-
- CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, procname, kCFStringEncodingASCII);
-
- RGFW_proc symbol = (RGFW_proc)CFBundleGetFunctionPointerForName(RGFWnsglFramework, symbolName);
-
- CFRelease(symbolName);
-
- return symbol;
-}
-#endif
-
-id NSWindow_delegate(RGFW_window* win) {
- return (id) objc_msgSend_id((id)win->src.window, sel_registerName("delegate"));
-}
-
-u32 RGFW_OnClose(id self) {
- RGFW_window* win = NULL;
- object_getInstanceVariable(self, (const char*)"RGFW_window", (void**)&win);
- if (win == NULL)
- return true;
-
- RGFW_eventQueuePushEx(e.type = RGFW_quit; e._win = win);
- RGFW_windowQuitCallback(win);
-
- return false;
-}
-
-/* NOTE(EimaMei): Fixes the constant clicking when the app is running under a terminal. */
-bool acceptsFirstResponder(void) { return true; }
-bool performKeyEquivalent(id event) { RGFW_UNUSED(event); return true; }
-
-NSDragOperation draggingEntered(id self, SEL sel, id sender) {
- RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel);
-
- return NSDragOperationCopy;
-}
-NSDragOperation draggingUpdated(id self, SEL sel, id sender) {
- RGFW_UNUSED(sel);
-
- RGFW_window* win = NULL;
- object_getInstanceVariable(self, "RGFW_window", (void**)&win);
- if (win == NULL || (!(win->_flags & RGFW_windowAllowDND)))
- return 0;
-
- NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation"));
- RGFW_eventQueuePushEx(e.type = RGFW_DNDInit;
- e.point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y));
- e._win = win);
-
- RGFW_dndInitCallback(win, win->event.point);
- return NSDragOperationCopy;
-}
-bool prepareForDragOperation(id self) {
- RGFW_window* win = NULL;
- object_getInstanceVariable(self, "RGFW_window", (void**)&win);
- if (win == NULL)
- return true;
-
- if (!(win->_flags & RGFW_windowAllowDND)) {
- return false;
- }
-
- return true;
-}
-
-void RGFW__osxDraggingEnded(id self, SEL sel, id sender);
-void RGFW__osxDraggingEnded(id self, SEL sel, id sender) { RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); 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) {
- RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel);
-
- RGFW_window* win = NULL;
- object_getInstanceVariable(self, "RGFW_window", (void**)&win);
-
- if (win == NULL)
- return false;
-
- /* id pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard")); */
-
- id pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard"));
-
- /* Get the types of data available on the pasteboard */
- id types = objc_msgSend_id(pasteBoard, sel_registerName("types"));
-
- /* Get the string type for file URLs */
- id fileURLsType = objc_msgSend_class_char(objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "NSFilenamesPboardType");
-
- /* Check if the pasteboard contains file URLs */
- if (objc_msgSend_id_bool(types, sel_registerName("containsObject:"), fileURLsType) == 0) {
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errClipboard, RGFW_DEBUG_CTX(win, 0), "No files found on the pasteboard.");
- return 0;
- }
-
- id fileURLs = objc_msgSend_id_id(pasteBoard, sel_registerName("propertyListForType:"), fileURLsType);
- int count = ((int (*)(id, SEL))objc_msgSend)(fileURLs, sel_registerName("count"));
-
- if (count == 0)
- return 0;
-
- int i;
- for (i = 0; i < count; i++) {
- id fileURL = objc_msgSend_arr(fileURLs, sel_registerName("objectAtIndex:"), i);
- const char *filePath = ((const char* (*)(id, SEL))objc_msgSend)(fileURL, sel_registerName("UTF8String"));
- RGFW_STRNCPY(win->event.droppedFiles[i], filePath, RGFW_MAX_PATH - 1);
- win->event.droppedFiles[i][RGFW_MAX_PATH - 1] = '\0';
- }
- NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation"));
-
- win->event.droppedFilesCount = (size_t)count;
- RGFW_eventQueuePushEx(e.type = RGFW_DND;
- e.point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y));
- e.droppedFilesCount = (size_t)count;
- e._win = win);
-
- RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount);
-
- return false;
-}
-
-#ifndef RGFW_NO_IOKIT
-#include
-#include
-
-u32 RGFW_osx_getFallbackRefreshRate(CGDirectDisplayID displayID) {
- u32 refreshRate = 0;
- io_iterator_t it;
- io_service_t service;
- CFNumberRef indexRef, clockRef, countRef;
- uint32_t clock, count;
-
-#ifdef kIOMainPortDefault
- if (IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceMatching("IOFramebuffer"), &it) != 0)
-#elif defined(kIOMasterPortDefault)
- if (IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceMatching("IOFramebuffer"), &it) != 0)
-#endif
- return RGFW_FALSE;
-
- while ((service = IOIteratorNext(it)) != 0) {
- uint32_t index;
- indexRef = (CFNumberRef)IORegistryEntryCreateCFProperty(service, CFSTR("IOFramebufferOpenGLIndex"), kCFAllocatorDefault, kNilOptions);
- if (indexRef == 0) continue;
-
- if (CFNumberGetValue(indexRef, kCFNumberIntType, &index) && CGOpenGLDisplayMaskToDisplayID(1 << index) == displayID) {
- CFRelease(indexRef);
- break;
- }
-
- CFRelease(indexRef);
- }
-
- if (service) {
- clockRef = (CFNumberRef)IORegistryEntryCreateCFProperty(service, CFSTR("IOFBCurrentPixelClock"), kCFAllocatorDefault, kNilOptions);
- if (clockRef) {
- if (CFNumberGetValue(clockRef, kCFNumberIntType, &clock) && clock) {
- countRef = (CFNumberRef)IORegistryEntryCreateCFProperty(service, CFSTR("IOFBCurrentPixelCount"), kCFAllocatorDefault, kNilOptions);
- if (countRef && CFNumberGetValue(countRef, kCFNumberIntType, &count) && count) {
- refreshRate = (u32)RGFW_ROUND(clock / (double) count);
- CFRelease(countRef);
- }
- }
- CFRelease(clockRef);
- }
- }
-
- IOObjectRelease(it);
- return refreshRate;
-}
-
-IOHIDDeviceRef RGFW_osxControllers[4] = {NULL};
-
-size_t findControllerIndex(IOHIDDeviceRef device) {
- size_t i;
- for (i = 0; i < 4; i++)
- if (RGFW_osxControllers[i] == device)
- return i;
- return (size_t)-1;
-}
-
-void RGFW__osxInputValueChangedCallback(void *context, IOReturn result, void *sender, IOHIDValueRef value) {
- RGFW_UNUSED(context); RGFW_UNUSED(result); RGFW_UNUSED(sender);
- IOHIDElementRef element = IOHIDValueGetElement(value);
-
- IOHIDDeviceRef device = IOHIDElementGetDevice(element);
- size_t index = findControllerIndex(device);
- if (index == (size_t)-1) return;
-
- uint32_t usagePage = IOHIDElementGetUsagePage(element);
- uint32_t usage = IOHIDElementGetUsage(element);
-
- CFIndex intValue = IOHIDValueGetIntegerValue(value);
-
- u8 RGFW_osx2RGFWSrc[2][RGFW_gamepadFinal] = {{
- 0, RGFW_gamepadSelect, RGFW_gamepadL3, RGFW_gamepadR3, RGFW_gamepadStart,
- RGFW_gamepadUp, RGFW_gamepadRight, RGFW_gamepadDown, RGFW_gamepadLeft,
- RGFW_gamepadL2, RGFW_gamepadR2, RGFW_gamepadL1, RGFW_gamepadR1,
- RGFW_gamepadY, RGFW_gamepadB, RGFW_gamepadA, RGFW_gamepadX, RGFW_gamepadHome},
- {0, RGFW_gamepadA, RGFW_gamepadB, RGFW_gamepadR3, RGFW_gamepadX,
- RGFW_gamepadY, RGFW_gamepadRight, RGFW_gamepadL1, RGFW_gamepadR1,
- RGFW_gamepadL2, RGFW_gamepadR2, RGFW_gamepadDown, RGFW_gamepadStart,
- RGFW_gamepadUp, RGFW_gamepadL3, RGFW_gamepadSelect, RGFW_gamepadStart, RGFW_gamepadHome}
- };
-
- u8* RGFW_osx2RGFW = RGFW_osx2RGFWSrc[0];
- if (RGFW_gamepads_type[index] == RGFW_gamepadMicrosoft)
- RGFW_osx2RGFW = RGFW_osx2RGFWSrc[1];
-
- switch (usagePage) {
- case kHIDPage_Button: {
- u8 button = 0;
- if (usage < sizeof(RGFW_osx2RGFW))
- button = RGFW_osx2RGFW[usage];
-
- RGFW_gamepadButtonCallback(_RGFW.root, (u16)index, button, (u8)intValue);
- RGFW_gamepadPressed[index][button].prev = RGFW_gamepadPressed[index][button].current;
- RGFW_gamepadPressed[index][button].current = RGFW_BOOL(intValue);
- RGFW_eventQueuePushEx(e.type = intValue ? RGFW_gamepadButtonPressed: RGFW_gamepadButtonReleased;
- e.button = button;
- e.gamepad = (u16)index;
- e._win = _RGFW.root);
- break;
- }
- case kHIDPage_GenericDesktop: {
- CFIndex logicalMin = IOHIDElementGetLogicalMin(element);
- CFIndex logicalMax = IOHIDElementGetLogicalMax(element);
-
- if (logicalMax <= logicalMin) return;
- if (intValue < logicalMin) intValue = logicalMin;
- if (intValue > logicalMax) intValue = logicalMax;
-
- i8 axisValue = (i8)(-100.0 + ((intValue - logicalMin) * 200.0) / (logicalMax - logicalMin));
-
- u8 whichAxis = 0;
- switch (usage) {
- case kHIDUsage_GD_X: RGFW_gamepadAxes[index][0].x = axisValue; whichAxis = 0; break;
- case kHIDUsage_GD_Y: RGFW_gamepadAxes[index][0].y = axisValue; whichAxis = 0; break;
- case kHIDUsage_GD_Z: RGFW_gamepadAxes[index][1].x = axisValue; whichAxis = 1; break;
- case kHIDUsage_GD_Rz: RGFW_gamepadAxes[index][1].y = axisValue; whichAxis = 1; break;
- default: return;
- }
-
- RGFW_event e;
- e.type = RGFW_gamepadAxisMove;
- e.gamepad = (u16)index;
- e.whichAxis = whichAxis;
- e._win = _RGFW.root;
- for (size_t i = 0; i < 4; i++)
- e.axis[i] = RGFW_gamepadAxes[index][i];
-
- RGFW_eventQueuePush(e);
-
- RGFW_gamepadAxisCallback(_RGFW.root, (u16)index, RGFW_gamepadAxes[index], 2, whichAxis);
- }
- }
-}
-
-void RGFW__osxDeviceAddedCallback(void* context, IOReturn result, void *sender, IOHIDDeviceRef device) {
- RGFW_UNUSED(context); RGFW_UNUSED(result); RGFW_UNUSED(sender);
- CFTypeRef usageRef = (CFTypeRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDPrimaryUsageKey));
- int usage = 0;
- if (usageRef)
- CFNumberGetValue((CFNumberRef)usageRef, kCFNumberIntType, (void*)&usage);
-
- if (usage != kHIDUsage_GD_Joystick && usage != kHIDUsage_GD_GamePad && usage != kHIDUsage_GD_MultiAxisController) {
- return;
- }
-
- size_t i;
- for (i = 0; i < 4; i++) {
- if (RGFW_osxControllers[i] != NULL)
- continue;
-
- RGFW_osxControllers[i] = device;
-
- IOHIDDeviceRegisterInputValueCallback(device, RGFW__osxInputValueChangedCallback, NULL);
-
- CFStringRef deviceName = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
- if (deviceName)
- CFStringGetCString(deviceName, RGFW_gamepads_name[i], sizeof(RGFW_gamepads_name[i]), kCFStringEncodingUTF8);
-
- RGFW_gamepads_type[i] = RGFW_gamepadUnknown;
- if (RGFW_STRSTR(RGFW_gamepads_name[i], "Microsoft") || RGFW_STRSTR(RGFW_gamepads_name[i], "X-Box") || RGFW_STRSTR(RGFW_gamepads_name[i], "Xbox"))
- RGFW_gamepads_type[i] = RGFW_gamepadMicrosoft;
- else if (RGFW_STRSTR(RGFW_gamepads_name[i], "PlayStation") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS3") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS4") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS5"))
- RGFW_gamepads_type[i] = RGFW_gamepadSony;
- else if (RGFW_STRSTR(RGFW_gamepads_name[i], "Nintendo"))
- RGFW_gamepads_type[i] = RGFW_gamepadNintendo;
- else if (RGFW_STRSTR(RGFW_gamepads_name[i], "Logitech"))
- RGFW_gamepads_type[i] = RGFW_gamepadLogitech;
-
- RGFW_gamepads[i] = (u16)i;
- RGFW_gamepadCount++;
-
- RGFW_eventQueuePushEx(e.type = RGFW_gamepadConnected;
- e.gamepad = (u16)i;
- e._win = _RGFW.root);
-
- RGFW_gamepadCallback(_RGFW.root, (u16)i, 1);
- break;
- }
-}
-
-void RGFW__osxDeviceRemovedCallback(void *context, IOReturn result, void *sender, IOHIDDeviceRef device) {
- RGFW_UNUSED(context); RGFW_UNUSED(result); RGFW_UNUSED(sender); RGFW_UNUSED(device);
- CFNumberRef usageRef = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDPrimaryUsageKey));
- int usage = 0;
- if (usageRef)
- CFNumberGetValue(usageRef, kCFNumberIntType, &usage);
-
- if (usage != kHIDUsage_GD_Joystick && usage != kHIDUsage_GD_GamePad && usage != kHIDUsage_GD_MultiAxisController) {
- return;
- }
-
- size_t index = findControllerIndex(device);
- if (index != (size_t)-1)
- RGFW_osxControllers[index] = NULL;
-
- RGFW_eventQueuePushEx(e.type = RGFW_gamepadDisconnected;
- e.gamepad = (u16)index;
- e._win = _RGFW.root);
- RGFW_gamepadCallback(_RGFW.root, (u16)index, 0);
-
- RGFW_gamepadCount--;
-}
-
-RGFWDEF void RGFW_osxInitIOKit(void);
-void RGFW_osxInitIOKit(void) {
- IOHIDManagerRef hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
- if (!hidManager) {
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errIOKit, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to create IOHIDManager.");
- return;
- }
-
- CFMutableDictionaryRef matchingDictionary = CFDictionaryCreateMutable(
- kCFAllocatorDefault,
- 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks
- );
- if (!matchingDictionary) {
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errIOKit, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to create matching dictionary for IOKit.");
- CFRelease(hidManager);
- return;
- }
-
- CFDictionarySetValue(
- matchingDictionary,
- CFSTR(kIOHIDDeviceUsagePageKey),
- CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, (int[]){kHIDPage_GenericDesktop})
- );
-
- IOHIDManagerSetDeviceMatching(hidManager, matchingDictionary);
-
- IOHIDManagerRegisterDeviceMatchingCallback(hidManager, RGFW__osxDeviceAddedCallback, NULL);
- IOHIDManagerRegisterDeviceRemovalCallback(hidManager, RGFW__osxDeviceRemovedCallback, NULL);
-
- IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-
- IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone);
-
- /* Execute the run loop once in order to register any initially-attached joysticks */
- CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
-}
-#endif
-
-void RGFW_moveToMacOSResourceDir(void) {
- char resourcesPath[256];
-
- CFBundleRef bundle = CFBundleGetMainBundle();
- if (!bundle)
- return;
-
- CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle);
- CFStringRef last = CFURLCopyLastPathComponent(resourcesURL);
-
- if (
- CFStringCompare(CFSTR("Resources"), last, 0) != kCFCompareEqualTo ||
- CFURLGetFileSystemRepresentation(resourcesURL, true, (u8*) resourcesPath, 255) == 0
- ) {
- CFRelease(last);
- CFRelease(resourcesURL);
- return;
- }
-
- CFRelease(last);
- CFRelease(resourcesURL);
-
- chdir(resourcesPath);
-}
-
-
-void RGFW__osxWindowDeminiaturize(id self, SEL sel) {
- RGFW_UNUSED(sel);
- RGFW_window* win = NULL;
- object_getInstanceVariable(self, "RGFW_window", (void**)&win);
- if (win == NULL) return;
-
- win->_flags |= RGFW_windowMinimize;
- RGFW_eventQueuePushEx(e.type = RGFW_windowRestored; e._win = win);
- RGFW_windowRestoredCallback(win, win->r);
-
-}
-void RGFW__osxWindowMiniaturize(id self, SEL sel) {
- RGFW_UNUSED(sel);
- RGFW_window* win = NULL;
- object_getInstanceVariable(self, "RGFW_window", (void**)&win);
- if (win == NULL) return;
-
- win->_flags &= ~(u32)RGFW_windowMinimize;
- RGFW_eventQueuePushEx(e.type = RGFW_windowMinimized; e._win = win);
- RGFW_windowMinimizedCallback(win, win->r);
-
-}
-
-void RGFW__osxWindowBecameKey(id self, SEL sel) {
- RGFW_UNUSED(sel);
- RGFW_window* win = NULL;
- object_getInstanceVariable(self, "RGFW_window", (void**)&win);
- if (win == NULL) return;
-
- win->_flags |= RGFW_windowFocus;
- RGFW_eventQueuePushEx(e.type = RGFW_focusIn; e._win = win);
-
- RGFW_focusCallback(win, RGFW_TRUE);
-
- if ((win->_flags & RGFW_HOLD_MOUSE)) RGFW_window_mouseHold(win, RGFW_AREA(win->r.w, win->r.h));
-}
-
-void RGFW__osxWindowResignKey(id self, SEL sel) {
- RGFW_UNUSED(sel);
- RGFW_window* win = NULL;
- object_getInstanceVariable(self, "RGFW_window", (void**)&win);
- if (win == NULL) return;
-
- RGFW_window_focusLost(win);
- RGFW_eventQueuePushEx(e.type = RGFW_focusOut; e._win = win);
- RGFW_focusCallback(win, RGFW_FALSE);
-}
-
-NSSize RGFW__osxWindowResize(id self, SEL sel, NSSize frameSize) {
- RGFW_UNUSED(sel);
-
- RGFW_window* win = NULL;
- object_getInstanceVariable(self, "RGFW_window", (void**)&win);
- if (win == NULL) return frameSize;
-
- win->r.w = (i32)frameSize.width;
- win->r.h = (i32)frameSize.height;
-
- RGFW_monitor mon = RGFW_window_getMonitor(win);
- if ((i32)mon.mode.area.w == win->r.w && (i32)mon.mode.area.h - 102 <= win->r.h) {
- win->_flags |= RGFW_windowMaximize;
- RGFW_eventQueuePushEx(e.type = RGFW_windowMaximized; e._win = win);
- RGFW_windowMaximizedCallback(win, win->r);
- } else if (win->_flags & RGFW_windowMaximize) {
- win->_flags &= ~(u32)RGFW_windowMaximize;
- RGFW_eventQueuePushEx(e.type = RGFW_windowRestored; e._win = win);
- RGFW_windowRestoredCallback(win, win->r);
-
- }
-
-
- RGFW_eventQueuePushEx(e.type = RGFW_windowResized; e._win = win);
- RGFW_windowResizedCallback(win, win->r);
- return frameSize;
-}
-
-void RGFW__osxWindowMove(id self, SEL sel) {
- RGFW_UNUSED(sel);
-
- RGFW_window* win = NULL;
- object_getInstanceVariable(self, "RGFW_window", (void**)&win);
- if (win == NULL) return;
-
- NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame"));
- win->r.x = (i32) frame.origin.x;
- win->r.y = (i32) frame.origin.y;
-
- RGFW_eventQueuePushEx(e.type = RGFW_windowMoved; e._win = win);
- RGFW_windowMovedCallback(win, win->r);
-}
-
-void RGFW__osxViewDidChangeBackingProperties(id self, SEL _cmd) {
- RGFW_UNUSED(_cmd);
- RGFW_window* win = NULL;
- object_getInstanceVariable(self, "RGFW_window", (void**)&win);
- if (win == NULL) return;
-
- RGFW_monitor mon = RGFW_window_getMonitor(win);
- RGFW_scaleUpdatedCallback(win, mon.scaleX, mon.scaleY);
- RGFW_eventQueuePushEx(e.type = RGFW_scaleUpdated; e.scaleX = mon.scaleX; e.scaleY = mon.scaleY ; e._win = win);
-}
-
-void RGFW__osxDrawRect(id self, SEL _cmd, CGRect rect) {
- RGFW_UNUSED(rect); RGFW_UNUSED(_cmd);
- RGFW_window* win = NULL;
- object_getInstanceVariable(self, "RGFW_window", (void**)&win);
- if (win == NULL) return;
-
- RGFW_eventQueuePushEx(e.type = RGFW_windowRefresh; e._win = win);
- RGFW_windowRefreshCallback(win);
-}
-
-void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area) {
- #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
- win->buffer = buffer;
- win->bufferSize = area;
- win->_flags |= RGFW_BUFFER_ALLOC;
- #ifdef RGFW_OSMESA
- win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL);
- OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, area.w, area.h);
- OSMesaPixelStore(OSMESA_Y_UP, 0);
- #endif
- #else
- RGFW_UNUSED(win); RGFW_UNUSED(buffer); RGFW_UNUSED(area); /*!< if buffer rendering is not being used */
- #endif
-}
-
-void RGFW_window_cocoaSetLayer(RGFW_window* win, void* layer) {
- objc_msgSend_void_id((id)win->src.view, sel_registerName("setLayer"), (id)layer);
-}
-
-void* RGFW_cocoaGetLayer(void) {
- return objc_msgSend_class((id)objc_getClass("CAMetalLayer"), (SEL)sel_registerName("layer"));
-}
-
-NSPasteboardType const NSPasteboardTypeURL = "public.url";
-NSPasteboardType const NSPasteboardTypeFileURL = "public.file-url";
-
-id RGFW__osx_generateViewClass(const char* subclass, RGFW_window* win) {
- Class customViewClass;
- customViewClass = objc_allocateClassPair(objc_getClass(subclass), "RGFWCustomView", 0);
-
- class_addIvar( customViewClass, "RGFW_window", sizeof(RGFW_window*), (u8)rint(log2(sizeof(RGFW_window*))), "L");
- class_addMethod(customViewClass, sel_registerName("drawRect:"), (IMP)RGFW__osxDrawRect, "v@:{CGRect=ffff}");
- class_addMethod(customViewClass, sel_registerName("viewDidChangeBackingProperties"), (IMP)RGFW__osxViewDidChangeBackingProperties, "");
-
- id customView = objc_msgSend_id(NSAlloc(customViewClass), sel_registerName("init"));
- object_setInstanceVariable(customView, "RGFW_window", win);
-
- return customView;
-}
-
-#ifndef RGFW_EGL
-void RGFW_window_initOpenGL(RGFW_window* win) {
-#ifdef RGFW_OPENGL
- void* attrs = RGFW_initFormatAttribs();
- void* format = NSOpenGLPixelFormat_initWithAttributes((uint32_t*)attrs);
-
- if (format == NULL) {
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to load pixel format for OpenGL");
- win->_flags |= RGFW_windowOpenglSoftware;
- void* subAttrs = RGFW_initFormatAttribs();
- format = NSOpenGLPixelFormat_initWithAttributes((uint32_t*)subAttrs);
-
- if (format == NULL)
- RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "and loading software rendering OpenGL failed");
- else
- RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, RGFW_DEBUG_CTX(win, 0), "Switching to software rendering");
- }
-
- /* the pixel format can be passed directly to opengl context creation to create a context
- this is because the format also includes information about the opengl version (which may be a bad thing) */
-
- win->src.view = (id) ((id(*)(id, SEL, NSRect, uint32_t*))objc_msgSend) (RGFW__osx_generateViewClass("NSOpenGLView", win),
- sel_registerName("initWithFrame:pixelFormat:"), (NSRect){{0, 0}, {win->r.w, win->r.h}}, (uint32_t*)format);
-
- objc_msgSend_void(win->src.view, sel_registerName("prepareOpenGL"));
- win->src.ctx = objc_msgSend_id(win->src.view, sel_registerName("openGLContext"));
-
- if (win->_flags & RGFW_windowTransparent) {
- i32 opacity = 0;
- #define NSOpenGLCPSurfaceOpacity 236
- NSOpenGLContext_setValues((id)win->src.ctx, &opacity, NSOpenGLCPSurfaceOpacity);
- }
-
- objc_msgSend_void(win->src.ctx, sel_registerName("makeCurrentContext"));
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context initalized");
-#else
- RGFW_UNUSED(win);
-#endif
-}
-
-void RGFW_window_freeOpenGL(RGFW_window* win) {
-#ifdef RGFW_OPENGL
- if (win->src.ctx == NULL) return;
- objc_msgSend_void(win->src.ctx, sel_registerName("release"));
- win->src.ctx = NULL;
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context freed");
-#else
- RGFW_UNUSED(win);
-#endif
-}
-#endif
-
-
-i32 RGFW_init(void) {
-#if defined(RGFW_C89) || defined(__cplusplus)
- if (_RGFW_init) return 0;
- _RGFW_init = RGFW_TRUE;
- _RGFW.root = NULL; _RGFW.current = NULL; _RGFW.windowCount = -1; _RGFW.eventLen = 0; _RGFW.eventIndex = 0;
-#endif
-
- /* 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", (void*)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((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
-
- ((void (*)(id, SEL, NSUInteger))objc_msgSend)
- (NSApp, sel_registerName("setActivationPolicy:"), NSApplicationActivationPolicyRegular);
-
- #ifndef RGFW_NO_IOKIT
- RGFW_osxInitIOKit();
- #endif
- }
-
- _RGFW.windowCount = 0;
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context initialized");
- return 0;
-}
-
-RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) {
- static u8 RGFW_loaded = 0;
- RGFW_window_basic_init(win, rect, flags);
-
- /* RR Create an autorelease pool */
- id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
- pool = objc_msgSend_id(pool, sel_registerName("init"));
-
- RGFW_window_setMouseDefault(win);
-
- NSRect windowRect;
- windowRect.origin.x = 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 (!(flags & RGFW_windowNoResize))
- macArgs |= NSWindowStyleMaskResizable;
- if (!(flags & RGFW_windowNoBorder))
- macArgs |= NSWindowStyleMaskTitled;
- {
- void* nsclass = objc_getClass("NSWindow");
- SEL func = sel_registerName("initWithContentRect:styleMask:backing:defer:");
-
- win->src.window = ((id(*)(id, SEL, NSRect, NSWindowStyleMask, NSBackingStoreType, bool))objc_msgSend)
- (NSAlloc(nsclass), func, windowRect, macArgs, macArgs, false);
- }
-
- id str = NSString_stringWithUTF8String(name);
- objc_msgSend_void_id((id)win->src.window, sel_registerName("setTitle:"), str);
-
- if ((flags & RGFW_windowNoInitAPI) == 0) {
- RGFW_window_initOpenGL(win);
- RGFW_window_initBuffer(win);
- }
-
- #ifdef RGFW_OPENGL
- else
- #endif
- {
- NSRect contentRect = (NSRect){{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);
- }
-
- void* contentView = NSWindow_contentView((id)win->src.window);
- objc_msgSend_void_bool(contentView, sel_registerName("setWantsLayer:"), true);
- objc_msgSend_int((id)win->src.view, sel_registerName("setLayerContentsPlacement:"), 4);
- objc_msgSend_void_id((id)win->src.window, sel_registerName("setContentView:"), win->src.view);
-
- if (flags & RGFW_windowTransparent) {
- objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), false);
-
- objc_msgSend_void_id((id)win->src.window, sel_registerName("setBackgroundColor:"),
- NSColor_colorWithSRGB(0, 0, 0, 0));
- }
-
- Class delegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "WindowDelegate", 0);
-
- class_addIvar(
- delegateClass, "RGFW_window",
- sizeof(RGFW_window*), (u8)rint(log2(sizeof(RGFW_window*))),
- "L"
- );
-
- 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, "");
- class_addMethod(delegateClass, sel_registerName("windowDidMiniaturize:"), (IMP) RGFW__osxWindowMiniaturize, "");
- class_addMethod(delegateClass, sel_registerName("windowDidDeminiaturize:"), (IMP) RGFW__osxWindowDeminiaturize, "");
- class_addMethod(delegateClass, sel_registerName("windowDidBecomeKey:"), (IMP) RGFW__osxWindowBecameKey, "");
- class_addMethod(delegateClass, sel_registerName("windowDidResignKey:"), (IMP) RGFW__osxWindowResignKey, "");
- class_addMethod(delegateClass, sel_registerName("draggingEntered:"), (IMP)draggingEntered, "l@:@");
- class_addMethod(delegateClass, sel_registerName("draggingUpdated:"), (IMP)draggingUpdated, "l@:@");
- class_addMethod(delegateClass, sel_registerName("draggingExited:"), (IMP)RGFW__osxDraggingEnded, "v@:@");
- class_addMethod(delegateClass, sel_registerName("draggingEnded:"), (IMP)RGFW__osxDraggingEnded, "v@:@");
- class_addMethod(delegateClass, sel_registerName("prepareForDragOperation:"), (IMP)prepareForDragOperation, "B@:@");
- class_addMethod(delegateClass, sel_registerName("performDragOperation:"), (IMP)performDragOperation, "B@:@");
-
- id delegate = objc_msgSend_id(NSAlloc(delegateClass), sel_registerName("init"));
-
- if (RGFW_COCOA_FRAME_NAME)
- objc_msgSend_ptr(win->src.view, sel_registerName("setFrameAutosaveName:"), RGFW_COCOA_FRAME_NAME);
-
- object_setInstanceVariable(delegate, "RGFW_window", win);
-
- objc_msgSend_void_id((id)win->src.window, sel_registerName("setDelegate:"), delegate);
-
- if (flags & RGFW_windowAllowDND) {
- win->_flags |= RGFW_windowAllowDND;
-
- NSPasteboardType types[] = {NSPasteboardTypeURL, NSPasteboardTypeFileURL, NSPasteboardTypeString};
- NSregisterForDraggedTypes((id)win->src.window, types, 3);
- }
-
- RGFW_window_setFlags(win, flags);
-
- /* Show the window */
- objc_msgSend_void_bool(NSApp, sel_registerName("activateIgnoringOtherApps:"), true);
- ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL);
- RGFW_window_show(win);
-
- if (!RGFW_loaded) {
- objc_msgSend_void(win->src.window, sel_registerName("makeMainWindow"));
-
- RGFW_loaded = 1;
- }
-
- objc_msgSend_void(win->src.window, sel_registerName("makeKeyWindow"));
-
- objc_msgSend_void(NSApp, sel_registerName("finishLaunching"));
- NSRetain(win->src.window);
- NSRetain(NSApp);
-
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new window was created");
- return win;
-}
-
-void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) {
- NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame"));
- NSRect content = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame"));
- float offset = 0;
-
- RGFW_setBit(&win->_flags, RGFW_windowNoBorder, !border);
- NSBackingStoreType storeType = NSWindowStyleMaskBorderless | NSWindowStyleMaskFullSizeContentView;
- if (border)
- storeType = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
- if (!(win->_flags & RGFW_windowNoResize)) {
- storeType |= NSWindowStyleMaskResizable;
- }
-
- ((void (*)(id, SEL, NSBackingStoreType))objc_msgSend)((id)win->src.window, sel_registerName("setStyleMask:"), storeType);
-
- if (!border) {
- id miniaturizeButton = objc_msgSend_int((id)win->src.window, sel_registerName("standardWindowButton:"), NSWindowMiniaturizeButton);
- id titleBarView = objc_msgSend_id(miniaturizeButton, sel_registerName("superview"));
- objc_msgSend_void_bool(titleBarView, sel_registerName("setHidden:"), true);
-
- offset = (float)(frame.size.height - content.size.height);
- }
-
- RGFW_window_resize(win, RGFW_AREA(win->r.w, win->r.h + offset));
- win->r.h -= (i32)offset;
-}
-
-RGFW_area RGFW_getScreenSize(void) {
- static CGDirectDisplayID display = 0;
-
- if (display == 0)
- display = CGMainDisplayID();
-
- return RGFW_AREA(CGDisplayPixelsWide(display), CGDisplayPixelsHigh(display));
-}
-
-RGFW_point RGFW_getGlobalMousePoint(void) {
- RGFW_ASSERT(_RGFW.root != NULL);
-
- CGEventRef e = CGEventCreate(NULL);
- CGPoint point = CGEventGetLocation(e);
- CFRelease(e);
-
- return RGFW_POINT((u32) point.x, (u32) point.y); /*!< the point is loaded during event checks */
-}
-
-typedef RGFW_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 = 29,
- NSEventTypeMagnify = 30,
- NSEventTypeSwipe = 31,
- NSEventTypeRotate = 18,
- NSEventTypeBeginGesture = 19,
- NSEventTypeEndGesture = 20,
-
- NSEventTypeSmartMagnify = 32,
- NSEventTypeQuickLook = 33,
-
- NSEventTypePressure = 34,
- NSEventTypeDirectTouch = 37,
-
- NSEventTypeChangeMode = 38,
-};
-
-typedef unsigned long long NSEventMask;
-
-typedef enum NSEventModifierFlags {
- NSEventModifierFlagCapsLock = 1 << 16,
- NSEventModifierFlagShift = 1 << 17,
- NSEventModifierFlagControl = 1 << 18,
- NSEventModifierFlagOption = 1 << 19,
- NSEventModifierFlagCommand = 1 << 20,
- NSEventModifierFlagNumericPad = 1 << 21
-} NSEventModifierFlags;
-
-void RGFW_stopCheckEvents(void) {
- id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
- eventPool = objc_msgSend_id(eventPool, sel_registerName("init"));
-
- id e = (id) ((id(*)(Class, SEL, NSEventType, NSPoint, NSEventModifierFlags, void*, NSInteger, void**, short, NSInteger, NSInteger))objc_msgSend)
- (objc_getClass("NSEvent"), sel_registerName("otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:"),
- NSEventTypeApplicationDefined, (NSPoint){0, 0}, (NSEventModifierFlags)0, NULL, (NSInteger)0, NULL, 0, 0, 0);
-
- ((void (*)(id, SEL, id, bool))objc_msgSend)
- (NSApp, sel_registerName("postEvent:atStart:"), e, 1);
-
- objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
-}
-
-void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) {
- RGFW_UNUSED(win);
-
- id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
- eventPool = objc_msgSend_id(eventPool, sel_registerName("init"));
-
- void* date = (void*) ((id(*)(Class, SEL, double))objc_msgSend)
- (objc_getClass("NSDate"), sel_registerName("dateWithTimeIntervalSinceNow:"), waitMS);
-
- SEL eventFunc = sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:");
- id e = (id) ((id(*)(id, SEL, NSEventMask, void*, id, bool))objc_msgSend)
- (NSApp, eventFunc,
- ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true);
-
- if (e) {
- ((void (*)(id, SEL, id, bool))objc_msgSend)
- (NSApp, sel_registerName("postEvent:atStart:"), e, 1);
- }
-
- objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
-}
-
-u8 RGFW_rgfwToKeyChar(u32 rgfw_keycode) {
- return (u8)rgfw_keycode; /* TODO */
-}
-
-RGFW_event* RGFW_window_checkEvent(RGFW_window* win) {
- if (win == NULL || ((win->_flags & RGFW_windowFreeOnClose) && (win->_flags & RGFW_EVENT_QUIT))) return NULL;
-
- objc_msgSend_void((id)win->src.mouse, sel_registerName("set"));
- RGFW_event* ev = RGFW_window_checkEventCore(win);
- if (ev) {
- ((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows"));
- return ev;
- }
-
- id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
- eventPool = objc_msgSend_id(eventPool, sel_registerName("init"));
-
- SEL eventFunc = sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:");
-
- void* date = NULL;
-
- id e = (id) ((id(*)(id, SEL, NSEventMask, void*, id, bool))objc_msgSend)
- (NSApp, eventFunc, ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true);
-
- if (e == NULL) {
- objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
- objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e);
- ((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows"));
- 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);
-
- objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e);
- objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
- ((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows"));
- 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;
-
- u32 type = (u32)objc_msgSend_uint(e, sel_registerName("type"));
- switch (type) {
- case NSEventTypeMouseEntered: {
- win->event.type = RGFW_mouseEnter;
- NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow"));
-
- win->event.point = RGFW_POINT((i32) p.x, (i32) (win->r.h - p.y));
- RGFW_mouseNotifyCallback(win, win->event.point, 1);
- break;
- }
-
- case NSEventTypeMouseExited:
- win->event.type = RGFW_mouseLeave;
- RGFW_mouseNotifyCallback(win, win->event.point, 0);
- break;
-
- case NSEventTypeKeyDown: {
- u32 key = (u16) objc_msgSend_uint(e, sel_registerName("keyCode"));
-
- u32 mappedKey = (u32)*(((char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("charactersIgnoringModifiers")))));
- if (((u8)mappedKey) == 239)
- mappedKey = 0;
-
- win->event.keyChar = (u8)mappedKey;
-
- win->event.key = (u8)RGFW_apiKeyToRGFW(key);
- RGFW_keyboard[win->event.key].prev = RGFW_keyboard[win->event.key].current;
-
- win->event.type = RGFW_keyPressed;
- win->event.repeat = RGFW_isPressed(win, win->event.key);
- RGFW_keyboard[win->event.key].current = 1;
-
- RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, 1);
- break;
- }
-
- case NSEventTypeKeyUp: {
- u32 key = (u16) objc_msgSend_uint(e, sel_registerName("keyCode"));
- u32 mappedKey = (u32)*(((char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("charactersIgnoringModifiers")))));
- if (((u8)mappedKey) == 239)
- mappedKey = 0;
-
- win->event.keyChar = (u8)mappedKey;
-
- win->event.key = (u8)RGFW_apiKeyToRGFW(key);
-
- RGFW_keyboard[win->event.key].prev = RGFW_keyboard[win->event.key].current;
-
- win->event.type = RGFW_keyReleased;
-
- RGFW_keyboard[win->event.key].current = 0;
- RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, 0);
- break;
- }
-
- case NSEventTypeFlagsChanged: {
- u32 flags = (u32)objc_msgSend_uint(e, sel_registerName("modifierFlags"));
- RGFW_updateKeyModsPro(win, ((u32)(flags & NSEventModifierFlagCapsLock) % 255), ((flags & NSEventModifierFlagNumericPad) % 255),
- ((flags & NSEventModifierFlagControl) % 255), ((flags & NSEventModifierFlagOption) % 255),
- ((flags & NSEventModifierFlagShift) % 255), ((flags & NSEventModifierFlagCommand) % 255), 0);
- u8 i;
- for (i = 0; i < 9; i++)
- RGFW_keyboard[i + RGFW_capsLock].prev = 0;
-
- for (i = 0; i < 5; i++) {
- u32 shift = (1 << (i + 16));
- u32 key = i + RGFW_capsLock;
-
- if ((flags & shift) && !RGFW_wasPressed(win, (u8)key)) {
- RGFW_keyboard[key].current = 1;
-
- if (key != RGFW_capsLock)
- RGFW_keyboard[key+ 4].current = 1;
-
- win->event.type = RGFW_keyPressed;
- win->event.key = (u8)key;
- break;
- }
-
- if (!(flags & shift) && RGFW_wasPressed(win, (u8)key)) {
- RGFW_keyboard[key].current = 0;
-
- if (key != RGFW_capsLock)
- RGFW_keyboard[key + 4].current = 0;
-
- win->event.type = RGFW_keyReleased;
- win->event.key = (u8)key;
- break;
- }
- }
-
- RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, win->event.type == RGFW_keyPressed);
-
- 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_POINT((u32) p.x, (u32) (win->r.h - p.y));
-
- p.x = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaX"));
- p.y = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaY"));
- win->event.vector = RGFW_POINT((i32)p.x, (i32)p.y);
-
- win->_lastMousePoint = win->event.point;
- RGFW_mousePosCallback(win, win->event.point, win->event.vector);
- break;
- }
- case NSEventTypeLeftMouseDown: case NSEventTypeRightMouseDown: case NSEventTypeOtherMouseDown: {
- u32 buttonNumber = (u32)objc_msgSend_uint(e, sel_registerName("buttonNumber"));
- switch (buttonNumber) {
- case 0: win->event.button = RGFW_mouseLeft; break;
- case 1: win->event.button = RGFW_mouseRight; break;
- case 2: win->event.button = RGFW_mouseMiddle; break;
- default: win->event.button = (u8)buttonNumber;
- }
-
- win->event.type = RGFW_mouseButtonPressed;
- RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
- RGFW_mouseButtons[win->event.button].current = 1;
- RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
- break;
- }
- case NSEventTypeLeftMouseUp: case NSEventTypeRightMouseUp: case NSEventTypeOtherMouseUp: {
- u32 buttonNumber = (u32)objc_msgSend_uint(e, sel_registerName("buttonNumber"));
- switch (buttonNumber) {
- case 0: win->event.button = RGFW_mouseLeft; break;
- case 1: win->event.button = RGFW_mouseRight; break;
- case 2: win->event.button = RGFW_mouseMiddle; break;
- default: win->event.button = (u8)buttonNumber;
- }
- RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
- RGFW_mouseButtons[win->event.button].current = 0;
- win->event.type = RGFW_mouseButtonReleased;
- RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
- 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;
- }
-
- RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
- RGFW_mouseButtons[win->event.button].current = 1;
-
- win->event.scroll = deltaY;
-
- win->event.type = RGFW_mouseButtonPressed;
- RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
- break;
- }
-
- default:
- objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e);
- ((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows"));
- return RGFW_window_checkEvent(win);
- }
-
- objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e);
- ((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows"));
- objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
- return &win->event;
-}
-
-
-void RGFW_window_move(RGFW_window* win, RGFW_point v) {
- RGFW_ASSERT(win != NULL);
-
- win->r.x = v.x;
- win->r.y = v.y;
- ((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend)
- ((id)win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{win->r.x, win->r.y}, {win->r.w, win->r.h}}, true, true);
-}
-
-void RGFW_window_resize(RGFW_window* win, RGFW_area a) {
- RGFW_ASSERT(win != NULL);
-
- NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame"));
- NSRect content = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame"));
- float offset = (float)(frame.size.height - content.size.height);
-
- win->r.w = (i32)a.w;
- win->r.h = (i32)a.h;
-
- ((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend)
- ((id)win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{win->r.x, win->r.y}, {win->r.w, win->r.h + offset}}, true, true);
-}
-
-void RGFW_window_focus(RGFW_window* win) {
- RGFW_ASSERT(win);
- objc_msgSend_void_bool(NSApp, sel_registerName("activateIgnoringOtherApps:"), true);
- ((void (*)(id, SEL))objc_msgSend)((id)win->src.window, sel_registerName("makeKeyWindow"));
-}
-
-void RGFW_window_raise(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
- ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("orderFront:"), (SEL)NULL);
- objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGNormalWindowLevelKey);
-}
-
-void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) {
- RGFW_ASSERT(win != NULL);
- if (fullscreen && (win->_flags & RGFW_windowFullscreen)) return;
- if (!fullscreen && !(win->_flags & RGFW_windowFullscreen)) return;
-
- if (fullscreen) {
- win->_oldRect = win->r;
- RGFW_monitor mon = RGFW_window_getMonitor(win);
- win->r = RGFW_RECT(0, 0, mon.x, mon.y);
- win->_flags |= RGFW_windowFullscreen;
- RGFW_window_resize(win, RGFW_AREA(mon.mode.area.w, mon.mode.area.h));
- RGFW_window_move(win, RGFW_POINT(0, 0));
- }
- objc_msgSend_void_SEL(win->src.window, sel_registerName("toggleFullScreen:"), NULL);
-
- if (!fullscreen) {
- win->r = win->_oldRect;
- win->_flags &= ~(u32)RGFW_windowFullscreen;
-
- RGFW_window_resize(win, RGFW_AREA(win->r.w, win->r.h));
- RGFW_window_move(win, RGFW_POINT(win->r.x, win->r.y));
- }
-}
-
-void RGFW_window_maximize(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
- if (RGFW_window_isMaximized(win)) return;
-
- win->_flags |= RGFW_windowMaximize;
- objc_msgSend_void_SEL(win->src.window, sel_registerName("zoom:"), NULL);
-}
-
-void RGFW_window_minimize(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
- objc_msgSend_void_SEL(win->src.window, sel_registerName("performMiniaturize:"), NULL);
-}
-
-void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) {
- RGFW_ASSERT(win != NULL);
- if (floating) objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGFloatingWindowLevelKey);
- else objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGNormalWindowLevelKey);
-}
-
-void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) {
- objc_msgSend_int(win->src.window, sel_registerName("setAlphaValue:"), opacity);
- objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), (opacity < (u8)255));
-
- if (opacity)
- objc_msgSend_void_id((id)win->src.window, sel_registerName("setBackgroundColor:"), NSColor_colorWithSRGB(0, 0, 0, opacity));
-
-}
-
-void RGFW_window_restore(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
-
- if (RGFW_window_isMaximized(win))
- objc_msgSend_void_SEL(win->src.window, sel_registerName("zoom:"), NULL);
-
- objc_msgSend_void_SEL(win->src.window, sel_registerName("deminiaturize:"), NULL);
- RGFW_window_show(win);
-}
-
-RGFW_bool RGFW_window_isFloating(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
- int level = ((int (*)(id, SEL))objc_msgSend) ((id)(win->src.window), (SEL)sel_registerName("level"));
- return level > kCGNormalWindowLevelKey;
-}
-
-void RGFW_window_setName(RGFW_window* win, const char* name) {
- RGFW_ASSERT(win != NULL);
-
- id str = NSString_stringWithUTF8String(name);
- objc_msgSend_void_id((id)win->src.window, sel_registerName("setTitle:"), str);
-}
-
-#ifndef RGFW_NO_PASSTHROUGH
-void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) {
- objc_msgSend_void_bool(win->src.window, sel_registerName("setIgnoresMouseEvents:"), passthrough);
-}
-#endif
-
-void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a) {
- if (a.w == 0 && a.h == 0) a = RGFW_AREA(1, 1);
-
- ((void (*)(id, SEL, NSSize))objc_msgSend)
- ((id)win->src.window, sel_registerName("setContentAspectRatio:"), (NSSize){a.w, a.h});
-}
-
-void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) {
- ((void (*)(id, SEL, NSSize))objc_msgSend)
- ((id)win->src.window, sel_registerName("setMinSize:"), (NSSize){a.w, a.h});
-}
-
-void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) {
- if (a.w == 0 && a.h == 0) {
- a = RGFW_getScreenSize();
- }
-
- ((void (*)(id, SEL, NSSize))objc_msgSend)
- ((id)win->src.window, sel_registerName("setMaxSize:"), (NSSize){a.w, a.h});
-}
-
-RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, RGFW_area area, i32 channels, u8 type) {
- RGFW_ASSERT(win != NULL);
- RGFW_UNUSED(type);
-
- if (data == NULL) {
- objc_msgSend_void_id(NSApp, sel_registerName("setApplicationIconImage:"), NULL);
- return RGFW_TRUE;
- }
-
- /* code by EimaMei: Make a bitmap representation, then copy the loaded image into it. */
- id representation = NSBitmapImageRep_initWithBitmapData(NULL, area.w, area.h, 8, channels, (channels == 4), false, "NSCalibratedRGBColorSpace", 1 << 1, area.w * (u32)channels, 8 * (u32)channels);
- RGFW_MEMCPY(NSBitmapImageRep_bitmapData(representation), data, area.w * area.h * (u32)channels);
-
- /* Add ze representation. */
- id dock_image = ((id(*)(id, SEL, NSSize))objc_msgSend) (NSAlloc((id)objc_getClass("NSImage")), sel_registerName("initWithSize:"), ((NSSize){area.w, area.h}));
-
- objc_msgSend_void_id(dock_image, sel_registerName("addRepresentation:"), representation);
-
- /* Finally, set the dock image to it. */
- objc_msgSend_void_id(NSApp, sel_registerName("setApplicationIconImage:"), dock_image);
- /* Free the garbage. */
- NSRelease(dock_image);
- NSRelease(representation);
-
- return RGFW_TRUE;
-}
-
-id NSCursor_arrowStr(const char* str) {
- void* nclass = objc_getClass("NSCursor");
- SEL func = sel_registerName(str);
- return (id) objc_msgSend_id(nclass, func);
-}
-
-RGFW_mouse* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels) {
- if (icon == NULL) {
- objc_msgSend_void(NSCursor_arrowStr("arrowCursor"), sel_registerName("set"));
- return NULL;
- }
-
- /* NOTE(EimaMei): Code by yours truly. */
- /* Make a bitmap representation, then copy the loaded image into it. */
- id representation = (id)NSBitmapImageRep_initWithBitmapData(NULL, a.w, a.h, 8, channels, (channels == 4), false, "NSCalibratedRGBColorSpace", 1 << 1, a.w * (u32)channels, 8 * (u32)channels);
- RGFW_MEMCPY(NSBitmapImageRep_bitmapData(representation), icon, a.w * a.h * (u32)channels);
-
- /* Add ze representation. */
- id cursor_image = ((id(*)(id, SEL, NSSize))objc_msgSend) (NSAlloc((id)objc_getClass("NSImage")), sel_registerName("initWithSize:"), ((NSSize){a.w, a.h}));
-
- objc_msgSend_void_id(cursor_image, sel_registerName("addRepresentation:"), representation);
-
- /* Finally, set the cursor image. */
- id cursor = (id) ((id(*)(id, SEL, id, NSPoint))objc_msgSend)
- (NSAlloc(objc_getClass("NSCursor")), sel_registerName("initWithImage:hotSpot:"), cursor_image, (NSPoint){0.0, 0.0});
-
- /* Free the garbage. */
- NSRelease(cursor_image);
- NSRelease(representation);
-
- return (void*)cursor;
-}
-
-void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) {
- RGFW_ASSERT(win != NULL); RGFW_ASSERT(mouse);
- CGDisplayShowCursor(kCGDirectMainDisplay);
- objc_msgSend_void((id)mouse, sel_registerName("set"));
- win->src.mouse = mouse;
-}
-
-void RGFW_freeMouse(RGFW_mouse* mouse) {
- RGFW_ASSERT(mouse);
- NSRelease((id)mouse);
-}
-
-RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) {
- return RGFW_window_setMouseStandard(win, RGFW_mouseArrow);
-}
-
-void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) {
- RGFW_window_showMouseFlags(win, show);
- if (show) CGDisplayShowCursor(kCGDirectMainDisplay);
- else CGDisplayHideCursor(kCGDirectMainDisplay);
-}
-
-RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 stdMouses) {
- static const char* mouseIconSrc[16] = {"arrowCursor", "arrowCursor", "IBeamCursor", "crosshairCursor", "pointingHandCursor", "resizeLeftRightCursor", "resizeUpDownCursor", "_windowResizeNorthWestSouthEastCursor", "_windowResizeNorthEastSouthWestCursor", "closedHandCursor", "operationNotAllowedCursor"};
- if (stdMouses > ((sizeof(mouseIconSrc)) / (sizeof(char*))))
- return RGFW_FALSE;
-
- const char* mouseStr = mouseIconSrc[stdMouses];
- id mouse = NSCursor_arrowStr(mouseStr);
-
- if (mouse == NULL)
- return RGFW_FALSE;
-
- RGFW_UNUSED(win);
- CGDisplayShowCursor(kCGDirectMainDisplay);
- objc_msgSend_void(mouse, sel_registerName("set"));
- win->src.mouse = mouse;
-
- return RGFW_TRUE;
-}
-
-void RGFW_releaseCursor(RGFW_window* win) {
- RGFW_UNUSED(win);
- CGAssociateMouseAndMouseCursorPosition(1);
-}
-
-void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) {
- RGFW_UNUSED(win);
-
- CGWarpMouseCursorPosition((CGPoint){r.x + (r.w / 2), r.y + (r.h / 2)});
- CGAssociateMouseAndMouseCursorPosition(0);
-}
-
-void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) {
- RGFW_UNUSED(win);
-
- win->_lastMousePoint = RGFW_POINT(v.x - win->r.x, v.y - win->r.y);
- CGWarpMouseCursorPosition((CGPoint){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) {
- if (win->_flags & RGFW_windowFocusOnShow)
- ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL);
-
- ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("orderFront:"), NULL);
- objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true);
-}
-
-RGFW_bool RGFW_window_isHidden(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
-
- bool visible = objc_msgSend_bool(win->src.window, sel_registerName("isVisible"));
- return visible == NO && !RGFW_window_isMinimized(win);
-}
-
-RGFW_bool RGFW_window_isMinimized(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
-
- return objc_msgSend_bool(win->src.window, sel_registerName("isMiniaturized")) == YES;
-}
-
-RGFW_bool RGFW_window_isMaximized(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
- RGFW_bool b = (RGFW_bool)objc_msgSend_bool(win->src.window, sel_registerName("isZoomed"));
- return b;
-}
-
-id RGFW_getNSScreenForDisplayID(CGDirectDisplayID display) {
- Class NSScreenClass = objc_getClass("NSScreen");
-
- id screens = objc_msgSend_id(NSScreenClass, sel_registerName("screens"));
-
- NSUInteger count = (NSUInteger)objc_msgSend_uint(screens, sel_registerName("count"));
- NSUInteger i;
- for (i = 0; i < count; i++) {
- id screen = ((id (*)(id, SEL, int))objc_msgSend) (screens, sel_registerName("objectAtIndex:"), (int)i);
- id description = objc_msgSend_id(screen, sel_registerName("deviceDescription"));
- id screenNumberKey = NSString_stringWithUTF8String("NSScreenNumber");
- id screenNumber = objc_msgSend_id_id(description, sel_registerName("objectForKey:"), screenNumberKey);
-
- if ((CGDirectDisplayID)objc_msgSend_uint(screenNumber, sel_registerName("unsignedIntValue")) == display) {
- return screen;
- }
- }
-
- return NULL;
-}
-
-u32 RGFW_osx_getFallbackRefreshRate(CGDirectDisplayID displayID);
-
-u32 RGFW_osx_getRefreshRate(CGDirectDisplayID display, CGDisplayModeRef mode) {
- if (mode) {
- u32 refreshRate = (u32)CGDisplayModeGetRefreshRate(mode);
- if (refreshRate != 0) return refreshRate;
- }
-
-#ifndef RGFW_NO_IOKIT
- u32 res = RGFW_osx_getFallbackRefreshRate(display);
- if (res != 0) return res;
-#else
- RGFW_UNUSED(display);
-#endif
- return 60;
-}
-
-RGFW_monitor RGFW_NSCreateMonitor(CGDirectDisplayID display, id screen) {
- RGFW_monitor monitor;
-
- const char name[] = "MacOS\0";
- RGFW_MEMCPY(monitor.name, name, 6);
-
- CGRect bounds = CGDisplayBounds(display);
- monitor.x = (i32)bounds.origin.x;
- monitor.y = (i32)bounds.origin.y;
- monitor.mode.area = RGFW_AREA((int) bounds.size.width, (int) bounds.size.height);
-
- monitor.mode.red = 8; monitor.mode.green = 8; monitor.mode.blue = 8;
-
- CGDisplayModeRef mode = CGDisplayCopyDisplayMode(display);
- monitor.mode.refreshRate = RGFW_osx_getRefreshRate(display, mode);
- CFRelease(mode);
-
- CGSize screenSizeMM = CGDisplayScreenSize(display);
- monitor.physW = (float)screenSizeMM.width / 25.4f;
- monitor.physH = (float)screenSizeMM.height / 25.4f;
-
- float ppi_width = (monitor.mode.area.w/monitor.physW);
- float ppi_height = (monitor.mode.area.h/monitor.physH);
-
- monitor.pixelRatio = (float)((CGFloat (*)(id, SEL))abi_objc_msgSend_fpret) (screen, sel_registerName("backingScaleFactor"));
- float dpi = 96.0f * monitor.pixelRatio;
-
- monitor.scaleX = ((i32)(((float) (ppi_width) / dpi) * 10.0f)) / 10.0f;
- monitor.scaleY = ((i32)(((float) (ppi_height) / dpi) * 10.0f)) / 10.0f;
-
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, RGFW_DEBUG_CTX_MON(monitor), "monitor found");
- return monitor;
-}
-
-
-RGFW_monitor* RGFW_getMonitors(size_t* len) {
- static CGDirectDisplayID displays[7];
- u32 count;
-
- if (CGGetActiveDisplayList(6, displays, &count) != kCGErrorSuccess)
- return NULL;
-
- if (count > 6) count = 6;
-
- static RGFW_monitor monitors[7];
-
- u32 i;
- for (i = 0; i < count; i++)
- monitors[i] = RGFW_NSCreateMonitor(displays[i], RGFW_getNSScreenForDisplayID(displays[i]));
-
- if (len != NULL) *len = count;
- return monitors;
-}
-
-RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) {
- CGPoint point = { mon.x, mon.y };
-
- CGDirectDisplayID display;
- uint32_t displayCount = 0;
- CGError err = CGGetDisplaysWithPoint(point, 1, &display, &displayCount);
- if (err != kCGErrorSuccess || displayCount != 1)
- return RGFW_FALSE;
-
- CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, NULL);
-
- if (allModes == NULL)
- return RGFW_FALSE;
-
- CFIndex i;
- for (i = 0; i < CFArrayGetCount(allModes); i++) {
- CGDisplayModeRef cmode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i);
-
- RGFW_monitorMode foundMode;
- foundMode.area = RGFW_AREA(CGDisplayModeGetWidth(cmode), CGDisplayModeGetHeight(cmode));
- foundMode.refreshRate = RGFW_osx_getRefreshRate(display, cmode);
- foundMode.red = 8; foundMode.green = 8; foundMode.blue = 8;
-
- if (RGFW_monitorModeCompare(mode, foundMode, request)) {
- if (CGDisplaySetDisplayMode(display, cmode, NULL) == kCGErrorSuccess) {
- CFRelease(allModes);
- return RGFW_TRUE;
- }
- break;
- }
- }
-
- CFRelease(allModes);
-
- return RGFW_FALSE;
-}
-
-RGFW_monitor RGFW_getPrimaryMonitor(void) {
- CGDirectDisplayID primary = CGMainDisplayID();
- return RGFW_NSCreateMonitor(primary, RGFW_getNSScreenForDisplayID(primary));
-}
-
-RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) {
- id screen = objc_msgSend_id(win->src.window, sel_registerName("screen"));
- id description = objc_msgSend_id(screen, sel_registerName("deviceDescription"));
- id screenNumberKey = NSString_stringWithUTF8String("NSScreenNumber");
- id screenNumber = objc_msgSend_id_id(description, sel_registerName("objectForKey:"), screenNumberKey);
-
- CGDirectDisplayID display = (CGDirectDisplayID)objc_msgSend_uint(screenNumber, sel_registerName("unsignedIntValue"));
-
- return RGFW_NSCreateMonitor(display, screen);
-}
-
-RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) {
- size_t clip_len;
- char* clip = (char*)NSPasteboard_stringForType(NSPasteboard_generalPasteboard(), NSPasteboardTypeString, &clip_len);
- if (clip == NULL) return -1;
-
- if (str != NULL) {
- if (strCapacity < clip_len)
- return 0;
-
- RGFW_MEMCPY(str, clip, clip_len);
-
- str[clip_len] = '\0';
- }
-
- return (RGFW_ssize_t)clip_len;
-}
-
-void RGFW_writeClipboard(const char* text, u32 textLen) {
- RGFW_UNUSED(textLen);
-
- NSPasteboardType array[] = { NSPasteboardTypeString, NULL };
- NSPasteBoard_declareTypes(NSPasteboard_generalPasteboard(), array, 1, NULL);
-
- SEL func = sel_registerName("setString:forType:");
- ((bool (*)(id, SEL, id, id))objc_msgSend)
- (NSPasteboard_generalPasteboard(), func, NSString_stringWithUTF8String(text), NSString_stringWithUTF8String(NSPasteboardTypeString));
-}
-
- #ifdef RGFW_OPENGL
- void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
- if (win != NULL)
- objc_msgSend_void(win->src.ctx, sel_registerName("makeCurrentContext"));
- else
- objc_msgSend_id(objc_getClass("NSOpenGLContext"), sel_registerName("clearCurrentContext"));
- }
- void* RGFW_getCurrent_OpenGL(void) {
- return objc_msgSend_id(objc_getClass("NSOpenGLContext"), sel_registerName("currentContext"));
- }
-
- void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) {
- objc_msgSend_void(win->src.ctx, sel_registerName("flushBuffer"));
- }
- #endif
-
- #if !defined(RGFW_EGL)
-
- void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) {
- RGFW_ASSERT(win != NULL);
- #if defined(RGFW_OPENGL)
-
- NSOpenGLContext_setValues((id)win->src.ctx, &swapInterval, 222);
- #else
- RGFW_UNUSED(swapInterval);
- #endif
- }
-
- #endif
-
-void RGFW_window_swapBuffers_software(RGFW_window* win) {
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
- RGFW_RGB_to_BGR(win, win->buffer);
- i32 channels = 4;
- id image = ((id (*)(Class, SEL))objc_msgSend)(objc_getClass("NSImage"), sel_getUid("alloc"));
- NSSize size = (NSSize){win->bufferSize.w, win->bufferSize.h};
- image = ((id (*)(id, SEL, NSSize))objc_msgSend)((id)image, sel_getUid("initWithSize:"), size);
-
- id rep = NSBitmapImageRep_initWithBitmapData(&win->buffer, win->r.w, win->r.h , 8, channels, (channels == 4), false,
- "NSDeviceRGBColorSpace", 1 << 1, (u32)win->bufferSize.w * (u32)channels, 8 * (u32)channels);
- ((void (*)(id, SEL, id))objc_msgSend)((id)image, sel_getUid("addRepresentation:"), rep);
-
- id contentView = ((id (*)(id, SEL))objc_msgSend)((id)win->src.window, sel_getUid("contentView"));
- ((void (*)(id, SEL, BOOL))objc_msgSend)(contentView, sel_getUid("setWantsLayer:"), YES);
- id layer = ((id (*)(id, SEL))objc_msgSend)(contentView, sel_getUid("layer"));
-
- ((void (*)(id, SEL, id))objc_msgSend)(layer, sel_getUid("setContents:"), (id)image);
- ((void (*)(id, SEL, BOOL))objc_msgSend)(contentView, sel_getUid("setNeedsDisplay:"), YES);
-
- NSRelease(rep);
- NSRelease(image);
-#else
- RGFW_UNUSED(win);
-#endif
-}
-
-void RGFW_deinit(void) {
- _RGFW.windowCount = -1;
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context deinitialized");
-}
-
-void RGFW_window_close(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
- NSRelease(win->src.view);
- if ((win->_flags & RGFW_windowNoInitAPI) == 0) RGFW_window_freeOpenGL(win);
-
- #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
- if ((win->_flags & RGFW_BUFFER_ALLOC))
- RGFW_FREE(win->buffer);
- #endif
-
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context deinitialized");
- _RGFW.windowCount--;
- if (_RGFW.windowCount == 0) RGFW_deinit();
-
- RGFW_clipboard_switch(NULL);
- RGFW_FREE(win->event.droppedFiles);
- if ((win->_flags & RGFW_WINDOW_ALLOC)) {
- RGFW_FREE(win);
- win = NULL;
- }
-}
-
-u64 RGFW_getTimerFreq(void) {
- static u64 freq = 0;
- if (freq == 0) {
- mach_timebase_info_data_t info;
- mach_timebase_info(&info);
- freq = (u64)((info.denom * 1e9) / info.numer);
- }
-
- return freq;
-}
-
-u64 RGFW_getTimerValue(void) { return (u64)mach_absolute_time(); }
-
-#endif /* RGFW_MACOS */
-
-/*
- End of MaOS defines
-*/
-
-/*
- WASM defines
-*/
-
-#ifdef RGFW_WASM
-EM_BOOL Emscripten_on_resize(int eventType, const EmscriptenUiEvent* E, void* userData) {
- RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
-
- RGFW_eventQueuePushEx(e.type = RGFW_windowResized; e._win = _RGFW.root);
- RGFW_windowResizedCallback(_RGFW.root, RGFW_RECT(0, 0, E->windowInnerWidth, E->windowInnerHeight));
- return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_fullscreenchange(int eventType, const EmscriptenFullscreenChangeEvent* E, void* userData) {
- RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
- static u8 fullscreen = RGFW_FALSE;
- static RGFW_rect ogRect;
-
- if (fullscreen == RGFW_FALSE) {
- ogRect = _RGFW.root->r;
- }
-
- fullscreen = !fullscreen;
- RGFW_eventQueuePushEx(e.type = RGFW_windowResized; e._win = _RGFW.root);
- _RGFW.root->r = RGFW_RECT(0, 0, E->screenWidth, E->screenHeight);
-
- EM_ASM("Module.canvas.focus();");
-
- if (fullscreen == RGFW_FALSE) {
- _RGFW.root->r = RGFW_RECT(0, 0, ogRect.w, ogRect.h);
- /* emscripten_request_fullscreen("#canvas", 0); */
- } else {
- #if __EMSCRIPTEN_major__ >= 1 && __EMSCRIPTEN_minor__ >= 29 && __EMSCRIPTEN_tiny__ >= 0
- EmscriptenFullscreenStrategy FSStrat = {0};
- FSStrat.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; /* EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT : EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; */
- FSStrat.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF;
- FSStrat.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
- emscripten_request_fullscreen_strategy("#canvas", 1, &FSStrat);
- #else
- emscripten_request_fullscreen("#canvas", 1);
- #endif
- }
-
- emscripten_set_canvas_element_size("#canvas", _RGFW.root->r.w, _RGFW.root->r.h);
-
- RGFW_windowResizedCallback(_RGFW.root, _RGFW.root->r);
- return EM_TRUE;
-}
-
-
-
-EM_BOOL Emscripten_on_focusin(int eventType, const EmscriptenFocusEvent* E, void* userData) {
- RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(E);
-
- RGFW_eventQueuePushEx(e.type = RGFW_focusIn; e._win = _RGFW.root);
- _RGFW.root->_flags |= RGFW_windowFocus;
- RGFW_focusCallback(_RGFW.root, 1);
-
- if ((_RGFW.root->_flags & RGFW_HOLD_MOUSE)) RGFW_window_mouseHold(_RGFW.root, RGFW_AREA(_RGFW.root->r.w, _RGFW.root->r.h));
- return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_focusout(int eventType, const EmscriptenFocusEvent* E, void* userData) {
- RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(E);
-
- RGFW_eventQueuePushEx(e.type = RGFW_focusOut; e._win = _RGFW.root);
- RGFW_window_focusLost(_RGFW.root);
- RGFW_focusCallback(_RGFW.root, 0);
- return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_mousemove(int eventType, const EmscriptenMouseEvent* E, void* userData) {
- RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
- RGFW_eventQueuePushEx(e.type = RGFW_mousePosChanged;
- e.point = RGFW_POINT(E->targetX, E->targetY);
- e.vector = RGFW_POINT(E->movementX, E->movementY);
- e._win = _RGFW.root);
-
- _RGFW.root->_lastMousePoint = RGFW_POINT(E->targetX, E->targetY);
- RGFW_mousePosCallback(_RGFW.root, RGFW_POINT(E->targetX, E->targetY), RGFW_POINT(E->movementX, E->movementY));
- return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_mousedown(int eventType, const EmscriptenMouseEvent* E, void* userData) {
- RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
-
- int button = E->button;
- if (button > 2)
- button += 2;
-
- RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonPressed;
- e.point = RGFW_POINT(E->targetX, E->targetY);
- e.vector = RGFW_POINT(E->movementX, E->movementY);
- e.button = (u8)button;
- e.scroll = 0;
- e._win = _RGFW.root);
- RGFW_mouseButtons[button].prev = RGFW_mouseButtons[button].current;
- RGFW_mouseButtons[button].current = 1;
-
- RGFW_mouseButtonCallback(_RGFW.root, button, 0, 1);
- return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_mouseup(int eventType, const EmscriptenMouseEvent* E, void* userData) {
- RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
-
- int button = E->button;
- if (button > 2)
- button += 2;
-
- RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonReleased;
- e.point = RGFW_POINT(E->targetX, E->targetY);
- e.vector = RGFW_POINT(E->movementX, E->movementY);
- e.button = (u8)button;
- e.scroll = 0;
- e._win = _RGFW.root);
- RGFW_mouseButtons[button].prev = RGFW_mouseButtons[button].current;
- RGFW_mouseButtons[button].current = 0;
-
- RGFW_mouseButtonCallback(_RGFW.root, button, 0, 0);
- return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_wheel(int eventType, const EmscriptenWheelEvent* E, void* userData) {
- RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
-
- int button = RGFW_mouseScrollUp + (E->deltaY < 0);
- RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonPressed;
- e.button = (u8)button;
- e.scroll = (double)(E->deltaY < 0 ? 1 : -1);
- e._win = _RGFW.root);
- RGFW_mouseButtons[button].prev = RGFW_mouseButtons[button].current;
- RGFW_mouseButtons[button].current = 1;
- RGFW_mouseButtonCallback(_RGFW.root, button, E->deltaY < 0 ? 1 : -1, 1);
-
- return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_touchstart(int eventType, const EmscriptenTouchEvent* E, void* userData) {
- RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
-
- size_t i;
- for (i = 0; i < (size_t)E->numTouches; i++) {
- RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonPressed;
- e.point = RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY);
- e.button = RGFW_mouseLeft;
- e._win = _RGFW.root);
-
- RGFW_mouseButtons[RGFW_mouseLeft].prev = RGFW_mouseButtons[RGFW_mouseLeft].current;
- RGFW_mouseButtons[RGFW_mouseLeft].current = 1;
-
- _RGFW.root->_lastMousePoint = RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY);
- RGFW_mousePosCallback(_RGFW.root, RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY), _RGFW.root->event.vector);
- RGFW_mouseButtonCallback(_RGFW.root, RGFW_mouseLeft, 0, 1);
- }
-
- return EM_TRUE;
-}
-EM_BOOL Emscripten_on_touchmove(int eventType, const EmscriptenTouchEvent* E, void* userData) {
- RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
-
- size_t i;
- for (i = 0; i < (size_t)E->numTouches; i++) {
- RGFW_eventQueuePushEx(e.type = RGFW_mousePosChanged;
- e.point = RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY);
- e.button = RGFW_mouseLeft;
- e._win = _RGFW.root);
-
- _RGFW.root->_lastMousePoint = RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY);
- RGFW_mousePosCallback(_RGFW.root, RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY), _RGFW.root->event.vector);
- }
- return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_touchend(int eventType, const EmscriptenTouchEvent* E, void* userData) {
- RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
-
- size_t i;
- for (i = 0; i < (size_t)E->numTouches; i++) {
- RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonReleased;
- e.point = RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY);
- e.button = RGFW_mouseLeft;
- e._win = _RGFW.root);
-
- RGFW_mouseButtons[RGFW_mouseLeft].prev = RGFW_mouseButtons[RGFW_mouseLeft].current;
- RGFW_mouseButtons[RGFW_mouseLeft].current = 0;
-
- _RGFW.root->_lastMousePoint = RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY);
- RGFW_mousePosCallback(_RGFW.root, RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY), _RGFW.root->event.vector);
- RGFW_mouseButtonCallback(_RGFW.root, RGFW_mouseLeft, 0, 0);
- }
- return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_touchcancel(int eventType, const EmscriptenTouchEvent* E, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); return EM_TRUE; }
-
-EM_BOOL Emscripten_on_gamepad(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) {
- RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
-
- if (gamepadEvent->index >= 4)
- return 0;
-
- size_t i = gamepadEvent->index;
- if (gamepadEvent->connected) {
- RGFW_STRNCPY(RGFW_gamepads_name[gamepadEvent->index], gamepadEvent->id, sizeof(RGFW_gamepads_name[gamepadEvent->index]) - 1);
- RGFW_gamepads_name[gamepadEvent->index][sizeof(RGFW_gamepads_name[gamepadEvent->index]) - 1] = '\0';
- RGFW_gamepads_type[i] = RGFW_gamepadUnknown;
- if (RGFW_STRSTR(RGFW_gamepads_name[i], "Microsoft") || RGFW_STRSTR(RGFW_gamepads_name[i], "X-Box"))
- RGFW_gamepads_type[i] = RGFW_gamepadMicrosoft;
- else if (RGFW_STRSTR(RGFW_gamepads_name[i], "PlayStation") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS3") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS4") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS5"))
- RGFW_gamepads_type[i] = RGFW_gamepadSony;
- else if (RGFW_STRSTR(RGFW_gamepads_name[i], "Nintendo"))
- RGFW_gamepads_type[i] = RGFW_gamepadNintendo;
- else if (RGFW_STRSTR(RGFW_gamepads_name[i], "Logitech"))
- RGFW_gamepads_type[i] = RGFW_gamepadLogitech;
- RGFW_gamepadCount++;
- } else {
- RGFW_gamepadCount--;
- }
-
- RGFW_eventQueuePushEx(e.type = (RGFW_eventType)(gamepadEvent->connected ? RGFW_gamepadConnected : RGFW_gamepadConnected);
- e.gamepad = (u16)gamepadEvent->index;
- e._win = _RGFW.root);
-
- RGFW_gamepadCallback(_RGFW.root, gamepadEvent->index, gamepadEvent->connected);
- RGFW_gamepads[gamepadEvent->index] = gamepadEvent->connected;
-
- return 1; /* The event was consumed by the callback handler */
-}
-
-u32 RGFW_wASMPhysicalToRGFW(u32 hash) {
- switch(hash) { /* 0x0000 */
- case 0x67243A2DU /* Escape */: return RGFW_escape; /* 0x0001 */
- case 0x67251058U /* Digit0 */: return RGFW_0; /* 0x0002 */
- case 0x67251059U /* Digit1 */: return RGFW_1; /* 0x0003 */
- case 0x6725105AU /* Digit2 */: return RGFW_2; /* 0x0004 */
- case 0x6725105BU /* Digit3 */: return RGFW_3; /* 0x0005 */
- case 0x6725105CU /* Digit4 */: return RGFW_4; /* 0x0006 */
- case 0x6725105DU /* Digit5 */: return RGFW_5; /* 0x0007 */
- case 0x6725105EU /* Digit6 */: return RGFW_6; /* 0x0008 */
- case 0x6725105FU /* Digit7 */: return RGFW_7; /* 0x0009 */
- case 0x67251050U /* Digit8 */: return RGFW_8; /* 0x000A */
- case 0x67251051U /* Digit9 */: return RGFW_9; /* 0x000B */
- case 0x92E14DD3U /* Minus */: return RGFW_minus; /* 0x000C */
- case 0x92E1FBACU /* Equal */: return RGFW_equals; /* 0x000D */
- case 0x36BF1CB5U /* Backspace */: return RGFW_backSpace; /* 0x000E */
- case 0x7B8E51E2U /* Tab */: return RGFW_tab; /* 0x000F */
- case 0x2C595B51U /* KeyQ */: return RGFW_q; /* 0x0010 */
- case 0x2C595B57U /* KeyW */: return RGFW_w; /* 0x0011 */
- case 0x2C595B45U /* KeyE */: return RGFW_e; /* 0x0012 */
- case 0x2C595B52U /* KeyR */: return RGFW_r; /* 0x0013 */
- case 0x2C595B54U /* KeyT */: return RGFW_t; /* 0x0014 */
- case 0x2C595B59U /* KeyY */: return RGFW_y; /* 0x0015 */
- case 0x2C595B55U /* KeyU */: return RGFW_u; /* 0x0016 */
- case 0x2C595B4FU /* KeyO */: return RGFW_o; /* 0x0018 */
- case 0x2C595B50U /* KeyP */: return RGFW_p; /* 0x0019 */
- case 0x45D8158CU /* BracketLeft */: return RGFW_closeBracket; /* 0x001A */
- case 0xDEEABF7CU /* BracketRight */: return RGFW_bracket; /* 0x001B */
- case 0x92E1C5D2U /* Enter */: return RGFW_return; /* 0x001C */
- case 0xE058958CU /* ControlLeft */: return RGFW_controlL; /* 0x001D */
- case 0x2C595B41U /* KeyA */: return RGFW_a; /* 0x001E */
- case 0x2C595B53U /* KeyS */: return RGFW_s; /* 0x001F */
- case 0x2C595B44U /* KeyD */: return RGFW_d; /* 0x0020 */
- case 0x2C595B46U /* KeyF */: return RGFW_f; /* 0x0021 */
- case 0x2C595B47U /* KeyG */: return RGFW_g; /* 0x0022 */
- case 0x2C595B48U /* KeyH */: return RGFW_h; /* 0x0023 */
- case 0x2C595B4AU /* KeyJ */: return RGFW_j; /* 0x0024 */
- case 0x2C595B4BU /* KeyK */: return RGFW_k; /* 0x0025 */
- case 0x2C595B4CU /* KeyL */: return RGFW_l; /* 0x0026 */
- case 0x2707219EU /* Semicolon */: return RGFW_semicolon; /* 0x0027 */
- case 0x92E0B58DU /* Quote */: return RGFW_apostrophe; /* 0x0028 */
- case 0x36BF358DU /* Backquote */: return RGFW_backtick; /* 0x0029 */
- case 0x26B1958CU /* ShiftLeft */: return RGFW_shiftL; /* 0x002A */
- case 0x36BF2438U /* Backslash */: return RGFW_backSlash; /* 0x002B */
- case 0x2C595B5AU /* KeyZ */: return RGFW_z; /* 0x002C */
- case 0x2C595B58U /* KeyX */: return RGFW_x; /* 0x002D */
- case 0x2C595B43U /* KeyC */: return RGFW_c; /* 0x002E */
- case 0x2C595B56U /* KeyV */: return RGFW_v; /* 0x002F */
- case 0x2C595B42U /* KeyB */: return RGFW_b; /* 0x0030 */
- case 0x2C595B4EU /* KeyN */: return RGFW_n; /* 0x0031 */
- case 0x2C595B4DU /* KeyM */: return RGFW_m; /* 0x0032 */
- case 0x92E1A1C1U /* Comma */: return RGFW_comma; /* 0x0033 */
- case 0x672FFAD4U /* Period */: return RGFW_period; /* 0x0034 */
- case 0x92E0A438U /* Slash */: return RGFW_slash; /* 0x0035 */
- case 0xC5A6BF7CU /* ShiftRight */: return RGFW_shiftR;
- case 0x5D64DA91U /* NumpadMultiply */: return RGFW_multiply;
- case 0xC914958CU /* AltLeft */: return RGFW_altL; /* 0x0038 */
- case 0x92E09CB5U /* Space */: return RGFW_space; /* 0x0039 */
- case 0xB8FAE73BU /* CapsLock */: return RGFW_capsLock; /* 0x003A */
- case 0x7174B789U /* F1 */: return RGFW_F1; /* 0x003B */
- case 0x7174B78AU /* F2 */: return RGFW_F2; /* 0x003C */
- case 0x7174B78BU /* F3 */: return RGFW_F3; /* 0x003D */
- case 0x7174B78CU /* F4 */: return RGFW_F4; /* 0x003E */
- case 0x7174B78DU /* F5 */: return RGFW_F5; /* 0x003F */
- case 0x7174B78EU /* F6 */: return RGFW_F6; /* 0x0040 */
- case 0x7174B78FU /* F7 */: return RGFW_F7; /* 0x0041 */
- case 0x7174B780U /* F8 */: return RGFW_F8; /* 0x0042 */
- case 0x7174B781U /* F9 */: return RGFW_F9; /* 0x0043 */
- case 0x7B8E57B0U /* F10 */: return RGFW_F10; /* 0x0044 */
- case 0xC925FCDFU /* Numpad7 */: return RGFW_multiply; /* 0x0047 */
- case 0xC925FCD0U /* Numpad8 */: return RGFW_KP_8; /* 0x0048 */
- case 0xC925FCD1U /* Numpad9 */: return RGFW_KP_9; /* 0x0049 */
- case 0x5EA3E8A4U /* NumpadSubtract */: return RGFW_minus; /* 0x004A */
- case 0xC925FCDCU /* Numpad4 */: return RGFW_KP_4; /* 0x004B */
- case 0xC925FCDDU /* Numpad5 */: return RGFW_KP_5; /* 0x004C */
- case 0xC925FCDEU /* Numpad6 */: return RGFW_KP_6; /* 0x004D */
- case 0xC925FCD9U /* Numpad1 */: return RGFW_KP_1; /* 0x004F */
- case 0xC925FCDAU /* Numpad2 */: return RGFW_KP_2; /* 0x0050 */
- case 0xC925FCDBU /* Numpad3 */: return RGFW_KP_3; /* 0x0051 */
- case 0xC925FCD8U /* Numpad0 */: return RGFW_KP_0; /* 0x0052 */
- case 0x95852DACU /* NumpadDecimal */: return RGFW_period; /* 0x0053 */
- case 0x7B8E57B1U /* F11 */: return RGFW_F11; /* 0x0057 */
- case 0x7B8E57B2U /* F12 */: return RGFW_F12; /* 0x0058 */
- case 0x7393FBACU /* NumpadEqual */: return RGFW_KP_Return;
- case 0xB88EBF7CU /* AltRight */: return RGFW_altR; /* 0xE038 */
- case 0xC925873BU /* NumLock */: return RGFW_numLock; /* 0xE045 */
- case 0x2C595F45U /* Home */: return RGFW_home; /* 0xE047 */
- case 0xC91BB690U /* ArrowUp */: return RGFW_up; /* 0xE048 */
- case 0x672F9210U /* PageUp */: return RGFW_pageUp; /* 0xE049 */
- case 0x3799258CU /* ArrowLeft */: return RGFW_left; /* 0xE04B */
- case 0x4CE33F7CU /* ArrowRight */: return RGFW_right; /* 0xE04D */
- case 0x7B8E55DCU /* End */: return RGFW_end; /* 0xE04F */
- case 0x3799379EU /* ArrowDown */: return RGFW_down; /* 0xE050 */
- case 0xBA90179EU /* PageDown */: return RGFW_pageDown; /* 0xE051 */
- case 0x6723CB2CU /* Insert */: return RGFW_insert; /* 0xE052 */
- case 0x6725C50DU /* Delete */: return RGFW_delete; /* 0xE053 */
- case 0x6723658CU /* OSLeft */: return RGFW_superL; /* 0xE05B */
- case 0x39643F7CU /* MetaRight */: return RGFW_superR; /* 0xE05C */
- }
-
- return 0;
-}
-
-void EMSCRIPTEN_KEEPALIVE RGFW_handleKeyEvent(char* key, char* code, RGFW_bool press) {
- const char* iCode = code;
-
- u32 hash = 0;
- while(*iCode) hash = ((hash ^ 0x7E057D79U) << 3) ^ (unsigned int)*iCode++;
-
- u32 physicalKey = RGFW_wASMPhysicalToRGFW(hash);
-
- u8 mappedKey = (u8)(*((u32*)key));
-
- if (*((u16*)key) != mappedKey) {
- mappedKey = 0;
- if (*((u32*)key) == *((u32*)"Tab")) mappedKey = RGFW_tab;
- }
-
- RGFW_eventQueuePushEx(e.type = (RGFW_eventType)(press ? RGFW_keyPressed : RGFW_keyReleased);
- e.key = (u8)physicalKey;
- e.keyChar = (u8)mappedKey;
- e.keyMod = _RGFW.root->event.keyMod;
- e._win = _RGFW.root);
-
- RGFW_keyboard[physicalKey].prev = RGFW_keyboard[physicalKey].current;
- RGFW_keyboard[physicalKey].current = press;
-
- RGFW_keyCallback(_RGFW.root, physicalKey, mappedKey, _RGFW.root->event.keyMod, press);
-}
-
-void EMSCRIPTEN_KEEPALIVE RGFW_handleKeyMods(RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll) {
- RGFW_updateKeyModsPro(_RGFW.root, capital, numlock, control, alt, shift, super, scroll);
-}
-
-void EMSCRIPTEN_KEEPALIVE Emscripten_onDrop(size_t count) {
- if (!(_RGFW.root->_flags & RGFW_windowAllowDND))
- return;
-
- _RGFW.root->event.droppedFilesCount = count;
- RGFW_eventQueuePushEx(e.type = RGFW_DND;
- e.droppedFilesCount = count;
- e._win = _RGFW.root);
- RGFW_dndCallback(_RGFW.root, _RGFW.root->event.droppedFiles, count);
-}
-
-RGFW_bool RGFW_stopCheckEvents_bool = RGFW_FALSE;
-void RGFW_stopCheckEvents(void) {
- RGFW_stopCheckEvents_bool = RGFW_TRUE;
-}
-
-void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) {
- RGFW_UNUSED(win);
- if (waitMS == 0) return;
-
- u32 start = (u32)(((u64)RGFW_getTimeNS()) / 1e+6);
-
- while ((_RGFW.eventLen == 0) && RGFW_stopCheckEvents_bool == RGFW_FALSE && (RGFW_getTimeNS() / 1e+6) - start < waitMS)
- emscripten_sleep(0);
-
- RGFW_stopCheckEvents_bool = RGFW_FALSE;
-}
-
-void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area){
- #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
- win->buffer = buffer;
- win->bufferSize = area;
- #ifdef RGFW_OSMESA
- win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL);
- OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, area.w, area.h);
- OSMesaPixelStore(OSMESA_Y_UP, 0);
- #endif
- #else
- RGFW_UNUSED(win); RGFW_UNUSED(buffer); RGFW_UNUSED(area); /*!< if buffer rendering is not being used */
- #endif
-}
-
-void EMSCRIPTEN_KEEPALIVE RGFW_makeSetValue(size_t index, char* file) {
- /* This seems like a terrible idea, don't replicate this unless you hate yourself or the OS */
- /* TODO: find a better way to do this
- */
- RGFW_STRNCPY((char*)_RGFW.root->event.droppedFiles[index], file, RGFW_MAX_PATH - 1);
- _RGFW.root->event.droppedFiles[index][RGFW_MAX_PATH - 1] = '\0';
-}
-
-#include
-#include
-#include
-#include
-
-void EMSCRIPTEN_KEEPALIVE RGFW_mkdir(char* name) { mkdir(name, 0755); }
-
-void EMSCRIPTEN_KEEPALIVE RGFW_writeFile(const char *path, const char *data, size_t len) {
- FILE* file = fopen(path, "w+");
- if (file == NULL)
- return;
-
- fwrite(data, sizeof(char), len, file);
- fclose(file);
-}
-
-void RGFW_window_initOpenGL(RGFW_window* win) {
-#if defined(RGFW_OPENGL) && !defined(RGFW_WEBGPU) && !defined(RGFW_OSMESA) && !defined(RGFW_BUFFER)
- EmscriptenWebGLContextAttributes attrs;
- attrs.alpha = RGFW_GL_HINTS[RGFW_glDepth];
- attrs.depth = RGFW_GL_HINTS[RGFW_glAlpha];
- attrs.stencil = RGFW_GL_HINTS[RGFW_glStencil];
- attrs.antialias = RGFW_GL_HINTS[RGFW_glSamples];
- attrs.premultipliedAlpha = EM_TRUE;
- attrs.preserveDrawingBuffer = EM_FALSE;
-
- if (RGFW_GL_HINTS[RGFW_glDoubleBuffer] == 0)
- attrs.renderViaOffscreenBackBuffer = 0;
- else
- attrs.renderViaOffscreenBackBuffer = RGFW_GL_HINTS[RGFW_glAuxBuffers];
-
- attrs.failIfMajorPerformanceCaveat = EM_FALSE;
- attrs.majorVersion = (RGFW_GL_HINTS[RGFW_glMajor] == 0) ? 1 : RGFW_GL_HINTS[RGFW_glMajor];
- attrs.minorVersion = RGFW_GL_HINTS[RGFW_glMinor];
-
- attrs.enableExtensionsByDefault = EM_TRUE;
- attrs.explicitSwapControl = EM_TRUE;
-
- emscripten_webgl_init_context_attributes(&attrs);
- win->src.ctx = emscripten_webgl_create_context("#canvas", &attrs);
- emscripten_webgl_make_context_current(win->src.ctx);
-
- #ifdef LEGACY_GL_EMULATION
- EM_ASM("Module.useWebGL = true; GLImmediate.init();");
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context initalized");
- #endif
- glViewport(0, 0, win->r.w, win->r.h);
-#endif
-}
-
-void RGFW_window_freeOpenGL(RGFW_window* win) {
-#if defined(RGFW_OPENGL) && !defined(RGFW_WEBGPU) && !defined(RGFW_OSMESA) && !defined(RGFW_OSMESA)
- if (win->src.ctx == 0) return;
- emscripten_webgl_destroy_context(win->src.ctx);
- win->src.ctx = 0;
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context freed");
-#elif defined(RGFW_OPENGL) && defined(RGFW_OSMESA)
- if(win->src.ctx == 0) return;
- OSMesaDestroyContext(win->src.ctx);
- win->src.ctx = 0;
-#else
- RGFW_UNUSED(win);
-#endif
-}
-
-i32 RGFW_init(void) {
-#if defined(RGFW_C89) || defined(__cplusplus)
- if (_RGFW_init) return 0;
- _RGFW_init = RGFW_TRUE;
- _RGFW.root = NULL; _RGFW.current = NULL; _RGFW.windowCount = -2; _RGFW.eventLen = 0; _RGFW.eventIndex = 0;
-#endif
-
- _RGFW.windowCount = 0;
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context initialized");
- return 0;
-}
-
-RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) {
- RGFW_window_basic_init(win, rect, flags);
- RGFW_window_initOpenGL(win);
-
- #if defined(RGFW_WEBGPU)
- win->src.ctx = wgpuCreateInstance(NULL);
- win->src.device = emscripten_webgpu_get_device();
- win->src.queue = wgpuDeviceGetQueue(win->src.device);
- #endif
-
- emscripten_set_canvas_element_size("#canvas", rect.w, rect.h);
- emscripten_set_window_title(name);
-
- /* load callbacks */
- emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_resize);
- emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, EM_FALSE, Emscripten_on_fullscreenchange);
- emscripten_set_mousemove_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mousemove);
- emscripten_set_touchstart_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchstart);
- emscripten_set_touchend_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchend);
- emscripten_set_touchmove_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchmove);
- emscripten_set_touchcancel_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchcancel);
- emscripten_set_mousedown_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mousedown);
- emscripten_set_mouseup_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mouseup);
- emscripten_set_wheel_callback("#canvas", NULL, EM_FALSE, Emscripten_on_wheel);
- emscripten_set_focusin_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_focusin);
- emscripten_set_focusout_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_focusout);
- emscripten_set_gamepadconnected_callback(NULL, 1, Emscripten_on_gamepad);
- emscripten_set_gamepaddisconnected_callback(NULL, 1, Emscripten_on_gamepad);
-
- if (flags & RGFW_windowAllowDND) {
- win->_flags |= RGFW_windowAllowDND;
- }
-
- EM_ASM({
- window.addEventListener("keydown",
- (event) => {
- var key = stringToNewUTF8(event.key); var code = stringToNewUTF8(event.code);
- Module._RGFW_handleKeyMods(event.getModifierState("CapsLock"), event.getModifierState("NumLock"), event.getModifierState("Control"), event.getModifierState("Alt"), event.getModifierState("Shift"), event.getModifierState("Meta"), event.getModifierState("ScrollLock"));
- Module._RGFW_handleKeyEvent(key, code, 1);
- _free(key); _free(code);
- },
- true);
- window.addEventListener("keyup",
- (event) => {
- var key = stringToNewUTF8(event.key); var code = stringToNewUTF8(event.code);
- Module._RGFW_handleKeyMods(event.getModifierState("CapsLock"), event.getModifierState("NumLock"), event.getModifierState("Control"), event.getModifierState("Alt"), event.getModifierState("Shift"), event.getModifierState("Meta"), event.getModifierState("ScrollLock"));
- Module._RGFW_handleKeyEvent(key, code, 0);
- _free(key); _free(code);
- },
- true);
- });
-
- EM_ASM({
- var canvas = document.getElementById('canvas');
- canvas.addEventListener('drop', function(e) {
- e.preventDefault();
- if (e.dataTransfer.file < 0)
- return;
-
- var filenamesArray = [];
- var count = e.dataTransfer.files.length;
-
- /* Read and save the files to emscripten's files */
- var drop_dir = '.rgfw_dropped_files';
- Module._RGFW_mkdir(drop_dir);
-
- for (var i = 0; i < count; i++) {
- var file = e.dataTransfer.files[i];
-
- var path = '/' + drop_dir + '/' + file.name.replace("//", '_');
- var reader = new FileReader();
-
- reader.onloadend = (e) => {
- if (reader.readyState != 2) {
- out('failed to read dropped file: '+file.name+': '+reader.error);
- }
- else {
- var data = e.target.result;
-
- _RGFW_writeFile(path, new Uint8Array(data), file.size);
- }
- };
-
- reader.readAsArrayBuffer(file);
- /* This works weird on modern opengl */
- var filename = stringToNewUTF8(path);
-
- filenamesArray.push(filename);
-
- Module._RGFW_makeSetValue(i, filename);
- }
-
- Module._Emscripten_onDrop(count);
-
- for (var i = 0; i < count; ++i) {
- _free(filenamesArray[i]);
- }
- }, true);
-
- canvas.addEventListener('dragover', function(e) { e.preventDefault(); return false; }, true);
- });
-
- RGFW_window_setFlags(win, flags);
-
- if ((flags & RGFW_windowNoInitAPI) == 0) {
- RGFW_window_initBuffer(win);
- }
-
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new window was created");
- return win;
-}
-
-u8 RGFW_rgfwToKeyChar(u32 rgfw_keycode) {
- return (u8)rgfw_keycode; /* TODO */
-}
-
-RGFW_event* RGFW_window_checkEvent(RGFW_window* win) {
- if (win == NULL || ((win->_flags & RGFW_windowFreeOnClose) && (win->_flags & RGFW_EVENT_QUIT))) return NULL;
- RGFW_event* ev = RGFW_window_checkEventCore(win);
- if (ev) return ev;
-
- emscripten_sample_gamepad_data();
- /* check gamepads */
- int i;
- for (i = 0; (i < emscripten_get_num_gamepads()) && (i < 4); i++) {
- if (RGFW_gamepads[i] == 0)
- continue;
- EmscriptenGamepadEvent gamepadState;
-
- if (emscripten_get_gamepad_status(i, &gamepadState) != EMSCRIPTEN_RESULT_SUCCESS)
- break;
-
- /* Register buttons data for every connected gamepad */
- int j;
- for (j = 0; (j < gamepadState.numButtons) && (j < 16); j++) {
- u32 map[] = {
- RGFW_gamepadA, RGFW_gamepadB, RGFW_gamepadX, RGFW_gamepadY,
- RGFW_gamepadL1, RGFW_gamepadR1, RGFW_gamepadL2, RGFW_gamepadR2,
- RGFW_gamepadSelect, RGFW_gamepadStart,
- RGFW_gamepadL3, RGFW_gamepadR3,
- RGFW_gamepadUp, RGFW_gamepadDown, RGFW_gamepadLeft, RGFW_gamepadRight, RGFW_gamepadHome
- };
-
-
- u32 button = map[j];
- if (button == 404)
- continue;
-
- if (RGFW_gamepadPressed[i][button].current != gamepadState.digitalButton[j]) {
- if (gamepadState.digitalButton[j])
- win->event.type = RGFW_gamepadButtonPressed;
- else
- win->event.type = RGFW_gamepadButtonReleased;
-
- win->event.gamepad = i;
- win->event.button = map[j];
-
- RGFW_gamepadPressed[i][button].prev = RGFW_gamepadPressed[i][button].current;
- RGFW_gamepadPressed[i][button].current = gamepadState.digitalButton[j];
-
- RGFW_gamepadButtonCallback(win, win->event.gamepad, win->event.button, gamepadState.digitalButton[j]);
- return &win->event;
- }
- }
-
- for (j = 0; (j < gamepadState.numAxes) && (j < 4); j += 2) {
- win->event.axisesCount = gamepadState.numAxes / 2;
- if (RGFW_gamepadAxes[i][(size_t)(j / 2)].x != (i8)(gamepadState.axis[j] * 100.0f) ||
- RGFW_gamepadAxes[i][(size_t)(j / 2)].y != (i8)(gamepadState.axis[j + 1] * 100.0f)
- ) {
-
- RGFW_gamepadAxes[i][(size_t)(j / 2)].x = (i8)(gamepadState.axis[j] * 100.0f);
- RGFW_gamepadAxes[i][(size_t)(j / 2)].y = (i8)(gamepadState.axis[j + 1] * 100.0f);
- win->event.axis[(size_t)(j / 2)] = RGFW_gamepadAxes[i][(size_t)(j / 2)];
-
- win->event.type = RGFW_gamepadAxisMove;
- win->event.gamepad = i;
- win->event.whichAxis = j / 2;
-
- RGFW_gamepadAxisCallback(win, win->event.gamepad, win->event.axis, win->event.axisesCount, win->event.whichAxis);
- return &win->event;
- }
- }
- }
-
- return NULL;
-}
-
-void RGFW_window_resize(RGFW_window* win, RGFW_area a) {
- RGFW_UNUSED(win);
- emscripten_set_canvas_element_size("#canvas", a.w, a.h);
-}
-
-/* NOTE: I don't know if this is possible */
-void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) { RGFW_UNUSED(win); RGFW_UNUSED(v); }
-/* this one might be possible but it looks iffy */
-RGFW_mouse* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels) { RGFW_UNUSED(channels); RGFW_UNUSED(a); RGFW_UNUSED(icon); return NULL; }
-
-void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) { RGFW_UNUSED(win); RGFW_UNUSED(mouse); }
-void RGFW_freeMouse(RGFW_mouse* mouse) { RGFW_UNUSED(mouse); }
-
-RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) {
- static const char cursors[16][16] = {
- "default", "default", "text", "crosshair",
- "pointer", "ew-resize", "ns-resize", "nwse-resize", "nesw-resize",
- "move", "not-allowed"
- };
-
- RGFW_UNUSED(win);
- EM_ASM( { document.getElementById("canvas").style.cursor = UTF8ToString($0); }, cursors[mouse]);
- return RGFW_TRUE;
-}
-
-RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) {
- return RGFW_window_setMouseStandard(win, RGFW_mouseNormal);
-}
-
-void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) {
- RGFW_window_showMouseFlags(win, show);
- if (show)
- RGFW_window_setMouseDefault(win);
- else
- EM_ASM(document.getElementById('canvas').style.cursor = 'none';);
-}
-
-RGFW_point RGFW_getGlobalMousePoint(void) {
- RGFW_point point;
- point.x = EM_ASM_INT({
- return window.mouseX || 0;
- });
- point.y = EM_ASM_INT({
- return window.mouseY || 0;
- });
- return point;
-}
-
-void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) {
- RGFW_UNUSED(win);
-
- EM_ASM_({
- var canvas = document.getElementById('canvas');
- if ($0) {
- canvas.style.pointerEvents = 'none';
- } else {
- canvas.style.pointerEvents = 'auto';
- }
- }, passthrough);
-}
-
-void RGFW_writeClipboard(const char* text, u32 textLen) {
- RGFW_UNUSED(textLen);
- EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, text);
-}
-
-
-RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) {
- RGFW_UNUSED(str); RGFW_UNUSED(strCapacity);
- /*
- placeholder code for later
- I'm not sure if this is possible do the the async stuff
- */
- return 0;
-}
-
-void RGFW_window_swapBuffers_software(RGFW_window* win) {
-#if defined(RGFW_OSMESA)
- EM_ASM_({
- var data = Module.HEAPU8.slice($0, $0 + $1 * $2 * 4);
- let context = document.getElementById("canvas").getContext("2d");
- let image = context.getImageData(0, 0, $1, $2);
- image.data.set(data);
- context.putImageData(image, 0, $4 - $2);
- }, win->buffer, win->bufferSize.w, win->bufferSize.h, win->r.w, win->r.h);
-#elif defined(RGFW_BUFFER)
- EM_ASM_({
- var data = Module.HEAPU8.slice($0, $0 + $1 * $2 * 4);
- let context = document.getElementById("canvas").getContext("2d");
- let image = context.getImageData(0, 0, $1, $2);
- image.data.set(data);
- context.putImageData(image, 0, 0);
- }, win->buffer, win->bufferSize.w, win->bufferSize.h, win->r.w, win->r.h);
- emscripten_sleep(0);
-#else
- RGFW_UNUSED(win);
-#endif
-}
-
-void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
-#if !defined(RGFW_WEBGPU) && !(defined(RGFW_OSMESA) || defined(RGFW_BUFFER))
- if (win == NULL)
- emscripten_webgl_make_context_current(0);
- else
- emscripten_webgl_make_context_current(win->src.ctx);
-#endif
-}
-
-
-void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) {
-#ifndef RGFW_WEBGPU
- emscripten_webgl_commit_frame();
-
-#endif
- emscripten_sleep(0);
-}
-
-#ifndef RGFW_WEBGPU
-void* RGFW_getCurrent_OpenGL(void) { return (void*)emscripten_webgl_get_current_context(); }
-#endif
-
-#ifndef RGFW_EGL
-void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { RGFW_UNUSED(win); RGFW_UNUSED(swapInterval); }
-#endif
-
-void RGFW_deinit(void) { _RGFW.windowCount = -1; RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context deinitialized"); }
-
-void RGFW_window_close(RGFW_window* win) {
- if ((win->_flags & RGFW_windowNoInitAPI) == 0) RGFW_window_freeOpenGL(win);
-
- #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
- if ((win->_flags & RGFW_BUFFER_ALLOC))
- RGFW_FREE(win->buffer);
- #endif
-
- RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a window was freed");
- _RGFW.windowCount--;
- if (_RGFW.windowCount == 0) RGFW_deinit();
-
- RGFW_clipboard_switch(NULL);
- RGFW_FREE(win->event.droppedFiles);
- if ((win->_flags & RGFW_WINDOW_ALLOC)) {
- RGFW_FREE(win);
- win = NULL;
- }
-}
-
-int RGFW_innerWidth(void) { return EM_ASM_INT({ return window.innerWidth; }); }
-int RGFW_innerHeight(void) { return EM_ASM_INT({ return window.innerHeight; }); }
-
-RGFW_area RGFW_getScreenSize(void) {
- return RGFW_AREA(RGFW_innerWidth(), RGFW_innerHeight());
-}
-
-RGFW_bool RGFW_extensionSupportedPlatform(const char* extension, size_t len) {
-#ifdef RGFW_OPENGL
- return EM_ASM_INT({
- var ext = UTF8ToString($0, $1);
- var canvas = document.querySelector('canvas');
- var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
- if (!gl) return 0;
-
- var supported = gl.getSupportedExtensions();
- return supported && supported.includes(ext) ? 1 : 0;
- }, extension, len);
-#else
- return RGFW_FALSE;
-#endif
-}
-
-RGFW_proc RGFW_getProcAddress(const char* procname) {
-#ifdef RGFW_OPENGL
- return (RGFW_proc)emscripten_webgl_get_proc_address(procname);
-#else
- return NULL
-#endif
-}
-
-void RGFW_sleep(u64 milisecond) {
- emscripten_sleep(milisecond);
-}
-
-u64 RGFW_getTimerFreq(void) { return (u64)1000; }
-u64 RGFW_getTimerValue(void) { return emscripten_get_now() * 1e+6; }
-
-void RGFW_releaseCursor(RGFW_window* win) {
- RGFW_UNUSED(win);
- emscripten_exit_pointerlock();
-}
-
-void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) {
- RGFW_UNUSED(win); RGFW_UNUSED(r);
-
- emscripten_request_pointerlock("#canvas", 1);
-}
-
-
-void RGFW_window_setName(RGFW_window* win, const char* name) {
- RGFW_UNUSED(win);
- emscripten_set_window_title(name);
-}
-
-void RGFW_window_maximize(RGFW_window* win) {
- RGFW_ASSERT(win != NULL);
-
- RGFW_area screen = RGFW_getScreenSize();
- RGFW_window_move(win, RGFW_POINT(0, 0));
- RGFW_window_resize(win, screen);
-}
-
-void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) {
- RGFW_ASSERT(win != NULL);
- if (fullscreen) {
- win->_flags |= RGFW_windowFullscreen;
- EM_ASM( Module.requestFullscreen(false, true); );
- return;
- }
- win->_flags &= ~(u32)RGFW_windowFullscreen;
- EM_ASM( Module.exitFullscreen(false, true); );
-}
-
-void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) {
- RGFW_UNUSED(win);
- EM_ASM({
- var element = document.getElementById("canvas");
- if (element)
- element.style.opacity = $1;
- }, "elementId", opacity);
-}
-
-/* unsupported functions */
-void RGFW_window_focus(RGFW_window* win) { RGFW_UNUSED(win); }
-void RGFW_window_raise(RGFW_window* win) { RGFW_UNUSED(win); }
-RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) { RGFW_UNUSED(mon); RGFW_UNUSED(mode); RGFW_UNUSED(request); return RGFW_FALSE; }
-RGFW_monitor* RGFW_getMonitors(size_t* len) { RGFW_UNUSED(len); return NULL; }
-RGFW_monitor RGFW_getPrimaryMonitor(void) { return (RGFW_monitor){}; }
-void RGFW_window_move(RGFW_window* win, RGFW_point v) { RGFW_UNUSED(win); RGFW_UNUSED(v); }
-void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a) { RGFW_UNUSED(win); RGFW_UNUSED(a); }
-void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { RGFW_UNUSED(win); RGFW_UNUSED(a); }
-void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { RGFW_UNUSED(win); RGFW_UNUSED(a); }
-void RGFW_window_minimize(RGFW_window* win) { RGFW_UNUSED(win); }
-void RGFW_window_restore(RGFW_window* win) { RGFW_UNUSED(win); }
-void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) { RGFW_UNUSED(win); RGFW_UNUSED(floating); }
-void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { RGFW_UNUSED(win); RGFW_UNUSED(border); }
-RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* icon, RGFW_area a, i32 channels, u8 type) { RGFW_UNUSED(win); RGFW_UNUSED(icon); RGFW_UNUSED(a); RGFW_UNUSED(channels); RGFW_UNUSED(type); return RGFW_FALSE; }
-void RGFW_window_hide(RGFW_window* win) { RGFW_UNUSED(win); }
-void RGFW_window_show(RGFW_window* win) {RGFW_UNUSED(win); }
-RGFW_bool RGFW_window_isHidden(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; }
-RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; }
-RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; }
-RGFW_bool RGFW_window_isFloating(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; }
-RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { RGFW_UNUSED(win); return (RGFW_monitor){}; }
-#endif
-
-/* end of web asm defines */
-
-/* unix (macOS, linux, web asm) only stuff */
-#if defined(RGFW_X11) || defined(RGFW_MACOS) || defined(RGFW_WASM) || defined(RGFW_WAYLAND)
-#ifndef RGFW_NO_THREADS
-#include
-
-RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args) {
- RGFW_thread t;
- pthread_create((pthread_t*) &t, NULL, *ptr, args);
- 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); }
-
-#if defined(__linux__)
-void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { pthread_setschedprio((pthread_t)thread, priority); }
-#else
-void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { RGFW_UNUSED(thread); RGFW_UNUSED(priority); }
-#endif
-#endif
-
-#ifndef RGFW_WASM
-void RGFW_sleep(u64 ms) {
- struct timespec time;
- time.tv_sec = 0;
- time.tv_nsec = (long int)((double)ms * 1e+6);
-
- #ifndef RGFW_NO_UNIX_CLOCK
- nanosleep(&time, NULL);
- #endif
-}
-#endif
-
-#endif /* end of unix / mac stuff */
-#endif /* RGFW_IMPLEMENTATION */
-
-#if defined(__cplusplus) && !defined(__EMSCRIPTEN__)
-}
-#endif
-
-#if _MSC_VER
- #pragma warning( pop )
-#endif
diff --git a/src/external/RGFW/RGFW.h b/src/external/RGFW/RGFW.h
new file mode 100644
index 000000000..427db0168
--- /dev/null
+++ b/src/external/RGFW/RGFW.h
@@ -0,0 +1,15891 @@
+/*
+*
+* RGFW 2.0.0-dev
+
+* Copyright (C) 2022-26 Riley Mabb (@ColleagueRiley)
+*
+* libpng license
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked as such, and must not be
+* misrepresented as being the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*
+*
+*/
+
+/*
+ (MAKE SURE RGFW_IMPLEMENTATION is in exactly one header or you use -D RGFW_IMPLEMENTATION)
+ #define RGFW_IMPLEMENTATION - makes it so source code is included with header
+*/
+
+/*
+ #define RGFW_IMPLEMENTATION - (required) makes it so the source code is included
+ #define RGFW_DEBUG - (optional) makes it so RGFW prints debug messages and errors when they're found
+ #define RGFW_EGL - (optional) compile with OpenGL functions, allowing you to use to use EGL instead of the native OpenGL functions
+ #define RGFW_DIRECTX - (optional) include integration directX functions (windows only)
+ #define RGFW_VULKAN - (optional) include helpful vulkan integration functions and macros
+ #define RGFW_WEBGPU - (optional) use WebGPU for rendering
+ #define RGFW_NATIVE - (optional) define native RGFW types that use native API structures
+
+ #define RGFW_X11 (optional) (unix only) if X11 should be used. This option is turned on by default by unix systems except for MacOS
+ #define RGFW_WAYLAND (optional) (unix only) use Wayland. (This can be used with X11)
+ #define RGFW_NO_X11 (optional) (unix only) don't fallback to X11 when using Wayland
+ #define RGFW_NO_LOAD_WGL (optional) (windows only) if WGL should be loaded dynamically during runtime
+ #define RGFW_NO_X11_CURSOR (optional) (unix only) don't use XCursor
+ #define RGFW_NO_X11_CURSOR_PRELOAD (optional) (unix only) use XCursor, but don't link it in code, (you'll have to link it with -lXcursor)
+ #define RGFW_NO_X11_EXT_PRELOAD (optional) (unix only) use Xext, but don't link it in code, (you'll have to link it with -lXext)
+ #define RGFW_NO_LOAD_WINMM (optional) (windows only) use winmm (timeBeginPeriod), but don't link it in code, (you'll have to link it with -lwinmm)
+ #define RGFW_NO_WINMM (optional) (windows only) don't use winmm
+ #define RGFW_NO_IOKIT (optional) (macOS) don't use IOKit
+ #define RGFW_NO_UNIX_CLOCK (optional) (unix) don't link unix clock functions
+ #define RGFW_NO_DWM (windows only) - do not use or link dwmapi
+ #define RGFW_USE_XDL (optional) (X11) if XDL (XLib Dynamic Loader) should be used to load X11 dynamically during runtime (must include XDL.h along with RGFW)
+ #define RGFW_COCOA_GRAPHICS_SWITCHING - (optional) (cocoa) use automatic graphics switching (allow the system to choose to use GPU or iGPU)
+ #define RGFW_COCOA_FRAME_NAME (optional) (cocoa) set frame name
+ #define RGFW_NO_DPI - do not calculate DPI and don't use libShcore (win32)
+ #define RGFW_NO_XRANDR - do use XRandr (X11)
+ #define RGFW_ADVANCED_SMOOTH_RESIZE - use advanced methods for smooth resizing (may result in a spike in memory usage or worse performance) (eg. WM_TIMER and XSyncValue)
+ #define RGFW_NO_INFO - do not define the RGFW_info struct (without RGFW_IMPLEMENTATION)
+ #define RGFW_NO_GLXWINDOW - do not use GLXWindow
+
+ #define RGFW_ALLOC x - choose the default allocation function (defaults to standard malloc)
+ #define RGFW_FREE x - choose the default deallocation function (defaults to standard free)
+ #define RGFW_USERPTR x - choose the default userptr sent to the malloc call, (NULL by default)
+
+ #define RGFW_EXPORT - use when building RGFW
+ #define RGFW_IMPORT - use when linking with RGFW (not as a single-header)
+
+ #define RGFW_USE_INT - force the use c-types rather than stdint.h (for systems that might not have stdint.h (msvc))
+ #define RGFW_bool x - choose what type to use for bool, by default u32 is used
+*/
+
+/*
+Example to get you started :
+
+linux : gcc main.c -lX11 -lXrandr -lGL
+windows : gcc main.c -lopengl32 -lgdi32
+macos : gcc main.c -framework Cocoa -framework CoreVideo -framework OpenGL -framework IOKit
+
+#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", 100, 100, 500, 500, (u64)0);
+ RGFW_event event;
+
+ RGFW_window_setExitKey(win, RGFW_escape);
+ RGFW_window_setIcon(win, icon, 3, 3, RGFW_formatRGBA8);
+
+ while (RGFW_window_shouldClose(win) == RGFW_FALSE) {
+ while (RGFW_window_checkEvent(win, &event)) {
+ if (event.type == RGFW_quit)
+ break;
+ }
+ }
+
+ RGFW_window_close(win);
+}
+
+ compiling :
+
+ if you wish to compile the library all you have to do is create a new file with this in it
+
+ rgfw.c
+ #define RGFW_IMPLEMENTATION
+ #include "RGFW.h"
+
+ You may also want to add
+ `#define RGFW_EXPORT` when compiling and
+ `#define RGFW_IMPORT`when linking RGFW on it's own:
+ this reduces inline functions and prevents bloat in the object file
+
+ then you can use gcc (or whatever compile you wish to use) to compile the library into object file
+
+ ex. gcc -c RGFW.c -fPIC
+
+ after you compile the library into an object file, you can also turn the object file into an static or shared library
+
+ (commands ar and gcc can be replaced with whatever equivalent your system uses)
+
+ static : ar rcs RGFW.a RGFW.o
+ shared :
+ windows:
+ gcc -shared RGFW.o -lopengl32 -lgdi32 -o RGFW.dll
+ linux:
+ gcc -shared RGFW.o -lX11 -lGL -lXrandr -o RGFW.so
+ macos:
+ gcc -shared RGFW.o -framework CoreVideo -framework Cocoa -framework OpenGL -framework IOKit
+*/
+
+
+
+/*
+ Credits :
+ EimaMei/Sacode : Code review, helped with X11, MacOS and Windows support, Silicon, siliapp.h -> referencing
+
+ stb : This project is heavily inspired by the stb single header files
+
+ SDL, GLFW and other online resources : reference implementations
+
+ contributors : (feel free to put yourself here if you contribute)
+ krisvers (@krisvers) -> code review
+ EimaMei (@SaCode) -> code review
+ Nycticebus (@Code-Nycticebus) -> bug fixes
+ Rob Rohan (@robrohan) -> X11 bugs and missing features, MacOS/Cocoa fixing memory issues/bugs
+ AICDG (@THISISAGOODNAME) -> vulkan support (example)
+ @Easymode -> support, testing/debugging, bug fixes and reviews
+ Joshua Rowe (omnisci3nce) - bug fix, review (macOS)
+ @lesleyrs -> bug fix, review (OpenGL)
+ Nick Porcino (@meshula) - testing, organization, review (MacOS, examples)
+ @therealmarrakesh -> documentation
+ @DarekParodia -> code review (X11) (C++)
+ @NishiOwO -> fix BSD support, fix OSMesa example
+ @BaynariKattu -> code review and documentation
+ Miguel Pinto (@konopimi) -> code review, fix vulkan example
+ @m-doescode -> code review (wayland)
+ Robert Gonzalez (@uni-dos) -> code review (wayland)
+ @TheLastVoyager -> code review
+ @yehoravramenko -> code review (winapi)
+ @halocupcake -> code review (OpenGL)
+ @GideonSerf -> documentation
+ Alexandre Almeida (@M374LX) -> code review (keycodes)
+ Vũ Xuân Trường (@wanwanvxt) -> code review (winapi)
+ Lucas (@lightspeedlucas) -> code review (msvc++)
+ Jeffery Myers (@JeffM2501) -> code review (msvc)
+ Zeni (@zenitsuyo) -> documentation
+ TheYahton (@TheYahton) -> documentation
+ nonexistant_object (@DiarrheaMcgee)
+ AC Gaudette (@acgaudette)
+*/
+
+#if _MSC_VER
+ #pragma comment(lib, "gdi32")
+ #pragma comment(lib, "shell32")
+ #pragma comment(lib, "User32")
+ #pragma comment(lib, "Advapi32")
+ #pragma warning( push )
+ #pragma warning( disable : 4996 4191 4127)
+ #if _MSC_VER < 600
+ #define RGFW_C89
+ #endif
+#else
+ #if defined(__STDC__) && !defined(__STDC_VERSION__)
+ #define RGFW_C89
+ #endif
+#endif
+
+#if defined(RGFW_EGL) && !defined(RGFW_OPENGL)
+ #define RGFW_OPENGL
+#endif
+
+/* these OS macros look better & are standardized */
+/* plus it helps with cross-compiling */
+
+#ifdef __EMSCRIPTEN__
+ #define RGFW_WASM
+#endif
+
+#if defined(RGFW_X11) && defined(__APPLE__) && !defined(RGFW_CUSTOM_BACKEND)
+ #define RGFW_MACOS_X11
+ #define RGFW_UNIX
+#endif
+
+#if defined(_WIN32) && !defined(RGFW_X11) && !defined(RGFW_UNIX) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND) /* (if you're using X11 on windows some how) */
+ #define RGFW_WINDOWS
+#endif
+#if defined(RGFW_WAYLAND)
+ #define RGFW_DEBUG /* wayland will be in debug mode by default for now */
+ #define RGFW_UNIX
+ #ifdef RGFW_OPENGL
+ #define RGFW_EGL
+ #endif
+ #ifdef RGFW_X11
+ #define RGFW_DYNAMIC
+ #endif
+#endif
+#if (!defined(RGFW_WAYLAND) && !defined(RGFW_X11)) && (defined(__unix__) || defined(RGFW_MACOS_X11) || defined(RGFW_X11)) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND)
+ #define RGFW_MACOS_X11
+ #define RGFW_X11
+ #define RGFW_UNIX
+#elif defined(__APPLE__) && !defined(RGFW_MACOS_X11) && !defined(RGFW_X11) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND)
+ #define RGFW_MACOS
+#endif
+
+#ifndef RGFW_ASSERT
+ #include
+ #define RGFW_ASSERT assert
+#endif
+
+#if !defined(__STDC_VERSION__)
+ #define RGFW_C89
+#endif
+
+#if !defined(RGFW_SNPRINTF) && (defined(RGFW_X11) || defined(RGFW_WAYLAND))
+
+ /* required for X11 errors */
+ #include
+
+ #ifdef RGFW_C89
+ #include
+ static int RGFW_c89_snprintf(char *dst, size_t size, const char *format, ...) {
+ va_list args;
+ size_t count = 0;
+ va_start(args, format);
+ count = (size_t)vsprintf(dst, format, args);
+ RGFW_ASSERT(count + 1 < size && "Buffer overflow");
+ va_end(args);
+ return (int)count;
+ }
+ #define RGFW_SNPRINTF RGFW_c89_snprintf
+ #else
+ #define RGFW_SNPRINTF snprintf
+ #endif /*RGFW_C89*/
+#endif
+
+#ifndef RGFW_USERPTR
+ #define RGFW_USERPTR NULL
+#endif
+
+#ifndef RGFW_UNUSED
+ #define RGFW_UNUSED(x) (void)(x)
+#endif
+
+#ifndef RGFW_ROUND
+ #define RGFW_ROUND(x) (i32)((x) >= 0 ? (x) + 0.5f : (x) - 0.5f)
+#endif
+
+#ifndef RGFW_ROUNDF
+ #define RGFW_ROUNDF(x) (float)((i32)((x) + ((x) < 0.0f ? -0.5f : 0.5f)))
+#endif
+
+#ifndef RGFW_MIN
+ #define RGFW_MIN(x, y) ((x < y) ? x : y)
+#endif
+
+#ifndef RGFW_ALLOC
+ #include
+ #define RGFW_ALLOC malloc
+ #define RGFW_FREE free
+#endif
+
+#if !defined(RGFW_MEMCPY) || !defined(RGFW_STRNCMP) || !defined(RGFW_STRNCPY) || !defined(RGFW_MEMSET)
+ #include
+#endif
+
+#ifndef RGFW_MEMSET
+ #define RGFW_MEMSET(ptr, value, num) memset(ptr, value, num)
+#endif
+
+#ifndef RGFW_MEMCPY
+ #define RGFW_MEMCPY(dist, src, len) memcpy(dist, src, len)
+#endif
+
+#ifndef RGFW_STRNCMP
+ #define RGFW_STRNCMP(s1, s2, max) strncmp(s1, s2, max)
+#endif
+
+#ifndef RGFW_STRNCPY
+ #define RGFW_STRNCPY(dist, src, len) strncpy(dist, src, len)
+#endif
+
+#ifndef RGFW_STRSTR
+ #define RGFW_STRSTR(str, substr) strstr(str, substr)
+#endif
+
+#ifndef RGFW_STRTOL
+ /* required for X11 XDnD and X11 Monitor DPI */
+ #include
+ #define RGFW_STRTOL(str, endptr, base) strtol(str, endptr, base)
+ #define RGFW_ATOF(num) atof(num)
+#endif
+
+#if !defined(RGFW_PRINTF) && ( defined(RGFW_DEBUG) || defined(RGFW_WAYLAND) )
+ /* required when using RGFW_DEBUG */
+ #include
+ #define RGFW_PRINTF printf
+#endif
+
+#ifndef RGFW_MAX_PATH
+ #define RGFW_MAX_PATH 260 /* max length of a path (for drag andn drop) */
+#endif
+#ifndef RGFW_MAX_DROPS
+ #define RGFW_MAX_DROPS 260 /* max items you can drop at once */
+#endif
+
+#ifndef RGFW_MAX_EVENTS
+ #define RGFW_MAX_EVENTS 32
+#endif
+
+#ifndef RGFW_MAX_MONITORS
+ #define RGFW_MAX_MONITORS 6
+#endif
+
+#ifndef RGFW_COCOA_FRAME_NAME
+ #define RGFW_COCOA_FRAME_NAME NULL
+#endif
+
+#ifdef RGFW_WIN95 /* for windows 95 testing (not that it really works) */
+ #define RGFW_NO_PASSTHROUGH
+#endif
+
+#if defined(RGFW_EXPORT) || defined(RGFW_IMPORT)
+ #if defined(_WIN32)
+ #if defined(__TINYC__) && (defined(RGFW_EXPORT) || defined(RGFW_IMPORT))
+ #define __declspec(x) __attribute__((x))
+ #endif
+
+ #if defined(RGFW_EXPORT)
+ #define RGFWDEF __declspec(dllexport)
+ #else
+ #define RGFWDEF __declspec(dllimport)
+ #endif
+ #else
+ #if defined(RGFW_EXPORT)
+ #define RGFWDEF __attribute__((visibility("default")))
+ #endif
+ #endif
+ #ifndef RGFWDEF
+ #define RGFWDEF
+ #endif
+#endif
+
+#ifndef RGFWDEF
+ #ifdef RGFW_C89
+ #define RGFWDEF __inline
+ #else
+ #define RGFWDEF inline
+ #endif
+#endif
+
+#if defined(__cplusplus) && !defined(__EMSCRIPTEN__)
+ extern "C" {
+#endif
+
+/* makes sure the header file part is only defined once by default */
+#ifndef RGFW_HEADER
+
+#define RGFW_HEADER
+
+#include
+#ifndef RGFW_INT_DEFINED
+ #ifdef RGFW_USE_INT /* optional for any system that might not have stdint.h */
+ typedef unsigned char u8;
+ typedef signed char i8;
+ typedef unsigned short u16;
+ typedef signed short i16;
+ typedef unsigned long int u32;
+ typedef signed long int i32;
+ typedef unsigned long long u64;
+ typedef signed long long i64;
+ #else /* use stdint standard types instead of c "standard" types */
+ #include
+
+ typedef uint8_t u8;
+ typedef int8_t i8;
+ typedef uint16_t u16;
+ typedef int16_t i16;
+ typedef uint32_t u32;
+ typedef int32_t i32;
+ typedef uint64_t u64;
+ typedef int64_t i64;
+ #endif
+ #define RGFW_INT_DEFINED
+#endif
+
+typedef ptrdiff_t RGFW_ssize_t;
+
+#ifndef RGFW_BOOL_DEFINED
+ #define RGFW_BOOL_DEFINED
+ typedef u8 RGFW_bool;
+#endif
+
+#define RGFW_BOOL(x) (RGFW_bool)((x) != 0) /* force a value to be 0 or 1 */
+#define RGFW_TRUE (RGFW_bool)1
+#define RGFW_FALSE (RGFW_bool)0
+
+#define RGFW_ENUM(type, name) type name; enum
+#define RGFW_BIT(x) (1 << (x))
+
+#ifdef RGFW_VULKAN
+
+ #if defined(RGFW_WAYLAND) && defined(RGFW_X11)
+ #define VK_USE_PLATFORM_WAYLAND_KHR
+ #define VK_USE_PLATFORM_XLIB_KHR
+ #define RGFW_VK_SURFACE ((RGFW_usingWayland()) ? ("VK_KHR_wayland_surface") : ("VK_KHR_xlib_surface"))
+ #elif defined(RGFW_WAYLAND)
+ #define VK_USE_PLATFORM_WAYLAND_KHR
+ #define VK_USE_PLATFORM_XLIB_KHR
+ #define RGFW_VK_SURFACE "VK_KHR_wayland_surface"
+ #elif defined(RGFW_X11)
+ #define VK_USE_PLATFORM_XLIB_KHR
+ #define RGFW_VK_SURFACE "VK_KHR_xlib_surface"
+ #elif defined(RGFW_WINDOWS)
+ #define VK_USE_PLATFORM_WIN32_KHR
+ #define OEMRESOURCE
+ #define RGFW_VK_SURFACE "VK_KHR_win32_surface"
+ #elif defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11)
+ #define VK_USE_PLATFORM_MACOS_MVK
+ #define RGFW_VK_SURFACE "VK_MVK_macos_surface"
+ #else
+ #define RGFW_VK_SURFACE NULL
+ #endif
+
+#endif
+
+
+/*! @brief The stucture that contains information about the current RGFW instance */
+typedef struct RGFW_info RGFW_info;
+
+/*! @brief The window stucture for interfacing with the window */
+typedef struct RGFW_window RGFW_window;
+
+/*! @brief The source window stucture for interfacing with the underlying windowing API (e.g. winapi, wayland, cocoa, etc) */
+typedef struct RGFW_window_src RGFW_window_src;
+
+/*! @brief The color format for pixel data */
+typedef RGFW_ENUM(u8, RGFW_format) {
+ RGFW_formatRGB8 = 0, /*!< 8-bit RGB (3 channels) */
+ RGFW_formatBGR8, /*!< 8-bit BGR (3 channels) */
+ RGFW_formatRGBA8, /*!< 8-bit RGBA (4 channels) */
+ RGFW_formatARGB8, /*!< 8-bit RGBA (4 channels) */
+ RGFW_formatBGRA8, /*!< 8-bit BGRA (4 channels) */
+ RGFW_formatABGR8, /*!< 8-bit BGRA (4 channels) */
+ RGFW_formatCount
+};
+
+/*! @brief layout struct for mapping out format types */
+typedef struct RGFW_colorLayout { i32 r, g, b, a; u32 channels; } RGFW_colorLayout;
+
+/*! @brief function type converting raw image data between formats */
+typedef void (* RGFW_convertImageDataFunc)(u8* dest_data, u8* src_data, const RGFW_colorLayout* srcLayout, const RGFW_colorLayout* destLayout, size_t count);
+
+/*! @brief a stucture for interfacing with the underlying native image (e.g. XImage, HBITMAP, etc) */
+typedef struct RGFW_nativeImage RGFW_nativeImage;
+
+/*! @brief a stucture for interfacing with pixel data as a renderable surface */
+typedef struct RGFW_surface RGFW_surface;
+
+/*! @brief gamma struct for monitors */
+typedef struct RGFW_gammaRamp {
+ u16* red; /*!< array for the red channel */
+ u16* green; /*!< array for the green channel */
+ u16* blue; /*!< array for the blue channel */
+ size_t count; /*! count of elements in each channel */
+} RGFW_gammaRamp;
+
+/*! @brief monitor mode data | can be changed by the user (with functions)*/
+typedef struct RGFW_monitorMode {
+ i32 w, h; /*!< monitor workarea size */
+ float refreshRate; /*!< monitor refresh rate */
+ u8 red, blue, green; /*!< sizeof rgb values */
+ void* src; /*!< source API mode */
+} RGFW_monitorMode;
+
+/*! @brief structure for monitor node and source monitor data */
+typedef struct RGFW_monitorNode RGFW_monitorNode;
+
+/*! @brief structure for monitor data */
+typedef struct RGFW_monitor {
+ i32 x, y; /*!< x - y of the monitor workarea */
+ char name[128]; /*!< monitor name */
+ float scaleX, scaleY; /*!< monitor content scale */
+ float pixelRatio; /*!< pixel ratio for monitor (1.0 for regular, 2.0 for hiDPI) */
+ float physW, physH; /*!< monitor physical size in inches */
+ RGFW_monitorMode mode; /*!< current mode of the monitor */
+ void* userPtr; /*!< pointer for user data */
+ RGFW_monitorNode* node; /*!< source node data of the monitor */
+} RGFW_monitor;
+
+/*! @brief what type of request you are making for the monitor */
+typedef RGFW_ENUM(u8, RGFW_modeRequest) {
+ RGFW_monitorScale = RGFW_BIT(0), /*!< scale the monitor size */
+ RGFW_monitorRefresh = RGFW_BIT(1), /*!< change the refresh rate */
+ RGFW_monitorRGB = RGFW_BIT(2), /*!< change the monitor RGB bits size */
+ RGFW_monitorAll = RGFW_monitorScale | RGFW_monitorRefresh | RGFW_monitorRGB
+};
+
+/*! a raw pointer to the underlying mouse handle for setting and creating custom mouse icons */
+typedef void RGFW_mouse;
+
+/*! @brief RGFW's abstract keycodes */
+typedef RGFW_ENUM(u8, RGFW_key) {
+ RGFW_keyNULL = 0,
+ RGFW_escape = '\033',
+ RGFW_backtick = '`',
+ RGFW_0 = '0',
+ RGFW_1 = '1',
+ RGFW_2 = '2',
+ RGFW_3 = '3',
+ RGFW_4 = '4',
+ RGFW_5 = '5',
+ RGFW_6 = '6',
+ RGFW_7 = '7',
+ RGFW_8 = '8',
+ RGFW_9 = '9',
+ RGFW_minus = '-',
+ RGFW_equal = '=',
+ RGFW_equals = RGFW_equal,
+ RGFW_backSpace = '\b',
+ RGFW_tab = '\t',
+ RGFW_space = ' ',
+ RGFW_a = 'a',
+ RGFW_b = 'b',
+ RGFW_c = 'c',
+ RGFW_d = 'd',
+ RGFW_e = 'e',
+ RGFW_f = 'f',
+ RGFW_g = 'g',
+ RGFW_h = 'h',
+ RGFW_i = 'i',
+ RGFW_j = 'j',
+ RGFW_k = 'k',
+ RGFW_l = 'l',
+ RGFW_m = 'm',
+ RGFW_n = 'n',
+ RGFW_o = 'o',
+ RGFW_p = 'p',
+ RGFW_q = 'q',
+ RGFW_r = 'r',
+ RGFW_s = 's',
+ RGFW_t = 't',
+ RGFW_u = 'u',
+ RGFW_v = 'v',
+ RGFW_w = 'w',
+ RGFW_x = 'x',
+ RGFW_y = 'y',
+ RGFW_z = 'z',
+ RGFW_period = '.',
+ RGFW_comma = ',',
+ RGFW_slash = '/',
+ RGFW_bracket = '[',
+ RGFW_closeBracket = ']',
+ RGFW_semicolon = ';',
+ RGFW_apostrophe = '\'',
+ RGFW_backSlash = '\\',
+ RGFW_return = '\n',
+ RGFW_enter = RGFW_return,
+ RGFW_delete = '\177', /* 127 */
+ RGFW_F1,
+ RGFW_F2,
+ RGFW_F3,
+ RGFW_F4,
+ RGFW_F5,
+ RGFW_F6,
+ RGFW_F7,
+ RGFW_F8,
+ RGFW_F9,
+ RGFW_F10,
+ RGFW_F11,
+ RGFW_F12,
+ RGFW_F13,
+ RGFW_F14,
+ RGFW_F15,
+ RGFW_F16,
+ RGFW_F17,
+ RGFW_F18,
+ RGFW_F19,
+ RGFW_F20,
+ RGFW_F21,
+ RGFW_F22,
+ RGFW_F23,
+ RGFW_F24,
+ RGFW_F25,
+ RGFW_capsLock,
+ RGFW_shiftL,
+ RGFW_controlL,
+ RGFW_altL,
+ RGFW_superL,
+ RGFW_shiftR,
+ RGFW_controlR,
+ RGFW_altR,
+ RGFW_superR,
+ RGFW_up,
+ RGFW_down,
+ RGFW_left,
+ RGFW_right,
+ RGFW_insert,
+ RGFW_menu,
+ RGFW_end,
+ RGFW_home,
+ RGFW_pageUp,
+ RGFW_pageDown,
+ RGFW_numLock,
+ RGFW_kpSlash,
+ RGFW_kpMultiply,
+ RGFW_kpPlus,
+ RGFW_kpMinus,
+ RGFW_kpEqual,
+ RGFW_kpEquals = RGFW_kpEqual,
+ RGFW_kp1,
+ RGFW_kp2,
+ RGFW_kp3,
+ RGFW_kp4,
+ RGFW_kp5,
+ RGFW_kp6,
+ RGFW_kp7,
+ RGFW_kp8,
+ RGFW_kp9,
+ RGFW_kp0,
+ RGFW_kpPeriod,
+ RGFW_kpReturn,
+ RGFW_scrollLock,
+ RGFW_printScreen,
+ RGFW_pause,
+ RGFW_world1,
+ RGFW_world2,
+ RGFW_keyLast = 256 /* padding for alignment ~(175 by default) */
+};
+
+/*! @brief abstract mouse button codes */
+typedef RGFW_ENUM(u8, RGFW_mouseButton) {
+ RGFW_mouseLeft = 0, /*!< left mouse button is pressed */
+ RGFW_mouseMiddle, /*!< mouse-wheel-button is pressed */
+ RGFW_mouseRight, /*!< right mouse button is pressed */
+ RGFW_mouseMisc1, RGFW_mouseMisc2, RGFW_mouseMisc3, RGFW_mouseMisc4, RGFW_mouseMisc5,
+ RGFW_mouseFinal
+};
+
+/*! abstract key modifier codes */
+typedef RGFW_ENUM(u8, RGFW_keymod) {
+ RGFW_modCapsLock = RGFW_BIT(0),
+ RGFW_modNumLock = RGFW_BIT(1),
+ RGFW_modControl = RGFW_BIT(2),
+ RGFW_modAlt = RGFW_BIT(3),
+ RGFW_modShift = RGFW_BIT(4),
+ RGFW_modSuper = RGFW_BIT(5),
+ RGFW_modScrollLock = RGFW_BIT(6)
+};
+
+/*! @brief codes for the event types that can be sent */
+typedef RGFW_ENUM(u8, RGFW_eventType) {
+ RGFW_eventNone = 0, /*!< no event has been sent */
+ RGFW_keyPressed, /*!< a key has been pressed */
+ RGFW_keyReleased, /*!< a key has been released */
+ RGFW_keyChar, /*!< keyboard character input event specifically for utf8 input */
+ RGFW_mouseButtonPressed, /*!< a mouse button has been pressed (left,middle,right) */
+ RGFW_mouseButtonReleased, /*!< a mouse button has been released (left,middle,right) */
+ RGFW_mouseScroll, /*!< a mouse scroll event */
+ RGFW_mousePosChanged, /*!< the position of the mouse has been changed */
+ /*! mouse event note
+ the x and y of the mouse can be found in the vector, RGFW_x, y
+
+ RGFW_event.button.value holds which mouse button was pressed
+ */
+ RGFW_windowMoved, /*!< the window was moved (by the user) */
+ RGFW_windowResized, /*!< the window was resized (by the user), [on WASM this means the browser was resized] */
+ RGFW_focusIn, /*!< window is in focus now */
+ RGFW_focusOut, /*!< window is out of focus now */
+ RGFW_mouseEnter, /* mouse entered the window */
+ RGFW_mouseLeave, /* mouse left the window */
+ RGFW_windowRefresh, /* The window content needs to be refreshed */
+
+ /* attribs change event note
+ The event data is sent straight to the window structure
+ with win->x, win->y, win->w and win->h
+ */
+ RGFW_quit, /*!< the user clicked the quit button */
+ RGFW_dataDrop, /*!< a file has been dropped into the window */
+ RGFW_dataDrag, /*!< the start of a drag and drop event, when the file is being dragged */
+ /* drop data note
+ The x and y coords of the drop are stored in the vector RGFW_x, y
+
+ RGFW_event.drop.count holds how many files were dropped
+
+ This is also the size of the array which stores all the dropped file string,
+ RGFW_event.drop.files
+ */
+ RGFW_windowMaximized, /*!< the window was maximized */
+ RGFW_windowMinimized, /*!< the window was minimized */
+ RGFW_windowRestored, /*!< the window was restored */
+ RGFW_scaleUpdated, /*!< content scale factor changed */
+ RGFW_monitorConnected, /*!< a monitor has been connected */
+ RGFW_monitorDisconnected /*!< a monitor has been disconnected */
+};
+
+/*! @brief flags for toggling wether or not an event should be processed */
+typedef RGFW_ENUM(u32, RGFW_eventFlag) {
+ RGFW_keyPressedFlag = RGFW_BIT(RGFW_keyPressed),
+ RGFW_keyReleasedFlag = RGFW_BIT(RGFW_keyReleased),
+ RGFW_keyCharFlag = RGFW_BIT(RGFW_keyChar),
+ RGFW_mouseScrollFlag = RGFW_BIT(RGFW_mouseScroll),
+ RGFW_mouseButtonPressedFlag = RGFW_BIT(RGFW_mouseButtonPressed),
+ RGFW_mouseButtonReleasedFlag = RGFW_BIT(RGFW_mouseButtonReleased),
+ RGFW_mousePosChangedFlag = RGFW_BIT(RGFW_mousePosChanged),
+ RGFW_mouseEnterFlag = RGFW_BIT(RGFW_mouseEnter),
+ RGFW_mouseLeaveFlag = RGFW_BIT(RGFW_mouseLeave),
+ RGFW_windowMovedFlag = RGFW_BIT(RGFW_windowMoved),
+ RGFW_windowResizedFlag = RGFW_BIT(RGFW_windowResized),
+ RGFW_focusInFlag = RGFW_BIT(RGFW_focusIn),
+ RGFW_focusOutFlag = RGFW_BIT(RGFW_focusOut),
+ RGFW_windowRefreshFlag = RGFW_BIT(RGFW_windowRefresh),
+ RGFW_windowMaximizedFlag = RGFW_BIT(RGFW_windowMaximized),
+ RGFW_windowMinimizedFlag = RGFW_BIT(RGFW_windowMinimized),
+ RGFW_windowRestoredFlag = RGFW_BIT(RGFW_windowRestored),
+ RGFW_scaleUpdatedFlag = RGFW_BIT(RGFW_scaleUpdated),
+ RGFW_quitFlag = RGFW_BIT(RGFW_quit),
+ RGFW_dataDropFlag = RGFW_BIT(RGFW_dataDrop),
+ RGFW_dataDragFlag = RGFW_BIT(RGFW_dataDrag),
+ RGFW_monitorConnectedFlag = RGFW_BIT(RGFW_monitorConnected),
+ RGFW_monitorDisconnectedFlag = RGFW_BIT(RGFW_monitorDisconnected),
+
+ RGFW_keyEventsFlag = RGFW_keyPressedFlag | RGFW_keyReleasedFlag | RGFW_keyCharFlag,
+ RGFW_mouseEventsFlag = RGFW_mouseButtonPressedFlag | RGFW_mouseButtonReleasedFlag | RGFW_mousePosChangedFlag | RGFW_mouseEnterFlag | RGFW_mouseLeaveFlag | RGFW_mouseScrollFlag ,
+ RGFW_windowEventsFlag = RGFW_windowMovedFlag | RGFW_windowResizedFlag | RGFW_windowRefreshFlag | RGFW_windowMaximizedFlag | RGFW_windowMinimizedFlag | RGFW_windowRestoredFlag | RGFW_scaleUpdatedFlag,
+ RGFW_focusEventsFlag = RGFW_focusInFlag | RGFW_focusOutFlag,
+ RGFW_dataDropEventsFlag = RGFW_dataDropFlag | RGFW_dataDragFlag,
+ RGFW_monitorEventsFlag = RGFW_monitorConnectedFlag | RGFW_monitorDisconnectedFlag,
+ RGFW_allEventFlags = RGFW_keyEventsFlag | RGFW_mouseEventsFlag | RGFW_windowEventsFlag | RGFW_focusEventsFlag | RGFW_dataDropEventsFlag | RGFW_quitFlag | RGFW_monitorEventsFlag
+};
+
+/*! Event structure(s) and union for checking/getting events */
+
+/*! @brief common event data across all events */
+typedef struct RGFW_commonEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+} RGFW_commonEvent;
+
+/*! @brief event data for any mouse button event (press/release) */
+typedef struct RGFW_mouseButtonEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+ RGFW_mouseButton value; /* !< which mouse button was pressed */
+} RGFW_mouseButtonEvent;
+
+/*! @brief event data for any mouse scroll event */
+typedef struct RGFW_mouseScrollEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+ float x, y; /*!< the raw mouse scroll value */
+} RGFW_mouseScrollEvent;
+
+/*! @brief event data for any mouse position event (RGFW_mousePosChanged) */
+typedef struct RGFW_mousePosEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+ i32 x, y; /*!< mouse x, y of event (or drop point) */
+ float vecX, vecY; /*!< raw mouse movement */
+} RGFW_mousePosEvent;
+
+/*! @brief event data for a key press/release event */
+typedef struct RGFW_keyEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+ RGFW_key value; /*!< the physical key of the event, refers to where key is physically */
+ RGFW_bool repeat; /*!< key press event repeated (the key is being held) */
+ RGFW_keymod mod;
+} RGFW_keyEvent;
+
+/*! @brief event data for a key character event */
+typedef struct RGFW_keyCharEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+ u32 value; /*!< the unicode value of the key */
+} RGFW_keyCharEvent;
+
+/*! @brief event data for any data drop event */
+typedef struct RGFW_dataDropEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+ /* 260 max paths with a max length of 260 */
+ char** files; /*!< dropped files */
+ size_t count; /*!< how many files were dropped */
+} RGFW_dataDropEvent;
+
+/*! @brief event data for any data drag event */
+typedef struct RGFW_dataDragEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+ i32 x, y; /*!< mouse x, y of event (or drop point) */
+} RGFW_dataDragEvent;
+
+/*! @brief event data for when the window scale (DPI) is updated */
+typedef struct RGFW_scaleUpdatedEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+ float x, y; /*!< DPI scaling */
+} RGFW_scaleUpdatedEvent;
+
+/*! @brief event data for when a monitor is connected, disconnected or updated */
+typedef struct RGFW_monitorEvent {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_window* win; /*!< the window this event applies to (for event queue events) */
+ const RGFW_monitor* monitor; /*!< the monitor that this event applies to */
+} RGFW_monitorEvent;
+
+/*! @brief union for all of the event stucture types */
+typedef union RGFW_event {
+ RGFW_eventType type; /*!< which event has been sent?*/
+ RGFW_commonEvent common; /*!< common event data (e.g.) type and win */
+ RGFW_mouseButtonEvent button; /*!< data for a button press/release */
+ RGFW_mouseScrollEvent scroll; /*!< data for a mouse scroll */
+ RGFW_mousePosEvent mouse; /*!< data for mouse motion events */
+ RGFW_keyEvent key; /*!< data for key press/release/hold events */
+ RGFW_keyCharEvent keyChar; /*!< data for key character events */
+ RGFW_dataDropEvent drop; /*!< dropping a file events */
+ RGFW_dataDragEvent drag; /*!< data for dragging a file events */
+ RGFW_scaleUpdatedEvent scale; /*!< data for dpi scaling update events */
+ RGFW_monitorEvent monitor; /*!< data for monitor events */
+} RGFW_event;
+
+/*!
+ @!brief codes for for RGFW_the code is stupid and C++ waitForEvent
+ waitMS -> Allows the function to keep checking for events even after there are no more events
+ if waitMS == 0, the loop will not wait for events
+ if waitMS > 0, the loop will wait that many miliseconds after there are no more events until it returns
+ if waitMS == -1 or waitMS == the max size of an unsigned 32-bit int, the loop will not return until it gets another event
+*/
+typedef RGFW_ENUM(i32, RGFW_eventWait) {
+ RGFW_eventNoWait = 0,
+ RGFW_eventWaitNext = -1
+};
+
+/*! @brief optional bitwise arguments for making a windows, these can be OR'd together */
+typedef RGFW_ENUM(u32, RGFW_windowFlags) {
+ RGFW_windowNoBorder = RGFW_BIT(0), /*!< the window doesn't have a border */
+ RGFW_windowNoResize = RGFW_BIT(1), /*!< the window cannot be resized by the user */
+ RGFW_windowAllowDND = RGFW_BIT(2), /*!< the window supports drag and drop */
+ RGFW_windowHideMouse = RGFW_BIT(3), /*! the window should hide the mouse (can be toggled later on using `RGFW_window_showMouse`) */
+ RGFW_windowFullscreen = RGFW_BIT(4), /*!< the window is fullscreen by default */
+ RGFW_windowTransparent = RGFW_BIT(5), /*!< the window is transparent (only properly works on X11 and MacOS, although it's meant for for windows) */
+ RGFW_windowCenter = RGFW_BIT(6), /*! center the window on the screen */
+ RGFW_windowRawMouse = RGFW_BIT(7), /*!< use raw mouse mouse on window creation */
+ RGFW_windowScaleToMonitor = RGFW_BIT(8), /*! scale the window to the screen */
+ RGFW_windowHide = RGFW_BIT(9), /*! the window is hidden */
+ RGFW_windowMaximize = RGFW_BIT(10), /*!< maximize the window on creation */
+ RGFW_windowCenterCursor = RGFW_BIT(11), /*!< center the cursor to the window on creation */
+ RGFW_windowFloating = RGFW_BIT(12), /*!< create a floating window */
+ RGFW_windowFocusOnShow = RGFW_BIT(13), /*!< focus the window when it's shown */
+ RGFW_windowMinimize = RGFW_BIT(14), /*!< focus the window when it's shown */
+ RGFW_windowFocus = RGFW_BIT(15), /*!< if the window is in focus */
+ RGFW_windowCaptureMouse = RGFW_BIT(16), /*!< capture the mouse mouse mouse on window creation */
+ RGFW_windowOpenGL = RGFW_BIT(17), /*!< create an OpenGL context (you can also do this manually with RGFW_window_createContext_OpenGL) */
+ RGFW_windowEGL = RGFW_BIT(18), /*!< create an EGL context (you can also do this manually with RGFW_window_createContext_EGL) */
+ RGFW_noDeinitOnClose = RGFW_BIT(19), /*!< do not auto deinit RGFW if the window closes and this is the last window open */
+ RGFW_windowedFullscreen = RGFW_windowNoBorder | RGFW_windowMaximize,
+ RGFW_windowCaptureRawMouse = RGFW_windowCaptureMouse | RGFW_windowRawMouse
+};
+
+/*! @brief the types of icon to set */
+typedef RGFW_ENUM(u8, RGFW_icon) {
+ RGFW_iconTaskbar = RGFW_BIT(0),
+ RGFW_iconWindow = RGFW_BIT(1),
+ RGFW_iconBoth = RGFW_iconTaskbar | RGFW_iconWindow
+};
+
+/*! @brief standard mouse icons */
+typedef RGFW_ENUM(u8, RGFW_mouseIcons) {
+ RGFW_mouseNormal = 0,
+ RGFW_mouseArrow,
+ RGFW_mouseIbeam,
+ RGFW_mouseText = RGFW_mouseIbeam,
+ RGFW_mouseCrosshair,
+ RGFW_mousePointingHand,
+ RGFW_mouseResizeEW,
+ RGFW_mouseResizeNS,
+ RGFW_mouseResizeNWSE,
+ RGFW_mouseResizeNESW,
+ RGFW_mouseResizeNW,
+ RGFW_mouseResizeN,
+ RGFW_mouseResizeNE,
+ RGFW_mouseResizeE,
+ RGFW_mouseResizeSE,
+ RGFW_mouseResizeS,
+ RGFW_mouseResizeSW,
+ RGFW_mouseResizeW,
+ RGFW_mouseResizeAll,
+ RGFW_mouseNotAllowed,
+ RGFW_mouseWait,
+ RGFW_mouseProgress,
+ RGFW_mouseIconCount,
+ RGFW_mouseIconFinal = 16 /* padding for alignment */
+};
+
+/*! @breif flash request type */
+typedef RGFW_ENUM(u8, RGFW_flashRequest) {
+ RGFW_flashCancel = 0,
+ RGFW_flashBriefly,
+ RGFW_flashUntilFocused
+};
+
+/*! @brief the type of debug message */
+typedef RGFW_ENUM(u8, RGFW_debugType) {
+ RGFW_typeError = 0, RGFW_typeWarning, RGFW_typeInfo
+};
+
+/*! @brief error codes for known failure types */
+typedef RGFW_ENUM(u8, RGFW_errorCode) {
+ RGFW_noError = 0, /*!< no error */
+ RGFW_errOutOfMemory,
+ RGFW_errOpenGLContext, RGFW_errEGLContext, /*!< error with the OpenGL context */
+ RGFW_errWayland, RGFW_errX11,
+ RGFW_errDirectXContext,
+ RGFW_errIOKit,
+ RGFW_errClipboard,
+ RGFW_errFailedFuncLoad,
+ RGFW_errBuffer,
+ RGFW_errMetal,
+ RGFW_errPlatform,
+ RGFW_errEventQueue,
+ RGFW_infoWindow, RGFW_infoBuffer, RGFW_infoGlobal, RGFW_infoOpenGL,
+ RGFW_warningWayland, RGFW_warningOpenGL
+};
+
+/*! @brief callback function type for debug messags */
+typedef void (* RGFW_debugfunc)(RGFW_debugType type, RGFW_errorCode err, const char* msg);
+
+/*! @brief RGFW_windowMoved, the window and its new rect value */
+typedef void (* RGFW_windowMovedfunc)(RGFW_window* win, i32 x, i32 y);
+/*! @brief RGFW_windowResized, the window and its new rect value */
+typedef void (* RGFW_windowResizedfunc)(RGFW_window* win, i32 w, i32 h);
+/*! @brief RGFW_windowRestored, the window and its new rect value */
+typedef void (* RGFW_windowRestoredfunc)(RGFW_window* win, i32 x, i32 y, i32 w, i32 h);
+/*! @brief RGFW_windowMaximized, the window and its new rect value */
+typedef void (* RGFW_windowMaximizedfunc)(RGFW_window* win, i32 x, i32 y, i32 w, i32 h);
+/*! @brief RGFW_windowMinimized, the window and its new rect value */
+typedef void (* RGFW_windowMinimizedfunc)(RGFW_window* win);
+/*! @brief RGFW_quit, the window that was closed */
+typedef void (* RGFW_windowQuitfunc)(RGFW_window* win);
+/*! @brief RGFW_focusIn / RGFW_focusOut, the window who's focus has changed and if its in focus */
+typedef void (* RGFW_focusfunc)(RGFW_window* win, RGFW_bool inFocus);
+/*! @brief RGFW_mouseEnter / RGFW_mouseLeave, the window that changed, the point of the mouse (enter only) and if the mouse has entered */
+typedef void (* RGFW_mouseNotifyfunc)(RGFW_window* win, i32 x, i32 y, RGFW_bool status);
+/*! @brief RGFW_mousePosChanged, the window that the move happened on, and the new point of the mouse */
+typedef void (* RGFW_mousePosfunc)(RGFW_window* win, i32 x, i32 y, float vecX, float vecY);
+/*! @brief RGFW_dataDrag, the window, the point of the drop on the windows */
+typedef void (* RGFW_dataDragfunc)(RGFW_window* win, i32 x, i32 y);
+/*! @brief RGFW_windowRefresh, the window that needs to be refreshed */
+typedef void (* RGFW_windowRefreshfunc)(RGFW_window* win);
+/*! @brief RGFW_keyChar, the window that got the event, the unicode key value */
+typedef void (* RGFW_keyCharfunc)(RGFW_window* win, u32 codepoint);
+/*! @brief RGFW_mouseButtonPressed / RGFW_mouseButtonReleased, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release) */
+
+/*! @brief RGFW_keyPressed / RGFW_keyReleased, the window that got the event, the mapped key, the physical key, the string version, the state of the mod keys, if it was a press (else it's a release) */
+typedef void (* RGFW_keyfunc)(RGFW_window* win, u8 key, RGFW_keymod mod, RGFW_bool repeat, RGFW_bool pressed);
+/*! @brief RGFW_mouseButtonPressed / RGFW_mouseButtonReleased, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release) */
+typedef void (* RGFW_mouseButtonfunc)(RGFW_window* win, RGFW_mouseButton button, RGFW_bool pressed);
+/*! @brief RGFW_mouseScroll, the window that got the event, the x scroll value, the y scroll value */
+typedef void (* RGFW_mouseScrollfunc)(RGFW_window* win, float x, float y);
+/*! @brief RGFW_dataDrop the window that had the drop, the drop data and the number of files dropped */
+typedef void (* RGFW_dataDropfunc)(RGFW_window* win, char** files, size_t count);
+/*! @brief RGFW_scaleUpdated, the window the event was sent to, content scaleX, content scaleY */
+typedef void (* RGFW_scaleUpdatedfunc)(RGFW_window* win, float scaleX, float scaleY);
+/*! @brief RGFW_monitorConnected / RGFW_monitorDisconnected, there was a monitor connected/disconnected */
+typedef void (* RGFW_monitorfunc)(RGFW_window* win, const RGFW_monitor* monitor, RGFW_bool connected);
+
+/*! @brief function pointer equivalent of void* */
+typedef void (*RGFW_proc)(void);
+
+#if defined(RGFW_OPENGL)
+
+/*! @brief abstract structure for interfacing with the underlying OpenGL API */
+typedef struct RGFW_glContext RGFW_glContext;
+
+/*! @brief abstract structure for interfacing with the underlying EGL API */
+typedef struct RGFW_eglContext RGFW_eglContext;
+
+/*! values for the releaseBehavior hint */
+typedef RGFW_ENUM(i32, RGFW_glReleaseBehavior) {
+ RGFW_glReleaseFlush = 0, /*!< flush the pipeline will be flushed when the context is release */
+ RGFW_glReleaseNone /*!< do nothing on release */
+};
+
+/*! values for the profile hint */
+typedef RGFW_ENUM(i32, RGFW_glProfile) {
+ RGFW_glCore = 0, /*!< the core OpenGL version, e.g. just support for that version */
+ RGFW_glForwardCompatibility, /*!< only compatibility for newer versions of OpenGL as well as the requested version */
+ RGFW_glCompatibility, /*!< allow compatibility for older versions of OpenGL as well as the requested version */
+ RGFW_glES /*!< use OpenGL ES */
+};
+
+/*! values for the renderer hint */
+typedef RGFW_ENUM(i32, RGFW_glRenderer) {
+ RGFW_glAccelerated = 0, /*!< hardware accelerated (GPU) */
+ RGFW_glSoftware /*!< software rendered (CPU) */
+};
+
+/*! OpenGL initalization hints */
+typedef struct RGFW_glHints {
+ i32 stencil; /*!< set stencil buffer bit size (0 by default) */
+ i32 samples; /*!< set number of sample buffers (0 by default) */
+ i32 stereo; /*!< hint the context to use stereoscopic frame buffers for 3D (false by default) */
+ i32 auxBuffers; /*!< number of aux buffers (0 by default) */
+ i32 doubleBuffer; /*!< request double buffering (true by default) */
+ i32 red, green, blue, alpha; /*!< set color bit sizes (all 8 by default) */
+ i32 depth; /*!< set depth buffer bit size (24 by default) */
+ i32 accumRed, accumGreen, accumBlue, accumAlpha; /*!< set accumulated RGBA bit sizes (all 0 by default) */
+ RGFW_bool sRGB; /*!< request sRGA format (false by default) */
+ RGFW_bool robustness; /*!< request a "robust" (as in memory-safe) context (false by default). For more information check the overview section: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_robustness.txt */
+ RGFW_bool debug; /*!< request OpenGL debugging (false by default). */
+ RGFW_bool noError; /*!< request no OpenGL errors (false by default). This causes OpenGL errors to be undefined behavior. For more information check the overview section: https://registry.khronos.org/OpenGL/extensions/KHR/KHR_no_error.txt */
+ RGFW_glReleaseBehavior releaseBehavior; /*!< hint how the OpenGL driver should behave when changing contexts (RGFW_glReleaseNone by default). For more information check the overview section: https://registry.khronos.org/OpenGL/extensions/KHR/KHR_context_flush_control.txt */
+ RGFW_glProfile profile; /*!< set OpenGL API profile (RGFW_glCore by default) */
+ i32 major, minor; /*!< set the OpenGL API profile version (by default RGFW_glMajor is 1, RGFW_glMinor is 0) */
+ RGFW_glContext* share; /*!< Share this OpenGL context with newly created OpenGL contexts; defaults to NULL. */
+ RGFW_eglContext* shareEGL; /*!< Share this EGL context with newly created OpenGL contexts; defaults to NULL. */
+ RGFW_glRenderer renderer; /*!< renderer to use e.g. accelerated or software defaults to accelerated */
+} RGFW_glHints;
+
+#endif
+
+/**!
+ * @brief Allocates memory using the allocator defined by RGFW_ALLOC at compile time.
+ * @param size The size (in bytes) of the memory block to allocate.
+ * @return A pointer to the allocated memory block.
+*/
+RGFWDEF void* RGFW_alloc(size_t size);
+
+/**!
+ * @brief Frees memory using the deallocator defined by RGFW_FREE at compile time.
+ * @param ptr A pointer to the memory block to free.
+*/
+RGFWDEF void RGFW_free(void* ptr);
+
+/**!
+ * @brief Returns the size (in bytes) of the RGFW_window structure.
+ * @return The size of the RGFW_window structure.
+*/
+RGFWDEF size_t RGFW_sizeofWindow(void);
+
+/**!
+ * @brief Returns the size (in bytes) of the RGFW_window_src structure.
+ * @return The size of the RGFW_window_src structure.
+*/
+RGFWDEF size_t RGFW_sizeofWindowSrc(void);
+
+/**!
+ * @brief (Unix) Toggles the use of Wayland.
+ * This is enabled by default when compiled with `RGFW_WAYLAND`.
+ * If not using `RGFW_WAYLAND`, Wayland functions are not exposed.
+ * This function can be used to force the use of XWayland.
+ * @param wayland A boolean value indicating whether to use Wayland (true) or not (false).
+*/
+RGFWDEF void RGFW_useWayland(RGFW_bool wayland);
+
+/**!
+ * @brief Checks if Wayland is currently being used.
+ * @return RGFW_TRUE if using Wayland, RGFW_FALSE otherwise.
+*/
+RGFWDEF RGFW_bool RGFW_usingWayland(void);
+
+/**!
+ * @brief Retrieves the current Cocoa layer (macOS only).
+ * @return A pointer to the Cocoa layer, or NULL if the platform is not in use.
+*/
+RGFWDEF void* RGFW_getLayer_OSX(void);
+
+/**!
+ * @brief Retrieves the current X11 display connection.
+ * @return A pointer to the X11 display, or NULL if the platform is not in use.
+*/
+RGFWDEF void* RGFW_getDisplay_X11(void);
+
+/**!
+ * @brief Retrieves the current Wayland display connection.
+ * @return A pointer to the Wayland display (`struct wl_display*`), or NULL if the platform is not in use.
+*/
+RGFWDEF struct wl_display* RGFW_getDisplay_Wayland(void);
+
+/**!
+ * @brief Sets the class name for X11 and WinAPI windows.
+ * Windows with the same class name will be grouped by the window manager.
+ * By default, the class name matches the root window’s name.
+ * @param name The class name to assign.
+*/
+RGFWDEF void RGFW_setClassName(const char* name);
+
+/**!
+ * @brief Sets the X11 instance name.
+ * By default, the window name will be used as the instance name.
+ * @param name The X11 instance name to set.
+*/
+RGFWDEF void RGFW_setXInstName(const char* name);
+
+/**!
+ * @brief (macOS only) Changes the current working directory to the application’s resource folder.
+*/
+RGFWDEF void RGFW_moveToMacOSResourceDir(void);
+
+/*! copy image to another image, respecting each image's format */
+RGFWDEF void RGFW_copyImageData(u8* dest_data, i32 w, i32 h, RGFW_format dest_format, u8* src_data, RGFW_format src_format, RGFW_convertImageDataFunc func);
+
+/**!
+ * @brief Returns the size (in bytes) of the RGFW_nativeImage structure.
+ * @return The size of the RGFW_nativeImage structure.
+*/
+RGFWDEF size_t RGFW_sizeofNativeImage(void);
+
+/**!
+ * @brief Returns the size (in bytes) of the RGFW_surface structure.
+ * @return The size of the RGFW_surface structure.
+*/
+RGFWDEF size_t RGFW_sizeofSurface(void);
+
+/**!
+ * @brief Returns the native format type for the system
+ * @return the native format type for the system as a RGFW_format enum value
+*/
+RGFWDEF RGFW_format RGFW_nativeFormat(void);
+
+/**!
+ * @brief Creates a new surface from raw pixel data.
+ * @param data A pointer to the pixel data buffer.
+ * @param w The width of the surface in pixels.
+ * @param h The height of the surface in pixels.
+ * @param format The pixel format of the data.
+ * @return A pointer to the newly created RGFW_surface.
+ *
+ * NOTE: when you create a surface using RGFW_createSurface / ptr, on X11 it uses the root window's visual
+ * this means it may fail to render on any other window if the visual does not match
+ * RGFW_window_createSurface and RGFW_window_createSurfacePtr exist only for X11 to address this issues
+ * Of course, you can also manually set the root window with RGFW_setRootWindow
+*/
+RGFWDEF RGFW_surface* RGFW_createSurface(u8* data, i32 w, i32 h, RGFW_format format);
+
+/**!
+ * @brief Creates a surface using a pre-allocated RGFW_surface structure.
+ * @param data A pointer to the pixel data buffer.
+ * @param w The width of the surface in pixels.
+ * @param h The height of the surface in pixels.
+ * @param format The pixel format of the data.
+ * @param surface A pointer to a pre-allocated RGFW_surface structure.
+ * @return RGFW_TRUE if successful, RGFW_FALSE otherwise.
+*/
+RGFWDEF RGFW_bool RGFW_createSurfacePtr(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface);
+
+/**!
+ * @brief Retrieves the native image associated with a surface.
+ * @param surface A pointer to the RGFW_surface.
+ * @return A pointer to the native RGFW_nativeImage associated with the surface.
+*/
+RGFWDEF RGFW_nativeImage* RGFW_surface_getNativeImage(RGFW_surface* surface);
+
+/**!
+ * @brief Frees the surface pointer and any buffers used for software rendering.
+ * @param surface A pointer to the RGFW_surface to free.
+*/
+RGFWDEF void RGFW_surface_free(RGFW_surface* surface);
+
+/**!
+ * @brief Frees only the internal buffers used for software rendering, leaving the surface struct intact.
+ * @param surface A pointer to the RGFW_surface whose buffers should be freed.
+*/
+RGFWDEF void RGFW_surface_freePtr(RGFW_surface* surface);
+
+
+/**!
+ * @brief Loads a mouse icon from bitmap data (similar to RGFW_window_setIcon).
+ * @param data A pointer to the bitmap pixel data.
+ * @param w The width of the mouse icon in pixels.
+ * @param h The height of the mouse icon in pixels.
+ * @param format The pixel format of the data.
+ * @return A pointer to the newly loaded RGFW_mouse structure.
+ *
+ * @note The icon is not resized by default.
+*/
+RGFWDEF RGFW_mouse* RGFW_loadMouse(u8* data, i32 w, i32 h, RGFW_format format);
+
+/**!
+ * @brief Frees the data associated with an RGFW_mouse structure.
+ * @param mouse A pointer to the RGFW_mouse to free.
+*/
+RGFWDEF void RGFW_freeMouse(RGFW_mouse* mouse);
+
+/**!
+ * @brief Get an allocated array of the supported modes of a monitor
+ * @param monitor the source monitor object
+ * @param count [OUTPUT] the count of the array
+ * @return the allocated array of supported modes
+*/
+RGFWDEF RGFW_monitorMode* RGFW_monitor_getModes(RGFW_monitor* monitor, size_t* count);
+
+/**!
+ * @brief Free RGFW allocated modes array
+ * @param monitor the source monitor object
+ * @param modes a pointer to an allocated array of modes
+*/
+RGFWDEF void RGFW_freeModes(RGFW_monitorMode* modes);
+
+/**!
+ * @brief Get the supported modes of a monitor using a pre-allocated array
+ * @param monitor the source monitor object
+ * @param modes [OUTPUT] a pointer to an allocated array of modes
+ * @return the number of (possible) modes, if [modes == NULL] the possible nodes *may* be less than the actual modes
+*/
+RGFWDEF size_t RGFW_monitor_getModesPtr(RGFW_monitor* monitor, RGFW_monitorMode** modes);
+
+/**!
+ * @brief find the closest monitor mode based on the give mode with size being the highest priority, format being the second and refreshrate being the third.
+ * @param monitor the source monitor object
+ * @param mode user filled mode to use for comparison
+ * @param modes [OUTPUT] a pointer to be filled with the output closest monitor
+ * @return returns true if a suitable monitor was found and false if no suitable monitor was found at all
+*/
+
+RGFWDEF RGFW_bool RGFW_monitor_findClosestMode(RGFW_monitor* monitor, RGFW_monitorMode* mode, RGFW_monitorMode* closest);
+
+/**!
+ * @brief Get the allocated gamma ramp
+ * @param monitor the source monitor object
+*/
+RGFWDEF RGFW_gammaRamp* RGFW_monitor_getGammaRamp(RGFW_monitor* monitor);
+
+/**!
+ * @brief Free the gamma ramp allocated by RGFW
+ * @param allocated gamma ramp
+*/
+RGFWDEF void RGFW_freeGammaRamp(RGFW_gammaRamp* ramp);
+
+/**!
+ * @brief Get the monitor's gamma ramp using a pre-allocated struct with allocated data
+ * @param monitor the source monitor object
+ * @param ramp [OUTPUT] a pointer to an allocated gamma ramp (can be NULL to just get the count)
+ * @return the count of the gamma ramp
+*/
+RGFWDEF size_t RGFW_monitor_getGammaRampPtr(RGFW_monitor* monitor, RGFW_gammaRamp* ramp);
+
+/**!
+ * @brief Set the monitor's gamma ramp using a pre-allocated struct with allocated data
+ * @param monitor the source monitor object
+ * @param ramp a pointer to an allocated gamma ramp
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_monitor_setGammaRamp(RGFW_monitor* monitor, RGFW_gammaRamp* ramp);
+
+/**!
+ * @brief Create and set the monitor's gamma ramp with a base gamma exponent
+ * @param monitor the source monitor object
+ * @param the gamma exponent
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_monitor_setGamma(RGFW_monitor* monitor, float gamma);
+
+/**!
+ * @brief Create and set the monitor's gamma ramp with a base gamma exponent using a pre-allocated array
+ * @param monitor the source monitor object
+ * @param gamma the gamma exponent
+ * @param pre-allocated gammaramp channel
+ * @param count the length of the allocated channel array
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_monitor_setGammaPtr(RGFW_monitor* monitor, float gamma, u16* ptr, size_t count);
+
+/**!
+ * @brief Get the workarea of a monitor, meaning the parts not occupied by OS graphics (i.e. the taskbar)
+ * @param monitor the source monitor object
+ * @param x [OUTPUT] the x pos of the workarea
+ * @param y [OUTPUT] the y pos of the workarea
+ * @param w [OUTPUT] the width of the workarea
+ * @param h [OUTPUT] the height of the workarea
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_monitor_getWorkarea(RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height);
+
+/**!
+ * @brief Get the position of a monitor (the same as monitor.x / monitor.y)
+ * @param x [OUTPUT] the x position of the monitor
+ * @param y [OUTPUT] the y position of the monitor
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_monitor_getPosition(RGFW_monitor* monitor, i32* x, i32* y);
+
+/**!
+ * @brief Get the name of a monitor (the same as monitor.name)
+ * @return the cstring of the monitor's name
+*/
+RGFWDEF const char* RGFW_monitor_getName(RGFW_monitor* monitor);
+
+/**!
+ * @brief Get the scale of a monitor (the same as monitor.scaleX / monitor.scaleY)
+ * @param monitor the source monitor object
+ * @param x [OUTPUT] the x scale of the monitor
+ * @param y [OUTPUT] the y scale of the monitor
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_monitor_getScale(RGFW_monitor* monitor, float* x, float* y);
+
+/**!
+ * @brief Get the physical size of a monitor (the same as monitor.physW / monitor.physH)
+ * @param monitor the source monitor object
+ * @param w [OUTPUT] the physical width of the monitor
+ * @param h [OUTPUT] the physical height of the monitor
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_monitor_getPhysicalSize(RGFW_monitor* monitor, float* w, float* h);
+
+/**!
+ * @brief Set the user pointer of a monitor (the same as monitor.userPtr = userPtr)
+ * @param monitor the source monitor object
+ * @param userPtr the new user pointer for the monitor
+*/
+RGFWDEF void RGFW_monitor_setUserPtr(RGFW_monitor* monitor, void* userPtr);
+
+/**!
+ * @brief Get the user pointer of a monitor (the same as monitor.userPtr)
+ * @param monitor the source monitor object
+ * @return the user pointer of the monitor
+*/
+RGFWDEF void* RGFW_monitor_getUserPtr(RGFW_monitor* monitor);
+
+/**!
+ * @brief Get the mode of a monitor (the same as monitor.mode)
+ * @param monitor the source monitor object
+ * @param mode [OUTPUT] current mode the monitor
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_monitor_getMode(RGFW_monitor* monitor, RGFW_monitorMode* mode);
+
+/**!
+ * @brief Poll and check for monitor updates (this is called internally on monitor update events and RGFW_init)
+*/
+RGFWDEF void RGFW_pollMonitors(void);
+
+/**!
+ * @brief Retrieves an array of all available monitors.
+ * @param len [OUTPUT] A pointer to store the number of monitors found (maximum of RGFW_MAX_MONITORS [6 by default]).
+ * @return An array of pointers to RGFW_monitor structures.
+*/
+RGFWDEF RGFW_monitor** RGFW_getMonitors(size_t* len);
+
+/**!
+ * @brief Retrieves the primary monitor.
+ * @return A pointer to the RGFW_monitor structure representing the primary monitor.
+*/
+RGFWDEF RGFW_monitor* RGFW_getPrimaryMonitor(void);
+
+/**!
+ * @brief Requests the display mode for a monitor (based on what attributes are directly requested).
+ * @param mon The monitor to apply the mode change to.
+ * @param mode The desired RGFW_monitorMode.
+ * @param request The RGFW_modeRequest describing how to handle the mode change.
+ * @return RGFW_TRUE if the mode was successfully applied, otherwise RGFW_FALSE.
+*/
+RGFWDEF RGFW_bool RGFW_monitor_requestMode(RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request);
+
+/**!
+ * @brief Sets a specific display mode for a monitor directly.
+ * @param mon The monitor to apply the mode change to.
+ * @param mode The desired RGFW_monitorMode.
+ * @param request The RGFW_modeRequest describing how to handle the mode change.
+ * @return RGFW_TRUE if the mode was successfully applied, otherwise RGFW_FALSE.
+*/
+RGFWDEF RGFW_bool RGFW_monitor_setMode(RGFW_monitor* mon, RGFW_monitorMode* mode);
+
+/**!
+ * @brief Compares two monitor modes to check if they are equivalent.
+ * @param mon The first monitor mode.
+ * @param mon2 The second monitor mode.
+ * @param request The RGFW_modeRequest that defines the comparison parameters.
+ * @return RGFW_TRUE if both modes are equivalent, otherwise RGFW_FALSE.
+*/
+RGFWDEF RGFW_bool RGFW_monitorModeCompare(RGFW_monitorMode* mon, RGFW_monitorMode* mon2, RGFW_modeRequest request);
+
+/**!
+ * @brief Scales a monitor’s mode to match a window’s size.
+ * @param mon The monitor to be scaled.
+ * @param win The window whose size should be used as a reference.
+ * @return RGFW_TRUE if the scaling was successful, otherwise RGFW_FALSE.
+*/
+RGFWDEF RGFW_bool RGFW_monitor_scaleToWindow(RGFW_monitor* mon, struct RGFW_window* win);
+
+ /**!
+ * @brief set (enable or disable) raw mouse mode globally
+ * @param the boolean state of raw mouse mode
+ *
+*/
+RGFWDEF void RGFW_setRawMouseMode(RGFW_bool state);
+
+/**!
+* @brief sleep until RGFW gets an event or the timer ends (defined by OS)
+* @param waitMS how long to wait for the next event (in miliseconds)
+*/
+RGFWDEF void RGFW_waitForEvent(i32 waitMS);
+
+/**!
+* @brief Set if events should be queued or not (enabled by default if the event queue is checked)
+* @param queue boolean value if RGFW should queue events or not
+*/
+RGFWDEF void RGFW_setQueueEvents(RGFW_bool queue);
+
+/**!
+* @brief check all the events until there are none left and updates window structure attributes
+*/
+RGFWDEF void RGFW_pollEvents(void);
+
+/**!
+* @brief check all the events until there are none left and updates window structure attributes
+* queues events if the queue is checked and/or requested
+*/
+RGFWDEF void RGFW_stopCheckEvents(void);
+
+/**!
+ * @brief polls and pops the next event
+ * @param event [OUTPUT] a pointer to store the retrieved event
+ * @return RGFW_TRUE if an event was found, RGFW_FALSE otherwise
+ *
+ * NOTE: Using this function without a loop may cause event lag.
+ * For multi-threaded systems, use RGFW_pollEvents combined with RGFW_checkQueuedEvent.
+ *
+ * Example:
+ * RGFW_event event;
+ * while (RGFW_checkEvent(win, &event)) {
+ * // handle event
+ * }
+*/
+RGFWDEF RGFW_bool RGFW_checkEvent(RGFW_event* event);
+
+/**!
+ * @brief pops the first queued event
+ * @param event [OUTPUT] a pointer to store the retrieved event
+ * @return RGFW_TRUE if an event was found, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_checkQueuedEvent(RGFW_event* event);
+
+/** * @defgroup Input
+* @{ */
+
+/**!
+ * @brief returns true if the key is pressed during the current frame
+ * @param key the key code of the key you want to check
+ * @return The boolean value if the key is pressed or not
+*/
+RGFWDEF RGFW_bool RGFW_isKeyPressed(RGFW_key key);
+
+/**!
+ * @brief returns true if the key was released during the current frame
+ * @param key the key code of the key you want to check
+ * @return The boolean value if the key is released or not
+*/
+RGFWDEF RGFW_bool RGFW_isKeyReleased(RGFW_key key);
+
+/**!
+ * @brief returns true if the key is down
+ * @param key the key code of the key you want to check
+ * @return The boolean value if the key is down or not
+*/
+RGFWDEF RGFW_bool RGFW_isKeyDown(RGFW_key key);
+
+/**!
+ * @brief returns true if the mouse button is pressed during the current frame
+ * @param button the mouse button code of the button you want to check
+ * @return The boolean value if the button is pressed or not
+*/
+RGFWDEF RGFW_bool RGFW_isMousePressed(RGFW_mouseButton button);
+
+/**!
+ * @brief returns true if the mouse button is released during the current frame
+ * @param button the mouse button code of the button you want to check
+ * @return The boolean value if the button is released or not
+*/
+RGFWDEF RGFW_bool RGFW_isMouseReleased(RGFW_mouseButton button);
+
+/**!
+ * @brief returns true if the mouse button is down
+ * @param button the mouse button code of the button you want to check
+ * @return The boolean value if the button is down or not
+*/
+RGFWDEF RGFW_bool RGFW_isMouseDown(RGFW_mouseButton button);
+
+/**!
+ * @brief outputs the current x, y position of the mouse
+ * @param X [OUTPUT] a pointer for the output X value
+ * @param Y [OUTPUT] a pointer for the output Y value
+*/
+RGFWDEF void RGFW_getMouseScroll(float* x, float* y);
+
+/**!
+ * @brief outputs the current x, y movement vector of the mouse
+ * @param X [OUTPUT] a pointer for the output X vector value
+ * @param Y [OUTPUT] a pointer for the output Y vector value
+*/
+RGFWDEF void RGFW_getMouseVector(float* x, float* y);
+/** @} */
+
+/**!
+ * @brief creates a new window
+ * @param name the requested title of the window
+ * @param x the requested x position of the window
+ * @param y the requested y position of the window
+ * @param w the requested width of the window
+ * @param h the requested height of the window
+ * @param flags extra arguments ((u32)0 means no flags used)
+ * @return A pointer to the newly created window structure
+ *
+ * NOTE: (windows) if the executable has an icon resource named RGFW_ICON, it will be set as the initial icon for the window
+*/
+RGFWDEF RGFW_window* RGFW_createWindow(const char* name, i32 x, i32 y, i32 w, i32 h, RGFW_windowFlags flags);
+
+/**!
+ * @brief creates a new window using a pre-allocated window structure
+ * @param name the requested title of the window
+ * @param x the requested x position of the window
+ * @param y the requested y position of the window
+ * @param w the requested width of the window
+ * @param h the requested height of the window
+ * @param flags extra arguments ((u32)0 means no flags used)
+ * @param win a pointer the pre-allocated window structure
+ * @return A pointer to the newly created window structure
+*/
+RGFWDEF RGFW_window* RGFW_createWindowPtr(const char* name, i32 x, i32 y, i32 w, i32 h, RGFW_windowFlags flags, RGFW_window* win);
+
+/**!
+ * @brief creates a new surface structure
+ * @param win the source window of the surface
+ * @param data a pointer to the raw data of the structure (you allocate this)
+ * @param w the width the data
+ * @param h the height of the data
+ * @return A pointer to the newly created surface structure
+ *
+ * NOTE: when you create a surface using RGFW_createSurface / ptr, on X11 it uses the root window's visual
+ * this means it may fail to render on any other window if the visual does not match
+ * RGFW_window_createSurface and RGFW_window_createSurfacePtr exist only for X11 to address this issues
+ * Of course, you can also manually set the root window with RGFW_setRootWindow
+ */
+RGFWDEF RGFW_surface* RGFW_window_createSurface(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format);
+
+/**!
+ * @brief creates a new surface structure using a pre-allocated surface structure
+ * @param win the source window of the surface
+ * @param data a pointer to the raw data of the structure (you allocate this)
+ * @param w the width the data
+ * @param h the height of the data
+ * @param a pointer to the pre-allocated surface structure
+ * @return a bool if the creation was successful or not
+*/
+RGFWDEF RGFW_bool RGFW_window_createSurfacePtr(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface);
+
+/**!
+ * @brief set the function/callback used for converting surface data between formats
+ * @param surface a pointer to the surface
+ * @param a function pointer for the function to use [if NULL the default function is used]
+*/
+RGFWDEF void RGFW_surface_setConvertFunc(RGFW_surface* surface, RGFW_convertImageDataFunc func);
+
+/**!
+ * @brief blits a surface stucture to the window
+ * @param win a pointer the window to blit to
+ * @param surface a pointer to the surface
+*/
+RGFWDEF void RGFW_window_blitSurface(RGFW_window* win, RGFW_surface* surface);
+
+/**!
+ * @brief gets the position of the window | with RGFW_window.x and window.y
+ * @param x [OUTPUT] the x position of the window
+ * @param y [OUTPUT] the y position of the window
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_window_getPosition(RGFW_window* win, i32* x, i32* y); /*!< */
+
+/**!
+ * @brief gets the size of the window | with RGFW_window.w and window.h
+ * @param win a pointer to the window
+ * @param w [OUTPUT] the width of the window
+ * @param h [OUTPUT] the height of the window
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_window_getSize(RGFW_window* win, i32* w, i32* h);
+
+/**!
+ * @brief gets the size of the window in exact pixels
+ * @param win a pointer to the window
+ * @param w [OUTPUT] the width of the window
+ * @param h [OUTPUT] the height of the window
+ * @return a bool if the function was successful
+*/
+RGFWDEF RGFW_bool RGFW_window_getSizeInPixels(RGFW_window* win, i32* w, i32* h);
+
+/**!
+ * @brief gets the flags of the window | returns RGFW_window._flags
+ * @param win a pointer to the window
+ * @return the window flags
+*/
+RGFWDEF u32 RGFW_window_getFlags(RGFW_window* win);
+
+/**!
+ * @brief returns the exit key assigned to the window
+ * @param win a pointer to the target window
+ * @return The key code assigned as the exit key
+*/
+RGFWDEF RGFW_key RGFW_window_getExitKey(RGFW_window* win);
+
+/**!
+ * @brief sets the exit key for the window
+ * @param win a pointer to the target window
+ * @param key the key code to assign as the exit key
+*/
+RGFWDEF void RGFW_window_setExitKey(RGFW_window* win, RGFW_key key);
+
+/**!
+ * @brief sets the types of events you want the window to receive
+ * @param win a pointer to the target window
+ * @param events the event flags to enable (use RGFW_allEventFlags for all)
+*/
+RGFWDEF void RGFW_window_setEnabledEvents(RGFW_window* win, RGFW_eventFlag events);
+
+/**!
+ * @brief gets the currently enabled events for the window
+ * @param win a pointer to the target window
+ * @return The enabled event flags for the window
+*/
+RGFWDEF RGFW_eventFlag RGFW_window_getEnabledEvents(RGFW_window* win);
+
+/**!
+ * @brief enables all events and disables selected ones
+ * @param win a pointer to the target window
+ * @param events the event flags to disable
+*/
+RGFWDEF void RGFW_window_setDisabledEvents(RGFW_window* win, RGFW_eventFlag events);
+
+/**!
+ * @brief directly enables or disables a specific event or group of events
+ * @param win a pointer to the target window
+ * @param event the event flag or group of flags to modify
+ * @param state RGFW_TRUE to enable, RGFW_FALSE to disable
+*/
+RGFWDEF void RGFW_window_setEventState(RGFW_window* win, RGFW_eventFlag event, RGFW_bool state);
+
+/**!
+ * @brief gets the user pointer associated with the window
+ * @param win a pointer to the target window
+ * @return The user-defined pointer stored in the window
+*/
+RGFWDEF void* RGFW_window_getUserPtr(RGFW_window* win);
+
+/**!
+ * @brief sets a user pointer for the window
+ * @param win a pointer to the target window
+ * @param ptr a pointer to associate with the window
+*/
+RGFWDEF void RGFW_window_setUserPtr(RGFW_window* win, void* ptr);
+
+/**!
+ * @brief retrieves the platform-specific window source pointer
+ * @param win a pointer to the target window
+ * @return A pointer to the internal RGFW_window_src structure
+*/
+RGFWDEF RGFW_window_src* RGFW_window_getSrc(RGFW_window* win);
+
+/**!
+ * @brief sets the macOS layer object associated with the window
+ * @param win a pointer to the target window
+ * @param layer a pointer to the macOS layer object
+ * @note Only available on macOS platforms
+*/
+RGFWDEF void RGFW_window_setLayer_OSX(RGFW_window* win, void* layer);
+
+/**!
+ * @brief retrieves the macOS view object associated with the window
+ * @param win a pointer to the target window
+ * @return A pointer to the macOS view object, or NULL if not on macOS
+*/
+RGFWDEF void* RGFW_window_getView_OSX(RGFW_window* win);
+
+/**!
+ * @brief retrieves the macOS window object
+ * @param win a pointer to the target window
+ * @return A pointer to the macOS window object, or NULL if not on macOS
+*/
+RGFWDEF void* RGFW_window_getWindow_OSX(RGFW_window* win);
+
+/**!
+ * @brief retrieves the HWND handle for the window
+ * @param win a pointer to the target window
+ * @return A pointer to the Windows HWND handle, or NULL if not on Windows
+*/
+RGFWDEF void* RGFW_window_getHWND(RGFW_window* win);
+
+/**!
+ * @brief retrieves the HDC handle for the window
+ * @param win a pointer to the target window
+ * @return A pointer to the Windows HDC handle, or NULL if not on Windows
+*/
+RGFWDEF void* RGFW_window_getHDC(RGFW_window* win);
+
+/**!
+ * @brief retrieves the X11 Window handle for the window
+ * @param win a pointer to the target window
+ * @return The X11 Window handle, or 0 if not on X11
+*/
+RGFWDEF u64 RGFW_window_getWindow_X11(RGFW_window* win);
+
+/**!
+ * @brief retrieves the Wayland surface handle for the window
+ * @param win a pointer to the target window
+ * @return A pointer to the Wayland wl_surface, or NULL if not on Wayland
+*/
+RGFWDEF struct wl_surface* RGFW_window_getWindow_Wayland(RGFW_window* win);
+
+/** * @defgroup Window_management
+* @{ */
+
+/*! set the window flags (will undo flags if they don't match the old ones) */
+RGFWDEF void RGFW_window_setFlags(RGFW_window* win, RGFW_windowFlags);
+
+/**!
+ * @brief polls and pops the next event with the matching target window in event queue, pushes back events that don't match
+ * @param win a pointer to the target window
+ * @param event [OUTPUT] a pointer to store the retrieved event
+ * @return RGFW_TRUE if an event was found, RGFW_FALSE otherwise
+ *
+ * NOTE: Using this function without a loop may cause event lag.
+ * For multi-threaded systems, use RGFW_pollEvents combined with RGFW_window_checkQueuedEvent.
+ *
+ * Example:
+ * RGFW_event event;
+ * while (RGFW_window_checkEvent(win, &event)) {
+ * // handle event
+ * }
+*/
+RGFWDEF RGFW_bool RGFW_window_checkEvent(RGFW_window* win, RGFW_event* event);
+
+/**!
+ * @brief pops the first queued event with the matching target window, pushes back events that don't match
+ * @param win a pointer to the target window
+ * @param event [OUTPUT] a pointer to store the retrieved event
+ * @return RGFW_TRUE if an event was found, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_checkQueuedEvent(RGFW_window* win, RGFW_event* event);
+
+/**!
+ * @brief checks if a key was pressed while the window is in focus
+ * @param win a pointer to the target window
+ * @param key the key code to check
+ * @return RGFW_TRUE if the key was pressed, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_isKeyPressed(RGFW_window* win, RGFW_key key);
+
+/**!
+ * @brief checks if a key is currently being held down
+ * @param win a pointer to the target window
+ * @param key the key code to check
+ * @return RGFW_TRUE if the key is held down, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_isKeyDown(RGFW_window* win, RGFW_key key);
+
+/**!
+ * @brief checks if a key was released
+ * @param win a pointer to the target window
+ * @param key the key code to check
+ * @return RGFW_TRUE if the key was released, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_isKeyReleased(RGFW_window* win, RGFW_key key);
+
+/**!
+ * @brief checks if a mouse button was pressed
+ * @param win a pointer to the target window
+ * @param button the mouse button code to check
+ * @return RGFW_TRUE if the mouse button was pressed, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_isMousePressed(RGFW_window* win, RGFW_mouseButton button);
+
+/**!
+ * @brief checks if a mouse button is currently held down
+ * @param win a pointer to the target window
+ * @param button the mouse button code to check
+ * @return RGFW_TRUE if the mouse button is down, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_isMouseDown(RGFW_window* win, RGFW_mouseButton button);
+
+/**!
+ * @brief checks if a mouse button was released
+ * @param win a pointer to the target window
+ * @param button the mouse button code to check
+ * @return RGFW_TRUE if the mouse button was released, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_isMouseReleased(RGFW_window* win, RGFW_mouseButton button);
+
+/**!
+ * @brief checks if the mouse left the window (true only for the first frame)
+ * @param win a pointer to the target window
+ * @return RGFW_TRUE if the mouse left, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_didMouseLeave(RGFW_window* win);
+
+/**!
+ * @brief checks if the mouse entered the window (true only for the first frame)
+ * @param win a pointer to the target window
+ * @return RGFW_TRUE if the mouse entered, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_didMouseEnter(RGFW_window* win);
+
+/**!
+ * @brief checks if the mouse is currently inside the window bounds
+ * @param win a pointer to the target window
+ * @return RGFW_TRUE if the mouse is inside, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_isMouseInside(RGFW_window* win);
+
+/**!
+ * @brief checks if there is data being dragged into or within the window
+ * @param win a pointer to the target window
+ * @return RGFW_TRUE if data is being dragged, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_isDataDragging(RGFW_window* win);
+
+/**!
+ * @brief gets the position of a data drag
+ * @param win a pointer to the target window
+ * @param x [OUTPUT] pointer to store the x position
+ * @param y [OUTPUT] pointer to store the y position
+ * @return RGFW_TRUE if there is an active drag, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_getDataDrag(RGFW_window* win, i32* x, i32* y);
+
+/**!
+ * @brief checks if a data drop occurred in the window (first frame only)
+ * @param win a pointer to the target window
+ * @return RGFW_TRUE if data was dropped, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_didDataDrop(RGFW_window* win);
+
+/**!
+ * @brief retrieves files from a data drop (drag and drop)
+ * @param win a pointer to the target window
+ * @param files [OUTPUT] a pointer to the array of file paths
+ * @param count [OUTPUT] the number of dropped files
+ * @return RGFW_TRUE if a data drop occurred, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_getDataDrop(RGFW_window* win, const char*** files, size_t* count);
+
+/**!
+ * @brief closes the window and frees its associated structure
+ * @param win a pointer to the target window
+*/
+RGFWDEF void RGFW_window_close(RGFW_window* win);
+
+/**!
+ * @brief closes the window without freeing its structure
+ * @param win a pointer to the target window
+*/
+RGFWDEF void RGFW_window_closePtr(RGFW_window* win);
+
+/**!
+ * @brief moves the window to a new position on the screen
+ * @param win a pointer to the target window
+ * @param x the new x position
+ * @param y the new y position
+*/
+RGFWDEF void RGFW_window_move(RGFW_window* win, i32 x, i32 y);
+
+/**!
+ * @brief moves the window to a specific monitor
+ * @param win a pointer to the target window
+ * @param m the target monitor
+*/
+RGFWDEF void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor* m);
+
+/**!
+ * @brief resizes the window to the given dimensions
+ * @param win a pointer to the target window
+ * @param w the new width
+ * @param h the new height
+*/
+RGFWDEF void RGFW_window_resize(RGFW_window* win, i32 w, i32 h);
+
+/**!
+ * @brief sets the aspect ratio of the window
+ * @param win a pointer to the target window
+ * @param w the width ratio
+ * @param h the height ratio
+*/
+RGFWDEF void RGFW_window_setAspectRatio(RGFW_window* win, i32 w, i32 h);
+
+/**!
+ * @brief sets the minimum size of the window
+ * @param win a pointer to the target window
+ * @param w the minimum width
+ * @param h the minimum height
+*/
+RGFWDEF void RGFW_window_setMinSize(RGFW_window* win, i32 w, i32 h);
+
+/**!
+ * @brief sets the maximum size of the window
+ * @param win a pointer to the target window
+ * @param w the maximum width
+ * @param h the maximum height
+*/
+RGFWDEF void RGFW_window_setMaxSize(RGFW_window* win, i32 w, i32 h);
+
+/**!
+ * @brief sets focus to the window
+ * @param win a pointer to the target window
+*/
+RGFWDEF void RGFW_window_focus(RGFW_window* win);
+
+/**!
+ * @brief checks if the window is currently in focus
+ * @param win a pointer to the target window
+ * @return RGFW_TRUE if the window is in focus, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_isInFocus(RGFW_window* win);
+
+/**!
+ * @brief raises the window to the top of the stack
+ * @param win a pointer to the target window
+*/
+RGFWDEF void RGFW_window_raise(RGFW_window* win);
+
+/**!
+ * @brief maximizes the window
+ * @param win a pointer to the target window
+*/
+RGFWDEF void RGFW_window_maximize(RGFW_window* win);
+
+/**!
+ * @brief toggles fullscreen mode for the window
+ * @param win a pointer to the target window
+ * @param fullscreen RGFW_TRUE to enable fullscreen, RGFW_FALSE to disable
+*/
+RGFWDEF void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen);
+
+/**!
+ * @brief centers the window on the screen
+ * @param win a pointer to the target window
+*/
+RGFWDEF void RGFW_window_center(RGFW_window* win);
+
+/**!
+ * @brief minimizes the window
+ * @param win a pointer to the target window
+*/
+RGFWDEF void RGFW_window_minimize(RGFW_window* win);
+
+/**!
+ * @brief restores the window from minimized state
+ * @param win a pointer to the target window
+*/
+RGFWDEF void RGFW_window_restore(RGFW_window* win);
+
+/**!
+ * @brief makes the window a floating window
+ * @param win a pointer to the target window
+ * @param floating RGFW_TRUE to float, RGFW_FALSE to disable
+*/
+RGFWDEF void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating);
+
+/**!
+ * @brief sets the opacity level of the window
+ * @param win a pointer to the target window
+ * @param opacity the opacity level (0–255)
+*/
+RGFWDEF void RGFW_window_setOpacity(RGFW_window* win, u8 opacity);
+
+/**!
+ * @brief toggles window borders
+ * @param win a pointer to the target window
+ * @param border RGFW_TRUE for bordered, RGFW_FALSE for borderless
+*/
+RGFWDEF void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border);
+
+/**!
+ * @brief checks if the window is borderless
+ * @param win a pointer to the target window
+ * @return RGFW_TRUE if borderless, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_borderless(RGFW_window* win);
+
+/**!
+ * @brief toggles drag-and-drop (DND) support for the window
+ * @param win a pointer to the target window
+ * @param allow RGFW_TRUE to allow DND, RGFW_FALSE to disable
+ * @note RGFW_windowAllowDND must still be passed when creating the window
+*/
+RGFWDEF void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow);
+
+/**!
+ * @brief checks if drag-and-drop (DND) is allowed
+ * @param win a pointer to the target window
+ * @return RGFW_TRUE if DND is enabled, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_allowsDND(RGFW_window* win);
+
+#ifndef RGFW_NO_PASSTHROUGH
+/**!
+ * @brief toggles mouse passthrough for the window
+ * @param win a pointer to the target window
+ * @param passthrough RGFW_TRUE to enable passthrough, RGFW_FALSE to disable
+*/
+RGFWDEF void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough);
+#endif
+
+/**!
+ * @brief renames the window
+ * @param win a pointer to the target window
+ * @param name the new title string for the window
+*/
+RGFWDEF void RGFW_window_setName(RGFW_window* win, const char* name);
+
+/**!
+ * @brief sets the icon for the window and taskbar
+ * @param win a pointer to the target window
+ * @param data the image data
+ * @param w the width of the icon
+ * @param h the height of the icon
+ * @param format the image format
+ * @return RGFW_TRUE if successful, RGFW_FALSE otherwise
+ *
+ * NOTE: The image may be resized by default.
+*/
+RGFWDEF RGFW_bool RGFW_window_setIcon(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format);
+
+/**!
+ * @brief sets the icon for the window and/or taskbar
+ * @param win a pointer to the target window
+ * @param data the image data
+ * @param w the width of the icon
+ * @param h the height of the icon
+ * @param format the image format
+ * @param type the target icon type (taskbar, window, or both)
+ * @return RGFW_TRUE if successful, RGFW_FALSE otherwise
+*/
+RGFWDEF RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_icon type);
+
+/**!
+ * @brief sets the mouse icon for the window using a loaded bitmap
+ * @param win a pointer to the target window
+ * @param mouse a pointer to the RGFW_mouse struct containing the icon
+*/
+RGFWDEF void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse);
+
+/**!
+ * @brief Sets the mouse to a standard system cursor.
+ * @param win The target window.
+ * @param mouse The standard cursor type (see RGFW_MOUSE enum).
+ * @return True if the standard cursor was successfully applied.
+*/
+RGFWDEF RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, RGFW_mouseIcons mouse);
+
+/**!
+ * @brief Sets the mouse to the default cursor icon.
+ * @param win The target window.
+ * @return True if the default cursor was successfully set.
+*/
+RGFWDEF RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win);
+
+/**!
+ * @brief set (enable or disable) raw mouse mode only for the select window
+ * @param win The target window.
+ * @param the boolean state of raw mouse mode
+ *
+*/
+RGFWDEF void RGFW_window_setRawMouseMode(RGFW_window* win, RGFW_bool state);
+
+/**!
+ * @brief lock/unlock the cursor.
+ * @param win The target window.
+ * @param the boolean state of the mouse's capture state
+ *
+*/
+RGFWDEF void RGFW_window_captureMouse(RGFW_window* win, RGFW_bool state);
+
+/**!
+ * @brief lock/unlock the cursor and enable raw mpuise mode.
+ * @param win The target window.
+ * @param the boolean state of raw mouse mode
+ *
+*/
+RGFWDEF void RGFW_window_captureRawMouse(RGFW_window* win, RGFW_bool state);
+
+/**!
+ * @brief Returns true if the mouse is using raw mouse mode
+ * @param win The target window.
+ * @return True if the mouse is using raw mouse input mode.
+*/
+RGFWDEF RGFW_bool RGFW_window_isRawMouseMode(RGFW_window* win);
+
+
+/**!
+ * @brief Returns true if the mouse is captured
+ * @param win The target window.
+ * @return True if the mouse is being captured.
+*/
+RGFWDEF RGFW_bool RGFW_window_isCaptured(RGFW_window* win);
+
+/**!
+ * @brief Hides the window from view.
+ * @param win The target window.
+*/
+RGFWDEF void RGFW_window_hide(RGFW_window* win);
+
+/**!
+ * @brief Shows the window if it was hidden.
+ * @param win The target window.
+*/
+RGFWDEF void RGFW_window_show(RGFW_window* win);
+
+/**!
+ * @breif request a window flash to get attention from the user
+ * @param win the target window
+ * @param request the flash operation requested
+*/
+RGFWDEF void RGFW_window_flash(RGFW_window* win, RGFW_flashRequest request);
+
+/**!
+ * @brief Sets whether the window should close.
+ * @param win The target window.
+ * @param shouldClose True to signal the window should close, false to keep it open.
+ *
+ * This can override or trigger the `RGFW_window_shouldClose` state by modifying window flags.
+*/
+RGFWDEF void RGFW_window_setShouldClose(RGFW_window* win, RGFW_bool shouldClose);
+
+/**!
+ * @brief Retrieves the current global mouse position.
+ * @param x [OUTPUT] Pointer to store the X position of the mouse on the screen.
+ * @param y [OUTPUT] Pointer to store the Y position of the mouse on the screen.
+ * @return True if the position was successfully retrieved.
+*/
+RGFWDEF RGFW_bool RGFW_getGlobalMouse(i32* x, i32* y);
+
+/**!
+ * @brief Retrieves the mouse position relative to the window.
+ * @param win The target window.
+ * @param x [OUTPUT] Pointer to store the X position within the window.
+ * @param y [OUTPUT] Pointer to store the Y position within the window.
+ * @return True if the position was successfully retrieved.
+*/
+RGFWDEF RGFW_bool RGFW_window_getMouse(RGFW_window* win, i32* x, i32* y);
+
+/**!
+ * @brief Shows or hides the mouse cursor for the window.
+ * @param win The target window.
+ * @param show True to show the mouse, false to hide it.
+*/
+RGFWDEF void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show);
+
+/**!
+ * @brief Checks if the mouse is currently hidden in the window.
+ * @param win The target window.
+ * @return True if the mouse is hidden.
+*/
+RGFWDEF RGFW_bool RGFW_window_isMouseHidden(RGFW_window* win);
+
+/**!
+ * @brief Moves the mouse to the specified position within the window.
+ * @param win The target window.
+ * @param x The new X position.
+ * @param y The new Y position.
+*/
+RGFWDEF void RGFW_window_moveMouse(RGFW_window* win, i32 x, i32 y);
+
+/**!
+ * @brief Checks if the window should close.
+ * @param win The target window.
+ * @return True if the window should close (for example, if ESC was pressed or a close event occurred).
+*/
+RGFWDEF RGFW_bool RGFW_window_shouldClose(RGFW_window* win);
+
+/**!
+ * @brief Checks if the window is currently fullscreen.
+ * @param win The target window.
+ * @return True if the window is fullscreen.
+*/
+RGFWDEF RGFW_bool RGFW_window_isFullscreen(RGFW_window* win);
+
+/**!
+ * @brief Checks if the window is currently hidden.
+ * @param win The target window.
+ * @return True if the window is hidden.
+*/
+RGFWDEF RGFW_bool RGFW_window_isHidden(RGFW_window* win);
+
+/**!
+ * @brief Checks if the window is minimized.
+ * @param win The target window.
+ * @return True if the window is minimized.
+*/
+RGFWDEF RGFW_bool RGFW_window_isMinimized(RGFW_window* win);
+
+/**!
+ * @brief Checks if the window is maximized.
+ * @param win The target window.
+ * @return True if the window is maximized.
+*/
+RGFWDEF RGFW_bool RGFW_window_isMaximized(RGFW_window* win);
+
+/**!
+ * @brief Checks if the window is floating.
+ * @param win The target window.
+ * @return True if the window is floating.
+*/
+RGFWDEF RGFW_bool RGFW_window_isFloating(RGFW_window* win);
+/** @} */
+
+/** * @defgroup Monitor
+* @{ */
+
+/**!
+ * @brief Scales the window to match its monitor’s resolution.
+ * @param win The target window.
+ *
+ * This function is automatically called when the flag `RGFW_scaleToMonitor`
+ * is used during window creation.
+*/
+RGFWDEF void RGFW_window_scaleToMonitor(RGFW_window* win);
+
+/**!
+ * @brief Retrieves the monitor structure associated with the window.
+ * @param win The target window.
+ * @return The monitor structure of the window.
+*/
+RGFWDEF RGFW_monitor* RGFW_window_getMonitor(RGFW_window* win);
+
+/** @} */
+
+/** * @defgroup Clipboard
+* @{ */
+
+/**!
+ * @brief Reads clipboard data.
+ * @param size [OUTPUT] A pointer that will be filled with the size of the clipboard data.
+ * @return A pointer to the clipboard data as a string.
+*/
+RGFWDEF const char* RGFW_readClipboard(size_t* size);
+
+/**!
+ * @brief Reads clipboard data into a provided buffer, or returns the required length if str is NULL.
+ * @param str [OUTPUT] A pointer to the buffer that will receive the clipboard data (or NULL to get required size).
+ * @param strCapacity The capacity of the provided buffer.
+ * @return The number of bytes read or required length of clipboard data.
+*/
+RGFWDEF RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity);
+
+/**!
+ * @brief Writes text to the clipboard.
+ * @param text The text to be written to the clipboard.
+ * @param textLen The length of the text being written.
+*/
+RGFWDEF void RGFW_writeClipboard(const char* text, u32 textLen);
+/** @} */
+
+
+
+/** * @defgroup error handling
+* @{ */
+/**!
+ * @brief Sets the callback function to handle debug messages from RGFW.
+ * @param func The function pointer to be used as the debug callback.
+ * @return The previously set debug callback function.
+*/
+RGFWDEF RGFW_debugfunc RGFW_setDebugCallback(RGFW_debugfunc func);
+
+/**!
+ * @brief Sends a debug message manually through the currently set debug callback.
+ * @param type The type of debug message being sent.
+ * @param err The associated error code.
+ * @param msg The debug message text.
+*/
+RGFWDEF void RGFW_sendDebugInfo(RGFW_debugType type, RGFW_errorCode err, const char* msg);
+/** @} */
+
+/**
+
+
+ event callbacks.
+ These are completely optional, so you can use the normal
+ RGFW_checkEvent() method if you prefer that
+
+* @defgroup Callbacks
+* @{
+*/
+
+/**!
+ * @brief Sets the callback function for window move events.
+ * @param func The function to be called when the window is moved.
+ * @return The previously set callback function, if any.
+*/
+RGFWDEF RGFW_windowMovedfunc RGFW_setWindowMovedCallback(RGFW_windowMovedfunc func);
+
+/**!
+ * @brief Sets the callback function for window resize events.
+ * @param func The function to be called when the window is resized.
+ * @return The previously set callback function, if any.
+*/
+RGFWDEF RGFW_windowResizedfunc RGFW_setWindowResizedCallback(RGFW_windowResizedfunc func);
+
+/**!
+ * @brief Sets the callback function for window quit events.
+ * @param func The function to be called when the window receives a quit signal.
+ * @return The previously set callback function, if any.
+*/
+RGFWDEF RGFW_windowQuitfunc RGFW_setWindowQuitCallback(RGFW_windowQuitfunc func);
+
+/**!
+ * @brief Sets the callback function for mouse move events.
+ * @param func The function to be called when the mouse moves within the window.
+ * @return The previously set callback function, if any.
+*/
+RGFWDEF RGFW_mousePosfunc RGFW_setMousePosCallback(RGFW_mousePosfunc func);
+
+/**!
+ * @brief Sets the callback function for window refresh events.
+ * @param func The function to be called when the window needs to be refreshed.
+ * @return The previously set callback function, if any.
+*/
+RGFWDEF RGFW_windowRefreshfunc RGFW_setWindowRefreshCallback(RGFW_windowRefreshfunc func);
+
+/**!
+ * @brief Sets the callback function for focus change events.
+ * @param func The function to be called when the window gains or loses focus.
+ * @return The previously set callback function, if any.
+*/
+RGFWDEF RGFW_focusfunc RGFW_setFocusCallback(RGFW_focusfunc func);
+
+/**!
+ * @brief Sets the callback function for mouse notification events.
+ * @param func The function to be called when a mouse notification event occurs.
+ * @return The previously set callback function, if any.
+*/
+RGFWDEF RGFW_mouseNotifyfunc RGFW_setMouseNotifyCallback(RGFW_mouseNotifyfunc func);
+
+/**!
+ * @brief Sets the callback function for data drop events.
+ * @param func The function to be called when data is dropped into the window.
+ * @return The previously set callback function, if any.
+*/
+RGFWDEF RGFW_dataDropfunc RGFW_setDataDropCallback(RGFW_dataDropfunc func);
+
+/**!
+ * @brief Sets the callback function for the start of a data drag event.
+ * @param func The function to be called when data dragging begins.
+ * @return The previously set callback function, if any.
+*/
+RGFWDEF RGFW_dataDragfunc RGFW_setDataDragCallback(RGFW_dataDragfunc func);
+
+/**!
+ * @brief Sets the callback function for key press and release events.
+ * @param func The function to be called when a key is pressed or released.
+ * @return The previously set callback function, if any.
+*/
+RGFWDEF RGFW_keyfunc RGFW_setKeyCallback(RGFW_keyfunc func);
+
+/**!
+ * @brief Sets the callback function for key character events.
+ * @param func The function to be called when a key is pressed or released.
+ * @return The previously set callback function, if any.
+*/
+RGFWDEF RGFW_keyCharfunc RGFW_setKeyCharCallback(RGFW_keyCharfunc func);
+
+/**!
+ * @brief Sets the callback function for mouse button press and release events.
+ * @param func The function to be called when a mouse button is pressed or released.
+ * @return The previously set callback function, if any.
+*/
+RGFWDEF RGFW_mouseButtonfunc RGFW_setMouseButtonCallback(RGFW_mouseButtonfunc func);
+
+/**!
+ * @brief Sets the callback function for mouse scroll events.
+ * @param func The function to be called when the mouse wheel is scrolled.
+ * @return The previously set callback function, if any.
+*/
+RGFWDEF RGFW_mouseScrollfunc RGFW_setMouseScrollCallback(RGFW_mouseScrollfunc func);
+
+/**!
+ * @brief Sets the callback function for window maximize events.
+ * @param func The function to be called when the window is maximized.
+ * @return The previously set callback function, if any.
+*/
+RGFWDEF RGFW_windowMaximizedfunc RGFW_setWindowMaximizedCallback(RGFW_windowMaximizedfunc func);
+
+/**!
+ * @brief Sets the callback function for window minimize events.
+ * @param func The function to be called when the window is minimized.
+ * @return The previously set callback function, if any.
+*/
+RGFWDEF RGFW_windowMinimizedfunc RGFW_setWindowMinimizedCallback(RGFW_windowMinimizedfunc func);
+
+/**!
+ * @brief Sets the callback function for window restore events.
+ * @param func The function to be called when the window is restored from a minimized or maximized state.
+ * @return The previously set callback function, if any.
+*/
+RGFWDEF RGFW_windowRestoredfunc RGFW_setWindowRestoredCallback(RGFW_windowRestoredfunc func);
+
+/**!
+ * @brief Sets the callback function for DPI (scale) update events.
+ * @param func The function to be called when the window’s DPI or scale changes.
+ * @return The previously set callback function, if any.
+*/
+RGFWDEF RGFW_scaleUpdatedfunc RGFW_setScaleUpdatedCallback(RGFW_scaleUpdatedfunc func);
+
+/**!
+ * @brief Sets the callback function for monitor connected and disconnect events.
+ * @param func The function to be called when a monitor is connected or disconnected.
+ * @return The previously set callback function, if any.
+*/
+RGFWDEF RGFW_monitorfunc RGFW_setMonitorCallback(RGFW_monitorfunc func);
+
+/** @} */
+
+/** * @defgroup graphics_API
+* @{ */
+
+/*! native rendering API functions */
+#if defined(RGFW_OPENGL)
+/* these are native opengl specific functions and will NOT work with EGL */
+
+/*!< make the window the current OpenGL drawing context
+
+ NOTE:
+ if you want to switch the graphics context's thread,
+ you have to run RGFW_window_makeCurrentContext_OpenGL(NULL); on the old thread
+ then RGFW_window_makeCurrentContext_OpenGL(valid_window) on the new thread
+*/
+
+/**!
+ * @brief Sets the global OpenGL hints to the specified pointer.
+ * @param hints A pointer to the RGFW_glHints structure containing the desired OpenGL settings.
+*/
+RGFWDEF void RGFW_setGlobalHints_OpenGL(RGFW_glHints* hints);
+
+/**!
+ * @brief Resets the global OpenGL hints to their default values.
+*/
+RGFWDEF void RGFW_resetGlobalHints_OpenGL(void);
+
+/**!
+ * @brief Gets the current global OpenGL hints pointer.
+ * @return A pointer to the currently active RGFW_glHints structure.
+*/
+RGFWDEF RGFW_glHints* RGFW_getGlobalHints_OpenGL(void);
+
+/**!
+ * @brief Creates and allocates an OpenGL context for the specified window.
+ * @param win A pointer to the target RGFW_window.
+ * @param hints A pointer to an RGFW_glHints structure defining context creation parameters.
+ * @return A pointer to the newly created RGFW_glContext.
+*/
+RGFWDEF RGFW_glContext* RGFW_window_createContext_OpenGL(RGFW_window* win, RGFW_glHints* hints);
+
+/**!
+ * @brief Creates an OpenGL context for the specified window using a preallocated context structure.
+ * @param win A pointer to the target RGFW_window.
+ * @param ctx A pointer to an already allocated RGFW_glContext structure.
+ * @param hints A pointer to an RGFW_glHints structure defining context creation parameters.
+ * @return RGFW_TRUE on success, RGFW_FALSE on failure.
+*/
+RGFWDEF RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints);
+
+/**!
+ * @brief Retrieves the OpenGL context associated with a window.
+ * @param win A pointer to the RGFW_window.
+ * @return A pointer to the associated RGFW_glContext, or NULL if none exists or if the context is EGL-based.
+*/
+RGFWDEF RGFW_glContext* RGFW_window_getContext_OpenGL(RGFW_window* win);
+
+/**!
+ * @brief Deletes and frees the OpenGL context.
+ * @param win A pointer to the RGFW_window.
+ * @param ctx A pointer to the RGFW_glContext to delete.
+ *
+ * @note This is automatically called by RGFW_window_close if the window’s context is not NULL.
+*/
+RGFWDEF void RGFW_window_deleteContext_OpenGL(RGFW_window* win, RGFW_glContext* ctx);
+
+/**!
+ * @brief Deletes the OpenGL context without freeing its memory.
+ * @param win A pointer to the RGFW_window.
+ * @param ctx A pointer to the RGFW_glContext to delete.
+ *
+ * @note This is automatically called by RGFW_window_close if the window’s context is not NULL.
+*/
+RGFWDEF void RGFW_window_deleteContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx);
+
+/**!
+ * @brief Retrieves the native source context from an RGFW_glContext.
+ * @param ctx A pointer to the RGFW_glContext.
+ * @return A pointer to the native OpenGL context handle.
+*/
+RGFWDEF void* RGFW_glContext_getSourceContext(RGFW_glContext* ctx);
+
+/**!
+ * @brief Makes the specified window the current OpenGL rendering target.
+ * @param win A pointer to the RGFW_window to make current.
+ *
+ * @note This is typically called internally by RGFW_window_makeCurrent.
+*/
+RGFWDEF void RGFW_window_makeCurrentWindow_OpenGL(RGFW_window* win);
+
+/**!
+ * @brief Makes the OpenGL context of the specified window current.
+ * @param win A pointer to the RGFW_window whose context should be made current.
+ *
+ * @note To move a context between threads, call RGFW_window_makeCurrentContext_OpenGL(NULL)
+ * on the old thread before making it current on the new one.
+*/
+RGFWDEF void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win);
+
+/**!
+ * @brief Swaps the OpenGL buffers for the specified window.
+ * @param win A pointer to the RGFW_window whose buffers should be swapped.
+*/
+RGFWDEF void RGFW_window_swapBuffers_OpenGL(RGFW_window* win);
+
+/**!
+ * @brief Retrieves the current OpenGL context.
+ * @return A pointer to the currently active OpenGL context (GLX, WGL, Cocoa, or WebGL backend).
+*/
+RGFWDEF void* RGFW_getCurrentContext_OpenGL(void);
+
+/**!
+ * @brief Retrieves the current OpenGL window.
+ * @return A pointer to the RGFW_window currently bound as the OpenGL context target.
+*/
+RGFWDEF RGFW_window* RGFW_getCurrentWindow_OpenGL(void);
+
+/**!
+ * @brief Sets the OpenGL swap interval (vsync).
+ * @param win A pointer to the RGFW_window.
+ * @param swapInterval The desired swap interval value (0 to disable vsync, 1 to enable).
+*/
+RGFWDEF void RGFW_window_swapInterval_OpenGL(RGFW_window* win, i32 swapInterval);
+
+/**!
+ * @brief Retrieves the address of a native OpenGL procedure.
+ * @param procname The name of the OpenGL function to look up.
+ * @return A pointer to the function, or NULL if not found.
+*/
+RGFWDEF RGFW_proc RGFW_getProcAddress_OpenGL(const char* procname);
+
+/**!
+ * @brief Checks whether a specific OpenGL or OpenGL ES API extension is supported.
+ * @param extension The name of the extension to check.
+ * @param len The length of the extension string.
+ * @return RGFW_TRUE if supported, RGFW_FALSE otherwise.
+*/
+RGFWDEF RGFW_bool RGFW_extensionSupported_OpenGL(const char* extension, size_t len);
+
+/**!
+ * @brief Checks whether a specific platform-dependent OpenGL extension is supported.
+ * @param extension The name of the extension to check.
+ * @param len The length of the extension string.
+ * @return RGFW_TRUE if supported, RGFW_FALSE otherwise.
+*/
+RGFWDEF RGFW_bool RGFW_extensionSupportedPlatform_OpenGL(const char* extension, size_t len);
+
+/* these are EGL specific functions, they may fallback to OpenGL */
+#ifdef RGFW_EGL
+/**!
+ * @brief Creates and allocates an OpenGL/EGL context for the specified window.
+ * @param win A pointer to the target RGFW_window.
+ * @param hints A pointer to an RGFW_glHints structure defining context creation parameters.
+ * @return A pointer to the newly created RGFW_eglContext.
+*/
+RGFWDEF RGFW_eglContext* RGFW_window_createContext_EGL(RGFW_window* win, RGFW_glHints* hints);
+
+/**!
+ * @brief Creates an OpenGL/EGL context for the specified window using a preallocated context structure.
+ * @param win A pointer to the target RGFW_window.
+ * @param ctx A pointer to an already allocated RGFW_eglContext structure.
+ * @param hints A pointer to an RGFW_glHints structure defining context creation parameters.
+ * @return RGFW_TRUE on success, RGFW_FALSE on failure.
+*/
+RGFWDEF RGFW_bool RGFW_window_createContextPtr_EGL(RGFW_window* win, RGFW_eglContext* ctx, RGFW_glHints* hints);
+
+/**!
+ * @brief Frees and deletes an OpenGL/EGL context.
+ * @param win A pointer to the RGFW_window.
+ * @param ctx A pointer to the RGFW_eglContext to delete.
+ *
+ * @note Automatically called by RGFW_window_close if RGFW owns the context.
+*/
+RGFWDEF void RGFW_window_deleteContext_EGL(RGFW_window* win, RGFW_eglContext* ctx);
+
+/**!
+ * @brief Deletes an OpenGL/EGL context without freeing its memory.
+ * @param win A pointer to the RGFW_window.
+ * @param ctx A pointer to the RGFW_eglContext to delete.
+ *
+ * @note Automatically called by RGFW_window_close if RGFW owns the context.
+*/
+RGFWDEF void RGFW_window_deleteContextPtr_EGL(RGFW_window* win, RGFW_eglContext* ctx);
+
+/**!
+ * @brief Retrieves the OpenGL/EGL context associated with a window.
+ * @param win A pointer to the RGFW_window.
+ * @return A pointer to the associated RGFW_eglContext, or NULL if none exists or if the context is a native OpenGL context.
+*/
+RGFWDEF RGFW_eglContext* RGFW_window_getContext_EGL(RGFW_window* win);
+
+/**!
+ * @brief Retrieves the EGL display handle.
+ * @return A pointer to the native EGLDisplay.
+*/
+RGFWDEF void* RGFW_getDisplay_EGL(void);
+
+/**!
+ * @brief Retrieves the native source context from an RGFW_eglContext.
+ * @param ctx A pointer to the RGFW_eglContext.
+ * @return A pointer to the native EGLContext handle.
+*/
+RGFWDEF void* RGFW_eglContext_getSourceContext(RGFW_eglContext* ctx);
+
+/**!
+ * @brief Retrieves the EGL surface handle from an RGFW_eglContext.
+ * @param ctx A pointer to the RGFW_eglContext.
+ * @return A pointer to the EGLSurface associated with the context.
+*/
+RGFWDEF void* RGFW_eglContext_getSurface(RGFW_eglContext* ctx);
+
+/**!
+ * @brief Retrieves the Wayland EGL window handle from an RGFW_eglContext.
+ * @param ctx A pointer to the RGFW_eglContext.
+ * @return A pointer to the wl_egl_window associated with the EGL context.
+*/
+RGFWDEF struct wl_egl_window* RGFW_eglContext_wlEGLWindow(RGFW_eglContext* ctx);
+
+/**!
+ * @brief Swaps the EGL buffers for the specified window.
+ * @param win A pointer to the RGFW_window whose buffers should be swapped.
+ *
+ * @note Typically called by RGFW_window_swapInterval.
+*/
+RGFWDEF void RGFW_window_swapBuffers_EGL(RGFW_window* win);
+
+/**!
+ * @brief Makes the specified window the current EGL rendering target.
+ * @param win A pointer to the RGFW_window to make current.
+ *
+ * @note This is typically called internally by RGFW_window_makeCurrent.
+*/
+RGFWDEF void RGFW_window_makeCurrentWindow_EGL(RGFW_window* win);
+
+/**!
+ * @brief Makes the EGL context of the specified window current.
+ * @param win A pointer to the RGFW_window whose context should be made current.
+ *
+ * @note To move a context between threads, call RGFW_window_makeCurrentContext_EGL(NULL)
+ * on the old thread before making it current on the new one.
+*/
+RGFWDEF void RGFW_window_makeCurrentContext_EGL(RGFW_window* win);
+
+/**!
+ * @brief Retrieves the current EGL context.
+ * @return A pointer to the currently active EGLContext.
+*/
+RGFWDEF void* RGFW_getCurrentContext_EGL(void);
+
+/**!
+ * @brief Retrieves the current EGL window.
+ * @return A pointer to the RGFW_window currently bound as the EGL context target.
+*/
+RGFWDEF RGFW_window* RGFW_getCurrentWindow_EGL(void);
+
+/**!
+ * @brief Sets the EGL swap interval (vsync).
+ * @param win A pointer to the RGFW_window.
+ * @param swapInterval The desired swap interval value (0 to disable vsync, 1 to enable).
+*/
+RGFWDEF void RGFW_window_swapInterval_EGL(RGFW_window* win, i32 swapInterval);
+
+/**!
+ * @brief Retrieves the address of a native OpenGL or OpenGL ES procedure in an EGL context.
+ * @param procname The name of the OpenGL function to look up.
+ * @return A pointer to the function, or NULL if not found.
+*/
+RGFWDEF RGFW_proc RGFW_getProcAddress_EGL(const char* procname);
+
+/**!
+ * @brief Checks whether a specific OpenGL or OpenGL ES API extension is supported in the current EGL context.
+ * @param extension The name of the extension to check.
+ * @param len The length of the extension string.
+ * @return RGFW_TRUE if supported, RGFW_FALSE otherwise.
+*/
+RGFWDEF RGFW_bool RGFW_extensionSupported_EGL(const char* extension, size_t len);
+
+/**!
+ * @brief Checks whether a specific platform-dependent EGL extension is supported in the current context.
+ * @param extension The name of the extension to check.
+ * @param len The length of the extension string.
+ * @return RGFW_TRUE if supported, RGFW_FALSE otherwise.
+*/
+RGFWDEF RGFW_bool RGFW_extensionSupportedPlatform_EGL(const char* extension, size_t len);
+#endif
+#endif
+
+#ifdef RGFW_VULKAN
+#include
+
+/* if you don't want to use the above macros */
+
+/**!
+ * @brief Retrieves the Vulkan instance extensions required by RGFW.
+ * @param count [OUTPUT] A pointer that will receive the number of required extensions (typically 2).
+ * @return A pointer to a static array of required Vulkan instance extension names.
+*/
+RGFWDEF const char** RGFW_getRequiredInstanceExtensions_Vulkan(size_t* count);
+
+/**!
+ * @brief Creates a Vulkan surface for the specified window.
+ * @param win A pointer to the RGFW_window for which to create the Vulkan surface.
+ * @param instance The Vulkan instance used to create the surface.
+ * @param surface [OUTPUT] A pointer to a VkSurfaceKHR handle that will receive the created surface.
+ * @return A VkResult indicating success or failure.
+*/
+RGFWDEF VkResult RGFW_window_createSurface_Vulkan(RGFW_window* win, VkInstance instance, VkSurfaceKHR* surface);
+
+/**!
+ * @brief Checks whether the specified Vulkan physical device and queue family support presentation for RGFW.
+ * @param instance The Vulkan instance.
+ * @param physicalDevice The Vulkan physical device to check.
+ * @param queueFamilyIndex The index of the queue family to query for presentation support.
+ * @return RGFW_TRUE if presentation is supported, RGFW_FALSE otherwise.
+*/
+RGFWDEF RGFW_bool RGFW_getPresentationSupport_Vulkan(VkPhysicalDevice physicalDevice, u32 queueFamilyIndex);
+#endif
+
+#ifdef RGFW_DIRECTX
+#ifndef RGFW_WINDOWS
+ #undef RGFW_DIRECTX
+#else
+ #define OEMRESOURCE
+ #include
+
+ #ifndef __cplusplus
+ #define __uuidof(T) IID_##T
+ #endif
+/**!
+ * @brief Creates a DirectX swap chain for the specified RGFW window.
+ * @param win A pointer to the RGFW_window for which to create the swap chain.
+ * @param pFactory A pointer to the IDXGIFactory used to create the swap chain.
+ * @param pDevice A pointer to the DirectX device (e.g., ID3D11Device or ID3D12Device).
+ * @param swapchain [OUTPUT] A pointer to an IDXGISwapChain pointer that will receive the created swap chain.
+ * @return An integer result code (0 on success, or a DirectX error code on failure).
+*/
+RGFWDEF int RGFW_window_createSwapChain_DirectX(RGFW_window* win, IDXGIFactory* pFactory, IUnknown* pDevice, IDXGISwapChain** swapchain);
+#endif
+#endif
+
+#ifdef RGFW_WEBGPU
+ #include
+ /**!
+ * @brief Creates a WebGPU surface for the specified RGFW window.
+ * @param window A pointer to the RGFW_window for which to create the surface.
+ * @param instance The WebGPU instance used to create the surface.
+ * @return The created WGPUSurface handle.
+ */
+ RGFWDEF WGPUSurface RGFW_window_createSurface_WebGPU(RGFW_window* window, WGPUInstance instance);
+#endif
+
+/** @} */
+
+/** * @defgroup Supporting
+* @{ */
+
+/**!
+ * @brief Sets the root (main) RGFW window.
+ * @param win A pointer to the RGFW_window to set as the root window.
+*/
+RGFWDEF void RGFW_setRootWindow(RGFW_window* win);
+
+/**!
+ * @brief Retrieves the current root RGFW window.
+ * @return A pointer to the current root RGFW_window.
+*/
+RGFWDEF RGFW_window* RGFW_getRootWindow(void);
+
+/**!
+ * @brief Pushes an event into the standard RGFW event queue.
+ * @param event A pointer to the RGFW_event to be added to the queue.
+*/
+RGFWDEF void RGFW_eventQueuePush(const RGFW_event* event);
+
+/**!
+ * @brief Clears all events from the RGFW event queue without processing them.
+*/
+RGFWDEF void RGFW_eventQueueFlush(void);
+
+/**!
+ * @brief Pops the next event from the RGFW event queue.
+ * @return A pointer to the popped RGFW_event, or NULL if the queue is empty.
+*/
+RGFWDEF RGFW_event* RGFW_eventQueuePop(void);
+
+/**!
+ * @brief Pops the next event from the RGFW event queue that matches the target window, pushes back events that don't matchj.
+ * @param win A pointer to the target RGFW_window.
+ * @return A pointer to the popped RGFW_event, or NULL if the queue is empty.
+*/
+RGFWDEF RGFW_event* RGFW_window_eventQueuePop(RGFW_window* win);
+
+/**!
+ * @brief Converts an API keycode to the RGFW unmapped (physical) key.
+ * @param keycode The platform-specific keycode.
+ * @return The corresponding RGFW keycode.
+*/
+RGFWDEF RGFW_key RGFW_apiKeyToRGFW(u32 keycode);
+
+/**!
+ * @brief Converts an RGFW keycode to the unmapped (physical) API key.
+ * @param keycode The RGFW keycode.
+ * @return The corresponding platform-specific keycode.
+*/
+RGFWDEF u32 RGFW_rgfwToApiKey(RGFW_key keycode);
+
+/**!
+ * @brief Converts an physical RGFW keycode to a mapped RGFW keycode.
+ * @param keycode the physical RGFW keycode.
+ * @return The corresponding mapped RGFW keycode.
+*/
+RGFWDEF RGFW_key RGFW_physicalToMappedKey(RGFW_key keycode);
+
+/**!
+ * @brief Retrieves the size of the RGFW_info structure.
+ * @return The size (in bytes) of RGFW_info.
+*/
+RGFWDEF size_t RGFW_sizeofInfo(void);
+
+/**!
+ * @brief Initializes the RGFW library.
+ * @return 0 on success, or a negative error code on failure.
+ * @note This is automatically called when the first window is created.
+*/
+RGFWDEF i32 RGFW_init(void);
+
+/**!
+ * @brief Deinitializes the RGFW library.
+ * @note This is automatically called when the last open window is closed.
+*/
+RGFWDEF void RGFW_deinit(void);
+
+/**!
+ * @brief Initializes RGFW using a user-provided RGFW_info structure.
+ * @param info A pointer to an RGFW_info structure to be used for initialization.
+ * @return 0 on success, or a negative error code on failure.
+*/
+RGFWDEF i32 RGFW_init_ptr(RGFW_info* info);
+
+/**!
+ * @brief Deinitializes a specific RGFW instance stored in the provided RGFW_info pointer.
+ * @param info A pointer to the RGFW_info structure representing the instance to deinitialize.
+*/
+RGFWDEF void RGFW_deinit_ptr(RGFW_info* info);
+
+/**!
+ * @brief Sets the global RGFW_info structure pointer.
+ * @param info A pointer to the RGFW_info structure to set.
+*/
+RGFWDEF void RGFW_setInfo(RGFW_info* info);
+
+/**!
+ * @brief Retrieves the global RGFW_info structure pointer.
+ * @return A pointer to the current RGFW_info structure.
+*/
+RGFWDEF RGFW_info* RGFW_getInfo(void);
+
+/** @} */
+#endif /* RGFW_HEADER */
+
+#if !defined(RGFW_NATIVE_HEADER) && (defined(RGFW_NATIVE) || defined(RGFW_IMPLEMENTATION))
+#define RGFW_NATIVE_HEADER
+ #if (defined(RGFW_OPENGL) || defined(RGFW_WEGL)) && defined(_MSC_VER)
+ #pragma comment(lib, "opengl32")
+ #endif
+
+ #ifdef RGFW_OPENGL
+ struct RGFW_eglContext {
+ void* ctx;
+ void* surface;
+ struct wl_egl_window* eglWindow;
+ };
+
+ typedef union RGFW_gfxContext {
+ RGFW_glContext* native;
+ RGFW_eglContext* egl;
+ } RGFW_gfxContext;
+
+ typedef RGFW_ENUM(u32, RGFW_gfxContextType) {
+ RGFW_gfxNativeOpenGL = RGFW_BIT(0),
+ RGFW_gfxEGL = RGFW_BIT(1),
+ RGFW_gfxOwnedByRGFW = RGFW_BIT(2)
+ };
+ #endif
+
+ /*! source data for the window (used by the APIs) */
+ #ifdef RGFW_WINDOWS
+
+ #ifndef WIN32_LEAN_AND_MEAN
+ #define WIN32_LEAN_AND_MEAN
+ #endif
+ #ifndef OEMRESOURCE
+ #define OEMRESOURCE
+ #endif
+
+ #include
+
+ struct RGFW_nativeImage {
+ HBITMAP bitmap;
+ u8* bitmapBits;
+ RGFW_format format;
+ HDC hdcMem;
+ };
+
+ #ifdef RGFW_OPENGL
+ struct RGFW_glContext { HGLRC ctx; };
+ #endif
+
+ struct RGFW_window_src {
+ HWND window; /*!< source window */
+ HDC hdc; /*!< source HDC */
+ HICON hIconSmall, hIconBig; /*!< source window icons */
+ i32 maxSizeW, maxSizeH, minSizeW, minSizeH, aspectRatioW, aspectRatioH; /*!< for setting max/min resize (RGFW_WINDOWS) */
+ RGFW_bool actionFrame; /* frame after a caption button was toggled (e.g. minimize, maximize or close) */
+ WCHAR highSurrogate;
+ #ifdef RGFW_OPENGL
+ RGFW_gfxContext ctx;
+ RGFW_gfxContextType gfxType;
+ #endif
+ };
+
+#elif defined(RGFW_UNIX)
+ #ifdef RGFW_X11
+ #include
+ #include
+
+ #ifndef RGFW_NO_XRANDR
+ #include
+ #include
+ #endif
+ #endif
+
+ #ifdef RGFW_WAYLAND
+ #ifdef RGFW_LIBDECOR
+ #include
+ #endif
+
+ #include
+ #include
+ #endif
+
+ struct RGFW_nativeImage {
+ #ifdef RGFW_X11
+ XImage* bitmap;
+ #endif
+ #ifdef RGFW_WAYLAND
+ struct wl_buffer* wl_buffer;
+ i32 fd;
+ struct wl_shm_pool* pool;
+ #endif
+ u8* buffer;
+ RGFW_format format;
+ };
+
+ #ifdef RGFW_OPENGL
+ struct RGFW_glContext {
+ #ifdef RGFW_X11
+ struct __GLXcontextRec* ctx; /*!< source graphics context */
+ Window window;
+ #endif
+ #ifdef RGFW_WAYLAND
+ RGFW_eglContext egl;
+ #endif
+ };
+ #endif
+
+ struct RGFW_window_src {
+ i32 x, y, w, h;
+ #ifdef RGFW_OPENGL
+ RGFW_gfxContext ctx;
+ RGFW_gfxContextType gfxType;
+ #endif
+#ifdef RGFW_X11
+ Window window; /*!< source window */
+ Window parent; /*!< parent window */
+ GC gc;
+ XIC ic;
+ u64 flashEnd;
+ #ifdef RGFW_ADVANCED_SMOOTH_RESIZE
+ i64 counter_value;
+ XID counter;
+ #endif
+#endif /* RGFW_X11 */
+
+#if defined(RGFW_WAYLAND)
+ struct wl_surface* surface;
+ struct xdg_surface* xdg_surface;
+ struct xdg_toplevel* xdg_toplevel;
+ struct zxdg_toplevel_decoration_v1* decoration;
+ struct zwp_locked_pointer_v1 *locked_pointer;
+ struct xdg_toplevel_icon_v1 *icon;
+ u32 decoration_mode;
+ /* State flags to configure the window */
+ RGFW_bool pending_activated;
+ RGFW_bool activated;
+ RGFW_bool resizing;
+ RGFW_bool pending_maximized;
+ RGFW_bool maximized;
+ RGFW_bool minimized;
+ RGFW_bool configured;
+
+ RGFW_bool using_custom_cursor;
+ struct wl_surface* custom_cursor_surface;
+
+ RGFW_monitorNode* active_monitor;
+
+ struct wl_data_source *data_source; // offer data to other clients
+
+ #ifdef RGFW_LIBDECOR
+ struct libdecor* decorContext;
+ #endif
+#endif /* RGFW_WAYLAND */
+ };
+
+#elif defined(RGFW_MACOS)
+ #include
+
+ struct RGFW_nativeImage {
+ RGFW_format format;
+ u8* buffer;
+ void* rep;
+ };
+
+ #ifdef RGFW_OPENGL
+ struct RGFW_glContext {
+ void* ctx;
+ void* format;
+ };
+ #endif
+
+ struct RGFW_window_src {
+ void* window;
+ void* view; /* apple viewpoint thingy */
+ void* mouse;
+ void* delegate;
+ #ifdef RGFW_OPENGL
+ RGFW_gfxContext ctx;
+ RGFW_gfxContextType gfxType;
+ #endif
+ };
+
+#elif defined(RGFW_WASM)
+
+ #include
+ #include
+
+ struct RGFW_nativeImage {
+ RGFW_format format;
+ };
+
+ #ifdef RGFW_OPENGL
+ struct RGFW_glContext {
+ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx;
+ };
+ #endif
+
+ struct RGFW_window_src {
+ #ifdef RGFW_OPENGL
+ RGFW_gfxContext ctx;
+ RGFW_gfxContextType gfxType;
+ #endif
+ };
+
+#endif
+
+struct RGFW_surface {
+ u8* data;
+ i32 w, h;
+ RGFW_format format;
+ RGFW_convertImageDataFunc convertFunc;
+ RGFW_nativeImage native;
+};
+
+/*! internal window data that is not specific to the OS */
+typedef struct RGFW_windowInternal {
+ /*! which key RGFW_window_shouldClose checks. Settting this to RGFW_keyNULL disables the feature. */
+ RGFW_key exitKey;
+ i32 lastMouseX, lastMouseY; /*!< last cusor point (for raw mouse data) */
+
+ RGFW_bool shouldClose;
+ RGFW_bool rawMouse;
+ RGFW_bool captureMouse;
+ RGFW_bool inFocus;
+ RGFW_bool mouseInside;
+ RGFW_keymod mod;
+ RGFW_eventFlag enabledEvents;
+ u32 flags; /*!< windows flags (for RGFW to check and modify) */
+ i32 oldX, oldY, oldW, oldH;
+} RGFW_windowInternal;
+
+struct RGFW_window {
+ RGFW_window_src src; /*!< src window data */
+ RGFW_windowInternal internal; /*!< internal window data that is not specific to the OS */
+ void* userPtr; /* ptr for user data */
+ i32 x, y, w, h; /*!< position and size of the window */
+}; /*!< window structure for the window */
+
+typedef struct RGFW_windowState {
+ RGFW_bool mouseEnter;
+ RGFW_bool dataDragging;
+ RGFW_bool dataDrop;
+ size_t filesCount;
+ i32 dropX, dropY;
+ RGFW_window* win; /*!< it's not possible for one of these events to happen in the frame that the other event happened */
+
+ RGFW_bool mouseLeave;
+ RGFW_window* winLeave; /*!< if a mouse leaves one window and enters the next */
+} RGFW_windowState;
+
+typedef struct {
+ RGFW_bool current;
+ RGFW_bool prev;
+} RGFW_keyState;
+
+struct RGFW_monitorNode {
+ RGFW_monitor mon;
+ RGFW_bool disconnected;
+ RGFW_monitorNode* next;
+#ifdef RGFW_WAYLAND
+ u32 id; /* Add id so wl_outputs can be removed */
+ struct wl_output *output;
+ struct zxdg_output_v1 *xdg_output;
+ RGFW_monitorMode* modes;
+ size_t modeCount;
+#endif
+#if defined(RGFW_X11) && !defined(RGFW_NO_XRANDR)
+ i32 screen;
+ RROutput rrOutput;
+ RRCrtc crtc;
+#endif
+#ifdef RGFW_WINDOWS
+ HMONITOR hMonitor;
+ WCHAR adapterName[32];
+ WCHAR deviceName[32];
+#endif
+#ifdef RGFW_MACOS
+ void* screen;
+ CGDirectDisplayID display;
+ u32 uintNum;
+#endif
+};
+
+typedef struct RGFW_monitorList {
+ RGFW_monitorNode* head;
+ RGFW_monitorNode* cur;
+} RGFW_monitorList;
+
+typedef struct RGFW_monitors {
+ RGFW_monitorList list;
+ RGFW_monitorList freeList;
+ size_t count;
+
+ RGFW_monitorNode* primary;
+ RGFW_monitorNode data[RGFW_MAX_MONITORS];
+} RGFW_monitors;
+
+RGFWDEF RGFW_monitorNode* RGFW_monitors_add(const RGFW_monitor* mon);
+RGFWDEF void RGFW_monitors_remove(RGFW_monitorNode* node, RGFW_monitorNode* prev);
+
+struct RGFW_info {
+ RGFW_window* root;
+ i32 windowCount;
+
+ RGFW_mouse* hiddenMouse;
+
+ RGFW_event events[RGFW_MAX_EVENTS]; /* A circular buffer (FIFO), using eventBottom/Len */
+
+ i32 eventBottom;
+ i32 eventLen;
+ RGFW_bool queueEvents;
+ RGFW_bool polledEvents;
+
+ u32 apiKeycodes[RGFW_keyLast];
+ #if defined(RGFW_X11) || defined(RGFW_WAYLAND)
+ RGFW_key keycodes[256];
+ #elif defined(RGFW_WINDOWS)
+ RGFW_key keycodes[512];
+ #elif defined(RGFW_MACOS)
+ RGFW_key keycodes[128];
+ #elif defined(RGFW_WASM)
+ RGFW_key keycodes[256];
+ #endif
+
+ const char* className;
+ RGFW_bool useWaylandBool;
+ RGFW_bool stopCheckEvents_bool ;
+ u64 timerOffset;
+
+ char* clipboard_data;
+ char* clipboard; /* for writing to the clipboard selection */
+ size_t clipboard_len;
+ char filesSrc[RGFW_MAX_PATH * RGFW_MAX_DROPS];
+ char** files;
+ #ifdef RGFW_X11
+ Display* display;
+ XContext context;
+ Window helperWindow;
+ const char* instName;
+ XErrorEvent* x11Error;
+ i32 xrandrEventBase;
+ XIM im;
+ #endif
+ #ifdef RGFW_WAYLAND
+ struct wl_display* wl_display;
+ struct xkb_context *xkb_context;
+ struct xkb_keymap *keymap;
+ struct xkb_state *xkb_state;
+ struct zxdg_decoration_manager_v1 *decoration_manager;
+ struct zwp_relative_pointer_manager_v1 *relative_pointer_manager;
+ struct zwp_relative_pointer_v1 *relative_pointer;
+ struct zwp_pointer_constraints_v1 *constraint_manager;
+ struct xdg_toplevel_icon_manager_v1 *icon_manager;
+
+ struct zxdg_output_manager_v1 *xdg_output_manager;
+
+ struct wl_data_device_manager *data_device_manager;
+ struct wl_data_device *data_device; // supports clipboard and DND
+ struct wp_pointer_warp_v1* wp_pointer_warp;
+
+ struct wl_keyboard* wl_keyboard;
+ struct wl_pointer* wl_pointer;
+ struct wl_compositor* compositor;
+ struct xdg_wm_base* xdg_wm_base;
+ struct wl_shm* shm;
+ struct wl_seat *seat;
+ struct wl_registry *registry;
+ u32 mouse_enter_serial;
+ struct wl_cursor_theme* wl_cursor_theme;
+ struct wl_surface* cursor_surface;
+ struct xkb_compose_state* composeState;
+
+ RGFW_window* kbOwner;
+ RGFW_window* mouseOwner; /* what window has access to the mouse */
+
+ #endif
+
+ RGFW_monitors monitors;
+
+ #ifdef RGFW_UNIX
+ int eventWait_forceStop[3];
+ i32 clock;
+ #endif
+
+ #ifdef RGFW_MACOS
+ void* NSApp;
+ i64 flash;
+ void* customViewClasses[2]; /* NSView and NSOpenGLView */
+ void* customNSAppDelegateClass;
+ void* customWindowDelegateClass;
+ void* customNSAppDelegate;
+ void* tisBundle;
+ #endif
+
+ #ifdef RGFW_OPENGL
+ RGFW_window* current;
+ #endif
+ #ifdef RGFW_EGL
+ void* EGL_display;
+ #endif
+
+ RGFW_bool rawMouse; /* global raw mouse toggle */
+
+ RGFW_windowState windowState; /*! for checking window state events */
+
+ RGFW_keyState mouseButtons[RGFW_mouseFinal];
+ RGFW_keyState keyboard[RGFW_keyLast];
+ float scrollX, scrollY;
+ float vectorX, vectorY;
+};
+#endif /* RGFW_NATIVE_HEADER */
+
+#ifdef RGFW_IMPLEMENTATION
+
+#ifndef RGFW_NO_MATH
+#include
+#endif
+
+/* global private API */
+
+/* for C++ / C89 */
+RGFWDEF RGFW_window* RGFW_createWindowPlatform(const char* name, RGFW_windowFlags flags, RGFW_window* win);
+RGFWDEF void RGFW_window_closePlatform(RGFW_window* win);
+
+RGFWDEF void RGFW_window_setFlagsInternal(RGFW_window* win, RGFW_windowFlags flags, RGFW_windowFlags cmpFlags);
+
+RGFWDEF void RGFW_initKeycodes(void);
+RGFWDEF void RGFW_initKeycodesPlatform(void);
+RGFWDEF void RGFW_resetPrevState(void);
+RGFWDEF void RGFW_resetKey(void);
+RGFWDEF void RGFW_unloadEGL(void);
+RGFWDEF void RGFW_updateKeyModsEx(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll);
+RGFWDEF void RGFW_updateKeyMods(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool scroll);
+RGFWDEF void RGFW_window_showMouseFlags(RGFW_window* win, RGFW_bool show);
+RGFWDEF void RGFW_updateKeyMod(RGFW_window* win, RGFW_keymod mod, RGFW_bool value);
+RGFWDEF void RGFW_monitors_refresh(void);
+
+RGFWDEF void RGFW_windowMaximizedCallback(RGFW_window* win, i32 x, i32 y, i32 w, i32 h);
+RGFWDEF void RGFW_windowMinimizedCallback(RGFW_window* win);
+RGFWDEF void RGFW_windowRestoredCallback(RGFW_window* win, i32 x, i32 y, i32 w, i32 h);
+RGFWDEF void RGFW_windowMovedCallback(RGFW_window* win, i32 x, i32 y);
+RGFWDEF void RGFW_windowResizedCallback(RGFW_window* win, i32 w, i32 h);
+RGFWDEF void RGFW_windowQuitCallback(RGFW_window* win);
+RGFWDEF void RGFW_mousePosCallback(RGFW_window* win, i32 x, i32 y, float vecX, float vecY);
+RGFWDEF void RGFW_windowRefreshCallback(RGFW_window* win);
+RGFWDEF void RGFW_focusCallback(RGFW_window* win, RGFW_bool inFocus);
+RGFWDEF void RGFW_mouseNotifyCallback(RGFW_window* win, i32 x, i32 y, RGFW_bool status);
+RGFWDEF void RGFW_dataDropCallback(RGFW_window* win, char** files, size_t count);
+RGFWDEF void RGFW_dataDragCallback(RGFW_window* win, i32 x, i32 y);
+RGFWDEF void RGFW_keyCharCallback(RGFW_window* win, u32 codepoint);
+RGFWDEF void RGFW_keyCallback(RGFW_window* win, RGFW_key key, RGFW_keymod mod, RGFW_bool repeat, RGFW_bool press);
+RGFWDEF void RGFW_mouseButtonCallback(RGFW_window* win, RGFW_mouseButton button, RGFW_bool press);
+RGFWDEF void RGFW_mouseScrollCallback(RGFW_window* win, float x, float y);
+RGFWDEF void RGFW_scaleUpdatedCallback(RGFW_window* win, float scaleX, float scaleY);
+RGFWDEF void RGFW_monitorCallback(RGFW_window* win, const RGFW_monitor* monitor, RGFW_bool connected);
+
+RGFWDEF void RGFW_setBit(u32* var, u32 mask, RGFW_bool set);
+RGFWDEF void RGFW_splitBPP(u32 bpp, RGFW_monitorMode* mode);
+
+RGFWDEF void RGFW_window_captureMousePlatform(RGFW_window* win, RGFW_bool state);
+RGFWDEF void RGFW_window_setRawMouseModePlatform(RGFW_window *win, RGFW_bool state);
+
+RGFWDEF void RGFW_copyImageData64(u8* dest_data, i32 w, i32 h, RGFW_format dest_format,
+ u8* src_data, RGFW_format src_format, RGFW_bool is64bit, RGFW_convertImageDataFunc func);
+
+RGFWDEF RGFW_bool RGFW_loadEGL(void);
+
+#ifdef RGFW_OPENGL
+typedef struct RGFW_attribStack {
+ i32* attribs;
+ size_t count;
+ size_t max;
+} RGFW_attribStack;
+RGFWDEF void RGFW_attribStack_init(RGFW_attribStack* stack, i32* attribs, size_t max);
+RGFWDEF void RGFW_attribStack_pushAttrib(RGFW_attribStack* stack, i32 attrib);
+RGFWDEF void RGFW_attribStack_pushAttribs(RGFW_attribStack* stack, i32 attrib1, i32 attrib2);
+
+RGFWDEF RGFW_bool RGFW_extensionSupportedStr(const char* extensions, const char* ext, size_t len);
+#endif
+
+#ifdef RGFW_X11
+RGFWDEF void RGFW_XCreateWindow (XVisualInfo visual, const char* name, RGFW_windowFlags flags, RGFW_window* win);
+#endif
+#ifdef RGFW_MACOS
+RGFWDEF void RGFW_osx_initView(RGFW_window* win);
+#endif
+/* end of global private API defs */
+
+RGFW_info* _RGFW = NULL;
+void RGFW_setInfo(RGFW_info* info) { _RGFW = info; }
+RGFW_info* RGFW_getInfo(void) { return _RGFW; }
+
+
+void* RGFW_alloc(size_t size) { return RGFW_ALLOC(size); }
+void RGFW_free(void* ptr) { RGFW_FREE(ptr); }
+
+void RGFW_useWayland(RGFW_bool wayland) { RGFW_init(); _RGFW->useWaylandBool = RGFW_BOOL(wayland); }
+RGFW_bool RGFW_usingWayland(void) { return _RGFW->useWaylandBool; }
+
+void RGFW_setRawMouseMode(RGFW_bool state) {
+ _RGFW->rawMouse = state;
+ RGFW_window_setRawMouseModePlatform(_RGFW->root, state);
+}
+
+void RGFW_clipboard_switch(char* newstr);
+void RGFW_clipboard_switch(char* newstr) {
+ if (_RGFW->clipboard_data != NULL)
+ RGFW_FREE(_RGFW->clipboard_data);
+ _RGFW->clipboard_data = newstr;
+}
+
+#define RGFW_CHECK_CLIPBOARD() \
+ if (size <= 0 && _RGFW->clipboard_data != NULL) \
+ return (const char*)_RGFW->clipboard_data; \
+ else if (size <= 0) \
+ return "\0";
+
+const char* RGFW_readClipboard(size_t* len) {
+ RGFW_ssize_t size = RGFW_readClipboardPtr(NULL, 0);
+ RGFW_CHECK_CLIPBOARD();
+ char* str = (char*)RGFW_ALLOC((size_t)size);
+ RGFW_ASSERT(str != NULL);
+ str[0] = '\0';
+
+ size = RGFW_readClipboardPtr(str, (size_t)size);
+
+ RGFW_CHECK_CLIPBOARD();
+
+ if (len != NULL) *len = (size_t)size;
+
+ RGFW_clipboard_switch(str);
+ return (const char*)str;
+}
+
+/*
+RGFW_IMPLEMENTATION starts with generic RGFW defines
+
+This is the start of keycode data
+*/
+
+
+
+void RGFW_initKeycodes(void) {
+ RGFW_MEMSET(_RGFW->keycodes, 0, sizeof(_RGFW->keycodes));
+ RGFW_initKeycodesPlatform();
+ size_t i, y;
+ for (i = 0; i < RGFW_keyLast; i++) {
+ for (y = 0; y < (sizeof(_RGFW->keycodes) / sizeof(RGFW_key)); y++) {
+ if (_RGFW->keycodes[y] == i) {
+ _RGFW->apiKeycodes[i] = (RGFW_key)y;
+ break;
+ }
+ }
+ }
+
+
+ RGFW_resetKey();
+}
+
+RGFW_key RGFW_apiKeyToRGFW(u32 keycode) {
+ /* make sure the key isn't out of bounds */
+ if (keycode > (sizeof(_RGFW->keycodes) / sizeof(RGFW_key)))
+ return 0;
+
+ return _RGFW->keycodes[keycode];
+}
+
+u32 RGFW_rgfwToApiKey(RGFW_key keycode) {
+ /* make sure the key isn't out of bounds */
+ return _RGFW->apiKeycodes[keycode];
+}
+
+void RGFW_resetKey(void) { RGFW_MEMSET(_RGFW->keyboard, 0, sizeof(_RGFW->keyboard)); }
+/*
+ this is the end of keycode data
+*/
+
+/*
+ event callback defines start here
+*/
+
+
+/*
+ These exist to avoid the
+ if (func == NULL) check
+ for (allegedly) better performance
+
+ RGFW_EMPTY_DEF exists to prevent the missing-prototypes warning
+*/
+#define RGFW_CALLBACK_DEFINE(x, x2) \
+RGFW_##x##func RGFW_##x##CallbackSrc = NULL; \
+RGFW_##x##func RGFW_set##x2##Callback(RGFW_##x##func func) { \
+ RGFW_##x##func prev = RGFW_##x##CallbackSrc; \
+ RGFW_##x##CallbackSrc = func; \
+ return prev; \
+}
+
+RGFW_CALLBACK_DEFINE(windowMaximized, WindowMaximized)
+RGFW_CALLBACK_DEFINE(windowMinimized, WindowMinimized)
+RGFW_CALLBACK_DEFINE(windowRestored, WindowRestored)
+RGFW_CALLBACK_DEFINE(windowMoved, WindowMoved)
+RGFW_CALLBACK_DEFINE(windowResized, WindowResized)
+RGFW_CALLBACK_DEFINE(windowQuit, WindowQuit)
+RGFW_CALLBACK_DEFINE(mousePos, MousePos)
+RGFW_CALLBACK_DEFINE(windowRefresh, WindowRefresh)
+RGFW_CALLBACK_DEFINE(focus, Focus)
+RGFW_CALLBACK_DEFINE(mouseNotify, MouseNotify)
+RGFW_CALLBACK_DEFINE(dataDrop, DataDrop)
+RGFW_CALLBACK_DEFINE(dataDrag, DataDrag)
+RGFW_CALLBACK_DEFINE(key, Key)
+RGFW_CALLBACK_DEFINE(keyChar, KeyChar)
+RGFW_CALLBACK_DEFINE(mouseButton, MouseButton)
+RGFW_CALLBACK_DEFINE(mouseScroll, MouseScroll)
+RGFW_CALLBACK_DEFINE(scaleUpdated, ScaleUpdated)
+RGFW_CALLBACK_DEFINE(monitor, Monitor)
+RGFW_CALLBACK_DEFINE(debug, Debug)
+#define RGFW_debugCallback(type, err, msg) if (RGFW_debugCallbackSrc) RGFW_debugCallbackSrc(type, err, msg);
+#undef RGFW_CALLBACK_DEFINE
+
+void RGFW_windowMaximizedCallback(RGFW_window* win, i32 x, i32 y, i32 w, i32 h) {
+ win->internal.flags |= RGFW_windowMaximize;
+
+ if (!(win->internal.enabledEvents & RGFW_windowMaximizedFlag)) return;
+
+ RGFW_event event;
+ event.type = RGFW_windowMaximized;
+ event.common.win = win;
+ RGFW_eventQueuePush(&event);
+
+ if (RGFW_windowMaximizedCallbackSrc) RGFW_windowMaximizedCallbackSrc(win, x, y, w, h);
+}
+
+void RGFW_windowMinimizedCallback(RGFW_window* win) {
+ win->internal.flags |= RGFW_windowMinimize;
+
+ if (!(win->internal.enabledEvents & RGFW_windowMinimizedFlag)) return;
+
+ RGFW_event event;
+ event.type = RGFW_windowMinimized;
+ event.common.win = win;
+ RGFW_eventQueuePush(&event);
+
+ if (RGFW_windowMinimizedCallbackSrc) RGFW_windowMinimizedCallbackSrc(win);
+}
+
+void RGFW_windowRestoredCallback(RGFW_window* win, i32 x, i32 y, i32 w, i32 h) {
+ win->internal.flags &= ~(u32)RGFW_windowMinimize;
+ if (RGFW_window_isMaximized(win) == RGFW_FALSE) win->internal.flags &= ~(u32)RGFW_windowMaximize;
+
+ if (!(win->internal.enabledEvents & RGFW_windowRestoredFlag)) return;
+
+ RGFW_event event;
+ event.type = RGFW_windowRestored;
+ event.common.win = win;
+ RGFW_eventQueuePush(&event);
+
+ if (RGFW_windowRestoredCallbackSrc) RGFW_windowRestoredCallbackSrc(win, x, y, w, h);
+}
+
+void RGFW_windowMovedCallback(RGFW_window* win, i32 x, i32 y) {
+ win->x = x;
+ win->y = y;
+ if (!(win->internal.enabledEvents & RGFW_windowMovedFlag)) return;
+
+ RGFW_event event;
+ event.type = RGFW_windowMoved;
+ event.common.win = win;
+ RGFW_eventQueuePush(&event);
+
+ if (RGFW_windowMovedCallbackSrc) RGFW_windowMovedCallbackSrc(win, x, y);
+}
+
+void RGFW_windowResizedCallback(RGFW_window* win, i32 w, i32 h) {
+ win->w = w;
+ win->h = h;
+
+ if (!(win->internal.enabledEvents & RGFW_windowResizedFlag)) return;
+ RGFW_event event;
+ event.type = RGFW_windowResized;
+ event.common.win = win;
+ RGFW_eventQueuePush(&event);
+
+ if (RGFW_windowResizedCallbackSrc) RGFW_windowResizedCallbackSrc(win, w, h);
+}
+
+void RGFW_windowQuitCallback(RGFW_window* win) {
+ win->internal.shouldClose = RGFW_TRUE;
+
+ RGFW_event event;
+ event.type = RGFW_quit;
+ event.common.win = win;
+ RGFW_eventQueuePush(&event);
+
+ if (RGFW_windowQuitCallbackSrc) RGFW_windowQuitCallbackSrc(win);
+}
+
+void RGFW_mousePosCallback(RGFW_window* win, i32 x, i32 y, float vecX, float vecY) {
+ win->internal.lastMouseX = x;
+ win->internal.lastMouseY = y;
+ _RGFW->vectorX = vecX;
+ _RGFW->vectorY = vecY;
+
+ if (!(win->internal.enabledEvents & RGFW_mousePosChangedFlag)) return;
+
+ RGFW_event event;
+ event.type = RGFW_mousePosChanged;
+ event.mouse.x = x;
+ event.mouse.y = y;
+ event.mouse.vecX = vecX;
+ event.mouse.vecY = vecY;
+ event.common.win = win;
+
+ RGFW_eventQueuePush(&event);
+
+ if (RGFW_mousePosCallbackSrc) RGFW_mousePosCallbackSrc(win, x, y, vecX, vecY);
+}
+
+void RGFW_windowRefreshCallback(RGFW_window* win) {
+ if (!(win->internal.enabledEvents & RGFW_windowRefreshFlag)) return;
+ RGFW_event event;
+ event.type = RGFW_windowRefresh;
+ event.common.win = win;
+ RGFW_eventQueuePush(&event);
+
+ if (RGFW_windowRefreshCallbackSrc) RGFW_windowRefreshCallbackSrc(win);
+}
+
+void RGFW_focusCallback(RGFW_window* win, RGFW_bool inFocus) {
+ win->internal.inFocus = inFocus;
+
+ if (win->internal.captureMouse) {
+ RGFW_window_captureMousePlatform(win, inFocus);
+ }
+
+ RGFW_event event;
+ event.common.win = win;
+
+ if (inFocus == RGFW_TRUE) {
+ if ((win->internal.flags & RGFW_windowFullscreen))
+ RGFW_window_raise(win);
+
+ event.type = RGFW_focusIn;
+ } else if (inFocus == RGFW_FALSE) {
+ if ((win->internal.flags & RGFW_windowFullscreen))
+ RGFW_window_minimize(win);
+
+ size_t key;
+ for (key = 0; key < RGFW_keyLast; key++) {
+ if (RGFW_isKeyDown((u8)key) == RGFW_FALSE) continue;
+
+ _RGFW->keyboard[key].current = RGFW_FALSE;
+ if ((win->internal.enabledEvents & RGFW_BIT(RGFW_keyReleased))) {
+ RGFW_keyCallback(win, (u8)key, win->internal.mod, RGFW_FALSE, RGFW_FALSE);
+ }
+ }
+
+ RGFW_resetKey();
+ event.type = RGFW_focusOut;
+ }
+
+ event.common.win = win;
+
+ RGFW_eventQueuePush(&event);
+
+ if (RGFW_focusCallbackSrc) RGFW_focusCallbackSrc(win, inFocus);
+}
+
+void RGFW_mouseNotifyCallback(RGFW_window* win, i32 x, i32 y, RGFW_bool status) {
+ win->internal.mouseInside = status;
+ _RGFW->windowState.win = win;
+
+ win->internal.lastMouseX = x;
+ win->internal.lastMouseY = y;
+
+ RGFW_event event;
+ event.common.win = win;
+ event.mouse.x = x;
+ event.mouse.y = y;
+
+ if (status) {
+ if (!(win->internal.enabledEvents & RGFW_mouseEnterFlag)) return;
+ _RGFW->windowState.mouseEnter = RGFW_TRUE;
+ _RGFW->windowState.win = win;
+ event.type = RGFW_mouseEnter;
+ } else {
+ if (!(win->internal.enabledEvents & RGFW_mouseLeaveFlag)) return;
+ _RGFW->windowState.winLeave = win;
+ _RGFW->windowState.mouseLeave = RGFW_TRUE;
+ event.type = RGFW_mouseLeave;
+ }
+
+ RGFW_eventQueuePush(&event);
+
+ if (RGFW_mouseNotifyCallbackSrc) RGFW_mouseNotifyCallbackSrc(win, x, y, status);
+}
+
+void RGFW_dataDropCallback(RGFW_window* win, char** files, size_t count) {
+ if (!(win->internal.enabledEvents & RGFW_dataDropFlag) || !(win->internal.flags & RGFW_windowAllowDND))
+ return;
+
+ _RGFW->windowState.win = win;
+ _RGFW->windowState.dataDrop = RGFW_TRUE;
+ _RGFW->windowState.filesCount = count;
+
+ RGFW_event event;
+ event.type = RGFW_dataDrop;
+ event.drop.files = files;
+ event.drop.count = count;
+ event.common.win = win;
+ RGFW_eventQueuePush(&event);
+
+ if (RGFW_dataDropCallbackSrc) RGFW_dataDropCallbackSrc(win, files, count);
+}
+
+void RGFW_dataDragCallback(RGFW_window* win, i32 x, i32 y) {
+ _RGFW->windowState.win = win;
+ _RGFW->windowState.dataDragging = RGFW_TRUE;
+ _RGFW->windowState.dropX = x;
+ _RGFW->windowState.dropY = y;
+
+ if (win->internal.enabledEvents & RGFW_dataDragFlag) return;
+
+ RGFW_event event;
+ event.type = RGFW_dataDrag;
+ event.drag.x = x;
+ event.drag.y = y;
+ event.common.win = win;
+ RGFW_eventQueuePush(&event);
+
+ if (RGFW_dataDragCallbackSrc) RGFW_dataDragCallbackSrc(win, x, y);
+}
+
+void RGFW_keyCharCallback(RGFW_window* win, u32 codepoint) {
+ if (!(win->internal.enabledEvents & RGFW_keyCharFlag)) return;
+
+ RGFW_event event;
+ event.type = RGFW_keyChar;
+ event.keyChar.value = codepoint;
+ event.common.win = win;
+ RGFW_eventQueuePush(&event);
+
+ if (RGFW_keyCharCallbackSrc) RGFW_keyCharCallbackSrc(win, codepoint);
+}
+
+void RGFW_keyCallback(RGFW_window* win, RGFW_key key, RGFW_keymod mod, RGFW_bool repeat, RGFW_bool press) {
+ RGFW_event event;
+
+ if (press) {
+ if (!(win->internal.enabledEvents & RGFW_keyPressedFlag)) return;
+ event.type = RGFW_keyPressed;
+ } else {
+ if (!(win->internal.enabledEvents & RGFW_keyReleasedFlag)) return;
+ event.type = RGFW_keyReleased;
+ }
+
+ _RGFW->keyboard[key].prev = _RGFW->keyboard[key].current;
+ _RGFW->keyboard[key].current = press;
+
+ event.key.value = key;
+ event.key.mod = repeat;
+ event.key.mod = mod;
+ event.common.win = win;
+ RGFW_eventQueuePush(&event);
+
+ if (RGFW_keyCallbackSrc) RGFW_keyCallbackSrc(win, key, mod, repeat, press);
+}
+
+void RGFW_mouseButtonCallback(RGFW_window* win, RGFW_mouseButton button, RGFW_bool press) {
+ RGFW_event event;
+ if (press) {
+ if (!(win->internal.enabledEvents & RGFW_mouseButtonPressedFlag)) return;
+ event.type = RGFW_mouseButtonPressed;
+ } else {
+ if (!(win->internal.enabledEvents & RGFW_mouseButtonReleasedFlag)) return;
+ event.type = RGFW_mouseButtonReleased;
+ }
+
+ _RGFW->mouseButtons[button].prev = _RGFW->mouseButtons[button].current;
+ _RGFW->mouseButtons[button].current = press;
+
+ event.button.value = button;
+ event.common.win = win;
+ RGFW_eventQueuePush(&event);
+
+ if (RGFW_mouseButtonCallbackSrc) RGFW_mouseButtonCallbackSrc(win, button, press);
+}
+
+void RGFW_mouseScrollCallback(RGFW_window* win, float x, float y) {
+ if (!(win->internal.enabledEvents & RGFW_mouseScrollFlag)) return;
+ _RGFW->scrollX = x;
+ _RGFW->scrollY = y;
+
+ RGFW_event event;
+ event.type = RGFW_mouseScroll;
+ event.scroll.x = x;
+ event.scroll.y = y;
+ event.common.win = win;
+ RGFW_eventQueuePush(&event);
+
+ if (RGFW_mouseScrollCallbackSrc) RGFW_mouseScrollCallbackSrc(win, x, y);
+}
+
+void RGFW_scaleUpdatedCallback(RGFW_window* win, float scaleX, float scaleY) {
+ if (win->internal.flags & RGFW_windowScaleToMonitor) RGFW_window_scaleToMonitor(win);
+ if (!(win->internal.enabledEvents & RGFW_scaleUpdatedFlag)) return;
+
+ RGFW_event event;
+ event.type = RGFW_scaleUpdated;
+ event.scale.x = scaleX;
+ event.scale.y = scaleY;
+ event.common.win = win;
+ RGFW_eventQueuePush(&event);
+
+ if (RGFW_scaleUpdatedCallbackSrc) RGFW_scaleUpdatedCallbackSrc(win, scaleX, scaleY);
+}
+
+void RGFW_monitorCallback(RGFW_window* win, const RGFW_monitor* monitor, RGFW_bool connected) {
+ if (win) {
+ if (connected && !(win->internal.enabledEvents & RGFW_monitorConnectedFlag)) return;
+ if (!connected && !(win->internal.enabledEvents & RGFW_monitorDisconnectedFlag)) return;
+ }
+
+ RGFW_event event;
+ event.type = (connected) ? (RGFW_monitorConnected) : (RGFW_monitorDisconnected);
+ event.monitor.monitor = monitor;
+ event.common.win = win;
+ RGFW_eventQueuePush(&event);
+
+ if (RGFW_monitorCallbackSrc) RGFW_monitorCallbackSrc(win, monitor, connected);
+}
+
+#ifdef RGFW_DEBUG
+#include
+#endif
+
+void RGFW_sendDebugInfo(RGFW_debugType type, RGFW_errorCode err, const char* msg) {
+ RGFW_debugCallback(type, err, msg);
+
+ #ifdef RGFW_DEBUG
+ switch (type) {
+ case RGFW_typeInfo: RGFW_PRINTF("RGFW INFO (%i %i): %s", type, err, msg); break;
+ case RGFW_typeError: RGFW_PRINTF("RGFW DEBUG (%i %i): %s", type, err, msg); break;
+ case RGFW_typeWarning: RGFW_PRINTF("RGFW WARNING (%i %i): %s", type, err, msg); break;
+ default: break;
+ }
+
+ RGFW_PRINTF("\n");
+ #endif
+}
+
+void RGFW_window_checkMode(RGFW_window* win);
+void RGFW_window_checkMode(RGFW_window* win) {
+ if (RGFW_window_isMinimized(win) && (win->internal.enabledEvents & RGFW_windowMinimizedFlag)) {
+ RGFW_windowMinimizedCallback(win);
+ } else if (RGFW_window_isMaximized(win) && (win->internal.enabledEvents & RGFW_windowMaximizedFlag)) {
+ RGFW_windowMaximizedCallback(win, win->x, win->y, win->w, win->h);
+ } else if ((((win->internal.flags & RGFW_windowMinimize) && !RGFW_window_isMaximized(win)) ||
+ (win->internal.flags & RGFW_windowMaximize && !RGFW_window_isMaximized(win))) && (win->internal.enabledEvents & RGFW_windowRestoredFlag)) {
+ RGFW_windowRestoredCallback(win, win->x, win->y, win->w, win->h);
+ }
+}
+
+/*
+no more event call back defines
+*/
+
+size_t RGFW_sizeofInfo(void) { return sizeof(RGFW_info); }
+size_t RGFW_sizeofNativeImage(void) { return sizeof(RGFW_nativeImage); }
+size_t RGFW_sizeofSurface(void) { return sizeof(RGFW_surface); }
+size_t RGFW_sizeofWindow(void) { return sizeof(RGFW_window); }
+size_t RGFW_sizeofWindowSrc(void) { return sizeof(RGFW_window_src); }
+
+RGFW_window_src* RGFW_window_getSrc(RGFW_window* win) { return &win->src; }
+RGFW_bool RGFW_window_getPosition(RGFW_window* win, i32* x, i32* y) { if (x) *x = win->x; if (y) *y = win->y; return RGFW_TRUE; }
+RGFW_bool RGFW_window_getSize(RGFW_window* win, i32* w, i32* h) { if (w) *w = win->w; if (h) *h = win->h; return RGFW_TRUE; }
+u32 RGFW_window_getFlags(RGFW_window* win) { return win->internal.flags; }
+RGFW_key RGFW_window_getExitKey(RGFW_window* win) { return win->internal.exitKey; }
+void RGFW_window_setExitKey(RGFW_window* win, RGFW_key key) { win->internal.exitKey = key; }
+void RGFW_window_setEnabledEvents(RGFW_window* win, RGFW_eventFlag events) { win->internal.enabledEvents = events; }
+RGFW_eventFlag RGFW_window_getEnabledEvents(RGFW_window* win) { return win->internal.enabledEvents; }
+void RGFW_window_setDisabledEvents(RGFW_window* win, RGFW_eventFlag events) { RGFW_window_setEnabledEvents(win, (RGFW_allEventFlags) & ~(u32)events); }
+void RGFW_window_setEventState(RGFW_window* win, RGFW_eventFlag event, RGFW_bool state) { RGFW_setBit(&win->internal.enabledEvents, event, state); }
+void* RGFW_window_getUserPtr(RGFW_window* win) { return win->userPtr; }
+void RGFW_window_setUserPtr(RGFW_window* win, void* ptr) { win->userPtr = ptr; }
+
+RGFW_bool RGFW_window_getSizeInPixels(RGFW_window* win, i32* w, i32* h) {
+ RGFW_monitor* mon = RGFW_window_getMonitor(win);
+ if (mon == NULL) return RGFW_FALSE;
+
+ if (w) *w = (i32)((float)win->w * mon->pixelRatio);
+ if (h) *h = (i32)((float)win->h * mon->pixelRatio);
+
+ return RGFW_TRUE;
+}
+
+
+#if defined(RGFW_USE_XDL) && defined(RGFW_X11)
+ #define XDL_IMPLEMENTATION
+ #include "XDL.h"
+#endif
+
+#ifndef RGFW_FORCE_INIT
+RGFW_info _rgfwGlobal;
+#endif
+
+i32 RGFW_init(void) { return RGFW_init_ptr(&_rgfwGlobal); }
+void RGFW_deinit(void) { RGFW_deinit_ptr(&_rgfwGlobal); }
+
+i32 RGFW_initPlatform(void);
+void RGFW_deinitPlatform(void);
+
+i32 RGFW_init_ptr(RGFW_info* info) {
+ if (info == _RGFW || info == NULL) return 1;
+
+ RGFW_setInfo(info);
+ RGFW_MEMSET(_RGFW, 0, sizeof(RGFW_info));
+ _RGFW->queueEvents = RGFW_FALSE;
+ _RGFW->polledEvents = RGFW_FALSE;
+#ifdef RGFW_WAYLAND
+ _RGFW->useWaylandBool = RGFW_TRUE;
+#endif
+
+ _RGFW->files = (char**)(void*)_RGFW->filesSrc;
+ u32 i;
+ for (i = 0; i < RGFW_MAX_DROPS; i++)
+ _RGFW->files[i] = (char*)(_RGFW->filesSrc + RGFW_MAX_DROPS + (i * RGFW_MAX_PATH));
+
+ _RGFW->monitors.freeList.head = &_RGFW->monitors.data[0];
+ _RGFW->monitors.freeList.cur = _RGFW->monitors.freeList.head;
+
+ for (i = 1; i < RGFW_MAX_MONITORS; i++) {
+ RGFW_monitorNode* newNode = &_RGFW->monitors.data[i];
+ _RGFW->monitors.freeList.cur->next = newNode;
+ _RGFW->monitors.freeList.cur = _RGFW->monitors.freeList.cur->next;
+ }
+
+ _RGFW->monitors.list.head = NULL;
+ _RGFW->monitors.list.head = NULL;
+ RGFW_initKeycodes();
+ i32 out = RGFW_initPlatform();
+
+ RGFW_pollMonitors();
+
+ RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, "global context initialized");
+
+ return out;
+}
+
+#ifndef RGFW_EGL
+void RGFW_unloadEGL(void) { }
+#endif
+
+void RGFW_deinit_ptr(RGFW_info* info) {
+ if (info == NULL) return;
+
+ RGFW_setInfo(info);
+ RGFW_unloadEGL();
+ RGFW_deinitPlatform();
+
+ _RGFW->root = NULL;
+ _RGFW->windowCount = 0;
+ RGFW_setInfo(NULL);
+ RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, "global context deinitialized");
+}
+
+RGFW_window* RGFW_createWindow(const char* name, i32 x, i32 y, i32 w, i32 h, RGFW_windowFlags flags) {
+ RGFW_window* win = (RGFW_window*)RGFW_ALLOC(sizeof(RGFW_window));
+ RGFW_ASSERT(win != NULL);
+ return RGFW_createWindowPtr(name, x, y, w, h, flags, win);
+}
+
+void RGFW_window_close(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_window_closePtr(win);
+ RGFW_FREE(win);
+}
+
+RGFW_window* RGFW_createWindowPtr(const char* name, i32 x, i32 y, i32 w, i32 h, RGFW_windowFlags flags, RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ if (name == NULL) name = "\0";
+
+ RGFW_MEMSET(win, 0, sizeof(RGFW_window));
+
+ if (_RGFW == NULL) RGFW_init();
+ _RGFW->windowCount++;
+
+ /* rect based the requested flags */
+ if (_RGFW->root == NULL) {
+ RGFW_setRootWindow(win);
+ }
+
+ /* set and init the new window's data */
+ win->x = x;
+ win->y = y;
+ win->w = w;
+ win->h = h;
+ win->internal.flags = flags;
+ win->internal.enabledEvents = RGFW_allEventFlags;
+
+ RGFW_window* ret = RGFW_createWindowPlatform(name, flags, win);
+
+#ifndef RGFW_X11
+ RGFW_window_setFlagsInternal(win, flags, 0);
+#endif
+
+#ifdef RGFW_OPENGL
+ win->src.gfxType = 0;
+ if (flags & RGFW_windowOpenGL)
+ RGFW_window_createContext_OpenGL(win, RGFW_getGlobalHints_OpenGL());
+#endif
+
+#ifdef RGFW_EGL
+ if (flags & RGFW_windowEGL)
+ RGFW_window_createContext_EGL(win, RGFW_getGlobalHints_OpenGL());
+#endif
+
+ /* X11 creates the window after the OpenGL context is created (because of visual garbage),
+ * so we have to wait to set the flags
+ * This is required so that way the user can create their own OpenGL context after RGFW_createWindow is used
+ * if a window is created, CreateContext will delete the window and create a new one
+ * */
+#ifdef RGFW_X11
+ RGFW_window_setFlagsInternal(win, flags, 0);
+#endif
+
+#ifdef RGFW_MACOS
+ /*NOTE: another OpenGL/setFlags related hack, this because OSX the 'view' class must be setup after the NSOpenGL view is made AND after setFlags happens */
+ RGFW_osx_initView(win);
+#endif
+
+#ifdef RGFW_WAYLAND
+ /* recieve all events needed to configure the surface */
+ /* also gets the wl_outputs */
+ if (RGFW_usingWayland()) {
+ wl_display_roundtrip(_RGFW->wl_display);
+ /* NOTE: this is a hack so that way wayland spawns a window, even if nothing is drawn */
+ if (!(flags & RGFW_windowOpenGL) && !(flags & RGFW_windowEGL)) {
+ u8* data = (u8*)RGFW_ALLOC((u32)(win->w * win->h * 3));
+ RGFW_MEMSET(data, 0, (u32)(win->w * win->h * 3) * sizeof(u8));
+ RGFW_surface* surface = RGFW_createSurface(data, win->w, win->h, RGFW_formatBGR8);
+ RGFW_window_blitSurface(win, surface);
+ RGFW_FREE(data);
+ RGFW_surface_free(surface);
+ }
+ }
+#endif
+
+ if (!(flags & RGFW_windowHideMouse)) {
+ RGFW_window_setMouseDefault(win);
+ }
+
+ RGFW_window_setName(win, name);
+ if (!(flags & RGFW_windowHide)) {
+ flags |= RGFW_windowHide;
+ RGFW_window_show(win);
+ }
+
+ RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, "a new window was created");
+
+ return ret;
+}
+
+void RGFW_window_closePtr(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+
+ if (win->internal.captureMouse) {
+ RGFW_window_captureMouse(win, RGFW_FALSE);
+ }
+
+ #ifdef RGFW_EGL
+ if ((win->src.gfxType & RGFW_gfxEGL) && win->src.ctx.egl) {
+ RGFW_window_deleteContext_EGL(win, win->src.ctx.egl);
+ win->src.ctx.egl = NULL;
+ }
+ #endif
+
+ #ifdef RGFW_OPENGL
+ if ((win->src.gfxType & RGFW_gfxNativeOpenGL) && win->src.ctx.native) {
+ RGFW_window_deleteContext_OpenGL(win, win->src.ctx.native);
+ win->src.ctx.native = NULL;
+ }
+ #endif
+
+ RGFW_window_closePlatform(win);
+
+ RGFW_clipboard_switch(NULL);
+
+ _RGFW->windowCount--;
+ RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, "a window was freed");
+
+ if (_RGFW->windowCount == 0 && !(win->internal.flags & RGFW_noDeinitOnClose)) RGFW_deinit();
+}
+
+void RGFW_setQueueEvents(RGFW_bool queue) { _RGFW->queueEvents = RGFW_BOOL(queue); }
+
+void RGFW_eventQueueFlush(void) { _RGFW->eventLen = 0; }
+
+void RGFW_eventQueuePush(const RGFW_event* event) {
+ if (_RGFW->queueEvents == RGFW_FALSE) return;
+ RGFW_ASSERT(_RGFW->eventLen >= 0);
+
+ if (_RGFW->eventLen >= RGFW_MAX_EVENTS) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errEventQueue, "Event queue limit 'RGFW_MAX_EVENTS' has been reached automatically flushing queue.");
+ RGFW_eventQueueFlush();
+ return;
+ }
+
+ i32 eventTop = (_RGFW->eventBottom + _RGFW->eventLen) % RGFW_MAX_EVENTS;
+ _RGFW->eventLen += 1;
+ _RGFW->events[eventTop] = *event;
+}
+
+RGFW_event* RGFW_eventQueuePop(void) {
+ RGFW_ASSERT(_RGFW->eventLen >= 0 && _RGFW->eventLen <= RGFW_MAX_EVENTS);
+ RGFW_event* ev;
+
+ if (_RGFW->eventLen == 0) {
+ return NULL;
+ }
+
+ ev = &_RGFW->events[_RGFW->eventBottom];
+ _RGFW->eventLen -= 1;
+ _RGFW->eventBottom = (_RGFW->eventBottom + 1) % RGFW_MAX_EVENTS;
+
+ return ev;
+}
+
+RGFW_bool RGFW_checkEvent(RGFW_event* event) {
+ if (_RGFW->eventLen == 0 && _RGFW->polledEvents == RGFW_FALSE) {
+ _RGFW->queueEvents = RGFW_TRUE;
+ RGFW_pollEvents();
+ _RGFW->polledEvents = RGFW_TRUE;
+ }
+
+ if (RGFW_checkQueuedEvent(event) == RGFW_FALSE) {
+ _RGFW->polledEvents = RGFW_FALSE;
+ return RGFW_FALSE;
+ }
+
+ return RGFW_TRUE;
+}
+
+RGFW_bool RGFW_checkQueuedEvent(RGFW_event* event) {
+ RGFW_event* ev;
+ _RGFW->queueEvents = RGFW_TRUE;
+ /* check queued events */
+ ev = RGFW_eventQueuePop();
+ if (ev != NULL) {
+ *event = *ev;
+ return RGFW_TRUE;
+ }
+
+ return RGFW_FALSE;
+}
+
+void RGFW_resetPrevState(void) {
+ size_t i; /*!< reset each previous state */
+ for (i = 0; i < RGFW_keyLast; i++) _RGFW->keyboard[i].prev = _RGFW->keyboard[i].current;
+ for (i = 0; i < RGFW_mouseFinal; i++) _RGFW->mouseButtons[i].prev = _RGFW->mouseButtons[i].current;
+ _RGFW->scrollX = 0.0f;
+ _RGFW->scrollY = 0.0f;
+ _RGFW->vectorX = (float)0.0f;
+ _RGFW->vectorY = (float)0.0f;
+ RGFW_MEMSET(&_RGFW->windowState, 0, sizeof(_RGFW->windowState));
+}
+
+RGFW_bool RGFW_isKeyPressed(RGFW_key key) {
+ RGFW_ASSERT(_RGFW != NULL);
+ return _RGFW->keyboard[key].current && !_RGFW->keyboard[key].prev;
+}
+RGFW_bool RGFW_isKeyDown(RGFW_key key) {
+ RGFW_ASSERT(_RGFW != NULL);
+ return _RGFW->keyboard[key].current;
+}
+RGFW_bool RGFW_isKeyReleased(RGFW_key key) {
+ RGFW_ASSERT(_RGFW != NULL);
+ return !_RGFW->keyboard[key].current && _RGFW->keyboard[key].prev;
+}
+
+
+RGFW_bool RGFW_isMousePressed(RGFW_mouseButton button) {
+ RGFW_ASSERT(_RGFW != NULL);
+ return _RGFW->mouseButtons[button].current && !_RGFW->mouseButtons[button].prev;
+}
+RGFW_bool RGFW_isMouseDown(RGFW_mouseButton button) {
+ RGFW_ASSERT(_RGFW != NULL);
+ return _RGFW->mouseButtons[button].current;
+}
+RGFW_bool RGFW_isMouseReleased(RGFW_mouseButton button) {
+ RGFW_ASSERT(_RGFW != NULL);
+ return !_RGFW->mouseButtons[button].current && _RGFW->mouseButtons[button].prev;
+}
+
+void RGFW_getMouseScroll(float* x, float* y) {
+ RGFW_ASSERT(_RGFW != NULL);
+ if (x) *x = _RGFW->scrollX;
+ if (y) *y = _RGFW->scrollY;
+}
+
+void RGFW_getMouseVector(float* x, float* y) {
+ RGFW_ASSERT(_RGFW != NULL);
+ if (x) *x = _RGFW->vectorX;
+ if (y) *y = _RGFW->vectorY;
+}
+
+RGFW_bool RGFW_window_didMouseLeave(RGFW_window* win) { return _RGFW->windowState.winLeave == win && _RGFW->windowState.mouseLeave; }
+RGFW_bool RGFW_window_didMouseEnter(RGFW_window* win) { return _RGFW->windowState.win == win && _RGFW->windowState.mouseEnter; }
+RGFW_bool RGFW_window_isMouseInside(RGFW_window* win) { return win->internal.mouseInside; }
+
+RGFW_bool RGFW_window_isDataDragging(RGFW_window* win) { return RGFW_window_getDataDrag(win, (i32*)NULL, (i32*)NULL); }
+RGFW_bool RGFW_window_didDataDrop(RGFW_window* win) { return RGFW_window_getDataDrop(win, (const char***)NULL, (size_t*)NULL);}
+
+
+RGFW_bool RGFW_window_getDataDrag(RGFW_window* win, i32* x, i32* y) {
+ if (_RGFW->windowState.win != win || _RGFW->windowState.dataDragging == RGFW_FALSE) return RGFW_FALSE;
+ if (x) *x = _RGFW->windowState.dropX;
+ if (y) *y = _RGFW->windowState.dropY;
+ return RGFW_TRUE;
+}
+RGFW_bool RGFW_window_getDataDrop(RGFW_window* win, const char*** files, size_t* count) {
+ if (_RGFW->windowState.win != win || _RGFW->windowState.dataDrop == RGFW_FALSE) return RGFW_FALSE;
+ if (files) *files = (const char**)_RGFW->files;
+ if (count) *count = _RGFW->windowState.filesCount;
+ return RGFW_TRUE;
+}
+
+RGFW_bool RGFW_window_checkEvent(RGFW_window* win, RGFW_event* event) {
+ if (_RGFW->eventLen == 0 && _RGFW->polledEvents == RGFW_FALSE) {
+ _RGFW->queueEvents = RGFW_TRUE;
+ RGFW_pollEvents();
+ _RGFW->polledEvents = RGFW_TRUE;
+ }
+
+ if (RGFW_window_checkQueuedEvent(win, event) == RGFW_FALSE) {
+ _RGFW->polledEvents = RGFW_FALSE;
+ return RGFW_FALSE;
+ }
+
+ return RGFW_TRUE;
+}
+
+RGFW_bool RGFW_window_checkQueuedEvent(RGFW_window* win, RGFW_event* event) {
+ RGFW_event* ev;
+ RGFW_ASSERT(win != NULL);
+ _RGFW->queueEvents = RGFW_TRUE;
+ /* check queued events */
+ ev = RGFW_window_eventQueuePop(win);
+ if (ev == NULL) return RGFW_FALSE;
+
+ *event = *ev;
+ return RGFW_TRUE;
+}
+
+RGFW_event* RGFW_window_eventQueuePop(RGFW_window* win) {
+ RGFW_event* ev = RGFW_eventQueuePop();
+ if (ev == NULL) return ev;
+
+ for (i32 i = 1; i < _RGFW->eventLen && ev->common.win != win && ev->common.win != NULL; i++) {
+ RGFW_eventQueuePush(ev);
+ ev = RGFW_eventQueuePop();
+ }
+
+ if (ev->common.win != win && ev->common.win != NULL) {
+ return NULL;
+ }
+
+ return ev;
+}
+
+void RGFW_setRootWindow(RGFW_window* win) { _RGFW->root = win; }
+RGFW_window* RGFW_getRootWindow(void) { return _RGFW->root; }
+
+#ifndef RGFW_EGL
+RGFW_bool RGFW_loadEGL(void) { return RGFW_FALSE; }
+#endif
+
+void RGFW_window_setFlagsInternal(RGFW_window* win, RGFW_windowFlags flags, RGFW_windowFlags cmpFlags) {
+ if (flags & RGFW_windowNoBorder) RGFW_window_setBorder(win, 0);
+ else if (cmpFlags & RGFW_windowNoBorder) RGFW_window_setBorder(win, 1);
+ if (flags & RGFW_windowScaleToMonitor) RGFW_window_scaleToMonitor(win);
+ if (flags & RGFW_windowMaximize) RGFW_window_maximize(win);
+ else if (cmpFlags & RGFW_windowMaximize) RGFW_window_restore(win);
+ if (flags & RGFW_windowMinimize) RGFW_window_minimize(win);
+ else if (cmpFlags & RGFW_windowMinimize) RGFW_window_restore(win);
+ if (flags & RGFW_windowCenter) RGFW_window_center(win);
+ if (flags & RGFW_windowCenterCursor) RGFW_window_moveMouse(win, win->x + (win->w / 2), win->y + (win->h / 2));
+ if (flags & RGFW_windowFullscreen) RGFW_window_setFullscreen(win, RGFW_TRUE);
+ else if (cmpFlags & RGFW_windowFullscreen) RGFW_window_setFullscreen(win, 0);
+ if (flags & RGFW_windowHideMouse) RGFW_window_showMouse(win, 0);
+ else if (cmpFlags & RGFW_windowHideMouse) RGFW_window_showMouse(win, 1);
+ if (flags & RGFW_windowHide) RGFW_window_hide(win);
+ else if (cmpFlags & RGFW_windowHide) RGFW_window_show(win);
+ if (flags & RGFW_windowFloating) RGFW_window_setFloating(win, 1);
+ else if (cmpFlags & RGFW_windowFloating) RGFW_window_setFloating(win, 0);
+ if (flags & RGFW_windowRawMouse) RGFW_window_setRawMouseMode(win, RGFW_TRUE);
+ else if (cmpFlags & RGFW_windowRawMouse) RGFW_window_setRawMouseMode(win, RGFW_FALSE);
+ if (flags & RGFW_windowCaptureMouse) RGFW_window_captureRawMouse(win, RGFW_TRUE);
+ else if (cmpFlags & RGFW_windowCaptureMouse) RGFW_window_captureMouse(win, RGFW_FALSE);
+ if (flags & RGFW_windowFocus) RGFW_window_focus(win);
+
+ if (flags & RGFW_windowNoResize) {
+ RGFW_window_setMaxSize(win, win->w, win->h);
+ RGFW_window_setMinSize(win, win->w, win->h);
+ } else if (cmpFlags & RGFW_windowNoResize) {
+ RGFW_window_setMaxSize(win, 0, 0);
+ RGFW_window_setMinSize(win, 0, 0);
+ }
+
+ win->internal.flags = flags;
+}
+
+
+void RGFW_window_setFlags(RGFW_window* win, RGFW_windowFlags flags) { RGFW_window_setFlagsInternal(win, flags, win->internal.flags); }
+
+RGFW_bool RGFW_window_isInFocus(RGFW_window* win) {
+#ifdef RGFW_WASM
+ return RGFW_TRUE;
+#else
+ return RGFW_BOOL(win->internal.inFocus);
+#endif
+}
+
+void RGFW_setClassName(const char* name) { RGFW_init(); _RGFW->className = name; }
+
+#ifndef RGFW_X11
+void RGFW_setXInstName(const char* name) { RGFW_UNUSED(name); }
+#endif
+
+RGFW_bool RGFW_window_getMouse(RGFW_window* win, i32* x, i32* y) {
+ RGFW_ASSERT(win != NULL);
+ if (x) *x = win->internal.lastMouseX;
+ if (y) *y = win->internal.lastMouseY;
+ return RGFW_TRUE;
+}
+
+RGFW_bool RGFW_window_isKeyPressed(RGFW_window* win, RGFW_key key) { return RGFW_isKeyPressed(key) && RGFW_window_isInFocus(win); }
+RGFW_bool RGFW_window_isKeyDown(RGFW_window* win, RGFW_key key) { return RGFW_isKeyDown(key) && RGFW_window_isInFocus(win); }
+RGFW_bool RGFW_window_isKeyReleased(RGFW_window* win, RGFW_key key) { return RGFW_isKeyReleased(key) && RGFW_window_isInFocus(win); }
+
+RGFW_bool RGFW_window_isMousePressed(RGFW_window* win, RGFW_mouseButton button) { return RGFW_isMousePressed(button) && RGFW_window_isInFocus(win); }
+RGFW_bool RGFW_window_isMouseDown(RGFW_window* win, RGFW_mouseButton button) { return RGFW_isMouseDown(button) && RGFW_window_isInFocus(win); }
+RGFW_bool RGFW_window_isMouseReleased(RGFW_window* win, RGFW_mouseButton button) { return RGFW_isMouseReleased(button) && RGFW_window_isInFocus(win); }
+
+
+
+#ifndef RGFW_X11
+void* RGFW_getDisplay_X11(void) { return NULL; }
+u64 RGFW_window_getWindow_X11(RGFW_window* win) { RGFW_UNUSED(win); return 0; }
+#endif
+
+#ifndef RGFW_WAYLAND
+struct wl_display* RGFW_getDisplay_Wayland(void) { return NULL; }
+struct wl_surface* RGFW_window_getWindow_Wayland(RGFW_window* win) { RGFW_UNUSED(win); return NULL; }
+#endif
+
+#ifndef RGFW_WINDOWS
+void* RGFW_window_getHWND(RGFW_window* win) { RGFW_UNUSED(win); return NULL; }
+void* RGFW_window_getHDC(RGFW_window* win) { RGFW_UNUSED(win); return NULL; }
+#endif
+
+#ifndef RGFW_MACOS
+void* RGFW_window_getView_OSX(RGFW_window* win) { RGFW_UNUSED(win); return NULL; }
+void RGFW_window_setLayer_OSX(RGFW_window* win, void* layer) { RGFW_UNUSED(win); RGFW_UNUSED(layer); }
+void* RGFW_getLayer_OSX(void) { return NULL; }
+void* RGFW_window_getWindow_OSX(RGFW_window* win) { RGFW_UNUSED(win); return NULL; }
+#endif
+
+void RGFW_setBit(u32* var, u32 mask, RGFW_bool set) {
+ if (set) *var |= mask;
+ else *var &= ~mask;
+}
+
+void RGFW_window_center(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_monitor* mon = RGFW_window_getMonitor(win);
+ if (mon == NULL) return;
+
+ RGFW_window_move(win, (i32)(mon->mode.w - win->w) / 2, (mon->mode.h - win->h) / 2);
+}
+
+RGFW_bool RGFW_monitor_scaleToWindow(RGFW_monitor* mon, RGFW_window* win) {
+ RGFW_monitorMode mode;
+ RGFW_ASSERT(win != NULL);
+
+ mode.w = win->w;
+ mode.h = win->h;
+ RGFW_bool ret = RGFW_monitor_requestMode(mon, &mode, RGFW_monitorScale);
+
+
+ /* move window to monitor origin so it doesn't move to the next monitor */
+ RGFW_window_move(win, mon->x, mon->y);
+
+ return ret;
+}
+
+void RGFW_splitBPP(u32 bpp, RGFW_monitorMode* mode) {
+ if (bpp == 32) bpp = 24;
+ mode->red = mode->green = mode->blue = (u8)(bpp / 3);
+
+ u32 delta = bpp - (mode->red * 3); /* handle leftovers */
+ if (delta >= 1) mode->green = mode->green + 1;
+ if (delta == 2) mode->red = mode->red + 1;
+}
+
+RGFW_bool RGFW_monitorModeCompare(RGFW_monitorMode* mon, RGFW_monitorMode* mon2, RGFW_modeRequest request) {
+ RGFW_ASSERT(mon);
+ RGFW_ASSERT(mon2);
+
+ return (((mon->w == mon2->w && mon->h == mon2->h) || !(request & RGFW_monitorScale)) &&
+ ((mon->refreshRate == mon2->refreshRate) || !(request & RGFW_monitorRefresh)) &&
+ ((mon->red == mon2->red && mon->green == mon2->green && mon->blue == mon2->blue) || !(request & RGFW_monitorRGB)));
+}
+
+RGFW_bool RGFW_window_shouldClose(RGFW_window* win) {
+ return (win == NULL || win->internal.shouldClose || (win->internal.exitKey && RGFW_window_isKeyDown(win, win->internal.exitKey)));
+}
+
+void RGFW_window_setShouldClose(RGFW_window* win, RGFW_bool shouldClose) {
+ if (shouldClose) {
+ RGFW_windowQuitCallback(win);
+ } else {
+ win->internal.shouldClose = RGFW_FALSE;
+ }
+}
+
+void RGFW_window_scaleToMonitor(RGFW_window* win) {
+ RGFW_monitor* monitor = RGFW_window_getMonitor(win);
+ if (monitor->scaleX == 0 && monitor->scaleY == 0)
+ return;
+
+ RGFW_window_resize(win, (i32)(monitor->scaleX * (float)win->w), (i32)(monitor->scaleY * (float)win->h));
+}
+
+void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor* m) {
+ RGFW_window_move(win, m->x + win->x, m->y + win->y);
+}
+
+RGFW_surface* RGFW_createSurface(u8* data, i32 w, i32 h, RGFW_format format) {
+ RGFW_surface* surface = (RGFW_surface*)RGFW_ALLOC(sizeof(RGFW_surface));
+ RGFW_MEMSET(surface, 0, sizeof(RGFW_surface));
+ RGFW_createSurfacePtr(data, w, h, format, surface);
+ return surface;
+}
+
+void RGFW_surface_setConvertFunc(RGFW_surface* surface, RGFW_convertImageDataFunc func) {
+ surface->convertFunc = func;
+}
+
+void RGFW_surface_free(RGFW_surface* surface) {
+ RGFW_surface_freePtr(surface);
+ RGFW_FREE(surface);
+}
+
+RGFW_nativeImage* RGFW_surface_getNativeImage(RGFW_surface* surface) {
+ return &surface->native;
+}
+
+RGFW_surface* RGFW_window_createSurface(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format) {
+ RGFW_surface* surface = (RGFW_surface*)RGFW_ALLOC(sizeof(RGFW_surface));
+ RGFW_MEMSET(surface, 0, sizeof(RGFW_surface));
+ RGFW_window_createSurfacePtr(win, data, w, h, format, surface);
+ return surface;
+}
+
+#ifndef RGFW_X11
+RGFW_bool RGFW_window_createSurfacePtr(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) {
+ RGFW_UNUSED(win);
+ return RGFW_createSurfacePtr(data, w, h, format, surface);
+}
+#endif
+
+const RGFW_colorLayout RGFW_layouts[RGFW_formatCount] = {
+ { 0, 1, 2, 3, 3 }, /* RGFW_formatRGB8 */
+ { 2, 1, 0, 3, 3 }, /* RGFW_formatBGR8 */
+ { 0, 1, 2, 3, 4 }, /* RGFW_formatRGBA8 */
+ { 1, 2, 3, 0, 4 }, /* RGFW_formatARGB8 */
+ { 2, 1, 0, 3, 4 }, /* RGFW_formatBGRA8 */
+ { 3, 2, 1, 0, 4 }, /* RGFW_formatABGR8 */
+};
+
+
+void RGFW_copyImageData(u8* dest_data, i32 w, i32 h, RGFW_format dest_format, u8* src_data, RGFW_format src_format, RGFW_convertImageDataFunc func) {
+ RGFW_copyImageData64(dest_data, w, h, dest_format, src_data, src_format, RGFW_FALSE, func);
+}
+
+RGFWDEF void RGFW_convertImageData64(u8* dest_data, u8* src_data, const RGFW_colorLayout* srcLayout, const RGFW_colorLayout* destLayout, size_t count, RGFW_bool is64bit);
+void RGFW_convertImageData64(u8* dest_data, u8* src_data, const RGFW_colorLayout* srcLayout, const RGFW_colorLayout* destLayout, size_t count, RGFW_bool is64bit) {
+ u32 i, i2 = 0;
+ u8 rgba[4] = {0};
+
+ for (i = 0; i < count; i++) {
+ const u8* src_px = &src_data[i * srcLayout->channels];
+ u8* dst_px = &dest_data[i2 * destLayout->channels];
+ rgba[0] = src_px[srcLayout->r];
+ rgba[1] = src_px[srcLayout->g];
+ rgba[2] = src_px[srcLayout->b];
+ rgba[3] = (srcLayout->channels == 4) ? src_px[srcLayout->a] : 255;
+
+ dst_px[destLayout->r] = rgba[0];
+ dst_px[destLayout->g] = rgba[1];
+ dst_px[destLayout->b] = rgba[2];
+ if (destLayout->channels == 4)
+ dst_px[destLayout->a] = rgba[3];
+
+ i2 += 1 + is64bit;
+ }
+}
+
+void RGFW_copyImageData64(u8* dest_data, i32 dest_w, i32 dest_h, RGFW_format dest_format, u8* src_data, RGFW_format src_format, RGFW_bool is64bit, RGFW_convertImageDataFunc func) {
+ RGFW_ASSERT(dest_data && src_data);
+
+ u32 count = (u32)(dest_w * dest_h);
+
+ if (src_format == dest_format) {
+ u32 channels = (dest_format >= RGFW_formatRGBA8) ? 4 : 3;
+ RGFW_MEMCPY(dest_data, src_data, count * channels);
+ return;
+ }
+
+ const RGFW_colorLayout* srcLayout = &RGFW_layouts[src_format];
+ const RGFW_colorLayout* destLayout = &RGFW_layouts[dest_format];
+
+ if (is64bit || func == NULL) {
+ RGFW_convertImageData64(dest_data, src_data, srcLayout, destLayout, count, is64bit);
+ } else {
+ func(dest_data, src_data, srcLayout, destLayout, count);
+ }
+}
+
+RGFW_monitorNode* RGFW_monitors_add(const RGFW_monitor* mon) {
+ RGFW_monitorNode* node = NULL;
+ if (_RGFW->monitors.freeList.head == NULL) return node;
+
+ node = _RGFW->monitors.freeList.head;
+
+ _RGFW->monitors.freeList.head = node->next;
+ if (_RGFW->monitors.freeList.head == NULL) {
+ _RGFW->monitors.freeList.cur = NULL;
+ }
+
+ node->next = NULL;
+
+ if (_RGFW->monitors.list.head == NULL) {
+ _RGFW->monitors.list.head = node;
+ } else {
+ _RGFW->monitors.list.cur->next = node;
+ }
+
+ _RGFW->monitors.list.cur = node;
+
+ if (mon) node->mon = *mon;
+ node->mon.node = node;
+ node->disconnected = RGFW_FALSE;
+
+ _RGFW->monitors.count += 1;
+ return node;
+}
+
+void RGFW_monitors_remove(RGFW_monitorNode* node, RGFW_monitorNode* prev) {
+ _RGFW->monitors.count -= 1;
+
+ /* remove node from the list */
+ if (prev != node) {
+ prev->next = node->next;
+ } else { /* node is the head */
+ _RGFW->monitors.list.head = NULL;
+ }
+
+ node->next = NULL;
+
+ /* move node to the free list */
+ if (_RGFW->monitors.freeList.head == NULL) {
+ _RGFW->monitors.freeList.head = node;
+ } else {
+ _RGFW->monitors.freeList.cur->next = node;
+ }
+
+ _RGFW->monitors.freeList.cur = node;
+}
+
+void RGFW_monitors_refresh(void) {
+ RGFW_monitorNode* prev = _RGFW->monitors.list.head;
+ for (RGFW_monitorNode* node = _RGFW->monitors.list.head; node; node = node->next) {
+ if (node->disconnected == RGFW_FALSE) continue;
+
+ RGFW_monitorCallback(_RGFW->root, &node->mon, RGFW_FALSE);
+ RGFW_monitors_remove(node, prev);
+ prev = node;
+ }
+}
+
+RGFW_monitorMode* RGFW_monitor_getModes(RGFW_monitor* monitor, size_t* count) {
+ size_t num = RGFW_monitor_getModesPtr(monitor, NULL);
+ RGFW_monitorMode* modes = (RGFW_monitorMode*)RGFW_ALLOC(num * sizeof(RGFW_monitorNode));
+ num = RGFW_monitor_getModesPtr(monitor, &modes);
+
+ if (count) *count = num;
+ return modes;
+}
+
+void RGFW_freeModes(RGFW_monitorMode* modes) {
+ RGFW_FREE(modes);
+}
+
+RGFW_bool RGFW_monitor_findClosestMode(RGFW_monitor* monitor, RGFW_monitorMode* mode, RGFW_monitorMode* closest) {
+ size_t count = RGFW_monitor_getModesPtr(monitor, NULL);
+ RGFW_monitorMode* modes = (RGFW_monitorMode*)RGFW_ALLOC(count * sizeof(RGFW_monitorNode));
+ count = RGFW_monitor_getModesPtr(monitor, &modes);
+
+ RGFW_monitorMode* chosen = NULL;
+
+ u32 topScore = 1;
+ for (size_t i = 0; i < count; i++) {
+ RGFW_monitorMode* mode2 = &modes[i];
+
+ u32 score = 0;
+ if (mode->w == mode2->w && mode->h == mode2->h) score += 1000;
+ if (mode->red == mode2->red && mode->green == mode2->green && mode->blue == mode2->blue) score += 100;
+ if (mode->refreshRate == mode->refreshRate) score += 10;
+
+ if (score > topScore) {
+ topScore = score;
+ chosen = mode2;
+ }
+ }
+
+ if (chosen && closest) *closest = *chosen;
+
+
+ RGFW_FREE(modes);
+
+ return (chosen == NULL) ? RGFW_FALSE : RGFW_TRUE;
+}
+
+RGFW_bool RGFW_monitor_getPosition(RGFW_monitor* monitor, i32* x, i32* y) {
+ if (x) *x = monitor->x;
+ if (y) *y = monitor->y;
+ return RGFW_TRUE;
+}
+
+const char* RGFW_monitor_getName(RGFW_monitor* monitor) {
+ return monitor->name;
+}
+
+RGFW_bool RGFW_monitor_getScale(RGFW_monitor* monitor, float* x, float* y) {
+ if (x) *x = monitor->scaleX;
+ if (y) *y = monitor->scaleY;
+ return RGFW_TRUE;
+}
+
+RGFW_bool RGFW_monitor_getPhysicalSize(RGFW_monitor* monitor, float* w, float* h) {
+ if (w) *w = monitor->physW;
+ if (h) *h = monitor->physH;
+ return RGFW_TRUE;
+}
+
+void RGFW_monitor_setUserPtr(RGFW_monitor* monitor, void* userPtr) {
+ monitor->userPtr = userPtr;
+}
+
+void* RGFW_monitor_getUserPtr(RGFW_monitor* monitor) {
+ return monitor->userPtr;
+}
+
+RGFW_bool RGFW_monitor_getMode(RGFW_monitor* monitor, RGFW_monitorMode* mode) {
+ if (mode) *mode = monitor->mode;
+ return RGFW_TRUE;
+}
+
+RGFW_gammaRamp* RGFW_monitor_getGammaRamp(RGFW_monitor* monitor) {
+ RGFW_gammaRamp* ramp = (RGFW_gammaRamp*)RGFW_ALLOC(sizeof(RGFW_gammaRamp));
+ ramp->count = RGFW_monitor_getGammaRampPtr(monitor, NULL);
+ ramp->red = (u16*)RGFW_ALLOC(sizeof(u16) * ramp->count);
+ ramp->green = (u16*)RGFW_ALLOC(sizeof(u16) * ramp->count);
+ ramp->blue = (u16*)RGFW_ALLOC(sizeof(u16) * ramp->count);
+ ramp->count = RGFW_monitor_getGammaRampPtr(monitor, ramp);
+
+ return ramp;
+}
+
+void RGFW_freeGammaRamp(RGFW_gammaRamp* ramp) {
+ RGFW_FREE(ramp->red);
+ RGFW_FREE(ramp->green);
+ RGFW_FREE(ramp->blue);
+ RGFW_FREE(ramp);
+}
+
+RGFW_bool RGFW_monitor_setGammaPtr(RGFW_monitor* monitor, float gamma, u16* ptr, size_t count) {
+ RGFW_ASSERT(monitor);
+ RGFW_ASSERT(gamma > 0.0f);
+
+ size_t i;
+ for (i = 0; i < count; i++) {
+ float value = (float)i / (float) (count - 1);
+ #ifndef RGFW_NO_MATH
+ value = powf(value, 1.f / gamma) * 65535.f + 0.5f;
+ #endif
+ value = RGFW_MIN(value, 65535.f);
+
+ ptr[i] = (u16)value;
+ }
+
+ RGFW_gammaRamp ramp;
+ ramp.red = ptr;
+ ramp.green = ptr;
+ ramp.blue = ptr;
+ ramp.count = count;
+
+ return RGFW_monitor_setGammaRamp(monitor, &ramp);
+}
+
+RGFW_bool RGFW_monitor_setGamma(RGFW_monitor* monitor, float gamma) {
+ size_t count = RGFW_monitor_getGammaRampPtr(monitor, NULL);
+ u16* ptr = (u16*)RGFW_ALLOC(count * sizeof(u16));
+
+ RGFW_bool ret = RGFW_monitor_setGammaPtr(monitor, gamma, ptr, count);
+ RGFW_FREE(ptr);
+
+ return ret;
+}
+
+RGFW_monitor** RGFW_getMonitors(size_t* len) {
+ static RGFW_monitor* monitors[RGFW_MAX_MONITORS];
+ RGFW_init();
+ if (len != NULL) {
+ *len = _RGFW->monitors.count;
+ }
+
+ u8 i = 0;
+ RGFW_monitorNode* cur_node = _RGFW->monitors.list.head;
+ while (cur_node != NULL) {
+ monitors[i] = &cur_node->mon;
+ i++;
+ cur_node = cur_node->next;
+ }
+ return monitors;
+}
+
+RGFW_monitor* RGFW_getPrimaryMonitor(void) {
+ if (_RGFW->monitors.primary == NULL) {
+ _RGFW->monitors.primary = _RGFW->monitors.list.head;
+ }
+
+ return &_RGFW->monitors.primary->mon;
+}
+
+RGFW_bool RGFW_window_setIcon(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format) {
+ return RGFW_window_setIconEx(win, data, w, h, format, RGFW_iconBoth);
+}
+
+void RGFW_window_captureMouse(RGFW_window* win, RGFW_bool state) {
+ win->internal.captureMouse = state;
+ RGFW_window_captureMousePlatform(win, state);
+}
+
+void RGFW_window_setRawMouseMode(RGFW_window* win, RGFW_bool state) {
+ win->internal.rawMouse = state;
+ RGFW_window_setRawMouseModePlatform(win, state);
+}
+
+void RGFW_window_captureRawMouse(RGFW_window* win, RGFW_bool state) {
+ RGFW_window_captureMouse(win, state);
+ RGFW_window_setRawMouseMode(win, state);
+}
+
+RGFW_bool RGFW_window_isRawMouseMode(RGFW_window* win) { return RGFW_BOOL(win->internal.rawMouse); }
+RGFW_bool RGFW_window_isCaptured(RGFW_window* win) { return RGFW_BOOL(win->internal.captureMouse); }
+
+void RGFW_updateKeyMod(RGFW_window* win, RGFW_keymod mod, RGFW_bool value) {
+ if (value) win->internal.mod |= mod;
+ else win->internal.mod &= ~mod;
+}
+
+void RGFW_updateKeyModsEx(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll) {
+ RGFW_updateKeyMod(win, RGFW_modCapsLock, capital);
+ RGFW_updateKeyMod(win, RGFW_modNumLock, numlock);
+ RGFW_updateKeyMod(win, RGFW_modControl, control);
+ RGFW_updateKeyMod(win, RGFW_modAlt, alt);
+ RGFW_updateKeyMod(win, RGFW_modShift, shift);
+ RGFW_updateKeyMod(win, RGFW_modSuper, super);
+ RGFW_updateKeyMod(win, RGFW_modScrollLock, scroll);
+}
+
+void RGFW_updateKeyMods(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool scroll) {
+ RGFW_updateKeyModsEx(win, capital, numlock,
+ RGFW_window_isKeyDown(win, RGFW_controlL) || RGFW_window_isKeyDown(win, RGFW_controlR),
+ RGFW_window_isKeyDown(win, RGFW_altL) || RGFW_window_isKeyDown(win, RGFW_altR),
+ RGFW_window_isKeyDown(win, RGFW_shiftL) || RGFW_window_isKeyDown(win, RGFW_shiftR),
+ RGFW_window_isKeyDown(win, RGFW_superL) || RGFW_window_isKeyDown(win, RGFW_superR),
+ scroll);
+}
+
+void RGFW_window_showMouseFlags(RGFW_window* win, RGFW_bool show) {
+ if (show && (win->internal.flags & RGFW_windowHideMouse))
+ win->internal.flags ^= RGFW_windowHideMouse;
+ else if (!show && !(win->internal.flags & RGFW_windowHideMouse))
+ win->internal.flags |= RGFW_windowHideMouse;
+}
+
+RGFW_bool RGFW_window_isMouseHidden(RGFW_window* win) {
+ return (RGFW_bool)RGFW_BOOL(((RGFW_window*)win)->internal.flags & RGFW_windowHideMouse);
+}
+
+RGFW_bool RGFW_window_borderless(RGFW_window* win) {
+ return (RGFW_bool)RGFW_BOOL(win->internal.flags & RGFW_windowNoBorder);
+}
+
+RGFW_bool RGFW_window_isFullscreen(RGFW_window* win){ return RGFW_BOOL(win->internal.flags & RGFW_windowFullscreen); }
+RGFW_bool RGFW_window_allowsDND(RGFW_window* win) { return RGFW_BOOL(win->internal.flags & RGFW_windowAllowDND); }
+
+#ifndef RGFW_WINDOWS
+void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow) {
+ RGFW_setBit(&win->internal.flags, RGFW_windowAllowDND, allow);
+}
+#endif
+
+#if defined(RGFW_X11) || defined(RGFW_MACOS) || defined(RGFW_WASM) || defined(RGFW_WAYLAND)
+#ifndef __USE_POSIX199309
+ #define __USE_POSIX199309
+#endif
+#include
+struct timespec;
+#endif
+
+#if defined(RGFW_WAYLAND) || defined(RGFW_X11) || defined(RGFW_WINDOWS)
+void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) {
+ RGFW_window_showMouseFlags(win, show);
+ if (show == RGFW_FALSE)
+ RGFW_window_setMouse(win, _RGFW->hiddenMouse);
+ else
+ RGFW_window_setMouseDefault(win);
+}
+#endif
+
+#ifndef RGFW_MACOS
+void RGFW_moveToMacOSResourceDir(void) { }
+#endif
+
+RGFWDEF RGFW_bool RGFW_isLatin(const char *string, size_t length);
+RGFW_bool RGFW_isLatin(const char *string, size_t length) {
+ for (size_t i = 0; i < length; i++) {
+ if ((u8)string[i] >= 0x80) {
+ return RGFW_TRUE;
+ }
+ }
+ return RGFW_FALSE;
+}
+
+RGFWDEF u32 RGFW_decodeUTF8(const char* string, size_t* starting_index);
+u32 RGFW_decodeUTF8(const char* string, size_t* starting_index) {
+ static const u32 offsets[] = {
+ 0x00000000u, 0x00003080u, 0x000e2080u,
+ 0x03c82080u, 0xfa082080u, 0x82082080u
+ };
+
+ u32 codepoint = (u8)string[(*starting_index)];
+ size_t count;
+ for (count = 1; (string[count + (*starting_index)] & 0xc0) == 0x80; count++) {
+ codepoint = (codepoint << 6) + (u8)string[count + (*starting_index)];
+ }
+
+ *starting_index += count;
+
+ RGFW_ASSERT(count <= 6);
+ return codepoint - offsets[count - 1];
+}
+
+/*
+ graphics API specific code (end of generic code)
+ starts here
+*/
+
+
+/*
+ OpenGL defines start here (Normal, EGL, OSMesa)
+*/
+
+#if defined(RGFW_OPENGL)
+/* EGL, OpenGL */
+#define RGFW_DEFAULT_GL_HINTS { \
+ /* Stencil */ 0, \
+ /* Samples */ 0, \
+ /* Stereo */ RGFW_FALSE, \
+ /* AuxBuffers */ 0, \
+ /* DoubleBuffer */ RGFW_TRUE, \
+ /* Red */ 8, \
+ /* Green */ 8, \
+ /* Blue */ 8, \
+ /* Alpha */ 8, \
+ /* Depth */ 24, \
+ /* AccumRed */ 0, \
+ /* AccumGreen */ 0, \
+ /* AccumBlue */ 0, \
+ /* AccumAlpha */ 0, \
+ /* SRGB */ RGFW_FALSE, \
+ /* Robustness */ RGFW_FALSE, \
+ /* Debug */ RGFW_FALSE, \
+ /* NoError */ RGFW_FALSE, \
+ /* ReleaseBehavior */ RGFW_glReleaseNone, \
+ /* Profile */ RGFW_glCore, \
+ /* Major */ 1, \
+ /* Minor */ 0, \
+ /* Share */ NULL, \
+ /* Share_EGL */ NULL, \
+ /* renderer */ RGFW_glAccelerated \
+}
+
+RGFW_glHints RGFW_globalHints_OpenGL_SRC = RGFW_DEFAULT_GL_HINTS;
+RGFW_glHints* RGFW_globalHints_OpenGL = &RGFW_globalHints_OpenGL_SRC;
+
+void RGFW_resetGlobalHints_OpenGL(void) {
+#if !defined(__cplusplus) || defined(RGFW_MACOS)
+ RGFW_globalHints_OpenGL_SRC = (RGFW_glHints)RGFW_DEFAULT_GL_HINTS;
+#else
+ RGFW_globalHints_OpenGL_SRC = RGFW_DEFAULT_GL_HINTS;
+#endif
+}
+void RGFW_setGlobalHints_OpenGL(RGFW_glHints* hints) { RGFW_globalHints_OpenGL = hints; }
+RGFW_glHints* RGFW_getGlobalHints_OpenGL(void) { RGFW_init(); return RGFW_globalHints_OpenGL; }
+
+
+void* RGFW_glContext_getSourceContext(RGFW_glContext* ctx) {
+ RGFW_UNUSED(ctx);
+
+#ifdef RGFW_WAYLAND
+ if (RGFW_usingWayland()) return (void*)ctx->egl.ctx;
+#endif
+
+#if defined(RGFW_X11)
+ return (void*)ctx->ctx;
+#else
+ return NULL;
+#endif
+}
+
+RGFW_glContext* RGFW_window_createContext_OpenGL(RGFW_window* win, RGFW_glHints* hints) {
+ #ifdef RGFW_WAYLAND
+ if (RGFW_usingWayland()) {
+ return (RGFW_glContext*)RGFW_window_createContext_EGL(win, hints);
+ }
+ #endif
+ RGFW_glContext* ctx = (RGFW_glContext*)RGFW_ALLOC(sizeof(RGFW_glContext));
+ if (RGFW_window_createContextPtr_OpenGL(win, ctx, hints) == RGFW_FALSE) {
+ RGFW_FREE(ctx);
+ win->src.ctx.native = NULL;
+ return NULL;
+ }
+ win->src.gfxType |= RGFW_gfxOwnedByRGFW;
+ return ctx;
+}
+
+RGFW_glContext* RGFW_window_getContext_OpenGL(RGFW_window* win) {
+ if (win->src.gfxType & RGFW_windowEGL) return NULL;
+ return win->src.ctx.native;
+}
+
+void RGFW_window_deleteContext_OpenGL(RGFW_window* win, RGFW_glContext* ctx) {
+ RGFW_window_deleteContextPtr_OpenGL(win, ctx);
+ if (win->src.gfxType & RGFW_gfxOwnedByRGFW) RGFW_FREE(ctx);
+}
+
+RGFW_bool RGFW_extensionSupportedStr(const char* extensions, const char* ext, size_t len) {
+ const char *start = extensions;
+ const char *where;
+ const char* terminator;
+
+ if (extensions == NULL || ext == NULL) {
+ return RGFW_FALSE;
+ }
+
+ while (ext[len - 1] == '\0' && len > 3) {
+ len--;
+ }
+
+ where = RGFW_STRSTR(extensions, ext);
+ while (where) {
+ terminator = where + len;
+ if ((where == start || *(where - 1) == ' ') &&
+ (*terminator == ' ' || *terminator == '\0')) {
+ return RGFW_TRUE;
+ }
+ where = RGFW_STRSTR(terminator, ext);
+ }
+
+ return RGFW_FALSE;
+}
+
+RGFWDEF RGFW_bool RGFW_extensionSupported_base(const char* extension, size_t len);
+RGFW_bool RGFW_extensionSupported_base(const char* extension, size_t len) {
+ #ifdef GL_NUM_EXTENSIONS
+ if (RGFW_globalHints_OpenGL->major >= 3) {
+ i32 i;
+
+ GLint count = 0;
+
+ RGFW_proc RGFW_glGetStringi = RGFW_getProcAddress_OpenGL("glGetStringi");
+ RGFW_proc RGFW_glGetIntegerv = RGFW_getProcAddress_OpenGL("glGetIntegerv");
+ if (RGFW_glGetIntegerv)
+ ((void(*)(GLenum, GLint*))RGFW_glGetIntegerv)(GL_NUM_EXTENSIONS, &count);
+
+ for (i = 0; RGFW_glGetStringi && i < count; i++) {
+ const char* en = ((const char* (*)(u32, u32))RGFW_glGetStringi)(GL_EXTENSIONS, (u32)i);
+ if (en && RGFW_STRNCMP(en, extension, len) == 0) {
+ return RGFW_TRUE;
+ }
+ }
+ } else
+#endif
+ {
+ RGFW_proc RGFW_glGetString = RGFW_getProcAddress_OpenGL("glGetString");
+ #define RGFW_GL_EXTENSIONS 0x1F03
+ if (RGFW_glGetString) {
+ const char* extensions = ((const char*(*)(u32))RGFW_glGetString)(RGFW_GL_EXTENSIONS);
+
+ if ((extensions != NULL) && RGFW_extensionSupportedStr(extensions, extension, len)) {
+ return RGFW_TRUE;
+ }
+ }
+ }
+ return RGFW_FALSE;
+}
+
+RGFW_bool RGFW_extensionSupported_OpenGL(const char* extension, size_t len) {
+ if (RGFW_extensionSupported_base(extension, len)) return RGFW_TRUE;
+ return RGFW_extensionSupportedPlatform_OpenGL(extension, len);
+}
+
+void RGFW_window_makeCurrentWindow_OpenGL(RGFW_window* win) {
+ if (win) {
+ _RGFW->current = win;
+ }
+
+ RGFW_window_makeCurrentContext_OpenGL(win);
+}
+
+RGFW_window* RGFW_getCurrentWindow_OpenGL(void) { return _RGFW->current; }
+void RGFW_attribStack_init(RGFW_attribStack* stack, i32* attribs, size_t max) { stack->attribs = attribs; stack->count = 0; stack->max = max; }
+void RGFW_attribStack_pushAttrib(RGFW_attribStack* stack, i32 attrib) {
+ RGFW_ASSERT(stack->count < stack->max);
+ stack->attribs[stack->count] = attrib;
+ stack->count += 1;
+}
+void RGFW_attribStack_pushAttribs(RGFW_attribStack* stack, i32 attrib1, i32 attrib2) {
+ RGFW_attribStack_pushAttrib(stack, attrib1);
+ RGFW_attribStack_pushAttrib(stack, attrib2);
+}
+
+/* EGL */
+#ifdef RGFW_EGL
+#include
+#include
+
+PFNEGLINITIALIZEPROC RGFW_eglInitialize;
+PFNEGLGETCONFIGSPROC RGFW_eglGetConfigs;
+PFNEGLCHOOSECONFIGPROC RGFW_eglChooseConfig;
+PFNEGLCREATEWINDOWSURFACEPROC RGFW_eglCreateWindowSurface;
+PFNEGLCREATECONTEXTPROC RGFW_eglCreateContext;
+PFNEGLMAKECURRENTPROC RGFW_eglMakeCurrent;
+PFNEGLGETDISPLAYPROC RGFW_eglGetDisplay;
+PFNEGLSWAPBUFFERSPROC RGFW_eglSwapBuffers;
+PFNEGLSWAPINTERVALPROC RGFW_eglSwapInterval;
+PFNEGLBINDAPIPROC RGFW_eglBindAPI;
+PFNEGLDESTROYCONTEXTPROC RGFW_eglDestroyContext;
+PFNEGLTERMINATEPROC RGFW_eglTerminate;
+PFNEGLDESTROYSURFACEPROC RGFW_eglDestroySurface;
+PFNEGLGETCURRENTCONTEXTPROC RGFW_eglGetCurrentContext;
+PFNEGLGETPROCADDRESSPROC RGFW_eglGetProcAddress = NULL;
+PFNEGLQUERYSTRINGPROC RGFW_eglQueryString;
+PFNEGLGETCONFIGATTRIBPROC RGFW_eglGetConfigAttrib;
+
+#define EGL_SURFACE_MAJOR_VERSION_KHR 0x3098
+#define EGL_SURFACE_MINOR_VERSION_KHR 0x30fb
+
+#ifdef RGFW_WINDOWS
+ #include
+#elif defined(RGFW_MACOS) || defined(RGFW_UNIX)
+ #include
+#endif
+
+#ifdef RGFW_WAYLAND
+#include
+#endif
+
+void* RGFW_eglLibHandle = NULL;
+
+void* RGFW_getDisplay_EGL(void) { return _RGFW->EGL_display; }
+void* RGFW_eglContext_getSourceContext(RGFW_eglContext* ctx) { return ctx->ctx; }
+void* RGFW_eglContext_getSurface(RGFW_eglContext* ctx) { return ctx->surface; }
+struct wl_egl_window* RGFW_eglContext_wlEGLWindow(RGFW_eglContext* ctx) { return ctx->eglWindow; }
+
+RGFW_bool RGFW_loadEGL(void) {
+ RGFW_init();
+ if (RGFW_eglGetProcAddress != NULL) {
+ return RGFW_TRUE;
+ }
+
+#ifndef RGFW_WASM
+ #ifdef RGFW_WINDOWS
+ const char* libNames[] = { "libEGL.dll", "EGL.dll" };
+ #elif defined(RGFW_MACOS) || defined(RGFW_UNIX)
+ /* Linux and macOS */
+ const char* libNames[] = {
+ "libEGL.so.1", /* most common */
+ "libEGL.so", /* fallback */
+ "/System/Library/Frameworks/OpenGL.framework/OpenGL" /* fallback for older macOS EGL-like systems */
+ };
+ #endif
+
+ for (size_t i = 0; i < sizeof(libNames) / sizeof(libNames[0]); i++) {
+ #ifdef RGFW_WINDOWS
+ RGFW_eglLibHandle = (void*)LoadLibraryA(libNames[i]);
+ if (RGFW_eglLibHandle) {
+ RGFW_eglGetProcAddress = (PFNEGLGETPROCADDRESSPROC)(RGFW_proc)GetProcAddress((HMODULE)RGFW_eglLibHandle, "eglGetProcAddress");
+ break;
+ }
+ #elif defined(RGFW_MACOS) || defined(RGFW_UNIX)
+ RGFW_eglLibHandle = dlopen(libNames[i], RTLD_LAZY | RTLD_GLOBAL);
+ if (RGFW_eglLibHandle) {
+ void* lib = dlsym(RGFW_eglLibHandle, "eglGetProcAddress");
+ if (lib != NULL) RGFW_MEMCPY(&RGFW_eglGetProcAddress, &lib, sizeof(PFNEGLGETPROCADDRESSPROC));
+ break;
+ }
+ #endif
+ }
+
+ if (!RGFW_eglLibHandle || !RGFW_eglGetProcAddress) {
+ return RGFW_FALSE;
+ }
+
+ RGFW_eglInitialize = (PFNEGLINITIALIZEPROC) RGFW_eglGetProcAddress("eglInitialize");
+ RGFW_eglGetConfigs = (PFNEGLGETCONFIGSPROC) RGFW_eglGetProcAddress("eglGetConfigs");
+ RGFW_eglChooseConfig = (PFNEGLCHOOSECONFIGPROC) RGFW_eglGetProcAddress("eglChooseConfig");
+ RGFW_eglCreateWindowSurface = (PFNEGLCREATEWINDOWSURFACEPROC) RGFW_eglGetProcAddress("eglCreateWindowSurface");
+ RGFW_eglCreateContext = (PFNEGLCREATECONTEXTPROC) RGFW_eglGetProcAddress("eglCreateContext");
+ RGFW_eglMakeCurrent = (PFNEGLMAKECURRENTPROC) RGFW_eglGetProcAddress("eglMakeCurrent");
+ RGFW_eglGetDisplay = (PFNEGLGETDISPLAYPROC) RGFW_eglGetProcAddress("eglGetDisplay");
+ RGFW_eglSwapBuffers = (PFNEGLSWAPBUFFERSPROC) RGFW_eglGetProcAddress("eglSwapBuffers");
+ RGFW_eglSwapInterval = (PFNEGLSWAPINTERVALPROC) RGFW_eglGetProcAddress("eglSwapInterval");
+ RGFW_eglBindAPI = (PFNEGLBINDAPIPROC) RGFW_eglGetProcAddress("eglBindAPI");
+ RGFW_eglDestroyContext = (PFNEGLDESTROYCONTEXTPROC) RGFW_eglGetProcAddress("eglDestroyContext");
+ RGFW_eglTerminate = (PFNEGLTERMINATEPROC) RGFW_eglGetProcAddress("eglTerminate");
+ RGFW_eglDestroySurface = (PFNEGLDESTROYSURFACEPROC) RGFW_eglGetProcAddress("eglDestroySurface");
+ RGFW_eglQueryString = (PFNEGLQUERYSTRINGPROC) RGFW_eglGetProcAddress("eglQueryString");
+ RGFW_eglGetCurrentContext = (PFNEGLGETCURRENTCONTEXTPROC) RGFW_eglGetProcAddress("eglGetCurrentContext");
+ RGFW_eglGetConfigAttrib = (PFNEGLGETCONFIGATTRIBPROC) RGFW_eglGetProcAddress("eglGetConfigAttrib");
+
+#else
+ RGFW_eglGetProcAddress = eglGetProcAddress;
+ RGFW_eglInitialize = (PFNEGLINITIALIZEPROC) eglInitialize;
+ RGFW_eglGetConfigs = (PFNEGLGETCONFIGSPROC) eglGetConfigs;
+ RGFW_eglChooseConfig = (PFNEGLCHOOSECONFIGPROC) eglChooseConfig;
+ RGFW_eglCreateWindowSurface = (PFNEGLCREATEWINDOWSURFACEPROC) eglCreateWindowSurface;
+ RGFW_eglCreateContext = (PFNEGLCREATECONTEXTPROC) eglCreateContext;
+ RGFW_eglMakeCurrent = (PFNEGLMAKECURRENTPROC) eglMakeCurrent;
+ RGFW_eglGetDisplay = (PFNEGLGETDISPLAYPROC) eglGetDisplay;
+ RGFW_eglSwapBuffers = (PFNEGLSWAPBUFFERSPROC) eglSwapBuffers;
+ RGFW_eglSwapInterval = (PFNEGLSWAPINTERVALPROC) eglSwapInterval;
+ RGFW_eglBindAPI = (PFNEGLBINDAPIPROC) eglBindAPI;
+ RGFW_eglDestroyContext = (PFNEGLDESTROYCONTEXTPROC) eglDestroyContext;
+ RGFW_eglTerminate = (PFNEGLTERMINATEPROC) eglTerminate;
+ RGFW_eglDestroySurface = (PFNEGLDESTROYSURFACEPROC) eglDestroySurface;
+ RGFW_eglQueryString = (PFNEGLQUERYSTRINGPROC) eglQueryString;
+ RGFW_eglGetCurrentContext = (PFNEGLGETCURRENTCONTEXTPROC) eglGetCurrentContext;
+ RGFW_eglGetConfigAttrib = (PFNEGLGETCONFIGATTRIBPROC)eglGetConfigAttrib;
+#endif
+
+ RGFW_bool out = RGFW_BOOL(RGFW_eglInitialize!= NULL &&
+ RGFW_eglGetConfigs!= NULL &&
+ RGFW_eglChooseConfig!= NULL &&
+ RGFW_eglCreateWindowSurface!= NULL &&
+ RGFW_eglCreateContext!= NULL &&
+ RGFW_eglMakeCurrent!= NULL &&
+ RGFW_eglGetDisplay!= NULL &&
+ RGFW_eglSwapBuffers!= NULL &&
+ RGFW_eglSwapInterval != NULL &&
+ RGFW_eglBindAPI!= NULL &&
+ RGFW_eglDestroyContext!= NULL &&
+ RGFW_eglTerminate!= NULL &&
+ RGFW_eglDestroySurface!= NULL &&
+ RGFW_eglQueryString != NULL &&
+ RGFW_eglGetCurrentContext != NULL &&
+ RGFW_eglGetConfigAttrib != NULL);
+
+ if (out) {
+ #ifdef RGFW_WINDOWS
+ HDC dc = GetDC(NULL);
+ _RGFW->EGL_display = RGFW_eglGetDisplay((EGLNativeDisplayType) dc);
+ ReleaseDC(NULL, dc);
+ #elif defined(RGFW_WAYLAND)
+ if (_RGFW->useWaylandBool)
+ _RGFW->EGL_display = RGFW_eglGetDisplay((EGLNativeDisplayType) _RGFW->wl_display);
+ else
+ #endif
+ #ifdef RGFW_X11
+ _RGFW->EGL_display = RGFW_eglGetDisplay((EGLNativeDisplayType) _RGFW->display);
+ #else
+ {}
+ #endif
+ #if !defined(RGFW_WAYLAND) && !defined(RGFW_WINDOWS) && !defined(RGFW_X11)
+ _RGFW->EGL_display = RGFW_eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ #endif
+ }
+
+ RGFW_eglInitialize(_RGFW->EGL_display, NULL, NULL);
+ return out;
+}
+
+
+void RGFW_unloadEGL(void) {
+ if (!RGFW_eglLibHandle) return;
+ RGFW_eglTerminate(_RGFW->EGL_display);
+ #ifdef RGFW_WINDOWS
+ FreeLibrary((HMODULE)RGFW_eglLibHandle);
+ #elif defined(RGFW_MACOS) || defined(RGFW_UNIX)
+ dlclose(RGFW_eglLibHandle);
+ #endif
+
+ RGFW_eglLibHandle = NULL;
+ RGFW_eglGetProcAddress = NULL;
+}
+
+RGFW_bool RGFW_window_createContextPtr_EGL(RGFW_window* win, RGFW_eglContext* ctx, RGFW_glHints* hints) {
+ if (RGFW_loadEGL() == RGFW_FALSE) return RGFW_FALSE;
+ win->src.ctx.egl = ctx;
+ win->src.gfxType = RGFW_gfxEGL;
+
+#ifdef RGFW_WAYLAND
+ if (_RGFW->useWaylandBool)
+ win->src.ctx.egl->eglWindow = wl_egl_window_create(win->src.surface, win->w, win->h);
+#endif
+
+ #ifndef EGL_OPENGL_ES1_BIT
+ #define EGL_OPENGL_ES1_BIT 0x1
+ #endif
+
+ EGLint egl_config[24];
+
+ {
+ RGFW_attribStack stack;
+ RGFW_attribStack_init(&stack, egl_config, 24);
+
+ RGFW_attribStack_pushAttribs(&stack, EGL_SURFACE_TYPE, EGL_WINDOW_BIT);
+ RGFW_attribStack_pushAttrib(&stack, EGL_RENDERABLE_TYPE);
+
+ if (hints->profile == RGFW_glES) {
+ switch (hints->major) {
+ case 1: RGFW_attribStack_pushAttrib(&stack, EGL_OPENGL_ES1_BIT); break;
+ case 2: RGFW_attribStack_pushAttrib(&stack, EGL_OPENGL_ES2_BIT); break;
+ case 3: RGFW_attribStack_pushAttrib(&stack, EGL_OPENGL_ES3_BIT); break;
+ default: break;
+ }
+ } else {
+ RGFW_attribStack_pushAttrib(&stack, EGL_OPENGL_BIT);
+ }
+
+ RGFW_attribStack_pushAttribs(&stack, EGL_RED_SIZE, hints->red);
+ RGFW_attribStack_pushAttribs(&stack, EGL_GREEN_SIZE, hints->green);
+ RGFW_attribStack_pushAttribs(&stack, EGL_BLUE_SIZE, hints->blue);
+ RGFW_attribStack_pushAttribs(&stack, EGL_ALPHA_SIZE, hints->alpha);
+ RGFW_attribStack_pushAttribs(&stack, EGL_DEPTH_SIZE, hints->depth);
+
+ RGFW_attribStack_pushAttribs(&stack, EGL_STENCIL_SIZE, hints->stencil);
+ if (hints->samples) {
+ RGFW_attribStack_pushAttribs(&stack, EGL_SAMPLE_BUFFERS, 1);
+ RGFW_attribStack_pushAttribs(&stack, EGL_SAMPLES, hints->samples);
+ }
+
+ RGFW_attribStack_pushAttribs(&stack, EGL_NONE, EGL_NONE);
+ }
+
+ EGLint numConfigs, best_config = -1, best_samples = 0;
+
+ RGFW_eglChooseConfig(_RGFW->EGL_display, egl_config, NULL, 0, &numConfigs);
+ EGLConfig* configs = (EGLConfig*)RGFW_ALLOC(sizeof(EGLConfig) * (u32)numConfigs);
+
+ RGFW_eglChooseConfig(_RGFW->EGL_display, egl_config, configs, numConfigs, &numConfigs);
+
+#ifdef RGFW_X11
+ RGFW_bool transparent = (win->internal.flags & RGFW_windowTransparent);
+ EGLint best_depth = 0;
+#endif
+
+ for (EGLint i = 0; i < numConfigs; i++) {
+ EGLint visual_id = 0;
+ EGLint samples = 0;
+
+ RGFW_eglGetConfigAttrib(_RGFW->EGL_display, configs[i], EGL_NATIVE_VISUAL_ID, &visual_id);
+ RGFW_eglGetConfigAttrib(_RGFW->EGL_display, configs[i], EGL_SAMPLES, &samples);
+
+ if (best_config == -1) best_config = i;
+
+#ifdef RGFW_X11
+ if (_RGFW->useWaylandBool == RGFW_FALSE) {
+ XVisualInfo vinfo_template;
+ vinfo_template.visualid = (VisualID)visual_id;
+
+ int num_visuals = 0;
+ XVisualInfo* vi = XGetVisualInfo(_RGFW->display, VisualIDMask, &vinfo_template, &num_visuals);
+ if (!vi) continue;
+ if ((!transparent || vi->depth == 32) && best_depth == 0) {
+ best_config = i;
+ best_depth = vi->depth;
+ }
+
+ if ((!(transparent) || vi->depth == 32) && (samples <= hints->samples && samples > best_samples)) {
+ best_depth = vi->depth;
+ best_config = i;
+ best_samples = samples;
+ XFree(vi);
+ continue;
+ }
+ }
+#endif
+
+ if (samples <= hints->samples && samples > best_samples) {
+ best_config = i;
+ best_samples = samples;
+ }
+ }
+
+ EGLConfig config = configs[best_config];
+ RGFW_FREE(configs);
+#ifdef RGFW_X11
+ if (_RGFW->useWaylandBool == RGFW_FALSE) {
+ /* This is required so that way the user can create their own OpenGL context after RGFW_createWindow is used */
+ XVisualInfo* result;
+ XVisualInfo desired;
+ EGLint visualID = 0, count = 0;
+
+ RGFW_eglGetConfigAttrib(_RGFW->EGL_display, config, EGL_NATIVE_VISUAL_ID, &visualID);
+ if (visualID) {
+ desired.visualid = (VisualID)visualID;
+ result = XGetVisualInfo(_RGFW->display, VisualIDMask, &desired, &count);
+ } else RGFW_sendDebugInfo(RGFW_typeError, RGFW_errEGLContext, "Failed to fetch a valid EGL VisualID");
+
+ if (result == NULL || count == 0) {
+ if (win->src.window == 0) {
+ /* try to create a EGL context anyway (this will work if you're not using a NVidia driver) */
+ win->internal.flags &= ~(u32)RGFW_windowEGL;
+ RGFW_createWindowPlatform("", win->internal.flags, win);
+ }
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errEGLContext, "Failed to find a valid visual for the EGL config");
+ } else {
+ RGFW_bool showWindow = RGFW_FALSE;
+ if (win->src.window) {
+ showWindow = (RGFW_window_isMinimized(win) == RGFW_FALSE);
+ RGFW_window_closePlatform(win);
+ }
+
+ RGFW_XCreateWindow(*result, "", win->internal.flags, win);
+
+ if (showWindow) {
+ RGFW_window_show(win);
+ }
+ XFree(result);
+ }
+ }
+#endif
+
+ EGLint surf_attribs[9];
+
+ {
+ RGFW_attribStack stack;
+ RGFW_attribStack_init(&stack, surf_attribs, 9);
+
+ const char present_opaque_str[] = "EGL_EXT_present_opaque";
+ RGFW_bool opaque_extension_Found = RGFW_extensionSupportedPlatform_EGL(present_opaque_str, sizeof(present_opaque_str));
+
+ #ifndef EGL_PRESENT_OPAQUE_EXT
+ #define EGL_PRESENT_OPAQUE_EXT 0x31df
+ #endif
+
+ #ifndef EGL_GL_COLORSPACE_KHR
+ #define EGL_GL_COLORSPACE_KHR 0x309D
+ #ifndef EGL_GL_COLORSPACE_SRGB_KHR
+ #define EGL_GL_COLORSPACE_SRGB_KHR 0x3089
+ #endif
+ #endif
+
+ const char gl_colorspace_str[] = "EGL_KHR_gl_colorspace";
+ RGFW_bool gl_colorspace_Found = RGFW_extensionSupportedPlatform_EGL(gl_colorspace_str, sizeof(gl_colorspace_str));
+
+ if (hints->sRGB && gl_colorspace_Found) {
+ RGFW_attribStack_pushAttribs(&stack, EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR);
+ }
+
+ if (!(win->internal.flags & RGFW_windowTransparent) && opaque_extension_Found)
+ RGFW_attribStack_pushAttribs(&stack, EGL_PRESENT_OPAQUE_EXT, EGL_TRUE);
+
+ if (hints->doubleBuffer == 0) {
+ RGFW_attribStack_pushAttribs(&stack, EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER);
+ }
+
+ RGFW_attribStack_pushAttribs(&stack, EGL_NONE, EGL_NONE);
+ }
+ #if defined(RGFW_MACOS)
+ void* layer = RGFW_getLayer_OSX();
+
+ RGFW_window_setLayer_OSX(win, layer);
+
+ win->src.ctx.egl->surface = RGFW_eglCreateWindowSurface(_RGFW->EGL_display, config, (EGLNativeWindowType) layer, surf_attribs);
+ #elif defined(RGFW_WINDOWS)
+ win->src.ctx.egl->surface = RGFW_eglCreateWindowSurface(_RGFW->EGL_display, config, (EGLNativeWindowType) win->src.window, surf_attribs);
+ #elif defined(RGFW_WAYLAND)
+ if (_RGFW->useWaylandBool)
+ win->src.ctx.egl->surface = RGFW_eglCreateWindowSurface(_RGFW->EGL_display, config, (EGLNativeWindowType) win->src.ctx.egl->eglWindow, surf_attribs);
+ else
+ #endif
+ #ifdef RGFW_X11
+ win->src.ctx.egl->surface = RGFW_eglCreateWindowSurface(_RGFW->EGL_display, config, (EGLNativeWindowType) win->src.window, surf_attribs);
+ #else
+ {}
+ #endif
+ #ifdef RGFW_WASM
+ win->src.ctx.egl->surface = eglCreateWindowSurface(_RGFW->EGL_display, config, 0, 0);
+ #endif
+
+ if (win->src.ctx.egl->surface == NULL) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errEGLContext, "Failed to create an EGL surface.");
+ return RGFW_FALSE;
+ }
+
+ EGLint attribs[20];
+ {
+ RGFW_attribStack stack;
+ RGFW_attribStack_init(&stack, attribs, 20);
+
+ if (hints->major || hints->minor) {
+ RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_MAJOR_VERSION, hints->major);
+ RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_MINOR_VERSION, hints->minor);
+ }
+
+ if (hints->profile == RGFW_glCore) {
+ RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT);
+ } else if (hints->profile == RGFW_glCompatibility) {
+ RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT);
+ } else if (hints->profile == RGFW_glForwardCompatibility) {
+ RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE, EGL_TRUE);
+ }
+
+
+ RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_OPENGL_ROBUST_ACCESS, hints->robustness);
+ RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_OPENGL_DEBUG, hints->debug);
+
+ #ifndef EGL_CONTEXT_RELEASE_BEHAVIOR_KHR
+ #define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097
+ #endif
+
+ #ifndef EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR
+ #define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098
+ #endif
+
+ if (hints->releaseBehavior == RGFW_glReleaseFlush) {
+ RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR);
+ } else {
+ RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, 0x0000);
+ }
+
+ RGFW_attribStack_pushAttribs(&stack, EGL_NONE, EGL_NONE);
+ }
+
+ if (hints->profile == RGFW_glES)
+ RGFW_eglBindAPI(EGL_OPENGL_ES_API);
+ else
+ RGFW_eglBindAPI(EGL_OPENGL_API);
+
+ win->src.ctx.egl->ctx = RGFW_eglCreateContext(_RGFW->EGL_display, config, hints->shareEGL, attribs);
+
+ if (win->src.ctx.egl->ctx == NULL) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errEGLContext, "Failed to create an EGL context.");
+ return RGFW_FALSE;
+ }
+
+ RGFW_eglMakeCurrent(_RGFW->EGL_display, win->src.ctx.egl->surface, win->src.ctx.egl->surface, win->src.ctx.egl->ctx);
+ RGFW_eglSwapBuffers(_RGFW->EGL_display, win->src.ctx.egl->surface);
+ RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "EGL context initalized.");
+ return RGFW_TRUE;
+}
+
+RGFW_eglContext* RGFW_window_getContext_EGL(RGFW_window* win) {
+ if (win->src.gfxType == RGFW_windowOpenGL) return NULL;
+ return win->src.ctx.egl;
+}
+
+void RGFW_window_deleteContextPtr_EGL(RGFW_window* win, RGFW_eglContext* ctx) {
+ if (_RGFW->EGL_display == NULL) return;
+
+ RGFW_eglDestroySurface(_RGFW->EGL_display, ctx->surface);
+ RGFW_eglDestroyContext(_RGFW->EGL_display, ctx->ctx);
+ RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "EGL context freed");
+ #ifdef RGFW_WAYLAND
+ if (_RGFW->useWaylandBool == RGFW_FALSE) return;
+ wl_egl_window_destroy(win->src.ctx.egl->eglWindow);
+ RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "EGL window context freed");
+ #endif
+ win->src.ctx.egl = NULL;
+}
+
+void RGFW_window_makeCurrentContext_EGL(RGFW_window* win) { if (win) RGFW_ASSERT(win->src.ctx.egl);
+ if (win == NULL)
+ RGFW_eglMakeCurrent(_RGFW->EGL_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ else {
+ RGFW_eglMakeCurrent(_RGFW->EGL_display, win->src.ctx.egl->surface, win->src.ctx.egl->surface, win->src.ctx.egl->ctx);
+ }
+}
+
+void RGFW_window_swapBuffers_EGL(RGFW_window* win) {
+ if (RGFW_eglSwapBuffers)
+ RGFW_eglSwapBuffers(_RGFW->EGL_display, win->src.ctx.egl->surface);
+ else RGFW_window_swapBuffers_OpenGL(win);
+}
+
+void* RGFW_getCurrentContext_EGL(void) {
+ return RGFW_eglGetCurrentContext();
+}
+
+RGFW_proc RGFW_getProcAddress_EGL(const char* procname) {
+ #if defined(RGFW_WINDOWS)
+ RGFW_proc proc = (RGFW_proc) GetProcAddress(RGFW_wgl_dll, procname);
+
+ if (proc)
+ return proc;
+ #endif
+
+ return (RGFW_proc) RGFW_eglGetProcAddress(procname);
+}
+
+RGFW_bool RGFW_extensionSupportedPlatform_EGL(const char* extension, size_t len) {
+ if (RGFW_loadEGL() == RGFW_FALSE) return RGFW_FALSE;
+ const char* extensions = RGFW_eglQueryString(_RGFW->EGL_display, EGL_EXTENSIONS);
+ return extensions != NULL && RGFW_extensionSupportedStr(extensions, extension, len);
+}
+
+void RGFW_window_swapInterval_EGL(RGFW_window* win, i32 swapInterval) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_eglSwapInterval(_RGFW->EGL_display, swapInterval);
+}
+
+RGFW_bool RGFW_extensionSupported_EGL(const char* extension, size_t len) {
+ if (RGFW_extensionSupported_base(extension, len)) return RGFW_TRUE;
+ return RGFW_extensionSupportedPlatform_EGL(extension, len);
+}
+
+void RGFW_window_makeCurrentWindow_EGL(RGFW_window* win) {
+ _RGFW->current = win;
+ RGFW_window_makeCurrentContext_EGL(win);
+}
+
+RGFW_window* RGFW_getCurrentWindow_EGL(void) { return _RGFW->current; }
+
+RGFW_eglContext* RGFW_window_createContext_EGL(RGFW_window* win, RGFW_glHints* hints) {
+ RGFW_eglContext* ctx = (RGFW_eglContext*)RGFW_ALLOC(sizeof(RGFW_eglContext));
+ if (RGFW_window_createContextPtr_EGL(win, ctx, hints) == RGFW_FALSE) {
+ RGFW_FREE(ctx);
+ win->src.ctx.egl = NULL;
+ return NULL;
+ }
+ win->src.gfxType |= RGFW_gfxOwnedByRGFW;
+ return ctx;
+}
+
+void RGFW_window_deleteContext_EGL(RGFW_window* win, RGFW_eglContext* ctx) {
+ RGFW_window_deleteContextPtr_EGL(win, ctx);
+ if (win->src.gfxType & RGFW_gfxOwnedByRGFW) RGFW_FREE(ctx);
+}
+
+#endif /* RGFW_EGL */
+
+/*
+ end of RGFW_EGL defines
+*/
+#endif /* end of RGFW_GL (OpenGL, EGL, OSMesa )*/
+
+/*
+ RGFW_VULKAN defines
+*/
+#ifdef RGFW_VULKAN
+#ifdef RGFW_MACOS
+#include
+#endif
+
+const char** RGFW_getRequiredInstanceExtensions_Vulkan(size_t* count) {
+ static const char* arr[2] = {VK_KHR_SURFACE_EXTENSION_NAME};
+ arr[1] = RGFW_VK_SURFACE;
+ if (count != NULL) *count = 2;
+
+ return (const char**)arr;
+}
+
+#ifndef RGFW_MACOS
+VkResult RGFW_window_createSurface_Vulkan(RGFW_window* win, VkInstance instance, VkSurfaceKHR* surface) {
+ RGFW_ASSERT(win != NULL); RGFW_ASSERT(instance);
+ RGFW_ASSERT(surface != NULL);
+
+ *surface = VK_NULL_HANDLE;
+
+#ifdef RGFW_X11
+
+ VkXlibSurfaceCreateInfoKHR x11 = { VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, 0, 0, (Display*) _RGFW->display, (Window) win->src.window };
+ return vkCreateXlibSurfaceKHR(instance, &x11, NULL, surface);
+#endif
+#if defined(RGFW_WAYLAND)
+
+ VkWaylandSurfaceCreateInfoKHR wayland = { VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, 0, 0, (struct wl_display*) _RGFW->wl_display, (struct wl_surface*) win->src.surface };
+ return vkCreateWaylandSurfaceKHR(instance, &wayland, NULL, surface);
+#elif defined(RGFW_WINDOWS)
+ VkWin32SurfaceCreateInfoKHR win32 = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, 0, 0, GetModuleHandle(NULL), (HWND)win->src.window };
+
+ return vkCreateWin32SurfaceKHR(instance, &win32, NULL, surface);
+#endif
+}
+#endif
+
+RGFW_bool RGFW_getPresentationSupport_Vulkan(VkPhysicalDevice physicalDevice, u32 queueFamilyIndex) {
+ if (_RGFW == NULL) RGFW_init();
+#ifdef RGFW_X11
+
+ Visual* visual = DefaultVisual(_RGFW->display, DefaultScreen(_RGFW->display));
+ RGFW_bool out = vkGetPhysicalDeviceXlibPresentationSupportKHR(physicalDevice, queueFamilyIndex, _RGFW->display, XVisualIDFromVisual(visual));
+ return out;
+#endif
+#if defined(RGFW_WAYLAND)
+
+ RGFW_bool wlout = vkGetPhysicalDeviceWaylandPresentationSupportKHR(physicalDevice, queueFamilyIndex, _RGFW->wl_display);
+ return wlout;
+#elif defined(RGFW_WINDOWS)
+ RGFW_bool out = vkGetPhysicalDeviceWin32PresentationSupportKHR(physicalDevice, queueFamilyIndex);
+ return out;
+#elif defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11)
+ RGFW_UNUSED(physicalDevice);
+ RGFW_UNUSED(queueFamilyIndex);
+ return RGFW_FALSE; /* TODO */
+#endif
+}
+#endif /* end of RGFW_vulkan */
+
+/*
+This is where OS specific stuff starts
+*/
+
+/* start of unix (wayland or X11 (unix) ) defines */
+
+#ifdef RGFW_UNIX
+#include
+#include
+#include
+
+void RGFW_stopCheckEvents(void) {
+
+ _RGFW->eventWait_forceStop[2] = 1;
+ while (1) {
+ const char byte = 0;
+ const ssize_t result = write(_RGFW->eventWait_forceStop[1], &byte, 1);
+ if (result == 1 || result == -1)
+ break;
+ }
+}
+
+RGFWDEF u64 RGFW_linux_getTimeNS(void);
+u64 RGFW_linux_getTimeNS(void) {
+ struct timespec ts;
+ const u64 scale_factor = 1000000000;
+ clock_gettime(_RGFW->clock, &ts);
+ return (u64)ts.tv_sec * scale_factor + (u64)ts.tv_nsec;
+}
+
+void RGFW_waitForEvent(i32 waitMS) {
+ if (waitMS == 0) return;
+
+ if (_RGFW->eventWait_forceStop[0] == 0 || _RGFW->eventWait_forceStop[1] == 0) {
+ if (pipe(_RGFW->eventWait_forceStop) != -1) {
+ fcntl(_RGFW->eventWait_forceStop[0], F_GETFL, 0);
+ fcntl(_RGFW->eventWait_forceStop[0], F_GETFD, 0);
+ fcntl(_RGFW->eventWait_forceStop[1], F_GETFL, 0);
+ fcntl(_RGFW->eventWait_forceStop[1], F_GETFD, 0);
+ }
+ }
+
+ struct pollfd fds[2];
+ fds[0].fd = 0;
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+ fds[1].fd = _RGFW->eventWait_forceStop[0];
+ fds[1].events = POLLIN;
+ fds[1].revents = 0;
+
+
+ if (RGFW_usingWayland()) {
+ #ifdef RGFW_WAYLAND
+ fds[0].fd = wl_display_get_fd(_RGFW->wl_display);
+
+ /* empty the queue */
+ while (wl_display_prepare_read(_RGFW->wl_display) != 0) {
+ /* error occured when dispatching the queue */
+ if (wl_display_dispatch_pending(_RGFW->wl_display) == -1) {
+ return;
+ }
+ }
+
+ /* send any pending requests to the compositor */
+ while (wl_display_flush(_RGFW->wl_display) == -1) {
+
+ /* queue is full dispatch them */
+ if (errno == EAGAIN) {
+ if (wl_display_dispatch_pending(_RGFW->wl_display) == -1) {
+ return;
+ }
+ } else {
+ return;
+ }
+ }
+ #endif
+ } else {
+ #ifdef RGFW_X11
+ fds[0].fd = ConnectionNumber(_RGFW->display);
+ #endif
+ }
+
+
+ u64 start = RGFW_linux_getTimeNS();
+ if (RGFW_usingWayland()) {
+ #ifdef RGFW_WAYLAND
+ while (wl_display_dispatch_pending(_RGFW->wl_display) == 0) {
+ if (poll(fds, 1, waitMS) <= 0) {
+ wl_display_cancel_read(_RGFW->wl_display);
+ break;
+ } else {
+ if (wl_display_read_events(_RGFW->wl_display) == -1)
+ return;
+ }
+
+ if (waitMS != RGFW_eventWaitNext) {
+ waitMS -= (i32)(RGFW_linux_getTimeNS() - start) / (i32)1e+6;
+ }
+ }
+
+ /* queue contains events from read, dispatch them */
+ if (wl_display_dispatch_pending(_RGFW->wl_display) == -1) {
+ return;
+ }
+ #endif
+ } else {
+ #ifdef RGFW_X11
+ while (XPending(_RGFW->display) == 0) {
+ if (poll(fds, 1, waitMS) <= 0)
+ break;
+
+ if (waitMS != RGFW_eventWaitNext) {
+ waitMS -= (i32)(RGFW_linux_getTimeNS() - start) / (i32)1e+6;
+ }
+ }
+ #endif
+ }
+
+ /* drain any data in the stop request */
+ if (_RGFW->eventWait_forceStop[2]) {
+ char data[64];
+ RGFW_MEMSET(data, 0, sizeof(data));
+ (void)!read(_RGFW->eventWait_forceStop[0], data, sizeof(data));
+
+ _RGFW->eventWait_forceStop[2] = 0;
+ }
+}
+
+char* RGFW_strtok(char* str, const char* delimStr);
+char* RGFW_strtok(char* str, const char* delimStr) {
+ static char* static_str = NULL;
+
+ if (str != NULL)
+ static_str = str;
+
+ if (static_str == NULL) {
+ return NULL;
+ }
+
+ while (*static_str != '\0') {
+ RGFW_bool delim = 0;
+ const char* d;
+ for (d = delimStr; *d != '\0'; d++) {
+ if (*static_str == *d) {
+ delim = 1;
+ break;
+ }
+ }
+ if (!delim)
+ break;
+ static_str++;
+ }
+
+ if (*static_str == '\0')
+ return NULL;
+
+ char* token_start = static_str;
+ while (*static_str != '\0') {
+ int delim = 0;
+ const char* d;
+ for (d = delimStr; *d != '\0'; d++) {
+ if (*static_str == *d) {
+ delim = 1;
+ break;
+ }
+ }
+
+ if (delim) {
+ *static_str = '\0';
+ static_str++;
+ break;
+ }
+ static_str++;
+ }
+
+ return token_start;
+}
+
+#ifdef RGFW_X11
+RGFWDEF i32 RGFW_initPlatform_X11(void);
+RGFWDEF void RGFW_deinitPlatform_X11(void);
+#endif
+#ifdef RGFW_WAYLAND
+RGFWDEF i32 RGFW_initPlatform_Wayland(void);
+RGFWDEF void RGFW_deinitPlatform_Wayland(void);
+#endif
+
+RGFWDEF void RGFW_load_X11(void);
+RGFWDEF void RGFW_load_Wayland(void);
+
+#if !defined(RGFW_X11) || !defined(RGFW_WAYLAND)
+void RGFW_load_X11(void) { }
+void RGFW_load_Wayland(void) { }
+#endif
+
+/*
+ * Sadly we have to use magic linux keycodes
+ * We can't use X11 functions, because that breaks Wayland, but they use the same keycodes so there's no use redeffing them
+ * We can't use linux enums, because the headers don't exist on BSD
+ */
+void RGFW_initKeycodesPlatform(void) {
+ _RGFW->keycodes[49] = RGFW_backtick;
+ _RGFW->keycodes[19] = RGFW_0;
+ _RGFW->keycodes[10] = RGFW_1;
+ _RGFW->keycodes[11] = RGFW_2;
+ _RGFW->keycodes[12] = RGFW_3;
+ _RGFW->keycodes[13] = RGFW_4;
+ _RGFW->keycodes[14] = RGFW_5;
+ _RGFW->keycodes[15] = RGFW_6;
+ _RGFW->keycodes[16] = RGFW_7;
+ _RGFW->keycodes[17] = RGFW_8;
+ _RGFW->keycodes[18] = RGFW_9;
+ _RGFW->keycodes[65] = RGFW_space;
+ _RGFW->keycodes[38] = RGFW_a;
+ _RGFW->keycodes[56] = RGFW_b;
+ _RGFW->keycodes[54] = RGFW_c;
+ _RGFW->keycodes[40] = RGFW_d;
+ _RGFW->keycodes[26] = RGFW_e;
+ _RGFW->keycodes[41] = RGFW_f;
+ _RGFW->keycodes[42] = RGFW_g;
+ _RGFW->keycodes[43] = RGFW_h;
+ _RGFW->keycodes[31] = RGFW_i;
+ _RGFW->keycodes[44] = RGFW_j;
+ _RGFW->keycodes[45] = RGFW_k;
+ _RGFW->keycodes[46] = RGFW_l;
+ _RGFW->keycodes[58] = RGFW_m;
+ _RGFW->keycodes[57] = RGFW_n;
+ _RGFW->keycodes[32] = RGFW_o;
+ _RGFW->keycodes[33] = RGFW_p;
+ _RGFW->keycodes[24] = RGFW_q;
+ _RGFW->keycodes[27] = RGFW_r;
+ _RGFW->keycodes[39] = RGFW_s;
+ _RGFW->keycodes[28] = RGFW_t;
+ _RGFW->keycodes[30] = RGFW_u;
+ _RGFW->keycodes[55] = RGFW_v;
+ _RGFW->keycodes[25] = RGFW_w;
+ _RGFW->keycodes[53] = RGFW_x;
+ _RGFW->keycodes[29] = RGFW_y;
+ _RGFW->keycodes[52] = RGFW_z;
+ _RGFW->keycodes[60] = RGFW_period;
+ _RGFW->keycodes[59] = RGFW_comma;
+ _RGFW->keycodes[61] = RGFW_slash;
+ _RGFW->keycodes[34] = RGFW_bracket;
+ _RGFW->keycodes[35] = RGFW_closeBracket;
+ _RGFW->keycodes[47] = RGFW_semicolon;
+ _RGFW->keycodes[48] = RGFW_apostrophe;
+ _RGFW->keycodes[51] = RGFW_backSlash;
+ _RGFW->keycodes[36] = RGFW_return;
+ _RGFW->keycodes[119] = RGFW_delete;
+ _RGFW->keycodes[77] = RGFW_numLock;
+ _RGFW->keycodes[106] = RGFW_kpSlash;
+ _RGFW->keycodes[63] = RGFW_kpMultiply;
+ _RGFW->keycodes[86] = RGFW_kpPlus;
+ _RGFW->keycodes[82] = RGFW_kpMinus;
+ _RGFW->keycodes[87] = RGFW_kp1;
+ _RGFW->keycodes[88] = RGFW_kp2;
+ _RGFW->keycodes[89] = RGFW_kp3;
+ _RGFW->keycodes[83] = RGFW_kp4;
+ _RGFW->keycodes[84] = RGFW_kp5;
+ _RGFW->keycodes[85] = RGFW_kp6;
+ _RGFW->keycodes[81] = RGFW_kp9;
+ _RGFW->keycodes[90] = RGFW_kp0;
+ _RGFW->keycodes[91] = RGFW_kpPeriod;
+ _RGFW->keycodes[104] = RGFW_kpReturn;
+ _RGFW->keycodes[20] = RGFW_minus;
+ _RGFW->keycodes[21] = RGFW_equals;
+ _RGFW->keycodes[22] = RGFW_backSpace;
+ _RGFW->keycodes[23] = RGFW_tab;
+ _RGFW->keycodes[66] = RGFW_capsLock;
+ _RGFW->keycodes[50] = RGFW_shiftL;
+ _RGFW->keycodes[37] = RGFW_controlL;
+ _RGFW->keycodes[64] = RGFW_altL;
+ _RGFW->keycodes[133] = RGFW_superL;
+ _RGFW->keycodes[105] = RGFW_controlR;
+ _RGFW->keycodes[134] = RGFW_superR;
+ _RGFW->keycodes[62] = RGFW_shiftR;
+ _RGFW->keycodes[108] = RGFW_altR;
+ _RGFW->keycodes[67] = RGFW_F1;
+ _RGFW->keycodes[68] = RGFW_F2;
+ _RGFW->keycodes[69] = RGFW_F3;
+ _RGFW->keycodes[70] = RGFW_F4;
+ _RGFW->keycodes[71] = RGFW_F5;
+ _RGFW->keycodes[72] = RGFW_F6;
+ _RGFW->keycodes[73] = RGFW_F7;
+ _RGFW->keycodes[74] = RGFW_F8;
+ _RGFW->keycodes[75] = RGFW_F9;
+ _RGFW->keycodes[76] = RGFW_F10;
+ _RGFW->keycodes[95] = RGFW_F11;
+ _RGFW->keycodes[96] = RGFW_F12;
+ _RGFW->keycodes[111] = RGFW_up;
+ _RGFW->keycodes[116] = RGFW_down;
+ _RGFW->keycodes[113] = RGFW_left;
+ _RGFW->keycodes[114] = RGFW_right;
+ _RGFW->keycodes[118] = RGFW_insert;
+ _RGFW->keycodes[115] = RGFW_end;
+ _RGFW->keycodes[112] = RGFW_pageUp;
+ _RGFW->keycodes[117] = RGFW_pageDown;
+ _RGFW->keycodes[9] = RGFW_escape;
+ _RGFW->keycodes[110] = RGFW_home;
+ _RGFW->keycodes[78] = RGFW_scrollLock;
+ _RGFW->keycodes[107] = RGFW_printScreen;
+ _RGFW->keycodes[128] = RGFW_pause;
+ _RGFW->keycodes[191] = RGFW_F13;
+ _RGFW->keycodes[192] = RGFW_F14;
+ _RGFW->keycodes[193] = RGFW_F15;
+ _RGFW->keycodes[194] = RGFW_F16;
+ _RGFW->keycodes[195] = RGFW_F17;
+ _RGFW->keycodes[196] = RGFW_F18;
+ _RGFW->keycodes[197] = RGFW_F19;
+ _RGFW->keycodes[198] = RGFW_F20;
+ _RGFW->keycodes[199] = RGFW_F21;
+ _RGFW->keycodes[200] = RGFW_F22;
+ _RGFW->keycodes[201] = RGFW_F23;
+ _RGFW->keycodes[202] = RGFW_F24;
+ _RGFW->keycodes[203] = RGFW_F25;
+ _RGFW->keycodes[142] = RGFW_kpEqual;
+ _RGFW->keycodes[161] = RGFW_world1; /* non-US key #1 */
+ _RGFW->keycodes[162] = RGFW_world2; /* non-US key #2 */
+}
+
+i32 RGFW_initPlatform(void) {
+ #if defined(_POSIX_MONOTONIC_CLOCK)
+ struct timespec ts;
+ RGFW_MEMSET(&ts, 0, sizeof(struct timespec));
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
+ _RGFW->clock = CLOCK_MONOTONIC;
+ #else
+ _RGFW->clock = CLOCK_REALTIME;
+ #endif
+
+#ifdef RGFW_WAYLAND
+ RGFW_load_Wayland();
+ i32 ret = RGFW_initPlatform_Wayland();
+ if (ret == 0) {
+ return 0;
+ } else {
+ #ifdef RGFW_X11
+ RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningWayland, "Falling back to X11");
+ RGFW_useWayland(0);
+ #else
+ return ret;
+ #endif
+ }
+#endif
+#ifdef RGFW_X11
+ RGFW_load_X11();
+ return RGFW_initPlatform_X11();
+#else
+ return 0;
+#endif
+}
+
+
+void RGFW_deinitPlatform(void) {
+ if (_RGFW->eventWait_forceStop[0] || _RGFW->eventWait_forceStop[1]){
+ close(_RGFW->eventWait_forceStop[0]);
+ close(_RGFW->eventWait_forceStop[1]);
+ }
+#ifdef RGFW_WAYLAND
+ if (_RGFW->useWaylandBool) {
+ RGFW_deinitPlatform_Wayland();
+ return;
+ }
+#endif
+#ifdef RGFW_X11
+ RGFW_deinitPlatform_X11();
+#endif
+}
+
+RGFWDEF size_t RGFW_unix_stringlen(const char* name);
+size_t RGFW_unix_stringlen(const char* name) {
+ size_t i = 0;
+ while (name[i]) { i++; }
+ return i;
+}
+
+#endif /* end of wayland or X11 defines */
+
+
+/*
+
+
+Start of Linux / Unix defines
+
+
+*/
+
+#ifdef RGFW_X11
+#ifdef RGFW_WAYLAND
+#define RGFW_FUNC(func) func##_X11
+#else
+#define RGFW_FUNC(func) func
+#endif
+
+#include
+#include
+
+#include /* for data limits (mainly used in drag and drop functions) */
+#include
+
+void RGFW_setXInstName(const char* name) { _RGFW->instName = name; }
+#if !defined(RGFW_NO_X11_CURSOR) && defined(RGFW_X11)
+ #include
+#endif
+
+#include
+#include
+#include
+
+#include /* for converting keycode to string */
+#include /* for hiding */
+#include
+#include
+#include
+
+#ifdef RGFW_OPENGL
+ #ifndef __gl_h_
+ #define __gl_h_
+ #define RGFW_gl_ndef
+ #define GLubyte unsigned char
+ #define GLenum unsigned int
+ #define GLint int
+ #define GLuint unsigned int
+ #define GLsizei int
+ #define GLfloat float
+ #define GLvoid void
+ #define GLbitfield unsigned int
+ #define GLintptr ptrdiff_t
+ #define GLsizeiptr ptrdiff_t
+ #define GLboolean unsigned char
+ #endif
+
+ #include /* GLX defs, xlib.h, gl.h */
+ #ifndef GLX_MESA_swap_control
+ #define GLX_MESA_swap_control
+ #endif
+
+ #ifdef RGFW_gl_ndef
+ #undef __gl_h_
+ #undef GLubyte
+ #undef GLenum
+ #undef GLint
+ #undef GLuint
+ #undef GLsizei
+ #undef GLfloat
+ #undef GLvoid
+ #undef GLbitfield
+ #undef GLintptr
+ #undef GLsizeiptr
+ #undef GLboolean
+ #endif
+ typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
+#endif
+
+/* atoms needed for drag and drop */
+#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD)
+ typedef XcursorImage* (*PFN_XcursorImageCreate)(int, int);
+ typedef void (*PFN_XcursorImageDestroy)(XcursorImage*);
+ typedef Cursor(*PFN_XcursorImageLoadCursor)(Display*, const XcursorImage*);
+#endif
+
+#if !defined(RGFW_NO_X11_XI_PRELOAD)
+ typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int);
+ PFN_XISelectEvents XISelectEventsSRC = NULL;
+ #define XISelectEvents XISelectEventsSRC
+
+ void* X11Xihandle = NULL;
+#endif
+
+#if !defined(RGFW_NO_X11_EXT_PRELOAD)
+ typedef void (* PFN_XSyncIntToValue)(XSyncValue*, int);
+ PFN_XSyncIntToValue XSyncIntToValueSRC = NULL;
+ #define XSyncIntToValue XSyncIntToValueSRC
+
+ typedef Status (* PFN_XSyncSetCounter)(Display*, XSyncCounter, XSyncValue);
+ PFN_XSyncSetCounter XSyncSetCounterSRC = NULL;
+ #define XSyncSetCounter XSyncSetCounterSRC
+
+ typedef XSyncCounter (* PFN_XSyncCreateCounter)(Display*, XSyncValue);
+ PFN_XSyncCreateCounter XSyncCreateCounterSRC = NULL;
+ #define XSyncCreateCounter XSyncCreateCounterSRC
+
+ typedef void (* PFN_XShapeCombineMask)(Display*,Window,int,int,int,Pixmap,int);
+ PFN_XShapeCombineMask XShapeCombineMaskSRC;
+ #define XShapeCombineMask XShapeCombineMaskSRC
+
+ typedef void (* PFN_XShapeCombineRegion)(Display*,Window,int,int,int,Region,int);
+ PFN_XShapeCombineRegion XShapeCombineRegionSRC;
+ #define XShapeCombineRegion XShapeCombineRegionSRC
+ void* X11XEXThandle = NULL;
+#endif
+
+#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD)
+ PFN_XcursorImageLoadCursor XcursorImageLoadCursorSRC = NULL;
+ PFN_XcursorImageCreate XcursorImageCreateSRC = NULL;
+ PFN_XcursorImageDestroy XcursorImageDestroySRC = NULL;
+
+ #define XcursorImageLoadCursor XcursorImageLoadCursorSRC
+ #define XcursorImageCreate XcursorImageCreateSRC
+ #define XcursorImageDestroy XcursorImageDestroySRC
+
+ void* X11Cursorhandle = NULL;
+#endif
+
+RGFWDEF RGFW_bool RGFW_waitForShowEvent_X11(RGFW_window* win);
+RGFW_bool RGFW_waitForShowEvent_X11(RGFW_window* win) {
+ XEvent dummy;
+ while (!XCheckTypedWindowEvent(_RGFW->display, win->src.window, VisibilityNotify, &dummy)) {
+ RGFW_waitForEvent(100);
+ }
+
+ return RGFW_TRUE;
+}
+
+RGFWDEF void RGFW_x11_icCallback(XIC ic, char* clientData, char* callData);
+void RGFW_x11_icCallback(XIC ic, char* clientData, char* callData) {
+ RGFW_UNUSED(ic); RGFW_UNUSED(callData);
+ RGFW_window* win = (RGFW_window*)(void*)clientData;
+ win->src.ic = NULL;
+}
+
+RGFWDEF void RGFW_x11_imCallback(XIM im, char* clientData, char* callData);
+void RGFW_x11_imCallback(XIM im, char* clientData, char* callData) {
+ RGFW_UNUSED(im); RGFW_UNUSED(clientData); RGFW_UNUSED(callData);
+ _RGFW->im = NULL;
+}
+
+RGFWDEF void RGFW_x11_imInitCallback(Display* display, XPointer clientData, XPointer callData);
+void RGFW_x11_imInitCallback(Display* display, XPointer clientData, XPointer callData) {
+ RGFW_UNUSED(display); RGFW_UNUSED(clientData); RGFW_UNUSED(callData);
+
+ if (_RGFW->im) {
+ return;
+ }
+
+ _RGFW->im = XOpenIM(_RGFW->display, 0, NULL, NULL);
+ if (_RGFW->im == NULL) {
+ return;
+ }
+
+ RGFW_bool found = RGFW_FALSE;
+ XIMStyles* styles = NULL;
+
+ if (XGetIMValues(_RGFW->im, XNQueryInputStyle, &styles, NULL) != NULL) {
+ found = RGFW_FALSE;
+ } else {
+ for (unsigned int i = 0; i < styles->count_styles; i++) {
+ if (styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing)) {
+ found = RGFW_TRUE;
+ break;
+ }
+ }
+
+ XFree(styles);
+ }
+
+ if (found == RGFW_FALSE) {
+ XCloseIM(_RGFW->im);
+ _RGFW->im = NULL;
+ }
+
+ XIMCallback callback;
+ callback.callback = (XIMProc) RGFW_x11_imCallback;
+ callback.client_data = NULL;
+ XSetIMValues(_RGFW->im, XNDestroyCallback, &callback, NULL);
+}
+
+void* RGFW_getDisplay_X11(void) { return _RGFW->display; }
+u64 RGFW_window_getWindow_X11(RGFW_window* win) { return (u64)win->src.window; }
+
+RGFWDEF RGFW_format RGFW_XImage_getFormat(XImage* image);
+RGFW_format RGFW_XImage_getFormat(XImage* image) {
+ switch (image->bits_per_pixel) {
+ case 24:
+ if (image->red_mask == 0xFF0000 && image->green_mask == 0x00FF00 && image->blue_mask == 0x0000FF)
+ return RGFW_formatRGB8;
+ if (image->red_mask == 0x0000FF && image->green_mask == 0x00FF00 && image->blue_mask == 0xFF0000)
+ return RGFW_formatBGR8;
+ break;
+ case 32:
+ if (image->red_mask == 0x00FF0000 && image->green_mask == 0x0000FF00 && image->blue_mask == 0x000000FF)
+ return RGFW_formatBGRA8;
+ if (image->red_mask == 0x000000FF && image->green_mask == 0x0000FF00 && image->blue_mask == 0x00FF0000)
+ return RGFW_formatRGBA8;
+ if (image->red_mask == 0x0000FF00 && image->green_mask == 0x00FF0000 && image->blue_mask == 0xFF000000)
+ return RGFW_formatABGR8;
+ if (image->red_mask == 0x00FF0000 && image->green_mask == 0x0000FF00 && image->blue_mask == 0x000000FF)
+ return RGFW_formatARGB8; /* ambiguous without alpha */
+ break;
+ }
+ return RGFW_formatARGB8;
+}
+
+
+RGFW_bool RGFW_window_createSurfacePtr(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) {
+ RGFW_ASSERT(surface != NULL);
+ surface->data = data;
+ surface->w = w;
+ surface->h = h;
+ surface->format = format;
+
+ XWindowAttributes attrs;
+ if (XGetWindowAttributes(_RGFW->display, win->src.window, &attrs) == 0) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, "Failed to get window attributes.");
+ return RGFW_FALSE;
+ }
+
+ surface->native.bitmap = XCreateImage(_RGFW->display, attrs.visual, (u32)attrs.depth,
+ ZPixmap, 0, NULL, (u32)surface->w, (u32)surface->h, 32, 0);
+
+ surface->native.buffer = (u8*)RGFW_ALLOC((size_t)(w * h * 4));
+ surface->native.format = RGFW_XImage_getFormat(surface->native.bitmap);
+
+ if (surface->native.bitmap == NULL) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, "Failed to create XImage.");
+ return RGFW_FALSE;
+ }
+
+ surface->native.format = RGFW_formatBGRA8;
+ return RGFW_TRUE;
+}
+
+RGFW_format RGFW_FUNC(RGFW_nativeFormat)(void) { return RGFW_formatBGRA8; }
+
+RGFW_bool RGFW_FUNC(RGFW_createSurfacePtr) (u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) {
+ return RGFW_window_createSurfacePtr(_RGFW->root, data, w, h, format, surface);
+}
+
+void RGFW_FUNC(RGFW_window_blitSurface) (RGFW_window* win, RGFW_surface* surface) {
+ RGFW_ASSERT(surface != NULL);
+ surface->native.bitmap->data = (char*)surface->native.buffer;
+ RGFW_copyImageData((u8*)surface->native.buffer, surface->w, RGFW_MIN(win->h, surface->h), surface->native.format, surface->data, surface->format, surface->convertFunc);
+
+ XPutImage(_RGFW->display, win->src.window, win->src.gc, surface->native.bitmap, 0, 0, 0, 0, (u32)RGFW_MIN(win->w, surface->w), (u32)RGFW_MIN(win->h, surface->h));
+ surface->native.bitmap->data = NULL;
+ return;
+}
+
+void RGFW_FUNC(RGFW_surface_freePtr) (RGFW_surface* surface) {
+ RGFW_ASSERT(surface != NULL);
+ RGFW_FREE(surface->native.buffer);
+ XDestroyImage(surface->native.bitmap);
+ return;
+}
+
+#define RGFW_LOAD_ATOM(name) \
+ static Atom name = 0; \
+ if (name == 0) name = XInternAtom(_RGFW->display, #name, False);
+
+void RGFW_FUNC(RGFW_window_setBorder) (RGFW_window* win, RGFW_bool border) {
+ RGFW_setBit(&win->internal.flags, RGFW_windowNoBorder, !border);
+ RGFW_LOAD_ATOM(_MOTIF_WM_HINTS);
+
+ struct __x11WindowHints {
+ unsigned long flags, functions, decorations, status;
+ long input_mode;
+ } hints;
+ hints.flags = 2;
+ hints.decorations = border;
+
+ XChangeProperty(_RGFW->display, win->src.window, _MOTIF_WM_HINTS, _MOTIF_WM_HINTS, 32, PropModeReplace, (u8*)&hints, 5);
+
+ if (RGFW_window_isHidden(win) == 0) {
+ RGFW_window_hide(win);
+ RGFW_window_show(win);
+ }
+}
+
+void RGFW_FUNC(RGFW_window_setRawMouseModePlatform) (RGFW_window* win, RGFW_bool state) {
+ RGFW_UNUSED(win); RGFW_UNUSED(state);
+}
+
+void RGFW_FUNC(RGFW_window_captureMousePlatform) (RGFW_window* win, RGFW_bool state) {
+ if (state) {
+ unsigned int event_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
+ XGrabPointer(_RGFW->display, win->src.window, True, event_mask, GrabModeAsync, GrabModeAsync, win->src.window, None, CurrentTime);
+ } else {
+ XUngrabPointer(_RGFW->display, CurrentTime);
+ }
+}
+
+#define RGFW_LOAD_LIBRARY(x, lib) if (x == NULL) x = dlopen(lib, RTLD_LAZY | RTLD_LOCAL)
+#define RGFW_PROC_DEF(proc, name) if (name##SRC == NULL && proc != NULL) { \
+ void* ptr = dlsym(proc, #name); \
+ if (ptr != NULL) RGFW_MEMCPY(&name##SRC, &ptr, sizeof(PFN_##name)); \
+}
+
+RGFWDEF void RGFW_window_getVisual(XVisualInfo* visual, RGFW_bool transparent);
+void RGFW_window_getVisual(XVisualInfo* visual, RGFW_bool transparent) {
+ visual->visual = DefaultVisual(_RGFW->display, DefaultScreen(_RGFW->display));
+ visual->depth = DefaultDepth(_RGFW->display, DefaultScreen(_RGFW->display));
+ if (transparent) {
+ XMatchVisualInfo(_RGFW->display, DefaultScreen(_RGFW->display), 32, TrueColor, visual); /*!< for RGBA backgrounds */
+ if (visual->depth != 32)
+ RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, "Failed to load a 32-bit depth.");
+ }
+}
+
+RGFWDEF int RGFW_XErrorHandler(Display* display, XErrorEvent* ev);
+int RGFW_XErrorHandler(Display* display, XErrorEvent* ev) {
+ char errorText[512];
+ XGetErrorText(display, ev->error_code, errorText, sizeof(errorText));
+
+ char buf[1024];
+ RGFW_SNPRINTF(buf, sizeof(buf), "[X Error] %s\n Error code: %d\n Request code: %d\n Minor code: %d\n Serial: %lu\n",
+ errorText,
+ ev->error_code, ev->request_code, ev->minor_code, ev->serial);
+
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errX11, buf);
+ _RGFW->x11Error = ev;
+ return 0;
+}
+
+void RGFW_XCreateWindow (XVisualInfo visual, const char* name, RGFW_windowFlags flags, RGFW_window* win) {
+ i64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask |
+ LeaveWindowMask | EnterWindowMask | ExposureMask | VisibilityChangeMask | PropertyChangeMask;
+
+ /* make X window attrubutes */
+ XSetWindowAttributes swa;
+ RGFW_MEMSET(&swa, 0, sizeof(swa));
+
+ win->src.parent = DefaultRootWindow(_RGFW->display);
+
+ Colormap cmap;
+ swa.colormap = cmap = XCreateColormap(_RGFW->display,
+ win->src.parent,
+ visual.visual, AllocNone);
+ swa.event_mask = event_mask;
+ swa.background_pixmap = None;
+
+ /* create the window */
+ win->src.window = XCreateWindow(_RGFW->display, win->src.parent, win->x, win->y, (u32)win->w, (u32)win->h,
+ 0, visual.depth, InputOutput, visual.visual,
+ CWBorderPixel | CWColormap | CWEventMask, &swa);
+
+ win->src.flashEnd = 0;
+
+ XFreeColors(_RGFW->display, cmap, NULL, 0, 0);
+
+ XSaveContext(_RGFW->display, win->src.window, _RGFW->context, (XPointer)win);
+
+ win->src.gc = XCreateGC(_RGFW->display, win->src.window, 0, NULL);
+
+ if (_RGFW->im) {
+ XIMCallback callback;
+ callback.callback = (XIMProc) RGFW_x11_icCallback;
+ callback.client_data = (XPointer) win;
+
+ win->src.ic = XCreateIC(_RGFW->im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, win->src.window, XNFocusWindow, win->src.window, XNDestroyCallback, &callback, NULL);
+ }
+
+
+ /* In your .desktop app, if you set the property
+ StartupWMClass=RGFW that will assoicate the launcher icon
+ with your application - robrohan */
+ if (_RGFW->className == NULL)
+ _RGFW->className = (char*)name;
+
+ XClassHint hint;
+ hint.res_class = (char*)_RGFW->className;
+
+ if (_RGFW->instName == NULL) hint.res_name = (char*)name;
+ else hint.res_name = (char*)_RGFW->instName;
+
+ XSetClassHint(_RGFW->display, win->src.window, &hint);
+
+ XWMHints hints;
+ hints.flags = StateHint;
+ hints.initial_state = NormalState;
+
+ XSetWMHints(_RGFW->display, win->src.window, &hints);
+
+ if (flags & RGFW_windowScaleToMonitor)
+ RGFW_window_scaleToMonitor(win);
+
+ XSelectInput(_RGFW->display, (Drawable) win->src.window, event_mask); /*!< tell X11 what events we want */
+
+ /* make it so the user can't close the window until the program does */
+ RGFW_LOAD_ATOM(WM_DELETE_WINDOW);
+ XSetWMProtocols(_RGFW->display, (Drawable) win->src.window, &WM_DELETE_WINDOW, 1);
+ /* set the background */
+ RGFW_window_setName(win, name);
+
+ XMoveWindow(_RGFW->display, (Drawable) win->src.window, win->x, win->y); /*!< move the window to it's proper cords */
+
+ if (flags & RGFW_windowAllowDND) { /* init drag and drop atoms and turn on drag and drop for this window */
+ win->internal.flags |= RGFW_windowAllowDND;
+
+ /* actions */
+ Atom XdndAware = XInternAtom(_RGFW->display, "XdndAware", False);
+ const u8 version = 5;
+
+ XChangeProperty(_RGFW->display, win->src.window,
+ XdndAware, 4, 32,
+ PropModeReplace, &version, 1); /*!< turns on drag and drop */
+ }
+
+#ifdef RGFW_ADVANCED_SMOOTH_RESIZE
+ RGFW_LOAD_ATOM(_NET_WM_SYNC_REQUEST_COUNTER)
+ RGFW_LOAD_ATOM(_NET_WM_SYNC_REQUEST)
+
+ Atom protcols[2] = {_NET_WM_SYNC_REQUEST, WM_DELETE_WINDOW};
+ XSetWMProtocols(_RGFW->display, win->src.window, protcols, 2);
+
+ XSyncValue initial_value;
+ XSyncIntToValue(&initial_value, 0);
+ win->src.counter = XSyncCreateCounter(_RGFW->display, initial_value);
+
+ XChangeProperty(_RGFW->display, win->src.window, _NET_WM_SYNC_REQUEST_COUNTER, XA_CARDINAL, 32, PropModeReplace, (u8*)&win->src.counter, 1);
+#endif
+
+ win->src.x = win->x;
+ win->src.y = win->y;
+ win->src.w = win->w;
+ win->src.h = win->h;
+
+ XSetWindowBackground(_RGFW->display, win->src.window, None);
+ XClearWindow(_RGFW->display, win->src.window);
+
+ /* stupid hack to make resizing the window less bad */
+ XSetWindowBackgroundPixmap(_RGFW->display, win->src.window, None);
+}
+
+RGFW_window* RGFW_FUNC(RGFW_createWindowPlatform) (const char* name, RGFW_windowFlags flags, RGFW_window* win) {
+ if ((flags & RGFW_windowOpenGL) || (flags & RGFW_windowEGL)) {
+ win->src.window = 0;
+ return win;
+ }
+
+ XVisualInfo visual;
+ RGFW_window_getVisual(&visual, RGFW_BOOL(win->internal.flags & RGFW_windowTransparent));
+ RGFW_XCreateWindow(visual, name, flags, win);
+ return win; /*return newly created window */
+}
+
+RGFW_bool RGFW_FUNC(RGFW_getGlobalMouse) (i32* fX, i32* fY) {
+ RGFW_init();
+ i32 x, y;
+ u32 z;
+ Window window1, window2;
+ XQueryPointer(_RGFW->display, XDefaultRootWindow(_RGFW->display), &window1, &window2, fX, fY, &x, &y, &z);
+ return RGFW_TRUE;
+}
+
+RGFWDEF void RGFW_XHandleClipboardSelection(XEvent* event);
+void RGFW_XHandleClipboardSelection(XEvent* event) { RGFW_UNUSED(event);
+ RGFW_LOAD_ATOM(ATOM_PAIR);
+ RGFW_LOAD_ATOM(MULTIPLE);
+ RGFW_LOAD_ATOM(TARGETS);
+ RGFW_LOAD_ATOM(SAVE_TARGETS);
+ RGFW_LOAD_ATOM(UTF8_STRING);
+
+ const XSelectionRequestEvent* request = &event->xselectionrequest;
+ Atom formats[2] = {0};
+ formats[0] = UTF8_STRING;
+ formats[1] = XA_STRING;
+ const int formatCount = sizeof(formats) / sizeof(formats[0]);
+
+ if (request->target == TARGETS) {
+ Atom targets[4] = {0};
+ targets[0] = TARGETS;
+ targets[1] = MULTIPLE;
+ targets[2] = UTF8_STRING;
+ targets[3] = XA_STRING;
+
+ XChangeProperty(_RGFW->display, request->requestor, request->property,
+ XA_ATOM, 32, PropModeReplace, (u8*) targets, sizeof(targets) / sizeof(Atom));
+ } else if (request->target == MULTIPLE) {
+ Atom* targets = NULL;
+
+ Atom actualType = 0;
+ int actualFormat = 0;
+ unsigned long count = 0, bytesAfter = 0;
+
+ XGetWindowProperty(_RGFW->display, request->requestor, request->property, 0, LONG_MAX,
+ False, ATOM_PAIR, &actualType, &actualFormat, &count, &bytesAfter, (u8**) &targets);
+
+ unsigned long i;
+ for (i = 0; i < (u32)count; i += 2) {
+ if (targets[i] == UTF8_STRING || targets[i] == XA_STRING)
+ XChangeProperty(_RGFW->display, request->requestor, targets[i + 1], targets[i],
+ 8, PropModeReplace, (const unsigned char *)_RGFW->clipboard, (i32)_RGFW->clipboard_len);
+ else
+ targets[i + 1] = None;
+ }
+
+ XChangeProperty(_RGFW->display,
+ request->requestor, request->property, ATOM_PAIR, 32,
+ PropModeReplace, (u8*) targets, (i32)count);
+
+ XFlush(_RGFW->display);
+ XFree(targets);
+ } else if (request->target == SAVE_TARGETS)
+ XChangeProperty(_RGFW->display, request->requestor, request->property, 0, 32, PropModeReplace, NULL, 0);
+ else {
+ int i;
+ for (i = 0; i < formatCount; i++) {
+ if (request->target != formats[i])
+ continue;
+ XChangeProperty(_RGFW->display, request->requestor, request->property, request->target,
+ 8, PropModeReplace, (u8*) _RGFW->clipboard, (i32)_RGFW->clipboard_len);
+ }
+ }
+
+ XEvent reply = { SelectionNotify };
+ reply.xselection.property = request->property;
+ reply.xselection.display = request->display;
+ reply.xselection.requestor = request->requestor;
+ reply.xselection.selection = request->selection;
+ reply.xselection.target = request->target;
+ reply.xselection.time = request->time;
+
+ XSendEvent(_RGFW->display, request->requestor, False, 0, &reply);
+ XFlush(_RGFW->display);
+}
+
+i32 RGFW_XHandleClipboardSelectionHelper(void);
+
+RGFW_key RGFW_FUNC(RGFW_physicalToMappedKey) (RGFW_key key) {
+ KeyCode keycode = (KeyCode)RGFW_rgfwToApiKey(key);
+ KeySym sym = XkbKeycodeToKeysym(_RGFW->display, keycode, 0, 0);
+
+ if (sym < 256) {
+ return (RGFW_key)sym;
+ }
+
+ switch (sym) {
+ case XK_F1: return RGFW_F1;
+ case XK_F2: return RGFW_F2;
+ case XK_F3: return RGFW_F3;
+ case XK_F4: return RGFW_F4;
+ case XK_F5: return RGFW_F5;
+ case XK_F6: return RGFW_F6;
+ case XK_F7: return RGFW_F7;
+ case XK_F8: return RGFW_F8;
+ case XK_F9: return RGFW_F9;
+ case XK_F10: return RGFW_F10;
+ case XK_F11: return RGFW_F11;
+ case XK_F12: return RGFW_F12;
+ case XK_F13: return RGFW_F13;
+ case XK_F14: return RGFW_F14;
+ case XK_F15: return RGFW_F15;
+ case XK_F16: return RGFW_F16;
+ case XK_F17: return RGFW_F17;
+ case XK_F18: return RGFW_F18;
+ case XK_F19: return RGFW_F19;
+ case XK_F20: return RGFW_F20;
+ case XK_F21: return RGFW_F21;
+ case XK_F22: return RGFW_F22;
+ case XK_F23: return RGFW_F23;
+ case XK_F24: return RGFW_F24;
+ case XK_F25: return RGFW_F25;
+ case XK_Shift_L: return RGFW_shiftL;
+ case XK_Shift_R: return RGFW_shiftR;
+ case XK_Control_L: return RGFW_controlL;
+ case XK_Control_R: return RGFW_controlR;
+ case XK_Alt_L: return RGFW_altL;
+ case XK_Alt_R: return RGFW_altR;
+ case XK_Super_L: return RGFW_superL;
+ case XK_Super_R: return RGFW_superR;
+ case XK_Caps_Lock: return RGFW_capsLock;
+ case XK_Num_Lock: return RGFW_numLock;
+ case XK_Scroll_Lock:return RGFW_scrollLock;
+ case XK_Up: return RGFW_up;
+ case XK_Down: return RGFW_down;
+ case XK_Left: return RGFW_left;
+ case XK_Right: return RGFW_right;
+ case XK_Home: return RGFW_home;
+ case XK_End: return RGFW_end;
+ case XK_Page_Up: return RGFW_pageUp;
+ case XK_Page_Down: return RGFW_pageDown;
+ case XK_Insert: return RGFW_insert;
+ case XK_Menu: return RGFW_menu;
+ case XK_KP_Add: return RGFW_kpPlus;
+ case XK_KP_Subtract: return RGFW_kpMinus;
+ case XK_KP_Multiply: return RGFW_kpMultiply;
+ case XK_KP_Divide: return RGFW_kpSlash;
+ case XK_KP_Equal: return RGFW_kpEqual;
+ case XK_KP_Enter: return RGFW_kpReturn;
+ case XK_KP_Decimal: return RGFW_kpPeriod;
+ case XK_KP_0: return RGFW_kp0;
+ case XK_KP_1: return RGFW_kp1;
+ case XK_KP_2: return RGFW_kp2;
+ case XK_KP_3: return RGFW_kp3;
+ case XK_KP_4: return RGFW_kp4;
+ case XK_KP_5: return RGFW_kp5;
+ case XK_KP_6: return RGFW_kp6;
+ case XK_KP_7: return RGFW_kp7;
+ case XK_KP_8: return RGFW_kp8;
+ case XK_KP_9: return RGFW_kp9;
+ case XK_Print: return RGFW_printScreen;
+ case XK_Pause: return RGFW_pause;
+ default: break;
+ }
+
+ return RGFW_keyNULL;
+}
+
+RGFWDEF void RGFW_XHandleEvent(void);
+void RGFW_XHandleEvent(void) {
+ RGFW_LOAD_ATOM(XdndTypeList);
+ RGFW_LOAD_ATOM(XdndSelection);
+ RGFW_LOAD_ATOM(XdndEnter);
+ RGFW_LOAD_ATOM(XdndPosition);
+ RGFW_LOAD_ATOM(XdndStatus);
+ RGFW_LOAD_ATOM(XdndLeave);
+ RGFW_LOAD_ATOM(XdndDrop);
+ RGFW_LOAD_ATOM(XdndFinished);
+ RGFW_LOAD_ATOM(XdndActionCopy);
+ RGFW_LOAD_ATOM(_NET_WM_SYNC_REQUEST);
+ RGFW_LOAD_ATOM(WM_PROTOCOLS);
+ RGFW_LOAD_ATOM(WM_STATE);
+ RGFW_LOAD_ATOM(_NET_WM_STATE);
+
+ /* xdnd data */
+ static Window source = 0;
+ static long version = 0;
+ static i32 format = 0;
+
+ static float deltaX = 0.0f;
+ static float deltaY = 0.0f;
+
+ XEvent reply = { ClientMessage };
+ XEvent E;
+
+ XNextEvent(_RGFW->display, &E);
+
+ if (E.type != GenericEvent) {
+ deltaX = 0.0f;
+ deltaY = 0.0f;
+ }
+
+#ifndef RGFW_NO_XRANDR
+ if (E.type == _RGFW->xrandrEventBase + RRNotify) {
+ RGFW_pollMonitors();
+ return;
+ }
+#endif
+
+ switch (E.type) {
+ case SelectionRequest:
+ RGFW_XHandleClipboardSelection(&E);
+ return;
+ case GenericEvent: {
+ XGetEventData(_RGFW->display, &E.xcookie);
+ switch (E.xcookie.evtype) {
+ case XI_RawMotion: {
+ XIRawEvent* raw = (XIRawEvent *)E.xcookie.data;
+ if (raw->valuators.mask_len == 0) {
+ XFreeEventData(_RGFW->display, &E.xcookie);
+ return;
+ }
+
+ i32 index = 0;
+ if (XIMaskIsSet(raw->valuators.mask, 0) != 0) {
+ deltaX += (float)raw->raw_values[index];
+ index += 1;
+ }
+
+ if (XIMaskIsSet(raw->valuators.mask, 1) != 0)
+ deltaY += (float)raw->raw_values[index];
+
+ _RGFW->vectorX = (float)deltaX;
+ _RGFW->vectorY = (float)deltaY;
+ }
+ default: break;
+ }
+
+ XFreeEventData(_RGFW->display, &E.xcookie);
+ return;
+ }
+ }
+
+ RGFW_window* win = NULL;
+ if (XFindContext(_RGFW->display, E.xany.window, _RGFW->context, (XPointer*) &win) != 0) {
+ return;
+ }
+
+ if (win->src.flashEnd) {
+ if ((win->src.flashEnd <= RGFW_linux_getTimeNS()) || RGFW_window_isInFocus(win)) {
+ RGFW_window_flash(win, RGFW_flashCancel);
+ }
+ }
+
+
+ /*
+ Repeated key presses are sent as a release followed by another press at the same time.
+ We want to convert that into a single key press event with the repeat flag set
+ */
+
+ RGFW_bool keyRepeat = RGFW_FALSE;
+
+ if (E.type == KeyRelease && XEventsQueued(_RGFW->display, QueuedAfterReading)) {
+ XEvent NE;
+ XPeekEvent(_RGFW->display, &NE);
+ if (NE.type == KeyPress && E.xkey.time == NE.xkey.time && E.xkey.keycode == NE.xkey.keycode) {
+ /* Use the next KeyPress event */
+ XNextEvent(_RGFW->display, &E);
+ keyRepeat = RGFW_TRUE;
+ }
+ }
+
+ switch (E.type) {
+ case KeyPress: {
+ if (!(win->internal.enabledEvents & RGFW_keyPressedFlag)) return;
+ RGFW_key value = (u8)RGFW_apiKeyToRGFW(E.xkey.keycode);
+
+ XkbStateRec state;
+ XkbGetState(_RGFW->display, XkbUseCoreKbd, &state);
+ RGFW_updateKeyMods(win, (state.locked_mods & LockMask), (state.locked_mods & Mod2Mask), (state.locked_mods & Mod3Mask));
+
+ if (win->src.ic && XFilterEvent(&E, None) == False) {
+ char buffer[100];
+ char* chars = buffer;
+
+ Status status;
+ size_t count = (size_t)Xutf8LookupString(win->src.ic, &E.xkey, buffer, sizeof(buffer) - 1, NULL, &status);
+
+ if (status == XBufferOverflow) {
+ chars = (char*)RGFW_ALLOC(count + 1);
+ count = (size_t)Xutf8LookupString(win->src.ic, &E.xkey, chars, (int)count, NULL, &status);
+ }
+
+ if (status == XLookupChars || status == XLookupBoth) {
+ chars[count] = '\0';
+ for (size_t index = 0; index < count;
+ RGFW_keyCharCallback(win, RGFW_decodeUTF8(&chars[index], &index))
+ );
+ }
+
+ if (chars != buffer)
+ RGFW_FREE(chars);
+ } else {
+ Window root = DefaultRootWindow(_RGFW->display);
+ Window ret_root, ret_child;
+ int root_x, root_y, win_x, win_y;
+ unsigned int mask;
+ XQueryPointer(_RGFW->display, root, &ret_root, &ret_child, &root_x, &root_y, &win_x, &win_y, &mask);
+ KeySym sym = (KeySym)XkbKeycodeToKeysym(_RGFW->display, (KeyCode)E.xkey.keycode, 0, (KeyCode)mask & ShiftMask ? 1 : 0);
+
+ if ((mask & LockMask) && sym >= XK_a && sym <= XK_z)
+ sym = (mask & ShiftMask) ? sym + 32 : sym - 32;
+ if ((u8)sym != (u32)sym)
+ sym = 0;
+
+ RGFW_keyCharCallback(win, (u8)sym);
+ }
+
+ RGFW_keyCallback(win, value, win->internal.mod, keyRepeat, RGFW_TRUE);
+ break;
+ }
+ case KeyRelease: {
+ if (!(win->internal.enabledEvents & RGFW_keyReleasedFlag)) return;
+
+ RGFW_key value = (u8)RGFW_apiKeyToRGFW(E.xkey.keycode);
+
+ XkbStateRec state;
+ XkbGetState(_RGFW->display, XkbUseCoreKbd, &state);
+ RGFW_updateKeyMods(win, (state.locked_mods & LockMask), (state.locked_mods & Mod2Mask), (state.locked_mods & Mod3Mask));
+
+ RGFW_keyCallback(win, value, win->internal.mod, keyRepeat, RGFW_FALSE);
+ break;
+ }
+ case ButtonPress: {
+ RGFW_bool scroll = RGFW_FALSE;
+ if (E.xbutton.button >= Button4 && E.xbutton.button <= 7) {
+ scroll = RGFW_TRUE;
+ }
+
+ float scrollX = 0.0f;
+ float scrollY = 0.0f;
+ RGFW_mouseButton value = 0;
+
+ switch (E.xbutton.button) {
+ case Button1: value = RGFW_mouseLeft; break;
+ case Button2: value = RGFW_mouseMiddle; break;
+ case Button3: value = RGFW_mouseRight; break;
+ case Button4: scrollY = 1.0; break;
+ case Button5: scrollY = -1.0; break;
+ case 6: scrollX = 1.0f; break;
+ case 7: scrollX = -1.0f; break;
+ default:
+ value = (u8)E.xbutton.button - Button1 - 4;
+ break;
+ }
+
+ if (scroll) {
+ RGFW_mouseScrollCallback(win, scrollX, scrollY);
+ break;
+ }
+
+ RGFW_mouseButtonCallback(win, value, RGFW_TRUE);
+ break;
+ }
+ case ButtonRelease: {
+ if (E.xbutton.button >= Button4 && E.xbutton.button <= 7) break;
+
+ RGFW_mouseButton value = 0;
+ switch(E.xbutton.button) {
+ case Button1: value = RGFW_mouseLeft; break;
+ case Button2: value = RGFW_mouseMiddle; break;
+ case Button3: value = RGFW_mouseRight; break;
+ default:
+ value = (u8)E.xbutton.button - Button1 - 4;
+ break;
+ }
+
+ RGFW_mouseButtonCallback(win, value, RGFW_FALSE);
+ break;
+ }
+ case MotionNotify:
+ RGFW_mousePosCallback(win, E.xmotion.x, E.xmotion.y, _RGFW->vectorX, _RGFW->vectorY);
+ break;
+
+ case Expose: {
+ RGFW_windowRefreshCallback(win);
+
+#ifdef RGFW_ADVANCED_SMOOTH_RESIZE
+ XSyncValue value;
+ XSyncIntToValue(&value, (i32)win->src.counter_value);
+ XSyncSetCounter(_RGFW->display, win->src.counter, value);
+#endif
+ break;
+ }
+
+ case PropertyNotify:
+ if (E.xproperty.state != PropertyNewValue) break;
+
+ if (E.xproperty.atom == WM_STATE) {
+ if (RGFW_window_isMinimized(win) && !(win->internal.flags & RGFW_windowMinimized)) {
+ RGFW_windowMinimizedCallback(win);
+ break;
+ }
+ } else if (E.xproperty.atom == _NET_WM_STATE) {
+ if (RGFW_window_isMaximized(win) && !(win->internal.flags & RGFW_windowMaximize)) {
+ RGFW_windowMaximizedCallback(win, win->x, win->y, win->w, win->h);
+ break;
+ }
+ }
+
+ RGFW_window_checkMode(win);
+ break;
+ case MapNotify: case UnmapNotify: RGFW_window_checkMode(win); break;
+ case ClientMessage: {
+ RGFW_LOAD_ATOM(WM_DELETE_WINDOW);
+ /* if the client closed the window */
+ if (E.xclient.data.l[0] == (long)WM_DELETE_WINDOW) {
+ RGFW_windowQuitCallback(win);
+ break;
+ }
+#ifdef RGFW_ADVANCED_SMOOTH_RESIZE
+ if (E.xclient.message_type == WM_PROTOCOLS && (Atom)E.xclient.data.l[0] == _NET_WM_SYNC_REQUEST) {
+ RGFW_windowRefreshCallback(win);
+ win->src.counter_value = 0;
+ win->src.counter_value |= E.xclient.data.l[2];
+ win->src.counter_value |= (E.xclient.data.l[3] << 32);
+
+ XSyncValue value;
+ XSyncIntToValue(&value, (i32)win->src.counter_value);
+ XSyncSetCounter(_RGFW->display, win->src.counter, value);
+ break;
+ }
+#endif
+ if ((win->internal.flags & RGFW_windowAllowDND) == 0)
+ return;
+
+ i32 dragX = 0;
+ i32 dragY = 0;
+
+ reply.xclient.window = source;
+ 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;
+
+ if (E.xclient.message_type == XdndEnter) {
+ if (version > 5)
+ break;
+
+ unsigned long count;
+ Atom* formats;
+ Atom real_formats[6];
+ Bool list = E.xclient.data.l[1] & 1;
+
+ source = (unsigned long int)E.xclient.data.l[0];
+ version = E.xclient.data.l[1] >> 24;
+ format = None;
+ if (list) {
+ Atom actualType;
+ i32 actualFormat;
+ unsigned long bytesAfter;
+
+ XGetWindowProperty(
+ _RGFW->display, source, XdndTypeList,
+ 0, LONG_MAX, False, 4,
+ &actualType, &actualFormat, &count, &bytesAfter, (u8**)&formats
+ );
+ } else {
+ count = 0;
+
+ size_t i;
+ for (i = 2; i < 5; i++) {
+ if (E.xclient.data.l[i] != None) {
+ real_formats[count] = (unsigned long int)E.xclient.data.l[i];
+ count += 1;
+ }
+ }
+
+ formats = real_formats;
+ }
+
+ Atom XtextPlain = XInternAtom(_RGFW->display, "text/plain", False);
+ Atom XtextUriList = XInternAtom(_RGFW->display, "text/uri-list", False);
+
+ size_t i;
+ for (i = 0; i < count; i++) {
+ if (formats[i] == XtextUriList || formats[i] == XtextPlain) {
+ format = (int)formats[i];
+ break;
+ }
+ }
+
+ if (list) {
+ XFree(formats);
+ }
+
+ 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 (version > 5)
+ break;
+
+ XTranslateCoordinates(
+ _RGFW->display, XDefaultRootWindow(_RGFW->display), win->src.window,
+ xabs, yabs, &xpos, &ypos, &dummy
+ );
+
+ dragX = xpos;
+ dragY = ypos;
+
+ reply.xclient.window = source;
+ reply.xclient.message_type = XdndStatus;
+
+ if (format) {
+ reply.xclient.data.l[1] = 1;
+ if (version >= 2)
+ reply.xclient.data.l[4] = (long)XdndActionCopy;
+ }
+
+ XSendEvent(_RGFW->display, source, False, NoEventMask, &reply);
+ XFlush(_RGFW->display);
+ break;
+ }
+ if (E.xclient.message_type != XdndDrop)
+ break;
+
+ if (version > 5)
+ break;
+
+ if (format) {
+ Time time = (version >= 1)
+ ? (Time)E.xclient.data.l[2]
+ : CurrentTime;
+
+ XConvertSelection(
+ _RGFW->display, XdndSelection, (Atom)format,
+ XdndSelection, win->src.window, time
+ );
+ } else if (version >= 2) {
+ XEvent new_reply = { ClientMessage };
+
+ XSendEvent(_RGFW->display, source, False, NoEventMask, &new_reply);
+ XFlush(_RGFW->display);
+ }
+
+ RGFW_dataDragCallback(win, dragX, dragY);
+ } break;
+ case SelectionNotify: {
+ /* this is only for checking for xdnd drops */
+ if (!(win->internal.enabledEvents & RGFW_dataDropFlag) || E.xselection.property != XdndSelection || !(win->internal.flags & RGFW_windowAllowDND))
+ return;
+ char* data;
+ unsigned long result;
+
+ Atom actualType;
+ i32 actualFormat;
+ unsigned long bytesAfter;
+
+ XGetWindowProperty(_RGFW->display, E.xselection.requestor, E.xselection.property, 0, LONG_MAX, False, E.xselection.target, &actualType, &actualFormat, &result, &bytesAfter, (u8**) &data);
+
+ if (result == 0)
+ break;
+
+ const char* prefix = (const char*)"file://";
+
+ char* line;
+
+ size_t count = 0;
+ char** files = _RGFW->files;
+
+ while ((line = (char*)RGFW_strtok(data, "\r\n"))) {
+ data = NULL;
+
+ if (line[0] == '#')
+ continue;
+
+ char* l;
+ for (l = line; 1; l++) {
+ if ((l - line) > 7)
+ break;
+ else if (*l != prefix[(l - line)])
+ break;
+ else if (*l == '\0' && prefix[(l - line)] == '\0') {
+ line += 7;
+ while (*line != '/')
+ line++;
+ break;
+ } else if (*l == '\0')
+ break;
+ }
+
+ count++;
+
+ size_t len = RGFW_unix_stringlen(line);
+ char* path = (char*)RGFW_ALLOC(len + 1);
+
+ size_t index = 0;
+ while (*line) {
+ if (line[0] == '%' && line[1] && line[2]) {
+ char digits[3] = {0};
+ digits[0] = line[1];
+ digits[1] = line[2];
+ digits[2] = '\0';
+ path[index] = (char) RGFW_STRTOL(digits, NULL, 16);
+ line += 2;
+ } else {
+ if (index >= len) {
+ break;
+ }
+
+ path[index] = *line;
+ }
+
+ index++;
+ line++;
+ }
+
+ path[len] = '\0';
+ size_t cnt = RGFW_MIN(len + 1, RGFW_MAX_PATH);
+ if (cnt == RGFW_MAX_PATH) {
+ path[cnt] = '\0';
+ }
+
+ RGFW_MEMCPY(files[count - 1], path, cnt);
+ RGFW_FREE(path);
+ }
+
+ RGFW_dataDropCallback(win, files, count);
+ if (data)
+ XFree(data);
+
+ if (version >= 2) {
+ XEvent new_reply = { ClientMessage };
+ new_reply.xclient.window = source;
+ new_reply.xclient.message_type = XdndFinished;
+ new_reply.xclient.format = 32;
+ new_reply.xclient.data.l[1] = (long int)result;
+ new_reply.xclient.data.l[2] = (long int)XdndActionCopy;
+ XSendEvent(_RGFW->display, source, False, NoEventMask, &new_reply);
+ XFlush(_RGFW->display);
+ }
+ break;
+ }
+ case FocusIn:
+ if (win->src.ic) XSetICFocus(win->src.ic);
+ RGFW_focusCallback(win, 1);
+ break;
+ case FocusOut:
+ if (win->src.ic) XUnsetICFocus(win->src.ic);
+ RGFW_focusCallback(win, 0);
+ break;
+ case EnterNotify: {
+ RGFW_mouseNotifyCallback(win, E.xcrossing.x, E.xcrossing.y, RGFW_TRUE);
+ break;
+ }
+
+ case LeaveNotify: {
+ RGFW_mouseNotifyCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, RGFW_FALSE);
+ break;
+ }
+ case ReparentNotify:
+ win->src.parent = E.xreparent.parent;
+ break;
+ case ConfigureNotify: {
+ /* detect resize */
+ if (E.xconfigure.width != win->src.w || E.xconfigure.height != win->src.h) {
+ RGFW_window_checkMode(win);
+ win->src.w = E.xconfigure.width;
+ win->src.h = E.xconfigure.height;
+ RGFW_windowResizedCallback(win, E.xconfigure.width, E.xconfigure.height);
+ }
+
+ i32 x = E.xconfigure.x;
+ i32 y = E.xconfigure.y;
+
+ /*
+ if the event came from the server and we're not a direct child of the root window then
+ we're using local coords which need to be translated into screen coords
+ */
+ Window root = DefaultRootWindow(_RGFW->display);
+ if (E.xany.send_event == 0 && win->src.parent != root) {
+ Window dummy = 0;
+ XTranslateCoordinates(_RGFW->display, win->src.parent, root, x, y, &x, &y, &dummy);
+ }
+
+ /* detect move */
+ if (E.xconfigure.x != win->src.x || E.xconfigure.y != win->src.y) {
+ win->src.x = E.xconfigure.x;
+ win->src.y = E.xconfigure.y;
+ RGFW_windowMovedCallback(win, E.xconfigure.x, E.xconfigure.y);
+ }
+ return;
+ }
+ default:
+ break;
+ }
+
+ XFlush(_RGFW->display);
+}
+
+void RGFW_FUNC(RGFW_pollEvents) (void) {
+ RGFW_resetPrevState();
+
+ XPending(_RGFW->display);
+ /* if there is no unread queued events, get a new one */
+ while (QLength(_RGFW->display)) {
+ RGFW_XHandleEvent();
+ }
+}
+
+void RGFW_FUNC(RGFW_window_move) (RGFW_window* win, i32 x, i32 y) {
+ RGFW_ASSERT(win != NULL);
+ win->x = x;
+ win->y = y;
+
+ XMoveWindow(_RGFW->display, win->src.window, x, y);
+ return;
+}
+
+
+void RGFW_FUNC(RGFW_window_resize) (RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+ win->w = (i32)w;
+ win->h = (i32)h;
+
+ XResizeWindow(_RGFW->display, win->src.window, (u32)w, (u32)h);
+
+ if ((win->internal.flags & RGFW_windowNoResize)) {
+ XSizeHints sh;
+ sh.flags = (1L << 4) | (1L << 5);
+ sh.min_width = sh.max_width = (i32)w;
+ sh.min_height = sh.max_height = (i32)h;
+
+ XSetWMSizeHints(_RGFW->display, (Drawable) win->src.window, &sh, XA_WM_NORMAL_HINTS);
+ }
+ return;
+}
+
+void RGFW_FUNC(RGFW_window_setAspectRatio) (RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+
+
+ if (w == 0 && h == 0)
+ return;
+ XSizeHints hints;
+ long flags;
+
+ XGetWMNormalHints(_RGFW->display, win->src.window, &hints, &flags);
+
+ hints.flags |= PAspect;
+
+ hints.min_aspect.x = hints.max_aspect.x = (i32)w;
+ hints.min_aspect.y = hints.max_aspect.y = (i32)h;
+
+ XSetWMNormalHints(_RGFW->display, win->src.window, &hints);
+ return;
+}
+
+void RGFW_FUNC(RGFW_window_setMinSize) (RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+
+ long flags;
+ XSizeHints hints;
+ RGFW_MEMSET(&hints, 0, sizeof(XSizeHints));
+
+ XGetWMNormalHints(_RGFW->display, win->src.window, &hints, &flags);
+
+ hints.flags |= PMinSize;
+
+ hints.min_width = (i32)w;
+ hints.min_height = (i32)h;
+
+ XSetWMNormalHints(_RGFW->display, win->src.window, &hints);
+ return;
+}
+
+void RGFW_FUNC(RGFW_window_setMaxSize) (RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+
+ long flags;
+ XSizeHints hints;
+ RGFW_MEMSET(&hints, 0, sizeof(XSizeHints));
+
+ XGetWMNormalHints(_RGFW->display, win->src.window, &hints, &flags);
+
+ hints.flags |= PMaxSize;
+
+ hints.max_width = (i32)w;
+ hints.max_height = (i32)h;
+
+ XSetWMNormalHints(_RGFW->display, win->src.window, &hints);
+ return;
+}
+
+void RGFW_toggleXMaximized(RGFW_window* win, RGFW_bool maximized);
+void RGFW_toggleXMaximized(RGFW_window* win, RGFW_bool maximized) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_LOAD_ATOM(_NET_WM_STATE);
+ RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
+ RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
+
+ XEvent xev = {0};
+ xev.type = ClientMessage;
+ xev.xclient.window = win->src.window;
+ xev.xclient.message_type = _NET_WM_STATE;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = maximized;
+ xev.xclient.data.l[1] = (long int)_NET_WM_STATE_MAXIMIZED_HORZ;
+ xev.xclient.data.l[2] = (long int)_NET_WM_STATE_MAXIMIZED_VERT;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = 0;
+
+ XSendEvent(_RGFW->display, DefaultRootWindow(_RGFW->display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+}
+
+void RGFW_FUNC(RGFW_window_maximize) (RGFW_window* win) {
+ win->internal.oldX = win->x;
+ win->internal.oldY = win->y;
+ win->internal.oldW = win->w;
+ win->internal.oldH = win->h;
+
+ RGFW_toggleXMaximized(win, 1);
+ return;
+}
+
+void RGFW_FUNC(RGFW_window_focus) (RGFW_window* win) {
+ RGFW_ASSERT(win);
+
+ XWindowAttributes attr;
+ XGetWindowAttributes(_RGFW->display, win->src.window, &attr);
+ if (attr.map_state != IsViewable) return;
+
+ XSetInputFocus(_RGFW->display, win->src.window, RevertToPointerRoot, CurrentTime);
+ XFlush(_RGFW->display);
+}
+
+void RGFW_FUNC(RGFW_window_raise) (RGFW_window* win) {
+ RGFW_ASSERT(win);
+ XMapRaised(_RGFW->display, win->src.window);
+ RGFW_window_setFullscreen(win, RGFW_window_isFullscreen(win));
+}
+
+void RGFW_window_setXAtom(RGFW_window* win, Atom netAtom, RGFW_bool fullscreen);
+void RGFW_window_setXAtom(RGFW_window* win, Atom netAtom, RGFW_bool fullscreen) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_LOAD_ATOM(_NET_WM_STATE);
+
+ XEvent xev = {0};
+ xev.xclient.type = ClientMessage;
+ xev.xclient.serial = 0;
+ xev.xclient.send_event = True;
+ xev.xclient.message_type = _NET_WM_STATE;
+ xev.xclient.window = win->src.window;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = fullscreen;
+ xev.xclient.data.l[1] = (long int)netAtom;
+ xev.xclient.data.l[2] = 0;
+
+ XSendEvent(_RGFW->display, DefaultRootWindow(_RGFW->display), False, SubstructureNotifyMask | SubstructureRedirectMask, &xev);
+}
+
+void RGFW_FUNC(RGFW_window_setFullscreen)(RGFW_window* win, RGFW_bool fullscreen) {
+ RGFW_ASSERT(win != NULL);
+
+ if (fullscreen) {
+ win->internal.flags |= RGFW_windowFullscreen;
+ win->internal.oldX = win->x;
+ win->internal.oldY = win->y;
+ win->internal.oldW = win->w;
+ win->internal.oldH = win->h;
+ }
+ else win->internal.flags &= ~(u32)RGFW_windowFullscreen;
+
+ XRaiseWindow(_RGFW->display, win->src.window);
+
+ RGFW_LOAD_ATOM(_NET_WM_STATE_FULLSCREEN);
+ RGFW_window_setXAtom(win, _NET_WM_STATE_FULLSCREEN, fullscreen);
+
+ if (!(win->internal.flags & RGFW_windowTransparent)) {
+ const unsigned char value = fullscreen;
+ RGFW_LOAD_ATOM(_NET_WM_BYPASS_COMPOSITOR);
+ XChangeProperty(
+ _RGFW->display, win->src.window,
+ _NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32,
+ PropModeReplace, &value, 1);
+ }
+}
+
+void RGFW_FUNC(RGFW_window_setFloating)(RGFW_window* win, RGFW_bool floating) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_LOAD_ATOM(_NET_WM_STATE_ABOVE);
+ RGFW_window_setXAtom(win, _NET_WM_STATE_ABOVE, floating);
+}
+
+void RGFW_FUNC(RGFW_window_setOpacity)(RGFW_window* win, u8 opacity) {
+ RGFW_ASSERT(win != NULL);
+ const u32 value = (u32) (0xffffffffu * (double) opacity);
+ RGFW_LOAD_ATOM(NET_WM_WINDOW_OPACITY);
+ XChangeProperty(_RGFW->display, win->src.window,
+ NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &value, 1);
+}
+
+void RGFW_FUNC(RGFW_window_minimize)(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+
+ if (RGFW_window_isMaximized(win)) return;
+
+ win->internal.oldX = win->x;
+ win->internal.oldY = win->y;
+ win->internal.oldW = win->w;
+ win->internal.oldH = win->h;
+ XIconifyWindow(_RGFW->display, win->src.window, DefaultScreen(_RGFW->display));
+ XFlush(_RGFW->display);
+}
+
+void RGFW_FUNC(RGFW_window_restore)(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_toggleXMaximized(win, RGFW_FALSE);
+ RGFW_window_move(win, win->internal.oldX, win->internal.oldY);
+ RGFW_window_resize(win, win->internal.oldW, win->internal.oldH);
+
+ RGFW_window_show(win);
+ XFlush(_RGFW->display);
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_isFloating)(RGFW_window* win) {
+ RGFW_LOAD_ATOM(_NET_WM_STATE);
+ RGFW_LOAD_ATOM(_NET_WM_STATE_ABOVE);
+
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems, bytes_after;
+ Atom* prop_return = NULL;
+
+ int status = XGetWindowProperty(_RGFW->display, win->src.window, _NET_WM_STATE, 0, (~0L), False, XA_ATOM,
+ &actual_type, &actual_format, &nitems, &bytes_after,
+ (unsigned char **)&prop_return);
+
+ if (status != Success || actual_type != XA_ATOM)
+ return RGFW_FALSE;
+
+ unsigned long i;
+ for (i = 0; i < nitems; i++)
+ if (prop_return[i] == _NET_WM_STATE_ABOVE) return RGFW_TRUE;
+
+ if (prop_return)
+ XFree(prop_return);
+ return RGFW_FALSE;
+}
+
+void RGFW_FUNC(RGFW_window_setName)(RGFW_window* win, const char* name) {
+ RGFW_ASSERT(win != NULL);
+ if (name == NULL) name = "\0";
+
+ Xutf8SetWMProperties(_RGFW->display, win->src.window, name, name, NULL, 0, NULL, NULL, NULL);
+ XStoreName(_RGFW->display, win->src.window, name);
+
+ RGFW_LOAD_ATOM(_NET_WM_NAME); RGFW_LOAD_ATOM(UTF8_STRING);
+
+ XChangeProperty(
+ _RGFW->display, win->src.window, _NET_WM_NAME, UTF8_STRING,
+ 8, PropModeReplace, (u8*)name, (int)RGFW_unix_stringlen(name)
+ );
+}
+
+#ifndef RGFW_NO_PASSTHROUGH
+void RGFW_FUNC(RGFW_window_setMousePassthrough) (RGFW_window* win, RGFW_bool passthrough) {
+ RGFW_ASSERT(win != NULL);
+ if (passthrough) {
+ Region region = XCreateRegion();
+ XShapeCombineRegion(_RGFW->display, win->src.window, ShapeInput, 0, 0, region, ShapeSet);
+ XDestroyRegion(region);
+
+ return;
+ }
+
+ XShapeCombineMask(_RGFW->display, win->src.window, ShapeInput, 0, 0, None, ShapeSet);
+}
+#endif /* RGFW_NO_PASSTHROUGH */
+
+RGFW_bool RGFW_FUNC(RGFW_window_setIconEx) (RGFW_window* win, u8* data_src, i32 w, i32 h, RGFW_format format, RGFW_icon type) {
+ Atom _NET_WM_ICON = XInternAtom(_RGFW->display, "_NET_WM_ICON", False);
+ RGFW_ASSERT(win != NULL);
+ if (data_src == NULL) {
+ RGFW_bool res = (RGFW_bool)XChangeProperty(
+ _RGFW->display, win->src.window, _NET_WM_ICON, XA_CARDINAL, 32,
+ PropModeReplace, (u8*)NULL, 0
+ );
+ return res;
+ }
+
+ i32 count = (i32)(2 + (w * h));
+
+ unsigned long* data = (unsigned long*) RGFW_ALLOC((u32)count * sizeof(unsigned long));
+ RGFW_ASSERT(data != NULL);
+
+ RGFW_MEMSET(data, 0, (u32)count * sizeof(unsigned long));
+ data[0] = (unsigned long)w;
+ data[1] = (unsigned long)h;
+
+ RGFW_copyImageData64((u8*)&data[2], w, h, RGFW_formatBGRA8, data_src, format, RGFW_TRUE, NULL);
+ RGFW_bool res = RGFW_TRUE;
+ if (type & RGFW_iconTaskbar) {
+ res = (RGFW_bool)XChangeProperty(
+ _RGFW->display, win->src.window, _NET_WM_ICON, XA_CARDINAL, 32,
+ PropModeReplace, (u8*)data, count
+ );
+ }
+
+ RGFW_copyImageData64((u8*)&data[2], w, h, RGFW_formatBGRA8, data_src, format, RGFW_FALSE, NULL);
+
+ if (type & RGFW_iconWindow) {
+ XWMHints wm_hints;
+ wm_hints.flags = IconPixmapHint;
+
+ i32 depth = DefaultDepth(_RGFW->display, DefaultScreen(_RGFW->display));
+ XImage *image = XCreateImage(_RGFW->display, DefaultVisual(_RGFW->display, DefaultScreen(_RGFW->display)),
+ (u32)depth, ZPixmap, 0, (char *)&data[2], (u32)w, (u32)h, 32, 0);
+
+ wm_hints.icon_pixmap = XCreatePixmap(_RGFW->display, win->src.window, (u32)w, (u32)h, (u32)depth);
+ XPutImage(_RGFW->display, wm_hints.icon_pixmap, DefaultGC(_RGFW->display, DefaultScreen(_RGFW->display)), image, 0, 0, 0, 0, (u32)w, (u32)h);
+ image->data = NULL;
+ XDestroyImage(image);
+
+ XSetWMHints(_RGFW->display, win->src.window, &wm_hints);
+ }
+
+ RGFW_FREE(data);
+ XFlush(_RGFW->display);
+ return RGFW_BOOL(res);
+}
+
+RGFW_mouse* RGFW_FUNC(RGFW_loadMouse) (u8* data, i32 w, i32 h, RGFW_format format) {
+ RGFW_ASSERT(data);
+#ifndef RGFW_NO_X11_CURSOR
+ RGFW_init();
+ XcursorImage* native = XcursorImageCreate((i32)w, (i32)h);
+ native->xhot = 0;
+ native->yhot = 0;
+ RGFW_MEMSET(native->pixels, 0, (u32)(w * h * 4));
+ RGFW_copyImageData((u8*)native->pixels, w, h, RGFW_formatBGRA8, data, format, NULL);
+
+ Cursor cursor = XcursorImageLoadCursor(_RGFW->display, native);
+ XcursorImageDestroy(native);
+
+ return (void*)cursor;
+#else
+ RGFW_UNUSED(data); RGFW_UNUSED(w); RGFW_UNUSED(h); RGFW_UNUSED(format);
+ return NULL;
+#endif
+}
+
+void RGFW_FUNC(RGFW_window_setMouse)(RGFW_window* win, RGFW_mouse* mouse) {
+ RGFW_ASSERT(win && mouse);
+ XDefineCursor(_RGFW->display, win->src.window, (Cursor)mouse);
+}
+
+void RGFW_FUNC(RGFW_freeMouse)(RGFW_mouse* mouse) {
+ RGFW_ASSERT(mouse);
+ XFreeCursor(_RGFW->display, (Cursor)mouse);
+}
+
+void RGFW_FUNC(RGFW_window_moveMouse)(RGFW_window* win, i32 x, i32 y) {
+ RGFW_ASSERT(win != NULL);
+
+ XEvent event;
+ XQueryPointer(_RGFW->display, DefaultRootWindow(_RGFW->display),
+ &event.xbutton.root, &event.xbutton.window,
+ &event.xbutton.x_root, &event.xbutton.y_root,
+ &event.xbutton.x, &event.xbutton.y,
+ &event.xbutton.state);
+
+ win->internal.lastMouseX = x - win->x;
+ win->internal.lastMouseY = y - win->y;
+ if (event.xbutton.x == x && event.xbutton.y == y)
+ return;
+
+ XWarpPointer(_RGFW->display, None, win->src.window, 0, 0, 0, 0, (int) x - win->x, (int) y - win->y);
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_setMouseDefault) (RGFW_window* win) {
+ return RGFW_window_setMouseStandard(win, RGFW_mouseArrow);
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_setMouseStandard) (RGFW_window* win, u8 mouse) {
+ RGFW_ASSERT(win != NULL);
+
+ u32 mouseIcon = 0;
+
+ switch (mouse) {
+ case RGFW_mouseNormal: mouseIcon = XC_left_ptr; break;
+ case RGFW_mouseArrow: mouseIcon = XC_left_ptr; break;
+ case RGFW_mouseIbeam: mouseIcon = XC_xterm; break;
+ case RGFW_mouseWait: mouseIcon = XC_watch; break;
+ case RGFW_mouseCrosshair: mouseIcon = XC_tcross; break;
+ case RGFW_mouseProgress: mouseIcon = XC_watch; break;
+ case RGFW_mouseResizeNWSE: mouseIcon = XC_top_left_corner; break;
+ case RGFW_mouseResizeNESW: mouseIcon = XC_top_right_corner; break;
+ case RGFW_mouseResizeEW: mouseIcon = XC_sb_h_double_arrow; break;
+ case RGFW_mouseResizeNS: mouseIcon = XC_sb_v_double_arrow; break;
+ case RGFW_mouseResizeNW: mouseIcon = XC_top_left_corner; break;
+ case RGFW_mouseResizeN: mouseIcon = XC_top_side; break;
+ case RGFW_mouseResizeNE: mouseIcon = XC_top_right_corner; break;
+ case RGFW_mouseResizeE: mouseIcon = XC_right_side; break;
+ case RGFW_mouseResizeSE: mouseIcon = XC_bottom_right_corner; break;
+ case RGFW_mouseResizeS: mouseIcon = XC_bottom_side; break;
+ case RGFW_mouseResizeSW: mouseIcon = XC_bottom_left_corner; break;
+ case RGFW_mouseResizeW: mouseIcon = XC_left_side; break;
+ case RGFW_mouseResizeAll: mouseIcon = XC_fleur; break;
+ case RGFW_mouseNotAllowed: mouseIcon = XC_pirate; break;
+ case RGFW_mousePointingHand: mouseIcon = XC_hand2; break;
+ default: return RGFW_FALSE;
+ }
+
+ Cursor cursor = XCreateFontCursor(_RGFW->display, mouseIcon);
+ XDefineCursor(_RGFW->display, win->src.window, (Cursor) cursor);
+ XFreeCursor(_RGFW->display, (Cursor) cursor);
+ return RGFW_TRUE;
+}
+
+void RGFW_FUNC(RGFW_window_hide)(RGFW_window* win) {
+ win->internal.flags |= (u32)RGFW_windowHide;
+ XUnmapWindow(_RGFW->display, win->src.window);
+
+ XFlush(_RGFW->display);
+}
+
+void RGFW_FUNC(RGFW_window_show) (RGFW_window* win) {
+ win->internal.flags &= ~(u32)RGFW_windowHide;
+ if (win->internal.flags & RGFW_windowFocusOnShow) RGFW_window_focus(win);
+
+ if (RGFW_window_isHidden(win) == RGFW_FALSE) {
+ return;
+ }
+
+ XMapWindow(_RGFW->display, win->src.window);
+ RGFW_window_move(win, win->x, win->y);
+
+ RGFW_waitForShowEvent_X11(win);
+ RGFW_window_setFullscreen(win, RGFW_window_isFullscreen(win));
+ return;
+}
+
+void RGFW_FUNC(RGFW_window_flash) (RGFW_window* win, RGFW_flashRequest request) {
+ if (RGFW_window_isInFocus(win) && request) {
+ return;
+ }
+
+ XWMHints* wmhints = XGetWMHints(_RGFW->display, win->src.window);
+ if (wmhints == NULL) return;
+
+ if (request) {
+ wmhints->flags |= XUrgencyHint;
+ if (request == RGFW_flashBriefly)
+ win->src.flashEnd = RGFW_linux_getTimeNS() + (u64)1e+9;
+ if (request == RGFW_flashUntilFocused)
+ win->src.flashEnd = (u64)-1;
+ } else {
+ win->src.flashEnd = 0;
+ wmhints->flags &= ~XUrgencyHint;
+ }
+
+ XSetWMHints(_RGFW->display, win->src.window, wmhints);
+ XFree(wmhints);
+}
+
+RGFW_ssize_t RGFW_FUNC(RGFW_readClipboardPtr)(char* str, size_t strCapacity) {
+ RGFW_init();
+ RGFW_LOAD_ATOM(XSEL_DATA); RGFW_LOAD_ATOM(UTF8_STRING); RGFW_LOAD_ATOM(CLIPBOARD);
+ if (XGetSelectionOwner(_RGFW->display, CLIPBOARD) == _RGFW->helperWindow) {
+ if (str != NULL)
+ RGFW_STRNCPY(str, _RGFW->clipboard, _RGFW->clipboard_len - 1);
+ _RGFW->clipboard[_RGFW->clipboard_len - 1] = '\0';
+ return (RGFW_ssize_t)_RGFW->clipboard_len - 1;
+ }
+
+ XEvent event;
+ int format;
+ unsigned long N, sizeN;
+ char* data;
+ Atom target;
+
+ XConvertSelection(_RGFW->display, CLIPBOARD, UTF8_STRING, XSEL_DATA, _RGFW->helperWindow, CurrentTime);
+ XSync(_RGFW->display, 0);
+ while (1) {
+ XNextEvent(_RGFW->display, &event);
+ if (event.type != SelectionNotify) continue;
+
+ if (event.xselection.selection != CLIPBOARD || event.xselection.property == 0)
+ return -1;
+ break;
+ }
+
+ XGetWindowProperty(event.xselection.display, event.xselection.requestor,
+ event.xselection.property, 0L, (~0L), 0, AnyPropertyType, &target,
+ &format, &sizeN, &N, (u8**) &data);
+
+ RGFW_ssize_t size;
+ if (sizeN > strCapacity && str != NULL)
+ size = -1;
+
+ if ((target == UTF8_STRING || target == XA_STRING) && str != NULL) {
+ RGFW_MEMCPY(str, data, sizeN);
+ str[sizeN] = '\0';
+ XFree(data);
+ } else if (str != NULL) size = -1;
+
+ XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property);
+ size = (RGFW_ssize_t)sizeN;
+
+ return size;
+}
+
+i32 RGFW_XHandleClipboardSelectionHelper(void) {
+ RGFW_LOAD_ATOM(SAVE_TARGETS);
+
+ XEvent event;
+ XPending(_RGFW->display);
+
+ if (QLength(_RGFW->display) || XEventsQueued(_RGFW->display, QueuedAlready) + XEventsQueued(_RGFW->display, QueuedAfterReading))
+ XNextEvent(_RGFW->display, &event);
+ else
+ return 0;
+
+ switch (event.type) {
+ case SelectionRequest:
+ RGFW_XHandleClipboardSelection(&event);
+ return 0;
+ case SelectionNotify:
+ if (event.xselection.target == SAVE_TARGETS)
+ return 0;
+ break;
+ default: break;
+ }
+
+ return 0;
+}
+
+void RGFW_FUNC(RGFW_writeClipboard)(const char* text, u32 textLen) {
+ RGFW_LOAD_ATOM(SAVE_TARGETS); RGFW_LOAD_ATOM(CLIPBOARD);
+ RGFW_init();
+
+ /* request ownership of the clipboard section and request to convert it, this means its our job to convert it */
+ XSetSelectionOwner(_RGFW->display, CLIPBOARD, _RGFW->helperWindow, CurrentTime);
+ if (XGetSelectionOwner(_RGFW->display, CLIPBOARD) != _RGFW->helperWindow) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errClipboard, "X11 failed to become owner of clipboard selection");
+ return;
+ }
+
+ if (_RGFW->clipboard)
+ RGFW_FREE(_RGFW->clipboard);
+
+ _RGFW->clipboard = (char*)RGFW_ALLOC(textLen);
+ RGFW_ASSERT(_RGFW->clipboard != NULL);
+
+ RGFW_STRNCPY(_RGFW->clipboard, text, textLen - 1);
+ _RGFW->clipboard[textLen - 1] = '\0';
+ _RGFW->clipboard_len = textLen;
+ return;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_isHidden)(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ XWindowAttributes windowAttributes;
+ XGetWindowAttributes(_RGFW->display, win->src.window, &windowAttributes);
+
+ return (windowAttributes.map_state != IsViewable);
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_isMinimized)(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_LOAD_ATOM(WM_STATE);
+
+ Atom actual_type;
+ i32 actual_format;
+ unsigned long nitems, bytes_after;
+ unsigned char* prop_data;
+
+ i32 status = XGetWindowProperty(_RGFW->display, win->src.window, WM_STATE, 0, 2, False,
+ AnyPropertyType, &actual_type, &actual_format,
+ &nitems, &bytes_after, &prop_data);
+
+ if (status == Success && nitems >= 1 && prop_data == (unsigned char*)IconicState) {
+ XFree(prop_data);
+ return RGFW_TRUE;
+ }
+
+ if (prop_data != NULL)
+ XFree(prop_data);
+
+ XWindowAttributes windowAttributes;
+ XGetWindowAttributes(_RGFW->display, win->src.window, &windowAttributes);
+ return windowAttributes.map_state != IsViewable;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_isMaximized)(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_LOAD_ATOM(_NET_WM_STATE);
+ RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
+ RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
+
+ Atom actual_type;
+ i32 actual_format;
+ unsigned long nitems, bytes_after;
+ unsigned char* prop_data;
+
+ i32 status = XGetWindowProperty(_RGFW->display, win->src.window, _NET_WM_STATE, 0, 1024, False,
+ XA_ATOM, &actual_type, &actual_format,
+ &nitems, &bytes_after, &prop_data);
+
+ if (status != Success) {
+ if (prop_data != NULL)
+ XFree(prop_data);
+
+ return RGFW_FALSE;
+ }
+
+ u64 i;
+ for (i = 0; i < nitems; i++) {
+ if (prop_data[i] == _NET_WM_STATE_MAXIMIZED_VERT ||
+ prop_data[i] == _NET_WM_STATE_MAXIMIZED_HORZ) {
+ XFree(prop_data);
+ return RGFW_TRUE;
+ }
+ }
+
+ if (prop_data != NULL)
+ XFree(prop_data);
+
+ return RGFW_FALSE;
+}
+
+RGFWDEF void RGFW_XGetSystemContentDPI(float* dpi);
+void RGFW_XGetSystemContentDPI(float* dpi) {
+ if (dpi == NULL) return;
+ float dpiOutput = 96.0f;
+
+ #ifndef RGFW_NO_XRANDR
+ char* rms = XResourceManagerString(_RGFW->display);
+ if (rms == NULL) return;
+
+ XrmDatabase db = XrmGetStringDatabase(rms);
+ if (db == NULL) return;
+
+ XrmValue value;
+ char* type = NULL;
+
+ if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value) && type && RGFW_STRNCMP(type, "String", 7) == 0)
+ dpiOutput = (float)RGFW_ATOF(value.addr);
+ XrmDestroyDatabase(db);
+ #endif
+
+ if (dpi) *dpi = dpiOutput;
+}
+
+RGFWDEF XRRModeInfo* RGFW_XGetMode(XRRCrtcInfo* ci, XRRScreenResources* res, RRMode mode, RGFW_monitorMode* foundMode);
+XRRModeInfo* RGFW_XGetMode(XRRCrtcInfo* ci, XRRScreenResources* res, RRMode mode, RGFW_monitorMode* foundMode) {
+ XRRModeInfo* mi = None;
+ for (i32 j = 0; j < res->nmode; j++) {
+ if (res->modes[j].id == mode)
+ mi = &res->modes[j];
+ }
+
+ if (mi == None) return NULL;
+
+ if ((mi->modeFlags & RR_Interlace) != 0) return NULL;
+
+ foundMode->w = (i32)mi->width;
+ foundMode->h = (i32)mi->height;
+ if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) {
+ foundMode->w = (i32)mi->height;
+ foundMode->h = (i32)mi->width;
+ } else {
+ foundMode->w = (i32)mi->width;
+ foundMode->h = (i32)mi->height;
+ }
+
+ RGFW_splitBPP((u32)DefaultDepth(_RGFW->display, DefaultScreen(_RGFW->display)), foundMode);
+
+ foundMode->src = (void*)mode;
+
+ foundMode->refreshRate = 0;
+ if (mi->hTotal == 0 || mi->vTotal == 0)
+ return mi;
+
+ u32 vTotal = mi->vTotal;
+
+ if (mi->modeFlags & RR_DoubleScan) {
+ vTotal *= 2;
+ }
+
+ if (mi->modeFlags & RR_Interlace) {
+ vTotal /= 2;
+ }
+
+ i32 numerator = (i32)mi->dotClock;
+ i32 denominator = (i32)(mi->hTotal * vTotal);
+ float refreshRate = 0;
+
+ if (denominator <= 0) {
+ denominator = 1;
+ }
+
+ refreshRate = ((float)numerator / (float)denominator);
+
+ foundMode->refreshRate = RGFW_ROUNDF((refreshRate * 100)) / 100.0f;
+ return mi;
+}
+
+void RGFW_FUNC(RGFW_pollMonitors) (void) {
+ RGFW_init();
+
+ Window root = XDefaultRootWindow(_RGFW->display);
+ XRRScreenResources* res = XRRGetScreenResourcesCurrent(_RGFW->display, root);
+ if (res == 0) {
+ return;
+ }
+
+ RROutput primary = XRRGetOutputPrimary(_RGFW->display, root);
+
+ for (RGFW_monitorNode* node = _RGFW->monitors.list.head; node; node = node->next) {
+ node->disconnected = RGFW_TRUE;
+ }
+
+ for (i32 i = 0; i < res->noutput; i++) {
+ RGFW_monitorNode* node = NULL;
+ for (node = _RGFW->monitors.list.head; node; node = node->next) {
+ if (node->rrOutput == res->outputs[i]) {
+ break;
+ }
+ }
+
+ if (node) {
+ node->disconnected = RGFW_FALSE;
+ if (node->rrOutput == primary) {
+ _RGFW->monitors.primary = node;
+ }
+ continue;
+ }
+
+ RGFW_monitor monitor;
+
+ XRROutputInfo* info = XRRGetOutputInfo(_RGFW->display, res, res->outputs[i]);
+ if (info == NULL || info->connection != RR_Connected || info->crtc == None) {
+ continue;
+ }
+
+ XRRCrtcInfo* ci = XRRGetCrtcInfo(_RGFW->display, res, info->crtc);
+
+ if (ci == NULL) {
+ continue;
+ }
+
+ float physW = (float)info->mm_width / 25.4f;
+ float physH = (float)info->mm_height / 25.4f;
+
+ RGFW_STRNCPY(monitor.name, info->name, sizeof(monitor.name) - 1);
+ monitor.name[sizeof(monitor.name) - 1] = '\0';
+
+ if (physW > 0.0f && physH > 0.0f) {
+ monitor.physW = physW;
+ monitor.physH = physH;
+ } else {
+ monitor.physW = (float) ((float)ci->width / 96.f);
+ monitor.physH = (float) ((float)ci->height / 96.f);
+ }
+
+ monitor.x = ci->x;
+ monitor.y = ci->y;
+
+ float dpi = 96.0f;
+ RGFW_XGetSystemContentDPI(&dpi);
+
+ monitor.scaleX = dpi / 96.0f;
+ monitor.scaleY = dpi / 96.0f;
+
+ monitor.pixelRatio = dpi >= 192.0f ? 2.0f : 1.0f;
+
+ XRRModeInfo* mi = RGFW_XGetMode(ci, res, ci->mode, &monitor.mode);
+
+ if (mi == NULL) {
+ break;
+ }
+
+ XRRFreeCrtcInfo(ci);
+
+ node = RGFW_monitors_add(&monitor);
+ if (node == NULL) break;
+
+ node->rrOutput = res->outputs[i];
+ node->crtc = info->crtc;
+
+ if (node->rrOutput == primary) {
+ _RGFW->monitors.primary = node;
+ }
+
+ XRRFreeOutputInfo(info);
+ info = NULL;
+
+ RGFW_monitorCallback(_RGFW->root, &node->mon, RGFW_TRUE);
+ }
+
+ XRRFreeScreenResources(res);
+
+ RGFW_monitors_refresh();
+}
+
+RGFW_bool RGFW_FUNC(RGFW_monitor_getWorkarea) (RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height) {
+ RGFW_LOAD_ATOM(_NET_WORKAREA);
+ RGFW_LOAD_ATOM(_NET_CURRENT_DESKTOP);
+
+ Window root = DefaultRootWindow(_RGFW->display);
+
+ i32 areaX = monitor->x;
+ i32 areaY = monitor->y;
+ i32 areaW = monitor->mode.w;
+ i32 areaH = monitor->mode.h;
+
+ if (_NET_WORKAREA && _NET_CURRENT_DESKTOP) {
+ Atom* extents = NULL;
+ Atom* desktop = NULL;
+
+ Atom actualType = 0;
+ int actualFormat = 0;
+ unsigned long extentCount = 0, bytesAfter = 0;
+ XGetWindowProperty(_RGFW->display, root, _NET_WORKAREA, 0, LONG_MAX, False, XA_CARDINAL, &actualType, &actualFormat, &extentCount, &bytesAfter, (u8**) &extents);
+
+ unsigned long count;
+ XGetWindowProperty(_RGFW->display, root, _NET_CURRENT_DESKTOP, 0, LONG_MAX, False, XA_CARDINAL, &actualType, &actualFormat, &count, &bytesAfter, (u8**) &desktop);
+
+ if (count) {
+ if (extentCount >= 4 && *desktop < extentCount / 4) {
+ i32 globalX = (i32)extents[*desktop * 4 + 0];
+ i32 globalY = (i32)extents[*desktop * 4 + 1];
+ i32 globalW = (i32)extents[*desktop * 4 + 2];
+ i32 globalH = (i32)extents[*desktop * 4 + 3];
+
+ if (areaX < globalX) {
+ areaW -= globalX - areaX;
+ areaX = globalX;
+ }
+
+ if (areaY < globalY) {
+ areaH -= globalY - areaY;
+ areaY = globalY;
+ }
+
+ if (areaX + areaW > globalX + globalW)
+ areaW = globalX - areaX + globalW;
+ if (areaY + areaH > globalY + globalH)
+ areaH = globalY - areaY + globalH;
+ }
+ }
+
+ if (extents)
+ XFree(extents);
+ if (desktop)
+ XFree(desktop);
+ }
+
+ if (x) *x = areaX;
+ if (y) *y = areaY;
+ if (width) *width = areaW;
+ if (height) *height = areaH;
+
+ return RGFW_TRUE;
+}
+
+size_t RGFW_FUNC(RGFW_monitor_getModesPtr) (RGFW_monitor* monitor, RGFW_monitorMode** modes) {
+ size_t count = 0;
+
+ XRRScreenResources* res = XRRGetScreenResourcesCurrent(_RGFW->display, DefaultRootWindow(_RGFW->display));
+ if (res == NULL) return 0;
+
+ XRRCrtcInfo* ci = XRRGetCrtcInfo(_RGFW->display, res, monitor->node->crtc);
+ XRROutputInfo* oi = XRRGetOutputInfo(_RGFW->display, res, monitor->node->rrOutput);
+ count = (size_t)oi->nmode;
+
+ int i;
+ for (i = 0; modes && i < oi->nmode; i++) {
+ XRRModeInfo* mi = RGFW_XGetMode(ci, res, oi->modes[i], &((*modes)[i]));
+ RGFW_UNUSED(mi);
+ }
+
+ XRRFreeOutputInfo(oi);
+ XRRFreeCrtcInfo(ci);
+ XRRFreeScreenResources(res);
+
+ return count;
+}
+
+size_t RGFW_FUNC(RGFW_monitor_getGammaRampPtr) (RGFW_monitor* monitor, RGFW_gammaRamp* ramp) {
+ RGFW_UNUSED(monitor); RGFW_UNUSED(ramp);
+#ifndef RGFW_NO_XRANDR
+ size_t size = (size_t)XRRGetCrtcGammaSize(_RGFW->display, monitor->node->crtc);
+ XRRCrtcGamma* gamma = XRRGetCrtcGamma(_RGFW->display, monitor->node->crtc);
+
+ if (ramp) {
+ RGFW_MEMCPY(ramp->red, gamma->red, size * sizeof(unsigned short));
+ RGFW_MEMCPY(ramp->green, gamma->green, size * sizeof(unsigned short));
+ RGFW_MEMCPY(ramp->blue, gamma->blue, size * sizeof(unsigned short));
+ }
+
+ XRRFreeGamma(gamma);
+ return size;
+#endif
+
+ return 0;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_monitor_setGammaRamp) (RGFW_monitor* monitor, RGFW_gammaRamp* ramp) {
+ RGFW_UNUSED(monitor); RGFW_UNUSED(ramp);
+
+#ifndef RGFW_NO_XRANDR
+ size_t size = (size_t)XRRGetCrtcGammaSize(_RGFW->display, monitor->node->crtc);
+ if (size != ramp->count) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errX11, "X11: Gamma ramp size must match current ramp size");
+ return RGFW_FALSE;
+ }
+
+ XRRCrtcGamma* gamma = XRRAllocGamma((int)ramp->count);
+
+ memcpy(gamma->red, ramp->red, ramp->count * sizeof(unsigned short));
+ memcpy(gamma->green, ramp->green, ramp->count * sizeof(unsigned short));
+ memcpy(gamma->blue, ramp->blue, ramp->count * sizeof(unsigned short));
+
+ XRRSetCrtcGamma(_RGFW->display, monitor->node->crtc, gamma);
+ XRRFreeGamma(gamma);
+
+ return RGFW_TRUE;
+#endif
+ return RGFW_FALSE;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_monitor_setMode)(RGFW_monitor* mon, RGFW_monitorMode* mode) {
+ RGFW_bool out = RGFW_FALSE;
+
+ XRRScreenResources* res = XRRGetScreenResourcesCurrent(_RGFW->display, DefaultRootWindow(_RGFW->display));
+ XRRCrtcInfo* ci = XRRGetCrtcInfo(_RGFW->display, res, mon->node->crtc);
+
+ if (XRRSetCrtcConfig(_RGFW->display, res, mon->node->crtc, CurrentTime, ci->x, ci->y, (RRMode)mode->src, ci->rotation, ci->outputs, ci->noutput) == True) {
+ out = RGFW_TRUE;
+ }
+
+ XRRFreeCrtcInfo(ci);
+ XRRFreeScreenResources(res);
+ return out;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_monitor_requestMode)(RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request) {
+ #ifndef RGFW_NO_XRANDR
+ RGFW_init();
+
+ RGFW_bool output = RGFW_FALSE;
+
+ XRRScreenResources* res = XRRGetScreenResourcesCurrent(_RGFW->display, DefaultRootWindow(_RGFW->display));
+ if (res == NULL) return RGFW_FALSE;
+
+ XRRCrtcInfo* ci = XRRGetCrtcInfo(_RGFW->display, res, mon->node->crtc);
+ XRROutputInfo* oi = XRRGetOutputInfo(_RGFW->display, res, mon->node->rrOutput);
+
+ RRMode native = None;
+
+ int i;
+ for (i = 0; i < oi->nmode; i++) {
+ RGFW_monitorMode foundMode;
+ XRRModeInfo* mi = RGFW_XGetMode(ci, res, oi->modes[i], &foundMode);
+ if (mi == NULL) {
+ continue;
+ }
+
+ if (RGFW_monitorModeCompare(mode, &foundMode, request)) {
+ native = mi->id;
+ output = RGFW_TRUE;
+ mon->mode = foundMode;
+ break;
+ }
+ }
+
+ if (native) {
+ XRRSetCrtcConfig(_RGFW->display, res, mon->node->crtc, CurrentTime, ci->x, ci->y, native, ci->rotation, ci->outputs, ci->noutput);
+ }
+
+ XRRFreeOutputInfo(oi);
+ XRRFreeCrtcInfo(ci);
+ XRRFreeScreenResources(res);
+ return output;
+#endif
+ return RGFW_FALSE;
+}
+
+RGFW_monitor* RGFW_FUNC(RGFW_window_getMonitor) (RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+
+ XWindowAttributes attrs;
+ if (!XGetWindowAttributes(_RGFW->display, win->src.window, &attrs)) {
+ return NULL;
+ }
+
+ for (RGFW_monitorNode* node = _RGFW->monitors.list.head; node; node = node->next) {
+ if ((attrs.x < node->mon.x + node->mon.mode.w) && (attrs.x + attrs.width > node->mon.x) && (attrs.y < node->mon.y + node->mon.mode.h) && (attrs.y + attrs.height > node->mon.y))
+ return &node->mon;
+ }
+
+
+ return &_RGFW->monitors.list.head->mon;
+}
+
+#ifdef RGFW_OPENGL
+RGFW_bool RGFW_FUNC(RGFW_window_createContextPtr_OpenGL) (RGFW_window* win, RGFW_glContext* context, RGFW_glHints* hints) {
+ /* for checking extensions later */
+ const char sRGBARBstr[] = "GLX_ARB_framebuffer_sRGB";
+ const char sRGBEXTstr[] = "GLX_EXT_framebuffer_sRGB";
+ const char noErorrStr[] = "GLX_ARB_create_context_no_error";
+ const char flushStr[] = "GLX_ARB_context_flush_control";
+ const char robustStr[] = "GLX_ARB_create_context_robustness";
+
+ /* basic RGFW int */
+ win->src.ctx.native = context;
+ win->src.gfxType = RGFW_gfxNativeOpenGL;
+
+ /* This is required so that way the user can create their own OpenGL context after RGFW_createWindow is used */
+ RGFW_bool showWindow = RGFW_FALSE;
+ if (win->src.window) {
+ showWindow = (RGFW_window_isMinimized(win) == RGFW_FALSE);
+ RGFW_window_closePlatform(win);
+ }
+
+ RGFW_bool transparent = (win->internal.flags & RGFW_windowTransparent);
+
+ /* start by creating a GLX config / X11 Viusal */
+ XVisualInfo visual;
+ GLXFBConfig bestFbc;
+
+ i32 visual_attribs[40];
+ RGFW_attribStack stack;
+ RGFW_attribStack_init(&stack, visual_attribs, 40);
+ RGFW_attribStack_pushAttribs(&stack, GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR);
+ RGFW_attribStack_pushAttribs(&stack, GLX_X_RENDERABLE, 1);
+ RGFW_attribStack_pushAttribs(&stack, GLX_RENDER_TYPE, GLX_RGBA_BIT);
+ RGFW_attribStack_pushAttribs(&stack, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT);
+ RGFW_attribStack_pushAttribs(&stack, GLX_DOUBLEBUFFER, 1);
+ RGFW_attribStack_pushAttribs(&stack, GLX_ALPHA_SIZE, hints->alpha);
+ RGFW_attribStack_pushAttribs(&stack, GLX_DEPTH_SIZE, hints->depth);
+ RGFW_attribStack_pushAttribs(&stack, GLX_STENCIL_SIZE, hints->stencil);
+ RGFW_attribStack_pushAttribs(&stack, GLX_STEREO, hints->stereo);
+ RGFW_attribStack_pushAttribs(&stack, GLX_AUX_BUFFERS, hints->auxBuffers);
+ RGFW_attribStack_pushAttribs(&stack, GLX_RED_SIZE, hints->red);
+ RGFW_attribStack_pushAttribs(&stack, GLX_GREEN_SIZE, hints->green);
+ RGFW_attribStack_pushAttribs(&stack, GLX_BLUE_SIZE, hints->blue);
+ RGFW_attribStack_pushAttribs(&stack, GLX_ACCUM_RED_SIZE, hints->accumRed);
+ RGFW_attribStack_pushAttribs(&stack, GLX_ACCUM_GREEN_SIZE, hints->accumGreen);
+ RGFW_attribStack_pushAttribs(&stack, GLX_ACCUM_BLUE_SIZE, hints->accumBlue);
+ RGFW_attribStack_pushAttribs(&stack, GLX_ACCUM_ALPHA_SIZE, hints->accumAlpha);
+
+ if (hints->sRGB) {
+ if (RGFW_extensionSupportedPlatform_OpenGL(sRGBARBstr, sizeof(sRGBARBstr)))
+ RGFW_attribStack_pushAttribs(&stack, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, hints->sRGB);
+ if (RGFW_extensionSupportedPlatform_OpenGL(sRGBEXTstr, sizeof(sRGBEXTstr)))
+ RGFW_attribStack_pushAttribs(&stack, GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, hints->sRGB);
+ }
+
+ RGFW_attribStack_pushAttribs(&stack, 0, 0);
+
+ /* find the configs */
+ i32 fbcount;
+ GLXFBConfig* fbc = glXChooseFBConfig(_RGFW->display, DefaultScreen(_RGFW->display), visual_attribs, &fbcount);
+
+ i32 best_fbc = -1;
+ i32 best_depth = 0;
+ i32 best_samples = 0;
+
+ if (fbcount == 0) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to find any valid GLX visual configs.");
+ return 0;
+ }
+
+ /* search through all found configs to find the best match */
+ i32 i;
+ for (i = 0; i < fbcount; i++) {
+ XVisualInfo* vi = glXGetVisualFromFBConfig(_RGFW->display, fbc[i]);
+ if (vi == NULL)
+ continue;
+
+ i32 samp_buf, samples;
+ glXGetFBConfigAttrib(_RGFW->display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
+ glXGetFBConfigAttrib(_RGFW->display, fbc[i], GLX_SAMPLES, &samples);
+
+ if (best_fbc == -1) best_fbc = i;
+ if ((!(transparent) || vi->depth == 32) && best_depth == 0) {
+ best_fbc = i;
+ best_depth = vi->depth;
+ }
+ if ((!(transparent) || vi->depth == 32) && samples <= hints->samples && samples > best_samples) {
+ best_fbc = i;
+ best_depth = vi->depth;
+ best_samples = samples;
+ }
+ XFree(vi);
+ }
+
+ if (best_fbc == -1) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to get a valid GLX visual.");
+ return 0;
+ }
+
+ /* we found a config */
+ bestFbc = fbc[best_fbc];
+ XVisualInfo* vi = glXGetVisualFromFBConfig(_RGFW->display, bestFbc);
+ if (vi->depth != 32 && transparent)
+ RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, "Failed to to find a matching visual with a 32-bit depth.");
+
+ if (best_samples < hints->samples)
+ RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, "Failed to load a matching sample count.");
+
+ XFree(fbc);
+ visual = *vi;
+ XFree(vi);
+
+ /* use the visual to create a new window */
+ RGFW_XCreateWindow(visual, "", win->internal.flags, win);
+
+ if (showWindow) {
+ RGFW_window_show(win);
+ }
+
+ /* create the actual OpenGL context */
+ i32 context_attribs[40];
+ RGFW_attribStack_init(&stack, context_attribs, 40);
+
+ i32 mask = 0;
+ switch (hints->profile) {
+ case RGFW_glES: mask |= GLX_CONTEXT_ES_PROFILE_BIT_EXT; break;
+ case RGFW_glForwardCompatibility: mask |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; break;
+ case RGFW_glCompatibility: mask |= GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; break;
+ case RGFW_glCore: mask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB; break;
+ default: mask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB; break;
+ }
+
+ RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_PROFILE_MASK_ARB, mask);
+
+ if (hints->minor || hints->major) {
+ RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_MAJOR_VERSION_ARB, hints->major);
+ RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_MINOR_VERSION_ARB, hints->minor);
+ }
+
+
+ if (RGFW_extensionSupportedPlatform_OpenGL(flushStr, sizeof(flushStr))) {
+ if (hints->releaseBehavior == RGFW_glReleaseFlush) {
+ RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB);
+ } else if (hints->releaseBehavior == RGFW_glReleaseNone) {
+ RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB);
+ }
+ }
+
+ i32 flags = 0;
+ if (hints->debug) flags |= GLX_CONTEXT_DEBUG_BIT_ARB;
+ if (hints->robustness && RGFW_extensionSupportedPlatform_OpenGL(robustStr, sizeof(robustStr))) flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB;
+ if (flags) {
+ RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_FLAGS_ARB, flags);
+ }
+
+ if (RGFW_extensionSupportedPlatform_OpenGL(noErorrStr, sizeof(noErorrStr))) {
+ RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_OPENGL_NO_ERROR_ARB, hints->noError);
+ }
+
+ RGFW_attribStack_pushAttribs(&stack, 0, 0);
+
+ /* create the context */
+ glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
+ char str[] = "glXCreateContextAttribsARB";
+ glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)glXGetProcAddressARB((u8*) str);
+
+ GLXContext ctx = NULL;
+ if (hints->share) {
+ ctx = hints->share->ctx;
+ }
+
+ if (glXCreateContextAttribsARB == NULL) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to load proc address 'glXCreateContextAttribsARB', loading a generic OpenGL context.");
+ win->src.ctx.native->ctx = glXCreateContext(_RGFW->display, &visual, ctx, True);
+ } else {
+ _RGFW->x11Error = NULL;
+ win->src.ctx.native->ctx = glXCreateContextAttribsARB(_RGFW->display, bestFbc, ctx, True, context_attribs);
+ if (_RGFW->x11Error || win->src.ctx.native->ctx == NULL) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to create an OpenGL context with AttribsARB, loading a generic OpenGL context.");
+ win->src.ctx.native->ctx = glXCreateContext(_RGFW->display, &visual, ctx, True);
+ }
+ }
+
+ #ifndef RGFW_NO_GLXWINDOW
+ win->src.ctx.native->window = glXCreateWindow(_RGFW->display, bestFbc, win->src.window, NULL);
+ #else
+ win->src.ctx.native->window = win->src.window;
+ #endif
+
+ glXMakeCurrent(_RGFW->display, (Drawable)win->src.ctx.native->window, (GLXContext)win->src.ctx.native->ctx);
+ RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context initalized.");
+
+ RGFW_window_swapInterval_OpenGL(win, 0);
+
+ return RGFW_TRUE;
+}
+
+void RGFW_FUNC(RGFW_window_deleteContextPtr_OpenGL) (RGFW_window* win, RGFW_glContext* ctx) {
+ #ifndef RGFW_NO_GLXWINDOW
+ if (win->src.ctx.native->window != win->src.window) {
+ glXDestroyWindow(_RGFW->display, win->src.ctx.native->window);
+ }
+ #endif
+
+ glXDestroyContext(_RGFW->display, ctx->ctx);
+ win->src.ctx.native = NULL;
+ RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context freed.");
+}
+
+RGFW_bool RGFW_FUNC(RGFW_extensionSupportedPlatform_OpenGL)(const char * extension, size_t len) {
+ RGFW_init();
+ const char* extensions = glXQueryExtensionsString(_RGFW->display, XDefaultScreen(_RGFW->display));
+ return (extensions != NULL) && RGFW_extensionSupportedStr(extensions, extension, len);
+}
+
+RGFW_proc RGFW_FUNC(RGFW_getProcAddress_OpenGL)(const char* procname) { return glXGetProcAddress((u8*) procname); }
+
+void RGFW_FUNC(RGFW_window_makeCurrentContext_OpenGL) (RGFW_window* win) { if (win) RGFW_ASSERT(win->src.ctx.native);
+ if (win == NULL)
+ glXMakeCurrent(NULL, (Drawable)NULL, (GLXContext) NULL);
+ else
+ glXMakeCurrent(_RGFW->display, (Drawable)win->src.ctx.native->window, (GLXContext) win->src.ctx.native->ctx);
+ return;
+}
+void* RGFW_FUNC(RGFW_getCurrentContext_OpenGL) (void) { return glXGetCurrentContext(); }
+void RGFW_FUNC(RGFW_window_swapBuffers_OpenGL) (RGFW_window* win) { RGFW_ASSERT(win->src.ctx.native); glXSwapBuffers(_RGFW->display, win->src.ctx.native->window); }
+
+void RGFW_FUNC(RGFW_window_swapInterval_OpenGL) (RGFW_window* win, i32 swapInterval) {
+ RGFW_ASSERT(win != NULL);
+ /* cached pfn to avoid calling glXGetProcAddress more than once */
+ static PFNGLXSWAPINTERVALEXTPROC pfn = NULL;
+ static int (*pfn2)(int) = NULL;
+
+ if (pfn == NULL) {
+ u8 str[] = "glXSwapIntervalEXT";
+ pfn = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress(str);
+ if (pfn == NULL) {
+ pfn = (PFNGLXSWAPINTERVALEXTPROC)1;
+ const char* array[] = {"GLX_MESA_swap_control", "GLX_SGI_swap_control"};
+
+ size_t i;
+ for (i = 0; i < sizeof(array) / sizeof(char*) && pfn2 == NULL; i++) {
+ pfn2 = (int(*)(int))glXGetProcAddress((u8*)array[i]);
+ }
+
+ if (pfn2 != NULL) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to load swap interval function, fallingback to the native swapinterval function");
+ } else {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to load swap interval function");
+ }
+ }
+ }
+
+ if (pfn != (PFNGLXSWAPINTERVALEXTPROC)1) {
+ pfn(_RGFW->display, win->src.ctx.native->window, swapInterval);
+ }
+ else if (pfn2 != NULL) {
+ pfn2(swapInterval);
+ }
+}
+#endif /* RGFW_OPENGL */
+
+i32 RGFW_initPlatform_X11(void) {
+ #ifdef RGFW_USE_XDL
+ XDL_init();
+ #endif
+
+ #if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD)
+ #if defined(__CYGWIN__)
+ RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor-1.so");
+ #elif defined(__OpenBSD__) || defined(__NetBSD__)
+ RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor.so");
+ #else
+ RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor.so.1");
+ #endif
+ RGFW_PROC_DEF(X11Cursorhandle, XcursorImageCreate);
+ RGFW_PROC_DEF(X11Cursorhandle, XcursorImageDestroy);
+ RGFW_PROC_DEF(X11Cursorhandle, XcursorImageLoadCursor);
+ #endif
+
+ #if !defined(RGFW_NO_X11_XI_PRELOAD)
+ #if defined(__CYGWIN__)
+ RGFW_LOAD_LIBRARY(X11Xihandle, "libXi-6.so");
+ #elif defined(__OpenBSD__) || defined(__NetBSD__)
+ RGFW_LOAD_LIBRARY(X11Xihandle, "libXi.so");
+ #else
+ RGFW_LOAD_LIBRARY(X11Xihandle, "libXi.so.6");
+ #endif
+ RGFW_PROC_DEF(X11Xihandle, XISelectEvents);
+ #endif
+
+ #if !defined(RGFW_NO_X11_EXT_PRELOAD)
+ #if defined(__CYGWIN__)
+ RGFW_LOAD_LIBRARY(X11XEXThandle, "libXext-6.so");
+ #elif defined(__OpenBSD__) || defined(__NetBSD__)
+ RGFW_LOAD_LIBRARY(X11XEXThandle, "libXext.so");
+ #else
+ RGFW_LOAD_LIBRARY(X11XEXThandle, "libXext.so.6");
+ #endif
+ RGFW_PROC_DEF(X11XEXThandle, XSyncCreateCounter);
+ RGFW_PROC_DEF(X11XEXThandle, XSyncIntToValue);
+ RGFW_PROC_DEF(X11XEXThandle, XSyncSetCounter);
+ RGFW_PROC_DEF(X11XEXThandle, XShapeCombineRegion);
+ RGFW_PROC_DEF(X11XEXThandle, XShapeCombineMask);
+ #endif
+
+ XInitThreads(); /*!< init X11 threading */
+ _RGFW->display = XOpenDisplay(0);
+ _RGFW->context = XUniqueContext();
+
+ XSetWindowAttributes wa;
+ RGFW_MEMSET(&wa, 0, sizeof(wa));
+ wa.event_mask = PropertyChangeMask;
+ _RGFW->helperWindow = XCreateWindow(_RGFW->display, XDefaultRootWindow(_RGFW->display), 0, 0, 1, 1, 0, 0,
+ InputOnly, DefaultVisual(_RGFW->display, DefaultScreen(_RGFW->display)), CWEventMask, &wa);
+
+ u8 RGFW_blk[] = { 0, 0, 0, 0 };
+ _RGFW->hiddenMouse = RGFW_loadMouse(RGFW_blk, 1, 1, RGFW_formatRGBA8);
+ _RGFW->clipboard = NULL;
+
+ XkbComponentNamesRec rec;
+ XkbDescPtr desc = XkbGetMap(_RGFW->display, 0, XkbUseCoreKbd);
+ XkbDescPtr evdesc;
+ XSetErrorHandler(RGFW_XErrorHandler);
+ u8 old[256];
+
+ XkbGetNames(_RGFW->display, XkbKeyNamesMask, desc);
+
+ RGFW_MEMSET(&rec, 0, sizeof(rec));
+ char evdev[] = "evdev";
+ rec.keycodes = evdev;
+ evdesc = XkbGetKeyboardByName(_RGFW->display, XkbUseCoreKbd, &rec, XkbGBN_KeyNamesMask, XkbGBN_KeyNamesMask, False);
+ /* memo: RGFW_keycodes[x11 keycode] = rgfw keycode */
+ if(evdesc != NULL && desc != NULL) {
+ int i, j;
+ for(i = 0; i < (int)sizeof(old); i++){
+ old[i] = _RGFW->keycodes[i];
+ _RGFW->keycodes[i] = 0;
+ }
+ for(i = evdesc->min_key_code; i <= evdesc->max_key_code; i++){
+ for(j = desc->min_key_code; j <= desc->max_key_code; j++){
+ if(RGFW_STRNCMP(evdesc->names->keys[i].name, desc->names->keys[j].name, XkbKeyNameLength) == 0){
+ _RGFW->keycodes[j] = old[i];
+ break;
+ }
+ }
+ }
+ XkbFreeKeyboard(desc, 0, True);
+ XkbFreeKeyboard(evdesc, 0, True);
+ }
+
+ XSetLocaleModifiers("");
+ XRegisterIMInstantiateCallback(_RGFW->display, NULL, NULL, NULL, RGFW_x11_imInitCallback, NULL);
+
+ unsigned char mask[XIMaskLen(XI_RawMotion)];
+ RGFW_MEMSET(mask, 0, sizeof(mask));
+ XISetMask(mask, XI_RawMotion);
+
+ XIEventMask em;
+ em.deviceid = XIAllMasterDevices;
+ em.mask_len = sizeof(mask);
+ em.mask = mask;
+
+ XISelectEvents(_RGFW->display, XDefaultRootWindow(_RGFW->display), &em, 1);
+
+#ifndef RGFW_NO_XRANDR
+ i32 errorBase;
+ if (XRRQueryExtension(_RGFW->display, &_RGFW->xrandrEventBase, &errorBase)) {
+ XRRSelectInput(_RGFW->display, RootWindow(_RGFW->display, DefaultScreen(_RGFW->display)), RROutputChangeNotifyMask);
+ }
+#endif
+
+ return 0;
+}
+
+void RGFW_deinitPlatform_X11(void) {
+ #define RGFW_FREE_LIBRARY(x) if (x != NULL) dlclose(x); x = NULL;
+ /* to save the clipboard on the x server after the window is closed */
+ RGFW_LOAD_ATOM(CLIPBOARD_MANAGER); RGFW_LOAD_ATOM(CLIPBOARD);
+ RGFW_LOAD_ATOM(SAVE_TARGETS);
+ if (XGetSelectionOwner(_RGFW->display, CLIPBOARD) == _RGFW->helperWindow) {
+ XConvertSelection(_RGFW->display, CLIPBOARD_MANAGER, SAVE_TARGETS, None, _RGFW->helperWindow, CurrentTime);
+ while (RGFW_XHandleClipboardSelectionHelper());
+ }
+
+ XUnregisterIMInstantiateCallback(_RGFW->display, NULL, NULL, NULL, RGFW_x11_imInitCallback, NULL);
+
+ if (_RGFW->im) {
+ XCloseIM(_RGFW->im);
+ _RGFW->im = NULL;
+ }
+
+ if (_RGFW->clipboard) {
+ RGFW_FREE(_RGFW->clipboard);
+ _RGFW->clipboard = NULL;
+ }
+
+ if (_RGFW->hiddenMouse) {
+ RGFW_freeMouse(_RGFW->hiddenMouse);
+ _RGFW->hiddenMouse = NULL;
+ }
+
+ XDestroyWindow(_RGFW->display, (Drawable) _RGFW->helperWindow); /*!< close the window */
+ XCloseDisplay(_RGFW->display); /*!< kill connection to the x server */
+
+ #if !defined(RGFW_NO_X11_CURSOR_PRELOAD) && !defined(RGFW_NO_X11_CURSOR)
+ RGFW_FREE_LIBRARY(X11Cursorhandle);
+ #endif
+ #if !defined(RGFW_NO_X11_XI_PRELOAD)
+ RGFW_FREE_LIBRARY(X11Xihandle);
+ #endif
+
+ #ifdef RGFW_USE_XDL
+ XDL_close();
+ #endif
+
+ #if !defined(RGFW_NO_X11_EXT_PRELOAD)
+ RGFW_FREE_LIBRARY(X11XEXThandle);
+ #endif
+}
+
+void RGFW_FUNC(RGFW_window_closePlatform)(RGFW_window* win) {
+ if (win->src.ic) {
+ XDestroyIC(win->src.ic);
+ win->src.ic = NULL;
+ }
+
+ XFreeGC(_RGFW->display, win->src.gc);
+ XDeleteContext(_RGFW->display, win->src.window, _RGFW->context);
+ XDestroyWindow(_RGFW->display, (Drawable) win->src.window); /*!< close the window */
+ return;
+}
+
+#ifdef RGFW_WEBGPU
+WGPUSurface RGFW_FUNC(RGFW_window_createSurface_WebGPU) (RGFW_window* window, WGPUInstance instance) {
+ WGPUSurfaceDescriptor surfaceDesc = {0};
+ WGPUSurfaceSourceXlibWindow fromXlib = {0};
+ fromXlib.chain.sType = WGPUSType_SurfaceSourceXlibWindow;
+ fromXlib.display = _RGFW->display;
+ fromXlib.window = window->src.window;
+
+ surfaceDesc.nextInChain = (WGPUChainedStruct*)&fromXlib.chain;
+ return wgpuInstanceCreateSurface(instance, &surfaceDesc);
+}
+#endif
+
+#endif
+/*
+ End of X11 linux / wayland / unix defines
+*/
+
+/*
+
+ Start of Wayland defayland
+*/
+
+#ifdef RGFW_WAYLAND
+#ifdef RGFW_X11
+#undef RGFW_FUNC /* remove previous define */
+#define RGFW_FUNC(func) func##_Wayland
+#else
+#define RGFW_FUNC(func) func
+#endif
+
+/*
+Wayland TODO: (out of date)
+- fix RGFW_keyPressed lock state
+
+ RGFW_windowMoved, the window was moved (by the user)
+ RGFW_windowRefresh The window content needs to be refreshed
+
+ RGFW_dataDrop a file has been dropped into the window
+ RGFW_dataDrag
+
+- window args:
+ #define RGFW_windowNoResize the window cannot be resized by the user
+ #define RGFW_windowAllowDND the window supports drag and drop
+ #define RGFW_scaleToMonitor scale the window to the screen
+
+- other missing functions functions ("TODO wayland") (~30 functions)
+- fix buffer rendering weird behavior
+*/
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+struct wl_display* RGFW_getDisplay_Wayland(void) { return _RGFW->wl_display; }
+struct wl_surface* RGFW_window_getWindow_Wayland(RGFW_window* win) { return win->src.surface; }
+
+
+/* wayland global garbage (wayland bad, X11 is fine (ish) (not really)) */
+#include "xdg-shell-client-protocol.h"
+#include "xdg-toplevel-icon-v1-client-protocol.h"
+#include "xdg-decoration-unstable-v1-client-protocol.h"
+#include "relative-pointer-unstable-v1-client-protocol.h"
+#include "pointer-constraints-unstable-v1-client-protocol.h"
+#include "xdg-output-unstable-v1-client-protocol.h"
+#include "pointer-warp-v1-client-protocol.h"
+
+void RGFW_toggleWaylandMaximized(RGFW_window* win, RGFW_bool maximized);
+
+static void RGFW_wl_setOpaque(RGFW_window* win) {
+ struct wl_region* wl_region = wl_compositor_create_region(_RGFW->compositor);
+
+ if (!wl_region) return; /* return if no region was created */
+
+ wl_region_add(wl_region, 0, 0, win->w, win->h);
+ wl_surface_set_opaque_region(win->src.surface, wl_region);
+ wl_region_destroy(wl_region);
+
+}
+
+static void RGFW_wl_xdg_wm_base_ping_handler(void* data, struct xdg_wm_base* wm_base,
+ u32 serial) {
+ RGFW_UNUSED(data);
+ xdg_wm_base_pong(wm_base, serial);
+}
+static void RGFW_wl_xdg_surface_configure_handler(void* data, struct xdg_surface* xdg_surface,
+ u32 serial) {
+
+ xdg_surface_ack_configure(xdg_surface, serial);
+
+ RGFW_window* win = (RGFW_window*)data;
+
+ if (win == NULL) {
+ win = _RGFW->kbOwner;
+ if (win == NULL)
+ return;
+ }
+
+ /* useful for libdecor */
+ if (win->src.activated != win->src.pending_activated) {
+ win->src.activated = win->src.pending_activated;
+ }
+
+ if (win->src.maximized != win->src.pending_maximized) {
+ RGFW_toggleWaylandMaximized(win, win->src.pending_maximized);
+
+ RGFW_window_checkMode(win);
+ }
+
+
+ if (win->src.resizing) {
+
+ RGFW_windowResizedCallback(win, win->w, win->h);
+ RGFW_window_resize(win, win->w, win->h);
+ if (!(win->internal.flags & RGFW_windowTransparent)) {
+ RGFW_wl_setOpaque(win);
+ }
+ }
+
+ win->src.configured = RGFW_TRUE;
+}
+
+static void RGFW_wl_xdg_toplevel_configure_handler(void* data, struct xdg_toplevel* toplevel,
+ i32 width, i32 height, struct wl_array* states) {
+
+ RGFW_UNUSED(toplevel);
+ RGFW_window* win = (RGFW_window*)data;
+
+
+ win->src.pending_activated = RGFW_FALSE;
+ win->src.pending_maximized = RGFW_FALSE;
+ win->src.resizing = RGFW_FALSE;
+
+
+ enum xdg_toplevel_state* state;
+ wl_array_for_each(state, states) {
+ switch (*state) {
+ case XDG_TOPLEVEL_STATE_ACTIVATED:
+ win->src.pending_activated = RGFW_TRUE;
+ break;
+ case XDG_TOPLEVEL_STATE_MAXIMIZED:
+ win->src.pending_maximized = RGFW_TRUE;
+ break;
+ default:
+ break;
+ }
+
+ }
+ /* if width and height are not zero and are not the same as the window */
+ /* the window is resizing so update the values */
+ if ((width && height) && (win->w != width || win->h != height)) {
+ win->src.resizing = RGFW_TRUE;
+ win->src.w = win->w = width;
+ win->src.h = win->h = height;
+ }
+}
+
+static void RGFW_wl_xdg_toplevel_close_handler(void* data, struct xdg_toplevel *toplevel) {
+ RGFW_UNUSED(toplevel);
+ RGFW_window* win = (RGFW_window*)data;
+
+ if (!win->internal.shouldClose) {
+ RGFW_windowQuitCallback(win);
+ }
+}
+
+static void RGFW_wl_xdg_decoration_configure_handler(void* data,
+ struct zxdg_toplevel_decoration_v1* zxdg_toplevel_decoration_v1, u32 mode) {
+ RGFW_window* win = (RGFW_window*)data; RGFW_UNUSED(zxdg_toplevel_decoration_v1);
+
+ /* this is expected to run once */
+ /* set the decoration mode set by earlier request */
+ if (mode != win->src.decoration_mode) {
+ win->src.decoration_mode = mode;
+ }
+}
+
+static void RGFW_wl_shm_format_handler(void* data, struct wl_shm *shm, u32 format) {
+ RGFW_UNUSED(data); RGFW_UNUSED(shm); RGFW_UNUSED(format);
+}
+
+static void RGFW_wl_relative_pointer_motion(void *data, struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1,
+ u32 time_hi, u32 time_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel) {
+
+ RGFW_UNUSED(zwp_relative_pointer_v1); RGFW_UNUSED(time_hi); RGFW_UNUSED(time_lo);
+ RGFW_UNUSED(dx_unaccel); RGFW_UNUSED(dy_unaccel);
+
+ RGFW_info* RGFW = (RGFW_info*)data;
+
+ RGFW_ASSERT(RGFW->mouseOwner != NULL);
+ RGFW_window* win = RGFW->mouseOwner;
+
+ RGFW_ASSERT(win);
+
+ float vecX = (float)wl_fixed_to_double(dx);
+ float vecY = (float)wl_fixed_to_double(dy);
+ RGFW_mousePosCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, vecX, vecY);
+}
+
+static void RGFW_wl_pointer_locked(void *data, struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1) {
+ RGFW_UNUSED(zwp_locked_pointer_v1);
+ RGFW_info* RGFW = (RGFW_info*)data;
+ wl_pointer_set_cursor(RGFW->wl_pointer, RGFW->mouse_enter_serial, NULL, 0, 0); /* draw no cursor */
+}
+
+static void RGFW_wl_pointer_enter(void* data, struct wl_pointer* pointer, u32 serial,
+ struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) {
+ RGFW_info* RGFW = (RGFW_info*)data;
+ RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface);
+
+ /* save when the pointer is locked or using default cursor */
+ RGFW->mouse_enter_serial = serial;
+ win->internal.mouseInside = RGFW_TRUE;
+ RGFW->windowState.mouseEnter = RGFW_TRUE;
+
+ RGFW->mouseOwner = win;
+
+ /* set the cursor */
+ if (win->src.using_custom_cursor) {
+ wl_pointer_set_cursor(pointer, serial, win->src.custom_cursor_surface, 0, 0);
+ }
+ else {
+ RGFW_window_setMouseDefault(win);
+ }
+
+ i32 x = (i32)wl_fixed_to_double(surface_x);
+ i32 y = (i32)wl_fixed_to_double(surface_y);
+ RGFW_mouseNotifyCallback(win, x, y, RGFW_TRUE);
+}
+
+static void RGFW_wl_pointer_leave(void* data, struct wl_pointer *pointer, u32 serial, struct wl_surface *surface) {
+ RGFW_UNUSED(pointer); RGFW_UNUSED(serial);
+ RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface);
+ RGFW_info* RGFW = (RGFW_info*)data;
+ if (RGFW->mouseOwner == win)
+ RGFW->mouseOwner = NULL;
+
+ RGFW_mouseNotifyCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, RGFW_FALSE);
+}
+
+static void RGFW_wl_pointer_motion(void* data, struct wl_pointer *pointer, u32 time, wl_fixed_t x, wl_fixed_t y) {
+ RGFW_UNUSED(pointer); RGFW_UNUSED(time);
+
+ RGFW_info* RGFW = (RGFW_info*)data;
+ RGFW_ASSERT(RGFW->mouseOwner != NULL);
+
+ RGFW_window* win = RGFW->mouseOwner;
+
+ i32 convertedX = (i32)wl_fixed_to_double(x);
+ i32 convertedY = (i32)wl_fixed_to_double(y);
+ float newVecX = (float)(convertedX - win->internal.lastMouseX);
+ float newVecY = (float)(convertedY - win->internal.lastMouseY);
+
+ RGFW_mousePosCallback(win, convertedX, convertedY, newVecX, newVecY);
+}
+
+static void RGFW_wl_pointer_button(void* data, struct wl_pointer *pointer, u32 serial, u32 time, u32 button, u32 state) {
+ RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(serial);
+ RGFW_info* RGFW = (RGFW_info*)data;
+
+ RGFW_ASSERT(RGFW->mouseOwner != NULL);
+ RGFW_window* win = RGFW->mouseOwner;
+
+ u32 b = (button - 0x110);
+
+ /* flip right and middle button codes */
+ if (b == 1) b = 2;
+ else if (b == 2) b = 1;
+
+ RGFW_mouseButtonCallback(win, (u8)b, RGFW_BOOL(state));
+}
+
+static void RGFW_wl_pointer_axis(void* data, struct wl_pointer *pointer, u32 time, u32 axis, wl_fixed_t value) {
+ RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(axis);
+
+ RGFW_info* RGFW = (RGFW_info*)data;
+ RGFW_ASSERT(RGFW->mouseOwner != NULL);
+ RGFW_window* win = RGFW->mouseOwner;
+
+ float scrollX = 0.0;
+ float scrollY = 0.0;
+
+ if (!(win->internal.enabledEvents & (RGFW_BIT(RGFW_mouseScroll)))) return;
+
+ if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
+ scrollX = (float)(-wl_fixed_to_double(value) / 10.0);
+ else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
+ scrollY = (float)(-wl_fixed_to_double(value) / 10.0);
+
+ RGFW_mouseScrollCallback(win, scrollX, scrollY);
+}
+
+
+static void RGFW_doNothing(void) { }
+
+static void RGFW_wl_keyboard_keymap(void* data, struct wl_keyboard *keyboard, u32 format, i32 fd, u32 size) {
+ RGFW_UNUSED(keyboard); RGFW_UNUSED(format);
+ RGFW_info* RGFW = (RGFW_info*)data;
+
+ char *keymap_string = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+ xkb_keymap_unref(RGFW->keymap);
+ RGFW->keymap = xkb_keymap_new_from_string(RGFW->xkb_context, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
+
+ munmap(keymap_string, size);
+ close(fd);
+ xkb_state_unref(RGFW->xkb_state);
+ RGFW->xkb_state = xkb_state_new(RGFW->keymap);
+
+ const char* locale = getenv("LC_ALL");
+ if (!locale)
+ locale = getenv("LC_CTYPE");
+ if (!locale)
+ locale = getenv("LANG");
+ if (!locale)
+ locale = "C";
+
+ struct xkb_compose_table* composeTable = xkb_compose_table_new_from_locale(RGFW->xkb_context, locale, XKB_COMPOSE_COMPILE_NO_FLAGS);
+ if (composeTable) {
+ RGFW->composeState = xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS);
+ xkb_compose_table_unref(composeTable);
+ }
+}
+
+static void RGFW_wl_keyboard_enter(void* data, struct wl_keyboard *keyboard, u32 serial, struct wl_surface *surface, struct wl_array *keys) {
+ RGFW_UNUSED(keyboard); RGFW_UNUSED(keys);
+
+ RGFW_info* RGFW = (RGFW_info*)data;
+ RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface);
+ RGFW->kbOwner = win;
+
+
+ // this is to prevent race conditions
+ if (RGFW->data_device != NULL && win->src.data_source != NULL) {
+ wl_data_device_set_selection(RGFW->data_device, win->src.data_source, serial);
+ }
+ /* is set when RGFW_window_minimize is called; if the minimize button is */
+ /* pressed this flag is not set since there is no event to listen for */
+ if (win->src.minimized == RGFW_TRUE) win->src.minimized = RGFW_FALSE;
+
+ RGFW_focusCallback(win, RGFW_TRUE);
+}
+
+static void RGFW_wl_keyboard_leave(void* data, struct wl_keyboard *keyboard, u32 serial, struct wl_surface *surface) {
+ RGFW_UNUSED(keyboard); RGFW_UNUSED(serial);
+
+ RGFW_info* RGFW = (RGFW_info*)data;
+ RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface);
+ if (RGFW->kbOwner == win)
+ RGFW->kbOwner = NULL;
+
+ RGFW_focusCallback(win, RGFW_FALSE);
+}
+
+static xkb_keysym_t RGFW_wl_composeSymbol(RGFW_info* RGFW, xkb_keysym_t sym) {
+ if (sym == XKB_KEY_NoSymbol || !RGFW->composeState)
+ return sym;
+ if (xkb_compose_state_feed(RGFW->composeState, sym) != XKB_COMPOSE_FEED_ACCEPTED)
+ return sym;
+ switch (xkb_compose_state_get_status(RGFW->composeState)) {
+ case XKB_COMPOSE_COMPOSED:
+ return xkb_compose_state_get_one_sym(RGFW->composeState);
+ case XKB_COMPOSE_COMPOSING:
+ case XKB_COMPOSE_CANCELLED:
+ return XKB_KEY_NoSymbol;
+ case XKB_COMPOSE_NOTHING:
+ default:
+ return sym;
+ }
+}
+
+static void RGFW_wl_keyboard_key(void* data, struct wl_keyboard *keyboard, u32 serial, u32 time, u32 key, u32 state) {
+ RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time);
+
+ RGFW_info* RGFW = (RGFW_info*)data;
+ if (RGFW->kbOwner == NULL) return;
+
+ RGFW_window *RGFW_key_win = RGFW->kbOwner;
+ RGFW_key RGFWkey = RGFW_apiKeyToRGFW(key + 8);
+
+ RGFW_updateKeyMods(RGFW_key_win, RGFW_BOOL(xkb_keymap_mod_get_index(RGFW->keymap, "Lock")), RGFW_BOOL(xkb_keymap_mod_get_index(RGFW->keymap, "Mod2")), RGFW_BOOL(xkb_keymap_mod_get_index(RGFW->keymap, "ScrollLock")));
+ RGFW_keyCallback(RGFW_key_win, (u8)RGFWkey, RGFW_key_win->internal.mod, RGFW_window_isKeyDown(RGFW_key_win, (u8)RGFWkey), RGFW_BOOL(state));
+
+ const xkb_keysym_t* keysyms;
+ if (xkb_state_key_get_syms(RGFW->xkb_state, key + 8, &keysyms) == 1) {
+ xkb_keysym_t keysym = RGFW_wl_composeSymbol(RGFW, keysyms[0]);
+ u32 codepoint = xkb_keysym_to_utf32(keysym);
+ if (codepoint != 0) {
+ RGFW_keyCharCallback(RGFW_key_win, codepoint);
+ }
+ }
+}
+
+static void RGFW_wl_keyboard_modifiers(void* data, struct wl_keyboard *keyboard, u32 serial, u32 mods_depressed, u32 mods_latched, u32 mods_locked, u32 group) {
+ RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time);
+ RGFW_info* RGFW = (RGFW_info*)data;
+ xkb_state_update_mask(RGFW->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
+}
+
+static void RGFW_wl_seat_capabilities(void* data, struct wl_seat *seat, u32 capabilities) {
+ RGFW_info* RGFW = (RGFW_info*)data;
+ static struct wl_pointer_listener pointer_listener;
+ RGFW_MEMSET(&pointer_listener, 0, sizeof(pointer_listener));
+ pointer_listener.enter = &RGFW_wl_pointer_enter;
+ pointer_listener.leave = &RGFW_wl_pointer_leave;
+ pointer_listener.motion = &RGFW_wl_pointer_motion;
+ pointer_listener.button = &RGFW_wl_pointer_button;
+ pointer_listener.axis = &RGFW_wl_pointer_axis;
+
+ static struct wl_keyboard_listener keyboard_listener;
+ RGFW_MEMSET(&keyboard_listener, 0, sizeof(keyboard_listener));
+ keyboard_listener.keymap = &RGFW_wl_keyboard_keymap;
+ keyboard_listener.enter = &RGFW_wl_keyboard_enter;
+ keyboard_listener.leave = &RGFW_wl_keyboard_leave;
+ keyboard_listener.key = &RGFW_wl_keyboard_key;
+ keyboard_listener.modifiers = &RGFW_wl_keyboard_modifiers;
+
+ if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !RGFW->wl_pointer) {
+ RGFW->wl_pointer = wl_seat_get_pointer(seat);
+ wl_pointer_add_listener(RGFW->wl_pointer, &pointer_listener, RGFW);
+ }
+ if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && !RGFW->wl_keyboard) {
+ RGFW->wl_keyboard = wl_seat_get_keyboard(seat);
+ wl_keyboard_add_listener(RGFW->wl_keyboard, &keyboard_listener, RGFW);
+ }
+
+ if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && RGFW->wl_pointer) {
+ wl_pointer_destroy(RGFW->wl_pointer);
+ }
+ if (!(capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && RGFW->wl_keyboard) {
+ wl_keyboard_destroy(RGFW->wl_keyboard);
+ }
+}
+
+static void RGFW_wl_output_set_geometry(void *data, struct wl_output *wl_output,
+ i32 x, i32 y, i32 physical_width, i32 physical_height,
+ i32 subpixel, const char *make, const char *model, i32 transform) {
+
+ RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon;
+ monitor->x = x;
+ monitor->y = y;
+
+ monitor->physW = (float)physical_width / 25.4f;
+ monitor->physH = (float)physical_height / 25.4f;
+
+ RGFW_UNUSED(wl_output);
+ RGFW_UNUSED(subpixel);
+ RGFW_UNUSED(make);
+ RGFW_UNUSED(model);
+ RGFW_UNUSED(transform);
+}
+
+static void RGFW_wl_output_handle_mode(void *data, struct wl_output *wl_output, u32 flags,
+ i32 width, i32 height, i32 refresh) {
+
+ RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon;
+
+ RGFW_monitorMode mode;
+ mode.w = width;
+ mode.h = height;
+ mode.refreshRate = (float)refresh / 1000.0f;
+ mode.src = wl_output;
+
+ monitor->node->modeCount += 1;
+
+ RGFW_monitorMode* modes = (RGFW_monitorMode*)RGFW_ALLOC(monitor->node->modeCount * sizeof(RGFW_monitorMode));
+
+ if (monitor->node->modeCount > 1) {
+ RGFW_monitor_getModesPtr(monitor, &modes);
+ RGFW_FREE(monitor->node->modes);
+ }
+
+ modes[monitor->node->modeCount - 1] = mode;
+ monitor->node->modes = modes;
+
+ if (flags & WL_OUTPUT_MODE_CURRENT) {
+ monitor->mode = mode;
+ } else {
+ }
+}
+
+static void RGFW_wl_output_set_scale(void *data, struct wl_output *wl_output, i32 factor) {
+ RGFW_UNUSED(wl_output);
+ RGFW_monitor* mon = &((RGFW_monitorNode*)data)->mon;
+
+ mon->scaleX = (float)factor;
+ mon->scaleY = (float)factor;
+}
+
+static void RGFW_wl_output_set_name(void *data, struct wl_output *wl_output, const char *name) {
+ RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon;
+
+ RGFW_STRNCPY(monitor->name, name, sizeof(monitor->name) - 1);
+ monitor->name[sizeof(monitor->name) - 1] = '\0';
+
+ RGFW_UNUSED(wl_output);
+
+}
+
+static void RGFW_xdg_output_logical_pos(void *data, struct zxdg_output_v1 *zxdg_output_v1, i32 x, i32 y) {
+ RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon;
+ monitor->x = x;
+ monitor->y = y;
+ RGFW_UNUSED(zxdg_output_v1);
+}
+
+static void RGFW_xdg_output_logical_size(void *data, struct zxdg_output_v1 *zxdg_output_v1, i32 width, i32 height) {
+ RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon;
+
+ float mon_float_width = (float) monitor->mode.w;
+ float mon_float_height = (float) monitor->mode.h;
+
+ float scaleX = (mon_float_width / (float) width);
+ float scaleY = (mon_float_height / (float) height);
+ RGFW_UNUSED(scaleY);
+
+ float dpi = scaleX * 96.0f;
+
+ monitor->pixelRatio = dpi >= 192.0f ? 2.0f : 1.0f;
+
+ /* under xwayland the monitor changes w & h when compositor scales it */
+ monitor->mode.w = width;
+ monitor->mode.h = height;
+ RGFW_UNUSED(zxdg_output_v1);
+}
+
+
+static void RGFW_wl_output_handle_done(void* data, struct wl_output* output) {
+ RGFW_UNUSED(output);
+
+ RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon;
+
+ if (monitor->physW <= 0 || monitor->physH <= 0) {
+ monitor->physW = (i32) ((float)monitor->mode.w / 96.0f);
+ monitor->physH = (i32) ((float)monitor->mode.h / 96.0f);
+ }
+
+ if (((RGFW_monitorNode*)data)->disconnected == RGFW_FALSE) {
+ return;
+ }
+
+ ((RGFW_monitorNode*)data)->disconnected = RGFW_TRUE;
+
+ RGFW_monitorCallback(_RGFW->root, monitor, RGFW_TRUE);
+}
+
+static void RGFW_wl_create_outputs(struct wl_registry *const registry, u32 id) {
+ struct wl_output *output = wl_registry_bind(registry, id, &wl_output_interface, wl_display_get_version(_RGFW->wl_display) < 4 ? 3 : 4);
+ RGFW_monitorNode* node;
+ RGFW_monitor mon;
+
+ if (!output) return;
+
+ char RGFW_mon_default_name[10];
+
+ RGFW_SNPRINTF(RGFW_mon_default_name, sizeof(RGFW_mon_default_name), "monitor-%li", _RGFW->monitors.count);
+ RGFW_STRNCPY(mon.name, RGFW_mon_default_name, sizeof(mon.name) - 1);
+ mon.name[sizeof(mon.name) - 1] = '\0';
+
+ /* set in case compositor does not send one */
+ /* or no xdg_output support */
+ mon.scaleY = mon.scaleX = mon.pixelRatio = 1.0f;
+
+ node = RGFW_monitors_add(&mon);
+ if (node == NULL) return;
+
+ node->modeCount = 0;
+ node->disconnected = RGFW_TRUE;
+ node->id = id;
+ node->output = output;
+
+ static const struct wl_output_listener wl_output_listener = {
+ .geometry = RGFW_wl_output_set_geometry,
+ .mode = RGFW_wl_output_handle_mode,
+ .done = RGFW_wl_output_handle_done,
+ .scale = RGFW_wl_output_set_scale,
+ .name = RGFW_wl_output_set_name,
+ .description = (void (*)(void *, struct wl_output *, const char *))&RGFW_doNothing
+ };
+
+ /* the wl_output will have a reference to the node */
+ wl_output_set_user_data(output, node);
+
+ /* pass the monitor so we can access it in the callback functions */
+ wl_output_add_listener(output, &wl_output_listener, node);
+
+ if (!_RGFW->xdg_output_manager)
+ return; /* compositor does not support it */
+
+ static const struct zxdg_output_v1_listener xdg_output_listener = {
+ .name = (void (*)(void *,struct zxdg_output_v1 *, const char *))&RGFW_doNothing,
+ .done = (void (*)(void *,struct zxdg_output_v1 *))&RGFW_doNothing,
+ .description = (void (*)(void *,struct zxdg_output_v1 *, const char *))&RGFW_doNothing,
+ .logical_position = RGFW_xdg_output_logical_pos,
+ .logical_size = RGFW_xdg_output_logical_size
+ };
+
+ node->xdg_output = zxdg_output_manager_v1_get_xdg_output(_RGFW->xdg_output_manager, node->output);
+ zxdg_output_v1_add_listener(node->xdg_output, &xdg_output_listener, node);
+}
+
+static void RGFW_wl_surface_enter(void *data, struct wl_surface *wl_surface, struct wl_output *output) {
+ RGFW_UNUSED(wl_surface);
+
+ RGFW_window* win = (RGFW_window*)data;
+ RGFW_monitorNode* node = wl_output_get_user_data(output);
+ if (node == NULL) return;
+
+ win->src.active_monitor = node;
+
+ if (win->internal.flags & RGFW_windowScaleToMonitor)
+ RGFW_window_scaleToMonitor(win);
+}
+
+static void RGFW_wl_data_source_send(void *data, struct wl_data_source *wl_data_source, const char *mime_type, i32 fd) {
+ RGFW_UNUSED(data); RGFW_UNUSED(wl_data_source);
+
+ // a client can accept our clipboard
+ if (RGFW_STRNCMP(mime_type, "text/plain;charset=utf-8", 25) == 0) {
+ // do not write \0
+ write(fd, _RGFW->clipboard, _RGFW->clipboard_len - 1);
+ }
+
+ close(fd);
+}
+
+static void RGFW_wl_data_source_cancelled(void *data, struct wl_data_source *wl_data_source) {
+
+ RGFW_info* RGFW = (RGFW_info*)data;
+
+ if (RGFW->kbOwner->src.data_source == wl_data_source) {
+ RGFW->kbOwner->src.data_source = NULL;
+ }
+
+ wl_data_source_destroy(wl_data_source);
+
+}
+
+static void RGFW_wl_data_device_data_offer(void *data, struct wl_data_device *wl_data_device, struct wl_data_offer *wl_data_offer) {
+
+ RGFW_UNUSED(data); RGFW_UNUSED(wl_data_device);
+ static const struct wl_data_offer_listener wl_data_offer_listener = {
+ .offer = (void (*)(void *data, struct wl_data_offer *wl_data_offer, const char *))RGFW_doNothing,
+ .source_actions = (void (*)(void *data, struct wl_data_offer *wl_data_offer, u32 dnd_action))RGFW_doNothing,
+ .action = (void (*)(void *data, struct wl_data_offer *wl_data_offer, u32 dnd_action))RGFW_doNothing
+ };
+ wl_data_offer_add_listener(wl_data_offer, &wl_data_offer_listener, NULL);
+}
+
+static void RGFW_wl_data_device_selection(void *data, struct wl_data_device *wl_data_device, struct wl_data_offer *wl_data_offer) {
+ RGFW_UNUSED(data); RGFW_UNUSED(wl_data_device);
+ /* Clipboard is empty */
+ if (wl_data_offer == NULL) {
+ return;
+ }
+
+ int pfds[2];
+ pipe(pfds);
+
+ wl_data_offer_receive(wl_data_offer, "text/plain;charset=utf-8", pfds[1]);
+ close(pfds[1]);
+
+ wl_display_roundtrip(_RGFW->wl_display);
+
+ char buf[1024];
+
+ ssize_t n = read(pfds[0], buf, sizeof(buf));
+
+ _RGFW->clipboard = (char*)RGFW_ALLOC((size_t)n);
+ RGFW_ASSERT(_RGFW->clipboard != NULL);
+ RGFW_STRNCPY(_RGFW->clipboard, buf, (size_t)n);
+
+ _RGFW->clipboard_len = (size_t)n + 1;
+
+ close(pfds[0]);
+
+ wl_data_offer_destroy(wl_data_offer);
+
+}
+
+static void RGFW_wl_global_registry_handler(void* data, struct wl_registry *registry, u32 id, const char *interface, u32 version) {
+
+ static struct wl_seat_listener seat_listener = {&RGFW_wl_seat_capabilities, (void (*)(void *, struct wl_seat *, const char *))&RGFW_doNothing};
+ static const struct wl_shm_listener shm_listener = { .format = RGFW_wl_shm_format_handler };
+
+ RGFW_info* RGFW = (RGFW_info*)data;
+ RGFW_UNUSED(version);
+
+ if (RGFW_STRNCMP(interface, "wl_compositor", 16) == 0) {
+ RGFW->compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 4);
+ } else if (RGFW_STRNCMP(interface, "xdg_wm_base", 12) == 0) {
+ RGFW->xdg_wm_base = wl_registry_bind(registry, id, &xdg_wm_base_interface, 1);
+ } else if (RGFW_STRNCMP(interface, zxdg_decoration_manager_v1_interface.name, 255) == 0) {
+ RGFW->decoration_manager = wl_registry_bind(registry, id, &zxdg_decoration_manager_v1_interface, 1);
+ } else if (RGFW_STRNCMP(interface, zwp_pointer_constraints_v1_interface.name, 255) == 0) {
+ RGFW->constraint_manager = wl_registry_bind(registry, id, &zwp_pointer_constraints_v1_interface, 1);
+ } else if (RGFW_STRNCMP(interface, zwp_relative_pointer_manager_v1_interface.name, 255) == 0) {
+ RGFW->relative_pointer_manager = wl_registry_bind(registry, id, &zwp_relative_pointer_manager_v1_interface, 1);
+ } else if (RGFW_STRNCMP(interface, xdg_toplevel_icon_manager_v1_interface.name, 255) == 0) {
+ RGFW->icon_manager = wl_registry_bind(registry, id, &xdg_toplevel_icon_manager_v1_interface, 1);
+ } else if (RGFW_STRNCMP(interface, "wl_shm", 7) == 0) {
+ RGFW->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
+ wl_shm_add_listener(RGFW->shm, &shm_listener, RGFW);
+ } else if (RGFW_STRNCMP(interface,"wl_seat", 8) == 0) {
+ RGFW->seat = wl_registry_bind(registry, id, &wl_seat_interface, 1);
+ wl_seat_add_listener(RGFW->seat, &seat_listener, RGFW);
+ } else if (RGFW_STRNCMP(interface, zxdg_output_manager_v1_interface.name, 255) == 0) {
+ RGFW->xdg_output_manager = wl_registry_bind(registry, id, &zxdg_output_manager_v1_interface, 1);
+ } else if (RGFW_STRNCMP(interface,"wl_output", 10) == 0) {
+ RGFW_wl_create_outputs(registry, id);
+ } else if (RGFW_STRNCMP(interface, wp_pointer_warp_v1_interface.name, 255) == 0) {
+ RGFW->wp_pointer_warp = wl_registry_bind(registry, id, &wp_pointer_warp_v1_interface, 1);
+ } else if (RGFW_STRNCMP(interface,"wl_data_device_manager", 23) == 0) {
+ RGFW->data_device_manager = wl_registry_bind(registry, id, &wl_data_device_manager_interface, 1);
+ }
+}
+
+static void RGFW_wl_global_registry_remove(void* data, struct wl_registry *registry, u32 id) {
+ RGFW_UNUSED(data); RGFW_UNUSED(registry);
+ RGFW_info* RGFW = (RGFW_info*)data;
+ RGFW_monitorNode* prev = RGFW->monitors.list.head;
+ RGFW_monitorNode* node = NULL;
+ if (prev == NULL) return;
+
+ if (prev->id != id) {
+ /* find the first node that has a matching id */
+ while(prev->next != NULL && prev->next->id != id) {
+ prev = prev->next;
+ }
+
+ if (prev->next == NULL) return;
+ node = prev->next;
+ } else {
+ node = prev;
+ }
+
+ if (node->output) {
+ wl_output_destroy(node->output);
+ }
+
+ if (node->xdg_output) {
+ zxdg_output_v1_destroy(node->xdg_output);
+ }
+
+ if (node->modeCount) {
+ RGFW_FREE(node->modes);
+ node->modeCount = 0;
+ }
+
+ RGFW_monitorCallback(_RGFW->root, &node->mon, RGFW_FALSE);
+ RGFW_monitors_remove(node, prev);
+}
+
+static void RGFW_wl_randname(char *buf) {
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ long r = ts.tv_nsec;
+
+ int i;
+ for (i = 0; i < 6; i++) {
+ buf[i] = (char)('A'+(r&15)+(r&16)*2);
+ r >>= 5;
+ }
+}
+
+static int RGFW_wl_anonymous_shm_open(void) {
+ char name[] = "/RGFW-wayland-XXXXXX";
+ int retries = 100;
+
+ do {
+ RGFW_wl_randname(name + RGFW_unix_stringlen(name) - 6);
+
+ --retries;
+ /* shm_open guarantees that O_CLOEXEC is set */
+ int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
+ if (fd >= 0) {
+ shm_unlink(name);
+ return fd;
+ }
+ } while (retries > 0 && errno == EEXIST);
+
+ return -1;
+}
+
+static int RGFW_wl_create_shm_file(off_t size) {
+ int fd = RGFW_wl_anonymous_shm_open();
+ if (fd < 0) {
+ return fd;
+ }
+
+ if (ftruncate(fd, size) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+i32 RGFW_initPlatform_Wayland(void) {
+ _RGFW->wl_display = wl_display_connect(NULL);
+ if (_RGFW->wl_display == NULL) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errWayland, "Failed to load Wayland display");
+ return -1;
+ }
+
+ _RGFW->compositor = NULL;
+ static const struct wl_registry_listener registry_listener = {
+ .global = RGFW_wl_global_registry_handler,
+ .global_remove = RGFW_wl_global_registry_remove,
+ };
+
+ _RGFW->registry = wl_display_get_registry(_RGFW->wl_display);
+ wl_registry_add_listener(_RGFW->registry, ®istry_listener, _RGFW);
+
+ wl_display_roundtrip(_RGFW->wl_display); /* bind to globals */
+
+ if (_RGFW->compositor == NULL) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errWayland, "Can't find compositor.");
+ return 1;
+ }
+
+ if (_RGFW->wl_cursor_theme == NULL) {
+ _RGFW->wl_cursor_theme = wl_cursor_theme_load(NULL, 24, _RGFW->shm);
+ _RGFW->cursor_surface = wl_compositor_create_surface(_RGFW->compositor);
+ }
+
+ u8 RGFW_blk[] = { 0, 0, 0, 0 };
+ _RGFW->hiddenMouse = RGFW_loadMouse(RGFW_blk, 1, 1, RGFW_formatRGBA8);
+
+ static const struct xdg_wm_base_listener xdg_wm_base_listener = {
+ .ping = RGFW_wl_xdg_wm_base_ping_handler,
+ };
+
+ xdg_wm_base_add_listener(_RGFW->xdg_wm_base, &xdg_wm_base_listener, NULL);
+
+ _RGFW->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+
+ static const struct wl_data_device_listener wl_data_device_listener = {
+ .data_offer = RGFW_wl_data_device_data_offer,
+ .enter = (void (*)(void *, struct wl_data_device *, u32, struct wl_surface*, wl_fixed_t, wl_fixed_t, struct wl_data_offer *))&RGFW_doNothing,
+ .leave = (void (*)(void *, struct wl_data_device *))&RGFW_doNothing,
+ .motion = (void (*)(void *, struct wl_data_device *, u32, wl_fixed_t, wl_fixed_t))&RGFW_doNothing,
+ .drop = (void (*)(void *, struct wl_data_device *))&RGFW_doNothing,
+ .selection = RGFW_wl_data_device_selection
+ };
+
+ if (_RGFW->seat && _RGFW->data_device_manager) {
+ _RGFW->data_device = wl_data_device_manager_get_data_device(_RGFW->data_device_manager, _RGFW->seat);
+ wl_data_device_add_listener(_RGFW->data_device, &wl_data_device_listener, NULL);
+ }
+
+ return 0;
+}
+
+void RGFW_deinitPlatform_Wayland(void) {
+ if (_RGFW->clipboard) {
+ RGFW_FREE(_RGFW->clipboard);
+ _RGFW->clipboard = NULL;
+ }
+
+ if (_RGFW->wl_pointer) {
+ wl_pointer_destroy(_RGFW->wl_pointer);
+ }
+ if (_RGFW->wl_keyboard) {
+ wl_keyboard_destroy(_RGFW->wl_keyboard);
+ }
+
+ wl_registry_destroy(_RGFW->registry);
+ if (_RGFW->decoration_manager != NULL)
+ zxdg_decoration_manager_v1_destroy(_RGFW->decoration_manager);
+ if (_RGFW->relative_pointer_manager != NULL) {
+ zwp_relative_pointer_manager_v1_destroy(_RGFW->relative_pointer_manager);
+ }
+
+ if (_RGFW->relative_pointer) {
+ zwp_relative_pointer_v1_destroy(_RGFW->relative_pointer);
+ }
+
+ if (_RGFW->constraint_manager != NULL) {
+ zwp_pointer_constraints_v1_destroy(_RGFW->constraint_manager);
+ }
+
+ if (_RGFW->xdg_output_manager != NULL)
+ if (_RGFW->icon_manager != NULL) {
+ xdg_toplevel_icon_manager_v1_destroy(_RGFW->icon_manager);
+ }
+
+ if (_RGFW->xdg_output_manager) {
+ zxdg_output_manager_v1_destroy(_RGFW->xdg_output_manager);
+ }
+
+ if (_RGFW->data_device_manager) {
+ wl_data_device_manager_destroy(_RGFW->data_device_manager);
+ }
+
+ if (_RGFW->data_device) {
+ wl_data_device_destroy(_RGFW->data_device);
+ }
+
+ if (_RGFW->wl_cursor_theme != NULL) {
+ wl_cursor_theme_destroy(_RGFW->wl_cursor_theme);
+ }
+
+ if (_RGFW->wp_pointer_warp != NULL) {
+ wp_pointer_warp_v1_destroy(_RGFW->wp_pointer_warp);
+ }
+
+ RGFW_freeMouse(_RGFW->hiddenMouse);
+
+ RGFW_monitorNode* node = _RGFW->monitors.list.head;
+
+ while (node != NULL) {
+ if (node->output) {
+ wl_output_destroy(node->output);
+ }
+
+ if (node->xdg_output) {
+ zxdg_output_v1_destroy(node->xdg_output);
+ }
+
+ _RGFW->monitors.count -= 1;
+ node = node->next;
+
+ }
+
+ wl_surface_destroy(_RGFW->cursor_surface);
+ wl_shm_destroy(_RGFW->shm);
+ wl_seat_release(_RGFW->seat);
+ xdg_wm_base_destroy(_RGFW->xdg_wm_base);
+ wl_compositor_destroy(_RGFW->compositor);
+ wl_display_disconnect(_RGFW->wl_display);
+}
+
+RGFW_format RGFW_FUNC(RGFW_nativeFormat)(void) { return RGFW_formatBGRA8; }
+
+RGFW_bool RGFW_FUNC(RGFW_createSurfacePtr) (u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) {
+ RGFW_ASSERT(surface != NULL);
+ surface->data = data;
+ surface->w = w;
+ surface->h = h;
+ surface->format = format;
+ RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoBuffer, "Creating a 4 channel buffer");
+
+ u32 size = (u32)(surface->w * surface->h * 4);
+ int fd = RGFW_wl_create_shm_file(size);
+ if (fd < 0) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, "Failed to create a buffer.");
+ return RGFW_FALSE;
+ }
+
+ surface->native.pool = wl_shm_create_pool(_RGFW->shm, fd, (i32)size);
+
+ surface->native.buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (surface->native.buffer == MAP_FAILED) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, "mmap failed.");
+ return RGFW_FALSE;
+ }
+
+ surface->native.fd = fd;
+ surface->native.format = RGFW_formatBGRA8;
+ return RGFW_TRUE;
+}
+
+void RGFW_FUNC(RGFW_window_blitSurface) (RGFW_window* win, RGFW_surface* surface) {
+ RGFW_ASSERT(surface != NULL);
+
+ surface->native.wl_buffer = wl_shm_pool_create_buffer(surface->native.pool, 0, RGFW_MIN(win->w, surface->w), RGFW_MIN(win->h, surface->h), (i32)surface->w * 4, WL_SHM_FORMAT_ARGB8888);
+ RGFW_copyImageData(surface->native.buffer, surface->w, RGFW_MIN(win->h, surface->h), surface->native.format, surface->data, surface->format, surface->convertFunc);
+
+ wl_surface_attach(win->src.surface, surface->native.wl_buffer, 0, 0);
+ wl_surface_damage(win->src.surface, 0, 0, RGFW_MIN(win->w, surface->w), RGFW_MIN(win->h, surface->h));
+ wl_surface_commit(win->src.surface);
+
+ wl_buffer_destroy(surface->native.wl_buffer);
+}
+
+void RGFW_FUNC(RGFW_surface_freePtr) (RGFW_surface* surface) {
+ RGFW_ASSERT(surface != NULL);
+
+ wl_shm_pool_destroy(surface->native.pool);
+ close(surface->native.fd);
+
+ munmap(surface->native.buffer, (size_t)(surface->w * surface->h * 4));
+}
+
+void RGFW_FUNC(RGFW_window_setBorder) (RGFW_window* win, RGFW_bool border) {
+ RGFW_setBit(&win->internal.flags, RGFW_windowNoBorder, !border);
+
+ /* for now just toggle between SSD & CSD depending on the bool */
+ if (_RGFW->decoration_manager != NULL) {
+ zxdg_toplevel_decoration_v1_set_mode(win->src.decoration, (border ? ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE : ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE));
+ }
+}
+
+void RGFW_FUNC(RGFW_window_setRawMouseModePlatform) (RGFW_window* win, RGFW_bool state) {
+ RGFW_ASSERT(win);
+ if (_RGFW->relative_pointer_manager == NULL) return;
+
+ if (state == RGFW_FALSE) {
+ if (_RGFW->relative_pointer != NULL)
+ zwp_relative_pointer_v1_destroy(_RGFW->relative_pointer);
+ _RGFW->relative_pointer = NULL;
+ return;
+ }
+
+ if (_RGFW->relative_pointer != NULL) return;
+
+ _RGFW->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(_RGFW->relative_pointer_manager, _RGFW->wl_pointer);
+
+ static const struct zwp_relative_pointer_v1_listener relative_motion_listener = {
+ .relative_motion = RGFW_wl_relative_pointer_motion
+ };
+
+ zwp_relative_pointer_v1_add_listener(_RGFW->relative_pointer, &relative_motion_listener, _RGFW);
+}
+
+void RGFW_FUNC(RGFW_window_captureMousePlatform) (RGFW_window* win, RGFW_bool state) {
+ RGFW_ASSERT(win);
+
+ /* compositor has no support or window already is locked do nothing */
+ if (_RGFW->constraint_manager == NULL) return;
+
+ if (state == RGFW_FALSE) {
+ if (win->src.locked_pointer != NULL)
+ zwp_locked_pointer_v1_destroy(win->src.locked_pointer);
+ win->src.locked_pointer = NULL;
+ return;
+ }
+
+
+ if (win->src.locked_pointer != NULL) return;
+ win->src.locked_pointer = zwp_pointer_constraints_v1_lock_pointer(_RGFW->constraint_manager, win->src.surface, _RGFW->wl_pointer, NULL, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
+
+ static const struct zwp_locked_pointer_v1_listener locked_listener = {
+ .locked = RGFW_wl_pointer_locked,
+ .unlocked = (void (*)(void *, struct zwp_locked_pointer_v1 *))RGFW_doNothing
+ };
+
+ zwp_locked_pointer_v1_add_listener(win->src.locked_pointer, &locked_listener, _RGFW);
+}
+
+RGFW_window* RGFW_FUNC(RGFW_createWindowPlatform) (const char* name, RGFW_windowFlags flags, RGFW_window* win) {
+ RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningWayland, "RGFW Wayland support is experimental");
+
+ static const struct xdg_surface_listener xdg_surface_listener = {
+ .configure = RGFW_wl_xdg_surface_configure_handler,
+ };
+
+ static const struct wl_surface_listener wl_surface_listener = {
+ .enter = RGFW_wl_surface_enter,
+ .leave = (void (*)(void *, struct wl_surface *, struct wl_output *))&RGFW_doNothing,
+ .preferred_buffer_scale = (void (*)(void *, struct wl_surface *, i32))&RGFW_doNothing,
+ .preferred_buffer_transform = (void (*)(void *, struct wl_surface *, u32))&RGFW_doNothing
+ };
+
+ win->src.surface = wl_compositor_create_surface(_RGFW->compositor);
+ wl_surface_add_listener(win->src.surface, &wl_surface_listener, win);
+
+ /* create a surface for a custom cursor */
+ win->src.custom_cursor_surface = wl_compositor_create_surface(_RGFW->compositor);
+
+ win->src.xdg_surface = xdg_wm_base_get_xdg_surface(_RGFW->xdg_wm_base, win->src.surface);
+ xdg_surface_add_listener(win->src.xdg_surface, &xdg_surface_listener, win);
+
+ xdg_wm_base_set_user_data(_RGFW->xdg_wm_base, win);
+
+ win->src.xdg_toplevel = xdg_surface_get_toplevel(win->src.xdg_surface);
+
+ if (_RGFW->className == NULL)
+ _RGFW->className = (char*)name;
+
+ xdg_toplevel_set_app_id(win->src.xdg_toplevel, name);
+
+ xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->w, win->h);
+
+ if (!(win->internal.flags & RGFW_windowTransparent)) { /* no transparency */
+ RGFW_wl_setOpaque(win);
+ }
+
+ static const struct xdg_toplevel_listener xdg_toplevel_listener = {
+ .configure = RGFW_wl_xdg_toplevel_configure_handler,
+ .close = RGFW_wl_xdg_toplevel_close_handler,
+ };
+
+ xdg_toplevel_add_listener(win->src.xdg_toplevel, &xdg_toplevel_listener, win);
+
+ /* compositor supports both SSD & CSD
+ So choose accordingly
+ */
+ if (_RGFW->decoration_manager) {
+ u32 decoration_mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
+ win->src.decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
+ _RGFW->decoration_manager, win->src.xdg_toplevel);
+
+ static const struct zxdg_toplevel_decoration_v1_listener xdg_decoration_listener = {
+ .configure = RGFW_wl_xdg_decoration_configure_handler
+ };
+
+ zxdg_toplevel_decoration_v1_add_listener(win->src.decoration, &xdg_decoration_listener, win);
+
+ /* we want no decorations */
+ if ((flags & RGFW_windowNoBorder)) {
+ decoration_mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
+ }
+
+ zxdg_toplevel_decoration_v1_set_mode(win->src.decoration, decoration_mode);
+
+ /* no xdg_decoration support */
+ } else if (!(flags & RGFW_windowNoBorder)) {
+ /* TODO, some fallback */
+ #ifdef RGFW_LIBDECOR
+ static struct libdecor_interface interface = {
+ .error = NULL,
+ };
+
+ static struct libdecor_frame_interface frameInterface = {0}; /*= {
+ RGFW_wl_handle_configure,
+ RGFW_wl_handle_close,
+ RGFW_wl_handle_commit,
+ RGFW_wl_handle_dismiss_popup,
+ };*/
+
+ win->src.decorContext = libdecor_new(_RGFW->wl_display, &interface);
+ if (win->src.decorContext) {
+ struct libdecor_frame *frame = libdecor_decorate(win->src.decorContext, win->src.surface, &frameInterface, win);
+ if (!frame) {
+ libdecor_unref(win->src.decorContext);
+ win->src.decorContext = NULL;
+ } else {
+ libdecor_frame_set_app_id(frame, "my-libdecor-app");
+ libdecor_frame_set_title(frame, "My Libdecor Window");
+ }
+ }
+ #endif
+ }
+
+ if (_RGFW->icon_manager != NULL) {
+ /* set the default wayland icon */
+ xdg_toplevel_icon_manager_v1_set_icon(_RGFW->icon_manager, win->src.xdg_toplevel, NULL);
+ }
+
+ wl_surface_commit(win->src.surface);
+
+ while (win->src.configured == RGFW_FALSE) {
+ wl_display_dispatch(_RGFW->wl_display);
+ }
+
+ RGFW_UNUSED(name);
+
+ return win;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_getGlobalMouse) (i32* x, i32* y) {
+ RGFW_init();
+ if (x) *x = 0;
+ if (y) *y = 0;
+ return RGFW_FALSE;
+}
+
+RGFW_key RGFW_FUNC(RGFW_physicalToMappedKey)(RGFW_key key) {
+ u32 keycode = RGFW_rgfwToApiKey(key);
+ xkb_keycode_t kc = keycode + 8;
+ xkb_keysym_t sym = xkb_state_key_get_one_sym(_RGFW->xkb_state, kc);
+ if (sym < 256) {
+ return (RGFW_key)sym;
+ }
+
+ switch (sym) {
+ case XKB_KEY_F1: return RGFW_F1;
+ case XKB_KEY_F2: return RGFW_F2;
+ case XKB_KEY_F3: return RGFW_F3;
+ case XKB_KEY_F4: return RGFW_F4;
+ case XKB_KEY_F5: return RGFW_F5;
+ case XKB_KEY_F6: return RGFW_F6;
+ case XKB_KEY_F7: return RGFW_F7;
+ case XKB_KEY_F8: return RGFW_F8;
+ case XKB_KEY_F9: return RGFW_F9;
+ case XKB_KEY_F10: return RGFW_F10;
+ case XKB_KEY_F11: return RGFW_F11;
+ case XKB_KEY_F12: return RGFW_F12;
+ case XKB_KEY_F13: return RGFW_F13;
+ case XKB_KEY_F14: return RGFW_F14;
+ case XKB_KEY_F15: return RGFW_F15;
+ case XKB_KEY_F16: return RGFW_F16;
+ case XKB_KEY_F17: return RGFW_F17;
+ case XKB_KEY_F18: return RGFW_F18;
+ case XKB_KEY_F19: return RGFW_F19;
+ case XKB_KEY_F20: return RGFW_F20;
+ case XKB_KEY_F21: return RGFW_F21;
+ case XKB_KEY_F22: return RGFW_F22;
+ case XKB_KEY_F23: return RGFW_F23;
+ case XKB_KEY_F24: return RGFW_F24;
+ case XKB_KEY_F25: return RGFW_F25;
+ case XKB_KEY_Shift_L: return RGFW_shiftL;
+ case XKB_KEY_Shift_R: return RGFW_shiftR;
+ case XKB_KEY_Control_L: return RGFW_controlL;
+ case XKB_KEY_Control_R: return RGFW_controlR;
+ case XKB_KEY_Alt_L: return RGFW_altL;
+ case XKB_KEY_Alt_R: return RGFW_altR;
+ case XKB_KEY_Super_L: return RGFW_superL;
+ case XKB_KEY_Super_R: return RGFW_superR;
+ case XKB_KEY_Caps_Lock: return RGFW_capsLock;
+ case XKB_KEY_Num_Lock: return RGFW_numLock;
+ case XKB_KEY_Scroll_Lock:return RGFW_scrollLock;
+ case XKB_KEY_Up: return RGFW_up;
+ case XKB_KEY_Down: return RGFW_down;
+ case XKB_KEY_Left: return RGFW_left;
+ case XKB_KEY_Right: return RGFW_right;
+ case XKB_KEY_Home: return RGFW_home;
+ case XKB_KEY_End: return RGFW_end;
+ case XKB_KEY_Page_Up: return RGFW_pageUp;
+ case XKB_KEY_Page_Down: return RGFW_pageDown;
+ case XKB_KEY_Insert: return RGFW_insert;
+ case XKB_KEY_Menu: return RGFW_menu;
+ case XKB_KEY_KP_Add: return RGFW_kpPlus;
+ case XKB_KEY_KP_Subtract: return RGFW_kpMinus;
+ case XKB_KEY_KP_Multiply: return RGFW_kpMultiply;
+ case XKB_KEY_KP_Divide: return RGFW_kpSlash;
+ case XKB_KEY_KP_Equal: return RGFW_kpEqual;
+ case XKB_KEY_KP_Enter: return RGFW_kpReturn;
+ case XKB_KEY_KP_Decimal: return RGFW_kpPeriod;
+ case XKB_KEY_KP_0: return RGFW_kp0;
+ case XKB_KEY_KP_1: return RGFW_kp1;
+ case XKB_KEY_KP_2: return RGFW_kp2;
+ case XKB_KEY_KP_3: return RGFW_kp3;
+ case XKB_KEY_KP_4: return RGFW_kp4;
+ case XKB_KEY_KP_5: return RGFW_kp5;
+ case XKB_KEY_KP_6: return RGFW_kp6;
+ case XKB_KEY_KP_7: return RGFW_kp7;
+ case XKB_KEY_KP_8: return RGFW_kp8;
+ case XKB_KEY_KP_9: return RGFW_kp9;
+ case XKB_KEY_Print: return RGFW_printScreen;
+ case XKB_KEY_Pause: return RGFW_pause;
+ default: break;
+ }
+
+ return RGFW_keyNULL;
+}
+
+void RGFW_FUNC(RGFW_pollEvents) (void) {
+ RGFW_resetPrevState();
+
+ /* send buffered requests to compositor */
+ while (wl_display_flush(_RGFW->wl_display) == -1) {
+ /* compositor not responding to new requests */
+ /* so let's dispatch some events so the compositor responds */
+ if (errno == EAGAIN) {
+ if (wl_display_dispatch_pending(_RGFW->wl_display) == -1) {
+ return;
+ }
+ } else {
+ return;
+ }
+ }
+
+ /* read the events; if empty this reads from the */
+ /* wayland file descriptor */
+ if (wl_display_dispatch(_RGFW->wl_display) == -1) {
+ return;
+ }
+
+}
+
+void RGFW_FUNC(RGFW_window_move) (RGFW_window* win, i32 x, i32 y) {
+ RGFW_ASSERT(win != NULL);
+ win->x = x;
+ win->y = y;
+}
+
+
+void RGFW_FUNC(RGFW_window_resize) (RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+ win->w = w;
+ win->h = h;
+ if (_RGFW->compositor) {
+ xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->w, win->h);
+ #ifdef RGFW_OPENGL
+ if (win->src.ctx.egl)
+ wl_egl_window_resize(win->src.ctx.egl->eglWindow, (i32)w, (i32)h, 0, 0);
+ #endif
+ }
+}
+
+void RGFW_FUNC(RGFW_window_setAspectRatio) (RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+
+ if (w == 0 && h == 0)
+ return;
+ xdg_toplevel_set_max_size(win->src.xdg_toplevel, (i32)w, (i32)h);
+}
+
+void RGFW_FUNC(RGFW_window_setMinSize) (RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+ xdg_toplevel_set_min_size(win->src.xdg_toplevel, w, h);
+}
+
+void RGFW_FUNC(RGFW_window_setMaxSize) (RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+ xdg_toplevel_set_max_size(win->src.xdg_toplevel, w, h);
+}
+
+void RGFW_toggleWaylandMaximized(RGFW_window* win, RGFW_bool maximized) {
+ win->src.maximized = maximized;
+ if (maximized) {
+ xdg_toplevel_set_maximized(win->src.xdg_toplevel);
+ } else {
+ xdg_toplevel_unset_maximized(win->src.xdg_toplevel);
+ }
+}
+
+void RGFW_FUNC(RGFW_window_maximize) (RGFW_window* win) {
+ win->internal.oldX = win->x;
+ win->internal.oldY = win->y;
+ win->internal.oldW = win->w;
+ win->internal.oldH = win->h;
+ RGFW_toggleWaylandMaximized(win, 1);
+ return;
+}
+
+void RGFW_FUNC(RGFW_window_focus)(RGFW_window* win) {
+ RGFW_ASSERT(win);
+}
+
+void RGFW_FUNC(RGFW_window_raise)(RGFW_window* win) {
+ RGFW_ASSERT(win);
+}
+
+void RGFW_FUNC(RGFW_window_setFullscreen)(RGFW_window* win, RGFW_bool fullscreen) {
+ RGFW_ASSERT(win != NULL);
+ if (fullscreen) {
+
+ win->internal.flags |= RGFW_windowFullscreen;
+ win->internal.oldX = win->x;
+ win->internal.oldY = win->y;
+ win->internal.oldW = win->w;
+ win->internal.oldH = win->h;
+ xdg_toplevel_set_fullscreen(win->src.xdg_toplevel, NULL); /* let the compositor decide */
+ } else {
+ win->internal.flags &= ~(u32)RGFW_windowFullscreen;
+ xdg_toplevel_unset_fullscreen(win->src.xdg_toplevel);
+ }
+
+}
+
+void RGFW_FUNC(RGFW_window_setFloating) (RGFW_window* win, RGFW_bool floating) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_UNUSED(floating);
+}
+
+void RGFW_FUNC(RGFW_window_setOpacity) (RGFW_window* win, u8 opacity) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_UNUSED(opacity);
+}
+
+void RGFW_FUNC(RGFW_window_minimize)(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ if (RGFW_window_isMaximized(win)) return;
+ win->internal.oldX = win->x;
+ win->internal.oldY = win->y;
+ win->internal.oldW = win->w;
+ win->internal.oldH = win->h;
+ win->src.minimized = RGFW_TRUE;
+ xdg_toplevel_set_minimized(win->src.xdg_toplevel);
+}
+
+void RGFW_FUNC(RGFW_window_restore)(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_toggleWaylandMaximized(win, RGFW_FALSE);
+
+ RGFW_window_move(win, win->internal.oldX, win->internal.oldY);
+ RGFW_window_resize(win, win->internal.oldW, win->internal.oldH);
+
+ RGFW_window_show(win);
+ RGFW_window_move(win, win->internal.oldX, win->internal.oldY);
+ RGFW_window_resize(win, win->internal.oldW, win->internal.oldH);
+
+ RGFW_window_show(win);
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_isFloating)(RGFW_window* win) {
+ return (!RGFW_window_isFullscreen(win) && !RGFW_window_isMaximized(win));
+}
+
+void RGFW_FUNC(RGFW_window_setName) (RGFW_window* win, const char* name) {
+ RGFW_ASSERT(win != NULL);
+ if (name == NULL) name = "\0";
+
+ if (_RGFW->compositor)
+ xdg_toplevel_set_title(win->src.xdg_toplevel, name);
+}
+
+#ifndef RGFW_NO_PASSTHROUGH
+void RGFW_FUNC(RGFW_window_setMousePassthrough) (RGFW_window* win, RGFW_bool passthrough) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_UNUSED(passthrough);
+}
+#endif /* RGFW_NO_PASSTHROUGH */
+
+RGFW_bool RGFW_FUNC(RGFW_window_setIconEx) (RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_icon type) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_UNUSED(type);
+
+ if (_RGFW->icon_manager == NULL || w != h) return RGFW_FALSE;
+
+ if (win->src.icon) {
+ xdg_toplevel_icon_v1_destroy(win->src.icon);
+ win->src.icon= NULL;
+ }
+
+ RGFW_surface* surface = RGFW_createSurface(data, w, h, format);
+
+ if (surface == NULL) return RGFW_FALSE;
+
+ RGFW_copyImageData(surface->native.buffer, RGFW_MIN(w, surface->w), RGFW_MIN(h, surface->h), surface->native.format, surface->data, surface->format, NULL);
+
+ win->src.icon = xdg_toplevel_icon_manager_v1_create_icon(_RGFW->icon_manager);
+ xdg_toplevel_icon_v1_add_buffer(win->src.icon, surface->native.wl_buffer, 1);
+ xdg_toplevel_icon_manager_v1_set_icon(_RGFW->icon_manager, win->src.xdg_toplevel, win->src.icon);
+
+ RGFW_surface_free(surface);
+ return RGFW_TRUE;
+}
+
+RGFW_mouse* RGFW_FUNC(RGFW_loadMouse)(u8* data, i32 w, i32 h, RGFW_format format) {
+
+ RGFW_surface *mouse_surface = RGFW_createSurface(data, w, h, format);
+
+ if (mouse_surface == NULL) return NULL;
+
+ RGFW_copyImageData(mouse_surface->native.buffer, RGFW_MIN(w, mouse_surface->w), RGFW_MIN(h, mouse_surface->h), mouse_surface->native.format, mouse_surface->data, mouse_surface->format, NULL);
+
+ return (void*) mouse_surface;
+}
+
+void RGFW_FUNC(RGFW_window_setMouse)(RGFW_window* win, RGFW_mouse* mouse) {
+ RGFW_ASSERT(win); RGFW_ASSERT(mouse);
+ RGFW_surface *mouse_surface = (RGFW_surface*)mouse;
+
+ win->src.using_custom_cursor = RGFW_TRUE;
+
+ struct wl_buffer *mouse_buffer = mouse_surface->native.wl_buffer;
+
+ wl_surface_attach(win->src.custom_cursor_surface, mouse_buffer, 0, 0);
+ wl_surface_damage(win->src.custom_cursor_surface, 0, 0, mouse_surface->w, mouse_surface->h);
+ wl_surface_commit(win->src.custom_cursor_surface);
+
+}
+
+void RGFW_FUNC(RGFW_freeMouse)(RGFW_mouse* mouse) {
+ if (mouse != NULL) {
+ RGFW_surface_free((RGFW_surface*)mouse);
+ }
+}
+
+void RGFW_FUNC(RGFW_window_moveMouse)(RGFW_window* win, i32 x, i32 y) {
+ if (_RGFW->wp_pointer_warp != NULL) {
+ wp_pointer_warp_v1_warp_pointer(_RGFW->wp_pointer_warp, win->src.surface, _RGFW->wl_pointer, wl_fixed_from_int(x), wl_fixed_from_int(y), _RGFW->mouse_enter_serial);
+ }
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_setMouseDefault)(RGFW_window* win) {
+ return RGFW_window_setMouseStandard(win, RGFW_mouseArrow);
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_setMouseStandard)(RGFW_window* win, u8 mouse) {
+ RGFW_ASSERT(win != NULL);
+
+ char* cursorName = NULL;
+ switch (mouse) {
+ case RGFW_mouseNormal: cursorName = (char*)"left_ptr"; break;
+ case RGFW_mouseArrow: cursorName = (char*)"left_ptr"; break;
+ case RGFW_mouseIbeam: cursorName = (char*)"xterm"; break;
+ case RGFW_mouseCrosshair: cursorName = (char*)"crosshair"; break;
+ case RGFW_mousePointingHand: cursorName = (char*)"hand2"; break;
+ case RGFW_mouseResizeEW: cursorName = (char*)"sb_h_double_arrow"; break;
+ case RGFW_mouseResizeNS: cursorName = (char*)"sb_v_double_arrow"; break;
+ case RGFW_mouseResizeNWSE: cursorName = (char*)"top_left_corner"; break; /* or fd_double_arrow */
+ case RGFW_mouseResizeNESW: cursorName = (char*)"top_right_corner"; break; /* or bd_double_arrow */
+ case RGFW_mouseResizeNW: cursorName = (char*)"top_left_corner"; break;
+ case RGFW_mouseResizeN: cursorName = (char*)"top_side"; break;
+ case RGFW_mouseResizeNE: cursorName = (char*)"top_right_corner"; break;
+ case RGFW_mouseResizeE: cursorName = (char*)"right_side"; break;
+ case RGFW_mouseResizeSE: cursorName = (char*)"bottom_right_corner"; break;
+ case RGFW_mouseResizeS: cursorName = (char*)"bottom_side"; break;
+ case RGFW_mouseResizeSW: cursorName = (char*)"bottom_left_corner"; break;
+ case RGFW_mouseResizeW: cursorName = (char*)"left_side"; break;
+ case RGFW_mouseResizeAll: cursorName = (char*)"fleur"; break;
+ case RGFW_mouseNotAllowed: cursorName = (char*)"not-allowed"; break;
+ case RGFW_mouseWait: cursorName = (char*)"watch"; break;
+ case RGFW_mouseProgress: cursorName = (char*)"watch"; break;
+ default: return RGFW_FALSE;
+ }
+
+ win->src.using_custom_cursor = RGFW_FALSE;
+
+ struct wl_cursor* wlcursor = wl_cursor_theme_get_cursor(_RGFW->wl_cursor_theme, cursorName);
+ if (wlcursor == NULL)
+ return RGFW_FALSE;
+ struct wl_cursor_image* cursor_image = wlcursor->images[0];
+ struct wl_buffer* cursor_buffer = wl_cursor_image_get_buffer(cursor_image);
+ wl_pointer_set_cursor(_RGFW->wl_pointer, _RGFW->mouse_enter_serial, _RGFW->cursor_surface, (i32)cursor_image->hotspot_x, (i32)cursor_image->hotspot_y);
+ wl_surface_attach(_RGFW->cursor_surface, cursor_buffer, 0, 0);
+ wl_surface_damage(_RGFW->cursor_surface, 0, 0, (i32)cursor_image->width, (i32)cursor_image->height);
+ wl_surface_commit(_RGFW->cursor_surface);
+ return RGFW_TRUE;
+}
+
+void RGFW_FUNC(RGFW_window_hide) (RGFW_window* win) {
+ wl_surface_attach(win->src.surface, NULL, 0, 0);
+ wl_surface_commit(win->src.surface);
+ win->internal.flags |= RGFW_windowHide;
+}
+
+void RGFW_FUNC(RGFW_window_show) (RGFW_window* win) {
+ win->internal.flags &= ~(u32)RGFW_windowHide;
+ if (win->internal.flags & RGFW_windowFocusOnShow) RGFW_window_focus(win);
+ /* wl_surface_attach(win->src.surface, win->x, win->y, win->w, win->h, 0, 0); */
+ wl_surface_commit(win->src.surface);
+}
+
+void RGFW_FUNC(RGFW_window_flash) (RGFW_window* win, RGFW_flashRequest request) {
+ if (RGFW_window_isInFocus(win) && request) {
+ return;
+ }
+}
+
+RGFW_ssize_t RGFW_FUNC(RGFW_readClipboardPtr) (char* str, size_t strCapacity) {
+ RGFW_UNUSED(strCapacity);
+
+ if (str != NULL)
+ RGFW_STRNCPY(str, _RGFW->clipboard, _RGFW->clipboard_len - 1);
+ _RGFW->clipboard[_RGFW->clipboard_len - 1] = '\0';
+ return (RGFW_ssize_t)_RGFW->clipboard_len - 1;
+}
+
+void RGFW_FUNC(RGFW_writeClipboard) (const char* text, u32 textLen) {
+
+ // compositor does not support wl_data_device_manager
+ // clients cannot read rgfw's clipboard
+ if (_RGFW->data_device_manager == NULL) return;
+ // clear the clipboard
+ if (_RGFW->clipboard)
+ RGFW_FREE(_RGFW->clipboard);
+
+ // set the contents
+ _RGFW->clipboard = (char*)RGFW_ALLOC(textLen);
+ RGFW_ASSERT(_RGFW->clipboard != NULL);
+ RGFW_STRNCPY(_RGFW->clipboard, text, textLen - 1);
+ _RGFW->clipboard[textLen - 1] = '\0';
+ _RGFW->clipboard_len = textLen;
+
+ // means we already wrote to the clipboard
+ // so destroy it to create a new one
+ RGFW_window* win = _RGFW->kbOwner;
+
+ if (win->src.data_source != NULL) {
+ wl_data_source_destroy(win->src.data_source);
+ win->src.data_source = NULL;
+ }
+
+ // advertise to other clients that we offer text
+ win->src.data_source = wl_data_device_manager_create_data_source(_RGFW->data_device_manager);
+
+ // basic error checking
+ if (win->src.data_source == NULL) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errClipboard, "Could not create clipboard data source");
+ return;
+ }
+ wl_data_source_offer(win->src.data_source , "text/plain;charset=utf-8");
+
+ // needed RGFW_doNothing because wayland will call the functions
+ // if not set they are random data that lead to a crash
+ static const struct wl_data_source_listener data_source_listener = {
+ .target = (void (*)(void *, struct wl_data_source *, const char *))&RGFW_doNothing,
+ .action = (void (*)(void *, struct wl_data_source *, u32))&RGFW_doNothing,
+ .dnd_drop_performed = (void (*)(void *, struct wl_data_source *))&RGFW_doNothing,
+ .dnd_finished = (void (*)(void *, struct wl_data_source *))&RGFW_doNothing,
+ .send = RGFW_wl_data_source_send,
+ .cancelled = RGFW_wl_data_source_cancelled
+ };
+
+ wl_data_source_add_listener(win->src.data_source, &data_source_listener, _RGFW);
+
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_isHidden) (RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ return RGFW_FALSE;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_isMinimized) (RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ return win->src.minimized;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_window_isMaximized) (RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ return win->src.maximized;
+}
+
+void RGFW_FUNC(RGFW_pollMonitors) (void) {
+ _RGFW->monitors.primary = _RGFW->monitors.list.head;
+}
+
+
+RGFW_bool RGFW_FUNC(RGFW_monitor_getWorkarea) (RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height) {
+ /* NOTE: Wayland has no way to get the actual workarea as far as I'm aware :( */
+ if (x) *x = monitor->x;
+ if (y) *y = monitor->y;
+ if (width) *width = monitor->mode.w;
+ if (height) *height = monitor->mode.h;
+ return RGFW_TRUE;
+}
+
+size_t RGFW_FUNC(RGFW_monitor_getModesPtr) (RGFW_monitor* monitor, RGFW_monitorMode** modes) {
+ if (modes) {
+ RGFW_MEMCPY((*modes), monitor->node->modes, monitor->node->modeCount * sizeof(RGFW_monitorMode));
+ }
+
+ return monitor->node->modeCount;
+}
+
+size_t RGFW_FUNC(RGFW_monitor_getGammaRampPtr) (RGFW_monitor* monitor, RGFW_gammaRamp* ramp) {
+ RGFW_UNUSED(monitor); RGFW_UNUSED(ramp);
+ return 0;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_monitor_setGammaRamp) (RGFW_monitor* monitor, RGFW_gammaRamp* ramp) {
+ RGFW_UNUSED(monitor); RGFW_UNUSED(ramp);
+ return RGFW_FALSE;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_monitor_requestMode) (RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request) {
+ for (size_t i = 0; i < mon->node->modeCount; i++) {
+ if (RGFW_monitorModeCompare(mode, &mon->node->modes[i], request) == RGFW_FALSE) {
+ continue;
+ }
+
+ RGFW_monitor_setMode(mon, &mon->node->modes[i]);
+ return RGFW_TRUE;
+ }
+
+ return RGFW_FALSE;
+}
+
+RGFW_bool RGFW_FUNC(RGFW_monitor_setMode) (RGFW_monitor* mon, RGFW_monitorMode* mode) {
+ RGFW_UNUSED(mon); RGFW_UNUSED(mode);
+ return RGFW_FALSE;
+}
+
+RGFW_monitor* RGFW_FUNC(RGFW_window_getMonitor) (RGFW_window* win) {
+ RGFW_ASSERT(win);
+ if (win->src.active_monitor == NULL) {
+ /* TODO: fix race condition [probably a problem with wayland] */
+ return RGFW_getPrimaryMonitor();
+ }
+
+ return &win->src.active_monitor->mon;
+}
+
+#ifdef RGFW_OPENGL
+RGFW_bool RGFW_FUNC(RGFW_extensionSupportedPlatform_OpenGL) (const char * extension, size_t len) { return RGFW_extensionSupportedPlatform_EGL(extension, len); }
+RGFW_proc RGFW_FUNC(RGFW_getProcAddress_OpenGL) (const char* procname) { return RGFW_getProcAddress_EGL(procname); }
+
+
+RGFW_bool RGFW_FUNC(RGFW_window_createContextPtr_OpenGL)(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) {
+ RGFW_bool out = RGFW_window_createContextPtr_EGL(win, &ctx->egl, hints);
+ win->src.gfxType = RGFW_gfxNativeOpenGL;
+
+ RGFW_window_swapInterval_OpenGL(win, 0);
+ return out;
+}
+void RGFW_FUNC(RGFW_window_deleteContextPtr_OpenGL) (RGFW_window* win, RGFW_glContext* ctx) { RGFW_window_deleteContextPtr_EGL(win, &ctx->egl); win->src.ctx.native = NULL; }
+
+void RGFW_FUNC(RGFW_window_makeCurrentContext_OpenGL) (RGFW_window* win) { RGFW_window_makeCurrentContext_EGL(win); }
+void* RGFW_FUNC(RGFW_getCurrentContext_OpenGL) (void) { return RGFW_getCurrentContext_EGL(); }
+void RGFW_FUNC(RGFW_window_swapBuffers_OpenGL) (RGFW_window* win) { RGFW_window_swapBuffers_EGL(win); }
+void RGFW_FUNC(RGFW_window_swapInterval_OpenGL) (RGFW_window* win, i32 swapInterval) { RGFW_window_swapInterval_EGL(win, swapInterval); }
+#endif /* RGFW_OPENGL */
+
+void RGFW_FUNC(RGFW_window_closePlatform)(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, "a window was freed");
+ #ifdef RGFW_LIBDECOR
+ if (win->src.decorContext)
+ libdecor_unref(win->src.decorContext);
+ #endif
+
+ if (win->src.decoration) {
+ zxdg_toplevel_decoration_v1_destroy(win->src.decoration);
+ }
+
+ if (win->src.xdg_toplevel) {
+ xdg_toplevel_destroy(win->src.xdg_toplevel);
+ }
+
+ wl_surface_destroy(win->src.custom_cursor_surface);
+
+ if (win->src.locked_pointer) {
+ zwp_locked_pointer_v1_destroy(win->src.locked_pointer);
+ }
+
+ if (win->src.icon) {
+ xdg_toplevel_icon_v1_destroy(win->src.icon);
+ }
+
+ xdg_surface_destroy(win->src.xdg_surface);
+ wl_surface_destroy(win->src.surface);
+}
+
+#ifdef RGFW_WEBGPU
+WGPUSurface RGFW_FUNC(RGFW_window_createSurface_WebGPU) (RGFW_window* window, WGPUInstance instance) {
+ WGPUSurfaceDescriptor surfaceDesc = {0};
+ WGPUSurfaceSourceWaylandSurface fromWl = {0};
+ fromWl.chain.sType = WGPUSType_SurfaceSourceWaylandSurface;
+ fromWl.display = _RGFW->wl_display;
+ fromWl.surface = window->src.surface;
+
+ surfaceDesc.nextInChain = (WGPUChainedStruct*)&fromWl.chain;
+ return wgpuInstanceCreateSurface(instance, &surfaceDesc);
+}
+#endif
+
+
+
+#endif /* RGFW_WAYLAND */
+/*
+ End of Wayland defines
+*/
+
+/*
+
+ Start of Windows defines
+
+
+*/
+
+#ifdef RGFW_WINDOWS
+#ifndef WIN32_LEAN_AND_MEAN
+ #define WIN32_LEAN_AND_MEAN
+#endif
+
+#ifndef OEMRESOURCE
+ #define OEMRESOURCE
+#endif
+
+#include
+
+#ifndef OCR_NORMAL
+#define OCR_NORMAL 32512
+#define OCR_IBEAM 32513
+#define OCR_WAIT 32514
+#define OCR_CROSS 32515
+#define OCR_UP 32516
+#define OCR_SIZENWSE 32642
+#define OCR_SIZENESW 32643
+#define OCR_SIZEWE 32644
+#define OCR_SIZENS 32645
+#define OCR_SIZEALL 32646
+#define OCR_NO 32648
+#define OCR_HAND 32649
+#define OCR_APPSTARTING 32650
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifndef WM_DPICHANGED
+#define WM_DPICHANGED 0x02E0
+#endif
+
+RGFWDEF DWORD RGFW_winapi_window_getStyle(RGFW_window* win, RGFW_windowFlags flags);
+DWORD RGFW_winapi_window_getStyle(RGFW_window* win, RGFW_windowFlags flags) {
+ RGFW_UNUSED(win);
+ DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
+
+ if ((flags & RGFW_windowFullscreen)) {
+ style |= WS_POPUP;
+ } else {
+ style |= WS_SYSMENU | WS_MINIMIZEBOX;
+
+ if (!(flags & RGFW_windowNoBorder)) {
+ style |= WS_CAPTION;
+
+ if (!(flags & RGFW_windowNoResize))
+ style |= WS_MAXIMIZEBOX | WS_THICKFRAME;
+ }
+ else
+ style |= WS_POPUP;
+ }
+
+ return style;
+}
+
+RGFWDEF DWORD RGFW_winapi_window_getExStyle(RGFW_window* win, RGFW_windowFlags flags);
+DWORD RGFW_winapi_window_getExStyle(RGFW_window* win, RGFW_windowFlags flags) {
+ DWORD style = WS_EX_APPWINDOW;
+ if (flags & RGFW_windowFullscreen || (flags & RGFW_windowFloating || RGFW_window_isFloating(win))) {
+ style |= WS_EX_TOPMOST;
+ }
+
+ return style;
+}
+
+RGFW_bool RGFW_createUTF8FromWideStringWin32(const WCHAR* source, char* out, size_t max);
+
+#define GL_FRONT 0x0404
+#define GL_BACK 0x0405
+#define GL_LEFT 0x0406
+#define GL_RIGHT 0x0407
+
+typedef int (*PFN_wglGetSwapIntervalEXT)(void);
+PFN_wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc = NULL;
+#define wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc
+
+/* these two wgl functions need to be preloaded */
+typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList);
+PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
+
+HMODULE RGFW_wgl_dll = NULL;
+
+#ifndef RGFW_NO_LOAD_WGL
+ typedef HGLRC(WINAPI* PFN_wglCreateContext)(HDC);
+ typedef BOOL(WINAPI* PFN_wglDeleteContext)(HGLRC);
+ typedef PROC(WINAPI* PFN_wglGetProcAddress)(LPCSTR);
+ typedef BOOL(WINAPI* PFN_wglMakeCurrent)(HDC, HGLRC);
+ typedef HDC(WINAPI* PFN_wglGetCurrentDC)(void);
+ typedef HGLRC(WINAPI* PFN_wglGetCurrentContext)(void);
+ typedef BOOL(WINAPI* PFN_wglShareLists)(HGLRC, HGLRC);
+
+ PFN_wglCreateContext wglCreateContextSRC;
+ PFN_wglDeleteContext wglDeleteContextSRC;
+ PFN_wglGetProcAddress wglGetProcAddressSRC;
+ PFN_wglMakeCurrent wglMakeCurrentSRC;
+ PFN_wglGetCurrentDC wglGetCurrentDCSRC;
+ PFN_wglGetCurrentContext wglGetCurrentContextSRC;
+ PFN_wglShareLists wglShareListsSRC;
+
+ #define wglCreateContext wglCreateContextSRC
+ #define wglDeleteContext wglDeleteContextSRC
+ #define wglGetProcAddress wglGetProcAddressSRC
+ #define wglMakeCurrent wglMakeCurrentSRC
+ #define wglGetCurrentDC wglGetCurrentDCSRC
+ #define wglGetCurrentContext wglGetCurrentContextSRC
+ #define wglShareLists wglShareListsSRC
+#endif
+
+void* RGFW_window_getHWND(RGFW_window* win) { return win->src.window; }
+void* RGFW_window_getHDC(RGFW_window* win) { return win->src.hdc; }
+
+#ifdef RGFW_OPENGL
+RGFWDEF void RGFW_win32_loadOpenGLFuncs(HWND dummyWin);
+
+typedef HRESULT (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats);
+PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL;
+
+typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval);
+PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
+#endif
+
+#ifndef RGFW_NO_DWM
+HMODULE RGFW_dwm_dll = NULL;
+#ifndef _DWMAPI_H_
+typedef struct { DWORD dwFlags; int fEnable; HRGN hRgnBlur; int fTransitionOnMaximized;} DWM_BLURBEHIND;
+#endif
+typedef HRESULT (WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND, const DWM_BLURBEHIND*);
+PFN_DwmEnableBlurBehindWindow DwmEnableBlurBehindWindowSRC = NULL;
+
+typedef HRESULT (WINAPI * PFN_DwmSetWindowAttribute)(HWND, DWORD, LPCVOID, DWORD);
+PFN_DwmSetWindowAttribute DwmSetWindowAttributeSRC = NULL;
+#endif
+void RGFW_win32_makeWindowTransparent(RGFW_window* win);
+void RGFW_win32_makeWindowTransparent(RGFW_window* win) {
+ if (!(win->internal.flags & RGFW_windowTransparent)) return;
+
+ #ifndef RGFW_NO_DWM
+ if (DwmEnableBlurBehindWindowSRC != NULL) {
+ DWM_BLURBEHIND bb = {0, 0, 0, 0};
+ bb.dwFlags = 0x1;
+ bb.fEnable = TRUE;
+ bb.hRgnBlur = NULL;
+ DwmEnableBlurBehindWindowSRC(win->src.window, &bb);
+
+ } else
+ #endif
+ {
+ SetWindowLong(win->src.window, GWL_EXSTYLE, WS_EX_LAYERED);
+ SetLayeredWindowAttributes(win->src.window, 0, 128, LWA_ALPHA);
+ }
+}
+
+RGFWDEF RGFW_bool RGFW_win32_getDarkModeState(void);
+RGFW_bool RGFW_win32_getDarkModeState(void) {
+ u32 lightMode = 1;
+ DWORD len = sizeof(lightMode);
+
+ RegGetValueW(
+ HKEY_CURRENT_USER,
+ L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
+ L"AppsUseLightTheme", RRF_RT_REG_DWORD, NULL, &lightMode, &len
+ );
+
+ return (lightMode == 0);
+}
+
+RGFWDEF void RGFW_win32_makeWindowDarkMode(RGFW_window* win, RGFW_bool state);
+void RGFW_win32_makeWindowDarkMode(RGFW_window* win, RGFW_bool state) {
+ BOOL value = (state == RGFW_TRUE) ? TRUE : FALSE;
+ DwmSetWindowAttributeSRC(win->src.window, 20 /* DWMWA_USE_IMMERSIVE_DARK_MODE */, &value, sizeof(value));
+}
+
+LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
+ RGFW_window* win = (RGFW_window*)GetPropW(hWnd, L"RGFW");
+ if (win == NULL) return DefWindowProcW(hWnd, message, wParam, lParam);
+
+ static BYTE keyboardState[256];
+ GetKeyboardState(keyboardState);
+
+ RECT frame;
+ ZeroMemory(&frame, sizeof(frame));
+ DWORD style = RGFW_winapi_window_getStyle(win, win->internal.flags);
+ DWORD exStyle = RGFW_winapi_window_getExStyle(win, win->internal.flags);
+ AdjustWindowRectEx(&frame, style, FALSE, exStyle);
+
+ switch (message) {
+ case WM_DISPLAYCHANGE:
+ RGFW_pollMonitors();
+ break;
+ case WM_CLOSE:
+ case WM_QUIT:
+ RGFW_windowQuitCallback(win);
+ return 0;
+ case WM_ACTIVATE: {
+ RGFW_bool inFocus = RGFW_BOOL(LOWORD(wParam) != WA_INACTIVE);
+ RGFW_focusCallback(win, inFocus);
+
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+ }
+ case WM_MOVE:
+ if (win->internal.captureMouse) {
+ RGFW_window_captureMousePlatform(win, RGFW_TRUE);
+ }
+
+ RGFW_windowMovedCallback(win, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+ case WM_SIZE: {
+ if (win->internal.captureMouse) {
+ RGFW_window_captureMousePlatform(win, RGFW_TRUE);
+ }
+
+ RGFW_windowResizedCallback(win, LOWORD(lParam), HIWORD(lParam));
+ RGFW_window_checkMode(win);
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+ }
+ case WM_MOUSEACTIVATE: {
+ if (HIWORD(lParam) == WM_LBUTTONDOWN) {
+ if (LOWORD(lParam) != HTCLIENT)
+ win->src.actionFrame = RGFW_TRUE;
+ }
+
+ break;
+ }
+ case WM_CAPTURECHANGED: {
+ if (lParam == 0 && win->src.actionFrame) {
+ RGFW_window_captureMousePlatform(win, win->internal.captureMouse);
+ win->src.actionFrame = RGFW_FALSE;
+ }
+
+ break;
+ }
+ #ifndef RGFW_NO_DPI
+ case WM_DPICHANGED: {
+ const float scaleX = HIWORD(wParam) / (float) 96;
+ const float scaleY = LOWORD(wParam) / (float) 96;
+
+ RGFW_scaleUpdatedCallback(win, scaleX, scaleY);
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+ }
+ #endif
+ case WM_SIZING: {
+ if (win->src.aspectRatioW == 0 && win->src.aspectRatioH == 0) {
+ break;
+ }
+
+ RECT* area = (RECT*)lParam;
+ i32 edge = (i32)wParam;
+
+ double ratio = (double)win->src.aspectRatioW / (double) win->src.aspectRatioH;
+
+ if (edge == WMSZ_LEFT || edge == WMSZ_BOTTOMLEFT || edge == WMSZ_RIGHT || edge == WMSZ_BOTTOMRIGHT) {
+ area->bottom = area->top + (frame.bottom - frame.top) + (i32) (((area->right - area->left) - (frame.right - frame.left)) / ratio);
+ } else if (edge == WMSZ_TOPLEFT || edge == WMSZ_TOPRIGHT) {
+ area->top = area->bottom - (frame.bottom - frame.top) - (i32) (((area->right - area->left) - (frame.right - frame.left)) / ratio);
+ } else if (edge == WMSZ_TOP || edge == WMSZ_BOTTOM) {
+ area->right = area->left + (frame.right - frame.left) + (i32) (((area->bottom - area->top) - (frame.bottom - frame.top)) * ratio);
+ }
+
+ return TRUE;
+ }
+ case WM_GETMINMAXINFO: {
+ MINMAXINFO* mmi = (MINMAXINFO*) lParam;
+ RGFW_bool resize = ((win->src.minSizeW == win->src.maxSizeW) && (win->src.minSizeH == win->src.maxSizeH));
+ RGFW_setBit(&win->internal.flags, RGFW_windowNoResize, resize);
+
+ mmi->ptMinTrackSize.x = (LONG)(win->src.minSizeW + (frame.right - frame.left));
+ mmi->ptMinTrackSize.y = (LONG)(win->src.minSizeH + (frame.bottom - frame.top));
+ if (win->src.maxSizeW == 0 && win->src.maxSizeH == 0)
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+
+ mmi->ptMaxTrackSize.x = (LONG)(win->src.maxSizeW + (frame.right - frame.left));
+ mmi->ptMaxTrackSize.y = (LONG)(win->src.maxSizeH + (frame.bottom - frame.top));
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+ }
+ case WM_PAINT: {
+ PAINTSTRUCT ps;
+ BeginPaint(hWnd, &ps);
+ RGFW_windowRefreshCallback(win);
+ EndPaint(hWnd, &ps);
+
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+ }
+ #if(_WIN32_WINNT >= 0x0600)
+ case WM_DWMCOMPOSITIONCHANGED:
+ case WM_DWMCOLORIZATIONCOLORCHANGED:
+ RGFW_win32_makeWindowTransparent(win);
+ break;
+ #endif
+
+ case WM_ENTERSIZEMOVE: {
+ if (win->src.actionFrame)
+ RGFW_window_captureMousePlatform(win, win->internal.captureMouse);
+
+ #ifdef RGFW_ADVANCED_SMOOTH_RESIZE
+ SetTimer(win->src.window, 1, USER_TIMER_MINIMUM, NULL); break;
+ #endif
+ break;
+ }
+ case WM_EXITSIZEMOVE: {
+ if (win->src.actionFrame)
+ RGFW_window_captureMousePlatform(win, win->internal.captureMouse);
+
+ #ifdef RGFW_ADVANCED_SMOOTH_RESIZE
+ KillTimer(win->src.window, 1); break;
+ #endif
+ break;
+ }
+ case WM_TIMER:
+ RGFW_windowRefreshCallback(win);
+ break;
+
+ case WM_NCLBUTTONDOWN: {
+ /* workaround for half-second pause when starting to move window
+ see: https://gamedev.net/forums/topic/672094-keeping-things-moving-during-win32-moveresize-events/5254386/
+ */
+ POINT point = { 0, 0 };
+ if (SendMessage(win->src.window, WM_NCHITTEST, wParam, lParam) != HTCAPTION || GetCursorPos(&point) == FALSE)
+ break;
+
+ ScreenToClient(win->src.window, &point);
+ PostMessage(win->src.window, WM_MOUSEMOVE, 0, (u32)(point.x)|((u32)(point.y) << 16));
+ break;
+ }
+ case WM_MOUSELEAVE:
+ RGFW_mouseNotifyCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, RGFW_FALSE);
+ break;
+
+ case WM_CHAR:
+ case WM_SYSCHAR: {
+ if (wParam >= 0xd800 && wParam <= 0xdbff)
+ win->src.highSurrogate = (WCHAR) wParam;
+ else {
+ u32 codepoint = 0;
+
+ if (wParam >= 0xdc00 && wParam <= 0xdfff) {
+ if (win->src.highSurrogate) {
+ codepoint += (u32)((win->src.highSurrogate - 0xd800) << 10);
+ codepoint += (u32)((WCHAR) wParam - 0xdc00);
+ codepoint += 0x10000;
+ }
+ }
+ else
+ codepoint = (WCHAR) wParam;
+
+ win->src.highSurrogate = 0;
+ RGFW_keyCharCallback(win, (u32)codepoint);
+ }
+
+ return 0;
+ }
+
+ case WM_UNICHAR: {
+ if (wParam == UNICODE_NOCHAR) {
+ return TRUE;
+ }
+
+ RGFW_keyCharCallback(win, (u32)wParam);
+ return 0;
+ }
+ case WM_SYSKEYUP: case WM_KEYUP: {
+ if (!(win->internal.enabledEvents & RGFW_keyReleasedFlag)) return DefWindowProcW(hWnd, message, wParam, lParam);
+ i32 scancode = (HIWORD(lParam) & (KF_EXTENDED | 0xff));
+ if (scancode == 0)
+ scancode = (i32)MapVirtualKeyW((UINT)wParam, MAPVK_VK_TO_VSC);
+
+ switch (scancode) {
+ case 0x54: scancode = 0x137; break; /* Alt+PrtS */
+ case 0x146: scancode = 0x45; break; /* Ctrl+Pause */
+ case 0x136: scancode = 0x36; break; /* CJK IME sets the extended bit for right Shift */
+ default: break;
+ }
+
+ RGFW_key value = (u8)RGFW_apiKeyToRGFW((u32) scancode);
+
+ if (wParam == VK_CONTROL) {
+ if (HIWORD(lParam) & KF_EXTENDED)
+ value = RGFW_controlR;
+ else value = RGFW_controlL;
+ }
+
+ RGFW_bool repeat = ((lParam & 0x40000000) != 0) || RGFW_window_isKeyDown(win, value);
+
+ RGFW_updateKeyMods(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001), (GetKeyState(VK_SCROLL) & 0x0001));
+ RGFW_keyCallback(win, value, win->internal.mod, repeat, RGFW_FALSE);
+ break;
+ }
+ case WM_SYSKEYDOWN: case WM_KEYDOWN: {
+ if (!(win->internal.enabledEvents & RGFW_keyPressedFlag)) return DefWindowProcW(hWnd, message, wParam, lParam);
+ i32 scancode = (HIWORD(lParam) & (KF_EXTENDED | 0xff));
+ if (scancode == 0)
+ scancode = (i32)MapVirtualKeyW((u32)wParam, MAPVK_VK_TO_VSC);
+
+ switch (scancode) {
+ case 0x54: scancode = 0x137; break; /* Alt+PrtS */
+ case 0x146: scancode = 0x45; break; /* Ctrl+Pause */
+ case 0x136: scancode = 0x36; break; /* CJK IME sets the extended bit for right Shift */
+ default: break;
+ }
+
+ RGFW_key value = (u8)RGFW_apiKeyToRGFW((u32) scancode);
+ if (wParam == VK_CONTROL) {
+ if (HIWORD(lParam) & KF_EXTENDED)
+ value = RGFW_controlR;
+ else value = RGFW_controlL;
+ }
+
+ RGFW_bool repeat = ((lParam & 0x40000000) != 0) || RGFW_window_isKeyDown(win, value);
+
+ RGFW_updateKeyMods(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001), (GetKeyState(VK_SCROLL) & 0x0001));
+ RGFW_keyCallback(win, value, win->internal.mod, repeat, 1);
+ break;
+ }
+ case WM_MOUSEMOVE: {
+ if (win->internal.mouseInside == RGFW_FALSE) {
+ RGFW_mouseNotifyCallback(win, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), RGFW_TRUE);
+ }
+
+ if ((win->internal.rawMouse) || _RGFW->rawMouse) {
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+ }
+
+ RGFW_mousePosCallback(win, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), _RGFW->vectorX, _RGFW->vectorY);
+ break;
+ }
+ case WM_INPUT: {
+ if (!(win->internal.rawMouse || _RGFW->rawMouse)) return DefWindowProcW(hWnd, message, wParam, lParam);
+ unsigned size = sizeof(RAWINPUT);
+ static RAWINPUT raw;
+
+ GetRawInputData((HRAWINPUT)lParam, RID_INPUT, &raw, &size, sizeof(RAWINPUTHEADER));
+
+ if (raw.header.dwType != RIM_TYPEMOUSE || (raw.data.mouse.lLastX == 0 && raw.data.mouse.lLastY == 0) )
+ break;
+
+ float vecX = 0.0f;
+ float vecY = 0.0f;
+
+ if (raw.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) {
+ POINT pos = {0, 0};
+ int width, height;
+
+ if (raw.data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) {
+ pos.x += GetSystemMetrics(SM_XVIRTUALSCREEN);
+ pos.y += GetSystemMetrics(SM_YVIRTUALSCREEN);
+ width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
+ height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
+ }
+ else {
+ width = GetSystemMetrics(SM_CXSCREEN);
+ height = GetSystemMetrics(SM_CYSCREEN);
+ }
+
+ pos.x += (int) (((float)raw.data.mouse.lLastX / 65535.f) * (float)width);
+ pos.y += (int) (((float)raw.data.mouse.lLastY / 65535.f) * (float)height);
+ ScreenToClient(win->src.window, &pos);
+
+ vecX = (float)(pos.x - win->internal.lastMouseX);
+ vecY = (float)(pos.y - win->internal.lastMouseY);
+ } else {
+ vecX = (float)(raw.data.mouse.lLastX);
+ vecY = (float)(raw.data.mouse.lLastY);
+ }
+
+ RGFW_mousePosCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, vecX, vecY);
+ break;
+ }
+ case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: case WM_XBUTTONDOWN: {
+ RGFW_mouseButton value = 0;
+ if (message == WM_XBUTTONDOWN)
+ value = RGFW_mouseMisc1 + (GET_XBUTTON_WPARAM(wParam) == XBUTTON2);
+ else value = (message == WM_LBUTTONDOWN) ? (u8)RGFW_mouseLeft :
+ (message == WM_RBUTTONDOWN) ? (u8)RGFW_mouseRight : (u8)RGFW_mouseMiddle;
+
+ RGFW_mouseButtonCallback(win, value, 1);
+ break;
+ }
+ case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: case WM_XBUTTONUP: {
+ RGFW_mouseButton value = 0;
+ if (message == WM_XBUTTONUP)
+ value = RGFW_mouseMisc1 + (GET_XBUTTON_WPARAM(wParam) == XBUTTON2);
+ else value = (message == WM_LBUTTONUP) ? (u8)RGFW_mouseLeft :
+ (message == WM_RBUTTONUP) ? (u8)RGFW_mouseRight : (u8)RGFW_mouseMiddle;
+
+ RGFW_mouseButtonCallback(win, value, 0);
+ break;
+ }
+ case WM_MOUSEWHEEL: {
+ float scrollY = (float)((i16) HIWORD(wParam) / (double) WHEEL_DELTA);
+ RGFW_mouseScrollCallback(win, 0.0f, scrollY);
+ break;
+ }
+ case 0x020E: {/* WM_MOUSEHWHEEL */
+ float scrollX = -(float)((i16) HIWORD(wParam) / (double) WHEEL_DELTA);
+ RGFW_mouseScrollCallback(win, scrollX, 0.0f);
+ break;
+ }
+ case WM_DROPFILES: {
+ HDROP drop = (HDROP) wParam;
+ POINT pt;
+
+ /* Move the mouse to the position of the drop */
+ DragQueryPoint(drop, &pt);
+ RGFW_dataDragCallback(win, pt.x, pt.y);
+
+ if (!(win->internal.enabledEvents & RGFW_dataDrop)) return DefWindowProcW(hWnd, message, wParam, lParam);
+ char** files = _RGFW->files;
+ size_t count = DragQueryFileW(drop, 0xffffffff, NULL, 0);
+
+ u32 i;
+ for (i = 0; i < count; i++) {
+ UINT length = DragQueryFileW(drop, i, NULL, 0);
+ if (length == 0)
+ continue;
+
+ WCHAR buffer[RGFW_MAX_PATH * 2];
+ if (length > (RGFW_MAX_PATH * 2) - 1)
+ length = RGFW_MAX_PATH * 2;
+
+ DragQueryFileW(drop, i, buffer, length + 1);
+
+ RGFW_createUTF8FromWideStringWin32(buffer, files[i], RGFW_MAX_PATH);
+
+ files[i][RGFW_MAX_PATH - 1] = '\0';
+ }
+
+ DragFinish(drop);
+
+ RGFW_dataDropCallback(win, files, count);
+ break;
+ }
+ default: break;
+ }
+
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+}
+
+#ifndef RGFW_NO_DPI
+ HMODULE RGFW_Shcore_dll = NULL;
+ typedef HRESULT (WINAPI *PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*);
+ PFN_GetDpiForMonitor GetDpiForMonitorSRC = NULL;
+ #define GetDpiForMonitor GetDpiForMonitorSRC
+#endif
+
+#if !defined(RGFW_NO_LOAD_WINMM) && !defined(RGFW_NO_WINMM)
+ HMODULE RGFW_winmm_dll = NULL;
+ typedef u32 (WINAPI * PFN_timeBeginPeriod)(u32);
+ typedef PFN_timeBeginPeriod PFN_timeEndPeriod;
+ PFN_timeBeginPeriod timeBeginPeriodSRC, timeEndPeriodSRC;
+ #define timeBeginPeriod timeBeginPeriodSRC
+ #define timeEndPeriod timeEndPeriodSRC
+#elif !defined(RGFW_NO_WINMM)
+ __declspec(dllimport) u32 __stdcall timeBeginPeriod(u32 uPeriod);
+ __declspec(dllimport) u32 __stdcall timeEndPeriod(u32 uPeriod);
+#endif
+#define RGFW_PROC_DEF(proc, name) if (name##SRC == NULL && proc != NULL) { \
+ name##SRC = (PFN_##name)(RGFW_proc)GetProcAddress((proc), (#name)); \
+ RGFW_ASSERT(name##SRC != NULL); \
+ }
+
+RGFW_format RGFW_nativeFormat(void) { return RGFW_formatBGRA8; }
+
+RGFW_bool RGFW_createSurfacePtr(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) {
+ RGFW_ASSERT(surface != NULL);
+ surface->data = data;
+ surface->w = w;
+ surface->h = h;
+ surface->format = format;
+
+ BITMAPV5HEADER bi;
+ ZeroMemory(&bi, sizeof(bi));
+ bi.bV5Size = sizeof(bi);
+ bi.bV5Width = (i32)w;
+ bi.bV5Height = -((LONG) h);
+ bi.bV5Planes = 1;
+ bi.bV5BitCount = (format >= RGFW_formatRGBA8) ? 32 : 24;
+ bi.bV5Compression = BI_RGB;
+
+ surface->native.bitmap = CreateDIBSection(_RGFW->root->src.hdc,
+ (BITMAPINFO*) &bi, DIB_RGB_COLORS,
+ (void**) &surface->native.bitmapBits,
+ NULL, (DWORD) 0);
+
+ surface->native.format = (format >= RGFW_formatRGBA8) ? RGFW_formatBGRA8 : RGFW_formatBGR8;
+
+ if (surface->native.bitmap == NULL) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, "Failed to create DIB section.");
+ return RGFW_FALSE;
+ }
+
+ surface->native.hdcMem = CreateCompatibleDC(_RGFW->root->src.hdc);
+ SelectObject(surface->native.hdcMem, surface->native.bitmap);
+
+ return RGFW_TRUE;
+}
+
+void RGFW_surface_freePtr(RGFW_surface* surface) {
+ RGFW_ASSERT(surface != NULL);
+
+ DeleteDC(surface->native.hdcMem);
+ DeleteObject(surface->native.bitmap);
+}
+
+void RGFW_window_blitSurface(RGFW_window* win, RGFW_surface* surface) {
+ RGFW_copyImageData(surface->native.bitmapBits, surface->w, RGFW_MIN(win->h, surface->h), surface->native.format, surface->data, surface->format, surface->convertFunc);
+ BitBlt(win->src.hdc, 0, 0, RGFW_MIN(win->w, surface->w), RGFW_MIN(win->h, surface->h), surface->native.hdcMem, 0, 0, SRCCOPY);
+}
+
+void RGFW_window_setRawMouseModePlatform(RGFW_window* win, RGFW_bool state) {
+ RGFW_UNUSED(win);
+ RAWINPUTDEVICE id = { 0x01, 0x02, 0, win->src.window };
+ id.dwFlags = (state == RGFW_TRUE) ? 0 : RIDEV_REMOVE;
+
+ RegisterRawInputDevices(&id, 1, sizeof(id));
+}
+
+void RGFW_window_captureMousePlatform(RGFW_window* win, RGFW_bool state) {
+ if (state == RGFW_FALSE) {
+ ClipCursor(NULL);
+ return;
+ }
+
+ RECT clipRect;
+ GetClientRect(win->src.window, &clipRect);
+ ClientToScreen(win->src.window, (POINT*) &clipRect.left);
+ ClientToScreen(win->src.window, (POINT*) &clipRect.right);
+ ClipCursor(&clipRect);
+}
+
+#define RGFW_LOAD_LIBRARY(x, lib) if (x == NULL) { x = LoadLibraryA(lib); RGFW_ASSERT(x != NULL); }
+
+#ifdef RGFW_DIRECTX
+int RGFW_window_createSwapChain_DirectX(RGFW_window* win, IDXGIFactory* pFactory, IUnknown* pDevice, IDXGISwapChain** swapchain) {
+ RGFW_ASSERT(win && pFactory && pDevice && swapchain);
+
+ static DXGI_SWAP_CHAIN_DESC swapChainDesc;
+ RGFW_MEMSET(&swapChainDesc, 0, sizeof(swapChainDesc));
+ swapChainDesc.BufferCount = 2;
+ swapChainDesc.BufferDesc.Width = win->w;
+ swapChainDesc.BufferDesc.Height = win->h;
+ swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ swapChainDesc.OutputWindow = (HWND)win->src.window;
+ swapChainDesc.SampleDesc.Count = 1;
+ swapChainDesc.SampleDesc.Quality = 0;
+ swapChainDesc.Windowed = TRUE;
+ swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
+
+ HRESULT hr = pFactory->lpVtbl->CreateSwapChain(pFactory, (IUnknown*)pDevice, &swapChainDesc, swapchain);
+ if (FAILED(hr)) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errDirectXContext, "Failed to create DirectX swap chain!");
+ return -2;
+ }
+
+ return 0;
+}
+#endif
+
+/* we're doing it with magic numbers because some keys are missing */
+void RGFW_initKeycodesPlatform(void) {
+ _RGFW->keycodes[0x00B] = RGFW_0;
+ _RGFW->keycodes[0x002] = RGFW_1;
+ _RGFW->keycodes[0x003] = RGFW_2;
+ _RGFW->keycodes[0x004] = RGFW_3;
+ _RGFW->keycodes[0x005] = RGFW_4;
+ _RGFW->keycodes[0x006] = RGFW_5;
+ _RGFW->keycodes[0x007] = RGFW_6;
+ _RGFW->keycodes[0x008] = RGFW_7;
+ _RGFW->keycodes[0x009] = RGFW_8;
+ _RGFW->keycodes[0x00A] = RGFW_9;
+ _RGFW->keycodes[0x01E] = RGFW_a;
+ _RGFW->keycodes[0x030] = RGFW_b;
+ _RGFW->keycodes[0x02E] = RGFW_c;
+ _RGFW->keycodes[0x020] = RGFW_d;
+ _RGFW->keycodes[0x012] = RGFW_e;
+ _RGFW->keycodes[0x021] = RGFW_f;
+ _RGFW->keycodes[0x022] = RGFW_g;
+ _RGFW->keycodes[0x023] = RGFW_h;
+ _RGFW->keycodes[0x017] = RGFW_i;
+ _RGFW->keycodes[0x024] = RGFW_j;
+ _RGFW->keycodes[0x025] = RGFW_k;
+ _RGFW->keycodes[0x026] = RGFW_l;
+ _RGFW->keycodes[0x032] = RGFW_m;
+ _RGFW->keycodes[0x031] = RGFW_n;
+ _RGFW->keycodes[0x018] = RGFW_o;
+ _RGFW->keycodes[0x019] = RGFW_p;
+ _RGFW->keycodes[0x010] = RGFW_q;
+ _RGFW->keycodes[0x013] = RGFW_r;
+ _RGFW->keycodes[0x01F] = RGFW_s;
+ _RGFW->keycodes[0x014] = RGFW_t;
+ _RGFW->keycodes[0x016] = RGFW_u;
+ _RGFW->keycodes[0x02F] = RGFW_v;
+ _RGFW->keycodes[0x011] = RGFW_w;
+ _RGFW->keycodes[0x02D] = RGFW_x;
+ _RGFW->keycodes[0x015] = RGFW_y;
+ _RGFW->keycodes[0x02C] = RGFW_z;
+ _RGFW->keycodes[0x028] = RGFW_apostrophe;
+ _RGFW->keycodes[0x02B] = RGFW_backSlash;
+ _RGFW->keycodes[0x033] = RGFW_comma;
+ _RGFW->keycodes[0x00D] = RGFW_equals;
+ _RGFW->keycodes[0x029] = RGFW_backtick;
+ _RGFW->keycodes[0x01A] = RGFW_bracket;
+ _RGFW->keycodes[0x00C] = RGFW_minus;
+ _RGFW->keycodes[0x034] = RGFW_period;
+ _RGFW->keycodes[0x01B] = RGFW_closeBracket;
+ _RGFW->keycodes[0x027] = RGFW_semicolon;
+ _RGFW->keycodes[0x035] = RGFW_slash;
+ _RGFW->keycodes[0x056] = RGFW_world2;
+ _RGFW->keycodes[0x00E] = RGFW_backSpace;
+ _RGFW->keycodes[0x153] = RGFW_delete;
+ _RGFW->keycodes[0x14F] = RGFW_end;
+ _RGFW->keycodes[0x01C] = RGFW_enter;
+ _RGFW->keycodes[0x001] = RGFW_escape;
+ _RGFW->keycodes[0x147] = RGFW_home;
+ _RGFW->keycodes[0x152] = RGFW_insert;
+ _RGFW->keycodes[0x15D] = RGFW_menu;
+ _RGFW->keycodes[0x151] = RGFW_pageDown;
+ _RGFW->keycodes[0x149] = RGFW_pageUp;
+ _RGFW->keycodes[0x045] = RGFW_pause;
+ _RGFW->keycodes[0x039] = RGFW_space;
+ _RGFW->keycodes[0x00F] = RGFW_tab;
+ _RGFW->keycodes[0x03A] = RGFW_capsLock;
+ _RGFW->keycodes[0x145] = RGFW_numLock;
+ _RGFW->keycodes[0x046] = RGFW_scrollLock;
+ _RGFW->keycodes[0x03B] = RGFW_F1;
+ _RGFW->keycodes[0x03C] = RGFW_F2;
+ _RGFW->keycodes[0x03D] = RGFW_F3;
+ _RGFW->keycodes[0x03E] = RGFW_F4;
+ _RGFW->keycodes[0x03F] = RGFW_F5;
+ _RGFW->keycodes[0x040] = RGFW_F6;
+ _RGFW->keycodes[0x041] = RGFW_F7;
+ _RGFW->keycodes[0x042] = RGFW_F8;
+ _RGFW->keycodes[0x043] = RGFW_F9;
+ _RGFW->keycodes[0x044] = RGFW_F10;
+ _RGFW->keycodes[0x057] = RGFW_F11;
+ _RGFW->keycodes[0x058] = RGFW_F12;
+ _RGFW->keycodes[0x064] = RGFW_F13;
+ _RGFW->keycodes[0x065] = RGFW_F14;
+ _RGFW->keycodes[0x066] = RGFW_F15;
+ _RGFW->keycodes[0x067] = RGFW_F16;
+ _RGFW->keycodes[0x068] = RGFW_F17;
+ _RGFW->keycodes[0x069] = RGFW_F18;
+ _RGFW->keycodes[0x06A] = RGFW_F19;
+ _RGFW->keycodes[0x06B] = RGFW_F20;
+ _RGFW->keycodes[0x06C] = RGFW_F21;
+ _RGFW->keycodes[0x06D] = RGFW_F22;
+ _RGFW->keycodes[0x06E] = RGFW_F23;
+ _RGFW->keycodes[0x076] = RGFW_F24;
+ _RGFW->keycodes[0x038] = RGFW_altL;
+ _RGFW->keycodes[0x01D] = RGFW_controlL;
+ _RGFW->keycodes[0x02A] = RGFW_shiftL;
+ _RGFW->keycodes[0x15B] = RGFW_superL;
+ _RGFW->keycodes[0x137] = RGFW_printScreen;
+ _RGFW->keycodes[0x138] = RGFW_altR;
+ _RGFW->keycodes[0x11D] = RGFW_controlR;
+ _RGFW->keycodes[0x036] = RGFW_shiftR;
+ _RGFW->keycodes[0x15C] = RGFW_superR;
+ _RGFW->keycodes[0x150] = RGFW_down;
+ _RGFW->keycodes[0x14B] = RGFW_left;
+ _RGFW->keycodes[0x14D] = RGFW_right;
+ _RGFW->keycodes[0x148] = RGFW_up;
+ _RGFW->keycodes[0x052] = RGFW_kp0;
+ _RGFW->keycodes[0x04F] = RGFW_kp1;
+ _RGFW->keycodes[0x050] = RGFW_kp2;
+ _RGFW->keycodes[0x051] = RGFW_kp3;
+ _RGFW->keycodes[0x04B] = RGFW_kp4;
+ _RGFW->keycodes[0x04C] = RGFW_kp5;
+ _RGFW->keycodes[0x04D] = RGFW_kp6;
+ _RGFW->keycodes[0x047] = RGFW_kp7;
+ _RGFW->keycodes[0x048] = RGFW_kp8;
+ _RGFW->keycodes[0x049] = RGFW_kp9;
+ _RGFW->keycodes[0x04E] = RGFW_kpPlus;
+ _RGFW->keycodes[0x053] = RGFW_kpPeriod;
+ _RGFW->keycodes[0x135] = RGFW_kpSlash;
+ _RGFW->keycodes[0x11C] = RGFW_kpReturn;
+ _RGFW->keycodes[0x059] = RGFW_kpEqual;
+ _RGFW->keycodes[0x037] = RGFW_kpMultiply;
+ _RGFW->keycodes[0x04A] = RGFW_kpMinus;
+}
+
+
+i32 RGFW_initPlatform(void) {
+#ifndef RGFW_NO_DPI
+ #if (_WIN32_WINNT >= 0x0600)
+ SetProcessDPIAware();
+ #endif
+#endif
+
+ #ifndef RGFW_NO_WINMM
+ #ifndef RGFW_NO_LOAD_WINMM
+ RGFW_LOAD_LIBRARY(RGFW_winmm_dll, "winmm.dll");
+ RGFW_PROC_DEF(RGFW_winmm_dll, timeBeginPeriod);
+ RGFW_PROC_DEF(RGFW_winmm_dll, timeEndPeriod);
+ #endif
+ timeBeginPeriod(1);
+ #endif
+
+ #ifndef RGFW_NO_DWM
+ RGFW_LOAD_LIBRARY(RGFW_dwm_dll, "dwmapi.dll");
+ RGFW_PROC_DEF(RGFW_dwm_dll, DwmEnableBlurBehindWindow);
+ RGFW_PROC_DEF(RGFW_dwm_dll, DwmSetWindowAttribute);
+ #endif
+
+ RGFW_LOAD_LIBRARY(RGFW_wgl_dll, "opengl32.dll");
+ #ifndef RGFW_NO_LOAD_WGL
+ RGFW_PROC_DEF(RGFW_wgl_dll, wglCreateContext);
+ RGFW_PROC_DEF(RGFW_wgl_dll, wglDeleteContext);
+ RGFW_PROC_DEF(RGFW_wgl_dll, wglGetProcAddress);
+ RGFW_PROC_DEF(RGFW_wgl_dll, wglMakeCurrent);
+ RGFW_PROC_DEF(RGFW_wgl_dll, wglGetCurrentDC);
+ RGFW_PROC_DEF(RGFW_wgl_dll, wglGetCurrentContext);
+ RGFW_PROC_DEF(RGFW_wgl_dll, wglShareLists);
+ #endif
+
+ u8 RGFW_blk[] = { 0, 0, 0, 0 };
+ _RGFW->hiddenMouse = RGFW_loadMouse(RGFW_blk, 1, 1, RGFW_formatRGBA8);
+ return 1;
+}
+
+RGFW_window* RGFW_createWindowPlatform(const char* name, RGFW_windowFlags flags, RGFW_window* win) {
+ if (name[0] == 0) name = (char*) " ";
+ win->src.hIconSmall = win->src.hIconBig = NULL;
+ win->src.maxSizeW = 0;
+ win->src.maxSizeH = 0;
+ win->src.minSizeW = 0;
+ win->src.minSizeH = 0;
+ win->src.aspectRatioW = 0;
+ win->src.aspectRatioH = 0;
+
+ HINSTANCE inh = GetModuleHandleA(NULL);
+
+ #ifndef __cplusplus
+ WNDCLASSW Class = {0}; /*!< Setup the Window class. */
+ #else
+ WNDCLASSW Class = {};
+ #endif
+
+ if (_RGFW->className == NULL)
+ _RGFW->className = (char*)name;
+
+ wchar_t wide_class[256];
+ MultiByteToWideChar(CP_UTF8, 0, _RGFW->className, -1, wide_class, 255);
+
+ Class.lpszClassName = wide_class;
+ Class.hInstance = inh;
+ Class.hCursor = LoadCursor(NULL, IDC_ARROW);
+ Class.lpfnWndProc = WndProcW;
+ Class.cbClsExtra = sizeof(RGFW_window*);
+
+ Class.hIcon = (HICON)LoadImageA(GetModuleHandleW(NULL), "RGFW_ICON", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+ if (Class.hIcon == NULL)
+ Class.hIcon = (HICON)LoadImageA(NULL, (LPCSTR)IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+
+ RegisterClassW(&Class);
+
+ DWORD window_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
+
+ RECT windowRect, clientRect;
+
+ if (!(flags & RGFW_windowNoBorder)) {
+ window_style |= WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_MINIMIZEBOX;
+
+ if (!(flags & RGFW_windowNoResize))
+ window_style |= WS_SIZEBOX | WS_MAXIMIZEBOX;
+ } else
+ window_style |= WS_POPUP | WS_VISIBLE | WS_SYSMENU;
+
+ wchar_t wide_name[256];
+ MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_name, 255);
+ HWND dummyWin = CreateWindowW(Class.lpszClassName, (wchar_t*)wide_name, window_style, win->x, win->y, win->w, win->h, 0, 0, inh, 0);
+
+ GetWindowRect(dummyWin, &windowRect);
+ GetClientRect(dummyWin, &clientRect);
+
+#ifdef RGFW_OPENGL
+ RGFW_win32_loadOpenGLFuncs(dummyWin);
+#endif
+
+ DestroyWindow(dummyWin);
+
+ RECT rect = { 0, 0, win->w, win->h};
+ DWORD style = RGFW_winapi_window_getStyle(win, flags);
+ DWORD exStyle = RGFW_winapi_window_getExStyle(win, flags);
+ AdjustWindowRectEx(&rect, style, FALSE, exStyle);
+
+ win->src.window = CreateWindowW(Class.lpszClassName, (wchar_t*)wide_name, window_style, win->x + rect.left, win->y + rect.top, rect.right - rect.left, rect.bottom - rect.top, 0, 0, inh, 0);
+ SetPropW(win->src.window, L"RGFW", win);
+ RGFW_window_resize(win, win->w, win->h); /* so WM_GETMINMAXINFO gets called again */
+
+ if (flags & RGFW_windowAllowDND) {
+ win->internal.flags |= RGFW_windowAllowDND;
+ RGFW_window_setDND(win, 1);
+ }
+ win->src.hdc = GetDC(win->src.window);
+
+ RGFW_win32_makeWindowDarkMode(win, RGFW_win32_getDarkModeState());
+ RGFW_win32_makeWindowTransparent(win);
+ return win;
+}
+
+void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) {
+ RGFW_setBit(&win->internal.flags, RGFW_windowNoBorder, !border);
+ LONG style = GetWindowLong(win->src.window, GWL_STYLE);
+
+ if (border == 0) {
+ SetWindowLong(win->src.window, GWL_STYLE, style & ~WS_OVERLAPPEDWINDOW);
+ SetWindowPos(
+ win->src.window, HWND_TOP, 0, 0, 0, 0,
+ SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE
+ );
+ }
+ else {
+ if (win->internal.flags & RGFW_windowNoResize) style &= ~WS_MAXIMIZEBOX;
+ SetWindowLong(win->src.window, GWL_STYLE, style | WS_OVERLAPPEDWINDOW);
+ SetWindowPos(
+ win->src.window, HWND_TOP, 0, 0, 0, 0,
+ SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE
+ );
+ }
+}
+
+void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow) {
+ RGFW_setBit(&win->internal.flags, RGFW_windowAllowDND, allow);
+ DragAcceptFiles(win->src.window, allow);
+}
+
+RGFW_bool RGFW_getGlobalMouse(i32* x, i32* y) {
+ POINT p;
+ GetCursorPos(&p);
+ if (x) *x = p.x;
+ if (y) *y = p.y;
+ return RGFW_TRUE;
+}
+
+void RGFW_window_setAspectRatio(RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+ win->src.aspectRatioW = w;
+ win->src.aspectRatioH = h;
+}
+
+void RGFW_window_setMinSize(RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+ win->src.minSizeW = w;
+ win->src.minSizeH = h;
+}
+
+void RGFW_window_setMaxSize(RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+ win->src.maxSizeW = w;
+ win->src.maxSizeH = h;
+}
+
+void RGFW_window_focus(RGFW_window* win) {
+ RGFW_ASSERT(win);
+ SetForegroundWindow(win->src.window);
+ SetFocus(win->src.window);
+}
+
+void RGFW_window_raise(RGFW_window* win) {
+ RGFW_ASSERT(win);
+ BringWindowToTop(win->src.window);
+ SetWindowPos(win->src.window, HWND_TOP, win->x, win->y, win->w, win->h, SWP_NOSIZE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
+}
+
+void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) {
+ RGFW_ASSERT(win != NULL);
+
+ if (fullscreen == RGFW_FALSE) {
+ RGFW_window_setBorder(win, 1);
+
+ RECT rect = { 0, 0, win->internal.oldW, win->internal.oldH};
+ DWORD style = RGFW_winapi_window_getStyle(win, win->internal.flags);
+ DWORD exStyle = RGFW_winapi_window_getExStyle(win, win->internal.flags);
+ AdjustWindowRectEx(&rect, style, FALSE, exStyle);
+ SetWindowPos(win->src.window, HWND_TOP, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER);
+
+
+ win->internal.flags &= ~(u32)RGFW_windowFullscreen;
+ win->x = win->internal.oldX;
+ win->y = win->internal.oldY;
+ win->w = win->internal.oldW;
+ win->h = win->internal.oldH;
+ return;
+ }
+
+ win->internal.oldX = win->x;
+ win->internal.oldY = win->y;
+ win->internal.oldW = win->w;
+ win->internal.oldH = win->h;
+ win->internal.flags |= RGFW_windowFullscreen;
+
+ RGFW_monitor* mon = RGFW_window_getMonitor(win);
+
+ RGFW_window_setBorder(win, 0);
+ RGFW_monitor_scaleToWindow(mon, win);
+
+ SetWindowPos(win->src.window, HWND_TOPMOST, (i32)mon->x, (i32)mon->y, (i32)mon->mode.w, (i32)mon->mode.h, SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW);
+
+ win->x = mon->x;
+ win->y = mon->y;
+ win->w = mon->mode.w;
+ win->h = mon->mode.h;
+}
+
+void RGFW_window_maximize(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_window_hide(win);
+ ShowWindow(win->src.window, SW_MAXIMIZE);
+}
+
+void RGFW_window_minimize(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ ShowWindow(win->src.window, SW_MINIMIZE);
+}
+
+void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) {
+ RGFW_ASSERT(win != NULL);
+ if (floating) SetWindowPos(win->src.window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
+ else SetWindowPos(win->src.window, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
+}
+
+void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) {
+ SetWindowLong(win->src.window, GWL_EXSTYLE, WS_EX_LAYERED);
+ SetLayeredWindowAttributes(win->src.window, 0, opacity, LWA_ALPHA);
+}
+
+void RGFW_window_restore(RGFW_window* win) { RGFW_window_show(win); }
+
+RGFW_bool RGFW_window_isFloating(RGFW_window* win) {
+ return (GetWindowLongPtr(win->src.window, GWL_EXSTYLE) & WS_EX_TOPMOST) != 0;
+}
+
+void RGFW_stopCheckEvents(void) {
+ PostMessageW(_RGFW->root->src.window, WM_NULL, 0, 0);
+}
+
+void RGFW_waitForEvent(i32 waitMS) {
+ MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD)waitMS, QS_ALLINPUT);
+}
+
+RGFW_key RGFW_physicalToMappedKey(RGFW_key key) {
+ UINT vsc = RGFW_rgfwToApiKey(key);
+ BYTE keyboardState[256] = {0};
+
+ if (!GetKeyboardState(keyboardState))
+ return key;
+
+ UINT vk = MapVirtualKeyW(vsc, MAPVK_VSC_TO_VK);
+ HKL layout = GetKeyboardLayout(0);
+
+ wchar_t charBuffer[4] = {0};
+ int result = ToUnicodeEx(vk, vsc, keyboardState, charBuffer, 1, 0, layout);
+
+ if (result == 1 && charBuffer[0] < 256) {
+ return (RGFW_key)charBuffer[0];
+ }
+
+ switch (vk) {
+ case VK_F1: return RGFW_F1;
+ case VK_F2: return RGFW_F2;
+ case VK_F3: return RGFW_F3;
+ case VK_F4: return RGFW_F4;
+ case VK_F5: return RGFW_F5;
+ case VK_F6: return RGFW_F6;
+ case VK_F7: return RGFW_F7;
+ case VK_F8: return RGFW_F8;
+ case VK_F9: return RGFW_F9;
+ case VK_F10: return RGFW_F10;
+ case VK_F11: return RGFW_F11;
+ case VK_F12: return RGFW_F12;
+ case VK_F13: return RGFW_F13;
+ case VK_F14: return RGFW_F14;
+ case VK_F15: return RGFW_F15;
+ case VK_F16: return RGFW_F16;
+ case VK_F17: return RGFW_F17;
+ case VK_F18: return RGFW_F18;
+ case VK_F19: return RGFW_F19;
+ case VK_F20: return RGFW_F20;
+ case VK_F21: return RGFW_F21;
+ case VK_F22: return RGFW_F22;
+ case VK_F23: return RGFW_F23;
+ case VK_F24: return RGFW_F24;
+ case VK_LSHIFT: return RGFW_shiftL;
+ case VK_RSHIFT: return RGFW_shiftR;
+ case VK_LCONTROL: return RGFW_controlL;
+ case VK_RCONTROL: return RGFW_controlR;
+ case VK_LMENU: return RGFW_altL;
+ case VK_RMENU: return RGFW_altR;
+ case VK_LWIN: return RGFW_superL;
+ case VK_RWIN: return RGFW_superR;
+ case VK_CAPITAL: return RGFW_capsLock;
+ case VK_NUMLOCK: return RGFW_numLock;
+ case VK_SCROLL: return RGFW_scrollLock;
+ case VK_UP: return RGFW_up;
+ case VK_DOWN: return RGFW_down;
+ case VK_LEFT: return RGFW_left;
+ case VK_RIGHT: return RGFW_right;
+ case VK_HOME: return RGFW_home;
+ case VK_END: return RGFW_end;
+ case VK_PRIOR: return RGFW_pageUp;
+ case VK_NEXT: return RGFW_pageDown;
+ case VK_INSERT: return RGFW_insert;
+ case VK_APPS: return RGFW_menu;
+ case VK_ADD: return RGFW_kpPlus;
+ case VK_SUBTRACT: return RGFW_kpMinus;
+ case VK_MULTIPLY: return RGFW_kpMultiply;
+ case VK_DIVIDE: return RGFW_kpSlash;
+ case VK_RETURN: return RGFW_kpReturn;
+ case VK_DECIMAL: return RGFW_kpPeriod;
+ case VK_NUMPAD0: return RGFW_kp0;
+ case VK_NUMPAD1: return RGFW_kp1;
+ case VK_NUMPAD2: return RGFW_kp2;
+ case VK_NUMPAD3: return RGFW_kp3;
+ case VK_NUMPAD4: return RGFW_kp4;
+ case VK_NUMPAD5: return RGFW_kp5;
+ case VK_NUMPAD6: return RGFW_kp6;
+ case VK_NUMPAD7: return RGFW_kp7;
+ case VK_NUMPAD8: return RGFW_kp8;
+ case VK_NUMPAD9: return RGFW_kp9;
+ case VK_SNAPSHOT: return RGFW_printScreen;
+ case VK_PAUSE: return RGFW_pause;
+ default: return RGFW_keyNULL;
+ }
+
+ return RGFW_keyNULL;
+}
+
+void RGFW_pollEvents(void) {
+ RGFW_resetPrevState();
+ MSG msg;
+ while (PeekMessageA(&msg, NULL, 0u, 0u, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessageA(&msg);
+ }
+}
+
+RGFW_bool RGFW_window_isHidden(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ return IsWindowVisible(win->src.window) == 0 && !RGFW_window_isMinimized(win);
+}
+
+RGFW_bool RGFW_window_isMinimized(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+
+ #ifndef __cplusplus
+ WINDOWPLACEMENT placement = {0};
+ #else
+ WINDOWPLACEMENT placement = {};
+ #endif
+ GetWindowPlacement(win->src.window, &placement);
+ return placement.showCmd == SW_SHOWMINIMIZED;
+}
+
+RGFW_bool RGFW_window_isMaximized(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+
+ #ifndef __cplusplus
+ WINDOWPLACEMENT placement = {0};
+ #else
+ WINDOWPLACEMENT placement = {};
+ #endif
+ GetWindowPlacement(win->src.window, &placement);
+ return placement.showCmd == SW_SHOWMAXIMIZED || IsZoomed(win->src.window);
+}
+
+RGFW_bool RGFW_monitor_getWorkarea(RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height) {
+ MONITORINFOEX mi;
+ mi.cbSize = sizeof(MONITORINFOEX);
+ GetMonitorInfoA(monitor->node->hMonitor, (LPMONITORINFO)&mi);
+
+ if (x) *x = mi.rcWork.left;
+ if (y) *y = mi.rcWork.top;
+ if (width) *width = mi.rcWork.right - mi.rcWork.left;
+ if (height) *height = mi.rcWork.bottom - mi.rcWork.top;
+
+ return RGFW_TRUE;
+}
+
+size_t RGFW_monitor_getGammaRampPtr(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) {
+ WORD values[3][256];
+
+ HDC dc = CreateDCW(L"DISPLAY", monitor->node->adapterName, NULL, NULL);
+ GetDeviceGammaRamp(dc, values);
+ DeleteDC(dc);
+
+ if (ramp) {
+ memcpy(ramp->red, values[0], sizeof(values[0]));
+ memcpy(ramp->green, values[1], sizeof(values[1]));
+ memcpy(ramp->blue, values[2], sizeof(values[2]));
+ }
+
+ return sizeof(values[0]) / sizeof(WORD);
+}
+
+RGFW_bool RGFW_monitor_setGammaRamp(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) {
+ WORD values[3][256];
+ if (ramp->count != 256) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errX11, "Win32: Gamma ramp size must be 256");
+ return RGFW_FALSE;
+ }
+
+ memcpy(values[0], ramp->red, sizeof(values[0]));
+ memcpy(values[1], ramp->green, sizeof(values[1]));
+ memcpy(values[2], ramp->blue, sizeof(values[2]));
+
+ HDC dc = CreateDCW(L"DISPLAY", monitor->node->adapterName, NULL, NULL);
+ SetDeviceGammaRamp(dc, values);
+ DeleteDC(dc);
+ return RGFW_TRUE;
+}
+
+BOOL CALLBACK RGFW_win32_getMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData);
+BOOL CALLBACK RGFW_win32_getMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
+ RGFW_UNUSED(hMonitor);
+ RGFW_UNUSED(hdcMonitor);
+ RGFW_UNUSED(lprcMonitor);
+ RGFW_UNUSED(dwData);
+
+ MONITORINFOEXW mi;
+ ZeroMemory(&mi, sizeof(mi));
+ mi.cbSize = sizeof(mi);
+
+ if (GetMonitorInfoW(hMonitor, (MONITORINFO*) &mi)) {
+ RGFW_monitorNode* node = (RGFW_monitorNode*)dwData;
+ if (wcscmp(mi.szDevice, node->adapterName) == 0) {
+ node->hMonitor = hMonitor;
+ }
+ }
+
+ return TRUE;
+}
+
+RGFWDEF void RGFW_win32_getMode(DEVMODEW* dm, RGFW_monitorMode* mode);
+void RGFW_win32_getMode(DEVMODEW* dm, RGFW_monitorMode* mode) {
+ mode->w = (i32)dm->dmPelsWidth;
+ mode->h = (i32)dm->dmPelsHeight;
+ RGFW_splitBPP(dm->dmBitsPerPel, mode);
+
+ switch (dm->dmDisplayFrequency) {
+ case 119:
+ case 59:
+ case 29:
+ mode->refreshRate = ((float)(dm->dmDisplayFrequency + 1) * 1000.0f) / 1001.f;
+ break;
+ default:
+ mode->refreshRate = (float)dm->dmDisplayFrequency;
+ break;
+ }
+}
+
+size_t RGFW_monitor_getModesPtr(RGFW_monitor* monitor, RGFW_monitorMode** modes){
+ size_t count = 0;
+ DWORD modeIndex = 0;
+
+ for (;;) {
+ DEVMODEW dm;
+ ZeroMemory(&dm, sizeof(dm));
+ dm.dmSize = sizeof(dm);
+
+ if (!EnumDisplaySettingsW(monitor->node->adapterName, modeIndex, &dm))
+ break;
+
+ modeIndex++;
+
+ if (dm.dmBitsPerPel < 15)
+ continue;
+
+ if (modes) {
+ RGFW_monitorMode mode;
+ RGFW_win32_getMode(&dm, &mode);
+
+ size_t i;
+ for (i = 0; i < count; i++) {
+ if (RGFW_monitorModeCompare(&(*modes)[i], &mode, RGFW_monitorAll) == RGFW_TRUE) {
+ break;
+ }
+ }
+
+ if (i < count) {
+ continue;
+ }
+
+ (*modes)[count] = mode;
+ }
+
+ count += 1;
+ }
+
+ return count;
+}
+
+RGFWDEF void RGFW_win32_createMonitor(DISPLAY_DEVICEW* adapter, DISPLAY_DEVICEW* dd);
+void RGFW_win32_createMonitor(DISPLAY_DEVICEW* adapter, DISPLAY_DEVICEW* dd) {
+ DEVMODEW dm;
+ ZeroMemory(&dm, sizeof(dm));
+ dm.dmSize = sizeof(dm);
+
+ if (!EnumDisplaySettingsW(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &dm)) {
+ return;
+ }
+
+ RGFW_monitorNode* node = RGFW_monitors_add(NULL);
+
+ wcscpy(node->adapterName, adapter->DeviceName);
+ wcscpy(node->deviceName, dd->DeviceName);
+
+ RGFW_createUTF8FromWideStringWin32(dd->DeviceString, node->mon.name, sizeof(node->mon.name));
+ node->mon.name[sizeof(node->mon.name) - 1] = '\0';
+
+ RECT rect;
+ rect.left = (LONG)dm.dmPosition.x;
+ rect.top = (LONG)dm.dmPosition.y;
+ rect.right = (LONG)((LONG)dm.dmPosition.x + (LONG)dm.dmPelsWidth);
+ rect.bottom = (LONG)((long)dm.dmPosition.y + (LONG)dm.dmPelsHeight);
+ EnumDisplayMonitors(NULL, &rect, RGFW_win32_getMonitorHandle, (LPARAM)node);
+
+ RGFW_win32_getMode(&dm, &node->mon.mode);
+
+ MONITORINFOEXW monitorInfo;
+ monitorInfo.cbSize = sizeof(MONITORINFOEXW);
+ GetMonitorInfoW(node->hMonitor, (LPMONITORINFO)&monitorInfo);
+
+ node->mon.x = monitorInfo.rcMonitor.left;
+ node->mon.y = monitorInfo.rcMonitor.top;
+
+ HDC hdc = CreateDCW(monitorInfo.szDevice, NULL, NULL, NULL);
+ /* get pixels per inch */
+ float dpiX = (float)GetDeviceCaps(hdc, LOGPIXELSX);
+ float dpiY = (float)GetDeviceCaps(hdc, LOGPIXELSX);
+
+ node->mon.scaleX = dpiX / 96.0f;
+ node->mon.scaleY = dpiY / 96.0f;
+ node->mon.pixelRatio = dpiX >= 192.0f ? 2.0f : 1.0f;
+
+ node->mon.physW = (float)GetDeviceCaps(hdc, HORZSIZE) / 25.4f;
+ node->mon.physH = (float)GetDeviceCaps(hdc, VERTSIZE) / 25.4f;
+ DeleteDC(hdc);
+
+#ifndef RGFW_NO_DPI
+ RGFW_LOAD_LIBRARY(RGFW_Shcore_dll, "shcore.dll");
+ RGFW_PROC_DEF(RGFW_Shcore_dll, GetDpiForMonitor);
+
+ if (GetDpiForMonitor != NULL) {
+ u32 x, y;
+ GetDpiForMonitor(node->hMonitor, MDT_EFFECTIVE_DPI, &x, &y);
+ node->mon.scaleX = (float) (x) / (float) 96.0f;
+ node->mon.scaleY = (float) (y) / (float) 96.0f;
+ node->mon.pixelRatio = dpiX >= 192.0f ? 2.0f : 1.0f;
+ }
+#endif
+
+ if (monitorInfo.dwFlags & MONITORINFOF_PRIMARY) {
+ _RGFW->monitors.primary = node;
+ }
+
+ RGFW_monitorCallback(_RGFW->root, &node->mon, RGFW_TRUE);
+}
+
+void RGFW_pollMonitors(void) {
+ for (RGFW_monitorNode* node = _RGFW->monitors.list.head; node; node = node->next) {
+ node->disconnected = RGFW_TRUE;
+ }
+
+ /* loop through display adapters (GPU) */
+ DISPLAY_DEVICEW adapter;
+ DWORD adapterNum;
+ for (adapterNum = 0; ; adapterNum++) {
+ ZeroMemory(&adapter, sizeof(adapter));
+ adapter.cb = sizeof(adapter);
+
+ if (!EnumDisplayDevicesW(NULL, adapterNum, &adapter, 0))
+ break;
+
+ if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE))
+ continue;
+
+ DISPLAY_DEVICEW dd;
+ dd.cb = sizeof(dd);
+
+ /* loop through display devices (monitors) */
+ DWORD deviceNum;
+ for (deviceNum = 0; ; deviceNum++) {
+ ZeroMemory(&dd, sizeof(dd));
+ dd.cb = sizeof(dd);
+
+ if (!EnumDisplayDevicesW(adapter.DeviceName, deviceNum, &dd, 0))
+ break;
+
+ if (!(dd.StateFlags & DISPLAY_DEVICE_ACTIVE))
+ continue;
+
+ RGFW_monitorNode* node;
+ for (node = _RGFW->monitors.list.head; node; node = node->next) {
+ if (node->disconnected == RGFW_TRUE && wcscmp(node->deviceName, dd.DeviceName) == 0) {
+ node->disconnected = RGFW_FALSE;
+ EnumDisplayMonitors(NULL, NULL, RGFW_win32_getMonitorHandle, (LPARAM) &node->mon);
+ break;
+ }
+ }
+
+ if (node) {
+ continue;
+ }
+
+ RGFW_win32_createMonitor(&adapter, &dd);
+ }
+
+ /* if there are no display devices, just use the monitor directly (hack borrowed from GLFW (I'm not giving it back)) */
+ if (deviceNum == 0) {
+ RGFW_monitorNode* node;
+ for (node = _RGFW->monitors.list.head; node; node = node->next) {
+ if (node->disconnected == RGFW_TRUE && wcscmp(node->adapterName, adapter.DeviceName) == 0) {
+ node->disconnected = RGFW_FALSE;
+ break;
+ }
+ }
+
+ if (node) {
+ continue;
+ }
+
+ RGFW_win32_createMonitor(&adapter, NULL);
+ }
+ }
+
+ RGFW_monitors_refresh();
+}
+
+RGFW_monitor* RGFW_window_getMonitor(RGFW_window* win) {
+ HMONITOR src = MonitorFromWindow(win->src.window, MONITOR_DEFAULTTOPRIMARY);
+ RGFW_monitorNode* node = _RGFW->monitors.list.head;
+
+ for (node = _RGFW->monitors.list.head; node; node = node->next) {
+ if (node->hMonitor == src) {
+ return &node->mon;
+ }
+ }
+
+ return RGFW_getPrimaryMonitor();
+}
+
+RGFW_bool RGFW_monitor_setMode(RGFW_monitor* mon, RGFW_monitorMode* mode) {
+ DEVMODEW dm;
+ ZeroMemory(&dm, sizeof(dm));
+ dm.dmSize = sizeof(dm);
+
+ dm.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT;
+ dm.dmPelsWidth = (u32)mode->w;
+ dm.dmPelsHeight = (u32)mode->h;
+
+ dm.dmFields |= DM_DISPLAYFREQUENCY;
+ dm.dmDisplayFrequency = (DWORD)mode->refreshRate;
+
+ dm.dmFields |= DM_BITSPERPEL;
+ dm.dmBitsPerPel = (DWORD)(mode->red + mode->green + mode->blue);
+
+ if (ChangeDisplaySettingsExW(mon->node->adapterName, &dm, NULL, CDS_TEST, NULL) == DISP_CHANGE_SUCCESSFUL) {
+ if (ChangeDisplaySettingsExW(mon->node->adapterName, &dm, NULL, CDS_UPDATEREGISTRY, NULL) == DISP_CHANGE_SUCCESSFUL)
+ return RGFW_TRUE;
+ return RGFW_FALSE;
+ } else return RGFW_FALSE;
+}
+
+RGFW_bool RGFW_monitor_requestMode(RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request) {
+ HMONITOR src = mon->node->hMonitor;
+
+ MONITORINFOEX monitorInfo;
+ monitorInfo.cbSize = sizeof(MONITORINFOEX);
+ GetMonitorInfoA(src, (LPMONITORINFO)&monitorInfo);
+
+ DEVMODEW dm;
+ ZeroMemory(&dm, sizeof(dm));
+ dm.dmSize = sizeof(dm);
+
+ if (EnumDisplaySettingsW(mon->node->adapterName, ENUM_CURRENT_SETTINGS, &dm)) {
+ if (request & RGFW_monitorScale) {
+ dm.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT;
+ dm.dmPelsWidth = (u32)mode->w;
+ dm.dmPelsHeight = (u32)mode->h;
+ }
+
+ if (request & RGFW_monitorRefresh) {
+ dm.dmFields |= DM_DISPLAYFREQUENCY;
+ dm.dmDisplayFrequency = (DWORD)mode->refreshRate;
+ }
+
+ if (request & RGFW_monitorRGB) {
+ dm.dmFields |= DM_BITSPERPEL;
+ dm.dmBitsPerPel = (DWORD)(mode->red + mode->green + mode->blue);
+ }
+
+ if (ChangeDisplaySettingsExW(mon->node->adapterName, &dm, NULL, CDS_TEST, NULL) == DISP_CHANGE_SUCCESSFUL) {
+ if (ChangeDisplaySettingsExW(mon->node->adapterName, &dm, NULL, CDS_UPDATEREGISTRY, NULL) == DISP_CHANGE_SUCCESSFUL) {
+ RGFW_win32_getMode(&dm, &mon->mode);
+ return RGFW_TRUE;
+ }
+ return RGFW_FALSE;
+ } else return RGFW_FALSE;
+ }
+
+ return RGFW_FALSE;
+}
+
+HICON RGFW_loadHandleImage(u8* data, i32 w, i32 h, RGFW_format format, BOOL icon);
+HICON RGFW_loadHandleImage(u8* data, i32 w, i32 h, RGFW_format format, BOOL icon) {
+ BITMAPV5HEADER bi;
+ ZeroMemory(&bi, sizeof(bi));
+ bi.bV5Size = sizeof(bi);
+ bi.bV5Width = (i32)w;
+ bi.bV5Height = -((LONG) h);
+ bi.bV5Planes = 1;
+ bi.bV5BitCount = (WORD)32;
+ bi.bV5Compression = BI_RGB;
+ HDC dc = GetDC(NULL);
+ u8* target = NULL;
+
+ HBITMAP color = CreateDIBSection(dc,
+ (BITMAPINFO*) &bi, DIB_RGB_COLORS, (void**) &target,
+ NULL, (DWORD) 0);
+
+ RGFW_copyImageData(target, w, h, RGFW_formatBGRA8, data, format, NULL);
+ ReleaseDC(NULL, dc);
+
+ HBITMAP mask = CreateBitmap((i32)w, (i32)h, 1, 1, NULL);
+
+ ICONINFO ii;
+ ZeroMemory(&ii, sizeof(ii));
+ ii.fIcon = icon;
+ ii.xHotspot = (u32)w / 2;
+ ii.yHotspot = (u32)h / 2;
+ ii.hbmMask = mask;
+ ii.hbmColor = color;
+
+ HICON handle = CreateIconIndirect(&ii);
+
+ DeleteObject(color);
+ DeleteObject(mask);
+
+ return handle;
+}
+RGFW_mouse* RGFW_loadMouse(u8* data, i32 w, i32 h, RGFW_format format) {
+ HCURSOR cursor = (HCURSOR) RGFW_loadHandleImage(data, w, h, format, FALSE);
+ return cursor;
+}
+
+void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) {
+ RGFW_ASSERT(win && mouse);
+ SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) mouse);
+ SetCursor((HCURSOR)mouse);
+}
+
+void RGFW_freeMouse(RGFW_mouse* mouse) {
+ RGFW_ASSERT(mouse);
+ DestroyCursor((HCURSOR)mouse);
+}
+
+RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) {
+ return RGFW_window_setMouseStandard(win, RGFW_mouseArrow);
+}
+
+RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) {
+ RGFW_ASSERT(win != NULL);
+
+ u32 mouseIcon = 0;
+
+ switch (mouse) {
+ case RGFW_mouseNormal: mouseIcon = OCR_NORMAL; break;
+ case RGFW_mouseArrow: mouseIcon = OCR_NORMAL; break;
+ case RGFW_mouseIbeam: mouseIcon = OCR_IBEAM; break;
+ case RGFW_mouseWait: mouseIcon = OCR_WAIT; break;
+ case RGFW_mouseCrosshair: mouseIcon = OCR_CROSS; break;
+ case RGFW_mouseProgress: mouseIcon = OCR_APPSTARTING; break;
+ case RGFW_mouseResizeNWSE: mouseIcon = OCR_SIZENWSE; break;
+ case RGFW_mouseResizeNESW: mouseIcon = OCR_SIZENESW; break;
+ case RGFW_mouseResizeEW: mouseIcon = OCR_SIZEWE; break;
+ case RGFW_mouseResizeNS: mouseIcon = OCR_SIZENS; break;
+ case RGFW_mouseResizeAll: mouseIcon = OCR_SIZEALL; break;
+ case RGFW_mouseNotAllowed: mouseIcon = OCR_NO; break;
+ case RGFW_mousePointingHand: mouseIcon = OCR_HAND; break;
+ case RGFW_mouseResizeNW: mouseIcon = OCR_SIZENWSE; break;
+ case RGFW_mouseResizeN: mouseIcon = OCR_SIZENS; break;
+ case RGFW_mouseResizeNE: mouseIcon = OCR_SIZENESW; break;
+ case RGFW_mouseResizeE: mouseIcon = OCR_SIZEWE; break;
+ case RGFW_mouseResizeSE: mouseIcon = OCR_SIZENWSE; break;
+ case RGFW_mouseResizeS: mouseIcon = OCR_SIZENS; break;
+ case RGFW_mouseResizeSW: mouseIcon = OCR_SIZENESW; break;
+ case RGFW_mouseResizeW: mouseIcon = OCR_SIZEWE; break;
+ default: return RGFW_FALSE;
+ }
+
+ char* icon = MAKEINTRESOURCEA(mouseIcon);
+
+ SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) LoadCursorA(NULL, icon));
+ SetCursor(LoadCursorA(NULL, icon));
+ return RGFW_TRUE;
+}
+
+void RGFW_window_hide(RGFW_window* win) {
+ ShowWindow(win->src.window, SW_HIDE);
+}
+
+void RGFW_window_show(RGFW_window* win) {
+ if (win->internal.flags & RGFW_windowFocusOnShow) RGFW_window_focus(win);
+ ShowWindow(win->src.window, SW_RESTORE);
+}
+
+void RGFW_window_flash(RGFW_window* win, RGFW_flashRequest request) {
+ if (RGFW_window_isInFocus(win) && request) {
+ return;
+ }
+
+ FLASHWINFO desc;
+ RGFW_MEMSET(&desc, 0, sizeof(desc));
+
+ desc.cbSize = sizeof(desc);
+ desc.hwnd = win->src.window;
+
+ switch (request) {
+ case RGFW_flashCancel:
+ desc.dwFlags = FLASHW_STOP;
+ break;
+ case RGFW_flashBriefly:
+ desc.dwFlags = FLASHW_TRAY;
+ desc.uCount = 1;
+ break;
+ case RGFW_flashUntilFocused:
+ desc.dwFlags = (FLASHW_TRAY | FLASHW_TIMERNOFG);
+ break;
+ default: break;
+ }
+
+ FlashWindowEx(&desc);
+}
+
+#define RGFW_FREE_LIBRARY(x) if (x != NULL) FreeLibrary(x); x = NULL;
+void RGFW_deinitPlatform(void) {
+ #ifndef RGFW_NO_DPI
+ RGFW_FREE_LIBRARY(RGFW_Shcore_dll);
+ #endif
+
+ #ifndef RGFW_NO_WINMM
+ timeEndPeriod(1);
+ #ifndef RGFW_NO_LOAD_WINMM
+ RGFW_FREE_LIBRARY(RGFW_winmm_dll);
+ #endif
+ #endif
+
+ RGFW_FREE_LIBRARY(RGFW_wgl_dll);
+
+ RGFW_freeMouse(_RGFW->hiddenMouse);
+}
+
+
+void RGFW_window_closePlatform(RGFW_window* win) {
+ RemovePropW(win->src.window, L"RGFW");
+ ReleaseDC(win->src.window, win->src.hdc); /*!< delete device context */
+ DestroyWindow(win->src.window); /*!< delete window */
+
+ if (win->src.hIconSmall) DestroyIcon(win->src.hIconSmall);
+ if (win->src.hIconBig) DestroyIcon(win->src.hIconBig);
+}
+
+void RGFW_window_move(RGFW_window* win, i32 x, i32 y) {
+ RGFW_ASSERT(win != NULL);
+
+ win->x = x;
+ win->y = y;
+ SetWindowPos(win->src.window, HWND_TOP, win->x, win->y, 0, 0, SWP_NOSIZE);
+}
+
+void RGFW_window_resize(RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+
+ win->w = w;
+ win->h = h;
+
+ RECT rect = { 0, 0, w, h};
+ DWORD style = RGFW_winapi_window_getStyle(win, win->internal.flags);
+ DWORD exStyle = RGFW_winapi_window_getExStyle(win, win->internal.flags);
+ AdjustWindowRectEx(&rect, style, FALSE, exStyle);
+
+ SetWindowPos(win->src.window, HWND_TOP, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER);
+}
+
+void RGFW_window_setName(RGFW_window* win, const char* name) {
+ RGFW_ASSERT(win != NULL);
+ if (name == NULL) name = "\0";
+
+ wchar_t wide_name[256];
+ MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_name, 256);
+ SetWindowTextW(win->src.window, wide_name);
+}
+
+#ifndef RGFW_NO_PASSTHROUGH
+void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) {
+ RGFW_ASSERT(win != NULL);
+ COLORREF key = 0;
+ BYTE alpha = 0;
+ DWORD flags = 0;
+ i32 exStyle = GetWindowLongW(win->src.window, GWL_EXSTYLE);
+
+ if (exStyle & WS_EX_LAYERED)
+ GetLayeredWindowAttributes(win->src.window, &key, &alpha, &flags);
+
+ if (passthrough)
+ exStyle |= (WS_EX_TRANSPARENT | WS_EX_LAYERED);
+ else {
+ exStyle &= ~WS_EX_TRANSPARENT;
+ if (exStyle & WS_EX_LAYERED && !(flags & LWA_ALPHA))
+ exStyle &= ~WS_EX_LAYERED;
+ }
+
+ SetWindowLongW(win->src.window, GWL_EXSTYLE, exStyle);
+
+ if (passthrough)
+ SetLayeredWindowAttributes(win->src.window, key, alpha, flags);
+}
+#endif
+
+RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_icon type) {
+ RGFW_ASSERT(win != NULL);
+ #ifndef RGFW_WIN95
+ if (win->src.hIconSmall && (type & RGFW_iconWindow)) DestroyIcon(win->src.hIconSmall);
+ if (win->src.hIconBig && (type & RGFW_iconTaskbar)) DestroyIcon(win->src.hIconBig);
+
+ if (data == NULL) {
+ HICON defaultIcon = LoadIcon(NULL, IDI_APPLICATION);
+ if (type & RGFW_iconWindow)
+ SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)defaultIcon);
+ if (type & RGFW_iconTaskbar)
+ SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)defaultIcon);
+ return RGFW_TRUE;
+ }
+
+ if (type & RGFW_iconWindow) {
+ win->src.hIconSmall = RGFW_loadHandleImage(data, w, h, format, TRUE);
+ SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)win->src.hIconSmall);
+ }
+ if (type & RGFW_iconTaskbar) {
+ win->src.hIconBig = RGFW_loadHandleImage(data, w, h, format, TRUE);
+ SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)win->src.hIconBig);
+ }
+ return RGFW_TRUE;
+ #else
+ RGFW_UNUSED(img);
+ RGFW_UNUSED(type);
+ return RGFW_FALSE;
+ #endif
+}
+
+RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) {
+ /* Open the clipboard */
+ if (OpenClipboard(NULL) == 0)
+ return -1;
+
+ /* Get the clipboard data as a Unicode string */
+ HANDLE hData = GetClipboardData(CF_UNICODETEXT);
+ if (hData == NULL) {
+ CloseClipboard();
+ return -1;
+ }
+
+ wchar_t* wstr = (wchar_t*) GlobalLock(hData);
+
+ RGFW_ssize_t textLen = 0;
+
+ {
+ setlocale(LC_ALL, "en_US.UTF-8");
+
+ textLen = (RGFW_ssize_t)wcstombs(NULL, wstr, 0) + 1;
+ if (str != NULL && (RGFW_ssize_t)strCapacity <= textLen - 1)
+ textLen = 0;
+
+ if (str != NULL && textLen) {
+ if (textLen > 1)
+ wcstombs(str, wstr, (size_t)(textLen));
+
+ str[textLen - 1] = '\0';
+ }
+ }
+
+ /* Release the clipboard data */
+ GlobalUnlock(hData);
+ CloseClipboard();
+
+ return textLen;
+}
+
+void RGFW_writeClipboard(const char* text, u32 textLen) {
+ HANDLE object;
+ WCHAR* buffer;
+
+ object = GlobalAlloc(GMEM_MOVEABLE, (1 + textLen) * sizeof(WCHAR));
+ if (!object)
+ return;
+
+ buffer = (WCHAR*) GlobalLock(object);
+ if (!buffer) {
+ GlobalFree(object);
+ return;
+ }
+
+ MultiByteToWideChar(CP_UTF8, 0, text, -1, buffer, (i32)textLen);
+ GlobalUnlock(object);
+
+ if (!OpenClipboard(_RGFW->root->src.window)) {
+ GlobalFree(object);
+ return;
+ }
+
+ EmptyClipboard();
+ SetClipboardData(CF_UNICODETEXT, object);
+ CloseClipboard();
+}
+
+void RGFW_window_moveMouse(RGFW_window* win, i32 x, i32 y) {
+ RGFW_ASSERT(win != NULL);
+ win->internal.lastMouseX = x - win->x;
+ win->internal.lastMouseY = y - win->y;
+ SetCursorPos(x, y);
+}
+
+#ifdef RGFW_OPENGL
+RGFW_bool RGFW_extensionSupportedPlatform_OpenGL(const char * extension, size_t len) {
+ const char* extensions = NULL;
+
+ RGFW_proc proc = RGFW_getProcAddress_OpenGL("wglGetExtensionsStringARB");
+ RGFW_proc proc2 = RGFW_getProcAddress_OpenGL("wglGetExtensionsStringEXT");
+
+ if (proc)
+ extensions = ((const char* (*)(HDC))proc)(wglGetCurrentDC());
+ else if (proc2)
+ extensions = ((const char*(*)(void))proc2)();
+ return extensions != NULL && RGFW_extensionSupportedStr(extensions, extension, len);
+}
+
+RGFW_proc RGFW_getProcAddress_OpenGL(const char* procname) {
+ RGFW_proc proc = (RGFW_proc)wglGetProcAddress(procname);
+ if (proc)
+ return proc;
+
+ return (RGFW_proc) GetProcAddress(RGFW_wgl_dll, procname);
+}
+
+void RGFW_win32_loadOpenGLFuncs(HWND dummyWin) {
+ if (wglSwapIntervalEXT != NULL && wglChoosePixelFormatARB != NULL && wglChoosePixelFormatARB != NULL)
+ return;
+
+ HDC dummy_dc = GetDC(dummyWin);
+ u32 pfd_flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
+
+ PIXELFORMATDESCRIPTOR pfd = {sizeof(pfd), 1, pfd_flags, PFD_TYPE_RGBA, 32, 8, PFD_MAIN_PLANE, 32, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 32, 8, 0, PFD_MAIN_PLANE, 0, 0, 0, 0};
+
+ int dummy_pixel_format = ChoosePixelFormat(dummy_dc, &pfd);
+ SetPixelFormat(dummy_dc, dummy_pixel_format, &pfd);
+
+ HGLRC dummy_context = wglCreateContext(dummy_dc);
+
+ HGLRC cur = wglGetCurrentContext();
+ wglMakeCurrent(dummy_dc, dummy_context);
+
+ wglCreateContextAttribsARB = ((PFNWGLCREATECONTEXTATTRIBSARBPROC(WINAPI *)(const char*)) wglGetProcAddress)("wglCreateContextAttribsARB");
+ wglChoosePixelFormatARB = ((PFNWGLCHOOSEPIXELFORMATARBPROC(WINAPI *)(const char*)) wglGetProcAddress)("wglChoosePixelFormatARB");
+
+ wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)(RGFW_proc)wglGetProcAddress("wglSwapIntervalEXT");
+ if (wglSwapIntervalEXT == NULL) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to load swap interval function");
+ }
+
+ wglMakeCurrent(dummy_dc, cur);
+ wglDeleteContext(dummy_context);
+ ReleaseDC(dummyWin, dummy_dc);
+}
+
+#define WGL_ACCELERATION_ARB 0x2003
+#define WGL_FULL_ACCELERATION_ARB 0x2027
+#define WGL_DRAW_TO_WINDOW_ARB 0x2001
+#define WGL_PIXEL_TYPE_ARB 0x2013
+#define WGL_TYPE_RGBA_ARB 0x202b
+#define WGL_SUPPORT_OPENGL_ARB 0x2010
+#define WGL_COLOR_BITS_ARB 0x2014
+#define WGL_DOUBLE_BUFFER_ARB 0x2011
+#define WGL_ALPHA_BITS_ARB 0x201b
+#define WGL_DEPTH_BITS_ARB 0x2022
+#define WGL_STENCIL_BITS_ARB 0x2023
+#define WGL_STEREO_ARB 0x2012
+#define WGL_AUX_BUFFERS_ARB 0x2024
+#define WGL_RED_BITS_ARB 0x2015
+#define WGL_GREEN_BITS_ARB 0x2017
+#define WGL_BLUE_BITS_ARB 0x2019
+#define WGL_ACCUM_RED_BITS_ARB 0x201e
+#define WGL_ACCUM_GREEN_BITS_ARB 0x201f
+#define WGL_ACCUM_BLUE_BITS_ARB 0x2020
+#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021
+#define WGL_COLORSPACE_SRGB_EXT 0x3089
+#define WGL_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3
+#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097
+#define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0x0000
+#define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098
+#define WGL_CONTEXT_FLAGS_ARB 0x2094
+#define WGL_ACCESS_READ_WRITE_NV 0x00000001
+#define WGL_COVERAGE_SAMPLES_NV 0x2042
+#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004
+#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
+#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
+#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
+#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
+#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
+#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
+#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20A9
+#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097
+#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001
+#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004
+
+RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) {
+ const char flushControl[] = "WGL_ARB_context_flush_control";
+ const char noError[] = "WGL_ARB_create_context_no_error";
+ const char robustness[] = "WGL_ARB_create_context_robustness";
+
+ win->src.ctx.native = ctx;
+ win->src.gfxType = RGFW_gfxNativeOpenGL;
+
+ PIXELFORMATDESCRIPTOR pfd;
+ pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
+ pfd.nVersion = 1;
+ pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
+ pfd.iPixelType = PFD_TYPE_RGBA;
+ pfd.iLayerType = PFD_MAIN_PLANE;
+ pfd.cColorBits = 32;
+ pfd.cAlphaBits = 8;
+ pfd.cDepthBits = 24;
+ pfd.cStencilBits = (BYTE)hints->stencil;
+ pfd.cAuxBuffers = (BYTE)hints->auxBuffers;
+ if (hints->stereo) pfd.dwFlags |= PFD_STEREO;
+
+ /* try to create the pixel format we want for OpenGL and then try to create an OpenGL context for the specified version */
+ if (hints->renderer == RGFW_glSoftware)
+ pfd.dwFlags |= PFD_GENERIC_FORMAT | PFD_GENERIC_ACCELERATED;
+
+ /* get pixel format, default to a basic pixel format */
+ int pixel_format = ChoosePixelFormat(win->src.hdc, &pfd);
+ if (wglChoosePixelFormatARB != NULL) {
+ i32 pixel_format_attribs[50];
+ RGFW_attribStack stack;
+ RGFW_attribStack_init(&stack, pixel_format_attribs, 50);
+
+ RGFW_attribStack_pushAttribs(&stack, WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB);
+ RGFW_attribStack_pushAttribs(&stack, WGL_DRAW_TO_WINDOW_ARB, 1);
+ RGFW_attribStack_pushAttribs(&stack, WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB);
+ RGFW_attribStack_pushAttribs(&stack, WGL_SUPPORT_OPENGL_ARB, 1);
+ RGFW_attribStack_pushAttribs(&stack, WGL_COLOR_BITS_ARB, 32);
+ RGFW_attribStack_pushAttribs(&stack, WGL_DOUBLE_BUFFER_ARB, 1);
+ RGFW_attribStack_pushAttribs(&stack, WGL_ALPHA_BITS_ARB, hints->alpha);
+ RGFW_attribStack_pushAttribs(&stack, WGL_DEPTH_BITS_ARB, hints->depth);
+ RGFW_attribStack_pushAttribs(&stack, WGL_STENCIL_BITS_ARB, hints->stencil);
+ RGFW_attribStack_pushAttribs(&stack, WGL_STEREO_ARB, hints->stereo);
+ RGFW_attribStack_pushAttribs(&stack, WGL_AUX_BUFFERS_ARB, hints->auxBuffers);
+ RGFW_attribStack_pushAttribs(&stack, WGL_RED_BITS_ARB, hints->red);
+ RGFW_attribStack_pushAttribs(&stack, WGL_GREEN_BITS_ARB, hints->blue);
+ RGFW_attribStack_pushAttribs(&stack, WGL_BLUE_BITS_ARB, hints->green);
+ RGFW_attribStack_pushAttribs(&stack, WGL_ACCUM_RED_BITS_ARB, hints->accumRed);
+ RGFW_attribStack_pushAttribs(&stack, WGL_ACCUM_GREEN_BITS_ARB, hints->accumGreen);
+ RGFW_attribStack_pushAttribs(&stack, WGL_ACCUM_BLUE_BITS_ARB, hints->accumBlue);
+ RGFW_attribStack_pushAttribs(&stack, WGL_ACCUM_ALPHA_BITS_ARB, hints->accumAlpha);
+
+ if(hints->sRGB) {
+ if (hints->profile != RGFW_glES)
+ RGFW_attribStack_pushAttribs(&stack, WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, 1);
+ else
+ RGFW_attribStack_pushAttribs(&stack, WGL_COLORSPACE_SRGB_EXT, hints->sRGB);
+ }
+
+ RGFW_attribStack_pushAttribs(&stack, WGL_COVERAGE_SAMPLES_NV, hints->samples);
+
+ RGFW_attribStack_pushAttribs(&stack, 0, 0);
+
+ int new_pixel_format;
+ UINT num_formats;
+ wglChoosePixelFormatARB(win->src.hdc, pixel_format_attribs, 0, 1, &new_pixel_format, &num_formats);
+ if (!num_formats)
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to create a pixel format for WGL");
+ else pixel_format = new_pixel_format;
+ }
+
+ PIXELFORMATDESCRIPTOR suggested;
+ if (!DescribePixelFormat(win->src.hdc, pixel_format, sizeof(suggested), &suggested) ||
+ !SetPixelFormat(win->src.hdc, pixel_format, &pfd))
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to set the WGL pixel format");
+
+ if (wglCreateContextAttribsARB != NULL) {
+ /* create OpenGL/WGL context for the specified version */
+ i32 attribs[40];
+ RGFW_attribStack stack;
+ RGFW_attribStack_init(&stack, attribs, 50);
+
+
+ i32 mask = 0;
+ switch (hints->profile) {
+ case RGFW_glES: mask |= WGL_CONTEXT_ES_PROFILE_BIT_EXT; break;
+ case RGFW_glCompatibility: mask |= WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; break;
+ case RGFW_glForwardCompatibility: mask |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; break;
+ case RGFW_glCore: mask |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB; break;
+ default: mask |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB; break;
+ }
+
+ RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_PROFILE_MASK_ARB, mask);
+
+ if (hints->minor || hints->major) {
+ RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_MAJOR_VERSION_ARB, hints->major);
+ RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_MINOR_VERSION_ARB, hints->minor);
+ }
+
+ if (RGFW_extensionSupportedPlatform_OpenGL(noError, sizeof(noError)))
+ RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_OPENGL_NO_ERROR_ARB, hints->noError);
+
+ if (RGFW_extensionSupportedPlatform_OpenGL(flushControl, sizeof(flushControl))) {
+ if (hints->releaseBehavior == RGFW_glReleaseFlush) {
+ RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); /* WGL_CONTEXT_RELEASE_BEHAVIOR_ARB */
+ } else if (hints->releaseBehavior == RGFW_glReleaseNone) {
+ RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB);
+ }
+ }
+
+ i32 flags = 0;
+ if (hints->debug) flags |= WGL_CONTEXT_DEBUG_BIT_ARB;
+ if (hints->robustness && RGFW_extensionSupportedPlatform_OpenGL(robustness, sizeof(robustness))) flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB;
+ if (flags) {
+ RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_FLAGS_ARB, flags);
+ }
+
+
+ RGFW_attribStack_pushAttribs(&stack, 0, 0);
+
+ win->src.ctx.native->ctx = (HGLRC)wglCreateContextAttribsARB(win->src.hdc, NULL, attribs);
+ }
+
+ if (wglCreateContextAttribsARB == NULL || win->src.ctx.native->ctx == NULL) { /* fall back to a default context (probably OpenGL 2 or something) */
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to create an accelerated OpenGL Context.");
+ win->src.ctx.native->ctx = wglCreateContext(win->src.hdc);
+ }
+
+ ReleaseDC(win->src.window, win->src.hdc);
+ win->src.hdc = GetDC(win->src.window);
+
+ if (hints->share) {
+ wglShareLists((HGLRC)RGFW_getCurrentContext_OpenGL(), hints->share->ctx);
+ }
+
+ wglMakeCurrent(win->src.hdc, win->src.ctx.native->ctx);
+ RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context initalized.");
+ return RGFW_TRUE;
+}
+
+void RGFW_window_deleteContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx) {
+ wglDeleteContext((HGLRC) ctx->ctx); /*!< delete OpenGL context */
+ win->src.ctx.native->ctx = NULL;
+ RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context freed.");
+}
+
+void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win) {
+ if (win == NULL)
+ wglMakeCurrent(NULL, NULL);
+ else
+ wglMakeCurrent(win->src.hdc, (HGLRC) win->src.ctx.native->ctx);
+}
+void* RGFW_getCurrentContext_OpenGL(void) {
+ return wglGetCurrentContext();
+}
+void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) {
+ RGFW_ASSERT(win->src.ctx.native);
+ SwapBuffers(win->src.hdc);
+}
+
+void RGFW_window_swapInterval_OpenGL(RGFW_window* win, i32 swapInterval) {
+ RGFW_ASSERT(win != NULL);
+ if (wglSwapIntervalEXT == NULL || wglSwapIntervalEXT(swapInterval) == FALSE)
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to set swap interval");
+}
+#endif
+
+RGFW_bool RGFW_createUTF8FromWideStringWin32(const WCHAR* source, char* output, size_t max) {
+ i32 size = 0;
+ if (source == NULL) {
+ return RGFW_FALSE;
+ }
+ size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL);
+ if (!size) {
+ return RGFW_FALSE;
+ }
+
+ if (size > (i32)max)
+ size = (i32)max;
+
+ if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, output, size, NULL, NULL)) {
+ return RGFW_FALSE;
+ }
+
+ output[size] = 0;
+ return RGFW_TRUE;
+}
+
+#ifdef RGFW_WEBGPU
+WGPUSurface RGFW_window_createSurface_WebGPU(RGFW_window* window, WGPUInstance instance) {
+ WGPUSurfaceDescriptor surfaceDesc = {0};
+ WGPUSurfaceSourceWindowsHWND fromHwnd = {0};
+ fromHwnd.chain.sType = WGPUSType_SurfaceSourceWindowsHWND;
+ fromHwnd.hwnd = window->src.window;
+
+ fromHwnd.hinstance = GetModuleHandle(NULL);
+
+ surfaceDesc.nextInChain = (WGPUChainedStruct*)&fromHwnd.chain;
+ return wgpuInstanceCreateSurface(instance, &surfaceDesc);
+}
+#endif
+
+#endif /* RGFW_WINDOWS */
+
+/*
+ End of Windows defines
+*/
+
+
+
+/*
+
+ Start of MacOS defines
+
+
+*/
+
+#if defined(RGFW_MACOS)
+/*
+ based on silicon.h
+ start of cocoa wrapper
+*/
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+typedef TISInputSourceRef (*PFN_TISCopyCurrentKeyboardLayoutInputSource)(void);
+PFN_TISCopyCurrentKeyboardLayoutInputSource TISCopyCurrentKeyboardLayoutInputSourceSrc;
+#define TISCopyCurrentKeyboardLayoutInputSource TISCopyCurrentKeyboardLayoutInputSourceSrc
+
+typedef CFDataRef (*PFN_TISGetInputSourceProperty)(TISInputSourceRef, CFStringRef);
+PFN_TISGetInputSourceProperty TISGetInputSourcePropertySrc;
+#define TISGetInputSourceProperty TISGetInputSourcePropertySrc
+
+typedef u8 (*PFN_LMGetKbdType)(void);
+PFN_LMGetKbdType LMGetKbdTypeSrc;
+#define LMGetKbdType LMGetKbdTypeSrc
+
+CFStringRef kTISPropertyUnicodeKeyLayoutDataSrc;
+
+#ifndef __OBJC__
+typedef CGRect NSRect;
+typedef CGPoint NSPoint;
+typedef CGSize NSSize;
+
+typedef const char* NSPasteboardType;
+typedef unsigned long NSUInteger;
+typedef long NSInteger;
+typedef NSInteger NSModalResponse;
+
+typedef enum NSRequestUserAttentionType {
+ NSCriticalRequest = 0,
+ NSInformationalRequest = 10
+} NSRequestUserAttentionType;
+
+typedef enum NSApplicationActivationPolicy {
+ NSApplicationActivationPolicyRegular,
+ NSApplicationActivationPolicyAccessory,
+ NSApplicationActivationPolicyProhibited
+} NSApplicationActivationPolicy;
+
+typedef RGFW_ENUM(u32, NSBackingStoreType) {
+ NSBackingStoreRetained = 0,
+ NSBackingStoreNonretained = 1,
+ NSBackingStoreBuffered = 2
+};
+
+typedef RGFW_ENUM(u32, NSWindowStyleMask) {
+ NSWindowStyleMaskBorderless = 0,
+ NSWindowStyleMaskTitled = 1 << 0,
+ NSWindowStyleMaskClosable = 1 << 1,
+ NSWindowStyleMaskMiniaturizable = 1 << 2,
+ NSWindowStyleMaskResizable = 1 << 3,
+ NSWindowStyleMaskTexturedBackground = 1 << 8, /* deprecated */
+ NSWindowStyleMaskUnifiedTitleAndToolbar = 1 << 12,
+ NSWindowStyleMaskFullScreen = 1 << 14,
+ NSWindowStyleMaskFullSizeContentView = 1 << 15,
+ NSWindowStyleMaskUtilityWindow = 1 << 4,
+ NSWindowStyleMaskDocModalWindow = 1 << 6,
+ NSWindowStyleMaskNonactivatingpanel = 1 << 7,
+ NSWindowStyleMaskHUDWindow = 1 << 13
+};
+
+#define NSPasteboardTypeString "public.utf8-plain-text"
+
+typedef RGFW_ENUM(i32, NSDragOperation) {
+ NSDragOperationNone = 0,
+ NSDragOperationCopy = 1,
+ NSDragOperationLink = 2,
+ NSDragOperationGeneric = 4,
+ NSDragOperationPrivate = 8,
+ NSDragOperationMove = 16,
+ NSDragOperationDelete = 32,
+ NSDragOperationEvery = (int)ULONG_MAX
+};
+
+typedef RGFW_ENUM(NSInteger, NSOpenGLContextParameter) {
+ NSOpenGLContextParameterSwapInterval = 222, /* 1 param. 0 -> Don't sync, 1 -> Sync to vertical retrace */
+ NSOpenGLContextParametectxaceOrder = 235, /* 1 param. 1 -> Above Window (default), -1 -> Below Window */
+ NSOpenGLContextParametectxaceOpacity = 236, /* 1 param. 1-> Surface is opaque (default), 0 -> non-opaque */
+ NSOpenGLContextParametectxaceBackingSize = 304, /* 2 params. Width/height of surface backing size */
+ NSOpenGLContextParameterReclaimResources = 308, /* 0 params. */
+ NSOpenGLContextParameterCurrentRendererID = 309, /* 1 param. Retrieves the current renderer ID */
+ NSOpenGLContextParameterGPUVertexProcessing = 310, /* 1 param. Currently processing vertices with GPU (get) */
+ NSOpenGLContextParameterGPUFragmentProcessing = 311, /* 1 param. Currently processing fragments with GPU (get) */
+ NSOpenGLContextParameterHasDrawable = 314, /* 1 param. Boolean returned if drawable is attached */
+ NSOpenGLContextParameterMPSwapsInFlight = 315, /* 1 param. Max number of swaps queued by the MP GL engine */
+
+ NSOpenGLContextParameterSwapRectangle API_DEPRECATED("", macos(10.0, 10.14)) = 200, /* 4 params. Set or get the swap rectangle {x, y, w, h} */
+ NSOpenGLContextParameterSwapRectangleEnable API_DEPRECATED("", macos(10.0, 10.14)) = 201, /* Enable or disable the swap rectangle */
+ NSOpenGLContextParameterRasterizationEnable API_DEPRECATED("", macos(10.0, 10.14)) = 221, /* Enable or disable all rasterization */
+ NSOpenGLContextParameterStateValidation API_DEPRECATED("", macos(10.0, 10.14)) = 301, /* Validate state for multi-screen functionality */
+ NSOpenGLContextParametectxaceSurfaceVolatile API_DEPRECATED("", macos(10.0, 10.14)) = 306, /* 1 param. Surface volatile state */
+};
+
+typedef RGFW_ENUM(NSInteger, NSWindowButton) {
+ NSWindowCloseButton = 0,
+ NSWindowMiniaturizeButton = 1,
+ NSWindowZoomButton = 2,
+ NSWindowToolbarButton = 3,
+ NSWindowDocumentIconButton = 4,
+ NSWindowDocumentVersionsButton = 6,
+ NSWindowFullScreenButton = 7,
+};
+
+#define NSPasteboardTypeURL "public.url"
+#define NSPasteboardTypeFileURL "public.file-url"
+#define NSTrackingMouseEnteredAndExited 0x01
+#define NSTrackingMouseMoved 0x02
+#define NSTrackingCursorUpdate 0x04
+#define NSTrackingActiveWhenFirstResponder 0x10
+#define NSTrackingActiveInKeyWindow 0x20
+#define NSTrackingActiveInActiveApp 0x40
+#define NSTrackingActiveAlways 0x80
+#define NSTrackingAssumeInside 0x100
+#define NSTrackingInVisibleRect 0x200
+#define NSTrackingEnabledDuringMouseDrag 0x400
+enum {
+ NSOpenGLPFAAllRenderers = 1, /* choose from all available renderers */
+ NSOpenGLPFATripleBuffer = 3, /* choose a triple buffered pixel format */
+ NSOpenGLPFADoubleBuffer = 5, /* choose a double buffered pixel format */
+ NSOpenGLPFAAuxBuffers = 7, /* number of aux buffers */
+ NSOpenGLPFAColorSize = 8, /* number of color buffer bits */
+ NSOpenGLPFAAlphaSize = 11, /* number of alpha component bits */
+ NSOpenGLPFADepthSize = 12, /* number of depth buffer bits */
+ NSOpenGLPFAStencilSize = 13, /* number of stencil buffer bits */
+ NSOpenGLPFAAccumSize = 14, /* number of accum buffer bits */
+ NSOpenGLPFAMinimumPolicy = 51, /* never choose smaller buffers than requested */
+ NSOpenGLPFAMaximumPolicy = 52, /* choose largest buffers of type requested */
+ NSOpenGLPFASampleBuffers = 55, /* number of multi sample buffers */
+ NSOpenGLPFASamples = 56, /* number of samples per multi sample buffer */
+ NSOpenGLPFAAuxDepthStencil = 57, /* each aux buffer has its own depth stencil */
+ NSOpenGLPFAColorFloat = 58, /* color buffers store floating point pixels */
+ NSOpenGLPFAMultisample = 59, /* choose multisampling */
+ NSOpenGLPFASupersample = 60, /* choose supersampling */
+ NSOpenGLPFASampleAlpha = 61, /* request alpha filtering */
+ NSOpenGLPFARendererID = 70, /* request renderer by ID */
+ NSOpenGLPFANoRecovery = 72, /* disable all failure recovery systems */
+ NSOpenGLPFAAccelerated = 73, /* choose a hardware accelerated renderer */
+ NSOpenGLPFAClosestPolicy = 74, /* choose the closest color buffer to request */
+ NSOpenGLPFABackingStore = 76, /* back buffer contents are valid after swap */
+ NSOpenGLPFAScreenMask = 84, /* bit mask of supported physical screens */
+ NSOpenGLPFAAllowOfflineRenderers = 96, /* allow use of offline renderers */
+ NSOpenGLPFAAcceleratedCompute = 97, /* choose a hardware accelerated compute device */
+ NSOpenGLPFAOpenGLProfile = 99, /* specify an OpenGL Profile to use */
+ NSOpenGLProfileVersionLegacy = 0x1000, /* The requested profile is a legacy (pre-OpenGL 3.0) profile. */
+ NSOpenGLProfileVersion3_2Core = 0x3200, /* The 3.2 Profile of OpenGL */
+ NSOpenGLProfileVersion4_1Core = 0x3200, /* The 4.1 profile of OpenGL */
+ NSOpenGLPFAVirtualScreenCount = 128, /* number of virtual screens in this format */
+ NSOpenGLPFAStereo = 6,
+ NSOpenGLPFAOffScreen = 53,
+ NSOpenGLPFAFullScreen = 54,
+ NSOpenGLPFASingleRenderer = 71,
+ NSOpenGLPFARobust = 75,
+ NSOpenGLPFAMPSafe = 78,
+ NSOpenGLPFAWindow = 80,
+ NSOpenGLPFAMultiScreen = 81,
+ NSOpenGLPFACompliant = 83,
+ NSOpenGLPFAPixelBuffer = 90,
+ NSOpenGLPFARemotePixelBuffer = 91,
+};
+
+typedef RGFW_ENUM(u32, NSEventType) { /* various types of events */
+ NSEventTypeApplicationDefined = 15,
+};
+typedef unsigned long long NSEventMask;
+
+typedef enum NSEventModifierFlags {
+ NSEventModifierFlagCapsLock = 1 << 16,
+ NSEventModifierFlagShift = 1 << 17,
+ NSEventModifierFlagControl = 1 << 18,
+ NSEventModifierFlagOption = 1 << 19,
+ NSEventModifierFlagCommand = 1 << 20,
+ NSEventModifierFlagNumericPad = 1 << 21
+} NSEventModifierFlags;
+
+typedef RGFW_ENUM(NSUInteger, NSBitmapFormat) {
+ NSBitmapFormatAlphaFirst = 1 << 0, /* 0 means is alpha last (RGBA, CMYKA, etc.) */
+ NSBitmapFormatAlphaNonpremultiplied = 1 << 1, /* 0 means is premultiplied */
+ NSBitmapFormatFloatingpointSamples = 1 << 2, /* 0 is integer */
+
+ NSBitmapFormatSixteenBitLittleEndian = (1 << 8),
+ NSBitmapFormatThirtyTwoBitLittleEndian = (1 << 9),
+ NSBitmapFormatSixteenBitBigEndian = (1 << 10),
+ NSBitmapFormatThirtyTwoBitBigEndian = (1 << 11)
+};
+
+#else
+#import
+#include
+#endif /* notdef __OBJC__ */
+
+#ifdef __arm64__
+ /* ARM just uses objc_msgSend */
+#define abi_objc_msgSend_stret objc_msgSend
+#define abi_objc_msgSend_fpret objc_msgSend
+#else /* __i386__ */
+ /* x86 just uses abi_objc_msgSend_fpret and (NSColor *)objc_msgSend_id respectively */
+#define abi_objc_msgSend_stret objc_msgSend_stret
+#define abi_objc_msgSend_fpret objc_msgSend_fpret
+#endif
+
+#define NSAlloc(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("alloc"))
+#define objc_msgSend_bool(x, y) ((BOOL (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y)
+#define objc_msgSend_void(x, y) ((void (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y)
+#define objc_msgSend_void_id(x, y, z) ((void (*)(id, SEL, id))objc_msgSend) ((id)x, (SEL)y, (id)z)
+#define objc_msgSend_uint(x, y) ((NSUInteger (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y)
+#define objc_msgSend_void_bool(x, y, z) ((void (*)(id, SEL, BOOL))objc_msgSend) ((id)(x), (SEL)y, (BOOL)z)
+#define objc_msgSend_bool_void(x, y) ((BOOL (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y)
+#define objc_msgSend_void_SEL(x, y, z) ((void (*)(id, SEL, SEL))objc_msgSend) ((id)(x), (SEL)y, (SEL)z)
+#define objc_msgSend_id(x, y) ((id (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y)
+#define objc_msgSend_id_id(x, y, z) ((id (*)(id, SEL, id))objc_msgSend) ((id)(x), (SEL)y, (id)z)
+#define objc_msgSend_id_bool(x, y, z) ((BOOL (*)(id, SEL, id))objc_msgSend) ((id)(x), (SEL)y, (id)z)
+#define objc_msgSend_int(x, y, z) ((id (*)(id, SEL, int))objc_msgSend) ((id)(x), (SEL)y, (int)z)
+#define objc_msgSend_arr(x, y, z) ((id (*)(id, SEL, int))objc_msgSend) ((id)(x), (SEL)y, (int)z)
+#define objc_msgSend_ptr(x, y, z) ((id (*)(id, SEL, void*))objc_msgSend) ((id)(x), (SEL)y, (void*)z)
+#define objc_msgSend_class(x, y) ((id (*)(Class, SEL))objc_msgSend) ((Class)(x), (SEL)y)
+#define objc_msgSend_class_char(x, y, z) ((id (*)(Class, SEL, char*))objc_msgSend) ((Class)(x), (SEL)y, (char*)z)
+
+#define NSRelease(obj) objc_msgSend_void((id)obj, sel_registerName("release"))
+RGFWDEF id NSString_stringWithUTF8String(const char* str);
+id NSString_stringWithUTF8String(const char* str) {
+ return ((id(*)(id, SEL, const char*))objc_msgSend) ((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), str);
+}
+
+RGFWDEF float RGFW_cocoaYTransform(float y);
+float RGFW_cocoaYTransform(float y) { return (float)(CGDisplayBounds(CGMainDisplayID()).size.height - (double)y - (double)1.0f); }
+
+const char* NSString_to_char(id str);
+const char* NSString_to_char(id str) {
+ return ((const char* (*)(id, SEL)) objc_msgSend) ((id)(id)str, sel_registerName("UTF8String"));
+}
+
+unsigned char* NSBitmapImageRep_bitmapData(id imageRep);
+unsigned char* NSBitmapImageRep_bitmapData(id imageRep) {
+ return ((unsigned char* (*)(id, SEL))objc_msgSend) ((id)imageRep, sel_registerName("bitmapData"));
+}
+
+id NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits);
+id NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits) {
+ SEL func = sel_registerName("initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bitmapFormat:bytesPerRow:bitsPerPixel:");
+
+ return (id) ((id(*)(id, SEL, unsigned char**, NSInteger, NSInteger, NSInteger, NSInteger, bool, bool, id, NSBitmapFormat, NSInteger, NSInteger))objc_msgSend)
+ (NSAlloc((id)objc_getClass("NSBitmapImageRep")), func, planes, width, height, bps, spp, alpha, isPlanar, NSString_stringWithUTF8String(colorSpaceName), bitmapFormat, rowBytes, pixelBits);
+}
+
+id NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha);
+id NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha) {
+ Class nsclass = objc_getClass("NSColor");
+ SEL func = sel_registerName("colorWithSRGBRed:green:blue:alpha:");
+ return ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend)
+ ((id)nsclass, func, red, green, blue, alpha);
+}
+
+id NSPasteboard_generalPasteboard(void);
+id NSPasteboard_generalPasteboard(void) {
+ return (id) objc_msgSend_id((id)objc_getClass("NSPasteboard"), sel_registerName("generalPasteboard"));
+}
+
+id* cstrToNSStringArray(char** strs, size_t len);
+id* cstrToNSStringArray(char** strs, size_t len) {
+ static id nstrs[6];
+ size_t i;
+ for (i = 0; i < len; i++)
+ nstrs[i] = NSString_stringWithUTF8String(strs[i]);
+
+ return nstrs;
+}
+
+const char* NSPasteboard_stringForType(id pasteboard, NSPasteboardType dataType, size_t* len);
+const char* NSPasteboard_stringForType(id pasteboard, NSPasteboardType dataType, size_t* len) {
+ SEL func = sel_registerName("stringForType:");
+ id nsstr = NSString_stringWithUTF8String((const char*)dataType);
+ id nsString = ((id(*)(id, SEL, id))objc_msgSend)(pasteboard, func, nsstr);
+ const char* str = NSString_to_char(nsString);
+ if (len != NULL)
+ *len = (size_t)((NSUInteger(*)(id, SEL, int))objc_msgSend)(nsString, sel_registerName("maximumLengthOfBytesUsingEncoding:"), 4);
+ return str;
+}
+
+id c_array_to_NSArray(void* array, size_t len);
+id c_array_to_NSArray(void* array, size_t len) {
+ return ((id (*)(id, SEL, void*, NSUInteger))objc_msgSend) (NSAlloc(objc_getClass("NSArray")), sel_registerName("initWithObjects:count:"), array, len);
+}
+
+
+void NSregisterForDraggedTypes(id view, NSPasteboardType* newTypes, size_t len);
+void NSregisterForDraggedTypes(id view, NSPasteboardType* newTypes, size_t len) {
+ id* ntypes = cstrToNSStringArray((char**)newTypes, len);
+
+ id array = c_array_to_NSArray(ntypes, len);
+ objc_msgSend_void_id(view, sel_registerName("registerForDraggedTypes:"), array);
+ NSRelease(array);
+}
+
+NSInteger NSPasteBoard_declareTypes(id pasteboard, NSPasteboardType* newTypes, size_t len, void* owner);
+NSInteger NSPasteBoard_declareTypes(id pasteboard, NSPasteboardType* newTypes, size_t len, void* owner) {
+ id* ntypes = cstrToNSStringArray((char**)newTypes, len);
+
+ SEL func = sel_registerName("declareTypes:owner:");
+
+ id array = c_array_to_NSArray(ntypes, len);
+
+ NSInteger output = ((NSInteger(*)(id, SEL, id, void*))objc_msgSend)
+ (pasteboard, func, array, owner);
+ NSRelease(array);
+
+ return output;
+}
+
+#define NSRetain(obj) objc_msgSend_void((id)obj, sel_registerName("retain"))
+
+/*
+ End of cocoa wrapper
+*/
+
+static id RGFW__osxCustomInitWithRGFWWindow(id self, SEL _cmd, RGFW_window* win) {
+ RGFW_UNUSED(_cmd);
+ struct objc_super s = { self, class_getSuperclass(object_getClass(self)) };
+ self = ((id (*)(struct objc_super*, SEL))objc_msgSendSuper)(&s, sel_registerName("init"));
+
+ if (self != nil) {
+ object_setInstanceVariable(self, "RGFW_window", win);
+ object_setInstanceVariable(self, "trackingArea", nil);
+
+ object_setInstanceVariable(
+ self, "markedText",
+ ((id (*)(id, SEL))objc_msgSend)(
+ ((id (*)(Class, SEL))objc_msgSend)(objc_getClass("NSMutableAttributedString"), sel_registerName("alloc")),
+ sel_registerName("init")
+ )
+ );
+
+ ((void (*)(id, SEL))objc_msgSend)(self, sel_registerName("updateTrackingAreas"));
+
+ ((void (*)(id, SEL, id))objc_msgSend)(
+ self, sel_registerName("registerForDraggedTypes:"),
+ ((id (*)(Class, SEL, id))objc_msgSend)(
+ objc_getClass("NSArray"),
+ sel_registerName("arrayWithObject:"),
+ ((id (*)(Class, SEL, const char*))objc_msgSend)(
+ objc_getClass("NSString"),
+ sel_registerName("stringWithUTF8String:"),
+ "public.url"
+ )
+ )
+ );
+ }
+
+ return self;
+}
+
+static u32 RGFW_OnClose(id self) {
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, (const char*)"RGFW_window", (void**)&win);
+ if (win == NULL) return true;
+
+ RGFW_windowQuitCallback(win);
+ return false;
+}
+
+/* NOTE(EimaMei): Fixes the constant clicking when the app is running under a terminal. */
+static bool RGFW__osxAcceptsFirstResponder(void) { return true; }
+static bool RGFW__osxPerformKeyEquivalent(id event) { RGFW_UNUSED(event); return true; }
+
+static NSDragOperation RGFW__osxDraggingEntered(id self, SEL sel, id sender) {
+ RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel);
+
+ return NSDragOperationCopy;
+}
+static NSDragOperation RGFW__osxDraggingUpdated(id self, SEL sel, id sender) {
+ RGFW_UNUSED(sel);
+
+ RGFW_window* win = NULL;
+
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL)
+ return 0;
+ if (!(win->internal.enabledEvents & RGFW_dataDragFlag)) return NSDragOperationCopy;
+
+ NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation"));
+ RGFW_dataDragCallback(win, (i32) p.x, (i32) (win->h - p.y));
+ return NSDragOperationCopy;
+}
+static bool RGFW__osxPrepareForDragOperation(id self) {
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL || (!(win->internal.enabledEvents & RGFW_dataDropFlag)))
+ return true;
+
+ if (!(win->internal.flags & RGFW_windowAllowDND)) {
+ return false;
+ }
+
+ return true;
+}
+
+void RGFW__osxDraggingEnded(id self, SEL sel, id sender);
+void RGFW__osxDraggingEnded(id self, SEL sel, id sender) { RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); return; }
+
+static bool RGFW__osxPerformDragOperation(id self, SEL sel, id sender) {
+ RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel);
+
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL || (!(win->internal.enabledEvents & RGFW_dataDropFlag)))
+ return false;
+
+ /* id pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard")); */
+
+ id pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard"));
+
+ /* Get the types of data available on the pasteboard */
+ id types = objc_msgSend_id(pasteBoard, sel_registerName("types"));
+
+ /* Get the string type for file URLs */
+ id fileURLsType = objc_msgSend_class_char(objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "NSFilenamesPboardType");
+
+ /* Check if the pasteboard contains file URLs */
+ if (objc_msgSend_id_bool(types, sel_registerName("containsObject:"), fileURLsType) == 0) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errClipboard, "No files found on the pasteboard.");
+ return 0;
+ }
+
+ id fileURLs = objc_msgSend_id_id(pasteBoard, sel_registerName("propertyListForType:"), fileURLsType);
+ int count = ((int (*)(id, SEL))objc_msgSend)(fileURLs, sel_registerName("count"));
+
+ if (count == 0)
+ return 0;
+
+ char** files = (char**)(void*)_RGFW->files;
+
+ u32 i;
+ for (i = 0; i < (u32)count; i++) {
+ id fileURL = objc_msgSend_arr(fileURLs, sel_registerName("objectAtIndex:"), i);
+ const char *filePath = ((const char* (*)(id, SEL))objc_msgSend)(fileURL, sel_registerName("UTF8String"));
+ RGFW_STRNCPY(files[i], filePath, RGFW_MAX_PATH - 1);
+ files[i][RGFW_MAX_PATH - 1] = '\0';
+ }
+
+ RGFW_dataDropCallback(win, files, (size_t)count);
+
+ return false;
+}
+
+#ifndef RGFW_NO_IOKIT
+#include
+
+float RGFW_osx_getFallbackRefreshRate(CGDirectDisplayID displayID);
+float RGFW_osx_getFallbackRefreshRate(CGDirectDisplayID displayID) {
+ float refreshRate = 0;
+ io_iterator_t it;
+ io_service_t service;
+ CFNumberRef indexRef, clockRef, countRef;
+ u32 clock, count;
+
+#ifdef kIOMainPortDefault
+ if (IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceMatching("IOFramebuffer"), &it) != 0)
+#elif defined(kIOMasterPortDefault)
+ if (IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceMatching("IOFramebuffer"), &it) != 0)
+#endif
+ return RGFW_FALSE;
+
+ while ((service = IOIteratorNext(it)) != 0) {
+ u32 index;
+ indexRef = (CFNumberRef)IORegistryEntryCreateCFProperty(service, CFSTR("IOFramebufferOpenGLIndex"), kCFAllocatorDefault, kNilOptions);
+ if (indexRef == 0) continue;
+
+ if (CFNumberGetValue(indexRef, kCFNumberIntType, &index) && CGOpenGLDisplayMaskToDisplayID(1 << index) == displayID) {
+ CFRelease(indexRef);
+ break;
+ }
+
+ CFRelease(indexRef);
+ }
+
+ if (service) {
+ clockRef = (CFNumberRef)IORegistryEntryCreateCFProperty(service, CFSTR("IOFBCurrentPixelClock"), kCFAllocatorDefault, kNilOptions);
+ if (clockRef) {
+ if (CFNumberGetValue(clockRef, kCFNumberIntType, &clock) && clock) {
+ countRef = (CFNumberRef)IORegistryEntryCreateCFProperty(service, CFSTR("IOFBCurrentPixelCount"), kCFAllocatorDefault, kNilOptions);
+ if (countRef && CFNumberGetValue(countRef, kCFNumberIntType, &count) && count) {
+ refreshRate = (float)((double)clock / (double) count);
+ CFRelease(countRef);
+ }
+ }
+ CFRelease(clockRef);
+ }
+ }
+
+ IOObjectRelease(it);
+ return refreshRate;
+}
+#endif
+
+void RGFW_moveToMacOSResourceDir(void) {
+ char resourcesPath[256];
+
+ CFBundleRef bundle = CFBundleGetMainBundle();
+ if (!bundle)
+ return;
+
+ CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle);
+ CFStringRef last = CFURLCopyLastPathComponent(resourcesURL);
+
+ if (
+ CFStringCompare(CFSTR("Resources"), last, 0) != kCFCompareEqualTo ||
+ CFURLGetFileSystemRepresentation(resourcesURL, true, (u8*) resourcesPath, 255) == 0
+ ) {
+ CFRelease(last);
+ CFRelease(resourcesURL);
+ return;
+ }
+
+ CFRelease(last);
+ CFRelease(resourcesURL);
+
+ chdir(resourcesPath);
+}
+
+static void RGFW__osxDidChangeScreenParameters(id self, SEL _cmd, id notification) {
+ RGFW_UNUSED(self); RGFW_UNUSED(_cmd); RGFW_UNUSED(notification);
+ RGFW_pollMonitors();
+}
+
+static void RGFW__osxWindowDeminiaturize(id self, SEL sel) {
+ RGFW_UNUSED(sel);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ RGFW_windowRestoredCallback(win, win->x, win->y, win->w, win->h);
+
+}
+static void RGFW__osxWindowMiniaturize(id self, SEL sel) {
+ RGFW_UNUSED(sel);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ RGFW_windowMinimizedCallback(win);
+
+}
+
+static void RGFW__osxWindowBecameKey(id self, SEL sel) {
+ RGFW_UNUSED(sel);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ RGFW_focusCallback(win, RGFW_TRUE);
+}
+
+static void RGFW__osxWindowResignKey(id self, SEL sel) {
+ RGFW_UNUSED(sel);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ RGFW_focusCallback(win, RGFW_FALSE);
+}
+
+static void RGFW__osxDidWindowResize(id self, SEL _cmd, id notification) {
+ RGFW_UNUSED(_cmd); RGFW_UNUSED(notification);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ NSRect frame;
+ if (win->src.view) frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame"));
+ else return;
+
+ if (frame.size.width == 0 || frame.size.height == 0) return;
+ win->w = (i32)frame.size.width;
+ win->h = (i32)frame.size.height;
+
+ RGFW_monitor* mon = RGFW_window_getMonitor(win);
+ if (mon == NULL) return;
+
+ if ((i32)mon->mode.w == win->w && (i32)mon->mode.h - 102 <= win->h) {
+ RGFW_windowMaximizedCallback(win, 0, 0, win->w, win->h);
+ } else if (win->internal.flags & RGFW_windowMaximize) {
+ RGFW_windowRestoredCallback(win, win->x, win->y, win->w, win->h);
+ }
+
+ RGFW_windowResizedCallback(win, win->w, win->h);
+}
+
+static void RGFW__osxWindowMove(id self, SEL sel) {
+ RGFW_UNUSED(sel);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame"));
+ NSRect content = ((NSRect(*)(id, SEL, NSRect))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("contentRectForFrameRect:"), frame);
+
+ float y = RGFW_cocoaYTransform((float)(content.origin.y + content.size.height - 1));
+
+ RGFW_windowMovedCallback(win, (i32)content.origin.x, (i32)y);
+}
+
+static void RGFW__osxViewDidChangeBackingProperties(id self, SEL _cmd) {
+ RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ RGFW_monitor* mon = RGFW_window_getMonitor(win);
+ if (mon == NULL) return;
+
+ RGFW_scaleUpdatedCallback(win, mon->scaleX, mon->scaleY);
+}
+
+static BOOL RGFW__osxWantsUpdateLayer(id self, SEL _cmd) { RGFW_UNUSED(self); RGFW_UNUSED(_cmd); return YES; }
+
+static void RGFW__osxUpdateLayer(id self, SEL _cmd) {
+ RGFW_UNUSED(self); RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+ RGFW_windowRefreshCallback(win);
+}
+
+static void RGFW__osxDrawRect(id self, SEL _cmd, CGRect rect) {
+ RGFW_UNUSED(rect); RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ RGFW_windowRefreshCallback(win);
+}
+
+static void RGFW__osxMouseEntered(id self, SEL _cmd, id event) {
+ RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ NSPoint p = ((NSPoint(*)(id, SEL))objc_msgSend)(event, sel_registerName("locationInWindow"));
+ RGFW_mouseNotifyCallback(win, (i32)p.x, (i32)(win->h - p.y), 1);
+}
+
+static void RGFW__osxMouseExited(id self, SEL _cmd, id event) {
+ RGFW_UNUSED(_cmd); RGFW_UNUSED(event);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ RGFW_mouseNotifyCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, RGFW_FALSE);
+}
+
+static void RGFW__osxKeyDown(id self, SEL _cmd, id event) {
+ RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL || !(win->internal.enabledEvents & RGFW_keyPressedFlag)) return;
+
+ u32 key = (u16)((u32(*)(id, SEL))objc_msgSend)(event, sel_registerName("keyCode"));
+
+ RGFW_key value = (u8)RGFW_apiKeyToRGFW(key);
+ RGFW_bool repeat = RGFW_window_isKeyPressed(win, value);
+
+ RGFW_keyCallback(win, value, win->internal.mod, repeat, 1);
+
+ id nsstring = ((id(*)(id, SEL))objc_msgSend)(event, sel_registerName("charactersIgnoringModifiers"));
+ const char* string = NSString_to_char(nsstring);
+ size_t count = (size_t)((int (*)(id, SEL))objc_msgSend)(nsstring, sel_registerName("length"));
+
+ for (size_t index = 0; index < count;
+ RGFW_keyCharCallback(win, RGFW_decodeUTF8(&string[index], &index))
+ );
+}
+
+static void RGFW__osxKeyUp(id self, SEL _cmd, id event) {
+ RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL || !(win->internal.enabledEvents & RGFW_keyReleasedFlag)) return;
+
+ u32 key = (u16)((u32(*)(id, SEL))objc_msgSend)(event, sel_registerName("keyCode"));
+
+ RGFW_key value = (u8)RGFW_apiKeyToRGFW(key);
+ RGFW_bool repeat = RGFW_window_isKeyDown(win, (u8)value);
+
+ RGFW_keyCallback(win, value, win->internal.mod, repeat, 0);
+}
+
+static void RGFW__osxFlagsChanged(id self, SEL _cmd, id event) {
+ RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ RGFW_key value = 0;
+ RGFW_bool pressed = RGFW_FALSE;
+
+ u32 flags = (u32)((u32(*)(id, SEL))objc_msgSend)(event, sel_registerName("modifierFlags"));
+ RGFW_updateKeyModsEx(win,
+ ((u32)(flags & NSEventModifierFlagCapsLock) % 255),
+ ((flags & NSEventModifierFlagNumericPad) % 255),
+ ((flags & NSEventModifierFlagControl) % 255),
+ ((flags & NSEventModifierFlagOption) % 255),
+ ((flags & NSEventModifierFlagShift) % 255),
+ ((flags & NSEventModifierFlagCommand) % 255), 0);
+ u8 i;
+ for (i = 0; i < 9; i++)
+ _RGFW->keyboard[i + RGFW_capsLock].prev = _RGFW->keyboard[i + RGFW_capsLock].current;
+
+ for (i = 0; i < 5; i++) {
+ u32 shift = (1 << (i + 16));
+ RGFW_key key = i + RGFW_capsLock;
+ if ((flags & shift) && !RGFW_window_isKeyDown(win, (u8)key)) {
+ pressed = RGFW_TRUE;
+ value = (u8)key;
+ break;
+ }
+ if (!(flags & shift) && RGFW_window_isKeyDown(win, (u8)key)) {
+ pressed = RGFW_FALSE;
+ value = (u8)key;
+ break;
+ }
+ }
+
+ RGFW_bool repeat = RGFW_window_isKeyDown(win, (u8)value);
+ RGFW_keyCallback(win, value, win->internal.mod, repeat, pressed);
+
+ if (value != RGFW_capsLock) {
+ RGFW_keyCallback(win, value + 4, win->internal.mod, repeat, pressed);
+ }
+
+}
+
+static void RGFW__osxMouseMoved(id self, SEL _cmd, id event) {
+ RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ NSPoint p = ((NSPoint(*)(id, SEL))objc_msgSend)(event, sel_registerName("locationInWindow"));
+
+ CGFloat vecX = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("deltaX"));
+ CGFloat vecY = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("deltaY"));
+
+ RGFW_mousePosCallback(win, (i32)p.x, (i32)(win->h - p.y), (float)vecX, (float)vecY);
+}
+
+static void RGFW__osxMouseDown(id self, SEL _cmd, id event) {
+ RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ u32 buttonNumber = (u32)((u32(*)(id, SEL))objc_msgSend)(event, sel_registerName("buttonNumber"));
+
+ RGFW_mouseButton value = 0;
+ switch (buttonNumber) {
+ case 0: value = RGFW_mouseLeft; break;
+ case 1: value = RGFW_mouseRight; break;
+ case 2: value = RGFW_mouseMiddle; break;
+ default: value = (u8)buttonNumber;
+ }
+
+ RGFW_mouseButtonCallback(win, value, 1);
+}
+
+static void RGFW__osxMouseUp(id self, SEL _cmd, id event) {
+ RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ u32 buttonNumber = (u32)((u32(*)(id, SEL))objc_msgSend)(event, sel_registerName("buttonNumber"));
+
+ RGFW_mouseButton value = 0;
+ switch (buttonNumber) {
+ case 0: value = RGFW_mouseLeft; break;
+ case 1: value = RGFW_mouseRight; break;
+ case 2: value = RGFW_mouseMiddle; break;
+ default: value = (u8)buttonNumber;
+ }
+
+ RGFW_mouseButtonCallback(win, value, 0);
+}
+
+static void RGFW__osxScrollWheel(id self, SEL _cmd, id event) {
+ RGFW_UNUSED(_cmd);
+ RGFW_window* win = NULL;
+ object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+ if (win == NULL) return;
+
+ float deltaX = (float)((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("deltaX"));
+ float deltaY = (float)((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("deltaY"));
+
+ RGFW_mouseScrollCallback(win, deltaX, deltaY);
+}
+
+RGFW_format RGFW_nativeFormat(void) { return RGFW_formatRGBA8; }
+
+RGFW_bool RGFW_createSurfacePtr(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) {
+ surface->data = data;
+ surface->w = w;
+ surface->h = h;
+ surface->format = format;
+ surface->native.format = RGFW_formatRGBA8;
+
+ surface->native.buffer = (u8*)RGFW_ALLOC((size_t)(w * h * 4));
+ return RGFW_TRUE;
+}
+
+void RGFW_surface_freePtr(RGFW_surface* surface) {
+ RGFW_FREE(surface->native.buffer);
+}
+
+void RGFW_window_blitSurface(RGFW_window* win, RGFW_surface* surface) {
+ id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ pool = objc_msgSend_id(pool, sel_registerName("init"));
+
+ int minW = RGFW_MIN(win->w, surface->w);
+ int minH = RGFW_MIN(win->h, surface->h);
+
+ RGFW_monitor* mon = RGFW_window_getMonitor(win);
+ if (mon == NULL) return;
+
+ minW = (i32)((float)minW * mon->pixelRatio);
+ minH = (i32)((float)minH * mon->pixelRatio);
+
+ surface->native.rep = (void*)NSBitmapImageRep_initWithBitmapData(&surface->native.buffer, minW, minH, 8, 4, true, false, "NSDeviceRGBColorSpace", 1 << 1, (u32)surface->w * 4, 32);
+
+ id image = ((id (*)(Class, SEL))objc_msgSend)(objc_getClass("NSImage"), sel_getUid("alloc"));
+ NSSize size = (NSSize){(double)minW, (double)minH};
+ image = ((id (*)(id, SEL, NSSize))objc_msgSend)((id)image, sel_getUid("initWithSize:"), size);
+
+ RGFW_copyImageData(NSBitmapImageRep_bitmapData((id)surface->native.rep), surface->w, minH, RGFW_formatRGBA8, surface->data, surface->native.format, surface->convertFunc);
+ ((void (*)(id, SEL, id))objc_msgSend)((id)image, sel_getUid("addRepresentation:"), (id)surface->native.rep);
+
+ id layer = ((id (*)(id, SEL))objc_msgSend)((id)win->src.view, sel_getUid("layer"));
+ ((void (*)(id, SEL, id))objc_msgSend)(layer, sel_getUid("setContents:"), (id)image);
+
+ NSRelease(image);
+ NSRelease(surface->native.rep);
+
+ objc_msgSend_bool_void(pool, sel_registerName("drain"));
+}
+
+void* RGFW_window_getView_OSX(RGFW_window* win) { return win->src.view; }
+
+void RGFW_window_setLayer_OSX(RGFW_window* win, void* layer) {
+ objc_msgSend_void_id((id)win->src.view, sel_registerName("setLayer:"), (id)layer);
+}
+
+void* RGFW_getLayer_OSX(void) {
+ return objc_msgSend_class((id)objc_getClass("CAMetalLayer"), (SEL)sel_registerName("layer"));
+}
+void* RGFW_window_getWindow_OSX(RGFW_window* win) { return win->src.window; }
+
+void RGFW_initKeycodesPlatform(void) {
+ _RGFW->keycodes[0x1D] = RGFW_0;
+ _RGFW->keycodes[0x12] = RGFW_1;
+ _RGFW->keycodes[0x13] = RGFW_2;
+ _RGFW->keycodes[0x14] = RGFW_3;
+ _RGFW->keycodes[0x15] = RGFW_4;
+ _RGFW->keycodes[0x17] = RGFW_5;
+ _RGFW->keycodes[0x16] = RGFW_6;
+ _RGFW->keycodes[0x1A] = RGFW_7;
+ _RGFW->keycodes[0x1C] = RGFW_8;
+ _RGFW->keycodes[0x19] = RGFW_9;
+ _RGFW->keycodes[0x00] = RGFW_a;
+ _RGFW->keycodes[0x0B] = RGFW_b;
+ _RGFW->keycodes[0x08] = RGFW_c;
+ _RGFW->keycodes[0x02] = RGFW_d;
+ _RGFW->keycodes[0x0E] = RGFW_e;
+ _RGFW->keycodes[0x03] = RGFW_f;
+ _RGFW->keycodes[0x05] = RGFW_g;
+ _RGFW->keycodes[0x04] = RGFW_h;
+ _RGFW->keycodes[0x22] = RGFW_i;
+ _RGFW->keycodes[0x26] = RGFW_j;
+ _RGFW->keycodes[0x28] = RGFW_k;
+ _RGFW->keycodes[0x25] = RGFW_l;
+ _RGFW->keycodes[0x2E] = RGFW_m;
+ _RGFW->keycodes[0x2D] = RGFW_n;
+ _RGFW->keycodes[0x1F] = RGFW_o;
+ _RGFW->keycodes[0x23] = RGFW_p;
+ _RGFW->keycodes[0x0C] = RGFW_q;
+ _RGFW->keycodes[0x0F] = RGFW_r;
+ _RGFW->keycodes[0x01] = RGFW_s;
+ _RGFW->keycodes[0x11] = RGFW_t;
+ _RGFW->keycodes[0x20] = RGFW_u;
+ _RGFW->keycodes[0x09] = RGFW_v;
+ _RGFW->keycodes[0x0D] = RGFW_w;
+ _RGFW->keycodes[0x07] = RGFW_x;
+ _RGFW->keycodes[0x10] = RGFW_y;
+ _RGFW->keycodes[0x06] = RGFW_z;
+ _RGFW->keycodes[0x27] = RGFW_apostrophe;
+ _RGFW->keycodes[0x2A] = RGFW_backSlash;
+ _RGFW->keycodes[0x2B] = RGFW_comma;
+ _RGFW->keycodes[0x18] = RGFW_equals;
+ _RGFW->keycodes[0x32] = RGFW_backtick;
+ _RGFW->keycodes[0x21] = RGFW_bracket;
+ _RGFW->keycodes[0x1B] = RGFW_minus;
+ _RGFW->keycodes[0x2F] = RGFW_period;
+ _RGFW->keycodes[0x1E] = RGFW_closeBracket;
+ _RGFW->keycodes[0x29] = RGFW_semicolon;
+ _RGFW->keycodes[0x2C] = RGFW_slash;
+ _RGFW->keycodes[0x0A] = RGFW_world1;
+ _RGFW->keycodes[0x33] = RGFW_backSpace;
+ _RGFW->keycodes[0x39] = RGFW_capsLock;
+ _RGFW->keycodes[0x75] = RGFW_delete;
+ _RGFW->keycodes[0x7D] = RGFW_down;
+ _RGFW->keycodes[0x77] = RGFW_end;
+ _RGFW->keycodes[0x24] = RGFW_enter;
+ _RGFW->keycodes[0x35] = RGFW_escape;
+ _RGFW->keycodes[0x7A] = RGFW_F1;
+ _RGFW->keycodes[0x78] = RGFW_F2;
+ _RGFW->keycodes[0x63] = RGFW_F3;
+ _RGFW->keycodes[0x76] = RGFW_F4;
+ _RGFW->keycodes[0x60] = RGFW_F5;
+ _RGFW->keycodes[0x61] = RGFW_F6;
+ _RGFW->keycodes[0x62] = RGFW_F7;
+ _RGFW->keycodes[0x64] = RGFW_F8;
+ _RGFW->keycodes[0x65] = RGFW_F9;
+ _RGFW->keycodes[0x6D] = RGFW_F10;
+ _RGFW->keycodes[0x67] = RGFW_F11;
+ _RGFW->keycodes[0x6F] = RGFW_F12;
+ _RGFW->keycodes[0x69] = RGFW_printScreen;
+ _RGFW->keycodes[0x6B] = RGFW_F14;
+ _RGFW->keycodes[0x71] = RGFW_F15;
+ _RGFW->keycodes[0x6A] = RGFW_F16;
+ _RGFW->keycodes[0x40] = RGFW_F17;
+ _RGFW->keycodes[0x4F] = RGFW_F18;
+ _RGFW->keycodes[0x50] = RGFW_F19;
+ _RGFW->keycodes[0x5A] = RGFW_F20;
+ _RGFW->keycodes[0x73] = RGFW_home;
+ _RGFW->keycodes[0x72] = RGFW_insert;
+ _RGFW->keycodes[0x7B] = RGFW_left;
+ _RGFW->keycodes[0x3A] = RGFW_altL;
+ _RGFW->keycodes[0x3B] = RGFW_controlL;
+ _RGFW->keycodes[0x38] = RGFW_shiftL;
+ _RGFW->keycodes[0x37] = RGFW_superL;
+ _RGFW->keycodes[0x6E] = RGFW_menu;
+ _RGFW->keycodes[0x47] = RGFW_numLock;
+ _RGFW->keycodes[0x79] = RGFW_pageDown;
+ _RGFW->keycodes[0x74] = RGFW_pageUp;
+ _RGFW->keycodes[0x7C] = RGFW_right;
+ _RGFW->keycodes[0x3D] = RGFW_altR;
+ _RGFW->keycodes[0x3E] = RGFW_controlR;
+ _RGFW->keycodes[0x3C] = RGFW_shiftR;
+ _RGFW->keycodes[0x36] = RGFW_superR;
+ _RGFW->keycodes[0x31] = RGFW_space;
+ _RGFW->keycodes[0x30] = RGFW_tab;
+ _RGFW->keycodes[0x7E] = RGFW_up;
+ _RGFW->keycodes[0x52] = RGFW_kp0;
+ _RGFW->keycodes[0x53] = RGFW_kp1;
+ _RGFW->keycodes[0x54] = RGFW_kp2;
+ _RGFW->keycodes[0x55] = RGFW_kp3;
+ _RGFW->keycodes[0x56] = RGFW_kp4;
+ _RGFW->keycodes[0x57] = RGFW_kp5;
+ _RGFW->keycodes[0x58] = RGFW_kp6;
+ _RGFW->keycodes[0x59] = RGFW_kp7;
+ _RGFW->keycodes[0x5B] = RGFW_kp8;
+ _RGFW->keycodes[0x5C] = RGFW_kp9;
+ _RGFW->keycodes[0x45] = RGFW_kpSlash;
+ _RGFW->keycodes[0x41] = RGFW_kpPeriod;
+ _RGFW->keycodes[0x4B] = RGFW_kpSlash;
+ _RGFW->keycodes[0x4C] = RGFW_kpReturn;
+ _RGFW->keycodes[0x51] = RGFW_kpEqual;
+ _RGFW->keycodes[0x43] = RGFW_kpMultiply;
+ _RGFW->keycodes[0x4E] = RGFW_kpMinus;
+}
+
+i32 RGFW_initPlatform(void) {
+ _RGFW->tisBundle = (void*)CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIToolbox"));
+
+ TISGetInputSourcePropertySrc = (PFN_TISGetInputSourceProperty)CFBundleGetFunctionPointerForName((CFBundleRef)_RGFW->tisBundle, CFSTR("TISGetInputSourceProperty"));
+ TISCopyCurrentKeyboardLayoutInputSourceSrc = (PFN_TISCopyCurrentKeyboardLayoutInputSource)CFBundleGetFunctionPointerForName((CFBundleRef)_RGFW->tisBundle, CFSTR("TISCopyCurrentKeyboardLayoutInputSource"));
+ LMGetKbdTypeSrc = (PFN_LMGetKbdType)CFBundleGetFunctionPointerForName((CFBundleRef)_RGFW->tisBundle, CFSTR("LMGetKbdType"));
+
+ CFStringRef* cfStr = (CFStringRef*)CFBundleGetDataPointerForName((CFBundleRef)_RGFW->tisBundle, CFSTR("kTISPropertyUnicodeKeyLayoutData"));;
+ if (cfStr) kTISPropertyUnicodeKeyLayoutDataSrc = *cfStr;
+
+ class_addMethod(objc_getClass("NSObject"), sel_registerName("windowShouldClose:"), (IMP)(void*)RGFW_OnClose, 0);
+
+ /* NOTE(EimaMei): Fixes the 'Boop' sfx from constantly playing each time you click a key. Only a problem when running in the terminal. */
+ class_addMethod(objc_getClass("NSWindowClass"), sel_registerName("acceptsFirstResponder:"), (IMP)(void*)RGFW__osxAcceptsFirstResponder, 0);
+ class_addMethod(objc_getClass("NSWindowClass"), sel_registerName("performKeyEquivalent:"), (IMP)(void*)RGFW__osxPerformKeyEquivalent, 0);
+
+ _RGFW->NSApp = objc_msgSend_id(objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
+
+ NSRetain(_RGFW->NSApp);
+
+ _RGFW->customNSAppDelegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "RGFWNSAppDelegate", 0);
+ class_addMethod((Class)_RGFW->customNSAppDelegateClass, sel_registerName("applicationDidChangeScreenParameters:"), (IMP)RGFW__osxDidChangeScreenParameters, "v@:@");
+ objc_registerClassPair((Class)_RGFW->customNSAppDelegateClass);
+ _RGFW->customNSAppDelegate = objc_msgSend_id(NSAlloc(_RGFW->customNSAppDelegateClass), sel_registerName("init"));
+
+ objc_msgSend_void_id(_RGFW->NSApp, sel_registerName("setDelegate:"), _RGFW->customNSAppDelegate);
+
+ ((void (*)(id, SEL, NSUInteger))objc_msgSend) ((id)_RGFW->NSApp, sel_registerName("setActivationPolicy:"), NSApplicationActivationPolicyRegular);
+
+ _RGFW->customViewClasses[0] = objc_allocateClassPair(objc_getClass("NSView"), "RGFWCustomView", 0);
+ _RGFW->customViewClasses[1] = objc_allocateClassPair(objc_getClass("NSOpenGLView"), "RGFWOpenGLCustomView", 0);
+ for (size_t i = 0; i < 2; i++) {
+ class_addIvar((Class)_RGFW->customViewClasses[i], "RGFW_window", sizeof(RGFW_window*), sizeof(RGFW_window*), "L");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("drawRect:"), (IMP)RGFW__osxDrawRect, "v@:{CGRect=ffff}");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("viewDidChangeBackingProperties"), (IMP)RGFW__osxViewDidChangeBackingProperties, "v@:");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseDown:"), (IMP)RGFW__osxMouseDown, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("rightMouseDown:"), (IMP)RGFW__osxMouseDown, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("otherMouseDown:"), (IMP)RGFW__osxMouseDown, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseUp:"), (IMP)RGFW__osxMouseUp, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("rightMouseUp:"), (IMP)RGFW__osxMouseUp, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("otherMouseUp:"), (IMP)RGFW__osxMouseUp, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("scrollWheel:"), (IMP)RGFW__osxScrollWheel, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseDragged:"), (IMP)RGFW__osxMouseMoved, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("rightMouseDragged:"), (IMP)RGFW__osxMouseMoved, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("otherMouseDragged:"), (IMP)RGFW__osxMouseMoved, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("keyDown:"), (IMP)RGFW__osxKeyDown, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("keyUp:"), (IMP)RGFW__osxKeyUp, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseMoved:"), (IMP)RGFW__osxMouseMoved, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseEntered:"), (IMP)RGFW__osxMouseEntered, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseExited:"), (IMP)RGFW__osxMouseExited, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("flagsChanged:"), (IMP)RGFW__osxFlagsChanged, "v@:@");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_getUid("acceptsFirstResponder"), (IMP)RGFW__osxAcceptsFirstResponder, "B@:");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("initWithRGFWWindow:"), (IMP)RGFW__osxCustomInitWithRGFWWindow, "@@:{CGRect={CGPoint=dd}{CGSize=dd}}");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("wantsUpdateLayer"), (IMP)RGFW__osxWantsUpdateLayer, "B@:");
+ class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("updateLayer"), (IMP)RGFW__osxUpdateLayer, "v@:");
+ objc_registerClassPair((Class)_RGFW->customViewClasses[i]);
+ }
+
+ _RGFW->customWindowDelegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "RGFWWindowDelegate", 0);
+ class_addIvar((Class)_RGFW->customWindowDelegateClass, "RGFW_window", sizeof(RGFW_window*), sizeof(RGFW_window*), "L");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidResize:"), (IMP)RGFW__osxDidWindowResize, "v@:@");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidMove:"), (IMP) RGFW__osxWindowMove, "");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidMiniaturize:"), (IMP) RGFW__osxWindowMiniaturize, "");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidDeminiaturize:"), (IMP) RGFW__osxWindowDeminiaturize, "");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidBecomeKey:"), (IMP) RGFW__osxWindowBecameKey, "");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidResignKey:"), (IMP) RGFW__osxWindowResignKey, "");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("draggingEntered:"), (IMP)RGFW__osxDraggingEntered, "l@:@");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("draggingUpdated:"), (IMP)RGFW__osxDraggingUpdated, "l@:@");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("draggingExited:"), (IMP)RGFW__osxDraggingEnded, "v@:@");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("draggingEnded:"), (IMP)RGFW__osxDraggingEnded, "v@:@");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("prepareForDragOperation:"), (IMP)RGFW__osxPrepareForDragOperation, "B@:@");
+ class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("performDragOperation:"), (IMP)RGFW__osxPerformDragOperation, "B@:@");
+ objc_registerClassPair((Class)_RGFW->customWindowDelegateClass);
+ return 0;
+}
+
+void RGFW_osx_initView(RGFW_window* win) {
+ NSRect contentRect;
+ contentRect.origin.x = 0;
+ contentRect.origin.y = 0;
+ contentRect.size.width = (double)win->w;
+ contentRect.size.height = (double)win->h;
+ ((void(*)(id, SEL, CGRect))objc_msgSend)((id)win->src.view, sel_registerName("setFrame:"), contentRect);
+
+
+ if (RGFW_COCOA_FRAME_NAME)
+ objc_msgSend_ptr(win->src.view, sel_registerName("setFrameAutosaveName:"), RGFW_COCOA_FRAME_NAME);
+
+ object_setInstanceVariable((id)win->src.view, "RGFW_window", win);
+ objc_msgSend_void_id((id)win->src.window, sel_registerName("setContentView:"), win->src.view);
+ objc_msgSend_void_bool(win->src.view, sel_registerName("setWantsLayer:"), true);
+ objc_msgSend_int((id)win->src.view, sel_registerName("setLayerContentsPlacement:"), 4);
+
+ id trackingArea = objc_msgSend_id(objc_getClass("NSTrackingArea"), sel_registerName("alloc"));
+ trackingArea = ((id (*)(id, SEL, NSRect, NSUInteger, id, id))objc_msgSend)(
+ trackingArea,
+ sel_registerName("initWithRect:options:owner:userInfo:"),
+ contentRect,
+ NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect,
+ (id)win->src.view,
+ nil
+ );
+
+ ((void (*)(id, SEL, id))objc_msgSend)((id)win->src.view, sel_registerName("addTrackingArea:"), trackingArea);
+ ((void (*)(id, SEL))objc_msgSend)(trackingArea, sel_registerName("release"));
+}
+
+RGFW_window* RGFW_createWindowPlatform(const char* name, RGFW_windowFlags flags, RGFW_window* win) {
+ /* RR Create an autorelease pool */
+ id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ pool = objc_msgSend_id(pool, sel_registerName("init"));
+
+ RGFW_window_setMouseDefault(win);
+
+ NSRect windowRect;
+ windowRect.origin.x = (double)win->x;
+ windowRect.origin.y = (double)RGFW_cocoaYTransform((float)(win->y + win->h - 1));
+ windowRect.size.width = (double)win->w;
+ windowRect.size.height = (double)win->h;
+ NSBackingStoreType macArgs = (NSBackingStoreType)(NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSBackingStoreBuffered | NSWindowStyleMaskTitled);
+
+ if (!(flags & RGFW_windowNoResize))
+ macArgs = (NSBackingStoreType)(macArgs | (NSBackingStoreType)NSWindowStyleMaskResizable);
+ if (!(flags & RGFW_windowNoBorder))
+ macArgs = (NSBackingStoreType)(macArgs | (NSBackingStoreType)NSWindowStyleMaskTitled);
+ {
+ void* nsclass = objc_getClass("NSWindow");
+ SEL func = sel_registerName("initWithContentRect:styleMask:backing:defer:");
+
+ win->src.window = ((id(*)(id, SEL, NSRect, NSWindowStyleMask, NSBackingStoreType, bool))objc_msgSend)
+ (NSAlloc(nsclass), func, windowRect, (NSWindowStyleMask)macArgs, macArgs, false);
+ }
+
+ id str = NSString_stringWithUTF8String(name);
+ objc_msgSend_void_id((id)win->src.window, sel_registerName("setTitle:"), str);
+
+ win->src.delegate = (void*)objc_msgSend_id(NSAlloc((Class)_RGFW->customWindowDelegateClass), sel_registerName("init"));
+ object_setInstanceVariable((id)win->src.delegate, "RGFW_window", win);
+
+ objc_msgSend_void_id((id)win->src.window, sel_registerName("setDelegate:"), (id)win->src.delegate);
+
+ if (flags & RGFW_windowAllowDND) {
+ win->internal.flags |= RGFW_windowAllowDND;
+
+ NSPasteboardType types[] = {NSPasteboardTypeURL, NSPasteboardTypeFileURL, NSPasteboardTypeString};
+ NSregisterForDraggedTypes((id)win->src.window, types, 3);
+ }
+
+ objc_msgSend_void_bool((id)win->src.window, sel_registerName("setAcceptsMouseMovedEvents:"), true);
+
+ if (flags & RGFW_windowTransparent) {
+ objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), false);
+
+ objc_msgSend_void_id((id)win->src.window, sel_registerName("setBackgroundColor:"),
+ NSColor_colorWithSRGB(0, 0, 0, 0));
+ }
+
+ /* Show the window */
+ objc_msgSend_void_bool((id)_RGFW->NSApp, sel_registerName("activateIgnoringOtherApps:"), true);
+
+ if (_RGFW->root == NULL) {
+ objc_msgSend_void(win->src.window, sel_registerName("makeMainWindow"));
+ }
+
+ objc_msgSend_void(win->src.window, sel_registerName("makeKeyWindow"));
+
+ NSRetain(win->src.window);
+
+ win->src.view = ((id(*)(id, SEL, RGFW_window*))objc_msgSend) (NSAlloc((Class)_RGFW->customViewClasses[0]), sel_registerName("initWithRGFWWindow:"), win);
+ return win;
+}
+
+void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) {
+ NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame"));
+ NSRect content = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame"));
+ double offset = 0;
+
+ RGFW_setBit(&win->internal.flags, RGFW_windowNoBorder, !border);
+ NSBackingStoreType storeType = (NSBackingStoreType)(NSWindowStyleMaskBorderless | NSWindowStyleMaskFullSizeContentView);
+ if (border)
+ storeType = (NSBackingStoreType)(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable);
+ if (!(win->internal.flags & RGFW_windowNoResize)) {
+ storeType = (NSBackingStoreType)(storeType | (NSBackingStoreType)NSWindowStyleMaskResizable);
+ }
+
+ ((void (*)(id, SEL, NSBackingStoreType))objc_msgSend)((id)win->src.window, sel_registerName("setStyleMask:"), storeType);
+
+ if (!border) {
+ id miniaturizeButton = objc_msgSend_int((id)win->src.window, sel_registerName("standardWindowButton:"), NSWindowMiniaturizeButton);
+ id titleBarView = objc_msgSend_id(miniaturizeButton, sel_registerName("superview"));
+ objc_msgSend_void_bool(titleBarView, sel_registerName("setHidden:"), true);
+
+ offset = (double)(frame.size.height - content.size.height);
+ }
+
+ RGFW_window_resize(win, win->w, win->h + (i32)offset);
+ win->h -= (i32)offset;
+}
+
+RGFW_bool RGFW_getGlobalMouse(i32* x, i32* y) {
+ RGFW_ASSERT(_RGFW->root != NULL);
+
+ CGEventRef e = CGEventCreate(NULL);
+ CGPoint point = CGEventGetLocation(e);
+ CFRelease(e);
+
+ if (x) *x = (i32)point.x;
+ if (y) *y = (i32)point.y;
+ return RGFW_TRUE;
+}
+
+void RGFW_stopCheckEvents(void) {
+ id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ eventPool = objc_msgSend_id(eventPool, sel_registerName("init"));
+
+ id e = (id) ((id(*)(Class, SEL, NSEventType, NSPoint, NSEventModifierFlags, void*, NSInteger, void**, short, NSInteger, NSInteger))objc_msgSend)
+ (objc_getClass("NSEvent"), sel_registerName("otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:"),
+ NSEventTypeApplicationDefined, (NSPoint){0, 0}, (NSEventModifierFlags)0, NULL, (NSInteger)0, NULL, 0, 0, 0);
+
+ ((void (*)(id, SEL, id, bool))objc_msgSend)
+ ((id)_RGFW->NSApp, sel_registerName("postEvent:atStart:"), e, 1);
+
+ objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
+}
+
+void RGFW_waitForEvent(i32 waitMS) {
+ id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ eventPool = objc_msgSend_id(eventPool, sel_registerName("init"));
+
+ void* date = (void*) ((id(*)(Class, SEL, double))objc_msgSend)
+ (objc_getClass("NSDate"), sel_registerName("dateWithTimeIntervalSinceNow:"), waitMS);
+
+ SEL eventFunc = sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:");
+ id e = (id) ((id(*)(id, SEL, NSEventMask, void*, id, bool))objc_msgSend)
+ ((id)_RGFW->NSApp, eventFunc,
+ ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true);
+
+ if (e) {
+ ((void (*)(id, SEL, id, bool))objc_msgSend)
+ ((id)_RGFW->NSApp, sel_registerName("postEvent:atStart:"), e, 1);
+ }
+
+ objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
+}
+
+RGFW_key RGFW_physicalToMappedKey(RGFW_key key) {
+ u16 keycode = (u16)RGFW_rgfwToApiKey(key);
+ TISInputSourceRef source = TISCopyCurrentKeyboardLayoutInputSource();
+ if (source == NULL)
+ return key;
+
+ CFDataRef layoutData = TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutDataSrc);
+
+ if (layoutData == NULL) {
+ CFRelease(source);
+ return key;
+ }
+
+ UCKeyboardLayout *layout = (UCKeyboardLayout*)(void*)CFDataGetBytePtr(layoutData);
+
+ UInt32 deadKeyState = 0;
+ UniChar chars[4];
+ UniCharCount len = 0;
+ u32 type = LMGetKbdType();
+ OSStatus status = UCKeyTranslate(layout, keycode, kUCKeyActionDown, 0, type, kUCKeyTranslateNoDeadKeysBit, &deadKeyState, 4, &len, chars );
+
+ CFRelease(source);
+
+ if (status == noErr && len == 1 && chars[0] < 256) {
+ return (RGFW_key)chars[0];
+ }
+
+ return key;
+}
+
+void RGFW_pollEvents(void) {
+ RGFW_resetPrevState();
+
+ id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ eventPool = objc_msgSend_id(eventPool, sel_registerName("init"));
+ SEL eventFunc = sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:");
+
+ while (1) {
+ void* date = NULL;
+ id e = (id) ((id(*)(id, SEL, NSEventMask, void*, id, bool))objc_msgSend)
+ ((id)_RGFW->NSApp, eventFunc, ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true);
+
+ if (e == NULL) {
+ break;
+ }
+
+ objc_msgSend_void_id((id)_RGFW->NSApp, sel_registerName("sendEvent:"), e);
+ }
+
+ objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
+}
+
+
+void RGFW_window_move(RGFW_window* win, i32 x, i32 y) {
+ RGFW_ASSERT(win != NULL);
+
+ NSRect content = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame"));
+
+ win->x = x;
+ win->y = (i32)RGFW_cocoaYTransform((float)y + (float)content.size.height - 1.0f);
+
+ ((void(*)(id,SEL,NSPoint))objc_msgSend)((id)win->src.window, sel_registerName("setFrameOrigin:"), (NSPoint){(double)x, (double)y});
+}
+
+void RGFW_window_resize(RGFW_window* win, i32 w, i32 h) {
+ RGFW_ASSERT(win != NULL);
+
+ NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame"));
+ NSRect content = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame"));
+ float offset = (float)(frame.size.height - content.size.height);
+
+ win->w = w;
+ win->h = h;
+
+
+ ((void(*)(id, SEL, CGRect))objc_msgSend)((id)win->src.view, sel_registerName("setFrame:"), (NSRect){{0, 0}, {(double)win->w, (double)win->h}});
+ ((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend)
+ ((id)win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{(double)win->x, (double)win->y}, {(double)win->w, (double)win->h + (double)offset}}, true, true);
+}
+
+void RGFW_window_focus(RGFW_window* win) {
+ RGFW_ASSERT(win);
+ objc_msgSend_void_bool((id)_RGFW->NSApp, sel_registerName("activateIgnoringOtherApps:"), true);
+ ((void (*)(id, SEL))objc_msgSend)((id)win->src.window, sel_registerName("makeKeyWindow"));
+}
+
+void RGFW_window_raise(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("orderFront:"), (SEL)NULL);
+ objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGNormalWindowLevelKey);
+}
+
+void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) {
+ RGFW_ASSERT(win != NULL);
+ if (fullscreen && (win->internal.flags & RGFW_windowFullscreen)) return;
+ if (!fullscreen && !(win->internal.flags & RGFW_windowFullscreen)) return;
+
+ if (fullscreen) {
+ win->internal.oldX = win->x;
+ win->internal.oldY = win->y;
+ win->internal.oldW = win->w;
+ win->internal.oldH = win->h;
+
+ win->internal.flags |= RGFW_windowFullscreen;
+
+ RGFW_monitor* mon = RGFW_window_getMonitor(win);
+ RGFW_monitor_scaleToWindow(mon, win);
+
+ RGFW_window_setBorder(win, RGFW_FALSE);
+
+ if (mon != NULL) {
+ win->x = mon->x;
+ win->y = mon->y;
+ win->w = mon->mode.w;
+ win->h = mon->mode.h;
+ RGFW_window_resize(win, mon->mode.w, mon->mode.h);
+ RGFW_window_move(win, mon->x, mon->y);
+ }
+
+ ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("orderFront:"), (SEL)NULL);
+ objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), 25);
+ }
+
+ objc_msgSend_void_SEL(win->src.window, sel_registerName("toggleFullScreen:"), NULL);
+
+ if (!fullscreen) {
+ win->x = win->internal.oldX;
+ win->y = win->internal.oldY;
+ win->w = win->internal.oldW;
+ win->h = win->internal.oldH;
+ win->internal.flags &= ~(u32)RGFW_windowFullscreen;
+
+ RGFW_window_resize(win, win->w, win->h);
+ RGFW_window_move(win, win->x, win->y);
+ }
+}
+
+void RGFW_window_maximize(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ if (RGFW_window_isMaximized(win)) return;
+
+ win->internal.flags |= RGFW_windowMaximize;
+ objc_msgSend_void_SEL(win->src.window, sel_registerName("zoom:"), NULL);
+}
+
+void RGFW_window_minimize(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ objc_msgSend_void_SEL(win->src.window, sel_registerName("performMiniaturize:"), NULL);
+}
+
+void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) {
+ RGFW_ASSERT(win != NULL);
+ if (floating) objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGFloatingWindowLevelKey);
+ else objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGNormalWindowLevelKey);
+}
+
+void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) {
+ objc_msgSend_int(win->src.window, sel_registerName("setAlphaValue:"), opacity);
+ objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), (opacity < (u8)255));
+
+ if (opacity)
+ objc_msgSend_void_id((id)win->src.window, sel_registerName("setBackgroundColor:"), NSColor_colorWithSRGB(0, 0, 0, opacity));
+
+}
+
+void RGFW_window_restore(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+
+ if (RGFW_window_isMaximized(win))
+ objc_msgSend_void_SEL(win->src.window, sel_registerName("zoom:"), NULL);
+
+ objc_msgSend_void_SEL(win->src.window, sel_registerName("deminiaturize:"), NULL);
+ RGFW_window_show(win);
+}
+
+RGFW_bool RGFW_window_isFloating(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ int level = ((int (*)(id, SEL))objc_msgSend) ((id)(win->src.window), (SEL)sel_registerName("level"));
+ return level > kCGNormalWindowLevelKey;
+}
+
+void RGFW_window_setName(RGFW_window* win, const char* name) {
+ RGFW_ASSERT(win != NULL);
+ if (name == NULL) name = "\0";
+
+ id str = NSString_stringWithUTF8String(name);
+ objc_msgSend_void_id((id)win->src.window, sel_registerName("setTitle:"), str);
+}
+
+#ifndef RGFW_NO_PASSTHROUGH
+void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) {
+ objc_msgSend_void_bool(win->src.window, sel_registerName("setIgnoresMouseEvents:"), passthrough);
+}
+#endif
+
+void RGFW_window_setAspectRatio(RGFW_window* win, i32 w, i32 h) {
+ if (w == 0 && h == 0) { w = 1; h = 1; };
+
+ ((void (*)(id, SEL, NSSize))objc_msgSend)
+ ((id)win->src.window, sel_registerName("setContentAspectRatio:"), (NSSize){(CGFloat)w, (CGFloat)h});
+}
+
+void RGFW_window_setMinSize(RGFW_window* win, i32 w, i32 h) {
+ ((void (*)(id, SEL, NSSize))objc_msgSend) ((id)win->src.window, sel_registerName("setMinSize:"), (NSSize){(CGFloat)w, (CGFloat)h});
+}
+
+void RGFW_window_setMaxSize(RGFW_window* win, i32 w, i32 h) {
+ if (w == 0 && h == 0) {
+ RGFW_monitor* mon = RGFW_window_getMonitor(win);
+ if (mon != NULL) {
+ w = mon->mode.w;
+ h = mon->mode.h;
+ }
+ }
+
+ ((void (*)(id, SEL, NSSize))objc_msgSend)
+ ((id)win->src.window, sel_registerName("setMaxSize:"), (NSSize){(CGFloat)w, (CGFloat)h});
+}
+
+RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_icon type) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_UNUSED(type);
+
+ id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ pool = objc_msgSend_id(pool, sel_registerName("init"));
+
+ if (data == NULL) {
+ objc_msgSend_void_id((id)_RGFW->NSApp, sel_registerName("setApplicationIconImage:"), NULL);
+ objc_msgSend_bool_void(pool, sel_registerName("drain"));
+ return RGFW_TRUE;
+ }
+
+ id representation = NSBitmapImageRep_initWithBitmapData(NULL, w, h, 8, (NSInteger)4, true, false, "NSCalibratedRGBColorSpace", 1 << 1, w * 4, 32);
+ RGFW_copyImageData(NSBitmapImageRep_bitmapData(representation), w, h, RGFW_formatRGBA8, data, format, NULL);
+
+ id dock_image = ((id(*)(id, SEL, NSSize))objc_msgSend) (NSAlloc((id)objc_getClass("NSImage")), sel_registerName("initWithSize:"), ((NSSize){(CGFloat)w, (CGFloat)h}));
+
+ objc_msgSend_void_id(dock_image, sel_registerName("addRepresentation:"), representation);
+
+ objc_msgSend_void_id((id)_RGFW->NSApp, sel_registerName("setApplicationIconImage:"), dock_image);
+
+ NSRelease(dock_image);
+ NSRelease(representation);
+
+ objc_msgSend_bool_void(pool, sel_registerName("drain"));
+
+ return RGFW_TRUE;
+}
+
+id NSCursor_arrowStr(const char* str);
+id NSCursor_arrowStr(const char* str) {
+ void* nclass = objc_getClass("NSCursor");
+ SEL func = sel_registerName(str);
+ return (id) objc_msgSend_id(nclass, func);
+}
+
+RGFW_mouse* RGFW_loadMouse(u8* data, i32 w, i32 h, RGFW_format format) {
+ id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ pool = objc_msgSend_id(pool, sel_registerName("init"));
+
+ if (data == NULL) {
+ objc_msgSend_void(NSCursor_arrowStr("arrowCursor"), sel_registerName("set"));
+
+ objc_msgSend_bool_void(pool, sel_registerName("drain"));
+ return NULL;
+ }
+
+ id representation = (id)NSBitmapImageRep_initWithBitmapData(NULL, w, h, 8, (NSInteger)4, true, false, "NSCalibratedRGBColorSpace", 1 << 1, w * 4, 32);
+ RGFW_copyImageData(NSBitmapImageRep_bitmapData(representation), w, h, RGFW_formatRGBA8, data, format, NULL);
+
+ id cursor_image = ((id(*)(id, SEL, NSSize))objc_msgSend) (NSAlloc((id)objc_getClass("NSImage")), sel_registerName("initWithSize:"), ((NSSize){(CGFloat)w, (CGFloat)h}));
+
+ objc_msgSend_void_id(cursor_image, sel_registerName("addRepresentation:"), representation);
+
+ id cursor = (id) ((id(*)(id, SEL, id, NSPoint))objc_msgSend)
+ (NSAlloc(objc_getClass("NSCursor")), sel_registerName("initWithImage:hotSpot:"), cursor_image, (NSPoint){0.0, 0.0});
+
+ NSRelease(cursor_image);
+ NSRelease(representation);
+
+ objc_msgSend_bool_void(pool, sel_registerName("drain"));
+
+ return (void*)cursor;
+}
+
+void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) {
+ RGFW_ASSERT(win != NULL); RGFW_ASSERT(mouse);
+ CGDisplayShowCursor(kCGDirectMainDisplay);
+ objc_msgSend_void((id)mouse, sel_registerName("set"));
+ win->src.mouse = mouse;
+}
+
+void RGFW_freeMouse(RGFW_mouse* mouse) {
+ RGFW_ASSERT(mouse);
+ NSRelease((id)mouse);
+}
+
+RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) {
+ return RGFW_window_setMouseStandard(win, RGFW_mouseArrow);
+}
+
+void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) {
+ RGFW_window_showMouseFlags(win, show);
+ if (show) CGDisplayShowCursor(kCGDirectMainDisplay);
+ else CGDisplayHideCursor(kCGDirectMainDisplay);
+}
+
+RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 stdMouse) {
+ const char* cursorSelectorStr;
+ switch (stdMouse) {
+ case RGFW_mouseNormal: cursorSelectorStr = "arrowCursor"; break;
+ case RGFW_mouseArrow: cursorSelectorStr = "arrowCursor"; break;
+ case RGFW_mouseIbeam: cursorSelectorStr = "IBeamCursor"; break;
+ case RGFW_mouseCrosshair: cursorSelectorStr = "crosshairCursor"; break;
+ case RGFW_mousePointingHand: cursorSelectorStr = "pointingHandCursor"; break;
+ case RGFW_mouseResizeEW: cursorSelectorStr = "resizeLeftRightCursor"; break;
+ case RGFW_mouseResizeE: cursorSelectorStr = "resizeLeftRightCursor"; break;
+ case RGFW_mouseResizeW: cursorSelectorStr = "resizeLeftRightCursor"; break;
+ case RGFW_mouseResizeNS: cursorSelectorStr = "resizeUpDownCursor"; break;
+ case RGFW_mouseResizeN: cursorSelectorStr = "resizeUpDownCursor"; break;
+ case RGFW_mouseResizeS: cursorSelectorStr = "resizeUpDownCursor"; break;
+ case RGFW_mouseResizeNWSE: cursorSelectorStr = "_windowResizeNorthWestSouthEastCursor"; break;
+ case RGFW_mouseResizeNW: cursorSelectorStr = "_windowResizeNorthWestSouthEastCursor"; break;
+ case RGFW_mouseResizeSE: cursorSelectorStr = "_windowResizeNorthWestSouthEastCursor"; break;
+ case RGFW_mouseResizeNESW: cursorSelectorStr = "_windowResizeNorthEastSouthWestCursor"; break;
+ case RGFW_mouseResizeNE: cursorSelectorStr = "_windowResizeNorthEastSouthWestCursor"; break;
+ case RGFW_mouseResizeSW: cursorSelectorStr = "_windowResizeNorthEastSouthWestCursor"; break;
+ case RGFW_mouseResizeAll: cursorSelectorStr = "openHandCursor"; break;
+ case RGFW_mouseNotAllowed: cursorSelectorStr = "operationNotAllowedCursor"; break;
+ case RGFW_mouseWait: cursorSelectorStr = "arrowCursor"; break;
+ case RGFW_mouseProgress: cursorSelectorStr = "arrowCursor"; break;
+ default:
+ return RGFW_FALSE;
+ }
+
+ id mouse = NSCursor_arrowStr(cursorSelectorStr);
+
+ if (mouse == NULL)
+ return RGFW_FALSE;
+
+ RGFW_UNUSED(win);
+ CGDisplayShowCursor(kCGDirectMainDisplay);
+ objc_msgSend_void(mouse, sel_registerName("set"));
+ win->src.mouse = mouse;
+
+ return RGFW_TRUE;
+}
+
+void RGFW_window_setRawMouseModePlatform(RGFW_window* win, RGFW_bool state) {
+ RGFW_UNUSED(win); RGFW_UNUSED(state);
+}
+
+void RGFW_window_captureMousePlatform(RGFW_window* win, RGFW_bool state) {
+ RGFW_UNUSED(win);
+ CGAssociateMouseAndMouseCursorPosition(!(state == RGFW_TRUE));
+}
+
+void RGFW_window_moveMouse(RGFW_window* win, i32 x, i32 y) {
+ RGFW_UNUSED(win);
+
+ win->internal.lastMouseX = x - win->x;
+ win->internal.lastMouseY = y - win->y;
+ CGWarpMouseCursorPosition((CGPoint){(CGFloat)x, (CGFloat)y});
+}
+
+
+void RGFW_window_hide(RGFW_window* win) {
+ objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), false);
+}
+
+void RGFW_window_show(RGFW_window* win) {
+ if (win->internal.flags & RGFW_windowFocusOnShow)
+ ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL);
+
+ ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("orderFront:"), NULL);
+ objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true);
+}
+
+void RGFW_window_flash(RGFW_window* win, RGFW_flashRequest request) {
+ if (RGFW_window_isInFocus(win) && request) {
+ return;
+ }
+
+ id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ pool = objc_msgSend_id(pool, sel_registerName("init"));
+
+ if (_RGFW->flash) {
+ ((void (*)(id, SEL, NSInteger))objc_msgSend) ((id)_RGFW->NSApp, sel_registerName("cancelUserAttentionRequest:"), _RGFW->flash);
+ }
+
+ switch (request) {
+ case RGFW_flashBriefly:
+ _RGFW->flash = ((NSInteger (*)(id, SEL, NSInteger))objc_msgSend) ((id)_RGFW->NSApp, sel_registerName("requestUserAttention:"), NSInformationalRequest);
+ break;
+ case RGFW_flashUntilFocused:
+ _RGFW->flash = ((NSInteger (*)(id, SEL, NSInteger))objc_msgSend) ((id)_RGFW->NSApp, sel_registerName("requestUserAttention:"), NSCriticalRequest);
+ break;
+ default: break;
+ }
+
+ objc_msgSend_bool_void(pool, sel_registerName("drain"));
+}
+
+RGFW_bool RGFW_window_isHidden(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+
+ bool visible = objc_msgSend_bool(win->src.window, sel_registerName("isVisible"));
+ return visible == NO && !RGFW_window_isMinimized(win);
+}
+
+RGFW_bool RGFW_window_isMinimized(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+
+ return objc_msgSend_bool(win->src.window, sel_registerName("isMiniaturized")) == YES;
+}
+
+RGFW_bool RGFW_window_isMaximized(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+ RGFW_bool b = (RGFW_bool)objc_msgSend_bool(win->src.window, sel_registerName("isZoomed"));
+ return b;
+}
+
+RGFWDEF id RGFW_getNSScreenForDisplayUInt(u32 uintNum);
+id RGFW_getNSScreenForDisplayUInt(u32 uintNum) {
+ Class NSScreenClass = objc_getClass("NSScreen");
+
+ id screens = objc_msgSend_id(NSScreenClass, sel_registerName("screens"));
+
+ NSUInteger count = (NSUInteger)objc_msgSend_uint(screens, sel_registerName("count"));
+ NSUInteger i;
+ for (i = 0; i < count; i++) {
+ id screen = ((id (*)(id, SEL, int))objc_msgSend) (screens, sel_registerName("objectAtIndex:"), (int)i);
+ id description = objc_msgSend_id(screen, sel_registerName("deviceDescription"));
+ id screenNumberKey = NSString_stringWithUTF8String("NSScreenNumber");
+ id screenNumber = objc_msgSend_id_id(description, sel_registerName("objectForKey:"), screenNumberKey);
+
+ if (CGDisplayUnitNumber((CGDirectDisplayID)objc_msgSend_uint(screenNumber, sel_registerName("unsignedIntValue"))) == uintNum) {
+ return screen;
+ }
+ }
+
+ return NULL;
+}
+
+float RGFW_osx_getRefreshRate(CGDirectDisplayID display, CGDisplayModeRef mode);
+float RGFW_osx_getRefreshRate(CGDirectDisplayID display, CGDisplayModeRef mode) {
+ if (mode) {
+ float refreshRate = (float)CGDisplayModeGetRefreshRate(mode);
+ if (refreshRate != 0) return refreshRate;
+ }
+
+#ifndef RGFW_NO_IOKIT
+ float res = RGFW_osx_getFallbackRefreshRate(display);
+ if (res != 0) return res;
+#else
+ RGFW_UNUSED(display);
+#endif
+ return 60;
+}
+
+void RGFW_pollMonitors(void) {
+ static CGDirectDisplayID displays[RGFW_MAX_MONITORS];
+ u32 count;
+
+ if (CGGetActiveDisplayList(RGFW_MAX_MONITORS, displays, &count) != kCGErrorSuccess) {
+ return;
+ }
+
+ if (count > RGFW_MAX_MONITORS) count = RGFW_MAX_MONITORS;
+
+ for (RGFW_monitorNode* node = _RGFW->monitors.list.head; node; node = node->next) {
+ node->disconnected = RGFW_TRUE;
+ }
+
+ CGDirectDisplayID primary = CGMainDisplayID();
+
+ u32 i;
+ for (i = 0; i < count; i++) {
+ RGFW_monitor monitor;
+
+ u32 uintNum = CGDisplayUnitNumber(displays[i]);
+ id screen = RGFW_getNSScreenForDisplayUInt(uintNum);
+
+ RGFW_monitorNode* node;
+ for (node = _RGFW->monitors.list.head; node; node = node->next) {
+ if (node->uintNum == uintNum) break;
+ }
+
+ if (node) {
+ node->screen = (void*)screen;
+ node->display = displays[i];
+ node->disconnected = RGFW_FALSE;
+ if (displays[i] == primary) {
+ _RGFW->monitors.primary = node;
+ }
+ continue;
+ }
+
+ const char name[] = "MacOS\0";
+ RGFW_MEMCPY(monitor.name, name, 6);
+
+ CGRect bounds = CGDisplayBounds(displays[i]);
+ monitor.x = (i32)bounds.origin.x;
+ monitor.y = (i32)RGFW_cocoaYTransform((float)(bounds.origin.y + bounds.size.height - 1));
+
+ CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displays[i]);
+ monitor.mode.w = (i32)CGDisplayModeGetWidth(mode);
+ monitor.mode.h = (i32)CGDisplayModeGetHeight(mode);
+ monitor.mode.src = (void*)mode;
+ monitor.mode.red = 8; monitor.mode.green = 8; monitor.mode.blue = 8;
+
+ monitor.mode.refreshRate = RGFW_osx_getRefreshRate(displays[i], mode);
+ CFRelease(mode);
+
+ CGSize screenSizeMM = CGDisplayScreenSize(displays[i]);
+ monitor.physW = (float)screenSizeMM.width / 25.4f;
+ monitor.physH = (float)screenSizeMM.height / 25.4f;
+
+ float ppi_width = ((float)monitor.mode.w / monitor.physW);
+ float ppi_height = ((float)monitor.mode.h / monitor.physH);
+
+ monitor.pixelRatio = (float)((CGFloat (*)(id, SEL))abi_objc_msgSend_fpret) (screen, sel_registerName("backingScaleFactor"));
+ float dpi = 96.0f * monitor.pixelRatio;
+
+ monitor.scaleX = ((((float) (ppi_width) / dpi) * 10.0f)) / 10.0f;
+ monitor.scaleY = ((((float) (ppi_height) / dpi) * 10.0f)) / 10.0f;
+
+ node = RGFW_monitors_add(&monitor);
+
+ node->screen = (void*)screen;
+ node->uintNum = uintNum;
+ node->display = displays[i];
+
+ if (displays[i] == primary) {
+ _RGFW->monitors.primary = node;
+ }
+
+ RGFW_monitorCallback(_RGFW->root, &node->mon, RGFW_TRUE);
+ }
+
+ RGFW_monitors_refresh();
+}
+
+RGFW_bool RGFW_monitor_getWorkarea(RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height) {
+ NSRect frameRect = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)monitor->node->screen, sel_registerName("visibleFrame"));
+
+ if (x) *x = (i32)frameRect.origin.x;
+ if (y) *y = (i32)RGFW_cocoaYTransform((float)(frameRect.origin.y + frameRect.size.height - (double)1.0f));
+ if (width) *width = (i32)frameRect.size.width;
+ if (height) *height = (i32)frameRect.size.height;
+
+ return RGFW_TRUE;
+}
+
+size_t RGFW_monitor_getGammaRampPtr(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) {
+ id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ pool = objc_msgSend_id(pool, sel_registerName("init"));
+
+ u32 size = CGDisplayGammaTableCapacity(monitor->node->display);
+ CGGammaValue* values = (CGGammaValue*)RGFW_ALLOC(size * 3 * sizeof(CGGammaValue));
+
+ CGGetDisplayTransferByTable(monitor->node->display, size, values, values + size, values + size * 2, &size);
+
+ for (u32 i = 0; ramp && i < size; i++) {
+ ramp->red[i] = (u16) (values[i] * 65535);
+ ramp->green[i] = (u16) (values[i + size] * 65535);
+ ramp->blue[i] = (u16) (values[i + size * 2] * 65535);
+ }
+
+ RGFW_FREE(values);
+
+ objc_msgSend_bool_void(pool, sel_registerName("drain"));
+ return size;
+}
+
+RGFW_bool RGFW_monitor_setGammaRamp(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) {
+ id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ pool = objc_msgSend_id(pool, sel_registerName("init"));
+
+ CGGammaValue* values = (CGGammaValue*)RGFW_ALLOC(ramp->count * 3 * sizeof(CGGammaValue));
+
+ for (u32 i = 0; i < ramp->count; i++) {
+ values[i] = ramp->red[i] / 65535.f;
+ values[i + ramp->count] = ramp->green[i] / 65535.f;
+ values[i + ramp->count * 2] = ramp->blue[i] / 65535.f;
+ }
+
+ CGSetDisplayTransferByTable(monitor->node->display, (u32)ramp->count, values, values + ramp->count, values + ramp->count * 2);
+
+ RGFW_FREE(values);
+
+ objc_msgSend_bool_void(pool, sel_registerName("drain"));
+
+ return RGFW_TRUE;
+}
+
+size_t RGFW_monitor_getModesPtr(RGFW_monitor* mon, RGFW_monitorMode** modes) {
+ CGDirectDisplayID display = mon->node->display;
+ CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, NULL);
+
+ if (allModes == NULL) {
+ return RGFW_FALSE;
+ }
+
+ size_t count = (size_t)CFArrayGetCount(allModes);
+
+ CFIndex i;
+ for (i = 0; i < (CFIndex)count && modes; i++) {
+ CGDisplayModeRef cmode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i);
+
+ RGFW_monitorMode foundMode;
+ foundMode.w = (i32)CGDisplayModeGetWidth(cmode);
+ foundMode.h = (i32)CGDisplayModeGetHeight(cmode);
+ foundMode.refreshRate = RGFW_osx_getRefreshRate(display, cmode);
+ foundMode.red = 8; foundMode.green = 8; foundMode.blue = 8;
+ foundMode.src = (void*)cmode;
+ (*modes)[i] = foundMode;
+ }
+
+ CFRelease(allModes);
+ return count;
+}
+
+RGFW_bool RGFW_monitor_setMode(RGFW_monitor* mon, RGFW_monitorMode* mode) {
+ if (CGDisplaySetDisplayMode(mon->node->display, (CGDisplayModeRef)mode->src, NULL) == kCGErrorSuccess) {
+ return RGFW_TRUE;
+ }
+
+ return RGFW_FALSE;
+}
+
+RGFW_bool RGFW_monitor_requestMode(RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request) {
+ CGDirectDisplayID display = mon->node->display;
+ CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, NULL);
+
+ if (allModes == NULL) {
+ return RGFW_FALSE;
+ }
+
+ CGDisplayModeRef native = NULL;
+
+ CFIndex i;
+ for (i = 0; i < CFArrayGetCount(allModes); i++) {
+ CGDisplayModeRef cmode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i);
+
+ RGFW_monitorMode foundMode;
+ foundMode.w = (i32)CGDisplayModeGetWidth(cmode);
+ foundMode.h = (i32)CGDisplayModeGetHeight(cmode);
+ foundMode.refreshRate = RGFW_osx_getRefreshRate(display, cmode);
+ foundMode.red = 8; foundMode.green = 8; foundMode.blue = 8;
+ foundMode.src = (void*)cmode;
+
+ if (RGFW_monitorModeCompare(mode, &foundMode, request)) {
+ native = cmode;
+ mon->mode = foundMode;
+ break;
+ }
+ }
+
+ CFRelease(allModes);
+
+ if (native) {
+ if (CGDisplaySetDisplayMode(display, native, NULL) == kCGErrorSuccess) {
+ return RGFW_TRUE;
+ }
+ }
+
+ return RGFW_FALSE;
+}
+
+RGFW_monitor* RGFW_window_getMonitor(RGFW_window* win) {
+ id screen = objc_msgSend_id(win->src.window, sel_registerName("screen"));
+ id description = objc_msgSend_id(screen, sel_registerName("deviceDescription"));
+ id screenNumberKey = NSString_stringWithUTF8String("NSScreenNumber");
+ id screenNumber = objc_msgSend_id_id(description, sel_registerName("objectForKey:"), screenNumberKey);
+
+ CGDirectDisplayID display = (CGDirectDisplayID)objc_msgSend_uint(screenNumber, sel_registerName("unsignedIntValue"));
+
+ RGFW_monitorNode* node = _RGFW->monitors.list.head;
+ for (node = _RGFW->monitors.list.head; node; node = node->next) {
+ if (node->display == display && (id)node->screen == screen) {
+ break;
+ }
+ }
+
+ return &node->mon;
+}
+
+RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) {
+ size_t clip_len;
+ char* clip = (char*)NSPasteboard_stringForType(NSPasteboard_generalPasteboard(), NSPasteboardTypeString, &clip_len);
+ if (clip == NULL) return -1;
+
+ if (str != NULL) {
+ if (strCapacity < clip_len)
+ return 0;
+
+ RGFW_MEMCPY(str, clip, clip_len);
+
+ str[clip_len] = '\0';
+ }
+
+ return (RGFW_ssize_t)clip_len;
+}
+
+void RGFW_writeClipboard(const char* text, u32 textLen) {
+ RGFW_UNUSED(textLen);
+
+ NSPasteboardType array[] = { NSPasteboardTypeString, NULL };
+ NSPasteBoard_declareTypes(NSPasteboard_generalPasteboard(), array, 1, NULL);
+
+ SEL func = sel_registerName("setString:forType:");
+ ((bool (*)(id, SEL, id, id))objc_msgSend)
+ (NSPasteboard_generalPasteboard(), func, NSString_stringWithUTF8String(text), NSString_stringWithUTF8String((const char*)NSPasteboardTypeString));
+}
+
+#ifdef RGFW_OPENGL
+void NSOpenGLContext_setValues(id context, const int* vals, NSOpenGLContextParameter param);
+void NSOpenGLContext_setValues(id context, const int* vals, NSOpenGLContextParameter param) {
+ ((void (*)(id, SEL, const int*, NSOpenGLContextParameter))objc_msgSend)
+ (context, sel_registerName("setValues:forParameter:"), vals, param);
+}
+
+
+/* MacOS OpenGL API spares us yet again (there are no extensions) */
+RGFW_bool RGFW_extensionSupportedPlatform_OpenGL(const char * extension, size_t len) { RGFW_UNUSED(extension); RGFW_UNUSED(len); return RGFW_FALSE; }
+
+RGFW_proc RGFW_getProcAddress_OpenGL(const char* procname) {
+ static CFBundleRef RGFWnsglFramework = NULL;
+ if (RGFWnsglFramework == NULL)
+ RGFWnsglFramework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
+
+ CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, procname, kCFStringEncodingASCII);
+
+ RGFW_proc symbol = (RGFW_proc)CFBundleGetFunctionPointerForName(RGFWnsglFramework, symbolName);
+
+ CFRelease(symbolName);
+
+ return symbol;
+}
+
+RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) {
+ win->src.ctx.native = ctx;
+ win->src.gfxType = RGFW_gfxNativeOpenGL;
+
+ i32 attribs[40];
+ size_t render_type_index = 0;
+ {
+ RGFW_attribStack stack;
+ RGFW_attribStack_init(&stack, attribs, 40);
+
+ i32 colorBits = (i32)(hints->red + hints->green + hints->blue + hints->alpha) / 4;
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAColorSize, colorBits);
+
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAAlphaSize, hints->alpha);
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFADepthSize, hints->depth);
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAStencilSize, hints->stencil);
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAAuxBuffers, hints->auxBuffers);
+ RGFW_attribStack_pushAttrib(&stack, NSOpenGLPFAClosestPolicy);
+ if (hints->samples) {
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFASampleBuffers, 1);
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFASamples, hints->samples);
+ } else RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFASampleBuffers, 0);
+
+ if (hints->doubleBuffer)
+ RGFW_attribStack_pushAttrib(&stack, NSOpenGLPFADoubleBuffer);
+
+ #ifdef RGFW_COCOA_GRAPHICS_SWITCHING
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAAllowOfflineRenderers, kCGLPFASupportsAutomaticGraphicsSwitching);
+ #endif
+ #if MAC_OS_X_VERSION_MAX_ALLOWED < 101200
+ if (hints->stereo) RGFW_attribStack_pushAttrib(&stack, NSOpenGLPFAStereo);
+ #endif
+
+ /* macOS has the surface attribs and the OpenGL attribs connected for some reason maybe this is to give macOS more control to limit openGL/the OpenGL version? */
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAOpenGLProfile,
+ (hints->major >= 4) ? NSOpenGLProfileVersion4_1Core : (hints->major >= 3) ?
+ NSOpenGLProfileVersion3_2Core : NSOpenGLProfileVersionLegacy);
+
+ if (hints->major <= 2) {
+ i32 accumSize = (i32)(hints->accumRed + hints->accumGreen + hints->accumBlue + hints->accumAlpha) / 4;
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAAccumSize, accumSize);
+ }
+
+ if (hints->renderer == RGFW_glSoftware) {
+ RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFARendererID, kCGLRendererGenericFloatID);
+ } else {
+ RGFW_attribStack_pushAttrib(&stack, NSOpenGLPFAAccelerated);
+ }
+ render_type_index = stack.count - 1;
+
+ RGFW_attribStack_pushAttribs(&stack, 0, 0);
+ }
+
+ void* format = (void*) ((id(*)(id, SEL, const u32*))objc_msgSend) (NSAlloc((id)objc_getClass("NSOpenGLPixelFormat")), sel_registerName("initWithAttributes:"), (u32*)attribs);
+ if (format == NULL) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to load pixel format for OpenGL");
+
+ assert(render_type_index + 3 < (sizeof(attribs) / sizeof(attribs[0])));
+ attribs[render_type_index] = NSOpenGLPFARendererID;
+ attribs[render_type_index + 1] = kCGLRendererGenericFloatID;
+ attribs[render_type_index + 3] = 0;
+
+ format = (void*) ((id(*)(id, SEL, const u32*))objc_msgSend) (NSAlloc((id)objc_getClass("NSOpenGLPixelFormat")), sel_registerName("initWithAttributes:"), (u32*)attribs);
+ if (format == NULL)
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "and loading software rendering OpenGL failed");
+ else
+ RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, "Switching to software rendering");
+ }
+
+ /* the pixel format can be passed directly to OpenGL context creation to create a context
+ this is because the format also includes information about the OpenGL version (which may be a bad thing) */
+
+ if (win->src.view)
+ NSRelease(win->src.view);
+ win->src.view = (id) ((id(*)(id, SEL, NSRect, u32*))objc_msgSend) (NSAlloc(_RGFW->customViewClasses[1]),
+ sel_registerName("initWithFrame:pixelFormat:"), (NSRect){{0, 0}, {(double)win->w, (double)win->h}}, (u32*)format);
+
+ id share = NULL;
+ if (hints->share) {
+ share = (id)hints->share->ctx;
+ }
+
+ win->src.ctx.native->ctx = ((id (*)(id, SEL, id, id))objc_msgSend)(NSAlloc(objc_getClass("NSOpenGLContext")),
+ sel_registerName("initWithFormat:shareContext:"),
+ (id)format, share);
+
+ win->src.ctx.native->format = format;
+
+ objc_msgSend_void_id(win->src.view, sel_registerName("setOpenGLContext:"), win->src.ctx.native->ctx);
+ if (win->internal.flags & RGFW_windowTransparent) {
+ i32 opacity = 0;
+ #define NSOpenGLCPSurfaceOpacity 236
+ NSOpenGLContext_setValues((id)win->src.ctx.native->ctx, &opacity, (NSOpenGLContextParameter)NSOpenGLCPSurfaceOpacity);
+
+ }
+
+ objc_msgSend_void(win->src.ctx.native->ctx, sel_registerName("makeCurrentContext"));
+
+ objc_msgSend_void_id((id)win->src.window, sel_registerName("setContentView:"), win->src.view);
+ objc_msgSend_void_bool(win->src.view, sel_registerName("setWantsLayer:"), true);
+ objc_msgSend_int((id)win->src.view, sel_registerName("setLayerContentsPlacement:"), 4);
+
+ RGFW_window_swapInterval_OpenGL(win, 0);
+
+ RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context initalized.");
+ return RGFW_TRUE;
+}
+
+void RGFW_window_deleteContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx) {
+ objc_msgSend_void(ctx->format, sel_registerName("release"));
+ win->src.ctx.native->format = NULL;
+
+ objc_msgSend_void(ctx->ctx, sel_registerName("release"));
+ win->src.ctx.native->ctx = NULL;
+ RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context freed.");
+}
+
+void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win) {
+ if (win) RGFW_ASSERT(win->src.ctx.native);
+ if (win != NULL)
+ objc_msgSend_void(win->src.ctx.native->ctx, sel_registerName("makeCurrentContext"));
+ else
+ objc_msgSend_id(objc_getClass("NSOpenGLContext"), sel_registerName("clearCurrentContext"));
+}
+void* RGFW_getCurrentContext_OpenGL(void) {
+ return objc_msgSend_id(objc_getClass("NSOpenGLContext"), sel_registerName("currentContext"));
+}
+
+void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) {
+ RGFW_ASSERT(win && win->src.ctx.native);
+ objc_msgSend_void(win->src.ctx.native->ctx, sel_registerName("flushBuffer"));
+}
+void RGFW_window_swapInterval_OpenGL(RGFW_window* win, i32 swapInterval) {
+ RGFW_ASSERT(win != NULL && win->src.ctx.native != NULL);
+ NSOpenGLContext_setValues((id)win->src.ctx.native->ctx, &swapInterval, (NSOpenGLContextParameter)222);
+}
+#endif
+
+void RGFW_deinitPlatform(void) {
+ objc_msgSend_void_id(_RGFW->NSApp, sel_registerName("setDelegate:"), NULL);
+
+ objc_msgSend_void_id(_RGFW->NSApp, sel_registerName("stop:"), NULL);
+ NSRelease(_RGFW->NSApp);
+ _RGFW->NSApp = NULL;
+
+ NSRelease(_RGFW->customNSAppDelegate);
+
+ _RGFW->customNSAppDelegate = NULL;
+
+ objc_disposeClassPair((Class)_RGFW->customViewClasses[0]);
+ objc_disposeClassPair((Class)_RGFW->customViewClasses[1]);
+ objc_disposeClassPair((Class)_RGFW->customWindowDelegateClass);
+ objc_disposeClassPair((Class)_RGFW->customNSAppDelegateClass);
+}
+
+void RGFW_window_closePlatform(RGFW_window* win) {
+ objc_msgSend_void_id((id)win->src.window, sel_registerName("setDelegate:"), NULL);
+ NSRelease((id)win->src.delegate);
+ NSRelease(win->src.view);
+
+ objc_msgSend_id(win->src.window, sel_registerName("close"));
+ NSRelease(win->src.window);
+}
+
+#ifdef RGFW_VULKAN
+VkResult RGFW_window_createSurface_Vulkan(RGFW_window* win, VkInstance instance, VkSurfaceKHR* surface) {
+ RGFW_ASSERT(win != NULL); RGFW_ASSERT(instance);
+ RGFW_ASSERT(surface != NULL);
+
+ *surface = VK_NULL_HANDLE;
+ id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+ pool = objc_msgSend_id(pool, sel_registerName("init"));
+
+ id nsView = (id)win->src.view;
+ if (!nsView) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errMetal, "NSView is NULL for macOS window");
+ return -1;
+ }
+
+
+ id layer = ((id (*)(id, SEL))objc_msgSend)(nsView, sel_registerName("layer"));
+
+ void* metalLayer = RGFW_getLayer_OSX();
+ if (metalLayer == NULL) {
+ return -1;
+ }
+ ((void (*)(id, SEL, id))objc_msgSend)((id)nsView, sel_registerName("setLayer:"), metalLayer);
+ ((void (*)(id, SEL, BOOL))objc_msgSend)(nsView, sel_registerName("setWantsLayer:"), YES);
+
+ VkResult result;
+/*
+ VkMetalSurfaceCreateInfoEXT macos;
+ macos.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
+ macos.slayer = metalLayer;
+ RGFW_MEMSET(&macos, 0, sizeof(macos));
+ result = vkCreateMacOSSurfaceMVK(instance, &macos, NULL, surface);
+*/
+
+ VkMacOSSurfaceCreateInfoMVK macos;
+ RGFW_MEMSET(&macos, 0, sizeof(macos));
+ macos.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
+ macos.pView = nsView;
+
+ result = vkCreateMacOSSurfaceMVK(instance, &macos, NULL, surface);
+
+ objc_msgSend_bool_void(pool, sel_registerName("drain"));
+
+ return result;
+}
+#endif
+
+#ifdef RGFW_WEBGPU
+WGPUSurface RGFW_window_createSurface_WebGPU(RGFW_window* window, WGPUInstance instance) {
+ WGPUSurfaceDescriptor surfaceDesc = {0};
+ id* nsView = (id*)window->src.view;
+ if (!nsView) {
+ RGFW_sendDebugInfo(RGFW_typeError, RGFW_errMetal, "NSView is NULL for macOS window");
+ return NULL;
+ }
+
+ ((void (*)(id, SEL, BOOL))objc_msgSend)(nsView, sel_registerName("setWantsLayer:"), YES);
+ id layer = ((id (*)(id, SEL))objc_msgSend)(nsView, sel_registerName("layer"));
+
+ void* metalLayer = RGFW_getLayer_OSX();
+ if (metalLayer == NULL) {
+ return NULL;
+ }
+ ((void (*)(id, SEL, id))objc_msgSend)((id)nsView, sel_registerName("setLayer:"), metalLayer);
+ layer = metalLayer;
+
+ WGPUSurfaceSourceMetalLayer fromMetal = {0};
+ fromMetal.chain.sType = WGPUSType_SurfaceSourceMetalLayer;
+#ifdef __OBJC__
+ fromMetal.layer = (__bridge CAMetalLayer*)layer; /* Use __bridge for ARC compatibility if mixing C/Obj-C */
+#else
+ fromMetal.layer = layer;
+#endif
+
+ surfaceDesc.nextInChain = (WGPUChainedStruct*)&fromMetal.chain;
+ return wgpuInstanceCreateSurface(instance, &surfaceDesc);
+}
+#endif
+
+#endif /* RGFW_MACOS */
+
+/*
+ End of MaOS defines
+*/
+
+/*
+ WASM defines
+*/
+
+#ifdef RGFW_WASM
+EM_BOOL Emscripten_on_resize(int eventType, const EmscriptenUiEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+ RGFW_windowResizedCallback(_RGFW->root, E->windowInnerWidth, E->windowInnerHeight);
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_fullscreenchange(int eventType, const EmscriptenFullscreenChangeEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+ if (!(_RGFW->root->internal.enabledEvents & RGFW_windowResizedFlag)) return EM_TRUE;
+
+ static u8 fullscreen = RGFW_FALSE;
+ static i32 originalW, originalH;
+
+ if (fullscreen == RGFW_FALSE) {
+ originalW = _RGFW->root->w;
+ originalH = _RGFW->root->h;
+ }
+
+ fullscreen = !fullscreen;
+ _RGFW->root->w = E->screenWidth;
+ _RGFW->root->h = E->screenHeight;
+
+ EM_ASM("Module.canvas.focus();");
+
+ if (fullscreen == RGFW_FALSE) {
+ _RGFW->root->w = originalW;
+ _RGFW->root->h = originalH;
+ } else {
+ #if __EMSCRIPTEN_major__ >= 1 && __EMSCRIPTEN_minor__ >= 29 && __EMSCRIPTEN_tiny__ >= 0
+ EmscriptenFullscreenStrategy FSStrat = {0};
+ FSStrat.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH;
+ FSStrat.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF;
+ FSStrat.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
+ emscripten_request_fullscreen_strategy("#canvas", 1, &FSStrat);
+ #else
+ emscripten_request_fullscreen("#canvas", 1);
+ #endif
+ }
+
+ emscripten_set_canvas_element_size("#canvas", _RGFW->root->w, _RGFW->root->h);
+ RGFW_windowResizedCallback(_RGFW->root, _RGFW->root->w, _RGFW->root->h);
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_focusin(int eventType, const EmscriptenFocusEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(E);
+
+ RGFW_focusCallback(_RGFW->root, 1);
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_focusout(int eventType, const EmscriptenFocusEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(E);
+
+ RGFW_focusCallback(_RGFW->root, 0);
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_mousemove(int eventType, const EmscriptenMouseEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+ RGFW_mousePosCallback(_RGFW->root, E->targetX, E->targetY, E->movementX, E->movementY);
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_mousedown(int eventType, const EmscriptenMouseEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+ int button = E->button;
+ if (button > 2)
+ button += 2;
+
+ RGFW_mouseButtonCallback(_RGFW->root, button, 1);
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_mouseup(int eventType, const EmscriptenMouseEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+ int button = E->button;
+ if (button > 2)
+ button += 2;
+
+ RGFW_mouseButtonCallback(_RGFW->root, button, 0);
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_wheel(int eventType, const EmscriptenWheelEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+ RGFW_mouseScrollCallback(_RGFW->root, E->deltaX, E->deltaY);
+
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_touchstart(int eventType, const EmscriptenTouchEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+ if (!(_RGFW->root->internal.enabledEvents & RGFW_mouseButtonPressedFlag)) return EM_TRUE;
+
+ size_t i;
+ for (i = 0; i < (size_t)E->numTouches; i++) {
+ RGFW_mousePosCallback(_RGFW->root, E->touches[i].targetX, E->touches[i].targetY, 0, 0);
+ RGFW_mouseButtonCallback(_RGFW->root, RGFW_mouseLeft, 1);
+ }
+
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_touchmove(int eventType, const EmscriptenTouchEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+ if (!(_RGFW->root->internal.enabledEvents & RGFW_mousePosChangedFlag)) return EM_TRUE;
+
+ size_t i;
+ for (i = 0; i < (size_t)E->numTouches; i++) {
+ RGFW_mousePosCallback(_RGFW->root, E->touches[i].targetX, E->touches[i].targetY, 0, 0);
+ }
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_touchend(int eventType, const EmscriptenTouchEvent* E, void* userData) {
+ RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+ if (!(_RGFW->root->internal.enabledEvents & RGFW_mouseButtonReleasedFlag)) return EM_TRUE;
+
+ size_t i;
+ for (i = 0; i < (size_t)E->numTouches; i++) {
+ RGFW_mousePosCallback(_RGFW->root, E->touches[i].targetX, E->touches[i].targetY, 0, 0);
+ RGFW_mouseButtonCallback(_RGFW->root, RGFW_mouseLeft, 0);
+ }
+ return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_touchcancel(int eventType, const EmscriptenTouchEvent* E, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); return EM_TRUE; }
+
+RGFW_key RGFW_WASMPhysicalToRGFW(u32 hash);
+
+void EMSCRIPTEN_KEEPALIVE RGFW_handleKeyEvent(char* code, u32 codepoint, RGFW_bool press) {
+ const char* iCode = code;
+
+ u32 hash = 0;
+ while(*iCode) hash = ((hash ^ 0x7E057D79U) << 3) ^ (unsigned int)*iCode++;
+
+ u32 physicalKey = RGFW_WASMPhysicalToRGFW(hash);
+
+ RGFW_keyCallback(_RGFW->root, physicalKey, _RGFW->root->internal.mod, RGFW_window_isKeyDown(_RGFW->root, (u8)physicalKey), press);
+ if (press) {
+; RGFW_keyCharCallback(_RGFW->root, codepoint);
+ }
+}
+
+void EMSCRIPTEN_KEEPALIVE RGFW_handleKeyMods(RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll) {
+ RGFW_updateKeyModsEx(_RGFW->root, capital, numlock, control, alt, shift, super, scroll);
+}
+
+void EMSCRIPTEN_KEEPALIVE Emscripten_onDrop(size_t count) {
+ RGFW_dataDropCallback(_RGFW->root, _RGFW->files, count);
+}
+
+void RGFW_stopCheckEvents(void) {
+ _RGFW->stopCheckEvents_bool = RGFW_TRUE;
+}
+
+RGFW_bool RGFW_createSurfacePtr(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) {
+ surface->data = data;
+ surface->w = w;
+ surface->h = h;
+ surface->format = format;
+ return RGFW_TRUE;
+}
+
+void RGFW_window_blitSurface(RGFW_window* win, RGFW_surface* surface) {
+ /* TODO: Needs fixing. */
+ RGFW_copyImageData(surface->data, surface->w, RGFW_MIN(win->h, surface->h), RGFW_formatRGBA8, surface->data, surface->format, surface->convertFunc);
+ EM_ASM_({
+ var data = Module.HEAPU8.slice($0, $0 + $1 * $2 * 4);
+ let context = document.getElementById("canvas").getContext("2d");
+ let image = context.getImageData(0, 0, $1, $2);
+ image.data.set(data);
+ context.putImageData(image, 0, $4 - $2);
+ }, surface->data, surface->w, surface->h, RGFW_MIN(win->h, surface->w), RGFW_MIN(win->h, surface->h));
+}
+
+void RGFW_surface_freePtr(RGFW_surface* surface) { }
+
+void EMSCRIPTEN_KEEPALIVE RGFW_makeSetValue(size_t index, char* file) {
+ /* This seems like a terrible idea, don't replicate this unless you hate yourself or the OS */
+ /* TODO: find a better way to do this
+ */
+ RGFW_STRNCPY((char*)_RGFW->files[index], file, RGFW_MAX_PATH - 1);
+ _RGFW->files[index][RGFW_MAX_PATH - 1] = '\0';
+}
+
+#include
+#include
+#include
+#include
+
+void EMSCRIPTEN_KEEPALIVE RGFW_mkdir(char* name) { mkdir(name, 0755); }
+
+void EMSCRIPTEN_KEEPALIVE RGFW_writeFile(const char *path, const char *data, size_t len) {
+ FILE* file = fopen(path, "w+");
+ if (file == NULL)
+ return;
+
+ fwrite(data, sizeof(char), len, file);
+ fclose(file);
+}
+
+void RGFW_initKeycodesPlatform(void) {
+ _RGFW->keycodes[DOM_VK_BACK_QUOTE] = RGFW_backtick;
+ _RGFW->keycodes[DOM_VK_0] = RGFW_0;
+ _RGFW->keycodes[DOM_VK_1] = RGFW_1;
+ _RGFW->keycodes[DOM_VK_2] = RGFW_2;
+ _RGFW->keycodes[DOM_VK_3] = RGFW_3;
+ _RGFW->keycodes[DOM_VK_4] = RGFW_4;
+ _RGFW->keycodes[DOM_VK_5] = RGFW_5;
+ _RGFW->keycodes[DOM_VK_6] = RGFW_6;
+ _RGFW->keycodes[DOM_VK_7] = RGFW_7;
+ _RGFW->keycodes[DOM_VK_8] = RGFW_8;
+ _RGFW->keycodes[DOM_VK_9] = RGFW_9;
+ _RGFW->keycodes[DOM_VK_SPACE] = RGFW_space;
+ _RGFW->keycodes[DOM_VK_A] = RGFW_a;
+ _RGFW->keycodes[DOM_VK_B] = RGFW_b;
+ _RGFW->keycodes[DOM_VK_C] = RGFW_c;
+ _RGFW->keycodes[DOM_VK_D] = RGFW_d;
+ _RGFW->keycodes[DOM_VK_E] = RGFW_e;
+ _RGFW->keycodes[DOM_VK_F] = RGFW_f;
+ _RGFW->keycodes[DOM_VK_G] = RGFW_g;
+ _RGFW->keycodes[DOM_VK_H] = RGFW_h;
+ _RGFW->keycodes[DOM_VK_I] = RGFW_i;
+ _RGFW->keycodes[DOM_VK_J] = RGFW_j;
+ _RGFW->keycodes[DOM_VK_K] = RGFW_k;
+ _RGFW->keycodes[DOM_VK_L] = RGFW_l;
+ _RGFW->keycodes[DOM_VK_M] = RGFW_m;
+ _RGFW->keycodes[DOM_VK_N] = RGFW_n;
+ _RGFW->keycodes[DOM_VK_O] = RGFW_o;
+ _RGFW->keycodes[DOM_VK_P] = RGFW_p;
+ _RGFW->keycodes[DOM_VK_Q] = RGFW_q;
+ _RGFW->keycodes[DOM_VK_R] = RGFW_r;
+ _RGFW->keycodes[DOM_VK_S] = RGFW_s;
+ _RGFW->keycodes[DOM_VK_T] = RGFW_t;
+ _RGFW->keycodes[DOM_VK_U] = RGFW_u;
+ _RGFW->keycodes[DOM_VK_V] = RGFW_v;
+ _RGFW->keycodes[DOM_VK_W] = RGFW_w;
+ _RGFW->keycodes[DOM_VK_X] = RGFW_x;
+ _RGFW->keycodes[DOM_VK_Y] = RGFW_y;
+ _RGFW->keycodes[DOM_VK_Z] = RGFW_z;
+ _RGFW->keycodes[DOM_VK_PERIOD] = RGFW_period;
+ _RGFW->keycodes[DOM_VK_COMMA] = RGFW_comma;
+ _RGFW->keycodes[DOM_VK_SLASH] = RGFW_slash;
+ _RGFW->keycodes[DOM_VK_OPEN_BRACKET] = RGFW_bracket;
+ _RGFW->keycodes[DOM_VK_CLOSE_BRACKET] = RGFW_closeBracket;
+ _RGFW->keycodes[DOM_VK_SEMICOLON] = RGFW_semicolon;
+ _RGFW->keycodes[DOM_VK_QUOTE] = RGFW_apostrophe;
+ _RGFW->keycodes[DOM_VK_BACK_SLASH] = RGFW_backSlash;
+ _RGFW->keycodes[DOM_VK_RETURN] = RGFW_return;
+ _RGFW->keycodes[DOM_VK_DELETE] = RGFW_delete;
+ _RGFW->keycodes[DOM_VK_NUM_LOCK] = RGFW_numLock;
+ _RGFW->keycodes[DOM_VK_DIVIDE] = RGFW_kpSlash;
+ _RGFW->keycodes[DOM_VK_MULTIPLY] = RGFW_kpMultiply;
+ _RGFW->keycodes[DOM_VK_SUBTRACT] = RGFW_kpMinus;
+ _RGFW->keycodes[DOM_VK_NUMPAD1] = RGFW_kp1;
+ _RGFW->keycodes[DOM_VK_NUMPAD2] = RGFW_kp2;
+ _RGFW->keycodes[DOM_VK_NUMPAD3] = RGFW_kp3;
+ _RGFW->keycodes[DOM_VK_NUMPAD4] = RGFW_kp4;
+ _RGFW->keycodes[DOM_VK_NUMPAD5] = RGFW_kp5;
+ _RGFW->keycodes[DOM_VK_NUMPAD6] = RGFW_kp6;
+ _RGFW->keycodes[DOM_VK_NUMPAD9] = RGFW_kp9;
+ _RGFW->keycodes[DOM_VK_NUMPAD0] = RGFW_kp0;
+ _RGFW->keycodes[DOM_VK_DECIMAL] = RGFW_kpPeriod;
+ _RGFW->keycodes[DOM_VK_RETURN] = RGFW_kpReturn;
+ _RGFW->keycodes[DOM_VK_HYPHEN_MINUS] = RGFW_minus;
+ _RGFW->keycodes[DOM_VK_EQUALS] = RGFW_equals;
+ _RGFW->keycodes[DOM_VK_BACK_SPACE] = RGFW_backSpace;
+ _RGFW->keycodes[DOM_VK_TAB] = RGFW_tab;
+ _RGFW->keycodes[DOM_VK_CAPS_LOCK] = RGFW_capsLock;
+ _RGFW->keycodes[DOM_VK_SHIFT] = RGFW_shiftL;
+ _RGFW->keycodes[DOM_VK_CONTROL] = RGFW_controlL;
+ _RGFW->keycodes[DOM_VK_ALT] = RGFW_altL;
+ _RGFW->keycodes[DOM_VK_META] = RGFW_superL;
+ _RGFW->keycodes[DOM_VK_F1] = RGFW_F1;
+ _RGFW->keycodes[DOM_VK_F2] = RGFW_F2;
+ _RGFW->keycodes[DOM_VK_F3] = RGFW_F3;
+ _RGFW->keycodes[DOM_VK_F4] = RGFW_F4;
+ _RGFW->keycodes[DOM_VK_F5] = RGFW_F5;
+ _RGFW->keycodes[DOM_VK_F6] = RGFW_F6;
+ _RGFW->keycodes[DOM_VK_F7] = RGFW_F7;
+ _RGFW->keycodes[DOM_VK_F8] = RGFW_F8;
+ _RGFW->keycodes[DOM_VK_F9] = RGFW_F9;
+ _RGFW->keycodes[DOM_VK_F10] = RGFW_F10;
+ _RGFW->keycodes[DOM_VK_F11] = RGFW_F11;
+ _RGFW->keycodes[DOM_VK_F12] = RGFW_F12;
+ _RGFW->keycodes[DOM_VK_UP] = RGFW_up;
+ _RGFW->keycodes[DOM_VK_DOWN] = RGFW_down;
+ _RGFW->keycodes[DOM_VK_LEFT] = RGFW_left;
+ _RGFW->keycodes[DOM_VK_RIGHT] = RGFW_right;
+ _RGFW->keycodes[DOM_VK_INSERT] = RGFW_insert;
+ _RGFW->keycodes[DOM_VK_END] = RGFW_end;
+ _RGFW->keycodes[DOM_VK_PAGE_UP] = RGFW_pageUp;
+ _RGFW->keycodes[DOM_VK_PAGE_DOWN] = RGFW_pageDown;
+ _RGFW->keycodes[DOM_VK_ESCAPE] = RGFW_escape;
+ _RGFW->keycodes[DOM_VK_HOME] = RGFW_home;
+ _RGFW->keycodes[DOM_VK_SCROLL_LOCK] = RGFW_scrollLock;
+ _RGFW->keycodes[DOM_VK_PRINTSCREEN] = RGFW_printScreen;
+ _RGFW->keycodes[DOM_VK_PAUSE] = RGFW_pause;
+ _RGFW->keycodes[DOM_VK_F13] = RGFW_F13;
+ _RGFW->keycodes[DOM_VK_F14] = RGFW_F14;
+ _RGFW->keycodes[DOM_VK_F15] = RGFW_F15;
+ _RGFW->keycodes[DOM_VK_F16] = RGFW_F16;
+ _RGFW->keycodes[DOM_VK_F17] = RGFW_F17;
+ _RGFW->keycodes[DOM_VK_F18] = RGFW_F18;
+ _RGFW->keycodes[DOM_VK_F19] = RGFW_F19;
+ _RGFW->keycodes[DOM_VK_F20] = RGFW_F20;
+ _RGFW->keycodes[DOM_VK_F21] = RGFW_F21;
+ _RGFW->keycodes[DOM_VK_F22] = RGFW_F22;
+ _RGFW->keycodes[DOM_VK_F23] = RGFW_F23;
+ _RGFW->keycodes[DOM_VK_F24] = RGFW_F24;
+}
+
+i32 RGFW_initPlatform(void) { return 0; }
+
+RGFW_window* RGFW_createWindowPlatform(const char* name, RGFW_windowFlags flags, RGFW_window* win) {
+ emscripten_set_canvas_element_size("#canvas", win->w, win->h);
+ emscripten_set_window_title(name);
+
+ /* load callbacks */
+ emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_resize);
+ emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, EM_FALSE, Emscripten_on_fullscreenchange);
+ emscripten_set_mousemove_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mousemove);
+ emscripten_set_touchstart_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchstart);
+ emscripten_set_touchend_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchend);
+ emscripten_set_touchmove_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchmove);
+ emscripten_set_touchcancel_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchcancel);
+ emscripten_set_mousedown_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mousedown);
+ emscripten_set_mouseup_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mouseup);
+ emscripten_set_wheel_callback("#canvas", NULL, EM_FALSE, Emscripten_on_wheel);
+ emscripten_set_focusin_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_focusin);
+ emscripten_set_focusout_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_focusout);
+
+ if (flags & RGFW_windowAllowDND) {
+ win->internal.flags |= RGFW_windowAllowDND;
+ }
+
+ EM_ASM({
+ window.addEventListener("keydown",
+ (event) => {
+ var code = stringToNewUTF8(event.code);
+ Module._RGFW_handleKeyMods(event.getModifierState("CapsLock"), event.getModifierState("NumLock"), event.getModifierState("Control"), event.getModifierState("Alt"), event.getModifierState("Shift"), event.getModifierState("Meta"), event.getModifierState("ScrollLock"));
+
+ var codepoint = event.key.charCodeAt(0);
+ if(codepoint < 0x7f && event.key.length > 1) {
+ codepoint = 0;
+ }
+
+ Module._RGFW_handleKeyEvent(code, codepoint, 1);
+ _free(code);
+ },
+ true);
+ window.addEventListener("keyup",
+ (event) => {
+ var code = stringToNewUTF8(event.code);
+ Module._RGFW_handleKeyMods(event.getModifierState("CapsLock"), event.getModifierState("NumLock"), event.getModifierState("Control"), event.getModifierState("Alt"), event.getModifierState("Shift"), event.getModifierState("Meta"), event.getModifierState("ScrollLock"));
+ Module._RGFW_handleKeyEvent(code, 0, 0);
+ _free(code);
+ },
+ true);
+ });
+
+ EM_ASM({
+ var canvas = document.getElementById('canvas');
+ canvas.addEventListener('drop', function(e) {
+ e.preventDefault();
+ if (e.dataTransfer.file < 0)
+ return;
+
+ var filenamesArray = [];
+ var count = e.dataTransfer.files.length;
+
+ /* Read and save the files to emscripten's files */
+ var drop_dir = '.rgfw_dropped_files';
+ Module._RGFW_mkdir(drop_dir);
+
+ for (var i = 0; i < count; i++) {
+ var file = e.dataTransfer.files[i];
+
+ var path = '/' + drop_dir + '/' + file.name.replace("//", '_');
+ var reader = new FileReader();
+
+ reader.onloadend = (e) => {
+ if (reader.readyState != 2) {
+ out('failed to read dropped file: '+file.name+': '+reader.error);
+ }
+ else {
+ var data = e.target.result;
+
+ Module._RGFW_writeFile(path, new Uint8Array(data), file.size);
+ }
+ };
+
+ reader.readAsArrayBuffer(file);
+ /* This works weird on modern OpenGL */
+ var filename = stringToNewUTF8(path);
+
+ filenamesArray.push(filename);
+
+ Module._RGFW_makeSetValue(i, filename);
+ }
+
+ Module._Emscripten_onDrop(count);
+
+ for (var i = 0; i < count; i++) {
+ _free(filenamesArray[i]);
+ }
+ }, true);
+
+ canvas.addEventListener('dragover', function(e) { e.preventDefault(); return false; }, true);
+ });
+
+ return win;
+}
+
+RGFW_key RGFW_physicalToMappedKey(RGFW_key key) {
+ return key;
+}
+
+void RGFW_pollEvents(void) {
+ RGFW_resetPrevState();
+ emscripten_sleep(0);
+}
+
+void RGFW_window_resize(RGFW_window* win, i32 w, i32 h) {
+ RGFW_UNUSED(win);
+ emscripten_set_canvas_element_size("#canvas", w, h);
+}
+
+/* NOTE: I don't know if this is possible */
+void RGFW_window_moveMouse(RGFW_window* win, i32 x, i32 y) { RGFW_UNUSED(win); RGFW_UNUSED(x); RGFW_UNUSED(y); }
+/* this one might be possible but it looks iffy */
+RGFW_mouse* RGFW_loadMouse(u8* data, i32 w, i32 h, RGFW_format format) { RGFW_UNUSED(data); RGFW_UNUSED(w); RGFW_UNUSED(h); RGFW_UNUSED(format); return NULL; }
+
+void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) { RGFW_UNUSED(win); RGFW_UNUSED(mouse); }
+void RGFW_freeMouse(RGFW_mouse* mouse) { RGFW_UNUSED(mouse); }
+
+RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) {
+ RGFW_UNUSED(win);
+ char* cursorName = NULL;
+
+ switch (mouse) {
+ case RGFW_mouseNormal: cursorName = (char*)"default"; break;
+ case RGFW_mouseArrow: cursorName = (char*)"default"; break;
+ case RGFW_mouseIbeam: cursorName = (char*)"text"; break;
+ case RGFW_mouseCrosshair: cursorName = (char*)"crosshair"; break;
+ case RGFW_mousePointingHand: cursorName = (char*)"pointer"; break;
+ case RGFW_mouseResizeEW: cursorName = (char*)"ew-resize"; break;
+ case RGFW_mouseResizeNS: cursorName = (char*)"ns-resize"; break;
+ case RGFW_mouseResizeNWSE: cursorName = (char*)"nwse-resize"; break;
+ case RGFW_mouseResizeNESW: cursorName = (char*)"nesw-resize"; break;
+ case RGFW_mouseResizeNW: cursorName = (char*)"nw-resize"; break;
+ case RGFW_mouseResizeN: cursorName = (char*)"n-resize"; break;
+ case RGFW_mouseResizeNE: cursorName = (char*)"ne-resize"; break;
+ case RGFW_mouseResizeE: cursorName = (char*)"e-resize"; break;
+ case RGFW_mouseResizeSE: cursorName = (char*)"se-resize"; break;
+ case RGFW_mouseResizeS: cursorName = (char*)"s-resize"; break;
+ case RGFW_mouseResizeSW: cursorName = (char*)"sw-resize"; break;
+ case RGFW_mouseResizeW: cursorName = (char*)"w-resize"; break;
+ case RGFW_mouseResizeAll: cursorName = (char*)"move"; break;
+ case RGFW_mouseNotAllowed: cursorName = (char*)"not-allowed"; break;
+ case RGFW_mouseWait: cursorName = (char*)"wait"; break;
+ case RGFW_mouseProgress: cursorName = (char*)"progress"; break;
+ default: return RGFW_FALSE;
+ }
+
+ EM_ASM( { document.getElementById("canvas").style.cursor = UTF8ToString($0); }, cursorName);
+ return RGFW_TRUE;
+}
+
+RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) {
+ return RGFW_window_setMouseStandard(win, RGFW_mouseNormal);
+}
+
+void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) {
+ RGFW_window_showMouseFlags(win, show);
+ if (show)
+ RGFW_window_setMouseDefault(win);
+ else
+ EM_ASM(document.getElementById('canvas').style.cursor = 'none';);
+}
+
+RGFW_bool RGFW_getGlobalMouse(i32* x, i32* y) {
+ if(x) *x = EM_ASM_INT({
+ return window.mouseX || 0;
+ });
+ if (y) *y = EM_ASM_INT({
+ return window.mouseY || 0;
+ });
+ return RGFW_TRUE;
+}
+
+void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) {
+ RGFW_UNUSED(win);
+
+ EM_ASM_({
+ var canvas = document.getElementById('canvas');
+ if ($0) {
+ canvas.style.pointerEvents = 'none';
+ } else {
+ canvas.style.pointerEvents = 'auto';
+ }
+ }, passthrough);
+}
+
+void RGFW_writeClipboard(const char* text, u32 textLen) {
+ RGFW_UNUSED(textLen);
+ EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, text);
+}
+
+
+RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) {
+ RGFW_UNUSED(str); RGFW_UNUSED(strCapacity);
+ /*
+ placeholder code for later
+ I'm not sure if this is possible do the the async stuff
+ */
+ return 0;
+}
+
+#ifdef RGFW_OPENGL
+RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) {
+ win->src.ctx.native = ctx;
+ win->src.gfxType = RGFW_gfxNativeOpenGL;
+
+ EmscriptenWebGLContextAttributes attrs;
+ attrs.alpha = hints->alpha;
+ attrs.depth = hints->depth;
+ attrs.stencil = hints->stencil;
+ attrs.antialias = hints->samples;
+ attrs.premultipliedAlpha = EM_TRUE;
+ attrs.preserveDrawingBuffer = EM_FALSE;
+
+ if (hints->doubleBuffer == 0)
+ attrs.renderViaOffscreenBackBuffer = 0;
+ else
+ attrs.renderViaOffscreenBackBuffer = hints->auxBuffers;
+
+ attrs.failIfMajorPerformanceCaveat = EM_FALSE;
+ attrs.majorVersion = (hints->major == 0) ? 1 : hints->major;
+ attrs.minorVersion = hints->minor;
+
+ attrs.enableExtensionsByDefault = EM_TRUE;
+ attrs.explicitSwapControl = EM_TRUE;
+
+ emscripten_webgl_init_context_attributes(&attrs);
+ win->src.ctx.native->ctx = emscripten_webgl_create_context("#canvas", &attrs);
+ emscripten_webgl_make_context_current(win->src.ctx.native->ctx);
+
+ #ifdef LEGACY_GL_EMULATION
+ EM_ASM("Module.useWebGL = true; GLImmediate.init();");
+ RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context initalized.");
+ #endif
+
+ RGFW_window_swapInterval_OpenGL(win, 0);
+
+ return RGFW_TRUE;
+}
+
+void RGFW_window_deleteContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx) {
+ emscripten_webgl_destroy_context(ctx->ctx);
+ win->src.ctx.native->ctx = 0;
+ RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context freed.");
+}
+
+void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win) {
+ if (win) RGFW_ASSERT(win->src.ctx.native);
+ if (win == NULL)
+ emscripten_webgl_make_context_current(0);
+ else
+ emscripten_webgl_make_context_current(win->src.ctx.native->ctx);
+}
+
+void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) {
+ RGFW_ASSERT(win && win->src.ctx.native);
+ emscripten_webgl_commit_frame();
+}
+void* RGFW_getCurrentContext_OpenGL(void) { return (void*)emscripten_webgl_get_current_context(); }
+
+RGFW_bool RGFW_extensionSupportedPlatform_OpenGL(const char* extension, size_t len) {
+ return EM_ASM_INT({
+ var ext = UTF8ToString($0, $1);
+ var canvas = document.querySelector('canvas');
+ var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
+ if (!gl) return 0;
+
+ var supported = gl.getSupportedExtensions();
+ return supported && supported.includes(ext) ? 1 : 0;
+ }, extension, len);
+ return RGFW_FALSE;
+}
+
+RGFW_proc RGFW_getProcAddress_OpenGL(const char* procname) {
+ return (RGFW_proc)emscripten_webgl_get_proc_address(procname);
+ return NULL;
+}
+
+#endif
+
+void RGFW_window_swapInterval_OpenGL(RGFW_window* win, i32 swapInterval) { RGFW_UNUSED(win); RGFW_UNUSED(swapInterval); }
+
+void RGFW_deinitPlatform(void) { }
+
+void RGFW_window_closePlatform(RGFW_window* win) { }
+
+int RGFW_innerWidth(void) { return EM_ASM_INT({ return window.innerWidth; }); }
+int RGFW_innerHeight(void) { return EM_ASM_INT({ return window.innerHeight; }); }
+
+void RGFW_window_setRawMouseModePlatform(RGFW_window* win, RGFW_bool state) {
+ RGFW_UNUSED(win); RGFW_UNUSED(state);
+}
+
+void RGFW_window_captureMousePlatform(RGFW_window* win, RGFW_bool state) {
+ RGFW_UNUSED(win);
+ if (state) {
+ emscripten_request_pointerlock("#canvas", 1);
+ } else {
+ emscripten_exit_pointerlock();
+ }
+}
+
+void RGFW_window_setName(RGFW_window* win, const char* name) {
+ RGFW_UNUSED(win);
+ if (name == NULL) name = "\0";
+
+ emscripten_set_window_title(name);
+}
+
+void RGFW_window_maximize(RGFW_window* win) {
+ RGFW_ASSERT(win != NULL);
+
+ RGFW_monitor* mon = RGFW_window_getMonitor(win);
+ if (mon != NULL) {
+ RGFW_window_resize(win, mon->mode.w, mon->mode.h);
+ }
+
+ RGFW_window_move(win, 0, 0);
+}
+
+void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) {
+ RGFW_ASSERT(win != NULL);
+ if (fullscreen) {
+ win->internal.flags |= RGFW_windowFullscreen;
+ EM_ASM( Module.requestFullscreen(false, true); );
+ return;
+ }
+ win->internal.flags &= ~(u32)RGFW_windowFullscreen;
+ EM_ASM( Module.exitFullscreen(false, true); );
+}
+
+void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) {
+ RGFW_UNUSED(win);
+ EM_ASM({
+ var element = document.getElementById("canvas");
+ if (element)
+ element.style.opacity = $1;
+ }, "elementId", opacity);
+}
+
+#ifdef RGFW_WEBGPU
+WGPUSurface RGFW_window_createSurface_WebGPU(RGFW_window* window, WGPUInstance instance) {
+ WGPUSurfaceDescriptor surfaceDesc = {0};
+ WGPUEmscriptenSurfaceSourceCanvasHTMLSelector canvasDesc = {0};
+ canvasDesc.chain.sType = WGPUSType_EmscriptenSurfaceSourceCanvasHTMLSelector;
+ canvasDesc.selector = (WGPUStringView){.data = "#canvas", .length = 7};
+
+ surfaceDesc.nextInChain = &canvasDesc.chain;
+ return wgpuInstanceCreateSurface(instance, &surfaceDesc);
+}
+#endif
+
+RGFW_key RGFW_WASMPhysicalToRGFW(u32 hash) {
+ switch(hash) { /* 0x0000 */
+ case 0x67243A2DU /* Escape */: return RGFW_escape; /* 0x0001 */
+ case 0x67251058U /* Digit0 */: return RGFW_0; /* 0x0002 */
+ case 0x67251059U /* Digit1 */: return RGFW_1; /* 0x0003 */
+ case 0x6725105AU /* Digit2 */: return RGFW_2; /* 0x0004 */
+ case 0x6725105BU /* Digit3 */: return RGFW_3; /* 0x0005 */
+ case 0x6725105CU /* Digit4 */: return RGFW_4; /* 0x0006 */
+ case 0x6725105DU /* Digit5 */: return RGFW_5; /* 0x0007 */
+ case 0x6725105EU /* Digit6 */: return RGFW_6; /* 0x0008 */
+ case 0x6725105FU /* Digit7 */: return RGFW_7; /* 0x0009 */
+ case 0x67251050U /* Digit8 */: return RGFW_8; /* 0x000A */
+ case 0x67251051U /* Digit9 */: return RGFW_9; /* 0x000B */
+ case 0x92E14DD3U /* Minus */: return RGFW_minus; /* 0x000C */
+ case 0x92E1FBACU /* Equal */: return RGFW_equals; /* 0x000D */
+ case 0x36BF1CB5U /* Backspace */: return RGFW_backSpace; /* 0x000E */
+ case 0x7B8E51E2U /* Tab */: return RGFW_tab; /* 0x000F */
+ case 0x2C595B51U /* KeyQ */: return RGFW_q; /* 0x0010 */
+ case 0x2C595B57U /* KeyW */: return RGFW_w; /* 0x0011 */
+ case 0x2C595B45U /* KeyE */: return RGFW_e; /* 0x0012 */
+ case 0x2C595B52U /* KeyR */: return RGFW_r; /* 0x0013 */
+ case 0x2C595B54U /* KeyT */: return RGFW_t; /* 0x0014 */
+ case 0x2C595B59U /* KeyY */: return RGFW_y; /* 0x0015 */
+ case 0x2C595B55U /* KeyU */: return RGFW_u; /* 0x0016 */
+ case 0x2C595B4FU /* KeyO */: return RGFW_o; /* 0x0018 */
+ case 0x2C595B50U /* KeyP */: return RGFW_p; /* 0x0019 */
+ case 0x45D8158CU /* BracketLeft */: return RGFW_closeBracket; /* 0x001A */
+ case 0xDEEABF7CU /* BracketRight */: return RGFW_bracket; /* 0x001B */
+ case 0x92E1C5D2U /* Enter */: return RGFW_return; /* 0x001C */
+ case 0xE058958CU /* ControlLeft */: return RGFW_controlL; /* 0x001D */
+ case 0x2C595B41U /* KeyA */: return RGFW_a; /* 0x001E */
+ case 0x2C595B53U /* KeyS */: return RGFW_s; /* 0x001F */
+ case 0x2C595B44U /* KeyD */: return RGFW_d; /* 0x0020 */
+ case 0x2C595B46U /* KeyF */: return RGFW_f; /* 0x0021 */
+ case 0x2C595B47U /* KeyG */: return RGFW_g; /* 0x0022 */
+ case 0x2C595B48U /* KeyH */: return RGFW_h; /* 0x0023 */
+ case 0x2C595B4AU /* KeyJ */: return RGFW_j; /* 0x0024 */
+ case 0x2C595B4BU /* KeyK */: return RGFW_k; /* 0x0025 */
+ case 0x2C595B4CU /* KeyL */: return RGFW_l; /* 0x0026 */
+ case 0x2707219EU /* Semicolon */: return RGFW_semicolon; /* 0x0027 */
+ case 0x92E0B58DU /* Quote */: return RGFW_apostrophe; /* 0x0028 */
+ case 0x36BF358DU /* Backquote */: return RGFW_backtick; /* 0x0029 */
+ case 0x26B1958CU /* ShiftLeft */: return RGFW_shiftL; /* 0x002A */
+ case 0x36BF2438U /* Backslash */: return RGFW_backSlash; /* 0x002B */
+ case 0x2C595B5AU /* KeyZ */: return RGFW_z; /* 0x002C */
+ case 0x2C595B58U /* KeyX */: return RGFW_x; /* 0x002D */
+ case 0x2C595B43U /* KeyC */: return RGFW_c; /* 0x002E */
+ case 0x2C595B56U /* KeyV */: return RGFW_v; /* 0x002F */
+ case 0x2C595B42U /* KeyB */: return RGFW_b; /* 0x0030 */
+ case 0x2C595B4EU /* KeyN */: return RGFW_n; /* 0x0031 */
+ case 0x2C595B4DU /* KeyM */: return RGFW_m; /* 0x0032 */
+ case 0x92E1A1C1U /* Comma */: return RGFW_comma; /* 0x0033 */
+ case 0x672FFAD4U /* Period */: return RGFW_period; /* 0x0034 */
+ case 0x92E0A438U /* Slash */: return RGFW_slash; /* 0x0035 */
+ case 0xC5A6BF7CU /* ShiftRight */: return RGFW_shiftR;
+ case 0x5D64DA91U /* NumpadMultiply */: return RGFW_kpMultiply;
+ case 0xC914958CU /* AltLeft */: return RGFW_altL; /* 0x0038 */
+ case 0x92E09CB5U /* Space */: return RGFW_space; /* 0x0039 */
+ case 0xB8FAE73BU /* CapsLock */: return RGFW_capsLock; /* 0x003A */
+ case 0x7174B789U /* F1 */: return RGFW_F1; /* 0x003B */
+ case 0x7174B78AU /* F2 */: return RGFW_F2; /* 0x003C */
+ case 0x7174B78BU /* F3 */: return RGFW_F3; /* 0x003D */
+ case 0x7174B78CU /* F4 */: return RGFW_F4; /* 0x003E */
+ case 0x7174B78DU /* F5 */: return RGFW_F5; /* 0x003F */
+ case 0x7174B78EU /* F6 */: return RGFW_F6; /* 0x0040 */
+ case 0x7174B78FU /* F7 */: return RGFW_F7; /* 0x0041 */
+ case 0x7174B780U /* F8 */: return RGFW_F8; /* 0x0042 */
+ case 0x7174B781U /* F9 */: return RGFW_F9; /* 0x0043 */
+ case 0x7B8E57B0U /* F10 */: return RGFW_F10; /* 0x0044 */
+ case 0xC925FCDFU /* Numpad7 */: return RGFW_kpMultiply; /* 0x0047 */
+ case 0xC925FCD0U /* Numpad8 */: return RGFW_kp8; /* 0x0048 */
+ case 0xC925FCD1U /* Numpad9 */: return RGFW_kp9; /* 0x0049 */
+ case 0x5EA3E8A4U /* NumpadSubtract */: return RGFW_minus; /* 0x004A */
+ case 0xC925FCDCU /* Numpad4 */: return RGFW_kp4; /* 0x004B */
+ case 0xC925FCDDU /* Numpad5 */: return RGFW_kp5; /* 0x004C */
+ case 0xC925FCDEU /* Numpad6 */: return RGFW_kp6; /* 0x004D */
+ case 0xC925FCD9U /* Numpad1 */: return RGFW_kp1; /* 0x004F */
+ case 0xC925FCDAU /* Numpad2 */: return RGFW_kp2; /* 0x0050 */
+ case 0xC925FCDBU /* Numpad3 */: return RGFW_kp3; /* 0x0051 */
+ case 0xC925FCD8U /* Numpad0 */: return RGFW_kp0; /* 0x0052 */
+ case 0x95852DACU /* NumpadDecimal */: return RGFW_period; /* 0x0053 */
+ case 0x7B8E57B1U /* F11 */: return RGFW_F11; /* 0x0057 */
+ case 0x7B8E57B2U /* F12 */: return RGFW_F12; /* 0x0058 */
+ case 0x7B8E57B3U /* F13 */: return DOM_PK_F13; /* 0x0064 */
+ case 0x7B8E57B4U /* F14 */: return DOM_PK_F14; /* 0x0065 */
+ case 0x7B8E57B5U /* F15 */: return DOM_PK_F15; /* 0x0066 */
+ case 0x7B8E57B6U /* F16 */: return DOM_PK_F16; /* 0x0067 */
+ case 0x7B8E57B7U /* F17 */: return DOM_PK_F17; /* 0x0068 */
+ case 0x7B8E57B8U /* F18 */: return DOM_PK_F18; /* 0x0069 */
+ case 0x7B8E57B9U /* F19 */: return DOM_PK_F19; /* 0x006A */
+ case 0x7B8E57A8U /* F20 */: return DOM_PK_F20; /* 0x006B */
+ case 0x7B8E57A9U /* F21 */: return DOM_PK_F21; /* 0x006C */
+ case 0x7B8E57AAU /* F22 */: return DOM_PK_F22; /* 0x006D */
+ case 0x7B8E57ABU /* F23 */: return DOM_PK_F23; /* 0x006E */
+ case 0x7393FBACU /* NumpadEqual */: return RGFW_kpReturn;
+ case 0xB88EBF7CU /* AltRight */: return RGFW_altR; /* 0xE038 */
+ case 0xC925873BU /* NumLock */: return RGFW_numLock; /* 0xE045 */
+ case 0x2C595F45U /* Home */: return RGFW_home; /* 0xE047 */
+ case 0xC91BB690U /* ArrowUp */: return RGFW_up; /* 0xE048 */
+ case 0x672F9210U /* PageUp */: return RGFW_pageUp; /* 0xE049 */
+ case 0x3799258CU /* ArrowLeft */: return RGFW_left; /* 0xE04B */
+ case 0x4CE33F7CU /* ArrowRight */: return RGFW_right; /* 0xE04D */
+ case 0x7B8E55DCU /* End */: return RGFW_end; /* 0xE04F */
+ case 0x3799379EU /* ArrowDown */: return RGFW_down; /* 0xE050 */
+ case 0xBA90179EU /* PageDown */: return RGFW_pageDown; /* 0xE051 */
+ case 0x6723CB2CU /* Insert */: return RGFW_insert; /* 0xE052 */
+ case 0x6725C50DU /* Delete */: return RGFW_delete; /* 0xE053 */
+ case 0x6723658CU /* OSLeft */: return RGFW_superL; /* 0xE05B */
+ case 0x39643F7CU /* MetaRight */: return RGFW_superR; /* 0xE05C */
+ case 0x380B9C8CU /* NumpadAdd */: return DOM_PK_NUMPAD_ADD; /* 0x004E */
+ default: return DOM_PK_UNKNOWN;
+ }
+
+ return 0;
+}
+
+/* unsupported functions */
+void RGFW_window_focus(RGFW_window* win) { RGFW_UNUSED(win); }
+void RGFW_window_raise(RGFW_window* win) { RGFW_UNUSED(win); }
+RGFW_bool RGFW_monitor_requestMode(RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request) { RGFW_UNUSED(mon); RGFW_UNUSED(mode); RGFW_UNUSED(request); return RGFW_FALSE; }
+RGFW_bool RGFW_monitor_getWorkarea(RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height) { RGFW_UNUSED(monitor); RGFW_UNUSED(x); RGFW_UNUSED(width); RGFW_UNUSED(height); return RGFW_FALSE; }
+size_t RGFW_monitor_getGammaRampPtr(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) { RGFW_UNUSED(monitor); RGFW_UNUSED(ramp); return 0; }
+RGFW_bool RGFW_monitor_setGammaRamp(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) { RGFW_UNUSED(monitor); RGFW_UNUSED(ramp); return RGFW_FALSE; }
+size_t RGFW_monitor_getModesPtr(RGFW_monitor* mon, RGFW_monitorMode** modes) { RGFW_UNUSED(mon); RGFW_UNUSED(modes); return 0; }
+RGFW_bool RGFW_monitor_setMode(RGFW_monitor* mon, RGFW_monitorMode* mode) { RGFW_UNUSED(mon); RGFW_UNUSED(mode); return RGFW_FALSE; }
+void RGFW_pollMonitors(void) { }
+void RGFW_window_move(RGFW_window* win, i32 x, i32 y) { RGFW_UNUSED(win); RGFW_UNUSED(x); RGFW_UNUSED(y); }
+void RGFW_window_setAspectRatio(RGFW_window* win, i32 w, i32 h) { RGFW_UNUSED(win); RGFW_UNUSED(w); RGFW_UNUSED(h); }
+void RGFW_window_setMinSize(RGFW_window* win, i32 w, i32 h) { RGFW_UNUSED(win); RGFW_UNUSED(w); RGFW_UNUSED(h); }
+void RGFW_window_setMaxSize(RGFW_window* win, i32 w, i32 h) { RGFW_UNUSED(win); RGFW_UNUSED(w); RGFW_UNUSED(h); }
+void RGFW_window_minimize(RGFW_window* win) { RGFW_UNUSED(win); }
+void RGFW_window_restore(RGFW_window* win) { RGFW_UNUSED(win); }
+void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) { RGFW_UNUSED(win); RGFW_UNUSED(floating); }
+void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { RGFW_UNUSED(win); RGFW_UNUSED(border); }
+RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_icon type) { RGFW_UNUSED(win); RGFW_UNUSED(data); RGFW_UNUSED(w); RGFW_UNUSED(h); RGFW_UNUSED(format); RGFW_UNUSED(type); return RGFW_FALSE; }
+void RGFW_window_hide(RGFW_window* win) { RGFW_UNUSED(win); }
+void RGFW_window_show(RGFW_window* win) {RGFW_UNUSED(win); }
+void RGFW_window_flash(RGFW_window* win, RGFW_flashRequest request) { RGFW_UNUSED(win); RGFW_UNUSED(request); }
+RGFW_bool RGFW_window_isHidden(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; }
+RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; }
+RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; }
+RGFW_bool RGFW_window_isFloating(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; }
+RGFW_monitor* RGFW_window_getMonitor(RGFW_window* win) { RGFW_UNUSED(win); return NULL; }
+void RGFW_waitForEvent(i32 waitMS) { RGFW_UNUSED(waitMS); }
+#endif
+
+/* end of web asm defines */
+
+/*
+ * RGFW function pointer backend, made to allow you to compile for Wayland but fallback to X11
+*/
+#ifdef RGFW_DYNAMIC
+typedef RGFW_window* (*RGFW_createWindowPlatform_ptr)(const char* name, RGFW_windowFlags flags, RGFW_window* win);
+typedef RGFW_bool (*RGFW_getMouse_ptr)(i32* x, i32* y);
+typedef RGFW_key (*RGFW_physicalToMappedKey_ptr)(RGFW_key key);
+typedef void (*RGFW_pollEvents_ptr)(void);
+typedef void (*RGFW_pollMonitors_ptr)(void);
+typedef void (*RGFW_window_move_ptr)(RGFW_window* win, i32 x, i32 y);
+typedef void (*RGFW_window_resize_ptr)(RGFW_window* win, i32 w, i32 h);
+typedef void (*RGFW_window_setAspectRatio_ptr)(RGFW_window* win, i32 w, i32 h);
+typedef void (*RGFW_window_setMinSize_ptr)(RGFW_window* win, i32 w, i32 h);
+typedef void (*RGFW_window_setMaxSize_ptr)(RGFW_window* win, i32 w, i32 h);
+typedef void (*RGFW_window_maximize_ptr)(RGFW_window* win);
+typedef void (*RGFW_window_focus_ptr)(RGFW_window* win);
+typedef void (*RGFW_window_raise_ptr)(RGFW_window* win);
+typedef void (*RGFW_window_setFullscreen_ptr)(RGFW_window* win, RGFW_bool fullscreen);
+typedef void (*RGFW_window_setFloating_ptr)(RGFW_window* win, RGFW_bool floating);
+typedef void (*RGFW_window_setOpacity_ptr)(RGFW_window* win, u8 opacity);
+typedef void (*RGFW_window_minimize_ptr)(RGFW_window* win);
+typedef void (*RGFW_window_restore_ptr)(RGFW_window* win);
+typedef RGFW_bool (*RGFW_window_isFloating_ptr)(RGFW_window* win);
+typedef void (*RGFW_window_setName_ptr)(RGFW_window* win, const char* name);
+typedef void (*RGFW_window_setMousePassthrough_ptr)(RGFW_window* win, RGFW_bool passthrough);
+typedef RGFW_bool (*RGFW_window_setIconEx_ptr)(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, u8 type);
+typedef RGFW_mouse* (*RGFW_loadMouse_ptr)(u8* data, i32 w, i32 h, RGFW_format format);
+typedef void (*RGFW_window_setMouse_ptr)(RGFW_window* win, RGFW_mouse* mouse);
+typedef void (*RGFW_window_moveMouse_ptr)(RGFW_window* win, i32 x, i32 y);
+typedef RGFW_bool (*RGFW_window_setMouseDefault_ptr)(RGFW_window* win);
+typedef RGFW_bool (*RGFW_window_setMouseStandard_ptr)(RGFW_window* win, u8 mouse);
+typedef void (*RGFW_window_hide_ptr)(RGFW_window* win);
+typedef void (*RGFW_window_show_ptr)(RGFW_window* win);
+typedef void (*RGFW_window_flash_ptr)(RGFW_window* win, RGFW_flashRequest request);
+typedef RGFW_ssize_t (*RGFW_readClipboardPtr_ptr)(char* str, size_t strCapacity);
+typedef void (*RGFW_writeClipboard_ptr)(const char* text, u32 textLen);
+typedef RGFW_bool (*RGFW_window_isHidden_ptr)(RGFW_window* win);
+typedef RGFW_bool (*RGFW_window_isMinimized_ptr)(RGFW_window* win);
+typedef RGFW_bool (*RGFW_window_isMaximized_ptr)(RGFW_window* win);
+typedef RGFW_bool (*RGFW_monitor_requestMode_ptr)(RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request);
+typedef RGFW_bool (*RGFW_monitor_getWorkarea_ptr)(RGFW_monitor* mon, i32* x, i32* y, i32* w, i32* h);
+typedef size_t (*RGFW_monitor_getModesPtr_ptr)(RGFW_monitor* mon, RGFW_monitorMode** modes);
+typedef size_t (*RGFW_monitor_getGammaRampPtr_ptr) (RGFW_monitor* monitor, RGFW_gammaRamp* ramp);
+typedef RGFW_bool (*RGFW_monitor_setGammaRamp_ptr) (RGFW_monitor* monitor, RGFW_gammaRamp* ramp);
+typedef RGFW_bool (*RGFW_monitor_setMode_ptr)(RGFW_monitor* mon, RGFW_monitorMode* mode);
+typedef RGFW_monitor* (*RGFW_window_getMonitor_ptr)(RGFW_window* win);
+typedef void (*RGFW_window_closePlatform_ptr)(RGFW_window* win);
+typedef RGFW_format (*RGFW_nativeFormat_ptr)(void);
+typedef RGFW_bool (*RGFW_createSurfacePtr_ptr)(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface);
+typedef void (*RGFW_window_blitSurface_ptr)(RGFW_window* win, RGFW_surface* surface);
+typedef void (*RGFW_surface_freePtr_ptr)(RGFW_surface* surface);
+typedef void (*RGFW_freeMouse_ptr)(RGFW_mouse* mouse);
+typedef void (*RGFW_window_setBorder_ptr)(RGFW_window* win, RGFW_bool border);
+typedef void (*RGFW_window_captureMousePlatform_ptr)(RGFW_window* win, RGFW_bool state);
+typedef void (*RGFW_window_setRawMouseModePlatform_ptr)(RGFW_window* win, RGFW_bool state);
+#ifdef RGFW_OPENGL
+typedef void (*RGFW_window_makeCurrentContext_OpenGL_ptr)(RGFW_window* win);
+typedef void* (*RGFW_getCurrentContext_OpenGL_ptr)(void);
+typedef void (*RGFW_window_swapBuffers_OpenGL_ptr)(RGFW_window* win);
+typedef void (*RGFW_window_swapInterval_OpenGL_ptr)(RGFW_window* win, i32 swapInterval);
+typedef RGFW_bool (*RGFW_extensionSupportedPlatform_OpenGL_ptr)(const char* extension, size_t len);
+typedef RGFW_proc (*RGFW_getProcAddress_OpenGL_ptr)(const char* procname);
+typedef RGFW_bool (*RGFW_window_createContextPtr_OpenGL_ptr)(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints);
+typedef void (*RGFW_window_deleteContextPtr_OpenGL_ptr)(RGFW_window* win, RGFW_glContext* ctx);
+#endif
+#ifdef RGFW_WEBGPU
+typedef WGPUSurface (*RGFW_window_createSurface_WebGPU_ptr)(RGFW_window* window, WGPUInstance instance);
+#endif
+
+/* Structure to hold all function pointers */
+typedef struct RGFW_FunctionPointers {
+ RGFW_nativeFormat_ptr nativeFormat;
+ RGFW_createSurfacePtr_ptr createSurfacePtr;
+ RGFW_window_blitSurface_ptr window_blitSurface;
+ RGFW_surface_freePtr_ptr surface_freePtr;
+ RGFW_freeMouse_ptr freeMouse;
+ RGFW_window_setBorder_ptr window_setBorder;
+ RGFW_window_captureMousePlatform_ptr window_captureMousePlatform;
+ RGFW_window_setRawMouseModePlatform_ptr window_setRawMouseModePlatform;
+ RGFW_createWindowPlatform_ptr createWindowPlatform;
+ RGFW_getMouse_ptr getGlobalMouse;
+ RGFW_physicalToMappedKey_ptr physicalToMappedKey;
+ RGFW_pollEvents_ptr pollEvents;
+ RGFW_pollMonitors_ptr pollMonitors;
+ RGFW_window_move_ptr window_move;
+ RGFW_window_resize_ptr window_resize;
+ RGFW_window_setAspectRatio_ptr window_setAspectRatio;
+ RGFW_window_setMinSize_ptr window_setMinSize;
+ RGFW_window_setMaxSize_ptr window_setMaxSize;
+ RGFW_window_maximize_ptr window_maximize;
+ RGFW_window_focus_ptr window_focus;
+ RGFW_window_raise_ptr window_raise;
+ RGFW_window_setFullscreen_ptr window_setFullscreen;
+ RGFW_window_setFloating_ptr window_setFloating;
+ RGFW_window_setOpacity_ptr window_setOpacity;
+ RGFW_window_minimize_ptr window_minimize;
+ RGFW_window_restore_ptr window_restore;
+ RGFW_window_isFloating_ptr window_isFloating;
+ RGFW_window_setName_ptr window_setName;
+ RGFW_window_setMousePassthrough_ptr window_setMousePassthrough;
+ RGFW_window_setIconEx_ptr window_setIconEx;
+ RGFW_loadMouse_ptr loadMouse;
+ RGFW_window_setMouse_ptr window_setMouse;
+ RGFW_window_moveMouse_ptr window_moveMouse;
+ RGFW_window_setMouseDefault_ptr window_setMouseDefault;
+ RGFW_window_setMouseStandard_ptr window_setMouseStandard;
+ RGFW_window_hide_ptr window_hide;
+ RGFW_window_show_ptr window_show;
+ RGFW_window_flash_ptr window_flash;
+ RGFW_readClipboardPtr_ptr readClipboardPtr;
+ RGFW_writeClipboard_ptr writeClipboard;
+ RGFW_window_isHidden_ptr window_isHidden;
+ RGFW_window_isMinimized_ptr window_isMinimized;
+ RGFW_window_isMaximized_ptr window_isMaximized;
+ RGFW_monitor_requestMode_ptr monitor_requestMode;
+ RGFW_monitor_getWorkarea_ptr monitor_getWorkarea;
+ RGFW_monitor_getModesPtr_ptr monitor_getModesPtr;
+ RGFW_monitor_getGammaRampPtr_ptr monitor_getGammaRampPtr;
+ RGFW_monitor_setGammaRamp_ptr monitor_setGammaRamp;
+ RGFW_monitor_setMode_ptr monitor_setMode;
+ RGFW_window_getMonitor_ptr window_getMonitor;
+ RGFW_window_closePlatform_ptr window_closePlatform;
+#ifdef RGFW_OPENGL
+ RGFW_extensionSupportedPlatform_OpenGL_ptr extensionSupportedPlatform_OpenGL;
+ RGFW_getProcAddress_OpenGL_ptr getProcAddress_OpenGL;
+ RGFW_window_createContextPtr_OpenGL_ptr window_createContextPtr_OpenGL;
+ RGFW_window_deleteContextPtr_OpenGL_ptr window_deleteContextPtr_OpenGL;
+ RGFW_window_makeCurrentContext_OpenGL_ptr window_makeCurrentContext_OpenGL;
+ RGFW_getCurrentContext_OpenGL_ptr getCurrentContext_OpenGL;
+ RGFW_window_swapBuffers_OpenGL_ptr window_swapBuffers_OpenGL;
+ RGFW_window_swapInterval_OpenGL_ptr window_swapInterval_OpenGL;
+#endif
+#ifdef RGFW_WEBGPU
+ RGFW_window_createSurface_WebGPU_ptr window_createSurface_WebGPU;
+#endif
+} RGFW_functionPointers;
+
+RGFW_functionPointers RGFW_api;
+
+RGFW_format RGFW_nativeFormat(void) { return RGFW_api.nativeFormat(); }
+RGFW_bool RGFW_createSurfacePtr(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) { return RGFW_api.createSurfacePtr(data, w, h, format, surface); }
+void RGFW_surface_freePtr(RGFW_surface* surface) { RGFW_api.surface_freePtr(surface); }
+void RGFW_freeMouse(RGFW_mouse* mouse) { RGFW_api.freeMouse(mouse); }
+void RGFW_window_blitSurface(RGFW_window* win, RGFW_surface* surface) { RGFW_api.window_blitSurface(win, surface); }
+void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { RGFW_api.window_setBorder(win, border); }
+void RGFW_window_captureMousePlatform(RGFW_window* win, RGFW_bool state) { RGFW_api.window_captureMousePlatform(win, state); }
+void RGFW_window_setRawMouseModePlatform(RGFW_window* win, RGFW_bool state) { RGFW_api.window_setRawMouseModePlatform(win, state); }
+RGFW_window* RGFW_createWindowPlatform(const char* name, RGFW_windowFlags flags, RGFW_window* win) { RGFW_init(); return RGFW_api.createWindowPlatform(name, flags, win); }
+RGFW_bool RGFW_getGlobalMouse(i32* x, i32* y) { return RGFW_api.getGlobalMouse(x, y); }
+RGFW_key RGFW_physicalToMappedKey(RGFW_key key) { return RGFW_api.physicalToMappedKey(key); }
+void RGFW_pollEvents(void) { RGFW_api.pollEvents(); }
+void RGFW_pollMonitors(void) { RGFW_api.pollMonitors(); }
+void RGFW_window_move(RGFW_window* win, i32 x, i32 y) { RGFW_api.window_move(win, x, y); }
+void RGFW_window_resize(RGFW_window* win, i32 w, i32 h) { RGFW_api.window_resize(win, w, h); }
+void RGFW_window_setAspectRatio(RGFW_window* win, i32 w, i32 h) { RGFW_api.window_setAspectRatio(win, w, h); }
+void RGFW_window_setMinSize(RGFW_window* win, i32 w, i32 h) { RGFW_api.window_setMinSize(win, w, h); }
+void RGFW_window_setMaxSize(RGFW_window* win, i32 w, i32 h) { RGFW_api.window_setMaxSize(win, w, h); }
+void RGFW_window_maximize(RGFW_window* win) { RGFW_api.window_maximize(win); }
+void RGFW_window_focus(RGFW_window* win) { RGFW_api.window_focus(win); }
+void RGFW_window_raise(RGFW_window* win) { RGFW_api.window_raise(win); }
+void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) { RGFW_api.window_setFullscreen(win, fullscreen); }
+void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) { RGFW_api.window_setFloating(win, floating); }
+void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) { RGFW_api.window_setOpacity(win, opacity); }
+void RGFW_window_minimize(RGFW_window* win) { RGFW_api.window_minimize(win); }
+void RGFW_window_restore(RGFW_window* win) { RGFW_api.window_restore(win); }
+RGFW_bool RGFW_window_isFloating(RGFW_window* win) { return RGFW_api.window_isFloating(win); }
+void RGFW_window_setName(RGFW_window* win, const char* name) { RGFW_api.window_setName(win, name); }
+
+#ifndef RGFW_NO_PASSTHROUGH
+void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) { RGFW_api.window_setMousePassthrough(win, passthrough); }
+#endif
+
+RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, u8 type) { return RGFW_api.window_setIconEx(win, data, w, h, format, type); }
+RGFW_mouse* RGFW_loadMouse(u8* data, i32 w, i32 h, RGFW_format format) { return RGFW_api.loadMouse(data, w, h, format); }
+void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) { RGFW_api.window_setMouse(win, mouse); }
+void RGFW_window_moveMouse(RGFW_window* win, i32 x, i32 y) { RGFW_api.window_moveMouse(win, x, y); }
+RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) { return RGFW_api.window_setMouseDefault(win); }
+RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { return RGFW_api.window_setMouseStandard(win, mouse); }
+void RGFW_window_hide(RGFW_window* win) { RGFW_api.window_hide(win); }
+void RGFW_window_show(RGFW_window* win) { RGFW_api.window_show(win); }
+void RGFW_window_flash(RGFW_window* win, RGFW_flashRequest request) { RGFW_api.window_flash(win, request); }
+RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { return RGFW_api.readClipboardPtr(str, strCapacity); }
+void RGFW_writeClipboard(const char* text, u32 textLen) { RGFW_api.writeClipboard(text, textLen); }
+RGFW_bool RGFW_window_isHidden(RGFW_window* win) { return RGFW_api.window_isHidden(win); }
+RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { return RGFW_api.window_isMinimized(win); }
+RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { return RGFW_api.window_isMaximized(win); }
+RGFW_bool RGFW_monitor_requestMode(RGFW_monitor* mon, RGFW_monitorMode* mode, RGFW_modeRequest request) { return RGFW_api.monitor_requestMode(mon, mode, request); }
+RGFW_bool RGFW_monitor_getWorkarea(RGFW_monitor* monitor, i32* x, i32* y, i32* width, i32* height) { return RGFW_api.monitor_getWorkarea(monitor, x, y, width, height); }
+size_t RGFW_monitor_getGammaRampPtr(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) { return RGFW_api.monitor_getGammaRampPtr(monitor, ramp); }
+RGFW_bool RGFW_monitor_setGammaRamp(RGFW_monitor* monitor, RGFW_gammaRamp* ramp) { return RGFW_api.monitor_setGammaRamp(monitor, ramp); }
+size_t RGFW_monitor_getModesPtr(RGFW_monitor* mon, RGFW_monitorMode** modes) { return RGFW_api.monitor_getModesPtr(mon, modes); }
+RGFW_bool RGFW_monitor_setMode(RGFW_monitor* mon, RGFW_monitorMode* mode) { return RGFW_api.monitor_setMode(mon, mode); }
+RGFW_monitor* RGFW_window_getMonitor(RGFW_window* win) { return RGFW_api.window_getMonitor(win); }
+void RGFW_window_closePlatform(RGFW_window* win) { RGFW_api.window_closePlatform(win); }
+
+#ifdef RGFW_OPENGL
+RGFW_bool RGFW_extensionSupportedPlatform_OpenGL(const char* extension, size_t len) { return RGFW_api.extensionSupportedPlatform_OpenGL(extension, len); }
+RGFW_proc RGFW_getProcAddress_OpenGL(const char* procname) { return RGFW_api.getProcAddress_OpenGL(procname); }
+RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) { return RGFW_api.window_createContextPtr_OpenGL(win, ctx, hints); }
+void RGFW_window_deleteContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx) { RGFW_api.window_deleteContextPtr_OpenGL(win, ctx); }
+void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win) { RGFW_api.window_makeCurrentContext_OpenGL(win); }
+void* RGFW_getCurrentContext_OpenGL(void) { return RGFW_api.getCurrentContext_OpenGL(); }
+void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) { RGFW_api.window_swapBuffers_OpenGL(win); }
+void RGFW_window_swapInterval_OpenGL(RGFW_window* win, i32 swapInterval) { RGFW_api.window_swapInterval_OpenGL(win, swapInterval); }
+#endif
+
+#ifdef RGFW_WEBGPU
+WGPUSurface RGFW_window_createSurface_WebGPU(RGFW_window* window, WGPUInstance instance) { return RGFW_api.window_createSurface_WebGPU(window, instance); }
+#endif
+#endif /* RGFW_DYNAMIC */
+
+/*
+ * start of X11 AND wayland defines
+ * this allows a single executable to support x11 AND wayland
+ * falling back to x11 if wayland fails to initalize
+*/
+#if defined(RGFW_WAYLAND) && defined(RGFW_X11)
+void RGFW_load_X11(void) {
+ RGFW_api.nativeFormat = RGFW_nativeFormat_X11;
+ RGFW_api.createSurfacePtr = RGFW_createSurfacePtr_X11;
+ RGFW_api.window_blitSurface = RGFW_window_blitSurface_X11;
+ RGFW_api.surface_freePtr = RGFW_surface_freePtr_X11;
+ RGFW_api.freeMouse = RGFW_freeMouse_X11;
+ RGFW_api.window_setBorder = RGFW_window_setBorder_X11;
+ RGFW_api.window_captureMousePlatform = RGFW_window_captureMousePlatform_X11;
+ RGFW_api.window_setRawMouseModePlatform = RGFW_window_setRawMouseModePlatform_X11;
+ RGFW_api.createWindowPlatform = RGFW_createWindowPlatform_X11;
+ RGFW_api.getGlobalMouse = RGFW_getGlobalMouse_X11;
+ RGFW_api.physicalToMappedKey = RGFW_physicalToMappedKey_X11;
+ RGFW_api.pollEvents = RGFW_pollEvents_X11;
+ RGFW_api.pollMonitors = RGFW_pollMonitors_X11;
+ RGFW_api.window_move = RGFW_window_move_X11;
+ RGFW_api.window_resize = RGFW_window_resize_X11;
+ RGFW_api.window_setAspectRatio = RGFW_window_setAspectRatio_X11;
+ RGFW_api.window_setMinSize = RGFW_window_setMinSize_X11;
+ RGFW_api.window_setMaxSize = RGFW_window_setMaxSize_X11;
+ RGFW_api.window_maximize = RGFW_window_maximize_X11;
+ RGFW_api.window_focus = RGFW_window_focus_X11;
+ RGFW_api.window_raise = RGFW_window_raise_X11;
+ RGFW_api.window_setFullscreen = RGFW_window_setFullscreen_X11;
+ RGFW_api.window_setFloating = RGFW_window_setFloating_X11;
+ RGFW_api.window_setOpacity = RGFW_window_setOpacity_X11;
+ RGFW_api.window_minimize = RGFW_window_minimize_X11;
+ RGFW_api.window_restore = RGFW_window_restore_X11;
+ RGFW_api.window_isFloating = RGFW_window_isFloating_X11;
+ RGFW_api.window_setName = RGFW_window_setName_X11;
+#ifndef RGFW_NO_PASSTHROUGH
+ RGFW_api.window_setMousePassthrough = RGFW_window_setMousePassthrough_X11;
+#endif
+ RGFW_api.window_setIconEx = RGFW_window_setIconEx_X11;
+ RGFW_api.loadMouse = RGFW_loadMouse_X11;
+ RGFW_api.window_setMouse = RGFW_window_setMouse_X11;
+ RGFW_api.window_moveMouse = RGFW_window_moveMouse_X11;
+ RGFW_api.window_setMouseDefault = RGFW_window_setMouseDefault_X11;
+ RGFW_api.window_setMouseStandard = RGFW_window_setMouseStandard_X11;
+ RGFW_api.window_hide = RGFW_window_hide_X11;
+ RGFW_api.window_show = RGFW_window_show_X11;
+ RGFW_api.window_flash = RGFW_window_flash_X11;
+ RGFW_api.readClipboardPtr = RGFW_readClipboardPtr_X11;
+ RGFW_api.writeClipboard = RGFW_writeClipboard_X11;
+ RGFW_api.window_isHidden = RGFW_window_isHidden_X11;
+ RGFW_api.window_isMinimized = RGFW_window_isMinimized_X11;
+ RGFW_api.window_isMaximized = RGFW_window_isMaximized_X11;
+ RGFW_api.monitor_requestMode = RGFW_monitor_requestMode_X11;
+ RGFW_api.monitor_getModesPtr = RGFW_monitor_getModesPtr_X11;
+ RGFW_api.monitor_setGammaRamp = RGFW_monitor_setGammaRamp_X11;
+ RGFW_api.monitor_getGammaRampPtr = RGFW_monitor_getGammaRampPtr_X11;
+ RGFW_api.monitor_setMode = RGFW_monitor_setMode_X11;
+ RGFW_api.window_getMonitor = RGFW_window_getMonitor_X11;
+ RGFW_api.window_closePlatform = RGFW_window_closePlatform_X11;
+#ifdef RGFW_OPENGL
+ RGFW_api.extensionSupportedPlatform_OpenGL = RGFW_extensionSupportedPlatform_OpenGL_X11;
+ RGFW_api.getProcAddress_OpenGL = RGFW_getProcAddress_OpenGL_X11;
+ RGFW_api.window_createContextPtr_OpenGL = RGFW_window_createContextPtr_OpenGL_X11;
+ RGFW_api.window_deleteContextPtr_OpenGL = RGFW_window_deleteContextPtr_OpenGL_X11;
+ RGFW_api.window_makeCurrentContext_OpenGL = RGFW_window_makeCurrentContext_OpenGL_X11;
+ RGFW_api.getCurrentContext_OpenGL = RGFW_getCurrentContext_OpenGL_X11;
+ RGFW_api.window_swapBuffers_OpenGL = RGFW_window_swapBuffers_OpenGL_X11;
+ RGFW_api.window_swapInterval_OpenGL = RGFW_window_swapInterval_OpenGL_X11;
+#endif
+#ifdef RGFW_WEBGPU
+ RGFW_api.window_createSurface_WebGPU = RGFW_window_createSurface_WebGPU_X11;
+#endif
+}
+
+void RGFW_load_Wayland(void) {
+ RGFW_api.nativeFormat = RGFW_nativeFormat_Wayland;
+ RGFW_api.createSurfacePtr = RGFW_createSurfacePtr_Wayland;
+ RGFW_api.window_blitSurface = RGFW_window_blitSurface_Wayland;
+ RGFW_api.surface_freePtr = RGFW_surface_freePtr_Wayland;
+ RGFW_api.freeMouse = RGFW_freeMouse_Wayland;
+ RGFW_api.window_setBorder = RGFW_window_setBorder_Wayland;
+ RGFW_api.window_captureMousePlatform = RGFW_window_captureMousePlatform_Wayland;
+ RGFW_api.window_setRawMouseModePlatform = RGFW_window_setRawMouseModePlatform_Wayland;
+ RGFW_api.createWindowPlatform = RGFW_createWindowPlatform_Wayland;
+ RGFW_api.getGlobalMouse = RGFW_getGlobalMouse_Wayland;
+ RGFW_api.physicalToMappedKey = RGFW_physicalToMappedKey_Wayland;
+ RGFW_api.pollEvents = RGFW_pollEvents_Wayland;
+ RGFW_api.pollMonitors = RGFW_pollMonitors_Wayland;
+ RGFW_api.window_move = RGFW_window_move_Wayland;
+ RGFW_api.window_resize = RGFW_window_resize_Wayland;
+ RGFW_api.window_setAspectRatio = RGFW_window_setAspectRatio_Wayland;
+ RGFW_api.window_setMinSize = RGFW_window_setMinSize_Wayland;
+ RGFW_api.window_setMaxSize = RGFW_window_setMaxSize_Wayland;
+ RGFW_api.window_maximize = RGFW_window_maximize_Wayland;
+ RGFW_api.window_focus = RGFW_window_focus_Wayland;
+ RGFW_api.window_raise = RGFW_window_raise_Wayland;
+ RGFW_api.window_setFullscreen = RGFW_window_setFullscreen_Wayland;
+ RGFW_api.window_setFloating = RGFW_window_setFloating_Wayland;
+ RGFW_api.window_setOpacity = RGFW_window_setOpacity_Wayland;
+ RGFW_api.window_minimize = RGFW_window_minimize_Wayland;
+ RGFW_api.window_restore = RGFW_window_restore_Wayland;
+ RGFW_api.window_isFloating = RGFW_window_isFloating_Wayland;
+ RGFW_api.window_setName = RGFW_window_setName_Wayland;
+#ifndef RGFW_NO_PASSTHROUGH
+ RGFW_api.window_setMousePassthrough = RGFW_window_setMousePassthrough_Wayland;
+#endif
+ RGFW_api.window_setIconEx = RGFW_window_setIconEx_Wayland;
+ RGFW_api.loadMouse = RGFW_loadMouse_Wayland;
+ RGFW_api.window_setMouse = RGFW_window_setMouse_Wayland;
+ RGFW_api.window_moveMouse = RGFW_window_moveMouse_Wayland;
+ RGFW_api.window_setMouseDefault = RGFW_window_setMouseDefault_Wayland;
+ RGFW_api.window_setMouseStandard = RGFW_window_setMouseStandard_Wayland;
+ RGFW_api.window_hide = RGFW_window_hide_Wayland;
+ RGFW_api.window_show = RGFW_window_show_Wayland;
+ RGFW_api.window_flash = RGFW_window_flash_X11;
+ RGFW_api.readClipboardPtr = RGFW_readClipboardPtr_Wayland;
+ RGFW_api.writeClipboard = RGFW_writeClipboard_Wayland;
+ RGFW_api.window_isHidden = RGFW_window_isHidden_Wayland;
+ RGFW_api.window_isMinimized = RGFW_window_isMinimized_Wayland;
+ RGFW_api.window_isMaximized = RGFW_window_isMaximized_Wayland;
+ RGFW_api.monitor_requestMode = RGFW_monitor_requestMode_Wayland;
+ RGFW_api.monitor_getModesPtr = RGFW_monitor_getModesPtr_Wayland;
+ RGFW_api.monitor_setGammaRamp = RGFW_monitor_setGammaRamp_Wayland;
+ RGFW_api.monitor_getGammaRampPtr = RGFW_monitor_getGammaRampPtr_Wayland;
+ RGFW_api.monitor_setMode = RGFW_monitor_setMode_Wayland;
+ RGFW_api.window_getMonitor = RGFW_window_getMonitor_Wayland;
+ RGFW_api.window_closePlatform = RGFW_window_closePlatform_Wayland;
+#ifdef RGFW_OPENGL
+ RGFW_api.extensionSupportedPlatform_OpenGL = RGFW_extensionSupportedPlatform_OpenGL_Wayland;
+ RGFW_api.getProcAddress_OpenGL = RGFW_getProcAddress_OpenGL_Wayland;
+ RGFW_api.window_createContextPtr_OpenGL = RGFW_window_createContextPtr_OpenGL_Wayland;
+ RGFW_api.window_deleteContextPtr_OpenGL = RGFW_window_deleteContextPtr_OpenGL_Wayland;
+ RGFW_api.window_makeCurrentContext_OpenGL = RGFW_window_makeCurrentContext_OpenGL_Wayland;
+ RGFW_api.getCurrentContext_OpenGL = RGFW_getCurrentContext_OpenGL_Wayland;
+ RGFW_api.window_swapBuffers_OpenGL = RGFW_window_swapBuffers_OpenGL_Wayland;
+ RGFW_api.window_swapInterval_OpenGL = RGFW_window_swapInterval_OpenGL_Wayland;
+#endif
+#ifdef RGFW_WEBGPU
+ RGFW_api.window_createSurface_WebGPU = RGFW_window_createSurface_WebGPU_Wayland;
+#endif
+}
+#endif /* wayland AND x11 */
+/* end of X11 AND wayland defines */
+
+#endif /* RGFW_IMPLEMENTATION */
+
+#if defined(__cplusplus) && !defined(__EMSCRIPTEN__)
+}
+#endif
+
+#if _MSC_VER
+ #pragma warning( pop )
+#endif
diff --git a/src/external/RGFW/deps/minigamepad.h b/src/external/RGFW/deps/minigamepad.h
new file mode 100644
index 000000000..dfadcbf40
--- /dev/null
+++ b/src/external/RGFW/deps/minigamepad.h
@@ -0,0 +1,5242 @@
+/*
+*
+* minigamepad - alpha
+
+* Copyright (C) 2025 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 MG_IMPLEMENTATION is in exactly one header or you use -D MG_IMPLEMENTATION)
+ #define MG_IMPLEMENTATION - makes it so source code is included with header
+*/
+
+/*
+ #define MG_IMPLEMENTATION - (required) makes it so the source code is included
+ #define MG_ALLOC x - choose the default allocation function (defaults to standard malloc)
+ #define MG_FREE x - choose the default deallocation function (defaults to standard free)
+
+ #define MG_EXPORT - use when building minigamepad
+ #define MG_IMPORT - use when linking with minigamepad (not as a single-header)
+
+ #define MG_USE_INT - force the use c-types rather than stdint.h (for systems that might not have stdint.h (msvc))
+ #define mg_bool x - choose what type to use for bool, by default u32 is used
+
+ #define MG_MAX_GAMEPADS x - set the max number of gamepads (4 by default)
+*/
+
+
+
+/*
+ Credits :
+ GLFW and SDL - used as a resource
+ https:///github.com/MysteriousJ/Joystick-Input-Examples - good resoruce for learning how to use gamepad code
+
+ contributors : (feel free to put yourself here if you contribute)
+*/
+
+#if _MSC_VER
+ #if _MSC_VER < 600
+ #define MG_C89
+ #endif
+#else
+ #if defined(__STDC__) && !defined(__STDC_VERSION__)
+ #define MG_C89
+ #endif
+#endif
+
+#ifndef MG_UNUSED
+ #define MG_UNUSED(x) (void)(x)
+#endif
+
+#ifndef MG_ALLOC
+ #include
+ #define MG_ALLOC malloc
+ #define MG_FREE free
+#endif
+
+#ifndef MG_ASSERT
+ #include
+ #define MG_ASSERT assert
+#endif
+
+#if !defined(MG_MEMCPY) || !defined(MG_STRNCMP) || !defined(MG_STRNCPY) || !defined(MG_MEMSET) || !defined(MG_STRCSPN) || !defined(MG_STRSPN)
+ #include
+#endif
+
+#ifndef MG_MEMSET
+ #define MG_MEMSET(ptr, value, num) memset(ptr, value, num)
+#endif
+
+#ifndef MG_MEMCPY
+ #define MG_MEMCPY(dist, src, len) memcpy(dist, src, len)
+#endif
+
+#ifndef MG_STRNCMP
+ #define MG_STRNCMP(s1, s2, max) strncmp(s1, s2, max)
+#endif
+
+#ifndef MG_STRNCPY
+ #define MG_STRNCPY(dist, src, len) strncpy(dist, src, len)
+#endif
+
+#ifndef MG_STRCSPN
+ #define MG_STRCSPN(str1, str2) strcspn(str1, str2)
+#endif
+
+#ifndef MG_STRSPN
+ #define MG_STRSPN(str1, str2) strspn(str1, str2)
+#endif
+
+#if !defined(MG_SPRINTF) || !defined(MG_FPRINTF)
+ #include
+#endif
+
+
+#if !defined(MG_SPRINTF)
+ #define MG_SPRINTF sprintf
+#endif
+
+#if !defined(MG_FPRINTF)
+ #define MG_FPRINTF fprintf
+#endif
+
+#ifndef MG_FABS
+#include
+#define MG_FABS(x) fabs(x)
+#endif
+
+#if defined(MG_EXPORT) || defined(MG_IMPORT)
+ #if defined(_WIN32)
+ #if defined(__TINYC__) && (defined(MG_EXPORT) || defined(MG_IMPORT))
+ #define __declspec(x) __attribute__((x))
+ #endif
+
+ #if defined(MG_EXPORT)
+ #define MG_API __declspec(dllexport)
+ #else
+ #define MG_API __declspec(dllimport)
+ #endif
+ #else
+ #if defined(MG_EXPORT)
+ #define MG_API __attribute__((visibility("default")))
+ #endif
+ #endif
+ #ifndef MG_API
+ #define MG_API
+ #endif
+#endif
+
+#ifndef MG_API
+ #ifdef MG_C89
+ #define MG_API
+ #else
+ #define MG_API inline
+ #endif
+#endif
+
+#ifndef MG_ENUM
+ #define MG_ENUM(type, name) type name; enum
+#endif
+
+
+#if defined(__cplusplus) && !defined(__EMSCRIPTEN__)
+ extern "C" {
+#endif
+
+ /* makes sure the header file part is only defined once by default */
+#ifndef MG_HEADER
+
+#define MG_HEADER
+
+#ifdef __EMSCRIPTEN__
+ #define MG_WASM
+#elif defined(_WIN32) || defined(__WIN32) || defined(__WIN32__)
+ #define MG_WINDOWS
+#elif defined(__linux__)
+ #define MG_LINUX
+#elif defined(__APPLE__)
+ #define MG_MACOS
+#endif
+
+#ifndef MG_INT_DEFINED
+ #ifdef MG_USE_INT /* optional for any system that might not have stdint.h */
+ typedef unsigned char u8;
+ typedef signed char i8;
+ typedef unsigned short u16;
+ typedef signed short i16;
+ typedef unsigned long int u32;
+ typedef signed long int i32;
+ typedef unsigned long long u64;
+ typedef signed long long i64;
+
+
+ typedef signed long mg_ssize_t;
+ typedef unsigned long mg_size_t;
+ #else /* use stdint standard types instead of c "standard" types */
+ #include
+ #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;
+
+#ifdef __linux__
+ #include
+ typedef ssize_t mg_ssize_t;
+#endif
+ typedef size_t mg_size_t;
+ #endif
+ #define MG_INT_DEFINED
+#endif
+
+#ifndef MG_BOOL_DEFINED
+ #define MG_BOOL_DEFINED
+ typedef u8 mg_bool;
+#endif
+
+#define MG_BOOL(x) (mg_bool)((x) ? MG_TRUE : MG_FALSE) /* force an value to be 0 or 1 */
+#define MG_TRUE (mg_bool)1
+#define MG_FALSE (mg_bool)0
+
+#ifndef MG_MAX_GAMEPADS
+ #define MG_MAX_GAMEPADS 4
+#endif
+
+#ifndef MG_MAX_EVENTS
+ #define MG_MAX_EVENTS 32
+#endif
+
+/**!
+ * @brief global stucture for the source API data of all the gamepads
+*/
+typedef struct mg_gamepads_src mg_gamepads_src;
+
+/**!
+ * @brief global stucture for the data of all the gamepads
+*/
+typedef struct mg_gamepads mg_gamepads;
+
+/**!
+ * @brief the source API data of the gamepad object
+*/
+typedef struct mg_gamepad_src mg_gamepad_src;
+
+/**!
+ * @brief data stucture for gamepad objects
+*/
+typedef struct mg_gamepad mg_gamepad;
+
+/**!
+ * @brief source gamepad list object
+*/
+typedef struct mg_gamepad_list {
+ mg_gamepad* head; /* head node of the list */
+ mg_gamepad* cur; /* current/tail node of the list */
+ mg_size_t count; /* number of nodes */
+} mg_gamepad_list;
+
+/**!
+ * @brief abstract constants for gamepad buttons
+*/
+typedef MG_ENUM(i8, mg_button) {
+ MG_BUTTON_UNKNOWN = -1,
+ MG_BUTTON_SOUTH, /**< Bottom face button (e.g. Xbox A button) */
+ MG_BUTTON_EAST, /**< Right face button (e.g. Xbox B button) */
+ MG_BUTTON_WEST, /**< Left face button (e.g. Xbox X button) */
+ MG_BUTTON_NORTH, /**< Top face button (e.g. Xbox Y button) */
+ MG_BUTTON_BACK, /**< back (or select) button on the gamepad */
+ MG_BUTTON_GUIDE, /**< guide button on the controller (e.g. the Xbox button or ps button) */
+ MG_BUTTON_START, /**< start button on the gamepad */
+ MG_BUTTON_LEFT_STICK, /**< left stick button (L3) */
+ MG_BUTTON_RIGHT_STICK, /**< left shoulder button (R4) */
+ MG_BUTTON_LEFT_SHOULDER, /**< left shoulder button (L1) */
+ MG_BUTTON_RIGHT_SHOULDER, /**< left shoulder button (R1) */
+ MG_BUTTON_DPAD_LEFT, /**< dpad left button */
+ MG_BUTTON_DPAD_RIGHT, /**< dpad right button */
+ MG_BUTTON_DPAD_UP, /**< dpad up button */
+ MG_BUTTON_DPAD_DOWN, /**< dpad down button */
+ MG_BUTTON_LEFT_TRIGGER, /**< left trigger button (L2) */
+ MG_BUTTON_RIGHT_TRIGGER, /**< right trigger button (R2) */
+ /* extras */
+ MG_BUTTON_MISC1, /**< Additional button (e.g. Xbox Series X share button, PS5 microphone button, Nintendo Switch Pro capture button, Amazon Luna microphone button, Google Stadia capture button) */
+ MG_BUTTON_RIGHT_PADDLE1, /**< Upper or primary paddle, under your right hand (e.g. Xbox Elite paddle P1) */
+ MG_BUTTON_LEFT_PADDLE1, /**< Upper or primary paddle, under your left hand (e.g. Xbox Elite paddle P3) */
+ MG_BUTTON_RIGHT_PADDLE2, /**< Lower or secondary paddle, under your right hand (e.g. Xbox Elite paddle P2) */
+ MG_BUTTON_LEFT_PADDLE2, /**< Lower or secondary paddle, under your left hand (e.g. Xbox Elite paddle P4) */
+ MG_BUTTON_TOUCHPAD, /**< PS4/PS5 touchpad button */
+ MG_BUTTON_MISC2, /**< Additional button */
+ MG_BUTTON_MISC3, /**< Additional button */
+ MG_BUTTON_MISC4, /**< Additional button */
+ MG_BUTTON_MISC5, /**< Additional button */
+ MG_BUTTON_MISC6, /**< Additional button */
+ MG_BUTTON_COUNT
+};
+
+/**!
+ * @brief abstract constants for gamepad axes
+*/
+typedef MG_ENUM(i8, mg_axis) {
+ MG_AXIS_UNKNOWN = -1, /**< */
+ MG_AXIS_LEFT_X, /**< X axis of the left stick */
+ MG_AXIS_LEFT_Y, /**< Y axis of the left stick */
+ MG_AXIS_RIGHT_X, /**< X axis of the right stick */
+ MG_AXIS_RIGHT_Y, /**< Y axis of the left stick */
+ MG_AXIS_LEFT_TRIGGER, /**< Axis of the left triggle (0 - 1) */
+ MG_AXIS_RIGHT_TRIGGER, /**< Axis of the right triggle (0 - 1) */
+ MG_AXIS_HAT_DPAD_LEFT_RIGHT, /**< d-pad left-right hat value */
+ MG_AXIS_HAT_DPAD_LEFT = MG_AXIS_HAT_DPAD_LEFT_RIGHT,
+ MG_AXIS_HAT_DPAD_RIGHT = MG_AXIS_HAT_DPAD_LEFT_RIGHT,
+ MG_AXIS_HAT_DPAD_UP_DOWN, /**< d-pad up-down hat value */
+ MG_AXIS_HAT_DPAD_UP = MG_AXIS_HAT_DPAD_UP_DOWN, /**< */
+ MG_AXIS_HAT_DPAD_DOWN = MG_AXIS_HAT_DPAD_UP_DOWN,
+/* extras */
+ MG_AXIS_THROTTLE,
+ MG_AXIS_RUDDER,
+ MG_AXIS_WHEEL,
+ MG_AXIS_GAS,
+ MG_AXIS_BRAKE,
+ MG_AXIS_HAT1X,
+ MG_AXIS_HAT1Y,
+ MG_AXIS_HAT2X,
+ MG_AXIS_HAT2Y,
+ MG_AXIS_HAT3X,
+ MG_AXIS_HAT3Y,
+ MG_AXIS_PRESSURE,
+ MG_AXIS_DISTANCE,
+ MG_AXIS_TILT_X,
+ MG_AXIS_TILT_Y,
+ MG_AXIS_TOOL_WIDTH,
+ MG_AXIS_VOLUME,
+ MG_AXIS_PROFILE,
+ MG_AXIS_MISC,
+
+ MG_AXIS_COUNT
+};
+
+/**!
+ * @brief the type of event in the queue
+*/
+typedef MG_ENUM(u8, mg_event_type) {
+ MG_EVENT_NONE = 0, /**< no/null event */
+ MG_EVENT_GAMEPAD_CONNECT, /**< a new gamepad connected */
+ MG_EVENT_GAMEPAD_DISCONNECT, /**< a gamepad was disconnected */
+ MG_EVENT_BUTTON_PRESS, /**< gamepad button was pressed */
+ MG_EVENT_BUTTON_RELEASE, /**< gamepad button was released */
+ MG_EVENT_AXIS_MOVE /**< gamepad axis was moved/changed */
+};
+
+/**!
+ * @brief button (press or release) event type
+*/
+typedef struct mg_button_state {
+ mg_bool supported; /* if the button is supported or not by the gamepad */
+ mg_bool current; /* the current state of the button */
+ mg_bool prev; /* the previous state of the button */
+} mg_button_state;
+
+/**!
+ * @brief axis motion event type
+*/
+typedef struct mg_axis_state {
+ mg_bool supported; /* if the axis is supported or not by the gamepad */
+ float value; /* the current value of the axis */
+ float deadzone; /* deadzones of the axis */
+} mg_axis_state;
+
+/**!
+ * @brief event structure for processing event data
+*/
+typedef struct mg_event {
+ mg_event_type type; /* the type of event */
+ mg_button button; /* button value */
+ mg_axis axis; /* axis value */
+ mg_gamepad* gamepad; /* which gamepad */
+} mg_event;
+
+/**!
+ * @brief stucture for all event data for collecting events
+*/
+typedef struct mg_events {
+ mg_event queue[MG_MAX_EVENTS]; /* event queue array for collecting the frame's events */
+ size_t len; /* the number of events in the array */
+} mg_events;
+
+/* callbacks */
+/**!
+ * @brief type for the connection callback function
+*/
+typedef void (*mg_gamepad_connection_func)(mg_gamepad* gamepad, mg_bool connected);
+
+/**!
+ * @brief type for the button state callback function
+*/
+typedef void (*mg_gamepad_button_func)(mg_gamepad* gamepad, mg_button button, mg_bool pressed);
+
+/**!
+ * @brief type for the axis move callback function
+*/
+typedef void (*mg_gamepad_axis_func)(mg_gamepad* gamepad, mg_axis);
+
+/**!
+ * @brief init gamepads, api and internal data
+ * @param pointer to a pre-allocated gamepads object
+*/
+MG_API void mg_gamepads_init(mg_gamepads* gamepads);
+
+/**!
+ * @brief enable or disable the event queue [disabled by default and enabled by default when mg_gamepads_check_event is called]
+ * @param gamepads object to modify
+*/
+MG_API void mg_gamepads_set_queue_events(mg_gamepads* gamepads, mg_bool queue_events);
+
+/**!
+ * @brief poll information on the gamepads
+ * @param gamepads object that needs to be updated
+ * @return returns a boolean value if there was an event or not to process
+*/
+MG_API mg_bool mg_gamepads_poll(mg_gamepads* gamepads);
+
+/**!
+ * @brief check and pop top event in the gamepad's event queue, does not poll for events
+ * @param gamepads object that needs to be updated
+ * @param pointer to a event object to fill the event with [can be NULL]
+ * @return returns a boolean value if there was an event or not to process
+*/
+MG_API mg_bool mg_gamepads_check_queued_event(mg_gamepads* gamepads, mg_event* event);
+
+/**!
+ * @brief poll for events if they weren't already polled then check and pop the top event in the event queue
+ * @param gamepads object that needs to be updated
+ * @param pointer to a event object to fill the event with [can be NULL]
+ * @return returns a boolean value if there was an event or not to process
+*/
+MG_API mg_bool mg_gamepads_check_event(mg_gamepads* gamepads, mg_event* event);
+
+/**!
+ * @brief free internal gamepads data
+ * @param gamepads object
+*/
+MG_API void mg_gamepads_free(mg_gamepads* gamepads);
+
+/**!
+ * @brief returns if a button of a gamepad was pressed or not
+ * @param gamepad object
+ * @param button to check
+ * @return boolean value button for if the button was pressed
+*/
+
+MG_API mg_bool mg_gamepad_button_is_pressed(mg_gamepad* gamepad, mg_button button);
+
+
+/* add a new mapping */
+/**!
+ * @brief returns if a button of a gamepad was released down or not
+ * @param the gamepad object
+ * @param button to check
+ * @return the boolean value for if the button was released
+*/
+
+MG_API mg_bool mg_gamepad_button_is_released(mg_gamepad* gamepad, mg_button button);
+
+/* add a new mapping */
+/**!
+ * @brief returns if a button of a gamepad is down or not
+ * @param the gamepad object
+ * @param the button to check
+ * @return the boolean state of the button
+*/
+
+MG_API mg_bool mg_gamepad_button_is_down(mg_gamepad* gamepad, mg_button button);
+
+/* add a new mapping */
+/**!
+ * @brief returns the axis value of an axis of a gamepad
+ * @param the source gamepad object
+ * @param the axis to check
+ * @return the current floating point value of the axis
+*/
+
+MG_API float mg_gamepad_axis_value(mg_gamepad* gamepad, mg_axis axis);
+
+/* add a new mapping */
+/**!
+ * @brief set the function object for gamepad connection events
+ * @param the function object to use
+ * @return the original function object in use
+*/
+
+MG_API mg_gamepad_connection_func mg_set_gamepad_connected_callback(mg_gamepad_connection_func func);
+
+/**!
+ * @brief set the function object for gamepad release events
+ * @param the function object to use
+ * @return the original function object in use
+*/
+MG_API mg_gamepad_button_func mg_set_gamepad_release_callback(mg_gamepad_button_func func);
+
+/**!
+ * @brief set the function object for gamepad axis movement aevents
+ * @param the function object to use
+ * @return the current function object in use
+*/
+MG_API mg_gamepad_axis_func mg_set_gamepad_axis_callback(mg_gamepad_axis_func func);
+
+/**!
+ * @brief set the gamepad disconnected callback function object
+ * @param the function object to use
+ * @return the original value of the disconnected callback
+*/
+
+MG_API mg_gamepad_connection_func mg_set_gamepad_disconnected_callback(mg_gamepad_connection_func func);
+
+/**!
+ * @brief set the gamepad press callbqack function to use
+ * @param the function object to use
+ * @return the original value of the gamepad press callback
+*/
+MG_API mg_gamepad_button_func mg_set_gamepad_press_callback(mg_gamepad_button_func func);
+
+/* add a new mapping */
+/**!
+ * @brief update the mappings the gamepads object uses with a new mapping
+ * @param the new mapping string
+ * @return returns a boolean value based on success
+*/
+MG_API mg_bool mg_update_gamepad_mappings(mg_gamepads* gamepads, const char* string);
+
+/**!
+ * @brief fetch the string name of a button (for testing)
+ * @param the button enum value
+ * @return constant c-string that represents the button's name
+*/
+MG_API const char* mg_button_get_name(mg_button button);
+
+/**!
+ * @brief fetch the string name of a axis (for testing)
+ * @param the axis enum value
+ * @return constant c-string that represents the axis's name
+*/
+MG_API const char* mg_axis_get_name(mg_axis axis);
+
+#endif /* MG_HEADER */
+
+#if (defined(MG_NATIVE) || defined(MG_IMPLEMENTATION)) && !defined(MG_NATIVE_HEADER)
+#define MG_NATIVE_HEADER
+
+#ifdef MG_LINUX
+
+struct mg_input_absinfo {
+ i32 value;
+ i32 minimum;
+ i32 maximum;
+ i32 fuzz;
+ i32 flat;
+ i32 resolution;
+};
+
+struct mg_gamepad_src {
+ int fd;
+ u8 keyMap[512];
+ u8 absMap[64];
+ struct mg_input_absinfo absInfo[64];
+ char full_path[256];
+};
+
+#elif defined(MG_WINDOWS)
+
+struct mg_gamepad_src {
+ void* device;
+ u32 xinput_index;
+};
+
+#elif defined(MG_MACOS)
+
+struct mg_gamepad_src {
+ void* device;
+ void* events;
+};
+
+#elif defined(MG_WASM)
+
+struct mg_gamepad_src {
+ int index;
+};
+
+#endif
+
+struct mg_mapping;
+
+struct mg_gamepad {
+ char name[128];
+ char guid[33];
+
+ mg_button_state buttons[MG_BUTTON_COUNT];
+ mg_axis_state axes[MG_AXIS_COUNT];
+
+ mg_bool connected;
+ mg_size_t index; /* index in gamepad array */
+
+ struct mg_mapping* mapping;
+ struct mg_gamepad* prev;
+ struct mg_gamepad* next;
+ mg_gamepad_src src;
+};
+
+#ifdef MG_LINUX
+#include
+#include
+
+struct mg_gamepads_src {
+ int inotify, watch;
+};
+#elif defined(MG_WINDOWS)
+struct mg_gamepads_src {
+ void* dinput;
+ void* ginput;
+ void* dummy_win;
+};
+#elif defined(MG_MACOS)
+struct mg_gamepads_src {
+ void* hidManager;
+};
+#elif defined(MG_WASM)
+struct mg_gamepads_src {
+ int TODO;
+};
+#endif
+
+struct mg_gamepads {
+ mg_gamepad gamepads[MG_MAX_GAMEPADS];
+
+ mg_gamepad_list list;
+ mg_gamepad_list free_list;
+
+ mg_events events;
+
+ mg_bool queue_events;
+ mg_bool polled_events;
+
+ mg_gamepads_src src;
+};
+
+#endif /* MG_NATIVE */
+
+#ifdef MG_IMPLEMENTATION
+
+/* gamepads->src.global API */
+/* find a valid unused gamepad or return NULL */
+MG_API mg_gamepad* mg_gamepad_find(mg_gamepads* gamepads);
+MG_API void mg_gamepad_release(mg_gamepads* gamepads, mg_gamepad* gamepad);
+MG_API void mg_list_swap_gamepad(mg_gamepad_list* from, mg_gamepad_list* to, mg_gamepad* gamepad);
+
+/* gamepads->src.platform-specific API */
+MG_API void mg_gamepads_init_platform(mg_gamepads* gamepads);
+/* updates gamepad structure by checking if any new gamepads are connected and adding them */
+MG_API mg_bool mg_gamepads_poll_platform(mg_gamepads* gamepads, mg_events* events);
+MG_API void mg_gamepads_free_platform(mg_gamepads* gamepads);
+MG_API mg_bool mg_gamepad_update_platform(mg_gamepad* gamepad, mg_events* events);
+MG_API void mg_gamepad_release_platform(mg_gamepad* gamepad);
+MG_API mg_button mg_get_gamepad_button_platform(u32 button);
+MG_API mg_axis mg_get_gamepad_axis_platform(u32 axis);
+
+/* gamepads->src.mappings API */
+MG_API struct mg_mapping* mg_gamepad_find_valid_mapping(mg_gamepad* gamepad);
+MG_API mg_button mg_get_gamepad_button(mg_gamepad* gamepad, u8 button);
+MG_API mg_axis mg_get_gamepad_axis(mg_gamepad* gamepad, u8 axis);
+MG_API void mg_mappings_init(void);
+/* public/global API implementation */
+
+mg_bool mg_gamepad_button_is_pressed(mg_gamepad* gamepad, mg_button button) {
+ return gamepad->buttons[button].current;
+}
+
+mg_bool mg_gamepad_button_is_released(mg_gamepad* gamepad, mg_button button) {
+ return gamepad->buttons[button].prev && !gamepad->buttons[button].current;
+}
+
+mg_bool mg_gamepad_button_is_down(mg_gamepad* gamepad, mg_button button) {
+ return gamepad->buttons[button].prev && gamepad->buttons[button].current;
+}
+
+float mg_gamepad_axis_value(mg_gamepad* gamepad, mg_axis axis) {
+ return gamepad->axes[axis].value;
+}
+
+static mg_gamepad_connection_func mg_gamepad_connected_callback = NULL;
+
+mg_gamepad_connection_func mg_set_gamepad_connected_callback(mg_gamepad_connection_func func) {
+ mg_gamepad_connection_func prev = mg_gamepad_connected_callback;
+ mg_gamepad_connected_callback = func;
+ return prev;
+}
+
+static mg_gamepad_connection_func mg_gamepad_disconnected_callback = NULL;
+
+mg_gamepad_connection_func mg_set_gamepad_disconnected_callback(mg_gamepad_connection_func func) {
+ mg_gamepad_connection_func prev = mg_gamepad_disconnected_callback;
+ mg_gamepad_disconnected_callback = func;
+ return prev;
+}
+
+static mg_gamepad_button_func mg_gamepad_press_callback = NULL;
+
+mg_gamepad_button_func mg_set_gamepad_press_callback(mg_gamepad_button_func func) {
+ mg_gamepad_button_func prev = mg_gamepad_press_callback;
+ mg_gamepad_press_callback = func;
+ return prev;
+}
+
+static mg_gamepad_button_func mg_gamepad_release_callback = NULL;
+#define mg_release_callback(gamepad, button) if (mg_gamepad_release_callback) mg_gamepad_release_callback(gamepad, button, MG_FALSE)
+
+mg_gamepad_button_func mg_set_gamepad_release_callback(mg_gamepad_button_func func) {
+ mg_gamepad_button_func prev = mg_gamepad_release_callback;
+ mg_gamepad_release_callback = func;
+ return prev;
+}
+
+static mg_gamepad_axis_func mg_gamepad_axis_callback = NULL;
+mg_gamepad_axis_func mg_set_gamepad_axis_callback(mg_gamepad_axis_func func) {
+ mg_gamepad_axis_func prev = mg_gamepad_axis_callback;
+ mg_gamepad_axis_callback = func;
+ return prev;
+}
+
+void mg_handle_event(mg_events* events, mg_event_type type, mg_button btn, mg_axis axis, mg_bool state, float value, mg_gamepad* gamepad) {
+ mg_event event;
+ event.gamepad = gamepad;
+ event.axis = axis;
+ event.button = btn;
+
+ switch (type) {
+ case MG_EVENT_GAMEPAD_CONNECT:
+ case MG_EVENT_GAMEPAD_DISCONNECT:
+ if (state) {
+ type = MG_EVENT_GAMEPAD_CONNECT;
+ if (mg_gamepad_connected_callback) mg_gamepad_connected_callback(gamepad, state);
+ } else {
+ type = MG_EVENT_GAMEPAD_DISCONNECT;
+ if (mg_gamepad_disconnected_callback) mg_gamepad_disconnected_callback(gamepad, state);
+ }
+ break;
+ case MG_EVENT_BUTTON_PRESS:
+ case MG_EVENT_BUTTON_RELEASE:
+ if (state == gamepad->buttons[btn].current) {
+ return;
+ }
+
+ gamepad->buttons[btn].prev = gamepad->buttons[btn].current;
+ gamepad->buttons[btn].current = state;
+ if (state) {
+ type = MG_EVENT_BUTTON_PRESS;
+ if (mg_gamepad_press_callback) mg_gamepad_press_callback(gamepad, btn, state);
+ }
+ else {
+ type = MG_EVENT_BUTTON_RELEASE;
+ if (mg_gamepad_release_callback) mg_gamepad_release_callback(gamepad, btn, state);
+ }
+ break;
+ case MG_EVENT_AXIS_MOVE:
+ if (value == gamepad->axes[axis].value) {
+ return;
+ }
+
+ gamepad->axes[axis].value = value;
+ if (mg_gamepad_axis_callback) mg_gamepad_axis_callback(gamepad, axis);
+ break;
+ default: break;
+ }
+
+ /* push event */
+ if (events == NULL || events->len >= MG_MAX_EVENTS) {
+ return;
+ }
+
+ event.type = type;
+ events->len += 1;
+ events->queue[MG_MAX_EVENTS - events->len] = event;
+}
+
+#define mg_handle_connection_event(events, state, gamepad) mg_handle_event(events, MG_EVENT_GAMEPAD_CONNECT, 0, 0, state, 0, gamepad)
+#define mg_handle_button_event(events, btn, state, gamepad) mg_handle_event(events, MG_EVENT_BUTTON_PRESS, btn, 0, state, 0, gamepad)
+#define mg_handle_axis_event(events, axis, value, gamepad) mg_handle_event(events, MG_EVENT_AXIS_MOVE, 0, axis, 0, value, gamepad)
+
+void mg_gamepads_init(mg_gamepads* gamepads) {
+ MG_ASSERT(gamepads != NULL);
+
+ gamepads->polled_events = MG_FALSE;
+ gamepads->queue_events = MG_FALSE;
+
+ mg_mappings_init();
+ MG_MEMSET(gamepads, 0, sizeof(mg_gamepads));
+
+ /* create free list */
+ gamepads->free_list.head = &gamepads->gamepads[0];
+ gamepads->free_list.cur = gamepads->free_list.head;
+
+ {
+ mg_size_t i;
+ for (i = 0; i < MG_MAX_GAMEPADS; i++) {
+ gamepads->free_list.cur->prev = NULL;
+ gamepads->free_list.cur->next = NULL;
+
+ if (i) {
+ gamepads->free_list.cur->prev = &gamepads->gamepads[i - 1];
+ }
+
+ if (i != MG_MAX_GAMEPADS - 1) {
+ gamepads->free_list.cur->next = &gamepads->gamepads[i + 1];
+ gamepads->free_list.cur = gamepads->free_list.cur->next;
+ }
+ }
+ }
+
+ mg_gamepads_init_platform(gamepads);
+}
+
+void mg_gamepads_set_queue_events(mg_gamepads* gamepads, mg_bool queue_events) {
+ gamepads->polled_events = queue_events;
+}
+
+mg_bool mg_gamepads_poll(mg_gamepads* gamepads) {
+ mg_gamepad* cur;
+ mg_bool out = MG_FALSE;
+
+ mg_events* events = (gamepads->queue_events) ? &gamepads->events : NULL;
+
+ if (mg_gamepads_poll_platform(gamepads, events)) {
+ out = MG_TRUE;
+ }
+
+ for (cur = gamepads->list.head; cur != NULL; cur = cur->next) {
+ if (mg_gamepad_update_platform(cur, events)) {
+ out = MG_TRUE;
+ }
+ }
+
+ return out;
+}
+
+mg_bool mg_events_pop(mg_events* events, mg_event* ev) {
+ MG_ASSERT(events->len <= MG_MAX_EVENTS);
+
+ if (events->len == 0) {
+ return MG_FALSE;
+ }
+
+ if (ev) {
+ *ev = events->queue[MG_MAX_EVENTS - events->len];
+ }
+
+ events->len -= 1;
+ return MG_TRUE;
+}
+
+mg_bool mg_gamepads_check_event(mg_gamepads* gamepads, mg_event* event) {
+ if (gamepads->events.len == 0 && gamepads->polled_events == MG_FALSE) {
+ gamepads->queue_events = MG_TRUE;
+ mg_gamepads_poll(gamepads);
+ gamepads->polled_events = MG_TRUE;
+ }
+
+ if (mg_gamepads_check_queued_event(gamepads, event) == MG_FALSE) {
+ gamepads->polled_events = MG_FALSE;
+ return MG_FALSE;
+ }
+
+ return MG_TRUE;
+}
+
+mg_bool mg_gamepads_check_queued_event(mg_gamepads* gamepads, mg_event* event) {
+ MG_ASSERT(gamepads != NULL);
+ gamepads->polled_events = MG_TRUE;
+
+ /* check queued events */
+ return mg_events_pop(&gamepads->events, event);
+}
+
+void mg_gamepads_free(mg_gamepads* gamepads) {
+ mg_gamepad* cur;
+ MG_ASSERT(gamepads != NULL);
+
+ mg_gamepads_free_platform(gamepads);
+
+ for (cur = gamepads->list.cur; cur != NULL; cur = cur->prev) {
+ mg_gamepad_release(gamepads, cur);
+ }
+ MG_MEMSET(gamepads, 0, sizeof(mg_gamepads));
+}
+
+void mg_list_swap_gamepad(mg_gamepad_list* from, mg_gamepad_list* to, mg_gamepad* gamepad) {
+ if (gamepad->prev != NULL) {
+ gamepad->prev->next = gamepad->next;
+ }
+
+ if (gamepad->next != NULL) {
+ gamepad->next->prev = gamepad->prev;
+ }
+
+ if (from->cur == gamepad) {
+ from->cur = gamepad->prev;
+ }
+
+ if (from->head == gamepad) {
+ from->head = gamepad->next;
+ }
+
+ MG_MEMSET(gamepad, 0, sizeof(mg_gamepad));
+
+ if (to->head == NULL) {
+ to->head = gamepad;
+ to->cur = NULL;
+ } else if (to->cur) {
+ to->cur->next = gamepad;
+ }
+
+ gamepad->prev = to->cur;
+ to->cur = gamepad;
+}
+
+mg_gamepad* mg_gamepad_find(mg_gamepads* gamepads) {
+ mg_gamepad* gamepad = gamepads->free_list.cur;
+ if (gamepad == NULL)
+ return NULL;
+
+ mg_list_swap_gamepad(&gamepads->free_list, &gamepads->list, gamepad);
+ gamepad->index = (mg_size_t)(gamepad - gamepads->gamepads);
+ return gamepad;
+}
+
+
+void mg_gamepad_release(mg_gamepads* gamepads, mg_gamepad* gamepad) {
+ mg_gamepad_release_platform(gamepad);
+ mg_list_swap_gamepad(&gamepads->list, &gamepads->free_list, gamepad);
+}
+
+/*
+ * start of linux
+ */
+
+#if defined(MG_LINUX)
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+mg_gamepad* mg_linux_setup_gamepad(mg_gamepads* gamepads, const char* full_path) {
+ int fd = 0;
+ struct input_id id = {0};
+ char evBits[(EV_CNT + 7) / 8] = {0};
+ char keyBits[(KEY_CNT + 7) / 8] = {0};
+ char absBits[(ABS_CNT + 7) / 8] = {0};
+ u32 i, btn, axis;
+ mg_size_t buttonCount = 0, axisCount = 0;
+
+ mg_gamepad* gamepad = mg_gamepad_find(gamepads);
+ if (gamepad == NULL) {
+ return NULL;
+ }
+
+ MG_STRNCPY(gamepad->src.full_path, full_path, 256);
+
+ gamepad->src.fd = open(full_path, O_RDWR);
+
+ fd = gamepad->src.fd;
+ if (fd <= 0) {
+ mg_gamepad_release(gamepads, gamepad);
+ return NULL;
+ }
+
+ if (ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits) < 0 ||
+ ioctl(fd, EVIOCGID, &id) < 0 ||
+ ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits) < 0 ||
+ ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits) < 0
+ ) {
+ mg_gamepad_release(gamepads, gamepad);
+ return NULL;
+ }
+
+ #define isBitSet(bit, arr) (arr[(bit) / 8] & (1 << ((bit) % 8)))
+ if (!isBitSet(EV_ABS, evBits)) {
+ mg_gamepad_release(gamepads, gamepad);
+ return NULL;
+ }
+
+ memset(gamepad->buttons, 0, sizeof(gamepad->buttons));
+ memset(gamepad->buttons, 0, sizeof(gamepad->axes));
+
+ /* go through any buttons a gamepad would have */
+ for (i = BTN_MISC; i < KEY_CNT; i++) {
+ if (!isBitSet(i, keyBits))
+ continue;
+
+ gamepad->src.keyMap[i - BTN_MISC] = (u8)buttonCount;
+ buttonCount++;
+ }
+
+ /* go through any axes a gamepad would have */
+ for (i = 0; i < ABS_CNT; i++) {
+ if (!isBitSet(i, absBits))
+ continue;
+
+ if (ioctl(fd, EVIOCGABS(i), (struct input_absinfo*)&gamepad->src.absInfo[i]) < 0)
+ continue;
+
+ gamepad->src.absMap[i] = (u8)axisCount;
+ axisCount++;
+ }
+
+ if ((axisCount == 0 && buttonCount == 0) || buttonCount > MG_BUTTON_COUNT + 10) {
+ mg_gamepad_release(gamepads, gamepad);
+ return NULL;
+ }
+
+ /* Generate a joystick GUID that matches the SDL 2.0.5+ one (sourced from GLFW) */
+ if (ioctl(gamepad->src.fd, EVIOCGNAME(sizeof(gamepad->name)), gamepad->name) < 0)
+ MG_STRNCPY(gamepad->name, "Unknown", sizeof(gamepad->name));
+
+ if (id.vendor && id.product && id.version) {
+ MG_SPRINTF(gamepad->guid, "%02x%02x0000%02x%02x0000%02x%02x0000%02x%02x0000",
+ id.bustype & 0xff, id.bustype >> 8,
+ id.vendor & 0xff, id.vendor >> 8,
+ id.product & 0xff, id.product >> 8,
+ id.version & 0xff, id.version >> 8);
+ } else {
+ const char* name = (const char*)gamepad->name;
+ MG_SPRINTF(gamepad->guid, "%02x%02x0000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00",
+ id.bustype & 0xff, id.bustype >> 8,
+ name[0], name[1], name[2], name[3],
+ name[4], name[5], name[6], name[7],
+ name[8], name[9], name[10]);
+ }
+
+ gamepad->mapping = mg_gamepad_find_valid_mapping(gamepad);
+
+ for (btn = BTN_MISC; btn < KEY_CNT; btn++) {
+ mg_button key = mg_get_gamepad_button(gamepad, gamepad->src.keyMap[btn - BTN_MISC]);
+ if (key == MG_BUTTON_UNKNOWN)
+ key = mg_get_gamepad_button_platform(btn);
+ if (key == MG_BUTTON_UNKNOWN)
+ continue;
+
+ if (gamepad->buttons[key].supported)
+ continue;
+
+ if (!isBitSet(btn, keyBits)) {
+ gamepad->buttons[key].supported = MG_FALSE;
+ continue;
+ }
+
+ gamepad->buttons[key].supported = MG_TRUE;
+ gamepad->buttons[key].current = 0;
+ }
+
+ for (axis = 0; axis < ABS_CNT; axis++) {
+ mg_axis key;
+ float deadzone = 0;
+ if (!isBitSet(axis, absBits)) {
+ continue;
+ }
+
+ switch (axis) {
+ case ABS_HAT0X:
+ gamepad->buttons[MG_BUTTON_DPAD_LEFT].supported = MG_TRUE;
+ gamepad->buttons[MG_BUTTON_DPAD_LEFT].current = 0;
+ gamepad->buttons[MG_BUTTON_DPAD_RIGHT].supported = MG_TRUE;
+ gamepad->buttons[MG_BUTTON_DPAD_RIGHT].current = 0;
+ deadzone = 0;
+ break;
+ case ABS_HAT0Y:
+ gamepad->buttons[MG_BUTTON_DPAD_UP].supported = MG_TRUE;
+ gamepad->buttons[MG_BUTTON_DPAD_UP].current = 0;
+ gamepad->buttons[MG_BUTTON_DPAD_DOWN].supported = MG_TRUE;
+ gamepad->buttons[MG_BUTTON_DPAD_DOWN].current = 0;
+ deadzone = 0;
+ break;
+ case ABS_HAT1X:
+ case ABS_HAT1Y:
+ case ABS_HAT2X:
+ case ABS_HAT2Y:
+ case ABS_HAT3X:
+ case ABS_HAT3Y:
+ deadzone = 0;
+ break;
+ case ABS_Z:
+ gamepad->buttons[MG_BUTTON_LEFT_TRIGGER].supported = MG_TRUE;
+ gamepad->buttons[MG_BUTTON_LEFT_TRIGGER].current = 0;
+
+ gamepad->axes[MG_AXIS_LEFT_TRIGGER].supported = MG_TRUE;
+ gamepad->axes[MG_AXIS_LEFT_TRIGGER].value = 0;
+ deadzone = 0;
+ break;
+ case ABS_RZ:
+ gamepad->buttons[MG_BUTTON_RIGHT_TRIGGER].supported = MG_TRUE;
+ gamepad->buttons[MG_BUTTON_RIGHT_TRIGGER].current = 0;
+
+ gamepad->axes[MG_AXIS_RIGHT_TRIGGER].supported = MG_TRUE;
+ gamepad->axes[MG_AXIS_RIGHT_TRIGGER].value = 0;
+ deadzone = 0;
+ break;
+ default:
+ deadzone = 0.15f;
+ break;
+ }
+
+ key = mg_get_gamepad_axis(gamepad, gamepad->src.absMap[axis]);
+ if (key == MG_AXIS_UNKNOWN) {
+ key = mg_get_gamepad_axis_platform(axis);
+ }
+ if (key == MG_AXIS_UNKNOWN)
+ continue;
+
+ gamepad->axes[key].supported = MG_TRUE;
+ gamepad->axes[key].value = 0;
+ gamepad->axes[key].deadzone = deadzone;
+ }
+
+ #undef isBitSet
+
+ {
+ i32 flags = fcntl(gamepad->src.fd, F_GETFL, 0);
+ fcntl(gamepad->src.fd, F_SETFL, flags | O_NONBLOCK);
+ }
+ gamepad->connected = MG_TRUE;
+
+ return gamepad;
+}
+
+void mg_gamepad_release_platform(mg_gamepad* gamepad) {
+ close(gamepad->src.fd);
+}
+
+
+void mg_gamepads_init_platform(mg_gamepads* gamepads) {
+ struct dirent *dp;
+ DIR *dfd;
+ char full_path[256];
+
+ gamepads->src.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+ if (gamepads->src.inotify > 0) {
+ /* HACK: Register for IN_ATTRIB to get notified when udev is done
+ This works well in practice but the MG_TRUE way is libudev */
+
+ gamepads->src.watch = inotify_add_watch(gamepads->src.inotify, "/dev/input/", IN_CREATE | IN_ATTRIB | IN_DELETE);
+ }
+
+ /* open the directory where all the devices are gonna be */
+ if ((dfd = opendir("/dev/input/")) == NULL) {
+ MG_FPRINTF(stderr, "Can't open /dev/input/\n");
+ return;
+ }
+
+ /* for each file found: */
+ while ((dp = readdir(dfd)) != NULL) {
+ /* get the full path of it (size of path + size of file name) */
+ const char path[] = "/dev/input/";
+ mg_gamepad* gamepad;
+
+ MG_STRNCPY(full_path, path, sizeof(full_path));
+ MG_STRNCPY(&full_path[sizeof(path) - 1], dp->d_name, sizeof(full_path) - sizeof(path));
+ full_path[255] = '\0';
+ gamepad = (mg_gamepad*)mg_linux_setup_gamepad(gamepads, (const char*)full_path);
+ if (gamepad) {
+ mg_handle_connection_event(&gamepads->events, MG_TRUE, gamepad);
+ }
+ }
+
+ closedir(dfd);
+}
+
+mg_bool mg_gamepads_poll_platform(mg_gamepads* gamepads, mg_events* events) {
+ mg_ssize_t offset = 0;
+ char buffer[16384];
+ char full_path[256];
+ const char path[] = "/dev/input/";
+ mg_ssize_t size;
+
+ if (gamepads->src.inotify <= 0)
+ return MG_FALSE;
+
+ size = read(gamepads->src.inotify, buffer, sizeof(buffer));
+
+ while (size > offset) {
+ const struct inotify_event* e = (struct inotify_event*) (buffer + offset);
+
+ offset += (mg_ssize_t)sizeof(struct inotify_event) + e->len;
+
+ if (strncmp(e->name, "event", 5) != 0) {
+ continue;
+ }
+
+ MG_STRNCPY(full_path, path, sizeof(full_path));
+ MG_STRNCPY(&full_path[sizeof(path) - 1], e->name, sizeof(full_path) - sizeof(path));
+ full_path[255] = '\0';
+
+ if (e->mask & (IN_CREATE | IN_ATTRIB)) {
+ mg_gamepad* gamepad = mg_linux_setup_gamepad(gamepads, full_path);
+ if (gamepad) {
+ mg_handle_connection_event(events, MG_TRUE, gamepad);
+ return MG_TRUE;
+ }
+ }
+
+ else if (e->mask & IN_DELETE) {
+ mg_gamepad* cur;
+ for (cur = gamepads->list.head; cur != NULL; cur = cur->next) {
+ if (MG_STRNCMP(cur->src.full_path, full_path, sizeof(cur->src.full_path)) == 0) {
+ mg_handle_connection_event(events, MG_FALSE, cur);
+ mg_gamepad_release(gamepads, cur);
+ return MG_TRUE;
+ }
+ }
+ }
+ }
+
+ return MG_FALSE;
+}
+
+void mg_gamepads_free_platform(mg_gamepads* gamepads) {
+ if (gamepads->src.inotify > 0) {
+ if (gamepads->src.watch > 0)
+ inotify_rm_watch(gamepads->src.inotify, gamepads->src.watch);
+
+ close(gamepads->src.inotify);
+ }
+
+ gamepads->src.inotify = 0;
+ gamepads->src.watch = 0;
+}
+
+mg_bool mg_gamepad_update_platform(mg_gamepad* gamepad, mg_events* events) {
+ mg_bool event_handled = MG_FALSE;
+ struct input_event ev;
+
+ i8 i;
+ if (gamepad->connected == MG_FALSE) return MG_FALSE;
+
+ for (i = 0; i < 2; i++) {
+ mg_button button = MG_BUTTON_LEFT_TRIGGER + i;
+ mg_axis axis = MG_AXIS_LEFT_TRIGGER + i;
+ mg_handle_button_event(events, button, MG_BOOL(gamepad->axes[axis].value >= 0.98f), gamepad);
+ }
+
+ for (i = 0; i < 2; i++) {
+ mg_button button = MG_BUTTON_DPAD_LEFT + (mg_button)(i * 2);
+ mg_button button2 = MG_BUTTON_DPAD_LEFT + 1 + (mg_button)(i * 2);
+ mg_axis axis = (mg_axis)(MG_AXIS_HAT_DPAD_LEFT + i);
+
+ mg_handle_button_event(events, button, gamepad->axes[axis].value < 0, gamepad);
+ mg_handle_button_event(events, button2, gamepad->axes[axis].value > 0, gamepad);
+ }
+
+
+ MG_MEMSET(&ev, 0, sizeof(ev));
+ while (read(gamepad->src.fd, &ev, sizeof(ev)) > 0) {
+ if (ev.type != EV_KEY && ev.type != EV_ABS) continue;
+
+ switch (ev.type) {
+ case EV_KEY: {
+ mg_button btn = mg_get_gamepad_button(gamepad, (u8)(gamepad->src.keyMap[ev.code - BTN_MISC]));
+ if (btn == MG_BUTTON_UNKNOWN) {
+ btn = mg_get_gamepad_button_platform(ev.code);
+ }
+
+ if (btn == MG_BUTTON_UNKNOWN) {
+ break;
+ }
+
+ mg_handle_button_event(events, btn, MG_BOOL(ev.value), gamepad);
+ event_handled = MG_TRUE;
+ break;
+ }
+ case EV_ABS: {
+ float deadzone, event_val;
+
+ mg_axis axis = mg_get_gamepad_axis(gamepad, gamepad->src.absMap[ev.code]);
+ const struct mg_input_absinfo info = gamepad->src.absInfo[ev.code];
+ float normalized = (float)ev.value;
+ const float range = (float)(info.maximum - info.minimum);
+
+ if (axis == MG_AXIS_UNKNOWN)
+ axis = mg_get_gamepad_axis_platform(ev.code);
+ if (axis == MG_AXIS_UNKNOWN) {
+ break;
+ }
+
+ if (range) {
+ /* Normalize to 0.0 -> 1.0 */
+ normalized = (normalized - (float)info.minimum) / range;
+ /* Normalize to -1.0 -> 1.0 */
+ normalized = normalized * 2.0f - 1.0f;
+ }
+
+ deadzone = gamepad->axes[axis].deadzone;
+ event_val = normalized;
+ if (MG_FABS(event_val) < deadzone) {
+ event_val = 0;
+ }
+
+ mg_handle_axis_event(events, axis, event_val, gamepad);
+ event_handled = MG_TRUE;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return event_handled;
+}
+
+mg_button mg_get_gamepad_button_platform(u32 button) {
+ switch (button) {
+ case BTN_WEST:
+ return MG_BUTTON_WEST;
+ case BTN_A:
+ return MG_BUTTON_SOUTH;
+ case BTN_NORTH:
+ return MG_BUTTON_NORTH;
+ case BTN_EAST:
+ return MG_BUTTON_EAST;
+ case BTN_BACK:
+ return MG_BUTTON_BACK;
+ case BTN_MODE:
+ return MG_BUTTON_GUIDE;
+ case BTN_START:
+ return MG_BUTTON_START;
+ case BTN_THUMBL:
+ return MG_BUTTON_LEFT_STICK;
+ case BTN_THUMBR:
+ return MG_BUTTON_RIGHT_STICK;
+ case BTN_TL:
+ return MG_BUTTON_LEFT_SHOULDER;
+ case BTN_DPAD_UP:
+ return MG_BUTTON_DPAD_UP;
+ case BTN_DPAD_DOWN:
+ return MG_BUTTON_DPAD_DOWN;
+ case BTN_DPAD_LEFT:
+ return MG_BUTTON_DPAD_LEFT;
+ case BTN_DPAD_RIGHT:
+ return MG_BUTTON_DPAD_RIGHT;
+ case BTN_TR:
+ return MG_BUTTON_RIGHT_SHOULDER;
+ case BTN_TOUCH:
+ return MG_BUTTON_TOUCHPAD;
+ case BTN_TRIGGER_HAPPY4:
+ return MG_BUTTON_RIGHT_PADDLE1;
+ case BTN_TRIGGER_HAPPY6:
+ return MG_BUTTON_RIGHT_PADDLE2;
+ case BTN_TRIGGER_HAPPY7:
+ return MG_BUTTON_LEFT_PADDLE1;
+ case BTN_TRIGGER_HAPPY8:
+ return MG_BUTTON_LEFT_PADDLE2;
+
+ case BTN_SELECT:
+ return MG_BUTTON_MISC1;
+ case BTN_TRIGGER_HAPPY2:
+ return MG_BUTTON_MISC2;
+ case BTN_TRIGGER_HAPPY3:
+ return MG_BUTTON_MISC3;
+ case BTN_TRIGGER_HAPPY9:
+ return MG_BUTTON_MISC5;
+ case BTN_TRIGGER_HAPPY10:
+ return MG_BUTTON_MISC6;
+
+ case BTN_TRIGGER: return MG_BUTTON_WEST;
+ case BTN_THUMB: return MG_BUTTON_SOUTH;
+ case BTN_THUMB2: return MG_BUTTON_EAST;
+ case BTN_TOP: return MG_BUTTON_NORTH;
+ case BTN_TOP2: return MG_BUTTON_START;
+ case BTN_PINKIE: return MG_BUTTON_LEFT_SHOULDER;
+ case BTN_BASE: return MG_BUTTON_RIGHT_SHOULDER;
+ case BTN_BASE2: return MG_BUTTON_BACK;
+
+ case BTN_BASE3: return MG_BUTTON_BACK;
+ case BTN_BASE4: return MG_BUTTON_START;
+ case BTN_BASE5: return MG_BUTTON_START;
+ case BTN_BASE6: return MG_BUTTON_RIGHT_STICK;
+ default:
+ return MG_BUTTON_UNKNOWN;
+ }
+ return MG_BUTTON_UNKNOWN;
+}
+
+mg_axis mg_get_gamepad_axis_platform(u32 axis) {
+ switch (axis) {
+ case ABS_X:
+ return MG_AXIS_LEFT_X;
+ case ABS_Y:
+ return MG_AXIS_LEFT_Y;
+ case ABS_Z:
+ return MG_AXIS_LEFT_TRIGGER;
+ case ABS_RX:
+ return MG_AXIS_RIGHT_X;
+ case ABS_RY:
+ return MG_AXIS_RIGHT_Y;
+ case ABS_RZ:
+ return MG_AXIS_RIGHT_TRIGGER;
+ case ABS_THROTTLE:
+ return MG_AXIS_THROTTLE;
+ case ABS_RUDDER:
+ return MG_AXIS_RUDDER;
+ case ABS_WHEEL:
+ return MG_AXIS_WHEEL;
+ case ABS_GAS:
+ return MG_AXIS_GAS;
+ case ABS_BRAKE:
+ return MG_AXIS_BRAKE;
+ case ABS_HAT0X:
+ return MG_AXIS_HAT_DPAD_LEFT_RIGHT;
+ case ABS_HAT0Y:
+ return MG_AXIS_HAT_DPAD_UP_DOWN;
+ case ABS_HAT1X:
+ return MG_AXIS_HAT1X;
+ case ABS_HAT1Y:
+ return MG_AXIS_HAT1Y;
+ case ABS_HAT2X:
+ return MG_AXIS_HAT2X;
+ case ABS_HAT2Y:
+ return MG_AXIS_HAT2Y;
+ case ABS_HAT3X:
+ return MG_AXIS_HAT3X;
+ case ABS_HAT3Y:
+ return MG_AXIS_HAT3Y;
+ case ABS_PRESSURE:
+ return MG_AXIS_PRESSURE;
+ case ABS_DISTANCE:
+ return MG_AXIS_DISTANCE;
+ case ABS_TILT_X:
+ return MG_AXIS_TILT_X;
+ case ABS_TILT_Y:
+ return MG_AXIS_TILT_Y;
+ case ABS_TOOL_WIDTH:
+ return MG_AXIS_TOOL_WIDTH;
+ case ABS_VOLUME:
+ return MG_AXIS_VOLUME;
+ case ABS_PROFILE:
+ return MG_AXIS_PROFILE;
+ case ABS_MISC:
+ return MG_AXIS_MISC;
+ default:
+ return MG_AXIS_UNKNOWN;
+ }
+
+ return MG_AXIS_UNKNOWN;
+}
+#endif /* MG_LINUX */
+
+/*
+ * start of windows
+ */
+
+#if defined(MG_WINDOWS)
+
+#ifdef __cplusplus
+#define MG_REF_GUID(g) *(g)
+#else
+#define MG_REF_GUID(g) (g)
+#endif
+
+#include
+#include
+
+#ifndef DIDFT_OPTIONAL
+#define DIDFT_OPTIONAL 0x80000000
+#endif
+
+typedef void (*mg_proc)(void); /* function pointer equivalent of void* */
+
+MG_API void mg_xinput_fetch_gamepads(mg_gamepads* gamepads, mg_events* events);
+
+mg_gamepad* mg_xinput_list[XUSER_MAX_COUNT];
+typedef DWORD (* PFN_XInputGetState)(DWORD,XINPUT_STATE*);
+typedef DWORD (* PFN_XInputGetCapabilities)(DWORD,DWORD,XINPUT_CAPABILITIES*);
+typedef DWORD (* PFN_XInputGetKeystroke)(DWORD, DWORD, PXINPUT_KEYSTROKE);
+typedef HRESULT (WINAPI * PFN_DirectInput8Create)(HINSTANCE,DWORD,REFIID,LPVOID*,LPUNKNOWN);
+typedef HRESULT (WINAPI * PFN_GameInputCreate)(void* gameinput);
+
+HINSTANCE mg_gameinput_dll = NULL;
+HINSTANCE mg_xinput_dll = NULL;
+HINSTANCE mg_dinput_dll = NULL;
+
+PFN_GameInputCreate GameInputCreateSrc = NULL;
+
+PFN_XInputGetState XInputGetStateSrc = NULL;
+PFN_XInputGetKeystroke XInputGetKeystrokeSrc = NULL;
+PFN_XInputGetCapabilities XInputGetCapabilitiesSrc = NULL;
+PFN_DirectInput8Create DInput8CreateSrc = NULL;
+
+const GUID MG_IID_IDirectInput8W =
+ {0xbf798031,0x483a,0x4da2,{0xaa,0x99,0x5d,0x64,0xed,0x36,0x97,0x00}};
+const GUID MG_GUID_XAxis =
+ {0xa36d02e0,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}};
+const GUID MG_GUID_YAxis =
+ {0xa36d02e1,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}};
+const GUID MG_GUID_ZAxis =
+ {0xa36d02e2,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}};
+const GUID MG_GUID_RxAxis =
+ {0xa36d02f4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}};
+const GUID MG_GUID_RyAxis =
+ {0xa36d02f5,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}};
+const GUID MG_GUID_RzAxis =
+ {0xa36d02e3,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}};
+const GUID MG_GUID_Slider =
+ {0xa36d02e4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}};
+const GUID MG_GUID_POV =
+ {0xa36d02f2,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}};
+
+static DIOBJECTDATAFORMAT mg_objectDataFormats[] = {
+ { &MG_GUID_XAxis,DIJOFS_X,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
+ { &MG_GUID_YAxis,DIJOFS_Y,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
+ { &MG_GUID_ZAxis,DIJOFS_Z,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
+ { &MG_GUID_RxAxis,DIJOFS_RX,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
+ { &MG_GUID_RyAxis,DIJOFS_RY,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
+ { &MG_GUID_RzAxis,DIJOFS_RZ,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
+ { &MG_GUID_Slider,DIJOFS_SLIDER(0),DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
+ { &MG_GUID_Slider,DIJOFS_SLIDER(1),DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION },
+ { &MG_GUID_POV,DIJOFS_POV(0),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { &MG_GUID_POV,DIJOFS_POV(1),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { &MG_GUID_POV,DIJOFS_POV(2),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { &MG_GUID_POV,DIJOFS_POV(3),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(0),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(1),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(2),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(3),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(4),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(5),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(6),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(7),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(8),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(9),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(10),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(11),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(12),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(13),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(14),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(15),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(16),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(17),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(18),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(19),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(20),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(21),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(22),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(23),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(24),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(25),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(26),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(27),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(28),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(29),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(30),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+ { NULL,DIJOFS_BUTTON(31),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 },
+};
+
+const DIDATAFORMAT mg_dataFormat = {
+ sizeof(DIDATAFORMAT),
+ sizeof(DIOBJECTDATAFORMAT),
+ DIDFT_ABSAXIS,
+ sizeof(DIJOYSTATE),
+ sizeof(mg_objectDataFormats) / sizeof(DIOBJECTDATAFORMAT),
+ mg_objectDataFormats
+};
+
+mg_bool mg_supportsXInput(const GUID* guid) {
+ RAWINPUTDEVICELIST* list;
+ unsigned int count = 0;
+ mg_size_t i;
+
+ if (mg_xinput_dll == NULL) {
+ return MG_FALSE;
+ }
+
+ if (GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST)) != 0)
+ return MG_FALSE;
+
+ list = (RAWINPUTDEVICELIST*)malloc(count * sizeof(RAWINPUTDEVICELIST));
+ MG_MEMSET(list, 0, count * sizeof(RAWINPUTDEVICELIST));
+
+ if ((int)GetRawInputDeviceList(list, &count, sizeof(RAWINPUTDEVICELIST)) == -1) {
+ free(list);
+ return MG_FALSE;
+ }
+
+ for (i = 0; i < count; i++) {
+ RID_DEVICE_INFO rdi = {0};
+ char name[256];
+ UINT size = sizeof(rdi);
+
+ rdi.cbSize = sizeof(rdi);
+
+ if (list[i].dwType != RIM_TYPEHID)
+ continue;
+
+ if ((int)GetRawInputDeviceInfoA(list[i].hDevice, RIDI_DEVICEINFO, &rdi, &size) == -1) {
+ continue;
+ }
+
+ if (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) != (LONG) guid->Data1)
+ continue;
+
+ MG_MEMSET(name, 0, sizeof(name));
+ size = sizeof(name);
+
+ if ((int)GetRawInputDeviceInfoA(list[i].hDevice, RIDI_DEVICENAME, name, &size) == -1) {
+ break;
+ }
+
+ name[sizeof(name) - 1] = '\0';
+ if (strstr((char*)name, "IG_")) {
+ free(list);
+ return MG_TRUE;
+ }
+ }
+
+ free(list);
+ return MG_FALSE;
+}
+
+#include
+
+BOOL CALLBACK DirectInputEnumDevicesCallback(LPCDIDEVICEINSTANCE inst, LPVOID userData) {
+ mg_gamepads* gamepads = (mg_gamepads*)userData;
+ mg_gamepad* gamepad;
+ u32 i;
+ DIDEVCAPS caps;
+ DIPROPDWORD dipd;
+ /* avoid clones */
+
+ if (mg_supportsXInput(&inst->guidProduct)) {
+ return DIENUM_CONTINUE;
+ }
+
+ gamepad = mg_gamepad_find(gamepads);
+ if (gamepad == NULL) return FALSE;
+
+ gamepad->src.device = NULL;
+
+ if (FAILED(IDirectInput8_CreateDevice((IDirectInput8*)gamepads->src.dinput, MG_REF_GUID(&inst->guidInstance), (IDirectInputDevice8**)&gamepad->src.device, NULL))) {
+ mg_gamepad_release(gamepads, gamepad);
+ return DIENUM_CONTINUE;
+ }
+
+
+ if (FAILED(IDirectInputDevice8_SetDataFormat((IDirectInputDevice8*)gamepad->src.device, &mg_dataFormat ))) {
+ mg_gamepad_release(gamepads, gamepad);
+ return DIENUM_CONTINUE;
+ }
+
+ MG_MEMSET(&caps, 0, sizeof(caps));
+ caps.dwSize = sizeof(DIDEVCAPS);
+
+ IDirectInputDevice8_GetCapabilities((IDirectInputDevice8*)gamepad->src.device, &caps);
+
+ MG_MEMSET(&dipd, 0, sizeof(dipd));
+ dipd.diph.dwSize = sizeof(dipd);
+ dipd.diph.dwHeaderSize = sizeof(dipd.diph);
+ dipd.diph.dwHow = DIPH_DEVICE;
+ dipd.dwData = DIPROPAXISMODE_ABS;
+
+ if (FAILED(IDirectInputDevice8_SetProperty((IDirectInputDevice8*)gamepad->src.device, DIPROP_AXISMODE, &dipd.diph))) {
+ mg_gamepad_release(gamepads, gamepad);
+ return DIENUM_CONTINUE;
+ }
+
+ if (!WideCharToMultiByte(CP_UTF8, 0, (const wchar_t*)inst->tszInstanceName, -1, gamepad->name, sizeof(gamepad->name), NULL, NULL)) {
+ mg_gamepad_release(gamepads, gamepad);
+ return DIENUM_STOP;
+ }
+
+ if (memcmp(&inst->guidProduct.Data4[2], "PIDVID", 6) == 0) {
+ MG_SPRINTF(gamepad->guid, "03000000%02x%02x0000%02x%02x000000000000",
+ (uint8_t) inst->guidProduct.Data1,
+ (uint8_t) (inst->guidProduct.Data1 >> 8),
+ (uint8_t) (inst->guidProduct.Data1 >> 16),
+ (uint8_t) (inst->guidProduct.Data1 >> 24));
+ } else {
+ MG_SPRINTF(gamepad->guid, "05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00",
+ gamepad->name[0], gamepad->name[1], gamepad->name[2], gamepad->name[3],
+ gamepad->name[4], gamepad->name[5], gamepad->name[6], gamepad->name[7],
+ gamepad->name[8], gamepad->name[9], gamepad->name[10]);
+ }
+
+
+ gamepad->mapping = mg_gamepad_find_valid_mapping(gamepad);
+
+{
+ HRESULT result;
+ DIJOYSTATE state;
+ MG_MEMSET(&state, 0, sizeof(state));
+
+ IDirectInputDevice8_Acquire((IDirectInputDevice8*)gamepad->src.device);
+ IDirectInputDevice8_Poll((IDirectInputDevice8*)gamepad->src.device);
+
+ result = IDirectInputDevice8_GetDeviceState((IDirectInputDevice8*)gamepad->src.device, sizeof(state), &state);
+ if (FAILED(result)) {
+ mg_gamepad_release(gamepads, gamepad);
+ return DIENUM_CONTINUE;
+ }
+}
+
+ for (i = 0; i < caps.dwButtons; i++) {
+ mg_button key = mg_get_gamepad_button(gamepad, (u8)i);
+ if (key == MG_BUTTON_UNKNOWN) {
+ key = mg_get_gamepad_button_platform(i);
+ if (key == MG_BUTTON_UNKNOWN) continue;
+ }
+
+ if (gamepad->buttons[key].supported)
+ continue;
+
+ gamepad->buttons[key].supported = MG_TRUE;
+ gamepad->buttons[key].current = 0;
+ }
+
+ for (i = 0; i < caps.dwAxes; i++) {
+ mg_axis key = mg_get_gamepad_axis(gamepad, (u8)i);
+ if (key == MG_AXIS_UNKNOWN) {
+ key = mg_get_gamepad_axis_platform(i);
+ if (key == MG_AXIS_UNKNOWN) continue;
+ }
+
+ if (gamepad->axes[key].supported)
+ continue;
+
+ gamepad->axes[key].supported = MG_TRUE;
+ gamepad->axes[key].value = 0;
+ }
+
+ if (caps.dwPOVs) {
+ const mg_axis povs[2] = {
+ MG_AXIS_HAT_DPAD_UP_DOWN,
+ MG_AXIS_HAT_DPAD_LEFT_RIGHT,
+ };
+
+ for (i = 0; i < 2; i++) {
+ mg_axis key = povs[i];
+ gamepad->axes[key].supported = MG_TRUE;
+ gamepad->axes[key].value = 0;
+ }
+ }
+
+ gamepad->connected = MG_TRUE;
+ mg_handle_connection_event(&gamepads->events, MG_TRUE, gamepad);
+
+ return DIENUM_CONTINUE;
+}
+
+#include
+
+static LRESULT CALLBACK mg_winproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
+ mg_gamepads* gamepads = (mg_gamepads*)GetPropW(hWnd, L"gamepads");
+ if (gamepads == NULL) return DefWindowProcW(hWnd, message, wParam, lParam);
+
+ switch (message) {
+ case WM_DEVICECHANGE: {
+ if (gamepads->src.dinput) {
+ IDirectInput8_EnumDevices((IDirectInput8*)gamepads->src.dinput,
+ DI8DEVCLASS_GAMECTRL,
+ DirectInputEnumDevicesCallback,
+ (void*)gamepads,
+ DIEDFL_ATTACHEDONLY);
+ }
+
+ mg_xinput_fetch_gamepads(gamepads, &gamepads->events);
+ break;
+ }
+ }
+
+ return DefWindowProcW(hWnd, message, wParam, lParam);
+}
+
+
+void mg_gamepads_init_platform(mg_gamepads* gamepads) {
+ HINSTANCE hInstance = GetModuleHandle(0);
+ WNDCLASSW Class;
+
+ if (mg_gameinput_dll == NULL) {
+ /* load gameinput dll and functions (if it's available) */
+ static const char* names[] = {"gameinput.lib", "gameinput.dll"};
+
+ uint32_t i;
+ for (i = 0; i < sizeof(names) / sizeof(const char*); i++) {
+ mg_gameinput_dll = LoadLibraryA(names[i]);
+ if (mg_gameinput_dll) {
+ GameInputCreateSrc = (PFN_GameInputCreate)(mg_proc)GetProcAddress(mg_gameinput_dll, "GameInputCreate");
+ }
+ }
+
+
+ if (mg_gameinput_dll && GameInputCreateSrc) {
+ /*HRESULT hr = GameInputCreateSrc(&gamepads->src.ginput);
+ MG_UNUSED(hr); TODO */
+ }
+ }
+
+ /* init global gamepads->src.data */
+ if (mg_xinput_dll == NULL) {
+ /* load xinput dll and functions (if it's available) */
+ static const char* names[] = {"xinput0_4.dll", "xinput9_1_0.dll", "xinput1_2.dll", "xinput1_1.dll"};
+
+ uint32_t i;
+ for (i = 0; i < sizeof(names) / sizeof(const char*) && (XInputGetStateSrc == NULL || XInputGetKeystrokeSrc != NULL); i++) {
+ mg_xinput_dll = LoadLibraryA(names[i]);
+ if (mg_xinput_dll) {
+ XInputGetStateSrc = (PFN_XInputGetState)(mg_proc)GetProcAddress(mg_xinput_dll, "XInputGetState");
+ XInputGetKeystrokeSrc = (PFN_XInputGetKeystroke)(mg_proc)GetProcAddress(mg_xinput_dll, "XInputGetKeystroke");
+ XInputGetCapabilitiesSrc = (PFN_XInputGetCapabilities)(mg_proc)GetProcAddress(mg_xinput_dll, "XInputGetCapabilities");
+ }
+ }
+
+ if (mg_xinput_dll) {
+ mg_xinput_fetch_gamepads(gamepads, &gamepads->events);
+ }
+ }
+
+ if (mg_dinput_dll == NULL && DInput8CreateSrc == NULL) {
+ /* load directinput dll and functions */
+ mg_dinput_dll = LoadLibraryA("dinput8.dll");
+ if (mg_dinput_dll)
+ DInput8CreateSrc = (PFN_DirectInput8Create)(mg_proc)GetProcAddress(mg_dinput_dll, "DirectInput8Create");
+ }
+
+ if (DInput8CreateSrc) {
+ if (FAILED(DInput8CreateSrc(hInstance,
+ DIRECTINPUT_VERSION,
+ MG_REF_GUID(&MG_IID_IDirectInput8W),
+ (void**) &gamepads->src.dinput,
+ NULL)) ||
+ FAILED(IDirectInput8_EnumDevices((IDirectInput8*)gamepads->src.dinput,
+ DI8DEVCLASS_GAMECTRL,
+ DirectInputEnumDevicesCallback,
+ (void*)gamepads,
+ DIEDFL_ALLDEVICES))) {
+ mg_dinput_dll = NULL;
+ }
+ }
+
+ MG_MEMSET(&Class, 0, sizeof(Class));
+
+ Class.hInstance = hInstance;
+ Class.lpfnWndProc = mg_winproc;
+ Class.cbClsExtra = sizeof(mg_gamepads*);
+ Class.lpszClassName = L"minigamepadclass";
+
+ RegisterClassW(&Class);
+
+ gamepads->src.dummy_win = CreateWindowW(Class.lpszClassName, (wchar_t*)"", 0, 0, 0, 0, 0, 0, 0, hInstance, 0);
+
+ SetPropW((HWND)gamepads->src.dummy_win, L"gamepads", gamepads);
+}
+
+#ifndef XINPUT_DEVSUBTYPE_FLIGHT_STICK
+ /* MINGW PLEASE FIX THIS */
+ #define XINPUT_DEVSUBTYPE_FLIGHT_STICK 0x04
+#endif
+
+static const char* mg_xinput_gamepad_name(const XINPUT_CAPABILITIES xic) {
+ switch (xic.SubType) {
+ case XINPUT_DEVSUBTYPE_WHEEL:
+ return "XInput Wheel";
+ case XINPUT_DEVSUBTYPE_ARCADE_STICK:
+ return "XInput Arcade Stick";
+ case XINPUT_DEVSUBTYPE_FLIGHT_STICK:
+ return "XInput Flight Stick";
+ case XINPUT_DEVSUBTYPE_DANCE_PAD:
+ return "XInput Dance Pad";
+ case XINPUT_DEVSUBTYPE_GUITAR:
+ return "XInput Guitar";
+ case XINPUT_DEVSUBTYPE_DRUM_KIT:
+ return "XInput Drum Kit";
+ case XINPUT_DEVSUBTYPE_GAMEPAD: {
+ if (xic.Flags & XINPUT_CAPS_WIRELESS)
+ return "Wireless Xbox Controller";
+ else
+ return "Xbox Controller";
+ }
+ }
+
+ return "Unknown XInput Device";
+}
+
+void mg_xinput_fetch_gamepads(mg_gamepads* gamepads, mg_events* events) {
+ DWORD dwResult, i;
+ if (mg_xinput_dll == NULL) return;
+
+ for (i = 0; i < XUSER_MAX_COUNT; i++) {
+ mg_gamepad* gamepad = mg_xinput_list[i];
+ XINPUT_STATE state;
+ mg_button button;
+ mg_axis axis;
+ char* name;
+ XINPUT_CAPABILITIES xic;
+
+ MG_MEMSET(&state, 0, sizeof(state));
+
+ dwResult = XInputGetStateSrc(i, &state);
+
+ if ((dwResult == ERROR_SUCCESS && gamepad) ||
+ (dwResult != ERROR_SUCCESS && gamepad == NULL)
+ )
+ continue;
+
+ if (dwResult != ERROR_SUCCESS) {
+ gamepad->src.xinput_index = 0;
+ mg_xinput_list[i] = NULL;
+ mg_handle_connection_event(events, MG_FALSE, gamepad);
+ mg_gamepad_release(gamepads, gamepad);
+
+ continue;
+ }
+
+ if (XInputGetCapabilitiesSrc(i, 0, &xic) != ERROR_SUCCESS)
+ continue;
+
+ gamepad = mg_gamepad_find(gamepads);
+
+ gamepad->src.xinput_index = i + 1;
+ MG_SPRINTF(gamepad->guid, "78696e707574%02x000000000000000000", xic.SubType & 0xff);
+
+ for (button = 0; button < MG_BUTTON_MISC1; button++) {
+ if (button == MG_BUTTON_GUIDE) continue;
+
+ gamepad->buttons[button].supported = MG_TRUE;
+ gamepad->buttons[button].current = MG_FALSE;
+ gamepad->buttons[button].prev = MG_FALSE;
+ }
+
+ for (axis = 0; axis < MG_AXIS_HAT_DPAD_LEFT_RIGHT; axis++) {
+ gamepad->axes[axis].value = 0;
+ gamepad->axes[axis].supported = MG_TRUE;
+ }
+
+ name = (char*)mg_xinput_gamepad_name(xic);
+ MG_STRNCPY(gamepad->name, name, sizeof(gamepad->name));
+
+ gamepad->connected = MG_TRUE;
+ mg_xinput_list[i] = gamepad;
+ mg_handle_connection_event(events, MG_TRUE, gamepad);
+ }
+}
+
+mg_bool mg_gamepads_poll_platform(mg_gamepads* gamepads, mg_events* events) {
+ mg_bool out = MG_FALSE;
+ MSG msg;
+
+ MG_UNUSED(gamepads); MG_UNUSED(events);
+
+ while (PeekMessageA(&msg, NULL, 0u, 0u, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessageA(&msg);
+ }
+
+ return out;
+}
+
+void mg_gamepads_free_platform(mg_gamepads* gamepads) {
+ DestroyWindow((HWND)gamepads->src.dummy_win);
+
+ if (mg_xinput_dll) {
+ FreeLibrary(mg_xinput_dll);
+ mg_xinput_dll = NULL;
+ }
+
+ if (mg_dinput_dll) {
+ if (gamepads->src.dinput)
+ IDirectInput8_Release((IDirectInput8*)gamepads->src.dinput);
+
+ FreeLibrary(mg_dinput_dll);
+ }
+}
+
+
+const u32 mg_xinput_map[MG_BUTTON_MISC1] = {
+ XINPUT_GAMEPAD_A,
+ XINPUT_GAMEPAD_B,
+ XINPUT_GAMEPAD_X,
+ XINPUT_GAMEPAD_Y,
+ XINPUT_GAMEPAD_BACK,
+ 0,
+ XINPUT_GAMEPAD_START,
+ XINPUT_GAMEPAD_LEFT_THUMB,
+ XINPUT_GAMEPAD_RIGHT_THUMB,
+ XINPUT_GAMEPAD_LEFT_SHOULDER,
+ XINPUT_GAMEPAD_RIGHT_SHOULDER,
+ XINPUT_GAMEPAD_DPAD_LEFT,
+ XINPUT_GAMEPAD_DPAD_RIGHT,
+ XINPUT_GAMEPAD_DPAD_UP,
+ XINPUT_GAMEPAD_DPAD_DOWN,
+ 0, 0
+};
+
+mg_bool mg_gamepad_update_platform(mg_gamepad* gamepad, mg_events* events) {
+
+ if (gamepad->connected == MG_FALSE) {
+/* mg_gamepad_release(gamepad); */
+ return MG_FALSE;
+ }
+
+ if (gamepad->src.xinput_index) {
+ DWORD dwResult;
+ XINPUT_STATE state;
+ DWORD i = gamepad->src.xinput_index - 1;
+ mg_button button;
+
+ if (gamepad != mg_xinput_list[i]) {
+ gamepad->connected = MG_FALSE;
+ return MG_TRUE;
+ }
+
+ MG_MEMSET(&state, 0, sizeof(state));
+ dwResult = XInputGetStateSrc(i, &state);
+ if (dwResult != ERROR_SUCCESS) {
+ gamepad->connected = MG_FALSE;
+ mg_handle_connection_event(events, MG_FALSE, gamepad);
+ return MG_TRUE;
+ }
+
+ for (button = 0; button < MG_BUTTON_MISC1; button++) {
+ u32 btn = mg_xinput_map[button];
+ mg_bool btn_state;
+
+ if (btn == 0) continue;
+ btn_state = MG_BOOL((state.Gamepad.wButtons & btn));
+
+ mg_handle_button_event(events, button, btn_state, gamepad);
+ }
+
+ mg_handle_button_event(events, MG_BUTTON_LEFT_TRIGGER, MG_BOOL(gamepad->axes[MG_AXIS_LEFT_TRIGGER].value > 0), gamepad);
+ mg_handle_button_event(events, MG_BUTTON_RIGHT_TRIGGER, MG_BOOL(gamepad->axes[MG_AXIS_RIGHT_TRIGGER].value > 0), gamepad);
+
+ mg_handle_axis_event(events, MG_AXIS_LEFT_TRIGGER, (state.Gamepad.bLeftTrigger / 127.5f - 1.0f), gamepad);
+ mg_handle_axis_event(events, MG_AXIS_RIGHT_TRIGGER, (state.Gamepad.bRightTrigger / 127.5f - 1.0f), gamepad);
+
+ mg_handle_axis_event(events, MG_AXIS_LEFT_X, (state.Gamepad.sThumbLX + 0.5f) / 32767.5f, gamepad);
+ mg_handle_axis_event(events, MG_AXIS_LEFT_Y, -(state.Gamepad.sThumbLY + 0.5f) / 32767.5f, gamepad);
+
+ mg_handle_axis_event(events, MG_AXIS_RIGHT_X, (state.Gamepad.sThumbRX + 0.5f) / 32767.5f, gamepad);
+ mg_handle_axis_event(events, MG_AXIS_RIGHT_Y, -(state.Gamepad.sThumbRY + 0.5f) / 32767.5f, gamepad);
+ }
+
+ if (gamepad->src.device) {
+ u32 i;
+ DIDEVCAPS caps;
+ DIJOYSTATE state;
+ HRESULT result;
+ LONG axes_state[6];
+
+ MG_MEMSET(&caps, 0, sizeof(caps));
+ caps.dwSize = sizeof(DIDEVCAPS);
+
+ IDirectInputDevice8_GetCapabilities((IDirectInputDevice8*)gamepad->src.device, &caps);
+ IDirectInputDevice8_Poll((IDirectInputDevice8*)gamepad->src.device);
+ result = IDirectInputDevice8_GetDeviceState((IDirectInputDevice8*)gamepad->src.device, sizeof(state), &state);
+ if (result == DIERR_NOTACQUIRED || result == DIERR_INPUTLOST) {
+ IDirectInputDevice8_Acquire((IDirectInputDevice8*)gamepad->src.device);
+ IDirectInputDevice8_Poll((IDirectInputDevice8*)gamepad->src.device);
+
+ result = IDirectInputDevice8_GetDeviceState((IDirectInputDevice8*)gamepad->src.device, sizeof(state), &state);
+ }
+
+ if (FAILED(result)) {
+ gamepad->connected = MG_FALSE;
+ mg_handle_connection_event(events, MG_FALSE, gamepad);
+ return MG_FALSE;
+ }
+
+ for (i = 0; i < caps.dwButtons; i++) {
+ mg_button key = mg_get_gamepad_button(gamepad, (u8)i);
+ if (key == MG_BUTTON_UNKNOWN) {
+ key = mg_get_gamepad_button_platform(i);
+ if (key == MG_BUTTON_UNKNOWN) continue;
+ }
+
+ mg_handle_button_event(events, key, MG_BOOL(state.rgbButtons[i]), gamepad);
+ }
+
+ memcpy(&axes_state, &state, sizeof(axes_state));
+
+ for (i = 0; i < 6; i++) {
+ float value;
+ mg_axis key = mg_get_gamepad_axis(gamepad, (u8)i);
+ if (key == MG_AXIS_UNKNOWN) {
+ key = mg_get_gamepad_axis_platform(i);
+ if (key == MG_AXIS_UNKNOWN) continue;
+ }
+
+ /* NOTE: this doesn't work with triggers because triggers are combined into one input
+ * TODO: fix this (or just use xinput)
+ * */
+ value = (((float)axes_state[i] + 0.5f) / 32767.5f) - 1.0f;
+ mg_handle_axis_event(events, key, value, gamepad);
+ }
+
+ if (caps.dwPOVs) {
+ DWORD pov = state.rgdwPOV[0];
+
+ if (pov != 0xFFFF) {
+ float angle = (float)pov / (45.0f * DI_DEGREES);
+ i32 x = 0, y = 0;
+
+ switch ((u32)angle) {
+ case 0: /* up */ x = 0; y = -1; break;
+ case 1: /* up-right */ x = 1; y = -1; break;
+ case 2: /* right */ x = 1; y = 0; break;
+ case 3: /* down-right */ x = 1; y = 1; break;
+ case 4: /* down */ x = 0; y = 1; break;
+ case 5: /* down-left */ x = -1; y = 1; break;
+ case 6: /* left */ x = -1; y = 0; break;
+ case 7: /* up-left */ x = -1; y = -1; break;
+ }
+
+ mg_handle_button_event(events, MG_BUTTON_DPAD_LEFT, x < 0, gamepad);
+ mg_handle_button_event(events, MG_BUTTON_DPAD_RIGHT, x > 0, gamepad);
+ mg_handle_button_event(events, MG_BUTTON_DPAD_UP, y < 0, gamepad);
+ mg_handle_button_event(events, MG_BUTTON_DPAD_DOWN, y > 0, gamepad);
+ } else {
+ mg_handle_axis_event(events, MG_AXIS_HAT_DPAD_LEFT_RIGHT, (float)0.0f, gamepad);
+ mg_handle_axis_event(events, MG_AXIS_HAT_DPAD_UP_DOWN, (float)0.0f, gamepad);
+ }
+ }
+ }
+
+ return MG_FALSE;
+}
+
+void mg_gamepad_release_platform(mg_gamepad* gamepad) {
+ if (gamepad->src.device) {
+ IDirectInputDevice8_Release((IDirectInputDevice8*)gamepad->src.device);
+ }
+
+ if (gamepad->src.xinput_index) {
+ mg_xinput_list[gamepad->src.xinput_index - 1] = NULL;
+ }
+}
+
+mg_button mg_get_gamepad_button_platform(u32 button) {
+ /* TODO */
+ switch (button) {
+ default: break;
+ }
+ return MG_BUTTON_UNKNOWN;
+}
+
+mg_axis mg_get_gamepad_axis_platform(u32 axis) {
+ /* TODO */
+ switch (axis) {
+ case 2: return MG_AXIS_LEFT_TRIGGER;
+ case 5: return MG_AXIS_RIGHT_TRIGGER;
+ default: break;
+ }
+
+ return MG_AXIS_UNKNOWN;
+}
+#endif /* MG_WINDOWS */
+
+/*
+ * start of macos
+ */
+
+#if defined(MG_MACOS)
+#include
+#include
+
+void mg_osx_input_value_changed_callback(void *context, IOReturn result, void *sender, IOHIDValueRef value) {
+ mg_gamepad* gamepad = (mg_gamepad*)context;
+
+ IOHIDElementRef element = IOHIDValueGetElement(value);
+
+ IOHIDDeviceRef device = IOHIDElementGetDevice(element);
+
+ uint32_t usagePage = IOHIDElementGetUsagePage(element);
+ uint32_t usage = IOHIDElementGetUsage(element);
+
+ CFIndex intValue = IOHIDValueGetIntegerValue(value);
+ MG_UNUSED(result); MG_UNUSED(sender);
+
+ if ((IOHIDDeviceRef)gamepad->src.device != device)
+ return;
+
+ switch (usagePage) {
+ case kHIDPage_Button: {
+ mg_button btn = mg_get_gamepad_button(gamepad, (u8)usage);
+ if (btn == 0)
+ btn = mg_get_gamepad_button_platform(usage);
+ if (btn == 0)
+ break;
+
+ mg_handle_button_event((mg_events*)gamepad->src.events, btn, MG_BOOL(intValue), gamepad);
+ break;
+ }
+ case kHIDPage_GenericDesktop: {
+ CFIndex logicalMin = IOHIDElementGetLogicalMin(element);
+ CFIndex logicalMax = IOHIDElementGetLogicalMax(element);
+ mg_axis btn = mg_get_gamepad_axis(gamepad, (u8)usage);
+ if (btn == 0)
+ btn = mg_get_gamepad_axis_platform(usage);
+ if (btn == 0)
+ break;
+
+ if (logicalMax <= logicalMin) return;
+ if (intValue < logicalMin) intValue = logicalMin;
+ if (intValue > logicalMax) intValue = logicalMax;
+
+ mg_handle_axis_event((mg_events*)gamepad->src.events, btn, (-1.0f + ((intValue - logicalMin) * 2.0f) / (float)(logicalMax - logicalMin)), gamepad);
+ }
+ }
+}
+
+
+void mg_osx_device_added_callback(void* context, IOReturn result, void *sender, IOHIDDeviceRef device) {
+ mg_gamepad* gamepad;
+ mg_gamepads* gamepads = (mg_gamepads*)context;
+
+ CFIndex i;
+ CFArrayRef elements;
+ CFTypeRef property;
+ u32 vendor = 0, product = 0, version = 0;
+ CFStringRef deviceName;
+ CFTypeRef usageRef = (CFTypeRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDPrimaryUsageKey));
+ int usage = 0;
+ if (usageRef)
+ CFNumberGetValue((CFNumberRef)usageRef, (CFNumberType)kCFNumberIntType, (void*)&usage);
+
+ MG_UNUSED(context); MG_UNUSED(result); MG_UNUSED(sender);
+ if (usage != kHIDUsage_GD_Joystick && usage != kHIDUsage_GD_GamePad && usage != kHIDUsage_GD_MultiAxisController) {
+ return;
+ }
+
+ elements = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone);
+ if (elements == NULL)
+ return;
+
+ gamepad = mg_gamepad_find(gamepads);
+ if (gamepad == NULL) {
+ return;
+ }
+
+ IOHIDDeviceRegisterInputValueCallback(device, mg_osx_input_value_changed_callback, gamepad);
+
+ deviceName = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
+ if (deviceName)
+ CFStringGetCString(deviceName, gamepad->name, sizeof(gamepad->name), kCFStringEncodingUTF8);
+ else
+ MG_STRNCPY(gamepad->name, "Unknown", sizeof(gamepad->name));
+
+ property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey));
+ if (property)
+ CFNumberGetValue((CFNumberRef)property, (CFNumberType)kCFNumberSInt32Type, (void*)&vendor);
+
+ property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey));
+ if (property)
+ CFNumberGetValue((CFNumberRef)property, kCFNumberSInt32Type, (void*)&product);
+
+ property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVersionNumberKey));
+ if (property)
+ CFNumberGetValue((CFNumberRef)property, (CFNumberType)kCFNumberSInt32Type, (void*)&version);
+
+ if (vendor && product) {
+ MG_SPRINTF(gamepad->guid, "03000000%02x%02x0000%02x%02x0000%02x%02x0000",
+ (u8) vendor, (u8) (vendor >> 8),
+ (u8) product, (u8) (product >> 8),
+ (u8) version, (u8) (version >> 8));
+ }
+ else {
+ MG_SPRINTF(gamepad->guid, "05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00",
+ gamepad->name[0], gamepad->name[1], gamepad->name[2], gamepad->name[3],
+ gamepad->name[4], gamepad->name[5], gamepad->name[6], gamepad->name[7],
+ gamepad->name[8], gamepad->name[9], gamepad->name[10]);
+ }
+
+
+ gamepad->mapping = mg_gamepad_find_valid_mapping(gamepad);
+ gamepad->connected = MG_TRUE;
+
+ for (i = 0; i < CFArrayGetCount(elements); i++) {
+ u32 elm_usage = 0, page = 0;
+ IOHIDElementType type;
+ IOHIDElementRef native = (IOHIDElementRef)
+ CFArrayGetValueAtIndex(elements, i);
+ if (CFGetTypeID(native) != IOHIDElementGetTypeID())
+ continue;
+
+ type = IOHIDElementGetType(native);
+ if ((type != kIOHIDElementTypeInput_Axis) &&
+ (type != kIOHIDElementTypeInput_Button) &&
+ (type != kIOHIDElementTypeInput_Misc))
+ {
+ continue;
+ }
+
+
+ elm_usage = IOHIDElementGetUsage(native);
+ page = IOHIDElementGetUsagePage(native);
+
+ switch (page) {
+ case kHIDPage_Button: {
+ mg_button btn = mg_get_gamepad_button(gamepad, (u8)elm_usage);
+ if (btn == 0)
+ btn = mg_get_gamepad_button_platform(elm_usage);
+ if (btn == 0)
+ break;
+
+ gamepad->buttons[btn].prev = 0;
+ gamepad->buttons[btn].current = 0;
+ gamepad->buttons[btn].supported = MG_TRUE;
+ break;
+ }
+ case kHIDPage_GenericDesktop: {
+ mg_axis btn = mg_get_gamepad_axis(gamepad, (u8)elm_usage);
+ if (btn == 0)
+ btn = mg_get_gamepad_axis_platform(elm_usage);
+ if (btn == 0)
+ break;
+
+ gamepad->axes[btn].value = 0.0f;
+ gamepad->axes[btn].supported = MG_TRUE;
+ break;
+ }
+ }
+ }
+
+ gamepad->src.events = (void*)&gamepads->events;
+ mg_handle_connection_event(&gamepads->events, MG_TRUE, gamepad);
+ CFRelease(elements);
+}
+
+void mg_osx_device_removed_callback(void *context, IOReturn result, void *sender, IOHIDDeviceRef device) {
+ mg_gamepads* gamepads = (mg_gamepads*)context;
+ mg_gamepad* cur = NULL;
+
+ CFNumberRef usageRef = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDPrimaryUsageKey));
+ int usage = 0;
+ if (usageRef)
+ CFNumberGetValue((CFNumberRef)usageRef, (CFNumberType)kCFNumberIntType, (void*)&usage);
+
+ MG_UNUSED(context); MG_UNUSED(result); MG_UNUSED(sender); MG_UNUSED(device);
+ if (usage != kHIDUsage_GD_Joystick && usage != kHIDUsage_GD_GamePad && usage != kHIDUsage_GD_MultiAxisController) {
+ return;
+ }
+
+ for (cur = gamepads->list.head; cur; cur = cur->next) {
+ if ((IOHIDDeviceRef)cur->src.device == device) {
+ mg_handle_connection_event(&gamepads->events, MG_FALSE, cur);
+ mg_gamepad_release(gamepads, cur);
+ return;
+ }
+ }
+}
+
+void mg_gamepads_init_platform(mg_gamepads* gamepads) {
+ const int filter[] = {kHIDPage_GenericDesktop};
+
+ CFMutableDictionaryRef matchingDictionary;
+ gamepads->src.hidManager = (void*)IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
+
+ matchingDictionary = CFDictionaryCreateMutable(
+ kCFAllocatorDefault,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks
+ );
+ if (!matchingDictionary) {
+ CFRelease((IOHIDManagerRef)gamepads->src.hidManager);
+ return;
+ }
+
+ CFDictionarySetValue(
+ matchingDictionary,
+ CFSTR(kIOHIDDeviceUsagePageKey),
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, filter)
+ );
+
+ IOHIDManagerSetDeviceMatching((IOHIDManagerRef)gamepads->src.hidManager, matchingDictionary);
+ CFRelease(matchingDictionary);
+
+ IOHIDManagerRegisterDeviceMatchingCallback((IOHIDManagerRef)gamepads->src.hidManager, mg_osx_device_added_callback, gamepads);
+ IOHIDManagerRegisterDeviceRemovalCallback((IOHIDManagerRef)gamepads->src.hidManager, mg_osx_device_removed_callback, gamepads);
+
+ IOHIDManagerScheduleWithRunLoop((IOHIDManagerRef)gamepads->src.hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+
+ IOHIDManagerOpen((IOHIDManagerRef)gamepads->src.hidManager, kIOHIDOptionsTypeNone);
+
+ /* Execute the run loop once in order to register any initially-attached joysticks */
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
+
+}
+
+mg_bool mg_gamepads_poll_platform(mg_gamepads* gamepads, mg_events* events) {
+ MG_UNUSED(gamepads); MG_UNUSED(events);
+ return MG_FALSE;
+}
+
+void mg_gamepads_free_platform(mg_gamepads* gamepads) {
+ CFRelease((IOHIDManagerRef)gamepads->src.hidManager);
+}
+
+mg_bool mg_gamepad_update_platform(mg_gamepad* gamepad, mg_events* events) {
+ gamepad->src.events = (void*)events;
+ return MG_FALSE;
+}
+
+
+void mg_gamepad_release_platform(mg_gamepad* gamepads) {
+ MG_UNUSED(gamepads);
+}
+
+mg_button mg_get_gamepad_button_platform(u32 button) {
+ switch (button) {
+ case kHIDUsage_GD_DPadUp: return MG_BUTTON_DPAD_UP;
+ case kHIDUsage_GD_DPadRight: return MG_BUTTON_DPAD_RIGHT;
+ case kHIDUsage_GD_DPadDown: return MG_BUTTON_DPAD_DOWN;
+ case kHIDUsage_GD_DPadLeft: return MG_BUTTON_DPAD_LEFT;
+ case kHIDUsage_GD_SystemMainMenu: return MG_BUTTON_GUIDE;
+ case kHIDUsage_GD_Select: return MG_BUTTON_BACK;
+ case kHIDUsage_GD_Start: return MG_BUTTON_START;
+ case kHIDUsage_Button_1: return MG_BUTTON_SOUTH;
+ case kHIDUsage_Button_2: return MG_BUTTON_WEST;
+ case kHIDUsage_Button_3: return MG_BUTTON_EAST;
+ case kHIDUsage_Button_4: return MG_BUTTON_NORTH;
+ case kHIDUsage_Button_5: return MG_BUTTON_LEFT_SHOULDER;
+ case kHIDUsage_Button_6: return MG_BUTTON_RIGHT_SHOULDER;
+ case kHIDUsage_Button_7: return MG_BUTTON_LEFT_TRIGGER;
+ case kHIDUsage_Button_8: return MG_BUTTON_RIGHT_TRIGGER;
+ case kHIDUsage_Button_9: return MG_BUTTON_LEFT_STICK;
+ case kHIDUsage_Button_10: return MG_BUTTON_RIGHT_STICK;
+ default: break;
+ }
+
+ return MG_BUTTON_UNKNOWN;
+}
+
+mg_axis mg_get_gamepad_axis_platform(u32 axis) {
+ switch (axis) {
+ case kHIDUsage_GD_X: return MG_AXIS_LEFT_X;
+ case kHIDUsage_GD_Y: return MG_AXIS_LEFT_Y;
+ case kHIDUsage_GD_Z: return MG_AXIS_LEFT_TRIGGER;
+ case kHIDUsage_GD_Rx: return MG_AXIS_RIGHT_X;
+ case kHIDUsage_GD_Ry: return MG_AXIS_RIGHT_Y;
+ case kHIDUsage_GD_Rz: return MG_AXIS_RIGHT_TRIGGER;
+ default: break;
+ }
+ return MG_AXIS_UNKNOWN;
+}
+#endif /* MG_MACOS */
+
+/*
+ * start of wasm
+ */
+
+#if defined(MG_WASM)
+#include
+
+mg_gamepad* mg_wasm_gamepads[MG_MAX_GAMEPADS];
+
+
+EM_BOOL mg_emscripten_on_gamepad(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) {
+ mg_gamepad* gamepad;
+ mg_gamepads* gamepads = (mg_gamepads*)userData;
+ if (gamepads == NULL) {
+ return 0;
+ }
+
+ MG_UNUSED(eventType);
+
+ gamepad = mg_wasm_gamepads[gamepadEvent->index];
+
+ if (gamepadEvent->connected) {
+ mg_button button;
+ mg_axis axis;
+
+ if (gamepad) {
+ mg_gamepad_release(gamepads, mg_wasm_gamepads[gamepadEvent->index]);
+ }
+
+ gamepad = mg_gamepad_find(gamepads);
+ if (gamepad == NULL) {
+ return 0;
+ }
+
+ for (button = 0; button < MG_BUTTON_MISC1; button++) {
+ gamepad->buttons[button].supported = MG_TRUE;
+ gamepad->buttons[button].current = MG_FALSE;
+ gamepad->buttons[button].prev = MG_FALSE;
+ }
+
+ for (axis = 0; axis < MG_AXIS_HAT_DPAD_LEFT_RIGHT; axis++) {
+ gamepad->axes[axis].value = 0;
+ gamepad->axes[axis].supported = MG_TRUE;
+ }
+
+ gamepad->connected = MG_TRUE;
+ mg_wasm_gamepads[gamepadEvent->index] = gamepad;
+ } else {
+ mg_gamepad_release(gamepads, mg_wasm_gamepads[gamepadEvent->index]);
+ mg_wasm_gamepads[gamepadEvent->index] = NULL;
+ }
+
+ return 1; /* The event was consumed by the callback handler */
+}
+
+void mg_gamepads_init_platform(mg_gamepads* gamepads) {
+ emscripten_set_gamepadconnected_callback(gamepads, 1, mg_emscripten_on_gamepad);
+ emscripten_set_gamepaddisconnected_callback(gamepads, 1, mg_emscripten_on_gamepad);
+
+ emscripten_sample_gamepad_data();
+}
+
+mg_bool mg_gamepads_poll_platform(mg_gamepads* gamepads, mg_events* events) {
+ MG_UNUSED(gamepads); MG_UNUSED(events);
+ return MG_FALSE;
+}
+
+void mg_gamepads_free_platform(mg_gamepads* gamepads) {
+ MG_UNUSED(gamepads);
+}
+
+mg_bool mg_gamepad_update_platform(mg_gamepad* gamepad, mg_events* events) {
+ int i = gamepad->src.index;
+ int j;
+ EmscriptenGamepadEvent gamepadState;
+
+ emscripten_sample_gamepad_data();
+
+ if (emscripten_get_gamepad_status(i, &gamepadState) != EMSCRIPTEN_RESULT_SUCCESS)
+ return MG_FALSE;
+
+ for (j = 0; j < gamepadState.numButtons; j++) {
+ mg_button map[] = {
+ MG_BUTTON_SOUTH, MG_BUTTON_EAST, MG_BUTTON_WEST, MG_BUTTON_NORTH,
+ MG_BUTTON_LEFT_SHOULDER, MG_BUTTON_RIGHT_SHOULDER, MG_BUTTON_LEFT_TRIGGER, MG_BUTTON_RIGHT_TRIGGER,
+ MG_BUTTON_BACK, MG_BUTTON_START,
+ MG_BUTTON_LEFT_STICK, MG_BUTTON_RIGHT_STICK,
+ MG_BUTTON_DPAD_UP, MG_BUTTON_DPAD_DOWN, MG_BUTTON_DPAD_LEFT, MG_BUTTON_DPAD_RIGHT,
+ MG_BUTTON_GUIDE
+ };
+ mg_button btn = MG_BUTTON_UNKNOWN;
+ if (j < (int)(sizeof(map) / sizeof(mg_button)))
+ btn = map[j];
+ if (btn == MG_BUTTON_UNKNOWN)
+ continue;
+
+ mg_handle_button_event(events, btn, MG_BOOL(gamepadState.digitalButton[j]), gamepad);
+ }
+
+ for (j = 0; j < gamepadState.numAxes; j += 2) {
+ mg_axis map[] = {
+ MG_AXIS_LEFT_X, MG_AXIS_LEFT_Y,
+ MG_AXIS_RIGHT_X, MG_AXIS_RIGHT_Y,
+ MG_AXIS_LEFT_TRIGGER, MG_AXIS_LEFT_TRIGGER
+ };
+ mg_axis btn = MG_AXIS_UNKNOWN;
+ if (j < (int)(sizeof(map) / sizeof(mg_axis)))
+ btn = map[j];
+ if (btn == MG_AXIS_UNKNOWN)
+ continue;
+
+ mg_handle_axis_event(events, btn, (float)gamepadState.axis[j], gamepad);
+ }
+
+ return MG_FALSE;
+}
+
+void mg_gamepad_release_platform(mg_gamepad* gamepad) {
+ mg_wasm_gamepads[gamepad->src.index] = NULL;
+}
+
+mg_button mg_get_gamepad_button_platform(u32 button) {
+ /* TODO */
+ MG_UNUSED(button);
+ return MG_BUTTON_UNKNOWN;
+}
+
+mg_axis mg_get_gamepad_axis_platform(u32 axis) {
+ /* TODO */
+ MG_UNUSED(axis);
+ return MG_AXIS_UNKNOWN;
+}
+
+#endif
+
+extern const char* sdl_db[];
+
+typedef struct mg_element {
+ unsigned char type;
+ unsigned char index;
+ signed char axisScale;
+ signed char axisOffset;
+} mg_element;
+
+typedef struct mg_mapping {
+ char name[128];
+ char guid[33];
+ mg_element buttons[16];
+ mg_element axes[6];
+
+ mg_button rButtons[256];
+ mg_axis rAxes[MG_AXIS_COUNT];
+} mg_mapping;
+
+#define MG_JOYSTICK_AXIS 1
+#define MG_JOYSTICK_BUTTON 2
+#define MG_JOYSTICK_HATBIT 3
+
+typedef struct mappings_data {
+ mg_mapping mappings[1300];
+ int mappingCount;
+ int mappingMax;
+} mappings_data;
+
+mappings_data mappings;
+
+mg_button mg_get_gamepad_button(mg_gamepad* gamepad, u8 btn) {
+ if (gamepad->mapping == NULL) {
+ return MG_BUTTON_UNKNOWN;
+ }
+
+ return gamepad->mapping->rButtons[btn];
+}
+
+mg_axis mg_get_gamepad_axis(mg_gamepad* gamepad, u8 axis) {
+ if (gamepad->mapping == NULL) {
+ return MG_AXIS_UNKNOWN;
+ }
+
+ if (axis >= MG_AXIS_COUNT) {
+ return MG_AXIS_UNKNOWN;
+ }
+
+ return gamepad->mapping->rAxes[axis];
+}
+
+void updateGamepadGUID(char* guid) {
+#ifdef __APPLE__
+ if ((strncmp(&guid[4], "000000000000", 12) == 0) &&
+ (strncmp(&guid[20], "000000000000", 12) == 0))
+ {
+ char original[33];
+ MG_STRNCPY(original, guid, sizeof(original) - 1);
+ MG_SPRINTF(guid,"03000000%.4s0000%.4s000000000000",
+ original, original + 16);
+ }
+#elif defined(_WIN32)
+ if (strncmp(&guid[20], "504944564944", 12) == 0) {
+ char original[33];
+ MG_STRNCPY(original, guid, sizeof(original) - 1);
+ MG_SPRINTF(guid, "03000000%.4s0000%.4s000000000000",
+ original, original + 4);
+ }
+#else
+ (void)(guid); /* prevent unused warning */
+#endif
+}
+
+MG_API mg_mapping* findMapping(const char* guid);
+mg_mapping* findMapping(const char* guid) {
+ int i;
+ for (i = 0; i < mappings.mappingCount; i++) {
+ if (strncmp(mappings.mappings[i].guid, guid, sizeof(mappings.mappings[i].guid)) == 0) {
+ return &mappings.mappings[i];
+ }
+ }
+
+ return NULL;
+}
+
+MG_API mg_mapping* findMappingPermisive(const char* guid);
+mg_mapping* findMappingPermisive(const char* guid) {
+ int i;
+ for (i = 0; i < mappings.mappingCount; i++) {
+ if (strncmp(mappings.mappings[i].guid, guid, sizeof(mappings.mappings[i].guid) - 8) == 0) {
+ return &mappings.mappings[i];
+ }
+ }
+
+ return NULL;
+}
+
+mg_mapping* mg_gamepad_find_valid_mapping(mg_gamepad* js) {
+ mg_mapping* mapping = findMapping(js->guid);
+ if (mapping == NULL) {
+ mapping = findMappingPermisive(js->guid);
+ if (mapping == NULL)
+ return NULL;
+ }
+
+ return mapping;
+}
+
+typedef struct mg_field {
+ const char* name;
+ mg_size_t len;
+ u8 val;
+ mg_element* element;
+} mg_field;
+
+
+MG_API mg_bool parseMapping(mg_mapping* mapping, const char* string);
+mg_bool parseMapping(mg_mapping* mapping, const char* string) {
+ const char* substr = string;
+ mg_size_t i, length, len;
+ mg_field fields[] = {
+ { "platform", 8, 0, NULL },
+ { "a", 1, MG_BUTTON_SOUTH , NULL},
+ { "b", 1, MG_BUTTON_EAST , NULL},
+ { "x", 1, MG_BUTTON_WEST , NULL},
+ { "y", 1, MG_BUTTON_NORTH , NULL},
+ { "back", 4, MG_BUTTON_BACK , NULL},
+ { "start", 5, MG_BUTTON_START , NULL},
+ { "guide", 5, MG_BUTTON_GUIDE , NULL},
+ { "leftshoulder", 12, MG_BUTTON_LEFT_SHOULDER , NULL},
+ { "rightshoulder", 13, MG_BUTTON_RIGHT_SHOULDER , NULL},
+ { "leftstick", 9, MG_BUTTON_LEFT_STICK , NULL},
+ { "rightstick", 10, MG_BUTTON_RIGHT_STICK , NULL},
+ { "dpup", 4, MG_BUTTON_DPAD_UP , NULL},
+ { "dpright", 7, MG_BUTTON_DPAD_RIGHT , NULL},
+ { "dpdown", 6, MG_BUTTON_DPAD_DOWN , NULL},
+ { "dpleft", 6, MG_BUTTON_DPAD_LEFT , NULL},
+ { "lefttrigger", 11, MG_BUTTON_LEFT_TRIGGER , NULL},
+ { "righttrigger", 12, MG_BUTTON_RIGHT_TRIGGER , NULL },
+
+ { "lefttrigger", 11, MG_AXIS_LEFT_TRIGGER, NULL},
+ { "righttrigger", 12, MG_AXIS_RIGHT_TRIGGER, NULL },
+ { "leftx", 5, MG_AXIS_LEFT_X, NULL },
+ { "lefty", 5, MG_AXIS_LEFT_Y, NULL } ,
+ { "rightx", 6, MG_AXIS_RIGHT_X, NULL},
+ { "righty", 6, MG_AXIS_RIGHT_Y, NULL }
+ };
+
+ len = (sizeof(fields) / sizeof(mg_field));
+
+ for (i = 1; i < len - 8; i++) {
+ fields[i].element = &mapping->buttons[fields[i].val];
+ }
+
+ for (i = len - 8; i < len; i++) {
+ fields[i].element = &mapping->axes[fields[i].val];
+ }
+
+ length = MG_STRCSPN(substr, ",");
+ if (length != 32 || substr[length] != ',') {
+ return MG_FALSE;
+ }
+
+ MG_MEMCPY(mapping->guid, substr, length);
+ substr += length + 1;
+
+ length = MG_STRCSPN(substr, ",");
+ if (length >= sizeof(mapping->name) || substr[length] != ',') {
+ return MG_FALSE;
+ }
+
+ MG_MEMCPY(mapping->name, substr, length);
+ substr += length + 1;
+
+ while (substr[0]) {
+ /* TODO: Implement output modifiers */
+ char mod = 0;;
+ if (substr[0] == '+' || substr[0] == '-') {
+ mod = substr[0];
+ substr++;
+ }
+
+ for (i = 0; i < sizeof(fields) / sizeof(fields[0]); i++) {
+ int8_t minimum = -1;
+ int8_t maximum = 1;
+ mg_element* e;
+
+ switch (mod) {
+ case '+':
+ minimum = 0;
+ break;
+ case '-':
+ maximum = 0;
+ break;
+ default: break;
+ }
+
+ length = fields[i].len;
+ if (strncmp(substr, fields[i].name, length) != 0 || substr[length] != ':')
+ continue;
+ substr += length + 1;
+
+ if (fields[i].element == NULL) {
+ #if defined(_WIN32)
+ const char name[] = "Windows";
+ #elif defined(__APPLE__)
+ const char name[] = "Mac OS X";
+ #elif defined(EMSCRIPTEN)
+ const char name[] = "Web";
+ #elif defined(__linux__)
+ const char name[] = "Linux";
+ #else
+ const char name[] = "";
+ #endif
+ if (strncmp(substr, name, sizeof(name)) != 0)
+ return MG_FALSE;
+
+ break;
+ }
+
+ e = fields[i].element;
+ if (e >= mapping->axes && e <= &mapping->axes[6] && substr[0] == 'b') {
+ continue;
+ }
+
+ switch (substr[0]) {
+ case '+':
+ minimum = 0;
+ substr += 1;
+ break;
+ case '-':
+ maximum = 0;
+ substr += 1;
+ break;
+ default: break;
+ }
+
+ switch (substr[0]) {
+ case 'a':
+
+ e->type = MG_JOYSTICK_AXIS;
+ e->axisScale = (signed char)(2 / (maximum - minimum));
+ e->axisOffset = (signed char)(-(maximum + minimum));
+
+ if (substr[0] == '~') {
+ e->axisScale = -e->axisScale;
+ e->axisOffset = -e->axisOffset;
+ }
+ e->index = (uint8_t) strtoul(&substr[1], (char**) &substr, 10);
+ break;
+ case 'b':
+ e->type = MG_JOYSTICK_BUTTON;
+ e->index = (uint8_t) strtoul(&substr[1], (char**) &substr, 10);
+ break;
+ case 'h': {
+ const unsigned long hat = strtoul(&substr[1], (char**) &substr, 10);
+ const unsigned long bit = strtoul(&substr[1], (char**) &substr, 10);
+
+ e->type = MG_JOYSTICK_HATBIT;
+ e->index = (uint8_t) ((hat << 4) | bit);
+
+ break;
+ }
+ default: break;
+ }
+
+ break;
+ }
+
+ substr += MG_STRCSPN(substr, ",");
+ substr += MG_STRSPN(substr, ",");
+ }
+
+ for (i = 0; i < 32; i++) {
+ if (mapping->guid[i] >= 'A' && mapping->guid[i] <= 'F')
+ mapping->guid[i] += 'a' - 'A';
+ }
+
+ for (i = 0; i < 255; i++) {
+ mg_size_t y;
+ mapping->rButtons[i] = MG_BUTTON_UNKNOWN;
+ for (y = 0; y < 16; y++) {
+ mg_element e = mapping->buttons[y];
+ if (e.index == i) {
+ mapping->rButtons[i] = (mg_button)y;
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < MG_AXIS_COUNT; i++) {
+ mg_size_t y;
+ mapping->rAxes[i] = MG_AXIS_UNKNOWN;
+ for (y = 0; y < 6; y++) {
+ mg_element e = mapping->axes[y];
+ if (e.index == i) {
+ mapping->rAxes[i] = (mg_axis)y;
+ break;
+ }
+ }
+ }
+
+ updateGamepadGUID(mapping->guid);
+ return MG_TRUE;
+}
+
+mg_bool mg_update_gamepad_mappings(mg_gamepads* gamepads, const char* string) {
+ const char* substr = string;
+ mg_gamepad* cur;
+ mg_mapping mapping;
+ mg_size_t length;
+ char line[1024];
+
+ if (mappings.mappingCount >= mappings.mappingMax) {
+ return MG_FALSE;
+ }
+
+ while (*substr) {
+ if (!(*substr >= '0' && *substr <= '9') &&
+ !(*substr >= 'a' && *substr <= 'f') &&
+ !(*substr >= 'A' && *substr <= 'F')) {
+ substr += MG_STRCSPN(substr, "\r\n");
+ substr += MG_STRSPN(substr, "\r\n");
+ break;
+ }
+
+ length = MG_STRCSPN(substr, "\r\n");
+ if (length < sizeof(line)) {
+ MG_MEMSET(&mapping, 0, sizeof(mapping));
+ MG_MEMCPY(line, substr, length);
+ line[length] = '\0';
+
+ if (parseMapping(&mapping, line)) {
+ mg_mapping* previous = findMapping(mapping.guid);
+ if (previous)
+ *previous = mapping;
+ else {
+ mappings.mappingCount++;
+ /*mappings.mappings =
+ realloc(mappings.mappings,
+ sizeof(mg_mapping) * (mg_size_t)mappings.mappingCount);*/
+ mappings.mappings[mappings.mappingCount - 1] = mapping;
+ }
+ }
+ }
+
+ substr += length;
+ }
+
+ for (cur = gamepads->list.head; cur; cur = cur->next) {
+ cur->mapping = mg_gamepad_find_valid_mapping(cur);
+ }
+
+ return MG_TRUE;
+}
+
+const char * sdl_db[] = {
+#ifdef _WIN32
+"03000000300f00000a01000000000000,3 In 1 Conversion Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,",
+"03000000fa190000918d000000000000,3 In 1 Conversion Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,",
+"03000000fa2d00000100000000000000,3dRudder Foot Motion Controller,leftx:a0,lefty:a1,rightx:a5,righty:a2,",
+"03000000d0160000040d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,",
+"03000000d0160000050d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,",
+"03000000d0160000060d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,",
+"03000000d0160000070d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,",
+"03000000d0160000600a000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,",
+"03000000c82d00000031000000000000,8BitDo Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000c82d00000531000000000000,8BitDo Adapter 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000c82d00000951000000000000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightx:a3,righty:a5,start:b11,",
+"03000000008000000210000000000000,8BitDo F30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,",
+"030000003512000011ab000000000000,8BitDo F30 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000c82d00001028000000000000,8BitDo F30 Arcade Joystick,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,",
+"03000000c82d000011ab000000000000,8BitDo F30 Arcade Joystick,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"03000000801000000900000000000000,8BitDo F30 Arcade Stick,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,",
+"03000000c82d00001038000000000000,8BitDo F30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,",
+"03000000c82d00000090000000000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"03000000c82d00006a28000000000000,8BitDo GameCube,a:b0,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b9,paddle2:b8,rightshoulder:b10,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b1,y:b4,",
+"03000000c82d00001251000000000000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"03000000c82d00001151000000000000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"03000000c82d00000150000000000000,8BitDo M30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b11,x:b4,y:b3,",
+"03000000c82d00000151000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b11,x:b3,y:b4,",
+"03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000c82d00005106000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,guide:b2,leftshoulder:b8,lefttrigger:b9,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,",
+"03000000c82d00002090000000000000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"03000000c82d00000310000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,",
+"03000000c82d00000451000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightx:a3,righty:a5,start:b11,",
+"03000000c82d00002028000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,",
+"03000000c82d00008010000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,",
+"03000000c82d0000e002000000000000,8BitDo N30,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b6,",
+"03000000c82d00000190000000000000,8BitDo N30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"03000000c82d00001590000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,",
+"03000000c82d00006528000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"03000000c82d00000290000000000000,8BitDo N64,+rightx:b9,+righty:b3,-rightx:b4,-righty:b8,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,",
+"03000000c82d00003038000000000000,8BitDo N64,+rightx:b9,+righty:b3,-rightx:b4,-righty:b8,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,",
+"03000000c82d00006928000000000000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b11,",
+"03000000c82d00002590000000000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"030000003512000012ab000000000000,8BitDo NES30,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,",
+"03000000c82d000012ab000000000000,8BitDo NES30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"03000000022000000090000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"03000000203800000900000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"03000000c82d00002038000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"03000000c82d00000751000000000000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a5,start:b11,x:b3,y:b4,",
+"03000000c82d00000851000000000000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a5,start:b11,x:b3,y:b4,",
+"03000000c82d00000360000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"03000000c82d00000361000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"03000000c82d00000660000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"03000000c82d00000131000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"03000000c82d00000231000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"03000000c82d00000331000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"03000000c82d00000431000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"03000000c82d00002867000000000000,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a2,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b10,x:b3,y:b4,",
+"03000000c82d00000130000000000000,8BitDo SF30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,",
+"03000000c82d00000060000000000000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000061000000000000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000102800000900000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"03000000c82d000021ab000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"03000000c82d00003028000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"030000003512000020ab000000000000,8BitDo SN30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b11,x:b4,y:b3,",
+"03000000c82d00000030000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"03000000c82d00000351000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a2,rightshoulder:b7,rightx:a3,righty:a5,start:b11,x:b4,y:b3,",
+"03000000c82d00001290000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"03000000c82d000020ab000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,",
+"03000000c82d00004028000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,",
+"03000000c82d00006228000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"03000000c82d00000021000000000000,8BitDo SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,",
+"03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000260000000000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000261000000000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00001230000000000000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000c82d00001b30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000c82d00001d30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000c82d00001530000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000c82d00001630000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000c82d00001730000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000c82d00001130000000000000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000c82d00001330000000000000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00000121000000000000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000a00500003232000000000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,",
+"03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"03000000c82d00003032000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"030000008f0e00001200000000000000,Acme GA02,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,",
+"03000000c01100000355000000000000,Acrux,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000fa190000f0ff000000000000,Acteck AGJ 3200,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000d1180000402c000000000000,ADT1,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a3,rightx:a2,righty:a5,x:b3,y:b4,",
+"030000006f0e00008801000000000000,Afterglow Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006f0e00000263000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006f0e00001101000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006f0e00001401000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006f0e00001402000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006f0e00001901000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006f0e00001a01000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006f0e00001301000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e00001302000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e00001304000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e00001413000000000000,Afterglow Xbox Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006f0e00003901000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000ab1200000103000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000ad1b000000f9000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000100000008200000000000000,Akishop Customs PS360,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000007c1800000006000000000000,Alienware Dual Compatible PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,",
+"03000000491900001904000000000000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,",
+"03000000710100001904000000000000,Amazon Luna Controller,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b8,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b4,rightstick:b7,rightx:a3,righty:a4,start:b6,x:b3,y:b2,",
+"0300000008100000e501000000000000,Anbernic Game Pad,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b3,y:b4,",
+"03000000020500000913000000000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a5,start:b11,x:b3,y:b4,",
+"03000000373500000710000000000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000373500004610000000000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000830500000160000000000000,Arcade,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b3,x:b4,y:b4,",
+"03000000120c0000100e000000000000,Armor 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000490b00004406000000000000,ASCII Seamic Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,",
+"03000000869800002500000000000000,Astro C40 TR PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000a30c00002700000000000000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,",
+"03000000a30c00002800000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,",
+"03000000050b00000579000000000000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000050b00000679000000000000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,start:b3,",
+"03000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,",
+"03000000380800001889000000000000,AtGames Legends Gamer Pro,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b13,lefttrigger:b14,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000008a3500000102000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,",
+"030000008a3500000201000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,",
+"030000008a3500000302000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,",
+"030000008a3500000402000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,",
+"03000000e4150000103f000000000000,Batarang,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000d6200000e557000000000000,Batarang PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,",
+"030000006f0e00003201000000000000,Battlefield 4 PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000ad1b000001f9000000000000,BB 070,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000d62000002a79000000000000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000bc2000005250000000000000,Beitong G3,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a3,righty:a4,start:b15,x:b3,y:b4,",
+"030000000d0500000208000000000000,Belkin Nostromo N40,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b2,y:b3,",
+"03000000bc2000006012000000000000,Betop 2126F,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000bc2000000055000000000000,Betop BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000bc2000006312000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000bc2000006321000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000bc2000006412000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000c01100000555000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000c01100000655000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000790000000700000000000000,Betop Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,",
+"03000000808300000300000000000000,Betop Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,",
+"030000006f0e00006401000000000000,BF One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a2,righty:a5,start:b7,x:b2,y:b3,",
+"03000000300f00000202000000000000,Bigben,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a5,righty:a2,start:b7,x:b2,y:b3,",
+"030000006b1400000209000000000000,Bigben,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006b1400000055000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"030000006b1400000103000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,",
+"03000000120c0000200e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000210e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000f10e000000000000,Brook PS2 Adapter,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000120c0000310c000000000000,Brook Super Converter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,",
+"03000000d81d00000b00000000000000,Buffalo BSGP1601 Series,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,",
+"030000005a1c00002400000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,",
+"030000005b1c00002400000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,",
+"030000005b1c00002500000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,",
+"030000006d04000042c2000000000000,ChillStream,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000e82000006058000000000000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000457500000401000000000000,Cobra,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000b0400003365000000000000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,",
+"030000004c050000c505000000000000,CronusMax Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000d814000007cd000000000000,Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000d8140000cefa000000000000,Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,",
+"030000003807000002cb000000000000,Cyborg,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000a306000022f6000000000000,Cyborg V.3 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,",
+"03000000f806000000a3000000000000,DA Leader,a:b7,b:b6,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b0,leftstick:b8,lefttrigger:b1,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:b3,rightx:a2,righty:a3,start:b12,x:b4,y:b5,",
+"030000001a1c00000001000000000000,Datel Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000451300000830000000000000,Defender Game Racer X7,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000791d00000103000000000000,Dual Box Wii,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000c0160000e105000000000000,Dual Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,",
+"030000004f040000070f000000000000,Dual Power,a:b8,b:b9,back:b4,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,leftshoulder:b13,leftstick:b6,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b12,rightstick:b7,righttrigger:b15,start:b5,x:b10,y:b11,",
+"030000004f04000012b3000000000000,Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,",
+"030000004f04000020b3000000000000,Dual Trigger,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,",
+"03000000bd12000002e0000000000000,Dual Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,",
+"03000000ff1100003133000000000000,DualForce,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b1,",
+"030000006f0e00003001000000000000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000fc0400000250000000000000,Easy Grip,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,",
+"03000000bc2000000091000000000000,EasySMX Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"030000006e0500000a20000000000000,Elecom DUX60 MMO,a:b2,b:b3,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b14,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b15,righttrigger:b13,rightx:a3,righty:a4,start:b20,x:b0,y:b1,",
+"03000000b80500000410000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,",
+"03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,",
+"03000095090000010000000000000000,Elecom JC-U609,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b8,x:b3,y:b4,",
+"0300004112000000e500000000000000,Elecom JC-U909Z,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b8,x:b3,y:b4,",
+"03000041120000001050000000000000,Elecom JC-U911,a:b1,b:b2,back:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b0,x:b4,y:b5,",
+"030000006e0500000520000000000000,Elecom P301U PlayStation Controller Adapter,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,",
+"03000000411200004450000000000000,Elecom U1012,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,",
+"030000006e0500000320000000000000,Elecom U3613M,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,",
+"030000006e0500000e20000000000000,Elecom U3912T,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,",
+"030000006e0500000f20000000000000,Elecom U4013S,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,",
+"030000006e0500001320000000000000,Elecom U4113,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006e0500001020000000000000,Elecom U4113S,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,",
+"030000006e0500000720000000000000,Elecom W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,",
+"030000007d0400000640000000000000,Eliminator AfterShock,a:b1,b:b2,back:b9,dpdown:+a3,dpleft:-a5,dpright:+a5,dpup:-a3,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a4,righty:a2,start:b8,x:b0,y:b3,",
+"03000000120c0000f61c000000000000,Elite,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000430b00000300000000000000,EMS Production PS2 Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+"03000000062000001801000000000000,EMS TrioLinker Plus II,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b2,y:b3,",
+"03000000242f000000b7000000000000,ESM 9110,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"03000000101c0000181c000000000000,Essential,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b4,leftx:a1,lefty:a0,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+"030000008f0e00000f31000000000000,EXEQ,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,",
+"03000000341a00000108000000000000,EXEQ RF Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"030000006f0e00008401000000000000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006f0e00008101000000000000,Faceoff Deluxe Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006f0e00008001000000000000,Faceoff Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000021000000090000000000000,FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"0300000011040000c600000000000000,FC801,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,",
+"03000000852100000201000000000000,FF GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000ad1b000028f0000000000000,Fightpad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000ad1b00002ef0000000000000,Fightpad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000ad1b000038f0000000000000,Fightpad TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,",
+"03005036852100000000000000000000,Final Fantasy XIV Online Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000f806000001a3000000000000,Firestorm,a:b9,b:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b0,leftstick:b10,lefttrigger:b1,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b12,x:b8,y:b4,",
+"03000000b50700000399000000000000,Firestorm 2,a:b2,b:b4,back:b10,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,righttrigger:b9,start:b11,x:b3,y:b5,",
+"03000000b50700001302000000000000,Firestorm D3,a:b0,b:b2,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,x:b1,y:b3,",
+"03000000b40400001024000000000000,Flydigi Apex,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,",
+"03000000151900004000000000000000,Flydigi Vader 2,a:b27,b:b26,back:b19,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b23,leftstick:b17,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b22,rightstick:b16,righttrigger:b20,rightx:a3,righty:a4,start:b18,x:b25,y:b24,",
+"03000000b40400001124000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b14,paddle1:b4,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,y:b3,",
+"03000000b40400001224000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"030000008305000000a0000000000000,G08XU,a:b0,b:b1,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b5,x:b2,y:b3,",
+"0300000066f700000100000000000000,Game VIB Joystick,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,",
+"03000000260900002625000000000000,GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,",
+"03000000341a000005f7000000000000,GameCube Controller,a:b2,b:b3,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b1,y:b0,",
+"03000000430b00000500000000000000,GameCube Controller,a:b0,b:b2,dpdown:b10,dpleft:b8,dpright:b9,dpup:b11,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a3,rightx:a5,righty:a2,start:b7,x:b1,y:b3,",
+"03000000790000004718000000000000,GameCube Controller,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,",
+"03000000790000004618000000000000,GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,",
+"030000008f0e00000d31000000000000,Gamepad 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000ac0500003d03000000000000,GameSir G3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000ac0500005b05000000000000,GameSir G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000ac0500002d02000000000000,GameSir G4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,",
+"03000000ac0500004d04000000000000,GameSir G4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000ac0500001a06000000000000,GameSir-T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"030000004c0e00001035000000000000,Gamester,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
+"030000000d0f00001110000000000000,GameStick Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a5,start:b11,x:b3,y:b4,",
+"0300000047530000616d000000000000,GameStop,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000c01100000140000000000000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000b62500000100000000000000,Gametel GT004 01,a:b3,b:b0,dpdown:b10,dpleft:b9,dpright:b8,dpup:b11,leftshoulder:b4,rightshoulder:b5,start:b7,x:b1,y:b2,",
+"030000008f0e00001411000000000000,Gamo2 Divaller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000120c0000a857000000000000,Gator Claw,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000c9110000f055000000000000,GC100XF,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"030000008305000009a0000000000000,Genius,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"030000008305000031b0000000000000,Genius Maxfire Blaze 3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000451300000010000000000000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"030000005c1a00003330000000000000,Genius MaxFire Grandias 12V,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,",
+"03000000300f00000b01000000000000,GGE909 Recoil,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+"03000000f0250000c283000000000000,Gioteck PlayStation Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000f025000021c1000000000000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000f025000031c1000000000000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000f0250000c383000000000000,Gioteck VX2 PlayStation Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000f0250000c483000000000000,Gioteck VX2 PlayStation Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000d11800000094000000000000,Google Stadia Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b11,rightx:a3,righty:a4,start:b9,x:b2,y:b3,",
+"030000004f04000026b3000000000000,GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"0300000079000000d418000000000000,GPD Win,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000c6240000025b000000000000,GPX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000007d0400000840000000000000,Gravis Destroyer Tilt,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,x:b0,y:b3,",
+"030000007d0400000540000000000000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"03000000280400000140000000000000,Gravis GamePad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a3,dpup:-a4,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000008f0e00000610000000000000,GreenAsia,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a5,righty:a2,start:b11,x:b3,y:b0,",
+"03000000ac0500006b05000000000000,GT2a,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,",
+"03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00004900000000000000,Hatsune Miku Sho PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000001008000001e1000000000000,Havit HV G60,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,",
+"030000000d0f00000c00000000000000,HEXT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000d81400000862000000000000,HitBox Edition Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,",
+"03000000632500002605000000000000,HJD X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"030000000d0f00000a00000000000000,Hori DOA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000000d0f00008500000000000000,Hori Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00002500000000000000,Hori Fighting Commander 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000000d0f00002d00000000000000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00005f00000000000000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00005e00000000000000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000000d0f00008400000000000000,Hori Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f00006201000000000000,Hori Fighting Commander Octa,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000000d0f00006401000000000000,Hori Fighting Commander Octa,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,start:b7,x:b2,y:b3,",
+"030000000d0f00005100000000000000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f00008600000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000000d0f0000ba00000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000000d0f00008800000000000000,Hori Fighting Stick mini 4 PS3,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,",
+"030000000d0f00008700000000000000,Hori Fighting Stick mini 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000000d0f00001000000000000000,Hori Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f00003200000000000000,Hori Fightstick 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f0000c000000000000000,Hori Fightstick 4,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000000d0f00000d00000000000000,Hori Fightstick EX2,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
+"030000000d0f00003701000000000000,Hori Fightstick Mini,a:b1,b:b0,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b3,y:b2,",
+"030000000d0f00004000000000000000,Hori Fightstick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,",
+"030000000d0f00002100000000000000,Hori Fightstick V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f00002700000000000000,Hori Fightstick V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000000d0f0000a000000000000000,Hori Grip TAC4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b13,x:b0,y:b3,",
+"030000000d0f0000a500000000000000,Hori Miku Project Diva X HD PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000000d0f0000a600000000000000,Hori Miku Project Diva X HD PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000000d0f00000101000000000000,Hori Mini Hatsune Miku FT,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00005400000000000000,Hori Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00000900000000000000,Hori Pad 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00004d00000000000000,Hori Pad A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00003801000000000000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,",
+"030000000d0f00009200000000000000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000000d0f00002301000000000000,Hori PS4 Controller Light,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,",
+"030000000d0f00001100000000000000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000000d0f00002600000000000000,Hori Real Arcade Pro 3P,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f00004b00000000000000,Hori Real Arcade Pro 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f00006a00000000000000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f00006b00000000000000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00008a00000000000000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f00008b00000000000000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00006f00000000000000,Hori Real Arcade Pro 4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00007000000000000000,Hori Real Arcade Pro 4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000000d0f00003d00000000000000,Hori Real Arcade Pro N3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b10,leftstick:b4,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b6,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f0000ae00000000000000,Hori Real Arcade Pro N4,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000000d0f00008c00000000000000,Hori Real Arcade Pro P4,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000000d0f0000aa00000000000000,Hori Real Arcade Pro S,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f0000d800000000000000,Hori Real Arcade Pro S,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"030000000d0f00002200000000000000,Hori Real Arcade Pro V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00005b00000000000000,Hori Real Arcade Pro V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f00005c00000000000000,Hori Real Arcade Pro V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f0000af00000000000000,Hori Real Arcade Pro VHS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,",
+"030000000d0f00001b00000000000000,Hori Real Arcade Pro VX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000ad1b000002f5000000000000,Hori Real Arcade Pro VX,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,rightx:a2,righty:a5,start:b6,x:b2,y:b3,",
+"030000000d0f00009c00000000000000,Hori TAC Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f0000c900000000000000,Hori Taiko Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00006400000000000000,Horipad 3TP,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f00001300000000000000,Horipad 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f00005500000000000000,Horipad 4 FPS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f00006e00000000000000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00006600000000000000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000000d0f00004200000000000000,Horipad A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000ad1b000001f5000000000000,Horipad EXT2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000000d0f0000ee00000000000000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f0000c100000000000000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f0000f600000000000000,Horipad Nintendo Switch Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"030000000d0f00006700000000000000,Horipad One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000000d0f00009601000000000000,Horipad Steam,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc2:b2,paddle1:b5,paddle2:b15,paddle3:b18,paddle4:b19,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"030000000d0f0000dc00000000000000,Horipad Switch,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000242e00000b20000000000000,Hyperkin Admiral N64 Controller,+rightx:b11,+righty:b13,-rightx:b8,-righty:b12,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,",
+"03000000242e0000ff0b000000000000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,",
+"03000000790000004e95000000000000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,",
+"03000000242e00006a48000000000000,Hyperkin RetroN Sq,a:b3,b:b7,back:b5,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b0,rightshoulder:b1,start:b4,x:b2,y:b6,",
+"03000000242f00000a20000000000000,Hyperkin Scout,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,",
+"03000000242e00000a20000000000000,Hyperkin Scout Premium SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,",
+"03000000242e00006a38000000000000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,",
+"03000000d81d00000e00000000000000,iBuffalo AC02 Arcade Joystick,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,rightx:a2,righty:a5,start:b8,x:b4,y:b5,",
+"03000000d81d00000f00000000000000,iBuffalo BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000d81d00001000000000000000,iBuffalo BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"030000005c0a00000285000000000000,iDroidCon,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b6,",
+"03000000696400006964000000000000,iDroidCon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000511d00000230000000000000,iGUGU Gamecore,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b1,leftstick:b4,lefttrigger:b3,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b2,",
+"03000000b50700001403000000000000,Impact Black,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,",
+"030000006f0e00002401000000000000,Injustice Fightstick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"03000000830500005130000000000000,InterAct ActionPad,a:b0,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,",
+"03000000ef0500000300000000000000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,",
+"03000000fd0500000230000000000000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a5,start:b11,x:b0,y:b1,",
+"03000000fd0500000030000000000000,Interact GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,",
+"03000000fd0500003902000000000000,InterAct Hammerhead,a:b3,b:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b2,lefttrigger:b8,rightshoulder:b7,rightstick:b5,righttrigger:b9,start:b10,x:b0,y:b1,",
+"03000000fd0500002a26000000000000,InterAct Hammerhead FX,a:b3,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b0,y:b1,",
+"03000000fd0500002f26000000000000,InterAct Hammerhead FX,a:b4,b:b5,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b1,y:b2,",
+"03000000fd0500005302000000000000,InterAct ProPad,a:b3,b:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,",
+"03000000ac0500002c02000000000000,Ipega Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000491900000204000000000000,Ipega PG9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000491900000304000000000000,Ipega PG9087,+righty:+a5,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,",
+"030000007e0500000620000000000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,",
+"030000007e0500000720000000000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,",
+"03000000250900000017000000000000,Joypad Adapter,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,",
+"03000000bd12000003c0000000000000,Joypad Alpha Shock,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000ff1100004033000000000000,JPD FFB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a2,start:b15,x:b3,y:b0,",
+"03000000242f00002d00000000000000,JYS Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000242f00008a00000000000000,JYS Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,",
+"03000000c4100000c082000000000000,KADE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000828200000180000000000000,Keio,a:b4,b:b5,back:b8,leftshoulder:b2,lefttrigger:b3,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b9,x:b0,y:b1,",
+"03000000790000000200000000000000,King PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,",
+"03000000bd12000001e0000000000000,Leadership,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+"030000006f0e00000103000000000000,Logic3,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e00000104000000000000,Logic3,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000008f0e00001300000000000000,Logic3,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"030000006d040000d1ca000000000000,Logitech ChillStream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006d040000d2ca000000000000,Logitech Cordless Precision,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,",
+"030000006d04000016c2000000000000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006d0400001dc2000000000000,Logitech F310,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006d04000018c2000000000000,Logitech F510,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006d0400001ec2000000000000,Logitech F510,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006d04000019c2000000000000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006d0400001fc2000000000000,Logitech F710,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006d0400001ac2000000000000,Logitech Precision,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000006d04000009c2000000000000,Logitech WingMan,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,",
+"030000006d0400000bc2000000000000,Logitech WingMan Action Pad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b8,lefttrigger:a5~,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:a2~,start:b8,x:b3,y:b4,",
+"030000006d0400000ac2000000000000,Logitech WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,",
+"03000000380700005645000000000000,Lynx,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000222200006000000000000000,Macally,a:b1,b:b2,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b33,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000380700003888000000000000,Mad Catz Arcade Fightstick TE S Plus PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000380700008532000000000000,Mad Catz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000380700006352000000000000,Mad Catz CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000380700006652000000000000,Mad Catz CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,",
+"03000000380700005032000000000000,Mad Catz Fightpad Pro PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000380700005082000000000000,Mad Catz Fightpad Pro PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000380700008031000000000000,Mad Catz FightStick Alpha PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000003807000038b7000000000000,Mad Catz Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,",
+"03000000380700008433000000000000,Mad Catz Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000380700008483000000000000,Mad Catz Fightstick TE S PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000380700008134000000000000,Mad Catz Fightstick TE2 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000380700008184000000000000,Mad Catz Fightstick TE2 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000380700006252000000000000,Mad Catz Micro CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,",
+"03000000380700008232000000000000,Mad Catz PlayStation Brawlpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000380700008731000000000000,Mad Catz PlayStation Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000003807000056a8000000000000,Mad Catz PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000380700001888000000000000,Mad Catz SFIV Fightstick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000380700008081000000000000,Mad Catz SFV Arcade Fightstick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000380700001847000000000000,Mad Catz Street Fighter 4 Xbox 360 FightStick,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,",
+"03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000002a0600001024000000000000,Matricom,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,",
+"030000009f000000adbb000000000000,MaxJoypad Virtual Controller,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+"03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,",
+"030000008f0e00001330000000000000,Mayflash Controller Adapter,a:b1,b:b2,back:b8,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3~,righty:a2,start:b9,x:b0,y:b3,",
+"03000000242f00003700000000000000,Mayflash F101,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"03000000790000003018000000000000,Mayflash F300 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"03000000242f00003900000000000000,Mayflash F300 Elite Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,",
+"03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,",
+"03000000242f00007300000000000000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,",
+"0300000079000000d218000000000000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000d620000010a7000000000000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000242f0000f500000000000000,Mayflash N64 Adapter,a:b2,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,",
+"03000000242f0000f400000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,",
+"03000000790000007918000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,righttrigger:b7,rightx:a3,righty:a2,start:b8,",
+"030000008f0e00001030000000000000,Mayflash Saturn Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,",
+"0300000025090000e803000000000000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,",
+"03000000790000000318000000000000,Mayflash Wii DolphinBar,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,",
+"03000000790000000018000000000000,Mayflash Wii U Pro Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000790000002418000000000000,Mega Drive Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b2,start:b9,x:b3,y:b4,",
+"0300000079000000ae18000000000000,Mega Drive Controller,a:b0,b:b1,back:b7,dpdown:b14,dpleft:b15,dpright:b13,dpup:b2,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,",
+"03000000c0160000990a000000000000,Mega Drive Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,righttrigger:b2,start:b3,",
+"030000005e0400002800000000000000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,",
+"030000005e0400000300000000000000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,",
+"030000005e0400000700000000000000,Microsoft SideWinder,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,",
+"030000005e0400000e00000000000000,Microsoft SideWinder Freestyle Pro,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b8,x:b3,y:b4,",
+"030000005e0400002700000000000000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,",
+"03000000280d00000202000000000000,Miller Lite Cantroller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b5,x:b2,y:b3,",
+"03000000ad1b000023f0000000000000,MLG,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a6,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
+"03000000ad1b00003ef0000000000000,MLG Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,",
+"03000000380700006382000000000000,MLG PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000004523000015e0000000000000,Mobapad Chitu HD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000491900000904000000000000,Mobapad Chitu HD,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000ffff00000000000000000000,Mocute M053,a:b3,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b11,leftstick:b7,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b6,righttrigger:b4,rightx:a3,righty:a4,start:b8,x:b1,y:b0,",
+"03000000d6200000e589000000000000,Moga 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a2,righty:a5,start:b7,x:b2,y:b3,",
+"03000000d62000007162000000000000,Moga Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a2,righty:a5,start:b7,x:b2,y:b3,",
+"03000000d6200000ad0d000000000000,Moga Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000c62400002a89000000000000,Moga XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000c62400002b89000000000000,Moga XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000c62400001a89000000000000,Moga XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000c62400001b89000000000000,Moga XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000250900006688000000000000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+"03000000091200004488000000000000,MUSIA PlayStation 2 Input Display,a:b0,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:b11,rightx:a2,righty:a3,start:b5,x:b1,y:b3,",
+"03000000f70600000100000000000000,N64 Adaptoid,+rightx:b2,+righty:b1,-rightx:b4,-righty:b5,a:b0,b:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,",
+"030000006b140000010c000000000000,Nacon GC 400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"030000006b1400001106000000000000,Nacon Revolution 3 PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"0300000085320000170d000000000000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"0300000085320000190d000000000000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000006b140000100d000000000000,Nacon Revolution Infinity PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000006b140000080d000000000000,Nacon Revolution Unlimited Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000bd12000001c0000000000000,Nebular,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b0,",
+"03000000eb0300000000000000000000,NeGcon Adapter,a:a2,b:b13,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,lefttrigger:a4,leftx:a1,righttrigger:b11,start:b3,x:a3,y:b12,",
+"0300000038070000efbe000000000000,NEO SE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"0300000092120000474e000000000000,NeoGeo X Arcade Stick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b3,y:b2,",
+"03000000921200004b46000000000000,NES 2 port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,",
+"03000000000f00000100000000000000,NES Controller,a:b1,b:b0,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,",
+"03000000921200004346000000000000,NES Controller,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,",
+"03000000790000004518000000000000,NEXILUX GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,",
+"030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,",
+"03000000050b00000045000000000000,Nexus,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,",
+"03000000152000000182000000000000,NGDS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,",
+"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"030000000d0500000308000000000000,Nostromo N45,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,",
+"030000007e0500001920000000000000,NSO N64 Controller,+rightx:b8,+righty:b2,-rightx:b3,-righty:b7,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,",
+"030000007e0500001720000000000000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b15,start:b9,x:b2,y:b3,",
+"03000000550900001472000000000000,NVIDIA Controller,a:b11,b:b10,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b5,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b4,righttrigger:a5,rightx:a3,righty:a6,start:b3,x:b9,y:b8,",
+"03000000550900001072000000000000,NVIDIA Shield,a:b9,b:b8,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b3,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b2,righttrigger:a4,rightx:a2,righty:a5,start:b0,x:b7,y:b6,",
+"030000005509000000b4000000000000,NVIDIA Virtual,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000120c00000288000000000000,Nyko Air Flo Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
+"030000004b120000014d000000000000,NYKO Airflo EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,",
+"03000000d62000001d57000000000000,Nyko Airflo PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000791d00000900000000000000,Nyko Playpad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a5,start:b11,x:b3,y:b4,",
+"03000000782300000a10000000000000,Onlive Controller,a:b15,b:b14,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b11,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b13,y:b12,",
+"030000000d0f00000401000000000000,Onyx,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000008916000001fd000000000000,Onza CE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a3,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000008916000000fd000000000000,Onza TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000d62000006d57000000000000,OPP PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006b14000001a1000000000000,Orange Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,",
+"0300000009120000072f000000000000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:-a2,leftx:a0,lefty:a1,righttrigger:-a5,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000362800000100000000000000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,",
+"03000000120c0000f60e000000000000,P4 Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,",
+"03000000790000002201000000000000,PC Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"030000006f0e00008501000000000000,PDP Fightpad Pro GameCube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"030000006f0e00000901000000000000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000006f0e00008901000000000000,PDP Realmz Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000008f0e00004100000000000000,PlaySega,a:b1,b:b0,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b4,y:b3,",
+"03000000d620000011a7000000000000,PowerA Core Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000dd62000015a7000000000000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000d620000012a7000000000000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000dd62000016a7000000000000,PowerA Fusion Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000d620000013a7000000000000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000d62000003340000000000000,PowerA OPS Pro Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000d62000002640000000000000,PowerA OPS Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"0300000062060000d570000000000000,PowerA PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000d620000014a7000000000000,PowerA Spectra Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006d04000084ca000000000000,Precision,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
+"03000000d62000009557000000000000,Pro Elite PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000c62400001a53000000000000,Pro Ex Mini,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000d62000009f31000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000d6200000c757000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000120c0000110e000000000000,Pro5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000100800000100000000000000,PS1 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+"030000008f0e00007530000000000000,PS1 Controller,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000100800000300000000000000,PS2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,",
+"03000000250900000088000000000000,PS2 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+"03000000250900006888000000000000,PS2 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b6,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+"03000000250900008888000000000000,PS2 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+"030000006b1400000303000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"030000009d0d00001330000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000151a00006222000000000000,PS2 Dual Plus Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+"03000000120a00000100000000000000,PS3 Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,",
+"03000000120c00001307000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000120c00001cf1000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000120c0000f90e000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000250900000118000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+"03000000250900000218000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+"03000000250900000500000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,",
+"030000004c0500006802000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b10,lefttrigger:a3~,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:a4~,rightx:a2,righty:a5,start:b8,x:b3,y:b0,",
+"030000004f1f00000800000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"03000000632500007505000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000888800000803000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b0,",
+"03000000888800000804000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
+"030000008f0e00000300000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,",
+"030000008f0e00001431000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000ba2200002010000000000000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b2,",
+"03000000120c00000807000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000111e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000121e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000130e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000150e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000180e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000181e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000191e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c00001e0e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000a957000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000aa57000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000f21c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000f31c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000f41c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000f51c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000f70e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000160e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000001a1e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000004c0500005f0e000000000000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000004c050000e60c000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000004c050000f20d000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000830500005020000000000000,PSX,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b2,y:b3,",
+"03000000300f00000111000000000000,Qanba 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000300f00000211000000000000,Qanba 2P,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000300f00000011000000000000,Qanba Arcade Stick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,",
+"03000000300f00001611000000000000,Qanba Arcade Stick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,",
+"03000000222c00000025000000000000,Qanba Dragon Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000222c00000020000000000000,Qanba Drone Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,",
+"03000000300f00001211000000000000,Qanba Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000300f00001210000000000000,Qanba Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,",
+"03000000341a00000104000000000000,Qanba Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,",
+"03000000222c00000223000000000000,Qanba Obsidian Arcade Stick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000222c00000023000000000000,Qanba Obsidian Arcade Stick PS4,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000008a2400006682000000000000,R1 Mobile Controller,a:b3,b:b1,back:b7,leftx:a0,lefty:a1,start:b6,x:b4,y:b0,",
+"03000000086700006626000000000000,RadioShack,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,",
+"03000000ff1100004733000000000000,Ramox FPS Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,",
+"030000009b2800002300000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,",
+"030000009b2800006900000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,",
+"030000009b2800000800000000000000,Raphnet Dreamcast Adapter,a:b2,b:b1,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,lefttrigger:a2,leftx:a0,righttrigger:a3,righty:a1,start:b3,x:b10,y:b9,",
+"030000009b280000d000000000000000,Raphnet Dreamcast Adapter,a:b1,b:b0,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,lefttrigger:+a5,leftx:a0,lefty:a1,righttrigger:+a2,start:b3,x:b5,y:b4,",
+"030000009b2800006200000000000000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,",
+"030000009b2800003200000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,",
+"030000009b2800006000000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,",
+"030000009b2800001800000000000000,Raphnet Jaguar Adapter,a:b2,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b10,start:b3,x:b11,y:b12,",
+"030000009b2800003c00000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,",
+"030000009b2800006100000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,",
+"030000009b2800006300000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,",
+"030000009b2800006400000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,",
+"030000009b2800000200000000000000,Raphnet NES Adapter,a:b7,b:b6,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b4,",
+"030000009b2800004400000000000000,Raphnet PS1 and PS2 Adapter,a:b1,b:b2,back:b5,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b9,rightx:a3,righty:a4,start:b4,x:b0,y:b3,",
+"030000009b2800004300000000000000,Raphnet Saturn,a:b0,b:b1,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,",
+"030000009b2800000500000000000000,Raphnet Saturn Adapter 2.0,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,",
+"030000009b2800000300000000000000,Raphnet SNES Adapter,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,",
+"030000009b2800002600000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,",
+"030000009b2800002e00000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,",
+"030000009b2800002f00000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,",
+"030000009b2800005600000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,",
+"030000009b2800005700000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,",
+"030000009b2800001e00000000000000,Raphnet Vectrex Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a1,lefty:a2,x:b2,y:b3,",
+"030000009b2800002b00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,",
+"030000009b2800002c00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,",
+"030000009b2800008000000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,",
+"03000000790000008f18000000000000,Rapoo Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b3,y:b0,",
+"0300000032150000a602000000000000,Razer Huntsman V3 Pro,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b12,dpright:b13,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000f8270000bf0b000000000000,Razer Kishi,a:b6,b:b7,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b18,leftshoulder:b12,leftstick:b19,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b13,rightstick:b20,righttrigger:b15,rightx:a3,righty:a4,start:b17,x:b9,y:b10,",
+"03000000321500000204000000000000,Razer Panthera PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000321500000104000000000000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000321500000010000000000000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000321500000507000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000321500000707000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000321500000710000000000000,Razer Raiju TE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000321500000a10000000000000,Razer Raiju TE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000321500000410000000000000,Razer Raiju UE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000321500000910000000000000,Razer Raiju UE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000321500000011000000000000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000321500000009000000000000,Razer Serval,+lefty:+a2,-lefty:-a1,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,leftx:a0,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000921200004547000000000000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b6,x:b3,y:b4,",
+"03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,",
+"03000000830500006020000000000000,Retro Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b8,righttrigger:b9,start:b7,x:b2,y:b3,",
+"03000000632500007805000000000000,Retro Fighters Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"0300000003040000c197000000000000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,",
+"03000000bd12000013d0000000000000,Retrolink Sega Saturn Classic Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,lefttrigger:b6,rightshoulder:b2,righttrigger:b7,start:b8,x:b3,y:b4,",
+"03000000bd12000015d0000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,",
+"03000000341200000400000000000000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,",
+"0300000000f000000300000000000000,RetroUSB RetroPad,a:b1,b:b5,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b4,",
+"0300000000f00000f100000000000000,RetroUSB Super RetroPort,a:b1,b:b5,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b4,",
+"03000000830500000960000000000000,Revenger,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b3,x:b4,y:b5,",
+"030000006b140000010d000000000000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000006b140000020d000000000000,Revolution Pro Controller 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000006b140000130d000000000000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000006f0e00001f01000000000000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e00004601000000000000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000c6240000fefa000000000000,Rock Candy Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e00008701000000000000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006f0e00001e01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006f0e00002801000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006f0e00002f01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000830500007030000000000000,Rockfire Space Ranger,a:b0,b:b1,back:b5,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b9,righttrigger:b8,start:b2,x:b3,y:b4,",
+"03000000050b0000e318000000000000,ROG Chakram,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,",
+"03000000050b0000e518000000000000,ROG Chakram,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,",
+"03000000050b00005819000000000000,ROG Chakram Core,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,",
+"03000000050b0000181a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,",
+"03000000050b00001a1a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,",
+"03000000050b00001c1a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,",
+"030000004f04000001d0000000000000,Rumble Force,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,",
+"030000000d0f0000ad00000000000000,RX Gamepad,a:b0,b:b4,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,rightshoulder:b6,start:b9,x:b2,y:b1,",
+"030000008916000000fe000000000000,Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000c6240000045d000000000000,Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e00001311000000000000,Saffun Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b0,",
+"03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,",
+"03000000a306000023f6000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,",
+"03000000300f00001201000000000000,Saitek Dual Analog,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,",
+"03000000a30600000701000000000000,Saitek P220,a:b2,b:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,x:b0,y:b1,",
+"03000000a30600000cff000000000000,Saitek P2500 Force Rumble,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b0,y:b1,",
+"03000000a30600000d5f000000000000,Saitek P2600,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,",
+"03000000a30600000dff000000000000,Saitek P2600,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b8,x:b0,y:b3,",
+"03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,",
+"03000000a306000018f5000000000000,Saitek P3200,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,",
+"03000000300f00001001000000000000,Saitek P480 Rumble,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,",
+"03000000a30600000901000000000000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b8,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b5,rightx:a3,righty:a2,x:b0,y:b1,",
+"03000000a30600000b04000000000000,Saitek P990,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,",
+"03000000a30600002106000000000000,Saitek PS1000 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,",
+"03000000a306000020f6000000000000,Saitek PS2700 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,",
+"03000000300f00001101000000000000,Saitek Rumble,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,",
+"03000000e804000000a0000000000000,Samsung EIGP20,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000c01100000252000000000000,Sanwa Easy Grip,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,",
+"03000000c01100004350000000000000,Sanwa Micro Grip P3,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,x:b3,y:b2,",
+"03000000411200004550000000000000,Sanwa Micro Grip Pro,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a1,righty:a2,start:b9,x:b1,y:b3,",
+"03000000c01100004150000000000000,Sanwa Micro Grip Pro,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,",
+"03000000c01100004450000000000000,Sanwa Online Grip,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b11,righttrigger:b9,rightx:a3,righty:a2,start:b14,x:b3,y:b4,",
+"03000000730700000401000000000000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,",
+"03000000830500006120000000000000,Sanwa Smart Grip II,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,x:b1,y:b3,",
+"03000000c01100000051000000000000,Satechi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,",
+"030000004f04000028b3000000000000,Score A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000952e00002577000000000000,Scuf PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000a30c00002500000000000000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,",
+"03000000a30c00002400000000000000,Sega Mega Drive Mini 6B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,",
+"03000000d804000086e6000000000000,Sega Multi Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a2,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,",
+"0300000000050000289b000000000000,Sega Saturn Adapter,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,",
+"0300000000f000000800000000000000,Sega Saturn Controller,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,righttrigger:b3,start:b0,x:b5,y:b6,",
+"03000000730700000601000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,",
+"03000000b40400000a01000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,",
+"030000003b07000004a1000000000000,SFX,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,",
+"03000000f82100001900000000000000,Shogun Bros Chameleon X1,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+"03000000120c00001c1e000000000000,SnakeByte 4S PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000140300000918000000000000,SNES Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,",
+"0300000081170000960a000000000000,SNES Controller,a:b4,b:b0,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b5,y:b1,",
+"03000000811700009d0a000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,",
+"030000008b2800000300000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,",
+"03000000921200004653000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,",
+"030000008f0e00000910000000000000,Sony DualShock 2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,",
+"03000000317300000100000000000000,Sony DualShock 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,",
+"03000000666600006706000000000000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,",
+"03000000e30500009605000000000000,Sony PlayStation Adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+"03000000fe1400002a23000000000000,Sony PlayStation Adapter,a:b0,b:b1,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,x:b2,y:b3,",
+"030000004c050000da0c000000000000,Sony PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,",
+"03000000632500002306000000000000,Sony PlayStation Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000f0250000c183000000000000,Sony PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000d9040000160f000000000000,Sony PlayStation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+"03000000ff000000cb01000000000000,Sony PlayStation Portable,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,",
+"030000004c0500003713000000000000,Sony PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,",
+"03000000341a00000208000000000000,Speedlink 6555,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,",
+"03000000341a00000908000000000000,Speedlink 6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000380700001722000000000000,Speedlink Competition Pro,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,x:b2,y:b3,",
+"030000008f0e00000800000000000000,Speedlink Strike FX,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000c01100000591000000000000,Speedlink Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000de280000fc11000000000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000de280000ff11000000000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000120c0000160e000000000000,Steel Play Metaltech PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000110100001914000000000000,SteelSeries,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000381000001214000000000000,SteelSeries Free,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000110100003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000381000003014000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000381000003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000381000001814000000000000,SteelSeries Stratus XL,a:b0,b:b1,back:b18,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b19,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b2,y:b3,",
+"03000000380700003847000000000000,Street Fighter Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,start:b7,x:b2,y:b3,",
+"030000001f08000001e4000000000000,Super Famicom Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,",
+"03000000790000000418000000000000,Super Famicom Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b33,rightshoulder:b5,start:b7,x:b2,y:b3,",
+"03000000341200001300000000000000,Super Racer,a:b2,b:b3,back:b8,leftshoulder:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b7,x:b0,y:b1,",
+"03000000457500002211000000000000,Szmy Power PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000004f0400000ab1000000000000,T16000M,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b10,x:b2,y:b3,",
+"030000000d0f00007b00000000000000,TAC GEAR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000e40a00000307000000000000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,",
+"03000000e40a00000207000000000000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b9,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,",
+"03000000d814000001a0000000000000,TE Kitty,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,",
+"03000000ba2200000701000000000000,Technology Innovation PS2 Adapter,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b2,",
+"03000000c61100001000000000000000,Tencent Xianyou Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,x:b3,y:b4,",
+"03000000790000001c18000000000000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000790000002601000000000000,TGZ Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,",
+"03000000591c00002400000000000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,",
+"03000000591c00002600000000000000,THEGamepad,a:b2,b:b1,back:b6,leftx:a0,lefty:a1,start:b7,x:b3,y:b0,",
+"030000004f04000015b3000000000000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,",
+"030000004f04000023b3000000000000,Thrustmaster Dual Trigger PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000004f0400000ed0000000000000,Thrustmaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000004f04000008d0000000000000,Thrustmaster Ferrari 150 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,",
+"030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,",
+"030000004f04000003d0000000000000,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000004f04000009d0000000000000,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006d04000088ca000000000000,Thunderpad,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
+"03000000666600000288000000000000,TigerGame PlayStation Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+"03000000666600000488000000000000,TigerGame PlayStation Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+"030000004f04000007d0000000000000,TMini,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000571d00002100000000000000,Tomee NES Controller Adapter,a:b1,b:b0,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,start:b3,",
+"03000000571d00002000000000000000,Tomee SNES Controller Adapter,a:b0,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,",
+"03000000d62000006000000000000000,Tournament PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000c01100000055000000000000,Tronsmart,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"030000005f140000c501000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000b80500000210000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"030000004f04000087b6000000000000,TWCS Throttle,dpdown:b8,dpleft:b9,dpright:b7,dpup:b6,leftstick:b5,lefttrigger:-a5,leftx:a0,lefty:a1,righttrigger:+a5,",
+"03000000411200000450000000000000,Twin Shock,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a4,start:b11,x:b3,y:b0,",
+"03000000d90400000200000000000000,TwinShock PS2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+"03000000151900005678000000000000,Uniplay U6,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000101c0000171c000000000000,uRage Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"030000000b0400003065000000000000,USB Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,",
+"03000000242f00006e00000000000000,USB Controller,a:b1,b:b4,back:b10,leftshoulder:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b7,rightx:a2,righty:a5,start:b11,x:b0,y:b3,",
+"03000000300f00000701000000000000,USB Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+"03000000341a00002308000000000000,USB Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000666600000188000000000000,USB Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+"030000006b1400000203000000000000,USB Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000790000000a00000000000000,USB Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,",
+"03000000b404000081c6000000000000,USB Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,",
+"03000000b50700001503000000000000,USB Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b0,y:b1,",
+"03000000bd12000012d0000000000000,USB Controller,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,",
+"03000000ff1100004133000000000000,USB Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,",
+"03000000632500002305000000000000,USB Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000882800000305000000000000,V5 Game Pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,x:b2,y:b3,",
+"03000000790000001a18000000000000,Venom PS4 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000790000001b18000000000000,Venom PS4 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000006f0e00000302000000000000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000006f0e00000702000000000000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,",
+"0300000034120000adbe000000000000,vJoy Device,a:b0,b:b1,back:b15,dpdown:b6,dpleft:b7,dpright:b8,dpup:b5,guide:b16,leftshoulder:b9,leftstick:b13,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b14,righttrigger:b12,rightx:a3,righty:a4,start:b4,x:b2,y:b3,",
+"03000000120c0000ab57000000000000,Warrior Joypad JS083,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000007e0500003003000000000000,Wii U Pro,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b6,leftstick:b11,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b12,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,",
+"0300000032150000030a000000000000,Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"0300000032150000140a000000000000,Wolverine,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000002e160000efbe000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,rightshoulder:b5,righttrigger:b11,start:b7,x:b2,y:b3,",
+"03000000380700001647000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000380700002045000000000000,Xbox 360 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000380700002644000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a2,righty:a5,start:b8,x:b2,y:b3,",
+"03000000380700002647000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000003807000026b7000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000380700003647000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a7,righty:a5,start:b7,x:b2,y:b3,",
+"030000005e0400001907000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e0400008e02000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e0400009102000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000ad1b000000fd000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000ad1b000001fd000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000ad1b000016f0000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000ad1b00008e02000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000c62400000053000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000c6240000fdfa000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000380700002847000000000000,Xbox 360 Fightpad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000a102000000000000,Xbox 360 Wireless Receiver,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000120c00000a88000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a2,righty:a4,start:b6,x:b2,y:b3,",
+"03000000120c00001088000000000000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2~,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5~,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000002a0600002000000000000000,Xbox Controller,a:b0,b:b1,back:b13,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b5,leftstick:b14,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b15,righttrigger:b7,rightx:a2,righty:a5,start:b12,x:b2,y:b3,",
+"03000000380700001645000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
+"03000000380700002645000000000000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000380700003645000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
+"03000000380700008645000000000000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e0400000202000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
+"030000005e0400008502000000000000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e0400008702000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
+"030000005e0400008902000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b10,leftstick:b8,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b9,righttrigger:b4,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
+"030000005e0400000c0b000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000d102000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000dd02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000e002000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000e302000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000ea02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000fd02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000ff02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e0000a802000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e0000c802000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000c62400003a54000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000130b000000000000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000341a00000608000000000000,Xeox,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000450c00002043000000000000,Xeox SL6556BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"030000006f0e00000300000000000000,XGear,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b3,y:b0,",
+"03000000e0ff00000201000000000000,Xiaomi Black Shark (L),back:b0,dpdown:b11,dpleft:b9,dpright:b10,dpup:b8,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,",
+"03000000172700004431000000000000,Xiaomi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,",
+"03000000172700003350000000000000,Xiaomi XMGP01YM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000bc2000005060000000000000,Xiaomi XMGP01YM,+lefty:+a2,+righty:+a5,-lefty:-a1,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,",
+"xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000007d0400000340000000000000,Xterminator Digital Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:-a4,lefttrigger:+a4,leftx:a0,lefty:a1,paddle1:b7,paddle2:b6,rightshoulder:b5,rightstick:b9,righttrigger:b2,rightx:a3,righty:a5,start:b8,x:b3,y:b4,",
+"030000002c3600000100000000000000,Yawman Arrow,+rightx:h0.2,+righty:h0.4,-rightx:h0.8,-righty:h0.1,a:b4,b:b5,back:b6,dpdown:b15,dpleft:b14,dpright:b16,dpup:b13,leftshoulder:b10,leftstick:b0,lefttrigger:-a4,leftx:a0,lefty:a1,paddle1:b11,paddle2:b12,rightshoulder:b8,rightstick:b9,righttrigger:+a4,start:b3,x:b1,y:b2,",
+"03000000790000004f18000000000000,ZDT Android Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"03000000120c00000500000000000000,Zeroplus Adapter,a:b2,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,",
+"03000000120c0000101e000000000000,Zeroplus P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,"
+#elif defined(__APPLE__)
+"030000008f0e00000300000009010000,2 In 1 Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000c82d00000031000001000000,8BitDo Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00000531000000020000,8BitDo Adapter 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00000951000000010000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,",
+"03000000c82d00000090000001000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00006a28000000010000,8BitDo GameCube,a:b0,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b9,paddle2:b8,rightshoulder:b10,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b1,y:b4,",
+"03000000c82d00001251000000010000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00001251000000020000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00001151000000010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00001151000000020000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000a30c00002400000006020000,8BitDo M30,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,guide:b9,leftshoulder:b6,lefttrigger:b5,rightshoulder:b4,righttrigger:b7,start:b8,x:b3,y:b0,",
+"03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00005106000000010000,8BitDo M30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b2,leftshoulder:b6,lefttrigger:a5,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,",
+"03000000c82d00002090000000010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000451000000010000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,",
+"03000000c82d00001590000001000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00006928000000010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,",
+"03000000c82d00002590000000010000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00002590000001000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00002690000000010000,8BitDo NEOGEO,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b10,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,",
+"030000003512000012ab000001000000,8BitDo NES30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"03000000c82d000012ab000001000000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,",
+"03000000c82d00002028000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,",
+"03000000022000000090000001000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000203800000900000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000190000001000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000751000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00000851000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000660000000020000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000131000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000231000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000331000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000431000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00002867000000010000,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b3,y:b4,",
+"03000000c82d00003028000000010000,8Bitdo SFC30 Gamepad,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"03000000102800000900000000000000,8BitDo SFC30 Joystick,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"03000000c82d00000351000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00001290000001000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"03000000c82d00004028000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"03000000c82d00000160000001000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,",
+"03000000c82d00000260000001000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00001b30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00001d30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00001530000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00001630000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00001730000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00001130000000020000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00001330000001000000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00001330000000020000,8BitDo Ultimate Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,",
+"03000000a00500003232000009010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,",
+"03000000c82d00001890000001000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"03000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a31,start:b11,x:b4,y:b3,",
+"03000000491900001904000001010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,",
+"03000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"0300000008100000e501000019040000,Anbernic Handheld,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b4,y:b3,",
+"03000000373500004610000001000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000a30c00002700000003030000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,",
+"03000000a30c00002800000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,",
+"03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000050b00000579000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b42,paddle1:b9,paddle2:b11,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,",
+"03000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b23,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,",
+"03000000503200000110000045010000,Atari VCS Classic,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,",
+"03000000503200000110000047010000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,",
+"03000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,",
+"030000008a3500000102000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,",
+"030000008a3500000201000000010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000008a3500000202000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,",
+"030000008a3500000402000000010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000008a3500000302000000010000,Backbone One PlayStation Edition,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,",
+"03000000c62400001a89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,",
+"03000000c62400001b89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000d62000002a79000000010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000200e000000010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000210e000000010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000d8140000cecf000000000000,Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,",
+"03000000a306000022f6000001030000,Cyborg V3 Rumble Pad PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,",
+"03000000791d00000103000009010000,Dual Box Wii Classic Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"030000006e0500000720000010020000,Elecom JC-W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,",
+"030000006f0e00008401000003010000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000151900004000000001000000,Flydigi Vader 2,a:b14,b:b15,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b0,y:b1,",
+"03000000b40400001124000001040000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000b40400001224000003030000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000790000004618000000010000,GameCube Controller Adapter,a:b4,b:b0,dpdown:b56,dpleft:b60,dpright:b52,dpup:b48,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,",
+"03000000ac0500001a06000002020000,GameSir-T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000ad1b000001f9000000000000,Gamestop BB070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000c01100000140000000010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000006f0e00000102000000000000,GameStop Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+"03000000ff1100003133000007010000,GameWare PC Control Pad,a:b2,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b0,",
+"03000000d11800000094000000010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,",
+"030000007d0400000540000001010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"03000000280400000140000000020000,Gravis GamePad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000008f0e00000300000007010000,GreenAsia Joystick,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,",
+"030000000d0f00002d00000000100000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00005f00000000000000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00005f00000000010000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00005e00000000000000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000000d0f00005e00000000010000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000000d0f00008400000000010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f00008500000000010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000341a00000302000014010000,Hori Fighting Stick Mini,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000000d0f00008800000000010000,Hori Fighting Stick mini 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000000d0f00008700000000010000,Hori Fighting Stick mini 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000000d0f00004d00000000000000,Hori Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00003801000008010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,",
+"030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000000d0f0000aa00000072050000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"030000000d0f00000002000017010000,Hori Split Pad Fit,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00000002000015010000,Hori Switch Split Pad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00006e00000000010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00006600000000010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000000d0f00006600000000000000,Horipad FPS Plus 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f0000ee00000000010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f0000c100000072050000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000242e0000ff0b000000010000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,",
+"03000000790000004e95000000010000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,",
+"03000000830500006020000000000000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,",
+"03000000ef0500000300000000020000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,",
+"03000000fd0500000030000010010000,Interact GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,",
+"030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,",
+"030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,",
+"03000000242f00002d00000007010000,JYS Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"030000006d04000019c2000000000000,Logitech Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006d04000016c2000000000000,Logitech F310,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006d04000018c2000000000000,Logitech F510,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006d04000019c2000005030000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006d0400001fc2000000000000,Logitech F710,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+"030000006d04000018c2000000010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,",
+"03000000380700005032000000010000,Mad Catz PS3 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000380700008433000000010000,Mad Catz PS3 Fightstick TE S Plus,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000380700005082000000010000,Mad Catz PS4 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000380700008483000000010000,Mad Catz PS4 Fightstick TE S Plus,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"0300000049190000020400001b010000,Manba One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b22,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000790000000600000007010000,Marvo GT-004,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"030000008f0e00001330000011010000,Mayflash Controller Adapter,a:b2,b:b4,back:b16,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b12,lefttrigger:b16,leftx:a0,lefty:a2,rightshoulder:b14,rightx:a6~,righty:a4,start:b18,x:b0,y:b6,",
+"03000000790000004318000000010000,Mayflash GameCube Adapter,a:b4,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,",
+"03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,",
+"03000000242f00007300000000020000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,",
+"0300000079000000d218000026010000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000d620000010a7000003010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000008f0e00001030000011010000,Mayflash Saturn Adapter,a:b0,b:b2,dpdown:b28,dpleft:b30,dpright:b26,dpup:b24,leftshoulder:b10,lefttrigger:b14,rightshoulder:b12,righttrigger:b4,start:b18,x:b6,y:b8,",
+"0300000025090000e803000000000000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,",
+"03000000790000000318000000010000,Mayflash Wii DolphinBar,a:b8,b:b12,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b44,leftshoulder:b16,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b4,",
+"03000000790000000018000000000000,Mayflash Wii U Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,",
+"03000000790000000018000000010000,Mayflash Wii U Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,",
+"030000005e0400002800000002010000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,",
+"030000005e0400000300000006010000,Microsoft SideWinder,a:b0,b:b1,back:b9,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,",
+"030000005e0400000700000006010000,Microsoft SideWinder,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,",
+"030000005e0400002700000001010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,",
+"030000004523000015e0000072050000,Mobapad Chitu HD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000d62000007162000001000000,Moga Pro 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"03000000c62400002a89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c62400002b89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000632500007505000000020000,NeoGeo mini PAD Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b2,y:b3,",
+"03000000921200004b46000003020000,NES 2-port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,",
+"030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,",
+"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"030000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"030000007e0500000920000010020000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,",
+"050000007e05000009200000ff070000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,",
+"030000007e0500001920000001000000,NSO N64 Controller,+rightx:b8,+righty:b7,-rightx:b3,-righty:b2,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,",
+"030000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b15,start:b9,x:b2,y:b3,",
+"03000000550900001472000025050000,NVIDIA Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,",
+"030000004b120000014d000000010000,Nyko Airflo EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,",
+"0300000009120000072f000000010000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a2,leftx:a0,lefty:a1,righttrigger:a5,rightx:a3,righty:a4,start:b11,x:b3,y:b4,",
+"030000006f0e00000901000002010000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000008f0e00000300000000000000,Piranha Xtreme PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+"03000000d620000011a7000000020000,PowerA Core Plus Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000d620000011a7000010050000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000d62000006dca000000010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000100800000300000006010000,PS2 Adapter,a:b2,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a3,start:b9,x:b3,y:b0,",
+"030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
+"030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
+"030000004c0500006802000072050000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
+"030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"0300004b4c0500005f0e000000010000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"050000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000005e040000e002000001000000,PXN P30 Pro Mobile,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000222c00000225000000010000,Qanba Dragon Arcade Joystick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000222c00000020000000010000,Qanba Drone Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000009b2800005600000020020000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,",
+"030000009b2800008000000022020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,",
+"030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+"03000000321500000204000000010000,Razer Panthera PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000321500000104000000010000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000321500000010000000010000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000321500000507000001010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000321500000011000000010000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000321500000009000000020000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,",
+"030000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,",
+"0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+"03000000632500008005000000010000,Redgear,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000632500002305000000010000,Redragon Saturn,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000921200004547000000020000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b2,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,lefttrigger:b14,rightshoulder:b10,righttrigger:b4,start:b12,x:b6,y:b8,",
+"03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,",
+"03000000790000001100000005010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,",
+"03000000830500006020000000010000,Retro Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b8,righttrigger:b9,start:b7,x:b2,y:b3,",
+"0300000003040000c197000000000000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,",
+"03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,",
+"03000000341200000400000000000000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,",
+"030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000006b140000130d000000010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000004c0500006802000002100000,Rii RK707,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b3,righttrigger:b9,rightx:a2,righty:a3,start:b1,x:b15,y:b12,",
+"030000006f0e00008701000005010000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000c6240000fefa000000000000,Rock Candy PS3,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+"03000000e804000000a000001b010000,Samsung EIGP20,a:b1,b:b3,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b11,leftx:a1,lefty:a3,rightshoulder:b12,rightx:a4,righty:a5,start:b16,x:b7,y:b9,",
+"03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,",
+"03000000a30c00002500000006020000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,",
+"03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,",
+"03000000b40400000a01000000000000,Sega Saturn,a:b0,b:b1,back:b5,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b2,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,",
+"030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"0300000000f00000f100000000000000,SNES RetroPort,a:b2,b:b3,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,rightshoulder:b7,start:b6,x:b0,y:b1,",
+"030000004c050000a00b000000000000,Sony DualShock 4 Adapter,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000666600006706000088020000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,",
+"030000004c050000da0c000000010000,Sony PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,",
+"030000004c0500003713000000010000,Sony PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,",
+"030000005e0400008e02000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+"03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,",
+"03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,",
+"03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,",
+"05000000484944204465766963650000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,",
+"050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,",
+"03000000381000003014000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+"03000000381000003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+"03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,",
+"03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,",
+"030000000d0f0000f600000000010000,Switch Hori Pad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000457500002211000000010000,SZMY Power PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000e40a00000307000001000000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,",
+"03000000e40a00000207000001000000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,",
+"03000000790000001c18000000010000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000790000001c18000003100000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000591c00002400000021000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,",
+"03000000591c00002600000021000000,THEGamepad,a:b2,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,",
+"030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,",
+"030000004f0400000ed0000000020000,Thrustmaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,",
+"03000000571d00002100000021000000,Tomee NES Controller Adapter,a:b1,b:b0,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,start:b3,",
+"03000000bd12000015d0000000010000,Tomee Retro Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,",
+"03000000bd12000015d0000000000000,Tomee SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,",
+"03000000571d00002000000021000000,Tomee SNES Controller Adapter,a:b0,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,",
+"030000005f140000c501000000020000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,",
+"03000000632500002605000000010000,Uberwith Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000151900005678000010010000,Uniplay U6,a:b3,b:b6,back:b25,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,leftstick:b31,lefttrigger:b21,leftx:a1,lefty:a3,rightshoulder:b19,rightstick:b33,righttrigger:b23,rightx:a4,righty:a5,start:b27,x:b11,y:b13,",
+"030000006f0e00000302000025040000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000006f0e00000702000003060000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,",
+"050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,",
+"050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,",
+"030000005e0400008e02000000000000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+"030000005e0400008e02000010010000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4~,start:b8,x:b2,y:b3,",
+"030000006f0e00000104000000000000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+"03000000c6240000045d000000000000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+"030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+"030000005e040000050b000003090000,Xbox Elite Controller Series 2,a:b0,b:b1,back:b31,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b53,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000005e040000130b000011050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000005e040000200b000011050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000005e040000200b000013050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000005e040000200b000015050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000005e040000d102000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+"030000005e040000dd02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+"030000005e040000e002000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000e002000003090000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000e302000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+"030000005e040000ea02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+"030000005e040000fd02000003090000,Xbox One Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c62400003a54000000000000,Xbox One PowerA Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,",
+"030000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000005e040000130b000009050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000005e040000130b000013050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000005e040000130b000015050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000005e040000130b000007050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000005e040000130b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000005e040000130b000022050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000005e040000220b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,",
+"03000000120c0000100e000000010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000120c0000101e000000010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,"
+#elif defined(TARGET_OS_IPHONE)
+"05000000ac0500000100000000006d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS,",
+"05000000ac050000010000004f066d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS,",
+"05000000ac05000001000000cf076d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS,",
+"05000000ac05000001000000df076d01,*,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,",
+"05000000ac05000001000000ff076d01,*,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,",
+"05000000ac0500000200000000006d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,platform:iOS,",
+"05000000ac050000020000004f066d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,platform:iOS,",
+"05000000ac05000004000000a8986d04,8BitDo Micro gamepad,a:b1,b:b0,back:b4,dpdown:b7,dpleft:b8,dpright:b9,dpup:b10,guide:b2,leftshoulder:b11,lefttrigger:b12,rightshoulder:b13,righttrigger:b14,start:b3,x:b6,y:b5,platform:iOS,",
+"05000000ac050000040000003b8a6d04,8BitDo SN30 Pro+,a:b1,b:b0,back:b4,dpdown:b7,dpleft:b8,dpright:b9,dpup:b10,guide:b2,leftshoulder:b11,leftstick:b12,lefttrigger:b13,leftx:a0,lefty:a1~,rightshoulder:b14,rightstick:b15,righttrigger:b16,rightx:a2,righty:a3~,start:b3,x:b6,y:b5,platform:iOS,",
+"050000008a35000003010000ff070000,Backbone One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,",
+"050000008a35000004010000ff070000,Backbone One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,",
+"4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS,",
+"4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS,",
+"050000007e050000062000000f060000,Nintendo Switch Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b2,leftshoulder:b4,rightshoulder:b5,x:b1,y:b3,platform:iOS,",
+"050000007e050000062000004f060000,Nintendo Switch Joy-Con (L),+leftx:h0.1,+lefty:h0.2,-leftx:h0.4,-lefty:h0.8,dpdown:b2,dpleft:b0,dpright:b3,dpup:b1,leftshoulder:b4,misc1:b6,rightshoulder:b5,platform:iOS,",
+"050000007e05000008200000df070000,Nintendo Switch Joy-Con (L/R),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,",
+"050000007e0500000e200000df070000,Nintendo Switch Joy-Con (L/R),a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:iOS,",
+"050000007e050000072000004f060000,Nintendo Switch Joy-Con (R),+rightx:h0.4,+righty:h0.8,-rightx:h0.1,-righty:h0.2,a:b1,b:b0,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b3,y:b2,platform:iOS,",
+"050000007e05000009200000df870000,Nintendo Switch Pro Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b10,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:iOS,",
+"050000007e05000009200000ff870000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,",
+"050000004c050000cc090000df070000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,",
+"050000004c050000cc090000df870001,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,",
+"050000004c050000cc090000ff070000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,",
+"050000004c050000cc090000ff870001,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,touchpad:b11,x:b2,y:b3,platform:iOS,",
+"050000004c050000cc090000ff876d01,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,",
+"050000004c050000e60c0000df870000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,touchpad:b10,x:b2,y:b3,platform:iOS,",
+"050000004c050000e60c0000ff870000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,touchpad:b11,x:b2,y:b3,platform:iOS,",
+"05000000ac0500000300000000006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS,",
+"05000000ac0500000300000043006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS,",
+"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS,",
+"05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS,",
+"050000005e040000050b0000df070001,Xbox Elite Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b10,paddle2:b12,paddle3:b11,paddle4:b13,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,",
+"050000005e040000050b0000ff070001,Xbox Elite Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,",
+"050000005e040000e0020000df070000,Xbox One Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,",
+"050000005e040000e0020000ff070000,Xbox One Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,",
+"050000005e040000130b0000df870001,Xbox Series Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b10,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS,",
+"050000005e040000130b0000ff870001,Xbox Series Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS,"
+#elif defined(__linux__)
+ "03000000c82d00000031000011010000,8BitDo Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00000631000000010000,8BitDo Adapter 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000c82d00000951000000010000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,",
+"03000000021000000090000011010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000090000011010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"05000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"05000000c82d00006a28000000010000,8BitDo GameCube,a:b0,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b9,paddle2:b8,rightshoulder:b10,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b1,y:b4,",
+"03000000c82d00001251000011010000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"05000000c82d00001251000000010000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00001151000011010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"05000000c82d00001151000000010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00000650000011010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"05000000c82d00005106000000010000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,",
+"03000000c82d00000a20000000020000,8BitDo M30 Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,start:b7,x:b2,y:b3,",
+"03000000c82d00002090000011010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"05000000c82d00002090000000010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000451000000010000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,",
+"03000000c82d00001590000011010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"05000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00006928000011010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,",
+"05000000c82d00006928000000010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,",
+"05000000c82d00002590000001000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000008000000210000011010000,8BitDo NES30,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"03000000c82d00000310000011010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,",
+"05000000c82d00008010000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,",
+"03000000022000000090000011010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000190000011010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"05000000203800000900000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"05000000c82d00002038000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000751000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"05000000c82d00000851000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00000660000011010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00001030000011010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"05000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000020000000000000,8BitDo Pro 2 for Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"06000000c82d00000020000006010000,8BitDo Pro 2 for Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000c82d00000131000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000231000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000331000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000431000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00002867000000010000,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b3,y:b4,",
+"03000000c82d00000060000011010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"05000000c82d00000060000000010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"05000000c82d00000061000000010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"030000003512000012ab000010010000,8BitDo SFC30,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,",
+"030000003512000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"03000000c82d000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"05000000102800000900000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"05000000c82d00003028000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"05000000c82d00000351000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"03000000c82d00000160000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"03000000c82d00001290000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,",
+"05000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"05000000c82d00006228000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00000260000011010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"05000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"05000000202800000900000000010000,8BitDo SNES30,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"05000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00000a31000014010000,8BitDo Ultimate 2C,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000c82d00001d30000011010000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"05000000c82d00001b30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00001530000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00001630000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00001730000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00001130000011010000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00000631000010010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000c82d00000760000011010000,8BitDo Ultimate Wireless,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c82d00001230000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00001330000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c82d00000631000014010000,8BitDo Ultimate Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000c82d00000121000011010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"05000000c82d00000121000000010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"05000000a00500003232000001000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,",
+"05000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,",
+"03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,",
+"05000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,",
+"03000000c01100000355000011010000,Acrux Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006f0e00008801000011010000,Afterglow Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006f0e00003901000000430000,Afterglow Prismatic Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e00003901000013020000,Afterglow Prismatic Controller 048-007-NA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e00001302000000010000,Afterglow Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e00003901000020060000,Afterglow Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000100000008200000011010000,Akishop Customs PS360,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000007c1800000006000010010000,Alienware Dual Compatible Game PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,",
+"05000000491900000204000021000000,Amazon Fire Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b17,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b12,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000491900001904000011010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,",
+"05000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"0300000008100000e501000001010000,Anbernic Handheld,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b3,y:b4,",
+"03000000020500000913000010010000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000373500000710000010010000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"05000000373500004610000001000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000790000003018000011010000,Arcade Fightstick F300,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"03000000a30c00002700000011010000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,",
+"03000000a30c00002800000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,",
+"05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,",
+"05000000050b00000045000040000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,",
+"03000000050b00000579000011010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b36,paddle1:b52,paddle2:b53,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"05000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b21,paddle1:b22,paddle2:b23,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,",
+"03000000503200000110000011010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,",
+"05000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,",
+"05000000503200000110000044010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,",
+"05000000503200000110000046010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,",
+"03000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,",
+"03000000503200000210000011010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,",
+"05000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,",
+"05000000503200000210000045010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,",
+"05000000503200000210000046010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,",
+"05000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:-a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,",
+"030000008a3500000201000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000008a3500000202000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000008a3500000302000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000008a3500000402000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000c62400001b89000011010000,BDA MOGA XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000d62000002a79000011010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000c21100000791000011010000,Be1 GC101 Controller 1.03,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000c31100000791000011010000,Be1 GC101 Controller 1.03,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000005e0400008e02000003030000,Be1 GC101 Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000bc2000004d50000011010000,Beitong A1T2 BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"05000000bc2000000055000001000000,Betop AX1 BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000bc2000006412000011010000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b30,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"030000006b1400000209000011010000,Bigben,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000120c0000300e000011010000,Brook Audio Fighting Board PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000120c0000310e000011010000,Brook Audio Fighting Board PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000200e000011010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000210e000011010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000120c0000f70e000011010000,Brook Universal Fighting Board,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,",
+"03000000d81d00000b00000010010000,Buffalo BSGP1601,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,",
+"03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000af1e00002400000010010000,Clockwork Pi DevTerm,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b9,x:b3,y:b0,",
+"030000000b0400003365000000010000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,",
+"03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,",
+"03000000a306000022f6000011010000,Cyborg V3 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,",
+"030000005e0400008e02000002010000,Data Frog S80,a:b1,b:b0,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,",
+"03000000791d00000103000010010000,Dual Box Wii Classic Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000c11100000191000011010000,EasySMX,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000242f00009100000000010000,EasySMX ESM-9101,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006e0500000320000010010000,Elecom U3613M,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,",
+"030000006e0500000720000010010000,Elecom W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,",
+"030000007d0400000640000010010000,Eliminator AfterShock,a:b1,b:b2,back:b9,dpdown:+a3,dpleft:-a5,dpright:+a5,dpup:-a3,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a4,righty:a2,start:b8,x:b0,y:b3,",
+"03000000430b00000300000000010000,EMS Production PS2 Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b3,y:b0,",
+"030000006f0e00008401000011010000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006f0e00008101000011010000,Faceoff Deluxe Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006f0e00008001000011010000,Faceoff Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03005036852100000201000010010000,Final Fantasy XIV Online Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"05000000b40400001224000001010000,Flydigi APEX 4,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b4,leftstick:b10,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b20,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000b40400001124000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000b40400001224000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"05000000151900004000000001000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000007e0500003703000000000000,GameCube Adapter,a:b0,b:b1,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,",
+"19000000030000000300000002030000,GameForce Controller,a:b1,b:b0,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b5,rightstick:b15,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,",
+"03000000ac0500005b05000010010000,GameSir G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000bc2000000055000011010000,GameSir G3w,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000558500001b06000010010000,GameSir G4 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"05000000ac0500002d0200001b010000,GameSir G4s,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b33,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000ac0500007a05000011010000,GameSir G5,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000373500009710000001020000,GameSir Kaleid Flux,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000bc2000005656000011010000,GameSir T4w,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000ac0500001a06000011010000,GameSir-T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000008f0e00000800000010010000,Gasia PlayStation Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000451300000010000010010000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000f0250000c283000010010000,Gioteck VX2 PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"190000004b4800000010000000010000,GO-Advance Controller,a:b1,b:b0,back:b10,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,leftshoulder:b4,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b13,start:b15,x:b2,y:b3,",
+"190000004b4800000010000001010000,GO-Advance Controller,a:b1,b:b0,back:b12,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b4,leftstick:b13,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b16,righttrigger:b15,start:b17,x:b2,y:b3,",
+"190000004b4800000011000000010000,GO-Super Gamepad,a:b0,b:b1,back:b12,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b16,leftshoulder:b4,leftstick:b14,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b3,y:b2,",
+"03000000f0250000c183000010010000,Goodbetterbest Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000d11800000094000011010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,",
+"05000000d11800000094000000010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,",
+"0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e0400008e02000001010000,GPD Win Max 2 6800U Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000007d0400000540000000010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"03000000280400000140000000010000,Gravis GamePad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000008f0e00000610000000010000,GreenAsia Electronics Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,",
+"030000008f0e00001200000010010000,GreenAsia Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,",
+"0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000f0250000c383000010010000,GT VX2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"06000000adde0000efbe000002010000,Hidromancer Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000d81400000862000011010000,HitBox PS3 PC Analog Mode,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,",
+"03000000c9110000f055000011010000,HJC Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"030000000d0f00006d00000020010000,Hori EDGE 301,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:+a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000000d0f00008400000011010000,Hori Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f00005f00000011010000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00005e00000011010000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000000d0f00005001000009040000,Hori Fighting Commander Octa Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000000d0f00008500000010010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00008600000002010000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"030000000d0f00003701000013010000,Hori Fighting Stick Mini,a:b1,b:b0,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b3,y:b2,",
+"030000000d0f00008800000011010000,Hori Fighting Stick mini 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000000d0f00008700000011010000,Hori Fighting Stick mini 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,rightshoulder:b5,rightstick:b11,righttrigger:a4,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000000d0f00001000000011010000,Hori Fightstick 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"03000000ad1b000003f5000033050000,Hori Fightstick VX,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b8,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,",
+"030000000d0f00004d00000011010000,Hori Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000000d0f00003801000011010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,",
+"030000000d0f00009200000011010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000000d0f00001100000011010000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00002200000011010000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000000d0f00006a00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f00006b00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00001600000000010000,Hori Real Arcade Pro EXSE,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,",
+"030000000d0f0000aa00000011010000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"030000000d0f00008501000017010000,Hori Split Pad Fit,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000000d0f00008501000015010000,Hori Switch Split Pad Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000000d0f00006e00000011010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00006600000011010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000000d0f0000ee00000011010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000000d0f0000c100000011010000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000000d0f00006700000001010000,Horipad One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000000d0f0000ab01000011010000,Horipad Steam,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc2:b2,paddle1:b19,paddle2:b18,paddle3:b15,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"050000000d0f00009601000091000000,Horipad Steam,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc2:b2,paddle1:b19,paddle2:b18,paddle3:b15,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"050000000d0f0000f600000001000000,Horipad Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000341a000005f7000010010000,HuiJia GameCube Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,",
+"05000000242e00000b20000001000000,Hyperkin Admiral N64 Controller,+rightx:b11,+righty:b13,-rightx:b8,-righty:b12,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,",
+"03000000242e0000ff0b000011010000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,",
+"03000000242e00006a38000010010000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,",
+"03000000242e00008816000001010000,Hyperkin X91,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000f00300008d03000011010000,HyperX Clutch,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000830500006020000010010000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,",
+"030000008f0e00001330000001010000,iCode Retro Adapter,b:b3,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b1,start:b7,x:b2,y:b0,",
+"050000006964726f69643a636f6e0000,idroidcon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000b50700001503000010010000,Impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,",
+"03000000d80400008200000003000000,IMS PCU0,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,",
+"03000000120c00000500000010010000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,",
+"03000000ef0500000300000000010000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,",
+"03000000fd0500000030000000010000,InterAct GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,",
+"03000000fd0500002a26000000010000,InterAct HammerHead FX,a:b3,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b1,",
+"0500000049190000020400001b010000,Ipega PG 9069,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b161,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000632500007505000011010000,Ipega PG 9099,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"0500000049190000030400001b010000,Ipega PG9099,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"05000000491900000204000000000000,Ipega PG9118,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000300f00001101000010010000,Jess Tech Colour Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,",
+"03000000300f00001001000010010000,Jess Tech Dual Analog Rumble,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,",
+"03000000300f00000b01000010010000,Jess Tech GGE909 PC Recoil,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+"03000000ba2200002010000001010000,Jess Technology Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+"030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,",
+"050000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,",
+"030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,",
+"050000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,",
+"03000000bd12000003c0000010010000,Joypad Alpha Shock,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000242f00002d00000011010000,JYS Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000242f00008a00000011010000,JYS Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,",
+"030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006d040000d1ca000000000000,Logitech Chillstream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006d040000d1ca000011010000,Logitech Chillstream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+ "030000006d0400001dc2000014400000,Logitech F310,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006d0400001ec2000019200000,Logitech F510,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006d0400001ec2000020200000,Logitech F510,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006d04000019c2000011010000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006d0400001fc2000005030000,Logitech F710,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,",
+"030000006d0400000ac2000010010000,Logitech WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,",
+"05000000380700006652000025010000,Mad Catz CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000380700008532000010010000,Mad Catz Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,start:b9,x:b0,y:b3,",
+"03000000380700005032000011010000,Mad Catz Fightpad Pro PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000380700005082000011010000,Mad Catz Fightpad Pro PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,",
+"03000000380700008031000011010000,Mad Catz FightStick Alpha PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000380700008081000011010000,Mad Catz FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000380700008034000011010000,Mad Catz Fightstick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000380700008084000011010000,Mad Catz Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000380700008433000011010000,Mad Catz Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000380700008483000011010000,Mad Catz Fightstick TE S PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000380700001888000010010000,Mad Catz Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000380700003888000010010000,Mad Catz Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000380700001647000010040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000380700003847000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000120c00000500000000010000,Manta DualShock 2,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,",
+"030000008f0e00001330000010010000,Mayflash Controller Adapter,a:b1,b:b2,back:b8,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3~,righty:a2,start:b9,x:b0,y:b3,",
+"03000000790000004318000010010000,Mayflash GameCube Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,",
+"03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,",
+"03000000242f00007300000011010000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,",
+"0300000079000000d218000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000d620000010a7000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000242f0000f700000001010000,Mayflash Magic S Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000008f0e00001030000010010000,Mayflash Saturn Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,",
+"0300000025090000e803000001010000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,",
+"03000000790000000318000011010000,Mayflash Wii DolphinBar,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,",
+"03000000790000000018000011010000,Mayflash Wii U Pro Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000b50700001203000010010000,Mega World Logic 3 Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,",
+"03000000b50700004f00000000010000,Mega World Logic 3 Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,",
+"03000000780000000600000010010000,Microntek Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,",
+"030000005e0400002800000000010000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,",
+"030000005e0400000300000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,",
+"030000005e0400000700000000010000,Microsoft SideWinder,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,",
+"030000005e0400000e00000000010000,Microsoft SideWinder Freestyle Pro,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,",
+"030000005e0400002700000000010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,",
+"030000005e0400008502000000010000,Microsoft Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,",
+"030000005e0400008902000021010000,Microsoft Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,",
+"030000005e0400008e02000001000000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.1,dpleft:h0.2,dpright:h0.8,dpup:h0.4,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e0400008e02000004010000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e0400008e02000056210000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e0400008e02000062230000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000d102000001010000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000d102000003020000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000dd02000003020000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000ea02000008040000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000ea0200000f050000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"060000005e040000120b000009050000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000e302000003020000,Microsoft Xbox One Elite,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000000b000007040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b12,paddle2:b14,paddle3:b13,paddle4:b15,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000000b000008040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b12,paddle2:b14,paddle3:b13,paddle4:b15,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"050000005e040000050b000003090000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"050000005e0400008e02000030110000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000120b00000b050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000120b000016050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000120b000017050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"060000005e040000120b000001050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,",
+"03000000790000001c18000010010000,Mobapad Chitu HD,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"050000004d4f435554452d3035335800,Mocute 053X,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"05000000e80400006e0400001b010000,Mocute 053X M59,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"050000004d4f435554452d3035305800,Mocute 054X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"05000000d6200000e589000001000000,Moga 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"05000000d62000007162000001000000,Moga Pro 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"03000000c62400002b89000011010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"05000000c62400002a89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b22,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"05000000c62400001a89000000010000,MOGA XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000250900006688000000010000,MP8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+"030000005e0400008e02000010020000,MSI GC20 V2,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000f70600000100000000010000,N64 Adaptoid,+rightx:b2,+righty:b1,-rightx:b4,-righty:b5,a:b0,b:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,",
+"030000006b1400000906000014010000,Nacon Asymmetric Wireless PS4 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006b140000010c000010010000,Nacon GC 400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"03000000853200000706000012010000,Nacon GC-100,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"05000000853200000503000000010000,Nacon MG-X Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"0300000085320000170d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"0300000085320000190d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000000d0f00000900000010010000,Natec Genesis P44,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000004f1f00000800000011010000,NeoGeo PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"0300000092120000474e000000010000,NeoGeo X Arcade Stick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b3,y:b2,",
+"03000000790000004518000010010000,Nexilux GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,",
+"030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,",
+"060000007e0500003713000000000000,Nintendo 3DS,a:b0,b:b1,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,",
+"030000007e0500003703000000016800,Nintendo GameCube Controller,a:b0,b:b2,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b1,y:b3,",
+"03000000790000004618000010010000,Nintendo GameCube Controller Adapter,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5~,righty:a2~,start:b9,x:b2,y:b3,",
+"060000004e696e74656e646f20537700,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,",
+"060000007e0500000620000000000000,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,",
+"060000007e0500000820000000000000,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,",
+"050000004c69632050726f20436f6e00,Nintendo Switch Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"050000007e0500000620000001800000,Nintendo Switch Left Joy-Con,a:b16,b:b15,back:b4,leftshoulder:b6,leftstick:b12,leftx:a1,lefty:a0~,rightshoulder:b8,start:b9,x:b14,y:b17,",
+"030000007e0500000920000000026803,Nintendo Switch Pro Controller,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,",
+"050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"050000007e0500000920000001800000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,",
+"050000007e0500000720000001800000,Nintendo Switch Right Joy-Con,a:b1,b:b2,back:b9,leftshoulder:b4,leftstick:b10,leftx:a1~,lefty:a0,rightshoulder:b6,start:b8,x:b0,y:b3,",
+"05000000010000000100000003000000,Nintendo Wii Remote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"050000007e0500003003000001000000,Nintendo Wii U Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,",
+"050000005a1d00000218000003000000,Nokia GC 5000,a:b9,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000000d0500000308000010010000,Nostromo n45 Dual Analog,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,",
+"030000007e0500001920000011810000,NSO N64 Controller,+rightx:b2,+righty:b3,-rightx:b4,-righty:b10,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b5,rightshoulder:b7,righttrigger:b9,start:b11,",
+"050000007e0500001920000001000000,NSO N64 Controller,+rightx:b8,+righty:b7,-rightx:b3,-righty:b2,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,",
+"050000007e0500001920000001800000,NSO N64 Controller,+rightx:b2,+righty:b3,-rightx:b4,-righty:b10,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b5,rightshoulder:b7,righttrigger:b9,start:b11,",
+"030000007e0500001e20000011810000,NSO Sega Genesis Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,misc1:b3,rightshoulder:b2,righttrigger:b4,start:b5,",
+"030000007e0500001720000011810000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,",
+"050000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b8,start:b10,x:b3,y:b2,",
+"050000007e0500001720000001800000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,",
+"03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,",
+"03000000550900001472000011010000,NVIDIA Controller,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,",
+"05000000550900001472000001000000,NVIDIA Controller,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,",
+"030000004b120000014d000000010000,NYKO Airflo EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,",
+"03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"19000000010000000100000001010000,ODROID Go 2,a:b1,b:b0,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,guide:b10,leftshoulder:b4,leftstick:b12,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b14,start:b15,x:b2,y:b3,",
+"19000000010000000200000011000000,ODROID Go 2,a:b1,b:b0,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b12,leftshoulder:b4,leftstick:b14,lefttrigger:b13,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b16,start:b17,x:b2,y:b3,",
+"05000000362800000100000002010000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,",
+"05000000362800000100000003010000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,",
+"05000000362800000100000004010000,OUYA Controller,a:b0,b:b3,back:b14,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,start:b16,x:b1,y:b2,",
+"03000000830500005020000010010000,Padix Rockfire PlayStation Bridge,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b2,y:b3,",
+"03000000ff1100003133000010010000,PC Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"030000006f0e0000b802000001010000,PDP Afterglow Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e0000b802000013020000,PDP Afterglow Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e0000d702000006640000,PDP Black Camo Wired Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b13,dpup:b14,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e00003101000000010000,PDP EA Sports Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e00008501000011010000,PDP Fightpad Pro Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"030000006f0e0000c802000012010000,PDP Kingdom Hearts Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e00002801000011010000,PDP PS3 Rock Candy Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006f0e00000901000011010000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000006f0e00002f01000011010000,PDP Wired PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000ad1b000004f9000000010000,PDP Xbox 360 Versus Fighting,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,",
+"030000006f0e0000f102000000000000,PDP Xbox Atomic,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e0000a802000023020000,PDP Xbox One Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"030000006f0e0000a702000023020000,PDP Xbox One Raven Black,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e0000d802000006640000,PDP Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e0000ef02000007640000,PDP Xbox Series Kinetic Wired Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000d62000000540000001010000,PowerA Advantage Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000d620000011a7000011010000,PowerA Core Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000dd62000015a7000011010000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000d620000012a7000011010000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000d62000000140000001010000,PowerA Fusion Pro 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000dd62000016a7000000000000,PowerA Fusion Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000c62400001a53000000010000,PowerA Mini Pro Ex,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000d620000013a7000011010000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000d62000006dca000011010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000d620000014a7000011010000,PowerA Spectra Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000c62400001a58000001010000,PowerA Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000d62000000220000001010000,PowerA Xbox One Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
+"03000000d62000000228000001010000,PowerA Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000c62400001a54000001010000,PowerA Xbox One Mini Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000d62000000240000001010000,PowerA Xbox One Spectra Infinity,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000d62000000520000050010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000d62000000b20000001010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000d62000000f20000001010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006d040000d2ca000011010000,Precision Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000250900000017000010010000,PS/SS/N64 Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b5,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2~,righty:a3,start:b8,",
+"03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,",
+"03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
+"030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+"030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
+"030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+"030000005f1400003102000010010000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"030000006f0e00001402000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000008f0e00000300000010010000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"050000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
+"050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
+"050000004c0500006802000000800000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+"050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+"05000000504c415953544154494f4e00,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
+"060000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,",
+"030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+"030000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+"030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+"030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+"03000000c01100000140000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+"050000004c050000c405000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+"050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+"050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+"0300004b4c0500005f0e000011010000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000004c050000e60c000011010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000004c050000e60c000011810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+"030000004c050000f20d000011010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000004c050000f20d000011810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+"050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"050000004c050000e60c000000810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+"050000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"050000004c050000f20d000000810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,",
+"03000000300f00001211000011010000,Qanba Arcade Joystick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,",
+"03000000222c00000225000011010000,Qanba Dragon Arcade Joystick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000222c00000025000011010000,Qanba Dragon Arcade Joystick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000222c00001220000011010000,Qanba Drone 2 Arcade Joystick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000222c00001020000011010000,Qanba Drone 2 Arcade Joystick PS5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000222c00000020000011010000,Qanba Drone Arcade PS4 Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000300f00001210000010010000,Qanba Joystick Plus,a:b0,b:b1,back:b8,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,start:b9,x:b2,y:b3,",
+"03000000222c00000223000011010000,Qanba Obsidian Arcade Joystick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000222c00000023000011010000,Qanba Obsidian Arcade Joystick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000009b2800000300000001010000,Raphnet 4nes4snes,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,",
+"030000009b2800004200000001010000,Raphnet Dual NES Adapter,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,",
+"0300132d9b2800006500000000000000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,",
+"0300132d9b2800006500000001010000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,",
+"030000009b2800003200000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,",
+"030000009b2800006000000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,",
+"030000009b2800008000000020020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,",
+"030000009b2800008000000001010000,Raphnet Wii Classic Adapter V3,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,",
+"03000000f8270000bf0b000011010000,Razer Kishi,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000321500000204000011010000,Razer Panthera PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000321500000104000011010000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000321500000810000011010000,Razer Panthera PS4 Evo Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000321500000010000011010000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000321500000507000000010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"05000000321500000a10000001000000,Razer Raiju Tournament Edition,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000321500000011000011010000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000008916000000fe000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000c6240000045d000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,",
+"050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,",
+"0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000321500000b10000011010000,Razer Wolverine PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"0300000032150000140a000001010000,Razer Wolverine Ultimate Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000000d0f0000c100000010010000,Retro Bit Legacy16,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b12,leftshoulder:b4,lefttrigger:b6,misc1:b13,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,",
+"030000000d0f0000c100000072056800,Retro Bit Legacy16,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b5,leftshoulder:b9,lefttrigger:+a4,misc1:b11,rightshoulder:b10,righttrigger:+a5,start:b6,x:b3,y:b2,",
+"03000000790000001100000010010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,",
+"0300000003040000c197000011010000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,",
+"190000004b4800000111000000010000,RetroGame Joypad,a:b1,b:b0,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"0300000081170000990a000001010000,Retronic Adapter,a:b0,leftx:a0,lefty:a1,",
+"0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,",
+"00000000526574726f53746f6e653200,RetroStone 2 Controller,a:b1,b:b0,back:b10,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,righttrigger:b9,start:b11,x:b4,y:b3,",
+"03000000341200000400000000010000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,",
+"030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000006b140000130d000011010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000006f0e00001f01000000010000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e00008701000011010000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000006f0e00001e01000011010000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000c6240000fefa000000010000,Rock Candy Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e00004601000001010000,Rock Candy Xbox One Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e00001311000011010000,Saffun Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b0,",
+"03000000a306000023f6000011010000,Saitek Cyborg PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,",
+"03000000a30600001005000000010000,Saitek P150,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b2,righttrigger:b5,x:b3,y:b4,",
+"03000000a30600000701000000010000,Saitek P220,a:b2,b:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,x:b0,y:b1,",
+"03000000a30600000cff000010010000,Saitek P2500 Force Rumble,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b0,y:b1,",
+"03000000a30600000d5f000010010000,Saitek P2600,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,",
+"03000000a30600000c04000011010000,Saitek P2900,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,",
+"03000000a306000018f5000010010000,Saitek P3200 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,",
+"03000000300f00001201000010010000,Saitek P380,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,",
+"03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,",
+"03000000a30600000b04000000010000,Saitek P990 Dual Analog,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,",
+"03000000a306000020f6000011010000,Saitek PS2700 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,",
+"05000000e804000000a000001b010000,Samsung EIGP20,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000d81d00000e00000010010000,Savior,a:b0,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b9,x:b4,y:b5,",
+"03000000952e00004b43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,",
+"03000000952e00004d43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,",
+"03000000952e00004e43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,",
+"03000000a30c00002500000011010000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,",
+"03000000790000001100000011010000,Sega Saturn,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,",
+"03000000790000002201000011010000,Sega Saturn,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,start:b9,x:b2,y:b3,",
+"03000000b40400000a01000000010000,Sega Saturn,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,",
+"03000000632500002305000010010000,ShanWan Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000632500002605000010010000,Shanwan Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000632500007505000010010000,Shanwan Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000bc2000000055000010010000,Shanwan Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000f025000021c1000010010000,Shanwan Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000341a00000908000010010000,SL6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"030000004b2900000430000011000000,Snakebyte Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"050000004c050000cc09000001000000,Sony DualShock 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,",
+"03000000666600006706000000010000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,",
+"030000004c050000da0c000011010000,Sony PlayStation Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,",
+"03000000d9040000160f000000010000,Sony PlayStation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+"03000000ff000000cb01000010010000,Sony PlayStation Portable,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,",
+"030000004c0500003713000011010000,Sony PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,",
+"03000000250900000500000000010000,Sony PS2 pad with SmartJoy Adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+"030000005e0400008e02000073050000,Speedlink Torid,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e0400008e02000020200000,SpeedLink Xeox Pro Analog,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
+"03000000de2800000112000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:+a5,dpleft:-a4,dpright:+a4,dpup:-a5,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a3,start:b11,x:b4,y:b5,",
+"03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
+"03000000de2800000211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b16,paddle2:b15,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,",
+"03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
+"03000000de2800004211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,paddle1:b16,paddle2:b15,rightshoulder:b7,righttrigger:a6,rightx:a2,righty:a3,start:b11,x:b4,y:b5,",
+"03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
+"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
+"05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
+"03000000de2800000512000010010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,",
+"03000000de2800000512000011010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,misc1:b2,paddle1:b21,paddle2:b20,paddle3:b23,paddle4:b22,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,",
+"03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b10,guide:b11,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,",
+"03000000381000003014000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000381000003114000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"0500000011010000311400001b010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b32,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"05000000110100001914000009010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000ad1b000038f0000090040000,Street Fighter IV Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000003b07000004a1000000010000,Suncom SFX Plus,a:b0,b:b2,back:b7,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,",
+"030000001f08000001e4000010010000,Super Famicom Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,",
+"03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,",
+"0300000000f00000f100000000010000,Super RetroPort,a:b1,b:b5,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b4,",
+"030000008f0e00000d31000010010000,SZMY Power 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000457500000401000011010000,SZMY Power DS4 Wired Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000457500002211000010010000,SZMY Power Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"030000008f0e00001431000010010000,SZMY Power PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000e40a00000307000011010000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,",
+"03000000e40a00000207000011010000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,",
+"03000000ba2200000701000001010000,Technology Innovation PS2 Adapter,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b2,",
+"03000000790000001c18000011010000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000591c00002400000010010000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,",
+"03000000591c00002600000010010000,THEGamepad,a:b2,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b0,",
+"030000004f04000015b3000001010000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,",
+"030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,",
+"030000004f04000020b3000010010000,Thrustmaster Dual Trigger,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,",
+"030000004f04000023b3000000010000,Thrustmaster Dual Trigger PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000004f0400000ed0000011010000,Thrustmaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000b50700000399000000010000,Thrustmaster Firestorm Digital 2,a:b2,b:b4,back:b11,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b0,righttrigger:b9,start:b1,x:b3,y:b5,",
+"030000004f04000003b3000010010000,Thrustmaster Firestorm Dual Analog 2,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b9,rightx:a2,righty:a3,x:b1,y:b3,",
+"030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,",
+"030000004f04000004b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,",
+"030000004f04000026b3000002040000,Thrustmaster GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000c6240000025b000002020000,Thrustmaster GPX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000004f04000008d0000000010000,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000004f04000009d0000000010000,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000004f04000007d0000000010000,Thrustmaster T Mini,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"030000004f04000012b3000010010000,Thrustmaster Vibrating Gamepad,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,",
+"03000000571d00002000000010010000,Tomee SNES Adapter,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,",
+"03000000bd12000015d0000010010000,Tomee SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,",
+"03000000d814000007cd000011010000,Toodles 2008 Chimp PC PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,",
+"030000005e0400008e02000070050000,Torid,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000c01100000591000011010000,Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"03000000680a00000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,",
+"03000000780300000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,",
+"03000000e00d00000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,",
+"03000000f00600000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,",
+"030000005f140000c501000010010000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,",
+"06000000f51000000870000003010000,Turtle Beach Recon,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000100800000100000010010000,Twin PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+"03000000151900005678000010010000,Uniplay U6,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,",
+"03000000790000000600000007010000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,",
+"03000000790000001100000000010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,",
+"03000000790000001a18000011010000,Venom PS4 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,",
+"03000000790000001b18000011010000,Venom PS4 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"030000006f0e00000302000011010000,Victrix Pro Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,",
+"030000006f0e00000702000011010000,Victrix Pro Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,",
+"05000000ac0500003232000001000000,VR Box Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,",
+"05000000434f4d4d414e440000000000,VX Gaming Command Series,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"0000000058626f782033363020576900,Xbox 360 Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
+"030000005e0400001907000000010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e0400008e02000010010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e0400008e02000014010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e0400009102000007010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000a102000000010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000a102000007010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000a102000030060000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e00001503000000020000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e0400008e02000000010000,Xbox 360 EasySMX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000a102000014010000,Xbox 360 Receiver,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"0000000058626f782047616d65706100,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,",
+"030000005e0400000202000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,",
+"030000005e0400008e02000072050000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000006f0e00001304000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000ffff0000ffff000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,",
+"030000005e0400000a0b000005040000,Xbox One Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,",
+"030000005e040000d102000002010000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000ea02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000ea02000001030000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"050000005e040000e002000003090000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"050000005e040000fd02000003090000,Xbox One Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"050000005e040000fd02000030110000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"060000005e040000dd02000003020000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"050000005e040000e302000002090000,Xbox One Elite,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"050000005e040000220b000013050000,Xbox One Elite 2 Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"050000005e040000050b000002090000,Xbox One Elite Series 2,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"030000005e040000ea02000011050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+ "030000005e040000ea02000015050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"060000005e040000ea0200000b050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"060000005e040000ea0200000d050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"060000005e040000ea02000016050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000120b000001050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000120b000005050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000120b000009050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000120b00000d050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000120b000011050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000120b000014050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000120b000015050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"050000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"050000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"050000005e040000130b000007050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"050000005e040000130b000009050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"050000005e040000130b000011050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"050000005e040000130b000013050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"050000005e040000130b000015050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"050000005e040000130b000017050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"060000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"060000005e040000120b00000b050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"060000005e040000120b00000d050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"060000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"050000005e040000200b000013050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"050000005e040000200b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"050000005e040000220b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,",
+"03000000450c00002043000010010000,XEOX SL6556 BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,",
+"05000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,",
+"03000000c0160000e105000001010000,XinMo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,",
+"030000005e0400008e02000020010000,XInput Adapter,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
+"03000000120c0000100e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,",
+"03000000120c0000101e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,"
+#elif defined(__ANDROID_API__)
+"38653964633230666463343334313533,8BitDo Adapter,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"36666264316630653965636634386234,8BitDo Adapter 2,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"38426974446f20417263616465205374,8BitDo Arcade Stick,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b5,leftshoulder:b9,lefttrigger:a4,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"61393962646434393836356631636132,8BitDo Arcade Stick,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,",
+"64323139346131306233636562663738,8BitDo Arcade Stick,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,",
+"64643565386136613265663236636564,8BitDo Arcade Stick,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,",
+"33313433353539306634656436353432,8BitDo Dogbone,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"38426974446f20446f67626f6e65204d,8BitDo Dogbone,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,",
+"34343439373236623466343934376233,8BitDo FC30 Pro,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b28,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b29,righttrigger:b7,start:b5,x:b30,y:b2,",
+"38426974446f204e4743204d6f646b69,8BitDo GameCube,a:b0,b:b2,back:b4,dpdown:b12,dpleft:b13,dpright:b14,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,paddle1:b18,paddle2:b17,rightshoulder:b15,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b1,y:b3,",
+"38426974446f2038426974446f204c69,8BitDo Lite,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"30643332373663313263316637356631,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"38426974446f204c6974652032000000,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"62656331626461363634633735353032,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"38393936616436383062666232653338,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"38426974446f204c6974652053450000,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"39356430616562366466646636643435,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"05000000c82d000006500000ffff3f00,8BitDo M30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b17,leftshoulder:b9,lefttrigger:a5,rightshoulder:b10,righttrigger:a4,start:b6,x:b3,y:b2,",
+"05000000c82d000051060000ffff3f00,8BitDo M30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b17,leftshoulder:b9,lefttrigger:a4,rightshoulder:b10,righttrigger:a5,start:b6,x:b3,y:b2,",
+"32323161363037623637326438643634,8BitDo M30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"33656266353630643966653238646264,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,start:b10,x:b19,y:b2,",
+"38426974446f204d3330204d6f646b69,8BitDo M30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"39366630663062373237616566353437,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,start:b6,x:b2,y:b3,",
+"64653533313537373934323436343563,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,start:b6,x:b2,y:b3,",
+"66356438346136366337386437653934,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,start:b18,x:b19,y:b2,",
+"66393064393162303732356665666366,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,",
+"38426974446f204d6963726f2067616d,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,",
+"61653365323561356263373333643266,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,",
+"62613137616239666338343866326336,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,",
+"33663431326134333366393233616633,8BitDo N30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,",
+"38426974446f204e3330204d6f646b69,8BitDo N30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,",
+"05000000c82d000015900000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"05000000c82d000065280000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"38323035343766666239373834336637,8BitDo N64,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,",
+"38426974446f204e3634204d6f646b69,8BitDo N64,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,",
+"32363135613966656338666638666237,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"35363534633333373639386466346631,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"38426974446f204e454f47454f204750,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"39383963623932353561633733306334,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"050000000220000000900000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"050000002038000009000000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"38313433643131656262306631373166,8BitDo P30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"38326536643339353865323063616339,8BitDo P30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"38426974446f2050333020636c617373,8BitDo P30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"35376664343164386333616535333434,8BitDo Pro 2,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,start:b10,x:b19,y:b2,",
+"38426974446f2038426974446f205072,8BitDo Pro 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"38426974446f2050726f203200000000,8BitDo Pro 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"61333362366131643730353063616330,8BitDo Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"62373739366537363166326238653463,8BitDo Pro 2,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b3,y:b2,",
+"38386464613034326435626130396565,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"38426974446f2038426974446f205265,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"66303230343038613365623964393766,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"38426974446f20533330204d6f646b69,8BitDo S30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"66316462353561376330346462316137,8BitDo S30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"05000000c82d000000600000ffff3f00,8BitDo SF30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"05000000c82d000000610000ffff3f00,8BitDo SF30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"38426974646f20534633302050726f00,8BitDo SF30 Pro,a:b1,b:b0,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b17,",
+"61623334636338643233383735326439,8BitDo SFC30,a:b0,b:b1,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b31,start:b5,x:b30,y:b2,",
+"05000000c82d000012900000ffff3f00,8BitDo SN30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,",
+"05000000c82d000062280000ffff3f00,8BitDo SN30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,",
+"38316230613931613964356666353839,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"38426974446f20534e3330204d6f646b,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"65323563303231646531383162646335,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"35383531346263653330306238353131,8BitDo SN30 PP,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"05000000c82d000001600000ffff3f00,8BitDo SN30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"05000000c82d000002600000ffff0f00,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"36653638656632326235346264663661,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,",
+"38303232393133383836366330346462,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,",
+"38346630346135363335366265656666,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"38426974446f20534e33302050726f2b,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"536f6e7920436f6d707574657220456e,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"66306331643531333230306437353936,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"050000002028000009000000ffff3f00,8BitDo SNES30,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"050000003512000020ab000000780f00,8BitDo SNES30,a:b21,b:b20,back:b30,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b26,rightshoulder:b27,start:b31,x:b24,y:b23,",
+"33666663316164653937326237613331,8BitDo Zero,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,",
+"38426974646f205a65726f2047616d65,8BitDo Zero,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,",
+"05000000c82d000018900000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,",
+"05000000c82d000030320000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,",
+"33663434393362303033616630346337,8BitDo Zero 2,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,x:b19,y:b2,",
+"34656330626361666438323266633963,8BitDo Zero 2,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b20,start:b10,x:b19,y:b2,",
+"63396666386564393334393236386630,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,",
+"63633435623263373466343461646430,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,",
+"32333634613735616163326165323731,Amazon Luna Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,",
+"4c696e757820342e31392e3137322077,Anbernic Handheld,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a4,start:b6,x:b2,y:b3,",
+"417374726f2063697479206d696e6920,Astro City Mini,a:b23,b:b22,back:b29,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b25,righttrigger:b26,start:b30,x:b24,y:b21,",
+"35643263313264386134376362363435,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,start:b6,",
+"32353831643566306563643065356239,Atari VCS Modern Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"4f64696e20436f6e74726f6c6c657200,AYN Odin,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b14,dpright:b13,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:+a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"32303165626138343962363666346165,Brook Mars PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,",
+"38383337343564366131323064613561,Brook Mars PS4 Controller,a:b1,b:b19,back:b17,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,",
+"34313430343161653665353737323365,Elecom JC-W01U,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,",
+"4875694a6961204a432d573031550000,Elecom JC-W01U,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,",
+"30363230653635633863366338623265,Evo VR,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,x:b2,y:b3,",
+"05000000b404000011240000dfff3f00,Flydigi Vader 2,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"05000000bc20000000550000ffff3f00,GameSir G3w,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"34323662653333636330306631326233,Google Nexus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"35383633353935396534393230616564,Google Stadia Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"476f6f676c65204c4c43205374616469,Google Stadia Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"5374616469614e3848532d6532633400,Google Stadia Controller,a:b0,b:b1,back:b15,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"05000000d6020000e5890000dfff3f00,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,",
+"05000000d6020000e5890000dfff3f80,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a3,rightx:a4,righty:a5,start:b6,x:b2,y:b3,",
+"66633030656131663837396562323935,Hori Battle,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
+"35623466343433653739346434636330,Hori Fighting Commander 3 Pro,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,",
+"484f524920434f2e2c4c54442e203130,Hori Fighting Commander 3 Pro,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b20,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b9,rightx:a2,righty:a3,start:b18,x:b0,y:b2,",
+"484f524920434f2e2c4c544420205041,Hori Gem Pad 3,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b0,y:b2,",
+"65656436646661313232656661616130,Hori PC Engine Mini Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,start:b18,",
+"31303433326562636431653534636633,Hori Real Arcade Pro 3,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,",
+"32656664353964393561366362333636,Hori Switch Split Pad Pro,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
+"30306539356238653637313730656134,HORIPAD Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,",
+"48797065726b696e2050616400000000,Hyperkin Admiral N64 Controller,+rightx:b6,+righty:b7,-rightx:b17,-righty:b5,a:b1,b:b0,leftshoulder:b3,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,",
+"62333331353131353034386136626636,Hyperkin Admiral N64 Controller,+rightx:b6,+righty:b7,-rightx:b17,-righty:b5,a:b1,b:b0,leftshoulder:b3,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,",
+"31306635363562663834633739396333,Hyperkin N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,",
+"5368616e57616e202020202048797065,Hyperkin N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,",
+"0500000083050000602000000ffe0000,iBuffalo SNES Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b15,rightshoulder:b16,start:b10,x:b2,y:b3,",
+"5553422c322d6178697320382d627574,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,rightshoulder:b18,start:b10,x:b3,y:b2,",
+"64306137363261396266353433303531,InterAct GoPad,a:b24,b:b25,leftshoulder:b23,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,x:b21,y:b22,",
+"532e542e442e20496e74657261637420,InterAct HammerHead FX,a:b23,b:b24,back:b30,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,leftstick:b22,lefttrigger:b28,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b25,righttrigger:b29,rightx:a2,righty:a3,start:b31,x:b20,y:b21,",
+"65346535636333663931613264643164,Joy-Con,a:b21,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b23,y:b24,",
+"33346566643039343630376565326335,Joy-Con (L),a:b0,b:b1,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b17,x:b19,y:b2,",
+"35313531613435623366313835326238,Joy-Con (L),a:b0,b:b1,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b17,x:b19,y:b2,",
+"4a6f792d436f6e20284c290000000000,Joy-Con (L),a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b17,x:b19,y:b2,",
+"38383665633039363066383334653465,Joy-Con (R),a:b0,b:b1,back:b5,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b18,x:b19,y:b2,",
+"39363561613936303237333537383931,Joy-Con (R),a:b0,b:b1,back:b5,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b18,x:b19,y:b2,",
+"39373064396565646338333134303131,Joy-Con (R),a:b1,b:b2,back:b5,leftstick:b8,leftx:a1~,lefty:a0,start:b6,x:b0,y:b3,",
+"4a6f792d436f6e202852290000000000,Joy-Con (R),a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,",
+"39656136363638323036303865326464,JYS Aapter,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,",
+"63316564383539663166353034616434,JYS Adapter,a:b1,b:b3,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b0,y:b2,",
+"64623163333561643339623235373232,Logitech F310,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"35623364393661626231343866613337,Logitech F710,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"4c6f6769746563682047616d65706164,Logitech F710,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"64396331333230326333313330336533,Logitech F710,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"39653365373864633935383236363438,Logitech G Cloud,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"416d617a6f6e2047616d6520436f6e74,Luna Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
+"4c756e612047616d6570616400000000,Luna Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"30363066623539323534363639323363,Magic NS,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,",
+"31353762393935386662336365626334,Magic NS,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,",
+"39623565346366623931666633323530,Magic NS,a:b1,b:b3,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b0,y:b2,",
+"6d6179666c617368206c696d69746564,Mayflash GameCube Adapter,a:b22,b:b21,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a5,righty:a2,start:b30,x:b23,y:b24,",
+"436f6e74726f6c6c6572000000000000,Mayflash N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,",
+"65666330633838383061313633326461,Mayflash N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,",
+"37316565396364386635383230353365,Mayflash Saturn Adapter,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,",
+"4875694a696120205553422047616d65,Mayflash Saturn Adapter,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,",
+"535a4d792d706f776572204c54442043,Mayflash Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b31,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,",
+"30653962643666303631376438373532,Mayflash Wii DolphinBar,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b0,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,",
+"39346131396233376535393665363161,Mayflash Wii U Pro Adapter,a:b22,b:b23,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,leftstick:b31,lefttrigger:b27,rightshoulder:b26,rightstick:b0,righttrigger:b28,rightx:a0,righty:a1,start:b30,x:b21,y:b24,",
+"31323564663862633234646330373138,Mega Drive,a:b23,b:b22,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b25,righttrigger:b26,start:b30,x:b24,y:b21,",
+"37333564393261653735306132613061,Mega Drive,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,",
+"64363363336633363736393038313464,Mega Drive,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,start:b9,x:b2,y:b3,",
+"33323763323132376537376266393366,Microsoft Dual Strike,a:b24,b:b23,back:b25,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b29,rightshoulder:b78,rightx:a0,righty:a1~,start:b26,x:b22,y:b21,",
+"30306461613834333439303734316539,Microsoft SideWinder Pro,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b20,lefttrigger:b9,rightshoulder:b19,righttrigger:b10,start:b17,x:b2,y:b3,",
+"32386235353630393033393135613831,Microsoft Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"4d4f42415041442050726f2d48440000,Mobapad Chitu HD,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"4d4f435554452d303533582d4d35312d,Mocute 053X,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"33343361376163623438613466616531,Mocute M053,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"39306635663061636563316166303966,Mocute M053,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"7573622067616d657061642020202020,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,",
+"050000007e05000009200000ffff0f00,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b17,y:b2,",
+"31316661666466633938376335383661,Nintendo Switch Pro Controller,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,misc1:b5,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,start:b6,x:b3,y:b2,",
+"34323437396534643531326161633738,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,misc1:b5,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"50726f20436f6e74726f6c6c65720000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b2,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b10,rightx:a2,righty:a3,start:b18,y:b3,",
+"36326533353166323965623661303933,NSO N64 Controller,+rightx:b17,+righty:b10,-rightx:b2,-righty:b19,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,righttrigger:b15,start:b18,",
+"4e363420436f6e74726f6c6c65720000,NSO N64 Controller,+rightx:b17,+righty:b10,-rightx:b2,-righty:b19,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,righttrigger:b15,start:b18,",
+"534e455320436f6e74726f6c6c657200,NSO SNES Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,",
+"64623863346133633561626136366634,NSO SNES Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,",
+"050000005509000003720000cf7f3f00,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"050000005509000010720000ffff3f00,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"050000005509000014720000df7f3f00,NVIDIA Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,",
+"050000005509000014720000df7f3f80,NVIDIA Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a3,rightx:a4,righty:a5,start:b6,x:b2,y:b3,",
+"37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"39383335313438623439373538343266,OUYA Controller,a:b0,b:b2,dpdown:b18,dpleft:b15,dpright:b16,dpup:b17,leftshoulder:b3,leftstick:b9,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,x:b1,y:b19,",
+"4f5559412047616d6520436f6e74726f,OUYA Controller,a:b0,b:b2,dpdown:b18,dpleft:b15,dpright:b6,dpup:b17,leftshoulder:b3,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b19,",
+"506572666f726d616e63652044657369,PDP PS3 Rock Candy Controller,a:b1,b:b17,back:h0.2,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b0,y:b2,",
+"61653962353232366130326530363061,Pokken,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,rightshoulder:b20,righttrigger:b10,start:b18,x:b0,y:b2,",
+"32666633663735353234363064386132,PS2,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a3,righty:a2,start:b30,x:b24,y:b21,",
+"050000004c05000068020000dfff3f00,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"536f6e7920504c415953544154494f4e,PS3 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"61363034663839376638653463633865,PS3 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"66366539656564653432353139356536,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"66383132326164626636313737373037,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"050000004c050000c405000000783f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"050000004c050000c4050000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,",
+"050000004c050000c4050000fffe3f80,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a3,rightx:a4,righty:a5,start:b16,x:b0,y:b2,",
+"050000004c050000c4050000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"050000004c050000cc090000fffe3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"050000004c050000cc090000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"30303839663330346632363232623138,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,",
+"31326235383662333266633463653332,PS4 Controller,a:b1,b:b16,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b17,x:b0,y:b2,",
+"31373231336561636235613666323035,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"31663838336334393132303338353963,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"34613139376634626133336530386430,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,",
+"37626233336235343937333961353732,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"37626464343430636562316661643863,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"38393161636261653636653532386639,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"63313733393535663339656564343962,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"63393662363836383439353064663939,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"65366465656364636137653363376531,PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,",
+"66613532303965383534396638613230,PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,",
+"050000004c050000e60c0000fffe3f00,PS5 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,",
+"050000004c050000e60c0000fffe3f80,PS5 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a3,rightx:a4,righty:a5,start:b16,x:b2,y:b17,",
+"050000004c050000e60c0000ffff3f00,PS5 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"32346465346533616263386539323932,PS5 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"32633532643734376632656664383733,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,",
+"37363764353731323963323639666565,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,",
+"61303162353165316365336436343139,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,",
+"64336263393933626535303339616332,Qanba 4RAF,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b20,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b9,rightx:a2,righty:a3,start:b18,x:b19,y:b2,",
+"36626666353861663864336130363137,Razer Junglecat,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"05000000f8270000bf0b0000ffff3f00,Razer Kishi,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"62653861643333663663383332396665,Razer Kishi,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"050000003215000005070000ffff3f00,Razer Raiju Mobile,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"050000003215000007070000ffff3f00,Razer Raiju Mobile,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"050000003215000000090000bf7f3f00,Razer Serval,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,",
+"5a6869587520526574726f2042697420,Retro Bit Saturn Controller,a:b21,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b26,rightshoulder:b27,righttrigger:b28,start:b30,x:b23,y:b24,",
+"32417865732031314b6579732047616d,Retro Bit SNES Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,",
+"36313938306539326233393732613361,Retro Bit SNES Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,",
+"526574726f466c616720576972656420,Retro Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,rightshoulder:b18,start:b10,x:b2,y:b3,",
+"61343739353764363165343237303336,Retro Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,lefttrigger:b18,leftx:a0,lefty:a1,start:b10,x:b2,y:b3,",
+"526574726f696420506f636b65742043,Retroid Pocket,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"582d426f7820436f6e74726f6c6c6572,Retroid Pocket,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"64633735616665613536653363336132,Retroid Pocket,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,paddle1:b19,paddle2:b20,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"38653130373365613538333235303036,Retroid Pocket 2,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"64363363336633363736393038313463,Retrolink,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,start:b6,",
+"37393234373533633333323633646531,RetroUSB N64 RetroPort,+rightx:b17,+righty:b15,-rightx:b18,-righty:b6,a:b10,b:b9,dpdown:b19,dpleft:b1,dpright:b0,dpup:b2,leftshoulder:b7,lefttrigger:b20,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,",
+"5365616c6965436f6d707574696e6720,RetroUSB N64 RetroPort,+rightx:b17,+righty:b15,-rightx:b18,-righty:b6,a:b10,b:b9,dpdown:b19,dpleft:b1,dpright:b0,dpup:b2,leftshoulder:b7,lefttrigger:b20,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,",
+"526574726f5553422e636f6d20534e45,RetroUSB SNES RetroPort,a:b1,b:b20,back:b19,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b2,x:b0,y:b3,",
+"64643037633038386238303966376137,RetroUSB SNES RetroPort,a:b1,b:b20,back:b19,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b2,x:b0,y:b3,",
+"37656564346533643138636436356230,Rock Candy Switch Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,",
+"33373336396634316434323337666361,RumblePad 2,a:b22,b:b23,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b24,",
+"36363537303435333566386638366333,Samsung EIGP20,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"53616d73756e672047616d6520506164,Samsung EIGP20,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"66386565396238363534313863353065,Sanwa PlayOnline Mobile,a:b21,b:b22,back:b23,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b24,",
+"32383165316333383766336338373261,Saturn,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:a4,righttrigger:a5,x:b2,y:b3,",
+"38613865396530353338373763623431,Saturn,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,lefttrigger:b10,rightshoulder:b20,righttrigger:b19,start:b17,x:b2,y:b3,",
+"61316232336262373631343137633631,Saturn,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:a4,righttrigger:a5,x:b2,y:b3,",
+"30353835333338613130373363646337,SG H510,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,",
+"66386262366536653765333235343634,SG H510,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,",
+"66633132393363353531373465633064,SG H510,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,",
+"62653761636366393366613135366338,SN30 PP,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,",
+"38376662666661636265313264613039,SNES,a:b0,b:b1,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b20,start:b10,x:b19,y:b2,",
+"5346432f555342205061640000000000,SNES Adapter,a:b0,b:b1,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b20,start:b10,x:b19,y:b2,",
+"5553422047616d657061642000000000,SNES Controller,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,",
+"62653335326261303663356263626339,Sony PlayStation Classic Controller,a:b19,b:b1,back:b17,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,lefttrigger:b3,rightshoulder:b10,righttrigger:b20,start:b18,x:b2,y:b0,",
+"536f6e7920496e746572616374697665,Sony PlayStation Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"576972656c65737320436f6e74726f6c,Sony PlayStation Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"63303964303462366136616266653561,Sony PSP,a:b21,b:b22,back:b27,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b28,x:b23,y:b24,",
+"63376637643462343766333462383235,Sony Vita,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a3,righty:a4,start:b18,x:b0,y:b2,",
+"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
+"05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,",
+"0500000011010000201400000f7e0f00,SteelSeries Nimbus,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,x:b19,y:b2,",
+"35306436396437373135383665646464,SteelSeries Nimbus Plus,a:b0,b:b1,leftshoulder:b3,leftstick:b17,lefttrigger:b9,leftx:a0,rightshoulder:b20,rightstick:b18,righttrigger:b10,rightx:a2,x:b19,y:b2,",
+"33313930373536613937326534303931,Taito Egret II Mini Control Panel,a:b25,b:b23,back:b27,guide:b30,leftx:a0,lefty:a1,rightshoulder:b21,righttrigger:b22,start:b28,x:b29,y:b24,",
+"54475a20436f6e74726f6c6c65720000,TGZ Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"62363434353532386238336663643836,TGZ Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"37323236633763666465316365313236,THEC64 Joystick,a:b21,b:b22,back:b27,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b27,x:b23,y:b24,",
+"38346162326232346533316164363336,THEGamepad,a:b23,b:b22,back:b27,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b28,x:b24,y:b21,",
+"050000004f0400000ed00000fffe3f00,Thrustmaster eSwap Pro Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"5477696e20555342204a6f7973746963,Twin Joystick,a:b22,b:b21,back:b28,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,",
+"30623739343039643830333266346439,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b24,paddle2:b23,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"31643365666432386133346639383937,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b24,paddle2:b23,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"30386438313564306161393537333663,Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,",
+"33333034646336346339646538643633,Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,",
+"050000005e0400008e02000000783f00,Xbox 360 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"30396232393162346330326334636566,Xbox 360 Controller,a:b0,b:b1,back:b4,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"38313038323730383864666463383533,Xbox 360 Controller,a:b0,b:b1,back:b4,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"58626f782033363020576972656c6573,Xbox 360 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"65353331386662343338643939643636,Xbox 360 Controller,a:b0,b:b1,back:b4,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"65613532386633373963616462363038,Xbox 360 Controller,a:b0,b:b1,back:b4,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"47656e6572696320582d426f78207061,Xbox Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"4d6963726f736f667420582d426f7820,Xbox Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"64633436313965656664373634323364,Xbox Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"050000005e04000091020000ff073f00,Xbox One Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
+"050000005e04000091020000ff073f80,Xbox One Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"050000005e040000e00200000ffe3f00,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,",
+"050000005e040000e00200000ffe3f80,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a2,righty:a3,start:b10,x:b17,y:b2,",
+"050000005e040000e0020000ffff3f00,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b4,leftshoulder:b3,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,",
+"050000005e040000e0020000ffff3f80,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b4,leftshoulder:b3,leftstick:b8,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b10,x:b17,y:b2,",
+"050000005e040000fd020000ffff3f00,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"33356661323266333733373865656366,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"34356136633366613530316338376136,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,",
+"35623965373264386238353433656138,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"36616131643361333337396261666433,Xbox One Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"58626f7820576972656c65737320436f,Xbox One Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"65316262316265373335666131623538,Xbox One Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"050000005e040000000b000000783f00,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
+"050000005e040000000b000000783f80,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"050000005e040000050b0000ffff3f00,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a6,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"050000005e040000e002000000783f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"050000005e040000ea02000000783f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"050000005e040000fd020000ff7f3f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"050000005e040000120b000000783f00,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,",
+"050000005e040000120b000000783f80,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"050000005e040000130b0000ffff3f00,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"65633038363832353634653836396239,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,",
+"050000001727000044310000ffff3f00,XiaoMi Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a6,rightx:a2,righty:a5,start:b6,x:b2,y:b3,"
+#elif defined(EMSCRIPTEN)
+"\0" /* look into at some point */
+#endif
+};
+
+void mg_mappings_init(void) {
+ mg_size_t i;
+ mappings.mappingCount = 0;
+ mappings.mappingMax = 1300;
+ MG_MEMSET(mappings.mappings, 0, sizeof(mappings.mappings));
+
+ for (i = 0; i < (sizeof(sdl_db) / sizeof(char*)); i++) {
+ if (parseMapping(&mappings.mappings[mappings.mappingCount], sdl_db[i]))
+ mappings.mappingCount++;
+ }
+}
+
+const char* mg_button_get_name(mg_button btn) {
+ switch (btn) {
+ case MG_BUTTON_COUNT:
+ case MG_BUTTON_UNKNOWN:
+ return "Unknown Button";
+ case MG_BUTTON_SOUTH:
+ return "South Button";
+ case MG_BUTTON_WEST:
+ return "West Button";
+ case MG_BUTTON_NORTH:
+ return "North Button";
+ case MG_BUTTON_EAST:
+ return "East Button";
+ case MG_BUTTON_BACK:
+ return "Back Button";
+ case MG_BUTTON_GUIDE:
+ return "Guide Button";
+ case MG_BUTTON_START:
+ return "Start Button";
+ case MG_BUTTON_LEFT_STICK:
+ return "Left Stick Button";
+ case MG_BUTTON_RIGHT_STICK:
+ return "Right Stick Button";
+ case MG_BUTTON_DPAD_UP:
+ return "D-pad Up";
+ case MG_BUTTON_DPAD_DOWN:
+ return "D-pad Down";
+ case MG_BUTTON_DPAD_LEFT:
+ return "D-pad Left";
+ case MG_BUTTON_DPAD_RIGHT:
+ return "D-pad Right";
+ case MG_BUTTON_LEFT_SHOULDER:
+ return "Left Shoulder Button";
+ case MG_BUTTON_RIGHT_SHOULDER:
+ return "Right Shoulder Button";
+ case MG_BUTTON_LEFT_TRIGGER:
+ return "Left Trigger Button";
+ case MG_BUTTON_RIGHT_TRIGGER:
+ return "Right Trigger Button";
+ case MG_BUTTON_MISC1:
+ return "Misc Button 1";
+ case MG_BUTTON_RIGHT_PADDLE1:
+ return "Paddle 1 Right";
+ case MG_BUTTON_LEFT_PADDLE1:
+ return "Paddle 1 Left";
+ case MG_BUTTON_RIGHT_PADDLE2:
+ return "Paddle 2 Right";
+ case MG_BUTTON_LEFT_PADDLE2:
+ return "Paddle 2 Left";
+ case MG_BUTTON_TOUCHPAD:
+ return "Touchpad";
+ case MG_BUTTON_MISC2:
+ return "Misc Button 2";
+ case MG_BUTTON_MISC3:
+ return "Misc Button 3";
+ case MG_BUTTON_MISC4:
+ return "Misc Button 4";
+ case MG_BUTTON_MISC5:
+ return "Misc Button 5";
+ case MG_BUTTON_MISC6:
+ return "Misc Button 6";
+ default: return NULL;
+ }
+ return NULL;
+}
+
+const char* mg_axis_get_name(mg_axis axis) {
+ switch (axis) {
+ case MG_AXIS_COUNT:
+ case MG_AXIS_UNKNOWN:
+ return "Unknown Axis";
+ case MG_AXIS_LEFT_X:
+ return "X Axis";
+ case MG_AXIS_LEFT_Y:
+ return "Y Axis";
+ case MG_AXIS_LEFT_TRIGGER:
+ return "Z Axis";
+ case MG_AXIS_RIGHT_X:
+ return "RX Axis";
+ case MG_AXIS_RIGHT_Y:
+ return "RY Axis";
+ case MG_AXIS_RIGHT_TRIGGER:
+ return "RZ Axis";
+ case MG_AXIS_THROTTLE:
+ return "Throttle";
+ case MG_AXIS_RUDDER:
+ return "Rudder";
+ case MG_AXIS_WHEEL:
+ return "Wheel";
+ case MG_AXIS_GAS:
+ return "Gas";
+ case MG_AXIS_BRAKE:
+ return "Brake";
+ case MG_AXIS_HAT_DPAD_LEFT_RIGHT:
+ return "Hat D-Pad Left-Right Axis";
+ case MG_AXIS_HAT_DPAD_UP_DOWN:
+ return "Hat D-Pad Up-Down Axis";
+ case MG_AXIS_HAT1X:
+ return "Hat 1 X Axis";
+ case MG_AXIS_HAT1Y:
+ return "Hat 1 Y Axis";
+ case MG_AXIS_HAT2X:
+ return "Hat 2 X Axis";
+ case MG_AXIS_HAT2Y:
+ return "Hat 2 Y Axis";
+ case MG_AXIS_HAT3X:
+ return "Hat 3 X Axis";
+ case MG_AXIS_HAT3Y:
+ return "Hat 3 Y Axis";
+ case MG_AXIS_PRESSURE:
+ return "Pressure Axis";
+ case MG_AXIS_DISTANCE:
+ return "Distance Axis";
+ case MG_AXIS_TILT_X:
+ return "Tilt X Axis";
+ case MG_AXIS_TILT_Y:
+ return "Tilt Y Axis";
+ case MG_AXIS_TOOL_WIDTH:
+ return "Tool Width Axis";
+ case MG_AXIS_VOLUME:
+ return "Volume Axis";
+ case MG_AXIS_PROFILE:
+ return "Profile Axis";
+ case MG_AXIS_MISC:
+ return "Misc Axis";
+ default: break;
+ }
+ return NULL;
+}
+
+#endif /* MG_IMPLEMENTATION */
+
+#if defined(__cplusplus) && !defined(__EMSCRIPTEN__)
+}
+#endif
diff --git a/src/external/RGFW/deps/wayland/pointer-constraints-unstable-v1.xml b/src/external/RGFW/deps/wayland/pointer-constraints-unstable-v1.xml
new file mode 100644
index 000000000..83b0a1c2c
--- /dev/null
+++ b/src/external/RGFW/deps/wayland/pointer-constraints-unstable-v1.xml
@@ -0,0 +1,334 @@
+
+
+
+
+ Copyright © 2014 Jonas Ådahl
+ Copyright © 2015 Red Hat Inc.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+
+
+ This protocol specifies a set of interfaces used for adding constraints to
+ the motion of a pointer. Possible constraints include confining pointer
+ motions to a given region, or locking it to its current position.
+
+ In order to constrain the pointer, a client must first bind the global
+ interface "wp_pointer_constraints" which, if a compositor supports pointer
+ constraints, is exposed by the registry. Using the bound global object, the
+ client uses the request that corresponds to the type of constraint it wants
+ to make. See wp_pointer_constraints for more details.
+
+ Warning! The protocol described in this file is experimental and backward
+ incompatible changes may be made. Backward compatible changes may be added
+ together with the corresponding interface version bump. Backward
+ incompatible changes are done by bumping the version number in the protocol
+ and interface names and resetting the interface version. Once the protocol
+ is to be declared stable, the 'z' prefix and the version number in the
+ protocol and interface names are removed and the interface version number is
+ reset.
+
+
+
+
+ The global interface exposing pointer constraining functionality. It
+ exposes two requests: lock_pointer for locking the pointer to its
+ position, and confine_pointer for locking the pointer to a region.
+
+ The lock_pointer and confine_pointer requests create the objects
+ wp_locked_pointer and wp_confined_pointer respectively, and the client can
+ use these objects to interact with the lock.
+
+ For any surface, only one lock or confinement may be active across all
+ wl_pointer objects of the same seat. If a lock or confinement is requested
+ when another lock or confinement is active or requested on the same surface
+ and with any of the wl_pointer objects of the same seat, an
+ 'already_constrained' error will be raised.
+
+
+
+
+ These errors can be emitted in response to wp_pointer_constraints
+ requests.
+
+
+
+
+
+
+ These values represent different lifetime semantics. They are passed
+ as arguments to the factory requests to specify how the constraint
+ lifetimes should be managed.
+
+
+
+ A oneshot pointer constraint will never reactivate once it has been
+ deactivated. See the corresponding deactivation event
+ (wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) for
+ details.
+
+
+
+
+ A persistent pointer constraint may again reactivate once it has
+ been deactivated. See the corresponding deactivation event
+ (wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) for
+ details.
+
+
+
+
+
+
+ Used by the client to notify the server that it will no longer use this
+ pointer constraints object.
+
+
+
+
+
+ The lock_pointer request lets the client request to disable movements of
+ the virtual pointer (i.e. the cursor), effectively locking the pointer
+ to a position. This request may not take effect immediately; in the
+ future, when the compositor deems implementation-specific constraints
+ are satisfied, the pointer lock will be activated and the compositor
+ sends a locked event.
+
+ The protocol provides no guarantee that the constraints are ever
+ satisfied, and does not require the compositor to send an error if the
+ constraints cannot ever be satisfied. It is thus possible to request a
+ lock that will never activate.
+
+ There may not be another pointer constraint of any kind requested or
+ active on the surface for any of the wl_pointer objects of the seat of
+ the passed pointer when requesting a lock. If there is, an error will be
+ raised. See general pointer lock documentation for more details.
+
+ The intersection of the region passed with this request and the input
+ region of the surface is used to determine where the pointer must be
+ in order for the lock to activate. It is up to the compositor whether to
+ warp the pointer or require some kind of user interaction for the lock
+ to activate. If the region is null the surface input region is used.
+
+ A surface may receive pointer focus without the lock being activated.
+
+ The request creates a new object wp_locked_pointer which is used to
+ interact with the lock as well as receive updates about its state. See
+ the the description of wp_locked_pointer for further information.
+
+ Note that while a pointer is locked, the wl_pointer objects of the
+ corresponding seat will not emit any wl_pointer.motion events, but
+ relative motion events will still be emitted via wp_relative_pointer
+ objects of the same seat. wl_pointer.axis and wl_pointer.button events
+ are unaffected.
+
+
+
+
+
+
+
+
+
+
+ The confine_pointer request lets the client request to confine the
+ pointer cursor to a given region. This request may not take effect
+ immediately; in the future, when the compositor deems implementation-
+ specific constraints are satisfied, the pointer confinement will be
+ activated and the compositor sends a confined event.
+
+ The intersection of the region passed with this request and the input
+ region of the surface is used to determine where the pointer must be
+ in order for the confinement to activate. It is up to the compositor
+ whether to warp the pointer or require some kind of user interaction for
+ the confinement to activate. If the region is null the surface input
+ region is used.
+
+ The request will create a new object wp_confined_pointer which is used
+ to interact with the confinement as well as receive updates about its
+ state. See the the description of wp_confined_pointer for further
+ information.
+
+
+
+
+
+
+
+
+
+
+
+ The wp_locked_pointer interface represents a locked pointer state.
+
+ While the lock of this object is active, the wl_pointer objects of the
+ associated seat will not emit any wl_pointer.motion events.
+
+ This object will send the event 'locked' when the lock is activated.
+ Whenever the lock is activated, it is guaranteed that the locked surface
+ will already have received pointer focus and that the pointer will be
+ within the region passed to the request creating this object.
+
+ To unlock the pointer, send the destroy request. This will also destroy
+ the wp_locked_pointer object.
+
+ If the compositor decides to unlock the pointer the unlocked event is
+ sent. See wp_locked_pointer.unlock for details.
+
+ When unlocking, the compositor may warp the cursor position to the set
+ cursor position hint. If it does, it will not result in any relative
+ motion events emitted via wp_relative_pointer.
+
+ If the surface the lock was requested on is destroyed and the lock is not
+ yet activated, the wp_locked_pointer object is now defunct and must be
+ destroyed.
+
+
+
+
+ Destroy the locked pointer object. If applicable, the compositor will
+ unlock the pointer.
+
+
+
+
+
+ Set the cursor position hint relative to the top left corner of the
+ surface.
+
+ If the client is drawing its own cursor, it should update the position
+ hint to the position of its own cursor. A compositor may use this
+ information to warp the pointer upon unlock in order to avoid pointer
+ jumps.
+
+ The cursor position hint is double-buffered state, see
+ wl_surface.commit.
+
+
+
+
+
+
+
+ Set a new region used to lock the pointer.
+
+ The new lock region is double-buffered, see wl_surface.commit.
+
+ For details about the lock region, see wp_locked_pointer.
+
+
+
+
+
+
+ Notification that the pointer lock of the seat's pointer is activated.
+
+
+
+
+
+ Notification that the pointer lock of the seat's pointer is no longer
+ active. If this is a oneshot pointer lock (see
+ wp_pointer_constraints.lifetime) this object is now defunct and should
+ be destroyed. If this is a persistent pointer lock (see
+ wp_pointer_constraints.lifetime) this pointer lock may again
+ reactivate in the future.
+
+
+
+
+
+
+ The wp_confined_pointer interface represents a confined pointer state.
+
+ This object will send the event 'confined' when the confinement is
+ activated. Whenever the confinement is activated, it is guaranteed that
+ the surface the pointer is confined to will already have received pointer
+ focus and that the pointer will be within the region passed to the request
+ creating this object. It is up to the compositor to decide whether this
+ requires some user interaction and if the pointer will warp to within the
+ passed region if outside.
+
+ To unconfine the pointer, send the destroy request. This will also destroy
+ the wp_confined_pointer object.
+
+ If the compositor decides to unconfine the pointer the unconfined event is
+ sent. The wp_confined_pointer object is at this point defunct and should
+ be destroyed.
+
+
+
+
+ Destroy the confined pointer object. If applicable, the compositor will
+ unconfine the pointer.
+
+
+
+
+
+ Set a new region used to confine the pointer.
+
+ The new confine region is double-buffered, see wl_surface.commit.
+
+ If the confinement is active when the new confinement region is applied
+ and the pointer ends up outside of newly applied region, the pointer may
+ warped to a position within the new confinement region. If warped, a
+ wl_pointer.motion event will be emitted, but no
+ wp_relative_pointer.relative_motion event.
+
+ The compositor may also, instead of using the new region, unconfine the
+ pointer.
+
+ For details about the confine region, see wp_confined_pointer.
+
+
+
+
+
+
+ Notification that the pointer confinement of the seat's pointer is
+ activated.
+
+
+
+
+
+ Notification that the pointer confinement of the seat's pointer is no
+ longer active. If this is a oneshot pointer confinement (see
+ wp_pointer_constraints.lifetime) this object is now defunct and should
+ be destroyed. If this is a persistent pointer confinement (see
+ wp_pointer_constraints.lifetime) this pointer confinement may again
+ reactivate in the future.
+
+
+
+
+
diff --git a/src/external/RGFW/deps/wayland/pointer-warp-v1.xml b/src/external/RGFW/deps/wayland/pointer-warp-v1.xml
new file mode 100644
index 000000000..158dad83c
--- /dev/null
+++ b/src/external/RGFW/deps/wayland/pointer-warp-v1.xml
@@ -0,0 +1,72 @@
+
+
+
+ Copyright © 2024 Neal Gompa
+ Copyright © 2024 Xaver Hugl
+ Copyright © 2024 Matthias Klumpp
+ Copyright © 2024 Vlad Zahorodnii
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+
+
+
+ This global interface allows applications to request the pointer to be
+ moved to a position relative to a wl_surface.
+
+ Note that if the desired behavior is to constrain the pointer to an area
+ or lock it to a position, this protocol does not provide a reliable way
+ to do that. The pointer constraint and pointer lock protocols should be
+ used for those use cases instead.
+
+ Warning! The protocol described in this file is currently in the testing
+ phase. Backward compatible changes may be added together with the
+ corresponding interface version bump. Backward incompatible changes can
+ only be done by creating a new major version of the extension.
+
+
+
+
+ Destroy the pointer warp manager.
+
+
+
+
+
+ Request the compositor to move the pointer to a surface-local position.
+ Whether or not the compositor honors the request is implementation defined,
+ but it should
+ - honor it if the surface has pointer focus, including
+ when it has an implicit pointer grab
+ - reject it if the enter serial is incorrect
+ - reject it if the requested position is outside of the surface
+
+ Note that the enter serial is valid for any surface of the client,
+ and does not have to be from the surface the pointer is warped to.
+
+
+
+
+
+
+
+
+
+
diff --git a/src/external/RGFW/deps/wayland/relative-pointer-unstable-v1.xml b/src/external/RGFW/deps/wayland/relative-pointer-unstable-v1.xml
new file mode 100644
index 000000000..ca6f81d12
--- /dev/null
+++ b/src/external/RGFW/deps/wayland/relative-pointer-unstable-v1.xml
@@ -0,0 +1,136 @@
+
+
+
+
+ Copyright © 2014 Jonas Ådahl
+ Copyright © 2015 Red Hat Inc.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+
+
+ This protocol specifies a set of interfaces used for making clients able to
+ receive relative pointer events not obstructed by barriers (such as the
+ monitor edge or other pointer barriers).
+
+ To start receiving relative pointer events, a client must first bind the
+ global interface "wp_relative_pointer_manager" which, if a compositor
+ supports relative pointer motion events, is exposed by the registry. After
+ having created the relative pointer manager proxy object, the client uses
+ it to create the actual relative pointer object using the
+ "get_relative_pointer" request given a wl_pointer. The relative pointer
+ motion events will then, when applicable, be transmitted via the proxy of
+ the newly created relative pointer object. See the documentation of the
+ relative pointer interface for more details.
+
+ Warning! The protocol described in this file is experimental and backward
+ incompatible changes may be made. Backward compatible changes may be added
+ together with the corresponding interface version bump. Backward
+ incompatible changes are done by bumping the version number in the protocol
+ and interface names and resetting the interface version. Once the protocol
+ is to be declared stable, the 'z' prefix and the version number in the
+ protocol and interface names are removed and the interface version number is
+ reset.
+
+
+
+
+ A global interface used for getting the relative pointer object for a
+ given pointer.
+
+
+
+
+ Used by the client to notify the server that it will no longer use this
+ relative pointer manager object.
+
+
+
+
+
+ Create a relative pointer interface given a wl_pointer object. See the
+ wp_relative_pointer interface for more details.
+
+
+
+
+
+
+
+
+ A wp_relative_pointer object is an extension to the wl_pointer interface
+ used for emitting relative pointer events. It shares the same focus as
+ wl_pointer objects of the same seat and will only emit events when it has
+ focus.
+
+
+
+
+
+
+
+
+ Relative x/y pointer motion from the pointer of the seat associated with
+ this object.
+
+ A relative motion is in the same dimension as regular wl_pointer motion
+ events, except they do not represent an absolute position. For example,
+ moving a pointer from (x, y) to (x', y') would have the equivalent
+ relative motion (x' - x, y' - y). If a pointer motion caused the
+ absolute pointer position to be clipped by for example the edge of the
+ monitor, the relative motion is unaffected by the clipping and will
+ represent the unclipped motion.
+
+ This event also contains non-accelerated motion deltas. The
+ non-accelerated delta is, when applicable, the regular pointer motion
+ delta as it was before having applied motion acceleration and other
+ transformations such as normalization.
+
+ Note that the non-accelerated delta does not represent 'raw' events as
+ they were read from some device. Pointer motion acceleration is device-
+ and configuration-specific and non-accelerated deltas and accelerated
+ deltas may have the same value on some devices.
+
+ Relative motions are not coupled to wl_pointer.motion events, and can be
+ sent in combination with such events, but also independently. There may
+ also be scenarios where wl_pointer.motion is sent, but there is no
+ relative motion. The order of an absolute and relative motion event
+ originating from the same physical motion is not guaranteed.
+
+ If the client needs button events or focus state, it can receive them
+ from a wl_pointer object of the same seat that the wp_relative_pointer
+ object is associated with.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/external/RGFW/deps/wayland/xdg-decoration-unstable-v1.xml b/src/external/RGFW/deps/wayland/xdg-decoration-unstable-v1.xml
new file mode 100644
index 000000000..82ca247c7
--- /dev/null
+++ b/src/external/RGFW/deps/wayland/xdg-decoration-unstable-v1.xml
@@ -0,0 +1,160 @@
+
+
+
+ Copyright © 2018 Simon Ser
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+
+
+
+ This interface allows a compositor to announce support for server-side
+ decorations.
+
+ A window decoration is a set of window controls as deemed appropriate by
+ the party managing them, such as user interface components used to move,
+ resize and change a window's state.
+
+ A client can use this protocol to request being decorated by a supporting
+ compositor.
+
+ If compositor and client do not negotiate the use of a server-side
+ decoration using this protocol, clients continue to self-decorate as they
+ see fit.
+
+ Warning! The protocol described in this file is experimental and
+ backward incompatible changes may be made. Backward compatible changes
+ may be added together with the corresponding interface version bump.
+ Backward incompatible changes are done by bumping the version number in
+ the protocol and interface names and resetting the interface version.
+ Once the protocol is to be declared stable, the 'z' prefix and the
+ version number in the protocol and interface names are removed and the
+ interface version number is reset.
+
+
+
+
+ Destroy the decoration manager. This doesn't destroy objects created
+ with the manager.
+
+
+
+
+
+ Create a new decoration object associated with the given toplevel.
+
+ Creating an xdg_toplevel_decoration from an xdg_toplevel which has a
+ buffer attached or committed is a client error, and any attempts by a
+ client to attach or manipulate a buffer prior to the first
+ xdg_toplevel_decoration.configure event must also be treated as
+ errors.
+
+
+
+
+
+
+
+
+ The decoration object allows the compositor to toggle server-side window
+ decorations for a toplevel surface. The client can request to switch to
+ another mode.
+
+ The xdg_toplevel_decoration object must be destroyed before its
+ xdg_toplevel.
+
+
+
+
+
+
+
+
+
+
+
+ Switch back to a mode without any server-side decorations at the next
+ commit.
+
+
+
+
+
+ These values describe window decoration modes.
+
+
+
+
+
+
+
+ Set the toplevel surface decoration mode. This informs the compositor
+ that the client prefers the provided decoration mode.
+
+ After requesting a decoration mode, the compositor will respond by
+ emitting an xdg_surface.configure event. The client should then update
+ its content, drawing it without decorations if the received mode is
+ server-side decorations. The client must also acknowledge the configure
+ when committing the new content (see xdg_surface.ack_configure).
+
+ The compositor can decide not to use the client's mode and enforce a
+ different mode instead.
+
+ Clients whose decoration mode depend on the xdg_toplevel state may send
+ a set_mode request in response to an xdg_surface.configure event and wait
+ for the next xdg_surface.configure event to prevent unwanted state.
+ Such clients are responsible for preventing configure loops and must
+ make sure not to send multiple successive set_mode requests with the
+ same decoration mode.
+
+ If an invalid mode is supplied by the client, the invalid_mode protocol
+ error is raised by the compositor.
+
+
+
+
+
+
+ Unset the toplevel surface decoration mode. This informs the compositor
+ that the client doesn't prefer a particular decoration mode.
+
+ This request has the same semantics as set_mode.
+
+
+
+
+
+ The configure event configures the effective decoration mode. The
+ configured state should not be applied immediately. Clients must send an
+ ack_configure in response to this event. See xdg_surface.configure and
+ xdg_surface.ack_configure for details.
+
+ A configure event can be sent at any time. The specified mode must be
+ obeyed by the client.
+
+
+
+
+
diff --git a/src/external/RGFW/deps/wayland/xdg-output-unstable-v1.xml b/src/external/RGFW/deps/wayland/xdg-output-unstable-v1.xml
new file mode 100644
index 000000000..a7306e419
--- /dev/null
+++ b/src/external/RGFW/deps/wayland/xdg-output-unstable-v1.xml
@@ -0,0 +1,222 @@
+
+
+
+
+ Copyright © 2017 Red Hat Inc.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+
+
+ This protocol aims at describing outputs in a way which is more in line
+ with the concept of an output on desktop oriented systems.
+
+ Some information are more specific to the concept of an output for
+ a desktop oriented system and may not make sense in other applications,
+ such as IVI systems for example.
+
+ Typically, the global compositor space on a desktop system is made of
+ a contiguous or overlapping set of rectangular regions.
+
+ The logical_position and logical_size events defined in this protocol
+ might provide information identical to their counterparts already
+ available from wl_output, in which case the information provided by this
+ protocol should be preferred to their equivalent in wl_output. The goal is
+ to move the desktop specific concepts (such as output location within the
+ global compositor space, etc.) out of the core wl_output protocol.
+
+ Warning! The protocol described in this file is experimental and
+ backward incompatible changes may be made. Backward compatible
+ changes may be added together with the corresponding interface
+ version bump.
+ Backward incompatible changes are done by bumping the version
+ number in the protocol and interface names and resetting the
+ interface version. Once the protocol is to be declared stable,
+ the 'z' prefix and the version number in the protocol and
+ interface names are removed and the interface version number is
+ reset.
+
+
+
+
+ A global factory interface for xdg_output objects.
+
+
+
+
+ Using this request a client can tell the server that it is not
+ going to use the xdg_output_manager object anymore.
+
+ Any objects already created through this instance are not affected.
+
+
+
+
+
+ This creates a new xdg_output object for the given wl_output.
+
+
+
+
+
+
+
+
+ An xdg_output describes part of the compositor geometry.
+
+ This typically corresponds to a monitor that displays part of the
+ compositor space.
+
+ For objects version 3 onwards, after all xdg_output properties have been
+ sent (when the object is created and when properties are updated), a
+ wl_output.done event is sent. This allows changes to the output
+ properties to be seen as atomic, even if they happen via multiple events.
+
+
+
+
+ Using this request a client can tell the server that it is not
+ going to use the xdg_output object anymore.
+
+
+
+
+
+ The position event describes the location of the wl_output within
+ the global compositor space.
+
+ The logical_position event is sent after creating an xdg_output
+ (see xdg_output_manager.get_xdg_output) and whenever the location
+ of the output changes within the global compositor space.
+
+
+
+
+
+
+
+ The logical_size event describes the size of the output in the
+ global compositor space.
+
+ Most regular Wayland clients should not pay attention to the
+ logical size and would rather rely on xdg_shell interfaces.
+
+ Some clients such as Xwayland, however, need this to configure
+ their surfaces in the global compositor space as the compositor
+ may apply a different scale from what is advertised by the output
+ scaling property (to achieve fractional scaling, for example).
+
+ For example, for a wl_output mode 3840×2160 and a scale factor 2:
+
+ - A compositor not scaling the monitor viewport in its compositing space
+ will advertise a logical size of 3840×2160,
+
+ - A compositor scaling the monitor viewport with scale factor 2 will
+ advertise a logical size of 1920×1080,
+
+ - A compositor scaling the monitor viewport using a fractional scale of
+ 1.5 will advertise a logical size of 2560×1440.
+
+ For example, for a wl_output mode 1920×1080 and a 90 degree rotation,
+ the compositor will advertise a logical size of 1080x1920.
+
+ The logical_size event is sent after creating an xdg_output
+ (see xdg_output_manager.get_xdg_output) and whenever the logical
+ size of the output changes, either as a result of a change in the
+ applied scale or because of a change in the corresponding output
+ mode(see wl_output.mode) or transform (see wl_output.transform).
+
+
+
+
+
+
+
+ This event is sent after all other properties of an xdg_output
+ have been sent.
+
+ This allows changes to the xdg_output properties to be seen as
+ atomic, even if they happen via multiple events.
+
+ For objects version 3 onwards, this event is deprecated. Compositors
+ are not required to send it anymore and must send wl_output.done
+ instead.
+
+
+
+
+
+
+
+ Many compositors will assign names to their outputs, show them to the
+ user, allow them to be configured by name, etc. The client may wish to
+ know this name as well to offer the user similar behaviors.
+
+ The naming convention is compositor defined, but limited to
+ alphanumeric characters and dashes (-). Each name is unique among all
+ wl_output globals, but if a wl_output global is destroyed the same name
+ may be reused later. The names will also remain consistent across
+ sessions with the same hardware and software configuration.
+
+ Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do
+ not assume that the name is a reflection of an underlying DRM
+ connector, X11 connection, etc.
+
+ The name event is sent after creating an xdg_output (see
+ xdg_output_manager.get_xdg_output). This event is only sent once per
+ xdg_output, and the name does not change over the lifetime of the
+ wl_output global.
+
+ This event is deprecated, instead clients should use wl_output.name.
+ Compositors must still support this event.
+
+
+
+
+
+
+ Many compositors can produce human-readable descriptions of their
+ outputs. The client may wish to know this description as well, to
+ communicate the user for various purposes.
+
+ The description is a UTF-8 string with no convention defined for its
+ contents. Examples might include 'Foocorp 11" Display' or 'Virtual X11
+ output via :1'.
+
+ The description event is sent after creating an xdg_output (see
+ xdg_output_manager.get_xdg_output) and whenever the description
+ changes. The description is optional, and may not be sent at all.
+
+ For objects of version 2 and lower, this event is only sent once per
+ xdg_output, and the description does not change over the lifetime of
+ the wl_output global.
+
+ This event is deprecated, instead clients should use
+ wl_output.description. Compositors must still support this event.
+
+
+
+
+
+
diff --git a/src/external/RGFW/deps/wayland/xdg-shell.xml b/src/external/RGFW/deps/wayland/xdg-shell.xml
new file mode 100644
index 000000000..39ecf8acc
--- /dev/null
+++ b/src/external/RGFW/deps/wayland/xdg-shell.xml
@@ -0,0 +1,1418 @@
+
+
+
+
+ Copyright © 2008-2013 Kristian Høgsberg
+ Copyright © 2013 Rafael Antognolli
+ Copyright © 2013 Jasper St. Pierre
+ Copyright © 2010-2013 Intel Corporation
+ Copyright © 2015-2017 Samsung Electronics Co., Ltd
+ Copyright © 2015-2017 Red Hat Inc.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+
+
+
+ The xdg_wm_base interface is exposed as a global object enabling clients
+ to turn their wl_surfaces into windows in a desktop environment. It
+ defines the basic functionality needed for clients and the compositor to
+ create windows that can be dragged, resized, maximized, etc, as well as
+ creating transient windows such as popup menus.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Destroy this xdg_wm_base object.
+
+ Destroying a bound xdg_wm_base object while there are surfaces
+ still alive created by this xdg_wm_base object instance is illegal
+ and will result in a defunct_surfaces error.
+
+
+
+
+
+ Create a positioner object. A positioner object is used to position
+ surfaces relative to some parent surface. See the interface description
+ and xdg_surface.get_popup for details.
+
+
+
+
+
+
+ This creates an xdg_surface for the given surface. While xdg_surface
+ itself is not a role, the corresponding surface may only be assigned
+ a role extending xdg_surface, such as xdg_toplevel or xdg_popup. It is
+ illegal to create an xdg_surface for a wl_surface which already has an
+ assigned role and this will result in a role error.
+
+ This creates an xdg_surface for the given surface. An xdg_surface is
+ used as basis to define a role to a given surface, such as xdg_toplevel
+ or xdg_popup. It also manages functionality shared between xdg_surface
+ based surface roles.
+
+ See the documentation of xdg_surface for more details about what an
+ xdg_surface is and how it is used.
+
+
+
+
+
+
+
+ A client must respond to a ping event with a pong request or
+ the client may be deemed unresponsive. See xdg_wm_base.ping
+ and xdg_wm_base.error.unresponsive.
+
+
+
+
+
+
+ The ping event asks the client if it's still alive. Pass the
+ serial specified in the event back to the compositor by sending
+ a "pong" request back with the specified serial. See xdg_wm_base.pong.
+
+ Compositors can use this to determine if the client is still
+ alive. It's unspecified what will happen if the client doesn't
+ respond to the ping request, or in what timeframe. Clients should
+ try to respond in a reasonable amount of time. The “unresponsive”
+ error is provided for compositors that wish to disconnect unresponsive
+ clients.
+
+ A compositor is free to ping in any way it wants, but a client must
+ always respond to any xdg_wm_base object it created.
+
+
+
+
+
+
+
+ The xdg_positioner provides a collection of rules for the placement of a
+ child surface relative to a parent surface. Rules can be defined to ensure
+ the child surface remains within the visible area's borders, and to
+ specify how the child surface changes its position, such as sliding along
+ an axis, or flipping around a rectangle. These positioner-created rules are
+ constrained by the requirement that a child surface must intersect with or
+ be at least partially adjacent to its parent surface.
+
+ See the various requests for details about possible rules.
+
+ At the time of the request, the compositor makes a copy of the rules
+ specified by the xdg_positioner. Thus, after the request is complete the
+ xdg_positioner object can be destroyed or reused; further changes to the
+ object will have no effect on previous usages.
+
+ For an xdg_positioner object to be considered complete, it must have a
+ non-zero size set by set_size, and a non-zero anchor rectangle set by
+ set_anchor_rect. Passing an incomplete xdg_positioner object when
+ positioning a surface raises an invalid_positioner error.
+
+
+
+
+
+
+
+
+ Notify the compositor that the xdg_positioner will no longer be used.
+
+
+
+
+
+ Set the size of the surface that is to be positioned with the positioner
+ object. The size is in surface-local coordinates and corresponds to the
+ window geometry. See xdg_surface.set_window_geometry.
+
+ If a zero or negative size is set the invalid_input error is raised.
+
+
+
+
+
+
+
+ Specify the anchor rectangle within the parent surface that the child
+ surface will be placed relative to. The rectangle is relative to the
+ window geometry as defined by xdg_surface.set_window_geometry of the
+ parent surface.
+
+ When the xdg_positioner object is used to position a child surface, the
+ anchor rectangle may not extend outside the window geometry of the
+ positioned child's parent surface.
+
+ If a negative size is set the invalid_input error is raised.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Defines the anchor point for the anchor rectangle. The specified anchor
+ is used derive an anchor point that the child surface will be
+ positioned relative to. If a corner anchor is set (e.g. 'top_left' or
+ 'bottom_right'), the anchor point will be at the specified corner;
+ otherwise, the derived anchor point will be centered on the specified
+ edge, or in the center of the anchor rectangle if no edge is specified.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Defines in what direction a surface should be positioned, relative to
+ the anchor point of the parent surface. If a corner gravity is
+ specified (e.g. 'bottom_right' or 'top_left'), then the child surface
+ will be placed towards the specified gravity; otherwise, the child
+ surface will be centered over the anchor point on any axis that had no
+ gravity specified. If the gravity is not in the ‘gravity’ enum, an
+ invalid_input error is raised.
+
+
+
+
+
+
+ The constraint adjustment value define ways the compositor will adjust
+ the position of the surface, if the unadjusted position would result
+ in the surface being partly constrained.
+
+ Whether a surface is considered 'constrained' is left to the compositor
+ to determine. For example, the surface may be partly outside the
+ compositor's defined 'work area', thus necessitating the child surface's
+ position be adjusted until it is entirely inside the work area.
+
+ The adjustments can be combined, according to a defined precedence: 1)
+ Flip, 2) Slide, 3) Resize.
+
+
+
+ Don't alter the surface position even if it is constrained on some
+ axis, for example partially outside the edge of an output.
+
+
+
+
+ Slide the surface along the x axis until it is no longer constrained.
+
+ First try to slide towards the direction of the gravity on the x axis
+ until either the edge in the opposite direction of the gravity is
+ unconstrained or the edge in the direction of the gravity is
+ constrained.
+
+ Then try to slide towards the opposite direction of the gravity on the
+ x axis until either the edge in the direction of the gravity is
+ unconstrained or the edge in the opposite direction of the gravity is
+ constrained.
+
+
+
+
+ Slide the surface along the y axis until it is no longer constrained.
+
+ First try to slide towards the direction of the gravity on the y axis
+ until either the edge in the opposite direction of the gravity is
+ unconstrained or the edge in the direction of the gravity is
+ constrained.
+
+ Then try to slide towards the opposite direction of the gravity on the
+ y axis until either the edge in the direction of the gravity is
+ unconstrained or the edge in the opposite direction of the gravity is
+ constrained.
+
+
+
+
+ Invert the anchor and gravity on the x axis if the surface is
+ constrained on the x axis. For example, if the left edge of the
+ surface is constrained, the gravity is 'left' and the anchor is
+ 'left', change the gravity to 'right' and the anchor to 'right'.
+
+ If the adjusted position also ends up being constrained, the resulting
+ position of the flip_x adjustment will be the one before the
+ adjustment.
+
+
+
+
+ Invert the anchor and gravity on the y axis if the surface is
+ constrained on the y axis. For example, if the bottom edge of the
+ surface is constrained, the gravity is 'bottom' and the anchor is
+ 'bottom', change the gravity to 'top' and the anchor to 'top'.
+
+ The adjusted position is calculated given the original anchor
+ rectangle and offset, but with the new flipped anchor and gravity
+ values.
+
+ If the adjusted position also ends up being constrained, the resulting
+ position of the flip_y adjustment will be the one before the
+ adjustment.
+
+
+
+
+ Resize the surface horizontally so that it is completely
+ unconstrained.
+
+
+
+
+ Resize the surface vertically so that it is completely unconstrained.
+
+
+
+
+
+
+ Specify how the window should be positioned if the originally intended
+ position caused the surface to be constrained, meaning at least
+ partially outside positioning boundaries set by the compositor. The
+ adjustment is set by constructing a bitmask describing the adjustment to
+ be made when the surface is constrained on that axis.
+
+ If no bit for one axis is set, the compositor will assume that the child
+ surface should not change its position on that axis when constrained.
+
+ If more than one bit for one axis is set, the order of how adjustments
+ are applied is specified in the corresponding adjustment descriptions.
+
+ The default adjustment is none.
+
+
+
+
+
+
+ Specify the surface position offset relative to the position of the
+ anchor on the anchor rectangle and the anchor on the surface. For
+ example if the anchor of the anchor rectangle is at (x, y), the surface
+ has the gravity bottom|right, and the offset is (ox, oy), the calculated
+ surface position will be (x + ox, y + oy). The offset position of the
+ surface is the one used for constraint testing. See
+ set_constraint_adjustment.
+
+ An example use case is placing a popup menu on top of a user interface
+ element, while aligning the user interface element of the parent surface
+ with some user interface element placed somewhere in the popup surface.
+
+
+
+
+
+
+
+
+
+ When set reactive, the surface is reconstrained if the conditions used
+ for constraining changed, e.g. the parent window moved.
+
+ If the conditions changed and the popup was reconstrained, an
+ xdg_popup.configure event is sent with updated geometry, followed by an
+ xdg_surface.configure event.
+
+
+
+
+
+ Set the parent window geometry the compositor should use when
+ positioning the popup. The compositor may use this information to
+ determine the future state the popup should be constrained using. If
+ this doesn't match the dimension of the parent the popup is eventually
+ positioned against, the behavior is undefined.
+
+ The arguments are given in the surface-local coordinate space.
+
+
+
+
+
+
+
+ Set the serial of an xdg_surface.configure event this positioner will be
+ used in response to. The compositor may use this information together
+ with set_parent_size to determine what future state the popup should be
+ constrained using.
+
+
+
+
+
+
+
+ An interface that may be implemented by a wl_surface, for
+ implementations that provide a desktop-style user interface.
+
+ It provides a base set of functionality required to construct user
+ interface elements requiring management by the compositor, such as
+ toplevel windows, menus, etc. The types of functionality are split into
+ xdg_surface roles.
+
+ Creating an xdg_surface does not set the role for a wl_surface. In order
+ to map an xdg_surface, the client must create a role-specific object
+ using, e.g., get_toplevel, get_popup. The wl_surface for any given
+ xdg_surface can have at most one role, and may not be assigned any role
+ not based on xdg_surface.
+
+ A role must be assigned before any other requests are made to the
+ xdg_surface object.
+
+ The client must call wl_surface.commit on the corresponding wl_surface
+ for the xdg_surface state to take effect.
+
+ Creating an xdg_surface from a wl_surface which has a buffer attached or
+ committed is a client error, and any attempts by a client to attach or
+ manipulate a buffer prior to the first xdg_surface.configure call must
+ also be treated as errors.
+
+ After creating a role-specific object and setting it up (e.g. by sending
+ the title, app ID, size constraints, parent, etc), the client must
+ perform an initial commit without any buffer attached. The compositor
+ will reply with initial wl_surface state such as
+ wl_surface.preferred_buffer_scale followed by an xdg_surface.configure
+ event. The client must acknowledge it and is then allowed to attach a
+ buffer to map the surface.
+
+ Mapping an xdg_surface-based role surface is defined as making it
+ possible for the surface to be shown by the compositor. Note that
+ a mapped surface is not guaranteed to be visible once it is mapped.
+
+ For an xdg_surface to be mapped by the compositor, the following
+ conditions must be met:
+ (1) the client has assigned an xdg_surface-based role to the surface
+ (2) the client has set and committed the xdg_surface state and the
+ role-dependent state to the surface
+ (3) the client has committed a buffer to the surface
+
+ A newly-unmapped surface is considered to have met condition (1) out
+ of the 3 required conditions for mapping a surface if its role surface
+ has not been destroyed, i.e. the client must perform the initial commit
+ again before attaching a buffer.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Destroy the xdg_surface object. An xdg_surface must only be destroyed
+ after its role object has been destroyed, otherwise
+ a defunct_role_object error is raised.
+
+
+
+
+
+ This creates an xdg_toplevel object for the given xdg_surface and gives
+ the associated wl_surface the xdg_toplevel role.
+
+ See the documentation of xdg_toplevel for more details about what an
+ xdg_toplevel is and how it is used.
+
+
+
+
+
+
+ This creates an xdg_popup object for the given xdg_surface and gives
+ the associated wl_surface the xdg_popup role.
+
+ If null is passed as a parent, a parent surface must be specified using
+ some other protocol, before committing the initial state.
+
+ See the documentation of xdg_popup for more details about what an
+ xdg_popup is and how it is used.
+
+
+
+
+
+
+
+
+ The window geometry of a surface is its "visible bounds" from the
+ user's perspective. Client-side decorations often have invisible
+ portions like drop-shadows which should be ignored for the
+ purposes of aligning, placing and constraining windows. Note that
+ in some situations, compositors may clip rendering to the window
+ geometry, so the client should avoid putting functional elements
+ outside of it.
+
+ The window geometry is double-buffered state, see wl_surface.commit.
+
+ When maintaining a position, the compositor should treat the (x, y)
+ coordinate of the window geometry as the top left corner of the window.
+ A client changing the (x, y) window geometry coordinate should in
+ general not alter the position of the window.
+
+ Once the window geometry of the surface is set, it is not possible to
+ unset it, and it will remain the same until set_window_geometry is
+ called again, even if a new subsurface or buffer is attached.
+
+ If never set, the value is the full bounds of the surface,
+ including any subsurfaces. This updates dynamically on every
+ commit. This unset is meant for extremely simple clients.
+
+ The arguments are given in the surface-local coordinate space of
+ the wl_surface associated with this xdg_surface, and may extend outside
+ of the wl_surface itself to mark parts of the subsurface tree as part of
+ the window geometry.
+
+ When applied, the effective window geometry will be the set window
+ geometry clamped to the bounding rectangle of the combined
+ geometry of the surface of the xdg_surface and the associated
+ subsurfaces.
+
+ The effective geometry will not be recalculated unless a new call to
+ set_window_geometry is done and the new pending surface state is
+ subsequently applied.
+
+ The width and height of the effective window geometry must be
+ greater than zero. Setting an invalid size will raise an
+ invalid_size error.
+
+
+
+
+
+
+
+
+
+ When a configure event is received, if a client commits the
+ surface in response to the configure event, then the client
+ must make an ack_configure request sometime before the commit
+ request, passing along the serial of the configure event.
+
+ For instance, for toplevel surfaces the compositor might use this
+ information to move a surface to the top left only when the client has
+ drawn itself for the maximized or fullscreen state.
+
+ If the client receives multiple configure events before it
+ can respond to one, it only has to ack the last configure event.
+ Acking a configure event that was never sent raises an invalid_serial
+ error.
+
+ A client is not required to commit immediately after sending
+ an ack_configure request - it may even ack_configure several times
+ before its next surface commit.
+
+ A client may send multiple ack_configure requests before committing, but
+ only the last request sent before a commit indicates which configure
+ event the client really is responding to.
+
+ Sending an ack_configure request consumes the serial number sent with
+ the request, as well as serial numbers sent by all configure events
+ sent on this xdg_surface prior to the configure event referenced by
+ the committed serial.
+
+ It is an error to issue multiple ack_configure requests referencing a
+ serial from the same configure event, or to issue an ack_configure
+ request referencing a serial from a configure event issued before the
+ event identified by the last ack_configure request for the same
+ xdg_surface. Doing so will raise an invalid_serial error.
+
+
+
+
+
+
+ The configure event marks the end of a configure sequence. A configure
+ sequence is a set of one or more events configuring the state of the
+ xdg_surface, including the final xdg_surface.configure event.
+
+ Where applicable, xdg_surface surface roles will during a configure
+ sequence extend this event as a latched state sent as events before the
+ xdg_surface.configure event. Such events should be considered to make up
+ a set of atomically applied configuration states, where the
+ xdg_surface.configure commits the accumulated state.
+
+ Clients should arrange their surface for the new states, and then send
+ an ack_configure request with the serial sent in this configure event at
+ some point before committing the new surface.
+
+ If the client receives multiple configure events before it can respond
+ to one, it is free to discard all but the last event it received.
+
+
+
+
+
+
+
+
+ This interface defines an xdg_surface role which allows a surface to,
+ among other things, set window-like properties such as maximize,
+ fullscreen, and minimize, set application-specific metadata like title and
+ id, and well as trigger user interactive operations such as interactive
+ resize and move.
+
+ A xdg_toplevel by default is responsible for providing the full intended
+ visual representation of the toplevel, which depending on the window
+ state, may mean things like a title bar, window controls and drop shadow.
+
+ Unmapping an xdg_toplevel means that the surface cannot be shown
+ by the compositor until it is explicitly mapped again.
+ All active operations (e.g., move, resize) are canceled and all
+ attributes (e.g. title, state, stacking, ...) are discarded for
+ an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to
+ the state it had right after xdg_surface.get_toplevel. The client
+ can re-map the toplevel by performing a commit without any buffer
+ attached, waiting for a configure event and handling it as usual (see
+ xdg_surface description).
+
+ Attaching a null buffer to a toplevel unmaps the surface.
+
+
+
+
+ This request destroys the role surface and unmaps the surface;
+ see "Unmapping" behavior in interface section for details.
+
+
+
+
+
+
+
+
+
+
+
+ Set the "parent" of this surface. This surface should be stacked
+ above the parent surface and all other ancestor surfaces.
+
+ Parent surfaces should be set on dialogs, toolboxes, or other
+ "auxiliary" surfaces, so that the parent is raised when the dialog
+ is raised.
+
+ Setting a null parent for a child surface unsets its parent. Setting
+ a null parent for a surface which currently has no parent is a no-op.
+
+ Only mapped surfaces can have child surfaces. Setting a parent which
+ is not mapped is equivalent to setting a null parent. If a surface
+ becomes unmapped, its children's parent is set to the parent of
+ the now-unmapped surface. If the now-unmapped surface has no parent,
+ its children's parent is unset. If the now-unmapped surface becomes
+ mapped again, its parent-child relationship is not restored.
+
+ The parent toplevel must not be one of the child toplevel's
+ descendants, and the parent must be different from the child toplevel,
+ otherwise the invalid_parent protocol error is raised.
+
+
+
+
+
+
+ Set a short title for the surface.
+
+ This string may be used to identify the surface in a task bar,
+ window list, or other user interface elements provided by the
+ compositor.
+
+ The string must be encoded in UTF-8.
+
+
+
+
+
+
+ Set an application identifier for the surface.
+
+ The app ID identifies the general class of applications to which
+ the surface belongs. The compositor can use this to group multiple
+ surfaces together, or to determine how to launch a new application.
+
+ For D-Bus activatable applications, the app ID is used as the D-Bus
+ service name.
+
+ The compositor shell will try to group application surfaces together
+ by their app ID. As a best practice, it is suggested to select app
+ ID's that match the basename of the application's .desktop file.
+ For example, "org.freedesktop.FooViewer" where the .desktop file is
+ "org.freedesktop.FooViewer.desktop".
+
+ Like other properties, a set_app_id request can be sent after the
+ xdg_toplevel has been mapped to update the property.
+
+ See the desktop-entry specification [0] for more details on
+ application identifiers and how they relate to well-known D-Bus
+ names and .desktop files.
+
+ [0] https://standards.freedesktop.org/desktop-entry-spec/
+
+
+
+
+
+
+ Clients implementing client-side decorations might want to show
+ a context menu when right-clicking on the decorations, giving the
+ user a menu that they can use to maximize or minimize the window.
+
+ This request asks the compositor to pop up such a window menu at
+ the given position, relative to the local surface coordinates of
+ the parent surface. There are no guarantees as to what menu items
+ the window menu contains, or even if a window menu will be drawn
+ at all.
+
+ This request must be used in response to some sort of user action
+ like a button press, key press, or touch down event.
+
+
+
+
+
+
+
+
+
+ Start an interactive, user-driven move of the surface.
+
+ This request must be used in response to some sort of user action
+ like a button press, key press, or touch down event. The passed
+ serial is used to determine the type of interactive move (touch,
+ pointer, etc).
+
+ The server may ignore move requests depending on the state of
+ the surface (e.g. fullscreen or maximized), or if the passed serial
+ is no longer valid.
+
+ If triggered, the surface will lose the focus of the device
+ (wl_pointer, wl_touch, etc) used for the move. It is up to the
+ compositor to visually indicate that the move is taking place, such as
+ updating a pointer cursor, during the move. There is no guarantee
+ that the device focus will return when the move is completed.
+
+
+
+
+
+
+
+ These values are used to indicate which edge of a surface
+ is being dragged in a resize operation.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Start a user-driven, interactive resize of the surface.
+
+ This request must be used in response to some sort of user action
+ like a button press, key press, or touch down event. The passed
+ serial is used to determine the type of interactive resize (touch,
+ pointer, etc).
+
+ The server may ignore resize requests depending on the state of
+ the surface (e.g. fullscreen or maximized).
+
+ If triggered, the client will receive configure events with the
+ "resize" state enum value and the expected sizes. See the "resize"
+ enum value for more details about what is required. The client
+ must also acknowledge configure events using "ack_configure". After
+ the resize is completed, the client will receive another "configure"
+ event without the resize state.
+
+ If triggered, the surface also will lose the focus of the device
+ (wl_pointer, wl_touch, etc) used for the resize. It is up to the
+ compositor to visually indicate that the resize is taking place,
+ such as updating a pointer cursor, during the resize. There is no
+ guarantee that the device focus will return when the resize is
+ completed.
+
+ The edges parameter specifies how the surface should be resized, and
+ is one of the values of the resize_edge enum. Values not matching
+ a variant of the enum will cause the invalid_resize_edge protocol error.
+ The compositor may use this information to update the surface position
+ for example when dragging the top left corner. The compositor may also
+ use this information to adapt its behavior, e.g. choose an appropriate
+ cursor image.
+
+
+
+
+
+
+
+
+ The different state values used on the surface. This is designed for
+ state values like maximized, fullscreen. It is paired with the
+ configure event to ensure that both the client and the compositor
+ setting the state can be synchronized.
+
+ States set in this way are double-buffered, see wl_surface.commit.
+
+
+
+ The surface is maximized. The window geometry specified in the configure
+ event must be obeyed by the client, or the xdg_wm_base.invalid_surface_state
+ error is raised.
+
+ The client should draw without shadow or other
+ decoration outside of the window geometry.
+
+
+
+
+ The surface is fullscreen. The window geometry specified in the
+ configure event is a maximum; the client cannot resize beyond it. For
+ a surface to cover the whole fullscreened area, the geometry
+ dimensions must be obeyed by the client. For more details, see
+ xdg_toplevel.set_fullscreen.
+
+
+
+
+ The surface is being resized. The window geometry specified in the
+ configure event is a maximum; the client cannot resize beyond it.
+ Clients that have aspect ratio or cell sizing configuration can use
+ a smaller size, however.
+
+
+
+
+ Client window decorations should be painted as if the window is
+ active. Do not assume this means that the window actually has
+ keyboard or pointer focus.
+
+
+
+
+ The window is currently in a tiled layout and the left edge is
+ considered to be adjacent to another part of the tiling grid.
+
+ The client should draw without shadow or other decoration outside of
+ the window geometry on the left edge.
+
+
+
+
+ The window is currently in a tiled layout and the right edge is
+ considered to be adjacent to another part of the tiling grid.
+
+ The client should draw without shadow or other decoration outside of
+ the window geometry on the right edge.
+
+
+
+
+ The window is currently in a tiled layout and the top edge is
+ considered to be adjacent to another part of the tiling grid.
+
+ The client should draw without shadow or other decoration outside of
+ the window geometry on the top edge.
+
+
+
+
+ The window is currently in a tiled layout and the bottom edge is
+ considered to be adjacent to another part of the tiling grid.
+
+ The client should draw without shadow or other decoration outside of
+ the window geometry on the bottom edge.
+
+
+
+
+ The surface is currently not ordinarily being repainted; for
+ example because its content is occluded by another window, or its
+ outputs are switched off due to screen locking.
+
+
+
+
+ The left edge of the window is currently constrained, meaning it
+ shouldn't attempt to resize from that edge. It can for example mean
+ it's tiled next to a monitor edge on the constrained side of the
+ window.
+
+
+
+
+ The right edge of the window is currently constrained, meaning it
+ shouldn't attempt to resize from that edge. It can for example mean
+ it's tiled next to a monitor edge on the constrained side of the
+ window.
+
+
+
+
+ The top edge of the window is currently constrained, meaning it
+ shouldn't attempt to resize from that edge. It can for example mean
+ it's tiled next to a monitor edge on the constrained side of the
+ window.
+
+
+
+
+ The bottom edge of the window is currently constrained, meaning it
+ shouldn't attempt to resize from that edge. It can for example mean
+ it's tiled next to a monitor edge on the constrained side of the
+ window.
+
+
+
+
+
+
+ Set a maximum size for the window.
+
+ The client can specify a maximum size so that the compositor does
+ not try to configure the window beyond this size.
+
+ The width and height arguments are in window geometry coordinates.
+ See xdg_surface.set_window_geometry.
+
+ Values set in this way are double-buffered, see wl_surface.commit.
+
+ The compositor can use this information to allow or disallow
+ different states like maximize or fullscreen and draw accurate
+ animations.
+
+ Similarly, a tiling window manager may use this information to
+ place and resize client windows in a more effective way.
+
+ The client should not rely on the compositor to obey the maximum
+ size. The compositor may decide to ignore the values set by the
+ client and request a larger size.
+
+ If never set, or a value of zero in the request, means that the
+ client has no expected maximum size in the given dimension.
+ As a result, a client wishing to reset the maximum size
+ to an unspecified state can use zero for width and height in the
+ request.
+
+ Requesting a maximum size to be smaller than the minimum size of
+ a surface is illegal and will result in an invalid_size error.
+
+ The width and height must be greater than or equal to zero. Using
+ strictly negative values for width or height will result in a
+ invalid_size error.
+
+
+
+
+
+
+
+ Set a minimum size for the window.
+
+ The client can specify a minimum size so that the compositor does
+ not try to configure the window below this size.
+
+ The width and height arguments are in window geometry coordinates.
+ See xdg_surface.set_window_geometry.
+
+ Values set in this way are double-buffered, see wl_surface.commit.
+
+ The compositor can use this information to allow or disallow
+ different states like maximize or fullscreen and draw accurate
+ animations.
+
+ Similarly, a tiling window manager may use this information to
+ place and resize client windows in a more effective way.
+
+ The client should not rely on the compositor to obey the minimum
+ size. The compositor may decide to ignore the values set by the
+ client and request a smaller size.
+
+ If never set, or a value of zero in the request, means that the
+ client has no expected minimum size in the given dimension.
+ As a result, a client wishing to reset the minimum size
+ to an unspecified state can use zero for width and height in the
+ request.
+
+ Requesting a minimum size to be larger than the maximum size of
+ a surface is illegal and will result in an invalid_size error.
+
+ The width and height must be greater than or equal to zero. Using
+ strictly negative values for width and height will result in a
+ invalid_size error.
+
+
+
+
+
+
+
+ Maximize the surface.
+
+ After requesting that the surface should be maximized, the compositor
+ will respond by emitting a configure event. Whether this configure
+ actually sets the window maximized is subject to compositor policies.
+ The client must then update its content, drawing in the configured
+ state. The client must also acknowledge the configure when committing
+ the new content (see ack_configure).
+
+ It is up to the compositor to decide how and where to maximize the
+ surface, for example which output and what region of the screen should
+ be used.
+
+ If the surface was already maximized, the compositor will still emit
+ a configure event with the "maximized" state.
+
+ If the surface is in a fullscreen state, this request has no direct
+ effect. It may alter the state the surface is returned to when
+ unmaximized unless overridden by the compositor.
+
+
+
+
+
+ Unmaximize the surface.
+
+ After requesting that the surface should be unmaximized, the compositor
+ will respond by emitting a configure event. Whether this actually
+ un-maximizes the window is subject to compositor policies.
+ If available and applicable, the compositor will include the window
+ geometry dimensions the window had prior to being maximized in the
+ configure event. The client must then update its content, drawing it in
+ the configured state. The client must also acknowledge the configure
+ when committing the new content (see ack_configure).
+
+ It is up to the compositor to position the surface after it was
+ unmaximized; usually the position the surface had before maximizing, if
+ applicable.
+
+ If the surface was already not maximized, the compositor will still
+ emit a configure event without the "maximized" state.
+
+ If the surface is in a fullscreen state, this request has no direct
+ effect. It may alter the state the surface is returned to when
+ unmaximized unless overridden by the compositor.
+
+
+
+
+
+ Make the surface fullscreen.
+
+ After requesting that the surface should be fullscreened, the
+ compositor will respond by emitting a configure event. Whether the
+ client is actually put into a fullscreen state is subject to compositor
+ policies. The client must also acknowledge the configure when
+ committing the new content (see ack_configure).
+
+ The output passed by the request indicates the client's preference as
+ to which display it should be set fullscreen on. If this value is NULL,
+ it's up to the compositor to choose which display will be used to map
+ this surface.
+
+ If the surface doesn't cover the whole output, the compositor will
+ position the surface in the center of the output and compensate with
+ with border fill covering the rest of the output. The content of the
+ border fill is undefined, but should be assumed to be in some way that
+ attempts to blend into the surrounding area (e.g. solid black).
+
+ If the fullscreened surface is not opaque, the compositor must make
+ sure that other screen content not part of the same surface tree (made
+ up of subsurfaces, popups or similarly coupled surfaces) are not
+ visible below the fullscreened surface.
+
+
+
+
+
+
+ Make the surface no longer fullscreen.
+
+ After requesting that the surface should be unfullscreened, the
+ compositor will respond by emitting a configure event.
+ Whether this actually removes the fullscreen state of the client is
+ subject to compositor policies.
+
+ Making a surface unfullscreen sets states for the surface based on the following:
+ * the state(s) it may have had before becoming fullscreen
+ * any state(s) decided by the compositor
+ * any state(s) requested by the client while the surface was fullscreen
+
+ The compositor may include the previous window geometry dimensions in
+ the configure event, if applicable.
+
+ The client must also acknowledge the configure when committing the new
+ content (see ack_configure).
+
+
+
+
+
+ Request that the compositor minimize your surface. There is no
+ way to know if the surface is currently minimized, nor is there
+ any way to unset minimization on this surface.
+
+ If you are looking to throttle redrawing when minimized, please
+ instead use the wl_surface.frame event for this, as this will
+ also work with live previews on windows in Alt-Tab, Expose or
+ similar compositor features.
+
+
+
+
+
+ This configure event asks the client to resize its toplevel surface or
+ to change its state. The configured state should not be applied
+ immediately. See xdg_surface.configure for details.
+
+ The width and height arguments specify a hint to the window
+ about how its surface should be resized in window geometry
+ coordinates. See set_window_geometry.
+
+ If the width or height arguments are zero, it means the client
+ should decide its own window dimension. This may happen when the
+ compositor needs to configure the state of the surface but doesn't
+ have any information about any previous or expected dimension.
+
+ The states listed in the event specify how the width/height
+ arguments should be interpreted, and possibly how it should be
+ drawn.
+
+ Clients must send an ack_configure in response to this event. See
+ xdg_surface.configure and xdg_surface.ack_configure for details.
+
+
+
+
+
+
+
+
+ The close event is sent by the compositor when the user
+ wants the surface to be closed. This should be equivalent to
+ the user clicking the close button in client-side decorations,
+ if your application has any.
+
+ This is only a request that the user intends to close the
+ window. The client may choose to ignore this request, or show
+ a dialog to ask the user to save their data, etc.
+
+
+
+
+
+
+
+ The configure_bounds event may be sent prior to a xdg_toplevel.configure
+ event to communicate the bounds a window geometry size is recommended
+ to constrain to.
+
+ The passed width and height are in surface coordinate space. If width
+ and height are 0, it means bounds is unknown and equivalent to as if no
+ configure_bounds event was ever sent for this surface.
+
+ The bounds can for example correspond to the size of a monitor excluding
+ any panels or other shell components, so that a surface isn't created in
+ a way that it cannot fit.
+
+ The bounds may change at any point, and in such a case, a new
+ xdg_toplevel.configure_bounds will be sent, followed by
+ xdg_toplevel.configure and xdg_surface.configure.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This event advertises the capabilities supported by the compositor. If
+ a capability isn't supported, clients should hide or disable the UI
+ elements that expose this functionality. For instance, if the
+ compositor doesn't advertise support for minimized toplevels, a button
+ triggering the set_minimized request should not be displayed.
+
+ The compositor will ignore requests it doesn't support. For instance,
+ a compositor which doesn't advertise support for minimized will ignore
+ set_minimized requests.
+
+ Compositors must send this event once before the first
+ xdg_surface.configure event. When the capabilities change, compositors
+ must send this event again and then send an xdg_surface.configure
+ event.
+
+ The configured state should not be applied immediately. See
+ xdg_surface.configure for details.
+
+ The capabilities are sent as an array of 32-bit unsigned integers in
+ native endianness.
+
+
+
+
+
+
+
+ A popup surface is a short-lived, temporary surface. It can be used to
+ implement for example menus, popovers, tooltips and other similar user
+ interface concepts.
+
+ A popup can be made to take an explicit grab. See xdg_popup.grab for
+ details.
+
+ When the popup is dismissed, a popup_done event will be sent out, and at
+ the same time the surface will be unmapped. See the xdg_popup.popup_done
+ event for details.
+
+ Explicitly destroying the xdg_popup object will also dismiss the popup and
+ unmap the surface. Clients that want to dismiss the popup when another
+ surface of their own is clicked should dismiss the popup using the destroy
+ request.
+
+ A newly created xdg_popup will be stacked on top of all previously created
+ xdg_popup surfaces associated with the same xdg_toplevel.
+
+ The parent of an xdg_popup must be mapped (see the xdg_surface
+ description) before the xdg_popup itself.
+
+ The client must call wl_surface.commit on the corresponding wl_surface
+ for the xdg_popup state to take effect.
+
+
+
+
+
+
+
+
+ This destroys the popup. Explicitly destroying the xdg_popup
+ object will also dismiss the popup, and unmap the surface.
+
+ If this xdg_popup is not the "topmost" popup, the
+ xdg_wm_base.not_the_topmost_popup protocol error will be sent.
+
+
+
+
+
+ This request makes the created popup take an explicit grab. An explicit
+ grab will be dismissed when the user dismisses the popup, or when the
+ client destroys the xdg_popup. This can be done by the user clicking
+ outside the surface, using the keyboard, or even locking the screen
+ through closing the lid or a timeout.
+
+ If the compositor denies the grab, the popup will be immediately
+ dismissed.
+
+ This request must be used in response to some sort of user action like a
+ button press, key press, or touch down event. The serial number of the
+ event should be passed as 'serial'.
+
+ The parent of a grabbing popup must either be an xdg_toplevel surface or
+ another xdg_popup with an explicit grab. If the parent is another
+ xdg_popup it means that the popups are nested, with this popup now being
+ the topmost popup.
+
+ Nested popups must be destroyed in the reverse order they were created
+ in, e.g. the only popup you are allowed to destroy at all times is the
+ topmost one.
+
+ When compositors choose to dismiss a popup, they may dismiss every
+ nested grabbing popup as well. When a compositor dismisses popups, it
+ will follow the same dismissing order as required from the client.
+
+ If the topmost grabbing popup is destroyed, the grab will be returned to
+ the parent of the popup, if that parent previously had an explicit grab.
+
+ If the parent is a grabbing popup which has already been dismissed, this
+ popup will be immediately dismissed. If the parent is a popup that did
+ not take an explicit grab, an error will be raised.
+
+ During a popup grab, the client owning the grab will receive pointer
+ and touch events for all their surfaces as normal (similar to an
+ "owner-events" grab in X11 parlance), while the top most grabbing popup
+ will always have keyboard focus.
+
+
+
+
+
+
+
+ This event asks the popup surface to configure itself given the
+ configuration. The configured state should not be applied immediately.
+ See xdg_surface.configure for details.
+
+ The x and y arguments represent the position the popup was placed at
+ given the xdg_positioner rule, relative to the upper left corner of the
+ window geometry of the parent surface.
+
+ For version 2 or older, the configure event for an xdg_popup is only
+ ever sent once for the initial configuration. Starting with version 3,
+ it may be sent again if the popup is setup with an xdg_positioner with
+ set_reactive requested, or in response to xdg_popup.reposition requests.
+
+
+
+
+
+
+
+
+
+ The popup_done event is sent out when a popup is dismissed by the
+ compositor. The client should destroy the xdg_popup object at this
+ point.
+
+
+
+
+
+
+
+ Reposition an already-mapped popup. The popup will be placed given the
+ details in the passed xdg_positioner object, and a
+ xdg_popup.repositioned followed by xdg_popup.configure and
+ xdg_surface.configure will be emitted in response. Any parameters set
+ by the previous positioner will be discarded.
+
+ The passed token will be sent in the corresponding
+ xdg_popup.repositioned event. The new popup position will not take
+ effect until the corresponding configure event is acknowledged by the
+ client. See xdg_popup.repositioned for details. The token itself is
+ opaque, and has no other special meaning.
+
+ If multiple reposition requests are sent, the compositor may skip all
+ but the last one.
+
+ If the popup is repositioned in response to a configure event for its
+ parent, the client should send an xdg_positioner.set_parent_configure
+ and possibly an xdg_positioner.set_parent_size request to allow the
+ compositor to properly constrain the popup.
+
+ If the popup is repositioned together with a parent that is being
+ resized, but not in response to a configure event, the client should
+ send an xdg_positioner.set_parent_size request.
+
+
+
+
+
+
+
+ The repositioned event is sent as part of a popup configuration
+ sequence, together with xdg_popup.configure and lastly
+ xdg_surface.configure to notify the completion of a reposition request.
+
+ The repositioned event is to notify about the completion of a
+ xdg_popup.reposition request. The token argument is the token passed
+ in the xdg_popup.reposition request.
+
+ Immediately after this event is emitted, xdg_popup.configure and
+ xdg_surface.configure will be sent with the updated size and position,
+ as well as a new configure serial.
+
+ The client should optionally update the content of the popup, but must
+ acknowledge the new popup configuration for the new position to take
+ effect. See xdg_surface.ack_configure for details.
+
+
+
+
+
+
diff --git a/src/external/RGFW/deps/wayland/xdg-toplevel-icon-v1.xml b/src/external/RGFW/deps/wayland/xdg-toplevel-icon-v1.xml
new file mode 100644
index 000000000..fc409fef7
--- /dev/null
+++ b/src/external/RGFW/deps/wayland/xdg-toplevel-icon-v1.xml
@@ -0,0 +1,205 @@
+
+
+
+
+ Copyright © 2023-2024 Matthias Klumpp
+ Copyright © 2024 David Edmundson
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+
+
+ This protocol allows clients to set icons for their toplevel surfaces
+ either via the XDG icon stock (using an icon name), or from pixel data.
+
+ A toplevel icon represents the individual toplevel (unlike the application
+ or launcher icon, which represents the application as a whole), and may be
+ shown in window switchers, window overviews and taskbars that list
+ individual windows.
+
+ This document adheres to RFC 2119 when using words like "must",
+ "should", "may", etc.
+
+ Warning! The protocol described in this file is currently in the testing
+ phase. Backward compatible changes may be added together with the
+ corresponding interface version bump. Backward incompatible changes can
+ only be done by creating a new major version of the extension.
+
+
+
+
+ This interface allows clients to create toplevel window icons and set
+ them on toplevel windows to be displayed to the user.
+
+
+
+
+ Destroy the toplevel icon manager.
+ This does not destroy objects created with the manager.
+
+
+
+
+
+ Creates a new icon object. This icon can then be attached to a
+ xdg_toplevel via the 'set_icon' request.
+
+
+
+
+
+
+ This request assigns the icon 'icon' to 'toplevel', or clears the
+ toplevel icon if 'icon' was null.
+ This state is double-buffered and is applied on the next
+ wl_surface.commit of the toplevel.
+
+ After making this call, the xdg_toplevel_icon_v1 provided as 'icon'
+ can be destroyed by the client without 'toplevel' losing its icon.
+ The xdg_toplevel_icon_v1 is immutable from this point, and any
+ future attempts to change it must raise the
+ 'xdg_toplevel_icon_v1.immutable' protocol error.
+
+ The compositor must set the toplevel icon from either the pixel data
+ the icon provides, or by loading a stock icon using the icon name.
+ See the description of 'xdg_toplevel_icon_v1' for details.
+
+ If 'icon' is set to null, the icon of the respective toplevel is reset
+ to its default icon (usually the icon of the application, derived from
+ its desktop-entry file, or a placeholder icon).
+ If this request is passed an icon with no pixel buffers or icon name
+ assigned, the icon must be reset just like if 'icon' was null.
+
+
+
+
+
+
+
+ This event indicates an icon size the compositor prefers to be
+ available if the client has scalable icons and can render to any size.
+
+ When the 'xdg_toplevel_icon_manager_v1' object is created, the
+ compositor may send one or more 'icon_size' events to describe the list
+ of preferred icon sizes. If the compositor has no size preference, it
+ may not send any 'icon_size' event, and it is up to the client to
+ decide a suitable icon size.
+
+ A sequence of 'icon_size' events must be finished with a 'done' event.
+ If the compositor has no size preferences, it must still send the
+ 'done' event, without any preceding 'icon_size' events.
+
+
+
+
+
+
+ This event is sent after all 'icon_size' events have been sent.
+
+
+
+
+
+
+ This interface defines a toplevel icon.
+ An icon can have a name, and multiple buffers.
+ In order to be applied, the icon must have either a name, or at least
+ one buffer assigned. Applying an empty icon (with no buffer or name) to
+ a toplevel should reset its icon to the default icon.
+
+ It is up to compositor policy whether to prefer using a buffer or loading
+ an icon via its name. See 'set_name' and 'add_buffer' for details.
+
+
+
+
+
+
+
+
+
+
+ Destroys the 'xdg_toplevel_icon_v1' object.
+ The icon must still remain set on every toplevel it was assigned to,
+ until the toplevel icon is reset explicitly.
+
+
+
+
+
+ This request assigns an icon name to this icon.
+ Any previously set name is overridden.
+
+ The compositor must resolve 'icon_name' according to the lookup rules
+ described in the XDG icon theme specification[1] using the
+ environment's current icon theme.
+
+ If the compositor does not support icon names or cannot resolve
+ 'icon_name' according to the XDG icon theme specification it must
+ fall back to using pixel buffer data instead.
+
+ If this request is made after the icon has been assigned to a toplevel
+ via 'set_icon', a 'immutable' error must be raised.
+
+ [1]: https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
+
+
+
+
+
+
+ This request adds pixel data supplied as wl_buffer to the icon.
+
+ The client should add pixel data for all icon sizes and scales that
+ it can provide, or which are explicitly requested by the compositor
+ via 'icon_size' events on xdg_toplevel_icon_manager_v1.
+
+ The wl_buffer supplying pixel data as 'buffer' must be backed by wl_shm
+ and must be a square (width and height being equal).
+ If any of these buffer requirements are not fulfilled, a 'invalid_buffer'
+ error must be raised.
+
+ If this icon instance already has a buffer of the same size and scale
+ from a previous 'add_buffer' request, data from the last request
+ overrides the preexisting pixel data.
+
+ The wl_buffer must be kept alive for as long as the xdg_toplevel_icon
+ it is associated with is not destroyed, otherwise a 'no_buffer' error
+ is raised. The buffer contents must not be modified after it was
+ assigned to the icon. As a result, the region of the wl_shm_pool's
+ backing storage used for the wl_buffer must not be modified after this
+ request is sent. The wl_buffer.release event is unused.
+
+ If this request is made after the icon has been assigned to a toplevel
+ via 'set_icon', a 'immutable' error must be raised.
+
+
+
+
+
+
diff --git a/src/external/rlsw.h b/src/external/rlsw.h
index aad99f937..46cba4c74 100644
--- a/src/external/rlsw.h
+++ b/src/external/rlsw.h
@@ -48,7 +48,7 @@
* recommended under specific situations and only if the developers know
* what are they doing; this flag is not defined by default
*
-* rlsw capabilities could be customized just defining some internal
+* rlsw capabilities could be customized defining some internal
* values before library inclusion (default values listed):
*
* #define SW_GL_FRAMEBUFFER_COPY_BGRA true
@@ -644,7 +644,7 @@ SWAPI void swBindTexture(uint32_t id);
#define SW_ARCH_RISCV
#endif
-#if defined(RLSW_USE_SIMD_INTRINSICS)
+#if RLSW_USE_SIMD_INTRINSICS
// Check for SIMD vector instructions
// NOTE: Compiler is responsible to enable required flags for host device,
// supported features are detected at compiler init but varies depending on compiler
@@ -695,7 +695,7 @@ SWAPI void swBindTexture(uint32_t id);
#define SW_HAS_RVV
#include
#endif
-#endif // RLSW_USE_SIMD_INTRINSICS
+#endif
#ifdef __cplusplus
#define SW_CURLY_INIT(name) name
@@ -3446,7 +3446,7 @@ static inline bool sw_is_texture_valid(uint32_t id)
else if (id >= SW_MAX_TEXTURES) valid = false;
else if (RLSW.loadedTextures[id].pixels == NULL) valid = false;
- return true;
+ return valid;
}
static inline bool sw_is_texture_filter_valid(int filter)
diff --git a/src/external/win32_clipboard.h b/src/external/win32_clipboard.h
index 1cb05457d..5c30fbd2e 100644
--- a/src/external/win32_clipboard.h
+++ b/src/external/win32_clipboard.h
@@ -14,7 +14,7 @@ unsigned char *Win32GetClipboardImageData(int *width, int *height, unsigned long
#include
// NOTE: These search for architecture is taken from "windows.h", and it's necessary to avoid including windows.h
-// and still make it compile on msvc, because import indirectly importing "winnt.h" (e.g. ) can cause problems is these are not defined.
+// and still make it compile on msvc, because import indirectly importing "winnt.h" (e.g. ) can cause problems is these are not defined
#if !defined(_X86_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_IX86)
#define _X86_
#if !defined(_CHPE_X86_ARM64_) && defined(_M_HYBRID)
@@ -178,8 +178,10 @@ typedef struct tagRGBQUAD {
#define BI_CMYK 0x000B
#define BI_CMYKRLE8 0x000C
#define BI_CMYKRLE4 0x000D
+#endif
-// Bitmap not compressed and that the color table consists of four DWORD color masks,
+#ifndef BI_ALPHABITFIELDS
+// Bitmap not compressed and that the color table consists of four DWORD color masks,
// that specify the red, green, blue, and alpha components of each pixel
#define BI_ALPHABITFIELDS 0x0006
#endif
@@ -214,7 +216,7 @@ static int GetPixelDataOffset(BITMAPINFOHEADER bih); // Get pixel data offset fr
unsigned char *Win32GetClipboardImageData(int *width, int *height, unsigned long long int *dataSize)
{
unsigned char *bmpData = NULL;
-
+
if (OpenClipboardRetrying(NULL))
{
HGLOBAL clipHandle = (HGLOBAL)GetClipboardData(CF_DIB);
@@ -229,7 +231,7 @@ unsigned char *Win32GetClipboardImageData(int *width, int *height, unsigned long
if (clipDataSize >= sizeof(BITMAPINFOHEADER))
{
int pixelOffset = GetPixelDataOffset(*bmpInfoHeader);
-
+
// Create the bytes for a correct BMP file and copy the data to a pointer
//------------------------------------------------------------------------
BITMAPFILEHEADER bmpFileHeader = { 0 };
@@ -243,10 +245,10 @@ unsigned char *Win32GetClipboardImageData(int *width, int *height, unsigned long
bmpData = (unsigned char *)RL_MALLOC(sizeof(bmpFileHeader) + clipDataSize);
memcpy(bmpData, &bmpFileHeader, sizeof(bmpFileHeader)); // Add BMP file header data
memcpy(bmpData + sizeof(bmpFileHeader), bmpInfoHeader, clipDataSize); // Add BMP info header data
-
+
GlobalUnlock(clipHandle);
CloseClipboard();
-
+
TRACELOG(LOG_INFO, "Clipboad image acquired successfully");
//------------------------------------------------------------------------
}
@@ -257,14 +259,14 @@ unsigned char *Win32GetClipboardImageData(int *width, int *height, unsigned long
CloseClipboard();
}
}
- else
+ else
{
TRACELOG(LOG_WARNING, "Clipboard data failed to be locked");
GlobalUnlock(clipHandle);
CloseClipboard();
}
}
- else
+ else
{
TRACELOG(LOG_WARNING, "Clipboard data is not an image");
CloseClipboard();
@@ -284,7 +286,7 @@ static BOOL OpenClipboardRetrying(HWND hWnd)
{
static const int maxTries = 20;
static const int sleepTimeMS = 60;
-
+
for (int i = 0; i < maxTries; i++)
{
// Might be being hold by another process
@@ -293,7 +295,7 @@ static BOOL OpenClipboardRetrying(HWND hWnd)
Sleep(sleepTimeMS);
}
-
+
return false;
}
@@ -317,7 +319,7 @@ static int GetPixelDataOffset(BITMAPINFOHEADER bih)
// If (bih.biCompression == BI_RGB) no need to be offset more
if (bih.biCompression == BI_BITFIELDS) offset += 3*rgbaSize;
- else if (bih.biCompression == BI_ALPHABITFIELDS) offset += 4*rgbaSize; // Not widely supported, but valid
+ else if (bih.biCompression == BI_ALPHABITFIELDS) offset += 4 * rgbaSize; // Not widely supported, but valid
}
}
diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c
index 74c7b3792..f501fba8a 100644
--- a/src/platforms/rcore_android.c
+++ b/src/platforms/rcore_android.c
@@ -275,12 +275,36 @@ static int android_write(void *cookie, const char *buf, int size);
static fpos_t android_seek(void *cookie, fpos_t offset, int whence);
static int android_close(void *cookie);
-FILE *android_fopen(const char *fileName, const char *mode); // Replacement for fopen() -> Read-only!
+// WARNING: fopen() calls are intercepted via linker flag -Wl,--wrap=fopen: the linker renames
+// the original fopen -> __real_fopen and redirects all call sites to __wrap_fopen
+// The flag MUST be applied at every final link step that needs wrapping;
+// it has no effect when only building a static archive (.a)
+//
+// STATIC library (.a) — wrapping deferred to consumer's final link step:
+// both raylib and consumer fopen calls are wrapped together in one link
+// CMake: handled automatically — the PUBLIC flag propagates as INTERFACE_LINK_OPTIONS
+// to the consumer's final link via target_link_libraries
+// Make: pass -Wl,--wrap=fopen to the linker command producing the final artifact
+// build.zig: pass -Wl,--wrap=fopen to the linker command producing the final artifact
+// custom: pass -Wl,--wrap=fopen to the linker command producing the final artifact
+//
+// SHARED library (.so) — wrapping is self-contained:
+// only fopen calls linked into the .so are wrapped; the consumer's own fopen calls
+// are NOT wrapped unless the consumer also links with -Wl,--wrap=fopen independently
+// CMake: handled automatically — CMakeLists.txt sets target_link_options(raylib PUBLIC
+// -Wl,--wrap=fopen) which applies the flag to the .so link;
+// only raylib internals are wrapped, app code requires a separate flag
+// Make: handled automatically — src/Makefile sets LDFLAGS += -Wl,--wrap=fopen;
+// only raylib internals are wrapped, app code requires a separate flag
+// build.zig: NOT supported — std.Build has no dedicated linker wrap helper, the flag
+// is not correctly applied at the .so link step
+// custom: apply -Wl,--wrap=fopen to the linker command producing the .so
+FILE *__real_fopen(const char *fileName, const char *mode); // Real fopen, provided by the linker (--wrap=fopen)
+FILE *__wrap_fopen(const char *fileName, const char *mode); // Replacement for fopen()
+
FILE *funopen(const void *cookie, int (*readfn)(void *, char *, int), int (*writefn)(void *, const char *, int),
fpos_t (*seekfn)(void *, fpos_t, int), int (*closefn)(void *));
-#define fopen(name, mode) android_fopen(name, mode)
-
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
@@ -713,9 +737,9 @@ const char *GetKeyName(int key)
// Register all input events
void PollInputEvents(void)
{
-#if defined(SUPPORT_GESTURES_SYSTEM)
+#if 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
+ // because ProcessGestureEvent() is called on an event, not every frame
UpdateGestures();
#endif
@@ -1055,7 +1079,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
InitGraphicsDevice();
// Initialize OpenGL context (states and resources)
- // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
+ // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, stored as globals in rlgl
rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
// Setup default viewport
@@ -1065,11 +1089,11 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
// Initialize hi-res timer
InitTimer();
- #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
+ #if SUPPORT_MODULE_RTEXT
// Load default font
// WARNING: External function: Module required: rtext
LoadFontDefault();
- #if defined(SUPPORT_MODULE_RSHAPES)
+ #if SUPPORT_MODULE_RSHAPES
// Set font white rectangle for shapes drawing, so shapes and text can be batched together
// WARNING: rshapes module is required, if not available, default internal white rectangle is used
Rectangle rec = GetFontDefault().recs[95];
@@ -1085,7 +1109,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
}
#endif
#else
- #if defined(SUPPORT_MODULE_RSHAPES)
+ #if SUPPORT_MODULE_RSHAPES
// Set default texture and rectangle to be used for shapes drawing
// NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
@@ -1299,7 +1323,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
}
else if ((keycode == AKEYCODE_BACK) || (keycode == AKEYCODE_MENU))
{
- // Eat BACK_BUTTON and AKEYCODE_MENU, just do nothing... and don't let to be handled by OS!
+ // Eat BACK_BUTTON and AKEYCODE_MENU, do nothing... and don't let to be handled by OS!
return 1;
}
else if ((keycode == AKEYCODE_VOLUME_UP) || (keycode == AKEYCODE_VOLUME_DOWN))
@@ -1347,7 +1371,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
}
}
-#if defined(SUPPORT_GESTURES_SYSTEM)
+#if SUPPORT_GESTURES_SYSTEM
GestureEvent gestureEvent = { 0 };
gestureEvent.pointCount = 0;
@@ -1524,25 +1548,20 @@ static void SetupFramebuffer(int width, int height)
}
}
-// Replacement for fopen()
+// Replacement for fopen(), used as linker wrap entry point (-Wl,--wrap=fopen)
// REF: https://developer.android.com/ndk/reference/group/asset
-FILE *android_fopen(const char *fileName, const char *mode)
+__attribute__((visibility("default"))) FILE *__wrap_fopen(const char *fileName, const char *mode)
{
FILE *file = NULL;
-
+
+ // NOTE: AAsset provides access to read-only asset, write operations use regular fopen
if (mode[0] == 'w')
{
- // NOTE: fopen() is mapped to android_fopen() that only grants read access to
- // assets directory through AAssetManager but it could be required to write data
- // using the standard stdio FILE access functions
- // REF: https://stackoverflow.com/questions/11294487/android-writing-saving-files-from-native-code-only
- #undef fopen
- file = fopen(TextFormat("%s/%s", platform.app->activity->internalDataPath, fileName), mode);
- #define fopen(name, mode) android_fopen(name, mode)
+ file = __real_fopen(TextFormat("%s/%s", platform.app->activity->internalDataPath, fileName), mode);
+ if (file == NULL) file = __real_fopen(fileName, mode);
}
else
{
- // NOTE: AAsset provides access to read-only asset
AAsset *asset = AAssetManager_open(platform.app->activity->assetManager, fileName, AASSET_MODE_UNKNOWN);
if (asset != NULL)
@@ -1552,14 +1571,12 @@ FILE *android_fopen(const char *fileName, const char *mode)
}
else
{
- #undef fopen
- // Just do a regular open if file is not found in the assets
- file = fopen(TextFormat("%s/%s", platform.app->activity->internalDataPath, fileName), mode);
- if (file == NULL) file = fopen(fileName, mode);
- #define fopen(name, mode) android_fopen(name, mode)
+ // Do a regular open if file is not found in the assets
+ file = __real_fopen(TextFormat("%s/%s", platform.app->activity->internalDataPath, fileName), mode);
+ if (file == NULL) file = __real_fopen(fileName, mode);
}
}
-
+
return file;
}
diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c
index af3e6b6c1..1975a7bfe 100644
--- a/src/platforms/rcore_desktop_glfw.c
+++ b/src/platforms/rcore_desktop_glfw.c
@@ -65,7 +65,7 @@
#define GLFW_NATIVE_INCLUDE_NONE // To avoid some symbols re-definition in windows.h
#include "GLFW/glfw3native.h"
- #if defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
+ #if SUPPORT_WINMM_HIGHRES_TIMER && !SUPPORT_BUSY_WAIT_LOOP
// NOTE: Those functions require linking with winmm library
//#pragma warning(disable: 4273)
__declspec(dllimport) unsigned int __stdcall timeEndPeriod(unsigned int uPeriod);
@@ -837,7 +837,7 @@ int GetCurrentMonitor(void)
{
// In case the window is between two monitors, below logic is used
// to try to detect the "current monitor" for that window, note that
- // this is probably an overengineered solution for a very side case
+ // this is probably an overengineered solution for a side case
// trying to match SDL behaviour
int closestDist = 0x7FFFFFFF;
@@ -1043,12 +1043,17 @@ const char *GetClipboardText(void)
return glfwGetClipboardString(platform.handle);
}
+#if SUPPORT_CLIPBOARD_IMAGE && defined(__linux__) && defined(_GLFW_X11)
+ #include
+ #include
+#endif
+
// Get clipboard image
Image GetClipboardImage(void)
{
Image image = { 0 };
-#if defined(SUPPORT_CLIPBOARD_IMAGE)
+#if SUPPORT_CLIPBOARD_IMAGE && SUPPORT_MODULE_RTEXTURES
#if defined(_WIN32)
unsigned long long int dataSize = 0;
void *bmpData = NULL;
@@ -1059,9 +1064,58 @@ Image GetClipboardImage(void)
if (bmpData == NULL) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data.");
else image = LoadImageFromMemory(".bmp", (const unsigned char *)bmpData, (int)dataSize);
+
+#elif defined(__linux__) && defined(_GLFW_X11)
+
+ // REF: https://github.com/ColleagueRiley/Clipboard-Copy-Paste/blob/main/x11.c
+ Display *dpy = XOpenDisplay(NULL);
+ if (!dpy) return image;
+
+ Window root = DefaultRootWindow(dpy);
+ Window win = XCreateSimpleWindow(
+ dpy, // The connection to the X Server
+ root, // The 'Parent' window (usually the desktop/root)
+ 0, 0, // X and Y position on the screen
+ 1, 1, // Width and Height (1x1 pixel)
+ 0, // Border width
+ 0, // Border color
+ 0 // Background color
+ );
+
+ Atom clipboard = XInternAtom(dpy, "CLIPBOARD", False);
+ Atom targetType = XInternAtom(dpy, "image/png", False); // Ask for PNG
+ Atom property = XInternAtom(dpy, "RAYLIB_CLIPBOARD_MANAGER", False);
+
+ // Request the data: "Convert whatever is in CLIPBOARD to image/png and put it in RAYLIB_CLIPBOARD_MANAGER"
+ XConvertSelection(dpy, clipboard, targetType, property, win, CurrentTime);
+
+ // Wait for the SelectionNotify event
+ XEvent ev = { 0 };
+ XNextEvent(dpy, &ev);
+
+ Atom actualType = { 0 };
+ int actualFormat = 0;
+ unsigned long nitems = 0;
+ unsigned long bytesAfter = 0;
+ unsigned char *data = NULL;
+
+ // Read the data from our ghost window's property
+ XGetWindowProperty(dpy, win, property, 0, ~0L, False, AnyPropertyType,
+ &actualType, &actualFormat, &nitems, &bytesAfter, &data);
+
+ if (data != NULL)
+ {
+ image = LoadImageFromMemory(".png", data, (int)nitems);
+ XFree(data);
+ }
+
+ XDestroyWindow(dpy, win);
+ XCloseDisplay(dpy);
#else
TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform");
-#endif
+#endif // defined(_WIN32)
+#else
+ TRACELOG(LOG_WARNING, "Clipboard image: SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly");
#endif // SUPPORT_CLIPBOARD_IMAGE
return image;
@@ -1202,9 +1256,9 @@ const char *GetKeyName(int key)
// Register all input events
void PollInputEvents(void)
{
-#if defined(SUPPORT_GESTURES_SYSTEM)
+#if 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
+ // because ProcessGestureEvent() is called on an event, not every frame
UpdateGestures();
#endif
@@ -1468,7 +1522,7 @@ int InitPlatform(void)
glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE);
#endif
#if defined(_GLFW_WAYLAND) && !defined(_GLFW_X11)
- // GLFW 3.4+ defaults GLFW_SCALE_FRAMEBUFFER to TRUE,
+ // GLFW 3.4+ defaults GLFW_SCALE_FRAMEBUFFER to TRUE,
// causing framebuffer/window size mismatch on Wayland with display scaling
glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE);
#endif
@@ -1514,7 +1568,7 @@ int InitPlatform(void)
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint)
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE);
-#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT)
+#if RLGL_ENABLE_OPENGL_DEBUG_CONTEXT
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context
#endif
}
@@ -1534,7 +1588,7 @@ int InitPlatform(void)
}
// NOTE: GLFW 3.4+ defers initialization of the Joystick subsystem on the first call to any Joystick related functions
- // Forcing this initialization here avoids doing it on PollInputEvents() called by EndDrawing() after first frame has been just drawn
+ // Forcing this initialization here avoids doing it on PollInputEvents() called by EndDrawing() after first frame has been drawn
// The initialization will still happen and possible delays still occur, but before the window is shown, which is a nicer experience
// REF: https://github.com/raysan5/raylib/issues/1554
glfwSetJoystickCallback(NULL);
@@ -1673,12 +1727,12 @@ int InitPlatform(void)
#if !defined(__APPLE__)
if (glfwGetPlatform() == GLFW_PLATFORM_WAYLAND)
{
- // On Wayland, GLFW_SCALE_FRAMEBUFFER handles scaling; read actual framebuffer size
+ // On Wayland, GLFW_SCALE_FRAMEBUFFER handles scaling; read actual framebuffer size
// instead of resizing the window (which would double-scale)
int fbWidth = 0;
int fbHeight = 0;
glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight);
-
+
CORE.Window.render.width = fbWidth;
CORE.Window.render.height = fbHeight;
}
@@ -1697,7 +1751,7 @@ int InitPlatform(void)
// Current active framebuffer size is main framebuffer size
CORE.Window.currentFbo = CORE.Window.render;
- TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully %s",
+ TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully %s",
FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)? "(HighDPI)" : "");
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);
@@ -1782,7 +1836,7 @@ int InitPlatform(void)
}
//----------------------------------------------------------------------------
- // Initialize timming system
+ // Initialize timing system
//----------------------------------------------------------------------------
InitTimer();
//----------------------------------------------------------------------------
@@ -1819,7 +1873,7 @@ void ClosePlatform(void)
glfwDestroyWindow(platform.handle);
glfwTerminate();
-#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
+#if defined(_WIN32) && SUPPORT_WINMM_HIGHRES_TIMER && !SUPPORT_BUSY_WAIT_LOOP
timeEndPeriod(1); // Restore time period
#endif
}
@@ -1881,14 +1935,14 @@ static void FramebufferSizeCallback(GLFWwindow *window, int width, int height)
int winWidth = 0;
int winHeight = 0;
glfwGetWindowSize(platform.handle, &winWidth, &winHeight);
-
+
if ((winWidth != width) || (winHeight != height))
{
CORE.Window.screen.width = winWidth;
CORE.Window.screen.height = winHeight;
float scaleX = (float)width/winWidth;
float scaleY = (float)height/winHeight;
-
+
CORE.Window.screenScale = MatrixScale(scaleX, scaleY, 1.0f);
}
}
@@ -2049,7 +2103,7 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int
CORE.Input.Mouse.currentButtonState[button] = action;
CORE.Input.Touch.currentTouchState[button] = action;
-#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)
+#if SUPPORT_GESTURES_SYSTEM && SUPPORT_MOUSE_GESTURES
// Process mouse events as touches to be able to use mouse-gestures
GestureEvent gestureEvent = { 0 };
@@ -2084,7 +2138,7 @@ static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
CORE.Input.Mouse.currentPosition.y = (float)y;
CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
-#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)
+#if SUPPORT_GESTURES_SYSTEM && SUPPORT_MOUSE_GESTURES
// Process mouse events as touches to be able to use mouse-gestures
GestureEvent gestureEvent = { 0 };
@@ -2124,16 +2178,19 @@ static void CursorEnterCallback(GLFWwindow *window, int enter)
// GLFW3: Joystick connected/disconnected callback
static void JoystickCallback(int jid, int event)
{
- if (event == GLFW_CONNECTED)
+ if (jid < MAX_GAMEPADS)
{
- // WARNING: If glfwGetJoystickName() is longer than MAX_GAMEPAD_NAME_LENGTH,
- // only copy up to (MAX_GAMEPAD_NAME_LENGTH -1) to destination string
- memset(CORE.Input.Gamepad.name[jid], 0, MAX_GAMEPAD_NAME_LENGTH);
- strncpy(CORE.Input.Gamepad.name[jid], glfwGetJoystickName(jid), MAX_GAMEPAD_NAME_LENGTH - 1);
- }
- else if (event == GLFW_DISCONNECTED)
- {
- memset(CORE.Input.Gamepad.name[jid], 0, MAX_GAMEPAD_NAME_LENGTH);
+ if (event == GLFW_CONNECTED)
+ {
+ // WARNING: If glfwGetJoystickName() is longer than MAX_GAMEPAD_NAME_LENGTH,
+ // only copy up to (MAX_GAMEPAD_NAME_LENGTH -1) to destination string
+ memset(CORE.Input.Gamepad.name[jid], 0, MAX_GAMEPAD_NAME_LENGTH);
+ strncpy(CORE.Input.Gamepad.name[jid], glfwGetJoystickName(jid), MAX_GAMEPAD_NAME_LENGTH - 1);
+ }
+ else if (event == GLFW_DISCONNECTED)
+ {
+ memset(CORE.Input.Gamepad.name[jid], 0, MAX_GAMEPAD_NAME_LENGTH);
+ }
}
}
diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c
old mode 100644
new mode 100755
index dbc5e0132..935b3fbb2
--- a/src/platforms/rcore_desktop_rgfw.c
+++ b/src/platforms/rcore_desktop_rgfw.c
@@ -45,8 +45,29 @@
*
**********************************************************************************************/
+#if defined(_WIN32) || defined(_WIN64)
+ #define BI_ALPHABITFIELDS 4
+ #define LoadImage LoadImageA
+
+ // Temporarily rename conflicting symbols
+ #define CloseWindow CloseWindowWin32
+ #define Rectangle RectangleWin32
+ #define ShowCursor ShowCursorWin32
+
+ #define WIN32_LEAN_AND_MEAN
+ #include
+
+ // Restore for raylib/RGFW
+ #undef CloseWindow
+ #undef Rectangle
+ #undef ShowCursor
+ #undef LoadImage
+
+ #include "../external/fix_win32_compatibility.h"
+#endif
+
#if defined(PLATFORM_WEB_RGFW)
-#define RGFW_NO_GL_HEADER
+ #define RGFW_NO_GL_HEADER
#endif
#if defined(GRAPHICS_API_OPENGL_ES2) && !defined(PLATFORM_WEB_RGFW)
@@ -55,15 +76,13 @@
void ShowCursor(void);
void CloseWindow(void);
-
-#if defined(__linux__)
- #define _INPUT_EVENT_CODES_H
-#endif
+double get_time_seconds(void);
#if defined(__unix__) || defined(__linux__)
#define _XTYPEDEF_FONT
#endif
+#define RGFW_OPENGL
#define RGFW_IMPLEMENTATION
#if defined(_WIN32) || defined(_WIN64)
@@ -73,8 +92,6 @@ void CloseWindow(void);
#define ShowCursor __imp_ShowCursor
#define _APISETSTRING_
- #undef MAX_PATH
-
#if defined(__cplusplus)
extern "C" {
#endif
@@ -88,13 +105,51 @@ extern "C" {
#if defined(__APPLE__)
#define Point NSPOINT
#define Size NSSIZE
+
+ #ifdef GetColor
+ #undef GetColor
+ #endif
+ #define GetColor GetColor_osx
+ #ifdef EventType
+ #undef EventType
+ #endif
+ #define EventType EventType_osx
#endif
+#if defined(__APPLE__)
+ // older macs (unsupported?) are missing these
+ #include
+ #ifndef kHIDUsage_Button_5
+ #define kHIDUsage_Button_5 0x05
+ #endif
+ #ifndef kHIDUsage_Button_6
+ #define kHIDUsage_Button_6 0x06
+ #endif
+ #ifndef kHIDUsage_Button_7
+ #define kHIDUsage_Button_7 0x07
+ #endif
+ #ifndef kHIDUsage_Button_8
+ #define kHIDUsage_Button_8 0x08
+ #endif
+ #ifndef kHIDUsage_Button_9
+ #define kHIDUsage_Button_9 0x09
+ #endif
+ #ifndef kHIDUsage_Button_10
+ #define kHIDUsage_Button_10 0x0A
+ #endif
+#endif
+
+// minigamepad used for gamepad support
+#define MG_MAX_GAMEPADS MAX_GAMEPADS // copy raylibs define into minigamepad
+#define MG_IMPLEMENTATION
+#include "../external/RGFW/deps/minigamepad.h"
+
#define RGFW_ALLOC RL_MALLOC
#define RGFW_FREE RL_FREE
#define RGFW_CALLOC RL_CALLOC
+#define RGFW_INT_DEFINED 1 // to avoid issues with minigamepad+RGFW definitions
-#include "../external/RGFW.h"
+#include "../external/RGFW/RGFW.h"
#if defined(_WIN32) || defined(_WIN64)
#undef DrawText
@@ -102,7 +157,9 @@ extern "C" {
#undef CloseWindow
#undef Rectangle
- #undef MAX_PATH
+ #ifdef MAX_PATH
+ #undef MAX_PATH
+ #endif
#define MAX_PATH 1025
#endif
@@ -112,14 +169,15 @@ extern "C" {
#endif
#include
-#include // Required for: strcmp()
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
typedef struct {
+ double startTime;
RGFW_window *window; // Native display device (physical screen connection)
- RGFW_monitor mon;
+ RGFW_monitor *monitor;
+ mg_gamepads minigamepad;
} PlatformData;
//----------------------------------------------------------------------------------
@@ -129,30 +187,125 @@ extern CoreData CORE; // Global CORE state context
static PlatformData platform = { 0 }; // Platform specific
-static bool RGFW_disableCursor = false;
+#if defined(__linux__) // prevent collision of raylibs KEY_ and linux/input.h KEY_
+ #undef KEY_NULL
+ // Alphanumeric keys
+ #undef KEY_APOSTROPHE
+ #undef KEY_COMMA
+ #undef KEY_MINUS
+ #undef KEY_PERIOD
+ #undef KEY_SLASH
+ #undef KEY_ZERO
+ #undef KEY_ONE
+ #undef KEY_TWO
+ #undef KEY_THREE
+ #undef KEY_FOUR
+ #undef KEY_FIVE
+ #undef KEY_SIX
+ #undef KEY_SEVEN
+ #undef KEY_EIGHT
+ #undef KEY_NINE
+ #undef KEY_SEMICOLON
+ #undef KEY_EQUAL
+ #undef KEY_A
+ #undef KEY_B
+ #undef KEY_C
+ #undef KEY_D
+ #undef KEY_E
+ #undef KEY_F
+ #undef KEY_G
+ #undef KEY_H
+ #undef KEY_I
+ #undef KEY_J
+ #undef KEY_K
+ #undef KEY_L
+ #undef KEY_M
+ #undef KEY_N
+ #undef KEY_O
+ #undef KEY_P
+ #undef KEY_Q
+ #undef KEY_R
+ #undef KEY_S
+ #undef KEY_T
+ #undef KEY_U
+ #undef KEY_V
+ #undef KEY_W
+ #undef KEY_X
+ #undef KEY_Y
+ #undef KEY_Z
+ #undef KEY_LEFT_BRACKET
+ #undef KEY_BACKSLASH
+ #undef KEY_RIGHT_BRACKET
+ #undef KEY_GRAVE
+ // Function keys
+ #undef KEY_SPACE
+ #undef KEY_ESCAPE
+ #undef KEY_ENTER
+ #undef KEY_TAB
+ #undef KEY_BACKSPACE
+ #undef KEY_INSERT
+ #undef KEY_DELETE
+ #undef KEY_RIGHT
+ #undef KEY_LEFT
+ #undef KEY_DOWN
+ #undef KEY_UP
+ #undef KEY_PAGE_UP
+ #undef KEY_PAGE_DOWN
+ #undef KEY_HOME
+ #undef KEY_END
+ #undef KEY_CAPS_LOCK
+ #undef KEY_SCROLL_LOCK
+ #undef KEY_NUM_LOCK
+ #undef KEY_PRINT_SCREEN
+ #undef KEY_PAUSE
+ #undef KEY_F1
+ #undef KEY_F2
+ #undef KEY_F3
+ #undef KEY_F4
+ #undef KEY_F5
+ #undef KEY_F6
+ #undef KEY_F7
+ #undef KEY_F8
+ #undef KEY_F9
+ #undef KEY_F10
+ #undef KEY_F11
+ #undef KEY_F12
+ #undef KEY_LEFT_SHIFT
+ #undef KEY_LEFT_CONTROL
+ #undef KEY_LEFT_ALT
+ #undef KEY_LEFT_SUPER
+ #undef KEY_RIGHT_SHIFT
+ #undef KEY_RIGHT_CONTROL
+ #undef KEY_RIGHT_ALT
+ #undef KEY_RIGHT_SUPER
+ #undef KEY_KB_MENU
+ // Keypad keys
+ #undef KEY_KP_0
+ #undef KEY_KP_1
+ #undef KEY_KP_2
+ #undef KEY_KP_3
+ #undef KEY_KP_4
+ #undef KEY_KP_5
+ #undef KEY_KP_6
+ #undef KEY_KP_7
+ #undef KEY_KP_8
+ #undef KEY_KP_9
+ #undef KEY_KP_DECIMAL
+ #undef KEY_KP_DIVIDE
+ #undef KEY_KP_MULTIPLY
+ #undef KEY_KP_SUBTRACT
+ #undef KEY_KP_ADD
+ #undef KEY_KP_ENTER
+ #undef KEY_KP_EQUAL
+#endif
-static const unsigned short keyMappingRGFW[] = {
+static const unsigned short RGFW_keyConvertTable[] = {
[RGFW_keyNULL] = KEY_NULL,
- [RGFW_return] = KEY_ENTER,
[RGFW_apostrophe] = KEY_APOSTROPHE,
[RGFW_comma] = KEY_COMMA,
[RGFW_minus] = KEY_MINUS,
[RGFW_period] = KEY_PERIOD,
[RGFW_slash] = KEY_SLASH,
- [RGFW_escape] = KEY_ESCAPE,
- [RGFW_F1] = KEY_F1,
- [RGFW_F2] = KEY_F2,
- [RGFW_F3] = KEY_F3,
- [RGFW_F4] = KEY_F4,
- [RGFW_F5] = KEY_F5,
- [RGFW_F6] = KEY_F6,
- [RGFW_F7] = KEY_F7,
- [RGFW_F8] = KEY_F8,
- [RGFW_F9] = KEY_F9,
- [RGFW_F10] = KEY_F10,
- [RGFW_F11] = KEY_F11,
- [RGFW_F12] = KEY_F12,
- [RGFW_backtick] = KEY_GRAVE,
[RGFW_0] = KEY_ZERO,
[RGFW_1] = KEY_ONE,
[RGFW_2] = KEY_TWO,
@@ -163,22 +316,8 @@ static const unsigned short keyMappingRGFW[] = {
[RGFW_7] = KEY_SEVEN,
[RGFW_8] = KEY_EIGHT,
[RGFW_9] = KEY_NINE,
+ [RGFW_semicolon] = KEY_SEMICOLON,
[RGFW_equals] = KEY_EQUAL,
- [RGFW_backSpace] = KEY_BACKSPACE,
- [RGFW_tab] = KEY_TAB,
- [RGFW_capsLock] = KEY_CAPS_LOCK,
- [RGFW_shiftL] = KEY_LEFT_SHIFT,
- [RGFW_controlL] = KEY_LEFT_CONTROL,
- [RGFW_altL] = KEY_LEFT_ALT,
- [RGFW_superL] = KEY_LEFT_SUPER,
- #ifndef RGFW_MACOS
- [RGFW_shiftR] = KEY_RIGHT_SHIFT,
- [RGFW_controlR] = KEY_RIGHT_CONTROL,
- [RGFW_altR] = KEY_RIGHT_ALT,
- [RGFW_superR] = KEY_RIGHT_SUPER,
- #endif
- [RGFW_space] = KEY_SPACE,
-
[RGFW_a] = KEY_A,
[RGFW_b] = KEY_B,
[RGFW_c] = KEY_C,
@@ -208,54 +347,104 @@ static const unsigned short keyMappingRGFW[] = {
[RGFW_bracket] = KEY_LEFT_BRACKET,
[RGFW_backSlash] = KEY_BACKSLASH,
[RGFW_closeBracket] = KEY_RIGHT_BRACKET,
- [RGFW_semicolon] = KEY_SEMICOLON,
+ [RGFW_backtick] = KEY_GRAVE,
+ [RGFW_space] = KEY_SPACE,
+ [RGFW_escape] = KEY_ESCAPE,
+ [RGFW_return] = KEY_ENTER,
+ [RGFW_tab] = KEY_TAB,
+ [RGFW_backSpace] = KEY_BACKSPACE,
[RGFW_insert] = KEY_INSERT,
- [RGFW_home] = KEY_HOME,
- [RGFW_pageUp] = KEY_PAGE_UP,
[RGFW_delete] = KEY_DELETE,
- [RGFW_end] = KEY_END,
- [RGFW_pageDown] = KEY_PAGE_DOWN,
[RGFW_right] = KEY_RIGHT,
[RGFW_left] = KEY_LEFT,
[RGFW_down] = KEY_DOWN,
[RGFW_up] = KEY_UP,
- [RGFW_numLock] = KEY_NUM_LOCK,
- [RGFW_KP_Slash] = KEY_KP_DIVIDE,
- [RGFW_multiply] = KEY_KP_MULTIPLY,
- [RGFW_KP_Minus] = KEY_KP_SUBTRACT,
- [RGFW_KP_Return] = KEY_KP_ENTER,
- [RGFW_KP_1] = KEY_KP_1,
- [RGFW_KP_2] = KEY_KP_2,
- [RGFW_KP_3] = KEY_KP_3,
- [RGFW_KP_4] = KEY_KP_4,
- [RGFW_KP_5] = KEY_KP_5,
- [RGFW_KP_6] = KEY_KP_6,
- [RGFW_KP_7] = KEY_KP_7,
- [RGFW_KP_8] = KEY_KP_8,
- [RGFW_KP_9] = KEY_KP_9,
- [RGFW_KP_0] = KEY_KP_0,
- [RGFW_KP_Period] = KEY_KP_DECIMAL,
+ [RGFW_pageUp] = KEY_PAGE_UP,
+ [RGFW_pageDown] = KEY_PAGE_DOWN,
+ [RGFW_home] = KEY_HOME,
+ [RGFW_end] = KEY_END,
+ [RGFW_capsLock] = KEY_CAPS_LOCK,
[RGFW_scrollLock] = KEY_SCROLL_LOCK,
+ [RGFW_numLock] = KEY_NUM_LOCK,
+ [RGFW_printScreen] = KEY_PRINT_SCREEN,
+ [RGFW_pause] = KEY_PAUSE,
+ [RGFW_F1] = KEY_F1,
+ [RGFW_F2] = KEY_F2,
+ [RGFW_F3] = KEY_F3,
+ [RGFW_F4] = KEY_F4,
+ [RGFW_F5] = KEY_F5,
+ [RGFW_F6] = KEY_F6,
+ [RGFW_F7] = KEY_F7,
+ [RGFW_F8] = KEY_F8,
+ [RGFW_F9] = KEY_F9,
+ [RGFW_F10] = KEY_F10,
+ [RGFW_F11] = KEY_F11,
+ [RGFW_F12] = KEY_F12,
+ [RGFW_shiftL] = KEY_LEFT_SHIFT,
+ [RGFW_controlL] = KEY_LEFT_CONTROL,
+ [RGFW_altL] = KEY_LEFT_ALT,
+ [RGFW_superL] = KEY_LEFT_SUPER,
+ // #ifndef RGFW_MACOS
+ [RGFW_shiftR] = KEY_RIGHT_SHIFT,
+ [RGFW_controlR] = KEY_RIGHT_CONTROL,
+ [RGFW_altR] = KEY_RIGHT_ALT,
+ [RGFW_superR] = KEY_RIGHT_SUPER,
+ // #endif
+ [RGFW_menu] = KEY_KB_MENU,
+ [RGFW_kp0] = KEY_KP_0,
+ [RGFW_kp1] = KEY_KP_1,
+ [RGFW_kp2] = KEY_KP_2,
+ [RGFW_kp3] = KEY_KP_3,
+ [RGFW_kp4] = KEY_KP_4,
+ [RGFW_kp5] = KEY_KP_5,
+ [RGFW_kp6] = KEY_KP_6,
+ [RGFW_kp7] = KEY_KP_7,
+ [RGFW_kp8] = KEY_KP_8,
+ [RGFW_kp9] = KEY_KP_9,
+ [RGFW_kpPeriod] = KEY_KP_DECIMAL,
+ [RGFW_kpSlash] = KEY_KP_DIVIDE,
+ [RGFW_kpMultiply] = KEY_KP_MULTIPLY,
+ [RGFW_kpMinus] = KEY_KP_SUBTRACT,
+ [RGFW_kpPlus] = KEY_KP_ADD,
+ [RGFW_kpReturn] = KEY_KP_ENTER,
+ [RGFW_kpEqual] = KEY_KP_EQUAL,
};
-static int RGFW_gpConvTable[18] = {
- [RGFW_gamepadY] = GAMEPAD_BUTTON_RIGHT_FACE_UP,
- [RGFW_gamepadB] = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT,
- [RGFW_gamepadA] = GAMEPAD_BUTTON_RIGHT_FACE_DOWN,
- [RGFW_gamepadX] = GAMEPAD_BUTTON_RIGHT_FACE_LEFT,
- [RGFW_gamepadL1] = GAMEPAD_BUTTON_LEFT_TRIGGER_1,
- [RGFW_gamepadR1] = GAMEPAD_BUTTON_RIGHT_TRIGGER_1,
- [RGFW_gamepadL2] = GAMEPAD_BUTTON_LEFT_TRIGGER_2,
- [RGFW_gamepadR2] = GAMEPAD_BUTTON_RIGHT_TRIGGER_2,
- [RGFW_gamepadSelect] = GAMEPAD_BUTTON_MIDDLE_LEFT,
- [RGFW_gamepadHome] = GAMEPAD_BUTTON_MIDDLE,
- [RGFW_gamepadStart] = GAMEPAD_BUTTON_MIDDLE_RIGHT,
- [RGFW_gamepadUp] = GAMEPAD_BUTTON_LEFT_FACE_UP,
- [RGFW_gamepadRight] = GAMEPAD_BUTTON_LEFT_FACE_RIGHT,
- [RGFW_gamepadDown] = GAMEPAD_BUTTON_LEFT_FACE_DOWN,
- [RGFW_gamepadLeft] = GAMEPAD_BUTTON_LEFT_FACE_LEFT,
- [RGFW_gamepadL3] = GAMEPAD_BUTTON_LEFT_THUMB,
- [RGFW_gamepadR3] = GAMEPAD_BUTTON_RIGHT_THUMB,
+static int mg_buttonConvertTable[] = {
+ [MG_BUTTON_NORTH] = GAMEPAD_BUTTON_RIGHT_FACE_UP,
+ [MG_BUTTON_EAST] = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT,
+ [MG_BUTTON_SOUTH] = GAMEPAD_BUTTON_RIGHT_FACE_DOWN,
+ [MG_BUTTON_WEST] = GAMEPAD_BUTTON_RIGHT_FACE_LEFT,
+ [MG_BUTTON_LEFT_SHOULDER] = GAMEPAD_BUTTON_LEFT_TRIGGER_1,
+ [MG_BUTTON_RIGHT_SHOULDER] = GAMEPAD_BUTTON_RIGHT_TRIGGER_1,
+ [MG_BUTTON_LEFT_TRIGGER] = GAMEPAD_BUTTON_LEFT_TRIGGER_2,
+ [MG_BUTTON_RIGHT_TRIGGER] = GAMEPAD_BUTTON_RIGHT_TRIGGER_2,
+ [MG_BUTTON_BACK] = GAMEPAD_BUTTON_MIDDLE_LEFT,
+ [MG_BUTTON_GUIDE] = GAMEPAD_BUTTON_MIDDLE,
+ [MG_BUTTON_START] = GAMEPAD_BUTTON_MIDDLE_RIGHT,
+ [MG_BUTTON_DPAD_UP] = GAMEPAD_BUTTON_LEFT_FACE_UP,
+ [MG_BUTTON_DPAD_RIGHT] = GAMEPAD_BUTTON_LEFT_FACE_RIGHT,
+ [MG_BUTTON_DPAD_DOWN] = GAMEPAD_BUTTON_LEFT_FACE_DOWN,
+ [MG_BUTTON_DPAD_LEFT] = GAMEPAD_BUTTON_LEFT_FACE_LEFT,
+ [MG_BUTTON_LEFT_STICK] = GAMEPAD_BUTTON_LEFT_THUMB,
+ [MG_BUTTON_RIGHT_STICK] = GAMEPAD_BUTTON_RIGHT_THUMB,
+};
+
+static int mg_axisConvertTable[] = {
+ [MG_AXIS_LEFT_X] = GAMEPAD_AXIS_LEFT_X,
+ [MG_AXIS_LEFT_Y] = GAMEPAD_AXIS_LEFT_Y,
+ [MG_AXIS_RIGHT_X] = GAMEPAD_AXIS_RIGHT_X,
+ [MG_AXIS_RIGHT_Y] = GAMEPAD_AXIS_RIGHT_Y,
+ [MG_AXIS_LEFT_TRIGGER] = GAMEPAD_AXIS_LEFT_TRIGGER,
+ [MG_AXIS_RIGHT_TRIGGER] = GAMEPAD_AXIS_RIGHT_TRIGGER,
+
+ /* unsupported in raylib */
+ [MG_AXIS_HAT_DPAD_LEFT_RIGHT] = -1,
+ [MG_AXIS_HAT_DPAD_LEFT] = -1,
+ [MG_AXIS_HAT_DPAD_RIGHT] = -1,
+ [MG_AXIS_HAT_DPAD_UP_DOWN] = -1,
+ [MG_AXIS_HAT_DPAD_UP] = -1,
+ [MG_AXIS_HAT_DPAD_DOWN] = -1,
};
//----------------------------------------------------------------------------------
@@ -289,61 +478,67 @@ void ToggleFullscreen(void)
{
if (!FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
{
- // Store previous window position (in case we exit fullscreen)
- CORE.Window.previousPosition = CORE.Window.position;
+ FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+ // Store previous window position (in case of exiting fullscreen)
+ Vector2 currentPosition = GetWindowPosition();
+ CORE.Window.previousPosition.x = currentPosition.x;
+ CORE.Window.previousPosition.y = currentPosition.y;
CORE.Window.previousScreen = CORE.Window.screen;
- platform.mon = RGFW_window_getMonitor(platform.window);
- FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
-
- RGFW_monitor_scaleToWindow(platform.mon, platform.window);
+ RGFW_monitor* currentMonitor = RGFW_window_getMonitor(platform.window);
+ RGFW_monitor_scaleToWindow(currentMonitor, platform.window);
RGFW_window_setFullscreen(platform.window, 1);
}
else
{
FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
- if (platform.mon.mode.area.w)
- {
- RGFW_monitor monitor = RGFW_window_getMonitor(platform.window);
- RGFW_monitor_requestMode(monitor, platform.mon.mode, RGFW_monitorScale);
-
- platform.mon.mode.area.w = 0;
- }
-
- // we update the window position right away
+ // Update the window position right away
CORE.Window.position = CORE.Window.previousPosition;
RGFW_window_setFullscreen(platform.window, 0);
- RGFW_window_move(platform.window, RGFW_POINT(CORE.Window.position.x, CORE.Window.position.y));
- RGFW_window_resize(platform.window, RGFW_AREA(CORE.Window.previousScreen.width, CORE.Window.previousScreen.height));
+ RGFW_window_move(platform.window, CORE.Window.position.x, CORE.Window.position.y);
+ RGFW_window_resize(platform.window, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height);
}
// Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS)
// NOTE: V-Sync can be enabled by graphic driver configuration
- if (FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT)) RGFW_window_swapInterval(platform.window, 1);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT)) RGFW_window_swapInterval_OpenGL(platform.window, 1);
}
// Toggle borderless windowed mode
void ToggleBorderlessWindowed(void)
{
- if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) ToggleFullscreen();
-
- if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE))
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
{
- CORE.Window.previousPosition = CORE.Window.position;
+ ToggleFullscreen();
+
+ // it seems like returning here is a more desireable outcome
+ return;
+ }
+
+ if (!FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE))
+ {
+ FLAG_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
+
+ Vector2 currentPosition = GetWindowPosition();
+ CORE.Window.previousPosition.x = (int)currentPosition.x;
+ CORE.Window.previousPosition.y = (int)currentPosition.y;
CORE.Window.previousScreen = CORE.Window.screen;
+ RGFW_monitor *currentMonitor = RGFW_window_getMonitor(platform.window);
RGFW_window_setBorder(platform.window, 0);
-
- RGFW_monitor mon = RGFW_window_getMonitor(platform.window);
- RGFW_window_resize(platform.window, mon.mode.area);
+ RGFW_window_move(platform.window, 0, 0);
+ RGFW_window_resize(platform.window, currentMonitor->mode.w, currentMonitor->mode.h);
}
else
{
+ FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
RGFW_window_setBorder(platform.window, 1);
CORE.Window.position = CORE.Window.previousPosition;
- RGFW_window_resize(platform.window, RGFW_AREA(CORE.Window.previousScreen.width, CORE.Window.previousScreen.height));
+
+ RGFW_window_resize(platform.window, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height);
+ RGFW_window_move(platform.window, CORE.Window.position.x, CORE.Window.position.y);
}
}
@@ -376,7 +571,7 @@ void SetWindowState(unsigned int flags)
if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
{
- RGFW_window_swapInterval(platform.window, 1);
+ RGFW_window_swapInterval_OpenGL(platform.window, 1);
}
if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
{
@@ -384,8 +579,8 @@ void SetWindowState(unsigned int flags)
}
if (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE))
{
- RGFW_window_setMaxSize(platform.window, RGFW_AREA(0, 0));
- RGFW_window_setMinSize(platform.window, RGFW_AREA(0, 0));
+ RGFW_window_setMaxSize(platform.window, 0, 0);
+ RGFW_window_setMinSize(platform.window, 0, 0);
}
if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
{
@@ -406,8 +601,8 @@ void SetWindowState(unsigned int flags)
if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
{
FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
- FLAG_CLEAR(platform.window->_flags, RGFW_windowFocusOnShow);
- RGFW_window_setFlags(platform.window, platform.window->_flags);
+ FLAG_CLEAR(platform.window->internal.flags, RGFW_windowFocusOnShow);
+ RGFW_window_setFlags(platform.window, platform.window->internal.flags);
}
if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
{
@@ -435,7 +630,9 @@ void SetWindowState(unsigned int flags)
}
if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
{
- RGFW_setGLHint(RGFW_glSamples, 4);
+ RGFW_glHints* hints = RGFW_getGlobalHints_OpenGL();
+ hints->samples = 4;
+ RGFW_setGlobalHints_OpenGL(hints);
}
if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
{
@@ -450,7 +647,7 @@ void ClearWindowState(unsigned int flags)
if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
{
- RGFW_window_swapInterval(platform.window, 0);
+ RGFW_window_swapInterval_OpenGL(platform.window, 0);
}
if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
{
@@ -458,8 +655,8 @@ void ClearWindowState(unsigned int flags)
}
if (FLAG_IS_SET(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));
+ RGFW_window_setMaxSize(platform.window, platform.window->w, platform.window->h);
+ RGFW_window_setMinSize(platform.window, platform.window->w, platform.window->h);
}
if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
{
@@ -485,7 +682,7 @@ void ClearWindowState(unsigned int flags)
}
if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
{
- RGFW_window_setFlags(platform.window, platform.window->_flags | RGFW_windowFocusOnShow);
+ RGFW_window_setFlags(platform.window, platform.window->internal.flags | RGFW_windowFocusOnShow);
}
if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
{
@@ -509,7 +706,9 @@ void ClearWindowState(unsigned int flags)
}
if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
{
- RGFW_setGLHint(RGFW_glSamples, 0);
+ RGFW_glHints* hints = RGFW_getGlobalHints_OpenGL();
+ hints->samples = 0;
+ RGFW_setGlobalHints_OpenGL(hints);
}
if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
{
@@ -525,7 +724,7 @@ void SetWindowIcon(Image image)
TRACELOG(LOG_WARNING, "RGFW: Window icon image must be in R8G8B8A8 pixel format");
return;
}
- RGFW_window_setIcon(platform.window, (u8 *)image.data, RGFW_AREA(image.width, image.height), 4);
+ RGFW_window_setIcon(platform.window, (u8 *)image.data, image.width, image.height, 4);
}
// Set icon for window
@@ -533,7 +732,7 @@ void SetWindowIcons(Image *images, int count)
{
if ((images == NULL) || (count <= 0))
{
- RGFW_window_setIcon(platform.window, NULL, RGFW_AREA(0, 0), 0);
+ RGFW_window_setIcon(platform.window, NULL, 0, 0, 0);
}
else
{
@@ -551,8 +750,8 @@ void SetWindowIcons(Image *images, int count)
if ((smallIcon == NULL) || ((images[i].width < smallIcon->width) && (images[i].height > smallIcon->height))) smallIcon = &images[i];
}
- if (smallIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)smallIcon->data, RGFW_AREA(smallIcon->width, smallIcon->height), 4, RGFW_iconWindow);
- if (bigIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)bigIcon->data, RGFW_AREA(bigIcon->width, bigIcon->height), 4, RGFW_iconTaskbar);
+ if (smallIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)smallIcon->data, smallIcon->width, smallIcon->height, 4, RGFW_iconWindow);
+ if (bigIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)bigIcon->data, bigIcon->width, bigIcon->height, 4, RGFW_iconTaskbar);
}
}
@@ -566,7 +765,7 @@ void SetWindowTitle(const char *title)
// Set window position on screen (windowed mode)
void SetWindowPosition(int x, int y)
{
- RGFW_window_move(platform.window, RGFW_POINT(x, y));
+ RGFW_window_move(platform.window, x, y);
}
// Set monitor for the current window
@@ -578,7 +777,7 @@ void SetWindowMonitor(int monitor)
// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
void SetWindowMinSize(int width, int height)
{
- RGFW_window_setMinSize(platform.window, RGFW_AREA(width, height));
+ RGFW_window_setMinSize(platform.window, width, height);
CORE.Window.screenMin.width = width;
CORE.Window.screenMin.height = height;
}
@@ -586,7 +785,7 @@ void SetWindowMinSize(int width, int height)
// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
void SetWindowMaxSize(int width, int height)
{
- RGFW_window_setMaxSize(platform.window, RGFW_AREA(width, height));
+ RGFW_window_setMaxSize(platform.window, width, height);
CORE.Window.screenMax.width = width;
CORE.Window.screenMax.height = height;
}
@@ -594,10 +793,36 @@ void SetWindowMaxSize(int width, int height)
// Set window dimensions
void SetWindowSize(int width, int height)
{
- CORE.Window.screen.width = width;
- CORE.Window.screen.height = height;
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
+ {
+ CORE.Window.screen.width = width;
+ CORE.Window.screen.height = height;
- RGFW_window_resize(platform.window, RGFW_AREA(width, height));
+ Vector2 scaleDpi = GetWindowScaleDPI();
+
+ #if defined(__APPLE__)
+ RGFW_monitor* currentMonitor = RGFW_window_getMonitor(platform.window);
+ CORE.Window.screenScale = MatrixScale(currentMonitor->pixelRatio, currentMonitor->pixelRatio, 1.0f);
+
+ CORE.Window.render.width = CORE.Window.screen.width * currentMonitor->pixelRatio;
+ CORE.Window.render.height = CORE.Window.screen.height * currentMonitor->pixelRatio;
+ CORE.Window.currentFbo.width = CORE.Window.render.width;
+ CORE.Window.currentFbo.height = CORE.Window.render.height;
+ #else
+ SetMouseScale(1.0f/scaleDpi.x, 1.0f/scaleDpi.y);
+ CORE.Window.screenScale = MatrixScale(scaleDpi.x, scaleDpi.y, 1.0f);
+ #endif
+
+ CORE.Window.currentFbo.width = CORE.Window.render.width;
+ CORE.Window.currentFbo.height = CORE.Window.render.height;
+ }
+ else
+ {
+ CORE.Window.screen.width = width;
+ CORE.Window.screen.height = height;
+ }
+
+ RGFW_window_resize(platform.window, CORE.Window.screen.width, CORE.Window.screen.height);
}
// Set window opacity, value opacity is between 0.0 and 1.0
@@ -616,8 +841,11 @@ void SetWindowFocused(void)
void *GetWindowHandle(void)
{
if (platform.window == NULL) return NULL;
-#ifdef RGFW_WASM
- return (void *)platform.window->src.ctx;
+
+#if defined(RGFW_WASM)
+ return (void *)&platform.window->src.ctx;
+#elif defined(RGFW_WAYLAND)
+ return (void *)platform.window->src.surface;
#else
return (void *)platform.window->src.window;
#endif
@@ -626,19 +854,8 @@ void *GetWindowHandle(void)
// Get number of monitors
int GetMonitorCount(void)
{
- #define MAX_MONITORS_SUPPORTED 6
-
- int count = MAX_MONITORS_SUPPORTED;
- RGFW_monitor *mons = RGFW_getMonitors(NULL);
-
- for (int i = 0; i < 6; i++)
- {
- if (!mons[i].x && !mons[i].y && !mons[i].mode.area.w && mons[i].mode.area.h)
- {
- count = i;
- break;
- }
- }
+ size_t count = 0;
+ RGFW_getMonitors(&count);
return count;
}
@@ -646,15 +863,15 @@ int GetMonitorCount(void)
// Get current monitor where window is placed
int GetCurrentMonitor(void)
{
- RGFW_monitor *mons = RGFW_getMonitors(NULL);
- RGFW_monitor mon = { 0 };
+ RGFW_monitor **mons = RGFW_getMonitors(NULL);
+ RGFW_monitor *mon = NULL;
if (platform.window) mon = RGFW_window_getMonitor(platform.window);
else mon = RGFW_getPrimaryMonitor();
for (int i = 0; i < 6; i++)
{
- if ((mons[i].x == mon.x) && (mons[i].y == mon.y)) return i;
+ if ((mons[i]->x == mon->x) && (mons[i]->y == mon->y)) return i;
}
return 0;
@@ -663,81 +880,109 @@ int GetCurrentMonitor(void)
// Get selected monitor position
Vector2 GetMonitorPosition(int monitor)
{
- RGFW_monitor *mons = RGFW_getMonitors(NULL);
+ RGFW_monitor **mons = RGFW_getMonitors(NULL);
- return (Vector2){ (float)mons[monitor].x, (float)mons[monitor].y };
+ return (Vector2){ (float)mons[monitor]->x, (float)mons[monitor]->y };
}
// Get selected monitor width (currently used by monitor)
int GetMonitorWidth(int monitor)
{
- RGFW_monitor *mons = RGFW_getMonitors(NULL);
+ RGFW_monitor **mons = RGFW_getMonitors(NULL);
- return mons[monitor].mode.area.w;
+ return mons[monitor]->mode.w;
}
// Get selected monitor height (currently used by monitor)
int GetMonitorHeight(int monitor)
{
- RGFW_monitor *mons = RGFW_getMonitors(NULL);
+ RGFW_monitor **mons = RGFW_getMonitors(NULL);
- return mons[monitor].mode.area.h;
+ return mons[monitor]->mode.h;
}
// Get selected monitor physical width in millimetres
int GetMonitorPhysicalWidth(int monitor)
{
- RGFW_monitor *mons = RGFW_getMonitors(NULL);
+ RGFW_monitor **mons = RGFW_getMonitors(NULL);
- return mons[monitor].physW;
+ return mons[monitor]->physW;
}
// Get selected monitor physical height in millimetres
int GetMonitorPhysicalHeight(int monitor)
{
- RGFW_monitor *mons = RGFW_getMonitors(NULL);
+ RGFW_monitor **mons = RGFW_getMonitors(NULL);
- return (int)mons[monitor].physH;
+ return (int)mons[monitor]->physH;
}
// Get selected monitor refresh rate
int GetMonitorRefreshRate(int monitor)
{
- RGFW_monitor *mons = RGFW_getMonitors(NULL);
+ RGFW_monitor **mons = RGFW_getMonitors(NULL);
- return (int)mons[monitor].mode.refreshRate;
+ return (int)mons[monitor]->mode.refreshRate;
}
// Get the human-readable, UTF-8 encoded name of the selected monitor
const char *GetMonitorName(int monitor)
{
- RGFW_monitor *mons = RGFW_getMonitors(NULL);
+ RGFW_monitor **mons = RGFW_getMonitors(NULL);
- return mons[monitor].name;
+ return mons[monitor]->name;
}
// Get window position XY on monitor
Vector2 GetWindowPosition(void)
{
- if (platform.window == NULL) return (Vector2){ 0.0f, 0.0f };
- return (Vector2){ (float)platform.window->r.x, (float)platform.window->r.y };
+ if (platform.window == NULL)
+ {
+ return (Vector2){ 0.0f, 0.0f };
+ }
+
+ if (RGFW_window_getPosition(platform.window, &platform.window->x, &platform.window->y)) {
+ return (Vector2){ (float)platform.window->x, (float)platform.window->y };
+ }
+
+ return (Vector2){ 0.0f, 0.0f };
}
// Get window scale DPI factor for current monitor
Vector2 GetWindowScaleDPI(void)
{
- RGFW_monitor monitor = { 0 };
+ RGFW_monitor *monitor = NULL;
if (platform.window) monitor = RGFW_window_getMonitor(platform.window);
else monitor = RGFW_getPrimaryMonitor();
- return (Vector2){ monitor.scaleX, monitor.scaleX };
+ #if defined(__APPLE__)
+ // Apple does < 1.0f scaling, example: 0.66f, 0.5f
+ // it needs to be convert to be consistent
+ return (Vector2){ 1.0f/monitor->scaleX, 1.0f/monitor->scaleX };
+ #else
+ // Linux and Windows do >= 1.0f scaling, example: 1.0f, 1.25f, 2.0f
+ return (Vector2){ monitor->scaleX, monitor->scaleX };
+ #endif
+}
+
+// Get monitor pixel ratio
+// WARNING: Function not used, neither exposed by raylib
+float GetMonitorPixelRatio(void)
+{
+ RGFW_monitor *monitor = NULL;
+
+ if (platform.window) monitor = RGFW_window_getMonitor(platform.window);
+ else monitor = RGFW_getPrimaryMonitor();
+
+ return monitor->pixelRatio;
}
// Set clipboard text content
void SetClipboardText(const char *text)
{
- RGFW_writeClipboard(text, strlen(text));
+ // add 1 for null terminator
+ RGFW_writeClipboard(text, strlen(text)+1);
}
// Get clipboard text content
@@ -747,13 +992,16 @@ const char *GetClipboardText(void)
return RGFW_readClipboard(NULL);
}
-#if defined(SUPPORT_CLIPBOARD_IMAGE)
+#if SUPPORT_CLIPBOARD_IMAGE
#if defined(_WIN32)
#define WIN32_CLIPBOARD_IMPLEMENTATION
#define WINUSER_ALREADY_INCLUDED
#define WINBASE_ALREADY_INCLUDED
#define WINGDI_ALREADY_INCLUDED
#include "../external/win32_clipboard.h"
+#elif defined(__linux__) && defined(DRGFW_X11)
+ #include
+ #include
#endif
#endif
@@ -761,20 +1009,71 @@ const char *GetClipboardText(void)
Image GetClipboardImage(void)
{
Image image = { 0 };
- unsigned long long int dataSize = 0;
- void *fileData = NULL;
-#if defined(SUPPORT_CLIPBOARD_IMAGE)
+#if SUPPORT_CLIPBOARD_IMAGE && SUPPORT_MODULE_RTEXTURES
#if defined(_WIN32)
+
+ unsigned long long int dataSize = 0; // moved into _WIN32 scope until other platforms gain support
+ void *fileData = NULL; // moved into _WIN32 scope until other platforms gain support
+
int width = 0;
int height = 0;
fileData = (void *)Win32GetClipboardImageData(&width, &height, &dataSize);
if (fileData == NULL) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data");
else image = LoadImageFromMemory(".bmp", (const unsigned char *)fileData, dataSize);
+
+#elif defined(__linux__) && defined(DRGFW_X11)
+
+ // REF: https://github.com/ColleagueRiley/Clipboard-Copy-Paste/blob/main/x11.c
+ Display *dpy = XOpenDisplay(NULL);
+ if (!dpy) return image;
+
+ Window root = DefaultRootWindow(dpy);
+ Window win = XCreateSimpleWindow(
+ dpy, // The connection to the X Server
+ root, // The 'Parent' window (usually the desktop/root)
+ 0, 0, // X and Y position on the screen
+ 1, 1, // Width and Height (1x1 pixel)
+ 0, // Border width
+ 0, // Border color
+ 0 // Background color
+ );
+
+ Atom clipboard = XInternAtom(dpy, "CLIPBOARD", False);
+ Atom targetType = XInternAtom(dpy, "image/png", False); // Ask for PNG
+ Atom property = XInternAtom(dpy, "RAYLIB_CLIPBOARD_MANAGER", False);
+
+ // Request the data: "Convert whatever is in CLIPBOARD to image/png and put it in RAYLIB_CLIPBOARD_MANAGER"
+ XConvertSelection(dpy, clipboard, targetType, property, win, CurrentTime);
+
+ // Wait for the SelectionNotify event
+ XEvent ev = { 0 };
+ XNextEvent(dpy, &ev);
+
+ Atom actualType = { 0 };
+ int actualFormat = 0;
+ unsigned long nitems = 0;
+ unsigned long bytesAfter = 0;
+ unsigned char *data = NULL;
+
+ // Read the data from our ghost window's property
+ XGetWindowProperty(dpy, win, property, 0, ~0L, False, AnyPropertyType,
+ &actualType, &actualFormat, &nitems, &bytesAfter, &data);
+
+ if (data != NULL)
+ {
+ image = LoadImageFromMemory(".png", data, (int)nitems);
+ XFree(data);
+ }
+
+ XDestroyWindow(dpy, win);
+ XCloseDisplay(dpy);
#else
TRACELOG(LOG_WARNING, "Clipboard image: PLATFORM_DESKTOP_RGFW doesn't implement GetClipboardImage() for this OS");
-#endif
+#endif // defined(_WIN32)
+#else
+ TRACELOG(LOG_WARNING, "Clipboard image: SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly");
#endif // SUPPORT_CLIPBOARD_IMAGE
return image;
@@ -797,8 +1096,7 @@ void HideCursor(void)
// Enables cursor (unlock cursor)
void EnableCursor(void)
{
- RGFW_disableCursor = false;
- RGFW_window_mouseUnhold(platform.window);
+ RGFW_window_captureMouse(platform.window, false);
// Set cursor position in the middle
SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
@@ -810,8 +1108,7 @@ void EnableCursor(void)
// Disables cursor (lock cursor)
void DisableCursor(void)
{
- RGFW_disableCursor = true;
- RGFW_window_mouseHold(platform.window, RGFW_AREA(0, 0));
+ RGFW_window_captureMouse(platform.window, true);
HideCursor();
CORE.Input.Mouse.cursorLocked = true;
@@ -820,7 +1117,7 @@ void DisableCursor(void)
// Swap back buffer with front buffer (screen drawing)
void SwapScreenBuffer(void)
{
- RGFW_window_swapBuffers(platform.window);
+ RGFW_window_swapBuffers_OpenGL(platform.window);
}
//----------------------------------------------------------------------------------
@@ -830,7 +1127,7 @@ void SwapScreenBuffer(void)
// Get elapsed time measure in seconds since InitTimer()
double GetTime(void)
{
- return RGFW_getTime();
+ return get_time_seconds() - platform.startTime;
}
// Open URL with default system browser (if available)
@@ -865,8 +1162,7 @@ void OpenURL(const char *url)
// Set internal gamepad mappings
int SetGamepadMappings(const char *mappings)
{
- TRACELOG(LOG_WARNING, "SetGamepadMappings() unsupported on target platform");
- return 0;
+ return mg_update_gamepad_mappings(&platform.minigamepad, mappings);
}
// Set gamepad vibration
@@ -878,7 +1174,7 @@ void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float d
// Set mouse position XY
void SetMousePosition(int x, int y)
{
- RGFW_window_moveMouse(platform.window, RGFW_POINT(x, y));
+ RGFW_window_moveMouse(platform.window, x, y);
CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
}
@@ -900,9 +1196,9 @@ const char *GetKeyName(int key)
// Register all input events
void PollInputEvents(void)
{
-#if defined(SUPPORT_GESTURES_SYSTEM)
+#if 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
+ // because ProcessGestureEvent() is called on an event, not every frame
UpdateGestures();
#endif
@@ -917,7 +1213,7 @@ void PollInputEvents(void)
// Register previous mouse position
// Reset last gamepad button/axis registered state
- for (int i = 0; (i < 4) && (i < MAX_GAMEPADS); i++)
+ for (int i = 0; i < MAX_GAMEPADS; i++)
{
// Check if gamepad is available
if (CORE.Input.Gamepad.ready[i])
@@ -955,7 +1251,7 @@ void PollInputEvents(void)
CORE.Window.resizedLastFrame = false;
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
- if (FLAG_IS_SET(platform.window->_flags, RGFW_HOLD_MOUSE))
+ if (RGFW_window_isCaptured(platform.window))
{
CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f };
CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f };
@@ -967,43 +1263,40 @@ void PollInputEvents(void)
if ((CORE.Window.eventWaiting) || (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)))
{
- RGFW_window_eventWait(platform.window, -1); // Wait for input events: keyboard/mouse/window events (callbacks) -> Update keys state
CORE.Time.previous = GetTime();
}
- while (RGFW_window_checkEvent(platform.window))
+ RGFW_event rgfw_event;
+ while (RGFW_window_checkEvent(platform.window, &rgfw_event))
{
- RGFW_event *event = &platform.window->event;
// All input events can be processed after polling
-
- switch (event->type)
+ switch (rgfw_event.type)
{
case RGFW_mouseEnter: CORE.Input.Mouse.cursorOnScreen = true; break;
case RGFW_mouseLeave: CORE.Input.Mouse.cursorOnScreen = false; break;
case RGFW_quit:
- event->type = 0;
- CORE.Window.shouldClose = true;
+ RGFW_window_setShouldClose(platform.window, true);
return;
- case RGFW_DND: // Dropped file
+ case RGFW_dataDrop: // Dropped file
{
- for (int i = 0; i < event->droppedFilesCount; i++)
+ for (int i = 0; i < rgfw_event.drop.count; 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
+ // When a new file is dropped, reserve a fixed number of slots for all possible dropped files
+ // at the moment limiting 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]);
+ strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], rgfw_event.drop.files[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]);
+ strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], rgfw_event.drop.files[i]);
CORE.Window.dropFileCount++;
}
@@ -1014,22 +1307,54 @@ void PollInputEvents(void)
// Window events are also polled (Minimized, maximized, close...)
case RGFW_windowResized:
{
- SetupViewport(platform.window->r.w, platform.window->r.h);
+ #if defined(__APPLE__)
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
+ {
+ RGFW_monitor* currentMonitor = RGFW_window_getMonitor(platform.window);
+ SetupViewport(platform.window->w * currentMonitor->pixelRatio, platform.window->h * currentMonitor->pixelRatio);
+ CORE.Window.screenScale = MatrixScale(currentMonitor->pixelRatio, currentMonitor->pixelRatio, 1.0f);
- // if we are doing automatic DPI scaling, then the "screen" size is divided by the window scale
- if (IsWindowState(FLAG_WINDOW_HIGHDPI))
- {
- CORE.Window.screen.width = (int)(platform.window->r.w/GetWindowScaleDPI().x);
- CORE.Window.screen.height = (int)(platform.window->r.h/GetWindowScaleDPI().y);
- }
- else
- {
- CORE.Window.screen.width = platform.window->r.w;
- CORE.Window.screen.height = platform.window->r.h;
- }
+ CORE.Window.screen.width = platform.window->w;
+ CORE.Window.screen.height = platform.window->h;
+ CORE.Window.render.width = CORE.Window.screen.width * currentMonitor->pixelRatio;
+ CORE.Window.render.height = CORE.Window.screen.height * currentMonitor->pixelRatio;
+ }
+ else
+ {
+ SetupViewport(platform.window->w, platform.window->h);
+ CORE.Window.screen.width = platform.window->w;
+ CORE.Window.screen.height = platform.window->h;
+ CORE.Window.render.width = CORE.Window.screen.width;
+ CORE.Window.render.height = CORE.Window.screen.height;
+ }
- CORE.Window.currentFbo.width = platform.window->r.w;
- CORE.Window.currentFbo.height = platform.window->r.h;
+ CORE.Window.currentFbo.width = CORE.Window.render.width;
+ CORE.Window.currentFbo.height = CORE.Window.render.height;
+ #elif defined(PLATFORM_WEB_RGFW)
+ return;
+ #else
+ SetupViewport(platform.window->w, platform.window->h);
+
+ // Consider content scaling if required
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
+ {
+ Vector2 scaleDpi = GetWindowScaleDPI();
+ CORE.Window.screen.width = (int)(platform.window->w/scaleDpi.x);
+ CORE.Window.screen.height = (int)(platform.window->h/scaleDpi.y);
+ CORE.Window.screenScale = MatrixScale(scaleDpi.x, scaleDpi.y, 1.0f);
+
+ // Mouse scale does not seem to be needed
+ //SetMouseScale(1.0f/scaleDpi.x, 1.0f/scaleDpi.y);
+ }
+ else
+ {
+ CORE.Window.screen.width = platform.window->w;
+ CORE.Window.screen.height = platform.window->h;
+ }
+
+ CORE.Window.currentFbo.width = CORE.Window.screen.width;
+ CORE.Window.currentFbo.height = CORE.Window.screen.height;
+ #endif
CORE.Window.resizedLastFrame = true;
} break;
case RGFW_windowMaximized:
@@ -1049,14 +1374,14 @@ void PollInputEvents(void)
} break;
case RGFW_windowMoved:
{
- CORE.Window.position.x = platform.window->r.x;
- CORE.Window.position.y = platform.window->r.x;
+ CORE.Window.position.x = platform.window->x;
+ CORE.Window.position.y = platform.window->x;
} break;
// Keyboard events
case RGFW_keyPressed:
{
- KeyboardKey key = ConvertScancodeToKey(event->key);
+ KeyboardKey key = ConvertScancodeToKey(rgfw_event.key.value);
if (key != KEY_NULL)
{
// If key was up, add it to the key pressed queue
@@ -1067,36 +1392,37 @@ void PollInputEvents(void)
}
CORE.Input.Keyboard.currentKeyState[key] = 1;
- }
- 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] = event->keyChar;
- CORE.Input.Keyboard.charPressedQueueCount++;
+ if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey]) RGFW_window_setShouldClose(platform.window, true);
}
} break;
case RGFW_keyReleased:
{
- KeyboardKey key = ConvertScancodeToKey(event->key);
+ KeyboardKey key = ConvertScancodeToKey(rgfw_event.key.value);
if (key != KEY_NULL) CORE.Input.Keyboard.currentKeyState[key] = 0;
} break;
+ case RGFW_keyChar:
+ {
+ // NOTE: event.text.text data comes an UTF-8 text sequence but registering 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_event.keyChar.value;
+ CORE.Input.Keyboard.charPressedQueueCount++;
+ }
+ } break;
+
// Check mouse events
+ case RGFW_mouseScroll:
+ {
+ CORE.Input.Mouse.currentWheelMove.x += rgfw_event.scroll.x;
+ CORE.Input.Mouse.currentWheelMove.y += rgfw_event.scroll.y;
+ } break;
case RGFW_mouseButtonPressed:
{
- if ((event->button == RGFW_mouseScrollUp) || (event->button == RGFW_mouseScrollDown))
- {
- CORE.Input.Mouse.currentWheelMove.y = event->scroll;
- break;
- }
- else CORE.Input.Mouse.currentWheelMove.y = 0;
-
- int btn = event->button;
+ int btn = rgfw_event.button.value;
if (btn == RGFW_mouseLeft) btn = 1;
else if (btn == RGFW_mouseRight) btn = 2;
else if (btn == RGFW_mouseMiddle) btn = 3;
@@ -1108,14 +1434,7 @@ void PollInputEvents(void)
} break;
case RGFW_mouseButtonReleased:
{
- if ((event->button == RGFW_mouseScrollUp) || (event->button == RGFW_mouseScrollDown))
- {
- CORE.Input.Mouse.currentWheelMove.y = event->scroll;
- break;
- }
- else CORE.Input.Mouse.currentWheelMove.y = 0;
-
- int btn = event->button;
+ int btn = rgfw_event.button.value;
if (btn == RGFW_mouseLeft) btn = 1;
else if (btn == RGFW_mouseRight) btn = 2;
else if (btn == RGFW_mouseMiddle) btn = 3;
@@ -1127,87 +1446,24 @@ void PollInputEvents(void)
} break;
case RGFW_mousePosChanged:
{
- if (FLAG_IS_SET(platform.window->_flags, RGFW_HOLD_MOUSE))
+ if (RGFW_window_isCaptured(platform.window))
{
- CORE.Input.Mouse.currentPosition.x += (float)event->vector.x;
- CORE.Input.Mouse.currentPosition.y += (float)event->vector.y;
+ CORE.Input.Mouse.currentPosition.x += (float)rgfw_event.mouse.vecX;
+ CORE.Input.Mouse.currentPosition.y += (float)rgfw_event.mouse.vecY;
}
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.Mouse.currentPosition.x = (float)rgfw_event.mouse.x;
+ CORE.Input.Mouse.currentPosition.y = (float)rgfw_event.mouse.y;
}
CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
touchAction = 2;
} break;
- case RGFW_gamepadConnected:
- {
- CORE.Input.Gamepad.ready[platform.window->event.gamepad] = true;
- CORE.Input.Gamepad.axisCount[platform.window->event.gamepad] = platform.window->event.axisesCount;
- CORE.Input.Gamepad.axisState[platform.window->event.gamepad][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
- CORE.Input.Gamepad.axisState[platform.window->event.gamepad][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
-
- strcpy(CORE.Input.Gamepad.name[platform.window->event.gamepad], RGFW_getGamepadName(platform.window, platform.window->event.gamepad));
- } break;
- case RGFW_gamepadDisconnected:
- {
- CORE.Input.Gamepad.ready[platform.window->event.gamepad] = false;
- } break;
- case RGFW_gamepadButtonPressed:
- {
- int button = RGFW_gpConvTable[event->button];
-
- if (button >= 0)
- {
- CORE.Input.Gamepad.currentButtonState[event->gamepad][button] = 1;
- CORE.Input.Gamepad.lastButtonPressed = button;
- }
- } break;
- case RGFW_gamepadButtonReleased:
- {
- int button = RGFW_gpConvTable[event->button];
-
- CORE.Input.Gamepad.currentButtonState[event->gamepad][button] = 0;
- if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
- } break;
- case RGFW_gamepadAxisMove:
- {
- int axis = -1;
- float value = 0;
-
- switch(event->whichAxis)
- {
- case 0:
- {
- CORE.Input.Gamepad.axisState[event->gamepad][GAMEPAD_AXIS_LEFT_X] = event->axis[0].x/100.0f;
- CORE.Input.Gamepad.axisState[event->gamepad][GAMEPAD_AXIS_LEFT_Y] = event->axis[0].y/100.0f;
- } break;
- case 1:
- {
- CORE.Input.Gamepad.axisState[event->gamepad][GAMEPAD_AXIS_RIGHT_X] = event->axis[1].x/100.0f;
- CORE.Input.Gamepad.axisState[event->gamepad][GAMEPAD_AXIS_RIGHT_Y] = event->axis[1].y/100.0f;
- } break;
- case 2: axis = GAMEPAD_AXIS_LEFT_TRIGGER;
- case 3:
- {
- if (axis == -1) 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->gamepad][button] = pressed;
-
- if (pressed) CORE.Input.Gamepad.lastButtonPressed = button;
- else if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
- }
- default: break;
- }
- } break;
default: break;
}
-#if defined(SUPPORT_GESTURES_SYSTEM)
+#if SUPPORT_GESTURES_SYSTEM
if (touchAction > -1)
{
// Process mouse events as touches to be able to use mouse-gestures
@@ -1238,6 +1494,76 @@ void PollInputEvents(void)
#endif
}
//-----------------------------------------------------------------------------
+
+ mg_event gamepad_event;
+ while (mg_gamepads_check_event(&platform.minigamepad, &gamepad_event)) {
+ int gamepadIndex = gamepad_event.gamepad->index;
+ switch (gamepad_event.type) {
+ case MG_EVENT_BUTTON_PRESS:
+ {
+ int button = mg_buttonConvertTable[gamepad_event.button];
+ if (button >= 0)
+ {
+ CORE.Input.Gamepad.currentButtonState[gamepadIndex][button] = 1;
+ CORE.Input.Gamepad.lastButtonPressed = button;
+ }
+ } break;
+ case MG_EVENT_BUTTON_RELEASE:
+ {
+ int button = mg_buttonConvertTable[gamepad_event.button];
+ if (button >= 0)
+ {
+ CORE.Input.Gamepad.currentButtonState[gamepadIndex][button] = 0;
+ if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
+ }
+ } break;
+ case MG_EVENT_AXIS_MOVE:
+ {
+ int axis = mg_axisConvertTable[gamepad_event.axis];
+
+ switch (axis) {
+ case GAMEPAD_AXIS_LEFT_X:
+ case GAMEPAD_AXIS_LEFT_Y:
+ case GAMEPAD_AXIS_RIGHT_X:
+ case GAMEPAD_AXIS_RIGHT_Y:
+ CORE.Input.Gamepad.axisState[gamepadIndex][axis] = platform.minigamepad.gamepads[gamepadIndex].axes[gamepad_event.axis].value;
+ break;
+ case GAMEPAD_AXIS_LEFT_TRIGGER:
+ case GAMEPAD_AXIS_RIGHT_TRIGGER:
+ CORE.Input.Gamepad.axisState[gamepadIndex][axis] = platform.minigamepad.gamepads[gamepadIndex].axes[gamepad_event.axis].value;
+
+ /* trigger button press when axis is all the way */
+ int button = (axis == GAMEPAD_AXIS_LEFT_TRIGGER) ? GAMEPAD_BUTTON_LEFT_TRIGGER_2 : GAMEPAD_BUTTON_RIGHT_TRIGGER_2;
+ int pressed = (platform.minigamepad.gamepads[gamepadIndex].axes[gamepad_event.axis].value >= 1.0f);
+
+ CORE.Input.Gamepad.currentButtonState[gamepadIndex][button] = pressed;
+ if (pressed) CORE.Input.Gamepad.lastButtonPressed = button;
+ else if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
+ break;
+ }
+ } break;
+ case MG_EVENT_GAMEPAD_CONNECT:
+ CORE.Input.Gamepad.ready[gamepadIndex] = true;
+ CORE.Input.Gamepad.axisState[gamepadIndex][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
+ CORE.Input.Gamepad.axisState[gamepadIndex][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
+
+ int axisCount = 0;
+ for (int i = 0; i < MG_AXIS_COUNT; i += 1) {
+ if (platform.minigamepad.gamepads[gamepadIndex].axes[i].supported) {
+ axisCount += 1;
+ } else {
+ break;
+ }
+ }
+ CORE.Input.Gamepad.axisCount[gamepadIndex] = axisCount;
+ strcpy(CORE.Input.Gamepad.name[gamepadIndex], platform.minigamepad.gamepads[gamepadIndex].name);
+ break;
+ case MG_EVENT_GAMEPAD_DISCONNECT:
+ CORE.Input.Gamepad.ready[gamepadIndex] = false;
+ break;
+ default: break;
+ }
+ }
}
//----------------------------------------------------------------------------------
@@ -1270,46 +1596,51 @@ int InitPlatform(void)
// NOTE: Some OpenGL context attributes must be set before window creation
// Check selection OpenGL version
+
+ RGFW_glHints* hints = RGFW_getGlobalHints_OpenGL();
if (rlGetVersion() == RL_OPENGL_21)
{
- RGFW_setGLHint(RGFW_glMajor, 2);
- RGFW_setGLHint(RGFW_glMinor, 1);
+ hints->major = 2;
+ hints->minor = 1;
}
else if (rlGetVersion() == RL_OPENGL_33)
{
- RGFW_setGLHint(RGFW_glMajor, 3);
- RGFW_setGLHint(RGFW_glMinor, 3);
+ hints->major = 3;
+ hints->minor = 3;
}
else if (rlGetVersion() == RL_OPENGL_43)
{
- RGFW_setGLHint(RGFW_glMajor, 4);
- RGFW_setGLHint(RGFW_glMinor, 3);
+ hints->major = 4;
+ hints->minor = 3;
}
- if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) RGFW_setGLHint(RGFW_glSamples, 4);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) hints->samples = 4;
if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) FLAG_SET(flags, RGFW_windowFocusOnShow | RGFW_windowFocus);
- platform.window = RGFW_createWindow((CORE.Window.title != 0)? CORE.Window.title : " ", RGFW_RECT(0, 0, CORE.Window.screen.width, CORE.Window.screen.height), flags);
- platform.mon.mode.area.w = 0;
-
- if (platform.window != NULL)
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
{
- // NOTE: RGFW's exit key is distinct from raylib's exit key (which can
- // be set with SetExitKey()) and defaults to Escape
- platform.window->exitKey = RGFW_keyNULL;
+ #if !defined(__APPLE__)
+ CORE.Window.screen.width = CORE.Window.screen.width * GetWindowScaleDPI().x;
+ CORE.Window.screen.height = CORE.Window.screen.height * GetWindowScaleDPI().y;
+ #endif
}
+ RGFW_setGlobalHints_OpenGL(hints);
+ platform.window = RGFW_createWindow((CORE.Window.title != 0)? CORE.Window.title : " ", 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, flags | RGFW_windowOpenGL);
+ platform.startTime = get_time_seconds();
+
#ifndef PLATFORM_WEB_RGFW
- RGFW_area screenSize = RGFW_getScreenSize();
- CORE.Window.display.width = screenSize.w;
- CORE.Window.display.height = screenSize.h;
+ i32 screenSizeWidth;
+ i32 screenSizeHeight;
+ RGFW_window_getSize(platform.window, &screenSizeWidth, &screenSizeHeight);
+ CORE.Window.display.width = screenSizeWidth;
+ CORE.Window.display.height = screenSizeHeight;
#else
CORE.Window.display.width = CORE.Window.screen.width;
CORE.Window.display.height = CORE.Window.screen.height;
#endif
- if (FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT)) RGFW_window_swapInterval(platform.window, 1);
- RGFW_window_makeCurrent(platform.window);
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT)) RGFW_window_swapInterval_OpenGL(platform.window, 1);
// Check surface and context activation
if (platform.window != NULL)
@@ -1326,16 +1657,42 @@ int InitPlatform(void)
TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device");
return -1;
}
+
+ // NOTE: RGFW's exit key is distinct from raylib's exit key and
+ // must be set to NULL to not interfere
+ RGFW_window_setExitKey(platform.window, RGFW_keyNULL);
+ RGFW_window_makeCurrentWindow_OpenGL(platform.window);
+
//----------------------------------------------------------------------------
- // If everything work as expected, we can continue
- CORE.Window.position.x = platform.window->r.x;
- CORE.Window.position.y = platform.window->r.y;
+ // If everything work as expected, continue
+ CORE.Window.position.x = platform.window->x;
+ CORE.Window.position.y = platform.window->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;
+ // adjust scale if using highdpi
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) {
+ Vector2 scaleDpi = GetWindowScaleDPI();
+
+ #if defined(__APPLE__)
+ RGFW_monitor* currentMonitor = RGFW_window_getMonitor(platform.window);
+ CORE.Window.screenScale = MatrixScale(currentMonitor->pixelRatio, currentMonitor->pixelRatio, 1.0f);
+
+ CORE.Window.render.width = CORE.Window.screen.width * currentMonitor->pixelRatio;
+ CORE.Window.render.height = CORE.Window.screen.height * currentMonitor->pixelRatio;
+ CORE.Window.currentFbo.width = CORE.Window.render.width;
+ CORE.Window.currentFbo.height = CORE.Window.render.height;
+ #else
+ SetMouseScale(1.0f/scaleDpi.x, 1.0f/scaleDpi.y);
+ CORE.Window.screenScale = MatrixScale(scaleDpi.x, scaleDpi.y, 1.0f);
+ CORE.Window.screen.width /= scaleDpi.x;
+ CORE.Window.screen.height /= scaleDpi.y;
+ #endif
+ }
+
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);
@@ -1345,7 +1702,7 @@ int InitPlatform(void)
// Load OpenGL extensions
// NOTE: GL procedures address loader is required to load extensions
//----------------------------------------------------------------------------
- rlLoadExtensions((void *)RGFW_getProcAddress);
+ rlLoadExtensions((void *)RGFW_getProcAddress_OpenGL);
//----------------------------------------------------------------------------
// Initialize timing system
@@ -1355,11 +1712,17 @@ int InitPlatform(void)
// Initialize storage system
//----------------------------------------------------------------------------
+ #if defined(__APPLE__)
+ // mac defaults to the users home folder for some reason
+ // this is done to help them read relative paths to the binary
+ ChangeDirectory(GetApplicationDirectory());
+ #endif
+
CORE.Storage.basePath = GetWorkingDirectory();
//----------------------------------------------------------------------------
#if defined(RGFW_WAYLAND)
- if (RGFW_useWaylandBool) TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Wayland): Initialized successfully");
+ if (RGFW_usingWayland()) TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Wayland): Initialized successfully");
else TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11 (fallback)): Initialized successfully");
#elif defined(RGFW_X11)
#if defined(__APPLE__)
@@ -1375,20 +1738,62 @@ int InitPlatform(void)
TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - MacOS): Initialized successfully");
#endif
+ mg_gamepads_init(&platform.minigamepad);
+
return 0;
}
// Close platform
void ClosePlatform(void)
{
+ mg_gamepads_free(&platform.minigamepad);
RGFW_window_close(platform.window);
}
// Keycode mapping
static KeyboardKey ConvertScancodeToKey(u32 keycode)
{
- if (keycode > sizeof(keyMappingRGFW)/sizeof(unsigned short)) return KEY_NULL;
+ if (keycode > sizeof(RGFW_keyConvertTable)/sizeof(unsigned short)) return KEY_NULL;
- return (KeyboardKey)keyMappingRGFW[keycode];
+ return (KeyboardKey)RGFW_keyConvertTable[keycode];
}
+// Helper functions for Time
+double get_time_seconds(void)
+{
+ double currentTime = 0.0;
+
+ #if defined(_WIN32)
+ static LARGE_INTEGER freq = { 0 };
+ static int freq_init = 0;
+ LARGE_INTEGER counter;
+ if (!freq_init) {
+ QueryPerformanceFrequency(&freq);
+ freq_init = 1;
+ }
+ QueryPerformanceCounter(&counter);
+ currentTime = (double)counter.QuadPart / (double)freq.QuadPart;
+ #elif defined(__EMSCRIPTEN__)
+ currentTime = emscripten_get_now() / 1000.0;
+ #elif defined(__APPLE__)
+ static mach_timebase_info_data_t tb;
+ static int tb_initialized = 0;
+
+ if (!tb_initialized) {
+ mach_timebase_info(&tb);
+ tb_initialized = 1;
+ }
+ uint64_t ticks = mach_absolute_time();
+
+ currentTime = (double)ticks * (double)tb.numer / (double)tb.denom / 1e9;
+ #elif defined(__linux__)
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ currentTime = (double)ts.tv_sec + (double)ts.tv_nsec / 1e9;
+ #else
+ // fallback to cstd
+ currentTime = (double)clock() / (double)CLOCKS_PER_SEC;
+ #endif
+
+ return currentTime;
+}
diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c
index 789457d2f..9dbd0b873 100644
--- a/src/platforms/rcore_desktop_sdl.c
+++ b/src/platforms/rcore_desktop_sdl.c
@@ -49,7 +49,7 @@
#define USING_SDL3_PROJECT
#endif
#ifndef SDL_ENABLE_OLD_NAMES
- #define SDL_ENABLE_OLD_NAMES // Just in case on SDL3, some in-between compatibily is needed
+ #define SDL_ENABLE_OLD_NAMES // In case on SDL3, some in-between compatibily is needed
#endif
// SDL base library (window/rendered, input, timing... functionality)
#ifdef USING_SDL3_PROJECT
@@ -250,10 +250,10 @@ static const int CursorsLUT[] = {
//SDL_SYSTEM_CURSOR_WAITARROW, // No equivalent implemented on MouseCursor enum on raylib.h
};
-// SDL3 Migration Layer made to avoid 'ifdefs' inside functions when we can
+// SDL3 migration layer made to avoid 'ifdefs' inside functions
#if defined(USING_VERSION_SDL3)
-// SDL3 Migration:
+// SDL3 migration:
// SDL_WINDOW_FULLSCREEN_DESKTOP has been removed,
// SDL_GetWindowFullscreenMode() can be called
// to see whether an exclusive fullscreen mode will be used
@@ -265,10 +265,10 @@ static const int CursorsLUT[] = {
#define SDL_ENABLE true
// SDL3 Migration: SDL_INIT_TIMER - no longer needed before calling SDL_AddTimer()
-#define SDL_INIT_TIMER 0x0 // It's a flag, so no problem in setting it to zero if we use in a bitor (|)
+#define SDL_INIT_TIMER 0x0 // It's a flag, so no problem in setting it to zero to be used in a bitor (|)
// SDL3 Migration: The SDL_WINDOW_SHOWN flag has been removed. Windows are shown by default and can be created hidden by using the SDL_WINDOW_HIDDEN flag
-#define SDL_WINDOW_SHOWN 0x0 // It's a flag, so no problem in setting it to zero if we use in a bitor (|)
+#define SDL_WINDOW_SHOWN 0x0 // It's a flag, so no problem in setting it to zero to be used in a bitor (|)
// SDL3 Migration: Renamed
// IMPORTANT: Might need to call SDL_CleanupEvent somewhere see :https://github.com/libsdl-org/SDL/issues/3540#issuecomment-1793449852
@@ -414,16 +414,16 @@ int SDL_GetNumTouchFingers(SDL_TouchID touchID)
#else // SDL2 fallback
-// Since SDL2 doesn't have this function we leave a stub
+// Since SDL2 doesn't have this function, leaving a stub
// SDL_GetClipboardData function is available since SDL 3.1.3. (e.g. SDL3)
void *SDL_GetClipboardData(const char *mime_type, size_t *size)
{
TRACELOG(LOG_WARNING, "SDL: Getting clipboard data that is not text not available in SDL2");
- // We could possibly implement it ourselves in this case for some easier platforms
+ // TODO: Implement getting clipboard data
+
return NULL;
}
-
#endif // USING_VERSION_SDL3
//----------------------------------------------------------------------------------
@@ -574,8 +574,6 @@ void SetWindowState(unsigned int flags)
}
if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
{
- // NOTE: To be able to implement this part it seems that we should
- // do it ourselves, via 'windows.h', 'X11/Xlib.h' or even 'Cocoa.h'
TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_SDL");
}
if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
@@ -857,8 +855,8 @@ void SetWindowMonitor(int monitor)
// ending up positioned partly outside the target display
// NOTE 2: The workaround for that is, previously to moving the window,
// setting the window size to the target display size, so they match
- // NOTE 3: It wasn't done here because we can't assume changing the window size automatically
- // is acceptable behavior by the user
+ // NOTE 3: It wasn't done here because it can not bee assumed that changing
+ // the window size automatically is acceptable behavior by the user
SDL_SetWindowPosition(platform.window, usableBounds.x, usableBounds.y);
CORE.Window.position.x = usableBounds.x;
CORE.Window.position.y = usableBounds.y;
@@ -1163,7 +1161,22 @@ Image GetClipboardImage(void)
{
Image image = { 0 };
-#if defined(SUPPORT_CLIPBOARD_IMAGE)
+#if SUPPORT_CLIPBOARD_IMAGE
+#if !SUPPORT_MODULE_RTEXTURES
+ TRACELOG(LOG_WARNING, "Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly");
+ return image;
+#endif
+
+// It's nice to have support Bitmap on Linux as well, but not as necessary as Windows
+#if !SUPPORT_FILEFORMAT_BMP && defined(_WIN32)
+ TRACELOG(LOG_WARNING, "WARNING: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_FILEFORMAT_BMP, specially on Windows");
+ return image;
+#endif
+
+// From what I've tested applications on Wayland saves images on clipboard as PNG
+#if (!SUPPORT_FILEFORMAT_PNG || !SUPPORT_FILEFORMAT_JPG) && !defined(_WIN32)
+ TRACELOG(LOG_WARNING, "WARNING: Getting image from the clipboard might not work without SUPPORT_FILEFORMAT_PNG or SUPPORT_FILEFORMAT_JPG");
+#endif
// Let's hope compiler put these arrays in static memory
const char *imageFormats[] = {
"image/bmp",
@@ -1198,7 +1211,7 @@ Image GetClipboardImage(void)
}
if (!IsImageValid(image)) TRACELOG(LOG_WARNING, "Clipboard: Couldn't get clipboard data. ERROR: %s", SDL_GetError());
-#endif
+#endif // SUPPORT_CLIPBOARD_IMAGE
return image;
}
@@ -1247,7 +1260,7 @@ void DisableCursor(void)
void SwapScreenBuffer(void)
{
#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
- // NOTE: We use a preprocessor condition here because rlCopyFramebuffer() is only declared for software rendering
+ // NOTE: Using a preprocessor condition here because rlCopyFramebuffer() is only declared for software rendering
SDL_Surface *surface = SDL_GetWindowSurface(platform.window);
rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, surface->pixels);
SDL_UpdateWindowSurface(platform.window);
@@ -1350,9 +1363,9 @@ const char *GetKeyName(int key)
// Register all input events
void PollInputEvents(void)
{
-#if defined(SUPPORT_GESTURES_SYSTEM)
+#if 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
+ // because ProcessGestureEvent() is called on an event, not every frame
UpdateGestures();
#endif
@@ -1428,8 +1441,8 @@ void PollInputEvents(void)
{
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
+ // When a new file is dropped, reserve a fixed number of slots for all possible dropped files
+ // at the moment 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 *));
@@ -1469,7 +1482,7 @@ void PollInputEvents(void)
#ifndef USING_VERSION_SDL3
// The SDL_WINDOWEVENT_* events have been moved to top level events, and SDL_WINDOWEVENT has been removed
- // In general, handling this change just means checking for the individual events instead of first checking for SDL_WINDOWEVENT
+ // In general, handling this change means checking for the individual events instead of first checking for SDL_WINDOWEVENT
// and then checking for window events; Events >= SDL_EVENT_WINDOW_FIRST and <= SDL_EVENT_WINDOW_LAST can be compared
// to see whether it's a window event
case SDL_WINDOWEVENT:
@@ -1483,7 +1496,8 @@ void PollInputEvents(void)
const int width = event.window.data1;
const int height = event.window.data2;
SetupViewport(width, height);
- // if we are doing automatic DPI scaling, then the "screen" size is divided by the window scale
+
+ // Consider content scaling if required
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
{
CORE.Window.screen.width = (int)(width/GetWindowScaleDPI().x);
@@ -1608,7 +1622,7 @@ void PollInputEvents(void)
case SDL_TEXTINPUT:
{
- // NOTE: event.text.text data comes an UTF-8 text sequence but we register codepoints (int)
+ // NOTE: event.text.text data comes an UTF-8 text sequence but register codepoints (int)
// Check if there is space available in the queue
if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
@@ -1871,7 +1885,7 @@ void PollInputEvents(void)
{
if (platform.gamepadId[i] == event.jaxis.which)
{
- // SDL axis value range is -32768 to 32767, we normalize it to raylib's -1.0 to 1.0f range
+ // SDL axis value range is -32768 to 32767, normalizing it to raylib's -1.0 to 1.0f range
float value = event.jaxis.value/(float)32767;
CORE.Input.Gamepad.axisState[i][axis] = value;
@@ -1892,7 +1906,7 @@ void PollInputEvents(void)
default: break;
}
-#if defined(SUPPORT_GESTURES_SYSTEM)
+#if SUPPORT_GESTURES_SYSTEM
if (touchAction > -1)
{
// Process mouse events as touches to be able to use mouse-gestures
@@ -1989,7 +2003,7 @@ int InitPlatform(void)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
- #if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT)
+ #if RLGL_ENABLE_OPENGL_DEBUG_CONTEXT
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); // Enable OpenGL Debug Context
#endif
}
@@ -2018,7 +2032,7 @@ int InitPlatform(void)
platform.window = SDL_CreateWindow(CORE.Window.title, CORE.Window.screen.width, CORE.Window.screen.height, flags);
- // NOTE: SDL3 no longer enables text input by default,
+ // NOTE: SDL3 no longer enables text input by default,
// it is needed to be enabled manually to keep GetCharPressed() working
// REF: https://github.com/libsdl-org/SDL/commit/72fc6f86e5d605a3787222bc7dc18c5379047f4a
const char *enableOSK = SDL_GetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD);
@@ -2113,7 +2127,7 @@ int InitPlatform(void)
// NOTE: No need to call InitTimer(), let SDL manage it internally
CORE.Time.previous = GetTime(); // Get time as double
- #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
+ #if defined(_WIN32) && SUPPORT_WINMM_HIGHRES_TIMER && !SUPPORT_BUSY_WAIT_LOOP
SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "1"); // SDL equivalent of timeBeginPeriod() and timeEndPeriod()
#endif
//----------------------------------------------------------------------------
diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c
index 8cb9645d2..a56a7f090 100644
--- a/src/platforms/rcore_desktop_win32.c
+++ b/src/platforms/rcore_desktop_win32.c
@@ -995,7 +995,7 @@ void SetWindowMaxSize(int width, int height)
CORE.Window.screenMax.width = width;
CORE.Window.screenMax.height = height;
-
+
SetWindowSize(platform.appScreenWidth, platform.appScreenHeight);
}
@@ -1655,7 +1655,7 @@ int InitPlatform(void)
TRACELOG(LOG_INFO, " > GLSL: %s", "NOT SUPPORTED");
}
- // Initialize timming system
+ // Initialize timing system
//----------------------------------------------------------------------------
LARGE_INTEGER time = { 0 };
QueryPerformanceCounter(&time);
@@ -1771,7 +1771,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara
SIZE maxWindowSize = CalcWindowSize(96, maxClientSize, style);
SIZE minClientSize = { CORE.Window.screenMin.width, CORE.Window.screenMin.height };
SIZE minWindowSize = CalcWindowSize(96, minClientSize, style);
-
+
LPMINMAXINFO lpmmi = (LPMINMAXINFO) lparam;
lpmmi->ptMaxSize.x = maxWindowSize.cx;
lpmmi->ptMaxSize.y = maxWindowSize.cy;
@@ -1865,7 +1865,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara
case WM_SIZE:
{
// WARNING: Don't trust the docs, they say this message can not be obtained if not calling DefWindowProc()
- // in response to WM_WINDOWPOSCHANGED but looks like when a window is created,
+ // in response to WM_WINDOWPOSCHANGED but looks like when a window is created,
// this message can be obtained without getting WM_WINDOWPOSCHANGED
HandleWindowResize(hwnd, &platform.appScreenWidth, &platform.appScreenHeight);
} break;
@@ -1880,7 +1880,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara
SIZE *inoutSize = (SIZE *)lparam;
UINT newDpi = (UINT)wparam; // TODO: WARNING: Converting from WPARAM = UINT_PTR
- // For the following flag changes, a window resize event should be posted,
+ // For the following flag changes, a window resize event should be posted,
// TODO: Should it be done after dpi changes?
if (CORE.Window.flags & FLAG_WINDOW_MINIMIZED) return TRUE;
if (CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) return TRUE;
@@ -2188,7 +2188,7 @@ static unsigned SanitizeFlags(int mode, unsigned flags)
// the state continues to change
//
// This design takes care of many odd corner cases. For example, in case of restoring
-// a window that was previously maximized AND minimized and those two flags need to be removed,
+// a window that was previously maximized AND minimized and those two flags need to be removed,
// ShowWindow with SW_RESTORE twice need to bee actually calleed. Another example is
// wheen having a maximized window, if the undecorated flag is modified then the window style
// needs to be updated, but updating the style would mean the window size would change
@@ -2198,7 +2198,7 @@ static unsigned SanitizeFlags(int mode, unsigned flags)
// retry loop that continues until either the desired state is reached or the state stops changing
static void UpdateFlags(HWND hwnd, unsigned desiredFlags, int width, int height)
{
- // Flags that just apply immediately without needing any operations
+ // Flags that apply immediately without needing any operations
CORE.Window.flags |= (desiredFlags & FLAG_MASK_NO_UPDATE);
int vsync = (desiredFlags & FLAG_VSYNC_HINT)? 1 : 0;
diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c
index 85db9b4e1..a32934aa9 100644
--- a/src/platforms/rcore_drm.c
+++ b/src/platforms/rcore_drm.c
@@ -58,7 +58,7 @@
#include // Linux: Joystick support library
// WARNING: Both 'linux/input.h' and 'raylib.h' define KEY_F12
-// To avoid conflict with the capturing code in rcore.c we undefine the macro KEY_F12,
+// To avoid conflict with the capturing code in rcore.c, undefine the macro KEY_F12,
// so the enum KEY_F12 from raylib is used
#undef KEY_F12
@@ -80,7 +80,7 @@
#include // Required for: EBUSY, EAGAIN
#define MAX_DRM_CACHED_BUFFERS 3
-#endif // SUPPORT_DRM_CACHE
+#endif
#ifndef EGL_OPENGL_ES3_BIT
#define EGL_OPENGL_ES3_BIT 0x40
@@ -97,7 +97,7 @@
#define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events
-// Actually biggest key is KEY_CNT but we only really map the keys up to KEY_ALS_TOGGLE
+// Actually biggest key is KEY_CNT but only mapping keys up to KEY_ALS_TOGGLE
#define KEYMAP_SIZE KEY_ALS_TOGGLE
//----------------------------------------------------------------------------------
@@ -160,7 +160,7 @@ static FramebufferCache fbCache[MAX_DRM_CACHED_BUFFERS] = { 0 };
static volatile int fbCacheCount = 0;
static volatile bool pendingFlip = false;
static bool crtcSet = false;
-#endif // SUPPORT_DRM_CACHE
+#endif
//----------------------------------------------------------------------------------
// Global Variables Definition
@@ -183,14 +183,14 @@ static const int evkeyToUnicodeLUT[] = {
0, 27, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 8, 0, 113, 119, 101, 114,
116, 121, 117, 105, 111, 112, 0, 0, 13, 0, 97, 115, 100, 102, 103, 104, 106, 107, 108, 59,
39, 96, 0, 92, 122, 120, 99, 118, 98, 110, 109, 44, 46, 47, 0, 0, 0, 32
- // LUT currently incomplete, just mapped the most essential keys
+ // LUT currently incomplete, only mapped the most essential keys
};
// This is the map used to map any keycode returned from linux to a raylib code from 'raylib.h'
// NOTE: Use short here to save a little memory
static const short linuxToRaylibMap[KEYMAP_SIZE] = {
- // We don't map those with designated initialization, because we would getting
- // into loads of naming conflicts
+ // Don't map with designated initialization,
+ // it will geenrate many naming conflicts
0, 256, 49, 50, 51, 52, 53, 54,
55, 56, 57, 48, 45, 61, 259, 258,
81, 87, 69, 82, 84, 89, 85, 73,
@@ -253,7 +253,7 @@ static const short linuxToRaylibMap[KEYMAP_SIZE] = {
int InitPlatform(void); // Initialize platform (graphics, inputs and more)
void ClosePlatform(void); // Close platform
-#if defined(SUPPORT_SSH_KEYBOARD_RPI)
+#if SUPPORT_SSH_KEYBOARD_RPI
static void InitKeyboard(void); // Initialize raw keyboard system
static void RestoreKeyboard(void); // Restore keyboard system
static void ProcessKeyboard(void); // Process keyboard events
@@ -764,9 +764,9 @@ void SwapScreenBuffer()
// Attempt page flip
// NOTE: rmModePageFlip() schedules a buffer-flip for the next vblank and then notifies us about it
// It takes a CRTC-id, fb-id and an arbitrary data-pointer and then schedules the page-flip
- // This is fully asynchronous and when the page-flip happens, the DRM-fd will become readable and we can call drmHandleEvent()
- // This will read all vblank/page-flip events and call our modeset_page_flip_event() callback with the data-pointer that we passed to drmModePageFlip()
- // We simply call modeset_draw_dev() then so the next frame is rendered... returns immediately
+ // This is fully asynchronous and when the page-flip happens, the DRM-fd will become readable and drmHandleEvent() can be called
+ // This will read all vblank/page-flip events and call our modeset_page_flip_event() callback with the data-pointer passed to drmModePageFlip()
+ // Simply call modeset_draw_dev() then so the next frame is rendered... returns immediately
if (drmModePageFlip(platform.fd, platform.crtc->crtc_id, fbId, DRM_MODE_PAGE_FLIP_EVENT, platform.prevBO))
{
if (errno == EBUSY) errCnt[3]++; // Display busy - skip flip
@@ -1065,9 +1065,9 @@ const char *GetKeyName(int key)
// Register all input events
void PollInputEvents(void)
{
-#if defined(SUPPORT_GESTURES_SYSTEM)
+#if 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
+ // because ProcessGestureEvent() is called on an event, not every frame
UpdateGestures();
#endif
@@ -1088,8 +1088,8 @@ void PollInputEvents(void)
PollKeyboardEvents();
-#if defined(SUPPORT_SSH_KEYBOARD_RPI)
- // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here
+#if SUPPORT_SSH_KEYBOARD_RPI
+ // NOTE: Keyboard reading could be done using input_event(s) or read from stdin, both methods are used here
// stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console
if (!platform.eventKeyboardMode) ProcessKeyboard();
#endif
@@ -1225,7 +1225,7 @@ int InitPlatform(void)
if (((con->connection == DRM_MODE_CONNECTED) || (con->connection == DRM_MODE_UNKNOWNCONNECTION)) && (con->count_modes > 0))
{
#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
- // For hardware rendering, we need an encoder_id
+ // For hardware rendering, an encoder_id is needed
if (con->encoder_id)
{
TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i connected with encoder", i);
@@ -1234,7 +1234,7 @@ int InitPlatform(void)
}
else TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i connected but no encoder", i);
#else
- // For software rendering, we can accept even without encoder_id
+ // For software rendering, accept even without encoder_id
TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i suitable for software rendering", i);
platform.connector = con;
break;
@@ -1428,7 +1428,7 @@ int InitPlatform(void)
if ((eglClientExtensions != NULL) && (strstr(eglClientExtensions, "EGL_EXT_platform_base") != NULL))
{
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
-
+
if (eglGetPlatformDisplayEXT != NULL) platform.device = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, platform.gbmDevice, NULL);
}
@@ -1534,7 +1534,7 @@ int InitPlatform(void)
return -1;
}
- // At this point we need to manage render size vs screen size
+ // At this point, manage render size vs screen size
// NOTE: This function use and modify global module variables:
// -> CORE.Window.screen.width/CORE.Window.screen.height
// -> CORE.Window.render.width/CORE.Window.render.height
@@ -1572,7 +1572,7 @@ int InitPlatform(void)
// NOTE: GL procedures address loader is required to load extensions
rlLoadExtensions(eglGetProcAddress);
#else
- // At this point we need to manage render size vs screen size
+ // At this point, manage render size vs screen size
// NOTE: This function use and modify global module variables:
// -> CORE.Window.screen.width/CORE.Window.screen.height
// -> CORE.Window.render.width/CORE.Window.render.height
@@ -1596,7 +1596,7 @@ int InitPlatform(void)
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) MinimizeWindow();
- // If graphic device is no properly initialized, we end program
+ // If graphic device is no properly initialized, end program
if (!CORE.Window.ready)
{
TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device");
@@ -1613,7 +1613,7 @@ int InitPlatform(void)
//----------------------------------------------------------------------------
// Initialize timing system
//----------------------------------------------------------------------------
- // NOTE: timming system must be initialized before the input events system
+ // NOTE: timing system must be initialized before the input events system
InitTimer();
//----------------------------------------------------------------------------
@@ -1621,7 +1621,7 @@ int InitPlatform(void)
//----------------------------------------------------------------------------
InitEvdevInput(); // Evdev inputs initialization
-#if defined(SUPPORT_SSH_KEYBOARD_RPI)
+#if SUPPORT_SSH_KEYBOARD_RPI
InitKeyboard(); // Keyboard init (stdin)
#endif
//----------------------------------------------------------------------------
@@ -1741,11 +1741,11 @@ void ClosePlatform(void)
}
}
-#if defined(SUPPORT_SSH_KEYBOARD_RPI)
+#if SUPPORT_SSH_KEYBOARD_RPI
// Initialize Keyboard system (using standard input)
static void InitKeyboard(void)
{
- // NOTE: We read directly from Standard Input (stdin) - STDIN_FILENO file descriptor,
+ // NOTE: Read directly from Standard Input (stdin) - STDIN_FILENO file descriptor,
// Reading directly from stdin will give chars already key-mapped by kernel to ASCII or UNICODE
// Save terminal keyboard settings
@@ -1900,7 +1900,7 @@ static void ProcessKeyboard(void)
}
}
}
-#endif // SUPPORT_SSH_KEYBOARD_RPI
+#endif // SUPPORT_SSH_KEYBOARD_RPI
// Initialize user input from evdev(/dev/input/event)
// this means mouse, keyboard or gamepad devices
@@ -1978,8 +1978,8 @@ static void ConfigureEvdevDevice(char *device)
return;
}
- // At this point we have a connection to the device, but we don't yet know what the device is
- // It could be many things, even as simple as a power button...
+ // At this point, a connection to the device has been stablished, but still left to know what the device is,
+ // it could be many things, even as simple as a power button...
//-------------------------------------------------------------------------------------------------------
// Identify the device
@@ -1990,8 +1990,8 @@ static void ConfigureEvdevDevice(char *device)
} absinfo[ABS_CNT] = { 0 };
// These flags aren't really a one of
- // Some devices could have properties we assosciate with keyboards as well as properties
- // we assosciate with mice
+ // Some devices could have properties associated with keyboards
+ // as well as properties associated with mice
bool isKeyboard = false;
bool isMouse = false;
bool isTouch = false;
@@ -2027,11 +2027,10 @@ static void ConfigureEvdevDevice(char *device)
TEST_BIT(keyBits, BTN_TOOL_FINGER) ||
TEST_BIT(keyBits, BTN_TOUCH))) isTouch = true;
- // Absolute mice should really only exist with VMWare, but it shouldn't
- // matter if we support them
+ // Absolute mice should really only exist with VMWare
else if (hasAbsXY && TEST_BIT(keyBits, BTN_MOUSE)) isMouse = true;
- // If any of the common joystick axes are present, we assume it's a gamepad
+ // If any of the common joystick axes are present, assume it's a gamepad
else
{
for (int axis = (hasAbsXY? ABS_Z : ABS_X); axis < ABS_PRESSURE; axis++)
@@ -2056,7 +2055,7 @@ static void ConfigureEvdevDevice(char *device)
{
ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits);
- // If it has any of the gamepad or touch features we tested so far, it's not a mouse
+ // If it has any of the gamepad or touch features tested so far, it's not a mouse
if (!isTouch &&
!isGamepad &&
TEST_BIT(relBits, REL_X) &&
@@ -2067,12 +2066,12 @@ static void ConfigureEvdevDevice(char *device)
if (TEST_BIT(evBits, EV_KEY))
{
// The first 32 keys as defined in input-event-codes.h are pretty much
- // exclusive to keyboards, so we can test them using a mask
+ // exclusive to keyboards, so they can be tested using a mask
// Leave out the first bit to not test KEY_RESERVED
const unsigned long mask = 0xFFFFFFFE;
if ((keyBits[0] & mask) == mask) isKeyboard = true;
- // If we find any of the common gamepad buttons we assume it's a gamepad
+ // If any of the common gamepad buttons is found, assume it's a gamepad
else
{
for (int button = BTN_JOYSTICK; button < BTN_DIGI; ++button)
@@ -2149,7 +2148,7 @@ static void ConfigureEvdevDevice(char *device)
if (absAxisCount > 0)
{
// TODO: Review GamepadAxis enum matching
- // So gamepad axes (as in the actual linux joydev.c) are just simply enumerated
+ // So gamepad axes (as in the actual linux joydev.c) are simply enumerated
// and (at least for some input drivers like xpat) it's convention to use
// ABS_X, ABX_Y for one joystick ABS_RX, ABS_RY for the other and the Z axes for the shoulder buttons
// If these are now enumerated, it results to LJOY_X, LJOY_Y, LEFT_SHOULDERB, RJOY_X, ...
@@ -2196,8 +2195,8 @@ static void PollKeyboardEvents(void)
// Check if the event is a key event
if (event.type != EV_KEY) continue;
-#if defined(SUPPORT_SSH_KEYBOARD_RPI)
- // If the event was a key, we know a working keyboard is connected, so disable the SSH keyboard
+#if SUPPORT_SSH_KEYBOARD_RPI
+ // If the event was a key, assume a working keyboard is connected, so disable the SSH keyboard
platform.eventKeyboardMode = true;
#endif
// Keyboard keys appear for codes 1 to 255, ignore everthing else
@@ -2206,7 +2205,7 @@ static void PollKeyboardEvents(void)
// Lookup the scancode in the keymap to get a keycode
keycode = linuxToRaylibMap[event.code];
- // Make sure we got a valid keycode
+ // Make sure a valid keycode is obtained
if ((keycode > 0) && (keycode < MAX_KEYBOARD_KEYS))
{
// WARNING: https://www.kernel.org/doc/Documentation/input/input.txt
@@ -2380,8 +2379,8 @@ static void PollMouseEvents(void)
{
platform.touchPosition[platform.touchSlot].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width;
- // If this slot is active, it's a move. If not, we are just updating the buffer for when it becomes active.
- // Only set to MOVE if we haven't already detected a DOWN or UP event this frame
+ // If this slot is active, it's a move; If not, update the buffer for when it becomes active
+ // Only set to MOVE if a DOWN or UP event has not been detected this frame
if (platform.touchActive[platform.touchSlot] && touchAction == -1) touchAction = 2; // TOUCH_ACTION_MOVE
}
}
@@ -2392,8 +2391,8 @@ static void PollMouseEvents(void)
{
platform.touchPosition[platform.touchSlot].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height;
- // If this slot is active, it's a move. If not, we are just updating the buffer for when it becomes active.
- // Only set to MOVE if we haven't already detected a DOWN or UP event this frame
+ // If this slot is active, it's a move; If not, update the buffer for when it becomes active
+ // Only set to MOVE if a DOWN or UP event have not been detected this frame
if (platform.touchActive[platform.touchSlot] && touchAction == -1) touchAction = 2; // TOUCH_ACTION_MOVE
}
}
@@ -2418,7 +2417,7 @@ static void PollMouseEvents(void)
platform.touchPosition[platform.touchSlot].y = -1;
platform.touchId[platform.touchSlot] = -1;
- // Force UP action if we haven't already set a DOWN action
+ // Force UP action if DOWN action has not already been set
// (DOWN takes priority over UP if both happen in one frame, though rare)
if (touchAction != 1) touchAction = 0; // TOUCH_ACTION_UP
}
@@ -2552,7 +2551,7 @@ static void PollMouseEvents(void)
CORE.Input.Touch.pointId[i] = -1;
}
-#if defined(SUPPORT_GESTURES_SYSTEM)
+#if SUPPORT_GESTURES_SYSTEM
if (touchAction > -1)
{
GestureEvent gestureEvent = { 0 };
@@ -2665,7 +2664,7 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt
// NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified
static void SetupFramebuffer(int width, int height)
{
- // Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var)
+ // Calculate CORE.Window.render.width and CORE.Window.render.height, using the display size (input params) and the desired screen size (global var)
if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height))
{
TRACELOG(LOG_WARNING, "DISPLAY: Downscaling required: Screen size (%ix%i) is bigger than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
@@ -2693,8 +2692,8 @@ static void SetupFramebuffer(int width, int height)
float scaleRatio = (float)CORE.Window.render.width/(float)CORE.Window.screen.width;
CORE.Window.screenScale = MatrixScale(scaleRatio, scaleRatio, 1.0f);
- // NOTE: We render to full display resolution!
- // We just need to calculate above parameters for downscale matrix and offsets
+ // NOTE: Rendering to full display resolution,
+ // calculate above parameters for downscale matrix and offsets
CORE.Window.render.width = CORE.Window.display.width;
CORE.Window.render.height = CORE.Window.display.height;
diff --git a/src/platforms/rcore_memory.c b/src/platforms/rcore_memory.c
index 1d69f5ed3..322841439 100644
--- a/src/platforms/rcore_memory.c
+++ b/src/platforms/rcore_memory.c
@@ -55,7 +55,7 @@
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
-// Platform-specific required data for timming (Win32)
+// Platform-specific required data for timing (Win32)
#if defined(_WIN32)
typedef struct _LARGE_INTEGER { int64_t QuadPart; } LARGE_INTEGER;
__declspec(dllimport) int __stdcall QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount);
@@ -435,9 +435,9 @@ const char *GetKeyName(int key)
// Register all input events
void PollInputEvents(void)
{
-#if defined(SUPPORT_GESTURES_SYSTEM)
+#if 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
+ // because ProcessGestureEvent() is called on an event, not every frame
UpdateGestures();
#endif
diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c
index 87cd2e21e..40a1922e7 100644
--- a/src/platforms/rcore_template.c
+++ b/src/platforms/rcore_template.c
@@ -341,7 +341,7 @@ void SwapScreenBuffer(void)
double GetTime(void)
{
double time = 0.0;
-
+
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;
@@ -405,9 +405,9 @@ const char *GetKeyName(int key)
// Register all input events
void PollInputEvents(void)
{
-#if defined(SUPPORT_GESTURES_SYSTEM)
+#if 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
+ // because ProcessGestureEvent() is called on an event, not every frame
UpdateGestures();
#endif
@@ -459,7 +459,7 @@ int InitPlatform(void)
if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
{
// TODO: Enable MSAA
-
+
TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
}
@@ -489,7 +489,7 @@ int InitPlatform(void)
}
//----------------------------------------------------------------------------
- // If everything work as expected, we can continue
+ // If everything worked as expected, continue
CORE.Window.render.width = CORE.Window.screen.width;
CORE.Window.render.height = CORE.Window.screen.height;
CORE.Window.currentFbo.width = CORE.Window.render.width;
diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c
index 5430cf5a9..ae847d8c9 100644
--- a/src/platforms/rcore_web.c
+++ b/src/platforms/rcore_web.c
@@ -801,34 +801,103 @@ void SetClipboardText(const char *text)
else EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, text);
}
+// Async EM_JS to be able to await clickboard read asynchronous function
+EM_ASYNC_JS(void, RequestClipboardData, (void), {
+ if (navigator.clipboard && window.isSecureContext)
+ {
+ let items = await navigator.clipboard.read();
+ for (const item of items)
+ {
+ // Check if this item contains plain text or image
+ if (item.types.includes("text/plain"))
+ {
+ const blob = await item.getType("text/plain");
+ const text = await blob.text();
+ window._lastClipboardString = text;
+ }
+ else if (item.types.find(t => t.startsWith("image/")))
+ {
+ const blob = await item.getType(item.types.find(t => t.startsWith("image/")));
+ const bitmap = await createImageBitmap(blob);
+
+ const canvas = document.createElement('canvas');
+ canvas.width = bitmap.width;
+ canvas.height = bitmap.height;
+ const ctx = canvas.getContext('2d');
+ ctx.drawImage(bitmap, 0, 0);
+
+ const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
+
+ // Store image and data for the Fetch function
+ window._lastImgWidth = canvas.width;
+ window._lastImgHeight = canvas.height;
+ window._lastImgData = imgData;
+ }
+ }
+ }
+ else console.warn("Clipboard read() requires HTTPS/Localhost");
+});
+
+// Returns the string created by RequestClipboardData from JS memory to Emscripten C memory
+EM_JS(char *, GetLastPastedText, (void), {
+ var str = window._lastClipboardString || "";
+ var length = lengthBytesUTF8(str) + 1;
+ if (length > 1)
+ {
+ var ptr = _malloc(length);
+ stringToUTF8(str, ptr, length);
+ return ptr;
+ }
+ return 0;
+});
+
+// Returns the image created by RequestClipboardData from JS memory to Emscripten C memory
+EM_JS(unsigned char *, GetLastPastedImage, (int *width, int *height), {
+ if (window._lastImgData)
+ {
+ const data = window._lastImgData;
+ if (data.length > 0)
+ {
+ const ptr = _malloc(data.length);
+ HEAPU8.set(data, ptr);
+
+ // Set the width and height via the pointers passed from C
+ // HEAP32 handles the 4-byte integers
+ if (width) setValue(width, window._lastImgWidth, 'i32');
+ if (height) setValue(height, window._lastImgHeight, 'i32');
+
+ // Clear the JS buffer so there is no need to fetch the same image twice
+ window._lastImgData = null;
+
+ return ptr;
+ }
+ }
+
+ return 0;
+});
+
// Get clipboard text content
// NOTE: returned string is allocated and freed by GLFW
const char *GetClipboardText(void)
{
-/*
- // Accessing clipboard data from browser is tricky due to security reasons
- // The method to use is navigator.clipboard.readText() but this is an asynchronous method
- // that will return at some moment after the function is called with the required data
- emscripten_run_script_string("navigator.clipboard.readText() \
- .then(text => { document.getElementById('clipboard').innerText = text; console.log('Pasted content: ', text); }) \
- .catch(err => { console.error('Failed to read clipboard contents: ', err); });"
- );
-
- // The main issue is getting that data, one approach could be using ASYNCIFY and wait
- // for the data but it requires adding Asyncify emscripten library on compilation
-
- // Another approach could be just copy the data in a HTML text field and try to retrieve it
- // later on if available... and clean it for future accesses
-*/
- return NULL;
+ RequestClipboardData();
+ return GetLastPastedText();
}
// Get clipboard image
Image GetClipboardImage(void)
{
Image image = { 0 };
-
- TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform");
+ int w = 0, h = 0;
+ RequestClipboardData();
+ unsigned char* data = GetLastPastedImage(&w, &h);
+ if (data != NULL) {
+ image.data = data;
+ image.width = w;
+ image.height = h;
+ image.mipmaps = 1;
+ image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
+ }
return image;
}
@@ -1007,9 +1076,9 @@ const char *GetKeyName(int key)
// Register all input events
void PollInputEvents(void)
{
-#if defined(SUPPORT_GESTURES_SYSTEM)
+#if 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
+ // because ProcessGestureEvent() is called on an event, not every frame
UpdateGestures();
#endif
@@ -1210,7 +1279,7 @@ int InitPlatform(void)
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint)
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE);
-#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT)
+#if RLGL_ENABLE_OPENGL_DEBUG_CONTEXT
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context
#endif
}
@@ -1255,7 +1324,7 @@ int InitPlatform(void)
// Remember center for switchinging 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, it's not possible to
+ // If screen width/height equal to the display, it's not possible to
// calculate the window position for toggling full-screened/windowed
CORE.Window.position.x = CORE.Window.display.width/4;
CORE.Window.position.y = CORE.Window.display.height/4;
@@ -1475,15 +1544,15 @@ static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float s
// GLFW3: Called on windows minimized/restored
static void WindowIconifyCallback(GLFWwindow *window, int iconified)
{
- if (iconified) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // The window was iconified
- else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // The window was restored
+ if (iconified) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // The window was iconified
+ else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // The window was restored
}
// GLFW3: Called on windows get/lose focus
static void WindowFocusCallback(GLFWwindow *window, int focused)
{
- if (focused) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // The window was focused
- else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // The window lost focus
+ if (focused) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // The window was focused
+ else FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // The window lost focus
}
// GLFW3: Called on file-drop over the window
@@ -1564,7 +1633,7 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int
CORE.Input.Mouse.currentButtonState[button] = action;
CORE.Input.Touch.currentTouchState[button] = action;
-#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)
+#if SUPPORT_GESTURES_SYSTEM && SUPPORT_MOUSE_GESTURES
// Process mouse events as touches to be able to use mouse-gestures
GestureEvent gestureEvent = { 0 };
@@ -1605,7 +1674,7 @@ static void MouseMoveCallback(GLFWwindow *window, double x, double y)
CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
}
-#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)
+#if SUPPORT_GESTURES_SYSTEM && SUPPORT_MOUSE_GESTURES
// Process mouse events as touches to be able to use mouse-gestures
GestureEvent gestureEvent = { 0 };
@@ -1748,7 +1817,7 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent
CORE.Input.Mouse.currentPosition.y = CORE.Input.Touch.position[0].y;
}
-#if defined(SUPPORT_GESTURES_SYSTEM)
+#if SUPPORT_GESTURES_SYSTEM
GestureEvent gestureEvent = { 0 };
gestureEvent.pointCount = CORE.Input.Touch.pointCount;
diff --git a/src/platforms/rcore_web_emscripten.c b/src/platforms/rcore_web_emscripten.c
index 13e685939..df3234217 100644
--- a/src/platforms/rcore_web_emscripten.c
+++ b/src/platforms/rcore_web_emscripten.c
@@ -779,36 +779,103 @@ void SetClipboardText(const char *text)
else EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, text);
}
+// Async EM_JS to be able to await clickboard read asynchronous function
+EM_ASYNC_JS(void, RequestClipboardData, (void), {
+ if (navigator.clipboard && window.isSecureContext)
+ {
+ let items = await navigator.clipboard.read();
+ for (const item of items)
+ {
+ // Check if this item contains plain text or image
+ if (item.types.includes("text/plain"))
+ {
+ const blob = await item.getType("text/plain");
+ const text = await blob.text();
+ window._lastClipboardString = text;
+ }
+ else if (item.types.find(t => t.startsWith("image/")))
+ {
+ const blob = await item.getType(item.types.find(t => t.startsWith("image/")));
+ const bitmap = await createImageBitmap(blob);
+
+ const canvas = document.createElement('canvas');
+ canvas.width = bitmap.width;
+ canvas.height = bitmap.height;
+ const ctx = canvas.getContext('2d');
+ ctx.drawImage(bitmap, 0, 0);
+
+ const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
+
+ // Store image and data for the Fetch function
+ window._lastImgWidth = canvas.width;
+ window._lastImgHeight = canvas.height;
+ window._lastImgData = imgData;
+ }
+ }
+ }
+ else console.warn("Clipboard read() requires HTTPS/Localhost");
+});
+
+// Returns the string created by RequestClipboardData from JS memory to Emscripten C memory
+EM_JS(char *, GetLastPastedText, (void), {
+ var str = window._lastClipboardString || "";
+ var length = lengthBytesUTF8(str) + 1;
+ if (length > 1)
+ {
+ var ptr = _malloc(length);
+ stringToUTF8(str, ptr, length);
+ return ptr;
+ }
+ return 0;
+});
+
+// Returns the image created by RequestClipboardData from JS memory to Emscripten C memory
+EM_JS(unsigned char *, GetLastPastedImage, (int *width, int *height), {
+ if (window._lastImgData)
+ {
+ const data = window._lastImgData;
+ if (data.length > 0)
+ {
+ const ptr = _malloc(data.length);
+ HEAPU8.set(data, ptr);
+
+ // Set the width and height via the pointers passed from C
+ // HEAP32 handles the 4-byte integers
+ if (width) setValue(width, window._lastImgWidth, 'i32');
+ if (height) setValue(height, window._lastImgHeight, 'i32');
+
+ // Clear the JS buffer so there is no need to fetch the same image twice
+ window._lastImgData = null;
+
+ return ptr;
+ }
+ }
+
+ return 0;
+});
+
// Get clipboard text content
// NOTE: returned string is allocated and freed by GLFW
const char *GetClipboardText(void)
{
-/*
- // Accessing clipboard data from browser is tricky due to security reasons
- // The method to use is navigator.clipboard.readText() but this is an asynchronous method
- // that will return at some moment after the function is called with the required data
- emscripten_run_script_string("navigator.clipboard.readText() \
- .then(text => { document.getElementById('clipboard').innerText = text; console.log('Pasted content: ', text); }) \
- .catch(err => { console.error('Failed to read clipboard contents: ', err); });"
- );
-
- // The main issue is getting that data, one approach could be using ASYNCIFY and wait
- // for the data but it requires adding Asyncify emscripten library on compilation
-
- // Another approach could be just copy the data in a HTML text field and try to retrieve it
- // later on if available... and clean it for future accesses
-*/
- return NULL;
+ RequestClipboardData();
+ return GetLastPastedText();
}
// Get clipboard image
Image GetClipboardImage(void)
{
Image image = { 0 };
-
- // NOTE: In theory, the new navigator.clipboard.read() can be used to return arbitrary data from clipboard (2024)
- // REF: https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/read
- TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform");
+ int w = 0, h = 0;
+ RequestClipboardData();
+ unsigned char* data = GetLastPastedImage(&w, &h);
+ if (data != NULL) {
+ image.data = data;
+ image.width = w;
+ image.height = h;
+ image.mipmaps = 1;
+ image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
+ }
return image;
}
@@ -983,9 +1050,9 @@ const char *GetKeyName(int key)
// Register all input events
void PollInputEvents(void)
{
-#if defined(SUPPORT_GESTURES_SYSTEM)
+#if 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
+ // because ProcessGestureEvent() is called on an event, not every frame
UpdateGestures();
#endif
@@ -1321,8 +1388,8 @@ static EM_BOOL EmscriptenFocusCallback(int eventType, const EmscriptenFocusEvent
switch (eventType)
{
- case EMSCRIPTEN_EVENT_BLUR: FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); break; // The canvas lost focus
- case EMSCRIPTEN_EVENT_FOCUS: FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); break;
+ case EMSCRIPTEN_EVENT_BLUR: FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); break;
+ case EMSCRIPTEN_EVENT_FOCUS: FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); break;
default: consumed = 0; break;
}
@@ -1457,7 +1524,7 @@ static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent
default: break;
}
-#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)
+#if SUPPORT_GESTURES_SYSTEM && SUPPORT_MOUSE_GESTURES
// Process mouse events as touches to be able to use mouse-gestures
GestureEvent gestureEvent = { 0 };
@@ -1529,7 +1596,7 @@ static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseE
CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
}
-#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)
+#if SUPPORT_GESTURES_SYSTEM && SUPPORT_MOUSE_GESTURES
// Process mouse events as touches to be able to use mouse-gestures
GestureEvent gestureEvent = { 0 };
@@ -1639,7 +1706,7 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent
CORE.Input.Mouse.currentPosition.y = CORE.Input.Touch.position[0].y;
}
-#if defined(SUPPORT_GESTURES_SYSTEM)
+#if SUPPORT_GESTURES_SYSTEM
GestureEvent gestureEvent = { 0 };
gestureEvent.pointCount = CORE.Input.Touch.pointCount;
diff --git a/src/raudio.c b/src/raudio.c
index 2740462b4..937c33ac3 100644
--- a/src/raudio.c
+++ b/src/raudio.c
@@ -26,7 +26,7 @@
* #define SUPPORT_FILEFORMAT_XM
* #define SUPPORT_FILEFORMAT_MOD
* Selected desired fileformats to be supported for loading. Some of those formats are
-* supported by default, to remove support, just comment unrequired #define in this module
+* supported by default, to remove support, comment unrequired #define in this module
*
* DEPENDENCIES:
* miniaudio.h - Audio device management lib (https://github.com/mackron/miniaudio)
@@ -77,7 +77,7 @@
#include "config.h" // Defines module configuration flags
#endif
-#if defined(SUPPORT_MODULE_RAUDIO) || defined(RAUDIO_STANDALONE)
+#if SUPPORT_MODULE_RAUDIO || defined(RAUDIO_STANDALONE)
#if defined(_WIN32)
// To avoid conflicting windows.h symbols with raylib, some flags are defined
@@ -200,7 +200,7 @@ typedef struct tagBITMAPINFOHEADER {
#endif
#endif
-#if defined(SUPPORT_FILEFORMAT_WAV)
+#if SUPPORT_FILEFORMAT_WAV
#define DRWAV_MALLOC RL_MALLOC
#define DRWAV_REALLOC RL_REALLOC
#define DRWAV_FREE RL_FREE
@@ -209,12 +209,12 @@ typedef struct tagBITMAPINFOHEADER {
#include "external/dr_wav.h" // WAV loading functions
#endif
-#if defined(SUPPORT_FILEFORMAT_OGG)
+#if SUPPORT_FILEFORMAT_OGG
// TODO: Remap stb_vorbis malloc()/free() calls to RL_MALLOC/RL_FREE
#include "external/stb_vorbis.c" // OGG loading functions
#endif
-#if defined(SUPPORT_FILEFORMAT_MP3)
+#if SUPPORT_FILEFORMAT_MP3
#define DRMP3_MALLOC RL_MALLOC
#define DRMP3_REALLOC RL_REALLOC
#define DRMP3_FREE RL_FREE
@@ -223,7 +223,7 @@ typedef struct tagBITMAPINFOHEADER {
#include "external/dr_mp3.h" // MP3 loading functions
#endif
-#if defined(SUPPORT_FILEFORMAT_QOA)
+#if SUPPORT_FILEFORMAT_QOA
#define QOA_MALLOC RL_MALLOC
#define QOA_FREE RL_FREE
@@ -243,7 +243,7 @@ typedef struct tagBITMAPINFOHEADER {
#endif
#endif
-#if defined(SUPPORT_FILEFORMAT_FLAC)
+#if SUPPORT_FILEFORMAT_FLAC
#define DRFLAC_MALLOC RL_MALLOC
#define DRFLAC_REALLOC RL_REALLOC
#define DRFLAC_FREE RL_FREE
@@ -253,7 +253,7 @@ typedef struct tagBITMAPINFOHEADER {
#include "external/dr_flac.h" // FLAC loading functions
#endif
-#if defined(SUPPORT_FILEFORMAT_XM)
+#if SUPPORT_FILEFORMAT_XM
#define JARXM_MALLOC RL_MALLOC
#define JARXM_FREE RL_FREE
@@ -270,7 +270,7 @@ typedef struct tagBITMAPINFOHEADER {
#endif
#endif
-#if defined(SUPPORT_FILEFORMAT_MOD)
+#if SUPPORT_FILEFORMAT_MOD
#define JARMOD_MALLOC RL_MALLOC
#define JARMOD_FREE RL_FREE
@@ -299,7 +299,7 @@ typedef struct tagBITMAPINFOHEADER {
#endif
#ifndef AUDIO_BUFFER_RESIDUAL_CAPACITY
- #define AUDIO_BUFFER_RESIDUAL_CAPACITY 8 // In PCM frames. For resampling and pitch shifting.
+ #define AUDIO_BUFFER_RESIDUAL_CAPACITY 8 // In PCM frames, for resampling and pitch shifting
#endif
//----------------------------------------------------------------------------------
@@ -406,7 +406,7 @@ static AudioData AUDIO = { // Global AUDIO context
// NOTE: Music buffer size is defined by number of samples, independent of sample size and channels number
// After some math, considering a sampleRate of 48000, a buffer refill rate of 1/60 seconds and a
// standard double-buffering system, a 4096 samples buffer has been chosen, it should be enough
- // In case of music-stalls, just increase this number
+ // In case of music-stalls, increase this number
.Buffer.defaultSize = 0,
.mixedProcessor = NULL
};
@@ -495,8 +495,9 @@ void InitAudioDevice(void)
return;
}
- // Mixing happens on a separate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may
- // want to look at something a bit smarter later on to keep everything real-time, if that's necessary
+ // Mixing happens on a separate thread which means synchronization is needed
+ // A mutex is used here to make things simple, but may want to look at something
+ // a bit smarter later on to keep everything real-time, if that's necessary
if (ma_mutex_init(&AUDIO.System.lock) != MA_SUCCESS)
{
TRACELOG(LOG_WARNING, "AUDIO: Failed to create mutex for mixing");
@@ -600,7 +601,7 @@ AudioBuffer *LoadAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sam
// be consumed. Any residual input frames need to be kept track of to
// ensure there are no discontinuities. Since raylib supports pitch
// shifting, which is done through resampling, a cache will always be
- // required. This will be kept relatively small to avoid too much wastage.
+ // required. This will be kept relatively small to avoid too much wastage
audioBuffer->converterResidualCount = 0;
audioBuffer->converterResidual = (unsigned char*)RL_CALLOC(AUDIO_BUFFER_RESIDUAL_CAPACITY*ma_get_bytes_per_frame(format, channels), 1);
@@ -720,7 +721,7 @@ void SetAudioBufferPitch(AudioBuffer *buffer, float pitch)
if ((buffer != NULL) && (pitch > 0.0f))
{
ma_mutex_lock(&AUDIO.System.lock);
- // Pitching is just an adjustment of the sample rate
+ // Pitching is an adjustment of the sample rate
// Note that this changes the duration of the sound:
// - higher pitches will make the sound faster
// - lower pitches make it slower
@@ -808,7 +809,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int
Wave wave = { 0 };
if (false) { }
-#if defined(SUPPORT_FILEFORMAT_WAV)
+#if SUPPORT_FILEFORMAT_WAV
else if ((strcmp(fileType, ".wav") == 0) || (strcmp(fileType, ".WAV") == 0))
{
drwav wav = { 0 };
@@ -822,7 +823,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int
wave.channels = wav.channels;
wave.data = (short *)RL_MALLOC((size_t)wave.frameCount*wave.channels*sizeof(short));
- // NOTE: We are forcing conversion to 16bit sample size on reading
+ // NOTE: Forcing conversion to 16bit sample size on reading
drwav_read_pcm_frames_s16(&wav, wave.frameCount, (drwav_int16 *)wave.data);
}
else TRACELOG(LOG_WARNING, "WAVE: Failed to load WAV data");
@@ -830,7 +831,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int
drwav_uninit(&wav);
}
#endif
-#if defined(SUPPORT_FILEFORMAT_OGG)
+#if SUPPORT_FILEFORMAT_OGG
else if ((strcmp(fileType, ".ogg") == 0) || (strcmp(fileType, ".OGG") == 0))
{
stb_vorbis *oggData = stb_vorbis_open_memory((unsigned char *)fileData, dataSize, NULL, NULL);
@@ -845,20 +846,20 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int
wave.frameCount = (unsigned int)stb_vorbis_stream_length_in_samples(oggData); // NOTE: It returns frames!
wave.data = (short *)RL_MALLOC(wave.frameCount*wave.channels*sizeof(short));
- // NOTE: Get the number of samples to process (be careful! we ask for number of shorts, not bytes!)
+ // NOTE: Get the number of samples to process (be careful! asking for number of shorts, not bytes!)
stb_vorbis_get_samples_short_interleaved(oggData, info.channels, (short *)wave.data, wave.frameCount*wave.channels);
stb_vorbis_close(oggData);
}
else TRACELOG(LOG_WARNING, "WAVE: Failed to load OGG data");
}
#endif
-#if defined(SUPPORT_FILEFORMAT_MP3)
+#if SUPPORT_FILEFORMAT_MP3
else if ((strcmp(fileType, ".mp3") == 0) || (strcmp(fileType, ".MP3") == 0))
{
drmp3_config config = { 0 };
unsigned long long int totalFrameCount = 0;
- // NOTE: We are forcing conversion to 32bit float sample size on reading
+ // NOTE: Forcing conversion to 32bit float sample size on reading
wave.data = drmp3_open_memory_and_read_pcm_frames_f32(fileData, dataSize, &config, &totalFrameCount, NULL);
wave.sampleSize = 32;
@@ -872,7 +873,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int
}
#endif
-#if defined(SUPPORT_FILEFORMAT_QOA)
+#if SUPPORT_FILEFORMAT_QOA
else if ((strcmp(fileType, ".qoa") == 0) || (strcmp(fileType, ".QOA") == 0))
{
qoa_desc qoa = { 0 };
@@ -891,12 +892,12 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int
}
#endif
-#if defined(SUPPORT_FILEFORMAT_FLAC)
+#if SUPPORT_FILEFORMAT_FLAC
else if ((strcmp(fileType, ".flac") == 0) || (strcmp(fileType, ".FLAC") == 0))
{
unsigned long long int totalFrameCount = 0;
- // NOTE: We are forcing conversion to 16bit sample size on reading
+ // NOTE: Forcing conversion to 16bit sample size on reading
wave.data = drflac_open_memory_and_read_pcm_frames_s16(fileData, dataSize, &wave.channels, &wave.sampleRate, &totalFrameCount, NULL);
wave.sampleSize = 16;
@@ -933,7 +934,7 @@ Sound LoadSound(const char *fileName)
Sound sound = LoadSoundFromWave(wave);
- UnloadWave(wave); // Sound is loaded, we can unload wave
+ UnloadWave(wave); // Sound is loaded, wave can be unloaded
return sound;
}
@@ -946,9 +947,9 @@ Sound LoadSoundFromWave(Wave wave)
if (wave.data != NULL)
{
- // When using miniaudio we need to do our own mixing
- // To simplify this we need convert the format of each sound to be consistent with
- // the format used to open the playback AUDIO.System.device. We can do this two ways:
+ // When using miniaudio mixing needs to b done manually
+ // To simplify this, the format of each sound needs to be converted to be consistent with
+ // the format used to open the playback AUDIO.System.device. It can be done in two ways:
//
// 1) Convert the whole sound in one go at load time (here)
// 2) Convert the audio data in chunks at mixing time
@@ -1045,7 +1046,7 @@ void UnloadSound(Sound sound)
void UnloadSoundAlias(Sound alias)
{
- // Untrack and unload just the sound buffer, not the sample data, it is shared with the source for the alias
+ // Untrack and unload the sound buffer, not the sample data, it is shared with the source for the alias
if (alias.stream.buffer != NULL)
{
UntrackAudioBuffer(alias.stream.buffer);
@@ -1073,7 +1074,7 @@ bool ExportWave(Wave wave, const char *fileName)
bool success = false;
if (false) { }
-#if defined(SUPPORT_FILEFORMAT_WAV)
+#if SUPPORT_FILEFORMAT_WAV
else if (IsFileExtension(fileName, ".wav"))
{
drwav wav = { 0 };
@@ -1096,7 +1097,7 @@ bool ExportWave(Wave wave, const char *fileName)
drwav_free(fileData, NULL);
}
#endif
-#if defined(SUPPORT_FILEFORMAT_QOA)
+#if SUPPORT_FILEFORMAT_QOA
else if (IsFileExtension(fileName, ".qoa"))
{
if (wave.sampleSize == 16)
@@ -1354,7 +1355,7 @@ Music LoadMusicStream(const char *fileName)
bool musicLoaded = false;
if (false) { }
-#if defined(SUPPORT_FILEFORMAT_WAV)
+#if SUPPORT_FILEFORMAT_WAV
else if (IsFileExtension(fileName, ".wav"))
{
drwav *ctxWav = (drwav *)RL_CALLOC(1, sizeof(drwav));
@@ -1378,7 +1379,7 @@ Music LoadMusicStream(const char *fileName)
}
}
#endif
-#if defined(SUPPORT_FILEFORMAT_OGG)
+#if SUPPORT_FILEFORMAT_OGG
else if (IsFileExtension(fileName, ".ogg"))
{
// Open ogg audio stream
@@ -1393,9 +1394,9 @@ Music LoadMusicStream(const char *fileName)
// OGG bit rate defaults to 16 bit, it's enough for compressed format
music.stream = LoadAudioStream(info.sample_rate, 16, info.channels);
- // WARNING: It seems this function returns length in frames, not samples, so we multiply by channels
+ // WARNING: It seems this function returns length in frames, not samples, so multiply by channels
music.frameCount = (unsigned int)stb_vorbis_stream_length_in_samples((stb_vorbis *)music.ctxData);
- music.looping = true; // Looping enabled by default
+ music.looping = true; // Looping enabled by default
musicLoaded = true;
}
else
@@ -1404,7 +1405,7 @@ Music LoadMusicStream(const char *fileName)
}
}
#endif
-#if defined(SUPPORT_FILEFORMAT_MP3)
+#if SUPPORT_FILEFORMAT_MP3
else if (IsFileExtension(fileName, ".mp3"))
{
drmp3 *ctxMp3 = (drmp3 *)RL_CALLOC(1, sizeof(drmp3));
@@ -1425,7 +1426,7 @@ Music LoadMusicStream(const char *fileName)
}
}
#endif
-#if defined(SUPPORT_FILEFORMAT_QOA)
+#if SUPPORT_FILEFORMAT_QOA
else if (IsFileExtension(fileName, ".qoa"))
{
qoaplay_desc *ctxQoa = qoaplay_open(fileName);
@@ -1434,8 +1435,8 @@ Music LoadMusicStream(const char *fileName)
{
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
+ // NOTE: Loading samples as 32bit float normalized data, so,
+ // configure the output audio stream to also use float 32bit
music.stream = LoadAudioStream(ctxQoa->info.samplerate, 32, ctxQoa->info.channels);
music.frameCount = ctxQoa->info.samples;
music.looping = true; // Looping enabled by default
@@ -1444,7 +1445,7 @@ Music LoadMusicStream(const char *fileName)
else{} //No uninit required
}
#endif
-#if defined(SUPPORT_FILEFORMAT_FLAC)
+#if SUPPORT_FILEFORMAT_FLAC
else if (IsFileExtension(fileName, ".flac"))
{
drflac *ctxFlac = drflac_open_file(fileName, NULL);
@@ -1466,7 +1467,7 @@ Music LoadMusicStream(const char *fileName)
}
}
#endif
-#if defined(SUPPORT_FILEFORMAT_XM)
+#if SUPPORT_FILEFORMAT_XM
else if (IsFileExtension(fileName, ".xm"))
{
jar_xm_context_t *ctxXm = NULL;
@@ -1486,7 +1487,7 @@ Music LoadMusicStream(const char *fileName)
music.stream = LoadAudioStream(AUDIO.System.device.sampleRate, bits, AUDIO_DEVICE_CHANNELS);
music.frameCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm); // NOTE: Always 2 channels (stereo)
music.looping = true; // Looping enabled by default
- jar_xm_reset(ctxXm); // Make sure we start at the beginning of the song
+ jar_xm_reset(ctxXm); // Make sure to start at the beginning of the song
musicLoaded = true;
}
else
@@ -1495,7 +1496,7 @@ Music LoadMusicStream(const char *fileName)
}
}
#endif
-#if defined(SUPPORT_FILEFORMAT_MOD)
+#if SUPPORT_FILEFORMAT_MOD
else if (IsFileExtension(fileName, ".mod"))
{
jar_mod_context_t *ctxMod = (jar_mod_context_t *)RL_CALLOC(1, sizeof(jar_mod_context_t));
@@ -1546,7 +1547,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
bool musicLoaded = false;
if (false) { }
-#if defined(SUPPORT_FILEFORMAT_WAV)
+#if SUPPORT_FILEFORMAT_WAV
else if ((strcmp(fileType, ".wav") == 0) || (strcmp(fileType, ".WAV") == 0))
{
drwav *ctxWav = (drwav *)RL_CALLOC(1, sizeof(drwav));
@@ -1572,7 +1573,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
}
}
#endif
-#if defined(SUPPORT_FILEFORMAT_OGG)
+#if SUPPORT_FILEFORMAT_OGG
else if ((strcmp(fileType, ".ogg") == 0) || (strcmp(fileType, ".OGG") == 0))
{
// Open ogg audio stream
@@ -1587,7 +1588,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
// OGG bit rate defaults to 16 bit, it's enough for compressed format
music.stream = LoadAudioStream(info.sample_rate, 16, info.channels);
- // WARNING: It seems this function returns length in frames, not samples, so we multiply by channels
+ // WARNING: It seems this function returns length in frames, not samples, so multiply by channels
music.frameCount = (unsigned int)stb_vorbis_stream_length_in_samples((stb_vorbis *)music.ctxData);
music.looping = true; // Looping enabled by default
musicLoaded = true;
@@ -1598,7 +1599,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
}
}
#endif
-#if defined(SUPPORT_FILEFORMAT_MP3)
+#if SUPPORT_FILEFORMAT_MP3
else if ((strcmp(fileType, ".mp3") == 0) || (strcmp(fileType, ".MP3") == 0))
{
drmp3 *ctxMp3 = (drmp3 *)RL_CALLOC(1, sizeof(drmp3));
@@ -1620,7 +1621,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
}
}
#endif
-#if defined(SUPPORT_FILEFORMAT_QOA)
+#if SUPPORT_FILEFORMAT_QOA
else if ((strcmp(fileType, ".qoa") == 0) || (strcmp(fileType, ".QOA") == 0))
{
qoaplay_desc *ctxQoa = NULL;
@@ -1633,8 +1634,9 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
{
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
+
+ // NOTE: Loading samples are 32bit float normalized data, so,
+ // configure the output audio stream to also use float 32bit
music.stream = LoadAudioStream(ctxQoa->info.samplerate, 32, ctxQoa->info.channels);
music.frameCount = ctxQoa->info.samples;
music.looping = true; // Looping enabled by default
@@ -1643,7 +1645,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
else{} //No uninit required
}
#endif
-#if defined(SUPPORT_FILEFORMAT_FLAC)
+#if SUPPORT_FILEFORMAT_FLAC
else if ((strcmp(fileType, ".flac") == 0) || (strcmp(fileType, ".FLAC") == 0))
{
drflac *ctxFlac = drflac_open_memory((const void *)data, dataSize, NULL);
@@ -1665,7 +1667,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
}
}
#endif
-#if defined(SUPPORT_FILEFORMAT_XM)
+#if SUPPORT_FILEFORMAT_XM
else if ((strcmp(fileType, ".xm") == 0) || (strcmp(fileType, ".XM") == 0))
{
jar_xm_context_t *ctxXm = NULL;
@@ -1684,7 +1686,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
music.stream = LoadAudioStream(AUDIO.System.device.sampleRate, bits, 2);
music.frameCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm); // NOTE: Always 2 channels (stereo)
music.looping = true; // Looping enabled by default
- jar_xm_reset(ctxXm); // Make sure we start at the beginning of the song
+ jar_xm_reset(ctxXm); // Make sure to start at the beginning of the song
musicLoaded = true;
}
@@ -1694,7 +1696,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data,
}
}
#endif
-#if defined(SUPPORT_FILEFORMAT_MOD)
+#if SUPPORT_FILEFORMAT_MOD
else if ((strcmp(fileType, ".mod") == 0) || (strcmp(fileType, ".MOD") == 0))
{
jar_mod_context_t *ctxMod = (jar_mod_context_t *)RL_MALLOC(sizeof(jar_mod_context_t));
@@ -1770,25 +1772,25 @@ void UnloadMusicStream(Music music)
if (music.ctxData != NULL)
{
if (false) { }
-#if defined(SUPPORT_FILEFORMAT_WAV)
+#if SUPPORT_FILEFORMAT_WAV
else if (music.ctxType == MUSIC_AUDIO_WAV) drwav_uninit((drwav *)music.ctxData);
#endif
-#if defined(SUPPORT_FILEFORMAT_OGG)
+#if SUPPORT_FILEFORMAT_OGG
else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData);
#endif
-#if defined(SUPPORT_FILEFORMAT_MP3)
+#if 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)
+#if SUPPORT_FILEFORMAT_QOA
else if (music.ctxType == MUSIC_AUDIO_QOA) qoaplay_close((qoaplay_desc *)music.ctxData);
#endif
-#if defined(SUPPORT_FILEFORMAT_FLAC)
+#if SUPPORT_FILEFORMAT_FLAC
else if (music.ctxType == MUSIC_AUDIO_FLAC) { drflac_close((drflac *)music.ctxData); drflac_free((drflac *)music.ctxData, NULL); }
#endif
-#if defined(SUPPORT_FILEFORMAT_XM)
+#if 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)
+#if SUPPORT_FILEFORMAT_MOD
else if (music.ctxType == MUSIC_MODULE_MOD) { jar_mod_unload((jar_mod_context_t *)music.ctxData); RL_FREE(music.ctxData); }
#endif
}
@@ -1819,25 +1821,25 @@ void StopMusicStream(Music music)
switch (music.ctxType)
{
-#if defined(SUPPORT_FILEFORMAT_WAV)
+#if SUPPORT_FILEFORMAT_WAV
case MUSIC_AUDIO_WAV: drwav_seek_to_first_pcm_frame((drwav *)music.ctxData); break;
#endif
-#if defined(SUPPORT_FILEFORMAT_OGG)
+#if SUPPORT_FILEFORMAT_OGG
case MUSIC_AUDIO_OGG: stb_vorbis_seek_start((stb_vorbis *)music.ctxData); break;
#endif
-#if defined(SUPPORT_FILEFORMAT_MP3)
+#if SUPPORT_FILEFORMAT_MP3
case MUSIC_AUDIO_MP3: drmp3_seek_to_start_of_stream((drmp3 *)music.ctxData); break;
#endif
-#if defined(SUPPORT_FILEFORMAT_QOA)
+#if SUPPORT_FILEFORMAT_QOA
case MUSIC_AUDIO_QOA: qoaplay_rewind((qoaplay_desc *)music.ctxData); break;
#endif
-#if defined(SUPPORT_FILEFORMAT_FLAC)
+#if SUPPORT_FILEFORMAT_FLAC
case MUSIC_AUDIO_FLAC: drflac__seek_to_first_frame((drflac *)music.ctxData); break;
#endif
-#if defined(SUPPORT_FILEFORMAT_XM)
+#if SUPPORT_FILEFORMAT_XM
case MUSIC_MODULE_XM: jar_xm_reset((jar_xm_context_t *)music.ctxData); break;
#endif
-#if defined(SUPPORT_FILEFORMAT_MOD)
+#if SUPPORT_FILEFORMAT_MOD
case MUSIC_MODULE_MOD: jar_mod_seek_start((jar_mod_context_t *)music.ctxData); break;
#endif
default: break;
@@ -1854,26 +1856,26 @@ void SeekMusicStream(Music music, float position)
switch (music.ctxType)
{
-#if defined(SUPPORT_FILEFORMAT_WAV)
+#if SUPPORT_FILEFORMAT_WAV
case MUSIC_AUDIO_WAV: drwav_seek_to_pcm_frame((drwav *)music.ctxData, positionInFrames); break;
#endif
-#if defined(SUPPORT_FILEFORMAT_OGG)
+#if SUPPORT_FILEFORMAT_OGG
case MUSIC_AUDIO_OGG: stb_vorbis_seek_frame((stb_vorbis *)music.ctxData, positionInFrames); break;
#endif
-#if defined(SUPPORT_FILEFORMAT_MP3)
+#if SUPPORT_FILEFORMAT_MP3
case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, positionInFrames); break;
#endif
-#if defined(SUPPORT_FILEFORMAT_QOA)
+#if SUPPORT_FILEFORMAT_QOA
case MUSIC_AUDIO_QOA:
{
int qoaFrame = positionInFrames/QOA_FRAME_LEN;
qoaplay_seek_frame((qoaplay_desc *)music.ctxData, qoaFrame); // Seeks to QOA frame, not PCM frame
- // We need to compute QOA frame number and update positionInFrames
+ // Compute QOA frame number and update positionInFrames
positionInFrames = ((qoaplay_desc *)music.ctxData)->sample_position;
} break;
#endif
-#if defined(SUPPORT_FILEFORMAT_FLAC)
+#if SUPPORT_FILEFORMAT_FLAC
case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, positionInFrames); break;
#endif
default: break;
@@ -1896,7 +1898,7 @@ void UpdateMusicStream(Music music)
unsigned int subBufferSizeInFrames = music.stream.buffer->sizeInFrames/2;
- // On first call of this function we lazily pre-allocated a temp buffer to read audio files/memory data in
+ // On first call of this function, lazily pre-allocated a temp buffer to read audio files/memory data in
int frameSize = music.stream.channels*music.stream.sampleSize/8;
unsigned int pcmSize = subBufferSizeInFrames*frameSize;
@@ -1937,7 +1939,7 @@ void UpdateMusicStream(Music music)
switch (music.ctxType)
{
- #if defined(SUPPORT_FILEFORMAT_WAV)
+ #if SUPPORT_FILEFORMAT_WAV
case MUSIC_AUDIO_WAV:
{
if (music.stream.sampleSize == 16)
@@ -1964,7 +1966,7 @@ void UpdateMusicStream(Music music)
}
} break;
#endif
- #if defined(SUPPORT_FILEFORMAT_OGG)
+ #if SUPPORT_FILEFORMAT_OGG
case MUSIC_AUDIO_OGG:
{
while (true)
@@ -1977,7 +1979,7 @@ void UpdateMusicStream(Music music)
}
} break;
#endif
- #if defined(SUPPORT_FILEFORMAT_MP3)
+ #if SUPPORT_FILEFORMAT_MP3
case MUSIC_AUDIO_MP3:
{
while (true)
@@ -1990,7 +1992,7 @@ void UpdateMusicStream(Music music)
}
} break;
#endif
- #if defined(SUPPORT_FILEFORMAT_QOA)
+ #if SUPPORT_FILEFORMAT_QOA
case MUSIC_AUDIO_QOA:
{
unsigned int frameCountRead = qoaplay_decode((qoaplay_desc *)music.ctxData, (float *)AUDIO.System.pcmBuffer, framesToStream);
@@ -2007,7 +2009,7 @@ void UpdateMusicStream(Music music)
*/
} break;
#endif
- #if defined(SUPPORT_FILEFORMAT_FLAC)
+ #if SUPPORT_FILEFORMAT_FLAC
case MUSIC_AUDIO_FLAC:
{
while (true)
@@ -2020,10 +2022,10 @@ void UpdateMusicStream(Music music)
}
} break;
#endif
- #if defined(SUPPORT_FILEFORMAT_XM)
+ #if SUPPORT_FILEFORMAT_XM
case MUSIC_MODULE_XM:
{
- // NOTE: Internally we consider 2 channels generation, so sampleCount/2
+ // NOTE: Internally considering 2 channels generation, so sampleCount/2
if (AUDIO_DEVICE_FORMAT == ma_format_f32) jar_xm_generate_samples((jar_xm_context_t *)music.ctxData, (float *)AUDIO.System.pcmBuffer, framesToStream);
else if (AUDIO_DEVICE_FORMAT == ma_format_s16) jar_xm_generate_samples_16bit((jar_xm_context_t *)music.ctxData, (short *)AUDIO.System.pcmBuffer, framesToStream);
else if (AUDIO_DEVICE_FORMAT == ma_format_u8) jar_xm_generate_samples_8bit((jar_xm_context_t *)music.ctxData, (char *)AUDIO.System.pcmBuffer, framesToStream);
@@ -2031,7 +2033,7 @@ void UpdateMusicStream(Music music)
} break;
#endif
- #if defined(SUPPORT_FILEFORMAT_MOD)
+ #if SUPPORT_FILEFORMAT_MOD
case MUSIC_MODULE_MOD:
{
// NOTE: 3rd parameter (nbsample) specify the number of stereo 16bits samples desired, so sampleCount/2
@@ -2089,7 +2091,7 @@ float GetMusicTimePlayed(Music music)
float secondsPlayed = 0.0f;
if (music.stream.buffer != NULL)
{
-#if defined(SUPPORT_FILEFORMAT_XM)
+#if SUPPORT_FILEFORMAT_XM
if (music.ctxType == MUSIC_MODULE_XM)
{
uint64_t framesPlayed = 0;
@@ -2261,7 +2263,7 @@ void SetAudioStreamCallback(AudioStream stream, AudioCallback callback)
// Add processor to audio stream. Contrary to buffers, the order of processors is important
// The new processor must be added at the end. As there aren't supposed to be a lot of processors attached to
-// a given stream, we iterate through the list to find the end. That way we don't need a pointer to the last element
+// a given stream, iterate through the list to find the end. That way there is no need to keep a pointer to the last element
void AttachAudioStreamProcessor(AudioStream stream, AudioCallback process)
{
ma_mutex_lock(&AUDIO.System.lock);
@@ -2314,7 +2316,7 @@ void DetachAudioStreamProcessor(AudioStream stream, AudioCallback process)
// Add processor to audio pipeline. Order of processors is important
// Works the same way as {Attach,Detach}AudioStreamProcessor() functions, except
-// these two work on the already mixed output just before sending it to the sound hardware
+// these two work on the already mixed output before sending it to the sound hardware
void AttachAudioMixedProcessor(AudioCallback process)
{
ma_mutex_lock(&AUDIO.System.lock);
@@ -2395,20 +2397,20 @@ static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer,
if (currentSubBufferIndex > 1) return 0;
// Another thread can update the processed state of buffers, so
- // we just take a copy here to try and avoid potential synchronization problems
+ // take a copy here to try and avoid potential synchronization problems
bool isSubBufferProcessed[2] = { 0 };
isSubBufferProcessed[0] = audioBuffer->isSubBufferProcessed[0];
isSubBufferProcessed[1] = audioBuffer->isSubBufferProcessed[1];
ma_uint32 frameSizeInBytes = ma_get_bytes_per_frame(audioBuffer->converter.formatIn, audioBuffer->converter.channelsIn);
- // Fill out every frame until we find a buffer that's marked as processed. Then fill the remainder with 0
+ // Fill out every frame until a buffer that's marked as processed is found, then fill the remainder with 0
ma_uint32 framesRead = 0;
while (1)
{
- // We break from this loop differently depending on the buffer's usage
- // - For static buffers, we simply fill as much data as we can
- // - For streaming buffers we only fill half of the buffer that are processed
+ // Break from this loop differently depending on the buffer's usage
+ // - For static buffers, simply fill as much data as possible
+ // - For streaming buffers, only fill half of the buffer that are processed
// Unprocessed halves must keep their audio data in-tact
if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC)
{
@@ -2463,8 +2465,8 @@ static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer,
{
memset((unsigned char *)framesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes);
- // For static buffers we can fill the remaining frames with silence for safety, but we don't want
- // to report those frames as "read". The reason for this is that the caller uses the return value
+ // For static buffers, fill the remaining frames with silence for safety, but don't report those
+ // frames as "read"; The reason for this is that the caller uses the return value
// to know whether a non-looping sound has finished playback
if (audioBuffer->usage != AUDIO_BUFFER_USAGE_STATIC) framesRead += totalFramesRemaining;
}
@@ -2475,48 +2477,48 @@ static ma_uint32 ReadAudioBufferFramesInInternalFormat(AudioBuffer *audioBuffer,
// Reads audio data from an AudioBuffer object in device format, returned data will be in a format appropriate for mixing
static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, float *framesOut, ma_uint32 frameCount)
{
- // NOTE: Continuously converting data from the AudioBuffer's internal format to the mixing format,
- // which should be defined by the output format of the data converter.
- // This is done until frameCount frames have been output.
+ // NOTE: Continuously converting data from the AudioBuffer's internal format to the mixing format,
+ // which should be defined by the output format of the data converter
+ // This is done until frameCount frames have been output
ma_uint32 bpf = ma_get_bytes_per_frame(audioBuffer->converter.formatIn, audioBuffer->converter.channelsIn);
ma_uint8 inputBuffer[4096] = { 0 };
ma_uint32 inputBufferFrameCap = sizeof(inputBuffer)/bpf;
-
+
ma_uint32 totalOutputFramesProcessed = 0;
while (totalOutputFramesProcessed < frameCount)
{
float *runningFramesOut = framesOut + (totalOutputFramesProcessed*audioBuffer->converter.channelsOut);
ma_uint64 outputFramesToProcessThisIteration = frameCount - totalOutputFramesProcessed;
- ma_uint64 inputFramesToProcessThisIteration = 0;
-
- // Process any residual input frames from the previous read first.
+ //ma_uint64 inputFramesToProcessThisIteration = 0;
+
+ // Process any residual input frames from the previous read first
if (audioBuffer->converterResidualCount > 0)
{
ma_uint64 inputFramesProcessedThisIteration = audioBuffer->converterResidualCount;
ma_uint64 outputFramesProcessedThisIteration = outputFramesToProcessThisIteration;
ma_data_converter_process_pcm_frames(&audioBuffer->converter, audioBuffer->converterResidual, &inputFramesProcessedThisIteration, runningFramesOut, &outputFramesProcessedThisIteration);
- // Make sure the data in the cache is consumed. This can be optimized to use a cursor instead of a memmove().
- memmove(audioBuffer->converterResidual, audioBuffer->converterResidual + inputFramesProcessedThisIteration*bpf, (size_t)(AUDIO_BUFFER_RESIDUAL_CAPACITY - inputFramesProcessedThisIteration) * bpf);
+ // Make sure the data in the cache is consumed, this can be optimized to use a cursor instead of a memmove()
+ memmove(audioBuffer->converterResidual, audioBuffer->converterResidual + inputFramesProcessedThisIteration*bpf, (size_t)(AUDIO_BUFFER_RESIDUAL_CAPACITY - inputFramesProcessedThisIteration)*bpf);
audioBuffer->converterResidualCount -= (ma_uint32)inputFramesProcessedThisIteration; // Safe cast
totalOutputFramesProcessed += (ma_uint32)outputFramesProcessedThisIteration; // Safe cast
}
else
{
- // Getting here means there are no residual frames from the previous read. Fresh data can now be
- // pulled from the AudioBuffer and processed.
+ // Getting here means there are no residual frames from the previous read
+ // Fresh data can now be pulled from the AudioBuffer and processed
//
- // A best guess needs to be used made to determine how many input frames to pull from the
- // buffer. There are three possible outcomes: 1) exact; 2) underestimated; 3) overestimated.
+ // A best guess needs to be used made to determine how many input frames to pull from the buffer
+ // There are three possible outcomes: 1) exact; 2) underestimated; 3) overestimated
//
- // When the guess is exactly correct or underestimated there is nothing special to handle - it'll be
- // handled naturally by the loop.
+ // When the guess is exactly correct or underestimated there is nothing special to handle,
+ // it'll be handled naturally by the loop
//
- // When the guess is overestimated, that's when it gets more complicated. In this case, any overflow
- // needs to be stored in a buffer for later processing by the next read.
- ma_uint32 estimatedInputFrameCount = (ma_uint32)(((float)audioBuffer->converter.resampler.sampleRateIn / audioBuffer->converter.resampler.sampleRateOut) * outputFramesToProcessThisIteration);
- if (estimatedInputFrameCount == 0) estimatedInputFrameCount = 1; // Make sure at least one input frame is read.
+ // When the guess is overestimated, that's when it gets more complicated
+ // In this case, any overflow needs to be stored in a buffer for later processing by the next read
+ ma_uint32 estimatedInputFrameCount = (ma_uint32)(((float)audioBuffer->converter.resampler.sampleRateIn / audioBuffer->converter.resampler.sampleRateOut)*outputFramesToProcessThisIteration);
+ if (estimatedInputFrameCount == 0) estimatedInputFrameCount = 1; // Make sure at least one input frame is read
if (estimatedInputFrameCount > inputBufferFrameCap) estimatedInputFrameCount = inputBufferFrameCap;
ma_uint32 inputFramesInInternalFormatCount = ReadAudioBufferFramesInInternalFormat(audioBuffer, inputBuffer, estimatedInputFrameCount);
@@ -2529,18 +2531,18 @@ static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, f
if (inputFramesInInternalFormatCount > inputFramesProcessedThisIteration)
{
- // Getting here means the estimated input frame count was overestimated. The residual needs
- // be stored for later use.
+ // Getting here means the estimated input frame count was overestimated
+ // The residual needs be stored for later use
ma_uint64 residualFrameCount = inputFramesInInternalFormatCount - inputFramesProcessedThisIteration;
- // A safety check to make sure the capacity of the residual cache is not exceeded.
+ // A safety check to make sure the capacity of the residual cache is not exceeded
if (residualFrameCount > AUDIO_BUFFER_RESIDUAL_CAPACITY)
{
residualFrameCount = AUDIO_BUFFER_RESIDUAL_CAPACITY;
}
- memcpy(audioBuffer->converterResidual, inputBuffer + inputFramesProcessedThisIteration*bpf, (size_t)(residualFrameCount * bpf));
- audioBuffer->converterResidualCount = residualFrameCount;
+ memcpy(audioBuffer->converterResidual, inputBuffer + inputFramesProcessedThisIteration*bpf, (size_t)(residualFrameCount*bpf));
+ audioBuffer->converterResidualCount = (unsigned int)residualFrameCount;
}
if (inputFramesInInternalFormatCount < estimatedInputFrameCount) break; // Reached the end of the sound
@@ -2557,7 +2559,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const
{
(void)pDevice;
- // Mixing is basically just an accumulation, we need to initialize the output buffer to 0
+ // Mixing is basically an accumulation, need to initialize the output buffer to 0
memset(pFramesOut, 0, frameCount*pDevice->playback.channels*ma_get_bytes_per_sample(pDevice->playback.format));
// Using a mutex here for thread-safety which makes things not real-time
@@ -2575,7 +2577,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const
{
if (framesRead >= frameCount) break;
- // Just read as much data as we can from the stream
+ // Read as much data as possible from the stream
ma_uint32 framesToRead = (frameCount - framesRead);
while (framesToRead > 0)
@@ -2614,7 +2616,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const
break;
}
- // If we weren't able to read all the frames we requested, break
+ // If all the frames requested can't be read, break
if (framesJustRead < framesToReadRightNow)
{
if (!audioBuffer->looping)
@@ -2624,7 +2626,7 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const
}
else
{
- // Should never get here, but just for safety,
+ // Should never get here, but for safety,
// move the cursor position back to the start and continue the loop
audioBuffer->frameCursorPos = 0;
continue;
@@ -2649,14 +2651,14 @@ static void OnSendAudioDataToDevice(ma_device *pDevice, void *pFramesOut, const
ma_mutex_unlock(&AUDIO.System.lock);
}
-// Main mixing function, pretty simple in this project, just an accumulation
+// Main mixing function, pretty simple in this project, only an accumulation
// NOTE: framesOut is both an input and an output, it is initially filled with zeros outside of this function
static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 frameCount, AudioBuffer *buffer)
{
const float localVolume = buffer->volume;
const ma_uint32 channels = AUDIO.System.device.playback.channels;
- if (channels == 2) // We consider panning
+ if (channels == 2) // Consider panning
{
const float right = (buffer->pan + 1.0f)/2.0f; // Normalize: [-1..1] -> [0..1]
const float left = 1.0f - right;
@@ -2676,7 +2678,7 @@ static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 fr
frameIn += 2;
}
}
- else // We do not consider panning
+ else // Do not consider panning
{
for (ma_uint32 frame = 0; frame < frameCount; frame++)
{
@@ -2737,7 +2739,7 @@ static void UpdateAudioStreamInLockedState(AudioStream stream, const void *data,
}
else
{
- // Just update whichever sub-buffer is processed
+ // Update whichever sub-buffer is processed
subBufferToUpdate = (stream.buffer->isSubBufferProcessed[0])? 0 : 1;
}
@@ -2831,7 +2833,7 @@ static const char *GetFileNameWithoutExt(const char *filePath)
{
if (fileName[i] == '.')
{
- // NOTE: We break on first '.' found
+ // NOTE: Break on first '.' found
fileName[i] = '\0';
break;
}
@@ -2862,7 +2864,7 @@ static unsigned char *LoadFileData(const char *fileName, int *dataSize)
{
data = (unsigned char *)RL_MALLOC(size*sizeof(unsigned char));
- // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements]
+ // NOTE: fread() returns number of read elements instead of bytes, so reading [1 byte, size elements]
unsigned int count = (unsigned int)fread(data, sizeof(unsigned char), size, file);
*dataSize = count;
@@ -2947,4 +2949,4 @@ static bool SaveFileText(const char *fileName, char *text)
#undef AudioBuffer
-#endif // SUPPORT_MODULE_RAUDIO
+#endif // SUPPORT_MODULE_RAUDIO
diff --git a/src/raylib.h b/src/raylib.h
index 06138ceca..a1391de19 100644
--- a/src/raylib.h
+++ b/src/raylib.h
@@ -1,6 +1,6 @@
/**********************************************************************************************
*
-* raylib v5.6-dev - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com)
+* raylib v6.0 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com)
*
* FEATURES:
* - NO external dependencies, all required libraries included with raylib
@@ -26,7 +26,7 @@
* - One default RenderBatch is loaded on rlglInit()->rlLoadRenderBatch() [rlgl] (OpenGL 3.3 or ES2)
*
* DEPENDENCIES:
-* [rcore] Depends on the selected platform backend, check rcore.c header for details
+* [rcore] Depends on the selected platform backend, check rcore.c header for details
* [rlgl] glad/glad_gles2 (David Herberth - github.com/Dav1dde/glad) for OpenGL extensions loading
* [raudio] miniaudio (David Reid - github.com/mackron/miniaudio) for audio device/context management
*
@@ -39,7 +39,7 @@
* [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG)
* [rtextures] stb_image_resize2 (Sean Barret) for image resizing algorithms
* [rtextures] stb_perlin (Sean Barret) for Perlin Noise image generation
-* [rtextures] rl_gputex (Ramon Santamaria) for GPU-compressed texture formats
+* [rtextures] rl_gputex (Ramon Santamaria) for GPU-compressed texture formats
* [rtext] stb_truetype (Sean Barret) for ttf fonts loading
* [rtext] stb_rect_pack (Sean Barret) for rectangles packing
* [rmodels] par_shapes (Philip Rideout) for parametric 3d shapes generation
@@ -85,10 +85,10 @@
#include // Required for: va_list - Only used by TraceLogCallback
-#define RAYLIB_VERSION_MAJOR 5
-#define RAYLIB_VERSION_MINOR 6
+#define RAYLIB_VERSION_MAJOR 6
+#define RAYLIB_VERSION_MINOR 0
#define RAYLIB_VERSION_PATCH 0
-#define RAYLIB_VERSION "5.6-dev"
+#define RAYLIB_VERSION "6.0"
// Function specifiers in case library is build/used as a shared library
// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll
@@ -159,7 +159,7 @@
// NOTE: Set some defines with some data types declared by raylib
// Other modules (raymath, rlgl) also require some of those types, so,
// to be able to use those other modules as standalone (not depending on raylib)
-// this defines are very useful for internal check and avoid type (re)definitions
+// this defines are useful for internal check and avoid type (re)definitions
#define RL_COLOR_TYPE
#define RL_RECTANGLE_TYPE
#define RL_VECTOR2_TYPE
@@ -353,13 +353,15 @@ typedef struct Mesh {
unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
unsigned short *indices; // Vertex indices (in case vertex data comes indexed)
- // Animation vertex data
+ // Skin data for animation
+ int boneCount; // Number of bones (MAX: 256 bones)
+ unsigned char *boneIndices; // Vertex bone indices, up to 4 bones influence by vertex (skinning) (shader-location = 6)
+ float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7)
+
+ // Runtime animation vertex data (CPU skinning)
+ // NOTE: In case of GPU skinning, not used, pointers are NULL
float *animVertices; // Animated vertex positions (after bones transformations)
float *animNormals; // Animated normals (after bones transformations)
- unsigned char *boneIds; // Vertex bone ids, max 255 bone ids, up to 4 bones influence by vertex (skinning) (shader-location = 6)
- float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7)
- Matrix *boneMatrices; // Bones animated transformation matrices
- int boneCount; // Number of bones
// OpenGL identifiers
unsigned int vaoId; // OpenGL Vertex Array Object id
@@ -393,12 +395,22 @@ typedef struct Transform {
Vector3 scale; // Scale
} Transform;
+// Anim pose, an array of Transform[]
+typedef Transform *ModelAnimPose;
+
// Bone, skeletal animation bone
typedef struct BoneInfo {
char name[32]; // Bone name
int parent; // Bone parent
} BoneInfo;
+// Skeleton, animation bones hierarchy
+typedef struct ModelSkeleton {
+ int boneCount; // Number of bones
+ BoneInfo *bones; // Bones information (skeleton)
+ ModelAnimPose bindPose; // Bones base transformation (Transform[])
+} ModelSkeleton;
+
// Model, meshes, materials and animation data
typedef struct Model {
Matrix transform; // Local transform matrix
@@ -410,18 +422,20 @@ typedef struct Model {
int *meshMaterial; // Mesh material number
// Animation data
- int boneCount; // Number of bones
- BoneInfo *bones; // Bones information (skeleton)
- Transform *bindPose; // Bones base transformation (pose)
+ ModelSkeleton skeleton; // Skeleton for animation
+
+ // Runtime animation data (CPU/GPU skinning)
+ ModelAnimPose currentPose; // Current animation pose (Transform[])
+ Matrix *boneMatrices; // Bones animated transformation matrices
} Model;
-// ModelAnimation
+// ModelAnimation, contains a full animation sequence
typedef struct ModelAnimation {
char name[32]; // Animation name
- int boneCount; // Number of bones
- int frameCount; // Number of animation frames
- BoneInfo *bones; // Bones information (skeleton)
- Transform **framePoses; // Poses array by frame
+
+ int boneCount; // Number of bones (per pose)
+ int keyframeCount; // Number of animation key frames
+ ModelAnimPose *keyframePoses; // Animation sequence keyframe poses [keyframe][pose]
} ModelAnimation;
// Ray, ray for raycasting
@@ -719,7 +733,7 @@ typedef enum {
// Gamepad buttons
typedef enum {
- GAMEPAD_BUTTON_UNKNOWN = 0, // Unknown button, just for error checking
+ GAMEPAD_BUTTON_UNKNOWN = 0, // Unknown button, for error checking
GAMEPAD_BUTTON_LEFT_FACE_UP, // Gamepad left DPAD up button
GAMEPAD_BUTTON_LEFT_FACE_RIGHT, // Gamepad left DPAD right button
GAMEPAD_BUTTON_LEFT_FACE_DOWN, // Gamepad left DPAD down button
@@ -768,6 +782,8 @@ typedef enum {
#define MATERIAL_MAP_SPECULAR MATERIAL_MAP_METALNESS
// Shader location index
+// NOTE: Some locations are tried to be set automatically on shader loading,
+// but only if default attributes/uniforms names are found, check config.h for names
typedef enum {
SHADER_LOC_VERTEX_POSITION = 0, // Shader location: vertex attribute: position
SHADER_LOC_VERTEX_TEXCOORD01, // Shader location: vertex attribute: texcoord01
@@ -790,15 +806,15 @@ typedef enum {
SHADER_LOC_MAP_ROUGHNESS, // Shader location: sampler2d texture: roughness
SHADER_LOC_MAP_OCCLUSION, // Shader location: sampler2d texture: occlusion
SHADER_LOC_MAP_EMISSION, // Shader location: sampler2d texture: emission
- SHADER_LOC_MAP_HEIGHT, // Shader location: sampler2d texture: height
+ SHADER_LOC_MAP_HEIGHT, // Shader location: sampler2d texture: heightmap
SHADER_LOC_MAP_CUBEMAP, // Shader location: samplerCube texture: cubemap
SHADER_LOC_MAP_IRRADIANCE, // Shader location: samplerCube texture: irradiance
SHADER_LOC_MAP_PREFILTER, // Shader location: samplerCube texture: prefilter
SHADER_LOC_MAP_BRDF, // Shader location: sampler2d texture: brdf
- SHADER_LOC_VERTEX_BONEIDS, // Shader location: vertex attribute: boneIds
- SHADER_LOC_VERTEX_BONEWEIGHTS, // Shader location: vertex attribute: boneWeights
- SHADER_LOC_BONE_MATRICES, // Shader location: array of matrices uniform: boneMatrices
- SHADER_LOC_VERTEX_INSTANCE_TX // Shader location: vertex attribute: instanceTransform
+ SHADER_LOC_VERTEX_BONEIDS, // Shader location: vertex attribute: bone indices
+ SHADER_LOC_VERTEX_BONEWEIGHTS, // Shader location: vertex attribute: bone weights
+ SHADER_LOC_MATRIX_BONETRANSFORMS, // Shader location: matrix attribute: bone transforms (animation)
+ SHADER_LOC_VERTEX_INSTANCETRANSFORM // Shader location: vertex attribute: instance transforms
} ShaderLocationIndex;
#define SHADER_LOC_MAP_DIFFUSE SHADER_LOC_MAP_ALBEDO
@@ -862,7 +878,7 @@ typedef enum {
// NOTE 1: Filtering considers mipmaps if available in the texture
// NOTE 2: Filter is accordingly set for minification and magnification
typedef enum {
- TEXTURE_FILTER_POINT = 0, // No filter, just pixel approximation
+ TEXTURE_FILTER_POINT = 0, // No filter, pixel approximation
TEXTURE_FILTER_BILINEAR, // Linear filtering
TEXTURE_FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps)
TEXTURE_FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x
@@ -1151,7 +1167,7 @@ RLAPI bool IsFileDropped(void); // Check if
RLAPI FilePathList LoadDroppedFiles(void); // Load dropped filepaths
RLAPI void UnloadDroppedFiles(FilePathList files); // Unload dropped filepaths
RLAPI unsigned int GetDirectoryFileCount(const char *dirPath); // Get the file count in a directory
-RLAPI unsigned int GetDirectoryFileCountEx(const char *basePath, const char *filter, bool scanSubdirs);// Get the file count in a directory with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result
+RLAPI unsigned int GetDirectoryFileCountEx(const char *basePath, const char *filter, bool scanSubdirs); // Get the file count in a directory with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result
// Compression/Encoding functionality
RLAPI unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize); // Compress data (DEFLATE algorithm), memory must be MemFree()
@@ -1413,8 +1429,8 @@ RLAPI void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color c
RLAPI void ImageDrawTriangle(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle within an image
RLAPI void ImageDrawTriangleEx(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color c1, Color c2, Color c3); // Draw triangle with interpolated colors within an image
RLAPI void ImageDrawTriangleLines(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline within an image
-RLAPI void ImageDrawTriangleFan(Image *dst, const Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points within an image (first vertex is the center)
-RLAPI void ImageDrawTriangleStrip(Image *dst, const Vector2 *points, int pointCount, Color color); // Draw a triangle strip defined by points within an image
+RLAPI void ImageDrawTriangleFan(Image *dst, const Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points within an image (first vertex is the center)
+RLAPI void ImageDrawTriangleStrip(Image *dst, const Vector2 *points, int pointCount, Color color); // Draw a triangle strip defined by points within an image
RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color tint); // Draw a source image within a destination image (tint applied to source)
RLAPI void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) within an image (destination)
RLAPI void ImageDrawTextEx(Image *dst, Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text (custom sprite font) within an image (destination)
@@ -1493,6 +1509,7 @@ RLAPI void DrawTextCodepoints(Font font, const int *codepoints, int codepointCou
RLAPI void SetTextLineSpacing(int spacing); // Set vertical line spacing when drawing with line-breaks
RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font
RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font
+RLAPI Vector2 MeasureTextCodepoints(Font font, const int *codepoints, int length, float fontSize, float spacing); // Measure string size for an existing array of codepoints for Font
RLAPI int GetGlyphIndex(Font font, int codepoint); // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found
RLAPI GlyphInfo GetGlyphInfo(Font font, int codepoint); // Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found
RLAPI Rectangle GetGlyphAtlasRec(Font font, int codepoint); // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found
@@ -1619,11 +1636,8 @@ RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId);
// Model animations loading/unloading functions
RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount); // Load model animations from file
-RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose (CPU)
-RLAPI void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame); // Update model animation mesh bone matrices (GPU skinning)
-RLAPI void UpdateModelAnimationBonesLerp(Model model, ModelAnimation animA, int frameA, ModelAnimation animB, int frameB, float value); // Update model animation mesh bone matrices with interpolation between two poses(GPU skinning)
-RLAPI void UpdateModelVertsToCurrentBones(Model model); // Update model vertices according to mesh bone matrices (CPU)
-RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data
+RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, float frame); // Update model animation pose (vertex buffers and bone matrices)
+RLAPI void UpdateModelAnimationEx(Model model, ModelAnimation animA, float frameA, ModelAnimation animB, float frameB, float blend); // Update model animation pose, blending two animations
RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount); // Unload animation array data
RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match
@@ -1710,7 +1724,7 @@ RLAPI bool IsAudioStreamPlaying(AudioStream stream); // Check i
RLAPI void StopAudioStream(AudioStream stream); // Stop audio stream
RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level)
RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level)
-RLAPI void SetAudioStreamPan(AudioStream stream, float pan); // Set pan for audio stream (0.5 is centered)
+RLAPI void SetAudioStreamPan(AudioStream stream, float pan); // Set pan for audio stream (-1.0 to 1.0 range, 0.0 is centered)
RLAPI void SetAudioStreamBufferSizeDefault(int size); // Default size for new audio streams
RLAPI void SetAudioStreamCallback(AudioStream stream, AudioCallback callback); // Audio thread callback to request new data
diff --git a/src/raymath.h b/src/raymath.h
index 214495a2c..8a0cce1a5 100644
--- a/src/raymath.h
+++ b/src/raymath.h
@@ -30,7 +30,7 @@
* #define RAYMATH_DISABLE_CPP_OPERATORS
* Disables C++ operator overloads for raymath types.
*
-* #define RAYMATH_USE_SIMD_INTRINSICS
+* #define RAYMATH_USE_SIMD_INTRINSICS 1
* Try to enable SIMD intrinsics for MatrixMultiply()
* Note that users enabling it must be aware of the target platform where application will
* run to support the selected SIMD intrinsic, for now, only SSE is supported
@@ -180,7 +180,7 @@ typedef struct float16 {
#include // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), floor(), fminf(), fmaxf(), fabsf()
-#if defined(RAYMATH_USE_SIMD_INTRINSICS)
+#if RAYMATH_USE_SIMD_INTRINSICS
// SIMD is used on the most costly raymath function MatrixMultiply()
// NOTE: Only SSE intrinsics support implemented
// TODO: Consider support for other SIMD instrinsics:
@@ -567,15 +567,9 @@ RMAPI Vector2 Vector2ClampValue(Vector2 v, float min, float max)
{
length = sqrtf(length);
- float scale = 1; // By default, 1 as the neutral element.
- if (length < min)
- {
- scale = min/length;
- }
- else if (length > max)
- {
- scale = max/length;
- }
+ float scale = 1; // By default, 1 as the neutral element
+ if (length < min) scale = min/length;
+ else if (length > max) scale = max/length;
result.x = v.x*scale;
result.y = v.y*scale;
@@ -1215,15 +1209,9 @@ RMAPI Vector3 Vector3ClampValue(Vector3 v, float min, float max)
{
length = sqrtf(length);
- float scale = 1; // By default, 1 as the neutral element.
- if (length < min)
- {
- scale = min/length;
- }
- else if (length > max)
- {
- scale = max/length;
- }
+ float scale = 1; // By default, 1 as the neutral element
+ if (length < min) scale = min/length;
+ else if (length > max) scale = max/length;
result.x = v.x*scale;
result.y = v.y*scale;
@@ -1523,7 +1511,7 @@ RMAPI float MatrixDeterminant(Matrix mat)
a20*a01*a12*a33 - a00*a21*a12*a33 - a10*a01*a22*a33 + a00*a11*a22*a33;
*/
// Using Laplace expansion (https://en.wikipedia.org/wiki/Laplace_expansion),
- // previous operation can be simplified to 40 multiplications, decreasing matrix
+ // previous operation can be simplified to 40 multiplications, decreasing matrix
// size from 4x4 to 2x2 using minors
// Cache the matrix values (speed optimization)
@@ -1686,20 +1674,20 @@ RMAPI Matrix MatrixSubtract(Matrix left, Matrix right)
RMAPI Matrix MatrixMultiply(Matrix left, Matrix right)
{
Matrix result = { 0 };
-
+
#if defined(RAYMATH_SSE_ENABLED)
// Load left side and right side
__m128 c0 = _mm_set_ps(right.m12, right.m8, right.m4, right.m0);
__m128 c1 = _mm_set_ps(right.m13, right.m9, right.m5, right.m1);
__m128 c2 = _mm_set_ps(right.m14, right.m10, right.m6, right.m2);
__m128 c3 = _mm_set_ps(right.m15, right.m11, right.m7, right.m3);
-
+
// Transpose so c0..c3 become *rows* of the right matrix in semantic order
_MM_TRANSPOSE4_PS(c0, c1, c2, c3);
float tmp[4] = { 0 };
__m128 row;
-
+
// Row 0 of result: [m0, m1, m2, m3]
row = _mm_mul_ps(_mm_set1_ps(left.m0), c0);
row = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m1), c1));
@@ -1765,6 +1753,18 @@ RMAPI Matrix MatrixMultiply(Matrix left, Matrix right)
return result;
}
+RMAPI Matrix MatrixMultiplyValue(Matrix left, float value)
+{
+ Matrix result = {
+ left.m0 * value, left.m4 * value, left.m8 * value, left.m12 * value,
+ left.m1 * value, left.m5 * value, left.m9 * value, left.m13 * value,
+ left.m2 * value, left.m6 * value, left.m10 * value, left.m14 * value,
+ left.m3 * value, left.m7 * value, left.m11 * value, left.m15 * value
+ };
+
+ return result;
+}
+
// Get translation matrix
RMAPI Matrix MatrixTranslate(float x, float y, float z)
{
@@ -2562,8 +2562,8 @@ RMAPI void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle
}
else
{
- // This occurs when the angle is zero.
- // Not a problem: just set an arbitrary normalized axis.
+ // This occurs when the angle is zero
+ // Not a problem, set an arbitrary normalized axis
resAxis.x = 1.0f;
}
@@ -2667,7 +2667,7 @@ RMAPI Matrix MatrixCompose(Vector3 translation, Quaternion rotation, Vector3 sca
right = Vector3RotateByQuaternion(right, rotation);
up = Vector3RotateByQuaternion(up, rotation);
forward = Vector3RotateByQuaternion(forward, rotation);
-
+
// Set result matrix output
Matrix result = {
right.x, up.x, forward.x, translation.x,
@@ -2690,10 +2690,10 @@ RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotatio
translation->y = mat.m13;
translation->z = mat.m14;
- // Matrix Columns - Rotation will be extracted into here.
- Vector3 matColumns[3] = { { mat.m0, mat.m4, mat.m8 },
+ // Matrix Columns - Rotation will be extracted into here
+ Vector3 matColumns[3] = {{ mat.m0, mat.m4, mat.m8 },
{ mat.m1, mat.m5, mat.m9 },
- { mat.m2, mat.m6, mat.m10 } };
+ { mat.m2, mat.m6, mat.m10 }};
// Shear Parameters XY, XZ, and YZ (extract and ignored)
float shear[3] = { 0 };
@@ -2708,7 +2708,7 @@ RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotatio
stabilizer = fmaxf(stabilizer, fabsf(matColumns[i].x));
stabilizer = fmaxf(stabilizer, fabsf(matColumns[i].y));
stabilizer = fmaxf(stabilizer, fabsf(matColumns[i].z));
- };
+ }
matColumns[0] = Vector3Scale(matColumns[0], 1.0f / stabilizer);
matColumns[1] = Vector3Scale(matColumns[1], 1.0f / stabilizer);
matColumns[2] = Vector3Scale(matColumns[2], 1.0f / stabilizer);
@@ -2744,7 +2744,7 @@ RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotatio
shear[2] /= scl.z; // Correct YZ shear
}
- // matColumns are now orthonormal in O(3). Now ensure its in SO(3) by enforcing det = 1.
+ // matColumns are now orthonormal in O(3). Now ensure its in SO(3) by enforcing det = 1
if (Vector3DotProduct(matColumns[0], Vector3CrossProduct(matColumns[1], matColumns[2])) < 0)
{
scl = Vector3Negate(scl);
@@ -3079,6 +3079,11 @@ inline const Quaternion& operator *= (Quaternion& lhs, const Matrix& rhs)
}
// Matrix operators
+static constexpr Matrix MatrixUnit = { 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1 };
+
inline Matrix operator + (const Matrix& lhs, const Matrix& rhs)
{
return MatrixAdd(lhs, rhs);
@@ -3111,7 +3116,19 @@ inline const Matrix& operator *= (Matrix& lhs, const Matrix& rhs)
lhs = MatrixMultiply(lhs, rhs);
return lhs;
}
-//-------------------------------------------------------------------------------
-#endif // C++ operators
-#endif // RAYMATH_H
+inline Matrix operator * (const Matrix& lhs, const float value)
+{
+ return MatrixMultiplyValue(lhs, value);
+}
+
+inline const Matrix& operator *= (Matrix& lhs, const float value)
+{
+ lhs = MatrixMultiplyValue(lhs, value);
+ return lhs;
+}
+
+//-------------------------------------------------------------------------------
+#endif // C++ operators
+
+#endif // RAYMATH_H
diff --git a/src/rcamera.h b/src/rcamera.h
index 3c880bab7..14616a771 100644
--- a/src/rcamera.h
+++ b/src/rcamera.h
@@ -54,9 +54,9 @@
#if defined(__TINYC__)
#define __declspec(x) __attribute__((x))
#endif
- #define RLAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll)
+ #define RLAPI __declspec(dllexport) // Building the library as a Win32 shared library (.dll)
#elif defined(USE_LIBTYPE_SHARED)
- #define RLAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll)
+ #define RLAPI __declspec(dllimport) // Using the library as a Win32 shared library (.dll)
#endif
#endif
@@ -191,7 +191,7 @@ RLAPI Matrix GetCameraProjectionMatrix(Camera *camera, float aspect);
// IsKeyDown()
// IsKeyPressed()
// GetFrameTime()
-
+
#include // Required for: fabsf()
//----------------------------------------------------------------------------------
@@ -364,8 +364,8 @@ void CameraPitch(Camera *camera, float angle, bool lockView, bool rotateAroundTa
if (lockView)
{
- // In these camera modes we clamp the Pitch angle
- // to allow only viewing straight up or down.
+ // In these camera modes, clamp the Pitch angle
+ // to allow only viewing straight up or down
// Clamp view up
float maxAngleUp = Vector3Angle(up, targetPosition);
@@ -460,7 +460,6 @@ void UpdateCamera(Camera *camera, int mode)
if (mode == CAMERA_CUSTOM) {}
else if (mode == CAMERA_ORBITAL)
{
- // Orbital can just orbit
Matrix rotation = MatrixRotate(GetCameraUp(camera), cameraOrbitalSpeed);
Vector3 view = Vector3Subtract(camera->position, camera->target);
view = Vector3Transform(view, rotation);
diff --git a/src/rcore.c b/src/rcore.c
index c29fb1e19..de1bb371c 100644
--- a/src/rcore.c
+++ b/src/rcore.c
@@ -35,10 +35,6 @@
* - Memory framebuffer output, using software renderer, no OS required
*
* CONFIGURATION:
-* #define SUPPORT_DEFAULT_FONT (default)
-* Default font is loaded on window initialization to be available for the user to render simple text
-* NOTE: If enabled, uses external module functions to load default raylib font (module: text)
-*
* #define SUPPORT_CAMERA_SYSTEM
* Camera module is included (rcamera.h) and multiple predefined cameras are available:
* free, 1st/3rd person, orbital, custom
@@ -126,17 +122,17 @@
#define RAYMATH_IMPLEMENTATION
#include "raymath.h" // Vector2, Vector3, Quaternion and Matrix functionality
-#if defined(SUPPORT_GESTURES_SYSTEM)
+#if SUPPORT_GESTURES_SYSTEM
#define RGESTURES_IMPLEMENTATION
#include "rgestures.h" // Gestures detection functionality
#endif
-#if defined(SUPPORT_CAMERA_SYSTEM)
+#if SUPPORT_CAMERA_SYSTEM
#define RCAMERA_IMPLEMENTATION
#include "rcamera.h" // Camera system functionality
#endif
-#if defined(SUPPORT_COMPRESSION_API)
+#if SUPPORT_COMPRESSION_API
#define SINFL_IMPLEMENTATION
#define SINFL_NO_SIMD
#include "external/sinfl.h" // Deflate (RFC 1951) decompressor
@@ -145,7 +141,7 @@
#include "external/sdefl.h" // Deflate (RFC 1951) compressor
#endif
-#if defined(SUPPORT_RPRAND_GENERATOR)
+#if SUPPORT_RPRAND_GENERATOR
#define RPRAND_IMPLEMENTATION
#include "external/rprand.h"
#endif
@@ -181,7 +177,7 @@
#elif defined(__APPLE__)
#include
#include
-#endif // OSs
+#endif
#define _CRT_INTERNAL_NONSTDC_NAMES 1
#include // Required for: stat(), S_ISREG [Used in GetFileModTime(), IsFilePath()]
@@ -407,11 +403,11 @@ static SaveFileDataCallback saveFileData = NULL; // SaveFileText callback fun
static LoadFileTextCallback loadFileText = NULL; // LoadFileText callback function pointer
static SaveFileTextCallback saveFileText = NULL; // SaveFileText callback function pointer
-#if defined(SUPPORT_SCREEN_CAPTURE)
+#if SUPPORT_SCREEN_CAPTURE
static int screenshotCounter = 0; // Screenshots counter
#endif
-#if defined(SUPPORT_AUTOMATION_EVENTS)
+#if SUPPORT_AUTOMATION_EVENTS
// Automation events type
typedef enum AutomationEventType {
EVENT_NONE = 0,
@@ -503,8 +499,7 @@ static bool automationEventRecording = false; // Recording automat
// Module Functions Declaration
// NOTE: Those functions are common for all platforms!
//----------------------------------------------------------------------------------
-
-#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
+#if SUPPORT_MODULE_RTEXT
extern void LoadFontDefault(void); // [Module: text] Loads default font on InitWindow()
extern void UnloadFontDefault(void); // [Module: text] Unloads default font from GPU memory
#endif
@@ -517,39 +512,23 @@ static void SetupViewport(int width, int height); // Set viewport for
static void ScanDirectoryFiles(const char *basePath, FilePathList *list, const char *filter, unsigned int expectedFileCount, bool scanSubdirs); // Scan all files and directories in a base path
-#if defined(SUPPORT_AUTOMATION_EVENTS)
+#if SUPPORT_AUTOMATION_EVENTS
static void RecordAutomationEvent(void); // Record frame events (to internal events array)
#endif
#if defined(_WIN32) && !defined(PLATFORM_DESKTOP_RGFW)
-// NOTE: We declare Sleep() function symbol to avoid including windows.h (kernel32.lib linkage required)
+// NOTE: Declaring Sleep() function symbol to avoid including windows.h (kernel32.lib linkage required)
__declspec(dllimport) void __stdcall Sleep(unsigned long msTimeout); // Required for: WaitTime()
#endif
-#if !defined(SUPPORT_MODULE_RTEXT)
+#if !SUPPORT_MODULE_RTEXT
const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed'
-#endif // !SUPPORT_MODULE_RTEXT
+#endif
#if defined(PLATFORM_DESKTOP)
#define PLATFORM_DESKTOP_GLFW
#endif
-// Using '#pragma message' because '#warning' is not adopted by MSVC
-#if defined(SUPPORT_CLIPBOARD_IMAGE)
- #if !defined(SUPPORT_MODULE_RTEXTURES)
- #pragma message ("WARNING: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly")
- #endif
-
- // It's nice to have support Bitmap on Linux as well, but not as necessary as Windows
- #if !defined(SUPPORT_FILEFORMAT_BMP) && defined(_WIN32)
- #pragma message ("WARNING: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_FILEFORMAT_BMP, specially on Windows")
- #endif
-
- // From what I've tested applications on Wayland saves images on clipboard as PNG
- #if (!defined(SUPPORT_FILEFORMAT_PNG) || !defined(SUPPORT_FILEFORMAT_JPG)) && !defined(_WIN32)
- #pragma message ("WARNING: Getting image from the clipboard might not work without SUPPORT_FILEFORMAT_PNG or SUPPORT_FILEFORMAT_JPG")
- #endif
-#endif // SUPPORT_CLIPBOARD_IMAGE
// Include platform-specific submodules
#if defined(PLATFORM_DESKTOP_GLFW)
@@ -653,27 +632,27 @@ void InitWindow(int width, int height, const char *title)
TRACELOG(LOG_INFO, "Supported raylib modules:");
TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)");
TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)");
-#if defined(SUPPORT_MODULE_RSHAPES)
+#if SUPPORT_MODULE_RSHAPES
TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)");
#else
TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)");
#endif
-#if defined(SUPPORT_MODULE_RTEXTURES)
+#if SUPPORT_MODULE_RTEXTURES
TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)");
#else
TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)");
#endif
-#if defined(SUPPORT_MODULE_RTEXT)
+#if SUPPORT_MODULE_RTEXT
TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)");
#else
TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)");
#endif
-#if defined(SUPPORT_MODULE_RMODELS)
+#if SUPPORT_MODULE_RMODELS
TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)");
#else
TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)");
#endif
-#if defined(SUPPORT_MODULE_RAUDIO)
+#if SUPPORT_MODULE_RAUDIO
TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)");
#else
TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)");
@@ -712,29 +691,27 @@ void InitWindow(int width, int height, const char *title)
// Setup default viewport
SetupViewport(CORE.Window.render.width, CORE.Window.render.height);
-#if defined(SUPPORT_MODULE_RTEXT)
- #if defined(SUPPORT_DEFAULT_FONT)
- // Load default font
- // WARNING: External function: Module required: rtext
- LoadFontDefault();
- #if defined(SUPPORT_MODULE_RSHAPES)
- // Set font white rectangle for shapes drawing, so shapes and text can be batched together
- // WARNING: rshapes module is required, if not available, default internal white rectangle is used
- Rectangle rec = GetFontDefault().recs[95];
- if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
- {
- // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering
- SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 });
- }
- else
- {
- // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding
- SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 });
- }
- #endif
+#if SUPPORT_MODULE_RTEXT
+ // Load default font
+ // WARNING: External function: Module required: rtext
+ LoadFontDefault();
+ #if SUPPORT_MODULE_RSHAPES
+ // Set font white rectangle for shapes drawing, so shapes and text can be batched together
+ // WARNING: rshapes module is required, if not available, default internal white rectangle is used
+ Rectangle rec = GetFontDefault().recs[95];
+ if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
+ {
+ // NOTE: Try to maxime rec padding to avoid pixel bleeding on MSAA filtering
+ SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 });
+ }
+ else
+ {
+ // NOTE: Set up a 1px padding on char rectangle to avoid pixel bleeding
+ SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 });
+ }
#endif
#else
- #if defined(SUPPORT_MODULE_RSHAPES)
+ #if SUPPORT_MODULE_RSHAPES
// Set default texture and rectangle to be used for shapes drawing
// NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
@@ -754,7 +731,7 @@ void InitWindow(int width, int height, const char *title)
// Close window and unload OpenGL context
void CloseWindow(void)
{
-#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
+#if SUPPORT_MODULE_RTEXT
UnloadFontDefault(); // WARNING: Module required: rtext
#endif
@@ -908,11 +885,11 @@ void EndDrawing(void)
{
rlDrawRenderBatchActive(); // Update and draw internal render batch
-#if defined(SUPPORT_AUTOMATION_EVENTS)
+#if SUPPORT_AUTOMATION_EVENTS
if (automationEventRecording) RecordAutomationEvent(); // Event recording
#endif
-#if !defined(SUPPORT_CUSTOM_FRAME_CONTROL)
+#if !SUPPORT_CUSTOM_FRAME_CONTROL
SwapScreenBuffer(); // Copy back buffer to front buffer (screen)
// Frame time control system
@@ -937,13 +914,13 @@ void EndDrawing(void)
PollInputEvents(); // Poll user events (before next frame update)
#endif
-#if defined(SUPPORT_SCREEN_CAPTURE)
+#if SUPPORT_SCREEN_CAPTURE
if (IsKeyPressed(KEY_F12))
{
TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
screenshotCounter++;
}
-#endif // SUPPORT_SCREEN_CAPTURE
+#endif
CORE.Time.frameCounter++;
}
@@ -1065,7 +1042,7 @@ void EndTextureMode(void)
// Set viewport to default framebuffer size
SetupViewport(CORE.Window.render.width, CORE.Window.render.height);
- // Go back to the modelview state from BeginDrawing since we are back to the default FBO
+ // Go back to the modelview state from BeginDrawing, back to the main framebuffer
rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
rlLoadIdentity(); // Reset current matrix (modelview)
rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
@@ -1102,7 +1079,7 @@ void EndBlendMode(void)
}
// Begin scissor mode (define screen area for following drawing)
-// NOTE: Scissor rec refers to bottom-left corner, we change it to upper-left
+// NOTE: Scissor rec refers to bottom-left corner, changing it to upper-left
void BeginScissorMode(int x, int y, int width, int height)
{
rlDrawRenderBatchActive(); // Update and draw internal render batch
@@ -1205,9 +1182,9 @@ VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device)
config.projection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f));
// Compute camera transformation matrices
- // NOTE: Camera movement might seem more natural if we model the head
- // Our axis of rotation is the base of our head, so we might want to add
- // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions
+ // NOTE: Camera movement might seem more natural if modelling the head
+ // Axis of rotation is the base of the head, so adding some y (base of head to eye level
+ // and -z (center of head to eye protrusion) to the camera positions
config.viewOffset[0] = MatrixTranslate(device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
config.viewOffset[1] = MatrixTranslate(-device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
@@ -1266,19 +1243,19 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
{
Shader shader = { 0 };
- shader.id = rlLoadShaderCode(vsCode, fsCode);
+ shader.id = rlLoadShaderProgram(vsCode, fsCode);
if (shader.id == 0)
{
- // Shader could not be loaded but we still load the location points to avoid potential crashes
- // NOTE: All locations set to -1 (no location)
+ // Shader could not be loaded but still loading the location points to avoid potential crashes
+ // NOTE: All locations set to -1 (no location found)
shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int));
for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1;
}
else if (shader.id == rlGetShaderIdDefault()) shader.locs = rlGetShaderLocsDefault();
else if (shader.id > 0)
{
- // After custom shader loading, we TRY to set default location names
+ // After custom shader loading, trying to set default location names
// Default shader attribute locations have been binded before linking:
// - vertex position location = 0
// - vertex texcoord location = 1
@@ -1286,7 +1263,7 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
// - vertex color location = 3
// - vertex tangent location = 4
// - vertex texcoord2 location = 5
- // - vertex boneIds location = 6
+ // - vertex boneIndices location = 6
// - vertex boneWeights location = 7
// NOTE: If any location is not found, loc point becomes -1
@@ -1303,9 +1280,9 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
shader.locs[SHADER_LOC_VERTEX_NORMAL] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL);
shader.locs[SHADER_LOC_VERTEX_TANGENT] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT);
shader.locs[SHADER_LOC_VERTEX_COLOR] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
- shader.locs[SHADER_LOC_VERTEX_BONEIDS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS);
+ shader.locs[SHADER_LOC_VERTEX_BONEIDS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES);
shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS);
- shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX);
+ shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORM] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORM);
// Get handles to GLSL uniform locations (vertex shader)
shader.locs[SHADER_LOC_MATRIX_MVP] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MVP);
@@ -1313,7 +1290,7 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
shader.locs[SHADER_LOC_MATRIX_PROJECTION] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION);
shader.locs[SHADER_LOC_MATRIX_MODEL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL);
shader.locs[SHADER_LOC_MATRIX_NORMAL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL);
- shader.locs[SHADER_LOC_BONE_MATRICES] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES);
+ shader.locs[SHADER_LOC_MATRIX_BONETRANSFORMS] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES);
// Get handles to GLSL uniform locations (fragment shader)
shader.locs[SHADER_LOC_COLOR_DIFFUSE] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR);
@@ -1471,7 +1448,7 @@ Ray GetScreenToWorldRayEx(Vector2 position, Camera camera, int width, int height
Vector3 farPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView);
// Unproject the mouse cursor in the near plane
- // We need this as the source position because orthographic projects,
+ // It is needed as the source position because orthographic projects,
// compared to perspective doesn't have a convergence point,
// meaning that the "eye" of the camera is more like a plane than a point
Vector3 cameraPlanePointerPos = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, -1.0f }, matProj, matView);
@@ -1507,7 +1484,7 @@ Matrix GetCameraMatrix2D(Camera2D camera)
// not for the camera getting bigger, hence the invert. Same deal with rotation
// 3. Move it by (-offset);
// Offset defines target transform relative to screen, but since effectively "moving" screen (camera)
- // we need to do it into opposite direction (inverse transform)
+ // it needs to be moved into opposite direction (inverse transform)
// Having camera transform in world-space, inverse of it gives the modelview transform
// Since (A*B*C)' = C'*B'*A', the modelview is
@@ -1593,7 +1570,7 @@ Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera)
}
//----------------------------------------------------------------------------------
-// Module Functions Definition: Timming
+// Module Functions Definition: Timing
//----------------------------------------------------------------------------------
// NOTE: Functions with a platform-specific implementation on rcore_.c
@@ -1609,12 +1586,12 @@ void SetTargetFPS(int fps)
}
// Get current FPS
-// NOTE: We calculate an average framerate
+// NOTE: Calculating an average framerate
int GetFPS(void)
{
int fps = 0;
-#if !defined(SUPPORT_CUSTOM_FRAME_CONTROL)
+#if !SUPPORT_CUSTOM_FRAME_CONTROL
#define FPS_CAPTURE_FRAMES_COUNT 30 // 30 captures
#define FPS_AVERAGE_TIME_SECONDS 0.5f // 500 milliseconds
#define FPS_STEP (FPS_AVERAGE_TIME_SECONDS/FPS_CAPTURE_FRAMES_COUNT)
@@ -1624,7 +1601,7 @@ int GetFPS(void)
static float average = 0, last = 0;
float fpsFrame = GetFrameTime();
- // if we reset the window, reset the FPS info
+ // If reseting the window, reset the FPS info
if (CORE.Time.frameCounter == 0)
{
average = 0;
@@ -1667,22 +1644,22 @@ float GetFrameTime(void)
// Wait for some time (stop program execution)
// NOTE: Sleep() granularity could be around 10 ms, it means, Sleep() could
-// take longer than expected... for that reason we use the busy wait loop
+// take longer than expected... for that reason a busy wait loop is used
// REF: http://stackoverflow.com/questions/43057578/c-programming-win32-games-sleep-taking-longer-than-expected
// REF: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timing on Win32!
void WaitTime(double seconds)
{
if (seconds < 0) return; // Security check
-#if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
+#if SUPPORT_BUSY_WAIT_LOOP || SUPPORT_PARTIALBUSY_WAIT_LOOP
double destinationTime = GetTime() + seconds;
#endif
-#if defined(SUPPORT_BUSY_WAIT_LOOP)
+#if SUPPORT_BUSY_WAIT_LOOP
while (GetTime() < destinationTime) { }
#else
- #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
- double sleepSeconds = seconds - seconds*0.05; // NOTE: We reserve a percentage of the time for busy waiting
+ #if SUPPORT_PARTIALBUSY_WAIT_LOOP
+ double sleepSeconds = seconds - seconds*0.05; // NOTE: Reserve a percentage of the time for busy waiting
#else
double sleepSeconds = seconds;
#endif
@@ -1705,7 +1682,7 @@ void WaitTime(double seconds)
usleep(sleepSeconds*1000000.0);
#endif
- #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
+ #if SUPPORT_PARTIALBUSY_WAIT_LOOP
while (GetTime() < destinationTime) { }
#endif
#endif
@@ -1721,7 +1698,7 @@ void WaitTime(double seconds)
// Set the seed for the random number generator
void SetRandomSeed(unsigned int seed)
{
-#if defined(SUPPORT_RPRAND_GENERATOR)
+#if SUPPORT_RPRAND_GENERATOR
rprand_set_seed(seed);
#else
srand(seed);
@@ -1740,7 +1717,7 @@ int GetRandomValue(int min, int max)
min = tmp;
}
-#if defined(SUPPORT_RPRAND_GENERATOR)
+#if SUPPORT_RPRAND_GENERATOR
value = rprand_get_value(min, max);
#else
// WARNING: Ranges higher than RAND_MAX will return invalid results
@@ -1779,6 +1756,7 @@ int GetRandomValue(int min, int max)
value = min + (int)(r%m);
}
#endif
+
return value;
}
@@ -1787,7 +1765,7 @@ int *LoadRandomSequence(unsigned int count, int min, int max)
{
int *values = NULL;
-#if defined(SUPPORT_RPRAND_GENERATOR)
+#if SUPPORT_RPRAND_GENERATOR
values = rprand_load_sequence(count, min, max);
#else
if (count > ((unsigned int)abs(max - min) + 1)) return values; // Security check
@@ -1818,13 +1796,14 @@ int *LoadRandomSequence(unsigned int count, int min, int max)
}
}
#endif
+
return values;
}
// Unload random values sequence
void UnloadRandomSequence(int *sequence)
{
-#if defined(SUPPORT_RPRAND_GENERATOR)
+#if SUPPORT_RPRAND_GENERATOR
rprand_unload_sequence(sequence);
#else
RL_FREE(sequence);
@@ -1835,11 +1814,11 @@ void UnloadRandomSequence(int *sequence)
// NOTE: Provided fileName should not contain paths, saving to working directory
void TakeScreenshot(const char *fileName)
{
-#if defined(SUPPORT_MODULE_RTEXTURES)
+#if SUPPORT_MODULE_RTEXTURES
// Security check to (partially) avoid malicious code
if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; }
- // Apply a scale if we are doing HIGHDPI auto-scaling
+ // Apply content scaling if required
Vector2 scale = { 1.0f, 1.0f };
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) scale = GetWindowScaleDPI();
@@ -1862,7 +1841,7 @@ void TakeScreenshot(const char *fileName)
// Setup window configuration flags (view FLAGS)
// NOTE: This function is expected to be called before window creation,
// because it sets up some flags for the window creation process
-// To configure window states after creation, just use SetWindowState()
+// To configure window states after creation, use SetWindowState()
void SetConfigFlags(unsigned int flags)
{
if (CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetConfigFlags called after window initialization, Use \"SetWindowState\" to set flags instead");
@@ -1883,7 +1862,7 @@ void SetTraceLogLevel(int logType) { logTypeLevel = logType; }
// Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG)
void TraceLog(int logType, const char *text, ...)
{
-#if defined(SUPPORT_TRACELOG)
+#if SUPPORT_TRACELOG
// Message has level below current threshold, don't emit
if ((logType < logTypeLevel) || (text == NULL)) return;
@@ -1932,8 +1911,7 @@ void TraceLog(int logType, const char *text, ...)
va_end(args);
if (logType == LOG_FATAL) exit(EXIT_FAILURE); // If fatal logging, exit program
-
-#endif // SUPPORT_TRACELOG
+#endif
}
// Set custom trace log
@@ -1977,12 +1955,8 @@ unsigned char *LoadFileData(const char *fileName, int *dataSize)
if (fileName != NULL)
{
- if (loadFileData)
- {
- data = loadFileData(fileName, dataSize);
- return data;
- }
-#if defined(SUPPORT_STANDARD_FILEIO)
+ if (loadFileData) return loadFileData(fileName, dataSize);
+
FILE *file = fopen(fileName, "rb");
if (file != NULL)
@@ -1999,11 +1973,11 @@ unsigned char *LoadFileData(const char *fileName, int *dataSize)
if (data != NULL)
{
- // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements]
+ // NOTE: fread() returns number of read elements instead of bytes, so reading [1 byte, size elements]
size_t count = fread(data, sizeof(unsigned char), size, file);
// WARNING: fread() returns a size_t value, usually 'unsigned int' (32bit compilation) and 'unsigned long long' (64bit compilation)
- // dataSize is unified along raylib as a 'int' type, so, for file-sizes > INT_MAX (2147483647 bytes) we have a limitation
+ // dataSize is unified along raylib as a 'int' type, so, for file-sizes > INT_MAX (2147483647 bytes) there is a limitation
if (count > 2147483647)
{
TRACELOG(LOG_WARNING, "FILEIO: [%s] File is bigger than 2147483647 bytes, avoid using LoadFileData()", fileName);
@@ -2026,9 +2000,6 @@ unsigned char *LoadFileData(const char *fileName, int *dataSize)
fclose(file);
}
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName);
-#else
- TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
-#endif
}
else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
@@ -2048,11 +2019,8 @@ bool SaveFileData(const char *fileName, void *data, int dataSize)
if (fileName != NULL)
{
- if (saveFileData)
- {
- return saveFileData(fileName, data, dataSize);
- }
-#if defined(SUPPORT_STANDARD_FILEIO)
+ if (saveFileData) return saveFileData(fileName, data, dataSize);
+
FILE *file = fopen(fileName, "wb");
if (file != NULL)
@@ -2069,9 +2037,6 @@ bool SaveFileData(const char *fileName, void *data, int dataSize)
if (result == 0) success = true;
}
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName);
-#else
- TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
-#endif
}
else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
@@ -2139,12 +2104,8 @@ char *LoadFileText(const char *fileName)
if (fileName != NULL)
{
- if (loadFileText)
- {
- text = loadFileText(fileName);
- return text;
- }
-#if defined(SUPPORT_STANDARD_FILEIO)
+ if (loadFileText) return loadFileText(fileName);
+
FILE *file = fopen(fileName, "rt");
if (file != NULL)
@@ -2180,9 +2141,6 @@ char *LoadFileText(const char *fileName)
fclose(file);
}
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName);
-#else
- TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
-#endif
}
else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
@@ -2202,11 +2160,8 @@ bool SaveFileText(const char *fileName, const char *text)
if (fileName != NULL)
{
- if (saveFileText)
- {
- return saveFileText(fileName, text);
- }
-#if defined(SUPPORT_STANDARD_FILEIO)
+ if (saveFileText) return saveFileText(fileName, text);
+
FILE *file = fopen(fileName, "wt");
if (file != NULL)
@@ -2220,9 +2175,6 @@ bool SaveFileText(const char *fileName, const char *text)
if (result == 0) success = true;
}
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName);
-#else
- TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
-#endif
}
else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
@@ -2332,7 +2284,7 @@ int FileTextReplace(const char *fileName, const char *search, const char *replac
char *fileText = NULL;
char *fileTextUpdated = { 0 };
-#if defined(SUPPORT_MODULE_RTEXT)
+#if SUPPORT_MODULE_RTEXT
if (FileExists(fileName))
{
fileText = LoadFileText(fileName);
@@ -2504,7 +2456,7 @@ long GetFileModTime(const char *fileName)
}
// Get pointer to extension for a filename string (includes the dot: .png)
-// WARNING: We just get the ptr but not the extension as a separate string
+// WARNING: Getting the pointer to the input string extension position (not a string copy)
const char *GetFileExtension(const char *fileName)
{
const char *dot = strrchr(fileName, '.');
@@ -2553,7 +2505,7 @@ const char *GetFileNameWithoutExt(const char *filePath)
{
if (fileName[i] == '.')
{
- // NOTE: We break on first '.' found
+ // NOTE: Break on first '.' found
fileName[i] = '\0';
break;
}
@@ -2579,11 +2531,11 @@ const char *GetDirectoryPath(const char *filePath)
static char dirPath[MAX_FILEPATH_LENGTH] = { 0 };
memset(dirPath, 0, MAX_FILEPATH_LENGTH);
- // In case provided path does not contain a root drive letter (C:\, D:\) nor leading path separator (\, /),
- // we add the current directory path to dirPath
+ // In case provided path does not contain a root drive letter (C:\, D:\)
+ // nor leading path separator (\, /), add the current directory path to dirPath
if ((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/'))
{
- // For security, we set starting path to current directory,
+ // For security, set starting path to current directory,
// obtained path will be concatenated to this
dirPath[0] = '.';
dirPath[1] = '/';
@@ -2982,7 +2934,7 @@ unsigned int GetDirectoryFileCountEx(const char *basePath, const char *filter, b
{
while ((entity = readdir(dir)) != NULL)
{
- // NOTE: We skip '.' (current dir) and '..' (parent dir) filepaths
+ // NOTE: Skipping '.' (current dir) and '..' (parent dir) filepaths
if ((strcmp(entity->d_name, ".") != 0) && (strcmp(entity->d_name, "..") != 0))
{
// Construct new path from our base path
@@ -3025,7 +2977,7 @@ unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDa
unsigned char *compData = NULL;
-#if defined(SUPPORT_COMPRESSION_API)
+#if SUPPORT_COMPRESSION_API
// Compress data and generate a valid DEFLATE stream
struct sdefl *sdefl = (struct sdefl *)RL_CALLOC(1, sizeof(struct sdefl)); // WARNING: Possible stack overflow, struct sdefl is almost 1MB
int bounds = sdefl_bound(dataSize);
@@ -3045,14 +2997,14 @@ unsigned char *DecompressData(const unsigned char *compData, int compDataSize, i
{
unsigned char *data = NULL;
-#if defined(SUPPORT_COMPRESSION_API)
+#if SUPPORT_COMPRESSION_API
// Decompress data from a valid DEFLATE stream
unsigned char *data0 = (unsigned char *)RL_CALLOC(MAX_DECOMPRESSION_SIZE*1024*1024, 1);
int size = sinflate(data0, MAX_DECOMPRESSION_SIZE*1024*1024, compData, compDataSize);
// WARNING: RL_REALLOC can make (and leave) data copies in memory,
// that can be a security concern in case of compression of sensitive data
- // So, we use a second buffer to copy data manually, wiping original buffer memory
+ // So, using a second buffer to copy data manually, wiping original buffer memory
data = (unsigned char *)RL_CALLOC(size, 1);
memcpy(data, data0, size);
memset(data0, 0, MAX_DECOMPRESSION_SIZE*1024*1024); // Wipe memory, is memset() safe?
@@ -3578,11 +3530,11 @@ AutomationEventList LoadAutomationEventList(const char *fileName)
{
AutomationEventList list = { 0 };
+#if SUPPORT_AUTOMATION_EVENTS
// Allocate and empty automation event list, ready to record new events
list.events = (AutomationEvent *)RL_CALLOC(MAX_AUTOMATION_EVENTS, sizeof(AutomationEvent));
list.capacity = MAX_AUTOMATION_EVENTS;
-#if defined(SUPPORT_AUTOMATION_EVENTS)
if (fileName == NULL) TRACELOG(LOG_INFO, "AUTOMATION: New empty events list loaded successfully");
else
{
@@ -3658,7 +3610,7 @@ AutomationEventList LoadAutomationEventList(const char *fileName)
// Unload automation events list from file
void UnloadAutomationEventList(AutomationEventList list)
{
-#if defined(SUPPORT_AUTOMATION_EVENTS)
+#if SUPPORT_AUTOMATION_EVENTS
RL_FREE(list.events);
#endif
}
@@ -3668,7 +3620,7 @@ bool ExportAutomationEventList(AutomationEventList list, const char *fileName)
{
bool success = false;
-#if defined(SUPPORT_AUTOMATION_EVENTS)
+#if SUPPORT_AUTOMATION_EVENTS
// Export events as binary file
// NOTE: Code not used, only for reference if required in the future
/*
@@ -3726,7 +3678,7 @@ bool ExportAutomationEventList(AutomationEventList list, const char *fileName)
// Setup automation event list to record to
void SetAutomationEventList(AutomationEventList *list)
{
-#if defined(SUPPORT_AUTOMATION_EVENTS)
+#if SUPPORT_AUTOMATION_EVENTS
currentEventList = list;
#endif
}
@@ -3740,7 +3692,7 @@ void SetAutomationEventBaseFrame(int frame)
// Start recording automation events (AutomationEventList must be set)
void StartAutomationEventRecording(void)
{
-#if defined(SUPPORT_AUTOMATION_EVENTS)
+#if SUPPORT_AUTOMATION_EVENTS
automationEventRecording = true;
#endif
}
@@ -3748,7 +3700,7 @@ void StartAutomationEventRecording(void)
// Stop recording automation events
void StopAutomationEventRecording(void)
{
-#if defined(SUPPORT_AUTOMATION_EVENTS)
+#if SUPPORT_AUTOMATION_EVENTS
automationEventRecording = false;
#endif
}
@@ -3756,7 +3708,7 @@ void StopAutomationEventRecording(void)
// Play a recorded automation event
void PlayAutomationEvent(AutomationEvent event)
{
-#if defined(SUPPORT_AUTOMATION_EVENTS)
+#if SUPPORT_AUTOMATION_EVENTS
// WARNING: When should event be played? After/before/replace PollInputEvents()? -> Up to the user!
if (!automationEventRecording)
@@ -3805,7 +3757,7 @@ void PlayAutomationEvent(AutomationEvent event)
{
CORE.Input.Gamepad.axisState[event.params[0]][event.params[1]] = ((float)event.params[2]/32768.0f);
} break;
- #if defined(SUPPORT_GESTURES_SYSTEM)
+ #if SUPPORT_GESTURES_SYSTEM
case INPUT_GESTURE: GESTURES.current = event.params[0]; break; // param[0]: gesture (enum Gesture) -> rgestures.h: GESTURES.current
#endif
// Window event
@@ -3813,9 +3765,8 @@ void PlayAutomationEvent(AutomationEvent event)
case WINDOW_MAXIMIZE: MaximizeWindow(); break;
case WINDOW_MINIMIZE: MinimizeWindow(); break;
case WINDOW_RESIZE: SetWindowSize(event.params[0], event.params[1]); break;
-
// Custom event
- #if defined(SUPPORT_SCREEN_CAPTURE)
+ #if SUPPORT_SCREEN_CAPTURE
case ACTION_TAKE_SCREENSHOT:
{
TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
@@ -4259,7 +4210,7 @@ void InitTimer(void)
// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often
// High resolutions can also prevent the CPU power management system from entering power-saving modes
// Setting a higher resolution does not improve the accuracy of the high-resolution performance counter
-#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) && !defined(PLATFORM_DESKTOP_SDL)
+#if defined(_WIN32) && SUPPORT_WINMM_HIGHRES_TIMER && !SUPPORT_BUSY_WAIT_LOOP && !defined(PLATFORM_DESKTOP_SDL)
timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms)
#endif
@@ -4352,7 +4303,7 @@ static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const
else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath); // Maybe it's a file...
}
-#if defined(SUPPORT_AUTOMATION_EVENTS)
+#if SUPPORT_AUTOMATION_EVENTS
// Automation event recording
// Checking events in current frame and save them into currentEventList
// NOTE: Recording is by default done at EndDrawing(), before PollInputEvents()
@@ -4593,7 +4544,7 @@ static void RecordAutomationEvent(void)
}
//-------------------------------------------------------------------------------------
-#if defined(SUPPORT_GESTURES_SYSTEM)
+#if SUPPORT_GESTURES_SYSTEM
// Gestures input currentEventList->events recording
//-------------------------------------------------------------------------------------
if (GESTURES.current != GESTURE_NONE)
@@ -4615,7 +4566,7 @@ static void RecordAutomationEvent(void)
}
#endif
-#if !defined(SUPPORT_MODULE_RTEXT)
+#if !SUPPORT_MODULE_RTEXT
// Formatting of text with variables to 'embed'
// WARNING: String returned will expire after this function is called MAX_TEXTFORMAT_BUFFERS times
const char *TextFormat(const char *text, ...)
@@ -4627,7 +4578,7 @@ const char *TextFormat(const char *text, ...)
#define MAX_TEXT_BUFFER_LENGTH 1024 // Maximum size of static text buffer
#endif
- // We create an array of buffers so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations
+ // Define an array of buffers, so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations
static char buffers[MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH] = { 0 };
static int index = 0;
@@ -4655,5 +4606,4 @@ const char *TextFormat(const char *text, ...)
return currentBuffer;
}
-
-#endif // !SUPPORT_MODULE_RTEXT
+#endif
diff --git a/src/rlgl.h b/src/rlgl.h
index 0cde3e6eb..b40b50b43 100644
--- a/src/rlgl.h
+++ b/src/rlgl.h
@@ -37,13 +37,13 @@
* If not defined, the library is in header only mode and can be included in other headers
* or source files without problems. But only ONE file should hold the implementation
*
-* #define RLGL_SHOW_GL_DETAILS_INFO
+* #if RLGL_SHOW_GL_DETAILS_INFO
* Show OpenGL extensions and capabilities detailed logs on init
*
-* #define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT
+* #if RLGL_ENABLE_OPENGL_DEBUG_CONTEXT
* Enable debug context (only available on OpenGL 4.3)
*
-* rlgl capabilities could be customized just defining some internal
+* rlgl capabilities could be customized defining some internal
* values before library inclusion (default values listed):
*
* #define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 8192 // Default internal render batch elements limits
@@ -65,7 +65,7 @@
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2
-* #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS "vertexBoneIds" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS
+* #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES "vertexBoneIndices" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES
* #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix
@@ -73,7 +73,7 @@
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView)))
* #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color)
-* #define RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES "boneMatrices" // bone matrices
+* #define RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES "boneMatrices" // bone matrices
* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0)
* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1)
* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2)
@@ -345,16 +345,14 @@
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES 6
#endif
-#ifdef RL_SUPPORT_MESH_GPU_SKINNING
-#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS
- #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS 7
+#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES
+ #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES 7
#endif
#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS
#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8
#endif
-#endif
-#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCE_TX
- #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCE_TX 9
+#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORM
+ #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORM 9
#endif
//----------------------------------------------------------------------------------
@@ -398,7 +396,7 @@ typedef struct rlVertexBuffer {
// Draw call type
// NOTE: Only texture changes register a new draw, other state-change-related elements are not
-// used at this moment (vaoId, shaderId, matrices), raylib just forces a batch draw call if any
+// used at this moment (vaoId, shaderId, matrices), raylib forces a batch draw call if any
// of those state-change happens (this is done in core module)
typedef struct rlDrawCall {
int mode; // Drawing mode: LINES, TRIANGLES, QUADS
@@ -480,7 +478,7 @@ typedef enum {
// NOTE 1: Filtering considers mipmaps if available in the texture
// NOTE 2: Filter is accordingly set for minification and magnification
typedef enum {
- RL_TEXTURE_FILTER_POINT = 0, // No filter, just pixel approximation
+ RL_TEXTURE_FILTER_POINT = 0, // No filter, pixel approximation
RL_TEXTURE_FILTER_BILINEAR, // Linear filtering
RL_TEXTURE_FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps)
RL_TEXTURE_FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x
@@ -764,7 +762,7 @@ RLAPI unsigned char *rlReadScreenPixels(int width, int height); // Rea
// Framebuffer management (fbo)
RLAPI unsigned int rlLoadFramebuffer(void); // Load an empty framebuffer
-RLAPI void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType, int mipLevel); // Attach texture/renderbuffer to a framebuffer
+RLAPI void rlFramebufferAttach(unsigned int id, unsigned int texId, int attachType, int texType, int mipLevel); // Attach texture/renderbuffer to a framebuffer
RLAPI bool rlFramebufferComplete(unsigned int id); // Verify framebuffer is complete
RLAPI void rlUnloadFramebuffer(unsigned int id); // Delete framebuffer from GPU
// WARNING: Copy and resize framebuffer functionality only defined for software backend
@@ -772,12 +770,14 @@ RLAPI void rlCopyFramebuffer(int x, int y, int width, int height, int format, vo
RLAPI void rlResizeFramebuffer(int width, int height); // Resize internal framebuffer
// Shaders management
-RLAPI unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode); // Load shader from code strings
-RLAPI unsigned int rlCompileShader(const char *shaderCode, int type); // Compile custom shader and return shader id (type: RL_VERTEX_SHADER, RL_FRAGMENT_SHADER, RL_COMPUTE_SHADER)
-RLAPI unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId); // Load custom shader program
+RLAPI unsigned int rlLoadShader(const char *code, int type); // Load (compile) shader and return shader id (type: RL_VERTEX_SHADER, RL_FRAGMENT_SHADER, RL_COMPUTE_SHADER)
+RLAPI unsigned int rlLoadShaderProgram(const char *vsCode, const char *fsCode); // Load shader from code strings
+RLAPI unsigned int rlLoadShaderProgramEx(unsigned int vsId, unsigned int fsId); // Load shader program, using already loaded shader ids
+RLAPI unsigned int rlLoadShaderProgramCompute(unsigned int csId); // Load compute shader program
+RLAPI void rlUnloadShader(unsigned int id); // Unload shader, loaded with rlLoadShader()
RLAPI void rlUnloadShaderProgram(unsigned int id); // Unload shader program
-RLAPI int rlGetLocationUniform(unsigned int shaderId, const char *uniformName); // Get shader location uniform, requires shader program id
-RLAPI int rlGetLocationAttrib(unsigned int shaderId, const char *attribName); // Get shader location attribute, requires shader program id
+RLAPI int rlGetLocationUniform(unsigned int id, const char *uniformName); // Get shader location uniform, requires shader program id
+RLAPI int rlGetLocationAttrib(unsigned int id, const char *attribName); // Get shader location attribute, requires shader program id
RLAPI void rlSetUniform(int locIndex, const void *value, int uniformType, int count); // Set shader value uniform
RLAPI void rlSetUniformMatrix(int locIndex, Matrix mat); // Set shader value matrix
RLAPI void rlSetUniformMatrices(int locIndex, const Matrix *mat, int count); // Set shader value matrices
@@ -785,7 +785,6 @@ RLAPI void rlSetUniformSampler(int locIndex, unsigned int textureId);
RLAPI void rlSetShader(unsigned int id, int *locs); // Set shader currently active (id and locations)
// Compute shader management
-RLAPI unsigned int rlLoadComputeShaderProgram(unsigned int shaderId); // Load compute shader program
RLAPI void rlComputeShaderDispatch(unsigned int groupX, unsigned int groupY, unsigned int groupZ); // Dispatch compute shader (equivalent to *draw* for graphics pipeline)
// Shader buffer storage object management (ssbo)
@@ -992,31 +991,34 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
// Default shader vertex attribute names to set location points
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION
- #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION
+ #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION
#endif
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD
- #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD
+ #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD
#endif
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL
- #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL
+ #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL
#endif
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR
- #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR
+ #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR
#endif
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT
- #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT
+ #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT
#endif
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2
- #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2
+ #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2
#endif
-#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS
- #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS "vertexBoneIds" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS
+#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES
+ #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES "vertexBoneIndices" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES
#endif
#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS
- #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS
+ #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS
#endif
-#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX
- #define RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX "instanceTransform" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX
+#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES
+ #define RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES "boneMatrices" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEMATRICES
+#endif
+#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORM
+ #define RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORM "instanceTransform" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORM
#endif
#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_MVP
@@ -1037,9 +1039,6 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR
#define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color)
#endif
-#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES
- #define RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES "boneMatrices" // bone matrices
-#endif
#ifndef RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0
#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0)
#endif
@@ -1049,6 +1048,9 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
#ifndef RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2
#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2)
#endif
+#ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES
+ #define RL_DEFAULT_SHADER_UNIFORM_NAME_BONEMATRICES "boneMatrices" // bone matrices (required for GPU skinning)
+#endif
//----------------------------------------------------------------------------------
// Module Types and Structures Definition
@@ -1133,7 +1135,7 @@ typedef struct rlglData {
} ExtSupported; // Extensions supported flags
} rlglData;
-#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2
+#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2
//----------------------------------------------------------------------------------
// Global Variables Definition
@@ -1143,7 +1145,7 @@ static double rlCullDistanceFar = RL_CULL_DISTANCE_FAR;
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
static rlglData RLGL = { 0 };
-#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2
+#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2
static bool isGpuReady = false;
#if defined(GRAPHICS_API_OPENGL_ES2) && !defined(GRAPHICS_API_OPENGL_ES3)
@@ -1164,10 +1166,10 @@ static PFNGLVERTEXATTRIBDIVISOREXTPROC glVertexAttribDivisor = NULL;
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
static void rlLoadShaderDefault(void); // Load default shader
static void rlUnloadShaderDefault(void); // Unload default shader
-#if defined(RLGL_SHOW_GL_DETAILS_INFO)
+#if RLGL_SHOW_GL_DETAILS_INFO
static const char *rlGetCompressedFormatName(int format); // Get compressed format official GL identifier name
-#endif // RLGL_SHOW_GL_DETAILS_INFO
-#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2
+#endif
+#endif
static int rlGetPixelDataSize(int width, int height, int format); // Get pixel data size in bytes (image or texture)
@@ -2209,7 +2211,7 @@ void rlSetBlendFactorsSeparate(int glSrcRGB, int glDstRGB, int glSrcAlpha, int g
//----------------------------------------------------------------------------------
// Module Functions Definition - OpenGL Debug
//----------------------------------------------------------------------------------
-#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) && defined(GRAPHICS_API_OPENGL_43)
+#if defined(GRAPHICS_API_OPENGL_43) && RLGL_ENABLE_OPENGL_DEBUG_CONTEXT
static void GLAPIENTRY rlDebugMessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam)
{
// Ignore non-significant error/warning codes (NVidia drivers)
@@ -2275,8 +2277,8 @@ void rlglInit(int width, int height)
{
isGpuReady = true;
- // Enable OpenGL debug context if required
-#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) && defined(GRAPHICS_API_OPENGL_43)
+ // Enable OpenGL debug context if requested (and supported)
+#if defined(GRAPHICS_API_OPENGL_43) && RLGL_ENABLE_OPENGL_DEBUG_CONTEXT
if ((glDebugMessageCallback != NULL) && (glDebugMessageControl != NULL))
{
glDebugMessageCallback(rlDebugMessageCallback, 0);
@@ -2320,7 +2322,7 @@ void rlglInit(int width, int height)
RLGL.State.projection = rlMatrixIdentity();
RLGL.State.modelview = rlMatrixIdentity();
RLGL.State.currentMatrix = &RLGL.State.modelview;
-#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2
+#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2
#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
// Initialize software renderer backend
@@ -2404,7 +2406,7 @@ void rlLoadExtensions(void *loader)
glGetIntegerv(GL_NUM_EXTENSIONS, &numExt);
TRACELOG(RL_LOG_INFO, "GL: Supported extensions count: %i", numExt);
-#if defined(RLGL_SHOW_GL_DETAILS_INFO)
+#if RLGL_SHOW_GL_DETAILS_INFO
// Get supported extensions list
// WARNING: glGetStringi() not available on OpenGL 2.1
TRACELOG(RL_LOG_INFO, "GL: OpenGL extensions:");
@@ -2446,7 +2448,7 @@ void rlLoadExtensions(void *loader)
RLGL.ExtSupported.ssbo = GLAD_GL_ARB_shader_storage_buffer_object;
#endif
-#endif // GRAPHICS_API_OPENGL_33
+#endif // GRAPHICS_API_OPENGL_33
#if defined(GRAPHICS_API_OPENGL_ES3)
// Register supported extensions flags
@@ -2502,7 +2504,7 @@ void rlLoadExtensions(void *loader)
TRACELOG(RL_LOG_INFO, "GL: Supported extensions count: %i", numExt);
-#if defined(RLGL_SHOW_GL_DETAILS_INFO)
+#if RLGL_SHOW_GL_DETAILS_INFO
TRACELOG(RL_LOG_INFO, "GL: OpenGL extensions:");
for (int i = 0; i < numExt; i++) TRACELOG(RL_LOG_INFO, " %s", extList[i]);
#endif
@@ -2612,7 +2614,7 @@ void rlLoadExtensions(void *loader)
// Free extensions pointers
RL_FREE(extList);
RL_FREE(extensionsDup); // Duplicated string must be deallocated
-#endif // GRAPHICS_API_OPENGL_ES2
+#endif // GRAPHICS_API_OPENGL_ES2
// Check OpenGL information and capabilities
//------------------------------------------------------------------------------
@@ -2632,7 +2634,7 @@ void rlLoadExtensions(void *loader)
#endif
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &RLGL.ExtSupported.maxAnisotropyLevel);
-#if defined(RLGL_SHOW_GL_DETAILS_INFO)
+#if RLGL_SHOW_GL_DETAILS_INFO
// Show some OpenGL GPU capabilities
TRACELOG(RL_LOG_INFO, "GL: OpenGL capabilities:");
GLint capability = 0;
@@ -2663,8 +2665,9 @@ void rlLoadExtensions(void *loader)
TRACELOG(RL_LOG_INFO, " GL_MAX_VERTEX_ATTRIB_BINDINGS: %i", capability);
glGetIntegerv(GL_MAX_UNIFORM_LOCATIONS, &capability);
TRACELOG(RL_LOG_INFO, " GL_MAX_UNIFORM_LOCATIONS: %i", capability);
-#endif // GRAPHICS_API_OPENGL_43
-#else // RLGL_SHOW_GL_DETAILS_INFO
+#endif
+
+#else // !RLGL_SHOW_GL_DETAILS_INFO
// Show some basic info about GL supported features
if (RLGL.ExtSupported.vao) TRACELOG(RL_LOG_INFO, "GL: VAO extension detected, VAO functions loaded successfully");
@@ -2678,9 +2681,9 @@ void rlLoadExtensions(void *loader)
if (RLGL.ExtSupported.texCompASTC) TRACELOG(RL_LOG_INFO, "GL: ASTC compressed textures supported");
if (RLGL.ExtSupported.computeShader) TRACELOG(RL_LOG_INFO, "GL: Compute shaders supported");
if (RLGL.ExtSupported.ssbo) TRACELOG(RL_LOG_INFO, "GL: Shader storage buffer objects supported");
-#endif // RLGL_SHOW_GL_DETAILS_INFO
+#endif
-#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2
+#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2
}
// Get OpenGL procedure address
@@ -2980,7 +2983,7 @@ void rlDrawRenderBatch(rlRenderBatch *batch)
{
// Activate elements VAO
if (RLGL.ExtSupported.vao) glBindVertexArray(batch->vertexBuffer[batch->currentBuffer].vaoId);
-
+
// TODO: If no data changed on the CPU arrays there is no need to re-upload data to GPU,
// a flag can be used to detect changes but it would imply keeping a copy buffer and memcmp() both, does it worth it?
@@ -3255,6 +3258,7 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format,
#if defined(GRAPHICS_API_OPENGL_11)
if (format >= RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)
{
+ // TODO: Support texture data decompression
TRACELOG(RL_LOG_WARNING, "GL: OpenGL 1.1 does not support GPU compressed texture formats");
return id;
}
@@ -3290,7 +3294,7 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format,
return id;
}
#endif
-#endif // GRAPHICS_API_OPENGL_11
+#endif // GRAPHICS_API_OPENGL_11
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
@@ -3353,7 +3357,7 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format,
}
// Texture parameters configuration
- // NOTE: glTexParameteri does NOT affect texture uploading, just the way it's used
+ // NOTE: glTexParameteri does NOT affect texture uploading
#if defined(GRAPHICS_API_OPENGL_ES2)
// NOTE: OpenGL ES 2.0 with no GL_OES_texture_npot support (i.e. WebGL) has limited NPOT support, so CLAMP_TO_EDGE must be used
if (RLGL.ExtSupported.texNPOT)
@@ -3738,7 +3742,7 @@ void *rlReadTexturePixels(unsigned int id, int width, int height, int format)
// Two possible Options:
// 1 - Bind texture to color fbo attachment and glReadPixels()
// 2 - Create an fbo, activate it, render quad with texture, glReadPixels()
- // Using Option 1, just need to care for texture format on retrieval
+ // Using Option 1, care for texture format on retrieval
// NOTE: This behaviour could be conditioned by graphic driver...
unsigned int fboId = rlLoadFramebuffer();
@@ -3826,19 +3830,19 @@ unsigned int rlLoadFramebuffer(void)
if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return fboId; }
#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2))
- glGenFramebuffers(1, &fboId); // Create the framebuffer object
- glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind any framebuffer
+ glGenFramebuffers(1, &fboId); // Create the framebuffer object
+ glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind any framebuffer
#endif
return fboId;
}
-// Attach color buffer texture to an fbo (unloads previous attachment)
+// Attach color buffer texture to a framebuffer object (unloads previous attachment)
// NOTE: Attach type: 0-Color, 1-Depth renderbuffer, 2-Depth texture
-void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType, int mipLevel)
+void rlFramebufferAttach(unsigned int id, unsigned int texId, int attachType, int texType, int mipLevel)
{
#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2))
- glBindFramebuffer(GL_FRAMEBUFFER, fboId);
+ glBindFramebuffer(GL_FRAMEBUFFER, id);
switch (attachType)
{
@@ -4175,93 +4179,14 @@ void rlUnloadVertexBuffer(unsigned int vboId)
// Shaders management
//-----------------------------------------------------------------------------------------------
-// Load shader from code strings
-// NOTE: If shader string is NULL, using default vertex/fragment shaders
-unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode)
-{
- unsigned int id = 0;
- if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return id; }
-
-#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- unsigned int vertexShaderId = 0;
- unsigned int fragmentShaderId = 0;
-
- // Compile vertex shader (if provided)
- // NOTE: If not vertex shader is provided, use default one
- if (vsCode != NULL) vertexShaderId = rlCompileShader(vsCode, GL_VERTEX_SHADER);
- else vertexShaderId = RLGL.State.defaultVShaderId;
-
- // Compile fragment shader (if provided)
- // NOTE: If not vertex shader is provided, use default one
- if (fsCode != NULL) fragmentShaderId = rlCompileShader(fsCode, GL_FRAGMENT_SHADER);
- else fragmentShaderId = RLGL.State.defaultFShaderId;
-
- // In case vertex and fragment shader are the default ones, no need to recompile, just assign the default shader program id
- if ((vertexShaderId == RLGL.State.defaultVShaderId) && (fragmentShaderId == RLGL.State.defaultFShaderId)) id = RLGL.State.defaultShaderId;
- else if ((vertexShaderId > 0) && (fragmentShaderId > 0))
- {
- // One of or both shader are new, a new shader program needs to be compiled
- id = rlLoadShaderProgram(vertexShaderId, fragmentShaderId);
-
- // Detaching and deleting vertex/fragment shaders (if not default ones)
- // WARNING: Detach shader before deletion to make sure memory is freed
- if (vertexShaderId != RLGL.State.defaultVShaderId)
- {
- // WARNING: Shader program linkage could fail and returned id is 0
- if (id > 0) glDetachShader(id, vertexShaderId);
- glDeleteShader(vertexShaderId);
- }
- if (fragmentShaderId != RLGL.State.defaultFShaderId)
- {
- // WARNING: Shader program linkage could fail and returned id is 0
- if (id > 0) glDetachShader(id, fragmentShaderId);
- glDeleteShader(fragmentShaderId);
- }
-
- // In case shader program loading failed, assign default shader
- if (id == 0)
- {
- // In case shader loading fails, reassigning default shader
- TRACELOG(RL_LOG_WARNING, "SHADER: Failed to load custom shader code, using default shader");
- id = RLGL.State.defaultShaderId;
- }
- /*
- else
- {
- // Get available shader uniforms
- // NOTE: This information is useful for debug...
- int uniformCount = -1;
- glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &uniformCount);
-
- for (int i = 0; i < uniformCount; i++)
- {
- int namelen = -1;
- int num = -1;
- char name[256] = { 0 }; // Assume no variable names longer than 256
- GLenum type = GL_ZERO;
-
- // Get the name of the uniforms
- glGetActiveUniform(id, i, sizeof(name) - 1, &namelen, &num, &type, name);
-
- name[namelen] = 0;
- TRACELOG(RL_LOG_DEBUG, "SHADER: [ID %i] Active uniform (%s) set at location: %i", id, name, glGetUniformLocation(id, name));
- }
- }
- */
- }
-#endif
-
- return id;
-}
-
-// Compile custom shader and return shader id
-unsigned int rlCompileShader(const char *shaderCode, int type)
+// Load (compile) shader and return shader id
+unsigned int rlLoadShader(const char *code, int type)
{
unsigned int shaderId = 0;
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
shaderId = glCreateShader(type);
- glShaderSource(shaderId, 1, &shaderCode, NULL);
+ glShaderSource(shaderId, 1, &code, NULL);
GLint success = 0;
glCompileShader(shaderId);
@@ -4319,8 +4244,87 @@ unsigned int rlCompileShader(const char *shaderCode, int type)
return shaderId;
}
-// Load custom shader strings and return program id
-unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId)
+// Load shader program from code strings
+// NOTE: If shader string is NULL, using default vertex/fragment shaders
+unsigned int rlLoadShaderProgram(const char *vsCode, const char *fsCode)
+{
+ unsigned int id = 0; // Shader program id
+ if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return id; }
+
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+ unsigned int vertexShaderId = 0;
+ unsigned int fragmentShaderId = 0;
+
+ // Compile vertex shader (if provided)
+ // NOTE: If not vertex shader is provided, use default one
+ if (vsCode != NULL) vertexShaderId = rlLoadShader(vsCode, GL_VERTEX_SHADER);
+ else vertexShaderId = RLGL.State.defaultVShaderId;
+
+ // Compile fragment shader (if provided)
+ // NOTE: If not vertex shader is provided, use default one
+ if (fsCode != NULL) fragmentShaderId = rlLoadShader(fsCode, GL_FRAGMENT_SHADER);
+ else fragmentShaderId = RLGL.State.defaultFShaderId;
+
+ // In case vertex and fragment shader are the default ones, no need to recompile, assign the default shader program id
+ if ((vertexShaderId == RLGL.State.defaultVShaderId) && (fragmentShaderId == RLGL.State.defaultFShaderId)) id = RLGL.State.defaultShaderId;
+ else if ((vertexShaderId > 0) && (fragmentShaderId > 0))
+ {
+ // One of or both shader are new, a new shader program needs to be compiled
+ id = rlLoadShaderProgramEx(vertexShaderId, fragmentShaderId);
+
+ // Detaching and deleting vertex/fragment shaders (if not default ones)
+ // WARNING: Detach shader before deletion to make sure memory is freed
+ if (vertexShaderId != RLGL.State.defaultVShaderId)
+ {
+ // WARNING: Shader program linkage could fail and returned id is 0
+ if (id > 0) glDetachShader(id, vertexShaderId);
+ glDeleteShader(vertexShaderId);
+ }
+ if (fragmentShaderId != RLGL.State.defaultFShaderId)
+ {
+ // WARNING: Shader program linkage could fail and returned id is 0
+ if (id > 0) glDetachShader(id, fragmentShaderId);
+ glDeleteShader(fragmentShaderId);
+ }
+
+ // In case shader program loading failed, assign default shader
+ if (id == 0)
+ {
+ // In case shader loading fails, reassigning default shader
+ TRACELOG(RL_LOG_WARNING, "SHADER: Failed to load custom shader code, using default shader");
+ id = RLGL.State.defaultShaderId;
+ }
+ /*
+ else
+ {
+ // Get available shader uniforms
+ // NOTE: This information is useful for debug...
+ int uniformCount = -1;
+ glGetProgramiv(id, GL_ACTIVE_UNIFORMS, &uniformCount);
+
+ for (int i = 0; i < uniformCount; i++)
+ {
+ int namelen = -1;
+ int num = -1;
+ char name[256] = { 0 }; // Assume no variable names longer than 256
+ GLenum type = GL_ZERO;
+
+ // Get the name of the uniforms
+ glGetActiveUniform(id, i, sizeof(name) - 1, &namelen, &num, &type, name);
+
+ name[namelen] = 0;
+ TRACELOG(RL_LOG_DEBUG, "SHADER: [ID %i] Active uniform (%s) set at location: %i", id, name, glGetUniformLocation(id, name));
+ }
+ }
+ */
+ }
+#endif
+
+ return id;
+}
+
+// Load shader program from already loaded shader ids
+unsigned int rlLoadShaderProgramEx(unsigned int vsId, unsigned int fsId)
{
unsigned int programId = 0;
if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return programId; }
@@ -4329,24 +4333,21 @@ unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId)
GLint success = 0;
programId = glCreateProgram();
- glAttachShader(programId, vShaderId);
- glAttachShader(programId, fShaderId);
+ glAttachShader(programId, vsId);
+ glAttachShader(programId, fsId);
- // NOTE: Default attribute shader locations must be Bound before linking
+ // Default attribute shader locations must be bound before linking
+ // NOTE: There is no problem with binding a generic attribute index to an attribute variable name
+ // that is never used; if some attrib name is no found on the shader, it locations becomes -1
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION);
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD);
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL);
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT);
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2);
- glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCE_TX, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX);
-
-#ifdef RL_SUPPORT_MESH_GPU_SKINNING
- glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS);
+ glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCETRANSFORM, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCETRANSFORM);
+ glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEINDICES);
glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS);
-#endif
-
- // NOTE: If some attrib name is no found on the shader, it locations becomes -1
glLinkProgram(programId);
@@ -4387,6 +4388,69 @@ unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId)
return programId;
}
+// Load compute shader program
+unsigned int rlLoadShaderProgramCompute(unsigned int csId)
+{
+ unsigned int programId = 0;
+
+#if defined(GRAPHICS_API_OPENGL_43)
+ GLint success = 0;
+ programId = glCreateProgram();
+
+ glAttachShader(programId, csId);
+
+ glLinkProgram(programId);
+
+ // NOTE: All uniform variables are intitialised to 0 when a program links
+
+ glGetProgramiv(programId, GL_LINK_STATUS, &success);
+
+ if (success == GL_FALSE)
+ {
+ TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to link compute shader program", programId);
+
+ int maxLength = 0;
+ glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &maxLength);
+
+ if (maxLength > 0)
+ {
+ int length = 0;
+ char *log = (char *)RL_CALLOC(maxLength, sizeof(char));
+ glGetProgramInfoLog(programId, maxLength, &length, log);
+ TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Link error: %s", programId, log);
+ RL_FREE(log);
+ }
+
+ glDeleteProgram(programId);
+
+ programId = 0;
+ }
+ else
+ {
+ // Get the size of compiled shader program (not available on OpenGL ES 2.0)
+ // NOTE: If GL_LINK_STATUS is GL_FALSE, program binary length is zero
+ //GLint binarySize = 0;
+ //glGetProgramiv(id, GL_PROGRAM_BINARY_LENGTH, &binarySize);
+
+ TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader program loaded successfully", programId);
+ }
+#else
+ TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not supported, enable GRAPHICS_API_OPENGL_43");
+#endif
+
+ return programId;
+}
+
+// Delete shader
+void rlUnloadShader(unsigned int id)
+{
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+ glDeleteShader(id);
+
+ TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Unloaded shader data from VRAM (GPU)", id);
+#endif
+}
+
// Unload shader program
void rlUnloadShaderProgram(unsigned int id)
{
@@ -4399,11 +4463,11 @@ void rlUnloadShaderProgram(unsigned int id)
// Get shader location uniform
// NOTE: First parameter refers to shader program id
-int rlGetLocationUniform(unsigned int shaderId, const char *uniformName)
+int rlGetLocationUniform(unsigned int id, const char *uniformName)
{
int location = -1;
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- location = glGetUniformLocation(shaderId, uniformName);
+ location = glGetUniformLocation(id, uniformName);
//if (location == -1) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to find shader uniform: %s", shaderId, uniformName);
//else TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Shader uniform (%s) set at location: %i", shaderId, uniformName, location);
@@ -4413,11 +4477,11 @@ int rlGetLocationUniform(unsigned int shaderId, const char *uniformName)
// Get shader location attribute
// NOTE: First parameter refers to shader program id
-int rlGetLocationAttrib(unsigned int shaderId, const char *attribName)
+int rlGetLocationAttrib(unsigned int id, const char *attribName)
{
int location = -1;
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- location = glGetAttribLocation(shaderId, attribName);
+ location = glGetAttribLocation(id, attribName);
//if (location == -1) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to find shader attribute: %s", shaderId, attribName);
//else TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Shader attribute (%s) set at location: %i", shaderId, attribName, location);
@@ -4527,57 +4591,6 @@ void rlSetShader(unsigned int id, int *locs)
#endif
}
-// Load compute shader program
-unsigned int rlLoadComputeShaderProgram(unsigned int shaderId)
-{
- unsigned int programId = 0;
-
-#if defined(GRAPHICS_API_OPENGL_43)
- GLint success = 0;
- programId = glCreateProgram();
- glAttachShader(programId, shaderId);
- glLinkProgram(programId);
-
- // NOTE: All uniform variables are intitialised to 0 when a program links
-
- glGetProgramiv(programId, GL_LINK_STATUS, &success);
-
- if (success == GL_FALSE)
- {
- TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to link compute shader program", programId);
-
- int maxLength = 0;
- glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &maxLength);
-
- if (maxLength > 0)
- {
- int length = 0;
- char *log = (char *)RL_CALLOC(maxLength, sizeof(char));
- glGetProgramInfoLog(programId, maxLength, &length, log);
- TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Link error: %s", programId, log);
- RL_FREE(log);
- }
-
- glDeleteProgram(programId);
-
- programId = 0;
- }
- else
- {
- // Get the size of compiled shader program (not available on OpenGL ES 2.0)
- // NOTE: If GL_LINK_STATUS is GL_FALSE, program binary length is zero
- //GLint binarySize = 0;
- //glGetProgramiv(id, GL_PROGRAM_BINARY_LENGTH, &binarySize);
-
- TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader program loaded successfully", programId);
- }
-#else
- TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43");
-#endif
-
- return programId;
-}
-
// Dispatch compute shader (equivalent to *draw* for graphics pilepine)
void rlComputeShaderDispatch(unsigned int groupX, unsigned int groupY, unsigned int groupZ)
{
@@ -4612,7 +4625,6 @@ void rlUnloadShaderBuffer(unsigned int ssboId)
#else
TRACELOG(RL_LOG_WARNING, "SSBO: SSBO not enabled. Define GRAPHICS_API_OPENGL_43");
#endif
-
}
// Update SSBO buffer data
@@ -4745,7 +4757,7 @@ Matrix rlGetMatrixTransform(void)
// TODO: Consider possible transform matrices in the RLGL.State.stack
//Matrix matStackTransform = rlMatrixIdentity();
//for (int i = RLGL.State.stackCounter; i > 0; i--) matStackTransform = rlMatrixMultiply(RLGL.State.stack[i], matStackTransform);
-
+
mat = RLGL.State.transform;
#endif
return mat;
@@ -5071,10 +5083,10 @@ static void rlLoadShaderDefault(void)
// NOTE: Compiled vertex/fragment shaders are not deleted,
// they are kept for re-use as default shaders in case some shader loading fails
- RLGL.State.defaultVShaderId = rlCompileShader(defaultVShaderCode, GL_VERTEX_SHADER); // Compile default vertex shader
- RLGL.State.defaultFShaderId = rlCompileShader(defaultFShaderCode, GL_FRAGMENT_SHADER); // Compile default fragment shader
+ RLGL.State.defaultVShaderId = rlLoadShader(defaultVShaderCode, GL_VERTEX_SHADER); // Compile default vertex shader
+ RLGL.State.defaultFShaderId = rlLoadShader(defaultFShaderCode, GL_FRAGMENT_SHADER); // Compile default fragment shader
- RLGL.State.defaultShaderId = rlLoadShaderProgram(RLGL.State.defaultVShaderId, RLGL.State.defaultFShaderId);
+ RLGL.State.defaultShaderId = rlLoadShaderProgramEx(RLGL.State.defaultVShaderId, RLGL.State.defaultFShaderId);
if (RLGL.State.defaultShaderId > 0)
{
@@ -5111,7 +5123,7 @@ static void rlUnloadShaderDefault(void)
TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Default shader unloaded successfully", RLGL.State.defaultShaderId);
}
-#if defined(RLGL_SHOW_GL_DETAILS_INFO)
+#if RLGL_SHOW_GL_DETAILS_INFO
// Get compressed format official GL identifier name
static const char *rlGetCompressedFormatName(int format)
{
@@ -5185,9 +5197,9 @@ static const char *rlGetCompressedFormatName(int format)
default: return "GL_COMPRESSED_UNKNOWN"; break;
}
}
-#endif // RLGL_SHOW_GL_DETAILS_INFO
+#endif
-#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2
+#endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2
// Get pixel data size in bytes (image or texture)
// NOTE: Size depends on pixel format
@@ -5390,4 +5402,4 @@ static Matrix rlMatrixInvert(Matrix mat)
}
#endif
-#endif // RLGL_IMPLEMENTATION
\ No newline at end of file
+#endif // RLGL_IMPLEMENTATION
diff --git a/src/rmodels.c b/src/rmodels.c
index b90b6a53f..1c7a5452c 100644
--- a/src/rmodels.c
+++ b/src/rmodels.c
@@ -44,7 +44,7 @@
#include "config.h" // Defines module configuration flags
-#if defined(SUPPORT_MODULE_RMODELS)
+#if SUPPORT_MODULE_RMODELS
#include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2
#include "raymath.h" // Required for: Vector3, Quaternion and Matrix functionality
@@ -54,7 +54,7 @@
#include // Required for: memcmp(), strlen(), strncpy()
#include // Required for: sinf(), cosf(), sqrtf(), fabsf()
-#if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL)
+#if SUPPORT_FILEFORMAT_OBJ || SUPPORT_FILEFORMAT_MTL
#define TINYOBJ_MALLOC RL_MALLOC
#define TINYOBJ_CALLOC RL_CALLOC
#define TINYOBJ_REALLOC RL_REALLOC
@@ -64,7 +64,7 @@
#include "external/tinyobj_loader_c.h" // OBJ/MTL file formats loading
#endif
-#if defined(SUPPORT_FILEFORMAT_GLTF)
+#if SUPPORT_FILEFORMAT_GLTF
#define CGLTF_MALLOC RL_MALLOC
#define CGLTF_FREE RL_FREE
@@ -72,7 +72,7 @@
#include "external/cgltf.h" // glTF file format loading
#endif
-#if defined(SUPPORT_FILEFORMAT_VOX)
+#if SUPPORT_FILEFORMAT_VOX
#define VOX_MALLOC RL_MALLOC
#define VOX_CALLOC RL_CALLOC
#define VOX_REALLOC RL_REALLOC
@@ -82,7 +82,7 @@
#include "external/vox_loader.h" // VOX file format loading (MagikaVoxel)
#endif
-#if defined(SUPPORT_FILEFORMAT_M3D)
+#if SUPPORT_FILEFORMAT_M3D
#define M3D_MALLOC RL_MALLOC
#define M3D_REALLOC RL_REALLOC
#define M3D_FREE RL_FREE
@@ -91,7 +91,7 @@
#include "external/m3d.h" // Model3D file format loading
#endif
-#if defined(SUPPORT_MESH_GENERATION)
+#if SUPPORT_MESH_GENERATION
#define PAR_MALLOC(T, N) ((T *)RL_MALLOC(N*sizeof(T)))
#define PAR_CALLOC(T, N) ((T *)RL_CALLOC(N*sizeof(T), 1))
#define PAR_REALLOC(T, BUF, N) ((T *)RL_REALLOC(BUF, sizeof(T)*(N)))
@@ -126,7 +126,13 @@
#define MAX_MATERIAL_MAPS 12 // Maximum number of maps supported
#endif
#ifndef MAX_MESH_VERTEX_BUFFERS
+#if SUPPORT_GPU_SKINNING
+ // NOTE: Two additional vertex buffers required to store bone indices and bone weights
+ // WARNING: Some GPUs could not support more than 8 VBOs
#define MAX_MESH_VERTEX_BUFFERS 9 // Maximum vertex buffers (VBO) per mesh
+#else
+ #define MAX_MESH_VERTEX_BUFFERS 7 // Maximum vertex buffers (VBO) per mesh
+#endif
#endif
#ifndef MAX_FILEPATH_LENGTH
#define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value)
@@ -145,28 +151,31 @@
//----------------------------------------------------------------------------------
// Module Internal Functions Declaration
//----------------------------------------------------------------------------------
-#if defined(SUPPORT_FILEFORMAT_OBJ)
+#if SUPPORT_FILEFORMAT_OBJ
static Model LoadOBJ(const char *fileName); // Load OBJ mesh data
#endif
-#if defined(SUPPORT_FILEFORMAT_IQM)
+#if SUPPORT_FILEFORMAT_IQM
static Model LoadIQM(const char *fileName); // Load IQM mesh data
static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCount); // Load IQM animation data
#endif
-#if defined(SUPPORT_FILEFORMAT_GLTF)
+#if SUPPORT_FILEFORMAT_GLTF
static Model LoadGLTF(const char *fileName); // Load GLTF mesh data
static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCount); // Load GLTF animation data
#endif
-#if defined(SUPPORT_FILEFORMAT_VOX)
+#if SUPPORT_FILEFORMAT_VOX
static Model LoadVOX(const char *filename); // Load VOX mesh data
#endif
-#if defined(SUPPORT_FILEFORMAT_M3D)
+#if SUPPORT_FILEFORMAT_M3D
static Model LoadM3D(const char *filename); // Load M3D mesh data
static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCount); // Load M3D animation data
#endif
-#if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL)
+#if SUPPORT_FILEFORMAT_OBJ || SUPPORT_FILEFORMAT_MTL
static void ProcessMaterialsOBJ(Material *rayMaterials, tinyobj_material_t *materials, int materialCount); // Process obj materials
#endif
+// Update model vertex data (positions and normals)
+static void UpdateModelAnimationVertexBuffers(Model model);
+
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
@@ -783,13 +792,9 @@ void DrawCapsule(Vector3 startPos, Vector3 endPos, float radius, int slices, int
{
for (int j = 0; j < slices; j++)
{
+ // Building up the rings from capCenter in the direction of the 'direction' vector computed earlier
- // we build up the rings from capCenter in the direction of the 'direction' vector we computed earlier
-
- // as we iterate through the rings they must be placed higher above the center, the height we need is sin(angle(i))
- // as we iterate through the rings they must get smaller by the cos(angle(i))
-
- // compute the four vertices
+ // Compute the four vertices
float ringSin1 = sinf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 0 ));
float ringCos1 = cosf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 0 ));
Vector3 w1 = (Vector3){
@@ -926,13 +931,9 @@ void DrawCapsuleWires(Vector3 startPos, Vector3 endPos, float radius, int slices
{
for (int j = 0; j < slices; j++)
{
+ // Building up the rings from capCenter in the direction of the 'direction' vector computed earlier
- // we build up the rings from capCenter in the direction of the 'direction' vector we computed earlier
-
- // as we iterate through the rings they must be placed higher above the center, the height we need is sin(angle(i))
- // as we iterate through the rings they must get smaller by the cos(angle(i))
-
- // compute the four vertices
+ // Compute the four vertices
float ringSin1 = sinf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 0 ));
float ringCos1 = cosf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 0 ));
Vector3 w1 = (Vector3){
@@ -1096,19 +1097,19 @@ Model LoadModel(const char *fileName)
{
Model model = { 0 };
-#if defined(SUPPORT_FILEFORMAT_OBJ)
+#if SUPPORT_FILEFORMAT_OBJ
if (IsFileExtension(fileName, ".obj")) model = LoadOBJ(fileName);
#endif
-#if defined(SUPPORT_FILEFORMAT_IQM)
+#if SUPPORT_FILEFORMAT_IQM
if (IsFileExtension(fileName, ".iqm")) model = LoadIQM(fileName);
#endif
-#if defined(SUPPORT_FILEFORMAT_GLTF)
+#if SUPPORT_FILEFORMAT_GLTF
if (IsFileExtension(fileName, ".gltf") || IsFileExtension(fileName, ".glb")) model = LoadGLTF(fileName);
#endif
-#if defined(SUPPORT_FILEFORMAT_VOX)
+#if SUPPORT_FILEFORMAT_VOX
if (IsFileExtension(fileName, ".vox")) model = LoadVOX(fileName);
#endif
-#if defined(SUPPORT_FILEFORMAT_M3D)
+#if SUPPORT_FILEFORMAT_M3D
if (IsFileExtension(fileName, ".m3d")) model = LoadM3D(fileName);
#endif
@@ -1138,7 +1139,7 @@ Model LoadModel(const char *fileName)
// Load model from generated mesh
// WARNING: A shallow copy of mesh is generated, passed by value,
-// as long as struct contains pointers to data and some values, we get a copy
+// as long as struct contains pointers to data and some values, get a copy
// of mesh pointing to same data as original version... be careful!
Model LoadModelFromMesh(Mesh mesh)
{
@@ -1182,10 +1183,10 @@ bool IsModelValid(Model model)
if ((model.meshes[i].tangents != NULL) && (model.meshes[i].vboId[4] == 0)) { result = false; break; } // Vertex tangents buffer not uploaded to GPU
if ((model.meshes[i].texcoords2 != NULL) && (model.meshes[i].vboId[5] == 0)) { result = false; break; } // Vertex texcoords2 buffer not uploaded to GPU
if ((model.meshes[i].indices != NULL) && (model.meshes[i].vboId[6] == 0)) { result = false; break; } // Vertex indices buffer not uploaded to GPU
- if ((model.meshes[i].boneIds != NULL) && (model.meshes[i].vboId[7] == 0)) { result = false; break; } // Vertex boneIds buffer not uploaded to GPU
+ if ((model.meshes[i].boneIndices != NULL) && (model.meshes[i].vboId[7] == 0)) { result = false; break; } // Vertex boneIndices buffer not uploaded to GPU
if ((model.meshes[i].boneWeights != NULL) && (model.meshes[i].vboId[8] == 0)) { result = false; break; } // Vertex boneWeights buffer not uploaded to GPU
- // NOTE: Some OpenGL versions do not support VAO, so we don't check it
+ // NOTE: Some OpenGL versions do not support VAO, so avoid below check
//if (model.meshes[i].vaoId == 0) { result = false; break }
}
@@ -1202,7 +1203,7 @@ void UnloadModel(Model model)
// Unload materials maps
// NOTE: As the user could be sharing shaders and textures between models,
- // we don't unload the material but just free its maps,
+ // don't unload the material but free its maps,
// the user is responsible for freeing models shaders and textures
for (int i = 0; i < model.materialCount; i++) RL_FREE(model.materials[i].maps);
@@ -1212,8 +1213,8 @@ void UnloadModel(Model model)
RL_FREE(model.meshMaterial);
// Unload animation data
- RL_FREE(model.bones);
- RL_FREE(model.bindPose);
+ RL_FREE(model.skeleton.bones);
+ RL_FREE(model.skeleton.bindPose);
TRACELOG(LOG_INFO, "MODEL: Unloaded model (and meshes) from RAM and VRAM");
}
@@ -1273,9 +1274,8 @@ void UploadMesh(Mesh *mesh, bool dynamic)
mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT] = 0; // Vertex buffer: tangents
mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2] = 0; // Vertex buffer: texcoords2
mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES] = 0; // Vertex buffer: indices
-
-#ifdef RL_SUPPORT_MESH_GPU_SKINNING
- mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS] = 0; // Vertex buffer: boneIds
+#if SUPPORT_GPU_SKINNING
+ mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES] = 0; // Vertex buffer: boneIndices
mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS] = 0; // Vertex buffer: boneWeights
#endif
@@ -1375,21 +1375,21 @@ void UploadMesh(Mesh *mesh, bool dynamic)
rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2);
}
-#ifdef RL_SUPPORT_MESH_GPU_SKINNING
- if (mesh->boneIds != NULL)
+#if SUPPORT_GPU_SKINNING
+ if (mesh->boneIndices != NULL)
{
- // Enable vertex attribute: boneIds (shader-location = 7)
- mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS] = rlLoadVertexBuffer(mesh->boneIds, mesh->vertexCount*4*sizeof(unsigned char), dynamic);
- rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, 4, RL_UNSIGNED_BYTE, 0, 0, 0);
- rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS);
+ // Enable vertex attribute: boneIndices (shader-location = 7)
+ mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES] = rlLoadVertexBuffer(mesh->boneIndices, mesh->vertexCount*4*sizeof(unsigned char), dynamic);
+ rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES, 4, RL_UNSIGNED_BYTE, 0, 0, 0);
+ rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES);
}
else
{
- // Default vertex attribute: boneIds
+ // Default vertex attribute: boneIndices
// WARNING: Default value provided to shader if location available
float value[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
- rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, value, SHADER_ATTRIB_VEC4, 4);
- rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS);
+ rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES, value, SHADER_ATTRIB_VEC4, 4);
+ rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES);
}
if (mesh->boneWeights != NULL)
@@ -1436,17 +1436,17 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform)
#define GL_COLOR_ARRAY 0x8076
#define GL_TEXTURE_COORD_ARRAY 0x8078
- if (mesh.texcoords && material.maps[MATERIAL_MAP_DIFFUSE].texture.id > 0) rlEnableTexture(material.maps[MATERIAL_MAP_DIFFUSE].texture.id);
+ if ((mesh.texcoords != NULL) && (material.maps[MATERIAL_MAP_DIFFUSE].texture.id > 0)) rlEnableTexture(material.maps[MATERIAL_MAP_DIFFUSE].texture.id);
- if (mesh.animVertices) rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.animVertices);
+ if (mesh.animVertices != NULL) rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.animVertices);
else rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.vertices);
if (mesh.texcoords) rlEnableStatePointer(GL_TEXTURE_COORD_ARRAY, mesh.texcoords);
- if (mesh.animNormals) rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.animNormals);
- else if (mesh.normals) rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.normals);
+ if (mesh.animNormals != NULL) rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.animNormals);
+ else if (mesh.normals != NULL) rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.normals);
- if (mesh.colors) rlEnableStatePointer(GL_COLOR_ARRAY, mesh.colors);
+ if (mesh.colors != NULL) rlEnableStatePointer(GL_COLOR_ARRAY, mesh.colors);
rlPushMatrix();
rlMultMatrixf(MatrixToFloat(transform));
@@ -1502,8 +1502,8 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform)
}
// Get a copy of current matrices to work with,
- // just in case stereo render is required, and we need to modify them
- // NOTE: At this point the modelview matrix just contains the view matrix (camera)
+ // in case stereo render is required, and they need to be modified
+ // NOTE: At this point the modelview matrix contains the view matrix (camera)
// That's because BeginMode3D() sets it and there is no model-drawing function
// that modifies it, all use rlPushMatrix() and rlPopMatrix()
Matrix matModel = MatrixIdentity();
@@ -1528,14 +1528,6 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform)
// Upload model normal matrix (if locations available)
if (material.shader.locs[SHADER_LOC_MATRIX_NORMAL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_NORMAL], MatrixTranspose(MatrixInvert(matModel)));
-
-#ifdef RL_SUPPORT_MESH_GPU_SKINNING
- // Upload Bone Transforms
- if ((material.shader.locs[SHADER_LOC_BONE_MATRICES] != -1) && mesh.boneMatrices)
- {
- rlSetUniformMatrices(material.shader.locs[SHADER_LOC_BONE_MATRICES], mesh.boneMatrices, mesh.boneCount);
- }
-#endif
//-----------------------------------------------------
// Bind active texture maps (if available)
@@ -1615,11 +1607,11 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform)
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]);
}
-#ifdef RL_SUPPORT_MESH_GPU_SKINNING
+#if SUPPORT_GPU_SKINNING
// Bind mesh VBO data: vertex bone ids (shader-location = 6, if available)
if (material.shader.locs[SHADER_LOC_VERTEX_BONEIDS] != -1)
{
- rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS]);
+ rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES]);
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS], 4, RL_UNSIGNED_BYTE, 0, 0, 0);
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS]);
}
@@ -1694,7 +1686,7 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
// Instancing required variables
- float16 *instanceTransforms = NULL;
+ float16 *instanceTransform = NULL;
unsigned int instancesVboId = 0;
// Bind shader program
@@ -1729,8 +1721,8 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i
}
// Get a copy of current matrices to work with,
- // just in case stereo render is required, and we need to modify them
- // NOTE: At this point the modelview matrix just contains the view matrix (camera)
+ // in case stereo render is required, and they need to be modified
+ // NOTE: At this point the modelview matrix contains the view matrix (camera)
// That's because BeginMode3D() sets it and there is no model-drawing function
// that modifies it, all use rlPushMatrix() and rlPopMatrix()
Matrix matModel = MatrixIdentity();
@@ -1743,28 +1735,28 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i
if (material.shader.locs[SHADER_LOC_MATRIX_PROJECTION] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_PROJECTION], matProjection);
// Create instances buffer
- instanceTransforms = (float16 *)RL_MALLOC(instances*sizeof(float16));
+ instanceTransform = (float16 *)RL_CALLOC(instances, sizeof(float16));
// Fill buffer with instances transformations as float16 arrays
- for (int i = 0; i < instances; i++) instanceTransforms[i] = MatrixToFloatV(transforms[i]);
+ for (int i = 0; i < instances; i++) instanceTransform[i] = MatrixToFloatV(transforms[i]);
// Enable mesh VAO to attach new buffer
rlEnableVertexArray(mesh.vaoId);
// This could alternatively use a static VBO and either glMapBuffer() or glBufferSubData()
// It isn't clear which would be reliably faster in all cases and on all platforms,
- // anecdotally glMapBuffer() seems very slow (syncs) while glBufferSubData() seems
+ // anecdotally glMapBuffer() seems quite slow (syncs) while glBufferSubData() seems
// no faster, since all the transform matrices are transferred anyway
- instancesVboId = rlLoadVertexBuffer(instanceTransforms, instances*sizeof(float16), false);
+ instancesVboId = rlLoadVertexBuffer(instanceTransform, instances*sizeof(float16), false);
- // Instances transformation matrices are sent to shader attribute location: SHADER_LOC_VERTEX_INSTANCE_TX
- if (material.shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] != -1)
+ // Instances transformation matrices are sent to shader attribute location: SHADER_LOC_VERTEX_INSTANCETRANSFORM
+ if (material.shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORM] != -1)
{
for (unsigned int i = 0; i < 4; i++)
{
- rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] + i);
- rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] + i, 4, RL_FLOAT, 0, sizeof(Matrix), i*sizeof(Vector4));
- rlSetVertexAttributeDivisor(material.shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] + i, 1);
+ rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORM] + i);
+ rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORM] + i, 4, RL_FLOAT, 0, sizeof(Matrix), i*sizeof(Vector4));
+ rlSetVertexAttributeDivisor(material.shader.locs[SHADER_LOC_VERTEX_INSTANCETRANSFORM] + i, 1);
}
}
@@ -1777,15 +1769,6 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i
// Upload model normal matrix (if locations available)
if (material.shader.locs[SHADER_LOC_MATRIX_NORMAL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_NORMAL], MatrixTranspose(MatrixInvert(matModel)));
-
-#ifdef RL_SUPPORT_MESH_GPU_SKINNING
- // Upload Bone Transforms
- if ((material.shader.locs[SHADER_LOC_BONE_MATRICES] != -1) && mesh.boneMatrices)
- {
- rlSetUniformMatrices(material.shader.locs[SHADER_LOC_BONE_MATRICES], mesh.boneMatrices, mesh.boneCount);
- }
-#endif
-
//-----------------------------------------------------
// Bind active texture maps (if available)
@@ -1863,11 +1846,11 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]);
}
-#ifdef RL_SUPPORT_MESH_GPU_SKINNING
+#if SUPPORT_GPU_SKINNING
// Bind mesh VBO data: vertex bone ids (shader-location = 6, if available)
if (material.shader.locs[SHADER_LOC_VERTEX_BONEIDS] != -1)
{
- rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS]);
+ rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEINDICES]);
rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS], 4, RL_UNSIGNED_BYTE, 0, 0, 0);
rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS]);
}
@@ -1933,7 +1916,7 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i
// Remove instance transforms buffer
rlUnloadVertexBuffer(instancesVboId);
- RL_FREE(instanceTransforms);
+ RL_FREE(instanceTransform);
#endif
}
@@ -1946,6 +1929,7 @@ void UnloadMesh(Mesh mesh)
if (mesh.vboId != NULL) for (int i = 0; i < MAX_MESH_VERTEX_BUFFERS; i++) rlUnloadVertexBuffer(mesh.vboId[i]);
RL_FREE(mesh.vboId);
+ // Unload mesh vertex buffers
RL_FREE(mesh.vertices);
RL_FREE(mesh.texcoords);
RL_FREE(mesh.normals);
@@ -1954,11 +1938,13 @@ void UnloadMesh(Mesh mesh)
RL_FREE(mesh.texcoords2);
RL_FREE(mesh.indices);
+ // Unload mesh skin animation data
+ RL_FREE(mesh.boneWeights);
+ RL_FREE(mesh.boneIndices);
+
+ // Unload mesh runtime CPU skinning data
RL_FREE(mesh.animVertices);
RL_FREE(mesh.animNormals);
- RL_FREE(mesh.boneWeights);
- RL_FREE(mesh.boneIds);
- RL_FREE(mesh.boneMatrices);
}
// Export mesh data to file
@@ -2137,7 +2123,7 @@ bool ExportMeshAsCode(Mesh mesh, const char *fileName)
return success;
}
-#if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL)
+#if SUPPORT_FILEFORMAT_OBJ || SUPPORT_FILEFORMAT_MTL
// Process obj materials
static void ProcessMaterialsOBJ(Material *materials, tinyobj_material_t *mats, int materialCount)
{
@@ -2181,7 +2167,7 @@ Material *LoadMaterials(const char *fileName, int *materialCount)
// TODO: Support IQM and GLTF for materials parsing
-#if defined(SUPPORT_FILEFORMAT_MTL)
+#if SUPPORT_FILEFORMAT_MTL
if (IsFileExtension(fileName, ".mtl"))
{
tinyobj_material_t *mats = NULL;
@@ -2189,7 +2175,7 @@ Material *LoadMaterials(const char *fileName, int *materialCount)
int result = tinyobj_parse_mtl_file(&mats, &count, fileName);
if (result != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to parse materials file", fileName);
- materials = (Material *)RL_MALLOC(count*sizeof(Material));
+ materials = (Material *)RL_CALLOC(count, sizeof(Material));
ProcessMaterialsOBJ(materials, mats, count);
tinyobj_materials_free(mats, count);
@@ -2228,10 +2214,11 @@ bool IsMaterialValid(Material material)
{
bool result = false;
- if ((material.maps != NULL) && // Validate material contain some map
+ if ((material.maps != NULL) && // Validate material contain some map
(material.shader.id > 0)) result = true; // Validate material shader is valid
- // TODO: Check if available maps contain loaded textures
+ // NOTE: Checking if available maps contain loaded textures does not determine if
+ // a material is valid, there can be maps without a texture assigned, only properties
return result;
}
@@ -2274,159 +2261,218 @@ ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount)
{
ModelAnimation *animations = NULL;
-#if defined(SUPPORT_FILEFORMAT_IQM)
+#if SUPPORT_FILEFORMAT_IQM
if (IsFileExtension(fileName, ".iqm")) animations = LoadModelAnimationsIQM(fileName, animCount);
#endif
-#if defined(SUPPORT_FILEFORMAT_M3D)
+#if SUPPORT_FILEFORMAT_M3D
if (IsFileExtension(fileName, ".m3d")) animations = LoadModelAnimationsM3D(fileName, animCount);
#endif
-#if defined(SUPPORT_FILEFORMAT_GLTF)
+#if SUPPORT_FILEFORMAT_GLTF
if (IsFileExtension(fileName, ".gltf;.glb")) animations = LoadModelAnimationsGLTF(fileName, animCount);
#endif
return animations;
}
-// Update model animated bones transform matrices for a given frame
-// NOTE: Updated data is not uploaded to GPU but kept at model.meshes[i].boneMatrices[boneId],
-// to be uploaded to shader at drawing, in case GPU skinning is enabled
-void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame)
+// Update model animation data (vertex buffers / bone matrices) for a specific pose
+// NOTE 1: Request frame could be fractional, using a lerp interpolation between two frames
+// NOTE 2: Updated vertex animation data is uploaded to GPU in case of CPU skinning,
+// for GPU skinning, bone matrices are uploaded to shader on DrawModelEx()
+void UpdateModelAnimation(Model model, ModelAnimation anim, float frame)
{
- if ((anim.frameCount > 0) && (anim.bones != NULL) && (anim.framePoses != NULL))
+ if (model.boneMatrices == NULL) return;
+
+ //UpdateModelAnimationEx(model, anim, frame, anim, frame, 0.0f);
+
+ // Update model animated bones transform matrices for a given frame
+ if ((anim.keyframeCount > 0) && (model.skeleton.bones != NULL) && (anim.keyframePoses != NULL))
{
- if (frame >= anim.frameCount) frame = frame%anim.frameCount;
+ // Get frame and blending from frame factor required
+ int currentFrame = (int)frame;
+ int nextFrame = currentFrame + 1;
+ float blend = frame - currentFrame;
+ blend = Clamp(blend, 0.0f, 1.0f);
+ if (currentFrame >= anim.keyframeCount) currentFrame = currentFrame%anim.keyframeCount;
+ if (nextFrame >= anim.keyframeCount) nextFrame = nextFrame%anim.keyframeCount;
- // Get first mesh which have bones
- int firstMeshWithBones = -1;
+ Matrix bindPoseMatrix = { 0 };
+ Matrix currentPoseMatrix = { 0 };
- for (int i = 0; i < model.meshCount; i++)
+ // Update all bones and bone matrices of model
+ for (int boneIndex = 0; boneIndex < model.skeleton.boneCount; boneIndex++)
{
- if (model.meshes[i].boneMatrices)
- {
- if (firstMeshWithBones == -1)
- {
- firstMeshWithBones = i;
- break;
- }
- }
+ // Compute interpolated pose between current and next frame
+ // NOTE: Storing animation frame data in model.currentPose
+ model.currentPose[boneIndex].translation = Vector3Lerp(
+ anim.keyframePoses[currentFrame][boneIndex].translation,
+ anim.keyframePoses[nextFrame][boneIndex].translation, blend);
+ model.currentPose[boneIndex].rotation = QuaternionSlerp(
+ anim.keyframePoses[currentFrame][boneIndex].rotation,
+ anim.keyframePoses[nextFrame][boneIndex].rotation, blend);
+ model.currentPose[boneIndex].scale = Vector3Lerp(
+ anim.keyframePoses[currentFrame][boneIndex].scale,
+ anim.keyframePoses[nextFrame][boneIndex].scale, blend);
+
+ // Compute runtime bone matrix from model current pose
+ //-----------------------------------------------------------------------------------
+ Transform *bindPoseTransform = &model.skeleton.bindPose[boneIndex];
+ bindPoseMatrix = MatrixMultiply(
+ MatrixMultiply(MatrixScale(bindPoseTransform->scale.x, bindPoseTransform->scale.y, bindPoseTransform->scale.z),
+ QuaternionToMatrix(bindPoseTransform->rotation)),
+ MatrixTranslate(bindPoseTransform->translation.x, bindPoseTransform->translation.y, bindPoseTransform->translation.z));
+
+ Transform *currentPoseTransform = &model.currentPose[boneIndex];
+ currentPoseMatrix = MatrixMultiply(
+ MatrixMultiply(MatrixScale(currentPoseTransform->scale.x, currentPoseTransform->scale.y, currentPoseTransform->scale.z),
+ QuaternionToMatrix(currentPoseTransform->rotation)),
+ MatrixTranslate(currentPoseTransform->translation.x, currentPoseTransform->translation.y, currentPoseTransform->translation.z));
+
+ model.boneMatrices[boneIndex] = MatrixMultiply(MatrixInvert(bindPoseMatrix), currentPoseMatrix);
+ //-----------------------------------------------------------------------------------
}
- if (firstMeshWithBones != -1)
- {
- // Update all bones and boneMatrices of first mesh with bones
- for (int boneId = 0; boneId < anim.boneCount; boneId++)
- {
- Transform *bindTransform = &model.bindPose[boneId];
- Matrix bindMatrix = MatrixMultiply(MatrixMultiply(
- MatrixScale(bindTransform->scale.x, bindTransform->scale.y, bindTransform->scale.z),
- QuaternionToMatrix(bindTransform->rotation)),
- MatrixTranslate(bindTransform->translation.x, bindTransform->translation.y, bindTransform->translation.z));
-
- Transform *targetTransform = &anim.framePoses[frame][boneId];
- Matrix targetMatrix = MatrixMultiply(MatrixMultiply(
- MatrixScale(targetTransform->scale.x, targetTransform->scale.y, targetTransform->scale.z),
- QuaternionToMatrix(targetTransform->rotation)),
- MatrixTranslate(targetTransform->translation.x, targetTransform->translation.y, targetTransform->translation.z));
-
- model.meshes[firstMeshWithBones].boneMatrices[boneId] = MatrixMultiply(MatrixInvert(bindMatrix), targetMatrix);
- }
-
- // Update remaining meshes with bones
- // NOTE: Using deep copy because shallow copy results in double free with 'UnloadModel()'
- for (int i = firstMeshWithBones + 1; i < model.meshCount; i++)
- {
- if (model.meshes[i].boneMatrices)
- {
- memcpy(model.meshes[i].boneMatrices,
- model.meshes[firstMeshWithBones].boneMatrices,
- model.meshes[i].boneCount*sizeof(model.meshes[i].boneMatrices[0]));
- }
- }
- }
+ // CPU skinning, updates CPU buffers and uploads them to GPU
+ // NOTE: On GPU skinning not supported, use CPU skinning
+ UpdateModelAnimationVertexBuffers(model);
}
}
-// Update model animated bones transform matrices by interpolating between two different given frames of different ModelAnimation(could be same too)
-// NOTE: Updated data is not uploaded to GPU but kept at model.meshes[i].boneMatrices[boneId],
-// to be uploaded to shader at drawing, in case GPU skinning is enabled
-void UpdateModelAnimationBonesLerp(Model model, ModelAnimation animA, int frameA, ModelAnimation animB, int frameB, float value)
+// Update model animation data (vertex buffers / bone matrices) for a specific pose,
+// defined by two different animations at specific frames blended together
+// NOTE 1: Request frames could be fractional, using a lerp interpolation between two frames
+// NOTE 2: Updated vertex animation data is uploaded to GPU in case of CPU skinning,
+// for GPU skinning, bone matrices are uploaded to shader on DrawModelEx()
+void UpdateModelAnimationEx(Model model, ModelAnimation animA, float frameA, ModelAnimation animB, float frameB, float blend)
{
- if ((animA.frameCount > 0) && (animA.bones != NULL) && (animA.framePoses != NULL) &&
- (animB.frameCount > 0) && (animB.bones != NULL) && (animB.framePoses != NULL) &&
- (value >= 0.0f) && (value <= 1.0f))
+ if (model.boneMatrices == NULL) return;
+
+ if ((animA.keyframeCount > 0) && (animA.keyframePoses != NULL) &&
+ (animB.keyframeCount > 0) && (animB.keyframePoses != NULL) &&
+ (blend >= 0.0f) && (blend <= 1.0f))
{
- frameA = frameA % animA.frameCount;
- frameB = frameB % animB.frameCount;
+ // Inter-frame interpolation values for first animation
+ int currentFrameA = (int)frameA%animA.keyframeCount;
+ int nextFrameA = currentFrameA + 1;
+ float blendA = frameA - currentFrameA;
+ blendA = Clamp(blendA, 0.0f, 1.0f);
+ if (currentFrameA >= animA.keyframeCount) currentFrameA = currentFrameA%animA.keyframeCount;
+ if (nextFrameA >= animA.keyframeCount) nextFrameA = nextFrameA%animA.keyframeCount;
- for (int i = 0; i < model.meshCount; i++)
+ // Inter-frame interpolation values for second animation
+ int currentFrameB = (int)frameB%animB.keyframeCount;
+ int nextFrameB = currentFrameB + 1;
+ float blendB = frameB - currentFrameB;
+ blendB = Clamp(blendB, 0.0f, 1.0f);
+ if (currentFrameB >= animB.keyframeCount) currentFrameB = currentFrameB%animB.keyframeCount;
+ if (nextFrameB >= animB.keyframeCount) nextFrameB = nextFrameB%animB.keyframeCount;
+
+ Matrix bindPoseMatrix = { 0 };
+ Matrix currentPoseMatrix = { 0 };
+
+ for (int boneIndex = 0; boneIndex < model.skeleton.boneCount; boneIndex++)
{
- if (model.meshes[i].boneMatrices)
- {
- assert(model.meshes[i].boneCount == animA.boneCount);
- assert(model.meshes[i].boneCount == animB.boneCount);
+ // Get frame-interpolation for first animation
+ Vector3 frameATranslation = Vector3Lerp(
+ animA.keyframePoses[currentFrameA][boneIndex].translation,
+ animA.keyframePoses[nextFrameA][boneIndex].translation, blendA);
+ Quaternion frameARotation = QuaternionSlerp(
+ animA.keyframePoses[currentFrameA][boneIndex].rotation,
+ animA.keyframePoses[nextFrameA][boneIndex].rotation, blendA);
+ Vector3 frameAScale = Vector3Lerp(
+ animA.keyframePoses[currentFrameA][boneIndex].scale,
+ animA.keyframePoses[nextFrameA][boneIndex].scale, blendA);
- for (int boneId = 0; boneId < model.meshes[i].boneCount; boneId++)
- {
- Vector3 inTranslation = model.bindPose[boneId].translation;
- Quaternion inRotation = model.bindPose[boneId].rotation;
- Vector3 inScale = model.bindPose[boneId].scale;
+ // Get frame-interpolation for second animation
+ Vector3 frameBTranslation = Vector3Lerp(
+ animB.keyframePoses[currentFrameB][boneIndex].translation,
+ animB.keyframePoses[nextFrameB][boneIndex].translation, blendB);
+ Quaternion frameBRotation = QuaternionSlerp(
+ animB.keyframePoses[currentFrameB][boneIndex].rotation,
+ animB.keyframePoses[nextFrameB][boneIndex].rotation, blendB);
+ Vector3 frameBScale = Vector3Lerp(
+ animB.keyframePoses[currentFrameB][boneIndex].scale,
+ animB.keyframePoses[nextFrameB][boneIndex].scale, blendB);
- Vector3 outATranslation = animA.framePoses[frameA][boneId].translation;
- Quaternion outARotation = animA.framePoses[frameA][boneId].rotation;
- Vector3 outAScale = animA.framePoses[frameA][boneId].scale;
+ // Compute interpolated pose between both animations frames
+ // NOTE: Storing animation frame data in model.currentPose
+ model.currentPose[boneIndex].translation = Vector3Lerp(frameATranslation, frameBTranslation, blend);
+ model.currentPose[boneIndex].rotation = QuaternionSlerp(frameARotation, frameBRotation, blend);
+ model.currentPose[boneIndex].scale = Vector3Lerp(frameAScale, frameBScale, blend);
- Vector3 outBTranslation = animB.framePoses[frameB][boneId].translation;
- Quaternion outBRotation = animB.framePoses[frameB][boneId].rotation;
- Vector3 outBScale = animB.framePoses[frameB][boneId].scale;
+ // Compute runtime bone matrix from model current pose
+ //-----------------------------------------------------------------------------------
+ Transform *bindPoseTransform = &model.skeleton.bindPose[boneIndex];
+ bindPoseMatrix = MatrixMultiply(
+ MatrixMultiply(MatrixScale(bindPoseTransform->scale.x, bindPoseTransform->scale.y, bindPoseTransform->scale.z),
+ QuaternionToMatrix(bindPoseTransform->rotation)),
+ MatrixTranslate(bindPoseTransform->translation.x, bindPoseTransform->translation.y, bindPoseTransform->translation.z));
- Vector3 outTranslation = Vector3Lerp(outATranslation, outBTranslation, value);
- Quaternion outRotation = QuaternionSlerp(outARotation, outBRotation, value);
- Vector3 outScale = Vector3Lerp(outAScale, outBScale, value);
+ Transform *currentPoseTransform = &model.currentPose[boneIndex];
+ currentPoseMatrix = MatrixMultiply(
+ MatrixMultiply(MatrixScale(currentPoseTransform->scale.x, currentPoseTransform->scale.y, currentPoseTransform->scale.z),
+ QuaternionToMatrix(currentPoseTransform->rotation)),
+ MatrixTranslate(currentPoseTransform->translation.x, currentPoseTransform->translation.y, currentPoseTransform->translation.z));
- Vector3 invTranslation = Vector3RotateByQuaternion(Vector3Negate(inTranslation), QuaternionInvert(inRotation));
- Quaternion invRotation = QuaternionInvert(inRotation);
- Vector3 invScale = Vector3Divide((Vector3){ 1.0f, 1.0f, 1.0f }, inScale);
+ model.boneMatrices[boneIndex] = MatrixMultiply(MatrixInvert(bindPoseMatrix), currentPoseMatrix);
+ //-----------------------------------------------------------------------------------
- Vector3 boneTranslation = Vector3Add(
- Vector3RotateByQuaternion(Vector3Multiply(outScale, invTranslation),
- outRotation), outTranslation);
- Quaternion boneRotation = QuaternionMultiply(outRotation, invRotation);
- Vector3 boneScale = Vector3Multiply(outScale, invScale);
+ /*
+ Vector3 outATranslation = animA.keyframePoses[currentFrameA][boneIndex].translation;
+ Quaternion outARotation = animA.keyframePoses[currentFrameA][boneIndex].rotation;
+ Vector3 outAScale = animA.keyframePoses[currentFrameA][boneIndex].scale;
- Matrix boneMatrix = MatrixMultiply(MatrixMultiply(
- QuaternionToMatrix(boneRotation),
- MatrixTranslate(boneTranslation.x, boneTranslation.y, boneTranslation.z)),
- MatrixScale(boneScale.x, boneScale.y, boneScale.z));
+ Vector3 outBTranslation = animB.keyframePoses[currentFrameB][boneIndex].translation;
+ Quaternion outBRotation = animB.keyframePoses[currentFrameB][boneIndex].rotation;
+ Vector3 outBScale = animB.keyframePoses[currentFrameB][boneIndex].scale;
- model.meshes[i].boneMatrices[boneId] = boneMatrix;
- }
- }
+ // Invert bind pose transformation
+ Vector3 invBindTranslation = Vector3RotateByQuaternion(
+ Vector3Negate(model.skeleton.bindPose[boneIndex].translation),
+ QuaternionInvert(model.skeleton.bindPose[boneIndex].rotation));
+ Quaternion invBindRotation = QuaternionInvert(model.skeleton.bindPose[boneIndex].rotation);
+ Vector3 invBindScale = Vector3Divide((Vector3){ 1.0f, 1.0f, 1.0f }, model.skeleton.bindPose[boneIndex].scale);
+
+ Vector3 boneTranslation = Vector3Add(Vector3RotateByQuaternion(
+ Vector3Multiply(model.currentPose[boneIndex].scale, invBindTranslation),
+ model.currentPose[boneIndex].rotation),
+ model.currentPose[boneIndex].translation);
+ Quaternion boneRotation = QuaternionMultiply(model.currentPose[boneIndex].rotation, invBindRotation);
+ Vector3 boneScale = Vector3Multiply(model.currentPose[boneIndex].scale, invBindScale);
+
+ model.boneMatrices[boneIndex] = MatrixMultiply(
+ MatrixMultiply(QuaternionToMatrix(boneRotation),
+ MatrixTranslate(boneTranslation.x, boneTranslation.y, boneTranslation.z)),
+ MatrixScale(boneScale.x, boneScale.y, boneScale.z));
+ */
}
+
+ // CPU skinning, updates CPU buffers and uploads them to GPU (if available)
+ // NOTE: Fallback in case GPU skinning is not supported or enabled
+ UpdateModelAnimationVertexBuffers(model);
}
}
-// Update model vertex data (positions and normals) from mesh bone data
-// NOTE: Updated data is uploaded to GPU
-void UpdateModelVertsToCurrentBones(Model model)
+// Update model vertex animation buffers (positions and normals)
+// NOTE: Required for CPU skinning, uploads animated vertex buffers to GPU
+static void UpdateModelAnimationVertexBuffers(Model model)
{
- //UpdateModelAnimationBones(model, anim, frame); // TODO: Review
-
for (int m = 0; m < model.meshCount; m++)
{
Mesh mesh = model.meshes[m];
Vector3 animVertex = { 0 };
Vector3 animNormal = { 0 };
- const int vValues = mesh.vertexCount*3;
-
- int boneId = 0;
+ const int vertexValuesCount = mesh.vertexCount*3;
+
+ int boneIndex = 0;
int boneCounter = 0;
float boneWeight = 0.0f;
- bool updated = false; // Flag to check when anim vertex information is updated
+ bool bufferUpdateRequired = false; // Flag to check when anim vertex information is updated
- // Skip if missing bone data, causes segfault without on some models
- if ((mesh.boneWeights == NULL) || (mesh.boneIds == NULL)) continue;
+ // Skip if missing bone data or missing anim buffers initialization
+ if ((mesh.boneWeights == NULL) || (mesh.boneIndices == NULL) ||
+ (mesh.animVertices == NULL) || (mesh.animNormals == NULL)) continue;
- for (int vCounter = 0; vCounter < vValues; vCounter += 3)
+ for (int vCounter = 0; vCounter < vertexValuesCount; vCounter += 3)
{
mesh.animVertices[vCounter] = 0;
mesh.animVertices[vCounter + 1] = 0;
@@ -2442,23 +2488,23 @@ void UpdateModelVertsToCurrentBones(Model model)
for (int j = 0; j < 4; j++, boneCounter++)
{
boneWeight = mesh.boneWeights[boneCounter];
- boneId = mesh.boneIds[boneCounter];
+ boneIndex = mesh.boneIndices[boneCounter];
// Early stop when no transformation will be applied
if (boneWeight == 0.0f) continue;
animVertex = (Vector3){ mesh.vertices[vCounter], mesh.vertices[vCounter + 1], mesh.vertices[vCounter + 2] };
- animVertex = Vector3Transform(animVertex, model.meshes[m].boneMatrices[boneId]);
+ animVertex = Vector3Transform(animVertex, model.boneMatrices[boneIndex]);
mesh.animVertices[vCounter] += animVertex.x*boneWeight;
- mesh.animVertices[vCounter+1] += animVertex.y*boneWeight;
- mesh.animVertices[vCounter+2] += animVertex.z*boneWeight;
- updated = true;
+ mesh.animVertices[vCounter + 1] += animVertex.y*boneWeight;
+ mesh.animVertices[vCounter + 2] += animVertex.z*boneWeight;
+ bufferUpdateRequired = true;
// Normals processing
- // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals)
+ // NOTE: Using meshes.baseNormals (default normal) to calculate meshes.normals (animated normals)
if ((mesh.normals != NULL) && (mesh.animNormals != NULL ))
{
animNormal = (Vector3){ mesh.normals[vCounter], mesh.normals[vCounter + 1], mesh.normals[vCounter + 2] };
- animNormal = Vector3Transform(animNormal, MatrixTranspose(MatrixInvert(model.meshes[m].boneMatrices[boneId])));
+ animNormal = Vector3Transform(animNormal, MatrixTranspose(MatrixInvert(model.boneMatrices[boneIndex])));
mesh.animNormals[vCounter] += animNormal.x*boneWeight;
mesh.animNormals[vCounter + 1] += animNormal.y*boneWeight;
mesh.animNormals[vCounter + 2] += animNormal.z*boneWeight;
@@ -2466,58 +2512,41 @@ void UpdateModelVertsToCurrentBones(Model model)
}
}
- if (updated)
+ if (bufferUpdateRequired)
{
- rlUpdateVertexBuffer(mesh.vboId[0], mesh.animVertices, mesh.vertexCount*3*sizeof(float), 0); // Update vertex position
- if (mesh.normals != NULL) rlUpdateVertexBuffer(mesh.vboId[2], mesh.animNormals, mesh.vertexCount*3*sizeof(float), 0); // Update vertex normals
+ // Update GPU vertex buffers with updated data (position + normals)
+ rlUpdateVertexBuffer(mesh.vboId[SHADER_LOC_VERTEX_POSITION], mesh.animVertices, mesh.vertexCount*3*sizeof(float), 0);
+ if (mesh.normals != NULL) rlUpdateVertexBuffer(mesh.vboId[SHADER_LOC_VERTEX_NORMAL], mesh.animNormals, mesh.vertexCount*3*sizeof(float), 0);
}
}
}
-// at least 2x speed up vs the old method
-// Update model animated vertex data (positions and normals) for a given frame
-// NOTE: Updated data is uploaded to GPU
-void UpdateModelAnimation(Model model, ModelAnimation anim, int frame)
-{
- UpdateModelAnimationBones(model,anim,frame);
- UpdateModelVertsToCurrentBones(model);
-}
-
// Unload animation array data
void UnloadModelAnimations(ModelAnimation *animations, int animCount)
{
- for (int i = 0; i < animCount; i++) UnloadModelAnimation(animations[i]);
+ for (int a = 0; a < animCount; a++)
+ {
+ for (int i = 0; i < animations[a].keyframeCount; i++)
+ RL_FREE(animations[a].keyframePoses[i]);
+
+ RL_FREE(animations[a].keyframePoses);
+ }
+
RL_FREE(animations);
}
-// Unload animation data
-void UnloadModelAnimation(ModelAnimation anim)
-{
- for (int i = 0; i < anim.frameCount; i++) RL_FREE(anim.framePoses[i]);
-
- RL_FREE(anim.bones);
- RL_FREE(anim.framePoses);
-}
-
// Check model animation skeleton match
// NOTE: Only number of bones and parent connections are checked
bool IsModelAnimationValid(Model model, ModelAnimation anim)
{
int result = true;
- if (model.boneCount != anim.boneCount) result = false;
- else
- {
- for (int i = 0; i < model.boneCount; i++)
- {
- if (model.bones[i].parent != anim.bones[i].parent) { result = false; break; }
- }
- }
+ if (model.skeleton.boneCount != anim.boneCount) result = false;
return result;
}
-#if defined(SUPPORT_MESH_GENERATION)
+#if SUPPORT_MESH_GENERATION
// Generate polygonal mesh
Mesh GenMeshPoly(int sides, float radius)
{
@@ -3338,7 +3367,7 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
Vector2 *mapTexcoords = (Vector2 *)RL_MALLOC(maxTriangles*3*sizeof(Vector2));
Vector3 *mapNormals = (Vector3 *)RL_MALLOC(maxTriangles*3*sizeof(Vector3));
- // Define the 6 normals of the cube, we will combine them accordingly later...
+ // Define the 6 normals of the cube, combined accordingly later
Vector3 n1 = { 1.0f, 0.0f, 0.0f };
Vector3 n2 = { -1.0f, 0.0f, 0.0f };
Vector3 n3 = { 0.0f, 1.0f, 0.0f };
@@ -3346,7 +3375,8 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
Vector3 n5 = { 0.0f, 0.0f, -1.0f };
Vector3 n6 = { 0.0f, 0.0f, 1.0f };
- // NOTE: We use texture rectangles to define different textures for top-bottom-front-back-right-left (6)
+ // NOTE: Using texture rectangles to define different
+ // textures for top-bottom-front-back-right-left (6)
typedef struct RectangleF {
float x;
float y;
@@ -3365,7 +3395,7 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
{
for (int x = 0; x < cubicmap.width; x++)
{
- // Define the 8 vertex of the cube, we will combine them accordingly later...
+ // Define the 8 vertex of the cube, to be combined accordingly later
Vector3 v1 = { w*(x - 0.5f), h2, h*(z - 0.5f) };
Vector3 v2 = { w*(x - 0.5f), h2, h*(z + 0.5f) };
Vector3 v3 = { w*(x + 0.5f), h2, h*(z + 0.5f) };
@@ -3375,7 +3405,7 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
Vector3 v7 = { w*(x - 0.5f), 0, h*(z + 0.5f) };
Vector3 v8 = { w*(x + 0.5f), 0, h*(z + 0.5f) };
- // We check pixel color to be WHITE -> draw full cube
+ // Check pixel color to be WHITE -> draw full cube
if (COLOR_EQUAL(pixels[z*cubicmap.width + x], WHITE))
{
// Define triangles and checking collateral cubes
@@ -3552,7 +3582,7 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
tcCounter += 6;
}
}
- // We check pixel color to be BLACK, we will only draw floor and roof
+ // Check pixel color to be BLACK, in that case only drawing floor and roof
else if (COLOR_EQUAL(pixels[z*cubicmap.width + x], BLACK))
{
// Define top triangles (2 tris, 6 vertex --> v1-v2-v3, v1-v3-v4)
@@ -3660,7 +3690,7 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
return mesh;
}
-#endif // SUPPORT_MESH_GENERATION
+#endif // SUPPORT_MESH_GENERATION
// Compute mesh bounding box limits
// NOTE: minVertex and maxVertex should be transformed by model transform matrix
@@ -3883,17 +3913,31 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota
for (int i = 0; i < model.meshCount; i++)
{
- Color color = model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color;
+ Material mat = model.materials[model.meshMaterial[i]];
+ Color colDiffuse = mat.maps[MATERIAL_MAP_DIFFUSE].color;
- Color colorTint = WHITE;
- colorTint.r = (unsigned char)(((int)color.r*(int)tint.r)/255);
- colorTint.g = (unsigned char)(((int)color.g*(int)tint.g)/255);
- colorTint.b = (unsigned char)(((int)color.b*(int)tint.b)/255);
- colorTint.a = (unsigned char)(((int)color.a*(int)tint.a)/255);
+ // Applying color tint directly to material diffuse map,
+ // because is comes as an input paramter to the function
+ Color colTinted = { 0 };
+ colTinted.r = (unsigned char)(((int)colDiffuse.r*(int)tint.r)/255);
+ colTinted.g = (unsigned char)(((int)colDiffuse.g*(int)tint.g)/255);
+ colTinted.b = (unsigned char)(((int)colDiffuse.b*(int)tint.b)/255);
+ colTinted.a = (unsigned char)(((int)colDiffuse.a*(int)tint.a)/255);
- model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = colorTint;
- DrawMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform);
- model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = color;
+ mat.maps[MATERIAL_MAP_DIFFUSE].color = colTinted;
+
+ // Upload runtime bone transforms matrices, to compute skinning on the shader (GPU-skinning)
+ // NOTE: Required location must be found and Mesh bones indices and weights must be also uploaded to shader
+ if ((mat.shader.locs != NULL) && (mat.shader.locs[SHADER_LOC_MATRIX_BONETRANSFORMS] != -1) && (model.boneMatrices != NULL))
+ {
+ rlEnableShader(mat.shader.id); // Enable shader to set bone transform matrices
+ rlSetUniformMatrices(mat.shader.locs[SHADER_LOC_MATRIX_BONETRANSFORMS], model.boneMatrices, model.skeleton.boneCount);
+ }
+
+ DrawMesh(model.meshes[i], mat, model.transform);
+
+ // Restore material diffuse map color (before tint applied)
+ mat.maps[MATERIAL_MAP_DIFFUSE].color = colDiffuse;
}
}
@@ -4055,8 +4099,8 @@ bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, floa
{
bool collision = false;
- // Simple way to check for collision, just checking distance between two points
- // Unfortunately, sqrtf() is a costly operation, so we avoid it with following solution
+ // Simple way to check for collision, checking distance between two points
+ // Unfortunately, sqrtf() is a costly operation, so avoid it with following solution
/*
float dx = center1.x - center2.x; // X distance between centers
float dy = center1.y - center2.y; // Y distance between centers
@@ -4184,7 +4228,7 @@ RayCollision GetRayCollisionBox(Ray ray, BoundingBox box)
// Get vector center point->hit point
collision.normal = Vector3Subtract(collision.point, collision.normal);
// Scale vector to unit cube
- // NOTE: We use an additional .01 to fix numerical errors
+ // NOTE: Use an additional .01 to fix numerical errors
collision.normal = Vector3Scale(collision.normal, 2.01f);
collision.normal = Vector3Divide(collision.normal, Vector3Subtract(box.max, box.min));
// The relevant elements of the vector are now slightly larger than 1.0f (or smaller than -1.0f)
@@ -4333,7 +4377,7 @@ RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Ve
//----------------------------------------------------------------------------------
// Module Internal Functions Definition
//----------------------------------------------------------------------------------
-#if defined(SUPPORT_FILEFORMAT_IQM) || defined(SUPPORT_FILEFORMAT_GLTF)
+#if SUPPORT_FILEFORMAT_IQM || SUPPORT_FILEFORMAT_GLTF
// Build pose from parent joints
// NOTE: Required for animations loading (required by IQM and GLTF)
static void BuildPoseFromParentJoints(BoneInfo *bones, int boneCount, Transform *transforms)
@@ -4344,7 +4388,7 @@ static void BuildPoseFromParentJoints(BoneInfo *bones, int boneCount, Transform
{
if (bones[i].parent > i)
{
- TRACELOG(LOG_WARNING, "Assumes bones are toplogically sorted, but bone %d has parent %d. Skipping.", i, bones[i].parent);
+ TRACELOG(LOG_WARNING, "Skipping bone not topologically sorted: Bone %d has parent %d", i, bones[i].parent);
continue;
}
transforms[i].rotation = QuaternionMultiply(transforms[bones[i].parent].rotation, transforms[i].rotation);
@@ -4357,13 +4401,12 @@ static void BuildPoseFromParentJoints(BoneInfo *bones, int boneCount, Transform
}
#endif
-#if defined(SUPPORT_FILEFORMAT_OBJ)
+#if SUPPORT_FILEFORMAT_OBJ
// Load OBJ mesh data
-//
-// Keep the following information in mind when reading this
+// Notes to keep in mind:
// - A mesh is created for every material present in the obj file
-// - the model.meshCount is therefore the materialCount returned from tinyobj
-// - the mesh is automatically triangulated by tinyobj
+// - The model.meshCount is therefore the materialCount returned from tinyobj
+// - The mesh is automatically triangulated by tinyobj
static Model LoadOBJ(const char *fileName)
{
tinyobj_attrib_t objAttributes = { 0 };
@@ -4426,7 +4469,7 @@ static Model LoadOBJ(const char *fileName)
}
else if ((lastMaterial != -1) && (objAttributes.material_ids[faceId] != lastMaterial))
{
- meshIndex++; // If this is a new material, we need to allocate a new mesh
+ meshIndex++; // If this is a new material, a new mesh is allocated
}
lastMaterial = objAttributes.material_ids[faceId];
@@ -4442,7 +4485,7 @@ static Model LoadOBJ(const char *fileName)
model.materialCount = objMaterialCount;
model.materials = (Material *)MemAlloc(sizeof(Material)*objMaterialCount);
}
- else // We must allocate at least one material
+ else // Allocate at least one material
{
model.materialCount = 1;
model.materials = (Material *)MemAlloc(sizeof(Material)*1);
@@ -4465,7 +4508,7 @@ static Model LoadOBJ(const char *fileName)
// Walk all the faces
for (unsigned int faceId = 0; faceId < objAttributes.num_faces; faceId++)
{
- bool newMesh = false; // Do we need a new mesh?
+ bool newMesh = false; // Is a new mesh required?
if (faceId >= nextShapeEnd)
{
// Try to find the last vert in the next shape
@@ -4510,7 +4553,8 @@ static Model LoadOBJ(const char *fileName)
model.meshes[i].texcoords = (float *)MemAlloc(sizeof(float)*vertexCount*2);
model.meshes[i].colors = (unsigned char *)MemAlloc(sizeof(unsigned char)*vertexCount*4);
#else
- if (objAttributes.texcoords != NULL && objAttributes.num_texcoords > 0) model.meshes[i].texcoords = (float *)MemAlloc(sizeof(float)*vertexCount*2);
+ if (objAttributes.texcoords != NULL && objAttributes.num_texcoords > 0)
+ model.meshes[i].texcoords = (float *)MemAlloc(sizeof(float)*vertexCount*2);
else model.meshes[i].texcoords = NULL;
model.meshes[i].colors = NULL;
#endif
@@ -4534,7 +4578,7 @@ static Model LoadOBJ(const char *fileName)
// Walk all the faces
for (unsigned int faceId = 0; faceId < objAttributes.num_faces; faceId++)
{
- bool newMesh = false; // Do we need a new mesh?
+ bool newMesh = false; // Is a new mesh required?
if (faceId >= nextShapeEnd)
{
// Try to find the last vert in the next shape
@@ -4544,7 +4588,7 @@ static Model LoadOBJ(const char *fileName)
newMesh = true;
}
- // If this is a new material, we need to allocate a new mesh
+ // If this is a new material, a new mesh is allocated
if (lastMaterial != -1 && objAttributes.material_ids[faceId] != lastMaterial) newMesh = true;
lastMaterial = objAttributes.material_ids[faceId];
@@ -4608,16 +4652,16 @@ static Model LoadOBJ(const char *fileName)
}
#endif
-#if defined(SUPPORT_FILEFORMAT_IQM)
+#if SUPPORT_FILEFORMAT_IQM
// Load IQM mesh data
static Model LoadIQM(const char *fileName)
{
- #define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number
- #define IQM_VERSION 2 // only IQM version 2 supported
+ #define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number
+ #define IQM_VERSION 2 // Only IQM version 2 supported
- #define BONE_NAME_LENGTH 32 // BoneInfo name string length
- #define MESH_NAME_LENGTH 32 // Mesh name string length
- #define MATERIAL_NAME_LENGTH 32 // Material name string length
+ #define BONE_NAME_LENGTH 32 // BoneInfo name string length
+ #define MESH_NAME_LENGTH 32 // Mesh name string length
+ #define MATERIAL_NAME_LENGTH 32 // Material name string length
int dataSize = 0;
unsigned char *fileData = LoadFileData(fileName, &dataSize);
@@ -4762,11 +4806,11 @@ static Model LoadIQM(const char *fileName)
for (int i = 0; i < model.meshCount; i++)
{
- //fseek(iqmFile, iqmHeader->ofs_text + imesh[i].name, SEEK_SET);
+ //fseek(iqmFile, iqmHeader->ofs_text + imesh[a].name, SEEK_SET);
//fread(name, sizeof(char), MESH_NAME_LENGTH, iqmFile);
memcpy(name, fileDataPtr + iqmHeader->ofs_text + imesh[i].name, MESH_NAME_LENGTH*sizeof(char));
- //fseek(iqmFile, iqmHeader->ofs_text + imesh[i].material, SEEK_SET);
+ //fseek(iqmFile, iqmHeader->ofs_text + imesh[a].material, SEEK_SET);
//fread(material, sizeof(char), MATERIAL_NAME_LENGTH, iqmFile);
memcpy(material, fileDataPtr + iqmHeader->ofs_text + imesh[i].material, MATERIAL_NAME_LENGTH*sizeof(char));
@@ -4783,16 +4827,18 @@ static Model LoadIQM(const char *fileName)
model.meshes[i].normals = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); // Default vertex normals
model.meshes[i].texcoords = (float *)RL_CALLOC(model.meshes[i].vertexCount*2, sizeof(float)); // Default vertex texcoords
- model.meshes[i].boneIds = (unsigned char *)RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(unsigned char)); // Up-to 4 bones supported!
+ model.meshes[i].boneIndices = (unsigned char *)RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(unsigned char)); // Up-to 4 bones supported!
model.meshes[i].boneWeights = (float *)RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(float)); // Up-to 4 bones supported!
model.meshes[i].triangleCount = imesh[i].num_triangles;
model.meshes[i].indices = (unsigned short *)RL_CALLOC(model.meshes[i].triangleCount*3, sizeof(unsigned short));
- // Animated vertex data, what we actually process for rendering
+#if !SUPPORT_GPU_SKINNING
+ // Animated vertex data, processed for rendering
// NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning)
model.meshes[i].animVertices = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));
model.meshes[i].animNormals = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));
+#endif
}
// Triangles data processing
@@ -4808,7 +4854,7 @@ static Model LoadIQM(const char *fileName)
for (unsigned int i = imesh[m].first_triangle; i < (imesh[m].first_triangle + imesh[m].num_triangles); i++)
{
// IQM triangles indexes are stored in counter-clockwise, but raylib processes the index in linear order,
- // expecting they point to the counter-clockwise vertex triangle, so we need to reverse triangle indexes
+ // expecting they point to the counter-clockwise vertex triangle, so triangle indexes need to be reversed
// NOTE: raylib renders vertex data in counter-clockwise order (standard convention) by default
model.meshes[m].indices[tcounter + 2] = tri[i].vertex[0] - imesh[m].first_vertex;
model.meshes[m].indices[tcounter + 1] = tri[i].vertex[1] - imesh[m].first_vertex;
@@ -4830,7 +4876,7 @@ static Model LoadIQM(const char *fileName)
case IQM_POSITION:
{
vertex = (float *)RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float));
- //fseek(iqmFile, va[i].offset, SEEK_SET);
+ //fseek(iqmFile, va[a].offset, SEEK_SET);
//fread(vertex, iqmHeader->num_vertexes*3*sizeof(float), 1, iqmFile);
memcpy(vertex, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*3*sizeof(float));
@@ -4840,7 +4886,7 @@ static Model LoadIQM(const char *fileName)
for (unsigned int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++)
{
model.meshes[m].vertices[vCounter] = vertex[i];
- model.meshes[m].animVertices[vCounter] = vertex[i];
+ if (model.meshes[m].animVertices != NULL) model.meshes[m].animVertices[vCounter] = vertex[i];
vCounter++;
}
}
@@ -4848,7 +4894,7 @@ static Model LoadIQM(const char *fileName)
case IQM_NORMAL:
{
normal = (float *)RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float));
- //fseek(iqmFile, va[i].offset, SEEK_SET);
+ //fseek(iqmFile, va[a].offset, SEEK_SET);
//fread(normal, iqmHeader->num_vertexes*3*sizeof(float), 1, iqmFile);
memcpy(normal, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*3*sizeof(float));
@@ -4858,7 +4904,7 @@ static Model LoadIQM(const char *fileName)
for (unsigned int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++)
{
model.meshes[m].normals[vCounter] = normal[i];
- model.meshes[m].animNormals[vCounter] = normal[i];
+ if (model.meshes[m].animNormals != NULL) model.meshes[m].animNormals[vCounter] = normal[i];
vCounter++;
}
}
@@ -4866,7 +4912,7 @@ static Model LoadIQM(const char *fileName)
case IQM_TEXCOORD:
{
text = (float *)RL_MALLOC(iqmHeader->num_vertexes*2*sizeof(float));
- //fseek(iqmFile, va[i].offset, SEEK_SET);
+ //fseek(iqmFile, va[a].offset, SEEK_SET);
//fread(text, iqmHeader->num_vertexes*2*sizeof(float), 1, iqmFile);
memcpy(text, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*2*sizeof(float));
@@ -4883,7 +4929,7 @@ static Model LoadIQM(const char *fileName)
case IQM_BLENDINDEXES:
{
blendi = (char *)RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(char));
- //fseek(iqmFile, va[i].offset, SEEK_SET);
+ //fseek(iqmFile, va[a].offset, SEEK_SET);
//fread(blendi, iqmHeader->num_vertexes*4*sizeof(char), 1, iqmFile);
memcpy(blendi, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(char));
@@ -4892,7 +4938,7 @@ static Model LoadIQM(const char *fileName)
int boneCounter = 0;
for (unsigned int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++)
{
- model.meshes[m].boneIds[boneCounter] = blendi[i];
+ model.meshes[m].boneIndices[boneCounter] = blendi[i];
boneCounter++;
}
}
@@ -4900,7 +4946,7 @@ static Model LoadIQM(const char *fileName)
case IQM_BLENDWEIGHTS:
{
blendw = (unsigned char *)RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char));
- //fseek(iqmFile, va[i].offset, SEEK_SET);
+ //fseek(iqmFile, va[a].offset, SEEK_SET);
//fread(blendw, iqmHeader->num_vertexes*4*sizeof(unsigned char), 1, iqmFile);
memcpy(blendw, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(unsigned char));
@@ -4917,7 +4963,7 @@ static Model LoadIQM(const char *fileName)
case IQM_COLOR:
{
color = (unsigned char *)RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char));
- //fseek(iqmFile, va[i].offset, SEEK_SET);
+ //fseek(iqmFile, va[a].offset, SEEK_SET);
//fread(blendw, iqmHeader->num_vertexes*4*sizeof(unsigned char), 1, iqmFile);
memcpy(color, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(unsigned char));
@@ -4942,45 +4988,39 @@ static Model LoadIQM(const char *fileName)
//fread(ijoint, sizeof(IQMJoint), iqmHeader->num_joints, iqmFile);
memcpy(ijoint, fileDataPtr + iqmHeader->ofs_joints, iqmHeader->num_joints*sizeof(IQMJoint));
- model.boneCount = iqmHeader->num_joints;
- model.bones = (BoneInfo *)RL_MALLOC(iqmHeader->num_joints*sizeof(BoneInfo));
- model.bindPose = (Transform *)RL_MALLOC(iqmHeader->num_joints*sizeof(Transform));
+ model.skeleton.boneCount = iqmHeader->num_joints;
+ model.skeleton.bones = (BoneInfo *)RL_CALLOC(iqmHeader->num_joints, sizeof(BoneInfo));
+ model.skeleton.bindPose = (Transform *)RL_CALLOC(iqmHeader->num_joints, sizeof(Transform));
for (unsigned int i = 0; i < iqmHeader->num_joints; i++)
{
// Bones
- model.bones[i].parent = ijoint[i].parent;
- //fseek(iqmFile, iqmHeader->ofs_text + ijoint[i].name, SEEK_SET);
- //fread(model.bones[i].name, sizeof(char), BONE_NAME_LENGTH, iqmFile);
- memcpy(model.bones[i].name, fileDataPtr + iqmHeader->ofs_text + ijoint[i].name, BONE_NAME_LENGTH*sizeof(char));
+ model.skeleton.bones[i].parent = ijoint[i].parent;
+ //fseek(iqmFile, iqmHeader->ofs_text + ijoint[a].name, SEEK_SET);
+ //fread(model.bones[a].name, sizeof(char), BONE_NAME_LENGTH, iqmFile);
+ memcpy(model.skeleton.bones[i].name, fileDataPtr + iqmHeader->ofs_text + ijoint[i].name, BONE_NAME_LENGTH*sizeof(char));
// Bind pose (base pose)
- model.bindPose[i].translation.x = ijoint[i].translate[0];
- model.bindPose[i].translation.y = ijoint[i].translate[1];
- model.bindPose[i].translation.z = ijoint[i].translate[2];
+ model.skeleton.bindPose[i].translation.x = ijoint[i].translate[0];
+ model.skeleton.bindPose[i].translation.y = ijoint[i].translate[1];
+ model.skeleton.bindPose[i].translation.z = ijoint[i].translate[2];
- model.bindPose[i].rotation.x = ijoint[i].rotate[0];
- model.bindPose[i].rotation.y = ijoint[i].rotate[1];
- model.bindPose[i].rotation.z = ijoint[i].rotate[2];
- model.bindPose[i].rotation.w = ijoint[i].rotate[3];
+ model.skeleton.bindPose[i].rotation.x = ijoint[i].rotate[0];
+ model.skeleton.bindPose[i].rotation.y = ijoint[i].rotate[1];
+ model.skeleton.bindPose[i].rotation.z = ijoint[i].rotate[2];
+ model.skeleton.bindPose[i].rotation.w = ijoint[i].rotate[3];
- model.bindPose[i].scale.x = ijoint[i].scale[0];
- model.bindPose[i].scale.y = ijoint[i].scale[1];
- model.bindPose[i].scale.z = ijoint[i].scale[2];
+ model.skeleton.bindPose[i].scale.x = ijoint[i].scale[0];
+ model.skeleton.bindPose[i].scale.y = ijoint[i].scale[1];
+ model.skeleton.bindPose[i].scale.z = ijoint[i].scale[2];
}
- BuildPoseFromParentJoints(model.bones, model.boneCount, model.bindPose);
+ BuildPoseFromParentJoints(model.skeleton.bones, model.skeleton.boneCount, model.skeleton.bindPose);
- for (int i = 0; i < model.meshCount; i++)
- {
- model.meshes[i].boneCount = model.boneCount;
- model.meshes[i].boneMatrices = (Matrix *)RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix));
-
- for (int j = 0; j < model.meshes[i].boneCount; j++)
- {
- model.meshes[i].boneMatrices[j] = MatrixIdentity();
- }
- }
+ // Initialize runtime animation data: current pose and bone matrices
+ model.currentPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform));
+ model.boneMatrices = (Matrix *)RL_CALLOC(model.skeleton.boneCount, sizeof(Matrix));
+ for (int j = 0; j < model.skeleton.boneCount; j++) model.boneMatrices[j] = MatrixIdentity();
UnloadFileData(fileData);
@@ -5078,37 +5118,42 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou
//fread(anim, sizeof(IQMAnim), iqmHeader->num_anims, iqmFile);
memcpy(anim, fileDataPtr + iqmHeader->ofs_anims, iqmHeader->num_anims*sizeof(IQMAnim));
- ModelAnimation *animations = (ModelAnimation *)RL_MALLOC(iqmHeader->num_anims*sizeof(ModelAnimation));
+ ModelAnimation *animations = (ModelAnimation *)RL_CALLOC(iqmHeader->num_anims, sizeof(ModelAnimation));
// frameposes
- unsigned short *framedata = (unsigned short *)RL_MALLOC(iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short));
+ unsigned short *framedata = (unsigned short *)RL_CALLOC(iqmHeader->num_frames*iqmHeader->num_framechannels, sizeof(unsigned short));
//fseek(iqmFile, iqmHeader->ofs_frames, SEEK_SET);
//fread(framedata, sizeof(unsigned short), iqmHeader->num_frames*iqmHeader->num_framechannels, iqmFile);
memcpy(framedata, fileDataPtr + iqmHeader->ofs_frames, iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short));
// joints
- IQMJoint *joints = (IQMJoint *)RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint));
+ IQMJoint *joints = (IQMJoint *)RL_CALLOC(iqmHeader->num_joints, sizeof(IQMJoint));
memcpy(joints, fileDataPtr + iqmHeader->ofs_joints, iqmHeader->num_joints*sizeof(IQMJoint));
for (unsigned int a = 0; a < iqmHeader->num_anims; a++)
{
- animations[a].frameCount = anim[a].num_frames;
animations[a].boneCount = iqmHeader->num_poses;
- animations[a].bones = (BoneInfo *)RL_MALLOC(iqmHeader->num_poses*sizeof(BoneInfo));
- animations[a].framePoses = (Transform **)RL_MALLOC(anim[a].num_frames*sizeof(Transform *));
+ BoneInfo *bones = (BoneInfo *)RL_CALLOC(iqmHeader->num_poses, sizeof(BoneInfo));
+
+ animations[a].keyframeCount = anim[a].num_frames;
+ animations[a].keyframePoses = (Transform **)RL_CALLOC(anim[a].num_frames, sizeof(Transform *));
memcpy(animations[a].name, fileDataPtr + iqmHeader->ofs_text + anim[a].name, 32);
- TRACELOG(LOG_INFO, "IQM Anim %s", animations[a].name);
- //animations[a].framerate = anim.framerate; // TODO: Use animation framerate data?
+ // TODO: Use animation framerate data?
+ //animations[a].framerate = anim.framerate;
+
+ TRACELOG(LOG_INFO, "MODEL: [%s] Loaded animation: %s | Frames: %d | Framerate: %f", fileName, animations[a].name, animations[a].keyframeCount, anim[a].framerate);
for (unsigned int j = 0; j < iqmHeader->num_poses; j++)
{
- // If animations and skeleton are in the same file, copy bone names to anim
- if (iqmHeader->num_joints > 0) memcpy(animations[a].bones[j].name, fileDataPtr + iqmHeader->ofs_text + joints[j].name, BONE_NAME_LENGTH*sizeof(char));
- else memcpy(animations[a].bones[j].name, "ANIMJOINTNAME", 13); // Default bone name otherwise
- animations[a].bones[j].parent = poses[j].parent;
+ bones[j].parent = poses[j].parent;
+
+ // NOTE: No need to store bones names, bones only required to generate keyframe poses
+ //if (iqmHeader->num_joints > 0) memcpy(bones[j].name, fileDataPtr + iqmHeader->ofs_text + joints[j].name, BONE_NAME_LENGTH*sizeof(char));
+ //else memcpy(bones[j].name, "ANIMJOINTNAME", 13); // Default bone name otherwise
}
- for (unsigned int j = 0; j < anim[a].num_frames; j++) animations[a].framePoses[j] = (Transform *)RL_MALLOC(iqmHeader->num_poses*sizeof(Transform));
+ for (unsigned int j = 0; j < anim[a].num_frames; j++)
+ animations[a].keyframePoses[j] = (Transform *)RL_MALLOC(iqmHeader->num_poses*sizeof(Transform));
int dcounter = anim[a].first_frame*iqmHeader->num_framechannels;
@@ -5116,87 +5161,87 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou
{
for (unsigned int i = 0; i < iqmHeader->num_poses; i++)
{
- animations[a].framePoses[frame][i].translation.x = poses[i].channeloffset[0];
+ animations[a].keyframePoses[frame][i].translation.x = poses[i].channeloffset[0];
if (poses[i].mask & 0x01)
{
- animations[a].framePoses[frame][i].translation.x += framedata[dcounter]*poses[i].channelscale[0];
+ animations[a].keyframePoses[frame][i].translation.x += framedata[dcounter]*poses[i].channelscale[0];
dcounter++;
}
- animations[a].framePoses[frame][i].translation.y = poses[i].channeloffset[1];
+ animations[a].keyframePoses[frame][i].translation.y = poses[i].channeloffset[1];
if (poses[i].mask & 0x02)
{
- animations[a].framePoses[frame][i].translation.y += framedata[dcounter]*poses[i].channelscale[1];
+ animations[a].keyframePoses[frame][i].translation.y += framedata[dcounter]*poses[i].channelscale[1];
dcounter++;
}
- animations[a].framePoses[frame][i].translation.z = poses[i].channeloffset[2];
+ animations[a].keyframePoses[frame][i].translation.z = poses[i].channeloffset[2];
if (poses[i].mask & 0x04)
{
- animations[a].framePoses[frame][i].translation.z += framedata[dcounter]*poses[i].channelscale[2];
+ animations[a].keyframePoses[frame][i].translation.z += framedata[dcounter]*poses[i].channelscale[2];
dcounter++;
}
- animations[a].framePoses[frame][i].rotation.x = poses[i].channeloffset[3];
+ animations[a].keyframePoses[frame][i].rotation.x = poses[i].channeloffset[3];
if (poses[i].mask & 0x08)
{
- animations[a].framePoses[frame][i].rotation.x += framedata[dcounter]*poses[i].channelscale[3];
+ animations[a].keyframePoses[frame][i].rotation.x += framedata[dcounter]*poses[i].channelscale[3];
dcounter++;
}
- animations[a].framePoses[frame][i].rotation.y = poses[i].channeloffset[4];
+ animations[a].keyframePoses[frame][i].rotation.y = poses[i].channeloffset[4];
if (poses[i].mask & 0x10)
{
- animations[a].framePoses[frame][i].rotation.y += framedata[dcounter]*poses[i].channelscale[4];
+ animations[a].keyframePoses[frame][i].rotation.y += framedata[dcounter]*poses[i].channelscale[4];
dcounter++;
}
- animations[a].framePoses[frame][i].rotation.z = poses[i].channeloffset[5];
+ animations[a].keyframePoses[frame][i].rotation.z = poses[i].channeloffset[5];
if (poses[i].mask & 0x20)
{
- animations[a].framePoses[frame][i].rotation.z += framedata[dcounter]*poses[i].channelscale[5];
+ animations[a].keyframePoses[frame][i].rotation.z += framedata[dcounter]*poses[i].channelscale[5];
dcounter++;
}
- animations[a].framePoses[frame][i].rotation.w = poses[i].channeloffset[6];
+ animations[a].keyframePoses[frame][i].rotation.w = poses[i].channeloffset[6];
if (poses[i].mask & 0x40)
{
- animations[a].framePoses[frame][i].rotation.w += framedata[dcounter]*poses[i].channelscale[6];
+ animations[a].keyframePoses[frame][i].rotation.w += framedata[dcounter]*poses[i].channelscale[6];
dcounter++;
}
- animations[a].framePoses[frame][i].scale.x = poses[i].channeloffset[7];
+ animations[a].keyframePoses[frame][i].scale.x = poses[i].channeloffset[7];
if (poses[i].mask & 0x80)
{
- animations[a].framePoses[frame][i].scale.x += framedata[dcounter]*poses[i].channelscale[7];
+ animations[a].keyframePoses[frame][i].scale.x += framedata[dcounter]*poses[i].channelscale[7];
dcounter++;
}
- animations[a].framePoses[frame][i].scale.y = poses[i].channeloffset[8];
+ animations[a].keyframePoses[frame][i].scale.y = poses[i].channeloffset[8];
if (poses[i].mask & 0x100)
{
- animations[a].framePoses[frame][i].scale.y += framedata[dcounter]*poses[i].channelscale[8];
+ animations[a].keyframePoses[frame][i].scale.y += framedata[dcounter]*poses[i].channelscale[8];
dcounter++;
}
- animations[a].framePoses[frame][i].scale.z = poses[i].channeloffset[9];
+ animations[a].keyframePoses[frame][i].scale.z = poses[i].channeloffset[9];
if (poses[i].mask & 0x200)
{
- animations[a].framePoses[frame][i].scale.z += framedata[dcounter]*poses[i].channelscale[9];
+ animations[a].keyframePoses[frame][i].scale.z += framedata[dcounter]*poses[i].channelscale[9];
dcounter++;
}
- animations[a].framePoses[frame][i].rotation = QuaternionNormalize(animations[a].framePoses[frame][i].rotation);
+ animations[a].keyframePoses[frame][i].rotation = QuaternionNormalize(animations[a].keyframePoses[frame][i].rotation);
}
}
@@ -5205,15 +5250,17 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou
{
for (int i = 0; i < animations[a].boneCount; i++)
{
- if (animations[a].bones[i].parent >= 0)
+ if (bones[i].parent >= 0)
{
- animations[a].framePoses[frame][i].rotation = QuaternionMultiply(animations[a].framePoses[frame][animations[a].bones[i].parent].rotation, animations[a].framePoses[frame][i].rotation);
- animations[a].framePoses[frame][i].translation = Vector3RotateByQuaternion(animations[a].framePoses[frame][i].translation, animations[a].framePoses[frame][animations[a].bones[i].parent].rotation);
- animations[a].framePoses[frame][i].translation = Vector3Add(animations[a].framePoses[frame][i].translation, animations[a].framePoses[frame][animations[a].bones[i].parent].translation);
- animations[a].framePoses[frame][i].scale = Vector3Multiply(animations[a].framePoses[frame][i].scale, animations[a].framePoses[frame][animations[a].bones[i].parent].scale);
+ animations[a].keyframePoses[frame][i].rotation = QuaternionMultiply(animations[a].keyframePoses[frame][bones[i].parent].rotation, animations[a].keyframePoses[frame][i].rotation);
+ animations[a].keyframePoses[frame][i].translation = Vector3RotateByQuaternion(animations[a].keyframePoses[frame][i].translation, animations[a].keyframePoses[frame][bones[i].parent].rotation);
+ animations[a].keyframePoses[frame][i].translation = Vector3Add(animations[a].keyframePoses[frame][i].translation, animations[a].keyframePoses[frame][bones[i].parent].translation);
+ animations[a].keyframePoses[frame][i].scale = Vector3Multiply(animations[a].keyframePoses[frame][i].scale, animations[a].keyframePoses[frame][bones[i].parent].scale);
}
}
}
+
+ RL_FREE(bones);
}
UnloadFileData(fileData);
@@ -5228,7 +5275,7 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou
#endif
-#if defined(SUPPORT_FILEFORMAT_GLTF)
+#if SUPPORT_FILEFORMAT_GLTF
// Load file data callback for cgltf
static cgltf_result LoadFileGLTFCallback(const struct cgltf_memory_options *memoryOptions, const struct cgltf_file_options *fileOptions, const char *path, cgltf_size *size, void **data)
{
@@ -5375,7 +5422,7 @@ static Model LoadGLTF(const char *fileName)
- Supports basic animations
- Transforms, including parent-child relations, are applied on the mesh data,
but the hierarchy is not kept (as it can't be represented)
- - Mesh instances in the glTF file (i.e. same mesh linked from multiple nodes)
+ - Mesh instances in the glTF file (a.e. same mesh linked from multiple nodes)
are turned into separate raylib Meshes
RESTRICTIONS:
@@ -5442,7 +5489,7 @@ static Model LoadGLTF(const char *fileName)
int primitivesCount = 0;
bool dracoCompression = false;
- // NOTE: We will load every primitive in the glTF as a separate raylib Mesh
+ // NOTE: Load every primitive in the glTF as a separate raylib Mesh
// Determine total number of meshes needed from the node hierarchy
for (unsigned int i = 0; i < data->nodes_count; i++)
{
@@ -5464,8 +5511,10 @@ static Model LoadGLTF(const char *fileName)
if (dracoCompression)
{
- return model;
TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load glTF data", fileName);
+ cgltf_free(data);
+ UnloadFileData(fileData);
+ return model;
}
TRACELOG(LOG_DEBUG, " > Primitives (triangles only) count based on hierarchy : %i", primitivesCount);
@@ -5474,7 +5523,7 @@ static Model LoadGLTF(const char *fileName)
model.meshCount = primitivesCount;
model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
- // NOTE: We keep an extra slot for default material, in case some mesh requires it
+ // NOTE: Keep an extra slot for default material, in case some mesh requires it
model.materialCount = (int)data->materials_count + 1;
model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
model.materials[0] = LoadMaterialDefault(); // Load default material (index: 0)
@@ -5604,7 +5653,7 @@ static Model LoadGLTF(const char *fileName)
// NOTE: Visit each node in the hierarchy and process any mesh linked from it
// - Each primitive within a glTF node becomes a raylib Mesh
// - The local-to-world transform of each node is used to transform the points/normals/tangents of the created Mesh(es)
- // - Any glTF mesh linked from more than one Node (i.e. instancing) is turned into multiple Mesh's, as each Node will have its own transform applied
+ // - Any glTF mesh linked from more than one Node (a.e. instancing) is turned into multiple Mesh's, as each Node will have its own transform applied
//
// WARNING: The code below disregards the scenes defined in the file, all nodes are used
//----------------------------------------------------------------------------------------------------
@@ -5630,7 +5679,7 @@ static Model LoadGLTF(const char *fileName)
for (unsigned int p = 0; p < mesh->primitives_count; p++)
{
- // NOTE: We only support primitives defined by triangles
+ // NOTE: Only support primitives defined by triangles
// Other alternatives: points, lines, line_strip, triangle_strip
if (mesh->primitives[p].type != cgltf_primitive_type_triangles) continue;
@@ -6013,7 +6062,7 @@ static Model LoadGLTF(const char *fileName)
float *temp = (float *)RL_MALLOC(attribute->count*4*sizeof(float));
LOAD_ATTRIBUTE(attribute, 4, float, temp);
- // Convert data to raylib color data type (4 bytes), we expect the color data normalized
+ // Convert data to raylib color data type (4 bytes), color data must be normalized
for (unsigned int c = 0; c < attribute->count*4; c++) model.meshes[meshIndex].colors[c] = (unsigned char)(temp[c]*255.0f);
RL_FREE(temp);
@@ -6071,7 +6120,7 @@ static Model LoadGLTF(const char *fileName)
{
// The primitive actually keeps the pointer to the corresponding material,
// raylib instead assigns to the mesh the by its index, as loaded in model.materials array
- // To get the index, we check if material pointers match, and we assign the corresponding index,
+ // To get the index, check if material pointers match, and assign the corresponding index,
// skipping index 0, the default material
if (&data->materials[m] == mesh->primitives[p].material)
{
@@ -6092,15 +6141,15 @@ static Model LoadGLTF(const char *fileName)
// LIMITATIONS:
// - Only supports 1 armature per file, and skips loading it if there are multiple armatures
// - Only supports linear interpolation (default method in Blender when checked "Always Sample Animations" when exporting a GLTF file)
- // - Only supports translation/rotation/scale animation channel.path, weights not considered (i.e. morph targets)
+ // - Only supports translation/rotation/scale animation channel.path, weights not considered (a.e. morph targets)
//----------------------------------------------------------------------------------------------------
if (data->skins_count > 0)
{
cgltf_skin skin = data->skins[0];
- model.bones = LoadBoneInfoGLTF(skin, &model.boneCount);
- model.bindPose = (Transform *)RL_MALLOC(model.boneCount*sizeof(Transform));
+ model.skeleton.bones = LoadBoneInfoGLTF(skin, &model.skeleton.boneCount);
+ model.skeleton.bindPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform));
- for (int i = 0; i < model.boneCount; i++)
+ for (int i = 0; i < model.skeleton.boneCount; i++)
{
cgltf_node *node = skin.joints[i];
cgltf_float worldTransform[16];
@@ -6111,7 +6160,11 @@ static Model LoadGLTF(const char *fileName)
worldTransform[2], worldTransform[6], worldTransform[10], worldTransform[14],
worldTransform[3], worldTransform[7], worldTransform[11], worldTransform[15]
};
- MatrixDecompose(worldMatrix, &(model.bindPose[i].translation), &(model.bindPose[i].rotation), &(model.bindPose[i].scale));
+
+ MatrixDecompose(worldMatrix,
+ &(model.skeleton.bindPose[i].translation),
+ &(model.skeleton.bindPose[i].rotation),
+ &(model.skeleton.bindPose[i].scale));
}
if (data->skins_count > 1) TRACELOG(LOG_WARNING, "MODEL: [%s] can only load one skin (armature) per model, but gltf skins_count == %i", fileName, data->skins_count);
@@ -6129,7 +6182,7 @@ static Model LoadGLTF(const char *fileName)
{
bool hasJoints = false;
- // NOTE: We only support primitives defined by triangles
+ // NOTE: Only support primitives defined by triangles
if (mesh->primitives[p].type != cgltf_primitive_type_triangles) continue;
for (unsigned int j = 0; j < mesh->primitives[p].attributes_count; j++)
@@ -6143,7 +6196,7 @@ static Model LoadGLTF(const char *fileName)
// NOTE: JOINTS_n can only be vec4 and u8/u16
// SPECS: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes-overview
- // WARNING: raylib only supports model.meshes[].boneIds as u8 (unsigned char),
+ // WARNING: raylib only supports model.meshes[].boneIndices as u8 (unsigned char),
// if data is provided in any other format, it is converted to supported format but
// it could imply data loss (a warning message is issued in that case)
@@ -6151,16 +6204,16 @@ static Model LoadGLTF(const char *fileName)
{
if (attribute->component_type == cgltf_component_type_r_8u)
{
- // Init raylib mesh boneIds to copy glTF attribute data
- model.meshes[meshIndex].boneIds = (unsigned char *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char));
+ // Init raylib mesh boneIndices to copy glTF attribute data
+ model.meshes[meshIndex].boneIndices = (unsigned char *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char));
// Load attribute: vec4, u8 (unsigned char)
- LOAD_ATTRIBUTE(attribute, 4, unsigned char, model.meshes[meshIndex].boneIds)
+ LOAD_ATTRIBUTE(attribute, 4, unsigned char, model.meshes[meshIndex].boneIndices)
}
else if (attribute->component_type == cgltf_component_type_r_16u)
{
- // Init raylib mesh boneIds to copy glTF attribute data
- model.meshes[meshIndex].boneIds = (unsigned char *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char));
+ // Init raylib mesh boneIndices to copy glTF attribute data
+ model.meshes[meshIndex].boneIndices = (unsigned char *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char));
// Load data into a temp buffer to be converted to raylib data type
unsigned short *temp = (unsigned short *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned short));
@@ -6176,8 +6229,8 @@ static Model LoadGLTF(const char *fileName)
boneIdOverflowWarning = true;
}
- // Despite the possible overflow, we convert data to unsigned char
- model.meshes[meshIndex].boneIds[b] = (unsigned char)temp[b];
+ // Despite the possible overflow, convert data to unsigned char
+ model.meshes[meshIndex].boneIndices[b] = (unsigned char)temp[b];
}
RL_FREE(temp);
@@ -6226,8 +6279,7 @@ static Model LoadGLTF(const char *fileName)
model.meshes[meshIndex].boneWeights = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float));
// Load 4 components of float data type into mesh.boneWeights
- // for cgltf_attribute_type_weights we have:
-
+ // for cgltf_attribute_type_weights:
// - data.meshes[0] (256 vertices)
// - 256 values, provided as cgltf_type_vec4 of float (4 byte per joint, stride 16)
LOAD_ATTRIBUTE(attribute, 4, float, model.meshes[meshIndex].boneWeights)
@@ -6238,12 +6290,12 @@ static Model LoadGLTF(const char *fileName)
}
}
- // Check if we are animated, and the mesh was not given any bone assignments, but is the child of a bone node
- // in this case we need to fully attach all the verts to the parent bone so it will animate with the bone
- if (data->skins_count > 0 && !hasJoints && node->parent != NULL && node->parent->mesh == NULL)
+ // Check if animated, and the mesh was not given any bone assignments, but is the child of a bone node
+ // in this case, all the verts need to be attached to the parent bone so it will animate with the bone
+ if ((data->skins_count > 0) && !hasJoints && (node->parent != NULL) && (node->parent->mesh == NULL))
{
int parentBoneId = -1;
- for (int joint = 0; joint < model.boneCount; joint++)
+ for (int joint = 0; joint < model.skeleton.boneCount; joint++)
{
if (data->skins[0].joints[joint] == node->parent)
{
@@ -6254,40 +6306,45 @@ static Model LoadGLTF(const char *fileName)
if (parentBoneId >= 0)
{
- model.meshes[meshIndex].boneIds = (unsigned char *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char));
+ model.meshes[meshIndex].boneIndices = (unsigned char *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char));
model.meshes[meshIndex].boneWeights = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float));
for (int vertexIndex = 0; vertexIndex < model.meshes[meshIndex].vertexCount*4; vertexIndex += 4)
{
- model.meshes[meshIndex].boneIds[vertexIndex] = (unsigned char)parentBoneId;
+ model.meshes[meshIndex].boneIndices[vertexIndex] = (unsigned char)parentBoneId;
model.meshes[meshIndex].boneWeights[vertexIndex] = 1.0f;
}
}
}
- // Animated vertex data
+#if !SUPPORT_GPU_SKINNING
+ // Animated vertex data (CPU skinning)
model.meshes[meshIndex].animVertices = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float));
memcpy(model.meshes[meshIndex].animVertices, model.meshes[meshIndex].vertices, model.meshes[meshIndex].vertexCount*3*sizeof(float));
model.meshes[meshIndex].animNormals = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float));
- if (model.meshes[meshIndex].normals != NULL)
- {
- memcpy(model.meshes[meshIndex].animNormals, model.meshes[meshIndex].normals, model.meshes[meshIndex].vertexCount*3*sizeof(float));
- }
-
- // Bone Transform Matrices
- model.meshes[meshIndex].boneCount = model.boneCount;
- model.meshes[meshIndex].boneMatrices = (Matrix *)RL_CALLOC(model.meshes[meshIndex].boneCount, sizeof(Matrix));
-
- for (int j = 0; j < model.meshes[meshIndex].boneCount; j++)
- {
- model.meshes[meshIndex].boneMatrices[j] = MatrixIdentity();
- }
+ if (model.meshes[meshIndex].normals != NULL) memcpy(model.meshes[meshIndex].animNormals, model.meshes[meshIndex].normals, model.meshes[meshIndex].vertexCount*3*sizeof(float));
+#endif
+ model.meshes[meshIndex].boneCount = model.skeleton.boneCount;
meshIndex++; // Move to next mesh
}
}
+
+ // Initialize runtime animation data: current pose and bone matrices
+ model.currentPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform));
+ model.boneMatrices = (Matrix *)RL_CALLOC(model.skeleton.boneCount, sizeof(Matrix));
+ for (int j = 0; j < model.skeleton.boneCount; j++) model.boneMatrices[j] = MatrixIdentity();
//----------------------------------------------------------------------------------------------------
+ // Free unused allocated memory in case of no bones defined
+ if (model.skeleton.boneCount == 0)
+ {
+ RL_FREE(model.currentPose);
+ RL_FREE(model.boneMatrices);
+ model.currentPose = NULL;
+ model.boneMatrices = NULL;
+ }
+
// Free all cgltf loaded data
cgltf_free(data);
}
@@ -6381,7 +6438,7 @@ static bool GetPoseAtTimeGLTF(cgltf_interpolation_type interpolationType, cgltf_
}
else if (output->type == cgltf_type_vec4)
{
- // Only v4 is for rotations, so we know it's a quaternion
+ // Only v4 is for rotations, so it's a quaternion
switch (interpolationType)
{
case cgltf_interpolation_type_step:
@@ -6484,11 +6541,11 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo
};
MatrixDecompose(worldMatrix, &worldTransform.translation, &worldTransform.rotation, &worldTransform.scale);
- for (unsigned int i = 0; i < data->animations_count; i++)
+ for (unsigned int a = 0; a < data->animations_count; a++)
{
- animations[i].bones = LoadBoneInfoGLTF(skin, &animations[i].boneCount);
+ BoneInfo *bones = LoadBoneInfoGLTF(skin, &animations[a].boneCount);
- cgltf_animation animData = data->animations[i];
+ cgltf_animation animData = data->animations[a];
struct Channels {
cgltf_animation_channel *translate;
@@ -6497,7 +6554,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo
cgltf_interpolation_type interpolationType;
};
- struct Channels *boneChannels = (struct Channels *)RL_CALLOC(animations[i].boneCount, sizeof(struct Channels));
+ struct Channels *boneChannels = (struct Channels *)RL_CALLOC(animations[a].boneCount, sizeof(struct Channels));
float animDuration = 0.0f;
for (unsigned int j = 0; j < animData.channels_count; j++)
@@ -6514,11 +6571,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo
}
}
- if (boneIndex == -1)
- {
- // Animation channel for a node not in the armature
- continue;
- }
+ if (boneIndex == -1) continue; // Animation channel for a node not in the skeleton
boneChannels[boneIndex].interpolationType = animData.channels[j].sampler->interpolation;
@@ -6538,34 +6591,34 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo
}
else
{
- TRACELOG(LOG_WARNING, "MODEL: [%s] Unsupported target_path on channel %d's sampler for animation %d. Skipping.", fileName, j, i);
+ TRACELOG(LOG_WARNING, "MODEL: [%s] Unsupported target_path on channel %d's sampler for animation %d. Skipping.", fileName, j, a);
}
}
else TRACELOG(LOG_WARNING, "MODEL: [%s] Invalid interpolation curve encountered for GLTF animation.", fileName);
- float t = 0.0f;
- cgltf_bool r = cgltf_accessor_read_float(channel.sampler->input, channel.sampler->input->count - 1, &t, 1);
+ float time = 0.0f;
+ cgltf_bool result = cgltf_accessor_read_float(channel.sampler->input, channel.sampler->input->count - 1, &time, 1);
- if (!r)
+ if (!result)
{
TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load input time", fileName);
continue;
}
- animDuration = (t > animDuration)? t : animDuration;
+ animDuration = (time > animDuration)? time : animDuration;
}
- if (animData.name != NULL) strncpy(animations[i].name, animData.name, sizeof(animations[i].name) - 1);
+ if (animData.name != NULL) strncpy(animations[a].name, animData.name, sizeof(animations[a].name) - 1);
- animations[i].frameCount = (int)(animDuration*GLTF_FRAMERATE) + 1;
- animations[i].framePoses = (Transform **)RL_MALLOC(animations[i].frameCount*sizeof(Transform *));
+ animations[a].keyframeCount = (int)(animDuration*GLTF_FRAMERATE) + 1;
+ animations[a].keyframePoses = (Transform **)RL_CALLOC(animations[a].keyframeCount, sizeof(Transform *));
- for (int j = 0; j < animations[i].frameCount; j++)
+ for (int j = 0; j < animations[a].keyframeCount; j++)
{
- animations[i].framePoses[j] = (Transform *)RL_MALLOC(animations[i].boneCount*sizeof(Transform));
+ animations[a].keyframePoses[j] = (Transform *)RL_CALLOC(animations[a].boneCount, sizeof(Transform));
float time = (float)j / GLTF_FRAMERATE;
- for (int k = 0; k < animations[i].boneCount; k++)
+ for (int k = 0; k < animations[a].boneCount; k++)
{
Vector3 translation = {skin.joints[k]->translation[0], skin.joints[k]->translation[1], skin.joints[k]->translation[2]};
Quaternion rotation = {skin.joints[k]->rotation[0], skin.joints[k]->rotation[1], skin.joints[k]->rotation[2], skin.joints[k]->rotation[3]};
@@ -6575,7 +6628,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo
{
if (!GetPoseAtTimeGLTF(boneChannels[k].interpolationType, boneChannels[k].translate->sampler->input, boneChannels[k].translate->sampler->output, time, &translation))
{
- TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load translate pose data for bone %s", fileName, animations[i].bones[k].name);
+ TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load translate pose data for bone %s", fileName, bones[k].name);
}
}
@@ -6583,7 +6636,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo
{
if (!GetPoseAtTimeGLTF(boneChannels[k].interpolationType, boneChannels[k].rotate->sampler->input, boneChannels[k].rotate->sampler->output, time, &rotation))
{
- TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load rotate pose data for bone %s", fileName, animations[i].bones[k].name);
+ TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load rotate pose data for bone %s", fileName, bones[k].name);
}
}
@@ -6591,45 +6644,48 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo
{
if (!GetPoseAtTimeGLTF(boneChannels[k].interpolationType, boneChannels[k].scale->sampler->input, boneChannels[k].scale->sampler->output, time, &scale))
{
- TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load scale pose data for bone %s", fileName, animations[i].bones[k].name);
+ TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load scale pose data for bone %s", fileName, bones[k].name);
}
}
- animations[i].framePoses[j][k] = (Transform){
+ animations[a].keyframePoses[j][k] = (Transform){
.translation = translation,
.rotation = rotation,
.scale = scale
};
}
- Transform *root = &animations[i].framePoses[j][0];
+ Transform *root = &animations[a].keyframePoses[j][0];
root->rotation = QuaternionMultiply(worldTransform.rotation, root->rotation);
root->scale = Vector3Multiply(root->scale, worldTransform.scale);
root->translation = Vector3Multiply(root->translation, worldTransform.scale);
root->translation = Vector3RotateByQuaternion(root->translation, worldTransform.rotation);
root->translation = Vector3Add(root->translation, worldTransform.translation);
- BuildPoseFromParentJoints(animations[i].bones, animations[i].boneCount, animations[i].framePoses[j]);
+ BuildPoseFromParentJoints(bones, animations[a].boneCount, animations[a].keyframePoses[j]);
}
- TRACELOG(LOG_INFO, "MODEL: [%s] Loaded animation: %s (%d frames, %fs)", fileName, (animData.name != NULL)? animData.name : "NULL", animations[i].frameCount, animDuration);
+ TRACELOG(LOG_INFO, "MODEL: [%s] Loaded animation: %s | Frames: %d | Duration: %fs", fileName, (animData.name != NULL)? animData.name : "NULL", animations[a].keyframeCount, animDuration);
RL_FREE(boneChannels);
+ RL_FREE(bones);
}
}
if (data->skins_count > 1)
{
- TRACELOG(LOG_WARNING, "MODEL: [%s] expected exactly one skin to load animation data from, but found %i", fileName, data->skins_count);
+ TRACELOG(LOG_WARNING, "MODEL: [%s] Expected one unique skin to load animation data from, but found %i", fileName, data->skins_count);
}
cgltf_free(data);
}
+
UnloadFileData(fileData);
+
return animations;
}
#endif
-#if defined(SUPPORT_FILEFORMAT_VOX)
+#if SUPPORT_FILEFORMAT_VOX
// Load VOX (MagicaVoxel) mesh data
static Model LoadVOX(const char *fileName)
{
@@ -6739,7 +6795,7 @@ static Model LoadVOX(const char *fileName)
}
#endif
-#if defined(SUPPORT_FILEFORMAT_M3D)
+#if SUPPORT_FILEFORMAT_M3D
// Hook LoadFileData()/UnloadFileData() calls to M3D loaders
unsigned char *m3d_loaderhook(char *fn, unsigned int *len) { return LoadFileData((const char *)fn, (int *)len); }
void m3d_freehook(void *data) { UnloadFileData((unsigned char *)data); }
@@ -6769,7 +6825,7 @@ static Model LoadM3D(const char *fileName)
}
else TRACELOG(LOG_INFO, "MODEL: [%s] M3D data loaded successfully: %i faces/%i materials", fileName, m3d->numface, m3d->nummaterial);
- // no face? this is probably just a material library
+ // Check if face is found, if not, probably just a material library
if (!m3d->numface)
{
m3d_free(m3d);
@@ -6788,20 +6844,20 @@ static Model LoadM3D(const char *fileName)
TRACELOG(LOG_INFO, "MODEL: No materials, putting all meshes in a default material");
}
- // We always need a default material, so we add +1
+ // A default material is always required, so adding +1
model.materialCount++;
// Faces must be in non-decreasing materialid order. Verify that quickly, sorting them otherwise
// WARNING: Sorting is not needed, valid M3D model files should already be sorted
- // Just keeping the sorting function for reference (Check PR #3363 #3385)
+ // Keeping the sorting function for reference (Check PR #3363 #3385)
/*
- for (i = 1; i < m3d->numface; i++)
+ for (a = 1; a < m3d->numface; a++)
{
- if (m3d->face[i-1].materialid <= m3d->face[i].materialid) continue;
+ if (m3d->face[a-1].materialid <= m3d->face[a].materialid) continue;
- // face[i-1] > face[i]. slide face[i] lower
- m3df_t slider = m3d->face[i];
- j = i-1;
+ // face[a-1] > face[a]. slide face[a] lower
+ m3df_t slider = m3d->face[a];
+ j = a-1;
do
{ // face[j] > slider, face[j+1] is svailable vacant gap
@@ -6849,7 +6905,7 @@ static Model LoadM3D(const char *fileName)
mi = m3d->face[i].materialid;
// Only allocate colors VertexBuffer if there's a color vertex in the model for this material batch
- // if all colors are fully transparent black for all verteces of this materal, then we assume no vertex colors
+ // if all colors are fully transparent black for all verteces of this materal, then assuming no vertex colors
for (j = i, l = vcolor = 0; (j < (int)m3d->numface) && (mi == m3d->face[j].materialid); j++, l++)
{
if (!m3d->vertex[m3d->face[j].vertex[0]].color ||
@@ -6863,11 +6919,11 @@ static Model LoadM3D(const char *fileName)
model.meshes[k].texcoords = (float *)RL_CALLOC(model.meshes[k].vertexCount*2, sizeof(float));
model.meshes[k].normals = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float));
- // If no map is provided, or we have colors defined, we allocate storage for vertex colors
+ // If no map is provided, or colors are defined, allocate storage for vertex colors
// M3D specs only consider vertex colors if no material is provided, however raylib uses both and mixes the colors
if ((mi == M3D_UNDEF) || vcolor) model.meshes[k].colors = (unsigned char *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char));
- // If no map is provided and we allocated vertex colors, set them to white
+ // If no map is provided and vertex colors are allocated, set them to white
if ((mi == M3D_UNDEF) && (model.meshes[k].colors != NULL))
{
for (int c = 0; c < model.meshes[k].vertexCount*4; c++) model.meshes[k].colors[c] = 255;
@@ -6875,10 +6931,12 @@ static Model LoadM3D(const char *fileName)
if (m3d->numbone && m3d->numskin)
{
- model.meshes[k].boneIds = (unsigned char *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char));
+ model.meshes[k].boneIndices = (unsigned char *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char));
model.meshes[k].boneWeights = (float *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(float));
+#if !SUPPORT_GPU_SKINNING
model.meshes[k].animVertices = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float));
model.meshes[k].animNormals = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float));
+#endif
}
model.meshMaterial[k] = mi + 1;
@@ -6896,7 +6954,7 @@ static Model LoadM3D(const char *fileName)
model.meshes[k].vertices[l*9 + 7] = m3d->vertex[m3d->face[i].vertex[2]].y*m3d->scale;
model.meshes[k].vertices[l*9 + 8] = m3d->vertex[m3d->face[i].vertex[2]].z*m3d->scale;
- // Without vertex color (full transparency), we use the default color
+ // Without vertex color (full transparency), using the default color
if (model.meshes[k].colors != NULL)
{
if (m3d->vertex[m3d->face[i].vertex[0]].color & 0xff000000)
@@ -6937,20 +6995,20 @@ static Model LoadM3D(const char *fileName)
{
int skinid = m3d->vertex[m3d->face[i].vertex[n]].skinid;
- // Check if there is a skin for this mesh, should be, just failsafe
+ // Check if there is a skin for this mesh
if ((skinid != M3D_UNDEF) && (skinid < (int)m3d->numskin))
{
for (j = 0; j < 4; j++)
{
- model.meshes[k].boneIds[l*12 + n*4 + j] = m3d->skin[skinid].boneid[j];
+ model.meshes[k].boneIndices[l*12 + n*4 + j] = m3d->skin[skinid].boneid[j];
model.meshes[k].boneWeights[l*12 + n*4 + j] = m3d->skin[skinid].weight[j];
}
}
else
{
- // raylib does not handle boneless meshes with skeletal animations, so
- // we put all vertices without a bone into a special "no bone" bone
- model.meshes[k].boneIds[l*12 + n*4] = m3d->numbone;
+ // Boneless meshes with skeletal animations are not supported, so
+ // putting all vertices without a bone into a special "no bone" bone
+ model.meshes[k].boneIndices[l*12 + n*4] = m3d->numbone;
model.meshes[k].boneWeights[l*12 + n*4] = 1.0f;
}
}
@@ -7031,47 +7089,47 @@ static Model LoadM3D(const char *fileName)
// Load bones
if (m3d->numbone)
{
- model.boneCount = m3d->numbone + 1;
- model.bones = (BoneInfo *)RL_CALLOC(model.boneCount, sizeof(BoneInfo));
- model.bindPose = (Transform *)RL_CALLOC(model.boneCount, sizeof(Transform));
+ model.skeleton.boneCount = m3d->numbone + 1;
+ model.skeleton.bones = (BoneInfo *)RL_CALLOC(model.skeleton.boneCount, sizeof(BoneInfo));
+ model.skeleton.bindPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform));
for (i = 0; i < (int)m3d->numbone; i++)
{
- model.bones[i].parent = m3d->bone[i].parent;
- strncpy(model.bones[i].name, m3d->bone[i].name, sizeof(model.bones[i].name) - 1);
- model.bindPose[i].translation.x = m3d->vertex[m3d->bone[i].pos].x*m3d->scale;
- model.bindPose[i].translation.y = m3d->vertex[m3d->bone[i].pos].y*m3d->scale;
- model.bindPose[i].translation.z = m3d->vertex[m3d->bone[i].pos].z*m3d->scale;
- model.bindPose[i].rotation.x = m3d->vertex[m3d->bone[i].ori].x;
- model.bindPose[i].rotation.y = m3d->vertex[m3d->bone[i].ori].y;
- model.bindPose[i].rotation.z = m3d->vertex[m3d->bone[i].ori].z;
- model.bindPose[i].rotation.w = m3d->vertex[m3d->bone[i].ori].w;
+ model.skeleton.bones[i].parent = m3d->bone[i].parent;
+ strncpy(model.skeleton.bones[i].name, m3d->bone[i].name, sizeof(model.skeleton.bones[i].name) - 1);
+ model.skeleton.bindPose[i].translation.x = m3d->vertex[m3d->bone[i].pos].x*m3d->scale;
+ model.skeleton.bindPose[i].translation.y = m3d->vertex[m3d->bone[i].pos].y*m3d->scale;
+ model.skeleton.bindPose[i].translation.z = m3d->vertex[m3d->bone[i].pos].z*m3d->scale;
+ model.skeleton.bindPose[i].rotation.x = m3d->vertex[m3d->bone[i].ori].x;
+ model.skeleton.bindPose[i].rotation.y = m3d->vertex[m3d->bone[i].ori].y;
+ model.skeleton.bindPose[i].rotation.z = m3d->vertex[m3d->bone[i].ori].z;
+ model.skeleton.bindPose[i].rotation.w = m3d->vertex[m3d->bone[i].ori].w;
- // TODO: If the orientation quaternion is not normalized, then that's encoding scaling
- model.bindPose[i].rotation = QuaternionNormalize(model.bindPose[i].rotation);
- model.bindPose[i].scale.x = model.bindPose[i].scale.y = model.bindPose[i].scale.z = 1.0f;
+ // NOTE: If the orientation quaternion is not normalized, then that's encoding scaling
+ model.skeleton.bindPose[i].rotation = QuaternionNormalize(model.skeleton.bindPose[i].rotation);
+ model.skeleton.bindPose[i].scale.x = model.skeleton.bindPose[i].scale.y = model.skeleton.bindPose[i].scale.z = 1.0f;
// Child bones are stored in parent bone relative space, convert that into model space
- if (model.bones[i].parent >= 0)
+ if (model.skeleton.bones[i].parent >= 0)
{
- model.bindPose[i].rotation = QuaternionMultiply(model.bindPose[model.bones[i].parent].rotation, model.bindPose[i].rotation);
- model.bindPose[i].translation = Vector3RotateByQuaternion(model.bindPose[i].translation, model.bindPose[model.bones[i].parent].rotation);
- model.bindPose[i].translation = Vector3Add(model.bindPose[i].translation, model.bindPose[model.bones[i].parent].translation);
- model.bindPose[i].scale = Vector3Multiply(model.bindPose[i].scale, model.bindPose[model.bones[i].parent].scale);
+ model.skeleton.bindPose[i].rotation = QuaternionMultiply(model.skeleton.bindPose[model.skeleton.bones[i].parent].rotation, model.skeleton.bindPose[i].rotation);
+ model.skeleton.bindPose[i].translation = Vector3RotateByQuaternion(model.skeleton.bindPose[i].translation, model.skeleton.bindPose[model.skeleton.bones[i].parent].rotation);
+ model.skeleton.bindPose[i].translation = Vector3Add(model.skeleton.bindPose[i].translation, model.skeleton.bindPose[model.skeleton.bones[i].parent].translation);
+ model.skeleton.bindPose[i].scale = Vector3Multiply(model.skeleton.bindPose[i].scale, model.skeleton.bindPose[model.skeleton.bones[i].parent].scale);
}
}
// Add a special "no bone" bone
- model.bones[i].parent = -1;
- memcpy(model.bones[i].name, "NO BONE", 7);
- model.bindPose[i].translation.x = 0.0f;
- model.bindPose[i].translation.y = 0.0f;
- model.bindPose[i].translation.z = 0.0f;
- model.bindPose[i].rotation.x = 0.0f;
- model.bindPose[i].rotation.y = 0.0f;
- model.bindPose[i].rotation.z = 0.0f;
- model.bindPose[i].rotation.w = 1.0f;
- model.bindPose[i].scale.x = model.bindPose[i].scale.y = model.bindPose[i].scale.z = 1.0f;
+ model.skeleton.bones[i].parent = -1;
+ memcpy(model.skeleton.bones[i].name, "NO BONE", 7);
+ model.skeleton.bindPose[i].translation.x = 0.0f;
+ model.skeleton.bindPose[i].translation.y = 0.0f;
+ model.skeleton.bindPose[i].translation.z = 0.0f;
+ model.skeleton.bindPose[i].rotation.x = 0.0f;
+ model.skeleton.bindPose[i].rotation.y = 0.0f;
+ model.skeleton.bindPose[i].rotation.z = 0.0f;
+ model.skeleton.bindPose[i].rotation.w = 1.0f;
+ model.skeleton.bindPose[i].scale.x = model.skeleton.bindPose[i].scale.y = model.skeleton.bindPose[i].scale.z = 1.0f;
}
// Load bone-pose default mesh into animation vertices. These will be updated when UpdateModelAnimation gets
@@ -7080,16 +7138,19 @@ static Model LoadM3D(const char *fileName)
{
for (i = 0; i < model.meshCount; i++)
{
+ model.meshes[i].boneCount = model.skeleton.boneCount;
+
+#if !SUPPORT_GPU_SKINNING
+ // Initialize vertex buffers for CPU skinning
memcpy(model.meshes[i].animVertices, model.meshes[i].vertices, model.meshes[i].vertexCount*3*sizeof(float));
memcpy(model.meshes[i].animNormals, model.meshes[i].normals, model.meshes[i].vertexCount*3*sizeof(float));
-
- model.meshes[i].boneCount = model.boneCount;
- model.meshes[i].boneMatrices = (Matrix *)RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix));
- for (j = 0; j < model.meshes[i].boneCount; j++)
- {
- model.meshes[i].boneMatrices[j] = MatrixIdentity();
- }
+#endif
}
+
+ // Initialize runtime animation data: current pose and bone matrices
+ model.currentPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform));
+ model.boneMatrices = (Matrix *)RL_CALLOC(model.skeleton.boneCount, sizeof(Matrix));
+ for (int j = 0; j < model.skeleton.boneCount; j++) model.boneMatrices[j] = MatrixIdentity();
}
m3d_free(m3d);
@@ -7123,8 +7184,7 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou
UnloadFileData(fileData);
return NULL;
}
- else TRACELOG(LOG_INFO, "MODEL: [%s] M3D data loaded successfully: %i animations, %i bones, %i skins", fileName,
- m3d->numaction, m3d->numbone, m3d->numskin);
+ else TRACELOG(LOG_INFO, "MODEL: [%s] M3D data loaded successfully: %i animations, %i bones, %i skins", fileName, m3d->numaction, m3d->numbone, m3d->numskin);
// No animation or bones, exit out. skins are not required because some people use one animation for N models
if (!m3d->numaction || !m3d->numbone)
@@ -7139,29 +7199,30 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou
for (unsigned int a = 0; a < m3d->numaction; a++)
{
- animations[a].frameCount = m3d->action[a].durationmsec/M3D_ANIMDELAY;
animations[a].boneCount = m3d->numbone + 1;
- animations[a].bones = (BoneInfo *)RL_MALLOC((m3d->numbone + 1)*sizeof(BoneInfo));
- animations[a].framePoses = (Transform **)RL_MALLOC(animations[a].frameCount*sizeof(Transform *));
+ BoneInfo *bones = (BoneInfo *)RL_CALLOC((m3d->numbone + 1), sizeof(BoneInfo));
+
+ animations[a].keyframeCount = m3d->action[a].durationmsec/M3D_ANIMDELAY;
+ animations[a].keyframePoses = (Transform **)RL_CALLOC(animations[a].keyframeCount, sizeof(Transform *));
strncpy(animations[a].name, m3d->action[a].name, sizeof(animations[a].name) - 1);
- TRACELOG(LOG_INFO, "MODEL: [%s] animation #%i: %i msec, %i frames", fileName, a, m3d->action[a].durationmsec, animations[a].frameCount);
+ TRACELOG(LOG_INFO, "MODEL: [%s] Loaded animation: %s | Frames: %d | Duration: %fs", fileName, animations[a].name, animations[a].keyframeCount, m3d->action[a].durationmsec);
for (i = 0; i < (int)m3d->numbone; i++)
{
- animations[a].bones[i].parent = m3d->bone[i].parent;
- strncpy(animations[a].bones[i].name, m3d->bone[i].name, sizeof(animations[a].bones[i].name) - 1);
+ bones[i].parent = m3d->bone[i].parent;
+ strncpy(bones[i].name, m3d->bone[i].name, sizeof(bones[i].name) - 1);
}
// A special, never transformed "no bone" bone, used for boneless vertices
- animations[a].bones[i].parent = -1;
- memcpy(animations[a].bones[i].name, "NO BONE", 7);
+ bones[i].parent = -1;
+ memcpy(bones[i].name, "NO BONE", 7);
- // M3D stores frames at arbitrary intervals with sparse skeletons. We need full skeletons at
+ // M3D stores frames at arbitrary intervals with sparse skeletons; Full skeletons is required at
// regular intervals, so let the M3D SDK do the heavy lifting and calculate interpolated bones
- for (i = 0; i < animations[a].frameCount; i++)
+ for (i = 0; i < animations[a].keyframeCount; i++)
{
- animations[a].framePoses[i] = (Transform *)RL_MALLOC((m3d->numbone + 1)*sizeof(Transform));
+ animations[a].keyframePoses[i] = (Transform *)RL_CALLOC((m3d->numbone + 1), sizeof(Transform));
m3db_t *pose = m3d_pose(m3d, a, i*M3D_ANIMDELAY);
@@ -7169,38 +7230,43 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou
{
for (j = 0; j < (int)m3d->numbone; j++)
{
- animations[a].framePoses[i][j].translation.x = m3d->vertex[pose[j].pos].x*m3d->scale;
- animations[a].framePoses[i][j].translation.y = m3d->vertex[pose[j].pos].y*m3d->scale;
- animations[a].framePoses[i][j].translation.z = m3d->vertex[pose[j].pos].z*m3d->scale;
- animations[a].framePoses[i][j].rotation.x = m3d->vertex[pose[j].ori].x;
- animations[a].framePoses[i][j].rotation.y = m3d->vertex[pose[j].ori].y;
- animations[a].framePoses[i][j].rotation.z = m3d->vertex[pose[j].ori].z;
- animations[a].framePoses[i][j].rotation.w = m3d->vertex[pose[j].ori].w;
- animations[a].framePoses[i][j].rotation = QuaternionNormalize(animations[a].framePoses[i][j].rotation);
- animations[a].framePoses[i][j].scale.x = animations[a].framePoses[i][j].scale.y = animations[a].framePoses[i][j].scale.z = 1.0f;
+ animations[a].keyframePoses[i][j].translation.x = m3d->vertex[pose[j].pos].x*m3d->scale;
+ animations[a].keyframePoses[i][j].translation.y = m3d->vertex[pose[j].pos].y*m3d->scale;
+ animations[a].keyframePoses[i][j].translation.z = m3d->vertex[pose[j].pos].z*m3d->scale;
+ animations[a].keyframePoses[i][j].rotation.x = m3d->vertex[pose[j].ori].x;
+ animations[a].keyframePoses[i][j].rotation.y = m3d->vertex[pose[j].ori].y;
+ animations[a].keyframePoses[i][j].rotation.z = m3d->vertex[pose[j].ori].z;
+ animations[a].keyframePoses[i][j].rotation.w = m3d->vertex[pose[j].ori].w;
+ animations[a].keyframePoses[i][j].rotation = QuaternionNormalize(animations[a].keyframePoses[i][j].rotation);
+ animations[a].keyframePoses[i][j].scale.x = animations[a].keyframePoses[i][j].scale.y = animations[a].keyframePoses[i][j].scale.z = 1.0f;
// Child bones are stored in parent bone relative space, convert that into model space
- if (animations[a].bones[j].parent >= 0)
+ if (bones[j].parent >= 0)
{
- animations[a].framePoses[i][j].rotation = QuaternionMultiply(animations[a].framePoses[i][animations[a].bones[j].parent].rotation, animations[a].framePoses[i][j].rotation);
- animations[a].framePoses[i][j].translation = Vector3RotateByQuaternion(animations[a].framePoses[i][j].translation, animations[a].framePoses[i][animations[a].bones[j].parent].rotation);
- animations[a].framePoses[i][j].translation = Vector3Add(animations[a].framePoses[i][j].translation, animations[a].framePoses[i][animations[a].bones[j].parent].translation);
- animations[a].framePoses[i][j].scale = Vector3Multiply(animations[a].framePoses[i][j].scale, animations[a].framePoses[i][animations[a].bones[j].parent].scale);
+ animations[a].keyframePoses[i][j].rotation = QuaternionMultiply(animations[a].keyframePoses[i][bones[j].parent].rotation, animations[a].keyframePoses[i][j].rotation);
+ animations[a].keyframePoses[i][j].translation = Vector3RotateByQuaternion(animations[a].keyframePoses[i][j].translation, animations[a].keyframePoses[i][bones[j].parent].rotation);
+ animations[a].keyframePoses[i][j].translation = Vector3Add(animations[a].keyframePoses[i][j].translation, animations[a].keyframePoses[i][bones[j].parent].translation);
+ animations[a].keyframePoses[i][j].scale = Vector3Multiply(animations[a].keyframePoses[i][j].scale, animations[a].keyframePoses[i][bones[j].parent].scale);
}
}
// Default transform for the "no bone" bone
- animations[a].framePoses[i][j].translation.x = 0.0f;
- animations[a].framePoses[i][j].translation.y = 0.0f;
- animations[a].framePoses[i][j].translation.z = 0.0f;
- animations[a].framePoses[i][j].rotation.x = 0.0f;
- animations[a].framePoses[i][j].rotation.y = 0.0f;
- animations[a].framePoses[i][j].rotation.z = 0.0f;
- animations[a].framePoses[i][j].rotation.w = 1.0f;
- animations[a].framePoses[i][j].scale.x = animations[a].framePoses[i][j].scale.y = animations[a].framePoses[i][j].scale.z = 1.0f;
+ animations[a].keyframePoses[i][j].translation.x = 0.0f;
+ animations[a].keyframePoses[i][j].translation.y = 0.0f;
+ animations[a].keyframePoses[i][j].translation.z = 0.0f;
+ animations[a].keyframePoses[i][j].rotation.x = 0.0f;
+ animations[a].keyframePoses[i][j].rotation.y = 0.0f;
+ animations[a].keyframePoses[i][j].rotation.z = 0.0f;
+ animations[a].keyframePoses[i][j].rotation.w = 1.0f;
+ animations[a].keyframePoses[i][j].scale.x = 1.0f;
+ animations[a].keyframePoses[i][j].scale.y = 1.0f;
+ animations[a].keyframePoses[i][j].scale.z = 1.0f;
+
RL_FREE(pose);
}
}
+
+ RL_FREE(bones);
}
m3d_free(m3d);
@@ -7211,4 +7277,4 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou
}
#endif
-#endif // SUPPORT_MODULE_RMODELS
+#endif // SUPPORT_MODULE_RMODELS
diff --git a/src/rshapes.c b/src/rshapes.c
index 35614bd6c..e24689040 100644
--- a/src/rshapes.c
+++ b/src/rshapes.c
@@ -48,7 +48,7 @@
#include "config.h" // Defines module configuration flags
-#if defined(SUPPORT_MODULE_RSHAPES)
+#if SUPPORT_MODULE_RSHAPES
#include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2
@@ -128,7 +128,7 @@ void DrawPixel(int posX, int posY, Color color)
// Draw a pixel (Vector version)
void DrawPixelV(Vector2 position, Color color)
{
-#if defined(SUPPORT_QUADS_DRAW_MODE)
+#if SUPPORT_QUADS_DRAW_MODE
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
@@ -353,7 +353,7 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA
float stepLength = (endAngle - startAngle)/(float)segments;
float angle = startAngle;
-#if defined(SUPPORT_QUADS_DRAW_MODE)
+#if SUPPORT_QUADS_DRAW_MODE
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
@@ -594,7 +594,7 @@ void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startA
float stepLength = (endAngle - startAngle)/(float)segments;
float angle = startAngle;
-#if defined(SUPPORT_QUADS_DRAW_MODE)
+#if SUPPORT_QUADS_DRAW_MODE
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
@@ -774,7 +774,7 @@ void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color
bottomRight.y = y + (dx + rec.width)*sinRotation + (dy + rec.height)*cosRotation;
}
-#if defined(SUPPORT_QUADS_DRAW_MODE)
+#if SUPPORT_QUADS_DRAW_MODE
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
@@ -884,7 +884,7 @@ void DrawRectangleLines(int posX, int posY, int width, int height, Color color)
/*
// Previous implementation, it has issues... but it does not require view matrix...
-#if defined(SUPPORT_QUADS_DRAW_MODE)
+#if SUPPORT_QUADS_DRAW_MODE
DrawRectangle(posX, posY, width, 1, color);
DrawRectangle(posX + width - 1, posY + 1, 1, height - 2, color);
DrawRectangle(posX, posY + height - 1, width, 1, color);
@@ -994,7 +994,7 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co
const Vector2 centers[4] = { point[8], point[9], point[10], point[11] };
const float angles[4] = { 180.0f, 270.0f, 0.0f, 90.0f };
-#if defined(SUPPORT_QUADS_DRAW_MODE)
+#if SUPPORT_QUADS_DRAW_MODE
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
@@ -1248,7 +1248,7 @@ void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, int segments, f
if (lineThick > 1)
{
-#if defined(SUPPORT_QUADS_DRAW_MODE)
+#if SUPPORT_QUADS_DRAW_MODE
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
@@ -1422,7 +1422,7 @@ void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, int segments, f
// NOTE: Vertex must be provided in counter-clockwise order
void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color)
{
-#if defined(SUPPORT_QUADS_DRAW_MODE)
+#if SUPPORT_QUADS_DRAW_MODE
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
@@ -1538,7 +1538,7 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col
float centralAngle = rotation*DEG2RAD;
float angleStep = 360.0f/(float)sides*DEG2RAD;
-#if defined(SUPPORT_QUADS_DRAW_MODE)
+#if SUPPORT_QUADS_DRAW_MODE
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
@@ -1607,7 +1607,7 @@ void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, fl
float exteriorAngle = 360.0f/(float)sides*DEG2RAD;
float innerRadius = radius - (lineThick*cosf(DEG2RAD*exteriorAngle/2.0f));
-#if defined(SUPPORT_QUADS_DRAW_MODE)
+#if SUPPORT_QUADS_DRAW_MODE
rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle();
@@ -1663,7 +1663,7 @@ void DrawSplineLinear(const Vector2 *points, int pointCount, float thick, Color
{
if (pointCount < 2) return;
-#if defined(SUPPORT_SPLINE_MITERS)
+#if SUPPORT_SPLINE_MITERS
Vector2 prevNormal = (Vector2){-(points[1].y - points[0].y), (points[1].x - points[0].x)};
float prevLength = sqrtf(prevNormal.x*prevNormal.x + prevNormal.y*prevNormal.y);
@@ -1770,8 +1770,8 @@ void DrawSplineLinear(const Vector2 *points, int pointCount, float thick, Color
}
#endif
-#if defined(SUPPORT_SPLINE_SEGMENT_CAPS)
- // TODO: Add spline segment rounded caps at the begin/end of the spline
+#if SUPPORT_SPLINE_SEGMENT_CAPS
+ // TODO: Add spline segment rounded caps at the begin/end of the spline?
#endif
}
@@ -1946,7 +1946,7 @@ void DrawSplineBezierCubic(const Vector2 *points, int pointCount, float thick, C
// Draw spline segment: Linear, 2 points
void DrawSplineSegmentLinear(Vector2 p1, Vector2 p2, float thick, Color color)
{
- // NOTE: For the linear spline no subdivisions are used, just a single quad
+ // NOTE: For the linear spline no subdivisions are used, only a single quad
Vector2 delta = { p2.x - p1.x, p2.y - p1.y };
float length = sqrtf(delta.x*delta.x + delta.y*delta.y);
@@ -2387,11 +2387,11 @@ bool CheckCollisionLines(Vector2 startPos1, Vector2 endPos1, Vector2 startPos2,
{
collisionPoint->x = startPos1.x + t*rx;
collisionPoint->y = startPos1.y + t*ry;
-
+
collision = true;
}
}
-
+
return collision;
}
@@ -2484,4 +2484,4 @@ static float EaseCubicInOut(float t, float b, float c, float d)
return result;
}
-#endif // SUPPORT_MODULE_RSHAPES
+#endif // SUPPORT_MODULE_RSHAPES
diff --git a/src/rtext.c b/src/rtext.c
index 840b452d3..936ab9c16 100644
--- a/src/rtext.c
+++ b/src/rtext.c
@@ -6,20 +6,11 @@
* #define SUPPORT_MODULE_RTEXT
* rtext module is included in the build
*
-* #define SUPPORT_DEFAULT_FONT
-* Load default raylib font on initialization to be used by DrawText() and MeasureText()
-* If no default font loaded, DrawTextEx() and MeasureTextEx() are required
-*
* #define SUPPORT_FILEFORMAT_FNT
* #define SUPPORT_FILEFORMAT_TTF
* #define SUPPORT_FILEFORMAT_BDF
* Selected desired fileformats to be supported for loading. Some of those formats are
-* supported by default, to remove support, just comment unrequired #define in this module
-*
-* #define SUPPORT_FONT_ATLAS_WHITE_REC
-* On font atlas image generation [GenImageFontAtlas()], add a 3x3 pixels white rectangle
-* at the bottom-right corner of the atlas. It can be useful to for shapes drawing, to allow
-* drawing text and shapes with a single draw call [SetShapesTexture()]
+* supported by default, to remove support, comment unrequired #define in this module
*
* #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH
* TextSplit() function static buffer max size
@@ -27,6 +18,11 @@
* #define MAX_TEXTSPLIT_COUNT
* TextSplit() function static substrings pointers array (pointing to static buffer)
*
+* #define FONT_ATLAS_CORNER_REC_SIZE
+* On font atlas image generation [GenImageFontAtlas()], add a NxN pixels white rectangle
+* at the bottom-right corner of the atlas. It can be useful to for shapes drawing, to allow
+* drawing text and shapes with a single draw call [SetShapesTexture()]
+*
* DEPENDENCIES:
* stb_truetype - Load TTF file and rasterize characters data
* stb_rect_pack - Rectangles packing algorithms, required for font atlas generation
@@ -57,7 +53,7 @@
#include "config.h" // Defines module configuration flags
-#if defined(SUPPORT_MODULE_RTEXT)
+#if SUPPORT_MODULE_RTEXT
#include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 -> Only DrawTextPro()
@@ -67,7 +63,7 @@
#include // Required for: va_list, va_start(), vsprintf(), va_end() [Used in TextFormat()]
#include // Required for: toupper(), tolower() [Used in TextToUpper(), TextToLower()]
-#if defined(SUPPORT_FILEFORMAT_TTF) || defined(SUPPORT_FILEFORMAT_BDF)
+#if SUPPORT_FILEFORMAT_TTF || SUPPORT_FILEFORMAT_BDF
#if defined(__GNUC__) // GCC and Clang
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
@@ -83,7 +79,7 @@
#endif
#endif
-#if defined(SUPPORT_FILEFORMAT_TTF)
+#if SUPPORT_FILEFORMAT_TTF
#if defined(__GNUC__) // GCC and Clang
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
@@ -105,14 +101,18 @@
// Defines and Macros
//----------------------------------------------------------------------------------
#ifndef MAX_TEXT_BUFFER_LENGTH
- #define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions:
- // TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal(), TextSplit()
-#endif
-#ifndef MAX_TEXT_UNICODE_CHARS
- #define MAX_TEXT_UNICODE_CHARS 512 // Maximum number of unicode codepoints: GetCodepoints()
+ #define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions:
+ // TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal(), TextSplit()
#endif
#ifndef MAX_TEXTSPLIT_COUNT
- #define MAX_TEXTSPLIT_COUNT 128 // Maximum number of substrings to split: TextSplit()
+ #define MAX_TEXTSPLIT_COUNT 128 // Maximum number of substrings to split: TextSplit()
+#endif
+
+#ifndef FONT_ATLAS_CORNER_REC_SIZE
+ // On font atlas image generation [GenImageFontAtlas()], add a 3x3 pixels white rectangle
+ // at the bottom-right corner of the atlas. It can be useful for shapes drawing, to allow
+ // drawing text and shapes with a single draw call [SetShapesTexture()]
+ #define FONT_ATLAS_CORNER_REC_SIZE 3 // Size of white rectangle drawn on font atlas on font loading
#endif
//----------------------------------------------------------------------------------
@@ -123,12 +123,12 @@
//----------------------------------------------------------------------------------
// Global variables
//----------------------------------------------------------------------------------
-#if defined(SUPPORT_DEFAULT_FONT)
// Default font provided by raylib
// NOTE: Default font is loaded on InitWindow() and disposed on CloseWindow() [module: core]
static Font defaultFont = { 0 };
-#endif
-static int textLineSpacing = 2; // Text vertical line spacing in pixels (between lines)
+
+// Text vertical line spacing in pixels (between lines)
+static int textLineSpacing = 2;
//----------------------------------------------------------------------------------
// Other Modules Functions Declaration (required by text)
@@ -138,38 +138,36 @@ static int textLineSpacing = 2; // Text vertical line spacing in pixels (between
//----------------------------------------------------------------------------------
// Module Internal Functions Declaration
//----------------------------------------------------------------------------------
-#if defined(SUPPORT_FILEFORMAT_FNT)
+#if SUPPORT_FILEFORMAT_FNT
static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file)
#endif
-#if defined(SUPPORT_FILEFORMAT_BDF)
+#if SUPPORT_FILEFORMAT_BDF
static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, const int *codepoints, int codepointCount, int *outFontSize);
#endif
-#if defined(SUPPORT_DEFAULT_FONT)
extern void LoadFontDefault(void);
extern void UnloadFontDefault(void);
-#endif
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
-#if defined(SUPPORT_DEFAULT_FONT)
// Load raylib default font
extern void LoadFontDefault(void)
{
#define BIT_CHECK(a,b) ((a) & (1u << (b)))
- // Check to see if we have already allocated the font for an image, and if we don't need to upload, then just return
+ // Check to see if the font for an image has alreeady been allocated,
+ // and if no need to upload, then return
if (defaultFont.glyphs != NULL) return;
// NOTE: Using UTF-8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement
// REF: http://www.utf8-chartable.de/unicode-utf8-table.pl
- defaultFont.glyphCount = 224; // Number of glyphs included in our default font
- defaultFont.glyphPadding = 0; // Characters padding
+ defaultFont.glyphCount = 224; // Number of glyphs included in our default font
+ defaultFont.glyphPadding = 0; // Characters padding
// Default font is directly defined here (data generated from a sprite font image)
- // This way, we reconstruct Font without creating large global variables
+ // This way, reconstructing Font without creating large global variables
// This data is automatically allocated to Stack and automatically deallocated at the end of this function
unsigned int defaultFontData[512] = {
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00200020, 0x0001b000, 0x00000000, 0x00000000, 0x8ef92520, 0x00020a00, 0x7dbe8000, 0x1f7df45f,
@@ -217,7 +215,7 @@ extern void LoadFontDefault(void)
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 };
int charsHeight = 10;
- int charsDivisor = 1; // Every char is separated from the consecutive by a 1 pixel divisor, horizontally and vertically
+ int charsDivisor = 1; // Every char is separated from the consecutive by a 1 pixel divisor, horizontally and vertically
int charsWidth[224] = { 3, 1, 4, 6, 5, 7, 6, 2, 3, 3, 5, 5, 2, 4, 1, 7, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 3, 4, 3, 6,
7, 6, 6, 6, 6, 6, 6, 6, 6, 3, 5, 6, 5, 7, 6, 6, 6, 6, 6, 6, 7, 6, 7, 7, 6, 6, 6, 2, 7, 2, 3, 5,
@@ -244,8 +242,8 @@ extern void LoadFontDefault(void)
{
if (BIT_CHECK(defaultFontData[counter], j))
{
- // NOTE: We are unreferencing data as short, so,
- // we must consider data as little-endian order (alpha + gray)
+ // NOTE: Unreferencing data as short, so,
+ // considering data as little-endian (alpha + gray)
((unsigned short *)imFont.data)[i + j] = 0xffff;
}
else
@@ -260,8 +258,8 @@ extern void LoadFontDefault(void)
defaultFont.texture = LoadTextureFromImage(imFont);
- // we have already loaded the font glyph data an image, and the GPU is ready, we are done
- // if we don't do this, we will leak memory by reallocating the glyphs and rects
+ // Check again if font glyph data has been already loaded
+ // to avoid reallocating the glyphs and rects
if (defaultFont.glyphs != NULL)
{
UnloadImage(imFont);
@@ -270,7 +268,6 @@ extern void LoadFontDefault(void)
// Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, glyphCount
//------------------------------------------------------------------------------
-
// Allocate space for our characters info data
// NOTE: This memory must be freed at end! --> Done by CloseWindow()
defaultFont.glyphs = (GlyphInfo *)RL_CALLOC(defaultFont.glyphCount, sizeof(GlyphInfo));
@@ -329,17 +326,11 @@ extern void UnloadFontDefault(void)
defaultFont.glyphs = NULL;
defaultFont.recs = NULL;
}
-#endif // SUPPORT_DEFAULT_FONT
// Get the default font, useful to be used with extended parameters
Font GetFontDefault()
{
-#if defined(SUPPORT_DEFAULT_FONT)
return defaultFont;
-#else
- Font font = { 0 };
- return font;
-#endif
}
// Load Font from file into GPU memory (VRAM)
@@ -361,15 +352,15 @@ Font LoadFont(const char *fileName)
Font font = { 0 };
-#if defined(SUPPORT_FILEFORMAT_TTF)
+#if SUPPORT_FILEFORMAT_TTF
if (IsFileExtension(fileName, ".ttf") || IsFileExtension(fileName, ".otf")) font = LoadFontEx(fileName, FONT_TTF_DEFAULT_SIZE, NULL, FONT_TTF_DEFAULT_NUMCHARS);
else
#endif
-#if defined(SUPPORT_FILEFORMAT_FNT)
+#if SUPPORT_FILEFORMAT_FNT
if (IsFileExtension(fileName, ".fnt")) font = LoadBMFont(fileName);
else
#endif
-#if defined(SUPPORT_FILEFORMAT_BDF)
+#if SUPPORT_FILEFORMAT_BDF
if (IsFileExtension(fileName, ".bdf")) font = LoadFontEx(fileName, FONT_TTF_DEFAULT_SIZE, NULL, FONT_TTF_DEFAULT_NUMCHARS);
else
#endif
@@ -383,7 +374,7 @@ Font LoadFont(const char *fileName)
if (font.texture.id == 0) TRACELOG(LOG_WARNING, "FONT: [%s] Failed to load font texture -> Using default font", fileName);
else
{
- SetTextureFilter(font.texture, TEXTURE_FILTER_POINT); // By default, we set point filter (the best performance)
+ SetTextureFilter(font.texture, TEXTURE_FILTER_POINT); // By default, set point filter (the best performance)
TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", font.baseSize, font.glyphCount);
}
@@ -429,8 +420,8 @@ Font LoadFontFromImage(Image image, Color key, int firstChar)
int x = 0;
int y = 0;
- // We allocate a temporal arrays for glyphs data measures,
- // once we get the actual number of glyphs, we copy data to a sized arrays
+ // Allocate a temporal arrays for glyphs data measures,
+ // once the actual number of glyphs is obtained, copy data to a sized array
int tempCharValues[MAX_GLYPHS_FROM_IMAGE] = { 0 };
Rectangle tempCharRecs[MAX_GLYPHS_FROM_IMAGE] = { 0 };
@@ -455,7 +446,8 @@ Font LoadFontFromImage(Image image, Color key, int firstChar)
int charHeight = 0;
int j = 0;
- while (!COLOR_EQUAL(pixels[(lineSpacing + j)*image.width + charSpacing], key)) j++;
+ while (((lineSpacing + j) < image.height) &&
+ !COLOR_EQUAL(pixels[(lineSpacing + j)*image.width + charSpacing], key)) j++;
charHeight = j;
@@ -478,7 +470,8 @@ Font LoadFontFromImage(Image image, Color key, int firstChar)
int charWidth = 0;
- while (!COLOR_EQUAL(pixels[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*image.width + xPosToRead + charWidth], key)) charWidth++;
+ while (((xPosToRead + charWidth) < image.width) &&
+ !COLOR_EQUAL(pixels[(lineSpacing + (charHeight + lineSpacing)*lineToRead)*image.width + xPosToRead + charWidth], key)) charWidth++;
tempCharRecs[index].width = (float)charWidth;
@@ -491,7 +484,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar)
xPosToRead = charSpacing;
}
- // NOTE: We need to remove key color borders from image to avoid weird
+ // NOTE: Key color borders need to be removed from image to avoid weird
// artifacts on texture scaling when using TEXTURE_FILTER_BILINEAR or TEXTURE_FILTER_TRILINEAR
for (int i = 0; i < image.height*image.width; i++) if (COLOR_EQUAL(pixels[i], key)) pixels[i] = BLANK;
@@ -509,8 +502,8 @@ Font LoadFontFromImage(Image image, Color key, int firstChar)
font.glyphCount = index;
font.glyphPadding = 0;
- // We got tempCharValues and tempCharsRecs populated with glyphs data
- // Now we move temp data to sized charValues and charRecs arrays
+ // Populate tempCharValues and tempCharsRecs with glyphs data
+ // Move temp data to sized charValues and charRecs arrays
font.glyphs = (GlyphInfo *)RL_MALLOC(font.glyphCount*sizeof(GlyphInfo));
font.recs = (Rectangle *)RL_MALLOC(font.glyphCount*sizeof(Rectangle));
@@ -548,7 +541,7 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int
font.baseSize = fontSize;
font.glyphPadding = 0;
-#if defined(SUPPORT_FILEFORMAT_TTF)
+#if SUPPORT_FILEFORMAT_TTF
if (TextIsEqual(fileExtLower, ".ttf") ||
TextIsEqual(fileExtLower, ".otf"))
{
@@ -556,7 +549,7 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int
}
else
#endif
-#if defined(SUPPORT_FILEFORMAT_BDF)
+#if SUPPORT_FILEFORMAT_BDF
if (TextIsEqual(fileExtLower, ".bdf"))
{
font.glyphs = LoadFontDataBDF(fileData, dataSize, codepoints, (codepointCount > 0)? codepointCount : 95, &font.baseSize);
@@ -568,7 +561,7 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int
font.glyphs = NULL;
}
-#if defined(SUPPORT_FILEFORMAT_TTF) || defined(SUPPORT_FILEFORMAT_BDF)
+#if SUPPORT_FILEFORMAT_TTF || SUPPORT_FILEFORMAT_BDF
if (font.glyphs != NULL)
{
font.glyphPadding = FONT_TTF_DEFAULT_CHARS_PADDING;
@@ -629,16 +622,17 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
GlyphInfo *glyphs = NULL;
int glyphCounter = 0;
-#if defined(SUPPORT_FILEFORMAT_TTF)
+#if SUPPORT_FILEFORMAT_TTF
// Load font data (including pixel data) from TTF memory file
// NOTE: Loaded information should be enough to generate font image atlas, using any packaging method
if (fileData != NULL)
{
bool genFontChars = false;
stbtt_fontinfo fontInfo = { 0 };
- int *requiredCodepoints = (int *)codepoints; // TODO: Should we create a shallow copy to avoid "dealing" with a const user array?
+ // TODO: Should a shallow copy be created to avoid "dealing" with a const user array?
+ int *requiredCodepoints = (int *)codepoints;
- if (stbtt_InitFont(&fontInfo, (unsigned char *)fileData, 0)) // Initialize font for data reading
+ if (stbtt_InitFont(&fontInfo, (unsigned char *)fileData, 0)) // Initialize font for data reading
{
// Calculate font scale factor
float scaleFactor = stbtt_ScaleForPixelHeight(&fontInfo, (float)fontSize);
@@ -654,7 +648,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
codepointCount = (codepointCount > 0)? codepointCount : 95;
// Fill fontChars in case not provided externally
- // NOTE: By default we fill glyphCount consecutively, starting at 32 (Space)
+ // NOTE: By default filling glyphCount consecutively, starting at 32 (Space)
if (requiredCodepoints == NULL)
{
requiredCodepoints = (int *)RL_MALLOC(codepointCount*sizeof(int));
@@ -732,13 +726,13 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
}
//else TRACELOG(LOG_WARNING, "FONT: Glyph [0x%08x] has no image data available", cp); // Only reported for 0x20 and 0x3000
- // We create an empty image for Space character (0x20), useful for sprite font generation
+ // Create an empty image for Space character (0x20), useful for sprite font generation
// NOTE: Another space to consider: 0x3000 (CJK - Ideographic Space)
if ((cp == 0x20) || (cp == 0x3000))
{
stbtt_GetCodepointHMetrics(&fontInfo, cp, &glyphs[k].advanceX, NULL);
glyphs[k].advanceX = (int)((float)glyphs[k].advanceX*scaleFactor);
-
+
Image imSpace = {
.data = NULL,
.width = glyphs[k].advanceX,
@@ -789,7 +783,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
// Generate image font atlas using chars info
// NOTE: Packing method: 0-Default, 1-Skyline
-#if defined(SUPPORT_FILEFORMAT_TTF) || defined(SUPPORT_FILEFORMAT_BDF)
+#if SUPPORT_FILEFORMAT_TTF || SUPPORT_FILEFORMAT_BDF
Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod)
{
Image atlas = { 0 };
@@ -802,7 +796,7 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp
*glyphRecs = NULL;
- // In case no chars count provided we suppose default of 95
+ // In case no chars count provided, suppose default of 95
glyphCount = (glyphCount > 0)? glyphCount : 95;
// NOTE: Rectangles memory is loaded here!
@@ -843,7 +837,7 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp
atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
atlas.mipmaps = 1;
- // DEBUG: We can see padding in the generated image setting a gray background...
+ // DEBUG: View padding in the generated image setting a gray background...
//for (int i = 0; i < atlas.width*atlas.height; i++) ((unsigned char *)atlas.data)[i] = 100;
if (packMethod == 0) // Use basic packing algorithm
@@ -867,14 +861,15 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp
if (offsetY > (atlas.height - fontSize - padding))
{
TRACELOG(LOG_WARNING, "FONT: Updating atlas size to fit all characters");
-
- // TODO: Increment atlas size (atlas.height*2) and continue adding glyphs
+
+ // Update atlas size to fit all characters
int updatedAtlasHeight = atlas.height*2;
- int updatedAtlasDataSize = atlas.width*atlas.height;
+ int updatedAtlasDataSize = atlas.width*updatedAtlasHeight;
unsigned char *updatedAtlasData = (unsigned char *)RL_CALLOC(updatedAtlasDataSize, 1);
-
+
memcpy(updatedAtlasData, atlas.data, atlasDataSize);
RL_FREE(atlas.data);
+ atlas.data = updatedAtlasData;
atlas.height = updatedAtlasHeight;
atlasDataSize = updatedAtlasDataSize;
}
@@ -961,13 +956,12 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp
RL_FREE(context);
}
-#if defined(SUPPORT_FONT_ATLAS_WHITE_REC)
// Add a 3x3 white rectangle at the bottom-right corner of the generated atlas,
// useful to use as the white texture to draw shapes with raylib
// Security: ensure the atlas is large enough to hold a 3x3 rectangle
- if ((atlas.width >= 3) && (atlas.height >= 3))
+ if ((FONT_ATLAS_CORNER_REC_SIZE > 0) && (atlas.width >= 3) && (atlas.height >= 3))
{
- for (int i = 0, k = atlas.width*atlas.height - 1; i < 3; i++)
+ for (int i = 0, k = atlas.width*atlas.height - 1; i < FONT_ATLAS_CORNER_REC_SIZE; i++)
{
((unsigned char *)atlas.data)[k - 0] = 255;
((unsigned char *)atlas.data)[k - 1] = 255;
@@ -975,7 +969,6 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp
k -= atlas.width;
}
}
-#endif
// Convert image data from GRAYSCALE to GRAY_ALPHA
unsigned char *dataGrayAlpha = (unsigned char *)RL_MALLOC(atlas.width*atlas.height*sizeof(unsigned char)*2); // Two channels
@@ -1033,7 +1026,7 @@ bool ExportFontAsCode(Font font, const char *fileName)
// Get file name from path
char fileNamePascal[256] = { 0 };
strncpy(fileNamePascal, TextToPascal(GetFileNameWithoutExt(fileName)), 256 - 1);
-
+
// Get font atlas image and size, required to estimate code file size
// NOTE: This mechanism is highly coupled to raylib
Image image = LoadImageFromTexture(font.texture);
@@ -1042,13 +1035,13 @@ bool ExportFontAsCode(Font font, const char *fileName)
// Image data is usually GRAYSCALE + ALPHA and can be reduced to GRAYSCALE
//ImageFormat(&image, PIXELFORMAT_UNCOMPRESSED_GRAYSCALE);
-
+
// Estimate text code size
// - Image data is stored as "0x%02x", so it requires at least 4 char per byte, let's use 6
// - font.recs[] data is stored as "{ %1.0f, %1.0f, %1.0f , %1.0f }", let's reserve 64 per rec
// - font.glyphs[] data is stored as "{ %i, %i, %i, %i, { 0 }},\n", let's reserve 64 per glyph
// - Comments and additional code, let's reserve 32KB
- int txtDataSize = imageDataSize*6 + font.glyphCount*64 + font.glyphCount*64 + 32768;
+ int txtDataSize = imageDataSize*6 + font.glyphCount*64 + font.glyphCount*64 + 32768;
char *txtData = (char *)RL_CALLOC(txtDataSize, sizeof(char));
int byteCount = 0;
@@ -1271,14 +1264,14 @@ void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSiz
float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor
// Character destination rectangle on screen
- // NOTE: We consider glyphPadding on drawing
+ // NOTE: Considering glyph padding on drawing
Rectangle dstRec = { position.x + font.glyphs[index].offsetX*scaleFactor - (float)font.glyphPadding*scaleFactor,
position.y + font.glyphs[index].offsetY*scaleFactor - (float)font.glyphPadding*scaleFactor,
(font.recs[index].width + 2.0f*font.glyphPadding)*scaleFactor,
(font.recs[index].height + 2.0f*font.glyphPadding)*scaleFactor };
// Character source rectangle from font texture atlas
- // NOTE: We consider glyphs padding when drawing, it could be required for outline/glow shader effects
+ // NOTE: Considering glyphs padding when drawing, it could be required for outline/glow shader effects
Rectangle srcRec = { font.recs[index].x - (float)font.glyphPadding, font.recs[index].y - (float)font.glyphPadding,
font.recs[index].width + 2.0f*font.glyphPadding, font.recs[index].height + 2.0f*font.glyphPadding };
@@ -1397,6 +1390,64 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
return textSize;
}
+// Measure string size for an existing array of codepoints for Font
+Vector2 MeasureTextCodepoints(Font font, const int *codepoints, int length, float fontSize, float spacing)
+{
+ Vector2 textSize = { 0 };
+
+ // Security check
+ if ((font.texture.id == 0) || (codepoints == NULL) || (length == 0)) return textSize;
+
+ float textWidth = 0.0f;
+ // Used to count longer text line width
+ float tempTextWidth = 0.0f;
+
+ // Used to count longer text line num chars
+ int tempGlyphCounter = 0;
+ int glyphCounter = 0;
+
+ float textHeight = fontSize;
+ float scaleFactor = fontSize/(float)font.baseSize;
+
+ // Current character
+ int letter = 0;
+ // Index position in sprite font
+ int index = 0;
+
+ for (int i = 0; i < length; i++)
+ {
+ letter = codepoints[i];
+ index = GetGlyphIndex(font, letter);
+
+ if (letter != '\n')
+ {
+ glyphCounter++;
+
+ if (font.glyphs[index].advanceX > 0) textWidth += font.glyphs[index].advanceX;
+ else textWidth += (font.recs[index].width + font.glyphs[index].offsetX);
+ }
+ else
+ {
+ if (tempTextWidth < textWidth) tempTextWidth = textWidth;
+
+ textWidth = 0;
+ glyphCounter = 0;
+
+ // NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup
+ textHeight += (fontSize + textLineSpacing);
+ }
+
+ if (tempGlyphCounter < glyphCounter) tempGlyphCounter = glyphCounter;
+ }
+
+ if (tempTextWidth < textWidth) tempTextWidth = textWidth;
+
+ textSize.x = tempTextWidth*scaleFactor + (float)((tempGlyphCounter - 1)*spacing);
+ textSize.y = textHeight;
+
+ return textSize;
+}
+
// Get index position for a unicode character on font
// NOTE: If codepoint is not found in the font it fallbacks to '?'
int GetGlyphIndex(Font font, int codepoint)
@@ -1503,7 +1554,7 @@ unsigned int TextLength(const char *text)
unsigned int length = 0;
if (text != NULL)
- {
+ {
while (text[length] != '\0') length++;
}
@@ -1518,7 +1569,7 @@ const char *TextFormat(const char *text, ...)
#define MAX_TEXTFORMAT_BUFFERS 4 // Maximum number of static buffers for text formatting
#endif
- // We create an array of buffers so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations
+ // Create an array of buffers so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations
static char buffers[MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH] = { 0 };
static int index = 0;
@@ -1601,7 +1652,6 @@ float TextToFloat(const char *text)
return value*sign;
}
-#if defined(SUPPORT_TEXT_MANIPULATION)
// Copy one string to another, returns bytes copied
// NOTE: Alternative implementation to strcpy(dst, src) from C standard library
int TextCopy(char *dst, const char *src)
@@ -1718,7 +1768,7 @@ char *TextReplace(const char *text, const char *search, const char *replacement)
char *result = NULL;
if ((text != NULL) && (search != NULL) && (search[0] != '\0'))
- {
+ {
if (replacement == NULL) replacement = "";
char *insertPoint = NULL; // Next insert point
@@ -1751,18 +1801,18 @@ char *TextReplace(const char *text, const char *search, const char *replacement)
{
insertPoint = (char *)strstr(text, search);
lastReplacePos = (int)(insertPoint - text);
-
+
memcpy(temp, text, lastReplacePos);
temp += lastReplacePos;
-
+
if (replaceLen > 0)
{
memcpy(temp, replacement, replaceLen);
- temp += replaceLen;
+ temp += replaceLen;
}
text += (lastReplacePos + searchLen); // Move to next "end of replace"
-
+
count--;
}
@@ -1825,7 +1875,7 @@ char *TextInsert(const char *text, const char *insert, int position)
result = (char *)RL_MALLOC(textLen + insertLen + 1);
for (int i = 0; i < position; i++) result[i] = text[i];
- for (int i = position; i < insertLen + position; i++) result[i] = insert[i];
+ for (int i = position; i < insertLen + position; i++) result[i] = insert[i - position];
for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i];
result[textLen + insertLen] = '\0'; // Add EOL
@@ -1889,7 +1939,7 @@ char **TextSplit(const char *text, char delimiter, int *count)
{
counter = 1;
- // Count how many substrings we have on text and point to every one
+ // Count how many substrings ar found on text and set pointers to every one
for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++)
{
buffer[i] = text[i];
@@ -2069,7 +2119,7 @@ char *LoadUTF8(const int *codepoints, int length)
if ((codepoints != NULL) && (length > 0))
{
- // We allocate enough memory to fit all possible codepoints
+ // Allocate enough memory to fit all possible codepoints
// NOTE: 5 bytes for every codepoint should be enough
text = (char *)RL_CALLOC(length*5, 1);
const char *utf8 = NULL;
@@ -2195,10 +2245,9 @@ const char *CodepointToUTF8(int codepoint, int *utf8Size)
return utf8;
}
-#endif // SUPPORT_TEXT_MANIPULATION
// Get next codepoint in a UTF-8 encoded text, scanning until '\0' is found
-// When an invalid UTF-8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned
+// When an invalid UTF-8 byte is encountered, exit as soon as possible and a '?'(0x3f) codepoint is returned
// Total number of bytes processed are returned as a parameter
// NOTE: The standard says U+FFFD should be returned in case of errors
// but that character is not supported by the default font in raylib
@@ -2221,7 +2270,7 @@ int GetCodepoint(const char *text, int *codepointSize)
*codepointSize = 1;
if (text == NULL) return codepoint;
- // NOTE: on decode errors we return as soon as possible
+ // NOTE: On decoding errors, return as soon as possible
int octet = (unsigned char)(text[0]); // The first UTF8 octet
if (octet <= 0x7f)
@@ -2370,7 +2419,7 @@ int GetCodepointPrevious(const char *text, int *codepointSize)
//----------------------------------------------------------------------------------
// Module Internal Functions Definition
//----------------------------------------------------------------------------------
-#if defined(SUPPORT_FILEFORMAT_FNT) || defined(SUPPORT_FILEFORMAT_BDF)
+#if SUPPORT_FILEFORMAT_FNT || SUPPORT_FILEFORMAT_BDF
// Read a line from memory
// REQUIRES: memcpy()
// NOTE: Returns the number of bytes read
@@ -2384,7 +2433,7 @@ static int GetLine(const char *origin, char *buffer, int maxLength)
}
#endif
-#if defined(SUPPORT_FILEFORMAT_FNT)
+#if SUPPORT_FILEFORMAT_FNT
// Load a BMFont file (AngelCode font file)
// REQUIRES: strstr(), sscanf(), strrchr(), memcpy()
static Font LoadBMFont(const char *fileName)
@@ -2415,7 +2464,7 @@ static Font LoadBMFont(const char *fileName)
char *fileTextPtr = fileText;
- // NOTE: We skip first line, it contains no useful information
+ // NOTE: Skip first line, it contains no useful information
readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
fileTextPtr += (readBytes + 1);
@@ -2561,7 +2610,7 @@ static Font LoadBMFont(const char *fileName)
}
#endif
-#if defined(SUPPORT_FILEFORMAT_BDF)
+#if SUPPORT_FILEFORMAT_BDF
// Convert hexadecimal to decimal (single digit)
static unsigned char HexToInt(char hex)
{
@@ -2618,7 +2667,7 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, c
if (codepoints == NULL)
{
// Fill internal codepoints array in case not provided externally
- // NOTE: By default we fill glyphCount consecutively, starting at 32 (Space)
+ // NOTE: By default, filling glyph count consecutively, starting at 32 (Space)
for (int i = 0; i < codepointCount; i++) requiredCodepoints[i] = i + 32;
internalCodepoints = true;
}
diff --git a/src/rtextures.c b/src/rtextures.c
index 1b147796e..cf19edd1e 100644
--- a/src/rtextures.c
+++ b/src/rtextures.c
@@ -22,15 +22,11 @@
* #define SUPPORT_FILEFORMAT_PVR
* #define SUPPORT_FILEFORMAT_ASTC
* Select desired fileformats to be supported for image data loading. Some of those formats are
-* supported by default, to remove support, just comment unrequired #define in this module
+* supported by default, to remove support, comment unrequired #define in this module
*
* #define SUPPORT_IMAGE_EXPORT
* Support image export in multiple file formats
*
-* #define SUPPORT_IMAGE_MANIPULATION
-* Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop...
-* If not defined only some image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageResize*()
-*
* #define SUPPORT_IMAGE_GENERATION
* Support procedural image generation functionality (gradient, spot, perlin-noise, cellular)
*
@@ -65,7 +61,7 @@
#include "config.h" // Defines module configuration flags
-#if defined(SUPPORT_MODULE_RTEXTURES)
+#if SUPPORT_MODULE_RTEXTURES
#include "rlgl.h" // OpenGL abstraction layer to multiple versions
@@ -75,60 +71,60 @@
#include // Required for: sprintf() [Used in ExportImageAsCode()]
// Support only desired texture formats on stb_image
-#if !defined(SUPPORT_FILEFORMAT_BMP)
+#if !SUPPORT_FILEFORMAT_BMP
#define STBI_NO_BMP
#endif
-#if !defined(SUPPORT_FILEFORMAT_PNG)
+#if !SUPPORT_FILEFORMAT_PNG
#define STBI_NO_PNG
#endif
-#if !defined(SUPPORT_FILEFORMAT_TGA)
+#if !SUPPORT_FILEFORMAT_TGA
#define STBI_NO_TGA
#endif
-#if !defined(SUPPORT_FILEFORMAT_JPG)
+#if !SUPPORT_FILEFORMAT_JPG
#define STBI_NO_JPEG // Image format .jpg and .jpeg
#endif
-#if !defined(SUPPORT_FILEFORMAT_PSD)
+#if !SUPPORT_FILEFORMAT_PSD
#define STBI_NO_PSD
#endif
-#if !defined(SUPPORT_FILEFORMAT_GIF)
+#if !SUPPORT_FILEFORMAT_GIF
#define STBI_NO_GIF
#endif
-#if !defined(SUPPORT_FILEFORMAT_PIC)
+#if !SUPPORT_FILEFORMAT_PIC
#define STBI_NO_PIC
#endif
-#if !defined(SUPPORT_FILEFORMAT_HDR)
+#if !SUPPORT_FILEFORMAT_HDR
#define STBI_NO_HDR
#endif
-#if !defined(SUPPORT_FILEFORMAT_PNM)
+#if !SUPPORT_FILEFORMAT_PNM
#define STBI_NO_PNM
#endif
-#if defined(SUPPORT_FILEFORMAT_DDS)
+#if SUPPORT_FILEFORMAT_DDS
#define RL_GPUTEX_SUPPORT_DDS
#endif
-#if defined(SUPPORT_FILEFORMAT_PKM)
+#if SUPPORT_FILEFORMAT_PKM
#define RL_GPUTEX_SUPPORT_PKM
#endif
-#if defined(SUPPORT_FILEFORMAT_KTX)
+#if SUPPORT_FILEFORMAT_KTX
#define RL_GPUTEX_SUPPORT_KTX
#endif
-#if defined(SUPPORT_FILEFORMAT_PVR)
+#if SUPPORT_FILEFORMAT_PVR
#define RL_GPUTEX_SUPPORT_PVR
#endif
-#if defined(SUPPORT_FILEFORMAT_ASTC)
+#if SUPPORT_FILEFORMAT_ASTC
#define RL_GPUTEX_SUPPORT_ASTC
#endif
// Image fileformats not supported by default
-#if (defined(SUPPORT_FILEFORMAT_BMP) || \
- defined(SUPPORT_FILEFORMAT_PNG) || \
- defined(SUPPORT_FILEFORMAT_TGA) || \
- defined(SUPPORT_FILEFORMAT_JPG) || \
- defined(SUPPORT_FILEFORMAT_PSD) || \
- defined(SUPPORT_FILEFORMAT_GIF) || \
- defined(SUPPORT_FILEFORMAT_HDR) || \
- defined(SUPPORT_FILEFORMAT_PIC) || \
- defined(SUPPORT_FILEFORMAT_PNM))
+#if (SUPPORT_FILEFORMAT_BMP || \
+ SUPPORT_FILEFORMAT_PNG || \
+ SUPPORT_FILEFORMAT_TGA || \
+ SUPPORT_FILEFORMAT_JPG || \
+ SUPPORT_FILEFORMAT_PSD || \
+ SUPPORT_FILEFORMAT_GIF || \
+ SUPPORT_FILEFORMAT_HDR || \
+ SUPPORT_FILEFORMAT_PIC || \
+ SUPPORT_FILEFORMAT_PNM)
#if defined(__GNUC__) // GCC and Clang
#pragma GCC diagnostic push
@@ -154,11 +150,11 @@
#endif
#endif
-#if (defined(SUPPORT_FILEFORMAT_DDS) || \
- defined(SUPPORT_FILEFORMAT_PKM) || \
- defined(SUPPORT_FILEFORMAT_KTX) || \
- defined(SUPPORT_FILEFORMAT_PVR) || \
- defined(SUPPORT_FILEFORMAT_ASTC))
+#if (SUPPORT_FILEFORMAT_DDS || \
+ SUPPORT_FILEFORMAT_PKM || \
+ SUPPORT_FILEFORMAT_KTX || \
+ SUPPORT_FILEFORMAT_PVR || \
+ SUPPORT_FILEFORMAT_ASTC)
#if defined(__GNUC__) // GCC and Clang
#pragma GCC diagnostic push
@@ -177,7 +173,7 @@
#endif
#endif
-#if defined(SUPPORT_FILEFORMAT_QOI)
+#if SUPPORT_FILEFORMAT_QOI
#define QOI_MALLOC RL_MALLOC
#define QOI_FREE RL_FREE
@@ -195,7 +191,7 @@
#endif
-#if defined(SUPPORT_IMAGE_EXPORT)
+#if SUPPORT_IMAGE_EXPORT
#define STBIW_MALLOC RL_MALLOC
#define STBIW_FREE RL_FREE
#define STBIW_REALLOC RL_REALLOC
@@ -204,7 +200,7 @@
#include "external/stb_image_write.h" // Required for: stbi_write_*()
#endif
-#if defined(SUPPORT_IMAGE_GENERATION)
+#if SUPPORT_IMAGE_GENERATION
#define STB_PERLIN_IMPLEMENTATION
#include "external/stb_perlin.h" // Required for: stb_perlin_fbm_noise3
#endif
@@ -268,15 +264,15 @@ Image LoadImage(const char *fileName)
{
Image image = { 0 };
-#if defined(SUPPORT_FILEFORMAT_PNG) || \
- defined(SUPPORT_FILEFORMAT_BMP) || \
- defined(SUPPORT_FILEFORMAT_TGA) || \
- defined(SUPPORT_FILEFORMAT_JPG) || \
- defined(SUPPORT_FILEFORMAT_GIF) || \
- defined(SUPPORT_FILEFORMAT_PIC) || \
- defined(SUPPORT_FILEFORMAT_HDR) || \
- defined(SUPPORT_FILEFORMAT_PNM) || \
- defined(SUPPORT_FILEFORMAT_PSD)
+#if SUPPORT_FILEFORMAT_PNG || \
+ SUPPORT_FILEFORMAT_BMP || \
+ SUPPORT_FILEFORMAT_TGA || \
+ SUPPORT_FILEFORMAT_JPG || \
+ SUPPORT_FILEFORMAT_GIF || \
+ SUPPORT_FILEFORMAT_PIC || \
+ SUPPORT_FILEFORMAT_HDR || \
+ SUPPORT_FILEFORMAT_PNM || \
+ SUPPORT_FILEFORMAT_PSD
#define STBI_REQUIRED
#endif
@@ -338,7 +334,7 @@ Image LoadImageAnim(const char *fileName, int *frames)
Image image = { 0 };
int frameCount = 0;
-#if defined(SUPPORT_FILEFORMAT_GIF)
+#if SUPPORT_FILEFORMAT_GIF
if (IsFileExtension(fileName, ".gif"))
{
int dataSize = 0;
@@ -383,7 +379,7 @@ Image LoadImageAnimFromMemory(const char *fileType, const unsigned char *fileDat
// Security check for input data
if ((fileType == NULL) || (fileData == NULL) || (dataSize == 0)) return image;
-#if defined(SUPPORT_FILEFORMAT_GIF)
+#if SUPPORT_FILEFORMAT_GIF
if ((strcmp(fileType, ".gif") == 0) || (strcmp(fileType, ".GIF") == 0))
{
if (fileData != NULL)
@@ -430,30 +426,30 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i
}
if ((false)
-#if defined(SUPPORT_FILEFORMAT_PNG)
+#if SUPPORT_FILEFORMAT_PNG
|| (strcmp(fileType, ".png") == 0) || (strcmp(fileType, ".PNG") == 0)
#endif
-#if defined(SUPPORT_FILEFORMAT_BMP)
+#if SUPPORT_FILEFORMAT_BMP
|| (strcmp(fileType, ".bmp") == 0) || (strcmp(fileType, ".BMP") == 0)
#endif
-#if defined(SUPPORT_FILEFORMAT_TGA)
+#if SUPPORT_FILEFORMAT_TGA
|| (strcmp(fileType, ".tga") == 0) || (strcmp(fileType, ".TGA") == 0)
#endif
-#if defined(SUPPORT_FILEFORMAT_JPG)
+#if SUPPORT_FILEFORMAT_JPG
|| (strcmp(fileType, ".jpg") == 0) || (strcmp(fileType, ".jpeg") == 0)
|| (strcmp(fileType, ".JPG") == 0) || (strcmp(fileType, ".JPEG") == 0)
#endif
-#if defined(SUPPORT_FILEFORMAT_GIF)
+#if SUPPORT_FILEFORMAT_GIF
|| (strcmp(fileType, ".gif") == 0) || (strcmp(fileType, ".GIF") == 0)
#endif
-#if defined(SUPPORT_FILEFORMAT_PIC)
+#if SUPPORT_FILEFORMAT_PIC
|| (strcmp(fileType, ".pic") == 0) || (strcmp(fileType, ".PIC") == 0)
#endif
-#if defined(SUPPORT_FILEFORMAT_PNM)
+#if SUPPORT_FILEFORMAT_PNM
|| (strcmp(fileType, ".ppm") == 0) || (strcmp(fileType, ".pgm") == 0)
|| (strcmp(fileType, ".PPM") == 0) || (strcmp(fileType, ".PGM") == 0)
#endif
-#if defined(SUPPORT_FILEFORMAT_PSD)
+#if SUPPORT_FILEFORMAT_PSD
|| (strcmp(fileType, ".psd") == 0) || (strcmp(fileType, ".PSD") == 0)
#endif
)
@@ -478,7 +474,7 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i
}
#endif
}
-#if defined(SUPPORT_FILEFORMAT_HDR)
+#if SUPPORT_FILEFORMAT_HDR
else if ((strcmp(fileType, ".hdr") == 0) || (strcmp(fileType, ".HDR") == 0))
{
#if defined(STBI_REQUIRED)
@@ -501,7 +497,7 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i
#endif
}
#endif
-#if defined(SUPPORT_FILEFORMAT_QOI)
+#if SUPPORT_FILEFORMAT_QOI
else if ((strcmp(fileType, ".qoi") == 0) || (strcmp(fileType, ".QOI") == 0))
{
if (fileData != NULL)
@@ -515,31 +511,31 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i
}
}
#endif
-#if defined(SUPPORT_FILEFORMAT_DDS)
+#if SUPPORT_FILEFORMAT_DDS
else if ((strcmp(fileType, ".dds") == 0) || (strcmp(fileType, ".DDS") == 0))
{
image.data = rl_load_dds_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps);
}
#endif
-#if defined(SUPPORT_FILEFORMAT_PKM)
+#if SUPPORT_FILEFORMAT_PKM
else if ((strcmp(fileType, ".pkm") == 0) || (strcmp(fileType, ".PKM") == 0))
{
image.data = rl_load_pkm_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps);
}
#endif
-#if defined(SUPPORT_FILEFORMAT_KTX)
+#if SUPPORT_FILEFORMAT_KTX
else if ((strcmp(fileType, ".ktx") == 0) || (strcmp(fileType, ".KTX") == 0))
{
image.data = rl_load_ktx_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps);
}
#endif
-#if defined(SUPPORT_FILEFORMAT_PVR)
+#if SUPPORT_FILEFORMAT_PVR
else if ((strcmp(fileType, ".pvr") == 0) || (strcmp(fileType, ".PVR") == 0))
{
image.data = rl_load_pvr_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps);
}
#endif
-#if defined(SUPPORT_FILEFORMAT_ASTC)
+#if SUPPORT_FILEFORMAT_ASTC
else if ((strcmp(fileType, ".astc") == 0) || (strcmp(fileType, ".ASTC") == 0))
{
image.data = rl_load_astc_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps);
@@ -628,7 +624,7 @@ bool ExportImage(Image image, const char *fileName)
// Security check for input data
if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return result;
-#if defined(SUPPORT_IMAGE_EXPORT)
+#if SUPPORT_IMAGE_EXPORT
int channels = 4;
bool allocatedData = false;
unsigned char *imgData = (unsigned char *)image.data;
@@ -645,7 +641,7 @@ bool ExportImage(Image image, const char *fileName)
}
if (false) { } // Required to attach following 'else' cases
-#if defined(SUPPORT_FILEFORMAT_PNG)
+#if SUPPORT_FILEFORMAT_PNG
else if (IsFileExtension(fileName, ".png"))
{
int dataSize = 0;
@@ -654,17 +650,17 @@ bool ExportImage(Image image, const char *fileName)
RL_FREE(fileData);
}
#endif
-#if defined(SUPPORT_FILEFORMAT_BMP)
+#if SUPPORT_FILEFORMAT_BMP
else if (IsFileExtension(fileName, ".bmp")) result = stbi_write_bmp(fileName, image.width, image.height, channels, imgData);
#endif
-#if defined(SUPPORT_FILEFORMAT_TGA)
+#if SUPPORT_FILEFORMAT_TGA
else if (IsFileExtension(fileName, ".tga")) result = stbi_write_tga(fileName, image.width, image.height, channels, imgData);
#endif
-#if defined(SUPPORT_FILEFORMAT_JPG)
+#if SUPPORT_FILEFORMAT_JPG
else if (IsFileExtension(fileName, ".jpg") ||
IsFileExtension(fileName, ".jpeg")) result = stbi_write_jpg(fileName, image.width, image.height, channels, imgData, 90); // JPG quality: between 1 and 100
#endif
-#if defined(SUPPORT_FILEFORMAT_QOI)
+#if SUPPORT_FILEFORMAT_QOI
else if (IsFileExtension(fileName, ".qoi"))
{
channels = 0;
@@ -684,7 +680,7 @@ bool ExportImage(Image image, const char *fileName)
}
}
#endif
-#if defined(SUPPORT_FILEFORMAT_KTX)
+#if SUPPORT_FILEFORMAT_KTX
else if (IsFileExtension(fileName, ".ktx"))
{
result = rl_save_ktx(fileName, image.data, image.width, image.height, image.format, image.mipmaps);
@@ -699,7 +695,7 @@ bool ExportImage(Image image, const char *fileName)
else TRACELOG(LOG_WARNING, "IMAGE: Export image format requested not supported");
if (allocatedData) RL_FREE(imgData);
-#endif // SUPPORT_IMAGE_EXPORT
+#endif // SUPPORT_IMAGE_EXPORT
if (result != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image exported successfully", fileName);
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image", fileName);
@@ -716,7 +712,6 @@ unsigned char *ExportImageToMemory(Image image, const char *fileType, int *dataS
// Security check for input data
if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return NULL;
-#if defined(SUPPORT_IMAGE_EXPORT)
int channels = 4;
if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) channels = 1;
@@ -724,13 +719,13 @@ unsigned char *ExportImageToMemory(Image image, const char *fileType, int *dataS
else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) channels = 3;
else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) channels = 4;
-#if defined(SUPPORT_FILEFORMAT_PNG)
+#if SUPPORT_IMAGE_EXPORT
if ((strcmp(fileType, ".png") == 0) || (strcmp(fileType, ".PNG") == 0))
{
fileData = stbi_write_png_to_mem((const unsigned char *)image.data, image.width*channels, image.width, image.height, channels, dataSize);
}
-#endif
-
+#else
+ TRACELOG(LOG_WARNING, "IMAGE: To export image, enable flag SUPPORT_IMAGE_EXPORT");
#endif
return fileData;
@@ -741,8 +736,6 @@ bool ExportImageAsCode(Image image, const char *fileName)
{
bool success = false;
-#if defined(SUPPORT_IMAGE_EXPORT)
-
#ifndef TEXT_BYTES_PER_LINE
#define TEXT_BYTES_PER_LINE 20
#endif
@@ -785,8 +778,6 @@ bool ExportImageAsCode(Image image, const char *fileName)
RL_FREE(txtData);
-#endif // SUPPORT_IMAGE_EXPORT
-
if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image as code exported successfully", fileName);
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image as code", fileName);
@@ -814,7 +805,7 @@ Image GenImageColor(int width, int height, Color color)
return image;
}
-#if defined(SUPPORT_IMAGE_GENERATION)
+#if SUPPORT_IMAGE_GENERATION
// Generate image: linear gradient
// The direction value specifies the direction of the gradient (in degrees)
// with 0 being vertical (from top to bottom), 90 being horizontal (from left to right)
@@ -1137,7 +1128,7 @@ Image GenImageText(int width, int height, const char *text)
return image;
}
-#endif // SUPPORT_IMAGE_GENERATION
+#endif // SUPPORT_IMAGE_GENERATION
//------------------------------------------------------------------------------------
// Image manipulation functions
@@ -1451,9 +1442,7 @@ void ImageFormat(Image *image, int newFormat)
if (image->mipmaps > 1)
{
image->mipmaps = 1;
- #if defined(SUPPORT_IMAGE_MANIPULATION)
if (image->data != NULL) ImageMipmaps(image);
- #endif
}
}
else TRACELOG(LOG_WARNING, "IMAGE: Data format is compressed, can not be converted");
@@ -1464,7 +1453,7 @@ void ImageFormat(Image *image, int newFormat)
Image ImageText(const char *text, int fontSize, Color color)
{
Image imText = { 0 };
-#if defined(SUPPORT_MODULE_RTEXT)
+#if SUPPORT_MODULE_RTEXT
int defaultFontSize = 10; // Default Font chars height in pixel
if (fontSize < defaultFontSize) fontSize = defaultFontSize;
int spacing = fontSize/defaultFontSize;
@@ -1481,7 +1470,7 @@ Image ImageText(const char *text, int fontSize, Color color)
Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint)
{
Image imText = { 0 };
-#if defined(SUPPORT_MODULE_RTEXT)
+#if SUPPORT_MODULE_RTEXT
if (text == NULL) return imText;
int textLength = (int)strlen(text); // Get length of text in bytes
@@ -1873,7 +1862,6 @@ void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, i
}
}
-#if defined(SUPPORT_IMAGE_MANIPULATION)
// Convert image to POT (power-of-two)
// NOTE: It could be useful on OpenGL ES 2.0 (RPI, HTML5)
void ImageToPOT(Image *image, Color fill)
@@ -1882,7 +1870,7 @@ void ImageToPOT(Image *image, Color fill)
if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
// Calculate next power-of-two values
- // NOTE: Just add the required amount of pixels at the right and bottom sides of image...
+ // NOTE: Add the required amount of pixels at the right and bottom sides of image...
int potWidth = (int)powf(2, ceilf(logf((float)image->width)/logf(2)));
int potHeight = (int)powf(2, ceilf(logf((float)image->height)/logf(2)));
@@ -2026,7 +2014,7 @@ void ImageAlphaMask(Image *image, Image alphaMask)
Image mask = ImageCopy(alphaMask);
if (mask.format != PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) ImageFormat(&mask, PIXELFORMAT_UNCOMPRESSED_GRAYSCALE);
- // In case image is only grayscale, just add alpha channel
+ // In case image is only grayscale, add alpha channel
if (image->format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)
{
unsigned char *data = (unsigned char *)RL_MALLOC(image->width*image->height*2);
@@ -2601,7 +2589,7 @@ void ImageFlipHorizontal(Image *image)
// OPTION 1: Move pixels with memcpy()
//memcpy(flippedData + (y*image->width + x)*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + (image->width - 1 - x))*bytesPerPixel, bytesPerPixel);
- // OPTION 2: Just copy data pixel by pixel
+ // OPTION 2: Copy data pixel by pixel
for (int i = 0; i < bytesPerPixel; i++) flippedData[(y*image->width + x)*bytesPerPixel + i] = ((unsigned char *)image->data)[(y*image->width + (image->width - 1 - x))*bytesPerPixel + i];
}
}
@@ -2944,7 +2932,6 @@ void ImageColorReplace(Image *image, Color color, Color replace)
(format == PIXELFORMAT_COMPRESSED_ETC2_RGB) ||
(format == PIXELFORMAT_COMPRESSED_PVRT_RGB)) ImageFormat(image, format);
}
-#endif // SUPPORT_IMAGE_MANIPULATION
// Load color data from image as a Color array (RGBA - 32bit)
// NOTE: Memory allocated should be freed using UnloadImageColors();
@@ -4095,7 +4082,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color
// Draw text (default font) within an image (destination)
void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color)
{
-#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
+#if SUPPORT_MODULE_RTEXT
// Make sure default font is loaded to be used on image text drawing
if (GetFontDefault().texture.id == 0) LoadFontDefault();
@@ -4234,13 +4221,11 @@ TextureCubemap LoadTextureCubemap(Image image, int layout)
ImageFormat(&faces, image.format);
Image mipmapped = ImageCopy(image);
- #if defined(SUPPORT_IMAGE_MANIPULATION)
if (image.mipmaps > 1)
{
ImageMipmaps(&mipmapped);
ImageMipmaps(&faces);
}
- #endif
for (int i = 0; i < 6; i++) ImageDraw(&faces, mipmapped, faceRecs[i], (Rectangle){ 0, (float)size*i, (float)size, (float)size }, WHITE);
@@ -4602,10 +4587,10 @@ void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2
rlSetTexture(0);
// NOTE: Vertex position can be transformed using matrices
- // but the process is way more costly than just calculating
+ // but the process is way more costly than calculating
// the vertex positions manually, like done above
// Old implementation is left here for educational purposes,
- // just in case someone wants to do some performance test
+ // in case someone wants to do some performance test
/*
rlSetTexture(texture.id);
rlPushMatrix();
@@ -5599,4 +5584,4 @@ static Vector4 *LoadImageDataNormalized(Image image)
return pixels;
}
-#endif // SUPPORT_MODULE_RTEXTURES
\ No newline at end of file
+#endif // SUPPORT_MODULE_RTEXTURES
diff --git a/src/shell.html b/src/shell.html
index e5e26a3b1..f1bf066d8 100644
--- a/src/shell.html
+++ b/src/shell.html
@@ -179,7 +179,7 @@ jwE50AGjLCVuS8Yt4H7OgZLKK5EKOsLviEWJSL/+0uMi7gLUSBseYwqEbXvSHCec1CJvZPyHCmYQffaB
-
+
diff --git a/tools/rexm/reports/examples_issues.md b/tools/rexm/reports/examples_issues.md
index 3fcc136e5..a648295cc 100644
--- a/tools/rexm/reports/examples_issues.md
+++ b/tools/rexm/reports/examples_issues.md
@@ -20,4 +20,3 @@ Example elements validated:
```
| **EXAMPLE NAME** | [C] | [CAT]| [INFO]|[PNG]|[WPNG]| [RES]| [MK] |[MKWEB]| [VCX]| [SOL]|[RDME]|[JS] | [WOUT]|[WMETA]|
|:---------------------------------|:---:|:----:|:-----:|:---:|:----:|:----:|:----:|:-----:|:----:|:----:|:----:|:---:|:-----:|:-----:|
-| models_animation_bone_blending | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
diff --git a/tools/rexm/reports/examples_validation.md b/tools/rexm/reports/examples_validation.md
index 9c0c02956..e46b93e46 100644
--- a/tools/rexm/reports/examples_validation.md
+++ b/tools/rexm/reports/examples_validation.md
@@ -155,7 +155,7 @@ Example elements validated:
| text_inline_styling | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| text_words_alignment | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| text_strings_management | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
-| models_animation_playing | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| models_loading_iqm | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| models_billboard_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| models_box_collisions | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| models_cubicmap_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
@@ -182,8 +182,9 @@ Example elements validated:
| models_rotating_cube | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| models_decals | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| models_directional_billboard | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
-| models_animation_bone_blending | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| models_animation_blend_custom | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| models_animation_blending | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
+| models_animation_timming | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| shaders_ascii_rendering | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| shaders_basic_lighting | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
| shaders_model_shader | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c
index 30ef6411f..c681530c9 100644
--- a/tools/rexm/rexm.c
+++ b/tools/rexm/rexm.c
@@ -659,7 +659,7 @@ int main(int argc, char *argv[])
// we must store provided file paths because pointers will be overwriten
// TODO: It seems projects are added to solution BUT not to required solution folder,
// that process still requires to be done manually
- LOG("INFO: [%s] Adding project to raylib solution (.sln)\n",
+ LOG("INFO: [%s] Adding project to raylib solution (.sln)\n",
TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName));
AddVSProjectToSolution(exVSProjectSolutionFile,
TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName), exCategory);
@@ -1040,7 +1040,7 @@ int main(int argc, char *argv[])
// Find position to add new example on list, just before the following category
// Category order: core, shapes, textures, text, models, shaders, audio
int exListNextCatIndex = -1;
- if (nextCatIndex != -1) exListNextCatIndex = TextFindIndex(exList, exCategories[nextCatIndex]);
+ if (nextCatIndex != -1) exListNextCatIndex = TextFindIndex(exList, TextFormat("\n%s", exCategories[nextCatIndex])) + 1;
else exListNextCatIndex = exListLen; // EOF
strncpy(exListUpdated, exList, exListNextCatIndex);
@@ -1254,7 +1254,7 @@ int main(int argc, char *argv[])
exCollection[i].status &= ~VALID_NOT_IN_README;
exCollection[i].status &= ~VALID_NOT_IN_JS;
}
-
+
// Check examples "status" information
for (int i = 0; i < exCollectionCount; i++)
{
@@ -2686,21 +2686,21 @@ static int AddVSProjectToSolution(const char *slnFile, const char *projFile, con
// Add project folder line
// NOTE: Folder uuid depends on category
- if (strcmp(category, "core") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex,
+ if (strcmp(category, "core") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex,
TextFormat("\t\t{%s} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035}\n", uuid));
- else if (strcmp(category, "shapes") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex,
+ else if (strcmp(category, "shapes") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex,
TextFormat("\t\t{%s} = {278D8859-20B1-428F-8448-064F46E1F021}\n", uuid));
- else if (strcmp(category, "textures") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex,
+ else if (strcmp(category, "textures") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex,
TextFormat("\t\t{%s} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE}\n", uuid));
- else if (strcmp(category, "text") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex,
+ else if (strcmp(category, "text") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex,
TextFormat("\t\t{%s} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A}\n", uuid));
- else if (strcmp(category, "models") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex,
+ else if (strcmp(category, "models") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex,
TextFormat("\t\t{%s} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C}\n", uuid));
- else if (strcmp(category, "shaders") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex,
+ else if (strcmp(category, "shaders") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex,
TextFormat("\t\t{%s} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9}\n", uuid));
- else if (strcmp(category, "audio") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex,
+ else if (strcmp(category, "audio") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex,
TextFormat("\t\t{%s} = {CC132A4D-D081-4C26-BFB9-AB11984054F8}\n", uuid));
- else if (strcmp(category, "other") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex,
+ else if (strcmp(category, "other") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex,
TextFormat("\t\t{%s} = {E9D708A5-9C1F-4B84-A795-C5F191801762}\n", uuid));
else LOG("WARNING: Provided category is not valid: %s\n", category);
//----------------------------------------------------------------------------------------
diff --git a/tools/rlparser/output/raylib_api.json b/tools/rlparser/output/raylib_api.json
index f102abb1d..082e349b7 100644
--- a/tools/rlparser/output/raylib_api.json
+++ b/tools/rlparser/output/raylib_api.json
@@ -9,13 +9,13 @@
{
"name": "RAYLIB_VERSION_MAJOR",
"type": "INT",
- "value": 5,
+ "value": 6,
"description": ""
},
{
"name": "RAYLIB_VERSION_MINOR",
"type": "INT",
- "value": 6,
+ "value": 0,
"description": ""
},
{
@@ -27,7 +27,7 @@
{
"name": "RAYLIB_VERSION",
"type": "STRING",
- "value": "5.6-dev",
+ "value": "6.0",
"description": ""
},
{
@@ -837,6 +837,21 @@
"name": "indices",
"description": "Vertex indices (in case vertex data comes indexed)"
},
+ {
+ "type": "int",
+ "name": "boneCount",
+ "description": "Number of bones (MAX: 256 bones)"
+ },
+ {
+ "type": "unsigned char *",
+ "name": "boneIndices",
+ "description": "Vertex bone indices, up to 4 bones influence by vertex (skinning) (shader-location = 6)"
+ },
+ {
+ "type": "float *",
+ "name": "boneWeights",
+ "description": "Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7)"
+ },
{
"type": "float *",
"name": "animVertices",
@@ -847,26 +862,6 @@
"name": "animNormals",
"description": "Animated normals (after bones transformations)"
},
- {
- "type": "unsigned char *",
- "name": "boneIds",
- "description": "Vertex bone ids, max 255 bone ids, up to 4 bones influence by vertex (skinning) (shader-location = 6)"
- },
- {
- "type": "float *",
- "name": "boneWeights",
- "description": "Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7)"
- },
- {
- "type": "Matrix *",
- "name": "boneMatrices",
- "description": "Bones animated transformation matrices"
- },
- {
- "type": "int",
- "name": "boneCount",
- "description": "Number of bones"
- },
{
"type": "unsigned int",
"name": "vaoId",
@@ -974,6 +969,27 @@
}
]
},
+ {
+ "name": "ModelSkeleton",
+ "description": "Skeleton, animation bones hierarchy",
+ "fields": [
+ {
+ "type": "int",
+ "name": "boneCount",
+ "description": "Number of bones"
+ },
+ {
+ "type": "BoneInfo *",
+ "name": "bones",
+ "description": "Bones information (skeleton)"
+ },
+ {
+ "type": "ModelAnimPose",
+ "name": "bindPose",
+ "description": "Bones base transformation (Transform[])"
+ }
+ ]
+ },
{
"name": "Model",
"description": "Model, meshes, materials and animation data",
@@ -1009,25 +1025,25 @@
"description": "Mesh material number"
},
{
- "type": "int",
- "name": "boneCount",
- "description": "Number of bones"
+ "type": "ModelSkeleton",
+ "name": "skeleton",
+ "description": "Skeleton for animation"
},
{
- "type": "BoneInfo *",
- "name": "bones",
- "description": "Bones information (skeleton)"
+ "type": "ModelAnimPose",
+ "name": "currentPose",
+ "description": "Current animation pose (Transform[])"
},
{
- "type": "Transform *",
- "name": "bindPose",
- "description": "Bones base transformation (pose)"
+ "type": "Matrix *",
+ "name": "boneMatrices",
+ "description": "Bones animated transformation matrices"
}
]
},
{
"name": "ModelAnimation",
- "description": "ModelAnimation",
+ "description": "ModelAnimation, contains a full animation sequence",
"fields": [
{
"type": "char[32]",
@@ -1037,22 +1053,17 @@
{
"type": "int",
"name": "boneCount",
- "description": "Number of bones"
+ "description": "Number of bones (per pose)"
},
{
"type": "int",
- "name": "frameCount",
- "description": "Number of animation frames"
+ "name": "keyframeCount",
+ "description": "Number of animation key frames"
},
{
- "type": "BoneInfo *",
- "name": "bones",
- "description": "Bones information (skeleton)"
- },
- {
- "type": "Transform **",
- "name": "framePoses",
- "description": "Poses array by frame"
+ "type": "ModelAnimPose *",
+ "name": "keyframePoses",
+ "description": "Animation sequence keyframe poses [keyframe][pose]"
}
]
},
@@ -1404,6 +1415,11 @@
"type": "Camera3D",
"name": "Camera",
"description": "Camera type fallback, defaults to Camera3D"
+ },
+ {
+ "type": "Transform",
+ "name": "*ModelAnimPose",
+ "description": "Anim pose, an array of Transform[]"
}
],
"enums": [
@@ -2204,7 +2220,7 @@
{
"name": "GAMEPAD_BUTTON_UNKNOWN",
"value": 0,
- "description": "Unknown button, just for error checking"
+ "description": "Unknown button, for error checking"
},
{
"name": "GAMEPAD_BUTTON_LEFT_FACE_UP",
@@ -2502,7 +2518,7 @@
{
"name": "SHADER_LOC_MAP_HEIGHT",
"value": 21,
- "description": "Shader location: sampler2d texture: height"
+ "description": "Shader location: sampler2d texture: heightmap"
},
{
"name": "SHADER_LOC_MAP_CUBEMAP",
@@ -2527,22 +2543,22 @@
{
"name": "SHADER_LOC_VERTEX_BONEIDS",
"value": 26,
- "description": "Shader location: vertex attribute: boneIds"
+ "description": "Shader location: vertex attribute: bone indices"
},
{
"name": "SHADER_LOC_VERTEX_BONEWEIGHTS",
"value": 27,
- "description": "Shader location: vertex attribute: boneWeights"
+ "description": "Shader location: vertex attribute: bone weights"
},
{
- "name": "SHADER_LOC_BONE_MATRICES",
+ "name": "SHADER_LOC_MATRIX_BONETRANSFORMS",
"value": 28,
- "description": "Shader location: array of matrices uniform: boneMatrices"
+ "description": "Shader location: matrix attribute: bone transforms (animation)"
},
{
- "name": "SHADER_LOC_VERTEX_INSTANCE_TX",
+ "name": "SHADER_LOC_VERTEX_INSTANCETRANSFORM",
"value": 29,
- "description": "Shader location: vertex attribute: instanceTransform"
+ "description": "Shader location: vertex attribute: instance transforms"
}
]
},
@@ -2776,7 +2792,7 @@
{
"name": "TEXTURE_FILTER_POINT",
"value": 0,
- "description": "No filter, just pixel approximation"
+ "description": "No filter, pixel approximation"
},
{
"name": "TEXTURE_FILTER_BILINEAR",
@@ -9598,6 +9614,33 @@
}
]
},
+ {
+ "name": "MeasureTextCodepoints",
+ "description": "Measure string size for an existing array of codepoints for Font",
+ "returnType": "Vector2",
+ "params": [
+ {
+ "type": "Font",
+ "name": "font"
+ },
+ {
+ "type": "const int *",
+ "name": "codepoints"
+ },
+ {
+ "type": "int",
+ "name": "length"
+ },
+ {
+ "type": "float",
+ "name": "fontSize"
+ },
+ {
+ "type": "float",
+ "name": "spacing"
+ }
+ ]
+ },
{
"name": "GetGlyphIndex",
"description": "Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found",
@@ -11400,7 +11443,7 @@
},
{
"name": "UpdateModelAnimation",
- "description": "Update model animation pose (CPU)",
+ "description": "Update model animation pose (vertex buffers and bone matrices)",
"returnType": "void",
"params": [
{
@@ -11412,33 +11455,14 @@
"name": "anim"
},
{
- "type": "int",
+ "type": "float",
"name": "frame"
}
]
},
{
- "name": "UpdateModelAnimationBones",
- "description": "Update model animation mesh bone matrices (GPU skinning)",
- "returnType": "void",
- "params": [
- {
- "type": "Model",
- "name": "model"
- },
- {
- "type": "ModelAnimation",
- "name": "anim"
- },
- {
- "type": "int",
- "name": "frame"
- }
- ]
- },
- {
- "name": "UpdateModelAnimationBonesLerp",
- "description": "Update model animation mesh bone matrices with interpolation between two poses(GPU skinning)",
+ "name": "UpdateModelAnimationEx",
+ "description": "Update model animation pose, blending two animations",
"returnType": "void",
"params": [
{
@@ -11450,7 +11474,7 @@
"name": "animA"
},
{
- "type": "int",
+ "type": "float",
"name": "frameA"
},
{
@@ -11458,34 +11482,12 @@
"name": "animB"
},
{
- "type": "int",
+ "type": "float",
"name": "frameB"
},
{
"type": "float",
- "name": "value"
- }
- ]
- },
- {
- "name": "UpdateModelVertsToCurrentBones",
- "description": "Update model vertices according to mesh bone matrices (CPU)",
- "returnType": "void",
- "params": [
- {
- "type": "Model",
- "name": "model"
- }
- ]
- },
- {
- "name": "UnloadModelAnimation",
- "description": "Unload animation data",
- "returnType": "void",
- "params": [
- {
- "type": "ModelAnimation",
- "name": "anim"
+ "name": "blend"
}
]
},
@@ -12410,7 +12412,7 @@
},
{
"name": "SetAudioStreamPan",
- "description": "Set pan for audio stream (0.5 is centered)",
+ "description": "Set pan for audio stream (-1.0 to 1.0 range, 0.0 is centered)",
"returnType": "void",
"params": [
{
diff --git a/tools/rlparser/output/raylib_api.lua b/tools/rlparser/output/raylib_api.lua
index 3707896bb..b68ab7563 100644
--- a/tools/rlparser/output/raylib_api.lua
+++ b/tools/rlparser/output/raylib_api.lua
@@ -9,13 +9,13 @@ return {
{
name = "RAYLIB_VERSION_MAJOR",
type = "INT",
- value = 5,
+ value = 6,
description = ""
},
{
name = "RAYLIB_VERSION_MINOR",
type = "INT",
- value = 6,
+ value = 0,
description = ""
},
{
@@ -27,7 +27,7 @@ return {
{
name = "RAYLIB_VERSION",
type = "STRING",
- value = "5.6-dev",
+ value = "6.0",
description = ""
},
{
@@ -837,6 +837,21 @@ return {
name = "indices",
description = "Vertex indices (in case vertex data comes indexed)"
},
+ {
+ type = "int",
+ name = "boneCount",
+ description = "Number of bones (MAX: 256 bones)"
+ },
+ {
+ type = "unsigned char *",
+ name = "boneIndices",
+ description = "Vertex bone indices, up to 4 bones influence by vertex (skinning) (shader-location = 6)"
+ },
+ {
+ type = "float *",
+ name = "boneWeights",
+ description = "Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7)"
+ },
{
type = "float *",
name = "animVertices",
@@ -847,26 +862,6 @@ return {
name = "animNormals",
description = "Animated normals (after bones transformations)"
},
- {
- type = "unsigned char *",
- name = "boneIds",
- description = "Vertex bone ids, max 255 bone ids, up to 4 bones influence by vertex (skinning) (shader-location = 6)"
- },
- {
- type = "float *",
- name = "boneWeights",
- description = "Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7)"
- },
- {
- type = "Matrix *",
- name = "boneMatrices",
- description = "Bones animated transformation matrices"
- },
- {
- type = "int",
- name = "boneCount",
- description = "Number of bones"
- },
{
type = "unsigned int",
name = "vaoId",
@@ -974,6 +969,27 @@ return {
}
}
},
+ {
+ name = "ModelSkeleton",
+ description = "Skeleton, animation bones hierarchy",
+ fields = {
+ {
+ type = "int",
+ name = "boneCount",
+ description = "Number of bones"
+ },
+ {
+ type = "BoneInfo *",
+ name = "bones",
+ description = "Bones information (skeleton)"
+ },
+ {
+ type = "ModelAnimPose",
+ name = "bindPose",
+ description = "Bones base transformation (Transform[])"
+ }
+ }
+ },
{
name = "Model",
description = "Model, meshes, materials and animation data",
@@ -1009,25 +1025,25 @@ return {
description = "Mesh material number"
},
{
- type = "int",
- name = "boneCount",
- description = "Number of bones"
+ type = "ModelSkeleton",
+ name = "skeleton",
+ description = "Skeleton for animation"
},
{
- type = "BoneInfo *",
- name = "bones",
- description = "Bones information (skeleton)"
+ type = "ModelAnimPose",
+ name = "currentPose",
+ description = "Current animation pose (Transform[])"
},
{
- type = "Transform *",
- name = "bindPose",
- description = "Bones base transformation (pose)"
+ type = "Matrix *",
+ name = "boneMatrices",
+ description = "Bones animated transformation matrices"
}
}
},
{
name = "ModelAnimation",
- description = "ModelAnimation",
+ description = "ModelAnimation, contains a full animation sequence",
fields = {
{
type = "char[32]",
@@ -1037,22 +1053,17 @@ return {
{
type = "int",
name = "boneCount",
- description = "Number of bones"
+ description = "Number of bones (per pose)"
},
{
type = "int",
- name = "frameCount",
- description = "Number of animation frames"
+ name = "keyframeCount",
+ description = "Number of animation key frames"
},
{
- type = "BoneInfo *",
- name = "bones",
- description = "Bones information (skeleton)"
- },
- {
- type = "Transform **",
- name = "framePoses",
- description = "Poses array by frame"
+ type = "ModelAnimPose *",
+ name = "keyframePoses",
+ description = "Animation sequence keyframe poses [keyframe][pose]"
}
}
},
@@ -1404,6 +1415,11 @@ return {
type = "Camera3D",
name = "Camera",
description = "Camera type fallback, defaults to Camera3D"
+ },
+ {
+ type = "Transform",
+ name = "*ModelAnimPose",
+ description = "Anim pose, an array of Transform[]"
}
},
enums = {
@@ -2204,7 +2220,7 @@ return {
{
name = "GAMEPAD_BUTTON_UNKNOWN",
value = 0,
- description = "Unknown button, just for error checking"
+ description = "Unknown button, for error checking"
},
{
name = "GAMEPAD_BUTTON_LEFT_FACE_UP",
@@ -2502,7 +2518,7 @@ return {
{
name = "SHADER_LOC_MAP_HEIGHT",
value = 21,
- description = "Shader location: sampler2d texture: height"
+ description = "Shader location: sampler2d texture: heightmap"
},
{
name = "SHADER_LOC_MAP_CUBEMAP",
@@ -2527,22 +2543,22 @@ return {
{
name = "SHADER_LOC_VERTEX_BONEIDS",
value = 26,
- description = "Shader location: vertex attribute: boneIds"
+ description = "Shader location: vertex attribute: bone indices"
},
{
name = "SHADER_LOC_VERTEX_BONEWEIGHTS",
value = 27,
- description = "Shader location: vertex attribute: boneWeights"
+ description = "Shader location: vertex attribute: bone weights"
},
{
- name = "SHADER_LOC_BONE_MATRICES",
+ name = "SHADER_LOC_MATRIX_BONETRANSFORMS",
value = 28,
- description = "Shader location: array of matrices uniform: boneMatrices"
+ description = "Shader location: matrix attribute: bone transforms (animation)"
},
{
- name = "SHADER_LOC_VERTEX_INSTANCE_TX",
+ name = "SHADER_LOC_VERTEX_INSTANCETRANSFORM",
value = 29,
- description = "Shader location: vertex attribute: instanceTransform"
+ description = "Shader location: vertex attribute: instance transforms"
}
}
},
@@ -2776,7 +2792,7 @@ return {
{
name = "TEXTURE_FILTER_POINT",
value = 0,
- description = "No filter, just pixel approximation"
+ description = "No filter, pixel approximation"
},
{
name = "TEXTURE_FILTER_BILINEAR",
@@ -6829,6 +6845,18 @@ return {
{type = "float", name = "spacing"}
}
},
+ {
+ name = "MeasureTextCodepoints",
+ description = "Measure string size for an existing array of codepoints for Font",
+ returnType = "Vector2",
+ params = {
+ {type = "Font", name = "font"},
+ {type = "const int *", name = "codepoints"},
+ {type = "int", name = "length"},
+ {type = "float", name = "fontSize"},
+ {type = "float", name = "spacing"}
+ }
+ },
{
name = "GetGlyphIndex",
description = "Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found",
@@ -7794,51 +7822,25 @@ return {
},
{
name = "UpdateModelAnimation",
- description = "Update model animation pose (CPU)",
+ description = "Update model animation pose (vertex buffers and bone matrices)",
returnType = "void",
params = {
{type = "Model", name = "model"},
{type = "ModelAnimation", name = "anim"},
- {type = "int", name = "frame"}
+ {type = "float", name = "frame"}
}
},
{
- name = "UpdateModelAnimationBones",
- description = "Update model animation mesh bone matrices (GPU skinning)",
- returnType = "void",
- params = {
- {type = "Model", name = "model"},
- {type = "ModelAnimation", name = "anim"},
- {type = "int", name = "frame"}
- }
- },
- {
- name = "UpdateModelAnimationBonesLerp",
- description = "Update model animation mesh bone matrices with interpolation between two poses(GPU skinning)",
+ name = "UpdateModelAnimationEx",
+ description = "Update model animation pose, blending two animations",
returnType = "void",
params = {
{type = "Model", name = "model"},
{type = "ModelAnimation", name = "animA"},
- {type = "int", name = "frameA"},
+ {type = "float", name = "frameA"},
{type = "ModelAnimation", name = "animB"},
- {type = "int", name = "frameB"},
- {type = "float", name = "value"}
- }
- },
- {
- name = "UpdateModelVertsToCurrentBones",
- description = "Update model vertices according to mesh bone matrices (CPU)",
- returnType = "void",
- params = {
- {type = "Model", name = "model"}
- }
- },
- {
- name = "UnloadModelAnimation",
- description = "Unload animation data",
- returnType = "void",
- params = {
- {type = "ModelAnimation", name = "anim"}
+ {type = "float", name = "frameB"},
+ {type = "float", name = "blend"}
}
},
{
@@ -8429,7 +8431,7 @@ return {
},
{
name = "SetAudioStreamPan",
- description = "Set pan for audio stream (0.5 is centered)",
+ description = "Set pan for audio stream (-1.0 to 1.0 range, 0.0 is centered)",
returnType = "void",
params = {
{type = "AudioStream", name = "stream"},
diff --git a/tools/rlparser/output/raylib_api.txt b/tools/rlparser/output/raylib_api.txt
index 9c25843f5..2213d3def 100644
--- a/tools/rlparser/output/raylib_api.txt
+++ b/tools/rlparser/output/raylib_api.txt
@@ -9,12 +9,12 @@ Define 001: RAYLIB_H
Define 002: RAYLIB_VERSION_MAJOR
Name: RAYLIB_VERSION_MAJOR
Type: INT
- Value: 5
+ Value: 6
Description:
Define 003: RAYLIB_VERSION_MINOR
Name: RAYLIB_VERSION_MINOR
Type: INT
- Value: 6
+ Value: 0
Description:
Define 004: RAYLIB_VERSION_PATCH
Name: RAYLIB_VERSION_PATCH
@@ -24,7 +24,7 @@ Define 004: RAYLIB_VERSION_PATCH
Define 005: RAYLIB_VERSION
Name: RAYLIB_VERSION
Type: STRING
- Value: "5.6-dev"
+ Value: "6.0"
Description:
Define 006: __declspec(x)
Name: __declspec(x)
@@ -287,7 +287,7 @@ Define 057: GetMouseRay
Value: GetScreenToWorldRay
Description: Compatibility hack for previous raylib versions
-Structures found: 34
+Structures found: 35
Struct 01: Vector2 (2 fields)
Name: Vector2
@@ -403,7 +403,7 @@ Struct 14: Camera2D (4 fields)
Field[2]: Vector2 target // Camera target (world space target point that is mapped to screen space offset)
Field[3]: float rotation // Camera rotation in degrees (pivots around target)
Field[4]: float zoom // Camera zoom (scaling around target), must not be set to 0, set to 1.0f for no scale
-Struct 15: Mesh (17 fields)
+Struct 15: Mesh (16 fields)
Name: Mesh
Description: Mesh, vertex data and vao/vbo
Field[1]: int vertexCount // Number of vertices stored in arrays
@@ -415,14 +415,13 @@ Struct 15: Mesh (17 fields)
Field[7]: float * tangents // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4)
Field[8]: unsigned char * colors // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
Field[9]: unsigned short * indices // Vertex indices (in case vertex data comes indexed)
- Field[10]: float * animVertices // Animated vertex positions (after bones transformations)
- Field[11]: float * animNormals // Animated normals (after bones transformations)
- Field[12]: unsigned char * boneIds // Vertex bone ids, max 255 bone ids, up to 4 bones influence by vertex (skinning) (shader-location = 6)
- Field[13]: float * boneWeights // Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7)
- Field[14]: Matrix * boneMatrices // Bones animated transformation matrices
- Field[15]: int boneCount // Number of bones
- Field[16]: unsigned int vaoId // OpenGL Vertex Array Object id
- Field[17]: unsigned int * vboId // OpenGL Vertex Buffer Objects id (default vertex data)
+ Field[10]: int boneCount // Number of bones (MAX: 256 bones)
+ Field[11]: unsigned char * boneIndices // Vertex bone indices, up to 4 bones influence by vertex (skinning) (shader-location = 6)
+ Field[12]: float * boneWeights // Vertex bone weight, up to 4 bones influence by vertex (skinning) (shader-location = 7)
+ Field[13]: float * animVertices // Animated vertex positions (after bones transformations)
+ Field[14]: float * animNormals // Animated normals (after bones transformations)
+ Field[15]: unsigned int vaoId // OpenGL Vertex Array Object id
+ Field[16]: unsigned int * vboId // OpenGL Vertex Buffer Objects id (default vertex data)
Struct 16: Shader (2 fields)
Name: Shader
Description: Shader
@@ -451,7 +450,13 @@ Struct 20: BoneInfo (2 fields)
Description: Bone, skeletal animation bone
Field[1]: char[32] name // Bone name
Field[2]: int parent // Bone parent
-Struct 21: Model (9 fields)
+Struct 21: ModelSkeleton (3 fields)
+ Name: ModelSkeleton
+ Description: Skeleton, animation bones hierarchy
+ Field[1]: int boneCount // Number of bones
+ Field[2]: BoneInfo * bones // Bones information (skeleton)
+ Field[3]: ModelAnimPose bindPose // Bones base transformation (Transform[])
+Struct 22: Model (9 fields)
Name: Model
Description: Model, meshes, materials and animation data
Field[1]: Matrix transform // Local transform matrix
@@ -460,35 +465,34 @@ Struct 21: Model (9 fields)
Field[4]: Mesh * meshes // Meshes array
Field[5]: Material * materials // Materials array
Field[6]: int * meshMaterial // Mesh material number
- Field[7]: int boneCount // Number of bones
- Field[8]: BoneInfo * bones // Bones information (skeleton)
- Field[9]: Transform * bindPose // Bones base transformation (pose)
-Struct 22: ModelAnimation (5 fields)
+ Field[7]: ModelSkeleton skeleton // Skeleton for animation
+ Field[8]: ModelAnimPose currentPose // Current animation pose (Transform[])
+ Field[9]: Matrix * boneMatrices // Bones animated transformation matrices
+Struct 23: ModelAnimation (4 fields)
Name: ModelAnimation
- Description: ModelAnimation
+ Description: ModelAnimation, contains a full animation sequence
Field[1]: char[32] name // Animation name
- Field[2]: int boneCount // Number of bones
- Field[3]: int frameCount // Number of animation frames
- Field[4]: BoneInfo * bones // Bones information (skeleton)
- Field[5]: Transform ** framePoses // Poses array by frame
-Struct 23: Ray (2 fields)
+ Field[2]: int boneCount // Number of bones (per pose)
+ Field[3]: int keyframeCount // Number of animation key frames
+ Field[4]: ModelAnimPose * keyframePoses // Animation sequence keyframe poses [keyframe][pose]
+Struct 24: Ray (2 fields)
Name: Ray
Description: Ray, ray for raycasting
Field[1]: Vector3 position // Ray position (origin)
Field[2]: Vector3 direction // Ray direction (normalized)
-Struct 24: RayCollision (4 fields)
+Struct 25: RayCollision (4 fields)
Name: RayCollision
Description: RayCollision, ray hit information
Field[1]: bool hit // Did the ray hit something?
Field[2]: float distance // Distance to the nearest hit
Field[3]: Vector3 point // Point of the nearest hit
Field[4]: Vector3 normal // Surface normal of hit
-Struct 25: BoundingBox (2 fields)
+Struct 26: BoundingBox (2 fields)
Name: BoundingBox
Description: BoundingBox
Field[1]: Vector3 min // Minimum vertex box-corner
Field[2]: Vector3 max // Maximum vertex box-corner
-Struct 26: Wave (5 fields)
+Struct 27: Wave (5 fields)
Name: Wave
Description: Wave, audio wave data
Field[1]: unsigned int frameCount // Total number of frames (considering channels)
@@ -496,7 +500,7 @@ Struct 26: Wave (5 fields)
Field[3]: unsigned int sampleSize // Bit depth (bits per sample): 8, 16, 32 (24 not supported)
Field[4]: unsigned int channels // Number of channels (1-mono, 2-stereo, ...)
Field[5]: void * data // Buffer data pointer
-Struct 27: AudioStream (5 fields)
+Struct 28: AudioStream (5 fields)
Name: AudioStream
Description: AudioStream, custom audio stream
Field[1]: rAudioBuffer * buffer // Pointer to internal data used by the audio system
@@ -504,12 +508,12 @@ Struct 27: AudioStream (5 fields)
Field[3]: unsigned int sampleRate // Frequency (samples per second)
Field[4]: unsigned int sampleSize // Bit depth (bits per sample): 8, 16, 32 (24 not supported)
Field[5]: unsigned int channels // Number of channels (1-mono, 2-stereo, ...)
-Struct 28: Sound (2 fields)
+Struct 29: Sound (2 fields)
Name: Sound
Description: Sound
Field[1]: AudioStream stream // Audio stream
Field[2]: unsigned int frameCount // Total number of frames (considering channels)
-Struct 29: Music (5 fields)
+Struct 30: Music (5 fields)
Name: Music
Description: Music, audio stream, anything longer than ~10 seconds should be streamed
Field[1]: AudioStream stream // Audio stream
@@ -517,7 +521,7 @@ Struct 29: Music (5 fields)
Field[3]: bool looping // Music looping enable
Field[4]: int ctxType // Type of music context (audio filetype)
Field[5]: void * ctxData // Audio context data, depends on type
-Struct 30: VrDeviceInfo (9 fields)
+Struct 31: VrDeviceInfo (9 fields)
Name: VrDeviceInfo
Description: VrDeviceInfo, Head-Mounted-Display device parameters
Field[1]: int hResolution // Horizontal resolution in pixels
@@ -529,7 +533,7 @@ Struct 30: VrDeviceInfo (9 fields)
Field[7]: float interpupillaryDistance // IPD (distance between pupils) in meters
Field[8]: float[4] lensDistortionValues // Lens distortion constant parameters
Field[9]: float[4] chromaAbCorrection // Chromatic aberration correction parameters
-Struct 31: VrStereoConfig (8 fields)
+Struct 32: VrStereoConfig (8 fields)
Name: VrStereoConfig
Description: VrStereoConfig, VR stereo rendering configuration for simulator
Field[1]: Matrix[2] projection // VR projection matrices (per eye)
@@ -540,25 +544,25 @@ Struct 31: VrStereoConfig (8 fields)
Field[6]: float[2] rightScreenCenter // VR right screen center
Field[7]: float[2] scale // VR distortion scale
Field[8]: float[2] scaleIn // VR distortion scale in
-Struct 32: FilePathList (2 fields)
+Struct 33: FilePathList (2 fields)
Name: FilePathList
Description: File path list
Field[1]: unsigned int count // Filepaths entries count
Field[2]: char ** paths // Filepaths entries
-Struct 33: AutomationEvent (3 fields)
+Struct 34: AutomationEvent (3 fields)
Name: AutomationEvent
Description: Automation event
Field[1]: unsigned int frame // Event frame
Field[2]: unsigned int type // Event type (AutomationEventType)
Field[3]: int[4] params // Event parameters (if required)
-Struct 34: AutomationEventList (3 fields)
+Struct 35: AutomationEventList (3 fields)
Name: AutomationEventList
Description: Automation event list
Field[1]: unsigned int capacity // Events max entries (MAX_AUTOMATION_EVENTS)
Field[2]: unsigned int count // Events entries count
Field[3]: AutomationEvent * events // Events entries
-Aliases found: 5
+Aliases found: 6
Alias 001: Quaternion
Type: Vector4
@@ -580,6 +584,10 @@ Alias 005: Camera
Type: Camera3D
Name: Camera
Description: Camera type fallback, defaults to Camera3D
+Alias 006: *ModelAnimPose
+ Type: Transform
+ Name: *ModelAnimPose
+ Description: Anim pose, an array of Transform[]
Enums found: 21
@@ -825,8 +833,8 @@ Enum 09: ShaderLocationIndex (30 values)
Value[SHADER_LOC_MAP_BRDF]: 25
Value[SHADER_LOC_VERTEX_BONEIDS]: 26
Value[SHADER_LOC_VERTEX_BONEWEIGHTS]: 27
- Value[SHADER_LOC_BONE_MATRICES]: 28
- Value[SHADER_LOC_VERTEX_INSTANCE_TX]: 29
+ Value[SHADER_LOC_MATRIX_BONETRANSFORMS]: 28
+ Value[SHADER_LOC_VERTEX_INSTANCETRANSFORM]: 29
Enum 10: ShaderUniformDataType (13 values)
Name: ShaderUniformDataType
Description: Shader uniform data type
@@ -992,7 +1000,7 @@ Callback 006: AudioCallback() (2 input parameters)
Param[1]: bufferData (type: void *)
Param[2]: frames (type: unsigned int)
-Functions found: 601
+Functions found: 599
Function 001: InitWindow() (3 input parameters)
Name: InitWindow
@@ -3668,137 +3676,146 @@ Function 422: MeasureTextEx() (4 input parameters)
Param[2]: text (type: const char *)
Param[3]: fontSize (type: float)
Param[4]: spacing (type: float)
-Function 423: GetGlyphIndex() (2 input parameters)
+Function 423: MeasureTextCodepoints() (5 input parameters)
+ Name: MeasureTextCodepoints
+ Return type: Vector2
+ Description: Measure string size for an existing array of codepoints for Font
+ Param[1]: font (type: Font)
+ Param[2]: codepoints (type: const int *)
+ Param[3]: length (type: int)
+ Param[4]: fontSize (type: float)
+ Param[5]: spacing (type: float)
+Function 424: GetGlyphIndex() (2 input parameters)
Name: GetGlyphIndex
Return type: int
Description: Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found
Param[1]: font (type: Font)
Param[2]: codepoint (type: int)
-Function 424: GetGlyphInfo() (2 input parameters)
+Function 425: GetGlyphInfo() (2 input parameters)
Name: GetGlyphInfo
Return type: GlyphInfo
Description: Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found
Param[1]: font (type: Font)
Param[2]: codepoint (type: int)
-Function 425: GetGlyphAtlasRec() (2 input parameters)
+Function 426: GetGlyphAtlasRec() (2 input parameters)
Name: GetGlyphAtlasRec
Return type: Rectangle
Description: Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found
Param[1]: font (type: Font)
Param[2]: codepoint (type: int)
-Function 426: LoadUTF8() (2 input parameters)
+Function 427: LoadUTF8() (2 input parameters)
Name: LoadUTF8
Return type: char *
Description: Load UTF-8 text encoded from codepoints array
Param[1]: codepoints (type: const int *)
Param[2]: length (type: int)
-Function 427: UnloadUTF8() (1 input parameters)
+Function 428: UnloadUTF8() (1 input parameters)
Name: UnloadUTF8
Return type: void
Description: Unload UTF-8 text encoded from codepoints array
Param[1]: text (type: char *)
-Function 428: LoadCodepoints() (2 input parameters)
+Function 429: LoadCodepoints() (2 input parameters)
Name: LoadCodepoints
Return type: int *
Description: Load all codepoints from a UTF-8 text string, codepoints count returned by parameter
Param[1]: text (type: const char *)
Param[2]: count (type: int *)
-Function 429: UnloadCodepoints() (1 input parameters)
+Function 430: UnloadCodepoints() (1 input parameters)
Name: UnloadCodepoints
Return type: void
Description: Unload codepoints data from memory
Param[1]: codepoints (type: int *)
-Function 430: GetCodepointCount() (1 input parameters)
+Function 431: GetCodepointCount() (1 input parameters)
Name: GetCodepointCount
Return type: int
Description: Get total number of codepoints in a UTF-8 encoded string
Param[1]: text (type: const char *)
-Function 431: GetCodepoint() (2 input parameters)
+Function 432: GetCodepoint() (2 input parameters)
Name: GetCodepoint
Return type: int
Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
Param[1]: text (type: const char *)
Param[2]: codepointSize (type: int *)
-Function 432: GetCodepointNext() (2 input parameters)
+Function 433: GetCodepointNext() (2 input parameters)
Name: GetCodepointNext
Return type: int
Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
Param[1]: text (type: const char *)
Param[2]: codepointSize (type: int *)
-Function 433: GetCodepointPrevious() (2 input parameters)
+Function 434: GetCodepointPrevious() (2 input parameters)
Name: GetCodepointPrevious
Return type: int
Description: Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
Param[1]: text (type: const char *)
Param[2]: codepointSize (type: int *)
-Function 434: CodepointToUTF8() (2 input parameters)
+Function 435: CodepointToUTF8() (2 input parameters)
Name: CodepointToUTF8
Return type: const char *
Description: Encode one codepoint into UTF-8 byte array (array length returned as parameter)
Param[1]: codepoint (type: int)
Param[2]: utf8Size (type: int *)
-Function 435: LoadTextLines() (2 input parameters)
+Function 436: LoadTextLines() (2 input parameters)
Name: LoadTextLines
Return type: char **
Description: Load text as separate lines ('\n')
Param[1]: text (type: const char *)
Param[2]: count (type: int *)
-Function 436: UnloadTextLines() (2 input parameters)
+Function 437: UnloadTextLines() (2 input parameters)
Name: UnloadTextLines
Return type: void
Description: Unload text lines
Param[1]: text (type: char **)
Param[2]: lineCount (type: int)
-Function 437: TextCopy() (2 input parameters)
+Function 438: TextCopy() (2 input parameters)
Name: TextCopy
Return type: int
Description: Copy one string to another, returns bytes copied
Param[1]: dst (type: char *)
Param[2]: src (type: const char *)
-Function 438: TextIsEqual() (2 input parameters)
+Function 439: TextIsEqual() (2 input parameters)
Name: TextIsEqual
Return type: bool
Description: Check if two text string are equal
Param[1]: text1 (type: const char *)
Param[2]: text2 (type: const char *)
-Function 439: TextLength() (1 input parameters)
+Function 440: TextLength() (1 input parameters)
Name: TextLength
Return type: unsigned int
Description: Get text length, checks for '\0' ending
Param[1]: text (type: const char *)
-Function 440: TextFormat() (2 input parameters)
+Function 441: TextFormat() (2 input parameters)
Name: TextFormat
Return type: const char *
Description: Text formatting with variables (sprintf() style)
Param[1]: text (type: const char *)
Param[2]: args (type: ...)
-Function 441: TextSubtext() (3 input parameters)
+Function 442: TextSubtext() (3 input parameters)
Name: TextSubtext
Return type: const char *
Description: Get a piece of a text string
Param[1]: text (type: const char *)
Param[2]: position (type: int)
Param[3]: length (type: int)
-Function 442: TextRemoveSpaces() (1 input parameters)
+Function 443: TextRemoveSpaces() (1 input parameters)
Name: TextRemoveSpaces
Return type: const char *
Description: Remove text spaces, concat words
Param[1]: text (type: const char *)
-Function 443: GetTextBetween() (3 input parameters)
+Function 444: GetTextBetween() (3 input parameters)
Name: GetTextBetween
Return type: char *
Description: Get text between two strings
Param[1]: text (type: const char *)
Param[2]: begin (type: const char *)
Param[3]: end (type: const char *)
-Function 444: TextReplace() (3 input parameters)
+Function 445: TextReplace() (3 input parameters)
Name: TextReplace
Return type: char *
Description: Replace text string (WARNING: memory must be freed!)
Param[1]: text (type: const char *)
Param[2]: search (type: const char *)
Param[3]: replacement (type: const char *)
-Function 445: TextReplaceBetween() (4 input parameters)
+Function 446: TextReplaceBetween() (4 input parameters)
Name: TextReplaceBetween
Return type: char *
Description: Replace text between two specific strings (WARNING: memory must be freed!)
@@ -3806,89 +3823,89 @@ Function 445: TextReplaceBetween() (4 input parameters)
Param[2]: begin (type: const char *)
Param[3]: end (type: const char *)
Param[4]: replacement (type: const char *)
-Function 446: TextInsert() (3 input parameters)
+Function 447: TextInsert() (3 input parameters)
Name: TextInsert
Return type: char *
Description: Insert text in a position (WARNING: memory must be freed!)
Param[1]: text (type: const char *)
Param[2]: insert (type: const char *)
Param[3]: position (type: int)
-Function 447: TextJoin() (3 input parameters)
+Function 448: TextJoin() (3 input parameters)
Name: TextJoin
Return type: char *
Description: Join text strings with delimiter
Param[1]: textList (type: char **)
Param[2]: count (type: int)
Param[3]: delimiter (type: const char *)
-Function 448: TextSplit() (3 input parameters)
+Function 449: TextSplit() (3 input parameters)
Name: TextSplit
Return type: char **
Description: Split text into multiple strings, using MAX_TEXTSPLIT_COUNT static strings
Param[1]: text (type: const char *)
Param[2]: delimiter (type: char)
Param[3]: count (type: int *)
-Function 449: TextAppend() (3 input parameters)
+Function 450: TextAppend() (3 input parameters)
Name: TextAppend
Return type: void
Description: Append text at specific position and move cursor
Param[1]: text (type: char *)
Param[2]: append (type: const char *)
Param[3]: position (type: int *)
-Function 450: TextFindIndex() (2 input parameters)
+Function 451: TextFindIndex() (2 input parameters)
Name: TextFindIndex
Return type: int
Description: Find first text occurrence within a string, -1 if not found
Param[1]: text (type: const char *)
Param[2]: search (type: const char *)
-Function 451: TextToUpper() (1 input parameters)
+Function 452: TextToUpper() (1 input parameters)
Name: TextToUpper
Return type: char *
Description: Get upper case version of provided string
Param[1]: text (type: const char *)
-Function 452: TextToLower() (1 input parameters)
+Function 453: TextToLower() (1 input parameters)
Name: TextToLower
Return type: char *
Description: Get lower case version of provided string
Param[1]: text (type: const char *)
-Function 453: TextToPascal() (1 input parameters)
+Function 454: TextToPascal() (1 input parameters)
Name: TextToPascal
Return type: char *
Description: Get Pascal case notation version of provided string
Param[1]: text (type: const char *)
-Function 454: TextToSnake() (1 input parameters)
+Function 455: TextToSnake() (1 input parameters)
Name: TextToSnake
Return type: char *
Description: Get Snake case notation version of provided string
Param[1]: text (type: const char *)
-Function 455: TextToCamel() (1 input parameters)
+Function 456: TextToCamel() (1 input parameters)
Name: TextToCamel
Return type: char *
Description: Get Camel case notation version of provided string
Param[1]: text (type: const char *)
-Function 456: TextToInteger() (1 input parameters)
+Function 457: TextToInteger() (1 input parameters)
Name: TextToInteger
Return type: int
Description: Get integer value from text
Param[1]: text (type: const char *)
-Function 457: TextToFloat() (1 input parameters)
+Function 458: TextToFloat() (1 input parameters)
Name: TextToFloat
Return type: float
Description: Get float value from text
Param[1]: text (type: const char *)
-Function 458: DrawLine3D() (3 input parameters)
+Function 459: DrawLine3D() (3 input parameters)
Name: DrawLine3D
Return type: void
Description: Draw a line in 3D world space
Param[1]: startPos (type: Vector3)
Param[2]: endPos (type: Vector3)
Param[3]: color (type: Color)
-Function 459: DrawPoint3D() (2 input parameters)
+Function 460: DrawPoint3D() (2 input parameters)
Name: DrawPoint3D
Return type: void
Description: Draw a point in 3D space, actually a small line
Param[1]: position (type: Vector3)
Param[2]: color (type: Color)
-Function 460: DrawCircle3D() (5 input parameters)
+Function 461: DrawCircle3D() (5 input parameters)
Name: DrawCircle3D
Return type: void
Description: Draw a circle in 3D world space
@@ -3897,7 +3914,7 @@ Function 460: DrawCircle3D() (5 input parameters)
Param[3]: rotationAxis (type: Vector3)
Param[4]: rotationAngle (type: float)
Param[5]: color (type: Color)
-Function 461: DrawTriangle3D() (4 input parameters)
+Function 462: DrawTriangle3D() (4 input parameters)
Name: DrawTriangle3D
Return type: void
Description: Draw a color-filled triangle (vertex in counter-clockwise order!)
@@ -3905,14 +3922,14 @@ Function 461: DrawTriangle3D() (4 input parameters)
Param[2]: v2 (type: Vector3)
Param[3]: v3 (type: Vector3)
Param[4]: color (type: Color)
-Function 462: DrawTriangleStrip3D() (3 input parameters)
+Function 463: DrawTriangleStrip3D() (3 input parameters)
Name: DrawTriangleStrip3D
Return type: void
Description: Draw a triangle strip defined by points
Param[1]: points (type: const Vector3 *)
Param[2]: pointCount (type: int)
Param[3]: color (type: Color)
-Function 463: DrawCube() (5 input parameters)
+Function 464: DrawCube() (5 input parameters)
Name: DrawCube
Return type: void
Description: Draw cube
@@ -3921,14 +3938,14 @@ Function 463: DrawCube() (5 input parameters)
Param[3]: height (type: float)
Param[4]: length (type: float)
Param[5]: color (type: Color)
-Function 464: DrawCubeV() (3 input parameters)
+Function 465: DrawCubeV() (3 input parameters)
Name: DrawCubeV
Return type: void
Description: Draw cube (Vector version)
Param[1]: position (type: Vector3)
Param[2]: size (type: Vector3)
Param[3]: color (type: Color)
-Function 465: DrawCubeWires() (5 input parameters)
+Function 466: DrawCubeWires() (5 input parameters)
Name: DrawCubeWires
Return type: void
Description: Draw cube wires
@@ -3937,21 +3954,21 @@ Function 465: DrawCubeWires() (5 input parameters)
Param[3]: height (type: float)
Param[4]: length (type: float)
Param[5]: color (type: Color)
-Function 466: DrawCubeWiresV() (3 input parameters)
+Function 467: DrawCubeWiresV() (3 input parameters)
Name: DrawCubeWiresV
Return type: void
Description: Draw cube wires (Vector version)
Param[1]: position (type: Vector3)
Param[2]: size (type: Vector3)
Param[3]: color (type: Color)
-Function 467: DrawSphere() (3 input parameters)
+Function 468: DrawSphere() (3 input parameters)
Name: DrawSphere
Return type: void
Description: Draw sphere
Param[1]: centerPos (type: Vector3)
Param[2]: radius (type: float)
Param[3]: color (type: Color)
-Function 468: DrawSphereEx() (5 input parameters)
+Function 469: DrawSphereEx() (5 input parameters)
Name: DrawSphereEx
Return type: void
Description: Draw sphere with extended parameters
@@ -3960,7 +3977,7 @@ Function 468: DrawSphereEx() (5 input parameters)
Param[3]: rings (type: int)
Param[4]: slices (type: int)
Param[5]: color (type: Color)
-Function 469: DrawSphereWires() (5 input parameters)
+Function 470: DrawSphereWires() (5 input parameters)
Name: DrawSphereWires
Return type: void
Description: Draw sphere wires
@@ -3969,7 +3986,7 @@ Function 469: DrawSphereWires() (5 input parameters)
Param[3]: rings (type: int)
Param[4]: slices (type: int)
Param[5]: color (type: Color)
-Function 470: DrawCylinder() (6 input parameters)
+Function 471: DrawCylinder() (6 input parameters)
Name: DrawCylinder
Return type: void
Description: Draw a cylinder/cone
@@ -3979,7 +3996,7 @@ Function 470: DrawCylinder() (6 input parameters)
Param[4]: height (type: float)
Param[5]: slices (type: int)
Param[6]: color (type: Color)
-Function 471: DrawCylinderEx() (6 input parameters)
+Function 472: DrawCylinderEx() (6 input parameters)
Name: DrawCylinderEx
Return type: void
Description: Draw a cylinder with base at startPos and top at endPos
@@ -3989,7 +4006,7 @@ Function 471: DrawCylinderEx() (6 input parameters)
Param[4]: endRadius (type: float)
Param[5]: sides (type: int)
Param[6]: color (type: Color)
-Function 472: DrawCylinderWires() (6 input parameters)
+Function 473: DrawCylinderWires() (6 input parameters)
Name: DrawCylinderWires
Return type: void
Description: Draw a cylinder/cone wires
@@ -3999,7 +4016,7 @@ Function 472: DrawCylinderWires() (6 input parameters)
Param[4]: height (type: float)
Param[5]: slices (type: int)
Param[6]: color (type: Color)
-Function 473: DrawCylinderWiresEx() (6 input parameters)
+Function 474: DrawCylinderWiresEx() (6 input parameters)
Name: DrawCylinderWiresEx
Return type: void
Description: Draw a cylinder wires with base at startPos and top at endPos
@@ -4009,7 +4026,7 @@ Function 473: DrawCylinderWiresEx() (6 input parameters)
Param[4]: endRadius (type: float)
Param[5]: sides (type: int)
Param[6]: color (type: Color)
-Function 474: DrawCapsule() (6 input parameters)
+Function 475: DrawCapsule() (6 input parameters)
Name: DrawCapsule
Return type: void
Description: Draw a capsule with the center of its sphere caps at startPos and endPos
@@ -4019,7 +4036,7 @@ Function 474: DrawCapsule() (6 input parameters)
Param[4]: slices (type: int)
Param[5]: rings (type: int)
Param[6]: color (type: Color)
-Function 475: DrawCapsuleWires() (6 input parameters)
+Function 476: DrawCapsuleWires() (6 input parameters)
Name: DrawCapsuleWires
Return type: void
Description: Draw capsule wireframe with the center of its sphere caps at startPos and endPos
@@ -4029,51 +4046,51 @@ Function 475: DrawCapsuleWires() (6 input parameters)
Param[4]: slices (type: int)
Param[5]: rings (type: int)
Param[6]: color (type: Color)
-Function 476: DrawPlane() (3 input parameters)
+Function 477: DrawPlane() (3 input parameters)
Name: DrawPlane
Return type: void
Description: Draw a plane XZ
Param[1]: centerPos (type: Vector3)
Param[2]: size (type: Vector2)
Param[3]: color (type: Color)
-Function 477: DrawRay() (2 input parameters)
+Function 478: DrawRay() (2 input parameters)
Name: DrawRay
Return type: void
Description: Draw a ray line
Param[1]: ray (type: Ray)
Param[2]: color (type: Color)
-Function 478: DrawGrid() (2 input parameters)
+Function 479: DrawGrid() (2 input parameters)
Name: DrawGrid
Return type: void
Description: Draw a grid (centered at (0, 0, 0))
Param[1]: slices (type: int)
Param[2]: spacing (type: float)
-Function 479: LoadModel() (1 input parameters)
+Function 480: LoadModel() (1 input parameters)
Name: LoadModel
Return type: Model
Description: Load model from files (meshes and materials)
Param[1]: fileName (type: const char *)
-Function 480: LoadModelFromMesh() (1 input parameters)
+Function 481: LoadModelFromMesh() (1 input parameters)
Name: LoadModelFromMesh
Return type: Model
Description: Load model from generated mesh (default material)
Param[1]: mesh (type: Mesh)
-Function 481: IsModelValid() (1 input parameters)
+Function 482: IsModelValid() (1 input parameters)
Name: IsModelValid
Return type: bool
Description: Check if a model is valid (loaded in GPU, VAO/VBOs)
Param[1]: model (type: Model)
-Function 482: UnloadModel() (1 input parameters)
+Function 483: UnloadModel() (1 input parameters)
Name: UnloadModel
Return type: void
Description: Unload model (including meshes) from memory (RAM and/or VRAM)
Param[1]: model (type: Model)
-Function 483: GetModelBoundingBox() (1 input parameters)
+Function 484: GetModelBoundingBox() (1 input parameters)
Name: GetModelBoundingBox
Return type: BoundingBox
Description: Compute model bounding box limits (considers all meshes)
Param[1]: model (type: Model)
-Function 484: DrawModel() (4 input parameters)
+Function 485: DrawModel() (4 input parameters)
Name: DrawModel
Return type: void
Description: Draw a model (with texture if set)
@@ -4081,7 +4098,7 @@ Function 484: DrawModel() (4 input parameters)
Param[2]: position (type: Vector3)
Param[3]: scale (type: float)
Param[4]: tint (type: Color)
-Function 485: DrawModelEx() (6 input parameters)
+Function 486: DrawModelEx() (6 input parameters)
Name: DrawModelEx
Return type: void
Description: Draw a model with extended parameters
@@ -4091,7 +4108,7 @@ Function 485: DrawModelEx() (6 input parameters)
Param[4]: rotationAngle (type: float)
Param[5]: scale (type: Vector3)
Param[6]: tint (type: Color)
-Function 486: DrawModelWires() (4 input parameters)
+Function 487: DrawModelWires() (4 input parameters)
Name: DrawModelWires
Return type: void
Description: Draw a model wires (with texture if set)
@@ -4099,7 +4116,7 @@ Function 486: DrawModelWires() (4 input parameters)
Param[2]: position (type: Vector3)
Param[3]: scale (type: float)
Param[4]: tint (type: Color)
-Function 487: DrawModelWiresEx() (6 input parameters)
+Function 488: DrawModelWiresEx() (6 input parameters)
Name: DrawModelWiresEx
Return type: void
Description: Draw a model wires (with texture if set) with extended parameters
@@ -4109,7 +4126,7 @@ Function 487: DrawModelWiresEx() (6 input parameters)
Param[4]: rotationAngle (type: float)
Param[5]: scale (type: Vector3)
Param[6]: tint (type: Color)
-Function 488: DrawModelPoints() (4 input parameters)
+Function 489: DrawModelPoints() (4 input parameters)
Name: DrawModelPoints
Return type: void
Description: Draw a model as points
@@ -4117,7 +4134,7 @@ Function 488: DrawModelPoints() (4 input parameters)
Param[2]: position (type: Vector3)
Param[3]: scale (type: float)
Param[4]: tint (type: Color)
-Function 489: DrawModelPointsEx() (6 input parameters)
+Function 490: DrawModelPointsEx() (6 input parameters)
Name: DrawModelPointsEx
Return type: void
Description: Draw a model as points with extended parameters
@@ -4127,13 +4144,13 @@ Function 489: DrawModelPointsEx() (6 input parameters)
Param[4]: rotationAngle (type: float)
Param[5]: scale (type: Vector3)
Param[6]: tint (type: Color)
-Function 490: DrawBoundingBox() (2 input parameters)
+Function 491: DrawBoundingBox() (2 input parameters)
Name: DrawBoundingBox
Return type: void
Description: Draw bounding box (wires)
Param[1]: box (type: BoundingBox)
Param[2]: color (type: Color)
-Function 491: DrawBillboard() (5 input parameters)
+Function 492: DrawBillboard() (5 input parameters)
Name: DrawBillboard
Return type: void
Description: Draw a billboard texture
@@ -4142,7 +4159,7 @@ Function 491: DrawBillboard() (5 input parameters)
Param[3]: position (type: Vector3)
Param[4]: scale (type: float)
Param[5]: tint (type: Color)
-Function 492: DrawBillboardRec() (6 input parameters)
+Function 493: DrawBillboardRec() (6 input parameters)
Name: DrawBillboardRec
Return type: void
Description: Draw a billboard texture defined by source
@@ -4152,7 +4169,7 @@ Function 492: DrawBillboardRec() (6 input parameters)
Param[4]: position (type: Vector3)
Param[5]: size (type: Vector2)
Param[6]: tint (type: Color)
-Function 493: DrawBillboardPro() (9 input parameters)
+Function 494: DrawBillboardPro() (9 input parameters)
Name: DrawBillboardPro
Return type: void
Description: Draw a billboard texture defined by source and rotation
@@ -4165,13 +4182,13 @@ Function 493: DrawBillboardPro() (9 input parameters)
Param[7]: origin (type: Vector2)
Param[8]: rotation (type: float)
Param[9]: tint (type: Color)
-Function 494: UploadMesh() (2 input parameters)
+Function 495: UploadMesh() (2 input parameters)
Name: UploadMesh
Return type: void
Description: Upload mesh vertex data in GPU and provide VAO/VBO ids
Param[1]: mesh (type: Mesh *)
Param[2]: dynamic (type: bool)
-Function 495: UpdateMeshBuffer() (5 input parameters)
+Function 496: UpdateMeshBuffer() (5 input parameters)
Name: UpdateMeshBuffer
Return type: void
Description: Update mesh vertex data in GPU for a specific buffer index
@@ -4180,19 +4197,19 @@ Function 495: UpdateMeshBuffer() (5 input parameters)
Param[3]: data (type: const void *)
Param[4]: dataSize (type: int)
Param[5]: offset (type: int)
-Function 496: UnloadMesh() (1 input parameters)
+Function 497: UnloadMesh() (1 input parameters)
Name: UnloadMesh
Return type: void
Description: Unload mesh data from CPU and GPU
Param[1]: mesh (type: Mesh)
-Function 497: DrawMesh() (3 input parameters)
+Function 498: DrawMesh() (3 input parameters)
Name: DrawMesh
Return type: void
Description: Draw a 3d mesh with material and transform
Param[1]: mesh (type: Mesh)
Param[2]: material (type: Material)
Param[3]: transform (type: Matrix)
-Function 498: DrawMeshInstanced() (4 input parameters)
+Function 499: DrawMeshInstanced() (4 input parameters)
Name: DrawMeshInstanced
Return type: void
Description: Draw multiple mesh instances with material and different transforms
@@ -4200,35 +4217,35 @@ Function 498: DrawMeshInstanced() (4 input parameters)
Param[2]: material (type: Material)
Param[3]: transforms (type: const Matrix *)
Param[4]: instances (type: int)
-Function 499: GetMeshBoundingBox() (1 input parameters)
+Function 500: GetMeshBoundingBox() (1 input parameters)
Name: GetMeshBoundingBox
Return type: BoundingBox
Description: Compute mesh bounding box limits
Param[1]: mesh (type: Mesh)
-Function 500: GenMeshTangents() (1 input parameters)
+Function 501: GenMeshTangents() (1 input parameters)
Name: GenMeshTangents
Return type: void
Description: Compute mesh tangents
Param[1]: mesh (type: Mesh *)
-Function 501: ExportMesh() (2 input parameters)
+Function 502: ExportMesh() (2 input parameters)
Name: ExportMesh
Return type: bool
Description: Export mesh data to file, returns true on success
Param[1]: mesh (type: Mesh)
Param[2]: fileName (type: const char *)
-Function 502: ExportMeshAsCode() (2 input parameters)
+Function 503: ExportMeshAsCode() (2 input parameters)
Name: ExportMeshAsCode
Return type: bool
Description: Export mesh as code file (.h) defining multiple arrays of vertex attributes
Param[1]: mesh (type: Mesh)
Param[2]: fileName (type: const char *)
-Function 503: GenMeshPoly() (2 input parameters)
+Function 504: GenMeshPoly() (2 input parameters)
Name: GenMeshPoly
Return type: Mesh
Description: Generate polygonal mesh
Param[1]: sides (type: int)
Param[2]: radius (type: float)
-Function 504: GenMeshPlane() (4 input parameters)
+Function 505: GenMeshPlane() (4 input parameters)
Name: GenMeshPlane
Return type: Mesh
Description: Generate plane mesh (with subdivisions)
@@ -4236,42 +4253,42 @@ Function 504: GenMeshPlane() (4 input parameters)
Param[2]: length (type: float)
Param[3]: resX (type: int)
Param[4]: resZ (type: int)
-Function 505: GenMeshCube() (3 input parameters)
+Function 506: GenMeshCube() (3 input parameters)
Name: GenMeshCube
Return type: Mesh
Description: Generate cuboid mesh
Param[1]: width (type: float)
Param[2]: height (type: float)
Param[3]: length (type: float)
-Function 506: GenMeshSphere() (3 input parameters)
+Function 507: GenMeshSphere() (3 input parameters)
Name: GenMeshSphere
Return type: Mesh
Description: Generate sphere mesh (standard sphere)
Param[1]: radius (type: float)
Param[2]: rings (type: int)
Param[3]: slices (type: int)
-Function 507: GenMeshHemiSphere() (3 input parameters)
+Function 508: GenMeshHemiSphere() (3 input parameters)
Name: GenMeshHemiSphere
Return type: Mesh
Description: Generate half-sphere mesh (no bottom cap)
Param[1]: radius (type: float)
Param[2]: rings (type: int)
Param[3]: slices (type: int)
-Function 508: GenMeshCylinder() (3 input parameters)
+Function 509: GenMeshCylinder() (3 input parameters)
Name: GenMeshCylinder
Return type: Mesh
Description: Generate cylinder mesh
Param[1]: radius (type: float)
Param[2]: height (type: float)
Param[3]: slices (type: int)
-Function 509: GenMeshCone() (3 input parameters)
+Function 510: GenMeshCone() (3 input parameters)
Name: GenMeshCone
Return type: Mesh
Description: Generate cone/pyramid mesh
Param[1]: radius (type: float)
Param[2]: height (type: float)
Param[3]: slices (type: int)
-Function 510: GenMeshTorus() (4 input parameters)
+Function 511: GenMeshTorus() (4 input parameters)
Name: GenMeshTorus
Return type: Mesh
Description: Generate torus mesh
@@ -4279,7 +4296,7 @@ Function 510: GenMeshTorus() (4 input parameters)
Param[2]: size (type: float)
Param[3]: radSeg (type: int)
Param[4]: sides (type: int)
-Function 511: GenMeshKnot() (4 input parameters)
+Function 512: GenMeshKnot() (4 input parameters)
Name: GenMeshKnot
Return type: Mesh
Description: Generate trefoil knot mesh
@@ -4287,106 +4304,89 @@ Function 511: GenMeshKnot() (4 input parameters)
Param[2]: size (type: float)
Param[3]: radSeg (type: int)
Param[4]: sides (type: int)
-Function 512: GenMeshHeightmap() (2 input parameters)
+Function 513: GenMeshHeightmap() (2 input parameters)
Name: GenMeshHeightmap
Return type: Mesh
Description: Generate heightmap mesh from image data
Param[1]: heightmap (type: Image)
Param[2]: size (type: Vector3)
-Function 513: GenMeshCubicmap() (2 input parameters)
+Function 514: GenMeshCubicmap() (2 input parameters)
Name: GenMeshCubicmap
Return type: Mesh
Description: Generate cubes-based map mesh from image data
Param[1]: cubicmap (type: Image)
Param[2]: cubeSize (type: Vector3)
-Function 514: LoadMaterials() (2 input parameters)
+Function 515: LoadMaterials() (2 input parameters)
Name: LoadMaterials
Return type: Material *
Description: Load materials from model file
Param[1]: fileName (type: const char *)
Param[2]: materialCount (type: int *)
-Function 515: LoadMaterialDefault() (0 input parameters)
+Function 516: LoadMaterialDefault() (0 input parameters)
Name: LoadMaterialDefault
Return type: Material
Description: Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps)
No input parameters
-Function 516: IsMaterialValid() (1 input parameters)
+Function 517: IsMaterialValid() (1 input parameters)
Name: IsMaterialValid
Return type: bool
Description: Check if a material is valid (shader assigned, map textures loaded in GPU)
Param[1]: material (type: Material)
-Function 517: UnloadMaterial() (1 input parameters)
+Function 518: UnloadMaterial() (1 input parameters)
Name: UnloadMaterial
Return type: void
Description: Unload material from GPU memory (VRAM)
Param[1]: material (type: Material)
-Function 518: SetMaterialTexture() (3 input parameters)
+Function 519: SetMaterialTexture() (3 input parameters)
Name: SetMaterialTexture
Return type: void
Description: Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...)
Param[1]: material (type: Material *)
Param[2]: mapType (type: int)
Param[3]: texture (type: Texture2D)
-Function 519: SetModelMeshMaterial() (3 input parameters)
+Function 520: SetModelMeshMaterial() (3 input parameters)
Name: SetModelMeshMaterial
Return type: void
Description: Set material for a mesh
Param[1]: model (type: Model *)
Param[2]: meshId (type: int)
Param[3]: materialId (type: int)
-Function 520: LoadModelAnimations() (2 input parameters)
+Function 521: LoadModelAnimations() (2 input parameters)
Name: LoadModelAnimations
Return type: ModelAnimation *
Description: Load model animations from file
Param[1]: fileName (type: const char *)
Param[2]: animCount (type: int *)
-Function 521: UpdateModelAnimation() (3 input parameters)
+Function 522: UpdateModelAnimation() (3 input parameters)
Name: UpdateModelAnimation
Return type: void
- Description: Update model animation pose (CPU)
+ Description: Update model animation pose (vertex buffers and bone matrices)
Param[1]: model (type: Model)
Param[2]: anim (type: ModelAnimation)
- Param[3]: frame (type: int)
-Function 522: UpdateModelAnimationBones() (3 input parameters)
- Name: UpdateModelAnimationBones
+ Param[3]: frame (type: float)
+Function 523: UpdateModelAnimationEx() (6 input parameters)
+ Name: UpdateModelAnimationEx
Return type: void
- Description: Update model animation mesh bone matrices (GPU skinning)
- Param[1]: model (type: Model)
- Param[2]: anim (type: ModelAnimation)
- Param[3]: frame (type: int)
-Function 523: UpdateModelAnimationBonesLerp() (6 input parameters)
- Name: UpdateModelAnimationBonesLerp
- Return type: void
- Description: Update model animation mesh bone matrices with interpolation between two poses(GPU skinning)
+ Description: Update model animation pose, blending two animations
Param[1]: model (type: Model)
Param[2]: animA (type: ModelAnimation)
- Param[3]: frameA (type: int)
+ Param[3]: frameA (type: float)
Param[4]: animB (type: ModelAnimation)
- Param[5]: frameB (type: int)
- Param[6]: value (type: float)
-Function 524: UpdateModelVertsToCurrentBones() (1 input parameters)
- Name: UpdateModelVertsToCurrentBones
- Return type: void
- Description: Update model vertices according to mesh bone matrices (CPU)
- Param[1]: model (type: Model)
-Function 525: UnloadModelAnimation() (1 input parameters)
- Name: UnloadModelAnimation
- Return type: void
- Description: Unload animation data
- Param[1]: anim (type: ModelAnimation)
-Function 526: UnloadModelAnimations() (2 input parameters)
+ Param[5]: frameB (type: float)
+ Param[6]: blend (type: float)
+Function 524: UnloadModelAnimations() (2 input parameters)
Name: UnloadModelAnimations
Return type: void
Description: Unload animation array data
Param[1]: animations (type: ModelAnimation *)
Param[2]: animCount (type: int)
-Function 527: IsModelAnimationValid() (2 input parameters)
+Function 525: IsModelAnimationValid() (2 input parameters)
Name: IsModelAnimationValid
Return type: bool
Description: Check model animation skeleton match
Param[1]: model (type: Model)
Param[2]: anim (type: ModelAnimation)
-Function 528: CheckCollisionSpheres() (4 input parameters)
+Function 526: CheckCollisionSpheres() (4 input parameters)
Name: CheckCollisionSpheres
Return type: bool
Description: Check collision between two spheres
@@ -4394,40 +4394,40 @@ Function 528: CheckCollisionSpheres() (4 input parameters)
Param[2]: radius1 (type: float)
Param[3]: center2 (type: Vector3)
Param[4]: radius2 (type: float)
-Function 529: CheckCollisionBoxes() (2 input parameters)
+Function 527: CheckCollisionBoxes() (2 input parameters)
Name: CheckCollisionBoxes
Return type: bool
Description: Check collision between two bounding boxes
Param[1]: box1 (type: BoundingBox)
Param[2]: box2 (type: BoundingBox)
-Function 530: CheckCollisionBoxSphere() (3 input parameters)
+Function 528: CheckCollisionBoxSphere() (3 input parameters)
Name: CheckCollisionBoxSphere
Return type: bool
Description: Check collision between box and sphere
Param[1]: box (type: BoundingBox)
Param[2]: center (type: Vector3)
Param[3]: radius (type: float)
-Function 531: GetRayCollisionSphere() (3 input parameters)
+Function 529: GetRayCollisionSphere() (3 input parameters)
Name: GetRayCollisionSphere
Return type: RayCollision
Description: Get collision info between ray and sphere
Param[1]: ray (type: Ray)
Param[2]: center (type: Vector3)
Param[3]: radius (type: float)
-Function 532: GetRayCollisionBox() (2 input parameters)
+Function 530: GetRayCollisionBox() (2 input parameters)
Name: GetRayCollisionBox
Return type: RayCollision
Description: Get collision info between ray and box
Param[1]: ray (type: Ray)
Param[2]: box (type: BoundingBox)
-Function 533: GetRayCollisionMesh() (3 input parameters)
+Function 531: GetRayCollisionMesh() (3 input parameters)
Name: GetRayCollisionMesh
Return type: RayCollision
Description: Get collision info between ray and mesh
Param[1]: ray (type: Ray)
Param[2]: mesh (type: Mesh)
Param[3]: transform (type: Matrix)
-Function 534: GetRayCollisionTriangle() (4 input parameters)
+Function 532: GetRayCollisionTriangle() (4 input parameters)
Name: GetRayCollisionTriangle
Return type: RayCollision
Description: Get collision info between ray and triangle
@@ -4435,7 +4435,7 @@ Function 534: GetRayCollisionTriangle() (4 input parameters)
Param[2]: p1 (type: Vector3)
Param[3]: p2 (type: Vector3)
Param[4]: p3 (type: Vector3)
-Function 535: GetRayCollisionQuad() (5 input parameters)
+Function 533: GetRayCollisionQuad() (5 input parameters)
Name: GetRayCollisionQuad
Return type: RayCollision
Description: Get collision info between ray and quad
@@ -4444,158 +4444,158 @@ Function 535: GetRayCollisionQuad() (5 input parameters)
Param[3]: p2 (type: Vector3)
Param[4]: p3 (type: Vector3)
Param[5]: p4 (type: Vector3)
-Function 536: InitAudioDevice() (0 input parameters)
+Function 534: InitAudioDevice() (0 input parameters)
Name: InitAudioDevice
Return type: void
Description: Initialize audio device and context
No input parameters
-Function 537: CloseAudioDevice() (0 input parameters)
+Function 535: CloseAudioDevice() (0 input parameters)
Name: CloseAudioDevice
Return type: void
Description: Close the audio device and context
No input parameters
-Function 538: IsAudioDeviceReady() (0 input parameters)
+Function 536: IsAudioDeviceReady() (0 input parameters)
Name: IsAudioDeviceReady
Return type: bool
Description: Check if audio device has been initialized successfully
No input parameters
-Function 539: SetMasterVolume() (1 input parameters)
+Function 537: SetMasterVolume() (1 input parameters)
Name: SetMasterVolume
Return type: void
Description: Set master volume (listener)
Param[1]: volume (type: float)
-Function 540: GetMasterVolume() (0 input parameters)
+Function 538: GetMasterVolume() (0 input parameters)
Name: GetMasterVolume
Return type: float
Description: Get master volume (listener)
No input parameters
-Function 541: LoadWave() (1 input parameters)
+Function 539: LoadWave() (1 input parameters)
Name: LoadWave
Return type: Wave
Description: Load wave data from file
Param[1]: fileName (type: const char *)
-Function 542: LoadWaveFromMemory() (3 input parameters)
+Function 540: LoadWaveFromMemory() (3 input parameters)
Name: LoadWaveFromMemory
Return type: Wave
Description: Load wave from memory buffer, fileType refers to extension: i.e. '.wav'
Param[1]: fileType (type: const char *)
Param[2]: fileData (type: const unsigned char *)
Param[3]: dataSize (type: int)
-Function 543: IsWaveValid() (1 input parameters)
+Function 541: IsWaveValid() (1 input parameters)
Name: IsWaveValid
Return type: bool
Description: Checks if wave data is valid (data loaded and parameters)
Param[1]: wave (type: Wave)
-Function 544: LoadSound() (1 input parameters)
+Function 542: LoadSound() (1 input parameters)
Name: LoadSound
Return type: Sound
Description: Load sound from file
Param[1]: fileName (type: const char *)
-Function 545: LoadSoundFromWave() (1 input parameters)
+Function 543: LoadSoundFromWave() (1 input parameters)
Name: LoadSoundFromWave
Return type: Sound
Description: Load sound from wave data
Param[1]: wave (type: Wave)
-Function 546: LoadSoundAlias() (1 input parameters)
+Function 544: LoadSoundAlias() (1 input parameters)
Name: LoadSoundAlias
Return type: Sound
Description: Create a new sound that shares the same sample data as the source sound, does not own the sound data
Param[1]: source (type: Sound)
-Function 547: IsSoundValid() (1 input parameters)
+Function 545: IsSoundValid() (1 input parameters)
Name: IsSoundValid
Return type: bool
Description: Checks if a sound is valid (data loaded and buffers initialized)
Param[1]: sound (type: Sound)
-Function 548: UpdateSound() (3 input parameters)
+Function 546: UpdateSound() (3 input parameters)
Name: UpdateSound
Return type: void
Description: Update sound buffer with new data (default data format: 32 bit float, stereo)
Param[1]: sound (type: Sound)
Param[2]: data (type: const void *)
Param[3]: sampleCount (type: int)
-Function 549: UnloadWave() (1 input parameters)
+Function 547: UnloadWave() (1 input parameters)
Name: UnloadWave
Return type: void
Description: Unload wave data
Param[1]: wave (type: Wave)
-Function 550: UnloadSound() (1 input parameters)
+Function 548: UnloadSound() (1 input parameters)
Name: UnloadSound
Return type: void
Description: Unload sound
Param[1]: sound (type: Sound)
-Function 551: UnloadSoundAlias() (1 input parameters)
+Function 549: UnloadSoundAlias() (1 input parameters)
Name: UnloadSoundAlias
Return type: void
Description: Unload a sound alias (does not deallocate sample data)
Param[1]: alias (type: Sound)
-Function 552: ExportWave() (2 input parameters)
+Function 550: ExportWave() (2 input parameters)
Name: ExportWave
Return type: bool
Description: Export wave data to file, returns true on success
Param[1]: wave (type: Wave)
Param[2]: fileName (type: const char *)
-Function 553: ExportWaveAsCode() (2 input parameters)
+Function 551: ExportWaveAsCode() (2 input parameters)
Name: ExportWaveAsCode
Return type: bool
Description: Export wave sample data to code (.h), returns true on success
Param[1]: wave (type: Wave)
Param[2]: fileName (type: const char *)
-Function 554: PlaySound() (1 input parameters)
+Function 552: PlaySound() (1 input parameters)
Name: PlaySound
Return type: void
Description: Play a sound
Param[1]: sound (type: Sound)
-Function 555: StopSound() (1 input parameters)
+Function 553: StopSound() (1 input parameters)
Name: StopSound
Return type: void
Description: Stop playing a sound
Param[1]: sound (type: Sound)
-Function 556: PauseSound() (1 input parameters)
+Function 554: PauseSound() (1 input parameters)
Name: PauseSound
Return type: void
Description: Pause a sound
Param[1]: sound (type: Sound)
-Function 557: ResumeSound() (1 input parameters)
+Function 555: ResumeSound() (1 input parameters)
Name: ResumeSound
Return type: void
Description: Resume a paused sound
Param[1]: sound (type: Sound)
-Function 558: IsSoundPlaying() (1 input parameters)
+Function 556: IsSoundPlaying() (1 input parameters)
Name: IsSoundPlaying
Return type: bool
Description: Check if a sound is currently playing
Param[1]: sound (type: Sound)
-Function 559: SetSoundVolume() (2 input parameters)
+Function 557: SetSoundVolume() (2 input parameters)
Name: SetSoundVolume
Return type: void
Description: Set volume for a sound (1.0 is max level)
Param[1]: sound (type: Sound)
Param[2]: volume (type: float)
-Function 560: SetSoundPitch() (2 input parameters)
+Function 558: SetSoundPitch() (2 input parameters)
Name: SetSoundPitch
Return type: void
Description: Set pitch for a sound (1.0 is base level)
Param[1]: sound (type: Sound)
Param[2]: pitch (type: float)
-Function 561: SetSoundPan() (2 input parameters)
+Function 559: SetSoundPan() (2 input parameters)
Name: SetSoundPan
Return type: void
Description: Set pan for a sound (-1.0 left, 0.0 center, 1.0 right)
Param[1]: sound (type: Sound)
Param[2]: pan (type: float)
-Function 562: WaveCopy() (1 input parameters)
+Function 560: WaveCopy() (1 input parameters)
Name: WaveCopy
Return type: Wave
Description: Copy a wave to a new wave
Param[1]: wave (type: Wave)
-Function 563: WaveCrop() (3 input parameters)
+Function 561: WaveCrop() (3 input parameters)
Name: WaveCrop
Return type: void
Description: Crop a wave to defined frames range
Param[1]: wave (type: Wave *)
Param[2]: initFrame (type: int)
Param[3]: finalFrame (type: int)
-Function 564: WaveFormat() (4 input parameters)
+Function 562: WaveFormat() (4 input parameters)
Name: WaveFormat
Return type: void
Description: Convert wave data to desired format
@@ -4603,203 +4603,203 @@ Function 564: WaveFormat() (4 input parameters)
Param[2]: sampleRate (type: int)
Param[3]: sampleSize (type: int)
Param[4]: channels (type: int)
-Function 565: LoadWaveSamples() (1 input parameters)
+Function 563: LoadWaveSamples() (1 input parameters)
Name: LoadWaveSamples
Return type: float *
Description: Load samples data from wave as a 32bit float data array
Param[1]: wave (type: Wave)
-Function 566: UnloadWaveSamples() (1 input parameters)
+Function 564: UnloadWaveSamples() (1 input parameters)
Name: UnloadWaveSamples
Return type: void
Description: Unload samples data loaded with LoadWaveSamples()
Param[1]: samples (type: float *)
-Function 567: LoadMusicStream() (1 input parameters)
+Function 565: LoadMusicStream() (1 input parameters)
Name: LoadMusicStream
Return type: Music
Description: Load music stream from file
Param[1]: fileName (type: const char *)
-Function 568: LoadMusicStreamFromMemory() (3 input parameters)
+Function 566: LoadMusicStreamFromMemory() (3 input parameters)
Name: LoadMusicStreamFromMemory
Return type: Music
Description: Load music stream from data
Param[1]: fileType (type: const char *)
Param[2]: data (type: const unsigned char *)
Param[3]: dataSize (type: int)
-Function 569: IsMusicValid() (1 input parameters)
+Function 567: IsMusicValid() (1 input parameters)
Name: IsMusicValid
Return type: bool
Description: Checks if a music stream is valid (context and buffers initialized)
Param[1]: music (type: Music)
-Function 570: UnloadMusicStream() (1 input parameters)
+Function 568: UnloadMusicStream() (1 input parameters)
Name: UnloadMusicStream
Return type: void
Description: Unload music stream
Param[1]: music (type: Music)
-Function 571: PlayMusicStream() (1 input parameters)
+Function 569: PlayMusicStream() (1 input parameters)
Name: PlayMusicStream
Return type: void
Description: Start music playing
Param[1]: music (type: Music)
-Function 572: IsMusicStreamPlaying() (1 input parameters)
+Function 570: IsMusicStreamPlaying() (1 input parameters)
Name: IsMusicStreamPlaying
Return type: bool
Description: Check if music is playing
Param[1]: music (type: Music)
-Function 573: UpdateMusicStream() (1 input parameters)
+Function 571: UpdateMusicStream() (1 input parameters)
Name: UpdateMusicStream
Return type: void
Description: Updates buffers for music streaming
Param[1]: music (type: Music)
-Function 574: StopMusicStream() (1 input parameters)
+Function 572: StopMusicStream() (1 input parameters)
Name: StopMusicStream
Return type: void
Description: Stop music playing
Param[1]: music (type: Music)
-Function 575: PauseMusicStream() (1 input parameters)
+Function 573: PauseMusicStream() (1 input parameters)
Name: PauseMusicStream
Return type: void
Description: Pause music playing
Param[1]: music (type: Music)
-Function 576: ResumeMusicStream() (1 input parameters)
+Function 574: ResumeMusicStream() (1 input parameters)
Name: ResumeMusicStream
Return type: void
Description: Resume playing paused music
Param[1]: music (type: Music)
-Function 577: SeekMusicStream() (2 input parameters)
+Function 575: SeekMusicStream() (2 input parameters)
Name: SeekMusicStream
Return type: void
Description: Seek music to a position (in seconds)
Param[1]: music (type: Music)
Param[2]: position (type: float)
-Function 578: SetMusicVolume() (2 input parameters)
+Function 576: SetMusicVolume() (2 input parameters)
Name: SetMusicVolume
Return type: void
Description: Set volume for music (1.0 is max level)
Param[1]: music (type: Music)
Param[2]: volume (type: float)
-Function 579: SetMusicPitch() (2 input parameters)
+Function 577: SetMusicPitch() (2 input parameters)
Name: SetMusicPitch
Return type: void
Description: Set pitch for a music (1.0 is base level)
Param[1]: music (type: Music)
Param[2]: pitch (type: float)
-Function 580: SetMusicPan() (2 input parameters)
+Function 578: SetMusicPan() (2 input parameters)
Name: SetMusicPan
Return type: void
Description: Set pan for a music (-1.0 left, 0.0 center, 1.0 right)
Param[1]: music (type: Music)
Param[2]: pan (type: float)
-Function 581: GetMusicTimeLength() (1 input parameters)
+Function 579: GetMusicTimeLength() (1 input parameters)
Name: GetMusicTimeLength
Return type: float
Description: Get music time length (in seconds)
Param[1]: music (type: Music)
-Function 582: GetMusicTimePlayed() (1 input parameters)
+Function 580: GetMusicTimePlayed() (1 input parameters)
Name: GetMusicTimePlayed
Return type: float
Description: Get current music time played (in seconds)
Param[1]: music (type: Music)
-Function 583: LoadAudioStream() (3 input parameters)
+Function 581: LoadAudioStream() (3 input parameters)
Name: LoadAudioStream
Return type: AudioStream
Description: Load audio stream (to stream raw audio pcm data)
Param[1]: sampleRate (type: unsigned int)
Param[2]: sampleSize (type: unsigned int)
Param[3]: channels (type: unsigned int)
-Function 584: IsAudioStreamValid() (1 input parameters)
+Function 582: IsAudioStreamValid() (1 input parameters)
Name: IsAudioStreamValid
Return type: bool
Description: Checks if an audio stream is valid (buffers initialized)
Param[1]: stream (type: AudioStream)
-Function 585: UnloadAudioStream() (1 input parameters)
+Function 583: UnloadAudioStream() (1 input parameters)
Name: UnloadAudioStream
Return type: void
Description: Unload audio stream and free memory
Param[1]: stream (type: AudioStream)
-Function 586: UpdateAudioStream() (3 input parameters)
+Function 584: UpdateAudioStream() (3 input parameters)
Name: UpdateAudioStream
Return type: void
Description: Update audio stream buffers with data
Param[1]: stream (type: AudioStream)
Param[2]: data (type: const void *)
Param[3]: frameCount (type: int)
-Function 587: IsAudioStreamProcessed() (1 input parameters)
+Function 585: IsAudioStreamProcessed() (1 input parameters)
Name: IsAudioStreamProcessed
Return type: bool
Description: Check if any audio stream buffers requires refill
Param[1]: stream (type: AudioStream)
-Function 588: PlayAudioStream() (1 input parameters)
+Function 586: PlayAudioStream() (1 input parameters)
Name: PlayAudioStream
Return type: void
Description: Play audio stream
Param[1]: stream (type: AudioStream)
-Function 589: PauseAudioStream() (1 input parameters)
+Function 587: PauseAudioStream() (1 input parameters)
Name: PauseAudioStream
Return type: void
Description: Pause audio stream
Param[1]: stream (type: AudioStream)
-Function 590: ResumeAudioStream() (1 input parameters)
+Function 588: ResumeAudioStream() (1 input parameters)
Name: ResumeAudioStream
Return type: void
Description: Resume audio stream
Param[1]: stream (type: AudioStream)
-Function 591: IsAudioStreamPlaying() (1 input parameters)
+Function 589: IsAudioStreamPlaying() (1 input parameters)
Name: IsAudioStreamPlaying
Return type: bool
Description: Check if audio stream is playing
Param[1]: stream (type: AudioStream)
-Function 592: StopAudioStream() (1 input parameters)
+Function 590: StopAudioStream() (1 input parameters)
Name: StopAudioStream
Return type: void
Description: Stop audio stream
Param[1]: stream (type: AudioStream)
-Function 593: SetAudioStreamVolume() (2 input parameters)
+Function 591: SetAudioStreamVolume() (2 input parameters)
Name: SetAudioStreamVolume
Return type: void
Description: Set volume for audio stream (1.0 is max level)
Param[1]: stream (type: AudioStream)
Param[2]: volume (type: float)
-Function 594: SetAudioStreamPitch() (2 input parameters)
+Function 592: SetAudioStreamPitch() (2 input parameters)
Name: SetAudioStreamPitch
Return type: void
Description: Set pitch for audio stream (1.0 is base level)
Param[1]: stream (type: AudioStream)
Param[2]: pitch (type: float)
-Function 595: SetAudioStreamPan() (2 input parameters)
+Function 593: SetAudioStreamPan() (2 input parameters)
Name: SetAudioStreamPan
Return type: void
- Description: Set pan for audio stream (0.5 is centered)
+ Description: Set pan for audio stream (-1.0 to 1.0 range, 0.0 is centered)
Param[1]: stream (type: AudioStream)
Param[2]: pan (type: float)
-Function 596: SetAudioStreamBufferSizeDefault() (1 input parameters)
+Function 594: SetAudioStreamBufferSizeDefault() (1 input parameters)
Name: SetAudioStreamBufferSizeDefault
Return type: void
Description: Default size for new audio streams
Param[1]: size (type: int)
-Function 597: SetAudioStreamCallback() (2 input parameters)
+Function 595: SetAudioStreamCallback() (2 input parameters)
Name: SetAudioStreamCallback
Return type: void
Description: Audio thread callback to request new data
Param[1]: stream (type: AudioStream)
Param[2]: callback (type: AudioCallback)
-Function 598: AttachAudioStreamProcessor() (2 input parameters)
+Function 596: AttachAudioStreamProcessor() (2 input parameters)
Name: AttachAudioStreamProcessor
Return type: void
Description: Attach audio stream processor to stream, receives frames x 2 samples as 'float' (stereo)
Param[1]: stream (type: AudioStream)
Param[2]: processor (type: AudioCallback)
-Function 599: DetachAudioStreamProcessor() (2 input parameters)
+Function 597: DetachAudioStreamProcessor() (2 input parameters)
Name: DetachAudioStreamProcessor
Return type: void
Description: Detach audio stream processor from stream
Param[1]: stream (type: AudioStream)
Param[2]: processor (type: AudioCallback)
-Function 600: AttachAudioMixedProcessor() (1 input parameters)
+Function 598: AttachAudioMixedProcessor() (1 input parameters)
Name: AttachAudioMixedProcessor
Return type: void
Description: Attach audio stream processor to the entire audio pipeline, receives frames x 2 samples as 'float' (stereo)
Param[1]: processor (type: AudioCallback)
-Function 601: DetachAudioMixedProcessor() (1 input parameters)
+Function 599: DetachAudioMixedProcessor() (1 input parameters)
Name: DetachAudioMixedProcessor
Return type: void
Description: Detach audio stream processor from the entire audio pipeline
diff --git a/tools/rlparser/output/raylib_api.xml b/tools/rlparser/output/raylib_api.xml
index d3314a5d1..2ec474efd 100644
--- a/tools/rlparser/output/raylib_api.xml
+++ b/tools/rlparser/output/raylib_api.xml
@@ -2,10 +2,10 @@
-
-
+
+
-
+
@@ -59,7 +59,7 @@
-
+
@@ -160,7 +160,7 @@
-
+
@@ -170,12 +170,11 @@
+
+
+
-
-
-
-
@@ -202,6 +201,11 @@
+
+
+
+
+
@@ -209,16 +213,15 @@
-
-
-
+
+
+
-
+
-
-
-
-
+
+
+
@@ -295,12 +298,13 @@
-
+
+
@@ -466,7 +470,7 @@
-
+
@@ -528,15 +532,15 @@
-
+
-
-
-
-
+
+
+
+
@@ -586,7 +590,7 @@
-
+
@@ -678,7 +682,7 @@
-
+
@@ -2433,6 +2437,13 @@
+
+
+
+
+
+
+
@@ -2908,29 +2919,18 @@
-
+
-
+
-
-
-
-
-
-
+
-
+
-
-
-
-
-
-
-
-
+
+
@@ -3181,7 +3181,7 @@
-
+