diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index e320161..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,143 +0,0 @@ -on: [push, pull_request] -name: Build -jobs: - test: - strategy: - fail-fast: false - matrix: - go-version: [1.21.x] - os: [ubuntu-latest, macos-latest, windows-latest] - runs-on: ${{ matrix.os }} - steps: - - name: Install Go - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.go-version }} - - name: Checkout code - uses: actions/checkout@v3 - - name: Install package - run: | - sudo apt-get update -y; sudo apt-get -y install libxi-dev libxinerama-dev libxcursor-dev libxrandr-dev libgl1-mesa-dev libwayland-dev libxkbcommon-dev - if: runner.os == 'Linux' - - name: Build - run: go build ./... - working-directory: raylib - - name: Verify dependencies - run: go mod verify - working-directory: raylib - - name: Build - run: go build -v ./... - working-directory: raylib - - name: Run go vet - run: go vet ./... - working-directory: raylib - - name: Install staticcheck - run: go install honnef.co/go/tools/cmd/staticcheck@latest - working-directory: raylib - - name: Run staticcheck - run: staticcheck ./... - working-directory: raylib - - name: Run tests - run: go test -race -vet=off ./... - working-directory: raylib - - test-drm: - strategy: - matrix: - go-version: [1.21.x] - os: [ubuntu-latest] - runs-on: ${{ matrix.os }} - steps: - - name: Install Go - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.go-version }} - - name: Checkout code - uses: actions/checkout@v3 - - name: Install package - run: | - sudo apt-get update -y; sudo apt-get -y install libegl1-mesa-dev libdrm-dev libgbm-dev - - name: Build - run: go build -tags drm - working-directory: raylib - - test-sdl: - strategy: - matrix: - go-version: [1.21.x] - os: [ubuntu-latest] - runs-on: ${{ matrix.os }} - steps: - - name: Install Go - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.go-version }} - - name: Checkout code - uses: actions/checkout@v3 - - name: Install package - run: | - sudo apt-get update -y; sudo apt-get -y install libgl1-mesa-dev libsdl2-dev - - name: Build - run: go build -tags sdl - working-directory: raylib - - test-rgfw: - strategy: - fail-fast: false - matrix: - go-version: [1.21.x] - os: [ubuntu-latest, macos-latest, windows-latest] - runs-on: ${{ matrix.os }} - steps: - - name: Install Go - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.go-version }} - - name: Checkout code - uses: actions/checkout@v3 - - name: Install package - run: | - sudo apt-get update -y; sudo apt-get -y install libxi-dev libxinerama-dev libxcursor-dev libxrandr-dev libgl1-mesa-dev libxkbcommon-dev - if: runner.os == 'Linux' - - name: Build - run: go build -tags rgfw - working-directory: raylib - - test-purego: - strategy: - matrix: - go-version: [1.21.x] - os: [ubuntu-latest] - runs-on: ${{ matrix.os }} - steps: - - name: Install Go - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.go-version }} - - name: Checkout code - uses: actions/checkout@v3 - - name: Build - run: go build - working-directory: raylib - env: - GOOS: windows - - test-examples: - strategy: - matrix: - go-version: [1.21.x] - os: [ubuntu-latest] - runs-on: ${{ matrix.os }} - steps: - - name: Install Go - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.go-version }} - - name: Checkout code - uses: actions/checkout@v3 - - name: Install package - run: | - sudo apt-get update -y; sudo apt-get -y install libxi-dev libxinerama-dev libxcursor-dev libxrandr-dev libgl1-mesa-dev libwayland-dev libxkbcommon-dev - - name: Build - run: go build ./... - working-directory: examples diff --git a/README.md b/README.md index d11c3da..034576d 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ -![logo](https://goo.gl/XlIcXz) +![logo](https://git.terah.dev/UnrealXR/raylib-go/raw/branch/master/assets/raylib-go_256x256.png) ## raylib-go -[![Build Status](https://github.com/gen2brain/raylib-go/actions/workflows/build.yml/badge.svg)](https://github.com/gen2brain/raylib-go/actions) -[![GoDoc](https://godoc.org/github.com/gen2brain/raylib-go/raylib?status.svg)](https://godoc.org/github.com/gen2brain/raylib-go/raylib) -[![Go Report Card](https://goreportcard.com/badge/github.com/gen2brain/raylib-go/raylib)](https://goreportcard.com/report/github.com/gen2brain/raylib-go/raylib) -[![Examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/gen2brain/raylib-go/tree/master/examples) +[![Build Status](https://git.terah.dev/UnrealXR/raylib-go/actions/workflows/build.yml/badge.svg)](https://git.terah.dev/UnrealXR/raylib-go/actions) +[![GoDoc](https://godoc.org/git.terah.dev/UnrealXR/raylib-go/raylib?status.svg)](https://godoc.org/git.terah.dev/UnrealXR/raylib-go/raylib) +[![Go Report Card](https://goreportcard.com/badge/git.terah.dev/UnrealXR/raylib-go/raylib)](https://goreportcard.com/report/git.terah.dev/UnrealXR/raylib-go/raylib) +[![Examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://git.terah.dev/UnrealXR/raylib-go/src/branch/master/examples) Golang bindings for [raylib](http://www.raylib.com/), a simple and easy-to-use library to enjoy videogames programming. +This is a fork of [gen2brain's `raylib-go` bindings](https://github.com/gen2brain/raylib-go) to add [DRM leasing](https://wayland.app/protocols/drm-lease-v1) support. + raylib C source code is included and compiled together with bindings. Note that the first build can take a few minutes. It is also possible to use raylib-go without cgo (Windows only; see requirements below). @@ -23,7 +25,7 @@ It is also possible to use raylib-go without cgo (Windows only; see requirements ##### macOS -On macOS you need Xcode or Command Line Tools for Xcode. +On macOS, you need Xcode or Command Line Tools for Xcode (if you have `brew` installed, you already have this). ##### Windows @@ -39,22 +41,28 @@ To remove console window, build with `-ldflags "-H=windowsgui"`. Download the raylib.dll from the assets on the [releases page](https://github.com/raysan5/raylib/releases). It is contained in the `raylib-*_win64_msvc*.zip`. Put the raylib.dll into the root folder of your project or copy it into `C:\Windows\System32` for a system-wide installation. -As of November 15, 2023, raylib 5.0 is the required version. - It is also possible to build the DLL yourself. You can find more info at [raylib's wiki](https://github.com/raysan5/raylib/wiki/Working-on-Windows). ##### Android -[Android example](https://github.com/gen2brain/raylib-go/tree/master/examples/others/android/example). +[Android example](https://git.terah.dev/UnrealXR/raylib-go/tree/master/examples/others/android/example). + +##### Wasm + +For web bindings, refer to [Raylib-Go-Wasm](https://github.com/BrownNPC/Raylib-Go-Wasm); it should be largely compatible with this repository. + ### Installation - go get -v -u github.com/gen2brain/raylib-go/raylib + go get -v -u git.terah.dev/UnrealXR/raylib-go/raylib ### Build tags * `drm` - build for Linux native [DRM](https://en.wikipedia.org/wiki/Direct_Rendering_Manager) mode, including Raspberry Pi 4 and other devices (PLATFORM_DRM) +* `drm_disable_input` - disables keyboard input capabilities for the DRM backend. Requires the `drm` build tag as a prerequisite +* `drm_leasing` - enables Wayland DRM leasing capabilties. Requires the `drm` build tag as a prerequisite * `sdl` - build for [SDL](https://github.com/libsdl-org/SDL) backend (PLATFORM_DESKTOP_SDL) +* `sdl3` - build for [SDL3](https://github.com/libsdl-org/SDL) backend (PLATFORM_DESKTOP_SDL3) * `rgfw` - build for [RGFW](https://github.com/ColleagueRiley/RGFW) backend (PLATFORM_DESKTOP_RGFW) * `noaudio` - disables audio functions * `opengl43` - uses OpenGL 4.3 backend @@ -63,17 +71,18 @@ It is also possible to build the DLL yourself. You can find more info at [raylib * `es2` - uses OpenGL ES 2.0 backend (can be used to link against [Google's ANGLE](https://github.com/google/angle)) * `es3` - experimental support for OpenGL ES 3.0 * `x11` - force X11 compatibility mode on Wayland (PLATFORM_DESKTOP/GLFW) +* `wayland` - force Wayland only mode (PLATFORM_DESKTOP/GLFW) ### Documentation -Documentation on [GoDoc](https://godoc.org/github.com/gen2brain/raylib-go/raylib). Also check raylib [cheatsheet](http://www.raylib.com/cheatsheet/cheatsheet.html). If you have problems or need assistance there is an active community in the #raylib-go channel of the [Raylib Discord Server](https://discord.gg/raylib) that can help. +Documentation on [GoDoc](https://godoc.org/git.terah.dev/UnrealXR/raylib-go/raylib). Also check raylib [cheatsheet](http://www.raylib.com/cheatsheet/cheatsheet.html). If you have problems or need assistance there is an active community in the #raylib-go channel of the [Raylib Discord Server](https://discord.gg/raylib) that can help. ### Example ```go package main -import rl "github.com/gen2brain/raylib-go/raylib" +import rl "git.terah.dev/UnrealXR/raylib-go/raylib" func main() { rl.InitWindow(800, 450, "raylib [core] example - basic window") @@ -92,7 +101,7 @@ func main() { } ``` -Check more [examples](https://github.com/gen2brain/raylib-go/tree/master/examples) organized by raylib modules. +Check more [examples](https://git.terah.dev/UnrealXR/raylib-go/tree/master/examples) organized by raylib modules. ### Cross-compile (Linux) @@ -122,4 +131,4 @@ basic_window: Mach-O 64-bit arm64 executable, flags: 0 { + g.paddle1.Pos.Y -= g.paddle1.Speed + } + if rl.IsKeyDown(rl.KeyS) && g.paddle1.Pos.Y < screenHeight-g.paddle1.Height { + g.paddle1.Pos.Y += g.paddle1.Speed + } + + // Player 2 - Up/Down + if rl.IsKeyDown(rl.KeyUp) && g.paddle2.Pos.Y > 0 { + g.paddle2.Pos.Y -= g.paddle2.Speed + } + if rl.IsKeyDown(rl.KeyDown) && g.paddle2.Pos.Y < screenHeight-g.paddle2.Height { + g.paddle2.Pos.Y += g.paddle2.Speed + } + + // Ball movement + g.ball.Pos.X += g.ball.Speed.X + g.ball.Pos.Y += g.ball.Speed.Y + + // Ball bounce off top/bottom walls + if g.ball.Pos.Y <= g.ball.Radius || g.ball.Pos.Y >= float32(screenHeight)-g.ball.Radius { + g.ball.Speed.Y *= -1 + } + + // Ball collisions with paddle sides only + if CheckCollisionSide(g.ball, g.paddle1) == "side" { + g.ball.Speed.X *= -1 + // Reposition ball outside paddle1 + g.ball.Pos.X = g.paddle1.Pos.X + g.paddle1.Width + g.ball.Radius + } + + if CheckCollisionSide(g.ball, g.paddle2) == "side" { + g.ball.Speed.X *= -1 + // Reposition ball outside paddle2 + g.ball.Pos.X = g.paddle2.Pos.X - g.ball.Radius + } + + // Scoring + if g.ball.Pos.X < 0 { + g.player2Score++ + g.ball.Pos = rl.NewVector2(float32(screenWidth/2), float32(screenHeight/2)) + g.ball.Speed.X *= -1 + } + if g.ball.Pos.X > float32(screenWidth) { + g.player1Score++ + g.ball.Pos = rl.NewVector2(float32(screenWidth/2), float32(screenHeight/2)) + g.ball.Speed.X *= -1 + } +} + +// Draw game elements +func (g *Game) Draw() { + // Draw paddles + rl.DrawRectangleV(g.paddle1.Pos, rl.NewVector2(g.paddle1.Width, g.paddle1.Height), rl.Black) + rl.DrawRectangleV(g.paddle2.Pos, rl.NewVector2(g.paddle2.Width, g.paddle2.Height), rl.Black) + + // Draw ball + rl.DrawCircleV(g.ball.Pos, g.ball.Radius, rl.Black) + + // Draw scores + rl.DrawText(fmt.Sprintf("Player 1 : %d", g.player1Score), 20, 20, 20, rl.DarkGray) + rl.DrawText(fmt.Sprintf("Player 2 : %d", g.player2Score), screenWidth-140, 20, 20, rl.DarkGray) +} + +func main() { + rl.InitWindow(screenWidth, screenHeight, "Pong in Go!") + rl.SetTargetFPS(60) + + var game Game + game.Init() + + for !rl.WindowShouldClose() { + game.Update() + + rl.BeginDrawing() + rl.ClearBackground(rl.RayWhite) + game.Draw() + rl.EndDrawing() + } + + rl.CloseWindow() +} diff --git a/examples/games/snake/main.go b/examples/games/snake/main.go index 7cb3f88..86eaaae 100644 --- a/examples/games/snake/main.go +++ b/examples/games/snake/main.go @@ -1,9 +1,5 @@ package main -import ( - "github.com/gen2brain/raylib-go/raylib" -) - const ( snakeLength = 256 squareSize = 31 diff --git a/examples/go.mod b/examples/go.mod index 64937a8..70eaebf 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -2,19 +2,19 @@ module examples go 1.21 -replace github.com/gen2brain/raylib-go/raylib => ../raylib +replace git.terah.dev/UnrealXR/raylib-go/raylib => ../raylib -replace github.com/gen2brain/raylib-go/raygui => ../raygui +replace git.terah.dev/UnrealXR/raylib-go/raygui => ../raygui -replace github.com/gen2brain/raylib-go/easings => ../easings +replace git.terah.dev/UnrealXR/raylib-go/easings => ../easings -replace github.com/gen2brain/raylib-go/physics => ../physics +replace git.terah.dev/UnrealXR/raylib-go/physics => ../physics require ( - github.com/gen2brain/raylib-go/easings v0.0.0-00010101000000-000000000000 - github.com/gen2brain/raylib-go/physics v0.0.0-00010101000000-000000000000 - github.com/gen2brain/raylib-go/raygui v0.0.0-00010101000000-000000000000 - github.com/gen2brain/raylib-go/raylib v0.0.0-20241202103652-5d50abe7c65b + git.terah.dev/UnrealXR/raylib-go/easings v0.0.0-00010101000000-000000000000 + git.terah.dev/UnrealXR/raylib-go/physics v0.0.0-00010101000000-000000000000 + git.terah.dev/UnrealXR/raylib-go/raygui v0.0.0-00010101000000-000000000000 + git.terah.dev/UnrealXR/raylib-go/raylib v0.0.0-20241202103652-5d50abe7c65b github.com/jakecoffman/cp v1.2.1 github.com/neguse/go-box2d-lite v0.0.0-20170921151050-5d8ed9b7272b ) diff --git a/examples/gui/button/main.go b/examples/gui/button/main.go index 0671a28..ef2d6fa 100644 --- a/examples/gui/button/main.go +++ b/examples/gui/button/main.go @@ -3,8 +3,8 @@ package main import ( "fmt" - gui "github.com/gen2brain/raylib-go/raygui" - rl "github.com/gen2brain/raylib-go/raylib" + gui "git.terah.dev/UnrealXR/raylib-go/raygui" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/gui/controls_test_suite/controls_test_suite.go b/examples/gui/controls_test_suite/controls_test_suite.go index 995093a..a635890 100644 --- a/examples/gui/controls_test_suite/controls_test_suite.go +++ b/examples/gui/controls_test_suite/controls_test_suite.go @@ -3,8 +3,8 @@ package main import ( "fmt" - gui "github.com/gen2brain/raylib-go/raygui" - rl "github.com/gen2brain/raylib-go/raylib" + gui "git.terah.dev/UnrealXR/raylib-go/raygui" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) /******************************************************************************************* diff --git a/examples/gui/icons/main.go b/examples/gui/icons/main.go index 1b48fd8..a86e84f 100644 --- a/examples/gui/icons/main.go +++ b/examples/gui/icons/main.go @@ -1,8 +1,8 @@ package main import ( - "github.com/gen2brain/raylib-go/raygui" - rl "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/raygui" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/gui/portable_window/portable_window.go b/examples/gui/portable_window/portable_window.go index 2485641..9f8463c 100644 --- a/examples/gui/portable_window/portable_window.go +++ b/examples/gui/portable_window/portable_window.go @@ -3,8 +3,8 @@ package main import ( "fmt" - gui "github.com/gen2brain/raylib-go/raygui" - rl "github.com/gen2brain/raylib-go/raylib" + gui "git.terah.dev/UnrealXR/raylib-go/raygui" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) /******************************************************************************************* diff --git a/examples/gui/scroll_panel/scroll_panel.go b/examples/gui/scroll_panel/scroll_panel.go index 6f7ee77..eed0a62 100644 --- a/examples/gui/scroll_panel/scroll_panel.go +++ b/examples/gui/scroll_panel/scroll_panel.go @@ -3,8 +3,8 @@ package main import ( "fmt" - gui "github.com/gen2brain/raylib-go/raygui" - rl "github.com/gen2brain/raylib-go/raylib" + gui "git.terah.dev/UnrealXR/raylib-go/raygui" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) /******************************************************************************************* diff --git a/examples/models/animation/main.go b/examples/models/animation/main.go index 851d38f..c5e06b7 100644 --- a/examples/models/animation/main.go +++ b/examples/models/animation/main.go @@ -21,9 +21,7 @@ package main import ( - "unsafe" - - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) const ( @@ -76,9 +74,8 @@ func main() { rl.DrawModelEx(model, position, rl.NewVector3(1, 0, 0), -90, rl.NewVector3(1, 1, 1), rl.White) // Draw translation cubes for i := int32(0); i < model.BoneCount; i++ { - framePose := unsafe.Slice(anims[0].FramePoses, anims[0].FrameCount) - trans := unsafe.Slice(framePose[animFrameCount], model.BoneCount) - rl.DrawCube(trans[i].Translation, 0.2, 0.2, 0.2, rl.Red) + pose := anims[0].GetFramePose(animFrameCount, int(i)) + rl.DrawCube(pose.Translation, 0.2, 0.2, 0.2, rl.Red) } rl.DrawGrid(10, 1) diff --git a/examples/models/billboard/main.go b/examples/models/billboard/main.go index 3ccad59..7f0320f 100644 --- a/examples/models/billboard/main.go +++ b/examples/models/billboard/main.go @@ -12,7 +12,7 @@ ********************************************************************************************/package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) const ( diff --git a/examples/models/box_collisions/main.go b/examples/models/box_collisions/main.go index 596a5d7..82c2c4c 100644 --- a/examples/models/box_collisions/main.go +++ b/examples/models/box_collisions/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/models/cubicmap/main.go b/examples/models/cubicmap/main.go index 5014ac7..f5790fe 100644 --- a/examples/models/cubicmap/main.go +++ b/examples/models/cubicmap/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/models/draw_cube_texture/cubicmap_atlas.png b/examples/models/draw_cube_texture/cubicmap_atlas.png new file mode 100644 index 0000000..9fc404a Binary files /dev/null and b/examples/models/draw_cube_texture/cubicmap_atlas.png differ diff --git a/examples/models/draw_cube_texture/main.go b/examples/models/draw_cube_texture/main.go new file mode 100644 index 0000000..9ae0ef5 --- /dev/null +++ b/examples/models/draw_cube_texture/main.go @@ -0,0 +1,256 @@ +/******************************************************************************************* +* +* raylib [models] example - Draw textured cube +* +* Example originally created with raylib 4.5, last time updated with raylib 4.5 +* +* 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) 2022-2024 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ +package main + +import rl "git.terah.dev/UnrealXR/raylib-go/raylib" + +const ( + screenWidth = 800 + screenHeight = 450 +) + +func main() { + rl.InitWindow(screenWidth, screenHeight, "raylib [models] example - draw cube texture") + + // Define the camera to look into our 3d world + camera := rl.Camera{ + Position: rl.Vector3{ + Y: 10.0, + Z: 10.0, + }, + Target: rl.Vector3{}, + Up: rl.Vector3{Y: 1.0}, + Fovy: 45.0, + Projection: rl.CameraPerspective, + } + + // Load texture to be applied to the cubes sides + texture := rl.LoadTexture("cubicmap_atlas.png") + + rl.SetTargetFPS(60) // Set our game to run at 60 frames-per-second + + // Main game loop + for !rl.WindowShouldClose() { // Detect window close button or ESC key + // Update + // TODO: Update your variables here + + // Draw + rl.BeginDrawing() + rl.ClearBackground(rl.RayWhite) + rl.BeginMode3D(camera) + + // Draw cube with an applied texture + vec := rl.Vector3{ + X: -2.0, + Y: 2.0, + } + DrawCubeTexture(texture, vec, 2.0, 4.0, 2.0, rl.White) + + // Draw cube with an applied texture, but only a defined rectangle piece of the texture + rec := rl.Rectangle{ + Y: float32(texture.Height) / 2.0, + Width: float32(texture.Width) / 2.0, + Height: float32(texture.Height) / 2.0, + } + vec = rl.Vector3{ + X: 2.0, + Y: 1.0, + } + DrawCubeTextureRec(texture, rec, vec, 2.0, 2.0, 2.0, rl.White) + + rl.DrawGrid(10, 1.0) // Draw a grid + rl.EndMode3D() + rl.DrawFPS(10, 10) + rl.EndDrawing() + } + + // De-Initialization + rl.UnloadTexture(texture) // Unload texture + rl.CloseWindow() // Close window and OpenGL context +} + +// DrawCubeTexture draws a textured cube +// NOTE: Cube position is the center position +func DrawCubeTexture(texture rl.Texture2D, position rl.Vector3, width, height, length float32, color rl.Color) { + x := position.X + y := position.Y + z := position.Z + + // Set desired texture to be enabled while drawing following vertex data + rl.SetTexture(texture.ID) + + // Vertex data transformation can be defined with the commented lines, + // but in this example we calculate the transformed vertex data directly when calling rlVertex3f() + // rl.PushMatrix() + // NOTE: Transformation is applied in inverse order (scale -> rotate -> translate) + //rl.Translatef(2.0, 0.0, 0.0) + //rl.Rotatef(45, 0, 1, ) + //rl.Scalef(2.0, 2.0, 2.0) + + rl.Begin(rl.Quads) + rl.Color4ub(color.R, color.G, color.B, color.A) + // Front Face + rl.Normal3f(0.0, 0.0, 1.0) // Normal Pointing Towards Viewer + rl.TexCoord2f(0.0, 0.0) + rl.Vertex3f(x-width/2, y-height/2, z+length/2) // Bottom Left Of The Texture and Quad + rl.TexCoord2f(1.0, 0.0) + rl.Vertex3f(x+width/2, y-height/2, z+length/2) // Bottom Right Of The Texture and Quad + rl.TexCoord2f(1.0, 1.0) + rl.Vertex3f(x+width/2, y+height/2, z+length/2) // Top Right Of The Texture and Quad + rl.TexCoord2f(0.0, 1.0) + rl.Vertex3f(x-width/2, y+height/2, z+length/2) // Top Left Of The Texture and Quad + // Back Face + rl.Normal3f(0.0, 0.0, -1.0) // Normal Pointing Away From Viewer + rl.TexCoord2f(1.0, 0.0) + rl.Vertex3f(x-width/2, y-height/2, z-length/2) // Bottom Right Of The Texture and Quad + rl.TexCoord2f(1.0, 1.0) + rl.Vertex3f(x-width/2, y+height/2, z-length/2) // Top Right Of The Texture and Quad + rl.TexCoord2f(0.0, 1.0) + rl.Vertex3f(x+width/2, y+height/2, z-length/2) // Top Left Of The Texture and Quad + rl.TexCoord2f(0.0, 0.0) + rl.Vertex3f(x+width/2, y-height/2, z-length/2) // Bottom Left Of The Texture and Quad + // Top Face + rl.Normal3f(0.0, 1.0, 0.0) // Normal Pointing Up + rl.TexCoord2f(0.0, 1.0) + rl.Vertex3f(x-width/2, y+height/2, z-length/2) // Top Left Of The Texture and Quad. + rl.TexCoord2f(0.0, 0.0) + rl.Vertex3f(x-width/2, y+height/2, z+length/2) // Bottom Left Of The Texture and Quad + rl.TexCoord2f(1.0, 0.0) + rl.Vertex3f(x+width/2, y+height/2, z+length/2) // Bottom Right Of The Texture and Quad + rl.TexCoord2f(1.0, 1.0) + rl.Vertex3f(x+width/2, y+height/2, z-length/2) // Top Right Of The Texture and Quad Bottom Face + rl.Normal3f(0.0, -1.0, 0.0) // Normal Pointing Down + rl.TexCoord2f(1.0, 1.0) + rl.Vertex3f(x-width/2, y-height/2, z-length/2) // Top Right Of The Texture and Quad + rl.TexCoord2f(0.0, 1.0) + rl.Vertex3f(x+width/2, y-height/2, z-length/2) // Top Left Of The Texture and Quad + rl.TexCoord2f(0.0, 0.0) + rl.Vertex3f(x+width/2, y-height/2, z+length/2) // Bottom Left Of The Texture and Quad + rl.TexCoord2f(1.0, 0.0) + rl.Vertex3f(x-width/2, y-height/2, z+length/2) // Bottom Right Of The Texture and Quad + // Right face + rl.Normal3f(1.0, 0.0, 0.0) // Normal Pointing Right + rl.TexCoord2f(1.0, 0.0) + rl.Vertex3f(x+width/2, y-height/2, z-length/2) // Bottom Right Of The Texture and Quad + rl.TexCoord2f(1.0, 1.0) + rl.Vertex3f(x+width/2, y+height/2, z-length/2) // Top Right Of The Texture and Quad + rl.TexCoord2f(0.0, 1.0) + rl.Vertex3f(x+width/2, y+height/2, z+length/2) // Top Left Of The Texture and Quad + rl.TexCoord2f(0.0, 0.0) + rl.Vertex3f(x+width/2, y-height/2, z+length/2) // Bottom Left Of The Texture and Quad + // Left Face + rl.Normal3f(-1.0, 0.0, 0.0) // Normal Pointing Left + rl.TexCoord2f(0.0, 0.0) + rl.Vertex3f(x-width/2, y-height/2, z-length/2) // Bottom Left Of The Texture and Quad + rl.TexCoord2f(1.0, 0.0) + rl.Vertex3f(x-width/2, y-height/2, z+length/2) // Bottom Right Of The Texture and Quad + rl.TexCoord2f(1.0, 1.0) + rl.Vertex3f(x-width/2, y+height/2, z+length/2) // Top Right Of The Texture and Quad + rl.TexCoord2f(0.0, 1.0) + rl.Vertex3f(x-width/2, y+height/2, z-length/2) // Top Left Of The Texture and Quad + + rl.End() + //rl.PopMatrix() + + rl.SetTexture(0) +} + +// DrawCubeTextureRec draws a cube with texture piece applied to all faces +func DrawCubeTextureRec(texture rl.Texture2D, source rl.Rectangle, position rl.Vector3, width, height, + length float32, color rl.Color) { + + x := position.X + y := position.Y + z := position.Z + + texWidth := float32(texture.Width) + texHeight := float32(texture.Height) + + // Set desired texture to be enabled while drawing following vertex data + rl.SetTexture(texture.ID) + + // We calculate the normalized texture coordinates for the desired texture-source-rectangle + // It means converting from (tex.width, tex.height) coordinates to [0.0f, 1.0f] equivalent + rl.Begin(rl.Quads) + rl.Color4ub(color.R, color.G, color.B, color.A) + + // Front face + rl.Normal3f(0.0, 0.0, 1.0) + rl.TexCoord2f(source.X/texWidth, (source.Y+source.Height)/texHeight) + rl.Vertex3f(x-width/2, y-height/2, z+length/2) + rl.TexCoord2f((source.X+source.Width)/texWidth, (source.Y+source.Height)/texHeight) + rl.Vertex3f(x+width/2, y-height/2, z+length/2) + rl.TexCoord2f((source.X+source.Width)/texWidth, source.Y/texHeight) + rl.Vertex3f(x+width/2, y+height/2, z+length/2) + rl.TexCoord2f(source.X/texWidth, source.Y/texHeight) + rl.Vertex3f(x-width/2, y+height/2, z+length/2) + + // Back face + rl.Normal3f(0.0, 0.0, -1.0) + rl.TexCoord2f((source.X+source.Width)/texWidth, (source.Y+source.Height)/texHeight) + rl.Vertex3f(x-width/2, y-height/2, z-length/2) + rl.TexCoord2f((source.X+source.Width)/texWidth, source.Y/texHeight) + rl.Vertex3f(x-width/2, y+height/2, z-length/2) + rl.TexCoord2f(source.X/texWidth, source.Y/texHeight) + rl.Vertex3f(x+width/2, y+height/2, z-length/2) + rl.TexCoord2f(source.X/texWidth, (source.Y+source.Height)/texHeight) + rl.Vertex3f(x+width/2, y-height/2, z-length/2) + + // Top face + rl.Normal3f(0.0, 1.0, 0.0) + rl.TexCoord2f(source.X/texWidth, source.Y/texHeight) + rl.Vertex3f(x-width/2, y+height/2, z-length/2) + rl.TexCoord2f(source.X/texWidth, (source.Y+source.Height)/texHeight) + rl.Vertex3f(x-width/2, y+height/2, z+length/2) + rl.TexCoord2f((source.X+source.Width)/texWidth, (source.Y+source.Height)/texHeight) + rl.Vertex3f(x+width/2, y+height/2, z+length/2) + rl.TexCoord2f((source.X+source.Width)/texWidth, source.Y/texHeight) + rl.Vertex3f(x+width/2, y+height/2, z-length/2) + + // Bottom face + rl.Normal3f(0.0, -1.0, 0.0) + rl.TexCoord2f((source.X+source.Width)/texWidth, source.Y/texHeight) + rl.Vertex3f(x-width/2, y-height/2, z-length/2) + rl.TexCoord2f(source.X/texWidth, source.Y/texHeight) + rl.Vertex3f(x+width/2, y-height/2, z-length/2) + rl.TexCoord2f(source.X/texWidth, (source.Y+source.Height)/texHeight) + rl.Vertex3f(x+width/2, y-height/2, z+length/2) + rl.TexCoord2f((source.X+source.Width)/texWidth, (source.Y+source.Height)/texHeight) + rl.Vertex3f(x-width/2, y-height/2, z+length/2) + + // Right face + rl.Normal3f(1.0, 0.0, 0.0) + rl.TexCoord2f((source.X+source.Width)/texWidth, (source.Y+source.Height)/texHeight) + rl.Vertex3f(x+width/2, y-height/2, z-length/2) + rl.TexCoord2f((source.X+source.Width)/texWidth, source.Y/texHeight) + rl.Vertex3f(x+width/2, y+height/2, z-length/2) + rl.TexCoord2f(source.X/texWidth, source.Y/texHeight) + rl.Vertex3f(x+width/2, y+height/2, z+length/2) + rl.TexCoord2f(source.X/texWidth, (source.Y+source.Height)/texHeight) + rl.Vertex3f(x+width/2, y-height/2, z+length/2) + + // Left face + rl.Normal3f(-1.0, 0.0, 0.0) + rl.TexCoord2f(source.X/texWidth, (source.Y+source.Height)/texHeight) + rl.Vertex3f(x-width/2, y-height/2, z-length/2) + rl.TexCoord2f((source.X+source.Width)/texWidth, (source.Y+source.Height)/texHeight) + rl.Vertex3f(x-width/2, y-height/2, z+length/2) + rl.TexCoord2f((source.X+source.Width)/texWidth, source.Y/texHeight) + rl.Vertex3f(x-width/2, y+height/2, z+length/2) + rl.TexCoord2f(source.X/texWidth, source.Y/texHeight) + rl.Vertex3f(x-width/2, y+height/2, z-length/2) + + rl.End() + + rl.SetTexture(0) +} diff --git a/examples/models/first_person_maze/main.go b/examples/models/first_person_maze/main.go index 339275d..310e31d 100644 --- a/examples/models/first_person_maze/main.go +++ b/examples/models/first_person_maze/main.go @@ -2,8 +2,8 @@ * * raylib [models] example - first person maze * -* This example has been created using raylib-go v0.0.0-20220104071325-2f072dc2d259 (https://github.com/gen2brain/raylib-go) -* raylib-go is licensed under an unmodified zlib/libpng license (https://github.com/gen2brain/raylib-go/blob/master/LICENSE) +* This example has been created using raylib-go v0.0.0-20220104071325-2f072dc2d259 (https://git.terah.dev/UnrealXR/raylib-go) +* raylib-go is licensed under an unmodified zlib/libpng license (https://git.terah.dev/UnrealXR/raylib-go/blob/master/LICENSE) * * Original C version for Raylib 2.5 Copyright (c) 2019 Ramon Santamaria (@raysan5) * Converted to Go by Michael Redman January 4, 2022 @@ -13,7 +13,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/models/geometric_shapes/main.go b/examples/models/geometric_shapes/main.go index 31a6397..bf78869 100644 --- a/examples/models/geometric_shapes/main.go +++ b/examples/models/geometric_shapes/main.go @@ -1,9 +1,5 @@ package main -import ( - "github.com/gen2brain/raylib-go/raylib" -) - func main() { screenWidth := int32(800) screenHeight := int32(450) diff --git a/examples/models/gltf_loading/main.go b/examples/models/gltf_loading/main.go index 26f3120..79505ab 100644 --- a/examples/models/gltf_loading/main.go +++ b/examples/models/gltf_loading/main.go @@ -3,7 +3,7 @@ package main import ( "fmt" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { @@ -62,7 +62,7 @@ func main() { rl.EndMode3D() - rl.DrawText("current animation number: "+fmt.Sprint(animIndex), 10, 10, 10, rl.Black) + rl.DrawText(fmt.Sprintf("current animation: %s [%d]", animPlaying.GetName(), animIndex), 10, 10, 10, rl.Black) rl.DrawText("UP/DOWN ARROW KEYS CHANGE ANIMATION", 10, 30, 10, rl.Black) rl.EndDrawing() diff --git a/examples/models/heightmap/main.go b/examples/models/heightmap/main.go index 1e9e5e7..896ffd8 100644 --- a/examples/models/heightmap/main.go +++ b/examples/models/heightmap/main.go @@ -3,7 +3,7 @@ package main import ( //"fmt" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/models/m3d_loading/main.go b/examples/models/m3d_loading/main.go index 2d8ef56..d979a2c 100644 --- a/examples/models/m3d_loading/main.go +++ b/examples/models/m3d_loading/main.go @@ -19,9 +19,7 @@ package main import ( - "unsafe" - - "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) const ( @@ -116,9 +114,8 @@ func main() { if drawSkeleton { modelBones := model.GetBones() modelPoses := model.GetBindPose() - animBones := unsafe.Slice(anims[animID].Bones, anims[animID].BoneCount) - animPoses := unsafe.Slice(anims[animID].FramePoses, anims[animID].FrameCount) - transforms := unsafe.Slice(animPoses[animFrameCounter], anims[animID].BoneCount) + anim := anims[animID] + animBones := anim.GetBones() for bone := 0; bone < int(model.BoneCount)-1; bone++ { if !animPlaying || animsCount == 0 { // Display the bind-pose skeleton @@ -128,9 +125,11 @@ func main() { } } else { // // Display the frame-pose skeleton - rl.DrawCube(transforms[bone].Translation, 0.05, 0.05, 0.05, rl.Red) + pos := anim.GetFramePose(animFrameCounter, bone).Translation + rl.DrawCube(pos, 0.05, 0.05, 0.05, rl.Red) if animBones[bone].Parent >= 0 { - rl.DrawLine3D(transforms[bone].Translation, transforms[animBones[bone].Parent].Translation, rl.Red) + endPos := anim.GetFramePose(animFrameCounter, int(animBones[bone].Parent)).Translation + rl.DrawLine3D(pos, endPos, rl.Red) } } } diff --git a/examples/models/mesh_generation/main.go b/examples/models/mesh_generation/main.go index de37789..9f1d0ec 100644 --- a/examples/models/mesh_generation/main.go +++ b/examples/models/mesh_generation/main.go @@ -3,7 +3,7 @@ package main import ( "unsafe" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/models/mesh_picking/main.go b/examples/models/mesh_picking/main.go index 7dacaaa..fba3e4e 100644 --- a/examples/models/mesh_picking/main.go +++ b/examples/models/mesh_picking/main.go @@ -19,7 +19,7 @@ import ( "math" "unsafe" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) const ( @@ -122,7 +122,7 @@ func main() { cursorColor := rl.White // Get ray and test against objects - // See issue : https://github.com/gen2brain/raylib-go/issues/457 + // See issue : https://git.terah.dev/UnrealXR/raylib-go/issues/457 //ray = rl.GetScreenToWorldRay(rl.GetMousePosition(), camera) ray = rl.GetMouseRay(rl.GetMousePosition(), camera) diff --git a/examples/models/obj_loading/main.go b/examples/models/obj_loading/main.go index a986924..806c726 100644 --- a/examples/models/obj_loading/main.go +++ b/examples/models/obj_loading/main.go @@ -5,7 +5,7 @@ import ( "slices" "unsafe" - "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/raylib" ) var supportedFileTypes = []string{ diff --git a/examples/models/orthographic/main.go b/examples/models/orthographic/main.go index 882821b..977b180 100644 --- a/examples/models/orthographic/main.go +++ b/examples/models/orthographic/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/models/rlgl_solar_system/main.go b/examples/models/rlgl_solar_system/main.go new file mode 100644 index 0000000..e784465 --- /dev/null +++ b/examples/models/rlgl_solar_system/main.go @@ -0,0 +1,155 @@ +/******************************************************************************************* +* +* raylib [models] example - rlgl module usage with push/pop matrix transformations +* +* NOTE: This example uses [rlgl] module functionality (pseudo-OpenGL 1.1 style coding) +* +* Example originally created with raylib 2.5, last time updated with raylib 4.0 +* +* 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) 2018-2024 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ +package main + +import ( + "math" + + rl "git.terah.dev/UnrealXR/raylib-go/raylib" +) + +const ( + screenWidth = 800 + screenHeight = 450 + + sunRadius = 4.0 + earthRadius = 0.6 + moonRadius = 0.16 + + earthOrbitRadius = 8.0 + moonOrbitRadius = 1.5 + + rings, slices = 16, 16 +) + +func main() { + // Initialization + title := "raylib [models] example - rlgl module usage with push/pop matrix transformations" + rl.InitWindow(screenWidth, screenHeight, title) + + // Define the camera to look into our 3d world + camera := rl.Camera{ + Position: rl.Vector3{ + X: 16.0, + Y: 16.0, + Z: 16.0, + }, // Camera position + Target: rl.Vector3{}, // Camera looking at point + Up: rl.Vector3{Y: 1.0}, // Camera up vector (rotation towards target) + Fovy: 45.0, // Camera field-of-view Y + Projection: rl.CameraPerspective, // Camera projection type + } + + var rotationSpeed float32 = 0.2 // General system rotation speed + var earthRotation float32 // Rotation of earth around itself (days) in degrees + var earthOrbitRotation float32 // Rotation of earth around the Sun (years) in degrees + var moonRotation float32 // Rotation of moon around itself + var moonOrbitRotation float32 // Rotation of moon around earth in degrees + + rl.SetTargetFPS(60) // Set our game to run at 60 frames-per-second + + // Main game loop + for !rl.WindowShouldClose() { // Detect window close button or ESC key + // Update + rl.UpdateCamera(&camera, rl.CameraOrbital) + + earthRotation += 5.0 * rotationSpeed + earthOrbitRotation += 365 / 360.0 * (5.0 * rotationSpeed) * rotationSpeed + moonRotation += 2.0 * rotationSpeed + moonOrbitRotation += 8.0 * rotationSpeed + + // Draw + rl.BeginDrawing() + rl.ClearBackground(rl.RayWhite) + rl.BeginMode3D(camera) + + rl.PushMatrix() + rl.Scalef(sunRadius, sunRadius, sunRadius) // Scale Sun + DrawSphereBasic(rl.Gold) // Draw the Sun + rl.PopMatrix() + + rl.PushMatrix() + rl.Rotatef(earthOrbitRotation, 0.0, 1.0, 0.0) // Rotation for Earth orbit around Sun + rl.Translatef(earthOrbitRadius, 0.0, 0.0) // Translation for Earth orbit + + rl.PushMatrix() + rl.Rotatef(earthRotation, 0.25, 1.0, 0.0) // Rotation for Earth itself + rl.Scalef(earthRadius, earthRadius, earthRadius) // Scale Earth + + DrawSphereBasic(rl.Blue) // Draw the Earth + rl.PopMatrix() + + rl.Rotatef(moonOrbitRotation, 0.0, 1.0, 0.0) // Rotation for Moon orbit around Earth + rl.Translatef(moonOrbitRadius, 0.0, 0.0) // Translation for Moon orbit + rl.Rotatef(moonRotation, 0.0, 1.0, 0.0) // Rotation for Moon itself + rl.Scalef(moonRadius, moonRadius, moonRadius) // Scale Moon + + DrawSphereBasic(rl.LightGray) // Draw the Moon + rl.PopMatrix() + + // Some reference elements (not affected by previous matrix transformations) + rl.DrawCircle3D(rl.Vector3{}, earthOrbitRadius, rl.NewVector3(1, 0, 0), 90.0, rl.Fade(rl.Red, 0.5)) + rl.DrawGrid(20, 1.0) + + rl.EndMode3D() + + rl.DrawText("EARTH ORBITING AROUND THE SUN!", 400, 10, 20, rl.Maroon) + rl.DrawFPS(10, 10) + + rl.EndDrawing() + } + + // De-Initialization + rl.CloseWindow() // Close window and OpenGL context +} + +// DrawSphereBasic draws a sphere without any matrix transformation +// NOTE: Sphere is drawn in world position ( 0, 0, 0 ) with radius 1.0f +func DrawSphereBasic(color rl.Color) { + // Make sure there is enough space in the internal render batch + // buffer to store all required vertex, batch is reset if required + rl.CheckRenderBatchLimit((rings + 2) * slices * 6) + + rl.Begin(rl.Triangles) + rl.Color4ub(color.R, color.G, color.B, color.A) + + for ring := int32(0); ring < (rings + 2); ring++ { + for slice := int32(0); slice < slices; slice++ { + rl.Vertex3f(getCoords(ring, slice)) + rl.Vertex3f(getCoords(ring+1, slice+1)) + rl.Vertex3f(getCoords(ring+1, slice)) + rl.Vertex3f(getCoords(ring, slice)) + rl.Vertex3f(getCoords(ring, slice+1)) + rl.Vertex3f(getCoords(ring+1, slice+1)) + } + } + rl.End() +} + +func getCoords(ring, slice int32) (x, y, z float32) { + ringF := float64(ring) + sliceF := float64(slice) + + // Calculate angels + alpha := rl.Deg2rad * (270 + (180/(float64(rings)+1))*ringF) + beta := rl.Deg2rad * (sliceF * 360 / float64(slices)) + + // Calculate coords + x = float32(math.Cos(alpha) * math.Sin(beta)) + y = float32(math.Sin(alpha)) + z = float32(math.Cos(alpha) * math.Cos(beta)) + + return x, y, z +} diff --git a/examples/models/skybox/main.go b/examples/models/skybox/main.go index f43d633..9307f53 100644 --- a/examples/models/skybox/main.go +++ b/examples/models/skybox/main.go @@ -5,7 +5,7 @@ import ( "path/filepath" "unsafe" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/models/vox_loading/main.go b/examples/models/vox_loading/main.go index 93aaf35..5311042 100644 --- a/examples/models/vox_loading/main.go +++ b/examples/models/vox_loading/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/models/waving_cubes/main.go b/examples/models/waving_cubes/main.go index c86de19..5fb874f 100644 --- a/examples/models/waving_cubes/main.go +++ b/examples/models/waving_cubes/main.go @@ -3,7 +3,7 @@ package main import ( "math" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/models/yaw_pitch_roll/main.go b/examples/models/yaw_pitch_roll/main.go new file mode 100644 index 0000000..4fea2f3 --- /dev/null +++ b/examples/models/yaw_pitch_roll/main.go @@ -0,0 +1,113 @@ +/******************************************************************************************* +* +* raylib [models] example - Plane rotations (yaw, pitch, roll) +* +* Example originally created with raylib 1.8, last time updated with raylib 4.0 +* +* Example contributed by Berni (@Berni8k) 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) 2017-2024 Berni (@Berni8k) and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +package main + +import rl "git.terah.dev/UnrealXR/raylib-go/raylib" + +const ( + screenWidth = 800 + screenHeight = 450 +) + +func main() { + //SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_WINDOW_HIGHDPI) + title := "raylib [models] example - plane rotations (yaw, pitch, roll)" + rl.InitWindow(screenWidth, screenHeight, title) + + camera := rl.Camera{ + Position: rl.Vector3{ + Y: 50.0, + Z: -120.0, + }, // Camera position perspective + Target: rl.Vector3{}, // Camera looking at point + Up: rl.Vector3{Y: 1.0}, // Camera up vector (rotation towards target) + Fovy: 30.0, // Camera field-of-view Y + Projection: rl.CameraPerspective, // Camera type + } + model := rl.LoadModel("plane.obj") // Load model + texture := rl.LoadTexture("plane_diffuse.png") // Load model texture + rl.SetMaterialTexture(model.Materials, rl.MapDiffuse, texture) // Set map diffuse texture + + var pitch, roll, yaw float32 + + rl.SetTargetFPS(60) // Set our game to run at 60 frames-per-second + + // Main game loop + for !rl.WindowShouldClose() { // Detect window close button or ESC key + // Update + + // Plane pitch (x-axis) controls + pitch = controlPlane(pitch, 0.6, rl.KeyDown, rl.KeyUp) + // Plane yaw (y-axis) controls + roll = controlPlane(roll, 1.0, rl.KeyLeft, rl.KeyRight) + // Plane roll (z-axis) controls + yaw = controlPlane(yaw, 1.0, rl.KeyA, rl.KeyS) + + // Transformation matrix for rotations + rotationV := rl.Vector3{ + X: rl.Deg2rad * pitch, + Y: rl.Deg2rad * yaw, + Z: rl.Deg2rad * roll, + } + model.Transform = rl.MatrixRotateXYZ(rotationV) + + // Draw + rl.BeginDrawing() + + rl.ClearBackground(rl.RayWhite) + + // Draw 3D model (recommended to draw 3D always before 2D) + rl.BeginMode3D(camera) + + // Draw 3d model with texture + rl.DrawModel(model, rl.Vector3{Y: -8.0}, 1.0, rl.White) + rl.DrawGrid(10, 10.0) + + rl.EndMode3D() + + // Draw controls info + rl.DrawRectangle(30, 370, 260, 70, rl.Fade(rl.Green, 0.5)) + rl.DrawRectangleLines(30, 370, 260, 70, rl.Fade(rl.DarkGreen, 0.5)) + rl.DrawText("Pitch controlled with: KEY_UP / KEY_DOWN", 40, 380, 10, rl.DarkGray) + rl.DrawText("Roll controlled with: KEY_LEFT / KEY_RIGHT", 40, 400, 10, rl.DarkGray) + rl.DrawText("Yaw controlled with: KEY_A / KEY_S", 40, 420, 10, rl.DarkGray) + + rl.DrawText("(c) WWI Plane Model created by GiaHanLam", screenWidth-240, screenHeight-20, 10, rl.DarkGray) + + rl.EndDrawing() + } + + // De-Initialization + rl.UnloadModel(model) // Unload model data + rl.UnloadTexture(texture) // Unload texture data + + rl.CloseWindow() // Close window and OpenGL context +} + +func controlPlane(ctrl, value float32, key1, key2 int32) float32 { + if rl.IsKeyDown(key1) { + ctrl -= value + } else if rl.IsKeyDown(key2) { + ctrl += value + } else { + if ctrl > 0.0 { + ctrl -= value / 2 + } else if ctrl < 0.0 { + ctrl += value / 2 + } + } + return ctrl +} diff --git a/examples/models/yaw_pitch_roll/plane.obj b/examples/models/yaw_pitch_roll/plane.obj new file mode 100644 index 0000000..29b5944 --- /dev/null +++ b/examples/models/yaw_pitch_roll/plane.obj @@ -0,0 +1,10858 @@ +# Blender v2.90.0 OBJ File: '' +# www.blender.org +o BODY_Material_#24_0 +v 2.545141 5.783802 -25.976692 +v 3.235504 6.677957 -13.125248 +v 1.687940 5.646317 -12.937294 +v -0.137555 3.728988 -25.602322 +v 0.973665 3.982364 -25.648491 +v -0.137555 5.284053 -12.871292 +v 0.973664 10.132854 -26.769033 +v 3.235504 13.408313 -14.351426 +v 4.269554 11.864355 -14.070142 +v 2.545140 8.331417 -26.440832 +v 4.632663 10.043135 -13.738335 +v 4.269555 8.221914 -13.406528 +v -0.137555 10.386230 -26.815191 +v -0.137555 14.802216 -14.605382 +v 1.687940 14.439952 -14.539380 +v -0.137555 10.386230 -26.815191 +v 0.973664 10.132854 -26.769033 +v -0.137555 10.132854 -26.769033 +v -0.137555 3.982364 -25.648491 +v 0.973665 3.982364 -25.648491 +v -0.137555 3.728988 -25.602322 +v 0.663374 15.705781 23.193211 +v 0.663374 14.114862 23.483055 +v 0.998288 14.910323 23.338135 +v 0.376462 18.602148 -30.355957 +v 0.330657 18.078314 -27.583200 +v 0.330657 17.075186 -24.744356 +v 0.376461 3.909487 -27.628571 +v 0.330618 3.524783 -30.914526 +v 0.285052 3.476720 -33.171906 +v 0.330657 11.401349 -24.776545 +v 0.376461 3.909487 -27.628571 +v 0.200132 4.545812 -36.862782 +v 0.200132 4.545812 -36.862782 +v 0.170088 5.657253 -38.139938 +v 0.170088 6.808916 -39.080044 +v 0.170088 12.034781 -40.236835 +v 0.170088 13.888091 -40.062054 +v 0.200132 15.707024 -39.244591 +v 0.200132 15.707024 -39.244591 +v 0.219588 17.377857 -38.011116 +v 0.285053 18.414829 -35.894424 +v 1.161025 13.829943 21.691204 +v 1.361917 14.567539 21.556824 +v 1.161026 15.305133 21.422447 +v 4.441549 12.221634 18.442724 +v 4.818830 14.087643 18.102762 +v 4.441548 15.953656 17.762800 +v 1.264851 22.121515 18.453445 +v 1.328365 22.272367 18.425961 +v 1.264851 22.423220 18.398476 +v 0.958179 22.423220 18.398476 +v 0.894666 22.272367 18.425961 +v 0.958179 22.121515 18.453445 +v 1.607252 21.900675 17.518620 +v 1.764696 22.274622 17.450493 +v 1.607252 22.648573 17.382362 +v 0.847047 22.648573 17.382362 +v 0.689604 22.274622 17.450493 +v 0.847048 21.900675 17.518620 +v 2.480337 19.942028 3.044717 +v 2.591266 20.552032 5.535551 +v 4.904634 19.031322 5.812605 +v 2.225156 18.538774 -2.685168 +v 3.460114 18.497147 0.467442 +v 4.473044 17.821404 0.554293 +v 2.406395 17.006306 3.793933 +v 3.460114 16.467627 0.837195 +v 2.406395 15.928947 -2.119541 +v -2.820251 5.783802 -25.976692 +v -1.963050 5.646317 -12.937294 +v -3.510613 6.677957 -13.125248 +v -0.137555 3.728988 -25.602322 +v -0.137555 5.284053 -12.871292 +v -1.248775 3.982364 -25.648491 +v -1.248774 10.132854 -26.769033 +v -4.544662 11.864355 -14.070142 +v -3.510613 13.408313 -14.351426 +v -2.820250 8.331417 -26.440832 +v -4.544664 8.221914 -13.406528 +v -4.907773 10.043135 -13.738335 +v -1.963049 14.439952 -14.539380 +v -0.137555 10.386230 -26.815191 +v -0.137555 10.132854 -26.769033 +v -1.248774 10.132854 -26.769033 +v -0.137555 3.982364 -25.648491 +v -0.137555 3.728988 -25.602322 +v -1.248775 3.982364 -25.648491 +v -0.938484 15.705781 23.193211 +v -1.273397 14.910323 23.338135 +v -0.938484 14.114862 23.483055 +v -0.651571 18.602148 -30.355957 +v -0.605767 17.075186 -24.744356 +v -0.605767 18.078314 -27.583200 +v -0.651571 3.909487 -27.628571 +v -0.560162 3.476720 -33.171906 +v -0.605727 3.524783 -30.914526 +v -0.605767 11.401349 -24.776545 +v -0.475242 4.545812 -36.862782 +v -0.651571 3.909487 -27.628571 +v -0.475242 4.545812 -36.862782 +v -0.445197 6.808916 -39.080044 +v -0.445197 5.657253 -38.139938 +v -0.445198 12.034781 -40.236835 +v -0.475242 15.707024 -39.244591 +v -0.445197 13.888091 -40.062054 +v -0.475242 15.707024 -39.244591 +v -0.560163 18.414829 -35.894424 +v -0.494698 17.377857 -38.011116 +v -1.436135 13.829943 21.691204 +v -1.436136 15.305133 21.422447 +v -1.637027 14.567539 21.556824 +v -4.716660 12.221634 18.442724 +v -4.716659 15.953656 17.762800 +v -5.093940 14.087643 18.102762 +v -1.539961 22.121515 18.453445 +v -1.539961 22.423220 18.398476 +v -1.603475 22.272367 18.425961 +v -1.233289 22.423220 18.398476 +v -1.233289 22.121515 18.453445 +v -1.169775 22.272367 18.425961 +v -1.882362 21.900675 17.518620 +v -1.882362 22.648573 17.382362 +v -2.039805 22.274622 17.450493 +v -1.122157 22.648573 17.382362 +v -1.122157 21.900675 17.518620 +v -0.964713 22.274622 17.450493 +v -2.755446 19.942028 3.044717 +v -5.179746 19.031322 5.812605 +v -2.866376 20.552032 5.535551 +v -2.500265 18.538774 -2.685168 +v -4.748155 17.821404 0.554293 +v -3.735223 18.497147 0.467442 +v -2.681505 17.006306 3.793933 +v -2.681505 15.928947 -2.119541 +v -3.735224 16.467627 0.837195 +v 5.886480 9.962349 1.986112 +v 4.473047 7.797970 2.380435 +v 6.382811 12.515405 1.520978 +v 2.357693 6.351778 2.643912 +v -0.137555 5.843942 2.736434 +v -0.137555 18.750629 -3.898979 +v 5.886480 15.068464 1.055844 +v -0.137555 21.086037 5.438262 +v -0.137555 20.817574 4.364942 +v -0.137555 15.705820 -3.344262 +v -0.137555 17.229437 5.018654 +v 2.406395 17.006306 3.793933 +v 2.480337 19.942028 3.044717 +v 3.460114 18.497147 0.467442 +v 3.460114 16.467627 0.837195 +v -0.137555 17.229437 5.018654 +v -0.137555 20.817574 4.364942 +v 2.480337 19.942028 3.044717 +v 2.406395 17.006306 3.793933 +v 2.406395 15.928947 -2.119541 +v 2.225156 18.538774 -2.685168 +v -0.137555 18.750629 -3.898979 +v -0.137555 15.705820 -3.344262 +v 3.460114 16.467627 0.837195 +v 3.460114 18.497147 0.467442 +v 6.450380 10.797626 7.312677 +v 4.904636 8.521720 7.727320 +v 6.993171 13.482239 6.823577 +v 2.591268 7.001008 8.004374 +v -0.137555 6.467003 8.101662 +v 6.450378 16.166853 6.334475 +v 0.973664 10.132854 -26.769033 +v 2.545140 8.331417 -26.440832 +v -0.137555 8.331417 -26.440832 +v -0.137555 10.132854 -26.769033 +v 2.545140 8.331417 -26.440832 +v 2.545141 5.783802 -25.976692 +v -0.137555 5.783802 -25.976692 +v -0.137555 8.331417 -26.440832 +v -0.137555 5.783802 -25.976692 +v 2.545141 5.783802 -25.976692 +v 0.973665 3.982364 -25.648491 +v -0.137555 3.982364 -25.648491 +v -0.137555 27.150551 8.418542 +v 10.657262 27.150551 8.418542 +v 10.657262 27.734844 13.241050 +v -0.137555 27.734844 13.241050 +v 10.657262 27.734844 13.241050 +v 10.657262 28.271790 13.143222 +v -0.137555 28.271790 13.143222 +v -0.137555 27.734844 13.241050 +v -0.137555 26.248919 3.469610 +v 10.657262 26.245462 3.654478 +v 21.452074 26.836880 8.475688 +v 21.452074 27.421173 13.298194 +v 21.452074 27.421173 13.298194 +v 21.452074 27.958118 13.200370 +v 10.657262 28.271790 13.143222 +v 10.657262 27.936581 8.275336 +v -0.137555 27.936581 8.275336 +v -0.137555 28.271790 13.143222 +v 2.342535 26.107084 2.691110 +v 10.657262 26.074389 2.673605 +v 21.452074 25.931791 3.711624 +v 32.246895 26.201221 8.591496 +v 32.246895 26.785511 13.414004 +v 32.246895 26.785511 13.414004 +v 32.246895 27.322460 13.316178 +v 21.452074 27.958118 13.200370 +v 21.452074 27.622913 8.332482 +v 10.657262 27.103384 3.498175 +v -0.137555 27.034948 3.326405 +v 6.234208 24.407400 -5.868575 +v 10.657262 24.407400 -5.868575 +v 10.657262 25.029203 -2.931825 +v 5.849846 25.098688 -2.843851 +v 21.452074 25.760714 2.730751 +v 32.246895 25.296129 3.827433 +v 41.450764 25.660938 8.689931 +v 38.136772 26.311825 13.500302 +v 38.136772 26.311825 13.500302 +v 38.136772 26.848774 13.402480 +v 32.246895 27.322460 13.316178 +v 32.246895 26.987251 8.448291 +v 21.452074 26.789709 3.555321 +v 10.657262 26.917547 2.519993 +v 2.342535 26.893122 2.547905 +v 10.657262 24.911684 -5.960440 +v 10.657262 24.407400 -5.868575 +v 6.234208 24.407400 -5.868575 +v 6.234208 24.911684 -5.960440 +v 21.452074 24.715536 -2.874681 +v 21.452074 25.098589 -1.065534 +v 10.657262 25.412260 -1.122682 +v 32.246895 25.125055 2.846561 +v 43.041698 24.755846 3.925868 +v 41.450764 25.660938 8.689931 +v 41.450764 26.446968 8.546725 +v 38.136772 26.848774 13.402480 +v 38.136772 26.311825 13.500302 +v 38.136772 26.848774 13.402480 +v 41.450764 26.446968 8.546725 +v 32.246895 26.154055 3.671129 +v 21.452074 26.603876 2.577139 +v 10.657262 26.198298 -1.265890 +v 10.657262 25.707838 -3.055464 +v 5.849846 25.884718 -2.987059 +v 5.429670 26.198298 -1.265890 +v 21.452074 24.598013 -5.903296 +v 21.452074 24.093729 -5.811423 +v 32.246895 24.079874 -2.758871 +v 32.246895 24.462931 -0.949727 +v 43.041698 24.584768 2.944992 +v 43.041698 24.755846 3.925868 +v 43.041698 25.613764 3.769564 +v 41.450764 25.660938 8.689931 +v 43.041698 25.613764 3.769564 +v 32.246883 25.968218 2.692949 +v 21.452074 25.884623 -1.208742 +v 21.452074 25.394167 -2.998319 +v 32.246895 23.962353 -5.787486 +v 32.246895 23.458073 -5.695620 +v 41.915051 23.539589 -2.660438 +v 43.041698 23.922642 -0.851296 +v 43.041698 24.584768 2.944992 +v 43.041698 25.427927 2.791382 +v 43.041698 25.427927 2.791382 +v 32.246895 25.248964 -1.092931 +v 32.246895 24.758509 -2.882509 +v 40.086185 23.422073 -5.689055 +v 40.086185 22.917782 -5.597183 +v 43.041698 23.922642 -0.851296 +v 41.915051 23.539589 -2.660438 +v 41.915051 24.218218 -2.784075 +v 43.041698 24.708681 -0.994498 +v 43.041698 24.708681 -0.994498 +v 41.915051 24.218218 -2.784075 +v -0.137555 26.248919 3.469610 +v -0.137555 27.034948 3.326405 +v 2.342535 26.893122 2.547905 +v 2.342535 26.107084 2.691110 +v 5.429670 26.198298 -1.265890 +v 5.429670 25.412260 -1.122682 +v 6.234208 24.911684 -5.960440 +v 6.234208 24.407400 -5.868575 +v 5.849846 25.098688 -2.843851 +v 5.849846 25.884718 -2.987059 +v 21.452074 24.093729 -5.811423 +v 32.246895 23.458073 -5.695620 +v 21.452074 24.598013 -5.903296 +v 10.657262 24.911684 -5.960440 +v 40.086185 22.917782 -5.597183 +v 32.246895 23.962353 -5.787486 +v 40.086185 22.917782 -5.597183 +v 40.086185 23.422073 -5.689055 +v 40.086185 23.422073 -5.689055 +v 5.429670 25.412260 -1.122682 +v 6.234208 24.911684 -5.960440 +v 0.301038 3.263966 -5.783109 +v 0.197856 3.753135 -5.871772 +v 31.931070 10.162366 -7.040006 +v 32.026306 9.709085 -6.957421 +v 39.669945 11.589635 -7.300035 +v 39.736111 11.274701 -7.242656 +v 0.387923 6.649406 14.232206 +v 32.075500 13.113069 13.054610 +v 31.962679 13.650052 12.956777 +v 0.266163 7.228906 14.126631 +v 37.826111 14.319102 12.834886 +v 37.726982 14.790937 12.748921 +v 32.026306 9.709085 -6.957421 +v 32.085331 10.333307 -1.938074 +v 0.387387 3.865942 -0.760052 +v 0.301038 3.263966 -5.783109 +v 39.736111 11.274701 -7.242656 +v 42.631611 12.549485 -2.430761 +v 42.539577 12.987517 -2.510567 +v 42.558872 13.816903 2.562746 +v 42.650913 13.378873 2.642551 +v 42.631611 12.549485 -2.430761 +v 31.952864 10.963766 -2.052936 +v 31.980639 11.746195 2.991455 +v 42.558872 13.816903 2.562746 +v 42.539577 12.987517 -2.510567 +v 31.931070 10.162366 -7.040006 +v 0.197856 3.753135 -5.871772 +v 0.243875 4.546322 -0.883360 +v 39.669945 11.589635 -7.300035 +v 42.539577 12.987517 -2.510567 +v 42.631611 12.549485 -2.430761 +v 39.736111 11.274701 -7.242656 +v 32.075500 13.113069 13.054610 +v 0.387923 6.649406 14.232206 +v 0.498981 5.383774 9.351863 +v 32.146481 11.874181 8.170041 +v 0.322121 6.222238 9.199900 +v 0.266163 7.228906 14.126631 +v 31.962679 13.650052 12.956777 +v 31.983232 12.651127 8.028491 +v 37.726982 14.790937 12.748921 +v 37.826111 14.319102 12.834886 +v 41.148281 13.882128 7.790358 +v 41.034866 14.421938 7.692010 +v 37.826111 14.319102 12.834886 +v 41.148281 13.882128 7.790358 +v 37.726982 14.790937 12.748921 +v 41.034866 14.421938 7.692010 +v 32.113106 11.115736 3.106316 +v 0.440122 4.637904 4.286060 +v 0.296608 5.318285 4.162748 +v 42.650913 13.378873 2.642551 +v 39.669945 11.589635 -7.300035 +v 4.091239 8.149817 -35.663517 +v 5.559022 7.933621 -36.487488 +v 10.651894 10.839343 -20.973984 +v 2.289506 10.581471 -20.926994 +v 2.316500 11.808965 -21.150627 +v 4.136919 9.079532 -35.830589 +v -0.137555 10.420605 -28.919138 +v -0.137555 11.835061 -21.155396 +v -0.137555 10.607282 -20.931702 +v 2.289506 10.581471 -20.926994 +v 2.316500 11.808965 -21.150627 +v -0.137555 11.835061 -21.155396 +v 10.651894 10.839343 -20.973984 +v 10.753733 11.399936 -21.076115 +v 12.581096 10.727206 -20.953552 +v 12.692742 11.199199 -21.039536 +v 15.755084 9.283175 -24.701189 +v 15.931952 9.756166 -24.787363 +v 15.310983 10.176061 -23.504894 +v 15.140950 9.703977 -23.418880 +v 17.404955 7.330409 -32.602814 +v 17.600180 7.780162 -32.684757 +v 13.768648 7.331349 -36.377769 +v 5.559022 7.933621 -36.487488 +v 5.622406 8.862197 -36.656662 +v 13.907855 7.973525 -36.291916 +v -0.137555 10.420605 -28.919138 +v 4.136919 9.079532 -35.830589 +v 4.091239 8.149817 -35.663517 +v -0.137555 9.192825 -28.695456 +v 12.692742 11.199199 -21.039536 +v 12.581096 10.727206 -20.953552 +v 16.102695 7.227387 -35.385410 +v 16.283424 7.700906 -35.471687 +v 16.283424 7.700906 -35.471687 +v 16.102695 7.227387 -35.385410 +v 15.310983 10.176061 -23.504894 +v 16.283424 7.700906 -35.471687 +v 13.907855 7.973525 -36.291916 +v 12.692742 11.199199 -21.039536 +v 10.753733 11.399936 -21.076115 +v 5.622406 8.862197 -36.656662 +v 17.404955 7.330409 -32.602814 +v 15.755084 9.283175 -24.701189 +v 15.140950 9.703977 -23.418880 +v 16.102695 7.227387 -35.385410 +v 13.768648 7.331349 -36.377769 +v 12.581096 10.727206 -20.953552 +v 15.931952 9.756166 -24.787363 +v 17.600180 7.780162 -32.684757 +v 30.305048 12.463110 10.208496 +v 30.963701 12.438213 10.208937 +v 30.813036 26.748894 11.243511 +v 30.154379 26.773792 11.243069 +v 30.963701 12.438213 10.208937 +v 30.960356 12.540001 8.359914 +v 30.809679 26.850677 9.394487 +v 30.813036 26.748894 11.243511 +v 30.960356 12.540001 8.359914 +v 30.301702 12.564899 8.359473 +v 30.151026 26.875576 9.394049 +v 30.809679 26.850677 9.394487 +v 30.301702 12.564899 8.359473 +v 30.305048 12.463110 10.208496 +v 30.154379 26.773792 11.243069 +v 30.151026 26.875576 9.394049 +v 0.634450 18.576508 2.205827 +v 0.634450 20.241886 1.902417 +v 1.850016 20.241886 1.902417 +v 1.850016 18.576508 2.205827 +v 0.634450 18.658239 2.654441 +v 1.850016 18.658239 2.654441 +v 1.850016 20.323616 2.351031 +v 0.634450 20.323616 2.351031 +v 0.634450 18.576508 2.205827 +v 1.850016 18.576508 2.205827 +v 1.850016 18.658239 2.654441 +v 0.634450 18.658239 2.654441 +v 1.850016 18.576508 2.205827 +v 1.850016 20.241886 1.902417 +v 1.850016 20.323616 2.351031 +v 1.850016 18.658239 2.654441 +v 1.850016 20.241886 1.902417 +v 0.634450 20.241886 1.902417 +v 0.634450 20.323616 2.351031 +v 1.850016 20.323616 2.351031 +v 0.634450 20.241886 1.902417 +v 0.634450 18.576508 2.205827 +v 0.634450 18.658239 2.654441 +v 0.634450 20.323616 2.351031 +v 6.450358 16.871246 6.704314 +v 6.450358 17.393959 9.573412 +v 6.993151 13.322628 10.315155 +v 6.993151 12.799915 7.446060 +v 4.904616 19.147156 6.289673 +v 4.904616 19.669867 9.158772 +v 2.591246 20.667866 6.012619 +v 2.591246 21.190580 8.881716 +v -0.137555 21.201870 5.915332 +v -0.137555 21.724585 8.784430 +v 2.591248 6.318684 8.626856 +v 2.591248 6.841393 11.495952 +v -0.137555 6.307393 11.593240 +v -0.137555 5.784679 8.724144 +v 4.904616 7.839396 8.349802 +v 4.904616 8.362108 11.218898 +v 6.450361 10.115304 7.935161 +v 6.450361 10.638018 10.804259 +v 6.450361 16.632854 11.177889 +v 6.993151 13.581101 11.733877 +v 6.993151 13.322628 10.315155 +v 6.450358 17.393959 9.573412 +v 4.904616 18.908764 10.763247 +v 4.904616 19.669867 9.158772 +v 2.591246 20.429476 10.486194 +v 2.591246 21.190580 8.881716 +v -0.137555 20.963480 10.388906 +v -0.137555 21.724585 8.784430 +v 2.591248 7.099868 12.914675 +v -0.137555 6.565864 13.011965 +v -0.137555 6.307393 11.593240 +v 2.591248 6.841393 11.495952 +v 4.904616 8.620581 12.637621 +v 4.904616 8.362108 11.218898 +v 6.450361 10.896488 12.222981 +v 6.450361 10.638018 10.804259 +v 6.450361 16.489346 12.240279 +v 6.993151 13.763827 12.736833 +v 6.993151 13.581101 11.733877 +v 6.450361 16.632854 11.177889 +v 4.904616 18.765253 11.825639 +v 4.904616 18.908764 10.763247 +v 2.591246 20.285967 11.548583 +v 2.591246 20.429476 10.486194 +v -0.137555 20.819971 11.451295 +v -0.137555 20.963480 10.388906 +v 2.591248 7.282592 13.917631 +v -0.137555 6.748590 14.014921 +v -0.137555 6.565864 13.011965 +v 2.591248 7.099868 12.914675 +v 4.904616 8.803307 13.640577 +v 4.904616 8.620581 12.637621 +v 6.450361 11.079216 13.225934 +v 6.450361 10.896488 12.222981 +v 6.450358 16.850937 14.456967 +v 6.993151 14.166320 14.946074 +v 6.993151 13.763827 12.736833 +v 6.450361 16.489346 12.240279 +v 4.904615 19.126841 14.042328 +v 4.904616 18.765253 11.825639 +v 2.591246 20.647554 13.765274 +v 2.591246 20.285967 11.548583 +v -0.137555 21.181559 13.667984 +v -0.137555 20.819971 11.451295 +v 2.591248 7.685090 16.126867 +v -0.137555 7.151085 16.224155 +v -0.137555 6.748590 14.014921 +v 2.591248 7.282592 13.917631 +v 4.904616 9.205801 15.849814 +v 4.904616 8.803307 13.640577 +v 6.450361 11.481709 15.435171 +v 6.450361 11.079216 13.225934 +v 6.450358 16.871246 6.704314 +v 6.993151 12.799915 7.446060 +v 5.267464 12.946739 7.419309 +v 4.856030 16.032776 6.857073 +v 4.904616 19.147156 6.289673 +v 3.684370 17.757896 6.542778 +v 2.591246 20.667866 6.012619 +v 1.930855 18.910583 6.332776 +v -0.137555 21.201870 5.915332 +v -0.137555 19.315353 6.259032 +v 2.591248 6.318684 8.626856 +v -0.137555 5.784679 8.724144 +v -0.137555 7.629245 8.388088 +v 1.930856 8.034017 8.314344 +v 4.904616 7.839396 8.349802 +v 3.684371 9.186704 8.104342 +v 6.450361 10.115304 7.935161 +v 4.856033 10.911824 7.790046 +v 6.358034 16.913443 14.535474 +v 6.358034 17.286711 16.584303 +v 6.893217 14.639720 17.066553 +v 6.893217 14.266452 15.017725 +v 4.833952 19.157454 14.126644 +v 4.833952 19.530722 16.175474 +v 2.553005 20.656855 13.853474 +v 2.553004 21.030119 15.902303 +v -0.137555 21.183367 13.757549 +v -0.137555 21.556639 15.806378 +v 2.553006 7.876051 16.181973 +v 2.553005 8.280234 18.225168 +v -0.137555 7.753714 18.321089 +v -0.137555 7.349532 16.277897 +v 4.833952 9.375453 15.908801 +v 4.833954 9.748725 17.957628 +v 2.553005 8.280234 18.225168 +v 2.553006 7.876051 16.181973 +v 6.358034 11.619465 15.499971 +v 6.358034 11.992731 17.548800 +v 4.833954 9.748725 17.957628 +v 4.833952 9.375453 15.908801 +v 3.367140 17.535583 17.474588 +v 3.367140 10.639707 18.730930 +v 7.131627 17.223406 19.063496 +v 5.827884 16.925011 20.438580 +v 6.319389 14.494061 20.881462 +v 7.730550 14.261175 19.603176 +v 5.426033 19.734667 18.605974 +v 4.428194 18.985874 20.063118 +v 2.873438 21.412638 18.300266 +v 2.333411 20.362902 19.812241 +v -0.137555 22.001863 18.192917 +v -0.137555 20.846445 19.724144 +v 2.873440 7.109707 20.906082 +v 2.333412 8.625226 21.950691 +v -0.137555 8.141681 22.038780 +v -0.137555 6.520483 21.013433 +v 5.426036 8.787681 20.600376 +v 4.428196 10.002252 21.699814 +v 7.131627 11.298944 20.142857 +v 5.827887 12.063114 21.324352 +v 6.319389 14.494061 20.881462 +v 5.827884 16.925011 20.438580 +v 4.652434 16.446011 20.525845 +v 5.047093 14.494061 20.881462 +v 4.428194 18.985874 20.063118 +v 3.528546 18.100796 20.224361 +v 2.333411 20.362902 19.812241 +v 1.846524 19.206488 20.022923 +v -0.137555 20.846445 19.724144 +v -0.137555 19.594753 19.952187 +v -0.137555 8.141681 22.038780 +v 2.333412 8.625226 21.950691 +v 1.846525 9.781638 21.740005 +v -0.137555 9.393373 21.810745 +v 4.428196 10.002252 21.699814 +v 3.528547 10.887330 21.538559 +v 5.827887 12.063114 21.324352 +v 4.652437 12.542114 21.237080 +v 4.652434 16.446011 20.525845 +v 4.441548 15.953656 17.762800 +v 4.818830 14.087643 18.102762 +v 5.047093 14.494061 20.881462 +v 3.528546 18.100796 20.224361 +v 3.367140 17.535583 17.474588 +v 1.846524 19.206488 20.022923 +v 1.759172 18.592596 17.282019 +v -0.137555 19.594753 19.952187 +v -0.137555 18.963770 17.214392 +v 1.846525 9.781638 21.740005 +v 1.759172 9.582694 18.923504 +v -0.137555 9.211522 18.991127 +v -0.137555 9.393373 21.810745 +v 3.528547 10.887330 21.538559 +v 3.367140 10.639707 18.730930 +v 4.652437 12.542114 21.237080 +v 4.441549 12.221634 18.442724 +v 7.061548 17.029957 17.660933 +v 7.654699 14.096279 18.195410 +v 6.893217 14.639720 17.066553 +v 6.358034 17.286711 16.584303 +v 5.372399 19.517012 17.207823 +v 4.833952 19.530722 16.175474 +v 2.844412 21.178797 16.905069 +v 2.553004 21.030119 15.902303 +v -0.137555 21.762342 16.798756 +v -0.137555 21.556639 15.806378 +v 2.844412 7.013754 19.485758 +v -0.137555 6.430205 19.592070 +v -0.137555 7.753714 18.321089 +v 5.372402 8.675551 19.182995 +v 7.061551 11.162606 18.729893 +v 6.358034 11.992731 17.548800 +v 7.131627 17.223406 19.063496 +v 7.730550 14.261175 19.603176 +v 7.654699 14.096279 18.195410 +v 7.061548 17.029957 17.660933 +v 5.426033 19.734667 18.605974 +v 5.372399 19.517012 17.207823 +v 2.873438 21.412638 18.300266 +v 2.844412 21.178797 16.905069 +v -0.137555 22.001863 18.192917 +v -0.137555 21.762342 16.798756 +v 2.873440 7.109707 20.906082 +v -0.137555 6.520483 21.013433 +v -0.137555 6.430205 19.592070 +v 2.844412 7.013754 19.485758 +v 5.426036 8.787681 20.600376 +v 5.372402 8.675551 19.182995 +v 7.131627 11.298944 20.142857 +v 7.061551 11.162606 18.729893 +v 10.347656 -5.313800 13.892914 +v 10.189377 -4.324817 14.341125 +v 10.173791 -4.065199 8.973198 +v 10.189374 -5.093708 9.342202 +v 9.902440 -2.803895 8.298583 +v 10.613137 -2.555629 8.260398 +v 10.749097 -4.127771 8.234436 +v 10.021957 -4.190809 8.244131 +v 9.801115 -1.547646 8.867919 +v 10.497879 -1.142371 8.805583 +v 9.733415 -0.613330 9.865463 +v 10.420865 -0.103137 9.786989 +v 9.709641 -0.143181 11.139349 +v 10.393823 0.403852 11.055208 +v 9.733415 -0.208778 12.495639 +v 10.420865 0.301416 12.417167 +v 9.722919 -0.800133 13.727847 +v 10.497879 -0.394859 13.665511 +v 9.824161 -1.827220 14.648387 +v 10.613143 -1.578959 14.610202 +v 9.943579 -3.133664 15.117107 +v 10.749100 -3.070621 15.107410 +v 10.062968 -4.520590 15.062659 +v 10.885056 -4.642758 15.081449 +v 10.144868 -5.776834 14.493324 +v 11.004154 -6.056028 14.536266 +v 10.221666 -6.653613 13.486927 +v 11.091516 -6.927130 13.528999 +v 11.004154 -6.056028 14.536266 +v 10.144868 -5.776834 14.493324 +v 10.248635 -7.046638 12.201180 +v 11.122193 -7.346391 12.247284 +v 10.221666 -7.058166 10.856751 +v 11.091516 -7.331683 10.898822 +v 10.144865 -6.524346 9.633391 +v 11.004151 -6.803540 9.676335 +v 10.062968 -5.497261 8.712852 +v 10.885054 -5.619434 8.731646 +v 11.410480 -5.221654 13.878742 +v 11.529890 -5.957200 13.093418 +v 11.410480 -5.810138 10.052730 +v 11.364158 -5.001564 9.328028 +v 9.902440 -2.803895 8.298583 +v 10.021957 -4.190809 8.244131 +v 10.021960 -4.078360 8.975224 +v 9.927868 -2.986496 9.018088 +v 10.062968 -5.497261 8.712852 +v 10.037540 -5.106868 9.344226 +v 10.144865 -6.524346 9.633391 +v 10.195823 -5.915451 10.068927 +v 10.221666 -7.058166 10.856751 +v 10.152031 -6.380999 11.038991 +v 10.248635 -7.046638 12.201180 +v 10.173262 -6.432639 12.106741 +v 10.221666 -6.653613 13.486927 +v 10.152031 -6.062517 13.109615 +v 10.144868 -5.776834 14.493324 +v 10.195823 -5.326966 13.894939 +v 10.144868 -5.776834 14.493324 +v 10.062968 -4.520590 15.062659 +v 10.037540 -4.337977 14.343151 +v 10.195823 -5.326966 13.894939 +v 9.943579 -3.133664 15.117107 +v 10.021963 -3.246119 14.386018 +v 9.824161 -1.827220 14.648387 +v 9.927871 -2.217605 14.017013 +v 9.722919 -0.800133 13.727847 +v 9.848101 -1.409030 13.292312 +v 9.733415 -0.208778 12.495639 +v 9.794803 -0.943481 12.322249 +v 9.709641 -0.143181 11.139349 +v 9.776086 -0.891838 11.254501 +v 9.733415 -0.613330 9.865463 +v 9.794800 -1.261969 10.251627 +v 9.801115 -1.547646 8.867919 +v 9.848098 -1.997514 9.466303 +v 11.388451 -4.072338 8.225910 +v 11.268930 -2.685418 8.280360 +v 11.294358 -2.868025 8.999867 +v 11.388016 -3.954838 8.957199 +v 11.167605 -1.429169 8.849696 +v 11.214588 -1.879037 9.448082 +v 11.099905 -0.494853 9.847242 +v 11.161290 -1.143492 10.233404 +v 11.076132 -0.024707 11.121126 +v 11.142576 -0.773364 11.236277 +v 11.099905 -0.090303 12.477416 +v 11.161293 -0.825004 12.304027 +v 11.167608 -0.681659 13.709624 +v 11.214591 -1.290552 13.274091 +v 11.268933 -1.708743 14.630164 +v 11.294361 -2.099133 13.998790 +v 11.388453 -3.015193 15.098885 +v 11.388453 -3.127642 14.367793 +v 11.541418 -4.402113 15.044438 +v 11.515990 -4.219506 14.324928 +v 11.694958 -5.658357 14.475101 +v 11.562313 -5.208493 13.876717 +v 11.694958 -5.658357 14.475101 +v 11.771752 -6.569247 13.473953 +v 11.702118 -5.944039 13.091393 +v 11.562313 -5.208493 13.876717 +v 11.798721 -6.962277 12.188204 +v 11.723349 -6.314167 12.088518 +v 11.771752 -6.973800 10.843776 +v 11.702118 -6.262527 11.020768 +v 11.694952 -6.405869 9.615170 +v 11.562313 -5.796978 10.050706 +v 11.541418 -5.378782 8.694632 +v 11.515990 -4.988397 9.326004 +v 9.927868 -2.986496 9.018088 +v 10.021960 -4.078360 8.975224 +v 10.173791 -4.065199 8.973198 +v 10.079700 -2.973335 9.016065 +v 10.037540 -5.106868 9.344226 +v 10.189374 -5.093708 9.342202 +v 10.195823 -5.915451 10.068927 +v 10.347653 -5.902290 10.066901 +v 10.152031 -6.380999 11.038991 +v 10.324265 -6.367838 11.036967 +v 10.173262 -6.432639 12.106741 +v 10.345494 -6.419479 12.104715 +v 10.152031 -6.062517 13.109615 +v 10.324265 -6.049350 13.107590 +v 10.195823 -5.326966 13.894939 +v 10.347656 -5.313800 13.892914 +v 10.195823 -5.326966 13.894939 +v 10.037540 -4.337977 14.343151 +v 10.189377 -4.324817 14.341125 +v 10.347656 -5.313800 13.892914 +v 10.021963 -3.246119 14.386018 +v 10.173796 -3.232952 14.383994 +v 9.927871 -2.217605 14.017013 +v 10.079700 -2.204439 14.014987 +v 9.848101 -1.409030 13.292312 +v 9.999933 -1.395863 13.290288 +v 9.794803 -0.943481 12.322249 +v 9.946635 -0.930319 12.320226 +v 9.776086 -0.891838 11.254501 +v 9.927916 -0.878675 11.252476 +v 9.794800 -1.261969 10.251627 +v 9.946632 -1.248802 10.249601 +v 9.848098 -1.997514 9.466303 +v 9.999930 -1.984348 9.464279 +v 11.388016 -3.954838 8.957199 +v 11.294358 -2.868025 8.999867 +v 11.142525 -2.881190 9.001891 +v 11.236621 -3.973048 8.959024 +v 11.214588 -1.879037 9.448082 +v 11.062758 -1.892203 9.450106 +v 11.161290 -1.143492 10.233404 +v 11.009460 -1.156657 10.235428 +v 11.142576 -0.773364 11.236277 +v 10.990744 -0.786530 11.238302 +v 11.161293 -0.825004 12.304027 +v 11.009460 -0.838170 12.306050 +v 11.214591 -1.290552 13.274091 +v 11.062758 -1.303718 13.276114 +v 11.294361 -2.099133 13.998790 +v 11.142528 -2.112300 14.000815 +v 11.388453 -3.127642 14.367793 +v 11.236621 -3.140807 14.369819 +v 11.515990 -4.219506 14.324928 +v 11.364158 -4.232666 14.326954 +v 11.562313 -5.208493 13.876717 +v 11.410480 -5.221654 13.878742 +v 11.562313 -5.208493 13.876717 +v 11.702118 -5.944039 13.091393 +v 11.529890 -5.957200 13.093418 +v 11.410480 -5.221654 13.878742 +v 11.723349 -6.314167 12.088518 +v 11.551121 -6.327328 12.090542 +v 11.702118 -6.262527 11.020768 +v 11.529890 -6.275688 11.022794 +v 11.562313 -5.796978 10.050706 +v 11.410480 -5.810138 10.052730 +v 11.515990 -4.988397 9.326004 +v 11.364158 -5.001564 9.328028 +v 11.268930 -2.685418 8.280360 +v 11.388451 -4.072338 8.225910 +v 10.749097 -4.127771 8.234436 +v 10.613137 -2.555629 8.260398 +v 11.167605 -1.429169 8.849696 +v 10.497879 -1.142371 8.805583 +v 11.099905 -0.494853 9.847242 +v 10.420865 -0.103137 9.786989 +v 11.076132 -0.024707 11.121126 +v 10.393823 0.403852 11.055208 +v 11.099905 -0.090303 12.477416 +v 10.420865 0.301416 12.417167 +v 11.167608 -0.681659 13.709624 +v 10.497879 -0.394859 13.665511 +v 11.268933 -1.708743 14.630164 +v 10.613143 -1.578959 14.610202 +v 11.388453 -3.015193 15.098885 +v 10.749100 -3.070621 15.107410 +v 11.541418 -4.402113 15.044438 +v 10.885056 -4.642758 15.081449 +v 11.694958 -5.658357 14.475101 +v 11.004154 -6.056028 14.536266 +v 11.771752 -6.569247 13.473953 +v 11.694958 -5.658357 14.475101 +v 11.004154 -6.056028 14.536266 +v 11.091516 -6.927130 13.528999 +v 11.798721 -6.962277 12.188204 +v 11.122193 -7.346391 12.247284 +v 11.771752 -6.973800 10.843776 +v 11.091516 -7.331683 10.898822 +v 11.694952 -6.405869 9.615170 +v 11.004151 -6.803540 9.676335 +v 11.541418 -5.378782 8.694632 +v 10.885054 -5.619434 8.731646 +v 2.834212 15.910932 19.358097 +v 2.834203 15.949227 19.568314 +v 3.293931 14.261284 19.875835 +v 3.293931 14.222986 19.665619 +v 1.578188 17.146614 19.132973 +v 1.578188 17.184916 19.343189 +v -0.137555 17.598896 19.050575 +v -0.137555 17.637207 19.260788 +v 1.578186 11.299354 20.198269 +v 1.578186 11.337653 20.408482 +v -0.137555 10.885364 20.490883 +v -0.137555 10.847074 20.280666 +v 2.834198 12.535027 19.973148 +v 2.834198 12.573324 20.183361 +v 3.293931 14.222986 19.665619 +v 3.293931 14.261284 19.875835 +v 2.834198 12.573324 20.183361 +v 2.834198 12.535027 19.973148 +v 0.612181 15.845090 21.324074 +v 0.612181 13.289986 21.789577 +v 2.322617 15.861096 20.732384 +v 1.674100 15.581246 21.285297 +v 1.954364 14.552227 21.472775 +v 2.703207 14.463718 20.986965 +v 1.282826 16.884050 20.546009 +v 0.908404 16.334541 21.148060 +v -0.137555 17.258476 20.477798 +v -0.137555 16.610268 21.097824 +v 1.282824 12.043385 21.427917 +v 0.908404 12.769909 21.797489 +v -0.137555 12.494183 21.847721 +v -0.137555 11.668958 21.496132 +v 2.322616 13.066336 21.241550 +v 1.674100 13.523205 21.660248 +v 1.954364 14.552227 21.472775 +v 1.674100 15.581246 21.285297 +v 1.298521 15.367919 21.324165 +v 1.520683 14.552227 21.472775 +v 1.674100 15.581246 21.285297 +v 0.908404 16.334541 21.148060 +v 0.691564 15.965047 21.215378 +v 1.298521 15.367919 21.324165 +v 0.908404 16.334541 21.148060 +v -0.137555 16.610268 21.097824 +v -0.137555 16.183609 21.175556 +v 0.691564 15.965047 21.215378 +v -0.137555 12.494183 21.847721 +v 0.908404 12.769909 21.797489 +v 0.691563 13.139404 21.730171 +v -0.137555 12.920841 21.769989 +v 0.908404 12.769909 21.797489 +v 1.674100 13.523205 21.660248 +v 1.298520 13.736532 21.621380 +v 0.691563 13.139404 21.730171 +v 1.674100 13.523205 21.660248 +v 1.954364 14.552227 21.472775 +v 1.520683 14.552227 21.472775 +v 1.298520 13.736532 21.621380 +v 1.520683 14.507906 21.229500 +v 1.298521 15.323596 21.080891 +v 1.161026 15.245500 21.095123 +v 1.361917 14.507906 21.229500 +v 1.298521 15.323596 21.080891 +v 0.691564 15.920726 20.972099 +v 0.612181 15.785457 20.996750 +v 1.161026 15.245500 21.095123 +v 0.691564 15.920726 20.972099 +v -0.137555 16.139288 20.932285 +v -0.137555 15.983094 20.960735 +v 0.612181 15.785457 20.996750 +v -0.137555 12.876520 21.526718 +v 0.691563 13.095083 21.486900 +v 0.612181 13.230352 21.462250 +v -0.137555 13.032715 21.498264 +v 0.691563 13.095083 21.486900 +v 1.298520 13.692211 21.378109 +v 1.161025 13.770308 21.363880 +v 0.612181 13.230352 21.462250 +v 1.298520 13.692211 21.378109 +v 1.520683 14.507906 21.229500 +v 1.361917 14.507906 21.229500 +v 1.161025 13.770308 21.363880 +v 1.520683 14.552227 21.472775 +v 1.298521 15.367919 21.324165 +v 1.298521 15.323596 21.080891 +v 1.520683 14.507906 21.229500 +v 1.298521 15.367919 21.324165 +v 0.691564 15.965047 21.215378 +v 0.691564 15.920726 20.972099 +v 1.298521 15.323596 21.080891 +v 0.691564 15.965047 21.215378 +v -0.137555 16.183609 21.175556 +v -0.137555 16.139288 20.932285 +v 0.691564 15.920726 20.972099 +v -0.137555 12.920841 21.769989 +v 0.691563 13.139404 21.730171 +v 0.691563 13.095083 21.486900 +v -0.137555 12.876520 21.526718 +v 0.691563 13.139404 21.730171 +v 1.298520 13.736532 21.621380 +v 1.298520 13.692211 21.378109 +v 0.691563 13.095083 21.486900 +v 1.298520 13.736532 21.621380 +v 1.520683 14.552227 21.472775 +v 1.520683 14.507906 21.229500 +v 1.298520 13.692211 21.378109 +v 1.161026 15.305133 21.422447 +v 1.361917 14.567539 21.556824 +v 1.361917 14.507906 21.229500 +v 1.161026 15.245500 21.095123 +v 1.361917 14.567539 21.556824 +v 1.161025 13.829943 21.691204 +v 1.161025 13.770308 21.363880 +v 1.361917 14.507906 21.229500 +v 1.161025 13.829943 21.691204 +v 0.612181 13.289986 21.789577 +v 0.612181 13.230352 21.462250 +v 1.161025 13.770308 21.363880 +v 0.612181 13.289986 21.789577 +v -0.137555 13.092350 21.825588 +v -0.137555 13.032715 21.498264 +v 0.612181 13.230352 21.462250 +v -0.137555 16.042728 21.288063 +v 0.612181 15.845090 21.324074 +v 0.612181 15.785457 20.996750 +v -0.137555 15.983094 20.960735 +v 0.612181 15.845090 21.324074 +v 1.161026 15.305133 21.422447 +v 1.161026 15.245500 21.095123 +v 0.612181 15.785457 20.996750 +v 2.322617 15.861096 20.732384 +v 2.703207 14.463718 20.986965 +v 2.703207 14.261284 19.875835 +v 2.322617 15.658666 19.621250 +v 1.282826 16.884050 20.546009 +v 1.282826 16.681616 19.434883 +v -0.137555 17.258476 20.477798 +v -0.137555 17.056042 19.366665 +v 1.282824 12.043385 21.427917 +v -0.137555 11.668958 21.496132 +v -0.137555 11.466525 20.385002 +v 1.282824 11.840951 20.316792 +v 2.322616 13.066336 21.241550 +v 2.322616 12.863903 20.130421 +v 2.703207 14.463718 20.986965 +v 2.322616 13.066336 21.241550 +v 2.322616 12.863903 20.130421 +v 2.703207 14.261284 19.875835 +v 2.322617 15.658666 19.621250 +v 2.703207 14.261284 19.875835 +v 3.293931 14.261284 19.875835 +v 2.834203 15.949227 19.568314 +v 2.703207 14.261284 19.875835 +v 2.322616 12.863903 20.130421 +v 2.834198 12.573324 20.183361 +v 3.293931 14.261284 19.875835 +v 2.322616 12.863903 20.130421 +v 1.282824 11.840951 20.316792 +v 1.578186 11.337653 20.408482 +v 2.834198 12.573324 20.183361 +v 1.282824 11.840951 20.316792 +v -0.137555 11.466525 20.385002 +v -0.137555 10.885364 20.490883 +v 1.578186 11.337653 20.408482 +v -0.137555 17.056042 19.366665 +v 1.282826 16.681616 19.434883 +v 1.578188 17.184916 19.343189 +v -0.137555 17.637207 19.260788 +v 1.282826 16.681616 19.434883 +v 2.322617 15.658666 19.621250 +v 2.834203 15.949227 19.568314 +v 1.578188 17.184916 19.343189 +v 0.663374 14.114862 23.483055 +v 0.715733 14.051090 23.425133 +v 1.072334 14.898061 23.270828 +v 0.998288 14.910323 23.338135 +v 0.715733 15.745030 23.116516 +v -0.137555 16.092754 23.053173 +v -0.137555 16.032167 23.133743 +v 0.663374 15.705781 23.193211 +v 0.998288 14.910323 23.338135 +v 1.072334 14.898061 23.270828 +v 0.715733 15.745030 23.116516 +v 0.663374 15.705781 23.193211 +v 0.715733 13.707285 21.538033 +v 1.072334 14.554255 21.383720 +v 1.072334 14.898061 23.270828 +v 0.715733 14.051090 23.425133 +v -0.137555 15.748948 21.166065 +v -0.137555 16.092754 23.053173 +v 0.715733 15.745030 23.116516 +v 0.715733 15.401227 21.229416 +v 1.072334 14.554255 21.383720 +v 0.715733 15.401227 21.229416 +v 0.715733 15.745030 23.116516 +v 1.072334 14.898061 23.270828 +v 6.777748 -3.374685 12.138167 +v 2.092322 7.513035 15.175448 +v 2.646245 7.840734 14.815410 +v 7.332256 -2.982224 11.851208 +v 2.704278 7.707127 15.538341 +v 7.388817 -3.256060 12.533605 +v 7.388817 -3.256060 12.533605 +v 2.704278 7.707127 15.538341 +v 2.092322 7.513035 15.175448 +v 6.777748 -3.374685 12.138167 +v 6.154264 -3.665658 11.352397 +v 1.461396 4.755218 2.757458 +v 2.013677 4.917661 2.296532 +v 6.703182 -3.810490 10.881698 +v 6.703182 -3.810490 10.881698 +v 2.013677 4.917661 2.296532 +v 2.074895 5.073448 3.014745 +v 6.769146 -3.258756 11.366552 +v 6.769146 -3.258756 11.366552 +v 2.074895 5.073448 3.014745 +v 1.461396 4.755218 2.757458 +v 6.154264 -3.665658 11.352397 +v -0.137555 -3.498145 12.355363 +v -0.137555 -4.092004 12.209984 +v 8.748431 -4.092004 12.209988 +v 8.748431 -3.498145 12.355365 +v -0.137555 -4.409125 11.687263 +v 8.748431 -4.409125 11.687264 +v -0.137555 -4.263747 11.093404 +v 8.748431 -4.263747 11.093406 +v -0.137555 -3.741025 10.776280 +v 8.748431 -3.741025 10.776284 +v -0.137555 -3.147167 10.921659 +v 8.748431 -3.147167 10.921661 +v -0.137555 -3.147167 10.921659 +v -0.137555 -2.830044 11.444382 +v 8.748431 -2.830044 11.444382 +v 8.748431 -3.147167 10.921661 +v -0.137555 -2.975422 12.038241 +v 8.748431 -2.975422 12.038241 +v 8.748431 -3.498145 12.355365 +v 8.748431 -4.092004 12.209988 +v 8.748431 -3.953632 12.021311 +v 8.748431 -3.533713 12.124108 +v 8.748431 -4.409125 11.687264 +v 8.748431 -4.177870 11.651694 +v 8.748431 -4.263747 11.093406 +v 8.748431 -4.075072 11.231778 +v 8.748431 -3.741025 10.776284 +v 8.748431 -3.705457 11.007539 +v 8.748431 -3.147167 10.921661 +v 8.748431 -3.285537 11.110336 +v 8.748431 -3.147167 10.921661 +v 8.748431 -2.830044 11.444382 +v 8.748431 -3.061300 11.479952 +v 8.748431 -3.285537 11.110336 +v 8.748431 -2.975422 12.038241 +v 8.748431 -3.164097 11.899868 +v 8.748431 -3.953632 12.021311 +v 9.643316 -3.959666 12.022240 +v 9.605914 -3.541481 12.125302 +v 8.748431 -3.533713 12.124108 +v 8.748431 -4.177870 11.651694 +v 9.658809 -4.183187 11.652513 +v 9.643316 -3.959666 12.022240 +v 8.748431 -3.953632 12.021311 +v 8.748431 -4.075072 11.231778 +v 9.643316 -4.081106 11.232705 +v 9.658809 -4.183187 11.652513 +v 8.748431 -4.177870 11.651694 +v 8.748431 -3.705457 11.007539 +v 9.605911 -3.713226 11.008735 +v 9.643316 -4.081106 11.232705 +v 8.748431 -4.075072 11.231778 +v 8.748431 -3.285537 11.110336 +v 9.568512 -3.295041 11.111799 +v 9.605911 -3.713226 11.008735 +v 8.748431 -3.705457 11.007539 +v 8.748431 -3.061300 11.479952 +v 9.553020 -3.071526 11.481525 +v 9.568512 -3.295041 11.111799 +v 8.748431 -3.285537 11.110336 +v 8.748431 -3.164097 11.899868 +v 9.568512 -3.173601 11.901333 +v 9.553020 -3.071526 11.481525 +v 8.748431 -3.061300 11.479952 +v 8.748431 -3.533713 12.124108 +v 9.605914 -3.541481 12.125302 +v 9.568512 -3.173601 11.901333 +v 8.748431 -3.164097 11.899868 +v 10.058415 -4.646990 13.001652 +v 9.940152 -3.324768 13.327479 +v 9.605914 -3.355837 13.332259 +v 9.724178 -4.678094 13.006378 +v 10.107403 -5.353782 11.832620 +v 10.058415 -4.646990 13.001652 +v 9.724178 -4.678094 13.006378 +v 9.773163 -5.384840 11.837338 +v 10.058415 -5.031002 10.505172 +v 10.107403 -5.353782 11.832620 +v 9.773163 -5.384840 11.837338 +v 9.724178 -5.062070 10.509952 +v 9.940152 -3.867794 9.797000 +v 10.058415 -5.031002 10.505172 +v 9.724178 -5.062070 10.509952 +v 9.605911 -3.898864 9.801778 +v 9.821888 -2.545539 10.122879 +v 9.940152 -3.867794 9.797000 +v 9.605911 -3.898864 9.801778 +v 9.487651 -2.576614 10.127659 +v 9.772903 -1.838800 11.291918 +v 9.821888 -2.545539 10.122879 +v 9.487651 -2.576614 10.127659 +v 9.438663 -1.869869 11.296698 +v 9.821891 -2.161563 12.619308 +v 9.772903 -1.838800 11.291918 +v 9.438663 -1.869869 11.296698 +v 9.487651 -2.192633 12.624087 +v 9.940152 -3.324768 13.327479 +v 9.821891 -2.161563 12.619308 +v 9.487651 -2.192633 12.624087 +v 9.605914 -3.355837 13.332259 +v 9.605914 -3.541481 12.125302 +v 9.643316 -3.959666 12.022240 +v 9.724178 -4.678094 13.006378 +v 9.605914 -3.355837 13.332259 +v 9.643316 -3.959666 12.022240 +v 9.658809 -4.183187 11.652513 +v 9.773163 -5.384840 11.837338 +v 9.724178 -4.678094 13.006378 +v 9.658809 -4.183187 11.652513 +v 9.643316 -4.081106 11.232705 +v 9.724178 -5.062070 10.509952 +v 9.773163 -5.384840 11.837338 +v 9.643316 -4.081106 11.232705 +v 9.605911 -3.713226 11.008735 +v 9.605911 -3.898864 9.801778 +v 9.724178 -5.062070 10.509952 +v 9.605911 -3.713226 11.008735 +v 9.568512 -3.295041 11.111799 +v 9.487651 -2.576614 10.127659 +v 9.605911 -3.898864 9.801778 +v 9.568512 -3.295041 11.111799 +v 9.553020 -3.071526 11.481525 +v 9.438663 -1.869869 11.296698 +v 9.487651 -2.576614 10.127659 +v 9.553020 -3.071526 11.481525 +v 9.568512 -3.173601 11.901333 +v 9.487651 -2.192633 12.624087 +v 9.438663 -1.869869 11.296698 +v 9.568512 -3.173601 11.901333 +v 9.605914 -3.541481 12.125302 +v 9.605914 -3.355837 13.332259 +v 9.487651 -2.192633 12.624087 +v 34.341240 10.973569 -4.305973 +v 34.243908 10.975993 -4.362118 +v 34.243908 24.352579 -3.784413 +v 34.341240 24.350153 -3.728268 +v 34.243908 10.971145 -4.249835 +v 34.243908 24.347727 -3.672129 +v 34.243908 10.971145 -4.249835 +v 34.243908 24.347727 -3.672129 +v 30.426413 12.832979 10.128780 +v 30.118052 12.800472 9.998507 +v 12.762002 27.443340 10.522055 +v 13.057944 27.553009 10.638275 +v 30.118052 12.800472 9.998507 +v 30.158758 12.818146 10.331888 +v 12.812175 27.472626 10.853327 +v 12.762002 27.443340 10.522055 +v 5.737073 7.860677 11.055306 +v 5.473027 7.993579 10.894900 +v 29.891050 26.787380 10.721846 +v 30.191610 26.743053 10.866119 +v 5.473027 7.993579 10.894900 +v 5.484356 7.996225 11.231019 +v 29.919748 26.796295 11.056828 +v 29.891050 26.787380 10.721846 +v 31.197435 10.997549 0.614511 +v 30.819914 11.104485 0.424588 +v 30.819914 27.064472 9.410694 +v 31.197435 26.957542 9.600615 +v 30.819914 10.890615 0.804433 +v 30.819914 26.850609 9.790541 +v 30.819914 10.890615 0.804433 +v 30.819914 26.850609 9.790541 +v 6.536887 16.464014 12.762231 +v 6.446542 16.478489 12.489015 +v 13.910448 26.973335 10.576986 +v 14.000791 26.958862 10.850204 +v 6.228430 16.608864 12.353185 +v 13.692336 27.103703 10.441158 +v 6.010320 16.778763 12.434308 +v 13.474224 27.273605 10.522282 +v 6.010320 16.778763 12.434308 +v 5.919975 16.888662 12.684867 +v 13.383882 27.383507 10.772839 +v 13.474224 27.273605 10.522282 +v 6.010320 16.874187 12.958085 +v 13.474224 27.369030 11.046057 +v 6.228430 16.743813 13.093915 +v 13.692336 27.238659 11.181887 +v 6.446542 16.573915 13.012792 +v 13.910448 27.068760 11.100763 +v 3.460064 17.646593 0.622345 +v 2.406346 17.756693 -2.452592 +v 2.406346 18.909416 -2.662604 +v 3.460064 18.907696 0.392586 +v -0.137555 17.892881 -3.742776 +v -0.137555 19.238968 -3.988016 +v -0.137555 19.903610 4.531394 +v 2.406346 19.189877 3.396056 +v 2.406346 20.489861 3.159215 +v -0.137555 21.291946 4.278457 +v 3.460064 18.907696 0.392586 +v 2.406346 18.909416 -2.662604 +v 1.988101 18.997980 -2.176482 +v 2.868576 18.907696 0.392586 +v 2.406346 18.909416 -2.662604 +v -0.137555 19.238968 -3.988016 +v -0.137555 19.364216 -3.300538 +v 1.988101 18.997980 -2.176482 +v -0.137555 21.291946 4.278457 +v 2.406346 20.489861 3.159215 +v 1.988101 20.286793 2.693955 +v -0.137555 20.931133 3.633897 +v 2.406346 20.489861 3.159215 +v 3.460064 18.907696 0.392586 +v 2.868576 18.907696 0.392586 +v 1.988101 20.286793 2.693955 +v 2.868576 18.907696 0.392586 +v 1.988101 18.997980 -2.176482 +v 1.988101 17.957739 -1.986966 +v 2.868576 17.970634 0.563307 +v 1.988101 18.997980 -2.176482 +v -0.137555 19.364216 -3.300538 +v -0.137555 18.330683 -3.112241 +v 1.988101 17.957739 -1.986966 +v -0.137555 20.931133 3.633897 +v 1.988101 20.286793 2.693955 +v 1.988101 19.330694 2.868146 +v -0.137555 20.041740 3.795932 +v 1.988101 20.286793 2.693955 +v 2.868576 18.907696 0.392586 +v 2.868576 17.970634 0.563307 +v 1.988101 19.330694 2.868146 +v 1.764696 19.540924 2.445557 +v 1.607252 19.914867 2.377429 +v 1.607252 22.648573 17.382362 +v 1.764696 22.274622 17.450493 +v 1.227150 20.069763 2.349210 +v 1.227150 22.803465 17.354145 +v 0.847047 19.914867 2.377429 +v 0.847047 22.648573 17.382362 +v 0.689604 19.540924 2.445557 +v 0.689604 22.274622 17.450493 +v 0.847048 19.166975 2.513686 +v 0.847048 21.900675 17.518620 +v 0.847048 19.166975 2.513686 +v 1.227150 19.012081 2.541905 +v 1.227150 21.745783 17.546841 +v 0.847048 21.900675 17.518620 +v 1.607252 19.166975 2.513686 +v 1.607252 21.900675 17.518620 +v 1.227150 22.803465 17.354145 +v 1.227150 21.745783 17.546841 +v 1.328365 22.063547 17.279758 +v 1.264851 22.214399 17.252277 +v 1.264851 22.423220 18.398476 +v 1.328365 22.272367 18.425961 +v 1.111515 22.276884 17.240891 +v 1.111515 22.485704 18.387093 +v 0.958179 22.214399 17.252277 +v 0.958179 22.423220 18.398476 +v 0.894666 22.063547 17.279758 +v 0.894666 22.272367 18.425961 +v 0.958179 21.912691 17.307243 +v 0.958179 22.121515 18.453445 +v 1.111515 21.850206 17.318626 +v 1.111515 22.059031 18.464828 +v 1.264851 21.912691 17.307243 +v 1.264851 22.121515 18.453445 +v 1.264851 21.912691 17.307243 +v 1.264851 22.121515 18.453445 +v 1.111515 22.485704 18.387093 +v 1.111515 22.059031 18.464828 +v 0.133934 16.457468 22.082756 +v 0.267938 16.083843 22.120594 +v 2.302677 19.353851 21.730213 +v 1.762202 19.625370 21.680744 +v 0.376600 15.523850 22.167568 +v 2.625365 19.191742 21.759747 +v 0.628120 15.397494 22.190586 +v 2.876886 19.065380 21.782768 +v 1.073912 15.597871 22.200733 +v 3.029603 18.988661 21.796749 +v 3.662046 22.581758 20.906145 +v 3.067275 22.880556 20.851706 +v 4.308591 22.256952 20.965319 +v 4.570708 22.125269 20.989309 +v 4.812643 22.003733 21.011454 +v 3.548905 24.540596 20.280066 +v 4.345898 24.140202 20.353010 +v 4.939451 25.479950 19.791376 +v 3.965612 25.969181 19.702246 +v 5.111174 23.755747 20.423056 +v 5.808561 25.043331 19.870920 +v 5.474634 23.573153 20.456320 +v 6.406145 24.743126 19.925615 +v 5.860458 23.379328 20.491632 +v 6.917789 24.486084 19.972445 +v 5.114145 27.688271 18.802736 +v 4.300347 26.658504 19.377474 +v 5.623532 26.764912 19.158976 +v 6.998875 29.319054 17.636089 +v 6.739822 26.703918 19.030687 +v 8.696851 30.098463 16.817387 +v 7.574537 26.468971 19.019972 +v 10.099993 29.987379 16.565411 +v 7.997971 25.885288 19.232836 +v 10.327551 28.673668 17.340019 +v 1.523451 15.759408 22.209934 +v 3.151719 18.927311 21.807922 +v 5.006096 21.906542 21.029158 +v 6.168966 23.224344 20.519869 +v 7.326908 24.280552 20.009890 +v 8.236472 25.164478 19.527105 +v 9.891048 26.912106 18.423786 +v 0.133934 16.457468 22.082756 +v 1.762202 19.625370 21.680744 +v 1.761461 19.573685 21.405102 +v 0.143137 16.425117 21.804653 +v 0.267938 16.083843 22.120594 +v 0.133934 16.457468 22.082756 +v 0.143137 16.425117 21.804653 +v 0.277901 16.052988 21.842400 +v 0.376600 15.523850 22.167568 +v 0.267938 16.083843 22.120594 +v 0.277901 16.052988 21.842400 +v 0.387803 15.495456 21.889267 +v 0.628120 15.397494 22.190586 +v 0.376600 15.523850 22.167568 +v 0.387803 15.495456 21.889267 +v 0.639325 15.369096 21.912289 +v 3.151719 18.927311 21.807922 +v 1.523451 15.759408 22.209934 +v 1.532655 15.727063 21.931828 +v 3.150980 18.875626 21.532272 +v 1.073912 15.597871 22.200733 +v 0.628120 15.397494 22.190586 +v 0.639325 15.369096 21.912289 +v 1.084074 15.567410 21.922523 +v 1.762202 19.625370 21.680744 +v 3.067275 22.880556 20.851706 +v 3.056602 22.809984 20.580938 +v 1.761461 19.573685 21.405102 +v 5.006096 21.906542 21.029158 +v 3.151719 18.927311 21.807922 +v 3.150980 18.875626 21.532272 +v 4.995420 21.835970 20.758394 +v 3.548905 24.540596 20.280066 +v 3.965612 25.969181 19.702246 +v 3.945781 25.881596 19.438158 +v 3.533330 24.460865 20.012613 +v 6.168966 23.224344 20.519869 +v 5.006096 21.906542 21.029158 +v 4.995420 21.835970 20.758394 +v 6.153392 23.144617 20.252417 +v 4.300347 26.658504 19.377474 +v 5.114145 27.688271 18.802736 +v 5.088283 27.589695 18.544235 +v 4.278272 26.566811 19.115355 +v 5.114145 27.688271 18.802736 +v 6.998875 29.319054 17.636089 +v 6.966145 29.208187 17.385136 +v 5.088283 27.589695 18.544235 +v 6.998875 29.319054 17.636089 +v 8.696851 30.098463 16.817387 +v 8.659721 29.979841 16.571980 +v 6.966145 29.208187 17.385136 +v 8.696851 30.098463 16.817387 +v 10.099993 29.987379 16.565411 +v 10.061254 29.865934 16.322176 +v 8.659721 29.979841 16.571980 +v 10.327551 28.673668 17.340019 +v 9.891048 26.912106 18.423786 +v 9.860877 26.805782 18.169872 +v 10.292060 28.557926 17.092480 +v 8.236472 25.164478 19.527105 +v 7.326908 24.280552 20.009890 +v 7.307082 24.192966 19.745806 +v 8.213132 25.070473 19.266150 +v 10.099993 29.987379 16.565411 +v 10.327551 28.673668 17.340019 +v 10.292060 28.557926 17.092480 +v 10.061254 29.865934 16.322176 +v 1.523451 15.759408 22.209934 +v 1.073912 15.597871 22.200733 +v 1.084074 15.567410 21.922523 +v 1.532655 15.727063 21.931828 +v 9.891048 26.912106 18.423786 +v 8.236472 25.164478 19.527105 +v 8.213132 25.070473 19.266150 +v 9.860877 26.805782 18.169872 +v 3.965612 25.969181 19.702246 +v 4.300347 26.658504 19.377474 +v 4.278272 26.566811 19.115355 +v 3.945781 25.881596 19.438158 +v 3.067275 22.880556 20.851706 +v 3.548905 24.540596 20.280066 +v 3.533330 24.460865 20.012613 +v 3.056602 22.809984 20.580938 +v 7.326908 24.280552 20.009890 +v 6.168966 23.224344 20.519869 +v 6.153392 23.144617 20.252417 +v 7.307082 24.192966 19.745806 +v 1.761461 19.573685 21.405102 +v 3.056602 22.809984 20.580938 +v 4.995420 21.835970 20.758394 +v 3.150980 18.875626 21.532272 +v 1.084074 15.567410 21.922523 +v 0.277901 16.052988 21.842400 +v 0.143137 16.425117 21.804653 +v 1.532655 15.727063 21.931828 +v 1.084074 15.567410 21.922523 +v 0.639325 15.369096 21.912289 +v 0.387803 15.495456 21.889267 +v 0.277901 16.052988 21.842400 +v 1.532655 15.727063 21.931828 +v 0.143137 16.425117 21.804653 +v 1.761461 19.573685 21.405102 +v 3.150980 18.875626 21.532272 +v 3.533330 24.460865 20.012613 +v 6.153392 23.144617 20.252417 +v 4.995420 21.835970 20.758394 +v 3.056602 22.809984 20.580938 +v 4.278272 26.566811 19.115355 +v 8.213132 25.070473 19.266150 +v 7.307082 24.192966 19.745806 +v 3.945781 25.881596 19.438158 +v 6.153392 23.144617 20.252417 +v 3.533330 24.460865 20.012613 +v 3.945781 25.881596 19.438158 +v 7.307082 24.192966 19.745806 +v 6.966145 29.208187 17.385136 +v 8.659721 29.979841 16.571980 +v 10.061254 29.865934 16.322176 +v 10.292060 28.557926 17.092480 +v 8.213132 25.070473 19.266150 +v 4.278272 26.566811 19.115355 +v 5.088283 27.589695 18.544235 +v 9.860877 26.805782 18.169872 +v 9.860877 26.805782 18.169872 +v 5.088283 27.589695 18.544235 +v 6.966145 29.208187 17.385136 +v 10.292060 28.557926 17.092480 +v 0.376462 18.602148 -30.355957 +v 0.330657 17.075186 -24.744356 +v 0.285053 18.414829 -35.894424 +v 0.330618 18.670710 -33.683582 +v 0.330657 17.075186 -24.744356 +v 0.330657 15.336257 -22.014980 +v 0.330657 12.973974 -22.107353 +v 0.330657 11.401349 -24.776545 +v 0.330657 11.401349 -24.776545 +v 0.330657 7.251087 -23.778145 +v 0.330657 4.289977 -24.225880 +v 0.376461 3.909487 -27.628571 +v 0.330657 11.401349 -24.776545 +v 0.200132 4.545812 -36.862782 +v 0.285053 18.414829 -35.894424 +v 0.330657 17.075186 -24.744356 +v 0.285052 3.476720 -33.171906 +v 0.219588 3.910732 -35.557438 +v 0.200132 4.545812 -36.862782 +v 0.376461 3.909487 -27.628571 +v 0.170088 12.034781 -40.236835 +v 0.200132 15.707024 -39.244591 +v 0.285053 18.414829 -35.894424 +v 0.200132 4.545812 -36.862782 +v 0.170088 6.808916 -39.080044 +v 0.170088 9.315046 -39.888615 +v 0.170088 12.034781 -40.236835 +v 0.200132 4.545812 -36.862782 +v 11.142525 -2.881190 9.001891 +v 11.236621 -3.140807 14.369819 +v 11.364158 -4.232666 14.326954 +v 11.236621 -3.973048 8.959024 +v 11.062758 -1.892203 9.450106 +v 11.142528 -2.112300 14.000815 +v 11.551121 -6.327328 12.090542 +v 11.529890 -6.275688 11.022794 +v 11.009460 -1.156657 10.235428 +v 11.062758 -1.303718 13.276114 +v 10.990744 -0.786530 11.238302 +v 11.009460 -0.838170 12.306050 +v 9.946635 -0.930319 12.320226 +v 9.927916 -0.878675 11.252476 +v 9.946632 -1.248802 10.249601 +v 9.999933 -1.395863 13.290288 +v 10.079700 -2.204439 14.014987 +v 9.999930 -1.984348 9.464279 +v 10.079700 -2.973335 9.016065 +v 10.173796 -3.232952 14.383994 +v 10.347653 -5.902290 10.066901 +v 10.324265 -6.049350 13.107590 +v 10.324265 -6.367838 11.036967 +v 10.345494 -6.419479 12.104715 +v -0.137555 16.032167 23.133743 +v -0.137555 13.788481 23.542515 +v -0.137555 16.042728 21.288063 +v -0.137555 13.092350 21.825588 +v 1.759172 18.592596 17.282019 +v 1.759172 9.582694 18.923504 +v -0.137555 18.963770 17.214392 +v -0.137555 9.211522 18.991127 +v -0.137555 10.607282 -20.931702 +v -0.137555 9.192825 -28.695456 +v 5.849846 25.098688 -2.843851 +v 5.429670 25.412260 -1.122682 +v 6.450380 10.797626 7.312677 +v 6.993171 13.482239 6.823577 +v 5.595087 13.518061 6.817050 +v 5.158012 11.356320 7.210891 +v 4.904636 8.521720 7.727320 +v 6.450380 10.797626 7.312677 +v 5.158012 11.356320 7.210891 +v 3.913326 9.523684 7.544775 +v 6.993171 13.482239 6.823577 +v 6.450378 16.166853 6.334475 +v 5.158011 15.679805 6.423209 +v 5.595087 13.518061 6.817050 +v 2.591268 7.001008 8.004374 +v 4.904636 8.521720 7.727320 +v 3.913326 9.523684 7.544775 +v 2.050524 8.299153 7.767869 +v 4.904634 19.031322 5.812605 +v 2.591266 20.552032 5.535551 +v 2.050523 19.210899 5.779888 +v 3.913326 17.986372 6.002982 +v 6.450378 16.166853 6.334475 +v 4.904634 19.031322 5.812605 +v 3.913326 17.986372 6.002982 +v 5.158011 15.679805 6.423209 +v 2.591266 20.552032 5.535551 +v -0.137555 21.086037 5.438262 +v -0.137555 19.639086 5.701879 +v 2.050523 19.210899 5.779888 +v 0.715733 14.051090 23.425133 +v 0.663374 14.114862 23.483055 +v -0.137555 13.788481 23.542515 +v -0.137555 13.703371 23.488483 +v 0.715733 13.707285 21.538033 +v 0.715733 14.051090 23.425133 +v -0.137555 13.703371 23.488483 +v -0.137555 13.359564 21.601383 +v -0.137555 6.467003 8.101662 +v 2.591268 7.001008 8.004374 +v 2.050524 8.299153 7.767869 +v -0.137555 7.870969 7.845878 +v -4.748156 7.797970 2.380435 +v -6.161592 9.962349 1.986112 +v -6.657920 12.515405 1.520978 +v -2.632802 6.351778 2.643912 +v -0.137555 5.843942 2.736434 +v -6.161589 15.068464 1.055844 +v -0.137555 17.229437 5.018654 +v -0.137555 15.705820 -3.344262 +v -2.681505 17.006306 3.793933 +v -3.735224 16.467627 0.837195 +v -3.735223 18.497147 0.467442 +v -2.755446 19.942028 3.044717 +v -0.137555 17.229437 5.018654 +v -2.681505 17.006306 3.793933 +v -2.755446 19.942028 3.044717 +v -0.137555 20.817574 4.364942 +v -2.681505 15.928947 -2.119541 +v -0.137555 15.705820 -3.344262 +v -0.137555 18.750629 -3.898979 +v -2.500265 18.538774 -2.685168 +v -3.735224 16.467627 0.837195 +v -3.735223 18.497147 0.467442 +v -5.179747 8.521720 7.727320 +v -6.725490 10.797626 7.312677 +v -7.268282 13.482239 6.823577 +v -2.866378 7.001008 8.004374 +v -0.137555 6.467003 8.101662 +v -6.725487 16.166853 6.334475 +v -0.137555 8.331417 -26.440832 +v -2.820250 8.331417 -26.440832 +v -1.248774 10.132854 -26.769033 +v -0.137555 10.132854 -26.769033 +v -0.137555 5.783802 -25.976692 +v -2.820251 5.783802 -25.976692 +v -2.820250 8.331417 -26.440832 +v -0.137555 8.331417 -26.440832 +v -0.137555 5.783802 -25.976692 +v -0.137555 3.982364 -25.648491 +v -1.248775 3.982364 -25.648491 +v -2.820251 5.783802 -25.976692 +v -10.932370 27.734844 13.241050 +v -10.932370 27.150551 8.418542 +v -0.137555 27.150551 8.418542 +v -0.137555 27.734844 13.241050 +v -0.137555 28.271790 13.143222 +v -10.932370 28.271790 13.143222 +v -10.932370 27.734844 13.241050 +v -0.137555 27.734844 13.241050 +v -10.932370 26.245462 3.654478 +v -0.137555 26.248919 3.469610 +v -21.727186 27.421173 13.298194 +v -21.727186 26.836880 8.475688 +v -21.727186 27.958118 13.200370 +v -21.727186 27.421173 13.298194 +v -0.137555 27.936581 8.275336 +v -10.932370 27.936581 8.275336 +v -10.932370 28.271790 13.143222 +v -0.137555 28.271790 13.143222 +v -10.932370 26.074389 2.673605 +v -2.617644 26.107084 2.691110 +v -21.727186 25.931791 3.711624 +v -32.521996 26.785511 13.414004 +v -32.521996 26.201221 8.591496 +v -32.521996 27.322460 13.316178 +v -32.521996 26.785511 13.414004 +v -21.727186 27.622913 8.332482 +v -21.727186 27.958118 13.200370 +v -0.137555 27.034948 3.326405 +v -10.932370 27.103384 3.498175 +v -6.509318 24.407400 -5.868575 +v -6.124956 25.098688 -2.843851 +v -10.932370 25.029203 -2.931825 +v -10.932370 24.407400 -5.868575 +v -21.727186 25.760714 2.730751 +v -32.521996 25.296129 3.827433 +v -38.411888 26.311825 13.500302 +v -41.725868 25.660938 8.689931 +v -38.411888 26.848774 13.402480 +v -38.411888 26.311825 13.500302 +v -32.521996 26.987251 8.448291 +v -32.521996 27.322460 13.316178 +v -21.727186 26.789709 3.555321 +v -2.617644 26.893122 2.547905 +v -10.932370 26.917547 2.519993 +v -6.509318 24.407400 -5.868575 +v -10.932370 24.407400 -5.868575 +v -10.932370 24.911684 -5.960440 +v -6.509318 24.911684 -5.960440 +v -21.727186 25.098589 -1.065534 +v -21.727186 24.715536 -2.874681 +v -10.932370 25.412260 -1.122682 +v -32.521996 25.125055 2.846561 +v -43.316811 24.755846 3.925868 +v -38.411888 26.848774 13.402480 +v -41.725868 26.446968 8.546725 +v -41.725868 25.660938 8.689931 +v -38.411888 26.311825 13.500302 +v -41.725868 26.446968 8.546725 +v -38.411888 26.848774 13.402480 +v -32.521996 26.154055 3.671129 +v -21.727186 26.603876 2.577139 +v -6.124956 25.884718 -2.987059 +v -10.932370 25.707838 -3.055464 +v -10.932370 26.198298 -1.265890 +v -5.704780 26.198298 -1.265890 +v -21.727186 24.598013 -5.903296 +v -21.727186 24.093729 -5.811423 +v -32.521996 24.462931 -0.949727 +v -32.521996 24.079874 -2.758871 +v -43.316811 24.584768 2.944992 +v -43.316811 25.613764 3.769564 +v -43.316811 24.755846 3.925868 +v -41.725868 25.660938 8.689931 +v -43.316811 25.613764 3.769564 +v -32.521996 25.968218 2.692949 +v -21.727186 25.394167 -2.998319 +v -21.727186 25.884623 -1.208742 +v -32.521996 23.962353 -5.787486 +v -32.521996 23.458073 -5.695620 +v -43.316811 23.922642 -0.851296 +v -42.190163 23.539589 -2.660438 +v -43.316811 25.427927 2.791382 +v -43.316811 24.584768 2.944992 +v -43.316811 25.427927 2.791382 +v -32.521996 24.758509 -2.882509 +v -32.521996 25.248964 -1.092931 +v -40.361286 23.422073 -5.689055 +v -40.361286 22.917782 -5.597183 +v -43.316811 23.922642 -0.851296 +v -43.316811 24.708681 -0.994498 +v -42.190163 24.218218 -2.784075 +v -42.190163 23.539589 -2.660438 +v -42.190163 24.218218 -2.784075 +v -43.316811 24.708681 -0.994498 +v -2.617644 26.893122 2.547905 +v -0.137555 27.034948 3.326405 +v -0.137555 26.248919 3.469610 +v -2.617644 26.107084 2.691110 +v -5.704780 25.412260 -1.122682 +v -5.704780 26.198298 -1.265890 +v -6.124956 25.098688 -2.843851 +v -6.509318 24.407400 -5.868575 +v -6.509318 24.911684 -5.960440 +v -6.124956 25.884718 -2.987059 +v -21.727186 24.093729 -5.811423 +v -32.521996 23.458073 -5.695620 +v -10.932370 24.911684 -5.960440 +v -21.727186 24.598013 -5.903296 +v -40.361286 22.917782 -5.597183 +v -32.521996 23.962353 -5.787486 +v -40.361286 23.422073 -5.689055 +v -40.361286 22.917782 -5.597183 +v -40.361286 23.422073 -5.689055 +v -5.704780 25.412260 -1.122682 +v -6.509318 24.911684 -5.960440 +v -0.576148 3.263966 -5.783109 +v -32.301418 9.709085 -6.957421 +v -32.206169 10.162366 -7.040006 +v -0.472966 3.753135 -5.871772 +v -40.011227 11.274701 -7.242656 +v -39.945049 11.589635 -7.300035 +v -0.663033 6.649406 14.232206 +v -0.541272 7.228906 14.126631 +v -32.237789 13.650052 12.956777 +v -32.350616 13.113069 13.054610 +v -38.002087 14.790937 12.748921 +v -38.101227 14.319102 12.834886 +v -0.662496 3.865942 -0.760052 +v -32.360443 10.333307 -1.938074 +v -32.301418 9.709085 -6.957421 +v -0.576148 3.263966 -5.783109 +v -42.906727 12.549485 -2.430761 +v -40.011227 11.274701 -7.242656 +v -42.926029 13.378873 2.642551 +v -42.833996 13.816903 2.562746 +v -42.814693 12.987517 -2.510567 +v -42.906727 12.549485 -2.430761 +v -42.833996 13.816903 2.562746 +v -32.255753 11.746195 2.991455 +v -32.227974 10.963766 -2.052936 +v -42.814693 12.987517 -2.510567 +v -32.206169 10.162366 -7.040006 +v -0.518984 4.546322 -0.883360 +v -0.472966 3.753135 -5.871772 +v -39.945049 11.589635 -7.300035 +v -40.011227 11.274701 -7.242656 +v -42.906727 12.549485 -2.430761 +v -42.814693 12.987517 -2.510567 +v -0.774090 5.383774 9.351863 +v -32.421593 11.874181 8.170041 +v -0.597230 6.222238 9.199900 +v -32.258347 12.651127 8.028491 +v -41.423397 13.882128 7.790358 +v -38.101227 14.319102 12.834886 +v -38.002087 14.790937 12.748921 +v -41.309978 14.421938 7.692010 +v -32.421593 11.874181 8.170041 +v -32.350616 13.113069 13.054610 +v -38.101227 14.319102 12.834886 +v -41.423397 13.882128 7.790358 +v -41.309978 14.421938 7.692010 +v -38.002087 14.790937 12.748921 +v -32.237789 13.650052 12.956777 +v -32.258347 12.651127 8.028491 +v -0.715231 4.637904 4.286060 +v -32.388218 11.115736 3.106316 +v -0.571717 5.318285 4.162748 +v -42.926029 13.378873 2.642551 +v -39.945049 11.589635 -7.300035 +v -0.774090 5.383774 9.351863 +v -0.597230 6.222238 9.199900 +v -10.927002 10.839343 -20.973984 +v -5.834132 7.933621 -36.487488 +v -4.366348 8.149817 -35.663517 +v -2.564616 10.581471 -20.926994 +v -2.591609 11.808965 -21.150627 +v -0.137555 11.835061 -21.155396 +v -0.137555 10.420605 -28.919138 +v -4.412028 9.079532 -35.830589 +v -0.137555 10.607282 -20.931702 +v -0.137555 11.835061 -21.155396 +v -2.591609 11.808965 -21.150627 +v -2.564616 10.581471 -20.926994 +v -11.028844 11.399936 -21.076115 +v -10.927002 10.839343 -20.973984 +v -12.967854 11.199199 -21.039536 +v -12.856207 10.727206 -20.953552 +v -15.586095 10.176061 -23.504894 +v -16.207060 9.756166 -24.787363 +v -16.030195 9.283175 -24.701189 +v -15.416062 9.703977 -23.418880 +v -17.875292 7.780162 -32.684757 +v -17.680067 7.330409 -32.602814 +v -14.043754 7.331349 -36.377769 +v -14.182967 7.973525 -36.291916 +v -5.897515 8.862197 -36.656662 +v -5.834132 7.933621 -36.487488 +v -4.366348 8.149817 -35.663517 +v -4.412028 9.079532 -35.830589 +v -0.137555 10.420605 -28.919138 +v -0.137555 9.192825 -28.695456 +v -12.967854 11.199199 -21.039536 +v -12.856207 10.727206 -20.953552 +v -16.558538 7.700906 -35.471687 +v -16.377802 7.227387 -35.385410 +v -16.377802 7.227387 -35.385410 +v -16.558538 7.700906 -35.471687 +v -15.586095 10.176061 -23.504894 +v -12.967854 11.199199 -21.039536 +v -14.182967 7.973525 -36.291916 +v -16.558538 7.700906 -35.471687 +v -11.028844 11.399936 -21.076115 +v -5.897515 8.862197 -36.656662 +v -15.416062 9.703977 -23.418880 +v -16.030195 9.283175 -24.701189 +v -17.680067 7.330409 -32.602814 +v -16.377802 7.227387 -35.385410 +v -14.043754 7.331349 -36.377769 +v -12.856207 10.727206 -20.953552 +v -17.875292 7.780162 -32.684757 +v -16.207060 9.756166 -24.787363 +v -30.580156 12.463110 10.208496 +v -30.429491 26.773792 11.243069 +v -31.088133 26.748894 11.243511 +v -31.238810 12.438213 10.208937 +v -31.238810 12.438213 10.208937 +v -31.088133 26.748894 11.243511 +v -31.084776 26.850677 9.394487 +v -31.235453 12.540001 8.359914 +v -31.235453 12.540001 8.359914 +v -31.084776 26.850677 9.394487 +v -30.426134 26.875576 9.394049 +v -30.576811 12.564899 8.359473 +v -30.576811 12.564899 8.359473 +v -30.426134 26.875576 9.394049 +v -30.429491 26.773792 11.243069 +v -30.580156 12.463110 10.208496 +v -0.909560 18.576508 2.205827 +v -2.125126 18.576508 2.205827 +v -2.125126 20.241886 1.902417 +v -0.909560 20.241886 1.902417 +v -0.909560 18.658239 2.654441 +v -0.909560 20.323616 2.351031 +v -2.125126 20.323616 2.351031 +v -2.125126 18.658239 2.654441 +v -0.909560 18.576508 2.205827 +v -0.909560 18.658239 2.654441 +v -2.125126 18.658239 2.654441 +v -2.125126 18.576508 2.205827 +v -2.125126 18.576508 2.205827 +v -2.125126 18.658239 2.654441 +v -2.125126 20.323616 2.351031 +v -2.125126 20.241886 1.902417 +v -2.125126 20.241886 1.902417 +v -2.125126 20.323616 2.351031 +v -0.909560 20.323616 2.351031 +v -0.909560 20.241886 1.902417 +v -0.909560 20.241886 1.902417 +v -0.909560 20.323616 2.351031 +v -0.909560 18.658239 2.654441 +v -0.909560 18.576508 2.205827 +v -7.268263 13.322628 10.315155 +v -6.725470 17.393959 9.573412 +v -6.725470 16.871246 6.704314 +v -7.268263 12.799915 7.446060 +v -5.179726 19.669867 9.158772 +v -5.179726 19.147156 6.289673 +v -2.866356 21.190580 8.881716 +v -2.866356 20.667866 6.012619 +v -0.137555 21.724585 8.784430 +v -0.137555 21.201870 5.915332 +v -0.137555 6.307393 11.593240 +v -2.866358 6.841393 11.495952 +v -2.866358 6.318684 8.626856 +v -0.137555 5.784679 8.724144 +v -5.179727 8.362108 11.218898 +v -5.179727 7.839396 8.349802 +v -5.179727 8.362108 11.218898 +v -6.725470 10.638018 10.804259 +v -6.725470 10.115304 7.935161 +v -5.179727 7.839396 8.349802 +v -6.725470 10.638018 10.804259 +v -6.725470 10.115304 7.935161 +v -7.268263 13.322628 10.315155 +v -7.268263 13.581101 11.733877 +v -6.725470 16.632854 11.177889 +v -6.725470 17.393959 9.573412 +v -5.179726 18.908764 10.763247 +v -5.179726 19.669867 9.158772 +v -2.866356 20.429476 10.486194 +v -2.866356 21.190580 8.881716 +v -0.137555 20.963480 10.388906 +v -0.137555 21.724585 8.784430 +v -0.137555 6.307393 11.593240 +v -0.137555 6.565864 13.011965 +v -2.866358 7.099868 12.914675 +v -2.866358 6.841393 11.495952 +v -5.179727 8.620581 12.637621 +v -6.725470 10.896488 12.222981 +v -7.268263 13.581101 11.733877 +v -7.268263 13.763827 12.736833 +v -6.725470 16.489346 12.240279 +v -6.725470 16.632854 11.177889 +v -5.179726 18.765253 11.825639 +v -5.179726 18.908764 10.763247 +v -2.866356 20.285967 11.548583 +v -2.866356 20.429476 10.486194 +v -0.137555 20.819971 11.451295 +v -0.137555 20.963480 10.388906 +v -0.137555 6.565864 13.011965 +v -0.137555 6.748590 14.014921 +v -2.866358 7.282592 13.917631 +v -2.866358 7.099868 12.914675 +v -5.179727 8.803307 13.640577 +v -5.179727 8.620581 12.637621 +v -6.725470 11.079216 13.225934 +v -6.725470 10.896488 12.222981 +v -7.268263 13.763827 12.736833 +v -7.268263 14.166320 14.946074 +v -6.725467 16.850937 14.456967 +v -6.725470 16.489346 12.240279 +v -5.179726 19.126841 14.042328 +v -5.179726 18.765253 11.825639 +v -2.866356 20.647554 13.765274 +v -2.866356 20.285967 11.548583 +v -0.137555 21.181559 13.667984 +v -0.137555 20.819971 11.451295 +v -0.137555 6.748590 14.014921 +v -0.137555 7.151085 16.224155 +v -2.866358 7.685090 16.126867 +v -2.866358 7.282592 13.917631 +v -5.179727 9.205801 15.849814 +v -5.179727 8.803307 13.640577 +v -6.725470 11.481709 15.435171 +v -6.725470 11.079216 13.225934 +v -6.725470 16.871246 6.704314 +v -5.131141 16.032776 6.857073 +v -5.542573 12.946739 7.419309 +v -7.268263 12.799915 7.446060 +v -5.179726 19.147156 6.289673 +v -3.959480 17.757896 6.542778 +v -2.866356 20.667866 6.012619 +v -2.205964 18.910583 6.332776 +v -0.137555 21.201870 5.915332 +v -0.137555 19.315353 6.259032 +v -2.866358 6.318684 8.626856 +v -2.205966 8.034017 8.314344 +v -0.137555 7.629245 8.388088 +v -0.137555 5.784679 8.724144 +v -3.959480 9.186704 8.104342 +v -5.131142 10.911824 7.790046 +v -7.168325 14.639720 17.066553 +v -6.633142 17.286711 16.584303 +v -6.633142 16.913443 14.535474 +v -7.168325 14.266452 15.017725 +v -5.109061 19.530722 16.175474 +v -5.109061 19.157454 14.126644 +v -2.828113 21.030119 15.902303 +v -2.828115 20.656855 13.853474 +v -0.137555 21.556639 15.806378 +v -0.137555 21.183367 13.757549 +v -0.137555 7.753714 18.321089 +v -2.828115 8.280234 18.225168 +v -2.828116 7.876051 16.181973 +v -0.137555 7.349532 16.277897 +v -5.109064 9.748725 17.957628 +v -5.109062 9.375453 15.908801 +v -6.633142 11.992731 17.548800 +v -6.633142 11.619465 15.499971 +v -3.642250 10.639707 18.730930 +v -3.642249 17.535583 17.474588 +v -4.716659 15.953656 17.762800 +v -4.716660 12.221634 18.442724 +v -7.406736 17.223406 19.063496 +v -8.005659 14.261175 19.603176 +v -6.594498 14.494061 20.881462 +v -6.102993 16.925011 20.438580 +v -5.701145 19.734667 18.605974 +v -4.703304 18.985874 20.063118 +v -3.148548 21.412638 18.300266 +v -2.608520 20.362902 19.812241 +v -0.137555 22.001863 18.192917 +v -0.137555 20.846445 19.724144 +v -3.148549 7.109707 20.906082 +v -0.137555 6.520483 21.013433 +v -0.137555 8.141681 22.038780 +v -2.608521 8.625226 21.950691 +v -5.701147 8.787681 20.600376 +v -4.703304 10.002252 21.699814 +v -7.406736 11.298944 20.142857 +v -6.102996 12.063114 21.324352 +v -6.594498 14.494061 20.881462 +v -5.322203 14.494061 20.881462 +v -4.927547 16.446011 20.525845 +v -6.102993 16.925011 20.438580 +v -3.803655 18.100796 20.224361 +v -4.703304 18.985874 20.063118 +v -2.121634 19.206488 20.022923 +v -2.608520 20.362902 19.812241 +v -0.137555 19.594753 19.952187 +v -0.137555 20.846445 19.724144 +v -0.137555 8.141681 22.038780 +v -0.137555 9.393373 21.810745 +v -2.121634 9.781638 21.740005 +v -2.608521 8.625226 21.950691 +v -3.803658 10.887330 21.538559 +v -4.703304 10.002252 21.699814 +v -4.927547 12.542114 21.237080 +v -6.102996 12.063114 21.324352 +v -5.093940 14.087643 18.102762 +v -4.716659 15.953656 17.762800 +v -4.927547 16.446011 20.525845 +v -5.322203 14.494061 20.881462 +v -3.642249 17.535583 17.474588 +v -3.803655 18.100796 20.224361 +v -2.034281 18.592596 17.282019 +v -2.121634 19.206488 20.022923 +v -0.137555 18.963770 17.214392 +v -0.137555 19.594753 19.952187 +v -0.137555 9.211522 18.991127 +v -2.034282 9.582694 18.923504 +v -2.121634 9.781638 21.740005 +v -0.137555 9.393373 21.810745 +v -3.642250 10.639707 18.730930 +v -3.803658 10.887330 21.538559 +v -4.716660 12.221634 18.442724 +v -4.927547 12.542114 21.237080 +v -7.168325 14.639720 17.066553 +v -7.929808 14.096279 18.195410 +v -7.336660 17.029957 17.660933 +v -6.633142 17.286711 16.584303 +v -5.647511 19.517012 17.207823 +v -5.109061 19.530722 16.175474 +v -3.119521 21.178797 16.905069 +v -2.828113 21.030119 15.902303 +v -0.137555 21.762342 16.798756 +v -0.137555 21.556639 15.806378 +v -0.137555 7.753714 18.321089 +v -0.137555 6.430205 19.592070 +v -3.119522 7.013754 19.485758 +v -2.828115 8.280234 18.225168 +v -5.647511 8.675551 19.182995 +v -5.109064 9.748725 17.957628 +v -7.336660 11.162606 18.729893 +v -6.633142 11.992731 17.548800 +v -7.929808 14.096279 18.195410 +v -8.005659 14.261175 19.603176 +v -7.406736 17.223406 19.063496 +v -7.336660 17.029957 17.660933 +v -5.701145 19.734667 18.605974 +v -5.647511 19.517012 17.207823 +v -3.148548 21.412638 18.300266 +v -3.119521 21.178797 16.905069 +v -0.137555 22.001863 18.192917 +v -0.137555 21.762342 16.798756 +v -0.137555 6.430205 19.592070 +v -0.137555 6.520483 21.013433 +v -3.148549 7.109707 20.906082 +v -3.119522 7.013754 19.485758 +v -5.701147 8.787681 20.600376 +v -5.647511 8.675551 19.182995 +v -7.406736 11.298944 20.142857 +v -7.336660 11.162606 18.729893 +v -10.448902 -4.065199 8.973198 +v -10.464485 -4.324817 14.341125 +v -10.622765 -5.313800 13.892914 +v -10.464482 -5.093708 9.342202 +v -11.024202 -4.127771 8.234436 +v -10.888246 -2.555629 8.260398 +v -10.177548 -2.803895 8.298583 +v -10.297066 -4.190809 8.244131 +v -10.772985 -1.142371 8.805583 +v -10.076224 -1.547646 8.867919 +v -10.695973 -0.103137 9.786989 +v -10.008523 -0.613330 9.865463 +v -10.668931 0.403852 11.055208 +v -9.984747 -0.143181 11.139349 +v -10.695973 0.301416 12.417167 +v -10.008523 -0.208778 12.495639 +v -10.772990 -0.394859 13.665511 +v -9.998027 -0.800133 13.727847 +v -10.888248 -1.578959 14.610202 +v -10.099266 -1.827220 14.648387 +v -11.024208 -3.070621 15.107410 +v -10.218687 -3.133664 15.117107 +v -11.160166 -4.642758 15.081449 +v -10.338077 -4.520590 15.062659 +v -11.279265 -6.056028 14.536266 +v -10.419976 -5.776834 14.493324 +v -11.279265 -6.056028 14.536266 +v -11.366625 -6.927130 13.528999 +v -10.496778 -6.653613 13.486927 +v -10.419976 -5.776834 14.493324 +v -11.397299 -7.346391 12.247284 +v -10.523744 -7.046638 12.201180 +v -11.366625 -7.331683 10.898822 +v -10.496775 -7.058166 10.856751 +v -11.279260 -6.803540 9.676335 +v -10.419973 -6.524346 9.633391 +v -11.160163 -5.619434 8.731646 +v -10.338074 -5.497261 8.712852 +v -11.685589 -5.810138 10.052730 +v -11.804996 -5.957200 13.093418 +v -11.685592 -5.221654 13.878742 +v -11.639263 -5.001564 9.328028 +v -10.177548 -2.803895 8.298583 +v -10.202976 -2.986496 9.018088 +v -10.297069 -4.078360 8.975224 +v -10.297066 -4.190809 8.244131 +v -10.297066 -4.190809 8.244131 +v -10.297069 -4.078360 8.975224 +v -10.312647 -5.106868 9.344226 +v -10.338074 -5.497261 8.712852 +v -10.338074 -5.497261 8.712852 +v -10.312647 -5.106868 9.344226 +v -10.470928 -5.915451 10.068927 +v -10.419973 -6.524346 9.633391 +v -10.419973 -6.524346 9.633391 +v -10.470928 -5.915451 10.068927 +v -10.427139 -6.380999 11.038991 +v -10.496775 -7.058166 10.856751 +v -10.496775 -7.058166 10.856751 +v -10.427139 -6.380999 11.038991 +v -10.448370 -6.432639 12.106741 +v -10.523744 -7.046638 12.201180 +v -10.523744 -7.046638 12.201180 +v -10.448370 -6.432639 12.106741 +v -10.427139 -6.062517 13.109615 +v -10.496778 -6.653613 13.486927 +v -10.496778 -6.653613 13.486927 +v -10.427139 -6.062517 13.109615 +v -10.470931 -5.326966 13.894939 +v -10.419976 -5.776834 14.493324 +v -10.419976 -5.776834 14.493324 +v -10.470931 -5.326966 13.894939 +v -10.312652 -4.337977 14.343151 +v -10.338077 -4.520590 15.062659 +v -10.338077 -4.520590 15.062659 +v -10.312652 -4.337977 14.343151 +v -10.297069 -3.246119 14.386018 +v -10.218687 -3.133664 15.117107 +v -10.218687 -3.133664 15.117107 +v -10.297069 -3.246119 14.386018 +v -10.202979 -2.217605 14.017013 +v -10.099266 -1.827220 14.648387 +v -10.099266 -1.827220 14.648387 +v -10.202979 -2.217605 14.017013 +v -10.123209 -1.409030 13.292312 +v -9.998027 -0.800133 13.727847 +v -9.998027 -0.800133 13.727847 +v -10.123209 -1.409030 13.292312 +v -10.069911 -0.943481 12.322249 +v -10.008523 -0.208778 12.495639 +v -10.008523 -0.208778 12.495639 +v -10.069911 -0.943481 12.322249 +v -10.051191 -0.891838 11.254501 +v -9.984747 -0.143181 11.139349 +v -9.984747 -0.143181 11.139349 +v -10.051191 -0.891838 11.254501 +v -10.069908 -1.261969 10.251627 +v -10.008523 -0.613330 9.865463 +v -10.008523 -0.613330 9.865463 +v -10.069908 -1.261969 10.251627 +v -10.123206 -1.997514 9.466303 +v -10.076224 -1.547646 8.867919 +v -10.076224 -1.547646 8.867919 +v -10.123206 -1.997514 9.466303 +v -10.202976 -2.986496 9.018088 +v -10.177548 -2.803895 8.298583 +v -11.663559 -4.072338 8.225910 +v -11.663124 -3.954838 8.957199 +v -11.569467 -2.868025 8.999867 +v -11.544039 -2.685418 8.280360 +v -11.544039 -2.685418 8.280360 +v -11.569467 -2.868025 8.999867 +v -11.489699 -1.879037 9.448082 +v -11.442715 -1.429169 8.849696 +v -11.442715 -1.429169 8.849696 +v -11.489699 -1.879037 9.448082 +v -11.436399 -1.143492 10.233404 +v -11.375010 -0.494853 9.847242 +v -11.375010 -0.494853 9.847242 +v -11.436399 -1.143492 10.233404 +v -11.417682 -0.773364 11.236277 +v -11.351240 -0.024707 11.121126 +v -11.351240 -0.024707 11.121126 +v -11.417682 -0.773364 11.236277 +v -11.436399 -0.825004 12.304027 +v -11.375016 -0.090303 12.477416 +v -11.375016 -0.090303 12.477416 +v -11.436399 -0.825004 12.304027 +v -11.489699 -1.290552 13.274091 +v -11.442718 -0.681659 13.709624 +v -11.442718 -0.681659 13.709624 +v -11.489699 -1.290552 13.274091 +v -11.569469 -2.099133 13.998790 +v -11.544042 -1.708743 14.630164 +v -11.544042 -1.708743 14.630164 +v -11.569469 -2.099133 13.998790 +v -11.663559 -3.127642 14.367793 +v -11.663562 -3.015193 15.098885 +v -11.663562 -3.015193 15.098885 +v -11.663559 -3.127642 14.367793 +v -11.791100 -4.219506 14.324928 +v -11.816527 -4.402113 15.044438 +v -11.816527 -4.402113 15.044438 +v -11.791100 -4.219506 14.324928 +v -11.837422 -5.208493 13.876717 +v -11.970063 -5.658357 14.475101 +v -11.970063 -5.658357 14.475101 +v -11.837422 -5.208493 13.876717 +v -11.977229 -5.944039 13.091393 +v -12.046864 -6.569247 13.473953 +v -12.046864 -6.569247 13.473953 +v -11.977229 -5.944039 13.091393 +v -11.998460 -6.314167 12.088518 +v -12.073831 -6.962277 12.188204 +v -12.073831 -6.962277 12.188204 +v -11.998460 -6.314167 12.088518 +v -11.977229 -6.262527 11.020768 +v -12.046861 -6.973800 10.843776 +v -12.046861 -6.973800 10.843776 +v -11.977229 -6.262527 11.020768 +v -11.837420 -5.796978 10.050706 +v -11.970063 -6.405869 9.615170 +v -11.970063 -6.405869 9.615170 +v -11.837420 -5.796978 10.050706 +v -11.791097 -4.988397 9.326004 +v -11.816525 -5.378782 8.694632 +v -11.816525 -5.378782 8.694632 +v -11.791097 -4.988397 9.326004 +v -11.663124 -3.954838 8.957199 +v -11.663559 -4.072338 8.225910 +v -10.202976 -2.986496 9.018088 +v -10.354806 -2.973335 9.016065 +v -10.448902 -4.065199 8.973198 +v -10.297069 -4.078360 8.975224 +v -10.297069 -4.078360 8.975224 +v -10.448902 -4.065199 8.973198 +v -10.464482 -5.093708 9.342202 +v -10.312647 -5.106868 9.344226 +v -10.312647 -5.106868 9.344226 +v -10.464482 -5.093708 9.342202 +v -10.622762 -5.902290 10.066901 +v -10.470928 -5.915451 10.068927 +v -10.470928 -5.915451 10.068927 +v -10.622762 -5.902290 10.066901 +v -10.599370 -6.367838 11.036967 +v -10.427139 -6.380999 11.038991 +v -10.427139 -6.380999 11.038991 +v -10.599370 -6.367838 11.036967 +v -10.620604 -6.419479 12.104715 +v -10.448370 -6.432639 12.106741 +v -10.448370 -6.432639 12.106741 +v -10.620604 -6.419479 12.104715 +v -10.599373 -6.049350 13.107590 +v -10.427139 -6.062517 13.109615 +v -10.427139 -6.062517 13.109615 +v -10.599373 -6.049350 13.107590 +v -10.622765 -5.313800 13.892914 +v -10.470931 -5.326966 13.894939 +v -10.470931 -5.326966 13.894939 +v -10.622765 -5.313800 13.892914 +v -10.464485 -4.324817 14.341125 +v -10.312652 -4.337977 14.343151 +v -10.312652 -4.337977 14.343151 +v -10.464485 -4.324817 14.341125 +v -10.448902 -3.232952 14.383994 +v -10.297069 -3.246119 14.386018 +v -10.297069 -3.246119 14.386018 +v -10.448902 -3.232952 14.383994 +v -10.354809 -2.204439 14.014987 +v -10.202979 -2.217605 14.017013 +v -10.202979 -2.217605 14.017013 +v -10.354809 -2.204439 14.014987 +v -10.275043 -1.395863 13.290288 +v -10.123209 -1.409030 13.292312 +v -10.123209 -1.409030 13.292312 +v -10.275043 -1.395863 13.290288 +v -10.221741 -0.930319 12.320226 +v -10.069911 -0.943481 12.322249 +v -10.069911 -0.943481 12.322249 +v -10.221741 -0.930319 12.320226 +v -10.203024 -0.878675 11.252476 +v -10.051191 -0.891838 11.254501 +v -10.051191 -0.891838 11.254501 +v -10.203024 -0.878675 11.252476 +v -10.221741 -1.248802 10.249601 +v -10.069908 -1.261969 10.251627 +v -10.069908 -1.261969 10.251627 +v -10.221741 -1.248802 10.249601 +v -10.275040 -1.984348 9.464279 +v -10.123206 -1.997514 9.466303 +v -10.123206 -1.997514 9.466303 +v -10.275040 -1.984348 9.464279 +v -10.354806 -2.973335 9.016065 +v -10.202976 -2.986496 9.018088 +v -11.663124 -3.954838 8.957199 +v -11.511726 -3.973048 8.959024 +v -11.417636 -2.881190 9.001891 +v -11.569467 -2.868025 8.999867 +v -11.569467 -2.868025 8.999867 +v -11.417636 -2.881190 9.001891 +v -11.337867 -1.892203 9.450106 +v -11.489699 -1.879037 9.448082 +v -11.489699 -1.879037 9.448082 +v -11.337867 -1.892203 9.450106 +v -11.284566 -1.156657 10.235428 +v -11.436399 -1.143492 10.233404 +v -11.436399 -1.143492 10.233404 +v -11.284566 -1.156657 10.235428 +v -11.265852 -0.786530 11.238302 +v -11.417682 -0.773364 11.236277 +v -11.417682 -0.773364 11.236277 +v -11.265852 -0.786530 11.238302 +v -11.284569 -0.838170 12.306050 +v -11.436399 -0.825004 12.304027 +v -11.436399 -0.825004 12.304027 +v -11.284569 -0.838170 12.306050 +v -11.337867 -1.303718 13.276114 +v -11.489699 -1.290552 13.274091 +v -11.489699 -1.290552 13.274091 +v -11.337867 -1.303718 13.276114 +v -11.417636 -2.112300 14.000815 +v -11.569469 -2.099133 13.998790 +v -11.569469 -2.099133 13.998790 +v -11.417636 -2.112300 14.000815 +v -11.511729 -3.140807 14.369819 +v -11.663559 -3.127642 14.367793 +v -11.663559 -3.127642 14.367793 +v -11.511729 -3.140807 14.369819 +v -11.639269 -4.232666 14.326954 +v -11.791100 -4.219506 14.324928 +v -11.791100 -4.219506 14.324928 +v -11.639269 -4.232666 14.326954 +v -11.685592 -5.221654 13.878742 +v -11.837422 -5.208493 13.876717 +v -11.837422 -5.208493 13.876717 +v -11.685592 -5.221654 13.878742 +v -11.804996 -5.957200 13.093418 +v -11.977229 -5.944039 13.091393 +v -11.977229 -5.944039 13.091393 +v -11.804996 -5.957200 13.093418 +v -11.826226 -6.327328 12.090542 +v -11.998460 -6.314167 12.088518 +v -11.998460 -6.314167 12.088518 +v -11.826226 -6.327328 12.090542 +v -11.804996 -6.275688 11.022794 +v -11.977229 -6.262527 11.020768 +v -11.977229 -6.262527 11.020768 +v -11.804996 -6.275688 11.022794 +v -11.685589 -5.810138 10.052730 +v -11.837420 -5.796978 10.050706 +v -11.837420 -5.796978 10.050706 +v -11.685589 -5.810138 10.052730 +v -11.639263 -5.001564 9.328028 +v -11.791097 -4.988397 9.326004 +v -11.791097 -4.988397 9.326004 +v -11.639263 -5.001564 9.328028 +v -11.511726 -3.973048 8.959024 +v -11.663124 -3.954838 8.957199 +v -11.663559 -4.072338 8.225910 +v -11.544039 -2.685418 8.280360 +v -11.442715 -1.429169 8.849696 +v -11.375010 -0.494853 9.847242 +v -11.351240 -0.024707 11.121126 +v -11.375016 -0.090303 12.477416 +v -11.442718 -0.681659 13.709624 +v -11.544042 -1.708743 14.630164 +v -11.663562 -3.015193 15.098885 +v -11.816527 -4.402113 15.044438 +v -11.970063 -5.658357 14.475101 +v -11.970063 -5.658357 14.475101 +v -12.046864 -6.569247 13.473953 +v -12.073831 -6.962277 12.188204 +v -12.046861 -6.973800 10.843776 +v -11.970063 -6.405869 9.615170 +v -11.816525 -5.378782 8.694632 +v -3.569041 14.261284 19.875835 +v -3.109313 15.949227 19.568314 +v -3.109322 15.910932 19.358097 +v -3.569041 14.222986 19.665619 +v -1.853298 17.184916 19.343189 +v -1.853298 17.146614 19.132973 +v -0.137555 17.637207 19.260788 +v -0.137555 17.598896 19.050575 +v -0.137555 10.885364 20.490883 +v -1.853296 11.337653 20.408482 +v -1.853296 11.299354 20.198269 +v -0.137555 10.847074 20.280666 +v -3.109308 12.573324 20.183361 +v -3.109308 12.535027 19.973148 +v -3.109308 12.573324 20.183361 +v -3.569041 14.261284 19.875835 +v -3.569041 14.222986 19.665619 +v -3.109308 12.535027 19.973148 +v -0.887290 13.289986 21.789577 +v -0.887291 15.845090 21.324074 +v -2.597726 15.861096 20.732384 +v -2.978316 14.463718 20.986965 +v -2.229474 14.552227 21.472775 +v -1.949210 15.581246 21.285297 +v -1.557935 16.884050 20.546009 +v -1.183514 16.334541 21.148060 +v -0.137555 17.258476 20.477798 +v -0.137555 16.610268 21.097824 +v -1.557934 12.043385 21.427917 +v -0.137555 11.668958 21.496132 +v -0.137555 12.494183 21.847721 +v -1.183513 12.769909 21.797489 +v -2.597726 13.066336 21.241550 +v -1.949209 13.523205 21.660248 +v -2.229474 14.552227 21.472775 +v -1.795793 14.552227 21.472775 +v -1.573631 15.367919 21.324165 +v -1.949210 15.581246 21.285297 +v -1.949210 15.581246 21.285297 +v -1.573631 15.367919 21.324165 +v -0.966673 15.965047 21.215378 +v -1.183514 16.334541 21.148060 +v -1.183514 16.334541 21.148060 +v -0.966673 15.965047 21.215378 +v -0.137555 16.183609 21.175556 +v -0.137555 16.610268 21.097824 +v -0.137555 12.494183 21.847721 +v -0.137555 12.920841 21.769989 +v -0.966673 13.139404 21.730171 +v -1.183513 12.769909 21.797489 +v -1.183513 12.769909 21.797489 +v -0.966673 13.139404 21.730171 +v -1.573630 13.736532 21.621380 +v -1.949209 13.523205 21.660248 +v -1.949209 13.523205 21.660248 +v -1.573630 13.736532 21.621380 +v -1.795793 14.552227 21.472775 +v -2.229474 14.552227 21.472775 +v -1.795793 14.507906 21.229500 +v -1.637027 14.507906 21.229500 +v -1.436136 15.245500 21.095123 +v -1.573631 15.323596 21.080891 +v -1.573631 15.323596 21.080891 +v -1.436136 15.245500 21.095123 +v -0.887291 15.785457 20.996750 +v -0.966673 15.920726 20.972099 +v -0.966673 15.920726 20.972099 +v -0.887291 15.785457 20.996750 +v -0.137555 15.983094 20.960735 +v -0.137555 16.139288 20.932285 +v -0.137555 12.876520 21.526718 +v -0.137555 13.032715 21.498264 +v -0.887290 13.230352 21.462250 +v -0.966673 13.095083 21.486900 +v -0.966673 13.095083 21.486900 +v -0.887290 13.230352 21.462250 +v -1.436135 13.770308 21.363880 +v -1.573630 13.692211 21.378109 +v -1.573630 13.692211 21.378109 +v -1.436135 13.770308 21.363880 +v -1.637027 14.507906 21.229500 +v -1.795793 14.507906 21.229500 +v -1.795793 14.552227 21.472775 +v -1.795793 14.507906 21.229500 +v -1.573631 15.323596 21.080891 +v -1.573631 15.367919 21.324165 +v -1.573631 15.367919 21.324165 +v -1.573631 15.323596 21.080891 +v -0.966673 15.920726 20.972099 +v -0.966673 15.965047 21.215378 +v -0.966673 15.965047 21.215378 +v -0.966673 15.920726 20.972099 +v -0.137555 16.139288 20.932285 +v -0.137555 16.183609 21.175556 +v -0.137555 12.920841 21.769989 +v -0.137555 12.876520 21.526718 +v -0.966673 13.095083 21.486900 +v -0.966673 13.139404 21.730171 +v -0.966673 13.139404 21.730171 +v -0.966673 13.095083 21.486900 +v -1.573630 13.692211 21.378109 +v -1.573630 13.736532 21.621380 +v -1.573630 13.736532 21.621380 +v -1.573630 13.692211 21.378109 +v -1.795793 14.507906 21.229500 +v -1.795793 14.552227 21.472775 +v -1.436136 15.305133 21.422447 +v -1.436136 15.245500 21.095123 +v -1.637027 14.507906 21.229500 +v -1.637027 14.567539 21.556824 +v -1.637027 14.567539 21.556824 +v -1.637027 14.507906 21.229500 +v -1.436135 13.770308 21.363880 +v -1.436135 13.829943 21.691204 +v -1.436135 13.829943 21.691204 +v -1.436135 13.770308 21.363880 +v -0.887290 13.230352 21.462250 +v -0.887290 13.289986 21.789577 +v -0.887290 13.289986 21.789577 +v -0.887290 13.230352 21.462250 +v -0.137555 13.032715 21.498264 +v -0.137555 13.092350 21.825588 +v -0.137555 16.042728 21.288063 +v -0.137555 15.983094 20.960735 +v -0.887291 15.785457 20.996750 +v -0.887291 15.845090 21.324074 +v -0.887291 15.845090 21.324074 +v -0.887291 15.785457 20.996750 +v -1.436136 15.245500 21.095123 +v -1.436136 15.305133 21.422447 +v -2.978316 14.261284 19.875835 +v -2.978316 14.463718 20.986965 +v -2.597726 15.861096 20.732384 +v -2.597726 15.658666 19.621250 +v -1.557935 16.884050 20.546009 +v -1.557935 16.681616 19.434883 +v -0.137555 17.258476 20.477798 +v -0.137555 17.056042 19.366665 +v -0.137555 11.466525 20.385002 +v -0.137555 11.668958 21.496132 +v -1.557934 12.043385 21.427917 +v -1.557934 11.840951 20.316792 +v -2.597726 13.066336 21.241550 +v -2.597726 12.863903 20.130421 +v -2.597726 12.863903 20.130421 +v -2.597726 13.066336 21.241550 +v -2.978316 14.463718 20.986965 +v -2.978316 14.261284 19.875835 +v -2.597726 15.658666 19.621250 +v -3.109313 15.949227 19.568314 +v -3.569041 14.261284 19.875835 +v -2.978316 14.261284 19.875835 +v -2.978316 14.261284 19.875835 +v -3.569041 14.261284 19.875835 +v -3.109308 12.573324 20.183361 +v -2.597726 12.863903 20.130421 +v -2.597726 12.863903 20.130421 +v -3.109308 12.573324 20.183361 +v -1.853296 11.337653 20.408482 +v -1.557934 11.840951 20.316792 +v -1.557934 11.840951 20.316792 +v -1.853296 11.337653 20.408482 +v -0.137555 10.885364 20.490883 +v -0.137555 11.466525 20.385002 +v -0.137555 17.056042 19.366665 +v -0.137555 17.637207 19.260788 +v -1.853298 17.184916 19.343189 +v -1.557935 16.681616 19.434883 +v -1.557935 16.681616 19.434883 +v -1.853298 17.184916 19.343189 +v -3.109313 15.949227 19.568314 +v -2.597726 15.658666 19.621250 +v -0.938484 14.114862 23.483055 +v -1.273397 14.910323 23.338135 +v -1.347445 14.898061 23.270828 +v -0.990843 14.051090 23.425133 +v -0.137555 16.032167 23.133743 +v -0.137555 16.092754 23.053173 +v -0.990843 15.745030 23.116516 +v -0.938484 15.705781 23.193211 +v -1.273397 14.910323 23.338135 +v -0.938484 15.705781 23.193211 +v -0.990843 15.745030 23.116516 +v -1.347445 14.898061 23.270828 +v -0.990843 13.707285 21.538033 +v -0.990843 14.051090 23.425133 +v -1.347445 14.898061 23.270828 +v -1.347445 14.554255 21.383720 +v -0.990843 15.745030 23.116516 +v -0.137555 16.092754 23.053173 +v -0.137555 15.748948 21.166065 +v -0.990843 15.401227 21.229416 +v -1.347445 14.554255 21.383720 +v -1.347445 14.898061 23.270828 +v -0.990843 15.745030 23.116516 +v -0.990843 15.401227 21.229416 +v -7.052857 -3.374685 12.138167 +v -7.607366 -2.982224 11.851208 +v -2.921354 7.840734 14.815410 +v -2.367431 7.513035 15.175448 +v -7.663925 -3.256060 12.533605 +v -2.979387 7.707127 15.538341 +v -7.052857 -3.374685 12.138167 +v -2.367431 7.513035 15.175448 +v -6.429377 -3.665658 11.352397 +v -6.978294 -3.810490 10.881698 +v -2.288786 4.917661 2.296532 +v -1.736506 4.755218 2.757458 +v -7.044255 -3.258756 11.366552 +v -2.350004 5.073448 3.014745 +v -6.429377 -3.665658 11.352397 +v -1.736506 4.755218 2.757458 +v -0.137555 -3.498145 12.355363 +v -9.023540 -3.498145 12.355365 +v -9.023540 -4.092004 12.209988 +v -0.137555 -4.092004 12.209984 +v -9.023540 -4.409125 11.687264 +v -0.137555 -4.409125 11.687263 +v -9.023540 -4.263747 11.093406 +v -0.137555 -4.263747 11.093404 +v -9.023540 -3.741025 10.776284 +v -0.137555 -3.741025 10.776280 +v -9.023540 -3.147167 10.921661 +v -0.137555 -3.147167 10.921659 +v -0.137555 -3.147167 10.921659 +v -9.023540 -3.147167 10.921661 +v -9.023540 -2.830044 11.444382 +v -0.137555 -2.830044 11.444382 +v -9.023540 -2.975422 12.038241 +v -0.137555 -2.975422 12.038241 +v -9.023540 -3.498145 12.355365 +v -9.023540 -3.533713 12.124108 +v -9.023540 -3.953632 12.021311 +v -9.023540 -4.092004 12.209988 +v -9.023540 -4.177870 11.651694 +v -9.023540 -4.409125 11.687264 +v -9.023540 -4.075072 11.231778 +v -9.023540 -4.263747 11.093406 +v -9.023540 -3.705457 11.007539 +v -9.023540 -3.741025 10.776284 +v -9.023540 -3.285537 11.110336 +v -9.023540 -3.147167 10.921661 +v -9.023540 -3.147167 10.921661 +v -9.023540 -3.285537 11.110336 +v -9.023540 -3.061300 11.479952 +v -9.023540 -2.830044 11.444382 +v -9.023540 -3.164097 11.899868 +v -9.023540 -2.975422 12.038241 +v -9.881022 -3.541481 12.125302 +v -9.918426 -3.959666 12.022240 +v -9.023540 -3.953632 12.021311 +v -9.023540 -3.533713 12.124108 +v -9.918426 -3.959666 12.022240 +v -9.933917 -4.183187 11.652513 +v -9.023540 -4.177870 11.651694 +v -9.023540 -3.953632 12.021311 +v -9.933917 -4.183187 11.652513 +v -9.918426 -4.081106 11.232705 +v -9.023540 -4.075072 11.231778 +v -9.023540 -4.177870 11.651694 +v -9.918426 -4.081106 11.232705 +v -9.881022 -3.713226 11.008735 +v -9.023540 -3.705457 11.007539 +v -9.023540 -4.075072 11.231778 +v -9.881022 -3.713226 11.008735 +v -9.843617 -3.295041 11.111799 +v -9.023540 -3.285537 11.110336 +v -9.023540 -3.705457 11.007539 +v -9.843617 -3.295041 11.111799 +v -9.828129 -3.071526 11.481525 +v -9.023540 -3.061300 11.479952 +v -9.023540 -3.285537 11.110336 +v -9.828129 -3.071526 11.481525 +v -9.843620 -3.173601 11.901333 +v -9.023540 -3.164097 11.899868 +v -9.023540 -3.061300 11.479952 +v -9.843620 -3.173601 11.901333 +v -9.881022 -3.541481 12.125302 +v -9.023540 -3.533713 12.124108 +v -9.023540 -3.164097 11.899868 +v -9.881022 -3.355837 13.332259 +v -10.215263 -3.324768 13.327479 +v -10.333525 -4.646990 13.001652 +v -9.999287 -4.678094 13.006378 +v -9.999287 -4.678094 13.006378 +v -10.333525 -4.646990 13.001652 +v -10.382511 -5.353782 11.832620 +v -10.048271 -5.384840 11.837338 +v -10.048271 -5.384840 11.837338 +v -10.382511 -5.353782 11.832620 +v -10.333525 -5.031002 10.505172 +v -9.999284 -5.062070 10.509952 +v -9.999284 -5.062070 10.509952 +v -10.333525 -5.031002 10.505172 +v -10.215261 -3.867794 9.797000 +v -9.881022 -3.898864 9.801778 +v -9.881022 -3.898864 9.801778 +v -10.215261 -3.867794 9.797000 +v -10.096996 -2.545539 10.122879 +v -9.762759 -2.576614 10.127659 +v -9.762759 -2.576614 10.127659 +v -10.096996 -2.545539 10.122879 +v -10.048013 -1.838800 11.291918 +v -9.713772 -1.869869 11.296698 +v -9.713772 -1.869869 11.296698 +v -10.048013 -1.838800 11.291918 +v -10.096999 -2.161563 12.619308 +v -9.762759 -2.192633 12.624087 +v -9.762759 -2.192633 12.624087 +v -10.096999 -2.161563 12.619308 +v -10.215263 -3.324768 13.327479 +v -9.881022 -3.355837 13.332259 +v -9.881022 -3.541481 12.125302 +v -9.881022 -3.355837 13.332259 +v -9.999287 -4.678094 13.006378 +v -9.918426 -3.959666 12.022240 +v -9.918426 -3.959666 12.022240 +v -9.999287 -4.678094 13.006378 +v -10.048271 -5.384840 11.837338 +v -9.933917 -4.183187 11.652513 +v -9.933917 -4.183187 11.652513 +v -10.048271 -5.384840 11.837338 +v -9.999284 -5.062070 10.509952 +v -9.918426 -4.081106 11.232705 +v -9.918426 -4.081106 11.232705 +v -9.999284 -5.062070 10.509952 +v -9.881022 -3.898864 9.801778 +v -9.881022 -3.713226 11.008735 +v -9.881022 -3.713226 11.008735 +v -9.881022 -3.898864 9.801778 +v -9.762759 -2.576614 10.127659 +v -9.843617 -3.295041 11.111799 +v -9.843617 -3.295041 11.111799 +v -9.762759 -2.576614 10.127659 +v -9.713772 -1.869869 11.296698 +v -9.828129 -3.071526 11.481525 +v -9.828129 -3.071526 11.481525 +v -9.713772 -1.869869 11.296698 +v -9.762759 -2.192633 12.624087 +v -9.843620 -3.173601 11.901333 +v -9.843620 -3.173601 11.901333 +v -9.762759 -2.192633 12.624087 +v -9.881022 -3.355837 13.332259 +v -9.881022 -3.541481 12.125302 +v -34.616352 10.973569 -4.305973 +v -34.616352 24.350153 -3.728268 +v -34.519024 24.352579 -3.784413 +v -34.519024 10.975993 -4.362118 +v -34.519024 24.347727 -3.672129 +v -34.519024 10.971145 -4.249835 +v -34.519024 10.971145 -4.249835 +v -34.519024 24.347727 -3.672129 +v -30.701521 12.832979 10.128780 +v -13.333050 27.553009 10.638275 +v -13.037113 27.443340 10.522055 +v -30.393160 12.800472 9.998507 +v -30.393160 12.800472 9.998507 +v -13.037113 27.443340 10.522055 +v -13.087281 27.472626 10.853327 +v -30.433870 12.818146 10.331888 +v -6.012183 7.860677 11.055306 +v -30.466719 26.743053 10.866119 +v -30.166159 26.787380 10.721846 +v -5.748137 7.993579 10.894900 +v -5.748137 7.993579 10.894900 +v -30.166159 26.787380 10.721846 +v -30.194857 26.796295 11.056828 +v -5.759465 7.996225 11.231019 +v -31.472544 10.997549 0.614511 +v -31.472544 26.957542 9.600615 +v -31.095026 27.064472 9.410694 +v -31.095026 11.104485 0.424588 +v -31.095026 26.850609 9.790541 +v -31.095026 10.890615 0.804433 +v -31.095026 10.890615 0.804433 +v -31.095026 26.850609 9.790541 +v -6.811996 16.464014 12.762231 +v -14.275897 26.958862 10.850204 +v -14.185555 26.973335 10.576986 +v -6.721651 16.478489 12.489015 +v -13.967443 27.103703 10.441158 +v -6.503540 16.608864 12.353185 +v -13.749335 27.273605 10.522282 +v -6.285429 16.778763 12.434308 +v -6.285429 16.778763 12.434308 +v -13.749335 27.273605 10.522282 +v -13.658987 27.383507 10.772839 +v -6.195086 16.888662 12.684867 +v -13.749335 27.369030 11.046057 +v -6.285429 16.874187 12.958085 +v -13.967443 27.238659 11.181887 +v -6.503540 16.743813 13.093915 +v -14.185555 27.068760 11.100763 +v -6.721651 16.573915 13.012792 +v -3.735173 17.646593 0.622345 +v -3.735174 18.907696 0.392586 +v -2.681455 18.909416 -2.662604 +v -2.681455 17.756693 -2.452592 +v -0.137555 19.238968 -3.988016 +v -0.137555 17.892881 -3.742776 +v -0.137555 19.903610 4.531394 +v -0.137555 21.291946 4.278457 +v -2.681456 20.489861 3.159215 +v -2.681455 19.189877 3.396056 +v -3.735174 18.907696 0.392586 +v -3.143685 18.907696 0.392586 +v -2.263211 18.997980 -2.176482 +v -2.681455 18.909416 -2.662604 +v -2.681455 18.909416 -2.662604 +v -2.263211 18.997980 -2.176482 +v -0.137555 19.364216 -3.300538 +v -0.137555 19.238968 -3.988016 +v -0.137555 21.291946 4.278457 +v -0.137555 20.931133 3.633897 +v -2.263211 20.286793 2.693955 +v -2.681456 20.489861 3.159215 +v -2.681456 20.489861 3.159215 +v -2.263211 20.286793 2.693955 +v -3.143685 18.907696 0.392586 +v -3.735174 18.907696 0.392586 +v -3.143685 18.907696 0.392586 +v -3.143685 17.970634 0.563307 +v -2.263211 17.957739 -1.986966 +v -2.263211 18.997980 -2.176482 +v -2.263211 18.997980 -2.176482 +v -2.263211 17.957739 -1.986966 +v -0.137555 18.330683 -3.112241 +v -0.137555 19.364216 -3.300538 +v -0.137555 20.931133 3.633897 +v -0.137555 20.041740 3.795932 +v -2.263211 19.330694 2.868146 +v -2.263211 20.286793 2.693955 +v -2.263211 20.286793 2.693955 +v -2.263211 19.330694 2.868146 +v -3.143685 17.970634 0.563307 +v -3.143685 18.907696 0.392586 +v -2.039805 19.540924 2.445557 +v -2.039805 22.274622 17.450493 +v -1.882362 22.648573 17.382362 +v -1.882362 19.914867 2.377429 +v -1.502259 22.803465 17.354145 +v -1.502259 20.069763 2.349210 +v -1.122157 22.648573 17.382362 +v -1.122157 19.914867 2.377429 +v -0.964713 22.274622 17.450493 +v -0.964713 19.540924 2.445557 +v -1.122157 21.900675 17.518620 +v -1.122157 19.166975 2.513686 +v -1.122157 19.166975 2.513686 +v -1.122157 21.900675 17.518620 +v -1.502259 21.745783 17.546841 +v -1.502259 19.012081 2.541905 +v -1.882362 21.900675 17.518620 +v -1.882362 19.166975 2.513686 +v -1.502259 21.745783 17.546841 +v -1.502259 22.803465 17.354145 +v -1.603475 22.063547 17.279758 +v -1.603475 22.272367 18.425961 +v -1.539961 22.423220 18.398476 +v -1.539961 22.214399 17.252277 +v -1.386625 22.485704 18.387093 +v -1.386625 22.276884 17.240891 +v -1.233289 22.423220 18.398476 +v -1.233289 22.214399 17.252277 +v -1.169775 22.272367 18.425961 +v -1.169775 22.063547 17.279758 +v -1.233289 22.121515 18.453445 +v -1.233289 21.912691 17.307243 +v -1.386625 22.059031 18.464828 +v -1.386625 21.850206 17.318626 +v -1.539961 22.121515 18.453445 +v -1.539961 21.912691 17.307243 +v -1.539961 21.912691 17.307243 +v -1.539961 22.121515 18.453445 +v -1.386625 22.059031 18.464828 +v -1.386625 22.485704 18.387093 +v -3.417006 10.968184 22.387386 +v -1.213201 14.126719 22.777767 +v -1.609402 14.102673 22.739937 +v -3.879512 11.357963 22.337925 +v -3.140869 10.735471 22.416927 +v -0.655696 14.247482 22.824745 +v -2.925635 10.554080 22.439945 +v -0.440460 14.066093 22.847767 +v -2.794947 10.443944 22.453922 +v -0.448988 13.577412 22.857906 +v -5.848219 8.446975 21.563320 +v -6.357187 8.875909 21.508881 +v -5.294947 7.980704 21.622498 +v -5.070643 7.791671 21.646484 +v -4.863611 7.617192 21.668633 +v -8.008676 6.131011 20.448557 +v -7.011161 7.204411 21.010193 +v -7.693181 7.779181 20.937244 +v -8.842028 6.833315 20.359426 +v -7.264944 5.504225 20.528097 +v -6.356288 6.652514 21.080233 +v -6.753573 5.073263 20.582792 +v -6.045258 6.390395 21.113504 +v -6.315737 4.704283 20.629625 +v -5.715096 6.112151 21.148817 +v -9.969534 5.100362 19.459917 +v -10.725815 2.725559 18.293270 +v -8.920165 4.995983 19.816156 +v -9.343699 6.254065 20.034655 +v -10.773172 0.857841 17.474567 +v -8.424287 3.994022 19.687868 +v -10.118230 -0.388031 17.222591 +v -7.879467 3.319399 19.677153 +v -8.821138 -0.079582 17.997200 +v -7.176163 3.160187 19.890013 +v -2.690449 10.355878 22.465099 +v -0.420340 13.100590 22.867107 +v -4.698066 7.477681 21.686337 +v -5.451099 5.889658 21.177050 +v -6.419691 3.224981 20.184286 +v -5.965638 4.409235 20.667070 +v -7.374048 1.015672 19.080969 +v -1.609402 14.102673 22.739937 +v -1.576042 14.106964 22.461826 +v -3.832299 11.379006 22.062275 +v -3.879512 11.357963 22.337925 +v -1.213201 14.126719 22.777767 +v -1.180917 14.129717 22.499580 +v -1.576042 14.106964 22.461826 +v -1.609402 14.102673 22.739937 +v -0.655696 14.247482 22.824745 +v -0.625182 14.248373 22.546444 +v -1.180917 14.129717 22.499580 +v -1.213201 14.126719 22.777767 +v -0.440460 14.066093 22.847767 +v -0.409945 14.066982 22.569466 +v -0.625182 14.248373 22.546444 +v -0.655696 14.247482 22.824745 +v -2.690449 10.355878 22.465099 +v -2.643234 10.376924 22.189453 +v -0.386982 13.104876 22.589008 +v -0.420340 13.100590 22.867107 +v -0.448988 13.577412 22.857906 +v -0.416986 13.580074 22.579697 +v -0.409945 14.066982 22.569466 +v -0.440460 14.066093 22.847767 +v -3.879512 11.357963 22.337925 +v -3.832299 11.379006 22.062275 +v -6.296528 8.913524 21.238117 +v -6.357187 8.875909 21.508881 +v -4.698066 7.477681 21.686337 +v -4.637406 7.515300 21.415569 +v -2.643234 10.376924 22.189453 +v -2.690449 10.355878 22.465099 +v -7.693181 7.779181 20.937244 +v -7.626042 7.824908 20.669788 +v -8.769339 6.886054 20.095335 +v -8.842028 6.833315 20.359426 +v -5.451099 5.889658 21.177050 +v -5.383957 5.935385 20.909594 +v -4.637406 7.515300 21.415569 +v -4.698066 7.477681 21.686337 +v -9.343699 6.254065 20.034655 +v -9.268124 6.310481 19.772535 +v -9.889118 5.162975 19.201414 +v -9.969534 5.100362 19.459917 +v -9.969534 5.100362 19.459917 +v -9.889118 5.162975 19.201414 +v -10.636815 2.799323 18.042316 +v -10.725815 2.725559 18.293270 +v -10.725815 2.725559 18.293270 +v -10.636815 2.799323 18.042316 +v -10.678776 0.938709 17.229160 +v -10.773172 0.857841 17.474567 +v -10.773172 0.857841 17.474567 +v -10.678776 0.938709 17.229160 +v -10.021882 -0.304576 16.979359 +v -10.118230 -0.388031 17.222591 +v -8.821138 -0.079582 17.997200 +v -8.728744 -0.001357 17.749660 +v -7.288219 1.085293 18.827051 +v -7.374048 1.015672 19.080969 +v -6.419691 3.224981 20.184286 +v -6.342486 3.283472 19.923330 +v -5.892945 4.461967 20.402983 +v -5.965638 4.409235 20.667070 +v -10.118230 -0.388031 17.222591 +v -10.021882 -0.304576 16.979359 +v -8.728744 -0.001357 17.749660 +v -8.821138 -0.079582 17.997200 +v -0.420340 13.100590 22.867107 +v -0.386982 13.104876 22.589008 +v -0.416986 13.580074 22.579697 +v -0.448988 13.577412 22.857906 +v -7.374048 1.015672 19.080969 +v -7.288219 1.085293 18.827051 +v -6.342486 3.283472 19.923330 +v -6.419691 3.224981 20.184286 +v -8.842028 6.833315 20.359426 +v -8.769339 6.886054 20.095335 +v -9.268124 6.310481 19.772535 +v -9.343699 6.254065 20.034655 +v -6.357187 8.875909 21.508881 +v -6.296528 8.913524 21.238117 +v -7.626042 7.824908 20.669788 +v -7.693181 7.779181 20.937244 +v -5.965638 4.409235 20.667070 +v -5.892945 4.461967 20.402983 +v -5.383957 5.935385 20.909594 +v -5.451099 5.889658 21.177050 +v -3.832299 11.379006 22.062275 +v -2.643234 10.376924 22.189453 +v -4.637406 7.515300 21.415569 +v -6.296528 8.913524 21.238117 +v -1.576042 14.106964 22.461826 +v -1.180917 14.129717 22.499580 +v -0.416986 13.580074 22.579697 +v -0.386982 13.104876 22.589008 +v -0.416986 13.580074 22.579697 +v -1.180917 14.129717 22.499580 +v -0.625182 14.248373 22.546444 +v -0.409945 14.066982 22.569466 +v -3.832299 11.379006 22.062275 +v -1.576042 14.106964 22.461826 +v -0.386982 13.104876 22.589008 +v -2.643234 10.376924 22.189453 +v -4.637406 7.515300 21.415569 +v -5.383957 5.935385 20.909594 +v -7.626042 7.824908 20.669788 +v -6.296528 8.913524 21.238117 +v -5.892945 4.461967 20.402983 +v -6.342486 3.283472 19.923330 +v -9.268124 6.310481 19.772535 +v -8.769339 6.886054 20.095335 +v -8.769339 6.886054 20.095335 +v -7.626042 7.824908 20.669788 +v -5.383957 5.935385 20.909594 +v -5.892945 4.461967 20.402983 +v -10.636815 2.799323 18.042316 +v -8.728744 -0.001357 17.749660 +v -10.021882 -0.304576 16.979359 +v -10.678776 0.938709 17.229160 +v -9.889118 5.162975 19.201414 +v -9.268124 6.310481 19.772535 +v -6.342486 3.283472 19.923330 +v -7.288219 1.085293 18.827051 +v -10.636815 2.799323 18.042316 +v -9.889118 5.162975 19.201414 +v -7.288219 1.085293 18.827051 +v -8.728744 -0.001357 17.749660 +v 0.330618 18.670710 -33.683582 +v 0.285053 18.414829 -35.894424 +v -0.560163 18.414829 -35.894424 +v -0.605727 18.670710 -33.683582 +v -0.651571 18.602148 -30.355957 +v -0.605727 18.670710 -33.683582 +v -0.560163 18.414829 -35.894424 +v -0.605767 17.075186 -24.744356 +v -0.605767 17.075186 -24.744356 +v -0.605767 11.401349 -24.776545 +v -0.605767 12.973974 -22.107353 +v -0.605767 15.336257 -22.014980 +v -0.605767 11.401349 -24.776545 +v -0.651571 3.909487 -27.628571 +v -0.605766 4.289977 -24.225880 +v -0.605767 7.251087 -23.778145 +v -0.560163 18.414829 -35.894424 +v -0.475242 4.545812 -36.862782 +v -0.605767 11.401349 -24.776545 +v -0.605767 17.075186 -24.744356 +v -0.475242 4.545812 -36.862782 +v -0.494697 3.910732 -35.557438 +v -0.560162 3.476720 -33.171906 +v -0.651571 3.909487 -27.628571 +v -0.560163 18.414829 -35.894424 +v -0.475242 15.707024 -39.244591 +v -0.445198 12.034781 -40.236835 +v -0.475242 4.545812 -36.862782 +v -0.445198 12.034781 -40.236835 +v -0.445197 9.315046 -39.888615 +v -0.445197 6.808916 -39.080044 +v -0.475242 4.545812 -36.862782 +v 0.330657 15.336257 -22.014980 +v 0.330657 17.075186 -24.744356 +v -0.605767 17.075186 -24.744356 +v -0.605767 15.336257 -22.014980 +v 0.330657 17.075186 -24.744356 +v 0.330657 18.078314 -27.583200 +v -0.605767 18.078314 -27.583200 +v -0.605767 17.075186 -24.744356 +v 0.170088 12.034781 -40.236835 +v -0.445198 12.034781 -40.236835 +v -0.445197 13.888091 -40.062054 +v 0.170088 13.888091 -40.062054 +v 0.330618 3.524783 -30.914526 +v 0.376461 3.909487 -27.628571 +v -0.651571 3.909487 -27.628571 +v -0.605727 3.524783 -30.914526 +v 0.376461 3.909487 -27.628571 +v 0.330657 4.289977 -24.225880 +v -0.605766 4.289977 -24.225880 +v -0.651571 3.909487 -27.628571 +v 0.200132 15.707024 -39.244591 +v -0.475242 15.707024 -39.244591 +v -0.494698 17.377857 -38.011116 +v 0.219588 17.377857 -38.011116 +v 0.219588 3.910732 -35.557438 +v -0.494697 3.910732 -35.557438 +v -0.475242 4.545812 -36.862782 +v 0.200132 4.545812 -36.862782 +v 0.170088 13.888091 -40.062054 +v -0.445197 13.888091 -40.062054 +v -0.475242 15.707024 -39.244591 +v 0.200132 15.707024 -39.244591 +v 0.285052 3.476720 -33.171906 +v -0.560162 3.476720 -33.171906 +v -0.494697 3.910732 -35.557438 +v 0.219588 3.910732 -35.557438 +v 0.170088 6.808916 -39.080044 +v -0.445197 6.808916 -39.080044 +v -0.445197 9.315046 -39.888615 +v 0.170088 9.315046 -39.888615 +v 0.219588 17.377857 -38.011116 +v -0.494698 17.377857 -38.011116 +v -0.560163 18.414829 -35.894424 +v 0.285053 18.414829 -35.894424 +v 0.200132 4.545812 -36.862782 +v -0.475242 4.545812 -36.862782 +v -0.445197 5.657253 -38.139938 +v 0.170088 5.657253 -38.139938 +v 0.330618 18.670710 -33.683582 +v -0.605727 18.670710 -33.683582 +v -0.651571 18.602148 -30.355957 +v 0.376462 18.602148 -30.355957 +v 0.330657 18.078314 -27.583200 +v 0.376462 18.602148 -30.355957 +v -0.651571 18.602148 -30.355957 +v -0.605767 18.078314 -27.583200 +v 0.170088 9.315046 -39.888615 +v -0.445197 9.315046 -39.888615 +v -0.445198 12.034781 -40.236835 +v 0.170088 12.034781 -40.236835 +v 0.330657 11.401349 -24.776545 +v 0.330657 12.973974 -22.107353 +v -0.605767 12.973974 -22.107353 +v -0.605767 11.401349 -24.776545 +v 0.330657 4.289977 -24.225880 +v 0.330657 7.251087 -23.778145 +v -0.605767 7.251087 -23.778145 +v -0.605766 4.289977 -24.225880 +v 0.285052 3.476720 -33.171906 +v 0.330618 3.524783 -30.914526 +v -0.605727 3.524783 -30.914526 +v -0.560162 3.476720 -33.171906 +v 0.170088 5.657253 -38.139938 +v -0.445197 5.657253 -38.139938 +v -0.445197 6.808916 -39.080044 +v 0.170088 6.808916 -39.080044 +v 0.330657 7.251087 -23.778145 +v 0.330657 11.401349 -24.776545 +v -0.605767 11.401349 -24.776545 +v -0.605767 7.251087 -23.778145 +v 0.330657 12.973974 -22.107353 +v 0.330657 15.336257 -22.014980 +v -0.605767 15.336257 -22.014980 +v -0.605767 12.973974 -22.107353 +v -11.639269 -4.232666 14.326954 +v -11.511729 -3.140807 14.369819 +v -11.417636 -2.881190 9.001891 +v -11.511726 -3.973048 8.959024 +v -11.417636 -2.112300 14.000815 +v -11.337867 -1.892203 9.450106 +v -11.804996 -6.275688 11.022794 +v -11.826226 -6.327328 12.090542 +v -11.337867 -1.303718 13.276114 +v -11.284566 -1.156657 10.235428 +v -11.284569 -0.838170 12.306050 +v -11.265852 -0.786530 11.238302 +v -10.221741 -1.248802 10.249601 +v -10.203024 -0.878675 11.252476 +v -10.221741 -0.930319 12.320226 +v -10.275043 -1.395863 13.290288 +v -10.354806 -2.973335 9.016065 +v -10.275040 -1.984348 9.464279 +v -10.354809 -2.204439 14.014987 +v -10.448902 -3.232952 14.383994 +v -10.599373 -6.049350 13.107590 +v -10.622762 -5.902290 10.066901 +v -10.620604 -6.419479 12.104715 +v -10.599370 -6.367838 11.036967 +v -0.137555 13.788481 23.542515 +v -0.137555 16.032167 23.133743 +v -0.137555 13.092350 21.825588 +v -0.137555 16.042728 21.288063 +v -2.034282 9.582694 18.923504 +v -2.034281 18.592596 17.282019 +v -0.137555 9.211522 18.991127 +v -0.137555 18.963770 17.214392 +v -0.137555 9.192825 -28.695456 +v -0.137555 10.607282 -20.931702 +v -5.704780 25.412260 -1.122682 +v -6.124956 25.098688 -2.843851 +v -6.725490 10.797626 7.312677 +v -5.433122 11.356320 7.210891 +v -5.870196 13.518061 6.817050 +v -7.268282 13.482239 6.823577 +v -5.179747 8.521720 7.727320 +v -4.188437 9.523684 7.544775 +v -5.433122 11.356320 7.210891 +v -6.725490 10.797626 7.312677 +v -7.268282 13.482239 6.823577 +v -5.870196 13.518061 6.817050 +v -5.433122 15.679805 6.423209 +v -6.725487 16.166853 6.334475 +v -2.866378 7.001008 8.004374 +v -2.325634 8.299153 7.767869 +v -4.188437 9.523684 7.544775 +v -5.179747 8.521720 7.727320 +v -5.179746 19.031322 5.812605 +v -4.188436 17.986372 6.002982 +v -2.325632 19.210899 5.779888 +v -2.866376 20.552032 5.535551 +v -6.725487 16.166853 6.334475 +v -5.433122 15.679805 6.423209 +v -4.188436 17.986372 6.002982 +v -5.179746 19.031322 5.812605 +v -2.866376 20.552032 5.535551 +v -2.325632 19.210899 5.779888 +v -0.137555 19.639086 5.701879 +v -0.137555 21.086037 5.438262 +v -0.137555 13.788481 23.542515 +v -0.938484 14.114862 23.483055 +v -0.990843 14.051090 23.425133 +v -0.137555 13.703371 23.488483 +v -0.137555 13.703371 23.488483 +v -0.990843 14.051090 23.425133 +v -0.990843 13.707285 21.538033 +v -0.137555 13.359564 21.601383 +v -0.137555 6.467003 8.101662 +v -0.137555 7.870969 7.845878 +v -2.325634 8.299153 7.767869 +v -2.866378 7.001008 8.004374 +v 1.870543 20.548264 5.038902 +v 1.870543 22.464592 4.689771 +v 0.505632 23.065418 4.580308 +v 0.505632 20.832825 4.987058 +v -0.859279 23.065418 4.580308 +v -0.859279 20.832825 4.987056 +v -2.224189 22.464592 4.689771 +v -2.224189 20.548264 5.038902 +v 1.870543 20.472183 4.621320 +v 0.505632 20.756752 4.569478 +v 0.505632 22.989342 4.162726 +v 1.870543 22.388515 4.272189 +v -0.859278 20.756752 4.569476 +v -0.859278 22.989342 4.162726 +v -2.224189 20.472183 4.621320 +v -2.224189 22.388515 4.272189 +v -2.224189 20.548264 5.038902 +v -2.224189 22.464592 4.689771 +v -2.224189 22.388515 4.272189 +v -2.224189 20.472183 4.621320 +v -2.224189 22.464592 4.689771 +v -0.859279 23.065418 4.580308 +v -0.859278 22.989342 4.162726 +v -2.224189 22.388515 4.272189 +v 0.505632 23.065418 4.580308 +v 0.505632 22.989342 4.162726 +v 1.870543 22.464592 4.689771 +v 1.870543 22.388515 4.272189 +v 1.870543 22.464592 4.689771 +v 1.870543 20.548264 5.038902 +v 1.870543 20.472183 4.621320 +v 1.870543 22.388515 4.272189 +v -1.806203 23.529200 16.307932 +v -1.365730 23.349699 16.340633 +v -1.183280 22.916361 16.419582 +v -1.806203 22.916361 16.419582 +v -1.183280 22.916361 16.419582 +v -1.365730 23.349699 16.340633 +v -1.365730 23.360544 16.400158 +v -1.183280 22.927206 16.479107 +v -1.806203 23.529200 16.307932 +v -1.806203 23.540041 16.367455 +v -2.246677 23.349699 16.340633 +v -2.246677 23.360544 16.400158 +v -2.429127 22.916361 16.419582 +v -2.429127 22.927206 16.479107 +v -2.246677 22.483023 16.498531 +v -2.246677 22.493864 16.558056 +v -1.806203 22.303526 16.531235 +v -1.806203 22.314371 16.590755 +v -1.365730 22.483023 16.498531 +v -1.365730 22.493864 16.558056 +v -1.183280 22.927206 16.479107 +v -1.365730 23.360544 16.400158 +v -1.806203 23.540041 16.367455 +v -1.806203 22.927206 16.479107 +v -2.246677 23.349699 16.340633 +v -2.429127 22.916361 16.419582 +v -2.246677 23.360544 16.400158 +v -2.429127 22.927206 16.479107 +v -2.246677 22.493864 16.558056 +v -1.806203 22.314371 16.590755 +v -1.365730 22.493864 16.558056 +v -1.365730 22.483023 16.498531 +v -1.806203 22.303526 16.531235 +v -2.246677 22.483023 16.498531 +v 29.751905 10.424383 -0.328292 +v 29.443541 10.391874 -0.458566 +v 12.087492 25.034750 0.064983 +v 12.383435 25.144411 0.181201 +v 29.443541 10.391874 -0.458566 +v 29.484251 10.409550 -0.125184 +v 12.137660 25.064028 0.396255 +v 12.087492 25.034750 0.064983 +v 5.062560 5.452081 0.598234 +v 4.798513 5.584981 0.437826 +v 29.216539 24.378778 0.264774 +v 29.517099 24.334452 0.409047 +v 4.798513 5.584981 0.437826 +v 4.809842 5.587626 0.773945 +v 29.245237 24.387697 0.599754 +v 29.216539 24.378778 0.264774 +v 5.862374 14.055417 2.305160 +v 5.772030 14.069890 2.031945 +v 13.235933 24.564737 0.119916 +v 13.326281 24.550264 0.393134 +v 5.553920 14.200265 1.896114 +v 13.017826 24.695112 -0.015912 +v 5.335808 14.370164 1.977235 +v 12.799714 24.865015 0.065210 +v 5.335808 14.370164 1.977235 +v 5.245464 14.480065 2.227794 +v 12.709373 24.974913 0.315768 +v 12.799714 24.865015 0.065210 +v 5.335808 14.465589 2.501012 +v 12.799714 24.960436 0.588987 +v 5.553920 14.335216 2.636842 +v 13.017826 24.830057 0.724817 +v 5.772030 14.165317 2.555719 +v 13.235933 24.660166 0.643693 +v 30.305048 10.643557 0.578635 +v 30.963701 10.618658 0.579076 +v 30.813036 24.929342 1.613645 +v 30.154379 24.954233 1.613205 +v 30.963701 10.618658 0.579076 +v 30.960356 10.720448 -1.269946 +v 30.809679 25.031126 -0.235375 +v 30.813036 24.929342 1.613645 +v 30.960356 10.720448 -1.269946 +v 30.301702 10.745346 -1.270385 +v 30.151026 25.056025 -0.235818 +v 30.809679 25.031126 -0.235375 +v 30.301702 10.745346 -1.270385 +v 30.305048 10.643557 0.578635 +v 30.154379 24.954233 1.613205 +v 30.151026 25.056025 -0.235818 +v 30.168859 12.297785 8.400374 +v 29.800108 12.215061 8.225577 +v 30.481874 25.568533 0.910831 +v 30.850637 25.651247 1.085624 +v 29.783651 12.389593 8.543138 +v 30.465429 25.743063 1.228393 +v 29.783651 12.389593 8.543138 +v 30.465429 25.743063 1.228393 +v -30.407583 10.874838 0.548656 +v -30.256914 25.185518 1.583229 +v -30.915556 25.160620 1.583671 +v -31.066236 10.849939 0.549096 +v -31.066236 10.849939 0.549096 +v -30.915556 25.160620 1.583671 +v -30.912214 25.262411 -0.265351 +v -31.062880 10.951727 -1.299926 +v -31.062880 10.951727 -1.299926 +v -30.912214 25.262411 -0.265351 +v -30.253561 25.287302 -0.265792 +v -30.404238 10.976625 -1.300365 +v -30.404238 10.976625 -1.300365 +v -30.253561 25.287302 -0.265792 +v -30.256914 25.185518 1.583229 +v -30.407583 10.874838 0.548656 +v -30.528948 11.244707 0.468940 +v -13.160481 25.964739 0.978435 +v -12.864539 25.855074 0.862214 +v -30.220583 11.212198 0.338664 +v -30.220583 11.212198 0.338664 +v -12.864539 25.855074 0.862214 +v -12.914712 25.884352 1.193487 +v -30.261293 11.229874 0.672046 +v -5.839613 6.272407 1.395468 +v -30.294146 25.154779 1.206283 +v -29.993582 25.199106 1.062009 +v -5.575566 6.405307 1.235060 +v -5.575566 6.405307 1.235060 +v -29.993582 25.199106 1.062009 +v -30.022280 25.208025 1.396987 +v -5.586895 6.407953 1.571177 +v -6.639424 14.875742 3.102392 +v -14.103328 25.370588 1.190364 +v -14.012986 25.385061 0.917148 +v -6.549077 14.890219 2.829175 +v -13.794873 25.515436 0.781316 +v -6.330967 15.020592 2.693344 +v -13.576761 25.685339 0.862441 +v -6.112858 15.190493 2.774467 +v -6.112858 15.190493 2.774467 +v -13.576761 25.685339 0.862441 +v -13.486419 25.795240 1.112998 +v -6.022512 15.300392 3.025024 +v -13.576761 25.780760 1.386217 +v -6.112858 15.285915 3.298242 +v -13.794873 25.650393 1.522045 +v -6.330967 15.155542 3.434072 +v -14.012986 25.480490 1.440923 +v -6.549077 14.985643 3.352951 +v -30.282450 25.731180 1.116691 +v -30.282450 12.223915 9.098432 +v -29.904940 12.318897 9.259166 +v -29.904940 25.826159 1.277427 +v -29.904940 12.128931 8.937694 +v -29.904940 25.636194 0.955959 +v -29.904940 25.636194 0.955959 +v -29.904940 12.128931 8.937694 +vt 1.879478 0.530174 +vt 1.897414 0.524735 +vt 1.879566 0.526113 +vt 1.881690 0.427537 +vt 1.899500 0.427921 +vt 1.881778 0.423476 +vt 1.948867 0.692212 +vt 1.948604 0.707056 +vt 1.953637 0.699684 +vt 0.565573 0.782724 +vt 0.597737 0.776562 +vt 0.631173 0.764652 +vt 0.596866 0.612516 +vt 0.558627 0.607968 +vt 0.532640 0.607381 +vt 0.630058 0.699236 +vt 0.596866 0.612516 +vt 0.489568 0.620042 +vt 0.489568 0.620042 +vt 0.475518 0.632309 +vt 0.464575 0.645515 +vt 0.450798 0.706790 +vt 0.452967 0.728116 +vt 0.462399 0.748874 +vt 0.462399 0.748874 +vt 0.476409 0.768140 +vt 0.500841 0.780471 +vt 1.802491 0.725181 +vt 1.806539 0.710319 +vt 1.802491 0.695456 +vt 1.052336 0.836845 +vt 1.057214 0.817874 +vt 1.052250 0.798920 +vt 0.848890 0.817343 +vt 0.848723 0.828857 +vt 0.856840 0.837024 +vt 0.876845 0.828942 +vt 0.877015 0.817427 +vt 0.868894 0.809261 +vt 1.880832 0.983071 +vt 1.880933 0.976152 +vt 1.876054 0.971245 +vt 1.864033 0.976101 +vt 1.863932 0.983021 +vt 1.868811 0.987928 +vt 0.879478 0.530174 +vt 0.879566 0.526113 +vt 0.897414 0.524735 +vt 0.881690 0.427537 +vt 0.881778 0.423476 +vt 0.899500 0.427921 +vt 0.948867 0.692212 +vt 0.953637 0.699684 +vt 0.948604 0.707056 +vt 0.565573 0.782724 +vt 0.631173 0.764652 +vt 0.597737 0.776562 +vt 0.596866 0.612516 +vt 0.532640 0.607381 +vt 0.558627 0.607968 +vt 0.630058 0.699236 +vt 0.489568 0.620042 +vt 0.596866 0.612516 +vt 0.489568 0.620042 +vt 0.464575 0.645515 +vt 0.475518 0.632309 +vt 0.450798 0.706790 +vt 0.462399 0.748874 +vt 0.452967 0.728116 +vt 0.462399 0.748874 +vt 0.500841 0.780471 +vt 0.476409 0.768140 +vt 0.802491 0.725181 +vt 0.802491 0.695456 +vt 0.806539 0.710319 +vt 0.052336 0.836845 +vt 0.052250 0.798920 +vt 0.057214 0.817874 +vt 1.848890 0.817343 +vt 1.856840 0.837024 +vt 1.848723 0.828857 +vt 1.876845 0.828942 +vt 1.868894 0.809261 +vt 1.877015 0.817427 +vt 0.880832 0.983071 +vt 0.876054 0.971245 +vt 0.880933 0.976152 +vt 0.864033 0.976101 +vt 0.868811 0.987928 +vt 0.863932 0.983021 +vt 0.764773 0.724732 +vt 0.734627 0.710218 +vt 0.734627 0.809321 +vt 1.897414 0.524735 +vt 1.923223 0.496405 +vt 1.880188 0.497240 +vt 1.879566 0.526113 +vt 1.923223 0.496405 +vt 1.924064 0.457336 +vt 1.881068 0.456409 +vt 1.880188 0.497240 +vt 1.881068 0.456409 +vt 1.924064 0.457336 +vt 1.899500 0.427921 +vt 1.881690 0.427537 +vt 0.122549 0.656233 +vt 0.122578 0.652637 +vt 0.010500 0.652637 +vt 0.010633 0.652337 +vt 0.234486 0.661460 +vt 0.234656 0.654561 +vt 0.346443 0.668053 +vt 0.346734 0.658459 +vt 0.407538 0.672188 +vt 0.408643 0.660136 +vt 0.122122 0.451058 +vt 0.123852 0.442823 +vt 0.078205 0.439206 +vt 0.076831 0.448786 +vt 0.234965 0.454150 +vt 0.235514 0.448414 +vt 0.347024 0.455886 +vt 0.347440 0.450682 +vt 0.428376 0.456721 +vt 0.428786 0.451517 +vt 0.454072 0.482134 +vt 0.433218 0.452772 +vt 0.428376 0.456721 +vt 1.008858 0.429252 +vt 1.359604 0.433571 +vt 1.361111 0.429252 +vt 1.415687 0.433571 +vt 1.415695 0.429252 +vt 1.007350 0.223041 +vt 1.359604 0.226016 +vt 1.360039 0.219522 +vt 1.007350 0.219056 +vt 1.408423 0.225906 +vt 1.411958 0.221276 +vt 1.417122 0.012457 +vt 1.444812 0.062060 +vt 1.449332 0.062060 +vt 1.421961 0.012442 +vt 1.676401 0.986658 +vt 1.700313 0.990996 +vt 1.701728 0.978157 +vt 1.677748 0.976969 +vt 1.786661 0.989188 +vt 1.787706 0.983066 +vt 1.808887 0.988109 +vt 1.806581 0.983761 +vt 2.956937 0.990430 +vt 2.964659 0.990613 +vt 2.960512 0.822945 +vt 2.952790 0.822763 +vt 2.964659 0.990613 +vt 2.986306 0.990450 +vt 2.982158 0.822781 +vt 2.960512 0.822945 +vt 2.986306 0.990450 +vt 2.994010 0.989886 +vt 2.989862 0.822218 +vt 2.982158 0.822781 +vt 2.935309 0.991339 +vt 2.956937 0.990430 +vt 2.952790 0.822763 +vt 2.931161 0.823668 +vt 0.880274 0.541093 +vt 0.880271 0.560769 +vt 0.894615 0.560769 +vt 0.894613 0.541093 +vt 0.880271 0.585741 +vt 0.894615 0.585741 +vt 0.894611 0.566056 +vt 0.880276 0.566056 +vt 0.880274 0.541093 +vt 0.894613 0.541093 +vt 0.894601 0.535809 +vt 0.880285 0.535809 +vt 0.899927 0.585758 +vt 0.899927 0.566039 +vt 0.894611 0.566056 +vt 0.894615 0.585741 +vt 0.894615 0.560769 +vt 0.880271 0.560769 +vt 0.880276 0.566056 +vt 0.894611 0.566056 +vt 0.874960 0.566039 +vt 0.874960 0.585758 +vt 0.880271 0.585741 +vt 0.880276 0.566056 +vt 0.151943 0.966273 +vt 0.118276 0.966204 +vt 0.118294 0.984845 +vt 0.151961 0.984813 +vt 0.178138 0.965761 +vt 0.178157 0.984787 +vt 0.204440 0.965590 +vt 0.204459 0.984762 +vt 0.232801 0.965563 +vt 0.232820 0.984734 +vt 0.038622 0.965136 +vt 0.012148 0.965162 +vt 0.014053 0.984946 +vt 0.038641 0.984922 +vt 0.065148 0.965369 +vt 0.065167 0.984896 +vt 0.091706 0.965741 +vt 0.091724 0.984871 +vt 1.041294 0.782869 +vt 1.041455 0.852946 +vt 1.347561 0.949223 +vt 1.320488 0.949446 +vt 1.320590 0.961766 +vt 1.347663 0.961543 +vt 1.293415 0.949670 +vt 1.293517 0.961990 +vt 1.266341 0.949894 +vt 1.266443 0.962214 +vt 1.239268 0.950118 +vt 1.239370 0.962438 +vt 1.455855 0.948328 +vt 1.428781 0.948551 +vt 1.428883 0.960871 +vt 1.455957 0.960648 +vt 1.401708 0.948775 +vt 1.401810 0.961095 +vt 1.374635 0.948999 +vt 1.374737 0.961319 +vt 0.570606 0.567542 +vt 0.583420 0.567306 +vt 0.583267 0.559022 +vt 0.570454 0.559258 +vt 0.597978 0.567038 +vt 0.597825 0.558754 +vt 0.613012 0.566762 +vt 0.612860 0.558477 +vt 0.613380 0.586739 +vt 0.598345 0.587015 +vt 0.598505 0.595698 +vt 0.613540 0.595421 +vt 0.583787 0.587283 +vt 0.583947 0.595966 +vt 0.570974 0.587519 +vt 0.571134 0.596202 +vt 0.557296 0.587771 +vt 0.557455 0.596453 +vt 0.543262 0.588029 +vt 0.543422 0.596712 +vt 0.528359 0.588303 +vt 0.528518 0.596986 +vt 1.932941 0.593358 +vt 1.936440 0.593358 +vt 1.936440 0.572867 +vt 1.932941 0.572867 +vt 1.791431 0.684576 +vt 1.791431 0.736062 +vt 1.978612 0.593111 +vt 1.978612 0.614013 +vt 1.985470 0.613849 +vt 1.985470 0.593358 +vt 1.978612 0.614013 +vt 1.978612 0.634422 +vt 1.985470 0.634339 +vt 1.985470 0.613849 +vt 1.978612 0.634422 +vt 1.978612 0.654830 +vt 1.985470 0.654830 +vt 1.985470 0.634339 +vt 1.978612 0.531886 +vt 1.978612 0.552294 +vt 1.985470 0.552377 +vt 1.985470 0.531886 +vt 1.978612 0.552294 +vt 1.978612 0.572703 +vt 1.985470 0.572867 +vt 1.985470 0.552377 +vt 1.978612 0.572703 +vt 1.978612 0.593111 +vt 1.985470 0.593358 +vt 1.985470 0.572867 +vt 1.989519 0.593358 +vt 1.989519 0.613849 +vt 1.992030 0.613849 +vt 1.992030 0.593358 +vt 1.989519 0.613849 +vt 1.989519 0.634339 +vt 1.992030 0.634339 +vt 1.992030 0.613849 +vt 1.989519 0.634339 +vt 1.989519 0.654830 +vt 1.992030 0.654830 +vt 1.992030 0.634339 +vt 1.989519 0.531886 +vt 1.989519 0.552377 +vt 1.992030 0.552377 +vt 1.992030 0.531886 +vt 1.989519 0.552377 +vt 1.989519 0.572867 +vt 1.992030 0.572867 +vt 1.992030 0.552377 +vt 1.989519 0.572867 +vt 1.989519 0.593358 +vt 1.992030 0.593358 +vt 1.992030 0.572867 +vt 1.985470 0.593358 +vt 1.985470 0.613849 +vt 1.989519 0.613849 +vt 1.989519 0.593358 +vt 1.985470 0.613849 +vt 1.985470 0.634339 +vt 1.989519 0.634339 +vt 1.989519 0.613849 +vt 1.985470 0.634339 +vt 1.985470 0.654830 +vt 1.989519 0.654830 +vt 1.989519 0.634339 +vt 1.985470 0.531886 +vt 1.985470 0.552377 +vt 1.989519 0.552377 +vt 1.989519 0.531886 +vt 1.985470 0.552377 +vt 1.985470 0.572867 +vt 1.989519 0.572867 +vt 1.989519 0.552377 +vt 1.985470 0.572867 +vt 1.985470 0.593358 +vt 1.989519 0.593358 +vt 1.989519 0.572867 +vt 1.997477 0.613849 +vt 1.997477 0.593358 +vt 1.992030 0.593358 +vt 1.992030 0.613849 +vt 1.997477 0.593358 +vt 1.997477 0.572867 +vt 1.992030 0.572867 +vt 1.992030 0.593358 +vt 1.997477 0.572867 +vt 1.997477 0.552377 +vt 1.992030 0.552377 +vt 1.992030 0.572867 +vt 1.997477 0.552377 +vt 1.997477 0.531886 +vt 1.992030 0.531886 +vt 1.992030 0.552377 +vt 1.997477 0.654830 +vt 1.997477 0.634339 +vt 1.992030 0.634339 +vt 1.992030 0.654830 +vt 1.997477 0.634339 +vt 1.997477 0.613849 +vt 1.992030 0.613849 +vt 1.992030 0.634339 +vt 1.964272 0.593111 +vt 1.964272 0.572703 +vt 1.945781 0.572867 +vt 1.945781 0.593358 +vt 1.945781 0.613849 +vt 1.945781 0.593358 +vt 1.936440 0.593358 +vt 1.936440 0.613848 +vt 1.945781 0.593358 +vt 1.945781 0.572867 +vt 1.936440 0.572867 +vt 1.936440 0.593358 +vt 1.945781 0.572867 +vt 1.945781 0.552377 +vt 1.936440 0.552377 +vt 1.936440 0.572867 +vt 1.945781 0.552377 +vt 1.945781 0.531886 +vt 1.936440 0.531886 +vt 1.936440 0.552377 +vt 1.945781 0.654830 +vt 1.945781 0.634339 +vt 1.936440 0.634339 +vt 1.936440 0.654830 +vt 1.945781 0.634339 +vt 1.945781 0.613849 +vt 1.936440 0.613848 +vt 1.936440 0.634339 +vt 1.979362 0.691436 +vt 1.979348 0.689810 +vt 1.963987 0.689810 +vt 1.964001 0.691436 +vt 1.964001 0.691436 +vt 1.963987 0.689810 +vt 1.948957 0.689810 +vt 1.948867 0.692212 +vt 1.979067 0.658221 +vt 1.963706 0.658221 +vt 1.963987 0.689810 +vt 1.979348 0.689810 +vt 1.963706 0.658221 +vt 1.948957 0.658221 +vt 1.948957 0.689810 +vt 1.963987 0.689810 +vt 0.787461 0.635838 +vt 0.787461 0.644547 +vt 0.790486 0.644521 +vt 0.790486 0.635883 +vt 0.787461 0.652662 +vt 0.790486 0.652704 +vt 0.787461 0.661373 +vt 0.790486 0.661424 +vt 0.787461 0.669213 +vt 0.790486 0.669213 +vt 0.787461 0.677249 +vt 0.790486 0.677249 +vt 0.787461 0.611351 +vt 0.787461 0.618762 +vt 0.790486 0.618762 +vt 0.790486 0.611351 +vt 0.787461 0.627702 +vt 0.790486 0.627702 +vt 2.838023 0.574485 +vt 2.819068 0.574477 +vt 2.820639 0.577859 +vt 2.836431 0.577819 +vt 2.850694 0.586961 +vt 2.838023 0.574485 +vt 2.836431 0.577819 +vt 2.847119 0.588558 +vt 2.850748 0.605962 +vt 2.850694 0.586961 +vt 2.847119 0.588558 +vt 2.847216 0.604497 +vt 2.838228 0.618611 +vt 2.850748 0.605962 +vt 2.847216 0.604497 +vt 2.836657 0.615142 +vt 2.819383 0.618676 +vt 2.838228 0.618611 +vt 2.836657 0.615142 +vt 2.820841 0.615024 +vt 2.806807 0.606156 +vt 2.819383 0.618676 +vt 2.820841 0.615024 +vt 2.810164 0.604546 +vt 2.806580 0.587149 +vt 2.806807 0.606156 +vt 2.810164 0.604546 +vt 2.810070 0.588715 +vt 2.819068 0.574477 +vt 2.806580 0.587149 +vt 2.810070 0.588715 +vt 2.820639 0.577859 +vt 2.826168 0.590676 +vt 2.831048 0.590663 +vt 2.836431 0.577819 +vt 2.820639 0.577859 +vt 2.831048 0.590663 +vt 2.834519 0.594115 +vt 2.847119 0.588558 +vt 2.836431 0.577819 +vt 2.834519 0.594115 +vt 2.834548 0.599011 +vt 2.847216 0.604497 +vt 2.847119 0.588558 +vt 2.834548 0.599011 +vt 2.831117 0.602483 +vt 2.836657 0.615142 +vt 2.847216 0.604497 +vt 2.831117 0.602483 +vt 2.826236 0.602496 +vt 2.820841 0.615024 +vt 2.836657 0.615142 +vt 2.826236 0.602496 +vt 2.822765 0.599043 +vt 2.810164 0.604546 +vt 2.820841 0.615024 +vt 2.822765 0.599043 +vt 2.822736 0.594147 +vt 2.810070 0.588715 +vt 2.810164 0.604546 +vt 2.822736 0.594147 +vt 2.826168 0.590676 +vt 2.820639 0.577859 +vt 2.810070 0.588715 +vt 1.712529 0.749275 +vt 1.712529 0.796196 +vt 1.722815 0.796196 +vt 1.722815 0.749275 +vt 1.712529 0.796196 +vt 1.712529 0.821096 +vt 1.722815 0.821096 +vt 1.722815 0.796196 +vt 1.712529 0.687755 +vt 1.712529 0.713461 +vt 1.722815 0.713461 +vt 1.722815 0.687755 +vt 1.712529 0.713461 +vt 1.712529 0.749275 +vt 1.722815 0.749275 +vt 1.722815 0.713461 +vt 1.869054 0.971224 +vt 1.875811 0.987949 +vt 0.868492 0.837058 +vt 0.857248 0.809226 +vt 1.963142 0.507307 +vt 1.956692 0.465842 +vt 1.953100 0.466601 +vt 1.959462 0.507483 +vt 1.975459 0.514343 +vt 1.971978 0.514904 +vt 1.971030 0.518012 +vt 1.975659 0.517915 +vt 1.974988 0.462717 +vt 1.981663 0.505121 +vt 1.985318 0.504333 +vt 1.978737 0.462523 +vt 1.932504 0.389173 +vt 1.926916 0.381387 +vt 1.928188 0.391026 +vt 1.953100 0.466601 +vt 1.941483 0.425665 +vt 1.918600 0.436844 +vt 1.935331 0.471062 +vt 1.945891 0.513500 +vt 1.959462 0.507483 +vt 1.941512 0.510082 +vt 1.945891 0.513500 +vt 1.953322 0.516859 +vt 1.956592 0.511103 +vt 1.941512 0.510082 +vt 1.959462 0.507483 +vt 1.953100 0.466601 +vt 1.935331 0.471062 +vt 1.935117 0.407189 +vt 1.905937 0.424989 +vt 1.918600 0.436844 +vt 1.941483 0.425665 +vt 1.905937 0.424989 +vt 1.935117 0.407189 +vt 1.928188 0.391026 +vt 1.886744 0.406160 +vt 0.631173 0.764652 +vt 0.661496 0.745082 +vt 0.660792 0.717479 +vt 0.630058 0.699236 +vt 2.858635 0.680042 +vt 2.871958 0.679834 +vt 2.882754 0.675465 +vt 2.847085 0.674987 +vt 2.891651 0.666530 +vt 2.838888 0.666443 +vt 1.939803 0.691444 +vt 1.939801 0.709338 +vt 1.776324 0.680594 +vt 1.776324 0.740044 +vt 1.026016 0.772165 +vt 1.026226 0.863725 +vt 1.006733 0.768437 +vt 1.006960 0.867541 +vt 0.480897 0.445316 +vt 0.478945 0.407182 +vt 0.467033 0.405254 +vt 0.468978 0.443492 +vt 0.483699 0.484442 +vt 0.480897 0.445316 +vt 0.468978 0.443492 +vt 0.473741 0.481656 +vt 0.478945 0.407182 +vt 0.473654 0.367990 +vt 0.464299 0.367976 +vt 0.467033 0.405254 +vt 0.484683 0.508705 +vt 0.483699 0.484442 +vt 0.473741 0.481656 +vt 0.475839 0.508026 +vt 0.471613 0.341395 +vt 0.470613 0.322225 +vt 0.465906 0.323652 +vt 0.465805 0.340155 +vt 0.473654 0.367990 +vt 0.471613 0.341395 +vt 0.465805 0.340155 +vt 0.464299 0.367976 +vt 0.470613 0.322225 +vt 0.470069 0.279726 +vt 0.464726 0.279813 +vt 0.465906 0.323652 +vt 0.485336 0.540193 +vt 0.484683 0.508705 +vt 0.475839 0.508026 +vt 0.474510 0.533329 +vt 1.734627 0.809321 +vt 1.734627 0.710218 +vt 1.764773 0.724732 +vt 0.880188 0.497240 +vt 0.923223 0.496405 +vt 0.897414 0.524735 +vt 0.879566 0.526113 +vt 0.881068 0.456409 +vt 0.924064 0.457336 +vt 0.923223 0.496405 +vt 0.880188 0.497240 +vt 0.881068 0.456409 +vt 0.881690 0.427537 +vt 0.899500 0.427921 +vt 0.924064 0.457336 +vt 1.010500 0.652637 +vt 1.122578 0.652637 +vt 1.122549 0.656233 +vt 1.010633 0.652337 +vt 1.234656 0.654561 +vt 1.234486 0.661460 +vt 1.346734 0.658459 +vt 1.346443 0.668053 +vt 1.408643 0.660136 +vt 1.407538 0.672188 +vt 1.078205 0.439206 +vt 1.123852 0.442823 +vt 1.122122 0.451058 +vt 1.076831 0.448786 +vt 1.234965 0.454150 +vt 1.235514 0.448414 +vt 1.347024 0.455886 +vt 1.347440 0.450682 +vt 1.428376 0.456721 +vt 1.428786 0.451517 +vt 1.454072 0.482134 +vt 1.447451 0.487014 +vt 1.428376 0.456721 +vt 1.433218 0.452772 +vt 0.008858 0.429252 +vt 0.361111 0.429252 +vt 0.359604 0.433571 +vt 0.415695 0.429252 +vt 0.415687 0.433571 +vt 0.417122 0.012457 +vt 0.421961 0.012442 +vt 0.449332 0.062060 +vt 0.444812 0.062060 +vt 0.676401 0.986658 +vt 0.677748 0.976969 +vt 0.701728 0.978157 +vt 0.700313 0.990996 +vt 0.787706 0.983066 +vt 0.786661 0.989188 +vt 0.806581 0.983761 +vt 0.808887 0.988109 +vt 3.956937 0.990430 +vt 3.952790 0.822763 +vt 3.960512 0.822945 +vt 3.964659 0.990613 +vt 3.964659 0.990613 +vt 3.960512 0.822945 +vt 3.982158 0.822781 +vt 3.986306 0.990450 +vt 3.986306 0.990450 +vt 3.982158 0.822781 +vt 3.989862 0.822218 +vt 3.994010 0.989886 +vt 3.935309 0.991339 +vt 3.931161 0.823668 +vt 3.952790 0.822763 +vt 3.956937 0.990430 +vt 1.880274 0.541093 +vt 1.894613 0.541093 +vt 1.894615 0.560769 +vt 1.880271 0.560769 +vt 1.880271 0.585741 +vt 1.880276 0.566056 +vt 1.894611 0.566056 +vt 1.894615 0.585741 +vt 1.880274 0.541093 +vt 1.880285 0.535809 +vt 1.894601 0.535809 +vt 1.894613 0.541093 +vt 1.899927 0.585758 +vt 1.894615 0.585741 +vt 1.894611 0.566056 +vt 1.899927 0.566039 +vt 1.894615 0.560769 +vt 1.894611 0.566056 +vt 1.880276 0.566056 +vt 1.880271 0.560769 +vt 1.874960 0.566039 +vt 1.880276 0.566056 +vt 1.880271 0.585741 +vt 1.874960 0.585758 +vt 1.151943 0.966273 +vt 1.151961 0.984813 +vt 1.118294 0.984845 +vt 1.118276 0.966204 +vt 1.178138 0.965761 +vt 1.178157 0.984787 +vt 1.204440 0.965590 +vt 1.204459 0.984762 +vt 1.232801 0.965563 +vt 1.232820 0.984734 +vt 1.038622 0.965136 +vt 1.038641 0.984922 +vt 1.014053 0.984946 +vt 1.012148 0.965162 +vt 1.091724 0.984871 +vt 0.041455 0.852946 +vt 0.041294 0.782869 +vt 0.052250 0.798920 +vt 0.052336 0.836845 +vt 0.347561 0.949223 +vt 0.347663 0.961543 +vt 0.320590 0.961766 +vt 0.320488 0.949446 +vt 0.293517 0.961990 +vt 0.293415 0.949670 +vt 0.266443 0.962214 +vt 0.266341 0.949894 +vt 0.239370 0.962438 +vt 0.239268 0.950118 +vt 0.455855 0.948328 +vt 0.455957 0.960648 +vt 0.428883 0.960871 +vt 0.428781 0.948551 +vt 0.401810 0.961095 +vt 0.401708 0.948775 +vt 0.374737 0.961319 +vt 0.374635 0.948999 +vt 1.613012 0.566762 +vt 1.612860 0.558477 +vt 1.627604 0.558206 +vt 1.627756 0.566490 +vt 1.513593 0.568591 +vt 1.513441 0.560307 +vt 1.527838 0.560042 +vt 1.527991 0.568326 +vt 1.527991 0.568326 +vt 1.527838 0.560042 +vt 1.542742 0.559767 +vt 1.542895 0.568052 +vt 1.556928 0.567794 +vt 1.556776 0.559509 +vt 1.570454 0.559258 +vt 1.570606 0.567542 +vt 1.570606 0.567542 +vt 1.570454 0.559258 +vt 1.583267 0.559022 +vt 1.583420 0.567306 +vt 1.583420 0.567306 +vt 1.583267 0.559022 +vt 1.597825 0.558754 +vt 1.597978 0.567038 +vt 1.597978 0.567038 +vt 1.597825 0.558754 +vt 1.612860 0.558477 +vt 1.613012 0.566762 +vt 1.628124 0.586467 +vt 1.628284 0.595150 +vt 1.613540 0.595421 +vt 1.613380 0.586739 +vt 1.613380 0.586739 +vt 1.613540 0.595421 +vt 1.598505 0.595698 +vt 1.598345 0.587015 +vt 1.598345 0.587015 +vt 1.598505 0.595698 +vt 1.583947 0.595966 +vt 1.583787 0.587283 +vt 1.583787 0.587283 +vt 1.583947 0.595966 +vt 1.571134 0.596202 +vt 1.570974 0.587519 +vt 1.570974 0.587519 +vt 1.571134 0.596202 +vt 1.557456 0.596453 +vt 1.557296 0.587771 +vt 1.557296 0.587771 +vt 1.557456 0.596453 +vt 1.543422 0.596712 +vt 1.543262 0.588029 +vt 1.543262 0.588029 +vt 1.543422 0.596712 +vt 1.528518 0.596986 +vt 1.528358 0.588303 +vt 1.528358 0.588303 +vt 1.528518 0.596986 +vt 1.514121 0.597251 +vt 1.513961 0.588568 +vt 1.612860 0.558477 +vt 1.612825 0.556578 +vt 1.627569 0.556307 +vt 1.627604 0.558206 +vt 1.627604 0.558206 +vt 1.627569 0.556307 +vt 1.642140 0.556039 +vt 1.642175 0.557938 +vt 1.642175 0.557938 +vt 1.642140 0.556039 +vt 1.656962 0.555766 +vt 1.656997 0.557665 +vt 1.670449 0.557417 +vt 1.670414 0.555518 +vt 1.684206 0.555265 +vt 1.684240 0.557164 +vt 1.684240 0.557164 +vt 1.684206 0.555265 +vt 1.698385 0.555004 +vt 1.698421 0.556903 +vt 1.482951 0.560868 +vt 1.497922 0.558693 +vt 1.497957 0.560592 +vt 1.497957 0.560592 +vt 1.497922 0.558693 +vt 1.513406 0.558408 +vt 1.513441 0.560307 +vt 1.513441 0.560307 +vt 1.513406 0.558408 +vt 1.527803 0.558143 +vt 1.527838 0.560042 +vt 1.527838 0.560042 +vt 1.527803 0.558143 +vt 1.542707 0.557869 +vt 1.542742 0.559767 +vt 1.542742 0.559767 +vt 1.542707 0.557869 +vt 1.556741 0.557610 +vt 1.556776 0.559509 +vt 1.556776 0.559509 +vt 1.556741 0.557610 +vt 1.570419 0.557359 +vt 1.570454 0.559258 +vt 1.570454 0.559258 +vt 1.570419 0.557359 +vt 1.583232 0.557123 +vt 1.583267 0.559022 +vt 1.583267 0.559022 +vt 1.583232 0.557123 +vt 1.597790 0.556855 +vt 1.597825 0.558754 +vt 1.597825 0.558754 +vt 1.597790 0.556855 +vt 1.612825 0.556578 +vt 1.628284 0.595150 +vt 1.628316 0.596895 +vt 1.613572 0.597166 +vt 1.613540 0.595421 +vt 1.613540 0.595421 +vt 1.613572 0.597166 +vt 1.598537 0.597443 +vt 1.598505 0.595698 +vt 1.598505 0.595698 +vt 1.598537 0.597443 +vt 1.583979 0.597710 +vt 1.583947 0.595966 +vt 1.583947 0.595966 +vt 1.583979 0.597710 +vt 1.571166 0.597946 +vt 1.571134 0.596202 +vt 1.571134 0.596202 +vt 1.571166 0.597946 +vt 1.557488 0.598198 +vt 1.557456 0.596453 +vt 1.557456 0.596453 +vt 1.557488 0.598198 +vt 1.543454 0.598456 +vt 1.543422 0.596712 +vt 1.543422 0.596712 +vt 1.543454 0.598456 +vt 1.528550 0.598731 +vt 1.528518 0.596986 +vt 1.528518 0.596986 +vt 1.528550 0.598731 +vt 1.514153 0.598995 +vt 1.514121 0.597251 +vt 1.514121 0.597251 +vt 1.514153 0.598995 +vt 1.498669 0.599280 +vt 1.498637 0.597536 +vt 1.498637 0.597536 +vt 1.498669 0.599280 +vt 1.483663 0.599557 +vt 1.483631 0.597812 +vt 1.699100 0.593847 +vt 1.699132 0.595591 +vt 1.684952 0.595852 +vt 1.684920 0.594108 +vt 1.684920 0.594108 +vt 1.684952 0.595852 +vt 1.671161 0.596106 +vt 1.671129 0.594361 +vt 1.657677 0.594609 +vt 1.657709 0.596354 +vt 1.642887 0.596626 +vt 1.642854 0.594882 +vt 0.936439 0.572867 +vt 0.936439 0.593358 +vt 0.932941 0.593358 +vt 0.932941 0.572867 +vt 0.791431 0.736062 +vt 0.791431 0.684576 +vt 0.978612 0.593111 +vt 0.985470 0.593358 +vt 0.985470 0.613849 +vt 0.978612 0.614013 +vt 0.978612 0.614013 +vt 0.985470 0.613849 +vt 0.985470 0.634339 +vt 0.978612 0.634422 +vt 0.978612 0.634422 +vt 0.985470 0.634339 +vt 0.985470 0.654830 +vt 0.978612 0.654830 +vt 0.978612 0.531886 +vt 0.985470 0.531886 +vt 0.985470 0.552377 +vt 0.978612 0.552294 +vt 0.978612 0.552294 +vt 0.985470 0.552377 +vt 0.985470 0.572867 +vt 0.978612 0.572703 +vt 0.978612 0.572703 +vt 0.985470 0.572867 +vt 0.985470 0.593358 +vt 0.978612 0.593111 +vt 0.989519 0.593358 +vt 0.992030 0.593358 +vt 0.992030 0.613849 +vt 0.989519 0.613849 +vt 0.989519 0.613849 +vt 0.992030 0.613849 +vt 0.992030 0.634339 +vt 0.989519 0.634339 +vt 0.989519 0.634339 +vt 0.992030 0.634339 +vt 0.992030 0.654830 +vt 0.989519 0.654830 +vt 0.989519 0.531886 +vt 0.992030 0.531886 +vt 0.992030 0.552377 +vt 0.989519 0.552377 +vt 0.989519 0.552377 +vt 0.992030 0.552377 +vt 0.992030 0.572867 +vt 0.989519 0.572867 +vt 0.989519 0.572867 +vt 0.992030 0.572867 +vt 0.992030 0.593358 +vt 0.989519 0.593358 +vt 0.985470 0.593358 +vt 0.989519 0.593358 +vt 0.989519 0.613849 +vt 0.985470 0.613849 +vt 0.985470 0.613849 +vt 0.989519 0.613849 +vt 0.989519 0.634339 +vt 0.985470 0.634339 +vt 0.985470 0.634339 +vt 0.989519 0.634339 +vt 0.989519 0.654830 +vt 0.985470 0.654830 +vt 0.985470 0.531886 +vt 0.989519 0.531886 +vt 0.989519 0.552377 +vt 0.985470 0.552377 +vt 0.985470 0.552377 +vt 0.989519 0.552377 +vt 0.989519 0.572867 +vt 0.985470 0.572867 +vt 0.985470 0.572867 +vt 0.989519 0.572867 +vt 0.989519 0.593358 +vt 0.985470 0.593358 +vt 0.997477 0.613849 +vt 0.992030 0.613849 +vt 0.992030 0.593358 +vt 0.997477 0.593358 +vt 0.997477 0.593358 +vt 0.992030 0.593358 +vt 0.992030 0.572867 +vt 0.997477 0.572867 +vt 0.997477 0.572867 +vt 0.992030 0.572867 +vt 0.992030 0.552377 +vt 0.997477 0.552377 +vt 0.997477 0.552377 +vt 0.992030 0.552377 +vt 0.992030 0.531886 +vt 0.997477 0.531886 +vt 0.997477 0.654830 +vt 0.992030 0.654830 +vt 0.992030 0.634339 +vt 0.997477 0.634339 +vt 0.997477 0.634339 +vt 0.992030 0.634339 +vt 0.992030 0.613849 +vt 0.945781 0.572867 +vt 0.964273 0.572703 +vt 0.964273 0.593111 +vt 0.945781 0.593358 +vt 0.945781 0.613849 +vt 0.936439 0.613848 +vt 0.936439 0.593358 +vt 0.945781 0.593358 +vt 0.945781 0.593358 +vt 0.936439 0.593358 +vt 0.936439 0.572867 +vt 0.945781 0.572867 +vt 0.945781 0.572867 +vt 0.936439 0.572867 +vt 0.936439 0.552377 +vt 0.945781 0.552377 +vt 0.945781 0.552377 +vt 0.936439 0.552377 +vt 0.936439 0.531886 +vt 0.945781 0.531886 +vt 0.945781 0.654830 +vt 0.936439 0.654830 +vt 0.936439 0.634339 +vt 0.945781 0.634339 +vt 0.945781 0.634339 +vt 0.936439 0.634339 +vt 0.936439 0.613848 +vt 0.945781 0.613849 +vt 0.979362 0.691436 +vt 0.964001 0.691436 +vt 0.963987 0.689810 +vt 0.979348 0.689810 +vt 0.964001 0.691436 +vt 0.948867 0.692212 +vt 0.948957 0.689810 +vt 0.963987 0.689810 +vt 0.979067 0.658221 +vt 0.979348 0.689810 +vt 0.963987 0.689810 +vt 0.963706 0.658221 +vt 0.963706 0.658221 +vt 0.963987 0.689810 +vt 0.948957 0.689810 +vt 0.948957 0.658221 +vt 1.787461 0.635838 +vt 1.790486 0.635883 +vt 1.790486 0.644521 +vt 1.787461 0.644547 +vt 1.790486 0.652704 +vt 1.787461 0.652662 +vt 1.790486 0.661424 +vt 1.787461 0.661373 +vt 1.790486 0.669213 +vt 1.787461 0.669213 +vt 1.790486 0.677249 +vt 1.787461 0.677249 +vt 1.787461 0.611351 +vt 1.790486 0.611351 +vt 1.790486 0.618762 +vt 1.787461 0.618762 +vt 1.790486 0.627702 +vt 1.787461 0.627702 +vt 0.820639 0.577859 +vt 0.819068 0.574477 +vt 0.838023 0.574485 +vt 0.836431 0.577819 +vt 0.836431 0.577819 +vt 0.838023 0.574485 +vt 0.850694 0.586961 +vt 0.847119 0.588558 +vt 0.847119 0.588558 +vt 0.850694 0.586961 +vt 0.850748 0.605962 +vt 0.847216 0.604497 +vt 0.847216 0.604497 +vt 0.850748 0.605962 +vt 0.838228 0.618611 +vt 0.836657 0.615142 +vt 0.836657 0.615142 +vt 0.838228 0.618611 +vt 0.819383 0.618676 +vt 0.820841 0.615024 +vt 0.820841 0.615024 +vt 0.819383 0.618676 +vt 0.806807 0.606156 +vt 0.810164 0.604546 +vt 0.810164 0.604546 +vt 0.806807 0.606156 +vt 0.806580 0.587149 +vt 0.810070 0.588715 +vt 0.810070 0.588715 +vt 0.806580 0.587149 +vt 0.819068 0.574477 +vt 0.820639 0.577859 +vt 0.826167 0.590676 +vt 0.820639 0.577859 +vt 0.836431 0.577819 +vt 0.831048 0.590663 +vt 0.831048 0.590663 +vt 0.836431 0.577819 +vt 0.847119 0.588558 +vt 0.834520 0.594115 +vt 0.834520 0.594115 +vt 0.847119 0.588558 +vt 0.847216 0.604497 +vt 0.834548 0.599011 +vt 0.834548 0.599011 +vt 0.847216 0.604497 +vt 0.836657 0.615142 +vt 0.831117 0.602483 +vt 0.831117 0.602483 +vt 0.836657 0.615142 +vt 0.820841 0.615024 +vt 0.826236 0.602496 +vt 0.826236 0.602496 +vt 0.820841 0.615024 +vt 0.810164 0.604546 +vt 0.822765 0.599043 +vt 0.822765 0.599043 +vt 0.810164 0.604546 +vt 0.810070 0.588715 +vt 0.822736 0.594147 +vt 0.822736 0.594147 +vt 0.810070 0.588715 +vt 0.820639 0.577859 +vt 0.826167 0.590676 +vt 0.712529 0.749275 +vt 0.722815 0.749275 +vt 0.722815 0.796196 +vt 0.712529 0.796196 +vt 0.712529 0.796196 +vt 0.722815 0.796196 +vt 0.722815 0.821096 +vt 0.712529 0.687755 +vt 0.722815 0.687755 +vt 0.722815 0.713461 +vt 0.712529 0.713461 +vt 0.712529 0.713461 +vt 0.722815 0.713461 +vt 0.722815 0.749275 +vt 0.712529 0.749275 +vt 0.875811 0.987949 +vt 0.869054 0.971224 +vt 1.857248 0.809226 +vt 1.868492 0.837058 +vt 0.963142 0.507307 +vt 0.959462 0.507483 +vt 0.953100 0.466601 +vt 0.956692 0.465842 +vt 0.975459 0.514343 +vt 0.975659 0.517915 +vt 0.971030 0.518012 +vt 0.974988 0.462717 +vt 0.978737 0.462523 +vt 0.985318 0.504333 +vt 0.981663 0.505121 +vt 0.953100 0.466601 +vt 0.935331 0.471062 +vt 0.918600 0.436844 +vt 0.941483 0.425665 +vt 0.945891 0.513500 +vt 0.956592 0.511103 +vt 0.953322 0.516859 +vt 0.953100 0.466601 +vt 0.959462 0.507483 +vt 0.941512 0.510082 +vt 0.935331 0.471062 +vt 0.918600 0.436844 +vt 0.905937 0.424989 +vt 0.935117 0.407189 +vt 0.941483 0.425665 +vt 0.928188 0.391026 +vt 0.935117 0.407189 +vt 0.905937 0.424989 +vt 0.886744 0.406160 +vt 0.526626 0.783518 +vt 0.500841 0.780471 +vt 0.500841 0.780471 +vt 0.526626 0.783518 +vt 0.631173 0.764652 +vt 0.630058 0.699236 +vt 0.660792 0.717479 +vt 0.661496 0.745082 +vt 0.661496 0.745082 +vt 0.631173 0.764652 +vt 0.631173 0.764652 +vt 0.661496 0.745082 +vt 0.631173 0.764652 +vt 0.597737 0.776562 +vt 0.597737 0.776562 +vt 0.631173 0.764652 +vt 0.450798 0.706790 +vt 0.450798 0.706790 +vt 0.452967 0.728116 +vt 0.452967 0.728116 +vt 0.558627 0.607968 +vt 0.596866 0.612516 +vt 0.596866 0.612516 +vt 0.558627 0.607968 +vt 0.596866 0.612516 +vt 0.637337 0.616972 +vt 0.637337 0.616972 +vt 0.596866 0.612516 +vt 0.462399 0.748874 +vt 0.462399 0.748874 +vt 0.476409 0.768140 +vt 0.476409 0.768140 +vt 0.504547 0.612629 +vt 0.504547 0.612629 +vt 0.489568 0.620042 +vt 0.489568 0.620042 +vt 0.452967 0.728116 +vt 0.452967 0.728116 +vt 0.462399 0.748874 +vt 0.462399 0.748874 +vt 0.532640 0.607381 +vt 0.532640 0.607381 +vt 0.504547 0.612629 +vt 0.504547 0.612629 +vt 0.464575 0.645515 +vt 0.464575 0.645515 +vt 0.454897 0.674918 +vt 0.454897 0.674918 +vt 0.476409 0.768140 +vt 0.476409 0.768140 +vt 0.500841 0.780471 +vt 0.500841 0.780471 +vt 0.489568 0.620042 +vt 0.489568 0.620042 +vt 0.475518 0.632309 +vt 0.475518 0.632309 +vt 0.526626 0.783518 +vt 0.526626 0.783518 +vt 0.565573 0.782724 +vt 0.565573 0.782724 +vt 0.597737 0.776562 +vt 0.565573 0.782724 +vt 0.565573 0.782724 +vt 0.597737 0.776562 +vt 0.454897 0.674918 +vt 0.454897 0.674918 +vt 0.450798 0.706790 +vt 0.450798 0.706790 +vt 0.630058 0.699236 +vt 0.660792 0.717479 +vt 0.660792 0.717479 +vt 0.630058 0.699236 +vt 0.637337 0.616972 +vt 0.641785 0.651360 +vt 0.641785 0.651360 +vt 0.637337 0.616972 +vt 0.532640 0.607381 +vt 0.558627 0.607968 +vt 0.558627 0.607968 +vt 0.532640 0.607381 +vt 0.475518 0.632309 +vt 0.475518 0.632309 +vt 0.464575 0.645515 +vt 0.464575 0.645515 +vt 0.641785 0.651360 +vt 0.630058 0.699236 +vt 0.630058 0.699236 +vt 0.641785 0.651360 +vt 0.660792 0.717479 +vt 0.661496 0.745082 +vt 0.661496 0.745082 +vt 0.660792 0.717479 +vt 3.838417 0.629355 +vt 3.847316 0.620416 +vt 3.883001 0.620895 +vt 3.891823 0.630072 +vt 3.858914 0.615670 +vt 3.871445 0.615838 +vt 0.882754 0.675465 +vt 0.871958 0.679834 +vt 0.858635 0.680042 +vt 0.847085 0.674987 +vt 0.891651 0.666530 +vt 0.838888 0.666443 +vt 0.939801 0.709338 +vt 0.939803 0.691444 +vt 0.776324 0.740044 +vt 0.776324 0.680594 +vt 0.026226 0.863725 +vt 0.026016 0.772165 +vt 0.006960 0.867541 +vt 0.006733 0.768437 +vt 0.475514 0.113873 +vt 0.463662 0.116085 +vt 0.462963 0.154364 +vt 0.474805 0.152050 +vt 0.477040 0.074678 +vt 0.467179 0.077787 +vt 0.463662 0.116085 +vt 0.475514 0.113873 +vt 0.474805 0.152050 +vt 0.462963 0.154364 +vt 0.461444 0.191712 +vt 0.470793 0.191393 +vt 0.477234 0.050396 +vt 0.468417 0.051363 +vt 0.467179 0.077787 +vt 0.477040 0.074678 +vt 0.469620 0.218039 +vt 0.463855 0.219468 +vt 0.464494 0.235959 +vt 0.469244 0.237231 +vt 0.470793 0.191393 +vt 0.461444 0.191712 +vt 0.463855 0.219468 +vt 0.469620 0.218039 +vt 0.469244 0.237231 +vt 0.464494 0.235959 +vt 0.464726 0.279813 +vt 0.470069 0.279726 +vt 0.476861 0.018904 +vt 0.466265 0.026117 +vt 0.468417 0.051363 +vt 0.477234 0.050396 +vt 1.981228 0.796580 +vt 1.981343 0.741059 +vt 1.942279 0.723767 +vt 1.941700 0.788339 +vt 1.901950 0.723781 +vt 1.902173 0.788339 +vt 1.862950 0.741047 +vt 1.862839 0.796665 +vt 0.862839 0.796665 +vt 0.902173 0.788339 +vt 0.901950 0.723781 +vt 0.862950 0.741047 +vt 0.941700 0.788339 +vt 0.942279 0.723767 +vt 0.981228 0.796580 +vt 0.981343 0.741059 +vt 1.862839 0.796665 +vt 1.862950 0.741047 +vt 1.855536 0.734860 +vt 1.855120 0.796308 +vt 1.981343 0.741059 +vt 1.981228 0.796580 +vt 1.989729 0.796687 +vt 1.989631 0.734959 +vt 0.844269 0.970059 +vt 0.840070 0.973577 +vt 0.839588 0.978994 +vt 0.846439 0.976789 +vt 1.853430 0.974539 +vt 1.849805 0.970333 +vt 1.844269 0.970059 +vt 1.846439 0.976789 +vt 0.849805 0.970333 +vt 0.853430 0.974539 +vt 1.840070 0.973577 +vt 1.839588 0.978994 +vt 1.843067 0.983246 +vt 1.848652 0.983663 +vt 1.852944 0.980064 +vt 0.843067 0.983246 +vt 0.848652 0.983663 +vt 0.852944 0.980064 +vt 1.956937 0.990430 +vt 1.964659 0.990613 +vt 1.960512 0.822945 +vt 1.952790 0.822763 +vt 1.964659 0.990613 +vt 1.986306 0.990450 +vt 1.982158 0.822781 +vt 1.960512 0.822945 +vt 1.986306 0.990450 +vt 1.994010 0.989886 +vt 1.989862 0.822218 +vt 1.982158 0.822781 +vt 1.935309 0.991339 +vt 1.956937 0.990430 +vt 1.952790 0.822763 +vt 1.931161 0.823668 +vt 0.956937 0.990430 +vt 0.952790 0.822763 +vt 0.960512 0.822945 +vt 0.964659 0.990613 +vt 0.964659 0.990613 +vt 0.960512 0.822945 +vt 0.982158 0.822781 +vt 0.986306 0.990450 +vt 0.986306 0.990450 +vt 0.982158 0.822781 +vt 0.989862 0.822218 +vt 0.994010 0.989886 +vt 0.935309 0.991339 +vt 0.931161 0.823668 +vt 0.952790 0.822763 +vt 0.956937 0.990430 +vt 0.869549 0.494493 +vt 0.685876 0.470732 +vt 0.686956 0.503877 +vt 0.845515 0.545239 +vt 0.858857 0.533495 +vt 0.699936 0.535074 +vt 0.865099 0.357931 +vt 0.681322 0.330970 +vt 0.682548 0.368587 +vt 0.867157 0.421069 +vt 0.683656 0.402609 +vt 0.684743 0.435968 +vt 0.863161 0.273323 +vt 0.678978 0.276933 +vt 0.680251 0.298094 +vt 0.489619 0.321696 +vt 0.470613 0.322225 +vt 0.471613 0.341395 +vt 0.535821 0.320678 +vt 0.518922 0.329838 +vt 0.525635 0.340736 +vt 0.764773 0.794808 +vt 0.777260 0.759770 +vt 0.862351 0.052066 +vt 0.679553 0.048633 +vt 0.679553 0.081796 +vt 0.836677 0.002131 +vt 0.691510 0.017031 +vt 0.850394 0.013434 +vt 0.862351 0.188698 +vt 0.679553 0.183992 +vt 0.679553 0.221628 +vt 0.862351 0.125527 +vt 0.679552 0.116577 +vt 0.679553 0.149953 +vt 0.679553 0.254521 +vt 0.488258 0.237141 +vt 0.469620 0.218039 +vt 0.469244 0.237231 +vt 0.534467 0.236654 +vt 0.523633 0.216939 +vt 0.517279 0.228049 +vt 1.764773 0.794808 +vt 1.777260 0.759770 +vt 0.526575 0.441997 +vt 0.527814 0.480018 +vt 0.525447 0.407375 +vt 0.528647 0.505581 +vt 0.529865 0.542964 +vt 0.549395 0.279043 +vt 0.524206 0.369307 +vt 0.470069 0.279726 +vt 0.481435 0.279541 +vt 1.773548 0.788447 +vt 1.794554 0.804641 +vt 1.810287 0.776335 +vt 1.792986 0.760963 +vt 1.767225 0.804455 +vt 1.785301 0.819129 +vt 1.794554 0.804641 +vt 1.773548 0.788447 +vt 1.811318 0.732380 +vt 1.842490 0.758277 +vt 1.857822 0.684972 +vt 1.807952 0.688354 +vt 1.792986 0.760963 +vt 1.810287 0.776335 +vt 0.480897 0.445316 +vt 0.483699 0.484442 +vt 0.478945 0.407182 +vt 0.484683 0.508705 +vt 0.485336 0.540193 +vt 0.473654 0.367990 +vt 0.007228 0.696445 +vt 0.119097 0.704237 +vt 0.122549 0.656233 +vt 0.010633 0.652337 +vt 0.003656 0.747731 +vt 0.115658 0.753608 +vt 0.231007 0.711437 +vt 0.234486 0.661460 +vt 0.122578 0.652637 +vt 0.122635 0.602044 +vt 0.010442 0.602044 +vt 0.010500 0.652637 +vt 0.028796 0.757590 +vt 0.114950 0.763773 +vt 0.227568 0.760808 +vt 0.342960 0.718029 +vt 0.346443 0.668053 +vt 0.234656 0.654561 +vt 0.234828 0.602638 +vt 0.122635 0.552394 +vt 0.010560 0.550570 +vt 0.062947 0.849104 +vt 0.108784 0.852297 +vt 0.110904 0.821863 +vt 0.061147 0.817481 +vt 0.226860 0.770973 +vt 0.339521 0.767400 +vt 0.438412 0.723653 +vt 0.407538 0.672188 +vt 0.346734 0.658459 +vt 0.347022 0.603842 +vt 0.234828 0.552988 +vt 0.122635 0.542227 +vt 0.036628 0.541905 +vt 0.222813 0.829063 +vt 0.224120 0.810314 +vt 0.112209 0.803114 +vt 0.338813 0.777565 +vt 0.451460 0.774172 +vt 0.449077 0.613331 +vt 0.441013 0.606700 +vt 0.408643 0.660136 +vt 0.414803 0.663012 +vt 0.408643 0.660136 +vt 0.441013 0.606700 +vt 0.347022 0.554192 +vt 0.234828 0.542822 +vt 0.122635 0.502880 +vt 0.122635 0.484280 +vt 0.072599 0.485738 +vt 0.067729 0.503711 +vt 0.334766 0.835655 +vt 0.336072 0.816907 +vt 0.450752 0.784337 +vt 0.468300 0.556341 +vt 0.458729 0.555094 +vt 0.449465 0.611753 +vt 0.458729 0.555094 +vt 0.347022 0.544025 +vt 0.234828 0.503474 +vt 0.234828 0.484874 +vt 0.435030 0.841614 +vt 0.448012 0.823679 +vt 0.467840 0.545989 +vt 0.458499 0.545633 +vt 0.458499 0.545633 +vt 0.347022 0.504678 +vt 0.347022 0.486078 +vt 0.466944 0.501505 +vt 0.447451 0.487014 +vt 0.458683 0.504904 +vt 0.458683 0.504904 +vt 0.447451 0.487014 +vt 0.008432 0.542215 +vt 0.010560 0.550570 +vt 0.036628 0.541905 +vt 0.031953 0.534333 +vt 0.067729 0.503711 +vt 0.059617 0.498005 +vt 0.076831 0.448786 +vt 0.071413 0.450752 +vt 0.063922 0.484353 +vt 0.072599 0.485738 +vt 0.220694 0.859497 +vt 0.332646 0.866089 +vt 0.234965 0.454150 +vt 0.122122 0.451058 +vt 0.413957 0.870728 +vt 0.347024 0.455886 +vt 0.428376 0.456721 +vt 0.058035 0.799341 +vt 0.076831 0.448786 +vt 1.007350 0.433571 +vt 1.361111 0.429252 +vt 1.359604 0.377913 +vt 1.007350 0.377143 +vt 1.008858 0.429252 +vt 1.415695 0.429252 +vt 1.443716 0.379205 +vt 1.444812 0.062060 +vt 1.442509 0.117159 +vt 1.447494 0.117159 +vt 1.449332 0.062060 +vt 1.359604 0.064109 +vt 1.359604 0.113625 +vt 1.442509 0.117159 +vt 1.444812 0.062060 +vt 1.359604 0.012457 +vt 1.007350 0.012457 +vt 1.007350 0.062893 +vt 1.359604 0.226016 +vt 1.007350 0.223041 +vt 1.007350 0.273165 +vt 1.359604 0.273993 +vt 1.007350 0.164184 +vt 1.007350 0.219056 +vt 1.360039 0.219522 +vt 1.359604 0.165360 +vt 1.411958 0.221276 +vt 1.417218 0.224158 +vt 1.444355 0.160955 +vt 1.440307 0.160955 +vt 1.408423 0.225906 +vt 1.424813 0.274675 +vt 1.411958 0.221276 +vt 1.440307 0.160955 +vt 1.359604 0.326053 +vt 1.007350 0.325257 +vt 1.007350 0.112438 +vt 1.442099 0.327051 +vt 1.417122 0.012457 +vt 1.619799 0.822990 +vt 1.602982 0.814799 +vt 1.556521 0.990202 +vt 1.649324 0.985311 +vt 1.701728 0.978157 +vt 1.727590 0.833933 +vt 1.682262 0.899936 +vt 1.677748 0.976969 +vt 1.843743 0.951051 +vt 1.839120 0.949321 +vt 1.832190 0.961777 +vt 1.836182 0.964853 +vt 1.864233 0.869956 +vt 1.859622 0.870344 +vt 1.826082 0.827429 +vt 1.741088 0.816903 +vt 1.742659 0.826246 +vt 1.825098 0.833812 +vt 1.682262 0.899936 +vt 1.727590 0.833933 +vt 1.721259 0.826124 +vt 1.671031 0.894589 +vt 1.806581 0.983761 +vt 1.808887 0.988109 +vt 1.850605 0.840104 +vt 1.847832 0.843279 +vt 1.847832 0.843279 +vt 1.850605 0.840104 +vt 1.832190 0.961777 +vt 1.847832 0.843279 +vt 1.825098 0.833812 +vt 1.806581 0.983761 +vt 1.787706 0.983066 +vt 1.742659 0.826246 +vt 1.474077 0.865561 +vt 1.497494 0.952154 +vt 1.505137 0.965983 +vt 1.486724 0.833848 +vt 1.511976 0.821328 +vt 1.535132 0.991677 +vt 1.839120 0.949321 +vt 1.859622 0.870344 +vt 0.151943 0.966273 +vt 0.151914 0.936365 +vt 0.118246 0.934817 +vt 0.118276 0.966204 +vt 0.178138 0.965761 +vt 0.178109 0.936030 +vt 0.204440 0.965590 +vt 0.204412 0.935862 +vt 0.232801 0.965563 +vt 0.232773 0.935835 +vt 0.038622 0.965136 +vt 0.038592 0.934188 +vt 0.010231 0.934215 +vt 0.012148 0.965162 +vt 0.065148 0.965369 +vt 0.065118 0.934322 +vt 0.091706 0.965741 +vt 0.091676 0.934542 +vt 0.151897 0.918153 +vt 0.118231 0.919298 +vt 0.118246 0.934817 +vt 0.151914 0.936365 +vt 0.178092 0.917926 +vt 0.178109 0.936030 +vt 0.204394 0.917878 +vt 0.204412 0.935862 +vt 0.232755 0.917851 +vt 0.232773 0.935835 +vt 0.038578 0.918885 +vt 0.010216 0.918912 +vt 0.010231 0.934215 +vt 0.038592 0.934188 +vt 0.065103 0.918970 +vt 0.065118 0.934322 +vt 0.091661 0.919114 +vt 0.091676 0.934542 +vt 0.151886 0.907159 +vt 0.118220 0.908326 +vt 0.118231 0.919298 +vt 0.151897 0.918153 +vt 0.178081 0.906997 +vt 0.178092 0.917926 +vt 0.204384 0.906970 +vt 0.204394 0.917878 +vt 0.232745 0.906943 +vt 0.232755 0.917851 +vt 0.038567 0.908066 +vt 0.010206 0.908094 +vt 0.010216 0.918912 +vt 0.038578 0.918885 +vt 0.065093 0.908117 +vt 0.065103 0.918970 +vt 0.091650 0.908208 +vt 0.091661 0.919114 +vt 0.151864 0.884126 +vt 0.118197 0.884159 +vt 0.118220 0.908326 +vt 0.151886 0.907159 +vt 0.178059 0.884101 +vt 0.178081 0.906997 +vt 0.204361 0.884075 +vt 0.204384 0.906970 +vt 0.232723 0.884048 +vt 0.232745 0.906943 +vt 0.038544 0.884236 +vt 0.010183 0.884263 +vt 0.010206 0.908094 +vt 0.038567 0.908066 +vt 0.065070 0.884210 +vt 0.065093 0.908117 +vt 0.091627 0.884184 +vt 0.091650 0.908208 +vt 1.319964 0.882191 +vt 1.320134 0.902752 +vt 1.347219 0.902528 +vt 1.347049 0.881967 +vt 1.292880 0.882414 +vt 1.293050 0.902975 +vt 1.265796 0.882638 +vt 1.265966 0.903199 +vt 1.238712 0.882862 +vt 1.238882 0.903423 +vt 1.428215 0.881296 +vt 1.428385 0.901857 +vt 1.455469 0.901633 +vt 1.455299 0.881072 +vt 1.401217 0.881519 +vt 1.401387 0.902080 +vt 1.428385 0.901857 +vt 1.428215 0.881296 +vt 1.374133 0.881743 +vt 1.374303 0.902304 +vt 1.401387 0.902080 +vt 1.401217 0.881519 +vt 1.320333 0.930704 +vt 1.320488 0.949446 +vt 1.347561 0.949223 +vt 1.347406 0.930480 +vt 1.293260 0.930928 +vt 1.293415 0.949670 +vt 1.266186 0.931151 +vt 1.266341 0.949894 +vt 1.239113 0.931375 +vt 1.239268 0.950118 +vt 1.428626 0.929809 +vt 1.428781 0.948551 +vt 1.455855 0.948328 +vt 1.455700 0.929585 +vt 1.401553 0.930033 +vt 1.401708 0.948775 +vt 1.374480 0.930256 +vt 1.374635 0.948999 +vt 1.320590 0.961766 +vt 1.320820 0.989579 +vt 1.347893 0.989356 +vt 1.347663 0.961543 +vt 1.293517 0.961990 +vt 1.293746 0.989803 +vt 1.266443 0.962214 +vt 1.266673 0.990027 +vt 1.239370 0.962438 +vt 1.239600 0.990251 +vt 1.428883 0.960871 +vt 1.429113 0.988684 +vt 1.456187 0.988461 +vt 1.455957 0.960648 +vt 1.401810 0.961095 +vt 1.402040 0.988908 +vt 1.374737 0.961319 +vt 1.374966 0.989132 +vt 1.320218 0.916692 +vt 1.347291 0.916468 +vt 1.347219 0.902528 +vt 1.320134 0.902752 +vt 1.293144 0.916916 +vt 1.293050 0.902975 +vt 1.266071 0.917140 +vt 1.265966 0.903199 +vt 1.238997 0.917363 +vt 1.238882 0.903423 +vt 1.428510 0.915797 +vt 1.455584 0.915573 +vt 1.455469 0.901633 +vt 1.401438 0.916021 +vt 1.374364 0.916245 +vt 1.374303 0.902304 +vt 1.320333 0.930704 +vt 1.347406 0.930480 +vt 1.347291 0.916468 +vt 1.320218 0.916692 +vt 1.293260 0.930928 +vt 1.293144 0.916916 +vt 1.266186 0.931151 +vt 1.266071 0.917140 +vt 1.239113 0.931375 +vt 1.238997 0.917363 +vt 1.428626 0.929809 +vt 1.455700 0.929585 +vt 1.455584 0.915573 +vt 1.428510 0.915797 +vt 1.401553 0.930033 +vt 1.401438 0.916021 +vt 1.374480 0.930256 +vt 1.374364 0.916245 +vt 2.838425 0.629350 +vt 2.833580 0.641115 +vt 2.896551 0.641960 +vt 2.891809 0.630066 +vt 0.613012 0.566762 +vt 0.613203 0.577108 +vt 0.627946 0.576837 +vt 0.627756 0.566490 +vt 0.597978 0.567038 +vt 0.598168 0.577385 +vt 0.583420 0.567306 +vt 0.583610 0.577653 +vt 0.570606 0.567542 +vt 0.570797 0.577889 +vt 0.556928 0.567794 +vt 0.557118 0.578140 +vt 0.542895 0.568052 +vt 0.543085 0.578399 +vt 0.527991 0.568326 +vt 0.528181 0.578673 +vt 0.513593 0.568591 +vt 0.513784 0.578938 +vt 0.498110 0.568876 +vt 0.498300 0.579223 +vt 0.483103 0.569152 +vt 0.483294 0.579499 +vt 0.698573 0.565187 +vt 0.698763 0.575534 +vt 0.712688 0.575278 +vt 0.712498 0.564931 +vt 0.684393 0.565448 +vt 0.684583 0.575795 +vt 0.670601 0.565702 +vt 0.670792 0.576048 +vt 0.657149 0.565949 +vt 0.657340 0.576296 +vt 0.642327 0.566222 +vt 0.642517 0.576569 +vt 4.838881 0.666464 +vt 4.847080 0.675011 +vt 4.882653 0.675536 +vt 4.891664 0.666551 +vt 0.627756 0.566490 +vt 0.627604 0.558206 +vt 0.642327 0.566222 +vt 0.642175 0.557938 +vt 0.657149 0.565949 +vt 0.656997 0.557665 +vt 0.670601 0.565702 +vt 0.670449 0.557417 +vt 0.684393 0.565448 +vt 0.684240 0.557164 +vt 0.698573 0.565187 +vt 0.698421 0.556903 +vt 0.712498 0.564931 +vt 0.712345 0.556646 +vt 0.483103 0.569152 +vt 0.498110 0.568876 +vt 0.497957 0.560592 +vt 0.482951 0.560868 +vt 0.513593 0.568591 +vt 0.513441 0.560307 +vt 0.527991 0.568326 +vt 0.527838 0.560042 +vt 0.542895 0.568052 +vt 0.542742 0.559767 +vt 0.556928 0.567794 +vt 0.556776 0.559509 +vt 0.628124 0.586467 +vt 0.628283 0.595150 +vt 0.513961 0.588568 +vt 0.514121 0.597251 +vt 0.498477 0.588853 +vt 0.498637 0.597536 +vt 0.483471 0.589129 +vt 0.483631 0.597812 +vt 0.712866 0.584908 +vt 0.698941 0.585164 +vt 0.699100 0.593847 +vt 0.713025 0.593590 +vt 0.684761 0.585425 +vt 0.684920 0.594108 +vt 0.670969 0.585679 +vt 0.671129 0.594361 +vt 0.657517 0.585926 +vt 0.657677 0.594609 +vt 0.642695 0.586199 +vt 0.642854 0.594882 +vt 0.612860 0.558477 +vt 0.627604 0.558206 +vt 0.627569 0.556307 +vt 0.612825 0.556578 +vt 0.642175 0.557938 +vt 0.642140 0.556039 +vt 0.656997 0.557665 +vt 0.656962 0.555766 +vt 0.670449 0.557417 +vt 0.670414 0.555518 +vt 0.684240 0.557164 +vt 0.684206 0.555265 +vt 0.698421 0.556903 +vt 0.698386 0.555004 +vt 0.712345 0.556646 +vt 0.712310 0.554747 +vt 0.482951 0.560868 +vt 0.497957 0.560592 +vt 0.497922 0.558693 +vt 0.482916 0.558969 +vt 0.513441 0.560307 +vt 0.513406 0.558408 +vt 0.527838 0.560042 +vt 0.527803 0.558143 +vt 0.542742 0.559767 +vt 0.542707 0.557869 +vt 0.556776 0.559509 +vt 0.556741 0.557610 +vt 0.570454 0.559258 +vt 0.570419 0.557359 +vt 0.583267 0.559022 +vt 0.583232 0.557123 +vt 0.597825 0.558754 +vt 0.597790 0.556855 +vt 0.628283 0.595150 +vt 0.613540 0.595421 +vt 0.613572 0.597166 +vt 0.628316 0.596895 +vt 0.598505 0.595698 +vt 0.598537 0.597443 +vt 0.583947 0.595966 +vt 0.583979 0.597710 +vt 0.571134 0.596202 +vt 0.571166 0.597946 +vt 0.557455 0.596453 +vt 0.557488 0.598198 +vt 0.543422 0.596712 +vt 0.543454 0.598456 +vt 0.528518 0.596986 +vt 0.528550 0.598731 +vt 0.514121 0.597251 +vt 0.514153 0.598995 +vt 0.498637 0.597536 +vt 0.498669 0.599280 +vt 0.483631 0.597812 +vt 0.483663 0.599557 +vt 0.713025 0.593590 +vt 0.699100 0.593847 +vt 0.699132 0.595591 +vt 0.713057 0.595335 +vt 0.684920 0.594108 +vt 0.684952 0.595852 +vt 0.671129 0.594361 +vt 0.671161 0.596106 +vt 0.657677 0.594609 +vt 0.657709 0.596354 +vt 0.642854 0.594882 +vt 0.642887 0.596626 +vt 0.613380 0.586739 +vt 0.628124 0.586467 +vt 0.627946 0.576837 +vt 0.613203 0.577108 +vt 0.598345 0.587015 +vt 0.598168 0.577385 +vt 0.583787 0.587283 +vt 0.583610 0.577653 +vt 0.570974 0.587519 +vt 0.570797 0.577889 +vt 0.557296 0.587771 +vt 0.557118 0.578140 +vt 0.543262 0.588029 +vt 0.543085 0.578399 +vt 0.528359 0.588303 +vt 0.528181 0.578673 +vt 0.513961 0.588568 +vt 0.513784 0.578938 +vt 0.498477 0.588853 +vt 0.498300 0.579223 +vt 0.483471 0.589129 +vt 0.483294 0.579499 +vt 0.698941 0.585164 +vt 0.712866 0.584908 +vt 0.712688 0.575278 +vt 0.698763 0.575534 +vt 0.684761 0.585425 +vt 0.684583 0.575795 +vt 0.670969 0.585679 +vt 0.670792 0.576048 +vt 0.657517 0.585926 +vt 0.657340 0.576296 +vt 0.642695 0.586199 +vt 0.642517 0.576569 +vt 1.932941 0.613848 +vt 1.936440 0.613848 +vt 1.936440 0.593358 +vt 1.932941 0.593358 +vt 1.932941 0.634339 +vt 1.936440 0.634339 +vt 1.932941 0.654830 +vt 1.936440 0.654830 +vt 1.932941 0.552377 +vt 1.936440 0.552377 +vt 1.936440 0.531886 +vt 1.932941 0.531886 +vt 1.932941 0.572867 +vt 1.936440 0.572867 +vt 1.964272 0.614013 +vt 1.978612 0.614013 +vt 1.978612 0.593111 +vt 1.964272 0.593111 +vt 1.964272 0.634422 +vt 1.978612 0.634422 +vt 1.964272 0.654830 +vt 1.978612 0.654830 +vt 1.964272 0.552294 +vt 1.978612 0.552294 +vt 1.978612 0.531886 +vt 1.964272 0.531886 +vt 1.964272 0.572703 +vt 1.978612 0.572703 +vt 1.964272 0.614013 +vt 1.964272 0.593111 +vt 1.945781 0.593358 +vt 1.945781 0.613849 +vt 1.964272 0.634422 +vt 1.945781 0.634339 +vt 1.964272 0.654830 +vt 1.945781 0.654830 +vt 1.964272 0.552294 +vt 1.964272 0.531886 +vt 1.945781 0.531886 +vt 1.945781 0.552377 +vt 1.964272 0.572703 +vt 1.945781 0.572867 +vt 1.948957 0.689810 +vt 1.939799 0.689810 +vt 1.939803 0.691444 +vt 1.948867 0.692212 +vt 1.939799 0.658221 +vt 1.939799 0.689810 +vt 1.948957 0.689810 +vt 1.948957 0.658221 +vt 1.901649 0.531322 +vt 1.901649 0.681789 +vt 1.911924 0.681789 +vt 1.911924 0.531322 +vt 1.919678 0.681789 +vt 1.919678 0.531322 +vt 1.919678 0.531322 +vt 1.919678 0.681789 +vt 1.929541 0.681789 +vt 1.927963 0.531322 +vt 2.929541 0.681789 +vt 2.927963 0.531322 +vt 2.919678 0.531322 +vt 2.919678 0.681789 +vt 2.919678 0.681789 +vt 2.919678 0.531322 +vt 2.911924 0.531322 +vt 2.911924 0.681789 +vt 2.911924 0.681789 +vt 2.911924 0.531322 +vt 2.901649 0.531322 +vt 2.901649 0.681789 +vt 0.653557 0.635717 +vt 0.653557 0.644666 +vt 0.787461 0.644547 +vt 0.787461 0.635838 +vt 0.653557 0.652542 +vt 0.787461 0.652662 +vt 0.653557 0.661492 +vt 0.787461 0.661373 +vt 0.653557 0.669213 +vt 0.787461 0.669213 +vt 0.653557 0.677249 +vt 0.787461 0.677249 +vt 0.653557 0.611351 +vt 0.653557 0.618762 +vt 0.787461 0.618762 +vt 0.787461 0.611351 +vt 0.653557 0.627702 +vt 0.787461 0.627702 +vt 0.790486 0.644521 +vt 0.804256 0.644590 +vt 0.804256 0.635967 +vt 0.790486 0.635883 +vt 0.790486 0.652704 +vt 0.804256 0.652654 +vt 0.804256 0.644590 +vt 0.790486 0.644521 +vt 0.790486 0.661424 +vt 0.804256 0.661401 +vt 0.804256 0.652654 +vt 0.790486 0.652704 +vt 0.790486 0.669213 +vt 0.804256 0.669213 +vt 0.804256 0.661401 +vt 0.790486 0.661424 +vt 0.790486 0.677249 +vt 0.804256 0.677249 +vt 0.804256 0.669213 +vt 0.790486 0.669213 +vt 0.790486 0.618762 +vt 0.804256 0.618762 +vt 0.804256 0.611351 +vt 0.790486 0.611351 +vt 0.790486 0.627702 +vt 0.804256 0.627702 +vt 0.804256 0.618762 +vt 0.790486 0.618762 +vt 0.790486 0.635883 +vt 0.804256 0.635967 +vt 0.804256 0.627702 +vt 0.790486 0.627702 +vt 1.907472 0.257985 +vt 1.908977 0.257975 +vt 1.912623 0.051317 +vt 1.911119 0.051328 +vt 1.910712 0.257976 +vt 1.914357 0.051377 +vt 1.905738 0.257926 +vt 1.909384 0.051326 +vt 1.945508 0.023291 +vt 1.942880 0.023291 +vt 1.942142 0.289169 +vt 1.945365 0.289169 +vt 1.953900 0.023291 +vt 1.948675 0.023291 +vt 1.948937 0.289169 +vt 1.953900 0.289169 +vt 1.884426 0.003712 +vt 1.882265 0.003712 +vt 1.882265 0.306753 +vt 1.884426 0.306753 +vt 1.891725 0.003712 +vt 1.887573 0.003712 +vt 1.887573 0.306753 +vt 1.891725 0.306753 +vt 0.672500 0.806611 +vt 0.671067 0.801549 +vt 0.457033 0.801549 +vt 0.458466 0.806611 +vt 0.671087 0.796454 +vt 0.457013 0.796454 +vt 0.672521 0.811706 +vt 0.458446 0.811706 +vt 0.973814 0.134518 +vt 0.970027 0.134518 +vt 0.970027 0.286613 +vt 0.973814 0.286613 +vt 0.967029 0.134518 +vt 0.967029 0.286613 +vt 0.963765 0.134518 +vt 0.963765 0.286613 +vt 0.991477 0.134518 +vt 0.987690 0.134518 +vt 0.987690 0.286613 +vt 0.991477 0.286613 +vt 0.983886 0.134518 +vt 0.983886 0.286613 +vt 0.980889 0.134518 +vt 0.980889 0.286613 +vt 0.977618 0.134518 +vt 0.977618 0.286613 +vt 1.688848 0.749275 +vt 1.688848 0.796196 +vt 1.707262 0.796196 +vt 1.707262 0.749275 +vt 1.688848 0.821096 +vt 1.707262 0.821096 +vt 1.688848 0.687755 +vt 1.688848 0.713461 +vt 1.707262 0.713461 +vt 1.707262 0.687755 +vt 1.707262 0.749275 +vt 1.707262 0.796196 +vt 1.712529 0.796196 +vt 1.712529 0.749275 +vt 1.707262 0.796196 +vt 1.707262 0.821096 +vt 1.712529 0.821096 +vt 1.712529 0.796196 +vt 1.707262 0.687755 +vt 1.707262 0.713461 +vt 1.712529 0.713461 +vt 1.712529 0.687755 +vt 1.707262 0.713461 +vt 1.707262 0.749275 +vt 1.712529 0.749275 +vt 1.712529 0.713461 +vt 0.901273 0.987954 +vt 0.905721 0.987777 +vt 0.898839 0.809308 +vt 0.894390 0.809475 +vt 0.910255 0.987933 +vt 0.903373 0.809475 +vt 0.914763 0.987434 +vt 0.907880 0.808954 +vt 0.919212 0.987273 +vt 0.912328 0.808772 +vt 0.923660 0.987101 +vt 0.916777 0.808601 +vt 0.887785 0.988453 +vt 0.892292 0.987943 +vt 0.885410 0.809505 +vt 0.880903 0.810015 +vt 0.896825 0.988115 +vt 0.889943 0.809656 +vt 1.835327 0.793387 +vt 1.831746 0.793387 +vt 1.831746 0.820585 +vt 1.835327 0.820585 +vt 1.828119 0.793387 +vt 1.828119 0.820585 +vt 1.824470 0.793387 +vt 1.824470 0.820585 +vt 1.820887 0.793387 +vt 1.820887 0.820585 +vt 1.817304 0.793387 +vt 1.817304 0.820585 +vt 1.813656 0.793387 +vt 1.813656 0.820585 +vt 1.810030 0.793387 +vt 1.810030 0.820585 +vt 1.838908 0.793387 +vt 1.838908 0.820585 +vt 1.963142 0.507307 +vt 1.966829 0.510170 +vt 1.963771 0.464386 +vt 1.956692 0.465842 +vt 1.971978 0.514904 +vt 1.968024 0.463687 +vt 1.975459 0.514343 +vt 1.971339 0.463143 +vt 1.978725 0.509767 +vt 1.973352 0.462812 +vt 1.954252 0.423683 +vt 1.945634 0.424628 +vt 1.962774 0.422283 +vt 1.966228 0.421716 +vt 1.969417 0.421193 +vt 1.938767 0.405843 +vt 1.949923 0.403838 +vt 1.946255 0.386738 +vt 1.932504 0.389173 +vt 1.960009 0.402182 +vt 1.957710 0.384857 +vt 1.964799 0.401395 +vt 1.965586 0.383564 +vt 1.969884 0.400560 +vt 1.972329 0.382456 +vt 1.930994 0.364212 +vt 1.931040 0.379851 +vt 1.943846 0.369533 +vt 1.936570 0.333645 +vt 1.955052 0.362291 +vt 1.950086 0.315819 +vt 1.964760 0.358704 +vt 1.963966 0.306471 +vt 1.972943 0.361369 +vt 1.976111 0.317948 +vt 1.981663 0.505121 +vt 1.974988 0.462717 +vt 1.972942 0.421304 +vt 1.974470 0.399545 +vt 1.977774 0.381266 +vt 1.980256 0.366925 +vt 1.985764 0.339373 +vt 1.966829 0.510170 +vt 1.963142 0.507307 +vt 1.960727 0.510219 +vt 1.964615 0.512998 +vt 1.971978 0.514904 +vt 1.966829 0.510170 +vt 1.964615 0.512998 +vt 1.971030 0.518012 +vt 1.978725 0.509767 +vt 1.975459 0.514343 +vt 1.975659 0.517915 +vt 1.979438 0.515266 +vt 1.956692 0.465842 +vt 1.945634 0.424628 +vt 1.941483 0.425665 +vt 1.953100 0.466601 +vt 1.972942 0.421304 +vt 1.974988 0.462717 +vt 1.978737 0.462523 +vt 1.977648 0.421732 +vt 1.938767 0.405843 +vt 1.932504 0.389173 +vt 1.928188 0.391026 +vt 1.935117 0.407189 +vt 1.974470 0.399545 +vt 1.972942 0.421304 +vt 1.977648 0.421732 +vt 1.978161 0.399738 +vt 1.931040 0.379851 +vt 1.930994 0.364212 +vt 1.926746 0.365415 +vt 1.926916 0.381387 +vt 1.930994 0.364212 +vt 1.936570 0.333645 +vt 1.933515 0.332831 +vt 1.926746 0.365415 +vt 1.936570 0.333645 +vt 1.950086 0.315819 +vt 1.948466 0.313374 +vt 1.933515 0.332831 +vt 1.950086 0.315819 +vt 1.963966 0.306471 +vt 1.962437 0.303992 +vt 1.948466 0.313374 +vt 1.976111 0.317948 +vt 1.985764 0.339373 +vt 1.990410 0.338499 +vt 1.978359 0.315358 +vt 1.980256 0.366925 +vt 1.977774 0.381266 +vt 1.981269 0.381445 +vt 1.984066 0.367261 +vt 1.963966 0.306471 +vt 1.976111 0.317948 +vt 1.978359 0.315358 +vt 1.965424 0.303475 +vt 1.981663 0.505121 +vt 1.978725 0.509767 +vt 1.979438 0.515266 +vt 1.982307 0.509582 +vt 1.985764 0.339373 +vt 1.980256 0.366925 +vt 1.984066 0.367261 +vt 1.990410 0.338499 +vt 1.931040 0.379851 +vt 1.945634 0.424628 +vt 1.938767 0.405843 +vt 1.935117 0.407189 +vt 1.941483 0.425665 +vt 1.977774 0.381266 +vt 1.974470 0.399545 +vt 1.978161 0.399738 +vt 1.981269 0.381445 +vt 1.956592 0.511103 +vt 1.950077 0.517329 +vt 1.926916 0.381387 +vt 1.876822 0.376070 +vt 1.886744 0.406160 +vt 1.928188 0.391026 +vt 1.933515 0.332831 +vt 1.931160 0.313322 +vt 1.921929 0.299465 +vt 1.906951 0.304459 +vt 1.876822 0.376070 +vt 1.926916 0.381387 +vt 1.926746 0.365415 +vt 1.882738 0.329557 +vt 1.882738 0.329557 +vt 1.926746 0.365415 +vt 1.933515 0.332831 +vt 1.906951 0.304459 +vt 0.565573 0.782724 +vt 0.631173 0.764652 +vt 0.500841 0.780471 +vt 0.526626 0.783518 +vt 0.630058 0.699236 +vt 0.641785 0.651360 +vt 0.637337 0.616972 +vt 0.596866 0.612516 +vt 0.630058 0.699236 +vt 0.489568 0.620042 +vt 0.500841 0.780471 +vt 0.631173 0.764652 +vt 0.532640 0.607381 +vt 0.504547 0.612629 +vt 0.489568 0.620042 +vt 0.596866 0.612516 +vt 0.450798 0.706790 +vt 0.462399 0.748874 +vt 0.500841 0.780471 +vt 0.489568 0.620042 +vt 0.464575 0.645515 +vt 0.454897 0.674918 +vt 0.450798 0.706790 +vt 0.489568 0.620042 +vt 4.896566 0.641971 +vt 4.833570 0.641126 +vt 4.833514 0.653935 +vt 4.896511 0.654781 +vt 4.891823 0.630072 +vt 4.838417 0.629355 +vt 4.858635 0.680069 +vt 4.871963 0.679861 +vt 4.883001 0.620895 +vt 4.847316 0.620416 +vt 4.871445 0.615838 +vt 4.858914 0.615670 +vt 2.896495 0.654765 +vt 2.833525 0.653920 +vt 2.882991 0.620893 +vt 2.847321 0.620414 +vt 2.871440 0.615838 +vt 2.858914 0.615670 +vt 1.676401 0.986658 +vt 1.671031 0.894589 +vt 0.063613 0.482216 +vt 0.058323 0.502256 +vt 1.979348 0.689810 +vt 1.979362 0.691436 +vt 1.993903 0.691436 +vt 1.993911 0.689810 +vt 1.979067 0.658221 +vt 1.979348 0.689810 +vt 1.993911 0.689810 +vt 1.993909 0.658221 +vt 0.521275 0.077663 +vt 0.521275 0.115703 +vt 0.521275 0.150343 +vt 0.521275 0.052086 +vt 0.521275 0.014684 +vt 0.521275 0.188430 +vt 0.773548 0.788447 +vt 0.792986 0.760963 +vt 0.810287 0.776335 +vt 0.794554 0.804641 +vt 0.767225 0.804455 +vt 0.773548 0.788447 +vt 0.794554 0.804641 +vt 0.785301 0.819129 +vt 0.811318 0.732380 +vt 0.807952 0.688354 +vt 0.857822 0.684972 +vt 0.842490 0.758277 +vt 0.792986 0.760963 +vt 0.810287 0.776335 +vt 0.477040 0.074678 +vt 0.475514 0.113873 +vt 0.474805 0.152050 +vt 0.477234 0.050396 +vt 0.476861 0.018904 +vt 0.470793 0.191393 +vt 1.122549 0.656233 +vt 1.119097 0.704237 +vt 1.007228 0.696445 +vt 1.010633 0.652337 +vt 1.115658 0.753608 +vt 1.003656 0.747731 +vt 1.234486 0.661460 +vt 1.231007 0.711437 +vt 1.010442 0.602044 +vt 1.122635 0.602044 +vt 1.122578 0.652637 +vt 1.010500 0.652637 +vt 1.114950 0.763773 +vt 1.028796 0.757590 +vt 1.227568 0.760808 +vt 1.346443 0.668053 +vt 1.342960 0.718029 +vt 1.234828 0.602638 +vt 1.234656 0.654561 +vt 1.010560 0.550570 +vt 1.122635 0.552394 +vt 1.062947 0.849104 +vt 1.061147 0.817481 +vt 1.110904 0.821863 +vt 1.108784 0.852297 +vt 1.226860 0.770973 +vt 1.339521 0.767400 +vt 1.407538 0.672188 +vt 1.438412 0.723653 +vt 1.347022 0.603842 +vt 1.346734 0.658459 +vt 1.234828 0.552988 +vt 1.036628 0.541905 +vt 1.122635 0.542227 +vt 1.224120 0.810314 +vt 1.222813 0.829063 +vt 1.112209 0.803114 +vt 1.338813 0.777565 +vt 1.451460 0.774172 +vt 1.408643 0.660136 +vt 1.441013 0.606700 +vt 1.449077 0.613331 +vt 1.414803 0.663012 +vt 1.441013 0.606700 +vt 1.408643 0.660136 +vt 1.347022 0.554192 +vt 1.234828 0.542822 +vt 1.072599 0.485738 +vt 1.122635 0.484280 +vt 1.122635 0.502880 +vt 1.067729 0.503711 +vt 1.336072 0.816907 +vt 1.334766 0.835655 +vt 1.450752 0.784337 +vt 1.458729 0.555094 +vt 1.468300 0.556341 +vt 1.449466 0.611753 +vt 1.458729 0.555094 +vt 1.347022 0.544025 +vt 1.234828 0.484874 +vt 1.234828 0.503474 +vt 1.448012 0.823679 +vt 1.435030 0.841614 +vt 1.458499 0.545633 +vt 1.467840 0.545989 +vt 1.458499 0.545633 +vt 1.347022 0.486078 +vt 1.347022 0.504678 +vt 1.466944 0.501505 +vt 1.458683 0.504904 +vt 1.447451 0.487014 +vt 1.458683 0.504904 +vt 1.036628 0.541905 +vt 1.010560 0.550570 +vt 1.008432 0.542215 +vt 1.031953 0.534333 +vt 1.059617 0.498005 +vt 1.067729 0.503711 +vt 1.063922 0.484353 +vt 1.071413 0.450752 +vt 1.076831 0.448786 +vt 1.072599 0.485738 +vt 1.220694 0.859497 +vt 1.332646 0.866089 +vt 1.122122 0.451058 +vt 1.234965 0.454150 +vt 1.413957 0.870728 +vt 1.347024 0.455886 +vt 1.428376 0.456721 +vt 1.058035 0.799341 +vt 1.076831 0.448786 +vt 0.007350 0.433571 +vt 0.007350 0.223041 +vt 0.007350 0.219056 +vt 0.360029 0.219502 +vt 0.359781 0.223060 +vt 0.412410 0.221355 +vt 0.408486 0.225878 +vt 0.007350 0.377143 +vt 0.359604 0.377913 +vt 0.361111 0.429252 +vt 0.008858 0.429252 +vt 0.443716 0.379205 +vt 0.415695 0.429252 +vt 0.447494 0.117159 +vt 0.442509 0.117159 +vt 0.444812 0.062060 +vt 0.449332 0.062060 +vt 0.442509 0.117159 +vt 0.359604 0.113625 +vt 0.359604 0.064109 +vt 0.444812 0.062060 +vt 0.359604 0.012457 +vt 0.007350 0.062893 +vt 0.007350 0.012457 +vt 0.007350 0.273165 +vt 0.359604 0.273993 +vt 0.007350 0.164184 +vt 0.359856 0.165360 +vt 0.444599 0.160996 +vt 0.417279 0.223981 +vt 0.411881 0.221286 +vt 0.439950 0.160996 +vt 0.359604 0.273993 +vt 0.359781 0.223060 +vt 0.408486 0.225878 +vt 0.424813 0.274675 +vt 0.439950 0.160996 +vt 0.411881 0.221286 +vt 0.360029 0.219502 +vt 0.359856 0.165360 +vt 0.007350 0.325257 +vt 0.359604 0.326053 +vt 0.007350 0.112438 +vt 0.442099 0.327051 +vt 0.417122 0.012457 +vt 0.007350 0.273165 +vt 0.007350 0.164184 +vt 0.556521 0.990202 +vt 0.602982 0.814799 +vt 0.619799 0.822990 +vt 0.649324 0.985311 +vt 0.701728 0.978157 +vt 0.677748 0.976969 +vt 0.682262 0.899936 +vt 0.727590 0.833933 +vt 0.832190 0.961777 +vt 0.839120 0.949321 +vt 0.843743 0.951051 +vt 0.836182 0.964853 +vt 0.859622 0.870344 +vt 0.864233 0.869956 +vt 0.826082 0.827429 +vt 0.825098 0.833812 +vt 0.742659 0.826246 +vt 0.741088 0.816903 +vt 0.721259 0.826124 +vt 0.727590 0.833933 +vt 0.682262 0.899936 +vt 0.671031 0.894589 +vt 0.806581 0.983761 +vt 0.808887 0.988109 +vt 0.847832 0.843279 +vt 0.850605 0.840104 +vt 0.850605 0.840104 +vt 0.847832 0.843279 +vt 0.832190 0.961777 +vt 0.806581 0.983761 +vt 0.825098 0.833812 +vt 0.847832 0.843279 +vt 0.787706 0.983066 +vt 0.742659 0.826246 +vt 0.505137 0.965983 +vt 0.497494 0.952154 +vt 0.474077 0.865561 +vt 0.486724 0.833848 +vt 0.511976 0.821328 +vt 0.535132 0.991677 +vt 0.859622 0.870344 +vt 0.839120 0.949321 +vt 1.118246 0.934817 +vt 1.151914 0.936365 +vt 1.151943 0.966273 +vt 1.118276 0.966204 +vt 1.178110 0.936030 +vt 1.178138 0.965761 +vt 1.204412 0.935862 +vt 1.204440 0.965590 +vt 1.232773 0.935835 +vt 1.232801 0.965563 +vt 1.010231 0.934215 +vt 1.038592 0.934188 +vt 1.038622 0.965136 +vt 1.012148 0.965162 +vt 1.065118 0.934322 +vt 1.065148 0.965369 +vt 1.065118 0.934322 +vt 1.091676 0.934542 +vt 1.091706 0.965741 +vt 1.065148 0.965369 +vt 1.091676 0.934542 +vt 1.091706 0.965741 +vt 1.118246 0.934817 +vt 1.118231 0.919298 +vt 1.151897 0.918153 +vt 1.151914 0.936365 +vt 1.178092 0.917926 +vt 1.178110 0.936030 +vt 1.204394 0.917878 +vt 1.204412 0.935862 +vt 1.232755 0.917851 +vt 1.232773 0.935835 +vt 1.010231 0.934215 +vt 1.010216 0.918912 +vt 1.038578 0.918885 +vt 1.038592 0.934188 +vt 1.065103 0.918970 +vt 1.091661 0.919114 +vt 1.118231 0.919298 +vt 1.118220 0.908326 +vt 1.151886 0.907159 +vt 1.151897 0.918153 +vt 1.178082 0.906997 +vt 1.178092 0.917926 +vt 1.204384 0.906970 +vt 1.204394 0.917878 +vt 1.232745 0.906943 +vt 1.232755 0.917851 +vt 1.010216 0.918912 +vt 1.010206 0.908094 +vt 1.038567 0.908066 +vt 1.038578 0.918885 +vt 1.065093 0.908117 +vt 1.065103 0.918970 +vt 1.091650 0.908208 +vt 1.091661 0.919114 +vt 1.118220 0.908326 +vt 1.118197 0.884159 +vt 1.151864 0.884126 +vt 1.151886 0.907159 +vt 1.178059 0.884101 +vt 1.178082 0.906997 +vt 1.204361 0.884075 +vt 1.204384 0.906970 +vt 1.232723 0.884048 +vt 1.232745 0.906943 +vt 1.010206 0.908094 +vt 1.010183 0.884263 +vt 1.038544 0.884236 +vt 1.038567 0.908066 +vt 1.065070 0.884210 +vt 1.065093 0.908117 +vt 1.091627 0.884184 +vt 1.091650 0.908208 +vt 1.065167 0.984896 +vt 0.347219 0.902528 +vt 0.320134 0.902752 +vt 0.319964 0.882191 +vt 0.347049 0.881967 +vt 0.293050 0.902975 +vt 0.292880 0.882414 +vt 0.265966 0.903199 +vt 0.265796 0.882638 +vt 0.238882 0.903423 +vt 0.238712 0.882862 +vt 0.455469 0.901633 +vt 0.428385 0.901857 +vt 0.428215 0.881296 +vt 0.455299 0.881072 +vt 0.401387 0.902080 +vt 0.401217 0.881519 +vt 0.374303 0.902304 +vt 0.374133 0.881743 +vt 0.320333 0.930704 +vt 0.347406 0.930480 +vt 0.347561 0.949223 +vt 0.320488 0.949446 +vt 0.293260 0.930928 +vt 0.293415 0.949670 +vt 0.266186 0.931151 +vt 0.266341 0.949894 +vt 0.239113 0.931375 +vt 0.239268 0.950118 +vt 0.428626 0.929809 +vt 0.455700 0.929585 +vt 0.455855 0.948328 +vt 0.428781 0.948551 +vt 0.401553 0.930033 +vt 0.401708 0.948775 +vt 0.374480 0.930256 +vt 0.374635 0.948999 +vt 0.347893 0.989356 +vt 0.320820 0.989579 +vt 0.320590 0.961766 +vt 0.347663 0.961543 +vt 0.293746 0.989803 +vt 0.293517 0.961990 +vt 0.266673 0.990027 +vt 0.266443 0.962214 +vt 0.239600 0.990251 +vt 0.239370 0.962438 +vt 0.456187 0.988461 +vt 0.429113 0.988684 +vt 0.428883 0.960871 +vt 0.455957 0.960648 +vt 0.402040 0.988908 +vt 0.401810 0.961095 +vt 0.374966 0.989132 +vt 0.374737 0.961319 +vt 0.347219 0.902528 +vt 0.347291 0.916468 +vt 0.320218 0.916692 +vt 0.320134 0.902752 +vt 0.293144 0.916916 +vt 0.293050 0.902975 +vt 0.266071 0.917140 +vt 0.265966 0.903199 +vt 0.238998 0.917363 +vt 0.238882 0.903423 +vt 0.455469 0.901633 +vt 0.455584 0.915573 +vt 0.428510 0.915797 +vt 0.428385 0.901857 +vt 0.401437 0.916021 +vt 0.401387 0.902080 +vt 0.374364 0.916245 +vt 0.374303 0.902304 +vt 0.347291 0.916468 +vt 0.347406 0.930480 +vt 0.320333 0.930704 +vt 0.320218 0.916692 +vt 0.293260 0.930928 +vt 0.293144 0.916916 +vt 0.266186 0.931151 +vt 0.266071 0.917140 +vt 0.239113 0.931375 +vt 0.238998 0.917363 +vt 0.455584 0.915573 +vt 0.455700 0.929585 +vt 0.428626 0.929809 +vt 0.428510 0.915797 +vt 0.401553 0.930033 +vt 0.401437 0.916021 +vt 0.374480 0.930256 +vt 0.374364 0.916245 +vt 0.896551 0.641960 +vt 0.833580 0.641115 +vt 0.838425 0.629350 +vt 0.891809 0.630066 +vt 1.627946 0.576837 +vt 1.613203 0.577108 +vt 1.613012 0.566762 +vt 1.627756 0.566490 +vt 1.598168 0.577385 +vt 1.597978 0.567038 +vt 1.583610 0.577653 +vt 1.583420 0.567306 +vt 1.570796 0.577889 +vt 1.570606 0.567542 +vt 1.557118 0.578140 +vt 1.556928 0.567794 +vt 1.543085 0.578399 +vt 1.542895 0.568052 +vt 1.528181 0.578673 +vt 1.527991 0.568326 +vt 1.513784 0.578938 +vt 1.513593 0.568591 +vt 1.498300 0.579223 +vt 1.498110 0.568876 +vt 1.483294 0.579499 +vt 1.483104 0.569152 +vt 1.712688 0.575278 +vt 1.698763 0.575534 +vt 1.698573 0.565187 +vt 1.712498 0.564931 +vt 1.684583 0.575795 +vt 1.684393 0.565448 +vt 1.670792 0.576048 +vt 1.670601 0.565702 +vt 1.657340 0.576296 +vt 1.657149 0.565949 +vt 1.642517 0.576569 +vt 1.642327 0.566222 +vt 3.882653 0.675536 +vt 3.847080 0.675011 +vt 3.838880 0.666464 +vt 3.891664 0.666551 +vt 1.627756 0.566490 +vt 1.627604 0.558206 +vt 1.642175 0.557938 +vt 1.642327 0.566222 +vt 1.642327 0.566222 +vt 1.642175 0.557938 +vt 1.656997 0.557665 +vt 1.657149 0.565949 +vt 1.657149 0.565949 +vt 1.656997 0.557665 +vt 1.670449 0.557417 +vt 1.670601 0.565702 +vt 1.670601 0.565702 +vt 1.670449 0.557417 +vt 1.684240 0.557164 +vt 1.684393 0.565448 +vt 1.684393 0.565448 +vt 1.684240 0.557164 +vt 1.698421 0.556903 +vt 1.698573 0.565187 +vt 1.698573 0.565187 +vt 1.698421 0.556903 +vt 1.712345 0.556646 +vt 1.712498 0.564931 +vt 1.483104 0.569152 +vt 1.482951 0.560868 +vt 1.497957 0.560592 +vt 1.498110 0.568876 +vt 1.498110 0.568876 +vt 1.497957 0.560592 +vt 1.513441 0.560307 +vt 1.513593 0.568591 +vt 1.542895 0.568052 +vt 1.542742 0.559767 +vt 1.556776 0.559509 +vt 1.556928 0.567794 +vt 1.513961 0.588568 +vt 1.514121 0.597251 +vt 1.498637 0.597536 +vt 1.498477 0.588853 +vt 1.498477 0.588853 +vt 1.498637 0.597536 +vt 1.483631 0.597812 +vt 1.483471 0.589129 +vt 1.712866 0.584908 +vt 1.713025 0.593590 +vt 1.699100 0.593847 +vt 1.698941 0.585164 +vt 1.698941 0.585164 +vt 1.699100 0.593847 +vt 1.684920 0.594108 +vt 1.684761 0.585425 +vt 1.684761 0.585425 +vt 1.684920 0.594108 +vt 1.671129 0.594361 +vt 1.670969 0.585679 +vt 1.670969 0.585679 +vt 1.671129 0.594361 +vt 1.657677 0.594609 +vt 1.657517 0.585926 +vt 1.657517 0.585926 +vt 1.657677 0.594609 +vt 1.642854 0.594882 +vt 1.642695 0.586199 +vt 1.642695 0.586199 +vt 1.642854 0.594882 +vt 1.628284 0.595150 +vt 1.628124 0.586467 +vt 1.656997 0.557665 +vt 1.656962 0.555766 +vt 1.670414 0.555518 +vt 1.670449 0.557417 +vt 1.698421 0.556903 +vt 1.698385 0.555004 +vt 1.712311 0.554747 +vt 1.712345 0.556646 +vt 1.482916 0.558969 +vt 1.612860 0.558477 +vt 1.713025 0.593590 +vt 1.713057 0.595335 +vt 1.699132 0.595591 +vt 1.699100 0.593847 +vt 1.671129 0.594361 +vt 1.671161 0.596106 +vt 1.657709 0.596354 +vt 1.657677 0.594609 +vt 1.642854 0.594882 +vt 1.642887 0.596626 +vt 1.628316 0.596895 +vt 1.628284 0.595150 +vt 1.628124 0.586467 +vt 1.613380 0.586739 +vt 1.598345 0.587015 +vt 1.583787 0.587283 +vt 1.570974 0.587519 +vt 1.557296 0.587771 +vt 1.543262 0.588029 +vt 1.528358 0.588303 +vt 1.513961 0.588568 +vt 1.498477 0.588853 +vt 1.483471 0.589129 +vt 1.712866 0.584908 +vt 1.698941 0.585164 +vt 1.684761 0.585425 +vt 1.670969 0.585679 +vt 1.657517 0.585926 +vt 1.642695 0.586199 +vt 0.936439 0.593358 +vt 0.936439 0.613848 +vt 0.932941 0.613848 +vt 0.932941 0.593358 +vt 0.936439 0.634339 +vt 0.932941 0.634339 +vt 0.936439 0.654830 +vt 0.932941 0.654830 +vt 0.936439 0.531886 +vt 0.936439 0.552377 +vt 0.932941 0.552377 +vt 0.932941 0.531886 +vt 0.936439 0.572867 +vt 0.932941 0.572867 +vt 0.964273 0.614013 +vt 0.964273 0.593111 +vt 0.978612 0.593111 +vt 0.978612 0.614013 +vt 0.964273 0.634422 +vt 0.978612 0.634422 +vt 0.964273 0.654830 +vt 0.978612 0.654830 +vt 0.964273 0.552294 +vt 0.964273 0.531886 +vt 0.978612 0.531886 +vt 0.978612 0.552294 +vt 0.964273 0.572703 +vt 0.978612 0.572703 +vt 0.997477 0.613849 +vt 0.945781 0.593358 +vt 0.964273 0.593111 +vt 0.964273 0.614013 +vt 0.945781 0.613849 +vt 0.964273 0.634422 +vt 0.945781 0.634339 +vt 0.964273 0.654830 +vt 0.945781 0.654830 +vt 0.945781 0.531886 +vt 0.964273 0.531886 +vt 0.964273 0.552294 +vt 0.945781 0.552377 +vt 0.964273 0.572703 +vt 0.945781 0.572867 +vt 0.939803 0.691444 +vt 0.939799 0.689810 +vt 0.948957 0.689810 +vt 0.948867 0.692212 +vt 0.948957 0.689810 +vt 0.939799 0.689810 +vt 0.939799 0.658221 +vt 0.948957 0.658221 +vt 2.901649 0.531322 +vt 2.911924 0.531322 +vt 2.911924 0.681789 +vt 2.901649 0.681789 +vt 2.919678 0.531322 +vt 2.919678 0.681789 +vt 2.927963 0.531322 +vt 2.929541 0.681789 +vt 0.929541 0.681789 +vt 0.919678 0.681789 +vt 0.919678 0.531322 +vt 0.927963 0.531322 +vt 0.911924 0.681789 +vt 0.911924 0.531322 +vt 0.901649 0.681789 +vt 0.901649 0.531322 +vt 1.653557 0.635717 +vt 1.787461 0.635838 +vt 1.787461 0.644547 +vt 1.653557 0.644666 +vt 1.787461 0.652662 +vt 1.653557 0.652542 +vt 1.787461 0.661373 +vt 1.653557 0.661492 +vt 1.787461 0.669213 +vt 1.653557 0.669213 +vt 1.787461 0.677249 +vt 1.653557 0.677249 +vt 1.653557 0.611351 +vt 1.787461 0.611351 +vt 1.787461 0.618762 +vt 1.653557 0.618762 +vt 1.787461 0.627702 +vt 1.653557 0.627702 +vt 1.804256 0.635967 +vt 1.804256 0.644590 +vt 1.790486 0.644521 +vt 1.790486 0.635883 +vt 1.804256 0.644590 +vt 1.804256 0.652654 +vt 1.790486 0.652704 +vt 1.790486 0.644521 +vt 1.804256 0.652654 +vt 1.804256 0.661401 +vt 1.790486 0.661424 +vt 1.790486 0.652704 +vt 1.804256 0.661401 +vt 1.804256 0.669213 +vt 1.790486 0.669213 +vt 1.790486 0.661424 +vt 1.804256 0.669213 +vt 1.804256 0.677249 +vt 1.790486 0.677249 +vt 1.790486 0.669213 +vt 1.804256 0.611351 +vt 1.804256 0.618762 +vt 1.790486 0.618762 +vt 1.790486 0.611351 +vt 1.804256 0.618762 +vt 1.804256 0.627702 +vt 1.790486 0.627702 +vt 1.790486 0.618762 +vt 1.804256 0.627702 +vt 1.804256 0.635967 +vt 1.790486 0.635883 +vt 1.790486 0.627702 +vt 0.907472 0.257985 +vt 0.911119 0.051328 +vt 0.912623 0.051317 +vt 0.908977 0.257975 +vt 0.914357 0.051377 +vt 0.910712 0.257976 +vt 0.905738 0.257926 +vt 0.909384 0.051326 +vt 2.945508 0.023291 +vt 2.945365 0.289169 +vt 2.942142 0.289169 +vt 2.942880 0.023291 +vt 2.953900 0.023291 +vt 2.953900 0.289169 +vt 2.948937 0.289169 +vt 2.948675 0.023291 +vt 2.884426 0.003712 +vt 2.884426 0.306753 +vt 2.882265 0.306753 +vt 2.882265 0.003712 +vt 2.891725 0.003712 +vt 2.891725 0.306753 +vt 2.887573 0.306753 +vt 2.887573 0.003712 +vt 3.672500 0.806611 +vt 3.458466 0.806611 +vt 3.457033 0.801549 +vt 3.671067 0.801549 +vt 3.457013 0.796454 +vt 3.671087 0.796454 +vt 3.672521 0.811706 +vt 3.458446 0.811706 +vt 3.973814 0.134518 +vt 3.973814 0.286613 +vt 3.970027 0.286613 +vt 3.970027 0.134518 +vt 3.967030 0.286613 +vt 3.967030 0.134518 +vt 3.963765 0.286613 +vt 3.963765 0.134518 +vt 3.991477 0.134518 +vt 3.991477 0.286613 +vt 3.987690 0.286613 +vt 3.987690 0.134518 +vt 3.983886 0.286613 +vt 3.983886 0.134518 +vt 3.980889 0.286613 +vt 3.980889 0.134518 +vt 3.977618 0.286613 +vt 3.977618 0.134518 +vt 0.688848 0.749275 +vt 0.707262 0.749275 +vt 0.707262 0.796196 +vt 0.688848 0.796196 +vt 0.707262 0.821096 +vt 0.688848 0.821096 +vt 0.688848 0.687755 +vt 0.707262 0.687755 +vt 0.707262 0.713461 +vt 0.688848 0.713461 +vt 0.707262 0.749275 +vt 0.712529 0.749275 +vt 0.712529 0.796196 +vt 0.707262 0.796196 +vt 0.707262 0.796196 +vt 0.712529 0.796196 +vt 0.712529 0.821096 +vt 0.707262 0.821096 +vt 0.707262 0.687755 +vt 0.712529 0.687755 +vt 0.712529 0.713461 +vt 0.707262 0.713461 +vt 0.707262 0.713461 +vt 0.712529 0.713461 +vt 0.712529 0.749275 +vt 0.707262 0.749275 +vt 0.712529 0.821096 +vt 1.901273 0.987954 +vt 1.894390 0.809475 +vt 1.898839 0.809308 +vt 1.905721 0.987777 +vt 1.903373 0.809475 +vt 1.910255 0.987933 +vt 1.907880 0.808954 +vt 1.914763 0.987434 +vt 1.912328 0.808772 +vt 1.919212 0.987273 +vt 1.916777 0.808601 +vt 1.923660 0.987101 +vt 1.887785 0.988453 +vt 1.880903 0.810015 +vt 1.885410 0.809505 +vt 1.892292 0.987943 +vt 1.889943 0.809656 +vt 1.896825 0.988115 +vt 0.835327 0.793387 +vt 0.835327 0.820585 +vt 0.831746 0.820585 +vt 0.831746 0.793387 +vt 0.828119 0.820585 +vt 0.828119 0.793387 +vt 0.824470 0.820585 +vt 0.824470 0.793387 +vt 0.820887 0.820585 +vt 0.820887 0.793387 +vt 0.817304 0.820585 +vt 0.817304 0.793387 +vt 0.813656 0.820585 +vt 0.813656 0.793387 +vt 0.810030 0.820585 +vt 0.810030 0.793387 +vt 0.838908 0.793387 +vt 0.838908 0.820585 +vt 0.963771 0.464386 +vt 0.966829 0.510170 +vt 0.963142 0.507307 +vt 0.956692 0.465842 +vt 0.968024 0.463687 +vt 0.971978 0.514904 +vt 0.971339 0.463143 +vt 0.975459 0.514343 +vt 0.973352 0.462812 +vt 0.978725 0.509767 +vt 0.954252 0.423683 +vt 0.945634 0.424628 +vt 0.962774 0.422283 +vt 0.966228 0.421716 +vt 0.969417 0.421193 +vt 0.946255 0.386738 +vt 0.949923 0.403838 +vt 0.938767 0.405843 +vt 0.932504 0.389173 +vt 0.957710 0.384857 +vt 0.960009 0.402182 +vt 0.965586 0.383564 +vt 0.964799 0.401395 +vt 0.972329 0.382456 +vt 0.969884 0.400560 +vt 0.930994 0.364212 +vt 0.936570 0.333645 +vt 0.943846 0.369533 +vt 0.931040 0.379851 +vt 0.950086 0.315819 +vt 0.955052 0.362291 +vt 0.963966 0.306471 +vt 0.964760 0.358704 +vt 0.976111 0.317948 +vt 0.972943 0.361369 +vt 0.974988 0.462717 +vt 0.981663 0.505121 +vt 0.972942 0.421304 +vt 0.974470 0.399545 +vt 0.980256 0.366925 +vt 0.977774 0.381266 +vt 0.985764 0.339373 +vt 0.966829 0.510170 +vt 0.964615 0.512998 +vt 0.960727 0.510219 +vt 0.963142 0.507307 +vt 0.971978 0.514904 +vt 0.971030 0.518012 +vt 0.964615 0.512998 +vt 0.966829 0.510170 +vt 0.971978 0.514904 +vt 0.978725 0.509767 +vt 0.979438 0.515266 +vt 0.975659 0.517915 +vt 0.975459 0.514343 +vt 0.956692 0.465842 +vt 0.953100 0.466601 +vt 0.941483 0.425665 +vt 0.945634 0.424628 +vt 0.972942 0.421304 +vt 0.977648 0.421732 +vt 0.978737 0.462523 +vt 0.974988 0.462717 +vt 0.938767 0.405843 +vt 0.935117 0.407189 +vt 0.928188 0.391026 +vt 0.932504 0.389173 +vt 0.974470 0.399545 +vt 0.978161 0.399738 +vt 0.977648 0.421732 +vt 0.972942 0.421304 +vt 0.931040 0.379851 +vt 0.926916 0.381387 +vt 0.926746 0.365415 +vt 0.930994 0.364212 +vt 0.930994 0.364212 +vt 0.926746 0.365415 +vt 0.933515 0.332831 +vt 0.936570 0.333645 +vt 0.936570 0.333645 +vt 0.933515 0.332831 +vt 0.948466 0.313374 +vt 0.950086 0.315819 +vt 0.950086 0.315819 +vt 0.948466 0.313374 +vt 0.962437 0.303992 +vt 0.963966 0.306471 +vt 0.976111 0.317948 +vt 0.978359 0.315358 +vt 0.990410 0.338499 +vt 0.985764 0.339373 +vt 0.980256 0.366925 +vt 0.984066 0.367261 +vt 0.981269 0.381445 +vt 0.977774 0.381266 +vt 0.963966 0.306471 +vt 0.965424 0.303475 +vt 0.978359 0.315358 +vt 0.976111 0.317948 +vt 0.981663 0.505121 +vt 0.982307 0.509582 +vt 0.979438 0.515266 +vt 0.978725 0.509767 +vt 0.985764 0.339373 +vt 0.990410 0.338499 +vt 0.984066 0.367261 +vt 0.980256 0.366925 +vt 0.932504 0.389173 +vt 0.928188 0.391026 +vt 0.926916 0.381387 +vt 0.931040 0.379851 +vt 0.945634 0.424628 +vt 0.941483 0.425665 +vt 0.935117 0.407189 +vt 0.938767 0.405843 +vt 0.977774 0.381266 +vt 0.981269 0.381445 +vt 0.978161 0.399738 +vt 0.974470 0.399545 +vt 0.959462 0.507483 +vt 0.956592 0.511103 +vt 0.945891 0.513500 +vt 0.941512 0.510082 +vt 0.950077 0.517329 +vt 0.886744 0.406160 +vt 0.876822 0.376070 +vt 0.926916 0.381387 +vt 0.928188 0.391026 +vt 0.933515 0.332831 +vt 0.906951 0.304459 +vt 0.921929 0.299465 +vt 0.931160 0.313322 +vt 0.926746 0.365415 +vt 0.926916 0.381387 +vt 0.876822 0.376070 +vt 0.882738 0.329557 +vt 0.933515 0.332831 +vt 0.926746 0.365415 +vt 0.882738 0.329557 +vt 0.906951 0.304459 +vt 0.565573 0.782724 +vt 0.526626 0.783518 +vt 0.500841 0.780471 +vt 0.631173 0.764652 +vt 0.630058 0.699236 +vt 0.596866 0.612516 +vt 0.637337 0.616972 +vt 0.641785 0.651360 +vt 0.500841 0.780471 +vt 0.489568 0.620042 +vt 0.630058 0.699236 +vt 0.631173 0.764652 +vt 0.489568 0.620042 +vt 0.504547 0.612629 +vt 0.532640 0.607381 +vt 0.596866 0.612516 +vt 0.500841 0.780471 +vt 0.462399 0.748874 +vt 0.450798 0.706790 +vt 0.489568 0.620042 +vt 0.450798 0.706790 +vt 0.454897 0.674918 +vt 0.464575 0.645515 +vt 0.489568 0.620042 +vt 3.833514 0.653935 +vt 3.833570 0.641126 +vt 3.896566 0.641971 +vt 3.896511 0.654781 +vt 3.871963 0.679861 +vt 3.858635 0.680069 +vt 0.896495 0.654765 +vt 0.833525 0.653920 +vt 0.847321 0.620414 +vt 0.882991 0.620893 +vt 0.858914 0.615670 +vt 0.871440 0.615838 +vt 0.671031 0.894589 +vt 0.676401 0.986658 +vt 1.058323 0.502256 +vt 1.063613 0.482216 +vt 0.993903 0.691436 +vt 0.979362 0.691436 +vt 0.979348 0.689810 +vt 0.993911 0.689810 +vt 0.993911 0.689810 +vt 0.979348 0.689810 +vt 0.979067 0.658221 +vt 0.993909 0.658221 +vt 1.862950 0.741047 +vt 1.901950 0.723781 +vt 1.899748 0.711405 +vt 1.855536 0.734860 +vt 1.942279 0.723767 +vt 1.944343 0.711333 +vt 1.981343 0.741059 +vt 1.989631 0.734959 +vt 1.853947 0.974376 +vt 1.850019 0.969849 +vt 1.849805 0.970333 +vt 1.853430 0.974539 +vt 1.844046 0.969394 +vt 1.844269 0.970059 +vt 1.839486 0.973242 +vt 1.840070 0.973577 +vt 1.838931 0.979200 +vt 1.839588 0.978994 +vt 1.842796 0.983726 +vt 1.843067 0.983246 +vt 1.848811 0.984156 +vt 1.848652 0.983663 +vt 1.853443 0.980298 +vt 1.852944 0.980064 +vt 0.945508 0.023291 +vt 0.942880 0.023291 +vt 0.942142 0.289169 +vt 0.945365 0.289169 +vt 0.953900 0.023291 +vt 0.948675 0.023291 +vt 0.948937 0.289169 +vt 0.953900 0.289169 +vt 3.884426 0.003712 +vt 3.882265 0.003712 +vt 3.882265 0.306753 +vt 3.884426 0.306753 +vt 3.891725 0.003712 +vt 3.887573 0.003712 +vt 3.887573 0.306753 +vt 3.891725 0.306753 +vt 1.973814 0.134518 +vt 1.970027 0.134518 +vt 1.970027 0.286613 +vt 1.973814 0.286613 +vt 1.967029 0.134518 +vt 1.967029 0.286613 +vt 1.963765 0.134518 +vt 1.963765 0.286613 +vt 1.991477 0.134518 +vt 1.987690 0.134518 +vt 1.987690 0.286613 +vt 1.991477 0.286613 +vt 1.983886 0.134518 +vt 1.983886 0.286613 +vt 1.980889 0.134518 +vt 1.980889 0.286613 +vt 1.977618 0.134518 +vt 1.977618 0.286613 +vt 2.672500 0.806611 +vt 2.671067 0.801549 +vt 2.457033 0.801549 +vt 2.458466 0.806611 +vt 2.671087 0.796454 +vt 2.457013 0.796454 +vt 2.672521 0.811706 +vt 2.458446 0.811706 +vt 3.945508 0.023291 +vt 3.945365 0.289169 +vt 3.942142 0.289169 +vt 3.942880 0.023291 +vt 3.953900 0.023291 +vt 3.953900 0.289169 +vt 3.948937 0.289169 +vt 3.948675 0.023291 +vt 0.884426 0.003712 +vt 0.884426 0.306753 +vt 0.882265 0.306753 +vt 0.882265 0.003712 +vt 0.891725 0.003712 +vt 0.891725 0.306753 +vt 0.887573 0.306753 +vt 0.887573 0.003712 +vt 2.973814 0.134518 +vt 2.973814 0.286613 +vt 2.970027 0.286613 +vt 2.970027 0.134518 +vt 2.967030 0.286613 +vt 2.967030 0.134518 +vt 2.963765 0.286613 +vt 2.963765 0.134518 +vt 2.991477 0.134518 +vt 2.991477 0.286613 +vt 2.987690 0.286613 +vt 2.987690 0.134518 +vt 2.983886 0.286613 +vt 2.983886 0.134518 +vt 2.980889 0.286613 +vt 2.980889 0.134518 +vt 2.977618 0.286613 +vt 2.977618 0.134518 +vt 1.672500 0.806611 +vt 1.458466 0.806611 +vt 1.457033 0.801549 +vt 1.671067 0.801549 +vt 1.457013 0.796454 +vt 1.671087 0.796454 +vt 1.672521 0.811706 +vt 1.458446 0.811706 +vn 0.0000 -0.1792 -0.9838 +vn 0.0000 -0.1793 -0.9838 +vn -0.0000 0.1792 0.9838 +vn 0.9944 0.0999 0.0353 +vn 0.9977 0.0649 -0.0215 +vn 0.9997 0.0130 -0.0182 +vn 0.9945 -0.0659 -0.0808 +vn 0.9989 0.0044 -0.0465 +vn 0.9991 0.0175 -0.0395 +vn -0.9944 0.0999 0.0353 +vn -0.9977 0.0649 -0.0215 +vn -0.9997 0.0130 -0.0182 +vn -0.9945 -0.0659 -0.0808 +vn -0.9989 0.0044 -0.0465 +vn -0.9991 0.0175 -0.0395 +vn 0.0000 0.9838 -0.1792 +vn 0.8539 -0.0933 -0.5120 +vn -0.0001 -0.1789 -0.9839 +vn 0.8444 0.0810 -0.5296 +vn 0.8444 0.0809 -0.5296 +vn -0.0034 -0.0721 0.9974 +vn 0.9999 0.0106 -0.0012 +vn 0.0034 0.0721 -0.9974 +vn -0.9999 -0.0106 0.0012 +vn 0.0000 -0.9838 0.1792 +vn 1.0000 0.0000 0.0000 +vn -1.0000 0.0000 0.0000 +vn -0.9962 -0.0864 0.0133 +vn 0.9962 0.0864 -0.0133 +vn 0.9659 -0.2546 0.0464 +vn 0.0000 0.1793 0.9838 +vn -0.9659 -0.2546 0.0464 +vn -0.7071 -0.6957 0.1267 +vn -0.2588 -0.9503 0.1731 +vn -0.2588 0.9503 -0.1731 +vn -0.7071 0.6957 -0.1267 +vn -0.9659 0.2546 -0.0464 +vn 0.9659 0.2546 -0.0464 +vn 0.7071 -0.6957 0.1267 +vn 0.2588 -0.9503 0.1731 +vn 0.2588 0.9503 -0.1731 +vn 0.7071 0.6957 -0.1267 +vn 0.6533 -0.1395 0.7441 +vn 0.6533 0.3930 0.6471 +vn 0.9239 -0.3765 0.0686 +vn 0.9239 0.3765 -0.0686 +vn 0.0358 -0.2361 0.9711 +vn 0.0357 -0.2361 0.9711 +vn 0.0865 -0.8509 0.5181 +vn 0.0864 -0.8510 0.5181 +vn 0.0865 -0.9673 -0.2384 +vn 0.0358 -0.5170 -0.8552 +vn -0.0358 0.2361 -0.9711 +vn -0.0865 0.8510 -0.5181 +vn -0.0865 0.9673 0.2384 +vn -0.0358 0.5170 0.8552 +vn -0.9956 -0.0926 0.0142 +vn -0.9956 -0.0925 0.0142 +vn -0.9437 0.0593 0.3255 +vn -0.4395 0.1610 0.8837 +vn -0.4396 0.1610 0.8837 +vn -0.4396 -0.1610 -0.8837 +vn -0.9437 -0.0593 -0.3255 +vn -0.8906 0.4474 -0.0815 +vn -0.4530 -0.8886 0.0724 +vn 0.8906 -0.4474 0.0815 +vn -0.9097 0.4098 -0.0673 +vn -0.0294 -0.2356 -0.9714 +vn -0.0295 -0.2356 -0.9714 +vn 0.0496 -0.0823 -0.9954 +vn 0.0499 -0.0821 -0.9954 +vn 0.0551 -0.0729 -0.9958 +vn 0.0219 -0.1369 -0.9903 +vn -0.0675 -0.3073 -0.9492 +vn -0.0908 -0.3505 -0.9321 +vn -0.8539 -0.0933 -0.5120 +vn 0.0001 -0.1789 -0.9839 +vn -0.8444 0.0810 -0.5296 +vn -0.8444 0.0809 -0.5296 +vn 0.0034 -0.0721 0.9974 +vn -0.9999 0.0106 -0.0012 +vn -0.0034 0.0721 -0.9974 +vn 0.9999 -0.0106 0.0012 +vn 0.9962 -0.0864 0.0133 +vn 0.9887 -0.1218 -0.0873 +vn 0.9887 -0.1217 -0.0872 +vn 0.9856 -0.1559 -0.0655 +vn 0.9856 -0.1558 -0.0655 +vn -0.9962 0.0864 -0.0133 +vn -0.0166 -0.0378 0.9991 +vn 0.0167 0.3374 0.9412 +vn 0.0474 0.6615 0.7484 +vn 0.0755 0.9959 0.0497 +vn 0.0755 0.9349 -0.3467 +vn 0.0474 0.4060 -0.9126 +vn 0.0167 0.0390 -0.9991 +vn -0.0166 -0.3363 -0.9416 +vn -0.0476 -0.6641 -0.7462 +vn -0.0720 -0.8977 -0.4348 +vn -0.0856 -0.9951 -0.0496 +vn -0.0856 -0.9342 0.3464 +vn -0.0720 -0.7255 0.6844 +vn -0.0476 -0.4091 0.9112 +vn 0.0166 0.0373 -0.9992 +vn 0.0477 0.4105 -0.9106 +vn 0.0477 0.6652 0.7452 +vn 0.0477 0.6651 0.7452 +vn -0.6533 -0.1395 0.7441 +vn -0.6533 0.3930 0.6471 +vn -0.9239 -0.3765 0.0686 +vn -0.9239 0.3765 -0.0686 +vn -0.0358 -0.2361 0.9711 +vn -0.0357 -0.2361 0.9711 +vn -0.0865 -0.8509 0.5181 +vn -0.0864 -0.8510 0.5181 +vn -0.0865 -0.9673 -0.2384 +vn -0.0358 -0.5170 -0.8552 +vn 0.0358 0.2361 -0.9711 +vn 0.0865 0.8510 -0.5181 +vn 0.0865 0.9673 0.2384 +vn 0.0358 0.5170 0.8552 +vn 0.9956 -0.0926 0.0142 +vn 0.9437 0.0593 0.3255 +vn 0.4396 0.1610 0.8837 +vn 0.4396 -0.1610 -0.8837 +vn 0.9437 -0.0593 -0.3255 +vn -0.7621 0.6423 -0.0815 +vn 0.6382 0.7665 0.0724 +vn 0.7621 -0.6423 0.0815 +vn 0.2049 0.1199 -0.9714 +vn 0.0887 -0.0219 -0.9958 +vn 0.1345 0.0338 -0.9903 +vn 0.1345 0.0339 -0.9903 +vn 0.2558 0.1831 -0.9492 +vn 0.2864 0.2216 -0.9321 +vn 0.0000 0.9934 -0.1150 +vn 0.0000 0.8434 0.5373 +vn 0.0000 0.9429 0.3332 +vn 0.0000 0.0939 -0.9956 +vn 0.0000 -0.9932 0.1163 +vn 0.0000 -0.9938 0.1111 +vn 0.0000 0.5939 -0.8045 +vn 0.0000 -0.8992 -0.4375 +vn 0.0000 0.4099 -0.9121 +vn 0.0000 -0.9838 -0.1790 +vn 0.0000 -0.3071 -0.9517 +vn 0.0000 0.8980 -0.4399 +vn 0.0000 -0.7544 -0.6565 +vn 0.0000 0.9998 0.0206 +vn 0.0000 0.9826 0.1856 +vn 0.0000 -0.1270 -0.9919 +vn 0.0000 -0.8616 0.5076 +vn 0.0000 -0.1495 0.9888 +vn 0.0000 -0.9998 0.0213 +vn 0.0000 -0.6324 -0.7747 +vn 0.0000 0.2339 0.9723 +vn 0.0000 -0.0391 0.9992 +vn 0.8197 -0.5727 -0.0082 +vn 0.7597 -0.6501 -0.0164 +vn 0.4703 -0.8816 0.0405 +vn -0.0000 -0.9926 0.1213 +vn 0.4006 -0.9109 0.0987 +vn 0.0000 -0.9980 0.0635 +vn 0.6690 0.6800 -0.3002 +vn 0.7067 0.6511 -0.2768 +vn 0.8960 0.3880 -0.2160 +vn 0.9775 0.1101 -0.1802 +vn 0.9876 0.0385 -0.1522 +vn 0.9463 -0.3136 -0.0788 +vn 0.0000 0.9347 -0.3555 +vn 0.0000 0.9403 -0.3405 +vn 0.3748 0.8697 -0.3211 +vn 0.1348 0.9134 -0.3841 +vn 0.3839 0.8929 -0.2354 +vn 0.7451 0.6335 -0.2084 +vn 0.4943 0.8181 -0.2940 +vn 0.1307 0.8932 -0.4302 +vn 0.7392 0.6373 -0.2177 +vn -0.5050 0.7994 -0.3256 +vn -0.5068 0.7980 -0.3260 +vn -0.8197 -0.5727 -0.0082 +vn -0.4704 -0.8815 0.0416 +vn -0.7597 -0.6501 -0.0164 +vn -0.4006 -0.9109 0.0987 +vn -0.6690 0.6800 -0.3002 +vn -0.8960 0.3880 -0.2160 +vn -0.7067 0.6511 -0.2768 +vn -0.9775 0.1101 -0.1802 +vn -0.9463 -0.3136 -0.0788 +vn -0.9876 0.0385 -0.1522 +vn -0.3748 0.8697 -0.3211 +vn -0.1348 0.9134 -0.3841 +vn -0.7451 0.6335 -0.2084 +vn -0.3840 0.8928 -0.2352 +vn -0.4943 0.8181 -0.2940 +vn -0.7392 0.6373 -0.2177 +vn -0.1307 0.8932 -0.4302 +vn 0.5050 0.7994 -0.3256 +vn 0.5068 0.7980 -0.3260 +vn 0.9200 -0.3895 -0.0438 +vn 0.7085 -0.7055 0.0167 +vn 0.9937 -0.0210 -0.1099 +vn 0.3860 -0.9206 0.0597 +vn 0.0000 -0.9972 0.0754 +vn -0.0000 0.9401 -0.3409 +vn 0.9362 0.3098 -0.1661 +vn 0.0000 0.9695 -0.2450 +vn -0.0000 0.9695 -0.2451 +vn -0.5050 0.7993 -0.3256 +vn 0.1308 0.8933 -0.4301 +vn -0.0000 -0.2046 -0.9789 +vn -0.0000 -0.2045 -0.9789 +vn -0.4656 -0.1810 -0.8663 +vn -0.7520 0.1019 0.6512 +vn -0.6996 0.1149 0.7053 +vn -0.9368 0.0336 0.3482 +vn 0.9219 -0.3858 -0.0358 +vn 0.7125 -0.7007 0.0373 +vn 0.9938 -0.0197 -0.1090 +vn 0.3893 -0.9164 0.0926 +vn -0.0000 -0.9935 0.1135 +vn 0.9394 0.3025 -0.1612 +vn -0.0000 -0.9884 0.1517 +vn -0.0154 -0.9881 0.1530 +vn -0.0147 -0.9926 0.1203 +vn 0.0000 -0.9927 0.1203 +vn -0.0000 -0.9835 0.1807 +vn -0.0161 -0.9839 0.1780 +vn -0.0446 -0.9872 0.1534 +vn -0.0446 -0.9918 0.1202 +vn 0.0147 0.9975 -0.0687 +vn 0.0142 0.9925 -0.1213 +vn 0.0000 0.9925 -0.1224 +vn 0.0000 0.9976 -0.0687 +vn -0.0031 -0.9852 0.1715 +vn -0.0159 -0.9849 0.1722 +vn -0.0446 -0.9828 0.1791 +vn -0.0606 -0.9873 0.1471 +vn -0.0638 -0.9917 0.1120 +vn 0.0444 0.9967 -0.0686 +vn 0.0445 0.9917 -0.1203 +vn 0.0135 0.9835 -0.1804 +vn -0.0000 0.9841 -0.1778 +vn -0.0053 -0.9766 0.2148 +vn -0.0174 -0.9773 0.2109 +vn -0.0174 -0.9785 0.2055 +vn -0.0052 -0.9793 0.2025 +vn -0.0446 -0.9841 0.1716 +vn -0.0563 -0.9827 0.1767 +vn -0.0593 -0.9865 0.1525 +vn -0.0679 -0.9923 0.1038 +vn 0.0635 0.9961 -0.0605 +vn 0.0605 0.9916 -0.1140 +vn 0.0446 0.9828 -0.1791 +vn 0.0136 0.9824 -0.1862 +vn -0.0031 0.9824 -0.1869 +vn -0.0446 -0.9773 0.2069 +vn -0.0446 -0.9809 0.1893 +vn -0.0166 -0.9823 0.1868 +vn -0.0553 -0.9836 0.1716 +vn -0.0526 -0.9833 0.1742 +vn 0.8962 0.0795 0.4364 +vn 0.8986 0.0786 0.4316 +vn 0.8256 0.1011 0.5551 +vn 0.0676 0.9963 -0.0525 +vn 0.0593 0.9904 -0.1252 +vn 0.0563 0.9826 -0.1767 +vn 0.0446 0.9814 -0.1865 +vn 0.0181 0.9766 -0.2141 +vn 0.0227 0.9655 -0.2596 +vn 0.0159 0.9680 -0.2503 +vn 0.0045 0.9802 -0.1978 +vn -0.0589 -0.9748 0.2154 +vn -0.0560 -0.9796 0.1932 +vn -0.0508 -0.9839 0.1716 +vn 0.9875 0.0283 0.1553 +vn 0.0526 0.9830 -0.1759 +vn 0.0553 0.9809 -0.1864 +vn 0.0446 0.9732 -0.2254 +vn 0.0445 0.9635 -0.2641 +vn -0.0565 -0.9730 0.2236 +vn -0.0519 -0.9800 0.1919 +vn 0.0508 0.9812 -0.1864 +vn 0.0559 0.9718 -0.2292 +vn 0.0587 0.9604 -0.2723 +vn 0.9624 -0.0487 -0.2673 +vn 0.9648 -0.0471 -0.2588 +vn 0.0519 0.9733 -0.2236 +vn 0.0562 0.9582 -0.2804 +vn -0.5676 -0.1476 -0.8100 +vn -0.8987 -0.0786 -0.4316 +vn -0.9924 -0.0221 -0.1211 +vn -0.9841 -0.0319 -0.1749 +vn -0.9835 -0.0324 -0.1781 +vn -0.0611 -0.9745 0.2161 +vn 0.0228 0.9610 -0.2757 +vn -0.0625 -0.9723 0.2253 +vn 0.0609 0.9601 -0.2730 +vn 0.0623 0.9574 -0.2820 +vn -0.0032 -0.9834 0.1815 +vn 0.0162 0.9577 -0.2872 +vn -0.0001 -0.1787 -0.9839 +vn -0.0000 -0.1790 -0.9838 +vn 0.2043 -0.9714 0.1206 +vn 0.2062 -0.9691 0.1355 +vn 0.2028 -0.9703 0.1316 +vn 0.2023 -0.9725 0.1157 +vn 0.2064 -0.9704 0.1254 +vn 0.2098 -0.9674 0.1418 +vn 0.9793 0.1992 -0.0363 +vn 0.9689 0.2231 0.1071 +vn 0.9685 0.2235 0.1098 +vn -0.1963 0.9684 -0.1536 +vn -0.1984 0.9667 -0.1615 +vn -0.1946 0.9674 -0.1621 +vn -0.1918 0.9688 -0.1569 +vn -0.1950 0.9680 -0.1577 +vn -0.2012 0.9675 -0.1532 +vn -0.2015 0.9679 -0.1504 +vn 0.2095 -0.9478 0.2403 +vn 0.2030 -0.9475 0.2469 +vn 0.2036 -0.9594 0.1952 +vn 0.2103 -0.9583 0.1935 +vn -0.2020 0.9618 -0.1846 +vn -0.2019 0.9594 -0.1968 +vn -0.1990 0.9601 -0.1966 +vn -0.1989 0.9625 -0.1847 +vn 0.8157 0.2651 0.5142 +vn 0.8863 0.2565 0.3856 +vn 0.8856 0.2566 0.3871 +vn 0.2161 -0.9480 0.2338 +vn 0.2170 -0.9595 0.1794 +vn -0.1960 0.9606 -0.1969 +vn -0.1958 0.9638 -0.1811 +vn 0.2091 -0.9666 0.1482 +vn 0.2034 -0.9683 0.1448 +vn -0.2018 0.9663 -0.1599 +vn 0.2144 -0.9649 0.1518 +vn -0.1887 0.9686 -0.1620 +vn 0.0194 -0.9850 0.1714 +vn -0.0243 -0.9816 0.1893 +vn -0.0351 -0.9805 0.1935 +vn 0.0195 -0.9851 0.1708 +vn 0.0303 0.9835 -0.1786 +vn 0.0432 0.9832 -0.1771 +vn 0.0000 0.9836 -0.1804 +vn 0.8843 -0.2718 0.3796 +vn 0.8851 -0.2730 0.3770 +vn 0.7757 -0.1504 0.6130 +vn 0.7694 -0.1448 0.6222 +vn 0.9213 -0.3842 -0.0598 +vn 0.9219 -0.3835 -0.0547 +vn 0.1710 -0.0770 -0.9823 +vn -0.2475 -0.0947 -0.9642 +vn -0.2487 -0.0949 -0.9639 +vn 0.2026 -0.0813 -0.9759 +vn -0.0000 -0.1442 -0.9895 +vn -0.6983 -0.1037 -0.7083 +vn -0.7022 -0.1031 -0.7044 +vn -0.0000 -0.1442 -0.9896 +vn 0.6740 -0.0671 0.7357 +vn 0.8315 -0.4074 -0.3777 +vn 0.3503 -0.1008 -0.9312 +vn 0.2032 0.9615 -0.1848 +vn 0.2145 0.9594 -0.1833 +vn 0.1479 0.9702 -0.1919 +vn 0.1350 0.9718 -0.1935 +vn 0.0864 0.9783 -0.1884 +vn 0.0784 0.9794 -0.1859 +vn -0.2904 -0.9414 0.1717 +vn -0.2905 -0.9414 0.1717 +vn -0.1809 -0.9647 0.1917 +vn -0.1928 -0.9627 0.1896 +vn -0.1140 -0.9728 0.2019 +vn -0.0992 -0.9740 0.2039 +vn 0.2978 0.9392 -0.1709 +vn 0.9360 0.3463 -0.0631 +vn 0.9995 -0.0324 0.0059 +vn 0.3827 0.9089 -0.1656 +vn 0.3827 -0.9089 0.1656 +vn 0.9469 0.3020 0.1101 +vn 0.9993 -0.0186 0.0309 +vn 0.9994 -0.0186 0.0308 +vn 0.8668 0.4564 0.2010 +vn 0.6734 0.6679 0.3168 +vn 0.5930 0.7275 0.3451 +vn 0.3440 0.8484 0.4024 +vn 0.2982 0.8624 0.4091 +vn -0.0000 0.9035 0.4286 +vn 0.9361 0.3503 0.0302 +vn 0.9999 -0.0040 0.0160 +vn 0.9056 0.4218 0.0451 +vn 0.7095 0.6984 0.0943 +vn 0.6699 0.7358 0.0994 +vn 0.3804 0.9165 0.1238 +vn 0.3539 0.9269 0.1252 +vn -0.0000 0.9910 0.1339 +vn 0.9250 0.3749 -0.0620 +vn 1.0000 -0.0005 0.0010 +vn 1.0000 -0.0005 0.0009 +vn 0.9233 0.3789 -0.0627 +vn 0.7082 0.6968 -0.1137 +vn 0.7059 0.6991 -0.1140 +vn 0.3834 0.9115 -0.1487 +vn 0.3818 0.9122 -0.1488 +vn 0.0000 0.9870 -0.1610 +vn 0.1951 -0.9621 0.1903 +vn 0.0000 -0.9810 0.1941 +vn 0.0000 -0.9810 0.1940 +vn 0.5516 -0.8195 0.1557 +vn 0.5376 -0.7017 -0.4676 +vn 0.3682 -0.7906 -0.4893 +vn 0.8315 -0.5466 0.0996 +vn 0.6258 0.3869 0.6773 +vn 0.6774 0.1319 0.7237 +vn 0.4790 0.6031 0.6379 +vn 0.2592 0.7475 0.6116 +vn -0.0000 0.7982 0.6023 +vn 0.2592 -0.4838 0.8359 +vn -0.0000 -0.5345 0.8452 +vn 0.4790 -0.3393 0.8096 +vn 0.6258 -0.1232 0.7702 +vn -0.9232 -0.3599 0.1346 +vn -0.9202 -0.3672 0.1357 +vn -0.9967 0.0102 0.0804 +vn -0.9967 0.0187 0.0791 +vn -0.7081 -0.6823 0.1818 +vn -0.7038 -0.6866 0.1824 +vn -0.3837 -0.8984 0.2135 +vn -0.3807 -0.8997 0.2136 +vn 0.0000 -0.9745 0.2246 +vn 0.0000 -0.9748 0.2229 +vn -0.3787 0.9240 -0.0535 +vn -0.3818 0.9227 -0.0533 +vn -0.0000 0.9979 -0.0644 +vn -0.7010 0.7128 -0.0226 +vn -0.7054 0.7085 -0.0219 +vn -0.9182 0.3953 0.0240 +vn -0.9213 0.3880 0.0250 +vn 0.8413 0.2013 -0.5017 +vn 0.7821 -0.1569 -0.6031 +vn 0.8184 -0.0436 -0.5730 +vn 0.8229 0.3371 -0.4573 +vn 0.7202 0.5883 -0.3678 +vn 0.6498 0.6848 -0.3298 +vn 0.4114 0.8766 -0.2495 +vn 0.3501 0.9064 -0.2361 +vn -0.0000 0.9792 -0.2030 +vn 0.2071 -0.6673 -0.7154 +vn -0.0000 -0.6926 -0.7213 +vn 0.4172 -0.5825 -0.6976 +vn 0.6203 -0.4184 -0.6635 +vn 0.6735 -0.3531 -0.6494 +vn 0.9262 0.3644 -0.0965 +vn 0.9985 -0.0174 -0.0518 +vn 0.9986 -0.0017 -0.0536 +vn 0.9207 0.3778 -0.0981 +vn 0.7110 0.6902 -0.1347 +vn 0.7031 0.6980 -0.1356 +vn 0.3855 0.9087 -0.1603 +vn 0.3799 0.9110 -0.1606 +vn -0.0000 0.9856 -0.1693 +vn 0.3776 -0.9244 0.0547 +vn -0.0000 -0.9980 0.0634 +vn 0.3831 -0.9221 0.0545 +vn 0.6998 -0.7137 0.0300 +vn 0.7077 -0.7059 0.0291 +vn 0.9183 -0.3958 -0.0073 +vn 0.9239 -0.3825 -0.0089 +vn -0.9961 -0.0867 0.0133 +vn -0.9978 -0.0661 0.0102 +vn -0.9978 -0.0658 0.0101 +vn -0.1233 0.1705 -0.9776 +vn -0.1523 0.2208 -0.9634 +vn -0.0231 -0.1539 -0.9878 +vn -0.0228 -0.1570 -0.9873 +vn -0.3348 0.4709 -0.8162 +vn -0.3835 0.5223 -0.7616 +vn -0.5382 0.6789 -0.4994 +vn -0.5575 0.6972 -0.4507 +vn -0.6150 0.7794 -0.1199 +vn -0.5281 0.8051 0.2699 +vn -0.5505 0.8062 0.2168 +vn -0.3156 0.7038 0.6364 +vn -0.3631 0.7368 0.5704 +vn -0.1133 0.4609 0.8802 +vn -0.1363 0.4997 0.8554 +vn -0.0211 0.1511 0.9883 +vn -0.0209 0.1492 0.9886 +vn -0.0599 -0.1890 0.9802 +vn -0.0766 -0.2392 0.9680 +vn -0.1901 -0.5504 0.8130 +vn -0.2160 -0.6149 0.7584 +vn -0.2929 -0.8311 0.4728 +vn -0.2972 -0.8469 0.4409 +vn -0.3157 -0.9378 0.1442 +vn -0.3157 -0.9378 0.1443 +vn -0.2929 -0.9347 -0.2012 +vn -0.2972 -0.9402 -0.1661 +vn -0.1901 -0.7693 -0.6100 +vn -0.2160 -0.8144 -0.5385 +vn -0.0611 -0.4773 -0.8766 +vn -0.0767 -0.5190 -0.8513 +vn 0.6292 0.4847 -0.6077 +vn 0.5871 0.7004 -0.4059 +vn 0.6222 0.6555 0.4281 +vn 0.6295 0.4441 0.6375 +vn -0.9984 -0.0568 0.0024 +vn -0.9984 -0.0569 0.0024 +vn -0.9984 -0.0435 0.0359 +vn -0.9985 -0.0431 0.0349 +vn -0.9983 -0.0385 0.0431 +vn -0.9983 -0.0388 0.0435 +vn -0.9979 -0.0634 0.0122 +vn -0.9978 -0.0658 0.0125 +vn -0.9935 -0.1129 0.0174 +vn -0.9979 -0.0641 0.0074 +vn -0.9978 -0.0665 0.0079 +vn -0.9983 -0.0497 -0.0295 +vn -0.9983 -0.0500 -0.0298 +vn -0.9966 -0.0667 -0.0485 +vn -0.9966 -0.0664 -0.0483 +vn -0.9941 -0.0876 -0.0633 +vn -0.9942 -0.0872 -0.0631 +vn -0.9873 -0.1391 -0.0761 +vn -0.9874 -0.1386 -0.0765 +vn -0.9891 -0.1378 -0.0517 +vn -0.9890 -0.1385 -0.0522 +vn -0.9946 -0.1032 -0.0123 +vn -0.9945 -0.1035 -0.0129 +vn 0.9949 0.0988 -0.0179 +vn 0.9950 0.0987 -0.0179 +vn 0.9949 0.0996 -0.0126 +vn 0.9927 0.1148 -0.0378 +vn 0.9927 0.1148 -0.0372 +vn 0.9893 0.1390 -0.0451 +vn 0.9894 0.1380 -0.0458 +vn 0.9893 0.1389 -0.0451 +vn 0.9907 0.1344 -0.0216 +vn 0.9907 0.1341 -0.0215 +vn 0.9941 0.1073 -0.0165 +vn 0.9907 0.1347 -0.0198 +vn 0.9907 0.1343 -0.0198 +vn 0.9893 0.1461 0.0012 +vn 0.9894 0.1454 0.0022 +vn 0.9927 0.1209 0.0015 +vn 0.9927 0.1206 0.0009 +vn 0.0327 -0.2277 0.9732 +vn -0.0004 0.1569 0.9876 +vn 0.0003 0.1482 0.9890 +vn -0.0326 0.5087 0.8603 +vn -0.0326 0.5086 0.8604 +vn -0.0588 0.7976 0.6003 +vn -0.0588 0.7970 0.6012 +vn -0.0732 0.9682 0.2394 +vn -0.0729 0.9660 0.2481 +vn -0.0771 0.9854 -0.1516 +vn -0.0732 0.8515 -0.5193 +vn -0.0730 0.8468 -0.5270 +vn -0.0588 0.5804 -0.8122 +vn -0.0588 0.5795 -0.8129 +vn -0.0326 0.2266 -0.9734 +vn -0.0004 -0.1472 -0.9891 +vn 0.0004 -0.1559 -0.9878 +vn 0.0327 -0.5096 -0.8598 +vn 0.0610 -0.7961 -0.6020 +vn 0.0804 -0.9656 -0.2471 +vn 0.0874 -0.9846 0.1514 +vn 0.0804 -0.8467 0.5259 +vn 0.0610 -0.5784 0.8134 +vn -0.0029 0.1489 0.9888 +vn 0.6889 -0.1066 0.7170 +vn 0.6661 0.1776 0.7244 +vn 0.7087 -0.3739 0.5983 +vn 0.7225 -0.5749 0.3840 +vn 0.7274 -0.6782 0.1043 +vn 0.7225 -0.6637 -0.1934 +vn 0.7087 -0.5367 -0.4579 +vn 0.6883 -0.3224 -0.6498 +vn -0.0001 -0.1505 -0.9886 +vn 0.6674 -0.0506 -0.7430 +vn -0.0328 0.2281 -0.9731 +vn 0.6304 0.2338 -0.7403 +vn -0.0587 0.5797 -0.8127 +vn 0.6292 0.4846 -0.6077 +vn -0.0730 0.8492 -0.5230 +vn 0.5871 0.7004 -0.4058 +vn 0.5708 0.8115 -0.1249 +vn -0.0730 0.9671 0.2436 +vn 0.5695 0.8047 0.1678 +vn -0.0588 0.7971 0.6010 +vn 0.6222 0.6555 0.4280 +vn -0.0358 0.5093 0.8599 +vn 0.0774 0.1839 -0.9799 +vn 0.0260 -0.1509 -0.9882 +vn 0.0258 -0.1495 -0.9884 +vn 0.1040 0.2455 -0.9638 +vn 0.2593 0.5127 -0.8185 +vn 0.3094 0.5759 -0.7567 +vn 0.4486 0.7407 -0.5002 +vn 0.4683 0.7617 -0.4477 +vn 0.5205 0.8439 -0.1298 +vn 0.4486 0.8567 0.2545 +vn 0.4683 0.8611 0.1981 +vn 0.2593 0.7350 0.6265 +vn 0.3093 0.7767 0.5486 +vn 0.0774 0.4699 0.8793 +vn 0.1040 0.5238 0.8455 +vn 0.0259 0.1531 0.9879 +vn 0.0259 0.1544 0.9877 +vn 0.1267 -0.1699 0.9773 +vn 0.1554 -0.2192 0.9632 +vn 0.3181 -0.4943 0.8090 +vn 0.3529 -0.5528 0.7549 +vn 0.4547 -0.7559 0.4710 +vn 0.4598 -0.7700 0.4423 +vn 0.4841 -0.8648 0.1330 +vn 0.4842 -0.8648 0.1330 +vn 0.4548 -0.8625 -0.2221 +vn 0.4598 -0.8674 -0.1905 +vn 0.3180 -0.7146 -0.6230 +vn 0.3529 -0.7541 -0.5539 +vn 0.1267 -0.4557 -0.8811 +vn 0.1555 -0.4985 -0.8529 +vn 0.8660 0.4919 -0.0896 +vn 0.5000 0.8520 -0.1552 +vn 0.0000 0.9838 -0.1793 +vn 0.5000 -0.8520 0.1552 +vn 0.4768 0.4204 0.7720 +vn 0.4767 0.4204 0.7720 +vn 0.5505 0.1496 0.8213 +vn 0.2753 0.6187 0.7359 +vn 0.0000 0.6912 0.7226 +vn 0.2753 -0.3194 0.9068 +vn -0.0000 -0.3920 0.9200 +vn 0.4768 -0.1212 0.8706 +vn 0.0000 0.9838 -0.1791 +vn 0.2706 0.7695 0.5785 +vn -0.0000 0.7993 0.6010 +vn -0.5120 0.0164 -0.8588 +vn 0.4238 0.4055 -0.8099 +vn 0.4278 0.4065 -0.8073 +vn 0.9216 0.3863 0.0369 +vn 0.9216 0.3863 0.0368 +vn -0.4064 -0.4012 0.8209 +vn -0.4064 -0.4013 0.8209 +vn -0.9326 -0.2755 0.2330 +vn -0.8821 -0.4710 0.0106 +vn -0.4229 -0.7463 -0.5140 +vn 0.9315 0.2618 -0.2524 +vn -0.4553 0.4941 0.7406 +vn -0.4553 0.4941 0.7407 +vn -0.0000 0.1520 0.9884 +vn -0.0000 -0.5914 0.8064 +vn 0.0000 -0.9884 0.1520 +vn 0.0000 -0.8064 -0.5914 +vn 0.0000 -0.1520 -0.9884 +vn 0.0000 0.5914 -0.8064 +vn 0.0000 0.9884 -0.1520 +vn -0.0000 0.8064 0.5914 +vn -0.0030 -0.2387 0.9711 +vn -0.0029 -0.2390 0.9710 +vn -0.0031 -0.2386 0.9711 +vn -0.0059 -0.8554 0.5179 +vn -0.0059 -0.8554 0.5180 +vn -0.0057 -0.9714 -0.2373 +vn -0.0059 -0.9715 -0.2369 +vn -0.0059 -0.9715 -0.2368 +vn -0.0059 -0.9715 -0.2370 +vn -0.0031 -0.5195 -0.8545 +vn -0.0030 -0.5194 -0.8545 +vn 0.0040 0.2388 -0.9711 +vn 0.0040 0.2387 -0.9711 +vn 0.0114 0.8554 -0.5178 +vn 0.0114 0.8555 -0.5177 +vn 0.0113 0.8554 -0.5178 +vn 0.0116 0.9714 0.2372 +vn 0.0114 0.9715 0.2368 +vn 0.0114 0.9715 0.2369 +vn 0.0040 0.5195 0.8544 +vn 0.0039 0.5196 0.8544 +vn 0.0038 0.5198 0.8543 +vn 0.0040 0.5194 0.8545 +vn -0.5000 0.0374 -0.8652 +vn -0.5000 -0.0373 0.8652 +vn -0.5000 -0.0374 0.8652 +vn 0.6385 0.7592 -0.1260 +vn -0.2118 -0.2181 -0.9527 +vn -0.5267 -0.6034 -0.5988 +vn 0.6331 0.7545 -0.1727 +vn 0.0371 0.0072 0.9993 +vn -0.3758 -0.4743 0.7961 +vn -0.5267 -0.6033 -0.5988 +vn 0.6092 -0.7909 -0.0581 +vn -0.4221 0.5423 -0.7264 +vn -0.1468 0.1824 -0.9722 +vn 0.6104 -0.7917 0.0240 +vn -0.4222 0.5423 -0.7264 +vn -0.3940 0.5200 0.7579 +vn -0.0558 0.0831 0.9950 +vn -0.1469 0.1824 -0.9722 +vn -0.5000 0.4249 -0.7546 +vn -0.5000 -0.4249 0.7547 +vn -0.5000 -0.4249 0.7546 +vn -0.5000 -0.4248 0.7546 +vn 0.8194 -0.5640 0.1027 +vn 0.5794 -0.5255 -0.6230 +vn 0.5794 -0.5256 -0.6230 +vn 0.8194 -0.5640 0.1028 +vn -0.5794 0.2721 -0.7683 +vn -0.8194 0.5640 -0.1028 +vn -0.5794 0.5255 0.6230 +vn -0.5794 0.5256 0.6230 +vn 0.5794 -0.2721 0.7683 +vn 0.9999 -0.0025 -0.0139 +vn 0.7750 -0.1133 -0.6217 +vn 0.7152 -0.1253 -0.6876 +vn 0.9999 0.0026 0.0143 +vn 0.7864 0.1107 0.6078 +vn 0.6983 0.1283 0.7042 +vn 0.0817 0.9966 -0.0118 +vn 0.2007 0.9739 -0.1059 +vn 0.0646 0.8796 -0.4713 +vn 0.0647 0.8796 -0.4713 +vn 0.0504 0.8706 -0.4894 +vn 0.0505 0.8706 -0.4894 +vn -0.7071 -0.6957 0.1268 +vn -0.7071 -0.6956 0.1269 +vn 0.7071 -0.6956 0.1268 +vn 0.7071 -0.6956 0.1269 +vn 0.7071 -0.6957 0.1268 +vn -0.0075 0.1264 0.9920 +vn 0.0034 0.1143 0.9934 +vn 0.0149 0.1770 0.9841 +vn 0.0115 0.1830 0.9830 +vn -0.0157 0.1212 0.9925 +vn 0.0094 0.1775 0.9841 +vn -0.0392 0.1348 0.9901 +vn -0.0106 0.1893 0.9819 +vn -0.0698 0.1559 0.9853 +vn -0.0188 0.1960 0.9804 +vn 0.0485 0.2717 0.9612 +vn 0.0503 0.2750 0.9601 +vn 0.0806 0.3318 0.9399 +vn 0.0792 0.3290 0.9410 +vn 0.1044 0.3691 0.9235 +vn 0.1042 0.3689 0.9236 +vn 0.1053 0.3718 0.9223 +vn 0.1026 0.3743 0.9216 +vn 0.0983 0.3753 0.9217 +vn 0.1528 0.4280 0.8908 +vn 0.1261 0.3967 0.9092 +vn 0.1415 0.4131 0.8996 +vn 0.1769 0.4466 0.8771 +vn 0.1512 0.4285 0.8908 +vn 0.1755 0.4662 0.8671 +vn 0.1376 0.4504 0.8822 +vn 0.1567 0.4798 0.8633 +vn 0.1143 0.4452 0.8881 +vn 0.1284 0.4901 0.8622 +vn -0.0624 0.1552 0.9859 +vn -0.0154 0.1967 0.9804 +vn 0.0502 0.2749 0.9602 +vn 0.0811 0.3326 0.9396 +vn 0.0980 0.3792 0.9201 +vn 0.1069 0.4232 0.8997 +vn 0.1151 0.4751 0.8724 +vn -0.9408 -0.3389 0.0056 +vn -0.9409 -0.3385 0.0060 +vn -0.9410 -0.3384 0.0061 +vn -0.9810 -0.1933 -0.0167 +vn -0.9810 -0.1933 -0.0168 +vn 0.4039 -0.9080 0.1116 +vn 0.4040 -0.9080 0.1115 +vn 0.4039 -0.9080 0.1115 +vn -0.9318 0.3578 -0.0606 +vn 0.8560 -0.5080 0.0959 +vn 0.8560 -0.5080 0.0958 +vn -0.9622 0.2714 -0.0213 +vn -0.9622 0.2714 -0.0214 +vn 0.7680 -0.6256 0.1373 +vn 0.7680 -0.6256 0.1372 +vn -0.8121 0.5683 -0.1328 +vn -0.8121 0.5682 -0.1329 +vn -0.7078 0.6783 -0.1974 +vn -0.5123 0.8047 -0.2999 +vn -0.5123 0.8047 -0.3000 +vn -0.0067 0.8979 -0.4401 +vn 0.9683 -0.2492 -0.0164 +vn 0.7309 -0.6613 0.1686 +vn 0.9815 0.0655 -0.1801 +vn 0.3323 -0.9359 0.1172 +vn 0.3323 -0.9359 0.1171 +vn 0.3323 -0.9359 0.1173 +vn 0.7653 -0.6228 0.1628 +vn 0.7653 -0.6228 0.1627 +vn -0.9096 0.4100 -0.0671 +vn -0.9096 0.4102 -0.0669 +vn -0.9627 0.2690 -0.0282 +vn -0.9627 0.2690 -0.0281 +vn 0.7033 -0.6902 0.1704 +vn 0.0495 -0.0825 -0.9954 +vn 0.0551 -0.0726 -0.9958 +vn 0.0551 -0.0727 -0.9958 +vn -0.1099 -0.3837 -0.9169 +vn -0.1793 -0.5131 -0.8394 +vn -0.1793 -0.5130 -0.8394 +vn -0.1337 -0.4227 -0.8963 +vn -0.1549 -0.4704 -0.8688 +vn -0.1548 -0.4704 -0.8688 +vn 0.9969 -0.0775 -0.0138 +vn 0.9999 0.0009 0.0110 +vn 0.9999 0.0009 0.0111 +vn 0.9999 0.0009 0.0114 +vn 1.0000 -0.0040 -0.0066 +vn 0.9997 0.0111 -0.0198 +vn 0.9998 -0.0043 -0.0201 +vn 0.9998 -0.0043 -0.0202 +vn 0.9998 -0.0044 -0.0200 +vn 0.9999 -0.0034 -0.0152 +vn 0.9999 -0.0034 -0.0153 +vn 0.6304 0.2338 -0.7402 +vn 0.7087 -0.3741 0.5982 +vn 0.6883 -0.3224 -0.6499 +vn 0.5708 0.8115 -0.1248 +vn 0.5695 0.8046 0.1678 +vn 0.7225 -0.5750 0.3840 +vn 0.7087 -0.5366 -0.4580 +vn 0.7225 -0.6638 -0.1934 +vn -0.9972 -0.0744 0.0115 +vn -0.9972 -0.0743 0.0114 +vn -1.0000 0.0026 -0.0004 +vn -0.0000 -0.9854 0.1700 +vn -0.9835 -0.0325 -0.1781 +vn 0.2706 -0.5160 0.8127 +vn -0.0000 -0.5360 0.8442 +vn -0.0000 -0.9838 0.1793 +vn -0.7085 -0.7055 0.0167 +vn -0.9200 -0.3896 -0.0438 +vn -0.9937 -0.0210 -0.1099 +vn -0.3860 -0.9206 0.0597 +vn -0.9362 0.3098 -0.1661 +vn 0.5050 0.7993 -0.3256 +vn -0.1308 0.8933 -0.4301 +vn 0.0000 -0.2023 -0.9793 +vn 0.4656 -0.1810 -0.8663 +vn 0.7520 0.1019 0.6512 +vn 0.6996 0.1148 0.7052 +vn 0.9368 0.0336 0.3482 +vn -0.7125 -0.7007 0.0373 +vn -0.9219 -0.3858 -0.0358 +vn -0.9938 -0.0197 -0.1090 +vn -0.3892 -0.9165 0.0926 +vn -0.9394 0.3025 -0.1612 +vn 0.0147 -0.9926 0.1203 +vn 0.0152 -0.9882 0.1526 +vn 0.0161 -0.9839 0.1780 +vn 0.0446 -0.9918 0.1202 +vn 0.0446 -0.9872 0.1534 +vn -0.0142 0.9925 -0.1213 +vn -0.0147 0.9975 -0.0687 +vn 0.0159 -0.9849 0.1722 +vn 0.0030 -0.9852 0.1716 +vn 0.0446 -0.9828 0.1791 +vn 0.0638 -0.9917 0.1120 +vn 0.0606 -0.9873 0.1471 +vn -0.0445 0.9917 -0.1203 +vn -0.0444 0.9967 -0.0686 +vn -0.0135 0.9835 -0.1804 +vn 0.0053 -0.9766 0.2148 +vn 0.0052 -0.9793 0.2025 +vn 0.0174 -0.9785 0.2055 +vn 0.0174 -0.9773 0.2109 +vn 0.0446 -0.9841 0.1716 +vn 0.0563 -0.9827 0.1767 +vn 0.0679 -0.9923 0.1038 +vn 0.0593 -0.9865 0.1525 +vn -0.0604 0.9916 -0.1140 +vn -0.0635 0.9961 -0.0605 +vn -0.0446 0.9828 -0.1791 +vn 0.0031 0.9824 -0.1869 +vn -0.0136 0.9824 -0.1862 +vn 0.0446 -0.9809 0.1893 +vn 0.0446 -0.9773 0.2069 +vn 0.0166 -0.9823 0.1868 +vn 0.0553 -0.9836 0.1716 +vn 0.0526 -0.9833 0.1742 +vn -0.8256 0.1011 0.5551 +vn -0.8986 0.0786 0.4316 +vn -0.8962 0.0795 0.4364 +vn -0.0593 0.9904 -0.1252 +vn -0.0676 0.9963 -0.0525 +vn -0.0563 0.9826 -0.1768 +vn -0.0446 0.9814 -0.1865 +vn -0.0159 0.9680 -0.2504 +vn -0.0227 0.9655 -0.2596 +vn -0.0181 0.9766 -0.2141 +vn -0.0045 0.9802 -0.1978 +vn 0.0560 -0.9796 0.1932 +vn 0.0589 -0.9747 0.2154 +vn 0.0508 -0.9839 0.1716 +vn -0.9875 0.0283 0.1553 +vn -0.0526 0.9830 -0.1759 +vn -0.0553 0.9809 -0.1864 +vn -0.0445 0.9635 -0.2641 +vn -0.0446 0.9732 -0.2254 +vn 0.0519 -0.9800 0.1919 +vn 0.0565 -0.9730 0.2236 +vn -0.0508 0.9812 -0.1864 +vn -0.0587 0.9604 -0.2723 +vn -0.0559 0.9718 -0.2292 +vn -0.9624 -0.0487 -0.2673 +vn -0.9648 -0.0471 -0.2588 +vn -0.0562 0.9582 -0.2804 +vn -0.0519 0.9733 -0.2236 +vn 0.5676 -0.1476 -0.8100 +vn 0.8987 -0.0786 -0.4316 +vn 0.9841 -0.0319 -0.1749 +vn 0.9924 -0.0221 -0.1211 +vn 0.9835 -0.0324 -0.1781 +vn 0.0611 -0.9745 0.2161 +vn -0.0228 0.9610 -0.2757 +vn 0.0625 -0.9723 0.2254 +vn -0.0609 0.9601 -0.2730 +vn -0.0623 0.9574 -0.2820 +vn 0.0032 -0.9834 0.1815 +vn -0.0162 0.9577 -0.2872 +vn 0.0001 -0.1787 -0.9839 +vn -0.1383 -0.5229 0.8411 +vn 0.1440 0.8129 0.5642 +vn 0.0912 0.5948 0.7987 +vn -0.0884 -0.2567 0.9624 +vn -0.2028 -0.9703 0.1316 +vn -0.2062 -0.9691 0.1355 +vn -0.2043 -0.9714 0.1206 +vn -0.2023 -0.9725 0.1158 +vn -0.2098 -0.9674 0.1418 +vn -0.2064 -0.9704 0.1254 +vn -0.9685 0.2235 0.1098 +vn -0.9689 0.2231 0.1071 +vn -0.9793 0.1992 -0.0363 +vn 0.1946 0.9674 -0.1621 +vn 0.1983 0.9667 -0.1616 +vn 0.1963 0.9684 -0.1536 +vn 0.1918 0.9688 -0.1569 +vn 0.1950 0.9681 -0.1577 +vn 0.2014 0.9679 -0.1504 +vn 0.2012 0.9675 -0.1532 +vn -0.2030 -0.9476 0.2468 +vn 0.2019 0.9594 -0.1968 +vn -0.8863 0.2565 0.3856 +vn -0.8157 0.2651 0.5142 +vn -0.8856 0.2566 0.3871 +vn -0.2126 -0.9613 0.1755 +vn -0.2160 -0.9480 0.2338 +vn -0.2161 -0.9480 0.2338 +vn -0.2170 -0.9595 0.1794 +vn 0.1958 0.9638 -0.1811 +vn 0.1960 0.9606 -0.1969 +vn 0.1959 0.9607 -0.1967 +vn 0.1977 0.9635 -0.1805 +vn -0.2034 -0.9683 0.1448 +vn -0.2090 -0.9666 0.1482 +vn 0.2018 0.9663 -0.1599 +vn -0.2144 -0.9649 0.1518 +vn 0.1887 0.9686 -0.1620 +vn -0.2037 -0.9687 0.1420 +vn 0.2020 0.9641 -0.1724 +vn 0.0350 -0.9805 0.1935 +vn 0.0243 -0.9816 0.1893 +vn -0.0194 -0.9850 0.1714 +vn -0.0195 -0.9851 0.1708 +vn -0.0303 0.9835 -0.1786 +vn -0.0432 0.9832 -0.1771 +vn -0.7757 -0.1504 0.6130 +vn -0.8851 -0.2730 0.3770 +vn -0.8843 -0.2718 0.3796 +vn -0.7694 -0.1448 0.6222 +vn -0.9219 -0.3835 -0.0547 +vn -0.9213 -0.3842 -0.0598 +vn -0.1710 -0.0770 -0.9823 +vn -0.2026 -0.0813 -0.9759 +vn 0.2487 -0.0949 -0.9639 +vn 0.2475 -0.0947 -0.9642 +vn 0.7022 -0.1031 -0.7044 +vn 0.6983 -0.1037 -0.7083 +vn -0.6740 -0.0671 0.7357 +vn -0.8315 -0.4074 -0.3777 +vn -0.3503 -0.1008 -0.9312 +vn -0.2032 0.9615 -0.1848 +vn -0.1350 0.9718 -0.1935 +vn -0.1479 0.9702 -0.1919 +vn -0.2145 0.9594 -0.1833 +vn -0.0864 0.9783 -0.1884 +vn -0.0784 0.9794 -0.1859 +vn 0.1809 -0.9647 0.1917 +vn 0.2905 -0.9413 0.1717 +vn 0.1928 -0.9627 0.1896 +vn 0.1140 -0.9728 0.2019 +vn 0.0991 -0.9740 0.2038 +vn -0.2978 0.9392 -0.1709 +vn -0.9995 -0.0324 0.0059 +vn -0.9360 0.3463 -0.0631 +vn -0.3827 0.9089 -0.1656 +vn -0.3827 -0.9089 0.1656 +vn -0.5556 -0.8180 0.1490 +vn -0.7523 -0.6481 0.1181 +vn -0.8965 -0.4358 0.0794 +vn -0.4125 -0.4268 -0.8048 +vn -0.9808 -0.1919 0.0350 +vn -0.9994 -0.0186 0.0308 +vn -0.9993 -0.0186 0.0309 +vn -0.9469 0.3020 0.1101 +vn -0.8668 0.4564 0.2010 +vn -0.6734 0.6679 0.3168 +vn -0.5930 0.7275 0.3451 +vn -0.3440 0.8484 0.4024 +vn -0.2982 0.8624 0.4091 +vn -0.9999 -0.0040 0.0160 +vn -0.9999 -0.0040 0.0161 +vn -0.9361 0.3503 0.0302 +vn -0.9056 0.4218 0.0451 +vn -0.7095 0.6984 0.0943 +vn -0.6699 0.7358 0.0994 +vn -0.3804 0.9165 0.1238 +vn -0.3539 0.9269 0.1252 +vn -1.0000 -0.0005 0.0009 +vn -1.0000 -0.0005 0.0010 +vn -0.9249 0.3751 -0.0623 +vn -0.9233 0.3788 -0.0626 +vn -0.7082 0.6968 -0.1137 +vn -0.7059 0.6991 -0.1140 +vn -0.3834 0.9115 -0.1487 +vn -0.3818 0.9122 -0.1488 +vn -0.3813 -0.9075 0.1765 +vn -0.3798 -0.9081 0.1764 +vn -0.7054 -0.6967 0.1302 +vn -0.7053 -0.6969 0.1300 +vn -0.6258 0.3869 0.6773 +vn -0.6774 0.1319 0.7237 +vn -0.4790 0.6031 0.6379 +vn -0.2592 0.7475 0.6116 +vn -0.2592 -0.4838 0.8359 +vn -0.4790 -0.3393 0.8096 +vn -0.6258 -0.1232 0.7702 +vn 0.9967 0.0102 0.0804 +vn 0.9202 -0.3672 0.1357 +vn 0.9233 -0.3599 0.1346 +vn 0.9967 0.0187 0.0791 +vn 0.7038 -0.6866 0.1824 +vn 0.7081 -0.6823 0.1818 +vn 0.3807 -0.8997 0.2136 +vn 0.3837 -0.8984 0.2135 +vn 0.3818 0.9227 -0.0533 +vn 0.3787 0.9240 -0.0535 +vn 0.7054 0.7085 -0.0219 +vn 0.7010 0.7128 -0.0226 +vn 0.9213 0.3880 0.0250 +vn 0.9182 0.3953 0.0240 +vn -0.8184 -0.0436 -0.5730 +vn -0.7821 -0.1569 -0.6031 +vn -0.8413 0.2013 -0.5017 +vn -0.8229 0.3371 -0.4573 +vn -0.7202 0.5883 -0.3678 +vn -0.6498 0.6848 -0.3298 +vn -0.4114 0.8766 -0.2495 +vn -0.3501 0.9064 -0.2361 +vn -0.2071 -0.6673 -0.7154 +vn -0.2297 -0.6614 -0.7140 +vn -0.4172 -0.5825 -0.6976 +vn -0.4608 -0.5558 -0.6920 +vn -0.6203 -0.4184 -0.6635 +vn -0.6735 -0.3531 -0.6494 +vn -0.9986 -0.0017 -0.0536 +vn -0.9985 -0.0174 -0.0518 +vn -0.9262 0.3644 -0.0965 +vn -0.9207 0.3778 -0.0981 +vn -0.7110 0.6902 -0.1347 +vn -0.7031 0.6980 -0.1356 +vn -0.3855 0.9087 -0.1603 +vn -0.3799 0.9110 -0.1606 +vn -0.3776 -0.9244 0.0547 +vn -0.3831 -0.9221 0.0545 +vn -0.6998 -0.7137 0.0300 +vn -0.7077 -0.7059 0.0291 +vn -0.9183 -0.3958 -0.0073 +vn -0.9239 -0.3825 -0.0089 +vn 0.9978 -0.0658 0.0101 +vn 0.9978 -0.0661 0.0102 +vn 0.9961 -0.0867 0.0133 +vn -0.0014 -0.1517 -0.9884 +vn 0.0245 0.2351 -0.9717 +vn 0.1233 0.1705 -0.9776 +vn 0.0228 -0.1570 -0.9873 +vn 0.0406 0.5855 -0.8097 +vn 0.3348 0.4709 -0.8162 +vn 0.0539 0.8503 -0.5236 +vn 0.5382 0.6789 -0.4994 +vn 0.0597 0.9866 -0.1518 +vn 0.6150 0.7794 -0.1199 +vn 0.0499 0.9692 0.2412 +vn 0.5281 0.8051 0.2699 +vn 0.0299 0.8037 0.5943 +vn 0.3156 0.7038 0.6364 +vn 0.0165 0.5155 0.8567 +vn 0.1133 0.4609 0.8802 +vn -0.0025 0.1519 0.9884 +vn 0.0211 0.1511 0.9883 +vn -0.0395 -0.2307 0.9722 +vn 0.0599 -0.1890 0.9802 +vn -0.0697 -0.6095 0.7897 +vn 0.1901 -0.5504 0.8130 +vn -0.0697 -0.6096 0.7897 +vn -0.0889 -0.8741 0.4775 +vn 0.2929 -0.8311 0.4728 +vn -0.0938 -0.9840 0.1514 +vn 0.3157 -0.9378 0.1442 +vn -0.0889 -0.9772 -0.1928 +vn 0.2930 -0.9347 -0.2012 +vn -0.0697 -0.8187 -0.5700 +vn 0.1901 -0.7693 -0.6100 +vn -0.0395 -0.5122 -0.8579 +vn 0.0611 -0.4773 -0.8766 +vn -0.9931 0.1161 -0.0179 +vn -0.9931 0.1162 -0.0179 +vn -0.9956 0.0925 -0.0143 +vn -0.9956 0.0924 -0.0142 +vn 0.9996 -0.0273 -0.0086 +vn 0.9951 -0.0592 0.0792 +vn 0.9998 -0.0178 0.0069 +vn 0.9998 -0.0179 0.0069 +vn 0.9935 -0.1128 0.0180 +vn 0.9934 -0.1129 0.0180 +vn 0.9935 -0.1130 0.0168 +vn 0.9935 -0.1130 0.0167 +vn 0.9998 -0.0191 -0.0012 +vn 0.9998 -0.0191 -0.0013 +vn 0.9951 -0.0803 -0.0578 +vn 0.9978 -0.0528 -0.0390 +vn 0.9920 -0.1203 -0.0385 +vn -0.9935 0.1129 -0.0120 +vn -0.9935 0.1129 -0.0119 +vn -0.9912 0.1167 -0.0629 +vn -0.9867 0.1601 -0.0280 +vn -0.9867 0.1602 -0.0280 +vn -0.9941 0.1075 -0.0149 +vn -0.9941 0.1076 -0.0149 +vn -0.9941 0.1071 -0.0181 +vn -0.9941 0.1070 -0.0181 +vn -0.9867 0.1611 -0.0215 +vn -0.9912 0.1302 0.0249 +vn -0.9935 0.1112 -0.0227 +vn -0.9935 0.1112 -0.0226 +vn 0.0677 0.9004 0.4298 +vn 0.0678 0.9004 0.4298 +vn 0.0678 0.7296 -0.6805 +vn 0.0673 0.7226 -0.6879 +vn 0.0673 0.8960 0.4390 +vn 0.0226 0.3343 0.9422 +vn -0.0260 -0.1509 -0.9882 +vn -0.0774 0.1839 -0.9799 +vn -0.2593 0.5128 -0.8185 +vn -0.4487 0.7406 -0.5001 +vn -0.5205 0.8439 -0.1298 +vn -0.4486 0.8567 0.2545 +vn -0.2593 0.7350 0.6265 +vn -0.0774 0.4699 0.8793 +vn -0.0259 0.1531 0.9879 +vn -0.1267 -0.1699 0.9773 +vn -0.3181 -0.4943 0.8090 +vn -0.4547 -0.7559 0.4710 +vn -0.4841 -0.8648 0.1330 +vn -0.4548 -0.8625 -0.2221 +vn -0.3180 -0.7146 -0.6230 +vn -0.1267 -0.4557 -0.8811 +vn -0.8660 0.4919 -0.0896 +vn -0.5000 0.8520 -0.1552 +vn -0.5000 -0.8520 0.1552 +vn -0.4768 0.4204 0.7720 +vn -0.5505 0.1496 0.8213 +vn -0.4767 0.4204 0.7720 +vn -0.2753 0.6187 0.7359 +vn -0.2753 -0.3194 0.9068 +vn -0.4768 -0.1212 0.8706 +vn -0.2706 0.7695 0.5785 +vn 0.9147 -0.4028 0.0328 +vn -0.4278 0.4065 -0.8073 +vn -0.4238 0.4055 -0.8099 +vn 0.9195 -0.3930 -0.0104 +vn -0.4724 -0.0396 0.8805 +vn -0.5109 -0.0173 0.8595 +vn 0.9147 -0.4027 0.0328 +vn 0.9195 -0.3929 -0.0104 +vn 0.9326 -0.2755 0.2330 +vn -0.4655 -0.4834 -0.7414 +vn -0.3343 -0.5671 -0.7528 +vn 0.8821 -0.4710 0.0106 +vn -0.4610 0.7435 0.4843 +vn -0.5679 0.7188 0.4010 +vn 0.0030 -0.2387 0.9711 +vn 0.0030 -0.2388 0.9711 +vn 0.0031 -0.2386 0.9711 +vn 0.0058 -0.8554 0.5179 +vn 0.0058 -0.8555 0.5179 +vn 0.0059 -0.8554 0.5179 +vn 0.0059 -0.9715 -0.2368 +vn 0.0057 -0.9714 -0.2373 +vn 0.0059 -0.9715 -0.2370 +vn 0.0030 -0.5194 -0.8545 +vn 0.0031 -0.5195 -0.8544 +vn 0.0031 -0.5195 -0.8545 +vn 0.0030 -0.5194 -0.8546 +vn -0.0040 0.2387 -0.9711 +vn -0.0040 0.2386 -0.9711 +vn -0.0040 0.2388 -0.9711 +vn -0.0116 0.8556 -0.5175 +vn -0.0114 0.8554 -0.5178 +vn -0.0112 0.8552 -0.5181 +vn -0.0113 0.8554 -0.5179 +vn -0.0114 0.9715 0.2369 +vn -0.0114 0.9715 0.2368 +vn -0.0116 0.9714 0.2372 +vn -0.0113 0.9715 0.2368 +vn -0.0040 0.5196 0.8544 +vn -0.0040 0.5195 0.8545 +vn -0.0040 0.5195 0.8544 +vn -0.0040 0.5194 0.8545 +vn 0.5000 0.0374 -0.8652 +vn 0.5000 -0.0374 0.8652 +vn 0.5000 -0.0373 0.8652 +vn -0.6385 0.7592 -0.1260 +vn -0.6331 0.7545 -0.1727 +vn 0.5267 -0.6034 -0.5988 +vn 0.2118 -0.2181 -0.9527 +vn 0.5267 -0.6033 -0.5988 +vn 0.3758 -0.4743 0.7962 +vn -0.0371 0.0072 0.9993 +vn -0.6092 -0.7909 -0.0581 +vn -0.6104 -0.7917 0.0240 +vn 0.1468 0.1824 -0.9722 +vn 0.4221 0.5423 -0.7264 +vn 0.4222 0.5423 -0.7264 +vn 0.1469 0.1824 -0.9722 +vn 0.0558 0.0831 0.9950 +vn 0.3940 0.5200 0.7579 +vn 0.5000 0.4249 -0.7546 +vn 0.5000 -0.4249 0.7546 +vn 0.5000 -0.4249 0.7547 +vn 0.5000 -0.4248 0.7546 +vn -0.8194 -0.5640 0.1027 +vn -0.8194 -0.5640 0.1028 +vn -0.5794 -0.5256 -0.6230 +vn -0.5794 -0.5255 -0.6230 +vn 0.5794 0.2721 -0.7683 +vn 0.5794 0.2720 -0.7683 +vn 0.8194 0.5640 -0.1028 +vn 0.5794 0.5255 0.6230 +vn 0.5794 0.5256 0.6230 +vn -0.5794 -0.2721 0.7683 +vn -0.9999 -0.0025 -0.0139 +vn -0.9999 0.0026 0.0143 +vn -0.7152 -0.1253 -0.6876 +vn -0.7750 -0.1133 -0.6217 +vn -0.6983 0.1283 0.7042 +vn -0.7864 0.1107 0.6078 +vn -0.0817 0.9966 -0.0118 +vn -0.2007 0.9739 -0.1059 +vn -0.0646 0.8796 -0.4713 +vn -0.0647 0.8796 -0.4713 +vn -0.0504 0.8706 -0.4894 +vn -0.0505 0.8706 -0.4894 +vn -0.7071 -0.6956 0.1268 +vn -0.1568 -0.0834 0.9841 +vn -0.1037 -0.0481 0.9934 +vn -0.1191 -0.0429 0.9920 +vn -0.1636 -0.0827 0.9830 +vn -0.1595 -0.0786 0.9841 +vn -0.1176 -0.0334 0.9925 +vn -0.1781 -0.0649 0.9819 +vn -0.1393 -0.0171 0.9901 +vn -0.1876 -0.0600 0.9804 +vn -0.1708 0.0027 0.9853 +vn -0.2306 -0.1516 0.9612 +vn -0.2329 -0.1546 0.9601 +vn -0.2981 -0.2413 0.9235 +vn -0.2712 -0.2024 0.9410 +vn -0.2732 -0.2048 0.9399 +vn -0.2980 -0.2412 0.9236 +vn -0.3003 -0.2433 0.9223 +vn -0.3037 -0.2418 0.9216 +vn -0.3063 -0.2382 0.9217 +vn -0.3331 -0.3091 0.8908 +vn -0.3408 -0.3385 0.8771 +vn -0.3240 -0.2929 0.8996 +vn -0.3149 -0.2722 0.9092 +vn -0.3593 -0.3450 0.8671 +vn -0.3343 -0.3078 0.8908 +vn -0.3792 -0.3331 0.8633 +vn -0.3598 -0.3039 0.8822 +vn -0.3999 -0.3111 0.8622 +vn -0.3641 -0.2805 0.8881 +vn -0.1868 -0.0633 0.9804 +vn -0.1673 -0.0038 0.9859 +vn -0.2328 -0.1545 0.9602 +vn -0.2738 -0.2056 0.9396 +vn -0.3468 -0.2650 0.8997 +vn -0.3099 -0.2395 0.9201 +vn -0.3913 -0.2930 0.8724 +vn -0.0595 0.9982 0.0060 +vn -0.0596 0.9982 0.0060 +vn -0.0598 0.9982 0.0062 +vn -0.0596 0.9982 0.0061 +vn -0.2089 0.9778 -0.0168 +vn -0.2089 0.9778 -0.0167 +vn 0.9937 -0.0135 0.1116 +vn 0.9937 -0.0135 0.1115 +vn 0.9937 -0.0136 0.1114 +vn -0.6960 0.7155 -0.0606 +vn 0.8042 -0.5866 0.0958 +vn 0.8042 -0.5866 0.0957 +vn -0.6286 0.7775 -0.0213 +vn 0.8776 -0.4594 0.1373 +vn 0.8776 -0.4594 0.1372 +vn -0.8422 0.5225 -0.1329 +vn -0.9023 0.3833 -0.1974 +vn -0.9415 0.1538 -0.2999 +vn -0.8279 -0.3476 -0.4400 +vn -0.8279 -0.3476 -0.4401 +vn 0.6106 -0.7918 -0.0165 +vn 0.6106 -0.7918 -0.0164 +vn 0.8958 -0.4113 0.1686 +vn 0.3265 -0.9279 -0.1801 +vn 0.9911 0.0634 0.1173 +vn 0.9911 0.0633 0.1172 +vn 0.8739 -0.4580 0.1628 +vn 0.8740 -0.4579 0.1628 +vn -0.7350 0.6747 -0.0674 +vn -0.7352 0.6745 -0.0671 +vn -0.7353 0.6745 -0.0670 +vn -0.6265 0.7789 -0.0282 +vn -0.6265 0.7789 -0.0281 +vn -0.6266 0.7789 -0.0281 +vn 0.9114 -0.3745 0.1704 +vn 0.9115 -0.3745 0.1704 +vn 0.0953 -0.0130 -0.9954 +vn 0.0953 -0.0131 -0.9954 +vn 0.0951 -0.0135 -0.9954 +vn 0.0884 -0.0220 -0.9958 +vn 0.0885 -0.0220 -0.9958 +vn 0.3094 0.2522 -0.9169 +vn 0.4009 0.3669 -0.8394 +vn 0.4010 0.3669 -0.8394 +vn 0.3358 0.2894 -0.8963 +vn 0.3358 0.2895 -0.8963 +vn 0.3713 0.3277 -0.8688 +vn 0.3713 0.3276 -0.8688 +vn -0.9969 -0.0775 -0.0138 +vn -0.9999 0.0009 0.0110 +vn -0.9999 0.0009 0.0111 +vn -0.9999 0.0009 0.0114 +vn -1.0000 -0.0040 -0.0066 +vn -1.0000 -0.0040 -0.0067 +vn -0.9997 0.0111 -0.0198 +vn -0.9998 -0.0043 -0.0202 +vn -0.9998 -0.0043 -0.0201 +vn -0.9999 -0.0034 -0.0152 +vn -0.9999 -0.0034 -0.0153 +vn -0.9954 0.0949 -0.0146 +vn -0.9954 0.0951 -0.0146 +vn -0.9954 0.0950 -0.0146 +vn -0.9856 0.1669 -0.0257 +vn 0.9972 -0.0743 0.0114 +vn 0.9972 -0.0744 0.0115 +vn 1.0000 0.0026 -0.0004 +vn 0.9835 -0.0325 -0.1781 +vn -0.2706 -0.5160 0.8127 +vn -0.4084 0.8980 -0.1636 +vn -0.2088 0.9621 -0.1753 +vn 0.2088 0.9621 -0.1753 +vn 0.4084 0.8980 -0.1636 +vn 0.7071 0.6957 -0.1268 +vn -0.7071 0.6957 -0.1268 +vn -0.8194 0.5640 -0.1027 +vn 0.9984 -0.0227 0.0516 +vn -0.5034 -0.3952 -0.7684 +vn -0.4923 -0.3987 -0.7737 +vn 0.9984 -0.0230 0.0511 +vn -0.5701 0.4169 0.7080 +vn -0.5599 0.4199 0.7143 +vn -0.5701 0.4169 0.7079 +vn -0.5600 0.4199 0.7142 +vn 0.3758 -0.4743 0.7961 +vn 0.5793 0.2721 -0.7683 +vn 0.8194 0.5640 -0.1027 +vn 0.5276 0.4322 0.7314 +vn 0.5276 -0.4321 -0.7314 +vn 0.5276 -0.4322 -0.7313 +s off +f 16/1/1 17/2/1 18/3/1 +f 19/4/2 20/5/2 21/6/2 +f 22/7/3 23/8/3 24/9/3 +f 25/10/4 26/11/4 27/12/4 +f 28/13/5 29/14/5 30/15/5 +f 31/16/6 32/17/6 33/18/6 +f 34/19/7 35/20/7 36/21/7 +f 37/22/8 38/23/8 39/24/8 +f 40/25/9 41/26/9 42/27/9 +f 43/28/3 44/29/3 45/30/3 +f 46/31/3 47/32/3 48/33/3 +f 49/34/3 50/35/3 51/36/3 +f 52/37/3 53/38/3 54/39/3 +f 55/40/3 56/41/3 57/42/3 +f 58/43/3 59/44/3 60/45/3 +f 83/46/1 84/47/1 85/48/1 +f 86/49/2 87/50/2 88/51/2 +f 89/52/3 90/53/3 91/54/3 +f 92/55/10 93/56/10 94/57/10 +f 95/58/11 96/59/11 97/60/11 +f 98/61/12 99/62/12 100/63/12 +f 101/64/13 102/65/13 103/66/13 +f 104/67/14 105/68/14 106/69/14 +f 107/70/15 108/71/15 109/72/15 +f 110/73/3 111/74/3 112/75/3 +f 113/76/3 114/77/3 115/78/3 +f 116/79/3 117/80/3 118/81/3 +f 119/82/3 120/83/3 121/84/3 +f 122/85/3 123/86/3 124/87/3 +f 125/88/3 126/89/3 127/90/3 +f 69/91/16 146/92/16 147/93/16 +f 168/94/1 169/95/1 170/96/1 +f 168/94/1 170/96/1 171/97/1 +f 172/98/1 173/99/1 174/100/1 +f 172/98/1 174/100/1 175/101/1 +f 176/102/1 177/103/1 178/104/1 +f 176/102/1 178/104/1 179/105/1 +f 184/106/3 185/107/3 186/108/3 +f 184/106/3 186/108/3 187/109/3 +f 192/110/3 193/111/3 185/107/3 +f 192/110/3 185/107/3 184/106/3 +f 203/112/3 204/113/3 193/111/3 +f 203/112/3 193/111/3 192/110/3 +f 217/114/3 218/115/3 204/113/3 +f 217/114/3 204/113/3 203/112/3 +f 224/116/1 225/117/1 226/118/1 +f 224/116/1 226/118/1 227/119/1 +f 225/117/1 224/116/1 245/120/1 +f 225/117/1 245/120/1 246/121/1 +f 246/121/1 245/120/1 257/122/1 +f 246/121/1 257/122/1 258/123/1 +f 258/123/1 257/122/1 266/124/1 +f 258/123/1 266/124/1 267/125/1 +f 269/126/17 290/127/17 291/128/17 +f 295/129/18 297/130/1 298/131/1 +f 298/131/1 297/130/1 299/132/1 +f 298/131/1 299/132/1 300/133/1 +f 301/134/3 302/135/3 303/136/3 +f 301/134/3 303/136/3 304/137/3 +f 302/135/3 305/138/3 306/139/3 +f 302/135/3 306/139/3 303/136/3 +f 324/140/19 325/141/19 326/142/19 +f 324/140/20 326/142/20 327/143/20 +f 357/144/3 358/145/3 359/146/3 +f 357/144/3 359/146/3 360/147/3 +f 358/145/3 361/148/3 362/149/3 +f 358/145/3 362/149/3 359/146/3 +f 363/150/3 364/151/3 362/149/3 +f 363/150/3 362/149/3 361/148/3 +f 399/152/21 400/153/21 401/154/21 +f 399/152/21 401/154/21 402/155/21 +f 403/156/22 404/157/22 405/158/22 +f 403/156/22 405/158/22 406/159/22 +f 407/160/23 408/161/23 409/162/23 +f 407/160/23 409/162/23 410/163/23 +f 411/164/24 412/165/24 413/166/24 +f 411/164/24 413/166/24 414/167/24 +f 415/168/1 416/169/1 417/170/1 +f 415/168/1 417/170/1 418/171/1 +f 419/172/3 420/173/3 421/174/3 +f 419/172/3 421/174/3 422/175/3 +f 423/176/25 424/177/25 425/178/25 +f 423/176/25 425/178/25 426/179/25 +f 427/180/26 428/181/26 429/182/26 +f 427/180/26 429/182/26 430/183/26 +f 431/184/16 432/185/16 433/186/16 +f 431/184/16 433/186/16 434/187/16 +f 435/188/27 436/189/27 437/190/27 +f 435/188/27 437/190/27 438/191/27 +f 511/192/1 512/193/1 513/194/1 +f 511/192/1 513/194/1 514/195/1 +f 515/196/1 511/192/1 514/195/1 +f 515/196/1 514/195/1 516/197/1 +f 517/198/1 515/196/1 516/197/1 +f 517/198/1 516/197/1 518/199/1 +f 519/200/1 517/198/1 518/199/1 +f 519/200/1 518/199/1 520/201/1 +f 521/202/1 522/203/1 523/204/1 +f 521/202/1 523/204/1 524/205/1 +f 525/206/1 521/202/1 524/205/1 +f 525/206/1 524/205/1 526/207/1 +f 527/208/1 525/206/1 526/207/1 +f 527/208/1 526/207/1 528/209/1 +f 512/193/1 527/208/1 528/209/1 +f 512/193/1 528/209/1 513/194/1 +f 48/33/3 551/210/3 552/211/3 +f 48/33/3 552/211/3 46/31/3 +f 571/212/3 572/213/3 573/214/3 +f 571/212/3 573/214/3 574/215/3 +f 572/213/3 575/216/3 576/217/3 +f 572/213/3 576/217/3 573/214/3 +f 575/216/3 577/218/3 578/219/3 +f 575/216/3 578/219/3 576/217/3 +f 577/218/3 579/220/3 580/221/3 +f 577/218/3 580/221/3 578/219/3 +f 581/222/3 582/223/3 583/224/3 +f 581/222/3 583/224/3 584/225/3 +f 582/223/3 585/226/3 586/227/3 +f 582/223/3 586/227/3 583/224/3 +f 585/226/3 587/228/3 588/229/3 +f 585/226/3 588/229/3 586/227/3 +f 587/228/3 571/212/3 574/215/3 +f 587/228/3 574/215/3 588/229/3 +f 711/230/28 713/231/28 714/232/28 +f 711/230/28 714/232/28 712/233/28 +f 713/231/28 715/234/28 716/235/28 +f 713/231/28 716/235/28 714/232/28 +f 715/234/28 683/236/28 686/237/28 +f 715/234/28 686/237/28 716/235/28 +f 718/238/29 721/239/29 722/240/29 +f 718/238/29 722/240/29 719/241/29 +f 721/239/29 723/242/29 724/243/29 +f 721/239/29 724/243/29 722/240/29 +f 723/242/29 725/244/29 726/245/29 +f 723/242/29 726/245/29 724/243/29 +f 725/244/29 727/246/29 728/247/29 +f 725/244/29 728/247/29 726/245/29 +f 727/246/29 729/248/29 730/249/29 +f 727/246/29 730/249/29 728/247/29 +f 729/248/29 731/250/29 732/251/29 +f 729/248/29 732/251/29 730/249/29 +f 867/252/30 868/253/30 869/254/30 +f 867/252/30 869/254/30 870/255/30 +f 45/30/3 871/256/3 872/257/3 +f 45/30/3 872/257/3 43/28/3 +f 887/258/3 888/259/3 889/260/3 +f 887/258/3 889/260/3 890/261/3 +f 891/262/3 892/263/3 893/264/3 +f 891/262/3 893/264/3 894/265/3 +f 895/266/3 896/267/3 897/268/3 +f 895/266/3 897/268/3 898/269/3 +f 899/270/3 900/271/3 901/272/3 +f 899/270/3 901/272/3 902/273/3 +f 903/274/3 904/275/3 905/276/3 +f 903/274/3 905/276/3 906/277/3 +f 907/278/3 908/279/3 909/280/3 +f 907/278/3 909/280/3 910/281/3 +f 911/282/3 912/283/3 913/284/3 +f 911/282/3 913/284/3 914/285/3 +f 915/286/31 916/287/31 917/288/31 +f 915/286/31 917/288/31 918/289/31 +f 919/290/3 920/291/3 921/292/3 +f 919/290/31 921/292/31 922/293/31 +f 923/294/31 924/295/31 925/296/31 +f 923/294/3 925/296/3 926/297/3 +f 927/298/3 928/299/3 929/300/3 +f 927/298/31 929/300/31 930/301/31 +f 931/302/3 932/303/3 933/304/3 +f 931/302/3 933/304/3 934/305/3 +f 935/306/32 936/307/32 937/308/32 +f 935/306/32 937/308/32 938/309/32 +f 939/310/33 940/311/33 941/312/33 +f 939/310/33 941/312/33 942/313/33 +f 943/314/34 944/315/34 945/316/34 +f 943/314/34 945/316/34 946/317/34 +f 947/318/35 948/319/35 949/320/35 +f 947/318/35 949/320/35 950/321/35 +f 951/322/36 952/323/36 953/324/36 +f 951/322/36 953/324/36 954/325/36 +f 955/326/37 956/327/37 957/328/37 +f 955/326/37 957/328/37 958/329/37 +f 959/330/38 960/331/38 961/332/38 +f 959/330/38 961/332/38 962/333/38 +f 963/334/30 964/335/30 965/336/30 +f 963/334/30 965/336/30 966/337/30 +f 967/338/39 968/339/39 969/340/39 +f 967/338/39 969/340/39 970/341/39 +f 971/342/40 972/343/40 973/344/40 +f 971/342/40 973/344/40 974/345/40 +f 975/346/41 976/347/41 977/348/41 +f 975/346/41 977/348/41 978/349/41 +f 979/350/42 980/351/42 981/352/42 +f 979/350/42 981/352/42 982/353/42 +f 997/354/30 998/355/30 999/356/30 +f 997/354/30 999/356/30 1000/357/30 +f 1001/358/3 1002/359/3 1003/360/3 +f 1001/358/3 1003/360/3 1004/361/3 +f 1005/362/3 1006/363/3 1007/364/3 +f 1005/362/3 1007/364/3 1008/365/3 +f 1009/366/3 1010/367/3 1011/368/3 +f 1009/366/3 1011/368/3 1012/369/3 +f 1013/370/3 1014/371/3 1015/372/3 +f 1013/370/3 1015/372/3 1016/373/3 +f 1017/374/3 1018/375/3 1019/376/3 +f 1017/374/3 1019/376/3 1020/377/3 +f 1021/378/3 1022/379/3 1023/380/3 +f 1021/378/3 1023/380/3 1024/381/3 +f 1025/382/43 1026/383/43 1027/384/43 +f 1025/382/43 1027/384/43 1028/385/43 +f 1033/386/44 1034/387/44 1035/388/44 +f 1033/386/44 1035/388/44 1036/389/44 +f 1037/390/45 1038/391/45 1039/392/45 +f 1037/390/45 1039/392/45 1040/393/45 +f 1045/394/46 1046/395/46 1047/396/46 +f 1045/394/46 1047/396/46 1048/397/46 +f 1089/398/26 1090/399/26 1091/400/26 +f 1089/398/26 1091/400/26 1092/401/26 +f 1090/399/26 1093/402/26 1094/403/26 +f 1090/399/26 1094/403/26 1091/400/26 +f 1093/402/26 1095/404/26 1096/405/26 +f 1093/402/26 1096/405/26 1094/403/26 +f 1095/404/26 1097/406/26 1098/407/26 +f 1095/404/26 1098/407/26 1096/405/26 +f 1097/406/26 1099/408/26 1100/409/26 +f 1097/406/26 1100/409/26 1098/407/26 +f 1101/410/26 1102/411/26 1103/412/26 +f 1101/410/26 1103/412/26 1104/413/26 +f 1102/411/26 1105/414/26 1106/415/26 +f 1102/411/26 1106/415/26 1103/412/26 +f 1105/414/26 1089/398/26 1092/401/26 +f 1105/414/26 1092/401/26 1106/415/26 +f 1139/416/47 1140/417/47 1141/418/47 +f 1139/416/48 1141/418/48 1142/419/48 +f 1143/420/49 1144/421/49 1145/422/49 +f 1143/420/50 1145/422/50 1146/423/50 +f 1147/424/51 1148/425/51 1149/426/51 +f 1147/424/51 1149/426/51 1150/427/51 +f 1151/428/52 1152/429/52 1153/430/52 +f 1151/428/52 1153/430/52 1154/431/52 +f 1155/432/53 1156/433/53 1157/434/53 +f 1155/432/53 1157/434/53 1158/435/53 +f 1159/436/54 1160/437/54 1161/438/54 +f 1159/436/54 1161/438/54 1162/439/54 +f 1163/440/55 1164/441/55 1165/442/55 +f 1163/440/55 1165/442/55 1166/443/55 +f 1167/444/56 1168/445/56 1169/446/56 +f 1167/444/56 1169/446/56 1170/447/56 +f 1171/448/57 1172/449/57 1173/450/57 +f 1171/448/57 1173/450/57 1174/451/57 +f 1175/452/57 1176/453/57 1177/454/57 +f 1175/452/57 1177/454/57 1178/455/57 +f 1179/456/57 1180/457/57 1181/458/57 +f 1179/456/57 1181/458/57 1182/459/57 +f 1183/460/57 1184/461/57 1185/462/57 +f 1183/460/57 1185/462/57 1186/463/57 +f 1187/464/57 1188/465/57 1189/466/57 +f 1187/464/57 1189/466/57 1190/467/57 +f 1191/468/57 1192/469/57 1193/470/57 +f 1191/468/57 1193/470/57 1194/471/57 +f 1195/472/57 1196/473/57 1197/474/57 +f 1195/472/57 1197/474/57 1198/475/57 +f 1199/476/58 1200/477/58 1201/478/58 +f 1199/476/57 1201/478/57 1202/479/57 +f 1279/480/59 1280/481/59 1281/482/59 +f 1279/480/59 1281/482/59 1282/483/59 +f 1283/484/60 1284/485/60 1285/486/60 +f 1283/484/61 1285/486/61 1286/487/61 +f 1287/488/62 1288/489/62 1289/490/62 +f 1287/488/62 1289/490/62 1290/491/62 +f 1291/492/63 1292/493/63 1293/494/63 +f 1291/492/63 1293/494/63 1294/495/63 +f 57/42/3 1313/496/3 1314/497/3 +f 57/42/3 1314/497/3 55/40/3 +f 51/36/3 1333/498/3 1334/499/3 +f 51/36/3 1334/499/3 49/34/3 +f 1377/500/64 1378/501/64 1379/502/64 +f 1377/500/64 1379/502/64 1380/503/64 +f 1389/504/65 1390/505/65 1391/506/65 +f 1389/504/65 1391/506/65 1392/507/65 +f 1393/508/66 1394/509/66 1395/510/66 +f 1393/508/66 1395/510/66 1396/511/66 +f 1453/512/67 1455/513/67 1456/514/67 +f 1465/515/68 1466/516/68 1467/517/68 +f 1465/515/69 1467/517/69 1468/518/69 +f 1469/519/70 1471/520/70 1472/521/71 +f 1473/522/72 1475/523/72 1476/524/72 +f 1477/525/73 1478/526/73 1479/527/73 +f 1477/525/73 1479/527/73 1480/528/73 +f 1481/529/74 1482/530/74 1483/531/74 +f 1481/529/74 1483/531/74 1484/532/74 +f 1489/533/75 1490/534/75 1491/535/75 +f 1489/533/75 1491/535/75 1492/536/75 +f 1509/537/26 1510/538/26 1511/539/26 +f 1509/537/26 1511/539/26 1512/540/26 +f 1545/541/28 1546/542/28 1547/543/28 +f 1545/541/28 1547/543/28 1548/544/28 +f 1548/544/28 1547/543/28 1550/545/28 +f 1548/544/28 1550/545/28 1549/546/28 +f 1557/547/3 1558/548/3 23/8/3 +f 1557/547/3 23/8/3 22/7/3 +f 1559/549/3 1560/550/3 872/257/3 +f 1559/549/3 872/257/3 871/256/3 +f 1561/551/3 1562/552/3 552/211/3 +f 1561/551/3 552/211/3 551/210/3 +f 1563/553/3 1564/554/3 1562/552/3 +f 1563/553/3 1562/552/3 1561/551/3 +f 52/37/3 54/39/3 1334/499/3 +f 52/37/3 1334/499/3 1333/498/3 +f 58/43/3 60/45/3 1314/497/3 +f 58/43/3 1314/497/3 1313/496/3 +f 1569/555/3 1570/556/3 1571/557/3 +f 1569/555/3 1571/557/3 1572/558/3 +f 1573/559/3 1574/560/3 1575/561/3 +f 1573/559/3 1575/561/3 1576/562/3 +f 1577/563/3 1578/564/3 1579/565/3 +f 1577/563/3 1579/565/3 1580/566/3 +f 1581/567/3 1582/568/3 1583/569/3 +f 1581/567/3 1583/569/3 1584/570/3 +f 1585/571/3 1586/572/3 1587/573/3 +f 1585/571/3 1587/573/3 1588/574/3 +f 1589/575/3 1590/576/3 1591/577/3 +f 1589/575/3 1591/577/3 1592/578/3 +f 1593/579/3 1594/580/3 1595/581/3 +f 1593/579/3 1595/581/3 1596/582/3 +f 1605/583/3 1606/584/3 1607/585/3 +f 1605/583/3 1607/585/3 1608/586/3 +f 1615/587/16 1616/588/16 135/589/16 +f 1637/590/1 1638/591/1 1639/592/1 +f 1637/590/1 1639/592/1 1640/593/1 +f 1641/594/1 1642/595/1 1643/596/1 +f 1641/594/1 1643/596/1 1644/597/1 +f 1645/598/1 1646/599/1 1647/600/1 +f 1645/598/1 1647/600/1 1648/601/1 +f 1653/602/3 1654/603/3 1655/604/3 +f 1653/602/3 1655/604/3 1656/605/3 +f 1654/603/3 1661/606/3 1662/607/3 +f 1654/603/3 1662/607/3 1655/604/3 +f 1661/606/3 1672/608/3 1673/609/3 +f 1661/606/3 1673/609/3 1662/607/3 +f 1672/608/3 1686/610/3 1687/611/3 +f 1672/608/3 1687/611/3 1673/609/3 +f 1693/612/1 1694/613/1 1695/614/1 +f 1693/612/1 1695/614/1 1696/615/1 +f 1714/616/1 1695/614/1 1694/613/1 +f 1714/616/1 1694/613/1 1715/617/1 +f 1726/618/1 1714/616/1 1715/617/1 +f 1726/618/1 1715/617/1 1727/619/1 +f 1735/620/1 1726/618/1 1727/619/1 +f 1735/620/1 1727/619/1 1736/621/1 +f 1740/622/76 1739/623/76 1759/624/76 +f 1740/622/76 1759/624/76 1760/625/76 +f 1764/626/77 1765/627/1 1766/628/1 +f 1765/627/1 1768/629/1 1769/630/1 +f 1765/627/1 1769/630/1 1766/628/1 +f 1793/631/78 1794/632/78 1795/633/78 +f 1793/631/79 1795/633/79 1796/634/79 +f 1828/635/3 1829/636/3 1830/637/3 +f 1828/635/3 1830/637/3 1831/638/3 +f 1831/638/3 1830/637/3 1832/639/3 +f 1831/638/3 1832/639/3 1833/640/3 +f 1832/639/3 1834/641/3 1835/642/3 +f 1832/639/3 1835/642/3 1833/640/3 +f 1870/643/80 1871/644/80 1872/645/80 +f 1870/643/80 1872/645/80 1873/646/80 +f 1874/647/81 1875/648/81 1876/649/81 +f 1874/647/81 1876/649/81 1877/650/81 +f 1878/651/82 1879/652/82 1880/653/82 +f 1878/651/82 1880/653/82 1881/654/82 +f 1882/655/83 1883/656/83 1884/657/83 +f 1882/655/83 1884/657/83 1885/658/83 +f 1886/659/1 1887/660/1 1888/661/1 +f 1886/659/1 1888/661/1 1889/662/1 +f 1890/663/3 1891/664/3 1892/665/3 +f 1890/663/3 1892/665/3 1893/666/3 +f 1894/667/25 1895/668/25 1896/669/25 +f 1894/667/25 1896/669/25 1897/670/25 +f 1898/671/27 1899/672/27 1900/673/27 +f 1898/671/27 1900/673/27 1901/674/27 +f 1902/675/16 1903/676/16 1904/677/16 +f 1902/675/16 1904/677/16 1905/678/16 +f 1906/679/26 1907/680/26 1908/681/26 +f 1906/679/26 1908/681/26 1909/682/26 +f 1984/683/1 1985/684/1 1986/685/1 +f 1984/683/1 1986/685/1 1987/686/1 +f 1988/687/1 1989/688/1 1985/684/1 +f 1988/687/1 1985/684/1 1984/683/1 +f 1990/689/1 1991/690/1 1989/688/1 +f 1990/689/1 1989/688/1 1988/687/1 +f 1992/691/1 1993/692/1 1991/690/1 +f 1992/691/1 1991/690/1 1990/689/1 +f 1994/693/1 1995/694/1 1996/695/1 +f 1994/693/1 1996/695/1 1997/696/1 +f 1987/686/1 1986/685/1 1999/697/1 +f 2018/698/3 2019/699/3 2020/700/3 +f 2018/698/3 2020/700/3 2021/701/3 +f 2040/702/3 2041/703/3 2042/704/3 +f 2040/702/3 2042/704/3 2043/705/3 +f 2043/705/3 2042/704/3 2044/706/3 +f 2043/705/3 2044/706/3 2045/707/3 +f 2045/707/3 2044/706/3 2046/708/3 +f 2045/707/3 2046/708/3 2047/709/3 +f 2047/709/3 2046/708/3 2048/710/3 +f 2047/709/3 2048/710/3 2049/711/3 +f 2050/712/3 2051/713/3 2052/714/3 +f 2050/712/3 2052/714/3 2053/715/3 +f 2053/715/3 2052/714/3 2054/716/3 +f 2053/715/3 2054/716/3 2055/717/3 +f 2055/717/3 2054/716/3 2056/718/3 +f 2055/717/3 2056/718/3 2057/719/3 +f 2057/719/3 2056/718/3 2041/703/3 +f 2057/719/3 2041/703/3 2040/702/3 +f 2154/720/84 2155/721/84 2156/722/84 +f 2154/720/84 2156/722/84 2157/723/84 +f 2190/724/85 2191/725/85 2192/726/85 +f 2190/724/86 2192/726/86 2193/727/86 +f 2194/728/87 2195/729/87 2196/730/87 +f 2194/728/88 2196/730/88 2197/731/88 +f 2202/732/84 2203/733/84 2204/734/84 +f 2202/732/84 2204/734/84 2205/735/84 +f 2206/736/84 2207/737/84 2208/738/84 +f 2206/736/84 2208/738/84 2209/739/84 +f 2210/740/84 2211/741/84 2212/742/84 +f 2210/740/84 2212/742/84 2213/743/84 +f 2214/744/84 2215/745/84 2216/746/84 +f 2214/744/84 2216/746/84 2217/747/84 +f 2218/748/89 2219/749/89 2220/750/89 +f 2218/748/89 2220/750/89 2221/751/89 +f 2222/752/89 2223/753/89 2224/754/89 +f 2222/752/89 2224/754/89 2225/755/89 +f 2226/756/89 2227/757/89 2228/758/89 +f 2226/756/89 2228/758/89 2229/759/89 +f 2230/760/89 2231/761/89 2232/762/89 +f 2230/760/89 2232/762/89 2233/763/89 +f 2234/764/89 2235/765/89 2236/766/89 +f 2234/764/89 2236/766/89 2237/767/89 +f 2238/768/89 2239/769/89 2240/770/89 +f 2238/768/89 2240/770/89 2241/771/89 +f 2242/772/89 2243/773/89 2244/774/89 +f 2242/772/89 2244/774/89 2245/775/89 +f 2246/776/89 2247/777/89 2248/778/89 +f 2246/776/89 2248/778/89 2249/779/89 +f 2282/780/90 2283/781/90 2284/782/90 +f 2282/780/90 2284/782/90 2285/783/90 +f 2286/784/91 2287/785/91 2288/786/91 +f 2286/784/91 2288/786/91 2289/787/91 +f 2290/788/92 2291/789/92 2292/790/92 +f 2290/788/92 2292/790/92 2293/791/92 +f 2298/792/93 2299/793/93 2300/794/93 +f 2298/792/93 2300/794/93 2301/795/93 +f 2302/796/94 2303/797/94 2304/798/94 +f 2302/796/94 2304/798/94 2305/799/94 +f 2310/800/95 2312/801/95 2313/802/95 +f 2314/803/96 2315/804/96 2316/805/96 +f 2314/803/96 2316/805/96 2317/806/96 +f 2318/807/97 2319/808/97 2320/809/97 +f 2318/807/97 2320/809/97 2321/810/97 +f 2322/811/98 2323/812/98 2324/813/98 +f 2322/811/98 2324/813/98 2325/814/98 +f 2326/815/99 2327/816/99 2328/817/99 +f 2326/815/99 2328/817/99 2329/818/99 +f 2330/819/100 2331/820/100 2332/821/100 +f 2330/819/100 2332/821/100 2333/822/100 +f 2334/823/101 2335/824/101 2336/825/101 +f 2334/823/101 2336/825/101 2337/826/101 +f 2338/827/102 2339/828/102 2340/829/102 +f 2338/827/102 2340/829/102 2341/830/102 +f 2342/831/103 2343/832/103 2344/833/103 +f 2346/834/90 2347/835/90 2348/836/90 +f 2346/834/90 2348/836/90 2349/837/90 +f 2350/838/103 2351/839/103 2352/840/103 +f 2350/838/103 2352/840/103 2353/841/103 +f 2354/842/102 2355/843/102 2356/844/102 +f 2354/842/102 2356/844/102 2357/845/102 +f 2358/846/101 2359/847/101 2360/848/101 +f 2358/846/101 2360/848/101 2361/849/101 +f 2362/850/100 2363/851/100 2364/852/100 +f 2362/850/100 2364/852/100 2365/853/100 +f 2366/854/99 2367/855/99 2368/856/99 +f 2366/854/99 2368/856/99 2369/857/99 +f 2370/858/98 2371/859/98 2372/860/98 +f 2370/858/98 2372/860/98 2373/861/98 +f 2374/862/97 2375/863/97 2376/864/97 +f 2374/862/97 2376/864/97 2377/865/97 +f 2378/866/104 2379/867/104 2380/868/104 +f 2378/866/104 2380/868/104 2381/869/104 +f 2382/870/105 2383/871/105 2384/872/105 +f 2382/870/105 2384/872/105 2385/873/105 +f 2390/874/94 2391/875/94 2392/876/94 +f 2390/874/94 2392/876/94 2393/877/94 +f 2394/878/93 2395/879/93 2396/880/93 +f 2394/878/93 2396/880/93 2397/881/93 +f 2402/882/106 2403/883/106 2404/884/106 +f 2402/882/107 2404/884/107 2405/885/107 +f 2441/886/32 2442/887/32 2443/888/32 +f 2441/886/32 2443/888/32 2444/889/32 +f 2445/890/3 2446/891/3 111/74/3 +f 2445/890/3 111/74/3 110/73/3 +f 2461/892/3 2462/893/3 2463/894/3 +f 2461/892/3 2463/894/3 2464/895/3 +f 2465/896/3 2466/897/3 2467/898/3 +f 2465/896/3 2467/898/3 2468/899/3 +f 2469/900/3 2470/901/3 2471/902/3 +f 2469/900/3 2471/902/3 2472/903/3 +f 2473/904/3 2474/905/3 2475/906/3 +f 2473/904/3 2475/906/3 2476/907/3 +f 2477/908/3 2478/909/3 2479/910/3 +f 2477/908/3 2479/910/3 2480/911/3 +f 2481/912/3 2482/913/3 2483/914/3 +f 2481/912/3 2483/914/3 2484/915/3 +f 2485/916/3 2486/917/3 2487/918/3 +f 2485/916/3 2487/918/3 2488/919/3 +f 2489/920/31 2490/921/31 2491/922/31 +f 2489/920/31 2491/922/31 2492/923/31 +f 2493/924/31 2494/925/31 2495/926/31 +f 2493/924/3 2495/926/3 2496/927/3 +f 2497/928/3 2498/929/3 2499/930/3 +f 2497/928/31 2499/930/31 2500/931/31 +f 2501/932/31 2502/933/31 2503/934/31 +f 2501/932/3 2503/934/3 2504/935/3 +f 2505/936/3 2506/937/3 2507/938/3 +f 2505/936/3 2507/938/3 2508/939/3 +f 2509/940/30 2510/941/30 2511/942/30 +f 2509/940/30 2511/942/30 2512/943/30 +f 2513/944/39 2514/945/39 2515/946/39 +f 2513/944/39 2515/946/39 2516/947/39 +f 2517/948/40 2518/949/40 2519/950/40 +f 2517/948/40 2519/950/40 2520/951/40 +f 2521/952/41 2522/953/41 2523/954/41 +f 2521/952/41 2523/954/41 2524/955/41 +f 2525/956/42 2526/957/42 2527/958/42 +f 2525/956/42 2527/958/42 2528/959/42 +f 2529/960/38 2530/961/38 2531/962/38 +f 2529/960/38 2531/962/38 2532/963/38 +f 2533/964/37 2534/965/37 2535/966/37 +f 2533/964/37 2535/966/37 2536/967/37 +f 2537/968/32 2538/969/32 2539/970/32 +f 2537/968/32 2539/970/32 2540/971/32 +f 2541/972/33 2542/973/33 2543/974/33 +f 2541/972/33 2543/974/33 2544/975/33 +f 2545/976/34 2546/977/34 2547/978/34 +f 2545/976/34 2547/978/34 2548/979/34 +f 2549/980/35 2550/981/35 2551/982/35 +f 2549/980/35 2551/982/35 2552/983/35 +f 2553/984/36 2554/985/36 2555/986/36 +f 2571/987/32 2572/988/32 2573/989/32 +f 2571/987/32 2573/989/32 2574/990/32 +f 2575/991/3 2576/992/3 2577/993/3 +f 2575/991/3 2577/993/3 2578/994/3 +f 2579/995/3 2580/996/3 2581/997/3 +f 2579/995/3 2581/997/3 2582/998/3 +f 2583/999/3 2584/1000/3 2585/1001/3 +f 2583/999/3 2585/1001/3 2586/1002/3 +f 2587/1003/3 2588/1004/3 2589/1005/3 +f 2587/1003/3 2589/1005/3 2590/1006/3 +f 2591/1007/3 2592/1008/3 2593/1009/3 +f 2591/1007/3 2593/1009/3 2594/1010/3 +f 2595/1011/3 2596/1012/3 2597/1013/3 +f 2595/1011/3 2597/1013/3 2598/1014/3 +f 2599/1015/108 2600/1016/108 2601/1017/108 +f 2599/1015/108 2601/1017/108 2602/1018/108 +f 2607/1019/109 2608/1020/109 2609/1021/109 +f 2607/1019/109 2609/1021/109 2610/1022/109 +f 2611/1023/110 2612/1024/110 2613/1025/110 +f 2611/1023/110 2613/1025/110 2614/1026/110 +f 2619/1027/111 2620/1028/111 2621/1029/111 +f 2619/1027/111 2621/1029/111 2622/1030/111 +f 2657/1031/27 2658/1032/27 2659/1033/27 +f 2657/1031/27 2659/1033/27 2660/1034/27 +f 2660/1034/27 2659/1033/27 2661/1035/27 +f 2660/1034/27 2661/1035/27 2662/1036/27 +f 2662/1036/27 2661/1035/27 2663/1037/27 +f 2662/1036/27 2663/1037/27 2664/1038/27 +f 2664/1038/27 2663/1037/27 2665/1039/27 +f 2664/1038/27 2665/1039/27 2666/1040/27 +f 2666/1040/27 2665/1039/27 2667/1041/27 +f 2666/1040/27 2667/1041/27 2668/1042/27 +f 2669/1043/27 2670/1044/27 2671/1045/27 +f 2669/1043/27 2671/1045/27 2672/1046/27 +f 2672/1046/27 2671/1045/27 2673/1047/27 +f 2672/1046/27 2673/1047/27 2674/1048/27 +f 2674/1048/27 2673/1047/27 2658/1032/27 +f 2674/1048/27 2658/1032/27 2657/1031/27 +f 2707/1049/112 2708/1050/112 2709/1051/112 +f 2707/1049/113 2709/1051/113 2710/1052/113 +f 2711/1053/114 2712/1054/114 2713/1055/114 +f 2711/1053/115 2713/1055/115 2714/1056/115 +f 2715/1057/116 2716/1058/116 2717/1059/116 +f 2715/1057/116 2717/1059/116 2718/1060/116 +f 2719/1061/117 2720/1062/117 2721/1063/117 +f 2719/1061/117 2721/1063/117 2722/1064/117 +f 2723/1065/118 2724/1066/118 2725/1067/118 +f 2723/1065/118 2725/1067/118 2726/1068/118 +f 2727/1069/119 2728/1070/119 2729/1071/119 +f 2727/1069/119 2729/1071/119 2730/1072/119 +f 2731/1073/120 2732/1074/120 2733/1075/120 +f 2731/1073/120 2733/1075/120 2734/1076/120 +f 2735/1077/121 2736/1078/121 2737/1079/121 +f 2735/1077/121 2737/1079/121 2738/1080/121 +f 2739/1081/122 2740/1082/122 2741/1083/122 +f 2739/1081/122 2741/1083/122 2742/1084/122 +f 2743/1085/122 2744/1086/122 2745/1087/122 +f 2743/1085/122 2745/1087/122 2746/1088/122 +f 2747/1089/122 2748/1090/122 2749/1091/122 +f 2747/1089/122 2749/1091/122 2750/1092/122 +f 2751/1093/122 2752/1094/122 2753/1095/122 +f 2751/1093/122 2753/1095/122 2754/1096/122 +f 2755/1097/122 2756/1098/122 2757/1099/122 +f 2755/1097/122 2757/1099/122 2758/1100/122 +f 2759/1101/122 2760/1102/122 2761/1103/122 +f 2759/1101/122 2761/1103/122 2762/1104/122 +f 2763/1105/122 2764/1106/122 2765/1107/122 +f 2763/1105/122 2765/1107/122 2766/1108/122 +f 2767/1109/122 2768/1110/122 2769/1111/122 +f 2767/1109/122 2769/1111/122 2770/1112/122 +f 2847/1113/123 2848/1114/123 2849/1115/123 +f 2847/1113/123 2849/1115/123 2850/1116/123 +f 2851/1117/124 2852/1118/124 2853/1119/124 +f 2855/1120/125 2856/1121/125 2857/1122/125 +f 2855/1120/125 2857/1122/125 2858/1123/125 +f 2859/1124/126 2860/1125/126 2861/1126/126 +f 2859/1124/126 2861/1126/126 2862/1127/126 +f 2881/1128/3 2882/1129/3 123/86/3 +f 2881/1128/3 123/86/3 122/85/3 +f 2901/1130/3 2902/1131/3 117/80/3 +f 2901/1130/3 117/80/3 116/79/3 +f 2945/1132/127 2946/1133/127 2947/1134/127 +f 2945/1132/127 2947/1134/127 2948/1135/127 +f 2957/1136/128 2958/1137/128 2959/1138/128 +f 2961/1139/129 2962/1140/129 2963/1141/129 +f 2961/1139/129 2963/1141/129 2964/1142/129 +f 3033/1143/130 3034/1144/130 3035/1145/130 +f 3033/1143/130 3035/1145/130 3036/1146/130 +f 3041/1147/131 3042/1148/131 3043/1149/131 +f 3045/1150/132 3046/1151/132 3047/1152/132 +f 3045/1150/133 3047/1152/133 3048/1153/133 +f 3049/1154/134 3050/1155/134 3051/1156/134 +f 3049/1154/134 3051/1156/134 3052/1157/134 +f 3057/1158/135 3058/1159/135 3059/1160/135 +f 3057/1158/135 3059/1160/135 3060/1161/135 +f 3073/1162/136 3074/1163/136 3075/1164/136 +f 3073/1162/136 3075/1164/136 3076/1165/136 +f 3081/1166/27 3082/1167/27 3083/1168/27 +f 3081/1166/27 3083/1168/27 3084/1169/27 +f 3105/1170/137 3106/1171/137 3107/1172/137 +f 3105/1170/137 3107/1172/137 3108/1173/137 +f 3109/1174/138 3110/1175/138 3111/1176/138 +f 3109/1174/138 3111/1176/138 3112/1177/138 +f 3113/1178/139 3114/1179/139 3115/1180/139 +f 3113/1178/139 3115/1180/139 3116/1181/139 +f 3117/1182/140 3118/1183/140 3119/1184/140 +f 3117/1182/140 3119/1184/140 3120/1185/140 +f 3121/1186/141 3122/1187/141 3123/1188/141 +f 3121/1186/141 3123/1188/141 3124/1189/141 +f 3125/1190/142 3126/1191/142 3127/1192/142 +f 3125/1190/142 3127/1192/142 3128/1193/142 +f 3129/1194/143 3130/1195/143 3131/1196/143 +f 3129/1194/143 3131/1196/143 3132/1197/143 +f 3133/1198/144 3134/1199/144 3135/1200/144 +f 3133/1198/144 3135/1200/144 3136/1201/144 +f 3137/1202/145 3138/1203/145 3139/1204/145 +f 3137/1202/145 3139/1204/145 3140/1205/145 +f 3141/1206/146 3142/1207/146 3143/1208/146 +f 3141/1206/146 3143/1208/146 3144/1209/146 +f 3145/1210/147 3146/1211/147 3147/1212/147 +f 3145/1210/147 3147/1212/147 3148/1213/147 +f 3149/1214/148 3150/1215/148 3151/1216/148 +f 3149/1214/148 3151/1216/148 3152/1217/148 +f 3153/1218/149 3154/1219/149 3155/1220/149 +f 3153/1218/149 3155/1220/149 3156/1221/149 +f 3157/1222/150 3158/1223/150 3159/1224/150 +f 3157/1222/150 3159/1224/150 3160/1225/150 +f 3161/1226/151 3162/1227/151 3163/1228/151 +f 3161/1226/151 3163/1228/151 3164/1229/151 +f 3165/1230/152 3166/1231/152 3167/1232/152 +f 3165/1230/152 3167/1232/152 3168/1233/152 +f 3169/1234/153 3170/1235/153 3171/1236/153 +f 3169/1234/153 3171/1236/153 3172/1237/153 +f 3173/1238/154 3174/1239/154 3175/1240/154 +f 3173/1238/154 3175/1240/154 3176/1241/154 +f 3177/1242/155 3178/1243/155 3179/1244/155 +f 3177/1242/155 3179/1244/155 3180/1245/155 +f 3181/1246/156 3182/1247/156 3183/1248/156 +f 3181/1246/156 3183/1248/156 3184/1249/156 +f 3185/1250/157 3186/1251/157 3187/1252/157 +f 3185/1250/157 3187/1252/157 3188/1253/157 +f 3193/1254/89 3197/1255/89 3198/1256/89 +f 3193/1254/89 3198/1256/89 3194/1257/89 +f 3197/1255/89 3199/1258/89 3200/1259/89 +f 3197/1255/89 3200/1259/89 3198/1256/89 +f 3201/1260/84 3202/1261/84 3203/1262/84 +f 3201/1260/84 3203/1262/84 3204/1263/84 +f 3206/1264/84 3201/1260/84 3204/1263/84 +f 3206/1264/84 3204/1263/84 3207/1265/84 +f 91/54/3 3213/1266/3 3214/1267/3 +f 91/54/3 3214/1267/3 89/52/3 +f 2445/890/3 3215/1268/3 3216/1269/3 +f 2445/890/3 3216/1269/3 2446/891/3 +f 2018/698/3 3217/1270/3 3218/1271/3 +f 2018/698/3 3218/1271/3 2019/699/3 +f 3217/1270/3 3219/1272/3 3220/1273/3 +f 3217/1270/3 3220/1273/3 3218/1271/3 +f 2901/1130/3 120/83/3 119/82/3 +f 2901/1130/3 119/82/3 2902/1131/3 +f 2881/1128/3 126/89/3 125/88/3 +f 2881/1128/3 125/88/3 2882/1129/3 +f 3225/1274/3 3226/1275/3 3227/1276/3 +f 3225/1274/3 3227/1276/3 3228/1277/3 +f 3229/1278/3 3230/1279/3 3231/1280/3 +f 3229/1278/3 3231/1280/3 3232/1281/3 +f 3233/1282/3 3234/1283/3 3235/1284/3 +f 3233/1282/3 3235/1284/3 3236/1285/3 +f 3237/1286/3 3238/1287/3 3239/1288/3 +f 3237/1286/3 3239/1288/3 3240/1289/3 +f 3241/1290/3 3242/1291/3 3243/1292/3 +f 3241/1290/3 3243/1292/3 3244/1293/3 +f 3245/1294/3 3246/1295/3 3247/1296/3 +f 3245/1294/3 3247/1296/3 3248/1297/3 +f 3249/1298/3 3250/1299/3 3251/1300/3 +f 3249/1298/3 3251/1300/3 3252/1301/3 +f 3261/1302/3 3262/1303/3 3263/1304/3 +f 3261/1302/3 3263/1304/3 3264/1305/3 +f 3265/1306/3 3266/1307/3 3267/1308/3 +f 3265/1306/3 3267/1308/3 3268/1309/3 +f 3268/1309/3 3267/1308/3 3269/1310/3 +f 3268/1309/3 3269/1310/3 3270/1311/3 +f 3270/1311/3 3269/1310/3 3271/1312/3 +f 3270/1311/3 3271/1312/3 3272/1313/3 +f 3273/1314/1 3274/1315/1 3275/1316/1 +f 3273/1314/1 3275/1316/1 3276/1317/1 +f 3274/1315/1 3277/1318/1 3278/1319/1 +f 3274/1315/1 3278/1319/1 3275/1316/1 +f 3277/1318/1 3279/1320/1 3280/1321/1 +f 3277/1318/1 3280/1321/1 3278/1319/1 +f 3281/1322/27 3282/1323/27 3283/1324/27 +f 3281/1322/27 3283/1324/27 3284/1325/27 +f 3293/1326/26 3294/1327/26 3295/1328/26 +f 3293/1326/26 3295/1328/26 3296/1329/26 +f 3297/1330/1 3298/1331/1 3299/1332/1 +f 3297/1330/1 3299/1332/1 3300/1333/1 +f 3317/1334/3 3318/1335/3 3319/1336/3 +f 3317/1334/3 3319/1336/3 3320/1337/3 +f 3321/1338/1 3297/1330/1 3300/1333/1 +f 3321/1338/1 3300/1333/1 3322/1339/1 +f 3319/1336/3 3323/1340/3 3324/1341/3 +f 3319/1336/3 3324/1341/3 3320/1337/3 +f 3324/1341/3 3325/1342/3 3326/1343/3 +f 3324/1341/3 3326/1343/3 3320/1337/3 +f 3326/1343/3 3327/1344/3 3317/1334/3 +f 3326/1343/3 3317/1334/3 3320/1337/3 +f 3299/1332/1 3328/1345/1 3329/1346/1 +f 3299/1332/1 3329/1346/1 3300/1333/1 +f 3300/1333/1 3329/1346/1 3330/1347/1 +f 3300/1333/1 3330/1347/1 3322/1339/1 +f 3365/1348/21 3366/1349/21 3367/1350/21 +f 3365/1348/21 3367/1350/21 3368/1351/21 +f 3369/1352/22 3370/1353/22 3371/1354/22 +f 3369/1352/22 3371/1354/22 3372/1355/22 +f 3373/1356/23 3374/1357/23 3375/1358/23 +f 3373/1356/23 3375/1358/23 3376/1359/23 +f 3377/1360/24 3378/1361/24 3379/1362/24 +f 3377/1360/24 3379/1362/24 3380/1363/24 +f 3389/1364/80 3390/1365/80 3391/1366/80 +f 3389/1364/80 3391/1366/80 3392/1367/80 +f 3393/1368/81 3394/1369/81 3395/1370/81 +f 3393/1368/81 3395/1370/81 3396/1371/81 +f 3397/1372/82 3398/1373/82 3399/1374/82 +f 3397/1372/82 3399/1374/82 3400/1375/82 +f 3401/1376/83 3402/1377/83 3403/1378/83 +f 3401/1376/83 3403/1378/83 3404/1379/83 +s 1 +f 1/1380/158 2/1381/159 3/1382/160 +f 4/1383/161 5/1384/162 6/1385/163 +f 7/1386/164 8/1387/165 9/1388/166 +f 10/1389/167 11/1390/168 12/1391/169 +f 13/1392/170 14/1393/171 15/1394/172 +f 61/1395/173 62/1396/174 63/1397/175 +f 64/1398/176 65/1399/177 66/1400/178 +f 67/1401/179 68/1402/180 69/91/16 +f 70/1403/181 71/1404/182 72/1405/183 +f 73/1406/161 74/1407/163 75/1408/184 +f 76/1409/185 77/1410/186 78/1411/187 +f 79/1412/188 80/1413/189 81/1414/190 +f 13/1392/170 82/1415/191 14/1393/171 +f 128/1416/192 129/1417/193 130/1418/194 +f 131/1419/195 132/1420/196 133/1421/197 +f 134/1422/198 135/589/16 136/1423/199 +f 1/1380/158 10/1389/167 12/1391/169 +f 1/1380/158 12/1391/169 2/1381/159 +f 5/1384/162 1/1380/158 3/1382/160 +f 5/1384/162 3/1382/160 6/1385/163 +f 7/1386/164 13/1392/170 15/1394/172 +f 7/1386/164 15/1394/172 8/1387/165 +f 10/1389/167 7/1386/164 9/1388/166 +f 10/1389/167 9/1388/166 11/1390/168 +f 137/1424/200 138/1425/201 2/1381/159 +f 137/1424/200 2/1381/159 12/1391/169 +f 139/1426/202 137/1424/200 12/1391/169 +f 139/1426/202 12/1391/169 11/1390/168 +f 138/1425/201 140/1427/203 3/1382/160 +f 138/1425/201 3/1382/160 2/1381/159 +f 140/1427/203 141/1428/204 6/1385/163 +f 140/1427/203 6/1385/163 3/1382/160 +f 14/1393/171 142/1429/205 64/1398/176 +f 14/1393/171 64/1398/176 15/1394/172 +f 64/1398/176 66/1400/178 8/1387/165 +f 64/1398/176 8/1387/165 15/1394/172 +f 66/1400/178 143/1430/206 9/1388/166 +f 66/1400/178 9/1388/166 8/1387/165 +f 143/1430/206 139/1426/202 11/1390/168 +f 143/1430/206 11/1390/168 9/1388/166 +f 144/1431/207 62/1396/174 61/1395/173 +f 144/1431/207 61/1395/173 145/1432/208 +f 69/91/16 147/93/16 67/1401/179 +f 148/1433/209 149/1434/173 150/1435/210 +f 148/1433/209 150/1435/210 151/1436/180 +f 152/1437/211 153/1438/212 154/1439/213 +f 152/1437/211 154/1439/213 155/1440/213 +f 156/1441/214 157/1442/215 158/1443/31 +f 156/1441/214 158/1443/31 159/1444/3 +f 160/1445/216 161/1446/216 157/1442/215 +f 160/1445/216 157/1442/215 156/1441/214 +f 137/1424/200 162/1447/217 163/1448/218 +f 137/1424/200 163/1448/218 138/1425/201 +f 164/1449/219 162/1447/217 137/1424/200 +f 164/1449/219 137/1424/200 139/1426/202 +f 138/1425/201 163/1448/218 165/1450/220 +f 138/1425/201 165/1450/220 140/1427/203 +f 165/1450/220 166/1451/221 141/1428/204 +f 165/1450/220 141/1428/204 140/1427/203 +f 63/1397/175 167/1452/222 143/1430/206 +f 63/1397/175 143/1430/206 66/1400/178 +f 167/1452/222 164/1449/219 139/1426/202 +f 167/1452/222 139/1426/202 143/1430/206 +f 61/1395/173 63/1397/175 66/1400/178 +f 61/1395/173 66/1400/178 65/1399/177 +f 180/1453/223 181/1454/224 182/1455/225 +f 180/1453/223 182/1455/225 183/1456/226 +f 188/1457/227 189/1458/228 181/1454/224 +f 188/1457/227 181/1454/224 180/1453/223 +f 181/1454/224 190/1459/229 191/1460/230 +f 181/1454/224 191/1460/230 182/1455/225 +f 194/1461/231 195/1462/232 196/1463/233 +f 194/1461/231 196/1463/233 197/1464/234 +f 188/1457/227 198/1465/235 199/1466/236 +f 188/1457/227 199/1466/236 189/1458/228 +f 189/1458/228 200/1467/237 190/1459/229 +f 189/1458/228 190/1459/229 181/1454/224 +f 190/1459/229 201/1468/238 202/1469/239 +f 190/1459/229 202/1469/239 191/1460/230 +f 205/1470/240 206/1471/241 195/1462/232 +f 205/1470/240 195/1462/232 194/1461/231 +f 195/1462/232 207/1472/242 208/1473/243 +f 195/1462/232 208/1473/243 196/1463/233 +f 209/1474/244 210/1475/245 211/1476/246 +f 209/1474/244 211/1476/246 212/1477/247 +f 199/1466/236 213/1478/248 200/1467/237 +f 199/1466/236 200/1467/237 189/1458/228 +f 200/1467/237 214/1479/249 201/1468/238 +f 200/1467/237 201/1468/238 190/1459/229 +f 201/1468/238 215/1480/250 216/1481/251 +f 201/1468/238 216/1481/251 202/1469/239 +f 219/1482/252 220/1483/253 206/1471/241 +f 219/1482/252 206/1471/241 205/1470/240 +f 206/1471/241 221/1484/254 207/1472/242 +f 206/1471/241 207/1472/242 195/1462/232 +f 222/1485/255 223/1486/256 208/1473/243 +f 222/1485/255 208/1473/243 207/1472/242 +f 211/1476/246 228/1487/257 229/1488/258 +f 211/1476/246 229/1488/258 230/1489/259 +f 213/1478/248 231/1490/260 214/1479/249 +f 213/1478/248 214/1479/249 200/1467/237 +f 214/1479/249 232/1491/261 215/1480/250 +f 214/1479/249 215/1480/250 201/1468/238 +f 233/1492/262 234/1493/263 235/1494/264 +f 233/1492/262 235/1494/264 236/1495/264 +f 237/1496/265 238/1497/266 220/1483/253 +f 237/1496/265 220/1483/253 219/1482/252 +f 220/1483/253 239/1498/267 221/1484/254 +f 220/1483/253 221/1484/254 206/1471/241 +f 221/1484/254 240/1499/268 222/1485/255 +f 221/1484/254 222/1485/255 207/1472/242 +f 241/1500/269 242/1501/270 243/1502/271 +f 241/1500/269 243/1502/271 244/1503/272 +f 228/1487/257 247/1504/273 248/1505/274 +f 228/1487/257 248/1505/274 229/1488/258 +f 231/1490/260 249/1506/275 232/1491/261 +f 231/1490/260 232/1491/261 214/1479/249 +f 250/1507/276 251/1508/276 234/1493/263 +f 250/1507/276 234/1493/263 252/1509/262 +f 238/1497/266 253/1510/277 239/1498/267 +f 238/1497/266 239/1498/267 220/1483/253 +f 239/1498/267 254/1511/278 240/1499/268 +f 239/1498/267 240/1499/268 221/1484/254 +f 255/1512/279 256/1513/280 242/1501/270 +f 255/1512/279 242/1501/270 241/1500/269 +f 248/1505/274 247/1504/273 259/1514/281 +f 248/1505/274 259/1514/281 260/1515/282 +f 261/1516/26 262/1517/26 251/1508/276 +f 261/1516/26 251/1508/276 250/1507/276 +f 253/1510/277 263/1518/283 254/1511/278 +f 253/1510/277 254/1511/278 239/1498/267 +f 264/1519/284 265/1520/285 256/1513/280 +f 264/1519/284 256/1513/280 255/1512/279 +f 268/1521/286 269/126/17 270/1522/17 +f 268/1521/286 270/1522/17 271/1523/287 +f 264/1519/284 272/1524/288 273/1525/289 +f 264/1519/284 273/1525/289 265/1520/285 +f 274/1526/1 275/1527/1 276/1528/290 +f 274/1526/1 276/1528/290 277/1529/290 +f 277/1529/290 276/1528/290 278/1530/291 +f 277/1529/290 278/1530/291 279/1531/291 +f 280/1532/292 281/1533/292 282/1534/293 +f 280/1532/292 282/1534/293 283/1535/294 +f 210/1475/245 284/1536/257 228/1487/257 +f 210/1475/245 228/1487/257 211/1476/246 +f 284/1536/257 285/1537/295 247/1504/273 +f 284/1536/257 247/1504/273 228/1487/257 +f 286/1538/280 287/1539/296 242/1501/270 +f 286/1538/280 242/1501/270 256/1513/280 +f 247/1504/273 285/1537/295 288/1540/297 +f 247/1504/273 288/1540/297 259/1514/281 +f 289/1541/298 286/1538/280 256/1513/280 +f 289/1541/298 256/1513/280 265/1520/285 +f 269/126/17 291/128/17 270/1522/17 +f 292/1542/299 289/1541/298 265/1520/285 +f 292/1542/299 265/1520/285 273/1525/289 +f 230/1489/259 229/1488/258 213/1478/248 +f 230/1489/259 213/1478/248 199/1466/236 +f 229/1488/258 248/1505/274 231/1490/260 +f 229/1488/258 231/1490/260 213/1478/248 +f 255/1512/279 241/1500/269 222/1485/255 +f 255/1512/279 222/1485/255 240/1499/268 +f 248/1505/274 260/1515/282 249/1506/275 +f 248/1505/274 249/1506/275 231/1490/260 +f 264/1519/284 255/1512/279 240/1499/268 +f 264/1519/284 240/1499/268 254/1511/278 +f 268/1521/286 271/1523/287 262/1517/26 +f 268/1521/286 262/1517/26 261/1516/26 +f 272/1524/288 264/1519/284 254/1511/278 +f 272/1524/288 254/1511/278 263/1518/283 +f 230/1489/259 293/1543/300 212/1477/247 +f 230/1489/259 212/1477/247 211/1476/246 +f 242/1501/270 287/1539/296 294/1544/301 +f 242/1501/270 294/1544/301 243/1502/271 +f 293/1543/300 230/1489/259 199/1466/236 +f 293/1543/300 199/1466/236 198/1465/235 +f 222/1485/255 241/1500/269 244/1503/272 +f 222/1485/255 244/1503/272 223/1486/256 +f 295/129/302 296/1545/302 297/130/303 +f 307/1546/304 308/1547/305 309/1548/306 +f 307/1546/304 309/1548/306 310/1549/307 +f 311/1550/308 312/1551/309 308/1547/305 +f 311/1550/308 308/1547/305 307/1546/304 +f 313/1552/310 314/1553/311 315/1554/312 +f 313/1552/310 315/1554/312 316/1555/310 +f 317/1556/313 318/1557/314 319/1558/315 +f 317/1556/313 319/1558/315 320/1559/316 +f 321/1560/317 322/1561/318 323/1562/319 +f 321/1560/317 323/1562/319 317/1556/313 +f 328/1563/320 329/1564/321 330/1565/322 +f 328/1563/320 330/1565/322 331/1566/323 +f 332/1567/324 333/1568/325 334/1569/326 +f 332/1567/324 334/1569/326 335/1570/327 +f 336/1571/328 337/1572/328 338/1573/329 +f 336/1571/328 338/1573/329 339/1574/330 +f 340/1575/331 328/1563/320 331/1566/323 +f 340/1575/331 331/1566/323 341/1576/332 +f 334/1569/326 342/1577/333 343/1578/334 +f 334/1569/326 343/1578/334 335/1570/327 +f 344/1579/335 345/1580/336 309/1548/306 +f 344/1579/335 309/1548/306 308/1547/305 +f 346/1581/337 318/1557/314 317/1556/313 +f 346/1581/337 317/1556/313 323/1562/319 +f 347/1582/338 344/1579/335 308/1547/305 +f 347/1582/338 308/1547/305 312/1551/309 +f 348/1583/339 321/1560/317 317/1556/313 +f 348/1583/339 317/1556/313 320/1559/316 +f 339/1574/330 338/1573/329 315/1554/312 +f 339/1574/330 315/1554/312 314/1553/311 +f 335/1570/327 343/1578/334 319/1558/315 +f 335/1570/327 319/1558/315 318/1557/314 +f 331/1566/323 330/1565/322 345/1580/336 +f 331/1566/323 345/1580/336 344/1579/335 +f 332/1567/324 335/1570/327 318/1557/314 +f 332/1567/324 318/1557/314 346/1581/337 +f 341/1576/332 331/1566/323 344/1579/335 +f 341/1576/332 344/1579/335 347/1582/338 +f 349/1584/340 350/1585/341 351/1586/342 +f 349/1584/340 351/1586/342 352/1587/343 +f 353/1588/344 354/1589/345 355/1590/346 +f 353/1588/344 355/1590/346 356/1591/346 +f 365/1592/347 366/1593/348 367/1594/349 +f 365/1592/347 367/1594/349 368/1595/350 +f 369/1596/351 370/1597/352 366/1593/348 +f 369/1596/351 366/1593/348 365/1592/347 +f 371/1598/353 372/1599/354 373/1600/355 +f 371/1598/353 373/1600/355 374/1601/356 +f 375/1602/357 376/1603/358 377/1604/359 +f 375/1602/357 377/1604/359 378/1605/360 +f 379/1606/361 380/1607/361 368/1595/350 +f 379/1606/361 368/1595/350 367/1594/349 +f 370/1597/352 369/1596/351 381/1608/362 +f 370/1597/352 381/1608/362 382/1609/362 +f 371/1598/353 374/1601/356 383/1610/363 +f 371/1598/353 383/1610/363 384/1611/363 +f 372/1599/354 377/1604/359 376/1603/358 +f 372/1599/354 376/1603/358 373/1600/355 +f 385/1612/364 386/1613/365 387/1614/366 +f 385/1612/364 387/1614/366 388/1615/367 +f 354/1589/345 353/1588/344 389/1616/368 +f 354/1589/345 389/1616/368 390/1617/369 +f 390/1617/369 389/1616/368 388/1615/367 +f 390/1617/369 388/1615/367 387/1614/366 +f 391/1618/370 392/1619/371 393/1620/372 +f 391/1618/370 393/1620/372 394/1621/373 +f 351/1586/342 350/1585/341 395/1622/374 +f 351/1586/342 395/1622/374 396/1623/375 +f 396/1623/375 395/1622/374 394/1621/373 +f 396/1623/375 394/1621/373 393/1620/372 +f 385/1612/364 397/1624/376 398/1625/376 +f 385/1612/364 398/1625/376 386/1613/365 +f 439/1626/377 440/1627/377 441/1628/378 +f 439/1626/377 441/1628/378 442/1629/378 +f 443/1630/42 444/1631/42 440/1627/377 +f 443/1630/42 440/1627/377 439/1626/377 +f 445/1632/379 446/1633/379 444/1631/42 +f 445/1632/379 444/1631/42 443/1630/42 +f 447/1634/16 448/1635/16 446/1633/379 +f 447/1634/16 446/1633/379 445/1632/379 +f 449/1636/380 450/1637/380 451/1638/25 +f 449/1636/380 451/1638/25 452/1639/25 +f 453/1640/39 454/1641/39 450/1637/380 +f 453/1640/39 450/1637/380 449/1636/380 +f 455/1642/45 456/1643/45 454/1641/39 +f 455/1642/45 454/1641/39 453/1640/39 +f 442/1629/378 441/1628/378 456/1643/45 +f 442/1629/378 456/1643/45 455/1642/45 +f 457/1644/381 458/1645/382 459/1646/383 +f 457/1644/381 459/1646/383 460/1647/384 +f 461/1648/385 457/1644/381 460/1647/384 +f 461/1648/385 460/1647/384 462/1649/386 +f 463/1650/387 461/1648/385 462/1649/386 +f 463/1650/387 462/1649/386 464/1651/388 +f 465/1652/389 463/1650/387 464/1651/388 +f 465/1652/389 464/1651/388 466/1653/389 +f 467/1654/380 468/1655/25 469/1656/25 +f 467/1654/380 469/1656/25 470/1657/380 +f 471/1658/39 467/1654/380 470/1657/380 +f 471/1658/39 470/1657/380 472/1659/39 +f 473/1660/45 471/1658/39 472/1659/39 +f 473/1660/45 472/1659/39 474/1661/45 +f 458/1645/382 473/1660/45 474/1661/45 +f 458/1645/382 474/1661/45 459/1646/383 +f 475/1662/390 476/1663/391 477/1664/391 +f 475/1662/390 477/1664/391 478/1665/392 +f 479/1666/393 475/1662/390 478/1665/392 +f 479/1666/393 478/1665/392 480/1667/394 +f 481/1668/395 479/1666/393 480/1667/394 +f 481/1668/395 480/1667/394 482/1669/396 +f 483/1670/397 481/1668/395 482/1669/396 +f 483/1670/397 482/1669/396 484/1671/397 +f 485/1672/380 486/1673/25 487/1674/25 +f 485/1672/380 487/1674/25 488/1675/380 +f 489/1676/39 485/1672/380 488/1675/380 +f 489/1676/39 488/1675/380 490/1677/39 +f 491/1678/45 489/1676/39 490/1677/39 +f 491/1678/45 490/1677/39 492/1679/45 +f 476/1663/391 491/1678/45 492/1679/45 +f 476/1663/391 492/1679/45 477/1664/391 +f 493/1680/398 494/1681/399 495/1682/400 +f 493/1680/398 495/1682/400 496/1683/401 +f 497/1684/402 493/1680/398 496/1683/401 +f 497/1684/402 496/1683/401 498/1685/403 +f 499/1686/404 497/1684/402 498/1685/403 +f 499/1686/404 498/1685/403 500/1687/405 +f 501/1688/406 499/1686/404 500/1687/405 +f 501/1688/406 500/1687/405 502/1689/406 +f 503/1690/380 504/1691/25 505/1692/25 +f 503/1690/380 505/1692/25 506/1693/380 +f 507/1694/39 503/1690/380 506/1693/380 +f 507/1694/39 506/1693/380 508/1695/39 +f 509/1696/45 507/1694/39 508/1695/39 +f 509/1696/45 508/1695/39 510/1697/45 +f 494/1681/399 509/1696/45 510/1697/45 +f 494/1681/399 510/1697/45 495/1682/400 +f 529/1698/46 530/1699/46 531/1700/26 +f 529/1698/46 531/1700/26 532/1701/26 +f 533/1702/42 534/1703/42 530/1699/46 +f 533/1702/42 530/1699/46 529/1698/46 +f 535/1704/379 536/1705/379 534/1703/42 +f 535/1704/379 534/1703/42 533/1702/42 +f 537/1706/16 538/1707/16 536/1705/379 +f 537/1706/16 536/1705/379 535/1704/379 +f 539/1708/407 540/1709/407 541/1710/408 +f 539/1708/407 541/1710/408 542/1711/409 +f 543/1712/410 544/1713/411 545/1714/412 +f 543/1712/410 545/1714/412 546/1715/410 +f 547/1716/45 548/1717/45 549/1718/413 +f 547/1716/45 549/1718/413 550/1719/413 +f 532/1701/26 531/1700/26 548/1717/45 +f 532/1701/26 548/1717/45 547/1716/45 +f 553/1720/414 554/1721/414 555/1722/415 +f 553/1720/414 555/1722/415 556/1723/415 +f 557/1724/416 558/1725/416 554/1721/414 +f 557/1724/416 554/1721/414 553/1720/414 +f 559/1726/417 560/1727/417 558/1725/416 +f 559/1726/417 558/1725/416 557/1724/416 +f 561/1728/418 562/1729/418 560/1727/417 +f 561/1728/418 560/1727/417 559/1726/417 +f 563/1730/419 564/1731/419 565/1732/420 +f 563/1730/419 565/1732/420 566/1733/420 +f 567/1734/421 568/1735/421 564/1731/419 +f 567/1734/421 564/1731/419 563/1730/419 +f 569/1736/422 570/1737/422 568/1735/421 +f 569/1736/422 568/1735/421 567/1734/421 +f 556/1723/415 555/1722/415 570/1737/422 +f 556/1723/415 570/1737/422 569/1736/422 +f 589/1738/423 590/1739/424 591/1740/425 +f 589/1738/423 591/1740/425 592/1741/426 +f 593/1742/427 594/1743/428 590/1739/424 +f 593/1742/427 590/1739/424 589/1738/423 +f 595/1744/429 596/1745/430 594/1743/428 +f 595/1744/429 594/1743/428 593/1742/427 +f 597/1746/431 598/1747/432 596/1745/430 +f 597/1746/431 596/1745/430 595/1744/429 +f 599/1748/433 600/1749/434 601/1750/435 +f 599/1748/433 601/1750/435 602/1751/435 +f 603/1752/436 604/1753/437 600/1749/434 +f 603/1752/436 600/1749/434 599/1748/433 +f 605/1754/438 606/1755/439 604/1753/437 +f 605/1754/438 604/1753/437 603/1752/436 +f 592/1741/426 591/1740/425 606/1755/439 +f 592/1741/426 606/1755/439 605/1754/438 +f 607/1756/440 608/1757/441 609/1758/442 +f 607/1756/440 609/1758/442 610/1759/443 +f 611/1760/444 607/1756/440 610/1759/443 +f 611/1760/444 610/1759/443 612/1761/445 +f 613/1762/446 611/1760/444 612/1761/445 +f 613/1762/446 612/1761/445 614/1763/447 +f 615/1764/448 613/1762/446 614/1763/447 +f 615/1764/448 614/1763/447 616/1765/448 +f 617/1766/449 618/1767/450 619/1768/450 +f 617/1766/449 619/1768/450 545/1714/412 +f 620/1769/451 617/1766/449 545/1714/412 +f 620/1769/451 545/1714/412 544/1713/411 +f 621/1770/452 620/1769/451 544/1713/411 +f 621/1770/452 544/1713/411 622/1771/453 +f 608/1757/441 621/1770/452 622/1771/453 +f 608/1757/441 622/1771/453 609/1758/442 +f 623/1772/454 624/1773/455 625/1774/456 +f 623/1772/454 625/1774/456 626/1775/457 +f 627/1776/458 623/1772/454 626/1775/457 +f 627/1776/458 626/1775/457 628/1777/459 +f 629/1778/460 627/1776/458 628/1777/459 +f 629/1778/460 628/1777/459 630/1779/461 +f 631/1780/462 629/1778/460 630/1779/461 +f 631/1780/462 630/1779/461 632/1781/462 +f 633/1782/463 634/1783/464 635/1784/464 +f 633/1782/463 635/1784/464 636/1785/465 +f 637/1786/466 633/1782/463 636/1785/465 +f 637/1786/466 636/1785/465 638/1787/467 +f 639/1788/468 637/1786/466 638/1787/467 +f 639/1788/468 638/1787/467 640/1789/469 +f 624/1773/455 639/1788/468 640/1789/469 +f 624/1773/455 640/1789/469 625/1774/456 +f 641/1790/470 642/1791/471 643/1792/472 +f 641/1790/470 643/1792/472 644/1793/470 +f 645/1794/473 646/1795/474 647/1796/475 +f 645/1794/473 647/1796/475 648/1797/476 +f 649/1798/477 650/1799/478 646/1795/474 +f 649/1798/477 646/1795/474 645/1794/473 +f 651/1800/479 652/1801/480 650/1799/478 +f 651/1800/479 650/1799/478 649/1798/477 +f 653/1802/481 654/1803/481 652/1801/480 +f 653/1802/481 652/1801/480 651/1800/479 +f 655/1804/482 656/1805/483 654/1803/481 +f 655/1804/482 654/1803/481 653/1802/481 +f 657/1806/484 658/1807/485 656/1805/483 +f 657/1806/484 656/1805/483 655/1804/482 +f 659/1808/486 660/1809/487 658/1807/485 +f 659/1808/486 658/1807/485 657/1806/484 +f 661/1810/488 662/1811/489 660/1809/487 +f 661/1810/488 660/1809/487 659/1808/486 +f 663/1812/490 664/1813/491 662/1811/489 +f 663/1812/490 662/1811/489 661/1810/488 +f 665/1814/492 666/1815/493 664/1813/491 +f 665/1814/492 664/1813/491 663/1812/490 +f 667/1816/494 668/1817/495 669/1818/493 +f 667/1816/494 669/1818/493 670/1819/492 +f 671/1820/496 672/1821/497 668/1817/495 +f 671/1820/496 668/1817/495 667/1816/494 +f 673/1822/498 674/1823/499 672/1821/497 +f 673/1822/498 672/1821/497 671/1820/496 +f 675/1824/500 676/1825/501 674/1823/499 +f 675/1824/500 674/1823/499 673/1822/498 +f 677/1826/502 678/1827/503 676/1825/501 +f 677/1826/502 676/1825/501 675/1824/500 +f 648/1797/476 647/1796/475 678/1827/503 +f 648/1797/476 678/1827/503 677/1826/502 +f 679/1828/504 680/1829/505 681/1830/506 +f 679/1828/504 681/1830/506 682/1831/507 +f 683/236/28 684/1832/508 685/1833/509 +f 683/236/28 685/1833/509 686/237/28 +f 684/1832/508 687/1834/510 688/1835/511 +f 684/1832/508 688/1835/511 685/1833/509 +f 687/1834/510 689/1836/512 690/1837/513 +f 687/1834/510 690/1837/513 688/1835/511 +f 689/1836/512 691/1838/514 692/1839/515 +f 689/1836/512 692/1839/515 690/1837/513 +f 691/1838/514 693/1840/516 694/1841/516 +f 691/1838/514 694/1841/516 692/1839/515 +f 693/1840/516 695/1842/517 696/1843/518 +f 693/1840/516 696/1843/518 694/1841/516 +f 695/1842/517 697/1844/519 698/1845/520 +f 695/1842/517 698/1845/520 696/1843/518 +f 699/1846/519 700/1847/521 701/1848/522 +f 699/1846/519 701/1848/522 702/1849/520 +f 700/1847/521 703/1850/523 704/1851/524 +f 700/1847/521 704/1851/524 701/1848/522 +f 703/1850/523 705/1852/525 706/1853/526 +f 703/1850/523 706/1853/526 704/1851/524 +f 705/1852/525 707/1854/527 708/1855/528 +f 705/1852/525 708/1855/528 706/1853/526 +f 707/1854/527 709/1856/529 710/1857/530 +f 707/1854/527 710/1857/530 708/1855/528 +f 709/1856/529 711/230/28 712/233/28 +f 709/1856/529 712/233/28 710/1857/530 +f 717/1858/531 718/238/29 719/241/29 +f 717/1858/531 719/241/29 720/1859/532 +f 731/250/29 733/1860/533 734/1861/533 +f 731/250/29 734/1861/533 732/251/29 +f 733/1860/533 735/1862/534 736/1863/535 +f 733/1860/533 736/1863/535 734/1861/533 +f 735/1862/534 737/1864/536 738/1865/537 +f 735/1862/534 738/1865/537 736/1863/535 +f 739/1866/538 740/1867/539 741/1868/540 +f 739/1866/538 741/1868/540 742/1869/537 +f 740/1867/539 743/1870/541 744/1871/541 +f 740/1867/539 744/1871/541 741/1868/540 +f 743/1870/541 745/1872/542 746/1873/543 +f 743/1870/541 746/1873/543 744/1871/541 +f 745/1872/542 747/1874/544 748/1875/545 +f 745/1872/542 748/1875/545 746/1873/543 +f 747/1874/544 749/1876/546 750/1877/547 +f 747/1874/544 750/1877/547 748/1875/545 +f 749/1876/546 717/1858/531 720/1859/532 +f 749/1876/546 720/1859/532 750/1877/547 +f 751/1878/548 752/1879/549 753/1880/550 +f 751/1878/548 753/1880/550 754/1881/548 +f 752/1879/549 755/1882/551 756/1883/552 +f 752/1879/549 756/1883/552 753/1880/550 +f 755/1882/551 757/1884/553 758/1885/554 +f 755/1882/551 758/1885/554 756/1883/552 +f 757/1884/553 759/1886/555 760/1887/556 +f 757/1884/553 760/1887/556 758/1885/554 +f 759/1886/555 761/1888/557 762/1889/557 +f 759/1886/555 762/1889/557 760/1887/556 +f 761/1888/557 763/1890/558 764/1891/559 +f 761/1888/557 764/1891/559 762/1889/557 +f 763/1890/558 765/1892/560 766/1893/561 +f 763/1890/558 766/1893/561 764/1891/559 +f 767/1894/560 768/1895/562 769/1896/562 +f 767/1894/560 769/1896/562 770/1897/561 +f 768/1895/562 771/1898/563 772/1899/564 +f 768/1895/562 772/1899/564 769/1896/562 +f 771/1898/563 773/1900/565 774/1901/565 +f 771/1898/563 774/1901/565 772/1899/564 +f 773/1900/565 775/1902/566 776/1903/566 +f 773/1900/565 776/1903/566 774/1901/565 +f 775/1902/566 777/1904/567 778/1905/567 +f 775/1902/566 778/1905/567 776/1903/566 +f 777/1904/567 779/1906/568 780/1907/568 +f 777/1904/567 780/1907/568 778/1905/567 +f 779/1906/568 781/1908/569 782/1909/569 +f 779/1906/568 782/1909/569 780/1907/568 +f 781/1908/569 783/1910/570 784/1911/570 +f 781/1908/569 784/1911/570 782/1909/569 +f 783/1910/570 751/1878/548 754/1881/548 +f 783/1910/570 754/1881/548 784/1911/570 +f 785/1912/571 786/1913/548 787/1914/572 +f 785/1912/571 787/1914/572 788/1915/573 +f 786/1913/548 789/1916/570 790/1917/574 +f 786/1913/548 790/1917/574 787/1914/572 +f 789/1916/570 791/1918/569 792/1919/575 +f 789/1916/570 792/1919/575 790/1917/574 +f 791/1918/569 793/1920/568 794/1921/576 +f 791/1918/569 794/1921/576 792/1919/575 +f 793/1920/568 795/1922/567 796/1923/577 +f 793/1920/568 796/1923/577 794/1921/576 +f 795/1922/567 797/1924/566 798/1925/578 +f 795/1922/567 798/1925/578 796/1923/577 +f 797/1924/566 799/1926/565 800/1927/579 +f 797/1924/566 800/1927/579 798/1925/578 +f 799/1926/565 801/1928/580 802/1929/581 +f 799/1926/565 802/1929/581 800/1927/579 +f 801/1928/580 803/1930/582 804/1931/583 +f 801/1928/580 804/1931/583 802/1929/581 +f 803/1930/582 805/1932/584 806/1933/585 +f 803/1930/582 806/1933/585 804/1931/583 +f 807/1934/584 808/1935/586 809/1936/587 +f 807/1934/584 809/1936/587 810/1937/585 +f 808/1935/586 811/1938/557 812/1939/588 +f 808/1935/586 812/1939/588 809/1936/587 +f 811/1938/557 813/1940/589 814/1941/590 +f 811/1938/557 814/1941/590 812/1939/588 +f 813/1940/589 815/1942/591 816/1943/592 +f 813/1940/589 816/1943/592 814/1941/590 +f 815/1942/591 817/1944/593 818/1945/507 +f 815/1942/591 818/1945/507 816/1943/592 +f 817/1944/593 785/1912/571 788/1915/573 +f 817/1944/593 788/1915/573 818/1945/507 +f 819/1946/594 820/1947/595 821/1948/596 +f 819/1946/594 821/1948/596 822/1949/597 +f 823/1950/598 819/1946/594 822/1949/597 +f 823/1950/598 822/1949/597 824/1951/599 +f 825/1952/600 823/1950/598 824/1951/599 +f 825/1952/600 824/1951/599 826/1953/601 +f 827/1954/602 825/1952/600 826/1953/601 +f 827/1954/602 826/1953/601 828/1955/602 +f 829/1956/603 827/1954/602 828/1955/602 +f 829/1956/603 828/1955/602 830/1957/604 +f 831/1958/605 829/1956/603 830/1957/604 +f 831/1958/605 830/1957/604 832/1959/606 +f 833/1960/607 831/1958/605 832/1959/606 +f 833/1960/607 832/1959/606 834/1961/608 +f 835/1962/609 833/1960/607 834/1961/608 +f 835/1962/609 834/1961/608 836/1963/610 +f 837/1964/611 835/1962/609 836/1963/610 +f 837/1964/611 836/1963/610 838/1965/612 +f 839/1966/613 837/1964/611 838/1965/612 +f 839/1966/613 838/1965/612 840/1967/614 +f 841/1968/615 842/1969/613 843/1970/614 +f 841/1968/615 843/1970/614 844/1971/616 +f 845/1972/617 841/1968/615 844/1971/616 +f 845/1972/617 844/1971/616 846/1973/618 +f 847/1974/619 845/1972/617 846/1973/618 +f 847/1974/619 846/1973/618 848/1975/620 +f 849/1976/621 847/1974/619 848/1975/620 +f 849/1976/621 848/1975/620 850/1977/622 +f 851/1978/623 849/1976/621 850/1977/622 +f 851/1978/623 850/1977/622 852/1979/624 +f 820/1947/595 851/1978/623 852/1979/624 +f 820/1947/595 852/1979/624 821/1948/596 +f 853/1980/625 854/1981/625 855/1982/38 +f 853/1980/625 855/1982/38 856/1983/38 +f 857/1984/626 858/1985/626 854/1981/625 +f 857/1984/626 854/1981/625 853/1980/625 +f 859/1986/627 860/1987/627 858/1985/626 +f 859/1986/627 858/1985/626 857/1984/626 +f 861/1988/628 862/1989/628 863/1990/25 +f 861/1988/628 863/1990/25 864/1991/25 +f 865/1992/39 866/1993/39 862/1989/628 +f 865/1992/39 862/1989/628 861/1988/628 +f 873/1994/629 874/1995/630 875/1996/631 +f 873/1994/629 875/1996/631 876/1997/631 +f 877/1998/632 878/1999/632 874/1995/630 +f 877/1998/632 874/1995/630 873/1994/629 +f 879/2000/633 880/2001/633 878/1999/632 +f 879/2000/633 878/1999/632 877/1998/632 +f 881/2002/634 882/2003/634 883/2004/635 +f 881/2002/634 883/2004/635 884/2005/635 +f 885/2006/636 886/2007/636 882/2003/634 +f 885/2006/636 882/2003/634 881/2002/634 +f 876/1997/631 875/1996/631 886/2007/636 +f 876/1997/631 886/2007/636 885/2006/636 +f 983/2008/625 984/2009/38 985/2010/38 +f 983/2008/625 985/2010/38 986/2011/625 +f 987/2012/626 983/2008/625 986/2011/625 +f 987/2012/626 986/2011/625 988/2013/626 +f 989/2014/637 987/2012/626 988/2013/626 +f 989/2014/637 988/2013/626 990/2015/16 +f 991/2016/628 992/2017/25 993/2018/25 +f 991/2016/628 993/2018/25 994/2019/628 +f 995/2020/39 991/2016/628 994/2019/628 +f 995/2020/39 994/2019/628 996/2021/39 +f 1029/2022/638 1030/2023/639 1031/2024/639 +f 1029/2022/638 1031/2024/639 1032/2025/638 +f 1041/2026/16 1042/2027/627 1043/2028/379 +f 1041/2026/16 1043/2028/379 1044/2029/379 +f 1049/2030/640 1050/2031/640 1051/2032/641 +f 1049/2030/640 1051/2032/641 1052/2033/642 +f 1052/2033/642 1051/2032/641 1053/2034/643 +f 1052/2033/642 1053/2034/643 1054/2035/644 +f 1055/2036/645 1056/2037/646 1057/2038/646 +f 1055/2036/645 1057/2038/646 1058/2039/645 +f 1059/2040/647 1060/2041/648 1061/2042/649 +f 1059/2040/647 1061/2042/649 1062/2043/649 +f 1063/2044/650 1064/2045/650 1065/2046/650 +f 1063/2044/650 1065/2046/650 1066/2047/650 +f 1067/2048/651 1068/2049/652 1069/2050/648 +f 1067/2048/651 1069/2050/648 1070/2051/647 +f 1071/2052/653 1072/2053/654 1073/2054/654 +f 1071/2052/653 1073/2054/654 1074/2055/653 +f 1072/2053/654 1075/2056/655 1076/2057/655 +f 1072/2053/654 1076/2057/655 1073/2054/654 +f 1075/2056/655 1077/2058/656 1078/2059/656 +f 1075/2056/655 1078/2059/656 1076/2057/655 +f 1077/2058/656 1079/2060/657 1080/2061/657 +f 1077/2058/656 1080/2061/657 1078/2059/656 +f 1079/2060/657 1081/2062/658 1082/2063/658 +f 1079/2060/657 1082/2063/658 1080/2061/657 +f 1083/2064/658 1084/2065/659 1085/2066/659 +f 1083/2064/658 1085/2066/659 1086/2067/658 +f 1084/2065/659 1087/2068/660 1088/2069/660 +f 1084/2065/659 1088/2069/660 1085/2066/659 +f 1087/2068/660 1071/2052/653 1074/2055/653 +f 1087/2068/660 1074/2055/653 1088/2069/660 +f 1107/2070/661 1108/2071/661 1109/2072/662 +f 1107/2070/661 1109/2072/662 1110/2073/663 +f 1111/2074/664 1112/2075/664 1113/2076/665 +f 1111/2074/664 1113/2076/665 1114/2077/665 +f 1115/2078/666 1116/2079/667 1117/2080/668 +f 1115/2078/666 1117/2080/668 1118/2081/669 +f 1119/2082/670 1120/2083/670 1121/2084/671 +f 1119/2082/670 1121/2084/671 1122/2085/671 +f 1123/2086/672 1124/2087/673 1125/2088/672 +f 1123/2086/672 1125/2088/672 1126/2089/673 +f 1127/2090/674 1128/2091/674 1129/2092/675 +f 1127/2090/674 1129/2092/675 1130/2093/676 +f 1131/2094/677 1132/2095/678 1133/2096/679 +f 1131/2094/677 1133/2096/679 1134/2097/678 +f 1135/2098/680 1136/2099/681 1137/2100/682 +f 1135/2098/680 1137/2100/682 1138/2101/683 +f 1203/2102/26 1204/2103/684 1205/2104/684 +f 1203/2102/26 1205/2104/684 1206/2105/26 +f 1204/2103/684 1207/2106/685 1208/2107/686 +f 1204/2103/684 1208/2107/686 1205/2104/684 +f 1209/2108/685 1203/2102/26 1206/2105/26 +f 1209/2108/685 1206/2105/26 1210/2109/686 +f 1211/2110/687 1212/2111/688 1213/2112/689 +f 1211/2110/687 1213/2112/689 1214/2113/690 +f 1215/2114/688 1216/2115/691 1217/2116/692 +f 1215/2114/688 1217/2116/692 1218/2117/693 +f 1216/2115/691 1211/2110/687 1214/2113/690 +f 1216/2115/691 1214/2113/690 1217/2116/692 +f 1219/2118/694 1220/2119/695 1221/2120/696 +f 1219/2118/694 1221/2120/696 1222/2121/697 +f 1223/2122/698 1224/2123/699 1225/2124/700 +f 1223/2122/698 1225/2124/700 1226/2125/701 +f 1224/2123/699 1219/2118/694 1222/2121/697 +f 1224/2123/699 1222/2121/697 1225/2124/700 +f 1227/2126/26 1228/2127/702 1229/2128/702 +f 1227/2126/26 1229/2128/702 1230/2129/26 +f 1228/2127/702 1231/2130/703 1232/2131/704 +f 1228/2127/702 1232/2131/704 1229/2128/702 +f 1233/2132/705 1227/2126/26 1230/2129/26 +f 1233/2132/705 1230/2129/26 1234/2133/704 +f 1235/2134/706 1236/2135/707 1237/2136/708 +f 1235/2134/706 1237/2136/708 1238/2137/709 +f 1236/2135/707 1239/2138/1 1240/2139/1 +f 1236/2135/707 1240/2139/1 1237/2136/708 +f 1239/2138/1 1241/2140/710 1242/2141/710 +f 1239/2138/1 1242/2141/710 1240/2139/1 +f 1243/2142/710 1244/2143/711 1245/2144/711 +f 1243/2142/710 1245/2144/711 1246/2145/710 +f 1244/2143/711 1247/2146/712 1248/2147/713 +f 1244/2143/711 1248/2147/713 1245/2144/711 +f 1247/2146/712 1249/2148/3 1250/2149/3 +f 1247/2146/712 1250/2149/3 1248/2147/713 +f 1249/2148/3 1251/2150/714 1252/2151/714 +f 1249/2148/3 1252/2151/714 1250/2149/3 +f 1251/2150/714 1235/2134/706 1238/2137/709 +f 1251/2150/714 1238/2137/709 1252/2151/714 +f 1253/2152/715 1254/2153/716 1255/2154/717 +f 1253/2152/715 1255/2154/717 1256/2155/718 +f 1254/2153/716 1257/2156/1 1258/2157/1 +f 1254/2153/716 1258/2157/1 1255/2154/717 +f 1259/2158/3 1260/2159/719 1261/2160/720 +f 1259/2158/3 1261/2160/720 1262/2161/3 +f 1260/2159/719 1253/2152/715 1256/2155/718 +f 1260/2159/719 1256/2155/718 1261/2160/720 +f 1263/2162/721 1264/2163/721 1265/2164/721 +f 1263/2162/721 1265/2164/721 1266/2165/721 +f 1267/2166/722 1268/2167/722 1269/2168/722 +f 1267/2166/722 1269/2168/722 1270/2169/722 +f 1271/2170/723 1272/2171/723 1273/2172/724 +f 1271/2170/723 1273/2172/724 1274/2173/723 +f 1275/2174/725 1276/2175/726 1277/2176/725 +f 1275/2174/725 1277/2176/725 1278/2177/726 +f 1295/2178/26 1296/2179/42 1297/2180/42 +f 1295/2178/26 1297/2180/42 1298/2181/26 +f 1296/2179/42 1299/2182/16 1300/2183/16 +f 1296/2179/42 1300/2183/16 1297/2180/42 +f 1299/2182/16 1301/2184/36 1302/2185/36 +f 1299/2182/16 1302/2185/36 1300/2183/16 +f 1301/2184/36 1303/2186/27 1304/2187/27 +f 1301/2184/36 1304/2187/27 1302/2185/36 +f 1303/2186/27 1305/2188/727 1306/2189/33 +f 1303/2186/27 1306/2189/33 1304/2187/27 +f 1307/2190/728 1308/2191/25 1309/2192/25 +f 1307/2190/728 1309/2192/25 1310/2193/33 +f 1308/2191/25 1311/2194/39 1312/2195/39 +f 1308/2191/25 1312/2195/39 1309/2192/25 +f 1311/2194/39 1295/2178/26 1298/2181/26 +f 1311/2194/39 1298/2181/26 1312/2195/39 +f 1315/2196/26 1316/2197/42 1317/2198/42 +f 1315/2196/26 1317/2198/42 1318/2199/26 +f 1316/2197/42 1319/2200/16 1320/2201/16 +f 1316/2197/42 1320/2201/16 1317/2198/42 +f 1319/2200/16 1321/2202/36 1322/2203/36 +f 1319/2200/16 1322/2203/36 1320/2201/16 +f 1321/2202/36 1323/2204/27 1324/2205/27 +f 1321/2202/36 1324/2205/27 1322/2203/36 +f 1323/2204/27 1325/2206/33 1326/2207/33 +f 1323/2204/27 1326/2207/33 1324/2205/27 +f 1325/2206/33 1327/2208/25 1328/2209/25 +f 1325/2206/33 1328/2209/25 1326/2207/33 +f 1327/2208/25 1329/2210/729 1330/2211/730 +f 1327/2208/25 1330/2211/730 1328/2209/25 +f 1331/2212/731 1315/2196/26 1318/2199/26 +f 1331/2212/731 1318/2199/26 1332/2213/39 +f 1335/2214/732 1336/2215/733 1337/2216/734 +f 1335/2214/732 1337/2216/734 1338/2217/735 +f 1336/2215/733 1339/2218/736 1340/2219/737 +f 1336/2215/733 1340/2219/737 1337/2216/734 +f 1339/2218/736 1341/2220/738 1342/2221/739 +f 1339/2218/736 1342/2221/739 1340/2219/737 +f 1341/2220/738 1343/2222/740 1344/2223/741 +f 1341/2220/738 1344/2223/741 1342/2221/739 +f 1338/2217/735 1337/2216/734 1345/2224/742 +f 1338/2217/735 1345/2224/742 1346/2225/743 +f 1337/2216/734 1340/2219/737 1347/2226/742 +f 1337/2216/734 1347/2226/742 1345/2224/742 +f 1340/2219/737 1342/2221/739 1348/2227/742 +f 1340/2219/737 1348/2227/742 1347/2226/742 +f 1348/2227/742 1342/2221/739 1344/2223/741 +f 1348/2227/742 1344/2223/741 1349/2228/742 +f 1350/2229/744 1351/2230/745 1352/2231/746 +f 1350/2229/744 1352/2231/746 1353/2232/747 +f 1351/2230/745 1354/2233/745 1355/2234/748 +f 1351/2230/745 1355/2234/748 1352/2231/746 +f 1355/2234/748 1354/2233/745 1356/2235/745 +f 1355/2234/748 1356/2235/745 1357/2236/749 +f 1357/2236/749 1356/2235/745 1358/2237/745 +f 1357/2236/749 1358/2237/745 1359/2238/750 +f 1360/2239/751 1361/2240/752 1362/2241/753 +f 1360/2239/751 1362/2241/753 1363/2242/754 +f 1363/2242/754 1362/2241/753 1364/2243/755 +f 1363/2242/754 1364/2243/755 1365/2244/756 +f 1365/2244/756 1364/2243/755 1366/2245/757 +f 1365/2244/756 1366/2245/757 1367/2246/758 +f 1366/2245/757 1368/2247/759 1369/2248/760 +f 1366/2245/757 1369/2248/760 1367/2246/758 +f 1370/2249/761 1371/2250/762 1344/2223/741 +f 1370/2249/761 1344/2223/741 1343/2222/740 +f 1371/2250/762 1372/2251/763 1349/2228/742 +f 1371/2250/762 1349/2228/742 1344/2223/741 +f 1372/2251/763 1373/2252/764 1358/2237/745 +f 1372/2251/763 1358/2237/745 1349/2228/742 +f 1359/2238/750 1374/2253/765 1375/2254/766 +f 1359/2238/750 1375/2254/766 1368/2247/759 +f 1361/2240/752 1353/2232/747 1352/2231/746 +f 1361/2240/752 1352/2231/746 1362/2241/753 +f 1362/2241/753 1352/2231/746 1355/2234/748 +f 1362/2241/753 1355/2234/748 1364/2243/755 +f 1364/2243/755 1355/2234/748 1357/2236/749 +f 1364/2243/755 1357/2236/749 1366/2245/757 +f 1357/2236/749 1359/2238/750 1368/2247/759 +f 1357/2236/749 1368/2247/759 1366/2245/757 +f 1376/2255/767 1369/2248/760 1368/2247/759 +f 1376/2255/767 1368/2247/759 1375/2254/766 +f 1346/2225/743 1345/2224/742 1351/2230/745 +f 1346/2225/743 1351/2230/745 1350/2229/744 +f 1345/2224/742 1347/2226/742 1354/2233/745 +f 1345/2224/742 1354/2233/745 1351/2230/745 +f 1354/2233/745 1347/2226/742 1348/2227/742 +f 1354/2233/745 1348/2227/742 1356/2235/745 +f 1356/2235/745 1348/2227/742 1349/2228/742 +f 1356/2235/745 1349/2228/742 1358/2237/745 +f 1373/2252/764 1374/2253/765 1359/2238/750 +f 1373/2252/764 1359/2238/750 1358/2237/745 +f 1381/2256/768 1382/2257/769 1383/2258/770 +f 1381/2256/768 1383/2258/770 1384/2259/769 +f 1385/2260/771 1386/2261/771 1387/2262/772 +f 1385/2260/771 1387/2262/772 1388/2263/771 +f 1397/2264/773 1398/2265/773 1399/2266/774 +f 1397/2264/773 1399/2266/774 1400/2267/775 +f 1401/2268/776 1402/2269/776 1403/2270/776 +f 1401/2268/776 1403/2270/776 1404/2271/776 +f 1405/2272/777 1406/2273/778 1407/2274/778 +f 1405/2272/777 1407/2274/778 1408/2275/778 +f 1409/2276/779 1410/2277/779 1411/2278/779 +f 1409/2276/779 1411/2278/779 1412/2279/780 +f 1413/2280/781 1414/2281/781 1415/2282/782 +f 1413/2280/781 1415/2282/782 1416/2283/782 +f 1417/2284/783 1418/2285/784 1419/2286/784 +f 1417/2284/783 1419/2286/784 1420/2287/784 +f 1421/2288/785 1422/2289/785 1423/2290/785 +f 1421/2288/785 1423/2290/785 1424/2291/785 +f 1425/2292/786 1426/2293/787 1427/2294/787 +f 1425/2292/786 1427/2294/787 1428/2295/787 +f 1429/2296/788 1430/2297/788 1431/2298/788 +f 1429/2296/788 1431/2298/788 1432/2299/788 +f 1433/2300/789 1434/2301/789 1435/2302/789 +f 1433/2300/789 1435/2302/789 1436/2303/789 +f 1437/2304/790 1438/2305/790 1439/2306/790 +f 1437/2304/790 1439/2306/790 1440/2307/790 +f 1441/2308/791 1442/2309/791 1443/2310/791 +f 1441/2308/791 1443/2310/791 1444/2311/791 +f 1445/2312/792 1446/2313/792 1447/2314/793 +f 1445/2312/792 1447/2314/793 1448/2315/794 +f 1449/2316/795 1450/2317/795 1451/2318/796 +f 1449/2316/795 1451/2318/796 1452/2319/795 +f 1453/512/797 1454/2320/798 1455/513/798 +f 1457/2321/799 1458/2322/800 1459/2323/799 +f 1457/2321/799 1459/2323/799 1460/2324/799 +f 1461/2325/801 1462/2326/801 1463/2327/801 +f 1461/2325/801 1463/2327/801 1464/2328/801 +f 1469/519/802 1470/2329/802 1471/520/802 +f 1473/522/803 1474/2330/804 1475/523/803 +f 1485/2331/805 1486/2332/805 1487/2333/805 +f 1485/2331/805 1487/2333/805 1488/2334/805 +f 1493/2335/806 1494/2336/807 1495/2337/806 +f 1493/2335/806 1495/2337/806 1496/2338/806 +f 1497/2339/808 1498/2340/808 1499/2341/808 +f 1497/2339/808 1499/2341/808 1500/2342/808 +f 1501/2343/809 1502/2344/810 1503/2345/809 +f 1501/2343/809 1503/2345/809 1504/2346/809 +f 1505/2347/811 1506/2348/811 1507/2349/811 +f 1505/2347/811 1507/2349/811 1508/2350/811 +f 1513/2351/812 1514/2352/813 1515/2353/814 +f 1513/2351/812 1515/2353/814 1516/2354/813 +f 1517/2355/815 1518/2356/815 1519/2357/815 +f 1517/2355/815 1519/2357/815 1520/2358/815 +f 1521/2359/816 1522/2360/816 1523/2361/816 +f 1521/2359/816 1523/2361/816 1524/2362/816 +f 1525/2363/817 1526/2364/818 1527/2365/818 +f 1525/2363/817 1527/2365/818 1528/2366/819 +f 1529/2367/820 1530/2368/821 1531/2369/820 +f 1529/2367/820 1531/2369/820 1532/2370/821 +f 1533/2371/572 1534/2372/581 1535/2373/822 +f 1533/2371/572 1535/2373/822 1536/2374/573 +f 1536/2374/573 1535/2373/822 679/1828/504 +f 1536/2374/573 679/1828/504 682/1831/507 +f 1537/2375/823 1538/2376/824 1534/2372/581 +f 1537/2375/823 1534/2372/581 1533/2371/572 +f 680/1829/505 1539/2377/825 1540/2378/826 +f 680/1829/505 1540/2378/826 681/1830/506 +f 1541/2379/827 1542/2380/828 1538/2376/824 +f 1541/2379/827 1538/2376/824 1537/2375/823 +f 1543/2381/576 1544/2382/829 1542/2380/828 +f 1543/2381/576 1542/2380/828 1541/2379/827 +f 1549/546/28 1550/545/28 1551/2383/471 +f 1549/546/28 1551/2383/471 1552/2384/472 +f 1552/2384/472 1551/2383/471 643/1792/472 +f 1552/2384/472 643/1792/472 642/1791/471 +f 1553/2385/830 1554/2386/831 641/1790/470 +f 1553/2385/830 641/1790/470 644/1793/470 +f 1555/2387/832 1556/2388/832 1554/2386/831 +f 1555/2387/832 1554/2386/831 1553/2385/830 +f 352/1587/343 1565/2389/833 1566/2390/833 +f 352/1587/343 1566/2390/833 349/1584/340 +f 278/1530/291 283/1535/834 1567/2391/293 +f 278/1530/291 1567/2391/293 1568/2392/291 +f 1597/2393/835 1598/2394/835 1599/2395/836 +f 1597/2393/835 1599/2395/836 1600/2396/836 +f 1601/2397/380 1602/2398/380 1603/2399/25 +f 1601/2397/380 1603/2399/25 1604/2400/837 +f 70/1403/181 72/1405/183 80/1413/189 +f 70/1403/181 80/1413/189 79/1412/188 +f 75/1408/184 74/1407/163 71/1404/182 +f 75/1408/184 71/1404/182 70/1403/181 +f 76/1409/185 78/1411/187 82/1415/191 +f 76/1409/185 82/1415/191 13/1392/170 +f 79/1412/188 81/1414/190 77/1410/186 +f 79/1412/188 77/1410/186 76/1409/185 +f 72/1405/183 1609/2401/838 1610/2402/839 +f 72/1405/183 1610/2402/839 80/1413/189 +f 80/1413/189 1610/2402/839 1611/2403/840 +f 80/1413/189 1611/2403/840 81/1414/190 +f 71/1404/182 1612/2404/841 1609/2401/838 +f 71/1404/182 1609/2401/838 72/1405/183 +f 74/1407/163 1613/2405/204 1612/2404/841 +f 74/1407/163 1612/2404/841 71/1404/182 +f 14/1393/171 82/1415/191 131/1419/195 +f 14/1393/171 131/1419/195 142/1429/205 +f 78/1411/187 132/1420/196 131/1419/195 +f 78/1411/187 131/1419/195 82/1415/191 +f 77/1410/186 1614/2406/842 132/1420/196 +f 77/1410/186 132/1420/196 78/1411/187 +f 81/1414/190 1611/2403/840 1614/2406/842 +f 81/1414/190 1614/2406/842 77/1410/186 +f 144/1431/207 145/1432/208 128/1416/192 +f 144/1431/207 128/1416/192 130/1418/194 +f 1615/587/16 135/589/16 134/1422/198 +f 1617/2407/843 1618/2408/199 1619/2409/844 +f 1617/2407/843 1619/2409/844 1620/2410/192 +f 1621/2411/845 1622/2412/846 1623/2413/846 +f 1621/2411/845 1623/2413/846 1624/2414/212 +f 1625/2415/847 1626/2416/3 1627/2417/31 +f 1625/2415/847 1627/2417/31 1628/2418/848 +f 1629/2419/849 1625/2415/847 1628/2418/848 +f 1629/2419/849 1628/2418/848 1630/2420/849 +f 1610/2402/839 1609/2401/838 1631/2421/850 +f 1610/2402/839 1631/2421/850 1632/2422/851 +f 1610/2402/839 1632/2422/851 1633/2423/852 +f 1610/2402/839 1633/2423/852 1611/2403/840 +f 1609/2401/838 1612/2404/841 1634/2424/853 +f 1609/2401/838 1634/2424/853 1631/2421/850 +f 1613/2405/204 1635/2425/221 1634/2424/853 +f 1613/2405/204 1634/2424/853 1612/2404/841 +f 1614/2406/842 1636/2426/854 129/1417/193 +f 1614/2406/842 129/1417/193 132/1420/196 +f 1611/2403/840 1633/2423/852 1636/2426/854 +f 1611/2403/840 1636/2426/854 1614/2406/842 +f 132/1420/196 129/1417/193 128/1416/192 +f 132/1420/196 128/1416/192 133/1421/197 +f 1649/2427/855 1650/2428/856 1651/2429/223 +f 1649/2427/855 1651/2429/223 1652/2430/226 +f 1650/2428/856 1657/2431/857 1658/2432/227 +f 1650/2428/856 1658/2432/227 1651/2429/223 +f 1659/2433/858 1660/2434/859 1650/2428/856 +f 1659/2433/858 1650/2428/856 1649/2427/855 +f 1663/2435/233 1664/2436/860 1665/2437/861 +f 1663/2435/233 1665/2437/861 1666/2438/234 +f 1658/2432/227 1657/2431/857 1667/2439/862 +f 1658/2432/227 1667/2439/862 1668/2440/863 +f 1660/2434/859 1669/2441/864 1657/2431/857 +f 1660/2434/859 1657/2431/857 1650/2428/856 +f 1670/2442/865 1671/2443/866 1660/2434/859 +f 1670/2442/865 1660/2434/859 1659/2433/858 +f 1664/2436/860 1674/2444/867 1675/2445/868 +f 1664/2436/860 1675/2445/868 1665/2437/861 +f 1676/2446/243 1677/2447/869 1664/2436/860 +f 1676/2446/243 1664/2436/860 1663/2435/233 +f 1678/2448/870 1679/2449/871 1680/2450/872 +f 1678/2448/870 1680/2450/872 1681/2451/873 +f 1669/2441/864 1682/2452/874 1667/2439/862 +f 1669/2441/864 1667/2439/862 1657/2431/857 +f 1671/2443/866 1683/2453/875 1669/2441/864 +f 1671/2443/866 1669/2441/864 1660/2434/859 +f 1684/2454/876 1685/2455/877 1671/2443/866 +f 1684/2454/876 1671/2443/866 1670/2442/865 +f 1674/2444/867 1688/2456/878 1689/2457/879 +f 1674/2444/867 1689/2457/879 1675/2445/868 +f 1677/2447/869 1690/2458/880 1674/2444/867 +f 1677/2447/869 1674/2444/867 1664/2436/860 +f 1676/2446/243 1691/2459/881 1692/2460/882 +f 1676/2446/243 1692/2460/882 1677/2447/869 +f 1697/2461/883 1698/2462/884 1680/2450/872 +f 1697/2461/883 1680/2450/872 1699/2463/885 +f 1683/2453/875 1700/2464/886 1682/2452/874 +f 1683/2453/875 1682/2452/874 1669/2441/864 +f 1685/2455/877 1701/2465/887 1683/2453/875 +f 1685/2455/877 1683/2453/875 1671/2443/866 +f 1702/2466/888 1703/2467/889 1704/2468/890 +f 1702/2466/888 1704/2468/890 1705/2469/888 +f 1688/2456/878 1706/2470/891 1707/2471/892 +f 1688/2456/878 1707/2471/892 1689/2457/879 +f 1690/2458/880 1708/2472/893 1688/2456/878 +f 1690/2458/880 1688/2456/878 1674/2444/867 +f 1692/2460/882 1709/2473/894 1690/2458/880 +f 1692/2460/882 1690/2458/880 1677/2447/869 +f 1710/2474/895 1711/2475/896 1712/2476/897 +f 1710/2474/895 1712/2476/897 1713/2477/898 +f 1716/2478/899 1717/2479/900 1698/2462/884 +f 1716/2478/899 1698/2462/884 1697/2461/883 +f 1701/2465/887 1718/2480/901 1700/2464/886 +f 1701/2465/887 1700/2464/886 1683/2453/875 +f 1703/2467/889 1719/2481/902 1720/2482/902 +f 1703/2467/889 1720/2482/902 1721/2483/890 +f 1708/2472/893 1722/2484/903 1706/2470/891 +f 1708/2472/893 1706/2470/891 1688/2456/878 +f 1709/2473/894 1723/2485/904 1708/2472/893 +f 1709/2473/894 1708/2472/893 1690/2458/880 +f 1711/2475/896 1724/2486/905 1725/2487/906 +f 1711/2475/896 1725/2487/906 1712/2476/897 +f 1716/2478/899 1728/2488/907 1729/2489/908 +f 1716/2478/899 1729/2489/908 1717/2479/900 +f 1719/2481/902 1730/2490/27 1731/2491/27 +f 1719/2481/902 1731/2491/27 1720/2482/902 +f 1723/2485/904 1732/2492/909 1722/2484/903 +f 1723/2485/904 1722/2484/903 1708/2472/893 +f 1724/2486/905 1733/2493/910 1734/2494/911 +f 1724/2486/905 1734/2494/911 1725/2487/906 +f 1737/2495/912 1738/2496/913 1739/623/76 +f 1737/2495/912 1739/623/76 1740/622/76 +f 1734/2494/911 1733/2493/910 1741/2497/914 +f 1734/2494/911 1741/2497/914 1742/2498/915 +f 1743/2499/916 1744/2500/1 1745/2501/1 +f 1743/2499/916 1745/2501/1 1746/2502/916 +f 1746/2502/916 1747/2503/917 1748/2504/917 +f 1746/2502/916 1748/2504/917 1743/2499/916 +f 1749/2505/918 1750/2506/919 1751/2507/919 +f 1749/2505/918 1751/2507/919 1752/2508/920 +f 1698/2462/884 1753/2509/884 1681/2451/873 +f 1698/2462/884 1681/2451/873 1680/2450/872 +f 1717/2479/900 1754/2510/921 1753/2509/884 +f 1717/2479/900 1753/2509/884 1698/2462/884 +f 1711/2475/896 1755/2511/922 1756/2512/905 +f 1711/2475/896 1756/2512/905 1724/2486/905 +f 1717/2479/900 1729/2489/908 1757/2513/923 +f 1717/2479/900 1757/2513/923 1754/2510/921 +f 1724/2486/905 1756/2512/905 1758/2514/924 +f 1724/2486/905 1758/2514/924 1733/2493/910 +f 1733/2493/910 1758/2514/924 1761/2515/925 +f 1733/2493/910 1761/2515/925 1741/2497/914 +f 1682/2452/874 1697/2461/883 1699/2463/885 +f 1682/2452/874 1699/2463/885 1667/2439/862 +f 1700/2464/886 1716/2478/899 1697/2461/883 +f 1700/2464/886 1697/2461/883 1682/2452/874 +f 1692/2460/882 1712/2476/897 1725/2487/906 +f 1692/2460/882 1725/2487/906 1709/2473/894 +f 1718/2480/901 1728/2488/907 1716/2478/899 +f 1718/2480/901 1716/2478/899 1700/2464/886 +f 1709/2473/894 1725/2487/906 1734/2494/911 +f 1709/2473/894 1734/2494/911 1723/2485/904 +f 1730/2490/27 1738/2496/913 1737/2495/912 +f 1730/2490/27 1737/2495/912 1731/2491/27 +f 1723/2485/904 1734/2494/911 1742/2498/915 +f 1723/2485/904 1742/2498/915 1732/2492/909 +f 1679/2449/871 1762/2516/926 1699/2463/885 +f 1679/2449/871 1699/2463/885 1680/2450/872 +f 1711/2475/896 1710/2474/895 1763/2517/927 +f 1711/2475/896 1763/2517/927 1755/2511/922 +f 1667/2439/862 1699/2463/885 1762/2516/926 +f 1667/2439/862 1762/2516/926 1668/2440/863 +f 1692/2460/882 1691/2459/881 1713/2477/898 +f 1692/2460/882 1713/2477/898 1712/2476/897 +f 1764/626/928 1766/628/303 1767/2518/928 +f 1770/2519/929 1771/2520/930 1772/2521/931 +f 1770/2519/929 1772/2521/931 1773/2522/932 +f 1773/2522/932 1772/2521/931 1774/2523/3 +f 1773/2522/932 1774/2523/3 1775/2524/3 +f 1776/2525/933 1777/2526/934 1778/2527/935 +f 1776/2525/933 1778/2527/935 1779/2528/936 +f 1777/2526/934 1780/2529/937 1781/2530/938 +f 1777/2526/934 1781/2530/938 1778/2527/935 +f 1782/2531/939 1783/2532/940 1784/2533/941 +f 1782/2531/939 1784/2533/941 1785/2534/941 +f 1786/2535/942 1787/2536/943 1788/2537/944 +f 1786/2535/942 1788/2537/944 1789/2538/945 +f 1790/2539/946 1788/2537/944 1791/2540/947 +f 1790/2539/946 1791/2540/947 1792/2541/948 +f 1797/2542/949 1770/2519/929 1773/2522/932 +f 1797/2542/949 1773/2522/932 1798/2543/949 +f 1799/2544/950 1800/2545/950 1772/2521/931 +f 1799/2544/950 1772/2521/931 1771/2520/930 +f 1801/2546/951 1802/2547/952 1803/2548/952 +f 1801/2546/951 1803/2548/952 1804/2549/953 +f 1805/2550/954 1806/2551/955 1807/2552/956 +f 1805/2550/954 1807/2552/956 1808/2553/957 +f 1809/2554/958 1810/2555/959 1811/2556/960 +f 1809/2554/958 1811/2556/960 1812/2557/961 +f 1776/2525/933 1813/2558/962 1814/2559/963 +f 1776/2525/933 1814/2559/963 1777/2526/934 +f 1788/2537/944 1787/2536/943 1815/2560/964 +f 1788/2537/944 1815/2560/964 1791/2540/947 +f 1777/2526/934 1814/2559/963 1816/2561/965 +f 1777/2526/934 1816/2561/965 1780/2529/937 +f 1788/2537/944 1790/2539/946 1817/2562/966 +f 1788/2537/944 1817/2562/966 1789/2538/945 +f 1782/2531/939 1801/2546/951 1804/2549/953 +f 1782/2531/939 1804/2549/953 1783/2532/940 +f 1786/2535/942 1809/2554/958 1812/2557/961 +f 1786/2535/942 1812/2557/961 1787/2536/943 +f 1813/2558/962 1818/2563/967 1805/2550/954 +f 1813/2558/962 1805/2550/954 1814/2559/963 +f 1787/2536/943 1812/2557/961 1819/2564/968 +f 1787/2536/943 1819/2564/968 1815/2560/964 +f 1814/2559/963 1805/2550/954 1808/2553/957 +f 1814/2559/963 1808/2553/957 1816/2561/965 +f 1820/2565/969 1821/2566/970 1822/2567/971 +f 1820/2565/969 1822/2567/971 1823/2568/972 +f 1824/2569/973 1825/2570/346 1826/2571/346 +f 1824/2569/973 1826/2571/346 1827/2572/974 +f 1836/2573/975 1837/2574/976 1838/2575/977 +f 1836/2573/975 1838/2575/977 1839/2576/978 +f 1837/2574/976 1840/2577/979 1841/2578/980 +f 1837/2574/976 1841/2578/980 1838/2575/977 +f 1842/2579/981 1843/2580/982 1844/2581/983 +f 1842/2579/981 1844/2581/983 1845/2582/984 +f 1846/2583/985 1847/2584/986 1848/2585/357 +f 1846/2583/985 1848/2585/357 1849/2586/360 +f 1850/2587/987 1836/2573/975 1839/2576/978 +f 1850/2587/987 1839/2576/978 1851/2588/987 +f 1840/2577/979 1852/2589/988 1853/2590/988 +f 1840/2577/979 1853/2590/988 1841/2578/980 +f 1842/2579/981 1854/2591/989 1855/2592/989 +f 1842/2579/981 1855/2592/989 1843/2580/982 +f 1845/2582/984 1844/2581/983 1847/2584/986 +f 1845/2582/984 1847/2584/986 1846/2583/985 +f 1856/2593/990 1857/2594/991 1858/2595/992 +f 1856/2593/990 1858/2595/992 1859/2596/993 +f 1860/2597/994 1824/2569/973 1827/2572/974 +f 1860/2597/994 1827/2572/974 1861/2598/995 +f 1857/2594/991 1860/2597/994 1861/2598/995 +f 1857/2594/991 1861/2598/995 1858/2595/992 +f 1862/2599/996 1863/2600/997 1864/2601/997 +f 1862/2599/996 1864/2601/997 1865/2602/998 +f 1866/2603/999 1821/2566/970 1820/2565/969 +f 1866/2603/999 1820/2565/969 1867/2604/1000 +f 1865/2602/998 1866/2603/999 1867/2604/1000 +f 1865/2602/998 1867/2604/1000 1862/2599/996 +f 1856/2593/990 1859/2596/993 1868/2605/1001 +f 1856/2593/990 1868/2605/1001 1869/2606/1001 +f 1910/2607/1002 1911/2608/1003 1912/2609/1003 +f 1910/2607/1002 1912/2609/1003 1913/2610/1002 +f 1911/2608/1003 1914/2611/36 1915/2612/36 +f 1911/2608/1003 1915/2612/36 1912/2609/1003 +f 1914/2611/36 1916/2613/1004 1917/2614/1004 +f 1914/2611/36 1917/2614/1004 1915/2612/36 +f 1916/2613/1004 1918/2615/16 1919/2616/16 +f 1916/2613/1004 1919/2616/16 1917/2614/1004 +f 1920/2617/25 1921/2618/1005 1922/2619/1005 +f 1920/2617/25 1922/2619/1005 1923/2620/25 +f 1921/2618/1005 1924/2621/1006 1925/2622/1006 +f 1921/2618/1005 1925/2622/1006 1922/2619/1005 +f 1926/2623/1007 1927/2624/1008 1928/2625/1009 +f 1926/2623/1007 1928/2625/1009 1929/2626/1009 +f 1930/2627/1010 1910/2607/1002 1913/2610/1002 +f 1930/2627/1010 1913/2610/1002 1931/2628/1010 +f 1932/2629/1011 1933/2630/1012 1934/2631/1013 +f 1932/2629/1011 1934/2631/1013 1935/2632/1014 +f 1935/2632/1014 1934/2631/1013 1936/2633/1015 +f 1935/2632/1014 1936/2633/1015 1937/2634/1016 +f 1937/2634/1016 1936/2633/1015 1938/2635/1017 +f 1937/2634/1016 1938/2635/1017 1939/2636/1018 +f 1939/2636/1018 1938/2635/1017 1940/2637/389 +f 1939/2636/1018 1940/2637/389 1941/2638/389 +f 1942/2639/25 1943/2640/25 1944/2641/1005 +f 1942/2639/25 1944/2641/1005 1945/2642/1005 +f 1945/2642/1005 1944/2641/1005 1946/2643/33 +f 1945/2642/1005 1946/2643/33 1926/2623/1007 +f 1926/2623/1007 1946/2643/33 1947/2644/110 +f 1926/2623/1007 1947/2644/110 1927/2624/1008 +f 1927/2624/1008 1947/2644/110 1933/2630/1012 +f 1927/2624/1008 1933/2630/1012 1932/2629/1011 +f 1948/2645/1019 1949/2646/1020 1950/2647/1021 +f 1948/2645/1019 1950/2647/1021 1951/2648/1022 +f 1951/2648/1022 1950/2647/1021 1952/2649/1023 +f 1951/2648/1022 1952/2649/1023 1953/2650/1024 +f 1953/2650/1024 1952/2649/1023 1954/2651/1025 +f 1953/2650/1024 1954/2651/1025 1955/2652/1026 +f 1955/2652/1026 1954/2651/1025 1956/2653/397 +f 1955/2652/1026 1956/2653/397 1957/2654/397 +f 1958/2655/25 1959/2656/25 1960/2657/1005 +f 1958/2655/25 1960/2657/1005 1961/2658/1005 +f 1961/2658/1005 1960/2657/1005 1962/2659/33 +f 1961/2658/1005 1962/2659/33 1963/2660/33 +f 1963/2660/33 1962/2659/33 1964/2661/110 +f 1963/2660/33 1964/2661/110 1965/2662/110 +f 1965/2662/110 1964/2661/110 1949/2646/1020 +f 1965/2662/110 1949/2646/1020 1948/2645/1019 +f 1966/2663/1027 1967/2664/1028 1968/2665/1029 +f 1966/2663/1027 1968/2665/1029 1969/2666/1030 +f 1969/2666/1030 1968/2665/1029 1970/2667/1031 +f 1969/2666/1030 1970/2667/1031 1971/2668/1032 +f 1971/2668/1032 1970/2667/1031 1972/2669/1033 +f 1971/2668/1032 1972/2669/1033 1973/2670/1034 +f 1973/2670/1034 1972/2669/1033 1974/2671/406 +f 1973/2670/1034 1974/2671/406 1975/2672/406 +f 1976/2673/25 1977/2674/25 1978/2675/1005 +f 1976/2673/25 1978/2675/1005 1979/2676/1005 +f 1979/2676/1005 1978/2675/1005 1980/2677/33 +f 1979/2676/1005 1980/2677/33 1981/2678/33 +f 1981/2678/33 1980/2677/33 1982/2679/110 +f 1981/2678/33 1982/2679/110 1983/2680/110 +f 1983/2680/110 1982/2679/110 1967/2664/1028 +f 1983/2680/110 1967/2664/1028 1966/2663/1027 +f 1929/2626/1009 1998/2681/1 1995/694/1 +f 1929/2626/1009 1995/694/1 1994/693/1 +f 1928/2625/1009 1999/697/1 1998/2681/1 +f 1928/2625/1009 1998/2681/1 1929/2626/1009 +f 1987/686/1 1999/697/1 1928/2625/1009 +f 2000/2682/27 2001/2683/111 2002/2684/111 +f 2000/2682/27 2002/2684/111 2003/2685/27 +f 2001/2683/111 2004/2686/36 2005/2687/36 +f 2001/2683/111 2005/2687/36 2002/2684/111 +f 2004/2686/36 2006/2688/1004 2007/2689/1004 +f 2004/2686/36 2007/2689/1004 2005/2687/36 +f 2006/2688/1004 2008/2690/16 2009/2691/16 +f 2006/2688/1004 2009/2691/16 2007/2689/1004 +f 2010/2692/408 2011/2693/1035 2012/2694/1036 +f 2010/2692/408 2012/2694/1036 2013/2695/409 +f 2011/2693/1035 2014/2696/1037 2015/2697/1038 +f 2011/2693/1035 2015/2697/1038 2012/2694/1036 +f 2014/2696/1037 2016/2698/110 2017/2699/110 +f 2014/2696/1037 2017/2699/110 2015/2697/1038 +f 2016/2698/110 2000/2682/27 2003/2685/27 +f 2016/2698/110 2003/2685/27 2017/2699/110 +f 2022/2700/1039 2023/2701/1040 2024/2702/1040 +f 2022/2700/1039 2024/2702/1040 2025/2703/1039 +f 2026/2704/1041 2022/2700/1039 2025/2703/1039 +f 2026/2704/1041 2025/2703/1039 2027/2705/1041 +f 2028/2706/1042 2026/2704/1041 2027/2705/1041 +f 2028/2706/1042 2027/2705/1041 2029/2707/1042 +f 2030/2708/418 2028/2706/1042 2029/2707/1042 +f 2030/2708/418 2029/2707/1042 2031/2709/418 +f 2032/2710/1043 2033/2711/420 2034/2712/420 +f 2032/2710/1043 2034/2712/420 2035/2713/1043 +f 2036/2714/1044 2032/2710/1043 2035/2713/1043 +f 2036/2714/1044 2035/2713/1043 2037/2715/1044 +f 2038/2716/1045 2036/2714/1044 2037/2715/1044 +f 2038/2716/1045 2037/2715/1044 2039/2717/1045 +f 2023/2701/1040 2038/2716/1045 2039/2717/1045 +f 2023/2701/1040 2039/2717/1045 2024/2702/1040 +f 2058/2718/1046 2059/2719/1047 2060/2720/1048 +f 2058/2718/1046 2060/2720/1048 2061/2721/1049 +f 2059/2719/1047 2062/2722/1050 2063/2723/1051 +f 2059/2719/1047 2063/2723/1051 2060/2720/1048 +f 2062/2722/1050 2064/2724/1052 2065/2725/1053 +f 2062/2722/1050 2065/2725/1053 2063/2723/1051 +f 2064/2724/1052 2066/2726/431 2067/2727/432 +f 2064/2724/1052 2067/2727/432 2065/2725/1053 +f 2068/2728/435 2069/2729/1054 2070/2730/1055 +f 2068/2728/435 2070/2730/1055 2071/2731/435 +f 2069/2729/1054 2072/2732/1056 2073/2733/1057 +f 2069/2729/1054 2073/2733/1057 2070/2730/1055 +f 2072/2732/1056 2074/2734/1058 2075/2735/1059 +f 2072/2732/1056 2075/2735/1059 2073/2733/1057 +f 2074/2734/1058 2058/2718/1046 2061/2721/1049 +f 2074/2734/1058 2061/2721/1049 2075/2735/1059 +f 2076/2736/1060 2077/2737/1061 2078/2738/1062 +f 2076/2736/1060 2078/2738/1062 2079/2739/1063 +f 2079/2739/1063 2078/2738/1062 2080/2740/1064 +f 2079/2739/1063 2080/2740/1064 2081/2741/1065 +f 2081/2741/1065 2080/2740/1064 2082/2742/1066 +f 2081/2741/1065 2082/2742/1066 2083/2743/1067 +f 2083/2743/1067 2082/2742/1066 2084/2744/448 +f 2083/2743/1067 2084/2744/448 2085/2745/448 +f 2086/2746/450 2087/2747/450 2088/2748/1068 +f 2086/2746/450 2088/2748/1068 2089/2749/1069 +f 2089/2749/1069 2088/2748/1068 2090/2750/1070 +f 2089/2749/1069 2090/2750/1070 2091/2751/1071 +f 2091/2751/1071 2090/2750/1070 2092/2752/1072 +f 2091/2751/1071 2092/2752/1072 2093/2753/1073 +f 2093/2753/1073 2092/2752/1072 2077/2737/1061 +f 2093/2753/1073 2077/2737/1061 2076/2736/1060 +f 2094/2754/1074 2095/2755/1075 2096/2756/1076 +f 2094/2754/1074 2096/2756/1076 2097/2757/1077 +f 2097/2757/1077 2096/2756/1076 2098/2758/1078 +f 2097/2757/1077 2098/2758/1078 2099/2759/1079 +f 2099/2759/1079 2098/2758/1078 2100/2760/1080 +f 2099/2759/1079 2100/2760/1080 2101/2761/1081 +f 2101/2761/1081 2100/2760/1080 2102/2762/462 +f 2101/2761/1081 2102/2762/462 2103/2763/462 +f 2104/2764/464 2105/2765/464 2106/2766/1082 +f 2104/2764/464 2106/2766/1082 2107/2767/1083 +f 2107/2767/1083 2106/2766/1082 2108/2768/1084 +f 2107/2767/1083 2108/2768/1084 2109/2769/1085 +f 2109/2769/1085 2108/2768/1084 2110/2770/1086 +f 2109/2769/1085 2110/2770/1086 2111/2771/1087 +f 2111/2771/1087 2110/2770/1086 2095/2755/1075 +f 2111/2771/1087 2095/2755/1075 2094/2754/1074 +f 2112/2772/1088 2113/2773/1089 2114/2774/1090 +f 2112/2772/1088 2114/2774/1090 2115/2775/1090 +f 2116/2776/1091 2117/2777/1092 2118/2778/1093 +f 2116/2776/1091 2118/2778/1093 2119/2779/1094 +f 2117/2777/1092 2120/2780/1095 2121/2781/1096 +f 2117/2777/1092 2121/2781/1096 2118/2778/1093 +f 2120/2780/1095 2122/2782/1097 2123/2783/1098 +f 2120/2780/1095 2123/2783/1098 2121/2781/1096 +f 2122/2782/1097 2124/2784/1099 2125/2785/1100 +f 2122/2782/1097 2125/2785/1100 2123/2783/1098 +f 2124/2784/1099 2126/2786/1101 2127/2787/1102 +f 2124/2784/1099 2127/2787/1102 2125/2785/1100 +f 2126/2786/1101 2128/2788/1103 2129/2789/1104 +f 2126/2786/1101 2129/2789/1104 2127/2787/1102 +f 2128/2788/1103 2130/2790/1105 2131/2791/1106 +f 2128/2788/1103 2131/2791/1106 2129/2789/1104 +f 2130/2790/1105 2132/2792/1107 2133/2793/1108 +f 2130/2790/1105 2133/2793/1108 2131/2791/1106 +f 2132/2792/1107 2134/2794/1109 2135/2795/1110 +f 2132/2792/1107 2135/2795/1110 2133/2793/1108 +f 2134/2794/1109 2136/2796/1111 2137/2797/1112 +f 2134/2794/1109 2137/2797/1112 2135/2795/1110 +f 2138/2798/1113 2139/2799/1114 2140/2800/1115 +f 2138/2798/1113 2140/2800/1115 2141/2801/1112 +f 2139/2799/1114 2142/2802/1116 2143/2803/1117 +f 2139/2799/1114 2143/2803/1117 2140/2800/1115 +f 2142/2802/1116 2144/2804/1118 2145/2805/1119 +f 2142/2802/1116 2145/2805/1119 2143/2803/1117 +f 2144/2804/1118 2146/2806/1120 2147/2807/1121 +f 2144/2804/1118 2147/2807/1121 2145/2805/1119 +f 2146/2806/1120 2148/2808/1122 2149/2809/1123 +f 2146/2806/1120 2149/2809/1123 2147/2807/1121 +f 2148/2808/1122 2116/2776/1091 2119/2779/1094 +f 2148/2808/1122 2119/2779/1094 2149/2809/1123 +f 2150/2810/1124 2151/2811/1125 2152/2812/1126 +f 2150/2810/1124 2152/2812/1126 2153/2813/1127 +f 2158/2814/1128 2159/2815/1128 2160/2816/1128 +f 2158/2814/1128 2160/2816/1128 2161/2817/1128 +f 2162/2818/1129 2163/2819/1129 2164/2820/1129 +f 2162/2818/1129 2164/2820/1129 2165/2821/1129 +f 2166/2822/1130 2167/2823/1130 2168/2824/1131 +f 2166/2822/1130 2168/2824/1131 2169/2825/1130 +f 2170/2826/1132 2171/2827/1132 2172/2828/1133 +f 2170/2826/1132 2172/2828/1133 2173/2829/1132 +f 2174/2830/1134 2175/2831/1134 2176/2832/1135 +f 2174/2830/1134 2176/2832/1135 2177/2833/1135 +f 2178/2834/1136 2179/2835/1136 2180/2836/1136 +f 2178/2834/1136 2180/2836/1136 2181/2837/1137 +f 2182/2838/1138 2183/2839/1138 2184/2840/1138 +f 2182/2838/1138 2184/2840/1138 2185/2841/1138 +f 2186/2842/1139 2187/2843/1139 2188/2844/1139 +f 2186/2842/1139 2188/2844/1139 2189/2845/1139 +f 2198/2846/1140 2199/2847/1140 2200/2848/1140 +f 2198/2846/1140 2200/2848/1140 2201/2849/1140 +f 2250/2850/1141 2251/2851/1141 2252/2852/1142 +f 2250/2850/1141 2252/2852/1142 2253/2853/1141 +f 2254/2854/1143 2255/2855/1143 2256/2856/1143 +f 2254/2854/1143 2256/2856/1143 2257/2857/1143 +f 2258/2858/1144 2259/2859/1144 2260/2860/1145 +f 2258/2858/1144 2260/2860/1145 2261/2861/1145 +f 2262/2862/1146 2263/2863/1147 2264/2864/1147 +f 2262/2862/1146 2264/2864/1147 2265/2865/1147 +f 2266/2866/1148 2267/2867/1148 2268/2868/1148 +f 2266/2866/1148 2268/2868/1148 2269/2869/1149 +f 2270/2870/1150 2271/2871/1150 2272/2872/1150 +f 2270/2870/1150 2272/2872/1150 2273/2873/1150 +f 2274/2874/1151 2275/2875/1151 2276/2876/1151 +f 2274/2874/1151 2276/2876/1151 2277/2877/1151 +f 2278/2878/1152 2279/2879/1153 2280/2880/1153 +f 2278/2878/1152 2280/2880/1153 2281/2881/1153 +f 2294/2882/1154 2295/2883/1155 2296/2884/1155 +f 2294/2882/1154 2296/2884/1155 2297/2885/1154 +f 2306/2886/1156 2307/2887/1156 2308/2888/1156 +f 2306/2886/1156 2308/2888/1156 2309/2889/1156 +f 2310/800/95 2311/2890/95 2312/801/95 +f 2342/831/103 2344/833/103 2345/2891/103 +f 2386/2892/1157 2387/2893/1157 2388/2894/1157 +f 2386/2892/1157 2388/2894/1157 2389/2895/1157 +f 2398/2896/1158 2399/2897/1158 2400/2898/1158 +f 2398/2896/1158 2400/2898/1158 2401/2899/1158 +f 2406/2900/1159 2407/2901/1159 2408/2902/1159 +f 2406/2900/1159 2408/2902/1159 2409/2903/1159 +f 2116/2776/1091 2410/2904/1160 2411/2905/1161 +f 2116/2776/1091 2411/2905/1161 2117/2777/1092 +f 2117/2777/1092 2411/2905/1161 2412/2906/1162 +f 2117/2777/1092 2412/2906/1162 2120/2780/1095 +f 2120/2780/1095 2412/2906/1162 2413/2907/1163 +f 2120/2780/1095 2413/2907/1163 2122/2782/1097 +f 2122/2782/1097 2413/2907/1163 2414/2908/1164 +f 2122/2782/1097 2414/2908/1164 2124/2784/1099 +f 2124/2784/1099 2414/2908/1164 2415/2909/1165 +f 2124/2784/1099 2415/2909/1165 2126/2786/1101 +f 2126/2786/1101 2415/2909/1165 2416/2910/1166 +f 2126/2786/1101 2416/2910/1166 2128/2788/1103 +f 2128/2788/1103 2416/2910/1166 2417/2911/1167 +f 2128/2788/1103 2417/2911/1167 2130/2790/1105 +f 2130/2790/1105 2417/2911/1167 2418/2912/1168 +f 2130/2790/1105 2418/2912/1168 2132/2792/1107 +f 2132/2792/1107 2418/2912/1168 2419/2913/1169 +f 2132/2792/1107 2419/2913/1169 2134/2794/1109 +f 2134/2794/1109 2419/2913/1169 2420/2914/1170 +f 2134/2794/1109 2420/2914/1170 2136/2796/1111 +f 2138/2798/1113 2421/2915/1170 2422/2916/1171 +f 2138/2798/1113 2422/2916/1171 2139/2799/1114 +f 2139/2799/1114 2422/2916/1171 2423/2917/1172 +f 2139/2799/1114 2423/2917/1172 2142/2802/1116 +f 2142/2802/1116 2423/2917/1172 2424/2918/1173 +f 2142/2802/1116 2424/2918/1173 2144/2804/1118 +f 2144/2804/1118 2424/2918/1173 2425/2919/1174 +f 2144/2804/1118 2425/2919/1174 2146/2806/1120 +f 2146/2806/1120 2425/2919/1174 2426/2920/1175 +f 2146/2806/1120 2426/2920/1175 2148/2808/1122 +f 2148/2808/1122 2426/2920/1175 2410/2904/1160 +f 2148/2808/1122 2410/2904/1160 2116/2776/1091 +f 2427/2921/37 2428/2922/1176 2429/2923/1176 +f 2427/2921/37 2429/2923/1176 2430/2924/37 +f 2428/2922/1176 2431/2925/1177 2432/2926/1177 +f 2428/2922/1176 2432/2926/1177 2429/2923/1176 +f 2431/2925/1177 2433/2927/16 2434/2928/627 +f 2431/2925/1177 2434/2928/627 2432/2926/1177 +f 2435/2929/25 2436/2930/1178 2437/2931/1178 +f 2435/2929/25 2437/2931/1178 2438/2932/25 +f 2436/2930/1178 2439/2933/33 2440/2934/33 +f 2436/2930/1178 2440/2934/33 2437/2931/1178 +f 2447/2935/1179 2448/2936/1180 2449/2937/1180 +f 2447/2935/1179 2449/2937/1180 2450/2938/1181 +f 2451/2939/1182 2447/2935/1179 2450/2938/1181 +f 2451/2939/1182 2450/2938/1181 2452/2940/1182 +f 2453/2941/633 2451/2939/1182 2452/2940/1182 +f 2453/2941/633 2452/2940/1182 2454/2942/633 +f 2455/2943/1183 2456/2944/635 2457/2945/635 +f 2455/2943/1183 2457/2945/635 2458/2946/1183 +f 2459/2947/1184 2455/2943/1183 2458/2946/1183 +f 2459/2947/1184 2458/2946/1183 2460/2948/1184 +f 2448/2936/1180 2459/2947/1184 2460/2948/1184 +f 2448/2936/1180 2460/2948/1184 2449/2937/1180 +f 2553/984/36 2555/986/36 2556/2949/36 +f 2557/2950/37 2558/2951/37 2559/2952/1176 +f 2557/2950/37 2559/2952/1176 2560/2953/1176 +f 2560/2953/1176 2559/2952/1176 2561/2954/1177 +f 2560/2953/1176 2561/2954/1177 2562/2955/1177 +f 2562/2955/1177 2561/2954/1177 2563/2956/627 +f 2562/2955/1177 2563/2956/627 2564/2957/16 +f 2565/2958/25 2566/2959/25 2567/2960/1178 +f 2565/2958/25 2567/2960/1178 2568/2961/1178 +f 2568/2961/1178 2567/2960/1178 2569/2962/33 +f 2568/2961/1178 2569/2962/33 2570/2963/33 +f 2603/2964/639 2604/2965/639 2605/2966/1185 +f 2603/2964/639 2605/2966/1185 2606/2967/1185 +f 2615/2968/1004 2616/2969/627 2617/2970/16 +f 2615/2968/1004 2617/2970/16 2618/2971/1004 +f 2623/2972/1186 2624/2973/1187 2625/2974/1188 +f 2623/2972/1186 2625/2974/1188 2626/2975/1189 +f 2624/2973/1187 2627/2976/1190 2628/2977/1191 +f 2624/2973/1187 2628/2977/1191 2625/2974/1188 +f 2627/2976/1190 2629/2978/1192 2630/2979/1193 +f 2627/2976/1190 2630/2979/1193 2628/2977/1191 +f 2631/2980/1194 2632/2981/1195 2633/2982/1196 +f 2631/2980/1194 2633/2982/1196 2634/2983/1197 +f 2632/2981/1195 2635/2984/1198 2636/2985/1199 +f 2632/2981/1195 2636/2985/1199 2633/2982/1196 +f 2635/2984/1198 2637/2986/1194 2638/2987/1197 +f 2635/2984/1198 2638/2987/1197 2636/2985/1199 +f 2639/2988/653 2640/2989/653 2641/2990/654 +f 2639/2988/653 2641/2990/654 2642/2991/654 +f 2642/2991/654 2641/2990/654 2643/2992/655 +f 2642/2991/654 2643/2992/655 2644/2993/655 +f 2644/2993/655 2643/2992/655 2645/2994/656 +f 2644/2993/655 2645/2994/656 2646/2995/656 +f 2646/2995/656 2645/2994/656 2647/2996/657 +f 2646/2995/656 2647/2996/657 2648/2997/657 +f 2648/2997/657 2647/2996/657 2649/2998/658 +f 2648/2997/657 2649/2998/658 2650/2999/658 +f 2651/3000/658 2652/3001/658 2653/3002/659 +f 2651/3000/658 2653/3002/659 2654/3003/659 +f 2654/3003/659 2653/3002/659 2655/3004/660 +f 2654/3003/659 2655/3004/660 2656/3005/660 +f 2656/3005/660 2655/3004/660 2640/2989/653 +f 2656/3005/660 2640/2989/653 2639/2988/653 +f 2675/3006/1200 2676/3007/1201 2677/3008/1200 +f 2675/3006/1200 2677/3008/1200 2678/3009/1202 +f 2679/3010/1203 2680/3011/1204 2681/3012/1205 +f 2679/3010/1203 2681/3012/1205 2682/3013/1205 +f 2683/3014/1206 2684/3015/1206 2685/3016/1207 +f 2683/3014/1206 2685/3016/1207 2686/3017/1208 +f 2687/3018/1209 2688/3019/1210 2689/3020/1211 +f 2687/3018/1209 2689/3020/1211 2690/3021/1212 +f 2691/3022/1213 2692/3023/1214 2693/3024/1215 +f 2691/3022/1213 2693/3024/1215 2694/3025/1213 +f 2695/3026/1216 2696/3027/1217 2697/3028/1218 +f 2695/3026/1216 2697/3028/1218 2698/3029/1219 +f 2699/3030/1220 2700/3031/1221 2701/3032/1222 +f 2699/3030/1220 2701/3032/1222 2702/3033/1223 +f 2703/3034/1224 2704/3035/1225 2705/3036/1226 +f 2703/3034/1224 2705/3036/1226 2706/3037/1227 +f 2771/3038/27 2772/3039/27 2773/3040/1228 +f 2771/3038/27 2773/3040/1228 2774/3041/1228 +f 2774/3041/1228 2773/3040/1228 2775/3042/1229 +f 2774/3041/1228 2775/3042/1229 2776/3043/1230 +f 2777/3044/1230 2778/3045/1229 2772/3039/27 +f 2777/3044/1230 2772/3039/27 2771/3038/27 +f 2779/3046/1231 2780/3047/1232 2781/3048/1233 +f 2779/3046/1231 2781/3048/1233 2782/3049/1234 +f 2783/3050/1234 2784/3051/1235 2785/3052/1236 +f 2783/3050/1234 2785/3052/1236 2786/3053/1237 +f 2786/3053/1237 2785/3052/1236 2780/3047/1232 +f 2786/3053/1237 2780/3047/1232 2779/3046/1231 +f 2787/3054/1238 2788/3055/1239 2789/3056/1240 +f 2787/3054/1238 2789/3056/1240 2790/3057/1241 +f 2791/3058/1242 2792/3059/1243 2793/3060/1244 +f 2791/3058/1242 2793/3060/1244 2794/3061/1245 +f 2794/3061/1245 2793/3060/1244 2788/3055/1239 +f 2794/3061/1245 2788/3055/1239 2787/3054/1238 +f 2795/3062/27 2796/3063/27 2797/3064/1246 +f 2795/3062/27 2797/3064/1246 2798/3065/1246 +f 2798/3065/1246 2797/3064/1246 2799/3066/1247 +f 2798/3065/1246 2799/3066/1247 2800/3067/1248 +f 2801/3068/1249 2802/3069/1247 2796/3063/27 +f 2801/3068/1249 2796/3063/27 2795/3062/27 +f 2803/3070/1250 2804/3071/1251 2805/3072/1252 +f 2803/3070/1250 2805/3072/1252 2806/3073/1253 +f 2806/3073/1253 2805/3072/1252 2807/3074/1 +f 2806/3073/1253 2807/3074/1 2808/3075/1 +f 2808/3075/1 2807/3074/1 2809/3076/1254 +f 2808/3075/1 2809/3076/1254 2810/3077/1254 +f 2811/3078/1254 2812/3079/1255 2813/3080/1256 +f 2811/3078/1254 2813/3080/1256 2814/3081/1256 +f 2814/3081/1256 2813/3080/1256 2815/3082/1257 +f 2814/3081/1256 2815/3082/1257 2816/3083/1258 +f 2816/3083/1258 2815/3082/1257 2817/3084/3 +f 2816/3083/1258 2817/3084/3 2818/3085/3 +f 2818/3085/3 2817/3084/3 2819/3086/1259 +f 2818/3085/3 2819/3086/1259 2820/3087/1259 +f 2820/3087/1259 2819/3086/1259 2804/3071/1251 +f 2820/3087/1259 2804/3071/1251 2803/3070/1250 +f 2821/3088/1260 2822/3089/1261 2823/3090/1262 +f 2821/3088/1260 2823/3090/1262 2824/3091/1263 +f 2824/3091/1263 2823/3090/1262 2825/3092/1 +f 2824/3091/1263 2825/3092/1 2826/3093/1 +f 2827/3094/3 2828/3095/3 2829/3096/1264 +f 2827/3094/3 2829/3096/1264 2830/3097/1265 +f 2830/3097/1265 2829/3096/1264 2822/3089/1261 +f 2830/3097/1265 2822/3089/1261 2821/3088/1260 +f 2831/3098/1266 2832/3099/1266 2833/3100/1266 +f 2831/3098/1266 2833/3100/1266 2834/3101/1266 +f 2835/3102/1267 2836/3103/1267 2837/3104/1267 +f 2835/3102/1267 2837/3104/1267 2838/3105/1267 +f 2839/3106/1268 2840/3107/1268 2841/3108/1269 +f 2839/3106/1268 2841/3108/1269 2842/3109/1268 +f 2843/3110/1270 2844/3111/1271 2845/3112/1270 +f 2843/3110/1270 2845/3112/1270 2846/3113/1271 +f 2851/1117/124 2853/1119/124 2854/3114/124 +f 2863/3115/27 2864/3116/27 2865/3117/36 +f 2863/3115/27 2865/3117/36 2866/3118/36 +f 2866/3118/36 2865/3117/36 2867/3119/16 +f 2866/3118/36 2867/3119/16 2868/3120/16 +f 2868/3120/16 2867/3119/16 2869/3121/42 +f 2868/3120/16 2869/3121/42 2870/3122/42 +f 2870/3122/42 2869/3121/42 2871/3123/26 +f 2870/3122/42 2871/3123/26 2872/3124/26 +f 2872/3124/26 2871/3123/26 2873/3125/39 +f 2872/3124/26 2873/3125/39 2874/3126/731 +f 2875/3127/729 2876/3128/39 2877/3129/25 +f 2875/3127/729 2877/3129/25 2878/3130/25 +f 2878/3130/25 2877/3129/25 2879/3131/33 +f 2878/3130/25 2879/3131/33 2880/3132/33 +f 2880/3132/33 2879/3131/33 2864/3116/27 +f 2880/3132/33 2864/3116/27 2863/3115/27 +f 2883/3133/27 2884/3134/27 2885/3135/36 +f 2883/3133/27 2885/3135/36 2886/3136/36 +f 2886/3136/36 2885/3135/36 2887/3137/16 +f 2886/3136/36 2887/3137/16 2888/3138/16 +f 2888/3138/16 2887/3137/16 2889/3139/42 +f 2888/3138/16 2889/3139/42 2890/3140/42 +f 2890/3140/42 2889/3139/42 2891/3141/26 +f 2890/3140/42 2891/3141/26 2892/3142/26 +f 2892/3142/26 2891/3141/26 2893/3143/39 +f 2892/3142/26 2893/3143/39 2894/3144/39 +f 2894/3144/39 2893/3143/39 2895/3145/25 +f 2894/3144/39 2895/3145/25 2896/3146/25 +f 2896/3146/25 2895/3145/25 2897/3147/33 +f 2896/3146/25 2897/3147/33 2898/3148/1272 +f 2899/3149/727 2900/3150/33 2884/3134/27 +f 2899/3149/727 2884/3134/27 2883/3133/27 +f 2903/3151/1273 2904/3152/1274 2905/3153/1275 +f 2903/3151/1273 2905/3153/1275 2906/3154/1276 +f 2907/3155/1277 2908/3156/1278 2904/3152/1274 +f 2907/3155/1277 2904/3152/1274 2903/3151/1273 +f 2909/3157/1279 2910/3158/1280 2908/3156/1278 +f 2909/3157/1279 2908/3156/1278 2907/3155/1277 +f 2911/3159/1281 2912/3160/1282 2910/3158/1280 +f 2911/3159/1281 2910/3158/1280 2909/3157/1279 +f 2913/3161/1283 2903/3151/1273 2906/3154/1276 +f 2913/3161/1283 2906/3154/1276 2914/3162/1284 +f 2915/3163/1283 2907/3155/1277 2903/3151/1273 +f 2915/3163/1283 2903/3151/1273 2913/3161/1283 +f 2916/3164/1283 2909/3157/1279 2907/3155/1277 +f 2916/3164/1283 2907/3155/1277 2915/3163/1283 +f 2916/3164/1283 2917/3165/1283 2911/3159/1281 +f 2916/3164/1283 2911/3159/1281 2909/3157/1279 +f 2918/3166/1285 2919/3167/1286 2920/3168/1287 +f 2918/3166/1285 2920/3168/1287 2921/3169/1288 +f 2922/3170/1289 2923/3171/1286 2919/3167/1286 +f 2922/3170/1289 2919/3167/1286 2918/3166/1285 +f 2922/3170/1289 2924/3172/1290 2925/3173/1286 +f 2922/3170/1289 2925/3173/1286 2923/3171/1286 +f 2924/3172/1290 2926/3174/1291 2927/3175/1286 +f 2924/3172/1290 2927/3175/1286 2925/3173/1286 +f 2928/3176/1292 2929/3177/1293 2930/3178/1294 +f 2928/3176/1292 2930/3178/1294 2931/3179/1295 +f 2929/3177/1293 2932/3180/1296 2933/3181/1297 +f 2929/3177/1293 2933/3181/1297 2930/3178/1294 +f 2932/3180/1296 2934/3182/1298 2935/3183/1299 +f 2932/3180/1296 2935/3183/1299 2933/3181/1297 +f 2936/3184/1300 2937/3185/1301 2935/3183/1299 +f 2936/3184/1300 2935/3183/1299 2934/3182/1298 +f 2911/3159/1281 2938/3186/1302 2939/3187/1303 +f 2911/3159/1281 2939/3187/1303 2912/3160/1282 +f 2917/3165/1283 2940/3188/1304 2938/3186/1302 +f 2917/3165/1283 2938/3186/1302 2911/3159/1281 +f 2927/3175/1286 2941/3189/1305 2940/3188/1304 +f 2927/3175/1286 2940/3188/1304 2917/3165/1283 +f 2926/3174/1291 2937/3185/1301 2942/3190/1306 +f 2926/3174/1291 2942/3190/1306 2943/3191/1307 +f 2931/3179/1295 2930/3178/1294 2918/3166/1285 +f 2931/3179/1295 2918/3166/1285 2921/3169/1288 +f 2930/3178/1294 2933/3181/1297 2922/3170/1289 +f 2930/3178/1294 2922/3170/1289 2918/3166/1285 +f 2933/3181/1297 2935/3183/1299 2924/3172/1290 +f 2933/3181/1297 2924/3172/1290 2922/3170/1289 +f 2937/3185/1301 2926/3174/1291 2924/3172/1290 +f 2937/3185/1301 2924/3172/1290 2935/3183/1299 +f 2937/3185/1301 2936/3184/1300 2944/3192/1308 +f 2937/3185/1301 2944/3192/1308 2942/3190/1306 +f 2919/3167/1286 2913/3161/1283 2914/3162/1284 +f 2919/3167/1286 2914/3162/1284 2920/3168/1287 +f 2923/3171/1286 2915/3163/1283 2913/3161/1283 +f 2923/3171/1286 2913/3161/1283 2919/3167/1286 +f 2923/3171/1286 2925/3173/1286 2916/3164/1283 +f 2923/3171/1286 2916/3164/1283 2915/3163/1283 +f 2925/3173/1286 2927/3175/1286 2917/3165/1283 +f 2925/3173/1286 2917/3165/1283 2916/3164/1283 +f 2941/3189/1305 2927/3175/1286 2926/3174/1291 +f 2941/3189/1305 2926/3174/1291 2943/3191/1307 +f 2949/3193/1309 2950/3194/1310 2951/3195/1311 +f 2949/3193/1309 2951/3195/1311 2952/3196/1312 +f 2953/3197/1313 2954/3198/1314 2955/3199/1314 +f 2953/3197/1313 2955/3199/1314 2956/3200/1314 +f 2957/1136/128 2959/1138/128 2960/3201/128 +f 2965/3202/1315 2966/3203/1316 2967/3204/1317 +f 2965/3202/1315 2967/3204/1317 2968/3205/1316 +f 2969/3206/1318 2970/3207/1318 2971/3208/1318 +f 2969/3206/1318 2971/3208/1318 2972/3209/1318 +f 2973/3210/1319 2974/3211/1319 2975/3212/1320 +f 2973/3210/1319 2975/3212/1320 2976/3213/1319 +f 2977/3214/1321 2978/3215/1321 2979/3216/1321 +f 2977/3214/1321 2979/3216/1321 2980/3217/1321 +f 2981/3218/1322 2982/3219/1323 2983/3220/1323 +f 2981/3218/1322 2983/3220/1323 2984/3221/1322 +f 2985/3222/1324 2986/3223/1324 2987/3224/1324 +f 2985/3222/1324 2987/3224/1324 2988/3225/1324 +f 2989/3226/1325 2990/3227/1325 2991/3228/1325 +f 2989/3226/1325 2991/3228/1325 2992/3229/1325 +f 2993/3230/1326 2994/3231/1326 2995/3232/1326 +f 2993/3230/1326 2995/3232/1326 2996/3233/1326 +f 2997/3234/1327 2998/3235/1328 2999/3236/1328 +f 2997/3234/1327 2999/3236/1328 3000/3237/1328 +f 3001/3238/1329 3002/3239/1330 3003/3240/1330 +f 3001/3238/1329 3003/3240/1330 3004/3241/1330 +f 3005/3242/1331 3006/3243/1331 3007/3244/1331 +f 3005/3242/1331 3007/3244/1331 3008/3245/1331 +f 3009/3246/1332 3010/3247/1332 3011/3248/1332 +f 3009/3246/1332 3011/3248/1332 3012/3249/1332 +f 3013/3250/1333 3014/3251/1333 3015/3252/1334 +f 3013/3250/1333 3015/3252/1334 3016/3253/1334 +f 3017/3254/1335 3018/3255/1335 3019/3256/1336 +f 3017/3254/1335 3019/3256/1336 3020/3257/1335 +f 3021/3258/1337 3022/3259/1338 3023/3260/1339 +f 3021/3258/1337 3023/3260/1339 3024/3261/1338 +f 3025/3262/1340 3026/3263/1340 3027/3264/1341 +f 3025/3262/1340 3027/3264/1341 3028/3265/1342 +f 3029/3266/1343 3030/3267/1344 3031/3268/1343 +f 3029/3266/1343 3031/3268/1343 3032/3269/1343 +f 3037/3270/1345 3038/3271/1345 3039/3272/1346 +f 3037/3270/1345 3039/3272/1346 3040/3273/1347 +f 3041/1147/1348 3043/1149/1349 3044/3274/1349 +f 3053/3275/1350 3054/3276/1350 3055/3277/1350 +f 3053/3275/1350 3055/3277/1350 3056/3278/1350 +f 3061/3279/1351 3062/3280/1352 3063/3281/1351 +f 3061/3279/1351 3063/3281/1351 3064/3282/1351 +f 3065/3283/1353 3066/3284/1354 3067/3285/1354 +f 3065/3283/1353 3067/3285/1354 3068/3286/1353 +f 3069/3287/1355 3070/3288/1356 3071/3289/1355 +f 3069/3287/1355 3071/3289/1355 3072/3290/1355 +f 3077/3291/1357 3078/3292/1357 3079/3293/1357 +f 3077/3291/1357 3079/3293/1357 3080/3294/1357 +f 3085/3295/1358 3086/3296/1359 3087/3297/1360 +f 3085/3295/1358 3087/3297/1360 3088/3298/1359 +f 3089/3299/1361 3090/3300/1362 3091/3301/1361 +f 3089/3299/1361 3091/3301/1361 3092/3302/1361 +f 3093/3303/1363 3094/3304/1363 3095/3305/1363 +f 3093/3303/1363 3095/3305/1363 3096/3306/1363 +f 3097/3307/1364 3098/3308/1364 3099/3309/1365 +f 3097/3307/1364 3099/3309/1365 3100/3310/1364 +f 3101/3311/1366 3102/3312/1367 3103/3313/1366 +f 3101/3311/1366 3103/3313/1366 3104/3314/1367 +f 3189/3315/1368 3190/3316/1369 3191/3317/1370 +f 3189/3315/1368 3191/3317/1370 3192/3318/1369 +f 2152/2812/1126 3189/3315/1368 3192/3318/1369 +f 2152/2812/1126 3192/3318/1369 2153/2813/1127 +f 3190/3316/1369 3193/1254/89 3194/1257/89 +f 3190/3316/1369 3194/1257/89 3191/3317/1370 +f 2151/2811/1125 2150/2810/1124 3195/3319/1371 +f 2151/2811/1125 3195/3319/1371 3196/3320/1371 +f 3205/3321/1089 3206/1264/84 3207/1265/84 +f 3205/3321/1089 3207/1265/84 3208/3322/1088 +f 2112/2772/1088 3205/3321/1089 3208/3322/1088 +f 2112/2772/1088 3208/3322/1088 2113/2773/1089 +f 2114/2774/1090 3209/3323/1372 3210/3324/1373 +f 2114/2774/1090 3210/3324/1373 2115/2775/1090 +f 3209/3323/1372 3211/3325/1374 3212/3326/1374 +f 3209/3323/1372 3212/3326/1374 3210/3324/1373 +f 3221/3327/833 3222/3328/833 1823/2568/972 +f 3221/3327/833 1823/2568/972 1822/2567/971 +f 1748/2504/917 3223/3329/917 3224/3330/918 +f 1748/2504/917 3224/3330/918 1752/2508/1375 +f 3253/3331/836 3254/3332/1376 3255/3333/1376 +f 3253/3331/836 3255/3333/1376 3256/3334/836 +f 3257/3335/25 3258/3336/1005 3259/3337/1005 +f 3257/3335/25 3259/3337/1005 3260/3338/837 +f 3285/3339/1377 3286/3340/1378 3287/3341/1378 +f 3285/3339/1377 3287/3341/1378 3288/3342/1377 +f 3286/3340/1378 3289/3343/1379 3290/3344/1379 +f 3286/3340/1378 3290/3344/1379 3287/3341/1378 +f 3289/3343/1379 3291/3345/1380 3292/3346/1380 +f 3289/3343/1379 3292/3346/1380 3290/3344/1379 +f 3301/3347/26 3302/3348/1381 3303/3349/1381 +f 3301/3347/26 3303/3349/1381 3304/3350/26 +f 3302/3348/1381 3305/3351/16 3306/3352/16 +f 3302/3348/1381 3306/3352/16 3303/3349/1381 +f 3305/3351/16 3307/3353/1382 3308/3354/1382 +f 3305/3351/16 3308/3354/1382 3306/3352/16 +f 3307/3353/1382 3309/3355/27 3310/3356/27 +f 3307/3353/1382 3310/3356/27 3308/3354/1382 +f 3309/3355/27 3311/3357/33 3312/3358/33 +f 3309/3355/27 3312/3358/33 3310/3356/27 +f 3311/3357/33 3313/3359/837 3314/3360/837 +f 3311/3357/33 3314/3360/837 3312/3358/33 +f 3313/3359/837 3315/3361/39 3316/3362/39 +f 3313/3359/837 3316/3362/39 3314/3360/837 +f 3315/3361/39 3301/3347/26 3304/3350/26 +f 3315/3361/39 3304/3350/26 3316/3362/39 +f 3331/3363/687 3332/3364/688 3333/3365/689 +f 3331/3363/687 3333/3365/689 3334/3366/690 +f 3335/3367/688 3336/3368/691 3337/3369/692 +f 3335/3367/688 3337/3369/692 3338/3370/693 +f 3336/3368/691 3331/3363/687 3334/3366/690 +f 3336/3368/691 3334/3366/690 3337/3369/692 +f 3339/3371/694 3340/3372/695 3341/3373/696 +f 3339/3371/694 3341/3373/696 3342/3374/697 +f 3343/3375/698 3344/3376/699 3345/3377/700 +f 3343/3375/698 3345/3377/700 3346/3378/701 +f 3344/3376/699 3339/3371/694 3342/3374/697 +f 3344/3376/699 3342/3374/697 3345/3377/700 +f 3347/3379/706 3348/3380/707 3349/3381/707 +f 3347/3379/706 3349/3381/707 3350/3382/709 +f 3348/3380/707 3351/3383/1 3352/3384/1 +f 3348/3380/707 3352/3384/1 3349/3381/707 +f 3351/3383/1 3353/3385/710 3354/3386/710 +f 3351/3383/1 3354/3386/710 3352/3384/1 +f 3355/3387/710 3356/3388/711 3357/3389/1383 +f 3355/3387/710 3357/3389/1383 3358/3390/710 +f 3356/3388/711 3359/3391/713 3360/3392/712 +f 3356/3388/711 3360/3392/712 3357/3389/1383 +f 3359/3391/713 3361/3393/3 3362/3394/3 +f 3359/3391/713 3362/3394/3 3360/3392/712 +f 3361/3393/3 3363/3395/714 3364/3396/714 +f 3361/3393/3 3364/3396/714 3362/3394/3 +f 3363/3395/714 3347/3379/706 3350/3382/709 +f 3363/3395/714 3350/3382/709 3364/3396/714 +f 3381/3397/1384 3382/3398/1385 3383/3399/1386 +f 3381/3397/1384 3383/3399/1386 3384/3400/1387 +f 3382/3398/1385 3385/3401/1388 3386/3402/1389 +f 3382/3398/1385 3386/3402/1389 3383/3399/1386 +f 3387/3403/1390 3381/3397/1384 3384/3400/1387 +f 3387/3403/1390 3384/3400/1387 3388/3404/1391 +f 3405/3405/1231 3406/3406/1232 3407/3407/1233 +f 3405/3405/1231 3407/3407/1233 3408/3408/1234 +f 3409/3409/1234 3410/3410/1235 3411/3411/1392 +f 3409/3409/1234 3411/3411/1392 3412/3412/1237 +f 3412/3412/1237 3411/3411/1392 3406/3406/1232 +f 3412/3412/1237 3406/3406/1232 3405/3405/1231 +f 3413/3413/1238 3414/3414/1239 3415/3415/1240 +f 3413/3413/1238 3415/3415/1240 3416/3416/1241 +f 3417/3417/1242 3418/3418/1243 3419/3419/1244 +f 3417/3417/1242 3419/3419/1244 3420/3420/1245 +f 3420/3420/1245 3419/3419/1244 3414/3414/1239 +f 3420/3420/1245 3414/3414/1239 3413/3413/1238 +f 3421/3421/1251 3422/3422/1251 3423/3423/1252 +f 3421/3421/1251 3423/3423/1252 3424/3424/1253 +f 3424/3424/1253 3423/3423/1252 3425/3425/1 +f 3424/3424/1253 3425/3425/1 3426/3426/1 +f 3426/3426/1 3425/3425/1 3427/3427/1254 +f 3426/3426/1 3427/3427/1254 3428/3428/1254 +f 3429/3429/1393 3430/3430/1254 3431/3431/1394 +f 3429/3429/1393 3431/3431/1394 3432/3432/1256 +f 3432/3432/1256 3431/3431/1394 3433/3433/1257 +f 3432/3432/1256 3433/3433/1257 3434/3434/1257 +f 3434/3434/1257 3433/3433/1257 3435/3435/3 +f 3434/3434/1257 3435/3435/3 3436/3436/3 +f 3436/3436/3 3435/3435/3 3437/3437/1259 +f 3436/3436/3 3437/3437/1259 3438/3438/1259 +f 3438/3438/1259 3437/3437/1259 3422/3422/1251 +f 3438/3438/1259 3422/3422/1251 3421/3421/1251 +f 3439/3439/27 3440/3440/27 3441/3441/1395 +f 3439/3439/27 3441/3441/1395 3442/3442/1395 +f 3442/3442/1395 3441/3441/1395 3443/3443/1396 +f 3442/3442/1395 3443/3443/1396 3444/3444/1397 +f 3445/3445/1397 3446/3446/1396 3440/3440/27 +f 3445/3445/1397 3440/3440/27 3439/3439/27 diff --git a/examples/models/yaw_pitch_roll/plane_diffuse.png b/examples/models/yaw_pitch_roll/plane_diffuse.png new file mode 100644 index 0000000..07371c0 Binary files /dev/null and b/examples/models/yaw_pitch_roll/plane_diffuse.png differ diff --git a/examples/others/android/example/androidcompile.bat b/examples/others/android/example/androidcompile.bat index ba18ba2..e45f342 100644 --- a/examples/others/android/example/androidcompile.bat +++ b/examples/others/android/example/androidcompile.bat @@ -20,12 +20,12 @@ ) :COMPILE - @echo compiling for platform %FL% + @echo compiling for platform %FL% and architecture %GOARCH% @set CGO_CFLAGS="-I%ANDROID_SYSROOT%/usr/include -I%ANDROID_SYSROOT%/usr/include/%TRIPLE% --sysroot=%ANDROID_SYSROOT% -D__ANDROID_API__=%ANDROID_API%" @set CGO_LDFLAGS="-L%ANDROID_SYSROOT%/usr/lib/%TRIPLE%/%ANDROID_API% -L%ANDROID_TOOLCHAIN%/%TRIPLE%/lib --sysroot=%ANDROID_SYSROOT%" @set CGO_ENABLED=1 @set GOOS=android - @set GOARCH=arm + @set GOARCH=%GOARCH% @go build -buildmode=c-shared -ldflags="-s -w -extldflags=-Wl,-soname,lib%LIBRARY_NAME%.so" -o=android/libs/%FL%/lib%LIBRARY_NAME%.so @EXIT /B @@ -48,21 +48,21 @@ @set GOARCH=arm @CALL:COMPILE ) @IF %TARGET_ARCH% == "arm64-v8a" ( - @set CC="armv7a-linux-androideabi%ANDROID_API%-clang" + @set CC="aarch64-linux-android%ANDROID_API%-clang" @set TRIPLE=aarch64-linux-android @set FL=arm64-v8a @set GOARCH=arm64 @CALL:COMPILE ) @IF %TARGET_ARCH% == "x86" ( - @set CC="armv7a-linux-androideabi%ANDROID_API%-clang" + @set CC="i686-linux-android%ANDROID_API%-clang" @set TRIPLE=i686-linux-android @set FL=x86 - @set GOARCH=arm + @set GOARCH=386 @CALL:COMPILE ) @IF %TARGET_ARCH% == "x86_64" ( - @set CC="armv7a-linux-androideabi%ANDROID_API%-clang" + @set CC="x86_64-linux-android%ANDROID_API%-clang" @set TRIPLE=x86_64-linux-android @set FL=x86_64 - @set GOARCH=arm64 + @set GOARCH=amd64 @CALL:COMPILE ) @EXIT /B diff --git a/examples/others/android/example/main.go b/examples/others/android/example/main.go index dc2d7a6..bfcfaa2 100644 --- a/examples/others/android/example/main.go +++ b/examples/others/android/example/main.go @@ -4,7 +4,7 @@ import ( "os" "runtime" - "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/raylib" ) // Game states diff --git a/examples/others/bunnymark/main.go b/examples/others/bunnymark/main.go index d7f1b43..cb86aa2 100644 --- a/examples/others/bunnymark/main.go +++ b/examples/others/bunnymark/main.go @@ -3,9 +3,13 @@ package main import ( "fmt" - "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) +// This is the maximum amount of elements (quads) per batch +// NOTE: This value is defined in [rlgl] module and can be changed there +const maxBatchElements = 8192 + // Bunny type type Bunny struct { Position rl.Vector2 @@ -14,15 +18,14 @@ type Bunny struct { } func main() { - screenWidth := int32(1280) - screenHeight := int32(960) + screenWidth := int32(800) + screenHeight := int32(450) - rl.InitWindow(screenWidth, screenHeight, "raylib [core] example - Bunnymark") + rl.InitWindow(screenWidth, screenHeight, "raylib [others] example - bunnymark") texture := rl.LoadTexture("wabbit_alpha.png") bunnies := make([]*Bunny, 0) - bunniesCount := 0 rl.SetTargetFPS(60) @@ -33,11 +36,11 @@ func main() { for i := 0; i < 100; i++ { b := &Bunny{} b.Position = rl.GetMousePosition() - b.Speed.X = float32(rl.GetRandomValue(250, 500)) / 60.0 - b.Speed.Y = float32(rl.GetRandomValue(250, 500)-500) / 60.0 + b.Speed.X = float32(rl.GetRandomValue(-250, 250)) / 60.0 + b.Speed.Y = float32(rl.GetRandomValue(-250, 250)) / 60.0 + b.Color = rl.NewColor(uint8(rl.GetRandomValue(50, 240)), uint8(rl.GetRandomValue(80, 240)), uint8(rl.GetRandomValue(100, 240)), 255) bunnies = append(bunnies, b) - bunniesCount++ } } @@ -46,11 +49,11 @@ func main() { b.Position.X += b.Speed.X b.Position.Y += b.Speed.Y - if (b.Position.X > float32(screenWidth)) || (b.Position.X < 0) { + if ((b.Position.X + float32(texture.Width/2)) > float32(screenWidth)) || ((b.Position.X + float32(texture.Width/2)) < 0) { b.Speed.X *= -1 } - if (b.Position.Y > float32(screenHeight)) || (b.Position.Y < 0) { + if ((b.Position.Y + float32(texture.Height/2)) > float32(screenHeight)) || ((b.Position.Y + float32(texture.Height/2-40)) < 0) { b.Speed.Y *= -1 } } @@ -60,18 +63,20 @@ func main() { rl.ClearBackground(rl.RayWhite) for _, b := range bunnies { - // NOTE: When internal QUADS batch limit is reached, a draw call is launched and - // batching buffer starts being filled again; before launching the draw call, - // updated vertex data from internal buffer is send to GPU... it seems it generates - // a stall and consequently a frame drop, limiting number of bunnies drawn at 60 fps - rl.DrawTexture(texture, int32(b.Position.X), int32(b.Position.Y), rl.RayWhite) + // NOTE: When internal batch buffer limit is reached (MAX_BATCH_ELEMENTS), + // a draw call is launched and buffer starts being filled again; + // before issuing a draw call, updated vertex data from internal CPU buffer is send to GPU... + // Process of sending data is costly and it could happen that GPU data has not been completely + // processed for drawing while new data is tried to be sent (updating current in-use buffers) + // it could generates a stall and consequently a frame drop, limiting the number of drawn bunnies + rl.DrawTexture(texture, int32(b.Position.X), int32(b.Position.Y), b.Color) } - rl.DrawRectangle(0, 0, screenWidth, 40, rl.LightGray) - rl.DrawText("raylib bunnymark", 10, 10, 20, rl.DarkGray) - rl.DrawText(fmt.Sprintf("bunnies: %d", bunniesCount), 400, 10, 20, rl.Red) + rl.DrawRectangle(0, 0, screenWidth, 40, rl.Black) + rl.DrawText(fmt.Sprintf("bunnies: %d", len(bunnies)), 120, 10, 20, rl.Green) + rl.DrawText(fmt.Sprintf("batched draw calls: %d", 1+len(bunnies)/maxBatchElements), 320, 10, 20, rl.Maroon) - rl.DrawFPS(260, 10) + rl.DrawFPS(10, 10) rl.EndDrawing() } diff --git a/examples/others/utils/utils.go b/examples/others/utils/utils.go index 6829333..97881fd 100644 --- a/examples/others/utils/utils.go +++ b/examples/others/utils/utils.go @@ -3,7 +3,7 @@ package main import ( "log" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/physics/box2d/main.go b/examples/physics/box2d/main.go index d0e61b6..d2da34e 100644 --- a/examples/physics/box2d/main.go +++ b/examples/physics/box2d/main.go @@ -4,8 +4,6 @@ import ( "math" "math/rand" - "github.com/gen2brain/raylib-go/raylib" - box2d "github.com/neguse/go-box2d-lite/box2dlite" ) diff --git a/examples/physics/chipmunk/main.go b/examples/physics/chipmunk/main.go index ac6c911..e304182 100644 --- a/examples/physics/chipmunk/main.go +++ b/examples/physics/chipmunk/main.go @@ -5,7 +5,7 @@ import ( "math" "math/rand" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" "github.com/jakecoffman/cp" ) diff --git a/examples/physics/physac/demo/main.go b/examples/physics/physac/demo/main.go index c4ff126..8c64634 100644 --- a/examples/physics/physac/demo/main.go +++ b/examples/physics/physac/demo/main.go @@ -1,8 +1,8 @@ package main import ( - "github.com/gen2brain/raylib-go/physics" - rl "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/physics" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/physics/physac/friction/main.go b/examples/physics/physac/friction/main.go index 6eed272..b384162 100644 --- a/examples/physics/physac/friction/main.go +++ b/examples/physics/physac/friction/main.go @@ -1,8 +1,8 @@ package main import ( - "github.com/gen2brain/raylib-go/physics" - "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/physics" + "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/physics/physac/movement/main.go b/examples/physics/physac/movement/main.go index 164b62f..0962a67 100644 --- a/examples/physics/physac/movement/main.go +++ b/examples/physics/physac/movement/main.go @@ -1,8 +1,7 @@ package main import ( - "github.com/gen2brain/raylib-go/physics" - "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/physics" ) const ( diff --git a/examples/physics/physac/restitution/main.go b/examples/physics/physac/restitution/main.go index 0c069b1..4658e40 100644 --- a/examples/physics/physac/restitution/main.go +++ b/examples/physics/physac/restitution/main.go @@ -1,8 +1,8 @@ package main import ( - "github.com/gen2brain/raylib-go/physics" - "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/physics" + "git.terah.dev/UnrealXR/raylib-go/raylib" ) const ( diff --git a/examples/physics/physac/shatter/main.go b/examples/physics/physac/shatter/main.go index f2ae1cd..264a1a5 100644 --- a/examples/physics/physac/shatter/main.go +++ b/examples/physics/physac/shatter/main.go @@ -1,8 +1,8 @@ package main import ( - "github.com/gen2brain/raylib-go/physics" - rl "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/physics" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) const ( diff --git a/examples/shaders/basic_lighting/light.go b/examples/shaders/basic_lighting/light.go index 055fe26..c398de0 100644 --- a/examples/shaders/basic_lighting/light.go +++ b/examples/shaders/basic_lighting/light.go @@ -4,7 +4,7 @@ import ( "fmt" "unsafe" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) type LightType int32 diff --git a/examples/shaders/basic_lighting/main.go b/examples/shaders/basic_lighting/main.go index a65b4cb..29c7d13 100644 --- a/examples/shaders/basic_lighting/main.go +++ b/examples/shaders/basic_lighting/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/shaders/custom_uniform/main.go b/examples/shaders/custom_uniform/main.go index cb6b91a..82c10f5 100644 --- a/examples/shaders/custom_uniform/main.go +++ b/examples/shaders/custom_uniform/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/shaders/eratosthenes/main.go b/examples/shaders/eratosthenes/main.go index c813e7e..62b070f 100644 --- a/examples/shaders/eratosthenes/main.go +++ b/examples/shaders/eratosthenes/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/shaders/mesh_instancing/light.go b/examples/shaders/mesh_instancing/light.go index 055fe26..c398de0 100644 --- a/examples/shaders/mesh_instancing/light.go +++ b/examples/shaders/mesh_instancing/light.go @@ -4,7 +4,7 @@ import ( "fmt" "unsafe" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) type LightType int32 diff --git a/examples/shaders/mesh_instancing/main.go b/examples/shaders/mesh_instancing/main.go index ee0080e..ce5862c 100644 --- a/examples/shaders/mesh_instancing/main.go +++ b/examples/shaders/mesh_instancing/main.go @@ -4,7 +4,7 @@ import ( "fmt" "math" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) const MAX_INSTANCES = 100000 diff --git a/examples/shaders/model_shader/main.go b/examples/shaders/model_shader/main.go index 2dc72b4..8c13f79 100644 --- a/examples/shaders/model_shader/main.go +++ b/examples/shaders/model_shader/main.go @@ -3,7 +3,7 @@ package main import ( "fmt" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/shaders/multi_sample2d/main.go b/examples/shaders/multi_sample2d/main.go index 6ebbc03..b85f5ff 100644 --- a/examples/shaders/multi_sample2d/main.go +++ b/examples/shaders/multi_sample2d/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/shaders/pbr/glsl330/pbr.fs b/examples/shaders/pbr/glsl330/pbr.fs new file mode 100644 index 0000000..0448073 --- /dev/null +++ b/examples/shaders/pbr/glsl330/pbr.fs @@ -0,0 +1,296 @@ +#version 330 + +#define MAX_LIGHTS 4 +#define LIGHT_DIRECTIONAL 0 +#define LIGHT_POINT 1 +#define LIGHT_SPOT 2 +#define PI 3.14159265358979323846 + +struct Light{ + int enabled; + int type; + float enargy; + float cutOff ; + float outerCutOff; + float constant; + float linear ; + float quadratic ; + float shiny ; + float specularStr; + vec3 position; + vec3 direction; + vec3 lightColor; +}; + +// Input vertex attributes (from vertex shader) +in vec3 fragPosition; +in vec2 fragTexCoord; +in vec4 fragColor; +in vec3 fragNormal; +in vec4 shadowPos; +in mat3 TBN; + +// Output fragment color +out vec4 finalColor; +// mask +uniform sampler2D mask; +uniform int frame; +// Input uniform values +uniform int numOfLights = 4; +uniform sampler2D albedoMap; +uniform sampler2D mraMap; +uniform sampler2D normalMap; +uniform sampler2D emissiveMap; // r: Hight g:emissive +// Input uniform values +uniform sampler2D texture0; +uniform sampler2D texture1; +uniform sampler2D flashlight; + +uniform vec4 colDiffuse; + +uniform vec2 tiling = vec2(0.5); +uniform vec2 offset ; +uniform vec2 tilingFlashlight = vec2(0.5); +uniform vec2 offsetFlashlight ; + +uniform int useTexAlbedo =1; +uniform int useTexNormal = 0; +uniform int useTexMRA =1; +uniform int useTexEmissive =1; + +uniform vec4 albedoColor ; +uniform vec4 emissiveColor ; +uniform float normalValue =0.5; +uniform float metallicValue =0.4; +uniform float roughnessValue =0; +uniform float aoValue =0.8; +uniform float emissivePower ; + +// Input lighting values +uniform Light lights[MAX_LIGHTS]; +uniform vec3 viewPos; + +uniform vec3 ambientColor = vec3(1,1,1); +uniform float ambientStrength = 0.2; +uniform float ambient = 0.03; +uniform float fogDensity; + +vec3 CalcDirLight(Light light,vec3 normal,vec3 viewDir,vec3 albedo,vec3 baseRefl,float roughness,float metallic); +vec3 CalcPointLight(Light light,vec3 normal,vec3 viewDir,vec3 albedo,vec3 baseRefl,float roughness,float metallic); +vec3 CalcSpotLight(Light light,vec3 normal,vec3 viewDir,vec3 albedo,vec3 baseRefl,float roughness,float metallic); + +// Reflectivity in range 0.0 to 1.0 +// NOTE: Reflectivity is increased when surface view at larger angle +vec3 SchlickFresnel(float hDotV,vec3 refl) +{ + return refl + (1.0 - refl)*pow(1.0 - hDotV, 5.0); +} + +float GgxDistribution(float nDotH,float roughness) +{ + float a = roughness * roughness * roughness * roughness; + float d = nDotH * nDotH * (a - 1.0) + 1.0; + d = PI * d * d; + return a / max(d,0.0000001); +} + +float GeomSmith(float nDotV,float nDotL,float roughness) +{ + float r = roughness + 1.0; + float k = r*r / 8.0; + float ik = 1.0 - k; + float ggx1 = nDotV/(nDotV*ik + k); + float ggx2 = nDotL/(nDotL*ik + k); + return ggx1*ggx2; +} + +vec3 ComputePBR() +{ + vec3 albedo = texture(albedoMap,vec2(fragTexCoord.x*tiling.x + offset.x, fragTexCoord.y*tiling.y + offset.y)).rgb; + albedo = vec3(albedoColor.x*albedo.x, albedoColor.y*albedo.y, albedoColor.z*albedo.z); + + float metallic = clamp(metallicValue, 0.0, 1.0); + float roughness = clamp(roughnessValue, 0.0, 1.0); + float ao = clamp(aoValue, 0.0, 1.0); + + if (useTexMRA == 1) + { + vec4 mra = texture(mraMap, vec2(fragTexCoord.x*tiling.x + offset.x, fragTexCoord.y*tiling.y + offset.y))*useTexMRA; + metallic = clamp(mra.r + metallicValue, 0.04, 1.0); + roughness = clamp(mra.g + roughnessValue, 0.04, 1.0); + ao = (mra.b + aoValue)*0.5; + } + + vec3 N = normalize(fragNormal); + if (useTexNormal == 1) + { + N = texture(normalMap, vec2(fragTexCoord.x*tiling.x + offset.y, fragTexCoord.y*tiling.y + offset.y)).rgb; + N = normalize(N*2.0 - 1.0); + N = normalize(N*TBN); + } + + vec3 V = normalize(viewPos - fragPosition); + + vec3 emissive = vec3(0); + emissive = (texture(emissiveMap, vec2(fragTexCoord.x*tiling.x+offset.x, fragTexCoord.y*tiling.y+offset.y)).rgb).g * emissiveColor.rgb*emissivePower * useTexEmissive; + + // return N;//vec3(metallic,metallic,metallic); + // if dia-electric use base reflectivity of 0.04 otherwise ut is a metal use albedo as base reflectivity + vec3 baseRefl = mix(vec3(0.04), albedo.rgb, metallic); + vec3 lightAccum = vec3(0.0); // Acumulate lighting lum + + vec3 norm=N; + vec3 viewDir=V; + vec3 result = vec3(0.0); + + for (int i = 0; i < MAX_LIGHTS; i++){ + + if(lights[i].enabled == 1){ + + if(lights[i].type == LIGHT_DIRECTIONAL){ + result += CalcDirLight(lights[i],norm,viewDir,albedo,baseRefl,roughness,metallic); + } + + if(lights[i].type == LIGHT_POINT){ + result += CalcPointLight(lights[i],norm,viewDir,albedo,baseRefl,roughness,metallic); + } + + if(lights[i].type == LIGHT_SPOT){ + result += CalcSpotLight(lights[i],norm,viewDir,albedo,baseRefl,roughness,metallic); + } + + } + + } + + vec3 ambientFinal = (ambientColor + albedo)*ambient*0.5; + + return ambientFinal+result*ao + emissive; + +} + + +void main() +{ + vec3 color = ComputePBR(); + + // HDR tonemapping + color = pow(color, color + vec3(1.0)); + + // // Gamma correction + // color = pow(color, vec3(1.0/2.5)); + + // Fog calculation + float dist = length(viewPos - fragPosition); + + // these could be parameters... + const vec4 fogColor = vec4(0.5, 0.5, 0.5, 1.0); + + + // Exponential fog + float fogFactor = 1.0/exp((dist*fogDensity)*(dist*fogDensity)); + + + fogFactor = clamp(fogFactor, 0.0, 1.0); + + finalColor = mix(fogColor,vec4(color,1.0), fogFactor); +} + + + +vec3 CalcDirLight(Light light,vec3 normal,vec3 viewDir,vec3 albedo,vec3 baseRefl,float roughness,float metallic) +{ + + vec3 L = normalize(-light.direction); + float diff=max(dot(normal,L),0.0); + vec3 diffuse=light.lightColor*diff*vec3(texture(texture0,fragTexCoord)); + vec3 H = normalize(diffuse + L); + + // Cook-Torrance BRDF distribution function + float nDotV = max(dot(normal,viewDir), 0.0000001); + float nDotL = max(dot(normal,L), 0.0000001); + float hDotV = max(dot(H,viewDir), 0.0); + float nDotH = max(dot(normal,H), 0.0); + float D = GgxDistribution(nDotH, roughness); // Larger the more micro-facets aligned to H + float G = GeomSmith(nDotV, nDotL, roughness); // Smaller the more micro-facets shadow + vec3 F = SchlickFresnel(hDotV, baseRefl); // Fresnel proportion of specular reflectance + + vec3 spec = (D*G*F)/(4.0*nDotV*nDotL); + + // Difuse and spec light can't be above 1.0 + // kD = 1.0 - kS diffuse component is equal 1.0 - spec comonent + vec3 kD = vec3(1.0) - F; + // Mult kD by the inverse of metallnes, only non-metals should have diffuse light + kD *= 1.0 - metallic; + // Angle of light has impact on result + return ((kD*albedo.rgb/PI + spec)*nDotL)*light.enabled; + +} + +vec3 CalcPointLight(Light light,vec3 normal,vec3 viewDir,vec3 albedo,vec3 baseRefl,float roughness,float metallic) +{ + + vec3 L = normalize(light.position - fragPosition); + vec3 H = normalize(viewDir + L); + float distance=length(light.position-fragPosition); + float attenuation=light.enargy/(light.constant+light.linear*distance+light.quadratic*(distance*distance)); + vec3 radiance = light.lightColor.rgb*light.enargy*attenuation; + + // Cook-Torrance BRDF distribution function + float nDotV = max(dot(normal,viewDir), 0.0000001); + float nDotL = max(dot(normal,L), 0.0000001); + float hDotV = max(dot(H,viewDir), 0.0); + float nDotH = max(dot(normal,H), 0.0); + float D = GgxDistribution(nDotH, roughness); // Larger the more micro-facets aligned to H + float G = GeomSmith(nDotV, nDotL, roughness); // Smaller the more micro-facets shadow + vec3 F = SchlickFresnel(hDotV, baseRefl); // Fresnel proportion of specular reflectance + + vec3 spec = (D*G*F)/(4.0*nDotV*nDotL); + + // Difuse and spec light can't be above 1.0 + // kD = 1.0 - kS diffuse component is equal 1.0 - spec comonent + vec3 kD = vec3(1.0) - F; + // Mult kD by the inverse of metallnes, only non-metals should have diffuse light + kD *= 1.0 - metallic; + // Angle of light has impact on result + return ((kD*albedo.rgb/PI + spec)*radiance*nDotL)*light.enabled ; +} + +vec3 CalcSpotLight(Light light,vec3 normal,vec3 viewDir,vec3 albedo,vec3 baseRefl,float roughness,float metallic){ + + vec3 L = normalize(light.position - fragPosition); + + float theta=dot(L,normalize(-light.direction)); + float epsilon=cos(radians(light.cutOff))-cos(radians(light.outerCutOff)); + float intensity=smoothstep(0.0,1.0,(theta-cos(radians(light.outerCutOff)))/epsilon);//clamp((theta-cos(radians(light.outerCutOff)))/epsilon,0.0,1.0); + intensity*= length(vec3(texture(flashlight,vec2(fragTexCoord.x*tilingFlashlight.x + offsetFlashlight.y, fragTexCoord.y*tilingFlashlight.y + offsetFlashlight.y)).rgb)); + + float diff=max(dot(normal,L),0.0); + vec3 H = light.lightColor*diff*vec3(texture(texture0,fragTexCoord)); + float distance=length(light.position-fragPosition); + float attenuation=light.enargy/(light.constant+light.linear*distance+light.quadratic*(distance*distance)); + vec3 radiance = light.lightColor.rgb*light.enargy*attenuation; + H*=intensity; + + // Cook-Torrance BRDF distribution function + float nDotV = max(dot(normal,viewDir), 0.0000001); + float nDotL = max(dot(normal,L), 0.0000001); + float hDotV = max(dot(H,viewDir), 0.0); + float nDotH = max(dot(normal,H), 0.0); + float D = GgxDistribution(nDotH, roughness); // Larger the more micro-facets aligned to H + float G = GeomSmith(nDotV, nDotL, roughness); // Smaller the more micro-facets shadow + vec3 F = SchlickFresnel(hDotV, baseRefl); // Fresnel proportion of specular reflectance + + vec3 spec = (D*G*F)/(4.0*nDotV*nDotL); + spec*=intensity; + + + // Difuse and spec light can't be above 1.0 + // kD = 1.0 - kS diffuse component is equal 1.0 - spec comonent + vec3 kD = vec3(1.0) - F; + // Mult kD by the inverse of metallnes, only non-metals should have diffuse light + kD *= 1.0 - metallic; + // Angle of light has impact on result + return ((kD*albedo.rgb/PI + spec)*radiance*nDotL)*light.enabled; +} + diff --git a/examples/shaders/pbr/glsl330/pbr.vs b/examples/shaders/pbr/glsl330/pbr.vs new file mode 100644 index 0000000..53b0d0f --- /dev/null +++ b/examples/shaders/pbr/glsl330/pbr.vs @@ -0,0 +1,50 @@ +#version 330 + +// Input vertex attributes +in vec3 vertexPosition; +in vec2 vertexTexCoord; +in vec3 vertexNormal; +in vec3 vertexTangent; +in vec4 vertexColor; + +// Input uniform values +uniform mat4 mvp; +uniform mat4 matModel; +uniform mat4 matNormal; +uniform vec3 lightPos; +uniform vec4 difColor; + +// Output vertex attributes (to fragment shader) +out vec3 fragPosition; +out vec2 fragTexCoord; +out vec2 fragTexCoord2; +out vec4 fragColor; +out vec3 fragNormal; +out mat3 TBN; + +const float normalOffset = 0.1; + +void main() +{ + // Compute binormal from vertex normal and tangent + vec3 vertexBinormal = cross(vertexNormal, vertexTangent); + + // Compute fragment normal based on normal transformations + mat3 normalMatrix = transpose(inverse(mat3(matModel))); + + // Compute fragment position based on model transformations + fragPosition = vec3(matModel*vec4(vertexPosition, 1.0f)); + + fragTexCoord = vertexTexCoord; + fragNormal = normalize(normalMatrix*vertexNormal); + vec3 fragTangent = normalize(normalMatrix*vertexTangent); + fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal); + vec3 fragBinormal = normalize(normalMatrix*vertexBinormal); + fragBinormal = cross(fragNormal, fragTangent); + + TBN = transpose(mat3(fragTangent, fragBinormal, fragNormal)); + + // Calculate final vertex position + gl_Position = mvp*vec4(vertexPosition, 1.0); +} + diff --git a/examples/shaders/pbr/light.go b/examples/shaders/pbr/light.go new file mode 100644 index 0000000..d99c9e0 --- /dev/null +++ b/examples/shaders/pbr/light.go @@ -0,0 +1,213 @@ +package main + +import ( + "fmt" + "unsafe" + + rl "git.terah.dev/UnrealXR/raylib-go/raylib" +) + +type LightType int32 + +const ( + LightTypeDirectional LightType = iota + LightTypePoint + LightTypeSpot +) + +type Light struct { + Shader rl.Shader + combineStatus bool + lightType LightType + position rl.Vector3 + direction rl.Vector3 + lightColor rl.Color + enabled int32 + enargy float32 + cutOff float32 + outerCutOff float32 + constant float32 + linear float32 + quadratic float32 + shiny float32 + specularStr float32 + // shader locations + enabledLoc int32 + typeLoc int32 + posLoc int32 + dirLoc int32 + colorLoc int32 + viewPosLoc int32 + enargyLoc int32 + cutOffLoc int32 + outerCutOffLoc int32 + constantLoc int32 + linearLoc int32 + quadraticLoc int32 + shinyLoc int32 + specularStrLoc int32 +} + +var MaxLights = 5 +var lightCount = 0 + +func (lt *Light) NewLight( + lightType LightType, + position, direction rl.Vector3, + color rl.Color, + enargy float32, shader *rl.Shader) Light { + + light := Light{ + Shader: *shader, + } + + if lightCount < MaxLights { + light.enabled = 1 + light.lightType = lightType + light.position = position + light.direction = direction + light.lightColor = color + light.enargy = enargy + light.cutOff = 12.5 + light.outerCutOff = 17.5 + light.constant = 1.0 + light.linear = 0.09 + light.quadratic = 0.032 + light.shiny = 32.0 + light.specularStr = 0.9 + light.enabledLoc = rl.GetShaderLocation(*shader, fmt.Sprintf("lights[%d].enabled", lightCount)) + light.typeLoc = rl.GetShaderLocation(*shader, fmt.Sprintf("lights[%d].type", lightCount)) + light.posLoc = rl.GetShaderLocation(*shader, fmt.Sprintf("lights[%d].position", lightCount)) + light.dirLoc = rl.GetShaderLocation(*shader, fmt.Sprintf("lights[%d].direction", lightCount)) + light.colorLoc = rl.GetShaderLocation(*shader, fmt.Sprintf("lights[%d].lightColor", lightCount)) + light.enargyLoc = rl.GetShaderLocation(*shader, fmt.Sprintf("lights[%d].enargy", lightCount)) + light.cutOffLoc = rl.GetShaderLocation(*shader, fmt.Sprintf("lights[%d].cutOff", lightCount)) + light.outerCutOffLoc = rl.GetShaderLocation(*shader, fmt.Sprintf("lights[%d].outerCutOff", lightCount)) + light.constantLoc = rl.GetShaderLocation(*shader, fmt.Sprintf("lights[%d].constant", lightCount)) + light.linearLoc = rl.GetShaderLocation(*shader, fmt.Sprintf("lights[%d].linear", lightCount)) + light.quadraticLoc = rl.GetShaderLocation(*shader, fmt.Sprintf("lights[%d].quadratic", lightCount)) + light.shinyLoc = rl.GetShaderLocation(*shader, fmt.Sprintf("lights[%d].shiny", lightCount)) + light.specularStrLoc = rl.GetShaderLocation(*shader, fmt.Sprintf("lights[%d].specularStr", lightCount)) + + light.UpdateValues() + lightCount++ + } + return light +} + +func (lt *Light) UpdateValues() { + // Send to shader light enabled state and type + rl.SetShaderValue(lt.Shader, lt.enabledLoc, unsafe.Slice((*float32)(unsafe.Pointer(<.enabled)), 4), rl.ShaderUniformInt) + rl.SetShaderValue(lt.Shader, lt.typeLoc, unsafe.Slice((*float32)(unsafe.Pointer(<.lightType)), 4), rl.ShaderUniformInt) + + // Send to shader light position values + rl.SetShaderValue(lt.Shader, lt.posLoc, []float32{lt.position.X, lt.position.Y, lt.position.Z}, rl.ShaderUniformVec3) + + // Send to shader light direction values + rl.SetShaderValue(lt.Shader, lt.dirLoc, []float32{lt.direction.X, lt.direction.Y, lt.direction.Z}, rl.ShaderUniformVec3) + + // Send to shader light color values + rl.SetShaderValue(lt.Shader, lt.colorLoc, + []float32{float32(lt.lightColor.R) / 255, float32(lt.lightColor.G) / 255, float32(lt.lightColor.B) / 255}, + rl.ShaderUniformVec3) + + // Send to shader light enargy values + rl.SetShaderValue(lt.Shader, lt.enargyLoc, []float32{lt.enargy}, rl.ShaderUniformFloat) + + // Send to shader light spot values + rl.SetShaderValue(lt.Shader, lt.cutOffLoc, []float32{lt.cutOff}, rl.ShaderUniformFloat) + rl.SetShaderValue(lt.Shader, lt.outerCutOffLoc, []float32{lt.outerCutOff}, rl.ShaderUniformFloat) + + // Send to shader light pointLight values + rl.SetShaderValue(lt.Shader, lt.constantLoc, []float32{lt.constant}, rl.ShaderUniformFloat) + rl.SetShaderValue(lt.Shader, lt.linearLoc, []float32{lt.linear}, rl.ShaderUniformFloat) + rl.SetShaderValue(lt.Shader, lt.quadraticLoc, []float32{lt.quadratic}, rl.ShaderUniformFloat) + + // Send to shader light shiness values + rl.SetShaderValue(lt.Shader, lt.shinyLoc, []float32{lt.shiny}, rl.ShaderUniformFloat) + rl.SetShaderValue(lt.Shader, lt.specularStrLoc, []float32{lt.specularStr}, rl.ShaderUniformFloat) + +} + +// if you want more 5 light in the light.fs change #define MAX_LIGHTS "your number" +func (lt *Light) SetMaxLight(max int) { + MaxLights = max +} + +func (lt *Light) SetConfigSpotLight(light *Light, cutOff, outerCutOff float32) { + light.cutOff = cutOff + light.outerCutOff = outerCutOff + light.UpdateValues() +} + +func (lt *Light) SetConfigPointLight(light *Light, constant, linear, quadratic float32) { + light.constant = constant + light.linear = linear + light.quadratic = quadratic + light.UpdateValues() +} + +func (lt *Light) SetConfigShiness(light *Light, shiny, specularStr float32) { + light.shiny = shiny + light.specularStr = specularStr + light.UpdateValues() +} + +func (lt *Light) SetMaterialTexture(materials []*rl.Material, texture []*rl.Texture2D) { + for index, material := range materials { + rl.SetMaterialTexture(material, rl.MapDiffuse, *texture[index]) + } +} + +func (lt *Light) SetFlashlightTexture(materials []*rl.Material, texure *rl.Texture2D) { + + lt.Shader.UpdateLocation(rl.ShaderLocMapOcclusion, rl.GetShaderLocation(lt.Shader, "flashlight")) + for _, material := range materials { + rl.SetMaterialTexture(material, rl.MapOcclusion, *texure) + } +} + +func (lt *Light) Init(ambientStrength float32, ambientColor rl.Vector3) { + if !lt.combineStatus { + lt.configShader() + } + lt.viewPosLoc = rl.GetShaderLocation(lt.Shader, "viewPos") + rl.SetShaderValue(lt.Shader, rl.GetShaderLocation(lt.Shader, "ambientColor"), []float32{ambientColor.X, ambientColor.Y, ambientColor.Z}, rl.ShaderUniformVec3) + rl.SetShaderValue(lt.Shader, rl.GetShaderLocation(lt.Shader, "ambientStrength"), []float32{ambientStrength}, rl.ShaderUniformFloat) +} + +func (lt *Light) DisableLight(light *Light) { + light.enabled *= -1 + light.UpdateValues() +} + +func (lt *Light) EnableLight(light *Light) { + light.enabled = 1 + light.UpdateValues() +} + +func (lt *Light) DrawSpherelight(light *Light) { + if light.enabled == 1 { + rl.DrawSphereEx(light.position, 0.2, 8, 8, light.lightColor) + } else { + rl.DrawSphereWires(light.position, 0.2, 8, 8, rl.Fade(light.lightColor, 0.3)) + } +} + +func (lt *Light) UpdateReflect(cameraPos rl.Vector3) { + rl.SetShaderValue(lt.Shader, lt.viewPosLoc, []float32{cameraPos.X, cameraPos.Y, cameraPos.Z}, rl.ShaderUniformVec3) +} + +func (lt *Light) configShader() { + lt.Shader = rl.LoadShader("pbr.vs", "./pbr.fs") +} + +// exce before init or set manually +func (lt *Light) SetCombineShader(CombineShader *rl.Shader) { + lt.combineStatus = true + lt.Shader = *CombineShader +} + +func (lt *Light) Unload() { + rl.UnloadShader(lt.Shader) +} diff --git a/examples/shaders/pbr/main.go b/examples/shaders/pbr/main.go new file mode 100644 index 0000000..28dba9f --- /dev/null +++ b/examples/shaders/pbr/main.go @@ -0,0 +1,83 @@ +package main + +import ( + rl "git.terah.dev/UnrealXR/raylib-go/raylib" +) + +func main() { + + screenWidth := int32(900) + screenHeight := int32(500) + rl.SetConfigFlags(rl.FlagMsaa4xHint) //ENABLE 4X MSAA IF AVAILABLE + + rl.InitWindow(screenWidth, screenHeight, "raylib [shaders] example - basic pbr") + + car := rl.LoadModel("./models/old_car_new.glb") + plane := rl.LoadModel("./models/plane.glb") + + cam := rl.Camera3D{} + cam.Fovy = 45 + cam.Position = rl.Vector3{2, 2, 8} + cam.Projection = rl.CameraPerspective + cam.Up = rl.Vector3{0, 1, 0} + + shader := rl.LoadShader("./glsl330/pbr.vs", "./glsl330/pbr.fs") + + l := Light{} + l.SetCombineShader(&shader) + l.Init(0.0, rl.Vector3{1, 1, 1}) + l1 := l.NewLight(LightTypePoint, rl.Vector3{-1, 1, -2}, rl.Vector3{}, rl.Yellow, 4, &l.Shader) + l2 := l.NewLight(LightTypePoint, rl.Vector3{2, 1, 1}, rl.Vector3{}, rl.Green, 3.3, &l.Shader) + l3 := l.NewLight(LightTypePoint, rl.Vector3{-2, 1, 1}, rl.Vector3{}, rl.Red, 8.3, &l.Shader) + l4 := l.NewLight(LightTypePoint, rl.Vector3{1, 1, -2}, rl.Vector3{}, rl.Blue, 2, &l.Shader) + + p := PhysicRender{} + p.SetCombineShader(&shader) + p.Init() + + p.UseTexAlbedo() + p.UseTexMRA() + p.UseTexNormal() + p.UseTexEmissive() + + car.GetMaterials()[0].Shader = shader + + p.AlbedoColorModel(&car, rl.White) + p.MetallicValue(0.0) + p.RoughnessValue(0) + p.EmissivePower(0.0) + p.AoValue(0.0) + p.NormalValue(0.2) + + p.EmissiveColor(rl.NewColor(255, 162, 0, 255)) + p.TextureMapAlbedo(&car.GetMaterials()[0], rl.LoadTexture("models/old_car_d.png")) + p.TextureMapMetalness(&car.GetMaterials()[0], rl.LoadTexture("models/old_car_mra.png")) + p.TextureMapNormal(&car.GetMaterials()[0], rl.LoadTexture("models/old_car_n.png")) + rl.SetMaterialTexture(&car.GetMaterials()[0], rl.MapEmission, rl.LoadTexture("models/old_car_e.png")) + + p.SetTiling(rl.NewVector2(1, 1)) + + plane.GetMaterials()[0].Shader = shader + p.TextureMapAlbedo(&plane.GetMaterials()[0], rl.LoadTexture("./models/road_a.png")) + p.TextureMapNormal(&plane.GetMaterials()[0], rl.LoadTexture("./models/road_n.png")) + p.TextureMapMetalness(&plane.GetMaterials()[0], rl.LoadTexture("./models/road_mra.png")) + + rl.SetTargetFPS(60) + for !rl.WindowShouldClose() { + rl.UpdateCamera(&cam, rl.CameraOrbital) + p.UpadteByCamera(cam.Position) + rl.BeginDrawing() + rl.DrawFPS(10, 20) + rl.ClearBackground(rl.Gray) + rl.BeginMode3D(cam) + rl.DrawModel(car, rl.Vector3{0, 0.01, 0}, 0.25, rl.RayWhite) + rl.DrawModel(plane, rl.Vector3{0, 0, 0}, 5, rl.RayWhite) + l.DrawSpherelight(&l1) + l.DrawSpherelight(&l2) + l.DrawSpherelight(&l3) + l.DrawSpherelight(&l4) + rl.EndMode3D() + rl.EndDrawing() + } + rl.CloseWindow() +} diff --git a/examples/shaders/pbr/models/old_car_d.png b/examples/shaders/pbr/models/old_car_d.png new file mode 100644 index 0000000..d8b3c83 Binary files /dev/null and b/examples/shaders/pbr/models/old_car_d.png differ diff --git a/examples/shaders/pbr/models/old_car_e.png b/examples/shaders/pbr/models/old_car_e.png new file mode 100644 index 0000000..23f01c0 Binary files /dev/null and b/examples/shaders/pbr/models/old_car_e.png differ diff --git a/examples/shaders/pbr/models/old_car_mra.png b/examples/shaders/pbr/models/old_car_mra.png new file mode 100644 index 0000000..0fb46b3 Binary files /dev/null and b/examples/shaders/pbr/models/old_car_mra.png differ diff --git a/examples/shaders/pbr/models/old_car_n.png b/examples/shaders/pbr/models/old_car_n.png new file mode 100644 index 0000000..11f689f Binary files /dev/null and b/examples/shaders/pbr/models/old_car_n.png differ diff --git a/examples/shaders/pbr/models/old_car_new.glb b/examples/shaders/pbr/models/old_car_new.glb new file mode 100644 index 0000000..119995c Binary files /dev/null and b/examples/shaders/pbr/models/old_car_new.glb differ diff --git a/examples/shaders/pbr/models/plane.glb b/examples/shaders/pbr/models/plane.glb new file mode 100644 index 0000000..452e1c5 Binary files /dev/null and b/examples/shaders/pbr/models/plane.glb differ diff --git a/examples/shaders/pbr/models/road_a.png b/examples/shaders/pbr/models/road_a.png new file mode 100644 index 0000000..1037773 Binary files /dev/null and b/examples/shaders/pbr/models/road_a.png differ diff --git a/examples/shaders/pbr/models/road_mra.png b/examples/shaders/pbr/models/road_mra.png new file mode 100644 index 0000000..988c839 Binary files /dev/null and b/examples/shaders/pbr/models/road_mra.png differ diff --git a/examples/shaders/pbr/models/road_n.png b/examples/shaders/pbr/models/road_n.png new file mode 100644 index 0000000..a5f3548 Binary files /dev/null and b/examples/shaders/pbr/models/road_n.png differ diff --git a/examples/shaders/pbr/pbr.go b/examples/shaders/pbr/pbr.go new file mode 100644 index 0000000..b43ed5a --- /dev/null +++ b/examples/shaders/pbr/pbr.go @@ -0,0 +1,218 @@ +package main + +import ( + "fmt" + "reflect" + "unsafe" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" +) + +const ( + LIGHT_DIRECTIONAL int32 = iota + LIGHT_POINT + LIGHT_SPOT +) + +type light struct { + Enabled int32 + Type int32 + Target rl.Vector3 + Position rl.Vector3 + Color [4]float32 + Intensity float32 + // Shader light parameters locations + enabledLoc int32 + typeLoc int32 + targetLoc int32 + positionLoc int32 + colorLoc int32 + intensityLoc int32 +} + +type PhysicRender struct { + Shader *rl.Shader + combineStatus bool +} + +// make light and pbr Shader +func (ph *PhysicRender) Init() { + + if !ph.combineStatus { + ph.configShader() + } + + ph.Shader.UpdateLocation(rl.ShaderLocMapAlbedo, rl.GetShaderLocation(*ph.Shader, "albedoMap")) + ph.Shader.UpdateLocation(rl.ShaderLocMapMetalness, rl.GetShaderLocation(*ph.Shader, "mraMap")) + ph.Shader.UpdateLocation(rl.ShaderLocMapNormal, rl.GetShaderLocation(*ph.Shader, "normalMap")) + ph.Shader.UpdateLocation(rl.ShaderLocMapEmission, rl.GetShaderLocation(*ph.Shader, "emissiveMap")) + ph.Shader.UpdateLocation(12, rl.GetShaderLocation(*ph.Shader, "albedoColor")) + ph.Shader.UpdateLocation(rl.ShaderLocVectorView, rl.GetShaderLocation(*ph.Shader, "viewPos")) + + ambientColor := rl.Color{R: 122, G: 36, B: 26, A: 100} + ambientColorNormalized := rl.NewVector3(float32(ambientColor.R)/255.0, float32(ambientColor.G)/255.0, float32(ambientColor.B)/255.0) + rl.SetShaderValue(*ph.Shader, rl.GetShaderLocation(*ph.Shader, "ambientColor"), []float32{ambientColorNormalized.X, ambientColorNormalized.Y, ambientColorNormalized.Z}, rl.ShaderUniformVec3) + rl.SetShaderValue(*ph.Shader, rl.GetShaderLocation(*ph.Shader, "ambient"), []float32{float32(0.03)}, rl.ShaderUniformFloat) + + lightCountLoc := rl.GetShaderLocation(*ph.Shader, "numOfLights") + rl.SetShaderValue(*ph.Shader, lightCountLoc, generiteIntForGlsl(int32(MaxLights)), rl.ShaderUniformInt) + +} + +func (ph *PhysicRender) UpadteByCamera(pos rl.Vector3) { + rl.SetShaderValue(*ph.Shader, rl.GetShaderLocation(*ph.Shader, "viewPos"), []float32{pos.X, pos.Y, pos.Z}, rl.ShaderUniformVec3) +} + +func (ph *PhysicRender) CreateLight(typeLight int32, position rl.Vector3, target rl.Vector3, color rl.Color, intensity float32) light { + light := light{} + if lightCount < MaxLights { + light.Enabled = 1 + light.Position = position + light.Type = typeLight + light.Target = target + light.Color[0] = float32(float32(color.R) / 255.0) + light.Color[1] = float32(float32(color.G) / 255.0) + light.Color[2] = float32(float32(color.B) / 255.0) + light.Color[3] = float32(float32(color.A) / 255.0) + light.Intensity = intensity + // NOTE: Shader parameters names for lights must match the requested ones + light.enabledLoc = rl.GetShaderLocation(*ph.Shader, fmt.Sprintf("lights[%d].enabled", lightCount)) + light.positionLoc = rl.GetShaderLocation(*ph.Shader, fmt.Sprintf("lights[%d].position", lightCount)) + light.colorLoc = rl.GetShaderLocation(*ph.Shader, fmt.Sprintf("lights[%d].color", lightCount)) + light.intensityLoc = rl.GetShaderLocation(*ph.Shader, fmt.Sprintf("lights[%d].intensity", lightCount)) + light.typeLoc = rl.GetShaderLocation(*ph.Shader, fmt.Sprintf("lights[%d].type", lightCount)) + //light.targetLoc = rl.GetShaderLocation(ph.Shader, fmt.Sprintf("lights[%d].target", lightCount)) + + ph.UpdateLight(*ph.Shader, light) + + lightCount++ + + } + + return light +} + +func (ph *PhysicRender) UpdateLight(Shader rl.Shader, light light) { + rl.SetShaderValue(Shader, light.enabledLoc, generiteIntForGlsl(light.Enabled), rl.ShaderUniformInt) + rl.SetShaderValue(Shader, light.positionLoc, []float32{light.Position.X, light.Position.Y, light.Position.Z}, rl.ShaderUniformVec3) + rl.SetShaderValue(Shader, light.colorLoc, []float32{light.Color[0], light.Color[1], light.Color[2], light.Color[3]}, rl.ShaderUniformVec4) + rl.SetShaderValue(Shader, light.intensityLoc, []float32{light.Intensity}, rl.ShaderUniformFloat) + rl.SetShaderValue(Shader, light.typeLoc, generiteIntForGlsl(light.Type), rl.ShaderUniformInt) + //rl.SetShaderValue(Shader, light.targetLoc,[]float32{light.Target.X, light.Target.Y, light.Target.Z}, rl.ShaderUniformVec3) +} + +func (ph *PhysicRender) MaxLights(value int) { + MaxLights = value +} + +func (ph *PhysicRender) TextureMapAlbedo(modelMaterials *rl.Material, texture rl.Texture2D) { + rl.SetMaterialTexture(modelMaterials, rl.MapAlbedo, texture) +} + +func (ph *PhysicRender) TextureMapMetalness(modelMaterials *rl.Material, texture rl.Texture2D) { + rl.SetMaterialTexture(modelMaterials, rl.MapMetalness, texture) +} + +func (ph *PhysicRender) TextureMapRoughness(modelMaterials *rl.Material, texture rl.Texture2D) { + rl.SetMaterialTexture(modelMaterials, rl.MapRoughness, texture) +} + +func (ph *PhysicRender) TextureMapNormal(modelMaterials *rl.Material, texture rl.Texture2D) { + rl.SetMaterialTexture(modelMaterials, rl.MapNormal, texture) +} + +func (ph *PhysicRender) TextureMapOcclusion(modelMaterials *rl.Material, texture rl.Texture2D) { + rl.SetMaterialTexture(modelMaterials, rl.MapOcclusion, texture) +} + +func (ph *PhysicRender) DrawSphereLoctionLight(li light, color rl.Color) { + rl.DrawSphereEx(li.Position, 0.2, 8, 8, color) +} + +func (ph *PhysicRender) AlbedoColorModel(model *rl.Model, color rl.Color) { + model.GetMaterials()[0].GetMap(rl.MapAlbedo).Color = color +} + +func (ph *PhysicRender) EmissiveColor(color rl.Color) { + rl.SetShaderValue(*ph.Shader, rl.GetShaderLocation(*ph.Shader, "emissiveColor"), []float32{float32(color.R), float32(color.G), float32(color.B), float32(color.A)}, rl.ShaderUniformVec4) +} + +func (ph *PhysicRender) NormalValue(value float32) { + rl.SetShaderValue(*ph.Shader, rl.GetShaderLocation(*ph.Shader, "emissiveColor"), []float32{value}, rl.ShaderUniformFloat) +} + +func (ph *PhysicRender) MetallicValue(value float32) { + rl.SetShaderValue(*ph.Shader, rl.GetShaderLocation(*ph.Shader, "metallicValue"), []float32{value}, rl.ShaderUniformFloat) +} + +func (ph *PhysicRender) RoughnessValue(value float32) { + rl.SetShaderValue(*ph.Shader, rl.GetShaderLocation(*ph.Shader, "roughnessValue"), []float32{value}, rl.ShaderUniformFloat) +} + +func (ph *PhysicRender) AoValue(value float32) { + rl.SetShaderValue(*ph.Shader, rl.GetShaderLocation(*ph.Shader, "aoValue"), []float32{value}, rl.ShaderUniformFloat) +} + +func (ph *PhysicRender) EmissivePower(value float32) { + rl.SetShaderValue(*ph.Shader, rl.GetShaderLocation(*ph.Shader, "emissivePower"), []float32{value}, rl.ShaderUniformFloat) +} + +func (ph *PhysicRender) AmbientColor(colorAmbient rl.Vector3, ambientValue float32) { + rl.SetShaderValue(*ph.Shader, rl.GetShaderLocation(*ph.Shader, "ambientColor"), []float32{colorAmbient.X, colorAmbient.Y, colorAmbient.Z}, rl.ShaderUniformVec3) + rl.SetShaderValue(*ph.Shader, rl.GetShaderLocation(*ph.Shader, "ambient"), []float32{ambientValue}, rl.ShaderUniformFloat) +} + +func (ph *PhysicRender) SetTiling(value rl.Vector2) { + rl.SetShaderValue(*ph.Shader, rl.GetShaderLocation(*ph.Shader, "tiling"), []float32{value.X, value.Y}, rl.ShaderUniformVec2) +} + +func (ph *PhysicRender) SetOffset(value rl.Vector2) { + rl.SetShaderValue(*ph.Shader, rl.GetShaderLocation(*ph.Shader, "offset"), []float32{value.X, value.Y}, rl.ShaderUniformVec2) +} + +func (ph *PhysicRender) SetTilingFlashlight(value rl.Vector2) { + rl.SetShaderValue(*ph.Shader, rl.GetShaderLocation(*ph.Shader, "tilingFlashlight"), []float32{value.X, value.Y}, rl.ShaderUniformVec2) +} + +func (ph *PhysicRender) SetOffsetFlashlight(value rl.Vector2) { + rl.SetShaderValue(*ph.Shader, rl.GetShaderLocation(*ph.Shader, "offsetFlashlight"), []float32{value.X, value.Y}, rl.ShaderUniformVec2) +} + +func (ph *PhysicRender) UseTexAlbedo() { + rl.SetShaderValue(*ph.Shader, rl.GetShaderLocation(*ph.Shader, "useTexAlbedo"), generiteIntForGlsl(1), rl.ShaderUniformInt) +} + +func (ph *PhysicRender) UseTexNormal() { + rl.SetShaderValue(*ph.Shader, rl.GetShaderLocation(*ph.Shader, "useTexNormal"), generiteIntForGlsl(1), rl.ShaderUniformInt) +} + +func (ph *PhysicRender) UseTexMRA() { + rl.SetShaderValue(*ph.Shader, rl.GetShaderLocation(*ph.Shader, "useTexMRA"), generiteIntForGlsl(1), rl.ShaderUniformInt) +} + +func (ph *PhysicRender) UseTexEmissive() { + rl.SetShaderValue(*ph.Shader, rl.GetShaderLocation(*ph.Shader, "useTexEmissive"), generiteIntForGlsl(1), rl.ShaderUniformInt) +} + +func (ph *PhysicRender) configShader() { + sh := rl.LoadShader("./pbr.vs", "./pbr.fs") + ph.Shader = &sh +} + +// exce before init or set manually +func (ph *PhysicRender) SetCombineShader(CombineShader *rl.Shader) { + ph.combineStatus = true + ph.Shader = CombineShader +} + +func (ph *PhysicRender) Unload() { + rl.UnloadShader(*ph.Shader) +} + +func generiteIntForGlsl(value int32) []float32 { + data := &reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(&value)), + Len: 4, + Cap: 4, + } + return *(*[]float32)(unsafe.Pointer(data)) +} diff --git a/examples/shaders/postprocessing/main.go b/examples/shaders/postprocessing/main.go index 8ca9f87..6377e36 100644 --- a/examples/shaders/postprocessing/main.go +++ b/examples/shaders/postprocessing/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) const MaxPostproShaders = 12 diff --git a/examples/shaders/raymarching/main.go b/examples/shaders/raymarching/main.go index 23f4833..23684f0 100644 --- a/examples/shaders/raymarching/main.go +++ b/examples/shaders/raymarching/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/shaders/shapes_textures/main.go b/examples/shaders/shapes_textures/main.go index 9bd6443..e374af2 100644 --- a/examples/shaders/shapes_textures/main.go +++ b/examples/shaders/shapes_textures/main.go @@ -1,9 +1,5 @@ package main -import ( - "github.com/gen2brain/raylib-go/raylib" -) - func main() { screenWidth := int32(800) screenHeight := int32(450) diff --git a/examples/shaders/texture_drawing/main.go b/examples/shaders/texture_drawing/main.go index 2d1566f..3f7136b 100644 --- a/examples/shaders/texture_drawing/main.go +++ b/examples/shaders/texture_drawing/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/shaders/texture_outline/main.go b/examples/shaders/texture_outline/main.go index 8b2b6d8..50b2257 100644 --- a/examples/shaders/texture_outline/main.go +++ b/examples/shaders/texture_outline/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/shaders/texture_tiling/main.go b/examples/shaders/texture_tiling/main.go index 71097b1..2af0f8a 100644 --- a/examples/shaders/texture_tiling/main.go +++ b/examples/shaders/texture_tiling/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/shaders/texture_waves/main.go b/examples/shaders/texture_waves/main.go index ecdccd0..ce32710 100644 --- a/examples/shaders/texture_waves/main.go +++ b/examples/shaders/texture_waves/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/shaders/vertex_displacement/glsl100/vertex_displacement.fs b/examples/shaders/vertex_displacement/glsl100/vertex_displacement.fs new file mode 100644 index 0000000..0a99e18 --- /dev/null +++ b/examples/shaders/vertex_displacement/glsl100/vertex_displacement.fs @@ -0,0 +1,18 @@ +#version 100 + +precision mediump float; + +// Input vertex attributes (from fragment shader) +varying vec2 fragTexCoord; +varying float height; + + +void main() +{ + vec4 darkblue = vec4(0.0, 0.13, 0.18, 1.0); + vec4 lightblue = vec4(1.0, 1.0, 1.0, 1.0); + // Interpolate between two colors based on height + vec4 finalColor = mix(darkblue, lightblue, height); + + gl_FragColor = finalColor; +} diff --git a/examples/shaders/vertex_displacement/glsl100/vertex_displacement.vs b/examples/shaders/vertex_displacement/glsl100/vertex_displacement.vs new file mode 100644 index 0000000..ef79097 --- /dev/null +++ b/examples/shaders/vertex_displacement/glsl100/vertex_displacement.vs @@ -0,0 +1,48 @@ +#version 100 + +precision mediump float; + +// Input vertex attributes +attribute vec3 vertexPosition; +attribute vec2 vertexTexCoord; +attribute vec3 vertexNormal; +attribute vec4 vertexColor; + +// Input uniform values +uniform mat4 mvp; +uniform mat4 matModel; +uniform mat4 matNormal; + +uniform float time; + +uniform sampler2D perlinNoiseMap; + +// Output vertex attributes (to fragment shader) +varying vec3 fragPosition; +varying vec2 fragTexCoord; +varying vec3 fragNormal; +varying float height; + +void main() +{ + // Calculate animated texture coordinates based on time and vertex position + vec2 animatedTexCoord = sin(vertexTexCoord + vec2(sin(time + vertexPosition.x * 0.1), cos(time + vertexPosition.z * 0.1)) * 0.3); + + // Normalize animated texture coordinates to range [0, 1] + animatedTexCoord = animatedTexCoord * 0.5 + 0.5; + + // Fetch displacement from the perlin noise map + float displacement = texture2D(perlinNoiseMap, animatedTexCoord).r * 7.0; // Amplified displacement + + // Displace vertex position + vec3 displacedPosition = vertexPosition + vec3(0.0, displacement, 0.0); + + // Send vertex attributes to fragment shader + fragPosition = vec3(matModel * vec4(displacedPosition, 1.0)); + fragTexCoord = vertexTexCoord; + fragNormal = normalize(vec3(matNormal * vec4(vertexNormal, 1.0))); + height = displacedPosition.y * 0.2; // Send height to fragment shader for coloring + + // Calculate final vertex position + gl_Position = mvp * vec4(displacedPosition, 1.0); +} diff --git a/examples/shaders/vertex_displacement/glsl330/vertex_displacement.fs b/examples/shaders/vertex_displacement/glsl330/vertex_displacement.fs new file mode 100644 index 0000000..b80b76e --- /dev/null +++ b/examples/shaders/vertex_displacement/glsl330/vertex_displacement.fs @@ -0,0 +1,16 @@ +#version 330 + +// Input fragment attributes (from fragment shader) +in vec2 fragTexCoord; +in float height; + +// Output fragment color +out vec4 finalColor; + +void main() +{ + vec4 darkblue = vec4(0.0, 0.13, 0.18, 1.0); + vec4 lightblue = vec4(1.0, 1.0, 1.0, 1.0); + // Interpolate between two colors based on height + finalColor = mix(darkblue, lightblue, height); +} diff --git a/examples/shaders/vertex_displacement/glsl330/vertex_displacement.vs b/examples/shaders/vertex_displacement/glsl330/vertex_displacement.vs new file mode 100644 index 0000000..c6526db --- /dev/null +++ b/examples/shaders/vertex_displacement/glsl330/vertex_displacement.vs @@ -0,0 +1,46 @@ +#version 330 + +// Input vertex attributes +in vec3 vertexPosition; +in vec2 vertexTexCoord; +in vec3 vertexNormal; +in vec4 vertexColor; + +// Input uniform values +uniform mat4 mvp; +uniform mat4 matModel; +uniform mat4 matNormal; + +uniform float time; + +uniform sampler2D perlinNoiseMap; + +// Output vertex attributes (to fragment shader) +out vec3 fragPosition; +out vec2 fragTexCoord; +out vec3 fragNormal; +out float height; + +void main() +{ + // Calculate animated texture coordinates based on time and vertex position + vec2 animatedTexCoord = sin(vertexTexCoord + vec2(sin(time + vertexPosition.x * 0.1), cos(time + vertexPosition.z * 0.1)) * 0.3); + + // Normalize animated texture coordinates to range [0, 1] + animatedTexCoord = animatedTexCoord * 0.5 + 0.5; + + // Fetch displacement from the perlin noise map + float displacement = texture(perlinNoiseMap, animatedTexCoord).r * 7.0; // Amplified displacement + + // Displace vertex position + vec3 displacedPosition = vertexPosition + vec3(0.0, displacement, 0.0); + + // Send vertex attributes to fragment shader + fragPosition = vec3(matModel * vec4(displacedPosition, 1.0)); + fragTexCoord = vertexTexCoord; + fragNormal = normalize(vec3(matNormal * vec4(vertexNormal, 1.0))); + height = displacedPosition.y * 0.2; // Send height to fragment shader for coloring + + // Calculate final vertex position + gl_Position = mvp * vec4(displacedPosition, 1.0); +} diff --git a/examples/shaders/vertex_displacement/main.go b/examples/shaders/vertex_displacement/main.go new file mode 100644 index 0000000..d7c0677 --- /dev/null +++ b/examples/shaders/vertex_displacement/main.go @@ -0,0 +1,143 @@ +/******************************************************************************************* +* +* raylib [shaders] example - Vertex displacement +* +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 5.0, last time updated with raylib 5.5 +* +* Example originally contributed by Alex ZH (@ZzzhHe) 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) 2023-2025 Alex ZH (@ZzzhHe) +* +********************************************************************************************/ + +package main + +import ( + "fmt" + "os" + "path/filepath" + + rl "git.terah.dev/UnrealXR/raylib-go/raylib" +) + +// NOTE: Usage: `PLATFORM_DESKTOP=1 go run .` +var glslVersion int + +func init() { + if v, ok := os.LookupEnv("PLATFORM_DESKTOP"); ok && v == "1" { + glslVersion = 330 + } else { // PLATFORM_ANDROID, PLATFORM_WEB + glslVersion = 100 + } +} + +// ------------------------------------------------------------------------------------ +// Program main entry point +// ------------------------------------------------------------------------------------ +func main() { + // Initialization + //-------------------------------------------------------------------------------------- + const screenWidth int32 = 800 + const screenHeight int32 = 450 + + rl.SetConfigFlags(rl.FlagMsaa4xHint) // Enable Multi Sampling Anti Aliasing 4x (if available) + + rl.InitWindow(screenWidth, screenHeight, "raylib [shaders] example - vertex displacement") + defer rl.CloseWindow() // Close window and OpenGL context + + // Set up camera + camera := rl.Camera{ + Position: rl.NewVector3(20.0, 5.0, -20.0), + Target: rl.Vector3Zero(), + Up: rl.NewVector3(0.0, 1.0, 0.0), + Fovy: 60.0, + Projection: rl.CameraPerspective, + } + + // Load vertex and fragment shaders + shaderDir := fmt.Sprintf("glsl%d", glslVersion) + + shader := rl.LoadShader( + filepath.Join(shaderDir, "vertex_displacement.vs"), + filepath.Join(shaderDir, "vertex_displacement.fs"), + ) + defer rl.UnloadShader(shader) + + timeLoc := rl.GetShaderLocation(shader, "time") + + // Load perlin noise texture + perlinNoiseImage := rl.GenImagePerlinNoise(512, 512, 0, 0, 1.0) + perlinNoiseMap := rl.LoadTextureFromImage(perlinNoiseImage) + defer rl.UnloadTexture(perlinNoiseMap) + rl.UnloadImage(perlinNoiseImage) + + // Set shader uniform location + perlinNoiseMapLoc := rl.GetShaderLocation(shader, "perlinNoiseMap") + rl.EnableShader(shader.ID) + rl.ActiveTextureSlot(1) + rl.EnableTexture(perlinNoiseMap.ID) + rl.SetUniformSampler(perlinNoiseMapLoc, 1) + + // Create a plane mesh and model + planeMesh := rl.GenMeshPlane(50, 50, 50, 50) + planeModel := rl.LoadModelFromMesh(planeMesh) + defer rl.UnloadModel(planeModel) + + // Set plane model material + planeModel.Materials.Shader = shader + + timer := float32(0) + + rl.DisableCursor() + + rl.SetTargetFPS(60) // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + for !rl.WindowShouldClose() { // Detect window close button or ESC key + // Update + //---------------------------------------------------------------------------------- + rl.UpdateCamera(&camera, rl.CameraFree) // Update camera with free camera mode + + deltaTime := rl.GetFrameTime() + timer += deltaTime + timeValue := []float32{timer} + + rl.SetShaderValue(shader, timeLoc, timeValue, rl.ShaderUniformFloat) // Send time value to shader + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + rl.BeginDrawing() + + rl.ClearBackground(rl.RayWhite) + + rl.BeginMode3D(camera) + + rl.BeginShaderMode(shader) + + // Draw plane model + rl.DrawModel(planeModel, rl.Vector3Zero(), 1.0, rl.White) + + rl.EndShaderMode() + + rl.EndMode3D() + + rl.DrawText("Vertex displacement", 10, 10, 20, rl.DarkGray) + + rl.DrawFPS(10, 40) + + rl.EndDrawing() + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + // NOTE: Unload all loaded resources at this point (that are not `defer`-ed) + //-------------------------------------------------------------------------------------- +} diff --git a/examples/shapes/basic_shapes/main.go b/examples/shapes/basic_shapes/main.go index 62fbdb9..560ce56 100644 --- a/examples/shapes/basic_shapes/main.go +++ b/examples/shapes/basic_shapes/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/shapes/bouncing_ball/main.go b/examples/shapes/bouncing_ball/main.go index ed7ddc5..f5a47d3 100644 --- a/examples/shapes/bouncing_ball/main.go +++ b/examples/shapes/bouncing_ball/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/shapes/collision_area/main.go b/examples/shapes/collision_area/main.go index 005c8c6..225ec09 100644 --- a/examples/shapes/collision_area/main.go +++ b/examples/shapes/collision_area/main.go @@ -3,7 +3,7 @@ package main import ( "fmt" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) var ( diff --git a/examples/shapes/colors_pallete/main.go b/examples/shapes/colors_pallete/main.go index c732bbb..f4189ae 100644 --- a/examples/shapes/colors_pallete/main.go +++ b/examples/shapes/colors_pallete/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/shapes/draw_circle_sector/main.go b/examples/shapes/draw_circle_sector/main.go index fc3f92c..e754294 100644 --- a/examples/shapes/draw_circle_sector/main.go +++ b/examples/shapes/draw_circle_sector/main.go @@ -18,8 +18,8 @@ import ( "fmt" "math" - gui "github.com/gen2brain/raylib-go/raygui" - rl "github.com/gen2brain/raylib-go/raylib" + gui "git.terah.dev/UnrealXR/raylib-go/raygui" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) const ( diff --git a/examples/shapes/draw_ring/main.go b/examples/shapes/draw_ring/main.go index d869ca9..ace9aee 100644 --- a/examples/shapes/draw_ring/main.go +++ b/examples/shapes/draw_ring/main.go @@ -18,8 +18,8 @@ import ( "fmt" "math" - gui "github.com/gen2brain/raylib-go/raygui" - rl "github.com/gen2brain/raylib-go/raylib" + gui "git.terah.dev/UnrealXR/raylib-go/raygui" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) const ( diff --git a/examples/shapes/easings_ball_anim/main.go b/examples/shapes/easings_ball_anim/main.go index 667104e..7927b8e 100644 --- a/examples/shapes/easings_ball_anim/main.go +++ b/examples/shapes/easings_ball_anim/main.go @@ -1,8 +1,8 @@ package main import ( - ez "github.com/gen2brain/raylib-go/easings" - rl "github.com/gen2brain/raylib-go/raylib" + ez "git.terah.dev/UnrealXR/raylib-go/easings" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/shapes/easings_box_anim/main.go b/examples/shapes/easings_box_anim/main.go index 2755169..9a65db5 100644 --- a/examples/shapes/easings_box_anim/main.go +++ b/examples/shapes/easings_box_anim/main.go @@ -1,8 +1,8 @@ package main import ( - ez "github.com/gen2brain/raylib-go/easings" - rl "github.com/gen2brain/raylib-go/raylib" + ez "git.terah.dev/UnrealXR/raylib-go/easings" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) var ( diff --git a/examples/shapes/easings_rectangle_array/main.go b/examples/shapes/easings_rectangle_array/main.go index 966a220..287cf75 100644 --- a/examples/shapes/easings_rectangle_array/main.go +++ b/examples/shapes/easings_rectangle_array/main.go @@ -1,8 +1,8 @@ package main import ( - ez "github.com/gen2brain/raylib-go/easings" - rl "github.com/gen2brain/raylib-go/raylib" + ez "git.terah.dev/UnrealXR/raylib-go/easings" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) var ( diff --git a/examples/shapes/following_eyes/main.go b/examples/shapes/following_eyes/main.go index 437ce9c..a52a318 100644 --- a/examples/shapes/following_eyes/main.go +++ b/examples/shapes/following_eyes/main.go @@ -3,7 +3,7 @@ package main import ( "math" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/shapes/lines_bezier/main.go b/examples/shapes/lines_bezier/main.go index dd2e0bb..f1eeb10 100644 --- a/examples/shapes/lines_bezier/main.go +++ b/examples/shapes/lines_bezier/main.go @@ -1,9 +1,5 @@ package main -import ( - "github.com/gen2brain/raylib-go/raylib" -) - func main() { screenWidth := int32(800) screenHeight := int32(450) diff --git a/examples/shapes/logo_raylib/main.go b/examples/shapes/logo_raylib/main.go index 79b2062..4e300ef 100644 --- a/examples/shapes/logo_raylib/main.go +++ b/examples/shapes/logo_raylib/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/shapes/logo_raylib_anim/main.go b/examples/shapes/logo_raylib_anim/main.go index 931084e..1e4e2c6 100644 --- a/examples/shapes/logo_raylib_anim/main.go +++ b/examples/shapes/logo_raylib_anim/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/shapes/rectangle_scaling/main.go b/examples/shapes/rectangle_scaling/main.go index d72e790..029fef5 100644 --- a/examples/shapes/rectangle_scaling/main.go +++ b/examples/shapes/rectangle_scaling/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) var ( diff --git a/examples/text/bmfont_ttf/main.go b/examples/text/bmfont_ttf/main.go index 1378eae..a1724dd 100644 --- a/examples/text/bmfont_ttf/main.go +++ b/examples/text/bmfont_ttf/main.go @@ -1,9 +1,5 @@ package main -import ( - "github.com/gen2brain/raylib-go/raylib" -) - func main() { screenWidth := int32(800) screenHeight := int32(450) diff --git a/examples/text/bmfont_unordered/main.go b/examples/text/bmfont_unordered/main.go index 54df292..f67f0b4 100644 --- a/examples/text/bmfont_unordered/main.go +++ b/examples/text/bmfont_unordered/main.go @@ -3,7 +3,7 @@ package main import ( "fmt" - "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/text/codepoints_loading/main.go b/examples/text/codepoints_loading/main.go index ecd1c56..d8793a1 100644 --- a/examples/text/codepoints_loading/main.go +++ b/examples/text/codepoints_loading/main.go @@ -16,7 +16,7 @@ import ( "fmt" "slices" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) const ( diff --git a/examples/text/draw_3d/main.go b/examples/text/draw_3d/main.go index 82f4c81..e534924 100644 --- a/examples/text/draw_3d/main.go +++ b/examples/text/draw_3d/main.go @@ -34,7 +34,7 @@ import ( "unicode/utf8" "unsafe" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) // Globals @@ -188,7 +188,7 @@ func main() { // Handle clicking the cube if rl.IsMouseButtonPressed(rl.MouseButtonLeft) { - // TODO : Missing function, see issue https://github.com/gen2brain/raylib-go/issues/457 + // TODO : Missing function, see issue https://git.terah.dev/UnrealXR/raylib-go/issues/457 //ray := rl.GetScreenToWorldRay(rl.GetMousePosition(), camera) ray := rl.GetMouseRay(rl.GetMousePosition(), camera) diff --git a/examples/text/font_filters/main.go b/examples/text/font_filters/main.go index dc6bb82..7c181de 100644 --- a/examples/text/font_filters/main.go +++ b/examples/text/font_filters/main.go @@ -20,7 +20,7 @@ import ( "fmt" "path/filepath" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) const ( diff --git a/examples/text/font_loading/main.go b/examples/text/font_loading/main.go index 28cb4a7..db1b49d 100644 --- a/examples/text/font_loading/main.go +++ b/examples/text/font_loading/main.go @@ -21,7 +21,7 @@ ********************************************************************************************/ package main -import rl "github.com/gen2brain/raylib-go/raylib" +import rl "git.terah.dev/UnrealXR/raylib-go/raylib" const ( screenWidth = 800 diff --git a/examples/text/font_sdf/main.go b/examples/text/font_sdf/main.go index a626512..915048d 100644 --- a/examples/text/font_sdf/main.go +++ b/examples/text/font_sdf/main.go @@ -5,7 +5,7 @@ import ( "fmt" "unsafe" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) //go:embed AnonymousPro-Bold.ttf diff --git a/examples/text/format_text/main.go b/examples/text/format_text/main.go index d5962c0..4320b5c 100644 --- a/examples/text/format_text/main.go +++ b/examples/text/format_text/main.go @@ -3,7 +3,7 @@ package main import ( "fmt" - "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/text/input_box/main.go b/examples/text/input_box/main.go index 75d23f0..b1e3e18 100644 --- a/examples/text/input_box/main.go +++ b/examples/text/input_box/main.go @@ -15,7 +15,7 @@ package main import ( "fmt" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) const ( diff --git a/examples/text/raylib_fonts/main.go b/examples/text/raylib_fonts/main.go index fcecdac..c5257ef 100644 --- a/examples/text/raylib_fonts/main.go +++ b/examples/text/raylib_fonts/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/raylib" ) const maxFonts = 8 diff --git a/examples/text/rectangle_bounds/main.go b/examples/text/rectangle_bounds/main.go index 89976b1..b0d4172 100644 --- a/examples/text/rectangle_bounds/main.go +++ b/examples/text/rectangle_bounds/main.go @@ -18,7 +18,7 @@ import ( "unicode/utf8" "unsafe" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) const ( @@ -33,7 +33,7 @@ func main() { rl.InitWindow(screenWidth, screenHeight, "raylib [text] example - draw text inside a rectangle") text := `Text cannot escape this container ...word wrap also works when active so here's a long text for testing. - + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Nec ullamcorper sit amet risus nullam eget felis eget.` resizing, wordWrap := false, true diff --git a/examples/text/sprite_fonts/main.go b/examples/text/sprite_fonts/main.go index 0958818..d388b62 100644 --- a/examples/text/sprite_fonts/main.go +++ b/examples/text/sprite_fonts/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/text/ttf_loading/main.go b/examples/text/ttf_loading/main.go index 1d266e6..d3dae4a 100644 --- a/examples/text/ttf_loading/main.go +++ b/examples/text/ttf_loading/main.go @@ -3,7 +3,7 @@ package main import ( "fmt" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/text/unicode/main.go b/examples/text/unicode/main.go index 99f51d3..42f1897 100644 --- a/examples/text/unicode/main.go +++ b/examples/text/unicode/main.go @@ -19,7 +19,7 @@ import ( "unicode/utf8" "unsafe" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) const ( diff --git a/examples/text/writing_anim/main.go b/examples/text/writing_anim/main.go index daabe5b..a6671a0 100644 --- a/examples/text/writing_anim/main.go +++ b/examples/text/writing_anim/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/textures/background_scrolling/main.go b/examples/textures/background_scrolling/main.go index f57efa6..ef36116 100644 --- a/examples/textures/background_scrolling/main.go +++ b/examples/textures/background_scrolling/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) var ( diff --git a/examples/textures/blend_modes/main.go b/examples/textures/blend_modes/main.go index 4171bad..0c857c6 100644 --- a/examples/textures/blend_modes/main.go +++ b/examples/textures/blend_modes/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) var ( diff --git a/examples/textures/draw_tiled/main.go b/examples/textures/draw_tiled/main.go index 377c6d3..2a4ca93 100644 --- a/examples/textures/draw_tiled/main.go +++ b/examples/textures/draw_tiled/main.go @@ -17,7 +17,7 @@ package main import ( "fmt" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) const ( diff --git a/examples/textures/image_drawing/main.go b/examples/textures/image_drawing/main.go index 365c579..c4f76e1 100644 --- a/examples/textures/image_drawing/main.go +++ b/examples/textures/image_drawing/main.go @@ -1,9 +1,5 @@ package main -import ( - "github.com/gen2brain/raylib-go/raylib" -) - func main() { screenWidth := int32(800) screenHeight := int32(450) diff --git a/examples/textures/image_generation/main.go b/examples/textures/image_generation/main.go index 8f1eade..16f78fd 100644 --- a/examples/textures/image_generation/main.go +++ b/examples/textures/image_generation/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) const numTextures = 7 diff --git a/examples/textures/image_image/main.go b/examples/textures/image_image/main.go index 7b356bd..8de80bb 100644 --- a/examples/textures/image_image/main.go +++ b/examples/textures/image_image/main.go @@ -4,7 +4,7 @@ import ( "image/png" "os" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/textures/image_loading/main.go b/examples/textures/image_loading/main.go index 9aeb41e..e97eb9b 100644 --- a/examples/textures/image_loading/main.go +++ b/examples/textures/image_loading/main.go @@ -1,9 +1,5 @@ package main -import ( - "github.com/gen2brain/raylib-go/raylib" -) - func main() { screenWidth := int32(800) screenHeight := int32(450) diff --git a/examples/textures/image_processing/main.go b/examples/textures/image_processing/main.go index d19c1b9..5dfdc87 100644 --- a/examples/textures/image_processing/main.go +++ b/examples/textures/image_processing/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) const numProcesses = 8 diff --git a/examples/textures/image_text/main.go b/examples/textures/image_text/main.go index 9905150..2b93857 100644 --- a/examples/textures/image_text/main.go +++ b/examples/textures/image_text/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/textures/logo_raylib/main.go b/examples/textures/logo_raylib/main.go index 8777074..86d8d55 100644 --- a/examples/textures/logo_raylib/main.go +++ b/examples/textures/logo_raylib/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/textures/mouse_painting/main.go b/examples/textures/mouse_painting/main.go index ae44f53..6b61854 100644 --- a/examples/textures/mouse_painting/main.go +++ b/examples/textures/mouse_painting/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) var ( diff --git a/examples/textures/npatch_drawing/main.go b/examples/textures/npatch_drawing/main.go index 84048c0..43bfe6b 100644 --- a/examples/textures/npatch_drawing/main.go +++ b/examples/textures/npatch_drawing/main.go @@ -16,7 +16,7 @@ ********************************************************************************************/ package main -import rl "github.com/gen2brain/raylib-go/raylib" +import rl "git.terah.dev/UnrealXR/raylib-go/raylib" const ( screenWidth = 800 diff --git a/examples/textures/particles_blending/main.go b/examples/textures/particles_blending/main.go index 414d700..8b4b7ca 100644 --- a/examples/textures/particles_blending/main.go +++ b/examples/textures/particles_blending/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/raylib" ) const ( diff --git a/examples/textures/raw_data/main.go b/examples/textures/raw_data/main.go index 714d1f0..c950d51 100644 --- a/examples/textures/raw_data/main.go +++ b/examples/textures/raw_data/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/textures/sprite_button/main.go b/examples/textures/sprite_button/main.go index d79bff1..dc685e8 100644 --- a/examples/textures/sprite_button/main.go +++ b/examples/textures/sprite_button/main.go @@ -12,7 +12,7 @@ ********************************************************************************************/ package main -import rl "github.com/gen2brain/raylib-go/raylib" +import rl "git.terah.dev/UnrealXR/raylib-go/raylib" const ( screenWidth = 800 diff --git a/examples/textures/sprite_explosion/main.go b/examples/textures/sprite_explosion/main.go index ecc1584..8cd487f 100644 --- a/examples/textures/sprite_explosion/main.go +++ b/examples/textures/sprite_explosion/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) var ( diff --git a/examples/textures/srcrec_dstrec/main.go b/examples/textures/srcrec_dstrec/main.go index 80768a2..c51181a 100644 --- a/examples/textures/srcrec_dstrec/main.go +++ b/examples/textures/srcrec_dstrec/main.go @@ -1,9 +1,5 @@ package main -import ( - "github.com/gen2brain/raylib-go/raylib" -) - func main() { screenWidth := int32(800) screenHeight := int32(450) diff --git a/examples/textures/textured_curve/main.go b/examples/textures/textured_curve/main.go index 3d3e762..67f0914 100644 --- a/examples/textures/textured_curve/main.go +++ b/examples/textures/textured_curve/main.go @@ -4,7 +4,7 @@ import ( "fmt" "math" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) var ( diff --git a/examples/textures/textures_gif_player/main.go b/examples/textures/textures_gif_player/main.go index 82a9e2f..51d1275 100644 --- a/examples/textures/textures_gif_player/main.go +++ b/examples/textures/textures_gif_player/main.go @@ -5,7 +5,7 @@ import ( "image/color" "unsafe" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/examples/textures/textures_sprite_anim/main.go b/examples/textures/textures_sprite_anim/main.go index 795989b..dfa715d 100644 --- a/examples/textures/textures_sprite_anim/main.go +++ b/examples/textures/textures_sprite_anim/main.go @@ -3,7 +3,7 @@ package main import ( "fmt" - "github.com/gen2brain/raylib-go/raylib" + "git.terah.dev/UnrealXR/raylib-go/raylib" ) const ( diff --git a/examples/textures/to_image/main.go b/examples/textures/to_image/main.go index 5863090..a65d4d1 100644 --- a/examples/textures/to_image/main.go +++ b/examples/textures/to_image/main.go @@ -1,7 +1,7 @@ package main import ( - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) func main() { diff --git a/physics/README.md b/physics/README.md index 0005dff..851f199 100644 --- a/physics/README.md +++ b/physics/README.md @@ -1,4 +1,4 @@ -## physics [![GoDoc](https://godoc.org/github.com/gen2brain/raylib-go/physics?status.svg)](https://godoc.org/github.com/gen2brain/raylib-go/physics) +## physics [![GoDoc](https://godoc.org/git.terah.dev/UnrealXR/raylib-go/physics?status.svg)](https://godoc.org/git.terah.dev/UnrealXR/raylib-go/physics) 2D Physics library for videogames. diff --git a/physics/go.mod b/physics/go.mod index 9eea555..67b71a1 100644 --- a/physics/go.mod +++ b/physics/go.mod @@ -1,8 +1,8 @@ -module github.com/gen2brain/raylib-go/physics +module git.terah.dev/UnrealXR/raylib-go/physics go 1.21 -require github.com/gen2brain/raylib-go/raylib v0.0.0-20241202103652-5d50abe7c65b +require git.terah.dev/UnrealXR/raylib-go/raylib v0.0.0-20241202103652-5d50abe7c65b require ( github.com/ebitengine/purego v0.8.1 // indirect diff --git a/physics/go.sum b/physics/go.sum index e740228..62eb5f5 100644 --- a/physics/go.sum +++ b/physics/go.sum @@ -1,7 +1,7 @@ github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE= github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= -github.com/gen2brain/raylib-go/raylib v0.0.0-20241202103652-5d50abe7c65b h1:wK8D9x3f+BX1xFGgjj399dYx2eskikDZHxlRaSSA19Q= -github.com/gen2brain/raylib-go/raylib v0.0.0-20241202103652-5d50abe7c65b/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q= +git.terah.dev/UnrealXR/raylib-go/raylib v0.0.0-20241202103652-5d50abe7c65b h1:wK8D9x3f+BX1xFGgjj399dYx2eskikDZHxlRaSSA19Q= +git.terah.dev/UnrealXR/raylib-go/raylib v0.0.0-20241202103652-5d50abe7c65b/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= diff --git a/physics/physics.go b/physics/physics.go index e98c675..0e57c95 100644 --- a/physics/physics.go +++ b/physics/physics.go @@ -8,7 +8,7 @@ import ( "math/rand" "time" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) // ShapeType type diff --git a/raygui/README.md b/raygui/README.md index 42c543c..85ffb99 100644 --- a/raygui/README.md +++ b/raygui/README.md @@ -1,4 +1,4 @@ -## raygui [![GoDoc](https://godoc.org/github.com/gen2brain/raylib-go/raygui?status.svg)](https://godoc.org/github.com/gen2brain/raylib-go/raygui) +## raygui [![GoDoc](https://godoc.org/git.terah.dev/UnrealXR/raylib-go/raygui?status.svg)](https://godoc.org/git.terah.dev/UnrealXR/raylib-go/raygui) raygui is simple and easy-to-use IMGUI (immediate mode GUI API) library. diff --git a/raygui/cstring.go b/raygui/cstring.go index c321ae2..023bfd4 100644 --- a/raygui/cstring.go +++ b/raygui/cstring.go @@ -70,7 +70,7 @@ func (arr *CStringArray) ToSlice() []string { defer C.free(unsafe.Pointer(cs)) p := uintptr(arr.Pointer) for { - cs = (**C.char)(unsafe.Pointer(p)) + cs = *(***C.char)(unsafe.Pointer(&p)) if *cs == nil { // skip NULL - the last element break } diff --git a/raygui/go.mod b/raygui/go.mod index 61d3563..e9096e0 100644 --- a/raygui/go.mod +++ b/raygui/go.mod @@ -1,8 +1,8 @@ -module github.com/gen2brain/raylib-go/raygui +module git.terah.dev/UnrealXR/raylib-go/raygui go 1.21 -require github.com/gen2brain/raylib-go/raylib v0.0.0-20241202103652-5d50abe7c65b +require git.terah.dev/UnrealXR/raylib-go/raylib v0.0.0-20241202103652-5d50abe7c65b require ( github.com/ebitengine/purego v0.8.1 // indirect diff --git a/raygui/go.sum b/raygui/go.sum index e740228..62eb5f5 100644 --- a/raygui/go.sum +++ b/raygui/go.sum @@ -1,7 +1,7 @@ github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE= github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= -github.com/gen2brain/raylib-go/raylib v0.0.0-20241202103652-5d50abe7c65b h1:wK8D9x3f+BX1xFGgjj399dYx2eskikDZHxlRaSSA19Q= -github.com/gen2brain/raylib-go/raylib v0.0.0-20241202103652-5d50abe7c65b/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q= +git.terah.dev/UnrealXR/raylib-go/raylib v0.0.0-20241202103652-5d50abe7c65b h1:wK8D9x3f+BX1xFGgjj399dYx2eskikDZHxlRaSSA19Q= +git.terah.dev/UnrealXR/raylib-go/raylib v0.0.0-20241202103652-5d50abe7c65b/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= diff --git a/raygui/raygui.go b/raygui/raygui.go index b72e28b..e41a749 100644 --- a/raygui/raygui.go +++ b/raygui/raygui.go @@ -12,7 +12,7 @@ import ( "strings" "unsafe" - rl "github.com/gen2brain/raylib-go/raylib" + rl "git.terah.dev/UnrealXR/raylib-go/raylib" ) const ( @@ -548,8 +548,8 @@ func ComboBox(bounds rl.Rectangle, text string, active int32) int32 { return int32(cactive) } -// Spinner control, returns selected value -func Spinner(bounds rl.Rectangle, text string, value *int32, minValue, maxValue int, editMode bool) int32 { +// Spinner control, sets value to the selected number and returns true when clicked. +func Spinner(bounds rl.Rectangle, text string, value *int32, minValue, maxValue int, editMode bool) bool { var cbounds C.struct_Rectangle cbounds.x = C.float(bounds.X) cbounds.y = C.float(bounds.Y) @@ -573,8 +573,7 @@ func Spinner(bounds rl.Rectangle, text string, value *int32, minValue, maxValue cmaxValue := C.int(maxValue) ceditMode := C.bool(editMode) - C.GuiSpinner(cbounds, ctext, &cvalue, cminValue, cmaxValue, ceditMode) - return int32(cvalue) + return C.GuiSpinner(cbounds, ctext, &cvalue, cminValue, cmaxValue, ceditMode) != 0 } // Slider control diff --git a/raylib/cgo_darwin.go b/raylib/cgo_darwin.go index 919518c..575fb88 100644 --- a/raylib/cgo_darwin.go +++ b/raylib/cgo_darwin.go @@ -1,5 +1,5 @@ -//go:build darwin && !rgfw && !sdl -// +build darwin,!rgfw,!sdl +//go:build darwin && !rgfw && !sdl && !sdl3 +// +build darwin,!rgfw,!sdl,!sdl3 package rl diff --git a/raylib/cgo_darwin_rgfw.go b/raylib/cgo_darwin_rgfw.go index 619c3fd..3006c24 100644 --- a/raylib/cgo_darwin_rgfw.go +++ b/raylib/cgo_darwin_rgfw.go @@ -1,5 +1,5 @@ -//go:build darwin && rgfw && !sdl -// +build darwin,rgfw,!sdl +//go:build darwin && rgfw && !sdl && !sdl3 +// +build darwin,rgfw,!sdl,!sdl3 package rl diff --git a/raylib/cgo_darwin_sdl.go b/raylib/cgo_darwin_sdl.go index e0902ea..a5d3a08 100644 --- a/raylib/cgo_darwin_sdl.go +++ b/raylib/cgo_darwin_sdl.go @@ -1,12 +1,17 @@ -//go:build darwin && sdl && !rgfw -// +build darwin,sdl,!rgfw +//go:build darwin && (sdl || sdl3) && !rgfw +// +build darwin +// +build sdl sdl3 +// +build !rgfw package rl /* #cgo darwin LDFLAGS: -framework Cocoa -framework IOKit -framework CoreVideo -framework CoreFoundation -#cgo darwin CFLAGS: -Wno-deprecated-declarations -Wno-implicit-const-int-float-conversion -DPLATFORM_DESKTOP_SDL -#cgo darwin pkg-config: sdl2 +#cgo darwin CFLAGS: -Wno-deprecated-declarations -Wno-implicit-const-int-float-conversion +#cgo darwin,sdl CFLAGS: -DPLATFORM_DESKTOP_SDL +#cgo darwin,sdl3 CFLAGS: -DPLATFORM_DESKTOP_SDL -DPLATFORM_DESKTOP_SDL3 +#cgo darwin,sdl pkg-config: sdl2 +#cgo darwin,sdl3 pkg-config: sdl3 #cgo darwin,!es2,!es3 LDFLAGS: -framework OpenGL diff --git a/raylib/cgo_freebsd.go b/raylib/cgo_freebsd.go index 99d6491..c29c731 100644 --- a/raylib/cgo_freebsd.go +++ b/raylib/cgo_freebsd.go @@ -1,5 +1,5 @@ -//go:build freebsd && !linux && !rgfw && !drm && !sdl && !android -// +build freebsd,!linux,!rgfw,!drm,!sdl,!android +//go:build freebsd && !linux && !rgfw && !drm && !sdl && !sdl3 && !android +// +build freebsd,!linux,!rgfw,!drm,!sdl,!sdl3,!android package rl diff --git a/raylib/cgo_freebsd_rgfw.go b/raylib/cgo_freebsd_rgfw.go index 35c8142..6df34b8 100644 --- a/raylib/cgo_freebsd_rgfw.go +++ b/raylib/cgo_freebsd_rgfw.go @@ -1,5 +1,5 @@ -//go:build freebsd && rgfw && !linux && !drm && !sdl && !android -// +build freebsd,rgfw,!linux,!drm,!sdl,!android +//go:build freebsd && rgfw && !linux && !drm && !sdl && !sdl3 && !android +// +build freebsd,rgfw,!linux,!drm,!sdl,!sdl3,!android package rl diff --git a/raylib/cgo_freebsd_sdl.go b/raylib/cgo_freebsd_sdl.go index a54ff47..cc56b7e 100644 --- a/raylib/cgo_freebsd_sdl.go +++ b/raylib/cgo_freebsd_sdl.go @@ -1,13 +1,21 @@ -//go:build freebsd && !linux && sdl && !rgfw && !drm && !android -// +build freebsd,!linux,sdl,!rgfw,!drm,!android +//go:build freebsd && !linux && (sdl || sdl3) && !rgfw && !drm && !android +// +build freebsd +// +build !linux +// +build sdl sdl3 +// +build !rgfw +// +build !drm +// +build !android package rl /* -#cgo freebsd CFLAGS: -I. -I/usr/local/include -DPLATFORM_DESKTOP_SDL +#cgo freebsd CFLAGS: -I. -I/usr/local/include +#cgo freebsd,sdl CFLAGS: -DPLATFORM_DESKTOP_SDL +#cgo freebsd,sdl3 CFLAGS: -DPLATFORM_DESKTOP_SDL -DPLATFORM_DESKTOP_SDL3 #cgo freebsd LDFLAGS: -L/usr/local/lib -#cgo freebsd pkg-config: sdl2 +#cgo freebsd,sdl pkg-config: sdl2 +#cgo freebsd,sdl3 pkg-config: sdl3 #cgo freebsd,!es2,!es3 LDFLAGS: -lGL diff --git a/raylib/cgo_linux.go b/raylib/cgo_linux.go index 07f9886..453d563 100644 --- a/raylib/cgo_linux.go +++ b/raylib/cgo_linux.go @@ -1,5 +1,5 @@ -//go:build linux && !rgfw && !drm && !sdl && !android -// +build linux,!rgfw,!drm,!sdl,!android +//go:build linux && !rgfw && !drm && !sdl && !sdl3 && !android +// +build linux,!rgfw,!drm,!sdl,!sdl3,!android package rl @@ -12,14 +12,18 @@ package rl #include "external/glfw/src/vulkan.c" #include "external/glfw/src/window.c" +#if defined _GLFW_WAYLAND #include "external/glfw/src/wl_init.c" #include "external/glfw/src/wl_monitor.c" #include "external/glfw/src/wl_window.c" +#endif +#if defined _GLFW_X11 #include "external/glfw/src/x11_init.c" #include "external/glfw/src/x11_monitor.c" #include "external/glfw/src/x11_window.c" #include "external/glfw/src/glx_context.c" +#endif #include "external/glfw/src/linux_joystick.c" #include "external/glfw/src/posix_module.c" @@ -35,12 +39,14 @@ GLFWbool _glfwConnectNull(int platformID, _GLFWplatform* platform) { } #cgo linux CFLAGS: -Iexternal/glfw/include -DPLATFORM_DESKTOP -Wno-stringop-overflow -#cgo linux LDFLAGS: -lm -pthread -ldl -lrt -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon +#cgo linux LDFLAGS: -lm -pthread -ldl -lrt -lxkbcommon +#cgo linux,!x11 LDFLAGS: -lwayland-client -lwayland-cursor -lwayland-egl #cgo linux,x11 CFLAGS: -D_GLFW_X11 -#cgo linux,!x11 CFLAGS: -D_GLFW_X11 -D_GLFW_WAYLAND +#cgo linux,wayland CFLAGS: -D_GLFW_WAYLAND +#cgo linux,!x11,!wayland CFLAGS: -D_GLFW_X11 -D_GLFW_WAYLAND -#cgo linux,!es2,!es3 LDFLAGS: -lGL +#cgo linux,!es2,!es3,!wayland LDFLAGS: -lGL #cgo linux,opengl11,!es2,!es3 CFLAGS: -DGRAPHICS_API_OPENGL_11 #cgo linux,opengl21,!es2,!es3 CFLAGS: -DGRAPHICS_API_OPENGL_21 diff --git a/raylib/cgo_linux_drm.go b/raylib/cgo_linux_drm.go index afc1ac0..66d07e4 100644 --- a/raylib/cgo_linux_drm.go +++ b/raylib/cgo_linux_drm.go @@ -1,10 +1,11 @@ -//go:build linux && drm && !rgfw && !sdl && !android -// +build linux,drm,!rgfw,!sdl,!android +//go:build linux && drm && !drm_leasing && !drm_disable_input && !rgfw && !sdl && !sdl3 && !android +// +build linux,drm,!drm_leasing,!drm_disable_input,!rgfw,!sdl,!sdl3,!android package rl /* -#cgo linux,drm LDFLAGS: -lGLESv2 -lEGL -ldrm -lgbm -lpthread -lrt -lm -ldl -#cgo linux,drm CFLAGS: -DPLATFORM_DRM -DGRAPHICS_API_OPENGL_ES2 -DEGL_NO_X11 -I/usr/include/libdrm +#cgo pkg-config: libdrm gbm +#cgo linux,drm LDFLAGS: -lGLESv2 -lEGL -lpthread -lrt -lm -ldl +#cgo linux,drm CFLAGS: -DSUPPORT_SSH_KEYBOARD_RPI -DPLATFORM_DRM -DGRAPHICS_API_OPENGL_ES2 -DEGL_NO_X11 */ import "C" diff --git a/raylib/cgo_linux_drm_leasing.go b/raylib/cgo_linux_drm_leasing.go new file mode 100644 index 0000000..b56afc0 --- /dev/null +++ b/raylib/cgo_linux_drm_leasing.go @@ -0,0 +1,11 @@ +//go:build linux && drm && drm_leasing && !drm_disable_input && !rgfw && !sdl && !sdl3 && !android +// +build linux,drm,drm_leasing,!drm_disable_input,!rgfw,!sdl,!sdl3,!android + +package rl + +/* +#cgo pkg-config: libdrm gbm wayland-client +#cgo linux,drm LDFLAGS: -lGLESv2 -lEGL -lpthread -lrt -lm -ldl +#cgo linux,drm CFLAGS: -DSUPPORT_SSH_KEYBOARD_RPI -DPLATFORM_DRM -DENABLE_WAYLAND_DRM_LEASING -DGRAPHICS_API_OPENGL_ES2 -DEGL_NO_X11 +*/ +import "C" diff --git a/raylib/cgo_linux_drm_leasing_no_keyboard.go b/raylib/cgo_linux_drm_leasing_no_keyboard.go new file mode 100644 index 0000000..bd04a7f --- /dev/null +++ b/raylib/cgo_linux_drm_leasing_no_keyboard.go @@ -0,0 +1,11 @@ +//go:build linux && drm && drm_leasing && drm_disable_input && !rgfw && !sdl && !sdl3 && !android +// +build linux,drm,drm_leasing,drm_disable_input,!rgfw,!sdl,!sdl3,!android + +package rl + +/* +#cgo pkg-config: libdrm gbm wayland-client +#cgo linux,drm LDFLAGS: -lGLESv2 -lEGL -lpthread -lrt -lm -ldl +#cgo linux,drm CFLAGS: -DDISABLE_EVDEV_INPUT -DPLATFORM_DRM -DENABLE_WAYLAND_DRM_LEASING -DGRAPHICS_API_OPENGL_ES2 -DEGL_NO_X11 +*/ +import "C" diff --git a/raylib/cgo_linux_drm_no_keyboard.go b/raylib/cgo_linux_drm_no_keyboard.go new file mode 100644 index 0000000..12c329d --- /dev/null +++ b/raylib/cgo_linux_drm_no_keyboard.go @@ -0,0 +1,11 @@ +//go:build linux && drm && drm_disable_input && !drm_leasing && !rgfw && !sdl && !sdl3 && !android +// +build linux,drm,drm_disable_input,!drm_leasing,!rgfw,!sdl,!sdl3,!android + +package rl + +/* +#cgo pkg-config: libdrm gbm +#cgo linux,drm LDFLAGS: -lGLESv2 -lEGL -lpthread -lrt -lm -ldl +#cgo linux,drm CFLAGS: -DDISABLE_EVDEV_INPUT -DPLATFORM_DRM -DGRAPHICS_API_OPENGL_ES2 -DEGL_NO_X11 +*/ +import "C" diff --git a/raylib/cgo_linux_rgfw.go b/raylib/cgo_linux_rgfw.go index 54ea8f9..7585836 100644 --- a/raylib/cgo_linux_rgfw.go +++ b/raylib/cgo_linux_rgfw.go @@ -1,5 +1,5 @@ -//go:build linux && rgfw && !drm && !sdl && !android -// +build linux,rgfw,!drm,!sdl,!android +//go:build linux && rgfw && !drm && !sdl && !sdl3 && !android +// +build linux,rgfw,!drm,!sdl,!sdl3,!android package rl diff --git a/raylib/cgo_linux_sdl.go b/raylib/cgo_linux_sdl.go index 90f3186..cde87da 100644 --- a/raylib/cgo_linux_sdl.go +++ b/raylib/cgo_linux_sdl.go @@ -1,12 +1,19 @@ -//go:build linux && sdl && !rgfw && !drm && !android -// +build linux,sdl,!rgfw,!drm,!android +//go:build linux && (sdl || sdl3) && !rgfw && !drm && !android +// +build linux +// +build sdl sdl3 +// +build !rgfw +// +build !drm +// +build !android package rl /* #cgo linux,!es2 LDFLAGS: -lm -#cgo linux CFLAGS: -DPLATFORM_DESKTOP_SDL -Wno-stringop-overflow -#cgo linux pkg-config: sdl2 +#cgo linux CFLAGS: -Wno-stringop-overflow +#cgo linux,sdl CFLAGS: -DPLATFORM_DESKTOP_SDL +#cgo linux,sdl3 CFLAGS: -DPLATFORM_DESKTOP_SDL -DPLATFORM_DESKTOP_SDL3 +#cgo linux,sdl pkg-config: sdl2 +#cgo linux,sdl3 pkg-config: sdl3 #cgo linux,!es2,!es3 LDFLAGS: -lGL diff --git a/raylib/cgo_openbsd.go b/raylib/cgo_openbsd.go index 9e8d6b5..a40ee1c 100644 --- a/raylib/cgo_openbsd.go +++ b/raylib/cgo_openbsd.go @@ -1,5 +1,5 @@ -//go:build openbsd && !linux && !rgfw && !drm && !sdl && !android -// +build openbsd,!linux,!rgfw,!drm,!sdl,!android +//go:build openbsd && !linux && !rgfw && !drm && !sdl && !sdl3 && !android +// +build openbsd,!linux,!rgfw,!drm,!sdl,!sdl3,!android package rl diff --git a/raylib/cgo_openbsd_rgfw.go b/raylib/cgo_openbsd_rgfw.go index a46ef7a..11441dc 100644 --- a/raylib/cgo_openbsd_rgfw.go +++ b/raylib/cgo_openbsd_rgfw.go @@ -1,5 +1,5 @@ -//go:build openbsd && rgfw && !linux && !sdl && !drm && !android -// +build openbsd,rgfw,!linux,!sdl,!drm,!android +//go:build openbsd && rgfw && !linux && !sdl && !sdl3 && !drm && !android +// +build openbsd,rgfw,!linux,!sdl,!sdl3,!drm,!android package rl diff --git a/raylib/cgo_openbsd_sdl.go b/raylib/cgo_openbsd_sdl.go index 2bd9f10..4fe5e5a 100644 --- a/raylib/cgo_openbsd_sdl.go +++ b/raylib/cgo_openbsd_sdl.go @@ -1,13 +1,21 @@ -//go:build openbsd && !linux && sdl && !rgfw && !drm && !android -// +build openbsd,!linux,sdl,!rgfw,!drm,!android +//go:build openbsd && !linux && (sdl || sdl3) && !rgfw && !drm && !android +// +build openbsd +// +build !linux +// +build sdl sdl3 +// +build !rgfw +// +build !drm +// +build !android package rl /* -#cgo openbsd CFLAGS: -I. -I/usr/X11R6/include -DPLATFORM_DESKTOP_SDL +#cgo openbsd CFLAGS: -I. -I/usr/X11R6/include +#cgo openbsd,sdl CFLAGS: -DPLATFORM_DESKTOP_SDL +#cgo openbsd,sdl3 CFLAGS: -DPLATFORM_DESKTOP_SDL -DPLATFORM_DESKTOP_SDL3 #cgo openbsd LDFLAGS: -L/usr/X11R6/lib -#cgo openbsd pkg-config: sdl2 +#cgo openbsd,sdl pkg-config: sdl2 +#cgo openbsd,sdl3 pkg-config: sdl3 #cgo openbsd,!es2,!es3 LDFLAGS: -lGL diff --git a/raylib/cgo_vendor.go b/raylib/cgo_vendor.go new file mode 100644 index 0000000..87308cb --- /dev/null +++ b/raylib/cgo_vendor.go @@ -0,0 +1,12 @@ +//go:build required +// +build required + +package rl + +import ( + _ "git.terah.dev/UnrealXR/raylib-go/raylib/external" + _ "git.terah.dev/UnrealXR/raylib-go/raylib/external/android/native_app_glue" + _ "git.terah.dev/UnrealXR/raylib-go/raylib/external/glfw/include/GLFW" + _ "git.terah.dev/UnrealXR/raylib-go/raylib/external/glfw/src" + _ "git.terah.dev/UnrealXR/raylib-go/raylib/platforms" +) diff --git a/raylib/cgo_windows.go b/raylib/cgo_windows.go index ee4ef36..c2cd044 100644 --- a/raylib/cgo_windows.go +++ b/raylib/cgo_windows.go @@ -1,5 +1,5 @@ -//go:build windows && !rgfw && !sdl -// +build windows,!rgfw,!sdl +//go:build windows && !rgfw && !sdl && !sdl3 +// +build windows,!rgfw,!sdl,!sdl3 package rl diff --git a/raylib/cgo_windows_rgfw.go b/raylib/cgo_windows_rgfw.go index 3508152..eea463d 100644 --- a/raylib/cgo_windows_rgfw.go +++ b/raylib/cgo_windows_rgfw.go @@ -1,5 +1,5 @@ -//go:build windows && rgfw && !sdl -// +build windows,rgfw,!sdl +//go:build windows && rgfw && !sdl && !sdl3 +// +build windows,rgfw,!sdl,!sdl3 package rl diff --git a/raylib/cgo_windows_sdl.go b/raylib/cgo_windows_sdl.go index 04cbd1e..58d53a4 100644 --- a/raylib/cgo_windows_sdl.go +++ b/raylib/cgo_windows_sdl.go @@ -1,11 +1,17 @@ -//go:build windows && sdl && !rgfw -// +build windows,sdl,!rgfw +//go:build windows && (sdl || sdl3) && !rgfw +// +build windows +// +build sdl sdl3 +// +build !rgfw package rl /* -#cgo windows LDFLAGS: -lgdi32 -lwinmm -lole32 -lSDL2 -#cgo windows CFLAGS: -Iexternal -DPLATFORM_DESKTOP_SDL -Wno-stringop-overflow +#cgo windows LDFLAGS: -lgdi32 -lwinmm -lole32 +#cgo windows,sdl LDFLAGS: -lSDL2 +#cgo windows,sdl3 LDFLAGS: -lSDL3 +#cgo windows CFLAGS: -Iexternal -Wno-stringop-overflow +#cgo windows,sdl CFLAGS: -DPLATFORM_DESKTOP_SDL +#cgo windows,sdl3 CFLAGS: -DPLATFORM_DESKTOP_SDL -DPLATFORM_DESKTOP_SDL3 #cgo windows,!es2,!es3 LDFLAGS: -lopengl32 diff --git a/raylib/config.h b/raylib/config.h index d682e46..c0d50cd 100644 --- a/raylib/config.h +++ b/raylib/config.h @@ -6,7 +6,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2018-2024 Ahmad Fatoum & Ramon Santamaria (@raysan5) +* Copyright (c) 2018-2025 Ahmad Fatoum & Ramon Santamaria (@raysan5) * * 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. @@ -50,7 +50,7 @@ // 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 +//#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 @@ -71,6 +71,30 @@ // 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 + +// NOTE: Clipboard image loading requires support for some image file formats +// TODO: Those defines should probably be removed from here, I prefer to let 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 +#endif + // rcore: Configuration values //------------------------------------------------------------------------------------ @@ -80,7 +104,7 @@ #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_AXIS 8 // Maximum number of axis supported (per gamepad) +#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 8 // Maximum number of touch points supported @@ -112,8 +136,8 @@ #define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported -#define RL_CULL_DISTANCE_NEAR 0.01 // Default projection matrix near cull distance -#define RL_CULL_DISTANCE_FAR 1000.0 // Default projection matrix far cull distance +#define RL_CULL_DISTANCE_NEAR 0.001 // Default projection matrix near cull distance +#define RL_CULL_DISTANCE_FAR 10000.0 // Default projection matrix far cull distance // Default shader vertex attribute locations #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION 0 @@ -127,6 +151,8 @@ #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 + // 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 @@ -273,31 +299,4 @@ //------------------------------------------------------------------------------------ #define MAX_TRACELOG_MSG_LENGTH 256 // Max length of one trace-log message - -// Enable partial support for clipboard image, only working on SDL3 or -// being on both Windows OS + GLFW or Windows OS + RGFW -#define SUPPORT_CLIPBOARD_IMAGE 1 - -#if defined(SUPPORT_CLIPBOARD_IMAGE) - #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_MODULE_RTEXTURES - #define SUPPORT_MODULE_RTEXTURES 1 - #endif -#endif - #endif // CONFIG_H diff --git a/raylib/external/RGFW.h b/raylib/external/RGFW.h index 978536f..1582fbf 100644 --- a/raylib/external/RGFW.h +++ b/raylib/external/RGFW.h @@ -1,9015 +1,10837 @@ -/* -* Copyright (C) 2023-24 ColleagueRiley -* -* libpng license -* -* This software is provided 'as-is', without any express or implied -* warranty. In no event will the authors be held liable for any damages -* arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, -* including commercial applications, and to alter it and redistribute it -* freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not -* claim that you wrote the original software. If you use this software -* in a product, an acknowledgment in the product documentation would be -* appreciated but is not required. -* 2. Altered source versions must be plainly marked as such, and must not be -* misrepresented as being the original software. -* 3. This notice may not be removed or altered from any source distribution. -* -* -*/ - -/* - (MAKE SURE RGFW_IMPLEMENTATION is in exactly one header or you use -D RGFW_IMPLEMENTATION) - #define RGFW_IMPLEMENTATION - makes it so source code is included with header -*/ - -/* - #define RGFW_IMPLEMENTATION - (required) makes it so the source code is included - #define RGFW_PRINT_ERRORS - (optional) makes it so RGFW prints errors when they're found - #define RGFW_OSMESA - (optional) use OSmesa as backend (instead of system's opengl api + regular opengl) - #define RGFW_BUFFER - (optional) just draw directly to (RGFW) window pixel buffer that is drawn to screen (the buffer is in the RGBA format) - #define RGFW_EGL - (optional) use EGL for loading an OpenGL context (instead of the system's opengl api) - #define RGFW_OPENGL_ES1 - (optional) use EGL to load and use Opengl ES (version 1) for backend rendering (instead of the system's opengl api) - 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) use directX for the rendering backend (rather than opengl) (windows only, defaults to opengl for unix) - #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_LINK_OSMESA (optional) (windows only) if EGL is being used, if OS Mesa functions should be defined dymanically (using GetProcAddress) - - #define RGFW_X11 (optional) (unix only) if X11 should be used. This option is turned on by default by unix systems except for MacOS - #define RGFW_WGL_LOAD (optional) (windows only) if WGL should be loaded dynamically during runtime - #define RGFW_NO_X11_CURSOR (optional) (unix only) don't use XCursor - #define RGFW_NO_X11_CURSOR_PRELOAD (optional) (unix only) Use XCursor, but don't link it in code, (you'll have to link it with -lXcursor) - - #define RGFW_NO_DPI - Do not include calculate DPI (no XRM nor libShcore included) - - #define RGFW_ALLOC_DROPFILES (optional) if room should be allocating for drop files (by default it's global data) - #define RGFW_MALLOC x - choose what function to use to allocate, by default the standard malloc is used - #define RGFW_CALLOC x - choose what function to use to allocate (calloc), by default the standard calloc is used - #define RGFW_FREE x - choose what function to use to allocated memory, by default the standard free is used - - #define RGFW_EXPORT - Use when building RGFW - #define RGFW_IMPORT - Use when linking with RGFW (not as a single-header) - - #define RGFW_STD_INT - force the use stdint.h (for systems that might not have stdint.h (msvc)) -*/ - -/* - 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 (used code is marked in some way), - this mainly includes, code for drag and drops, code for setting the icon to a bitmap and the code for managing the clipboard for X11 (as these parts are not documented very well) - - GLFW Copyright, https::/github.com/GLFW/GLFW - - Copyright (c) 2002-2006 Marcus Geelnard - Copyright (c) 2006-2019 Camilla Löwy - - 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) -*/ - -#if _MSC_VER - #pragma comment(lib, "gdi32") - #pragma comment(lib, "shell32") - #pragma comment(lib, "opengl32") - #pragma comment(lib, "winmm") - #pragma comment(lib, "user32") -#endif - -#ifndef RGFW_MALLOC - #include - - #ifndef __USE_POSIX199309 - #define __USE_POSIX199309 - #endif - - #include - #define RGFW_MALLOC malloc - #define RGFW_CALLOC calloc - #define RGFW_FREE free -#endif - -#if !_MSC_VER - #ifndef inline - #ifndef __APPLE__ - #define inline __inline - #endif - #endif -#endif - -#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 -#endif - -#ifndef RGFWDEF - #ifdef __clang__ - #define RGFWDEF static inline - #else - #define RGFWDEF inline - #endif -#endif - -#ifndef RGFW_ENUM - #define RGFW_ENUM(type, name) type name; enum -#endif - -#ifndef RGFW_UNUSED - #define RGFW_UNUSED(x) (void)(x); -#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 - -#if !defined(u8) - #if ((defined(_MSC_VER) || defined(__SYMBIAN32__)) && !defined(RGFW_STD_INT)) /* MSVC might not have stdint.h */ - typedef unsigned char u8; - typedef signed char i8; - typedef unsigned short u16; - typedef signed short i16; - typedef unsigned int u32; - typedef signed int i32; - typedef unsigned long u64; - typedef signed long i64; - #else /* 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 -#endif - -#if !defined(b8) /* RGFW bool type */ - typedef u8 b8; - typedef u32 b32; -#endif - -#define RGFW_TRUE (!(0)) -#define RGFW_FALSE 0 - -/* thse OS macros looks better & are standardized */ -/* plus it helps with cross-compiling */ - -#ifdef __EMSCRIPTEN__ - #define RGFW_WEBASM - - #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__) - #define RGFW_MACOS_X11 - #undef __APPLE__ -#endif - -#if defined(_WIN32) && !defined(RGFW_X11) && !defined(RGFW_WEBASM) /* (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 - - #if defined(RGFW_DIRECTX) - #include - #include - #include - #include - - #ifndef __cplusplus - #define __uuidof(T) IID_##T - #endif - #endif - -#elif defined(RGFW_WAYLAND) - #if !defined(RGFW_NO_API) && (!defined(RGFW_BUFFER) || defined(RGFW_OPENGL)) - #define RGFW_EGL - #define RGFW_OPENGL - #include - #endif - - #include -#elif (defined(__unix__) || defined(RGFW_MACOS_X11) || defined(RGFW_X11)) && !defined(RGFW_WEBASM) - #define RGFW_MACOS_X11 - #define RGFW_X11 - #include -#elif defined(__APPLE__) && !defined(RGFW_MACOS_X11) && !defined(RGFW_X11) && !defined(RGFW_WEBASM) - #define RGFW_MACOS -#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) - #ifndef __APPLE__ - #include - #else - #include - #endif -#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 - -#ifndef RGFW_ALPHA - #define RGFW_ALPHA 128 /* alpha value for RGFW_TRANSPARENT_WINDOW (WINAPI ONLY, macOS + linux don't need this) */ -#endif - -/*! Optional arguments for making a windows */ -#define RGFW_TRANSPARENT_WINDOW (1L<<9) /*!< the window is transparent (only properly works on X11 and MacOS, although it's although for windows) */ -#define RGFW_NO_BORDER (1L<<3) /*!< the window doesn't have border */ -#define RGFW_NO_RESIZE (1L<<4) /*!< the window cannot be resized by the user */ -#define RGFW_ALLOW_DND (1L<<5) /*!< the window supports drag and drop*/ -#define RGFW_HIDE_MOUSE (1L<<6) /*! the window should hide the mouse or not (can be toggled later on) using `RGFW_window_mouseShow*/ -#define RGFW_FULLSCREEN (1L<<8) /* the window is fullscreen by default or not */ -#define RGFW_CENTER (1L<<10) /*! center the window on the screen */ -#define RGFW_OPENGL_SOFTWARE (1L<<11) /*! use OpenGL software rendering */ -#define RGFW_COCOA_MOVE_TO_RESOURCE_DIR (1L << 12) /* (cocoa only), move to resource folder */ -#define RGFW_SCALE_TO_MONITOR (1L << 13) /* scale the window to the screen */ -#define RGFW_NO_INIT_API (1L << 2) /* DO not init an API (mostly for bindings, you should use `#define RGFW_NO_API` in C */ - -#define RGFW_NO_GPU_RENDER (1L<<14) /* don't render (using the GPU based API)*/ -#define RGFW_NO_CPU_RENDER (1L<<15) /* don't render (using the CPU based buffer rendering)*/ -#define RGFW_WINDOW_HIDE (1L << 16)/* the window is hidden */ - -typedef RGFW_ENUM(u8, RGFW_event_types) { - /*! event codes */ - RGFW_keyPressed = 1, /* 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.keyCode - !!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.lockState holds the current lockState - 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_jsButtonPressed, /*!< a joystick button was pressed */ - RGFW_jsButtonReleased, /*!< a joystick button was released */ - RGFW_jsAxisMove, /*!< an axis of a joystick was moved*/ - /*! joystick event note - RGFW_Event.joystick holds which joystick was altered, if any - RGFW_Event.button holds which joystick button was pressed - - RGFW_Event.axis holds the data of all the axis - RGFW_Event.axisCount says how many axis there are - */ - RGFW_windowMoved, /*!< the window was moved (by the user) */ - RGFW_windowResized, /*!< the window was resized (by the user), [on webASM 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_dnd_init /*!< the start of a dnd event, when the place where the file drop is known */ - /* dnd data note - The x and y coords of the drop are stored in the vector RGFW_Event.point - - RGFW_Event.droppedFilesCount holds how many files were dropped - - This is also the size of the array which stores all the dropped file string, - RGFW_Event.droppedFiles - */ -}; - -/*! mouse button codes (RGFW_Event.button) */ -#define RGFW_mouseLeft 1 /*!< left mouse button is pressed*/ -#define RGFW_mouseMiddle 2 /*!< mouse-wheel-button is pressed*/ -#define RGFW_mouseRight 3 /*!< right mouse button is pressed*/ -#define RGFW_mouseScrollUp 4 /*!< mouse wheel is scrolling up*/ -#define RGFW_mouseScrollDown 5 /*!< mouse wheel is scrolling down*/ - -#ifndef RGFW_MAX_PATH -#define RGFW_MAX_PATH 260 /* max length of a path (for dnd) */ -#endif -#ifndef RGFW_MAX_DROPS -#define RGFW_MAX_DROPS 260 /* max items you can drop at once */ -#endif - - -/* for RGFW_Event.lockstate */ -#define RGFW_CAPSLOCK (1L << 1) -#define RGFW_NUMLOCK (1L << 2) - -/*! joystick button codes (based on xbox/playstation), you may need to change these values per controller */ -#ifndef RGFW_joystick_codes - typedef RGFW_ENUM(u8, RGFW_joystick_codes) { - RGFW_JS_A = 0, /*!< or PS X button */ - RGFW_JS_B = 1, /*!< or PS circle button */ - RGFW_JS_Y = 2, /*!< or PS triangle button */ - RGFW_JS_X = 3, /*!< or PS square button */ - RGFW_JS_START = 9, /*!< start button */ - RGFW_JS_SELECT = 8, /*!< select button */ - RGFW_JS_HOME = 10, /*!< home button */ - RGFW_JS_UP = 13, /*!< dpad up */ - RGFW_JS_DOWN = 14, /*!< dpad down*/ - RGFW_JS_LEFT = 15, /*!< dpad left */ - RGFW_JS_RIGHT = 16, /*!< dpad right */ - RGFW_JS_L1 = 4, /*!< left bump */ - RGFW_JS_L2 = 5, /*!< left trigger*/ - RGFW_JS_R1 = 6, /*!< right bumper */ - RGFW_JS_R2 = 7, /*!< right trigger */ - }; -#endif - -/*! basic vector type, if there's not already a point/vector type of choice */ -#ifndef RGFW_point - typedef struct { i32 x, y; } RGFW_point; -#endif - -/*! basic rect type, if there's not already a rect type of choice */ -#ifndef RGFW_rect - typedef struct { i32 x, y, w, h; } RGFW_rect; -#endif - -/*! basic area type, if there's not already a area type of choice */ -#ifndef RGFW_area - typedef struct { u32 w, h; } RGFW_area; -#endif - -#ifndef __cplusplus -#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)} -#else -#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)} -#endif - -#ifndef RGFW_NO_MONITOR - /*! structure for monitor data */ - typedef struct RGFW_monitor { - char name[128]; /*!< monitor name */ - RGFW_rect rect; /*!< monitor Workarea */ - float scaleX, scaleY; /*!< monitor content scale*/ - float physW, physH; /*!< monitor physical size */ - } RGFW_monitor; - - /* - NOTE : Monitor functions should be ran only as many times as needed (not in a loop) - */ - - /*! get an array of all the monitors (max 6) */ - RGFWDEF RGFW_monitor* RGFW_getMonitors(void); - /*! get the primary monitor */ - RGFWDEF RGFW_monitor RGFW_getPrimaryMonitor(void); -#endif - -/* 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 { - char keyName[16]; /*!< key name of event*/ - - /*! drag and drop data */ - /* 260 max paths with a max length of 260 */ -#ifdef RGFW_ALLOC_DROPFILES - char** droppedFiles; -#else - char droppedFiles[RGFW_MAX_DROPS][RGFW_MAX_PATH]; /*!< dropped files*/ -#endif - u32 droppedFilesCount; /*!< house many files were dropped */ - - u32 type; /*!< which event has been sent?*/ - RGFW_point point; /*!< mouse x, y of event (or drop point) */ - - u8 keyCode; /*!< keycode of event !!Keycodes defined at the bottom of the RGFW_HEADER part of this file!! */ - - b8 repeat; /*!< key press event repeated (the key is being held) */ - b8 inFocus; /*!< if the window is in focus or not (this is always true for MacOS windows due to the api being weird) */ - - u8 lockState; - - u8 button; /* !< which mouse button was pressed */ - double scroll; /*!< the raw mouse scroll value */ - - u16 joystick; /*! which joystick this event applies to (if applicable to any) */ - u8 axisesCount; /*!< number of axises */ - RGFW_point axis[2]; /*!< x, y of axises (-100 to 100) */ - - u64 frameTime, frameTime2; /*!< this is used for counting the fps */ -} RGFW_Event; - -/*! source data for the window (used by the APIs) */ -typedef struct RGFW_window_src { -#ifdef RGFW_WINDOWS - HWND window; /*!< source window */ - HDC hdc; /*!< source HDC */ - u32 hOffset; /*!< height offset for window */ - #if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL) - HGLRC ctx; /*!< source graphics context */ - #elif defined(RGFW_OSMESA) - OSMesaContext ctx; - #elif defined(RGFW_DIRECTX) - IDXGISwapChain* swapchain; - ID3D11RenderTargetView* renderTargetView; - ID3D11DepthStencilView* pDepthStencilView; - #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; - #endif - RGFW_area maxSize, minSize; /*!< for setting max/min resize (RGFW_WINDOWS) */ -#elif 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 */ - #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; - GC gc; -#endif -#elif defined(RGFW_WAYLAND) - struct wl_display* display; - struct wl_surface* surface; - struct wl_buffer* wl_buffer; - struct wl_keyboard* keyboard; - - struct xdg_surface* xdg_surface; - struct xdg_toplevel* xdg_toplevel; - struct zxdg_toplevel_decoration_v1* decoration; - RGFW_Event events[20]; - i32 eventLen; - size_t eventIndex; - #if defined(RGFW_EGL) - struct wl_egl_window* window; - EGLSurface EGL_surface; - EGLDisplay EGL_display; - EGLContext EGL_context; - #elif defined(RGFW_OSMESA) - OSMesaContext ctx; - #endif -#elif defined(RGFW_MACOS) - u32 display; - void* displayLink; - void* window; - b8 dndPassed; -#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*/ - -#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - void* bitmap; /*!< API's bitmap for storing or managing */ - void* image; -#endif -#elif defined(RGFW_WEBASM) - #ifdef RGFW_WEBGPU - WGPUInstance ctx; - WGPUDevice device; - WGPUQueue queue; - #else - EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx; - #endif -#endif -} RGFW_window_src; - - - -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 */ -#endif - void* userPtr; /* ptr for usr data */ - - RGFW_Event event; /*!< current event */ - - RGFW_rect r; /*!< the x, y, w and h of the struct */ - - RGFW_point _lastMousePoint; /*!< last cusor point (for raw mouse data) */ - - u32 _winArgs; /*!< windows args (for RGFW to check) */ -} RGFW_window; /*!< Window structure for managing the window */ - -#if defined(RGFW_X11) || defined(RGFW_MACOS) - typedef u64 RGFW_thread; /*!< thread type unix */ -#else - typedef void* RGFW_thread; /*!< thread type for window */ -#endif - -/** * @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(char* name); - -/*! this has to be set before createWindow is called, else the fulscreen size is used */ -RGFWDEF void RGFW_setBufferSize(RGFW_area size); /*!< the buffer cannot be resized (by RGFW) */ - -RGFWDEF RGFW_window* RGFW_createWindow( - const char* name, /* name of the window */ - RGFW_rect rect, /* rect of window */ - u16 args /* extra arguments (NULL / (u16)0 means no args used)*/ -); /*!< function to create a window struct */ - -/*! get the size of the screen to an area struct */ -RGFWDEF RGFW_area RGFW_getScreenSize(void); - -/*! - this function checks an *individual* event (and updates window structure attributes) - this means, using this function without a while loop may cause event lag - - ex. - - while (RGFW_window_checkEvent(win) != NULL) [this keeps checking events until it reaches the last one] - - 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 th e function to keep checking for events even after `RGFW_window_checkEvent == NULL` - if waitMS == 0, the loop will not wait for events - if waitMS == a positive integer, the loop will wait that many miliseconds after there are no more events until it returns - if waitMS == a negative integer, the loop will not return until it gets another event -*/ -typedef RGFW_ENUM(i32, RGFW_eventWait) { - RGFW_NEXT = -1, - RGFW_NO_WAIT = 0 -}; - -/*! 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 */ - -/*! moves window to a given point */ -RGFWDEF void RGFW_window_move(RGFW_window* win, - RGFW_point v/*!< new pos*/ -); - -#ifndef RGFW_NO_MONITOR - /*! move 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 the minimum size a user can shrink a window to a given size/area */ -RGFWDEF void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a); -/*! set the minimum size a user can extend a window to a given size/area */ -RGFWDEF void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a); - -RGFWDEF void RGFW_window_maximize(RGFW_window* win); /*!< maximize the window size */ -RGFWDEF void RGFW_window_minimize(RGFW_window* win); /*!< minimize the window (in taskbar (per OS))*/ -RGFWDEF void RGFW_window_restore(RGFW_window* win); /*!< restore the window from minimized (per OS)*/ - -/*! if the window should have a border or not (borderless) based on bool value of `border` */ -RGFWDEF void RGFW_window_setBorder(RGFW_window* win, b8 border); - -/*! turn on / off dnd (RGFW_ALLOW_DND stil must be passed to the window)*/ -RGFWDEF void RGFW_window_setDND(RGFW_window* win, b8 allow); - -#ifndef RGFW_NO_PASSTHROUGH - /*!! turn on / off mouse passthrough */ - RGFWDEF void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough); -#endif - -/*! rename window to a given string */ -RGFWDEF void RGFW_window_setName(RGFW_window* win, - char* name -); - -RGFWDEF void RGFW_window_setIcon(RGFW_window* win, /*!< source window */ - u8* icon /*!< icon bitmap */, - RGFW_area a /*!< width and height of the bitmap*/, - i32 channels /*!< how many channels the bitmap has (rgb : 3, rgba : 4) */ -); /*!< image resized by default */ - -/*!< sets mouse to bitmap (very simular to RGFW_window_setIcon), image NOT resized by default*/ -RGFWDEF void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels); - -/*!< sets the mouse to a standard API cursor (based on RGFW_MOUSE, as seen at the end of the RGFW_HEADER part of this file) */ -RGFWDEF void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse); - -RGFWDEF void 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 become raw mouse movement data - - this is useful for a 3D camera -*/ -RGFWDEF void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area); -/*! 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 - by setting the window event.type to RGFW_quit -*/ -RGFWDEF void RGFW_window_setShouldClose(RGFW_window* win); - -/*! where the mouse is on the screen */ -RGFWDEF RGFW_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, i8 show); -/*! move the mouse to a set x, y pos*/ -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 b8 RGFW_window_shouldClose(RGFW_window* win); -/*! if window is fullscreen'd */ -RGFWDEF b8 RGFW_window_isFullscreen(RGFW_window* win); -/*! if window is hidden */ -RGFWDEF b8 RGFW_window_isHidden(RGFW_window* win); -/*! if window is minimized */ -RGFWDEF b8 RGFW_window_isMinimized(RGFW_window* win); -/*! if window is maximized */ -RGFWDEF b8 RGFW_window_isMaximized(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_SCALE_TO_MONITOR` during window creation -*/ -RGFWDEF void RGFW_window_scaleToMonitor(RGFW_window* win); -/*! get the struct of the window's monitor */ -RGFWDEF RGFW_monitor RGFW_window_getMonitor(RGFW_window* win); -#endif - -/** @} */ - -/** * @defgroup Input -* @{ */ - -/*error handling*/ -RGFWDEF b8 RGFW_Error(void); /*!< returns true if an error has occurred (doesn't print errors itself) */ - -/*! returns true if the key should be shifted */ -RGFWDEF b8 RGFW_shouldShift(u32 keycode, u8 lockState); - -/*! get char from RGFW keycode (using a LUT), uses shift'd version if shift = true */ -RGFWDEF char RGFW_keyCodeToChar(u32 keycode, b8 shift); -/*! get char from RGFW keycode (using a LUT), uses lockState for shouldShift) */ -RGFWDEF char RGFW_keyCodeToCharAuto(u32 keycode, u8 lockState); - -/*! 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 b8 RGFW_isPressed(RGFW_window* win, u8 key); /*!< if key is pressed (key code)*/ - -RGFWDEF b8 RGFW_wasPressed(RGFW_window* win, u8 key); /*!< if key was pressed (checks previous state only) (key code)*/ - -RGFWDEF b8 RGFW_isHeld(RGFW_window* win, u8 key); /*!< if key is held (key code)*/ -RGFWDEF b8 RGFW_isReleased(RGFW_window* win, u8 key); /*!< if key is released (key code)*/ - -/* if a key is pressed and then released, pretty much the same as RGFW_isReleased */ -RGFWDEF b8 RGFW_isClicked(RGFW_window* win, u8 key /*!< key code*/); - -/*! if a mouse button is pressed */ -RGFWDEF b8 RGFW_isMousePressed(RGFW_window* win, u8 button /*!< mouse button code */ ); -/*! if a mouse button is held */ -RGFWDEF b8 RGFW_isMouseHeld(RGFW_window* win, u8 button /*!< mouse button code */ ); -/*! if a mouse button was released */ -RGFWDEF b8 RGFW_isMouseReleased(RGFW_window* win, u8 button /*!< mouse button code */ ); -/*! if a mouse button was pressed (checks previous state only) */ -RGFWDEF b8 RGFW_wasMousePressed(RGFW_window* win, u8 button /*!< mouse button code */ ); -/** @} */ - -/** * @defgroup Clipboard -* @{ */ -RGFWDEF char* RGFW_readClipboard(size_t* size); /*!< read clipboard data */ -RGFWDEF void RGFW_clipboardFree(char* str); /*!< the string returned from RGFW_readClipboard must be freed */ - -RGFWDEF void RGFW_writeClipboard(const char* text, u32 textLen); /*!< write text to the clipboard */ -/** @} */ - -/** - - - Event callbacks, - these are completely optional, 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_windowmovefunc)(RGFW_window* win, RGFW_rect r); -/*! RGFW_windowResized, the window and its new rect value */ -typedef void (* RGFW_windowresizefunc)(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 inFocus */ -typedef void (* RGFW_focusfunc)(RGFW_window* win, b8 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, b8 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_dnd_init, 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 keycode, the string version, the state of mod keys, if it was a press (else it's a release) */ -typedef void (* RGFW_keyfunc)(RGFW_window* win, u32 keycode, char keyName[16], u8 lockState, b8 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, u8 button, double scroll, b8 pressed); -/*! RGFW_jsButtonPressed / RGFW_jsButtonReleased, 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_jsButtonfunc)(RGFW_window* win, u16 joystick, u8 button, b8 pressed); -/*! RGFW_jsAxisMove, the window that got the event, the joystick in question, the axis values and the amount of axises */ -typedef void (* RGFW_jsAxisfunc)(RGFW_window* win, u16 joystick, RGFW_point axis[2], u8 axisesCount); - - -/*! RGFW_dnd, the window that had the drop, the drop data and the amount files dropped returns previous callback function (if it was set) */ -#ifdef RGFW_ALLOC_DROPFILES - typedef void (* RGFW_dndfunc)(RGFW_window* win, char** droppedFiles, u32 droppedFilesCount); -#else - typedef void (* RGFW_dndfunc)(RGFW_window* win, char droppedFiles[RGFW_MAX_DROPS][RGFW_MAX_PATH], u32 droppedFilesCount); -#endif -/*! set callback for a window move event returns previous callback function (if it was set) */ -RGFWDEF RGFW_windowmovefunc RGFW_setWindowMoveCallback(RGFW_windowmovefunc func); -/*! set callback for a window resize event returns previous callback function (if it was set) */ -RGFWDEF RGFW_windowresizefunc RGFW_setWindowResizeCallback(RGFW_windowresizefunc 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_jsButtonfunc RGFW_setjsButtonCallback(RGFW_jsButtonfunc func); -/*! set callback for a joystick axis mov event returns previous callback function (if it was set) */ -RGFWDEF RGFW_jsAxisfunc RGFW_setjsAxisCallback(RGFW_jsAxisfunc 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_WEBASM) - 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 joystick -* @{ */ - -/*! joystick count starts at 0*/ -/*!< register joystick to window based on a number (the number is based on when it was connected eg. /dev/js0)*/ -RGFWDEF u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber); -RGFWDEF u16 RGFW_registerJoystickF(RGFW_window* win, char* file); - -RGFWDEF u32 RGFW_isPressedJS(RGFW_window* win, u16 controller, u8 button); - -/** @} */ - -/** * @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); - -/*< 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_window_checkFPS(RGFW_window* win, u32 fpsCap); - -/* supports openGL, directX, OSMesa, EGL and software rendering */ -RGFWDEF void RGFW_window_swapBuffers(RGFW_window* win); /*!< swap the rendering buffer */ -RGFWDEF void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval); - -RGFWDEF void RGFW_window_setGPURender(RGFW_window* win, i8 set); -RGFWDEF void RGFW_window_setCPURender(RGFW_window* win, i8 set); - -/*! native API functions */ -#if defined(RGFW_OPENGL) || defined(RGFW_EGL) - /*! OpenGL init hints */ - RGFWDEF void RGFW_setGLStencil(i32 stencil); /*!< set stencil buffer bit size (8 by default) */ - RGFWDEF void RGFW_setGLSamples(i32 samples); /*!< set number of sampiling buffers (4 by default) */ - RGFWDEF void RGFW_setGLStereo(i32 stereo); /*!< use GL_STEREO (GL_FALSE by default) */ - RGFWDEF void RGFW_setGLAuxBuffers(i32 auxBuffers); /*!< number of aux buffers (0 by default) */ - - /*! which profile to use for the opengl verion */ - typedef RGFW_ENUM(u8, RGFW_GL_profile) { RGFW_GL_CORE = 0, RGFW_GL_COMPATIBILITY }; - /*! Set OpenGL version hint (core or compatibility profile)*/ - RGFWDEF void RGFW_setGLVersion(RGFW_GL_profile profile, i32 major, i32 minor); - RGFWDEF void RGFW_setDoubleBuffer(b8 useDoubleBuffer); - RGFWDEF void* 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 */ -#elif defined(RGFW_DIRECTX) - typedef struct { - IDXGIFactory* pFactory; - IDXGIAdapter* pAdapter; - ID3D11Device* pDevice; - ID3D11DeviceContext* pDeviceContext; - } RGFW_directXinfo; - - /* - RGFW stores a global instance of RGFW_directXinfo, - you can use this function to get a pointer the instance - */ - RGFWDEF RGFW_directXinfo* RGFW_getDirectXInfo(void); -#endif - -/** @} */ - -/** * @defgroup Supporting -* @{ */ -RGFWDEF u64 RGFW_getTime(void); /*!< get time in seconds */ -RGFWDEF u64 RGFW_getTimeNS(void); /*!< get time in nanoseconds */ -RGFWDEF void RGFW_sleep(u64 milisecond); /*!< sleep for a set time */ - -/*! - key codes and mouse icon enums -*/ - -typedef RGFW_ENUM(u8, RGFW_Key) { - RGFW_KEY_NULL = 0, - RGFW_Escape, - 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_Backtick, - - RGFW_0, - RGFW_1, - RGFW_2, - RGFW_3, - RGFW_4, - RGFW_5, - RGFW_6, - RGFW_7, - RGFW_8, - RGFW_9, - - RGFW_Minus, - RGFW_Equals, - RGFW_BackSpace, - RGFW_Tab, - RGFW_CapsLock, - RGFW_ShiftL, - RGFW_ControlL, - RGFW_AltL, - RGFW_SuperL, - RGFW_ShiftR, - RGFW_ControlR, - RGFW_AltR, - RGFW_SuperR, - RGFW_Space, - - RGFW_a, - RGFW_b, - RGFW_c, - RGFW_d, - RGFW_e, - RGFW_f, - RGFW_g, - RGFW_h, - RGFW_i, - RGFW_j, - RGFW_k, - RGFW_l, - RGFW_m, - RGFW_n, - RGFW_o, - RGFW_p, - RGFW_q, - RGFW_r, - RGFW_s, - RGFW_t, - RGFW_u, - RGFW_v, - RGFW_w, - RGFW_x, - RGFW_y, - RGFW_z, - - RGFW_Period, - RGFW_Comma, - RGFW_Slash, - RGFW_Bracket, - RGFW_CloseBracket, - RGFW_Semicolon, - RGFW_Return, - RGFW_Quote, - RGFW_BackSlash, - - RGFW_Up, - RGFW_Down, - RGFW_Left, - RGFW_Right, - - RGFW_Delete, - 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, - - final_key, -}; - - -typedef RGFW_ENUM(u8, RGFW_mouseIcons) { - RGFW_MOUSE_NORMAL = 0, - RGFW_MOUSE_ARROW, - RGFW_MOUSE_IBEAM, - RGFW_MOUSE_CROSSHAIR, - RGFW_MOUSE_POINTING_HAND, - RGFW_MOUSE_RESIZE_EW, - RGFW_MOUSE_RESIZE_NS, - RGFW_MOUSE_RESIZE_NWSE, - RGFW_MOUSE_RESIZE_NESW, - RGFW_MOUSE_RESIZE_ALL, - RGFW_MOUSE_NOT_ALLOWED, -}; - -/** @} */ - -#endif /* RGFW_HEADER */ - -/* -Example to get you started : - -linux : gcc main.c -lX11 -lXcursor -lGL -windows : gcc main.c -lopengl32 -lshell32 -lgdi32 -macos : gcc main.c -framework Foundation -framework AppKit -framework OpenGL -framework CoreVideo - -#define RGFW_IMPLEMENTATION -#include "RGFW.h" - -u8 icon[4 * 3 * 3] = {0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF}; - -int main() { - RGFW_window* win = RGFW_createWindow("name", RGFW_RECT(500, 500, 500, 500), (u64)0); - - RGFW_window_setIcon(win, icon, RGFW_AREA(3, 3), 4); - - for (;;) { - RGFW_window_checkEvent(win); // NOTE: checking events outside of a while loop may cause input lag - if (win->event.type == RGFW_quit || RGFW_isPressed(win, RGFW_Escape)) - break; - - RGFW_window_swapBuffers(win); - - glClearColor(0xFF, 0XFF, 0xFF, 0xFF); - glClear(GL_COLOR_BUFFER_BIT); - } - - RGFW_window_close(win); -} - - compiling : - - if you wish to compile the library all you have to do is create a new file with this in it - - rgfw.c - #define RGFW_IMPLEMENTATION - #include "RGFW.h" - - then you can use gcc (or whatever compile you wish to use) to compile the library into object file - - ex. gcc -c RGFW.c -fPIC - - after you compile the library into an object file, you can also turn the object file into an static or shared library - - (commands ar and gcc can be replaced with whatever equivalent your system uses) - static : ar rcs RGFW.a RGFW.o - shared : - windows: - gcc -shared RGFW.o -lwinmm -lopengl32 -lshell32 -lgdi32 -o RGFW.dll - linux: - gcc -shared RGFW.o -lX11 -lXcursor -lGL -lXrandr -o RGFW.so - macos: - gcc -shared RGFW.o -framework Foundation -framework AppKit -framework OpenGL -framework CoreVideo -*/ - -#ifdef RGFW_X11 - #define RGFW_OS_BASED_VALUE(l, w, m, h, ww) l -#elif defined(RGFW_WINDOWS) - #define RGFW_OS_BASED_VALUE(l, w, m, h, ww) w -#elif defined(RGFW_MACOS) - #define RGFW_OS_BASED_VALUE(l, w, m, h, ww) m -#elif defined(RGFW_WEBASM) - #define RGFW_OS_BASED_VALUE(l, w, m, h, ww) h -#elif defined(RGFW_WAYLAND) - #define RGFW_OS_BASED_VALUE(l, w, m, h, ww) ww -#endif - - -#ifdef RGFW_IMPLEMENTATION - -#include -#include -#include -#include - -/* -RGFW_IMPLEMENTATION starts with generic RGFW defines - -This is the start of keycode data - - Why not use macros instead of the numbers itself? - Windows -> Not all virtual keys are macros (VK_0 - VK_1, VK_a - VK_z) - Linux -> Only symcodes are values, (XK_0 - XK_1, XK_a - XK_z) are larger than 0xFF00, I can't find any way to work with them without making the array an unreasonable size - MacOS -> windows and linux already don't have keycodes as macros, so there's no point -*/ - - - -/* - 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 __cplusplus -#define RGFW_NEXT , -#define RGFW_MAP -#else -#define RGFW_NEXT ; -#define RGFW_MAP RGFW_keycodes -#endif - -#ifdef RGFW_WAYLAND -#include -#endif - -u8 RGFW_keycodes [RGFW_OS_BASED_VALUE(136, 337, 128, DOM_VK_WIN_OEM_CLEAR + 1, 130)] = { -#ifdef __cplusplus - 0 -}; -void RGFW_init_keys(void) { -#endif - RGFW_MAP [RGFW_OS_BASED_VALUE(49, 192, 50, DOM_VK_BACK_QUOTE, KEY_GRAVE)] = RGFW_Backtick RGFW_NEXT - - RGFW_MAP [RGFW_OS_BASED_VALUE(19, 0x30, 29, DOM_VK_0, KEY_0)] = RGFW_0 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(10, 0x31, 18, DOM_VK_1, KEY_1)] = RGFW_1 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(11, 0x32, 19, DOM_VK_2, KEY_2)] = RGFW_2 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(12, 0x33, 20, DOM_VK_3, KEY_3)] = RGFW_3 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(13, 0x34, 21, DOM_VK_4, KEY_4)] = RGFW_4 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(14, 0x35, 23, DOM_VK_5, KEY_5)] = RGFW_5 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(15, 0x36, 22, DOM_VK_6, KEY_6)] = RGFW_6 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(16, 0x37, 26, DOM_VK_7, KEY_7)] = RGFW_7 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(17, 0x38, 28, DOM_VK_8, KEY_8)] = RGFW_8 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(18, 0x39, 25, DOM_VK_9, KEY_9)] = RGFW_9, - - RGFW_MAP [RGFW_OS_BASED_VALUE(65, 0x20, 49, DOM_VK_SPACE, KEY_SPACE)] = RGFW_Space, - - RGFW_MAP [RGFW_OS_BASED_VALUE(38, 0x41, 0, DOM_VK_A, KEY_A)] = RGFW_a RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(56, 0x42, 11, DOM_VK_B, KEY_B)] = RGFW_b RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(54, 0x43, 8, DOM_VK_C, KEY_C)] = RGFW_c RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(40, 0x44, 2, DOM_VK_D, KEY_D)] = RGFW_d RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(26, 0x45, 14, DOM_VK_E, KEY_E)] = RGFW_e RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(41, 0x46, 3, DOM_VK_F, KEY_F)] = RGFW_f RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(42, 0x47, 5, DOM_VK_G, KEY_G)] = RGFW_g RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(43, 0x48, 4, DOM_VK_H, KEY_H)] = RGFW_h RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(31, 0x49, 34, DOM_VK_I, KEY_I)] = RGFW_i RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(44, 0x4A, 38, DOM_VK_J, KEY_J)] = RGFW_j RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(45, 0x4B, 40, DOM_VK_K, KEY_K)] = RGFW_k RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(46, 0x4C, 37, DOM_VK_L, KEY_L)] = RGFW_l RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(58, 0x4D, 46, DOM_VK_M, KEY_M)] = RGFW_m RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(57, 0x4E, 45, DOM_VK_N, KEY_N)] = RGFW_n RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(32, 0x4F, 31, DOM_VK_O, KEY_O)] = RGFW_o RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(33, 0x50, 35, DOM_VK_P, KEY_P)] = RGFW_p RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(24, 0x51, 12, DOM_VK_Q, KEY_Q)] = RGFW_q RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(27, 0x52, 15, DOM_VK_R, KEY_R)] = RGFW_r RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(39, 0x53, 1, DOM_VK_S, KEY_S)] = RGFW_s RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(28, 0x54, 17, DOM_VK_T, KEY_T)] = RGFW_t RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(30, 0x55, 32, DOM_VK_U, KEY_U)] = RGFW_u RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(55, 0x56, 9, DOM_VK_V, KEY_V)] = RGFW_v RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(25, 0x57, 13, DOM_VK_W, KEY_W)] = RGFW_w RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(53, 0x58, 7, DOM_VK_X, KEY_X)] = RGFW_x RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(29, 0x59, 16, DOM_VK_Y, KEY_Y)] = RGFW_y RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(52, 0x5A, 6, DOM_VK_Z, KEY_Z)] = RGFW_z, - - RGFW_MAP [RGFW_OS_BASED_VALUE(60, 190, 47, DOM_VK_PERIOD, KEY_DOT)] = RGFW_Period RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(59, 188, 43, DOM_VK_COMMA, KEY_COMMA)] = RGFW_Comma RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(61, 191, 44, DOM_VK_SLASH, KEY_SLASH)] = RGFW_Slash RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(34, 219, 33, DOM_VK_OPEN_BRACKET, KEY_LEFTBRACE)] = RGFW_Bracket RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(35, 221, 30, DOM_VK_CLOSE_BRACKET, KEY_RIGHTBRACE)] = RGFW_CloseBracket RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(47, 186, 41, DOM_VK_SEMICOLON, KEY_SEMICOLON)] = RGFW_Semicolon RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(48, 222, 39, DOM_VK_QUOTE, KEY_APOSTROPHE)] = RGFW_Quote RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(51, 322, 42, DOM_VK_BACK_SLASH, KEY_BACKSLASH)] = RGFW_BackSlash, - - RGFW_MAP [RGFW_OS_BASED_VALUE(36, 0x0D, 36, DOM_VK_RETURN, KEY_ENTER)] = RGFW_Return RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(119, 0x2E, 118, DOM_VK_DELETE, KEY_DELETE)] = RGFW_Delete RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(77, 0x90, 72, DOM_VK_NUM_LOCK, KEY_NUMLOCK)] = RGFW_Numlock RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(106, 0x6F, 82, DOM_VK_DIVIDE, KEY_KPSLASH)] = RGFW_KP_Slash RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(63, 0x6A, 76, DOM_VK_MULTIPLY, KEY_KPASTERISK)] = RGFW_Multiply RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(82, 0x6D, 67, DOM_VK_SUBTRACT, KEY_KPMINUS)] = RGFW_KP_Minus RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(87, 0x61, 84, DOM_VK_NUMPAD1, KEY_KP1)] = RGFW_KP_1 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(88, 0x62, 85, DOM_VK_NUMPAD2, KEY_KP2)] = RGFW_KP_2 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(89, 0x63, 86, DOM_VK_NUMPAD3, KEY_KP3)] = RGFW_KP_3 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(83, 0x64, 87, DOM_VK_NUMPAD4, KEY_KP4)] = RGFW_KP_4 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(84, 0x65, 88, DOM_VK_NUMPAD5, KEY_KP5)] = RGFW_KP_5 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(85, 0x66, 89, DOM_VK_NUMPAD6, KEY_KP6)] = RGFW_KP_6 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(79, 0x67, 90, DOM_VK_NUMPAD7, KEY_KP7)] = RGFW_KP_7 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(80, 0x68, 92, DOM_VK_NUMPAD8, KEY_KP8)] = RGFW_KP_8 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(81, 0x69, 93, DOM_VK_NUMPAD9, KEY_KP9)] = RGFW_KP_9 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(90, 0x60, 83, DOM_VK_NUMPAD0, KEY_KP0)] = RGFW_KP_0 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(91, 0x6E, 65, DOM_VK_DECIMAL, KEY_KPDOT)] = RGFW_KP_Period RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(104, 0x92, 77, 0, KEY_KPENTER)] = RGFW_KP_Return, - - RGFW_MAP [RGFW_OS_BASED_VALUE(20, 189, 27, DOM_VK_HYPHEN_MINUS, KEY_MINUS)] = RGFW_Minus RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(21, 187, 24, DOM_VK_EQUALS, KEY_EQUAL)] = RGFW_Equals RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(22, 8, 51, DOM_VK_BACK_SPACE, KEY_BACKSPACE)] = RGFW_BackSpace RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(23, 0x09, 48, DOM_VK_TAB, KEY_TAB)] = RGFW_Tab RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(66, 20, 57, DOM_VK_CAPS_LOCK, KEY_CAPSLOCK)] = RGFW_CapsLock RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(50, 0x10, 56, DOM_VK_SHIFT, KEY_LEFTSHIFT)] = RGFW_ShiftL RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(37, 0x11, 59, DOM_VK_CONTROL, KEY_LEFTCTRL)] = RGFW_ControlL RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(64,0x12, 58, DOM_VK_ALT, KEY_LEFTALT)] = RGFW_AltL RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(133, 0x5B, 55, DOM_VK_WIN, KEY_LEFTMETA)] = RGFW_SuperL, - - #if !defined(RGFW_WINDOWS) && !defined(RGFW_MACOS) && !defined(RGFW_WEBASM) - RGFW_MAP [RGFW_OS_BASED_VALUE(105, 0x11, 59, 0, KEY_RIGHTCTRL)] = RGFW_ControlR RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(135, 0xA4, 55, 0, KEY_RIGHTMETA)] = RGFW_SuperR, - RGFW_MAP [RGFW_OS_BASED_VALUE(62, 0x5C, 56, 0, KEY_RIGHTSHIFT)] = RGFW_ShiftR RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(108, 165, 58, 0, KEY_RIGHTALT)] = RGFW_AltR, - #endif - - RGFW_MAP [RGFW_OS_BASED_VALUE(67, 0x70, 127, DOM_VK_F1, KEY_F1)] = RGFW_F1 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(68, 0x71, 121, DOM_VK_F2, KEY_F2)] = RGFW_F2 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(69, 0x72, 100, DOM_VK_F3, KEY_F3)] = RGFW_F3 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(70, 0x73, 119, DOM_VK_F4, KEY_F4)] = RGFW_F4 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(71, 0x74, 97, DOM_VK_F5, KEY_F5)] = RGFW_F5 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(72, 0x75, 98, DOM_VK_F6, KEY_F6)] = RGFW_F6 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(73, 0x76, 99, DOM_VK_F7, KEY_F7)] = RGFW_F7 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(74, 0x77, 101, DOM_VK_F8, KEY_F8)] = RGFW_F8 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(75, 0x78, 102, DOM_VK_F9, KEY_F9)] = RGFW_F9 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(76, 0x79, 110, DOM_VK_F10, KEY_F10)] = RGFW_F10 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(95, 0x7A, 104, DOM_VK_F11, KEY_F11)] = RGFW_F11 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(96, 0x7B, 112, DOM_VK_F12, KEY_F12)] = RGFW_F12 RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(111, 0x26, 126, DOM_VK_UP, KEY_UP)] = RGFW_Up RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(116, 0x28, 125, DOM_VK_DOWN, KEY_DOWN)] = RGFW_Down RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(113, 0x25, 123, DOM_VK_LEFT, KEY_LEFT)] = RGFW_Left RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(114, 0x27, 124, DOM_VK_RIGHT, KEY_RIGHT)] = RGFW_Right RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(118, 0x2D, 115, DOM_VK_INSERT, KEY_INSERT)] = RGFW_Insert RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(115, 0x23, 120, DOM_VK_END, KEY_END)] = RGFW_End RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(112, 336, 117, DOM_VK_PAGE_UP, KEY_PAGEUP)] = RGFW_PageUp RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(117, 325, 122, DOM_VK_PAGE_DOWN, KEY_PAGEDOWN)] = RGFW_PageDown RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(9, 0x1B, 53, DOM_VK_ESCAPE, KEY_ESC)] = RGFW_Escape RGFW_NEXT - RGFW_MAP [RGFW_OS_BASED_VALUE(110, 0x24, 116, DOM_VK_HOME, KEY_HOME)] = RGFW_Home RGFW_NEXT -#ifndef __cplusplus -}; -#else -} -#endif - -#undef RGFW_NEXT -#undef RGFW_MAP - -typedef struct { - b8 current : 1; - b8 prev : 1; -} RGFW_keyState; - -RGFW_keyState RGFW_keyboard[final_key] = { {0, 0} }; - -RGFWDEF u32 RGFW_apiKeyCodeToRGFW(u32 keycode); - -u32 RGFW_apiKeyCodeToRGFW(u32 keycode) { - #ifdef __cplusplus - if (RGFW_OS_BASED_VALUE(49, 192, 50, DOM_VK_BACK_QUOTE, KEY_GRAVE) != 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]; -} - -RGFWDEF void RGFW_resetKey(void); -void RGFW_resetKey(void) { - size_t len = final_key; /*!< last_key == length */ - - size_t i; /*!< reset each previous state */ - for (i = 0; i < len; i++) - RGFW_keyboard[i].prev = 0; -} - -b8 RGFW_shouldShift(u32 keycode, u8 lockState) { - #define RGFW_xor(x, y) (( (x) && (!(y)) ) || ((y) && (!(x)) )) - b8 caps4caps = (lockState & RGFW_CAPSLOCK) && ((keycode >= RGFW_a) && (keycode <= RGFW_z)); - b8 shouldShift = RGFW_xor((RGFW_isPressed(NULL, RGFW_ShiftL) || RGFW_isPressed(NULL, RGFW_ShiftR)), caps4caps); - #undef RGFW_xor - - return shouldShift; -} - -char RGFW_keyCodeToChar(u32 keycode, b8 shift) { - static const char map[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '`', '0', '1', '2', '3', '4', '5', '6', '7', '8', - '9', '-', '=', 0, '\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '.', ',', '/', '[', ']', ';', '\n', '\'', '\\', - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '/', '*', '-', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '\n' - }; - - static const char mapCaps[] = { - 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, ' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', - 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', - 'X', 'Y', 'Z', '>', '<', '?', '{', '}', ':', '\n', '"', '|', - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '?', '*', '-', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - if (shift == RGFW_FALSE) - return map[keycode]; - return mapCaps[keycode]; -} - -char RGFW_keyCodeToCharAuto(u32 keycode, u8 lockState) { return RGFW_keyCodeToChar(keycode, RGFW_shouldShift(keycode, lockState)); } - -/* - this is the end of keycode data -*/ - -/* joystick data */ -u8 RGFW_jsPressed[4][16]; /*!< if a key is currently pressed or not (per joystick) */ - -i32 RGFW_joysticks[4]; /*!< limit of 4 joysticks at a time */ -u16 RGFW_joystickCount; /*!< the actual amount of joysticks */ - -/* - event callback defines start here -*/ - - -/* - These exist to avoid the - if (func == NULL) check - for (allegedly) better performance -*/ -void RGFW_windowmovefuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); } -void RGFW_windowresizefuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); } -void RGFW_windowquitfuncEMPTY(RGFW_window* win) { RGFW_UNUSED(win); } -void RGFW_focusfuncEMPTY(RGFW_window* win, b8 inFocus) {RGFW_UNUSED(win); RGFW_UNUSED(inFocus);} -void RGFW_mouseNotifyfuncEMPTY(RGFW_window* win, RGFW_point point, b8 status) {RGFW_UNUSED(win); RGFW_UNUSED(point); RGFW_UNUSED(status);} -void RGFW_mouseposfuncEMPTY(RGFW_window* win, RGFW_point point) {RGFW_UNUSED(win); RGFW_UNUSED(point);} -void RGFW_dndInitfuncEMPTY(RGFW_window* win, RGFW_point point) {RGFW_UNUSED(win); RGFW_UNUSED(point);} -void RGFW_windowrefreshfuncEMPTY(RGFW_window* win) {RGFW_UNUSED(win); } -void RGFW_keyfuncEMPTY(RGFW_window* win, u32 keycode, char keyName[16], u8 lockState, b8 pressed) {RGFW_UNUSED(win); RGFW_UNUSED(keycode); RGFW_UNUSED(keyName); RGFW_UNUSED(lockState); RGFW_UNUSED(pressed);} -void RGFW_mousebuttonfuncEMPTY(RGFW_window* win, u8 button, double scroll, b8 pressed) {RGFW_UNUSED(win); RGFW_UNUSED(button); RGFW_UNUSED(scroll); RGFW_UNUSED(pressed);} -void RGFW_jsButtonfuncEMPTY(RGFW_window* win, u16 joystick, u8 button, b8 pressed){RGFW_UNUSED(win); RGFW_UNUSED(joystick); RGFW_UNUSED(button); RGFW_UNUSED(pressed); } -void RGFW_jsAxisfuncEMPTY(RGFW_window* win, u16 joystick, RGFW_point axis[2], u8 axisesCount){RGFW_UNUSED(win); RGFW_UNUSED(joystick); RGFW_UNUSED(axis); RGFW_UNUSED(axisesCount); } - -#ifdef RGFW_ALLOC_DROPFILES -void RGFW_dndfuncEMPTY(RGFW_window* win, char** droppedFiles, u32 droppedFilesCount) {RGFW_UNUSED(win); RGFW_UNUSED(droppedFiles); RGFW_UNUSED(droppedFilesCount);} -#else -void RGFW_dndfuncEMPTY(RGFW_window* win, char droppedFiles[RGFW_MAX_DROPS][RGFW_MAX_PATH], u32 droppedFilesCount) {RGFW_UNUSED(win); RGFW_UNUSED(droppedFiles); RGFW_UNUSED(droppedFilesCount);} -#endif - -RGFW_windowmovefunc RGFW_windowMoveCallback = RGFW_windowmovefuncEMPTY; -RGFW_windowresizefunc RGFW_windowResizeCallback = RGFW_windowresizefuncEMPTY; -RGFW_windowquitfunc RGFW_windowQuitCallback = RGFW_windowquitfuncEMPTY; -RGFW_mouseposfunc RGFW_mousePosCallback = RGFW_mouseposfuncEMPTY; -RGFW_windowrefreshfunc RGFW_windowRefreshCallback = RGFW_windowrefreshfuncEMPTY; -RGFW_focusfunc RGFW_focusCallback = RGFW_focusfuncEMPTY; -RGFW_mouseNotifyfunc RGFW_mouseNotifyCallBack = RGFW_mouseNotifyfuncEMPTY; -RGFW_dndfunc RGFW_dndCallback = RGFW_dndfuncEMPTY; -RGFW_dndInitfunc RGFW_dndInitCallback = RGFW_dndInitfuncEMPTY; -RGFW_keyfunc RGFW_keyCallback = RGFW_keyfuncEMPTY; -RGFW_mousebuttonfunc RGFW_mouseButtonCallback = RGFW_mousebuttonfuncEMPTY; -RGFW_jsButtonfunc RGFW_jsButtonCallback = RGFW_jsButtonfuncEMPTY; -RGFW_jsAxisfunc RGFW_jsAxisCallback = RGFW_jsAxisfuncEMPTY; - -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_WEBASM /* webasm needs to run the sleep function for asyncify */ - RGFW_sleep(0); - #endif -} - -RGFW_windowmovefunc RGFW_setWindowMoveCallback(RGFW_windowmovefunc func) { - RGFW_windowmovefunc prev = (RGFW_windowMoveCallback == RGFW_windowmovefuncEMPTY) ? NULL : RGFW_windowMoveCallback; - RGFW_windowMoveCallback = func; - return prev; -} -RGFW_windowresizefunc RGFW_setWindowResizeCallback(RGFW_windowresizefunc func) { - RGFW_windowresizefunc prev = (RGFW_windowResizeCallback == RGFW_windowresizefuncEMPTY) ? NULL : RGFW_windowResizeCallback; - RGFW_windowResizeCallback = func; - return prev; -} -RGFW_windowquitfunc RGFW_setWindowQuitCallback(RGFW_windowquitfunc func) { - RGFW_windowquitfunc prev = (RGFW_windowQuitCallback == RGFW_windowquitfuncEMPTY) ? NULL : RGFW_windowQuitCallback; - RGFW_windowQuitCallback = func; - return prev; -} - -RGFW_mouseposfunc RGFW_setMousePosCallback(RGFW_mouseposfunc func) { - RGFW_mouseposfunc prev = (RGFW_mousePosCallback == RGFW_mouseposfuncEMPTY) ? NULL : RGFW_mousePosCallback; - RGFW_mousePosCallback = func; - return prev; -} -RGFW_windowrefreshfunc RGFW_setWindowRefreshCallback(RGFW_windowrefreshfunc func) { - RGFW_windowrefreshfunc prev = (RGFW_windowRefreshCallback == RGFW_windowrefreshfuncEMPTY) ? NULL : RGFW_windowRefreshCallback; - RGFW_windowRefreshCallback = func; - return prev; -} -RGFW_focusfunc RGFW_setFocusCallback(RGFW_focusfunc func) { - RGFW_focusfunc prev = (RGFW_focusCallback == RGFW_focusfuncEMPTY) ? NULL : RGFW_focusCallback; - RGFW_focusCallback = func; - return prev; -} - -RGFW_mouseNotifyfunc RGFW_setMouseNotifyCallBack(RGFW_mouseNotifyfunc func) { - RGFW_mouseNotifyfunc prev = (RGFW_mouseNotifyCallBack == RGFW_mouseNotifyfuncEMPTY) ? NULL : RGFW_mouseNotifyCallBack; - RGFW_mouseNotifyCallBack = func; - return prev; -} -RGFW_dndfunc RGFW_setDndCallback(RGFW_dndfunc func) { - RGFW_dndfunc prev = (RGFW_dndCallback == RGFW_dndfuncEMPTY) ? NULL : RGFW_dndCallback; - RGFW_dndCallback = func; - return prev; -} -RGFW_dndInitfunc RGFW_setDndInitCallback(RGFW_dndInitfunc func) { - RGFW_dndInitfunc prev = (RGFW_dndInitCallback == RGFW_dndInitfuncEMPTY) ? NULL : RGFW_dndInitCallback; - RGFW_dndInitCallback = func; - return prev; -} -RGFW_keyfunc RGFW_setKeyCallback(RGFW_keyfunc func) { - RGFW_keyfunc prev = (RGFW_keyCallback == RGFW_keyfuncEMPTY) ? NULL : RGFW_keyCallback; - RGFW_keyCallback = func; - return prev; -} -RGFW_mousebuttonfunc RGFW_setMouseButtonCallback(RGFW_mousebuttonfunc func) { - RGFW_mousebuttonfunc prev = (RGFW_mouseButtonCallback == RGFW_mousebuttonfuncEMPTY) ? NULL : RGFW_mouseButtonCallback; - RGFW_mouseButtonCallback = func; - return prev; -} -RGFW_jsButtonfunc RGFW_setjsButtonCallback(RGFW_jsButtonfunc func) { - RGFW_jsButtonfunc prev = (RGFW_jsButtonCallback == RGFW_jsButtonfuncEMPTY) ? NULL : RGFW_jsButtonCallback; - RGFW_jsButtonCallback = func; - return prev; -} -RGFW_jsAxisfunc RGFW_setjsAxisCallback(RGFW_jsAxisfunc func) { - RGFW_jsAxisfunc prev = (RGFW_jsAxisCallback == RGFW_jsAxisfuncEMPTY) ? NULL : RGFW_jsAxisCallback; - RGFW_jsAxisCallback = func; - return prev; -} -/* -no more event call back defines -*/ - -#define RGFW_ASSERT(check, str) {\ - if (!(check)) { \ - printf(str); \ - assert(check); \ - } \ -} - -b8 RGFW_error = 0; -b8 RGFW_Error(void) { return RGFW_error; } - -#define SET_ATTRIB(a, v) { \ - assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ - attribs[index++] = a; \ - attribs[index++] = v; \ -} - -RGFW_area RGFW_bufferSize = {0, 0}; -void RGFW_setBufferSize(RGFW_area size) { - RGFW_bufferSize = size; -} - - -RGFWDEF RGFW_window* RGFW_window_basic_init(RGFW_rect rect, u16 args); - -/* do a basic initialization for RGFW_window, this is to standard it for each OS */ -RGFW_window* RGFW_window_basic_init(RGFW_rect rect, u16 args) { - RGFW_window* win = (RGFW_window*) RGFW_MALLOC(sizeof(RGFW_window)); /*!< make a new RGFW struct */ - - /* clear out dnd info */ -#ifdef RGFW_ALLOC_DROPFILES - win->event.droppedFiles = (char**) RGFW_MALLOC(sizeof(char*) * RGFW_MAX_DROPS); - u32 i; - for (i = 0; i < RGFW_MAX_DROPS; i++) - win->event.droppedFiles[i] = (char*) RGFW_CALLOC(RGFW_MAX_PATH, sizeof(char)); -#endif - - /* X11 requires us to have a display to get the screen size */ - #ifndef RGFW_X11 - RGFW_area screenR = RGFW_getScreenSize(); - #else - win->src.display = XOpenDisplay(NULL); - assert(win->src.display != NULL); - - Screen* scrn = DefaultScreenOfDisplay((Display*)win->src.display); - RGFW_area screenR = RGFW_AREA((u32)scrn->width, (u32)scrn->height); - #endif - - /* rect based the requested args */ - if (args & RGFW_FULLSCREEN) - rect = RGFW_RECT(0, 0, screenR.w, screenR.h); - - /* set and init the new window's data */ - win->r = rect; - win->event.inFocus = 1; - win->event.droppedFilesCount = 0; - RGFW_joystickCount = 0; - win->_winArgs = 0; - win->event.lockState = 0; - - return win; -} - -#ifndef RGFW_NO_MONITOR -void RGFW_window_scaleToMonitor(RGFW_window* win) { - RGFW_monitor monitor = RGFW_window_getMonitor(win); - - RGFW_window_resize(win, RGFW_AREA((u32)(monitor.scaleX * (float)win->r.w), (u32)(monitor.scaleX * (float)win->r.h))); -} -#endif - -RGFW_window* RGFW_root = NULL; - - -#define RGFW_HOLD_MOUSE (1L<<2) /*!< hold the moues still */ -#define RGFW_MOUSE_LEFT (1L<<3) /* if mouse left the window */ - -#ifdef RGFW_MACOS -RGFWDEF void RGFW_window_cocoaSetLayer(RGFW_window* win, void* layer); -RGFWDEF void* RGFW_cocoaGetLayer(void); -#endif - -char* RGFW_className = NULL; -void RGFW_setClassName(char* name) { - RGFW_className = name; -} - -void RGFW_clipboardFree(char* str) { RGFW_FREE(str); } - -RGFW_keyState RGFW_mouseButtons[5] = { {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0} }; - -b8 RGFW_isMousePressed(RGFW_window* win, u8 button) { - assert(win != NULL); - return RGFW_mouseButtons[button].current && (win != NULL) && win->event.inFocus; -} -b8 RGFW_wasMousePressed(RGFW_window* win, u8 button) { - assert(win != NULL); - return RGFW_mouseButtons[button].prev && (win != NULL) && win->event.inFocus; -} -b8 RGFW_isMouseHeld(RGFW_window* win, u8 button) { - return (RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button)); -} -b8 RGFW_isMouseReleased(RGFW_window* win, u8 button) { - return (!RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button)); -} - -b8 RGFW_isPressed(RGFW_window* win, u8 key) { - return RGFW_keyboard[key].current && (win == NULL || win->event.inFocus); -} - -b8 RGFW_wasPressed(RGFW_window* win, u8 key) { - return RGFW_keyboard[key].prev && (win == NULL || win->event.inFocus); -} - -b8 RGFW_isHeld(RGFW_window* win, u8 key) { - return (RGFW_isPressed(win, key) && RGFW_wasPressed(win, key)); -} - -b8 RGFW_isClicked(RGFW_window* win, u8 key) { - return (RGFW_wasPressed(win, key) && !RGFW_isPressed(win, key)); -} - -b8 RGFW_isReleased(RGFW_window* win, u8 key) { - return (!RGFW_isPressed(win, key) && RGFW_wasPressed(win, key)); -} - -#if defined(RGFW_WINDOWS) && defined(RGFW_DIRECTX) /* defines for directX context*/ - RGFW_directXinfo RGFW_dxInfo; - RGFW_directXinfo* RGFW_getDirectXInfo(void) { return &RGFW_dxInfo; } -#endif - -void RGFW_window_makeCurrent(RGFW_window* win) { -#if defined(RGFW_WINDOWS) && defined(RGFW_DIRECTX) - if (win == NULL) - RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, NULL, NULL); - else - RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, &win->src.renderTargetView, NULL); -#elif defined(RGFW_OPENGL) - RGFW_window_makeCurrent_OpenGL(win); -#else - RGFW_UNUSED(win) -#endif -} - -void RGFW_window_setGPURender(RGFW_window* win, i8 set) { - if (!set && !(win->_winArgs & RGFW_NO_GPU_RENDER)) - win->_winArgs |= RGFW_NO_GPU_RENDER; - - else if (set && win->_winArgs & RGFW_NO_GPU_RENDER) - win->_winArgs ^= RGFW_NO_GPU_RENDER; -} - -void RGFW_window_setCPURender(RGFW_window* win, i8 set) { - if (!set && !(win->_winArgs & RGFW_NO_CPU_RENDER)) - win->_winArgs |= RGFW_NO_CPU_RENDER; - - else if (set && win->_winArgs & RGFW_NO_CPU_RENDER) - win->_winArgs ^= RGFW_NO_CPU_RENDER; -} - -void RGFW_window_maximize(RGFW_window* win) { - assert(win != NULL); - - RGFW_area screen = RGFW_getScreenSize(); - - RGFW_window_move(win, RGFW_POINT(0, 0)); - RGFW_window_resize(win, screen); -} - -b8 RGFW_window_shouldClose(RGFW_window* win) { - assert(win != NULL); - return (win->event.type == RGFW_quit || RGFW_isPressed(win, RGFW_Escape)); -} - -void RGFW_window_setShouldClose(RGFW_window* win) { win->event.type = RGFW_quit; RGFW_windowQuitCallback(win); } - -#ifndef RGFW_NO_MONITOR - void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m) { - RGFW_window_move(win, RGFW_POINT(m.rect.x + win->r.x, m.rect.y + win->r.y)); - } -#endif - -RGFWDEF void RGFW_captureCursor(RGFW_window* win, RGFW_rect); -RGFWDEF void RGFW_releaseCursor(RGFW_window* win); - -void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area) { - if ((win->_winArgs & RGFW_HOLD_MOUSE)) - return; - - - if (!area.w && !area.h) - area = RGFW_AREA(win->r.w / 2, win->r.h / 2); - - win->_winArgs |= 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) { - if ((win->_winArgs & RGFW_HOLD_MOUSE)) { - win->_winArgs ^= RGFW_HOLD_MOUSE; - - RGFW_releaseCursor(win); - } -} - -u32 RGFW_window_checkFPS(RGFW_window* win, u32 fpsCap) { - u64 deltaTime = RGFW_getTimeNS() - win->event.frameTime; - - u32 output_fps = 0; - u64 fps = round(1e+9 / deltaTime); - output_fps= fps; - - if (fpsCap && fps > fpsCap) { - u64 frameTimeNS = 1e+9 / fpsCap; - u64 sleepTimeMS = (frameTimeNS - deltaTime) / 1e6; - - if (sleepTimeMS > 0) { - RGFW_sleep(sleepTimeMS); - win->event.frameTime = 0; - } - } - - win->event.frameTime = RGFW_getTimeNS(); - - if (fpsCap == 0) - return (u32) output_fps; - - deltaTime = RGFW_getTimeNS() - win->event.frameTime2; - output_fps = round(1e+9 / deltaTime); - win->event.frameTime2 = RGFW_getTimeNS(); - - return output_fps; -} - -u32 RGFW_isPressedJS(RGFW_window* win, u16 c, u8 button) { - RGFW_UNUSED(win); - return RGFW_jsPressed[c][button]; -} - -#if defined(RGFW_X11) || defined(RGFW_WINDOWS) - void RGFW_window_showMouse(RGFW_window* win, i8 show) { - static u8 RGFW_blk[] = { 0, 0, 0, 0 }; - if (show == 0) - RGFW_window_setMouse(win, RGFW_blk, RGFW_AREA(1, 1), 4); - else - RGFW_window_setMouseDefault(win); - } -#endif - -RGFWDEF void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock); -void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock) { - if (capital && !(win->event.lockState & RGFW_CAPSLOCK)) - win->event.lockState |= RGFW_CAPSLOCK; - else if (!capital && (win->event.lockState & RGFW_CAPSLOCK)) - win->event.lockState ^= RGFW_CAPSLOCK; - - if (numlock && !(win->event.lockState & RGFW_NUMLOCK)) - win->event.lockState |= RGFW_NUMLOCK; - else if (!numlock && (win->event.lockState & RGFW_NUMLOCK)) - win->event.lockState ^= RGFW_NUMLOCK; -} - -#if defined(RGFW_X11) || defined(RGFW_MACOS) || defined(RGFW_WEBASM) || defined(RGFW_WAYLAND) - struct timespec; - - int nanosleep(const struct timespec* duration, struct timespec* rem); - int clock_gettime(clockid_t clk_id, struct timespec* tp); - int setenv(const char *name, const char *value, int overwrite); - - void RGFW_window_setDND(RGFW_window* win, b8 allow) { - if (allow && !(win->_winArgs & RGFW_ALLOW_DND)) - win->_winArgs |= RGFW_ALLOW_DND; - - else if (!allow && (win->_winArgs & RGFW_ALLOW_DND)) - win->_winArgs ^= RGFW_ALLOW_DND; - } -#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) || defined(RGFW_OSMESA) - #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 */ -#if !defined(RGFW_OSMESA) - i32 RGFW_majorVersion = 0, RGFW_minorVersion = 0; - b8 RGFW_profile = RGFW_GL_CORE; - - #ifndef RGFW_EGL - i32 RGFW_STENCIL = 8, RGFW_SAMPLES = 4, RGFW_STEREO = 0, RGFW_AUX_BUFFERS = 0, RGFW_DOUBLE_BUFFER = 1; - #else - i32 RGFW_STENCIL = 0, RGFW_SAMPLES = 0, RGFW_STEREO = 0, RGFW_AUX_BUFFERS = 0, RGFW_DOUBLE_BUFFER = 1; - #endif - - - void RGFW_setGLStencil(i32 stencil) { RGFW_STENCIL = stencil; } - void RGFW_setGLSamples(i32 samples) { RGFW_SAMPLES = samples; } - void RGFW_setGLStereo(i32 stereo) { RGFW_STEREO = stereo; } - void RGFW_setGLAuxBuffers(i32 auxBuffers) { RGFW_AUX_BUFFERS = auxBuffers; } - void RGFW_setDoubleBuffer(b8 useDoubleBuffer) { RGFW_DOUBLE_BUFFER = useDoubleBuffer; } - - void RGFW_setGLVersion(b8 profile, i32 major, i32 minor) { - RGFW_profile = profile; - RGFW_majorVersion = major; - RGFW_minorVersion = minor; - } - -/* OPENGL normal only (no EGL / OSMesa) */ -#ifndef RGFW_EGL - -#define RGFW_GL_RENDER_TYPE RGFW_OS_BASED_VALUE(GLX_X_VISUAL_TYPE, 0x2003, 73, 0, 0) - #define RGFW_GL_ALPHA_SIZE RGFW_OS_BASED_VALUE(GLX_ALPHA_SIZE, 0x201b, 11, 0, 0) - #define RGFW_GL_DEPTH_SIZE RGFW_OS_BASED_VALUE(GLX_DEPTH_SIZE, 0x2022, 12, 0, 0) - #define RGFW_GL_DOUBLEBUFFER RGFW_OS_BASED_VALUE(GLX_DOUBLEBUFFER, 0x2011, 5, 0, 0) - #define RGFW_GL_STENCIL_SIZE RGFW_OS_BASED_VALUE(GLX_STENCIL_SIZE, 0x2023, 13, 0, 0) - #define RGFW_GL_SAMPLES RGFW_OS_BASED_VALUE(GLX_SAMPLES, 0x2042, 55, 0, 0) - #define RGFW_GL_STEREO RGFW_OS_BASED_VALUE(GLX_STEREO, 0x2012, 6, 0, 0) - #define RGFW_GL_AUX_BUFFERS RGFW_OS_BASED_VALUE(GLX_AUX_BUFFERS, 0x2024, 7, 0, 0) - -#if defined(RGFW_X11) || defined(RGFW_WINDOWS) - #define RGFW_GL_DRAW RGFW_OS_BASED_VALUE(GLX_X_RENDERABLE, 0x2001, 0, 0, 0) - #define RGFW_GL_DRAW_TYPE RGFW_OS_BASED_VALUE(GLX_RENDER_TYPE, 0x2013, 0, 0, 0) - #define RGFW_GL_FULL_FORMAT RGFW_OS_BASED_VALUE(GLX_TRUE_COLOR, 0x2027, 0, 0, 0) - #define RGFW_GL_RED_SIZE RGFW_OS_BASED_VALUE(GLX_RED_SIZE, 0x2015, 0, 0, 0) - #define RGFW_GL_GREEN_SIZE RGFW_OS_BASED_VALUE(GLX_GREEN_SIZE, 0x2017, 0, 0, 0) - #define RGFW_GL_BLUE_SIZE RGFW_OS_BASED_VALUE(GLX_BLUE_SIZE, 0x2019, 0, 0, 0) - #define RGFW_GL_USE_RGBA RGFW_OS_BASED_VALUE(GLX_RGBA_BIT, 0x202B, 0, 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 */ - static u32* RGFW_initFormatAttribs(u32 useSoftware) { - RGFW_UNUSED(useSoftware); - static u32 attribs[] = { - #if defined(RGFW_X11) || defined(RGFW_WINDOWS) - RGFW_GL_RENDER_TYPE, - RGFW_GL_FULL_FORMAT, - #endif - RGFW_GL_ALPHA_SIZE , 8, - RGFW_GL_DEPTH_SIZE , 24, - #if defined(RGFW_X11) || defined(RGFW_WINDOWS) - RGFW_GL_DRAW, 1, - RGFW_GL_RED_SIZE , 8, - RGFW_GL_GREEN_SIZE , 8, - RGFW_GL_BLUE_SIZE , 8, - RGFW_GL_DRAW_TYPE , RGFW_GL_USE_RGBA, - #endif - - #ifdef RGFW_X11 - GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, - #endif - - #ifdef RGFW_MACOS - 72, - 8, 24, - #endif - - #ifdef RGFW_WINDOWS - WGL_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 - }; - - size_t index = (sizeof(attribs) / sizeof(attribs[0])) - 13; - - #define RGFW_GL_ADD_ATTRIB(attrib, attVal) \ - if (attVal) { \ - attribs[index] = attrib;\ - attribs[index + 1] = attVal;\ - index += 2;\ - } - - RGFW_GL_ADD_ATTRIB(RGFW_GL_DOUBLEBUFFER, 1); - - RGFW_GL_ADD_ATTRIB(RGFW_GL_STENCIL_SIZE, RGFW_STENCIL); - RGFW_GL_ADD_ATTRIB(RGFW_GL_STEREO, RGFW_STEREO); - RGFW_GL_ADD_ATTRIB(RGFW_GL_AUX_BUFFERS, RGFW_AUX_BUFFERS); - -#ifndef RGFW_X11 - RGFW_GL_ADD_ATTRIB(RGFW_GL_SAMPLES, RGFW_SAMPLES); -#endif - -#ifdef RGFW_MACOS - if (useSoftware) { - RGFW_GL_ADD_ATTRIB(70, kCGLRendererGenericFloatID); - } else { - attribs[index] = RGFW_GL_RENDER_TYPE; - index += 1; - } -#endif - -#ifdef RGFW_MACOS - /* 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_majorVersion >= 4 || RGFW_majorVersion >= 3) { - attribs[index + 1] = (u32) ((RGFW_majorVersion >= 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; - PFNEGLCHOOSECONFIGPROC eglChooseConfigSource; - PFNEGLCREATEWINDOWSURFACEPROC eglCreateWindowSurfaceSource; - PFNEGLCREATECONTEXTPROC eglCreateContextSource; - PFNEGLMAKECURRENTPROC eglMakeCurrentSource; - PFNEGLGETDISPLAYPROC eglGetDisplaySource; - PFNEGLSWAPBUFFERSPROC eglSwapBuffersSource; - PFNEGLSWAPINTERVALPROC eglSwapIntervalSource; - PFNEGLBINDAPIPROC eglBindAPISource; - PFNEGLDESTROYCONTEXTPROC eglDestroyContextSource; - PFNEGLTERMINATEPROC eglTerminateSource; - PFNEGLDESTROYSURFACEPROC eglDestroySurfaceSource; - -#define eglInitialize eglInitializeSource -#define eglGetConfigs eglGetConfigsSource -#define eglChooseConfig eglChooseConfigSource -#define eglCreateWindowSurface eglCreateWindowSurfaceSource -#define eglCreateContext eglCreateContextSource -#define eglMakeCurrent eglMakeCurrentSource -#define eglGetDisplay eglGetDisplaySource -#define eglSwapBuffers eglSwapBuffersSource -#define eglSwapInterval eglSwapIntervalSource -#define eglBindAPI eglBindAPISource -#define eglDestroyContext eglDestroyContextSource -#define eglTerminate eglTerminateSource -#define eglDestroySurface eglDestroySurfaceSource; -#endif - - -#define EGL_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_createOpenGLContext(RGFW_window* win) { -#if defined(RGFW_LINK_EGL) - eglInitializeSource = (PFNEGLINITIALIZEPROC) eglGetProcAddress("eglInitialize"); - eglGetConfigsSource = (PFNEGLGETCONFIGSPROC) eglGetProcAddress("eglGetConfigs"); - eglChooseConfigSource = (PFNEGLCHOOSECONFIGPROC) eglGetProcAddress("eglChooseConfig"); - eglCreateWindowSurfaceSource = (PFNEGLCREATEWINDOWSURFACEPROC) eglGetProcAddress("eglCreateWindowSurface"); - eglCreateContextSource = (PFNEGLCREATECONTEXTPROC) eglGetProcAddress("eglCreateContext"); - eglMakeCurrentSource = (PFNEGLMAKECURRENTPROC) eglGetProcAddress("eglMakeCurrent"); - eglGetDisplaySource = (PFNEGLGETDISPLAYPROC) eglGetProcAddress("eglGetDisplay"); - eglSwapBuffersSource = (PFNEGLSWAPBUFFERSPROC) eglGetProcAddress("eglSwapBuffers"); - eglSwapIntervalSource = (PFNEGLSWAPINTERVALPROC) eglGetProcAddress("eglSwapInterval"); - eglBindAPISource = (PFNEGLBINDAPIPROC) eglGetProcAddress("eglBindAPI"); - eglDestroyContextSource = (PFNEGLDESTROYCONTEXTPROC) eglGetProcAddress("eglDestroyContext"); - eglTerminateSource = (PFNEGLTERMINATEPROC) eglGetProcAddress("eglTerminate"); - eglDestroySurfaceSource = (PFNEGLDESTROYSURFACEPROC) eglGetProcAddress("eglDestroySurface"); -#endif /* RGFW_LINK_EGL */ - - #ifdef RGFW_WINDOWS - win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.hdc); - #elif defined(RGFW_MACOS) - win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType)0); - #else - 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[] = { - 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 - }; - - 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); - #else - win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL); - #endif - - EGLint attribs[] = { - EGL_CONTEXT_CLIENT_VERSION, - #ifdef RGFW_OPENGL_ES1 - 1, - #else - 2, - #endif - EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE - }; - - size_t index = 4; - RGFW_GL_ADD_ATTRIB(EGL_STENCIL_SIZE, RGFW_STENCIL); - RGFW_GL_ADD_ATTRIB(EGL_SAMPLES, RGFW_SAMPLES); - - if (RGFW_DOUBLE_BUFFER) - RGFW_GL_ADD_ATTRIB(EGL_RENDER_BUFFER, EGL_BACK_BUFFER); - - if (RGFW_majorVersion) { - attribs[1] = RGFW_majorVersion; - - RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MAJOR_VERSION, RGFW_majorVersion); - RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MINOR_VERSION, RGFW_minorVersion); - - if (RGFW_profile == RGFW_GL_CORE) { - 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); - } - - } - - #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) - fprintf(stderr, "failed to create an EGL opengl context\n"); - - 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); - } - - void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { - eglMakeCurrent(win->src.EGL_display, win->src.EGL_surface, win->src.EGL_surface, win->src.EGL_context); - } - - #ifdef RGFW_APPLE - void* RGFWnsglFramework = NULL; - #elif defined(RGFW_WINDOWS) - static HMODULE wglinstance = NULL; - #endif - - void* RGFW_getProcAddress(const char* procname) { - #if defined(RGFW_WINDOWS) - void* proc = (void*) GetProcAddress(wglinstance, procname); - - if (proc) - return proc; - #endif - - return (void*) eglGetProcAddress(procname); - } - - void RGFW_closeEGL(RGFW_window* win) { - eglDestroySurface(win->src.EGL_display, win->src.EGL_surface); - eglDestroyContext(win->src.EGL_display, win->src.EGL_context); - - eglTerminate(win->src.EGL_display); - } - - void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { - assert(win != NULL); - - eglSwapInterval(win->src.EGL_display, swapInterval); - - } -#endif /* RGFW_EGL */ - -/* - end of RGFW_EGL defines -*/ - -/* OPENGL Normal / EGL defines only (no OS MESA) Ends here */ - -#elif defined(RGFW_OSMESA) /* OSmesa only */ -RGFWDEF void RGFW_OSMesa_reorganize(void); - -/* reorganize buffer for osmesa */ -void RGFW_OSMesa_reorganize(void) { - u8* row = (u8*) RGFW_MALLOC(win->r.w * 3); - - i32 half_height = win->r.h / 2; - i32 stride = win->r.w * 3; - - i32 y; - for (y = 0; y < half_height; ++y) { - i32 top_offset = y * stride; - i32 bottom_offset = (win->r.h - y - 1) * stride; - memcpy(row, win->buffer + top_offset, stride); - memcpy(win->buffer + top_offset, win->buffer + bottom_offset, stride); - memcpy(win->buffer + bottom_offset, row, stride); - } - - RGFW_FREE(row); -} -#endif /* RGFW_OSMesa */ - -#endif /* RGFW_GL (OpenGL, EGL, OSMesa )*/ - -/* -This is where OS specific stuff starts -*/ - - -#if defined(RGFW_WAYLAND) || defined(RGFW_X11) - int RGFW_eventWait_forceStop[] = {0, 0, 0}; /* for wait events */ - - #ifdef __linux__ - #include - #include - #include - - RGFW_Event* RGFW_linux_updateJoystick(RGFW_window* win) { - static int xAxis = 0, yAxis = 0; - u8 i; - for (i = 0; i < RGFW_joystickCount; i++) { - struct js_event e; - - - if (RGFW_joysticks[i] == 0) - continue; - - i32 flags = fcntl(RGFW_joysticks[i], F_GETFL, 0); - fcntl(RGFW_joysticks[i], F_SETFL, flags | O_NONBLOCK); - - ssize_t bytes; - while ((bytes = read(RGFW_joysticks[i], &e, sizeof(e))) > 0) { - switch (e.type) { - case JS_EVENT_BUTTON: - win->event.type = e.value ? RGFW_jsButtonPressed : RGFW_jsButtonReleased; - win->event.button = e.number; - RGFW_jsPressed[i][e.number] = e.value; - RGFW_jsButtonCallback(win, i, e.number, e.value); - return &win->event; - case JS_EVENT_AXIS: - ioctl(RGFW_joysticks[i], JSIOCGAXES, &win->event.axisesCount); - - if ((e.number == 0 || e.number % 2) && e.number != 1) - xAxis = e.value; - else - yAxis = e.value; - - win->event.axis[e.number / 2].x = xAxis; - win->event.axis[e.number / 2].y = yAxis; - win->event.type = RGFW_jsAxisMove; - win->event.joystick = i; - RGFW_jsAxisCallback(win, i, win->event.axis, win->event.axisesCount); - return &win->event; - - default: break; - } - } - } - - return NULL; - } - - #endif -#endif - -/* - - -Start of Linux / Unix defines - - -*/ - -#ifdef RGFW_X11 -#ifndef RGFW_NO_X11_CURSOR -#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 - - -#ifdef __linux__ -#include -#endif - - u8 RGFW_mouseIconSrc[] = { 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}; - /*atoms needed for drag and drop*/ - Atom XdndAware, XdndTypeList, XdndSelection, XdndEnter, XdndPosition, XdndStatus, XdndLeave, XdndDrop, XdndFinished, XdndActionCopy, XtextPlain, XtextUriList; - - Atom wm_delete_window = 0; - -#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) - typedef XcursorImage* (*PFN_XcursorImageCreate)(int, int); - typedef void (*PFN_XcursorImageDestroy)(XcursorImage*); - typedef Cursor(*PFN_XcursorImageLoadCursor)(Display*, const XcursorImage*); -#endif -#ifdef RGFW_OPENGL - typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); -#endif - -#if !defined(RGFW_NO_X11_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_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) - PFN_XcursorImageLoadCursor XcursorImageLoadCursorSrc = NULL; - PFN_XcursorImageCreate XcursorImageCreateSrc = NULL; - PFN_XcursorImageDestroy XcursorImageDestroySrc = NULL; - -#define XcursorImageLoadCursor XcursorImageLoadCursorSrc -#define XcursorImageCreate XcursorImageCreateSrc -#define XcursorImageDestroy XcursorImageDestroySrc - - void* X11Cursorhandle = NULL; -#endif - - u32 RGFW_windowsOpen = 0; - -#ifdef RGFW_OPENGL - void* RGFW_getProcAddress(const char* procname) { return (void*) glXGetProcAddress((GLubyte*) procname); } -#endif - - RGFWDEF void RGFW_init_buffer(RGFW_window* win, XVisualInfo* vi); - void RGFW_init_buffer(RGFW_window* win, XVisualInfo* vi) { -#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0) - RGFW_bufferSize = RGFW_getScreenSize(); - - win->buffer = (u8*)RGFW_MALLOC(RGFW_bufferSize.w * RGFW_bufferSize.h * 4); - - #ifdef RGFW_OSMESA - win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL); - OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h); - #endif - - win->src.bitmap = XCreateImage( - win->src.display, XDefaultVisual(win->src.display, vi->screen), - vi->depth, - ZPixmap, 0, NULL, RGFW_bufferSize.w, RGFW_bufferSize.h, - 32, 0 - ); - - win->src.gc = XCreateGC(win->src.display, win->src.window, 0, NULL); - - #else - RGFW_UNUSED(win); /*!< if buffer rendering is not being used */ - RGFW_UNUSED(vi) - #endif - } - - - - void RGFW_window_setBorder(RGFW_window* win, u8 border) { - static Atom _MOTIF_WM_HINTS = 0; - if (_MOTIF_WM_HINTS == 0 ) - _MOTIF_WM_HINTS = XInternAtom(win->src.display, "_MOTIF_WM_HINTS", False); - - struct __x11WindowHints { - unsigned long flags, functions, decorations, status; - long input_mode; - } hints; - hints.flags = (1L << 1); - hints.decorations = border; - - XChangeProperty( - win->src.display, win->src.window, - _MOTIF_WM_HINTS, _MOTIF_WM_HINTS, - 32, PropModeReplace, (u8*)&hints, 5 - ); - } - - void RGFW_releaseCursor(RGFW_window* win) { - 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); - } - - void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { - /* 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))); - } - - RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) { -#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) - if (X11Cursorhandle == NULL) { -#if defined(__CYGWIN__) - X11Cursorhandle = dlopen("libXcursor-1.so", RTLD_LAZY | RTLD_LOCAL); -#elif defined(__OpenBSD__) || defined(__NetBSD__) - X11Cursorhandle = dlopen("libXcursor.so", RTLD_LAZY | RTLD_LOCAL); -#else - X11Cursorhandle = dlopen("libXcursor.so.1", RTLD_LAZY | RTLD_LOCAL); -#endif - - XcursorImageCreateSrc = (PFN_XcursorImageCreate) dlsym(X11Cursorhandle, "XcursorImageCreate"); - XcursorImageDestroySrc = (PFN_XcursorImageDestroy) dlsym(X11Cursorhandle, "XcursorImageDestroy"); - XcursorImageLoadCursorSrc = (PFN_XcursorImageLoadCursor) dlsym(X11Cursorhandle, "XcursorImageLoadCursor"); - } -#endif - -#if !defined(RGFW_NO_X11_XI_PRELOAD) - if (X11Xihandle == NULL) { -#if defined(__CYGWIN__) - X11Xihandle = dlopen("libXi-6.so", RTLD_LAZY | RTLD_LOCAL); -#elif defined(__OpenBSD__) || defined(__NetBSD__) - X11Xihandle = dlopen("libXi.so", RTLD_LAZY | RTLD_LOCAL); -#else - X11Xihandle = dlopen("libXi.so.6", RTLD_LAZY | RTLD_LOCAL); -#endif - - XISelectEventsSrc = (PFN_XISelectEvents) dlsym(X11Xihandle, "XISelectEvents"); - } -#endif - - XInitThreads(); /*!< init X11 threading*/ - - if (args & RGFW_OPENGL_SOFTWARE) - setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1); - - RGFW_window* win = RGFW_window_basic_init(rect, args); - - u64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask | LeaveWindowMask | EnterWindowMask | ExposureMask; /*!< X11 events accepted*/ - -#ifdef RGFW_OPENGL - u32* visual_attribs = RGFW_initFormatAttribs(args & RGFW_OPENGL_SOFTWARE); - i32 fbcount; - GLXFBConfig* fbc = glXChooseFBConfig((Display*) win->src.display, DefaultScreen(win->src.display), (i32*) visual_attribs, &fbcount); - - i32 best_fbc = -1; - - if (fbcount == 0) { - printf("Failed to find any valid GLX visual configs\n"); - return NULL; - } - - u32 i; - for (i = 0; i < (u32)fbcount; i++) { - XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) win->src.display, fbc[i]); - if (vi == NULL) - continue; - - XFree(vi); - - i32 samp_buf, samples; - glXGetFBConfigAttrib((Display*) win->src.display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf); - glXGetFBConfigAttrib((Display*) win->src.display, fbc[i], GLX_SAMPLES, &samples); - - if ((!(args & RGFW_TRANSPARENT_WINDOW) || vi->depth == 32) && - (best_fbc < 0 || samp_buf) && (samples == RGFW_SAMPLES || best_fbc == -1)) { - best_fbc = i; - } - } - - if (best_fbc == -1) { - printf("Failed to get a valid GLX visual\n"); - return NULL; - } - - GLXFBConfig bestFbc = fbc[best_fbc]; - - /* Get a visual */ - XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) win->src.display, bestFbc); - - XFree(fbc); -#else - XVisualInfo viNorm; - - viNorm.visual = DefaultVisual((Display*) win->src.display, DefaultScreen((Display*) win->src.display)); - - viNorm.depth = 0; - XVisualInfo* vi = &viNorm; - - XMatchVisualInfo((Display*) win->src.display, DefaultScreen((Display*) win->src.display), 32, TrueColor, vi); /*!< for RGBA backgrounds*/ -#endif - /* make X window attrubutes*/ - XSetWindowAttributes swa; - Colormap cmap; - - swa.colormap = cmap = XCreateColormap((Display*) win->src.display, - DefaultRootWindow(win->src.display), - vi->visual, AllocNone); - - swa.background_pixmap = None; - swa.border_pixel = 0; - swa.event_mask = event_mask; - - swa.background_pixel = 0; - - /* create the window*/ - win->src.window = XCreateWindow((Display*) win->src.display, DefaultRootWindow((Display*) win->src.display), win->r.x, win->r.y, win->r.w, win->r.h, - 0, vi->depth, InputOutput, vi->visual, - CWColormap | CWBorderPixel | CWBackPixel | CWEventMask, &swa); - - XFreeColors((Display*) win->src.display, cmap, NULL, 0, 0); - - #ifdef RGFW_OPENGL - XFree(vi); - #endif - - // 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 = XAllocClassHint(); - assert(hint != NULL); - hint->res_class = (char*)RGFW_className; - hint->res_name = (char*)name; // just use the window name as the app name - XSetClassHint((Display*) win->src.display, win->src.window, hint); - XFree(hint); - - if ((args & RGFW_NO_INIT_API) == 0) { -#ifdef RGFW_OPENGL /* This is the second part of setting up opengl. This is where we ask OpenGL for a specific version. */ - i32 context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 }; - context_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB; - if (RGFW_profile == RGFW_GL_CORE) - context_attribs[1] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB; - else - context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; - - if (RGFW_majorVersion || RGFW_minorVersion) { - context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB; - context_attribs[3] = RGFW_majorVersion; - context_attribs[4] = GLX_CONTEXT_MINOR_VERSION_ARB; - context_attribs[5] = RGFW_minorVersion; - } - - glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; - glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) - glXGetProcAddressARB((GLubyte*) "glXCreateContextAttribsARB"); - - GLXContext ctx = NULL; - - if (RGFW_root != NULL) - ctx = RGFW_root->src.ctx; - - win->src.ctx = glXCreateContextAttribsARB((Display*) win->src.display, bestFbc, ctx, True, context_attribs); -#endif - if (RGFW_root == NULL) - RGFW_root = win; - - RGFW_init_buffer(win, vi); - } - - - #ifndef RGFW_NO_MONITOR - if (args & RGFW_SCALE_TO_MONITOR) - RGFW_window_scaleToMonitor(win); - #endif - - if (args & RGFW_CENTER) { - RGFW_area screenR = RGFW_getScreenSize(); - RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2)); - } - - if (args & RGFW_NO_RESIZE) { /* make it so the user can't resize the window*/ - XSizeHints* sh = XAllocSizeHints(); - sh->flags = (1L << 4) | (1L << 5); - sh->min_width = sh->max_width = win->r.w; - sh->min_height = sh->max_height = win->r.h; - - XSetWMSizeHints((Display*) win->src.display, (Drawable) win->src.window, sh, XA_WM_NORMAL_HINTS); - XFree(sh); - } - - if (args & RGFW_NO_BORDER) { - RGFW_window_setBorder(win, 0); - } - - XSelectInput((Display*) win->src.display, (Drawable) win->src.window, event_mask); /*!< tell X11 what events we want*/ - - /* make it so the user can't close the window until the program does*/ - if (wm_delete_window == 0) - wm_delete_window = XInternAtom((Display*) win->src.display, "WM_DELETE_WINDOW", False); - - XSetWMProtocols((Display*) win->src.display, (Drawable) win->src.window, &wm_delete_window, 1); - - /* connect the context to the window*/ -#ifdef RGFW_OPENGL - if ((args & RGFW_NO_INIT_API) == 0) - glXMakeCurrent((Display*) win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx); -#endif - - /* set the background*/ - XStoreName((Display*) win->src.display, (Drawable) win->src.window, name); /*!< set the name*/ - - XMapWindow((Display*) win->src.display, (Drawable) win->src.window); /* draw the window*/ - XMoveWindow((Display*) win->src.display, (Drawable) win->src.window, win->r.x, win->r.y); /*!< move the window to it's proper cords*/ - - if (args & RGFW_ALLOW_DND) { /* init drag and drop atoms and turn on drag and drop for this window */ - win->_winArgs |= RGFW_ALLOW_DND; - - XdndTypeList = XInternAtom((Display*) win->src.display, "XdndTypeList", False); - XdndSelection = XInternAtom((Display*) win->src.display, "XdndSelection", False); - - /* client messages */ - XdndEnter = XInternAtom((Display*) win->src.display, "XdndEnter", False); - XdndPosition = XInternAtom((Display*) win->src.display, "XdndPosition", False); - XdndStatus = XInternAtom((Display*) win->src.display, "XdndStatus", False); - XdndLeave = XInternAtom((Display*) win->src.display, "XdndLeave", False); - XdndDrop = XInternAtom((Display*) win->src.display, "XdndDrop", False); - XdndFinished = XInternAtom((Display*) win->src.display, "XdndFinished", False); - - /* actions */ - XdndActionCopy = XInternAtom((Display*) win->src.display, "XdndActionCopy", False); - - XtextUriList = XInternAtom((Display*) win->src.display, "text/uri-list", False); - XtextPlain = XInternAtom((Display*) win->src.display, "text/plain", False); - - XdndAware = XInternAtom((Display*) win->src.display, "XdndAware", False); - const u8 version = 5; - - XChangeProperty((Display*) win->src.display, (Window) win->src.window, - XdndAware, 4, 32, - PropModeReplace, &version, 1); /*!< turns on drag and drop */ - } - - #ifdef RGFW_EGL - if ((args & RGFW_NO_INIT_API) == 0) - RGFW_createOpenGLContext(win); - #endif - - RGFW_window_setMouseDefault(win); - - RGFW_windowsOpen++; - - return win; /*return newly created window*/ - } - - RGFW_area RGFW_getScreenSize(void) { - assert(RGFW_root != NULL); - - Screen* scrn = DefaultScreenOfDisplay((Display*) RGFW_root->src.display); - return RGFW_AREA(scrn->width, scrn->height); - } - - RGFW_point RGFW_getGlobalMousePoint(void) { - assert(RGFW_root != NULL); - - RGFW_point RGFWMouse; - - i32 x, y; - u32 z; - Window window1, window2; - XQueryPointer((Display*) RGFW_root->src.display, XDefaultRootWindow((Display*) RGFW_root->src.display), &window1, &window2, &RGFWMouse.x, &RGFWMouse.y, &x, &y, &z); - - return RGFWMouse; - } - - RGFW_point RGFW_window_getMousePoint(RGFW_window* win) { - assert(win != NULL); - - RGFW_point RGFWMouse; - - i32 x, y; - u32 z; - Window window1, window2; - XQueryPointer((Display*) win->src.display, win->src.window, &window1, &window2, &x, &y, &RGFWMouse.x, &RGFWMouse.y, &z); - - return RGFWMouse; - } - - int xAxis = 0, yAxis = 0; - - RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) { - assert(win != NULL); - - static struct { - long source, version; - i32 format; - } xdnd; - - if (win->event.type == 0) - RGFW_resetKey(); - - if (win->event.type == RGFW_quit) { - return NULL; - } - - win->event.type = 0; - -#ifdef __linux__ - RGFW_Event* event = RGFW_linux_updateJoystick(win); - if (event != NULL) - return event; -#endif - - 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((Display*) win->src.display, QueuedAlready) + XEventsQueued((Display*) win->src.display, QueuedAfterReading)) - && win->event.type != RGFW_quit - ) - XNextEvent((Display*) win->src.display, &E); - else { - return NULL; - } - - u32 i; - win->event.type = 0; - - - 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((Display*) win->src.display, QueuedAfterReading)) { /* get next event if there is one*/ - XEvent NE; - XPeekEvent((Display*) win->src.display, &NE); - - if (E.xkey.time == NE.xkey.time && E.xkey.keycode == NE.xkey.keycode) /* check if the current and next are both the same*/ - win->event.repeat = RGFW_TRUE; - } - - /* set event key data */ - KeySym sym = (KeySym)XkbKeycodeToKeysym((Display*) win->src.display, E.xkey.keycode, 0, E.xkey.state & ShiftMask ? 1 : 0); - win->event.keyCode = RGFW_apiKeyCodeToRGFW(E.xkey.keycode); - - char* str = (char*)XKeysymToString(sym); - if (str != NULL) - strncpy(win->event.keyName, str, 16); - - win->event.keyName[15] = '\0'; - - RGFW_keyboard[win->event.keyCode].prev = RGFW_isPressed(win, win->event.keyCode); - - /* get keystate data */ - win->event.type = (E.type == KeyPress) ? RGFW_keyPressed : RGFW_keyReleased; - - XKeyboardState keystate; - XGetKeyboardControl((Display*) win->src.display, &keystate); - - RGFW_updateLockState(win, (keystate.led_mask & 1), (keystate.led_mask & 2)); - RGFW_keyboard[win->event.keyCode].current = (E.type == KeyPress); - RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, (E.type == KeyPress)); - break; - } - case ButtonPress: - case ButtonRelease: - win->event.type = RGFW_mouseButtonPressed + (E.type == ButtonRelease); // the events match - - switch(win->event.button) { - case RGFW_mouseScrollUp: - win->event.scroll = 1; - break; - case RGFW_mouseScrollDown: - win->event.scroll = -1; - break; - default: break; - } - - win->event.button = E.xbutton.button; - 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.keyCode); - - 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; - - if ((win->_winArgs & RGFW_HOLD_MOUSE)) { - win->event.point.y = E.xmotion.y; - - win->event.point.x = win->_lastMousePoint.x - abs(win->event.point.x); - win->event.point.y = win->_lastMousePoint.y - abs(win->event.point.y); - } - - win->_lastMousePoint = RGFW_POINT(E.xmotion.x, E.xmotion.y); - - win->event.type = RGFW_mousePosChanged; - RGFW_mousePosCallback(win, win->event.point); - break; - - case GenericEvent: { - /* MotionNotify is used for mouse events if the mouse isn't held */ - if (!(win->_winArgs & 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.point = RGFW_POINT((i32)deltaX, (i32)deltaY); - - 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); - } - - XFreeEventData(win->src.display, &E.xcookie); - break; - } - - case Expose: - win->event.type = RGFW_windowRefresh; - RGFW_windowRefreshCallback(win); - break; - - case ClientMessage: - /* if the client closed the window*/ - if (E.xclient.data.l[0] == (i64) wm_delete_window) { - win->event.type = RGFW_quit; - RGFW_windowQuitCallback(win); - break; - } - - /* reset DND values */ - if (win->event.droppedFilesCount) { - for (i = 0; i < win->event.droppedFilesCount; i++) - win->event.droppedFiles[i][0] = '\0'; - } - - win->event.droppedFilesCount = 0; - - if ((win->_winArgs & RGFW_ALLOW_DND) == 0) - break; - - XEvent reply = { ClientMessage }; - reply.xclient.window = xdnd.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) { - unsigned long count; - Atom* formats; - Atom real_formats[6]; - - Bool list = E.xclient.data.l[1] & 1; - - xdnd.source = E.xclient.data.l[0]; - xdnd.version = E.xclient.data.l[1] >> 24; - xdnd.format = None; - - if (xdnd.version > 5) - break; - - if (list) { - Atom actualType; - i32 actualFormat; - unsigned long bytesAfter; - - XGetWindowProperty((Display*) win->src.display, - xdnd.source, - XdndTypeList, - 0, - LONG_MAX, - False, - 4, - &actualType, - &actualFormat, - &count, - &bytesAfter, - (u8**) &formats); - } else { - count = 0; - - if (E.xclient.data.l[2] != None) - real_formats[count++] = E.xclient.data.l[2]; - if (E.xclient.data.l[3] != None) - real_formats[count++] = E.xclient.data.l[3]; - if (E.xclient.data.l[4] != None) - real_formats[count++] = E.xclient.data.l[4]; - - formats = real_formats; - } - - unsigned long i; - for (i = 0; i < count; i++) { - if (formats[i] == XtextUriList || formats[i] == XtextPlain) { - xdnd.format = 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 (xdnd.version > 5) - break; - - XTranslateCoordinates((Display*) win->src.display, - XDefaultRootWindow((Display*) win->src.display), - (Window) win->src.window, - xabs, yabs, - &xpos, &ypos, - &dummy); - - win->event.point.x = xpos; - win->event.point.y = ypos; - - reply.xclient.window = xdnd.source; - reply.xclient.message_type = XdndStatus; - - if (xdnd.format) { - reply.xclient.data.l[1] = 1; - if (xdnd.version >= 2) - reply.xclient.data.l[4] = XdndActionCopy; - } - - XSendEvent((Display*) win->src.display, xdnd.source, False, NoEventMask, &reply); - XFlush((Display*) win->src.display); - break; - } - - if (E.xclient.message_type != XdndDrop) - break; - - if (xdnd.version > 5) - break; - - win->event.type = RGFW_dnd_init; - - if (xdnd.format) { - Time time = CurrentTime; - - if (xdnd.version >= 1) - time = E.xclient.data.l[2]; - - XConvertSelection((Display*) win->src.display, - XdndSelection, - xdnd.format, - XdndSelection, - (Window) win->src.window, - time); - } else if (xdnd.version >= 2) { - XEvent reply = { ClientMessage }; - - XSendEvent((Display*) win->src.display, xdnd.source, - False, NoEventMask, &reply); - XFlush((Display*) win->src.display); - } - - RGFW_dndInitCallback(win, win->event.point); - break; - case SelectionNotify: { - /* this is only for checking for xdnd drops */ - if (E.xselection.property != XdndSelection || !(win->_winArgs | RGFW_ALLOW_DND)) - break; - - char* data; - unsigned long result; - - Atom actualType; - i32 actualFormat; - unsigned long bytesAfter; - - XGetWindowProperty((Display*) win->src.display, E.xselection.requestor, E.xselection.property, 0, LONG_MAX, False, E.xselection.target, &actualType, &actualFormat, &result, &bytesAfter, (u8**) &data); - - if (result == 0) - break; - - /* - SOURCED FROM GLFW _glfwParseUriList - Copyright (c) 2002-2006 Marcus Geelnard - Copyright (c) 2006-2019 Camilla Löwy - */ - - const char* prefix = (const char*)"file://"; - - char* line; - - win->event.droppedFilesCount = 0; - - win->event.type = RGFW_dnd; - - while ((line = strtok(data, "\r\n"))) { - char path[RGFW_MAX_PATH]; - - data = NULL; - - if (line[0] == '#') - continue; - - char* l; - for (l = line; 1; l++) { - if ((l - line) > 7) - break; - else if (*l != prefix[(l - line)]) - break; - else if (*l == '\0' && prefix[(l - line)] == '\0') { - line += 7; - while (*line != '/') - line++; - break; - } else if (*l == '\0') - break; - } - - win->event.droppedFilesCount++; - - size_t index = 0; - while (*line) { - if (line[0] == '%' && line[1] && line[2]) { - const char digits[3] = { line[1], line[2], '\0' }; - path[index] = (char) strtol(digits, NULL, 16); - line += 2; - } else - path[index] = *line; - - index++; - line++; - } - path[index] = '\0'; - strncpy(win->event.droppedFiles[win->event.droppedFilesCount - 1], path, index + 1); - } - - if (data) - XFree(data); - - if (xdnd.version >= 2) { - reply.xclient.message_type = XdndFinished; - reply.xclient.data.l[1] = result; - reply.xclient.data.l[2] = XdndActionCopy; - - XSendEvent((Display*) win->src.display, xdnd.source, False, NoEventMask, &reply); - XFlush((Display*) win->src.display); - } - - RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount); - break; - } - case FocusIn: - win->event.inFocus = 1; - win->event.type = RGFW_focusIn; - RGFW_focusCallback(win, 1); - break; - - break; - case FocusOut: - win->event.inFocus = 0; - win->event.type = RGFW_focusOut; - RGFW_focusCallback(win, 0); - 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 */ - if (E.xconfigure.width != win->r.w || E.xconfigure.height != win->r.h) { - win->event.type = RGFW_windowResized; - win->r = RGFW_RECT(win->r.x, win->r.y, E.xconfigure.width, E.xconfigure.height); - RGFW_windowResizeCallback(win, win->r); - break; - } - - /* detect move */ - if (E.xconfigure.x != win->r.x || E.xconfigure.y != win->r.y) { - win->event.type = RGFW_windowMoved; - win->r = RGFW_RECT(E.xconfigure.x, E.xconfigure.y, win->r.w, win->r.h); - RGFW_windowMoveCallback(win, win->r); - break; - } - - break; - } - default: { - break; - } - } - - XFlush((Display*) win->src.display); - - if (win->event.type) - return &win->event; - else - return NULL; - } - - void RGFW_window_move(RGFW_window* win, RGFW_point v) { - assert(win != NULL); - win->r.x = v.x; - win->r.y = v.y; - - XMoveWindow((Display*) win->src.display, (Window) win->src.window, v.x, v.y); - } - - - void RGFW_window_resize(RGFW_window* win, RGFW_area a) { - assert(win != NULL); - win->r.w = a.w; - win->r.h = a.h; - - XResizeWindow((Display*) win->src.display, (Window) win->src.window, a.w, a.h); - } - - void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { - assert(win != NULL); - - if (a.w == 0 && a.h == 0) - return; - - XSizeHints hints; - long flags; - - XGetWMNormalHints(win->src.display, (Window) win->src.window, &hints, &flags); - - hints.flags |= PMinSize; - - hints.min_width = a.w; - hints.min_height = a.h; - - XSetWMNormalHints(win->src.display, (Window) win->src.window, &hints); - } - - void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { - assert(win != NULL); - - if (a.w == 0 && a.h == 0) - return; - - XSizeHints hints; - long flags; - - XGetWMNormalHints(win->src.display, (Window) win->src.window, &hints, &flags); - - hints.flags |= PMaxSize; - - hints.max_width = a.w; - hints.max_height = a.h; - - XSetWMNormalHints(win->src.display, (Window) win->src.window, &hints); - } - - - void RGFW_window_minimize(RGFW_window* win) { - assert(win != NULL); - - XIconifyWindow(win->src.display, (Window) win->src.window, DefaultScreen(win->src.display)); - XFlush(win->src.display); - } - - void RGFW_window_restore(RGFW_window* win) { - assert(win != NULL); - - XMapWindow(win->src.display, (Window) win->src.window); - XFlush(win->src.display); - } - - void RGFW_window_setName(RGFW_window* win, char* name) { - assert(win != NULL); - - XStoreName((Display*) win->src.display, (Window) win->src.window, name); - } - - void* RGFW_libxshape = NULL; - - #ifndef RGFW_NO_PASSTHROUGH - void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) { - assert(win != NULL); - - #if defined(__CYGWIN__) - RGFW_libxshape = dlopen("libXext-6.so", RTLD_LAZY | RTLD_LOCAL); - #elif defined(__OpenBSD__) || defined(__NetBSD__) - RGFW_libxshape = dlopen("libXext.so", RTLD_LAZY | RTLD_LOCAL); - #else - RGFW_libxshape = dlopen("libXext.so.6", RTLD_LAZY | RTLD_LOCAL); - #endif - - typedef void (* PFN_XShapeCombineMask)(Display*,Window,int,int,int,Pixmap,int); - static PFN_XShapeCombineMask XShapeCombineMask; - - typedef void (* PFN_XShapeCombineRegion)(Display*,Window,int,int,int,Region,int); - static PFN_XShapeCombineRegion XShapeCombineRegion; - - if (XShapeCombineMask != NULL) - XShapeCombineMask = (PFN_XShapeCombineMask) dlsym(RGFW_libxshape, "XShapeCombineMask"); - - if (XShapeCombineRegion != NULL) - XShapeCombineRegion = (PFN_XShapeCombineRegion) dlsym(RGFW_libxshape, "XShapeCombineMask"); - - 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 - - /* - the majority function is sourced from GLFW - */ - - void RGFW_window_setIcon(RGFW_window* win, u8* icon, RGFW_area a, i32 channels) { - assert(win != NULL); - - i32 longCount = 2 + a.w * a.h; - - u64* X11Icon = (u64*) RGFW_MALLOC(longCount * sizeof(u64)); - u64* target = X11Icon; - - *target++ = a.w; - *target++ = a.h; - - u32 i; - - for (i = 0; i < a.w * a.h; i++) { - if (channels == 3) - *target++ = ((icon[i * 3 + 0]) << 16) | - ((icon[i * 3 + 1]) << 8) | - ((icon[i * 3 + 2]) << 0) | - (0xFF << 24); - - else if (channels == 4) - *target++ = ((icon[i * 4 + 0]) << 16) | - ((icon[i * 4 + 1]) << 8) | - ((icon[i * 4 + 2]) << 0) | - ((icon[i * 4 + 3]) << 24); - } - - static Atom NET_WM_ICON = 0; - if (NET_WM_ICON == 0) - NET_WM_ICON = XInternAtom((Display*) win->src.display, "_NET_WM_ICON", False); - - XChangeProperty((Display*) win->src.display, (Window) win->src.window, - NET_WM_ICON, - 6, 32, - PropModeReplace, - (u8*) X11Icon, - longCount); - - RGFW_FREE(X11Icon); - - XFlush((Display*) win->src.display); - } - - void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) { - assert(win != NULL); - -#ifndef RGFW_NO_X11_CURSOR - XcursorImage* native = XcursorImageCreate(a.w, a.h); - native->xhot = 0; - native->yhot = 0; - - u8* source = (u8*) image; - XcursorPixel* target = native->pixels; - - u32 i; - for (i = 0; i < a.w * a.h; i++, target++, source += 4) { - u8 alpha = 0xFF; - if (channels == 4) - alpha = source[3]; - - *target = (alpha << 24) | (((source[0] * alpha) / 255) << 16) | (((source[1] * alpha) / 255) << 8) | (((source[2] * alpha) / 255) << 0); - } - - Cursor cursor = XcursorImageLoadCursor((Display*) win->src.display, native); - XDefineCursor((Display*) win->src.display, (Window) win->src.window, (Cursor) cursor); - - XFreeCursor((Display*) win->src.display, (Cursor) cursor); - XcursorImageDestroy(native); -#else - RGFW_UNUSED(image) RGFW_UNUSED(a.w) RGFW_UNUSED(channels) -#endif - } - - void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) { - assert(win != NULL); - - XEvent event; - XQueryPointer(win->src.display, DefaultRootWindow(win->src.display), - &event.xbutton.root, &event.xbutton.window, - &event.xbutton.x_root, &event.xbutton.y_root, - &event.xbutton.x, &event.xbutton.y, - &event.xbutton.state); - - if (event.xbutton.x == v.x && event.xbutton.y == v.y) - return; - - XWarpPointer(win->src.display, None, win->src.window, 0, 0, 0, 0, (int) v.x - win->r.x, (int) v.y - win->r.y); - } - - RGFWDEF void RGFW_window_disableMouse(RGFW_window* win) { - RGFW_UNUSED(win); - } - - void RGFW_window_setMouseDefault(RGFW_window* win) { - RGFW_window_setMouseStandard(win, RGFW_MOUSE_ARROW); - } - - void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { - assert(win != NULL); - - if (mouse > (sizeof(RGFW_mouseIconSrc) / sizeof(u8))) - return; - - mouse = RGFW_mouseIconSrc[mouse]; - - Cursor cursor = XCreateFontCursor((Display*) win->src.display, mouse); - XDefineCursor((Display*) win->src.display, (Window) win->src.window, (Cursor) cursor); - - XFreeCursor((Display*) win->src.display, (Cursor) cursor); - } - - void RGFW_window_hide(RGFW_window* win) { - XMapWindow(win->src.display, win->src.window); - } - - void RGFW_window_show(RGFW_window* win) { - XUnmapWindow(win->src.display, win->src.window); - } - - /* - the majority function is sourced from GLFW - */ - char* RGFW_readClipboard(size_t* size) { - static Atom UTF8 = 0; - if (UTF8 == 0) - UTF8 = XInternAtom(RGFW_root->src.display, "UTF8_STRING", True); - - XEvent event; - int format; - unsigned long N, sizeN; - char* data, * s = NULL; - Atom target; - Atom CLIPBOARD = 0, XSEL_DATA = 0; - - if (CLIPBOARD == 0) { - CLIPBOARD = XInternAtom(RGFW_root->src.display, "CLIPBOARD", 0); - XSEL_DATA = XInternAtom(RGFW_root->src.display, "XSEL_DATA", 0); - } - - XConvertSelection(RGFW_root->src.display, CLIPBOARD, UTF8, XSEL_DATA, RGFW_root->src.window, CurrentTime); - XSync(RGFW_root->src.display, 0); - XNextEvent(RGFW_root->src.display, &event); - - if (event.type != SelectionNotify || event.xselection.selection != CLIPBOARD || event.xselection.property == 0) - return NULL; - - XGetWindowProperty(event.xselection.display, event.xselection.requestor, - event.xselection.property, 0L, (~0L), 0, AnyPropertyType, &target, - &format, &sizeN, &N, (unsigned char**) &data); - - if (target == UTF8 || target == XA_STRING) { - s = (char*)RGFW_MALLOC(sizeof(char) * sizeN); - strncpy(s, data, sizeN); - s[sizeN] = '\0'; - XFree(data); - } - - XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property); - - if (s != NULL && size != NULL) - *size = sizeN; - - return s; - } - - /* - almost all of this function is sourced from GLFW - */ - void RGFW_writeClipboard(const char* text, u32 textLen) { - static Atom CLIPBOARD = 0, - UTF8_STRING = 0, - SAVE_TARGETS = 0, - TARGETS = 0, - MULTIPLE = 0, - ATOM_PAIR = 0, - CLIPBOARD_MANAGER = 0; - - if (CLIPBOARD == 0) { - CLIPBOARD = XInternAtom((Display*) RGFW_root->src.display, "CLIPBOARD", False); - UTF8_STRING = XInternAtom((Display*) RGFW_root->src.display, "UTF8_STRING", False); - SAVE_TARGETS = XInternAtom((Display*) RGFW_root->src.display, "SAVE_TARGETS", False); - TARGETS = XInternAtom((Display*) RGFW_root->src.display, "TARGETS", False); - MULTIPLE = XInternAtom((Display*) RGFW_root->src.display, "MULTIPLE", False); - ATOM_PAIR = XInternAtom((Display*) RGFW_root->src.display, "ATOM_PAIR", False); - CLIPBOARD_MANAGER = XInternAtom((Display*) RGFW_root->src.display, "CLIPBOARD_MANAGER", False); - } - - XSetSelectionOwner((Display*) RGFW_root->src.display, CLIPBOARD, (Window) RGFW_root->src.window, CurrentTime); - - XConvertSelection((Display*) RGFW_root->src.display, CLIPBOARD_MANAGER, SAVE_TARGETS, None, (Window) RGFW_root->src.window, CurrentTime); - for (;;) { - XEvent event; - - XNextEvent((Display*) RGFW_root->src.display, &event); - if (event.type != SelectionRequest) { - break; - } - - const XSelectionRequestEvent* request = &event.xselectionrequest; - - XEvent reply = { SelectionNotify }; - reply.xselection.property = 0; - - if (request->target == TARGETS) { - const Atom targets[] = { TARGETS, - MULTIPLE, - UTF8_STRING, - XA_STRING }; - - XChangeProperty((Display*) RGFW_root->src.display, - request->requestor, - request->property, - 4, - 32, - PropModeReplace, - (u8*) targets, - sizeof(targets) / sizeof(targets[0])); - - reply.xselection.property = request->property; - } - - if (request->target == MULTIPLE) { - Atom* targets = NULL; - - Atom actualType = 0; - int actualFormat = 0; - unsigned long count = 0, bytesAfter = 0; - - XGetWindowProperty((Display*) RGFW_root->src.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((Display*) RGFW_root->src.display, - request->requestor, - targets[i + 1], - targets[i], - 8, - PropModeReplace, - (u8*) text, - textLen); - XFlush(RGFW_root->src.display); - } else { - targets[i + 1] = None; - } - } - - XChangeProperty((Display*) RGFW_root->src.display, - request->requestor, - request->property, - ATOM_PAIR, - 32, - PropModeReplace, - (u8*) targets, - count); - - XFlush(RGFW_root->src.display); - XFree(targets); - - reply.xselection.property = request->property; - } - - reply.xselection.display = request->display; - reply.xselection.requestor = request->requestor; - reply.xselection.selection = request->selection; - reply.xselection.target = request->target; - reply.xselection.time = request->time; - - XSendEvent((Display*) RGFW_root->src.display, request->requestor, False, 0, &reply); - XFlush(RGFW_root->src.display); - } - } - - u8 RGFW_window_isFullscreen(RGFW_window* win) { - assert(win != NULL); - - XWindowAttributes windowAttributes; - XGetWindowAttributes(win->src.display, (Window) win->src.window, &windowAttributes); - - /* check if the window is visable */ - if (windowAttributes.map_state != IsViewable) - return 0; - - /* check if the window covers the full screen */ - return (windowAttributes.x == 0 && windowAttributes.y == 0 && - windowAttributes.width == XDisplayWidth(win->src.display, DefaultScreen(win->src.display)) && - windowAttributes.height == XDisplayHeight(win->src.display, DefaultScreen(win->src.display))); - } - - u8 RGFW_window_isHidden(RGFW_window* win) { - assert(win != NULL); - - XWindowAttributes windowAttributes; - XGetWindowAttributes(win->src.display, (Window) win->src.window, &windowAttributes); - - return (windowAttributes.map_state == IsUnmapped && !RGFW_window_isMinimized(win)); - } - - u8 RGFW_window_isMinimized(RGFW_window* win) { - assert(win != NULL); - - static Atom prop = 0; - if (prop == 0) - prop = XInternAtom(win->src.display, "WM_STATE", False); - - Atom actual_type; - i32 actual_format; - unsigned long nitems, bytes_after; - unsigned char* prop_data; - - i16 status = XGetWindowProperty(win->src.display, (Window) win->src.window, prop, 0, 2, False, - AnyPropertyType, &actual_type, &actual_format, - &nitems, &bytes_after, &prop_data); - - if (status == Success && nitems >= 1 && *((int*) prop_data) == IconicState) { - XFree(prop_data); - return 1; - } - - if (prop_data != NULL) - XFree(prop_data); - - return 0; - } - - u8 RGFW_window_isMaximized(RGFW_window* win) { - assert(win != NULL); - - static Atom net_wm_state = 0; - static Atom net_wm_state_maximized_horz = 0; - static Atom net_wm_state_maximized_vert = 0; - - if (net_wm_state == 0) { - net_wm_state = XInternAtom(win->src.display, "_NET_WM_STATE", False); - net_wm_state_maximized_vert = XInternAtom(win->src.display, "_NET_WM_STATE_MAXIMIZED_VERT", False); - net_wm_state_maximized_horz = XInternAtom(win->src.display, "_NET_WM_STATE_MAXIMIZED_HORZ", False); - } - - Atom actual_type; - i32 actual_format; - unsigned long nitems, bytes_after; - unsigned char* prop_data; - - i16 status = XGetWindowProperty(win->src.display, (Window) win->src.window, net_wm_state, 0, 1024, False, - XA_ATOM, &actual_type, &actual_format, - &nitems, &bytes_after, &prop_data); - - if (status != Success) { - if (prop_data != NULL) - XFree(prop_data); - - return 0; - } - - Atom* atoms = (Atom*) prop_data; - u64 i; - for (i = 0; i < nitems; ++i) { - if (atoms[i] == net_wm_state_maximized_horz || - atoms[i] == net_wm_state_maximized_vert) { - XFree(prop_data); - return 1; - } - } - - return 0; - } - - static void XGetSystemContentScale(Display* display, float* xscale, float* yscale) { - float xdpi = 96.f, ydpi = 96.f; - -#ifndef RGFW_NO_DPI - char* rms = XResourceManagerString(display); - XrmDatabase db = NULL; - - if (rms && db) - db = XrmGetStringDatabase(rms); - - if (db == 0) { - *xscale = xdpi / 96.f; - *yscale = ydpi / 96.f; - return; - } - - XrmValue value; - char* type = NULL; - - if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value) && type && strncmp(type, "String", 7) == 0) - xdpi = ydpi = atof(value.addr); - XrmDestroyDatabase(db); -#endif - - * xscale = xdpi / 96.f; - *yscale = ydpi / 96.f; - } - - RGFW_monitor RGFW_XCreateMonitor(i32 screen) { - RGFW_monitor monitor; - - Display* display = XOpenDisplay(NULL); - - RGFW_area size = RGFW_getScreenSize(); - - monitor.rect = RGFW_RECT(0, 0, size.w, size.h); - monitor.physW = DisplayWidthMM(display, screen); - monitor.physH = DisplayHeightMM(display, screen); - - XGetSystemContentScale(display, &monitor.scaleX, &monitor.scaleY); - XRRScreenResources* sr = XRRGetScreenResourcesCurrent(display, RootWindow(display, screen)); - - XRRCrtcInfo* ci = NULL; - int crtc = screen; - - if (sr->ncrtc > crtc) { - ci = XRRGetCrtcInfo(display, sr, sr->crtcs[crtc]); - } - - if (ci == NULL) { - float dpi_width = round((double)monitor.rect.w/(((double)monitor.physW)/25.4)); - float dpi_height = round((double)monitor.rect.h/(((double)monitor.physH)/25.4)); - - monitor.scaleX = (float) (dpi_width) / (float) 96; - monitor.scaleY = (float) (dpi_height) / (float) 96; - XRRFreeScreenResources(sr); - XCloseDisplay(display); - return monitor; - } - - XRROutputInfo* info = XRRGetOutputInfo (display, sr, sr->outputs[screen]); - monitor.physW = info->mm_width; - monitor.physH = info->mm_height; - - monitor.rect.x = ci->x; - monitor.rect.y = ci->y; - monitor.rect.w = ci->width; - monitor.rect.h = ci->height; - - float dpi_width = round((double)monitor.rect.w/(((double)monitor.physW)/25.4)); - float dpi_height = round((double)monitor.rect.h/(((double)monitor.physH)/25.4)); - - monitor.scaleX = (float) (dpi_width) / (float) 96; - monitor.scaleY = (float) (dpi_height) / (float) 96; - - if (monitor.scaleX > 1 && monitor.scaleX < 1.1) - monitor.scaleX = 1; - - if (monitor.scaleY > 1 && monitor.scaleY < 1.1) - monitor.scaleY = 1; - - XRRFreeCrtcInfo(ci); - XRRFreeScreenResources(sr); - - XCloseDisplay(display); - - return monitor; - } - - RGFW_monitor RGFW_monitors[6]; - RGFW_monitor* RGFW_getMonitors(void) { - size_t i; - for (i = 0; i < (size_t)ScreenCount(RGFW_root->src.display) && i < 6; i++) - RGFW_monitors[i] = RGFW_XCreateMonitor(i); - - return RGFW_monitors; - } - - RGFW_monitor RGFW_getPrimaryMonitor(void) { - assert(RGFW_root != NULL); - - i32 primary = -1; - Window root = DefaultRootWindow(RGFW_root->src.display); - XRRScreenResources* res = XRRGetScreenResources(RGFW_root->src.display, root); - - for (int i = 0; i < res->noutput; i++) { - XRROutputInfo* output_info = XRRGetOutputInfo(RGFW_root->src.display, res, res->outputs[i]); - if (output_info->connection == RR_Connected && output_info->crtc) { - XRRCrtcInfo* crtc_info = XRRGetCrtcInfo(RGFW_root->src.display, res, output_info->crtc); - if (crtc_info->mode != None && crtc_info->x == 0 && crtc_info->y == 0) { - primary = i; - XRRFreeCrtcInfo(crtc_info); - XRRFreeOutputInfo(output_info); - break; - } - XRRFreeCrtcInfo(crtc_info); - } - XRRFreeOutputInfo(output_info); - } - - XRRFreeScreenResources(res); - - return RGFW_XCreateMonitor(primary); - } - - RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { - return RGFW_XCreateMonitor(DefaultScreen(win->src.display)); - } - - #ifdef RGFW_OPENGL - void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { - if (win == NULL) - glXMakeCurrent((Display*) NULL, (Drawable)NULL, (GLXContext) NULL); - else - glXMakeCurrent((Display*) win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx); - } - #endif - - - void RGFW_window_swapBuffers(RGFW_window* win) { - assert(win != NULL); - - /* clear the window*/ - if (!(win->_winArgs & RGFW_NO_CPU_RENDER)) { -#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - #ifdef RGFW_OSMESA - RGFW_OSMesa_reorganize(); - #endif - RGFW_area area = RGFW_bufferSize; - -#ifndef RGFW_X11_DONT_CONVERT_BGR - win->src.bitmap->data = (char*) win->buffer; - 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 * area.w) + x * 4; - - u8 red = win->src.bitmap->data[index]; - win->src.bitmap->data[index] = win->buffer[index + 2]; - win->src.bitmap->data[index + 2] = red; - - } - } -#endif - XPutImage(win->src.display, (Window) win->src.window, win->src.gc, win->src.bitmap, 0, 0, 0, 0, RGFW_bufferSize.w, RGFW_bufferSize.h); -#endif - } - - if (!(win->_winArgs & RGFW_NO_GPU_RENDER)) { - #ifdef RGFW_EGL - eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); - #elif defined(RGFW_OPENGL) - glXSwapBuffers((Display*) win->src.display, (Window) win->src.window); - #endif - } - } - - #if !defined(RGFW_EGL) - void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { - assert(win != NULL); - - #if defined(RGFW_OPENGL) - ((PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress((GLubyte*) "glXSwapIntervalEXT"))((Display*) win->src.display, (Window) win->src.window, swapInterval); - #else - RGFW_UNUSED(swapInterval); - #endif - } - #endif - - - void RGFW_window_close(RGFW_window* win) { - /* ungrab pointer if it was grabbed */ - if (win->_winArgs & RGFW_HOLD_MOUSE) - XUngrabPointer(win->src.display, CurrentTime); - - assert(win != NULL); -#ifdef RGFW_EGL - RGFW_closeEGL(win); -#endif - -#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - if (win->buffer != NULL) { - XDestroyImage((XImage*) win->src.bitmap); - XFreeGC(win->src.display, win->src.gc); - } -#endif - - if ((Display*) win->src.display) { -#ifdef RGFW_OPENGL - glXDestroyContext((Display*) win->src.display, win->src.ctx); -#endif - - if (win == RGFW_root) - RGFW_root = NULL; - - if ((Drawable) win->src.window) - XDestroyWindow((Display*) win->src.display, (Drawable) win->src.window); /*!< close the window*/ - - XCloseDisplay((Display*) win->src.display); /*!< kill the display*/ - } - -#ifdef RGFW_ALLOC_DROPFILES - { - u32 i; - for (i = 0; i < RGFW_MAX_DROPS; i++) - RGFW_FREE(win->event.droppedFiles[i]); - - - RGFW_FREE(win->event.droppedFiles); - } -#endif - - RGFW_windowsOpen--; -#if !defined(RGFW_NO_X11_CURSOR_PRELOAD) && !defined(RGFW_NO_X11_CURSOR) - if (X11Cursorhandle != NULL && RGFW_windowsOpen <= 0) { - dlclose(X11Cursorhandle); - - X11Cursorhandle = NULL; - } -#endif -#if !defined(RGFW_NO_X11_XI_PRELOAD) - if (X11Xihandle != NULL && RGFW_windowsOpen <= 0) { - dlclose(X11Xihandle); - - X11Xihandle = NULL; - } -#endif - - if (RGFW_libxshape != NULL && RGFW_windowsOpen <= 0) { - dlclose(RGFW_libxshape); - RGFW_libxshape = NULL; - } - - if (RGFW_windowsOpen <= 0) { - 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_joystickCount; i++) - close(RGFW_joysticks[i]); - } - - /* set cleared display / window to NULL for error checking */ - win->src.display = (Display*) 0; - win->src.window = (Window) 0; - - RGFW_FREE(win); /*!< free collected window data */ - } - - -/* - End of X11 linux / unix defines -*/ - -#endif /* RGFW_X11 */ - - -/* wayland or X11 defines*/ -#if defined(RGFW_WAYLAND) || defined(RGFW_X11) -#include -#include -#include - u16 RGFW_registerJoystickF(RGFW_window* win, char* file) { - assert(win != NULL); - -#ifdef __linux__ - - i32 js = open(file, O_RDONLY); - - if (js && RGFW_joystickCount < 4) { - RGFW_joystickCount++; - - RGFW_joysticks[RGFW_joystickCount - 1] = open(file, O_RDONLY); - - u8 i; - for (i = 0; i < 16; i++) - RGFW_jsPressed[RGFW_joystickCount - 1][i] = 0; - - } - - else { -#ifdef RGFW_PRINT_ERRORS - RGFW_error = 1; - fprintf(stderr, "Error RGFW_registerJoystickF : Cannot open file %s\n", file); -#endif - } - - return RGFW_joystickCount - 1; -#endif - } - - u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber) { - assert(win != NULL); - -#ifdef __linux__ - char file[15]; - sprintf(file, "/dev/input/js%i", jsNumber); - - return RGFW_registerJoystickF(win, file); -#endif - } - - 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.display), POLLIN, 0 }, - #else - { ConnectionNumber(win->src.display), POLLIN, 0 }, - #endif - { RGFW_eventWait_forceStop[0], POLLIN, 0 }, - #ifdef __linux__ /* blank space for 4 joystick files*/ - { -1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0} - #endif - }; - - u8 index = 2; - - #if defined(__linux__) - for (i = 0; i < RGFW_joystickCount; i++) { - if (RGFW_joysticks[i] == 0) - continue; - - fds[index].fd = RGFW_joysticks[i]; - index++; - } - #endif - - - u64 start = RGFW_getTimeNS(); - - #ifdef RGFW_WAYLAND - while (wl_display_dispatch(win->src.display) <= 0 && waitMS >= -1) { - #else - while (XPending(win->src.display) == 0 && waitMS >= -1) { - #endif - if (poll(fds, index, waitMS) <= 0) - break; - - if (waitMS > 0) { - waitMS -= (RGFW_getTimeNS() - start) / 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; - } - } - - u64 RGFW_getTimeNS(void) { - struct timespec ts = { 0 }; - clock_gettime(1, &ts); - unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; - - return nanoSeconds; - } - - u64 RGFW_getTime(void) { - struct timespec ts = { 0 }; - clock_gettime(1, &ts); - unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; - - return (double)(nanoSeconds) * 1e-9; - } -#endif /* end of wayland or X11 time defines*/ - - -/* - - Start of Wayland defines - - -*/ - -#ifdef RGFW_WAYLAND -/* -Wayland TODO: -- fix RGFW_keyPressed lock state - - RGFW_windowMoved, the window was moved (by the user) - RGFW_windowResized the window was resized (by the user), [on webASM 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_dnd_init - -- window args: - #define RGFW_NO_RESIZE the window cannot be resized by the user - #define RGFW_ALLOW_DND the window supports drag and drop - #define RGFW_SCALE_TO_MONITOR 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; - -void RGFW_eventPipe_push(RGFW_window* win, RGFW_Event event) { - if (win == NULL) { - win = RGFW_key_win; - - if (win == NULL) return; - } - - if (win->src.eventLen >= (i32)(sizeof(win->src.events) / sizeof(win->src.events[0]))) - return; - - win->src.events[win->src.eventLen] = event; - win->src.eventLen += 1; -} - -RGFW_Event RGFW_eventPipe_pop(RGFW_window* win) { - RGFW_Event ev; - ev.type = 0; - - if (win->src.eventLen > -1) - win->src.eventLen -= 1; - - if (win->src.eventLen >= 0) - ev = win->src.events[win->src.eventLen]; - - return ev; -} - -/* wayland global garbage (wayland bad, X11 is fine (ish) (not really)) */ -#include "xdg-shell.h" -#include "xdg-decoration-unstable-v1.h" - -struct xdg_wm_base *xdg_wm_base; -struct wl_compositor* RGFW_compositor = NULL; -struct wl_shm* shm = NULL; -struct wl_shell* RGFW_shell = NULL; -static struct wl_seat *seat = NULL; -static struct xkb_context *xkb_context; -static struct xkb_keymap *keymap = NULL; -static struct xkb_state *xkb_state = NULL; -enum zxdg_toplevel_decoration_v1_mode client_preferred_mode, RGFW_current_mode; -static 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; - -static 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); -} - -static const struct xdg_wm_base_listener xdg_wm_base_listener = { - .ping = xdg_wm_base_ping_handler, -}; - -b8 RGFW_wl_configured = 0; - -static 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); - #ifdef RGFW_DEBUG - printf("Surface configured\n"); - #endif - RGFW_wl_configured = 1; -} - -static const struct xdg_surface_listener xdg_surface_listener = { - .configure = xdg_surface_configure_handler, -}; - -static 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) - fprintf(stderr, "XDG toplevel configure: %dx%d\n", width, height); -} - -static 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_Event ev; - ev.type = RGFW_quit; - - RGFW_eventPipe_push(win, ev); - - RGFW_windowQuitCallback(win); -} - -static void shm_format_handler(void *data, - struct wl_shm *shm, uint32_t format) -{ - RGFW_UNUSED(data); RGFW_UNUSED(shm); - fprintf(stderr, "Format %d\n", format); -} - -static const struct wl_shm_listener shm_listener = { - .format = shm_format_handler, -}; - -static const struct xdg_toplevel_listener xdg_toplevel_listener = { - .configure = xdg_toplevel_configure_handler, - .close = xdg_toplevel_close_handler, -}; - -RGFW_window* RGFW_mouse_win = NULL; - -static 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_Event ev; - ev.type = RGFW_mouseEnter; - ev.point = win->event.point; - - RGFW_eventPipe_push(win, ev); - - RGFW_mouseNotifyCallBack(win, win->event.point, RGFW_TRUE); -} -static 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_Event ev; - ev.type = RGFW_mouseLeave; - ev.point = win->event.point; - RGFW_eventPipe_push(win, ev); - - RGFW_mouseNotifyCallBack(win, win->event.point, RGFW_FALSE); -} -static 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); - - assert(RGFW_mouse_win != NULL); - - RGFW_Event ev; - ev.type = RGFW_mousePosChanged; - ev.point = RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y)); - RGFW_eventPipe_push(RGFW_mouse_win, ev); - - RGFW_mousePosCallback(RGFW_mouse_win, RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y))); -} -static 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); - assert(RGFW_mouse_win != NULL); - - u32 b = (button - 0x110) + 1; - - /* flip right and middle button codes */ - if (b == 2) b = 3; - else if (b == 3) b = 2; - - RGFW_mouseButtons[b].prev = RGFW_mouseButtons[b].current; - RGFW_mouseButtons[b].current = state; - - RGFW_Event ev; - ev.type = RGFW_mouseButtonPressed + state; - ev.button = b; - RGFW_eventPipe_push(RGFW_mouse_win, ev); - - RGFW_mouseButtonCallback(RGFW_mouse_win, b, 0, state); -} -static 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); - assert(RGFW_mouse_win != NULL); - - double scroll = wl_fixed_to_double(value); - - RGFW_Event ev; - ev.type = RGFW_mouseButtonPressed; - ev.button = RGFW_mouseScrollUp + (scroll < 0); - RGFW_eventPipe_push(RGFW_mouse_win, ev); - - RGFW_mouseButtonCallback(RGFW_mouse_win, RGFW_mouseScrollUp + (scroll < 0), scroll, 1); -} - -void RGFW_doNothing(void) { } -static struct wl_pointer_listener pointer_listener = (struct wl_pointer_listener){&pointer_enter, &pointer_leave, &pointer_motion, &pointer_button, &pointer_axis, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing}; - -static 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); -} -static 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_Event ev; - ev.type = RGFW_focusIn; - ev.inFocus = RGFW_TRUE; - RGFW_key_win->event.inFocus = RGFW_TRUE; - - RGFW_eventPipe_push((RGFW_window*)RGFW_mouse_win, ev); - - RGFW_focusCallback(RGFW_key_win, RGFW_TRUE); -} -static 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_Event ev; - ev.type = RGFW_focusOut; - ev.inFocus = RGFW_FALSE; - win->event.inFocus = RGFW_FALSE; - RGFW_eventPipe_push(win, ev); - - RGFW_focusCallback(win, RGFW_FALSE); -} -static 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); - - assert(RGFW_key_win != NULL); - - xkb_keysym_t keysym = xkb_state_key_get_one_sym (xkb_state, key+8); - char name[16]; - xkb_keysym_get_name(keysym, name, 16); - - u32 RGFW_key = RGFW_apiKeyCodeToRGFW(key); - RGFW_keyboard[RGFW_key].prev = RGFW_keyboard[RGFW_key].current; - RGFW_keyboard[RGFW_key].current = state; - RGFW_Event ev; - ev.type = RGFW_keyPressed + state; - ev.keyCode = RGFW_key; - strcpy(ev.keyName, name); - ev.repeat = RGFW_isHeld(RGFW_key_win, RGFW_key); - RGFW_eventPipe_push(RGFW_key_win, ev); - - RGFW_updateLockState(RGFW_key_win, xkb_keymap_mod_get_index(keymap, "Lock"), xkb_keymap_mod_get_index(keymap, "Mod2")); - - RGFW_keyCallback(RGFW_key_win, RGFW_key, name, RGFW_key_win->event.lockState, state); -} -static 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); -} -static struct wl_keyboard_listener keyboard_listener = {&keyboard_keymap, &keyboard_enter, &keyboard_leave, &keyboard_key, &keyboard_modifiers, (void*)&RGFW_doNothing}; - -static void seat_capabilities (void *data, struct wl_seat *seat, uint32_t capabilities) { - RGFW_UNUSED(data); - - 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); - } -} -static struct wl_seat_listener seat_listener = {&seat_capabilities, (void*)&RGFW_doNothing}; - -static void wl_global_registry_handler(void *data, - struct wl_registry *registry, uint32_t id, const char *interface, - uint32_t version) -{ - RGFW_UNUSED(data); RGFW_UNUSED(version); - - if (strcmp(interface, "wl_compositor") == 0) { - RGFW_compositor = wl_registry_bind(registry, - id, &wl_compositor_interface, 4); - } else if (strcmp(interface, "xdg_wm_base") == 0) { - xdg_wm_base = wl_registry_bind(registry, - id, &xdg_wm_base_interface, 1); - } else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) { - decoration_manager = wl_registry_bind(registry, id, &zxdg_decoration_manager_v1_interface, 1); - } else if (strcmp(interface, "wl_shm") == 0) { - shm = wl_registry_bind(registry, - id, &wl_shm_interface, 1); - wl_shm_add_listener(shm, &shm_listener, NULL); - } else if (strcmp(interface,"wl_seat") == 0) { - seat = wl_registry_bind(registry, id, &wl_seat_interface, 1); - wl_seat_add_listener(seat, &seat_listener, NULL); - } - - else { - #ifdef RGFW_DEBUG - printf("did not register %s\n", interface); - return; - #endif - } - - #ifdef RGFW_DEBUG - printf("registered %s\n", interface); - #endif -} - -static void wl_global_registry_remove(void *data, struct wl_registry *registry, uint32_t name) { RGFW_UNUSED(data); RGFW_UNUSED(registry); RGFW_UNUSED(name); } -static const struct wl_registry_listener registry_listener = { - .global = wl_global_registry_handler, - .global_remove = wl_global_registry_remove, -}; - -static const char *get_mode_name(enum zxdg_toplevel_decoration_v1_mode mode) { - switch (mode) { - case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE: - return "client-side decorations"; - case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE: - return "server-side decorations"; - } - abort(); -} - - -static 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); - printf("Using %s\n", get_mode_name(mode)); - RGFW_current_mode = mode; -} - -static const struct zxdg_toplevel_decoration_v1_listener decoration_listener = { - .configure = decoration_handle_configure, -}; - -static void randname(char *buf) { - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - long r = ts.tv_nsec; - for (int i = 0; i < 6; ++i) { - buf[i] = 'A'+(r&15)+(r&16)*2; - r >>= 5; - } -} - -static int anonymous_shm_open(void) { - char name[] = "/RGFW-wayland-XXXXXX"; - int retries = 100; - - do { - randname(name + strlen(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; -} - -static void wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) { - #ifdef RGFW_BUFFER - RGFW_window* win = (RGFW_window*)data; - if ((win->_winArgs & RGFW_NO_CPU_RENDER)) - return; - - #ifndef RGFW_X11_DONT_CONVERT_BGR - 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->r.w) + x * 4; - - u8 red = win->buffer[index]; - win->buffer[index] = win->buffer[index + 2]; - win->buffer[index + 2] = red; - - } - } - #endif - - 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 -} - -static const struct wl_callback_listener wl_surface_frame_listener = { - .done = wl_surface_frame_done, -}; - - - /* normal wayland RGFW stuff */ - - RGFW_area RGFW_getScreenSize(void) { - RGFW_area area = {}; - - if (RGFW_root != NULL) - /* this isn't right but it's here for buffers */ - area = RGFW_AREA(RGFW_root->r.w, RGFW_root->r.h); - - /* TODO wayland */ - return area; - } - - void RGFW_releaseCursor(RGFW_window* win) { - RGFW_UNUSED(win); - } - - void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { - RGFW_UNUSED(win); RGFW_UNUSED(r); - - /* TODO wayland */ - } - - - RGFWDEF void RGFW_init_buffer(RGFW_window* win); - void RGFW_init_buffer(RGFW_window* win) { - #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - size_t size = win->r.w * win->r.h * 4; - int fd = create_shm_file(size); - if (fd < 0) { - fprintf(stderr, "Failed to create a buffer. size: %ld\n", size); - exit(1); - } - - win->buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (win->buffer == MAP_FAILED) { - fprintf(stderr, "mmap failed!\n"); - close(fd); - exit(1); - } - - struct wl_shm_pool* pool = wl_shm_create_pool(shm, fd, 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 < size; i += 4) { - memcpy(&win->buffer[i], color, 4); - } - - #if defined(RGFW_OSMESA) - win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL); - OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h); - #endif - #else - RGFW_UNUSED(win); - #endif - } - - - RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) { - RGFW_window* win = RGFW_window_basic_init(rect, args); - - fprintf(stderr, "Warning: RGFW Wayland support is experimental\n"); - - win->src.display = wl_display_connect(NULL); - if (win->src.display == NULL) { - #ifdef RGFW_DEBUG - fprintf(stderr, "Failed to load Wayland display\n"); - #endif - return NULL; - } - - struct wl_registry *registry = wl_display_get_registry(win->src.display); - wl_registry_add_listener(registry, ®istry_listener, NULL); - - wl_display_dispatch(win->src.display); - wl_display_roundtrip(win->src.display); - - if (RGFW_compositor == NULL) { - #ifdef RGFW_DEBUG - fprintf(stderr, "Can't find compositor.\n"); - #endif - - return NULL; - } - - if (RGFW_wl_cursor_theme == NULL) { - RGFW_wl_cursor_theme = wl_cursor_theme_load(NULL, 24, shm); - RGFW_cursor_surface = wl_compositor_create_surface(RGFW_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); - } - - if (RGFW_root == NULL) - xdg_wm_base_add_listener(xdg_wm_base, &xdg_wm_base_listener, NULL); - - xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - - win->src.surface = wl_compositor_create_surface(RGFW_compositor); - wl_surface_set_user_data(win->src.surface, win); - - win->src.xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, win->src.surface); - xdg_surface_add_listener(win->src.xdg_surface, &xdg_surface_listener, NULL); - - xdg_wm_base_set_user_data(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_set_title(win->src.xdg_toplevel, name); - 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 (!(args & RGFW_NO_BORDER)) { - win->src.decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( - decoration_manager, win->src.xdg_toplevel); - } - - if (args & RGFW_CENTER) { - RGFW_area screenR = RGFW_getScreenSize(); - RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2)); - } - - if (args & RGFW_OPENGL_SOFTWARE) - setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1); - - wl_display_roundtrip(win->src.display); - - wl_surface_commit(win->src.surface); - - /* wait for the surface to be configured */ - while (wl_display_dispatch(win->src.display) != -1 && !RGFW_wl_configured) { } - - - #ifdef RGFW_OPENGL - if ((args & RGFW_NO_INIT_API) == 0) { - win->src.window = wl_egl_window_create(win->src.surface, win->r.w, win->r.h); - RGFW_createOpenGLContext(win); - } - #endif - - RGFW_init_buffer(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); - - if (args & RGFW_HIDE_MOUSE) { - RGFW_window_showMouse(win, 0); - } - - if (RGFW_root == NULL) { - RGFW_root = win; - } - - win->src.eventIndex = 0; - win->src.eventLen = 0; - - return win; - } - - RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) { - if (win->_winArgs & RGFW_WINDOW_HIDE) - return NULL; - - if (win->src.eventIndex == 0) { - if (wl_display_roundtrip(win->src.display) == -1) { - return NULL; - } - RGFW_resetKey(); - } - - #ifdef __linux__ - RGFW_Event* event = RGFW_linux_updateJoystick(win); - if (event != NULL) - return event; - #endif - - if (win->src.eventLen == 0) { - return NULL; - } - - RGFW_Event ev = RGFW_eventPipe_pop(win); - - if (ev.type == 0 || win->event.type == RGFW_quit) { - return NULL; - } - - ev.frameTime = win->event.frameTime; - ev.frameTime2 = win->event.frameTime2; - ev.inFocus = win->event.inFocus; - win->event = ev; - - return &win->event; - } - - - void RGFW_window_resize(RGFW_window* win, RGFW_area a) { - RGFW_UNUSED(win); RGFW_UNUSED(a); - - /* TODO wayland */ - } - - void RGFW_window_move(RGFW_window* win, RGFW_point v) { - RGFW_UNUSED(win); RGFW_UNUSED(v); - - /* TODO wayland */ - } - - void RGFW_window_setIcon(RGFW_window* win, u8* src, RGFW_area a, i32 channels) { - RGFW_UNUSED(win); RGFW_UNUSED(src); RGFW_UNUSED(a); RGFW_UNUSED(channels) - /* TODO wayland */ - } - - void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) { - RGFW_UNUSED(win); RGFW_UNUSED(v); - - /* TODO wayland */ - } - - void RGFW_window_showMouse(RGFW_window* win, i8 show) { - RGFW_UNUSED(win); - - if (show) { - - } - else { - - } - - /* TODO wayland */ - } - - b8 RGFW_window_isMaximized(RGFW_window* win) { - RGFW_UNUSED(win); - /* TODO wayland */ - return 0; - } - - b8 RGFW_window_isMinimized(RGFW_window* win) { - RGFW_UNUSED(win); - /* TODO wayland */ - return 0; - } - - b8 RGFW_window_isHidden(RGFW_window* win) { - RGFW_UNUSED(win); - /* TODO wayland */ - return 0; - } - - b8 RGFW_window_isFullscreen(RGFW_window* win) { - RGFW_UNUSED(win); - /* TODO wayland */ - return 0; - } - - RGFW_point RGFW_window_getMousePoint(RGFW_window* win) { - RGFW_UNUSED(win); - /* TODO wayland */ - return RGFW_POINT(0, 0); - } - - RGFW_point RGFW_getGlobalMousePoint(void) { - /* TODO wayland */ - return RGFW_POINT(0, 0); - } - - void RGFW_window_show(RGFW_window* win) { - //wl_surface_attach(win->src.surface, win->rc., 0, 0); - wl_surface_commit(win->src.surface); - - if (win->_winArgs & RGFW_WINDOW_HIDE) - win->_winArgs ^= RGFW_WINDOW_HIDE; - } - - void RGFW_window_hide(RGFW_window* win) { - wl_surface_attach(win->src.surface, NULL, 0, 0); - wl_surface_commit(win->src.surface); - win->_winArgs |= RGFW_WINDOW_HIDE; - } - - void RGFW_window_setMouseDefault(RGFW_window* win) { - RGFW_UNUSED(win); - - RGFW_window_setMouseStandard(win, RGFW_MOUSE_NORMAL); - } - - void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { - RGFW_UNUSED(win); - - static const char* iconStrings[] = { "left_ptr", "left_ptr", "text", "cross", "pointer", "e-resize", "n-resize", "nw-resize", "ne-resize", "all-resize", "not-allowed" }; - - struct wl_cursor* cursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, iconStrings[mouse]); - 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); - } - - void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) { - RGFW_UNUSED(win); RGFW_UNUSED(image); RGFW_UNUSED(a); RGFW_UNUSED(channels) - //struct wl_cursor* cursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, iconStrings[mouse]); - //RGFW_cursor_image = image; - 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); - } - - void RGFW_window_setName(RGFW_window* win, char* name) { - xdg_toplevel_set_title(win->src.xdg_toplevel, name); - } - - void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) { - RGFW_UNUSED(win); RGFW_UNUSED(passthrough); - - /* TODO wayland */ - } - - void RGFW_window_setBorder(RGFW_window* win, b8 border) { - RGFW_UNUSED(win); RGFW_UNUSED(border); - - /* TODO wayland */ - } - - void RGFW_window_restore(RGFW_window* win) { - RGFW_UNUSED(win); - - /* TODO wayland */ - } - - void RGFW_window_minimize(RGFW_window* win) { - RGFW_UNUSED(win); - - /* TODO wayland */ - } - - void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { - RGFW_UNUSED(win); RGFW_UNUSED(a); - - /* TODO wayland */ - } - - void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { - RGFW_UNUSED(win); RGFW_UNUSED(a); - - /* TODO wayland */ - } - - RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { - RGFW_monitor m = {}; - RGFW_UNUSED(win); - RGFW_UNUSED(m); - /* TODO wayland */ - - return m; - } - - - #ifndef RGFW_EGL - void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { RGFW_UNUSED(win); RGFW_UNUSED(swapInterval); } - #endif - - void RGFW_window_swapBuffers(RGFW_window* win) { - assert(win != NULL); - - /* clear the window*/ - #ifdef RGFW_BUFFER - wl_surface_frame_done(win, NULL, 0); - if (!(win->_winArgs & RGFW_NO_GPU_RENDER)) - #endif - { - #ifdef RGFW_OPENGL - eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); - #endif - } - - wl_display_flush(win->src.display); - } - - void RGFW_window_close(RGFW_window* win) { - #ifdef RGFW_EGL - RGFW_closeEGL(win); - #endif - - if (RGFW_root == win) { - RGFW_root = NULL; - } - - xdg_toplevel_destroy(win->src.xdg_toplevel); - xdg_surface_destroy(win->src.xdg_surface); - wl_surface_destroy(win->src.surface); - - #ifdef RGFW_BUFFER - wl_buffer_destroy(win->src.wl_buffer); - #endif - - wl_display_disconnect(win->src.display); - RGFW_FREE(win); - } - - RGFW_monitor RGFW_getPrimaryMonitor(void) { - /* TODO wayland */ - - return (RGFW_monitor){}; - } - - RGFW_monitor* RGFW_getMonitors(void) { - /* TODO wayland */ - - return NULL; - } - - void RGFW_writeClipboard(const char* text, u32 textLen) { - RGFW_UNUSED(text); RGFW_UNUSED(textLen); - - /* TODO wayland */ - } - - char* RGFW_readClipboard(size_t* size) { - RGFW_UNUSED(size); - - /* TODO wayland */ - - return NULL; - } -#endif /* RGFW_WAYLAND */ - -/* - End of Wayland defines -*/ - - -/* - - Start of Windows defines - - -*/ - -#ifdef RGFW_WINDOWS - #define WIN32_LEAN_AND_MEAN - #define OEMRESOURCE - #include - - #include - #include - #include - #include - #include - #include - - #include - - __declspec(dllimport) int __stdcall WideCharToMultiByte( UINT CodePage, DWORD dwFlags, const WCHAR* lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar); - - #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 - - static HMODULE RGFW_XInput_dll = NULL; - #endif - - u32 RGFW_mouseIconSrc[] = {OCR_NORMAL, OCR_NORMAL, OCR_IBEAM, OCR_CROSS, OCR_HAND, OCR_SIZEWE, OCR_SIZENS, OCR_SIZENWSE, OCR_SIZENESW, OCR_SIZEALL, OCR_NO}; - - char* createUTF8FromWideStringWin32(const WCHAR* source); - -#define GL_FRONT 0x0404 -#define GL_BACK 0x0405 -#define GL_LEFT 0x0406 -#define GL_RIGHT 0x0407 - -#if defined(RGFW_OSMESA) && defined(RGFW_LINK_OSMESA) - - typedef void (GLAPIENTRY* PFN_OSMesaDestroyContext)(OSMesaContext); - typedef i32(GLAPIENTRY* PFN_OSMesaMakeCurrent)(OSMesaContext, void*, int, int, int); - typedef OSMesaContext(GLAPIENTRY* PFN_OSMesaCreateContext)(GLenum, OSMesaContext); - - PFN_OSMesaMakeCurrent OSMesaMakeCurrentSource; - PFN_OSMesaCreateContext OSMesaCreateContextSource; - PFN_OSMesaDestroyContext OSMesaDestroyContextSource; - -#define OSMesaCreateContext OSMesaCreateContextSource -#define OSMesaMakeCurrent OSMesaMakeCurrentSource -#define OSMesaDestroyContext OSMesaDestroyContextSource -#endif - - typedef int (*PFN_wglGetSwapIntervalEXT)(void); - PFN_wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc = NULL; -#define wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc - - - void* RGFWjoystickApi = NULL; - - /* these two wgl functions need to be preloaded */ - typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList); - PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL; - - /* defines for creating ARB attributes */ -#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 -#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 -#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 -#define WGL_DRAW_TO_WINDOW_ARB 0x2001 -#define WGL_ACCELERATION_ARB 0x2003 -#define WGL_NO_ACCELERATION_ARB 0x2025 -#define WGL_DOUBLE_BUFFER_ARB 0x2011 -#define WGL_COLOR_BITS_ARB 0x2014 -#define WGL_RED_BITS_ARB 0x2015 -#define WGL_RED_SHIFT_ARB 0x2016 -#define WGL_GREEN_BITS_ARB 0x2017 -#define WGL_GREEN_SHIFT_ARB 0x2018 -#define WGL_BLUE_BITS_ARB 0x2019 -#define WGL_BLUE_SHIFT_ARB 0x201a -#define WGL_ALPHA_BITS_ARB 0x201b -#define WGL_ALPHA_SHIFT_ARB 0x201c -#define WGL_ACCUM_BITS_ARB 0x201d -#define WGL_ACCUM_RED_BITS_ARB 0x201e -#define WGL_ACCUM_GREEN_BITS_ARB 0x201f -#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 -#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 -#define WGL_DEPTH_BITS_ARB 0x2022 -#define WGL_AUX_BUFFERS_ARB 0x2024 -#define WGL_STEREO_ARB 0x2012 -#define WGL_DEPTH_BITS_ARB 0x2022 -#define WGL_STENCIL_BITS_ARB 0x2023 -#define WGL_FULL_ACCELERATION_ARB 0x2027 -#define WGL_CONTEXT_FLAGS_ARB 0x2094 -#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 -#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 -#define WGL_SAMPLE_BUFFERS_ARB 0x2041 -#define WGL_SAMPLES_ARB 0x2042 -#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9 - -#ifndef RGFW_EGL -static HMODULE wglinstance = NULL; -#endif - -#ifdef RGFW_WGL_LOAD - typedef HGLRC(WINAPI* PFN_wglCreateContext)(HDC); - typedef BOOL(WINAPI* PFN_wglDeleteContext)(HGLRC); - typedef PROC(WINAPI* PFN_wglGetProcAddress)(LPCSTR); - typedef BOOL(WINAPI* PFN_wglMakeCurrent)(HDC, HGLRC); - typedef HDC(WINAPI* PFN_wglGetCurrentDC)(); - typedef HGLRC(WINAPI* PFN_wglGetCurrentContext)(); - - PFN_wglCreateContext wglCreateContextSRC; - PFN_wglDeleteContext wglDeleteContextSRC; - PFN_wglGetProcAddress wglGetProcAddressSRC; - PFN_wglMakeCurrent wglMakeCurrentSRC; - PFN_wglGetCurrentDC wglGetCurrentDCSRC; - PFN_wglGetCurrentContext wglGetCurrentContextSRC; - - #define wglCreateContext wglCreateContextSRC - #define wglDeleteContext wglDeleteContextSRC - #define wglGetProcAddress wglGetProcAddressSRC - #define wglMakeCurrent wglMakeCurrentSRC - - #define wglGetCurrentDC wglGetCurrentDCSRC - #define wglGetCurrentContext wglGetCurrentContextSRC -#endif - -#ifdef RGFW_OPENGL - void* RGFW_getProcAddress(const char* procname) { - void* proc = (void*) wglGetProcAddress(procname); - if (proc) - return proc; - - return (void*) GetProcAddress(wglinstance, procname); - } - - typedef HRESULT (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats); - static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL; -#endif - - RGFW_window RGFW_eventWindow; - - LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { - switch (message) { - case WM_MOVE: - RGFW_eventWindow.r.x = LOWORD(lParam); - RGFW_eventWindow.r.y = HIWORD(lParam); - RGFW_eventWindow.src.window = hWnd; - return DefWindowProcA(hWnd, message, wParam, lParam); - case WM_SIZE: - RGFW_eventWindow.r.w = LOWORD(lParam); - RGFW_eventWindow.r.h = HIWORD(lParam); - RGFW_eventWindow.src.window = hWnd; - return DefWindowProcA(hWnd, message, wParam, lParam); // Call DefWindowProc after handling - default: - return DefWindowProcA(hWnd, message, wParam, lParam); - } - } - - #ifndef RGFW_NO_DPI - static HMODULE RGFW_Shcore_dll = NULL; - typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*); - PFN_GetDpiForMonitor GetDpiForMonitorSRC = NULL; - #define GetDpiForMonitor GetDpiForMonitorSRC - #endif - - __declspec(dllimport) u32 __stdcall timeBeginPeriod(u32 uPeriod); - - #ifndef RGFW_NO_XINPUT - void RGFW_loadXInput(void) { - u32 i; - static const char* names[] = { - "xinput1_4.dll", - "xinput1_3.dll", - "xinput9_1_0.dll", - "xinput1_2.dll", - "xinput1_1.dll" - }; - - for (i = 0; i < sizeof(names) / sizeof(const char*); i++) { - RGFW_XInput_dll = LoadLibraryA(names[i]); - - if (RGFW_XInput_dll) { - XInputGetStateSRC = (PFN_XInputGetState)(void*)GetProcAddress(RGFW_XInput_dll, "XInputGetState"); - - if (XInputGetStateSRC == NULL) - printf("Failed to load XInputGetState"); - } - } - } - #endif - - RGFWDEF void RGFW_init_buffer(RGFW_window* win); - void RGFW_init_buffer(RGFW_window* win) { -#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0) - RGFW_bufferSize = RGFW_getScreenSize(); - - BITMAPV5HEADER bi = { 0 }; - ZeroMemory(&bi, sizeof(bi)); - bi.bV5Size = sizeof(bi); - bi.bV5Width = RGFW_bufferSize.w; - bi.bV5Height = -((LONG) RGFW_bufferSize.h); - bi.bV5Planes = 1; - bi.bV5BitCount = 32; - bi.bV5Compression = BI_BITFIELDS; - bi.bV5BlueMask = 0x00ff0000; - bi.bV5GreenMask = 0x0000ff00; - bi.bV5RedMask = 0x000000ff; - bi.bV5AlphaMask = 0xff000000; - - win->src.bitmap = CreateDIBSection(win->src.hdc, - (BITMAPINFO*) &bi, - DIB_RGB_COLORS, - (void**) &win->buffer, - NULL, - (DWORD) 0); - - win->src.hdcMem = CreateCompatibleDC(win->src.hdc); - - #if defined(RGFW_OSMESA) - win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL); - OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h); - #endif -#else -RGFW_UNUSED(win); /*!< if buffer rendering is not being used */ -#endif - } - - void RGFW_window_setDND(RGFW_window* win, b8 allow) { - DragAcceptFiles(win->src.window, allow); - } - - 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)); - } - - RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) { - #ifndef RGFW_NO_XINPUT - if (RGFW_XInput_dll == NULL) - RGFW_loadXInput(); - #endif - - #ifndef RGFW_NO_DPI - if (RGFW_Shcore_dll == NULL) { - RGFW_Shcore_dll = LoadLibraryA("shcore.dll"); - GetDpiForMonitorSRC = (PFN_GetDpiForMonitor)(void*)GetProcAddress(RGFW_Shcore_dll, "GetDpiForMonitor"); - SetProcessDPIAware(); - } - #endif - - if (wglinstance == NULL) { - wglinstance = LoadLibraryA("opengl32.dll"); -#ifdef RGFW_WGL_LOAD - wglCreateContextSRC = (PFN_wglCreateContext) GetProcAddress(wglinstance, "wglCreateContext"); - wglDeleteContextSRC = (PFN_wglDeleteContext) GetProcAddress(wglinstance, "wglDeleteContext"); - wglGetProcAddressSRC = (PFN_wglGetProcAddress) GetProcAddress(wglinstance, "wglGetProcAddress"); - wglMakeCurrentSRC = (PFN_wglMakeCurrent) GetProcAddress(wglinstance, "wglMakeCurrent"); - wglGetCurrentDCSRC = (PFN_wglGetCurrentDC) GetProcAddress(wglinstance, "wglGetCurrentDC"); - wglGetCurrentContextSRC = (PFN_wglGetCurrentContext) GetProcAddress(wglinstance, "wglGetCurrentContext"); -#endif - } - - if (name[0] == 0) name = (char*) " "; - - RGFW_eventWindow.r = RGFW_RECT(-1, -1, -1, -1); - RGFW_eventWindow.src.window = NULL; - - RGFW_window* win = RGFW_window_basic_init(rect, args); - - win->src.maxSize = RGFW_AREA(0, 0); - win->src.minSize = RGFW_AREA(0, 0); - - - HINSTANCE inh = GetModuleHandleA(NULL); - - #ifndef __cplusplus - WNDCLASSA Class = { 0 }; /*!< Setup the Window class. */ - #else - WNDCLASSA Class = { }; - #endif - - if (RGFW_className == NULL) - RGFW_className = (char*)name; - - Class.lpszClassName = RGFW_className; - Class.hInstance = inh; - Class.hCursor = LoadCursor(NULL, IDC_ARROW); - Class.lpfnWndProc = WndProc; - - RegisterClassA(&Class); - - DWORD window_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN; - - RECT windowRect, clientRect; - - if (!(args & RGFW_NO_BORDER)) { - window_style |= WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_MINIMIZEBOX; - - if (!(args & RGFW_NO_RESIZE)) - window_style |= WS_SIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME; - } else - window_style |= WS_POPUP | WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX; - - HWND dummyWin = CreateWindowA(Class.lpszClassName, name, window_style, win->r.x, win->r.y, win->r.w, win->r.h, 0, 0, inh, 0); - - GetWindowRect(dummyWin, &windowRect); - GetClientRect(dummyWin, &clientRect); - - win->src.hOffset = (windowRect.bottom - windowRect.top) - (clientRect.bottom - clientRect.top); - win->src.window = CreateWindowA(Class.lpszClassName, name, window_style, win->r.x, win->r.y, win->r.w, win->r.h + win->src.hOffset, 0, 0, inh, 0); - - if (args & RGFW_ALLOW_DND) { - win->_winArgs |= RGFW_ALLOW_DND; - RGFW_window_setDND(win, 1); - } - win->src.hdc = GetDC(win->src.window); - - if ((args & RGFW_NO_INIT_API) == 0) { -#ifdef RGFW_DIRECTX - assert(FAILED(CreateDXGIFactory(&__uuidof(IDXGIFactory), (void**) &RGFW_dxInfo.pFactory)) == 0); - - if (FAILED(RGFW_dxInfo.pFactory->lpVtbl->EnumAdapters(RGFW_dxInfo.pFactory, 0, &RGFW_dxInfo.pAdapter))) { - fprintf(stderr, "Failed to enumerate DXGI adapters\n"); - RGFW_dxInfo.pFactory->lpVtbl->Release(RGFW_dxInfo.pFactory); - return NULL; - } - - D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0 }; - - if (FAILED(D3D11CreateDevice(RGFW_dxInfo.pAdapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, featureLevels, 1, D3D11_SDK_VERSION, &RGFW_dxInfo.pDevice, NULL, &RGFW_dxInfo.pDeviceContext))) { - fprintf(stderr, "Failed to create Direct3D device\n"); - RGFW_dxInfo.pAdapter->lpVtbl->Release(RGFW_dxInfo.pAdapter); - RGFW_dxInfo.pFactory->lpVtbl->Release(RGFW_dxInfo.pFactory); - return NULL; - } - - DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 }; - swapChainDesc.BufferCount = 1; - swapChainDesc.BufferDesc.Width = win->r.w; - swapChainDesc.BufferDesc.Height = win->r.h; - swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapChainDesc.OutputWindow = win->src.window; - swapChainDesc.SampleDesc.Count = 1; - swapChainDesc.SampleDesc.Quality = 0; - swapChainDesc.Windowed = TRUE; - RGFW_dxInfo.pFactory->lpVtbl->CreateSwapChain(RGFW_dxInfo.pFactory, (IUnknown*) RGFW_dxInfo.pDevice, &swapChainDesc, &win->src.swapchain); - - ID3D11Texture2D* pBackBuffer; - win->src.swapchain->lpVtbl->GetBuffer(win->src.swapchain, 0, &__uuidof(ID3D11Texture2D), (LPVOID*) &pBackBuffer); - RGFW_dxInfo.pDevice->lpVtbl->CreateRenderTargetView(RGFW_dxInfo.pDevice, (ID3D11Resource*) pBackBuffer, NULL, &win->src.renderTargetView); - pBackBuffer->lpVtbl->Release(pBackBuffer); - - D3D11_TEXTURE2D_DESC depthStencilDesc = { 0 }; - depthStencilDesc.Width = win->r.w; - depthStencilDesc.Height = win->r.h; - depthStencilDesc.MipLevels = 1; - depthStencilDesc.ArraySize = 1; - depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; - depthStencilDesc.SampleDesc.Count = 1; - depthStencilDesc.SampleDesc.Quality = 0; - depthStencilDesc.Usage = D3D11_USAGE_DEFAULT; - depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; - - ID3D11Texture2D* pDepthStencilTexture = NULL; - RGFW_dxInfo.pDevice->lpVtbl->CreateTexture2D(RGFW_dxInfo.pDevice, &depthStencilDesc, NULL, &pDepthStencilTexture); - - D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc = { 0 }; - depthStencilViewDesc.Format = depthStencilDesc.Format; - depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; - depthStencilViewDesc.Texture2D.MipSlice = 0; - - RGFW_dxInfo.pDevice->lpVtbl->CreateDepthStencilView(RGFW_dxInfo.pDevice, (ID3D11Resource*) pDepthStencilTexture, &depthStencilViewDesc, &win->src.pDepthStencilView); - - pDepthStencilTexture->lpVtbl->Release(pDepthStencilTexture); - - RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, &win->src.renderTargetView, win->src.pDepthStencilView); -#endif - -#ifdef RGFW_OPENGL - HDC dummy_dc = GetDC(dummyWin); - - u32 pfd_flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; - - //if (RGFW_DOUBLE_BUFFER) - pfd_flags |= PFD_DOUBLEBUFFER; - - PIXELFORMATDESCRIPTOR pfd = { - sizeof(pfd), - 1, /* version */ - pfd_flags, - PFD_TYPE_RGBA, /* ipixel type */ - 24, /* color bits */ - 0, 0, 0, 0, 0, 0, - 8, /* alpha bits */ - 0, 0, 0, 0, 0, 0, - 32, /* depth bits */ - 8, /* stencil bits */ - 0, - PFD_MAIN_PLANE, /* Layer type */ - 0, 0, 0, 0 - }; - - int pixel_format = ChoosePixelFormat(dummy_dc, &pfd); - SetPixelFormat(dummy_dc, pixel_format, &pfd); - - HGLRC dummy_context = wglCreateContext(dummy_dc); - wglMakeCurrent(dummy_dc, dummy_context); - - if (wglChoosePixelFormatARB == NULL) { - wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) (void*) wglGetProcAddress("wglCreateContextAttribsARB"); - wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) (void*)wglGetProcAddress("wglChoosePixelFormatARB"); - } - - wglMakeCurrent(dummy_dc, 0); - wglDeleteContext(dummy_context); - ReleaseDC(dummyWin, dummy_dc); - - /* try to create the pixel format we want for opengl and then try to create an opengl context for the specified version */ - if (wglCreateContextAttribsARB != NULL) { - PIXELFORMATDESCRIPTOR pfd = {sizeof(pfd), 1, pfd_flags, PFD_TYPE_RGBA, 32, 8, PFD_MAIN_PLANE, 24, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - - if (args & RGFW_OPENGL_SOFTWARE) - pfd.dwFlags |= PFD_GENERIC_FORMAT | PFD_GENERIC_ACCELERATED; - - if (wglChoosePixelFormatARB != NULL) { - i32* pixel_format_attribs = (i32*)RGFW_initFormatAttribs(args & RGFW_OPENGL_SOFTWARE); - - int pixel_format; - UINT num_formats; - wglChoosePixelFormatARB(win->src.hdc, pixel_format_attribs, 0, 1, &pixel_format, &num_formats); - if (!num_formats) { - printf("Failed to create a pixel format for WGL.\n"); - } - - DescribePixelFormat(win->src.hdc, pixel_format, sizeof(pfd), &pfd); - if (!SetPixelFormat(win->src.hdc, pixel_format, &pfd)) { - printf("Failed to set the WGL pixel format.\n"); - } - } - - /* create opengl/WGL context for the specified version */ - u32 index = 0; - i32 attribs[40]; - - if (RGFW_profile == RGFW_GL_CORE) { - 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_majorVersion || RGFW_minorVersion) { - SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_majorVersion); - SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_minorVersion); - } - - SET_ATTRIB(0, 0); - - win->src.ctx = (HGLRC)wglCreateContextAttribsARB(win->src.hdc, NULL, attribs); - } else { /* fall back to a default context (probably opengl 2 or something) */ - fprintf(stderr, "Failed to create an accelerated OpenGL Context\n"); - - int pixel_format = ChoosePixelFormat(win->src.hdc, &pfd); - SetPixelFormat(win->src.hdc, pixel_format, &pfd); - - win->src.ctx = wglCreateContext(win->src.hdc); - } - - wglMakeCurrent(win->src.hdc, win->src.ctx); -#endif - } - -#ifdef RGFW_OSMESA -#ifdef RGFW_LINK_OSM ESA - OSMesaMakeCurrentSource = (PFN_OSMesaMakeCurrent) GetProcAddress(win->src.hdc, "OSMesaMakeCurrent"); - OSMesaCreateContextSource = (PFN_OSMesaCreateContext) GetProcAddress(win->src.hdc, "OSMesaCreateContext"); - OSMesaDestroyContextSource = (PFN_OSMesaDestroyContext) GetProcAddress(win->src.hdc, "OSMesaDestroyContext"); -#endif -#endif - -#ifdef RGFW_OPENGL - if ((args & RGFW_NO_INIT_API) == 0) { - ReleaseDC(win->src.window, win->src.hdc); - win->src.hdc = GetDC(win->src.window); - wglMakeCurrent(win->src.hdc, win->src.ctx); - } -#endif - - DestroyWindow(dummyWin); - RGFW_init_buffer(win); - - - #ifndef RGFW_NO_MONITOR - if (args & RGFW_SCALE_TO_MONITOR) - RGFW_window_scaleToMonitor(win); - #endif - - if (args & RGFW_CENTER) { - RGFW_area screenR = RGFW_getScreenSize(); - RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2)); - } - -#ifdef RGFW_EGL - if ((args & RGFW_NO_INIT_API) == 0) - RGFW_createOpenGLContext(win); -#endif - - if (args & RGFW_HIDE_MOUSE) - RGFW_window_showMouse(win, 0); - - if (args & RGFW_TRANSPARENT_WINDOW) { - SetWindowLong(win->src.window, GWL_EXSTYLE, GetWindowLong(win->src.window, GWL_EXSTYLE) | WS_EX_LAYERED); - SetLayeredWindowAttributes(win->src.window, RGB(255, 255, 255), RGFW_ALPHA, LWA_ALPHA); - } - - ShowWindow(win->src.window, SW_SHOWNORMAL); - - if (RGFW_root == NULL) - RGFW_root = win; - - #ifdef RGFW_OPENGL - else - wglShareLists(RGFW_root->src.ctx, win->src.ctx); - #endif - - return win; - } - - void RGFW_window_setBorder(RGFW_window* win, u8 border) { - DWORD 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 { - 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 - ); - } - } - - - RGFW_area RGFW_getScreenSize(void) { - return RGFW_AREA(GetDeviceCaps(GetDC(NULL), HORZRES), GetDeviceCaps(GetDC(NULL), VERTRES)); - } - - RGFW_point RGFW_getGlobalMousePoint(void) { - POINT p; - GetCursorPos(&p); - - return RGFW_POINT(p.x, p.y); - } - - RGFW_point RGFW_window_getMousePoint(RGFW_window* win) { - POINT p; - GetCursorPos(&p); - ScreenToClient(win->src.window, &p); - - return RGFW_POINT(p.x, p.y); - } - - void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { - assert(win != NULL); - win->src.minSize = a; - } - - void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { - assert(win != NULL); - win->src.maxSize = a; - } - - - void RGFW_window_minimize(RGFW_window* win) { - assert(win != NULL); - - ShowWindow(win->src.window, SW_MINIMIZE); - } - - void RGFW_window_restore(RGFW_window* win) { - assert(win != NULL); - - ShowWindow(win->src.window, SW_RESTORE); - } - - - u8 RGFW_xinput2RGFW[] = { - RGFW_JS_A, /* or PS X button */ - RGFW_JS_B, /* or PS circle button */ - RGFW_JS_X, /* or PS square button */ - RGFW_JS_Y, /* or PS triangle button */ - RGFW_JS_R1, /* right bumper */ - RGFW_JS_L1, /* left bump */ - RGFW_JS_L2, /* left trigger*/ - RGFW_JS_R2, /* right trigger */ - 0, 0, 0, 0, 0, 0, 0, 0, - RGFW_JS_UP, /* dpad up */ - RGFW_JS_DOWN, /* dpad down*/ - RGFW_JS_LEFT, /* dpad left */ - RGFW_JS_RIGHT, /* dpad right */ - RGFW_JS_START, /* start button */ - RGFW_JS_SELECT/* select button */ - }; - - static i32 RGFW_checkXInput(RGFW_window* win, RGFW_Event* e) { - RGFW_UNUSED(win) - - size_t 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_BACK) - continue; - - // RGFW_jsButtonPressed + 1 = RGFW_jsButtonReleased - e->type = RGFW_jsButtonPressed + !(keystroke.Flags & XINPUT_KEYSTROKE_KEYDOWN); - e->button = RGFW_xinput2RGFW[keystroke.VirtualKey - 0x5800]; - RGFW_jsPressed[i][e->button] = !(keystroke.Flags & XINPUT_KEYSTROKE_KEYDOWN); - - return 1; - } - - XINPUT_STATE state; - if (XInputGetState == NULL || - XInputGetState((DWORD) i, &state) == ERROR_DEVICE_NOT_CONNECTED - ) - return 0; -#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(state.Gamepad.sThumbLX, state.Gamepad.sThumbLY); - RGFW_point axis2 = RGFW_POINT(state.Gamepad.sThumbRX, state.Gamepad.sThumbRY); - - if (axis1.x != e->axis[0].x || axis1.y != e->axis[0].y || axis2.x != e->axis[1].x || axis2.y != e->axis[1].y) { - e->type = RGFW_jsAxisMove; - - e->axis[0] = axis1; - e->axis[1] = axis2; - - return 1; - } - - e->axis[0] = axis1; - e->axis[1] = axis2; - } - - return 0; - } - - 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 * 1e3), QS_ALLINPUT); - } - - RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) { - assert(win != NULL); - - if (win->event.type == RGFW_quit) { - return NULL; - } - - MSG msg; - - if (RGFW_eventWindow.src.window == win->src.window) { - if (RGFW_eventWindow.r.x != -1) { - win->r.x = RGFW_eventWindow.r.x; - win->r.y = RGFW_eventWindow.r.y; - win->event.type = RGFW_windowMoved; - RGFW_windowMoveCallback(win, win->r); - } - - if (RGFW_eventWindow.r.w != -1) { - win->r.w = RGFW_eventWindow.r.w; - win->r.h = RGFW_eventWindow.r.h; - win->event.type = RGFW_windowResized; - RGFW_windowResizeCallback(win, win->r); - } - - RGFW_eventWindow.src.window = NULL; - RGFW_eventWindow.r = RGFW_RECT(-1, -1, -1, -1); - - return &win->event; - } - - - static HDROP drop; - - if (win->event.type == RGFW_dnd_init) { - 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); - //win->event.droppedFiles = (char**)RGFW_CALLOC(win->event.droppedFilesCount, sizeof(char*)); - - u32 i; - for (i = 0; i < win->event.droppedFilesCount; i++) { - const UINT length = DragQueryFileW(drop, i, NULL, 0); - WCHAR* buffer = (WCHAR*) RGFW_CALLOC((size_t) length + 1, sizeof(WCHAR)); - - DragQueryFileW(drop, i, buffer, length + 1); - strncpy(win->event.droppedFiles[i], createUTF8FromWideStringWin32(buffer), RGFW_MAX_PATH); - win->event.droppedFiles[i][RGFW_MAX_PATH - 1] = '\0'; - RGFW_FREE(buffer); - } - - DragFinish(drop); - RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount); - - win->event.type = RGFW_dnd; - return &win->event; - } - - win->event.inFocus = (GetForegroundWindow() == win->src.window); - - if (RGFW_checkXInput(win, &win->event)) - return &win->event; - - static BYTE keyboardState[256]; - - if (PeekMessageA(&msg, win->src.window, 0u, 0u, PM_REMOVE)) { - switch (msg.message) { - case WM_CLOSE: - case WM_QUIT: - RGFW_windowQuitCallback(win); - win->event.type = RGFW_quit; - break; - - case WM_ACTIVATE: - win->event.inFocus = (LOWORD(msg.wParam) == WA_INACTIVE); - - if (win->event.inFocus) { - win->event.type = RGFW_focusIn; - RGFW_focusCallback(win, 1); - } - else { - win->event.type = RGFW_focusOut; - RGFW_focusCallback(win, 0); - } - - break; - - case WM_PAINT: - win->event.type = RGFW_windowRefresh; - RGFW_windowRefreshCallback(win); - break; - - case WM_MOUSELEAVE: - win->event.type = RGFW_mouseLeave; - win->_winArgs |= RGFW_MOUSE_LEFT; - RGFW_mouseNotifyCallBack(win, win->event.point, 0); - break; - - case WM_KEYUP: { - win->event.keyCode = RGFW_apiKeyCodeToRGFW((u32) msg.wParam); - - RGFW_keyboard[win->event.keyCode].prev = RGFW_isPressed(win, win->event.keyCode); - - static char keyName[16]; - - { - GetKeyNameTextA((LONG) msg.lParam, keyName, 16); - - if ((!(GetKeyState(VK_CAPITAL) & 0x0001) && !(GetKeyState(VK_SHIFT) & 0x8000)) || - ((GetKeyState(VK_CAPITAL) & 0x0001) && (GetKeyState(VK_SHIFT) & 0x8000))) { - CharLowerBuffA(keyName, 16); - } - } - - RGFW_updateLockState(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001)); - - strncpy(win->event.keyName, keyName, 16); - - if (RGFW_isPressed(win, RGFW_ShiftL)) { - ToAscii((UINT) msg.wParam, MapVirtualKey((UINT) msg.wParam, MAPVK_VK_TO_CHAR), - keyboardState, (LPWORD) win->event.keyName, 0); - } - - win->event.type = RGFW_keyReleased; - RGFW_keyboard[win->event.keyCode].current = 0; - RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 0); - break; - } - case WM_KEYDOWN: { - win->event.keyCode = RGFW_apiKeyCodeToRGFW((u32) msg.wParam); - - RGFW_keyboard[win->event.keyCode].prev = RGFW_isPressed(win, win->event.keyCode); - - static char keyName[16]; - - { - GetKeyNameTextA((LONG) msg.lParam, keyName, 16); - - if ((!(GetKeyState(VK_CAPITAL) & 0x0001) && !(GetKeyState(VK_SHIFT) & 0x8000)) || - ((GetKeyState(VK_CAPITAL) & 0x0001) && (GetKeyState(VK_SHIFT) & 0x8000))) { - CharLowerBuffA(keyName, 16); - } - } - - RGFW_updateLockState(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001)); - - strncpy(win->event.keyName, keyName, 16); - - if (RGFW_isPressed(win, RGFW_ShiftL) & 0x8000) { - ToAscii((UINT) msg.wParam, MapVirtualKey((UINT) msg.wParam, MAPVK_VK_TO_CHAR), - keyboardState, (LPWORD) win->event.keyName, 0); - } - - win->event.type = RGFW_keyPressed; - win->event.repeat = RGFW_isPressed(win, win->event.keyCode); - RGFW_keyboard[win->event.keyCode].current = 1; - RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 1); - break; - } - - case WM_MOUSEMOVE: - if ((win->_winArgs & RGFW_HOLD_MOUSE)) - break; - - win->event.type = RGFW_mousePosChanged; - - win->event.point.x = GET_X_LPARAM(msg.lParam); - win->event.point.y = GET_Y_LPARAM(msg.lParam); - - RGFW_mousePosCallback(win, win->event.point); - - if (win->_winArgs & RGFW_MOUSE_LEFT) { - win->_winArgs ^= RGFW_MOUSE_LEFT; - win->event.type = RGFW_mouseEnter; - RGFW_mouseNotifyCallBack(win, win->event.point, 1); - } - - break; - - case WM_INPUT: { - if (!(win->_winArgs & RGFW_HOLD_MOUSE)) - break; - - unsigned size = sizeof(RAWINPUT); - static RAWINPUT raw[sizeof(RAWINPUT)]; - 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; - - win->event.type = RGFW_mousePosChanged; - win->event.point.x = raw->data.mouse.lLastX; - win->event.point.y = raw->data.mouse.lLastY; - break; - } - - case WM_LBUTTONDOWN: - win->event.button = RGFW_mouseLeft; - RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; - RGFW_mouseButtons[win->event.button].current = 1; - win->event.type = RGFW_mouseButtonPressed; - RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); - break; - case WM_RBUTTONDOWN: - win->event.button = RGFW_mouseRight; - 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_MBUTTONDOWN: - win->event.button = 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_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_LBUTTONUP: - - win->event.button = RGFW_mouseLeft; - 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_RBUTTONUP: - win->event.button = RGFW_mouseRight; - 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_MBUTTONUP: - win->event.button = 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; - - /* - much of this event is source from glfw - */ - case WM_DROPFILES: { - win->event.type = RGFW_dnd_init; - - 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; - case WM_GETMINMAXINFO: - { - if (win->src.maxSize.w == 0 && win->src.maxSize.h == 0) - break; - - MINMAXINFO* mmi = (MINMAXINFO*) msg.lParam; - mmi->ptMinTrackSize.x = win->src.minSize.w; - mmi->ptMinTrackSize.y = win->src.minSize.h; - mmi->ptMaxTrackSize.x = win->src.maxSize.w; - mmi->ptMaxTrackSize.y = win->src.maxSize.h; - return 0; - } - default: - win->event.type = 0; - break; - } - - TranslateMessage(&msg); - DispatchMessageA(&msg); - } - - else - win->event.type = 0; - - if (!IsWindow(win->src.window)) { - win->event.type = RGFW_quit; - RGFW_windowQuitCallback(win); - } - - if (win->event.type) - return &win->event; - else - return NULL; - } - - u8 RGFW_window_isFullscreen(RGFW_window* win) { - assert(win != NULL); - - #ifndef __cplusplus - WINDOWPLACEMENT placement = { 0 }; - #else - WINDOWPLACEMENT placement = { }; - #endif - GetWindowPlacement(win->src.window, &placement); - return placement.showCmd == SW_SHOWMAXIMIZED; - } - - u8 RGFW_window_isHidden(RGFW_window* win) { - assert(win != NULL); - - return IsWindowVisible(win->src.window) == 0 && !RGFW_window_isMinimized(win); - } - - u8 RGFW_window_isMinimized(RGFW_window* win) { - assert(win != NULL); - - #ifndef __cplusplus - WINDOWPLACEMENT placement = { 0 }; - #else - WINDOWPLACEMENT placement = { }; - #endif - GetWindowPlacement(win->src.window, &placement); - return placement.showCmd == SW_SHOWMINIMIZED; - } - - u8 RGFW_window_isMaximized(RGFW_window* win) { - assert(win != NULL); - - #ifndef __cplusplus - WINDOWPLACEMENT placement = { 0 }; - #else - WINDOWPLACEMENT placement = { }; - #endif - GetWindowPlacement(win->src.window, &placement); - return placement.showCmd == SW_SHOWMAXIMIZED; - } - - typedef struct { int iIndex; HMONITOR hMonitor; } RGFW_mInfo; - BOOL CALLBACK GetMonitorByHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { - RGFW_UNUSED(hdcMonitor) - RGFW_UNUSED(lprcMonitor) - - RGFW_mInfo* info = (RGFW_mInfo*) dwData; - if (info->hMonitor == hMonitor) - return FALSE; - - info->iIndex++; - return TRUE; - } - - #ifndef RGFW_NO_MONITOR - RGFW_monitor win32CreateMonitor(HMONITOR src) { - RGFW_monitor monitor; - MONITORINFO monitorInfo; - - monitorInfo.cbSize = sizeof(MONITORINFO); - GetMonitorInfoA(src, &monitorInfo); - - RGFW_mInfo info; - info.iIndex = 0; - info.hMonitor = src; - - /* get the monitor's index */ - if (EnumDisplayMonitors(NULL, NULL, GetMonitorByHandle, (LPARAM) &info)) { - DISPLAY_DEVICEA dd; - dd.cb = sizeof(dd); - - /* loop through the devices until you find a device with the monitor's index */ - size_t deviceIndex; - for (deviceIndex = 0; EnumDisplayDevicesA(0, (DWORD) deviceIndex, &dd, 0); deviceIndex++) { - char* deviceName = dd.DeviceName; - if (EnumDisplayDevicesA(deviceName, info.iIndex, &dd, 0)) { - strncpy(monitor.name, dd.DeviceString, 128); /*!< copy the monitor's name */ - break; - } - } - } - - monitor.rect.x = monitorInfo.rcWork.left; - monitor.rect.y = monitorInfo.rcWork.top; - monitor.rect.w = monitorInfo.rcWork.right - monitorInfo.rcWork.left; - monitor.rect.h = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top; - -#ifndef RGFW_NO_DPI - #ifndef USER_DEFAULT_SCREEN_DPI - #define USER_DEFAULT_SCREEN_DPI 96 - #endif - - if (GetDpiForMonitor != NULL) { - u32 x, y; - GetDpiForMonitor(src, MDT_EFFECTIVE_DPI, &x, &y); - - monitor.scaleX = (float) (x) / (float) USER_DEFAULT_SCREEN_DPI; - monitor.scaleY = (float) (y) / (float) USER_DEFAULT_SCREEN_DPI; - } -#endif - - HDC hdc = GetDC(NULL); - /* get pixels per inch */ - i32 ppiX = GetDeviceCaps(hdc, LOGPIXELSX); - i32 ppiY = GetDeviceCaps(hdc, LOGPIXELSY); - ReleaseDC(NULL, hdc); - - /* Calculate physical height in inches */ - monitor.physW = GetSystemMetrics(SM_CYSCREEN) / (float) ppiX; - monitor.physH = GetSystemMetrics(SM_CXSCREEN) / (float) ppiY; - - return monitor; - } - #endif /* RGFW_NO_MONITOR */ - - - #ifndef RGFW_NO_MONITOR - RGFW_monitor RGFW_monitors[6]; - 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; - - RGFW_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(void) { - RGFW_mInfo info; - info.iIndex = 0; - while (EnumDisplayMonitors(NULL, NULL, GetMonitorHandle, (LPARAM) &info)); - - return RGFW_monitors; - } - - RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { - HMONITOR src = MonitorFromWindow(win->src.window, MONITOR_DEFAULTTOPRIMARY); - return win32CreateMonitor(src); - } - #endif - - HICON RGFW_loadHandleImage(RGFW_window* win, u8* src, RGFW_area a, BOOL icon) { - assert(win != NULL); - - u32 i; - HDC dc; - HICON handle; - HBITMAP color, mask; - BITMAPV5HEADER bi; - ICONINFO ii; - u8* target = NULL; - u8* source = src; - - ZeroMemory(&bi, sizeof(bi)); - bi.bV5Size = sizeof(bi); - bi.bV5Width = a.w; - bi.bV5Height = -((LONG) a.h); - bi.bV5Planes = 1; - bi.bV5BitCount = 32; - bi.bV5Compression = BI_BITFIELDS; - bi.bV5RedMask = 0x00ff0000; - bi.bV5GreenMask = 0x0000ff00; - bi.bV5BlueMask = 0x000000ff; - bi.bV5AlphaMask = 0xff000000; - - dc = GetDC(NULL); - color = CreateDIBSection(dc, - (BITMAPINFO*) &bi, - DIB_RGB_COLORS, - (void**) &target, - NULL, - (DWORD) 0); - ReleaseDC(NULL, dc); - - mask = CreateBitmap(a.w, a.h, 1, 1, NULL); - - for (i = 0; i < a.w * a.h; i++) { - target[0] = source[2]; - target[1] = source[1]; - target[2] = source[0]; - target[3] = source[3]; - target += 4; - source += 4; - } - - ZeroMemory(&ii, sizeof(ii)); - ii.fIcon = icon; - ii.xHotspot = 0; - ii.yHotspot = 0; - ii.hbmMask = mask; - ii.hbmColor = color; - - handle = CreateIconIndirect(&ii); - - DeleteObject(color); - DeleteObject(mask); - - return handle; - } - - void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) { - assert(win != NULL); - RGFW_UNUSED(channels) - - HCURSOR cursor = (HCURSOR) RGFW_loadHandleImage(win, image, a, FALSE); - SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) cursor); - SetCursor(cursor); - DestroyCursor(cursor); - } - - void RGFW_window_setMouseDefault(RGFW_window* win) { - RGFW_window_setMouseStandard(win, RGFW_MOUSE_ARROW); - } - - void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { - assert(win != NULL); - - if (mouse > (sizeof(RGFW_mouseIconSrc) / sizeof(u32))) - return; - - char* icon = MAKEINTRESOURCEA(RGFW_mouseIconSrc[mouse]); - - SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) LoadCursorA(NULL, icon)); - SetCursor(LoadCursorA(NULL, icon)); - } - - void RGFW_window_hide(RGFW_window* win) { - ShowWindow(win->src.window, SW_HIDE); - } - - void RGFW_window_show(RGFW_window* win) { - ShowWindow(win->src.window, SW_RESTORE); - } - - void RGFW_window_close(RGFW_window* win) { - assert(win != NULL); - -#ifdef RGFW_EGL - RGFW_closeEGL(win); -#endif - - if (win == RGFW_root) { -#ifdef RGFW_DIRECTX - RGFW_dxInfo.pDeviceContext->lpVtbl->Release(RGFW_dxInfo.pDeviceContext); - RGFW_dxInfo.pDevice->lpVtbl->Release(RGFW_dxInfo.pDevice); - RGFW_dxInfo.pAdapter->lpVtbl->Release(RGFW_dxInfo.pAdapter); - RGFW_dxInfo.pFactory->lpVtbl->Release(RGFW_dxInfo.pFactory); -#endif - - if (RGFW_XInput_dll != NULL) { - FreeLibrary(RGFW_XInput_dll); - RGFW_XInput_dll = NULL; - } - - #ifndef RGFW_NO_DPI - if (RGFW_Shcore_dll != NULL) { - FreeLibrary(RGFW_Shcore_dll); - RGFW_Shcore_dll = NULL; - } - #endif - - if (wglinstance != NULL) { - FreeLibrary(wglinstance); - wglinstance = NULL; - } - - RGFW_root = NULL; - } - -#ifdef RGFW_DIRECTX - win->src.swapchain->lpVtbl->Release(win->src.swapchain); - win->src.renderTargetView->lpVtbl->Release(win->src.renderTargetView); - win->src.pDepthStencilView->lpVtbl->Release(win->src.pDepthStencilView); -#endif - -#ifdef RGFW_BUFFER - DeleteDC(win->src.hdcMem); - DeleteObject(win->src.bitmap); -#endif - -#ifdef RGFW_OPENGL - wglDeleteContext((HGLRC) win->src.ctx); /*!< delete opengl context */ -#endif - DeleteDC(win->src.hdc); /*!< delete device context */ - DestroyWindow(win->src.window); /*!< delete window */ - -#if defined(RGFW_OSMESA) - if (win->buffer != NULL) - RGFW_FREE(win->buffer); -#endif - -#ifdef RGFW_ALLOC_DROPFILES - { - u32 i; - for (i = 0; i < RGFW_MAX_DROPS; i++) - RGFW_FREE(win->event.droppedFiles[i]); - - - RGFW_FREE(win->event.droppedFiles); - } -#endif - - RGFW_FREE(win); - } - - void RGFW_window_move(RGFW_window* win, RGFW_point v) { - assert(win != NULL); - - win->r.x = v.x; - win->r.y = v.y; - SetWindowPos(win->src.window, HWND_TOP, win->r.x, win->r.y, 0, 0, SWP_NOSIZE); - } - - void RGFW_window_resize(RGFW_window* win, RGFW_area a) { - assert(win != NULL); - - win->r.w = a.w; - win->r.h = a.h; - SetWindowPos(win->src.window, HWND_TOP, 0, 0, win->r.w, win->r.h + win->src.hOffset, SWP_NOMOVE); - } - - - void RGFW_window_setName(RGFW_window* win, char* name) { - assert(win != NULL); - - SetWindowTextA(win->src.window, name); - } - - /* sourced from GLFW */ - #ifndef RGFW_NO_PASSTHROUGH - void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) { - assert(win != NULL); - - COLORREF key = 0; - BYTE alpha = 0; - DWORD flags = 0; - DWORD 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; - // NOTE: Window opacity also needs the layered window style so do not - // remove it if the window is alpha blended - if (exStyle & WS_EX_LAYERED) - { - if (!(flags & LWA_ALPHA)) - exStyle &= ~WS_EX_LAYERED; - } - } - - SetWindowLongW(win->src.window, GWL_EXSTYLE, exStyle); - - if (passthrough) { - SetLayeredWindowAttributes(win->src.window, key, alpha, flags); - } - } - #endif - - /* much of this function is sourced from GLFW */ - void RGFW_window_setIcon(RGFW_window* win, u8* src, RGFW_area a, i32 channels) { - assert(win != NULL); - #ifndef RGFW_WIN95 - RGFW_UNUSED(channels) - - HICON handle = RGFW_loadHandleImage(win, src, a, TRUE); - - SetClassLongPtrA(win->src.window, GCLP_HICON, (LPARAM) handle); - - DestroyIcon(handle); - #else - RGFW_UNUSED(src) - RGFW_UNUSED(a) - RGFW_UNUSED(channels) - #endif - } - - char* RGFW_readClipboard(size_t* size) { - /* Open the clipboard */ - if (OpenClipboard(NULL) == 0) - return (char*) ""; - - /* Get the clipboard data as a Unicode string */ - HANDLE hData = GetClipboardData(CF_UNICODETEXT); - if (hData == NULL) { - CloseClipboard(); - return (char*) ""; - } - - wchar_t* wstr = (wchar_t*) GlobalLock(hData); - - char* text; - - { - setlocale(LC_ALL, "en_US.UTF-8"); - - size_t textLen = wcstombs(NULL, wstr, 0); - if (textLen == 0) - return (char*) ""; - - text = (char*) RGFW_MALLOC((textLen * sizeof(char)) + 1); - - wcstombs(text, wstr, (textLen) +1); - - if (size != NULL) - *size = textLen + 1; - - text[textLen] = '\0'; - } - - /* Release the clipboard data */ - GlobalUnlock(hData); - CloseClipboard(); - - return text; - } - - void RGFW_writeClipboard(const char* text, u32 textLen) { - HANDLE object; - WCHAR* buffer; - - object = GlobalAlloc(GMEM_MOVEABLE, (1 + textLen) * sizeof(WCHAR)); - if (!object) - return; - - buffer = (WCHAR*) GlobalLock(object); - if (!buffer) { - GlobalFree(object); - return; - } - - MultiByteToWideChar(CP_UTF8, 0, text, -1, buffer, textLen); - GlobalUnlock(object); - - if (!OpenClipboard(RGFW_root->src.window)) { - GlobalFree(object); - return; - } - - EmptyClipboard(); - SetClipboardData(CF_UNICODETEXT, object); - CloseClipboard(); - } - - u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber) { - assert(win != NULL); - - RGFW_UNUSED(jsNumber) - - return RGFW_registerJoystickF(win, (char*) ""); - } - - u16 RGFW_registerJoystickF(RGFW_window* win, char* file) { - assert(win != NULL); - RGFW_UNUSED(file) - - return RGFW_joystickCount - 1; - } - - void RGFW_window_moveMouse(RGFW_window* win, RGFW_point p) { - assert(win != NULL); - - 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); - } - #endif - - #ifndef RGFW_EGL - void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { - assert(win != NULL); - - #if defined(RGFW_OPENGL) - typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval); - static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; - static void* loadSwapFunc = (void*) 1; - - if (loadSwapFunc == NULL) { - fprintf(stderr, "wglSwapIntervalEXT not supported\n"); - return; - } - - if (wglSwapIntervalEXT == NULL) { - loadSwapFunc = (void*) wglGetProcAddress("wglSwapIntervalEXT"); - wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) loadSwapFunc; - } - - if (wglSwapIntervalEXT(swapInterval) == FALSE) - fprintf(stderr, "Failed to set swap interval\n"); - #else - RGFW_UNUSED(swapInterval); - #endif - - } - #endif - - void RGFW_window_swapBuffers(RGFW_window* win) { - //assert(win != NULL); - /* clear the window*/ - - if (!(win->_winArgs & RGFW_NO_CPU_RENDER)) { -#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - #ifdef RGFW_OSMESA - RGFW_OSMesa_reorganize(); - #endif - - HGDIOBJ oldbmp = SelectObject(win->src.hdcMem, win->src.bitmap); - BitBlt(win->src.hdc, 0, 0, win->r.w, win->r.h, win->src.hdcMem, 0, 0, SRCCOPY); - SelectObject(win->src.hdcMem, oldbmp); -#endif - } - - if (!(win->_winArgs & RGFW_NO_GPU_RENDER)) { - #ifdef RGFW_EGL - eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); - #elif defined(RGFW_OPENGL) - SwapBuffers(win->src.hdc); - #endif - - #if defined(RGFW_WINDOWS) && defined(RGFW_DIRECTX) - win->src.swapchain->lpVtbl->Present(win->src.swapchain, 0, 0); - #endif - } - } - - char* createUTF8FromWideStringWin32(const WCHAR* source) { - char* target; - i32 size; - - size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL); - if (!size) { - return NULL; - } - - target = (char*) RGFW_CALLOC(size, 1); - - if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, size, NULL, NULL)) { - RGFW_FREE(target); - return NULL; - } - - return target; - } - - static inline LARGE_INTEGER RGFW_win32_initTimer(void) { - static LARGE_INTEGER frequency = {{0, 0}}; - if (frequency.QuadPart == 0) { - timeBeginPeriod(1); - QueryPerformanceFrequency(&frequency); - } - - return frequency; - } - - u64 RGFW_getTimeNS(void) { - LARGE_INTEGER frequency = RGFW_win32_initTimer(); - - LARGE_INTEGER counter; - QueryPerformanceCounter(&counter); - - return (u64) ((counter.QuadPart * 1e9) / frequency.QuadPart); - } - - u64 RGFW_getTime(void) { - LARGE_INTEGER frequency = RGFW_win32_initTimer(); - - LARGE_INTEGER counter; - QueryPerformanceCounter(&counter); - return (u64) (counter.QuadPart / (double) frequency.QuadPart); - } - - void RGFW_sleep(u64 ms) { - Sleep(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 - - typedef CGRect NSRect; - typedef CGPoint NSPoint; - typedef CGSize NSSize; - - typedef void NSBitmapImageRep; - typedef void NSCursor; - typedef void NSDraggingInfo; - typedef void NSWindow; - typedef void NSApplication; - typedef void NSScreen; - typedef void NSEvent; - typedef void NSString; - typedef void NSOpenGLContext; - typedef void NSPasteboard; - typedef void NSColor; - typedef void NSArray; - typedef void NSImageRep; - typedef void NSImage; - typedef void NSOpenGLView; - - - typedef const char* NSPasteboardType; - typedef unsigned long NSUInteger; - typedef long NSInteger; - typedef NSInteger NSModalResponse; - -#ifdef __arm64__ - /* ARM just uses objc_msgSend */ -#define abi_objc_msgSend_stret objc_msgSend -#define abi_objc_msgSend_fpret objc_msgSend -#else /* __i386__ */ - /* x86 just uses abi_objc_msgSend_fpret and (NSColor *)objc_msgSend_id respectively */ -#define abi_objc_msgSend_stret objc_msgSend_stret -#define abi_objc_msgSend_fpret objc_msgSend_fpret -#endif - -#define NSAlloc(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("alloc")) -#define objc_msgSend_bool ((BOOL (*)(id, SEL))objc_msgSend) -#define objc_msgSend_void ((void (*)(id, SEL))objc_msgSend) -#define objc_msgSend_void_id ((void (*)(id, SEL, id))objc_msgSend) -#define objc_msgSend_uint ((NSUInteger (*)(id, SEL))objc_msgSend) -#define objc_msgSend_void_bool ((void (*)(id, SEL, BOOL))objc_msgSend) -#define objc_msgSend_bool_void ((BOOL (*)(id, SEL))objc_msgSend) -#define objc_msgSend_void_SEL ((void (*)(id, SEL, SEL))objc_msgSend) -#define objc_msgSend_id ((id (*)(id, SEL))objc_msgSend) -#define objc_msgSend_id_id ((id (*)(id, SEL, id))objc_msgSend) -#define objc_msgSend_id_bool ((BOOL (*)(id, SEL, id))objc_msgSend) -#define objc_msgSend_int ((id (*)(id, SEL, int))objc_msgSend) -#define objc_msgSend_arr ((id (*)(id, SEL, int))objc_msgSend) -#define objc_msgSend_ptr ((id (*)(id, SEL, void*))objc_msgSend) -#define objc_msgSend_class ((id (*)(Class, SEL))objc_msgSend) -#define objc_msgSend_class_char ((id (*)(Class, SEL, char*))objc_msgSend) - - NSApplication* NSApp = NULL; - - void NSRelease(id obj) { - objc_msgSend_void(obj, sel_registerName("release")); - } - - #define release NSRelease - - NSString* 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(NSString* str) { - return ((const char* (*)(id, SEL)) objc_msgSend) (str, sel_registerName("UTF8String")); - } - - void si_impl_func_to_SEL_with_name(const char* class_name, const char* register_name, void* function) { - Class selected_class; - - if (strcmp(class_name, "NSView") == 0) { - selected_class = objc_getClass("ViewClass"); - } else if (strcmp(class_name, "NSWindow") == 0) { - selected_class = objc_getClass("WindowClass"); - } else { - selected_class = objc_getClass(class_name); - } - - class_addMethod(selected_class, sel_registerName(register_name), (IMP) function, 0); - } - - /* Header for the array. */ - typedef struct siArrayHeader { - size_t count; - /* TODO(EimaMei): Add a `type_width` later on. */ - } siArrayHeader; - - /* Gets the header of the siArray. */ -#define SI_ARRAY_HEADER(s) ((siArrayHeader*)s - 1) - - void* si_array_init_reserve(size_t sizeof_element, size_t count) { - siArrayHeader* ptr = malloc(sizeof(siArrayHeader) + (sizeof_element * count)); - void* array = ptr + sizeof(siArrayHeader); - - siArrayHeader* header = SI_ARRAY_HEADER(array); - header->count = count; - - return array; - } - -#define si_array_len(array) (SI_ARRAY_HEADER(array)->count) -#define si_func_to_SEL(class_name, function) si_impl_func_to_SEL_with_name(class_name, #function":", function) - /* Creates an Objective-C method (SEL) from a regular C function with the option to set the register name.*/ -#define si_func_to_SEL_with_name(class_name, register_name, function) si_impl_func_to_SEL_with_name(class_name, register_name":", function) - - unsigned char* NSBitmapImageRep_bitmapData(NSBitmapImageRep* imageRep) { - return ((unsigned char* (*)(id, SEL))objc_msgSend) - (imageRep, sel_registerName("bitmapData")); - } - -#define NS_ENUM(type, name) type name; enum - - typedef NS_ENUM(NSUInteger, NSBitmapFormat) { - NSBitmapFormatAlphaFirst = 1 << 0, // 0 means is alpha last (RGBA, CMYKA, etc.) - NSBitmapFormatAlphaNonpremultiplied = 1 << 1, // 0 means is premultiplied - NSBitmapFormatFloatingPointSamples = 1 << 2, // 0 is integer - - NSBitmapFormatSixteenBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 8), - NSBitmapFormatThirtyTwoBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 9), - NSBitmapFormatSixteenBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 10), - NSBitmapFormatThirtyTwoBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 11) - }; - - NSBitmapImageRep* NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits) { - void* func = sel_registerName("initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bitmapFormat:bytesPerRow:bitsPerPixel:"); - - return (NSBitmapImageRep*) ((id(*)(id, SEL, unsigned char**, NSInteger, NSInteger, NSInteger, NSInteger, bool, bool, const char*, NSBitmapFormat, NSInteger, NSInteger))objc_msgSend) - (NSAlloc((id)objc_getClass("NSBitmapImageRep")), func, planes, width, height, bps, spp, alpha, isPlanar, NSString_stringWithUTF8String(colorSpaceName), bitmapFormat, rowBytes, pixelBits); - } - - NSColor* NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha) { - void* nsclass = objc_getClass("NSColor"); - void* func = sel_registerName("colorWithSRGBRed:green:blue:alpha:"); - return ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend) - (nsclass, func, red, green, blue, alpha); - } - - NSCursor* NSCursor_initWithImage(NSImage* newImage, NSPoint aPoint) { - void* func = sel_registerName("initWithImage:hotSpot:"); - void* nsclass = objc_getClass("NSCursor"); - - return (NSCursor*) ((id(*)(id, SEL, id, NSPoint))objc_msgSend) - (NSAlloc(nsclass), func, newImage, aPoint); - } - - void NSImage_addRepresentation(NSImage* image, NSImageRep* imageRep) { - void* func = sel_registerName("addRepresentation:"); - objc_msgSend_void_id(image, func, imageRep); - } - - NSImage* NSImage_initWithSize(NSSize size) { - void* func = sel_registerName("initWithSize:"); - return ((id(*)(id, SEL, NSSize))objc_msgSend) - (NSAlloc((id)objc_getClass("NSImage")), func, size); - } -#define NS_OPENGL_ENUM_DEPRECATED(minVers, maxVers) API_AVAILABLE(macos(minVers)) - typedef NS_ENUM(NSInteger, NSOpenGLContextParameter) { - NSOpenGLContextParameterSwapInterval NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 222, /* 1 param. 0 -> Don't sync, 1 -> Sync to vertical retrace */ - NSOpenGLContextParametectxaceOrder NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 235, /* 1 param. 1 -> Above Window (default), -1 -> Below Window */ - NSOpenGLContextParametectxaceOpacity NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 236, /* 1 param. 1-> Surface is opaque (default), 0 -> non-opaque */ - NSOpenGLContextParametectxaceBackingSize NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 304, /* 2 params. Width/height of surface backing size */ - NSOpenGLContextParameterReclaimResources NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 308, /* 0 params. */ - NSOpenGLContextParameterCurrentRendererID NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 309, /* 1 param. Retrieves the current renderer ID */ - NSOpenGLContextParameterGPUVertexProcessing NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 310, /* 1 param. Currently processing vertices with GPU (get) */ - NSOpenGLContextParameterGPUFragmentProcessing NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 311, /* 1 param. Currently processing fragments with GPU (get) */ - NSOpenGLContextParameterHasDrawable NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 314, /* 1 param. Boolean returned if drawable is attached */ - NSOpenGLContextParameterMPSwapsInFlight NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 315, /* 1 param. Max number of swaps queued by the MP GL engine */ - - NSOpenGLContextParameterSwapRectangle API_DEPRECATED("", macos(10.0, 10.14)) = 200, /* 4 params. Set or get the swap rectangle {x, y, w, h} */ - NSOpenGLContextParameterSwapRectangleEnable API_DEPRECATED("", macos(10.0, 10.14)) = 201, /* Enable or disable the swap rectangle */ - NSOpenGLContextParameterRasterizationEnable API_DEPRECATED("", macos(10.0, 10.14)) = 221, /* Enable or disable all rasterization */ - NSOpenGLContextParameterStateValidation API_DEPRECATED("", macos(10.0, 10.14)) = 301, /* Validate state for multi-screen functionality */ - NSOpenGLContextParametectxaceSurfaceVolatile API_DEPRECATED("", macos(10.0, 10.14)) = 306, /* 1 param. Surface volatile state */ - }; - - - void NSOpenGLContext_setValues(NSOpenGLContext* context, const int* vals, NSOpenGLContextParameter param) { - void* func = sel_registerName("setValues:forParameter:"); - ((void (*)(id, SEL, const int*, NSOpenGLContextParameter))objc_msgSend) - (context, func, vals, param); - } - - void* NSOpenGLPixelFormat_initWithAttributes(const uint32_t* attribs) { - void* func = sel_registerName("initWithAttributes:"); - return (void*) ((id(*)(id, SEL, const uint32_t*))objc_msgSend) - (NSAlloc((id)objc_getClass("NSOpenGLPixelFormat")), func, attribs); - } - - NSOpenGLView* NSOpenGLView_initWithFrame(NSRect frameRect, uint32_t* format) { - void* func = sel_registerName("initWithFrame:pixelFormat:"); - return (NSOpenGLView*) ((id(*)(id, SEL, NSRect, uint32_t*))objc_msgSend) - (NSAlloc((id)objc_getClass("NSOpenGLView")), func, frameRect, format); - } - - void NSCursor_performSelector(NSCursor* cursor, void* selector) { - void* func = sel_registerName("performSelector:"); - objc_msgSend_void_SEL(cursor, func, selector); - } - - NSPasteboard* NSPasteboard_generalPasteboard(void) { - return (NSPasteboard*) objc_msgSend_id((id)objc_getClass("NSPasteboard"), sel_registerName("generalPasteboard")); - } - - NSString** cstrToNSStringArray(char** strs, size_t len) { - static NSString* nstrs[6]; - size_t i; - for (i = 0; i < len; i++) - nstrs[i] = NSString_stringWithUTF8String(strs[i]); - - return nstrs; - } - - const char* NSPasteboard_stringForType(NSPasteboard* pasteboard, NSPasteboardType dataType) { - void* func = sel_registerName("stringForType:"); - return (const char*) NSString_to_char(((id(*)(id, SEL, const char*))objc_msgSend)(pasteboard, func, NSString_stringWithUTF8String(dataType))); - } - - NSArray* c_array_to_NSArray(void* array, size_t len) { - 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(void* view, NSPasteboardType* newTypes, size_t len) { - NSString** ntypes = cstrToNSStringArray((char**)newTypes, len); - - NSArray* array = c_array_to_NSArray(ntypes, len); - objc_msgSend_void_id(view, sel_registerName("registerForDraggedTypes:"), array); - NSRelease(array); - } - - NSInteger NSPasteBoard_declareTypes(NSPasteboard* pasteboard, NSPasteboardType* newTypes, size_t len, void* owner) { - NSString** ntypes = cstrToNSStringArray((char**)newTypes, len); - - void* func = sel_registerName("declareTypes:owner:"); - - NSArray* array = c_array_to_NSArray(ntypes, len); - - NSInteger output = ((NSInteger(*)(id, SEL, id, void*))objc_msgSend) - (pasteboard, func, array, owner); - NSRelease(array); - - return output; - } - - bool NSPasteBoard_setString(NSPasteboard* pasteboard, const char* stringToWrite, NSPasteboardType dataType) { - void* func = sel_registerName("setString:forType:"); - return ((bool (*)(id, SEL, id, NSPasteboardType))objc_msgSend) - (pasteboard, func, NSString_stringWithUTF8String(stringToWrite), NSString_stringWithUTF8String(dataType)); - } - - void NSRetain(id obj) { objc_msgSend_void(obj, sel_registerName("retain")); } - - typedef enum NSApplicationActivationPolicy { - NSApplicationActivationPolicyRegular, - NSApplicationActivationPolicyAccessory, - NSApplicationActivationPolicyProhibited - } NSApplicationActivationPolicy; - - typedef NS_ENUM(u32, NSBackingStoreType) { - NSBackingStoreRetained = 0, - NSBackingStoreNonretained = 1, - NSBackingStoreBuffered = 2 - }; - - typedef NS_ENUM(u32, NSWindowStyleMask) { - NSWindowStyleMaskBorderless = 0, - NSWindowStyleMaskTitled = 1 << 0, - NSWindowStyleMaskClosable = 1 << 1, - NSWindowStyleMaskMiniaturizable = 1 << 2, - NSWindowStyleMaskResizable = 1 << 3, - NSWindowStyleMaskTexturedBackground = 1 << 8, /* deprecated */ - NSWindowStyleMaskUnifiedTitleAndToolbar = 1 << 12, - NSWindowStyleMaskFullScreen = 1 << 14, - NSWindowStyleMaskFullSizeContentView = 1 << 15, - NSWindowStyleMaskUtilityWindow = 1 << 4, - NSWindowStyleMaskDocModalWindow = 1 << 6, - NSWindowStyleMaskNonactivatingPanel = 1 << 7, - NSWindowStyleMaskHUDWindow = 1 << 13 - }; - - NSPasteboardType const NSPasteboardTypeString = "public.utf8-plain-text"; // Replaces NSStringPboardType - - - typedef NS_ENUM(i32, NSDragOperation) { - NSDragOperationNone = 0, - NSDragOperationCopy = 1, - NSDragOperationLink = 2, - NSDragOperationGeneric = 4, - NSDragOperationPrivate = 8, - NSDragOperationMove = 16, - NSDragOperationDelete = 32, - NSDragOperationEvery = ULONG_MAX, - - //NSDragOperationAll_Obsolete API_DEPRECATED("", macos(10.0,10.10)) = 15, // Use NSDragOperationEvery - //NSDragOperationAll API_DEPRECATED("", macos(10.0,10.10)) = NSDragOperationAll_Obsolete, // Use NSDragOperationEvery - }; - - void* NSArray_objectAtIndex(NSArray* array, NSUInteger index) { - void* func = sel_registerName("objectAtIndex:"); - return ((id(*)(id, SEL, NSUInteger))objc_msgSend)(array, func, index); - } - - const char** NSPasteboard_readObjectsForClasses(NSPasteboard* pasteboard, Class* classArray, size_t len, void* options) { - void* func = sel_registerName("readObjectsForClasses:options:"); - - NSArray* array = c_array_to_NSArray(classArray, len); - - NSArray* output = (NSArray*) ((id(*)(id, SEL, id, void*))objc_msgSend) - (pasteboard, func, array, options); - - NSRelease(array); - NSUInteger count = ((NSUInteger(*)(id, SEL))objc_msgSend)(output, sel_registerName("count")); - - const char** res = si_array_init_reserve(sizeof(const char*), count); - - void* path_func = sel_registerName("path"); - - for (NSUInteger i = 0; i < count; i++) { - void* url = NSArray_objectAtIndex(output, i); - NSString* url_str = ((id(*)(id, SEL))objc_msgSend)(url, path_func); - res[i] = NSString_to_char(url_str); - } - - return res; - } - - void* NSWindow_contentView(NSWindow* window) { - void* func = sel_registerName("contentView"); - return objc_msgSend_id(window, func); - } - - /* - End of cocoa wrapper - */ - - char* RGFW_mouseIconSrc[] = {"arrowCursor", "arrowCursor", "IBeamCursor", "crosshairCursor", "pointingHandCursor", "resizeLeftRightCursor", "resizeUpDownCursor", "_windowResizeNorthWestSouthEastCursor", "_windowResizeNorthEastSouthWestCursor", "closedHandCursor", "operationNotAllowedCursor"}; - - void* RGFWnsglFramework = NULL; - -#ifdef RGFW_OPENGL - void* RGFW_getProcAddress(const char* procname) { - if (RGFWnsglFramework == NULL) - RGFWnsglFramework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); - - CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, procname, kCFStringEncodingASCII); - - void* symbol = CFBundleGetFunctionPointerForName(RGFWnsglFramework, symbolName); - - CFRelease(symbolName); - - return symbol; - } -#endif - - CVReturn displayCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) { - RGFW_UNUSED(displayLink) RGFW_UNUSED(inNow) RGFW_UNUSED(inOutputTime) RGFW_UNUSED(flagsIn) RGFW_UNUSED(flagsOut) RGFW_UNUSED(displayLinkContext) - return kCVReturnSuccess; - } - - id NSWindow_delegate(RGFW_window* win) { - return (id) objc_msgSend_id(win->src.window, sel_registerName("delegate")); - } - - u32 RGFW_OnClose(void* self) { - RGFW_window* win = NULL; - object_getInstanceVariable(self, "RGFW_window", (void*)&win); - if (win == NULL) - return true; - - win->event.type = RGFW_quit; - RGFW_windowQuitCallback(win); - - return true; - } - - /* NOTE(EimaMei): Fixes the constant clicking when the app is running under a terminal. */ - bool acceptsFirstResponder(void) { return true; } - bool performKeyEquivalent(NSEvent* 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) - return 0; - - if (!(win->_winArgs & RGFW_ALLOW_DND)) { - return 0; - } - - win->event.type = RGFW_dnd_init; - win->src.dndPassed = 0; - - NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation")); - - win->event.point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y)); - 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->_winArgs & RGFW_ALLOW_DND)) { - return false; - } - - return true; - } - - 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; - - // NSPasteboard* 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) { - #ifdef RGFW_DEBUG - printf("No files found on the pasteboard.\n"); - #endif - - 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; - - for (int 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")); - strncpy(win->event.droppedFiles[i], filePath, RGFW_MAX_PATH); - win->event.droppedFiles[i][RGFW_MAX_PATH - 1] = '\0'; - } - win->event.droppedFilesCount = count; - - win->event.type = RGFW_dnd; - win->src.dndPassed = 0; - - NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation")); - win->event.point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y)); - - RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount); - - return false; - } - - static void NSMoveToResourceDir(void) { - /* sourced from glfw */ - char resourcesPath[255]; - - CFBundleRef bundle = CFBundleGetMainBundle(); - if (!bundle) - return; - - CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle); - CFStringRef last = CFURLCopyLastPathComponent(resourcesURL); - - if ( - CFStringCompare(CFSTR("Resources"), last, 0) != kCFCompareEqualTo || - CFURLGetFileSystemRepresentation(resourcesURL, true, (u8*) resourcesPath, 255) == 0 - ) { - CFRelease(last); - CFRelease(resourcesURL); - return; - } - - CFRelease(last); - CFRelease(resourcesURL); - - chdir(resourcesPath); - } - - - NSSize RGFW__osxWindowResize(void* self, SEL sel, NSSize frameSize) { - RGFW_UNUSED(sel); - - RGFW_window* win = NULL; - object_getInstanceVariable(self, "RGFW_window", (void*)&win); - if (win == NULL) - return frameSize; - - win->r.w = frameSize.width; - win->r.h = frameSize.height; - win->event.type = RGFW_windowResized; - RGFW_windowResizeCallback(win, win->r); - return frameSize; - } - - void RGFW__osxWindowMove(void* 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)(win->src.window, sel_registerName("frame")); - win->r.x = (i32) frame.origin.x; - win->r.y = (i32) frame.origin.y; - - win->event.type = RGFW_windowMoved; - RGFW_windowMoveCallback(win, win->r); - } - - void RGFW__osxUpdateLayer(void* self, SEL sel) { - RGFW_UNUSED(sel); - - RGFW_window* win = NULL; - object_getInstanceVariable(self, "RGFW_window", (void*)&win); - if (win == NULL) - return; - - win->event.type = RGFW_windowRefresh; - RGFW_windowRefreshCallback(win); - } - - RGFWDEF void RGFW_init_buffer(RGFW_window* win); - void RGFW_init_buffer(RGFW_window* win) { - #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0) - RGFW_bufferSize = RGFW_getScreenSize(); - - win->buffer = RGFW_MALLOC(RGFW_bufferSize.w * RGFW_bufferSize.h * 4); - - #ifdef RGFW_OSMESA - win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL); - OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h); - #endif - #else - RGFW_UNUSED(win); /*!< if buffer rendering is not being used */ - #endif - } - - - void RGFW_window_cocoaSetLayer(RGFW_window* win, void* layer) { - objc_msgSend_void_id(win->src.view, sel_registerName("setLayer"), layer); - } - - void* RGFW_cocoaGetLayer(void) { - return objc_msgSend_class(objc_getClass("CAMetalLayer"), sel_registerName("layer")); - } - - - NSPasteboardType const NSPasteboardTypeURL = "public.url"; - NSPasteboardType const NSPasteboardTypeFileURL = "public.file-url"; - - RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) { - static u8 RGFW_loaded = 0; - - /* NOTE(EimaMei): Why does Apple hate good code? Like wtf, who thought of methods being a great idea??? - Imagine a universe, where MacOS had a proper system API (we would probably have like 20% better performance). - */ - si_func_to_SEL_with_name("NSObject", "windowShouldClose", RGFW_OnClose); - - /* NOTE(EimaMei): Fixes the 'Boop' sfx from constantly playing each time you click a key. Only a problem when running in the terminal. */ - si_func_to_SEL("NSWindow", acceptsFirstResponder); - si_func_to_SEL("NSWindow", performKeyEquivalent); - - // RR Create an autorelease pool - id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); - pool = objc_msgSend_id(pool, sel_registerName("init")); - - 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); - } - - RGFW_window* win = RGFW_window_basic_init(rect, args); - - 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 (!(args & RGFW_NO_RESIZE)) - macArgs |= NSWindowStyleMaskResizable; - if (!(args & RGFW_NO_BORDER)) - macArgs |= NSWindowStyleMaskTitled; - else - macArgs = NSWindowStyleMaskBorderless; - { - void* nsclass = objc_getClass("NSWindow"); - void* func = sel_registerName("initWithContentRect:styleMask:backing:defer:"); - - win->src.window = ((id(*)(id, SEL, NSRect, NSWindowStyleMask, NSBackingStoreType, bool))objc_msgSend) - (NSAlloc(nsclass), func, windowRect, macArgs, macArgs, false); - } - - NSString* str = NSString_stringWithUTF8String(name); - objc_msgSend_void_id(win->src.window, sel_registerName("setTitle:"), str); - -#ifdef RGFW_EGL - if ((args & RGFW_NO_INIT_API) == 0) - RGFW_createOpenGLContext(win); -#endif - -#ifdef RGFW_OPENGL - if ((args & RGFW_NO_INIT_API) == 0) { - void* attrs = RGFW_initFormatAttribs(args & RGFW_OPENGL_SOFTWARE); - void* format = NSOpenGLPixelFormat_initWithAttributes(attrs); - - if (format == NULL) { - printf("Failed to load pixel format for OpenGL\n"); - - void* attrs = RGFW_initFormatAttribs(1); - format = NSOpenGLPixelFormat_initWithAttributes(attrs); - if (format == NULL) - printf("and loading software rendering OpenGL failed\n"); - else - printf("Switching to software rendering\n"); - } - - /* 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 = NSOpenGLView_initWithFrame((NSRect){{0, 0}, {win->r.w, win->r.h}}, format); - objc_msgSend_void(win->src.view, sel_registerName("prepareOpenGL")); - win->src.ctx = objc_msgSend_id(win->src.view, sel_registerName("openGLContext")); - } else -#endif - { - NSRect contentRect = (NSRect){{0, 0}, {win->r.w, win->r.h}}; - win->src.view = ((id(*)(id, SEL, NSRect))objc_msgSend) - (NSAlloc((id)objc_getClass("NSView")), sel_registerName("initWithFrame:"), - contentRect); - } - - void* contentView = NSWindow_contentView(win->src.window); - objc_msgSend_void_bool(contentView, sel_registerName("setWantsLayer:"), true); - - objc_msgSend_void_id(win->src.window, sel_registerName("setContentView:"), win->src.view); - -#ifdef RGFW_OPENGL - if ((args & RGFW_NO_INIT_API) == 0) - objc_msgSend_void(win->src.ctx, sel_registerName("makeCurrentContext")); -#endif - if (args & RGFW_TRANSPARENT_WINDOW) { -#ifdef RGFW_OPENGL - if ((args & RGFW_NO_INIT_API) == 0) { - i32 opacity = 0; - #define NSOpenGLCPSurfaceOpacity 236 - NSOpenGLContext_setValues(win->src.ctx, &opacity, NSOpenGLCPSurfaceOpacity); - } -#endif - - objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), false); - - objc_msgSend_void_id(win->src.window, sel_registerName("setBackgroundColor:"), - NSColor_colorWithSRGB(0, 0, 0, 0)); - } - - win->src.display = CGMainDisplayID(); - CVDisplayLinkCreateWithCGDisplay(win->src.display, (CVDisplayLinkRef*)&win->src.displayLink); - CVDisplayLinkSetOutputCallback(win->src.displayLink, displayCallback, win); - CVDisplayLinkStart(win->src.displayLink); - - RGFW_init_buffer(win); - - #ifndef RGFW_NO_MONITOR - if (args & RGFW_SCALE_TO_MONITOR) - RGFW_window_scaleToMonitor(win); - #endif - - if (args & RGFW_CENTER) { - RGFW_area screenR = RGFW_getScreenSize(); - RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2)); - } - - if (args & RGFW_HIDE_MOUSE) - RGFW_window_showMouse(win, 0); - - if (args & RGFW_COCOA_MOVE_TO_RESOURCE_DIR) - NSMoveToResourceDir(); - - Class delegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "WindowDelegate", 0); - - class_addIvar( - delegateClass, "RGFW_window", - sizeof(RGFW_window*), 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("updateLayer:"), (IMP) RGFW__osxUpdateLayer, ""); - class_addMethod(delegateClass, sel_registerName("windowWillMove:"), (IMP) RGFW__osxWindowMove, ""); - class_addMethod(delegateClass, sel_registerName("windowDidMove:"), (IMP) RGFW__osxWindowMove, ""); - 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")); - - object_setInstanceVariable(delegate, "RGFW_window", win); - - objc_msgSend_void_id(win->src.window, sel_registerName("setDelegate:"), delegate); - - if (args & RGFW_ALLOW_DND) { - win->_winArgs |= RGFW_ALLOW_DND; - - NSPasteboardType types[] = {NSPasteboardTypeURL, NSPasteboardTypeFileURL, NSPasteboardTypeString}; - NSregisterForDraggedTypes(win->src.window, types, 3); - } - - // Show the window - objc_msgSend_void_bool(NSApp, sel_registerName("activateIgnoringOtherApps:"), true); - ((id(*)(id, SEL, SEL))objc_msgSend)(win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL); - objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true); - - if (!RGFW_loaded) { - objc_msgSend_void(win->src.window, sel_registerName("makeMainWindow")); - - RGFW_loaded = 1; - } - - objc_msgSend_void(win->src.window, sel_registerName("makeKeyWindow")); - - objc_msgSend_void(NSApp, sel_registerName("finishLaunching")); - - if (RGFW_root == NULL) - RGFW_root = win; - - NSRetain(win->src.window); - NSRetain(NSApp); - - return win; - } - - void RGFW_window_setBorder(RGFW_window* win, u8 border) { - NSBackingStoreType storeType = NSWindowStyleMaskBorderless; - if (!border) { - storeType = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable; - } - if (!(win->_winArgs & RGFW_NO_RESIZE)) { - storeType |= NSWindowStyleMaskResizable; - } - - ((void (*)(id, SEL, NSBackingStoreType))objc_msgSend)(win->src.window, sel_registerName("setStyleMask:"), storeType); - - objc_msgSend_void_bool(win->src.window, sel_registerName("setHasShadow:"), border); - } - - 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) { - 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 */ - } - - RGFW_point RGFW_window_getMousePoint(RGFW_window* win) { - NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(win->src.window, sel_registerName("mouseLocationOutsideOfEventStream")); - - return RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y)); - } - - u32 RGFW_keysPressed[10]; /*10 keys at a time*/ - typedef NS_ENUM(u32, NSEventType) { /* various types of events */ - NSEventTypeLeftMouseDown = 1, - NSEventTypeLeftMouseUp = 2, - NSEventTypeRightMouseDown = 3, - NSEventTypeRightMouseUp = 4, - NSEventTypeMouseMoved = 5, - NSEventTypeLeftMouseDragged = 6, - NSEventTypeRightMouseDragged = 7, - NSEventTypeMouseEntered = 8, - NSEventTypeMouseExited = 9, - NSEventTypeKeyDown = 10, - NSEventTypeKeyUp = 11, - NSEventTypeFlagsChanged = 12, - NSEventTypeAppKitDefined = 13, - NSEventTypeSystemDefined = 14, - NSEventTypeApplicationDefined = 15, - NSEventTypePeriodic = 16, - NSEventTypeCursorUpdate = 17, - NSEventTypeScrollWheel = 22, - NSEventTypeTabletPoint = 23, - NSEventTypeTabletProximity = 24, - NSEventTypeOtherMouseDown = 25, - NSEventTypeOtherMouseUp = 26, - NSEventTypeOtherMouseDragged = 27, - /* The following event types are available on some hardware on 10.5.2 and later */ - NSEventTypeGesture API_AVAILABLE(macos(10.5)) = 29, - NSEventTypeMagnify API_AVAILABLE(macos(10.5)) = 30, - NSEventTypeSwipe API_AVAILABLE(macos(10.5)) = 31, - NSEventTypeRotate API_AVAILABLE(macos(10.5)) = 18, - NSEventTypeBeginGesture API_AVAILABLE(macos(10.5)) = 19, - NSEventTypeEndGesture API_AVAILABLE(macos(10.5)) = 20, - - NSEventTypeSmartMagnify API_AVAILABLE(macos(10.8)) = 32, - NSEventTypeQuickLook API_AVAILABLE(macos(10.8)) = 33, - - NSEventTypePressure API_AVAILABLE(macos(10.10.3)) = 34, - NSEventTypeDirectTouch API_AVAILABLE(macos(10.10)) = 37, - - NSEventTypeChangeMode API_AVAILABLE(macos(10.15)) = 38, - }; - - typedef NS_ENUM(unsigned long long, NSEventMask) { /* masks for the types of events */ - NSEventMaskLeftMouseDown = 1ULL << NSEventTypeLeftMouseDown, - NSEventMaskLeftMouseUp = 1ULL << NSEventTypeLeftMouseUp, - NSEventMaskRightMouseDown = 1ULL << NSEventTypeRightMouseDown, - NSEventMaskRightMouseUp = 1ULL << NSEventTypeRightMouseUp, - NSEventMaskMouseMoved = 1ULL << NSEventTypeMouseMoved, - NSEventMaskLeftMouseDragged = 1ULL << NSEventTypeLeftMouseDragged, - NSEventMaskRightMouseDragged = 1ULL << NSEventTypeRightMouseDragged, - NSEventMaskMouseEntered = 1ULL << NSEventTypeMouseEntered, - NSEventMaskMouseExited = 1ULL << NSEventTypeMouseExited, - NSEventMaskKeyDown = 1ULL << NSEventTypeKeyDown, - NSEventMaskKeyUp = 1ULL << NSEventTypeKeyUp, - NSEventMaskFlagsChanged = 1ULL << NSEventTypeFlagsChanged, - NSEventMaskAppKitDefined = 1ULL << NSEventTypeAppKitDefined, - NSEventMaskSystemDefined = 1ULL << NSEventTypeSystemDefined, - NSEventMaskApplicationDefined = 1ULL << NSEventTypeApplicationDefined, - NSEventMaskPeriodic = 1ULL << NSEventTypePeriodic, - NSEventMaskCursorUpdate = 1ULL << NSEventTypeCursorUpdate, - NSEventMaskScrollWheel = 1ULL << NSEventTypeScrollWheel, - NSEventMaskTabletPoint = 1ULL << NSEventTypeTabletPoint, - NSEventMaskTabletProximity = 1ULL << NSEventTypeTabletProximity, - NSEventMaskOtherMouseDown = 1ULL << NSEventTypeOtherMouseDown, - NSEventMaskOtherMouseUp = 1ULL << NSEventTypeOtherMouseUp, - NSEventMaskOtherMouseDragged = 1ULL << NSEventTypeOtherMouseDragged, - /* The following event masks are available on some hardware on 10.5.2 and later */ - NSEventMaskGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeGesture, - NSEventMaskMagnify API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeMagnify, - NSEventMaskSwipe API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeSwipe, - NSEventMaskRotate API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeRotate, - NSEventMaskBeginGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeBeginGesture, - NSEventMaskEndGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeEndGesture, - - /* Note: You can only use these event masks on 64 bit. In other words, you cannot setup a local, nor global, event monitor for these event types on 32 bit. Also, you cannot search the event queue for them (nextEventMatchingMask:...) on 32 bit. - */ - NSEventMaskSmartMagnify API_AVAILABLE(macos(10.8)) = 1ULL << NSEventTypeSmartMagnify, - NSEventMaskPressure API_AVAILABLE(macos(10.10.3)) = 1ULL << NSEventTypePressure, - NSEventMaskDirectTouch API_AVAILABLE(macos(10.12.2)) = 1ULL << NSEventTypeDirectTouch, - - NSEventMaskChangeMode API_AVAILABLE(macos(10.15)) = 1ULL << NSEventTypeChangeMode, - - NSEventMaskAny = ULONG_MAX, - - }; - - 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")); - - NSEvent* e = (NSEvent*) ((id(*)(id, SEL, NSEventType, NSPoint, NSEventModifierFlags, void*, NSInteger, void**, short, NSInteger, NSInteger))objc_msgSend) - (NSApp, sel_registerName("otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:"), - NSEventTypeApplicationDefined, (NSPoint){0, 0}, 0, 0, 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); - - NSEvent* e = (NSEvent*) ((id(*)(id, SEL, NSEventMask, void*, NSString*, bool))objc_msgSend) - (NSApp, sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"), - ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true); - - - if (e) { - objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e); - } - - objc_msgSend_bool_void(eventPool, sel_registerName("drain")); - } - - RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) { - assert(win != NULL); - - if (win->event.type == RGFW_quit) - return NULL; - - if ((win->event.type == RGFW_dnd || win->event.type == RGFW_dnd_init) && win->src.dndPassed == 0) { - win->src.dndPassed = 1; - return &win->event; - } - - id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); - eventPool = objc_msgSend_id(eventPool, sel_registerName("init")); - - static void* eventFunc = NULL; - if (eventFunc == NULL) - eventFunc = sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"); - - if ((win->event.type == RGFW_windowMoved || win->event.type == RGFW_windowResized || win->event.type == RGFW_windowRefresh) && win->event.keyCode != 120) { - win->event.keyCode = 120; - objc_msgSend_bool_void(eventPool, sel_registerName("drain")); - return &win->event; - } - - void* date = NULL; - - NSEvent* e = (NSEvent*) ((id(*)(id, SEL, NSEventMask, void*, NSString*, bool))objc_msgSend) - (NSApp, eventFunc, ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true); - - if (e == NULL) { - objc_msgSend_bool_void(eventPool, sel_registerName("drain")); - 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_bool_void(eventPool, sel_registerName("drain")); - 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; - - switch (objc_msgSend_uint(e, sel_registerName("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")); - win->event.keyCode = RGFW_apiKeyCodeToRGFW(key); - RGFW_keyboard[win->event.keyCode].prev = RGFW_keyboard[win->event.keyCode].current; - - win->event.type = RGFW_keyPressed; - char* str = (char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("characters"))); - strncpy(win->event.keyName, str, 16); - win->event.repeat = RGFW_isPressed(win, win->event.keyCode); - RGFW_keyboard[win->event.keyCode].current = 1; - - RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 1); - break; - } - - case NSEventTypeKeyUp: { - u32 key = (u16) objc_msgSend_uint(e, sel_registerName("keyCode")); - win->event.keyCode = RGFW_apiKeyCodeToRGFW(key);; - - RGFW_keyboard[win->event.keyCode].prev = RGFW_keyboard[win->event.keyCode].current; - - win->event.type = RGFW_keyReleased; - char* str = (char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("characters"))); - strncpy(win->event.keyName, str, 16); - - RGFW_keyboard[win->event.keyCode].current = 0; - RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 0); - break; - } - - case NSEventTypeFlagsChanged: { - u32 flags = objc_msgSend_uint(e, sel_registerName("modifierFlags")); - RGFW_updateLockState(win, ((u32)(flags & NSEventModifierFlagCapsLock) % 255), ((flags & NSEventModifierFlagNumericPad) % 255)); - - 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, key)) { - RGFW_keyboard[key].current = 1; - - if (key != RGFW_CapsLock) - RGFW_keyboard[key+ 4].current = 1; - - win->event.type = RGFW_keyPressed; - win->event.keyCode = key; - break; - } - - if (!(flags & shift) && RGFW_wasPressed(win, key)) { - RGFW_keyboard[key].current = 0; - - if (key != RGFW_CapsLock) - RGFW_keyboard[key + 4].current = 0; - - win->event.type = RGFW_keyReleased; - win->event.keyCode = key; - break; - } - } - - RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 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)); - - if ((win->_winArgs & RGFW_HOLD_MOUSE)) { - 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.point = RGFW_POINT((i32)p.x, (i32)p.y); - } - - RGFW_mousePosCallback(win, win->event.point); - break; - - case NSEventTypeLeftMouseDown: - win->event.button = RGFW_mouseLeft; - 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 NSEventTypeOtherMouseDown: - win->event.button = 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 NSEventTypeRightMouseDown: - win->event.button = RGFW_mouseRight; - 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: - win->event.button = RGFW_mouseLeft; - 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 NSEventTypeOtherMouseUp: - win->event.button = RGFW_mouseMiddle; - 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 NSEventTypeRightMouseUp: - win->event.button = RGFW_mouseRight; - 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: - break; - } - - 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) { - assert(win != NULL); - - win->r.x = v.x; - win->r.y = v.y; - ((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend) - (win->src.window, sel_registerName("setFrame:display:animate:"), (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) { - assert(win != NULL); - - win->r.w = a.w; - win->r.h = a.h; - ((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend) - (win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{win->r.x, win->r.y}, {win->r.w, win->r.h}}, true, true); - } - - void RGFW_window_minimize(RGFW_window* win) { - assert(win != NULL); - - objc_msgSend_void_SEL(win->src.window, sel_registerName("performMiniaturize:"), NULL); - } - - void RGFW_window_restore(RGFW_window* win) { - assert(win != NULL); - - objc_msgSend_void_SEL(win->src.window, sel_registerName("deminiaturize:"), NULL); - } - - void RGFW_window_setName(RGFW_window* win, char* name) { - assert(win != NULL); - - NSString* str = NSString_stringWithUTF8String(name); - objc_msgSend_void_id(win->src.window, sel_registerName("setTitle:"), str); - } - - #ifndef RGFW_NO_PASSTHROUGH - void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) { - objc_msgSend_void_bool(win->src.window, sel_registerName("setIgnoresMouseEvents:"), passthrough); - } - #endif - - void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { - if (a.w == 0 && a.h == 0) - return; - - ((void (*)(id, SEL, NSSize))objc_msgSend) - (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) - return; - - ((void (*)(id, SEL, NSSize))objc_msgSend) - (win->src.window, sel_registerName("setMaxSize:"), (NSSize){a.w, a.h}); - } - - void RGFW_window_setIcon(RGFW_window* win, u8* data, RGFW_area area, i32 channels) { - assert(win != NULL); - - /* code by EimaMei */ - // Make a bitmap representation, then copy the loaded image into it. - void* representation = NSBitmapImageRep_initWithBitmapData(NULL, area.w, area.h, 8, channels, (channels == 4), false, "NSCalibratedRGBColorSpace", 1 << 1, area.w * channels, 8 * channels); - memcpy(NSBitmapImageRep_bitmapData(representation), data, area.w * area.h * channels); - - // Add ze representation. - void* dock_image = NSImage_initWithSize((NSSize){area.w, area.h}); - NSImage_addRepresentation(dock_image, (void*) representation); - - // Finally, set the dock image to it. - objc_msgSend_void_id(NSApp, sel_registerName("setApplicationIconImage:"), dock_image); - // Free the garbage. - release(dock_image); - release(representation); - } - - NSCursor* NSCursor_arrowStr(char* str) { - void* nclass = objc_getClass("NSCursor"); - void* func = sel_registerName(str); - return (NSCursor*) objc_msgSend_id(nclass, func); - } - - void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) { - assert(win != NULL); - - if (image == NULL) { - objc_msgSend_void(NSCursor_arrowStr("arrowCursor"), sel_registerName("set")); - return; - } - - /* NOTE(EimaMei): Code by yours truly. */ - // Make a bitmap representation, then copy the loaded image into it. - void* representation = NSBitmapImageRep_initWithBitmapData(NULL, a.w, a.h, 8, channels, (channels == 4), false, "NSCalibratedRGBColorSpace", 1 << 1, a.w * channels, 8 * channels); - memcpy(NSBitmapImageRep_bitmapData(representation), image, a.w * a.h * channels); - - // Add ze representation. - void* cursor_image = NSImage_initWithSize((NSSize){a.w, a.h}); - NSImage_addRepresentation(cursor_image, representation); - - // Finally, set the cursor image. - void* cursor = NSCursor_initWithImage(cursor_image, (NSPoint){0.0, 0.0}); - - objc_msgSend_void(cursor, sel_registerName("set")); - - // Free the garbage. - release(cursor_image); - release(representation); - } - - void RGFW_window_setMouseDefault(RGFW_window* win) { - RGFW_window_setMouseStandard(win, RGFW_MOUSE_ARROW); - } - - void RGFW_window_showMouse(RGFW_window* win, i8 show) { - RGFW_UNUSED(win); - - if (show) { - CGDisplayShowCursor(kCGDirectMainDisplay); - } - else { - CGDisplayHideCursor(kCGDirectMainDisplay); - } - } - - void RGFW_window_setMouseStandard(RGFW_window* win, u8 stdMouses) { - if (stdMouses > ((sizeof(RGFW_mouseIconSrc)) / (sizeof(char*)))) - return; - - char* mouseStr = RGFW_mouseIconSrc[stdMouses]; - void* mouse = NSCursor_arrowStr(mouseStr); - - if (mouse == NULL) - return; - - RGFW_UNUSED(win); - CGDisplayShowCursor(kCGDirectMainDisplay); - objc_msgSend_void(mouse, sel_registerName("set")); - } - - void RGFW_releaseCursor(RGFW_window* win) { - RGFW_UNUSED(win); - CGAssociateMouseAndMouseCursorPosition(1); - } - - void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { - RGFW_UNUSED(win) - - CGWarpMouseCursorPosition(CGPointMake(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); - - CGWarpMouseCursorPosition(CGPointMake(v.x, v.y)); - } - - - void RGFW_window_hide(RGFW_window* win) { - objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), false); - } - - void RGFW_window_show(RGFW_window* win) { - ((id(*)(id, SEL, SEL))objc_msgSend)(win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL); - objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true); - } - - u8 RGFW_window_isFullscreen(RGFW_window* win) { - assert(win != NULL); - - NSWindowStyleMask mask = (NSWindowStyleMask) objc_msgSend_uint(win->src.window, sel_registerName("styleMask")); - return (mask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen; - } - - u8 RGFW_window_isHidden(RGFW_window* win) { - assert(win != NULL); - - bool visible = objc_msgSend_bool(win->src.window, sel_registerName("isVisible")); - return visible == NO && !RGFW_window_isMinimized(win); - } - - u8 RGFW_window_isMinimized(RGFW_window* win) { - assert(win != NULL); - - return objc_msgSend_bool(win->src.window, sel_registerName("isMiniaturized")) == YES; - } - - u8 RGFW_window_isMaximized(RGFW_window* win) { - assert(win != NULL); - - return objc_msgSend_bool(win->src.window, sel_registerName("isZoomed")); - } - - static RGFW_monitor RGFW_NSCreateMonitor(CGDirectDisplayID display) { - RGFW_monitor monitor; - - CGRect bounds = CGDisplayBounds(display); - monitor.rect = RGFW_RECT((int) bounds.origin.x, (int) bounds.origin.y, (int) bounds.size.width, (int) bounds.size.height); - - CGSize screenSizeMM = CGDisplayScreenSize(display); - monitor.physW = screenSizeMM.width; - monitor.physH = screenSizeMM.height; - - monitor.scaleX = ((monitor.rect.w / (screenSizeMM.width / 25.4)) / 96) + 0.25; - monitor.scaleY = ((monitor.rect.h / (screenSizeMM.height / 25.4)) / 96) + 0.25; - - return monitor; - } - - - static RGFW_monitor RGFW_monitors[7]; - - RGFW_monitor* RGFW_getMonitors(void) { - static CGDirectDisplayID displays[7]; - u32 count; - - if (CGGetActiveDisplayList(6, displays, &count) != kCGErrorSuccess) - return NULL; - - for (u32 i = 0; i < count; i++) - RGFW_monitors[i] = RGFW_NSCreateMonitor(displays[i]); - - return RGFW_monitors; - } - - RGFW_monitor RGFW_getPrimaryMonitor(void) { - CGDirectDisplayID primary = CGMainDisplayID(); - return RGFW_NSCreateMonitor(primary); - } - - RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { - return RGFW_NSCreateMonitor(win->src.display); - } - - char* RGFW_readClipboard(size_t* size) { - char* clip = (char*)NSPasteboard_stringForType(NSPasteboard_generalPasteboard(), NSPasteboardTypeString); - - size_t clip_len = 1; - - if (clip != NULL) { - clip_len = strlen(clip) + 1; - } - - char* str = (char*)RGFW_MALLOC(sizeof(char) * clip_len); - - if (clip != NULL) { - strncpy(str, clip, clip_len); - } - - str[clip_len] = '\0'; - - if (size != NULL) - *size = clip_len; - return str; - } - - void RGFW_writeClipboard(const char* text, u32 textLen) { - RGFW_UNUSED(textLen); - - NSPasteboardType array[] = { NSPasteboardTypeString, NULL }; - NSPasteBoard_declareTypes(NSPasteboard_generalPasteboard(), array, 1, NULL); - - NSPasteBoard_setString(NSPasteboard_generalPasteboard(), text, NSPasteboardTypeString); - } - - u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber) { - RGFW_UNUSED(jsNumber); - - assert(win != NULL); - - return RGFW_registerJoystickF(win, (char*) ""); - } - - u16 RGFW_registerJoystickF(RGFW_window* win, char* file) { - RGFW_UNUSED(file); - - assert(win != NULL); - - return RGFW_joystickCount - 1; - } - - #ifdef RGFW_OPENGL - void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { - assert(win != NULL); - objc_msgSend_void(win->src.ctx, sel_registerName("makeCurrentContext")); - } - #endif - - #if !defined(RGFW_EGL) - void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { - assert(win != NULL); - #if defined(RGFW_OPENGL) - - NSOpenGLContext_setValues(win->src.ctx, &swapInterval, 222); - #else - RGFW_UNUSED(swapInterval); - #endif - } - #endif - - // Function to create a CGImageRef from an array of bytes - CGImageRef createImageFromBytes(unsigned char *buffer, int width, int height) - { - // Define color space - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - // Create bitmap context - CGContextRef context = CGBitmapContextCreate( - buffer, - width, height, - 8, - RGFW_bufferSize.w * 4, - colorSpace, - kCGImageAlphaPremultipliedLast); - // Create image from bitmap context - CGImageRef image = CGBitmapContextCreateImage(context); - // Release the color space and context - CGColorSpaceRelease(colorSpace); - CGContextRelease(context); - - return image; - } - - void RGFW_window_swapBuffers(RGFW_window* win) { - assert(win != NULL); - /* clear the window*/ - - if (!(win->_winArgs & RGFW_NO_CPU_RENDER)) { -#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - #ifdef RGFW_OSMESA - RGFW_OSMesa_reorganize(); - #endif - - void* view = NSWindow_contentView(win->src.window); - void* layer = objc_msgSend_id(view, sel_registerName("layer")); - - ((void(*)(id, SEL, NSRect))objc_msgSend)(layer, - sel_registerName("setFrame:"), - (NSRect){{0, 0}, {win->r.w, win->r.h}}); - - CGImageRef image = createImageFromBytes(win->buffer, win->r.w, win->r.h); - // Get the current graphics context - id graphicsContext = objc_msgSend_class(objc_getClass("NSGraphicsContext"), sel_registerName("currentContext")); - // Get the CGContext from the current NSGraphicsContext - id cgContext = objc_msgSend_id(graphicsContext, sel_registerName("graphicsPort")); - // Draw the image in the context - NSRect bounds = (NSRect){{0,0}, {win->r.w, win->r.h}}; - CGContextDrawImage((void*)cgContext, *(CGRect*)&bounds, image); - // Flush the graphics context to ensure the drawing is displayed - objc_msgSend_id(graphicsContext, sel_registerName("flushGraphics")); - - objc_msgSend_void_id(layer, sel_registerName("setContents:"), (id)image); - objc_msgSend_id(layer, sel_registerName("setNeedsDisplay")); - - CGImageRelease(image); -#endif - } - - if (!(win->_winArgs & RGFW_NO_GPU_RENDER)) { - #ifdef RGFW_EGL - eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); - #elif defined(RGFW_OPENGL) - objc_msgSend_void(win->src.ctx, sel_registerName("flushBuffer")); - #endif - } - } - - void RGFW_window_close(RGFW_window* win) { - assert(win != NULL); - release(win->src.view); - -#ifdef RGFW_ALLOC_DROPFILES - { - u32 i; - for (i = 0; i < RGFW_MAX_DROPS; i++) - RGFW_FREE(win->event.droppedFiles[i]); - - - RGFW_FREE(win->event.droppedFiles); - } -#endif - -#ifdef RGFW_BUFFER - release(win->src.bitmap); - release(win->src.image); -#endif - - CVDisplayLinkStop(win->src.displayLink); - CVDisplayLinkRelease(win->src.displayLink); - - RGFW_FREE(win); - } - - u64 RGFW_getTimeNS(void) { - static mach_timebase_info_data_t timebase_info; - if (timebase_info.denom == 0) { - mach_timebase_info(&timebase_info); - } - return mach_absolute_time() * timebase_info.numer / timebase_info.denom; - } - - u64 RGFW_getTime(void) { - static mach_timebase_info_data_t timebase_info; - if (timebase_info.denom == 0) { - mach_timebase_info(&timebase_info); - } - return (double) mach_absolute_time() * (double) timebase_info.numer / ((double) timebase_info.denom * 1e9); - } -#endif /* RGFW_MACOS */ - -/* - End of MaOS defines -*/ - -/* - WEBASM defines -*/ - -#ifdef RGFW_WEBASM -RGFW_Event RGFW_events[20]; -size_t RGFW_eventLen = 0; - -EM_BOOL Emscripten_on_keydown(int eventType, const EmscriptenKeyboardEvent* e, void* userData) { - RGFW_UNUSED(eventType); RGFW_UNUSED(userData); - - RGFW_events[RGFW_eventLen].type = RGFW_keyPressed; - memcpy(RGFW_events[RGFW_eventLen].keyName, e->key, 16); - RGFW_events[RGFW_eventLen].keyCode = RGFW_apiKeyCodeToRGFW(e->keyCode); - RGFW_events[RGFW_eventLen].lockState = 0; - RGFW_eventLen++; - - RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].prev = RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].current; - RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].current = 1; - RGFW_keyCallback(RGFW_root, RGFW_apiKeyCodeToRGFW(e->keyCode), RGFW_events[RGFW_eventLen].keyName, 0, 1); - - return EM_TRUE; -} - -EM_BOOL Emscripten_on_keyup(int eventType, const EmscriptenKeyboardEvent* e, void* userData) { - RGFW_UNUSED(eventType); RGFW_UNUSED(userData); - - RGFW_events[RGFW_eventLen].type = RGFW_keyReleased; - memcpy(RGFW_events[RGFW_eventLen].keyName, e->key, 16); - RGFW_events[RGFW_eventLen].keyCode = RGFW_apiKeyCodeToRGFW(e->keyCode); - RGFW_events[RGFW_eventLen].lockState = 0; - RGFW_eventLen++; - - RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].prev = RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].current; - RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].current = 0; - - RGFW_keyCallback(RGFW_root, RGFW_apiKeyCodeToRGFW(e->keyCode), RGFW_events[RGFW_eventLen].keyName, 0, 0); - - return EM_TRUE; -} - -EM_BOOL Emscripten_on_resize(int eventType, const EmscriptenUiEvent* e, void* userData) { - RGFW_UNUSED(eventType); RGFW_UNUSED(userData); - - RGFW_events[RGFW_eventLen].type = RGFW_windowResized; - RGFW_eventLen++; - - RGFW_windowResizeCallback(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); - - RGFW_events[RGFW_eventLen].type = RGFW_windowResized; - RGFW_eventLen++; - - RGFW_root->r = RGFW_RECT(0, 0, e->elementWidth, e->elementHeight); - RGFW_windowResizeCallback(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_events[RGFW_eventLen].type = RGFW_focusIn; - RGFW_eventLen++; - - RGFW_root->event.inFocus = 1; - 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_events[RGFW_eventLen].type = RGFW_focusOut; - RGFW_eventLen++; - - RGFW_root->event.inFocus = 0; - 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_events[RGFW_eventLen].type = RGFW_mousePosChanged; - - if ((RGFW_root->_winArgs & RGFW_HOLD_MOUSE)) { - RGFW_point p = RGFW_POINT(e->movementX, e->movementY); - RGFW_events[RGFW_eventLen].point = p; - } - else - RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->targetX, e->targetY); - RGFW_eventLen++; - - RGFW_mousePosCallback(RGFW_root, RGFW_events[RGFW_eventLen].point); - return EM_TRUE; -} - -EM_BOOL Emscripten_on_mousedown(int eventType, const EmscriptenMouseEvent* e, void* userData) { - RGFW_UNUSED(eventType); RGFW_UNUSED(userData); - - RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonPressed; - RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->targetX, e->targetY); - RGFW_events[RGFW_eventLen].button = e->button + 1; - RGFW_events[RGFW_eventLen].scroll = 0; - - RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current; - RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 1; - - RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 1); - RGFW_eventLen++; - - return EM_TRUE; -} - -EM_BOOL Emscripten_on_mouseup(int eventType, const EmscriptenMouseEvent* e, void* userData) { - RGFW_UNUSED(eventType); RGFW_UNUSED(userData); - - RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonReleased; - RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->targetX, e->targetY); - RGFW_events[RGFW_eventLen].button = e->button + 1; - RGFW_events[RGFW_eventLen].scroll = 0; - - RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current; - RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 0; - - RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 0); - RGFW_eventLen++; - return EM_TRUE; -} - -EM_BOOL Emscripten_on_wheel(int eventType, const EmscriptenWheelEvent* e, void* userData) { - RGFW_UNUSED(eventType); RGFW_UNUSED(userData); - - RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonPressed; - RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->mouse.targetX, e->mouse.targetY); - RGFW_events[RGFW_eventLen].button = RGFW_mouseScrollUp + (e->deltaY < 0); - RGFW_events[RGFW_eventLen].scroll = e->deltaY; - - RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current; - RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 1; - - RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 1); - RGFW_eventLen++; - - 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_events[RGFW_eventLen].type = RGFW_mouseButtonPressed; - RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY); - RGFW_events[RGFW_eventLen].button = 1; - RGFW_events[RGFW_eventLen].scroll = 0; - - - RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current; - RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 1; - - RGFW_mousePosCallback(RGFW_root, RGFW_events[RGFW_eventLen].point); - - RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 1); - RGFW_eventLen++; - } - - 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_events[RGFW_eventLen].type = RGFW_mousePosChanged; - RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY); - - RGFW_mousePosCallback(RGFW_root, RGFW_events[RGFW_eventLen].point); - RGFW_eventLen++; - } - 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_events[RGFW_eventLen].type = RGFW_mouseButtonReleased; - RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY); - RGFW_events[RGFW_eventLen].button = 1; - RGFW_events[RGFW_eventLen].scroll = 0; - - RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current; - RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 0; - - RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 0); - RGFW_eventLen++; - } - return EM_TRUE; -} - -EM_BOOL Emscripten_on_touchcancel(int eventType, const EmscriptenTouchEvent* e, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(e); 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; - - RGFW_joysticks[gamepadEvent->index] = gamepadEvent->connected; - - return 1; // The event was consumed by the callback handler -} - -void EMSCRIPTEN_KEEPALIVE Emscripten_onDrop(size_t count) { - if (!(RGFW_root->_winArgs & RGFW_ALLOW_DND)) - return; - - RGFW_events[RGFW_eventLen].droppedFilesCount = count; - RGFW_dndCallback(RGFW_root, RGFW_events[RGFW_eventLen].droppedFiles, count); - RGFW_eventLen++; -} - -b8 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 && - (waitMS < 0 || (RGFW_getTimeNS() / 1e+6) - start < waitMS) - ) { - emscripten_sleep(0); - } - - RGFW_stopCheckEvents_bool = RGFW_FALSE; -} - -RGFWDEF void RGFW_init_buffer(RGFW_window* win); -void RGFW_init_buffer(RGFW_window* win) { - #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0) - RGFW_bufferSize = RGFW_getScreenSize(); - - win->buffer = RGFW_MALLOC(RGFW_bufferSize.w * RGFW_bufferSize.h * 4); - #ifdef RGFW_OSMESA - win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL); - OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h); - #endif - #else - RGFW_UNUSED(win); /*!< 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, - strcpy doesn't seem to work, maybe because of asyncio - */ - - RGFW_events[RGFW_eventLen].type = RGFW_dnd; - char** arr = (char**)&RGFW_events[RGFW_eventLen].droppedFiles[index]; - *arr = file; -} - -#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); -} - -RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) { - RGFW_UNUSED(name) - - RGFW_UNUSED(RGFW_initFormatAttribs); - - RGFW_window* win = RGFW_window_basic_init(rect, args); - -#ifndef RGFW_WEBGPU - EmscriptenWebGLContextAttributes attrs; - attrs.alpha = EM_TRUE; - attrs.depth = EM_TRUE; - attrs.alpha = EM_TRUE; - attrs.stencil = RGFW_STENCIL; - attrs.antialias = RGFW_SAMPLES; - attrs.premultipliedAlpha = EM_TRUE; - attrs.preserveDrawingBuffer = EM_FALSE; - - if (RGFW_DOUBLE_BUFFER == 0) - attrs.renderViaOffscreenBackBuffer = 0; - else - attrs.renderViaOffscreenBackBuffer = RGFW_AUX_BUFFERS; - - attrs.failIfMajorPerformanceCaveat = EM_FALSE; - attrs.majorVersion = (RGFW_majorVersion == 0) ? 1 : RGFW_majorVersion; - attrs.minorVersion = RGFW_minorVersion; - - 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();"); - #endif -#else - 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_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_keydown); - emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_keyup); - 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 (args & RGFW_ALLOW_DND) { - win->_winArgs |= RGFW_ALLOW_DND; - } - - 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_init_buffer(win); - glViewport(0, 0, rect.w, rect.h); - - RGFW_root = win; - - if (args & RGFW_HIDE_MOUSE) { - RGFW_window_showMouse(win, 0); - } - - if (args & RGFW_FULLSCREEN) { - RGFW_window_resize(win, RGFW_getScreenSize()); - } - - return win; -} - -RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) { - static u8 index = 0; - - if (index == 0) - RGFW_resetKey(); - - /* check gamepads */ - for (int i = 0; (i < emscripten_get_num_gamepads()) && (i < 4); i++) { - if (RGFW_joysticks[i] == 0) - continue;; - - EmscriptenGamepadEvent gamepadState; - - if (emscripten_get_gamepad_status(i, &gamepadState) != EMSCRIPTEN_RESULT_SUCCESS) - break; - - // Register buttons data for every connected gamepad - for (int j = 0; (j < gamepadState.numButtons) && (j < 16); j++) { - u32 map[] = { - RGFW_JS_A, RGFW_JS_X, RGFW_JS_B, RGFW_JS_Y, - RGFW_JS_L1, RGFW_JS_R1, RGFW_JS_L2, RGFW_JS_R2, - RGFW_JS_SELECT, RGFW_JS_START, - 0, 0, - RGFW_JS_UP, RGFW_JS_DOWN, RGFW_JS_LEFT, RGFW_JS_RIGHT - }; - - u32 button = map[j]; - if (RGFW_jsPressed[i][button] != gamepadState.digitalButton[j]) { - win->event.type = RGFW_jsButtonPressed; - win->event.joystick = i; - win->event.button = map[j]; - return &win->event; - } - - RGFW_jsPressed[i][button] = gamepadState.digitalButton[j]; - } - - for (int j = 0; (j < gamepadState.numAxes) && (j < 4); j += 2) { - win->event.axisesCount = gamepadState.numAxes; - if (win->event.axis[j].x != gamepadState.axis[j] || - win->event.axis[j].y != gamepadState.axis[j + 1] - ) { - win->event.axis[j].x = gamepadState.axis[j]; - win->event.axis[j].y = gamepadState.axis[j + 1]; - win->event.type = RGFW_jsAxisMove; - win->event.joystick = i; - return &win->event; - } - } - } - - /* check queued events */ - if (RGFW_eventLen == 0) - return NULL; - - RGFW_events[index].frameTime = win->event.frameTime; - RGFW_events[index].frameTime2 = win->event.frameTime2; - RGFW_events[index].inFocus = win->event.inFocus; - - win->event = RGFW_events[index]; - - RGFW_eventLen--; - - if (RGFW_eventLen) - index++; - else - index = 0; - - return &win->event; -} - -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 */ -void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) { RGFW_UNUSED(win); RGFW_UNUSED(channels) RGFW_UNUSED(a) RGFW_UNUSED(image) } - -const char RGFW_CURSORS[11][12] = { - "default", - "default", - "text", - "crosshair", - "pointer", - "ew-resize", - "ns-resize", - "nwse-resize", - "nesw-resize", - "move", - "not-allowed" -}; - -void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { - RGFW_UNUSED(win) - EM_ASM( { document.getElementById("canvas").style.cursor = UTF8ToString($0); }, RGFW_CURSORS[mouse]); -} - -void RGFW_window_setMouseDefault(RGFW_window* win) { - RGFW_window_setMouseStandard(win, RGFW_MOUSE_NORMAL); -} - -void RGFW_window_showMouse(RGFW_window* win, i8 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; -} - -RGFW_point RGFW_window_getMousePoint(RGFW_window* win) { - RGFW_UNUSED(win); - - EmscriptenMouseEvent mouseEvent; - emscripten_get_mouse_status(&mouseEvent); - return RGFW_POINT( mouseEvent.targetX, mouseEvent.targetY); -} - -void RGFW_window_setMousePassthrough(RGFW_window* win, b8 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); -} - - -char* RGFW_readClipboard(size_t* size) { - /* - placeholder code for later - I'm not sure if this is possible do the the async stuff - */ - - if (size != NULL) - *size = 0; - - char* str = (char*)malloc(1); - str[0] = '\0'; - - return str; -} - -void RGFW_window_swapBuffers(RGFW_window* win) { - RGFW_UNUSED(win); - - #ifdef RGFW_BUFFER - if (!(win->_winArgs & RGFW_NO_CPU_RENDER)) { - glEnable(GL_TEXTURE_2D); - - GLuint texture; - glGenTextures(1,&texture); - - glBindTexture(GL_TEXTURE_2D,texture); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, RGFW_bufferSize.w, RGFW_bufferSize.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, win->buffer); - - float ratioX = ((float)win->r.w / (float)RGFW_bufferSize.w); - float ratioY = ((float)win->r.h / (float)RGFW_bufferSize.h); - - // Set up the viewport - glClear(GL_COLOR_BUFFER_BIT); - - glBegin(GL_TRIANGLES); - glTexCoord2f(0, ratioY); glColor3f(1, 1, 1); glVertex2f(-1, -1); - glTexCoord2f(0, 0); glColor3f(1, 1, 1); glVertex2f(-1, 1); - glTexCoord2f(ratioX, ratioY); glColor3f(1, 1, 1); glVertex2f(1, -1); - - glTexCoord2f(ratioX, 0); glColor3f(1, 1, 1); glVertex2f(1, 1); - glTexCoord2f(ratioX, ratioY); glColor3f(1, 1, 1); glVertex2f(1, -1); - glTexCoord2f(0, 0); glColor3f(1, 1, 1); glVertex2f(-1, 1); - glEnd(); - - glDeleteTextures(1, &texture); - } - #endif - -#ifndef RGFW_WEBGPU - emscripten_webgl_commit_frame(); -#endif - emscripten_sleep(0); -} - - -void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { -#ifndef RGFW_WEBGPU - if (win == NULL) - emscripten_webgl_make_context_current(0); - else - emscripten_webgl_make_context_current(win->src.ctx); -#endif -} - -#ifndef RGFW_EGL -void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { RGFW_UNUSED(win); RGFW_UNUSED(swapInterval); } -#endif - -void RGFW_window_close(RGFW_window* win) { -#ifndef RGFW_WEBGPU - emscripten_webgl_destroy_context(win->src.ctx); -#endif - - free(win); -} - -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()); -} - -void* RGFW_getProcAddress(const char* procname) { - return emscripten_webgl_get_proc_address(procname); -} - -void RGFW_sleep(u64 milisecond) { - emscripten_sleep(milisecond); -} - -u64 RGFW_getTimeNS(void) { - return emscripten_get_now() * 1e+6; -} - -u64 RGFW_getTime(void) { - return emscripten_get_now() * 1000; -} - -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, char* name) { - RGFW_UNUSED(win); - emscripten_set_window_title(name); -} - -/* unsupported functions */ -RGFW_monitor* RGFW_getMonitors(void) { 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_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_setBorder(RGFW_window* win, b8 border) { RGFW_UNUSED(win) RGFW_UNUSED(border) } -void RGFW_window_setIcon(RGFW_window* win, u8* icon, RGFW_area a, i32 channels) { RGFW_UNUSED(win) RGFW_UNUSED(icon) RGFW_UNUSED(a) RGFW_UNUSED(channels) } -void RGFW_window_hide(RGFW_window* win) { RGFW_UNUSED(win) } -void RGFW_window_show(RGFW_window* win) {RGFW_UNUSED(win) } -b8 RGFW_window_isHidden(RGFW_window* win) { RGFW_UNUSED(win) return 0; } -b8 RGFW_window_isMinimized(RGFW_window* win) { RGFW_UNUSED(win) return 0; } -b8 RGFW_window_isMaximized(RGFW_window* win) { RGFW_UNUSED(win) return 0; } -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_WEBASM) || defined(RGFW_WAYLAND) -/* unix threading */ -#ifndef RGFW_NO_THREADS -#include - - RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args) { - RGFW_UNUSED(args); - - RGFW_thread t; - pthread_create((pthread_t*) &t, NULL, *ptr, NULL); - return t; - } - void RGFW_cancelThread(RGFW_thread thread) { pthread_cancel((pthread_t) thread); } - void RGFW_joinThread(RGFW_thread thread) { pthread_join((pthread_t) thread, NULL); } -#ifdef __linux__ - void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { pthread_setschedprio((pthread_t)thread, priority); } -#endif -#endif - -#ifndef RGFW_WEBASM -/* unix sleep */ - void RGFW_sleep(u64 ms) { - struct timespec time; - time.tv_sec = 0; - time.tv_nsec = ms * 1e+6; - - nanosleep(&time, NULL); - } -#endif - -#endif /* end of unix / mac stuff*/ -#endif /*RGFW_IMPLEMENTATION*/ - -#if defined(__cplusplus) && !defined(__EMSCRIPTEN__) -} -#endif +/* +* +* 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, 1, 1, 1); + 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 + #define RGFW_UNIX + #include + #endif + + #include +#endif +#if !defined(RGFW_NO_X11) && !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 */ + u32 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 */ + +/*! 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, RGFW_bool software); +/*!< 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_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_keyLast = 256 /* padding for alignment ~(175 by default) */ + }; + +RGFWDEF u32 RGFW_apiKeyToRGFW(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 +#else +#define RGFW_GOTO_WAYLAND(fallback) +#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 + + Why not use macros instead of the numbers itself? + Windows -> Not all scancodes keys are macros + Linux -> Only symcodes are values, (XK_0 - XK_1, XK_a - XK_z) are larger than 0xFF00, I can't find any way to work with them without making the array an unreasonable size + MacOS -> windows and linux already don't have keycodes as macros, so there's no point +*/ + + + +/* + 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 + +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, 112, 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 +#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]; +} +#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) + 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 && _RGFW.eventIndex < (_RGFW.eventLen - 1)) + _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_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); } + +#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_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(u32 useSoftware); +i32* RGFW_initFormatAttribs(u32 useSoftware) { + RGFW_UNUSED(useSoftware); + 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 (useSoftware) { + 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, RGFW_bool software) { + RGFW_UNUSED(software); +#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 + win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.display); + #else + 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 + win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL); + #else + 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) +wayland: + 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) +wayland: + 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) + 1; + + /* flip right and middle button codes */ + if (b == 2) b = 3; + else if (b == 3) b = 2; + + RGFW_mouseButtons[b].prev = RGFW_mouseButtons[b].current; + RGFW_mouseButtons[b].current = RGFW_BOOL(state); + + RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonPressed + RGFW_BOOL(state); + 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.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); + + RGFW_resetKey(); + if ((win->_flags & RGFW_HOLD_MOUSE)) RGFW_window_mouseHold(win, RGFW_AREA(win->r.w, 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); + win->_flags &= ~(u32)RGFW_windowFocus; + RGFW_focusCallback(win, RGFW_FALSE); +} +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); + struct wl_pointer_listener pointer_listener = (struct wl_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}; + + 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_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) + typedef XcursorImage* (*PFN_XcursorImageCreate)(int, int); + typedef void (*PFN_XcursorImageDestroy)(XcursorImage*); + typedef Cursor(*PFN_XcursorImageLoadCursor)(Display*, const XcursorImage*); +#endif +#ifdef RGFW_OPENGL + typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); +#endif + +#if !defined(RGFW_NO_X11_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 + +const char* RGFW_instName = NULL; +void RGFW_setXInstName(const char* name) { + RGFW_instName = name; +} + +#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 + wayland: {} + 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 + wayland:{} + #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 + wayland: + 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 + wayland: + 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 + wayland: + 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)); \ +} + + + +void RGFW_window_getVisual(RGFW_window* win, RGFW_bool software) { +#if defined(RGFW_OPENGL) && !defined(RGFW_EGL) + i32* visual_attribs = RGFW_initFormatAttribs(software); + 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"); + + XFree(fbc); + win->src.visual = *vi; + XFree(vi); +#else + RGFW_UNUSED(software); + 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 +} + +#ifndef RGFW_EGL +void RGFW_window_initOpenGL(RGFW_window* win, RGFW_bool software) { + RGFW_UNUSED(software); +#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); RGFW_UNUSED(software); +#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 +wayland: + _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, RGFW_BOOL(flags & RGFW_windowOpenglSoftware)); + + /* 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_BOOL(flags & RGFW_windowOpenglSoftware)); + 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 + wayland: + 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.window = _RGFW.helperWindow; + XMapWindow(win->src.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_BOOL(flags & RGFW_windowOpenglSoftware)); + 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_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 + wayland: return RGFW_AREA(_RGFW.root->r.w, _RGFW.root->r.h); /* TODO */ + #endif +} + +RGFW_point RGFW_getGlobalMousePoint(void) { + RGFW_init(); + RGFW_point RGFWMouse; + + 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; +} + +RGFWDEF void RGFW_XHandleClipboardSelection(XEvent* event); +void RGFW_XHandleClipboardSelection(XEvent* event) { + 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); +} + +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); + +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); + KeySym sym = (KeySym)XkbKeycodeToKeysym(win->src.display, (KeyCode)E.xkey.keycode, 0, (KeyCode)E.xkey.state & ShiftMask ? 1 : 0); + + if ((E.xkey.state & LockMask) && sym >= XK_a && sym <= XK_z) + sym = (E.xkey.state & ShiftMask) ? sym + 32 : sym - 32; + if ((u8)sym != (u32)sym) + sym = 0; + + win->event.keyChar = (u8)sym; + + 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.format = 32; + new_reply.xclient.message_type = XdndFinished; + 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); + + + RGFW_resetKey(); + if ((win->_flags & RGFW_HOLD_MOUSE)) RGFW_window_mouseHold(win, RGFW_AREA(win->r.w, win->r.h)); + break; + case FocusOut: + if ((win->_flags & RGFW_windowFullscreen)) + RGFW_window_minimize(win); + + win->_flags &= ~(u32)RGFW_windowFocus; + win->event.type = RGFW_focusOut; + RGFW_focusCallback(win, 0); + 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 + wayland: + 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 + wayland: + 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 + wayland: + 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); + + if (a.w == 0 && a.h == 0) + return; + + 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); +} + +void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { + RGFW_ASSERT(win != NULL); + + 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); +} + +void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { + RGFW_ASSERT(win != NULL); + + 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); +} + +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); +} + +void RGFW_window_maximize(RGFW_window* win) { + win->_oldRect = win->r; + RGFW_toggleXMaximized(win, 1); +} + +void RGFW_window_focus(RGFW_window* win) { + RGFW_ASSERT(win); + + 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); +} + +void RGFW_window_raise(RGFW_window* win) { + RGFW_ASSERT(win); + XRaiseWindow(win->src.display, win->src.window); + XMapRaised(win->src.display, win->src.window); +} + +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); +} + +void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) { + RGFW_ASSERT(win != NULL); + if (fullscreen) { + win->_flags |= RGFW_windowFullscreen; + win->_oldRect = win->r; + } + else win->_flags &= ~(u32)RGFW_windowFullscreen; + + 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); +} + +void 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_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(win->src.display, win->src.window, + NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &value, 1); +} + +void RGFW_window_minimize(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + if (RGFW_window_isMaximized(win)) return; + + win->_oldRect = win->r; + XIconifyWindow(win->src.display, win->src.window, DefaultScreen(win->src.display)); + XFlush(win->src.display); +} + +void RGFW_window_restore(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_toggleXMaximized(win, 0); + + 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); + XFlush(win->src.display); +} + +RGFW_bool 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(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); + + 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 + wayland: + 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); + 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 /* 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 + wayland: + 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 + wayland: + 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 + wayland: + 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 + wayland: + 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 + wayland: + 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 + wayland: { } + 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 + wayland: + 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 + wayland: + /* 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) +wayland: 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 + wayland: + RGFW_UNUSED(text); RGFW_UNUSED(textLen); + #endif +} + +RGFW_bool RGFW_window_isHidden(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + XWindowAttributes windowAttributes; + XGetWindowAttributes(win->src.display, win->src.window, &windowAttributes); + + return (windowAttributes.map_state == IsUnmapped && !RGFW_window_isMinimized(win)); +} + +RGFW_bool 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(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; +} + +RGFW_bool 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(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); + + 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 + + +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; +} + +RGFW_monitor RGFW_XCreateMonitor(i32 screen); +RGFW_monitor RGFW_XCreateMonitor(i32 screen) { + RGFW_monitor monitor; + RGFW_init(); + 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; +} + +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 + wayland: 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 + wayland: 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 +wayland: +#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 +wayland: +#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 + wayland: + #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 + wayland: +#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); + 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); + + #ifdef RGFW_X11 + RGFW_GOTO_WAYLAND(0); + + /* 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 + wayland: + + #ifdef RGFW_X11 + XDestroyWindow(win->src.display, (Drawable) win->src.window); + #endif + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a window was freed"); + _RGFW.windowCount--; + if (_RGFW.windowCount == 0) RGFW_deinit(); + + xdg_toplevel_destroy(win->src.xdg_toplevel); + xdg_surface_destroy(win->src.xdg_surface); + wl_surface_destroy(win->src.surface); + + #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); + + 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; + int height = windowRect.bottom - windowRect.top; + 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((windowRect.right - windowRect.left), + (u32)(windowRect.bottom - windowRect.top) - (u32)win->src.hOffset)); + } + + win->r.w = windowRect.right - windowRect.left; + 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; + 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; + 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, RGFW_bool software) { +#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 (software) + 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(software); + + 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 (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); RGFW_UNUSED(software); +#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.window = CreateWindowW(Class.lpszClassName, (wchar_t*)wide_name, window_style, win->r.x, win->r.y, win->r.w, 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_BOOL(flags & RGFW_windowOpenglSoftware)); + 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, 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); +} + +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 (ChangeDisplaySettingsEx(dd.DeviceName, &dm, NULL, CDS_TEST, NULL) == DISP_CHANGE_SUCCESSFUL) { + if (ChangeDisplaySettingsEx(dd.DeviceName, &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, 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); + + RGFW_resetKey(); + 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; + + win->_flags &= ~(u32)RGFW_windowFocus; + 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, RGFW_bool software) { +#ifdef RGFW_OPENGL + void* attrs = RGFW_initFormatAttribs(software); + 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"); + void* subAttrs = RGFW_initFormatAttribs(1); + 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); RGFW_UNUSED(software); +#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_BOOL(flags & RGFW_windowOpenglSoftware)); + 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")); +} + +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); + + RGFW_resetKey(); + 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.root->_flags &= ~(u32)RGFW_windowFocus; + 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, RGFW_bool software) { +#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, 0); + + #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; +} + +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/raylib/external/android/native_app_glue/vendor.go b/raylib/external/android/native_app_glue/vendor.go new file mode 100644 index 0000000..0fb2a0f --- /dev/null +++ b/raylib/external/android/native_app_glue/vendor.go @@ -0,0 +1,4 @@ +//go:build required +// +build required + +package vendor diff --git a/raylib/external/glfw/include/GLFW/vendor.go b/raylib/external/glfw/include/GLFW/vendor.go new file mode 100644 index 0000000..0fb2a0f --- /dev/null +++ b/raylib/external/glfw/include/GLFW/vendor.go @@ -0,0 +1,4 @@ +//go:build required +// +build required + +package vendor diff --git a/raylib/external/glfw/src/fractional-scale-v1-client-protocol-code.h b/raylib/external/glfw/src/fractional-scale-v1-client-protocol-code.h index 090166b..f847c1e 100644 --- a/raylib/external/glfw/src/fractional-scale-v1-client-protocol-code.h +++ b/raylib/external/glfw/src/fractional-scale-v1-client-protocol-code.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.22.0 */ +/* Generated by wayland-scanner 1.23.1 */ /* * Copyright © 2022 Kenny Levinsen @@ -23,6 +23,7 @@ * DEALINGS IN THE SOFTWARE. */ +#include #include #include #include "wayland-util.h" diff --git a/raylib/external/glfw/src/fractional-scale-v1-client-protocol.h b/raylib/external/glfw/src/fractional-scale-v1-client-protocol.h index 7f0dec4..8df7558 100644 --- a/raylib/external/glfw/src/fractional-scale-v1-client-protocol.h +++ b/raylib/external/glfw/src/fractional-scale-v1-client-protocol.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.22.0 */ +/* Generated by wayland-scanner 1.23.1 */ #ifndef FRACTIONAL_SCALE_V1_CLIENT_PROTOCOL_H #define FRACTIONAL_SCALE_V1_CLIENT_PROTOCOL_H diff --git a/raylib/external/glfw/src/idle-inhibit-unstable-v1-client-protocol-code.h b/raylib/external/glfw/src/idle-inhibit-unstable-v1-client-protocol-code.h index 76ef732..0399e11 100644 --- a/raylib/external/glfw/src/idle-inhibit-unstable-v1-client-protocol-code.h +++ b/raylib/external/glfw/src/idle-inhibit-unstable-v1-client-protocol-code.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.22.0 */ +/* Generated by wayland-scanner 1.23.1 */ /* * Copyright © 2015 Samsung Electronics Co., Ltd @@ -23,6 +23,7 @@ * DEALINGS IN THE SOFTWARE. */ +#include #include #include #include "wayland-util.h" diff --git a/raylib/external/glfw/src/idle-inhibit-unstable-v1-client-protocol.h b/raylib/external/glfw/src/idle-inhibit-unstable-v1-client-protocol.h index a033a11..ef97ceb 100644 --- a/raylib/external/glfw/src/idle-inhibit-unstable-v1-client-protocol.h +++ b/raylib/external/glfw/src/idle-inhibit-unstable-v1-client-protocol.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.22.0 */ +/* Generated by wayland-scanner 1.23.1 */ #ifndef IDLE_INHIBIT_UNSTABLE_V1_CLIENT_PROTOCOL_H #define IDLE_INHIBIT_UNSTABLE_V1_CLIENT_PROTOCOL_H diff --git a/raylib/external/glfw/src/pointer-constraints-unstable-v1-client-protocol-code.h b/raylib/external/glfw/src/pointer-constraints-unstable-v1-client-protocol-code.h index 5935c0b..4184538 100644 --- a/raylib/external/glfw/src/pointer-constraints-unstable-v1-client-protocol-code.h +++ b/raylib/external/glfw/src/pointer-constraints-unstable-v1-client-protocol-code.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.22.0 */ +/* Generated by wayland-scanner 1.23.1 */ /* * Copyright © 2014 Jonas Ådahl @@ -24,6 +24,7 @@ * DEALINGS IN THE SOFTWARE. */ +#include #include #include #include "wayland-util.h" diff --git a/raylib/external/glfw/src/pointer-constraints-unstable-v1-client-protocol.h b/raylib/external/glfw/src/pointer-constraints-unstable-v1-client-protocol.h index 5ff3867..dd7b871 100644 --- a/raylib/external/glfw/src/pointer-constraints-unstable-v1-client-protocol.h +++ b/raylib/external/glfw/src/pointer-constraints-unstable-v1-client-protocol.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.22.0 */ +/* Generated by wayland-scanner 1.23.1 */ #ifndef POINTER_CONSTRAINTS_UNSTABLE_V1_CLIENT_PROTOCOL_H #define POINTER_CONSTRAINTS_UNSTABLE_V1_CLIENT_PROTOCOL_H @@ -512,9 +512,8 @@ zwp_locked_pointer_v1_destroy(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v * information to warp the pointer upon unlock in order to avoid pointer * jumps. * - * The cursor position hint is double buffered. The new hint will only take - * effect when the associated surface gets it pending state applied. See - * wl_surface.commit for details. + * The cursor position hint is double-buffered state, see + * wl_surface.commit. */ static inline void zwp_locked_pointer_v1_set_cursor_position_hint(struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1, wl_fixed_t surface_x, wl_fixed_t surface_y) @@ -528,9 +527,7 @@ zwp_locked_pointer_v1_set_cursor_position_hint(struct zwp_locked_pointer_v1 *zwp * * Set a new region used to lock the pointer. * - * The new lock region is double-buffered. The new lock region will - * only take effect when the associated surface gets its pending state - * applied. See wl_surface.commit for details. + * The new lock region is double-buffered, see wl_surface.commit. * * For details about the lock region, see wp_locked_pointer. */ @@ -638,9 +635,7 @@ zwp_confined_pointer_v1_destroy(struct zwp_confined_pointer_v1 *zwp_confined_poi * * Set a new region used to confine the pointer. * - * The new confine region is double-buffered. The new confine region will - * only take effect when the associated surface gets its pending state - * applied. See wl_surface.commit for details. + * 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 diff --git a/raylib/external/glfw/src/relative-pointer-unstable-v1-client-protocol-code.h b/raylib/external/glfw/src/relative-pointer-unstable-v1-client-protocol-code.h index e650496..605149b 100644 --- a/raylib/external/glfw/src/relative-pointer-unstable-v1-client-protocol-code.h +++ b/raylib/external/glfw/src/relative-pointer-unstable-v1-client-protocol-code.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.22.0 */ +/* Generated by wayland-scanner 1.23.1 */ /* * Copyright © 2014 Jonas Ådahl @@ -24,6 +24,7 @@ * DEALINGS IN THE SOFTWARE. */ +#include #include #include #include "wayland-util.h" diff --git a/raylib/external/glfw/src/relative-pointer-unstable-v1-client-protocol.h b/raylib/external/glfw/src/relative-pointer-unstable-v1-client-protocol.h index 5aa213e..5c79482 100644 --- a/raylib/external/glfw/src/relative-pointer-unstable-v1-client-protocol.h +++ b/raylib/external/glfw/src/relative-pointer-unstable-v1-client-protocol.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.22.0 */ +/* Generated by wayland-scanner 1.23.1 */ #ifndef RELATIVE_POINTER_UNSTABLE_V1_CLIENT_PROTOCOL_H #define RELATIVE_POINTER_UNSTABLE_V1_CLIENT_PROTOCOL_H diff --git a/raylib/external/glfw/src/vendor.go b/raylib/external/glfw/src/vendor.go new file mode 100644 index 0000000..0fb2a0f --- /dev/null +++ b/raylib/external/glfw/src/vendor.go @@ -0,0 +1,4 @@ +//go:build required +// +build required + +package vendor diff --git a/raylib/external/glfw/src/viewporter-client-protocol-code.h b/raylib/external/glfw/src/viewporter-client-protocol-code.h index fa70d5d..d685858 100644 --- a/raylib/external/glfw/src/viewporter-client-protocol-code.h +++ b/raylib/external/glfw/src/viewporter-client-protocol-code.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.22.0 */ +/* Generated by wayland-scanner 1.23.1 */ /* * Copyright © 2013-2016 Collabora, Ltd. @@ -23,6 +23,7 @@ * DEALINGS IN THE SOFTWARE. */ +#include #include #include #include "wayland-util.h" diff --git a/raylib/external/glfw/src/viewporter-client-protocol.h b/raylib/external/glfw/src/viewporter-client-protocol.h index e1295d4..f286095 100644 --- a/raylib/external/glfw/src/viewporter-client-protocol.h +++ b/raylib/external/glfw/src/viewporter-client-protocol.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.22.0 */ +/* Generated by wayland-scanner 1.23.1 */ #ifndef VIEWPORTER_CLIENT_PROTOCOL_H #define VIEWPORTER_CLIENT_PROTOCOL_H @@ -86,8 +86,7 @@ extern const struct wl_interface wp_viewporter_interface; * src_y, src_width, src_height), and the destination size (dst_width, * dst_height). The contents of the source rectangle are scaled to the * destination size, and content outside the source rectangle is ignored. - * This state is double-buffered, and is applied on the next - * wl_surface.commit. + * This state is double-buffered, see wl_surface.commit. * * The two parts of crop and scale state are independent: the source * rectangle, and the destination size. Initially both are unset, that @@ -147,8 +146,7 @@ extern const struct wl_interface wp_viewporter_interface; * src_y, src_width, src_height), and the destination size (dst_width, * dst_height). The contents of the source rectangle are scaled to the * destination size, and content outside the source rectangle is ignored. - * This state is double-buffered, and is applied on the next - * wl_surface.commit. + * This state is double-buffered, see wl_surface.commit. * * The two parts of crop and scale state are independent: the source * rectangle, and the destination size. Initially both are unset, that @@ -359,8 +357,7 @@ wp_viewport_destroy(struct wp_viewport *wp_viewport) * or negative, or x or y are negative, raise the bad_value protocol * error. * - * The crop and scale state is double-buffered state, and will be - * applied on the next wl_surface.commit. + * The crop and scale state is double-buffered, see wl_surface.commit. */ static inline void wp_viewport_set_source(struct wp_viewport *wp_viewport, wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height) @@ -381,8 +378,7 @@ wp_viewport_set_source(struct wp_viewport *wp_viewport, wl_fixed_t x, wl_fixed_t * contains zero or negative values raises the bad_value protocol * error. * - * The crop and scale state is double-buffered state, and will be - * applied on the next wl_surface.commit. + * The crop and scale state is double-buffered, see wl_surface.commit. */ static inline void wp_viewport_set_destination(struct wp_viewport *wp_viewport, int32_t width, int32_t height) diff --git a/raylib/external/glfw/src/wayland-client-protocol-code.h b/raylib/external/glfw/src/wayland-client-protocol-code.h index cd1a838..d640bb4 100644 --- a/raylib/external/glfw/src/wayland-client-protocol-code.h +++ b/raylib/external/glfw/src/wayland-client-protocol-code.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.22.0 */ +/* Generated by wayland-scanner 1.23.1 */ /* * Copyright © 2008-2011 Kristian Høgsberg @@ -27,6 +27,7 @@ * SOFTWARE. */ +#include #include #include #include "wayland-util.h" @@ -154,6 +155,7 @@ static const struct wl_interface *wayland_types[] = { &wl_surface_interface, &wl_surface_interface, &wl_surface_interface, + &wl_registry_interface, }; static const struct wl_message wl_display_requests[] = { @@ -397,7 +399,7 @@ static const struct wl_message wl_seat_events[] = { }; WL_PRIVATE const struct wl_interface wl_seat_interface = { - "wl_seat", 9, + "wl_seat", 10, 4, wl_seat_requests, 2, wl_seat_events, }; @@ -422,7 +424,7 @@ static const struct wl_message wl_pointer_events[] = { }; WL_PRIVATE const struct wl_interface wl_pointer_interface = { - "wl_pointer", 9, + "wl_pointer", 10, 2, wl_pointer_requests, 11, wl_pointer_events, }; @@ -441,7 +443,7 @@ static const struct wl_message wl_keyboard_events[] = { }; WL_PRIVATE const struct wl_interface wl_keyboard_interface = { - "wl_keyboard", 9, + "wl_keyboard", 10, 1, wl_keyboard_requests, 6, wl_keyboard_events, }; @@ -461,7 +463,7 @@ static const struct wl_message wl_touch_events[] = { }; WL_PRIVATE const struct wl_interface wl_touch_interface = { - "wl_touch", 9, + "wl_touch", 10, 1, wl_touch_requests, 7, wl_touch_events, }; @@ -523,3 +525,14 @@ WL_PRIVATE const struct wl_interface wl_subsurface_interface = { 0, NULL, }; +static const struct wl_message wl_fixes_requests[] = { + { "destroy", "", wayland_types + 0 }, + { "destroy_registry", "o", wayland_types + 95 }, +}; + +WL_PRIVATE const struct wl_interface wl_fixes_interface = { + "wl_fixes", 1, + 2, wl_fixes_requests, + 0, NULL, +}; + diff --git a/raylib/external/glfw/src/wayland-client-protocol.h b/raylib/external/glfw/src/wayland-client-protocol.h index d200c79..fadb460 100644 --- a/raylib/external/glfw/src/wayland-client-protocol.h +++ b/raylib/external/glfw/src/wayland-client-protocol.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.22.0 */ +/* Generated by wayland-scanner 1.23.1 */ #ifndef WAYLAND_CLIENT_PROTOCOL_H #define WAYLAND_CLIENT_PROTOCOL_H @@ -36,6 +36,7 @@ extern "C" { * - @subpage page_iface_wl_region - region interface * - @subpage page_iface_wl_subcompositor - sub-surface compositing * - @subpage page_iface_wl_subsurface - sub-surface interface to a wl_surface + * - @subpage page_iface_wl_fixes - wayland protocol fixes * @section page_copyright_wayland Copyright *
  *
@@ -73,6 +74,7 @@ struct wl_data_device_manager;
 struct wl_data_offer;
 struct wl_data_source;
 struct wl_display;
+struct wl_fixes;
 struct wl_keyboard;
 struct wl_output;
 struct wl_pointer;
@@ -283,8 +285,10 @@ extern const struct wl_interface wl_shm_interface;
  * client provides and updates the contents is defined by the buffer factory
  * interface.
  *
- * If the buffer uses a format that has an alpha channel, the alpha channel
- * is assumed to be premultiplied in the electrical color channel values
+ * Color channels are assumed to be electrical rather than optical (in other
+ * words, encoded with a transfer function) unless otherwise specified. If
+ * the buffer uses a format that has an alpha channel, the alpha channel is
+ * assumed to be premultiplied into the electrical color channel values
  * (after transfer function encoding) unless otherwise specified.
  *
  * Note, because wl_buffer objects are created from multiple independent
@@ -302,8 +306,10 @@ extern const struct wl_interface wl_shm_interface;
  * client provides and updates the contents is defined by the buffer factory
  * interface.
  *
- * If the buffer uses a format that has an alpha channel, the alpha channel
- * is assumed to be premultiplied in the electrical color channel values
+ * Color channels are assumed to be electrical rather than optical (in other
+ * words, encoded with a transfer function) unless otherwise specified. If
+ * the buffer uses a format that has an alpha channel, the alpha channel is
+ * assumed to be premultiplied into the electrical color channel values
  * (after transfer function encoding) unless otherwise specified.
  *
  * Note, because wl_buffer objects are created from multiple independent
@@ -652,6 +658,16 @@ extern const struct wl_interface wl_pointer_interface;
  *
  * The wl_keyboard interface represents one or more keyboards
  * associated with a seat.
+ *
+ * Each wl_keyboard has the following logical state:
+ *
+ * - an active surface (possibly null),
+ * - the keys currently logically down,
+ * - the active modifiers,
+ * - the active group.
+ *
+ * By default, the active surface is null, the keys currently logically down
+ * are empty, the active modifiers and the active group are 0.
  * @section page_iface_wl_keyboard_api API
  * See @ref iface_wl_keyboard.
  */
@@ -660,6 +676,16 @@ extern const struct wl_interface wl_pointer_interface;
  *
  * The wl_keyboard interface represents one or more keyboards
  * associated with a seat.
+ *
+ * Each wl_keyboard has the following logical state:
+ *
+ * - an active surface (possibly null),
+ * - the keys currently logically down,
+ * - the active modifiers,
+ * - the active group.
+ *
+ * By default, the active surface is null, the keys currently logically down
+ * are empty, the active modifiers and the active group are 0.
  */
 extern const struct wl_interface wl_keyboard_interface;
 #endif
@@ -916,6 +942,25 @@ extern const struct wl_interface wl_subcompositor_interface;
  */
 extern const struct wl_interface wl_subsurface_interface;
 #endif
+#ifndef WL_FIXES_INTERFACE
+#define WL_FIXES_INTERFACE
+/**
+ * @page page_iface_wl_fixes wl_fixes
+ * @section page_iface_wl_fixes_desc Description
+ *
+ * This global fixes problems with other core-protocol interfaces that
+ * cannot be fixed in these interfaces themselves.
+ * @section page_iface_wl_fixes_api API
+ * See @ref iface_wl_fixes.
+ */
+/**
+ * @defgroup iface_wl_fixes The wl_fixes interface
+ *
+ * This global fixes problems with other core-protocol interfaces that
+ * cannot be fixed in these interfaces themselves.
+ */
+extern const struct wl_interface wl_fixes_interface;
+#endif
 
 #ifndef WL_DISPLAY_ERROR_ENUM
 #define WL_DISPLAY_ERROR_ENUM
@@ -1050,7 +1095,7 @@ wl_display_get_version(struct wl_display *wl_display)
  * compositor after the callback is fired and as such the client must not
  * attempt to use it after that point.
  *
- * The callback_data passed in the callback is the event serial.
+ * The callback_data passed in the callback is undefined and should be ignored.
  */
 static inline struct wl_callback *
 wl_display_sync(struct wl_display *wl_display)
@@ -3715,10 +3760,9 @@ struct wl_surface_listener {
 	 * Before receiving this event the preferred buffer transform for
 	 * this surface is normal.
 	 *
-	 * It is intended that transform aware clients use this event to
-	 * apply the transform to their content and use
-	 * wl_surface.set_buffer_transform to indicate the transform they
-	 * have rendered with.
+	 * Applying this transformation to the surface buffer contents and
+	 * using wl_surface.set_buffer_transform might allow the compositor
+	 * to use the surface buffer more efficiently.
 	 * @param transform preferred transform
 	 * @since 6
 	 */
@@ -3902,9 +3946,15 @@ wl_surface_destroy(struct wl_surface *wl_surface)
  * mutates the underlying buffer storage, the surface contents become
  * undefined immediately.
  *
- * If wl_surface.attach is sent with a NULL wl_buffer, or the pending
- * wl_buffer has been destroyed, the following wl_surface.commit will
- * remove the surface content.
+ * If wl_surface.attach is sent with a NULL wl_buffer, the
+ * following wl_surface.commit will remove the surface content.
+ *
+ * If a pending wl_buffer has been destroyed, the result is not specified.
+ * Many compositors are known to remove the surface content on the following
+ * wl_surface.commit, but this behaviour is not universal. Clients seeking to
+ * maximise compatibility should not destroy pending buffers and should
+ * ensure that they explicitly remove content from surfaces, even after
+ * destroying buffers.
  */
 static inline void
 wl_surface_attach(struct wl_surface *wl_surface, struct wl_buffer *buffer, int32_t x, int32_t y)
@@ -4065,16 +4115,18 @@ wl_surface_set_input_region(struct wl_surface *wl_surface, struct wl_region *reg
  *
  * Surface state (input, opaque, and damage regions, attached buffers,
  * etc.) is double-buffered. Protocol requests modify the pending state,
- * as opposed to the current state in use by the compositor. A commit
- * request atomically applies all pending state, replacing the current
- * state. After commit, the new pending state is as documented for each
- * related request.
+ * as opposed to the active state in use by the compositor.
  *
- * On commit, a pending wl_buffer is applied first, and all other state
- * second. This means that all coordinates in double-buffered state are
- * relative to the new wl_buffer coming into use, except for
- * wl_surface.attach itself. If there is no pending wl_buffer, the
- * coordinates are relative to the current surface contents.
+ * A commit request atomically creates a content update from the pending
+ * state, even if the pending state has not been touched. The content
+ * update is placed in a queue until it becomes active. After commit, the
+ * new pending state is as documented for each related request.
+ *
+ * When the content update is applied, the wl_buffer is applied before all
+ * other state. This means that all coordinates in double-buffered state
+ * are relative to the newly attached wl_buffers, except for
+ * wl_surface.attach itself. If there is no newly attached wl_buffer, the
+ * coordinates are relative to the previous content update.
  *
  * All requests that need a commit to become effective are documented
  * to affect double-buffered state.
@@ -4091,10 +4143,12 @@ wl_surface_commit(struct wl_surface *wl_surface)
 /**
  * @ingroup iface_wl_surface
  *
- * This request sets an optional transformation on how the compositor
- * interprets the contents of the buffer attached to the surface. The
- * accepted values for the transform parameter are the values for
- * wl_output.transform.
+ * This request sets the transformation that the client has already applied
+ * to the content of the buffer. The accepted values for the transform
+ * parameter are the values for wl_output.transform.
+ *
+ * The compositor applies the inverse of this transformation whenever it
+ * uses the buffer contents.
  *
  * Buffer transform is double-buffered state, see wl_surface.commit.
  *
@@ -4214,6 +4268,9 @@ wl_surface_damage_buffer(struct wl_surface *wl_surface, int32_t x, int32_t y, in
  * x and y, combined with the new surface size define in which
  * directions the surface's size changes.
  *
+ * The exact semantics of wl_surface.offset are role-specific. Refer to
+ * the documentation of specific roles for more information.
+ *
  * Surface location offset is double-buffered state, see
  * wl_surface.commit.
  *
@@ -4855,6 +4912,7 @@ struct wl_pointer_listener {
 	 * @param axis axis type
 	 * @param discrete number of steps
 	 * @since 5
+	 * @deprecated Deprecated since version 8
 	 */
 	void (*axis_discrete)(void *data,
 			      struct wl_pointer *wl_pointer,
@@ -5123,6 +5181,14 @@ enum wl_keyboard_keymap_format {
  * physical key state
  *
  * Describes the physical state of a key that produced the key event.
+ *
+ * Since version 10, the key can be in a "repeated" pseudo-state which
+ * means the same as "pressed", but is used to signal repetition in the
+ * key event.
+ *
+ * The key may only enter the repeated state after entering the pressed
+ * state and before entering the released state. This event may be
+ * generated multiple times while the key is down.
  */
 enum wl_keyboard_key_state {
 	/**
@@ -5133,7 +5199,16 @@ enum wl_keyboard_key_state {
 	 * key is pressed
 	 */
 	WL_KEYBOARD_KEY_STATE_PRESSED = 1,
+	/**
+	 * key was repeated
+	 * @since 10
+	 */
+	WL_KEYBOARD_KEY_STATE_REPEATED = 2,
 };
+/**
+ * @ingroup iface_wl_keyboard
+ */
+#define WL_KEYBOARD_KEY_STATE_REPEATED_SINCE_VERSION 10
 #endif /* WL_KEYBOARD_KEY_STATE_ENUM */
 
 /**
@@ -5167,9 +5242,18 @@ struct wl_keyboard_listener {
 	 *
 	 * The compositor must send the wl_keyboard.modifiers event after
 	 * this event.
+	 *
+	 * In the wl_keyboard logical state, this event sets the active
+	 * surface to the surface argument and the keys currently logically
+	 * down to the keys in the keys argument. The compositor must not
+	 * send this event if the wl_keyboard already had an active surface
+	 * immediately before this event.
+	 *
+	 * Clients should not use the list of pressed keys to emulate
+	 * key-press events. The order of keys in the list is unspecified.
 	 * @param serial serial number of the enter event
 	 * @param surface surface gaining keyboard focus
-	 * @param keys the currently pressed keys
+	 * @param keys the keys currently logically down
 	 */
 	void (*enter)(void *data,
 		      struct wl_keyboard *wl_keyboard,
@@ -5185,10 +5269,10 @@ struct wl_keyboard_listener {
 	 * The leave notification is sent before the enter notification for
 	 * the new focus.
 	 *
-	 * After this event client must assume that no keys are pressed, it
-	 * must stop key repeating if there's some going on and until it
-	 * receives the next wl_keyboard.modifiers event, the client must
-	 * also assume no modifiers are active.
+	 * In the wl_keyboard logical state, this event resets all values
+	 * to their defaults. The compositor must not send this event if
+	 * the active surface of the wl_keyboard was not equal to the
+	 * surface argument immediately before this event.
 	 * @param serial serial number of the leave event
 	 * @param surface surface that lost keyboard focus
 	 */
@@ -5208,8 +5292,20 @@ struct wl_keyboard_listener {
 	 * If this event produces a change in modifiers, then the resulting
 	 * wl_keyboard.modifiers event must be sent after this event.
 	 *
-	 * The compositor must not send this event without a surface of the
-	 * client having keyboard focus.
+	 * In the wl_keyboard logical state, this event adds the key to the
+	 * keys currently logically down (if the state argument is pressed)
+	 * or removes the key from the keys currently logically down (if
+	 * the state argument is released). The compositor must not send
+	 * this event if the wl_keyboard did not have an active surface
+	 * immediately before this event. The compositor must not send this
+	 * event if state is pressed (resp. released) and the key was
+	 * already logically down (resp. was not logically down)
+	 * immediately before this event.
+	 *
+	 * Since version 10, compositors may send key events with the
+	 * "repeated" key state when a wl_keyboard.repeat_info event with a
+	 * rate argument of 0 has been received. This allows the compositor
+	 * to take over the responsibility of key repetition.
 	 * @param serial serial number of the key event
 	 * @param time timestamp with millisecond granularity
 	 * @param key key that produced the event
@@ -5235,6 +5331,9 @@ struct wl_keyboard_listener {
 	 * the next wl_keyboard.modifiers event. In order to reset the
 	 * modifier state again, the compositor can send a
 	 * wl_keyboard.modifiers event with no pressed modifiers.
+	 *
+	 * In the wl_keyboard logical state, this event updates the
+	 * modifiers and group.
 	 * @param serial serial number of the modifiers event
 	 * @param mods_depressed depressed modifiers
 	 * @param mods_latched latched modifiers
@@ -5434,6 +5533,8 @@ struct wl_touch_listener {
 	 * points currently active on this client's surface. The client is
 	 * responsible for finalizing the touch points, future touch points
 	 * on this surface may reuse the touch point ID.
+	 *
+	 * No frame event is required after the cancel event.
 	 */
 	void (*cancel)(void *data,
 		       struct wl_touch *wl_touch);
@@ -5637,11 +5738,10 @@ enum wl_output_subpixel {
 #define WL_OUTPUT_TRANSFORM_ENUM
 /**
  * @ingroup iface_wl_output
- * transform from framebuffer to output
+ * transformation applied to buffer contents
  *
- * This describes the transform that a compositor will apply to a
- * surface to compensate for the rotation or mirroring of an
- * output device.
+ * This describes transformations that clients and compositors apply to
+ * buffer contents.
  *
  * The flipped values correspond to an initial flip around a
  * vertical axis followed by rotation.
@@ -5744,7 +5844,7 @@ struct wl_output_listener {
 	 * @param subpixel subpixel orientation of the output
 	 * @param make textual description of the manufacturer
 	 * @param model textual description of the model
-	 * @param transform transform that maps framebuffer to output
+	 * @param transform additional transformation applied to buffer contents during presentation
 	 */
 	void (*geometry)(void *data,
 			 struct wl_output *wl_output,
@@ -6352,6 +6452,69 @@ wl_subsurface_set_desync(struct wl_subsurface *wl_subsurface)
 			 WL_SUBSURFACE_SET_DESYNC, NULL, wl_proxy_get_version((struct wl_proxy *) wl_subsurface), 0);
 }
 
+#define WL_FIXES_DESTROY 0
+#define WL_FIXES_DESTROY_REGISTRY 1
+
+
+/**
+ * @ingroup iface_wl_fixes
+ */
+#define WL_FIXES_DESTROY_SINCE_VERSION 1
+/**
+ * @ingroup iface_wl_fixes
+ */
+#define WL_FIXES_DESTROY_REGISTRY_SINCE_VERSION 1
+
+/** @ingroup iface_wl_fixes */
+static inline void
+wl_fixes_set_user_data(struct wl_fixes *wl_fixes, void *user_data)
+{
+	wl_proxy_set_user_data((struct wl_proxy *) wl_fixes, user_data);
+}
+
+/** @ingroup iface_wl_fixes */
+static inline void *
+wl_fixes_get_user_data(struct wl_fixes *wl_fixes)
+{
+	return wl_proxy_get_user_data((struct wl_proxy *) wl_fixes);
+}
+
+static inline uint32_t
+wl_fixes_get_version(struct wl_fixes *wl_fixes)
+{
+	return wl_proxy_get_version((struct wl_proxy *) wl_fixes);
+}
+
+/**
+ * @ingroup iface_wl_fixes
+ */
+static inline void
+wl_fixes_destroy(struct wl_fixes *wl_fixes)
+{
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_fixes,
+			 WL_FIXES_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_fixes), WL_MARSHAL_FLAG_DESTROY);
+}
+
+/**
+ * @ingroup iface_wl_fixes
+ *
+ * This request destroys a wl_registry object.
+ *
+ * The client should no longer use the wl_registry after making this
+ * request.
+ *
+ * The compositor will emit a wl_display.delete_id event with the object ID
+ * of the registry and will no longer emit any events on the registry. The
+ * client should re-use the object ID once it receives the
+ * wl_display.delete_id event.
+ */
+static inline void
+wl_fixes_destroy_registry(struct wl_fixes *wl_fixes, struct wl_registry *registry)
+{
+	wl_proxy_marshal_flags((struct wl_proxy *) wl_fixes,
+			 WL_FIXES_DESTROY_REGISTRY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_fixes), 0, registry);
+}
+
 #ifdef  __cplusplus
 }
 #endif
diff --git a/raylib/external/glfw/src/xdg-activation-v1-client-protocol-code.h b/raylib/external/glfw/src/xdg-activation-v1-client-protocol-code.h
index 9e6cfc5..ceece5a 100644
--- a/raylib/external/glfw/src/xdg-activation-v1-client-protocol-code.h
+++ b/raylib/external/glfw/src/xdg-activation-v1-client-protocol-code.h
@@ -1,4 +1,4 @@
-/* Generated by wayland-scanner 1.22.0 */
+/* Generated by wayland-scanner 1.23.1 */
 
 /*
  * Copyright © 2020 Aleix Pol Gonzalez 
@@ -24,6 +24,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
+#include 
 #include 
 #include 
 #include "wayland-util.h"
diff --git a/raylib/external/glfw/src/xdg-activation-v1-client-protocol.h b/raylib/external/glfw/src/xdg-activation-v1-client-protocol.h
index 7e8cd29..b26c548 100644
--- a/raylib/external/glfw/src/xdg-activation-v1-client-protocol.h
+++ b/raylib/external/glfw/src/xdg-activation-v1-client-protocol.h
@@ -1,4 +1,4 @@
-/* Generated by wayland-scanner 1.22.0 */
+/* Generated by wayland-scanner 1.23.1 */
 
 #ifndef XDG_ACTIVATION_V1_CLIENT_PROTOCOL_H
 #define XDG_ACTIVATION_V1_CLIENT_PROTOCOL_H
diff --git a/raylib/external/glfw/src/xdg-decoration-unstable-v1-client-protocol-code.h b/raylib/external/glfw/src/xdg-decoration-unstable-v1-client-protocol-code.h
index 9389145..85496af 100644
--- a/raylib/external/glfw/src/xdg-decoration-unstable-v1-client-protocol-code.h
+++ b/raylib/external/glfw/src/xdg-decoration-unstable-v1-client-protocol-code.h
@@ -1,4 +1,4 @@
-/* Generated by wayland-scanner 1.22.0 */
+/* Generated by wayland-scanner 1.23.1 */
 
 /*
  * Copyright © 2018 Simon Ser
@@ -23,6 +23,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
+#include 
 #include 
 #include 
 #include "wayland-util.h"
diff --git a/raylib/external/glfw/src/xdg-decoration-unstable-v1-client-protocol.h b/raylib/external/glfw/src/xdg-decoration-unstable-v1-client-protocol.h
index 308c297..217af20 100644
--- a/raylib/external/glfw/src/xdg-decoration-unstable-v1-client-protocol.h
+++ b/raylib/external/glfw/src/xdg-decoration-unstable-v1-client-protocol.h
@@ -1,4 +1,4 @@
-/* Generated by wayland-scanner 1.22.0 */
+/* Generated by wayland-scanner 1.23.1 */
 
 #ifndef XDG_DECORATION_UNSTABLE_V1_CLIENT_PROTOCOL_H
 #define XDG_DECORATION_UNSTABLE_V1_CLIENT_PROTOCOL_H
@@ -215,6 +215,10 @@ enum zxdg_toplevel_decoration_v1_error {
 	 * xdg_toplevel destroyed before the decoration object
 	 */
 	ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ORPHANED = 2,
+	/**
+	 * invalid mode
+	 */
+	ZXDG_TOPLEVEL_DECORATION_V1_ERROR_INVALID_MODE = 3,
 };
 #endif /* ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ENUM */
 
@@ -347,6 +351,9 @@ zxdg_toplevel_decoration_v1_destroy(struct zxdg_toplevel_decoration_v1 *zxdg_top
  * 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.
  */
 static inline void
 zxdg_toplevel_decoration_v1_set_mode(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, uint32_t mode)
diff --git a/raylib/external/glfw/src/xdg-shell-client-protocol-code.h b/raylib/external/glfw/src/xdg-shell-client-protocol-code.h
index 03826cd..d698c2c 100644
--- a/raylib/external/glfw/src/xdg-shell-client-protocol-code.h
+++ b/raylib/external/glfw/src/xdg-shell-client-protocol-code.h
@@ -1,4 +1,4 @@
-/* Generated by wayland-scanner 1.22.0 */
+/* Generated by wayland-scanner 1.23.1 */
 
 /*
  * Copyright © 2008-2013 Kristian Høgsberg
@@ -28,6 +28,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
+#include 
 #include 
 #include 
 #include "wayland-util.h"
diff --git a/raylib/external/glfw/src/xdg-shell-client-protocol.h b/raylib/external/glfw/src/xdg-shell-client-protocol.h
index 8b24f09..1f077b4 100644
--- a/raylib/external/glfw/src/xdg-shell-client-protocol.h
+++ b/raylib/external/glfw/src/xdg-shell-client-protocol.h
@@ -1,4 +1,4 @@
-/* Generated by wayland-scanner 1.22.0 */
+/* Generated by wayland-scanner 1.23.1 */
 
 #ifndef XDG_SHELL_CLIENT_PROTOCOL_H
 #define XDG_SHELL_CLIENT_PROTOCOL_H
@@ -167,7 +167,8 @@ extern const struct wl_interface xdg_positioner_interface;
  * 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, the client must
+ * 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
@@ -220,7 +221,8 @@ extern const struct wl_interface xdg_positioner_interface;
  * 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, the client must
+ * 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
@@ -267,7 +269,7 @@ extern const struct wl_interface xdg_surface_interface;
  * 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 perfoming a commit without any buffer
+ * 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).
  *
@@ -294,7 +296,7 @@ extern const struct wl_interface xdg_surface_interface;
  * 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 perfoming a commit without any buffer
+ * 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).
  *
@@ -1156,8 +1158,7 @@ xdg_surface_get_popup(struct xdg_surface *xdg_surface, struct xdg_surface *paren
  * portions like drop-shadows which should be ignored for the
  * purposes of aligning, placing and constraining windows.
  *
- * The window geometry is double buffered, and will be applied at the
- * time wl_surface.commit of the corresponding wl_surface is called.
+ * 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.
@@ -1291,8 +1292,7 @@ enum xdg_toplevel_resize_edge {
  * 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. They will get applied on
- * the next commit.
+ * States set in this way are double-buffered, see wl_surface.commit.
  */
 enum xdg_toplevel_state {
 	/**
@@ -1342,6 +1342,9 @@ enum xdg_toplevel_state {
 	 *
 	 * 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.
 	 * @since 2
 	 */
 	XDG_TOPLEVEL_STATE_TILED_LEFT = 5,
@@ -1350,6 +1353,9 @@ enum xdg_toplevel_state {
 	 *
 	 * 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.
 	 * @since 2
 	 */
 	XDG_TOPLEVEL_STATE_TILED_RIGHT = 6,
@@ -1358,6 +1364,9 @@ enum xdg_toplevel_state {
 	 *
 	 * 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.
 	 * @since 2
 	 */
 	XDG_TOPLEVEL_STATE_TILED_TOP = 7,
@@ -1366,6 +1375,9 @@ enum xdg_toplevel_state {
 	 *
 	 * 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.
 	 * @since 2
 	 */
 	XDG_TOPLEVEL_STATE_TILED_BOTTOM = 8,
@@ -1848,8 +1860,7 @@ xdg_toplevel_resize(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uin
  * The width and height arguments are in window geometry coordinates.
  * See xdg_surface.set_window_geometry.
  *
- * Values set in this way are double-buffered. They will get applied
- * on the next commit.
+ * 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
@@ -1893,8 +1904,7 @@ xdg_toplevel_set_max_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int3
  * The width and height arguments are in window geometry coordinates.
  * See xdg_surface.set_window_geometry.
  *
- * Values set in this way are double-buffered. They will get applied
- * on the next commit.
+ * 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
diff --git a/raylib/external/miniaudio.h b/raylib/external/miniaudio.h
index ad11333..c74bebe 100644
--- a/raylib/external/miniaudio.h
+++ b/raylib/external/miniaudio.h
@@ -1,6 +1,6 @@
 /*
 Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
-miniaudio - v0.11.21 - 2023-11-15
+miniaudio - v0.11.22 - 2025-02-24
 
 David Reid - mackron@gmail.com
 
@@ -12,15 +12,18 @@ GitHub:        https://github.com/mackron/miniaudio
 /*
 1. Introduction
 ===============
-miniaudio is a single file library for audio playback and capture. To use it, do the following in
-one .c file:
+To use miniaudio, include "miniaudio.h":
 
     ```c
-    #define MINIAUDIO_IMPLEMENTATION
     #include "miniaudio.h"
     ```
 
-You can do `#include "miniaudio.h"` in other parts of the program just like any other header.
+The implementation is contained in "miniaudio.c". Just compile this like any other source file. You
+can include miniaudio.c if you want to compile your project as a single translation unit:
+
+    ```c
+    #include "miniaudio.c"
+    ```
 
 miniaudio includes both low level and high level APIs. The low level API is good for those who want
 to do all of their mixing themselves and only require a light weight interface to the underlying
@@ -293,7 +296,7 @@ avoids the same sound being loaded multiple times.
 
 The node graph is used for mixing and effect processing. The idea is that you connect a number of
 nodes into the graph by connecting each node's outputs to another node's inputs. Each node can
-implement it's own effect. By chaining nodes together, advanced mixing and effect processing can
+implement its own effect. By chaining nodes together, advanced mixing and effect processing can
 be achieved.
 
 The engine encapsulates both the resource manager and the node graph to create a simple, easy to
@@ -398,7 +401,7 @@ the be started and/or stopped at a specific time. This can be done with the foll
     ```
 
 The start/stop time needs to be specified based on the absolute timer which is controlled by the
-engine. The current global time time in PCM frames can be retrieved with
+engine. The current global time in PCM frames can be retrieved with
 `ma_engine_get_time_in_pcm_frames()`. The engine's global time can be changed with
 `ma_engine_set_time_in_pcm_frames()` for synchronization purposes if required. Note that scheduling
 a start time still requires an explicit call to `ma_sound_start()` before anything will play:
@@ -430,11 +433,11 @@ Sounds and sound groups are nodes in the engine's node graph and can be plugged
 API. This makes it possible to connect sounds and sound groups to effect nodes to produce complex
 effect chains.
 
-A sound can have it's volume changed with `ma_sound_set_volume()`. If you prefer decibel volume
+A sound can have its volume changed with `ma_sound_set_volume()`. If you prefer decibel volume
 control you can use `ma_volume_db_to_linear()` to convert from decibel representation to linear.
 
 Panning and pitching is supported with `ma_sound_set_pan()` and `ma_sound_set_pitch()`. If you know
-a sound will never have it's pitch changed with `ma_sound_set_pitch()` or via the doppler effect,
+a sound will never have its pitch changed with `ma_sound_set_pitch()` or via the doppler effect,
 you can specify the `MA_SOUND_FLAG_NO_PITCH` flag when initializing the sound for an optimization.
 
 By default, sounds and sound groups have spatialization enabled. If you don't ever want to
@@ -483,21 +486,12 @@ link the relevant frameworks but should compile cleanly out of the box with Xcod
 through the command line requires linking to `-lpthread` and `-lm`.
 
 Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's
-notarization process. To fix this there are two options. The first is to use the
-`MA_NO_RUNTIME_LINKING` option, like so:
-
-    ```c
-    #ifdef __APPLE__
-        #define MA_NO_RUNTIME_LINKING
-    #endif
-    #define MINIAUDIO_IMPLEMENTATION
-    #include "miniaudio.h"
-    ```
-
-This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioToolbox`.
-If you get errors about AudioToolbox, try with `-framework AudioUnit` instead. You may get this when
-using older versions of iOS. Alternatively, if you would rather keep using runtime linking you can
-add the following to your entitlements.xcent file:
+notarization process. To fix this there are two options. The first is to compile with
+`-DMA_NO_RUNTIME_LINKING` which in turn will require linking with
+`-framework CoreFoundation -framework CoreAudio -framework AudioToolbox`. If you get errors about
+AudioToolbox, try with `-framework AudioUnit` instead. You may get this when using older versions
+of iOS. Alternatively, if you would rather keep using runtime linking you can add the following to
+your entitlements.xcent file:
 
     ```
     com.apple.security.cs.allow-dyld-environment-variables
@@ -555,7 +549,7 @@ To run locally, you'll need to use emrun:
 
 2.7. Build Options
 ------------------
-`#define` these options before including miniaudio.h.
+`#define` these options before including miniaudio.c, or pass them as compiler flags:
 
     +----------------------------------+--------------------------------------------------------------------+
     | Option                           | Description                                                        |
@@ -586,6 +580,8 @@ To run locally, you'll need to use emrun:
     +----------------------------------+--------------------------------------------------------------------+
     | MA_NO_WEBAUDIO                   | Disables the Web Audio backend.                                    |
     +----------------------------------+--------------------------------------------------------------------+
+    | MA_NO_CUSTOM                     | Disables support for custom backends.                              |
+    +----------------------------------+--------------------------------------------------------------------+
     | MA_NO_NULL                       | Disables the null backend.                                         |
     +----------------------------------+--------------------------------------------------------------------+
     | MA_ENABLE_ONLY_SPECIFIC_BACKENDS | Disables all backends by default and requires `MA_ENABLE_*` to     |
@@ -630,6 +626,9 @@ To run locally, you'll need to use emrun:
     | MA_ENABLE_WEBAUDIO               | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to       |
     |                                  | enable the Web Audio backend.                                      |
     +----------------------------------+--------------------------------------------------------------------+
+    | MA_ENABLE_CUSTOM                 | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to       |
+    |                                  | enable custom backends.                                            |
+    +----------------------------------+--------------------------------------------------------------------+
     | MA_ENABLE_NULL                   | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to       |
     |                                  | enable the null backend.                                           |
     +----------------------------------+--------------------------------------------------------------------+
@@ -693,11 +692,30 @@ To run locally, you'll need to use emrun:
     |                                  | You may need to enable this if your target platform does not allow |
     |                                  | runtime linking via `dlopen()`.                                    |
     +----------------------------------+--------------------------------------------------------------------+
+    | MA_USE_STDINT                    | (Pass this in as a compiler flag. Do not `#define` this before     |
+    |                                  | miniaudio.c) Forces the use of stdint.h for sized types.           |
+    +----------------------------------+--------------------------------------------------------------------+
     | MA_DEBUG_OUTPUT                  | Enable `printf()` output of debug logs (`MA_LOG_LEVEL_DEBUG`).     |
     +----------------------------------+--------------------------------------------------------------------+
     | MA_COINIT_VALUE                  | Windows only. The value to pass to internal calls to               |
     |                                  | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`.            |
     +----------------------------------+--------------------------------------------------------------------+
+    | MA_FORCE_UWP                     | Windows only. Affects only the WASAPI backend. Will force the      |
+    |                                  | WASAPI backend to use the UWP code path instead of the regular     |
+    |                                  | desktop path. This is normally auto-detected and should rarely be  |
+    |                                  | needed to be used explicitly, but can be useful for debugging.     |
+    +----------------------------------+--------------------------------------------------------------------+
+    | MA_ON_THREAD_ENTRY               | Defines some code that will be executed as soon as an internal     |
+    |                                  | miniaudio-managed thread is created. This will be the first thing  |
+    |                                  | to be executed by the thread entry point.                          |
+    +----------------------------------+--------------------------------------------------------------------+
+    | MA_ON_THREAD_EXIT                | Defines some code that will be executed from the entry point of an |
+    |                                  | internal miniaudio-managed thread upon exit. This will be the last |
+    |                                  | thing to be executed before the thread's entry point exits.        |
+    +----------------------------------+--------------------------------------------------------------------+
+    | MA_THREAD_DEFAULT_STACK_SIZE     | If set, specifies the default stack size used by miniaudio-managed |
+    |                                  | threads.                                                           |
+    +----------------------------------+--------------------------------------------------------------------+
     | MA_API                           | Controls how public APIs should be decorated. Default is `extern`. |
     +----------------------------------+--------------------------------------------------------------------+
 
@@ -1309,7 +1327,7 @@ only works for sounds that were initialized with `ma_sound_init_from_file()` and
 
 When you initialize a sound, if you specify a sound group the sound will be attached to that group
 automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint.
-If you would instead rather leave the sound unattached by default, you can can specify the
+If you would instead rather leave the sound unattached by default, you can specify the
 `MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT` flag. This is useful if you want to set up a complex node
 graph.
 
@@ -1686,6 +1704,7 @@ combination of the following flags:
     MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE
     MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC
     MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT
+    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING
     ```
 
 When no flags are specified (set to 0), the sound will be fully loaded into memory, but not
@@ -1706,6 +1725,14 @@ can instead stream audio data which you can do by specifying the
 second pages. When a new page needs to be decoded, a job will be posted to the job queue and then
 subsequently processed in a job thread.
 
+The `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flag can be used so that the sound will loop
+when it reaches the end by default. It's recommended you use this flag when you want to have a
+looping streaming sound. If you try loading a very short sound as a stream, you will get a glitch.
+This is because the resource manager needs to pre-fill the initial buffer at initialization time,
+and if you don't specify the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flag, the resource
+manager will assume the sound is not looping and will stop filling the buffer when it reaches the
+end, therefore resulting in a discontinuous buffer.
+
 For in-memory sounds, reference counting is used to ensure the data is loaded only once. This means
 multiple calls to `ma_resource_manager_data_source_init()` with the same file path will result in
 the file data only being loaded once. Each call to `ma_resource_manager_data_source_init()` must be
@@ -1720,7 +1747,7 @@ actual file paths. When `ma_resource_manager_data_source_init()` is called (with
 `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag), the resource manager will look for these
 explicitly registered data buffers and, if found, will use it as the backing data for the data
 source. Note that the resource manager does *not* make a copy of this data so it is up to the
-caller to ensure the pointer stays valid for it's lifetime. Use
+caller to ensure the pointer stays valid for its lifetime. Use
 `ma_resource_manager_unregister_data()` to unregister the self-managed data. You can also use
 `ma_resource_manager_register_file()` and `ma_resource_manager_unregister_file()` to register and
 unregister a file. It does not make sense to use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM`
@@ -2031,7 +2058,7 @@ In the above graph, it starts with two data sources whose outputs are attached t
 splitter node. It's at this point that the two data sources are mixed. After mixing, the splitter
 performs it's processing routine and produces two outputs which is simply a duplication of the
 input stream. One output is attached to a low pass filter, whereas the other output is attached to
-a echo/delay. The outputs of the the low pass filter and the echo are attached to the endpoint, and
+a echo/delay. The outputs of the low pass filter and the echo are attached to the endpoint, and
 since they're both connected to the same input bus, they'll be mixed.
 
 Each input bus must be configured to accept the same number of channels, but the number of channels
@@ -2072,7 +2099,7 @@ data from the graph:
     ```
 
 When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in
-data from it's input attachments, which in turn recursively pull in data from their inputs, and so
+data from its input attachments, which in turn recursively pull in data from their inputs, and so
 on. At the start of the graph there will be some kind of data source node which will have zero
 inputs and will instead read directly from a data source. The base nodes don't literally need to
 read from a `ma_data_source` object, but they will always have some kind of underlying object that
@@ -2318,7 +2345,7 @@ You can start and stop a node with the following:
 
 By default the node is in a started state, but since it won't be connected to anything won't
 actually be invoked by the node graph until it's connected. When you stop a node, data will not be
-read from any of it's input connections. You can use this property to stop a group of sounds
+read from any of its input connections. You can use this property to stop a group of sounds
 atomically.
 
 You can configure the initial state of a node in it's config:
@@ -2411,29 +2438,29 @@ audio thread is finished so that control is not handed back to the caller thereb
 chance to free the node's memory.
 
 When the audio thread is processing a node, it does so by reading from each of the output buses of
-the node. In order for a node to process data for one of it's output buses, it needs to read from
-each of it's input buses, and so on an so forth. It follows that once all output buses of a node
+the node. In order for a node to process data for one of its output buses, it needs to read from
+each of its input buses, and so on an so forth. It follows that once all output buses of a node
 are detached, the node as a whole will be disconnected and no further processing will occur unless
 it's output buses are reattached, which won't be happening when the node is being uninitialized.
 By having `ma_node_detach_output_bus()` wait until the audio thread is finished with it, we can
 simplify a few things, at the expense of making `ma_node_detach_output_bus()` a bit slower. By
 doing this, the implementation of `ma_node_uninit()` becomes trivial - just detach all output
-nodes, followed by each of the attachments to each of it's input nodes, and then do any final clean
+nodes, followed by each of the attachments to each of its input nodes, and then do any final clean
 up.
 
 With the above design, the worst-case scenario is `ma_node_detach_output_bus()` taking as long as
 it takes to process the output bus being detached. This will happen if it's called at just the
 wrong moment where the audio thread has just iterated it and has just started processing. The
 caller of `ma_node_detach_output_bus()` will stall until the audio thread is finished, which
-includes the cost of recursively processing it's inputs. This is the biggest compromise made with
-the approach taken by miniaudio for it's lock-free processing system. The cost of detaching nodes
+includes the cost of recursively processing its inputs. This is the biggest compromise made with
+the approach taken by miniaudio for its lock-free processing system. The cost of detaching nodes
 earlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching
 higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass
 detachments, detach starting from the lowest level nodes and work your way towards the final
 endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not
 running, detachment will be fast and detachment in any order will be the same. The reason nodes
 need to wait for their input attachments to complete is due to the potential for desyncs between
-data sources. If the node was to terminate processing mid way through processing it's inputs,
+data sources. If the node was to terminate processing mid way through processing its inputs,
 there's a chance that some of the underlying data sources will have been read, but then others not.
 That will then result in a potential desynchronization when detaching and reattaching higher-level
 nodes. A possible solution to this is to have an option when detaching to terminate processing
@@ -2804,7 +2831,7 @@ weights. Custom weights can be passed in as the last parameter of
 `ma_channel_converter_config_init()`.
 
 Predefined channel maps can be retrieved with `ma_channel_map_init_standard()`. This takes a
-`ma_standard_channel_map` enum as it's first parameter, which can be one of the following:
+`ma_standard_channel_map` enum as its first parameter, which can be one of the following:
 
     +-----------------------------------+-----------------------------------------------------------+
     | Name                              | Description                                               |
@@ -2890,7 +2917,7 @@ like the following:
         ma_resample_algorithm_linear);
 
     ma_resampler resampler;
-    ma_result result = ma_resampler_init(&config, &resampler);
+    ma_result result = ma_resampler_init(&config, NULL, &resampler);
     if (result != MA_SUCCESS) {
         // An error occurred...
     }
@@ -3132,7 +3159,7 @@ Biquad filtering is achieved with the `ma_biquad` API. Example:
 
     ```c
     ma_biquad_config config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2);
-    ma_result result = ma_biquad_init(&config, &biquad);
+    ma_result result = ma_biquad_init(&config, NULL, &biquad);
     if (result != MA_SUCCESS) {
         // Error.
     }
@@ -3723,7 +3750,7 @@ extern "C" {
 
 #define MA_VERSION_MAJOR    0
 #define MA_VERSION_MINOR    11
-#define MA_VERSION_REVISION 21
+#define MA_VERSION_REVISION 22
 #define MA_VERSION_STRING   MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION)
 
 #if defined(_MSC_VER) && !defined(__clang__)
@@ -3740,8 +3767,7 @@ extern "C" {
 #endif
 
 
-
-#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__)
+#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) || defined(__ppc64__)
     #define MA_SIZEOF_PTR   8
 #else
     #define MA_SIZEOF_PTR   4
@@ -3805,7 +3831,7 @@ typedef void* ma_handle;
 typedef void* ma_ptr;
 
 /*
-ma_proc is annoying because when compiling with GCC we get pendantic warnings about converting
+ma_proc is annoying because when compiling with GCC we get pedantic warnings about converting
 between `void*` and `void (*)()`. We can't use `void (*)()` with MSVC however, because we'll get
 warning C4191 about "type cast between incompatible function types". To work around this I'm going
 to use a different data type depending on the compiler.
@@ -3999,7 +4025,7 @@ Special wchar_t type to ensure any structures in the public sections that refere
 consistent size across all platforms.
 
 On Windows, wchar_t is 2 bytes, whereas everywhere else it's 4 bytes. Since Windows likes to use
-wchar_t for it's IDs, we need a special explicitly sized wchar type that is always 2 bytes on all
+wchar_t for its IDs, we need a special explicitly sized wchar type that is always 2 bytes on all
 platforms.
 */
 #if !defined(MA_POSIX) && defined(MA_WIN32)
@@ -4025,7 +4051,7 @@ MA_LOG_LEVEL_INFO
     callback.
 
 MA_LOG_LEVEL_WARNING
-    Warnings. You should enable this in you development builds and action them when encounted. These
+    Warnings. You should enable this in you development builds and action them when encountered. These
     logs usually indicate a potential problem or misconfiguration, but still allow you to keep
     running. This will never be called from within the data callback.
 
@@ -5457,7 +5483,7 @@ input frames.
 MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);
 
 /*
-Resets the resampler's timer and clears it's internal cache.
+Resets the resampler's timer and clears its internal cache.
 */
 MA_API ma_result ma_resampler_reset(ma_resampler* pResampler);
 
@@ -5678,7 +5704,7 @@ MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannel
 /*
 Copies a channel map.
 
-Both input and output channel map buffers must have a capacity of at at least `channels`.
+Both input and output channel map buffers must have a capacity of at least `channels`.
 */
 MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels);
 
@@ -5817,6 +5843,8 @@ MA_API void ma_data_source_uninit(ma_data_source* pDataSource);
 MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);   /* Must support pFramesOut = NULL in which case a forward seek should be performed. */
 MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */
 MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex);
+MA_API ma_result ma_data_source_seek_seconds(ma_data_source* pDataSource, float secondCount, float* pSecondsSeeked); /* Can only seek forward. Abstraction to ma_data_source_seek_pcm_frames() */
+MA_API ma_result ma_data_source_seek_to_second(ma_data_source* pDataSource, float seekPointInSeconds); /* Abstraction to ma_data_source_seek_to_pcm_frame() */
 MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
 MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor);
 MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength);    /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */
@@ -6182,6 +6210,12 @@ MA_API ma_result ma_event_wait(ma_event* pEvent);
 Signals the specified auto-reset event.
 */
 MA_API ma_result ma_event_signal(ma_event* pEvent);
+
+
+MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore);
+MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore);
+MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore);
+MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore);
 #endif  /* MA_NO_THREADING */
 
 
@@ -6273,7 +6307,7 @@ Job Queue
 /*
 Slot Allocator
 --------------
-The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocator an index that can be used
+The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocate an index that can be used
 as the insertion point for an object.
 
 Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs.
@@ -7006,6 +7040,8 @@ typedef union
     int nullbackend;                /* The null backend uses an integer for device IDs. */
 } ma_device_id;
 
+MA_API ma_bool32 ma_device_id_equal(const ma_device_id* pA, const ma_device_id* pB);
+
 
 typedef struct ma_context_config    ma_context_config;
 typedef struct ma_device_config     ma_device_config;
@@ -7093,6 +7129,7 @@ struct ma_device_config
     {
         const char* pStreamNamePlayback;
         const char* pStreamNameCapture;
+        int channelMap;
     } pulse;
     struct
     {
@@ -7112,6 +7149,7 @@ struct ma_device_config
         ma_aaudio_allowed_capture_policy allowedCapturePolicy;
         ma_bool32 noAutoStartAfterReroute;
         ma_bool32 enableCompatibilityWorkarounds;
+        ma_bool32 allowSetBufferCapacity;
     } aaudio;
 };
 
@@ -7184,7 +7222,7 @@ and on output returns detailed information about the device in `ma_device_info`.
 case when the device ID is NULL, in which case information about the default device needs to be retrieved.
 
 Once the context has been created and the device ID retrieved (if using anything other than the default device), the device can be created.
-This is a little bit more complicated than initialization of the context due to it's more complicated configuration. When initializing a
+This is a little bit more complicated than initialization of the context due to its more complicated configuration. When initializing a
 device, a duplex device may be requested. This means a separate data format needs to be specified for both playback and capture. On input,
 the data format is set to what the application wants. On output it's set to the native format which should match as closely as possible to
 the requested format. The conversion between the format requested by the application and the device's native format will be handled
@@ -7205,10 +7243,10 @@ asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should
 The handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit
 easier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and
 `onDeviceWrite()` callbacks can optionally be implemented. These are blocking and work just like reading and writing from a file. If the
-backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within it's callback.
+backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within its callback.
 This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback.
 
-If the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceDataLoop()` callback
+If the backend requires absolute flexibility with its data delivery, it can optionally implement the `onDeviceDataLoop()` callback
 which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional.
 
 The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been
@@ -7248,6 +7286,10 @@ struct ma_context_config
     void* pUserData;
     ma_allocation_callbacks allocationCallbacks;
     struct
+    {
+        ma_handle hWnd; /* HWND. Optional window handle to pass into SetCooperativeLevel(). Will default to the foreground window, and if that fails, the desktop window. */
+    } dsound;
+    struct
     {
         ma_bool32 useVerboseDeviceEnumeration;
     } alsa;
@@ -7336,6 +7378,7 @@ struct ma_context
 #ifdef MA_SUPPORT_DSOUND
         struct
         {
+            ma_handle hWnd; /* Can be null. */
             ma_handle hDSoundDLL;
             ma_proc DirectSoundCreate;
             ma_proc DirectSoundEnumerateA;
@@ -7942,6 +7985,7 @@ struct ma_device
         {
             /*AAudioStream**/ ma_ptr pStreamPlayback;
             /*AAudioStream**/ ma_ptr pStreamCapture;
+            ma_mutex rerouteLock;
             ma_aaudio_usage usage;
             ma_aaudio_content_type contentType;
             ma_aaudio_input_preset inputPreset;
@@ -8365,6 +8409,10 @@ Retrieves basic information about every active playback and/or capture device.
 This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos`
 parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback.
 
+Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require
+opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this,
+but don't call it from within the enumeration callback.
+
 
 Parameters
 ----------
@@ -8406,7 +8454,7 @@ The returned pointers will become invalid upon the next call this this function,
 
 See Also
 --------
-ma_context_get_devices()
+ma_context_enumerate_devices()
 */
 MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount);
 
@@ -8545,7 +8593,7 @@ from a microphone. Whether or not you should send or receive data from the devic
 playback, capture, full-duplex or loopback. (Note that loopback mode is only supported on select backends.) Sending and receiving audio data to and from the
 device is done via a callback which is fired by miniaudio at periodic time intervals.
 
-The frequency at which data is delivered to and from a device depends on the size of it's period. The size of the period can be defined in terms of PCM frames
+The frequency at which data is delivered to and from a device depends on the size of its period. The size of the period can be defined in terms of PCM frames
 or milliseconds, whichever is more convenient. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and
 increased risk of glitching due to the more frequent and granular data deliver intervals. The size of a period will depend on your requirements, but
 miniaudio's defaults should work fine for most scenarios. If you're building a game you should leave this fairly small, whereas if you're building a simple
@@ -8619,7 +8667,7 @@ then be set directly on the structure. Below are the members of the `ma_device_c
 
     performanceProfile
         A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or
-        `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at it's default value.
+        `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at its default value.
 
     noPreSilencedOutputBuffer
         When set to true, the contents of the output buffer passed into the data callback will be left undefined. When set to false (default), the contents of
@@ -8659,7 +8707,7 @@ then be set directly on the structure. Below are the members of the `ma_device_c
         A pointer that will passed to callbacks in pBackendVTable.
 
     resampling.linear.lpfOrder
-        The linear resampler applies a low-pass filter as part of it's processing for anti-aliasing. This setting controls the order of the filter. The higher
+        The linear resampler applies a low-pass filter as part of its processing for anti-aliasing. This setting controls the order of the filter. The higher
         the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is
         `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`.
 
@@ -8736,6 +8784,9 @@ then be set directly on the structure. Below are the members of the `ma_device_c
     pulse.pStreamNameCapture
         PulseAudio only. Sets the stream name for capture.
 
+    pulse.channelMap
+        PulseAudio only. Sets the channel map that is requested from PulseAudio. See MA_PA_CHANNEL_MAP_* constants. Defaults to MA_PA_CHANNEL_MAP_AIFF.
+
     coreaudio.allowNominalSampleRateChange
         Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This
         is disabled by default in order to prevent intrusive changes to the user's system. This is useful if you want to use a sample rate
@@ -8909,7 +8960,7 @@ Unsafe. It is not safe to call this inside any callback.
 
 Remarks
 -------
-You only need to use this function if you want to configure the context differently to it's defaults. You should never use this function if you want to manage
+You only need to use this function if you want to configure the context differently to its defaults. You should never use this function if you want to manage
 your own context.
 
 See the documentation for `ma_context_init()` for information on the different context configuration options.
@@ -9674,7 +9725,7 @@ Utilities
 ************************************************************************************************************************************************************/
 
 /*
-Calculates a buffer size in milliseconds from the specified number of frames and sample rate.
+Calculates a buffer size in milliseconds (rounded up) from the specified number of frames and sample rate.
 */
 MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate);
 
@@ -9931,7 +9982,7 @@ struct ma_decoder
     void* pInputCache;              /* In input format. Can be null if it's not needed. */
     ma_uint64 inputCacheCap;        /* The capacity of the input cache. */
     ma_uint64 inputCacheConsumed;   /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */
-    ma_uint64 inputCacheRemaining;  /* The number of valid frames remaining in the cahce. */
+    ma_uint64 inputCacheRemaining;  /* The number of valid frames remaining in the cache. */
     ma_allocation_callbacks allocationCallbacks;
     union
     {
@@ -9972,7 +10023,7 @@ This is not thread safe without your own synchronization.
 MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
 
 /*
-Seeks to a PCM frame based on it's absolute index.
+Seeks to a PCM frame based on its absolute index.
 
 This is not thread safe without your own synchronization.
 */
@@ -10235,7 +10286,8 @@ typedef enum
     MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE         = 0x00000002,   /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage. */
     MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC          = 0x00000004,   /* When set, the resource manager will load the data source asynchronously. */
     MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT      = 0x00000008,   /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */
-    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010    /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */
+    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010,   /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */
+    MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING        = 0x00000020    /* When set, configures the data source to loop by default. */
 } ma_resource_manager_data_source_flags;
 
 
@@ -10303,8 +10355,8 @@ typedef struct
     ma_uint64 rangeEndInPCMFrames;
     ma_uint64 loopPointBegInPCMFrames;
     ma_uint64 loopPointEndInPCMFrames;
-    ma_bool32 isLooping;
     ma_uint32 flags;
+    ma_bool32 isLooping;    /* Deprecated. Use the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING flag in `flags` instead. */
 } ma_resource_manager_data_source_config;
 
 MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void);
@@ -10547,6 +10599,16 @@ Node Graph
 /* Use this when the bus count is determined by the node instance rather than the vtable. */
 #define MA_NODE_BUS_COUNT_UNKNOWN   255
 
+
+/* For some internal memory management of ma_node_graph. */
+typedef struct
+{
+    size_t offset;
+    size_t sizeInBytes;
+    unsigned char _data[1];
+} ma_stack;
+
+
 typedef struct ma_node_graph ma_node_graph;
 typedef void ma_node;
 
@@ -10586,7 +10648,7 @@ typedef struct
     void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut);
 
     /*
-    A callback for retrieving the number of a input frames that are required to output the
+    A callback for retrieving the number of input frames that are required to output the
     specified number of output frames. You would only want to implement this when the node performs
     resampling. This is optional, even for nodes that perform resampling, but it does offer a
     small reduction in latency as it allows miniaudio to calculate the exact number of input frames
@@ -10671,10 +10733,14 @@ typedef struct ma_node_base ma_node_base;
 struct ma_node_base
 {
     /* These variables are set once at startup. */
-    ma_node_graph* pNodeGraph;              /* The graph this node belongs to. */
+    ma_node_graph* pNodeGraph;                  /* The graph this node belongs to. */
     const ma_node_vtable* vtable;
-    float* pCachedData;                     /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */
-    ma_uint16 cachedDataCapInFramesPerBus;  /* The capacity of the input data cache in frames, per bus. */
+    ma_uint32 inputBusCount;
+    ma_uint32 outputBusCount;
+    ma_node_input_bus* pInputBuses;
+    ma_node_output_bus* pOutputBuses;
+    float* pCachedData;                         /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */
+    ma_uint16 cachedDataCapInFramesPerBus;      /* The capacity of the input data cache in frames, per bus. */
 
     /* These variables are read and written only from the audio thread. */
     ma_uint16 cachedFrameCountOut;
@@ -10682,13 +10748,9 @@ struct ma_node_base
     ma_uint16 consumedFrameCountIn;
 
     /* These variables are read and written between different threads. */
-    MA_ATOMIC(4, ma_node_state) state;      /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */
-    MA_ATOMIC(8, ma_uint64) stateTimes[2];  /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */
-    MA_ATOMIC(8, ma_uint64) localTime;      /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */
-    ma_uint32 inputBusCount;
-    ma_uint32 outputBusCount;
-    ma_node_input_bus* pInputBuses;
-    ma_node_output_bus* pOutputBuses;
+    MA_ATOMIC(4, ma_node_state) state;          /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */
+    MA_ATOMIC(8, ma_uint64) stateTimes[2];      /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */
+    MA_ATOMIC(8, ma_uint64) localTime;          /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */
 
     /* Memory management. */
     ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT];
@@ -10724,7 +10786,8 @@ MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime);
 typedef struct
 {
     ma_uint32 channels;
-    ma_uint16 nodeCacheCapInFrames;
+    ma_uint32 processingSizeInFrames;   /* This is the preferred processing size for node processing callbacks unless overridden by a node itself. Can be 0 in which case it will be based on the frame count passed into ma_node_graph_read_pcm_frames(), but will not be well defined. */
+    size_t preMixStackSizeInBytes;      /* Defaults to 512KB per channel. Reducing this will save memory, but the depth of your node graph will be more restricted. */
 } ma_node_graph_config;
 
 MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels);
@@ -10735,10 +10798,15 @@ struct ma_node_graph
     /* Immutable. */
     ma_node_base base;                  /* The node graph itself is a node so it can be connected as an input to different node graph. This has zero inputs and calls ma_node_graph_read_pcm_frames() to generate it's output. */
     ma_node_base endpoint;              /* Special node that all nodes eventually connect to. Data is read from this node in ma_node_graph_read_pcm_frames(). */
-    ma_uint16 nodeCacheCapInFrames;
+    float* pProcessingCache;            /* This will be allocated when processingSizeInFrames is non-zero. This is needed because ma_node_graph_read_pcm_frames() can be called with a variable number of frames, and we may need to do some buffering in situations where the caller requests a frame count that's not a multiple of processingSizeInFrames. */
+    ma_uint32 processingCacheFramesRemaining;
+    ma_uint32 processingSizeInFrames;
 
     /* Read and written by multiple threads. */
     MA_ATOMIC(4, ma_bool32) isReading;
+
+    /* Modified only by the audio thread. */
+    ma_stack* pPreMixStack;
 };
 
 MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph);
@@ -11023,6 +11091,7 @@ typedef enum
     MA_SOUND_FLAG_ASYNC                 = 0x00000004,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */
     MA_SOUND_FLAG_WAIT_INIT             = 0x00000008,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */
     MA_SOUND_FLAG_UNKNOWN_LENGTH        = 0x00000010,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH */
+    MA_SOUND_FLAG_LOOPING               = 0x00000020,   /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING */
 
     /* ma_sound specific flags. */
     MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00001000,   /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */
@@ -11062,7 +11131,7 @@ MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_e
 /* Base node object for both ma_sound and ma_sound_group. */
 typedef struct
 {
-    ma_node_base baseNode;                              /* Must be the first member for compatiblity with the ma_node API. */
+    ma_node_base baseNode;                              /* Must be the first member for compatibility with the ma_node API. */
     ma_engine* pEngine;                                 /* A pointer to the engine. Set based on the value from the config. */
     ma_uint32 sampleRate;                               /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */
     ma_uint32 volumeSmoothTimeInPCMFrames;
@@ -11122,13 +11191,13 @@ typedef struct
     ma_uint64 rangeEndInPCMFrames;
     ma_uint64 loopPointBegInPCMFrames;
     ma_uint64 loopPointEndInPCMFrames;
-    ma_bool32 isLooping;
     ma_sound_end_proc endCallback;              /* Fired when the sound reaches the end. Will be fired from the audio thread. Do not restart, uninitialize or otherwise change the state of the sound from here. Instead fire an event or set a variable to indicate to a different thread to change the start of the sound. Will not be fired in response to a scheduled stop with ma_sound_set_stop_time_*(). */
     void* pEndCallbackUserData;
 #ifndef MA_NO_RESOURCE_MANAGER
     ma_resource_manager_pipeline_notifications initNotifications;
 #endif
     ma_fence* pDoneFence;                       /* Deprecated. Use initNotifications instead. Released when the resource manager has finished decoding the entire sound. Not used with streams. */
+    ma_bool32 isLooping;                        /* Deprecated. Use the MA_SOUND_FLAG_LOOPING flag in `flags` instead. */
 } ma_sound_config;
 
 MA_API ma_sound_config ma_sound_config_init(void);                  /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */
@@ -11192,6 +11261,7 @@ typedef struct
     ma_uint32 gainSmoothTimeInFrames;               /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */
     ma_uint32 gainSmoothTimeInMilliseconds;         /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */
     ma_uint32 defaultVolumeSmoothTimeInPCMFrames;   /* Defaults to 0. Controls the default amount of smoothing to apply to volume changes to sounds. High values means more smoothing at the expense of high latency (will take longer to reach the new volume). */
+    ma_uint32 preMixStackSizeInBytes;               /* A stack is used for internal processing in the node graph. This allows you to configure the size of this stack. Smaller values will reduce the maximum depth of your node graph. You should rarely need to modify this. */
     ma_allocation_callbacks allocationCallbacks;
     ma_bool32 noAutoStart;                          /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */
     ma_bool32 noDevice;                             /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */
@@ -11206,12 +11276,12 @@ MA_API ma_engine_config ma_engine_config_init(void);
 
 struct ma_engine
 {
-    ma_node_graph nodeGraph;                /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */
+    ma_node_graph nodeGraph;                        /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */
 #if !defined(MA_NO_RESOURCE_MANAGER)
     ma_resource_manager* pResourceManager;
 #endif
 #if !defined(MA_NO_DEVICE_IO)
-    ma_device* pDevice;                     /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */
+    ma_device* pDevice;                             /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */
 #endif
     ma_log* pLog;
     ma_uint32 sampleRate;
@@ -11220,10 +11290,10 @@ struct ma_engine
     ma_allocation_callbacks allocationCallbacks;
     ma_bool8 ownsResourceManager;
     ma_bool8 ownsDevice;
-    ma_spinlock inlinedSoundLock;               /* For synchronizing access so the inlined sound list. */
-    ma_sound_inlined* pInlinedSoundHead;        /* The first inlined sound. Inlined sounds are tracked in a linked list. */
-    MA_ATOMIC(4, ma_uint32) inlinedSoundCount;  /* The total number of allocated inlined sound objects. Used for debugging. */
-    ma_uint32 gainSmoothTimeInFrames;           /* The number of frames to interpolate the gain of spatialized sounds across. */
+    ma_spinlock inlinedSoundLock;                   /* For synchronizing access to the inlined sound list. */
+    ma_sound_inlined* pInlinedSoundHead;            /* The first inlined sound. Inlined sounds are tracked in a linked list. */
+    MA_ATOMIC(4, ma_uint32) inlinedSoundCount;      /* The total number of allocated inlined sound objects. Used for debugging. */
+    ma_uint32 gainSmoothTimeInFrames;               /* The number of frames to interpolate the gain of spatialized sounds across. */
     ma_uint32 defaultVolumeSmoothTimeInPCMFrames;
     ma_mono_expansion_mode monoExpansionMode;
     ma_engine_process_proc onProcess;
@@ -11348,6 +11418,7 @@ MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping);
 MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound);
 MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound);
 MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex); /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */
+MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds); /* Abstraction to ma_sound_seek_to_pcm_frame() */
 MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
 MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor);
 MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength);
@@ -13861,7 +13932,7 @@ static ma_uint32 ma_ffs_32(ma_uint32 x)
 
     /* Just a naive implementation just to get things working for now. Will optimize this later. */
     for (i = 0; i < 32; i += 1) {
-        if ((x & (1 << i)) != 0) {
+        if ((x & (1U << i)) != 0) {
             return i;
         }
     }
@@ -14024,7 +14095,7 @@ static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 dith
 Atomics
 
 **************************************************************************************************************************************************************/
-/* ma_atomic.h begin */
+/* c89atomic.h begin */
 #ifndef ma_atomic_h
 #if defined(__cplusplus)
 extern "C" {
@@ -14750,12 +14821,12 @@ typedef int ma_atomic_memory_order;
         typedef ma_uint8 ma_atomic_flag;
         #define ma_atomic_flag_test_and_set_explicit(ptr, order)    (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order)
         #define ma_atomic_flag_clear_explicit(ptr, order)           ma_atomic_clear_explicit_8(ptr, order)
-        #define c89atoimc_flag_load_explicit(ptr, order)            ma_atomic_load_explicit_8(ptr, order)
+        #define ma_atomic_flag_load_explicit(ptr, order)            ma_atomic_load_explicit_8(ptr, order)
     #else
         typedef ma_uint32 ma_atomic_flag;
         #define ma_atomic_flag_test_and_set_explicit(ptr, order)    (ma_bool32)ma_atomic_test_and_set_explicit_32(ptr, order)
         #define ma_atomic_flag_clear_explicit(ptr, order)           ma_atomic_clear_explicit_32(ptr, order)
-        #define c89atoimc_flag_load_explicit(ptr, order)            ma_atomic_load_explicit_32(ptr, order)
+        #define ma_atomic_flag_load_explicit(ptr, order)            ma_atomic_load_explicit_32(ptr, order)
     #endif
 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
     #define MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE
@@ -14836,15 +14907,24 @@ typedef int ma_atomic_memory_order;
         __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
         return expected;
     }
+    #if defined(__clang__)
+        #pragma clang diagnostic push
+        #if __clang_major__ >= 8
+            #pragma clang diagnostic ignored "-Watomic-alignment"
+        #endif
+    #endif
     static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired)
     {
         __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
         return expected;
     }
+    #if defined(__clang__)
+        #pragma clang diagnostic pop
+    #endif
     typedef ma_uint8 ma_atomic_flag;
     #define ma_atomic_flag_test_and_set_explicit(dst, order)        (ma_bool32)__atomic_test_and_set(dst, order)
     #define ma_atomic_flag_clear_explicit(dst, order)               __atomic_clear(dst, order)
-    #define c89atoimc_flag_load_explicit(ptr, order)                ma_atomic_load_explicit_8(ptr, order)
+    #define ma_atomic_flag_load_explicit(ptr, order)                ma_atomic_load_explicit_8(ptr, order)
 #else
     #define ma_atomic_memory_order_relaxed  1
     #define ma_atomic_memory_order_consume  2
@@ -15358,7 +15438,7 @@ typedef int ma_atomic_memory_order;
     typedef ma_uint8 ma_atomic_flag;
     #define ma_atomic_flag_test_and_set_explicit(ptr, order)        (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order)
     #define ma_atomic_flag_clear_explicit(ptr, order)               ma_atomic_clear_explicit_8(ptr, order)
-    #define c89atoimc_flag_load_explicit(ptr, order)                ma_atomic_load_explicit_8(ptr, order)
+    #define ma_atomic_flag_load_explicit(ptr, order)                ma_atomic_load_explicit_8(ptr, order)
 #endif
 #if !defined(MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE)
     #if defined(MA_ATOMIC_HAS_8)
@@ -15883,7 +15963,7 @@ static MA_INLINE void ma_atomic_spinlock_lock(volatile ma_atomic_spinlock* pSpin
         if (ma_atomic_flag_test_and_set_explicit(pSpinlock, ma_atomic_memory_order_acquire) == 0) {
             break;
         }
-        while (c89atoimc_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) {
+        while (ma_atomic_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) {
         }
     }
 }
@@ -15898,7 +15978,7 @@ static MA_INLINE void ma_atomic_spinlock_unlock(volatile ma_atomic_spinlock* pSp
 }
 #endif
 #endif
-/* ma_atomic.h end */
+/* c89atomic.h end */
 
 #define MA_ATOMIC_SAFE_TYPE_IMPL(c89TypeExtension, type) \
     static MA_INLINE ma_##type ma_atomic_##type##_get(ma_atomic_##type* x) \
@@ -16096,7 +16176,7 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority
     int result;
     pthread_attr_t* pAttr = NULL;
 
-#if !defined(__EMSCRIPTEN__)
+#if !defined(__EMSCRIPTEN__) && !defined(__3DS__)
     /* Try setting the thread priority. It's not critical if anything fails here. */
     pthread_attr_t attr;
     if (pthread_attr_init(&attr) == 0) {
@@ -16142,19 +16222,34 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority
                 if (priority == ma_thread_priority_idle) {
                     sched.sched_priority = priorityMin;
                 } else if (priority == ma_thread_priority_realtime) {
-                    sched.sched_priority = priorityMax;
-                } else {
-                    sched.sched_priority += ((int)priority + 5) * priorityStep;  /* +5 because the lowest priority is -5. */
-                    if (sched.sched_priority < priorityMin) {
-                        sched.sched_priority = priorityMin;
+                    #if defined(MA_PTHREAD_REALTIME_THREAD_PRIORITY)
+                    {
+                        sched.sched_priority = MA_PTHREAD_REALTIME_THREAD_PRIORITY;
                     }
-                    if (sched.sched_priority > priorityMax) {
+                    #else
+                    {
                         sched.sched_priority = priorityMax;
                     }
+                    #endif
+                } else {
+                    sched.sched_priority += ((int)priority + 5) * priorityStep;  /* +5 because the lowest priority is -5. */
                 }
 
-                /* I'm not treating a failure of setting the priority as a critical error so not checking the return value here. */
-                pthread_attr_setschedparam(&attr, &sched);
+                if (sched.sched_priority < priorityMin) {
+                    sched.sched_priority = priorityMin;
+                }
+                if (sched.sched_priority > priorityMax) {
+                    sched.sched_priority = priorityMax;
+                }
+
+                /* I'm not treating a failure of setting the priority as a critical error so not aborting on failure here. */
+                if (pthread_attr_setschedparam(&attr, &sched) == 0) {
+                    #if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28)
+                    {
+                        pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
+                    }
+                    #endif
+                }
             }
         }
     }
@@ -16187,7 +16282,7 @@ static void ma_thread_wait__posix(ma_thread* pThread)
 static ma_result ma_mutex_init__posix(ma_mutex* pMutex)
 {
     int result;
-    
+
     if (pMutex == NULL) {
         return MA_INVALID_ARGS;
     }
@@ -17406,7 +17501,7 @@ static ma_job_proc g_jobVTable[MA_JOB_TYPE_COUNT] =
 
     /* Device. */
 #if !defined(MA_NO_DEVICE_IO)
-    ma_job_process__device__aaudio_reroute                      /*MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE*/
+    ma_job_process__device__aaudio_reroute                      /* MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE */
 #endif
 };
 
@@ -17751,7 +17846,7 @@ MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob)
         is stored. One thread can fall through to the freeing of this item while another is still using "head" for the
         retrieval of the "next" variable.
 
-        The slot allocator might need to make use of some reference counting to ensure it's only truely freed when
+        The slot allocator might need to make use of some reference counting to ensure it's only truly freed when
         there are no more references to the item. This must be fixed before removing these locks.
         */
 
@@ -17859,7 +17954,16 @@ MA_API void ma_dlclose(ma_log* pLog, ma_handle handle)
     #ifdef MA_WIN32
         FreeLibrary((HMODULE)handle);
     #else
-        dlclose((void*)handle);
+        /* Hack for Android bug (see https://github.com/android/ndk/issues/360). Calling dlclose() pre-API 28 may segfault. */
+        #if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28)
+        {
+            dlclose((void*)handle);
+        }
+        #else
+        {
+            (void)handle;
+        }
+        #endif
     #endif
 
     (void)pLog;
@@ -17880,12 +17984,12 @@ MA_API ma_proc ma_dlsym(ma_log* pLog, ma_handle handle, const char* symbol)
 #ifdef _WIN32
     proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol);
 #else
-#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
+#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__)
     #pragma GCC diagnostic push
     #pragma GCC diagnostic ignored "-Wpedantic"
 #endif
     proc = (ma_proc)dlsym((void*)handle, symbol);
-#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
+#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__)
     #pragma GCC diagnostic pop
 #endif
 #endif
@@ -17923,9 +18027,13 @@ DEVICE I/O
     #endif
 #endif
 
+#ifdef MA_APPLE
+    #include 
+#endif
+
 #ifndef MA_NO_DEVICE_IO
 
-#if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
+#if defined(MA_APPLE) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
     #include  /* For mach_absolute_time() */
 #endif
 
@@ -17939,6 +18047,10 @@ DEVICE I/O
     #endif
 #endif
 
+/* This must be set to at least 26. */
+#ifndef MA_AAUDIO_MIN_ANDROID_SDK_VERSION
+#define MA_AAUDIO_MIN_ANDROID_SDK_VERSION 27
+#endif
 
 
 MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags)
@@ -18085,7 +18197,7 @@ MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend)
         #if defined(MA_HAS_AAUDIO)
             #if defined(MA_ANDROID)
             {
-                return ma_android_sdk_version() >= 26;
+                return ma_android_sdk_version() >= MA_AAUDIO_MIN_ANDROID_SDK_VERSION;
             }
             #else
                 return MA_FALSE;
@@ -18402,7 +18514,6 @@ typedef LONG    (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey);
 typedef LONG    (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, const char* lpValueName, DWORD* lpReserved, DWORD* lpType, BYTE* lpData, DWORD* lpcbData);
 #endif  /* MA_WIN32_DESKTOP */
 
-
 MA_API size_t ma_strlen_WCHAR(const WCHAR* str)
 {
     size_t len = 0;
@@ -18487,7 +18598,7 @@ Timing
 
         return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart;
     }
-#elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
+#elif defined(MA_APPLE) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
     static ma_uint64 g_ma_TimerFrequency = 0;
     static void ma_timer_init(ma_timer* pTimer)
     {
@@ -18670,11 +18781,16 @@ static void ma_device__on_notification_rerouted(ma_device* pDevice)
 #endif
 
 #if defined(MA_EMSCRIPTEN)
-EMSCRIPTEN_KEEPALIVE
-void ma_device__on_notification_unlocked(ma_device* pDevice)
+#ifdef __cplusplus
+extern "C" {
+#endif
+void EMSCRIPTEN_KEEPALIVE ma_device__on_notification_unlocked(ma_device* pDevice)
 {
     ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_unlocked));
 }
+#ifdef __cplusplus
+}
+#endif
 #endif
 
 
@@ -18802,7 +18918,7 @@ static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut
         unsigned int prevDenormalState = ma_device_disable_denormals(pDevice);
         {
             /* Volume control of input makes things a bit awkward because the input buffer is read-only. We'll need to use a temp buffer and loop in this case. */
-            if (pFramesIn != NULL && masterVolumeFactor < 1) {
+            if (pFramesIn != NULL && masterVolumeFactor != 1) {
                 ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
                 ma_uint32 bpfCapture  = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
                 ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
@@ -18825,7 +18941,7 @@ static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut
 
             /* Volume control and clipping for playback devices. */
             if (pFramesOut != NULL) {
-                if (masterVolumeFactor < 1) {
+                if (masterVolumeFactor != 1) {
                     if (pFramesIn == NULL) {    /* <-- In full-duplex situations, the volume will have been applied to the input samples before the data callback. Applying it again post-callback will incorrectly compound it. */
                         ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor);
                     }
@@ -18837,6 +18953,11 @@ static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut
             }
         }
         ma_device_restore_denormals(pDevice, prevDenormalState);
+    } else {
+        /* No data callback. Just silence the output. */
+        if (pFramesOut != NULL) {
+            ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels);
+        }
     }
 }
 
@@ -18922,9 +19043,7 @@ static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 fra
                     framesToReadThisIterationIn = requiredInputFrameCount;
                 }
 
-                if (framesToReadThisIterationIn > 0) {
-                    ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn);
-                }
+                ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn);
 
                 /*
                 At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any
@@ -18965,7 +19084,7 @@ static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frame
         ma_uint64 totalClientFramesProcessed = 0;
         const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat;
 
-        /* We just keep going until we've exhaused all of our input frames and cannot generate any more output frames. */
+        /* We just keep going until we've exhausted all of our input frames and cannot generate any more output frames. */
         for (;;) {
             ma_uint64 deviceFramesProcessedThisIteration;
             ma_uint64 clientFramesProcessedThisIteration;
@@ -19248,7 +19367,7 @@ static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice)
                         }
 
                         /*
-                        If we weren't able to generate any output frames it must mean we've exhaused all of our input. The only time this would not be the case is if capturedClientData was too small
+                        If we weren't able to generate any output frames it must mean we've exhausted all of our input. The only time this would not be the case is if capturedClientData was too small
                         which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
                         */
                         if (capturedClientFramesToProcessThisIteration == 0) {
@@ -19451,7 +19570,7 @@ static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 oper
 
     /*
     The first thing to do is wait for an operation slot to become available. We only have a single slot for this, but we could extend this later
-    to support queing of operations.
+    to support queuing of operations.
     */
     result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore);
     if (result != MA_SUCCESS) {
@@ -21268,7 +21387,7 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context
     }
 
     /*
-    Exlcusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on
+    Exclusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on
     UWP. Failure to retrieve the exclusive mode format is not considered an error, so from here on
     out, MA_SUCCESS is guaranteed to be returned.
     */
@@ -21473,10 +21592,23 @@ static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device
     MA_ASSERT(pContext != NULL);
     MA_ASSERT(ppMMDevice != NULL);
 
+    /*
+    This weird COM init/uninit here is a hack to work around a crash when changing devices. What is happening is
+    WASAPI fires a callback from another thread when the device is changed. It's from that thread where this
+    function is getting called. What I'm suspecting is that the other thread is not initializing COM which in turn
+    results in CoCreateInstance() failing.
+
+    The community has reported that this seems to fix the crash. There are future plans to move all WASAPI operation
+    over to a single thread to make everything safer, but in the meantime while we wait for that to come online I'm
+    happy enough to use this hack instead.
+    */
     ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE);
-    hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
+    {
+        hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
+    }
     ma_CoUninitialize(pContext);
-    if (FAILED(hr)) {
+
+    if (FAILED(hr)) {   /* <-- This is checking the call above to ma_CoCreateInstance(). */
         ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n");
         return ma_result_from_HRESULT(hr);
     }
@@ -21508,7 +21640,7 @@ static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pCon
         size_t idlen = ma_strlen_WCHAR(pDeviceIDString);
         if (idlen+1 > ma_countof(pDeviceID->wasapi)) {
             ma_CoTaskMemFree(pContext, pDeviceIDString);
-            MA_ASSERT(MA_FALSE);  /* NOTE: If this is triggered, please report it. It means the format of the ID must haved change and is too long to fit in our fixed sized buffer. */
+            MA_ASSERT(MA_FALSE);  /* NOTE: If this is triggered, please report it. It means the format of the ID must have changed and is too long to fit in our fixed sized buffer. */
             return MA_ERROR;
         }
 
@@ -21952,12 +22084,16 @@ static ma_result ma_device_uninit__wasapi(ma_device* pDevice)
 {
     MA_ASSERT(pDevice != NULL);
 
-#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
-    if (pDevice->wasapi.pDeviceEnumerator) {
-        ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient);
-        ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator);
+    #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
+    {
+        if (pDevice->wasapi.pDeviceEnumerator) {
+            ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient);
+            ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator);
+        }
+
+        ma_mutex_uninit(&pDevice->wasapi.rerouteLock);
     }
-#endif
+    #endif
 
     if (pDevice->wasapi.pRenderClient) {
         if (pDevice->wasapi.pMappedBufferPlayback != NULL) {
@@ -22258,7 +22394,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device
         MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10;
 
         /*
-        If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing
+        If the periodicity is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing
         it and trying it again.
         */
         hr = E_FAIL;
@@ -22268,7 +22404,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device
                 if (bufferDuration > 500*10000) {
                     break;
                 } else {
-                    if (bufferDuration == 0) {  /* <-- Just a sanity check to prevent an infinit loop. Should never happen, but it makes me feel better. */
+                    if (bufferDuration == 0) {  /* <-- Just a sanity check to prevent an infinite loop. Should never happen, but it makes me feel better. */
                         break;
                     }
 
@@ -23007,6 +23143,14 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice)
     }
 
     if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
+        /* If we have a mapped buffer we need to release it. */
+        if (pDevice->wasapi.pMappedBufferCapture != NULL) {
+            ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
+            pDevice->wasapi.pMappedBufferCapture = NULL;
+            pDevice->wasapi.mappedBufferCaptureCap = 0;
+            pDevice->wasapi.mappedBufferCaptureLen = 0;
+        }
+
         hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
         if (FAILED(hr)) {
             ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device.");
@@ -23020,31 +23164,34 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice)
             return ma_result_from_HRESULT(hr);
         }
 
-        /* If we have a mapped buffer we need to release it. */
-        if (pDevice->wasapi.pMappedBufferCapture != NULL) {
-            ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
-            pDevice->wasapi.pMappedBufferCapture = NULL;
-            pDevice->wasapi.mappedBufferCaptureCap = 0;
-            pDevice->wasapi.mappedBufferCaptureLen = 0;
-        }
-
         ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE);
     }
 
     if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
+        if (pDevice->wasapi.pMappedBufferPlayback != NULL) {
+            ma_silence_pcm_frames(
+                ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels),
+                pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen,
+                pDevice->playback.internalFormat, pDevice->playback.internalChannels
+            );
+            ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0);
+            pDevice->wasapi.pMappedBufferPlayback = NULL;
+            pDevice->wasapi.mappedBufferPlaybackCap = 0;
+            pDevice->wasapi.mappedBufferPlaybackLen = 0;
+        }
+
         /*
         The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to
         the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played.
         */
         if (ma_atomic_bool32_get(&pDevice->wasapi.isStartedPlayback)) {
             /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */
-            DWORD waitTime = pDevice->wasapi.actualBufferSizeInFramesPlayback / pDevice->playback.internalSampleRate;
+            DWORD waitTime = (pDevice->wasapi.actualBufferSizeInFramesPlayback * 1000) / pDevice->playback.internalSampleRate;
 
             if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
                 WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime);
-            }
-            else {
-                ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1;
+            } else {
+                ma_uint32 prevFramesAvailablePlayback = (ma_uint32)-1;
                 ma_uint32 framesAvailablePlayback;
                 for (;;) {
                     result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
@@ -23060,13 +23207,13 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice)
                     Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames
                     has not changed, get out of the loop. I don't think this should ever happen, but I think it's nice to have just in case.
                     */
-                    if (framesAvailablePlayback == prevFramesAvaialablePlayback) {
+                    if (framesAvailablePlayback == prevFramesAvailablePlayback) {
                         break;
                     }
-                    prevFramesAvaialablePlayback = framesAvailablePlayback;
+                    prevFramesAvailablePlayback = framesAvailablePlayback;
 
-                    WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime * 1000);
                     ResetEvent((HANDLE)pDevice->wasapi.hEventPlayback); /* Manual reset. */
+                    WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime);
                 }
             }
         }
@@ -23078,19 +23225,20 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice)
         }
 
         /* The audio client needs to be reset otherwise restarting will fail. */
-        hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
+        {
+            ma_int32 retries = 5;
+
+            while ((hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback)) == MA_AUDCLNT_E_BUFFER_OPERATION_PENDING && retries > 0) {
+                ma_sleep(10);
+                retries -= 1;
+            }
+        }
+
         if (FAILED(hr)) {
             ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device.");
             return ma_result_from_HRESULT(hr);
         }
 
-        if (pDevice->wasapi.pMappedBufferPlayback != NULL) {
-            ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0);
-            pDevice->wasapi.pMappedBufferPlayback = NULL;
-            pDevice->wasapi.mappedBufferPlaybackCap = 0;
-            pDevice->wasapi.mappedBufferPlaybackLen = 0;
-        }
-
         ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
     }
 
@@ -23657,6 +23805,13 @@ DirectSound Backend
 #define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010
 #define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020
 
+#define MA_DSBSTATUS_PLAYING            0x00000001
+#define MA_DSBSTATUS_BUFFERLOST         0x00000002
+#define MA_DSBSTATUS_LOOPING            0x00000004
+#define MA_DSBSTATUS_LOCHARDWARE        0x00000008
+#define MA_DSBSTATUS_LOCSOFTWARE        0x00000010
+#define MA_DSBSTATUS_TERMINATED         0x00000020
+
 #define MA_DSCBSTART_LOOPING            0x00000001
 
 typedef struct
@@ -24026,9 +24181,12 @@ static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma
     }
 
     /* The cooperative level must be set before doing anything else. */
-    hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)();
+    hWnd = (HWND)pContext->dsound.hWnd;
     if (hWnd == 0) {
-        hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)();
+        hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)();
+        if (hWnd == 0) {
+            hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)();
+        }
     }
 
     hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY);
@@ -24532,8 +24690,8 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf
     }
 
     /*
-    Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices. We need to initialize
-    the capture device first because we'll want to match it's buffer size and period count on the playback side if we're using
+    Unfortunately DirectSound uses different APIs and data structures for playback and capture devices. We need to initialize
+    the capture device first because we'll want to match its buffer size and period count on the playback side if we're using
     full-duplex mode.
     */
     if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
@@ -24816,6 +24974,7 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice)
     ma_bool32 isPlaybackDeviceStarted = MA_FALSE;
     ma_uint32 framesWrittenToPlaybackDevice = 0;   /* For knowing whether or not the playback device needs to be started. */
     ma_uint32 waitTimeInMilliseconds = 1;
+    DWORD playbackBufferStatus = 0;
 
     MA_ASSERT(pDevice != NULL);
 
@@ -25144,6 +25303,20 @@ static ma_result ma_device_data_loop__dsound(ma_device* pDevice)
                     break;
                 }
 
+                hr = ma_IDirectSoundBuffer_GetStatus((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &playbackBufferStatus);
+                if (SUCCEEDED(hr) && (playbackBufferStatus & MA_DSBSTATUS_PLAYING) == 0 && isPlaybackDeviceStarted) {
+                    ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[DirectSound] Attempting to resume audio due to state: %d.", (int)playbackBufferStatus);
+                    hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
+                    if (FAILED(hr)) {
+                        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed after attempting to resume from state %d.", (int)playbackBufferStatus);
+                        return ma_result_from_HRESULT(hr);
+                    }
+
+                    isPlaybackDeviceStarted = MA_TRUE;
+                    ma_sleep(waitTimeInMilliseconds);
+                    continue;
+                }
+
                 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
                     physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
                 }
@@ -25345,6 +25518,8 @@ static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_
         return MA_API_NOT_FOUND;
     }
 
+    pContext->dsound.hWnd = pConfig->dsound.hWnd;
+
     pCallbacks->onContextInit             = ma_context_init__dsound;
     pCallbacks->onContextUninit           = ma_context_uninit__dsound;
     pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound;
@@ -25667,7 +25842,7 @@ static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext,
     - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters.
     - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The
       problem, however is that WASAPI and DirectSound use " ()" format (such as "Speakers (High Definition Audio)"),
-      but WinMM does not specificy the component name. From my admittedly limited testing, I've notice the component name seems to
+      but WinMM does not specify the component name. From my admittedly limited testing, I've notice the component name seems to
       usually fit within the 31 characters of the fixed sized buffer, so what I'm going to do is parse that string for the component
       name, and then concatenate the name from the registry.
     */
@@ -25935,7 +26110,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi
         return MA_DEVICE_TYPE_NOT_SUPPORTED;
     }
 
-    /* No exlusive mode with WinMM. */
+    /* No exclusive mode with WinMM. */
     if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
         ((pConfig->deviceType == ma_device_type_capture  || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode  == ma_share_mode_exclusive)) {
         return MA_SHARE_MODE_NOT_SUPPORTED;
@@ -25957,7 +26132,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi
         /* We use an event to know when a new fragment needs to be enqueued. */
         pDevice->winmm.hEventCapture = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL);
         if (pDevice->winmm.hEventCapture == NULL) {
-            errorMsg = "[WinMM] Failed to create event for fragment enqueing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError());
+            errorMsg = "[WinMM] Failed to create event for fragment enqueuing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError());
             goto on_error;
         }
 
@@ -25995,7 +26170,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi
         /* We use an event to know when a new fragment needs to be enqueued. */
         pDevice->winmm.hEventPlayback = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL);
         if (pDevice->winmm.hEventPlayback == NULL) {
-            errorMsg = "[WinMM] Failed to create event for fragment enqueing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError());
+            errorMsg = "[WinMM] Failed to create event for fragment enqueuing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError());
             goto on_error;
         }
 
@@ -27117,7 +27292,7 @@ static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode s
         /*
         We're trying to open a specific device. There's a few things to consider here:
 
-        miniaudio recongnizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When
+        miniaudio recognizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When
         an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it
         finds an appropriate one that works. This comes in very handy when trying to open a device in shared mode ("dmix"), vs exclusive mode ("hw").
         */
@@ -27216,7 +27391,7 @@ static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enu
                     /*
                     At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the
                     plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device
-                    initialization time and is used as an indicator to try and use the most appropriate plugin depending on the
+                    initialization time and is used as an indicator to try to use the most appropriate plugin depending on the
                     device type and sharing mode.
                     */
                     char* dst = hwid;
@@ -27395,7 +27570,7 @@ static void ma_context_iterate_rates_and_add_native_data_format__alsa(ma_context
     ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir);
     ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir);
 
-    /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculus. */
+    /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculous. */
     minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max);
     maxSampleRate = ma_clamp(maxSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max);
 
@@ -27471,10 +27646,10 @@ static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_devic
     /*
     Some ALSA devices can support many permutations of formats, channels and rates. We only support
     a fixed number of permutations which means we need to employ some strategies to ensure the best
-    combinations are returned. An example is the "pulse" device which can do it's own data conversion
+    combinations are returned. An example is the "pulse" device which can do its own data conversion
     in software and as a result can support any combination of format, channels and rate.
 
-    We want to ensure the the first data formats are the best. We have a list of favored sample
+    We want to ensure that the first data formats are the best. We have a list of favored sample
     formats and sample rates, so these will be the basis of our iteration.
     */
 
@@ -28052,7 +28227,21 @@ static ma_result ma_device_start__alsa(ma_device* pDevice)
     }
 
     if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
-        /* Don't need to do anything for playback because it'll be started automatically when enough data has been written. */
+        /*        
+        When data is written to the device we wait for the device to get ready to receive data with poll(). In my testing
+        I have observed that poll() can sometimes block forever unless the device is started explicitly with snd_pcm_start()
+        or some data is written with snd_pcm_writei().
+
+        To resolve this I've decided to do an explicit start with snd_pcm_start(). The problem with this is that the device
+        is started without any data in the internal buffer which will result in an immediate underrun. If instead we were
+        to call into snd_pcm_writei() in an attempt to prevent the underrun, we would run the risk of a weird deadlock
+        issue as documented inside ma_device_write__alsa().
+        */
+        resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
+        if (resultALSA < 0) {
+            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start playback device.");
+            return ma_result_from_errno(-resultALSA);
+        }
     }
 
     return MA_SUCCESS;
@@ -28065,6 +28254,7 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice)
     a small chance that our wakeupfd has not been cleared. We'll clear that out now if applicable.
     */
     int resultPoll;
+    int resultRead;
 
     if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
         ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n");
@@ -28079,12 +28269,15 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice)
             ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n");
         }
 
-    /* Clear the wakeupfd. */
-    resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0);
-    if (resultPoll > 0) {
-        ma_uint64 t;
-        read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t));
-    }
+        /* Clear the wakeupfd. */
+        resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0);
+        if (resultPoll > 0) {
+            ma_uint64 t;
+            resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t));
+            if (resultRead != sizeof(t)) {
+                ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from capture wakeupfd. read() = %d\n", resultRead);
+            }
+        }
     }
 
     if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
@@ -28101,12 +28294,14 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice)
         }
 
         /* Clear the wakeupfd. */
-    resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0);
-    if (resultPoll > 0) {
-        ma_uint64 t;
-        read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t));
-    }
-
+        resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0);
+        if (resultPoll > 0) {
+            ma_uint64 t;
+            resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t));
+            if (resultRead != sizeof(t)) {
+                ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from playback wakeupfd. read() = %d\n", resultRead);
+            }
+        }
     }
 
     return MA_SUCCESS;
@@ -28119,13 +28314,20 @@ static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, st
         int resultALSA;
         int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1);
         if (resultPoll < 0) {
-            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] poll() failed.\n");
-            return ma_result_from_errno(errno);
+            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] poll() failed.\n");
+
+            /*
+            There have been reports that poll() is returning an error randomly and that instead of
+            returning an error, simply trying again will work. I'm experimenting with adopting this
+            advice.
+            */
+            continue;
+            /*return ma_result_from_errno(errno);*/
         }
 
         /*
         Before checking the ALSA poll descriptor flag we need to check if the wakeup descriptor
-        has had it's POLLIN flag set. If so, we need to actually read the data and then exit
+        has had it's POLLIN flag set. If so, we need to actually read the data and then exit the
         function. The wakeup descriptor will be the first item in the descriptors buffer.
         */
         if ((pPollDescriptors[0].revents & POLLIN) != 0) {
@@ -28154,7 +28356,7 @@ static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, st
             ma_snd_pcm_state_t state = ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM);
             if (state == MA_SND_PCM_STATE_XRUN) {
                 /* The PCM is in a xrun state. This will be recovered from at a higher level. We can disregard this. */
-        } else {
+            } else {
                 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] POLLERR detected. status = %d\n", ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM));
             }
         }
@@ -28587,7 +28789,7 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co
 
     return MA_SUCCESS;
 }
-#endif  /* ALSA */
+#endif  /* MA_HAS_ALSA */
 
 
 
@@ -28598,7 +28800,7 @@ PulseAudio Backend
 ******************************************************************************/
 #ifdef MA_HAS_PULSEAUDIO
 /*
-The PulseAudio API, along with Apple's Core Audio, is the worst of the maintream audio APIs. This is a brief description of what's going on
+The PulseAudio API, along with Apple's Core Audio, is the worst of the mainstream audio APIs. This is a brief description of what's going on
 in the PulseAudio backend. I apologize if this gets a bit ranty for your liking - you might want to skip this discussion.
 
 PulseAudio has something they call the "Simple API", which unfortunately isn't suitable for miniaudio. I've not seen anywhere where it
@@ -28613,7 +28815,7 @@ get fun, and I don't mean that in a good way...
 
 The problems start with the very name of the API - "asynchronous". Yes, this is an asynchronous oriented API which means your commands
 don't immediately take effect. You instead need to issue your commands, and then wait for them to complete. The waiting mechanism is
-enabled through the use of a "main loop". In the asychronous API you cannot get away from the main loop, and the main loop is where almost
+enabled through the use of a "main loop". In the asynchronous API you cannot get away from the main loop, and the main loop is where almost
 all of PulseAudio's problems stem from.
 
 When you first initialize PulseAudio you need an object referred to as "main loop". You can implement this yourself by defining your own
@@ -28663,7 +28865,7 @@ because PulseAudio takes it literally, specifically the "can be". You would thin
 writing and reading data to and from the stream, and that would be right, except when it's not. When you initialize the stream, you can
 set a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices
 straight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified,
-PulseAudio will immediately fire it's write or read callback. This is *technically* correct (based on the wording in the documentation)
+PulseAudio will immediately fire its write or read callback. This is *technically* correct (based on the wording in the documentation)
 because indeed, data *can* be written at this point. The problem is that it's not *practical*. It makes sense that the write/read callback
 would be where a program will want to write or read data to or from the stream, but when it's called before the application has even
 requested that the stream be started, it's just not practical because the program probably isn't ready for any kind of data delivery at
@@ -30041,16 +30243,18 @@ static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFra
 
 static ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap)
 {
-    static int g_StreamCounter = 0;
+    static ma_atomic_uint32 g_StreamCounter = { 0 };
     char actualStreamName[256];
 
     if (pStreamName != NULL) {
         ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1);
     } else {
-        ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:");
-        ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10);  /* 8 = strlen("miniaudio:") */
+        const char* pBaseName = "miniaudio:";
+        size_t baseNameLen = strlen(pBaseName);
+        ma_strcpy_s(actualStreamName, sizeof(actualStreamName), pBaseName);
+        ma_itoa_s((int)ma_atomic_uint32_get(&g_StreamCounter), actualStreamName + baseNameLen, sizeof(actualStreamName)-baseNameLen, 10);
     }
-    g_StreamCounter += 1;
+    ma_atomic_uint32_fetch_add(&g_StreamCounter, 1);
 
     return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap);
 }
@@ -30304,6 +30508,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
     ma_pa_buffer_attr attr;
     const ma_pa_sample_spec* pActualSS   = NULL;
     const ma_pa_buffer_attr* pActualAttr = NULL;
+    const ma_pa_channel_map* pActualChannelMap = NULL;
     ma_uint32 iChannel;
     ma_pa_stream_flags_t streamFlags;
 
@@ -30364,7 +30569,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
         }
 
         /* Use a default channel map. */
-        ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT);
+        ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, pConfig->pulse.channelMap);
 
         /* Use the requested sample rate if one was specified. */
         if (pDescriptorCapture->sampleRate != 0) {
@@ -30453,7 +30658,12 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
             goto on_error4;
         }
 
+
         /* Internal channel map. */
+        pActualChannelMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
+        if (pActualChannelMap == NULL) {
+            pActualChannelMap = &cmap;  /* Fallback just in case. */
+        }
 
         /*
         Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting
@@ -30463,8 +30673,8 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
         fixed sooner than later. I might remove this hack later.
         */
         if (pDescriptorCapture->channels > 2) {
-            for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) {
-                pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
+            for (iChannel = 0; iChannel < pDescriptorCapture->channels; iChannel += 1) {
+                pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(pActualChannelMap->map[iChannel]);
             }
         } else {
             /* Hack for mono and stereo. */
@@ -30511,7 +30721,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
         }
 
         /* Use a default channel map. */
-        ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT);
+        ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, pConfig->pulse.channelMap);
 
 
         /* Use the requested sample rate if one was specified. */
@@ -30605,7 +30815,12 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
             goto on_error4;
         }
 
+
         /* Internal channel map. */
+        pActualChannelMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
+        if (pActualChannelMap == NULL) {
+            pActualChannelMap = &cmap;  /* Fallback just in case. */
+        }
 
         /*
         Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting
@@ -30615,8 +30830,8 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
         fixed sooner than later. I might remove this hack later.
         */
         if (pDescriptorPlayback->channels > 2) {
-            for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) {
-                pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
+            for (iChannel = 0; iChannel < pDescriptorPlayback->channels; iChannel += 1) {
+                pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(pActualChannelMap->map[iChannel]);
             }
         } else {
             /* Hack for mono and stereo. */
@@ -31769,7 +31984,7 @@ static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_co
 
     return MA_SUCCESS;
 }
-#endif  /* JACK */
+#endif  /* MA_HAS_JACK */
 
 
 
@@ -31860,7 +32075,7 @@ that supports this level of detail. There was some public domain sample code I s
 and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the
 distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API.
 
-Most (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When
+Most (all?) functions in the AudioObject API take a AudioObjectID as its input. This is the device identifier. When
 retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific
 data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the
 devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be
@@ -32195,6 +32410,12 @@ static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout*
 #define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMaster
 #endif
 
+/* kAudioDevicePropertyScope* were renamed to kAudioObjectPropertyScope* in 10.8. */
+#if !defined(MAC_OS_X_VERSION_10_8) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8)
+#define kAudioObjectPropertyScopeInput kAudioDevicePropertyScopeInput
+#define kAudioObjectPropertyScopeOutput kAudioDevicePropertyScopeOutput
+#endif
+
 static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */
 {
     AudioObjectPropertyAddress propAddressDevices;
@@ -32784,7 +33005,7 @@ static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjec
 
     desiredSampleRate = sampleRate;
     if (desiredSampleRate == 0) {
-        desiredSampleRate = pOrigFormat->mSampleRate;
+        desiredSampleRate = (ma_uint32)pOrigFormat->mSampleRate;
     }
 
     desiredChannelCount = channels;
@@ -33427,7 +33648,7 @@ static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFl
         }
     } else {
         /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */
-        MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS);   /* This should heve been validated at initialization time. */
+        MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS);   /* This should have been validated at initialization time. */
 
         /*
         For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
@@ -33518,11 +33739,12 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla
     */
     for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
         pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels;
+        /*printf("DEBUG: nDataByteSize = %d\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/
     }
 
     status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList);
     if (status != noErr) {
-        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "  ERROR: AudioUnitRender() failed with %d.\n", (int)status);
+        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "ERROR: AudioUnitRender() failed with %d.\n", (int)status);
         return status;
     }
 
@@ -33758,7 +33980,7 @@ static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContex
 
     ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio);
     {
-        /* Don't do anything if we've already initializd device tracking. */
+        /* Don't do anything if we've already initialized device tracking. */
         if (g_DeviceTrackingInitCounter_CoreAudio == 0) {
             AudioObjectPropertyAddress propAddress;
             propAddress.mScope    = kAudioObjectPropertyScopeGlobal;
@@ -34070,11 +34292,11 @@ typedef struct
 
 static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference)   /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */
 {
-    ma_result result;
+    ma_result result = MA_SUCCESS;
     OSStatus status;
     UInt32 enableIOFlag;
     AudioStreamBasicDescription bestFormat;
-    UInt32 actualPeriodSizeInFrames;
+    ma_uint32 actualPeriodSizeInFrames;
     AURenderCallbackStruct callbackInfo;
 #if defined(MA_APPLE_DESKTOP)
     AudioObjectID deviceObjectID;
@@ -34226,7 +34448,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev
         returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but
         this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format.
 
-        Something that does seem to work, however, has been setting the nominal sample rate on the deivce object. The problem with
+        Something that does seem to work, however, has been setting the nominal sample rate on the device object. The problem with
         this, however, is that it actually changes the sample rate at the operating system level and not just the application. This
         could be intrusive to the user, however, so I don't think it's wise to make this the default. Instead I'm making this a
         configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample
@@ -34277,15 +34499,28 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev
             /*
             I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with
             AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead.
+
+            UPDATE 20/02/2025:
+            When testing on the simulator with an iPhone 15 and iOS 17 I get an error when initializing the audio
+            unit if set the input channels to pAudioSession.inputNumberOfChannels. What is happening is the channel
+            count returned from AudioUnitGetProperty() above is set to 2, but pAudioSession is reporting a channel
+            count of 1. When this happens, the call to AudioUnitSetProprty() below just down below will succeed, but
+            AudioUnitInitialize() further down will fail. The only solution I have come up with is to not set the
+            channel count to pAudioSession.inputNumberOfChannels.
             */
             if (deviceType == ma_device_type_playback) {
                 bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels;
             }
+
+            #if 0
             if (deviceType == ma_device_type_capture) {
+                /*printf("DEBUG: bestFormat.mChannelsPerFrame = %d; pAudioSession.inputNumberOfChannels = %d\n", (int)bestFormat.mChannelsPerFrame, (int)pAudioSession.inputNumberOfChannels);*/
                 bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels;
             }
+            #endif
         }
 
+        
         status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
         if (status != noErr) {
             ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
@@ -34305,7 +34540,7 @@ static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_dev
         }
 
         pData->channelsOut   = bestFormat.mChannelsPerFrame;
-        pData->sampleRateOut = bestFormat.mSampleRate;
+        pData->sampleRateOut = (ma_uint32)bestFormat.mSampleRate;
     }
 
     /* Clamp the channel count for safety. */
@@ -34612,7 +34847,7 @@ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_c
         ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio);
 
         /*
-        If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
+        If we are using the default device we'll need to listen for changes to the system's default device so we can seamlessly
         switch the device in the background.
         */
         if (pConfig->capture.pDeviceID == NULL) {
@@ -34676,7 +34911,7 @@ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_c
         ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio);
 
         /*
-        If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
+        If we are using the default device we'll need to listen for changes to the system's default device so we can seamlessly
         switch the device in the background.
         */
         if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) {
@@ -34994,7 +35229,7 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte
 
     return MA_SUCCESS;
 }
-#endif  /* Core Audio */
+#endif  /* MA_HAS_COREAUDIO */
 
 
 
@@ -35486,7 +35721,7 @@ static ma_result ma_device_uninit__sndio(ma_device* pDevice)
         ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
     }
 
-    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
+    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
         ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
     }
 
@@ -35841,7 +36076,7 @@ static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_c
     (void)pConfig;
     return MA_SUCCESS;
 }
-#endif  /* sndio */
+#endif  /* MA_HAS_SNDIO */
 
 
 
@@ -35859,6 +36094,10 @@ audio(4) Backend
 #include 
 #include 
 
+#ifdef __NetBSD__
+#include 
+#endif
+
 #if defined(__OpenBSD__)
     #include 
     #if defined(OpenBSD) && OpenBSD >= 201709
@@ -36078,7 +36317,7 @@ static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext
         ma_uint32 channels;
         ma_uint32 sampleRate;
 
-#ifdef __NetBSD__
+#if defined(__NetBSD__) && (__NetBSD_Version__ >= 900000000)
         if (ioctl(fd, AUDIO_GETFORMAT, &fdInfo) < 0) {
             return MA_ERROR;
         }
@@ -36364,7 +36603,7 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c
             /* We're using a default device. Get the info from the /dev/audioctl file instead of /dev/audio. */
             int fdctl = open(pDefaultDeviceCtlNames[iDefaultDevice], fdFlags, 0);
             if (fdctl != -1) {
-#ifdef __NetBSD__
+#if defined(__NetBSD__) && (__NetBSD_Version__ >= 900000000)
                 fdInfoResult = ioctl(fdctl, AUDIO_GETFORMAT, &fdInfo);
 #else
                 fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo);
@@ -36735,7 +36974,7 @@ static ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_
 
     return MA_SUCCESS;
 }
-#endif  /* audio4 */
+#endif  /* MA_HAS_AUDIO4 */
 
 
 /******************************************************************************
@@ -37098,7 +37337,7 @@ static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_conf
     }
 
     /*
-    The OSS documantation is very clear about the order we should be initializing the device's properties:
+    The OSS documentation is very clear about the order we should be initializing the device's properties:
       1) Format
       2) Channels
       3) Sample rate.
@@ -37366,7 +37605,7 @@ static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_con
 
     return MA_SUCCESS;
 }
-#endif  /* OSS */
+#endif  /* MA_HAS_OSS */
 
 
 
@@ -37379,7 +37618,9 @@ AAudio Backend
 ******************************************************************************/
 #ifdef MA_HAS_AAUDIO
 
-/*#include */
+#ifdef MA_NO_RUNTIME_LINKING
+    #include 
+#endif
 
 typedef int32_t                                         ma_aaudio_result_t;
 typedef int32_t                                         ma_aaudio_direction_t;
@@ -37592,9 +37833,7 @@ static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUs
     MA_ASSERT(pDevice != NULL);
 
     (void)error;
-
     ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream));
-
     /*
     When we get an error, we'll assume that the stream is in an erroneous state and needs to be restarted. From the documentation,
     we cannot do this from the error callback. Therefore we are going to use an event thread for the AAudio backend to do this
@@ -37622,7 +37861,9 @@ static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(
     ma_device* pDevice = (ma_device*)pUserData;
     MA_ASSERT(pDevice != NULL);
 
-    ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, frameCount);
+    if (frameCount > 0) {
+        ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, (ma_uint32)frameCount);
+    }
 
     (void)pStream;
     return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
@@ -37633,7 +37874,14 @@ static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio
     ma_device* pDevice = (ma_device*)pUserData;
     MA_ASSERT(pDevice != NULL);
 
-    ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, frameCount);
+    /*
+    I've had a report that AAudio can sometimes post a frame count of 0. We need to check for that here
+    so we don't get any errors at a deeper level. I'm doing the same with the capture side for safety,
+    though I've not yet had any reports about that one.
+    */
+    if (frameCount > 0) {
+        ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, (ma_uint32)frameCount);
+    }
 
     (void)pStream;
     return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
@@ -37668,32 +37916,25 @@ static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context*
             ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate);
         }
 
-        if (deviceType == ma_device_type_capture) {
-            if (pDescriptor->channels != 0) {
-                ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels);
-            }
-            if (pDescriptor->format != ma_format_unknown) {
-                ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
-            }
-        } else {
-            if (pDescriptor->channels != 0) {
-                ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels);
-            }
-            if (pDescriptor->format != ma_format_unknown) {
-                ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
-            }
+        if (pDescriptor->channels != 0) {
+            ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels);
+        }
+
+        if (pDescriptor->format != ma_format_unknown) {
+            ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
         }
 
 
         /*
-        There have been reports where setting the frames per data callback results in an error
-        later on from Android. To address this, I'm experimenting with simply not setting it on
-        anything from Android 11 and earlier. Suggestions welcome on how we might be able to make
-        this more targetted.
+        There have been reports where setting the frames per data callback results in an error.
+        In particular, re-routing may inadvertently switch from low-latency mode, resulting in a less stable
+        stream from the legacy path (AudioStreamLegacy). To address this, we simply don't set the value. It
+        can still be set if it's explicitly requested via the aaudio.allowSetBufferCapacity variable in the
+        device config.
         */
-        if (!pConfig->aaudio.enableCompatibilityWorkarounds || ma_android_sdk_version() > 30) {
+        if ((!pConfig->aaudio.enableCompatibilityWorkarounds || ma_android_sdk_version() > 30) && pConfig->aaudio.allowSetBufferCapacity) {
             /*
-            AAudio is annoying when it comes to it's buffer calculation stuff because it doesn't let you
+            AAudio is annoying when it comes to its buffer calculation stuff because it doesn't let you
             retrieve the actual sample rate until after you've opened the stream. But you need to configure
             the buffer capacity before you open the stream... :/
 
@@ -37727,7 +37968,11 @@ static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context*
             ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice);
         }
 
-        /* Not sure how this affects things, but since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let go ahead and set it. */
+        /*
+        If we set AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, we allow for MMAP (non-legacy path).
+        Since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let's use it.
+        Beware though, with a conservative performance profile, AAudio will indeed take the legacy path.
+        */
         ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MA_AAUDIO_PERFORMANCE_MODE_NONE);
 
         /* We need to set an error callback to detect device changes. */
@@ -37763,6 +38008,9 @@ static ma_result ma_open_stream_basic__aaudio(ma_context* pContext, const ma_dev
         return result;
     }
 
+    /* Let's give AAudio a hint to avoid the legacy path (AudioStreamLegacy). */
+    ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+
     return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream);
 }
 
@@ -37787,6 +38035,10 @@ static ma_result ma_open_stream__aaudio(ma_device* pDevice, const ma_device_conf
 
 static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream)
 {
+    if (pStream == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
     return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream));
 }
 
@@ -37913,20 +38165,36 @@ static ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_dev
     return MA_SUCCESS;
 }
 
+static ma_result ma_close_streams__aaudio(ma_device* pDevice)
+{
+    MA_ASSERT(pDevice != NULL);
+
+    /* When re-routing, streams may have been closed and never re-opened. Hence the extra checks below. */
+    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
+        ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
+        pDevice->aaudio.pStreamCapture = NULL;
+    }
+    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
+        ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
+        pDevice->aaudio.pStreamPlayback = NULL;
+    }
+
+    return MA_SUCCESS;
+}
 
 static ma_result ma_device_uninit__aaudio(ma_device* pDevice)
 {
     MA_ASSERT(pDevice != NULL);
 
-    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
-        ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
-        pDevice->aaudio.pStreamCapture = NULL;
+    /* Wait for any rerouting to finish before attempting to close the streams. */
+    ma_mutex_lock(&pDevice->aaudio.rerouteLock);
+    {
+        ma_close_streams__aaudio(pDevice);
     }
+    ma_mutex_unlock(&pDevice->aaudio.rerouteLock);
 
-    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
-        ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
-        pDevice->aaudio.pStreamPlayback = NULL;
-    }
+    /* Destroy re-routing lock. */
+    ma_mutex_uninit(&pDevice->aaudio.rerouteLock);
 
     return MA_SUCCESS;
 }
@@ -37978,7 +38246,7 @@ static ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_dev
     return MA_SUCCESS;
 }
 
-static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
+static ma_result ma_device_init_streams__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
 {
     ma_result result;
 
@@ -38011,6 +38279,25 @@ static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_conf
     return MA_SUCCESS;
 }
 
+static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
+{
+    ma_result result;
+
+    MA_ASSERT(pDevice != NULL);
+
+    result = ma_device_init_streams__aaudio(pDevice, pConfig, pDescriptorPlayback, pDescriptorCapture);
+    if (result != MA_SUCCESS) {
+        return result;
+    }
+
+    result = ma_mutex_init(&pDevice->aaudio.rerouteLock);
+    if (result != MA_SUCCESS) {
+        return result;
+    }
+
+    return MA_SUCCESS;
+}
+
 static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
 {
     ma_aaudio_result_t resultAA;
@@ -38018,12 +38305,16 @@ static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStr
 
     MA_ASSERT(pDevice != NULL);
 
+    if (pStream == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
     resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream);
     if (resultAA != MA_AAUDIO_OK) {
         return ma_result_from_aaudio(resultAA);
     }
 
-    /* Do we actually need to wait for the device to transition into it's started state? */
+    /* Do we actually need to wait for the device to transition into its started state? */
 
     /* The device should be in either a starting or started state. If it's not set to started we need to wait for it to transition. It should go from starting to started. */
     currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
@@ -38050,6 +38341,10 @@ static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStre
 
     MA_ASSERT(pDevice != NULL);
 
+    if (pStream == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
     /*
     From the AAudio documentation:
 
@@ -38135,22 +38430,20 @@ static ma_result ma_device_stop__aaudio(ma_device* pDevice)
 static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType)
 {
     ma_result result;
+    int32_t retries = 0;
 
     MA_ASSERT(pDevice != NULL);
 
-    /* The first thing to do is close the streams. */
-    if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
-        ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
-        pDevice->aaudio.pStreamCapture = NULL;
-    }
-
-    if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
-        ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
-        pDevice->aaudio.pStreamPlayback = NULL;
-    }
-
-    /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */
+    /*
+     TODO: Stop retrying if main thread is about to uninit device.
+    */
+    ma_mutex_lock(&pDevice->aaudio.rerouteLock);
     {
+error_disconnected:
+        /* The first thing to do is close the streams. */
+        ma_close_streams__aaudio(pDevice);
+
+        /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */
         ma_device_config deviceConfig;
         ma_device_descriptor descriptorPlayback;
         ma_device_descriptor descriptorCapture;
@@ -38199,15 +38492,17 @@ static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type dev
             descriptorPlayback.periodCount        = deviceConfig.periods;
         }
 
-        result = ma_device_init__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture);
+        result = ma_device_init_streams__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture);
         if (result != MA_SUCCESS) {
-            return result;
+            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to create stream after route change.");
+            goto done;
         }
 
         result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture);
         if (result != MA_SUCCESS) {
-            ma_device_uninit__aaudio(pDevice);
-            return result;
+            ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to initialize device after route change.");
+            ma_close_streams__aaudio(pDevice);
+            goto done;
         }
 
         /* We'll only ever do this in response to a reroute. */
@@ -38216,14 +38511,29 @@ static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type dev
         /* If the device is started, start the streams. Maybe make this configurable? */
         if (ma_device_get_state(pDevice) == ma_device_state_started) {
             if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) {
-                ma_device_start__aaudio(pDevice);
+                result = ma_device_start__aaudio(pDevice);
+                if (result != MA_SUCCESS) {
+                    /* We got disconnected! Retry a few times, until we find a connected device! */
+                    retries += 1;
+                    if (retries <= 3) {
+                        ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change, retrying(%d)", retries);
+                        goto error_disconnected;
+                    }
+                    ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change.");
+                    goto done;
+                }
             } else {
                 ma_device_stop(pDevice);    /* Do a full device stop so we set internal state correctly. */
             }
         }
-
-        return MA_SUCCESS;
+        
+        result = MA_SUCCESS;
     }
+done:
+    /* Re-routing done */
+    ma_mutex_unlock(&pDevice->aaudio.rerouteLock);
+
+    return result;
 }
 
 static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo)
@@ -38234,12 +38544,12 @@ static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type t
     MA_ASSERT(type        != ma_device_type_duplex);
     MA_ASSERT(pDeviceInfo != NULL);
 
-    if (type == ma_device_type_playback) {
+    if (type == ma_device_type_capture) {
         pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture;
         pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio;
         ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);     /* Only supporting default devices. */
     }
-    if (type == ma_device_type_capture) {
+    if (type == ma_device_type_playback) {
         pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback;
         pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio;
         ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);    /* Only supporting default devices. */
@@ -38272,6 +38582,7 @@ static ma_result ma_context_uninit__aaudio(ma_context* pContext)
 
 static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
 {
+#if !defined(MA_NO_RUNTIME_LINKING)
     size_t i;
     const char* libNames[] = {
         "libaaudio.so"
@@ -38317,7 +38628,39 @@ static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_
     pContext->aaudio.AAudioStream_getFramesPerBurst                = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst");
     pContext->aaudio.AAudioStream_requestStart                     = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStart");
     pContext->aaudio.AAudioStream_requestStop                      = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStop");
-
+#else
+    pContext->aaudio.AAudio_createStreamBuilder                    = (ma_proc)AAudio_createStreamBuilder;
+    pContext->aaudio.AAudioStreamBuilder_delete                    = (ma_proc)AAudioStreamBuilder_delete;
+    pContext->aaudio.AAudioStreamBuilder_setDeviceId               = (ma_proc)AAudioStreamBuilder_setDeviceId;
+    pContext->aaudio.AAudioStreamBuilder_setDirection              = (ma_proc)AAudioStreamBuilder_setDirection;
+    pContext->aaudio.AAudioStreamBuilder_setSharingMode            = (ma_proc)AAudioStreamBuilder_setSharingMode;
+    pContext->aaudio.AAudioStreamBuilder_setFormat                 = (ma_proc)AAudioStreamBuilder_setFormat;
+    pContext->aaudio.AAudioStreamBuilder_setChannelCount           = (ma_proc)AAudioStreamBuilder_setChannelCount;
+    pContext->aaudio.AAudioStreamBuilder_setSampleRate             = (ma_proc)AAudioStreamBuilder_setSampleRate;
+    pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)AAudioStreamBuilder_setBufferCapacityInFrames;
+    pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback  = (ma_proc)AAudioStreamBuilder_setFramesPerDataCallback;
+    pContext->aaudio.AAudioStreamBuilder_setDataCallback           = (ma_proc)AAudioStreamBuilder_setDataCallback;
+    pContext->aaudio.AAudioStreamBuilder_setErrorCallback          = (ma_proc)AAudioStreamBuilder_setErrorCallback;
+    pContext->aaudio.AAudioStreamBuilder_setPerformanceMode        = (ma_proc)AAudioStreamBuilder_setPerformanceMode;
+    pContext->aaudio.AAudioStreamBuilder_setUsage                  = (ma_proc)AAudioStreamBuilder_setUsage;
+    pContext->aaudio.AAudioStreamBuilder_setContentType            = (ma_proc)AAudioStreamBuilder_setContentType;
+    pContext->aaudio.AAudioStreamBuilder_setInputPreset            = (ma_proc)AAudioStreamBuilder_setInputPreset;
+    #if defined(__ANDROID_API__) && __ANDROID_API__ >= 29
+    pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy   = (ma_proc)AAudioStreamBuilder_setAllowedCapturePolicy;
+    #endif
+    pContext->aaudio.AAudioStreamBuilder_openStream                = (ma_proc)AAudioStreamBuilder_openStream;
+    pContext->aaudio.AAudioStream_close                            = (ma_proc)AAudioStream_close;
+    pContext->aaudio.AAudioStream_getState                         = (ma_proc)AAudioStream_getState;
+    pContext->aaudio.AAudioStream_waitForStateChange               = (ma_proc)AAudioStream_waitForStateChange;
+    pContext->aaudio.AAudioStream_getFormat                        = (ma_proc)AAudioStream_getFormat;
+    pContext->aaudio.AAudioStream_getChannelCount                  = (ma_proc)AAudioStream_getChannelCount;
+    pContext->aaudio.AAudioStream_getSampleRate                    = (ma_proc)AAudioStream_getSampleRate;
+    pContext->aaudio.AAudioStream_getBufferCapacityInFrames        = (ma_proc)AAudioStream_getBufferCapacityInFrames;
+    pContext->aaudio.AAudioStream_getFramesPerDataCallback         = (ma_proc)AAudioStream_getFramesPerDataCallback;
+    pContext->aaudio.AAudioStream_getFramesPerBurst                = (ma_proc)AAudioStream_getFramesPerBurst;
+    pContext->aaudio.AAudioStream_requestStart                     = (ma_proc)AAudioStream_requestStart;
+    pContext->aaudio.AAudioStream_requestStop                      = (ma_proc)AAudioStream_requestStop;
+#endif
 
     pCallbacks->onContextInit             = ma_context_init__aaudio;
     pCallbacks->onContextUninit           = ma_context_uninit__aaudio;
@@ -38355,6 +38698,7 @@ static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_
 
 static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob)
 {
+    ma_result result;
     ma_device* pDevice;
 
     MA_ASSERT(pJob != NULL);
@@ -38363,7 +38707,18 @@ static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob)
     MA_ASSERT(pDevice != NULL);
 
     /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */
-    return ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType);
+    result = ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType);
+    if (result != MA_SUCCESS) {
+        /*
+        Getting here means we failed to reroute the device. The best thing I can think of here is to
+        just stop the device.
+        */
+        ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[AAudio] Stopping device due to reroute failure.");
+        ma_device_stop(pDevice);
+        return result;
+    }
+
+    return MA_SUCCESS;
 }
 #else
 /* Getting here means there is no AAudio backend so we need a no-op job implementation. */
@@ -39649,6 +40004,10 @@ Web Audio Backend
 #if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 32)))
     #include 
     #define MA_SUPPORT_AUDIO_WORKLETS
+
+    #if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 70)))
+        #define MA_SUPPORT_AUDIO_WORKLETS_VARIABLE_BUFFER_SIZE
+    #endif
 #endif
 
 /*
@@ -39660,7 +40019,7 @@ TODO: Version 0.12: Swap this logic around so that AudioWorklets are used by def
 
 /* The thread stack size must be a multiple of 16. */
 #ifndef MA_AUDIO_WORKLETS_THREAD_STACK_SIZE
-#define MA_AUDIO_WORKLETS_THREAD_STACK_SIZE 16384
+#define MA_AUDIO_WORKLETS_THREAD_STACK_SIZE     131072
 #endif
 
 #if defined(MA_USE_AUDIO_WORKLETS)
@@ -39786,12 +40145,14 @@ static ma_result ma_device_uninit__webaudio(ma_device* pDevice)
     #if defined(MA_USE_AUDIO_WORKLETS)
     {
         EM_ASM({
-            var device = miniaudio.get_device_by_index($0);
+            var device = window.miniaudio.get_device_by_index($0);
 
             if (device.streamNode !== undefined) {
                 device.streamNode.disconnect();
                 device.streamNode = undefined;
             }
+
+            device.pDevice = undefined;
         }, pDevice->webaudio.deviceIndex);
 
         emscripten_destroy_web_audio_node(pDevice->webaudio.audioWorklet);
@@ -39801,7 +40162,7 @@ static ma_result ma_device_uninit__webaudio(ma_device* pDevice)
     #else
     {
         EM_ASM({
-            var device = miniaudio.get_device_by_index($0);
+            var device = window.miniaudio.get_device_by_index($0);
 
             /* Make sure all nodes are disconnected and marked for collection. */
             if (device.scriptNode !== undefined) {
@@ -39828,7 +40189,7 @@ static ma_result ma_device_uninit__webaudio(ma_device* pDevice)
 
     /* Clean up the device on the JS side. */
     EM_ASM({
-        miniaudio.untrack_device_by_index($0);
+        window.miniaudio.untrack_device_by_index($0);
     }, pDevice->webaudio.deviceIndex);
 
     ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks);
@@ -39894,10 +40255,6 @@ static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const
     (void)paramCount;
     (void)pParams;
 
-    if (ma_device_get_state(pDevice) != ma_device_state_started) {
-        return EM_TRUE;
-    }
-
     /*
     The Emscripten documentation says that it'll always be 128 frames being passed in. Hard coding it like that feels
     like a very bad idea to me. Even if it's hard coded in the backend, the API and documentation should always refer
@@ -39906,7 +40263,20 @@ static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const
     Unfortunately the audio data is not interleaved so we'll need to convert it before we give the data to miniaudio
     for further processing.
     */
-    frameCount = 128;
+    if (pDevice->type == ma_device_type_playback) {
+        frameCount = pDevice->playback.internalPeriodSizeInFrames;
+    } else {
+        frameCount = pDevice->capture.internalPeriodSizeInFrames;
+    }
+
+    if (ma_device_get_state(pDevice) != ma_device_state_started) {
+        /* Fill the output buffer with zero to avoid a noise sound */
+        for (int i = 0; i < outputCount; i += 1) {
+            MA_ZERO_MEMORY(pOutputs[i].data, pOutputs[i].numberOfChannels * frameCount * sizeof(float));
+        }
+
+        return EM_TRUE;
+    }
 
     if (inputCount > 0) {
         /* Input data needs to be interleaved before we hand it to the client. */
@@ -39961,7 +40331,7 @@ static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T a
     count from MediaStreamAudioSourceNode (what we use for capture)? The only way to have control is to configure an
     output channel count on the capture side. This is slightly confusing for capture mode because intuitively you
     wouldn't actually connect an output to an input-only node, but this is what we'll have to do in order to have
-    proper control over the channel count. In the capture case, we'll have to output silence to it's output node.
+    proper control over the channel count. In the capture case, we'll have to output silence to its output node.
     */
     if (pParameters->pConfig->deviceType == ma_device_type_capture) {
         channels = (int)((pParameters->pDescriptorCapture->channels > 0) ? pParameters->pDescriptorCapture->channels : MA_DEFAULT_CHANNELS);
@@ -39984,7 +40354,15 @@ static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T a
     Now that we know the channel count to use we can allocate the intermediary buffer. The
     intermediary buffer is used for interleaving and deinterleaving.
     */
-    intermediaryBufferSizeInFrames = 128;
+    #if defined(MA_SUPPORT_AUDIO_WORKLETS_VARIABLE_BUFFER_SIZE)
+    {
+        intermediaryBufferSizeInFrames = (size_t)emscripten_audio_context_quantum_size(audioContext);
+    }
+    #else
+    {
+        intermediaryBufferSizeInFrames = 128;
+    }
+    #endif
 
     pParameters->pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(intermediaryBufferSizeInFrames * (ma_uint32)channels * sizeof(float), &pParameters->pDevice->pContext->allocationCallbacks);
     if (pParameters->pDevice->webaudio.pIntermediaryBuffer == NULL) {
@@ -39993,7 +40371,6 @@ static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T a
         return;
     }
 
-
     pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice);
 
     /* With the audio worklet initialized we can now attach it to the graph. */
@@ -40133,7 +40510,6 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
         /* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */
         pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes);
 
-
         /*
         With the context created we can now create the worklet. We can only have a single worklet per audio
         context which means we'll need to craft this appropriately to handle duplex devices correctly.
@@ -40182,11 +40558,12 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
 
         /* We need to add an entry to the miniaudio.devices list on the JS side so we can do some JS/C interop. */
         pDevice->webaudio.deviceIndex = EM_ASM_INT({
-            return miniaudio.track_device({
+            return window.miniaudio.track_device({
                 webaudio: emscriptenGetAudioObject($0),
-                state:    1 /* 1 = ma_device_state_stopped */
+                state:    1, /* 1 = ma_device_state_stopped */
+                pDevice: $1
             });
-        }, pDevice->webaudio.audioContext);
+        }, pDevice->webaudio.audioContext, pDevice);
 
         return MA_SUCCESS;
     }
@@ -40198,7 +40575,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
         ma_uint32 sampleRate;
         ma_uint32 periodSizeInFrames;
 
-        /* The channel count will depend on the device type. If it's a capture, use it's, otherwise use the playback side. */
+        /* The channel count will depend on the device type. If it's a capture, use its, otherwise use the playback side. */
         if (pConfig->deviceType == ma_device_type_capture) {
             channels = (pDescriptorCapture->channels  > 0) ? pDescriptorCapture->channels  : MA_DEFAULT_CHANNELS;
         } else {
@@ -40267,11 +40644,11 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
             /* The node processing callback. */
             device.scriptNode.onaudioprocess = function(e) {
                 if (device.intermediaryBufferView == null || device.intermediaryBufferView.length == 0) {
-                    device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels);
+                    device.intermediaryBufferView = new Float32Array(HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels);
                 }
 
                 /* Do the capture side first. */
-                if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) {
+                if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) {
                     /* The data must be interleaved before being processed miniaudio. */
                     for (var iChannel = 0; iChannel < channels; iChannel += 1) {
                         var inputBuffer = e.inputBuffer.getChannelData(iChannel);
@@ -40285,7 +40662,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
                     _ma_device_process_pcm_frames_capture__webaudio(pDevice, bufferSize, pIntermediaryBuffer);
                 }
 
-                if (deviceType == miniaudio.device_type.playback || deviceType == miniaudio.device_type.duplex) {
+                if (deviceType == window.miniaudio.device_type.playback || deviceType == window.miniaudio.device_type.duplex) {
                     _ma_device_process_pcm_frames_playback__webaudio(pDevice, bufferSize, pIntermediaryBuffer);
 
                     for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
@@ -40305,7 +40682,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
             };
 
             /* Now we need to connect our node to the graph. */
-            if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) {
+            if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) {
                 navigator.mediaDevices.getUserMedia({audio:true, video:false})
                     .then(function(stream) {
                         device.streamNode = device.webaudio.createMediaStreamSource(stream);
@@ -40317,13 +40694,13 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
                     });
             }
 
-            if (deviceType == miniaudio.device_type.playback) {
+            if (deviceType == window.miniaudio.device_type.playback) {
                 device.scriptNode.connect(device.webaudio.destination);
             }
 
             device.pDevice = pDevice;
 
-            return miniaudio.track_device(device);
+            return window.miniaudio.track_device(device);
         }, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice);
 
         if (deviceIndex < 0) {
@@ -40333,7 +40710,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co
         pDevice->webaudio.deviceIndex = deviceIndex;
 
         /* Grab the sample rate from the audio context directly. */
-        sampleRate = (ma_uint32)EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex);
+        sampleRate = (ma_uint32)EM_ASM_INT({ return window.miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex);
 
         if (pDescriptorCapture != NULL) {
             pDescriptorCapture->format              = ma_format_f32;
@@ -40363,9 +40740,9 @@ static ma_result ma_device_start__webaudio(ma_device* pDevice)
     MA_ASSERT(pDevice != NULL);
 
     EM_ASM({
-        var device = miniaudio.get_device_by_index($0);
+        var device = window.miniaudio.get_device_by_index($0);
         device.webaudio.resume();
-        device.state = miniaudio.device_state.started;
+        device.state = window.miniaudio.device_state.started;
     }, pDevice->webaudio.deviceIndex);
 
     return MA_SUCCESS;
@@ -40385,9 +40762,9 @@ static ma_result ma_device_stop__webaudio(ma_device* pDevice)
     do any kind of explicit draining.
     */
     EM_ASM({
-        var device = miniaudio.get_device_by_index($0);
+        var device = window.miniaudio.get_device_by_index($0);
         device.webaudio.suspend();
-        device.state = miniaudio.device_state.stopped;
+        device.state = window.miniaudio.device_state.stopped;
     }, pDevice->webaudio.deviceIndex);
 
     ma_device__on_notification_stopped(pDevice);
@@ -40405,6 +40782,10 @@ static ma_result ma_context_uninit__webaudio(ma_context* pContext)
     /* Remove the global miniaudio object from window if there are no more references to it. */
     EM_ASM({
         if (typeof(window.miniaudio) !== 'undefined') {
+            miniaudio.unlock_event_types.map(function(event_type) {
+                document.removeEventListener(event_type, miniaudio.unlock, true);
+            });
+
             window.miniaudio.referenceCount -= 1;
             if (window.miniaudio.referenceCount === 0) {
                 delete window.miniaudio;
@@ -40446,6 +40827,7 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex
             window.miniaudio.device_state.started = $4;
 
             /* Device cache for mapping devices to indexes for JavaScript/C interop. */
+            let miniaudio = window.miniaudio;
             miniaudio.devices = [];
 
             miniaudio.track_device = function(device) {
@@ -40497,13 +40879,13 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex
                     var device = miniaudio.devices[i];
                     if (device != null &&
                         device.webaudio != null &&
-                        device.state === window.miniaudio.device_state.started) {
+                        device.state === miniaudio.device_state.started) {
 
                         device.webaudio.resume().then(() => {
-                                Module._ma_device__on_notification_unlocked(device.pDevice);
-                            },
-                            (error) => {console.error("Failed to resume audiocontext", error);
-                            });
+                            _ma_device__on_notification_unlocked(device.pDevice);
+                        },
+                        (error) => {console.error("Failed to resume audiocontext", error);
+                        });
                     }
                 }
                 miniaudio.unlock_event_types.map(function(event_type) {
@@ -40539,7 +40921,7 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex
 
     return MA_SUCCESS;
 }
-#endif  /* Web Audio */
+#endif  /* MA_HAS_WEBAUDIO */
 
 
 
@@ -40818,7 +41200,7 @@ MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceTy
         ma_device_info deviceInfo;
 
         if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
-            result = ma_device_get_info(pDevice, (deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo);
+            result = ma_device_get_info(pDevice, ma_device_type_capture, &deviceInfo);
             if (result == MA_SUCCESS) {
                 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1);
             } else {
@@ -40865,7 +41247,7 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData)
 #endif
 
     /*
-    When the device is being initialized it's initial state is set to ma_device_state_uninitialized. Before returning from
+    When the device is being initialized its initial state is set to ma_device_state_uninitialized. Before returning from
     ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately
     after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker
     thread to signal an event to know when the worker thread is ready for action.
@@ -41210,6 +41592,24 @@ MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_
 }
 
 
+MA_API ma_bool32 ma_device_id_equal(const ma_device_id* pA, const ma_device_id* pB)
+{
+    size_t i;
+
+    if (pA == NULL || pB == NULL) {
+        return MA_FALSE;
+    }
+
+    for (i = 0; i < sizeof(ma_device_id); i += 1) {
+        if (((const char*)pA)[i] != ((const char*)pB)[i]) {
+            return MA_FALSE;
+        }
+    }
+
+    return MA_TRUE;
+}
+
+
 
 MA_API ma_context_config ma_context_config_init(void)
 {
@@ -41983,7 +42383,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC
             return result;
         }
 
-        /* Wait for the worker thread to put the device into it's stopped state for real. */
+        /* Wait for the worker thread to put the device into its stopped state for real. */
         ma_event_wait(&pDevice->stopEvent);
         MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
     } else {
@@ -42009,7 +42409,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC
         ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend));
         if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
             char name[MA_MAX_DEVICE_NAME_LENGTH + 1];
-            ma_device_get_name(pDevice, (pDevice->type == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, name, sizeof(name), NULL);
+            ma_device_get_name(pDevice, ma_device_type_capture, name, sizeof(name), NULL);
 
             ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "  %s (%s)\n", name, "Capture");
             ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "    Format:      %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format));
@@ -42262,6 +42662,17 @@ MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_
     if (type == ma_device_type_playback) {
         return ma_context_get_device_info(pDevice->pContext, type, pDevice->playback.pID, pDeviceInfo);
     } else {
+        /*
+        Here we're getting the capture side, which is the branch we'll be entering for a loopback
+        device, since loopback is capturing. However, if the device is using the default device ID,
+        it won't get the correct information because it'll think we're asking for the default
+        capture device, where in fact for loopback we want the default *playback* device. We'll do
+        a bit of a hack here to make sure we get the correct info.
+        */
+        if (pDevice->type == ma_device_type_loopback && pDevice->capture.pID == NULL) {
+            type = ma_device_type_playback;
+        }
+
         return ma_context_get_device_info(pDevice->pContext, type, pDevice->capture.pID, pDeviceInfo);
     }
 }
@@ -42323,6 +42734,15 @@ MA_API ma_result ma_device_start(ma_device* pDevice)
 
     ma_mutex_lock(&pDevice->startStopLock);
     {
+        /*
+        We need to check again if the device is in a started state because it's possible for one thread to have started the device
+        while another was waiting on the mutex.
+        */
+        if (ma_device_get_state(pDevice) == ma_device_state_started) {
+            ma_mutex_unlock(&pDevice->startStopLock);
+            return MA_SUCCESS;  /* Already started. */
+        }
+
         /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */
         MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
 
@@ -42383,6 +42803,15 @@ MA_API ma_result ma_device_stop(ma_device* pDevice)
 
     ma_mutex_lock(&pDevice->startStopLock);
     {
+        /*
+        We need to check again if the device is in a stopped state because it's possible for one thread to have stopped the device
+        while another was waiting on the mutex.
+        */
+        if (ma_device_get_state(pDevice) == ma_device_state_stopped) {
+            ma_mutex_unlock(&pDevice->startStopLock);
+            return MA_SUCCESS;  /* Already stopped. */
+        }
+
         /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */
         MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started);
 
@@ -42401,7 +42830,7 @@ MA_API ma_result ma_device_stop(ma_device* pDevice)
         } else {
             /*
             Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If
-            the backend is implementing it's own audio thread loop we'll need to wake it up if required. Note that we need to make
+            the backend is implementing its own audio thread loop we'll need to wake it up if required. Note that we need to make
             sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super
             important though, so I'm asserting it here as well for extra safety in case we accidentally change something later.
             */
@@ -42518,6 +42947,15 @@ MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void
         return MA_INVALID_ARGS;
     }
 
+    /*
+    There is an assert deeper in the code that checks that frameCount > 0. Since this is a public facing
+    API we'll need to check for that here. I've had reports that AAudio can sometimes post a frame count
+    of 0.
+    */
+    if (frameCount == 0) {
+        return MA_INVALID_ARGS;
+    }
+
     if (pDevice->type == ma_device_type_duplex) {
         if (pInput != NULL) {
             ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb);
@@ -42592,7 +43030,7 @@ MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32
         return 0;
     }
 
-    return bufferSizeInFrames*1000 / sampleRate;
+    return (bufferSizeInFrames*1000 + (sampleRate - 1)) / sampleRate;
 }
 
 MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate)
@@ -47420,7 +47858,7 @@ static ma_result ma_bpf_get_heap_layout(const ma_bpf_config* pConfig, ma_bpf_hea
         return MA_INVALID_ARGS;
     }
 
-    bpf2Count = pConfig->channels / 2;
+    bpf2Count = pConfig->order / 2;
 
     pHeapLayout->sizeInBytes = 0;
 
@@ -49478,7 +49916,7 @@ MA_API float ma_fader_get_current_volume(const ma_fader* pFader)
     } else if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) {   /* Safe case because the < 0 case was checked above. */
         return pFader->volumeEnd;
     } else {
-        /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpoluation between volumeBeg and volumeEnd based on our cursor position. */
+        /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpolation between volumeBeg and volumeEnd based on our cursor position. */
         return ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, (ma_uint32)pFader->cursorInFrames / (float)((ma_uint32)pFader->lengthInFrames));    /* Safe cast to uint32 because we clamp it in ma_fader_process_pcm_frames(). */
     }
 }
@@ -49701,9 +50139,9 @@ static float ma_attenuation_exponential(float distance, float minDistance, float
 
 
 /*
-Dopper Effect calculation taken from the OpenAL spec, with two main differences:
+Doppler Effect calculation taken from the OpenAL spec, with two main differences:
 
-  1) The source to listener vector will have already been calcualted at an earlier step so we can
+  1) The source to listener vector will have already been calculated at an earlier step so we can
      just use that directly. We need only the position of the source relative to the origin.
 
   2) We don't scale by a frequency because we actually just want the ratio which we'll plug straight
@@ -49742,7 +50180,7 @@ static void ma_get_default_channel_map_for_spatializer(ma_channel* pChannelMap,
     Special case for stereo. Want to default the left and right speakers to side left and side
     right so that they're facing directly down the X axis rather than slightly forward. Not
     doing this will result in sounds being quieter when behind the listener. This might
-    actually be good for some scenerios, but I don't think it's an appropriate default because
+    actually be good for some scenarios, but I don't think it's an appropriate default because
     it can be a bit unexpected.
     */
     if (channelCount == 2) {
@@ -50076,7 +50514,7 @@ MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma
     config.maxDistance                  = MA_FLT_MAX;
     config.rolloff                      = 1;
     config.coneInnerAngleInRadians      = 6.283185f; /* 360 degrees. */
-    config.coneOuterAngleInRadians      = 6.283185f; /* 360 degress. */
+    config.coneOuterAngleInRadians      = 6.283185f; /* 360 degrees. */
     config.coneOuterGain                = 0.0f;
     config.dopplerFactor                = 1;
     config.directionalAttenuationFactor = 1;
@@ -50310,7 +50748,7 @@ static float ma_calculate_angular_gain(ma_vec3f dirA, ma_vec3f dirB, float coneI
     To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We
     just need to get the direction from the source to the listener and then do a dot product against that and the
     direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer
-    angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than
+    angles. If the dot product is greater than the outer angle, we just use coneOuterGain. If it's less than
     the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain.
     */
     if (coneInnerAngleInRadians < 6.283185f) {
@@ -50380,7 +50818,7 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer,
         ma_vec3f relativePosNormalized;
         ma_vec3f relativePos;   /* The position relative to the listener. */
         ma_vec3f relativeDir;   /* The direction of the sound, relative to the listener. */
-        ma_vec3f listenerVel;   /* The volocity of the listener. For doppler pitch calculation. */
+        ma_vec3f listenerVel;   /* The velocity of the listener. For doppler pitch calculation. */
         float speedOfSound;
         float distance = 0;
         float gain = 1;
@@ -50461,11 +50899,11 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer,
         To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We
         just need to get the direction from the source to the listener and then do a dot product against that and the
         direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer
-        angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than
+        angles. If the dot product is greater than the outer angle, we just use coneOuterGain. If it's less than
         the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain.
         */
         if (distance > 0) {
-            /* Source anglular gain. */
+            /* Source angular gain. */
             float spatializerConeInnerAngle;
             float spatializerConeOuterAngle;
             float spatializerConeOuterGain;
@@ -50977,7 +51415,7 @@ MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatiali
         listenerDirection    = ma_spatializer_listener_get_direction(pListener);
 
         /*
-        We need to calcualte the right vector from our forward and up vectors. This is done with
+        We need to calculate the right vector from our forward and up vectors. This is done with
         a cross product.
         */
         axisZ = ma_vec3f_normalize(listenerDirection);                                  /* Normalization required here because we can't trust the caller. */
@@ -51123,7 +51561,7 @@ static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pRes
     lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder);
 
     /*
-    If the resampler is alreay initialized we don't want to do a fresh initialization of the low-pass filter because it will result in the cached frames
+    If the resampler is already initialized we don't want to do a fresh initialization of the low-pass filter because it will result in the cached frames
     getting cleared. Instead we re-initialize the filter which will maintain any cached frames.
     */
     if (isResamplerAlreadyInitialized) {
@@ -51818,7 +52256,7 @@ MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_li
     preliminaryInputFrameCount         = (pResampler->inTimeInt  + outputFrameCount*pResampler->inAdvanceInt ) + preliminaryInputFrameCountFromFrac;
 
     /*
-    If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greather than
+    If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greater than
     the amount of whole input frames we have available as input we need to *not* add an extra output frame as there won't be enough data
     to actually process. Otherwise we need to add the extra output frame.
     */
@@ -51856,7 +52294,7 @@ MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler)
         }
     }
 
-    /* The low pass filter needs to have it's cache reset. */
+    /* The low pass filter needs to have its cache reset. */
     ma_lpf_clear_cache(&pResampler->lpf);
 
     return MA_SUCCESS;
@@ -52373,19 +52811,19 @@ static float ma_calculate_channel_position_rectangular_weight(ma_channel channel
     of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated.
 
     Imagine the front/left speaker as emitting audio from two planes - the front plane and the left plane. You can think of the front/left
-    speaker emitting half of it's total volume from the front, and the other half from the left. Since part of it's volume is being emitted
+    speaker emitting half of its total volume from the front, and the other half from the left. Since part of its volume is being emitted
     from the left side, and the side/left and back/left channels also emit audio from the left plane, one would expect that they would
     receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between
     the two speakers. Note that in the examples below I've added a top/front/left speaker as an example just to show how the math works
     across 3 spatial dimensions.
 
     The first thing to do is figure out how each speaker's volume is spread over each of plane:
-     - front/left:     2 planes (front and left)      = 1/2 = half it's total volume on each plane
+     - front/left:     2 planes (front and left)      = 1/2 = half its total volume on each plane
      - side/left:      1 plane (left only)            = 1/1 = entire volume from left plane
-     - back/left:      2 planes (back and left)       = 1/2 = half it's total volume on each plane
-     - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane
+     - back/left:      2 planes (back and left)       = 1/2 = half its total volume on each plane
+     - top/front/left: 3 planes (top, front and left) = 1/3 = one third its total volume on each plane
 
-    The amount of volume each channel contributes to each of it's planes is what controls how much it is willing to given and take to other
+    The amount of volume each channel contributes to each of its planes is what controls how much it is willing to given and take to other
     channels on the same plane. The volume that is willing to the given by one channel is multiplied by the volume that is willing to be
     taken by the other to produce the final contribution.
     */
@@ -52496,12 +52934,7 @@ static ma_channel_conversion_path ma_channel_map_get_conversion_path(const ma_ch
         ma_uint32 iChannelIn;
         ma_bool32 areAllChannelPositionsPresent = MA_TRUE;
         for (iChannelIn = 0; iChannelIn < channelsIn; ++iChannelIn) {
-            ma_bool32 isInputChannelPositionInOutput = MA_FALSE;
-            if (ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn))) {
-                isInputChannelPositionInOutput = MA_TRUE;
-                break;
-            }
-
+            ma_bool32 isInputChannelPositionInOutput = ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn));
             if (!isInputChannelPositionInOutput) {
                 areAllChannelPositionsPresent = MA_FALSE;
                 break;
@@ -52528,8 +52961,8 @@ static ma_result ma_channel_map_build_shuffle_table(const ma_channel* pChannelMa
     }
 
     /*
-    When building the shuffle table we just do a 1:1 mapping based on the first occurance of a channel. If the
-    input channel has more than one occurance of a channel position, the second one will be ignored.
+    When building the shuffle table we just do a 1:1 mapping based on the first occurrence of a channel. If the
+    input channel has more than one occurrence of a channel position, the second one will be ignored.
     */
     for (iChannelOut = 0; iChannelOut < channelCountOut; iChannelOut += 1) {
         ma_channel channelOut;
@@ -54824,7 +55257,7 @@ static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_co
         Before doing any processing we need to determine how many frames we should try processing
         this iteration, for both input and output. The resampler requires us to perform format and
         channel conversion before passing any data into it. If we get our input count wrong, we'll
-        end up peforming redundant pre-processing. This isn't the end of the world, but it does
+        end up performing redundant pre-processing. This isn't the end of the world, but it does
         result in some inefficiencies proportionate to how far our estimates are off.
 
         If the resampler has a means to calculate exactly how much we'll need, we'll use that.
@@ -55994,7 +56427,7 @@ MA_API const char* ma_channel_position_to_string(ma_channel channel)
         case MA_CHANNEL_LFE               : return "CHANNEL_LFE";
         case MA_CHANNEL_BACK_LEFT         : return "CHANNEL_BACK_LEFT";
         case MA_CHANNEL_BACK_RIGHT        : return "CHANNEL_BACK_RIGHT";
-        case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER ";
+        case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER";
         case MA_CHANNEL_FRONT_RIGHT_CENTER: return "CHANNEL_FRONT_RIGHT_CENTER";
         case MA_CHANNEL_BACK_CENTER       : return "CHANNEL_BACK_CENTER";
         case MA_CHANNEL_SIDE_LEFT         : return "CHANNEL_SIDE_LEFT";
@@ -56299,13 +56732,9 @@ MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes)
         newReadOffsetLoopFlag ^= 0x80000000;
     }
 
-    ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes));
+    ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag));
 
-    if (ma_rb_pointer_distance(pRB) == 0) {
-        return MA_AT_END;
-    } else {
-        return MA_SUCCESS;
-    }
+    return MA_SUCCESS;
 }
 
 MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
@@ -56385,13 +56814,9 @@ MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes)
         newWriteOffsetLoopFlag ^= 0x80000000;
     }
 
-    ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes));
+    ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag));
 
-    if (ma_rb_pointer_distance(pRB) == 0) {
-        return MA_AT_END;
-    } else {
-        return MA_SUCCESS;
-    }
+    return MA_SUCCESS;
 }
 
 MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes)
@@ -56614,6 +57039,16 @@ static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, voi
         totalFramesRead += mappedFrameCount;
     }
 
+    /*
+    There is no notion of an "end" in a ring buffer. If we didn't have enough data to fill the requested frame
+    count we'll need to pad with silence. If we don't do this, totalFramesRead might equal 0 which will result
+    in the data source layer at a higher level translating this to MA_AT_END which is incorrect for a ring buffer.
+    */
+    if (totalFramesRead < frameCount) {
+        ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), (frameCount - totalFramesRead), pRB->format, pRB->channels);
+        totalFramesRead = frameCount;
+    }
+
     *pFramesRead = totalFramesRead;
     return MA_SUCCESS;
 }
@@ -57162,6 +57597,10 @@ MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_da
         return MA_INVALID_ARGS;
     }
 
+    if (pConfig->vtable == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
     pDataSourceBase->vtable           = pConfig->vtable;
     pDataSourceBase->rangeBegInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG;
     pDataSourceBase->rangeEndInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END;
@@ -57212,6 +57651,58 @@ static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_
     return MA_SUCCESS;
 }
 
+static ma_result ma_data_source_read_pcm_frames_from_backend(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
+{
+    ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
+
+    MA_ASSERT(pDataSourceBase                 != NULL);
+    MA_ASSERT(pDataSourceBase->vtable         != NULL);
+    MA_ASSERT(pDataSourceBase->vtable->onRead != NULL);
+    MA_ASSERT(pFramesRead != NULL);
+
+    if (pFramesOut != NULL) {
+        return pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, pFramesRead);
+    } else {
+        /*
+        No output buffer. Probably seeking forward. Read and discard. Can probably optimize this in terms of
+        onSeek and onGetCursor, but need to keep in mind that the data source may not implement these functions.
+        */
+        ma_result result;
+        ma_uint64 framesRead;
+        ma_format format;
+        ma_uint32 channels;
+        ma_uint64 discardBufferCapInFrames;
+        ma_uint8  pDiscardBuffer[4096];
+
+        result = ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0);
+        if (result != MA_SUCCESS) {
+            return result;
+        }
+
+        discardBufferCapInFrames = sizeof(pDiscardBuffer) / ma_get_bytes_per_frame(format, channels);
+
+        framesRead = 0;
+        while (framesRead < frameCount) {
+            ma_uint64 framesReadThisIteration = 0;
+            ma_uint64 framesToRead = frameCount - framesRead;
+            if (framesToRead > discardBufferCapInFrames) {
+                framesToRead = discardBufferCapInFrames;
+            }
+
+            result = pDataSourceBase->vtable->onRead(pDataSourceBase, pDiscardBuffer, framesToRead, &framesReadThisIteration);
+            if (result != MA_SUCCESS) {
+                return result;
+            }
+
+            framesRead += framesReadThisIteration;
+        }
+
+        *pFramesRead = framesRead;
+
+        return MA_SUCCESS;
+    }
+}
+
 static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
 {
     ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
@@ -57227,9 +57718,11 @@ static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDa
         return MA_INVALID_ARGS;
     }
 
+    MA_ASSERT(pDataSourceBase->vtable != NULL);
+
     if ((pDataSourceBase->vtable->flags & MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT) != 0 || (pDataSourceBase->rangeEndInFrames == ~((ma_uint64)0) && (pDataSourceBase->loopEndInFrames == ~((ma_uint64)0) || loop == MA_FALSE))) {
         /* Either the data source is self-managing the range, or no range is set - just read like normal. The data source itself will tell us when the end is reached. */
-        result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead);
+        result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead);
     } else {
         /* Need to clamp to within the range. */
         ma_uint64 relativeCursor;
@@ -57238,7 +57731,7 @@ static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDa
         result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &relativeCursor);
         if (result != MA_SUCCESS) {
             /* Failed to retrieve the cursor. Cannot read within a range or loop points. Just read like normal - this may happen for things like noise data sources where it doesn't really matter. */
-            result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead);
+            result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead);
         } else {
             ma_uint64 rangeBeg;
             ma_uint64 rangeEnd;
@@ -57266,7 +57759,7 @@ static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDa
             MA_AT_END so the higher level function can know about it.
             */
             if (frameCount > 0) {
-                result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead);
+                result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead);
             } else {
                 result = MA_AT_END; /* The cursor is sitting on the end of the range which means we're at the end. */
             }
@@ -57348,7 +57841,7 @@ MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, voi
         totalFramesProcessed += framesProcessed;
 
         /*
-        If we encounted an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is
+        If we encountered an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is
         not necessarily considered an error.
         */
         if (result != MA_SUCCESS && result != MA_AT_END) {
@@ -57439,7 +57932,7 @@ MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, m
     ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
 
     if (pDataSourceBase == NULL) {
-        return MA_SUCCESS;
+        return MA_INVALID_ARGS;
     }
 
     if (pDataSourceBase->vtable->onSeek == NULL) {
@@ -57447,12 +57940,61 @@ MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, m
     }
 
     if (frameIndex > pDataSourceBase->rangeEndInFrames) {
-        return MA_INVALID_OPERATION;    /* Trying to seek to far forward. */
+        return MA_INVALID_OPERATION;    /* Trying to seek too far forward. */
     }
 
+    MA_ASSERT(pDataSourceBase->vtable != NULL);
+
     return pDataSourceBase->vtable->onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex);
 }
 
+MA_API ma_result ma_data_source_seek_seconds(ma_data_source* pDataSource, float secondCount, float* pSecondsSeeked)
+{
+    ma_uint64 frameCount;
+    ma_uint64 framesSeeked = 0;
+    ma_uint32 sampleRate;
+    ma_result result;
+
+    if (pDataSource == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
+    result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0);
+    if (result != MA_SUCCESS) {
+        return result;
+    }
+
+    /* We need PCM frames instead of seconds */
+    frameCount = (ma_uint64)(secondCount * sampleRate);
+
+    result = ma_data_source_seek_pcm_frames(pDataSource, frameCount, &framesSeeked);
+
+    /* VC6 doesn't support division between unsigned 64-bit integer and floating point number. Signed integer needed. This shouldn't affect anything in practice */
+    *pSecondsSeeked = (ma_int64)framesSeeked / (float)sampleRate;
+    return result;
+}
+
+MA_API ma_result ma_data_source_seek_to_second(ma_data_source* pDataSource, float seekPointInSeconds)
+{
+    ma_uint64 frameIndex;
+    ma_uint32 sampleRate;
+    ma_result result;
+
+    if (pDataSource == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
+    result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0);
+    if (result != MA_SUCCESS) {
+        return result;
+    }
+
+    /* We need PCM frames instead of seconds */
+    frameIndex = (ma_uint64)(seekPointInSeconds * sampleRate);
+
+    return ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex);
+}
+
 MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
 {
     ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
@@ -57479,6 +58021,8 @@ MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_
         return MA_INVALID_ARGS;
     }
 
+    MA_ASSERT(pDataSourceBase->vtable != NULL);
+
     if (pDataSourceBase->vtable->onGetDataFormat == NULL) {
         return MA_NOT_IMPLEMENTED;
     }
@@ -57519,6 +58063,8 @@ MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSo
         return MA_SUCCESS;
     }
 
+    MA_ASSERT(pDataSourceBase->vtable != NULL);
+
     if (pDataSourceBase->vtable->onGetCursor == NULL) {
         return MA_NOT_IMPLEMENTED;
     }
@@ -57552,6 +58098,8 @@ MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSo
         return MA_INVALID_ARGS;
     }
 
+    MA_ASSERT(pDataSourceBase->vtable != NULL);
+
     /*
     If we have a range defined we'll use that to determine the length. This is one of rare times
     where we'll actually trust the caller. If they've set the range, I think it's mostly safe to
@@ -57639,6 +58187,8 @@ MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool
 
     ma_atomic_exchange_32(&pDataSourceBase->isLooping, isLooping);
 
+    MA_ASSERT(pDataSourceBase->vtable != NULL);
+
     /* If there's no callback for this just treat it as a successful no-op. */
     if (pDataSourceBase->vtable->onSetLooping == NULL) {
         return MA_SUCCESS;
@@ -57676,7 +58226,7 @@ MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSou
 
     /*
     We may need to adjust the position of the cursor to ensure it's clamped to the range. Grab it now
-    so we can calculate it's absolute position before we change the range.
+    so we can calculate its absolute position before we change the range.
     */
     result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &relativeCursor);
     if (result == MA_SUCCESS) {
@@ -57710,7 +58260,7 @@ MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSou
 
     /*
     Seek to within range. Note that our seek positions here are relative to the new range. We don't want
-    do do this if we failed to retrieve the cursor earlier on because it probably means the data source
+    to do this if we failed to retrieve the cursor earlier on because it probably means the data source
     has no notion of a cursor. In practice the seek would probably fail (which we silently ignore), but
     I'm just not even going to attempt it.
     */
@@ -57729,6 +58279,13 @@ MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSo
 {
     const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
 
+    if (pRangeBegInFrames != NULL) {
+        *pRangeBegInFrames = 0;
+    }
+    if (pRangeEndInFrames != NULL) {
+        *pRangeEndInFrames = 0;
+    }
+
     if (pDataSource == NULL) {
         return;
     }
@@ -57773,6 +58330,13 @@ MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pD
 {
     const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
 
+    if (pLoopBegInFrames != NULL) {
+        *pLoopBegInFrames = 0;
+    }
+    if (pLoopEndInFrames != NULL) {
+        *pLoopEndInFrames = 0;
+    }
+
     if (pDataSource == NULL) {
         return;
     }
@@ -59167,7 +59731,7 @@ static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_i
         result = ma_SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod);
     } else if (ma_SetFilePointer != NULL) {
         /* No SetFilePointerEx() so restrict to 31 bits. */
-        if (origin > 0x7FFFFFFF) {
+        if (offset > 0x7FFFFFFF) {
             return MA_OUT_OF_RANGE;
         }
 
@@ -59377,7 +59941,7 @@ static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_i
         result = _fseeki64((FILE*)file, offset, whence);
     #else
         /* No _fseeki64() so restrict to 31 bits. */
-        if (origin > 0x7FFFFFFF) {
+        if (offset > 0x7FFFFFFF) {
             return MA_OUT_OF_RANGE;
         }
 
@@ -59770,7 +60334,7 @@ extern "C" {
 #define MA_DR_WAV_XSTRINGIFY(x)     MA_DR_WAV_STRINGIFY(x)
 #define MA_DR_WAV_VERSION_MAJOR     0
 #define MA_DR_WAV_VERSION_MINOR     13
-#define MA_DR_WAV_VERSION_REVISION  13
+#define MA_DR_WAV_VERSION_REVISION  18
 #define MA_DR_WAV_VERSION_STRING    MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MAJOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MINOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_REVISION)
 #include 
 #define MA_DR_WAVE_FORMAT_PCM          0x1
@@ -60190,7 +60754,7 @@ extern "C" {
 #define MA_DR_FLAC_XSTRINGIFY(x)     MA_DR_FLAC_STRINGIFY(x)
 #define MA_DR_FLAC_VERSION_MAJOR     0
 #define MA_DR_FLAC_VERSION_MINOR     12
-#define MA_DR_FLAC_VERSION_REVISION  42
+#define MA_DR_FLAC_VERSION_REVISION  43
 #define MA_DR_FLAC_VERSION_STRING    MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MAJOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MINOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_REVISION)
 #include 
 #if defined(_MSC_VER) && _MSC_VER >= 1700
@@ -60477,7 +61041,7 @@ extern "C" {
 #define MA_DR_MP3_XSTRINGIFY(x)     MA_DR_MP3_STRINGIFY(x)
 #define MA_DR_MP3_VERSION_MAJOR     0
 #define MA_DR_MP3_VERSION_MINOR     6
-#define MA_DR_MP3_VERSION_REVISION  38
+#define MA_DR_MP3_VERSION_REVISION  40
 #define MA_DR_MP3_VERSION_STRING    MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MAJOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MINOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_REVISION)
 #include 
 #define MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME  1152
@@ -60639,7 +61203,7 @@ MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint3
     return config;
 }
 
-MA_API ma_decoder_config ma_decoder_config_init_default()
+MA_API ma_decoder_config ma_decoder_config_init_default(void)
 {
     return ma_decoder_config_init(ma_format_unknown, 0, 0);
 }
@@ -63232,7 +63796,7 @@ MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_
     #if !defined(MA_NO_VORBIS)
     {
         /*
-        stb_vorbis lacks a callback based API for it's pulling API which means we're stuck with the
+        stb_vorbis lacks a callback based API for its pulling API which means we're stuck with the
         pushing API. In order for us to be able to successfully initialize the decoder we need to
         supply it with enough data. We need to keep loading data until we have enough.
         */
@@ -63313,7 +63877,7 @@ MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, co
     {
         (void)pAllocationCallbacks;
 
-        /* stb_vorbis uses an int as it's size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */
+        /* stb_vorbis uses an int as its size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */
         if (dataSize > INT_MAX) {
             return MA_TOO_BIG;
         }
@@ -63403,7 +63967,7 @@ MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFram
                     /* The first thing to do is read from any already-cached frames. */
                     ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->push.framesRemaining, (frameCount - totalFramesRead));  /* Safe cast because pVorbis->framesRemaining is 32-bit. */
 
-                    /* The output pointer can be null in which case we just treate it as a seek. */
+                    /* The output pointer can be null in which case we just treat it as a seek. */
                     if (pFramesOut != NULL) {
                         ma_uint64 iFrame;
                         for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) {
@@ -63477,7 +64041,7 @@ MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFram
                         }
                     }
 
-                    /* If we don't have a success code at this point it means we've encounted an error or the end of the file has been reached (probably the latter). */
+                    /* If we don't have a success code at this point it means we've encountered an error or the end of the file has been reached (probably the latter). */
                     if (result != MA_SUCCESS) {
                         break;
                     }
@@ -64291,8 +64855,7 @@ MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, cons
 #if defined(MA_HAS_WAV)    || \
     defined(MA_HAS_MP3)    || \
     defined(MA_HAS_FLAC)   || \
-    defined(MA_HAS_VORBIS) || \
-    defined(MA_HAS_OPUS)
+    defined(MA_HAS_VORBIS)
 #define MA_HAS_PATH_API
 #endif
 
@@ -65107,7 +65670,7 @@ MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesO
     } else {
         /*
         Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we
-        need to run through each sample because we need to ensure it's internal cache is updated.
+        need to run through each sample because we need to ensure its internal cache is updated.
         */
         if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) {
             result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut);
@@ -65197,8 +65760,17 @@ MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesO
 
                     if (requiredInputFrameCount > 0) {
                         result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn);
+
+                        /*
+                        Note here that even if we've reached the end, we don't want to abort because there might be more output frames needing to be
+                        generated from cached input data, which might happen if resampling is being performed.
+                        */
+                        if (result != MA_SUCCESS && result != MA_AT_END) {
+                            break;
+                        }
                     } else {
                         framesReadThisIterationIn = 0;
+                        pIntermediaryBuffer[0] = 0; /* <-- This is just to silence a static analysis warning. */
                     }
 
                     /*
@@ -66679,7 +67251,7 @@ MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type)
 
     /*
     This function should never have been implemented in the first place. Changing the type dynamically is not
-    supported. Instead you need to uninitialize and reinitiailize a fresh `ma_noise` object. This function
+    supported. Instead you need to uninitialize and reinitialize a fresh `ma_noise` object. This function
     will be removed in version 0.12.
     */
     MA_ASSERT(MA_FALSE);
@@ -67725,7 +68297,7 @@ MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pCon
         pResourceManager->config.pVFS = &pResourceManager->defaultVFS;
     }
 
-    /* If threading has been disabled at compile time, enfore it at run time as well. */
+    /* If threading has been disabled at compile time, enforce it at run time as well. */
     #ifdef MA_NO_THREADING
     {
         pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING;
@@ -67762,15 +68334,17 @@ MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pCon
     /* Custom decoding backends. */
     if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) {
         size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount;
+        ma_decoding_backend_vtable** ppCustomDecodingBackendVTables;
 
-        pResourceManager->config.ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks);
+        ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks);
         if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) {
             ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
             return MA_OUT_OF_MEMORY;
         }
 
-        MA_COPY_MEMORY(pResourceManager->config.ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes);
+        MA_COPY_MEMORY(ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes);
 
+        pResourceManager->config.ppCustomDecodingBackendVTables = ppCustomDecodingBackendVTables;
         pResourceManager->config.customDecodingBackendCount     = pConfig->customDecodingBackendCount;
         pResourceManager->config.pCustomDecodingBackendUserData = pConfig->pCustomDecodingBackendUserData;
     }
@@ -67821,7 +68395,7 @@ static void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager
         ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode;
         ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
 
-        /* The data buffer has been removed from the BST, so now we need to free it's data. */
+        /* The data buffer has been removed from the BST, so now we need to free its data. */
         ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
     }
 }
@@ -67834,7 +68408,7 @@ MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager)
 
     /*
     Job threads need to be killed first. To do this we need to post a quit message to the message queue and then wait for the thread. The quit message will never be removed from the
-    queue which means it will never not be returned after being encounted for the first time which means all threads will eventually receive it.
+    queue which means it will never not be returned after being encountered for the first time which means all threads will eventually receive it.
     */
     ma_resource_manager_post_job_quit(pResourceManager);
 
@@ -67874,7 +68448,7 @@ MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager)
         #endif
     }
 
-    ma_free(pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks);
+    ma_free((ma_decoding_backend_vtable**)pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks);  /* <-- Naughty const-cast, but this is safe. */
 
     if (pResourceManager->config.pLog == &pResourceManager->log) {
         ma_log_uninit(&pResourceManager->log);
@@ -68292,7 +68866,7 @@ static ma_result ma_resource_manager_data_buffer_node_decode_next_page(ma_resour
             }
 
             result = ma_decoder_read_pcm_frames(pDecoder, pPage->pAudioData, framesToTryReading, &framesRead);
-            if (framesRead > 0) {
+            if (result == MA_SUCCESS && framesRead > 0) {
                 pPage->sizeInFrames = framesRead;
 
                 result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage);
@@ -68445,7 +69019,7 @@ static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(m
                 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
                     ma_resource_manager_inline_notification_uninit(pInitNotification);
                 } else {
-                    /* These will have been freed by the job thread, but with WAIT_INIT they will already have happend sinced the job has already been handled. */
+                    /* These will have been freed by the job thread, but with WAIT_INIT they will already have happened since the job has already been handled. */
                     ma_free(pFilePathCopy,  &pResourceManager->config.allocationCallbacks);
                     ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks);
                 }
@@ -68810,6 +69384,10 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma
         flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC;
     }
 
+    if (pConfig->isLooping) {
+        flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING;
+    }
+
     async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0;
 
     /*
@@ -68822,7 +69400,7 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma
 
     These fences are always released at the "done" tag at the end of this function. They'll be
     acquired a second if loading asynchronously. This double acquisition system is just done to
-    simplify code maintanence.
+    simplify code maintenance.
     */
     ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications);
     {
@@ -68867,7 +69445,7 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma
 
             /*
             The status of the data buffer needs to be set to MA_BUSY before posting the job so that the
-            worker thread is aware of it's busy state. If the LOAD_DATA_BUFFER job sees a status other
+            worker thread is aware of its busy state. If the LOAD_DATA_BUFFER job sees a status other
             than MA_BUSY, it'll assume an error and fall through to an early exit.
             */
             ma_atomic_exchange_i32(&pDataBuffer->result, MA_BUSY);
@@ -68886,7 +69464,7 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma
             job.data.resourceManager.loadDataBuffer.rangeEndInPCMFrames     = pConfig->rangeEndInPCMFrames;
             job.data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames;
             job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames;
-            job.data.resourceManager.loadDataBuffer.isLooping               = pConfig->isLooping;
+            job.data.resourceManager.loadDataBuffer.isLooping               = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0;
 
             /* If we need to wait for initialization to complete we can just process the job in place. */
             if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
@@ -69107,22 +69685,29 @@ MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_man
         isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY);
 
         if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) {
-            /* Don't try reading more than the available frame count. */
-            if (frameCount > availableFrames) {
-                frameCount = availableFrames;
+            /* Don't try reading more than the available frame count if the data buffer node is still loading. */
+            if (isDecodedBufferBusy) {
+                if (frameCount > availableFrames) {
+                    frameCount = availableFrames;
 
-                /*
-                If there's no frames available we want to set the status to MA_AT_END. The logic below
-                will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this
-                is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count
-                is 0 because that'll result in a situation where it's possible MA_AT_END won't get
-                returned.
-                */
-                if (frameCount == 0) {
-                    result = MA_AT_END;
+                    /*
+                    If there's no frames available we want to set the status to MA_AT_END. The logic below
+                    will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this
+                    is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count
+                    is 0 because that'll result in a situation where it's possible MA_AT_END won't get
+                    returned.
+                    */
+                    if (frameCount == 0) {
+                        result = MA_AT_END;
+                    }
+                } else {
+                    isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */
                 }
             } else {
-                isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */
+                /*
+                Getting here means the buffer has been fully loaded. We can just pass the frame count straight
+                into ma_data_source_read_pcm_frames() below and let ma_data_source handle it.
+                */
             }
         }
     }
@@ -69522,6 +70107,7 @@ MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pR
     ma_bool32 waitBeforeReturning = MA_FALSE;
     ma_resource_manager_inline_notification waitNotification;
     ma_resource_manager_pipeline_notifications notifications;
+    ma_uint32 flags;
 
     if (pDataStream == NULL) {
         if (pConfig != NULL && pConfig->pNotifications != NULL) {
@@ -69552,13 +70138,18 @@ MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pR
         return result;
     }
 
+    flags = pConfig->flags;
+    if (pConfig->isLooping) {
+        flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING;
+    }
+
     pDataStream->pResourceManager = pResourceManager;
     pDataStream->flags            = pConfig->flags;
     pDataStream->result           = MA_BUSY;
 
     ma_data_source_set_range_in_pcm_frames(pDataStream, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames);
     ma_data_source_set_loop_point_in_pcm_frames(pDataStream, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames);
-    ma_data_source_set_looping(pDataStream, pConfig->isLooping);
+    ma_data_source_set_looping(pDataStream, (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0);
 
     if (pResourceManager == NULL || (pConfig->pFilePath == NULL && pConfig->pFilePathW == NULL)) {
         ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications);
@@ -70180,6 +70771,9 @@ static ma_result ma_resource_manager_data_source_preinit(ma_resource_manager* pR
     }
 
     pDataSource->flags = pConfig->flags;
+    if (pConfig->isLooping) {
+        pDataSource->flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING;
+    }
 
     return MA_SUCCESS;
 }
@@ -70738,9 +71332,10 @@ static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob
     */
     result = ma_resource_manager_data_buffer_result(pDataBuffer);
     if (result != MA_BUSY) {
-        goto done;  /* <-- This will ensure the exucution pointer is incremented. */
+        goto done;  /* <-- This will ensure the execution pointer is incremented. */
     } else {
         result = MA_SUCCESS;    /* <-- Make sure this is reset. */
+        (void)result;           /* <-- This is to suppress a static analysis diagnostic about "result" not being used. But for safety when I do future maintenance I don't want to delete that assignment. */
     }
 
     /* Try initializing the connector if we haven't already. */
@@ -71087,11 +71682,74 @@ static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob
 
 
 #ifndef MA_NO_NODE_GRAPH
+
+static ma_stack* ma_stack_init(size_t sizeInBytes, const ma_allocation_callbacks* pAllocationCallbacks)
+{
+    ma_stack* pStack;
+
+    if (sizeInBytes == 0) {
+        return NULL;
+    }
+
+    pStack = (ma_stack*)ma_malloc(sizeof(*pStack) - sizeof(pStack->_data) + sizeInBytes, pAllocationCallbacks);
+    if (pStack == NULL) {
+        return NULL;
+    }
+
+    pStack->offset = 0;
+    pStack->sizeInBytes = sizeInBytes;
+
+    return pStack;
+}
+
+static void ma_stack_uninit(ma_stack* pStack, const ma_allocation_callbacks* pAllocationCallbacks)
+{
+    if (pStack == NULL) {
+        return;
+    }
+
+    ma_free(pStack, pAllocationCallbacks);
+}
+
+static void* ma_stack_alloc(ma_stack* pStack, size_t sz)
+{
+    /* The size of the allocation is stored in the memory directly before the pointer. This needs to include padding to keep it aligned to ma_uintptr */
+    void* p = (void*)((char*)pStack->_data + pStack->offset);
+    size_t* pSize = (size_t*)p;
+
+    sz = (sz + (sizeof(ma_uintptr) - 1)) & ~(sizeof(ma_uintptr) - 1);  /* Padding. */
+    if (pStack->offset + sz + sizeof(size_t) > pStack->sizeInBytes) {
+        return NULL;    /* Out of memory. */
+    }
+
+    pStack->offset += sz + sizeof(size_t);
+
+    *pSize = sz;
+    return (void*)((char*)p + sizeof(size_t));
+}
+
+static void ma_stack_free(ma_stack* pStack, void* p)
+{
+    size_t* pSize;
+
+    if (p == NULL) {
+        return;
+    }
+
+    pSize = (size_t*)p - 1;
+    pStack->offset -= *pSize + sizeof(size_t);
+}
+
+
+
 /* 10ms @ 48K = 480. Must never exceed 65535. */
 #ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS
 #define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480
 #endif
 
+#ifndef MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL
+#define MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL    524288
+#endif
 
 static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime);
 
@@ -71131,8 +71789,8 @@ MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels)
     ma_node_graph_config config;
 
     MA_ZERO_OBJECT(&config);
-    config.channels             = channels;
-    config.nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS;
+    config.channels               = channels;
+    config.processingSizeInFrames = 0;
 
     return config;
 }
@@ -71219,11 +71877,7 @@ MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const m
     }
 
     MA_ZERO_OBJECT(pNodeGraph);
-    pNodeGraph->nodeCacheCapInFrames = pConfig->nodeCacheCapInFrames;
-    if (pNodeGraph->nodeCacheCapInFrames == 0) {
-        pNodeGraph->nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS;
-    }
-
+    pNodeGraph->processingSizeInFrames = pConfig->processingSizeInFrames;
 
     /* Base node so we can use the node graph as a node into another graph. */
     baseConfig = ma_node_config_init();
@@ -71248,6 +71902,40 @@ MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const m
         return result;
     }
 
+
+    /* Processing cache. */
+    if (pConfig->processingSizeInFrames > 0) {
+        pNodeGraph->pProcessingCache = (float*)ma_malloc(pConfig->processingSizeInFrames * pConfig->channels * sizeof(float), pAllocationCallbacks);
+        if (pNodeGraph->pProcessingCache == NULL) {
+            ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks);
+            ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks);
+            return MA_OUT_OF_MEMORY;
+        }
+    }
+
+
+    /*
+    We need a pre-mix stack. The size of this stack is configurable via the config. The default value depends on the channel count.
+    */
+    {
+        size_t preMixStackSizeInBytes = pConfig->preMixStackSizeInBytes;
+        if (preMixStackSizeInBytes == 0) {
+            preMixStackSizeInBytes = pConfig->channels * MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL;
+        }
+
+        pNodeGraph->pPreMixStack = ma_stack_init(preMixStackSizeInBytes, pAllocationCallbacks);
+        if (pNodeGraph->pPreMixStack == NULL) {
+            ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks);
+            ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks);
+            if (pNodeGraph->pProcessingCache != NULL) {
+                ma_free(pNodeGraph->pProcessingCache, pAllocationCallbacks);
+            }
+
+            return MA_OUT_OF_MEMORY;
+        }
+    }
+
+
     return MA_SUCCESS;
 }
 
@@ -71258,6 +71946,17 @@ MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_
     }
 
     ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks);
+    ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks);
+
+    if (pNodeGraph->pProcessingCache != NULL) {
+        ma_free(pNodeGraph->pProcessingCache, pAllocationCallbacks);
+        pNodeGraph->pProcessingCache = NULL;
+    }
+
+    if (pNodeGraph->pPreMixStack != NULL) {
+        ma_stack_uninit(pNodeGraph->pPreMixStack, pAllocationCallbacks);
+        pNodeGraph->pPreMixStack = NULL;
+    }
 }
 
 MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph)
@@ -71290,27 +71989,72 @@ MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void*
     totalFramesRead = 0;
     while (totalFramesRead < frameCount) {
         ma_uint32 framesJustRead;
-        ma_uint64 framesToRead = frameCount - totalFramesRead;
+        ma_uint64 framesToRead;
+        float* pRunningFramesOut;
 
+        framesToRead = frameCount - totalFramesRead;
         if (framesToRead > 0xFFFFFFFF) {
             framesToRead = 0xFFFFFFFF;
         }
 
-        ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE);
-        {
-            result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint));
-        }
-        ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE);
+        pRunningFramesOut = (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels);
 
-        totalFramesRead += framesJustRead;
+        /* If there's anything in the cache, consume that first. */
+        if (pNodeGraph->processingCacheFramesRemaining > 0) {
+            ma_uint32 framesToReadFromCache;
 
-        if (result != MA_SUCCESS) {
-            break;
-        }
+            framesToReadFromCache = (ma_uint32)framesToRead;
+            if (framesToReadFromCache > pNodeGraph->processingCacheFramesRemaining) {
+                framesToReadFromCache = pNodeGraph->processingCacheFramesRemaining;
+            }
 
-        /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */
-        if (framesJustRead == 0) {
-            break;
+            MA_COPY_MEMORY(pRunningFramesOut, pNodeGraph->pProcessingCache, framesToReadFromCache * channels * sizeof(float));
+            MA_MOVE_MEMORY(pNodeGraph->pProcessingCache, pNodeGraph->pProcessingCache + (framesToReadFromCache * channels), (pNodeGraph->processingCacheFramesRemaining - framesToReadFromCache) * channels * sizeof(float));
+            pNodeGraph->processingCacheFramesRemaining -= framesToReadFromCache;
+
+            totalFramesRead += framesToReadFromCache;
+            continue;
+        } else {
+            /*
+            If processingSizeInFrames is non-zero, we need to make sure we always read in chunks of that size. If the frame count is less than
+            that, we need to read into the cache and then continue on.
+            */
+            float* pReadDst = pRunningFramesOut;
+
+            if (pNodeGraph->processingSizeInFrames > 0) {
+                if (framesToRead < pNodeGraph->processingSizeInFrames) {
+                    pReadDst = pNodeGraph->pProcessingCache;    /* We need to read into the cache because otherwise we'll overflow the output buffer. */
+                }
+
+                framesToRead = pNodeGraph->processingSizeInFrames;
+            }
+
+            ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE);
+            {
+                result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, pReadDst, (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint));
+            }
+            ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE);
+
+            /*
+            Do not increment the total frames read counter if we read into the cache. We use this to determine how many frames have
+            been written to the final output buffer.
+            */
+            if (pReadDst == pNodeGraph->pProcessingCache) {
+                /* We read into the cache. */
+                pNodeGraph->processingCacheFramesRemaining = framesJustRead;
+            } else {
+                /* We read straight into the output buffer. */
+                totalFramesRead += framesJustRead;
+            }
+
+            if (result != MA_SUCCESS) {
+                break;
+            }
+
+            /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */
+            if (framesJustRead == 0) {
+                break;
+            }
         }
     }
 
@@ -71511,7 +72255,7 @@ static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInp
     *not* using a lock when iterating over the list in the audio thread. We therefore need to craft
     this in a way such that the iteration on the audio thread doesn't break.
 
-    The the first thing to do is swap out the "next" pointer of the previous output bus with the
+    The first thing to do is swap out the "next" pointer of the previous output bus with the
     new "next" output bus. This is the operation that matters for iteration on the audio thread.
     After that, the previous pointer on the new "next" pointer needs to be updated, after which
     point the linked list will be in a good state.
@@ -71604,7 +72348,7 @@ static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_outpu
         /*
         Now we need to attach the output bus to the linked list. This involves updating two pointers on
         two different output buses so I'm going to go ahead and keep this simple and just use a lock.
-        There are ways to do this without a lock, but it's just too hard to maintain for it's value.
+        There are ways to do this without a lock, but it's just too hard to maintain for its value.
 
         Although we're locking here, it's important to remember that we're *not* locking when iterating
         and reading audio data since that'll be running on the audio thread. As a result we need to be
@@ -71697,11 +72441,9 @@ static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_
     ma_uint32 inputChannels;
     ma_bool32 doesOutputBufferHaveContent = MA_FALSE;
 
-    (void)pInputNode;   /* Not currently used. */
-
     /*
     This will be called from the audio thread which means we can't be doing any locking. Basically,
-    this function will not perfom any locking, whereas attaching and detaching will, but crafted in
+    this function will not perform any locking, whereas attaching and detaching will, but crafted in
     such a way that we don't need to perform any locking here. The important thing to remember is
     to always iterate in a forward direction.
 
@@ -71747,19 +72489,12 @@ static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_
 
         if (pFramesOut != NULL) {
             /* Read. */
-            float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)];
-            ma_uint32 tempCapInFrames = ma_countof(temp) / inputChannels;
-
             while (framesProcessed < frameCount) {
                 float* pRunningFramesOut;
                 ma_uint32 framesToRead;
-                ma_uint32 framesJustRead;
+                ma_uint32 framesJustRead = 0;
 
                 framesToRead = frameCount - framesProcessed;
-                if (framesToRead > tempCapInFrames) {
-                    framesToRead = tempCapInFrames;
-                }
-
                 pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels);
 
                 if (doesOutputBufferHaveContent == MA_FALSE) {
@@ -71767,11 +72502,32 @@ static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_
                     result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed);
                 } else {
                     /* Slow path. Not the first attachment. Mixing required. */
-                    result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, temp, framesToRead, &framesJustRead, globalTime + framesProcessed);
-                    if (result == MA_SUCCESS || result == MA_AT_END) {
-                        if (isSilentOutput == MA_FALSE) {   /* Don't mix if the node outputs silence. */
-                            ma_mix_pcm_frames_f32(pRunningFramesOut, temp, framesJustRead, inputChannels, /*volume*/1);
+                    ma_uint32 preMixBufferCapInFrames = ((ma_node_base*)pInputNode)->cachedDataCapInFramesPerBus;
+                    float* pPreMixBuffer = (float*)ma_stack_alloc(((ma_node_base*)pInputNode)->pNodeGraph->pPreMixStack, preMixBufferCapInFrames * inputChannels * sizeof(float));
+
+                    if (pPreMixBuffer == NULL) {
+                        /*
+                        If you're hitting this assert it means you've got an unusually deep chain of nodes, you've got an excessively large processing
+                        size, or you have a combination of both, and as a result have run out of stack space. You can increase this using the
+                        preMixStackSizeInBytes variable in ma_node_graph_config. If you're using ma_engine, you can do it via the preMixStackSizeInBytes
+                        variable in ma_engine_config. It defaults to 512KB per output channel.
+                        */
+                        MA_ASSERT(MA_FALSE);
+                    } else {
+                        if (framesToRead > preMixBufferCapInFrames) {
+                            framesToRead = preMixBufferCapInFrames;
                         }
+
+                        result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pPreMixBuffer, framesToRead, &framesJustRead, globalTime + framesProcessed);
+                        if (result == MA_SUCCESS || result == MA_AT_END) {
+                            if (isSilentOutput == MA_FALSE) {   /* Don't mix if the node outputs silence. */
+                                ma_mix_pcm_frames_f32(pRunningFramesOut, pPreMixBuffer, framesJustRead, inputChannels, /*volume*/1);
+                            }
+                        }
+
+                        /* The pre-mix buffer is no longer required. */
+                        ma_stack_free(((ma_node_base*)pInputNode)->pNodeGraph->pPreMixStack, pPreMixBuffer);
+                        pPreMixBuffer = NULL;
                     }
                 }
 
@@ -71826,6 +72582,25 @@ MA_API ma_node_config ma_node_config_init(void)
     return config;
 }
 
+static ma_uint16 ma_node_config_get_cache_size_in_frames(const ma_node_config* pConfig, const ma_node_graph* pNodeGraph)
+{
+    ma_uint32 cacheSizeInFrames;
+
+    (void)pConfig;
+
+    if (pNodeGraph->processingSizeInFrames > 0) {
+        cacheSizeInFrames = pNodeGraph->processingSizeInFrames;
+    } else {
+        cacheSizeInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS;
+    }
+
+    if (cacheSizeInFrames > 0xFFFF) {
+        cacheSizeInFrames = 0xFFFF;
+    }
+
+    return (ma_uint16)cacheSizeInFrames;
+}
+
 
 
 static ma_result ma_node_detach_full(ma_node* pNode);
@@ -71980,7 +72755,7 @@ static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_nod
     /*
     Cached audio data.
 
-    We need to allocate memory for a caching both input and output data. We have an optimization
+    We need to allocate memory for caching both input and output data. We have an optimization
     where no caching is necessary for specific conditions:
 
         - The node has 0 inputs and 1 output.
@@ -71999,14 +72774,18 @@ static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_nod
     } else {
         /* Slow path. Cache needed. */
         size_t cachedDataSizeInBytes = 0;
+        ma_uint32 cacheCapInFrames;
         ma_uint32 iBus;
 
+        /* The capacity of the cache is based on our callback processing size. */
+        cacheCapInFrames = ma_node_config_get_cache_size_in_frames(pConfig, pNodeGraph);
+
         for (iBus = 0; iBus < inputBusCount; iBus += 1) {
-            cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]);
+            cachedDataSizeInBytes += cacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]);
         }
 
         for (iBus = 0; iBus < outputBusCount; iBus += 1) {
-            cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]);
+            cachedDataSizeInBytes += cacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]);
         }
 
         pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes;
@@ -72092,13 +72871,12 @@ MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_n
 
     if (heapLayout.cachedDataOffset != MA_SIZE_MAX) {
         pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset);
-        pNodeBase->cachedDataCapInFramesPerBus = pNodeGraph->nodeCacheCapInFrames;
+        pNodeBase->cachedDataCapInFramesPerBus = ma_node_config_get_cache_size_in_frames(pConfig, pNodeGraph);
     } else {
         pNodeBase->pCachedData = NULL;
     }
 
 
-
     /* We need to run an initialization step for each input and output bus. */
     for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) {
         result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]);
@@ -72272,7 +73050,7 @@ static ma_result ma_node_detach_full(ma_node* pNode)
 
     /*
     At this point all output buses will have been detached from the graph and we can be guaranteed
-    that none of it's input nodes will be getting processed by the graph. We can detach these
+    that none of its input nodes will be getting processed by the graph. We can detach these
     without needing to worry about the audio thread touching them.
     */
     for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) {
@@ -72287,7 +73065,7 @@ static ma_result ma_node_detach_full(ma_node* pNode)
         linked list logic. We don't need to worry about the audio thread referencing these because the step
         above severed the connection to the graph.
         */
-        for (pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext)) {
+        for (pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext)) {
             ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex);   /* This won't do any waiting in practice and should be efficient. */
         }
     }
@@ -72309,7 +73087,7 @@ MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIn
         return MA_INVALID_ARGS; /* Invalid output bus index. */
     }
 
-    /* We need to lock the output bus because we need to inspect the input node and grab it's input bus. */
+    /* We need to lock the output bus because we need to inspect the input node and grab its input bus. */
     ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]);
     {
         pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode;
@@ -72475,7 +73253,7 @@ MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_ui
 
     /*
     Getting here means the node is marked as started, but it may still not be truly started due to
-    it's start time not having been reached yet. Also, the stop time may have also been reached in
+    its start time not having been reached yet. Also, the stop time may have also been reached in
     which case it'll be considered stopped.
     */
     if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeBeg) {
@@ -72486,7 +73264,7 @@ MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_ui
         return ma_node_state_stopped;   /* Stop time has been reached. */
     }
 
-    /* Getting here means the node is marked as started and is within it's start/stop times. */
+    /* Getting here means the node is marked as started and is within its start/stop times. */
     return ma_node_state_started;
 }
 
@@ -72648,12 +73426,12 @@ static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusInde
                 frameCountOut = totalFramesRead;
 
                 if (totalFramesRead > 0) {
-                    ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut);  /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */
+                    ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut);  /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Explicit cast to silence the warning. */
                 }
 
                 /*
                 A passthrough should never have modified the input and output frame counts. If you're
-                triggering these assers you need to fix your processing callback.
+                triggering these asserts you need to fix your processing callback.
                 */
                 MA_ASSERT(frameCountIn  == totalFramesRead);
                 MA_ASSERT(frameCountOut == totalFramesRead);
@@ -72831,7 +73609,7 @@ static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusInde
                         frames available right now.
                         */
                         if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) {
-                            ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut);    /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */
+                            ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut);    /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Explicit cast to silence the warning. */
                         } else {
                             frameCountOut = 0;  /* No data was processed. */
                         }
@@ -74068,7 +74846,7 @@ static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngin
 {
     MA_ASSERT(pEngineNode != NULL);
 
-    /* Don't try to be clever by skiping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */
+    /* Don't try to be clever by skipping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */
     return !ma_atomic_load_explicit_32(&pEngineNode->isPitchDisabled, ma_atomic_memory_order_acquire);
 }
 
@@ -74105,7 +74883,7 @@ static ma_result ma_engine_node_set_volume(ma_engine_node* pEngineNode, float vo
 
     /* If we're not smoothing we should bypass the volume gainer entirely. */
     if (pEngineNode->volumeSmoothTimeInPCMFrames == 0) {
-        /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for hodling our volume. */
+        /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for holding our volume. */
         ma_spatializer_set_master_volume(&pEngineNode->spatializer, volume);
     } else {
         /* We're using volume smoothing, so apply the master volume to the gainer. */
@@ -74420,7 +75198,7 @@ static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float
                 ma_sound_set_at_end(pSound, MA_TRUE);   /* This will be set to false in ma_sound_start(). */
             }
 
-            pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_engine_get_channels(ma_sound_get_engine(pSound)));
+            pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_node_get_output_channels(pNode, 0));
 
             frameCountIn = (ma_uint32)framesJustRead;
             frameCountOut = framesRemaining;
@@ -74751,7 +75529,7 @@ MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* p
 
 
     /*
-    Spatialization comes next. We spatialize based ont he node's output channel count. It's up the caller to
+    Spatialization comes next. We spatialize based on the node's output channel count. It's up the caller to
     ensure channels counts link up correctly in the node graph.
     */
     spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig);
@@ -74941,6 +75719,21 @@ static void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOu
 
     ma_engine_read_pcm_frames(pEngine, pFramesOut, frameCount, NULL);
 }
+
+static ma_uint32 ma_device__get_processing_size_in_frames(ma_device* pDevice)
+{
+    /*
+    The processing size is the period size. The device can have a fixed sized processing size, or
+    it can be decided by the backend in which case it can be variable.
+    */
+    if (pDevice->playback.intermediaryBufferCap > 0) {
+        /* Using a fixed sized processing callback. */
+        return pDevice->playback.intermediaryBufferCap;
+    } else {
+        /* Not using a fixed sized processing callback. Need to estimate the processing size based on the backend. */
+        return pDevice->playback.internalPeriodSizeInFrames;
+    }
+}
 #endif
 
 MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine)
@@ -75034,6 +75827,14 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng
         if (pEngine->pDevice != NULL) {
             engineConfig.channels   = pEngine->pDevice->playback.channels;
             engineConfig.sampleRate = pEngine->pDevice->sampleRate;
+
+            /*
+            The processing size used by the engine is determined by engineConfig.periodSizeInFrames. We want
+            to make this equal to what the device is using for it's period size. If we don't do that, it's
+            possible that the node graph will split it's processing into multiple passes which can introduce
+            glitching.
+            */
+            engineConfig.periodSizeInFrames = ma_device__get_processing_size_in_frames(pEngine->pDevice);
         }
     }
     #endif
@@ -75060,9 +75861,10 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng
     }
 
 
-    /* The engine is a node graph. This needs to be initialized after we have the device so we can can determine the channel count. */
+    /* The engine is a node graph. This needs to be initialized after we have the device so we can determine the channel count. */
     nodeGraphConfig = ma_node_graph_config_init(engineConfig.channels);
-    nodeGraphConfig.nodeCacheCapInFrames = (engineConfig.periodSizeInFrames > 0xFFFF) ? 0xFFFF : (ma_uint16)engineConfig.periodSizeInFrames;
+    nodeGraphConfig.processingSizeInFrames = engineConfig.periodSizeInFrames;
+    nodeGraphConfig.preMixStackSizeInBytes = engineConfig.preMixStackSizeInBytes;
 
     result = ma_node_graph_init(&nodeGraphConfig, &pEngine->allocationCallbacks, &pEngine->nodeGraph);
     if (result != MA_SUCCESS) {
@@ -75142,8 +75944,8 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng
             ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks);
             resourceManagerConfig.pVFS              = engineConfig.pResourceManagerVFS;
 
-            /* The Emscripten build cannot use threads. */
-            #if defined(MA_EMSCRIPTEN)
+            /* The Emscripten build cannot use threads unless it's targeting pthreads. */
+            #if defined(MA_EMSCRIPTEN) && !defined(__EMSCRIPTEN_PTHREADS__)
             {
                 resourceManagerConfig.jobThreadCount = 0;
                 resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING;
@@ -75658,7 +76460,7 @@ MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePa
         return MA_INVALID_ARGS;
     }
 
-    /* Attach to the endpoint node if nothing is specicied. */
+    /* Attach to the endpoint node if nothing is specified. */
     if (pNode == NULL) {
         pNode = ma_node_graph_get_endpoint(&pEngine->nodeGraph);
         nodeInputBusIndex = 0;
@@ -75875,7 +76677,7 @@ static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, con
         ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames);
     }
 
-    ma_sound_set_looping(pSound, pConfig->isLooping);
+    ma_sound_set_looping(pSound, pConfig->isLooping || ((pConfig->flags & MA_SOUND_FLAG_LOOPING) != 0));
 
     return MA_SUCCESS;
 }
@@ -75899,6 +76701,9 @@ MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_s
     it and can avoid accessing the sound from within the notification.
     */
     flags = pConfig->flags | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT;
+    if (pConfig->isLooping) {
+        flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING;
+    }
 
     pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks);
     if (pSound->pResourceManagerDataSource == NULL) {
@@ -75927,7 +76732,7 @@ MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_s
         resourceManagerDataSourceConfig.rangeEndInPCMFrames         = pConfig->rangeEndInPCMFrames;
         resourceManagerDataSourceConfig.loopPointBegInPCMFrames     = pConfig->loopPointBegInPCMFrames;
         resourceManagerDataSourceConfig.loopPointEndInPCMFrames     = pConfig->loopPointEndInPCMFrames;
-        resourceManagerDataSourceConfig.isLooping                   = pConfig->isLooping;
+        resourceManagerDataSourceConfig.isLooping                   = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0;
 
         result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource);
         if (result != MA_SUCCESS) {
@@ -76079,7 +76884,7 @@ MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pCo
     {
         /*
         Getting here means we're not loading from a file. We may be loading from an already-initialized
-        data source, or none at all. If we aren't specifying any data source, we'll be initializing the
+        data source, or none at all. If we aren't specifying any data source, we'll be initializing
         the equivalent to a group. ma_data_source_init_from_data_source_internal() will deal with this
         for us, so no special treatment required here.
         */
@@ -76799,6 +77604,27 @@ MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameInd
     return MA_SUCCESS;
 }
 
+MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds)
+{
+    ma_uint64 frameIndex;
+    ma_uint32 sampleRate;
+    ma_result result;
+
+    if (pSound == NULL) {
+        return MA_INVALID_ARGS;
+    }
+
+    result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0);
+    if (result != MA_SUCCESS) {
+        return result;
+    }
+
+    /* We need PCM frames. We need to convert first */
+    frameIndex = (ma_uint64)(seekPointInSeconds * sampleRate);
+
+    return ma_sound_seek_to_pcm_frame(pSound, frameIndex);
+}
+
 MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
 {
     if (pSound == NULL) {
@@ -77245,7 +78071,7 @@ code below please report the bug to the respective repository for the relevant p
 ***************************************************************************************************************************************************************
 **************************************************************************************************************************************************************/
 #if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING))
-#if !defined(MA_DR_WAV_IMPLEMENTATION) && !defined(MA_DR_WAV_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
+#if !defined(MA_DR_WAV_IMPLEMENTATION)
 /* dr_wav_c begin */
 #ifndef ma_dr_wav_c
 #define ma_dr_wav_c
@@ -78567,7 +79393,6 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_p
         }
         if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) {
             if (ma_dr_wav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) {
-                return MA_FALSE;
             }
         } else if (pWav->container == ma_dr_wav_container_rf64) {
             if (ma_dr_wav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) {
@@ -78836,7 +79661,9 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_p
                     compressionFormat = MA_DR_WAVE_FORMAT_MULAW;
                 } else if (ma_dr_wav_fourcc_equal(type, "ima4")) {
                     compressionFormat = MA_DR_WAVE_FORMAT_DVI_ADPCM;
-                    sampleSizeInBits = 4;
+                    sampleSizeInBits  = 4;
+                    (void)compressionFormat;
+                    (void)sampleSizeInBits;
                     return MA_FALSE;
                 } else {
                     return MA_FALSE;
@@ -78894,9 +79721,7 @@ MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_p
             }
         }
         if (isProcessingMetadata) {
-            ma_uint64 metadataBytesRead;
-            metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown);
-            MA_DR_WAV_ASSERT(metadataBytesRead <= header.sizeInBytes);
+            ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown);
             if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) {
                 break;
             }
@@ -80344,6 +81169,12 @@ MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToW
 MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
 {
     ma_uint64 totalFramesRead = 0;
+    static ma_int32 adaptationTable[] = {
+        230, 230, 230, 230, 307, 409, 512, 614,
+        768, 614, 512, 409, 307, 230, 230, 230
+    };
+    static ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460,  392 };
+    static ma_int32 coeff2Table[] = { 0,  -256, 0, 64,  0,  -208, -232 };
     MA_DR_WAV_ASSERT(pWav != NULL);
     MA_DR_WAV_ASSERT(framesToRead > 0);
     while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
@@ -80362,6 +81193,9 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_
                 pWav->msadpcm.cachedFrames[2]  = pWav->msadpcm.prevFrames[0][0];
                 pWav->msadpcm.cachedFrames[3]  = pWav->msadpcm.prevFrames[0][1];
                 pWav->msadpcm.cachedFrameCount = 2;
+                if (pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff1Table)) {
+                    return totalFramesRead;
+                }
             } else {
                 ma_uint8 header[14];
                 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
@@ -80381,6 +81215,9 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_
                 pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1];
                 pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1];
                 pWav->msadpcm.cachedFrameCount = 2;
+                if (pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= ma_dr_wav_countof(coeff2Table)) {
+                    return totalFramesRead;
+                }
             }
         }
         while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
@@ -80403,12 +81240,6 @@ MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_
             if (pWav->msadpcm.bytesRemainingInBlock == 0) {
                 continue;
             } else {
-                static ma_int32 adaptationTable[] = {
-                    230, 230, 230, 230, 307, 409, 512, 614,
-                    768, 614, 512, 409, 307, 230, 230, 230
-                };
-                static ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460,  392 };
-                static ma_int32 coeff2Table[] = { 0,  -256, 0, 64,  0,  -208, -232 };
                 ma_uint8 nibbles;
                 ma_int32 nibble0;
                 ma_int32 nibble1;
@@ -81659,7 +82490,7 @@ MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sample
         return;
     }
     for (i = 0; i < sampleCount; ++i) {
-        *pOut++ = (ma_int32)(2147483648.0 * pIn[i]);
+        *pOut++ = (ma_int32)(2147483648.0f * pIn[i]);
     }
 }
 MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount)
@@ -82073,7 +82904,7 @@ MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b)
 #endif  /* MA_NO_WAV */
 
 #if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING)
-#if !defined(MA_DR_FLAC_IMPLEMENTATION) && !defined(MA_DR_FLAC_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
+#if !defined(MA_DR_FLAC_IMPLEMENTATION)
 /* dr_flac_c begin */
 #ifndef ma_dr_flac_c
 #define ma_dr_flac_c
@@ -85105,6 +85936,7 @@ static ma_bool32 ma_dr_flac__read_subframe_header(ma_dr_flac_bs* bs, ma_dr_flac_
     if ((header & 0x80) != 0) {
         return MA_FALSE;
     }
+    pSubframe->lpcOrder = 0;
     type = (header & 0x7E) >> 1;
     if (type == 0) {
         pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_CONSTANT;
@@ -85162,6 +85994,9 @@ static ma_bool32 ma_dr_flac__decode_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame
     }
     subframeBitsPerSample -= pSubframe->wastedBitsPerSample;
     pSubframe->pSamplesS32 = pDecodedSamplesOut;
+    if (frame->header.blockSizeInPCMFrames < pSubframe->lpcOrder) {
+        return MA_FALSE;
+    }
     switch (pSubframe->subframeType)
     {
         case MA_DR_FLAC_SUBFRAME_CONSTANT:
@@ -89818,7 +90653,7 @@ MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterat
 #endif  /* MA_NO_FLAC */
 
 #if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING)
-#if !defined(MA_DR_MP3_IMPLEMENTATION) && !defined(MA_DR_MP3_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */
+#if !defined(MA_DR_MP3_IMPLEMENTATION)
 /* dr_mp3_c begin */
 #ifndef ma_dr_mp3_c
 #define ma_dr_mp3_c
@@ -89879,7 +90714,7 @@ MA_API const char* ma_dr_mp3_version_string(void)
 #define MA_DR_MP3_MIN(a, b)           ((a) > (b) ? (b) : (a))
 #define MA_DR_MP3_MAX(a, b)           ((a) < (b) ? (b) : (a))
 #if !defined(MA_DR_MP3_NO_SIMD)
-#if !defined(MA_DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64))
+#if !defined(MA_DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC))
 #define MA_DR_MP3_ONLY_SIMD
 #endif
 #if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)))
@@ -89952,7 +90787,7 @@ end:
     return g_have_simd - 1;
 #endif
 }
-#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)
+#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
 #include 
 #define MA_DR_MP3_HAVE_SSE 0
 #define MA_DR_MP3_HAVE_SIMD 1
@@ -89981,7 +90816,7 @@ static int ma_dr_mp3_have_simd(void)
 #else
 #define MA_DR_MP3_HAVE_SIMD 0
 #endif
-#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(__ARM_ARCH_6M__)
+#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(_M_ARM64EC) && !defined(__ARM_ARCH_6M__)
 #define MA_DR_MP3_HAVE_ARMV6 1
 static __inline__ __attribute__((always_inline)) ma_int32 ma_dr_mp3_clip_int16_arm(ma_int32 a)
 {
@@ -91147,8 +91982,8 @@ static ma_int16 ma_dr_mp3d_scale_pcm(float sample)
     s32 -= (s32 < 0);
     s = (ma_int16)ma_dr_mp3_clip_int16_arm(s32);
 #else
-    if (sample >=  32766.5) return (ma_int16) 32767;
-    if (sample <= -32767.5) return (ma_int16)-32768;
+    if (sample >=  32766.5f) return (ma_int16) 32767;
+    if (sample <= -32767.5f) return (ma_int16)-32768;
     s = (ma_int16)(sample + .5f);
     s -= (s < 0);
 #endif
@@ -91534,9 +92369,9 @@ MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_s
     for(; i < num_samples; i++)
     {
         float sample = in[i] * 32768.0f;
-        if (sample >=  32766.5)
+        if (sample >=  32766.5f)
             out[i] = (ma_int16) 32767;
-        else if (sample <= -32767.5)
+        else if (sample <= -32767.5f)
             out[i] = (ma_int16)-32768;
         else
         {
@@ -92614,7 +93449,7 @@ For more information, please refer to 
 ===============================================================================
 ALTERNATIVE 2 - MIT No Attribution
 ===============================================================================
-Copyright 2023 David Reid
+Copyright 2025 David Reid
 
 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
diff --git a/raylib/external/sinfl.h b/raylib/external/sinfl.h
index 3c71737..a749501 100644
--- a/raylib/external/sinfl.h
+++ b/raylib/external/sinfl.h
@@ -171,10 +171,11 @@ extern int zsinflate(void *out, int cap, const void *in, int size);
 
 static int
 sinfl_bsr(unsigned n) {
-#if defined(_MSC_VER) && !defined(__clang__)
-  _BitScanReverse(&n, n);
-  return n;
-#elif defined(__GNUC__) || defined(__clang__)
+#ifdef _MSC_VER
+  unsigned long uln = 0;
+  _BitScanReverse(&uln, n);
+  return (int)(uln);
+#else // defined(__GNUC__) || defined(__clang__) || defined(__TINYC__)
   return 31 - __builtin_clz(n);
 #endif
 }
diff --git a/raylib/external/stb_truetype.h b/raylib/external/stb_truetype.h
index 90a5c2e..491a854 100644
--- a/raylib/external/stb_truetype.h
+++ b/raylib/external/stb_truetype.h
@@ -1863,11 +1863,11 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s
                stbtt_vertex* v = &comp_verts[i];
                stbtt_vertex_type x,y;
                x=v->x; y=v->y;
-               v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
-               v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
+               v->x = (stbtt_vertex_type)(mtx[0]*x + mtx[2]*y + mtx[4]*m);
+               v->y = (stbtt_vertex_type)(mtx[1]*x + mtx[3]*y + mtx[5]*n);
                x=v->cx; y=v->cy;
-               v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
-               v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
+               v->cx = (stbtt_vertex_type)(mtx[0]*x + mtx[2]*y + mtx[4]*m);
+               v->cy = (stbtt_vertex_type)(mtx[1]*x + mtx[3]*y + mtx[5]*n);
             }
             // Append vertices.
             tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata);
diff --git a/raylib/external/vendor.go b/raylib/external/vendor.go
new file mode 100644
index 0000000..0fb2a0f
--- /dev/null
+++ b/raylib/external/vendor.go
@@ -0,0 +1,4 @@
+//go:build required
+// +build required
+
+package vendor
diff --git a/raylib/go.mod b/raylib/go.mod
index 4dc010b..fe16d16 100644
--- a/raylib/go.mod
+++ b/raylib/go.mod
@@ -1,4 +1,4 @@
-module github.com/gen2brain/raylib-go/raylib
+module git.terah.dev/UnrealXR/raylib-go/raylib
 
 go 1.21
 
diff --git a/raylib/platform_desktop.go b/raylib/platform_desktop.go
index bda078d..91634fb 100644
--- a/raylib/platform_desktop.go
+++ b/raylib/platform_desktop.go
@@ -1,5 +1,5 @@
-//go:build !rgfw && !sdl && !drm && !android
-// +build !rgfw,!sdl,!drm,!android
+//go:build !rgfw && !sdl && !sdl3 && !drm && !android
+// +build !rgfw,!sdl,!sdl3,!drm,!android
 
 package rl
 
diff --git a/raylib/platform_desktop_rgfw.go b/raylib/platform_desktop_rgfw.go
index 5cfe6a2..ba77eec 100644
--- a/raylib/platform_desktop_rgfw.go
+++ b/raylib/platform_desktop_rgfw.go
@@ -1,5 +1,5 @@
-//go:build rgfw && !sdl && !drm && !android
-// +build rgfw,!sdl,!drm,!android
+//go:build rgfw && !sdl && !sdl3 && !drm && !android
+// +build rgfw,!sdl,!sdl3,!drm,!android
 
 package rl
 
diff --git a/raylib/platform_desktop_sdl.go b/raylib/platform_desktop_sdl.go
index ee930d6..82cf7ad 100644
--- a/raylib/platform_desktop_sdl.go
+++ b/raylib/platform_desktop_sdl.go
@@ -1,5 +1,8 @@
-//go:build sdl && !rgfw && !drm && !android
-// +build sdl,!rgfw,!drm,!android
+//go:build (sdl || sdl3) && !rgfw && !drm && !android
+// +build sdl sdl3
+// +build !rgfw
+// +build !drm
+// +build !android
 
 package rl
 
diff --git a/raylib/platform_drm.go b/raylib/platform_drm.go
index 6618e0e..fa036d5 100644
--- a/raylib/platform_drm.go
+++ b/raylib/platform_drm.go
@@ -1,5 +1,5 @@
-//go:build linux && drm && !rgfw && !sdl && !android
-// +build linux,drm,!rgfw,!sdl,!android
+//go:build linux && drm && !rgfw && !sdl && !sdl3 && !android
+// +build linux,drm,!rgfw,!sdl,!sdl3,!android
 
 package rl
 
diff --git a/raylib/platforms/drm-lease-v1.c b/raylib/platforms/drm-lease-v1.c
new file mode 100644
index 0000000..5f75126
--- /dev/null
+++ b/raylib/platforms/drm-lease-v1.c
@@ -0,0 +1,116 @@
+/* Generated by wayland-scanner 1.23.1 */
+
+/*
+ * Copyright © 2018 NXP
+ * Copyright © 2019 Status Research & Development GmbH.
+ * Copyright © 2021 Xaver Hugl
+ *
+ * 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.
+ */
+
+#include 
+#include 
+#include 
+#include "wayland-util.h"
+
+#ifndef __has_attribute
+# define __has_attribute(x) 0  /* Compatibility with non-clang compilers. */
+#endif
+
+#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
+#define WL_PRIVATE __attribute__ ((visibility("hidden")))
+#else
+#define WL_PRIVATE
+#endif
+
+extern const struct wl_interface wp_drm_lease_connector_v1_interface;
+extern const struct wl_interface wp_drm_lease_request_v1_interface;
+extern const struct wl_interface wp_drm_lease_v1_interface;
+
+static const struct wl_interface *drm_lease_v1_types[] = {
+	NULL,
+	&wp_drm_lease_request_v1_interface,
+	&wp_drm_lease_connector_v1_interface,
+	&wp_drm_lease_connector_v1_interface,
+	&wp_drm_lease_v1_interface,
+};
+
+static const struct wl_message wp_drm_lease_device_v1_requests[] = {
+	{ "create_lease_request", "n", drm_lease_v1_types + 1 },
+	{ "release", "", drm_lease_v1_types + 0 },
+};
+
+static const struct wl_message wp_drm_lease_device_v1_events[] = {
+	{ "drm_fd", "h", drm_lease_v1_types + 0 },
+	{ "connector", "n", drm_lease_v1_types + 2 },
+	{ "done", "", drm_lease_v1_types + 0 },
+	{ "released", "", drm_lease_v1_types + 0 },
+};
+
+WL_PRIVATE const struct wl_interface wp_drm_lease_device_v1_interface = {
+	"wp_drm_lease_device_v1", 1,
+	2, wp_drm_lease_device_v1_requests,
+	4, wp_drm_lease_device_v1_events,
+};
+
+static const struct wl_message wp_drm_lease_connector_v1_requests[] = {
+	{ "destroy", "", drm_lease_v1_types + 0 },
+};
+
+static const struct wl_message wp_drm_lease_connector_v1_events[] = {
+	{ "name", "s", drm_lease_v1_types + 0 },
+	{ "description", "s", drm_lease_v1_types + 0 },
+	{ "connector_id", "u", drm_lease_v1_types + 0 },
+	{ "done", "", drm_lease_v1_types + 0 },
+	{ "withdrawn", "", drm_lease_v1_types + 0 },
+};
+
+WL_PRIVATE const struct wl_interface wp_drm_lease_connector_v1_interface = {
+	"wp_drm_lease_connector_v1", 1,
+	1, wp_drm_lease_connector_v1_requests,
+	5, wp_drm_lease_connector_v1_events,
+};
+
+static const struct wl_message wp_drm_lease_request_v1_requests[] = {
+	{ "request_connector", "o", drm_lease_v1_types + 3 },
+	{ "submit", "n", drm_lease_v1_types + 4 },
+};
+
+WL_PRIVATE const struct wl_interface wp_drm_lease_request_v1_interface = {
+	"wp_drm_lease_request_v1", 1,
+	2, wp_drm_lease_request_v1_requests,
+	0, NULL,
+};
+
+static const struct wl_message wp_drm_lease_v1_requests[] = {
+	{ "destroy", "", drm_lease_v1_types + 0 },
+};
+
+static const struct wl_message wp_drm_lease_v1_events[] = {
+	{ "lease_fd", "h", drm_lease_v1_types + 0 },
+	{ "finished", "", drm_lease_v1_types + 0 },
+};
+
+WL_PRIVATE const struct wl_interface wp_drm_lease_v1_interface = {
+	"wp_drm_lease_v1", 1,
+	1, wp_drm_lease_v1_requests,
+	2, wp_drm_lease_v1_events,
+};
+
diff --git a/raylib/platforms/drm-lease-v1.h b/raylib/platforms/drm-lease-v1.h
new file mode 100644
index 0000000..ef24934
--- /dev/null
+++ b/raylib/platforms/drm-lease-v1.h
@@ -0,0 +1,743 @@
+/* Generated by wayland-scanner 1.23.1 */
+
+#ifndef DRM_LEASE_V1_CLIENT_PROTOCOL_H
+#define DRM_LEASE_V1_CLIENT_PROTOCOL_H
+
+#include 
+#include 
+#include "wayland-client.h"
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @page page_drm_lease_v1 The drm_lease_v1 protocol
+ * @section page_ifaces_drm_lease_v1 Interfaces
+ * - @subpage page_iface_wp_drm_lease_device_v1 - lease device
+ * - @subpage page_iface_wp_drm_lease_connector_v1 - a leasable DRM connector
+ * - @subpage page_iface_wp_drm_lease_request_v1 - DRM lease request
+ * - @subpage page_iface_wp_drm_lease_v1 - a DRM lease
+ * @section page_copyright_drm_lease_v1 Copyright
+ * 
+ *
+ * Copyright © 2018 NXP
+ * Copyright © 2019 Status Research & Development GmbH.
+ * Copyright © 2021 Xaver Hugl
+ *
+ * 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.
+ * 
+ */ +struct wp_drm_lease_connector_v1; +struct wp_drm_lease_device_v1; +struct wp_drm_lease_request_v1; +struct wp_drm_lease_v1; + +#ifndef WP_DRM_LEASE_DEVICE_V1_INTERFACE +#define WP_DRM_LEASE_DEVICE_V1_INTERFACE +/** + * @page page_iface_wp_drm_lease_device_v1 wp_drm_lease_device_v1 + * @section page_iface_wp_drm_lease_device_v1_desc Description + * + * This protocol is used by Wayland compositors which act as Direct + * Rendering Manager (DRM) masters to lease DRM resources to Wayland + * clients. + * + * The compositor will advertise one wp_drm_lease_device_v1 global for each + * DRM node. Some time after a client binds to the wp_drm_lease_device_v1 + * global, the compositor will send a drm_fd event followed by zero, one or + * more connector events. After all currently available connectors have been + * sent, the compositor will send a wp_drm_lease_device_v1.done event. + * + * When the list of connectors available for lease changes the compositor + * will send wp_drm_lease_device_v1.connector events for added connectors and + * wp_drm_lease_connector_v1.withdrawn events for removed connectors, + * followed by a wp_drm_lease_device_v1.done event. + * + * The compositor will indicate when a device is gone by removing the global + * via a wl_registry.global_remove event. Upon receiving this event, the + * client should destroy any matching wp_drm_lease_device_v1 object. + * + * To destroy a wp_drm_lease_device_v1 object, the client must first issue + * a release request. Upon receiving this request, the compositor will + * immediately send a released event and destroy the object. The client must + * continue to process and discard drm_fd and connector events until it + * receives the released event. Upon receiving the released event, the + * client can safely cleanup any client-side resources. + * + * 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. + * @section page_iface_wp_drm_lease_device_v1_api API + * See @ref iface_wp_drm_lease_device_v1. + */ +/** + * @defgroup iface_wp_drm_lease_device_v1 The wp_drm_lease_device_v1 interface + * + * This protocol is used by Wayland compositors which act as Direct + * Rendering Manager (DRM) masters to lease DRM resources to Wayland + * clients. + * + * The compositor will advertise one wp_drm_lease_device_v1 global for each + * DRM node. Some time after a client binds to the wp_drm_lease_device_v1 + * global, the compositor will send a drm_fd event followed by zero, one or + * more connector events. After all currently available connectors have been + * sent, the compositor will send a wp_drm_lease_device_v1.done event. + * + * When the list of connectors available for lease changes the compositor + * will send wp_drm_lease_device_v1.connector events for added connectors and + * wp_drm_lease_connector_v1.withdrawn events for removed connectors, + * followed by a wp_drm_lease_device_v1.done event. + * + * The compositor will indicate when a device is gone by removing the global + * via a wl_registry.global_remove event. Upon receiving this event, the + * client should destroy any matching wp_drm_lease_device_v1 object. + * + * To destroy a wp_drm_lease_device_v1 object, the client must first issue + * a release request. Upon receiving this request, the compositor will + * immediately send a released event and destroy the object. The client must + * continue to process and discard drm_fd and connector events until it + * receives the released event. Upon receiving the released event, the + * client can safely cleanup any client-side resources. + * + * 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. + */ +extern const struct wl_interface wp_drm_lease_device_v1_interface; +#endif +#ifndef WP_DRM_LEASE_CONNECTOR_V1_INTERFACE +#define WP_DRM_LEASE_CONNECTOR_V1_INTERFACE +/** + * @page page_iface_wp_drm_lease_connector_v1 wp_drm_lease_connector_v1 + * @section page_iface_wp_drm_lease_connector_v1_desc Description + * + * Represents a DRM connector which is available for lease. These objects are + * created via wp_drm_lease_device_v1.connector events, and should be passed + * to lease requests via wp_drm_lease_request_v1.request_connector. + * Immediately after the wp_drm_lease_connector_v1 object is created the + * compositor will send a name, a description, a connector_id and a done + * event. When the description is updated the compositor will send a + * description event followed by a done event. + * @section page_iface_wp_drm_lease_connector_v1_api API + * See @ref iface_wp_drm_lease_connector_v1. + */ +/** + * @defgroup iface_wp_drm_lease_connector_v1 The wp_drm_lease_connector_v1 interface + * + * Represents a DRM connector which is available for lease. These objects are + * created via wp_drm_lease_device_v1.connector events, and should be passed + * to lease requests via wp_drm_lease_request_v1.request_connector. + * Immediately after the wp_drm_lease_connector_v1 object is created the + * compositor will send a name, a description, a connector_id and a done + * event. When the description is updated the compositor will send a + * description event followed by a done event. + */ +extern const struct wl_interface wp_drm_lease_connector_v1_interface; +#endif +#ifndef WP_DRM_LEASE_REQUEST_V1_INTERFACE +#define WP_DRM_LEASE_REQUEST_V1_INTERFACE +/** + * @page page_iface_wp_drm_lease_request_v1 wp_drm_lease_request_v1 + * @section page_iface_wp_drm_lease_request_v1_desc Description + * + * A client that wishes to lease DRM resources will attach the list of + * connectors advertised with wp_drm_lease_device_v1.connector that they + * wish to lease, then use wp_drm_lease_request_v1.submit to submit the + * request. + * @section page_iface_wp_drm_lease_request_v1_api API + * See @ref iface_wp_drm_lease_request_v1. + */ +/** + * @defgroup iface_wp_drm_lease_request_v1 The wp_drm_lease_request_v1 interface + * + * A client that wishes to lease DRM resources will attach the list of + * connectors advertised with wp_drm_lease_device_v1.connector that they + * wish to lease, then use wp_drm_lease_request_v1.submit to submit the + * request. + */ +extern const struct wl_interface wp_drm_lease_request_v1_interface; +#endif +#ifndef WP_DRM_LEASE_V1_INTERFACE +#define WP_DRM_LEASE_V1_INTERFACE +/** + * @page page_iface_wp_drm_lease_v1 wp_drm_lease_v1 + * @section page_iface_wp_drm_lease_v1_desc Description + * + * A DRM lease object is used to transfer the DRM file descriptor to the + * client and manage the lifetime of the lease. + * + * Some time after the wp_drm_lease_v1 object is created, the compositor + * will reply with the lease request's result. If the lease request is + * granted, the compositor will send a lease_fd event. If the lease request + * is denied, the compositor will send a finished event without a lease_fd + * event. + * @section page_iface_wp_drm_lease_v1_api API + * See @ref iface_wp_drm_lease_v1. + */ +/** + * @defgroup iface_wp_drm_lease_v1 The wp_drm_lease_v1 interface + * + * A DRM lease object is used to transfer the DRM file descriptor to the + * client and manage the lifetime of the lease. + * + * Some time after the wp_drm_lease_v1 object is created, the compositor + * will reply with the lease request's result. If the lease request is + * granted, the compositor will send a lease_fd event. If the lease request + * is denied, the compositor will send a finished event without a lease_fd + * event. + */ +extern const struct wl_interface wp_drm_lease_v1_interface; +#endif + +/** + * @ingroup iface_wp_drm_lease_device_v1 + * @struct wp_drm_lease_device_v1_listener + */ +struct wp_drm_lease_device_v1_listener { + /** + * open a non-master fd for this DRM node + * + * The compositor will send this event when the + * wp_drm_lease_device_v1 global is bound, although there are no + * guarantees as to how long this takes - the compositor might need + * to wait until regaining DRM master. The included fd is a + * non-master DRM file descriptor opened for this device and the + * compositor must not authenticate it. The purpose of this event + * is to give the client the ability to query DRM and discover + * information which may help them pick the appropriate DRM device + * or select the appropriate connectors therein. + * @param fd DRM file descriptor + */ + void (*drm_fd)(void *data, + struct wp_drm_lease_device_v1 *wp_drm_lease_device_v1, + int32_t fd); + /** + * advertise connectors available for leases + * + * The compositor will use this event to advertise connectors + * available for lease by clients. This object may be passed into a + * lease request to indicate the client would like to lease that + * connector, see wp_drm_lease_request_v1.request_connector for + * details. While the compositor will make a best effort to not + * send disconnected connectors, no guarantees can be made. + * + * The compositor must send the drm_fd event before sending + * connectors. After the drm_fd event it will send all available + * connectors but may send additional connectors at any time. + */ + void (*connector)(void *data, + struct wp_drm_lease_device_v1 *wp_drm_lease_device_v1, + struct wp_drm_lease_connector_v1 *id); + /** + * signals grouping of connectors + * + * The compositor will send this event to indicate that it has + * sent all currently available connectors after the client binds + * to the global or when it updates the connector list, for example + * on hotplug, drm master change or when a leased connector becomes + * available again. It will similarly send this event to group + * wp_drm_lease_connector_v1.withdrawn events of connectors of this + * device. + */ + void (*done)(void *data, + struct wp_drm_lease_device_v1 *wp_drm_lease_device_v1); + /** + * the compositor has finished using the device + * + * This event is sent in response to the release request and + * indicates that the compositor is done sending connector events. + * The compositor will destroy this object immediately after + * sending the event and it will become invalid. The client should + * release any resources associated with this device after + * receiving this event. + */ + void (*released)(void *data, + struct wp_drm_lease_device_v1 *wp_drm_lease_device_v1); +}; + +/** + * @ingroup iface_wp_drm_lease_device_v1 + */ +static inline int +wp_drm_lease_device_v1_add_listener(struct wp_drm_lease_device_v1 *wp_drm_lease_device_v1, + const struct wp_drm_lease_device_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) wp_drm_lease_device_v1, + (void (**)(void)) listener, data); +} + +#define WP_DRM_LEASE_DEVICE_V1_CREATE_LEASE_REQUEST 0 +#define WP_DRM_LEASE_DEVICE_V1_RELEASE 1 + +/** + * @ingroup iface_wp_drm_lease_device_v1 + */ +#define WP_DRM_LEASE_DEVICE_V1_DRM_FD_SINCE_VERSION 1 +/** + * @ingroup iface_wp_drm_lease_device_v1 + */ +#define WP_DRM_LEASE_DEVICE_V1_CONNECTOR_SINCE_VERSION 1 +/** + * @ingroup iface_wp_drm_lease_device_v1 + */ +#define WP_DRM_LEASE_DEVICE_V1_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_wp_drm_lease_device_v1 + */ +#define WP_DRM_LEASE_DEVICE_V1_RELEASED_SINCE_VERSION 1 + +/** + * @ingroup iface_wp_drm_lease_device_v1 + */ +#define WP_DRM_LEASE_DEVICE_V1_CREATE_LEASE_REQUEST_SINCE_VERSION 1 +/** + * @ingroup iface_wp_drm_lease_device_v1 + */ +#define WP_DRM_LEASE_DEVICE_V1_RELEASE_SINCE_VERSION 1 + +/** @ingroup iface_wp_drm_lease_device_v1 */ +static inline void +wp_drm_lease_device_v1_set_user_data(struct wp_drm_lease_device_v1 *wp_drm_lease_device_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) wp_drm_lease_device_v1, user_data); +} + +/** @ingroup iface_wp_drm_lease_device_v1 */ +static inline void * +wp_drm_lease_device_v1_get_user_data(struct wp_drm_lease_device_v1 *wp_drm_lease_device_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) wp_drm_lease_device_v1); +} + +static inline uint32_t +wp_drm_lease_device_v1_get_version(struct wp_drm_lease_device_v1 *wp_drm_lease_device_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) wp_drm_lease_device_v1); +} + +/** @ingroup iface_wp_drm_lease_device_v1 */ +static inline void +wp_drm_lease_device_v1_destroy(struct wp_drm_lease_device_v1 *wp_drm_lease_device_v1) +{ + wl_proxy_destroy((struct wl_proxy *) wp_drm_lease_device_v1); +} + +/** + * @ingroup iface_wp_drm_lease_device_v1 + * + * Creates a lease request object. + * + * See the documentation for wp_drm_lease_request_v1 for details. + */ +static inline struct wp_drm_lease_request_v1 * +wp_drm_lease_device_v1_create_lease_request(struct wp_drm_lease_device_v1 *wp_drm_lease_device_v1) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) wp_drm_lease_device_v1, + WP_DRM_LEASE_DEVICE_V1_CREATE_LEASE_REQUEST, &wp_drm_lease_request_v1_interface, wl_proxy_get_version((struct wl_proxy *) wp_drm_lease_device_v1), 0, NULL); + + return (struct wp_drm_lease_request_v1 *) id; +} + +/** + * @ingroup iface_wp_drm_lease_device_v1 + * + * Indicates the client no longer wishes to use this object. In response + * the compositor will immediately send the released event and destroy + * this object. It can however not guarantee that the client won't receive + * connector events before the released event. The client must not send any + * requests after this one, doing so will raise a wl_display error. + * Existing connectors, lease request and leases will not be affected. + */ +static inline void +wp_drm_lease_device_v1_release(struct wp_drm_lease_device_v1 *wp_drm_lease_device_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) wp_drm_lease_device_v1, + WP_DRM_LEASE_DEVICE_V1_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) wp_drm_lease_device_v1), 0); +} + +/** + * @ingroup iface_wp_drm_lease_connector_v1 + * @struct wp_drm_lease_connector_v1_listener + */ +struct wp_drm_lease_connector_v1_listener { + /** + * name + * + * The compositor sends this event once the connector is created + * to indicate the name of this connector. This will not change for + * the duration of the Wayland session, but is not guaranteed to be + * consistent between sessions. + * + * If the compositor supports wl_output version 4 and this + * connector corresponds to a wl_output, the compositor should use + * the same name as for the wl_output. + * @param name connector name + */ + void (*name)(void *data, + struct wp_drm_lease_connector_v1 *wp_drm_lease_connector_v1, + const char *name); + /** + * description + * + * The compositor sends this event once the connector is created + * to provide a human-readable description for this connector, + * which may be presented to the user. The compositor may send this + * event multiple times over the lifetime of this object to reflect + * changes in the description. + * @param description connector description + */ + void (*description)(void *data, + struct wp_drm_lease_connector_v1 *wp_drm_lease_connector_v1, + const char *description); + /** + * connector_id + * + * The compositor sends this event once the connector is created + * to indicate the DRM object ID which represents the underlying + * connector that is being offered. Note that the final lease may + * include additional object IDs, such as CRTCs and planes. + * @param connector_id DRM connector ID + */ + void (*connector_id)(void *data, + struct wp_drm_lease_connector_v1 *wp_drm_lease_connector_v1, + uint32_t connector_id); + /** + * all properties have been sent + * + * This event is sent after all properties of a connector have + * been sent. This allows changes to the properties to be seen as + * atomic even if they happen via multiple events. + */ + void (*done)(void *data, + struct wp_drm_lease_connector_v1 *wp_drm_lease_connector_v1); + /** + * lease offer withdrawn + * + * Sent to indicate that the compositor will no longer honor + * requests for DRM leases which include this connector. The client + * may still issue a lease request including this connector, but + * the compositor will send wp_drm_lease_v1.finished without + * issuing a lease fd. Compositors are encouraged to send this + * event when they lose access to connector, for example when the + * connector is hot-unplugged, when the connector gets leased to a + * client or when the compositor loses DRM master. + * + * If a client holds a lease for the connector, the status of the + * lease remains the same. The client should destroy the object + * after receiving this event. + */ + void (*withdrawn)(void *data, + struct wp_drm_lease_connector_v1 *wp_drm_lease_connector_v1); +}; + +/** + * @ingroup iface_wp_drm_lease_connector_v1 + */ +static inline int +wp_drm_lease_connector_v1_add_listener(struct wp_drm_lease_connector_v1 *wp_drm_lease_connector_v1, + const struct wp_drm_lease_connector_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) wp_drm_lease_connector_v1, + (void (**)(void)) listener, data); +} + +#define WP_DRM_LEASE_CONNECTOR_V1_DESTROY 0 + +/** + * @ingroup iface_wp_drm_lease_connector_v1 + */ +#define WP_DRM_LEASE_CONNECTOR_V1_NAME_SINCE_VERSION 1 +/** + * @ingroup iface_wp_drm_lease_connector_v1 + */ +#define WP_DRM_LEASE_CONNECTOR_V1_DESCRIPTION_SINCE_VERSION 1 +/** + * @ingroup iface_wp_drm_lease_connector_v1 + */ +#define WP_DRM_LEASE_CONNECTOR_V1_CONNECTOR_ID_SINCE_VERSION 1 +/** + * @ingroup iface_wp_drm_lease_connector_v1 + */ +#define WP_DRM_LEASE_CONNECTOR_V1_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_wp_drm_lease_connector_v1 + */ +#define WP_DRM_LEASE_CONNECTOR_V1_WITHDRAWN_SINCE_VERSION 1 + +/** + * @ingroup iface_wp_drm_lease_connector_v1 + */ +#define WP_DRM_LEASE_CONNECTOR_V1_DESTROY_SINCE_VERSION 1 + +/** @ingroup iface_wp_drm_lease_connector_v1 */ +static inline void +wp_drm_lease_connector_v1_set_user_data(struct wp_drm_lease_connector_v1 *wp_drm_lease_connector_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) wp_drm_lease_connector_v1, user_data); +} + +/** @ingroup iface_wp_drm_lease_connector_v1 */ +static inline void * +wp_drm_lease_connector_v1_get_user_data(struct wp_drm_lease_connector_v1 *wp_drm_lease_connector_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) wp_drm_lease_connector_v1); +} + +static inline uint32_t +wp_drm_lease_connector_v1_get_version(struct wp_drm_lease_connector_v1 *wp_drm_lease_connector_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) wp_drm_lease_connector_v1); +} + +/** + * @ingroup iface_wp_drm_lease_connector_v1 + * + * The client may send this request to indicate that it will not use this + * connector. Clients are encouraged to send this after receiving the + * "withdrawn" event so that the server can release the resources + * associated with this connector offer. Neither existing lease requests + * nor leases will be affected. + */ +static inline void +wp_drm_lease_connector_v1_destroy(struct wp_drm_lease_connector_v1 *wp_drm_lease_connector_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) wp_drm_lease_connector_v1, + WP_DRM_LEASE_CONNECTOR_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wp_drm_lease_connector_v1), WL_MARSHAL_FLAG_DESTROY); +} + +#ifndef WP_DRM_LEASE_REQUEST_V1_ERROR_ENUM +#define WP_DRM_LEASE_REQUEST_V1_ERROR_ENUM +enum wp_drm_lease_request_v1_error { + /** + * requested a connector from a different lease device + */ + WP_DRM_LEASE_REQUEST_V1_ERROR_WRONG_DEVICE = 0, + /** + * requested a connector twice + */ + WP_DRM_LEASE_REQUEST_V1_ERROR_DUPLICATE_CONNECTOR = 1, + /** + * requested a lease without requesting a connector + */ + WP_DRM_LEASE_REQUEST_V1_ERROR_EMPTY_LEASE = 2, +}; +#endif /* WP_DRM_LEASE_REQUEST_V1_ERROR_ENUM */ + +#define WP_DRM_LEASE_REQUEST_V1_REQUEST_CONNECTOR 0 +#define WP_DRM_LEASE_REQUEST_V1_SUBMIT 1 + + +/** + * @ingroup iface_wp_drm_lease_request_v1 + */ +#define WP_DRM_LEASE_REQUEST_V1_REQUEST_CONNECTOR_SINCE_VERSION 1 +/** + * @ingroup iface_wp_drm_lease_request_v1 + */ +#define WP_DRM_LEASE_REQUEST_V1_SUBMIT_SINCE_VERSION 1 + +/** @ingroup iface_wp_drm_lease_request_v1 */ +static inline void +wp_drm_lease_request_v1_set_user_data(struct wp_drm_lease_request_v1 *wp_drm_lease_request_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) wp_drm_lease_request_v1, user_data); +} + +/** @ingroup iface_wp_drm_lease_request_v1 */ +static inline void * +wp_drm_lease_request_v1_get_user_data(struct wp_drm_lease_request_v1 *wp_drm_lease_request_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) wp_drm_lease_request_v1); +} + +static inline uint32_t +wp_drm_lease_request_v1_get_version(struct wp_drm_lease_request_v1 *wp_drm_lease_request_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) wp_drm_lease_request_v1); +} + +/** @ingroup iface_wp_drm_lease_request_v1 */ +static inline void +wp_drm_lease_request_v1_destroy(struct wp_drm_lease_request_v1 *wp_drm_lease_request_v1) +{ + wl_proxy_destroy((struct wl_proxy *) wp_drm_lease_request_v1); +} + +/** + * @ingroup iface_wp_drm_lease_request_v1 + * + * Indicates that the client would like to lease the given connector. + * This is only used as a suggestion, the compositor may choose to + * include any resources in the lease it issues, or change the set of + * leased resources at any time. Compositors are however encouraged to + * include the requested connector and other resources necessary + * to drive the connected output in the lease. + * + * Requesting a connector that was created from a different lease device + * than this lease request raises the wrong_device error. Requesting a + * connector twice will raise the duplicate_connector error. + */ +static inline void +wp_drm_lease_request_v1_request_connector(struct wp_drm_lease_request_v1 *wp_drm_lease_request_v1, struct wp_drm_lease_connector_v1 *connector) +{ + wl_proxy_marshal_flags((struct wl_proxy *) wp_drm_lease_request_v1, + WP_DRM_LEASE_REQUEST_V1_REQUEST_CONNECTOR, NULL, wl_proxy_get_version((struct wl_proxy *) wp_drm_lease_request_v1), 0, connector); +} + +/** + * @ingroup iface_wp_drm_lease_request_v1 + * + * Submits the lease request and creates a new wp_drm_lease_v1 object. + * After calling submit the compositor will immediately destroy this + * object, issuing any more requests will cause a wl_display error. + * The compositor doesn't make any guarantees about the events of the + * lease object, clients cannot expect an immediate response. + * Not requesting any connectors before submitting the lease request + * will raise the empty_lease error. + */ +static inline struct wp_drm_lease_v1 * +wp_drm_lease_request_v1_submit(struct wp_drm_lease_request_v1 *wp_drm_lease_request_v1) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) wp_drm_lease_request_v1, + WP_DRM_LEASE_REQUEST_V1_SUBMIT, &wp_drm_lease_v1_interface, wl_proxy_get_version((struct wl_proxy *) wp_drm_lease_request_v1), WL_MARSHAL_FLAG_DESTROY, NULL); + + return (struct wp_drm_lease_v1 *) id; +} + +/** + * @ingroup iface_wp_drm_lease_v1 + * @struct wp_drm_lease_v1_listener + */ +struct wp_drm_lease_v1_listener { + /** + * shares the DRM file descriptor + * + * This event returns a file descriptor suitable for use with + * DRM-related ioctls. The client should use drmModeGetLease to + * enumerate the DRM objects which have been leased to them. The + * compositor guarantees it will not use the leased DRM objects + * itself until it sends the finished event. If the compositor + * cannot or will not grant a lease for the requested connectors, + * it will not send this event, instead sending the finished event. + * + * The compositor will send this event at most once during this + * objects lifetime. + * @param leased_fd leased DRM file descriptor + */ + void (*lease_fd)(void *data, + struct wp_drm_lease_v1 *wp_drm_lease_v1, + int32_t leased_fd); + /** + * sent when the lease has been revoked + * + * The compositor uses this event to either reject a lease + * request, or if it previously sent a lease_fd, to notify the + * client that the lease has been revoked. If the client requires a + * new lease, they should destroy this object and submit a new + * lease request. The compositor will send no further events for + * this object after sending the finish event. Compositors should + * revoke the lease when any of the leased resources become + * unavailable, namely when a hot-unplug occurs or when the + * compositor loses DRM master. Compositors may advertise the + * connector for leasing again, if the resource is available, by + * sending the connector event through the wp_drm_lease_device_v1 + * interface. + */ + void (*finished)(void *data, + struct wp_drm_lease_v1 *wp_drm_lease_v1); +}; + +/** + * @ingroup iface_wp_drm_lease_v1 + */ +static inline int +wp_drm_lease_v1_add_listener(struct wp_drm_lease_v1 *wp_drm_lease_v1, + const struct wp_drm_lease_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) wp_drm_lease_v1, + (void (**)(void)) listener, data); +} + +#define WP_DRM_LEASE_V1_DESTROY 0 + +/** + * @ingroup iface_wp_drm_lease_v1 + */ +#define WP_DRM_LEASE_V1_LEASE_FD_SINCE_VERSION 1 +/** + * @ingroup iface_wp_drm_lease_v1 + */ +#define WP_DRM_LEASE_V1_FINISHED_SINCE_VERSION 1 + +/** + * @ingroup iface_wp_drm_lease_v1 + */ +#define WP_DRM_LEASE_V1_DESTROY_SINCE_VERSION 1 + +/** @ingroup iface_wp_drm_lease_v1 */ +static inline void +wp_drm_lease_v1_set_user_data(struct wp_drm_lease_v1 *wp_drm_lease_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) wp_drm_lease_v1, user_data); +} + +/** @ingroup iface_wp_drm_lease_v1 */ +static inline void * +wp_drm_lease_v1_get_user_data(struct wp_drm_lease_v1 *wp_drm_lease_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) wp_drm_lease_v1); +} + +static inline uint32_t +wp_drm_lease_v1_get_version(struct wp_drm_lease_v1 *wp_drm_lease_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) wp_drm_lease_v1); +} + +/** + * @ingroup iface_wp_drm_lease_v1 + * + * The client should send this to indicate that it no longer wishes to use + * this lease. The compositor should use drmModeRevokeLease on the + * appropriate file descriptor, if necessary. + * + * Upon destruction, the compositor should advertise the connector for + * leasing again by sending the connector event through the + * wp_drm_lease_device_v1 interface. + */ +static inline void +wp_drm_lease_v1_destroy(struct wp_drm_lease_v1 *wp_drm_lease_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) wp_drm_lease_v1, + WP_DRM_LEASE_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wp_drm_lease_v1), WL_MARSHAL_FLAG_DESTROY); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/raylib/platforms/drm-lease-v1.xml b/raylib/platforms/drm-lease-v1.xml new file mode 100644 index 0000000..1fca538 --- /dev/null +++ b/raylib/platforms/drm-lease-v1.xml @@ -0,0 +1,317 @@ + + + + Copyright © 2018 NXP + Copyright © 2019 Status Research & Development GmbH. + Copyright © 2021 Xaver Hugl + + 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 is used by Wayland compositors which act as Direct + Rendering Manager (DRM) masters to lease DRM resources to Wayland + clients. + + The compositor will advertise one wp_drm_lease_device_v1 global for each + DRM node. Some time after a client binds to the wp_drm_lease_device_v1 + global, the compositor will send a drm_fd event followed by zero, one or + more connector events. After all currently available connectors have been + sent, the compositor will send a wp_drm_lease_device_v1.done event. + + When the list of connectors available for lease changes the compositor + will send wp_drm_lease_device_v1.connector events for added connectors and + wp_drm_lease_connector_v1.withdrawn events for removed connectors, + followed by a wp_drm_lease_device_v1.done event. + + The compositor will indicate when a device is gone by removing the global + via a wl_registry.global_remove event. Upon receiving this event, the + client should destroy any matching wp_drm_lease_device_v1 object. + + To destroy a wp_drm_lease_device_v1 object, the client must first issue + a release request. Upon receiving this request, the compositor will + immediately send a released event and destroy the object. The client must + continue to process and discard drm_fd and connector events until it + receives the released event. Upon receiving the released event, the + client can safely cleanup any client-side resources. + + 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. + + + + + Creates a lease request object. + + See the documentation for wp_drm_lease_request_v1 for details. + + + + + + + Indicates the client no longer wishes to use this object. In response + the compositor will immediately send the released event and destroy + this object. It can however not guarantee that the client won't receive + connector events before the released event. The client must not send any + requests after this one, doing so will raise a wl_display error. + Existing connectors, lease request and leases will not be affected. + + + + + + The compositor will send this event when the wp_drm_lease_device_v1 + global is bound, although there are no guarantees as to how long this + takes - the compositor might need to wait until regaining DRM master. + The included fd is a non-master DRM file descriptor opened for this + device and the compositor must not authenticate it. + The purpose of this event is to give the client the ability to + query DRM and discover information which may help them pick the + appropriate DRM device or select the appropriate connectors therein. + + + + + + + The compositor will use this event to advertise connectors available for + lease by clients. This object may be passed into a lease request to + indicate the client would like to lease that connector, see + wp_drm_lease_request_v1.request_connector for details. While the + compositor will make a best effort to not send disconnected connectors, + no guarantees can be made. + + The compositor must send the drm_fd event before sending connectors. + After the drm_fd event it will send all available connectors but may + send additional connectors at any time. + + + + + + + The compositor will send this event to indicate that it has sent all + currently available connectors after the client binds to the global or + when it updates the connector list, for example on hotplug, drm master + change or when a leased connector becomes available again. It will + similarly send this event to group wp_drm_lease_connector_v1.withdrawn + events of connectors of this device. + + + + + + This event is sent in response to the release request and indicates + that the compositor is done sending connector events. + The compositor will destroy this object immediately after sending the + event and it will become invalid. The client should release any + resources associated with this device after receiving this event. + + + + + + + Represents a DRM connector which is available for lease. These objects are + created via wp_drm_lease_device_v1.connector events, and should be passed + to lease requests via wp_drm_lease_request_v1.request_connector. + Immediately after the wp_drm_lease_connector_v1 object is created the + compositor will send a name, a description, a connector_id and a done + event. When the description is updated the compositor will send a + description event followed by a done event. + + + + + The compositor sends this event once the connector is created to + indicate the name of this connector. This will not change for the + duration of the Wayland session, but is not guaranteed to be consistent + between sessions. + + If the compositor supports wl_output version 4 and this connector + corresponds to a wl_output, the compositor should use the same name as + for the wl_output. + + + + + + + The compositor sends this event once the connector is created to provide + a human-readable description for this connector, which may be presented + to the user. The compositor may send this event multiple times over the + lifetime of this object to reflect changes in the description. + + + + + + + The compositor sends this event once the connector is created to + indicate the DRM object ID which represents the underlying connector + that is being offered. Note that the final lease may include additional + object IDs, such as CRTCs and planes. + + + + + + + This event is sent after all properties of a connector have been sent. + This allows changes to the properties to be seen as atomic even if they + happen via multiple events. + + + + + + Sent to indicate that the compositor will no longer honor requests for + DRM leases which include this connector. The client may still issue a + lease request including this connector, but the compositor will send + wp_drm_lease_v1.finished without issuing a lease fd. Compositors are + encouraged to send this event when they lose access to connector, for + example when the connector is hot-unplugged, when the connector gets + leased to a client or when the compositor loses DRM master. + + If a client holds a lease for the connector, the status of the lease + remains the same. The client should destroy the object after receiving + this event. + + + + + + The client may send this request to indicate that it will not use this + connector. Clients are encouraged to send this after receiving the + "withdrawn" event so that the server can release the resources + associated with this connector offer. Neither existing lease requests + nor leases will be affected. + + + + + + + A client that wishes to lease DRM resources will attach the list of + connectors advertised with wp_drm_lease_device_v1.connector that they + wish to lease, then use wp_drm_lease_request_v1.submit to submit the + request. + + + + + + + + + + + Indicates that the client would like to lease the given connector. + This is only used as a suggestion, the compositor may choose to + include any resources in the lease it issues, or change the set of + leased resources at any time. Compositors are however encouraged to + include the requested connector and other resources necessary + to drive the connected output in the lease. + + Requesting a connector that was created from a different lease device + than this lease request raises the wrong_device error. Requesting a + connector twice will raise the duplicate_connector error. + + + + + + + Submits the lease request and creates a new wp_drm_lease_v1 object. + After calling submit the compositor will immediately destroy this + object, issuing any more requests will cause a wl_display error. + The compositor doesn't make any guarantees about the events of the + lease object, clients cannot expect an immediate response. + Not requesting any connectors before submitting the lease request + will raise the empty_lease error. + + + + + + + + A DRM lease object is used to transfer the DRM file descriptor to the + client and manage the lifetime of the lease. + + Some time after the wp_drm_lease_v1 object is created, the compositor + will reply with the lease request's result. If the lease request is + granted, the compositor will send a lease_fd event. If the lease request + is denied, the compositor will send a finished event without a lease_fd + event. + + + + + This event returns a file descriptor suitable for use with DRM-related + ioctls. The client should use drmModeGetLease to enumerate the DRM + objects which have been leased to them. The compositor guarantees it + will not use the leased DRM objects itself until it sends the finished + event. If the compositor cannot or will not grant a lease for the + requested connectors, it will not send this event, instead sending the + finished event. + + The compositor will send this event at most once during this objects + lifetime. + + + + + + + The compositor uses this event to either reject a lease request, or if + it previously sent a lease_fd, to notify the client that the lease has + been revoked. If the client requires a new lease, they should destroy + this object and submit a new lease request. The compositor will send + no further events for this object after sending the finish event. + Compositors should revoke the lease when any of the leased resources + become unavailable, namely when a hot-unplug occurs or when the + compositor loses DRM master. Compositors may advertise the connector + for leasing again, if the resource is available, by sending the + connector event through the wp_drm_lease_device_v1 interface. + + + + + + The client should send this to indicate that it no longer wishes to use + this lease. The compositor should use drmModeRevokeLease on the + appropriate file descriptor, if necessary. + + Upon destruction, the compositor should advertise the connector for + leasing again by sending the connector event through the + wp_drm_lease_device_v1 interface. + + + + diff --git a/raylib/platforms/rcore_android.c b/raylib/platforms/rcore_android.c index 132cfbd..b41f8a1 100644 --- a/raylib/platforms/rcore_android.c +++ b/raylib/platforms/rcore_android.c @@ -27,7 +27,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors +* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) and contributors * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -70,6 +70,16 @@ typedef struct { EGLConfig config; // Graphic config } PlatformData; +typedef struct { + // Store data for both Hover and Touch events + // Used to ignore Hover events which are interpreted as Touch events + int32_t pointCount; // Number of touch points active + int32_t pointId[MAX_TOUCH_POINTS]; // Point identifiers + Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen + + int32_t hoverPoints[MAX_TOUCH_POINTS]; // Hover Points +} TouchRaw; + //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- @@ -246,6 +256,8 @@ static const KeyboardKey mapKeycode[KEYCODE_MAP_SIZE] = { KEY_KP_EQUAL // AKEYCODE_NUMPAD_EQUALS }; +static TouchRaw touchRaw = { 0 }; + //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- @@ -339,7 +351,7 @@ void MinimizeWindow(void) TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform"); } -// Set window state: not minimized/maximized +// Restore window from being minimized/maximized void RestoreWindow(void) { TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); @@ -433,7 +445,7 @@ int GetMonitorCount(void) return 1; } -// Get number of monitors +// Get current monitor where window is placed int GetCurrentMonitor(void) { TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform"); @@ -462,17 +474,21 @@ int GetMonitorHeight(int monitor) } // Get selected monitor physical width in millimetres +// NOTE: It seems to return a slightly underestimated value on some devices int GetMonitorPhysicalWidth(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform"); - return 0; + int widthPixels = ANativeWindow_getWidth(platform.app->window); + float dpi = AConfiguration_getDensity(platform.app->config); + return (widthPixels/dpi)*25.4f; } // Get selected monitor physical height in millimetres +// NOTE: It seems to return a slightly underestimated value on some devices int GetMonitorPhysicalHeight(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform"); - return 0; + int heightPixels = ANativeWindow_getHeight(platform.app->window); + float dpi = AConfiguration_getDensity(platform.app->config); + return (heightPixels/dpi)*25.4f; } // Get selected monitor refresh rate @@ -499,8 +515,9 @@ Vector2 GetWindowPosition(void) // Get window scale DPI factor for current monitor Vector2 GetWindowScaleDPI(void) { - TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform"); - return (Vector2){ 1.0f, 1.0f }; + int density = AConfiguration_getDensity(platform.app->config); + float scale = (float)density/160; + return (Vector2){ scale, scale }; } // Set clipboard text content @@ -517,6 +534,16 @@ const char *GetClipboardText(void) return NULL; } +// Get clipboard image +Image GetClipboardImage(void) +{ + Image image = { 0 }; + + TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform"); + + return image; +} + // Show mouse cursor void ShowCursor(void) { @@ -619,7 +646,7 @@ int SetGamepadMappings(const char *mappings) // Set gamepad vibration void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration) { - TRACELOG(LOG_WARNING, "GamepadSetVibration() not implemented on target platform"); + TRACELOG(LOG_WARNING, "SetGamepadVibration() not implemented on target platform"); } // Set mouse position XY @@ -691,7 +718,7 @@ void PollInputEvents(void) // Poll Events (registered events) until we reach TIMEOUT which indicates there are no events left to poll // NOTE: Activity is paused if not enabled (platform.appEnabled) - while ((pollResult = ALooper_pollOnce(platform.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&platform.source)) > ALOOPER_POLL_TIMEOUT) + while ((pollResult = ALooper_pollOnce(platform.appEnabled? 0 : -1, NULL, &pollEvents, ((void **)&platform.source)) > ALOOPER_POLL_TIMEOUT)) { // Process this event if (platform.source != NULL) platform.source->process(platform.app, platform.source); @@ -778,7 +805,7 @@ int InitPlatform(void) while (!CORE.Window.ready) { // Process events until we reach TIMEOUT, which indicates no more events queued. - while ((pollResult = ALooper_pollOnce(0, NULL, &pollEvents, (void**)&platform.source)) > ALOOPER_POLL_TIMEOUT) + while ((pollResult = ALooper_pollOnce(0, NULL, &pollEvents, ((void **)&platform.source)) > ALOOPER_POLL_TIMEOUT)) { // Process this event if (platform.source != NULL) platform.source->process(platform.app, platform.source); @@ -788,6 +815,8 @@ int InitPlatform(void) } } + for (int i = 0; i < MAX_TOUCH_POINTS; i++) touchRaw.hoverPoints[i] = -1; + return 0; } @@ -840,22 +869,20 @@ static int InitGraphicsDevice(void) TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4"); } - const EGLint framebufferAttribs[] = - { - EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, // Type of context support + const EGLint framebufferAttribs[] = { + EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, // Type of context support EGL_RED_SIZE, 8, // RED color bit depth (alternative: 5) EGL_GREEN_SIZE, 8, // GREEN color bit depth (alternative: 6) EGL_BLUE_SIZE, 8, // BLUE color bit depth (alternative: 5) //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI) - EGL_DEPTH_SIZE, 16, // Depth buffer size (Required to use Depth testing!) + EGL_DEPTH_SIZE, 24, // Depth buffer size (Required to use Depth testing!) //EGL_STENCIL_SIZE, 8, // Stencil buffer size - EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA + EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs) EGL_NONE }; - const EGLint contextAttribs[] = - { + const EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; @@ -1218,7 +1245,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) return 1; // Handled gamepad button } - KeyboardKey key = (keycode > 0 && keycode < KEYCODE_MAP_SIZE)? mapKeycode[keycode] : KEY_NULL; + KeyboardKey key = ((keycode > 0) && (keycode < KEYCODE_MAP_SIZE))? mapKeycode[keycode] : KEY_NULL; if (key != KEY_NULL) { // Save current key and its state @@ -1258,25 +1285,85 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) } // Register touch points count - CORE.Input.Touch.pointCount = AMotionEvent_getPointerCount(event); + touchRaw.pointCount = AMotionEvent_getPointerCount(event); - for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++) + for (int i = 0; (i < touchRaw.pointCount) && (i < MAX_TOUCH_POINTS); i++) { // Register touch points id - CORE.Input.Touch.pointId[i] = AMotionEvent_getPointerId(event, i); + touchRaw.pointId[i] = AMotionEvent_getPointerId(event, i); // Register touch points position - CORE.Input.Touch.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) }; + touchRaw.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) }; // Normalize CORE.Input.Touch.position[i] for CORE.Window.screen.width and CORE.Window.screen.height float widthRatio = (float)(CORE.Window.screen.width + CORE.Window.renderOffset.x)/(float)CORE.Window.display.width; float heightRatio = (float)(CORE.Window.screen.height + CORE.Window.renderOffset.y)/(float)CORE.Window.display.height; - CORE.Input.Touch.position[i].x = CORE.Input.Touch.position[i].x*widthRatio - (float)CORE.Window.renderOffset.x/2; - CORE.Input.Touch.position[i].y = CORE.Input.Touch.position[i].y*heightRatio - (float)CORE.Window.renderOffset.y/2; + touchRaw.position[i].x = touchRaw.position[i].x*widthRatio - (float)CORE.Window.renderOffset.x/2; + touchRaw.position[i].y = touchRaw.position[i].y*heightRatio - (float)CORE.Window.renderOffset.y/2; } int32_t action = AMotionEvent_getAction(event); unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; + int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + + if (flags == AMOTION_EVENT_ACTION_HOVER_ENTER) + { + // The new pointer is hover + // So add it to hoverPoints + for (int i = 0; i < MAX_TOUCH_POINTS; i++) + { + if (touchRaw.hoverPoints[i] == -1) + { + touchRaw.hoverPoints[i] = touchRaw.pointId[pointerIndex]; + break; + } + } + } + + if ((flags == AMOTION_EVENT_ACTION_POINTER_UP) || (flags == AMOTION_EVENT_ACTION_UP) || (flags == AMOTION_EVENT_ACTION_HOVER_EXIT)) + { + // One of the touchpoints is released, remove it from touch point arrays + if (flags == AMOTION_EVENT_ACTION_HOVER_EXIT) + { + // If the touchPoint is hover, remove it from hoverPoints + for (int i = 0; i < MAX_TOUCH_POINTS; i++) + { + if (touchRaw.hoverPoints[i] == touchRaw.pointId[pointerIndex]) + { + touchRaw.hoverPoints[i] = -1; + break; + } + } + } + for (int i = pointerIndex; (i < touchRaw.pointCount - 1) && (i < MAX_TOUCH_POINTS - 1); i++) + { + touchRaw.pointId[i] = touchRaw.pointId[i+1]; + touchRaw.position[i] = touchRaw.position[i+1]; + } + touchRaw.pointCount--; + } + + int pointCount = 0; + for (int i = 0; (i < touchRaw.pointCount) && (i < MAX_TOUCH_POINTS); i++) + { + // If the touchPoint is hover, Ignore it + bool hover = false; + for (int j = 0; j < MAX_TOUCH_POINTS; j++) + { + // Check if the touchPoint is in hoverPointers + if (touchRaw.hoverPoints[j] == touchRaw.pointId[i]) + { + hover = true; + break; + } + } + if (hover) continue; + + CORE.Input.Touch.pointId[pointCount] = touchRaw.pointId[i]; + CORE.Input.Touch.position[pointCount] = touchRaw.position[i]; + pointCount++; + } + CORE.Input.Touch.pointCount = pointCount; #if defined(SUPPORT_GESTURES_SYSTEM) GestureEvent gestureEvent = { 0 }; @@ -1301,20 +1388,6 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) ProcessGestureEvent(gestureEvent); #endif - int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - - if (flags == AMOTION_EVENT_ACTION_POINTER_UP || flags == AMOTION_EVENT_ACTION_UP) - { - // One of the touchpoints is released, remove it from touch point arrays - for (int i = pointerIndex; (i < CORE.Input.Touch.pointCount - 1) && (i < MAX_TOUCH_POINTS); i++) - { - CORE.Input.Touch.pointId[i] = CORE.Input.Touch.pointId[i+1]; - CORE.Input.Touch.position[i] = CORE.Input.Touch.position[i+1]; - } - - CORE.Input.Touch.pointCount--; - } - // When all touchpoints are tapped and released really quickly, this event is generated if (flags == AMOTION_EVENT_ACTION_CANCEL) CORE.Input.Touch.pointCount = 0; diff --git a/raylib/platforms/rcore_desktop_glfw.c b/raylib/platforms/rcore_desktop_glfw.c index baf2967..9bbd655 100644 --- a/raylib/platforms/rcore_desktop_glfw.c +++ b/raylib/platforms/rcore_desktop_glfw.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rcore_desktop - Functions to manage window, graphics device and inputs +* rcore_desktop_glfw - Functions to manage window, graphics device and inputs * * PLATFORM: DESKTOP: GLFW * - Windows (Win32, Win64) @@ -30,7 +30,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors +* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) and contributors * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -68,7 +68,7 @@ // NOTE: Those functions require linking with winmm library //#pragma warning(disable: 4273) __declspec(dllimport) unsigned int __stdcall timeEndPeriod(unsigned int uPeriod); - //#pragma warning(default: 4273) + //#pragma warning(default: 4273) #endif #endif #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) @@ -86,6 +86,8 @@ #include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow() #endif +#include // Required for: size_t + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- @@ -127,6 +129,11 @@ static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffs static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area static void JoystickCallback(int jid, int event); // GLFW3 Joystick Connected/Disconnected Callback +// Wrappers used by glfwInitAllocator +static void *AllocateWrapper(size_t size, void *user); // GLFW3 GLFWallocatefun, wrapps around RL_MALLOC macro +static void *ReallocateWrapper(void *block, size_t size, void *user); // GLFW3 GLFWreallocatefun, wrapps around RL_MALLOC macro +static void DeallocateWrapper(void *block, void *user); // GLFW3 GLFWdeallocatefun, wraps around RL_FREE macro + //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- @@ -223,11 +230,9 @@ void ToggleBorderlessWindowed(void) if (!wasOnFullscreen) CORE.Window.previousPosition = CORE.Window.position; CORE.Window.previousScreen = CORE.Window.screen; - // Set undecorated and topmost modes and flags + // Set undecorated flag glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE); CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; - glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_TRUE); - CORE.Window.flags |= FLAG_WINDOW_TOPMOST; // Get monitor position and size int monitorPosX = 0; @@ -247,9 +252,7 @@ void ToggleBorderlessWindowed(void) } else { - // Remove topmost and undecorated modes and flags - glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_FALSE); - CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; + // Remove undecorated flag glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE); CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; @@ -289,7 +292,7 @@ void MinimizeWindow(void) glfwIconifyWindow(platform.handle); } -// Set window state: not minimized/maximized +// Restore window from being minimized/maximized void RestoreWindow(void) { if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) @@ -304,6 +307,8 @@ void RestoreWindow(void) // Set window configuration state using flags void SetWindowState(unsigned int flags) { + if (!CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetWindowState does nothing before window initialization, Use \"SetConfigFlags\" instead"); + // Check previous state and requested state to apply required changes // NOTE: In most cases the functions already change the flags internally @@ -322,7 +327,7 @@ void SetWindowState(unsigned int flags) } // State change: FLAG_FULLSCREEN_MODE - if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) != (flags & FLAG_FULLSCREEN_MODE)) + if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) != (flags & FLAG_FULLSCREEN_MODE) && ((flags & FLAG_FULLSCREEN_MODE) > 0)) { ToggleFullscreen(); // NOTE: Window state flag updated inside function } @@ -571,7 +576,7 @@ void SetWindowIcons(Image *images, int count) else { int valid = 0; - GLFWimage *icons = RL_CALLOC(count, sizeof(GLFWimage)); + GLFWimage *icons = (GLFWimage *)RL_CALLOC(count, sizeof(GLFWimage)); for (int i = 0; i < count; i++) { @@ -733,7 +738,7 @@ int GetMonitorCount(void) return monitorCount; } -// Get number of monitors +// Get current monitor where window is placed int GetCurrentMonitor(void) { int index = 0; @@ -967,32 +972,29 @@ const char *GetClipboardText(void) return glfwGetClipboardString(platform.handle); } -#if defined(SUPPORT_CLIPBOARD_IMAGE) // Get clipboard image Image GetClipboardImage(void) { - Image image = {0}; + Image image = { 0 }; + +#if defined(SUPPORT_CLIPBOARD_IMAGE) +#if defined(_WIN32) unsigned long long int dataSize = 0; - void* fileData = NULL; + void *fileData = NULL; + int width = 0; + int height = 0; -#ifdef _WIN32 - int width, height; fileData = (void*)Win32GetClipboardImageData(&width, &height, &dataSize); -#else - TRACELOG(LOG_WARNING, "Clipboard image: PLATFORM_DESKTOP_GLFW doesn't implement `GetClipboardImage` for this OS"); -#endif - if (fileData == NULL) - { - TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data."); - } - else - { - image = LoadImageFromMemory(".bmp", fileData, (int)dataSize); - } + if (fileData == NULL) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data."); + else image = LoadImageFromMemory(".bmp", fileData, (int)dataSize); +#else + TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform"); +#endif +#endif // SUPPORT_CLIPBOARD_IMAGE + return image; } -#endif // SUPPORT_CLIPBOARD_IMAGE // Show mouse cursor void ShowCursor(void) @@ -1024,6 +1026,9 @@ void EnableCursor(void) // Disables cursor (lock cursor) void DisableCursor(void) { + // Reset mouse position within the window area before disabling cursor + SetMousePosition(CORE.Window.screen.width, CORE.Window.screen.height); + glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); // Set cursor position in the middle @@ -1091,7 +1096,7 @@ int SetGamepadMappings(const char *mappings) // Set gamepad vibration void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration) { - TRACELOG(LOG_WARNING, "GamepadSetVibration() not available on target platform"); + TRACELOG(LOG_WARNING, "SetGamepadVibration() not available on target platform"); } // Set mouse position XY @@ -1233,7 +1238,7 @@ void PollInputEvents(void) } } - // Get current axis state + // Get current state of axes const float *axes = state.axes; for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1); k++) @@ -1241,9 +1246,19 @@ void PollInputEvents(void) CORE.Input.Gamepad.axisState[i][k] = axes[k]; } - // Register buttons for 2nd triggers (because GLFW doesn't count these as buttons but rather axis) - CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1f); - CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.1f); + // Register buttons for 2nd triggers (because GLFW doesn't count these as buttons but rather as axes) + if (CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1f) + { + CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = 1; + CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_LEFT_TRIGGER_2; + } + else CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = 0; + if (CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.1f) + { + CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = 1; + CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; + } + else CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = 0; CORE.Input.Gamepad.axisCount[i] = GLFW_GAMEPAD_AXIS_LAST + 1; } @@ -1251,12 +1266,13 @@ void PollInputEvents(void) CORE.Window.resizedLastFrame = false; - if (CORE.Window.eventWaiting) glfwWaitEvents(); // Wait for in input events before continue (drawing is paused) + if ((CORE.Window.eventWaiting) || (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN))) + { + glfwWaitEvents(); // Wait for in input events before continue (drawing is paused) + CORE.Time.previous = GetTime(); + } else glfwPollEvents(); // Poll input events: keyboard/mouse/window events (callbacks) -> Update keys state - // While window minimized, stop loop execution - while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents(); - CORE.Window.shouldClose = glfwWindowShouldClose(platform.handle); // Reset close status for next frame @@ -1280,21 +1296,39 @@ static void SetDimensionsFromMonitor(GLFWmonitor *monitor) if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height; } +// Function wrappers around RL_*alloc macros, used by glfwInitAllocator() inside of InitPlatform() +// We need to provide these because GLFWallocator expects function pointers with specific signatures. +// Similar wrappers exist in utils.c but we cannot reuse them here due to declaration mismatch. +// https://www.glfw.org/docs/latest/intro_guide.html#init_allocator +static void *AllocateWrapper(size_t size, void *user) +{ + (void)user; + return RL_MALLOC(size); +} +static void *ReallocateWrapper(void *block, size_t size, void *user) +{ + (void)user; + return RL_REALLOC(block, size); +} +static void DeallocateWrapper(void *block, void *user) +{ + (void)user; + RL_FREE(block); +} + // Initialize platform: graphics, inputs and more int InitPlatform(void) { glfwSetErrorCallback(ErrorCallback); -/* - // TODO: Setup GLFW custom allocators to match raylib ones + const GLFWallocator allocator = { - .allocate = MemAlloc, - .deallocate = MemFree, - .reallocate = MemRealloc, - .user = NULL + .allocate = AllocateWrapper, + .deallocate = DeallocateWrapper, + .reallocate = ReallocateWrapper, + .user = NULL, // RL_*ALLOC macros are not capable of handling user-provided data }; glfwInitAllocator(&allocator); -*/ #if defined(__APPLE__) glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); @@ -1348,6 +1382,12 @@ int InitPlatform(void) if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); // Transparent framebuffer else glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_FALSE); // Opaque framebuffer + // HACK: Most of this was written before GLFW_SCALE_FRAMEBUFFER existed and + // was enabled by default. Disabling it gets back the old behavior. A + // complete fix will require removing a lot of CORE.Window.render + // manipulation code. + glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE); + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) { // Resize window content area based on the monitor content scale. @@ -1355,7 +1395,7 @@ int InitPlatform(void) // On platforms like macOS the resolution of the framebuffer is changed independently of the window size. glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); // Scale content area based on the monitor content scale where window is placed on #if defined(__APPLE__) - glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); + glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE); #endif } else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE); @@ -1490,6 +1530,12 @@ int InitPlatform(void) SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", monitor, NULL); + if (!platform.handle) + { + glfwTerminate(); + TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); + return -1; + } // NOTE: Full-screen change, not working properly... //glfwSetWindowMonitor(platform.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); @@ -1504,6 +1550,12 @@ int InitPlatform(void) int creationHeight = CORE.Window.screen.height != 0 ? CORE.Window.screen.height : 1; platform.handle = glfwCreateWindow(creationWidth, creationHeight, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); + if (!platform.handle) + { + glfwTerminate(); + TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); + return -1; + } // After the window was created, determine the monitor that the window manager assigned. // Derive display sizes, and, if possible, window size in case it was zero at beginning. @@ -1527,18 +1579,8 @@ int InitPlatform(void) return -1; } - if (platform.handle) - { - CORE.Window.render.width = CORE.Window.screen.width; - CORE.Window.render.height = CORE.Window.screen.height; - } - } - - if (!platform.handle) - { - glfwTerminate(); - TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); - return -1; + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; } glfwMakeContextCurrent(platform.handle); @@ -1567,7 +1609,7 @@ int InitPlatform(void) if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) { // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling. - // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); + // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE); #if !defined(__APPLE__) glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight); @@ -1655,7 +1697,9 @@ int InitPlatform(void) // Retrieve gamepad names for (int i = 0; i < MAX_GAMEPADS; i++) { - if (glfwJoystickPresent(i)) strcpy(CORE.Input.Gamepad.name[i], glfwGetJoystickName(i)); + // WARNING: If glfwGetJoystickName() is longer than MAX_GAMEPAD_NAME_LENGTH, + // we can get a not-NULL terminated string, so, we only copy up to (MAX_GAMEPAD_NAME_LENGTH - 1) + if (glfwJoystickPresent(i)) strncpy(CORE.Input.Gamepad.name[i], glfwGetJoystickName(i), MAX_GAMEPAD_NAME_LENGTH - 1); } //---------------------------------------------------------------------------- @@ -1708,9 +1752,13 @@ static void ErrorCallback(int error, const char *description) } // GLFW3 WindowSize Callback, runs when window is resizedLastFrame -// NOTE: Window resizing not allowed by default +// NOTE: Window resizing not enabled by default, use SetConfigFlags() static void WindowSizeCallback(GLFWwindow *window, int width, int height) { + // WARNING: On window minimization, callback is called, + // but we don't want to change internal screen values, it breaks things + if ((width == 0) || (height == 0)) return; + // Reset viewport and projection matrix for new size SetupViewport(width, height); @@ -1720,12 +1768,22 @@ static void WindowSizeCallback(GLFWwindow *window, int width, int height) if (IsWindowFullscreen()) return; - // Set current screen size + // if we are doing automatic DPI scaling, then the "screen" size is divided by the window scale + if (IsWindowState(FLAG_WINDOW_HIGHDPI)) + { + width = (int)(width/GetWindowScaleDPI().x); + height = (int)(height/GetWindowScaleDPI().y); + } + // Set render size + CORE.Window.render.width = width; + CORE.Window.render.height = height; + + // Set current screen size CORE.Window.screen.width = width; CORE.Window.screen.height = height; - // NOTE: Postprocessing texture is not scaled to new size + // WARNING: If using a render texture, it is not scaled to new size } static void WindowPosCallback(GLFWwindow* window, int x, int y) { @@ -1918,11 +1976,14 @@ static void JoystickCallback(int jid, int event) { if (event == GLFW_CONNECTED) { - strcpy(CORE.Input.Gamepad.name[jid], glfwGetJoystickName(jid)); + // WARNING: If glfwGetJoystickName() is longer than MAX_GAMEPAD_NAME_LENGTH, + // we can get a not-NULL terminated string, so, we clean destination and only copy up to -1 + 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, 64); + memset(CORE.Input.Gamepad.name[jid], 0, MAX_GAMEPAD_NAME_LENGTH); } } diff --git a/raylib/platforms/rcore_desktop_rgfw.c b/raylib/platforms/rcore_desktop_rgfw.c index f3f26e3..a1694af 100644 --- a/raylib/platforms/rcore_desktop_rgfw.c +++ b/raylib/platforms/rcore_desktop_rgfw.c @@ -6,6 +6,8 @@ * - Windows (Win32, Win64) * - Linux (X11/Wayland desktop mode) * - MacOS (Cocoa) +* - HTML5 (Emscripten) +* - Others (untested) * * LIMITATIONS: * - TODO @@ -27,7 +29,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5), Colleague Riley and contributors +* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5), Colleague Riley and contributors * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -46,7 +48,16 @@ * **********************************************************************************************/ -#if defined(GRAPHICS_API_OPENGL_ES2) +#ifndef RAYLIB_H /* this should never actually happen, it's only here for IDEs */ +#include "raylib.h" +#include "../rcore.c" +#endif + +#if defined(PLATFORM_WEB_RGFW) +#define RGFW_NO_GL_HEADER +#endif + +#if defined(GRAPHICS_API_OPENGL_ES2) && !defined(PLATFORM_WEB_RGFW) #define RGFW_OPENGL_ES2 #endif @@ -65,14 +76,14 @@ void CloseWindow(void); #if defined(_WIN32) || defined(_WIN64) #define WIN32_LEAN_AND_MEAN - #define Rectangle rectangle_win32 + #define Rectangle rectangle_win32 #define CloseWindow CloseWindow_win32 #define ShowCursor __imp_ShowCursor - #define _APISETSTRING_ - - #undef MAX_PATH + #define _APISETSTRING_ + + #undef MAX_PATH - __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int CodePage, unsigned long dwFlags, const char *lpMultiByteStr, int cbMultiByte, wchar_t *lpWideCharStr, int cchWideChar); + __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int CodePage, unsigned long dwFlags, const char *lpMultiByteStr, int cbMultiByte, wchar_t *lpWideCharStr, int cchWideChar); #endif #if defined(__APPLE__) @@ -80,6 +91,10 @@ void CloseWindow(void); #define Size NSSIZE #endif +#define RGFW_ALLOC RL_MALLOC +#define RGFW_FREE RL_FREE +#define RGFW_CALLOC RL_CALLOC + #include "../external/RGFW.h" #if defined(_WIN32) || defined(_WIN64) @@ -88,8 +103,8 @@ void CloseWindow(void); #undef CloseWindow #undef Rectangle - #undef MAX_PATH - #define MAX_PATH 1025 + #undef MAX_PATH + #define MAX_PATH 1025 #endif #if defined(__APPLE__) @@ -105,6 +120,7 @@ void CloseWindow(void); //---------------------------------------------------------------------------------- typedef struct { RGFW_window *window; // Native display device (physical screen connection) + RGFW_monitor mon; } PlatformData; //---------------------------------------------------------------------------------- @@ -112,18 +128,20 @@ typedef struct { //---------------------------------------------------------------------------------- extern CoreData CORE; // Global CORE state context -static PlatformData platform = { NULL }; // Platform specific +static PlatformData platform = { 0 }; // Platform specific static bool RGFW_disableCursor = false; static const unsigned short keyMappingRGFW[] = { - [RGFW_KEY_NULL] = KEY_NULL, - [RGFW_Quote] = KEY_APOSTROPHE, - [RGFW_Comma] = KEY_COMMA, - [RGFW_Minus] = KEY_MINUS, - [RGFW_Period] = KEY_PERIOD, - [RGFW_Slash] = KEY_SLASH, - [RGFW_Escape] = KEY_ESCAPE, + [RGFW_keyNULL] = KEY_NULL, + [RGFW_return] = KEY_ENTER, + [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, @@ -136,7 +154,7 @@ static const unsigned short keyMappingRGFW[] = { [RGFW_F10] = KEY_F10, [RGFW_F11] = KEY_F11, [RGFW_F12] = KEY_F12, - [RGFW_Backtick] = KEY_GRAVE, + [RGFW_backtick] = KEY_GRAVE, [RGFW_0] = KEY_ZERO, [RGFW_1] = KEY_ONE, [RGFW_2] = KEY_TWO, @@ -147,20 +165,21 @@ static const unsigned short keyMappingRGFW[] = { [RGFW_7] = KEY_SEVEN, [RGFW_8] = KEY_EIGHT, [RGFW_9] = KEY_NINE, - [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, + [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_AltR] = KEY_RIGHT_ALT, + [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_space] = KEY_SPACE, [RGFW_a] = KEY_A, [RGFW_b] = KEY_B, @@ -188,23 +207,23 @@ static const unsigned short keyMappingRGFW[] = { [RGFW_x] = KEY_X, [RGFW_y] = KEY_Y, [RGFW_z] = KEY_Z, - [RGFW_Bracket] = KEY_LEFT_BRACKET, - [RGFW_BackSlash] = KEY_BACKSLASH, - [RGFW_CloseBracket] = KEY_RIGHT_BRACKET, - [RGFW_Semicolon] = KEY_SEMICOLON, - [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_bracket] = KEY_LEFT_BRACKET, + [RGFW_backSlash] = KEY_BACKSLASH, + [RGFW_closeBracket] = KEY_RIGHT_BRACKET, + [RGFW_semicolon] = KEY_SEMICOLON, + [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_multiply] = KEY_KP_MULTIPLY, [RGFW_KP_Minus] = KEY_KP_SUBTRACT, [RGFW_KP_Return] = KEY_KP_ENTER, [RGFW_KP_1] = KEY_KP_1, @@ -217,7 +236,8 @@ static const unsigned short keyMappingRGFW[] = { [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_KP_Period] = KEY_KP_DECIMAL, + [RGFW_scrollLock] = KEY_SCROLL_LOCK, }; //---------------------------------------------------------------------------------- @@ -246,18 +266,67 @@ bool WindowShouldClose(void) // Toggle fullscreen mode void ToggleFullscreen(void) -{ - RGFW_window_maximize(platform.window); - ToggleBorderlessWindowed(); +{ + if (!CORE.Window.fullscreen) + { + // Store previous window position (in case we exit fullscreen) + CORE.Window.previousPosition = CORE.Window.position; + CORE.Window.previousScreen = CORE.Window.screen; + + platform.mon = RGFW_window_getMonitor(platform.window); + CORE.Window.fullscreen = true; + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; + + RGFW_monitor_scaleToWindow(platform.mon, platform.window); + RGFW_window_setFullscreen(platform.window, 1); + } + else + { + CORE.Window.fullscreen = false; + 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 + 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)); + } + + // 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 (CORE.Window.flags & FLAG_VSYNC_HINT) RGFW_window_swapInterval(platform.window, 1); } // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - if (platform.window != NULL) + if (CORE.Window.fullscreen) { - RGFW_window_setBorder(platform.window, CORE.Window.flags & FLAG_WINDOW_UNDECORATED); + CORE.Window.previousPosition = CORE.Window.position; + CORE.Window.previousScreen = CORE.Window.screen; + + RGFW_window_setBorder(platform.window, 0); + + RGFW_monitor mon = RGFW_window_getMonitor(platform.window); + RGFW_window_resize(platform.window, mon.mode.area); } + else + { + 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)); + } + + CORE.Window.fullscreen = !CORE.Window.fullscreen; } // Set window state: maximized, if resizable @@ -272,15 +341,19 @@ void MinimizeWindow(void) RGFW_window_minimize(platform.window); } -// Set window state: not minimized/maximized +// Restore window from being minimized/maximized void RestoreWindow(void) { + if (!(CORE.Window.flags & FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window); + RGFW_window_restore(platform.window); } // Set window configuration state using flags void SetWindowState(unsigned int flags) { + if (!CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetWindowState does nothing before window initialization, Use \"SetConfigFlags\" instead"); + CORE.Window.flags |= flags; if (flags & FLAG_VSYNC_HINT) @@ -289,17 +362,16 @@ void SetWindowState(unsigned int flags) } if (flags & FLAG_FULLSCREEN_MODE) { - RGFW_window_maximize(platform.window); - ToggleBorderlessWindowed(); + if (!CORE.Window.fullscreen) ToggleFullscreen(); } if (flags & FLAG_WINDOW_RESIZABLE) { - RGFW_window_setMaxSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h)); - RGFW_window_setMinSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h)); + RGFW_window_setMaxSize(platform.window, RGFW_AREA(0, 0)); + RGFW_window_setMinSize(platform.window, RGFW_AREA(0, 0)); } if (flags & FLAG_WINDOW_UNDECORATED) { - ToggleBorderlessWindowed(); + RGFW_window_setBorder(platform.window, 0); } if (flags & FLAG_WINDOW_HIDDEN) { @@ -315,27 +387,29 @@ void SetWindowState(unsigned int flags) } if (flags & FLAG_WINDOW_UNFOCUSED) { - TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_RGFW"); + CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; + platform.window->_flags &= ~RGFW_windowFocusOnShow; + RGFW_window_setFlags(platform.window, platform.window->_flags); } if (flags & FLAG_WINDOW_TOPMOST) { - TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_TOPMOST is not supported on PLATFORM_DESKTOP_RGFW"); + RGFW_window_setFloating(platform.window, RGFW_TRUE); } if (flags & FLAG_WINDOW_ALWAYS_RUN) { - TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_ALWAYS_RUN is not supported on PLATFORM_DESKTOP_RGFW"); + CORE.Window.flags |= FLAG_WINDOW_ALWAYS_RUN; } if (flags & FLAG_WINDOW_TRANSPARENT) { - TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_TRANSPARENT post window creation post window creation is not supported on PLATFORM_DESKTOP_RGFW"); + TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization"); } if (flags & FLAG_WINDOW_HIGHDPI) { - TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_RGFW"); + TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization"); } if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) { - RGFW_window_setMousePassthrough(platform.window, flags & FLAG_WINDOW_MOUSE_PASSTHROUGH); + RGFW_window_setMousePassthrough(platform.window, 1); } if (flags & FLAG_BORDERLESS_WINDOWED_MODE) { @@ -343,11 +417,11 @@ void SetWindowState(unsigned int flags) } if (flags & FLAG_MSAA_4X_HINT) { - RGFW_setGLSamples(4); + RGFW_setGLHint(RGFW_glSamples, 4); } if (flags & FLAG_INTERLACED_HINT) { - TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_RGFW"); + TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization"); } } @@ -362,102 +436,96 @@ void ClearWindowState(unsigned int flags) } if (flags & FLAG_FULLSCREEN_MODE) { - ToggleBorderlessWindowed(); - RGFW_window_restore(platform.window); - CORE.Window.fullscreen = false; + if (CORE.Window.fullscreen) ToggleFullscreen(); } if (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, RGFW_AREA(platform.window->r.w, platform.window->r.h)); + RGFW_window_setMinSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h)); } if (flags & FLAG_WINDOW_UNDECORATED) { - ToggleBorderlessWindowed(); + RGFW_window_setBorder(platform.window, 1); } if (flags & FLAG_WINDOW_HIDDEN) { + if (!(CORE.Window.flags & FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window); + RGFW_window_show(platform.window); } if (flags & FLAG_WINDOW_MINIMIZED) { + if (!(CORE.Window.flags & FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window); + RGFW_window_restore(platform.window); } if (flags & FLAG_WINDOW_MAXIMIZED) { + if (!(CORE.Window.flags & FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window); + RGFW_window_restore(platform.window); } if (flags & FLAG_WINDOW_UNFOCUSED) { - TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_RGFW"); + RGFW_window_setFlags(platform.window, platform.window->_flags | RGFW_windowFocusOnShow); + CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; } if (flags & FLAG_WINDOW_TOPMOST) { - TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_TOPMOST is not supported on PLATFORM_DESKTOP_RGFW"); + RGFW_window_setFloating(platform.window, RGFW_FALSE); } if (flags & FLAG_WINDOW_ALWAYS_RUN) { - TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_ALWAYS_RUN is not supported on PLATFORM_DESKTOP_RGFW"); + CORE.Window.flags &= ~FLAG_WINDOW_ALWAYS_RUN; } if (flags & FLAG_WINDOW_TRANSPARENT) { - TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_TRANSPARENT is not supported on PLATFORM_DESKTOP_RGFW"); + TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization"); } if (flags & FLAG_WINDOW_HIGHDPI) { - // NOTE: There also doesn't seem to be a feature to disable high DPI once enabled - TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_RGFW"); + TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization"); } if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) { - RGFW_window_setMousePassthrough(platform.window, flags & FLAG_WINDOW_MOUSE_PASSTHROUGH); - TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_RGFW"); + RGFW_window_setMousePassthrough(platform.window, 0); } if (flags & FLAG_BORDERLESS_WINDOWED_MODE) { - ToggleFullscreen(); + if (CORE.Window.fullscreen) ToggleBorderlessWindowed(); } if (flags & FLAG_MSAA_4X_HINT) { - RGFW_setGLSamples(0); + RGFW_setGLHint(RGFW_glSamples, 0); } if (flags & FLAG_INTERLACED_HINT) { - TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_RGFW"); + TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization"); } } -// Set icon for window -void SetWindowIcon(Image image) +int RGFW_formatToChannels(int format) { - i32 channels = 4; - - switch (image.format) + switch (format) { case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: case PIXELFORMAT_UNCOMPRESSED_R16: // 16 bpp (1 channel - half float) case PIXELFORMAT_UNCOMPRESSED_R32: // 32 bpp (1 channel - float) - { - channels = 1; - } break; + return 1; case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: // 8*2 bpp (2 channels) case PIXELFORMAT_UNCOMPRESSED_R5G6B5: // 16 bpp case PIXELFORMAT_UNCOMPRESSED_R8G8B8: // 24 bpp case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: // 16 bpp (1 bit alpha) case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: // 16 bpp (4 bit alpha) case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: // 32 bpp - { - channels = 2; - } break; + return 2; case PIXELFORMAT_UNCOMPRESSED_R32G32B32: // 32*3 bpp (3 channels - float) case PIXELFORMAT_UNCOMPRESSED_R16G16B16: // 16*3 bpp (3 channels - half float) case PIXELFORMAT_COMPRESSED_DXT1_RGB: // 4 bpp (no alpha) case PIXELFORMAT_COMPRESSED_ETC1_RGB: // 4 bpp case PIXELFORMAT_COMPRESSED_ETC2_RGB: // 4 bpp case PIXELFORMAT_COMPRESSED_PVRT_RGB: // 4 bpp - { - channels = 3; - } break; + return 3; case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: // 32*4 bpp (4 channels - float) case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: // 16*4 bpp (4 channels - half float) case PIXELFORMAT_COMPRESSED_DXT1_RGBA: // 4 bpp (1 bit alpha) @@ -467,25 +535,44 @@ void SetWindowIcon(Image image) case PIXELFORMAT_COMPRESSED_PVRT_RGBA: // 4 bpp case PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: // 8 bpp case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: // 2 bpp - { - channels = 4; - } break; - default: break; + return 4; + default: return 4; } +} - RGFW_window_setIcon(platform.window, image.data, RGFW_AREA(image.width, image.height), channels); +// Set icon for window +void SetWindowIcon(Image image) +{ + RGFW_window_setIcon(platform.window, image.data, RGFW_AREA(image.width, image.height), RGFW_formatToChannels(image.format)); } // Set icon for window void SetWindowIcons(Image *images, int count) { - TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform"); + if ((images == NULL) || (count <= 0)) + { + RGFW_window_setIcon(platform.window, NULL, RGFW_AREA(0, 0), 0); + } + else + { + Image *bigIcon = NULL; + Image *smallIcon = NULL; + + for (int i = 0; i < count; i++) + { + if ((bigIcon == NULL) || ((images[i].width > bigIcon->width) && (images[i].height > bigIcon->height))) bigIcon = &images[i]; + if ((smallIcon == NULL) || ((images[i].width < smallIcon->width) && (images[i].height > smallIcon->height))) smallIcon = &images[i]; + } + + if (smallIcon != NULL) RGFW_window_setIconEx(platform.window, smallIcon->data, RGFW_AREA(smallIcon->width, smallIcon->height), RGFW_formatToChannels(smallIcon->format), RGFW_iconWindow); + if (bigIcon != NULL) RGFW_window_setIconEx(platform.window, bigIcon->data, RGFW_AREA(bigIcon->width, bigIcon->height), RGFW_formatToChannels(bigIcon->format), RGFW_iconTaskbar); + } } // Set title for window void SetWindowTitle(const char *title) { - RGFW_window_setName(platform.window, (char*)title); + RGFW_window_setName(platform.window, (char *)title); CORE.Window.title = title; } @@ -498,7 +585,7 @@ void SetWindowPosition(int x, int y) // Set monitor for the current window void SetWindowMonitor(int monitor) { - RGFW_window_moveToMonitor(platform.window, RGFW_getMonitors()[monitor]); + RGFW_window_moveToMonitor(platform.window, RGFW_getMonitors(NULL)[monitor]); } // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) @@ -529,22 +616,23 @@ void SetWindowSize(int width, int height) // Set window opacity, value opacity is between 0.0 and 1.0 void SetWindowOpacity(float opacity) { - TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform"); + RGFW_window_setOpacity(platform.window, opacity); } // Set window focused void SetWindowFocused(void) { - RGFW_window_show(platform.window); + RGFW_window_focus(platform.window); } // Get native window handle void *GetWindowHandle(void) { -#ifdef RGFW_WEBASM - return (void*)platform.window->src.ctx; + if (platform.window == NULL) return NULL; +#ifdef RGFW_WASM + return (void *)platform.window->src.ctx; #else - return (void*)platform.window->src.window; + return (void *)platform.window->src.window; #endif } @@ -554,11 +642,11 @@ int GetMonitorCount(void) #define MAX_MONITORS_SUPPORTED 6 int count = MAX_MONITORS_SUPPORTED; - RGFW_monitor *mons = RGFW_getMonitors(); + RGFW_monitor *mons = RGFW_getMonitors(NULL); for (int i = 0; i < 6; i++) { - if (!mons[i].rect.x && !mons[i].rect.y && !mons[i].rect.w && mons[i].rect.h) + if (!mons[i].x && !mons[i].y && !mons[i].mode.area.w && mons[i].mode.area.h) { count = i; break; @@ -568,15 +656,18 @@ int GetMonitorCount(void) return count; } -// Get number of monitors +// Get current monitor where window is placed int GetCurrentMonitor(void) { - RGFW_monitor *mons = RGFW_getMonitors(); - RGFW_monitor mon = RGFW_window_getMonitor(platform.window); + RGFW_monitor *mons = RGFW_getMonitors(NULL); + RGFW_monitor mon = { 0 }; + + if (platform.window) mon = RGFW_window_getMonitor(platform.window); + else mon = RGFW_getPrimaryMonitor(); for (int i = 0; i < 6; i++) { - if ((mons[i].rect.x == mon.rect.x) && (mons[i].rect.y == mon.rect.y)) return i; + if ((mons[i].x == mon.x) && (mons[i].y == mon.y)) return i; } return 0; @@ -585,31 +676,31 @@ int GetCurrentMonitor(void) // Get selected monitor position Vector2 GetMonitorPosition(int monitor) { - RGFW_monitor *mons = RGFW_getMonitors(); + RGFW_monitor *mons = RGFW_getMonitors(NULL); - return (Vector2){mons[monitor].rect.x, mons[monitor].rect.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(); + RGFW_monitor *mons = RGFW_getMonitors(NULL); - return mons[monitor].rect.w; + return mons[monitor].mode.area.w; } // Get selected monitor height (currently used by monitor) int GetMonitorHeight(int monitor) { - RGFW_monitor *mons = RGFW_getMonitors(); + RGFW_monitor *mons = RGFW_getMonitors(NULL); - return mons[monitor].rect.h; + return mons[monitor].mode.area.h; } // Get selected monitor physical width in millimetres int GetMonitorPhysicalWidth(int monitor) { - RGFW_monitor* mons = RGFW_getMonitors(); + RGFW_monitor *mons = RGFW_getMonitors(NULL); return mons[monitor].physW; } @@ -617,22 +708,23 @@ int GetMonitorPhysicalWidth(int monitor) // Get selected monitor physical height in millimetres int GetMonitorPhysicalHeight(int monitor) { - RGFW_monitor *mons = RGFW_getMonitors(); + RGFW_monitor *mons = RGFW_getMonitors(NULL); - return mons[monitor].physH; + return (int)mons[monitor].physH; } // Get selected monitor refresh rate int GetMonitorRefreshRate(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform"); - return 0; + RGFW_monitor *mons = RGFW_getMonitors(NULL); + + 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(); + RGFW_monitor *mons = RGFW_getMonitors(NULL); return mons[monitor].name; } @@ -640,15 +732,19 @@ const char *GetMonitorName(int monitor) // Get window position XY on monitor Vector2 GetWindowPosition(void) { - return (Vector2){ platform.window->r.x, platform.window->r.y }; + if (platform.window == NULL) return (Vector2){ 0.0f, 0.0f }; + return (Vector2){ (float)platform.window->r.x, (float)platform.window->r.y }; } // Get window scale DPI factor for current monitor Vector2 GetWindowScaleDPI(void) { - RGFW_monitor monitor = RGFW_window_getMonitor(platform.window); + RGFW_monitor monitor = { 0 }; - return (Vector2){monitor.scaleX, monitor.scaleX}; + if (platform.window) monitor = RGFW_window_getMonitor(platform.window); + else monitor = RGFW_getPrimaryMonitor(); + + return (Vector2){ monitor.scaleX, monitor.scaleX }; } // Set clipboard text content @@ -658,48 +754,44 @@ void SetClipboardText(const char *text) } // Get clipboard text content -// NOTE: returned string is allocated and freed by GLFW +// NOTE: returned string is allocated and freed by RGFW const char *GetClipboardText(void) { return RGFW_readClipboard(NULL); } - #if defined(SUPPORT_CLIPBOARD_IMAGE) - -#ifdef _WIN32 -# define WIN32_CLIPBOARD_IMPLEMENTATION -# define WINUSER_ALREADY_INCLUDED -# define WINBASE_ALREADY_INCLUDED -# define WINGDI_ALREADY_INCLUDED -# include "../external/win32_clipboard.h" +#if defined(_WIN32) + #define WIN32_CLIPBOARD_IMPLEMENTATION + #define WINUSER_ALREADY_INCLUDED + #define WINBASE_ALREADY_INCLUDED + #define WINGDI_ALREADY_INCLUDED + #include "../external/win32_clipboard.h" +#endif #endif // Get clipboard image Image GetClipboardImage(void) { - Image image = {0}; + Image image = { 0 }; unsigned long long int dataSize = 0; - void* fileData = NULL; + void *fileData = NULL; -#ifdef _WIN32 - int width, height; - fileData = (void*)Win32GetClipboardImageData(&width, &height, &dataSize); +#if defined(SUPPORT_CLIPBOARD_IMAGE) +#if defined(_WIN32) + 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", fileData, dataSize); #else - TRACELOG(LOG_WARNING, "Clipboard image: PLATFORM_DESKTOP_RGFW doesn't implement `GetClipboardImage` for this OS"); + TRACELOG(LOG_WARNING, "Clipboard image: PLATFORM_DESKTOP_RGFW doesn't implement GetClipboardImage() for this OS"); #endif +#endif // SUPPORT_CLIPBOARD_IMAGE - if (fileData == NULL) - { - TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data."); - } - else - { - image = LoadImageFromMemory(".bmp", fileData, dataSize); - } return image; } -#endif // SUPPORT_CLIPBOARD_IMAGE // Show mouse cursor void ShowCursor(void) @@ -731,9 +823,7 @@ void EnableCursor(void) void DisableCursor(void) { RGFW_disableCursor = true; - RGFW_window_mouseHold(platform.window, RGFW_AREA(0, 0)); - HideCursor(); } @@ -750,11 +840,7 @@ void SwapScreenBuffer(void) // Get elapsed time measure in seconds since InitTimer() double GetTime(void) { - double time = 0.0; - unsigned long long int nanoSeconds = RGFW_getTimeNS(); - time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer() - - return time; + return RGFW_getTime(); } // Open URL with default system browser (if available) @@ -779,10 +865,16 @@ void OpenURL(const char *url) // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { - TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform"); + TRACELOG(LOG_WARNING, "SetGamepadMappings() unsupported on target platform"); return 0; } +// Set gamepad vibration +void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration) +{ + TRACELOG(LOG_WARNING, "SetGamepadVibration() unsupported on target platform"); +} + // Set mouse position XY void SetMousePosition(int x, int y) { @@ -800,63 +892,32 @@ void SetMouseCursor(int cursor) // Get physical key name. const char *GetKeyName(int key) { - TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform"); + TRACELOG(LOG_WARNING, "GetKeyName() unsupported on target platform"); + return ""; } static KeyboardKey ConvertScancodeToKey(u32 keycode); -// TODO: Review function to avoid duplicate with RSGL -char RSGL_keystrToChar(const char *str) -{ - if (str[1] == 0) return str[0]; - - static const char *map[] = { - "asciitilde", "`", - "grave", "~", - "exclam", "!", - "at", "@", - "numbersign", "#", - "dollar", "$", - "percent", "%%", - "asciicircum", "^", - "ampersand", "&", - "asterisk", "*", - "parenleft", "(", - "parenright", ")", - "underscore", "_", - "minus", "-", - "plus", "+", - "equal", "=", - "braceleft", "{", - "bracketleft", "[", - "bracketright", "]", - "braceright", "}", - "colon", ":", - "semicolon", ";", - "quotedbl", "\"", - "apostrophe", "'", - "bar", "|", - "backslash", "\'", - "less", "<", - "comma", ",", - "greater", ">", - "period", ".", - "question", "?", - "slash", "/", - "space", " ", - "Return", "\n", - "Enter", "\n", - "enter", "\n", - }; - - for (unsigned char i = 0; i < (sizeof(map)/sizeof(char *)); i += 2) - { - if (strcmp(map[i], str) == 0) return *map[i + 1]; - } - - return '\0'; -} +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, +}; // Register all input events void PollInputEvents(void) @@ -866,7 +927,7 @@ void PollInputEvents(void) // because ProcessGestureEvent() is just called on an event, not every frame UpdateGestures(); #endif - + // Reset keys/chars pressed registered CORE.Input.Keyboard.keyPressedQueueCount = 0; CORE.Input.Keyboard.charPressedQueueCount = 0; @@ -878,7 +939,6 @@ void PollInputEvents(void) // Register previous mouse position // Reset last gamepad button/axis registered state - for (int i = 0; (i < 4) && (i < MAX_GAMEPADS); i++) { // Check if gamepad is available @@ -917,8 +977,7 @@ void PollInputEvents(void) CORE.Window.resizedLastFrame = false; CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; - #define RGFW_HOLD_MOUSE (1L<<2) - if (platform.window->_winArgs & RGFW_HOLD_MOUSE) + if (platform.window->_flags & RGFW_HOLD_MOUSE) { CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f }; CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f }; @@ -928,27 +987,26 @@ void PollInputEvents(void) CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; } + 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)) { - if ((platform.window->event.type >= RGFW_jsButtonPressed) && (platform.window->event.type <= RGFW_jsAxisMove)) - { - if (!CORE.Input.Gamepad.ready[platform.window->event.joystick]) - { - CORE.Input.Gamepad.ready[platform.window->event.joystick] = true; - CORE.Input.Gamepad.axisCount[platform.window->event.joystick] = platform.window->event.axisesCount; - CORE.Input.Gamepad.name[platform.window->event.joystick][0] = '\0'; - CORE.Input.Gamepad.axisState[platform.window->event.joystick][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f; - CORE.Input.Gamepad.axisState[platform.window->event.joystick][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f; - } - } - - RGFW_Event *event = &platform.window->event; - + RGFW_event *event = &platform.window->event; // All input events can be processed after polling + switch (event->type) { - case RGFW_quit: CORE.Window.shouldClose = true; break; - case RGFW_dnd: // Dropped file + 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; + return; + case RGFW_DND: // Dropped file { for (int i = 0; i < event->droppedFilesCount; i++) { @@ -961,7 +1019,7 @@ void PollInputEvents(void) CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event->droppedFiles[i]); - + CORE.Window.dropFileCount++; } else if (CORE.Window.dropFileCount < 1024) @@ -979,12 +1037,38 @@ void PollInputEvents(void) case RGFW_windowResized: { SetupViewport(platform.window->r.w, platform.window->r.h); - CORE.Window.screen.width = platform.window->r.w; - CORE.Window.screen.height = platform.window->r.h; + + // 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.currentFbo.width = platform.window->r.w; CORE.Window.currentFbo.height = platform.window->r.h; CORE.Window.resizedLastFrame = true; } break; + case RGFW_windowMaximized: + { + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // The window was maximized + } break; + case RGFW_windowMinimized: + { + CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; // The window was iconified + } break; + case RGFW_windowRestored: + { + if (RGFW_window_isMaximized(platform.window)) + CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; // The window was restored + if (RGFW_window_isMinimized(platform.window)) + CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // The window was restored + } break; case RGFW_windowMoved: { CORE.Window.position.x = platform.window->r.x; @@ -994,8 +1078,7 @@ void PollInputEvents(void) // Keyboard events case RGFW_keyPressed: { - KeyboardKey key = ConvertScancodeToKey(event->keyCode); - + KeyboardKey key = ConvertScancodeToKey(event->key); if (key != KEY_NULL) { // If key was up, add it to the key pressed queue @@ -1019,13 +1102,13 @@ void PollInputEvents(void) if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) { // Add character (codepoint) to the queue - CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = RSGL_keystrToChar(event->keyName); + CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = event->keyChar; CORE.Input.Keyboard.charPressedQueueCount++; } } break; case RGFW_keyReleased: { - KeyboardKey key = ConvertScancodeToKey(event->keyCode); + KeyboardKey key = ConvertScancodeToKey(event->key); if (key != KEY_NULL) CORE.Input.Keyboard.currentKeyState[key] = 0; } break; @@ -1037,6 +1120,7 @@ void PollInputEvents(void) CORE.Input.Mouse.currentWheelMove.y = event->scroll; break; } + else CORE.Input.Mouse.currentWheelMove.y = 0; int btn = event->button; if (btn == RGFW_mouseLeft) btn = 1; @@ -1050,12 +1134,12 @@ 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; if (btn == RGFW_mouseLeft) btn = 1; @@ -1069,10 +1153,10 @@ void PollInputEvents(void) } break; case RGFW_mousePosChanged: { - if (platform.window->_winArgs & RGFW_HOLD_MOUSE) + if (platform.window->_flags & RGFW_HOLD_MOUSE) { - CORE.Input.Mouse.currentPosition.x += (float)event->point.x; - CORE.Input.Mouse.currentPosition.y += (float)event->point.y; + CORE.Input.Mouse.currentPosition.x += (float)event->vector.x; + CORE.Input.Mouse.currentPosition.y += (float)event->vector.y; } else { @@ -1084,123 +1168,66 @@ void PollInputEvents(void) CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; touchAction = 2; } break; - case RGFW_jsButtonPressed: + case RGFW_gamepadConnected: { - int button = -1; + 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; - switch (event->button) - { - case RGFW_JS_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; - case RGFW_JS_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; - case RGFW_JS_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; - case RGFW_JS_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; - - case RGFW_JS_L1: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; - case RGFW_JS_R1: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; - - case RGFW_JS_L2: button = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break; - case RGFW_JS_R2: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break; - - case RGFW_JS_SELECT: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break; - case RGFW_JS_HOME: button = GAMEPAD_BUTTON_MIDDLE; break; - case RGFW_JS_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; - - case RGFW_JS_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break; - case RGFW_JS_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; - case RGFW_JS_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; - case RGFW_JS_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; - - default: break; - } + 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->joystick][button] = 1; + CORE.Input.Gamepad.currentButtonState[event->gamepad][button] = 1; CORE.Input.Gamepad.lastButtonPressed = button; } } break; - case RGFW_jsButtonReleased: + case RGFW_gamepadButtonReleased: { - int button = -1; - switch (event->button) - { - case RGFW_JS_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; - case RGFW_JS_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; - case RGFW_JS_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; - case RGFW_JS_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; + int button = RGFW_gpConvTable[event->button]; - case RGFW_JS_L1: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; - case RGFW_JS_R1: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; - - case RGFW_JS_L2: button = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break; - case RGFW_JS_R2: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break; - - case RGFW_JS_SELECT: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break; - case RGFW_JS_HOME: button = GAMEPAD_BUTTON_MIDDLE; break; - case RGFW_JS_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; - - case RGFW_JS_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break; - case RGFW_JS_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; - case RGFW_JS_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; - case RGFW_JS_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; - default: break; - } - - if (button >= 0) - { - CORE.Input.Gamepad.currentButtonState[event->joystick][button] = 0; - if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0; - } + CORE.Input.Gamepad.currentButtonState[event->gamepad][button] = 0; + if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0; } break; - case RGFW_jsAxisMove: + case RGFW_gamepadAxisMove: { int axis = -1; - for (int i = 0; i < event->axisesCount; i++) + float value = 0; + + switch(event->whichAxis) { - switch(i) + case 0: { - case 0: - { - if (abs(event->axis[i].x) > abs(event->axis[i].y)) - { - axis = GAMEPAD_AXIS_LEFT_X; - break; - } - - axis = GAMEPAD_AXIS_LEFT_Y; - } break; - case 1: - { - if (abs(event->axis[i].x) > abs(event->axis[i].y)) - { - axis = GAMEPAD_AXIS_RIGHT_X; - break; - } - - axis = GAMEPAD_AXIS_RIGHT_Y; - } break; - case 2: axis = GAMEPAD_AXIS_LEFT_TRIGGER; break; - case 3: axis = GAMEPAD_AXIS_RIGHT_TRIGGER; break; - default: break; - } - - #ifdef __linux__ - float value = (event->axis[i].x + event->axis[i].y)/(float)32767; - #else - float value = (event->axis[i].x + -event->axis[i].y)/(float)32767; - #endif - CORE.Input.Gamepad.axisState[event->joystick][axis] = value; - - // Register button state for triggers in addition to their axes - if ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER)) + 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->joystick][button] = pressed; + 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; @@ -1247,56 +1274,73 @@ void PollInputEvents(void) int InitPlatform(void) { // Initialize RGFW internal global state, only required systems - unsigned int flags = RGFW_CENTER | RGFW_ALLOW_DND; + unsigned int flags = RGFW_windowCenter | RGFW_windowAllowDND; // Check window creation flags if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) { CORE.Window.fullscreen = true; - flags |= RGFW_FULLSCREEN; + flags |= RGFW_windowFullscreen; } - if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) flags |= RGFW_NO_BORDER; - if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) flags |= RGFW_NO_RESIZE; + if ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0) + { + CORE.Window.fullscreen = true; + flags |= RGFW_windowedFullscreen; + } - if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) flags |= RGFW_TRANSPARENT_WINDOW; - - if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) flags |= RGFW_FULLSCREEN; + if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) flags |= RGFW_windowNoBorder; + if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) flags |= RGFW_windowNoResize; + if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) flags |= RGFW_windowTransparent; + if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) flags |= RGFW_windowFullscreen; + if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) flags |= RGFW_windowHide; + if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) flags |= RGFW_windowMaximize; // NOTE: Some OpenGL context attributes must be set before window creation - // Check selection OpenGL version if (rlGetVersion() == RL_OPENGL_21) { - RGFW_setGLVersion(RGFW_GL_CORE, 2, 1); + RGFW_setGLHint(RGFW_glMajor, 2); + RGFW_setGLHint(RGFW_glMinor, 1); } else if (rlGetVersion() == RL_OPENGL_33) { - RGFW_setGLVersion(RGFW_GL_CORE, 3, 3); + RGFW_setGLHint(RGFW_glMajor, 3); + RGFW_setGLHint(RGFW_glMinor, 3); } else if (rlGetVersion() == RL_OPENGL_43) { - RGFW_setGLVersion(RGFW_GL_CORE, 4, 1); + RGFW_setGLHint(RGFW_glMajor, 4); + RGFW_setGLHint(RGFW_glMinor, 3); } - if (CORE.Window.flags & FLAG_MSAA_4X_HINT) - { - RGFW_setGLSamples(4); - } + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) RGFW_setGLHint(RGFW_glSamples, 4); + + if (!(CORE.Window.flags & FLAG_WINDOW_UNFOCUSED)) flags |= RGFW_windowFocusOnShow | RGFW_windowFocus; platform.window = RGFW_createWindow(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) + { + // 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; + } + +#ifndef PLATFORM_WEB_RGFW RGFW_area screenSize = RGFW_getScreenSize(); CORE.Window.display.width = screenSize.w; CORE.Window.display.height = screenSize.h; - /* - I think this is needed by Raylib now ? - If so, rcore_destkop_sdl should be updated too - */ - SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); - +#else + CORE.Window.display.width = CORE.Window.screen.width; + CORE.Window.display.height = CORE.Window.screen.height; +#endif + // TODO: Is this needed by raylib now? + // If so, rcore_desktop_sdl should be updated too + //SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); + if (CORE.Window.flags & FLAG_VSYNC_HINT) RGFW_window_swapInterval(platform.window, 1); - RGFW_window_makeCurrent(platform.window); // Check surface and context activation @@ -1308,12 +1352,6 @@ int InitPlatform(void) CORE.Window.render.height = CORE.Window.screen.height; CORE.Window.currentFbo.width = CORE.Window.render.width; CORE.Window.currentFbo.height = CORE.Window.render.height; - - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); - TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); - TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); - TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); - TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); } else { @@ -1339,15 +1377,7 @@ int InitPlatform(void) // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions //---------------------------------------------------------------------------- - rlLoadExtensions((void*)RGFW_getProcAddress); - //---------------------------------------------------------------------------- - - // TODO: Initialize input events system - // It could imply keyboard, mouse, gamepad, touch... - // Depending on the platform libraries/SDK it could use a callback mechanism - // For system events and inputs evens polling on a per-frame basis, use PollInputEvents() - //---------------------------------------------------------------------------- - // ... + rlLoadExtensions((void *)RGFW_getProcAddress); //---------------------------------------------------------------------------- // Initialize timing system @@ -1360,15 +1390,23 @@ int InitPlatform(void) CORE.Storage.basePath = GetWorkingDirectory(); //---------------------------------------------------------------------------- -#ifdef RGFW_X11 - for (int i = 0; (i < 4) && (i < MAX_GAMEPADS); i++) - { - RGFW_registerJoystick(platform.window, i); - } +#if defined(RGFW_WAYLAND) + if (RGFW_useWaylandBool) 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__) + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11 (MacOS)): Initialized successfully"); + #else + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11): Initialized successfully"); + #endif +#elif defined (RGFW_WINDOWS) + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Win32): Initialized successfully"); +#elif defined(RGFW_WASM) + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - WASMs): Initialized successfully"); +#elif defined(RGFW_MACOS) + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - MacOS): Initialized successfully"); #endif - TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Initialized successfully"); - return 0; } diff --git a/raylib/platforms/rcore_desktop_sdl.c b/raylib/platforms/rcore_desktop_sdl.c index a201f2c..6af1cb6 100644 --- a/raylib/platforms/rcore_desktop_sdl.c +++ b/raylib/platforms/rcore_desktop_sdl.c @@ -23,13 +23,13 @@ * Custom flag for rcore on target platform -not used- * * DEPENDENCIES: -* - SDL 2 or SLD 3 (main library): Windowing and inputs management +* - SDL 2 or SDL 3 (main library): Windowing and inputs management * - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors +* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) and contributors * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -68,13 +68,12 @@ #define MAX_CLIPBOARD_BUFFER_LENGTH 1024 // Size of the clipboard buffer used on GetClipboardText() #endif -#if ((defined(SDL_MAJOR_VERSION) && SDL_MAJOR_VERSION == 3) && (defined(SDL_MINOR_VERSION) && SDL_MINOR_VERSION >= 1)) +#if ((defined(SDL_MAJOR_VERSION) && (SDL_MAJOR_VERSION == 3)) && (defined(SDL_MINOR_VERSION) && (SDL_MINOR_VERSION >= 1))) #ifndef PLATFORM_DESKTOP_SDL3 #define PLATFORM_DESKTOP_SDL3 #endif #endif - //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- @@ -83,6 +82,7 @@ typedef struct { SDL_GLContext glContext; SDL_GameController *gamepad[MAX_GAMEPADS]; + SDL_JoystickID gamepadId[MAX_GAMEPADS]; // Joystick instance ids SDL_Cursor *cursor; bool cursorRelative; } PlatformData; @@ -240,16 +240,15 @@ static const int CursorsLUT[] = { // SDL3 Migration Layer made to avoid `ifdefs` inside functions when we can. -#ifdef PLATFORM_DESKTOP_SDL3 +#if defined(PLATFORM_DESKTOP_SDL3) // SDL3 Migration: // SDL_WINDOW_FULLSCREEN_DESKTOP has been removed, // and you can call SDL_GetWindowFullscreenMode() -// to see whether an exclusive fullscreen mode will be used +// to see whether an exclusive fullscreen mode will be used // or the borderless fullscreen desktop mode will be used #define SDL_WINDOW_FULLSCREEN_DESKTOP SDL_WINDOW_FULLSCREEN - #define SDL_IGNORE false #define SDL_DISABLE false #define SDL_ENABLE true @@ -260,27 +259,29 @@ static const int CursorsLUT[] = { // 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 (|) -// // SDL3 Migration: Renamed -// IMPORTANT: -// Might need to call SDL_CleanupEvent somewhere see :https://github.com/libsdl-org/SDL/issues/3540#issuecomment-1793449852 -// +// IMPORTANT: Might need to call SDL_CleanupEvent somewhere see :https://github.com/libsdl-org/SDL/issues/3540#issuecomment-1793449852 #define SDL_DROPFILE SDL_EVENT_DROP_FILE - -const char* SDL_GameControllerNameForIndex(int joystickIndex) +// SDL2 implementation for SDL3 function +const char *SDL_GameControllerNameForIndex(int joystickIndex) { // NOTE: SDL3 uses the IDs itself (SDL_JoystickID) instead of SDL2 joystick_index - const char* name = NULL; + const char *name = NULL; int numJoysticks = 0; SDL_JoystickID *joysticks = SDL_GetJoysticks(&numJoysticks); - if (joysticks) { - if (joystickIndex < numJoysticks) { + + if (joysticks) + { + if (joystickIndex < numJoysticks) + { SDL_JoystickID instance_id = joysticks[joystickIndex]; name = SDL_GetGamepadNameForID(instance_id); } + SDL_free(joysticks); } + return name; } @@ -288,45 +289,40 @@ int SDL_GetNumVideoDisplays(void) { int monitorCount = 0; SDL_DisplayID *displays = SDL_GetDisplays(&monitorCount); - // Safe because If `mem` is NULL, SDL_free does nothing. + + // Safe because If `mem` is NULL, SDL_free does nothing SDL_free(displays); return monitorCount; } - -// SLD3 Migration: -// To emulate SDL2 this function should return `SDL_DISABLE` or `SDL_ENABLE` -// representing the *processing state* of the event before this function makes any changes to it. -Uint8 SDL_EventState(Uint32 type, int state) { - +// SLD3 Migration: To emulate SDL2 this function should return `SDL_DISABLE` or `SDL_ENABLE` +// representing the *processing state* of the event before this function makes any changes to it +Uint8 SDL_EventState(Uint32 type, int state) +{ Uint8 stateBefore = SDL_EventEnabled(type); + switch (state) { case SDL_DISABLE: SDL_SetEventEnabled(type, false); break; case SDL_ENABLE: SDL_SetEventEnabled(type, true); break; default: TRACELOG(LOG_WARNING, "Event sate: unknow type"); } + return stateBefore; } void SDL_GetCurrentDisplayMode_Adapter(SDL_DisplayID displayID, SDL_DisplayMode* mode) { const SDL_DisplayMode* currMode = SDL_GetCurrentDisplayMode(displayID); - if (currMode == NULL) - { - TRACELOG(LOG_WARNING, "No current display mode"); - } - else - { - *mode = *currMode; - } + + if (currMode == NULL) TRACELOG(LOG_WARNING, "No current display mode"); + else *mode = *currMode; } // SDL3 Migration: Renamed #define SDL_GetCurrentDisplayMode SDL_GetCurrentDisplayMode_Adapter - SDL_Surface *SDL_CreateRGBSurface(Uint32 flags, int width, int height, int depth, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) { return SDL_CreateSurface(width, height, SDL_GetPixelFormatForMasks(depth, Rmask, Gmask, Bmask, Amask)); @@ -337,11 +333,14 @@ SDL_Surface *SDL_CreateRGBSurface(Uint32 flags, int width, int height, int depth // not reliable across platforms, approximately replaced by multiplying // SDL_GetWindowDisplayScale() times 160 on iPhone and Android, and 96 on other platforms. // returns 0 on success or a negative error code on failure -int SDL_GetDisplayDPI(int displayIndex, float * ddpi, float * hdpi, float * vdpi) { - float dpi = SDL_GetWindowDisplayScale(platform.window) * 96.0; +int SDL_GetDisplayDPI(int displayIndex, float *ddpi, float *hdpi, float *vdpi) +{ + float dpi = SDL_GetWindowDisplayScale(platform.window)*96.0; + if (ddpi != NULL) *ddpi = dpi; if (hdpi != NULL) *hdpi = dpi; if (vdpi != NULL) *vdpi = dpi; + return 0; } @@ -368,17 +367,13 @@ int SDL_NumJoysticks(void) return numJoysticks; } - // SDL_SetRelativeMouseMode // returns 0 on success or a negative error code on failure // If relative mode is not supported, this returns -1. int SDL_SetRelativeMouseMode_Adapter(SDL_bool enabled) { - - // // SDL_SetWindowRelativeMouseMode(SDL_Window *window, bool enabled) // \returns true on success or false on failure; call SDL_GetError() for more - // if (SDL_SetWindowRelativeMouseMode(platform.window, enabled)) { return 0; // success @@ -398,7 +393,6 @@ bool SDL_GetRelativeMouseMode_Adapter(void) #define SDL_GetRelativeMouseMode SDL_GetRelativeMouseMode_Adapter - int SDL_GetNumTouchFingers(SDL_TouchID touchID) { // SDL_Finger **SDL_GetTouchFingers(SDL_TouchID touchID, int *count) @@ -412,16 +406,16 @@ int SDL_GetNumTouchFingers(SDL_TouchID touchID) // Since SDL2 doesn't have this function we leave 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) { +void *SDL_GetClipboardData(const char *mime_type, size_t *size) +{ TRACELOG(LOG_WARNING, "Getting clipboard data that is not text is only available in SDL3"); + // We could possibly implement it ourselves in this case for some easier platforms return NULL; } #endif // PLATFORM_DESKTOP_SDL3 - - //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- @@ -452,7 +446,7 @@ void ToggleFullscreen(void) const int monitor = SDL_GetWindowDisplayIndex(platform.window); const int monitorCount = SDL_GetNumVideoDisplays(); -#ifdef PLATFORM_DESKTOP_SDL3 // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure +#if defined(PLATFORM_DESKTOP_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure if ((monitor > 0) && (monitor <= monitorCount)) #else if ((monitor >= 0) && (monitor < monitorCount)) @@ -479,7 +473,8 @@ void ToggleBorderlessWindowed(void) { const int monitor = SDL_GetWindowDisplayIndex(platform.window); const int monitorCount = SDL_GetNumVideoDisplays(); -#ifdef PLATFORM_DESKTOP_SDL3 // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure + +#if defined(PLATFORM_DESKTOP_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure if ((monitor > 0) && (monitor <= monitorCount)) #else if ((monitor >= 0) && (monitor < monitorCount)) @@ -503,25 +498,28 @@ void ToggleBorderlessWindowed(void) void MaximizeWindow(void) { SDL_MaximizeWindow(platform.window); - CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; + if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) == 0) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; } // Set window state: minimized void MinimizeWindow(void) { SDL_MinimizeWindow(platform.window); - CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) == 0) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; } -// Set window state: not minimized/maximized +// Restore window from being minimized/maximized void RestoreWindow(void) { - SDL_ShowWindow(platform.window); + SDL_RestoreWindow(platform.window); + // CORE.Window.flags will be removed on PollInputEvents() } // Set window configuration state using flags void SetWindowState(unsigned int flags) { + if (!CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetWindowState does nothing before window initialization, Use \"SetConfigFlags\" instead"); + CORE.Window.flags |= flags; if (flags & FLAG_VSYNC_HINT) @@ -532,7 +530,8 @@ void SetWindowState(unsigned int flags) { const int monitor = SDL_GetWindowDisplayIndex(platform.window); const int monitorCount = SDL_GetNumVideoDisplays(); - #ifdef PLATFORM_DESKTOP_SDL3 // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure + + #if defined(PLATFORM_DESKTOP_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure if ((monitor > 0) && (monitor <= monitorCount)) #else if ((monitor >= 0) && (monitor < monitorCount)) @@ -575,7 +574,7 @@ void SetWindowState(unsigned int flags) } if (flags & FLAG_WINDOW_ALWAYS_RUN) { - TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_ALWAYS_RUN is not supported on PLATFORM_DESKTOP_SDL"); + CORE.Window.flags |= FLAG_WINDOW_ALWAYS_RUN; } if (flags & FLAG_WINDOW_TRANSPARENT) { @@ -595,7 +594,8 @@ void SetWindowState(unsigned int flags) { const int monitor = SDL_GetWindowDisplayIndex(platform.window); const int monitorCount = SDL_GetNumVideoDisplays(); - #ifdef PLATFORM_DESKTOP_SDL3 // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure + + #if defined(PLATFORM_DESKTOP_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure if ((monitor > 0) && (monitor <= monitorCount)) #else if ((monitor >= 0) && (monitor < monitorCount)) @@ -661,7 +661,7 @@ void ClearWindowState(unsigned int flags) } if (flags & FLAG_WINDOW_ALWAYS_RUN) { - TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_ALWAYS_RUN is not supported on PLATFORM_DESKTOP_SDL"); + CORE.Window.flags &= ~FLAG_WINDOW_ALWAYS_RUN; } if (flags & FLAG_WINDOW_TRANSPARENT) { @@ -704,70 +704,84 @@ void SetWindowIcon(Image image) switch (image.format) { case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: + { rmask = 0xFF, gmask = 0; bmask = 0, amask = 0; depth = 8, pitch = image.width; - break; + } break; case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: + { rmask = 0xFF, gmask = 0xFF00; bmask = 0, amask = 0; depth = 16, pitch = image.width*2; - break; + } break; case PIXELFORMAT_UNCOMPRESSED_R5G6B5: + { rmask = 0xF800, gmask = 0x07E0; bmask = 0x001F, amask = 0; depth = 16, pitch = image.width*2; - break; - case PIXELFORMAT_UNCOMPRESSED_R8G8B8: // Uses BGR for 24-bit - rmask = 0x0000FF, gmask = 0x00FF00; - bmask = 0xFF0000, amask = 0; + } break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8: + { + // WARNING: SDL2 could be using BGR but SDL3 RGB + rmask = 0xFF0000, gmask = 0x00FF00; + bmask = 0x0000FF, amask = 0; depth = 24, pitch = image.width*3; - break; + } break; case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: + { rmask = 0xF800, gmask = 0x07C0; bmask = 0x003E, amask = 0x0001; depth = 16, pitch = image.width*2; - break; + } break; case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: + { rmask = 0xF000, gmask = 0x0F00; bmask = 0x00F0, amask = 0x000F; depth = 16, pitch = image.width*2; - break; + } break; case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: + { rmask = 0xFF000000, gmask = 0x00FF0000; bmask = 0x0000FF00, amask = 0x000000FF; depth = 32, pitch = image.width*4; - break; + } break; case PIXELFORMAT_UNCOMPRESSED_R32: + { rmask = 0xFFFFFFFF, gmask = 0; bmask = 0, amask = 0; depth = 32, pitch = image.width*4; - break; + } break; case PIXELFORMAT_UNCOMPRESSED_R32G32B32: + { rmask = 0xFFFFFFFF, gmask = 0xFFFFFFFF; bmask = 0xFFFFFFFF, amask = 0; depth = 96, pitch = image.width*12; - break; + } break; case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: + { rmask = 0xFFFFFFFF, gmask = 0xFFFFFFFF; bmask = 0xFFFFFFFF, amask = 0xFFFFFFFF; depth = 128, pitch = image.width*16; - break; + } break; case PIXELFORMAT_UNCOMPRESSED_R16: + { rmask = 0xFFFF, gmask = 0; bmask = 0, amask = 0; depth = 16, pitch = image.width*2; - break; + } break; case PIXELFORMAT_UNCOMPRESSED_R16G16B16: + { rmask = 0xFFFF, gmask = 0xFFFF; bmask = 0xFFFF, amask = 0; depth = 48, pitch = image.width*6; - break; + } break; case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + { rmask = 0xFFFF, gmask = 0xFFFF; bmask = 0xFFFF, amask = 0xFFFF; depth = 64, pitch = image.width*8; - break; + } break; default: return; // Compressed formats are not supported } @@ -818,7 +832,8 @@ void SetWindowMonitor(int monitor) const int screenWidth = CORE.Window.screen.width; const int screenHeight = CORE.Window.screen.height; SDL_Rect usableBounds; - #ifdef PLATFORM_DESKTOP_SDL3 // Different style for success checking + + #if defined(PLATFORM_DESKTOP_SDL3) // Different style for success checking if (SDL_GetDisplayUsableBounds(monitor, &usableBounds)) #else if (SDL_GetDisplayUsableBounds(monitor, &usableBounds) == 0) @@ -915,7 +930,7 @@ int GetMonitorCount(void) return monitorCount; } -// Get number of monitors +// Get current monitor where window is placed int GetCurrentMonitor(void) { int currentMonitor = 0; @@ -933,7 +948,8 @@ Vector2 GetMonitorPosition(int monitor) if ((monitor >= 0) && (monitor < monitorCount)) { SDL_Rect displayBounds; - #ifdef PLATFORM_DESKTOP_SDL3 + + #if defined(PLATFORM_DESKTOP_SDL3) if (SDL_GetDisplayUsableBounds(monitor, &displayBounds)) #else if (SDL_GetDisplayUsableBounds(monitor, &displayBounds) == 0) @@ -1104,53 +1120,55 @@ const char *GetClipboardText(void) return buffer; } - -#if defined(SUPPORT_CLIPBOARD_IMAGE) // Get clipboard image Image GetClipboardImage(void) { + Image image = { 0 }; + +#if defined(SUPPORT_CLIPBOARD_IMAGE) // Let's hope compiler put these arrays in static memory - const char *image_formats[] = { + const char *imageFormats[] = { "image/bmp", "image/png", "image/jpg", "image/tiff", }; - const char *image_extensions[] = { + const char *imageExtensions[] = { ".bmp", ".png", ".jpg", ".tiff", }; - - Image image = {0}; size_t dataSize = 0; void *fileData = NULL; - for (int i = 0; i < SDL_arraysize(image_formats); ++i) + + for (int i = 0; i < SDL_arraysize(imageFormats); ++i) { - // NOTE: This pointer should be free with SDL_free() at some point. - fileData = SDL_GetClipboardData(image_formats[i], &dataSize); - if (fileData) { - image = LoadImageFromMemory(image_extensions[i], fileData, dataSize); + // NOTE: This pointer should be free with SDL_free() at some point + fileData = SDL_GetClipboardData(imageFormats[i], &dataSize); + + if (fileData) + { + image = LoadImageFromMemory(imageExtensions[i], fileData, dataSize); if (IsImageValid(image)) { - TRACELOG(LOG_INFO, "Clipboard image: Got image from clipboard as a `%s` successfully", image_extensions[i]); + TRACELOG(LOG_INFO, "Clipboard image: Got image from clipboard as a `%s` successfully", imageExtensions[i]); return image; } } } - TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data. %s", SDL_GetError()); - return image; -} + if (!IsImageValid(image)) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data. Error: %s", SDL_GetError()); #endif + return image; +} // Show mouse cursor void ShowCursor(void) { -#ifdef PLATFORM_DESKTOP_SDL3 +#if defined(PLATFORM_DESKTOP_SDL3) SDL_ShowCursor(); #else SDL_ShowCursor(SDL_ENABLE); @@ -1161,7 +1179,7 @@ void ShowCursor(void) // Hides mouse cursor void HideCursor(void) { -#ifdef PLATFORM_DESKTOP_SDL3 +#if defined(PLATFORM_DESKTOP_SDL3) SDL_HideCursor(); #else SDL_ShowCursor(SDL_DISABLE); @@ -1174,7 +1192,7 @@ void EnableCursor(void) { SDL_SetRelativeMouseMode(SDL_FALSE); -#ifdef PLATFORM_DESKTOP_SDL3 +#if defined(PLATFORM_DESKTOP_SDL3) // SDL_ShowCursor() has been split into three functions: SDL_ShowCursor(), SDL_HideCursor(), and SDL_CursorVisible() SDL_ShowCursor(); #else @@ -1275,7 +1293,7 @@ const char *GetKeyName(int key) static void UpdateTouchPointsSDL(SDL_TouchFingerEvent event) { -#ifdef PLATFORM_DESKTOP_SDL3 // SDL3 +#if defined(PLATFORM_DESKTOP_SDL3) // SDL3 int count = 0; SDL_Finger **fingers = SDL_GetTouchFingers(event.touchID, &count); CORE.Input.Touch.pointCount = count; @@ -1288,7 +1306,9 @@ static void UpdateTouchPointsSDL(SDL_TouchFingerEvent event) CORE.Input.Touch.position[i].y = finger->y*CORE.Window.screen.height; CORE.Input.Touch.currentTouchState[i] = 1; } + SDL_free(fingers); + #else // SDL2 CORE.Input.Touch.pointCount = SDL_GetNumTouchFingers(event.touchId); @@ -1345,7 +1365,7 @@ void PollInputEvents(void) for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; // Map touch position to mouse position for convenience - CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; + if (CORE.Input.Touch.pointCount == 0) CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE bool realTouch = false; // Flag to differentiate real touch gestures from mouse ones @@ -1375,6 +1395,12 @@ void PollInputEvents(void) CORE.Window.resizedLastFrame = false; + if ((CORE.Window.eventWaiting) || (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) && ((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) == 0))) + { + SDL_WaitEvent(NULL); + CORE.Time.previous = GetTime(); + } + SDL_Event event = { 0 }; while (SDL_PollEvent(&event) != 0) { @@ -1393,7 +1419,8 @@ void PollInputEvents(void) CORE.Window.dropFilepaths = (char **)RL_CALLOC(1024, sizeof(char *)); CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); - #ifdef PLATFORM_DESKTOP_SDL3 + + #if defined(PLATFORM_DESKTOP_SDL3) // const char *data; /**< The text for SDL_EVENT_DROP_TEXT and the file name for SDL_EVENT_DROP_FILE, NULL for other events */ // Event memory is now managed by SDL, so you should not free the data in SDL_EVENT_DROP_FILE, and if you want to hold onto the text in SDL_EVENT_TEXT_EDITING and SDL_EVENT_TEXT_INPUT events, you should make a copy of it. SDL_TEXTINPUTEVENT_TEXT_SIZE is no longer necessary and has been removed. strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.data); @@ -1407,7 +1434,8 @@ void PollInputEvents(void) else if (CORE.Window.dropFileCount < 1024) { CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); - #ifdef PLATFORM_DESKTOP_SDL3 + + #if defined(PLATFORM_DESKTOP_SDL3) strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.data); #else strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file); @@ -1422,7 +1450,7 @@ void PollInputEvents(void) // Window events are also polled (Minimized, maximized, close...) - #ifndef PLATFORM_DESKTOP_SDL3 + #ifndef PLATFORM_DESKTOP_SDL3 // SDL3 states: // The SDL_WINDOWEVENT_* events have been moved to top level events, // and SDL_WINDOWEVENT has been removed. @@ -1432,19 +1460,45 @@ void PollInputEvents(void) { switch (event.window.event) { - #endif + #endif case SDL_WINDOWEVENT_RESIZED: case SDL_WINDOWEVENT_SIZE_CHANGED: { const int width = event.window.data1; const int height = event.window.data2; SetupViewport(width, height); - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; + // 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)(width / GetWindowScaleDPI().x); + CORE.Window.screen.height = (int)(height / GetWindowScaleDPI().y); + } + else + { + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + } CORE.Window.currentFbo.width = width; CORE.Window.currentFbo.height = height; CORE.Window.resizedLastFrame = true; + + #ifndef PLATFORM_DESKTOP_SDL3 + // Manually detect if the window was maximized (due to SDL2 restore being unreliable on some platforms) to remove the FLAG_WINDOW_MAXIMIZED accordingly + if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) + { + int borderTop = 0; + int borderLeft = 0; + int borderBottom = 0; + int borderRight = 0; + SDL_GetWindowBordersSize(platform.window, &borderTop, &borderLeft, &borderBottom, &borderRight); + SDL_Rect usableBounds; + SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(platform.window), &usableBounds); + + if ((width + borderLeft + borderRight != usableBounds.w) && (height + borderTop + borderBottom != usableBounds.h)) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; + } + #endif } break; + case SDL_WINDOWEVENT_ENTER: { CORE.Input.Mouse.cursorOnScreen = true; @@ -1453,16 +1507,49 @@ void PollInputEvents(void) { CORE.Input.Mouse.cursorOnScreen = false; } break; - case SDL_WINDOWEVENT_HIDDEN: + case SDL_WINDOWEVENT_MINIMIZED: - case SDL_WINDOWEVENT_FOCUS_LOST: - case SDL_WINDOWEVENT_SHOWN: - case SDL_WINDOWEVENT_FOCUS_GAINED: + { + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) == 0) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; + } break; case SDL_WINDOWEVENT_MAXIMIZED: + { + if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) == 0) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; + } break; case SDL_WINDOWEVENT_RESTORED: - #ifdef PLATFORM_DESKTOP_SDL3 - break; - #else + { + if ((SDL_GetWindowFlags(platform.window) & SDL_WINDOW_MINIMIZED) == 0) + { + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; + } + + #ifdef PLATFORM_DESKTOP_SDL3 + if ((SDL_GetWindowFlags(platform.window) & SDL_WINDOW_MAXIMIZED) == 0) + { + if ((CORE.Window.flags & SDL_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~SDL_WINDOW_MAXIMIZED; + } + #endif + } break; + + case SDL_WINDOWEVENT_HIDDEN: + { + if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) == 0) CORE.Window.flags |= FLAG_WINDOW_HIDDEN; + } break; + case SDL_WINDOWEVENT_SHOWN: + { + if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; + } break; + + case SDL_WINDOWEVENT_FOCUS_GAINED: + { + if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; + } break; + case SDL_WINDOWEVENT_FOCUS_LOST: + { + if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0) CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; + } break; + + #ifndef PLATFORM_DESKTOP_SDL3 default: break; } } break; @@ -1471,7 +1558,7 @@ void PollInputEvents(void) // Keyboard events case SDL_KEYDOWN: { - #ifdef PLATFORM_DESKTOP_SDL3 + #if defined(PLATFORM_DESKTOP_SDL3) // SDL3 Migration: The following structures have been removed: * SDL_Keysym KeyboardKey key = ConvertScancodeToKey(event.key.scancode); #else @@ -1502,7 +1589,7 @@ void PollInputEvents(void) case SDL_KEYUP: { - #ifdef PLATFORM_DESKTOP_SDL3 + #if defined(PLATFORM_DESKTOP_SDL3) KeyboardKey key = ConvertScancodeToKey(event.key.scancode); #else KeyboardKey key = ConvertScancodeToKey(event.key.keysym.scancode); @@ -1597,11 +1684,12 @@ void PollInputEvents(void) // Check gamepad events case SDL_JOYDEVICEADDED: { - int jid = event.jdevice.which; + int jid = event.jdevice.which; // Joystick device index - if (!CORE.Input.Gamepad.ready[jid] && (jid < MAX_GAMEPADS)) + if (CORE.Input.Gamepad.ready[jid] && (jid < MAX_GAMEPADS)) { platform.gamepad[jid] = SDL_GameControllerOpen(jid); + platform.gamepadId[jid] = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(platform.gamepad[jid])); if (platform.gamepad[jid]) { @@ -1609,8 +1697,8 @@ void PollInputEvents(void) CORE.Input.Gamepad.axisCount[jid] = SDL_JoystickNumAxes(SDL_GameControllerGetJoystick(platform.gamepad[jid])); CORE.Input.Gamepad.axisState[jid][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f; CORE.Input.Gamepad.axisState[jid][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f; - strncpy(CORE.Input.Gamepad.name[jid], SDL_GameControllerNameForIndex(jid), 63); - CORE.Input.Gamepad.name[jid][63] = '\0'; + memset(CORE.Input.Gamepad.name[jid], 0, MAX_GAMEPAD_NAME_LENGTH); + strncpy(CORE.Input.Gamepad.name[jid], SDL_GameControllerNameForIndex(jid), MAX_GAMEPAD_NAME_LENGTH - 1); } else { @@ -1620,14 +1708,18 @@ void PollInputEvents(void) } break; case SDL_JOYDEVICEREMOVED: { - int jid = event.jdevice.which; + int jid = event.jdevice.which; // Joystick instance id - if (jid == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(platform.gamepad[jid]))) + for (int i = 0; i < MAX_GAMEPADS; i++) { - SDL_GameControllerClose(platform.gamepad[jid]); - platform.gamepad[jid] = SDL_GameControllerOpen(0); - CORE.Input.Gamepad.ready[jid] = false; - memset(CORE.Input.Gamepad.name[jid], 0, 64); + if (platform.gamepadId[i] == jid) + { + SDL_GameControllerClose(platform.gamepad[i]); + CORE.Input.Gamepad.ready[i] = false; + memset(CORE.Input.Gamepad.name[i], 0, MAX_GAMEPAD_NAME_LENGTH); + platform.gamepadId[i] = -1; + break; + } } } break; case SDL_CONTROLLERBUTTONDOWN: @@ -1660,8 +1752,15 @@ void PollInputEvents(void) if (button >= 0) { - CORE.Input.Gamepad.currentButtonState[event.jbutton.which][button] = 1; - CORE.Input.Gamepad.lastButtonPressed = button; + for (int i = 0; i < MAX_GAMEPADS; i++) + { + if (platform.gamepadId[i] == event.jbutton.which) + { + CORE.Input.Gamepad.currentButtonState[i][button] = 1; + CORE.Input.Gamepad.lastButtonPressed = button; + break; + } + } } } break; case SDL_CONTROLLERBUTTONUP: @@ -1694,8 +1793,15 @@ void PollInputEvents(void) if (button >= 0) { - CORE.Input.Gamepad.currentButtonState[event.jbutton.which][button] = 0; - if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0; + for (int i = 0; i < MAX_GAMEPADS; i++) + { + if (platform.gamepadId[i] == event.jbutton.which) + { + CORE.Input.Gamepad.currentButtonState[i][button] = 0; + if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0; + break; + } + } } } break; case SDL_CONTROLLERAXISMOTION: @@ -1715,18 +1821,25 @@ void PollInputEvents(void) if (axis >= 0) { - // SDL axis value range is -32768 to 32767, we normalize it to RayLib's -1.0 to 1.0f range - float value = event.jaxis.value/(float)32767; - CORE.Input.Gamepad.axisState[event.jaxis.which][axis] = value; - - // Register button state for triggers in addition to their axes - if ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER)) + for (int i = 0; i < MAX_GAMEPADS; i++) { - 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.jaxis.which][button] = pressed; - if (pressed) CORE.Input.Gamepad.lastButtonPressed = button; - else if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0; + 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 + float value = event.jaxis.value/(float)32767; + CORE.Input.Gamepad.axisState[i][axis] = value; + + // Register button state for triggers in addition to their axes + if ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER)) + { + int button = (axis == GAMEPAD_AXIS_LEFT_TRIGGER)? GAMEPAD_BUTTON_LEFT_TRIGGER_2 : GAMEPAD_BUTTON_RIGHT_TRIGGER_2; + int pressed = (value > 0.1f); + CORE.Input.Gamepad.currentButtonState[i][button] = pressed; + if (pressed) CORE.Input.Gamepad.lastButtonPressed = button; + else if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0; + } + break; + } } } } break; @@ -1858,7 +1971,7 @@ int InitPlatform(void) } // Init window -#ifdef PLATFORM_DESKTOP_SDL3 +#if defined(PLATFORM_DESKTOP_SDL3) platform.window = SDL_CreateWindow(CORE.Window.title, CORE.Window.screen.width, CORE.Window.screen.height, flags); #else platform.window = SDL_CreateWindow(CORE.Window.title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, CORE.Window.screen.width, CORE.Window.screen.height, flags); @@ -1906,9 +2019,15 @@ int InitPlatform(void) // Initialize input events system //---------------------------------------------------------------------------- // Initialize gamepads + for (int i = 0; i < MAX_GAMEPADS; i++) + { + platform.gamepadId[i] = -1; // Set all gamepad initial instance ids as invalid to not conflict with instance id zero + } + for (int i = 0; (i < SDL_NumJoysticks()) && (i < MAX_GAMEPADS); i++) { platform.gamepad[i] = SDL_GameControllerOpen(i); + platform.gamepadId[i] = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(platform.gamepad[i])); if (platform.gamepad[i]) { @@ -1916,8 +2035,8 @@ int InitPlatform(void) CORE.Input.Gamepad.axisCount[i] = SDL_JoystickNumAxes(SDL_GameControllerGetJoystick(platform.gamepad[i])); CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f; CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f; - strncpy(CORE.Input.Gamepad.name[i], SDL_GameControllerNameForIndex(i), 63); - CORE.Input.Gamepad.name[i][63] = '\0'; + strncpy(CORE.Input.Gamepad.name[i], SDL_GameControllerNameForIndex(i), MAX_GAMEPAD_NAME_LENGTH - 1); + CORE.Input.Gamepad.name[i][MAX_GAMEPAD_NAME_LENGTH - 1] = '\0'; } else TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError()); } @@ -1946,11 +2065,10 @@ int InitPlatform(void) CORE.Storage.basePath = SDL_GetBasePath(); // Alternative: GetWorkingDirectory(); //---------------------------------------------------------------------------- - -#ifdef PLATFORM_DESKTOP_SDL3 +#if defined(PLATFORM_DESKTOP_SDL3) TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (SDL3): Initialized successfully"); #else - TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (SDL): Initialized successfully"); + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (SDL2): Initialized successfully"); #endif return 0; @@ -1968,7 +2086,7 @@ void ClosePlatform(void) // Scancode to keycode mapping static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode) { - if (sdlScancode >= 0 && sdlScancode < SCANCODE_MAPPED_NUM) + if ((sdlScancode >= 0) && (sdlScancode < SCANCODE_MAPPED_NUM)) { return mapScancodeToKey[sdlScancode]; } diff --git a/raylib/platforms/rcore_drm.c b/raylib/platforms/rcore_drm.c index 69b1ed1..20d676e 100644 --- a/raylib/platforms/rcore_drm.c +++ b/raylib/platforms/rcore_drm.c @@ -21,15 +21,24 @@ * Reconfigure standard input to receive key inputs, works with SSH connection. * WARNING: Reconfiguring standard input could lead to undesired effects, like breaking other * running processes orblocking the device if not restored properly. Use with care. +* #define ENABLE_WAYLAND_DRM_LEASING +* Instead of acquiring DRM exclusively, this leases a DRM instance from the currently running +* Wayland compositor. This requires the screen to have the non-desktop property, which is set +* through EDIDs. X11 is not supported, but it may be in the future. +* See: +* https://learn.microsoft.com/en-us/windows-hardware/drivers/display/specialized-monitors-edid-extension * * DEPENDENCIES: * - DRM and GLM: System libraries for display initialization and configuration * - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) +* - wayland-client: Needed for DRM leasing. Only used if ENABLE_WAYLAND_DRM_LEASING is set. +* - drm-lease-v1: Generated Wayland code for the DRM leasing extension. Only used if +* ENABLE_WAYLAND_DRM_LEASING is set. * * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors +* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) and contributors * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -71,6 +80,13 @@ #include "EGL/egl.h" // Native platform windowing system interface #include "EGL/eglext.h" // EGL extensions +#if defined(ENABLE_WAYLAND_DRM_LEASING) + #include "wayland-client.h" // Wayland client. Used to do DRM leasing. + // DRM leasing protocol that's used + #include "drm-lease-v1.c" + #include "drm-lease-v1.h" +#endif + #ifndef EGL_OPENGL_ES3_BIT #define EGL_OPENGL_ES3_BIT 0x40 #endif @@ -125,9 +141,13 @@ typedef struct { // Gamepad data int gamepadStreamFd[MAX_GAMEPADS]; // Gamepad device file descriptor - int gamepadAbsAxisRange[MAX_GAMEPADS][MAX_GAMEPAD_AXIS][2]; // [0] = min, [1] = range value of the axis + int gamepadAbsAxisRange[MAX_GAMEPADS][MAX_GAMEPAD_AXES][2]; // [0] = min, [1] = range value of the axes int gamepadAbsAxisMap[MAX_GAMEPADS][ABS_CNT]; // Maps the axes gamepads from the evdev api to a sequential one int gamepadCount; // The number of gamepads registered + + #if defined(ENABLE_WAYLAND_DRM_LEASING) + bool usingDRMLeasing; // True if we are using DRM leasing + #endif } PlatformData; //---------------------------------------------------------------------------------- @@ -278,7 +298,7 @@ void MinimizeWindow(void) TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform"); } -// Set window state: not minimized/maximized +// Restore window from being minimized/maximized void RestoreWindow(void) { TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); @@ -372,7 +392,7 @@ int GetMonitorCount(void) return 1; } -// Get number of monitors +// Get current monitor where window is placed int GetCurrentMonitor(void) { TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform"); @@ -510,6 +530,16 @@ const char *GetClipboardText(void) return NULL; } +// Get clipboard image +Image GetClipboardImage(void) +{ + Image image = { 0 }; + + TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform"); + + return image; +} + // Show mouse cursor void ShowCursor(void) { @@ -613,7 +643,7 @@ int SetGamepadMappings(const char *mappings) // Set gamepad vibration void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration) { - TRACELOG(LOG_WARNING, "GamepadSetVibration() not implemented on target platform"); + TRACELOG(LOG_WARNING, "SetGamepadVibration() not implemented on target platform"); } // Set mouse position XY @@ -707,10 +737,200 @@ void PollInputEvents(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- +#if defined(ENABLE_WAYLAND_DRM_LEASING) + // Wayland state for DRM leasing + struct wayland_state { + struct wp_drm_lease_device_v1 *lease_device; + struct wp_drm_lease_connector_v1 *lease_connector; + }; + + // Called when we get a potential DRM lease + void lease_handler(void *data, struct wp_drm_lease_v1 *wp_drm_lease_v1, int32_t leased_fd) + { + int *fd = data; + *fd = leased_fd; + } + + void lease_remove_handler(void *data, struct wp_drm_lease_v1 *wp_drm_lease_v1) + { + // Do nothing + } + + static const struct wp_drm_lease_v1_listener lease_listener = { + .lease_fd = lease_handler, + .finished = lease_remove_handler, + }; + + // Called when we get a potential DRM lease + static void lease_device_fd_handler(void *data, struct wp_drm_lease_device_v1 *lease_device, int fd) + { + close(fd); + } + + // Called when we get a lease connector + static void lease_device_connector_handler(void *data, struct wp_drm_lease_device_v1 *lease_device, struct wp_drm_lease_connector_v1 *conn) + { + struct wayland_state *state = data; + + if (!state->lease_connector) { + state->lease_connector = conn; + } + } + + static void lease_device_on_done(void *data, struct wp_drm_lease_device_v1 *lease_device) + { + // Do nothing + } + + static void lease_device_on_release(void *data, struct wp_drm_lease_device_v1 *lease_device) + { + // Do nothing + } + + static const struct wp_drm_lease_device_v1_listener lease_manager_listener = { + .drm_fd = lease_device_fd_handler, + .connector = lease_device_connector_handler, + .done = lease_device_on_done, + .released = lease_device_on_release, + }; + + // Listen for protocol list/dump + static void registry_handler(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) + { + struct wayland_state *state = data; + + if (strcmp(interface, "wp_drm_lease_device_v1") == 0) { + state->lease_device = wl_registry_bind(registry, name, &wp_drm_lease_device_v1_interface, version); + wp_drm_lease_device_v1_add_listener(state->lease_device, &lease_manager_listener, state); + } + } + + // Listen for remove event + static void registry_remove_handler(void *data, struct wl_registry *registry, uint32_t name) + { + // Do nothing + } + + static const struct wl_registry_listener registry_listener = { + .global = registry_handler, + .global_remove = registry_remove_handler, + }; + + // Get the DRM device's file descriptor via DRM leasing. Seperate function due to readability concerns + int GetDRMFDViaWaylandLeasing(void) + { + int fd = -1; + + // Connect to the potentially running Wayland instance + struct wayland_state state = { 0 }; + struct wl_display *display = wl_display_connect(NULL); + + if (!display) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to connect to Wayland server for DRM leasing! Is Wayland running? Cannot do DRM leasing"); + return -1; + } + + // Check the Wayland registry to determine if the DRM leasing protocol (wp_drm_lease_device_v1) is supported. If so, get the lease device and connector. + struct wl_registry *registry = wl_display_get_registry(display); + wl_registry_add_listener(registry, ®istry_listener, &state); + wl_display_dispatch(display); + wl_display_roundtrip(display); + + if (!state.lease_device) { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to acquire leasing device"); + return -1; + } + + if (!state.lease_connector) { + TRACELOG(LOG_WARNING, "DISPLAY: No connectors offered for lease"); + return -1; + } + + struct wp_drm_lease_request_v1 *req = wp_drm_lease_device_v1_create_lease_request(state.lease_device); + wp_drm_lease_request_v1_request_connector(req, state.lease_connector); + + struct wp_drm_lease_v1 *lease = wp_drm_lease_request_v1_submit(req); + wp_drm_lease_v1_add_listener(lease, &lease_listener, &fd); + wl_display_dispatch(display); + wl_display_roundtrip(display); + + if (fd < 0) { + TRACELOG(LOG_WARNING, "DISPLAY: DRM file descriptor is not set. Cannot use DRM leased value"); + return -1; + } + + return fd; + } +#endif + +// Get the DRM device's file descriptor +int GetDRMFD(void) +{ + int fd; + + #if defined(ENABLE_WAYLAND_DRM_LEASING) + TRACELOG(LOG_INFO, "DISPLAY: Attempting Wayland DRM leasing"); + fd = GetDRMFDViaWaylandLeasing(); + + if (fd != -1) + { + TRACELOG(LOG_INFO, "DISPLAY: Leased DRM device opened successfully"); + platform.usingDRMLeasing = true; + return fd; + } + #endif + + #if defined(DEFAULT_GRAPHIC_DEVICE_DRM) + fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR); + + if (fd != -1) + { + TRACELOG(LOG_INFO, "DISPLAY: Default graphic device DRM opened successfully"); + return fd; + } + #else + TRACELOG(LOG_WARNING, "DISPLAY: No graphic card set, trying platform-gpu-card"); + fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4) + + if (fd != -1) + { + TRACELOG(LOG_INFO, "DISPLAY: platform-gpu-card opened successfully"); + return fd; + } + + if (drmModeGetResources(fd) == NULL) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to open platform-gpu-card, trying card1"); + fd = open("/dev/dri/card1", O_RDWR); // Other Embedded + + if (fd != -1) + { + TRACELOG(LOG_INFO, "DISPLAY: card1 opened successfully"); + return fd; + } + } + + if (drmModeGetResources(fd) == NULL) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card1, trying card0"); + fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3) + + if (fd != -1) + { + TRACELOG(LOG_INFO, "DISPLAY: card0 opened successfully"); + return fd; + } + } + #endif + + return fd; +} + // Initialize platform: graphics, inputs and more int InitPlatform(void) { - platform.fd = -1; + platform.fd = GetDRMFD(); platform.connector = NULL; platform.modeIndex = -1; platform.crtc = NULL; @@ -724,25 +944,6 @@ int InitPlatform(void) CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; -#if defined(DEFAULT_GRAPHIC_DEVICE_DRM) - platform.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR); -#else - TRACELOG(LOG_INFO, "DISPLAY: No graphic card set, trying platform-gpu-card"); - platform.fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4) - - if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL)) - { - TRACELOG(LOG_INFO, "DISPLAY: Failed to open platform-gpu-card, trying card1"); - platform.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded - } - - if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL)) - { - TRACELOG(LOG_INFO, "DISPLAY: Failed to open graphic card1, trying card0"); - platform.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3) - } -#endif - if (platform.fd == -1) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card"); @@ -767,17 +968,48 @@ int InitPlatform(void) // In certain cases the status of the conneciton is reported as UKNOWN, but it is still connected. // This might be a hardware or software limitation like on Raspberry Pi Zero with composite output. - if (((con->connection == DRM_MODE_CONNECTED) || (con->connection == DRM_MODE_UNKNOWNCONNECTION)) && (con->encoder_id)) - { - TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected"); - platform.connector = con; - break; - } - else - { - TRACELOG(LOG_TRACE, "DISPLAY: DRM mode NOT connected (deleting)"); - drmModeFreeConnector(con); - } + #if defined(ENABLE_WAYLAND_DRM_LEASING) + if (platform.usingDRMLeasing) + { + if (((con->connection == DRM_MODE_CONNECTED) || (con->connection == DRM_MODE_UNKNOWNCONNECTION))) + { + TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected"); + platform.connector = con; + break; + } + else + { + TRACELOG(LOG_TRACE, "DISPLAY: DRM mode NOT connected (deleting)"); + drmModeFreeConnector(con); + } + } + else + { + if (((con->connection == DRM_MODE_CONNECTED) || (con->connection == DRM_MODE_UNKNOWNCONNECTION)) && (con->encoder_id)) + { + TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected"); + platform.connector = con; + break; + } + else + { + TRACELOG(LOG_TRACE, "DISPLAY: DRM mode NOT connected (deleting)"); + drmModeFreeConnector(con); + } + } + #else + if (((con->connection == DRM_MODE_CONNECTED) || (con->connection == DRM_MODE_UNKNOWNCONNECTION)) && (con->encoder_id)) + { + TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected"); + platform.connector = con; + break; + } + else + { + TRACELOG(LOG_TRACE, "DISPLAY: DRM mode NOT connected (deleting)"); + drmModeFreeConnector(con); + } + #endif } if (!platform.connector) @@ -787,22 +1019,83 @@ int InitPlatform(void) return -1; } - drmModeEncoder *enc = drmModeGetEncoder(platform.fd, platform.connector->encoder_id); - if (!enc) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder"); - drmModeFreeResources(res); - return -1; - } + #if defined(ENABLE_WAYLAND_DRM_LEASING) + drmModeEncoder *enc = NULL; - platform.crtc = drmModeGetCrtc(platform.fd, enc->crtc_id); - if (!platform.crtc) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc"); - drmModeFreeEncoder(enc); - drmModeFreeResources(res); - return -1; - } + if (platform.usingDRMLeasing) + { + drmModeResPtr drm_resources = drmModeGetResources(platform.fd); + + if (!drm_resources) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM resources"); + drmModeFreeResources(res); + return -1; + } + + if (res->count_crtcs == 0) + { + TRACELOG(LOG_WARNING, "DISPLAY: No CRTCs found for display"); + } + + for (size_t i = 0; i < res->count_crtcs; ++i) + { + platform.crtc = drmModeGetCrtc(platform.fd, res->crtcs[i]); + + if (!platform.crtc) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc"); + drmModeFreeResources(drm_resources); + drmModeFreeResources(res); + return -1; + } + } + + if (!platform.crtc) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc"); + drmModeFreeResources(drm_resources); + drmModeFreeResources(res); + return -1; + } + } + else + { + enc = drmModeGetEncoder(platform.fd, platform.connector->encoder_id); + if (!enc) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder"); + drmModeFreeResources(res); + return -1; + } + + platform.crtc = drmModeGetCrtc(platform.fd, enc->crtc_id); + if (!platform.crtc) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc"); + drmModeFreeEncoder(enc); + drmModeFreeResources(res); + return -1; + } + } + #else + drmModeEncoder *enc = drmModeGetEncoder(platform.fd, platform.connector->encoder_id); + if (!enc) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder"); + drmModeFreeResources(res); + return -1; + } + + platform.crtc = drmModeGetCrtc(platform.fd, enc->crtc_id); + if (!platform.crtc) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc"); + drmModeFreeEncoder(enc); + drmModeFreeResources(res); + return -1; + } + #endif // If InitWindow should use the current mode find it in the connector's mode list if ((CORE.Window.screen.width <= 0) || (CORE.Window.screen.height <= 0)) @@ -814,7 +1107,11 @@ int InitPlatform(void) if (platform.modeIndex < 0) { TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found"); - drmModeFreeEncoder(enc); + #if defined(ENABLE_WAYLAND_DRM_LEASING) + if (!platform.usingDRMLeasing) drmModeFreeEncoder(enc); + #else + drmModeFreeEncoder(enc); + #endif drmModeFreeResources(res); return -1; } @@ -842,7 +1139,11 @@ int InitPlatform(void) if (platform.modeIndex < 0) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode"); - drmModeFreeEncoder(enc); + #if defined(ENABLE_WAYLAND_DRM_LEASING) + if (!platform.usingDRMLeasing) drmModeFreeEncoder(enc); + #else + drmModeFreeEncoder(enc); + #endif drmModeFreeResources(res); return -1; } @@ -859,8 +1160,16 @@ int InitPlatform(void) CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; - drmModeFreeEncoder(enc); - enc = NULL; + #if defined(ENABLE_WAYLAND_DRM_LEASING) + if (!platform.usingDRMLeasing) + { + drmModeFreeEncoder(enc); + enc = NULL; + } + #else + drmModeFreeEncoder(enc); + enc = NULL; + #endif drmModeFreeResources(res); res = NULL; @@ -936,7 +1245,7 @@ int InitPlatform(void) TRACELOG(LOG_TRACE, "DISPLAY: EGL configs available: %d", numConfigs); - EGLConfig *configs = RL_CALLOC(numConfigs, sizeof(*configs)); + EGLConfig *configs = (EGLConfig *)RL_CALLOC(numConfigs, sizeof(*configs)); if (!configs) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get memory for EGL configs"); @@ -1330,46 +1639,52 @@ static void ProcessKeyboard(void) // this means mouse, keyboard or gamepad devices static void InitEvdevInput(void) { - char path[MAX_FILEPATH_LENGTH] = { 0 }; - DIR *directory = NULL; - struct dirent *entity = NULL; + #if !defined(DISABLE_EVDEV_INPUT) + char path[MAX_FILEPATH_LENGTH] = { 0 }; + DIR *directory = NULL; + struct dirent *entity = NULL; - // Initialise keyboard file descriptor - platform.keyboardFd = -1; - platform.mouseFd = -1; + // Initialise keyboard file descriptor + platform.keyboardFd = -1; + platform.mouseFd = -1; - // Reset variables - for (int i = 0; i < MAX_TOUCH_POINTS; ++i) - { - CORE.Input.Touch.position[i].x = -1; - CORE.Input.Touch.position[i].y = -1; - } - - // Reset keyboard key state - for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) - { - CORE.Input.Keyboard.currentKeyState[i] = 0; - CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; - } - - // Open the linux directory of "/dev/input" - directory = opendir(DEFAULT_EVDEV_PATH); - - if (directory) - { - while ((entity = readdir(directory)) != NULL) + // Reset variables + for (int i = 0; i < MAX_TOUCH_POINTS; ++i) { - if ((strncmp("event", entity->d_name, strlen("event")) == 0) || // Search for devices named "event*" - (strncmp("mouse", entity->d_name, strlen("mouse")) == 0)) // Search for devices named "mouse*" - { - sprintf(path, "%s%s", DEFAULT_EVDEV_PATH, entity->d_name); - ConfigureEvdevDevice(path); // Configure the device if appropriate - } + CORE.Input.Touch.position[i].x = -1; + CORE.Input.Touch.position[i].y = -1; } - closedir(directory); - } - else TRACELOG(LOG_WARNING, "INPUT: Failed to open linux event directory: %s", DEFAULT_EVDEV_PATH); + // Reset keyboard key state + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) + { + CORE.Input.Keyboard.currentKeyState[i] = 0; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } + + // Open the linux directory of "/dev/input" + directory = opendir(DEFAULT_EVDEV_PATH); + + if (directory) + { + while ((entity = readdir(directory)) != NULL) + { + if ((strncmp("event", entity->d_name, strlen("event")) == 0) || // Search for devices named "event*" + (strncmp("mouse", entity->d_name, strlen("mouse")) == 0)) // Search for devices named "mouse*" + { + sprintf(path, "%s%s", DEFAULT_EVDEV_PATH, entity->d_name); + ConfigureEvdevDevice(path); // Configure the device if appropriate + } + } + + closedir(directory); + } + else TRACELOG(LOG_WARNING, "INPUT: Failed to open linux event directory: %s", DEFAULT_EVDEV_PATH); + #else + TRACELOG(LOG_INFO, "INPUT: Not doing evdev input configuration as it's disabled"); + platform.keyboardFd = -1; + platform.mouseFd = -1; + #endif } // Identifies a input device and configures it for use if appropriate @@ -1391,7 +1706,7 @@ static void ConfigureEvdevDevice(char *device) int fd = open(device, O_RDONLY | O_NONBLOCK); if (fd < 0) { - TRACELOG(LOG_WARNING, "DRM: Failed to open input device: %s", device); + TRACELOG(LOG_WARNING, "SYSTEM: Failed to open input device: %s", device); return; } @@ -1448,7 +1763,7 @@ static void ConfigureEvdevDevice(char *device) // matter if we support them else if (hasAbsXY && TEST_BIT(keyBits, BTN_MOUSE)) isMouse = true; - // If any of the common joystick axis is present, we assume it's a gamepad + // If any of the common joystick axes are present, we assume it's a gamepad else { for (int axis = (hasAbsXY? ABS_Z : ABS_X); axis < ABS_PRESSURE; axis++) @@ -1520,7 +1835,7 @@ static void ConfigureEvdevDevice(char *device) platform.absRange.height = absinfo[ABS_Y].info.maximum - absinfo[ABS_Y].info.minimum; } } - else if (isGamepad && !isMouse && !isKeyboard && platform.gamepadCount < MAX_GAMEPADS) + else if (isGamepad && !isMouse && !isKeyboard && (platform.gamepadCount < MAX_GAMEPADS)) { deviceKindStr = "gamepad"; int index = platform.gamepadCount++; @@ -1534,7 +1849,7 @@ static void ConfigureEvdevDevice(char *device) if (absAxisCount > 0) { // TODO / NOTE - // So gamepad axis (as in the actual linux joydev.c) are just simply enumerated + // So gamepad axes (as in the actual linux joydev.c) are just 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 @@ -1669,7 +1984,7 @@ static void PollGamepadEvents(void) TRACELOG(LOG_DEBUG, "INPUT: Gamepad %2i: Axis: %2i Value: %i", i, axisRaylib, event.value); - if (axisRaylib < MAX_GAMEPAD_AXIS) + if (axisRaylib < MAX_GAMEPAD_AXES) { int min = platform.gamepadAbsAxisRange[i][event.code][0]; int range = platform.gamepadAbsAxisRange[i][event.code][1]; @@ -1884,7 +2199,7 @@ static int FindExactConnectorMode(const drmModeConnector *connector, uint width, TRACELOG(LOG_TRACE, "DISPLAY: DRM Mode %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive"); - if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced)) continue; + if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && !allowInterlaced) continue; if ((mode->hdisplay == width) && (mode->vdisplay == height) && (mode->vrefresh == fps)) return i; } @@ -1914,7 +2229,7 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt continue; } - if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced)) + if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && !allowInterlaced) { TRACELOG(LOG_TRACE, "DISPLAY: DRM shouldn't choose an interlaced mode"); continue; diff --git a/raylib/platforms/vendor.go b/raylib/platforms/vendor.go new file mode 100644 index 0000000..0fb2a0f --- /dev/null +++ b/raylib/platforms/vendor.go @@ -0,0 +1,4 @@ +//go:build required +// +build required + +package vendor diff --git a/raylib/raudio.c b/raylib/raudio.c index 47d28e0..0faf3ad 100644 --- a/raylib/raudio.c +++ b/raylib/raudio.c @@ -53,7 +53,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) * * 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. @@ -607,6 +607,7 @@ AudioBuffer *LoadAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sam audioBuffer->usage = usage; audioBuffer->frameCursorPos = 0; + audioBuffer->framesProcessed = 0; audioBuffer->sizeInFrames = sizeInFrames; // Buffers should be marked as processed by default so that a call to @@ -653,6 +654,9 @@ void PlayAudioBuffer(AudioBuffer *buffer) buffer->playing = true; buffer->paused = false; buffer->frameCursorPos = 0; + buffer->framesProcessed = 0; + buffer->isSubBufferProcessed[0] = true; + buffer->isSubBufferProcessed[1] = true; ma_mutex_unlock(&AUDIO.System.lock); } } @@ -804,10 +808,10 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int wave.sampleRate = wav.sampleRate; wave.sampleSize = 16; wave.channels = wav.channels; - wave.data = (short *)RL_MALLOC(wave.frameCount*wave.channels*sizeof(short)); + wave.data = (short *)RL_MALLOC((size_t)wave.frameCount*wave.channels*sizeof(short)); // NOTE: We are forcing conversion to 16bit sample size on reading - drwav_read_pcm_frames_s16(&wav, wav.totalPCMFrameCount, wave.data); + drwav_read_pcm_frames_s16(&wav, wave.frameCount, wave.data); } else TRACELOG(LOG_WARNING, "WAVE: Failed to load WAV data"); @@ -1128,7 +1132,7 @@ bool ExportWaveAsCode(Wave wave, const char *fileName) byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n"); byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); - byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2024 Ramon Santamaria (@raysan5) //\n"); + byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2025 Ramon Santamaria (@raysan5) //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); byteCount += sprintf(txtData + byteCount, "//////////////////////////////////////////////////////////////////////////////////\n\n"); @@ -1243,6 +1247,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels) frameCount = (ma_uint32)ma_convert_frames(data, frameCount, formatOut, channels, sampleRate, wave->data, frameCountIn, formatIn, wave->channels, wave->sampleRate); if (frameCount == 0) { + RL_FREE(wave->data); TRACELOG(LOG_WARNING, "WAVE: Failed format conversion"); return; } @@ -1335,7 +1340,7 @@ Music LoadMusicStream(const char *fileName) #if defined(SUPPORT_FILEFORMAT_WAV) else if (IsFileExtension(fileName, ".wav")) { - drwav *ctxWav = RL_CALLOC(1, sizeof(drwav)); + drwav *ctxWav = (drwav *)RL_CALLOC(1, sizeof(drwav)); bool success = drwav_init_file(ctxWav, fileName, NULL); if (success) @@ -1385,7 +1390,7 @@ Music LoadMusicStream(const char *fileName) #if defined(SUPPORT_FILEFORMAT_MP3) else if (IsFileExtension(fileName, ".mp3")) { - drmp3 *ctxMp3 = RL_CALLOC(1, sizeof(drmp3)); + drmp3 *ctxMp3 = (drmp3 *)RL_CALLOC(1, sizeof(drmp3)); int result = drmp3_init_file(ctxMp3, fileName, NULL); if (result > 0) @@ -1476,7 +1481,7 @@ Music LoadMusicStream(const char *fileName) #if defined(SUPPORT_FILEFORMAT_MOD) else if (IsFileExtension(fileName, ".mod")) { - jar_mod_context_t *ctxMod = RL_CALLOC(1, sizeof(jar_mod_context_t)); + jar_mod_context_t *ctxMod = (jar_mod_context_t *)RL_CALLOC(1, sizeof(jar_mod_context_t)); jar_mod_init(ctxMod); int result = jar_mod_load_file(ctxMod, fileName); @@ -1527,7 +1532,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, #if defined(SUPPORT_FILEFORMAT_WAV) else if ((strcmp(fileType, ".wav") == 0) || (strcmp(fileType, ".WAV") == 0)) { - drwav *ctxWav = RL_CALLOC(1, sizeof(drwav)); + drwav *ctxWav = (drwav *)RL_CALLOC(1, sizeof(drwav)); bool success = drwav_init_memory(ctxWav, (const void *)data, dataSize, NULL); @@ -1553,7 +1558,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, else if ((strcmp(fileType, ".ogg") == 0) || (strcmp(fileType, ".OGG") == 0)) { // Open ogg audio stream - stb_vorbis* ctxOgg = stb_vorbis_open_memory((const unsigned char *)data, dataSize, NULL, NULL); + stb_vorbis *ctxOgg = stb_vorbis_open_memory((const unsigned char *)data, dataSize, NULL, NULL); if (ctxOgg != NULL) { @@ -1578,7 +1583,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, #if defined(SUPPORT_FILEFORMAT_MP3) else if ((strcmp(fileType, ".mp3") == 0) || (strcmp(fileType, ".MP3") == 0)) { - drmp3 *ctxMp3 = RL_CALLOC(1, sizeof(drmp3)); + drmp3 *ctxMp3 = (drmp3 *)RL_CALLOC(1, sizeof(drmp3)); int success = drmp3_init_memory(ctxMp3, (const void*)data, dataSize, NULL); if (success) @@ -1858,6 +1863,8 @@ void SeekMusicStream(Music music, float position) ma_mutex_lock(&AUDIO.System.lock); music.stream.buffer->framesProcessed = positionInFrames; + music.stream.buffer->isSubBufferProcessed[0] = true; + music.stream.buffer->isSubBufferProcessed[1] = true; ma_mutex_unlock(&AUDIO.System.lock); } @@ -2104,8 +2111,12 @@ AudioStream LoadAudioStream(unsigned int sampleRate, unsigned int sampleSize, un // The size of a streaming buffer must be at least double the size of a period unsigned int periodSize = AUDIO.System.device.playback.internalPeriodSizeInFrames; - // If the buffer is not set, compute one that would give us a buffer good enough for a decent frame rate - unsigned int subBufferSize = (AUDIO.Buffer.defaultSize == 0)? AUDIO.System.device.sampleRate/30 : AUDIO.Buffer.defaultSize; + // If the buffer is not set, compute one that would give us a buffer good enough for a decent frame rate at the device bit size/rate + int deviceBitsPerSample = AUDIO.System.device.playback.format; + if (deviceBitsPerSample > 4) deviceBitsPerSample = 4; + deviceBitsPerSample *= AUDIO.System.device.playback.channels; + + unsigned int subBufferSize = (AUDIO.Buffer.defaultSize == 0) ? (AUDIO.System.device.sampleRate/30*deviceBitsPerSample) : AUDIO.Buffer.defaultSize; if (subBufferSize < periodSize) subBufferSize = periodSize; @@ -2462,23 +2473,18 @@ static ma_uint32 ReadAudioBufferFramesInMixingFormat(AudioBuffer *audioBuffer, f float *runningFramesOut = framesOut + (totalOutputFramesProcessed*audioBuffer->converter.channelsOut); - /* At this point we can convert the data to our mixing format. */ - ma_uint64 inputFramesProcessedThisIteration = ReadAudioBufferFramesInInternalFormat(audioBuffer, inputBuffer, (ma_uint32)inputFramesToProcessThisIteration); /* Safe cast. */ + // At this point we can convert the data to our mixing format + ma_uint64 inputFramesProcessedThisIteration = ReadAudioBufferFramesInInternalFormat(audioBuffer, inputBuffer, (ma_uint32)inputFramesToProcessThisIteration); ma_uint64 outputFramesProcessedThisIteration = outputFramesToProcessThisIteration; ma_data_converter_process_pcm_frames(&audioBuffer->converter, inputBuffer, &inputFramesProcessedThisIteration, runningFramesOut, &outputFramesProcessedThisIteration); - totalOutputFramesProcessed += (ma_uint32)outputFramesProcessedThisIteration; /* Safe cast. */ + totalOutputFramesProcessed += (ma_uint32)outputFramesProcessedThisIteration; // Safe cast - if (inputFramesProcessedThisIteration < inputFramesToProcessThisIteration) - { - break; /* Ran out of input data. */ - } + if (inputFramesProcessedThisIteration < inputFramesToProcessThisIteration) break; // Ran out of input data - /* This should never be hit, but will add it here for safety. Ensures we get out of the loop when no input nor output frames are processed. */ - if (inputFramesProcessedThisIteration == 0 && outputFramesProcessedThisIteration == 0) - { - break; - } + // This should never be hit, but added here for safety + // Ensures we get out of the loop when no input nor output frames are processed + if ((inputFramesProcessedThisIteration == 0) && (outputFramesProcessedThisIteration == 0)) break; } return totalOutputFramesProcessed; diff --git a/raylib/raudio.go b/raylib/raudio.go index ee0aba7..129a71e 100644 --- a/raylib/raudio.go +++ b/raylib/raudio.go @@ -237,6 +237,12 @@ func UnloadSound(sound Sound) { C.UnloadSound(*csound) } +// UnloadSoundAlias - Unload a sound alias (does not deallocate sample data) +func UnloadSoundAlias(sound Sound) { + csound := sound.cptr() + C.UnloadSoundAlias(*csound) +} + // ExportWave - Export wave data to file func ExportWave(wave Wave, fileName string) { cwave := wave.cptr() @@ -299,7 +305,7 @@ func SetSoundPan(sound Sound, pan float32) { } // WaveFormat - Convert wave data to desired format -func WaveFormat(wave Wave, sampleRate int32, sampleSize int32, channels int32) { +func WaveFormat(wave *Wave, sampleRate int32, sampleSize int32, channels int32) { cwave := wave.cptr() csampleRate := (C.int)(sampleRate) csampleSize := (C.int)(sampleSize) @@ -316,7 +322,7 @@ func WaveCopy(wave Wave) Wave { } // WaveCrop - Crop a wave to defined frames range -func WaveCrop(wave Wave, initFrame int32, finalFrame int32) { +func WaveCrop(wave *Wave, initFrame int32, finalFrame int32) { cwave := wave.cptr() cinitFrame := (C.int)(initFrame) cfinalFrame := (C.int)(finalFrame) diff --git a/raylib/raylib.go b/raylib/raylib.go index bbe5d26..0cd3126 100644 --- a/raylib/raylib.go +++ b/raylib/raylib.go @@ -31,7 +31,7 @@ type Wave struct { // Number of channels (1-mono, 2-stereo) Channels uint32 // Buffer data pointer - data unsafe.Pointer + Data unsafe.Pointer } // NewWave - Returns new Wave @@ -929,7 +929,29 @@ type ModelAnimation struct { FrameCount int32 Bones *BoneInfo FramePoses **Transform - Name [32]int8 + Name [32]uint8 +} + +// GetBones returns the bones information (skeleton) of a ModelAnimation as go slice +func (m ModelAnimation) GetBones() []BoneInfo { + return unsafe.Slice(m.Bones, m.BoneCount) +} + +// GetFramePose returns the Transform for a specific bone at a specific frame +func (m ModelAnimation) GetFramePose(frame, bone int) Transform { + framePoses := unsafe.Slice(m.FramePoses, m.FrameCount) + return unsafe.Slice(framePoses[frame], m.BoneCount)[bone] +} + +// GetName returns the ModelAnimation's name as go string +func (m ModelAnimation) GetName() string { + var end int + for end = range m.Name { + if m.Name[end] == 0 { + break + } + } + return string(m.Name[:end]) } // RayCollision type - ray hit information diff --git a/raylib/raylib.h b/raylib/raylib.h index a26b8ce..f42ab40 100644 --- a/raylib/raylib.h +++ b/raylib/raylib.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* raylib v5.5 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) +* raylib v5.6-dev - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) * * FEATURES: * - NO external dependencies, all required libraries included with raylib @@ -63,7 +63,7 @@ * raylib is 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) 2013-2024 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) * * 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. @@ -88,9 +88,9 @@ #include // Required for: va_list - Only used by TraceLogCallback #define RAYLIB_VERSION_MAJOR 5 -#define RAYLIB_VERSION_MINOR 5 +#define RAYLIB_VERSION_MINOR 6 #define RAYLIB_VERSION_PATCH 0 -#define RAYLIB_VERSION "5.5" +#define RAYLIB_VERSION "5.6-dev" // 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 @@ -743,7 +743,7 @@ typedef enum { GAMEPAD_BUTTON_RIGHT_THUMB // Gamepad joystick pressed button right } GamepadButton; -// Gamepad axis +// Gamepad axes typedef enum { GAMEPAD_AXIS_LEFT_X = 0, // Gamepad left stick X axis GAMEPAD_AXIS_LEFT_Y = 1, // Gamepad left stick Y axis @@ -801,7 +801,8 @@ typedef enum { 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_BONE_MATRICES, // Shader location: array of matrices uniform: boneMatrices + SHADER_LOC_VERTEX_INSTANCE_TX // Shader location: vertex attribute: instanceTransform } ShaderLocationIndex; #define SHADER_LOC_MAP_DIFFUSE SHADER_LOC_MAP_ALBEDO @@ -817,6 +818,10 @@ typedef enum { SHADER_UNIFORM_IVEC2, // Shader uniform type: ivec2 (2 int) SHADER_UNIFORM_IVEC3, // Shader uniform type: ivec3 (3 int) SHADER_UNIFORM_IVEC4, // Shader uniform type: ivec4 (4 int) + SHADER_UNIFORM_UINT, // Shader uniform type: unsigned int + SHADER_UNIFORM_UIVEC2, // Shader uniform type: uivec2 (2 unsigned int) + SHADER_UNIFORM_UIVEC3, // Shader uniform type: uivec3 (3 unsigned int) + SHADER_UNIFORM_UIVEC4, // Shader uniform type: uivec4 (4 unsigned int) SHADER_UNIFORM_SAMPLER2D // Shader uniform type: sampler2d } ShaderUniformDataType; @@ -949,7 +954,7 @@ typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args); typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, int *dataSize); // FileIO: Load binary data typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, int dataSize); // FileIO: Save binary data typedef char *(*LoadFileTextCallback)(const char *fileName); // FileIO: Load text data -typedef bool (*SaveFileTextCallback)(const char *fileName, char *text); // FileIO: Save text data +typedef bool (*SaveFileTextCallback)(const char *fileName, const char *text); // FileIO: Save text data //------------------------------------------------------------------------------------ // Global Variables Definition @@ -982,7 +987,7 @@ RLAPI void ToggleFullscreen(void); // Toggle wind RLAPI void ToggleBorderlessWindowed(void); // Toggle window state: borderless windowed, resizes window to match monitor resolution RLAPI void MaximizeWindow(void); // Set window state: maximized, if resizable RLAPI void MinimizeWindow(void); // Set window state: minimized, if resizable -RLAPI void RestoreWindow(void); // Set window state: not minimized/maximized +RLAPI void RestoreWindow(void); // Restore window from being minimized/maximized RLAPI void SetWindowIcon(Image image); // Set icon for window (single image, RGBA 32bit) RLAPI void SetWindowIcons(Image *images, int count); // Set icon for window (multiple images, RGBA 32bit) RLAPI void SetWindowTitle(const char *title); // Set title for window @@ -1056,7 +1061,7 @@ RLAPI int GetShaderLocationAttrib(Shader shader, const char *attribName); // Ge RLAPI void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType); // Set shader uniform value RLAPI void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count); // Set shader uniform value vector RLAPI void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat); // Set shader uniform value (matrix 4x4) -RLAPI void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture); // Set shader uniform value for texture (sampler2d) +RLAPI void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture); // Set shader uniform value and bind the texture (sampler2d) RLAPI void UnloadShader(Shader shader); // Unload shader from GPU memory (VRAM) // Screen-space-related functions @@ -1118,7 +1123,7 @@ RLAPI bool SaveFileData(const char *fileName, void *data, int dataSize); // Save RLAPI bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName); // Export data to code (.h), returns true on success RLAPI char *LoadFileText(const char *fileName); // Load text data from file (read), returns a '\0' terminated string RLAPI void UnloadFileText(char *text); // Unload file text data allocated by LoadFileText() -RLAPI bool SaveFileText(const char *fileName, char *text); // Save text data to file (write), string must be '\0' terminated, returns true on success +RLAPI bool SaveFileText(const char *fileName, const char *text); // Save text data to file (write), string must be '\0' terminated, returns true on success //------------------------------------------------------------------ // File system functions @@ -1148,22 +1153,21 @@ RLAPI long GetFileModTime(const char *fileName); // Get file mo // Compression/Encoding functionality RLAPI unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize); // Compress data (DEFLATE algorithm), memory must be MemFree() RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // Decompress data (DEFLATE algorithm), memory must be MemFree() -RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string, memory must be MemFree() -RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree() -RLAPI unsigned int ComputeCRC32(unsigned char *data, int dataSize); // Compute CRC32 hash code -RLAPI unsigned int *ComputeMD5(unsigned char *data, int dataSize); // Compute MD5 hash code, returns static int[4] (16 bytes) -RLAPI unsigned int *ComputeSHA1(unsigned char *data, int dataSize); // Compute SHA1 hash code, returns static int[5] (20 bytes) - +RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string (includes NULL terminator), memory must be MemFree() +RLAPI unsigned char *DecodeDataBase64(const char *text, int *outputSize); // Decode Base64 string (expected NULL terminated), memory must be MemFree() +RLAPI unsigned int ComputeCRC32(unsigned char *data, int dataSize); // Compute CRC32 hash code +RLAPI unsigned int *ComputeMD5(unsigned char *data, int dataSize); // Compute MD5 hash code, returns static int[4] (16 bytes) +RLAPI unsigned int *ComputeSHA1(unsigned char *data, int dataSize); // Compute SHA1 hash code, returns static int[5] (20 bytes) // Automation events functionality -RLAPI AutomationEventList LoadAutomationEventList(const char *fileName); // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS -RLAPI void UnloadAutomationEventList(AutomationEventList list); // Unload automation events list from file -RLAPI bool ExportAutomationEventList(AutomationEventList list, const char *fileName); // Export automation events list as text file -RLAPI void SetAutomationEventList(AutomationEventList *list); // Set automation event list to record to -RLAPI void SetAutomationEventBaseFrame(int frame); // Set automation event internal base frame to start recording -RLAPI void StartAutomationEventRecording(void); // Start recording automation events (AutomationEventList must be set) -RLAPI void StopAutomationEventRecording(void); // Stop recording automation events -RLAPI void PlayAutomationEvent(AutomationEvent event); // Play a recorded automation event +RLAPI AutomationEventList LoadAutomationEventList(const char *fileName); // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS +RLAPI void UnloadAutomationEventList(AutomationEventList list); // Unload automation events list from file +RLAPI bool ExportAutomationEventList(AutomationEventList list, const char *fileName); // Export automation events list as text file +RLAPI void SetAutomationEventList(AutomationEventList *list); // Set automation event list to record to +RLAPI void SetAutomationEventBaseFrame(int frame); // Set automation event internal base frame to start recording +RLAPI void StartAutomationEventRecording(void); // Start recording automation events (AutomationEventList must be set) +RLAPI void StopAutomationEventRecording(void); // Stop recording automation events +RLAPI void PlayAutomationEvent(AutomationEvent event); // Play a recorded automation event //------------------------------------------------------------------------------------ // Input Handling Functions (Module: core) @@ -1177,19 +1181,20 @@ RLAPI bool IsKeyReleased(int key); // Check if a key RLAPI bool IsKeyUp(int key); // Check if a key is NOT being pressed RLAPI int GetKeyPressed(void); // Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty RLAPI int GetCharPressed(void); // Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty +RLAPI const char *GetKeyName(int key); // Get name of a QWERTY key on the current keyboard layout (eg returns string 'q' for KEY_A on an AZERTY keyboard) RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC) // Input-related functions: gamepads -RLAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is available -RLAPI const char *GetGamepadName(int gamepad); // Get gamepad internal name id -RLAPI bool IsGamepadButtonPressed(int gamepad, int button); // Check if a gamepad button has been pressed once -RLAPI bool IsGamepadButtonDown(int gamepad, int button); // Check if a gamepad button is being pressed -RLAPI bool IsGamepadButtonReleased(int gamepad, int button); // Check if a gamepad button has been released once -RLAPI bool IsGamepadButtonUp(int gamepad, int button); // Check if a gamepad button is NOT being pressed -RLAPI int GetGamepadButtonPressed(void); // Get the last gamepad button pressed -RLAPI int GetGamepadAxisCount(int gamepad); // Get gamepad axis count for a gamepad -RLAPI float GetGamepadAxisMovement(int gamepad, int axis); // Get axis movement value for a gamepad axis -RLAPI int SetGamepadMappings(const char *mappings); // Set internal gamepad mappings (SDL_GameControllerDB) +RLAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is available +RLAPI const char *GetGamepadName(int gamepad); // Get gamepad internal name id +RLAPI bool IsGamepadButtonPressed(int gamepad, int button); // Check if a gamepad button has been pressed once +RLAPI bool IsGamepadButtonDown(int gamepad, int button); // Check if a gamepad button is being pressed +RLAPI bool IsGamepadButtonReleased(int gamepad, int button); // Check if a gamepad button has been released once +RLAPI bool IsGamepadButtonUp(int gamepad, int button); // Check if a gamepad button is NOT being pressed +RLAPI int GetGamepadButtonPressed(void); // Get the last gamepad button pressed +RLAPI int GetGamepadAxisCount(int gamepad); // Get axis count for a gamepad +RLAPI float GetGamepadAxisMovement(int gamepad, int axis); // Get movement value for a gamepad axis +RLAPI int SetGamepadMappings(const char *mappings); // Set internal gamepad mappings (SDL_GameControllerDB) RLAPI void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration); // Set gamepad vibration for both motors (duration in seconds) // Input-related functions: mouse @@ -1218,19 +1223,19 @@ RLAPI int GetTouchPointCount(void); // Get number of t //------------------------------------------------------------------------------------ // Gestures and Touch Handling Functions (Module: rgestures) //------------------------------------------------------------------------------------ -RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags -RLAPI bool IsGestureDetected(unsigned int gesture); // Check if a gesture have been detected -RLAPI int GetGestureDetected(void); // Get latest detected gesture -RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in seconds -RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector -RLAPI float GetGestureDragAngle(void); // Get gesture drag angle -RLAPI Vector2 GetGesturePinchVector(void); // Get gesture pinch delta -RLAPI float GetGesturePinchAngle(void); // Get gesture pinch angle +RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags +RLAPI bool IsGestureDetected(unsigned int gesture); // Check if a gesture have been detected +RLAPI int GetGestureDetected(void); // Get latest detected gesture +RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in seconds +RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector +RLAPI float GetGestureDragAngle(void); // Get gesture drag angle +RLAPI Vector2 GetGesturePinchVector(void); // Get gesture pinch delta +RLAPI float GetGesturePinchAngle(void); // Get gesture pinch angle //------------------------------------------------------------------------------------ // Camera System Functions (Module: rcamera) //------------------------------------------------------------------------------------ -RLAPI void UpdateCamera(Camera *camera, int mode); // Update camera position for selected mode +RLAPI void UpdateCamera(Camera *camera, int mode); // Update camera position for selected mode RLAPI void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, float zoom); // Update camera movement/rotation //------------------------------------------------------------------------------------ @@ -1239,9 +1244,9 @@ RLAPI void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, f // Set texture and rectangle to be used on shapes drawing // NOTE: It can be useful when using basic shapes and one single font, // defining a font char white rectangle would allow drawing everything in a single draw call -RLAPI void SetShapesTexture(Texture2D texture, Rectangle source); // Set texture and rectangle to be used on shapes drawing -RLAPI Texture2D GetShapesTexture(void); // Get texture that is used for shapes drawing -RLAPI Rectangle GetShapesTextureRectangle(void); // Get texture source rectangle that is used for shapes drawing +RLAPI void SetShapesTexture(Texture2D texture, Rectangle source); // Set texture and rectangle to be used on shapes drawing +RLAPI Texture2D GetShapesTexture(void); // Get texture that is used for shapes drawing +RLAPI Rectangle GetShapesTextureRectangle(void); // Get texture source rectangle that is used for shapes drawing // Basic shapes drawing functions RLAPI void DrawPixel(int posX, int posY, Color color); // Draw a pixel using geometry [Can be slow, use with care] @@ -1259,7 +1264,9 @@ RLAPI void DrawCircleV(Vector2 center, float radius, Color color); RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline RLAPI void DrawCircleLinesV(Vector2 center, float radius, Color color); // Draw circle outline (Vector version) RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse +RLAPI void DrawEllipseV(Vector2 center, float radiusH, float radiusV, Color color); // Draw ellipse (Vector version) RLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse outline +RLAPI void DrawEllipseLinesV(Vector2 center, float radiusH, float radiusV, Color color); // Draw ellipse outline (Vector version) RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring RLAPI void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring outline RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle @@ -1268,7 +1275,7 @@ RLAPI void DrawRectangleRec(Rectangle rec, Color color); RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color); // Draw a color-filled rectangle with pro parameters RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color top, Color bottom); // Draw a vertical-gradient-filled rectangle RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color left, Color right); // Draw a horizontal-gradient-filled rectangle -RLAPI void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color topRight, Color bottomRight); // Draw a gradient-filled rectangle with custom vertex colors +RLAPI void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color bottomRight, Color topRight); // Draw a gradient-filled rectangle with custom vertex colors RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color); // Draw rectangle outline RLAPI void DrawRectangleLinesEx(Rectangle rec, float lineThick, Color color); // Draw rectangle outline with extended parameters RLAPI void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color); // Draw rectangle with rounded edges @@ -1283,11 +1290,11 @@ RLAPI void DrawPolyLines(Vector2 center, int sides, float radius, float rotation RLAPI void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, float lineThick, Color color); // Draw a polygon outline of n sides with extended parameters // Splines drawing functions -RLAPI void DrawSplineLinear(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Linear, minimum 2 points -RLAPI void DrawSplineBasis(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: B-Spline, minimum 4 points -RLAPI void DrawSplineCatmullRom(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Catmull-Rom, minimum 4 points -RLAPI void DrawSplineBezierQuadratic(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...] -RLAPI void DrawSplineBezierCubic(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...] +RLAPI void DrawSplineLinear(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Linear, minimum 2 points +RLAPI void DrawSplineBasis(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: B-Spline, minimum 4 points +RLAPI void DrawSplineCatmullRom(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Catmull-Rom, minimum 4 points +RLAPI void DrawSplineBezierQuadratic(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...] +RLAPI void DrawSplineBezierCubic(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...] RLAPI void DrawSplineSegmentLinear(Vector2 p1, Vector2 p2, float thick, Color color); // Draw spline segment: Linear, 2 points RLAPI void DrawSplineSegmentBasis(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color); // Draw spline segment: B-Spline, 4 points RLAPI void DrawSplineSegmentCatmullRom(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color); // Draw spline segment: Catmull-Rom, 4 points @@ -1360,7 +1367,7 @@ RLAPI void ImageAlphaPremultiply(Image *image); RLAPI void ImageBlurGaussian(Image *image, int blurSize); // Apply Gaussian blur using a box blur approximation RLAPI void ImageKernelConvolution(Image *image, const float *kernel, int kernelSize); // Apply custom square convolution kernel to image RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (Bicubic scaling algorithm) -RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight); // Resize image (Nearest-Neighbor scaling algorithm) +RLAPI void ImageResizeNN(Image *image, int newWidth, int newHeight); // Resize image (Nearest-Neighbor scaling algorithm) RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill); // Resize canvas and fill with color RLAPI void ImageMipmaps(Image *image); // Compute all mipmap levels for a provided image RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) @@ -1401,8 +1408,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, 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, 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) @@ -1486,18 +1493,19 @@ RLAPI GlyphInfo GetGlyphInfo(Font font, int codepoint); RLAPI Rectangle GetGlyphAtlasRec(Font font, int codepoint); // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found // Text codepoints management functions (unicode characters) -RLAPI char *LoadUTF8(const int *codepoints, int length); // Load UTF-8 text encoded from codepoints array -RLAPI void UnloadUTF8(char *text); // Unload UTF-8 text encoded from codepoints array -RLAPI int *LoadCodepoints(const char *text, int *count); // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter -RLAPI void UnloadCodepoints(int *codepoints); // Unload codepoints data from memory -RLAPI int GetCodepointCount(const char *text); // Get total number of codepoints in a UTF-8 encoded string -RLAPI int GetCodepoint(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure -RLAPI int GetCodepointNext(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure -RLAPI int GetCodepointPrevious(const char *text, int *codepointSize); // Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure -RLAPI const char *CodepointToUTF8(int codepoint, int *utf8Size); // Encode one codepoint into UTF-8 byte array (array length returned as parameter) +RLAPI char *LoadUTF8(const int *codepoints, int length); // Load UTF-8 text encoded from codepoints array +RLAPI void UnloadUTF8(char *text); // Unload UTF-8 text encoded from codepoints array +RLAPI int *LoadCodepoints(const char *text, int *count); // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter +RLAPI void UnloadCodepoints(int *codepoints); // Unload codepoints data from memory +RLAPI int GetCodepointCount(const char *text); // Get total number of codepoints in a UTF-8 encoded string +RLAPI int GetCodepoint(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure +RLAPI int GetCodepointNext(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure +RLAPI int GetCodepointPrevious(const char *text, int *codepointSize); // Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure +RLAPI const char *CodepointToUTF8(int codepoint, int *utf8Size); // Encode one codepoint into UTF-8 byte array (array length returned as parameter) // Text strings management functions (no UTF-8 strings, only byte chars) -// NOTE: Some strings allocate memory internally for returned strings, just be careful! +// WARNING 1: Most of these functions use internal static buffers, it's recommended to store returned data on user-side for re-use +// WARNING 2: Some strings allocate memory internally for the returned strings, those strings must be free by user using MemFree() RLAPI int TextCopy(char *dst, const char *src); // Copy one string to another, returns bytes copied RLAPI bool TextIsEqual(const char *text1, const char *text2); // Check if two text string are equal RLAPI unsigned int TextLength(const char *text); // Get text length, checks for '\0' ending @@ -1505,18 +1513,18 @@ RLAPI const char *TextFormat(const char *text, ...); RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string RLAPI char *TextReplace(const char *text, const char *replace, const char *by); // Replace text string (WARNING: memory must be freed!) RLAPI char *TextInsert(const char *text, const char *insert, int position); // Insert text in a position (WARNING: memory must be freed!) -RLAPI const char *TextJoin(const char **textList, int count, const char *delimiter); // Join text strings with delimiter -RLAPI const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings +RLAPI char *TextJoin(char **textList, int count, const char *delimiter); // Join text strings with delimiter +RLAPI char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings RLAPI void TextAppend(char *text, const char *append, int *position); // Append text at specific position and move cursor! RLAPI int TextFindIndex(const char *text, const char *find); // Find first text occurrence within a string -RLAPI const char *TextToUpper(const char *text); // Get upper case version of provided string -RLAPI const char *TextToLower(const char *text); // Get lower case version of provided string -RLAPI const char *TextToPascal(const char *text); // Get Pascal case notation version of provided string -RLAPI const char *TextToSnake(const char *text); // Get Snake case notation version of provided string -RLAPI const char *TextToCamel(const char *text); // Get Camel case notation version of provided string +RLAPI char *TextToUpper(const char *text); // Get upper case version of provided string +RLAPI char *TextToLower(const char *text); // Get lower case version of provided string +RLAPI char *TextToPascal(const char *text); // Get Pascal case notation version of provided string +RLAPI char *TextToSnake(const char *text); // Get Snake case notation version of provided string +RLAPI char *TextToCamel(const char *text); // Get Camel case notation version of provided string -RLAPI int TextToInteger(const char *text); // Get integer value from text (negative values not supported) -RLAPI float TextToFloat(const char *text); // Get float value from text (negative values not supported) +RLAPI int TextToInteger(const char *text); // Get integer value from text +RLAPI float TextToFloat(const char *text); // Get float value from text //------------------------------------------------------------------------------------ // Basic 3d Shapes Drawing Functions (Module: models) @@ -1609,14 +1617,14 @@ RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount); RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match // Collision detection functions -RLAPI bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2); // Check collision between two spheres -RLAPI bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2); // Check collision between two bounding boxes -RLAPI bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius); // Check collision between box and sphere -RLAPI RayCollision GetRayCollisionSphere(Ray ray, Vector3 center, float radius); // Get collision info between ray and sphere -RLAPI RayCollision GetRayCollisionBox(Ray ray, BoundingBox box); // Get collision info between ray and box -RLAPI RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform); // Get collision info between ray and mesh -RLAPI RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3); // Get collision info between ray and triangle -RLAPI RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4); // Get collision info between ray and quad +RLAPI bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2); // Check collision between two spheres +RLAPI bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2); // Check collision between two bounding boxes +RLAPI bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius); // Check collision between box and sphere +RLAPI RayCollision GetRayCollisionSphere(Ray ray, Vector3 center, float radius); // Get collision info between ray and sphere +RLAPI RayCollision GetRayCollisionBox(Ray ray, BoundingBox box); // Get collision info between ray and box +RLAPI RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform); // Get collision info between ray and mesh +RLAPI RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3); // Get collision info between ray and triangle +RLAPI RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4); // Get collision info between ray and quad //------------------------------------------------------------------------------------ // Audio Loading and Playing Functions (Module: audio) @@ -1695,10 +1703,10 @@ RLAPI void SetAudioStreamPan(AudioStream stream, float pan); // Set pan 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 -RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream, receives the samples as 'float' +RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream, receives frames x 2 samples as 'float' (stereo) RLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Detach audio stream processor from stream -RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline, receives the samples as 'float' +RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline, receives frames x 2 samples as 'float' (stereo) RLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline #if defined(__cplusplus) diff --git a/raylib/raylib_purego.go b/raylib/raylib_purego.go index bdfd4c1..70e8b61 100644 --- a/raylib/raylib_purego.go +++ b/raylib/raylib_purego.go @@ -8,6 +8,7 @@ import ( "image" "image/color" "os" + "reflect" "unsafe" "github.com/ebitengine/purego" @@ -17,6 +18,10 @@ import ( var ( // raylibDll is the pointer to the shared library raylibDll uintptr + + // audioCallbacks is needed to have a reference between Go functions (keys) created by the user + // and C function pointers (values) created by purego.NewCallback + audioCallbacks map[uintptr]uintptr ) var initWindow func(width int32, height int32, title string) @@ -532,6 +537,7 @@ var detachAudioMixedProcessor func(processor uintptr) func init() { raylibDll = loadLibrary() + audioCallbacks = make(map[uintptr]uintptr) initRlglPurego() @@ -3894,15 +3900,15 @@ func AttachAudioStreamProcessor(stream AudioStream, processor AudioCallback) { processor(unsafe.Slice((*float32)(bufferData), frames), int(frames)) return 0 }) + ptr := uintptr(reflect.ValueOf(processor).UnsafePointer()) + audioCallbacks[ptr] = fn attachAudioStreamProcessor(uintptr(unsafe.Pointer(&stream)), fn) } // DetachAudioStreamProcessor - Detach audio stream processor from stream func DetachAudioStreamProcessor(stream AudioStream, processor AudioCallback) { - fn := purego.NewCallback(func(bufferData unsafe.Pointer, frames int32) uintptr { - processor(unsafe.Slice((*float32)(bufferData), frames), int(frames)) - return 0 - }) + ptr := uintptr(reflect.ValueOf(processor).UnsafePointer()) + fn := audioCallbacks[ptr] detachAudioStreamProcessor(uintptr(unsafe.Pointer(&stream)), fn) } @@ -3912,15 +3918,15 @@ func AttachAudioMixedProcessor(processor AudioCallback) { processor(unsafe.Slice((*float32)(bufferData), frames), int(frames)) return 0 }) + ptr := uintptr(reflect.ValueOf(processor).UnsafePointer()) + audioCallbacks[ptr] = fn attachAudioMixedProcessor(fn) } // DetachAudioMixedProcessor - Detach audio stream processor from the entire audio pipeline func DetachAudioMixedProcessor(processor AudioCallback) { - fn := purego.NewCallback(func(bufferData unsafe.Pointer, frames int32) uintptr { - processor(unsafe.Slice((*float32)(bufferData), frames), int(frames)) - return 0 - }) + ptr := uintptr(reflect.ValueOf(processor).UnsafePointer()) + fn := audioCallbacks[ptr] detachAudioMixedProcessor(fn) } diff --git a/raylib/raymath.h b/raylib/raymath.h index e522113..a531b3a 100644 --- a/raylib/raymath.h +++ b/raylib/raymath.h @@ -32,7 +32,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2015-2024 Ramon Santamaria (@raysan5) +* Copyright (c) 2015-2025 Ramon Santamaria (@raysan5) * * 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. @@ -304,6 +304,14 @@ RMAPI float Vector2DotProduct(Vector2 v1, Vector2 v2) return result; } +// Calculate two vectors cross product +RMAPI float Vector2CrossProduct(Vector2 v1, Vector2 v2) +{ + float result = (v1.x*v2.y - v1.y*v2.x); + + return result; +} + // Calculate distance between two vectors RMAPI float Vector2Distance(Vector2 v1, Vector2 v2) { @@ -320,8 +328,9 @@ RMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2) return result; } -// Calculate angle between two vectors -// NOTE: Angle is calculated from origin point (0, 0) +// Calculate the signed angle from v1 to v2, relative to the origin (0, 0) +// NOTE: Coordinate system convention: positive X right, positive Y down, +// positive angles appear clockwise, and negative angles appear counterclockwise RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) { float result = 0.0f; @@ -1459,19 +1468,35 @@ RMAPI int Vector4Equals(Vector4 p, Vector4 q) RMAPI float MatrixDeterminant(Matrix mat) { float result = 0.0f; - +/* // Cache the matrix values (speed optimization) float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3; float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7; float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11; float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15; + // NOTE: It takes 72 multiplication to calculate 4x4 matrix determinant result = a30*a21*a12*a03 - a20*a31*a12*a03 - a30*a11*a22*a03 + a10*a31*a22*a03 + a20*a11*a32*a03 - a10*a21*a32*a03 - a30*a21*a02*a13 + a20*a31*a02*a13 + a30*a01*a22*a13 - a00*a31*a22*a13 - a20*a01*a32*a13 + a00*a21*a32*a13 + a30*a11*a02*a23 - a10*a31*a02*a23 - a30*a01*a12*a23 + a00*a31*a12*a23 + a10*a01*a32*a23 - a00*a11*a32*a23 - a20*a11*a02*a33 + a10*a21*a02*a33 + 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 + // size from 4x4 to 2x2 using minors + + // Cache the matrix values (speed optimization) + float m0 = mat.m0, m1 = mat.m1, m2 = mat.m2, m3 = mat.m3; + float m4 = mat.m4, m5 = mat.m5, m6 = mat.m6, m7 = mat.m7; + float m8 = mat.m8, m9 = mat.m9, m10 = mat.m10, m11 = mat.m11; + float m12 = mat.m12, m13 = mat.m13, m14 = mat.m14, m15 = mat.m15; + + result = (m0*((m5*(m10*m15 - m11*m14) - m9*(m6*m15 - m7*m14) + m13*(m6*m11 - m7*m10))) - + m4*((m1*(m10*m15 - m11*m14) - m9*(m2*m15 - m3*m14) + m13*(m2*m11 - m3*m10))) + + m8*((m1*(m6*m15 - m7*m14) - m5*(m2*m15 - m3*m14) + m13*(m2*m7 - m3*m6))) - + m12*((m1*(m6*m11 - m7*m10) - m5*(m2*m11 - m3*m10) + m9*(m2*m7 - m3*m6)))); return result; } @@ -2648,7 +2673,7 @@ inline Vector2 operator * (const Vector2& lhs, const Matrix& rhs) return Vector2Transform(lhs, rhs); } -inline const Vector2& operator -= (Vector2& lhs, const Matrix& rhs) +inline const Vector2& operator *= (Vector2& lhs, const Matrix& rhs) { lhs = Vector2Transform(lhs, rhs); return lhs; @@ -2656,12 +2681,12 @@ inline const Vector2& operator -= (Vector2& lhs, const Matrix& rhs) inline Vector2 operator / (const Vector2& lhs, const float& rhs) { - return Vector2Scale(lhs, 1.0f / rhs); + return Vector2Scale(lhs, 1.0f/rhs); } inline const Vector2& operator /= (Vector2& lhs, const float& rhs) { - lhs = Vector2Scale(lhs, rhs); + lhs = Vector2Scale(lhs, 1.0f/rhs); return lhs; } @@ -2742,7 +2767,7 @@ inline Vector3 operator * (const Vector3& lhs, const Matrix& rhs) return Vector3Transform(lhs, rhs); } -inline const Vector3& operator -= (Vector3& lhs, const Matrix& rhs) +inline const Vector3& operator *= (Vector3& lhs, const Matrix& rhs) { lhs = Vector3Transform(lhs, rhs); return lhs; @@ -2750,12 +2775,12 @@ inline const Vector3& operator -= (Vector3& lhs, const Matrix& rhs) inline Vector3 operator / (const Vector3& lhs, const float& rhs) { - return Vector3Scale(lhs, 1.0f / rhs); + return Vector3Scale(lhs, 1.0f/rhs); } inline const Vector3& operator /= (Vector3& lhs, const float& rhs) { - lhs = Vector3Scale(lhs, rhs); + lhs = Vector3Scale(lhs, 1.0f/rhs); return lhs; } @@ -2834,12 +2859,12 @@ inline const Vector4& operator *= (Vector4& lhs, const Vector4& rhs) inline Vector4 operator / (const Vector4& lhs, const float& rhs) { - return Vector4Scale(lhs, 1.0f / rhs); + return Vector4Scale(lhs, 1.0f/rhs); } inline const Vector4& operator /= (Vector4& lhs, const float& rhs) { - lhs = Vector4Scale(lhs, rhs); + lhs = Vector4Scale(lhs, 1.0f/rhs); return lhs; } diff --git a/raylib/rcamera.h b/raylib/rcamera.h index bd2b36e..a598e11 100644 --- a/raylib/rcamera.h +++ b/raylib/rcamera.h @@ -20,7 +20,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2022-2024 Christoph Wagner (@Crydsch) & Ramon Santamaria (@raysan5) +* Copyright (c) 2022-2025 Christoph Wagner (@Crydsch) & Ramon Santamaria (@raysan5) * * 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. @@ -154,7 +154,7 @@ RLAPI void CameraPitch(Camera *camera, float angle, bool lockView, bool rotateAr RLAPI void CameraRoll(Camera *camera, float angle); RLAPI Matrix GetCameraViewMatrix(Camera *camera); -RLAPI Matrix GetCameraProjectionMatrix(Camera* camera, float aspect); +RLAPI Matrix GetCameraProjectionMatrix(Camera *camera, float aspect); #if defined(__cplusplus) } diff --git a/raylib/rcore.c b/raylib/rcore.c index ba23df7..f8b061a 100644 --- a/raylib/rcore.c +++ b/raylib/rcore.c @@ -12,6 +12,13 @@ * - Windows (Win32, Win64) * - Linux (X11/Wayland desktop mode) * - Others (not tested) +* > PLATFORM_DESKTOP_RGFW (RGFW backend): +* - Windows (Win32, Win64) +* - Linux (X11/Wayland desktop mode) +* - macOS/OSX (x64, arm64) +* - Others (not tested) +* > PLATFORM_WEB_RGFW: +* - HTML5 (WebAssembly) * > PLATFORM_WEB: * - HTML5 (WebAssembly) * > PLATFORM_DRM: @@ -63,7 +70,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors +* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) and contributors * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -85,12 +92,12 @@ //---------------------------------------------------------------------------------- // Feature Test Macros required for this module //---------------------------------------------------------------------------------- -#if (defined(__linux__) || defined(PLATFORM_WEB)) && (_XOPEN_SOURCE < 500) +#if (defined(__linux__) || defined(PLATFORM_WEB) || defined(PLATFORM_WEB_RGFW)) && (_XOPEN_SOURCE < 500) #undef _XOPEN_SOURCE #define _XOPEN_SOURCE 500 // Required for: readlink if compiled with c99 without gnu ext. #endif -#if (defined(__linux__) || defined(PLATFORM_WEB)) && (_POSIX_C_SOURCE < 199309L) +#if (defined(__linux__) || defined(PLATFORM_WEB) || defined(PLATFORM_WEB_RGFW)) && (_POSIX_C_SOURCE < 199309L) #undef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext. #endif @@ -158,9 +165,10 @@ #ifndef MAX_PATH #define MAX_PATH 1025 #endif -__declspec(dllimport) unsigned long __stdcall GetModuleFileNameA(void *hModule, void *lpFilename, unsigned long nSize); -__declspec(dllimport) unsigned long __stdcall GetModuleFileNameW(void *hModule, void *lpFilename, unsigned long nSize); -__declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, void *widestr, int cchwide, void *str, int cbmb, void *defchar, int *used_default); +struct HINSTANCE__; +__declspec(dllimport) unsigned long __stdcall GetModuleFileNameA(struct HINSTANCE__ *hModule, char *lpFilename, unsigned long nSize); +__declspec(dllimport) unsigned long __stdcall GetModuleFileNameW(struct HINSTANCE__ *hModule, wchar_t *lpFilename, unsigned long nSize); +__declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); __declspec(dllimport) unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod); __declspec(dllimport) unsigned int __stdcall timeEndPeriod(unsigned int uPeriod); #elif defined(__linux__) @@ -226,8 +234,11 @@ __declspec(dllimport) unsigned int __stdcall timeEndPeriod(unsigned int uPeriod) #ifndef MAX_GAMEPADS #define MAX_GAMEPADS 4 // Maximum number of gamepads supported #endif -#ifndef MAX_GAMEPAD_AXIS - #define MAX_GAMEPAD_AXIS 8 // Maximum number of axis supported (per gamepad) +#ifndef MAX_GAMEPAD_NAME_LENGTH + #define MAX_GAMEPAD_NAME_LENGTH 128 // Maximum number of characters in a gamepad name (byte size) +#endif +#ifndef MAX_GAMEPAD_AXES + #define MAX_GAMEPAD_AXES 8 // Maximum number of axes supported (per gamepad) #endif #ifndef MAX_GAMEPAD_BUTTONS #define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad) @@ -343,12 +354,12 @@ typedef struct CoreData { } Touch; struct { int lastButtonPressed; // Register last gamepad button pressed - int axisCount[MAX_GAMEPADS]; // Register number of available gamepad axis + int axisCount[MAX_GAMEPADS]; // Register number of available gamepad axes bool ready[MAX_GAMEPADS]; // Flag to know if gamepad is ready - char name[MAX_GAMEPADS][64]; // Gamepad name holder + char name[MAX_GAMEPADS][MAX_GAMEPAD_NAME_LENGTH]; // Gamepad name holder char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state - float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state + float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXES]; // Gamepad axes state } Gamepad; } Input; @@ -501,7 +512,7 @@ static void RecordAutomationEvent(void); // Record frame events (to internal eve #if defined(_WIN32) && !defined(PLATFORM_DESKTOP_RGFW) // NOTE: We declare Sleep() function symbol to avoid including windows.h (kernel32.lib linkage required) -void __stdcall Sleep(unsigned long msTimeout); // Required for: WaitTime() +__declspec(dllimport) void __stdcall Sleep(unsigned long msTimeout); // Required for: WaitTime() #endif #if !defined(SUPPORT_MODULE_RTEXT) @@ -512,25 +523,25 @@ const char *TextFormat(const char *text, ...); // Formatting of tex #define PLATFORM_DESKTOP_GLFW #endif -// We're using `#pragma message` because `#warning` is not adopted by MSVC. +// We're 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") + #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") + #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. + // 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") + #pragma message ("WARNING: Getting image from the clipboard might not work without SUPPORT_FILEFORMAT_PNG or SUPPORT_FILEFORMAT_JPG") #endif - // Not needed because `rtexture.c` will automatically defined STBI_REQUIRED when any SUPPORT_FILEFORMAT_* is defined. + // Not needed because `rtexture.c` will automatically defined STBI_REQUIRED when any SUPPORT_FILEFORMAT_* is defined // #if !defined(STBI_REQUIRED) - // #pragma message ("Warning: "STBI_REQUIRED is not defined, that means we can't load images from clipbard" + // #pragma message ("WARNING: "STBI_REQUIRED is not defined, that means we can't load images from clipbard" // #endif #endif // SUPPORT_CLIPBOARD_IMAGE @@ -540,7 +551,7 @@ const char *TextFormat(const char *text, ...); // Formatting of tex #include "platforms/rcore_desktop_glfw.c" #elif defined(PLATFORM_DESKTOP_SDL) #include "platforms/rcore_desktop_sdl.c" -#elif defined(PLATFORM_DESKTOP_RGFW) +#elif (defined(PLATFORM_DESKTOP_RGFW) || defined(PLATFORM_WEB_RGFW)) #include "platforms/rcore_desktop_rgfw.c" #elif defined(PLATFORM_WEB) #include "platforms/rcore_web.c" @@ -611,6 +622,8 @@ void InitWindow(int width, int height, const char *title) TRACELOG(LOG_INFO, "Platform backend: DESKTOP (SDL)"); #elif defined(PLATFORM_DESKTOP_RGFW) TRACELOG(LOG_INFO, "Platform backend: DESKTOP (RGFW)"); +#elif defined(PLATFORM_WEB_RGFW) + TRACELOG(LOG_INFO, "Platform backend: WEB (RGFW) (HTML5)"); #elif defined(PLATFORM_WEB) TRACELOG(LOG_INFO, "Platform backend: WEB (HTML5)"); #elif defined(PLATFORM_DRM) @@ -668,7 +681,13 @@ void InitWindow(int width, int height, const char *title) // Initialize platform //-------------------------------------------------------------- - InitPlatform(); + int result = InitPlatform(); + + if (result != 0) + { + TRACELOG(LOG_WARNING, "SYSTEM: Failed to initialize Platform"); + return; + } //-------------------------------------------------------------- // Initialize rlgl default data (buffers and shaders) @@ -1100,7 +1119,7 @@ void BeginTextureMode(RenderTexture2D target) //rlScalef(0.0f, -1.0f, 0.0f); // Flip Y-drawing (?) // Setup current width/height for proper aspect ratio - // calculation when using BeginMode3D() + // calculation when using BeginTextureMode() CORE.Window.currentFbo.width = target.texture.width; CORE.Window.currentFbo.height = target.texture.height; CORE.Window.usingFbo = true; @@ -1302,6 +1321,8 @@ Shader LoadShader(const char *vsFileName, const char *fsFileName) if (vsFileName != NULL) vShaderStr = LoadFileText(vsFileName); if (fsFileName != NULL) fShaderStr = LoadFileText(fsFileName); + if ((vShaderStr == NULL) && (fShaderStr == NULL)) TraceLog(LOG_WARNING, "SHADER: Shader files provided are not valid, using default shader"); + shader = LoadShaderFromMemory(vShaderStr, fShaderStr); UnloadFileText(vShaderStr); @@ -1317,9 +1338,10 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode) shader.id = rlLoadShaderCode(vsCode, fsCode); - // After shader loading, we TRY to set default location names - if (shader.id > 0) + if (shader.id == rlGetShaderIdDefault()) shader.locs = rlGetShaderLocsDefault(); + else if (shader.id > 0) { + // After custom shader loading, we TRY to set default location names // Default shader attribute locations have been binded before linking: // vertex position location = 0 // vertex texcoord location = 1 @@ -1346,6 +1368,7 @@ Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode) 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_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); // Get handles to GLSL uniform locations (vertex shader) shader.locs[SHADER_LOC_MATRIX_MVP] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MVP); @@ -1856,12 +1879,15 @@ void TakeScreenshot(const char *fileName) // Security check to (partially) avoid malicious code if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } - Vector2 scale = GetWindowScaleDPI(); + // Apply a scale if we are doing HIGHDPI auto-scaling + Vector2 scale = { 1.0f, 1.0f }; + if (IsWindowState(FLAG_WINDOW_HIGHDPI)) scale = GetWindowScaleDPI(); + unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; char path[512] = { 0 }; - strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, GetFileName(fileName))); + strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); ExportImage(image, path); // WARNING: Module required: rtextures RL_FREE(imgData); @@ -1879,6 +1905,8 @@ void TakeScreenshot(const char *fileName) // To configure window states after creation, just 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"); + // Selected flags are set but not evaluated at this point, // flag evaluation happens at InitWindow() or SetWindowState() CORE.Window.flags |= flags; @@ -1920,7 +1948,7 @@ bool IsFileExtension(const char *fileName, const char *ext) { #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_TEXT_MANIPULATION) int extCount = 0; - const char **checkExts = TextSplit(ext, ';', &extCount); // WARNING: Module required: rtext + char **checkExts = TextSplit(ext, ';', &extCount); // WARNING: Module required: rtext char fileExtLower[MAX_FILE_EXTENSION_LENGTH + 1] = { 0 }; strncpy(fileExtLower, TextToLower(fileExt), MAX_FILE_EXTENSION_LENGTH); // WARNING: Module required: rtext @@ -2062,7 +2090,7 @@ const char *GetDirectoryPath(const char *filePath) // 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 - if (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/') + if ((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/')) { // For security, we set starting path to current directory, // obtained path will be concatenated to this @@ -2286,9 +2314,12 @@ FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool // WARNING: files.count is not reseted to 0 after unloading void UnloadDirectoryFiles(FilePathList files) { - for (unsigned int i = 0; i < files.capacity; i++) RL_FREE(files.paths[i]); + if (files.paths != NULL) + { + for (unsigned int i = 0; i < files.capacity; i++) RL_FREE(files.paths[i]); - RL_FREE(files.paths); + RL_FREE(files.paths); + } } // Create directories (including full path requested), returns 0 on success @@ -2331,6 +2362,7 @@ bool ChangeDirectory(const char *dir) bool result = CHDIR(dir); if (result != 0) TRACELOG(LOG_WARNING, "SYSTEM: Failed to change to directory: %s", dir); + else TRACELOG(LOG_INFO, "SYSTEM: Working Directory: %s", dir); return (result == 0); } @@ -2468,7 +2500,7 @@ unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDa #if defined(SUPPORT_COMPRESSION_API) // Compress data and generate a valid DEFLATE stream - struct sdefl *sdefl = RL_CALLOC(1, sizeof(struct sdefl)); // WARNING: Possible stack overflow, struct sdefl is almost 1MB + 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); compData = (unsigned char *)RL_CALLOC(bounds, 1); @@ -2507,96 +2539,112 @@ unsigned char *DecompressData(const unsigned char *compData, int compDataSize, i } // Encode data to Base64 string +// NOTE: Returned string includes NULL terminator, considered on outputSize char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize) { - static const unsigned char base64encodeTable[] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' - }; + // Base64 conversion table from RFC 4648 [0..63] + // NOTE: They represent 64 values (6 bits), to encode 3 bytes of data into 4 "sixtets" (6bit characters) + static const char base64EncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - static const int modTable[] = { 0, 2, 1 }; + // Compute expected size and padding + int paddedSize = dataSize; + while (paddedSize%3 != 0) paddedSize++; // Padding bytes to round 4*(dataSize/3) to 4 bytes + int estimatedOutputSize = 4*(paddedSize/3); + int padding = paddedSize - dataSize; - *outputSize = 4*((dataSize + 2)/3); + // Adding null terminator to string + estimatedOutputSize += 1; - char *encodedData = (char *)RL_MALLOC(*outputSize); + // Load some memory to store encoded string + char *encodedData = (char *)RL_CALLOC(estimatedOutputSize, 1); + if (encodedData == NULL) return NULL; - if (encodedData == NULL) return NULL; // Security check - - for (int i = 0, j = 0; i < dataSize;) + int outputCount = 0; + for (int i = 0; i < dataSize;) { - unsigned int octetA = (i < dataSize)? (unsigned char)data[i++] : 0; - unsigned int octetB = (i < dataSize)? (unsigned char)data[i++] : 0; - unsigned int octetC = (i < dataSize)? (unsigned char)data[i++] : 0; + unsigned int octetA = 0; + unsigned int octetB = 0; + unsigned int octetC = 0; + unsigned int octetPack = 0; - unsigned int triple = (octetA << 0x10) + (octetB << 0x08) + octetC; + octetA = data[i]; // Generates 2 sextets + octetB = ((i + 1) < dataSize)? data[i + 1] : 0; // Generates 3 sextets + octetC = ((i + 2) < dataSize)? data[i + 2] : 0; // Generates 4 sextets - encodedData[j++] = base64encodeTable[(triple >> 3*6) & 0x3F]; - encodedData[j++] = base64encodeTable[(triple >> 2*6) & 0x3F]; - encodedData[j++] = base64encodeTable[(triple >> 1*6) & 0x3F]; - encodedData[j++] = base64encodeTable[(triple >> 0*6) & 0x3F]; + octetPack = (octetA << 16) | (octetB << 8) | octetC; + + encodedData[outputCount + 0] = (unsigned char)(base64EncodeTable[(octetPack >> 18) & 0x3f]); + encodedData[outputCount + 1] = (unsigned char)(base64EncodeTable[(octetPack >> 12) & 0x3f]); + encodedData[outputCount + 2] = (unsigned char)(base64EncodeTable[(octetPack >> 6) & 0x3f]); + encodedData[outputCount + 3] = (unsigned char)(base64EncodeTable[octetPack & 0x3f]); + outputCount += 4; + i += 3; } - for (int i = 0; i < modTable[dataSize%3]; i++) encodedData[*outputSize - 1 - i] = '='; // Padding character + // Add required padding bytes + for (int p = 0; p < padding; p++) encodedData[outputCount - p - 1] = '='; + // Add null terminator to string + encodedData[outputCount] = '\0'; + outputCount++; + + if (outputCount != estimatedOutputSize) TRACELOG(LOG_WARNING, "BASE64: Output size differs from estimation"); + + *outputSize = estimatedOutputSize; return encodedData; } -// Decode Base64 string data -unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize) +// Decode Base64 string (expected NULL terminated) +unsigned char *DecodeDataBase64(const char *text, int *outputSize) { - static const unsigned char base64decodeTable[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 + // Base64 decode table + // NOTE: Following ASCII order [0..255] assigning the expected sixtet value to + // every character in the corresponding ASCII position + static const unsigned char base64DecodeTable[256] = { + ['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, ['E'] = 4, ['F'] = 5, ['G'] = 6, ['H'] = 7, + ['I'] = 8, ['J'] = 9, ['K'] = 10, ['L'] = 11, ['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15, + ['Q'] = 16, ['R'] = 17, ['S'] = 18, ['T'] = 19, ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23, ['Y'] = 24, ['Z'] = 25, + ['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29, ['e'] = 30, ['f'] = 31, ['g'] = 32, ['h'] = 33, + ['i'] = 34, ['j'] = 35, ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39, ['o'] = 40, ['p'] = 41, + ['q'] = 42, ['r'] = 43, ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47, ['w'] = 48, ['x'] = 49, ['y'] = 50, ['z'] = 51, + ['0'] = 52, ['1'] = 53, ['2'] = 54, ['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59, + ['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63 }; - // Get output size of Base64 input data - int outSize = 0; - for (int i = 0; data[4*i] != 0; i++) + // Compute expected size and padding + int dataSize = (int)strlen(text); // WARNING: Expecting NULL terminated strings! + int ending = dataSize - 1; + int padding = 0; + while (text[ending] == '=') { padding++; ending--; } + int estimatedOutputSize = 3*(dataSize/4) - padding; + int maxOutputSize = 3*(dataSize/4); + + // Load some memory to store decoded data + // NOTE: Allocated enough size to include padding + unsigned char *decodedData = (unsigned char *)RL_CALLOC(maxOutputSize, 1); + if (decodedData == NULL) return NULL; + + int outputCount = 0; + for (int i = 0; i < dataSize;) { - if (data[4*i + 3] == '=') - { - if (data[4*i + 2] == '=') outSize += 1; - else outSize += 2; - } - else outSize += 3; + // Every 4 sixtets must generate 3 octets + unsigned int sixtetA = base64DecodeTable[(unsigned char)text[i]]; + unsigned int sixtetB = base64DecodeTable[(unsigned char)text[i + 1]]; + unsigned int sixtetC = ((unsigned char)text[i + 2] != '=')? base64DecodeTable[(unsigned char)text[i + 2]] : 0; + unsigned int sixtetD = ((unsigned char)text[i + 3] != '=')? base64DecodeTable[(unsigned char)text[i + 3]] : 0; + + unsigned int octetPack = (sixtetA << 18) | (sixtetB << 12) | (sixtetC << 6) | sixtetD; + + decodedData[outputCount + 0] = (octetPack >> 16) & 0xff; + decodedData[outputCount + 1] = (octetPack >> 8) & 0xff; + decodedData[outputCount + 2] = octetPack & 0xff; + outputCount += 3; + i += 4; } - // Allocate memory to store decoded Base64 data - unsigned char *decodedData = (unsigned char *)RL_MALLOC(outSize); + if (estimatedOutputSize != (outputCount - padding)) TRACELOG(LOG_WARNING, "BASE64: Decoded size differs from estimation"); - for (int i = 0; i < outSize/3; i++) - { - unsigned char a = base64decodeTable[(int)data[4*i]]; - unsigned char b = base64decodeTable[(int)data[4*i + 1]]; - unsigned char c = base64decodeTable[(int)data[4*i + 2]]; - unsigned char d = base64decodeTable[(int)data[4*i + 3]]; - - decodedData[3*i] = (a << 2) | (b >> 4); - decodedData[3*i + 1] = (b << 4) | (c >> 2); - decodedData[3*i + 2] = (c << 6) | d; - } - - if (outSize%3 == 1) - { - int n = outSize/3; - unsigned char a = base64decodeTable[(int)data[4*n]]; - unsigned char b = base64decodeTable[(int)data[4*n + 1]]; - decodedData[outSize - 1] = (a << 2) | (b >> 4); - } - else if (outSize%3 == 2) - { - int n = outSize/3; - unsigned char a = base64decodeTable[(int)data[4*n]]; - unsigned char b = base64decodeTable[(int)data[4*n + 1]]; - unsigned char c = base64decodeTable[(int)data[4*n + 2]]; - decodedData[outSize - 2] = (a << 2) | (b >> 4); - decodedData[outSize - 1] = (b << 4) | (c >> 2); - } - - *outputSize = outSize; + *outputSize = estimatedOutputSize; return decodedData; } @@ -2604,38 +2652,38 @@ unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize) unsigned int ComputeCRC32(unsigned char *data, int dataSize) { static unsigned int crcTable[256] = { - 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, - 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, - 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, - 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, - 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, - 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, - 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, - 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, - 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, - 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, - 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, - 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, - 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, - 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, - 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, - 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, - 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, - 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, - 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, - 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, - 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, - 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, - 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, - 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, - 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, - 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, - 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, - 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, - 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, - 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, - 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, - 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; unsigned int crc = ~0u; @@ -2699,7 +2747,7 @@ unsigned int *ComputeMD5(unsigned char *data, int dataSize) int newDataSize = ((((dataSize + 8)/64) + 1)*64) - 8; - unsigned char *msg = RL_CALLOC(newDataSize + 64, 1); // Initialize with '0' bits, allocating 64 extra bytes + unsigned char *msg = (unsigned char *)RL_CALLOC(newDataSize + 64, 1); // Initialize with '0' bits, allocating 64 extra bytes memcpy(msg, data, dataSize); msg[dataSize] = 128; // Write the '1' bit @@ -2765,7 +2813,8 @@ unsigned int *ComputeMD5(unsigned char *data, int dataSize) // Compute SHA-1 hash code // NOTE: Returns a static int[5] array (20 bytes) -unsigned int *ComputeSHA1(unsigned char *data, int dataSize) { +unsigned int *ComputeSHA1(unsigned char *data, int dataSize) +{ #define ROTATE_LEFT(x, c) (((x) << (c)) | ((x) >> (32 - (c)))) static unsigned int hash[5] = { 0 }; // Hash to be returned @@ -2788,7 +2837,7 @@ unsigned int *ComputeSHA1(unsigned char *data, int dataSize) { int newDataSize = ((((dataSize + 8)/64) + 1)*64); - unsigned char *msg = RL_CALLOC(newDataSize, 1); // Initialize with '0' bits + unsigned char *msg = (unsigned char *)RL_CALLOC(newDataSize, 1); // Initialize with '0' bits memcpy(msg, data, dataSize); msg[dataSize] = 128; // Write the '1' bit @@ -2800,17 +2849,16 @@ unsigned int *ComputeSHA1(unsigned char *data, int dataSize) { { // Break chunk into sixteen 32-bit words w[j], 0 <= j <= 15 unsigned int w[80] = {0}; - for (int i = 0; i < 16; i++) { - w[i] = (msg[offset + (i * 4) + 0] << 24) | - (msg[offset + (i * 4) + 1] << 16) | - (msg[offset + (i * 4) + 2] << 8) | - (msg[offset + (i * 4) + 3]); + for (int i = 0; i < 16; i++) + { + w[i] = (msg[offset + (i*4) + 0] << 24) | + (msg[offset + (i*4) + 1] << 16) | + (msg[offset + (i*4) + 2] << 8) | + (msg[offset + (i*4) + 3]); } // Message schedule: extend the sixteen 32-bit words into eighty 32-bit words: - for (int i = 16; i < 80; ++i) { - w[i] = ROTATE_LEFT(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1); - } + for (int i = 16; i < 80; i++) w[i] = ROTATE_LEFT(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1); // Initialize hash value for this chunk unsigned int a = hash[0]; @@ -2824,16 +2872,23 @@ unsigned int *ComputeSHA1(unsigned char *data, int dataSize) { unsigned int f = 0; unsigned int k = 0; - if (i < 20) { + if (i < 20) + { f = (b & c) | ((~b) & d); k = 0x5A827999; - } else if (i < 40) { + } + else if (i < 40) + { f = b ^ c ^ d; k = 0x6ED9EBA1; - } else if (i < 60) { + } + else if (i < 60) + { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC; - } else { + } + else + { f = b ^ c ^ d; k = 0xCA62C1D6; } @@ -2982,7 +3037,7 @@ bool ExportAutomationEventList(AutomationEventList list, const char *fileName) byteCount += sprintf(txtData + byteCount, "# more info and bugs-report: github.com/raysan5/raylib\n"); byteCount += sprintf(txtData + byteCount, "# feedback and support: ray[at]raylib.com\n"); byteCount += sprintf(txtData + byteCount, "#\n"); - byteCount += sprintf(txtData + byteCount, "# Copyright (c) 2023-2024 Ramon Santamaria (@raysan5)\n"); + byteCount += sprintf(txtData + byteCount, "# Copyright (c) 2023-2025 Ramon Santamaria (@raysan5)\n"); byteCount += sprintf(txtData + byteCount, "#\n\n"); // Add events data @@ -3313,10 +3368,11 @@ int GetGamepadAxisCount(int gamepad) // Get axis movement vector for a gamepad float GetGamepadAxisMovement(int gamepad, int axis) { - float value = (axis == GAMEPAD_AXIS_LEFT_TRIGGER || axis == GAMEPAD_AXIS_RIGHT_TRIGGER)? -1.0f : 0.0f; + float value = ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER))? -1.0f : 0.0f; - if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXIS)) { - float movement = value < 0.0f ? CORE.Input.Gamepad.axisState[gamepad][axis] : fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]); + if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXES)) + { + float movement = (value < 0.0f)? CORE.Input.Gamepad.axisState[gamepad][axis] : fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]); if (movement > value) value = CORE.Input.Gamepad.axisState[gamepad][axis]; } @@ -3657,12 +3713,16 @@ static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const (strcmp(dp->d_name, "..") != 0)) { #if defined(_WIN32) - sprintf(path, "%s\\%s", basePath, dp->d_name); + int pathLength = snprintf(path, MAX_FILEPATH_LENGTH - 1, "%s\\%s", basePath, dp->d_name); #else - sprintf(path, "%s/%s", basePath, dp->d_name); + int pathLength = snprintf(path, MAX_FILEPATH_LENGTH - 1, "%s/%s", basePath, dp->d_name); #endif - if (filter != NULL) + if ((pathLength < 0) || (pathLength >= MAX_FILEPATH_LENGTH)) + { + TRACELOG(LOG_WARNING, "FILEIO: Path longer than %d characters (%s...)", MAX_FILEPATH_LENGTH, basePath); + } + else if (filter != NULL) { if (IsPathFile(path)) { @@ -3674,7 +3734,7 @@ static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const } else { - if (TextFindIndex(filter, DIRECTORY_FILTER_TAG) >= 0) + if (strstr(filter, DIRECTORY_FILTER_TAG) != NULL) { strcpy(files->paths[files->count], path); files->count++; @@ -3697,7 +3757,7 @@ static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const // Scan all files and directories recursively from a base path static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *files, const char *filter) { - char path[MAX_FILEPATH_LENGTH] = { 0 }; + static char path[MAX_FILEPATH_LENGTH] = { 0 }; memset(path, 0, MAX_FILEPATH_LENGTH); struct dirent *dp = NULL; @@ -3711,12 +3771,16 @@ static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *fi { // Construct new path from our base path #if defined(_WIN32) - sprintf(path, "%s\\%s", basePath, dp->d_name); + int pathLength = snprintf(path, MAX_FILEPATH_LENGTH - 1, "%s\\%s", basePath, dp->d_name); #else - sprintf(path, "%s/%s", basePath, dp->d_name); + int pathLength = snprintf(path, MAX_FILEPATH_LENGTH - 1, "%s/%s", basePath, dp->d_name); #endif - if (IsPathFile(path)) + if ((pathLength < 0) || (pathLength >= MAX_FILEPATH_LENGTH)) + { + TRACELOG(LOG_WARNING, "FILEIO: Path longer than %d characters (%s...)", MAX_FILEPATH_LENGTH, basePath); + } + else if (IsPathFile(path)) { if (filter != NULL) { @@ -3740,7 +3804,7 @@ static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *fi } else { - if ((filter != NULL) && (TextFindIndex(filter, DIRECTORY_FILTER_TAG) >= 0)) + if ((filter != NULL) && (strstr(filter, DIRECTORY_FILTER_TAG) != NULL)) { strcpy(files->paths[files->count], path); files->count++; @@ -3986,10 +4050,10 @@ static void RecordAutomationEvent(void) if (currentEventList->count == currentEventList->capacity) return; // Security check } - for (int axis = 0; axis < MAX_GAMEPAD_AXIS; axis++) + for (int axis = 0; axis < MAX_GAMEPAD_AXES; axis++) { // Event type: INPUT_GAMEPAD_AXIS_MOTION - float defaultMovement = (axis == GAMEPAD_AXIS_LEFT_TRIGGER || axis == GAMEPAD_AXIS_RIGHT_TRIGGER)? -1.0f : 0.0f; + float defaultMovement = ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER))? -1.0f : 0.0f; if (GetGamepadAxisMovement(gamepad, axis) != defaultMovement) { currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; diff --git a/raylib/rgestures.h b/raylib/rgestures.h index b5624be..1bf4e05 100644 --- a/raylib/rgestures.h +++ b/raylib/rgestures.h @@ -21,7 +21,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2024 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2025 Ramon Santamaria (@raysan5) * * 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. diff --git a/raylib/rlgl.go b/raylib/rlgl.go index eeb10e1..cc24f30 100644 --- a/raylib/rlgl.go +++ b/raylib/rlgl.go @@ -50,6 +50,41 @@ const ( FragmentShader = 0x8B30 // GL_FRAGMENT_SHADER VertexShader = 0x8B31 // GL_VERTEX_SHADER ComputeShader = 0x91B9 // GL_COMPUTE_SHADER + + // GL blending factors + Zero = 0 // GL_ZERO + One = 1 // GL_ONE + SrcColor = 0x0300 // GL_SRC_COLOR + OneMinusSrcColor = 0x0301 // GL_ONE_MINUS_SRC_COLOR + SrcAlpha = 0x0302 // GL_SRC_ALPHA + OneMinusSrcAlpha = 0x0303 // GL_ONE_MINUS_SRC_ALPHA + DstAlpha = 0x0304 // GL_DST_ALPHA + OneMinusDstAlpha = 0x0305 // GL_ONE_MINUS_DST_ALPHA + DstColor = 0x0306 // GL_DST_COLOR + OneMinusDstColor = 0x0307 // GL_ONE_MINUS_DST_COLOR + SrcAlphaSaturate = 0x0308 // GL_SRC_ALPHA_SATURATE + ConstantColor = 0x8001 // GL_CONSTANT_COLOR + OneMinusConstantColor = 0x8002 // GL_ONE_MINUS_CONSTANT_COLOR + ConstantAlpha = 0x8003 // GL_CONSTANT_ALPHA + OneMinusConstantAlpha = 0x8004 // GL_ONE_MINUS_CONSTANT_ALPHA + + // GL blending functions/equations + FuncAdd = 0x8006 // GL_FUNC_ADD + Min = 0x8007 // GL_MIN + Max = 0x8008 // GL_MAX + FuncSubtract = 0x800A // GL_FUNC_SUBTRACT + FuncReverseSubtract = 0x800B // GL_FUNC_REVERSE_SUBTRACT + BlendEquation = 0x8009 // GL_BLEND_EQUATION + BlendEquationRgb = BlendEquation // GL_BLEND_EQUATION_RGB (Same as BLEND_EQUATION) + BlendEquationAlpha = 0x883D // GL_BLEND_EQUATION_ALPHA + BlendDstRgb = 0x80C8 // GL_BLEND_DST_RGB + BlendSrcRgb = 0x80C9 // GL_BLEND_SRC_RGB + BlendDstAlpha = 0x80CA // GL_BLEND_DST_ALPHA + BlendSrcAlpha = 0x80CB // GL_BLEND_SRC_ALPHA + BlendColor = 0x8005 // GL_BLEND_COLOR + + ReadFramebuffer = 0x8CA8 // GL_READ_FRAMEBUFFER + DrawFramebuffer = 0x8CA9 // GL_DRAW_FRAMEBUFFER ) // VertexBuffer - Dynamic vertex buffers (position + texcoords + colors + indices arrays) diff --git a/raylib/rlgl.h b/raylib/rlgl.h index 756656e..c39d096 100644 --- a/raylib/rlgl.h +++ b/raylib/rlgl.h @@ -3,14 +3,14 @@ * rlgl v5.0 - A multi-OpenGL abstraction layer with an immediate-mode style API * * DESCRIPTION: -* An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0) +* An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0, ES 3.0) * that provides a pseudo-OpenGL 1.1 immediate-mode style API (rlVertex, rlTranslate, rlRotate...) * * ADDITIONAL NOTES: * When choosing an OpenGL backend different than OpenGL 1.1, some internal buffer are * initialized on rlglInit() to accumulate vertex data * -* When an internal state change is required all the stored vertex data is renderer in batch, +* When an internal state change is required all the stored vertex data is rendered in batch, * additionally, rlDrawRenderBatchActive() could be called to force flushing of the batch * * Some resources are also loaded for convenience, here the complete list: @@ -56,8 +56,8 @@ * * #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.01 // Default projection matrix near cull distance -* #define RL_CULL_DISTANCE_FAR 1000.0 // Default projection matrix far cull distance +* #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 * * When loading a shader, the following vertex attributes and uniform * location names are tried to be set automatically: @@ -88,7 +88,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2024 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2025 Ramon Santamaria (@raysan5) * * 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. @@ -234,10 +234,10 @@ // Projection matrix culling #ifndef RL_CULL_DISTANCE_NEAR - #define RL_CULL_DISTANCE_NEAR 0.01 // Default near cull distance + #define RL_CULL_DISTANCE_NEAR 0.05 // Default near cull distance #endif #ifndef RL_CULL_DISTANCE_FAR - #define RL_CULL_DISTANCE_FAR 1000.0 // Default far cull distance + #define RL_CULL_DISTANCE_FAR 4000.0 // Default far cull distance #endif // Texture parameters (equivalent to OpenGL defines) @@ -355,6 +355,9 @@ #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 +#endif //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -681,9 +684,10 @@ RLAPI void rlSetCullFace(int mode); // Set face culling mode RLAPI void rlEnableScissorTest(void); // Enable scissor test RLAPI void rlDisableScissorTest(void); // Disable scissor test RLAPI void rlScissor(int x, int y, int width, int height); // Scissor test -RLAPI void rlEnableWireMode(void); // Enable wire mode RLAPI void rlEnablePointMode(void); // Enable point mode -RLAPI void rlDisableWireMode(void); // Disable wire (and point) mode +RLAPI void rlDisablePointMode(void); // Disable point mode +RLAPI void rlEnableWireMode(void); // Enable wire mode +RLAPI void rlDisableWireMode(void); // Disable wire mode RLAPI void rlSetLineWidth(float width); // Set the line drawing width RLAPI float rlGetLineWidth(void); // Get the line drawing width RLAPI void rlEnableSmoothLines(void); // Enable line aliasing @@ -998,6 +1002,9 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #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 #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 +#endif #ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_MVP #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix @@ -1741,7 +1748,6 @@ void rlTextureParameters(unsigned int id, int param, int value) #endif } else glTexParameteri(GL_TEXTURE_2D, param, value); - } break; case RL_TEXTURE_MAG_FILTER: case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_2D, param, value); break; @@ -1786,7 +1792,6 @@ void rlCubemapParameters(unsigned int id, int param, int value) else TRACELOG(RL_LOG_WARNING, "GL: Clamp mirror wrap mode not supported (GL_MIRROR_CLAMP_EXT)"); } else glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value); - } break; case RL_TEXTURE_MAG_FILTER: case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value); break; @@ -1884,16 +1889,6 @@ void rlActiveDrawBuffers(int count) else { unsigned int buffers[8] = { -#if defined(GRAPHICS_API_OPENGL_ES3) - GL_COLOR_ATTACHMENT0_EXT, - GL_COLOR_ATTACHMENT1_EXT, - GL_COLOR_ATTACHMENT2_EXT, - GL_COLOR_ATTACHMENT3_EXT, - GL_COLOR_ATTACHMENT4_EXT, - GL_COLOR_ATTACHMENT5_EXT, - GL_COLOR_ATTACHMENT6_EXT, - GL_COLOR_ATTACHMENT7_EXT, -#else GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, @@ -1902,14 +1897,9 @@ void rlActiveDrawBuffers(int count) GL_COLOR_ATTACHMENT5, GL_COLOR_ATTACHMENT6, GL_COLOR_ATTACHMENT7, -#endif }; -#if defined(GRAPHICS_API_OPENGL_ES3) - glDrawBuffersEXT(count, buffers); -#else glDrawBuffers(count, buffers); -#endif } } else TRACELOG(LOG_WARNING, "GL: One color buffer active by default"); @@ -1976,6 +1966,15 @@ void rlEnableWireMode(void) #endif } +// Disable wire mode +void rlDisableWireMode(void) +{ +#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + // NOTE: glPolygonMode() not available on OpenGL ES + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +#endif +} + // Enable point mode void rlEnablePointMode(void) { @@ -1986,8 +1985,8 @@ void rlEnablePointMode(void) #endif } -// Disable wire mode -void rlDisableWireMode(void) +// Disable point mode +void rlDisablePointMode(void) { #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) // NOTE: glPolygonMode() not available on OpenGL ES @@ -2111,14 +2110,12 @@ void rlSetBlendMode(int mode) { // NOTE: Using GL blend src/dst factors and GL equation configured with rlSetBlendFactors() glBlendFunc(RLGL.State.glBlendSrcFactor, RLGL.State.glBlendDstFactor); glBlendEquation(RLGL.State.glBlendEquation); - } break; case RL_BLEND_CUSTOM_SEPARATE: { // NOTE: Using GL blend src/dst factors and GL equation configured with rlSetBlendFactorsSeparate() glBlendFuncSeparate(RLGL.State.glBlendSrcFactorRGB, RLGL.State.glBlendDestFactorRGB, RLGL.State.glBlendSrcFactorAlpha, RLGL.State.glBlendDestFactorAlpha); glBlendEquationSeparate(RLGL.State.glBlendEquationRGB, RLGL.State.glBlendEquationAlpha); - } break; default: break; } @@ -2428,7 +2425,7 @@ void rlLoadExtensions(void *loader) // Get supported extensions list GLint numExt = 0; - const char **extList = RL_MALLOC(512*sizeof(const char *)); // Allocate 512 strings pointers (2 KB) + const char **extList = (const char **)RL_MALLOC(512*sizeof(const char *)); // Allocate 512 strings pointers (2 KB) const char *extensions = (const char *)glGetString(GL_EXTENSIONS); // One big const string // NOTE: We have to duplicate string because glGetString() returns a const string @@ -2525,11 +2522,11 @@ void rlLoadExtensions(void *loader) // Check depth texture support if (strcmp(extList[i], (const char *)"GL_OES_depth_texture") == 0) RLGL.ExtSupported.texDepth = true; - if (strcmp(extList[i], (const char *)"GL_WEBGL_depth_texture") == 0) RLGL.ExtSupported.texDepthWebGL = true; // WebGL requires unsized internal format + if (strcmp(extList[i], (const char *)"GL_WEBGL_depth_texture") == 0) RLGL.ExtSupported.texDepthWebGL = true; // WebGL requires unsized internal format if (RLGL.ExtSupported.texDepthWebGL) RLGL.ExtSupported.texDepth = true; - if (strcmp(extList[i], (const char *)"GL_OES_depth24") == 0) RLGL.ExtSupported.maxDepthBits = 24; // Not available on WebGL - if (strcmp(extList[i], (const char *)"GL_OES_depth32") == 0) RLGL.ExtSupported.maxDepthBits = 32; // Not available on WebGL + if (strcmp(extList[i], (const char *)"GL_OES_depth24") == 0) RLGL.ExtSupported.maxDepthBits = 24; // Not available on WebGL + if (strcmp(extList[i], (const char *)"GL_OES_depth32") == 0) RLGL.ExtSupported.maxDepthBits = 32; // Not available on WebGL // Check texture compression support: DXT if ((strcmp(extList[i], (const char *)"GL_EXT_texture_compression_s3tc") == 0) || @@ -3054,7 +3051,7 @@ void rlDrawRenderBatch(rlRenderBatch *batch) for (int i = 0, vertexOffset = 0; i < batch->drawCounter; i++) { - // Bind current draw call texture, activated as GL_TEXTURE0 and Bound to sampler2D texture0 by default + // Bind current draw call texture, activated as GL_TEXTURE0 and bound to sampler2D texture0 by default glBindTexture(GL_TEXTURE_2D, batch->draws[i].textureId); if ((batch->draws[i].mode == RL_LINES) || (batch->draws[i].mode == RL_TRIANGLES)) glDrawArrays(batch->draws[i].mode, vertexOffset, batch->draws[i].vertexCount); @@ -3310,6 +3307,7 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, // Activate Trilinear filtering if mipmaps are available glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipmapCount); // Required for user-defined mip count } #endif @@ -3417,9 +3415,9 @@ unsigned int rlLoadTextureCubemap(const void *data, int size, int format, int mi { if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) { - if ((format == RL_PIXELFORMAT_UNCOMPRESSED_R32) || + if ((format == RL_PIXELFORMAT_UNCOMPRESSED_R32) || (format == RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32) || - (format == RL_PIXELFORMAT_UNCOMPRESSED_R16) || + (format == RL_PIXELFORMAT_UNCOMPRESSED_R16) || (format == RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16)) TRACELOG(RL_LOG_WARNING, "TEXTURES: Cubemap requested format not supported"); else glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, mipmapLevel, glInternalFormat, mipSize, mipSize, 0, glFormat, glType, NULL); } @@ -3674,29 +3672,37 @@ void *rlReadTexturePixels(unsigned int id, int width, int height, int format) // Read screen pixel data (color buffer) unsigned char *rlReadScreenPixels(int width, int height) { - unsigned char *screenData = (unsigned char *)RL_CALLOC(width*height*4, sizeof(unsigned char)); + unsigned char *imgData = (unsigned char *)RL_CALLOC(width*height*4, sizeof(unsigned char)); // NOTE 1: glReadPixels returns image flipped vertically -> (0,0) is the bottom left corner of the framebuffer // NOTE 2: We are getting alpha channel! Be careful, it can be transparent if not cleared properly! - glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, screenData); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, imgData); // Flip image vertically! - unsigned char *imgData = (unsigned char *)RL_MALLOC(width*height*4*sizeof(unsigned char)); - - for (int y = height - 1; y >= 0; y--) + // NOTE: Alpha value has already been applied to RGB in framebuffer, we don't need it! + for (int y = height - 1; y >= height/2; y--) { - for (int x = 0; x < (width*4); x++) + for (int x = 0; x < (width*4); x += 4) { - imgData[((height - 1) - y)*width*4 + x] = screenData[(y*width*4) + x]; // Flip line + unsigned int s = ((height - 1) - y)*width*4 + x; + unsigned int e = y*width*4 + x; - // Set alpha component value to 255 (no trasparent image retrieval) - // NOTE: Alpha value has already been applied to RGB in framebuffer, we don't need it! - if (((x + 1)%4) == 0) imgData[((height - 1) - y)*width*4 + x] = 255; + unsigned char r = imgData[s]; + unsigned char g = imgData[s+1]; + unsigned char b = imgData[s+2]; + + imgData[s] = imgData[e]; + imgData[s+1] = imgData[e+1]; + imgData[s+2] = imgData[e+2]; + imgData[s+3] = 255; // Set alpha component value to 255 (no trasparent image retrieval) + + imgData[e] = r; + imgData[e+1] = g; + imgData[e+2] = b; + imgData[e+3] = 255; // Ditto } } - RL_FREE(screenData); - return imgData; // NOTE: image data should be freed } @@ -3737,19 +3743,16 @@ void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_TEXTURE_2D, texId, mipLevel); else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_RENDERBUFFER, texId); else if (texType >= RL_ATTACHMENT_CUBEMAP_POSITIVE_X) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_TEXTURE_CUBE_MAP_POSITIVE_X + texType, texId, mipLevel); - } break; case RL_ATTACHMENT_DEPTH: { if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texId, mipLevel); else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, texId); - } break; case RL_ATTACHMENT_STENCIL: { if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texId, mipLevel); else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, texId); - } break; default: break; } @@ -3959,7 +3962,7 @@ void rlDrawVertexArrayElements(int offset, int count, const void *buffer) void rlDrawVertexArrayInstanced(int offset, int count, int instances) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glDrawArraysInstanced(GL_TRIANGLES, 0, count, instances); + glDrawArraysInstanced(GL_TRIANGLES, offset, count, instances); #endif } @@ -4216,6 +4219,7 @@ unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId) glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR); glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT); glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2); + glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCE_TX, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX); #ifdef RL_SUPPORT_MESH_GPU_SKINNING glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS); diff --git a/raylib/rlgl_cgo.go b/raylib/rlgl_cgo.go index 0c51788..bd60809 100644 --- a/raylib/rlgl_cgo.go +++ b/raylib/rlgl_cgo.go @@ -621,11 +621,12 @@ func SetVertexAttributeDivisor(index uint32, divisor int32) { } // LoadTextureDepth - Load depth texture/renderbuffer (to be attached to fbo) -func LoadTextureDepth(width, height int32, useRenderBuffer bool) { +func LoadTextureDepth(width, height int32, useRenderBuffer bool) uint32 { cwidth := C.int(width) cheight := C.int(height) cuseRenderBuffer := C.bool(useRenderBuffer) - C.rlLoadTextureDepth(cwidth, cheight, cuseRenderBuffer) + cid := C.rlLoadTextureDepth(cwidth, cheight, cuseRenderBuffer) + return uint32(cid) } // LoadFramebuffer - Load an empty framebuffer diff --git a/raylib/rlgl_purego.go b/raylib/rlgl_purego.go index e0fd23b..1439cfa 100644 --- a/raylib/rlgl_purego.go +++ b/raylib/rlgl_purego.go @@ -764,8 +764,8 @@ func SetVertexAttributeDivisor(index uint32, divisor int32) { } // LoadTextureDepth - Load depth texture/renderbuffer (to be attached to fbo) -func LoadTextureDepth(width, height int32, useRenderBuffer bool) { - rlLoadTextureDepth(width, height, useRenderBuffer) +func LoadTextureDepth(width, height int32, useRenderBuffer bool) uint32{ + return rlLoadTextureDepth(width, height, useRenderBuffer) } // LoadFramebuffer - Load an empty framebuffer diff --git a/raylib/rmodels.c b/raylib/rmodels.c index b5830b2..84daabc 100644 --- a/raylib/rmodels.c +++ b/raylib/rmodels.c @@ -21,7 +21,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) * * 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. @@ -96,9 +96,9 @@ #endif #if defined(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))) + #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))) #define PAR_FREE RL_FREE #if defined(_MSC_VER) // Disable some MSVC warning @@ -183,6 +183,7 @@ void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color) } // Draw a point in 3D space, actually a small line +// WARNING: OpenGL ES 2.0 does not support point mode drawing void DrawPoint3D(Vector3 position, Color color) { rlPushMatrix(); @@ -495,12 +496,18 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color vertices[2] = (Vector3){ cosslice*vertices[2].x - sinslice*vertices[2].z, vertices[2].y, sinslice*vertices[2].x + cosslice*vertices[2].z }; // Rotation matrix around y axis vertices[3] = (Vector3){ cosslice*vertices[3].x - sinslice*vertices[3].z, vertices[3].y, sinslice*vertices[3].x + cosslice*vertices[3].z }; + rlNormal3f(vertices[0].x, vertices[0].y, vertices[0].z); rlVertex3f(vertices[0].x, vertices[0].y, vertices[0].z); + rlNormal3f(vertices[3].x, vertices[3].y, vertices[3].z); rlVertex3f(vertices[3].x, vertices[3].y, vertices[3].z); + rlNormal3f(vertices[1].x, vertices[1].y, vertices[1].z); rlVertex3f(vertices[1].x, vertices[1].y, vertices[1].z); + rlNormal3f(vertices[0].x, vertices[0].y, vertices[0].z); rlVertex3f(vertices[0].x, vertices[0].y, vertices[0].z); + rlNormal3f(vertices[2].x, vertices[2].y, vertices[2].z); rlVertex3f(vertices[2].x, vertices[2].y, vertices[2].z); + rlNormal3f(vertices[3].x, vertices[3].y, vertices[3].z); rlVertex3f(vertices[3].x, vertices[3].y, vertices[3].z); } @@ -1306,7 +1313,7 @@ void UploadMesh(Mesh *mesh, bool dynamic) { // Default vertex attribute: normal // WARNING: Default value provided to shader if location available - float value[3] = { 1.0f, 1.0f, 1.0f }; + float value[3] = { 0.0f, 0.0f, 1.0f }; rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL, value, SHADER_ATTRIB_VEC3, 3); rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL); } @@ -1338,7 +1345,7 @@ void UploadMesh(Mesh *mesh, bool dynamic) { // Default vertex attribute: tangent // WARNING: Default value provided to shader if location available - float value[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + float value[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, value, SHADER_ATTRIB_VEC4, 4); rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT); } @@ -1422,9 +1429,13 @@ void DrawMesh(Mesh mesh, Material material, Matrix transform) rlEnableTexture(material.maps[MATERIAL_MAP_DIFFUSE].texture.id); - rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.vertices); + if (mesh.animVertices) rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.animVertices); + else rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.vertices); + rlEnableStatePointer(GL_TEXTURE_COORD_ARRAY, mesh.texcoords); - rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.normals); + if (mesh.normals) rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.animNormals); + else rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.normals); + rlEnableStatePointer(GL_COLOR_ARRAY, mesh.colors); rlPushMatrix(); @@ -1734,12 +1745,12 @@ void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, i // no faster, since we're transferring all the transform matrices anyway instancesVboId = rlLoadVertexBuffer(instanceTransforms, instances*sizeof(float16), false); - // Instances transformation matrices are send to shader attribute location: SHADER_LOC_MATRIX_MODEL + // Instances transformation matrices are sent to shader attribute location: SHADER_LOC_VERTEX_INSTANCE_TX for (unsigned int i = 0; i < 4; i++) { - rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_MATRIX_MODEL] + i); - rlSetVertexAttribute(material.shader.locs[SHADER_LOC_MATRIX_MODEL] + i, 4, RL_FLOAT, 0, sizeof(Matrix), i*sizeof(Vector4)); - rlSetVertexAttributeDivisor(material.shader.locs[SHADER_LOC_MATRIX_MODEL] + i, 1); + 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); } rlDisableVertexBuffer(); @@ -1943,13 +1954,14 @@ bool ExportMesh(Mesh mesh, const char *fileName) if (IsFileExtension(fileName, ".obj")) { // Estimated data size, it should be enough... - int dataSize = mesh.vertexCount*(int)strlen("v 0000.00f 0000.00f 0000.00f") + - mesh.vertexCount*(int)strlen("vt 0.000f 0.00f") + - mesh.vertexCount*(int)strlen("vn 0.000f 0.00f 0.00f") + - mesh.triangleCount*(int)strlen("f 00000/00000/00000 00000/00000/00000 00000/00000/00000"); + int vc = mesh.vertexCount; + int dataSize = vc*(int)strlen("v -0000.000000f -0000.000000f -0000.000000f\n") + + vc*(int)strlen("vt -0.000000f -0.000000f\n") + + vc*(int)strlen("vn -0.0000f -0.0000f -0.0000f\n") + + mesh.triangleCount*snprintf(NULL, 0, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", vc, vc, vc, vc, vc, vc, vc, vc, vc); // NOTE: Text data buffer size is estimated considering mesh data size - char *txtData = (char *)RL_CALLOC(dataSize*2 + 2000, sizeof(char)); + char *txtData = (char *)RL_CALLOC(dataSize + 1000, sizeof(char)); int byteCount = 0; byteCount += sprintf(txtData + byteCount, "# //////////////////////////////////////////////////////////////////////////////////\n"); @@ -1959,7 +1971,7 @@ bool ExportMesh(Mesh mesh, const char *fileName) byteCount += sprintf(txtData + byteCount, "# // more info and bugs-report: github.com/raysan5/raylib //\n"); byteCount += sprintf(txtData + byteCount, "# // feedback and support: ray[at]raylib.com //\n"); byteCount += sprintf(txtData + byteCount, "# // //\n"); - byteCount += sprintf(txtData + byteCount, "# // Copyright (c) 2018-2024 Ramon Santamaria (@raysan5) //\n"); + byteCount += sprintf(txtData + byteCount, "# // Copyright (c) 2018-2025 Ramon Santamaria (@raysan5) //\n"); byteCount += sprintf(txtData + byteCount, "# // //\n"); byteCount += sprintf(txtData + byteCount, "# //////////////////////////////////////////////////////////////////////////////////\n\n"); byteCount += sprintf(txtData + byteCount, "# Vertex Count: %i\n", mesh.vertexCount); @@ -1969,17 +1981,17 @@ bool ExportMesh(Mesh mesh, const char *fileName) for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3) { - byteCount += sprintf(txtData + byteCount, "v %.2f %.2f %.2f\n", mesh.vertices[v], mesh.vertices[v + 1], mesh.vertices[v + 2]); + byteCount += sprintf(txtData + byteCount, "v %.6f %.6f %.6f\n", mesh.vertices[v], mesh.vertices[v + 1], mesh.vertices[v + 2]); } for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 2) { - byteCount += sprintf(txtData + byteCount, "vt %.3f %.3f\n", mesh.texcoords[v], mesh.texcoords[v + 1]); + byteCount += sprintf(txtData + byteCount, "vt %.6f %.6f\n", mesh.texcoords[v], mesh.texcoords[v + 1]); } for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3) { - byteCount += sprintf(txtData + byteCount, "vn %.3f %.3f %.3f\n", mesh.normals[v], mesh.normals[v + 1], mesh.normals[v + 2]); + byteCount += sprintf(txtData + byteCount, "vn %.4f %.4f %.4f\n", mesh.normals[v], mesh.normals[v + 1], mesh.normals[v + 2]); } if (mesh.indices != NULL) @@ -2000,8 +2012,6 @@ bool ExportMesh(Mesh mesh, const char *fileName) } } - byteCount += sprintf(txtData + byteCount, "\n"); - // NOTE: Text data length exported is determined by '\0' (NULL) character success = SaveFileText(fileName, txtData); @@ -2164,7 +2174,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 = RL_MALLOC(count*sizeof(Material)); + materials = (Material *)RL_MALLOC(count*sizeof(Material)); ProcessMaterialsOBJ(materials, mats, count); tinyobj_materials_free(mats, count); @@ -2271,50 +2281,63 @@ void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame) { if (frame >= anim.frameCount) frame = frame%anim.frameCount; + // Get first mesh which have bones + int firstMeshWithBones = -1; + for (int i = 0; i < model.meshCount; i++) { if (model.meshes[i].boneMatrices) { - assert(model.meshes[i].boneCount == anim.boneCount); - - for (int boneId = 0; boneId < model.meshes[i].boneCount; boneId++) + if (firstMeshWithBones == -1) { - Vector3 inTranslation = model.bindPose[boneId].translation; - Quaternion inRotation = model.bindPose[boneId].rotation; - Vector3 inScale = model.bindPose[boneId].scale; + firstMeshWithBones = i; + break; + } + } + } - Vector3 outTranslation = anim.framePoses[frame][boneId].translation; - Quaternion outRotation = anim.framePoses[frame][boneId].rotation; - Vector3 outScale = anim.framePoses[frame][boneId].scale; + 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)); - Vector3 invTranslation = Vector3RotateByQuaternion(Vector3Negate(inTranslation), QuaternionInvert(inRotation)); - Quaternion invRotation = QuaternionInvert(inRotation); - Vector3 invScale = Vector3Divide((Vector3){ 1.0f, 1.0f, 1.0f }, inScale); + 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)); - Vector3 boneTranslation = Vector3Add( - Vector3RotateByQuaternion(Vector3Multiply(outScale, invTranslation), - outRotation), outTranslation); - Quaternion boneRotation = QuaternionMultiply(outRotation, invRotation); - Vector3 boneScale = Vector3Multiply(outScale, invScale); + model.meshes[firstMeshWithBones].boneMatrices[boneId] = MatrixMultiply(MatrixInvert(bindMatrix), targetMatrix); + } - Matrix boneMatrix = MatrixMultiply(MatrixMultiply( - QuaternionToMatrix(boneRotation), - MatrixTranslate(boneTranslation.x, boneTranslation.y, boneTranslation.z)), - MatrixScale(boneScale.x, boneScale.y, boneScale.z)); - - model.meshes[i].boneMatrices[boneId] = boneMatrix; + // 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])); } } } } } -// at least 2x speed up vs the old method +// 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); + for (int m = 0; m < model.meshCount; m++) { Mesh mesh = model.meshes[m]; @@ -2323,8 +2346,12 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) int boneId = 0; int boneCounter = 0; float boneWeight = 0.0; - bool updated = false; // Flag to check when anim vertex information is updated + bool updated = false; // Flag to check when anim vertex information is updated const int vValues = mesh.vertexCount*3; + + // Skip if missing bone data, causes segfault without on some models + if ((mesh.boneWeights == NULL) || (mesh.boneIds == NULL)) continue; + for (int vCounter = 0; vCounter < vValues; vCounter += 3) { mesh.animVertices[vCounter] = 0; @@ -2336,35 +2363,39 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) mesh.animNormals[vCounter + 1] = 0; mesh.animNormals[vCounter + 2] = 0; } - // Iterates over 4 bones per vertex + + // Iterates over 4 bones per vertex for (int j = 0; j < 4; j++, boneCounter++) { boneWeight = mesh.boneWeights[boneCounter]; boneId = mesh.boneIds[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]); - mesh.animVertices[vCounter] += animVertex.x * boneWeight; - mesh.animVertices[vCounter+1] += animVertex.y * boneWeight; - mesh.animVertices[vCounter+2] += animVertex.z * boneWeight; + mesh.animVertices[vCounter] += animVertex.x*boneWeight; + mesh.animVertices[vCounter+1] += animVertex.y*boneWeight; + mesh.animVertices[vCounter+2] += animVertex.z*boneWeight; updated = true; + // Normals processing // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals) - if (mesh.normals != NULL) + if ((mesh.normals != NULL) && (mesh.animNormals != NULL )) { animNormal = (Vector3){ mesh.normals[vCounter], mesh.normals[vCounter + 1], mesh.normals[vCounter + 2] }; - animNormal = Vector3Transform(animNormal,model.meshes[m].boneMatrices[boneId]); + animNormal = Vector3Transform(animNormal, MatrixTranspose(MatrixInvert(model.meshes[m].boneMatrices[boneId]))); mesh.animNormals[vCounter] += animNormal.x*boneWeight; mesh.animNormals[vCounter + 1] += animNormal.y*boneWeight; mesh.animNormals[vCounter + 2] += animNormal.z*boneWeight; } } } + if (updated) { rlUpdateVertexBuffer(mesh.vboId[0], mesh.animVertices, mesh.vertexCount*3*sizeof(float), 0); // Update vertex position - rlUpdateVertexBuffer(mesh.vboId[2], mesh.animNormals, mesh.vertexCount*3*sizeof(float), 0); // Update vertex normals + if (mesh.normals != NULL) rlUpdateVertexBuffer(mesh.vboId[2], mesh.animNormals, mesh.vertexCount*3*sizeof(float), 0); // Update vertex normals } } } @@ -2726,11 +2757,11 @@ Mesh GenMeshCube(float width, float height, float length) #else // Use par_shapes library to generate cube mesh /* // Platonic solids: -par_shapes_mesh* par_shapes_create_tetrahedron(); // 4 sides polyhedron (pyramid) -par_shapes_mesh* par_shapes_create_cube(); // 6 sides polyhedron (cube) -par_shapes_mesh* par_shapes_create_octahedron(); // 8 sides polyhedron (diamond) -par_shapes_mesh* par_shapes_create_dodecahedron(); // 12 sides polyhedron -par_shapes_mesh* par_shapes_create_icosahedron(); // 20 sides polyhedron +par_shapes_mesh *par_shapes_create_tetrahedron(); // 4 sides polyhedron (pyramid) +par_shapes_mesh *par_shapes_create_cube(); // 6 sides polyhedron (cube) +par_shapes_mesh *par_shapes_create_octahedron(); // 8 sides polyhedron (diamond) +par_shapes_mesh *par_shapes_create_dodecahedron(); // 12 sides polyhedron +par_shapes_mesh *par_shapes_create_icosahedron(); // 20 sides polyhedron */ // Platonic solid generation: cube (6 sides) // NOTE: No normals/texcoords generated by default @@ -3577,16 +3608,16 @@ BoundingBox GetMeshBoundingBox(Mesh mesh) } // Compute mesh tangents -// NOTE: To calculate mesh tangents and binormals we need mesh vertex positions and texture coordinates -// Implementation based on: https://answers.unity.com/questions/7789/calculating-tangents-vector4.html void GenMeshTangents(Mesh *mesh) { - if ((mesh->vertices == NULL) || (mesh->texcoords == NULL)) + // Check if input mesh data is useful + if ((mesh == NULL) || (mesh->vertices == NULL) || (mesh->texcoords == NULL) || (mesh->normals == NULL)) { - TRACELOG(LOG_WARNING, "MESH: Tangents generation requires texcoord vertex attribute data"); + TRACELOG(LOG_WARNING, "MESH: Tangents generation requires vertices, texcoords and normals vertex attribute data"); return; } + // Allocate or reallocate tangents data if (mesh->tangents == NULL) mesh->tangents = (float *)RL_MALLOC(mesh->vertexCount*4*sizeof(float)); else { @@ -3594,26 +3625,51 @@ void GenMeshTangents(Mesh *mesh) mesh->tangents = (float *)RL_MALLOC(mesh->vertexCount*4*sizeof(float)); } - Vector3 *tan1 = (Vector3 *)RL_MALLOC(mesh->vertexCount*sizeof(Vector3)); - Vector3 *tan2 = (Vector3 *)RL_MALLOC(mesh->vertexCount*sizeof(Vector3)); + // Allocate temporary arrays for tangents calculation + Vector3 *tan1 = (Vector3 *)RL_CALLOC(mesh->vertexCount, sizeof(Vector3)); + Vector3 *tan2 = (Vector3 *)RL_CALLOC(mesh->vertexCount, sizeof(Vector3)); - if (mesh->vertexCount % 3 != 0) + if (tan1 == NULL || tan2 == NULL) { - TRACELOG(LOG_WARNING, "MESH: vertexCount expected to be a multiple of 3. Expect uninitialized values."); + TRACELOG(LOG_WARNING, "MESH: Failed to allocate temporary memory for tangent calculation"); + if (tan1) RL_FREE(tan1); + if (tan2) RL_FREE(tan2); + return; } - for (int i = 0; i <= mesh->vertexCount - 3; i += 3) + // Process all triangles of the mesh + // 'triangleCount' must be always valid + for (int t = 0; t < mesh->triangleCount; t++) { - // Get triangle vertices - Vector3 v1 = { mesh->vertices[(i + 0)*3 + 0], mesh->vertices[(i + 0)*3 + 1], mesh->vertices[(i + 0)*3 + 2] }; - Vector3 v2 = { mesh->vertices[(i + 1)*3 + 0], mesh->vertices[(i + 1)*3 + 1], mesh->vertices[(i + 1)*3 + 2] }; - Vector3 v3 = { mesh->vertices[(i + 2)*3 + 0], mesh->vertices[(i + 2)*3 + 1], mesh->vertices[(i + 2)*3 + 2] }; + // Get triangle vertex indices + int i0, i1, i2; + + if (mesh->indices != NULL) + { + // Use indices if available + i0 = mesh->indices[t*3 + 0]; + i1 = mesh->indices[t*3 + 1]; + i2 = mesh->indices[t*3 + 2]; + } + else + { + // Sequential access for non-indexed mesh + i0 = t*3 + 0; + i1 = t*3 + 1; + i2 = t*3 + 2; + } + + // Get triangle vertices position + Vector3 v1 = { mesh->vertices[i0*3 + 0], mesh->vertices[i0*3 + 1], mesh->vertices[i0*3 + 2] }; + Vector3 v2 = { mesh->vertices[i1*3 + 0], mesh->vertices[i1*3 + 1], mesh->vertices[i1*3 + 2] }; + Vector3 v3 = { mesh->vertices[i2*3 + 0], mesh->vertices[i2*3 + 1], mesh->vertices[i2*3 + 2] }; // Get triangle texcoords - Vector2 uv1 = { mesh->texcoords[(i + 0)*2 + 0], mesh->texcoords[(i + 0)*2 + 1] }; - Vector2 uv2 = { mesh->texcoords[(i + 1)*2 + 0], mesh->texcoords[(i + 1)*2 + 1] }; - Vector2 uv3 = { mesh->texcoords[(i + 2)*2 + 0], mesh->texcoords[(i + 2)*2 + 1] }; + Vector2 uv1 = { mesh->texcoords[i0*2 + 0], mesh->texcoords[i0*2 + 1] }; + Vector2 uv2 = { mesh->texcoords[i1*2 + 0], mesh->texcoords[i1*2 + 1] }; + Vector2 uv3 = { mesh->texcoords[i2*2 + 0], mesh->texcoords[i2*2 + 1] }; + // Calculate triangle edges float x1 = v2.x - v1.x; float y1 = v2.y - v1.y; float z1 = v2.z - v1.z; @@ -3621,65 +3677,95 @@ void GenMeshTangents(Mesh *mesh) float y2 = v3.y - v1.y; float z2 = v3.z - v1.z; + // Calculate texture coordinate differences float s1 = uv2.x - uv1.x; float t1 = uv2.y - uv1.y; float s2 = uv3.x - uv1.x; float t2 = uv3.y - uv1.y; + // Calculate denominator and check for degenerate UV float div = s1*t2 - s2*t1; - float r = (div == 0.0f)? 0.0f : 1.0f/div; + float r = (fabsf(div) < 0.0001f)? 0.0f : 1.0f/div; + // Calculate tangent and bitangent directions Vector3 sdir = { (t2*x1 - t1*x2)*r, (t2*y1 - t1*y2)*r, (t2*z1 - t1*z2)*r }; Vector3 tdir = { (s1*x2 - s2*x1)*r, (s1*y2 - s2*y1)*r, (s1*z2 - s2*z1)*r }; - tan1[i + 0] = sdir; - tan1[i + 1] = sdir; - tan1[i + 2] = sdir; + // Accumulate tangents and bitangents for each vertex of the triangle + tan1[i0] = Vector3Add(tan1[i0], sdir); + tan1[i1] = Vector3Add(tan1[i1], sdir); + tan1[i2] = Vector3Add(tan1[i2], sdir); - tan2[i + 0] = tdir; - tan2[i + 1] = tdir; - tan2[i + 2] = tdir; + tan2[i0] = Vector3Add(tan2[i0], tdir); + tan2[i1] = Vector3Add(tan2[i1], tdir); + tan2[i2] = Vector3Add(tan2[i2], tdir); } - // Compute tangents considering normals + // Calculate final tangents for each vertex for (int i = 0; i < mesh->vertexCount; i++) { Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] }; Vector3 tangent = tan1[i]; - // TODO: Review, not sure if tangent computation is right, just used reference proposed maths... -#if defined(COMPUTE_TANGENTS_METHOD_01) - Vector3 tmp = Vector3Subtract(tangent, Vector3Scale(normal, Vector3DotProduct(normal, tangent))); - tmp = Vector3Normalize(tmp); - mesh->tangents[i*4 + 0] = tmp.x; - mesh->tangents[i*4 + 1] = tmp.y; - mesh->tangents[i*4 + 2] = tmp.z; - mesh->tangents[i*4 + 3] = 1.0f; -#else - Vector3OrthoNormalize(&normal, &tangent); - mesh->tangents[i*4 + 0] = tangent.x; - mesh->tangents[i*4 + 1] = tangent.y; - mesh->tangents[i*4 + 2] = tangent.z; - mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, tangent), tan2[i]) < 0.0f)? -1.0f : 1.0f; -#endif + // Handle zero tangent (can happen with degenerate UVs) + if (Vector3Length(tangent) < 0.0001f) + { + // Create a tangent perpendicular to the normal + if (fabsf(normal.z) > 0.707f) tangent = (Vector3){ 1.0f, 0.0f, 0.0f }; + else tangent = Vector3Normalize((Vector3){ -normal.y, normal.x, 0.0f }); + + mesh->tangents[i*4 + 0] = tangent.x; + mesh->tangents[i*4 + 1] = tangent.y; + mesh->tangents[i*4 + 2] = tangent.z; + mesh->tangents[i*4 + 3] = 1.0f; + continue; + } + + // Gram-Schmidt orthogonalization to make tangent orthogonal to normal + // T_prime = T - N * dot(N, T) + Vector3 orthogonalized = Vector3Subtract(tangent, Vector3Scale(normal, Vector3DotProduct(normal, tangent))); + + // Handle cases where orthogonalized vector is too small + if (Vector3Length(orthogonalized) < 0.0001f) + { + // Create a tangent perpendicular to the normal + if (fabsf(normal.z) > 0.707f) orthogonalized = (Vector3){ 1.0f, 0.0f, 0.0f }; + else orthogonalized = Vector3Normalize((Vector3){ -normal.y, normal.x, 0.0f }); + } + else + { + // Normalize the orthogonalized tangent + orthogonalized = Vector3Normalize(orthogonalized); + } + + // Store the calculated tangent + mesh->tangents[i*4 + 0] = orthogonalized.x; + mesh->tangents[i*4 + 1] = orthogonalized.y; + mesh->tangents[i*4 + 2] = orthogonalized.z; + + // Calculate the handedness (w component) + mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, orthogonalized), tan2[i]) < 0.0f)? -1.0f : 1.0f; } + // Free temporary arrays RL_FREE(tan1); RL_FREE(tan2); + // Update vertex buffers if available if (mesh->vboId != NULL) { if (mesh->vboId[SHADER_LOC_VERTEX_TANGENT] != 0) { - // Update existing vertex buffer + // Update existing tangent vertex buffer rlUpdateVertexBuffer(mesh->vboId[SHADER_LOC_VERTEX_TANGENT], mesh->tangents, mesh->vertexCount*4*sizeof(float), 0); } else { - // Load a new tangent attributes buffer + // Create new tangent vertex buffer mesh->vboId[SHADER_LOC_VERTEX_TANGENT] = rlLoadVertexBuffer(mesh->tangents, mesh->vertexCount*4*sizeof(float), false); } + // Set up vertex attributes for shader rlEnableVertexArray(mesh->vaoId); rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, 4, RL_FLOAT, 0, 0, 0); rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT); @@ -3749,6 +3835,7 @@ void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float } // Draw a model points +// WARNING: OpenGL ES 2.0 does not support point mode drawing void DrawModelPoints(Model model, Vector3 position, float scale, Color tint) { rlEnablePointMode(); @@ -3757,10 +3844,11 @@ void DrawModelPoints(Model model, Vector3 position, float scale, Color tint) DrawModel(model, position, scale, tint); rlEnableBackfaceCulling(); - rlDisableWireMode(); + rlDisablePointMode(); } // Draw a model points +// WARNING: OpenGL ES 2.0 does not support point mode drawing void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint) { rlEnablePointMode(); @@ -3769,7 +3857,7 @@ void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, floa DrawModelEx(model, position, rotationAxis, rotationAngle, scale, tint); rlEnableBackfaceCulling(); - rlDisableWireMode(); + rlDisablePointMode(); } // Draw a billboard @@ -3841,15 +3929,15 @@ void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector for (int i = 0; i < 4; i++) { points[i] = Vector3Subtract(points[i], origin3D); - if (rotation != 0.0) points[i] = Vector3RotateByAxisAngle(points[i], forward, rotation * DEG2RAD); + if (rotation != 0.0) points[i] = Vector3RotateByAxisAngle(points[i], forward, rotation*DEG2RAD); points[i] = Vector3Add(points[i], position); } Vector2 texcoords[4]; - texcoords[0] = (Vector2) { (float)source.x/texture.width, (float)(source.y + source.height)/texture.height }; - texcoords[1] = (Vector2) { (float)(source.x + source.width)/texture.width, (float)(source.y + source.height)/texture.height }; - texcoords[2] = (Vector2) { (float)(source.x + source.width)/texture.width, (float)source.y/texture.height }; - texcoords[3] = (Vector2) { (float)source.x/texture.width, (float)source.y/texture.height }; + texcoords[0] = (Vector2){ (float)source.x/texture.width, (float)(source.y + source.height)/texture.height }; + texcoords[1] = (Vector2){ (float)(source.x + source.width)/texture.width, (float)(source.y + source.height)/texture.height }; + texcoords[2] = (Vector2){ (float)(source.x + source.width)/texture.width, (float)source.y/texture.height }; + texcoords[3] = (Vector2){ (float)source.x/texture.width, (float)source.y/texture.height }; rlSetTexture(texture.id); rlBegin(RL_QUADS); @@ -4050,7 +4138,7 @@ RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform) for (int i = 0; i < triangleCount; i++) { Vector3 a, b, c; - Vector3* vertdata = (Vector3*)mesh.vertices; + Vector3 *vertdata = (Vector3 *)mesh.vertices; if (mesh.indices) { @@ -4191,30 +4279,27 @@ static void BuildPoseFromParentJoints(BoneInfo *bones, int boneCount, Transform static Model LoadOBJ(const char *fileName) { tinyobj_attrib_t objAttributes = { 0 }; - tinyobj_shape_t* objShapes = NULL; + tinyobj_shape_t *objShapes = NULL; unsigned int objShapeCount = 0; - tinyobj_material_t* objMaterials = NULL; + tinyobj_material_t *objMaterials = NULL; unsigned int objMaterialCount = 0; Model model = { 0 }; model.transform = MatrixIdentity(); - char* fileText = LoadFileText(fileName); + char *fileText = LoadFileText(fileName); if (fileText == NULL) { - TRACELOG(LOG_ERROR, "MODEL Unable to read obj file %s", fileName); + TRACELOG(LOG_ERROR, "MODEL: [%s] Unable to read obj file", fileName); return model; } char currentDir[1024] = { 0 }; strcpy(currentDir, GetWorkingDirectory()); // Save current working directory - const char* workingDir = GetDirectoryPath(fileName); // Switch to OBJ directory for material path correctness - if (CHDIR(workingDir) != 0) - { - TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", workingDir); - } + const char *workingDir = GetDirectoryPath(fileName); // Switch to OBJ directory for material path correctness + if (CHDIR(workingDir) != 0) TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", workingDir); unsigned int dataSize = (unsigned int)strlen(fileText); @@ -4223,7 +4308,7 @@ static Model LoadOBJ(const char *fileName) if (ret != TINYOBJ_SUCCESS) { - TRACELOG(LOG_ERROR, "MODEL Unable to read obj data %s", fileName); + TRACELOG(LOG_ERROR, "MODEL: Unable to read obj data %s", fileName); return model; } @@ -4234,52 +4319,51 @@ static Model LoadOBJ(const char *fileName) int lastMaterial = -1; unsigned int meshIndex = 0; - // count meshes + // Count meshes unsigned int nextShapeEnd = objAttributes.num_face_num_verts; - // see how many verts till the next shape - + // See how many verts till the next shape if (objShapeCount > 1) nextShapeEnd = objShapes[nextShape].face_offset; - // walk all the faces + // Walk all the faces for (unsigned int faceId = 0; faceId < objAttributes.num_faces; faceId++) { if (faceId >= nextShapeEnd) { - // try to find the last vert in the next shape + // Try to find the last vert in the next shape nextShape++; if (nextShape < objShapeCount) nextShapeEnd = objShapes[nextShape].face_offset; - else nextShapeEnd = objAttributes.num_face_num_verts; // this is actually the total number of face verts in the file, not faces + else nextShapeEnd = objAttributes.num_face_num_verts; // This is actually the total number of face verts in the file, not faces meshIndex++; } - else if (lastMaterial != -1 && objAttributes.material_ids[faceId] != lastMaterial) + 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, we need to allocate a new mesh } lastMaterial = objAttributes.material_ids[faceId]; faceVertIndex += objAttributes.face_num_verts[faceId]; } - // allocate the base meshes and materials + // Allocate the base meshes and materials model.meshCount = meshIndex + 1; - model.meshes = (Mesh*)MemAlloc(sizeof(Mesh) * model.meshCount); + model.meshes = (Mesh *)MemAlloc(sizeof(Mesh)*model.meshCount); if (objMaterialCount > 0) { model.materialCount = objMaterialCount; - model.materials = (Material*)MemAlloc(sizeof(Material) * objMaterialCount); + model.materials = (Material *)MemAlloc(sizeof(Material)*objMaterialCount); } - else // we must allocate at least one material + else // We must allocate at least one material { model.materialCount = 1; - model.materials = (Material*)MemAlloc(sizeof(Material) * 1); + model.materials = (Material *)MemAlloc(sizeof(Material)*1); } - model.meshMaterial = (int*)MemAlloc(sizeof(int) * model.meshCount); + model.meshMaterial = (int *)MemAlloc(sizeof(int)*model.meshCount); - // see how many verts are in each mesh - unsigned int* localMeshVertexCounts = (unsigned int*)MemAlloc(sizeof(unsigned int) * model.meshCount); + // See how many verts are in each mesh + unsigned int *localMeshVertexCounts = (unsigned int *)MemAlloc(sizeof(unsigned int)*model.meshCount); faceVertIndex = 0; nextShapeEnd = objAttributes.num_face_num_verts; @@ -4288,23 +4372,22 @@ static Model LoadOBJ(const char *fileName) unsigned int localMeshVertexCount = 0; nextShape = 1; - if (objShapeCount > 1) - nextShapeEnd = objShapes[nextShape].face_offset; + if (objShapeCount > 1) nextShapeEnd = objShapes[nextShape].face_offset; - // walk all the faces + // 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; // Do we need a new mesh? if (faceId >= nextShapeEnd) { - // try to find the last vert in the next shape + // Try to find the last vert in the next shape nextShape++; if (nextShape < objShapeCount) nextShapeEnd = objShapes[nextShape].face_offset; else nextShapeEnd = objAttributes.num_face_num_verts; // this is actually the total number of face verts in the file, not faces newMesh = true; } - else if (lastMaterial != -1 && objAttributes.material_ids[faceId] != lastMaterial) + else if ((lastMaterial != -1) && (objAttributes.material_ids[faceId] != lastMaterial)) { newMesh = true; } @@ -4322,50 +4405,52 @@ static Model LoadOBJ(const char *fileName) faceVertIndex += objAttributes.face_num_verts[faceId]; localMeshVertexCount += objAttributes.face_num_verts[faceId]; } + localMeshVertexCounts[meshIndex] = localMeshVertexCount; for (int i = 0; i < model.meshCount; i++) { - // allocate the buffers for each mesh + // Allocate the buffers for each mesh unsigned int vertexCount = localMeshVertexCounts[i]; model.meshes[i].vertexCount = vertexCount; - model.meshes[i].triangleCount = vertexCount / 3; + model.meshes[i].triangleCount = vertexCount/3; - model.meshes[i].vertices = (float*)MemAlloc(sizeof(float) * vertexCount * 3); - model.meshes[i].normals = (float*)MemAlloc(sizeof(float) * vertexCount * 3); - model.meshes[i].texcoords = (float*)MemAlloc(sizeof(float) * vertexCount * 2); - model.meshes[i].colors = (unsigned char*)MemAlloc(sizeof(unsigned char) * vertexCount * 4); + model.meshes[i].vertices = (float *)MemAlloc(sizeof(float)*vertexCount*3); + model.meshes[i].normals = (float *)MemAlloc(sizeof(float)*vertexCount*3); + model.meshes[i].texcoords = (float *)MemAlloc(sizeof(float)*vertexCount*2); + model.meshes[i].colors = (unsigned char *)MemAlloc(sizeof(unsigned char)*vertexCount*4); } MemFree(localMeshVertexCounts); localMeshVertexCounts = NULL; - // fill meshes + // Fill meshes faceVertIndex = 0; nextShapeEnd = objAttributes.num_face_num_verts; - // see how many verts till the next shape + // See how many verts till the next shape nextShape = 1; if (objShapeCount > 1) nextShapeEnd = objShapes[nextShape].face_offset; lastMaterial = -1; meshIndex = 0; localMeshVertexCount = 0; - // walk all the faces + // 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; // Do we need a new mesh? if (faceId >= nextShapeEnd) { - // try to find the last vert in the next shape + // Try to find the last vert in the next shape nextShape++; if (nextShape < objShapeCount) nextShapeEnd = objShapes[nextShape].face_offset; - else nextShapeEnd = objAttributes.num_face_num_verts; // this is actually the total number of face verts in the file, not faces + else nextShapeEnd = objAttributes.num_face_num_verts; // This is actually the total number of face verts in the file, not faces newMesh = true; } - // if this is a new material, we need to allocate a new mesh + + // If this is a new material, we need to allocate a new mesh if (lastMaterial != -1 && objAttributes.material_ids[faceId] != lastMaterial) newMesh = true; lastMaterial = objAttributes.material_ids[faceId]; @@ -4376,8 +4461,7 @@ static Model LoadOBJ(const char *fileName) } int matId = 0; - if (lastMaterial >= 0 && lastMaterial < (int)objMaterialCount) - matId = lastMaterial; + if ((lastMaterial >= 0) && (lastMaterial < (int)objMaterialCount)) matId = lastMaterial; model.meshMaterial[meshIndex] = matId; @@ -4387,19 +4471,15 @@ static Model LoadOBJ(const char *fileName) int normalIndex = objAttributes.faces[faceVertIndex].vn_idx; int texcordIndex = objAttributes.faces[faceVertIndex].vt_idx; - for (int i = 0; i < 3; i++) - model.meshes[meshIndex].vertices[localMeshVertexCount * 3 + i] = objAttributes.vertices[vertIndex * 3 + i]; + for (int i = 0; i < 3; i++) model.meshes[meshIndex].vertices[localMeshVertexCount*3 + i] = objAttributes.vertices[vertIndex*3 + i]; - for (int i = 0; i < 3; i++) - model.meshes[meshIndex].normals[localMeshVertexCount * 3 + i] = objAttributes.normals[normalIndex * 3 + i]; + for (int i = 0; i < 3; i++) model.meshes[meshIndex].normals[localMeshVertexCount*3 + i] = objAttributes.normals[normalIndex*3 + i]; - for (int i = 0; i < 2; i++) - model.meshes[meshIndex].texcoords[localMeshVertexCount * 2 + i] = objAttributes.texcoords[texcordIndex * 2 + i]; + for (int i = 0; i < 2; i++) model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + i] = objAttributes.texcoords[texcordIndex*2 + i]; - model.meshes[meshIndex].texcoords[localMeshVertexCount * 2 + 1] = 1.0f - model.meshes[meshIndex].texcoords[localMeshVertexCount * 2 + 1]; + model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1] = 1.0f - model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1]; - for (int i = 0; i < 4; i++) - model.meshes[meshIndex].colors[localMeshVertexCount * 4 + i] = 255; + for (int i = 0; i < 4; i++) model.meshes[meshIndex].colors[localMeshVertexCount*4 + i] = 255; faceVertIndex++; localMeshVertexCount++; @@ -4413,8 +4493,7 @@ static Model LoadOBJ(const char *fileName) tinyobj_shapes_free(objShapes, objShapeCount); tinyobj_materials_free(objMaterials, objMaterialCount); - for (int i = 0; i < model.meshCount; i++) - UploadMesh(model.meshes + i, true); + for (int i = 0; i < model.meshCount; i++) UploadMesh(model.meshes + i, true); // Restore current working directory if (CHDIR(currentDir) != 0) @@ -4549,25 +4628,27 @@ static Model LoadIQM(const char *fileName) if (memcmp(iqmHeader->magic, IQM_MAGIC, sizeof(IQM_MAGIC)) != 0) { TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file is not a valid model", fileName); + UnloadFileData(fileData); return model; } if (iqmHeader->version != IQM_VERSION) { TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file version not supported (%i)", fileName, iqmHeader->version); + UnloadFileData(fileData); return model; } //fileDataPtr += sizeof(IQMHeader); // Move file data pointer // Meshes data processing - imesh = RL_MALLOC(iqmHeader->num_meshes*sizeof(IQMMesh)); + imesh = (IQMMesh *)RL_MALLOC(iqmHeader->num_meshes*sizeof(IQMMesh)); //fseek(iqmFile, iqmHeader->ofs_meshes, SEEK_SET); //fread(imesh, sizeof(IQMMesh)*iqmHeader->num_meshes, 1, iqmFile); memcpy(imesh, fileDataPtr + iqmHeader->ofs_meshes, iqmHeader->num_meshes*sizeof(IQMMesh)); model.meshCount = iqmHeader->num_meshes; - model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh)); + model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); model.materialCount = model.meshCount; model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); @@ -4595,24 +4676,24 @@ static Model LoadIQM(const char *fileName) model.meshes[i].vertexCount = imesh[i].num_vertexes; - model.meshes[i].vertices = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); // Default vertex positions - model.meshes[i].normals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); // Default vertex normals - model.meshes[i].texcoords = RL_CALLOC(model.meshes[i].vertexCount*2, sizeof(float)); // Default vertex texcoords + model.meshes[i].vertices = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); // Default vertex positions + 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 = RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(unsigned char)); // Up-to 4 bones supported! - model.meshes[i].boneWeights = RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(float)); // Up-to 4 bones supported! + model.meshes[i].boneIds = (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 = RL_CALLOC(model.meshes[i].triangleCount*3, sizeof(unsigned short)); + 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 // NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning) - model.meshes[i].animVertices = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); - model.meshes[i].animNormals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); + 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)); } // Triangles data processing - tri = RL_MALLOC(iqmHeader->num_triangles*sizeof(IQMTriangle)); + tri = (IQMTriangle *)RL_MALLOC(iqmHeader->num_triangles*sizeof(IQMTriangle)); //fseek(iqmFile, iqmHeader->ofs_triangles, SEEK_SET); //fread(tri, sizeof(IQMTriangle), iqmHeader->num_triangles, iqmFile); memcpy(tri, fileDataPtr + iqmHeader->ofs_triangles, iqmHeader->num_triangles*sizeof(IQMTriangle)); @@ -4634,7 +4715,7 @@ static Model LoadIQM(const char *fileName) } // Vertex arrays data processing - va = RL_MALLOC(iqmHeader->num_vertexarrays*sizeof(IQMVertexArray)); + va = (IQMVertexArray *)RL_MALLOC(iqmHeader->num_vertexarrays*sizeof(IQMVertexArray)); //fseek(iqmFile, iqmHeader->ofs_vertexarrays, SEEK_SET); //fread(va, sizeof(IQMVertexArray), iqmHeader->num_vertexarrays, iqmFile); memcpy(va, fileDataPtr + iqmHeader->ofs_vertexarrays, iqmHeader->num_vertexarrays*sizeof(IQMVertexArray)); @@ -4645,7 +4726,7 @@ static Model LoadIQM(const char *fileName) { case IQM_POSITION: { - vertex = RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float)); + vertex = (float *)RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float)); //fseek(iqmFile, va[i].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)); @@ -4663,7 +4744,7 @@ static Model LoadIQM(const char *fileName) } break; case IQM_NORMAL: { - normal = RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float)); + normal = (float *)RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float)); //fseek(iqmFile, va[i].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)); @@ -4681,7 +4762,7 @@ static Model LoadIQM(const char *fileName) } break; case IQM_TEXCOORD: { - text = RL_MALLOC(iqmHeader->num_vertexes*2*sizeof(float)); + text = (float *)RL_MALLOC(iqmHeader->num_vertexes*2*sizeof(float)); //fseek(iqmFile, va[i].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)); @@ -4698,7 +4779,7 @@ static Model LoadIQM(const char *fileName) } break; case IQM_BLENDINDEXES: { - blendi = RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(char)); + blendi = (char *)RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(char)); //fseek(iqmFile, va[i].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)); @@ -4715,7 +4796,7 @@ static Model LoadIQM(const char *fileName) } break; case IQM_BLENDWEIGHTS: { - blendw = RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char)); + blendw = (unsigned char *)RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char)); //fseek(iqmFile, va[i].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)); @@ -4732,14 +4813,14 @@ static Model LoadIQM(const char *fileName) } break; case IQM_COLOR: { - color = RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char)); + color = (unsigned char *)RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char)); //fseek(iqmFile, va[i].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)); for (unsigned int m = 0; m < iqmHeader->num_meshes; m++) { - model.meshes[m].colors = RL_CALLOC(model.meshes[m].vertexCount*4, sizeof(unsigned char)); + model.meshes[m].colors = (unsigned char *)RL_CALLOC(model.meshes[m].vertexCount*4, sizeof(unsigned char)); int vCounter = 0; for (unsigned int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++) @@ -4753,14 +4834,14 @@ static Model LoadIQM(const char *fileName) } // Bones (joints) data processing - ijoint = RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint)); + ijoint = (IQMJoint *)RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint)); //fseek(iqmFile, iqmHeader->ofs_joints, SEEK_SET); //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 = RL_MALLOC(iqmHeader->num_joints*sizeof(BoneInfo)); - model.bindPose = RL_MALLOC(iqmHeader->num_joints*sizeof(Transform)); + model.bones = (BoneInfo *)RL_MALLOC(iqmHeader->num_joints*sizeof(BoneInfo)); + model.bindPose = (Transform *)RL_MALLOC(iqmHeader->num_joints*sizeof(Transform)); for (unsigned int i = 0; i < iqmHeader->num_joints; i++) { @@ -4790,7 +4871,7 @@ static Model LoadIQM(const char *fileName) for (int i = 0; i < model.meshCount; i++) { model.meshes[i].boneCount = model.boneCount; - model.meshes[i].boneMatrices = RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix)); + model.meshes[i].boneMatrices = (Matrix *)RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix)); for (int j = 0; j < model.meshes[i].boneCount; j++) { @@ -4870,46 +4951,48 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou if (memcmp(iqmHeader->magic, IQM_MAGIC, sizeof(IQM_MAGIC)) != 0) { TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file is not a valid model", fileName); + UnloadFileData(fileData); return NULL; } if (iqmHeader->version != IQM_VERSION) { TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file version not supported (%i)", fileName, iqmHeader->version); + UnloadFileData(fileData); return NULL; } // Get bones data - IQMPose *poses = RL_MALLOC(iqmHeader->num_poses*sizeof(IQMPose)); + IQMPose *poses = (IQMPose *)RL_MALLOC(iqmHeader->num_poses*sizeof(IQMPose)); //fseek(iqmFile, iqmHeader->ofs_poses, SEEK_SET); //fread(poses, sizeof(IQMPose), iqmHeader->num_poses, iqmFile); memcpy(poses, fileDataPtr + iqmHeader->ofs_poses, iqmHeader->num_poses*sizeof(IQMPose)); // Get animations data *animCount = iqmHeader->num_anims; - IQMAnim *anim = RL_MALLOC(iqmHeader->num_anims*sizeof(IQMAnim)); + IQMAnim *anim = (IQMAnim *)RL_MALLOC(iqmHeader->num_anims*sizeof(IQMAnim)); //fseek(iqmFile, iqmHeader->ofs_anims, SEEK_SET); //fread(anim, sizeof(IQMAnim), iqmHeader->num_anims, iqmFile); memcpy(anim, fileDataPtr + iqmHeader->ofs_anims, iqmHeader->num_anims*sizeof(IQMAnim)); - ModelAnimation *animations = RL_MALLOC(iqmHeader->num_anims*sizeof(ModelAnimation)); + ModelAnimation *animations = (ModelAnimation *)RL_MALLOC(iqmHeader->num_anims*sizeof(ModelAnimation)); // frameposes - unsigned short *framedata = RL_MALLOC(iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short)); + unsigned short *framedata = (unsigned short *)RL_MALLOC(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 = RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint)); + IQMJoint *joints = (IQMJoint *)RL_MALLOC(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 = RL_MALLOC(iqmHeader->num_poses*sizeof(BoneInfo)); - animations[a].framePoses = RL_MALLOC(anim[a].num_frames*sizeof(Transform *)); + animations[a].bones = (BoneInfo *)RL_MALLOC(iqmHeader->num_poses*sizeof(BoneInfo)); + animations[a].framePoses = (Transform **)RL_MALLOC(anim[a].num_frames*sizeof(Transform *)); memcpy(animations[a].name, fileDataPtr + iqmHeader->ofs_text + anim[a].name, 32); // I don't like this 32 here TraceLog(LOG_INFO, "IQM Anim %s", animations[a].name); // animations[a].framerate = anim.framerate; // TODO: Use animation framerate data? @@ -4924,7 +5007,7 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou animations[a].bones[j].parent = poses[j].parent; } - for (unsigned int j = 0; j < anim[a].num_frames; j++) animations[a].framePoses[j] = RL_MALLOC(iqmHeader->num_poses*sizeof(Transform)); + for (unsigned int j = 0; j < anim[a].num_frames; j++) animations[a].framePoses[j] = (Transform *)RL_MALLOC(iqmHeader->num_poses*sizeof(Transform)); int dcounter = anim[a].first_frame*iqmHeader->num_framechannels; @@ -5070,6 +5153,8 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat { Image image = { 0 }; + if (cgltfImage == NULL) return image; + if (cgltfImage->uri != NULL) // Check if image data is provided as an uri (base64 or path) { if ((strlen(cgltfImage->uri) > 5) && @@ -5111,9 +5196,9 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat image = LoadImage(TextFormat("%s/%s", texPath, cgltfImage->uri)); } } - else if (cgltfImage->buffer_view->buffer->data != NULL) // Check if image is provided as data buffer + else if ((cgltfImage->buffer_view != NULL) && (cgltfImage->buffer_view->buffer->data != NULL)) // Check if image is provided as data buffer { - unsigned char *data = RL_MALLOC(cgltfImage->buffer_view->size); + unsigned char *data = (unsigned char *)RL_MALLOC(cgltfImage->buffer_view->size); int offset = (int)cgltfImage->buffer_view->offset; int stride = (int)cgltfImage->buffer_view->stride? (int)cgltfImage->buffer_view->stride : 1; @@ -5126,10 +5211,14 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat // Check mime_type for image: (cgltfImage->mime_type == "image/png") // NOTE: Detected that some models define mime_type as "image\\/png" - if ((strcmp(cgltfImage->mime_type, "image\\/png") == 0) || - (strcmp(cgltfImage->mime_type, "image/png") == 0)) image = LoadImageFromMemory(".png", data, (int)cgltfImage->buffer_view->size); - else if ((strcmp(cgltfImage->mime_type, "image\\/jpeg") == 0) || - (strcmp(cgltfImage->mime_type, "image/jpeg") == 0)) image = LoadImageFromMemory(".jpg", data, (int)cgltfImage->buffer_view->size); + if ((strcmp(cgltfImage->mime_type, "image\\/png") == 0) || (strcmp(cgltfImage->mime_type, "image/png") == 0)) + { + image = LoadImageFromMemory(".png", data, (int)cgltfImage->buffer_view->size); + } + else if ((strcmp(cgltfImage->mime_type, "image\\/jpeg") == 0) || (strcmp(cgltfImage->mime_type, "image/jpeg") == 0)) + { + image = LoadImageFromMemory(".jpg", data, (int)cgltfImage->buffer_view->size); + } else TRACELOG(LOG_WARNING, "MODEL: glTF image data MIME type not recognized", TextFormat("%s/%s", texPath, cgltfImage->uri)); RL_FREE(data); @@ -5142,16 +5231,12 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat static BoneInfo *LoadBoneInfoGLTF(cgltf_skin skin, int *boneCount) { *boneCount = (int)skin.joints_count; - BoneInfo *bones = RL_MALLOC(skin.joints_count*sizeof(BoneInfo)); + BoneInfo *bones = (BoneInfo *)RL_CALLOC(skin.joints_count, sizeof(BoneInfo)); for (unsigned int i = 0; i < skin.joints_count; i++) { cgltf_node node = *skin.joints[i]; - if (node.name != NULL) - { - strncpy(bones[i].name, node.name, sizeof(bones[i].name)); - bones[i].name[sizeof(bones[i].name) - 1] = '\0'; - } + if (node.name != NULL) strncpy(bones[i].name, node.name, sizeof(bones[i].name) - 1); // Find parent bone index int parentIndex = -1; @@ -5177,7 +5262,7 @@ static Model LoadGLTF(const char *fileName) /********************************************************************************************* Function implemented by Wilhem Barbier(@wbrbr), with modifications by Tyler Bezera(@gamerfiend) - Transform handling implemented by Paul Melis (@paulmelis). + Transform handling implemented by Paul Melis (@paulmelis) Reviewed by Ramon Santamaria (@raysan5) FEATURES: @@ -5187,10 +5272,10 @@ static Model LoadGLTF(const char *fileName) PBR specular/glossiness flow and extended texture flows not supported - Supports multiple meshes per model (every primitives is loaded as a separate mesh) - 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). + - 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) - are turned into separate raylib Meshes. + are turned into separate raylib Meshes RESTRICTIONS: - Only triangle meshes supported @@ -5255,34 +5340,33 @@ static Model LoadGLTF(const char *fileName) if (result != cgltf_result_success) TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load mesh/material buffers", fileName); int primitivesCount = 0; + // NOTE: We will 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++) { cgltf_node *node = &(data->nodes[i]); cgltf_mesh *mesh = node->mesh; - if (!mesh) - continue; + if (!mesh) continue; for (unsigned int p = 0; p < mesh->primitives_count; p++) { - if (mesh->primitives[p].type == cgltf_primitive_type_triangles) - primitivesCount++; + if (mesh->primitives[p].type == cgltf_primitive_type_triangles) primitivesCount++; } } TRACELOG(LOG_DEBUG, " > Primitives (triangles only) count based on hierarchy : %i", primitivesCount); // Load our model data: meshes and materials model.meshCount = primitivesCount; - model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh)); + model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); // NOTE: We keep an extra slot for default material, in case some mesh requires it model.materialCount = (int)data->materials_count + 1; - model.materials = RL_CALLOC(model.materialCount, sizeof(Material)); + model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); model.materials[0] = LoadMaterialDefault(); // Load default material (index: 0) // Load mesh-material indices, by default all meshes are mapped to material index: 0 - model.meshMaterial = RL_CALLOC(model.meshCount, sizeof(int)); + model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); // Load materials data //---------------------------------------------------------------------------------------------------- @@ -5317,7 +5401,34 @@ static Model LoadGLTF(const char *fileName) Image imMetallicRoughness = LoadImageFromCgltfImage(data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture->image, texPath); if (imMetallicRoughness.data != NULL) { - model.materials[j].maps[MATERIAL_MAP_ROUGHNESS].texture = LoadTextureFromImage(imMetallicRoughness); + Image imMetallic = { 0 }; + Image imRoughness = { 0 }; + + imMetallic.data = RL_MALLOC(imMetallicRoughness.width*imMetallicRoughness.height); + imRoughness.data = RL_MALLOC(imMetallicRoughness.width*imMetallicRoughness.height); + + imMetallic.width = imRoughness.width = imMetallicRoughness.width; + imMetallic.height = imRoughness.height = imMetallicRoughness.height; + + imMetallic.format = imRoughness.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; + imMetallic.mipmaps = imRoughness.mipmaps = 1; + + for (int x = 0; x < imRoughness.width; x++) + { + for (int y = 0; y < imRoughness.height; y++) + { + Color color = GetImageColor(imMetallicRoughness, x, y); + + ((unsigned char *)imRoughness.data)[y*imRoughness.width + x] = color.g; // Roughness color channel + ((unsigned char *)imMetallic.data)[y*imMetallic.width + x] = color.b; // Metallic color channel + } + } + + model.materials[j].maps[MATERIAL_MAP_ROUGHNESS].texture = LoadTextureFromImage(imRoughness); + model.materials[j].maps[MATERIAL_MAP_METALNESS].texture = LoadTextureFromImage(imMetallic); + + UnloadImage(imRoughness); + UnloadImage(imMetallic); UnloadImage(imMetallicRoughness); } @@ -5380,7 +5491,7 @@ static Model LoadGLTF(const char *fileName) // 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. - // Note: the code below disregards the scenes defined in the file, all nodes are used. + // NOTE: The code below disregards the scenes defined in the file, all nodes are used. //---------------------------------------------------------------------------------------------------- int meshIndex = 0; for (unsigned int i = 0; i < data->nodes_count; i++) @@ -5425,7 +5536,7 @@ static Model LoadGLTF(const char *fileName) { // Init raylib mesh vertices to copy glTF attribute data model.meshes[meshIndex].vertexCount = (int)attribute->count; - model.meshes[meshIndex].vertices = RL_MALLOC(attribute->count*3*sizeof(float)); + model.meshes[meshIndex].vertices = (float *)RL_MALLOC(attribute->count*3*sizeof(float)); // Load 3 components of float data type into mesh.vertices LOAD_ATTRIBUTE(attribute, 3, float, model.meshes[meshIndex].vertices) @@ -5449,7 +5560,7 @@ static Model LoadGLTF(const char *fileName) if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_32f)) { // Init raylib mesh normals to copy glTF attribute data - model.meshes[meshIndex].normals = RL_MALLOC(attribute->count*3*sizeof(float)); + model.meshes[meshIndex].normals = (float *)RL_MALLOC(attribute->count*3*sizeof(float)); // Load 3 components of float data type into mesh.normals LOAD_ATTRIBUTE(attribute, 3, float, model.meshes[meshIndex].normals) @@ -5473,7 +5584,7 @@ static Model LoadGLTF(const char *fileName) if ((attribute->type == cgltf_type_vec4) && (attribute->component_type == cgltf_component_type_r_32f)) { // Init raylib mesh tangent to copy glTF attribute data - model.meshes[meshIndex].tangents = RL_MALLOC(attribute->count*4*sizeof(float)); + model.meshes[meshIndex].tangents = (float *)RL_MALLOC(attribute->count*4*sizeof(float)); // Load 4 components of float data type into mesh.tangents LOAD_ATTRIBUTE(attribute, 4, float, model.meshes[meshIndex].tangents) @@ -5559,10 +5670,10 @@ static Model LoadGLTF(const char *fileName) if (attribute->component_type == cgltf_component_type_r_8u) { // Init raylib mesh color to copy glTF attribute data - model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); + model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char)); // Load data into a temp buffer to be converted to raylib data type - unsigned char *temp = RL_MALLOC(attribute->count*3*sizeof(unsigned char)); + unsigned char *temp = (unsigned char *)RL_MALLOC(attribute->count*3*sizeof(unsigned char)); LOAD_ATTRIBUTE(attribute, 3, unsigned char, temp); // Convert data to raylib color data type (4 bytes) @@ -5579,10 +5690,10 @@ static Model LoadGLTF(const char *fileName) else if (attribute->component_type == cgltf_component_type_r_16u) { // Init raylib mesh color to copy glTF attribute data - model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); + model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char)); // Load data into a temp buffer to be converted to raylib data type - unsigned short *temp = RL_MALLOC(attribute->count*3*sizeof(unsigned short)); + unsigned short *temp = (unsigned short *)RL_MALLOC(attribute->count*3*sizeof(unsigned short)); LOAD_ATTRIBUTE(attribute, 3, unsigned short, temp); // Convert data to raylib color data type (4 bytes) @@ -5599,10 +5710,10 @@ static Model LoadGLTF(const char *fileName) else if (attribute->component_type == cgltf_component_type_r_32f) { // Init raylib mesh color to copy glTF attribute data - model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); + model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char)); // Load data into a temp buffer to be converted to raylib data type - float *temp = RL_MALLOC(attribute->count*3*sizeof(float)); + float *temp = (float *)RL_MALLOC(attribute->count*3*sizeof(float)); LOAD_ATTRIBUTE(attribute, 3, float, temp); // Convert data to raylib color data type (4 bytes) @@ -5623,7 +5734,7 @@ static Model LoadGLTF(const char *fileName) if (attribute->component_type == cgltf_component_type_r_8u) { // Init raylib mesh color to copy glTF attribute data - model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); + model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char)); // Load 4 components of unsigned char data type into mesh.colors LOAD_ATTRIBUTE(attribute, 4, unsigned char, model.meshes[meshIndex].colors) @@ -5631,10 +5742,10 @@ static Model LoadGLTF(const char *fileName) else if (attribute->component_type == cgltf_component_type_r_16u) { // Init raylib mesh color to copy glTF attribute data - model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); + model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char)); // Load data into a temp buffer to be converted to raylib data type - unsigned short *temp = RL_MALLOC(attribute->count*4*sizeof(unsigned short)); + unsigned short *temp = (unsigned short *)RL_MALLOC(attribute->count*4*sizeof(unsigned short)); LOAD_ATTRIBUTE(attribute, 4, unsigned short, temp); // Convert data to raylib color data type (4 bytes) @@ -5645,10 +5756,10 @@ static Model LoadGLTF(const char *fileName) else if (attribute->component_type == cgltf_component_type_r_32f) { // Init raylib mesh color to copy glTF attribute data - model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); + model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char)); // Load data into a temp buffer to be converted to raylib data type - float *temp = RL_MALLOC(attribute->count*4*sizeof(float)); + 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 @@ -5665,7 +5776,7 @@ static Model LoadGLTF(const char *fileName) } // Load primitive indices data (if provided) - if (mesh->primitives[p].indices != NULL) + if ((mesh->primitives[p].indices != NULL) && (mesh->primitives[p].indices->buffer_view != NULL)) { cgltf_accessor *attribute = mesh->primitives[p].indices; @@ -5674,7 +5785,7 @@ static Model LoadGLTF(const char *fileName) if (attribute->component_type == cgltf_component_type_r_16u) { // Init raylib mesh indices to copy glTF attribute data - model.meshes[meshIndex].indices = RL_MALLOC(attribute->count*sizeof(unsigned short)); + model.meshes[meshIndex].indices = (unsigned short *)RL_MALLOC(attribute->count*sizeof(unsigned short)); // Load unsigned short data type into mesh.indices LOAD_ATTRIBUTE(attribute, 1, unsigned short, model.meshes[meshIndex].indices) @@ -5682,14 +5793,14 @@ static Model LoadGLTF(const char *fileName) else if (attribute->component_type == cgltf_component_type_r_8u) { // Init raylib mesh indices to copy glTF attribute data - model.meshes[meshIndex].indices = RL_MALLOC(attribute->count * sizeof(unsigned short)); + model.meshes[meshIndex].indices = (unsigned short *)RL_MALLOC(attribute->count*sizeof(unsigned short)); LOAD_ATTRIBUTE_CAST(attribute, 1, unsigned char, model.meshes[meshIndex].indices, unsigned short) } else if (attribute->component_type == cgltf_component_type_r_32u) { // Init raylib mesh indices to copy glTF attribute data - model.meshes[meshIndex].indices = RL_MALLOC(attribute->count*sizeof(unsigned short)); + model.meshes[meshIndex].indices = (unsigned short *)RL_MALLOC(attribute->count*sizeof(unsigned short)); LOAD_ATTRIBUTE_CAST(attribute, 1, unsigned int, model.meshes[meshIndex].indices, unsigned short); TRACELOG(LOG_WARNING, "MODEL: [%s] Indices data converted from u32 to u16, possible loss of data", fileName); @@ -5733,11 +5844,11 @@ static Model LoadGLTF(const char *fileName) { cgltf_skin skin = data->skins[0]; model.bones = LoadBoneInfoGLTF(skin, &model.boneCount); - model.bindPose = RL_MALLOC(model.boneCount*sizeof(Transform)); + model.bindPose = (Transform *)RL_MALLOC(model.boneCount*sizeof(Transform)); for (int i = 0; i < model.boneCount; i++) { - cgltf_node* node = skin.joints[i]; + cgltf_node *node = skin.joints[i]; cgltf_float worldTransform[16]; cgltf_node_transform_world(node, worldTransform); Matrix worldMatrix = { @@ -5765,15 +5876,17 @@ static Model LoadGLTF(const char *fileName) for (unsigned int p = 0; p < mesh->primitives_count; p++) { + bool hasJoints = false; + // NOTE: We 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++) { // NOTE: JOINTS_1 + WEIGHT_1 will be used for +4 joints influencing a vertex -> Not supported by raylib - if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_joints) // JOINTS_n (vec4: 4 bones max per vertex / u8, u16) { + hasJoints = true; cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data; // NOTE: JOINTS_n can only be vec4 and u8/u16 @@ -5788,7 +5901,7 @@ 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 = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char)); + model.meshes[meshIndex].boneIds = (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) @@ -5796,10 +5909,10 @@ static Model LoadGLTF(const char *fileName) else if (attribute->component_type == cgltf_component_type_r_16u) { // Init raylib mesh boneIds to copy glTF attribute data - model.meshes[meshIndex].boneIds = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char)); + model.meshes[meshIndex].boneIds = (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 = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned short)); + unsigned short *temp = (unsigned short *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned short)); LOAD_ATTRIBUTE(attribute, 4, unsigned short, temp); // Convert data to raylib color data type (4 bytes) @@ -5828,14 +5941,13 @@ static Model LoadGLTF(const char *fileName) if (attribute->type == cgltf_type_vec4) { - // TODO: Support component types: u8, u16? if (attribute->component_type == cgltf_component_type_r_8u) { // Init raylib mesh bone weight to copy glTF attribute data - model.meshes[meshIndex].boneWeights = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float)); + model.meshes[meshIndex].boneWeights = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float)); // Load data into a temp buffer to be converted to raylib data type - unsigned char *temp = RL_MALLOC(attribute->count*4*sizeof(unsigned char)); + unsigned char *temp = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char)); LOAD_ATTRIBUTE(attribute, 4, unsigned char, temp); // Convert data to raylib bone weight data type (4 bytes) @@ -5846,10 +5958,10 @@ static Model LoadGLTF(const char *fileName) else if (attribute->component_type == cgltf_component_type_r_16u) { // Init raylib mesh bone weight to copy glTF attribute data - model.meshes[meshIndex].boneWeights = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float)); + model.meshes[meshIndex].boneWeights = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float)); // Load data into a temp buffer to be converted to raylib data type - unsigned short *temp = RL_MALLOC(attribute->count*4*sizeof(unsigned short)); + unsigned short *temp = (unsigned short *)RL_MALLOC(attribute->count*4*sizeof(unsigned short)); LOAD_ATTRIBUTE(attribute, 4, unsigned short, temp); // Convert data to raylib bone weight data type @@ -5860,10 +5972,11 @@ static Model LoadGLTF(const char *fileName) else if (attribute->component_type == cgltf_component_type_r_32f) { // Init raylib mesh bone weight to copy glTF attribute data - model.meshes[meshIndex].boneWeights = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float)); + 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: + // - 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) @@ -5874,10 +5987,37 @@ 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) + { + int parentBoneId = -1; + for (int joint = 0; joint < model.boneCount; joint++) + { + if (data->skins[0].joints[joint] == node->parent) + { + parentBoneId = joint; + break; + } + } + + if (parentBoneId >= 0) + { + model.meshes[meshIndex].boneIds = (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].boneWeights[vertexIndex] = 1.0f; + } + } + } + // Animated vertex data - model.meshes[meshIndex].animVertices = RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float)); + 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 = RL_CALLOC(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)); @@ -5885,16 +6025,15 @@ static Model LoadGLTF(const char *fileName) // Bone Transform Matrices model.meshes[meshIndex].boneCount = model.boneCount; - model.meshes[meshIndex].boneMatrices = RL_CALLOC(model.meshes[meshIndex].boneCount, sizeof(Matrix)); + 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(); } - meshIndex++; // Move to next mesh + meshIndex++; // Move to next mesh } - } // Free all cgltf loaded data @@ -6076,7 +6215,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo { cgltf_skin skin = data->skins[0]; *animCount = (int)data->animations_count; - animations = RL_MALLOC(data->animations_count*sizeof(ModelAnimation)); + animations = (ModelAnimation *)RL_CALLOC(data->animations_count, sizeof(ModelAnimation)); for (unsigned int i = 0; i < data->animations_count; i++) { @@ -6091,7 +6230,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo cgltf_interpolation_type interpolationType; }; - struct Channels *boneChannels = RL_CALLOC(animations[i].boneCount, sizeof(struct Channels)); + struct Channels *boneChannels = (struct Channels *)RL_CALLOC(animations[i].boneCount, sizeof(struct Channels)); float animDuration = 0.0f; for (unsigned int j = 0; j < animData.channels_count; j++) @@ -6149,18 +6288,14 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo animDuration = (t > animDuration)? t : animDuration; } - if (animData.name != NULL) - { - strncpy(animations[i].name, animData.name, sizeof(animations[i].name)); - animations[i].name[sizeof(animations[i].name) - 1] = '\0'; - } + if (animData.name != NULL) strncpy(animations[i].name, animData.name, sizeof(animations[i].name) - 1); animations[i].frameCount = (int)(animDuration*1000.0f/GLTF_ANIMDELAY) + 1; - animations[i].framePoses = RL_MALLOC(animations[i].frameCount*sizeof(Transform *)); + animations[i].framePoses = (Transform **)RL_MALLOC(animations[i].frameCount*sizeof(Transform *)); for (int j = 0; j < animations[i].frameCount; j++) { - animations[i].framePoses[j] = RL_MALLOC(animations[i].boneCount*sizeof(Transform)); + animations[i].framePoses[j] = (Transform *)RL_MALLOC(animations[i].boneCount*sizeof(Transform)); float time = ((float) j*GLTF_ANIMDELAY)/1000.0f; for (int k = 0; k < animations[i].boneCount; k++) @@ -6310,7 +6445,7 @@ static Model LoadVOX(const char *fileName) // Copy colors size = pmesh->vertexCount*sizeof(Color); - pmesh->colors = RL_MALLOC(size); + pmesh->colors = (unsigned char *)RL_MALLOC(size); memcpy(pmesh->colors, pcolors, size); // First material index @@ -6446,7 +6581,7 @@ static Model LoadM3D(const char *fileName) // If no map is provided, or we have colors defined, we 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 = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); + 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 ((mi == M3D_UNDEF) && (model.meshes[k].colors != NULL)) @@ -6480,11 +6615,11 @@ static Model LoadM3D(const char *fileName) // Without vertex color (full transparency), we use the default color if (model.meshes[k].colors != NULL) { - if (m3d->vertex[m3d->face[i].vertex[0]].color & 0xFF000000) + if (m3d->vertex[m3d->face[i].vertex[0]].color & 0xff000000) memcpy(&model.meshes[k].colors[l*12 + 0], &m3d->vertex[m3d->face[i].vertex[0]].color, 4); - if (m3d->vertex[m3d->face[i].vertex[1]].color & 0xFF000000) + if (m3d->vertex[m3d->face[i].vertex[1]].color & 0xff000000) memcpy(&model.meshes[k].colors[l*12 + 4], &m3d->vertex[m3d->face[i].vertex[1]].color, 4); - if (m3d->vertex[m3d->face[i].vertex[2]].color & 0xFF000000) + if (m3d->vertex[m3d->face[i].vertex[2]].color & 0xff000000) memcpy(&model.meshes[k].colors[l*12 + 8], &m3d->vertex[m3d->face[i].vertex[2]].color, 4); } @@ -6613,13 +6748,13 @@ static Model LoadM3D(const char *fileName) if (m3d->numbone) { model.boneCount = m3d->numbone + 1; - model.bones = RL_CALLOC(model.boneCount, sizeof(BoneInfo)); - model.bindPose = RL_CALLOC(model.boneCount, sizeof(Transform)); + model.bones = (BoneInfo *)RL_CALLOC(model.boneCount, sizeof(BoneInfo)); + model.bindPose = (Transform *)RL_CALLOC(model.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)); + 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; @@ -6665,7 +6800,7 @@ static Model LoadM3D(const char *fileName) 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 = RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix)); + 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(); @@ -6715,24 +6850,23 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou return NULL; } - animations = RL_MALLOC(m3d->numaction*sizeof(ModelAnimation)); + animations = (ModelAnimation *)RL_CALLOC(m3d->numaction, sizeof(ModelAnimation)); *animCount = m3d->numaction; 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 = RL_MALLOC((m3d->numbone + 1)*sizeof(BoneInfo)); - animations[a].framePoses = RL_MALLOC(animations[a].frameCount*sizeof(Transform *)); - strncpy(animations[a].name, m3d->action[a].name, sizeof(animations[a].name)); - animations[a].name[sizeof(animations[a].name) - 1] = '\0'; + animations[a].bones = (BoneInfo *)RL_MALLOC((m3d->numbone + 1)*sizeof(BoneInfo)); + animations[a].framePoses = (Transform **)RL_MALLOC(animations[a].frameCount*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); 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)); + strncpy(animations[a].bones[i].name, m3d->bone[i].name, sizeof(animations[a].bones[i].name) - 1); } // A special, never transformed "no bone" bone, used for boneless vertices @@ -6743,7 +6877,7 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou // regular intervals, so let the M3D SDK do the heavy lifting and calculate interpolated bones for (i = 0; i < animations[a].frameCount; i++) { - animations[a].framePoses[i] = RL_MALLOC((m3d->numbone + 1)*sizeof(Transform)); + animations[a].framePoses[i] = (Transform *)RL_MALLOC((m3d->numbone + 1)*sizeof(Transform)); m3db_t *pose = m3d_pose(m3d, a, i*M3D_ANIMDELAY); diff --git a/raylib/rshapes.c b/raylib/rshapes.c index ece5513..be28e6c 100644 --- a/raylib/rshapes.c +++ b/raylib/rshapes.c @@ -25,7 +25,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) * * 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. @@ -285,6 +285,7 @@ void DrawCircleV(Vector2 center, float radius, Color color) // Draw a piece of a circle void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color) { + if (startAngle == endAngle) return; if (radius <= 0.0f) radius = 0.1f; // Avoid div by zero // Function expects (endAngle > startAngle) @@ -376,6 +377,7 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA // Draw a piece of a circle outlines void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color) { + if (startAngle == endAngle) return; if (radius <= 0.0f) radius = 0.1f; // Avoid div by zero issue // Function expects (endAngle > startAngle) @@ -468,27 +470,39 @@ void DrawCircleLinesV(Vector2 center, float radius, Color color) // Draw ellipse void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color) +{ + DrawEllipseV((Vector2){ (float)centerX, (float)centerY }, radiusH, radiusV, color); +} + +// Draw ellipse (Vector version) +void DrawEllipseV(Vector2 center, float radiusH, float radiusV, Color color) { rlBegin(RL_TRIANGLES); for (int i = 0; i < 360; i += 10) { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f((float)centerX, (float)centerY); - rlVertex2f((float)centerX + cosf(DEG2RAD*(i + 10))*radiusH, (float)centerY + sinf(DEG2RAD*(i + 10))*radiusV); - rlVertex2f((float)centerX + cosf(DEG2RAD*i)*radiusH, (float)centerY + sinf(DEG2RAD*i)*radiusV); + rlVertex2f(center.x, center.y); + rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radiusH, center.y + sinf(DEG2RAD*(i + 10))*radiusV); + rlVertex2f(center.x + cosf(DEG2RAD*i)*radiusH, center.y + sinf(DEG2RAD*i)*radiusV); } rlEnd(); } // Draw ellipse outline void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color) +{ + DrawEllipseLinesV((Vector2){ (float)centerX, (float)centerY }, radiusH, radiusV, color); +} + +// Draw ellipse outline +void DrawEllipseLinesV(Vector2 center, float radiusH, float radiusV, Color color) { rlBegin(RL_LINES); for (int i = 0; i < 360; i += 10) { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(centerX + cosf(DEG2RAD*(i + 10))*radiusH, centerY + sinf(DEG2RAD*(i + 10))*radiusV); - rlVertex2f(centerX + cosf(DEG2RAD*i)*radiusH, centerY + sinf(DEG2RAD*i)*radiusV); + rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radiusH, center.y + sinf(DEG2RAD*(i + 10))*radiusV); + rlVertex2f(center.x + cosf(DEG2RAD*i)*radiusH, center.y + sinf(DEG2RAD*i)*radiusV); } rlEnd(); } @@ -772,7 +786,7 @@ void DrawRectangleGradientH(int posX, int posY, int width, int height, Color lef } // Draw a gradient-filled rectangle -void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color topRight, Color bottomRight) +void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color bottomRight, Color topRight) { rlSetTexture(GetShapesTexture().id); Rectangle shapeRect = GetShapesTextureRectangle(); @@ -789,11 +803,11 @@ void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Col rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); rlVertex2f(rec.x, rec.y + rec.height); - rlColor4ub(topRight.r, topRight.g, topRight.b, topRight.a); + rlColor4ub(bottomRight.r, bottomRight.g, bottomRight.b, bottomRight.a); rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); rlVertex2f(rec.x + rec.width, rec.y + rec.height); - rlColor4ub(bottomRight.r, bottomRight.g, bottomRight.b, bottomRight.a); + rlColor4ub(topRight.r, topRight.g, topRight.b, topRight.a); rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); rlVertex2f(rec.x + rec.width, rec.y); rlEnd(); @@ -807,22 +821,25 @@ void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Col // but it solves another issue: https://github.com/raysan5/raylib/issues/3884 void DrawRectangleLines(int posX, int posY, int width, int height, Color color) { - Matrix mat = rlGetMatrixModelview(); - float zoomFactor = 0.5f/mat.m0; + Matrix mat = rlGetMatrixTransform(); + float xOffset = 0.5f/mat.m0; + float yOffset = 0.5f/mat.m5; + rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f((float)posX - zoomFactor, (float)posY); - rlVertex2f((float)posX + (float)width + zoomFactor, (float)posY); + rlVertex2f((float)posX + xOffset, (float)posY + yOffset); + rlVertex2f((float)posX + (float)width - xOffset, (float)posY + yOffset); - rlVertex2f((float)posX + (float)width, (float)posY - zoomFactor); - rlVertex2f((float)posX + (float)width, (float)posY + (float)height + zoomFactor); + rlVertex2f((float)posX + (float)width - xOffset, (float)posY + yOffset); + rlVertex2f((float)posX + (float)width - xOffset, (float)posY + (float)height - yOffset); - rlVertex2f((float)posX + (float)width + zoomFactor, (float)posY + (float)height); - rlVertex2f((float)posX - zoomFactor, (float)posY + (float)height); + rlVertex2f((float)posX + (float)width - xOffset, (float)posY + (float)height - yOffset); + rlVertex2f((float)posX + xOffset, (float)posY + (float)height - yOffset); - rlVertex2f((float)posX, (float)posY + (float)height + zoomFactor); - rlVertex2f((float)posX, (float)posY - zoomFactor); + rlVertex2f((float)posX + xOffset, (float)posY + (float)height - yOffset); + rlVertex2f((float)posX + xOffset, (float)posY + yOffset); rlEnd(); + /* // Previous implementation, it has issues... but it does not require view matrix... #if defined(SUPPORT_QUADS_DRAW_MODE) @@ -845,7 +862,7 @@ void DrawRectangleLines(int posX, int posY, int width, int height, Color color) rlVertex2f((float)posX + 1, (float)posY + (float)height); rlVertex2f((float)posX + 1, (float)posY + 1); rlEnd(); -//#endif +#endif */ } @@ -884,7 +901,7 @@ void DrawRectangleLinesEx(Rectangle rec, float lineThick, Color color) void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color) { // Not a rounded rectangle - if ((roundness <= 0.0f) || (rec.width < 1) || (rec.height < 1 )) + if (roundness <= 0.0f) { DrawRectangleRec(rec, color); return; @@ -1160,18 +1177,29 @@ void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, int segments, f P5 ================== P4 */ const Vector2 point[16] = { - {(float)rec.x + innerRadius, rec.y - lineThick}, {(float)(rec.x + rec.width) - innerRadius, rec.y - lineThick}, { rec.x + rec.width + lineThick, (float)rec.y + innerRadius }, // PO, P1, P2 - {rec.x + rec.width + lineThick, (float)(rec.y + rec.height) - innerRadius}, {(float)(rec.x + rec.width) - innerRadius, rec.y + rec.height + lineThick}, // P3, P4 - {(float)rec.x + innerRadius, rec.y + rec.height + lineThick}, { rec.x - lineThick, (float)(rec.y + rec.height) - innerRadius}, {rec.x - lineThick, (float)rec.y + innerRadius}, // P5, P6, P7 - {(float)rec.x + innerRadius, rec.y}, {(float)(rec.x + rec.width) - innerRadius, rec.y}, // P8, P9 - { rec.x + rec.width, (float)rec.y + innerRadius }, {rec.x + rec.width, (float)(rec.y + rec.height) - innerRadius}, // P10, P11 - {(float)(rec.x + rec.width) - innerRadius, rec.y + rec.height}, {(float)rec.x + innerRadius, rec.y + rec.height}, // P12, P13 - { rec.x, (float)(rec.y + rec.height) - innerRadius}, {rec.x, (float)rec.y + innerRadius} // P14, P15 + {(float)rec.x + innerRadius + 0.5f, rec.y - lineThick + 0.5f}, + {(float)(rec.x + rec.width) - innerRadius - 0.5f, rec.y - lineThick + 0.5f}, + {rec.x + rec.width + lineThick - 0.5f, (float)rec.y + innerRadius + 0.5f}, // PO, P1, P2 + {rec.x + rec.width + lineThick - 0.5f, (float)(rec.y + rec.height) - innerRadius - 0.5f}, + {(float)(rec.x + rec.width) - innerRadius - 0.5f, rec.y + rec.height + lineThick - 0.5f}, // P3, P4 + {(float)rec.x + innerRadius + 0.5f, rec.y + rec.height + lineThick - 0.5f}, + {rec.x - lineThick + 0.5f, (float)(rec.y + rec.height) - innerRadius - 0.5f}, + {rec.x - lineThick + 0.5f, (float)rec.y + innerRadius + 0.5f}, // P5, P6, P7 + {(float)rec.x + innerRadius + 0.5f, rec.y + 0.5f}, + {(float)(rec.x + rec.width) - innerRadius - 0.5f, rec.y + 0.5f}, // P8, P9 + {rec.x + rec.width - 0.5f, (float)rec.y + innerRadius + 0.5f}, + {rec.x + rec.width - 0.5f, (float)(rec.y + rec.height) - innerRadius - 0.5f}, // P10, P11 + {(float)(rec.x + rec.width) - innerRadius - 0.5f, rec.y + rec.height - 0.5f}, + {(float)rec.x + innerRadius + 0.5f, rec.y + rec.height - 0.5f}, // P12, P13 + {rec.x + 0.5f, (float)(rec.y + rec.height) - innerRadius - 0.5f}, + {rec.x + 0.5f, (float)rec.y + innerRadius + 0.5f} // P14, P15 }; const Vector2 centers[4] = { - {(float)rec.x + innerRadius, (float)rec.y + innerRadius}, {(float)(rec.x + rec.width) - innerRadius, (float)rec.y + innerRadius}, // P16, P17 - {(float)(rec.x + rec.width) - innerRadius, (float)(rec.y + rec.height) - innerRadius}, {(float)rec.x + innerRadius, (float)(rec.y + rec.height) - innerRadius} // P18, P19 + {(float)rec.x + innerRadius + 0.5f, (float)rec.y + innerRadius + 0.5f}, + {(float)(rec.x + rec.width) - innerRadius - 0.5f, (float)rec.y + innerRadius + 0.5f}, // P16, P17 + {(float)(rec.x + rec.width) - innerRadius - 0.5f, (float)(rec.y + rec.height) - innerRadius - 0.5f}, + {(float)rec.x + innerRadius + 0.5f, (float)(rec.y + rec.height) - innerRadius - 0.5f} // P18, P19 }; const float angles[4] = { 180.0f, 270.0f, 0.0f, 90.0f }; @@ -2223,7 +2251,7 @@ bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 // NOTE: Based on http://jeffreythompson.org/collision-detection/poly-point.php bool CheckCollisionPointPoly(Vector2 point, const Vector2 *points, int pointCount) { - bool inside = false; + bool collision = false; if (pointCount > 2) { @@ -2232,12 +2260,12 @@ bool CheckCollisionPointPoly(Vector2 point, const Vector2 *points, int pointCoun if ((points[i].y > point.y) != (points[j].y > point.y) && (point.x < (points[j].x - points[i].x)*(point.y - points[i].y)/(points[j].y - points[i].y) + points[i].x)) { - inside = !inside; + collision = !collision; } } } - return inside; + return collision; } // Check collision between two rectangles @@ -2342,7 +2370,7 @@ bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshol return collision; } -// Check if circle collides with a line created betweeen two points [p1] and [p2] +// Check if circle collides with a line created between two points [p1] and [p2] RLAPI bool CheckCollisionCircleLine(Vector2 center, float radius, Vector2 p1, Vector2 p2) { float dx = p1.x - p2.x; diff --git a/raylib/rtext.c b/raylib/rtext.c index 81bfe1a..d7818d2 100644 --- a/raylib/rtext.c +++ b/raylib/rtext.c @@ -34,7 +34,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) * * 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. @@ -161,6 +161,10 @@ extern void LoadFontDefault(void) { #define BIT_CHECK(a,b) ((a) & (1u << (b))) + // check to see if we have allready allocated the font for an image, and if we don't need to upload, then just return + if (defaultFont.glyphs != NULL && !isGpuReady) + 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 @@ -247,13 +251,28 @@ extern void LoadFontDefault(void) // we must consider data as little-endian order (alpha + gray) ((unsigned short *)imFont.data)[i + j] = 0xffff; } - else ((unsigned short *)imFont.data)[i + j] = 0x00ff; + else + { + ((unsigned char *)imFont.data)[(i + j)*sizeof(short)] = 0xff; + ((unsigned char *)imFont.data)[(i + j)*sizeof(short) + 1] = 0x00; + } } counter++; } - if (isGpuReady) defaultFont.texture = LoadTextureFromImage(imFont); + if (isGpuReady) + { + 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 + if (defaultFont.glyphs != NULL) + { + UnloadImage(imFont); + return; + } + } // Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, glyphCount //------------------------------------------------------------------------------ @@ -278,7 +297,7 @@ extern void LoadFontDefault(void) testPosX += (int)(defaultFont.recs[i].width + (float)charsDivisor); - if (testPosX >= defaultFont.texture.width) + if (testPosX >= imFont.width) { currentLine++; currentPosX = 2*charsDivisor + charsWidth[i]; @@ -312,6 +331,9 @@ extern void UnloadFontDefault(void) if (isGpuReady) UnloadTexture(defaultFont.texture); RL_FREE(defaultFont.glyphs); RL_FREE(defaultFont.recs); + defaultFont.glyphCount = 0; + defaultFont.glyphs = NULL; + defaultFont.recs = NULL; } #endif // SUPPORT_DEFAULT_FONT @@ -985,7 +1007,7 @@ bool ExportFontAsCode(Font font, const char *fileName) byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n"); byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); - byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2024 Ramon Santamaria (@raysan5) //\n"); + byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2025 Ramon Santamaria (@raysan5) //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); byteCount += sprintf(txtData + byteCount, "// ---------------------------------------------------------------------------------- //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); @@ -1071,7 +1093,7 @@ bool ExportFontAsCode(Font font, const char *fileName) byteCount += sprintf(txtData + byteCount, " Image imFont = { fontImageData_%s, %i, %i, 1, %i };\n\n", styleName, image.width, image.height, image.format); #endif byteCount += sprintf(txtData + byteCount, " // Load texture from image\n"); - byteCount += sprintf(txtData + byteCount, " if (isGpuReady) font.texture = LoadTextureFromImage(imFont);\n"); + byteCount += sprintf(txtData + byteCount, " font.texture = LoadTextureFromImage(imFont);\n"); #if defined(SUPPORT_COMPRESSED_FONT_ATLAS) byteCount += sprintf(txtData + byteCount, " UnloadImage(imFont); // Uncompressed data can be unloaded from memory\n\n"); #endif @@ -1282,7 +1304,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing { Vector2 textSize = { 0 }; - if ((isGpuReady && (font.texture.id == 0)) || + if ((isGpuReady && (font.texture.id == 0)) || (text == NULL) || (text[0] == '\0')) return textSize; // Security check int size = TextLength(text); // Get size in bytes of text @@ -1339,6 +1361,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing int GetGlyphIndex(Font font, int codepoint) { int index = 0; + if (!IsFontValid(font)) return index; #define SUPPORT_UNORDERED_CHARSET #if defined(SUPPORT_UNORDERED_CHARSET) @@ -1535,27 +1558,27 @@ const char *TextSubtext(const char *text, int position, int length) if (position >= textLength) { - position = textLength - 1; - length = 0; + return buffer; //First char is already '\0' by memset } - if (length >= textLength) length = textLength; + int maxLength = textLength - position; + if (length > maxLength) length = maxLength; + if (length >= MAX_TEXT_BUFFER_LENGTH) length = MAX_TEXT_BUFFER_LENGTH - 1; // NOTE: Alternative: memcpy(buffer, text + position, length) for (int c = 0 ; c < length ; c++) { - *(buffer + c) = *(text + position); - text++; + buffer[c] = text[position + c]; } - *(buffer + length) = '\0'; + buffer[length] = '\0'; return buffer; } // Replace text string -// REQUIRES: strlen(), strstr(), strncpy(), strcpy() +// REQUIRES: strstr(), strncpy(), strcpy() // WARNING: Allocated memory must be manually freed char *TextReplace(const char *text, const char *replace, const char *by) { @@ -1624,7 +1647,7 @@ char *TextInsert(const char *text, const char *insert, int position) // Join text strings with delimiter // REQUIRES: memset(), memcpy() -const char *TextJoin(const char **textList, int count, const char *delimiter) +char *TextJoin(char **textList, int count, const char *delimiter) { static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); @@ -1658,7 +1681,7 @@ const char *TextJoin(const char **textList, int count, const char *delimiter) // Split string into multiple strings // REQUIRES: memset() -const char **TextSplit(const char *text, char delimiter, int *count) +char **TextSplit(const char *text, char delimiter, int *count) { // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, @@ -1666,7 +1689,7 @@ const char **TextSplit(const char *text, char delimiter, int *count) // 1. Maximum number of possible split strings is set by MAX_TEXTSPLIT_COUNT // 2. Maximum size of text to split is MAX_TEXT_BUFFER_LENGTH - static const char *result[MAX_TEXTSPLIT_COUNT] = { NULL }; + static char *result[MAX_TEXTSPLIT_COUNT] = { NULL }; static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); @@ -1722,7 +1745,7 @@ int TextFindIndex(const char *text, const char *find) // Get upper case version of provided string // WARNING: Limited functionality, only basic characters set // TODO: Support UTF-8 diacritics to upper-case, check codepoints -const char *TextToUpper(const char *text) +char *TextToUpper(const char *text) { static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); @@ -1741,7 +1764,7 @@ const char *TextToUpper(const char *text) // Get lower case version of provided string // WARNING: Limited functionality, only basic characters set -const char *TextToLower(const char *text) +char *TextToLower(const char *text) { static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); @@ -1760,7 +1783,7 @@ const char *TextToLower(const char *text) // Get Pascal case notation version of provided string // WARNING: Limited functionality, only basic characters set -const char *TextToPascal(const char *text) +char *TextToPascal(const char *text) { static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); @@ -1788,7 +1811,7 @@ const char *TextToPascal(const char *text) // Get snake case notation version of provided string // WARNING: Limited functionality, only basic characters set -const char *TextToSnake(const char *text) +char *TextToSnake(const char *text) { static char buffer[MAX_TEXT_BUFFER_LENGTH] = {0}; memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); @@ -1816,7 +1839,7 @@ const char *TextToSnake(const char *text) // Get Camel case notation version of provided string // WARNING: Limited functionality, only basic characters set -const char *TextToCamel(const char *text) +char *TextToCamel(const char *text) { static char buffer[MAX_TEXT_BUFFER_LENGTH] = {0}; memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); @@ -1910,7 +1933,7 @@ void UnloadCodepoints(int *codepoints) int GetCodepointCount(const char *text) { unsigned int length = 0; - char *ptr = (char *)&text[0]; + const char *ptr = text; while (*ptr != '\0') { @@ -2324,9 +2347,9 @@ static Font LoadBMFont(const char *fileName) // Convert hexadecimal to decimal (single digit) static unsigned char HexToInt(char hex) { - if (hex >= '0' && hex <= '9') return hex - '0'; - else if (hex >= 'a' && hex <= 'f') return hex - 'a' + 10; - else if (hex >= 'A' && hex <= 'F') return hex - 'A' + 10; + if ((hex >= '0') && (hex <= '9')) return hex - '0'; + else if ((hex >= 'a') && (hex <= 'f')) return hex - 'a' + 10; + else if ((hex >= 'A') && (hex <= 'F')) return hex - 'A' + 10; else return 0; } diff --git a/raylib/rtextures.c b/raylib/rtextures.c index 2d269d7..ac06d6d 100644 --- a/raylib/rtextures.c +++ b/raylib/rtextures.c @@ -42,7 +42,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) * * 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. @@ -124,10 +124,6 @@ #endif // Image fileformats not supported by default -#if defined(__TINYC__) - #define STBI_NO_SIMD -#endif - #if (defined(SUPPORT_FILEFORMAT_BMP) || \ defined(SUPPORT_FILEFORMAT_PNG) || \ defined(SUPPORT_FILEFORMAT_TGA) || \ @@ -149,6 +145,10 @@ #define STBI_NO_THREAD_LOCALS + #if defined(__TINYC__) + #define STBI_NO_SIMD + #endif + #define STB_IMAGE_IMPLEMENTATION #include "external/stb_image.h" // Required for: stbi_load_from_file() // NOTE: Used to read image data (multiple formats support) @@ -218,6 +218,9 @@ #pragma GCC diagnostic ignored "-Wunused-function" #endif +#if defined(__TINYC__) + #define STBIR_NO_SIMD +#endif #define STB_IMAGE_RESIZE_IMPLEMENTATION #include "external/stb_image_resize2.h" // Required for: stbir_resize_uint8_linear() [ImageResize()] @@ -761,7 +764,7 @@ bool ExportImageAsCode(Image image, const char *fileName) byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n"); byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); - byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2024 Ramon Santamaria (@raysan5) //\n"); + byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2025 Ramon Santamaria (@raysan5) //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n"); @@ -829,25 +832,23 @@ Image GenImageGradientLinear(int width, int height, int direction, Color start, // Calculate how far the top-left pixel is along the gradient direction from the center of said gradient float startingPos = 0.5f - (cosDir*width/2) - (sinDir*height/2); - // With directions that lie in the first or third quadrant (i.e. from top-left to + + // With directions that lie in the first or third quadrant (i.e. from top-left to // bottom-right or vice-versa), pixel (0, 0) is the farthest point on the gradient // (i.e. the pixel which should become one of the gradient's ends color); while for - // directions that lie in the second or fourth quadrant, that point is pixel (width, 0). - float maxPosValue = - ((signbit(sinDir) != 0) == (signbit(cosDir) != 0)) - ? fabsf(startingPos) - : fabsf(startingPos+width*cosDir); + // directions that lie in the second or fourth quadrant, that point is pixel (width, 0) + float maxPosValue = ((signbit(sinDir) != 0) == (signbit(cosDir) != 0))? fabsf(startingPos) : fabsf(startingPos + width*cosDir); for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { // Calculate the relative position of the pixel along the gradient direction - float pos = (startingPos + (i*cosDir + j*sinDir)) / maxPosValue; + float pos = (startingPos + (i*cosDir + j*sinDir))/maxPosValue; float factor = pos; factor = (factor > 1.0f)? 1.0f : factor; // Clamp to [-1,1] factor = (factor < -1.0f)? -1.0f : factor; // Clamp to [-1,1] - factor = factor / 2 + 0.5f; + factor = factor/2.0f + 0.5f; // Generate the color for this pixel pixels[j*width + i].r = (int)((float)end.r*factor + (float)start.r*(1.0f - factor)); @@ -1007,7 +1008,8 @@ Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float { Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); - float aspectRatio = (float)width / (float)height; + float aspectRatio = (float)width/(float)height; + for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) @@ -2100,8 +2102,8 @@ void ImageBlurGaussian(Image *image, int blurSize) Color *pixels = LoadImageColors(*image); // Loop switches between pixelsCopy1 and pixelsCopy2 - Vector4 *pixelsCopy1 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4)); - Vector4 *pixelsCopy2 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4)); + Vector4 *pixelsCopy1 = (Vector4 *)RL_MALLOC((image->height)*(image->width)*sizeof(Vector4)); + Vector4 *pixelsCopy2 = (Vector4 *)RL_MALLOC((image->height)*(image->width)*sizeof(Vector4)); for (int i = 0; i < (image->height*image->width); i++) { @@ -2249,8 +2251,8 @@ void ImageKernelConvolution(Image *image, const float *kernel, int kernelSize) Color *pixels = LoadImageColors(*image); - Vector4 *imageCopy2 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4)); - Vector4 *temp = RL_MALLOC(kernelSize*sizeof(Vector4)); + Vector4 *imageCopy2 = (Vector4 *)RL_MALLOC((image->height)*(image->width)*sizeof(Vector4)); + Vector4 *temp = (Vector4 *)RL_MALLOC(kernelSize*sizeof(Vector4)); for (int i = 0; i < kernelSize; i++) { @@ -3567,7 +3569,7 @@ void ImageDrawLineEx(Image *dst, Vector2 start, Vector2 end, int thick, Color co ImageDrawLine(dst, x1, y1, x2, y2, color); // Determine if the line is more horizontal or vertical - if (dx != 0 && abs(dy/dx) < 1) + if ((dx != 0) && (abs(dy/dx) < 1)) { // Line is more horizontal // Calculate half the width of the line @@ -3834,7 +3836,7 @@ void ImageDrawTriangleEx(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color c // Calculate the inverse of the sum of the barycentric coordinates for normalization // NOTE 1: Here, we act as if we multiply by 255 the reciprocal, which avoids additional - // calculations in the loop. This is acceptable because we are only interpolating colors. + // calculations in the loop. This is acceptable because we are only interpolating colors // NOTE 2: This sum remains constant throughout the triangle float wInvSum = 255.0f/(w1Row + w2Row + w3Row); @@ -3889,7 +3891,7 @@ void ImageDrawTriangleLines(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Colo } // Draw a triangle fan defined by points within an image (first vertex is the center) -void ImageDrawTriangleFan(Image *dst, Vector2 *points, int pointCount, Color color) +void ImageDrawTriangleFan(Image *dst, const Vector2 *points, int pointCount, Color color) { if (pointCount >= 3) { @@ -3901,7 +3903,7 @@ void ImageDrawTriangleFan(Image *dst, Vector2 *points, int pointCount, Color col } // Draw a triangle strip defined by points within an image -void ImageDrawTriangleStrip(Image *dst, Vector2 *points, int pointCount, Color color) +void ImageDrawTriangleStrip(Image *dst, const Vector2 *points, int pointCount, Color color) { if (pointCount >= 3) { @@ -4206,8 +4208,13 @@ TextureCubemap LoadTextureCubemap(Image image, int layout) ImageFormat(&faces, image.format); Image mipmapped = ImageCopy(image); - ImageMipmaps(&mipmapped); - ImageMipmaps(&faces); + #if defined(SUPPORT_IMAGE_MANIPULATION) + if (image.mipmaps > 1) + { + ImageMipmaps(&mipmapped); + ImageMipmaps(&faces); + } + #endif // NOTE: Image formatting does not work with compressed textures @@ -4223,7 +4230,7 @@ TextureCubemap LoadTextureCubemap(Image image, int layout) if (cubemap.id != 0) { cubemap.format = faces.format; - cubemap.mipmaps = 1; + cubemap.mipmaps = faces.mipmaps; } else TRACELOG(LOG_WARNING, "IMAGE: Failed to load cubemap image"); @@ -5141,10 +5148,10 @@ Color GetColor(unsigned int hexValue) { Color color; - color.r = (unsigned char)(hexValue >> 24) & 0xFF; - color.g = (unsigned char)(hexValue >> 16) & 0xFF; - color.b = (unsigned char)(hexValue >> 8) & 0xFF; - color.a = (unsigned char)hexValue & 0xFF; + color.r = (unsigned char)(hexValue >> 24) & 0xff; + color.g = (unsigned char)(hexValue >> 16) & 0xff; + color.b = (unsigned char)(hexValue >> 8) & 0xff; + color.a = (unsigned char)hexValue & 0xff; return color; } @@ -5384,13 +5391,18 @@ static float HalfToFloat(unsigned short x) { float result = 0.0f; - const unsigned int e = (x & 0x7C00) >> 10; // Exponent - const unsigned int m = (x & 0x03FF) << 13; // Mantissa - const float fm = (float)m; - const unsigned int v = (*(unsigned int*)&fm) >> 23; // Evil log2 bit hack to count leading zeros in denormalized format - const unsigned int r = (x & 0x8000) << 16 | (e != 0)*((e + 112) << 23 | m) | ((e == 0)&(m != 0))*((v - 37) << 23 | ((m << (150 - v)) & 0x007FE000)); // sign : normalized : denormalized + union { + float fm; + unsigned int ui; + } uni; - result = *(float *)&r; + const unsigned int e = (x & 0x7c00) >> 10; // Exponent + const unsigned int m = (x & 0x03cc) << 13; // Mantissa + uni.fm = (float)m; + const unsigned int v = uni.ui >> 23; // Evil log2 bit hack to count leading zeros in denormalized format + uni.ui = (x & 0x8000) << 16 | (e != 0)*((e + 112) << 23 | m) | ((e == 0)&(m != 0))*((v - 37) << 23 | ((m << (150 - v)) & 0x007fe000)); // sign : normalized : denormalized + + result = uni.fm; return result; } @@ -5400,11 +5412,17 @@ static unsigned short FloatToHalf(float x) { unsigned short result = 0; - const unsigned int b = (*(unsigned int*) & x) + 0x00001000; // Round-to-nearest-even: add last bit after truncated mantissa - const unsigned int e = (b & 0x7F800000) >> 23; // Exponent - const unsigned int m = b & 0x007FFFFF; // Mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding + union { + float fm; + unsigned int ui; + } uni; + uni.fm = x; - result = (b & 0x80000000) >> 16 | (e > 112)*((((e - 112) << 10) & 0x7C00) | m >> 13) | ((e < 113) & (e > 101))*((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) | (e > 143)*0x7FFF; // sign : normalized : denormalized : saturate + const unsigned int b = uni.ui + 0x00001000; // Round-to-nearest-even: add last bit after truncated mantissa + const unsigned int e = (b & 0x7f800000) >> 23; // Exponent + const unsigned int m = b & 0x007fffff; // Mantissa; in line below: 0x007ff000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding + + result = (b & 0x80000000) >> 16 | (e > 112)*((((e - 112) << 10) & 0x7c00) | m >> 13) | ((e < 113) & (e > 101))*((((0x007ff000 + m) >> (125 - e)) + 1) >> 1) | (e > 143)*0x7fff; // sign : normalized : denormalized : saturate return result; } @@ -5493,6 +5511,7 @@ static Vector4 *LoadImageDataNormalized(Image image) pixels[i].z = 0.0f; pixels[i].w = 1.0f; + k += 1; } break; case PIXELFORMAT_UNCOMPRESSED_R32G32B32: { @@ -5518,6 +5537,8 @@ static Vector4 *LoadImageDataNormalized(Image image) pixels[i].y = 0.0f; pixels[i].z = 0.0f; pixels[i].w = 1.0f; + + k += 1; } break; case PIXELFORMAT_UNCOMPRESSED_R16G16B16: { diff --git a/raylib/utils.c b/raylib/utils.c index c5d9748..26e7a1b 100644 --- a/raylib/utils.c +++ b/raylib/utils.c @@ -10,7 +10,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2024 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2025 Ramon Santamaria (@raysan5) * * 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. @@ -308,7 +308,7 @@ bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileN byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n"); byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); - byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2022-2024 Ramon Santamaria (@raysan5) //\n"); + byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2022-2025 Ramon Santamaria (@raysan5) //\n"); byteCount += sprintf(txtData + byteCount, "// //\n"); byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n"); @@ -405,7 +405,7 @@ void UnloadFileText(char *text) } // Save text data to file (write), string must be '\0' terminated -bool SaveFileText(const char *fileName, char *text) +bool SaveFileText(const char *fileName, const char *text) { bool success = false; diff --git a/rres/README.md b/rres/README.md deleted file mode 100644 index 55dc7db..0000000 --- a/rres/README.md +++ /dev/null @@ -1,3 +0,0 @@ -## rres [![GoDoc](https://godoc.org/github.com/gen2brain/raylib-go/rres?status.svg)](https://godoc.org/github.com/gen2brain/raylib-go/rres) - -Golang cgo bindings for [raysan5's rres](https://github.com/raysan5/rres) diff --git a/rres/cgo.go b/rres/cgo.go deleted file mode 100644 index e0ab56a..0000000 --- a/rres/cgo.go +++ /dev/null @@ -1,6 +0,0 @@ -package rres - -/* -#cgo CFLAGS: -std=gnu99 -Wno-unused-result -Wno-implicit-function-declaration -Wno-deprecated-declarations -*/ -import "C" diff --git a/rres/external/aes.c b/rres/external/aes.c deleted file mode 100644 index 4481f7b..0000000 --- a/rres/external/aes.c +++ /dev/null @@ -1,572 +0,0 @@ -/* - -This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode. -Block size can be chosen in aes.h - available choices are AES128, AES192, AES256. - -The implementation is verified against the test vectors in: - National Institute of Standards and Technology Special Publication 800-38A 2001 ED - -ECB-AES128 ----------- - - plain-text: - 6bc1bee22e409f96e93d7e117393172a - ae2d8a571e03ac9c9eb76fac45af8e51 - 30c81c46a35ce411e5fbc1191a0a52ef - f69f2445df4f9b17ad2b417be66c3710 - - key: - 2b7e151628aed2a6abf7158809cf4f3c - - resulting cipher - 3ad77bb40d7a3660a89ecaf32466ef97 - f5d3d58503b9699de785895a96fdbaaf - 43b1cd7f598ece23881b00e3ed030688 - 7b0c785e27e8ad3f8223207104725dd4 - - -NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0) - You should pad the end of the string with zeros if this is not the case. - For AES192/256 the key size is proportionally larger. - -*/ - - -/*****************************************************************************/ -/* Includes: */ -/*****************************************************************************/ -#include // CBC mode, for memset -#include "aes.h" - -/*****************************************************************************/ -/* Defines: */ -/*****************************************************************************/ -// The number of columns comprising a state in AES. This is a constant in AES. Value=4 -#define Nb 4 - -#if defined(AES256) && (AES256 == 1) - #define Nk 8 - #define Nr 14 -#elif defined(AES192) && (AES192 == 1) - #define Nk 6 - #define Nr 12 -#else - #define Nk 4 // The number of 32 bit words in a key. - #define Nr 10 // The number of rounds in AES Cipher. -#endif - -// jcallan@github points out that declaring Multiply as a function -// reduces code size considerably with the Keil ARM compiler. -// See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3 -#ifndef MULTIPLY_AS_A_FUNCTION - #define MULTIPLY_AS_A_FUNCTION 0 -#endif - - - - -/*****************************************************************************/ -/* Private variables: */ -/*****************************************************************************/ -// state - array holding the intermediate results during decryption. -typedef uint8_t state_t[4][4]; - - - -// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM -// The numbers below can be computed dynamically trading ROM for RAM - -// This can be useful in (embedded) bootloader applications, where ROM is often limited. -static const uint8_t sbox[256] = { - //0 1 2 3 4 5 6 7 8 9 A B C D E F - 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, - 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, - 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, - 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, - 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, - 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, - 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, - 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, - 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, - 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, - 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, - 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, - 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, - 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, - 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, - 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; - -#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) -static const uint8_t rsbox[256] = { - 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, - 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, - 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, - 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, - 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, - 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, - 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, - 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, - 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, - 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, - 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, - 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, - 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, - 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, - 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, - 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; -#endif - -// The round constant word array, Rcon[i], contains the values given by -// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) -static const uint8_t Rcon[11] = { - 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 }; - -/* - * Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12), - * that you can remove most of the elements in the Rcon array, because they are unused. - * - * From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon - * - * "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed), - * up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm." - */ - - -/*****************************************************************************/ -/* Private functions: */ -/*****************************************************************************/ -/* -static uint8_t getSBoxValue(uint8_t num) -{ - return sbox[num]; -} -*/ -#define getSBoxValue(num) (sbox[(num)]) - -// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. -static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key) -{ - unsigned i, j, k; - uint8_t tempa[4]; // Used for the column/row operations - - // The first round key is the key itself. - for (i = 0; i < Nk; ++i) - { - RoundKey[(i * 4) + 0] = Key[(i * 4) + 0]; - RoundKey[(i * 4) + 1] = Key[(i * 4) + 1]; - RoundKey[(i * 4) + 2] = Key[(i * 4) + 2]; - RoundKey[(i * 4) + 3] = Key[(i * 4) + 3]; - } - - // All other round keys are found from the previous round keys. - for (i = Nk; i < Nb * (Nr + 1); ++i) - { - { - k = (i - 1) * 4; - tempa[0]=RoundKey[k + 0]; - tempa[1]=RoundKey[k + 1]; - tempa[2]=RoundKey[k + 2]; - tempa[3]=RoundKey[k + 3]; - - } - - if (i % Nk == 0) - { - // This function shifts the 4 bytes in a word to the left once. - // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] - - // Function RotWord() - { - const uint8_t u8tmp = tempa[0]; - tempa[0] = tempa[1]; - tempa[1] = tempa[2]; - tempa[2] = tempa[3]; - tempa[3] = u8tmp; - } - - // SubWord() is a function that takes a four-byte input word and - // applies the S-box to each of the four bytes to produce an output word. - - // Function Subword() - { - tempa[0] = getSBoxValue(tempa[0]); - tempa[1] = getSBoxValue(tempa[1]); - tempa[2] = getSBoxValue(tempa[2]); - tempa[3] = getSBoxValue(tempa[3]); - } - - tempa[0] = tempa[0] ^ Rcon[i/Nk]; - } -#if defined(AES256) && (AES256 == 1) - if (i % Nk == 4) - { - // Function Subword() - { - tempa[0] = getSBoxValue(tempa[0]); - tempa[1] = getSBoxValue(tempa[1]); - tempa[2] = getSBoxValue(tempa[2]); - tempa[3] = getSBoxValue(tempa[3]); - } - } -#endif - j = i * 4; k=(i - Nk) * 4; - RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0]; - RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1]; - RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2]; - RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3]; - } -} - -void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key) -{ - KeyExpansion(ctx->RoundKey, key); -} -#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) -void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv) -{ - KeyExpansion(ctx->RoundKey, key); - memcpy (ctx->Iv, iv, AES_BLOCKLEN); -} -void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv) -{ - memcpy (ctx->Iv, iv, AES_BLOCKLEN); -} -#endif - -// This function adds the round key to state. -// The round key is added to the state by an XOR function. -static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey) -{ - uint8_t i,j; - for (i = 0; i < 4; ++i) - { - for (j = 0; j < 4; ++j) - { - (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j]; - } - } -} - -// The SubBytes Function Substitutes the values in the -// state matrix with values in an S-box. -static void SubBytes(state_t* state) -{ - uint8_t i, j; - for (i = 0; i < 4; ++i) - { - for (j = 0; j < 4; ++j) - { - (*state)[j][i] = getSBoxValue((*state)[j][i]); - } - } -} - -// The ShiftRows() function shifts the rows in the state to the left. -// Each row is shifted with different offset. -// Offset = Row number. So the first row is not shifted. -static void ShiftRows(state_t* state) -{ - uint8_t temp; - - // Rotate first row 1 columns to left - temp = (*state)[0][1]; - (*state)[0][1] = (*state)[1][1]; - (*state)[1][1] = (*state)[2][1]; - (*state)[2][1] = (*state)[3][1]; - (*state)[3][1] = temp; - - // Rotate second row 2 columns to left - temp = (*state)[0][2]; - (*state)[0][2] = (*state)[2][2]; - (*state)[2][2] = temp; - - temp = (*state)[1][2]; - (*state)[1][2] = (*state)[3][2]; - (*state)[3][2] = temp; - - // Rotate third row 3 columns to left - temp = (*state)[0][3]; - (*state)[0][3] = (*state)[3][3]; - (*state)[3][3] = (*state)[2][3]; - (*state)[2][3] = (*state)[1][3]; - (*state)[1][3] = temp; -} - -static uint8_t xtime(uint8_t x) -{ - return ((x<<1) ^ (((x>>7) & 1) * 0x1b)); -} - -// MixColumns function mixes the columns of the state matrix -static void MixColumns(state_t* state) -{ - uint8_t i; - uint8_t Tmp, Tm, t; - for (i = 0; i < 4; ++i) - { - t = (*state)[i][0]; - Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ; - Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ; - Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ; - Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ; - Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ; - } -} - -// Multiply is used to multiply numbers in the field GF(2^8) -// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary -// The compiler seems to be able to vectorize the operation better this way. -// See https://github.com/kokke/tiny-AES-c/pull/34 -#if MULTIPLY_AS_A_FUNCTION -static uint8_t Multiply(uint8_t x, uint8_t y) -{ - return (((y & 1) * x) ^ - ((y>>1 & 1) * xtime(x)) ^ - ((y>>2 & 1) * xtime(xtime(x))) ^ - ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ - ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */ - } -#else -#define Multiply(x, y) \ - ( ((y & 1) * x) ^ \ - ((y>>1 & 1) * xtime(x)) ^ \ - ((y>>2 & 1) * xtime(xtime(x))) ^ \ - ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \ - ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \ - -#endif - -#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) -/* -static uint8_t getSBoxInvert(uint8_t num) -{ - return rsbox[num]; -} -*/ -#define getSBoxInvert(num) (rsbox[(num)]) - -// MixColumns function mixes the columns of the state matrix. -// The method used to multiply may be difficult to understand for the inexperienced. -// Please use the references to gain more information. -static void InvMixColumns(state_t* state) -{ - int i; - uint8_t a, b, c, d; - for (i = 0; i < 4; ++i) - { - a = (*state)[i][0]; - b = (*state)[i][1]; - c = (*state)[i][2]; - d = (*state)[i][3]; - - (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); - (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); - (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); - (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); - } -} - - -// The SubBytes Function Substitutes the values in the -// state matrix with values in an S-box. -static void InvSubBytes(state_t* state) -{ - uint8_t i, j; - for (i = 0; i < 4; ++i) - { - for (j = 0; j < 4; ++j) - { - (*state)[j][i] = getSBoxInvert((*state)[j][i]); - } - } -} - -static void InvShiftRows(state_t* state) -{ - uint8_t temp; - - // Rotate first row 1 columns to right - temp = (*state)[3][1]; - (*state)[3][1] = (*state)[2][1]; - (*state)[2][1] = (*state)[1][1]; - (*state)[1][1] = (*state)[0][1]; - (*state)[0][1] = temp; - - // Rotate second row 2 columns to right - temp = (*state)[0][2]; - (*state)[0][2] = (*state)[2][2]; - (*state)[2][2] = temp; - - temp = (*state)[1][2]; - (*state)[1][2] = (*state)[3][2]; - (*state)[3][2] = temp; - - // Rotate third row 3 columns to right - temp = (*state)[0][3]; - (*state)[0][3] = (*state)[1][3]; - (*state)[1][3] = (*state)[2][3]; - (*state)[2][3] = (*state)[3][3]; - (*state)[3][3] = temp; -} -#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) - -// Cipher is the main function that encrypts the PlainText. -static void Cipher(state_t* state, const uint8_t* RoundKey) -{ - uint8_t round = 0; - - // Add the First round key to the state before starting the rounds. - AddRoundKey(0, state, RoundKey); - - // There will be Nr rounds. - // The first Nr-1 rounds are identical. - // These Nr rounds are executed in the loop below. - // Last one without MixColumns() - for (round = 1; ; ++round) - { - SubBytes(state); - ShiftRows(state); - if (round == Nr) { - break; - } - MixColumns(state); - AddRoundKey(round, state, RoundKey); - } - // Add round key to last round - AddRoundKey(Nr, state, RoundKey); -} - -#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) -static void InvCipher(state_t* state, const uint8_t* RoundKey) -{ - uint8_t round = 0; - - // Add the First round key to the state before starting the rounds. - AddRoundKey(Nr, state, RoundKey); - - // There will be Nr rounds. - // The first Nr-1 rounds are identical. - // These Nr rounds are executed in the loop below. - // Last one without InvMixColumn() - for (round = (Nr - 1); ; --round) - { - InvShiftRows(state); - InvSubBytes(state); - AddRoundKey(round, state, RoundKey); - if (round == 0) { - break; - } - InvMixColumns(state); - } - -} -#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) - -/*****************************************************************************/ -/* Public functions: */ -/*****************************************************************************/ -#if defined(ECB) && (ECB == 1) - - -void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf) -{ - // The next function call encrypts the PlainText with the Key using AES algorithm. - Cipher((state_t*)buf, ctx->RoundKey); -} - -void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf) -{ - // The next function call decrypts the PlainText with the Key using AES algorithm. - InvCipher((state_t*)buf, ctx->RoundKey); -} - - -#endif // #if defined(ECB) && (ECB == 1) - - - - - -#if defined(CBC) && (CBC == 1) - - -static void XorWithIv(uint8_t* buf, const uint8_t* Iv) -{ - uint8_t i; - for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size - { - buf[i] ^= Iv[i]; - } -} - -void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t* buf, size_t length) -{ - size_t i; - uint8_t *Iv = ctx->Iv; - for (i = 0; i < length; i += AES_BLOCKLEN) - { - XorWithIv(buf, Iv); - Cipher((state_t*)buf, ctx->RoundKey); - Iv = buf; - buf += AES_BLOCKLEN; - } - /* store Iv in ctx for next call */ - memcpy(ctx->Iv, Iv, AES_BLOCKLEN); -} - -void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length) -{ - size_t i; - uint8_t storeNextIv[AES_BLOCKLEN]; - for (i = 0; i < length; i += AES_BLOCKLEN) - { - memcpy(storeNextIv, buf, AES_BLOCKLEN); - InvCipher((state_t*)buf, ctx->RoundKey); - XorWithIv(buf, ctx->Iv); - memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN); - buf += AES_BLOCKLEN; - } - -} - -#endif // #if defined(CBC) && (CBC == 1) - - - -#if defined(CTR) && (CTR == 1) - -/* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */ -void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length) -{ - uint8_t buffer[AES_BLOCKLEN]; - - size_t i; - int bi; - for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi) - { - if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */ - { - - memcpy(buffer, ctx->Iv, AES_BLOCKLEN); - Cipher((state_t*)buffer,ctx->RoundKey); - - /* Increment Iv and handle overflow */ - for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi) - { - /* inc will overflow */ - if (ctx->Iv[bi] == 255) - { - ctx->Iv[bi] = 0; - continue; - } - ctx->Iv[bi] += 1; - break; - } - bi = 0; - } - - buf[i] = (buf[i] ^ buffer[bi]); - } -} - -#endif // #if defined(CTR) && (CTR == 1) - diff --git a/rres/external/aes.h b/rres/external/aes.h deleted file mode 100644 index 702858a..0000000 --- a/rres/external/aes.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef _AES_H_ -#define _AES_H_ - -#include -#include - -// #define the macros below to 1/0 to enable/disable the mode of operation. -// -// CBC enables AES encryption in CBC-mode of operation. -// CTR enables encryption in counter-mode. -// ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously. - -// The #ifndef-guard allows it to be configured before #include'ing or at compile time. -#ifndef CBC - #define CBC 1 -#endif - -#ifndef ECB - #define ECB 1 -#endif - -#ifndef CTR - #define CTR 1 -#endif - - -//#define AES128 1 -//#define AES192 1 -#define AES256 1 - -#define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only - -#if defined(AES256) && (AES256 == 1) - #define AES_KEYLEN 32 - #define AES_keyExpSize 240 -#elif defined(AES192) && (AES192 == 1) - #define AES_KEYLEN 24 - #define AES_keyExpSize 208 -#else - #define AES_KEYLEN 16 // Key length in bytes - #define AES_keyExpSize 176 -#endif - -struct AES_ctx -{ - uint8_t RoundKey[AES_keyExpSize]; -#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) - uint8_t Iv[AES_BLOCKLEN]; -#endif -}; - -void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key); -#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) -void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv); -void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv); -#endif - -#if defined(ECB) && (ECB == 1) -// buffer size is exactly AES_BLOCKLEN bytes; -// you need only AES_init_ctx as IV is not used in ECB -// NB: ECB is considered insecure for most uses -void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf); -void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf); - -#endif // #if defined(ECB) && (ECB == !) - - -#if defined(CBC) && (CBC == 1) -// buffer size MUST be mutile of AES_BLOCKLEN; -// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme -// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv() -// no IV should ever be reused with the same key -void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); -void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); - -#endif // #if defined(CBC) && (CBC == 1) - - -#if defined(CTR) && (CTR == 1) - -// Same function for encrypting as for decrypting. -// IV is incremented for every block, and used after encryption as XOR-compliment for output -// Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme -// NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv() -// no IV should ever be reused with the same key -void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length); - -#endif // #if defined(CTR) && (CTR == 1) - - -#endif // _AES_H_ diff --git a/rres/external/lz4.c b/rres/external/lz4.c deleted file mode 100644 index a2272cf..0000000 --- a/rres/external/lz4.c +++ /dev/null @@ -1,2526 +0,0 @@ -/* - LZ4 - Fast LZ compression algorithm - Copyright (C) 2011-2020, Yann Collet. - - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - You can contact the author at : - - LZ4 homepage : http://www.lz4.org - - LZ4 source repository : https://github.com/lz4/lz4 -*/ - -/*-************************************ -* Tuning parameters -**************************************/ -/* - * LZ4_HEAPMODE : - * Select how default compression functions will allocate memory for their hash table, - * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()). - */ -#ifndef LZ4_HEAPMODE -# define LZ4_HEAPMODE 0 -#endif - -/* - * LZ4_ACCELERATION_DEFAULT : - * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0 - */ -#define LZ4_ACCELERATION_DEFAULT 1 -/* - * LZ4_ACCELERATION_MAX : - * Any "acceleration" value higher than this threshold - * get treated as LZ4_ACCELERATION_MAX instead (fix #876) - */ -#define LZ4_ACCELERATION_MAX 65537 - - -/*-************************************ -* CPU Feature Detection -**************************************/ -/* LZ4_FORCE_MEMORY_ACCESS - * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. - * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. - * The below switch allow to select different access method for improved performance. - * Method 0 (default) : use `memcpy()`. Safe and portable. - * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). - * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. - * Method 2 : direct access. This method is portable but violate C standard. - * It can generate buggy code on targets which assembly generation depends on alignment. - * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) - * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. - * Prefer these methods in priority order (0 > 1 > 2) - */ -#ifndef LZ4_FORCE_MEMORY_ACCESS /* can be defined externally */ -# if defined(__GNUC__) && \ - ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \ - || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) -# define LZ4_FORCE_MEMORY_ACCESS 2 -# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__) -# define LZ4_FORCE_MEMORY_ACCESS 1 -# endif -#endif - -/* - * LZ4_FORCE_SW_BITCOUNT - * Define this parameter if your target system or compiler does not support hardware bit count - */ -#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for WinCE doesn't support Hardware bit count */ -# undef LZ4_FORCE_SW_BITCOUNT /* avoid double def */ -# define LZ4_FORCE_SW_BITCOUNT -#endif - - - -/*-************************************ -* Dependency -**************************************/ -/* - * LZ4_SRC_INCLUDED: - * Amalgamation flag, whether lz4.c is included - */ -#ifndef LZ4_SRC_INCLUDED -# define LZ4_SRC_INCLUDED 1 -#endif - -#ifndef LZ4_STATIC_LINKING_ONLY -#define LZ4_STATIC_LINKING_ONLY -#endif - -#ifndef LZ4_DISABLE_DEPRECATE_WARNINGS -#define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */ -#endif - -#define LZ4_STATIC_LINKING_ONLY /* LZ4_DISTANCE_MAX */ -#include "lz4.h" -/* see also "memory routines" below */ - - -/*-************************************ -* Compiler Options -**************************************/ -#if defined(_MSC_VER) && (_MSC_VER >= 1400) /* Visual Studio 2005+ */ -# include /* only present in VS2005+ */ -# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -#endif /* _MSC_VER */ - -#ifndef LZ4_FORCE_INLINE -# ifdef _MSC_VER /* Visual Studio */ -# define LZ4_FORCE_INLINE static __forceinline -# else -# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ -# ifdef __GNUC__ -# define LZ4_FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define LZ4_FORCE_INLINE static inline -# endif -# else -# define LZ4_FORCE_INLINE static -# endif /* __STDC_VERSION__ */ -# endif /* _MSC_VER */ -#endif /* LZ4_FORCE_INLINE */ - -/* LZ4_FORCE_O2 and LZ4_FORCE_INLINE - * gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy8, - * together with a simple 8-byte copy loop as a fall-back path. - * However, this optimization hurts the decompression speed by >30%, - * because the execution does not go to the optimized loop - * for typical compressible data, and all of the preamble checks - * before going to the fall-back path become useless overhead. - * This optimization happens only with the -O3 flag, and -O2 generates - * a simple 8-byte copy loop. - * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy8 - * functions are annotated with __attribute__((optimize("O2"))), - * and also LZ4_wildCopy8 is forcibly inlined, so that the O2 attribute - * of LZ4_wildCopy8 does not affect the compression speed. - */ -#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) && !defined(__clang__) -# define LZ4_FORCE_O2 __attribute__((optimize("O2"))) -# undef LZ4_FORCE_INLINE -# define LZ4_FORCE_INLINE static __inline __attribute__((optimize("O2"),always_inline)) -#else -# define LZ4_FORCE_O2 -#endif - -#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__) -# define expect(expr,value) (__builtin_expect ((expr),(value)) ) -#else -# define expect(expr,value) (expr) -#endif - -#ifndef likely -#define likely(expr) expect((expr) != 0, 1) -#endif -#ifndef unlikely -#define unlikely(expr) expect((expr) != 0, 0) -#endif - -/* Should the alignment test prove unreliable, for some reason, - * it can be disabled by setting LZ4_ALIGN_TEST to 0 */ -#ifndef LZ4_ALIGN_TEST /* can be externally provided */ -# define LZ4_ALIGN_TEST 1 -#endif - - -/*-************************************ -* Memory routines -**************************************/ -#ifdef LZ4_USER_MEMORY_FUNCTIONS -/* memory management functions can be customized by user project. - * Below functions must exist somewhere in the Project - * and be available at link time */ -void* LZ4_malloc(size_t s); -void* LZ4_calloc(size_t n, size_t s); -void LZ4_free(void* p); -# define ALLOC(s) LZ4_malloc(s) -# define ALLOC_AND_ZERO(s) LZ4_calloc(1,s) -# define FREEMEM(p) LZ4_free(p) -#else -# include /* malloc, calloc, free */ -# define ALLOC(s) malloc(s) -# define ALLOC_AND_ZERO(s) calloc(1,s) -# define FREEMEM(p) free(p) -#endif - -#include /* memset, memcpy */ -#define MEM_INIT(p,v,s) memset((p),(v),(s)) - - -/*-************************************ -* Common Constants -**************************************/ -#define MINMATCH 4 - -#define WILDCOPYLENGTH 8 -#define LASTLITERALS 5 /* see ../doc/lz4_Block_format.md#parsing-restrictions */ -#define MFLIMIT 12 /* see ../doc/lz4_Block_format.md#parsing-restrictions */ -#define MATCH_SAFEGUARD_DISTANCE ((2*WILDCOPYLENGTH) - MINMATCH) /* ensure it's possible to write 2 x wildcopyLength without overflowing output buffer */ -#define FASTLOOP_SAFE_DISTANCE 64 -static const int LZ4_minLength = (MFLIMIT+1); - -#define KB *(1 <<10) -#define MB *(1 <<20) -#define GB *(1U<<30) - -#define LZ4_DISTANCE_ABSOLUTE_MAX 65535 -#if (LZ4_DISTANCE_MAX > LZ4_DISTANCE_ABSOLUTE_MAX) /* max supported by LZ4 format */ -# error "LZ4_DISTANCE_MAX is too big : must be <= 65535" -#endif - -#define ML_BITS 4 -#define ML_MASK ((1U<=1) -# include -#else -# ifndef assert -# define assert(condition) ((void)0) -# endif -#endif - -#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use after variable declarations */ - -#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2) -# include - static int g_debuglog_enable = 1; -# define DEBUGLOG(l, ...) { \ - if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \ - fprintf(stderr, __FILE__ ": "); \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, " \n"); \ - } } -#else -# define DEBUGLOG(l, ...) {} /* disabled */ -#endif - -static int LZ4_isAligned(const void* ptr, size_t alignment) -{ - return ((size_t)ptr & (alignment -1)) == 0; -} - - -/*-************************************ -* Types -**************************************/ -#include -#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) -# include - typedef uint8_t BYTE; - typedef uint16_t U16; - typedef uint32_t U32; - typedef int32_t S32; - typedef uint64_t U64; - typedef uintptr_t uptrval; -#else -# if UINT_MAX != 4294967295UL -# error "LZ4 code (when not C++ or C99) assumes that sizeof(int) == 4" -# endif - typedef unsigned char BYTE; - typedef unsigned short U16; - typedef unsigned int U32; - typedef signed int S32; - typedef unsigned long long U64; - typedef size_t uptrval; /* generally true, except OpenVMS-64 */ -#endif - -#if defined(__x86_64__) - typedef U64 reg_t; /* 64-bits in x32 mode */ -#else - typedef size_t reg_t; /* 32-bits in x32 mode */ -#endif - -typedef enum { - notLimited = 0, - limitedOutput = 1, - fillOutput = 2 -} limitedOutput_directive; - - -/*-************************************ -* Reading and writing into memory -**************************************/ - -/** - * LZ4 relies on memcpy with a constant size being inlined. In freestanding - * environments, the compiler can't assume the implementation of memcpy() is - * standard compliant, so it can't apply its specialized memcpy() inlining - * logic. When possible, use __builtin_memcpy() to tell the compiler to analyze - * memcpy() as if it were standard compliant, so it can inline it in freestanding - * environments. This is needed when decompressing the Linux Kernel, for example. - */ -#if defined(__GNUC__) && (__GNUC__ >= 4) -#define LZ4_memcpy(dst, src, size) __builtin_memcpy(dst, src, size) -#else -#define LZ4_memcpy(dst, src, size) memcpy(dst, src, size) -#endif - -static unsigned LZ4_isLittleEndian(void) -{ - const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ - return one.c[0]; -} - - -#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2) -/* lie to the compiler about data alignment; use with caution */ - -static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; } -static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; } -static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; } - -static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } -static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; } - -#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1) - -/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ -/* currently only defined for gcc and icc */ -typedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) LZ4_unalign; - -static U16 LZ4_read16(const void* ptr) { return ((const LZ4_unalign*)ptr)->u16; } -static U32 LZ4_read32(const void* ptr) { return ((const LZ4_unalign*)ptr)->u32; } -static reg_t LZ4_read_ARCH(const void* ptr) { return ((const LZ4_unalign*)ptr)->uArch; } - -static void LZ4_write16(void* memPtr, U16 value) { ((LZ4_unalign*)memPtr)->u16 = value; } -static void LZ4_write32(void* memPtr, U32 value) { ((LZ4_unalign*)memPtr)->u32 = value; } - -#else /* safe and portable access using memcpy() */ - -static U16 LZ4_read16(const void* memPtr) -{ - U16 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; -} - -static U32 LZ4_read32(const void* memPtr) -{ - U32 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; -} - -static reg_t LZ4_read_ARCH(const void* memPtr) -{ - reg_t val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; -} - -static void LZ4_write16(void* memPtr, U16 value) -{ - LZ4_memcpy(memPtr, &value, sizeof(value)); -} - -static void LZ4_write32(void* memPtr, U32 value) -{ - LZ4_memcpy(memPtr, &value, sizeof(value)); -} - -#endif /* LZ4_FORCE_MEMORY_ACCESS */ - - -static U16 LZ4_readLE16(const void* memPtr) -{ - if (LZ4_isLittleEndian()) { - return LZ4_read16(memPtr); - } else { - const BYTE* p = (const BYTE*)memPtr; - return (U16)((U16)p[0] + (p[1]<<8)); - } -} - -static void LZ4_writeLE16(void* memPtr, U16 value) -{ - if (LZ4_isLittleEndian()) { - LZ4_write16(memPtr, value); - } else { - BYTE* p = (BYTE*)memPtr; - p[0] = (BYTE) value; - p[1] = (BYTE)(value>>8); - } -} - -/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */ -LZ4_FORCE_INLINE -void LZ4_wildCopy8(void* dstPtr, const void* srcPtr, void* dstEnd) -{ - BYTE* d = (BYTE*)dstPtr; - const BYTE* s = (const BYTE*)srcPtr; - BYTE* const e = (BYTE*)dstEnd; - - do { LZ4_memcpy(d,s,8); d+=8; s+=8; } while (d= 16. */ -LZ4_FORCE_INLINE void -LZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd) -{ - BYTE* d = (BYTE*)dstPtr; - const BYTE* s = (const BYTE*)srcPtr; - BYTE* const e = (BYTE*)dstEnd; - - do { LZ4_memcpy(d,s,16); LZ4_memcpy(d+16,s+16,16); d+=32; s+=32; } while (d= dstPtr + MINMATCH - * - there is at least 8 bytes available to write after dstEnd */ -LZ4_FORCE_INLINE void -LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset) -{ - BYTE v[8]; - - assert(dstEnd >= dstPtr + MINMATCH); - - switch(offset) { - case 1: - MEM_INIT(v, *srcPtr, 8); - break; - case 2: - LZ4_memcpy(v, srcPtr, 2); - LZ4_memcpy(&v[2], srcPtr, 2); - LZ4_memcpy(&v[4], v, 4); - break; - case 4: - LZ4_memcpy(v, srcPtr, 4); - LZ4_memcpy(&v[4], srcPtr, 4); - break; - default: - LZ4_memcpy_using_offset_base(dstPtr, srcPtr, dstEnd, offset); - return; - } - - LZ4_memcpy(dstPtr, v, 8); - dstPtr += 8; - while (dstPtr < dstEnd) { - LZ4_memcpy(dstPtr, v, 8); - dstPtr += 8; - } -} -#endif - - -/*-************************************ -* Common functions -**************************************/ -static unsigned LZ4_NbCommonBytes (reg_t val) -{ - assert(val != 0); - if (LZ4_isLittleEndian()) { - if (sizeof(val) == 8) { -# if defined(_MSC_VER) && (_MSC_VER >= 1800) && (defined(_M_AMD64) && !defined(_M_ARM64EC)) && !defined(LZ4_FORCE_SW_BITCOUNT) -/*-************************************************************************************************* -* ARM64EC is a Microsoft-designed ARM64 ABI compatible with AMD64 applications on ARM64 Windows 11. -* The ARM64EC ABI does not support AVX/AVX2/AVX512 instructions, nor their relevant intrinsics -* including _tzcnt_u64. Therefore, we need to neuter the _tzcnt_u64 code path for ARM64EC. -****************************************************************************************************/ -# if defined(__clang__) && (__clang_major__ < 10) - /* Avoid undefined clang-cl intrinics issue. - * See https://github.com/lz4/lz4/pull/1017 for details. */ - return (unsigned)__builtin_ia32_tzcnt_u64(val) >> 3; -# else - /* x64 CPUS without BMI support interpret `TZCNT` as `REP BSF` */ - return (unsigned)_tzcnt_u64(val) >> 3; -# endif -# elif defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) - unsigned long r = 0; - _BitScanForward64(&r, (U64)val); - return (unsigned)r >> 3; -# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ - ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ - !defined(LZ4_FORCE_SW_BITCOUNT) - return (unsigned)__builtin_ctzll((U64)val) >> 3; -# else - const U64 m = 0x0101010101010101ULL; - val ^= val - 1; - return (unsigned)(((U64)((val & (m - 1)) * m)) >> 56); -# endif - } else /* 32 bits */ { -# if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT) - unsigned long r; - _BitScanForward(&r, (U32)val); - return (unsigned)r >> 3; -# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ - ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ - !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT) - return (unsigned)__builtin_ctz((U32)val) >> 3; -# else - const U32 m = 0x01010101; - return (unsigned)((((val - 1) ^ val) & (m - 1)) * m) >> 24; -# endif - } - } else /* Big Endian CPU */ { - if (sizeof(val)==8) { -# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ - ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ - !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT) - return (unsigned)__builtin_clzll((U64)val) >> 3; -# else -#if 1 - /* this method is probably faster, - * but adds a 128 bytes lookup table */ - static const unsigned char ctz7_tab[128] = { - 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, - }; - U64 const mask = 0x0101010101010101ULL; - U64 const t = (((val >> 8) - mask) | val) & mask; - return ctz7_tab[(t * 0x0080402010080402ULL) >> 57]; -#else - /* this method doesn't consume memory space like the previous one, - * but it contains several branches, - * that may end up slowing execution */ - static const U32 by32 = sizeof(val)*4; /* 32 on 64 bits (goal), 16 on 32 bits. - Just to avoid some static analyzer complaining about shift by 32 on 32-bits target. - Note that this code path is never triggered in 32-bits mode. */ - unsigned r; - if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; } - if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } - r += (!val); - return r; -#endif -# endif - } else /* 32 bits */ { -# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ - ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ - !defined(LZ4_FORCE_SW_BITCOUNT) - return (unsigned)__builtin_clz((U32)val) >> 3; -# else - val >>= 8; - val = ((((val + 0x00FFFF00) | 0x00FFFFFF) + val) | - (val + 0x00FF0000)) >> 24; - return (unsigned)val ^ 3; -# endif - } - } -} - - -#define STEPSIZE sizeof(reg_t) -LZ4_FORCE_INLINE -unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit) -{ - const BYTE* const pStart = pIn; - - if (likely(pIn < pInLimit-(STEPSIZE-1))) { - reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); - if (!diff) { - pIn+=STEPSIZE; pMatch+=STEPSIZE; - } else { - return LZ4_NbCommonBytes(diff); - } } - - while (likely(pIn < pInLimit-(STEPSIZE-1))) { - reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); - if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; } - pIn += LZ4_NbCommonBytes(diff); - return (unsigned)(pIn - pStart); - } - - if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; } - if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; } - if ((pIn compression run slower on incompressible data */ - - -/*-************************************ -* Local Structures and types -**************************************/ -typedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t; - -/** - * This enum distinguishes several different modes of accessing previous - * content in the stream. - * - * - noDict : There is no preceding content. - * - withPrefix64k : Table entries up to ctx->dictSize before the current blob - * blob being compressed are valid and refer to the preceding - * content (of length ctx->dictSize), which is available - * contiguously preceding in memory the content currently - * being compressed. - * - usingExtDict : Like withPrefix64k, but the preceding content is somewhere - * else in memory, starting at ctx->dictionary with length - * ctx->dictSize. - * - usingDictCtx : Everything concerning the preceding content is - * in a separate context, pointed to by ctx->dictCtx. - * ctx->dictionary, ctx->dictSize, and table entries - * in the current context that refer to positions - * preceding the beginning of the current compression are - * ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx - * ->dictSize describe the location and size of the preceding - * content, and matches are found by looking in the ctx - * ->dictCtx->hashTable. - */ -typedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive; -typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive; - - -/*-************************************ -* Local Utils -**************************************/ -int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; } -const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; } -int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); } -int LZ4_sizeofState(void) { return LZ4_STREAMSIZE; } - - -/*-**************************************** -* Internal Definitions, used only in Tests -*******************************************/ -#if defined (__cplusplus) -extern "C" { -#endif - -int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize); - -int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, - int compressedSize, int maxOutputSize, - const void* dictStart, size_t dictSize); - -#if defined (__cplusplus) -} -#endif - -/*-****************************** -* Compression functions -********************************/ -LZ4_FORCE_INLINE U32 LZ4_hash4(U32 sequence, tableType_t const tableType) -{ - if (tableType == byU16) - return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1))); - else - return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG)); -} - -LZ4_FORCE_INLINE U32 LZ4_hash5(U64 sequence, tableType_t const tableType) -{ - const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG; - if (LZ4_isLittleEndian()) { - const U64 prime5bytes = 889523592379ULL; - return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog)); - } else { - const U64 prime8bytes = 11400714785074694791ULL; - return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog)); - } -} - -LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType) -{ - if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType); - return LZ4_hash4(LZ4_read32(p), tableType); -} - -LZ4_FORCE_INLINE void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType) -{ - switch (tableType) - { - default: /* fallthrough */ - case clearedTable: { /* illegal! */ assert(0); return; } - case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = NULL; return; } - case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = 0; return; } - case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = 0; return; } - } -} - -LZ4_FORCE_INLINE void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType) -{ - switch (tableType) - { - default: /* fallthrough */ - case clearedTable: /* fallthrough */ - case byPtr: { /* illegal! */ assert(0); return; } - case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; } - case byU16: { U16* hashTable = (U16*) tableBase; assert(idx < 65536); hashTable[h] = (U16)idx; return; } - } -} - -LZ4_FORCE_INLINE void LZ4_putPositionOnHash(const BYTE* p, U32 h, - void* tableBase, tableType_t const tableType, - const BYTE* srcBase) -{ - switch (tableType) - { - case clearedTable: { /* illegal! */ assert(0); return; } - case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; } - case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; } - case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; } - } -} - -LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase) -{ - U32 const h = LZ4_hashPosition(p, tableType); - LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase); -} - -/* LZ4_getIndexOnHash() : - * Index of match position registered in hash table. - * hash position must be calculated by using base+index, or dictBase+index. - * Assumption 1 : only valid if tableType == byU32 or byU16. - * Assumption 2 : h is presumed valid (within limits of hash table) - */ -LZ4_FORCE_INLINE U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType) -{ - LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2); - if (tableType == byU32) { - const U32* const hashTable = (const U32*) tableBase; - assert(h < (1U << (LZ4_MEMORY_USAGE-2))); - return hashTable[h]; - } - if (tableType == byU16) { - const U16* const hashTable = (const U16*) tableBase; - assert(h < (1U << (LZ4_MEMORY_USAGE-1))); - return hashTable[h]; - } - assert(0); return 0; /* forbidden case */ -} - -static const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType, const BYTE* srcBase) -{ - if (tableType == byPtr) { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; } - if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; return hashTable[h] + srcBase; } - { const U16* const hashTable = (const U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */ -} - -LZ4_FORCE_INLINE const BYTE* -LZ4_getPosition(const BYTE* p, - const void* tableBase, tableType_t tableType, - const BYTE* srcBase) -{ - U32 const h = LZ4_hashPosition(p, tableType); - return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase); -} - -LZ4_FORCE_INLINE void -LZ4_prepareTable(LZ4_stream_t_internal* const cctx, - const int inputSize, - const tableType_t tableType) { - /* If the table hasn't been used, it's guaranteed to be zeroed out, and is - * therefore safe to use no matter what mode we're in. Otherwise, we figure - * out if it's safe to leave as is or whether it needs to be reset. - */ - if ((tableType_t)cctx->tableType != clearedTable) { - assert(inputSize >= 0); - if ((tableType_t)cctx->tableType != tableType - || ((tableType == byU16) && cctx->currentOffset + (unsigned)inputSize >= 0xFFFFU) - || ((tableType == byU32) && cctx->currentOffset > 1 GB) - || tableType == byPtr - || inputSize >= 4 KB) - { - DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", cctx); - MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE); - cctx->currentOffset = 0; - cctx->tableType = (U32)clearedTable; - } else { - DEBUGLOG(4, "LZ4_prepareTable: Re-use hash table (no reset)"); - } - } - - /* Adding a gap, so all previous entries are > LZ4_DISTANCE_MAX back, - * is faster than compressing without a gap. - * However, compressing with currentOffset == 0 is faster still, - * so we preserve that case. - */ - if (cctx->currentOffset != 0 && tableType == byU32) { - DEBUGLOG(5, "LZ4_prepareTable: adding 64KB to currentOffset"); - cctx->currentOffset += 64 KB; - } - - /* Finally, clear history */ - cctx->dictCtx = NULL; - cctx->dictionary = NULL; - cctx->dictSize = 0; -} - -/** LZ4_compress_generic() : - * inlined, to ensure branches are decided at compilation time. - * Presumed already validated at this stage: - * - source != NULL - * - inputSize > 0 - */ -LZ4_FORCE_INLINE int LZ4_compress_generic_validated( - LZ4_stream_t_internal* const cctx, - const char* const source, - char* const dest, - const int inputSize, - int* inputConsumed, /* only written when outputDirective == fillOutput */ - const int maxOutputSize, - const limitedOutput_directive outputDirective, - const tableType_t tableType, - const dict_directive dictDirective, - const dictIssue_directive dictIssue, - const int acceleration) -{ - int result; - const BYTE* ip = (const BYTE*) source; - - U32 const startIndex = cctx->currentOffset; - const BYTE* base = (const BYTE*) source - startIndex; - const BYTE* lowLimit; - - const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx; - const BYTE* const dictionary = - dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary; - const U32 dictSize = - dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize; - const U32 dictDelta = (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0; /* make indexes in dictCtx comparable with index in current context */ - - int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx); - U32 const prefixIdxLimit = startIndex - dictSize; /* used when dictDirective == dictSmall */ - const BYTE* const dictEnd = dictionary ? dictionary + dictSize : dictionary; - const BYTE* anchor = (const BYTE*) source; - const BYTE* const iend = ip + inputSize; - const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1; - const BYTE* const matchlimit = iend - LASTLITERALS; - - /* the dictCtx currentOffset is indexed on the start of the dictionary, - * while a dictionary in the current context precedes the currentOffset */ - const BYTE* dictBase = (dictionary == NULL) ? NULL : - (dictDirective == usingDictCtx) ? - dictionary + dictSize - dictCtx->currentOffset : - dictionary + dictSize - startIndex; - - BYTE* op = (BYTE*) dest; - BYTE* const olimit = op + maxOutputSize; - - U32 offset = 0; - U32 forwardH; - - DEBUGLOG(5, "LZ4_compress_generic_validated: srcSize=%i, tableType=%u", inputSize, tableType); - assert(ip != NULL); - /* If init conditions are not met, we don't have to mark stream - * as having dirty context, since no action was taken yet */ - if (outputDirective == fillOutput && maxOutputSize < 1) { return 0; } /* Impossible to store anything */ - if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) { return 0; } /* Size too large (not within 64K limit) */ - if (tableType==byPtr) assert(dictDirective==noDict); /* only supported use case with byPtr */ - assert(acceleration >= 1); - - lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0); - - /* Update context state */ - if (dictDirective == usingDictCtx) { - /* Subsequent linked blocks can't use the dictionary. */ - /* Instead, they use the block we just compressed. */ - cctx->dictCtx = NULL; - cctx->dictSize = (U32)inputSize; - } else { - cctx->dictSize += (U32)inputSize; - } - cctx->currentOffset += (U32)inputSize; - cctx->tableType = (U32)tableType; - - if (inputSizehashTable, tableType, base); - ip++; forwardH = LZ4_hashPosition(ip, tableType); - - /* Main Loop */ - for ( ; ; ) { - const BYTE* match; - BYTE* token; - const BYTE* filledIp; - - /* Find a match */ - if (tableType == byPtr) { - const BYTE* forwardIp = ip; - int step = 1; - int searchMatchNb = acceleration << LZ4_skipTrigger; - do { - U32 const h = forwardH; - ip = forwardIp; - forwardIp += step; - step = (searchMatchNb++ >> LZ4_skipTrigger); - - if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; - assert(ip < mflimitPlusOne); - - match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base); - forwardH = LZ4_hashPosition(forwardIp, tableType); - LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base); - - } while ( (match+LZ4_DISTANCE_MAX < ip) - || (LZ4_read32(match) != LZ4_read32(ip)) ); - - } else { /* byU32, byU16 */ - - const BYTE* forwardIp = ip; - int step = 1; - int searchMatchNb = acceleration << LZ4_skipTrigger; - do { - U32 const h = forwardH; - U32 const current = (U32)(forwardIp - base); - U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); - assert(matchIndex <= current); - assert(forwardIp - base < (ptrdiff_t)(2 GB - 1)); - ip = forwardIp; - forwardIp += step; - step = (searchMatchNb++ >> LZ4_skipTrigger); - - if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; - assert(ip < mflimitPlusOne); - - if (dictDirective == usingDictCtx) { - if (matchIndex < startIndex) { - /* there was no match, try the dictionary */ - assert(tableType == byU32); - matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); - match = dictBase + matchIndex; - matchIndex += dictDelta; /* make dictCtx index comparable with current context */ - lowLimit = dictionary; - } else { - match = base + matchIndex; - lowLimit = (const BYTE*)source; - } - } else if (dictDirective == usingExtDict) { - if (matchIndex < startIndex) { - DEBUGLOG(7, "extDict candidate: matchIndex=%5u < startIndex=%5u", matchIndex, startIndex); - assert(startIndex - matchIndex >= MINMATCH); - assert(dictBase); - match = dictBase + matchIndex; - lowLimit = dictionary; - } else { - match = base + matchIndex; - lowLimit = (const BYTE*)source; - } - } else { /* single continuous memory segment */ - match = base + matchIndex; - } - forwardH = LZ4_hashPosition(forwardIp, tableType); - LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); - - DEBUGLOG(7, "candidate at pos=%u (offset=%u \n", matchIndex, current - matchIndex); - if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) { continue; } /* match outside of valid area */ - assert(matchIndex < current); - if ( ((tableType != byU16) || (LZ4_DISTANCE_MAX < LZ4_DISTANCE_ABSOLUTE_MAX)) - && (matchIndex+LZ4_DISTANCE_MAX < current)) { - continue; - } /* too far */ - assert((current - matchIndex) <= LZ4_DISTANCE_MAX); /* match now expected within distance */ - - if (LZ4_read32(match) == LZ4_read32(ip)) { - if (maybe_extMem) offset = current - matchIndex; - break; /* match found */ - } - - } while(1); - } - - /* Catch up */ - filledIp = ip; - while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; } - - /* Encode Literals */ - { unsigned const litLength = (unsigned)(ip - anchor); - token = op++; - if ((outputDirective == limitedOutput) && /* Check output buffer overflow */ - (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)) ) { - return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ - } - if ((outputDirective == fillOutput) && - (unlikely(op + (litLength+240)/255 /* litlen */ + litLength /* literals */ + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit))) { - op--; - goto _last_literals; - } - if (litLength >= RUN_MASK) { - int len = (int)(litLength - RUN_MASK); - *token = (RUN_MASK<= 255 ; len-=255) *op++ = 255; - *op++ = (BYTE)len; - } - else *token = (BYTE)(litLength< olimit)) { - /* the match was too close to the end, rewind and go to last literals */ - op = token; - goto _last_literals; - } - - /* Encode Offset */ - if (maybe_extMem) { /* static test */ - DEBUGLOG(6, " with offset=%u (ext if > %i)", offset, (int)(ip - (const BYTE*)source)); - assert(offset <= LZ4_DISTANCE_MAX && offset > 0); - LZ4_writeLE16(op, (U16)offset); op+=2; - } else { - DEBUGLOG(6, " with offset=%u (same segment)", (U32)(ip - match)); - assert(ip-match <= LZ4_DISTANCE_MAX); - LZ4_writeLE16(op, (U16)(ip - match)); op+=2; - } - - /* Encode MatchLength */ - { unsigned matchCode; - - if ( (dictDirective==usingExtDict || dictDirective==usingDictCtx) - && (lowLimit==dictionary) /* match within extDict */ ) { - const BYTE* limit = ip + (dictEnd-match); - assert(dictEnd > match); - if (limit > matchlimit) limit = matchlimit; - matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit); - ip += (size_t)matchCode + MINMATCH; - if (ip==limit) { - unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit); - matchCode += more; - ip += more; - } - DEBUGLOG(6, " with matchLength=%u starting in extDict", matchCode+MINMATCH); - } else { - matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit); - ip += (size_t)matchCode + MINMATCH; - DEBUGLOG(6, " with matchLength=%u", matchCode+MINMATCH); - } - - if ((outputDirective) && /* Check output buffer overflow */ - (unlikely(op + (1 + LASTLITERALS) + (matchCode+240)/255 > olimit)) ) { - if (outputDirective == fillOutput) { - /* Match description too long : reduce it */ - U32 newMatchCode = 15 /* in token */ - 1 /* to avoid needing a zero byte */ + ((U32)(olimit - op) - 1 - LASTLITERALS) * 255; - ip -= matchCode - newMatchCode; - assert(newMatchCode < matchCode); - matchCode = newMatchCode; - if (unlikely(ip <= filledIp)) { - /* We have already filled up to filledIp so if ip ends up less than filledIp - * we have positions in the hash table beyond the current position. This is - * a problem if we reuse the hash table. So we have to remove these positions - * from the hash table. - */ - const BYTE* ptr; - DEBUGLOG(5, "Clearing %u positions", (U32)(filledIp - ip)); - for (ptr = ip; ptr <= filledIp; ++ptr) { - U32 const h = LZ4_hashPosition(ptr, tableType); - LZ4_clearHash(h, cctx->hashTable, tableType); - } - } - } else { - assert(outputDirective == limitedOutput); - return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ - } - } - if (matchCode >= ML_MASK) { - *token += ML_MASK; - matchCode -= ML_MASK; - LZ4_write32(op, 0xFFFFFFFF); - while (matchCode >= 4*255) { - op+=4; - LZ4_write32(op, 0xFFFFFFFF); - matchCode -= 4*255; - } - op += matchCode / 255; - *op++ = (BYTE)(matchCode % 255); - } else - *token += (BYTE)(matchCode); - } - /* Ensure we have enough space for the last literals. */ - assert(!(outputDirective == fillOutput && op + 1 + LASTLITERALS > olimit)); - - anchor = ip; - - /* Test end of chunk */ - if (ip >= mflimitPlusOne) break; - - /* Fill table */ - LZ4_putPosition(ip-2, cctx->hashTable, tableType, base); - - /* Test next position */ - if (tableType == byPtr) { - - match = LZ4_getPosition(ip, cctx->hashTable, tableType, base); - LZ4_putPosition(ip, cctx->hashTable, tableType, base); - if ( (match+LZ4_DISTANCE_MAX >= ip) - && (LZ4_read32(match) == LZ4_read32(ip)) ) - { token=op++; *token=0; goto _next_match; } - - } else { /* byU32, byU16 */ - - U32 const h = LZ4_hashPosition(ip, tableType); - U32 const current = (U32)(ip-base); - U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); - assert(matchIndex < current); - if (dictDirective == usingDictCtx) { - if (matchIndex < startIndex) { - /* there was no match, try the dictionary */ - matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); - match = dictBase + matchIndex; - lowLimit = dictionary; /* required for match length counter */ - matchIndex += dictDelta; - } else { - match = base + matchIndex; - lowLimit = (const BYTE*)source; /* required for match length counter */ - } - } else if (dictDirective==usingExtDict) { - if (matchIndex < startIndex) { - assert(dictBase); - match = dictBase + matchIndex; - lowLimit = dictionary; /* required for match length counter */ - } else { - match = base + matchIndex; - lowLimit = (const BYTE*)source; /* required for match length counter */ - } - } else { /* single memory segment */ - match = base + matchIndex; - } - LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); - assert(matchIndex < current); - if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1) - && (((tableType==byU16) && (LZ4_DISTANCE_MAX == LZ4_DISTANCE_ABSOLUTE_MAX)) ? 1 : (matchIndex+LZ4_DISTANCE_MAX >= current)) - && (LZ4_read32(match) == LZ4_read32(ip)) ) { - token=op++; - *token=0; - if (maybe_extMem) offset = current - matchIndex; - DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i", - (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source)); - goto _next_match; - } - } - - /* Prepare next loop */ - forwardH = LZ4_hashPosition(++ip, tableType); - - } - -_last_literals: - /* Encode Last Literals */ - { size_t lastRun = (size_t)(iend - anchor); - if ( (outputDirective) && /* Check output buffer overflow */ - (op + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > olimit)) { - if (outputDirective == fillOutput) { - /* adapt lastRun to fill 'dst' */ - assert(olimit >= op); - lastRun = (size_t)(olimit-op) - 1/*token*/; - lastRun -= (lastRun + 256 - RUN_MASK) / 256; /*additional length tokens*/ - } else { - assert(outputDirective == limitedOutput); - return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ - } - } - DEBUGLOG(6, "Final literal run : %i literals", (int)lastRun); - if (lastRun >= RUN_MASK) { - size_t accumulator = lastRun - RUN_MASK; - *op++ = RUN_MASK << ML_BITS; - for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; - *op++ = (BYTE) accumulator; - } else { - *op++ = (BYTE)(lastRun< 0); - DEBUGLOG(5, "LZ4_compress_generic: compressed %i bytes into %i bytes", inputSize, result); - return result; -} - -/** LZ4_compress_generic() : - * inlined, to ensure branches are decided at compilation time; - * takes care of src == (NULL, 0) - * and forward the rest to LZ4_compress_generic_validated */ -LZ4_FORCE_INLINE int LZ4_compress_generic( - LZ4_stream_t_internal* const cctx, - const char* const src, - char* const dst, - const int srcSize, - int *inputConsumed, /* only written when outputDirective == fillOutput */ - const int dstCapacity, - const limitedOutput_directive outputDirective, - const tableType_t tableType, - const dict_directive dictDirective, - const dictIssue_directive dictIssue, - const int acceleration) -{ - DEBUGLOG(5, "LZ4_compress_generic: srcSize=%i, dstCapacity=%i", - srcSize, dstCapacity); - - if ((U32)srcSize > (U32)LZ4_MAX_INPUT_SIZE) { return 0; } /* Unsupported srcSize, too large (or negative) */ - if (srcSize == 0) { /* src == NULL supported if srcSize == 0 */ - if (outputDirective != notLimited && dstCapacity <= 0) return 0; /* no output, can't write anything */ - DEBUGLOG(5, "Generating an empty block"); - assert(outputDirective == notLimited || dstCapacity >= 1); - assert(dst != NULL); - dst[0] = 0; - if (outputDirective == fillOutput) { - assert (inputConsumed != NULL); - *inputConsumed = 0; - } - return 1; - } - assert(src != NULL); - - return LZ4_compress_generic_validated(cctx, src, dst, srcSize, - inputConsumed, /* only written into if outputDirective == fillOutput */ - dstCapacity, outputDirective, - tableType, dictDirective, dictIssue, acceleration); -} - - -int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) -{ - LZ4_stream_t_internal* const ctx = & LZ4_initStream(state, sizeof(LZ4_stream_t)) -> internal_donotuse; - assert(ctx != NULL); - if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; - if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; - if (maxOutputSize >= LZ4_compressBound(inputSize)) { - if (inputSize < LZ4_64Klimit) { - return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration); - } else { - const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32; - return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); - } - } else { - if (inputSize < LZ4_64Klimit) { - return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); - } else { - const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32; - return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration); - } - } -} - -/** - * LZ4_compress_fast_extState_fastReset() : - * A variant of LZ4_compress_fast_extState(). - * - * Using this variant avoids an expensive initialization step. It is only safe - * to call if the state buffer is known to be correctly initialized already - * (see comment in lz4.h on LZ4_resetStream_fast() for a definition of - * "correctly initialized"). - */ -int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration) -{ - LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse; - if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; - if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; - - if (dstCapacity >= LZ4_compressBound(srcSize)) { - if (srcSize < LZ4_64Klimit) { - const tableType_t tableType = byU16; - LZ4_prepareTable(ctx, srcSize, tableType); - if (ctx->currentOffset) { - return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, dictSmall, acceleration); - } else { - return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); - } - } else { - const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; - LZ4_prepareTable(ctx, srcSize, tableType); - return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); - } - } else { - if (srcSize < LZ4_64Klimit) { - const tableType_t tableType = byU16; - LZ4_prepareTable(ctx, srcSize, tableType); - if (ctx->currentOffset) { - return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration); - } else { - return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration); - } - } else { - const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; - LZ4_prepareTable(ctx, srcSize, tableType); - return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration); - } - } -} - - -int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) -{ - int result; -#if (LZ4_HEAPMODE) - LZ4_stream_t* ctxPtr = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ - if (ctxPtr == NULL) return 0; -#else - LZ4_stream_t ctx; - LZ4_stream_t* const ctxPtr = &ctx; -#endif - result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration); - -#if (LZ4_HEAPMODE) - FREEMEM(ctxPtr); -#endif - return result; -} - - -int LZ4_compress_default(const char* src, char* dst, int srcSize, int maxOutputSize) -{ - return LZ4_compress_fast(src, dst, srcSize, maxOutputSize, 1); -} - - -/* Note!: This function leaves the stream in an unclean/broken state! - * It is not safe to subsequently use the same state with a _fastReset() or - * _continue() call without resetting it. */ -static int LZ4_compress_destSize_extState (LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize) -{ - void* const s = LZ4_initStream(state, sizeof (*state)); - assert(s != NULL); (void)s; - - if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */ - return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1); - } else { - if (*srcSizePtr < LZ4_64Klimit) { - return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, 1); - } else { - tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; - return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, 1); - } } -} - - -int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize) -{ -#if (LZ4_HEAPMODE) - LZ4_stream_t* ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ - if (ctx == NULL) return 0; -#else - LZ4_stream_t ctxBody; - LZ4_stream_t* ctx = &ctxBody; -#endif - - int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize); - -#if (LZ4_HEAPMODE) - FREEMEM(ctx); -#endif - return result; -} - - - -/*-****************************** -* Streaming functions -********************************/ - -LZ4_stream_t* LZ4_createStream(void) -{ - LZ4_stream_t* const lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); - LZ4_STATIC_ASSERT(LZ4_STREAMSIZE >= sizeof(LZ4_stream_t_internal)); /* A compilation error here means LZ4_STREAMSIZE is not large enough */ - DEBUGLOG(4, "LZ4_createStream %p", lz4s); - if (lz4s == NULL) return NULL; - LZ4_initStream(lz4s, sizeof(*lz4s)); - return lz4s; -} - -static size_t LZ4_stream_t_alignment(void) -{ -#if LZ4_ALIGN_TEST - typedef struct { char c; LZ4_stream_t t; } t_a; - return sizeof(t_a) - sizeof(LZ4_stream_t); -#else - return 1; /* effectively disabled */ -#endif -} - -LZ4_stream_t* LZ4_initStream (void* buffer, size_t size) -{ - DEBUGLOG(5, "LZ4_initStream"); - if (buffer == NULL) { return NULL; } - if (size < sizeof(LZ4_stream_t)) { return NULL; } - if (!LZ4_isAligned(buffer, LZ4_stream_t_alignment())) return NULL; - MEM_INIT(buffer, 0, sizeof(LZ4_stream_t_internal)); - return (LZ4_stream_t*)buffer; -} - -/* resetStream is now deprecated, - * prefer initStream() which is more general */ -void LZ4_resetStream (LZ4_stream_t* LZ4_stream) -{ - DEBUGLOG(5, "LZ4_resetStream (ctx:%p)", LZ4_stream); - MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t_internal)); -} - -void LZ4_resetStream_fast(LZ4_stream_t* ctx) { - LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32); -} - -int LZ4_freeStream (LZ4_stream_t* LZ4_stream) -{ - if (!LZ4_stream) return 0; /* support free on NULL */ - DEBUGLOG(5, "LZ4_freeStream %p", LZ4_stream); - FREEMEM(LZ4_stream); - return (0); -} - - -#define HASH_UNIT sizeof(reg_t) -int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) -{ - LZ4_stream_t_internal* dict = &LZ4_dict->internal_donotuse; - const tableType_t tableType = byU32; - const BYTE* p = (const BYTE*)dictionary; - const BYTE* const dictEnd = p + dictSize; - const BYTE* base; - - DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, dictionary, LZ4_dict); - - /* It's necessary to reset the context, - * and not just continue it with prepareTable() - * to avoid any risk of generating overflowing matchIndex - * when compressing using this dictionary */ - LZ4_resetStream(LZ4_dict); - - /* We always increment the offset by 64 KB, since, if the dict is longer, - * we truncate it to the last 64k, and if it's shorter, we still want to - * advance by a whole window length so we can provide the guarantee that - * there are only valid offsets in the window, which allows an optimization - * in LZ4_compress_fast_continue() where it uses noDictIssue even when the - * dictionary isn't a full 64k. */ - dict->currentOffset += 64 KB; - - if (dictSize < (int)HASH_UNIT) { - return 0; - } - - if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB; - base = dictEnd - dict->currentOffset; - dict->dictionary = p; - dict->dictSize = (U32)(dictEnd - p); - dict->tableType = (U32)tableType; - - while (p <= dictEnd-HASH_UNIT) { - LZ4_putPosition(p, dict->hashTable, tableType, base); - p+=3; - } - - return (int)dict->dictSize; -} - -void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream) -{ - const LZ4_stream_t_internal* dictCtx = (dictionaryStream == NULL) ? NULL : - &(dictionaryStream->internal_donotuse); - - DEBUGLOG(4, "LZ4_attach_dictionary (%p, %p, size %u)", - workingStream, dictionaryStream, - dictCtx != NULL ? dictCtx->dictSize : 0); - - if (dictCtx != NULL) { - /* If the current offset is zero, we will never look in the - * external dictionary context, since there is no value a table - * entry can take that indicate a miss. In that case, we need - * to bump the offset to something non-zero. - */ - if (workingStream->internal_donotuse.currentOffset == 0) { - workingStream->internal_donotuse.currentOffset = 64 KB; - } - - /* Don't actually attach an empty dictionary. - */ - if (dictCtx->dictSize == 0) { - dictCtx = NULL; - } - } - workingStream->internal_donotuse.dictCtx = dictCtx; -} - - -static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, int nextSize) -{ - assert(nextSize >= 0); - if (LZ4_dict->currentOffset + (unsigned)nextSize > 0x80000000) { /* potential ptrdiff_t overflow (32-bits mode) */ - /* rescale hash table */ - U32 const delta = LZ4_dict->currentOffset - 64 KB; - const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize; - int i; - DEBUGLOG(4, "LZ4_renormDictT"); - for (i=0; ihashTable[i] < delta) LZ4_dict->hashTable[i]=0; - else LZ4_dict->hashTable[i] -= delta; - } - LZ4_dict->currentOffset = 64 KB; - if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB; - LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize; - } -} - - -int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, - const char* source, char* dest, - int inputSize, int maxOutputSize, - int acceleration) -{ - const tableType_t tableType = byU32; - LZ4_stream_t_internal* const streamPtr = &LZ4_stream->internal_donotuse; - const char* dictEnd = streamPtr->dictSize ? (const char*)streamPtr->dictionary + streamPtr->dictSize : NULL; - - DEBUGLOG(5, "LZ4_compress_fast_continue (inputSize=%i, dictSize=%u)", inputSize, streamPtr->dictSize); - - LZ4_renormDictT(streamPtr, inputSize); /* fix index overflow */ - if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; - if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; - - /* invalidate tiny dictionaries */ - if ( (streamPtr->dictSize < 4) /* tiny dictionary : not enough for a hash */ - && (dictEnd != source) /* prefix mode */ - && (inputSize > 0) /* tolerance : don't lose history, in case next invocation would use prefix mode */ - && (streamPtr->dictCtx == NULL) /* usingDictCtx */ - ) { - DEBUGLOG(5, "LZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small", streamPtr->dictSize, streamPtr->dictionary); - /* remove dictionary existence from history, to employ faster prefix mode */ - streamPtr->dictSize = 0; - streamPtr->dictionary = (const BYTE*)source; - dictEnd = source; - } - - /* Check overlapping input/dictionary space */ - { const char* const sourceEnd = source + inputSize; - if ((sourceEnd > (const char*)streamPtr->dictionary) && (sourceEnd < dictEnd)) { - streamPtr->dictSize = (U32)(dictEnd - sourceEnd); - if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB; - if (streamPtr->dictSize < 4) streamPtr->dictSize = 0; - streamPtr->dictionary = (const BYTE*)dictEnd - streamPtr->dictSize; - } - } - - /* prefix mode : source data follows dictionary */ - if (dictEnd == source) { - if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) - return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration); - else - return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration); - } - - /* external dictionary mode */ - { int result; - if (streamPtr->dictCtx) { - /* We depend here on the fact that dictCtx'es (produced by - * LZ4_loadDict) guarantee that their tables contain no references - * to offsets between dictCtx->currentOffset - 64 KB and - * dictCtx->currentOffset - dictCtx->dictSize. This makes it safe - * to use noDictIssue even when the dict isn't a full 64 KB. - */ - if (inputSize > 4 KB) { - /* For compressing large blobs, it is faster to pay the setup - * cost to copy the dictionary's tables into the active context, - * so that the compression loop is only looking into one table. - */ - LZ4_memcpy(streamPtr, streamPtr->dictCtx, sizeof(*streamPtr)); - result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration); - } else { - result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration); - } - } else { /* small data <= 4 KB */ - if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) { - result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration); - } else { - result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration); - } - } - streamPtr->dictionary = (const BYTE*)source; - streamPtr->dictSize = (U32)inputSize; - return result; - } -} - - -/* Hidden debug function, to force-test external dictionary mode */ -int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize) -{ - LZ4_stream_t_internal* streamPtr = &LZ4_dict->internal_donotuse; - int result; - - LZ4_renormDictT(streamPtr, srcSize); - - if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) { - result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, dictSmall, 1); - } else { - result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, noDictIssue, 1); - } - - streamPtr->dictionary = (const BYTE*)source; - streamPtr->dictSize = (U32)srcSize; - - return result; -} - - -/*! LZ4_saveDict() : - * If previously compressed data block is not guaranteed to remain available at its memory location, - * save it into a safer place (char* safeBuffer). - * Note : no need to call LZ4_loadDict() afterwards, dictionary is immediately usable, - * one can therefore call LZ4_compress_fast_continue() right after. - * @return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error. - */ -int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize) -{ - LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse; - - DEBUGLOG(5, "LZ4_saveDict : dictSize=%i, safeBuffer=%p", dictSize, safeBuffer); - - if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */ - if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; } - - if (safeBuffer == NULL) assert(dictSize == 0); - if (dictSize > 0) { - const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize; - assert(dict->dictionary); - memmove(safeBuffer, previousDictEnd - dictSize, dictSize); - } - - dict->dictionary = (const BYTE*)safeBuffer; - dict->dictSize = (U32)dictSize; - - return dictSize; -} - - - -/*-******************************* - * Decompression functions - ********************************/ - -typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive; -typedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive; - -#undef MIN -#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) - -/* Read the variable-length literal or match length. - * - * ip - pointer to use as input. - * lencheck - end ip. Return an error if ip advances >= lencheck. - * loop_check - check ip >= lencheck in body of loop. Returns loop_error if so. - * initial_check - check ip >= lencheck before start of loop. Returns initial_error if so. - * error (output) - error code. Should be set to 0 before call. - */ -typedef enum { loop_error = -2, initial_error = -1, ok = 0 } variable_length_error; -LZ4_FORCE_INLINE unsigned -read_variable_length(const BYTE**ip, const BYTE* lencheck, - int loop_check, int initial_check, - variable_length_error* error) -{ - U32 length = 0; - U32 s; - if (initial_check && unlikely((*ip) >= lencheck)) { /* overflow detection */ - *error = initial_error; - return length; - } - do { - s = **ip; - (*ip)++; - length += s; - if (loop_check && unlikely((*ip) >= lencheck)) { /* overflow detection */ - *error = loop_error; - return length; - } - } while (s==255); - - return length; -} - -/*! LZ4_decompress_generic() : - * This generic decompression function covers all use cases. - * It shall be instantiated several times, using different sets of directives. - * Note that it is important for performance that this function really get inlined, - * in order to remove useless branches during compilation optimization. - */ -LZ4_FORCE_INLINE int -LZ4_decompress_generic( - const char* const src, - char* const dst, - int srcSize, - int outputSize, /* If endOnInput==endOnInputSize, this value is `dstCapacity` */ - - endCondition_directive endOnInput, /* endOnOutputSize, endOnInputSize */ - earlyEnd_directive partialDecoding, /* full, partial */ - dict_directive dict, /* noDict, withPrefix64k, usingExtDict */ - const BYTE* const lowPrefix, /* always <= dst, == dst when no prefix */ - const BYTE* const dictStart, /* only if dict==usingExtDict */ - const size_t dictSize /* note : = 0 if noDict */ - ) -{ - if ((src == NULL) || (outputSize < 0)) { return -1; } - - { const BYTE* ip = (const BYTE*) src; - const BYTE* const iend = ip + srcSize; - - BYTE* op = (BYTE*) dst; - BYTE* const oend = op + outputSize; - BYTE* cpy; - - const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize; - - const int safeDecode = (endOnInput==endOnInputSize); - const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB))); - - - /* Set up the "end" pointers for the shortcut. */ - const BYTE* const shortiend = iend - (endOnInput ? 14 : 8) /*maxLL*/ - 2 /*offset*/; - const BYTE* const shortoend = oend - (endOnInput ? 14 : 8) /*maxLL*/ - 18 /*maxML*/; - - const BYTE* match; - size_t offset; - unsigned token; - size_t length; - - - DEBUGLOG(5, "LZ4_decompress_generic (srcSize:%i, dstSize:%i)", srcSize, outputSize); - - /* Special cases */ - assert(lowPrefix <= op); - if ((endOnInput) && (unlikely(outputSize==0))) { - /* Empty output buffer */ - if (partialDecoding) return 0; - return ((srcSize==1) && (*ip==0)) ? 0 : -1; - } - if ((!endOnInput) && (unlikely(outputSize==0))) { return (*ip==0 ? 1 : -1); } - if ((endOnInput) && unlikely(srcSize==0)) { return -1; } - - /* Currently the fast loop shows a regression on qualcomm arm chips. */ -#if LZ4_FAST_DEC_LOOP - if ((oend - op) < FASTLOOP_SAFE_DISTANCE) { - DEBUGLOG(6, "skip fast decode loop"); - goto safe_decode; - } - - /* Fast loop : decode sequences as long as output < iend-FASTLOOP_SAFE_DISTANCE */ - while (1) { - /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */ - assert(oend - op >= FASTLOOP_SAFE_DISTANCE); - if (endOnInput) { assert(ip < iend); } - token = *ip++; - length = token >> ML_BITS; /* literal length */ - - assert(!endOnInput || ip <= iend); /* ip < iend before the increment */ - - /* decode literal length */ - if (length == RUN_MASK) { - variable_length_error error = ok; - length += read_variable_length(&ip, iend-RUN_MASK, (int)endOnInput, (int)endOnInput, &error); - if (error == initial_error) { goto _output_error; } - if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ - if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ - - /* copy literals */ - cpy = op+length; - LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); - if (endOnInput) { /* LZ4_decompress_safe() */ - if ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; } - LZ4_wildCopy32(op, ip, cpy); - } else { /* LZ4_decompress_fast() */ - if (cpy>oend-8) { goto safe_literal_copy; } - LZ4_wildCopy8(op, ip, cpy); /* LZ4_decompress_fast() cannot copy more than 8 bytes at a time : - * it doesn't know input length, and only relies on end-of-block properties */ - } - ip += length; op = cpy; - } else { - cpy = op+length; - if (endOnInput) { /* LZ4_decompress_safe() */ - DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length); - /* We don't need to check oend, since we check it once for each loop below */ - if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; } - /* Literals can only be 14, but hope compilers optimize if we copy by a register size */ - LZ4_memcpy(op, ip, 16); - } else { /* LZ4_decompress_fast() */ - /* LZ4_decompress_fast() cannot copy more than 8 bytes at a time : - * it doesn't know input length, and relies on end-of-block properties */ - LZ4_memcpy(op, ip, 8); - if (length > 8) { LZ4_memcpy(op+8, ip+8, 8); } - } - ip += length; op = cpy; - } - - /* get offset */ - offset = LZ4_readLE16(ip); ip+=2; - match = op - offset; - assert(match <= op); - - /* get matchlength */ - length = token & ML_MASK; - - if (length == ML_MASK) { - variable_length_error error = ok; - if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ - length += read_variable_length(&ip, iend - LASTLITERALS + 1, (int)endOnInput, 0, &error); - if (error != ok) { goto _output_error; } - if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */ - length += MINMATCH; - if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { - goto safe_match_copy; - } - } else { - length += MINMATCH; - if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { - goto safe_match_copy; - } - - /* Fastpath check: Avoids a branch in LZ4_wildCopy32 if true */ - if ((dict == withPrefix64k) || (match >= lowPrefix)) { - if (offset >= 8) { - assert(match >= lowPrefix); - assert(match <= op); - assert(op + 18 <= oend); - - LZ4_memcpy(op, match, 8); - LZ4_memcpy(op+8, match+8, 8); - LZ4_memcpy(op+16, match+16, 2); - op += length; - continue; - } } } - - if (checkOffset && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ - /* match starting within external dictionary */ - if ((dict==usingExtDict) && (match < lowPrefix)) { - if (unlikely(op+length > oend-LASTLITERALS)) { - if (partialDecoding) { - DEBUGLOG(7, "partialDecoding: dictionary match, close to dstEnd"); - length = MIN(length, (size_t)(oend-op)); - } else { - goto _output_error; /* end-of-block condition violated */ - } } - - if (length <= (size_t)(lowPrefix-match)) { - /* match fits entirely within external dictionary : just copy */ - memmove(op, dictEnd - (lowPrefix-match), length); - op += length; - } else { - /* match stretches into both external dictionary and current block */ - size_t const copySize = (size_t)(lowPrefix - match); - size_t const restSize = length - copySize; - LZ4_memcpy(op, dictEnd - copySize, copySize); - op += copySize; - if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ - BYTE* const endOfMatch = op + restSize; - const BYTE* copyFrom = lowPrefix; - while (op < endOfMatch) { *op++ = *copyFrom++; } - } else { - LZ4_memcpy(op, lowPrefix, restSize); - op += restSize; - } } - continue; - } - - /* copy match within block */ - cpy = op + length; - - assert((op <= oend) && (oend-op >= 32)); - if (unlikely(offset<16)) { - LZ4_memcpy_using_offset(op, match, cpy, offset); - } else { - LZ4_wildCopy32(op, match, cpy); - } - - op = cpy; /* wildcopy correction */ - } - safe_decode: -#endif - - /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */ - while (1) { - token = *ip++; - length = token >> ML_BITS; /* literal length */ - - assert(!endOnInput || ip <= iend); /* ip < iend before the increment */ - - /* A two-stage shortcut for the most common case: - * 1) If the literal length is 0..14, and there is enough space, - * enter the shortcut and copy 16 bytes on behalf of the literals - * (in the fast mode, only 8 bytes can be safely copied this way). - * 2) Further if the match length is 4..18, copy 18 bytes in a similar - * manner; but we ensure that there's enough space in the output for - * those 18 bytes earlier, upon entering the shortcut (in other words, - * there is a combined check for both stages). - */ - if ( (endOnInput ? length != RUN_MASK : length <= 8) - /* strictly "less than" on input, to re-enter the loop with at least one byte */ - && likely((endOnInput ? ip < shortiend : 1) & (op <= shortoend)) ) { - /* Copy the literals */ - LZ4_memcpy(op, ip, endOnInput ? 16 : 8); - op += length; ip += length; - - /* The second stage: prepare for match copying, decode full info. - * If it doesn't work out, the info won't be wasted. */ - length = token & ML_MASK; /* match length */ - offset = LZ4_readLE16(ip); ip += 2; - match = op - offset; - assert(match <= op); /* check overflow */ - - /* Do not deal with overlapping matches. */ - if ( (length != ML_MASK) - && (offset >= 8) - && (dict==withPrefix64k || match >= lowPrefix) ) { - /* Copy the match. */ - LZ4_memcpy(op + 0, match + 0, 8); - LZ4_memcpy(op + 8, match + 8, 8); - LZ4_memcpy(op +16, match +16, 2); - op += length + MINMATCH; - /* Both stages worked, load the next token. */ - continue; - } - - /* The second stage didn't work out, but the info is ready. - * Propel it right to the point of match copying. */ - goto _copy_match; - } - - /* decode literal length */ - if (length == RUN_MASK) { - variable_length_error error = ok; - length += read_variable_length(&ip, iend-RUN_MASK, (int)endOnInput, (int)endOnInput, &error); - if (error == initial_error) { goto _output_error; } - if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ - if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ - } - - /* copy literals */ - cpy = op+length; -#if LZ4_FAST_DEC_LOOP - safe_literal_copy: -#endif - LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); - if ( ((endOnInput) && ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) ) - || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) ) - { - /* We've either hit the input parsing restriction or the output parsing restriction. - * In the normal scenario, decoding a full block, it must be the last sequence, - * otherwise it's an error (invalid input or dimensions). - * In partialDecoding scenario, it's necessary to ensure there is no buffer overflow. - */ - if (partialDecoding) { - /* Since we are partial decoding we may be in this block because of the output parsing - * restriction, which is not valid since the output buffer is allowed to be undersized. - */ - assert(endOnInput); - DEBUGLOG(7, "partialDecoding: copying literals, close to input or output end") - DEBUGLOG(7, "partialDecoding: literal length = %u", (unsigned)length); - DEBUGLOG(7, "partialDecoding: remaining space in dstBuffer : %i", (int)(oend - op)); - DEBUGLOG(7, "partialDecoding: remaining space in srcBuffer : %i", (int)(iend - ip)); - /* Finishing in the middle of a literals segment, - * due to lack of input. - */ - if (ip+length > iend) { - length = (size_t)(iend-ip); - cpy = op + length; - } - /* Finishing in the middle of a literals segment, - * due to lack of output space. - */ - if (cpy > oend) { - cpy = oend; - assert(op<=oend); - length = (size_t)(oend-op); - } - } else { - /* We must be on the last sequence because of the parsing limitations so check - * that we exactly regenerate the original size (must be exact when !endOnInput). - */ - if ((!endOnInput) && (cpy != oend)) { goto _output_error; } - /* We must be on the last sequence (or invalid) because of the parsing limitations - * so check that we exactly consume the input and don't overrun the output buffer. - */ - if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) { - DEBUGLOG(6, "should have been last run of literals") - DEBUGLOG(6, "ip(%p) + length(%i) = %p != iend (%p)", ip, (int)length, ip+length, iend); - DEBUGLOG(6, "or cpy(%p) > oend(%p)", cpy, oend); - goto _output_error; - } - } - memmove(op, ip, length); /* supports overlapping memory regions; only matters for in-place decompression scenarios */ - ip += length; - op += length; - /* Necessarily EOF when !partialDecoding. - * When partialDecoding, it is EOF if we've either - * filled the output buffer or - * can't proceed with reading an offset for following match. - */ - if (!partialDecoding || (cpy == oend) || (ip >= (iend-2))) { - break; - } - } else { - LZ4_wildCopy8(op, ip, cpy); /* may overwrite up to WILDCOPYLENGTH beyond cpy */ - ip += length; op = cpy; - } - - /* get offset */ - offset = LZ4_readLE16(ip); ip+=2; - match = op - offset; - - /* get matchlength */ - length = token & ML_MASK; - - _copy_match: - if (length == ML_MASK) { - variable_length_error error = ok; - length += read_variable_length(&ip, iend - LASTLITERALS + 1, (int)endOnInput, 0, &error); - if (error != ok) goto _output_error; - if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */ - } - length += MINMATCH; - -#if LZ4_FAST_DEC_LOOP - safe_match_copy: -#endif - if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error; /* Error : offset outside buffers */ - /* match starting within external dictionary */ - if ((dict==usingExtDict) && (match < lowPrefix)) { - if (unlikely(op+length > oend-LASTLITERALS)) { - if (partialDecoding) length = MIN(length, (size_t)(oend-op)); - else goto _output_error; /* doesn't respect parsing restriction */ - } - - if (length <= (size_t)(lowPrefix-match)) { - /* match fits entirely within external dictionary : just copy */ - memmove(op, dictEnd - (lowPrefix-match), length); - op += length; - } else { - /* match stretches into both external dictionary and current block */ - size_t const copySize = (size_t)(lowPrefix - match); - size_t const restSize = length - copySize; - LZ4_memcpy(op, dictEnd - copySize, copySize); - op += copySize; - if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ - BYTE* const endOfMatch = op + restSize; - const BYTE* copyFrom = lowPrefix; - while (op < endOfMatch) *op++ = *copyFrom++; - } else { - LZ4_memcpy(op, lowPrefix, restSize); - op += restSize; - } } - continue; - } - assert(match >= lowPrefix); - - /* copy match within block */ - cpy = op + length; - - /* partialDecoding : may end anywhere within the block */ - assert(op<=oend); - if (partialDecoding && (cpy > oend-MATCH_SAFEGUARD_DISTANCE)) { - size_t const mlen = MIN(length, (size_t)(oend-op)); - const BYTE* const matchEnd = match + mlen; - BYTE* const copyEnd = op + mlen; - if (matchEnd > op) { /* overlap copy */ - while (op < copyEnd) { *op++ = *match++; } - } else { - LZ4_memcpy(op, match, mlen); - } - op = copyEnd; - if (op == oend) { break; } - continue; - } - - if (unlikely(offset<8)) { - LZ4_write32(op, 0); /* silence msan warning when offset==0 */ - op[0] = match[0]; - op[1] = match[1]; - op[2] = match[2]; - op[3] = match[3]; - match += inc32table[offset]; - LZ4_memcpy(op+4, match, 4); - match -= dec64table[offset]; - } else { - LZ4_memcpy(op, match, 8); - match += 8; - } - op += 8; - - if (unlikely(cpy > oend-MATCH_SAFEGUARD_DISTANCE)) { - BYTE* const oCopyLimit = oend - (WILDCOPYLENGTH-1); - if (cpy > oend-LASTLITERALS) { goto _output_error; } /* Error : last LASTLITERALS bytes must be literals (uncompressed) */ - if (op < oCopyLimit) { - LZ4_wildCopy8(op, match, oCopyLimit); - match += oCopyLimit - op; - op = oCopyLimit; - } - while (op < cpy) { *op++ = *match++; } - } else { - LZ4_memcpy(op, match, 8); - if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); } - } - op = cpy; /* wildcopy correction */ - } - - /* end of decoding */ - if (endOnInput) { - DEBUGLOG(5, "decoded %i bytes", (int) (((char*)op)-dst)); - return (int) (((char*)op)-dst); /* Nb of output bytes decoded */ - } else { - return (int) (((const char*)ip)-src); /* Nb of input bytes read */ - } - - /* Overflow error detected */ - _output_error: - return (int) (-(((const char*)ip)-src))-1; - } -} - - -/*===== Instantiate the API decoding functions. =====*/ - -LZ4_FORCE_O2 -int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize) -{ - return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, - endOnInputSize, decode_full_block, noDict, - (BYTE*)dest, NULL, 0); -} - -LZ4_FORCE_O2 -int LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, int targetOutputSize, int dstCapacity) -{ - dstCapacity = MIN(targetOutputSize, dstCapacity); - return LZ4_decompress_generic(src, dst, compressedSize, dstCapacity, - endOnInputSize, partial_decode, - noDict, (BYTE*)dst, NULL, 0); -} - -LZ4_FORCE_O2 -int LZ4_decompress_fast(const char* source, char* dest, int originalSize) -{ - return LZ4_decompress_generic(source, dest, 0, originalSize, - endOnOutputSize, decode_full_block, withPrefix64k, - (BYTE*)dest - 64 KB, NULL, 0); -} - -/*===== Instantiate a few more decoding cases, used more than once. =====*/ - -LZ4_FORCE_O2 /* Exported, an obsolete API function. */ -int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize) -{ - return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, - endOnInputSize, decode_full_block, withPrefix64k, - (BYTE*)dest - 64 KB, NULL, 0); -} - -/* Another obsolete API function, paired with the previous one. */ -int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize) -{ - /* LZ4_decompress_fast doesn't validate match offsets, - * and thus serves well with any prefixed dictionary. */ - return LZ4_decompress_fast(source, dest, originalSize); -} - -LZ4_FORCE_O2 -static int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, int compressedSize, int maxOutputSize, - size_t prefixSize) -{ - return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, - endOnInputSize, decode_full_block, noDict, - (BYTE*)dest-prefixSize, NULL, 0); -} - -LZ4_FORCE_O2 -int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, - int compressedSize, int maxOutputSize, - const void* dictStart, size_t dictSize) -{ - return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, - endOnInputSize, decode_full_block, usingExtDict, - (BYTE*)dest, (const BYTE*)dictStart, dictSize); -} - -LZ4_FORCE_O2 -static int LZ4_decompress_fast_extDict(const char* source, char* dest, int originalSize, - const void* dictStart, size_t dictSize) -{ - return LZ4_decompress_generic(source, dest, 0, originalSize, - endOnOutputSize, decode_full_block, usingExtDict, - (BYTE*)dest, (const BYTE*)dictStart, dictSize); -} - -/* The "double dictionary" mode, for use with e.g. ring buffers: the first part - * of the dictionary is passed as prefix, and the second via dictStart + dictSize. - * These routines are used only once, in LZ4_decompress_*_continue(). - */ -LZ4_FORCE_INLINE -int LZ4_decompress_safe_doubleDict(const char* source, char* dest, int compressedSize, int maxOutputSize, - size_t prefixSize, const void* dictStart, size_t dictSize) -{ - return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, - endOnInputSize, decode_full_block, usingExtDict, - (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize); -} - -LZ4_FORCE_INLINE -int LZ4_decompress_fast_doubleDict(const char* source, char* dest, int originalSize, - size_t prefixSize, const void* dictStart, size_t dictSize) -{ - return LZ4_decompress_generic(source, dest, 0, originalSize, - endOnOutputSize, decode_full_block, usingExtDict, - (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize); -} - -/*===== streaming decompression functions =====*/ - -LZ4_streamDecode_t* LZ4_createStreamDecode(void) -{ - LZ4_streamDecode_t* lz4s = (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t)); - LZ4_STATIC_ASSERT(LZ4_STREAMDECODESIZE >= sizeof(LZ4_streamDecode_t_internal)); /* A compilation error here means LZ4_STREAMDECODESIZE is not large enough */ - return lz4s; -} - -int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream) -{ - if (LZ4_stream == NULL) { return 0; } /* support free on NULL */ - FREEMEM(LZ4_stream); - return 0; -} - -/*! LZ4_setStreamDecode() : - * Use this function to instruct where to find the dictionary. - * This function is not necessary if previous data is still available where it was decoded. - * Loading a size of 0 is allowed (same effect as no dictionary). - * @return : 1 if OK, 0 if error - */ -int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize) -{ - LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; - lz4sd->prefixSize = (size_t)dictSize; - if (dictSize) { - assert(dictionary != NULL); - lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize; - } else { - lz4sd->prefixEnd = (const BYTE*) dictionary; - } - lz4sd->externalDict = NULL; - lz4sd->extDictSize = 0; - return 1; -} - -/*! LZ4_decoderRingBufferSize() : - * when setting a ring buffer for streaming decompression (optional scenario), - * provides the minimum size of this ring buffer - * to be compatible with any source respecting maxBlockSize condition. - * Note : in a ring buffer scenario, - * blocks are presumed decompressed next to each other. - * When not enough space remains for next block (remainingSize < maxBlockSize), - * decoding resumes from beginning of ring buffer. - * @return : minimum ring buffer size, - * or 0 if there is an error (invalid maxBlockSize). - */ -int LZ4_decoderRingBufferSize(int maxBlockSize) -{ - if (maxBlockSize < 0) return 0; - if (maxBlockSize > LZ4_MAX_INPUT_SIZE) return 0; - if (maxBlockSize < 16) maxBlockSize = 16; - return LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize); -} - -/* -*_continue() : - These decoding functions allow decompression of multiple blocks in "streaming" mode. - Previously decoded blocks must still be available at the memory position where they were decoded. - If it's not possible, save the relevant part of decoded data into a safe buffer, - and indicate where it stands using LZ4_setStreamDecode() -*/ -LZ4_FORCE_O2 -int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize) -{ - LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; - int result; - - if (lz4sd->prefixSize == 0) { - /* The first call, no dictionary yet. */ - assert(lz4sd->extDictSize == 0); - result = LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize); - if (result <= 0) return result; - lz4sd->prefixSize = (size_t)result; - lz4sd->prefixEnd = (BYTE*)dest + result; - } else if (lz4sd->prefixEnd == (BYTE*)dest) { - /* They're rolling the current segment. */ - if (lz4sd->prefixSize >= 64 KB - 1) - result = LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize); - else if (lz4sd->extDictSize == 0) - result = LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, - lz4sd->prefixSize); - else - result = LZ4_decompress_safe_doubleDict(source, dest, compressedSize, maxOutputSize, - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); - if (result <= 0) return result; - lz4sd->prefixSize += (size_t)result; - lz4sd->prefixEnd += result; - } else { - /* The buffer wraps around, or they're switching to another buffer. */ - lz4sd->extDictSize = lz4sd->prefixSize; - lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; - result = LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, - lz4sd->externalDict, lz4sd->extDictSize); - if (result <= 0) return result; - lz4sd->prefixSize = (size_t)result; - lz4sd->prefixEnd = (BYTE*)dest + result; - } - - return result; -} - -LZ4_FORCE_O2 -int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize) -{ - LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; - int result; - assert(originalSize >= 0); - - if (lz4sd->prefixSize == 0) { - assert(lz4sd->extDictSize == 0); - result = LZ4_decompress_fast(source, dest, originalSize); - if (result <= 0) return result; - lz4sd->prefixSize = (size_t)originalSize; - lz4sd->prefixEnd = (BYTE*)dest + originalSize; - } else if (lz4sd->prefixEnd == (BYTE*)dest) { - if (lz4sd->prefixSize >= 64 KB - 1 || lz4sd->extDictSize == 0) - result = LZ4_decompress_fast(source, dest, originalSize); - else - result = LZ4_decompress_fast_doubleDict(source, dest, originalSize, - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); - if (result <= 0) return result; - lz4sd->prefixSize += (size_t)originalSize; - lz4sd->prefixEnd += originalSize; - } else { - lz4sd->extDictSize = lz4sd->prefixSize; - lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; - result = LZ4_decompress_fast_extDict(source, dest, originalSize, - lz4sd->externalDict, lz4sd->extDictSize); - if (result <= 0) return result; - lz4sd->prefixSize = (size_t)originalSize; - lz4sd->prefixEnd = (BYTE*)dest + originalSize; - } - - return result; -} - - -/* -Advanced decoding functions : -*_usingDict() : - These decoding functions work the same as "_continue" ones, - the dictionary must be explicitly provided within parameters -*/ - -int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) -{ - if (dictSize==0) - return LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize); - if (dictStart+dictSize == dest) { - if (dictSize >= 64 KB - 1) { - return LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize); - } - assert(dictSize >= 0); - return LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, (size_t)dictSize); - } - assert(dictSize >= 0); - return LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, dictStart, (size_t)dictSize); -} - -int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize) -{ - if (dictSize==0 || dictStart+dictSize == dest) - return LZ4_decompress_fast(source, dest, originalSize); - assert(dictSize >= 0); - return LZ4_decompress_fast_extDict(source, dest, originalSize, dictStart, (size_t)dictSize); -} - - -/*=************************************************* -* Obsolete Functions -***************************************************/ -/* obsolete compression functions */ -int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) -{ - return LZ4_compress_default(source, dest, inputSize, maxOutputSize); -} -int LZ4_compress(const char* src, char* dest, int srcSize) -{ - return LZ4_compress_default(src, dest, srcSize, LZ4_compressBound(srcSize)); -} -int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) -{ - return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); -} -int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) -{ - return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); -} -int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int dstCapacity) -{ - return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, dstCapacity, 1); -} -int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) -{ - return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); -} - -/* -These decompression functions are deprecated and should no longer be used. -They are only provided here for compatibility with older user programs. -- LZ4_uncompress is totally equivalent to LZ4_decompress_fast -- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe -*/ -int LZ4_uncompress (const char* source, char* dest, int outputSize) -{ - return LZ4_decompress_fast(source, dest, outputSize); -} -int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) -{ - return LZ4_decompress_safe(source, dest, isize, maxOutputSize); -} - -/* Obsolete Streaming functions */ - -int LZ4_sizeofStreamState(void) { return LZ4_STREAMSIZE; } - -int LZ4_resetStreamState(void* state, char* inputBuffer) -{ - (void)inputBuffer; - LZ4_resetStream((LZ4_stream_t*)state); - return 0; -} - -void* LZ4_create (char* inputBuffer) -{ - (void)inputBuffer; - return LZ4_createStream(); -} - -char* LZ4_slideInputBuffer (void* state) -{ - /* avoid const char * -> char * conversion warning */ - return (char *)(uptrval)((LZ4_stream_t*)state)->internal_donotuse.dictionary; -} - -#endif /* LZ4_COMMONDEFS_ONLY */ diff --git a/rres/external/lz4.h b/rres/external/lz4.h deleted file mode 100644 index 7c401f6..0000000 --- a/rres/external/lz4.h +++ /dev/null @@ -1,785 +0,0 @@ -/* - * LZ4 - Fast LZ compression algorithm - * Header File - * Copyright (C) 2011-2020, Yann Collet. - - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - You can contact the author at : - - LZ4 homepage : http://www.lz4.org - - LZ4 source repository : https://github.com/lz4/lz4 -*/ -#if defined (__cplusplus) -extern "C" { -#endif - -#ifndef LZ4_H_2983827168210 -#define LZ4_H_2983827168210 - -/* --- Dependency --- */ -#include /* size_t */ - - -/** - Introduction - - LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core, - scalable with multi-cores CPU. It features an extremely fast decoder, with speed in - multiple GB/s per core, typically reaching RAM speed limits on multi-core systems. - - The LZ4 compression library provides in-memory compression and decompression functions. - It gives full buffer control to user. - Compression can be done in: - - a single step (described as Simple Functions) - - a single step, reusing a context (described in Advanced Functions) - - unbounded multiple steps (described as Streaming compression) - - lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md). - Decompressing such a compressed block requires additional metadata. - Exact metadata depends on exact decompression function. - For the typical case of LZ4_decompress_safe(), - metadata includes block's compressed size, and maximum bound of decompressed size. - Each application is free to encode and pass such metadata in whichever way it wants. - - lz4.h only handle blocks, it can not generate Frames. - - Blocks are different from Frames (doc/lz4_Frame_format.md). - Frames bundle both blocks and metadata in a specified manner. - Embedding metadata is required for compressed data to be self-contained and portable. - Frame format is delivered through a companion API, declared in lz4frame.h. - The `lz4` CLI can only manage frames. -*/ - -/*^*************************************************************** -* Export parameters -*****************************************************************/ -/* -* LZ4_DLL_EXPORT : -* Enable exporting of functions when building a Windows DLL -* LZ4LIB_VISIBILITY : -* Control library symbols visibility. -*/ -#ifndef LZ4LIB_VISIBILITY -# if defined(__GNUC__) && (__GNUC__ >= 4) -# define LZ4LIB_VISIBILITY __attribute__ ((visibility ("default"))) -# else -# define LZ4LIB_VISIBILITY -# endif -#endif -#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1) -# define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY -#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1) -# define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ -#else -# define LZ4LIB_API LZ4LIB_VISIBILITY -#endif - -/*------ Version ------*/ -#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ -#define LZ4_VERSION_MINOR 9 /* for new (non-breaking) interface capabilities */ -#define LZ4_VERSION_RELEASE 3 /* for tweaks, bug-fixes, or development */ - -#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) - -#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE -#define LZ4_QUOTE(str) #str -#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str) -#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION) - -LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version */ -LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version */ - - -/*-************************************ -* Tuning parameter -**************************************/ -#define LZ4_MEMORY_USAGE_MIN 10 -#define LZ4_MEMORY_USAGE_DEFAULT 14 -#define LZ4_MEMORY_USAGE_MAX 20 - -/*! - * LZ4_MEMORY_USAGE : - * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; ) - * Increasing memory usage improves compression ratio, at the cost of speed. - * Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality. - * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache - */ -#ifndef LZ4_MEMORY_USAGE -# define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT -#endif - -#if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN) -# error "LZ4_MEMORY_USAGE is too small !" -#endif - -#if (LZ4_MEMORY_USAGE > LZ4_MEMORY_USAGE_MAX) -# error "LZ4_MEMORY_USAGE is too large !" -#endif - -/*-************************************ -* Simple Functions -**************************************/ -/*! LZ4_compress_default() : - * Compresses 'srcSize' bytes from buffer 'src' - * into already allocated 'dst' buffer of size 'dstCapacity'. - * Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize). - * It also runs faster, so it's a recommended setting. - * If the function cannot compress 'src' into a more limited 'dst' budget, - * compression stops *immediately*, and the function result is zero. - * In which case, 'dst' content is undefined (invalid). - * srcSize : max supported value is LZ4_MAX_INPUT_SIZE. - * dstCapacity : size of buffer 'dst' (which must be already allocated) - * @return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity) - * or 0 if compression fails - * Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer). - */ -LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity); - -/*! LZ4_decompress_safe() : - * compressedSize : is the exact complete size of the compressed block. - * dstCapacity : is the size of destination buffer (which must be already allocated), presumed an upper bound of decompressed size. - * @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity) - * If destination buffer is not large enough, decoding will stop and output an error code (negative value). - * If the source stream is detected malformed, the function will stop decoding and return a negative result. - * Note 1 : This function is protected against malicious data packets : - * it will never writes outside 'dst' buffer, nor read outside 'source' buffer, - * even if the compressed block is maliciously modified to order the decoder to do these actions. - * In such case, the decoder stops immediately, and considers the compressed block malformed. - * Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them. - * The implementation is free to send / store / derive this information in whichever way is most beneficial. - * If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead. - */ -LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity); - - -/*-************************************ -* Advanced Functions -**************************************/ -#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */ -#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) - -/*! LZ4_compressBound() : - Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible) - This function is primarily useful for memory allocation purposes (destination buffer size). - Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example). - Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize) - inputSize : max supported value is LZ4_MAX_INPUT_SIZE - return : maximum output size in a "worst case" scenario - or 0, if input size is incorrect (too large or negative) -*/ -LZ4LIB_API int LZ4_compressBound(int inputSize); - -/*! LZ4_compress_fast() : - Same as LZ4_compress_default(), but allows selection of "acceleration" factor. - The larger the acceleration value, the faster the algorithm, but also the lesser the compression. - It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed. - An acceleration value of "1" is the same as regular LZ4_compress_default() - Values <= 0 will be replaced by LZ4_ACCELERATION_DEFAULT (currently == 1, see lz4.c). - Values > LZ4_ACCELERATION_MAX will be replaced by LZ4_ACCELERATION_MAX (currently == 65537, see lz4.c). -*/ -LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - - -/*! LZ4_compress_fast_extState() : - * Same as LZ4_compress_fast(), using an externally allocated memory space for its state. - * Use LZ4_sizeofState() to know how much memory must be allocated, - * and allocate it on 8-bytes boundaries (using `malloc()` typically). - * Then, provide this buffer as `void* state` to compression function. - */ -LZ4LIB_API int LZ4_sizeofState(void); -LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - - -/*! LZ4_compress_destSize() : - * Reverse the logic : compresses as much data as possible from 'src' buffer - * into already allocated buffer 'dst', of size >= 'targetDestSize'. - * This function either compresses the entire 'src' content into 'dst' if it's large enough, - * or fill 'dst' buffer completely with as much data as possible from 'src'. - * note: acceleration parameter is fixed to "default". - * - * *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'. - * New value is necessarily <= input value. - * @return : Nb bytes written into 'dst' (necessarily <= targetDestSize) - * or 0 if compression fails. - * - * Note : from v1.8.2 to v1.9.1, this function had a bug (fixed un v1.9.2+): - * the produced compressed content could, in specific circumstances, - * require to be decompressed into a destination buffer larger - * by at least 1 byte than the content to decompress. - * If an application uses `LZ4_compress_destSize()`, - * it's highly recommended to update liblz4 to v1.9.2 or better. - * If this can't be done or ensured, - * the receiving decompression function should provide - * a dstCapacity which is > decompressedSize, by at least 1 byte. - * See https://github.com/lz4/lz4/issues/859 for details - */ -LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize); - - -/*! LZ4_decompress_safe_partial() : - * Decompress an LZ4 compressed block, of size 'srcSize' at position 'src', - * into destination buffer 'dst' of size 'dstCapacity'. - * Up to 'targetOutputSize' bytes will be decoded. - * The function stops decoding on reaching this objective. - * This can be useful to boost performance - * whenever only the beginning of a block is required. - * - * @return : the number of bytes decoded in `dst` (necessarily <= targetOutputSize) - * If source stream is detected malformed, function returns a negative result. - * - * Note 1 : @return can be < targetOutputSize, if compressed block contains less data. - * - * Note 2 : targetOutputSize must be <= dstCapacity - * - * Note 3 : this function effectively stops decoding on reaching targetOutputSize, - * so dstCapacity is kind of redundant. - * This is because in older versions of this function, - * decoding operation would still write complete sequences. - * Therefore, there was no guarantee that it would stop writing at exactly targetOutputSize, - * it could write more bytes, though only up to dstCapacity. - * Some "margin" used to be required for this operation to work properly. - * Thankfully, this is no longer necessary. - * The function nonetheless keeps the same signature, in an effort to preserve API compatibility. - * - * Note 4 : If srcSize is the exact size of the block, - * then targetOutputSize can be any value, - * including larger than the block's decompressed size. - * The function will, at most, generate block's decompressed size. - * - * Note 5 : If srcSize is _larger_ than block's compressed size, - * then targetOutputSize **MUST** be <= block's decompressed size. - * Otherwise, *silent corruption will occur*. - */ -LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity); - - -/*-********************************************* -* Streaming Compression Functions -***********************************************/ -typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */ - -LZ4LIB_API LZ4_stream_t* LZ4_createStream(void); -LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr); - -/*! LZ4_resetStream_fast() : v1.9.0+ - * Use this to prepare an LZ4_stream_t for a new chain of dependent blocks - * (e.g., LZ4_compress_fast_continue()). - * - * An LZ4_stream_t must be initialized once before usage. - * This is automatically done when created by LZ4_createStream(). - * However, should the LZ4_stream_t be simply declared on stack (for example), - * it's necessary to initialize it first, using LZ4_initStream(). - * - * After init, start any new stream with LZ4_resetStream_fast(). - * A same LZ4_stream_t can be re-used multiple times consecutively - * and compress multiple streams, - * provided that it starts each new stream with LZ4_resetStream_fast(). - * - * LZ4_resetStream_fast() is much faster than LZ4_initStream(), - * but is not compatible with memory regions containing garbage data. - * - * Note: it's only useful to call LZ4_resetStream_fast() - * in the context of streaming compression. - * The *extState* functions perform their own resets. - * Invoking LZ4_resetStream_fast() before is redundant, and even counterproductive. - */ -LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr); - -/*! LZ4_loadDict() : - * Use this function to reference a static dictionary into LZ4_stream_t. - * The dictionary must remain available during compression. - * LZ4_loadDict() triggers a reset, so any previous data will be forgotten. - * The same dictionary will have to be loaded on decompression side for successful decoding. - * Dictionary are useful for better compression of small data (KB range). - * While LZ4 accept any input as dictionary, - * results are generally better when using Zstandard's Dictionary Builder. - * Loading a size of 0 is allowed, and is the same as reset. - * @return : loaded dictionary size, in bytes (necessarily <= 64 KB) - */ -LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); - -/*! LZ4_compress_fast_continue() : - * Compress 'src' content using data from previously compressed blocks, for better compression ratio. - * 'dst' buffer must be already allocated. - * If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster. - * - * @return : size of compressed block - * or 0 if there is an error (typically, cannot fit into 'dst'). - * - * Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block. - * Each block has precise boundaries. - * Each block must be decompressed separately, calling LZ4_decompress_*() with relevant metadata. - * It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together. - * - * Note 2 : The previous 64KB of source data is __assumed__ to remain present, unmodified, at same address in memory ! - * - * Note 3 : When input is structured as a double-buffer, each buffer can have any size, including < 64 KB. - * Make sure that buffers are separated, by at least one byte. - * This construction ensures that each block only depends on previous block. - * - * Note 4 : If input buffer is a ring-buffer, it can have any size, including < 64 KB. - * - * Note 5 : After an error, the stream status is undefined (invalid), it can only be reset or freed. - */ -LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - -/*! LZ4_saveDict() : - * If last 64KB data cannot be guaranteed to remain available at its current memory location, - * save it into a safer place (char* safeBuffer). - * This is schematically equivalent to a memcpy() followed by LZ4_loadDict(), - * but is much faster, because LZ4_saveDict() doesn't need to rebuild tables. - * @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error. - */ -LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize); - - -/*-********************************************** -* Streaming Decompression Functions -* Bufferless synchronous API -************************************************/ -typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* tracking context */ - -/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() : - * creation / destruction of streaming decompression tracking context. - * A tracking context can be re-used multiple times. - */ -LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void); -LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream); - -/*! LZ4_setStreamDecode() : - * An LZ4_streamDecode_t context can be allocated once and re-used multiple times. - * Use this function to start decompression of a new stream of blocks. - * A dictionary can optionally be set. Use NULL or size 0 for a reset order. - * Dictionary is presumed stable : it must remain accessible and unmodified during next decompression. - * @return : 1 if OK, 0 if error - */ -LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize); - -/*! LZ4_decoderRingBufferSize() : v1.8.2+ - * Note : in a ring buffer scenario (optional), - * blocks are presumed decompressed next to each other - * up to the moment there is not enough remaining space for next block (remainingSize < maxBlockSize), - * at which stage it resumes from beginning of ring buffer. - * When setting such a ring buffer for streaming decompression, - * provides the minimum size of this ring buffer - * to be compatible with any source respecting maxBlockSize condition. - * @return : minimum ring buffer size, - * or 0 if there is an error (invalid maxBlockSize). - */ -LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize); -#define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize)) /* for static allocation; maxBlockSize presumed valid */ - -/*! LZ4_decompress_*_continue() : - * These decoding functions allow decompression of consecutive blocks in "streaming" mode. - * A block is an unsplittable entity, it must be presented entirely to a decompression function. - * Decompression functions only accepts one block at a time. - * The last 64KB of previously decoded data *must* remain available and unmodified at the memory position where they were decoded. - * If less than 64KB of data has been decoded, all the data must be present. - * - * Special : if decompression side sets a ring buffer, it must respect one of the following conditions : - * - Decompression buffer size is _at least_ LZ4_decoderRingBufferSize(maxBlockSize). - * maxBlockSize is the maximum size of any single block. It can have any value > 16 bytes. - * In which case, encoding and decoding buffers do not need to be synchronized. - * Actually, data can be produced by any source compliant with LZ4 format specification, and respecting maxBlockSize. - * - Synchronized mode : - * Decompression buffer size is _exactly_ the same as compression buffer size, - * and follows exactly same update rule (block boundaries at same positions), - * and decoding function is provided with exact decompressed size of each block (exception for last block of the stream), - * _then_ decoding & encoding ring buffer can have any size, including small ones ( < 64 KB). - * - Decompression buffer is larger than encoding buffer, by a minimum of maxBlockSize more bytes. - * In which case, encoding and decoding buffers do not need to be synchronized, - * and encoding ring buffer can have any size, including small ones ( < 64 KB). - * - * Whenever these conditions are not possible, - * save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression, - * then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block. -*/ -LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int srcSize, int dstCapacity); - - -/*! LZ4_decompress_*_usingDict() : - * These decoding functions work the same as - * a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue() - * They are stand-alone, and don't need an LZ4_streamDecode_t structure. - * Dictionary is presumed stable : it must remain accessible and unmodified during decompression. - * Performance tip : Decompression speed can be substantially increased - * when dst == dictStart + dictSize. - */ -LZ4LIB_API int LZ4_decompress_safe_usingDict (const char* src, char* dst, int srcSize, int dstCapcity, const char* dictStart, int dictSize); - -#endif /* LZ4_H_2983827168210 */ - - -/*^************************************* - * !!!!!! STATIC LINKING ONLY !!!!!! - ***************************************/ - -/*-**************************************************************************** - * Experimental section - * - * Symbols declared in this section must be considered unstable. Their - * signatures or semantics may change, or they may be removed altogether in the - * future. They are therefore only safe to depend on when the caller is - * statically linked against the library. - * - * To protect against unsafe usage, not only are the declarations guarded, - * the definitions are hidden by default - * when building LZ4 as a shared/dynamic library. - * - * In order to access these declarations, - * define LZ4_STATIC_LINKING_ONLY in your application - * before including LZ4's headers. - * - * In order to make their implementations accessible dynamically, you must - * define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library. - ******************************************************************************/ - -#ifdef LZ4_STATIC_LINKING_ONLY - -#ifndef LZ4_STATIC_3504398509 -#define LZ4_STATIC_3504398509 - -#ifdef LZ4_PUBLISH_STATIC_FUNCTIONS -#define LZ4LIB_STATIC_API LZ4LIB_API -#else -#define LZ4LIB_STATIC_API -#endif - - -/*! LZ4_compress_fast_extState_fastReset() : - * A variant of LZ4_compress_fast_extState(). - * - * Using this variant avoids an expensive initialization step. - * It is only safe to call if the state buffer is known to be correctly initialized already - * (see above comment on LZ4_resetStream_fast() for a definition of "correctly initialized"). - * From a high level, the difference is that - * this function initializes the provided state with a call to something like LZ4_resetStream_fast() - * while LZ4_compress_fast_extState() starts with a call to LZ4_resetStream(). - */ -LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); - -/*! LZ4_attach_dictionary() : - * This is an experimental API that allows - * efficient use of a static dictionary many times. - * - * Rather than re-loading the dictionary buffer into a working context before - * each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a - * working LZ4_stream_t, this function introduces a no-copy setup mechanism, - * in which the working stream references the dictionary stream in-place. - * - * Several assumptions are made about the state of the dictionary stream. - * Currently, only streams which have been prepared by LZ4_loadDict() should - * be expected to work. - * - * Alternatively, the provided dictionaryStream may be NULL, - * in which case any existing dictionary stream is unset. - * - * If a dictionary is provided, it replaces any pre-existing stream history. - * The dictionary contents are the only history that can be referenced and - * logically immediately precede the data compressed in the first subsequent - * compression call. - * - * The dictionary will only remain attached to the working stream through the - * first compression call, at the end of which it is cleared. The dictionary - * stream (and source buffer) must remain in-place / accessible / unchanged - * through the completion of the first compression call on the stream. - */ -LZ4LIB_STATIC_API void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream); - - -/*! In-place compression and decompression - * - * It's possible to have input and output sharing the same buffer, - * for highly constrained memory environments. - * In both cases, it requires input to lay at the end of the buffer, - * and decompression to start at beginning of the buffer. - * Buffer size must feature some margin, hence be larger than final size. - * - * |<------------------------buffer--------------------------------->| - * |<-----------compressed data--------->| - * |<-----------decompressed size------------------>| - * |<----margin---->| - * - * This technique is more useful for decompression, - * since decompressed size is typically larger, - * and margin is short. - * - * In-place decompression will work inside any buffer - * which size is >= LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize). - * This presumes that decompressedSize > compressedSize. - * Otherwise, it means compression actually expanded data, - * and it would be more efficient to store such data with a flag indicating it's not compressed. - * This can happen when data is not compressible (already compressed, or encrypted). - * - * For in-place compression, margin is larger, as it must be able to cope with both - * history preservation, requiring input data to remain unmodified up to LZ4_DISTANCE_MAX, - * and data expansion, which can happen when input is not compressible. - * As a consequence, buffer size requirements are much higher, - * and memory savings offered by in-place compression are more limited. - * - * There are ways to limit this cost for compression : - * - Reduce history size, by modifying LZ4_DISTANCE_MAX. - * Note that it is a compile-time constant, so all compressions will apply this limit. - * Lower values will reduce compression ratio, except when input_size < LZ4_DISTANCE_MAX, - * so it's a reasonable trick when inputs are known to be small. - * - Require the compressor to deliver a "maximum compressed size". - * This is the `dstCapacity` parameter in `LZ4_compress*()`. - * When this size is < LZ4_COMPRESSBOUND(inputSize), then compression can fail, - * in which case, the return code will be 0 (zero). - * The caller must be ready for these cases to happen, - * and typically design a backup scheme to send data uncompressed. - * The combination of both techniques can significantly reduce - * the amount of margin required for in-place compression. - * - * In-place compression can work in any buffer - * which size is >= (maxCompressedSize) - * with maxCompressedSize == LZ4_COMPRESSBOUND(srcSize) for guaranteed compression success. - * LZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and LZ4_DISTANCE_MAX, - * so it's possible to reduce memory requirements by playing with them. - */ - -#define LZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize) (((compressedSize) >> 8) + 32) -#define LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize) ((decompressedSize) + LZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize)) /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */ - -#ifndef LZ4_DISTANCE_MAX /* history window size; can be user-defined at compile time */ -# define LZ4_DISTANCE_MAX 65535 /* set to maximum value by default */ -#endif - -#define LZ4_COMPRESS_INPLACE_MARGIN (LZ4_DISTANCE_MAX + 32) /* LZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */ -#define LZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize) ((maxCompressedSize) + LZ4_COMPRESS_INPLACE_MARGIN) /**< maxCompressedSize is generally LZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */ - -#endif /* LZ4_STATIC_3504398509 */ -#endif /* LZ4_STATIC_LINKING_ONLY */ - - - -#ifndef LZ4_H_98237428734687 -#define LZ4_H_98237428734687 - -/*-************************************************************ - * Private Definitions - ************************************************************** - * Do not use these definitions directly. - * They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`. - * Accessing members will expose user code to API and/or ABI break in future versions of the library. - **************************************************************/ -#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2) -#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE) -#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */ - -#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) -# include - typedef int8_t LZ4_i8; - typedef uint8_t LZ4_byte; - typedef uint16_t LZ4_u16; - typedef uint32_t LZ4_u32; -#else - typedef signed char LZ4_i8; - typedef unsigned char LZ4_byte; - typedef unsigned short LZ4_u16; - typedef unsigned int LZ4_u32; -#endif - -typedef struct LZ4_stream_t_internal LZ4_stream_t_internal; -struct LZ4_stream_t_internal { - LZ4_u32 hashTable[LZ4_HASH_SIZE_U32]; - LZ4_u32 currentOffset; - LZ4_u32 tableType; - const LZ4_byte* dictionary; - const LZ4_stream_t_internal* dictCtx; - LZ4_u32 dictSize; -}; - -typedef struct { - const LZ4_byte* externalDict; - size_t extDictSize; - const LZ4_byte* prefixEnd; - size_t prefixSize; -} LZ4_streamDecode_t_internal; - - -/*! LZ4_stream_t : - * Do not use below internal definitions directly ! - * Declare or allocate an LZ4_stream_t instead. - * LZ4_stream_t can also be created using LZ4_createStream(), which is recommended. - * The structure definition can be convenient for static allocation - * (on stack, or as part of larger structure). - * Init this structure with LZ4_initStream() before first use. - * note : only use this definition in association with static linking ! - * this definition is not API/ABI safe, and may change in future versions. - */ -#define LZ4_STREAMSIZE ((1UL << LZ4_MEMORY_USAGE) + 32) /* static size, for inter-version compatibility */ -#define LZ4_STREAMSIZE_VOIDP (LZ4_STREAMSIZE / sizeof(void*)) -union LZ4_stream_u { - void* table[LZ4_STREAMSIZE_VOIDP]; - LZ4_stream_t_internal internal_donotuse; -}; /* previously typedef'd to LZ4_stream_t */ - - -/*! LZ4_initStream() : v1.9.0+ - * An LZ4_stream_t structure must be initialized at least once. - * This is automatically done when invoking LZ4_createStream(), - * but it's not when the structure is simply declared on stack (for example). - * - * Use LZ4_initStream() to properly initialize a newly declared LZ4_stream_t. - * It can also initialize any arbitrary buffer of sufficient size, - * and will @return a pointer of proper type upon initialization. - * - * Note : initialization fails if size and alignment conditions are not respected. - * In which case, the function will @return NULL. - * Note2: An LZ4_stream_t structure guarantees correct alignment and size. - * Note3: Before v1.9.0, use LZ4_resetStream() instead - */ -LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size); - - -/*! LZ4_streamDecode_t : - * information structure to track an LZ4 stream during decompression. - * init this structure using LZ4_setStreamDecode() before first use. - * note : only use in association with static linking ! - * this definition is not API/ABI safe, - * and may change in a future version ! - */ -#define LZ4_STREAMDECODESIZE_U64 (4 + ((sizeof(void*)==16) ? 2 : 0) /*AS-400*/ ) -#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long)) -union LZ4_streamDecode_u { - unsigned long long table[LZ4_STREAMDECODESIZE_U64]; - LZ4_streamDecode_t_internal internal_donotuse; -} ; /* previously typedef'd to LZ4_streamDecode_t */ - - - -/*-************************************ -* Obsolete Functions -**************************************/ - -/*! Deprecation warnings - * - * Deprecated functions make the compiler generate a warning when invoked. - * This is meant to invite users to update their source code. - * Should deprecation warnings be a problem, it is generally possible to disable them, - * typically with -Wno-deprecated-declarations for gcc - * or _CRT_SECURE_NO_WARNINGS in Visual. - * - * Another method is to define LZ4_DISABLE_DEPRECATE_WARNINGS - * before including the header file. - */ -#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS -# define LZ4_DEPRECATED(message) /* disable deprecation warnings */ -#else -# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ -# define LZ4_DEPRECATED(message) [[deprecated(message)]] -# elif defined(_MSC_VER) -# define LZ4_DEPRECATED(message) __declspec(deprecated(message)) -# elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 45)) -# define LZ4_DEPRECATED(message) __attribute__((deprecated(message))) -# elif defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 31) -# define LZ4_DEPRECATED(message) __attribute__((deprecated)) -# else -# pragma message("WARNING: LZ4_DEPRECATED needs custom implementation for this compiler") -# define LZ4_DEPRECATED(message) /* disabled */ -# endif -#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */ - -/*! Obsolete compression functions (since v1.7.3) */ -LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* src, char* dest, int srcSize); -LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize); -LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize); -LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); -LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize); -LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize); - -/*! Obsolete decompression functions (since v1.8.0) */ -LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize); -LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); - -/* Obsolete streaming functions (since v1.7.0) - * degraded functionality; do not use! - * - * In order to perform streaming compression, these functions depended on data - * that is no longer tracked in the state. They have been preserved as well as - * possible: using them will still produce a correct output. However, they don't - * actually retain any history between compression calls. The compression ratio - * achieved will therefore be no better than compressing each chunk - * independently. - */ -LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API void* LZ4_create (char* inputBuffer); -LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int LZ4_sizeofStreamState(void); -LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer); -LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state); - -/*! Obsolete streaming decoding functions (since v1.7.0) */ -LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize); -LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize); - -/*! Obsolete LZ4_decompress_fast variants (since v1.9.0) : - * These functions used to be faster than LZ4_decompress_safe(), - * but this is no longer the case. They are now slower. - * This is because LZ4_decompress_fast() doesn't know the input size, - * and therefore must progress more cautiously into the input buffer to not read beyond the end of block. - * On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability. - * As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated. - * - * The last remaining LZ4_decompress_fast() specificity is that - * it can decompress a block without knowing its compressed size. - * Such functionality can be achieved in a more secure manner - * by employing LZ4_decompress_safe_partial(). - * - * Parameters: - * originalSize : is the uncompressed size to regenerate. - * `dst` must be already allocated, its size must be >= 'originalSize' bytes. - * @return : number of bytes read from source buffer (== compressed size). - * The function expects to finish at block's end exactly. - * If the source stream is detected malformed, the function stops decoding and returns a negative result. - * note : LZ4_decompress_fast*() requires originalSize. Thanks to this information, it never writes past the output buffer. - * However, since it doesn't know its 'src' size, it may read an unknown amount of input, past input buffer bounds. - * Also, since match offsets are not validated, match reads from 'src' may underflow too. - * These issues never happen if input (compressed) data is correct. - * But they may happen if input data is invalid (error or intentional tampering). - * As a consequence, use these functions in trusted environments with trusted data **only**. - */ -LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead") -LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize); -LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead") -LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize); -LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_usingDict() instead") -LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize); - -/*! LZ4_resetStream() : - * An LZ4_stream_t structure must be initialized at least once. - * This is done with LZ4_initStream(), or LZ4_resetStream(). - * Consider switching to LZ4_initStream(), - * invoking LZ4_resetStream() will trigger deprecation warnings in the future. - */ -LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr); - - -#endif /* LZ4_H_98237428734687 */ - - -#if defined (__cplusplus) -} -#endif diff --git a/rres/external/monocypher.c b/rres/external/monocypher.c deleted file mode 100644 index e056db0..0000000 --- a/rres/external/monocypher.c +++ /dev/null @@ -1,2961 +0,0 @@ -// Monocypher version 4.0.1 -// -// This file is dual-licensed. Choose whichever licence you want from -// the two licences listed below. -// -// The first licence is a regular 2-clause BSD licence. The second licence -// is the CC-0 from Creative Commons. It is intended to release Monocypher -// to the public domain. The BSD licence serves as a fallback option. -// -// SPDX-License-Identifier: BSD-2-Clause OR CC0-1.0 -// -// ------------------------------------------------------------------------ -// -// Copyright (c) 2017-2020, Loup Vaillant -// All rights reserved. -// -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the -// distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// ------------------------------------------------------------------------ -// -// Written in 2017-2020 by Loup Vaillant -// -// To the extent possible under law, the author(s) have dedicated all copyright -// and related neighboring rights to this software to the public domain -// worldwide. This software is distributed without any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication along -// with this software. If not, see -// - -#include "monocypher.h" - -#ifdef MONOCYPHER_CPP_NAMESPACE -namespace MONOCYPHER_CPP_NAMESPACE { -#endif - -///////////////// -/// Utilities /// -///////////////// -#define FOR_T(type, i, start, end) for (type i = (start); i < (end); i++) -#define FOR(i, start, end) FOR_T(size_t, i, start, end) -#define COPY(dst, src, size) FOR(_i_, 0, size) (dst)[_i_] = (src)[_i_] -#define ZERO(buf, size) FOR(_i_, 0, size) (buf)[_i_] = 0 -#define WIPE_CTX(ctx) crypto_wipe(ctx , sizeof(*(ctx))) -#define WIPE_BUFFER(buffer) crypto_wipe(buffer, sizeof(buffer)) -#define MIN(a, b) ((a) <= (b) ? (a) : (b)) -#define MAX(a, b) ((a) >= (b) ? (a) : (b)) - -typedef int8_t i8; -typedef uint8_t u8; -typedef int16_t i16; -typedef uint32_t u32; -typedef int32_t i32; -typedef int64_t i64; -typedef uint64_t u64; - -static const u8 zero[128] = {0}; - -// returns the smallest positive integer y such that -// (x + y) % pow_2 == 0 -// Basically, it's how many bytes we need to add to "align" x. -// Only works when pow_2 is a power of 2. -// Note: we use ~x+1 instead of -x to avoid compiler warnings -static size_t align(size_t x, size_t pow_2) -{ - return (~x + 1) & (pow_2 - 1); -} - -static u32 load24_le(const u8 s[3]) -{ - return - ((u32)s[0] << 0) | - ((u32)s[1] << 8) | - ((u32)s[2] << 16); -} - -static u32 load32_le(const u8 s[4]) -{ - return - ((u32)s[0] << 0) | - ((u32)s[1] << 8) | - ((u32)s[2] << 16) | - ((u32)s[3] << 24); -} - -static u64 load64_le(const u8 s[8]) -{ - return load32_le(s) | ((u64)load32_le(s+4) << 32); -} - -static void store32_le(u8 out[4], u32 in) -{ - out[0] = in & 0xff; - out[1] = (in >> 8) & 0xff; - out[2] = (in >> 16) & 0xff; - out[3] = (in >> 24) & 0xff; -} - -static void store64_le(u8 out[8], u64 in) -{ - store32_le(out , (u32)in ); - store32_le(out + 4, in >> 32); -} - -static void load32_le_buf (u32 *dst, const u8 *src, size_t size) { - FOR(i, 0, size) { dst[i] = load32_le(src + i*4); } -} -static void load64_le_buf (u64 *dst, const u8 *src, size_t size) { - FOR(i, 0, size) { dst[i] = load64_le(src + i*8); } -} -static void store32_le_buf(u8 *dst, const u32 *src, size_t size) { - FOR(i, 0, size) { store32_le(dst + i*4, src[i]); } -} -static void store64_le_buf(u8 *dst, const u64 *src, size_t size) { - FOR(i, 0, size) { store64_le(dst + i*8, src[i]); } -} - -static u64 rotr64(u64 x, u64 n) { return (x >> n) ^ (x << (64 - n)); } -static u32 rotl32(u32 x, u32 n) { return (x << n) ^ (x >> (32 - n)); } - -static int neq0(u64 diff) -{ - // constant time comparison to zero - // return diff != 0 ? -1 : 0 - u64 half = (diff >> 32) | ((u32)diff); - return (1 & ((half - 1) >> 32)) - 1; -} - -static u64 x16(const u8 a[16], const u8 b[16]) -{ - return (load64_le(a + 0) ^ load64_le(b + 0)) - | (load64_le(a + 8) ^ load64_le(b + 8)); -} -static u64 x32(const u8 a[32],const u8 b[32]){return x16(a,b)| x16(a+16, b+16);} -static u64 x64(const u8 a[64],const u8 b[64]){return x32(a,b)| x32(a+32, b+32);} -int crypto_verify16(const u8 a[16], const u8 b[16]){ return neq0(x16(a, b)); } -int crypto_verify32(const u8 a[32], const u8 b[32]){ return neq0(x32(a, b)); } -int crypto_verify64(const u8 a[64], const u8 b[64]){ return neq0(x64(a, b)); } - -void crypto_wipe(void *secret, size_t size) -{ - volatile u8 *v_secret = (u8*)secret; - ZERO(v_secret, size); -} - -///////////////// -/// Chacha 20 /// -///////////////// -#define QUARTERROUND(a, b, c, d) \ - a += b; d = rotl32(d ^ a, 16); \ - c += d; b = rotl32(b ^ c, 12); \ - a += b; d = rotl32(d ^ a, 8); \ - c += d; b = rotl32(b ^ c, 7) - -static void chacha20_rounds(u32 out[16], const u32 in[16]) -{ - // The temporary variables make Chacha20 10% faster. - u32 t0 = in[ 0]; u32 t1 = in[ 1]; u32 t2 = in[ 2]; u32 t3 = in[ 3]; - u32 t4 = in[ 4]; u32 t5 = in[ 5]; u32 t6 = in[ 6]; u32 t7 = in[ 7]; - u32 t8 = in[ 8]; u32 t9 = in[ 9]; u32 t10 = in[10]; u32 t11 = in[11]; - u32 t12 = in[12]; u32 t13 = in[13]; u32 t14 = in[14]; u32 t15 = in[15]; - - FOR (i, 0, 10) { // 20 rounds, 2 rounds per loop. - QUARTERROUND(t0, t4, t8 , t12); // column 0 - QUARTERROUND(t1, t5, t9 , t13); // column 1 - QUARTERROUND(t2, t6, t10, t14); // column 2 - QUARTERROUND(t3, t7, t11, t15); // column 3 - QUARTERROUND(t0, t5, t10, t15); // diagonal 0 - QUARTERROUND(t1, t6, t11, t12); // diagonal 1 - QUARTERROUND(t2, t7, t8 , t13); // diagonal 2 - QUARTERROUND(t3, t4, t9 , t14); // diagonal 3 - } - out[ 0] = t0; out[ 1] = t1; out[ 2] = t2; out[ 3] = t3; - out[ 4] = t4; out[ 5] = t5; out[ 6] = t6; out[ 7] = t7; - out[ 8] = t8; out[ 9] = t9; out[10] = t10; out[11] = t11; - out[12] = t12; out[13] = t13; out[14] = t14; out[15] = t15; -} - -static const u8 *chacha20_constant = (const u8*)"expand 32-byte k"; // 16 bytes - -void crypto_chacha20_h(u8 out[32], const u8 key[32], const u8 in [16]) -{ - u32 block[16]; - load32_le_buf(block , chacha20_constant, 4); - load32_le_buf(block + 4, key , 8); - load32_le_buf(block + 12, in , 4); - - chacha20_rounds(block, block); - - // prevent reversal of the rounds by revealing only half of the buffer. - store32_le_buf(out , block , 4); // constant - store32_le_buf(out+16, block+12, 4); // counter and nonce - WIPE_BUFFER(block); -} - -u64 crypto_chacha20_djb(u8 *cipher_text, const u8 *plain_text, - size_t text_size, const u8 key[32], const u8 nonce[8], - u64 ctr) -{ - u32 input[16]; - load32_le_buf(input , chacha20_constant, 4); - load32_le_buf(input + 4, key , 8); - load32_le_buf(input + 14, nonce , 2); - input[12] = (u32) ctr; - input[13] = (u32)(ctr >> 32); - - // Whole blocks - u32 pool[16]; - size_t nb_blocks = text_size >> 6; - FOR (i, 0, nb_blocks) { - chacha20_rounds(pool, input); - if (plain_text != 0) { - FOR (j, 0, 16) { - u32 p = pool[j] + input[j]; - store32_le(cipher_text, p ^ load32_le(plain_text)); - cipher_text += 4; - plain_text += 4; - } - } else { - FOR (j, 0, 16) { - u32 p = pool[j] + input[j]; - store32_le(cipher_text, p); - cipher_text += 4; - } - } - input[12]++; - if (input[12] == 0) { - input[13]++; - } - } - text_size &= 63; - - // Last (incomplete) block - if (text_size > 0) { - if (plain_text == 0) { - plain_text = zero; - } - chacha20_rounds(pool, input); - u8 tmp[64]; - FOR (i, 0, 16) { - store32_le(tmp + i*4, pool[i] + input[i]); - } - FOR (i, 0, text_size) { - cipher_text[i] = tmp[i] ^ plain_text[i]; - } - WIPE_BUFFER(tmp); - } - ctr = input[12] + ((u64)input[13] << 32) + (text_size > 0); - - WIPE_BUFFER(pool); - WIPE_BUFFER(input); - return ctr; -} - -u32 crypto_chacha20_ietf(u8 *cipher_text, const u8 *plain_text, - size_t text_size, - const u8 key[32], const u8 nonce[12], u32 ctr) -{ - u64 big_ctr = ctr + ((u64)load32_le(nonce) << 32); - return (u32)crypto_chacha20_djb(cipher_text, plain_text, text_size, - key, nonce + 4, big_ctr); -} - -u64 crypto_chacha20_x(u8 *cipher_text, const u8 *plain_text, - size_t text_size, - const u8 key[32], const u8 nonce[24], u64 ctr) -{ - u8 sub_key[32]; - crypto_chacha20_h(sub_key, key, nonce); - ctr = crypto_chacha20_djb(cipher_text, plain_text, text_size, - sub_key, nonce + 16, ctr); - WIPE_BUFFER(sub_key); - return ctr; -} - -///////////////// -/// Poly 1305 /// -///////////////// - -// h = (h + c) * r -// preconditions: -// ctx->h <= 4_ffffffff_ffffffff_ffffffff_ffffffff -// ctx->r <= 0ffffffc_0ffffffc_0ffffffc_0fffffff -// end <= 1 -// Postcondition: -// ctx->h <= 4_ffffffff_ffffffff_ffffffff_ffffffff -static void poly_block(crypto_poly1305_ctx *ctx, const u8 in[16], unsigned end) -{ - u32 s[4]; - load32_le_buf(s, in, 4); - - //- PROOF Poly1305 - //- - //- # Inputs & preconditions - //- ctx->h[0] = u32() - //- ctx->h[1] = u32() - //- ctx->h[2] = u32() - //- ctx->h[3] = u32() - //- ctx->h[4] = u32(limit = 4) - //- - //- ctx->r[0] = u32(limit = 0x0fffffff) - //- ctx->r[1] = u32(limit = 0x0ffffffc) - //- ctx->r[2] = u32(limit = 0x0ffffffc) - //- ctx->r[3] = u32(limit = 0x0ffffffc) - //- - //- s[0] = u32() - //- s[1] = u32() - //- s[2] = u32() - //- s[3] = u32() - //- - //- end = unsigned(limit = 1) - - // s = h + c, without carry propagation - const u64 s0 = ctx->h[0] + (u64)s[0]; // s0 <= 1_fffffffe - const u64 s1 = ctx->h[1] + (u64)s[1]; // s1 <= 1_fffffffe - const u64 s2 = ctx->h[2] + (u64)s[2]; // s2 <= 1_fffffffe - const u64 s3 = ctx->h[3] + (u64)s[3]; // s3 <= 1_fffffffe - const u32 s4 = ctx->h[4] + end; // s4 <= 5 - - // Local all the things! - const u32 r0 = ctx->r[0]; // r0 <= 0fffffff - const u32 r1 = ctx->r[1]; // r1 <= 0ffffffc - const u32 r2 = ctx->r[2]; // r2 <= 0ffffffc - const u32 r3 = ctx->r[3]; // r3 <= 0ffffffc - const u32 rr0 = (r0 >> 2) * 5; // rr0 <= 13fffffb // lose 2 bits... - const u32 rr1 = (r1 >> 2) + r1; // rr1 <= 13fffffb // rr1 == (r1 >> 2) * 5 - const u32 rr2 = (r2 >> 2) + r2; // rr2 <= 13fffffb // rr1 == (r2 >> 2) * 5 - const u32 rr3 = (r3 >> 2) + r3; // rr3 <= 13fffffb // rr1 == (r3 >> 2) * 5 - - // (h + c) * r, without carry propagation - const u64 x0 = s0*r0+ s1*rr3+ s2*rr2+ s3*rr1+ s4*rr0; // <= 97ffffe007fffff8 - const u64 x1 = s0*r1+ s1*r0 + s2*rr3+ s3*rr2+ s4*rr1; // <= 8fffffe20ffffff6 - const u64 x2 = s0*r2+ s1*r1 + s2*r0 + s3*rr3+ s4*rr2; // <= 87ffffe417fffff4 - const u64 x3 = s0*r3+ s1*r2 + s2*r1 + s3*r0 + s4*rr3; // <= 7fffffe61ffffff2 - const u32 x4 = s4 * (r0 & 3); // ...recover 2 bits // <= f - - // partial reduction modulo 2^130 - 5 - const u32 u5 = x4 + (x3 >> 32); // u5 <= 7ffffff5 - const u64 u0 = (u5 >> 2) * 5 + (x0 & 0xffffffff); - const u64 u1 = (u0 >> 32) + (x1 & 0xffffffff) + (x0 >> 32); - const u64 u2 = (u1 >> 32) + (x2 & 0xffffffff) + (x1 >> 32); - const u64 u3 = (u2 >> 32) + (x3 & 0xffffffff) + (x2 >> 32); - const u64 u4 = (u3 >> 32) + (u5 & 3); - - // Update the hash - ctx->h[0] = u0 & 0xffffffff; // u0 <= 1_9ffffff0 - ctx->h[1] = u1 & 0xffffffff; // u1 <= 1_97ffffe0 - ctx->h[2] = u2 & 0xffffffff; // u2 <= 1_8fffffe2 - ctx->h[3] = u3 & 0xffffffff; // u3 <= 1_87ffffe4 - ctx->h[4] = u4 & 0xffffffff; // u4 <= 4 - - //- # postconditions - //- ASSERT(ctx->h[4].limit() <= 4) - //- CQFD Poly1305 -} - -void crypto_poly1305_init(crypto_poly1305_ctx *ctx, const u8 key[32]) -{ - ZERO(ctx->h, 5); // Initial hash is zero - ctx->c_idx = 0; - // load r and pad (r has some of its bits cleared) - load32_le_buf(ctx->r , key , 4); - load32_le_buf(ctx->pad, key+16, 4); - FOR (i, 0, 1) { ctx->r[i] &= 0x0fffffff; } - FOR (i, 1, 4) { ctx->r[i] &= 0x0ffffffc; } -} - -void crypto_poly1305_update(crypto_poly1305_ctx *ctx, - const u8 *message, size_t message_size) -{ - // Align ourselves with block boundaries - size_t aligned = MIN(align(ctx->c_idx, 16), message_size); - FOR (i, 0, aligned) { - ctx->c[ctx->c_idx] = *message; - ctx->c_idx++; - message++; - message_size--; - } - - // If block is complete, process it - if (ctx->c_idx == 16) { - poly_block(ctx, ctx->c, 1); - ctx->c_idx = 0; - } - - // Process the message block by block - size_t nb_blocks = message_size >> 4; - FOR (i, 0, nb_blocks) { - poly_block(ctx, message, 1); - message += 16; - } - message_size &= 15; - - // remaining bytes (we never complete a block here) - FOR (i, 0, message_size) { - ctx->c[ctx->c_idx] = message[i]; - ctx->c_idx++; - } -} - -void crypto_poly1305_final(crypto_poly1305_ctx *ctx, u8 mac[16]) -{ - // Process the last block (if any) - // We move the final 1 according to remaining input length - // (this will add less than 2^130 to the last input block) - if (ctx->c_idx != 0) { - ZERO(ctx->c + ctx->c_idx, 16 - ctx->c_idx); - ctx->c[ctx->c_idx] = 1; - poly_block(ctx, ctx->c, 0); - } - - // check if we should subtract 2^130-5 by performing the - // corresponding carry propagation. - u64 c = 5; - FOR (i, 0, 4) { - c += ctx->h[i]; - c >>= 32; - } - c += ctx->h[4]; - c = (c >> 2) * 5; // shift the carry back to the beginning - // c now indicates how many times we should subtract 2^130-5 (0 or 1) - FOR (i, 0, 4) { - c += (u64)ctx->h[i] + ctx->pad[i]; - store32_le(mac + i*4, (u32)c); - c = c >> 32; - } - WIPE_CTX(ctx); -} - -void crypto_poly1305(u8 mac[16], const u8 *message, - size_t message_size, const u8 key[32]) -{ - crypto_poly1305_ctx ctx; - crypto_poly1305_init (&ctx, key); - crypto_poly1305_update(&ctx, message, message_size); - crypto_poly1305_final (&ctx, mac); -} - -//////////////// -/// BLAKE2 b /// -//////////////// -static const u64 iv[8] = { - 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, - 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, - 0x510e527fade682d1, 0x9b05688c2b3e6c1f, - 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179, -}; - -static void blake2b_compress(crypto_blake2b_ctx *ctx, int is_last_block) -{ - static const u8 sigma[12][16] = { - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, - { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, - { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, - { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, - { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, - { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, - { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, - { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, - { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, - }; - - // increment input offset - u64 *x = ctx->input_offset; - size_t y = ctx->input_idx; - x[0] += y; - if (x[0] < y) { - x[1]++; - } - - // init work vector - u64 v0 = ctx->hash[0]; u64 v8 = iv[0]; - u64 v1 = ctx->hash[1]; u64 v9 = iv[1]; - u64 v2 = ctx->hash[2]; u64 v10 = iv[2]; - u64 v3 = ctx->hash[3]; u64 v11 = iv[3]; - u64 v4 = ctx->hash[4]; u64 v12 = iv[4] ^ ctx->input_offset[0]; - u64 v5 = ctx->hash[5]; u64 v13 = iv[5] ^ ctx->input_offset[1]; - u64 v6 = ctx->hash[6]; u64 v14 = iv[6] ^ (u64)~(is_last_block - 1); - u64 v7 = ctx->hash[7]; u64 v15 = iv[7]; - - // mangle work vector - u64 *input = ctx->input; -#define BLAKE2_G(a, b, c, d, x, y) \ - a += b + x; d = rotr64(d ^ a, 32); \ - c += d; b = rotr64(b ^ c, 24); \ - a += b + y; d = rotr64(d ^ a, 16); \ - c += d; b = rotr64(b ^ c, 63) -#define BLAKE2_ROUND(i) \ - BLAKE2_G(v0, v4, v8 , v12, input[sigma[i][ 0]], input[sigma[i][ 1]]); \ - BLAKE2_G(v1, v5, v9 , v13, input[sigma[i][ 2]], input[sigma[i][ 3]]); \ - BLAKE2_G(v2, v6, v10, v14, input[sigma[i][ 4]], input[sigma[i][ 5]]); \ - BLAKE2_G(v3, v7, v11, v15, input[sigma[i][ 6]], input[sigma[i][ 7]]); \ - BLAKE2_G(v0, v5, v10, v15, input[sigma[i][ 8]], input[sigma[i][ 9]]); \ - BLAKE2_G(v1, v6, v11, v12, input[sigma[i][10]], input[sigma[i][11]]); \ - BLAKE2_G(v2, v7, v8 , v13, input[sigma[i][12]], input[sigma[i][13]]); \ - BLAKE2_G(v3, v4, v9 , v14, input[sigma[i][14]], input[sigma[i][15]]) - -#ifdef BLAKE2_NO_UNROLLING - FOR (i, 0, 12) { - BLAKE2_ROUND(i); - } -#else - BLAKE2_ROUND(0); BLAKE2_ROUND(1); BLAKE2_ROUND(2); BLAKE2_ROUND(3); - BLAKE2_ROUND(4); BLAKE2_ROUND(5); BLAKE2_ROUND(6); BLAKE2_ROUND(7); - BLAKE2_ROUND(8); BLAKE2_ROUND(9); BLAKE2_ROUND(10); BLAKE2_ROUND(11); -#endif - - // update hash - ctx->hash[0] ^= v0 ^ v8; ctx->hash[1] ^= v1 ^ v9; - ctx->hash[2] ^= v2 ^ v10; ctx->hash[3] ^= v3 ^ v11; - ctx->hash[4] ^= v4 ^ v12; ctx->hash[5] ^= v5 ^ v13; - ctx->hash[6] ^= v6 ^ v14; ctx->hash[7] ^= v7 ^ v15; -} - -void crypto_blake2b_keyed_init(crypto_blake2b_ctx *ctx, size_t hash_size, - const u8 *key, size_t key_size) -{ - // initial hash - COPY(ctx->hash, iv, 8); - ctx->hash[0] ^= 0x01010000 ^ (key_size << 8) ^ hash_size; - - ctx->input_offset[0] = 0; // beginning of the input, no offset - ctx->input_offset[1] = 0; // beginning of the input, no offset - ctx->hash_size = hash_size; - ctx->input_idx = 0; - ZERO(ctx->input, 16); - - // if there is a key, the first block is that key (padded with zeroes) - if (key_size > 0) { - u8 key_block[128] = {0}; - COPY(key_block, key, key_size); - // same as calling crypto_blake2b_update(ctx, key_block , 128) - load64_le_buf(ctx->input, key_block, 16); - ctx->input_idx = 128; - } -} - -void crypto_blake2b_init(crypto_blake2b_ctx *ctx, size_t hash_size) -{ - crypto_blake2b_keyed_init(ctx, hash_size, 0, 0); -} - -void crypto_blake2b_update(crypto_blake2b_ctx *ctx, - const u8 *message, size_t message_size) -{ - // Avoid undefined NULL pointer increments with empty messages - if (message_size == 0) { - return; - } - - // Align with word boundaries - if ((ctx->input_idx & 7) != 0) { - size_t nb_bytes = MIN(align(ctx->input_idx, 8), message_size); - size_t word = ctx->input_idx >> 3; - size_t byte = ctx->input_idx & 7; - FOR (i, 0, nb_bytes) { - ctx->input[word] |= (u64)message[i] << ((byte + i) << 3); - } - ctx->input_idx += nb_bytes; - message += nb_bytes; - message_size -= nb_bytes; - } - - // Align with block boundaries (faster than byte by byte) - if ((ctx->input_idx & 127) != 0) { - size_t nb_words = MIN(align(ctx->input_idx, 128), message_size) >> 3; - load64_le_buf(ctx->input + (ctx->input_idx >> 3), message, nb_words); - ctx->input_idx += nb_words << 3; - message += nb_words << 3; - message_size -= nb_words << 3; - } - - // Process block by block - size_t nb_blocks = message_size >> 7; - FOR (i, 0, nb_blocks) { - if (ctx->input_idx == 128) { - blake2b_compress(ctx, 0); - } - load64_le_buf(ctx->input, message, 16); - message += 128; - ctx->input_idx = 128; - } - message_size &= 127; - - if (message_size != 0) { - // Compress block & flush input buffer as needed - if (ctx->input_idx == 128) { - blake2b_compress(ctx, 0); - ctx->input_idx = 0; - } - if (ctx->input_idx == 0) { - ZERO(ctx->input, 16); - } - // Fill remaining words (faster than byte by byte) - size_t nb_words = message_size >> 3; - load64_le_buf(ctx->input, message, nb_words); - ctx->input_idx += nb_words << 3; - message += nb_words << 3; - message_size -= nb_words << 3; - - // Fill remaining bytes - FOR (i, 0, message_size) { - size_t word = ctx->input_idx >> 3; - size_t byte = ctx->input_idx & 7; - ctx->input[word] |= (u64)message[i] << (byte << 3); - ctx->input_idx++; - } - } -} - -void crypto_blake2b_final(crypto_blake2b_ctx *ctx, u8 *hash) -{ - blake2b_compress(ctx, 1); // compress the last block - size_t hash_size = MIN(ctx->hash_size, 64); - size_t nb_words = hash_size >> 3; - store64_le_buf(hash, ctx->hash, nb_words); - FOR (i, nb_words << 3, hash_size) { - hash[i] = (ctx->hash[i >> 3] >> (8 * (i & 7))) & 0xff; - } - WIPE_CTX(ctx); -} - -void crypto_blake2b_keyed(u8 *hash, size_t hash_size, - const u8 *key, size_t key_size, - const u8 *message, size_t message_size) -{ - crypto_blake2b_ctx ctx; - crypto_blake2b_keyed_init(&ctx, hash_size, key, key_size); - crypto_blake2b_update (&ctx, message, message_size); - crypto_blake2b_final (&ctx, hash); -} - -void crypto_blake2b(u8 *hash, size_t hash_size, const u8 *msg, size_t msg_size) -{ - crypto_blake2b_keyed(hash, hash_size, 0, 0, msg, msg_size); -} - -////////////// -/// Argon2 /// -////////////// -// references to R, Z, Q etc. come from the spec - -// Argon2 operates on 1024 byte blocks. -typedef struct { u64 a[128]; } blk; - -// updates a BLAKE2 hash with a 32 bit word, little endian. -static void blake_update_32(crypto_blake2b_ctx *ctx, u32 input) -{ - u8 buf[4]; - store32_le(buf, input); - crypto_blake2b_update(ctx, buf, 4); - WIPE_BUFFER(buf); -} - -static void blake_update_32_buf(crypto_blake2b_ctx *ctx, - const u8 *buf, u32 size) -{ - blake_update_32(ctx, size); - crypto_blake2b_update(ctx, buf, size); -} - - -static void copy_block(blk *o,const blk*in){FOR(i, 0, 128) o->a[i] = in->a[i];} -static void xor_block(blk *o,const blk*in){FOR(i, 0, 128) o->a[i] ^= in->a[i];} - -// Hash with a virtually unlimited digest size. -// Doesn't extract more entropy than the base hash function. -// Mainly used for filling a whole kilobyte block with pseudo-random bytes. -// (One could use a stream cipher with a seed hash as the key, but -// this would introduce another dependency —and point of failure.) -static void extended_hash(u8 *digest, u32 digest_size, - const u8 *input , u32 input_size) -{ - crypto_blake2b_ctx ctx; - crypto_blake2b_init (&ctx, MIN(digest_size, 64)); - blake_update_32 (&ctx, digest_size); - crypto_blake2b_update(&ctx, input, input_size); - crypto_blake2b_final (&ctx, digest); - - if (digest_size > 64) { - // the conversion to u64 avoids integer overflow on - // ludicrously big hash sizes. - u32 r = (u32)(((u64)digest_size + 31) >> 5) - 2; - u32 i = 1; - u32 in = 0; - u32 out = 32; - while (i < r) { - // Input and output overlap. This is intentional - crypto_blake2b(digest + out, 64, digest + in, 64); - i += 1; - in += 32; - out += 32; - } - crypto_blake2b(digest + out, digest_size - (32 * r), digest + in , 64); - } -} - -#define LSB(x) ((x) & 0xffffffff) -#define G(a, b, c, d) \ - a += b + 2 * LSB(a) * LSB(b); d ^= a; d = rotr64(d, 32); \ - c += d + 2 * LSB(c) * LSB(d); b ^= c; b = rotr64(b, 24); \ - a += b + 2 * LSB(a) * LSB(b); d ^= a; d = rotr64(d, 16); \ - c += d + 2 * LSB(c) * LSB(d); b ^= c; b = rotr64(b, 63) -#define ROUND(v0, v1, v2, v3, v4, v5, v6, v7, \ - v8, v9, v10, v11, v12, v13, v14, v15) \ - G(v0, v4, v8, v12); G(v1, v5, v9, v13); \ - G(v2, v6, v10, v14); G(v3, v7, v11, v15); \ - G(v0, v5, v10, v15); G(v1, v6, v11, v12); \ - G(v2, v7, v8, v13); G(v3, v4, v9, v14) - -// Core of the compression function G. Computes Z from R in place. -static void g_rounds(blk *b) -{ - // column rounds (work_block = Q) - for (int i = 0; i < 128; i += 16) { - ROUND(b->a[i ], b->a[i+ 1], b->a[i+ 2], b->a[i+ 3], - b->a[i+ 4], b->a[i+ 5], b->a[i+ 6], b->a[i+ 7], - b->a[i+ 8], b->a[i+ 9], b->a[i+10], b->a[i+11], - b->a[i+12], b->a[i+13], b->a[i+14], b->a[i+15]); - } - // row rounds (b = Z) - for (int i = 0; i < 16; i += 2) { - ROUND(b->a[i ], b->a[i+ 1], b->a[i+ 16], b->a[i+ 17], - b->a[i+32], b->a[i+33], b->a[i+ 48], b->a[i+ 49], - b->a[i+64], b->a[i+65], b->a[i+ 80], b->a[i+ 81], - b->a[i+96], b->a[i+97], b->a[i+112], b->a[i+113]); - } -} - -const crypto_argon2_extras crypto_argon2_no_extras = { 0, 0, 0, 0 }; - -void crypto_argon2(u8 *hash, u32 hash_size, void *work_area, - crypto_argon2_config config, - crypto_argon2_inputs inputs, - crypto_argon2_extras extras) -{ - const u32 segment_size = config.nb_blocks / config.nb_lanes / 4; - const u32 lane_size = segment_size * 4; - const u32 nb_blocks = lane_size * config.nb_lanes; // rounding down - - // work area seen as blocks (must be suitably aligned) - blk *blocks = (blk*)work_area; - { - u8 initial_hash[72]; // 64 bytes plus 2 words for future hashes - crypto_blake2b_ctx ctx; - crypto_blake2b_init (&ctx, 64); - blake_update_32 (&ctx, config.nb_lanes ); // p: number of "threads" - blake_update_32 (&ctx, hash_size); - blake_update_32 (&ctx, config.nb_blocks); - blake_update_32 (&ctx, config.nb_passes); - blake_update_32 (&ctx, 0x13); // v: version number - blake_update_32 (&ctx, config.algorithm); // y: Argon2i, Argon2d... - blake_update_32_buf (&ctx, inputs.pass, inputs.pass_size); - blake_update_32_buf (&ctx, inputs.salt, inputs.salt_size); - blake_update_32_buf (&ctx, extras.key, extras.key_size); - blake_update_32_buf (&ctx, extras.ad, extras.ad_size); - crypto_blake2b_final(&ctx, initial_hash); // fill 64 first bytes only - - // fill first 2 blocks of each lane - u8 hash_area[1024]; - FOR_T(u32, l, 0, config.nb_lanes) { - FOR_T(u32, i, 0, 2) { - store32_le(initial_hash + 64, i); // first additional word - store32_le(initial_hash + 68, l); // second additional word - extended_hash(hash_area, 1024, initial_hash, 72); - load64_le_buf(blocks[l * lane_size + i].a, hash_area, 128); - } - } - - WIPE_BUFFER(initial_hash); - WIPE_BUFFER(hash_area); - } - - // Argon2i and Argon2id start with constant time indexing - int constant_time = config.algorithm != CRYPTO_ARGON2_D; - - // Fill (and re-fill) the rest of the blocks - // - // Note: even though each segment within the same slice can be - // computed in parallel, (one thread per lane), we are computing - // them sequentially, because Monocypher doesn't support threads. - // - // Yet optimal performance (and therefore security) requires one - // thread per lane. The only reason Monocypher supports multiple - // lanes is compatibility. - blk tmp; - FOR_T(u32, pass, 0, config.nb_passes) { - FOR_T(u32, slice, 0, 4) { - // On the first slice of the first pass, - // blocks 0 and 1 are already filled, hence pass_offset. - u32 pass_offset = pass == 0 && slice == 0 ? 2 : 0; - u32 slice_offset = slice * segment_size; - - // Argon2id switches back to non-constant time indexing - // after the first two slices of the first pass - if (slice == 2 && config.algorithm == CRYPTO_ARGON2_ID) { - constant_time = 0; - } - - // Each iteration of the following loop may be performed in - // a separate thread. All segments must be fully completed - // before we start filling the next slice. - FOR_T(u32, segment, 0, config.nb_lanes) { - blk index_block; - u32 index_ctr = 1; - FOR_T (u32, block, pass_offset, segment_size) { - // Current and previous blocks - u32 lane_offset = segment * lane_size; - blk *segment_start = blocks + lane_offset + slice_offset; - blk *current = segment_start + block; - blk *previous = - block == 0 && slice_offset == 0 - ? segment_start + lane_size - 1 - : segment_start + block - 1; - - u64 index_seed; - if (constant_time) { - if (block == pass_offset || (block % 128) == 0) { - // Fill or refresh deterministic indices block - - // seed the beginning of the block... - ZERO(index_block.a, 128); - index_block.a[0] = pass; - index_block.a[1] = segment; - index_block.a[2] = slice; - index_block.a[3] = nb_blocks; - index_block.a[4] = config.nb_passes; - index_block.a[5] = config.algorithm; - index_block.a[6] = index_ctr; - index_ctr++; - - // ... then shuffle it - copy_block(&tmp, &index_block); - g_rounds (&index_block); - xor_block (&index_block, &tmp); - copy_block(&tmp, &index_block); - g_rounds (&index_block); - xor_block (&index_block, &tmp); - } - index_seed = index_block.a[block % 128]; - } else { - index_seed = previous->a[0]; - } - - // Establish the reference set. *Approximately* comprises: - // - The last 3 slices (if they exist yet) - // - The already constructed blocks in the current segment - u32 next_slice = ((slice + 1) % 4) * segment_size; - u32 window_start = pass == 0 ? 0 : next_slice; - u32 nb_segments = pass == 0 ? slice : 3; - u32 window_size = nb_segments * segment_size + block - 1; - - // Find reference block - u64 j1 = index_seed & 0xffffffff; // block selector - u64 j2 = index_seed >> 32; // lane selector - u64 x = (j1 * j1) >> 32; - u64 y = (window_size * x) >> 32; - u64 z = (window_size - 1) - y; - u64 ref = (window_start + z) % lane_size; - u32 index = (j2%config.nb_lanes)*lane_size + (u32)ref; - blk *reference = blocks + index; - - // Shuffle the previous & reference block - // into the current block - copy_block(&tmp, previous); - xor_block (&tmp, reference); - if (pass == 0) { copy_block(current, &tmp); } - else { xor_block (current, &tmp); } - g_rounds (&tmp); - xor_block (current, &tmp); - } - } - } - } - - // Wipe temporary block - volatile u64* p = tmp.a; - ZERO(p, 128); - - // XOR last blocks of each lane - blk *last_block = blocks + lane_size - 1; - FOR_T (u32, lane, 1, config.nb_lanes) { - blk *next_block = last_block + lane_size; - xor_block(next_block, last_block); - last_block = next_block; - } - - // Serialize last block - u8 final_block[1024]; - store64_le_buf(final_block, last_block->a, 128); - - // Wipe work area - p = (u64*)work_area; - ZERO(p, 128 * nb_blocks); - - // Hash the very last block with H' into the output hash - extended_hash(hash, hash_size, final_block, 1024); - WIPE_BUFFER(final_block); -} - -//////////////////////////////////// -/// Arithmetic modulo 2^255 - 19 /// -//////////////////////////////////// -// Originally taken from SUPERCOP's ref10 implementation. -// A bit bigger than TweetNaCl, over 4 times faster. - -// field element -typedef i32 fe[10]; - -// field constants -// -// fe_one : 1 -// sqrtm1 : sqrt(-1) -// d : -121665 / 121666 -// D2 : 2 * -121665 / 121666 -// lop_x, lop_y: low order point in Edwards coordinates -// ufactor : -sqrt(-1) * 2 -// A2 : 486662^2 (A squared) -static const fe fe_one = {1}; -static const fe sqrtm1 = { - -32595792, -7943725, 9377950, 3500415, 12389472, - -272473, -25146209, -2005654, 326686, 11406482, -}; -static const fe d = { - -10913610, 13857413, -15372611, 6949391, 114729, - -8787816, -6275908, -3247719, -18696448, -12055116, -}; -static const fe D2 = { - -21827239, -5839606, -30745221, 13898782, 229458, - 15978800, -12551817, -6495438, 29715968, 9444199, -}; -static const fe lop_x = { - 21352778, 5345713, 4660180, -8347857, 24143090, - 14568123, 30185756, -12247770, -33528939, 8345319, -}; -static const fe lop_y = { - -6952922, -1265500, 6862341, -7057498, -4037696, - -5447722, 31680899, -15325402, -19365852, 1569102, -}; -static const fe ufactor = { - -1917299, 15887451, -18755900, -7000830, -24778944, - 544946, -16816446, 4011309, -653372, 10741468, -}; -static const fe A2 = { - 12721188, 3529, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -static void fe_0(fe h) { ZERO(h , 10); } -static void fe_1(fe h) { h[0] = 1; ZERO(h+1, 9); } - -static void fe_copy(fe h,const fe f ){FOR(i,0,10) h[i] = f[i]; } -static void fe_neg (fe h,const fe f ){FOR(i,0,10) h[i] = -f[i]; } -static void fe_add (fe h,const fe f,const fe g){FOR(i,0,10) h[i] = f[i] + g[i];} -static void fe_sub (fe h,const fe f,const fe g){FOR(i,0,10) h[i] = f[i] - g[i];} - -static void fe_cswap(fe f, fe g, int b) -{ - i32 mask = -b; // -1 = 0xffffffff - FOR (i, 0, 10) { - i32 x = (f[i] ^ g[i]) & mask; - f[i] = f[i] ^ x; - g[i] = g[i] ^ x; - } -} - -static void fe_ccopy(fe f, const fe g, int b) -{ - i32 mask = -b; // -1 = 0xffffffff - FOR (i, 0, 10) { - i32 x = (f[i] ^ g[i]) & mask; - f[i] = f[i] ^ x; - } -} - - -// Signed carry propagation -// ------------------------ -// -// Let t be a number. It can be uniquely decomposed thus: -// -// t = h*2^26 + l -// such that -2^25 <= l < 2^25 -// -// Let c = (t + 2^25) / 2^26 (rounded down) -// c = (h*2^26 + l + 2^25) / 2^26 (rounded down) -// c = h + (l + 2^25) / 2^26 (rounded down) -// c = h (exactly) -// Because 0 <= l + 2^25 < 2^26 -// -// Let u = t - c*2^26 -// u = h*2^26 + l - h*2^26 -// u = l -// Therefore, -2^25 <= u < 2^25 -// -// Additionally, if |t| < x, then |h| < x/2^26 (rounded down) -// -// Notations: -// - In C, 1<<25 means 2^25. -// - In C, x>>25 means floor(x / (2^25)). -// - All of the above applies with 25 & 24 as well as 26 & 25. -// -// -// Note on negative right shifts -// ----------------------------- -// -// In C, x >> n, where x is a negative integer, is implementation -// defined. In practice, all platforms do arithmetic shift, which is -// equivalent to division by 2^26, rounded down. Some compilers, like -// GCC, even guarantee it. -// -// If we ever stumble upon a platform that does not propagate the sign -// bit (we won't), visible failures will show at the slightest test, and -// the signed shifts can be replaced by the following: -// -// typedef struct { i64 x:39; } s25; -// typedef struct { i64 x:38; } s26; -// i64 shift25(i64 x) { s25 s; s.x = ((u64)x)>>25; return s.x; } -// i64 shift26(i64 x) { s26 s; s.x = ((u64)x)>>26; return s.x; } -// -// Current compilers cannot optimise this, causing a 30% drop in -// performance. Fairly expensive for something that never happens. -// -// -// Precondition -// ------------ -// -// |t0| < 2^63 -// |t1|..|t9| < 2^62 -// -// Algorithm -// --------- -// c = t0 + 2^25 / 2^26 -- |c| <= 2^36 -// t0 -= c * 2^26 -- |t0| <= 2^25 -// t1 += c -- |t1| <= 2^63 -// -// c = t4 + 2^25 / 2^26 -- |c| <= 2^36 -// t4 -= c * 2^26 -- |t4| <= 2^25 -// t5 += c -- |t5| <= 2^63 -// -// c = t1 + 2^24 / 2^25 -- |c| <= 2^38 -// t1 -= c * 2^25 -- |t1| <= 2^24 -// t2 += c -- |t2| <= 2^63 -// -// c = t5 + 2^24 / 2^25 -- |c| <= 2^38 -// t5 -= c * 2^25 -- |t5| <= 2^24 -// t6 += c -- |t6| <= 2^63 -// -// c = t2 + 2^25 / 2^26 -- |c| <= 2^37 -// t2 -= c * 2^26 -- |t2| <= 2^25 < 1.1 * 2^25 (final t2) -// t3 += c -- |t3| <= 2^63 -// -// c = t6 + 2^25 / 2^26 -- |c| <= 2^37 -// t6 -= c * 2^26 -- |t6| <= 2^25 < 1.1 * 2^25 (final t6) -// t7 += c -- |t7| <= 2^63 -// -// c = t3 + 2^24 / 2^25 -- |c| <= 2^38 -// t3 -= c * 2^25 -- |t3| <= 2^24 < 1.1 * 2^24 (final t3) -// t4 += c -- |t4| <= 2^25 + 2^38 < 2^39 -// -// c = t7 + 2^24 / 2^25 -- |c| <= 2^38 -// t7 -= c * 2^25 -- |t7| <= 2^24 < 1.1 * 2^24 (final t7) -// t8 += c -- |t8| <= 2^63 -// -// c = t4 + 2^25 / 2^26 -- |c| <= 2^13 -// t4 -= c * 2^26 -- |t4| <= 2^25 < 1.1 * 2^25 (final t4) -// t5 += c -- |t5| <= 2^24 + 2^13 < 1.1 * 2^24 (final t5) -// -// c = t8 + 2^25 / 2^26 -- |c| <= 2^37 -// t8 -= c * 2^26 -- |t8| <= 2^25 < 1.1 * 2^25 (final t8) -// t9 += c -- |t9| <= 2^63 -// -// c = t9 + 2^24 / 2^25 -- |c| <= 2^38 -// t9 -= c * 2^25 -- |t9| <= 2^24 < 1.1 * 2^24 (final t9) -// t0 += c * 19 -- |t0| <= 2^25 + 2^38*19 < 2^44 -// -// c = t0 + 2^25 / 2^26 -- |c| <= 2^18 -// t0 -= c * 2^26 -- |t0| <= 2^25 < 1.1 * 2^25 (final t0) -// t1 += c -- |t1| <= 2^24 + 2^18 < 1.1 * 2^24 (final t1) -// -// Postcondition -// ------------- -// |t0|, |t2|, |t4|, |t6|, |t8| < 1.1 * 2^25 -// |t1|, |t3|, |t5|, |t7|, |t9| < 1.1 * 2^24 -#define FE_CARRY \ - i64 c; \ - c = (t0 + ((i64)1<<25)) >> 26; t0 -= c * ((i64)1 << 26); t1 += c; \ - c = (t4 + ((i64)1<<25)) >> 26; t4 -= c * ((i64)1 << 26); t5 += c; \ - c = (t1 + ((i64)1<<24)) >> 25; t1 -= c * ((i64)1 << 25); t2 += c; \ - c = (t5 + ((i64)1<<24)) >> 25; t5 -= c * ((i64)1 << 25); t6 += c; \ - c = (t2 + ((i64)1<<25)) >> 26; t2 -= c * ((i64)1 << 26); t3 += c; \ - c = (t6 + ((i64)1<<25)) >> 26; t6 -= c * ((i64)1 << 26); t7 += c; \ - c = (t3 + ((i64)1<<24)) >> 25; t3 -= c * ((i64)1 << 25); t4 += c; \ - c = (t7 + ((i64)1<<24)) >> 25; t7 -= c * ((i64)1 << 25); t8 += c; \ - c = (t4 + ((i64)1<<25)) >> 26; t4 -= c * ((i64)1 << 26); t5 += c; \ - c = (t8 + ((i64)1<<25)) >> 26; t8 -= c * ((i64)1 << 26); t9 += c; \ - c = (t9 + ((i64)1<<24)) >> 25; t9 -= c * ((i64)1 << 25); t0 += c * 19; \ - c = (t0 + ((i64)1<<25)) >> 26; t0 -= c * ((i64)1 << 26); t1 += c; \ - h[0]=(i32)t0; h[1]=(i32)t1; h[2]=(i32)t2; h[3]=(i32)t3; h[4]=(i32)t4; \ - h[5]=(i32)t5; h[6]=(i32)t6; h[7]=(i32)t7; h[8]=(i32)t8; h[9]=(i32)t9 - -// Decodes a field element from a byte buffer. -// mask specifies how many bits we ignore. -// Traditionally we ignore 1. It's useful for EdDSA, -// which uses that bit to denote the sign of x. -// Elligator however uses positive representatives, -// which means ignoring 2 bits instead. -static void fe_frombytes_mask(fe h, const u8 s[32], unsigned nb_mask) -{ - u32 mask = 0xffffff >> nb_mask; - i64 t0 = load32_le(s); // t0 < 2^32 - i64 t1 = load24_le(s + 4) << 6; // t1 < 2^30 - i64 t2 = load24_le(s + 7) << 5; // t2 < 2^29 - i64 t3 = load24_le(s + 10) << 3; // t3 < 2^27 - i64 t4 = load24_le(s + 13) << 2; // t4 < 2^26 - i64 t5 = load32_le(s + 16); // t5 < 2^32 - i64 t6 = load24_le(s + 20) << 7; // t6 < 2^31 - i64 t7 = load24_le(s + 23) << 5; // t7 < 2^29 - i64 t8 = load24_le(s + 26) << 4; // t8 < 2^28 - i64 t9 = (load24_le(s + 29) & mask) << 2; // t9 < 2^25 - FE_CARRY; // Carry precondition OK -} - -static void fe_frombytes(fe h, const u8 s[32]) -{ - fe_frombytes_mask(h, s, 1); -} - - -// Precondition -// |h[0]|, |h[2]|, |h[4]|, |h[6]|, |h[8]| < 1.1 * 2^25 -// |h[1]|, |h[3]|, |h[5]|, |h[7]|, |h[9]| < 1.1 * 2^24 -// -// Therefore, |h| < 2^255-19 -// There are two possibilities: -// -// - If h is positive, all we need to do is reduce its individual -// limbs down to their tight positive range. -// - If h is negative, we also need to add 2^255-19 to it. -// Or just remove 19 and chop off any excess bit. -static void fe_tobytes(u8 s[32], const fe h) -{ - i32 t[10]; - COPY(t, h, 10); - i32 q = (19 * t[9] + (((i32) 1) << 24)) >> 25; - // |t9| < 1.1 * 2^24 - // -1.1 * 2^24 < t9 < 1.1 * 2^24 - // -21 * 2^24 < 19 * t9 < 21 * 2^24 - // -2^29 < 19 * t9 + 2^24 < 2^29 - // -2^29 / 2^25 < (19 * t9 + 2^24) / 2^25 < 2^29 / 2^25 - // -16 < (19 * t9 + 2^24) / 2^25 < 16 - FOR (i, 0, 5) { - q += t[2*i ]; q >>= 26; // q = 0 or -1 - q += t[2*i+1]; q >>= 25; // q = 0 or -1 - } - // q = 0 iff h >= 0 - // q = -1 iff h < 0 - // Adding q * 19 to h reduces h to its proper range. - q *= 19; // Shift carry back to the beginning - FOR (i, 0, 5) { - t[i*2 ] += q; q = t[i*2 ] >> 26; t[i*2 ] -= q * ((i32)1 << 26); - t[i*2+1] += q; q = t[i*2+1] >> 25; t[i*2+1] -= q * ((i32)1 << 25); - } - // h is now fully reduced, and q represents the excess bit. - - store32_le(s + 0, ((u32)t[0] >> 0) | ((u32)t[1] << 26)); - store32_le(s + 4, ((u32)t[1] >> 6) | ((u32)t[2] << 19)); - store32_le(s + 8, ((u32)t[2] >> 13) | ((u32)t[3] << 13)); - store32_le(s + 12, ((u32)t[3] >> 19) | ((u32)t[4] << 6)); - store32_le(s + 16, ((u32)t[5] >> 0) | ((u32)t[6] << 25)); - store32_le(s + 20, ((u32)t[6] >> 7) | ((u32)t[7] << 19)); - store32_le(s + 24, ((u32)t[7] >> 13) | ((u32)t[8] << 12)); - store32_le(s + 28, ((u32)t[8] >> 20) | ((u32)t[9] << 6)); - - WIPE_BUFFER(t); -} - -// Precondition -// ------------- -// |f0|, |f2|, |f4|, |f6|, |f8| < 1.65 * 2^26 -// |f1|, |f3|, |f5|, |f7|, |f9| < 1.65 * 2^25 -// -// |g0|, |g2|, |g4|, |g6|, |g8| < 1.65 * 2^26 -// |g1|, |g3|, |g5|, |g7|, |g9| < 1.65 * 2^25 -static void fe_mul_small(fe h, const fe f, i32 g) -{ - i64 t0 = f[0] * (i64) g; i64 t1 = f[1] * (i64) g; - i64 t2 = f[2] * (i64) g; i64 t3 = f[3] * (i64) g; - i64 t4 = f[4] * (i64) g; i64 t5 = f[5] * (i64) g; - i64 t6 = f[6] * (i64) g; i64 t7 = f[7] * (i64) g; - i64 t8 = f[8] * (i64) g; i64 t9 = f[9] * (i64) g; - // |t0|, |t2|, |t4|, |t6|, |t8| < 1.65 * 2^26 * 2^31 < 2^58 - // |t1|, |t3|, |t5|, |t7|, |t9| < 1.65 * 2^25 * 2^31 < 2^57 - - FE_CARRY; // Carry precondition OK -} - -// Precondition -// ------------- -// |f0|, |f2|, |f4|, |f6|, |f8| < 1.65 * 2^26 -// |f1|, |f3|, |f5|, |f7|, |f9| < 1.65 * 2^25 -// -// |g0|, |g2|, |g4|, |g6|, |g8| < 1.65 * 2^26 -// |g1|, |g3|, |g5|, |g7|, |g9| < 1.65 * 2^25 -static void fe_mul(fe h, const fe f, const fe g) -{ - // Everything is unrolled and put in temporary variables. - // We could roll the loop, but that would make curve25519 twice as slow. - i32 f0 = f[0]; i32 f1 = f[1]; i32 f2 = f[2]; i32 f3 = f[3]; i32 f4 = f[4]; - i32 f5 = f[5]; i32 f6 = f[6]; i32 f7 = f[7]; i32 f8 = f[8]; i32 f9 = f[9]; - i32 g0 = g[0]; i32 g1 = g[1]; i32 g2 = g[2]; i32 g3 = g[3]; i32 g4 = g[4]; - i32 g5 = g[5]; i32 g6 = g[6]; i32 g7 = g[7]; i32 g8 = g[8]; i32 g9 = g[9]; - i32 F1 = f1*2; i32 F3 = f3*2; i32 F5 = f5*2; i32 F7 = f7*2; i32 F9 = f9*2; - i32 G1 = g1*19; i32 G2 = g2*19; i32 G3 = g3*19; - i32 G4 = g4*19; i32 G5 = g5*19; i32 G6 = g6*19; - i32 G7 = g7*19; i32 G8 = g8*19; i32 G9 = g9*19; - // |F1|, |F3|, |F5|, |F7|, |F9| < 1.65 * 2^26 - // |G0|, |G2|, |G4|, |G6|, |G8| < 2^31 - // |G1|, |G3|, |G5|, |G7|, |G9| < 2^30 - - i64 t0 = f0*(i64)g0 + F1*(i64)G9 + f2*(i64)G8 + F3*(i64)G7 + f4*(i64)G6 - + F5*(i64)G5 + f6*(i64)G4 + F7*(i64)G3 + f8*(i64)G2 + F9*(i64)G1; - i64 t1 = f0*(i64)g1 + f1*(i64)g0 + f2*(i64)G9 + f3*(i64)G8 + f4*(i64)G7 - + f5*(i64)G6 + f6*(i64)G5 + f7*(i64)G4 + f8*(i64)G3 + f9*(i64)G2; - i64 t2 = f0*(i64)g2 + F1*(i64)g1 + f2*(i64)g0 + F3*(i64)G9 + f4*(i64)G8 - + F5*(i64)G7 + f6*(i64)G6 + F7*(i64)G5 + f8*(i64)G4 + F9*(i64)G3; - i64 t3 = f0*(i64)g3 + f1*(i64)g2 + f2*(i64)g1 + f3*(i64)g0 + f4*(i64)G9 - + f5*(i64)G8 + f6*(i64)G7 + f7*(i64)G6 + f8*(i64)G5 + f9*(i64)G4; - i64 t4 = f0*(i64)g4 + F1*(i64)g3 + f2*(i64)g2 + F3*(i64)g1 + f4*(i64)g0 - + F5*(i64)G9 + f6*(i64)G8 + F7*(i64)G7 + f8*(i64)G6 + F9*(i64)G5; - i64 t5 = f0*(i64)g5 + f1*(i64)g4 + f2*(i64)g3 + f3*(i64)g2 + f4*(i64)g1 - + f5*(i64)g0 + f6*(i64)G9 + f7*(i64)G8 + f8*(i64)G7 + f9*(i64)G6; - i64 t6 = f0*(i64)g6 + F1*(i64)g5 + f2*(i64)g4 + F3*(i64)g3 + f4*(i64)g2 - + F5*(i64)g1 + f6*(i64)g0 + F7*(i64)G9 + f8*(i64)G8 + F9*(i64)G7; - i64 t7 = f0*(i64)g7 + f1*(i64)g6 + f2*(i64)g5 + f3*(i64)g4 + f4*(i64)g3 - + f5*(i64)g2 + f6*(i64)g1 + f7*(i64)g0 + f8*(i64)G9 + f9*(i64)G8; - i64 t8 = f0*(i64)g8 + F1*(i64)g7 + f2*(i64)g6 + F3*(i64)g5 + f4*(i64)g4 - + F5*(i64)g3 + f6*(i64)g2 + F7*(i64)g1 + f8*(i64)g0 + F9*(i64)G9; - i64 t9 = f0*(i64)g9 + f1*(i64)g8 + f2*(i64)g7 + f3*(i64)g6 + f4*(i64)g5 - + f5*(i64)g4 + f6*(i64)g3 + f7*(i64)g2 + f8*(i64)g1 + f9*(i64)g0; - // t0 < 0.67 * 2^61 - // t1 < 0.41 * 2^61 - // t2 < 0.52 * 2^61 - // t3 < 0.32 * 2^61 - // t4 < 0.38 * 2^61 - // t5 < 0.22 * 2^61 - // t6 < 0.23 * 2^61 - // t7 < 0.13 * 2^61 - // t8 < 0.09 * 2^61 - // t9 < 0.03 * 2^61 - - FE_CARRY; // Everything below 2^62, Carry precondition OK -} - -// Precondition -// ------------- -// |f0|, |f2|, |f4|, |f6|, |f8| < 1.65 * 2^26 -// |f1|, |f3|, |f5|, |f7|, |f9| < 1.65 * 2^25 -// -// Note: we could use fe_mul() for this, but this is significantly faster -static void fe_sq(fe h, const fe f) -{ - i32 f0 = f[0]; i32 f1 = f[1]; i32 f2 = f[2]; i32 f3 = f[3]; i32 f4 = f[4]; - i32 f5 = f[5]; i32 f6 = f[6]; i32 f7 = f[7]; i32 f8 = f[8]; i32 f9 = f[9]; - i32 f0_2 = f0*2; i32 f1_2 = f1*2; i32 f2_2 = f2*2; i32 f3_2 = f3*2; - i32 f4_2 = f4*2; i32 f5_2 = f5*2; i32 f6_2 = f6*2; i32 f7_2 = f7*2; - i32 f5_38 = f5*38; i32 f6_19 = f6*19; i32 f7_38 = f7*38; - i32 f8_19 = f8*19; i32 f9_38 = f9*38; - // |f0_2| , |f2_2| , |f4_2| , |f6_2| , |f8_2| < 1.65 * 2^27 - // |f1_2| , |f3_2| , |f5_2| , |f7_2| , |f9_2| < 1.65 * 2^26 - // |f5_38|, |f6_19|, |f7_38|, |f8_19|, |f9_38| < 2^31 - - i64 t0 = f0 *(i64)f0 + f1_2*(i64)f9_38 + f2_2*(i64)f8_19 - + f3_2*(i64)f7_38 + f4_2*(i64)f6_19 + f5 *(i64)f5_38; - i64 t1 = f0_2*(i64)f1 + f2 *(i64)f9_38 + f3_2*(i64)f8_19 - + f4 *(i64)f7_38 + f5_2*(i64)f6_19; - i64 t2 = f0_2*(i64)f2 + f1_2*(i64)f1 + f3_2*(i64)f9_38 - + f4_2*(i64)f8_19 + f5_2*(i64)f7_38 + f6 *(i64)f6_19; - i64 t3 = f0_2*(i64)f3 + f1_2*(i64)f2 + f4 *(i64)f9_38 - + f5_2*(i64)f8_19 + f6 *(i64)f7_38; - i64 t4 = f0_2*(i64)f4 + f1_2*(i64)f3_2 + f2 *(i64)f2 - + f5_2*(i64)f9_38 + f6_2*(i64)f8_19 + f7 *(i64)f7_38; - i64 t5 = f0_2*(i64)f5 + f1_2*(i64)f4 + f2_2*(i64)f3 - + f6 *(i64)f9_38 + f7_2*(i64)f8_19; - i64 t6 = f0_2*(i64)f6 + f1_2*(i64)f5_2 + f2_2*(i64)f4 - + f3_2*(i64)f3 + f7_2*(i64)f9_38 + f8 *(i64)f8_19; - i64 t7 = f0_2*(i64)f7 + f1_2*(i64)f6 + f2_2*(i64)f5 - + f3_2*(i64)f4 + f8 *(i64)f9_38; - i64 t8 = f0_2*(i64)f8 + f1_2*(i64)f7_2 + f2_2*(i64)f6 - + f3_2*(i64)f5_2 + f4 *(i64)f4 + f9 *(i64)f9_38; - i64 t9 = f0_2*(i64)f9 + f1_2*(i64)f8 + f2_2*(i64)f7 - + f3_2*(i64)f6 + f4 *(i64)f5_2; - // t0 < 0.67 * 2^61 - // t1 < 0.41 * 2^61 - // t2 < 0.52 * 2^61 - // t3 < 0.32 * 2^61 - // t4 < 0.38 * 2^61 - // t5 < 0.22 * 2^61 - // t6 < 0.23 * 2^61 - // t7 < 0.13 * 2^61 - // t8 < 0.09 * 2^61 - // t9 < 0.03 * 2^61 - - FE_CARRY; -} - -// Parity check. Returns 0 if even, 1 if odd -static int fe_isodd(const fe f) -{ - u8 s[32]; - fe_tobytes(s, f); - u8 isodd = s[0] & 1; - WIPE_BUFFER(s); - return isodd; -} - -// Returns 1 if equal, 0 if not equal -static int fe_isequal(const fe f, const fe g) -{ - u8 fs[32]; - u8 gs[32]; - fe_tobytes(fs, f); - fe_tobytes(gs, g); - int isdifferent = crypto_verify32(fs, gs); - WIPE_BUFFER(fs); - WIPE_BUFFER(gs); - return 1 + isdifferent; -} - -// Inverse square root. -// Returns true if x is a square, false otherwise. -// After the call: -// isr = sqrt(1/x) if x is a non-zero square. -// isr = sqrt(sqrt(-1)/x) if x is not a square. -// isr = 0 if x is zero. -// We do not guarantee the sign of the square root. -// -// Notes: -// Let quartic = x^((p-1)/4) -// -// x^((p-1)/2) = chi(x) -// quartic^2 = chi(x) -// quartic = sqrt(chi(x)) -// quartic = 1 or -1 or sqrt(-1) or -sqrt(-1) -// -// Note that x is a square if quartic is 1 or -1 -// There are 4 cases to consider: -// -// if quartic = 1 (x is a square) -// then x^((p-1)/4) = 1 -// x^((p-5)/4) * x = 1 -// x^((p-5)/4) = 1/x -// x^((p-5)/8) = sqrt(1/x) or -sqrt(1/x) -// -// if quartic = -1 (x is a square) -// then x^((p-1)/4) = -1 -// x^((p-5)/4) * x = -1 -// x^((p-5)/4) = -1/x -// x^((p-5)/8) = sqrt(-1) / sqrt(x) -// x^((p-5)/8) * sqrt(-1) = sqrt(-1)^2 / sqrt(x) -// x^((p-5)/8) * sqrt(-1) = -1/sqrt(x) -// x^((p-5)/8) * sqrt(-1) = -sqrt(1/x) or sqrt(1/x) -// -// if quartic = sqrt(-1) (x is not a square) -// then x^((p-1)/4) = sqrt(-1) -// x^((p-5)/4) * x = sqrt(-1) -// x^((p-5)/4) = sqrt(-1)/x -// x^((p-5)/8) = sqrt(sqrt(-1)/x) or -sqrt(sqrt(-1)/x) -// -// Note that the product of two non-squares is always a square: -// For any non-squares a and b, chi(a) = -1 and chi(b) = -1. -// Since chi(x) = x^((p-1)/2), chi(a)*chi(b) = chi(a*b) = 1. -// Therefore a*b is a square. -// -// Since sqrt(-1) and x are both non-squares, their product is a -// square, and we can compute their square root. -// -// if quartic = -sqrt(-1) (x is not a square) -// then x^((p-1)/4) = -sqrt(-1) -// x^((p-5)/4) * x = -sqrt(-1) -// x^((p-5)/4) = -sqrt(-1)/x -// x^((p-5)/8) = sqrt(-sqrt(-1)/x) -// x^((p-5)/8) = sqrt( sqrt(-1)/x) * sqrt(-1) -// x^((p-5)/8) * sqrt(-1) = sqrt( sqrt(-1)/x) * sqrt(-1)^2 -// x^((p-5)/8) * sqrt(-1) = sqrt( sqrt(-1)/x) * -1 -// x^((p-5)/8) * sqrt(-1) = -sqrt(sqrt(-1)/x) or sqrt(sqrt(-1)/x) -static int invsqrt(fe isr, const fe x) -{ - fe t0, t1, t2; - - // t0 = x^((p-5)/8) - // Can be achieved with a simple double & add ladder, - // but it would be slower. - fe_sq(t0, x); - fe_sq(t1,t0); fe_sq(t1, t1); fe_mul(t1, x, t1); - fe_mul(t0, t0, t1); - fe_sq(t0, t0); fe_mul(t0, t1, t0); - fe_sq(t1, t0); FOR (i, 1, 5) fe_sq(t1, t1); fe_mul(t0, t1, t0); - fe_sq(t1, t0); FOR (i, 1, 10) fe_sq(t1, t1); fe_mul(t1, t1, t0); - fe_sq(t2, t1); FOR (i, 1, 20) fe_sq(t2, t2); fe_mul(t1, t2, t1); - fe_sq(t1, t1); FOR (i, 1, 10) fe_sq(t1, t1); fe_mul(t0, t1, t0); - fe_sq(t1, t0); FOR (i, 1, 50) fe_sq(t1, t1); fe_mul(t1, t1, t0); - fe_sq(t2, t1); FOR (i, 1, 100) fe_sq(t2, t2); fe_mul(t1, t2, t1); - fe_sq(t1, t1); FOR (i, 1, 50) fe_sq(t1, t1); fe_mul(t0, t1, t0); - fe_sq(t0, t0); FOR (i, 1, 2) fe_sq(t0, t0); fe_mul(t0, t0, x); - - // quartic = x^((p-1)/4) - i32 *quartic = t1; - fe_sq (quartic, t0); - fe_mul(quartic, quartic, x); - - i32 *check = t2; - fe_0 (check); int z0 = fe_isequal(x , check); - fe_1 (check); int p1 = fe_isequal(quartic, check); - fe_neg(check, check ); int m1 = fe_isequal(quartic, check); - fe_neg(check, sqrtm1); int ms = fe_isequal(quartic, check); - - // if quartic == -1 or sqrt(-1) - // then isr = x^((p-1)/4) * sqrt(-1) - // else isr = x^((p-1)/4) - fe_mul(isr, t0, sqrtm1); - fe_ccopy(isr, t0, 1 - (m1 | ms)); - - WIPE_BUFFER(t0); - WIPE_BUFFER(t1); - WIPE_BUFFER(t2); - return p1 | m1 | z0; -} - -// Inverse in terms of inverse square root. -// Requires two additional squarings to get rid of the sign. -// -// 1/x = x * (+invsqrt(x^2))^2 -// = x * (-invsqrt(x^2))^2 -// -// A fully optimised exponentiation by p-1 would save 6 field -// multiplications, but it would require more code. -static void fe_invert(fe out, const fe x) -{ - fe tmp; - fe_sq(tmp, x); - invsqrt(tmp, tmp); - fe_sq(tmp, tmp); - fe_mul(out, tmp, x); - WIPE_BUFFER(tmp); -} - -// trim a scalar for scalar multiplication -void crypto_eddsa_trim_scalar(u8 out[32], const u8 in[32]) -{ - COPY(out, in, 32); - out[ 0] &= 248; - out[31] &= 127; - out[31] |= 64; -} - -// get bit from scalar at position i -static int scalar_bit(const u8 s[32], int i) -{ - if (i < 0) { return 0; } // handle -1 for sliding windows - return (s[i>>3] >> (i&7)) & 1; -} - -/////////////// -/// X-25519 /// Taken from SUPERCOP's ref10 implementation. -/////////////// -static void scalarmult(u8 q[32], const u8 scalar[32], const u8 p[32], - int nb_bits) -{ - // computes the scalar product - fe x1; - fe_frombytes(x1, p); - - // computes the actual scalar product (the result is in x2 and z2) - fe x2, z2, x3, z3, t0, t1; - // Montgomery ladder - // In projective coordinates, to avoid divisions: x = X / Z - // We don't care about the y coordinate, it's only 1 bit of information - fe_1(x2); fe_0(z2); // "zero" point - fe_copy(x3, x1); fe_1(z3); // "one" point - int swap = 0; - for (int pos = nb_bits-1; pos >= 0; --pos) { - // constant time conditional swap before ladder step - int b = scalar_bit(scalar, pos); - swap ^= b; // xor trick avoids swapping at the end of the loop - fe_cswap(x2, x3, swap); - fe_cswap(z2, z3, swap); - swap = b; // anticipates one last swap after the loop - - // Montgomery ladder step: replaces (P2, P3) by (P2*2, P2+P3) - // with differential addition - fe_sub(t0, x3, z3); - fe_sub(t1, x2, z2); - fe_add(x2, x2, z2); - fe_add(z2, x3, z3); - fe_mul(z3, t0, x2); - fe_mul(z2, z2, t1); - fe_sq (t0, t1 ); - fe_sq (t1, x2 ); - fe_add(x3, z3, z2); - fe_sub(z2, z3, z2); - fe_mul(x2, t1, t0); - fe_sub(t1, t1, t0); - fe_sq (z2, z2 ); - fe_mul_small(z3, t1, 121666); - fe_sq (x3, x3 ); - fe_add(t0, t0, z3); - fe_mul(z3, x1, z2); - fe_mul(z2, t1, t0); - } - // last swap is necessary to compensate for the xor trick - // Note: after this swap, P3 == P2 + P1. - fe_cswap(x2, x3, swap); - fe_cswap(z2, z3, swap); - - // normalises the coordinates: x == X / Z - fe_invert(z2, z2); - fe_mul(x2, x2, z2); - fe_tobytes(q, x2); - - WIPE_BUFFER(x1); - WIPE_BUFFER(x2); WIPE_BUFFER(z2); WIPE_BUFFER(t0); - WIPE_BUFFER(x3); WIPE_BUFFER(z3); WIPE_BUFFER(t1); -} - -void crypto_x25519(u8 raw_shared_secret[32], - const u8 your_secret_key [32], - const u8 their_public_key [32]) -{ - // restrict the possible scalar values - u8 e[32]; - crypto_eddsa_trim_scalar(e, your_secret_key); - scalarmult(raw_shared_secret, e, their_public_key, 255); - WIPE_BUFFER(e); -} - -void crypto_x25519_public_key(u8 public_key[32], - const u8 secret_key[32]) -{ - static const u8 base_point[32] = {9}; - crypto_x25519(public_key, secret_key, base_point); -} - -/////////////////////////// -/// Arithmetic modulo L /// -/////////////////////////// -static const u32 L[8] = { - 0x5cf5d3ed, 0x5812631a, 0xa2f79cd6, 0x14def9de, - 0x00000000, 0x00000000, 0x00000000, 0x10000000, -}; - -// p = a*b + p -static void multiply(u32 p[16], const u32 a[8], const u32 b[8]) -{ - FOR (i, 0, 8) { - u64 carry = 0; - FOR (j, 0, 8) { - carry += p[i+j] + (u64)a[i] * b[j]; - p[i+j] = (u32)carry; - carry >>= 32; - } - p[i+8] = (u32)carry; - } -} - -static int is_above_l(const u32 x[8]) -{ - // We work with L directly, in a 2's complement encoding - // (-L == ~L + 1) - u64 carry = 1; - FOR (i, 0, 8) { - carry += (u64)x[i] + (~L[i] & 0xffffffff); - carry >>= 32; - } - return (int)carry; // carry is either 0 or 1 -} - -// Final reduction modulo L, by conditionally removing L. -// if x < l , then r = x -// if l <= x 2*l, then r = x-l -// otherwise the result will be wrong -static void remove_l(u32 r[8], const u32 x[8]) -{ - u64 carry = (u64)is_above_l(x); - u32 mask = ~(u32)carry + 1; // carry == 0 or 1 - FOR (i, 0, 8) { - carry += (u64)x[i] + (~L[i] & mask); - r[i] = (u32)carry; - carry >>= 32; - } -} - -// Full reduction modulo L (Barrett reduction) -static void mod_l(u8 reduced[32], const u32 x[16]) -{ - static const u32 r[9] = { - 0x0a2c131b,0xed9ce5a3,0x086329a7,0x2106215d, - 0xffffffeb,0xffffffff,0xffffffff,0xffffffff,0xf, - }; - // xr = x * r - u32 xr[25] = {0}; - FOR (i, 0, 9) { - u64 carry = 0; - FOR (j, 0, 16) { - carry += xr[i+j] + (u64)r[i] * x[j]; - xr[i+j] = (u32)carry; - carry >>= 32; - } - xr[i+16] = (u32)carry; - } - // xr = floor(xr / 2^512) * L - // Since the result is guaranteed to be below 2*L, - // it is enough to only compute the first 256 bits. - // The division is performed by saying xr[i+16]. (16 * 32 = 512) - ZERO(xr, 8); - FOR (i, 0, 8) { - u64 carry = 0; - FOR (j, 0, 8-i) { - carry += xr[i+j] + (u64)xr[i+16] * L[j]; - xr[i+j] = (u32)carry; - carry >>= 32; - } - } - // xr = x - xr - u64 carry = 1; - FOR (i, 0, 8) { - carry += (u64)x[i] + (~xr[i] & 0xffffffff); - xr[i] = (u32)carry; - carry >>= 32; - } - // Final reduction modulo L (conditional subtraction) - remove_l(xr, xr); - store32_le_buf(reduced, xr, 8); - - WIPE_BUFFER(xr); -} - -void crypto_eddsa_reduce(u8 reduced[32], const u8 expanded[64]) -{ - u32 x[16]; - load32_le_buf(x, expanded, 16); - mod_l(reduced, x); - WIPE_BUFFER(x); -} - -// r = (a * b) + c -void crypto_eddsa_mul_add(u8 r[32], - const u8 a[32], const u8 b[32], const u8 c[32]) -{ - u32 A[8]; load32_le_buf(A, a, 8); - u32 B[8]; load32_le_buf(B, b, 8); - u32 p[16]; load32_le_buf(p, c, 8); ZERO(p + 8, 8); - multiply(p, A, B); - mod_l(r, p); - WIPE_BUFFER(p); - WIPE_BUFFER(A); - WIPE_BUFFER(B); -} - -/////////////// -/// Ed25519 /// -/////////////// - -// Point (group element, ge) in a twisted Edwards curve, -// in extended projective coordinates. -// ge : x = X/Z, y = Y/Z, T = XY/Z -// ge_cached : Yp = X+Y, Ym = X-Y, T2 = T*D2 -// ge_precomp: Z = 1 -typedef struct { fe X; fe Y; fe Z; fe T; } ge; -typedef struct { fe Yp; fe Ym; fe Z; fe T2; } ge_cached; -typedef struct { fe Yp; fe Ym; fe T2; } ge_precomp; - -static void ge_zero(ge *p) -{ - fe_0(p->X); - fe_1(p->Y); - fe_1(p->Z); - fe_0(p->T); -} - -static void ge_tobytes(u8 s[32], const ge *h) -{ - fe recip, x, y; - fe_invert(recip, h->Z); - fe_mul(x, h->X, recip); - fe_mul(y, h->Y, recip); - fe_tobytes(s, y); - s[31] ^= fe_isodd(x) << 7; - - WIPE_BUFFER(recip); - WIPE_BUFFER(x); - WIPE_BUFFER(y); -} - -// h = -s, where s is a point encoded in 32 bytes -// -// Variable time! Inputs must not be secret! -// => Use only to *check* signatures. -// -// From the specifications: -// The encoding of s contains y and the sign of x -// x = sqrt((y^2 - 1) / (d*y^2 + 1)) -// In extended coordinates: -// X = x, Y = y, Z = 1, T = x*y -// -// Note that num * den is a square iff num / den is a square -// If num * den is not a square, the point was not on the curve. -// From the above: -// Let num = y^2 - 1 -// Let den = d*y^2 + 1 -// x = sqrt((y^2 - 1) / (d*y^2 + 1)) -// x = sqrt(num / den) -// x = sqrt(num^2 / (num * den)) -// x = num * sqrt(1 / (num * den)) -// -// Therefore, we can just compute: -// num = y^2 - 1 -// den = d*y^2 + 1 -// isr = invsqrt(num * den) // abort if not square -// x = num * isr -// Finally, negate x if its sign is not as specified. -static int ge_frombytes_neg_vartime(ge *h, const u8 s[32]) -{ - fe_frombytes(h->Y, s); - fe_1(h->Z); - fe_sq (h->T, h->Y); // t = y^2 - fe_mul(h->X, h->T, d ); // x = d*y^2 - fe_sub(h->T, h->T, h->Z); // t = y^2 - 1 - fe_add(h->X, h->X, h->Z); // x = d*y^2 + 1 - fe_mul(h->X, h->T, h->X); // x = (y^2 - 1) * (d*y^2 + 1) - int is_square = invsqrt(h->X, h->X); - if (!is_square) { - return -1; // Not on the curve, abort - } - fe_mul(h->X, h->T, h->X); // x = sqrt((y^2 - 1) / (d*y^2 + 1)) - if (fe_isodd(h->X) == (s[31] >> 7)) { - fe_neg(h->X, h->X); - } - fe_mul(h->T, h->X, h->Y); - return 0; -} - -static void ge_cache(ge_cached *c, const ge *p) -{ - fe_add (c->Yp, p->Y, p->X); - fe_sub (c->Ym, p->Y, p->X); - fe_copy(c->Z , p->Z ); - fe_mul (c->T2, p->T, D2 ); -} - -// Internal buffers are not wiped! Inputs must not be secret! -// => Use only to *check* signatures. -static void ge_add(ge *s, const ge *p, const ge_cached *q) -{ - fe a, b; - fe_add(a , p->Y, p->X ); - fe_sub(b , p->Y, p->X ); - fe_mul(a , a , q->Yp); - fe_mul(b , b , q->Ym); - fe_add(s->Y, a , b ); - fe_sub(s->X, a , b ); - - fe_add(s->Z, p->Z, p->Z ); - fe_mul(s->Z, s->Z, q->Z ); - fe_mul(s->T, p->T, q->T2); - fe_add(a , s->Z, s->T ); - fe_sub(b , s->Z, s->T ); - - fe_mul(s->T, s->X, s->Y); - fe_mul(s->X, s->X, b ); - fe_mul(s->Y, s->Y, a ); - fe_mul(s->Z, a , b ); -} - -// Internal buffers are not wiped! Inputs must not be secret! -// => Use only to *check* signatures. -static void ge_sub(ge *s, const ge *p, const ge_cached *q) -{ - ge_cached neg; - fe_copy(neg.Ym, q->Yp); - fe_copy(neg.Yp, q->Ym); - fe_copy(neg.Z , q->Z ); - fe_neg (neg.T2, q->T2); - ge_add(s, p, &neg); -} - -static void ge_madd(ge *s, const ge *p, const ge_precomp *q, fe a, fe b) -{ - fe_add(a , p->Y, p->X ); - fe_sub(b , p->Y, p->X ); - fe_mul(a , a , q->Yp); - fe_mul(b , b , q->Ym); - fe_add(s->Y, a , b ); - fe_sub(s->X, a , b ); - - fe_add(s->Z, p->Z, p->Z ); - fe_mul(s->T, p->T, q->T2); - fe_add(a , s->Z, s->T ); - fe_sub(b , s->Z, s->T ); - - fe_mul(s->T, s->X, s->Y); - fe_mul(s->X, s->X, b ); - fe_mul(s->Y, s->Y, a ); - fe_mul(s->Z, a , b ); -} - -// Internal buffers are not wiped! Inputs must not be secret! -// => Use only to *check* signatures. -static void ge_msub(ge *s, const ge *p, const ge_precomp *q, fe a, fe b) -{ - ge_precomp neg; - fe_copy(neg.Ym, q->Yp); - fe_copy(neg.Yp, q->Ym); - fe_neg (neg.T2, q->T2); - ge_madd(s, p, &neg, a, b); -} - -static void ge_double(ge *s, const ge *p, ge *q) -{ - fe_sq (q->X, p->X); - fe_sq (q->Y, p->Y); - fe_sq (q->Z, p->Z); // qZ = pZ^2 - fe_mul_small(q->Z, q->Z, 2); // qZ = pZ^2 * 2 - fe_add(q->T, p->X, p->Y); - fe_sq (s->T, q->T); - fe_add(q->T, q->Y, q->X); - fe_sub(q->Y, q->Y, q->X); - fe_sub(q->X, s->T, q->T); - fe_sub(q->Z, q->Z, q->Y); - - fe_mul(s->X, q->X , q->Z); - fe_mul(s->Y, q->T , q->Y); - fe_mul(s->Z, q->Y , q->Z); - fe_mul(s->T, q->X , q->T); -} - -// 5-bit signed window in cached format (Niels coordinates, Z=1) -static const ge_precomp b_window[8] = { - {{25967493,-14356035,29566456,3660896,-12694345, - 4014787,27544626,-11754271,-6079156,2047605,}, - {-12545711,934262,-2722910,3049990,-727428, - 9406986,12720692,5043384,19500929,-15469378,}, - {-8738181,4489570,9688441,-14785194,10184609, - -12363380,29287919,11864899,-24514362,-4438546,},}, - {{15636291,-9688557,24204773,-7912398,616977, - -16685262,27787600,-14772189,28944400,-1550024,}, - {16568933,4717097,-11556148,-1102322,15682896, - -11807043,16354577,-11775962,7689662,11199574,}, - {30464156,-5976125,-11779434,-15670865,23220365, - 15915852,7512774,10017326,-17749093,-9920357,},}, - {{10861363,11473154,27284546,1981175,-30064349, - 12577861,32867885,14515107,-15438304,10819380,}, - {4708026,6336745,20377586,9066809,-11272109, - 6594696,-25653668,12483688,-12668491,5581306,}, - {19563160,16186464,-29386857,4097519,10237984, - -4348115,28542350,13850243,-23678021,-15815942,},}, - {{5153746,9909285,1723747,-2777874,30523605, - 5516873,19480852,5230134,-23952439,-15175766,}, - {-30269007,-3463509,7665486,10083793,28475525, - 1649722,20654025,16520125,30598449,7715701,}, - {28881845,14381568,9657904,3680757,-20181635, - 7843316,-31400660,1370708,29794553,-1409300,},}, - {{-22518993,-6692182,14201702,-8745502,-23510406, - 8844726,18474211,-1361450,-13062696,13821877,}, - {-6455177,-7839871,3374702,-4740862,-27098617, - -10571707,31655028,-7212327,18853322,-14220951,}, - {4566830,-12963868,-28974889,-12240689,-7602672, - -2830569,-8514358,-10431137,2207753,-3209784,},}, - {{-25154831,-4185821,29681144,7868801,-6854661, - -9423865,-12437364,-663000,-31111463,-16132436,}, - {25576264,-2703214,7349804,-11814844,16472782, - 9300885,3844789,15725684,171356,6466918,}, - {23103977,13316479,9739013,-16149481,817875, - -15038942,8965339,-14088058,-30714912,16193877,},}, - {{-33521811,3180713,-2394130,14003687,-16903474, - -16270840,17238398,4729455,-18074513,9256800,}, - {-25182317,-4174131,32336398,5036987,-21236817, - 11360617,22616405,9761698,-19827198,630305,}, - {-13720693,2639453,-24237460,-7406481,9494427, - -5774029,-6554551,-15960994,-2449256,-14291300,},}, - {{-3151181,-5046075,9282714,6866145,-31907062, - -863023,-18940575,15033784,25105118,-7894876,}, - {-24326370,15950226,-31801215,-14592823,-11662737, - -5090925,1573892,-2625887,2198790,-15804619,}, - {-3099351,10324967,-2241613,7453183,-5446979, - -2735503,-13812022,-16236442,-32461234,-12290683,},}, -}; - -// Incremental sliding windows (left to right) -// Based on Roberto Maria Avanzi[2005] -typedef struct { - i16 next_index; // position of the next signed digit - i8 next_digit; // next signed digit (odd number below 2^window_width) - u8 next_check; // point at which we must check for a new window -} slide_ctx; - -static void slide_init(slide_ctx *ctx, const u8 scalar[32]) -{ - // scalar is guaranteed to be below L, either because we checked (s), - // or because we reduced it modulo L (h_ram). L is under 2^253, so - // so bits 253 to 255 are guaranteed to be zero. No need to test them. - // - // Note however that L is very close to 2^252, so bit 252 is almost - // always zero. If we were to start at bit 251, the tests wouldn't - // catch the off-by-one error (constructing one that does would be - // prohibitively expensive). - // - // We should still check bit 252, though. - int i = 252; - while (i > 0 && scalar_bit(scalar, i) == 0) { - i--; - } - ctx->next_check = (u8)(i + 1); - ctx->next_index = -1; - ctx->next_digit = -1; -} - -static int slide_step(slide_ctx *ctx, int width, int i, const u8 scalar[32]) -{ - if (i == ctx->next_check) { - if (scalar_bit(scalar, i) == scalar_bit(scalar, i - 1)) { - ctx->next_check--; - } else { - // compute digit of next window - int w = MIN(width, i + 1); - int v = -(scalar_bit(scalar, i) << (w-1)); - FOR_T (int, j, 0, w-1) { - v += scalar_bit(scalar, i-(w-1)+j) << j; - } - v += scalar_bit(scalar, i-w); - int lsb = v & (~v + 1); // smallest bit of v - int s = // log2(lsb) - (((lsb & 0xAA) != 0) << 0) | - (((lsb & 0xCC) != 0) << 1) | - (((lsb & 0xF0) != 0) << 2); - ctx->next_index = (i16)(i-(w-1)+s); - ctx->next_digit = (i8) (v >> s ); - ctx->next_check -= (u8) w; - } - } - return i == ctx->next_index ? ctx->next_digit: 0; -} - -#define P_W_WIDTH 3 // Affects the size of the stack -#define B_W_WIDTH 5 // Affects the size of the binary -#define P_W_SIZE (1<<(P_W_WIDTH-2)) - -int crypto_eddsa_check_equation(const u8 signature[64], const u8 public_key[32], - const u8 h[32]) -{ - ge minus_A; // -public_key - ge minus_R; // -first_half_of_signature - const u8 *s = signature + 32; - - // Check that A and R are on the curve - // Check that 0 <= S < L (prevents malleability) - // *Allow* non-cannonical encoding for A and R - { - u32 s32[8]; - load32_le_buf(s32, s, 8); - if (ge_frombytes_neg_vartime(&minus_A, public_key) || - ge_frombytes_neg_vartime(&minus_R, signature) || - is_above_l(s32)) { - return -1; - } - } - - // look-up table for minus_A - ge_cached lutA[P_W_SIZE]; - { - ge minus_A2, tmp; - ge_double(&minus_A2, &minus_A, &tmp); - ge_cache(&lutA[0], &minus_A); - FOR (i, 1, P_W_SIZE) { - ge_add(&tmp, &minus_A2, &lutA[i-1]); - ge_cache(&lutA[i], &tmp); - } - } - - // sum = [s]B - [h]A - // Merged double and add ladder, fused with sliding - slide_ctx h_slide; slide_init(&h_slide, h); - slide_ctx s_slide; slide_init(&s_slide, s); - int i = MAX(h_slide.next_check, s_slide.next_check); - ge *sum = &minus_A; // reuse minus_A for the sum - ge_zero(sum); - while (i >= 0) { - ge tmp; - ge_double(sum, sum, &tmp); - int h_digit = slide_step(&h_slide, P_W_WIDTH, i, h); - int s_digit = slide_step(&s_slide, B_W_WIDTH, i, s); - if (h_digit > 0) { ge_add(sum, sum, &lutA[ h_digit / 2]); } - if (h_digit < 0) { ge_sub(sum, sum, &lutA[-h_digit / 2]); } - fe t1, t2; - if (s_digit > 0) { ge_madd(sum, sum, b_window + s_digit/2, t1, t2); } - if (s_digit < 0) { ge_msub(sum, sum, b_window + -s_digit/2, t1, t2); } - i--; - } - - // Compare [8](sum-R) and the zero point - // The multiplication by 8 eliminates any low-order component - // and ensures consistency with batched verification. - ge_cached cached; - u8 check[32]; - static const u8 zero_point[32] = {1}; // Point of order 1 - ge_cache(&cached, &minus_R); - ge_add(sum, sum, &cached); - ge_double(sum, sum, &minus_R); // reuse minus_R as temporary - ge_double(sum, sum, &minus_R); // reuse minus_R as temporary - ge_double(sum, sum, &minus_R); // reuse minus_R as temporary - ge_tobytes(check, sum); - return crypto_verify32(check, zero_point); -} - -// 5-bit signed comb in cached format (Niels coordinates, Z=1) -static const ge_precomp b_comb_low[8] = { - {{-6816601,-2324159,-22559413,124364,18015490, - 8373481,19993724,1979872,-18549925,9085059,}, - {10306321,403248,14839893,9633706,8463310, - -8354981,-14305673,14668847,26301366,2818560,}, - {-22701500,-3210264,-13831292,-2927732,-16326337, - -14016360,12940910,177905,12165515,-2397893,},}, - {{-12282262,-7022066,9920413,-3064358,-32147467, - 2927790,22392436,-14852487,2719975,16402117,}, - {-7236961,-4729776,2685954,-6525055,-24242706, - -15940211,-6238521,14082855,10047669,12228189,}, - {-30495588,-12893761,-11161261,3539405,-11502464, - 16491580,-27286798,-15030530,-7272871,-15934455,},}, - {{17650926,582297,-860412,-187745,-12072900, - -10683391,-20352381,15557840,-31072141,-5019061,}, - {-6283632,-2259834,-4674247,-4598977,-4089240, - 12435688,-31278303,1060251,6256175,10480726,}, - {-13871026,2026300,-21928428,-2741605,-2406664, - -8034988,7355518,15733500,-23379862,7489131,},}, - {{6883359,695140,23196907,9644202,-33430614, - 11354760,-20134606,6388313,-8263585,-8491918,}, - {-7716174,-13605463,-13646110,14757414,-19430591, - -14967316,10359532,-11059670,-21935259,12082603,}, - {-11253345,-15943946,10046784,5414629,24840771, - 8086951,-6694742,9868723,15842692,-16224787,},}, - {{9639399,11810955,-24007778,-9320054,3912937, - -9856959,996125,-8727907,-8919186,-14097242,}, - {7248867,14468564,25228636,-8795035,14346339, - 8224790,6388427,-7181107,6468218,-8720783,}, - {15513115,15439095,7342322,-10157390,18005294, - -7265713,2186239,4884640,10826567,7135781,},}, - {{-14204238,5297536,-5862318,-6004934,28095835, - 4236101,-14203318,1958636,-16816875,3837147,}, - {-5511166,-13176782,-29588215,12339465,15325758, - -15945770,-8813185,11075932,-19608050,-3776283,}, - {11728032,9603156,-4637821,-5304487,-7827751, - 2724948,31236191,-16760175,-7268616,14799772,},}, - {{-28842672,4840636,-12047946,-9101456,-1445464, - 381905,-30977094,-16523389,1290540,12798615,}, - {27246947,-10320914,14792098,-14518944,5302070, - -8746152,-3403974,-4149637,-27061213,10749585,}, - {25572375,-6270368,-15353037,16037944,1146292, - 32198,23487090,9585613,24714571,-1418265,},}, - {{19844825,282124,-17583147,11004019,-32004269, - -2716035,6105106,-1711007,-21010044,14338445,}, - {8027505,8191102,-18504907,-12335737,25173494, - -5923905,15446145,7483684,-30440441,10009108,}, - {-14134701,-4174411,10246585,-14677495,33553567, - -14012935,23366126,15080531,-7969992,7663473,},}, -}; - -static const ge_precomp b_comb_high[8] = { - {{33055887,-4431773,-521787,6654165,951411, - -6266464,-5158124,6995613,-5397442,-6985227,}, - {4014062,6967095,-11977872,3960002,8001989, - 5130302,-2154812,-1899602,-31954493,-16173976,}, - {16271757,-9212948,23792794,731486,-25808309, - -3546396,6964344,-4767590,10976593,10050757,},}, - {{2533007,-4288439,-24467768,-12387405,-13450051, - 14542280,12876301,13893535,15067764,8594792,}, - {20073501,-11623621,3165391,-13119866,13188608, - -11540496,-10751437,-13482671,29588810,2197295,}, - {-1084082,11831693,6031797,14062724,14748428, - -8159962,-20721760,11742548,31368706,13161200,},}, - {{2050412,-6457589,15321215,5273360,25484180, - 124590,-18187548,-7097255,-6691621,-14604792,}, - {9938196,2162889,-6158074,-1711248,4278932, - -2598531,-22865792,-7168500,-24323168,11746309,}, - {-22691768,-14268164,5965485,9383325,20443693, - 5854192,28250679,-1381811,-10837134,13717818,},}, - {{-8495530,16382250,9548884,-4971523,-4491811, - -3902147,6182256,-12832479,26628081,10395408,}, - {27329048,-15853735,7715764,8717446,-9215518, - -14633480,28982250,-5668414,4227628,242148,}, - {-13279943,-7986904,-7100016,8764468,-27276630, - 3096719,29678419,-9141299,3906709,11265498,},}, - {{11918285,15686328,-17757323,-11217300,-27548967, - 4853165,-27168827,6807359,6871949,-1075745,}, - {-29002610,13984323,-27111812,-2713442,28107359, - -13266203,6155126,15104658,3538727,-7513788,}, - {14103158,11233913,-33165269,9279850,31014152, - 4335090,-1827936,4590951,13960841,12787712,},}, - {{1469134,-16738009,33411928,13942824,8092558, - -8778224,-11165065,1437842,22521552,-2792954,}, - {31352705,-4807352,-25327300,3962447,12541566, - -9399651,-27425693,7964818,-23829869,5541287,}, - {-25732021,-6864887,23848984,3039395,-9147354, - 6022816,-27421653,10590137,25309915,-1584678,},}, - {{-22951376,5048948,31139401,-190316,-19542447, - -626310,-17486305,-16511925,-18851313,-12985140,}, - {-9684890,14681754,30487568,7717771,-10829709, - 9630497,30290549,-10531496,-27798994,-13812825,}, - {5827835,16097107,-24501327,12094619,7413972, - 11447087,28057551,-1793987,-14056981,4359312,},}, - {{26323183,2342588,-21887793,-1623758,-6062284, - 2107090,-28724907,9036464,-19618351,-13055189,}, - {-29697200,14829398,-4596333,14220089,-30022969, - 2955645,12094100,-13693652,-5941445,7047569,}, - {-3201977,14413268,-12058324,-16417589,-9035655, - -7224648,9258160,1399236,30397584,-5684634,},}, -}; - -static void lookup_add(ge *p, ge_precomp *tmp_c, fe tmp_a, fe tmp_b, - const ge_precomp comb[8], const u8 scalar[32], int i) -{ - u8 teeth = (u8)((scalar_bit(scalar, i) ) + - (scalar_bit(scalar, i + 32) << 1) + - (scalar_bit(scalar, i + 64) << 2) + - (scalar_bit(scalar, i + 96) << 3)); - u8 high = teeth >> 3; - u8 index = (teeth ^ (high - 1)) & 7; - FOR (j, 0, 8) { - i32 select = 1 & (((j ^ index) - 1) >> 8); - fe_ccopy(tmp_c->Yp, comb[j].Yp, select); - fe_ccopy(tmp_c->Ym, comb[j].Ym, select); - fe_ccopy(tmp_c->T2, comb[j].T2, select); - } - fe_neg(tmp_a, tmp_c->T2); - fe_cswap(tmp_c->T2, tmp_a , high ^ 1); - fe_cswap(tmp_c->Yp, tmp_c->Ym, high ^ 1); - ge_madd(p, p, tmp_c, tmp_a, tmp_b); -} - -// p = [scalar]B, where B is the base point -static void ge_scalarmult_base(ge *p, const u8 scalar[32]) -{ - // twin 4-bits signed combs, from Mike Hamburg's - // Fast and compact elliptic-curve cryptography (2012) - // 1 / 2 modulo L - static const u8 half_mod_L[32] = { - 247,233,122,46,141,49,9,44,107,206,123,81,239,124,111,10, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8, - }; - // (2^256 - 1) / 2 modulo L - static const u8 half_ones[32] = { - 142,74,204,70,186,24,118,107,184,231,190,57,250,173,119,99, - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,7, - }; - - // All bits set form: 1 means 1, 0 means -1 - u8 s_scalar[32]; - crypto_eddsa_mul_add(s_scalar, scalar, half_mod_L, half_ones); - - // Double and add ladder - fe tmp_a, tmp_b; // temporaries for addition - ge_precomp tmp_c; // temporary for comb lookup - ge tmp_d; // temporary for doubling - fe_1(tmp_c.Yp); - fe_1(tmp_c.Ym); - fe_0(tmp_c.T2); - - // Save a double on the first iteration - ge_zero(p); - lookup_add(p, &tmp_c, tmp_a, tmp_b, b_comb_low , s_scalar, 31); - lookup_add(p, &tmp_c, tmp_a, tmp_b, b_comb_high, s_scalar, 31+128); - // Regular double & add for the rest - for (int i = 30; i >= 0; i--) { - ge_double(p, p, &tmp_d); - lookup_add(p, &tmp_c, tmp_a, tmp_b, b_comb_low , s_scalar, i); - lookup_add(p, &tmp_c, tmp_a, tmp_b, b_comb_high, s_scalar, i+128); - } - // Note: we could save one addition at the end if we assumed the - // scalar fit in 252 bits. Which it does in practice if it is - // selected at random. However, non-random, non-hashed scalars - // *can* overflow 252 bits in practice. Better account for that - // than leaving that kind of subtle corner case. - - WIPE_BUFFER(tmp_a); WIPE_CTX(&tmp_d); - WIPE_BUFFER(tmp_b); WIPE_CTX(&tmp_c); - WIPE_BUFFER(s_scalar); -} - -void crypto_eddsa_scalarbase(u8 point[32], const u8 scalar[32]) -{ - ge P; - ge_scalarmult_base(&P, scalar); - ge_tobytes(point, &P); - WIPE_CTX(&P); -} - -void crypto_eddsa_key_pair(u8 secret_key[64], u8 public_key[32], u8 seed[32]) -{ - // To allow overlaps, observable writes happen in this order: - // 1. seed - // 2. secret_key - // 3. public_key - u8 a[64]; - COPY(a, seed, 32); - crypto_wipe(seed, 32); - COPY(secret_key, a, 32); - crypto_blake2b(a, 64, a, 32); - crypto_eddsa_trim_scalar(a, a); - crypto_eddsa_scalarbase(secret_key + 32, a); - COPY(public_key, secret_key + 32, 32); - WIPE_BUFFER(a); -} - -static void hash_reduce(u8 h[32], - const u8 *a, size_t a_size, - const u8 *b, size_t b_size, - const u8 *c, size_t c_size) -{ - u8 hash[64]; - crypto_blake2b_ctx ctx; - crypto_blake2b_init (&ctx, 64); - crypto_blake2b_update(&ctx, a, a_size); - crypto_blake2b_update(&ctx, b, b_size); - crypto_blake2b_update(&ctx, c, c_size); - crypto_blake2b_final (&ctx, hash); - crypto_eddsa_reduce(h, hash); -} - -// Digital signature of a message with from a secret key. -// -// The secret key comprises two parts: -// - The seed that generates the key (secret_key[ 0..31]) -// - The public key (secret_key[32..63]) -// -// The seed and the public key are bundled together to make sure users -// don't use mismatched seeds and public keys, which would instantly -// leak the secret scalar and allow forgeries (allowing this to happen -// has resulted in critical vulnerabilities in the wild). -// -// The seed is hashed to derive the secret scalar and a secret prefix. -// The sole purpose of the prefix is to generate a secret random nonce. -// The properties of that nonce must be as follows: -// - Unique: we need a different one for each message. -// - Secret: third parties must not be able to predict it. -// - Random: any detectable bias would break all security. -// -// There are two ways to achieve these properties. The obvious one is -// to simply generate a random number. Here that would be a parameter -// (Monocypher doesn't have an RNG). It works, but then users may reuse -// the nonce by accident, which _also_ leaks the secret scalar and -// allows forgeries. This has happened in the wild too. -// -// This is no good, so instead we generate that nonce deterministically -// by reducing modulo L a hash of the secret prefix and the message. -// The secret prefix makes the nonce unpredictable, the message makes it -// unique, and the hash/reduce removes all bias. -// -// The cost of that safety is hashing the message twice. If that cost -// is unacceptable, there are two alternatives: -// -// - Signing a hash of the message instead of the message itself. This -// is fine as long as the hash is collision resistant. It is not -// compatible with existing "pure" signatures, but at least it's safe. -// -// - Using a random nonce. Please exercise **EXTREME CAUTION** if you -// ever do that. It is absolutely **critical** that the nonce is -// really an unbiased random number between 0 and L-1, never reused, -// and wiped immediately. -// -// To lower the likelihood of complete catastrophe if the RNG is -// either flawed or misused, you can hash the RNG output together with -// the secret prefix and the beginning of the message, and use the -// reduction of that hash instead of the RNG output itself. It's not -// foolproof (you'd need to hash the whole message) but it helps. -// -// Signing a message involves the following operations: -// -// scalar, prefix = HASH(secret_key) -// r = HASH(prefix || message) % L -// R = [r]B -// h = HASH(R || public_key || message) % L -// S = ((h * a) + r) % L -// signature = R || S -void crypto_eddsa_sign(u8 signature [64], const u8 secret_key[64], - const u8 *message, size_t message_size) -{ - u8 a[64]; // secret scalar and prefix - u8 r[32]; // secret deterministic "random" nonce - u8 h[32]; // publically verifiable hash of the message (not wiped) - u8 R[32]; // first half of the signature (allows overlapping inputs) - - crypto_blake2b(a, 64, secret_key, 32); - crypto_eddsa_trim_scalar(a, a); - hash_reduce(r, a + 32, 32, message, message_size, 0, 0); - crypto_eddsa_scalarbase(R, r); - hash_reduce(h, R, 32, secret_key + 32, 32, message, message_size); - COPY(signature, R, 32); - crypto_eddsa_mul_add(signature + 32, h, a, r); - - WIPE_BUFFER(a); - WIPE_BUFFER(r); -} - -// To check the signature R, S of the message M with the public key A, -// there are 3 steps: -// -// compute h = HASH(R || A || message) % L -// check that A is on the curve. -// check that R == [s]B - [h]A -// -// The last two steps are done in crypto_eddsa_check_equation() -int crypto_eddsa_check(const u8 signature[64], const u8 public_key[32], - const u8 *message, size_t message_size) -{ - u8 h[32]; - hash_reduce(h, signature, 32, public_key, 32, message, message_size); - return crypto_eddsa_check_equation(signature, public_key, h); -} - -///////////////////////// -/// EdDSA <--> X25519 /// -///////////////////////// -void crypto_eddsa_to_x25519(u8 x25519[32], const u8 eddsa[32]) -{ - // (u, v) = ((1+y)/(1-y), sqrt(-486664)*u/x) - // Only converting y to u, the sign of x is ignored. - fe t1, t2; - fe_frombytes(t2, eddsa); - fe_add(t1, fe_one, t2); - fe_sub(t2, fe_one, t2); - fe_invert(t2, t2); - fe_mul(t1, t1, t2); - fe_tobytes(x25519, t1); - WIPE_BUFFER(t1); - WIPE_BUFFER(t2); -} - -void crypto_x25519_to_eddsa(u8 eddsa[32], const u8 x25519[32]) -{ - // (x, y) = (sqrt(-486664)*u/v, (u-1)/(u+1)) - // Only converting u to y, x is assumed positive. - fe t1, t2; - fe_frombytes(t2, x25519); - fe_sub(t1, t2, fe_one); - fe_add(t2, t2, fe_one); - fe_invert(t2, t2); - fe_mul(t1, t1, t2); - fe_tobytes(eddsa, t1); - WIPE_BUFFER(t1); - WIPE_BUFFER(t2); -} - -///////////////////////////////////////////// -/// Dirty ephemeral public key generation /// -///////////////////////////////////////////// - -// Those functions generates a public key, *without* clearing the -// cofactor. Sending that key over the network leaks 3 bits of the -// private key. Use only to generate ephemeral keys that will be hidden -// with crypto_curve_to_hidden(). -// -// The public key is otherwise compatible with crypto_x25519(), which -// properly clears the cofactor. -// -// Note that the distribution of the resulting public keys is almost -// uniform. Flipping the sign of the v coordinate (not provided by this -// function), covers the entire key space almost perfectly, where -// "almost" means a 2^-128 bias (undetectable). This uniformity is -// needed to ensure the proper randomness of the resulting -// representatives (once we apply crypto_curve_to_hidden()). -// -// Recall that Curve25519 has order C = 2^255 + e, with e < 2^128 (not -// to be confused with the prime order of the main subgroup, L, which is -// 8 times less than that). -// -// Generating all points would require us to multiply a point of order C -// (the base point plus any point of order 8) by all scalars from 0 to -// C-1. Clamping limits us to scalars between 2^254 and 2^255 - 1. But -// by negating the resulting point at random, we also cover scalars from -// -2^255 + 1 to -2^254 (which modulo C is congruent to e+1 to 2^254 + e). -// -// In practice: -// - Scalars from 0 to e + 1 are never generated -// - Scalars from 2^255 to 2^255 + e are never generated -// - Scalars from 2^254 + 1 to 2^254 + e are generated twice -// -// Since e < 2^128, detecting this bias requires observing over 2^100 -// representatives from a given source (this will never happen), *and* -// recovering enough of the private key to determine that they do, or do -// not, belong to the biased set (this practically requires solving -// discrete logarithm, which is conjecturally intractable). -// -// In practice, this means the bias is impossible to detect. - -// s + (x*L) % 8*L -// Guaranteed to fit in 256 bits iff s fits in 255 bits. -// L < 2^253 -// x%8 < 2^3 -// L * (x%8) < 2^255 -// s < 2^255 -// s + L * (x%8) < 2^256 -static void add_xl(u8 s[32], u8 x) -{ - u64 mod8 = x & 7; - u64 carry = 0; - FOR (i , 0, 8) { - carry = carry + load32_le(s + 4*i) + L[i] * mod8; - store32_le(s + 4*i, (u32)carry); - carry >>= 32; - } -} - -// "Small" dirty ephemeral key. -// Use if you need to shrink the size of the binary, and can afford to -// slow down by a factor of two (compared to the fast version) -// -// This version works by decoupling the cofactor from the main factor. -// -// - The trimmed scalar determines the main factor -// - The clamped bits of the scalar determine the cofactor. -// -// Cofactor and main factor are combined into a single scalar, which is -// then multiplied by a point of order 8*L (unlike the base point, which -// has prime order). That "dirty" base point is the addition of the -// regular base point (9), and a point of order 8. -void crypto_x25519_dirty_small(u8 public_key[32], const u8 secret_key[32]) -{ - // Base point of order 8*L - // Raw scalar multiplication with it does not clear the cofactor, - // and the resulting public key will reveal 3 bits of the scalar. - // - // The low order component of this base point has been chosen - // to yield the same results as crypto_x25519_dirty_fast(). - static const u8 dirty_base_point[32] = { - 0xd8, 0x86, 0x1a, 0xa2, 0x78, 0x7a, 0xd9, 0x26, - 0x8b, 0x74, 0x74, 0xb6, 0x82, 0xe3, 0xbe, 0xc3, - 0xce, 0x36, 0x9a, 0x1e, 0x5e, 0x31, 0x47, 0xa2, - 0x6d, 0x37, 0x7c, 0xfd, 0x20, 0xb5, 0xdf, 0x75, - }; - // separate the main factor & the cofactor of the scalar - u8 scalar[32]; - crypto_eddsa_trim_scalar(scalar, secret_key); - - // Separate the main factor and the cofactor - // - // The scalar is trimmed, so its cofactor is cleared. The three - // least significant bits however still have a main factor. We must - // remove it for X25519 compatibility. - // - // cofactor = lsb * L (modulo 8*L) - // combined = scalar + cofactor (modulo 8*L) - add_xl(scalar, secret_key[0]); - scalarmult(public_key, scalar, dirty_base_point, 256); - WIPE_BUFFER(scalar); -} - -// Select low order point -// We're computing the [cofactor]lop scalar multiplication, where: -// -// cofactor = tweak & 7. -// lop = (lop_x, lop_y) -// lop_x = sqrt((sqrt(d + 1) + 1) / d) -// lop_y = -lop_x * sqrtm1 -// -// The low order point has order 8. There are 4 such points. We've -// chosen the one whose both coordinates are positive (below p/2). -// The 8 low order points are as follows: -// -// [0]lop = ( 0 , 1 ) -// [1]lop = ( lop_x , lop_y) -// [2]lop = ( sqrt(-1), -0 ) -// [3]lop = ( lop_x , -lop_y) -// [4]lop = (-0 , -1 ) -// [5]lop = (-lop_x , -lop_y) -// [6]lop = (-sqrt(-1), 0 ) -// [7]lop = (-lop_x , lop_y) -// -// The x coordinate is either 0, sqrt(-1), lop_x, or their opposite. -// The y coordinate is either 0, -1 , lop_y, or their opposite. -// The pattern for both is the same, except for a rotation of 2 (modulo 8) -// -// This helper function captures the pattern, and we can use it thus: -// -// select_lop(x, lop_x, sqrtm1, cofactor); -// select_lop(y, lop_y, fe_one, cofactor + 2); -// -// This is faster than an actual scalar multiplication, -// and requires less code than naive constant time look up. -static void select_lop(fe out, const fe x, const fe k, u8 cofactor) -{ - fe tmp; - fe_0(out); - fe_ccopy(out, k , (cofactor >> 1) & 1); // bit 1 - fe_ccopy(out, x , (cofactor >> 0) & 1); // bit 0 - fe_neg (tmp, out); - fe_ccopy(out, tmp, (cofactor >> 2) & 1); // bit 2 - WIPE_BUFFER(tmp); -} - -// "Fast" dirty ephemeral key -// We use this one by default. -// -// This version works by performing a regular scalar multiplication, -// then add a low order point. The scalar multiplication is done in -// Edwards space for more speed (*2 compared to the "small" version). -// The cost is a bigger binary for programs that don't also sign messages. -void crypto_x25519_dirty_fast(u8 public_key[32], const u8 secret_key[32]) -{ - // Compute clean scalar multiplication - u8 scalar[32]; - ge pk; - crypto_eddsa_trim_scalar(scalar, secret_key); - ge_scalarmult_base(&pk, scalar); - - // Compute low order point - fe t1, t2; - select_lop(t1, lop_x, sqrtm1, secret_key[0]); - select_lop(t2, lop_y, fe_one, secret_key[0] + 2); - ge_precomp low_order_point; - fe_add(low_order_point.Yp, t2, t1); - fe_sub(low_order_point.Ym, t2, t1); - fe_mul(low_order_point.T2, t2, t1); - fe_mul(low_order_point.T2, low_order_point.T2, D2); - - // Add low order point to the public key - ge_madd(&pk, &pk, &low_order_point, t1, t2); - - // Convert to Montgomery u coordinate (we ignore the sign) - fe_add(t1, pk.Z, pk.Y); - fe_sub(t2, pk.Z, pk.Y); - fe_invert(t2, t2); - fe_mul(t1, t1, t2); - - fe_tobytes(public_key, t1); - - WIPE_BUFFER(t1); WIPE_CTX(&pk); - WIPE_BUFFER(t2); WIPE_CTX(&low_order_point); - WIPE_BUFFER(scalar); -} - -/////////////////// -/// Elligator 2 /// -/////////////////// -static const fe A = {486662}; - -// Elligator direct map -// -// Computes the point corresponding to a representative, encoded in 32 -// bytes (little Endian). Since positive representatives fits in 254 -// bits, The two most significant bits are ignored. -// -// From the paper: -// w = -A / (fe(1) + non_square * r^2) -// e = chi(w^3 + A*w^2 + w) -// u = e*w - (fe(1)-e)*(A//2) -// v = -e * sqrt(u^3 + A*u^2 + u) -// -// We ignore v because we don't need it for X25519 (the Montgomery -// ladder only uses u). -// -// Note that e is either 0, 1 or -1 -// if e = 0 u = 0 and v = 0 -// if e = 1 u = w -// if e = -1 u = -w - A = w * non_square * r^2 -// -// Let r1 = non_square * r^2 -// Let r2 = 1 + r1 -// Note that r2 cannot be zero, -1/non_square is not a square. -// We can (tediously) verify that: -// w^3 + A*w^2 + w = (A^2*r1 - r2^2) * A / r2^3 -// Therefore: -// chi(w^3 + A*w^2 + w) = chi((A^2*r1 - r2^2) * (A / r2^3)) -// chi(w^3 + A*w^2 + w) = chi((A^2*r1 - r2^2) * (A / r2^3)) * 1 -// chi(w^3 + A*w^2 + w) = chi((A^2*r1 - r2^2) * (A / r2^3)) * chi(r2^6) -// chi(w^3 + A*w^2 + w) = chi((A^2*r1 - r2^2) * (A / r2^3) * r2^6) -// chi(w^3 + A*w^2 + w) = chi((A^2*r1 - r2^2) * A * r2^3) -// Corollary: -// e = 1 if (A^2*r1 - r2^2) * A * r2^3) is a non-zero square -// e = -1 if (A^2*r1 - r2^2) * A * r2^3) is not a square -// Note that w^3 + A*w^2 + w (and therefore e) can never be zero: -// w^3 + A*w^2 + w = w * (w^2 + A*w + 1) -// w^3 + A*w^2 + w = w * (w^2 + A*w + A^2/4 - A^2/4 + 1) -// w^3 + A*w^2 + w = w * (w + A/2)^2 - A^2/4 + 1) -// which is zero only if: -// w = 0 (impossible) -// (w + A/2)^2 = A^2/4 - 1 (impossible, because A^2/4-1 is not a square) -// -// Let isr = invsqrt((A^2*r1 - r2^2) * A * r2^3) -// isr = sqrt(1 / ((A^2*r1 - r2^2) * A * r2^3)) if e = 1 -// isr = sqrt(sqrt(-1) / ((A^2*r1 - r2^2) * A * r2^3)) if e = -1 -// -// if e = 1 -// let u1 = -A * (A^2*r1 - r2^2) * A * r2^2 * isr^2 -// u1 = w -// u1 = u -// -// if e = -1 -// let ufactor = -non_square * sqrt(-1) * r^2 -// let vfactor = sqrt(ufactor) -// let u2 = -A * (A^2*r1 - r2^2) * A * r2^2 * isr^2 * ufactor -// u2 = w * -1 * -non_square * r^2 -// u2 = w * non_square * r^2 -// u2 = u -void crypto_elligator_map(u8 curve[32], const u8 hidden[32]) -{ - fe r, u, t1, t2, t3; - fe_frombytes_mask(r, hidden, 2); // r is encoded in 254 bits. - fe_sq(r, r); - fe_add(t1, r, r); - fe_add(u, t1, fe_one); - fe_sq (t2, u); - fe_mul(t3, A2, t1); - fe_sub(t3, t3, t2); - fe_mul(t3, t3, A); - fe_mul(t1, t2, u); - fe_mul(t1, t3, t1); - int is_square = invsqrt(t1, t1); - fe_mul(u, r, ufactor); - fe_ccopy(u, fe_one, is_square); - fe_sq (t1, t1); - fe_mul(u, u, A); - fe_mul(u, u, t3); - fe_mul(u, u, t2); - fe_mul(u, u, t1); - fe_neg(u, u); - fe_tobytes(curve, u); - - WIPE_BUFFER(t1); WIPE_BUFFER(r); - WIPE_BUFFER(t2); WIPE_BUFFER(u); - WIPE_BUFFER(t3); -} - -// Elligator inverse map -// -// Computes the representative of a point, if possible. If not, it does -// nothing and returns -1. Note that the success of the operation -// depends only on the point (more precisely its u coordinate). The -// tweak parameter is used only upon success -// -// The tweak should be a random byte. Beyond that, its contents are an -// implementation detail. Currently, the tweak comprises: -// - Bit 1 : sign of the v coordinate (0 if positive, 1 if negative) -// - Bit 2-5: not used -// - Bits 6-7: random padding -// -// From the paper: -// Let sq = -non_square * u * (u+A) -// if sq is not a square, or u = -A, there is no mapping -// Assuming there is a mapping: -// if v is positive: r = sqrt(-u / (non_square * (u+A))) -// if v is negative: r = sqrt(-(u+A) / (non_square * u )) -// -// We compute isr = invsqrt(-non_square * u * (u+A)) -// if it wasn't a square, abort. -// else, isr = sqrt(-1 / (non_square * u * (u+A)) -// -// If v is positive, we return isr * u: -// isr * u = sqrt(-1 / (non_square * u * (u+A)) * u -// isr * u = sqrt(-u / (non_square * (u+A)) -// -// If v is negative, we return isr * (u+A): -// isr * (u+A) = sqrt(-1 / (non_square * u * (u+A)) * (u+A) -// isr * (u+A) = sqrt(-(u+A) / (non_square * u) -int crypto_elligator_rev(u8 hidden[32], const u8 public_key[32], u8 tweak) -{ - fe t1, t2, t3; - fe_frombytes(t1, public_key); // t1 = u - - fe_add(t2, t1, A); // t2 = u + A - fe_mul(t3, t1, t2); - fe_mul_small(t3, t3, -2); - int is_square = invsqrt(t3, t3); // t3 = sqrt(-1 / non_square * u * (u+A)) - if (is_square) { - // The only variable time bit. This ultimately reveals how many - // tries it took us to find a representable key. - // This does not affect security as long as we try keys at random. - - fe_ccopy (t1, t2, tweak & 1); // multiply by u if v is positive, - fe_mul (t3, t1, t3); // multiply by u+A otherwise - fe_mul_small(t1, t3, 2); - fe_neg (t2, t3); - fe_ccopy (t3, t2, fe_isodd(t1)); - fe_tobytes(hidden, t3); - - // Pad with two random bits - hidden[31] |= tweak & 0xc0; - } - - WIPE_BUFFER(t1); - WIPE_BUFFER(t2); - WIPE_BUFFER(t3); - return is_square - 1; -} - -void crypto_elligator_key_pair(u8 hidden[32], u8 secret_key[32], u8 seed[32]) -{ - u8 pk [32]; // public key - u8 buf[64]; // seed + representative - COPY(buf + 32, seed, 32); - do { - crypto_chacha20_djb(buf, 0, 64, buf+32, zero, 0); - crypto_x25519_dirty_fast(pk, buf); // or the "small" version - } while(crypto_elligator_rev(buf+32, pk, buf[32])); - // Note that the return value of crypto_elligator_rev() is - // independent from its tweak parameter. - // Therefore, buf[32] is not actually reused. Either we loop one - // more time and buf[32] is used for the new seed, or we succeeded, - // and buf[32] becomes the tweak parameter. - - crypto_wipe(seed, 32); - COPY(hidden , buf + 32, 32); - COPY(secret_key, buf , 32); - WIPE_BUFFER(buf); - WIPE_BUFFER(pk); -} - -/////////////////////// -/// Scalar division /// -/////////////////////// - -// Montgomery reduction. -// Divides x by (2^256), and reduces the result modulo L -// -// Precondition: -// x < L * 2^256 -// Constants: -// r = 2^256 (makes division by r trivial) -// k = (r * (1/r) - 1) // L (1/r is computed modulo L ) -// Algorithm: -// s = (x * k) % r -// t = x + s*L (t is always a multiple of r) -// u = (t/r) % L (u is always below 2*L, conditional subtraction is enough) -static void redc(u32 u[8], u32 x[16]) -{ - static const u32 k[8] = { - 0x12547e1b, 0xd2b51da3, 0xfdba84ff, 0xb1a206f2, - 0xffa36bea, 0x14e75438, 0x6fe91836, 0x9db6c6f2, - }; - - // s = x * k (modulo 2^256) - // This is cheaper than the full multiplication. - u32 s[8] = {0}; - FOR (i, 0, 8) { - u64 carry = 0; - FOR (j, 0, 8-i) { - carry += s[i+j] + (u64)x[i] * k[j]; - s[i+j] = (u32)carry; - carry >>= 32; - } - } - u32 t[16] = {0}; - multiply(t, s, L); - - // t = t + x - u64 carry = 0; - FOR (i, 0, 16) { - carry += (u64)t[i] + x[i]; - t[i] = (u32)carry; - carry >>= 32; - } - - // u = (t / 2^256) % L - // Note that t / 2^256 is always below 2*L, - // So a constant time conditional subtraction is enough - remove_l(u, t+8); - - WIPE_BUFFER(s); - WIPE_BUFFER(t); -} - -void crypto_x25519_inverse(u8 blind_salt [32], const u8 private_key[32], - const u8 curve_point[32]) -{ - static const u8 Lm2[32] = { // L - 2 - 0xeb, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, - 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, - }; - // 1 in Montgomery form - u32 m_inv [8] = { - 0x8d98951d, 0xd6ec3174, 0x737dcf70, 0xc6ef5bf4, - 0xfffffffe, 0xffffffff, 0xffffffff, 0x0fffffff, - }; - - u8 scalar[32]; - crypto_eddsa_trim_scalar(scalar, private_key); - - // Convert the scalar in Montgomery form - // m_scl = scalar * 2^256 (modulo L) - u32 m_scl[8]; - { - u32 tmp[16]; - ZERO(tmp, 8); - load32_le_buf(tmp+8, scalar, 8); - mod_l(scalar, tmp); - load32_le_buf(m_scl, scalar, 8); - WIPE_BUFFER(tmp); // Wipe ASAP to save stack space - } - - // Compute the inverse - u32 product[16]; - for (int i = 252; i >= 0; i--) { - ZERO(product, 16); - multiply(product, m_inv, m_inv); - redc(m_inv, product); - if (scalar_bit(Lm2, i)) { - ZERO(product, 16); - multiply(product, m_inv, m_scl); - redc(m_inv, product); - } - } - // Convert the inverse *out* of Montgomery form - // scalar = m_inv / 2^256 (modulo L) - COPY(product, m_inv, 8); - ZERO(product + 8, 8); - redc(m_inv, product); - store32_le_buf(scalar, m_inv, 8); // the *inverse* of the scalar - - // Clear the cofactor of scalar: - // cleared = scalar * (3*L + 1) (modulo 8*L) - // cleared = scalar + scalar * 3 * L (modulo 8*L) - // Note that (scalar * 3) is reduced modulo 8, so we only need the - // first byte. - add_xl(scalar, scalar[0] * 3); - - // Recall that 8*L < 2^256. However it is also very close to - // 2^255. If we spanned the ladder over 255 bits, random tests - // wouldn't catch the off-by-one error. - scalarmult(blind_salt, scalar, curve_point, 256); - - WIPE_BUFFER(scalar); WIPE_BUFFER(m_scl); - WIPE_BUFFER(product); WIPE_BUFFER(m_inv); -} - -//////////////////////////////// -/// Authenticated encryption /// -//////////////////////////////// -static void lock_auth(u8 mac[16], const u8 auth_key[32], - const u8 *ad , size_t ad_size, - const u8 *cipher_text, size_t text_size) -{ - u8 sizes[16]; // Not secret, not wiped - store64_le(sizes + 0, ad_size); - store64_le(sizes + 8, text_size); - crypto_poly1305_ctx poly_ctx; // auto wiped... - crypto_poly1305_init (&poly_ctx, auth_key); - crypto_poly1305_update(&poly_ctx, ad , ad_size); - crypto_poly1305_update(&poly_ctx, zero , align(ad_size, 16)); - crypto_poly1305_update(&poly_ctx, cipher_text, text_size); - crypto_poly1305_update(&poly_ctx, zero , align(text_size, 16)); - crypto_poly1305_update(&poly_ctx, sizes , 16); - crypto_poly1305_final (&poly_ctx, mac); // ...here -} - -void crypto_aead_init_x(crypto_aead_ctx *ctx, - u8 const key[32], const u8 nonce[24]) -{ - crypto_chacha20_h(ctx->key, key, nonce); - COPY(ctx->nonce, nonce + 16, 8); - ctx->counter = 0; -} - -void crypto_aead_init_djb(crypto_aead_ctx *ctx, - const u8 key[32], const u8 nonce[8]) -{ - COPY(ctx->key , key , 32); - COPY(ctx->nonce, nonce, 8); - ctx->counter = 0; -} - -void crypto_aead_init_ietf(crypto_aead_ctx *ctx, - const u8 key[32], const u8 nonce[12]) -{ - COPY(ctx->key , key , 32); - COPY(ctx->nonce, nonce + 4, 8); - ctx->counter = (u64)load32_le(nonce) << 32; -} - -void crypto_aead_write(crypto_aead_ctx *ctx, u8 *cipher_text, u8 mac[16], - const u8 *ad, size_t ad_size, - const u8 *plain_text, size_t text_size) -{ - u8 auth_key[64]; // the last 32 bytes are used for rekeying. - crypto_chacha20_djb(auth_key, 0, 64, ctx->key, ctx->nonce, ctx->counter); - crypto_chacha20_djb(cipher_text, plain_text, text_size, - ctx->key, ctx->nonce, ctx->counter + 1); - lock_auth(mac, auth_key, ad, ad_size, cipher_text, text_size); - COPY(ctx->key, auth_key + 32, 32); - WIPE_BUFFER(auth_key); -} - -int crypto_aead_read(crypto_aead_ctx *ctx, u8 *plain_text, const u8 mac[16], - const u8 *ad, size_t ad_size, - const u8 *cipher_text, size_t text_size) -{ - u8 auth_key[64]; // the last 32 bytes are used for rekeying. - u8 real_mac[16]; - crypto_chacha20_djb(auth_key, 0, 64, ctx->key, ctx->nonce, ctx->counter); - lock_auth(real_mac, auth_key, ad, ad_size, cipher_text, text_size); - int mismatch = crypto_verify16(mac, real_mac); - if (!mismatch) { - crypto_chacha20_djb(plain_text, cipher_text, text_size, - ctx->key, ctx->nonce, ctx->counter + 1); - COPY(ctx->key, auth_key + 32, 32); - } - WIPE_BUFFER(auth_key); - WIPE_BUFFER(real_mac); - return mismatch; -} - -void crypto_aead_lock(u8 *cipher_text, u8 mac[16], const u8 key[32], - const u8 nonce[24], const u8 *ad, size_t ad_size, - const u8 *plain_text, size_t text_size) -{ - crypto_aead_ctx ctx; - crypto_aead_init_x(&ctx, key, nonce); - crypto_aead_write(&ctx, cipher_text, mac, ad, ad_size, - plain_text, text_size); - crypto_wipe(&ctx, sizeof(ctx)); -} - -int crypto_aead_unlock(u8 *plain_text, const u8 mac[16], const u8 key[32], - const u8 nonce[24], const u8 *ad, size_t ad_size, - const u8 *cipher_text, size_t text_size) -{ - crypto_aead_ctx ctx; - crypto_aead_init_x(&ctx, key, nonce); - int mismatch = crypto_aead_read(&ctx, plain_text, mac, ad, ad_size, - cipher_text, text_size); - crypto_wipe(&ctx, sizeof(ctx)); - return mismatch; -} - -#ifdef MONOCYPHER_CPP_NAMESPACE -} -#endif diff --git a/rres/external/monocypher.h b/rres/external/monocypher.h deleted file mode 100644 index 8f466e3..0000000 --- a/rres/external/monocypher.h +++ /dev/null @@ -1,321 +0,0 @@ -// Monocypher version 4.0.1 -// -// This file is dual-licensed. Choose whichever licence you want from -// the two licences listed below. -// -// The first licence is a regular 2-clause BSD licence. The second licence -// is the CC-0 from Creative Commons. It is intended to release Monocypher -// to the public domain. The BSD licence serves as a fallback option. -// -// SPDX-License-Identifier: BSD-2-Clause OR CC0-1.0 -// -// ------------------------------------------------------------------------ -// -// Copyright (c) 2017-2019, Loup Vaillant -// All rights reserved. -// -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the -// distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// ------------------------------------------------------------------------ -// -// Written in 2017-2019 by Loup Vaillant -// -// To the extent possible under law, the author(s) have dedicated all copyright -// and related neighboring rights to this software to the public domain -// worldwide. This software is distributed without any warranty. -// -// You should have received a copy of the CC0 Public Domain Dedication along -// with this software. If not, see -// - -#ifndef MONOCYPHER_H -#define MONOCYPHER_H - -#include -#include - -#ifdef MONOCYPHER_CPP_NAMESPACE -namespace MONOCYPHER_CPP_NAMESPACE { -#elif defined(__cplusplus) -extern "C" { -#endif - -// Constant time comparisons -// ------------------------- - -// Return 0 if a and b are equal, -1 otherwise -int crypto_verify16(const uint8_t a[16], const uint8_t b[16]); -int crypto_verify32(const uint8_t a[32], const uint8_t b[32]); -int crypto_verify64(const uint8_t a[64], const uint8_t b[64]); - - -// Erase sensitive data -// -------------------- -void crypto_wipe(void *secret, size_t size); - - -// Authenticated encryption -// ------------------------ -void crypto_aead_lock(uint8_t *cipher_text, - uint8_t mac [16], - const uint8_t key [32], - const uint8_t nonce[24], - const uint8_t *ad, size_t ad_size, - const uint8_t *plain_text, size_t text_size); -int crypto_aead_unlock(uint8_t *plain_text, - const uint8_t mac [16], - const uint8_t key [32], - const uint8_t nonce[24], - const uint8_t *ad, size_t ad_size, - const uint8_t *cipher_text, size_t text_size); - -// Authenticated stream -// -------------------- -typedef struct { - uint64_t counter; - uint8_t key[32]; - uint8_t nonce[8]; -} crypto_aead_ctx; - -void crypto_aead_init_x(crypto_aead_ctx *ctx, - const uint8_t key[32], const uint8_t nonce[24]); -void crypto_aead_init_djb(crypto_aead_ctx *ctx, - const uint8_t key[32], const uint8_t nonce[8]); -void crypto_aead_init_ietf(crypto_aead_ctx *ctx, - const uint8_t key[32], const uint8_t nonce[12]); - -void crypto_aead_write(crypto_aead_ctx *ctx, - uint8_t *cipher_text, - uint8_t mac[16], - const uint8_t *ad , size_t ad_size, - const uint8_t *plain_text, size_t text_size); -int crypto_aead_read(crypto_aead_ctx *ctx, - uint8_t *plain_text, - const uint8_t mac[16], - const uint8_t *ad , size_t ad_size, - const uint8_t *cipher_text, size_t text_size); - - -// General purpose hash (BLAKE2b) -// ------------------------------ - -// Direct interface -void crypto_blake2b(uint8_t *hash, size_t hash_size, - const uint8_t *message, size_t message_size); - -void crypto_blake2b_keyed(uint8_t *hash, size_t hash_size, - const uint8_t *key, size_t key_size, - const uint8_t *message, size_t message_size); - -// Incremental interface -typedef struct { - // Do not rely on the size or contents of this type, - // for they may change without notice. - uint64_t hash[8]; - uint64_t input_offset[2]; - uint64_t input[16]; - size_t input_idx; - size_t hash_size; -} crypto_blake2b_ctx; - -void crypto_blake2b_init(crypto_blake2b_ctx *ctx, size_t hash_size); -void crypto_blake2b_keyed_init(crypto_blake2b_ctx *ctx, size_t hash_size, - const uint8_t *key, size_t key_size); -void crypto_blake2b_update(crypto_blake2b_ctx *ctx, - const uint8_t *message, size_t message_size); -void crypto_blake2b_final(crypto_blake2b_ctx *ctx, uint8_t *hash); - - -// Password key derivation (Argon2) -// -------------------------------- -#define CRYPTO_ARGON2_D 0 -#define CRYPTO_ARGON2_I 1 -#define CRYPTO_ARGON2_ID 2 - -typedef struct { - uint32_t algorithm; // Argon2d, Argon2i, Argon2id - uint32_t nb_blocks; // memory hardness, >= 8 * nb_lanes - uint32_t nb_passes; // CPU hardness, >= 1 (>= 3 recommended for Argon2i) - uint32_t nb_lanes; // parallelism level (single threaded anyway) -} crypto_argon2_config; - -typedef struct { - const uint8_t *pass; - const uint8_t *salt; - uint32_t pass_size; - uint32_t salt_size; // 16 bytes recommended -} crypto_argon2_inputs; - -typedef struct { - const uint8_t *key; // may be NULL if no key - const uint8_t *ad; // may be NULL if no additional data - uint32_t key_size; // 0 if no key (32 bytes recommended otherwise) - uint32_t ad_size; // 0 if no additional data -} crypto_argon2_extras; - -extern const crypto_argon2_extras crypto_argon2_no_extras; - -void crypto_argon2(uint8_t *hash, uint32_t hash_size, void *work_area, - crypto_argon2_config config, - crypto_argon2_inputs inputs, - crypto_argon2_extras extras); - - -// Key exchange (X-25519) -// ---------------------- - -// Shared secrets are not quite random. -// Hash them to derive an actual shared key. -void crypto_x25519_public_key(uint8_t public_key[32], - const uint8_t secret_key[32]); -void crypto_x25519(uint8_t raw_shared_secret[32], - const uint8_t your_secret_key [32], - const uint8_t their_public_key [32]); - -// Conversion to EdDSA -void crypto_x25519_to_eddsa(uint8_t eddsa[32], const uint8_t x25519[32]); - -// scalar "division" -// Used for OPRF. Be aware that exponential blinding is less secure -// than Diffie-Hellman key exchange. -void crypto_x25519_inverse(uint8_t blind_salt [32], - const uint8_t private_key[32], - const uint8_t curve_point[32]); - -// "Dirty" versions of x25519_public_key(). -// Use with crypto_elligator_rev(). -// Leaks 3 bits of the private key. -void crypto_x25519_dirty_small(uint8_t pk[32], const uint8_t sk[32]); -void crypto_x25519_dirty_fast (uint8_t pk[32], const uint8_t sk[32]); - - -// Signatures -// ---------- - -// EdDSA with curve25519 + BLAKE2b -void crypto_eddsa_key_pair(uint8_t secret_key[64], - uint8_t public_key[32], - uint8_t seed[32]); -void crypto_eddsa_sign(uint8_t signature [64], - const uint8_t secret_key[64], - const uint8_t *message, size_t message_size); -int crypto_eddsa_check(const uint8_t signature [64], - const uint8_t public_key[32], - const uint8_t *message, size_t message_size); - -// Conversion to X25519 -void crypto_eddsa_to_x25519(uint8_t x25519[32], const uint8_t eddsa[32]); - -// EdDSA building blocks -void crypto_eddsa_trim_scalar(uint8_t out[32], const uint8_t in[32]); -void crypto_eddsa_reduce(uint8_t reduced[32], const uint8_t expanded[64]); -void crypto_eddsa_mul_add(uint8_t r[32], - const uint8_t a[32], - const uint8_t b[32], - const uint8_t c[32]); -void crypto_eddsa_scalarbase(uint8_t point[32], const uint8_t scalar[32]); -int crypto_eddsa_check_equation(const uint8_t signature[64], - const uint8_t public_key[32], - const uint8_t h_ram[32]); - - -// Chacha20 -// -------- - -// Specialised hash. -// Used to hash X25519 shared secrets. -void crypto_chacha20_h(uint8_t out[32], - const uint8_t key[32], - const uint8_t in [16]); - -// Unauthenticated stream cipher. -// Don't forget to add authentication. -uint64_t crypto_chacha20_djb(uint8_t *cipher_text, - const uint8_t *plain_text, - size_t text_size, - const uint8_t key[32], - const uint8_t nonce[8], - uint64_t ctr); -uint32_t crypto_chacha20_ietf(uint8_t *cipher_text, - const uint8_t *plain_text, - size_t text_size, - const uint8_t key[32], - const uint8_t nonce[12], - uint32_t ctr); -uint64_t crypto_chacha20_x(uint8_t *cipher_text, - const uint8_t *plain_text, - size_t text_size, - const uint8_t key[32], - const uint8_t nonce[24], - uint64_t ctr); - - -// Poly 1305 -// --------- - -// This is a *one time* authenticator. -// Disclosing the mac reveals the key. -// See crypto_lock() on how to use it properly. - -// Direct interface -void crypto_poly1305(uint8_t mac[16], - const uint8_t *message, size_t message_size, - const uint8_t key[32]); - -// Incremental interface -typedef struct { - // Do not rely on the size or contents of this type, - // for they may change without notice. - uint8_t c[16]; // chunk of the message - size_t c_idx; // How many bytes are there in the chunk. - uint32_t r [4]; // constant multiplier (from the secret key) - uint32_t pad[4]; // random number added at the end (from the secret key) - uint32_t h [5]; // accumulated hash -} crypto_poly1305_ctx; - -void crypto_poly1305_init (crypto_poly1305_ctx *ctx, const uint8_t key[32]); -void crypto_poly1305_update(crypto_poly1305_ctx *ctx, - const uint8_t *message, size_t message_size); -void crypto_poly1305_final (crypto_poly1305_ctx *ctx, uint8_t mac[16]); - - -// Elligator 2 -// ----------- - -// Elligator mappings proper -void crypto_elligator_map(uint8_t curve [32], const uint8_t hidden[32]); -int crypto_elligator_rev(uint8_t hidden[32], const uint8_t curve [32], - uint8_t tweak); - -// Easy to use key pair generation -void crypto_elligator_key_pair(uint8_t hidden[32], uint8_t secret_key[32], - uint8_t seed[32]); - -#ifdef __cplusplus -} -#endif - -#endif // MONOCYPHER_H diff --git a/rres/external/qoi.h b/rres/external/qoi.h deleted file mode 100644 index 988f9ed..0000000 --- a/rres/external/qoi.h +++ /dev/null @@ -1,671 +0,0 @@ -/* - -QOI - The "Quite OK Image" format for fast, lossless image compression - -Dominic Szablewski - https://phoboslab.org - - --- LICENSE: The MIT License(MIT) - -Copyright(c) 2021 Dominic Szablewski - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files(the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions : -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - --- About - -QOI encodes and decodes images in a lossless format. Compared to stb_image and -stb_image_write QOI offers 20x-50x faster encoding, 3x-4x faster decoding and -20% better compression. - - --- Synopsis - -// Define `QOI_IMPLEMENTATION` in *one* C/C++ file before including this -// library to create the implementation. - -#define QOI_IMPLEMENTATION -#include "qoi.h" - -// Encode and store an RGBA buffer to the file system. The qoi_desc describes -// the input pixel data. -qoi_write("image_new.qoi", rgba_pixels, &(qoi_desc){ - .width = 1920, - .height = 1080, - .channels = 4, - .colorspace = QOI_SRGB -}); - -// Load and decode a QOI image from the file system into a 32bbp RGBA buffer. -// The qoi_desc struct will be filled with the width, height, number of channels -// and colorspace read from the file header. -qoi_desc desc; -void *rgba_pixels = qoi_read("image.qoi", &desc, 4); - - - --- Documentation - -This library provides the following functions; -- qoi_read -- read and decode a QOI file -- qoi_decode -- decode the raw bytes of a QOI image from memory -- qoi_write -- encode and write a QOI file -- qoi_encode -- encode an rgba buffer into a QOI image in memory - -See the function declaration below for the signature and more information. - -If you don't want/need the qoi_read and qoi_write functions, you can define -QOI_NO_STDIO before including this library. - -This library uses malloc() and free(). To supply your own malloc implementation -you can define QOI_MALLOC and QOI_FREE before including this library. - -This library uses memset() to zero-initialize the index. To supply your own -implementation you can define QOI_ZEROARR before including this library. - - --- Data Format - -A QOI file has a 14 byte header, followed by any number of data "chunks" and an -8-byte end marker. - -struct qoi_header_t { - char magic[4]; // magic bytes "qoif" - uint32_t width; // image width in pixels (BE) - uint32_t height; // image height in pixels (BE) - uint8_t channels; // 3 = RGB, 4 = RGBA - uint8_t colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear -}; - -Images are encoded row by row, left to right, top to bottom. The decoder and -encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous pixel value. An -image is complete when all pixels specified by width * height have been covered. - -Pixels are encoded as - - a run of the previous pixel - - an index into an array of previously seen pixels - - a difference to the previous pixel value in r,g,b - - full r,g,b or r,g,b,a values - -The color channels are assumed to not be premultiplied with the alpha channel -("un-premultiplied alpha"). - -A running array[64] (zero-initialized) of previously seen pixel values is -maintained by the encoder and decoder. Each pixel that is seen by the encoder -and decoder is put into this array at the position formed by a hash function of -the color value. In the encoder, if the pixel value at the index matches the -current pixel, this index position is written to the stream as QOI_OP_INDEX. -The hash function for the index is: - - index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64 - -Each chunk starts with a 2- or 8-bit tag, followed by a number of data bits. The -bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. All -values encoded in these data bits have the most significant bit on the left. - -The 8-bit tags have precedence over the 2-bit tags. A decoder must check for the -presence of an 8-bit tag first. - -The byte stream's end is marked with 7 0x00 bytes followed a single 0x01 byte. - - -The possible chunks are: - - -.- QOI_OP_INDEX ----------. -| Byte[0] | -| 7 6 5 4 3 2 1 0 | -|-------+-----------------| -| 0 0 | index | -`-------------------------` -2-bit tag b00 -6-bit index into the color index array: 0..63 - -A valid encoder must not issue 2 or more consecutive QOI_OP_INDEX chunks to the -same index. QOI_OP_RUN should be used instead. - - -.- QOI_OP_DIFF -----------. -| Byte[0] | -| 7 6 5 4 3 2 1 0 | -|-------+-----+-----+-----| -| 0 1 | dr | dg | db | -`-------------------------` -2-bit tag b01 -2-bit red channel difference from the previous pixel between -2..1 -2-bit green channel difference from the previous pixel between -2..1 -2-bit blue channel difference from the previous pixel between -2..1 - -The difference to the current channel values are using a wraparound operation, -so "1 - 2" will result in 255, while "255 + 1" will result in 0. - -Values are stored as unsigned integers with a bias of 2. E.g. -2 is stored as -0 (b00). 1 is stored as 3 (b11). - -The alpha value remains unchanged from the previous pixel. - - -.- QOI_OP_LUMA -------------------------------------. -| Byte[0] | Byte[1] | -| 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | -|-------+-----------------+-------------+-----------| -| 1 0 | green diff | dr - dg | db - dg | -`---------------------------------------------------` -2-bit tag b10 -6-bit green channel difference from the previous pixel -32..31 -4-bit red channel difference minus green channel difference -8..7 -4-bit blue channel difference minus green channel difference -8..7 - -The green channel is used to indicate the general direction of change and is -encoded in 6 bits. The red and blue channels (dr and db) base their diffs off -of the green channel difference and are encoded in 4 bits. I.e.: - dr_dg = (cur_px.r - prev_px.r) - (cur_px.g - prev_px.g) - db_dg = (cur_px.b - prev_px.b) - (cur_px.g - prev_px.g) - -The difference to the current channel values are using a wraparound operation, -so "10 - 13" will result in 253, while "250 + 7" will result in 1. - -Values are stored as unsigned integers with a bias of 32 for the green channel -and a bias of 8 for the red and blue channel. - -The alpha value remains unchanged from the previous pixel. - - -.- QOI_OP_RUN ------------. -| Byte[0] | -| 7 6 5 4 3 2 1 0 | -|-------+-----------------| -| 1 1 | run | -`-------------------------` -2-bit tag b11 -6-bit run-length repeating the previous pixel: 1..62 - -The run-length is stored with a bias of -1. Note that the run-lengths 63 and 64 -(b111110 and b111111) are illegal as they are occupied by the QOI_OP_RGB and -QOI_OP_RGBA tags. - - -.- QOI_OP_RGB ------------------------------------------. -| Byte[0] | Byte[1] | Byte[2] | Byte[3] | -| 7 6 5 4 3 2 1 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | -|-------------------------+---------+---------+---------| -| 1 1 1 1 1 1 1 0 | red | green | blue | -`-------------------------------------------------------` -8-bit tag b11111110 -8-bit red channel value -8-bit green channel value -8-bit blue channel value - -The alpha value remains unchanged from the previous pixel. - - -.- QOI_OP_RGBA ---------------------------------------------------. -| Byte[0] | Byte[1] | Byte[2] | Byte[3] | Byte[4] | -| 7 6 5 4 3 2 1 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | -|-------------------------+---------+---------+---------+---------| -| 1 1 1 1 1 1 1 1 | red | green | blue | alpha | -`-----------------------------------------------------------------` -8-bit tag b11111111 -8-bit red channel value -8-bit green channel value -8-bit blue channel value -8-bit alpha channel value - -*/ - - -/* ----------------------------------------------------------------------------- -Header - Public functions */ - -#ifndef QOI_H -#define QOI_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* A pointer to a qoi_desc struct has to be supplied to all of qoi's functions. -It describes either the input format (for qoi_write and qoi_encode), or is -filled with the description read from the file header (for qoi_read and -qoi_decode). - -The colorspace in this qoi_desc is an enum where - 0 = sRGB, i.e. gamma scaled RGB channels and a linear alpha channel - 1 = all channels are linear -You may use the constants QOI_SRGB or QOI_LINEAR. The colorspace is purely -informative. It will be saved to the file header, but does not affect -how chunks are en-/decoded. */ - -#define QOI_SRGB 0 -#define QOI_LINEAR 1 - -typedef struct { - unsigned int width; - unsigned int height; - unsigned char channels; - unsigned char colorspace; -} qoi_desc; - -#ifndef QOI_NO_STDIO - -/* Encode raw RGB or RGBA pixels into a QOI image and write it to the file -system. The qoi_desc struct must be filled with the image width, height, -number of channels (3 = RGB, 4 = RGBA) and the colorspace. - -The function returns 0 on failure (invalid parameters, or fopen or malloc -failed) or the number of bytes written on success. */ - -int qoi_write(const char *filename, const void *data, const qoi_desc *desc); - - -/* Read and decode a QOI image from the file system. If channels is 0, the -number of channels from the file header is used. If channels is 3 or 4 the -output format will be forced into this number of channels. - -The function either returns NULL on failure (invalid data, or malloc or fopen -failed) or a pointer to the decoded pixels. On success, the qoi_desc struct -will be filled with the description from the file header. - -The returned pixel data should be free()d after use. */ - -void *qoi_read(const char *filename, qoi_desc *desc, int channels); - -#endif /* QOI_NO_STDIO */ - - -/* Encode raw RGB or RGBA pixels into a QOI image in memory. - -The function either returns NULL on failure (invalid parameters or malloc -failed) or a pointer to the encoded data on success. On success the out_len -is set to the size in bytes of the encoded data. - -The returned qoi data should be free()d after use. */ - -void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len); - - -/* Decode a QOI image from memory. - -The function either returns NULL on failure (invalid parameters or malloc -failed) or a pointer to the decoded pixels. On success, the qoi_desc struct -is filled with the description from the file header. - -The returned pixel data should be free()d after use. */ - -void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels); - - -#ifdef __cplusplus -} -#endif -#endif /* QOI_H */ - - -/* ----------------------------------------------------------------------------- -Implementation */ - -#ifdef QOI_IMPLEMENTATION -#include -#include - -#ifndef QOI_MALLOC - #define QOI_MALLOC(sz) malloc(sz) - #define QOI_FREE(p) free(p) -#endif -#ifndef QOI_ZEROARR - #define QOI_ZEROARR(a) memset((a),0,sizeof(a)) -#endif - -#define QOI_OP_INDEX 0x00 /* 00xxxxxx */ -#define QOI_OP_DIFF 0x40 /* 01xxxxxx */ -#define QOI_OP_LUMA 0x80 /* 10xxxxxx */ -#define QOI_OP_RUN 0xc0 /* 11xxxxxx */ -#define QOI_OP_RGB 0xfe /* 11111110 */ -#define QOI_OP_RGBA 0xff /* 11111111 */ - -#define QOI_MASK_2 0xc0 /* 11000000 */ - -#define QOI_COLOR_HASH(C) (C.rgba.r*3 + C.rgba.g*5 + C.rgba.b*7 + C.rgba.a*11) -#define QOI_MAGIC \ - (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \ - ((unsigned int)'i') << 8 | ((unsigned int)'f')) -#define QOI_HEADER_SIZE 14 - -/* 2GB is the max file size that this implementation can safely handle. We guard -against anything larger than that, assuming the worst case with 5 bytes per -pixel, rounded down to a nice clean value. 400 million pixels ought to be -enough for anybody. */ -#define QOI_PIXELS_MAX ((unsigned int)400000000) - -typedef union { - struct { unsigned char r, g, b, a; } rgba; - unsigned int v; -} qoi_rgba_t; - -static const unsigned char qoi_padding[8] = {0,0,0,0,0,0,0,1}; - -static void qoi_write_32(unsigned char *bytes, int *p, unsigned int v) { - bytes[(*p)++] = (0xff000000 & v) >> 24; - bytes[(*p)++] = (0x00ff0000 & v) >> 16; - bytes[(*p)++] = (0x0000ff00 & v) >> 8; - bytes[(*p)++] = (0x000000ff & v); -} - -static unsigned int qoi_read_32(const unsigned char *bytes, int *p) { - unsigned int a = bytes[(*p)++]; - unsigned int b = bytes[(*p)++]; - unsigned int c = bytes[(*p)++]; - unsigned int d = bytes[(*p)++]; - return a << 24 | b << 16 | c << 8 | d; -} - -void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { - int i, max_size, p, run; - int px_len, px_end, px_pos, channels; - unsigned char *bytes; - const unsigned char *pixels; - qoi_rgba_t index[64]; - qoi_rgba_t px, px_prev; - - if ( - data == NULL || out_len == NULL || desc == NULL || - desc->width == 0 || desc->height == 0 || - desc->channels < 3 || desc->channels > 4 || - desc->colorspace > 1 || - desc->height >= QOI_PIXELS_MAX / desc->width - ) { - return NULL; - } - - max_size = - desc->width * desc->height * (desc->channels + 1) + - QOI_HEADER_SIZE + sizeof(qoi_padding); - - p = 0; - bytes = (unsigned char *) QOI_MALLOC(max_size); - if (!bytes) { - return NULL; - } - - qoi_write_32(bytes, &p, QOI_MAGIC); - qoi_write_32(bytes, &p, desc->width); - qoi_write_32(bytes, &p, desc->height); - bytes[p++] = desc->channels; - bytes[p++] = desc->colorspace; - - - pixels = (const unsigned char *)data; - - QOI_ZEROARR(index); - - run = 0; - px_prev.rgba.r = 0; - px_prev.rgba.g = 0; - px_prev.rgba.b = 0; - px_prev.rgba.a = 255; - px = px_prev; - - px_len = desc->width * desc->height * desc->channels; - px_end = px_len - desc->channels; - channels = desc->channels; - - for (px_pos = 0; px_pos < px_len; px_pos += channels) { - if (channels == 4) { - px = *(qoi_rgba_t *)(pixels + px_pos); - } - else { - px.rgba.r = pixels[px_pos + 0]; - px.rgba.g = pixels[px_pos + 1]; - px.rgba.b = pixels[px_pos + 2]; - } - - if (px.v == px_prev.v) { - run++; - if (run == 62 || px_pos == px_end) { - bytes[p++] = QOI_OP_RUN | (run - 1); - run = 0; - } - } - else { - int index_pos; - - if (run > 0) { - bytes[p++] = QOI_OP_RUN | (run - 1); - run = 0; - } - - index_pos = QOI_COLOR_HASH(px) % 64; - - if (index[index_pos].v == px.v) { - bytes[p++] = QOI_OP_INDEX | index_pos; - } - else { - index[index_pos] = px; - - if (px.rgba.a == px_prev.rgba.a) { - signed char vr = px.rgba.r - px_prev.rgba.r; - signed char vg = px.rgba.g - px_prev.rgba.g; - signed char vb = px.rgba.b - px_prev.rgba.b; - - signed char vg_r = vr - vg; - signed char vg_b = vb - vg; - - if ( - vr > -3 && vr < 2 && - vg > -3 && vg < 2 && - vb > -3 && vb < 2 - ) { - bytes[p++] = QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2); - } - else if ( - vg_r > -9 && vg_r < 8 && - vg > -33 && vg < 32 && - vg_b > -9 && vg_b < 8 - ) { - bytes[p++] = QOI_OP_LUMA | (vg + 32); - bytes[p++] = (vg_r + 8) << 4 | (vg_b + 8); - } - else { - bytes[p++] = QOI_OP_RGB; - bytes[p++] = px.rgba.r; - bytes[p++] = px.rgba.g; - bytes[p++] = px.rgba.b; - } - } - else { - bytes[p++] = QOI_OP_RGBA; - bytes[p++] = px.rgba.r; - bytes[p++] = px.rgba.g; - bytes[p++] = px.rgba.b; - bytes[p++] = px.rgba.a; - } - } - } - px_prev = px; - } - - for (i = 0; i < (int)sizeof(qoi_padding); i++) { - bytes[p++] = qoi_padding[i]; - } - - *out_len = p; - return bytes; -} - -void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { - const unsigned char *bytes; - unsigned int header_magic; - unsigned char *pixels; - qoi_rgba_t index[64]; - qoi_rgba_t px; - int px_len, chunks_len, px_pos; - int p = 0, run = 0; - - if ( - data == NULL || desc == NULL || - (channels != 0 && channels != 3 && channels != 4) || - size < QOI_HEADER_SIZE + (int)sizeof(qoi_padding) - ) { - return NULL; - } - - bytes = (const unsigned char *)data; - - header_magic = qoi_read_32(bytes, &p); - desc->width = qoi_read_32(bytes, &p); - desc->height = qoi_read_32(bytes, &p); - desc->channels = bytes[p++]; - desc->colorspace = bytes[p++]; - - if ( - desc->width == 0 || desc->height == 0 || - desc->channels < 3 || desc->channels > 4 || - desc->colorspace > 1 || - header_magic != QOI_MAGIC || - desc->height >= QOI_PIXELS_MAX / desc->width - ) { - return NULL; - } - - if (channels == 0) { - channels = desc->channels; - } - - px_len = desc->width * desc->height * channels; - pixels = (unsigned char *) QOI_MALLOC(px_len); - if (!pixels) { - return NULL; - } - - QOI_ZEROARR(index); - px.rgba.r = 0; - px.rgba.g = 0; - px.rgba.b = 0; - px.rgba.a = 255; - - chunks_len = size - (int)sizeof(qoi_padding); - for (px_pos = 0; px_pos < px_len; px_pos += channels) { - if (run > 0) { - run--; - } - else if (p < chunks_len) { - int b1 = bytes[p++]; - - if (b1 == QOI_OP_RGB) { - px.rgba.r = bytes[p++]; - px.rgba.g = bytes[p++]; - px.rgba.b = bytes[p++]; - } - else if (b1 == QOI_OP_RGBA) { - px.rgba.r = bytes[p++]; - px.rgba.g = bytes[p++]; - px.rgba.b = bytes[p++]; - px.rgba.a = bytes[p++]; - } - else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) { - px = index[b1]; - } - else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF) { - px.rgba.r += ((b1 >> 4) & 0x03) - 2; - px.rgba.g += ((b1 >> 2) & 0x03) - 2; - px.rgba.b += ( b1 & 0x03) - 2; - } - else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA) { - int b2 = bytes[p++]; - int vg = (b1 & 0x3f) - 32; - px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f); - px.rgba.g += vg; - px.rgba.b += vg - 8 + (b2 & 0x0f); - } - else if ((b1 & QOI_MASK_2) == QOI_OP_RUN) { - run = (b1 & 0x3f); - } - - index[QOI_COLOR_HASH(px) % 64] = px; - } - - if (channels == 4) { - *(qoi_rgba_t*)(pixels + px_pos) = px; - } - else { - pixels[px_pos + 0] = px.rgba.r; - pixels[px_pos + 1] = px.rgba.g; - pixels[px_pos + 2] = px.rgba.b; - } - } - - return pixels; -} - -#ifndef QOI_NO_STDIO -#include - -int qoi_write(const char *filename, const void *data, const qoi_desc *desc) { - FILE *f = fopen(filename, "wb"); - int size; - void *encoded; - - if (!f) { - return 0; - } - - encoded = qoi_encode(data, desc, &size); - if (!encoded) { - fclose(f); - return 0; - } - - fwrite(encoded, 1, size, f); - fclose(f); - - QOI_FREE(encoded); - return size; -} - -void *qoi_read(const char *filename, qoi_desc *desc, int channels) { - FILE *f = fopen(filename, "rb"); - int size, bytes_read; - void *pixels, *data; - - if (!f) { - return NULL; - } - - fseek(f, 0, SEEK_END); - size = ftell(f); - if (size <= 0) { - fclose(f); - return NULL; - } - fseek(f, 0, SEEK_SET); - - data = QOI_MALLOC(size); - if (!data) { - fclose(f); - return NULL; - } - - bytes_read = fread(data, 1, size, f); - fclose(f); - - pixels = qoi_decode(data, bytes_read, desc, channels); - QOI_FREE(data); - return pixels; -} - -#endif /* QOI_NO_STDIO */ -#endif /* QOI_IMPLEMENTATION */ diff --git a/rres/go.mod b/rres/go.mod deleted file mode 100644 index bcdb1b3..0000000 --- a/rres/go.mod +++ /dev/null @@ -1,11 +0,0 @@ -module github.com/gen2brain/raylib-go/rres - -go 1.21 - -require github.com/gen2brain/raylib-go/raylib v0.0.0-20241202103652-5d50abe7c65b - -require ( - github.com/ebitengine/purego v0.8.1 // indirect - golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect - golang.org/x/sys v0.27.0 // indirect -) diff --git a/rres/go.sum b/rres/go.sum deleted file mode 100644 index e740228..0000000 --- a/rres/go.sum +++ /dev/null @@ -1,8 +0,0 @@ -github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE= -github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= -github.com/gen2brain/raylib-go/raylib v0.0.0-20241202103652-5d50abe7c65b h1:wK8D9x3f+BX1xFGgjj399dYx2eskikDZHxlRaSSA19Q= -github.com/gen2brain/raylib-go/raylib v0.0.0-20241202103652-5d50abe7c65b/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/rres/raylib.h b/rres/raylib.h deleted file mode 100644 index 1c4c4a0..0000000 --- a/rres/raylib.h +++ /dev/null @@ -1,1662 +0,0 @@ -/********************************************************************************************** -* -* raylib v5.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 -* - Multiplatform: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly, -* MacOS, Haiku, Android, Raspberry Pi, DRM native, HTML5. -* - Written in plain C code (C99) in PascalCase/camelCase notation -* - Hardware accelerated with OpenGL (1.1, 2.1, 3.3, 4.3 or ES2 - choose at compile) -* - Unique OpenGL abstraction layer (usable as standalone module): [rlgl] -* - Multiple Fonts formats supported (TTF, XNA fonts, AngelCode fonts) -* - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC) -* - Full 3d support for 3d Shapes, Models, Billboards, Heightmaps and more! -* - Flexible Materials system, supporting classic maps and PBR maps -* - Animated 3D models supported (skeletal bones animation) (IQM) -* - Shaders support, including Model shaders and Postprocessing shaders -* - Powerful math module for Vector, Matrix and Quaternion operations: [raymath] -* - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD) -* - VR stereo rendering with configurable HMD device parameters -* - Bindings to multiple programming languages available! -* -* NOTES: -* - One default Font is loaded on InitWindow()->LoadFontDefault() [core, text] -* - One default Texture2D is loaded on rlglInit(), 1x1 white pixel R8G8B8A8 [rlgl] (OpenGL 3.3 or ES2) -* - One default Shader is loaded on rlglInit()->rlLoadShaderDefault() [rlgl] (OpenGL 3.3 or ES2) -* - One default RenderBatch is loaded on rlglInit()->rlLoadRenderBatch() [rlgl] (OpenGL 3.3 or ES2) -* -* DEPENDENCIES (included): -* [rcore] rglfw (Camilla Löwy - github.com/glfw/glfw) for window/context management and input (PLATFORM_DESKTOP) -* [rlgl] glad (David Herberth - github.com/Dav1dde/glad) for OpenGL 3.3 extensions loading (PLATFORM_DESKTOP) -* [raudio] miniaudio (David Reid - github.com/mackron/miniaudio) for audio device/context management -* -* OPTIONAL DEPENDENCIES (included): -* [rcore] msf_gif (Miles Fogle) for GIF recording -* [rcore] sinfl (Micha Mettke) for DEFLATE decompression algorithm -* [rcore] sdefl (Micha Mettke) for DEFLATE compression algorithm -* [rtextures] stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...) -* [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG) -* [rtextures] stb_image_resize (Sean Barret) for image resizing algorithms -* [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 -* [rmodels] tinyobj_loader_c (Syoyo Fujita) for models loading (OBJ, MTL) -* [rmodels] cgltf (Johannes Kuhlmann) for models loading (glTF) -* [rmodels] Model3D (bzt) for models loading (M3D, https://bztsrc.gitlab.io/model3d) -* [raudio] dr_wav (David Reid) for WAV audio file loading -* [raudio] dr_flac (David Reid) for FLAC audio file loading -* [raudio] dr_mp3 (David Reid) for MP3 audio file loading -* [raudio] stb_vorbis (Sean Barret) for OGG audio loading -* [raudio] jar_xm (Joshua Reisenauer) for XM audio module loading -* [raudio] jar_mod (Joshua Reisenauer) for MOD audio module loading -* -* -* LICENSE: zlib/libpng -* -* raylib is 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) 2013-2023 Ramon Santamaria (@raysan5) -* -* 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. -* -**********************************************************************************************/ - -#ifndef RAYLIB_H -#define RAYLIB_H - -#include // Required for: va_list - Only used by TraceLogCallback - -#define RAYLIB_VERSION_MAJOR 5 -#define RAYLIB_VERSION_MINOR 0 -#define RAYLIB_VERSION_PATCH 0 -#define RAYLIB_VERSION "5.0" - -// Function specifiers in case library is build/used as a shared library (Windows) -// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll -#if defined(_WIN32) - #if defined(BUILD_LIBTYPE_SHARED) - #if defined(__TINYC__) - #define __declspec(x) __attribute__((x)) - #endif - #define RLAPI __declspec(dllexport) // We are 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) - #endif -#endif - -#ifndef RLAPI - #define RLAPI // Functions defined as 'extern' by default (implicit specifiers) -#endif - -//---------------------------------------------------------------------------------- -// Some basic Defines -//---------------------------------------------------------------------------------- -#ifndef PI - #define PI 3.14159265358979323846f -#endif -#ifndef DEG2RAD - #define DEG2RAD (PI/180.0f) -#endif -#ifndef RAD2DEG - #define RAD2DEG (180.0f/PI) -#endif - -// Allow custom memory allocators -// NOTE: Require recompiling raylib sources -#ifndef RL_MALLOC - #define RL_MALLOC(sz) malloc(sz) -#endif -#ifndef RL_CALLOC - #define RL_CALLOC(n,sz) calloc(n,sz) -#endif -#ifndef RL_REALLOC - #define RL_REALLOC(ptr,sz) realloc(ptr,sz) -#endif -#ifndef RL_FREE - #define RL_FREE(ptr) free(ptr) -#endif - -// NOTE: MSVC C++ compiler does not support compound literals (C99 feature) -// Plain structures in C++ (without constructors) can be initialized with { } -// This is called aggregate initialization (C++11 feature) -#if defined(__cplusplus) - #define CLITERAL(type) type -#else - #define CLITERAL(type) (type) -#endif - -// Some compilers (mostly macos clang) default to C++98, -// where aggregate initialization can't be used -// So, give a more clear error stating how to fix this -#if !defined(_MSC_VER) && (defined(__cplusplus) && __cplusplus < 201103L) - #error "C++11 or later is required. Add -std=c++11" -#endif - -// NOTE: We 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 -#define RL_COLOR_TYPE -#define RL_RECTANGLE_TYPE -#define RL_VECTOR2_TYPE -#define RL_VECTOR3_TYPE -#define RL_VECTOR4_TYPE -#define RL_QUATERNION_TYPE -#define RL_MATRIX_TYPE - -// Some Basic Colors -// NOTE: Custom raylib color palette for amazing visuals on WHITE background -#define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 } // Light Gray -#define GRAY CLITERAL(Color){ 130, 130, 130, 255 } // Gray -#define DARKGRAY CLITERAL(Color){ 80, 80, 80, 255 } // Dark Gray -#define YELLOW CLITERAL(Color){ 253, 249, 0, 255 } // Yellow -#define GOLD CLITERAL(Color){ 255, 203, 0, 255 } // Gold -#define ORANGE CLITERAL(Color){ 255, 161, 0, 255 } // Orange -#define PINK CLITERAL(Color){ 255, 109, 194, 255 } // Pink -#define RED CLITERAL(Color){ 230, 41, 55, 255 } // Red -#define MAROON CLITERAL(Color){ 190, 33, 55, 255 } // Maroon -#define GREEN CLITERAL(Color){ 0, 228, 48, 255 } // Green -#define LIME CLITERAL(Color){ 0, 158, 47, 255 } // Lime -#define DARKGREEN CLITERAL(Color){ 0, 117, 44, 255 } // Dark Green -#define SKYBLUE CLITERAL(Color){ 102, 191, 255, 255 } // Sky Blue -#define BLUE CLITERAL(Color){ 0, 121, 241, 255 } // Blue -#define DARKBLUE CLITERAL(Color){ 0, 82, 172, 255 } // Dark Blue -#define PURPLE CLITERAL(Color){ 200, 122, 255, 255 } // Purple -#define VIOLET CLITERAL(Color){ 135, 60, 190, 255 } // Violet -#define DARKPURPLE CLITERAL(Color){ 112, 31, 126, 255 } // Dark Purple -#define BEIGE CLITERAL(Color){ 211, 176, 131, 255 } // Beige -#define BROWN CLITERAL(Color){ 127, 106, 79, 255 } // Brown -#define DARKBROWN CLITERAL(Color){ 76, 63, 47, 255 } // Dark Brown - -#define WHITE CLITERAL(Color){ 255, 255, 255, 255 } // White -#define BLACK CLITERAL(Color){ 0, 0, 0, 255 } // Black -#define BLANK CLITERAL(Color){ 0, 0, 0, 0 } // Blank (Transparent) -#define MAGENTA CLITERAL(Color){ 255, 0, 255, 255 } // Magenta -#define RAYWHITE CLITERAL(Color){ 245, 245, 245, 255 } // My own White (raylib logo) - -//---------------------------------------------------------------------------------- -// Structures Definition -//---------------------------------------------------------------------------------- -// Boolean type -#if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800) - #include -#elif !defined(__cplusplus) && !defined(bool) - typedef enum bool { false = 0, true = !false } bool; - #define RL_BOOL_TYPE -#endif - -// Vector2, 2 components -typedef struct Vector2 { - float x; // Vector x component - float y; // Vector y component -} Vector2; - -// Vector3, 3 components -typedef struct Vector3 { - float x; // Vector x component - float y; // Vector y component - float z; // Vector z component -} Vector3; - -// Vector4, 4 components -typedef struct Vector4 { - float x; // Vector x component - float y; // Vector y component - float z; // Vector z component - float w; // Vector w component -} Vector4; - -// Quaternion, 4 components (Vector4 alias) -typedef Vector4 Quaternion; - -// Matrix, 4x4 components, column major, OpenGL style, right-handed -typedef struct Matrix { - float m0, m4, m8, m12; // Matrix first row (4 components) - float m1, m5, m9, m13; // Matrix second row (4 components) - float m2, m6, m10, m14; // Matrix third row (4 components) - float m3, m7, m11, m15; // Matrix fourth row (4 components) -} Matrix; - -// Color, 4 components, R8G8B8A8 (32bit) -typedef struct Color { - unsigned char r; // Color red value - unsigned char g; // Color green value - unsigned char b; // Color blue value - unsigned char a; // Color alpha value -} Color; - -// Rectangle, 4 components -typedef struct Rectangle { - float x; // Rectangle top-left corner position x - float y; // Rectangle top-left corner position y - float width; // Rectangle width - float height; // Rectangle height -} Rectangle; - -// Image, pixel data stored in CPU memory (RAM) -typedef struct Image { - void *data; // Image raw data - int width; // Image base width - int height; // Image base height - int mipmaps; // Mipmap levels, 1 by default - int format; // Data format (PixelFormat type) -} Image; - -// Texture, tex data stored in GPU memory (VRAM) -typedef struct Texture { - unsigned int id; // OpenGL texture id - int width; // Texture base width - int height; // Texture base height - int mipmaps; // Mipmap levels, 1 by default - int format; // Data format (PixelFormat type) -} Texture; - -// Texture2D, same as Texture -typedef Texture Texture2D; - -// TextureCubemap, same as Texture -typedef Texture TextureCubemap; - -// RenderTexture, fbo for texture rendering -typedef struct RenderTexture { - unsigned int id; // OpenGL framebuffer object id - Texture texture; // Color buffer attachment texture - Texture depth; // Depth buffer attachment texture -} RenderTexture; - -// RenderTexture2D, same as RenderTexture -typedef RenderTexture RenderTexture2D; - -// NPatchInfo, n-patch layout info -typedef struct NPatchInfo { - Rectangle source; // Texture source rectangle - int left; // Left border offset - int top; // Top border offset - int right; // Right border offset - int bottom; // Bottom border offset - int layout; // Layout of the n-patch: 3x3, 1x3 or 3x1 -} NPatchInfo; - -// GlyphInfo, font characters glyphs info -typedef struct GlyphInfo { - int value; // Character value (Unicode) - int offsetX; // Character offset X when drawing - int offsetY; // Character offset Y when drawing - int advanceX; // Character advance position X - Image image; // Character image data -} GlyphInfo; - -// Font, font texture and GlyphInfo array data -typedef struct Font { - int baseSize; // Base size (default chars height) - int glyphCount; // Number of glyph characters - int glyphPadding; // Padding around the glyph characters - Texture2D texture; // Texture atlas containing the glyphs - Rectangle *recs; // Rectangles in texture for the glyphs - GlyphInfo *glyphs; // Glyphs info data -} Font; - -// Camera, defines position/orientation in 3d space -typedef struct Camera3D { - Vector3 position; // Camera position - Vector3 target; // Camera target it looks-at - Vector3 up; // Camera up vector (rotation over its axis) - float fovy; // Camera field-of-view aperture in Y (degrees) in perspective, used as near plane width in orthographic - int projection; // Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC -} Camera3D; - -typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D - -// Camera2D, defines position/orientation in 2d space -typedef struct Camera2D { - Vector2 offset; // Camera offset (displacement from target) - Vector2 target; // Camera target (rotation and zoom origin) - float rotation; // Camera rotation in degrees - float zoom; // Camera zoom (scaling), should be 1.0f by default -} Camera2D; - -// Mesh, vertex data and vao/vbo -typedef struct Mesh { - int vertexCount; // Number of vertices stored in arrays - int triangleCount; // Number of triangles stored (indexed or not) - - // Vertex attributes data - float *vertices; // Vertex position (XYZ - 3 components per vertex) (shader-location = 0) - float *texcoords; // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) - float *texcoords2; // Vertex texture second coordinates (UV - 2 components per vertex) (shader-location = 5) - float *normals; // Vertex normals (XYZ - 3 components per vertex) (shader-location = 2) - float *tangents; // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4) - 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 - 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) - float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) - - // OpenGL identifiers - unsigned int vaoId; // OpenGL Vertex Array Object id - unsigned int *vboId; // OpenGL Vertex Buffer Objects id (default vertex data) -} Mesh; - -// Shader -typedef struct Shader { - unsigned int id; // Shader program id - int *locs; // Shader locations array (RL_MAX_SHADER_LOCATIONS) -} Shader; - -// MaterialMap -typedef struct MaterialMap { - Texture2D texture; // Material map texture - Color color; // Material map color - float value; // Material map value -} MaterialMap; - -// Material, includes shader and maps -typedef struct Material { - Shader shader; // Material shader - MaterialMap *maps; // Material maps array (MAX_MATERIAL_MAPS) - float params[4]; // Material generic parameters (if required) -} Material; - -// Transform, vertex transformation data -typedef struct Transform { - Vector3 translation; // Translation - Quaternion rotation; // Rotation - Vector3 scale; // Scale -} Transform; - -// Bone, skeletal animation bone -typedef struct BoneInfo { - char name[32]; // Bone name - int parent; // Bone parent -} BoneInfo; - -// Model, meshes, materials and animation data -typedef struct Model { - Matrix transform; // Local transform matrix - - int meshCount; // Number of meshes - int materialCount; // Number of materials - Mesh *meshes; // Meshes array - Material *materials; // Materials array - int *meshMaterial; // Mesh material number - - // Animation data - int boneCount; // Number of bones - BoneInfo *bones; // Bones information (skeleton) - Transform *bindPose; // Bones base transformation (pose) -} Model; - -// ModelAnimation -typedef struct ModelAnimation { - int boneCount; // Number of bones - int frameCount; // Number of animation frames - BoneInfo *bones; // Bones information (skeleton) - Transform **framePoses; // Poses array by frame - char name[32]; // Animation name -} ModelAnimation; - -// Ray, ray for raycasting -typedef struct Ray { - Vector3 position; // Ray position (origin) - Vector3 direction; // Ray direction -} Ray; - -// RayCollision, ray hit information -typedef struct RayCollision { - bool hit; // Did the ray hit something? - float distance; // Distance to the nearest hit - Vector3 point; // Point of the nearest hit - Vector3 normal; // Surface normal of hit -} RayCollision; - -// BoundingBox -typedef struct BoundingBox { - Vector3 min; // Minimum vertex box-corner - Vector3 max; // Maximum vertex box-corner -} BoundingBox; - -// Wave, audio wave data -typedef struct Wave { - unsigned int frameCount; // Total number of frames (considering channels) - unsigned int sampleRate; // Frequency (samples per second) - unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) - unsigned int channels; // Number of channels (1-mono, 2-stereo, ...) - void *data; // Buffer data pointer -} Wave; - -// Opaque structs declaration -// NOTE: Actual structs are defined internally in raudio module -typedef struct rAudioBuffer rAudioBuffer; -typedef struct rAudioProcessor rAudioProcessor; - -// AudioStream, custom audio stream -typedef struct AudioStream { - rAudioBuffer *buffer; // Pointer to internal data used by the audio system - rAudioProcessor *processor; // Pointer to internal data processor, useful for audio effects - - unsigned int sampleRate; // Frequency (samples per second) - unsigned int sampleSize; // Bit depth (bits per sample): 8, 16, 32 (24 not supported) - unsigned int channels; // Number of channels (1-mono, 2-stereo, ...) -} AudioStream; - -// Sound -typedef struct Sound { - AudioStream stream; // Audio stream - unsigned int frameCount; // Total number of frames (considering channels) -} Sound; - -// Music, audio stream, anything longer than ~10 seconds should be streamed -typedef struct Music { - AudioStream stream; // Audio stream - unsigned int frameCount; // Total number of frames (considering channels) - bool looping; // Music looping enable - - int ctxType; // Type of music context (audio filetype) - void *ctxData; // Audio context data, depends on type -} Music; - -// VrDeviceInfo, Head-Mounted-Display device parameters -typedef struct VrDeviceInfo { - int hResolution; // Horizontal resolution in pixels - int vResolution; // Vertical resolution in pixels - float hScreenSize; // Horizontal size in meters - float vScreenSize; // Vertical size in meters - float vScreenCenter; // Screen center in meters - float eyeToScreenDistance; // Distance between eye and display in meters - float lensSeparationDistance; // Lens separation distance in meters - float interpupillaryDistance; // IPD (distance between pupils) in meters - float lensDistortionValues[4]; // Lens distortion constant parameters - float chromaAbCorrection[4]; // Chromatic aberration correction parameters -} VrDeviceInfo; - -// VrStereoConfig, VR stereo rendering configuration for simulator -typedef struct VrStereoConfig { - Matrix projection[2]; // VR projection matrices (per eye) - Matrix viewOffset[2]; // VR view offset matrices (per eye) - float leftLensCenter[2]; // VR left lens center - float rightLensCenter[2]; // VR right lens center - float leftScreenCenter[2]; // VR left screen center - float rightScreenCenter[2]; // VR right screen center - float scale[2]; // VR distortion scale - float scaleIn[2]; // VR distortion scale in -} VrStereoConfig; - -// File path list -typedef struct FilePathList { - unsigned int capacity; // Filepaths max entries - unsigned int count; // Filepaths entries count - char **paths; // Filepaths entries -} FilePathList; - -// Automation event -typedef struct AutomationEvent { - unsigned int frame; // Event frame - unsigned int type; // Event type (AutomationEventType) - int params[4]; // Event parameters (if required) -} AutomationEvent; - -// Automation event list -typedef struct AutomationEventList { - unsigned int capacity; // Events max entries (MAX_AUTOMATION_EVENTS) - unsigned int count; // Events entries count - AutomationEvent *events; // Events entries -} AutomationEventList; - -//---------------------------------------------------------------------------------- -// Enumerators Definition -//---------------------------------------------------------------------------------- -// System/Window config flags -// NOTE: Every bit registers one state (use it with bit masks) -// By default all flags are set to 0 -typedef enum { - FLAG_VSYNC_HINT = 0x00000040, // Set to try enabling V-Sync on GPU - FLAG_FULLSCREEN_MODE = 0x00000002, // Set to run program in fullscreen - FLAG_WINDOW_RESIZABLE = 0x00000004, // Set to allow resizable window - FLAG_WINDOW_UNDECORATED = 0x00000008, // Set to disable window decoration (frame and buttons) - FLAG_WINDOW_HIDDEN = 0x00000080, // Set to hide window - FLAG_WINDOW_MINIMIZED = 0x00000200, // Set to minimize window (iconify) - FLAG_WINDOW_MAXIMIZED = 0x00000400, // Set to maximize window (expanded to monitor) - FLAG_WINDOW_UNFOCUSED = 0x00000800, // Set to window non focused - FLAG_WINDOW_TOPMOST = 0x00001000, // Set to window always on top - FLAG_WINDOW_ALWAYS_RUN = 0x00000100, // Set to allow windows running while minimized - FLAG_WINDOW_TRANSPARENT = 0x00000010, // Set to allow transparent framebuffer - FLAG_WINDOW_HIGHDPI = 0x00002000, // Set to support HighDPI - FLAG_WINDOW_MOUSE_PASSTHROUGH = 0x00004000, // Set to support mouse passthrough, only supported when FLAG_WINDOW_UNDECORATED - FLAG_BORDERLESS_WINDOWED_MODE = 0x00008000, // Set to run program in borderless windowed mode - FLAG_MSAA_4X_HINT = 0x00000020, // Set to try enabling MSAA 4X - FLAG_INTERLACED_HINT = 0x00010000 // Set to try enabling interlaced video format (for V3D) -} ConfigFlags; - -// Trace log level -// NOTE: Organized by priority level -typedef enum { - LOG_ALL = 0, // Display all logs - LOG_TRACE, // Trace logging, intended for internal use only - LOG_DEBUG, // Debug logging, used for internal debugging, it should be disabled on release builds - LOG_INFO, // Info logging, used for program execution info - LOG_WARNING, // Warning logging, used on recoverable failures - LOG_ERROR, // Error logging, used on unrecoverable failures - LOG_FATAL, // Fatal logging, used to abort program: exit(EXIT_FAILURE) - LOG_NONE // Disable logging -} TraceLogLevel; - -// Keyboard keys (US keyboard layout) -// NOTE: Use GetKeyPressed() to allow redefining -// required keys for alternative layouts -typedef enum { - KEY_NULL = 0, // Key: NULL, used for no key pressed - // Alphanumeric keys - KEY_APOSTROPHE = 39, // Key: ' - KEY_COMMA = 44, // Key: , - KEY_MINUS = 45, // Key: - - KEY_PERIOD = 46, // Key: . - KEY_SLASH = 47, // Key: / - KEY_ZERO = 48, // Key: 0 - KEY_ONE = 49, // Key: 1 - KEY_TWO = 50, // Key: 2 - KEY_THREE = 51, // Key: 3 - KEY_FOUR = 52, // Key: 4 - KEY_FIVE = 53, // Key: 5 - KEY_SIX = 54, // Key: 6 - KEY_SEVEN = 55, // Key: 7 - KEY_EIGHT = 56, // Key: 8 - KEY_NINE = 57, // Key: 9 - KEY_SEMICOLON = 59, // Key: ; - KEY_EQUAL = 61, // Key: = - KEY_A = 65, // Key: A | a - KEY_B = 66, // Key: B | b - KEY_C = 67, // Key: C | c - KEY_D = 68, // Key: D | d - KEY_E = 69, // Key: E | e - KEY_F = 70, // Key: F | f - KEY_G = 71, // Key: G | g - KEY_H = 72, // Key: H | h - KEY_I = 73, // Key: I | i - KEY_J = 74, // Key: J | j - KEY_K = 75, // Key: K | k - KEY_L = 76, // Key: L | l - KEY_M = 77, // Key: M | m - KEY_N = 78, // Key: N | n - KEY_O = 79, // Key: O | o - KEY_P = 80, // Key: P | p - KEY_Q = 81, // Key: Q | q - KEY_R = 82, // Key: R | r - KEY_S = 83, // Key: S | s - KEY_T = 84, // Key: T | t - KEY_U = 85, // Key: U | u - KEY_V = 86, // Key: V | v - KEY_W = 87, // Key: W | w - KEY_X = 88, // Key: X | x - KEY_Y = 89, // Key: Y | y - KEY_Z = 90, // Key: Z | z - KEY_LEFT_BRACKET = 91, // Key: [ - KEY_BACKSLASH = 92, // Key: '\' - KEY_RIGHT_BRACKET = 93, // Key: ] - KEY_GRAVE = 96, // Key: ` - // Function keys - KEY_SPACE = 32, // Key: Space - KEY_ESCAPE = 256, // Key: Esc - KEY_ENTER = 257, // Key: Enter - KEY_TAB = 258, // Key: Tab - KEY_BACKSPACE = 259, // Key: Backspace - KEY_INSERT = 260, // Key: Ins - KEY_DELETE = 261, // Key: Del - KEY_RIGHT = 262, // Key: Cursor right - KEY_LEFT = 263, // Key: Cursor left - KEY_DOWN = 264, // Key: Cursor down - KEY_UP = 265, // Key: Cursor up - KEY_PAGE_UP = 266, // Key: Page up - KEY_PAGE_DOWN = 267, // Key: Page down - KEY_HOME = 268, // Key: Home - KEY_END = 269, // Key: End - KEY_CAPS_LOCK = 280, // Key: Caps lock - KEY_SCROLL_LOCK = 281, // Key: Scroll down - KEY_NUM_LOCK = 282, // Key: Num lock - KEY_PRINT_SCREEN = 283, // Key: Print screen - KEY_PAUSE = 284, // Key: Pause - KEY_F1 = 290, // Key: F1 - KEY_F2 = 291, // Key: F2 - KEY_F3 = 292, // Key: F3 - KEY_F4 = 293, // Key: F4 - KEY_F5 = 294, // Key: F5 - KEY_F6 = 295, // Key: F6 - KEY_F7 = 296, // Key: F7 - KEY_F8 = 297, // Key: F8 - KEY_F9 = 298, // Key: F9 - KEY_F10 = 299, // Key: F10 - KEY_F11 = 300, // Key: F11 - KEY_F12 = 301, // Key: F12 - KEY_LEFT_SHIFT = 340, // Key: Shift left - KEY_LEFT_CONTROL = 341, // Key: Control left - KEY_LEFT_ALT = 342, // Key: Alt left - KEY_LEFT_SUPER = 343, // Key: Super left - KEY_RIGHT_SHIFT = 344, // Key: Shift right - KEY_RIGHT_CONTROL = 345, // Key: Control right - KEY_RIGHT_ALT = 346, // Key: Alt right - KEY_RIGHT_SUPER = 347, // Key: Super right - KEY_KB_MENU = 348, // Key: KB menu - // Keypad keys - KEY_KP_0 = 320, // Key: Keypad 0 - KEY_KP_1 = 321, // Key: Keypad 1 - KEY_KP_2 = 322, // Key: Keypad 2 - KEY_KP_3 = 323, // Key: Keypad 3 - KEY_KP_4 = 324, // Key: Keypad 4 - KEY_KP_5 = 325, // Key: Keypad 5 - KEY_KP_6 = 326, // Key: Keypad 6 - KEY_KP_7 = 327, // Key: Keypad 7 - KEY_KP_8 = 328, // Key: Keypad 8 - KEY_KP_9 = 329, // Key: Keypad 9 - KEY_KP_DECIMAL = 330, // Key: Keypad . - KEY_KP_DIVIDE = 331, // Key: Keypad / - KEY_KP_MULTIPLY = 332, // Key: Keypad * - KEY_KP_SUBTRACT = 333, // Key: Keypad - - KEY_KP_ADD = 334, // Key: Keypad + - KEY_KP_ENTER = 335, // Key: Keypad Enter - KEY_KP_EQUAL = 336, // Key: Keypad = - // Android key buttons - KEY_BACK = 4, // Key: Android back button - KEY_MENU = 82, // Key: Android menu button - KEY_VOLUME_UP = 24, // Key: Android volume up button - KEY_VOLUME_DOWN = 25 // Key: Android volume down button -} KeyboardKey; - -// Add backwards compatibility support for deprecated names -#define MOUSE_LEFT_BUTTON MOUSE_BUTTON_LEFT -#define MOUSE_RIGHT_BUTTON MOUSE_BUTTON_RIGHT -#define MOUSE_MIDDLE_BUTTON MOUSE_BUTTON_MIDDLE - -// Mouse buttons -typedef enum { - MOUSE_BUTTON_LEFT = 0, // Mouse button left - MOUSE_BUTTON_RIGHT = 1, // Mouse button right - MOUSE_BUTTON_MIDDLE = 2, // Mouse button middle (pressed wheel) - MOUSE_BUTTON_SIDE = 3, // Mouse button side (advanced mouse device) - MOUSE_BUTTON_EXTRA = 4, // Mouse button extra (advanced mouse device) - MOUSE_BUTTON_FORWARD = 5, // Mouse button forward (advanced mouse device) - MOUSE_BUTTON_BACK = 6, // Mouse button back (advanced mouse device) -} MouseButton; - -// Mouse cursor -typedef enum { - MOUSE_CURSOR_DEFAULT = 0, // Default pointer shape - MOUSE_CURSOR_ARROW = 1, // Arrow shape - MOUSE_CURSOR_IBEAM = 2, // Text writing cursor shape - MOUSE_CURSOR_CROSSHAIR = 3, // Cross shape - MOUSE_CURSOR_POINTING_HAND = 4, // Pointing hand cursor - MOUSE_CURSOR_RESIZE_EW = 5, // Horizontal resize/move arrow shape - MOUSE_CURSOR_RESIZE_NS = 6, // Vertical resize/move arrow shape - MOUSE_CURSOR_RESIZE_NWSE = 7, // Top-left to bottom-right diagonal resize/move arrow shape - MOUSE_CURSOR_RESIZE_NESW = 8, // The top-right to bottom-left diagonal resize/move arrow shape - MOUSE_CURSOR_RESIZE_ALL = 9, // The omnidirectional resize/move cursor shape - MOUSE_CURSOR_NOT_ALLOWED = 10 // The operation-not-allowed shape -} MouseCursor; - -// Gamepad buttons -typedef enum { - GAMEPAD_BUTTON_UNKNOWN = 0, // Unknown button, just 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 - GAMEPAD_BUTTON_LEFT_FACE_LEFT, // Gamepad left DPAD left button - GAMEPAD_BUTTON_RIGHT_FACE_UP, // Gamepad right button up (i.e. PS3: Triangle, Xbox: Y) - GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, // Gamepad right button right (i.e. PS3: Square, Xbox: X) - GAMEPAD_BUTTON_RIGHT_FACE_DOWN, // Gamepad right button down (i.e. PS3: Cross, Xbox: A) - GAMEPAD_BUTTON_RIGHT_FACE_LEFT, // Gamepad right button left (i.e. PS3: Circle, Xbox: B) - GAMEPAD_BUTTON_LEFT_TRIGGER_1, // Gamepad top/back trigger left (first), it could be a trailing button - GAMEPAD_BUTTON_LEFT_TRIGGER_2, // Gamepad top/back trigger left (second), it could be a trailing button - GAMEPAD_BUTTON_RIGHT_TRIGGER_1, // Gamepad top/back trigger right (one), it could be a trailing button - GAMEPAD_BUTTON_RIGHT_TRIGGER_2, // Gamepad top/back trigger right (second), it could be a trailing button - GAMEPAD_BUTTON_MIDDLE_LEFT, // Gamepad center buttons, left one (i.e. PS3: Select) - GAMEPAD_BUTTON_MIDDLE, // Gamepad center buttons, middle one (i.e. PS3: PS, Xbox: XBOX) - GAMEPAD_BUTTON_MIDDLE_RIGHT, // Gamepad center buttons, right one (i.e. PS3: Start) - GAMEPAD_BUTTON_LEFT_THUMB, // Gamepad joystick pressed button left - GAMEPAD_BUTTON_RIGHT_THUMB // Gamepad joystick pressed button right -} GamepadButton; - -// Gamepad axis -typedef enum { - GAMEPAD_AXIS_LEFT_X = 0, // Gamepad left stick X axis - GAMEPAD_AXIS_LEFT_Y = 1, // Gamepad left stick Y axis - GAMEPAD_AXIS_RIGHT_X = 2, // Gamepad right stick X axis - GAMEPAD_AXIS_RIGHT_Y = 3, // Gamepad right stick Y axis - GAMEPAD_AXIS_LEFT_TRIGGER = 4, // Gamepad back trigger left, pressure level: [1..-1] - GAMEPAD_AXIS_RIGHT_TRIGGER = 5 // Gamepad back trigger right, pressure level: [1..-1] -} GamepadAxis; - -// Material map index -typedef enum { - MATERIAL_MAP_ALBEDO = 0, // Albedo material (same as: MATERIAL_MAP_DIFFUSE) - MATERIAL_MAP_METALNESS, // Metalness material (same as: MATERIAL_MAP_SPECULAR) - MATERIAL_MAP_NORMAL, // Normal material - MATERIAL_MAP_ROUGHNESS, // Roughness material - MATERIAL_MAP_OCCLUSION, // Ambient occlusion material - MATERIAL_MAP_EMISSION, // Emission material - MATERIAL_MAP_HEIGHT, // Heightmap material - MATERIAL_MAP_CUBEMAP, // Cubemap material (NOTE: Uses GL_TEXTURE_CUBE_MAP) - MATERIAL_MAP_IRRADIANCE, // Irradiance material (NOTE: Uses GL_TEXTURE_CUBE_MAP) - MATERIAL_MAP_PREFILTER, // Prefilter material (NOTE: Uses GL_TEXTURE_CUBE_MAP) - MATERIAL_MAP_BRDF // Brdf material -} MaterialMapIndex; - -#define MATERIAL_MAP_DIFFUSE MATERIAL_MAP_ALBEDO -#define MATERIAL_MAP_SPECULAR MATERIAL_MAP_METALNESS - -// Shader location index -typedef enum { - SHADER_LOC_VERTEX_POSITION = 0, // Shader location: vertex attribute: position - SHADER_LOC_VERTEX_TEXCOORD01, // Shader location: vertex attribute: texcoord01 - SHADER_LOC_VERTEX_TEXCOORD02, // Shader location: vertex attribute: texcoord02 - SHADER_LOC_VERTEX_NORMAL, // Shader location: vertex attribute: normal - SHADER_LOC_VERTEX_TANGENT, // Shader location: vertex attribute: tangent - SHADER_LOC_VERTEX_COLOR, // Shader location: vertex attribute: color - SHADER_LOC_MATRIX_MVP, // Shader location: matrix uniform: model-view-projection - SHADER_LOC_MATRIX_VIEW, // Shader location: matrix uniform: view (camera transform) - SHADER_LOC_MATRIX_PROJECTION, // Shader location: matrix uniform: projection - SHADER_LOC_MATRIX_MODEL, // Shader location: matrix uniform: model (transform) - SHADER_LOC_MATRIX_NORMAL, // Shader location: matrix uniform: normal - SHADER_LOC_VECTOR_VIEW, // Shader location: vector uniform: view - SHADER_LOC_COLOR_DIFFUSE, // Shader location: vector uniform: diffuse color - SHADER_LOC_COLOR_SPECULAR, // Shader location: vector uniform: specular color - SHADER_LOC_COLOR_AMBIENT, // Shader location: vector uniform: ambient color - SHADER_LOC_MAP_ALBEDO, // Shader location: sampler2d texture: albedo (same as: SHADER_LOC_MAP_DIFFUSE) - SHADER_LOC_MAP_METALNESS, // Shader location: sampler2d texture: metalness (same as: SHADER_LOC_MAP_SPECULAR) - SHADER_LOC_MAP_NORMAL, // Shader location: sampler2d texture: normal - 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_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 -} ShaderLocationIndex; - -#define SHADER_LOC_MAP_DIFFUSE SHADER_LOC_MAP_ALBEDO -#define SHADER_LOC_MAP_SPECULAR SHADER_LOC_MAP_METALNESS - -// Shader uniform data type -typedef enum { - SHADER_UNIFORM_FLOAT = 0, // Shader uniform type: float - SHADER_UNIFORM_VEC2, // Shader uniform type: vec2 (2 float) - SHADER_UNIFORM_VEC3, // Shader uniform type: vec3 (3 float) - SHADER_UNIFORM_VEC4, // Shader uniform type: vec4 (4 float) - SHADER_UNIFORM_INT, // Shader uniform type: int - SHADER_UNIFORM_IVEC2, // Shader uniform type: ivec2 (2 int) - SHADER_UNIFORM_IVEC3, // Shader uniform type: ivec3 (3 int) - SHADER_UNIFORM_IVEC4, // Shader uniform type: ivec4 (4 int) - SHADER_UNIFORM_SAMPLER2D // Shader uniform type: sampler2d -} ShaderUniformDataType; - -// Shader attribute data types -typedef enum { - SHADER_ATTRIB_FLOAT = 0, // Shader attribute type: float - SHADER_ATTRIB_VEC2, // Shader attribute type: vec2 (2 float) - SHADER_ATTRIB_VEC3, // Shader attribute type: vec3 (3 float) - SHADER_ATTRIB_VEC4 // Shader attribute type: vec4 (4 float) -} ShaderAttributeDataType; - -// Pixel formats -// NOTE: Support depends on OpenGL version and platform -typedef enum { - PIXELFORMAT_UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha) - PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA, // 8*2 bpp (2 channels) - PIXELFORMAT_UNCOMPRESSED_R5G6B5, // 16 bpp - PIXELFORMAT_UNCOMPRESSED_R8G8B8, // 24 bpp - PIXELFORMAT_UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) - PIXELFORMAT_UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha) - PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, // 32 bpp - PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) - PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) - PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) - PIXELFORMAT_UNCOMPRESSED_R16, // 16 bpp (1 channel - half float) - PIXELFORMAT_UNCOMPRESSED_R16G16B16, // 16*3 bpp (3 channels - half float) - PIXELFORMAT_UNCOMPRESSED_R16G16B16A16, // 16*4 bpp (4 channels - half float) - PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) - PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) - PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp - PIXELFORMAT_COMPRESSED_DXT5_RGBA, // 8 bpp - PIXELFORMAT_COMPRESSED_ETC1_RGB, // 4 bpp - PIXELFORMAT_COMPRESSED_ETC2_RGB, // 4 bpp - PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA, // 8 bpp - PIXELFORMAT_COMPRESSED_PVRT_RGB, // 4 bpp - PIXELFORMAT_COMPRESSED_PVRT_RGBA, // 4 bpp - PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA, // 8 bpp - PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA // 2 bpp -} PixelFormat; - -// Texture parameters: filter mode -// 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_BILINEAR, // Linear filtering - TEXTURE_FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps) - TEXTURE_FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x - TEXTURE_FILTER_ANISOTROPIC_8X, // Anisotropic filtering 8x - TEXTURE_FILTER_ANISOTROPIC_16X, // Anisotropic filtering 16x -} TextureFilter; - -// Texture parameters: wrap mode -typedef enum { - TEXTURE_WRAP_REPEAT = 0, // Repeats texture in tiled mode - TEXTURE_WRAP_CLAMP, // Clamps texture to edge pixel in tiled mode - TEXTURE_WRAP_MIRROR_REPEAT, // Mirrors and repeats the texture in tiled mode - TEXTURE_WRAP_MIRROR_CLAMP // Mirrors and clamps to border the texture in tiled mode -} TextureWrap; - -// Cubemap layouts -typedef enum { - CUBEMAP_LAYOUT_AUTO_DETECT = 0, // Automatically detect layout type - CUBEMAP_LAYOUT_LINE_VERTICAL, // Layout is defined by a vertical line with faces - CUBEMAP_LAYOUT_LINE_HORIZONTAL, // Layout is defined by a horizontal line with faces - CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR, // Layout is defined by a 3x4 cross with cubemap faces - CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE, // Layout is defined by a 4x3 cross with cubemap faces - CUBEMAP_LAYOUT_PANORAMA // Layout is defined by a panorama image (equirrectangular map) -} CubemapLayout; - -// Font type, defines generation method -typedef enum { - FONT_DEFAULT = 0, // Default font generation, anti-aliased - FONT_BITMAP, // Bitmap font generation, no anti-aliasing - FONT_SDF // SDF font generation, requires external shader -} FontType; - -// Color blending modes (pre-defined) -typedef enum { - BLEND_ALPHA = 0, // Blend textures considering alpha (default) - BLEND_ADDITIVE, // Blend textures adding colors - BLEND_MULTIPLIED, // Blend textures multiplying colors - BLEND_ADD_COLORS, // Blend textures adding colors (alternative) - BLEND_SUBTRACT_COLORS, // Blend textures subtracting colors (alternative) - BLEND_ALPHA_PREMULTIPLY, // Blend premultiplied textures considering alpha - BLEND_CUSTOM, // Blend textures using custom src/dst factors (use rlSetBlendFactors()) - BLEND_CUSTOM_SEPARATE // Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendFactorsSeparate()) -} BlendMode; - -// Gesture -// NOTE: Provided as bit-wise flags to enable only desired gestures -typedef enum { - GESTURE_NONE = 0, // No gesture - GESTURE_TAP = 1, // Tap gesture - GESTURE_DOUBLETAP = 2, // Double tap gesture - GESTURE_HOLD = 4, // Hold gesture - GESTURE_DRAG = 8, // Drag gesture - GESTURE_SWIPE_RIGHT = 16, // Swipe right gesture - GESTURE_SWIPE_LEFT = 32, // Swipe left gesture - GESTURE_SWIPE_UP = 64, // Swipe up gesture - GESTURE_SWIPE_DOWN = 128, // Swipe down gesture - GESTURE_PINCH_IN = 256, // Pinch in gesture - GESTURE_PINCH_OUT = 512 // Pinch out gesture -} Gesture; - -// Camera system modes -typedef enum { - CAMERA_CUSTOM = 0, // Custom camera - CAMERA_FREE, // Free camera - CAMERA_ORBITAL, // Orbital camera - CAMERA_FIRST_PERSON, // First person camera - CAMERA_THIRD_PERSON // Third person camera -} CameraMode; - -// Camera projection -typedef enum { - CAMERA_PERSPECTIVE = 0, // Perspective projection - CAMERA_ORTHOGRAPHIC // Orthographic projection -} CameraProjection; - -// N-patch layout -typedef enum { - NPATCH_NINE_PATCH = 0, // Npatch layout: 3x3 tiles - NPATCH_THREE_PATCH_VERTICAL, // Npatch layout: 1x3 tiles - NPATCH_THREE_PATCH_HORIZONTAL // Npatch layout: 3x1 tiles -} NPatchLayout; - -// Callbacks to hook some internal functions -// WARNING: These callbacks are intended for advance users -typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args); // Logging: Redirect trace log messages -typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, int *dataSize); // FileIO: Load binary data -typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, int dataSize); // FileIO: Save binary data -typedef char *(*LoadFileTextCallback)(const char *fileName); // FileIO: Load text data -typedef bool (*SaveFileTextCallback)(const char *fileName, char *text); // FileIO: Save text data - -//------------------------------------------------------------------------------------ -// Global Variables Definition -//------------------------------------------------------------------------------------ -// It's lonely here... - -//------------------------------------------------------------------------------------ -// Window and Graphics Device Functions (Module: core) -//------------------------------------------------------------------------------------ - -#if defined(__cplusplus) -extern "C" { // Prevents name mangling of functions -#endif - -// Window-related functions -RLAPI void InitWindow(int width, int height, const char *title); // Initialize window and OpenGL context -RLAPI void CloseWindow(void); // Close window and unload OpenGL context -RLAPI bool WindowShouldClose(void); // Check if application should close (KEY_ESCAPE pressed or windows close icon clicked) -RLAPI bool IsWindowReady(void); // Check if window has been initialized successfully -RLAPI bool IsWindowFullscreen(void); // Check if window is currently fullscreen -RLAPI bool IsWindowHidden(void); // Check if window is currently hidden (only PLATFORM_DESKTOP) -RLAPI bool IsWindowMinimized(void); // Check if window is currently minimized (only PLATFORM_DESKTOP) -RLAPI bool IsWindowMaximized(void); // Check if window is currently maximized (only PLATFORM_DESKTOP) -RLAPI bool IsWindowFocused(void); // Check if window is currently focused (only PLATFORM_DESKTOP) -RLAPI bool IsWindowResized(void); // Check if window has been resized last frame -RLAPI bool IsWindowState(unsigned int flag); // Check if one specific window flag is enabled -RLAPI void SetWindowState(unsigned int flags); // Set window configuration state using flags (only PLATFORM_DESKTOP) -RLAPI void ClearWindowState(unsigned int flags); // Clear window configuration state flags -RLAPI void ToggleFullscreen(void); // Toggle window state: fullscreen/windowed (only PLATFORM_DESKTOP) -RLAPI void ToggleBorderlessWindowed(void); // Toggle window state: borderless windowed (only PLATFORM_DESKTOP) -RLAPI void MaximizeWindow(void); // Set window state: maximized, if resizable (only PLATFORM_DESKTOP) -RLAPI void MinimizeWindow(void); // Set window state: minimized, if resizable (only PLATFORM_DESKTOP) -RLAPI void RestoreWindow(void); // Set window state: not minimized/maximized (only PLATFORM_DESKTOP) -RLAPI void SetWindowIcon(Image image); // Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP) -RLAPI void SetWindowIcons(Image *images, int count); // Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP) -RLAPI void SetWindowTitle(const char *title); // Set title for window (only PLATFORM_DESKTOP and PLATFORM_WEB) -RLAPI void SetWindowPosition(int x, int y); // Set window position on screen (only PLATFORM_DESKTOP) -RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window -RLAPI void SetWindowMinSize(int width, int height); // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE) -RLAPI void SetWindowMaxSize(int width, int height); // Set window maximum dimensions (for FLAG_WINDOW_RESIZABLE) -RLAPI void SetWindowSize(int width, int height); // Set window dimensions -RLAPI void SetWindowOpacity(float opacity); // Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP) -RLAPI void SetWindowFocused(void); // Set window focused (only PLATFORM_DESKTOP) -RLAPI void *GetWindowHandle(void); // Get native window handle -RLAPI int GetScreenWidth(void); // Get current screen width -RLAPI int GetScreenHeight(void); // Get current screen height -RLAPI int GetRenderWidth(void); // Get current render width (it considers HiDPI) -RLAPI int GetRenderHeight(void); // Get current render height (it considers HiDPI) -RLAPI int GetMonitorCount(void); // Get number of connected monitors -RLAPI int GetCurrentMonitor(void); // Get current connected monitor -RLAPI Vector2 GetMonitorPosition(int monitor); // Get specified monitor position -RLAPI int GetMonitorWidth(int monitor); // Get specified monitor width (current video mode used by monitor) -RLAPI int GetMonitorHeight(int monitor); // Get specified monitor height (current video mode used by monitor) -RLAPI int GetMonitorPhysicalWidth(int monitor); // Get specified monitor physical width in millimetres -RLAPI int GetMonitorPhysicalHeight(int monitor); // Get specified monitor physical height in millimetres -RLAPI int GetMonitorRefreshRate(int monitor); // Get specified monitor refresh rate -RLAPI Vector2 GetWindowPosition(void); // Get window position XY on monitor -RLAPI Vector2 GetWindowScaleDPI(void); // Get window scale DPI factor -RLAPI const char *GetMonitorName(int monitor); // Get the human-readable, UTF-8 encoded name of the specified monitor -RLAPI void SetClipboardText(const char *text); // Set clipboard text content -RLAPI const char *GetClipboardText(void); // Get clipboard text content -RLAPI void EnableEventWaiting(void); // Enable waiting for events on EndDrawing(), no automatic event polling -RLAPI void DisableEventWaiting(void); // Disable waiting for events on EndDrawing(), automatic events polling - -// Cursor-related functions -RLAPI void ShowCursor(void); // Shows cursor -RLAPI void HideCursor(void); // Hides cursor -RLAPI bool IsCursorHidden(void); // Check if cursor is not visible -RLAPI void EnableCursor(void); // Enables cursor (unlock cursor) -RLAPI void DisableCursor(void); // Disables cursor (lock cursor) -RLAPI bool IsCursorOnScreen(void); // Check if cursor is on the screen - -// Drawing-related functions -RLAPI void ClearBackground(Color color); // Set background color (framebuffer clear color) -RLAPI void BeginDrawing(void); // Setup canvas (framebuffer) to start drawing -RLAPI void EndDrawing(void); // End canvas drawing and swap buffers (double buffering) -RLAPI void BeginMode2D(Camera2D camera); // Begin 2D mode with custom camera (2D) -RLAPI void EndMode2D(void); // Ends 2D mode with custom camera -RLAPI void BeginMode3D(Camera3D camera); // Begin 3D mode with custom camera (3D) -RLAPI void EndMode3D(void); // Ends 3D mode and returns to default 2D orthographic mode -RLAPI void BeginTextureMode(RenderTexture2D target); // Begin drawing to render texture -RLAPI void EndTextureMode(void); // Ends drawing to render texture -RLAPI void BeginShaderMode(Shader shader); // Begin custom shader drawing -RLAPI void EndShaderMode(void); // End custom shader drawing (use default shader) -RLAPI void BeginBlendMode(int mode); // Begin blending mode (alpha, additive, multiplied, subtract, custom) -RLAPI void EndBlendMode(void); // End blending mode (reset to default: alpha blending) -RLAPI void BeginScissorMode(int x, int y, int width, int height); // Begin scissor mode (define screen area for following drawing) -RLAPI void EndScissorMode(void); // End scissor mode -RLAPI void BeginVrStereoMode(VrStereoConfig config); // Begin stereo rendering (requires VR simulator) -RLAPI void EndVrStereoMode(void); // End stereo rendering (requires VR simulator) - -// VR stereo config functions for VR simulator -RLAPI VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device); // Load VR stereo config for VR simulator device parameters -RLAPI void UnloadVrStereoConfig(VrStereoConfig config); // Unload VR stereo config - -// Shader management functions -// NOTE: Shader functionality is not available on OpenGL 1.1 -RLAPI Shader LoadShader(const char *vsFileName, const char *fsFileName); // Load shader from files and bind default locations -RLAPI Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode); // Load shader from code strings and bind default locations -RLAPI bool IsShaderReady(Shader shader); // Check if a shader is ready -RLAPI int GetShaderLocation(Shader shader, const char *uniformName); // Get shader uniform location -RLAPI int GetShaderLocationAttrib(Shader shader, const char *attribName); // Get shader attribute location -RLAPI void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType); // Set shader uniform value -RLAPI void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count); // Set shader uniform value vector -RLAPI void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat); // Set shader uniform value (matrix 4x4) -RLAPI void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture); // Set shader uniform value for texture (sampler2d) -RLAPI void UnloadShader(Shader shader); // Unload shader from GPU memory (VRAM) - -// Screen-space-related functions -RLAPI Ray GetMouseRay(Vector2 mousePosition, Camera camera); // Get a ray trace from mouse position -RLAPI Matrix GetCameraMatrix(Camera camera); // Get camera transform matrix (view matrix) -RLAPI Matrix GetCameraMatrix2D(Camera2D camera); // Get camera 2d transform matrix -RLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera); // Get the screen space position for a 3d world space position -RLAPI Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera); // Get the world space position for a 2d camera screen space position -RLAPI Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int height); // Get size position for a 3d world space position -RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera); // Get the screen space position for a 2d camera world space position - -// Timing-related functions -RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum) -RLAPI float GetFrameTime(void); // Get time in seconds for last frame drawn (delta time) -RLAPI double GetTime(void); // Get elapsed time in seconds since InitWindow() -RLAPI int GetFPS(void); // Get current FPS - -// Custom frame control functions -// NOTE: Those functions are intended for advance users that want full control over the frame processing -// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents() -// To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL -RLAPI void SwapScreenBuffer(void); // Swap back buffer with front buffer (screen drawing) -RLAPI void PollInputEvents(void); // Register all input events -RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution) - -// Random values generation functions -RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator -RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included) -RLAPI int *LoadRandomSequence(unsigned int count, int min, int max); // Load random values sequence, no values repeated -RLAPI void UnloadRandomSequence(int *sequence); // Unload random values sequence - -// Misc. functions -RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format) -RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS) -RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available) - -// NOTE: Following functions implemented in module [utils] -//------------------------------------------------------------------ -RLAPI void TraceLog(int logLevel, const char *text, ...); // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) -RLAPI void SetTraceLogLevel(int logLevel); // Set the current threshold (minimum) log level -RLAPI void *MemAlloc(unsigned int size); // Internal memory allocator -RLAPI void *MemRealloc(void *ptr, unsigned int size); // Internal memory reallocator -RLAPI void MemFree(void *ptr); // Internal memory free - -// Set custom callbacks -// WARNING: Callbacks setup is intended for advance users -RLAPI void SetTraceLogCallback(TraceLogCallback callback); // Set custom trace log -RLAPI void SetLoadFileDataCallback(LoadFileDataCallback callback); // Set custom file binary data loader -RLAPI void SetSaveFileDataCallback(SaveFileDataCallback callback); // Set custom file binary data saver -RLAPI void SetLoadFileTextCallback(LoadFileTextCallback callback); // Set custom file text data loader -RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom file text data saver - -// Files management functions -RLAPI unsigned char *LoadFileData(const char *fileName, int *dataSize); // Load file data as byte array (read) -RLAPI void UnloadFileData(unsigned char *data); // Unload file data allocated by LoadFileData() -RLAPI bool SaveFileData(const char *fileName, void *data, int dataSize); // Save data to file from byte array (write), returns true on success -RLAPI bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName); // Export data to code (.h), returns true on success -RLAPI char *LoadFileText(const char *fileName); // Load text data from file (read), returns a '\0' terminated string -RLAPI void UnloadFileText(char *text); // Unload file text data allocated by LoadFileText() -RLAPI bool SaveFileText(const char *fileName, char *text); // Save text data to file (write), string must be '\0' terminated, returns true on success -//------------------------------------------------------------------ - -// File system functions -RLAPI bool FileExists(const char *fileName); // Check if file exists -RLAPI bool DirectoryExists(const char *dirPath); // Check if a directory path exists -RLAPI bool IsFileExtension(const char *fileName, const char *ext); // Check file extension (including point: .png, .wav) -RLAPI int GetFileLength(const char *fileName); // Get file length in bytes (NOTE: GetFileSize() conflicts with windows.h) -RLAPI const char *GetFileExtension(const char *fileName); // Get pointer to extension for a filename string (includes dot: '.png') -RLAPI const char *GetFileName(const char *filePath); // Get pointer to filename for a path string -RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filename string without extension (uses static string) -RLAPI const char *GetDirectoryPath(const char *filePath); // Get full path for a given fileName with path (uses static string) -RLAPI const char *GetPrevDirectoryPath(const char *dirPath); // Get previous directory path for a given path (uses static string) -RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string) -RLAPI const char *GetApplicationDirectory(void); // Get the directory of the running application (uses static string) -RLAPI bool ChangeDirectory(const char *dir); // Change working directory, return true on success -RLAPI bool IsPathFile(const char *path); // Check if a given path is a file or a directory -RLAPI FilePathList LoadDirectoryFiles(const char *dirPath); // Load directory filepaths -RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and recursive directory scan -RLAPI void UnloadDirectoryFiles(FilePathList files); // Unload filepaths -RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window -RLAPI FilePathList LoadDroppedFiles(void); // Load dropped filepaths -RLAPI void UnloadDroppedFiles(FilePathList files); // Unload dropped filepaths -RLAPI long GetFileModTime(const char *fileName); // Get file modification time (last write time) - -// Compression/Encoding functionality -RLAPI unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize); // Compress data (DEFLATE algorithm), memory must be MemFree() -RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // Decompress data (DEFLATE algorithm), memory must be MemFree() -RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string, memory must be MemFree() -RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree() - -// Automation events functionality -RLAPI AutomationEventList LoadAutomationEventList(const char *fileName); // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS -RLAPI void UnloadAutomationEventList(AutomationEventList *list); // Unload automation events list from file -RLAPI bool ExportAutomationEventList(AutomationEventList list, const char *fileName); // Export automation events list as text file -RLAPI void SetAutomationEventList(AutomationEventList *list); // Set automation event list to record to -RLAPI void SetAutomationEventBaseFrame(int frame); // Set automation event internal base frame to start recording -RLAPI void StartAutomationEventRecording(void); // Start recording automation events (AutomationEventList must be set) -RLAPI void StopAutomationEventRecording(void); // Stop recording automation events -RLAPI void PlayAutomationEvent(AutomationEvent event); // Play a recorded automation event - -//------------------------------------------------------------------------------------ -// Input Handling Functions (Module: core) -//------------------------------------------------------------------------------------ - -// Input-related functions: keyboard -RLAPI bool IsKeyPressed(int key); // Check if a key has been pressed once -RLAPI bool IsKeyPressedRepeat(int key); // Check if a key has been pressed again (Only PLATFORM_DESKTOP) -RLAPI bool IsKeyDown(int key); // Check if a key is being pressed -RLAPI bool IsKeyReleased(int key); // Check if a key has been released once -RLAPI bool IsKeyUp(int key); // Check if a key is NOT being pressed -RLAPI int GetKeyPressed(void); // Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty -RLAPI int GetCharPressed(void); // Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty -RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC) - -// Input-related functions: gamepads -RLAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is available -RLAPI const char *GetGamepadName(int gamepad); // Get gamepad internal name id -RLAPI bool IsGamepadButtonPressed(int gamepad, int button); // Check if a gamepad button has been pressed once -RLAPI bool IsGamepadButtonDown(int gamepad, int button); // Check if a gamepad button is being pressed -RLAPI bool IsGamepadButtonReleased(int gamepad, int button); // Check if a gamepad button has been released once -RLAPI bool IsGamepadButtonUp(int gamepad, int button); // Check if a gamepad button is NOT being pressed -RLAPI int GetGamepadButtonPressed(void); // Get the last gamepad button pressed -RLAPI int GetGamepadAxisCount(int gamepad); // Get gamepad axis count for a gamepad -RLAPI float GetGamepadAxisMovement(int gamepad, int axis); // Get axis movement value for a gamepad axis -RLAPI int SetGamepadMappings(const char *mappings); // Set internal gamepad mappings (SDL_GameControllerDB) - -// Input-related functions: mouse -RLAPI bool IsMouseButtonPressed(int button); // Check if a mouse button has been pressed once -RLAPI bool IsMouseButtonDown(int button); // Check if a mouse button is being pressed -RLAPI bool IsMouseButtonReleased(int button); // Check if a mouse button has been released once -RLAPI bool IsMouseButtonUp(int button); // Check if a mouse button is NOT being pressed -RLAPI int GetMouseX(void); // Get mouse position X -RLAPI int GetMouseY(void); // Get mouse position Y -RLAPI Vector2 GetMousePosition(void); // Get mouse position XY -RLAPI Vector2 GetMouseDelta(void); // Get mouse delta between frames -RLAPI void SetMousePosition(int x, int y); // Set mouse position XY -RLAPI void SetMouseOffset(int offsetX, int offsetY); // Set mouse offset -RLAPI void SetMouseScale(float scaleX, float scaleY); // Set mouse scaling -RLAPI float GetMouseWheelMove(void); // Get mouse wheel movement for X or Y, whichever is larger -RLAPI Vector2 GetMouseWheelMoveV(void); // Get mouse wheel movement for both X and Y -RLAPI void SetMouseCursor(int cursor); // Set mouse cursor - -// Input-related functions: touch -RLAPI int GetTouchX(void); // Get touch position X for touch point 0 (relative to screen size) -RLAPI int GetTouchY(void); // Get touch position Y for touch point 0 (relative to screen size) -RLAPI Vector2 GetTouchPosition(int index); // Get touch position XY for a touch point index (relative to screen size) -RLAPI int GetTouchPointId(int index); // Get touch point identifier for given index -RLAPI int GetTouchPointCount(void); // Get number of touch points - -//------------------------------------------------------------------------------------ -// Gestures and Touch Handling Functions (Module: rgestures) -//------------------------------------------------------------------------------------ -RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags -RLAPI bool IsGestureDetected(unsigned int gesture); // Check if a gesture have been detected -RLAPI int GetGestureDetected(void); // Get latest detected gesture -RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds -RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector -RLAPI float GetGestureDragAngle(void); // Get gesture drag angle -RLAPI Vector2 GetGesturePinchVector(void); // Get gesture pinch delta -RLAPI float GetGesturePinchAngle(void); // Get gesture pinch angle - -//------------------------------------------------------------------------------------ -// Camera System Functions (Module: rcamera) -//------------------------------------------------------------------------------------ -RLAPI void UpdateCamera(Camera *camera, int mode); // Update camera position for selected mode -RLAPI void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, float zoom); // Update camera movement/rotation - -//------------------------------------------------------------------------------------ -// Basic Shapes Drawing Functions (Module: shapes) -//------------------------------------------------------------------------------------ -// Set texture and rectangle to be used on shapes drawing -// NOTE: It can be useful when using basic shapes and one single font, -// defining a font char white rectangle would allow drawing everything in a single draw call -RLAPI void SetShapesTexture(Texture2D texture, Rectangle source); // Set texture and rectangle to be used on shapes drawing - -// Basic shapes drawing functions -RLAPI void DrawPixel(int posX, int posY, Color color); // Draw a pixel -RLAPI void DrawPixelV(Vector2 position, Color color); // Draw a pixel (Vector version) -RLAPI void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw a line -RLAPI void DrawLineV(Vector2 startPos, Vector2 endPos, Color color); // Draw a line (using gl lines) -RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line (using triangles/quads) -RLAPI void DrawLineStrip(Vector2 *points, int pointCount, Color color); // Draw lines sequence (using gl lines) -RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw line segment cubic-bezier in-out interpolation -RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle -RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw a piece of a circle -RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw circle sector outline -RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle -RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) -RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline -RLAPI void DrawCircleLinesV(Vector2 center, float radius, Color color); // Draw circle outline (Vector version) -RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse -RLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse outline -RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring -RLAPI void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring outline -RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle -RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color); // Draw a color-filled rectangle (Vector version) -RLAPI void DrawRectangleRec(Rectangle rec, Color color); // Draw a color-filled rectangle -RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color); // Draw a color-filled rectangle with pro parameters -RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a vertical-gradient-filled rectangle -RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a horizontal-gradient-filled rectangle -RLAPI void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // Draw a gradient-filled rectangle with custom vertex colors -RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color); // Draw rectangle outline -RLAPI void DrawRectangleLinesEx(Rectangle rec, float lineThick, Color color); // Draw rectangle outline with extended parameters -RLAPI void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color); // Draw rectangle with rounded edges -RLAPI void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, float lineThick, Color color); // Draw rectangle with rounded edges outline -RLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw a color-filled triangle (vertex in counter-clockwise order!) -RLAPI void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline (vertex in counter-clockwise order!) -RLAPI void DrawTriangleFan(Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points (first vertex is the center) -RLAPI void DrawTriangleStrip(Vector2 *points, int pointCount, Color color); // Draw a triangle strip defined by points -RLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color); // Draw a regular polygon (Vector version) -RLAPI void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Color color); // Draw a polygon outline of n sides -RLAPI void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, float lineThick, Color color); // Draw a polygon outline of n sides with extended parameters - -// Splines drawing functions -RLAPI void DrawSplineLinear(Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Linear, minimum 2 points -RLAPI void DrawSplineBasis(Vector2 *points, int pointCount, float thick, Color color); // Draw spline: B-Spline, minimum 4 points -RLAPI void DrawSplineCatmullRom(Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Catmull-Rom, minimum 4 points -RLAPI void DrawSplineBezierQuadratic(Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...] -RLAPI void DrawSplineBezierCubic(Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...] -RLAPI void DrawSplineSegmentLinear(Vector2 p1, Vector2 p2, float thick, Color color); // Draw spline segment: Linear, 2 points -RLAPI void DrawSplineSegmentBasis(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color); // Draw spline segment: B-Spline, 4 points -RLAPI void DrawSplineSegmentCatmullRom(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color); // Draw spline segment: Catmull-Rom, 4 points -RLAPI void DrawSplineSegmentBezierQuadratic(Vector2 p1, Vector2 c2, Vector2 p3, float thick, Color color); // Draw spline segment: Quadratic Bezier, 2 points, 1 control point -RLAPI void DrawSplineSegmentBezierCubic(Vector2 p1, Vector2 c2, Vector2 c3, Vector2 p4, float thick, Color color); // Draw spline segment: Cubic Bezier, 2 points, 2 control points - -// Spline segment point evaluation functions, for a given t [0.0f .. 1.0f] -RLAPI Vector2 GetSplinePointLinear(Vector2 startPos, Vector2 endPos, float t); // Get (evaluate) spline point: Linear -RLAPI Vector2 GetSplinePointBasis(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float t); // Get (evaluate) spline point: B-Spline -RLAPI Vector2 GetSplinePointCatmullRom(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float t); // Get (evaluate) spline point: Catmull-Rom -RLAPI Vector2 GetSplinePointBezierQuad(Vector2 p1, Vector2 c2, Vector2 p3, float t); // Get (evaluate) spline point: Quadratic Bezier -RLAPI Vector2 GetSplinePointBezierCubic(Vector2 p1, Vector2 c2, Vector2 c3, Vector2 p4, float t); // Get (evaluate) spline point: Cubic Bezier - -// Basic shapes collision detection functions -RLAPI bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2); // Check collision between two rectangles -RLAPI bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, float radius2); // Check collision between two circles -RLAPI bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec); // Check collision between circle and rectangle -RLAPI bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle -RLAPI bool CheckCollisionPointCircle(Vector2 point, Vector2 center, float radius); // Check if point is inside circle -RLAPI bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 p3); // Check if point is inside a triangle -RLAPI bool CheckCollisionPointPoly(Vector2 point, Vector2 *points, int pointCount); // Check if point is within a polygon described by array of vertices -RLAPI bool CheckCollisionLines(Vector2 startPos1, Vector2 endPos1, Vector2 startPos2, Vector2 endPos2, Vector2 *collisionPoint); // Check the collision between two lines defined by two points each, returns collision point by reference -RLAPI bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshold); // Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold] -RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); // Get collision rectangle for two rectangles collision - -//------------------------------------------------------------------------------------ -// Texture Loading and Drawing Functions (Module: textures) -//------------------------------------------------------------------------------------ - -// Image loading functions -// NOTE: These functions do not require GPU access -RLAPI Image LoadImage(const char *fileName); // Load image from file into CPU memory (RAM) -RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data -RLAPI Image LoadImageSvg(const char *fileNameOrString, int width, int height); // Load image from SVG file data or string with specified size -RLAPI Image LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data) -RLAPI Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load image from memory buffer, fileType refers to extension: i.e. '.png' -RLAPI Image LoadImageFromTexture(Texture2D texture); // Load image from GPU texture data -RLAPI Image LoadImageFromScreen(void); // Load image from screen buffer and (screenshot) -RLAPI bool IsImageReady(Image image); // Check if an image is ready -RLAPI void UnloadImage(Image image); // Unload image from CPU memory (RAM) -RLAPI bool ExportImage(Image image, const char *fileName); // Export image data to file, returns true on success -RLAPI unsigned char *ExportImageToMemory(Image image, const char *fileType, int *fileSize); // Export image to memory buffer -RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes, returns true on success - -// Image generation functions -RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color -RLAPI Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end); // Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient -RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient -RLAPI Image GenImageGradientSquare(int width, int height, float density, Color inner, Color outer); // Generate image: square gradient -RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked -RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise -RLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale); // Generate image: perlin noise -RLAPI Image GenImageCellular(int width, int height, int tileSize); // Generate image: cellular algorithm, bigger tileSize means bigger cells -RLAPI Image GenImageText(int width, int height, const char *text); // Generate image: grayscale image from text data - -// Image manipulation functions -RLAPI Image ImageCopy(Image image); // Create an image duplicate (useful for transformations) -RLAPI Image ImageFromImage(Image image, Rectangle rec); // Create an image from another image piece -RLAPI Image ImageText(const char *text, int fontSize, Color color); // Create an image from text (default font) -RLAPI Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint); // Create an image from text (custom sprite font) -RLAPI void ImageFormat(Image *image, int newFormat); // Convert image data to desired format -RLAPI void ImageToPOT(Image *image, Color fill); // Convert image to POT (power-of-two) -RLAPI void ImageCrop(Image *image, Rectangle crop); // Crop an image to a defined rectangle -RLAPI void ImageAlphaCrop(Image *image, float threshold); // Crop image depending on alpha value -RLAPI void ImageAlphaClear(Image *image, Color color, float threshold); // Clear alpha channel to desired color -RLAPI void ImageAlphaMask(Image *image, Image alphaMask); // Apply alpha mask to image -RLAPI void ImageAlphaPremultiply(Image *image); // Premultiply alpha channel -RLAPI void ImageBlurGaussian(Image *image, int blurSize); // Apply Gaussian blur using a box blur approximation -RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (Bicubic scaling algorithm) -RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight); // Resize image (Nearest-Neighbor scaling algorithm) -RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill); // Resize canvas and fill with color -RLAPI void ImageMipmaps(Image *image); // Compute all mipmap levels for a provided image -RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) -RLAPI void ImageFlipVertical(Image *image); // Flip image vertically -RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally -RLAPI void ImageRotate(Image *image, int degrees); // Rotate image by input angle in degrees (-359 to 359) -RLAPI void ImageRotateCW(Image *image); // Rotate image clockwise 90deg -RLAPI void ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg -RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint -RLAPI void ImageColorInvert(Image *image); // Modify image color: invert -RLAPI void ImageColorGrayscale(Image *image); // Modify image color: grayscale -RLAPI void ImageColorContrast(Image *image, float contrast); // Modify image color: contrast (-100 to 100) -RLAPI void ImageColorBrightness(Image *image, int brightness); // Modify image color: brightness (-255 to 255) -RLAPI void ImageColorReplace(Image *image, Color color, Color replace); // Modify image color: replace color -RLAPI Color *LoadImageColors(Image image); // Load color data from image as a Color array (RGBA - 32bit) -RLAPI Color *LoadImagePalette(Image image, int maxPaletteSize, int *colorCount); // Load colors palette from image as a Color array (RGBA - 32bit) -RLAPI void UnloadImageColors(Color *colors); // Unload color data loaded with LoadImageColors() -RLAPI void UnloadImagePalette(Color *colors); // Unload colors palette loaded with LoadImagePalette() -RLAPI Rectangle GetImageAlphaBorder(Image image, float threshold); // Get image alpha border rectangle -RLAPI Color GetImageColor(Image image, int x, int y); // Get image pixel color at (x, y) position - -// Image drawing functions -// NOTE: Image software-rendering functions (CPU) -RLAPI void ImageClearBackground(Image *dst, Color color); // Clear image background with given color -RLAPI void ImageDrawPixel(Image *dst, int posX, int posY, Color color); // Draw pixel within an image -RLAPI void ImageDrawPixelV(Image *dst, Vector2 position, Color color); // Draw pixel within an image (Vector version) -RLAPI void ImageDrawLine(Image *dst, int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw line within an image -RLAPI void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color); // Draw line within an image (Vector version) -RLAPI void ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color color); // Draw a filled circle within an image -RLAPI void ImageDrawCircleV(Image *dst, Vector2 center, int radius, Color color); // Draw a filled circle within an image (Vector version) -RLAPI void ImageDrawCircleLines(Image *dst, int centerX, int centerY, int radius, Color color); // Draw circle outline within an image -RLAPI void ImageDrawCircleLinesV(Image *dst, Vector2 center, int radius, Color color); // Draw circle outline within an image (Vector version) -RLAPI void ImageDrawRectangle(Image *dst, int posX, int posY, int width, int height, Color color); // Draw rectangle within an image -RLAPI void ImageDrawRectangleV(Image *dst, Vector2 position, Vector2 size, Color color); // Draw rectangle within an image (Vector version) -RLAPI void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color); // Draw rectangle within an image -RLAPI void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color); // Draw rectangle lines 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) - -// Texture loading functions -// NOTE: These functions require GPU access -RLAPI Texture2D LoadTexture(const char *fileName); // Load texture from file into GPU memory (VRAM) -RLAPI Texture2D LoadTextureFromImage(Image image); // Load texture from image data -RLAPI TextureCubemap LoadTextureCubemap(Image image, int layout); // Load cubemap from image, multiple image cubemap layouts supported -RLAPI RenderTexture2D LoadRenderTexture(int width, int height); // Load texture for rendering (framebuffer) -RLAPI bool IsTextureReady(Texture2D texture); // Check if a texture is ready -RLAPI void UnloadTexture(Texture2D texture); // Unload texture from GPU memory (VRAM) -RLAPI bool IsRenderTextureReady(RenderTexture2D target); // Check if a render texture is ready -RLAPI void UnloadRenderTexture(RenderTexture2D target); // Unload render texture from GPU memory (VRAM) -RLAPI void UpdateTexture(Texture2D texture, const void *pixels); // Update GPU texture with new data -RLAPI void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels); // Update GPU texture rectangle with new data - -// Texture configuration functions -RLAPI void GenTextureMipmaps(Texture2D *texture); // Generate GPU mipmaps for a texture -RLAPI void SetTextureFilter(Texture2D texture, int filter); // Set texture scaling filter mode -RLAPI void SetTextureWrap(Texture2D texture, int wrap); // Set texture wrapping mode - -// Texture drawing functions -RLAPI void DrawTexture(Texture2D texture, int posX, int posY, Color tint); // Draw a Texture2D -RLAPI void DrawTextureV(Texture2D texture, Vector2 position, Color tint); // Draw a Texture2D with position defined as Vector2 -RLAPI void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint); // Draw a Texture2D with extended parameters -RLAPI void DrawTextureRec(Texture2D texture, Rectangle source, Vector2 position, Color tint); // Draw a part of a texture defined by a rectangle -RLAPI void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, Color tint); // Draw a part of a texture defined by a rectangle with 'pro' parameters -RLAPI void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest, Vector2 origin, float rotation, Color tint); // Draws a texture (or part of it) that stretches or shrinks nicely - -// Color/pixel related functions -RLAPI Color Fade(Color color, float alpha); // Get color with alpha applied, alpha goes from 0.0f to 1.0f -RLAPI int ColorToInt(Color color); // Get hexadecimal value for a Color -RLAPI Vector4 ColorNormalize(Color color); // Get Color normalized as float [0..1] -RLAPI Color ColorFromNormalized(Vector4 normalized); // Get Color from normalized values [0..1] -RLAPI Vector3 ColorToHSV(Color color); // Get HSV values for a Color, hue [0..360], saturation/value [0..1] -RLAPI Color ColorFromHSV(float hue, float saturation, float value); // Get a Color from HSV values, hue [0..360], saturation/value [0..1] -RLAPI Color ColorTint(Color color, Color tint); // Get color multiplied with another color -RLAPI Color ColorBrightness(Color color, float factor); // Get color with brightness correction, brightness factor goes from -1.0f to 1.0f -RLAPI Color ColorContrast(Color color, float contrast); // Get color with contrast correction, contrast values between -1.0f and 1.0f -RLAPI Color ColorAlpha(Color color, float alpha); // Get color with alpha applied, alpha goes from 0.0f to 1.0f -RLAPI Color ColorAlphaBlend(Color dst, Color src, Color tint); // Get src alpha-blended into dst color with tint -RLAPI Color GetColor(unsigned int hexValue); // Get Color structure from hexadecimal value -RLAPI Color GetPixelColor(void *srcPtr, int format); // Get Color from a source pixel pointer of certain format -RLAPI void SetPixelColor(void *dstPtr, Color color, int format); // Set color formatted into destination pixel pointer -RLAPI int GetPixelDataSize(int width, int height, int format); // Get pixel data size in bytes for certain format - -//------------------------------------------------------------------------------------ -// Font Loading and Text Drawing Functions (Module: text) -//------------------------------------------------------------------------------------ - -// Font loading/unloading functions -RLAPI Font GetFontDefault(void); // Get the default Font -RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM) -RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character set -RLAPI Font LoadFontFromImage(Image image, Color key, int firstChar); // Load font from Image (XNA style) -RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' -RLAPI bool IsFontReady(Font font); // Check if a font is ready -RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount, int type); // Load font data for further use -RLAPI Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info -RLAPI void UnloadFontData(GlyphInfo *glyphs, int glyphCount); // Unload font chars info data (RAM) -RLAPI void UnloadFont(Font font); // Unload font from GPU memory (VRAM) -RLAPI bool ExportFontAsCode(Font font, const char *fileName); // Export font as code file, returns true on success - -// Text drawing functions -RLAPI void DrawFPS(int posX, int posY); // Draw current FPS -RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) -RLAPI void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters -RLAPI void DrawTextPro(Font font, const char *text, Vector2 position, Vector2 origin, float rotation, float fontSize, float spacing, Color tint); // Draw text using Font and pro parameters (rotation) -RLAPI void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint); // Draw one character (codepoint) -RLAPI void DrawTextCodepoints(Font font, const int *codepoints, int codepointCount, Vector2 position, float fontSize, float spacing, Color tint); // Draw multiple character (codepoint) - -// Text font info functions -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 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 - -// Text codepoints management functions (unicode characters) -RLAPI char *LoadUTF8(const int *codepoints, int length); // Load UTF-8 text encoded from codepoints array -RLAPI void UnloadUTF8(char *text); // Unload UTF-8 text encoded from codepoints array -RLAPI int *LoadCodepoints(const char *text, int *count); // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter -RLAPI void UnloadCodepoints(int *codepoints); // Unload codepoints data from memory -RLAPI int GetCodepointCount(const char *text); // Get total number of codepoints in a UTF-8 encoded string -RLAPI int GetCodepoint(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure -RLAPI int GetCodepointNext(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure -RLAPI int GetCodepointPrevious(const char *text, int *codepointSize); // Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure -RLAPI const char *CodepointToUTF8(int codepoint, int *utf8Size); // Encode one codepoint into UTF-8 byte array (array length returned as parameter) - -// Text strings management functions (no UTF-8 strings, only byte chars) -// NOTE: Some strings allocate memory internally for returned strings, just be careful! -RLAPI int TextCopy(char *dst, const char *src); // Copy one string to another, returns bytes copied -RLAPI bool TextIsEqual(const char *text1, const char *text2); // Check if two text string are equal -RLAPI unsigned int TextLength(const char *text); // Get text length, checks for '\0' ending -RLAPI const char *TextFormat(const char *text, ...); // Text formatting with variables (sprintf() style) -RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string -RLAPI char *TextReplace(char *text, const char *replace, const char *by); // Replace text string (WARNING: memory must be freed!) -RLAPI char *TextInsert(const char *text, const char *insert, int position); // Insert text in a position (WARNING: memory must be freed!) -RLAPI const char *TextJoin(const char **textList, int count, const char *delimiter); // Join text strings with delimiter -RLAPI const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings -RLAPI void TextAppend(char *text, const char *append, int *position); // Append text at specific position and move cursor! -RLAPI int TextFindIndex(const char *text, const char *find); // Find first text occurrence within a string -RLAPI const char *TextToUpper(const char *text); // Get upper case version of provided string -RLAPI const char *TextToLower(const char *text); // Get lower case version of provided string -RLAPI const char *TextToPascal(const char *text); // Get Pascal case notation version of provided string -RLAPI int TextToInteger(const char *text); // Get integer value from text (negative values not supported) - -//------------------------------------------------------------------------------------ -// Basic 3d Shapes Drawing Functions (Module: models) -//------------------------------------------------------------------------------------ - -// Basic geometric 3D shapes drawing functions -RLAPI void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color); // Draw a line in 3D world space -RLAPI void DrawPoint3D(Vector3 position, Color color); // Draw a point in 3D space, actually a small line -RLAPI void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rotationAngle, Color color); // Draw a circle in 3D world space -RLAPI void DrawTriangle3D(Vector3 v1, Vector3 v2, Vector3 v3, Color color); // Draw a color-filled triangle (vertex in counter-clockwise order!) -RLAPI void DrawTriangleStrip3D(Vector3 *points, int pointCount, Color color); // Draw a triangle strip defined by points -RLAPI void DrawCube(Vector3 position, float width, float height, float length, Color color); // Draw cube -RLAPI void DrawCubeV(Vector3 position, Vector3 size, Color color); // Draw cube (Vector version) -RLAPI void DrawCubeWires(Vector3 position, float width, float height, float length, Color color); // Draw cube wires -RLAPI void DrawCubeWiresV(Vector3 position, Vector3 size, Color color); // Draw cube wires (Vector version) -RLAPI void DrawSphere(Vector3 centerPos, float radius, Color color); // Draw sphere -RLAPI void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere with extended parameters -RLAPI void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere wires -RLAPI void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone -RLAPI void DrawCylinderEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color); // Draw a cylinder with base at startPos and top at endPos -RLAPI void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone wires -RLAPI void DrawCylinderWiresEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color); // Draw a cylinder wires with base at startPos and top at endPos -RLAPI void DrawCapsule(Vector3 startPos, Vector3 endPos, float radius, int slices, int rings, Color color); // Draw a capsule with the center of its sphere caps at startPos and endPos -RLAPI void DrawCapsuleWires(Vector3 startPos, Vector3 endPos, float radius, int slices, int rings, Color color); // Draw capsule wireframe with the center of its sphere caps at startPos and endPos -RLAPI void DrawPlane(Vector3 centerPos, Vector2 size, Color color); // Draw a plane XZ -RLAPI void DrawRay(Ray ray, Color color); // Draw a ray line -RLAPI void DrawGrid(int slices, float spacing); // Draw a grid (centered at (0, 0, 0)) - -//------------------------------------------------------------------------------------ -// Model 3d Loading and Drawing Functions (Module: models) -//------------------------------------------------------------------------------------ - -// Model management functions -RLAPI Model LoadModel(const char *fileName); // Load model from files (meshes and materials) -RLAPI Model LoadModelFromMesh(Mesh mesh); // Load model from generated mesh (default material) -RLAPI bool IsModelReady(Model model); // Check if a model is ready -RLAPI void UnloadModel(Model model); // Unload model (including meshes) from memory (RAM and/or VRAM) -RLAPI BoundingBox GetModelBoundingBox(Model model); // Compute model bounding box limits (considers all meshes) - -// Model drawing functions -RLAPI void DrawModel(Model model, Vector3 position, float scale, Color tint); // Draw a model (with texture if set) -RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters -RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set) -RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters -RLAPI void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires) -RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float size, Color tint); // Draw a billboard texture -RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source -RLAPI void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint); // Draw a billboard texture defined by source and rotation - -// Mesh management functions -RLAPI void UploadMesh(Mesh *mesh, bool dynamic); // Upload mesh vertex data in GPU and provide VAO/VBO ids -RLAPI void UpdateMeshBuffer(Mesh mesh, int index, const void *data, int dataSize, int offset); // Update mesh vertex data in GPU for a specific buffer index -RLAPI void UnloadMesh(Mesh mesh); // Unload mesh data from CPU and GPU -RLAPI void DrawMesh(Mesh mesh, Material material, Matrix transform); // Draw a 3d mesh with material and transform -RLAPI void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, int instances); // Draw multiple mesh instances with material and different transforms -RLAPI bool ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file, returns true on success -RLAPI BoundingBox GetMeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits -RLAPI void GenMeshTangents(Mesh *mesh); // Compute mesh tangents - -// Mesh generation functions -RLAPI Mesh GenMeshPoly(int sides, float radius); // Generate polygonal mesh -RLAPI Mesh GenMeshPlane(float width, float length, int resX, int resZ); // Generate plane mesh (with subdivisions) -RLAPI Mesh GenMeshCube(float width, float height, float length); // Generate cuboid mesh -RLAPI Mesh GenMeshSphere(float radius, int rings, int slices); // Generate sphere mesh (standard sphere) -RLAPI Mesh GenMeshHemiSphere(float radius, int rings, int slices); // Generate half-sphere mesh (no bottom cap) -RLAPI Mesh GenMeshCylinder(float radius, float height, int slices); // Generate cylinder mesh -RLAPI Mesh GenMeshCone(float radius, float height, int slices); // Generate cone/pyramid mesh -RLAPI Mesh GenMeshTorus(float radius, float size, int radSeg, int sides); // Generate torus mesh -RLAPI Mesh GenMeshKnot(float radius, float size, int radSeg, int sides); // Generate trefoil knot mesh -RLAPI Mesh GenMeshHeightmap(Image heightmap, Vector3 size); // Generate heightmap mesh from image data -RLAPI Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize); // Generate cubes-based map mesh from image data - -// Material loading/unloading functions -RLAPI Material *LoadMaterials(const char *fileName, int *materialCount); // Load materials from model file -RLAPI Material LoadMaterialDefault(void); // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) -RLAPI bool IsMaterialReady(Material material); // Check if a material is ready -RLAPI void UnloadMaterial(Material material); // Unload material from GPU memory (VRAM) -RLAPI void SetMaterialTexture(Material *material, int mapType, Texture2D texture); // Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) -RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); // Set material for a mesh - -// 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 -RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data -RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount); // Unload animation array data -RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match - -// Collision detection functions -RLAPI bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2); // Check collision between two spheres -RLAPI bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2); // Check collision between two bounding boxes -RLAPI bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius); // Check collision between box and sphere -RLAPI RayCollision GetRayCollisionSphere(Ray ray, Vector3 center, float radius); // Get collision info between ray and sphere -RLAPI RayCollision GetRayCollisionBox(Ray ray, BoundingBox box); // Get collision info between ray and box -RLAPI RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform); // Get collision info between ray and mesh -RLAPI RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3); // Get collision info between ray and triangle -RLAPI RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4); // Get collision info between ray and quad - -//------------------------------------------------------------------------------------ -// Audio Loading and Playing Functions (Module: audio) -//------------------------------------------------------------------------------------ -typedef void (*AudioCallback)(void *bufferData, unsigned int frames); - -// Audio device management functions -RLAPI void InitAudioDevice(void); // Initialize audio device and context -RLAPI void CloseAudioDevice(void); // Close the audio device and context -RLAPI bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully -RLAPI void SetMasterVolume(float volume); // Set master volume (listener) -RLAPI float GetMasterVolume(void); // Get master volume (listener) - -// Wave/Sound loading/unloading functions -RLAPI Wave LoadWave(const char *fileName); // Load wave data from file -RLAPI Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load wave from memory buffer, fileType refers to extension: i.e. '.wav' -RLAPI bool IsWaveReady(Wave wave); // Checks if wave data is ready -RLAPI Sound LoadSound(const char *fileName); // Load sound from file -RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data -RLAPI Sound LoadSoundAlias(Sound source); // Create a new sound that shares the same sample data as the source sound, does not own the sound data -RLAPI bool IsSoundReady(Sound sound); // Checks if a sound is ready -RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data -RLAPI void UnloadWave(Wave wave); // Unload wave data -RLAPI void UnloadSound(Sound sound); // Unload sound -RLAPI void UnloadSoundAlias(Sound alias); // Unload a sound alias (does not deallocate sample data) -RLAPI bool ExportWave(Wave wave, const char *fileName); // Export wave data to file, returns true on success -RLAPI bool ExportWaveAsCode(Wave wave, const char *fileName); // Export wave sample data to code (.h), returns true on success - -// Wave/Sound management functions -RLAPI void PlaySound(Sound sound); // Play a sound -RLAPI void StopSound(Sound sound); // Stop playing a sound -RLAPI void PauseSound(Sound sound); // Pause a sound -RLAPI void ResumeSound(Sound sound); // Resume a paused sound -RLAPI bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing -RLAPI void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level) -RLAPI void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level) -RLAPI void SetSoundPan(Sound sound, float pan); // Set pan for a sound (0.5 is center) -RLAPI Wave WaveCopy(Wave wave); // Copy a wave to a new wave -RLAPI void WaveCrop(Wave *wave, int initSample, int finalSample); // Crop a wave to defined samples range -RLAPI void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels); // Convert wave data to desired format -RLAPI float *LoadWaveSamples(Wave wave); // Load samples data from wave as a 32bit float data array -RLAPI void UnloadWaveSamples(float *samples); // Unload samples data loaded with LoadWaveSamples() - -// Music management functions -RLAPI Music LoadMusicStream(const char *fileName); // Load music stream from file -RLAPI Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, int dataSize); // Load music stream from data -RLAPI bool IsMusicReady(Music music); // Checks if a music stream is ready -RLAPI void UnloadMusicStream(Music music); // Unload music stream -RLAPI void PlayMusicStream(Music music); // Start music playing -RLAPI bool IsMusicStreamPlaying(Music music); // Check if music is playing -RLAPI void UpdateMusicStream(Music music); // Updates buffers for music streaming -RLAPI void StopMusicStream(Music music); // Stop music playing -RLAPI void PauseMusicStream(Music music); // Pause music playing -RLAPI void ResumeMusicStream(Music music); // Resume playing paused music -RLAPI void SeekMusicStream(Music music, float position); // Seek music to a position (in seconds) -RLAPI void SetMusicVolume(Music music, float volume); // Set volume for music (1.0 is max level) -RLAPI void SetMusicPitch(Music music, float pitch); // Set pitch for a music (1.0 is base level) -RLAPI void SetMusicPan(Music music, float pan); // Set pan for a music (0.5 is center) -RLAPI float GetMusicTimeLength(Music music); // Get music time length (in seconds) -RLAPI float GetMusicTimePlayed(Music music); // Get current music time played (in seconds) - -// AudioStream management functions -RLAPI AudioStream LoadAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels); // Load audio stream (to stream raw audio pcm data) -RLAPI bool IsAudioStreamReady(AudioStream stream); // Checks if an audio stream is ready -RLAPI void UnloadAudioStream(AudioStream stream); // Unload audio stream and free memory -RLAPI void UpdateAudioStream(AudioStream stream, const void *data, int frameCount); // Update audio stream buffers with data -RLAPI bool IsAudioStreamProcessed(AudioStream stream); // Check if any audio stream buffers requires refill -RLAPI void PlayAudioStream(AudioStream stream); // Play audio stream -RLAPI void PauseAudioStream(AudioStream stream); // Pause audio stream -RLAPI void ResumeAudioStream(AudioStream stream); // Resume audio stream -RLAPI bool IsAudioStreamPlaying(AudioStream stream); // Check if audio stream is playing -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 SetAudioStreamBufferSizeDefault(int size); // Default size for new audio streams -RLAPI void SetAudioStreamCallback(AudioStream stream, AudioCallback callback); // Audio thread callback to request new data - -RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream, receives the samples as s -RLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Detach audio stream processor from stream - -RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline, receives the samples as s -RLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline - -#if defined(__cplusplus) -} -#endif - -#endif // RAYLIB_H diff --git a/rres/rres-raylib.go b/rres/rres-raylib.go deleted file mode 100644 index 6c0d76f..0000000 --- a/rres/rres-raylib.go +++ /dev/null @@ -1,91 +0,0 @@ -package rres - -// #include -// #define RRES_RAYLIB_IMPLEMENTATION -// #define RRES_SUPPORT_COMPRESSION_LZ4 -// #define RRES_SUPPORT_ENCRYPTION_AES -// #define RRES_SUPPORT_ENCRYPTION_XCHACHA20 -// #include -// #include -// #include -import "C" -import ( - "unsafe" - - rl "github.com/gen2brain/raylib-go/raylib" -) - -// LoadDataFromResource - Load raw data from rres resource chunk -// -// NOTE: Chunk data must be provided uncompressed/unencrypted -func LoadDataFromResource(chunk ResourceChunk) []byte { - cchunk := *(*C.rresResourceChunk)(unsafe.Pointer(&chunk)) - var csize C.uint - ret := C.LoadDataFromResource(cchunk, &csize) - defer C.free(ret) - v := C.GoBytes(ret, C.int(csize)) - return v -} - -// LoadTextFromResource - Load text data from rres resource chunk -func LoadTextFromResource(chunk ResourceChunk) string { - cchunk := *(*C.rresResourceChunk)(unsafe.Pointer(&chunk)) - ret := C.LoadTextFromResource(cchunk) - defer C.free(unsafe.Pointer(ret)) - v := C.GoString(ret) - return v -} - -// LoadImageFromResource - Load Image data from rres resource chunk -func LoadImageFromResource(chunk ResourceChunk) rl.Image { - cchunk := *(*C.rresResourceChunk)(unsafe.Pointer(&chunk)) - ret := C.LoadImageFromResource(cchunk) - v := *(*rl.Image)(unsafe.Pointer(&ret)) - return v -} - -// LoadWaveFromResource - Load Wave data from rres resource chunk -func LoadWaveFromResource(chunk ResourceChunk) rl.Wave { - cchunk := *(*C.rresResourceChunk)(unsafe.Pointer(&chunk)) - ret := C.LoadWaveFromResource(cchunk) - v := *(*rl.Wave)(unsafe.Pointer(&ret)) - return v -} - -// LoadFontFromResource - Load Font data from rres resource multiple chunks -func LoadFontFromResource(multi ResourceMulti) rl.Font { - cmulti := *(*C.rresResourceMulti)(unsafe.Pointer(&multi)) - ret := C.LoadFontFromResource(cmulti) - v := *(*rl.Font)(unsafe.Pointer(&ret)) - return v -} - -// LoadMeshFromResource - Load Mesh data from rres resource multiple chunks -func LoadMeshFromResource(multi ResourceMulti) rl.Mesh { - cmulti := *(*C.rresResourceMulti)(unsafe.Pointer(&multi)) - ret := C.LoadMeshFromResource(cmulti) - v := *(*rl.Mesh)(unsafe.Pointer(&ret)) - return v -} - -// UnpackResourceChunk - Unpack resource chunk data (decompres/decrypt data) -// -// NOTE: Function return 0 on success or other value on failure -func UnpackResourceChunk(chunk *ResourceChunk) ErrorType { - cchunk := (*C.rresResourceChunk)(unsafe.Pointer(chunk)) - ret := C.UnpackResourceChunk(cchunk) - v := ErrorType(ret) - return v -} - -// SetBaseDirectory - Set base directory for externally linked data -// -// NOTE: When resource chunk contains an external link (FourCC: LINK, Type: RRES_DATA_LINK), -// a base directory is required to be prepended to link path -// -// If not provided, the application path is prepended to link by default -func SetBaseDirectory(baseDir string) { - cbaseDir := C.CString(baseDir) - defer C.free(unsafe.Pointer(cbaseDir)) - C.SetBaseDirectory(cbaseDir) -} diff --git a/rres/rres-raylib.h b/rres/rres-raylib.h deleted file mode 100644 index 71d6f2a..0000000 --- a/rres/rres-raylib.h +++ /dev/null @@ -1,1094 +0,0 @@ -/********************************************************************************************** -* -* rres-raylib v1.2 - rres loaders specific for raylib data structures -* -* CONFIGURATION: -* -* #define RRES_RAYLIB_IMPLEMENTATION -* Generates the implementation of the library into the included file. -* 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 RRES_SUPPORT_COMPRESSION_LZ4 -* Support data compression algorithm LZ4, provided by lz4.h/lz4.c library -* -* #define RRES_SUPPORT_ENCRYPTION_AES -* Support data encryption algorithm AES, provided by aes.h/aes.c library -* -* #define RRES_SUPPORT_ENCRYPTION_XCHACHA20 -* Support data encryption algorithm XChaCha20-Poly1305, -* provided by monocypher.h/monocypher.c library -* -* DEPENDENCIES: -* -* - raylib.h: Data types definition and data loading from memory functions -* WARNING: raylib.h MUST be included before including rres-raylib.h -* - rres.h: Base implementation of rres specs, required to read rres files and resource chunks -* - lz4.h: LZ4 compression support (optional) -* - aes.h: AES-256 CTR encryption support (optional) -* - monocypher.h: for XChaCha20-Poly1305 encryption support (optional) -* -* VERSION HISTORY: -* -* - 1.2 (15-Apr-2023): Updated to monocypher 4.0.1 -* - 1.0 (11-May-2022): Initial implementation release -* -* -* LICENSE: MIT -* -* Copyright (c) 2020-2023 Ramon Santamaria (@raysan5) -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -* -**********************************************************************************************/ - -#ifndef RRES_RAYLIB_H -#define RRES_RAYLIB_H - -#ifndef RRES_H - #include "rres.h" -#endif - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -//... - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -//... - -//---------------------------------------------------------------------------------- -// Global variables -//---------------------------------------------------------------------------------- -//... - -//---------------------------------------------------------------------------------- -// Module Functions Declaration -//---------------------------------------------------------------------------------- -#if defined(__cplusplus) -extern "C" { // Prevents name mangling of functions -#endif - -// rres data loading to raylib data structures -// NOTE: Chunk data must be provided uncompressed/unencrypted -RLAPI void *LoadDataFromResource(rresResourceChunk chunk, unsigned int *size); // Load raw data from rres resource chunk -RLAPI char *LoadTextFromResource(rresResourceChunk chunk); // Load text data from rres resource chunk -RLAPI Image LoadImageFromResource(rresResourceChunk chunk); // Load Image data from rres resource chunk -RLAPI Wave LoadWaveFromResource(rresResourceChunk chunk); // Load Wave data from rres resource chunk -RLAPI Font LoadFontFromResource(rresResourceMulti multi); // Load Font data from rres resource multiple chunks -RLAPI Mesh LoadMeshFromResource(rresResourceMulti multi); // Load Mesh data from rres resource multiple chunks - -// Unpack resource chunk data (decompres/decrypt data) -// NOTE: Function return 0 on success or other value on failure -RLAPI int UnpackResourceChunk(rresResourceChunk *chunk); // Unpack resource chunk data (decompress/decrypt) - -// Set base directory for externally linked data -// NOTE: When resource chunk contains an external link (FourCC: LINK, Type: RRES_DATA_LINK), -// a base directory is required to be prepended to link path -// If not provided, the application path is prepended to link by default -RLAPI void SetBaseDirectory(const char *baseDir); // Set base directory for externally linked data - -#if defined(__cplusplus) -} -#endif - -#endif // RRES_RAYLIB_H - -/*********************************************************************************** -* -* RRES RAYLIB IMPLEMENTATION -* -************************************************************************************/ - -#if defined(RRES_RAYLIB_IMPLEMENTATION) - -// Compression/Encryption algorithms supported -// NOTE: They should be the same supported by the rres packaging tool (rrespacker) -// https://github.com/phoboslab/qoi -#include "external/qoi.h" // Compression algorithm: QOI (implementation in raylib) - -#if defined(RRES_SUPPORT_COMPRESSION_LZ4) - // https://github.com/lz4/lz4 - #include "external/lz4.h" // Compression algorithm: LZ4 - #include "external/lz4.c" // Compression algorithm implementation: LZ4 -#endif -#if defined(RRES_SUPPORT_ENCRYPTION_AES) - // https://github.com/kokke/tiny-AES-c - #include "external/aes.h" // Encryption algorithm: AES - #include "external/aes.c" // Encryption algorithm implementation: AES -#endif -#if defined(RRES_SUPPORT_ENCRYPTION_XCHACHA20) - // https://github.com/LoupVaillant/Monocypher - #include "external/monocypher.h" // Encryption algorithm: XChaCha20-Poly1305 - #include "external/monocypher.c" // Encryption algorithm implementation: XChaCha20-Poly1305 -#endif - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -//... - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -//... - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- -static const char *baseDir = NULL; // Base directory pointer, used on external linked data loading - -//---------------------------------------------------------------------------------- -// Module specific Functions Declaration -//---------------------------------------------------------------------------------- - -// Load simple data chunks that are later required by multi-chunk resources -// NOTE: Chunk data must be provided uncompressed/unencrypted -static void *LoadDataFromResourceLink(rresResourceChunk chunk, unsigned int *size); // Load chunk: RRES_DATA_LINK -static void *LoadDataFromResourceChunk(rresResourceChunk chunk, unsigned int *size); // Load chunk: RRES_DATA_RAW -static char *LoadTextFromResourceChunk(rresResourceChunk chunk, unsigned int *codeLang); // Load chunk: RRES_DATA_TEXT -static Image LoadImageFromResourceChunk(rresResourceChunk chunk); // Load chunk: RRES_DATA_IMAGE - -static const char *GetExtensionFromProps(unsigned int ext01, unsigned int ext02); // Get file extension from RRES_DATA_RAW properties (unsigned int) -static unsigned int *ComputeMD5(unsigned char *data, int size); // Compute MD5 hash code, returns 4 integers array (static) - -//---------------------------------------------------------------------------------- -// Module Functions Definition -//---------------------------------------------------------------------------------- - -// Load raw data from rres resource -void *LoadDataFromResource(rresResourceChunk chunk, unsigned int *size) -{ - void *rawData = NULL; - - // Data can be provided in the resource or linked to an external file - if (rresGetDataType(chunk.info.type) == RRES_DATA_RAW) // Raw data - { - rawData = LoadDataFromResourceChunk(chunk, size); - } - else if (rresGetDataType(chunk.info.type) == RRES_DATA_LINK) // Link to external file - { - // Get raw data from external linked file - unsigned int dataSize = 0; - void *data = LoadDataFromResourceLink(chunk, &dataSize); - - rawData = data; - *size = dataSize; - } - - return rawData; -} - -// Load text data from rres resource -// NOTE: Text must be NULL terminated -char *LoadTextFromResource(rresResourceChunk chunk) -{ - char *text = NULL; - int codeLang = 0; - - if (rresGetDataType(chunk.info.type) == RRES_DATA_TEXT) // Text data - { - text = LoadTextFromResourceChunk(chunk, &codeLang); - - // TODO: Consider text code language to load shader or code scripts - } - else if (rresGetDataType(chunk.info.type) == RRES_DATA_RAW) // Raw text file - { - unsigned int size = 0; - text = LoadDataFromResourceChunk(chunk, &size); - } - else if (rresGetDataType(chunk.info.type) == RRES_DATA_LINK) // Link to external file - { - // Get raw data from external linked file - unsigned int dataSize = 0; - void *data = LoadDataFromResourceLink(chunk, &dataSize); - text = data; - } - - return text; -} - -// Load Image data from rres resource -Image LoadImageFromResource(rresResourceChunk chunk) -{ - Image image = { 0 }; - - if (rresGetDataType(chunk.info.type) == RRES_DATA_IMAGE) // Image data - { - image = LoadImageFromResourceChunk(chunk); - } - else if (rresGetDataType(chunk.info.type) == RRES_DATA_RAW) // Raw image file - { - unsigned int dataSize = 0; - unsigned char *data = LoadDataFromResourceChunk(chunk, &dataSize); - - image = LoadImageFromMemory(GetExtensionFromProps(chunk.data.props[1], chunk.data.props[2]), data, dataSize); - - RL_FREE(data); - } - else if (rresGetDataType(chunk.info.type) == RRES_DATA_LINK) // Link to external file - { - // Get raw data from external linked file - unsigned int dataSize = 0; - void *data = LoadDataFromResourceLink(chunk, &dataSize); - - // Load image from linked file data - // NOTE: Function checks internally if the file extension is supported to - // properly load the data, if it fails it logs the result and image.data = NULL - image = LoadImageFromMemory(GetFileExtension(chunk.data.raw), data, dataSize); - } - - return image; -} - -// Load Wave data from rres resource -Wave LoadWaveFromResource(rresResourceChunk chunk) -{ - Wave wave = { 0 }; - - if (rresGetDataType(chunk.info.type) == RRES_DATA_WAVE) // Wave data - { - if ((chunk.info.compType == RRES_COMP_NONE) && (chunk.info.cipherType == RRES_CIPHER_NONE)) - { - wave.frameCount = chunk.data.props[0]; - wave.sampleRate = chunk.data.props[1]; - wave.sampleSize = chunk.data.props[2]; - wave.channels = chunk.data.props[3]; - - unsigned int size = wave.frameCount*wave.sampleSize/8; - wave.data = RL_CALLOC(size, 1); - memcpy(wave.data, chunk.data.raw, size); - } - RRES_LOG("RRES: %c%c%c%c: WARNING: Data must be decompressed/decrypted\n", chunk.info.type[0], chunk.info.type[1], chunk.info.type[2], chunk.info.type[3]); - } - else if (rresGetDataType(chunk.info.type) == RRES_DATA_RAW) // Raw wave file - { - unsigned int dataSize = 0; - unsigned char *data = LoadDataFromResourceChunk(chunk, &dataSize); - - wave = LoadWaveFromMemory(GetExtensionFromProps(chunk.data.props[1], chunk.data.props[2]), data, dataSize); - - RL_FREE(data); - } - else if (rresGetDataType(chunk.info.type) == RRES_DATA_LINK) // Link to external file - { - // Get raw data from external linked file - unsigned int dataSize = 0; - void *data = LoadDataFromResourceLink(chunk, &dataSize); - - // Load wave from linked file data - // NOTE: Function checks internally if the file extension is supported to - // properly load the data, if it fails it logs the result and wave.data = NULL - wave = LoadWaveFromMemory(GetFileExtension(chunk.data.raw), data, dataSize); - } - - return wave; -} - -// Load Font data from rres resource -Font LoadFontFromResource(rresResourceMulti multi) -{ - Font font = { 0 }; - - // Font resource consist of (2) chunks: - // - RRES_DATA_FONT_GLYPHS: Basic font and glyphs properties/data - // - RRES_DATA_IMAGE: Image atlas for the font characters - if (multi.count >= 2) - { - if (rresGetDataType(multi.chunks[0].info.type) == RRES_DATA_FONT_GLYPHS) - { - if ((multi.chunks[0].info.compType == RRES_COMP_NONE) && (multi.chunks[0].info.cipherType == RRES_CIPHER_NONE)) - { - // Load font basic properties from chunk[0] - font.baseSize = multi.chunks[0].data.props[0]; // Base size (default chars height) - font.glyphCount = multi.chunks[0].data.props[1]; // Number of characters (glyphs) - font.glyphPadding = multi.chunks[0].data.props[2]; // Padding around the chars - - font.recs = (Rectangle *)RL_CALLOC(font.glyphCount, sizeof(Rectangle)); - font.glyphs = (GlyphInfo *)RL_CALLOC(font.glyphCount, sizeof(GlyphInfo)); - - for (int i = 0; i < font.glyphCount; i++) - { - // Font glyphs info comes as a data blob - font.recs[i].x = (float)((rresFontGlyphInfo *)multi.chunks[0].data.raw)[i].x; - font.recs[i].y = (float)((rresFontGlyphInfo *)multi.chunks[0].data.raw)[i].y; - font.recs[i].width = (float)((rresFontGlyphInfo *)multi.chunks[0].data.raw)[i].width; - font.recs[i].height = (float)((rresFontGlyphInfo *)multi.chunks[0].data.raw)[i].height; - - font.glyphs[i].value = ((rresFontGlyphInfo *)multi.chunks[0].data.raw)[i].value; - font.glyphs[i].offsetX = ((rresFontGlyphInfo *)multi.chunks[0].data.raw)[i].offsetX; - font.glyphs[i].offsetY = ((rresFontGlyphInfo *)multi.chunks[0].data.raw)[i].offsetY; - font.glyphs[i].advanceX = ((rresFontGlyphInfo *)multi.chunks[0].data.raw)[i].advanceX; - - // NOTE: font.glyphs[i].image is not loaded - } - } - else RRES_LOG("RRES: %s: WARNING: Data must be decompressed/decrypted\n", multi.chunks[0].info.type); - } - - // Load font image chunk - if (rresGetDataType(multi.chunks[1].info.type) == RRES_DATA_IMAGE) - { - if ((multi.chunks[0].info.compType == RRES_COMP_NONE) && (multi.chunks[0].info.cipherType == RRES_CIPHER_NONE)) - { - Image image = LoadImageFromResourceChunk(multi.chunks[1]); - font.texture = LoadTextureFromImage(image); - UnloadImage(image); - } - else RRES_LOG("RRES: %s: WARNING: Data must be decompressed/decrypted\n", multi.chunks[1].info.type); - } - } - else // One chunk of data: RRES_DATA_RAW or RRES_DATA_LINK? - { - if (rresGetDataType(multi.chunks[0].info.type) == RRES_DATA_RAW) // Raw font file - { - unsigned int dataSize = 0; - unsigned char *rawData = LoadDataFromResourceChunk(multi.chunks[0], &dataSize); - - font = LoadFontFromMemory(GetExtensionFromProps(multi.chunks[0].data.props[1], multi.chunks[0].data.props[2]), rawData, dataSize, 32, NULL, 0); - - RL_FREE(rawData); - } - if (rresGetDataType(multi.chunks[0].info.type) == RRES_DATA_LINK) // Link to external font file - { - // Get raw data from external linked file - unsigned int dataSize = 0; - void *rawData = LoadDataFromResourceLink(multi.chunks[0], &dataSize); - - // Load image from linked file data - // NOTE 1: Loading font at 32px base size and default charset (95 glyphs) - // NOTE 2: Function checks internally if the file extension is supported to - // properly load the data, if it fails it logs the result and font.texture.id = 0 - font = LoadFontFromMemory(GetFileExtension(multi.chunks[0].data.raw), rawData, dataSize, 32, NULL, 0); - - RRES_FREE(rawData); - } - } - - return font; -} - -// Load Mesh data from rres resource -// NOTE: We try to load vertex data following raylib structure constraints, -// in case data does not fit raylib Mesh structure, it is not loaded -Mesh LoadMeshFromResource(rresResourceMulti multi) -{ - Mesh mesh = { 0 }; - - // TODO: Support externally linked mesh resource? - - // Mesh resource consist of (n) chunks: - for (unsigned int i = 0; i < multi.count; i++) - { - if ((multi.chunks[0].info.compType == RRES_COMP_NONE) && (multi.chunks[0].info.cipherType == RRES_CIPHER_NONE)) - { - // NOTE: raylib only supports vertex arrays with same vertex count, - // rres.chunks[0] defined vertexCount will be the reference for the following chunks - // The only exception to vertexCount is the mesh.indices array - if (mesh.vertexCount == 0) mesh.vertexCount = multi.chunks[0].data.props[0]; - - // Verify chunk type and vertex count - if (rresGetDataType(multi.chunks[i].info.type) == RRES_DATA_VERTEX) - { - // In case vertex count do not match we skip that resource chunk - if ((multi.chunks[i].data.props[1] != RRES_VERTEX_ATTRIBUTE_INDEX) && (multi.chunks[i].data.props[0] != mesh.vertexCount)) continue; - - // NOTE: We are only loading raylib supported rresVertexFormat and raylib expected components count - switch (multi.chunks[i].data.props[1]) // Check rresVertexAttribute value - { - case RRES_VERTEX_ATTRIBUTE_POSITION: - { - // raylib expects 3 components per vertex and float vertex format - if ((multi.chunks[i].data.props[2] == 3) && (multi.chunks[i].data.props[3] == RRES_VERTEX_FORMAT_FLOAT)) - { - mesh.vertices = (float *)RL_CALLOC(mesh.vertexCount*3, sizeof(float)); - memcpy(mesh.vertices, multi.chunks[i].data.raw, mesh.vertexCount*3*sizeof(float)); - } - else RRES_LOG("RRES: WARNING: MESH: Vertex attribute position not valid, componentCount/vertexFormat do not fit\n"); - - } break; - case RRES_VERTEX_ATTRIBUTE_TEXCOORD1: - { - // raylib expects 2 components per vertex and float vertex format - if ((multi.chunks[i].data.props[2] == 2) && (multi.chunks[i].data.props[3] == RRES_VERTEX_FORMAT_FLOAT)) - { - mesh.texcoords = (float *)RL_CALLOC(mesh.vertexCount*2, sizeof(float)); - memcpy(mesh.texcoords, multi.chunks[i].data.raw, mesh.vertexCount*2*sizeof(float)); - } - else RRES_LOG("RRES: WARNING: MESH: Vertex attribute texcoord1 not valid, componentCount/vertexFormat do not fit\n"); - - } break; - case RRES_VERTEX_ATTRIBUTE_TEXCOORD2: - { - // raylib expects 2 components per vertex and float vertex format - if ((multi.chunks[i].data.props[2] == 2) && (multi.chunks[i].data.props[3] == RRES_VERTEX_FORMAT_FLOAT)) - { - mesh.texcoords2 = (float *)RL_CALLOC(mesh.vertexCount*2, sizeof(float)); - memcpy(mesh.texcoords2, multi.chunks[i].data.raw, mesh.vertexCount*2*sizeof(float)); - } - else RRES_LOG("RRES: WARNING: MESH: Vertex attribute texcoord2 not valid, componentCount/vertexFormat do not fit\n"); - - } break; - case RRES_VERTEX_ATTRIBUTE_TEXCOORD3: - { - RRES_LOG("RRES: WARNING: MESH: Vertex attribute texcoord3 not supported\n"); - - } break; - case RRES_VERTEX_ATTRIBUTE_TEXCOORD4: - { - RRES_LOG("RRES: WARNING: MESH: Vertex attribute texcoord4 not supported\n"); - - } break; - case RRES_VERTEX_ATTRIBUTE_NORMAL: - { - // raylib expects 3 components per vertex and float vertex format - if ((multi.chunks[i].data.props[2] == 3) && (multi.chunks[i].data.props[3] == RRES_VERTEX_FORMAT_FLOAT)) - { - mesh.normals = (float *)RL_CALLOC(mesh.vertexCount*3, sizeof(float)); - memcpy(mesh.normals, multi.chunks[i].data.raw, mesh.vertexCount*3*sizeof(float)); - } - else RRES_LOG("RRES: WARNING: MESH: Vertex attribute normal not valid, componentCount/vertexFormat do not fit\n"); - - } break; - case RRES_VERTEX_ATTRIBUTE_TANGENT: - { - // raylib expects 4 components per vertex and float vertex format - if ((multi.chunks[i].data.props[2] == 4) && (multi.chunks[i].data.props[3] == RRES_VERTEX_FORMAT_FLOAT)) - { - mesh.tangents = (float *)RL_CALLOC(mesh.vertexCount*4, sizeof(float)); - memcpy(mesh.tangents, multi.chunks[i].data.raw, mesh.vertexCount*4*sizeof(float)); - } - else RRES_LOG("RRES: WARNING: MESH: Vertex attribute tangent not valid, componentCount/vertexFormat do not fit\n"); - - } break; - case RRES_VERTEX_ATTRIBUTE_COLOR: - { - // raylib expects 4 components per vertex and unsigned char vertex format - if ((multi.chunks[i].data.props[2] == 4) && (multi.chunks[i].data.props[3] == RRES_VERTEX_FORMAT_UBYTE)) - { - mesh.colors = (unsigned char *)RL_CALLOC(mesh.vertexCount*4, sizeof(unsigned char)); - memcpy(mesh.colors, multi.chunks[i].data.raw, mesh.vertexCount*4*sizeof(unsigned char)); - } - else RRES_LOG("RRES: WARNING: MESH: Vertex attribute color not valid, componentCount/vertexFormat do not fit\n"); - - } break; - case RRES_VERTEX_ATTRIBUTE_INDEX: - { - // raylib expects 1 components per index and unsigned short vertex format - if ((multi.chunks[i].data.props[2] == 1) && (multi.chunks[i].data.props[3] == RRES_VERTEX_FORMAT_USHORT)) - { - mesh.indices = (unsigned short *)RL_CALLOC(multi.chunks[i].data.props[0], sizeof(unsigned short)); - memcpy(mesh.indices, multi.chunks[i].data.raw, multi.chunks[i].data.props[0]*sizeof(unsigned short)); - } - else RRES_LOG("RRES: WARNING: MESH: Vertex attribute index not valid, componentCount/vertexFormat do not fit\n"); - - } break; - default: break; - } - } - } - else RRES_LOG("RRES: WARNING: Vertex provided data must be decompressed/decrypted\n"); - } - - return mesh; -} - -// Unpack compressed/encrypted data from resource chunk -// In case data could not be processed by rres.h, it is just copied in chunk.data.raw for processing here -// NOTE 1: Function return 0 on success or an error code on failure -// NOTE 2: Data corruption CRC32 check has already been performed by rresLoadResourceMulti() on rres.h -int UnpackResourceChunk(rresResourceChunk *chunk) -{ - int result = 0; - bool updateProps = false; - - // Result error codes: - // 0 - No error, decompression/decryption successful - // 1 - Encryption algorithm not supported - // 2 - Invalid password on decryption - // 3 - Compression algorithm not supported - // 4 - Error on data decompression - - // NOTE 1: If data is compressed/encrypted the properties are not loaded by rres.h because - // it's up to the user to process the data; *chunk must be properly updated by this function - // NOTE 2: rres-raylib should support the same algorithms and libraries used by rrespacker tool - void *unpackedData = NULL; - - // STEP 1. Data decryption - //------------------------------------------------------------------------------------- - unsigned char *decryptedData = NULL; - - switch (chunk->info.cipherType) - { - case RRES_CIPHER_NONE: decryptedData = chunk->data.raw; break; -#if defined(RRES_SUPPORT_ENCRYPTION_AES) - case RRES_CIPHER_AES: - { - // WARNING: Implementation dependant! - // rrespacker tool appends (salt[16] + MD5[16]) to encrypted data for convenience, - // Actually, chunk->info.packedSize considers those additional elements - - // Get some memory for the possible message output - decryptedData = (unsigned char *)RL_CALLOC(chunk->info.packedSize - 16 - 16, 1); - if (decryptedData != NULL) memcpy(decryptedData, chunk->data.raw, chunk->info.packedSize - 16 - 16); - - // Required variables for key stretching - uint8_t key[32] = { 0 }; // Encryption key - uint8_t salt[16] = { 0 }; // Key stretching salt - - // Retrieve salt from chunk packed data - // salt is stored at the end of packed data, before nonce and MAC: salt[16] + MD5[16] - memcpy(salt, ((unsigned char *)chunk->data.raw) + (chunk->info.packedSize - 16 - 16), 16); - - // Key stretching configuration - crypto_argon2_config config = { - .algorithm = CRYPTO_ARGON2_I, // Algorithm: Argon2i - .nb_blocks = 16384, // Blocks: 16 MB - .nb_passes = 3, // Iterations - .nb_lanes = 1 // Single-threaded - }; - crypto_argon2_inputs inputs = { - .pass = (const uint8_t *)rresGetCipherPassword(), // User password - .pass_size = strlen(rresGetCipherPassword()), // Password length - .salt = salt, // Salt for the password - .salt_size = 16 - }; - crypto_argon2_extras extras = { 0 }; // Extra parameters unused - - void *workArea = RL_MALLOC(config.nb_blocks*1024); // Key stretching work area - - // Generate strong encryption key, generated from user password using Argon2i algorithm (256 bit) - crypto_argon2(key, 32, workArea, config, inputs, extras); - - // Wipe key generation secrets, they are no longer needed - crypto_wipe(salt, 16); - RL_FREE(workArea); - - // Required variables for decryption and message authentication - unsigned int md5[4] = { 0 }; // Message Authentication Code generated on encryption - - // Retrieve MD5 from chunk packed data - // NOTE: MD5 is stored at the end of packed data, after salt: salt[16] + MD5[16] - memcpy(md5, ((unsigned char *)chunk->data.raw) + (chunk->info.packedSize - 16), 4*sizeof(unsigned int)); - - // Message decryption, requires key - struct AES_ctx ctx = { 0 }; - AES_init_ctx(&ctx, key); - AES_CTR_xcrypt_buffer(&ctx, (uint8_t *)decryptedData, chunk->info.packedSize - 16 - 16); // AES Counter mode, stream cipher - - // Verify MD5 to check if data decryption worked - unsigned int decryptMD5[4] = { 0 }; - unsigned int *md5Ptr = ComputeMD5(decryptedData, chunk->info.packedSize - 16 - 16); - for (int i = 0; i < 4; i++) decryptMD5[i] = md5Ptr[i]; - - // Wipe secrets if they are no longer needed - crypto_wipe(key, 32); - - if (memcmp(decryptMD5, md5, 4*sizeof(unsigned int)) == 0) // Decrypted successfully! - { - chunk->info.packedSize -= (16 + 16); // We remove additional data size from packed size (salt[16] + MD5[16]) - RRES_LOG("RRES: %c%c%c%c: Data decrypted successfully (AES)\n", chunk->info.type[0], chunk->info.type[1], chunk->info.type[2], chunk->info.type[3]); - } - else - { - result = 2; // Data was not decrypted as expected, wrong password or message corrupted - RRES_LOG("RRES: WARNING: %c%c%c%c: Data decryption failed, wrong password or corrupted data\n", chunk->info.type[0], chunk->info.type[1], chunk->info.type[2], chunk->info.type[3]); - } - - } break; -#endif -#if defined(RRES_SUPPORT_ENCRYPTION_XCHACHA20) - case RRES_CIPHER_XCHACHA20_POLY1305: - { - // WARNING: Implementation dependant! - // rrespacker tool appends (salt[16] + nonce[24] + MAC[16]) to encrypted data for convenience, - // Actually, chunk->info.packedSize considers those additional elements - - // Get some memory for the possible message output - decryptedData = (unsigned char *)RL_CALLOC(chunk->info.packedSize - 16 - 24 - 16, 1); - - // Required variables for key stretching - uint8_t key[32] = { 0 }; // Encryption key - uint8_t salt[16] = { 0 }; // Key stretching salt - - // Retrieve salt from chunk packed data - // salt is stored at the end of packed data, before nonce and MAC: salt[16] + nonce[24] + MAC[16] - memcpy(salt, ((unsigned char *)chunk->data.raw) + (chunk->info.packedSize - 16 - 24 - 16), 16); - - // Key stretching configuration - crypto_argon2_config config = { - .algorithm = CRYPTO_ARGON2_I, // Algorithm: Argon2i - .nb_blocks = 16384, // Blocks: 16 MB - .nb_passes = 3, // Iterations - .nb_lanes = 1 // Single-threaded - }; - crypto_argon2_inputs inputs = { - .pass = (const uint8_t *)rresGetCipherPassword(), // User password - .pass_size = strlen(rresGetCipherPassword()), // Password length - .salt = salt, // Salt for the password - .salt_size = 16 - }; - crypto_argon2_extras extras = { 0 }; // Extra parameters unused - - void *workArea = RL_MALLOC(config.nb_blocks*1024); // Key stretching work area - - // Generate strong encryption key, generated from user password using Argon2i algorithm (256 bit) - crypto_argon2(key, 32, workArea, config, inputs, extras); - - // Wipe key generation secrets, they are no longer needed - crypto_wipe(salt, 16); - RL_FREE(workArea); - - // Required variables for decryption and message authentication - uint8_t nonce[24] = { 0 }; // nonce used on encryption, unique to processed file - uint8_t mac[16] = { 0 }; // Message Authentication Code generated on encryption - - // Retrieve nonce and MAC from chunk packed data - // nonce and MAC are stored at the end of packed data, after salt: salt[16] + nonce[24] + MAC[16] - memcpy(nonce, ((unsigned char *)chunk->data.raw) + (chunk->info.packedSize - 16 - 24), 24); - memcpy(mac, ((unsigned char *)chunk->data.raw) + (chunk->info.packedSize - 16), 16); - - // Message decryption requires key, nonce and MAC - int decryptResult = crypto_aead_unlock(decryptedData, mac, key, nonce, NULL, 0, chunk->data.raw, (chunk->info.packedSize - 16 - 24 - 16)); - - // Wipe secrets if they are no longer needed - crypto_wipe(nonce, 24); - crypto_wipe(key, 32); - - if (decryptResult == 0) // Decrypted successfully! - { - chunk->info.packedSize -= (16 + 24 + 16); // We remove additional data size from packed size - RRES_LOG("RRES: %c%c%c%c: Data decrypted successfully (XChaCha20)\n", chunk->info.type[0], chunk->info.type[1], chunk->info.type[2], chunk->info.type[3]); - } - else if (decryptResult == -1) - { - result = 2; // Wrong password or message corrupted - RRES_LOG("RRES: WARNING: %c%c%c%c: Data decryption failed, wrong password or corrupted data\n", chunk->info.type[0], chunk->info.type[1], chunk->info.type[2], chunk->info.type[3]); - } - } break; -#endif - default: - { - result = 1; // Decryption algorithm not supported - RRES_LOG("RRES: WARNING: %c%c%c%c: Chunk data encryption algorithm not supported\n", chunk->info.type[0], chunk->info.type[1], chunk->info.type[2], chunk->info.type[3]); - - } break; - } - - if ((result == 0) && (chunk->info.cipherType != RRES_CIPHER_NONE)) - { - // Data is not encrypted any more, register it - chunk->info.cipherType = RRES_CIPHER_NONE; - updateProps = true; - } - - // STEP 2: Data decompression (if decryption was successful) - //------------------------------------------------------------------------------------- - unsigned char *uncompData = NULL; - - if (result == 0) - { - switch (chunk->info.compType) - { - case RRES_COMP_NONE: unpackedData = decryptedData; break; - case RRES_COMP_DEFLATE: - { - int uncompDataSize = 0; - - // TODO: WARNING: Possible issue with allocators: RL_CALLOC() vs RRES_CALLOC() - uncompData = DecompressData(decryptedData, chunk->info.packedSize, &uncompDataSize); - - if ((uncompData != NULL) && (uncompDataSize > 0)) // Decompression successful - { - unpackedData = uncompData; - chunk->info.packedSize = uncompDataSize; - RRES_LOG("RRES: %c%c%c%c: Data decompressed successfully (DEFLATE)\n", chunk->info.type[0], chunk->info.type[1], chunk->info.type[2], chunk->info.type[3]); - } - else - { - result = 4; // Decompression process failed - RRES_LOG("RRES: WARNING: %c%c%c%c: Chunk data decompression failed\n", chunk->info.type[0], chunk->info.type[1], chunk->info.type[2], chunk->info.type[3]); - } - - // Security check, uncompDataSize must match the provided chunk->baseSize - if (uncompDataSize != chunk->info.baseSize) RRES_LOG("RRES: WARNING: Decompressed data could be corrupted, unexpected size\n"); - } break; -#if defined(RRES_SUPPORT_COMPRESSION_LZ4) - case RRES_COMP_LZ4: - { - int uncompDataSize = 0; - uncompData = (unsigned char *)RRES_CALLOC(chunk->info.baseSize, 1); - uncompDataSize = LZ4_decompress_safe(decryptedData, uncompData, chunk->info.packedSize, chunk->info.baseSize); - - if ((uncompData != NULL) && (uncompDataSize > 0)) // Decompression successful - { - unpackedData = uncompData; - chunk->info.packedSize = uncompDataSize; - RRES_LOG("RRES: %c%c%c%c: Data decompressed successfully (LZ4)\n", chunk->info.type[0], chunk->info.type[1], chunk->info.type[2], chunk->info.type[3]); - } - else - { - result = 4; // Decompression process failed - RRES_LOG("RRES: WARNING: %c%c%c%c: Chunk data decompression failed\n", chunk->info.type[0], chunk->info.type[1], chunk->info.type[2], chunk->info.type[3]); - } - - // WARNING: Decompression could be successful but not the original message size returned - if (uncompDataSize != chunk->info.baseSize) RRES_LOG("RRES: WARNING: Decompressed data could be corrupted, unexpected size\n"); - } break; -#endif - case RRES_COMP_QOI: - { - int uncompDataSize = 0; - qoi_desc desc = { 0 }; - - // TODO: WARNING: Possible issue with allocators: QOI_MALLOC() vs RRES_MALLOC() - uncompData = qoi_decode(decryptedData, chunk->info.packedSize, &desc, 0); - uncompDataSize = (desc.width*desc.height*desc.channels) + 20; // Add the 20 bytes of (propCount + props[4]) - - if ((uncompData != NULL) && (uncompDataSize > 0)) // Decompression successful - { - unpackedData = uncompData; - chunk->info.packedSize = uncompDataSize; - RRES_LOG("RRES: %c%c%c%c: Data decompressed successfully (QOI)\n", chunk->info.type[0], chunk->info.type[1], chunk->info.type[2], chunk->info.type[3]); - } - else - { - result = 4; // Decompression process failed - RRES_LOG("RRES: WARNING: %c%c%c%c: Chunk data decompression failed\n", chunk->info.type[0], chunk->info.type[1], chunk->info.type[2], chunk->info.type[3]); - } - - if (uncompDataSize != chunk->info.baseSize) RRES_LOG("RRES: WARNING: Decompressed data could be corrupted, unexpected size\n"); - } break; - default: - { - result = 3; - RRES_LOG("RRES: WARNING: %c%c%c%c: Chunk data compression algorithm not supported\n", chunk->info.type[0], chunk->info.type[1], chunk->info.type[2], chunk->info.type[3]); - } break; - } - } - - if ((result == 0) && (chunk->info.compType != RRES_COMP_NONE)) - { - // Data is not encrypted any more, register it - chunk->info.compType = RRES_COMP_NONE; - updateProps = true; - } - - // Update chunk->data.propCount and chunk->data.props if required - if (updateProps && (unpackedData != NULL)) - { - // Data is decompressed/decrypted into chunk->data.raw but data.propCount and data.props[] are still empty, - // they must be filled with the just updated chunk->data.raw (that contains everything) - chunk->data.propCount = ((int *)unpackedData)[0]; - - if (chunk->data.propCount > 0) - { - chunk->data.props = (unsigned int *)RRES_CALLOC(chunk->data.propCount, sizeof(int)); - for (unsigned int i = 0; i < chunk->data.propCount; i++) chunk->data.props[i] = ((int *)unpackedData)[1 + i]; - } - - // Move chunk->data.raw pointer (chunk->data.propCount*sizeof(int)) positions - void *raw = RRES_CALLOC(chunk->info.baseSize - 20, 1); - if (raw != NULL) memcpy(raw, ((unsigned char *)unpackedData) + 20, chunk->info.baseSize - 20); - RRES_FREE(chunk->data.raw); - chunk->data.raw = raw; - RL_FREE(unpackedData); - } - - return result; -} - -//---------------------------------------------------------------------------------- -// Module specific Functions Definition -//---------------------------------------------------------------------------------- - -// Load data chunk: RRES_DATA_LINK -static void *LoadDataFromResourceLink(rresResourceChunk chunk, unsigned int *size) -{ - unsigned char fullFilePath[2048] = { 0 }; - void *data = NULL; - *size = 0; - - // Get external link filepath - unsigned char *linkFilePath = RL_CALLOC(chunk.data.props[0], 1); - if (linkFilePath != NULL) memcpy(linkFilePath, chunk.data.raw, chunk.data.props[0]); - - // Get base directory to append filepath if not provided by user - if (baseDir == NULL) baseDir = GetApplicationDirectory(); - - strcpy(fullFilePath, baseDir); - strcat(fullFilePath, linkFilePath); - - RRES_LOG("RRES: %c%c%c%c: Data file linked externally: %s\n", chunk.info.type[0], chunk.info.type[1], chunk.info.type[2], chunk.info.type[3], linkFilePath); - - if (FileExists(fullFilePath)) - { - // Load external file as raw data - // NOTE: We check if file is a text file to allow automatic line-endings processing - if (IsFileExtension(linkFilePath, ".txt;.md;.vs;.fs;.info;.c;.h;.json;.xml;.glsl")) // Text file - { - data = LoadFileText(fullFilePath); - *size = TextLength(data); - } - else data = LoadFileData(fullFilePath, size); - - if ((data != NULL) && (*size > 0)) RRES_LOG("RRES: %c%c%c%c: External linked file loaded successfully\n", chunk.info.type[0], chunk.info.type[1], chunk.info.type[2], chunk.info.type[3]); - } - else RRES_LOG("RRES: WARNING: [%s] Linked external file could not be found\n", linkFilePath); - - return data; -} - -// Load data chunk: RRES_DATA_RAW -// NOTE: This chunk can be used raw files embedding or other binary blobs -static void *LoadDataFromResourceChunk(rresResourceChunk chunk, unsigned int *size) -{ - void *rawData = NULL; - - if ((chunk.info.compType == RRES_COMP_NONE) && (chunk.info.cipherType == RRES_CIPHER_NONE)) - { - rawData = RL_CALLOC(chunk.data.props[0], 1); - if (rawData != NULL) memcpy(rawData, chunk.data.raw, chunk.data.props[0]); - *size = chunk.data.props[0]; - } - else RRES_LOG("RRES: %c%c%c%c: WARNING: Data must be decompressed/decrypted\n", chunk.info.type[0], chunk.info.type[1], chunk.info.type[2], chunk.info.type[3]); - - return rawData; -} - -// Load data chunk: RRES_DATA_TEXT -// NOTE: This chunk can be used for shaders or other text data elements (materials?) -static char *LoadTextFromResourceChunk(rresResourceChunk chunk, unsigned int *codeLang) -{ - void *text = NULL; - - if ((chunk.info.compType == RRES_COMP_NONE) && (chunk.info.cipherType == RRES_CIPHER_NONE)) - { - text = (char *)RL_CALLOC(chunk.data.props[0] + 1, 1); // We add NULL terminator, just in case - if (text != NULL) memcpy(text, chunk.data.raw, chunk.data.props[0]); - - // TODO: We got some extra text properties, in case they could be useful for users: - // chunk.props[1]:rresTextEncoding, chunk.props[2]:rresCodeLang, chunk. props[3]:cultureCode - *codeLang = chunk.data.props[2]; - //chunks.props[3]:cultureCode could be useful for localized text - } - else RRES_LOG("RRES: %c%c%c%c: WARNING: Data must be decompressed/decrypted\n", chunk.info.type[0], chunk.info.type[1], chunk.info.type[2], chunk.info.type[3]); - - return text; -} - -// Load data chunk: RRES_DATA_IMAGE -// NOTE: Many data types use images data in some way (font, material...) -static Image LoadImageFromResourceChunk(rresResourceChunk chunk) -{ - Image image = { 0 }; - - if ((chunk.info.compType == RRES_COMP_NONE) && (chunk.info.cipherType == RRES_CIPHER_NONE)) - { - image.width = chunk.data.props[0]; - image.height = chunk.data.props[1]; - int format = chunk.data.props[2]; - - // Assign equivalent pixel formats for our engine - // NOTE: In this case rresPixelFormat defined values match raylib PixelFormat values - switch (format) - { - case RRES_PIXELFORMAT_UNCOMP_GRAYSCALE: image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; break; - case RRES_PIXELFORMAT_UNCOMP_GRAY_ALPHA: image.format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA; break; - case RRES_PIXELFORMAT_UNCOMP_R5G6B5: image.format = PIXELFORMAT_UNCOMPRESSED_R5G6B5; break; - case RRES_PIXELFORMAT_UNCOMP_R8G8B8: image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8; break; - case RRES_PIXELFORMAT_UNCOMP_R5G5B5A1: image.format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1; break; - case RRES_PIXELFORMAT_UNCOMP_R4G4B4A4: image.format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4; break; - case RRES_PIXELFORMAT_UNCOMP_R8G8B8A8: image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; break; - case RRES_PIXELFORMAT_UNCOMP_R32: image.format = PIXELFORMAT_UNCOMPRESSED_R32; break; - case RRES_PIXELFORMAT_UNCOMP_R32G32B32: image.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32; break; - case RRES_PIXELFORMAT_UNCOMP_R32G32B32A32: image.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32A32; break; - case RRES_PIXELFORMAT_COMP_DXT1_RGB: image.format = PIXELFORMAT_COMPRESSED_DXT1_RGB; break; - case RRES_PIXELFORMAT_COMP_DXT1_RGBA: image.format = PIXELFORMAT_COMPRESSED_DXT1_RGBA; break; - case RRES_PIXELFORMAT_COMP_DXT3_RGBA: image.format = PIXELFORMAT_COMPRESSED_DXT3_RGBA; break; - case RRES_PIXELFORMAT_COMP_DXT5_RGBA: image.format = PIXELFORMAT_COMPRESSED_DXT5_RGBA; break; - case RRES_PIXELFORMAT_COMP_ETC1_RGB: image.format = PIXELFORMAT_COMPRESSED_ETC1_RGB; break; - case RRES_PIXELFORMAT_COMP_ETC2_RGB: image.format = PIXELFORMAT_COMPRESSED_ETC2_RGB; break; - case RRES_PIXELFORMAT_COMP_ETC2_EAC_RGBA: image.format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA; break; - case RRES_PIXELFORMAT_COMP_PVRT_RGB: image.format = PIXELFORMAT_COMPRESSED_PVRT_RGB; break; - case RRES_PIXELFORMAT_COMP_PVRT_RGBA: image.format = PIXELFORMAT_COMPRESSED_PVRT_RGBA; break; - case RRES_PIXELFORMAT_COMP_ASTC_4x4_RGBA: image.format = PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA; break; - case RRES_PIXELFORMAT_COMP_ASTC_8x8_RGBA: image.format = PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA; break; - default: break; - } - - image.mipmaps = chunk.data.props[3]; - - // Image data size can be computed from image properties - unsigned int size = GetPixelDataSize(image.width, image.height, image.format); - - // NOTE: Computed image data must match the data size of the chunk processed (minus propCount + props[4] size) - if (size == (chunk.info.baseSize - 20)) - { - image.data = RL_CALLOC(size, 1); - if (image.data != NULL) memcpy(image.data, chunk.data.raw, size); - } - else RRES_LOG("RRES: WARNING: IMGE: Chunk data size do not match expected image data size\n"); - } - else RRES_LOG("RRES: %c%c%c%c: WARNING: Data must be decompressed/decrypted\n", chunk.info.type[0], chunk.info.type[1], chunk.info.type[2], chunk.info.type[3]); - - return image; -} - -// Get file extension from RRES_DATA_RAW properties (unsigned int) -static const char *GetExtensionFromProps(unsigned int ext01, unsigned int ext02) -{ - static char extension[8] = { 0 }; - memset(extension, 0, 8); - - // Convert file extension provided as 2 unsigned int properties, to a char[] array - // NOTE: Extension is defined as 2 unsigned int big-endian values (4 bytes each), - // starting with a dot, i.e 0x2e706e67 => ".png" - extension[0] = (unsigned char)((ext01 & 0xff000000) >> 24); - extension[1] = (unsigned char)((ext01 & 0x00ff0000) >> 16); - extension[2] = (unsigned char)((ext01 & 0x0000ff00) >> 8); - extension[3] = (unsigned char)(ext01 & 0x000000ff); - - extension[4] = (unsigned char)((ext02 & 0xff000000) >> 24); - extension[5] = (unsigned char)((ext02 & 0x00ff0000) >> 16); - extension[6] = (unsigned char)((ext02 & 0x0000ff00) >> 8); - extension[7] = (unsigned char)(ext02 & 0x000000ff); - - return extension; -} - -// Compute MD5 hash code, returns 4 integers array (static) -static unsigned int *ComputeMD5(unsigned char *data, int size) -{ -#define LEFTROTATE(x, c) (((x) << (c)) | ((x) >> (32 - (c)))) - - static unsigned int hash[4] = { 0 }; - - // NOTE: All variables are unsigned 32 bit and wrap modulo 2^32 when calculating - - // r specifies the per-round shift amounts - unsigned int r[] = { - 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, - 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, - 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, - 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 - }; - - // Use binary integer part of the sines of integers (in radians) as constants// Initialize variables: - unsigned int k[] = { - 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, - 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, - 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, - 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, - 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, - 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, - 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, - 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, - 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, - 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, - 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, - 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, - 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, - 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, - 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, - 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 - }; - - hash[0] = 0x67452301; - hash[1] = 0xefcdab89; - hash[2] = 0x98badcfe; - hash[3] = 0x10325476; - - // Pre-processing: adding a single 1 bit - // Append '1' bit to message - // NOTE: The input bytes are considered as bits strings, - // where the first bit is the most significant bit of the byte - - // Pre-processing: padding with zeros - // Append '0' bit until message length in bit 448 (mod 512) - // Append length mod (2 pow 64) to message - - int newDataSize = ((((size + 8)/64) + 1)*64) - 8; - - unsigned char *msg = RL_CALLOC(newDataSize + 64, 1); // Also appends "0" bits (we alloc also 64 extra bytes...) - memcpy(msg, data, size); - msg[size] = 128; // Write the "1" bit - - unsigned int bitsLen = 8*size; - memcpy(msg + newDataSize, &bitsLen, 4); // We append the len in bits at the end of the buffer - - // Process the message in successive 512-bit chunks for each 512-bit chunk of message - for (int offset = 0; offset < newDataSize; offset += (512/8)) - { - // Break chunk into sixteen 32-bit words w[j], 0 <= j <= 15 - unsigned int *w = (unsigned int *)(msg + offset); - - // Initialize hash value for this chunk - unsigned int a = hash[0]; - unsigned int b = hash[1]; - unsigned int c = hash[2]; - unsigned int d = hash[3]; - - for (int i = 0; i < 64; i++) - { - unsigned int f, g; - - if (i < 16) - { - f = (b & c) | ((~b) & d); - g = i; - } - else if (i < 32) - { - f = (d & b) | ((~d) & c); - g = (5*i + 1)%16; - } - else if (i < 48) - { - f = b ^ c ^ d; - g = (3*i + 5)%16; - } - else - { - f = c ^ (b | (~d)); - g = (7*i)%16; - } - - unsigned int temp = d; - d = c; - c = b; - b = b + LEFTROTATE((a + f + k[i] + w[g]), r[i]); - a = temp; - } - - // Add chunk's hash to result so far - hash[0] += a; - hash[1] += b; - hash[2] += c; - hash[3] += d; - } - - RL_FREE(msg); - - return hash; -} - -#endif // RRES_RAYLIB_IMPLEMENTATION diff --git a/rres/rres.go b/rres/rres.go deleted file mode 100644 index 477fe37..0000000 --- a/rres/rres.go +++ /dev/null @@ -1,497 +0,0 @@ -package rres - -/* -#define RRES_IMPLEMENTATION -#include -#include -#include -rresResourceChunkInfo GetResourceChunkInfoFromArray(rresResourceChunkInfo *infos, int index) -{ - return infos[index]; -} -*/ -import "C" -import ( - "hash/crc32" - "unsafe" -) - -const MaxFilenameSize = 1024 - -// FileHeader - (16 bytes) -type FileHeader struct { - Id [4]byte // File identifier: rres - Version uint16 // File version: 100 for version 1.0 - ChunkCount uint16 // Number of resource chunks in the file (MAX: 65535) - CdOffset uint32 // Central Directory offset in file (0 if not available) - Reserved uint32 // -} - -// ResourceChunkInfo - header (32 bytes) -type ResourceChunkInfo struct { - Type [4]byte // Resource chunk type (FourCC) - Id uint32 // Resource chunk identifier (generated from filename CRC32 hash) - CompType byte // Data compression algorithm - CipherType byte // Data encryption algorithm - Flags uint16 // Data flags (if required) - PackedSize uint32 // Data chunk size (compressed/encrypted + custom data appended) - BaseSize uint32 // Data base size (uncompressed/unencrypted) - NextOffset uint32 // Next resource chunk global offset (if resource has multiple chunks) - Reserved uint32 // - Crc32 uint32 // Data chunk CRC32 (propCount + props[] + data) -} - -// ResourceChunkData -type ResourceChunkData struct { - PropCount uint32 // Resource chunk properties count - Props *uint32 // Resource chunk properties - Raw unsafe.Pointer // Resource chunk raw data -} - -// ResourceChunk -type ResourceChunk struct { - Info ResourceChunkInfo // Resource chunk info - Data ResourceChunkData // Resource chunk packed data, contains propCount, props[] and raw data -} - -// ResourceMulti -// -// NOTE: It supports multiple resource chunks -type ResourceMulti struct { - Count uint32 // Resource chunks count - chunks *ResourceChunk // Resource chunks -} - -// Chunks - Resource chunks -func (r *ResourceMulti) Chunks() []ResourceChunk { - return unsafe.Slice(r.chunks, r.Count) -} - -// DirEntry - CDIR: rres central directory entry -type DirEntry struct { - Id uint32 // Resource id - Offset uint32 // Resource global offset in file - Reserved uint32 // reserved - FileNameSize uint32 // Resource fileName size (NULL terminator and 4-byte alignment padding considered) - fileName [MaxFilenameSize]int8 // Resource original fileName (NULL terminated and padded to 4-byte alignment) -} - -// FileName - Resource original fileName -func (d *DirEntry) FileName() string { - cpointer := (*C.char)(unsafe.Pointer(&d.fileName[0])) - clength := C.int(d.FileNameSize) - fileName := C.GoStringN(cpointer, clength) - return fileName -} - -// CentralDir - CDIR: rres central directory -// -// NOTE: This data conforms the ResourceChunkData -type CentralDir struct { - Count uint32 // Central directory entries count - entries *DirEntry // Central directory entries -} - -// Entries - Central directory entries -func (c *CentralDir) Entries() []DirEntry { - return unsafe.Slice(c.entries, c.Count) -} - -// FontGlyphInfo - FNTG: rres font glyphs info (32 bytes) -// -// NOTE: Array of this type conforms the ResourceChunkData -type FontGlyphInfo struct { - X, Y, Width, Height int32 // Glyph rectangle in the atlas image - Value int32 // Glyph codepoint value - OffsetX, OffsetY int32 // Glyph drawing offset (from base line) - AdvanceX int32 // Glyph advance X for next character -} - -// ResourceDataType -// -// NOTE 1: Data type determines the properties and the data included in every chunk -// -// NOTE 2: This enum defines the basic resource data types, -// some input files could generate multiple resource chunks: -// -// Fonts processed could generate (2) resource chunks: -// - [FNTG] rres[0]: RRES_DATA_FONT_GLYPHS -// - [IMGE] rres[1]: RRES_DATA_IMAGE -// -// Mesh processed could generate (n) resource chunks: -// - [VRTX] rres[0]: RRES_DATA_VERTEX -// ... -// - [VRTX] rres[n]: RRES_DATA_VERTEX -type ResourceDataType int32 - -const ( - // FourCC: NULL - Reserved for empty chunks, no props/data - DataNull ResourceDataType = iota - // FourCC: RAWD - Raw file data, 4 properties - // props[0]:size (bytes) - // props[1]:extension01 (big-endian: ".png" = 0x2e706e67) - // props[2]:extension02 (additional part, extensions with +3 letters) - // props[3]:reserved - // data: raw bytes - DataRaw - // FourCC: TEXT - Text file data, 4 properties - // props[0]:size (bytes) - // props[1]:rresTextEncoding - // props[2]:rresCodeLang - // props[3]:cultureCode - // data: text - DataText - // FourCC: IMGE - Image file data, 4 properties - // props[0]:width - // props[1]:height - // props[2]:rresPixelFormat - // props[3]:mipmaps - // data: pixels - DataImage - // FourCC: WAVE - Audio file data, 4 properties - // props[0]:frameCount - // props[1]:sampleRate - // props[2]:sampleSize - // props[3]:channels - // data: samples - DataWave - // FourCC: VRTX - Vertex file data, 4 properties - // props[0]:vertexCount - // props[1]:rresVertexAttribute - // props[2]:componentCount - // props[3]:rresVertexFormat - // data: vertex - DataVertex - // FourCC: FNTG - Font glyphs info data, 4 properties - // props[0]:baseSize - // props[1]:glyphCount - // props[2]:glyphPadding - // props[3]:rresFontStyle - // data: rresFontGlyphInfo[0..glyphCount] - DataFontGlyphs - // FourCC: LINK - External linked file, 1 property - // props[0]:size (bytes) - // data: filepath (as provided on input) - DataLink ResourceDataType = 99 - // FourCC: CDIR - Central directory for input files - // props[0]:entryCount, 1 property - // data: rresDirEntry[0..entryCount] - DataDirectory ResourceDataType = 100 -) - -// CompressionType - Compression algorithms -// -// value required by ResourceChunkInfo.CompType -// -// NOTE 1: This enum just lists some common data compression algorithms for convenience, -// The rres packer tool and the engine-specific library are responsible to implement the desired ones, -// -// NOTE 2: ResourceChunkInfo.CompType is a byte-size value, limited to [0..255] -type CompressionType int32 - -const ( - CompNone CompressionType = 0 // No data compression - CompRle CompressionType = 1 // RLE compression - CompDeflate CompressionType = 10 // DEFLATE compression - CompLz4 CompressionType = 20 // LZ4 compression - CompLzma2 CompressionType = 30 // LZMA2 compression - CompQoi CompressionType = 40 // QOI compression, useful for RGB(A) image data -) - -// EncryptionType - Encryption algorithms -// -// value required by ResourceChunkInfo.CipherType -// -// NOTE 1: This enum just lists some common data encryption algorithms for convenience, -// The rres packer tool and the engine-specific library are responsible to implement the desired ones, -// -// NOTE 2: Some encryption algorithms could require/generate additional data (seed, salt, nonce, MAC...) -// in those cases, that extra data must be appended to the original encrypted message and added to the resource data chunk -// -// NOTE 3: ResourceChunkInfo.CipherType is a byte-size value, limited to [0..255] -type EncryptionType int32 - -const ( - CipherNone EncryptionType = 0 // No data encryption - CipherXor EncryptionType = 1 // XOR encryption, generic using 128bit key in blocks - CipherDes EncryptionType = 10 // DES encryption - CipherTdes EncryptionType = 11 // Triple DES encryption - CipherIdea EncryptionType = 20 // IDEA encryption - CipherAes EncryptionType = 30 // AES (128bit or 256bit) encryption - CipherAesGCM EncryptionType = 31 // AES Galois/Counter Mode (Galois Message Authentication Code - GMAC) - CipherXtea EncryptionType = 40 // XTEA encryption - CipherBlowfish EncryptionType = 50 // BLOWFISH encryption - CipherRsa EncryptionType = 60 // RSA asymmetric encryption - CipherSalsa20 EncryptionType = 70 // SALSA20 encryption - CipherChacha20 EncryptionType = 71 // CHACHA20 encryption - CipherXchacha20 EncryptionType = 72 // XCHACHA20 encryption - CipherXchacha20Poly1305 EncryptionType = 73 // XCHACHA20 with POLY1305 for message authentication (MAC) -) - -// ErrorType - error codes -// -// NOTE: Error codes when processing rres files -type ErrorType int32 - -const ( - Success ErrorType = iota // rres file loaded/saved successfully - ErrorFileNotFound // rres file can not be opened (spelling issues, file actually does not exist...) - ErrorFileFormat // rres file format not a supported (wrong header, wrong identifier) - ErrorMemoryAlloc // Memory could not be allocated for operation. -) - -// TextEncoding - TEXT: Text encoding property values -type TextEncoding int32 - -const ( - TextEncodingUndefined TextEncoding = 0 // Not defined, usually UTF-8 - TextEncodingUtf8 TextEncoding = 1 // UTF-8 text encoding - TextEncodingUtf8Bom TextEncoding = 2 // UTF-8 text encoding with Byte-Order-Mark - TextEncodingUtf16Le TextEncoding = 10 // UTF-16 Little Endian text encoding - TextEncodingUtf16Be TextEncoding = 11 // UTF-16 Big Endian text encoding -) - -// CodeLang - TEXT: Text code language -// -// NOTE: It could be useful for code script resources -type CodeLang int32 - -const ( - CodeLangUndefined CodeLang = iota // Undefined code language, text is plain text - CodeLangC // Text contains C code - CodeLangCpp // Text contains C++ code - CodeLangCs // Text contains C# code - CodeLangLua // Text contains Lua code - CodeLangJs // Text contains JavaScript code - CodeLangPython // Text contains Python code - CodeLangRust // Text contains Rust code - CodeLangZig // Text contains Zig code - CodeLangOdin // Text contains Odin code - CodeLangJai // Text contains Jai code - CodeLangGdscript // Text contains GDScript (Godot) code - CodeLangGlsl // Text contains GLSL shader code -) - -// PixelFormat - IMGE: Image/Texture pixel formats -type PixelFormat int32 - -const ( - PixelFormatUndefined PixelFormat = iota // Undefined pixel format - PixelFormatUncompGrayscale // 8 bit per pixel (no alpha) - PixelFormatUncompGrayAlpha // 16 bpp (2 channels) - PixelFormatUncompR5g6b5 // 16 bpp - PixelFormatUncompR8g8b8 // 24 bpp - PixelFormatUncompR5g5b5a1 // 16 bpp (1 bit alpha) - PixelFormatUncompR4g4b4a4 // 16 bpp (4 bit alpha) - PixelFormatUncompR8g8b8a8 // 32 bpp - PixelFormatUncompR32 // 32 bpp (1 channel - float) - PixelFormatUncompR32g32b32 // 32*3 bpp (3 channels - float) - PixelFormatUncompR32g32b32a32 // 32*4 bpp (4 channels - float) - PixelFormatCompDxt1Rgb // 4 bpp (no alpha) - PixelFormatCompDxt1Rgba // 4 bpp (1 bit alpha) - PixelFormatCompDxt3Rgba // 8 bpp - PixelFormatCompDxt5Rgba // 8 bpp - PixelFormatCompEtc1Rgb // 4 bpp - PixelFormatCompEtc2Rgb // 4 bpp - PixelFormatCompEtc2EacRgba // 8 bpp - PixelFormatCompPvrtRgb // 4 bpp - PixelFormatCompPvrtRgba // 4 bpp - PixelFormatCompAtsc4x4Rgba // 8 bpp - PixelFormatCompAtsc8x8Rgba // 2 bpp -) - -// VertexAttribute - VRTX: Vertex data attribute -// -// NOTE: The expected number of components for every vertex attribute is provided as a property to data, -// the listed components count are the expected/default ones -type VertexAttribute int32 - -const ( - VertexAttributePosition VertexAttribute = 0 // Vertex position attribute: [x, y, z] - VertexAttributeTexcoord1 VertexAttribute = 10 // Vertex texture coordinates attribute: [u, v] - VertexAttributeTexcoord2 VertexAttribute = 11 // Vertex texture coordinates attribute: [u, v] - VertexAttributeTexcoord3 VertexAttribute = 12 // Vertex texture coordinates attribute: [u, v] - VertexAttributeTexcoord4 VertexAttribute = 13 // Vertex texture coordinates attribute: [u, v] - VertexAttributeNormal VertexAttribute = 20 // Vertex normal attribute: [x, y, z] - VertexAttributeTangent VertexAttribute = 30 // Vertex tangent attribute: [x, y, z, w] - VertexAttributeColor VertexAttribute = 40 // Vertex color attribute: [r, g, b, a] - VertexAttributeIndex VertexAttribute = 100 // Vertex index attribute: [i] -) - -// VertexFormat - VRTX: Vertex data format type -type VertexFormat int32 - -const ( - VertexFormatUbyte VertexFormat = iota // 8 bit unsigned integer data - VertexFormatByte // 8 bit signed integer data - VertexFormatUshort // 16 bit unsigned integer data - VertexFormatShort // 16 bit signed integer data - VertexFormatUint // 32 bit unsigned integer data - VertexFormatInt // 32 bit integer data - VertexFormatHfloat // 16 bit float data - VertexFormatFloat // 32 bit float data -) - -// FontStyle - FNTG: Font style -type FontStyle int32 - -const ( - FontStyleUndefined FontStyle = iota // Undefined font style - FontStyleRegular // Regular font style - FontStyleBold // Bold font style - FontStyleItalic // Italic font style -) - -// LoadResourceChunk - Load one resource chunk for provided id -func LoadResourceChunk(fileName string, rresId int32) ResourceChunk { - cfileName := C.CString(fileName) - defer C.free(unsafe.Pointer(cfileName)) - ret := C.rresLoadResourceChunk(cfileName, C.int(rresId)) - v := *(*ResourceChunk)(unsafe.Pointer(&ret)) - return v -} - -// UnloadResourceChunk - Unload resource chunk from memory -func UnloadResourceChunk(chunk *ResourceChunk) { - cchunk := *(*C.rresResourceChunk)(unsafe.Pointer(chunk)) - C.rresUnloadResourceChunk(cchunk) -} - -// LoadResourceMulti - Load resource for provided id (multiple resource chunks) -func LoadResourceMulti(fileName string, rresId int32) ResourceMulti { - cfileName := C.CString(fileName) - defer C.free(unsafe.Pointer(cfileName)) - ret := C.rresLoadResourceMulti(cfileName, C.int(rresId)) - v := *(*ResourceMulti)(unsafe.Pointer(&ret)) - return v -} - -// UnloadResourceMulti - Unload resource from memory (multiple resource chunks) -func UnloadResourceMulti(multi *ResourceMulti) { - cmulti := *(*C.rresResourceMulti)(unsafe.Pointer(multi)) - C.rresUnloadResourceMulti(cmulti) -} - -// LoadResourceChunkInfo - Load resource chunk info for provided id -func LoadResourceChunkInfo(fileName string, rresId int32) ResourceChunkInfo { - cfileName := C.CString(fileName) - defer C.free(unsafe.Pointer(cfileName)) - ret := C.rresLoadResourceChunkInfo(cfileName, C.int(rresId)) - v := *(*ResourceChunkInfo)(unsafe.Pointer(&ret)) - return v -} - -// LoadResourceChunkInfoAll - Load all resource chunks info -func LoadResourceChunkInfoAll(fileName string) []ResourceChunkInfo { - // Convert the fileName into a CString and releases the memory afterwards - cfileName := C.CString(fileName) - defer C.free(unsafe.Pointer(cfileName)) - - // The length of the resulted array is saved in the chunkCount variable - var chunkCount C.uint - cinfos := C.rresLoadResourceChunkInfoAll(cfileName, &chunkCount) - - // The C array can be released afterwards, because the values are stored in a golang slice - defer C.free(unsafe.Pointer(cinfos)) - - // Iterate over the C array and store the values in a golang slice - infos := make([]ResourceChunkInfo, chunkCount) - for i := 0; i < int(chunkCount); i++ { - // Get the C value from the C array - ret := C.GetResourceChunkInfoFromArray(cinfos, C.int(i)) - // Convert the C value into a golang value - v := *(*ResourceChunkInfo)(unsafe.Pointer(&ret)) - // Save the golang value in the golang slice - infos[i] = v - } - - return infos -} - -// LoadCentralDirectory - Load central directory resource chunk from file -func LoadCentralDirectory(fileName string) CentralDir { - cfileName := C.CString(fileName) - defer C.free(unsafe.Pointer(cfileName)) - ret := C.rresLoadCentralDirectory(cfileName) - v := *(*CentralDir)(unsafe.Pointer(&ret)) - return v -} - -// UnloadCentralDirectory - Unload central directory resource chunk -func UnloadCentralDirectory(dir *CentralDir) { - cdir := *(*C.rresCentralDir)(unsafe.Pointer(dir)) - C.rresUnloadCentralDirectory(cdir) -} - -// GetDataType - Get ResourceDataType from FourCC code -func GetDataType(fourCC [4]byte) ResourceDataType { - value := string(fourCC[:]) - switch value { - case "NULL": - return DataNull - case "RAWD": - return DataRaw - case "TEXT": - return DataText - case "IMGE": - return DataImage - case "WAVE": - return DataWave - case "VRTX": - return DataVertex - case "FNTG": - return DataFontGlyphs - case "LINK": - return DataLink - case "CDIR": - return DataDirectory - default: - return 0 - } -} - -// GetResourceId - Get resource id for a provided filename -// -// NOTE: It requires CDIR available in the file (it's optinal by design) -func GetResourceId(dir CentralDir, fileName string) int32 { - cfileName := C.CString(fileName) - defer C.free(unsafe.Pointer(cfileName)) - cdir := *(*C.rresCentralDir)(unsafe.Pointer(&dir)) - ret := C.rresGetResourceId(cdir, cfileName) - v := int32(ret) - return v -} - -// ComputeCRC32 - Compute CRC32 hash -// -// NOTE: CRC32 is used as rres id, generated from original filename -func ComputeCRC32(data []byte) uint32 { - return crc32.ChecksumIEEE(data) -} - -// SetCipherPassword - Set password to be used on data decryption -// -// NOTE: The cipher password is kept as an internal pointer to provided string, it's up to the user to manage that sensible data properly -// -// Password should be to allocate and set before loading an encrypted resource and it should be cleaned/wiped after the encrypted resource has been loaded -// -// You can use the WipeCipherPassword function to clear the password -func SetCipherPassword(pass string) { - cpass := C.CString(pass) - C.rresSetCipherPassword(cpass) -} - -// GetCipherPassword - Get password to be used on data decryption -func GetCipherPassword() string { - cpass := C.rresGetCipherPassword() - return C.GoString(cpass) -} - -// WipeCipherPassword - Clears the password from the C memory using explicit_bzero -// -// This is an approach but no guarantee -func WipeCipherPassword() { - cpass := C.rresGetCipherPassword() - C.explicit_bzero(unsafe.Pointer(cpass), C.strlen(cpass)) - C.free(unsafe.Pointer(cpass)) -} diff --git a/rres/rres.h b/rres/rres.h deleted file mode 100644 index ffd4df7..0000000 --- a/rres/rres.h +++ /dev/null @@ -1,1096 +0,0 @@ -/********************************************************************************************** -* -* rres v1.0 - A simple and easy-to-use file-format to package resources -* -* CONFIGURATION: -* -* #define RRES_IMPLEMENTATION -* Generates the implementation of the library into the included file. -* 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. -* -* FEATURES: -* -* - Multi-resource files: Some files could end-up generating multiple connected resources in -* the rres output file (i.e TTF files could generate RRES_DATA_FONT_GLYPHS and RRES_DATA_IMAGE). -* - File packaging as raw resource data: Avoid data processing and just package the file bytes. -* - Per-file data compression/encryption: Configure compression/encription for every input file. -* - Externally linked files: Package only the file path, to be loaded from external file when the -* specific id is requested. WARNING: Be careful with path, it should be relative to application dir. -* - Central Directory resource (optional): Create a central directory with the input filename relation -* to the resource(s) id. This is the default option but it can be avoided; in that case, a header -* file (.h) is generated with the file ids definitions. -* -* FILE STRUCTURE: -* -* rres files consist of a file header followed by a number of resource chunks. -* -* Optionally it can contain a Central Directory resource chunk (usually at the end) with the info -* of all the files processed into the rres file. -* -* NOTE: Chunks count could not match files count, some processed files (i.e Font, Mesh) -* could generate multiple chunks with the same id related by the rresResourceChunkInfo.nextOffset -* Those chunks are loaded together when resource is loaded -* -* rresFileHeader (16 bytes) -* Signature Id (4 bytes) // File signature id: 'rres' -* Version (2 bytes) // Format version -* Resource Count (2 bytes) // Number of resource chunks contained -* CD Offset (4 bytes) // Central Directory offset (if available) -* Reserved (4 bytes) // -* -* rresResourceChunk[] -* { -* rresResourceChunkInfo (32 bytes) -* Type (4 bytes) // Resource type (FourCC) -* Id (4 bytes) // Resource identifier (CRC32 filename hash or custom) -* Compressor (1 byte) // Data compression algorithm -* Cipher (1 byte) // Data encryption algorithm -* Flags (2 bytes) // Data flags (if required) -* Data Packed Size (4 bytes) // Data packed size (compressed/encrypted + custom data appended) -* Data Base Size (4 bytes) // Data base size (uncompressed/unencrypted) -* Next Offset (4 bytes) // Next resource chunk offset (if required) -* Reserved (4 bytes) // -* CRC32 (4 bytes) // Resource Data Chunk CRC32 -* -* rresResourceChunkData (n bytes) // Packed data -* Property Count (4 bytes) // Number of properties contained -* Properties[] (4*i bytes) // Resource data required properties, depend on Type -* Data (m bytes) // Resource data -* } -* -* rresResourceChunk: RRES_DATA_DIRECTORY // Central directory (special resource chunk) -* { -* rresResourceChunkInfo (32 bytes) -* -* rresCentralDir (n bytes) // rresResourceChunkData -* Entries Count (4 bytes) // Central directory entries count (files) -* rresDirEntry[] -* { -* Id (4 bytes) // Resource id -* Offset (4 bytes) // Resource global offset in file -* reserved (4 bytes) // -* FileName Size (4 bytes) // Resource fileName size (NULL terminator and 4-bytes align padding considered) -* FileName (m bytes) // Resource original fileName (NULL terminated and padded to 4-byte alignment) -* } -* } -* -* DESIGN DECISIONS / LIMITATIONS: -* -* - rres file maximum chunks: 65535 (16bit chunk count in rresFileHeader) -* - rres file maximum size: 4GB (chunk offset and Central Directory Offset is 32bit, so it can not address more than 4GB -* - Chunk search by ID is done one by one, starting at first chunk and accessed with fread() function -* - Endianness: rres does not care about endianness, data is stored as desired by the host platform (most probably Little Endian) -* Endianness won't affect chunk data but it will affect rresFileHeader and rresResourceChunkInfo -* - CRC32 hash is used to to generate the rres file identifier from filename -* There is a "small" probability of random collision (1 in 2^32 approx.) but considering -* the chance of collision is related to the number of data inputs, not the size of the inputs, we assume that risk -* Also note that CRC32 is not used as a security/cryptographic hash, just an identifier for the input file -* - CRC32 hash is also used to detect chunk data corruption. CRC32 is smaller and computationally much less complex than MD5 or SHA1. -* Using a hash function like MD5 is probably overkill for random error detection -* - Central Directory rresDirEntry.fileName is NULL terminated and padded to 4-byte, rresDirEntry.fileNameSize considers the padding -* - Compression and Encryption. rres supports chunks data compression and encryption, it provides two fields in the rresResourceChunkInfo to -* note it, but in those cases is up to the user to implement the desired compressor/uncompressor and encryption/decryption mechanisms -* In case of data encryption, it's recommended that any additional resource data (i.e. MAC) to be appended to data chunk and properly -* noted in the packed data size field of rresResourceChunkInfo. Data compression should be applied before encryption. -* -* DEPENDENCIES: -* -* rres library dependencies has been keep to the minimum. It depends only some libc functionality: -* -* - stdlib.h: Required for memory allocation: malloc(), calloc(), free() -* NOTE: Allocators can be redefined with macros RRES_MALLOC, RRES_CALLOC, RRES_FREE -* - stdio.h: Required for file access functionality: FILE, fopen(), fseek(), fread(), fclose() -* - string.h: Required for memory data management: memcpy(), memcmp() -* -* VERSION HISTORY: -* -* - 1.0 (12-May-2022): Implementation review for better alignment with rres specs -* - 0.9 (28-Apr-2022): Initial implementation of rres specs -* -* -* LICENSE: MIT -* -* Copyright (c) 2016-2022 Ramon Santamaria (@raysan5) -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -* -**********************************************************************************************/ - -#ifndef RRES_H -#define RRES_H - -#include - -// Function specifiers in case library is build/used as a shared library (Windows) -// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll -#if defined(_WIN32) - #if defined(BUILD_LIBTYPE_SHARED) - #define RRESAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll) - #elif defined(USE_LIBTYPE_SHARED) - #define RRESAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll) - #endif -#endif - -// Function specifiers definition -#ifndef RRESAPI - #define RRESAPI // Functions defined as 'extern' by default (implicit specifiers) -#endif - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- - -// Allow custom memory allocators -#ifndef RRES_MALLOC - #define RRES_MALLOC(sz) malloc(sz) -#endif -#ifndef RRES_CALLOC - #define RRES_CALLOC(ptr,sz) calloc(ptr,sz) -#endif -#ifndef RRES_REALLOC - #define RRES_REALLOC(ptr,sz) realloc(ptr,sz) -#endif -#ifndef RRES_FREE - #define RRES_FREE(ptr) free(ptr) -#endif - -// Simple log system to avoid printf() calls if required -// NOTE: Avoiding those calls, also avoids const strings memory usage -#define RRES_SUPPORT_LOG_INFO -#if defined(RRES_SUPPORT_LOG_INFO) - #define RRES_LOG(...) printf(__VA_ARGS__) -#else - #define RRES_LOG(...) -#endif - -// On Windows, MAX_PATH is limited to 256 by default, -// on Linux, it could go up to 4096 -#define RRES_MAX_FILENAME_SIZE 1024 - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -// rres file header (16 bytes) -typedef struct rresFileHeader { - unsigned char id[4]; // File identifier: rres - unsigned short version; // File version: 100 for version 1.0 - unsigned short chunkCount; // Number of resource chunks in the file (MAX: 65535) - unsigned int cdOffset; // Central Directory offset in file (0 if not available) - unsigned int reserved; // -} rresFileHeader; - -// rres resource chunk info header (32 bytes) -typedef struct rresResourceChunkInfo { - unsigned char type[4]; // Resource chunk type (FourCC) - unsigned int id; // Resource chunk identifier (generated from filename CRC32 hash) - unsigned char compType; // Data compression algorithm - unsigned char cipherType; // Data encription algorithm - unsigned short flags; // Data flags (if required) - unsigned int packedSize; // Data chunk size (compressed/encrypted + custom data appended) - unsigned int baseSize; // Data base size (uncompressed/unencrypted) - unsigned int nextOffset; // Next resource chunk global offset (if resource has multiple chunks) - unsigned int reserved; // - unsigned int crc32; // Data chunk CRC32 (propCount + props[] + data) -} rresResourceChunkInfo; - -// rres resource chunk data -typedef struct rresResourceChunkData { - unsigned int propCount; // Resource chunk properties count - unsigned int *props; // Resource chunk properties - void *raw; // Resource chunk raw data -} rresResourceChunkData; - -// rres resource chunk -typedef struct rresResourceChunk { - rresResourceChunkInfo info; // Resource chunk info - rresResourceChunkData data; // Resource chunk packed data, contains propCount, props[] and raw data -} rresResourceChunk; - -// rres resource multi -// NOTE: It supports multiple resource chunks -typedef struct rresResourceMulti { - unsigned int count; // Resource chunks count - rresResourceChunk *chunks; // Resource chunks -} rresResourceMulti; - -// Useful data types for specific chunk types -//---------------------------------------------------------------------- -// CDIR: rres central directory entry -typedef struct rresDirEntry { - unsigned int id; // Resource id - unsigned int offset; // Resource global offset in file - unsigned int reserved; // reserved - unsigned int fileNameSize; // Resource fileName size (NULL terminator and 4-byte alignment padding considered) - char fileName[RRES_MAX_FILENAME_SIZE]; // Resource original fileName (NULL terminated and padded to 4-byte alignment) -} rresDirEntry; - -// CDIR: rres central directory -// NOTE: This data conforms the rresResourceChunkData -typedef struct rresCentralDir { - unsigned int count; // Central directory entries count - rresDirEntry *entries; // Central directory entries -} rresCentralDir; - -// FNTG: rres font glyphs info (32 bytes) -// NOTE: And array of this type conforms the rresResourceChunkData -typedef struct rresFontGlyphInfo { - int x, y, width, height; // Glyph rectangle in the atlas image - int value; // Glyph codepoint value - int offsetX, offsetY; // Glyph drawing offset (from base line) - int advanceX; // Glyph advance X for next character -} rresFontGlyphInfo; - -//---------------------------------------------------------------------------------- -// Enums Definition -// The following enums are useful to fill some fields of the rresResourceChunkInfo -// and also some fields of the different data types properties -//---------------------------------------------------------------------------------- - -// rres resource chunk data type -// NOTE 1: Data type determines the properties and the data included in every chunk -// NOTE 2: This enum defines the basic resource data types, -// some input files could generate multiple resource chunks: -// Fonts processed could generate (2) resource chunks: -// - [FNTG] rres[0]: RRES_DATA_FONT_GLYPHS -// - [IMGE] rres[1]: RRES_DATA_IMAGE -// -// Mesh processed could generate (n) resource chunks: -// - [VRTX] rres[0]: RRES_DATA_VERTEX -// ... -// - [VRTX] rres[n]: RRES_DATA_VERTEX -typedef enum rresResourceDataType { - RRES_DATA_NULL = 0, // FourCC: NULL - Reserved for empty chunks, no props/data - RRES_DATA_RAW = 1, // FourCC: RAWD - Raw file data, 4 properties - // props[0]:size (bytes) - // props[1]:extension01 (big-endian: ".png" = 0x2e706e67) - // props[2]:extension02 (additional part, extensions with +3 letters) - // props[3]:reserved - // data: raw bytes - RRES_DATA_TEXT = 2, // FourCC: TEXT - Text file data, 4 properties - // props[0]:size (bytes) - // props[1]:rresTextEncoding - // props[2]:rresCodeLang - // props[3]:cultureCode - // data: text - RRES_DATA_IMAGE = 3, // FourCC: IMGE - Image file data, 4 properties - // props[0]:width - // props[1]:height - // props[2]:rresPixelFormat - // props[3]:mipmaps - // data: pixels - RRES_DATA_WAVE = 4, // FourCC: WAVE - Audio file data, 4 properties - // props[0]:frameCount - // props[1]:sampleRate - // props[2]:sampleSize - // props[3]:channels - // data: samples - RRES_DATA_VERTEX = 5, // FourCC: VRTX - Vertex file data, 4 properties - // props[0]:vertexCount - // props[1]:rresVertexAttribute - // props[2]:componentCount - // props[3]:rresVertexFormat - // data: vertex - RRES_DATA_FONT_GLYPHS = 6, // FourCC: FNTG - Font glyphs info data, 4 properties - // props[0]:baseSize - // props[1]:glyphCount - // props[2]:glyphPadding - // props[3]:rresFontStyle - // data: rresFontGlyphInfo[0..glyphCount] - RRES_DATA_LINK = 99, // FourCC: LINK - External linked file, 1 property - // props[0]:size (bytes) - // data: filepath (as provided on input) - RRES_DATA_DIRECTORY = 100, // FourCC: CDIR - Central directory for input files - // props[0]:entryCount, 1 property - // data: rresDirEntry[0..entryCount] - - // TODO: 2.0: Support resource package types (muti-resource) - // NOTE: They contains multiple rresResourceChunk in rresResourceData.raw - //RRES_DATA_PACK_FONT = 110, // FourCC: PFNT - Resources Pack: Font data, 1 property (2 resource chunks: RRES_DATA_GLYPHS, RRES_DATA_IMAGE) - // props[0]:chunkCount - //RRES_DATA_PACK_MESH = 120, // FourCC: PMSH - Resources Pack: Mesh data, 1 property (n resource chunks: RRES_DATA_VERTEX) - // props[0]:chunkCount - - // TODO: Add additional resource data types if required (define props + data) - -} rresResourceDataType; - -// Compression algorithms -// Value required by rresResourceChunkInfo.compType -// NOTE 1: This enum just list some common data compression algorithms for convenience, -// The rres packer tool and the engine-specific library are responsible to implement the desired ones, -// NOTE 2: rresResourceChunkInfo.compType is a byte-size value, limited to [0..255] -typedef enum rresCompressionType { - RRES_COMP_NONE = 0, // No data compression - RRES_COMP_RLE = 1, // RLE compression - RRES_COMP_DEFLATE = 10, // DEFLATE compression - RRES_COMP_LZ4 = 20, // LZ4 compression - RRES_COMP_LZMA2 = 30, // LZMA2 compression - RRES_COMP_QOI = 40, // QOI compression, useful for RGB(A) image data - // TODO: Add additional compression algorithms if required -} rresCompressionType; - -// Encryption algoritms -// Value required by rresResourceChunkInfo.cipherType -// NOTE 1: This enum just lists some common data encryption algorithms for convenience, -// The rres packer tool and the engine-specific library are responsible to implement the desired ones, -// NOTE 2: Some encryption algorithm could require/generate additional data (seed, salt, nonce, MAC...) -// in those cases, that extra data must be appended to the original encrypted message and added to the resource data chunk -// NOTE 3: rresResourceChunkInfo.cipherType is a byte-size value, limited to [0..255] -typedef enum rresEncryptionType { - RRES_CIPHER_NONE = 0, // No data encryption - RRES_CIPHER_XOR = 1, // XOR encryption, generic using 128bit key in blocks - RRES_CIPHER_DES = 10, // DES encryption - RRES_CIPHER_TDES = 11, // Triple DES encryption - RRES_CIPHER_IDEA = 20, // IDEA encryption - RRES_CIPHER_AES = 30, // AES (128bit or 256bit) encryption - RRES_CIPHER_AES_GCM = 31, // AES Galois/Counter Mode (Galois Message Authentification Code - GMAC) - RRES_CIPHER_XTEA = 40, // XTEA encryption - RRES_CIPHER_BLOWFISH = 50, // BLOWFISH encryption - RRES_CIPHER_RSA = 60, // RSA asymmetric encryption - RRES_CIPHER_SALSA20 = 70, // SALSA20 encryption - RRES_CIPHER_CHACHA20 = 71, // CHACHA20 encryption - RRES_CIPHER_XCHACHA20 = 72, // XCHACHA20 encryption - RRES_CIPHER_XCHACHA20_POLY1305 = 73, // XCHACHA20 with POLY1305 for message authentification (MAC) - // TODO: Add additional encryption algorithm if required -} rresEncryptionType; - -// TODO: rres error codes (not used at this moment) -// NOTE: Error codes when processing rres files -typedef enum rresErrorType { - RRES_SUCCESS = 0, // rres file loaded/saved successfully - RRES_ERROR_FILE_NOT_FOUND, // rres file can not be opened (spelling issues, file actually does not exist...) - RRES_ERROR_FILE_FORMAT, // rres file format not a supported (wrong header, wrong identifier) - RRES_ERROR_MEMORY_ALLOC, // Memory could not be allocated for operation. -} rresErrorType; - -// Enums required by specific resource types for its properties -//---------------------------------------------------------------------------------- -// TEXT: Text encoding property values -typedef enum rresTextEncoding { - RRES_TEXT_ENCODING_UNDEFINED = 0, // Not defined, usually UTF-8 - RRES_TEXT_ENCODING_UTF8 = 1, // UTF-8 text encoding - RRES_TEXT_ENCODING_UTF8_BOM = 2, // UTF-8 text encoding with Byte-Order-Mark - RRES_TEXT_ENCODING_UTF16_LE = 10, // UTF-16 Little Endian text encoding - RRES_TEXT_ENCODING_UTF16_BE = 11, // UTF-16 Big Endian text encoding - // TODO: Add additional encodings if required -} rresTextEncoding; - -// TEXT: Text code language -// NOTE: It could be useful for code script resources -typedef enum rresCodeLang { - RRES_CODE_LANG_UNDEFINED = 0, // Undefined code language, text is plain text - RRES_CODE_LANG_C, // Text contains C code - RRES_CODE_LANG_CPP, // Text contains C++ code - RRES_CODE_LANG_CS, // Text contains C# code - RRES_CODE_LANG_LUA, // Text contains Lua code - RRES_CODE_LANG_JS, // Text contains JavaScript code - RRES_CODE_LANG_PYTHON, // Text contains Python code - RRES_CODE_LANG_RUST, // Text contains Rust code - RRES_CODE_LANG_ZIG, // Text contains Zig code - RRES_CODE_LANG_ODIN, // Text contains Odin code - RRES_CODE_LANG_JAI, // Text contains Jai code - RRES_CODE_LANG_GDSCRIPT, // Text contains GDScript (Godot) code - RRES_CODE_LANG_GLSL, // Text contains GLSL shader code - // TODO: Add additional code languages if required -} rresCodeLang; - -// IMGE: Image/Texture pixel formats -typedef enum rresPixelFormat { - RRES_PIXELFORMAT_UNDEFINED = 0, - RRES_PIXELFORMAT_UNCOMP_GRAYSCALE = 1, // 8 bit per pixel (no alpha) - RRES_PIXELFORMAT_UNCOMP_GRAY_ALPHA, // 16 bpp (2 channels) - RRES_PIXELFORMAT_UNCOMP_R5G6B5, // 16 bpp - RRES_PIXELFORMAT_UNCOMP_R8G8B8, // 24 bpp - RRES_PIXELFORMAT_UNCOMP_R5G5B5A1, // 16 bpp (1 bit alpha) - RRES_PIXELFORMAT_UNCOMP_R4G4B4A4, // 16 bpp (4 bit alpha) - RRES_PIXELFORMAT_UNCOMP_R8G8B8A8, // 32 bpp - RRES_PIXELFORMAT_UNCOMP_R32, // 32 bpp (1 channel - float) - RRES_PIXELFORMAT_UNCOMP_R32G32B32, // 32*3 bpp (3 channels - float) - RRES_PIXELFORMAT_UNCOMP_R32G32B32A32, // 32*4 bpp (4 channels - float) - RRES_PIXELFORMAT_COMP_DXT1_RGB, // 4 bpp (no alpha) - RRES_PIXELFORMAT_COMP_DXT1_RGBA, // 4 bpp (1 bit alpha) - RRES_PIXELFORMAT_COMP_DXT3_RGBA, // 8 bpp - RRES_PIXELFORMAT_COMP_DXT5_RGBA, // 8 bpp - RRES_PIXELFORMAT_COMP_ETC1_RGB, // 4 bpp - RRES_PIXELFORMAT_COMP_ETC2_RGB, // 4 bpp - RRES_PIXELFORMAT_COMP_ETC2_EAC_RGBA, // 8 bpp - RRES_PIXELFORMAT_COMP_PVRT_RGB, // 4 bpp - RRES_PIXELFORMAT_COMP_PVRT_RGBA, // 4 bpp - RRES_PIXELFORMAT_COMP_ASTC_4x4_RGBA, // 8 bpp - RRES_PIXELFORMAT_COMP_ASTC_8x8_RGBA // 2 bpp - // TOO: Add additional pixel formats if required -} rresPixelFormat; - -// VRTX: Vertex data attribute -// NOTE: The expected number of components for every vertex attributes is provided as a property to data, -// the listed components count are the expected/default ones -typedef enum rresVertexAttribute { - RRES_VERTEX_ATTRIBUTE_POSITION = 0, // Vertex position attribute: [x, y, z] - RRES_VERTEX_ATTRIBUTE_TEXCOORD1 = 10, // Vertex texture coordinates attribute: [u, v] - RRES_VERTEX_ATTRIBUTE_TEXCOORD2 = 11, // Vertex texture coordinates attribute: [u, v] - RRES_VERTEX_ATTRIBUTE_TEXCOORD3 = 12, // Vertex texture coordinates attribute: [u, v] - RRES_VERTEX_ATTRIBUTE_TEXCOORD4 = 13, // Vertex texture coordinates attribute: [u, v] - RRES_VERTEX_ATTRIBUTE_NORMAL = 20, // Vertex normal attribute: [x, y, z] - RRES_VERTEX_ATTRIBUTE_TANGENT = 30, // Vertex tangent attribute: [x, y, z, w] - RRES_VERTEX_ATTRIBUTE_COLOR = 40, // Vertex color attribute: [r, g, b, a] - RRES_VERTEX_ATTRIBUTE_INDEX = 100, // Vertex index attribute: [i] - // TODO: Add additional attributes if required -} rresVertexAttribute; - -// VRTX: Vertex data format type -typedef enum rresVertexFormat { - RRES_VERTEX_FORMAT_UBYTE = 0, // 8 bit unsigned integer data - RRES_VERTEX_FORMAT_BYTE, // 8 bit signed integer data - RRES_VERTEX_FORMAT_USHORT, // 16 bit unsigned integer data - RRES_VERTEX_FORMAT_SHORT, // 16 bit signed integer data - RRES_VERTEX_FORMAT_UINT, // 32 bit unsigned integer data - RRES_VERTEX_FORMAT_INT, // 32 bit integer data - RRES_VERTEX_FORMAT_HFLOAT, // 16 bit float data - RRES_VERTEX_FORMAT_FLOAT, // 32 bit float data - // TODO: Add additional required vertex formats (i.e. normalized data) -} rresVertexFormat; - -// FNTG: Font style -typedef enum rresFontStyle { - RRES_FONT_STYLE_UNDEFINED = 0, // Undefined font style - RRES_FONT_STYLE_REGULAR, // Regular font style - RRES_FONT_STYLE_BOLD, // Bold font style - RRES_FONT_STYLE_ITALIC, // Italic font style - // TODO: Add additional font styles if required -} rresFontStyle; - -//---------------------------------------------------------------------------------- -// Global variables -//---------------------------------------------------------------------------------- -//... - -//---------------------------------------------------------------------------------- -// Module Functions Declaration -//---------------------------------------------------------------------------------- -#ifdef __cplusplus -extern "C" { // Prevents name mangling of functions -#endif - -// Load only one resource chunk (first resource id found) -RRESAPI rresResourceChunk rresLoadResourceChunk(const char *fileName, int rresId); // Load one resource chunk for provided id -RRESAPI void rresUnloadResourceChunk(rresResourceChunk chunk); // Unload resource chunk from memory - -// Load multi resource chunks for a specified rresId -RRESAPI rresResourceMulti rresLoadResourceMulti(const char *fileName, int rresId); // Load resource for provided id (multiple resource chunks) -RRESAPI void rresUnloadResourceMulti(rresResourceMulti multi); // Unload resource from memory (multiple resource chunks) - -// Load resource(s) chunk info from file -RRESAPI rresResourceChunkInfo rresLoadResourceChunkInfo(const char *fileName, int rresId); // Load resource chunk info for provided id -RRESAPI rresResourceChunkInfo *rresLoadResourceChunkInfoAll(const char *fileName, unsigned int *chunkCount); // Load all resource chunks info - -RRESAPI rresCentralDir rresLoadCentralDirectory(const char *fileName); // Load central directory resource chunk from file -RRESAPI void rresUnloadCentralDirectory(rresCentralDir dir); // Unload central directory resource chunk - -RRESAPI unsigned int rresGetDataType(const unsigned char *fourCC); // Get rresResourceDataType from FourCC code -RRESAPI int rresGetResourceId(rresCentralDir dir, const char *fileName); // Get resource id for a provided filename - // NOTE: It requires CDIR available in the file (it's optinal by design) -RRESAPI unsigned int rresComputeCRC32(unsigned char *data, int len); // Compute CRC32 for provided data - -// Manage password for data encryption/decryption -// NOTE: The cipher password is kept as an internal pointer to provided string, it's up to the user to manage that sensible data properly -// Password should be to allocate and set before loading an encrypted resource and it should be cleaned/wiped after the encrypted resource has been loaded -// TODO: Move this functionality to engine-library, after all rres.h does not manage data decryption -RRESAPI void rresSetCipherPassword(const char *pass); // Set password to be used on data decryption -RRESAPI const char *rresGetCipherPassword(void); // Get password to be used on data decryption - -#ifdef __cplusplus -} -#endif - -#endif // RRES_H - - -/*********************************************************************************** -* -* RRES IMPLEMENTATION -* -************************************************************************************/ - -#if defined(RRES_IMPLEMENTATION) - -// Boolean type -#if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800) - #include -#elif !defined(__cplusplus) && !defined(bool) - typedef enum bool { false = 0, true = !false } bool; - #define RL_BOOL_TYPE -#endif - -#include // Required for: malloc(), free() -#include // Required for: FILE, fopen(), fseek(), fread(), fclose() -#include // Required for: memcpy(), memcmp() - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -//... - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -//... - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- -static const char *password = NULL; // Password pointer, managed by user libraries - -//---------------------------------------------------------------------------------- -// Module Internal Functions Declaration -//---------------------------------------------------------------------------------- -// Load resource chunk packed data into our data struct -static rresResourceChunkData rresLoadResourceChunkData(rresResourceChunkInfo info, void *packedData); - -//---------------------------------------------------------------------------------- -// Module Functions Definition -//---------------------------------------------------------------------------------- -// Load one resource chunk for provided id -rresResourceChunk rresLoadResourceChunk(const char *fileName, int rresId) -{ - rresResourceChunk chunk = { 0 }; - - FILE *rresFile = fopen(fileName, "rb"); - - if (rresFile == NULL) RRES_LOG("RRES: WARNING: [%s] rres file could not be opened\n", fileName); - else - { - RRES_LOG("RRES: INFO: Loading resource from file: %s\n", fileName); - - rresFileHeader header = { 0 }; - - // Read rres file header - fread(&header, sizeof(rresFileHeader), 1, rresFile); - - // Verify file signature: "rres" and file version: 100 - if (((header.id[0] == 'r') && (header.id[1] == 'r') && (header.id[2] == 'e') && (header.id[3] == 's')) && (header.version == 100)) - { - bool found = false; - - // Check all available chunks looking for the requested id - for (int i = 0; i < header.chunkCount; i++) - { - rresResourceChunkInfo info = { 0 }; - - // Read resource info header - fread(&info, sizeof(rresResourceChunkInfo), 1, rresFile); - - // Check if resource id is the requested one - if (info.id == rresId) - { - found = true; - - RRES_LOG("RRES: INFO: Found requested resource id: 0x%08x\n", info.id); - RRES_LOG("RRES: %c%c%c%c: Id: 0x%08x | Base size: %i | Packed size: %i\n", info.type[0], info.type[1], info.type[2], info.type[3], info.id, info.baseSize, info.packedSize); - - // NOTE: We only load first matching id resource chunk found but - // we show a message if additional chunks are detected - if (info.nextOffset != 0) RRES_LOG("RRES: WARNING: Multiple linked resource chunks available for the provided id"); - - /* - // Variables required to check multiple chunks - int chunkCount = 0; - long currentFileOffset = ftell(rresFile); // Store current file position - rresResourceChunkInfo temp = info; // Temp info header to scan resource chunks - - // Count all linked resource chunks checking temp.nextOffset - while (temp.nextOffset != 0) - { - fseek(rresFile, temp.nextOffset, SEEK_SET); // Jump to next linked resource - fread(&temp, sizeof(rresResourceChunkInfo), 1, rresFile); // Read next resource info header - chunkCount++; - } - - fseek(rresFile, currentFileOffset, SEEK_SET); // Return to first resource chunk position - */ - - // Read and resource chunk from file data - // NOTE: Read data can be compressed/encrypted, it's up to the user library to manage decompression/decryption - void *data = RRES_MALLOC(info.packedSize); // Allocate enough memory to store resource data chunk - fread(data, info.packedSize, 1, rresFile); // Read data: propsCount + props[] + data (+additional_data) - - // Get chunk.data properly organized (only if uncompressed/unencrypted) - chunk.data = rresLoadResourceChunkData(info, data); - chunk.info = info; - - RRES_FREE(data); - - break; // Resource id found and loaded, stop checking the file - } - else - { - // Skip required data size to read next resource info header - fseek(rresFile, info.packedSize, SEEK_CUR); - } - } - - if (!found) RRES_LOG("RRES: WARNING: Requested resource not found: 0x%08x\n", rresId); - } - else RRES_LOG("RRES: WARNING: The provided file is not a valid rres file, file signature or version not valid\n"); - - fclose(rresFile); - } - - return chunk; -} - -// Unload resource chunk from memory -void rresUnloadResourceChunk(rresResourceChunk chunk) -{ - RRES_FREE(chunk.data.props); // Resource chunk properties - RRES_FREE(chunk.data.raw); // Resource chunk raw data -} - -// Load resource from file by id -// NOTE: All resources conected to base id are loaded -rresResourceMulti rresLoadResourceMulti(const char *fileName, int rresId) -{ - rresResourceMulti rres = { 0 }; - - FILE *rresFile = fopen(fileName, "rb"); - - if (rresFile == NULL) RRES_LOG("RRES: WARNING: [%s] rres file could not be opened\n", fileName); - else - { - rresFileHeader header = { 0 }; - - // Read rres file header - fread(&header, sizeof(rresFileHeader), 1, rresFile); - - // Verify file signature: "rres" and file version: 100 - if (((header.id[0] == 'r') && (header.id[1] == 'r') && (header.id[2] == 'e') && (header.id[3] == 's')) && (header.version == 100)) - { - bool found = false; - - // Check all available chunks looking for the requested id - for (int i = 0; i < header.chunkCount; i++) - { - rresResourceChunkInfo info = { 0 }; - - // Read resource info header - fread(&info, sizeof(rresResourceChunkInfo), 1, rresFile); - - // Check if resource id is the requested one - if (info.id == rresId) - { - found = true; - - RRES_LOG("RRES: INFO: Found requested resource id: 0x%08x\n", info.id); - RRES_LOG("RRES: %c%c%c%c: Id: 0x%08x | Base size: %i | Packed size: %i\n", info.type[0], info.type[1], info.type[2], info.type[3], info.id, info.baseSize, info.packedSize); - - rres.count = 1; - - long currentFileOffset = ftell(rresFile); // Store current file position - rresResourceChunkInfo temp = info; // Temp info header to scan resource chunks - - // Count all linked resource chunks checking temp.nextOffset - while (temp.nextOffset != 0) - { - fseek(rresFile, temp.nextOffset, SEEK_SET); // Jump to next linked resource - fread(&temp, sizeof(rresResourceChunkInfo), 1, rresFile); // Read next resource info header - rres.count++; - } - - rres.chunks = (rresResourceChunk *)RRES_CALLOC(rres.count, sizeof(rresResourceChunk)); // Load as many rres slots as required - fseek(rresFile, currentFileOffset, SEEK_SET); // Return to first resource chunk position - - // Read and load data chunk from file data - // NOTE: Read data can be compressed/encrypted, - // it's up to the user library to manage decompression/decryption - void *data = RRES_MALLOC(info.packedSize); // Allocate enough memory to store resource data chunk - fread(data, info.packedSize, 1, rresFile); // Read data: propsCount + props[] + data (+additional_data) - - // Get chunk.data properly organized (only if uncompressed/unencrypted) - rres.chunks[0].data = rresLoadResourceChunkData(info, data); - rres.chunks[0].info = info; - - RRES_FREE(data); - - int i = 1; - - // Load all linked resource chunks - while (info.nextOffset != 0) - { - fseek(rresFile, info.nextOffset, SEEK_SET); // Jump to next resource chunk - fread(&info, sizeof(rresResourceChunkInfo), 1, rresFile); // Read next resource info header - - RRES_LOG("RRES: %c%c%c%c: Id: 0x%08x | Base size: %i | Packed size: %i\n", info.type[0], info.type[1], info.type[2], info.type[3], info.id, info.baseSize, info.packedSize); - - void *data = RRES_MALLOC(info.packedSize); // Allocate enough memory to store resource data chunk - fread(data, info.packedSize, 1, rresFile); // Read data: propsCount + props[] + data (+additional_data) - - // Get chunk.data properly organized (only if uncompressed/unencrypted) - rres.chunks[i].data = rresLoadResourceChunkData(info, data); - rres.chunks[i].info = info; - - RRES_FREE(data); - - i++; - } - - break; // Resource id found and loaded, stop checking the file - } - else - { - // Skip required data size to read next resource info header - fseek(rresFile, info.packedSize, SEEK_CUR); - } - } - - if (!found) RRES_LOG("RRES: WARNING: Requested resource not found: 0x%08x\n", rresId); - } - else RRES_LOG("RRES: WARNING: The provided file is not a valid rres file, file signature or version not valid\n"); - - fclose(rresFile); - } - - return rres; -} - -// Unload resource data -void rresUnloadResourceMulti(rresResourceMulti multi) -{ - for (unsigned int i = 0; i < multi.count; i++) rresUnloadResourceChunk(multi.chunks[i]); - - RRES_FREE(multi.chunks); -} - -// Load resource chunk info for provided id -RRESAPI rresResourceChunkInfo rresLoadResourceChunkInfo(const char *fileName, int rresId) -{ - rresResourceChunkInfo info = { 0 }; - - FILE *rresFile = fopen(fileName, "rb"); - - if (rresFile != NULL) - { - rresFileHeader header = { 0 }; - - fread(&header, sizeof(rresFileHeader), 1, rresFile); - - // Verify file signature: "rres", file version: 100 - if (((header.id[0] == 'r') && (header.id[1] == 'r') && (header.id[2] == 'e') && (header.id[3] == 's')) && (header.version == 100)) - { - // Try to find provided resource chunk id and read info chunk - for (int i = 0; i < header.chunkCount; i++) - { - // Read resource chunk info - fread(&info, sizeof(rresResourceChunkInfo), 1, rresFile); - - if (info.id == rresId) - { - // TODO: Jump to next resource chunk for provided id - //if (info.nextOffset > 0) fseek(rresFile, info.nextOffset, SEEK_SET); - - break; // If requested rresId is found, we return the read rresResourceChunkInfo - } - else fseek(rresFile, info.packedSize, SEEK_CUR); // Jump to next resource - } - } - else RRES_LOG("RRES: WARNING: The provided file is not a valid rres file, file signature or version not valid\n"); - - fclose(rresFile); - } - - return info; -} - -// Load all resource chunks info -RRESAPI rresResourceChunkInfo *rresLoadResourceChunkInfoAll(const char *fileName, unsigned int *chunkCount) -{ - rresResourceChunkInfo *infos = { 0 }; - unsigned int count = 0; - - FILE *rresFile = fopen(fileName, "rb"); - - if (rresFile != NULL) - { - rresFileHeader header = { 0 }; - - fread(&header, sizeof(rresFileHeader), 1, rresFile); - - // Verify file signature: "rres", file version: 100 - if (((header.id[0] == 'r') && (header.id[1] == 'r') && (header.id[2] == 'e') && (header.id[3] == 's')) && (header.version == 100)) - { - // Load all resource chunks info - infos = (rresResourceChunkInfo *)RRES_CALLOC(header.chunkCount, sizeof(rresResourceChunkInfo)); - count = header.chunkCount; - - for (unsigned int i = 0; i < count; i++) - { - fread(&infos[i], sizeof(rresResourceChunkInfo), 1, rresFile); // Read resource chunk info - - if (infos[i].nextOffset > 0) fseek(rresFile, infos[i].nextOffset, SEEK_SET); // Jump to next resource - else fseek(rresFile, infos[i].packedSize, SEEK_CUR); // Jump to next resource - } - } - else RRES_LOG("RRES: WARNING: The provided file is not a valid rres file, file signature or version not valid\n"); - - fclose(rresFile); - } - - *chunkCount = count; - return infos; -} - -// Load central directory data -rresCentralDir rresLoadCentralDirectory(const char *fileName) -{ - rresCentralDir dir = { 0 }; - - FILE *rresFile = fopen(fileName, "rb"); - - if (rresFile != NULL) - { - rresFileHeader header = { 0 }; - - fread(&header, sizeof(rresFileHeader), 1, rresFile); - - // Verify file signature: "rres", file version: 100 - if (((header.id[0] == 'r') && (header.id[1] == 'r') && (header.id[2] == 'e') && (header.id[3] == 's')) && (header.version == 100)) - { - // Check if there is a Central Directory available - if (header.cdOffset == 0) RRES_LOG("RRES: WARNING: CDIR: No central directory found\n"); - else - { - rresResourceChunkInfo info = { 0 }; - - fseek(rresFile, header.cdOffset, SEEK_CUR); // Move to central directory position - fread(&info, sizeof(rresResourceChunkInfo), 1, rresFile); // Read resource info - - // Verify resource type is CDIR - if ((info.type[0] == 'C') && (info.type[1] == 'D') && (info.type[2] == 'I') && (info.type[3] == 'R')) - { - RRES_LOG("RRES: CDIR: Central Directory found at offset: 0x%08x\n", header.cdOffset); - - void *data = RRES_MALLOC(info.packedSize); - fread(data, info.packedSize, 1, rresFile); - - // Load resource chunk data (central directory), data is uncompressed/unencrypted by default - rresResourceChunkData chunkData = rresLoadResourceChunkData(info, data); - RRES_FREE(data); - - dir.count = chunkData.props[0]; // File entries count - - RRES_LOG("RRES: CDIR: Central Directory file entries count: %i\n", dir.count); - - unsigned char *ptr = (unsigned char *)chunkData.raw; - dir.entries = (rresDirEntry *)RRES_CALLOC(dir.count, sizeof(rresDirEntry)); - - for (unsigned int i = 0; i < dir.count; i++) - { - dir.entries[i].id = ((int *)ptr)[0]; // Resource id - dir.entries[i].offset = ((int *)ptr)[1]; // Resource offset in file - // NOTE: There is a reserved integer value before fileNameSize - dir.entries[i].fileNameSize = ((int *)ptr)[3]; // Resource fileName size - - // Resource fileName, NULL terminated and 0-padded to 4-byte, - // fileNameSize considers NULL and padding - memcpy(dir.entries[i].fileName, ptr + 16, dir.entries[i].fileNameSize); - - ptr += (16 + dir.entries[i].fileNameSize); // Move pointer for next entry - } - - RRES_FREE(chunkData.props); - RRES_FREE(chunkData.raw); - } - } - } - else RRES_LOG("RRES: WARNING: The provided file is not a valid rres file, file signature or version not valid\n"); - - fclose(rresFile); - } - - return dir; -} - -// Unload central directory data -void rresUnloadCentralDirectory(rresCentralDir dir) -{ - RRES_FREE(dir.entries); -} - -// Get rresResourceDataType from FourCC code -// NOTE: Function expects to receive a char[4] array -unsigned int rresGetDataType(const unsigned char *fourCC) -{ - unsigned int type = 0; - - if (fourCC != NULL) - { - if (memcmp(fourCC, "NULL", 4) == 0) type = RRES_DATA_NULL; // Reserved for empty chunks, no props/data - else if (memcmp(fourCC, "RAWD", 4) == 0) type = RRES_DATA_RAW; // Raw file data, input file is not processed, just packed as is - else if (memcmp(fourCC, "TEXT", 4) == 0) type = RRES_DATA_TEXT; // Text file data, byte data extracted from text file - else if (memcmp(fourCC, "IMGE", 4) == 0) type = RRES_DATA_IMAGE; // Image file data, pixel data extracted from image file - else if (memcmp(fourCC, "WAVE", 4) == 0) type = RRES_DATA_WAVE; // Audio file data, samples data extracted from audio file - else if (memcmp(fourCC, "VRTX", 4) == 0) type = RRES_DATA_VERTEX; // Vertex file data, extracted from a mesh file - else if (memcmp(fourCC, "FNTG", 4) == 0) type = RRES_DATA_FONT_GLYPHS; // Font glyphs info, generated from an input font file - else if (memcmp(fourCC, "LINK", 4) == 0) type = RRES_DATA_LINK; // External linked file, filepath as provided on file input - else if (memcmp(fourCC, "CDIR", 4) == 0) type = RRES_DATA_DIRECTORY; // Central directory for input files relation to resource chunks - } - - /* - // Assign type (unsigned int) FourCC (char[4]) - if ((fourCC[0] == 'N') && (fourCC[1] == 'U') && (fourCC[2] == 'L') && (fourCC[3] == 'L')) type = RRES_DATA_NULL; // NULL - if ((fourCC[0] == 'R') && (fourCC[1] == 'A') && (fourCC[2] == 'W') && (fourCC[3] == 'D')) type = RRES_DATA_RAW; // RAWD - else if ((fourCC[0] == 'T') && (fourCC[1] == 'E') && (fourCC[2] == 'X') && (fourCC[3] == 'T')) type = RRES_DATA_TEXT; // TEXT - else if ((fourCC[0] == 'I') && (fourCC[1] == 'M') && (fourCC[2] == 'G') && (fourCC[3] == 'E')) type = RRES_DATA_IMAGE; // IMGE - else if ((fourCC[0] == 'W') && (fourCC[1] == 'A') && (fourCC[2] == 'V') && (fourCC[3] == 'E')) type = RRES_DATA_WAVE; // WAVE - else if ((fourCC[0] == 'V') && (fourCC[1] == 'R') && (fourCC[2] == 'T') && (fourCC[3] == 'X')) type = RRES_DATA_VERTEX; // VRTX - else if ((fourCC[0] == 'F') && (fourCC[1] == 'N') && (fourCC[2] == 'T') && (fourCC[3] == 'G')) type = RRES_DATA_FONT_GLYPHS; // FNTG - else if ((fourCC[0] == 'L') && (fourCC[1] == 'I') && (fourCC[2] == 'N') && (fourCC[3] == 'K')) type = RRES_DATA_LINK; // LINK - else if ((fourCC[0] == 'C') && (fourCC[1] == 'D') && (fourCC[2] == 'I') && (fourCC[3] == 'R')) type = RRES_DATA_DIRECTORY; // CDIR - */ - - return type; -} - -// Get resource identifier from filename -// WARNING: It requires the central directory previously loaded -int rresGetResourceId(rresCentralDir dir, const char *fileName) -{ - int id = 0; - - for (unsigned int i = 0, len = 0; i < dir.count; i++) - { - len = (unsigned int)strlen(fileName); - - // NOTE: entries[i].fileName is NULL terminated and padded to 4-bytes - if (strncmp((const char *)dir.entries[i].fileName, fileName, len) == 0) - { - id = dir.entries[i].id; - break; - } - } - - return id; -} - -// Compute CRC32 hash -// NOTE: CRC32 is used as rres id, generated from original filename -unsigned int rresComputeCRC32(unsigned char *data, int len) -{ - static unsigned int crcTable[256] = { - 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, - 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, - 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, - 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, - 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, - 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, - 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, - 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, - 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, - 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, - 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, - 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, - 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, - 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, - 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, - 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, - 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, - 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, - 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, - 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, - 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, - 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, - 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, - 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, - 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, - 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, - 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, - 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, - 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, - 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, - 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, - 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D - }; - - unsigned int crc = ~0u; - - for (int i = 0; i < len; i++) crc = (crc >> 8)^crcTable[data[i]^(crc&0xff)]; - - return ~crc; -} - -// Set password to be used on data decryption -void rresSetCipherPassword(const char *pass) -{ - password = pass; -} - -// Get password to be used on data decryption -const char *rresGetCipherPassword(void) -{ - if (password == NULL) password = "password12345"; - - return password; -} - -//---------------------------------------------------------------------------------- -// Module Internal Functions Definition -//---------------------------------------------------------------------------------- -// Load user resource chunk from resource packed data (as contained in .rres file) -// WARNING: Data can be compressed and/or encrypted, in those cases is up to the user to process it, -// and chunk.data.propCount = 0, chunk.data.props = NULL and chunk.data.raw contains all resource packed data -static rresResourceChunkData rresLoadResourceChunkData(rresResourceChunkInfo info, void *data) -{ - rresResourceChunkData chunkData = { 0 }; - - // CRC32 data validation, verify packed data is not corrupted - unsigned int crc32 = rresComputeCRC32((unsigned char *)data, info.packedSize); - - if ((rresGetDataType(info.type) != RRES_DATA_NULL) && (crc32 == info.crc32)) // Make sure chunk contains data and data is not corrupted - { - // Check if data chunk is compressed/encrypted to retrieve properties + data - if ((info.compType == RRES_COMP_NONE) && (info.cipherType == RRES_CIPHER_NONE)) - { - // Data is not compressed/encrypted (info.packedSize = info.baseSize) - chunkData.propCount = ((unsigned int *)data)[0]; - - if (chunkData.propCount > 0) - { - chunkData.props = (unsigned int *)RRES_CALLOC(chunkData.propCount, sizeof(unsigned int)); - for (unsigned int i = 0; i < chunkData.propCount; i++) chunkData.props[i] = ((unsigned int *)data)[i + 1]; - } - - int rawSize = info.baseSize - sizeof(int) - (chunkData.propCount*sizeof(int)); - chunkData.raw = RRES_MALLOC(rawSize); - memcpy(chunkData.raw, ((unsigned char *)data) + sizeof(int) + (chunkData.propCount*sizeof(int)), rawSize); - } - else - { - // Data is compressed/encrypted - // We just return the loaded resource packed data from .rres file, - // it's up to the user to manage decompression/decryption on user library - chunkData.raw = RRES_MALLOC(info.packedSize); - memcpy(chunkData.raw, (unsigned char *)data, info.packedSize); - } - } - - if (crc32 != info.crc32) RRES_LOG("RRES: WARNING: [ID %i] CRC32 does not match, data can be corrupted\n", info.id); - - return chunkData; -} - -#endif // RRES_IMPLEMENTATION