Initial commit
This commit is contained in:
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
root=true
|
||||
[**]
|
||||
indent_style=space
|
||||
indent_size=4
|
||||
trim_trailing_whitespace=true
|
||||
insert_final_newline=true
|
||||
|
||||
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
name: build_and_test
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: prepare
|
||||
run: |
|
||||
mkdir workspace
|
||||
cd workspace
|
||||
git clone https://github.com/floooh/sokol-samples
|
||||
cd sokol-samples
|
||||
- name: win64-vstudio-debug
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build win64-vstudio-debug
|
||||
- name: win64-vstudio-release
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build win64-vstudio-release
|
||||
- name: d3d11-win64-vstudio-debug
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build d3d11-win64-vstudio-debug
|
||||
- name: d3d11-win64-vstudio-release
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build d3d11-win64-vstudio-release
|
||||
- name: sapp-win64-vstudio-debug
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build sapp-win64-vstudio-debug
|
||||
- name: sapp-win64-vstudio-release
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build sapp-win64-vstudio-release
|
||||
- name: sapp-d3d11-win64-vstudio-debug
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build sapp-d3d11-win64-vstudio-debug
|
||||
- name: sapp-d3d11-win64-vstudio-release
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build sapp-d3d11-win64-vstudio-release
|
||||
- name: sokol-test sapp-win64-vstudio-debug
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips run sokol-test sapp-win64-vstudio-debug
|
||||
mac:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: prepare
|
||||
run: |
|
||||
mkdir workspace
|
||||
cd workspace
|
||||
git clone https://github.com/floooh/sokol-samples
|
||||
cd sokol-samples
|
||||
- name: osx-make-debug
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build osx-make-debug
|
||||
- name: osx-make-release
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build osx-make-release
|
||||
- name: metal-osx-make-debug
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build metal-osx-make-debug
|
||||
- name: metal-osx-make-release
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build metal-osx-make-release
|
||||
- name: sapp-metal-osx-make-debug
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build sapp-metal-osx-make-debug
|
||||
- name: sapp-metal-osx-make-release
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build sapp-metal-osx-make-release
|
||||
- name: sokol-test sapp-metal-osx-make-debug
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips run sokol-test sapp-metal-osx-make-debug
|
||||
ios:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: prepare
|
||||
run: |
|
||||
mkdir workspace
|
||||
cd workspace
|
||||
git clone https://github.com/floooh/sokol-samples
|
||||
cd sokol-samples
|
||||
- name: ios-xcode-debug
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build ios-xcode-debug -- CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
|
||||
- name: ios-xcode-release
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build ios-xcode-release -- CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
|
||||
- name: metal-ios-xcode-debug
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build metal-ios-xcode-debug -- CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
|
||||
- name: metal-ios-xcode-release
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build metal-ios-xcode-release -- CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
|
||||
- name: sapp-ios-xcode-debug
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build sapp-ios-xcode-debug -- CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
|
||||
- name: sapp-metal-ios-xcode-release
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build sapp-metal-ios-xcode-debug -- CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
|
||||
linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: prepare
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install libglu1-mesa-dev mesa-common-dev xorg-dev libasound-dev
|
||||
mkdir workspace
|
||||
cd workspace
|
||||
git clone https://github.com/floooh/sokol-samples
|
||||
cd sokol-samples
|
||||
- name: linux-make-debug
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build linux-make-debug
|
||||
- name: linux-make-release
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build linux-make-release
|
||||
- name: sapp-linux-make-debug
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build sapp-linux-make-debug
|
||||
- name: sapp-linux-make-release
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build sapp-linux-make-release
|
||||
- name: sapp-linux-make-debug
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips run sokol-test sapp-linux-make-debug
|
||||
emscripten:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: prepare
|
||||
run: |
|
||||
sudo apt-get install ninja-build
|
||||
mkdir workspace
|
||||
cd workspace
|
||||
git clone https://github.com/floooh/sokol-samples
|
||||
cd sokol-samples
|
||||
python fips emsdk install latest
|
||||
- name: sapp-webgl2-wasm-ninja-debug
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build sapp-webgl2-wasm-ninja-debug
|
||||
- name: sapp-webgl2-wasm-ninja-release
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build sapp-webgl2-wasm-ninja-release
|
||||
- name: wasm-ninja-debug
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips make cube-emsc wasm-ninja-debug
|
||||
- name: wasm-ninja-release
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips make cube-emsc wasm-ninja-release
|
||||
android:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: '8'
|
||||
- name: prepare
|
||||
run: |
|
||||
mkdir workspace
|
||||
cd workspace
|
||||
git clone https://github.com/floooh/sokol-samples
|
||||
cd sokol-samples
|
||||
yes | python fips setup android
|
||||
- name: sapp-android-make-debug
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build sapp-android-make-debug
|
||||
- name: sapp-android-make-release
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build sapp-android-make-release
|
||||
uwp:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: prepare
|
||||
run: |
|
||||
mkdir workspace
|
||||
cd workspace
|
||||
git clone https://github.com/floooh/sokol-samples
|
||||
cd sokol-samples
|
||||
- name: sapp-uwp-vstudio-debug
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build sapp-uwp-vstudio-debug
|
||||
- name: sapp-uwp-vstudio-release
|
||||
run: |
|
||||
cd workspace/sokol-samples
|
||||
python fips build sapp-uwp-vstudio-release
|
||||
@@ -0,0 +1,6 @@
|
||||
.vscode/
|
||||
#>fips
|
||||
# this area is managed by fips, do not edit
|
||||
.fips-*
|
||||
*.pyc
|
||||
#<fips
|
||||
Vendored
+1131
File diff suppressed because it is too large
Load Diff
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
zlib/libpng license
|
||||
|
||||
Copyright (c) 2018 Andre Weissflog
|
||||
|
||||
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.
|
||||
Vendored
+449
@@ -0,0 +1,449 @@
|
||||
# Sokol
|
||||
|
||||
[](https://github.com/floooh/sokol/actions)
|
||||
|
||||
Simple
|
||||
[STB-style](https://github.com/nothings/stb/blob/master/docs/stb_howto.txt)
|
||||
cross-platform libraries for C and C++, written in C.
|
||||
|
||||
[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**08-Oct-2021** revisited and cleaned up texture compression support in sokol_gfx.h)
|
||||
|
||||
## Examples and Related Projects
|
||||
|
||||
- [Live Samples](https://floooh.github.io/sokol-html5/index.html) via WASM ([source](https://github.com/floooh/sokol-samples))
|
||||
|
||||
- [Doom Shareware](https://floooh.github.io/doom-sokol/) ported to the Sokol headers ([source](https://github.com/floooh/doom-sokol))
|
||||
|
||||
- [LearnOpenGL examples ported to sokol-gfx](https://www.geertarien.com/learnopengl-examples-html5/) by @geertarien (cool stuff!)
|
||||
|
||||
- [Dear ImGui starterkit](https://github.com/floooh/cimgui-sokol-starterkit) a self-contained starterkit for writing Dear ImGui apps in C.
|
||||
|
||||
- [Tiny 8-bit emulators](https://floooh.github.io/tiny8bit/)
|
||||
|
||||
- A 'single-file' [Pacman clone in C99](https://github.com/floooh/pacman.c/), also available in [Zig](https://github.com/floooh/pacman.zig/)
|
||||
|
||||
- A [Minigolf game](https://mgerdes.github.io/minigolf.html) ([source](https://github.com/mgerdes/minigolf)).
|
||||
|
||||
- ['Dealer's Dungeon'](https://dealers-dungeon.com/demo/) ([lower graphics quality](https://dealers-dungeon.com/demo/?q=3),
|
||||
[source](https://github.com/bqqbarbhg/spear))
|
||||
|
||||
- [Command line tools](https://github.com/floooh/sokol-tools) (shader compiler)
|
||||
|
||||
- [How to build without a build system](https://github.com/floooh/sokol-samples#how-to-build-without-a-build-system):
|
||||
useful details for integrating the Sokol headers into your own project with your favourite C/C++ build system
|
||||
|
||||
## Core libraries
|
||||
|
||||
- [**sokol\_gfx.h**](https://github.com/floooh/sokol/blob/master/sokol_gfx.h): 3D-API wrapper (GL + Metal + D3D11)
|
||||
- [**sokol\_app.h**](https://github.com/floooh/sokol/blob/master/sokol_app.h): app framework wrapper (entry + window + 3D-context + input)
|
||||
- [**sokol\_time.h**](https://github.com/floooh/sokol/blob/master/sokol_time.h): time measurement
|
||||
- [**sokol\_audio.h**](https://github.com/floooh/sokol/blob/master/sokol_audio.h): minimal buffer-streaming audio playback
|
||||
- [**sokol\_fetch.h**](https://github.com/floooh/sokol/blob/master/sokol_fetch.h): asynchronous data streaming from HTTP and local filesystem
|
||||
- [**sokol\_args.h**](https://github.com/floooh/sokol/blob/master/sokol_args.h): unified cmdline/URL arg parser for web and native apps
|
||||
|
||||
## Utility libraries
|
||||
|
||||
- [**sokol\_imgui.h**](https://github.com/floooh/sokol/blob/master/util/sokol_imgui.h): sokol_gfx.h rendering backend for [Dear ImGui](https://github.com/ocornut/imgui)
|
||||
- [**sokol\_nuklear.h**](https://github.com/floooh/sokol/blob/master/util/sokol_nuklear.h): sokol_gfx.h rendering backend for [Nuklear](https://github.com/Immediate-Mode-UI/Nuklear)
|
||||
- [**sokol\_gl.h**](https://github.com/floooh/sokol/blob/master/util/sokol_gl.h): OpenGL 1.x style immediate-mode rendering API on top of sokol_gfx.h
|
||||
- [**sokol\_fontstash.h**](https://github.com/floooh/sokol/blob/master/util/sokol_fontstash.h): sokol_gl.h rendering backend for [fontstash](https://github.com/memononen/fontstash)
|
||||
- [**sokol\_gfx\_imgui.h**](https://github.com/floooh/sokol/blob/master/util/sokol_gfx_imgui.h): debug-inspection UI for sokol_gfx.h (implemented with Dear ImGui)
|
||||
- [**sokol\_debugtext.h**](https://github.com/floooh/sokol/blob/master/util/sokol_debugtext.h): a simple text renderer using vintage home computer fonts
|
||||
- [**sokol\_memtrack.h**](https://github.com/floooh/sokol/blob/master/util/sokol_memtrack.h): easily track memory allocations in sokol headers
|
||||
- [**sokol\_shape.h**](https://github.com/floooh/sokol/blob/master/util/sokol_shape.h): generate simple shapes and plug them into sokol-gfx resource creation structs
|
||||
- [**sokol\_color.h**](https://github.com/floooh/sokol/blob/master/util/sokol_color.h): X11 style color constants and functions for creating sg_color objects
|
||||
|
||||
## Notes
|
||||
|
||||
WebAssembly is a 'first-class citizen', one important motivation for the
|
||||
Sokol headers is to provide a collection of cross-platform APIs with a
|
||||
minimal footprint on the web platform while still being useful.
|
||||
|
||||
The core headers are standalone and can be used independently from each other.
|
||||
|
||||
### Why C:
|
||||
|
||||
- easier integration with other languages
|
||||
- easier integration into other projects
|
||||
- adds only minimal size overhead to executables
|
||||
|
||||
A blog post with more background info: [A Tour of sokol_gfx.h](http://floooh.github.io/2017/07/29/sokol-gfx-tour.html)
|
||||
|
||||
# sokol_gfx.h:
|
||||
|
||||
- simple, modern wrapper around GLES2/WebGL, GLES3/WebGL2, GL3.3, D3D11 and Metal
|
||||
- buffers, images, shaders, pipeline-state-objects and render-passes
|
||||
- does *not* handle window creation or 3D API context initialization
|
||||
- does *not* provide shader dialect cross-translation (**BUT** there's now an 'official' shader-cross-compiler solution which
|
||||
seamlessly integrates with sokol_gfx.h and IDEs: [see here for details](https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md)
|
||||
|
||||
A triangle in C99 with GLFW:
|
||||
|
||||
```c
|
||||
#define SOKOL_IMPL
|
||||
#define SOKOL_GLCORE33
|
||||
#include "sokol_gfx.h"
|
||||
#define GLFW_INCLUDE_NONE
|
||||
#include "GLFW/glfw3.h"
|
||||
|
||||
int main() {
|
||||
|
||||
/* create window and GL context via GLFW */
|
||||
glfwInit();
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
GLFWwindow* w = glfwCreateWindow(640, 480, "Sokol Triangle GLFW", 0, 0);
|
||||
glfwMakeContextCurrent(w);
|
||||
glfwSwapInterval(1);
|
||||
|
||||
/* setup sokol_gfx */
|
||||
sg_setup(&(sg_desc){0});
|
||||
|
||||
/* a vertex buffer */
|
||||
const float vertices[] = {
|
||||
// positions // colors
|
||||
0.0f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f,
|
||||
0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f,
|
||||
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f
|
||||
};
|
||||
sg_buffer vbuf = sg_make_buffer(&(sg_buffer_desc){
|
||||
.data = SG_RANGE(vertices)
|
||||
});
|
||||
|
||||
/* a shader */
|
||||
sg_shader shd = sg_make_shader(&(sg_shader_desc){
|
||||
.vs.source =
|
||||
"#version 330\n"
|
||||
"layout(location=0) in vec4 position;\n"
|
||||
"layout(location=1) in vec4 color0;\n"
|
||||
"out vec4 color;\n"
|
||||
"void main() {\n"
|
||||
" gl_Position = position;\n"
|
||||
" color = color0;\n"
|
||||
"}\n",
|
||||
.fs.source =
|
||||
"#version 330\n"
|
||||
"in vec4 color;\n"
|
||||
"out vec4 frag_color;\n"
|
||||
"void main() {\n"
|
||||
" frag_color = color;\n"
|
||||
"}\n"
|
||||
});
|
||||
|
||||
/* a pipeline state object (default render states are fine for triangle) */
|
||||
sg_pipeline pip = sg_make_pipeline(&(sg_pipeline_desc){
|
||||
.shader = shd,
|
||||
.layout = {
|
||||
.attrs = {
|
||||
[0].format=SG_VERTEXFORMAT_FLOAT3,
|
||||
[1].format=SG_VERTEXFORMAT_FLOAT4
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* resource bindings */
|
||||
sg_bindings bind = {
|
||||
.vertex_buffers[0] = vbuf
|
||||
};
|
||||
|
||||
/* default pass action (clear to grey) */
|
||||
sg_pass_action pass_action = {0};
|
||||
|
||||
/* draw loop */
|
||||
while (!glfwWindowShouldClose(w)) {
|
||||
int cur_width, cur_height;
|
||||
glfwGetFramebufferSize(w, &cur_width, &cur_height);
|
||||
sg_begin_default_pass(&pass_action, cur_width, cur_height);
|
||||
sg_apply_pipeline(pip);
|
||||
sg_apply_bindings(&bind);
|
||||
sg_draw(0, 3, 1);
|
||||
sg_end_pass();
|
||||
sg_commit();
|
||||
glfwSwapBuffers(w);
|
||||
glfwPollEvents();
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
sg_shutdown();
|
||||
glfwTerminate();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
# sokol_app.h
|
||||
|
||||
A minimal cross-platform application-wrapper library:
|
||||
|
||||
- unified application entry
|
||||
- single window or canvas for 3D rendering
|
||||
- 3D context initialization
|
||||
- event-based keyboard, mouse and touch input
|
||||
- supported platforms: Win32, MacOS, Linux (X11), iOS, WASM, Android, UWP
|
||||
- supported 3D-APIs: GL3.3 (GLX/WGL), Metal, D3D11, GLES2/WebGL, GLES3/WebGL2
|
||||
|
||||
A simple clear-loop sample using sokol_app.h and sokol_gfx.h (does not include
|
||||
separate sokol.c/.m implementation file which is necessary
|
||||
to split the Objective-C code from the C code of the sample):
|
||||
|
||||
```c
|
||||
#include "sokol_gfx.h"
|
||||
#include "sokol_app.h"
|
||||
#include "sokol_glue.h"
|
||||
|
||||
sg_pass_action pass_action;
|
||||
|
||||
void init(void) {
|
||||
sg_setup(&(sg_desc){
|
||||
.context = sapp_sgcontext()
|
||||
});
|
||||
pass_action = (sg_pass_action) {
|
||||
.colors[0] = { .action=SG_ACTION_CLEAR, .value={1.0f, 0.0f, 0.0f, 1.0f} }
|
||||
};
|
||||
}
|
||||
|
||||
void frame(void) {
|
||||
float g = pass_action.colors[0].value.g + 0.01f;
|
||||
pass_action.colors[0].value.g = (g > 1.0f) ? 0.0f : g;
|
||||
sg_begin_default_pass(&pass_action, sapp_width(), sapp_height());
|
||||
sg_end_pass();
|
||||
sg_commit();
|
||||
}
|
||||
|
||||
void cleanup(void) {
|
||||
sg_shutdown();
|
||||
}
|
||||
|
||||
sapp_desc sokol_main(int argc, char* argv[]) {
|
||||
return (sapp_desc){
|
||||
.init_cb = init,
|
||||
.frame_cb = frame,
|
||||
.cleanup_cb = cleanup,
|
||||
.width = 400,
|
||||
.height = 300,
|
||||
.window_title = "Clear Sample",
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
# sokol_audio.h
|
||||
|
||||
A minimal audio-streaming API:
|
||||
|
||||
- you provide a mono- or stereo-stream of 32-bit float samples which sokol_audio.h forwards into platform-specific backends
|
||||
- two ways to provide the data:
|
||||
1. directly fill backend audio buffer from your callback function running in the audio thread
|
||||
2. alternatively push small packets of audio data from your main loop,
|
||||
or a separate thread created by you
|
||||
- platform backends:
|
||||
- Windows: WASAPI
|
||||
- macOS/iOS: CoreAudio
|
||||
- Linux: ALSA
|
||||
- emscripten: WebAudio + ScriptProcessorNode (doesn't use the emscripten-provided OpenAL or SDL Audio wrappers)
|
||||
|
||||
A simple mono square-wave generator using the callback model:
|
||||
|
||||
```c
|
||||
// the sample callback, running in audio thread
|
||||
static void stream_cb(float* buffer, int num_frames, int num_channels) {
|
||||
assert(1 == num_channels);
|
||||
static uint32_t count = 0;
|
||||
for (int i = 0; i < num_frames; i++) {
|
||||
buffer[i] = (count++ & (1<<3)) ? 0.5f : -0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
// init sokol-audio with default params
|
||||
saudio_setup(&(saudio_desc){
|
||||
.stream_cb = stream_cb
|
||||
});
|
||||
|
||||
// run main loop
|
||||
...
|
||||
|
||||
// shutdown sokol-audio
|
||||
saudio_shutdown();
|
||||
return 0;
|
||||
```
|
||||
|
||||
The same code using the push-model
|
||||
|
||||
```c
|
||||
#define BUF_SIZE (32)
|
||||
int main() {
|
||||
// init sokol-audio with default params, no callback
|
||||
saudio_setup(&(saudio_desc){0});
|
||||
assert(saudio_channels() == 1);
|
||||
|
||||
// a small intermediate buffer so we don't need to push
|
||||
// individual samples, which would be quite inefficient
|
||||
float buf[BUF_SIZE];
|
||||
int buf_pos = 0;
|
||||
uint32_t count = 0;
|
||||
|
||||
// push samples from main loop
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
// generate and push audio samples...
|
||||
int num_frames = saudio_expect();
|
||||
for (int i = 0; i < num_frames; i++) {
|
||||
// simple square wave generator
|
||||
buf[buf_pos++] = (count++ & (1<<3)) ? 0.5f : -0.5f;
|
||||
if (buf_pos == BUF_SIZE) {
|
||||
buf_pos = 0;
|
||||
saudio_push(buf, BUF_SIZE);
|
||||
}
|
||||
}
|
||||
// handle other per-frame stuff...
|
||||
...
|
||||
}
|
||||
|
||||
// shutdown sokol-audio
|
||||
saudio_shutdown();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
# sokol_fetch.h
|
||||
|
||||
Load entire files, or stream data asynchronously over HTTP (emscripten/wasm)
|
||||
or the local filesystem (all native platforms).
|
||||
|
||||
Simple C99 example loading a file into a static buffer:
|
||||
|
||||
```c
|
||||
#include "sokol_fetch.h"
|
||||
|
||||
static void response_callback(const sfetch_response*);
|
||||
|
||||
#define MAX_FILE_SIZE (1024*1024)
|
||||
static uint8_t buffer[MAX_FILE_SIZE];
|
||||
|
||||
// application init
|
||||
static void init(void) {
|
||||
...
|
||||
// setup sokol-fetch with default config:
|
||||
sfetch_setup(&(sfetch_desc_t){0});
|
||||
|
||||
// start loading a file into a statically allocated buffer:
|
||||
sfetch_send(&(sfetch_request_t){
|
||||
.path = "hello_world.txt",
|
||||
.callback = response_callback
|
||||
.buffer_ptr = buffer,
|
||||
.buffer_size = sizeof(buffer)
|
||||
});
|
||||
}
|
||||
|
||||
// per frame...
|
||||
static void frame(void) {
|
||||
...
|
||||
// need to call sfetch_dowork() once per frame to 'turn the gears':
|
||||
sfetch_dowork();
|
||||
...
|
||||
}
|
||||
|
||||
// the response callback is where the interesting stuff happens:
|
||||
static void response_callback(const sfetch_response_t* response) {
|
||||
if (response->fetched) {
|
||||
// data has been loaded into the provided buffer, do something
|
||||
// with the data...
|
||||
const void* data = response->buffer_ptr;
|
||||
uint64_t data_size = response->fetched_size;
|
||||
}
|
||||
// the finished flag is set both on success and failure
|
||||
if (response->failed) {
|
||||
// oops, something went wrong
|
||||
switch (response->error_code) {
|
||||
SFETCH_ERROR_FILE_NOT_FOUND: ...
|
||||
SFETCH_ERROR_BUFFER_TOO_SMALL: ...
|
||||
...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// application shutdown
|
||||
static void shutdown(void) {
|
||||
...
|
||||
sfetch_shutdown();
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
# sokol_time.h:
|
||||
|
||||
Simple cross-platform time measurement:
|
||||
|
||||
```c
|
||||
#include "sokol_time.h"
|
||||
...
|
||||
/* initialize sokol_time */
|
||||
stm_setup();
|
||||
|
||||
/* take start timestamp */
|
||||
uint64_t start = stm_now();
|
||||
|
||||
...some code to measure...
|
||||
|
||||
/* compute elapsed time */
|
||||
uint64_t elapsed = stm_since(start);
|
||||
|
||||
/* convert to time units */
|
||||
double seconds = stm_sec(elapsed);
|
||||
double milliseconds = stm_ms(elapsed);
|
||||
double microseconds = stm_us(elapsed);
|
||||
double nanoseconds = stm_ns(elapsed);
|
||||
|
||||
/* difference between 2 time stamps */
|
||||
uint64_t start = stm_now();
|
||||
...
|
||||
uint64_t end = stm_now();
|
||||
uint64_t elapsed = stm_diff(end, start);
|
||||
|
||||
/* compute a 'lap time' (e.g. for fps) */
|
||||
uint64_t last_time = 0;
|
||||
while (!done) {
|
||||
...render something...
|
||||
double frame_time_ms = stm_ms(stm_laptime(&last_time));
|
||||
}
|
||||
```
|
||||
|
||||
# sokol_args.h
|
||||
|
||||
Unified argument parsing for web and native apps. Uses argc/argv on native
|
||||
platforms and the URL query string on the web.
|
||||
|
||||
Example URL with one arg:
|
||||
|
||||
https://floooh.github.io/tiny8bit/kc85.html?type=kc85_4
|
||||
|
||||
The same as command line app:
|
||||
|
||||
> kc85 type=kc85_4
|
||||
|
||||
Parsed like this:
|
||||
|
||||
```c
|
||||
#include "sokol_args.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
sargs_setup(&(sargs_desc){ .argc=argc, .argv=argv });
|
||||
if (sargs_exists("type")) {
|
||||
if (sargs_equals("type", "kc85_4")) {
|
||||
// start as KC85/4
|
||||
}
|
||||
else if (sargs_equals("type", "kc85_3")) {
|
||||
// start as KC85/3
|
||||
}
|
||||
else {
|
||||
// start as KC85/2
|
||||
}
|
||||
}
|
||||
sargs_shutdown();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
See the sokol_args.h header for a more complete documentation, and the [Tiny
|
||||
Emulators](https://floooh.github.io/tiny8bit/) for more interesting usage examples.
|
||||
@@ -0,0 +1,6 @@
|
||||
*.json
|
||||
*.nim
|
||||
*.zig
|
||||
__pycache__/
|
||||
sokol-nim/
|
||||
sokol-zig/
|
||||
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
## Language Binding Generation Scripts
|
||||
|
||||
### Zig
|
||||
|
||||
First make sure that clang and python3 are in the path:
|
||||
|
||||
```
|
||||
> clang --version
|
||||
> python3 --version
|
||||
```
|
||||
|
||||
...on Windows I simply install those with scoop:
|
||||
|
||||
```
|
||||
> scoop install llvm
|
||||
> scoop install python
|
||||
```
|
||||
|
||||
To update the Zig bindings:
|
||||
|
||||
```
|
||||
> cd sokol/bindgen
|
||||
> git clone https://github.com/floooh/sokol-zig
|
||||
> git clone https://github.com/floooh/sokol-nim
|
||||
> python3 gen_all.py
|
||||
```
|
||||
|
||||
Test and run samples:
|
||||
|
||||
```
|
||||
> cd sokol/bindgen/sokol-zig
|
||||
> zig build run-clear
|
||||
> zig build run-triangle
|
||||
> zig build run-cube
|
||||
...
|
||||
```
|
||||
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
import os, gen_nim, gen_zig
|
||||
|
||||
tasks = [
|
||||
[ '../sokol_gfx.h', 'sg_', [] ],
|
||||
[ '../sokol_app.h', 'sapp_', [] ],
|
||||
[ '../sokol_time.h', 'stm_', [] ],
|
||||
[ '../sokol_audio.h', 'saudio_', [] ],
|
||||
[ '../util/sokol_gl.h', 'sgl_', ['sg_'] ],
|
||||
[ '../util/sokol_debugtext.h', 'sdtx_', ['sg_'] ],
|
||||
[ '../util/sokol_shape.h', 'sshape_', ['sg_'] ],
|
||||
]
|
||||
|
||||
# Nim
|
||||
gen_nim.prepare()
|
||||
for task in tasks:
|
||||
c_header_path = task[0]
|
||||
main_prefix = task[1]
|
||||
dep_prefixes = task[2]
|
||||
gen_nim.gen(c_header_path, main_prefix, dep_prefixes)
|
||||
|
||||
# Zig
|
||||
gen_zig.prepare()
|
||||
for task in tasks:
|
||||
c_header_path = task[0]
|
||||
main_prefix = task[1]
|
||||
dep_prefixes = task[2]
|
||||
gen_zig.gen(c_header_path, main_prefix, dep_prefixes)
|
||||
Vendored
+122
@@ -0,0 +1,122 @@
|
||||
#-------------------------------------------------------------------------------
|
||||
# Generate an intermediate representation of a clang AST dump.
|
||||
#-------------------------------------------------------------------------------
|
||||
import json, sys, subprocess
|
||||
|
||||
def is_api_decl(decl, prefix):
|
||||
if 'name' in decl:
|
||||
return decl['name'].startswith(prefix)
|
||||
elif decl['kind'] == 'EnumDecl':
|
||||
# an anonymous enum, check if the items start with the prefix
|
||||
return decl['inner'][0]['name'].lower().startswith(prefix)
|
||||
else:
|
||||
return False
|
||||
|
||||
def is_dep_decl(decl, dep_prefixes):
|
||||
for prefix in dep_prefixes:
|
||||
if is_api_decl(decl, prefix):
|
||||
return True
|
||||
return False
|
||||
|
||||
def dep_prefix(decl, dep_prefixes):
|
||||
for prefix in dep_prefixes:
|
||||
if is_api_decl(decl, prefix):
|
||||
return prefix
|
||||
return None
|
||||
|
||||
def filter_types(str):
|
||||
return str.replace('_Bool', 'bool')
|
||||
|
||||
def parse_struct(decl):
|
||||
outp = {}
|
||||
outp['kind'] = 'struct'
|
||||
outp['name'] = decl['name']
|
||||
outp['fields'] = []
|
||||
for item_decl in decl['inner']:
|
||||
if item_decl['kind'] != 'FieldDecl':
|
||||
sys.exit(f"ERROR: Structs must only contain simple fields ({decl['name']})")
|
||||
item = {}
|
||||
if 'name' in item_decl:
|
||||
item['name'] = item_decl['name']
|
||||
item['type'] = filter_types(item_decl['type']['qualType'])
|
||||
outp['fields'].append(item)
|
||||
return outp
|
||||
|
||||
def parse_enum(decl):
|
||||
outp = {}
|
||||
if 'name' in decl:
|
||||
outp['kind'] = 'enum'
|
||||
outp['name'] = decl['name']
|
||||
needs_value = False
|
||||
else:
|
||||
outp['kind'] = 'consts'
|
||||
needs_value = True
|
||||
outp['items'] = []
|
||||
for item_decl in decl['inner']:
|
||||
if item_decl['kind'] == 'EnumConstantDecl':
|
||||
item = {}
|
||||
item['name'] = item_decl['name']
|
||||
if 'inner' in item_decl:
|
||||
const_expr = item_decl['inner'][0]
|
||||
if const_expr['kind'] != 'ConstantExpr':
|
||||
sys.exit(f"ERROR: Enum values must be a ConstantExpr ({decl['name']})")
|
||||
if const_expr['valueCategory'] != 'rvalue':
|
||||
sys.exit(f"ERROR: Enum value ConstantExpr must be 'rvalue' ({decl['name']})")
|
||||
if not ((len(const_expr['inner']) == 1) and (const_expr['inner'][0]['kind'] == 'IntegerLiteral')):
|
||||
sys.exit(f"ERROR: Enum value ConstantExpr must have exactly one IntegerLiteral ({decl['name']})")
|
||||
item['value'] = const_expr['inner'][0]['value']
|
||||
if needs_value and 'value' not in item:
|
||||
sys.exit(f"ERROR: anonymous enum items require an explicit value")
|
||||
outp['items'].append(item)
|
||||
return outp
|
||||
|
||||
def parse_func(decl):
|
||||
outp = {}
|
||||
outp['kind'] = 'func'
|
||||
outp['name'] = decl['name']
|
||||
outp['type'] = filter_types(decl['type']['qualType'])
|
||||
outp['params'] = []
|
||||
if 'inner' in decl:
|
||||
for param in decl['inner']:
|
||||
if param['kind'] != 'ParmVarDecl':
|
||||
print(f"warning: ignoring func {decl['name']} (unsupported parameter type)")
|
||||
return None
|
||||
outp_param = {}
|
||||
outp_param['name'] = param['name']
|
||||
outp_param['type'] = filter_types(param['type']['qualType'])
|
||||
outp['params'].append(outp_param)
|
||||
return outp
|
||||
|
||||
def parse_decl(decl):
|
||||
kind = decl['kind']
|
||||
if kind == 'RecordDecl':
|
||||
return parse_struct(decl)
|
||||
elif kind == 'EnumDecl':
|
||||
return parse_enum(decl)
|
||||
elif kind == 'FunctionDecl':
|
||||
return parse_func(decl)
|
||||
else:
|
||||
return None
|
||||
|
||||
def clang(csrc_path):
|
||||
cmd = ['clang', '-Xclang', '-ast-dump=json', '-c' ]
|
||||
cmd.append(csrc_path)
|
||||
return subprocess.check_output(cmd)
|
||||
|
||||
def gen(header_path, source_path, module, main_prefix, dep_prefixes):
|
||||
ast = clang(source_path)
|
||||
inp = json.loads(ast)
|
||||
outp = {}
|
||||
outp['module'] = module
|
||||
outp['prefix'] = main_prefix
|
||||
outp['dep_prefixes'] = dep_prefixes
|
||||
outp['decls'] = []
|
||||
for decl in inp['inner']:
|
||||
is_dep = is_dep_decl(decl, dep_prefixes)
|
||||
if is_api_decl(decl, main_prefix) or is_dep:
|
||||
outp_decl = parse_decl(decl)
|
||||
if outp_decl is not None:
|
||||
outp_decl['is_dep'] = is_dep
|
||||
outp_decl['dep_prefix'] = dep_prefix(decl, dep_prefixes)
|
||||
outp['decls'].append(outp_decl)
|
||||
return outp
|
||||
Vendored
+562
@@ -0,0 +1,562 @@
|
||||
#-------------------------------------------------------------------------------
|
||||
# Read output of gen_json.py and generate Zig language bindings.
|
||||
#
|
||||
# Nim coding style:
|
||||
# - types and constants are PascalCase
|
||||
# - functions, parameters, and fields are camelCase
|
||||
#-------------------------------------------------------------------------------
|
||||
import gen_ir
|
||||
import json, re, os, shutil
|
||||
|
||||
module_names = {
|
||||
'sg_': 'gfx',
|
||||
'sapp_': 'app',
|
||||
'stm_': 'time',
|
||||
'saudio_': 'audio',
|
||||
'sgl_': 'gl',
|
||||
'sdtx_': 'debugtext',
|
||||
'sshape_': 'shape',
|
||||
}
|
||||
|
||||
c_source_paths = {
|
||||
'sg_': 'sokol-nim/src/sokol/c/sokol_gfx.c',
|
||||
'sapp_': 'sokol-nim/src/sokol/c/sokol_app.c',
|
||||
'stm_': 'sokol-nim/src/sokol/c/sokol_time.c',
|
||||
'saudio_': 'sokol-nim/src/sokol/c/sokol_audio.c',
|
||||
'sgl_': 'sokol-nim/src/sokol/c/sokol_gl.c',
|
||||
'sdtx_': 'sokol-nim/src/sokol/c/sokol_debugtext.c',
|
||||
'sshape_': 'sokol-nim/src/sokol/c/sokol_shape.c',
|
||||
}
|
||||
|
||||
func_name_ignores = [
|
||||
'sdtx_printf',
|
||||
'sdtx_vprintf',
|
||||
]
|
||||
|
||||
func_name_overrides = {
|
||||
'sgl_error': 'sgl_get_error', # 'error' is reserved in Zig
|
||||
'sgl_deg': 'sgl_as_degrees',
|
||||
'sgl_rad': 'sgl_as_radians',
|
||||
}
|
||||
|
||||
struct_field_type_overrides = {
|
||||
'sg_context_desc.color_format': 'int',
|
||||
'sg_context_desc.depth_format': 'int',
|
||||
}
|
||||
|
||||
prim_types = {
|
||||
'int': 'int32',
|
||||
'bool': 'bool',
|
||||
'char': 'char',
|
||||
'int8_t': 'int8',
|
||||
'uint8_t': 'uint8',
|
||||
'int16_t': 'int16',
|
||||
'uint16_t': 'uint16',
|
||||
'int32_t': 'int32',
|
||||
'uint32_t': 'uint32',
|
||||
'int64_t': 'int64',
|
||||
'uint64_t': 'uint64',
|
||||
'float': 'float32',
|
||||
'double': 'float64',
|
||||
'uintptr_t': 'uint',
|
||||
'intptr_t': 'int',
|
||||
'size_t': 'int',
|
||||
}
|
||||
|
||||
prim_defaults = {
|
||||
'int': '0',
|
||||
'bool': 'false',
|
||||
'int8_t': '0',
|
||||
'uint8_t': '0',
|
||||
'int16_t': '0',
|
||||
'uint16_t': '0',
|
||||
'int32_t': '0',
|
||||
'uint32_t': '0',
|
||||
'int64_t': '0',
|
||||
'uint64_t': '0',
|
||||
'float': '0.0',
|
||||
'double': '0.0',
|
||||
'uintptr_t': '0',
|
||||
'intptr_t': '0',
|
||||
'size_t': '0'
|
||||
}
|
||||
|
||||
struct_types = []
|
||||
enum_types = []
|
||||
enum_items = {}
|
||||
out_lines = ''
|
||||
|
||||
def reset_globals():
|
||||
global struct_types
|
||||
global enum_types
|
||||
global enum_items
|
||||
global out_lines
|
||||
struct_types = []
|
||||
enum_types = []
|
||||
enum_items = {}
|
||||
out_lines = ''
|
||||
|
||||
re_1d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]$")
|
||||
re_2d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]\[\d*\]$")
|
||||
|
||||
def l(s):
|
||||
global out_lines
|
||||
out_lines += s + '\n'
|
||||
|
||||
def as_nim_prim_type(s):
|
||||
return prim_types[s]
|
||||
|
||||
# prefix_bla_blub(_t) => (dep.)BlaBlub
|
||||
def as_nim_struct_type(s, prefix):
|
||||
parts = s.lower().split('_')
|
||||
outp = '' if s.startswith(prefix) else f'{parts[0]}.'
|
||||
for part in parts[1:]:
|
||||
if (part != 't'):
|
||||
outp += part.capitalize()
|
||||
return outp
|
||||
|
||||
# prefix_bla_blub(_t) => (dep.)BlaBlub
|
||||
def as_nim_enum_type(s, prefix):
|
||||
parts = s.lower().split('_')
|
||||
outp = '' if s.startswith(prefix) else f'{parts[0]}.'
|
||||
for part in parts[1:]:
|
||||
if (part != 't'):
|
||||
outp += part.capitalize()
|
||||
return outp
|
||||
|
||||
# prefix_bla_blub(_t) => (dep.)BlaBlub
|
||||
def as_nim_const_type(s, prefix):
|
||||
parts = s.lower().split('_')
|
||||
outp = '' if s.startswith(prefix) else f'{parts[0]}.'
|
||||
for part in parts[1:]:
|
||||
if (part != 't'):
|
||||
outp += part.capitalize()
|
||||
return outp
|
||||
|
||||
def check_struct_field_type_override(struct_name, field_name, orig_type):
|
||||
s = f"{struct_name}.{field_name}"
|
||||
if s in struct_field_type_overrides:
|
||||
return struct_field_type_overrides[s]
|
||||
else:
|
||||
return orig_type
|
||||
|
||||
def check_func_name_ignore(func_name):
|
||||
return func_name in func_name_ignores
|
||||
|
||||
def check_func_name_override(func_name):
|
||||
if func_name in func_name_overrides:
|
||||
return func_name_overrides[func_name]
|
||||
else:
|
||||
return func_name
|
||||
|
||||
def trim_prefix(s, prefix):
|
||||
outp = s;
|
||||
if outp.lower().startswith(prefix.lower()):
|
||||
outp = outp[len(prefix):]
|
||||
return outp
|
||||
|
||||
# PREFIX_BLA_BLUB to bla_blub
|
||||
def as_snake_case(s, prefix = ""):
|
||||
return trim_prefix(s, prefix).lower()
|
||||
|
||||
# prefix_bla_blub => blaBlub
|
||||
def as_camel_case(s, prefix = ""):
|
||||
parts = trim_prefix(s, prefix).lower().split('_')
|
||||
outp = parts[0]
|
||||
for part in parts[1:]:
|
||||
outp += part.capitalize()
|
||||
return outp
|
||||
|
||||
# prefix_bla_blub => BlaBlub
|
||||
def as_pascal_case(s, prefix):
|
||||
parts = trim_prefix(s, prefix).lower().split('_')
|
||||
outp = ""
|
||||
for part in parts:
|
||||
outp += part.capitalize()
|
||||
return outp
|
||||
|
||||
# PREFIX_ENUM_BLA => Bla, _PREFIX_ENUM_BLA => Bla
|
||||
def as_enum_item_name(s):
|
||||
outp = s
|
||||
if outp.startswith('_'):
|
||||
outp = outp[1:]
|
||||
parts = outp.lower().split('_')[2:]
|
||||
outp = ""
|
||||
for part in parts:
|
||||
outp += part.capitalize()
|
||||
if outp[0].isdigit():
|
||||
outp = 'N' + outp
|
||||
return outp
|
||||
|
||||
def enum_default_item(enum_name):
|
||||
return enum_items[enum_name][0]
|
||||
|
||||
def is_prim_type(s):
|
||||
return s in prim_types
|
||||
|
||||
def is_struct_type(s):
|
||||
return s in struct_types
|
||||
|
||||
def is_enum_type(s):
|
||||
return s in enum_types
|
||||
|
||||
def is_string_ptr(s):
|
||||
return s == "const char *"
|
||||
|
||||
def is_const_void_ptr(s):
|
||||
return s == "const void *"
|
||||
|
||||
def is_void_ptr(s):
|
||||
return s == "void *"
|
||||
|
||||
def is_const_prim_ptr(s):
|
||||
for prim_type in prim_types:
|
||||
if s == f"const {prim_type} *":
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_prim_ptr(s):
|
||||
for prim_type in prim_types:
|
||||
if s == f"{prim_type} *":
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_const_struct_ptr(s):
|
||||
for struct_type in struct_types:
|
||||
if s == f"const {struct_type} *":
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_func_ptr(s):
|
||||
return '(*)' in s
|
||||
|
||||
def is_1d_array_type(s):
|
||||
return re_1d_array.match(s)
|
||||
|
||||
def is_2d_array_type(s):
|
||||
return re_2d_array.match(s)
|
||||
|
||||
def type_default_value(s):
|
||||
return prim_defaults[s]
|
||||
|
||||
def extract_array_type(s):
|
||||
return s[:s.index('[')].strip()
|
||||
|
||||
def extract_array_nums(s):
|
||||
return s[s.index('['):].replace('[', ' ').replace(']', ' ').split()
|
||||
|
||||
def extract_ptr_type(s):
|
||||
tokens = s.split()
|
||||
if tokens[0] == 'const':
|
||||
return tokens[1]
|
||||
else:
|
||||
return tokens[0]
|
||||
|
||||
def as_extern_c_arg_type(arg_type, prefix):
|
||||
if arg_type == "void":
|
||||
return "void"
|
||||
elif is_prim_type(arg_type):
|
||||
return as_nim_prim_type(arg_type)
|
||||
elif is_struct_type(arg_type):
|
||||
return as_nim_struct_type(arg_type, prefix)
|
||||
elif is_enum_type(arg_type):
|
||||
return as_nim_enum_type(arg_type, prefix)
|
||||
elif is_void_ptr(arg_type):
|
||||
return "pointer"
|
||||
elif is_const_void_ptr(arg_type):
|
||||
return "pointer"
|
||||
elif is_string_ptr(arg_type):
|
||||
return "cstring"
|
||||
elif is_const_struct_ptr(arg_type):
|
||||
return f"ptr {as_nim_struct_type(extract_ptr_type(arg_type), prefix)}"
|
||||
elif is_prim_ptr(arg_type):
|
||||
return f"[*c] {as_nim_prim_type(extract_ptr_type(arg_type))}"
|
||||
elif is_const_prim_ptr(arg_type):
|
||||
return f"ptr {as_nim_prim_type(extract_ptr_type(arg_type))}"
|
||||
else:
|
||||
return '??? (as_extern_c_arg_type)'
|
||||
|
||||
def as_nim_arg_type(arg_prefix, arg_type, prefix):
|
||||
# NOTE: if arg_prefix is None, the result is used as return value
|
||||
pre = "" if arg_prefix is None else arg_prefix
|
||||
if arg_type == "void":
|
||||
if arg_prefix is None:
|
||||
return "void"
|
||||
else:
|
||||
return ""
|
||||
elif is_prim_type(arg_type):
|
||||
return pre + as_nim_prim_type(arg_type)
|
||||
elif is_struct_type(arg_type):
|
||||
return pre + as_nim_struct_type(arg_type, prefix)
|
||||
elif is_enum_type(arg_type):
|
||||
return pre + as_nim_enum_type(arg_type, prefix)
|
||||
elif is_void_ptr(arg_type):
|
||||
return pre + "pointer"
|
||||
elif is_const_void_ptr(arg_type):
|
||||
return pre + "pointer"
|
||||
elif is_string_ptr(arg_type):
|
||||
return pre + "cstring"
|
||||
elif is_const_struct_ptr(arg_type):
|
||||
return pre + f"ptr {as_nim_struct_type(extract_ptr_type(arg_type), prefix)}"
|
||||
elif is_prim_ptr(arg_type):
|
||||
return pre + f"ptr {as_nim_prim_type(extract_ptr_type(arg_type))}"
|
||||
elif is_const_prim_ptr(arg_type):
|
||||
return pre + f"ptr {as_nim_prim_type(extract_ptr_type(arg_type))}"
|
||||
else:
|
||||
return arg_prefix + "??? (as_nim_arg_type)"
|
||||
|
||||
# get C-style arguments of a function pointer as string
|
||||
def funcptr_args_c(field_type, prefix):
|
||||
tokens = field_type[field_type.index('(*)')+4:-1].split(',')
|
||||
s = ""
|
||||
n = 0
|
||||
for token in tokens:
|
||||
n += 1
|
||||
arg_type = token.strip()
|
||||
if s != "":
|
||||
s += ", "
|
||||
c_arg = f"a{n}:" + as_extern_c_arg_type(arg_type, prefix)
|
||||
if (c_arg == "void"):
|
||||
return ""
|
||||
else:
|
||||
s += c_arg
|
||||
if s == "a1:void":
|
||||
s = ""
|
||||
return s
|
||||
|
||||
# get C-style result of a function pointer as string
|
||||
def funcptr_res_c(field_type):
|
||||
res_type = field_type[:field_type.index('(*)')].strip()
|
||||
if res_type == 'void':
|
||||
return ''
|
||||
elif is_const_void_ptr(res_type):
|
||||
return ':pointer'
|
||||
else:
|
||||
return '???'
|
||||
|
||||
def funcdecl_args_c(decl, prefix):
|
||||
s = ""
|
||||
for param_decl in decl['params']:
|
||||
if s != "":
|
||||
s += ", "
|
||||
arg_type = param_decl['type']
|
||||
s += as_extern_c_arg_type(arg_type, prefix)
|
||||
return s
|
||||
|
||||
def funcdecl_args_nim(decl, prefix):
|
||||
s = ""
|
||||
for param_decl in decl['params']:
|
||||
if s != "":
|
||||
s += ", "
|
||||
arg_name = param_decl['name']
|
||||
arg_type = param_decl['type']
|
||||
s += f"{as_nim_arg_type(f'{arg_name}:', arg_type, prefix)}"
|
||||
return s
|
||||
|
||||
def funcdecl_res_c(decl, prefix):
|
||||
decl_type = decl['type']
|
||||
res_type = decl_type[:decl_type.index('(')].strip()
|
||||
return as_extern_c_arg_type(res_type, prefix)
|
||||
|
||||
def funcdecl_res_nim(decl, prefix):
|
||||
decl_type = decl['type']
|
||||
res_type = decl_type[:decl_type.index('(')].strip()
|
||||
nim_res_type = as_nim_arg_type(None, res_type, prefix)
|
||||
if nim_res_type == "":
|
||||
nim_res_type = "void"
|
||||
return nim_res_type
|
||||
|
||||
def gen_struct(decl, prefix, use_raw_name=False):
|
||||
struct_name = decl['name']
|
||||
nim_type = struct_name if use_raw_name else as_nim_struct_type(struct_name, prefix)
|
||||
l(f"type {nim_type}* = object")
|
||||
isPublic = True
|
||||
for field in decl['fields']:
|
||||
field_name = field['name']
|
||||
if field_name == "__pad":
|
||||
# FIXME: these should be guarded by SOKOL_ZIG_BINDINGS, but aren't?
|
||||
continue
|
||||
isPublic = not field_name.startswith("_")
|
||||
field_name = as_camel_case(field_name, "_")
|
||||
if field_name == "ptr":
|
||||
field_name = "source"
|
||||
if field_name == "ref":
|
||||
field_name = "`ref`"
|
||||
if field_name == "type":
|
||||
field_name = "`type`"
|
||||
if isPublic:
|
||||
field_name += "*"
|
||||
field_type = field['type']
|
||||
field_type = check_struct_field_type_override(struct_name, field_name, field_type)
|
||||
if is_prim_type(field_type):
|
||||
l(f" {field_name}:{as_nim_prim_type(field_type)}")
|
||||
elif is_struct_type(field_type):
|
||||
l(f" {field_name}:{as_nim_struct_type(field_type, prefix)}")
|
||||
elif is_enum_type(field_type):
|
||||
l(f" {field_name}:{as_nim_enum_type(field_type, prefix)}")
|
||||
elif is_string_ptr(field_type):
|
||||
l(f" {field_name}:cstring")
|
||||
elif is_const_void_ptr(field_type):
|
||||
l(f" {field_name}:pointer")
|
||||
elif is_void_ptr(field_type):
|
||||
l(f" {field_name}:pointer")
|
||||
elif is_const_prim_ptr(field_type):
|
||||
l(f" {field_name}:ptr {as_nim_prim_type(extract_ptr_type(field_type))}")
|
||||
elif is_func_ptr(field_type):
|
||||
l(f" {field_name}:proc({funcptr_args_c(field_type, prefix)}){funcptr_res_c(field_type)} {{.cdecl.}}")
|
||||
elif is_1d_array_type(field_type):
|
||||
array_type = extract_array_type(field_type)
|
||||
array_nums = extract_array_nums(field_type)
|
||||
if is_prim_type(array_type) or is_struct_type(array_type):
|
||||
if is_prim_type(array_type):
|
||||
nim_type = as_nim_prim_type(array_type)
|
||||
elif is_struct_type(array_type):
|
||||
nim_type = as_nim_struct_type(array_type, prefix)
|
||||
elif is_enum_type(array_type):
|
||||
nim_type = as_nim_enum_type(array_type, prefix)
|
||||
else:
|
||||
nim_type = '??? (array type)'
|
||||
t0 = f"array[{array_nums[0]}, {nim_type}]"
|
||||
t0_slice = f"[]const {nim_type}"
|
||||
t1 = f"[_]{nim_type}"
|
||||
l(f" {field_name}:{t0}")
|
||||
elif is_const_void_ptr(array_type):
|
||||
l(f" {field_name}:array[{array_nums[0]}, pointer]")
|
||||
else:
|
||||
l(f"// FIXME: ??? array {field_name}:{field_type} => {array_type} [{array_nums[0]}]")
|
||||
elif is_2d_array_type(field_type):
|
||||
array_type = extract_array_type(field_type)
|
||||
array_nums = extract_array_nums(field_type)
|
||||
if is_prim_type(array_type):
|
||||
nim_type = as_nim_prim_type(array_type)
|
||||
def_val = type_default_value(array_type)
|
||||
elif is_struct_type(array_type):
|
||||
nim_type = as_nim_struct_type(array_type, prefix)
|
||||
def_val = ".{ }"
|
||||
else:
|
||||
nim_type = "???"
|
||||
def_val = "???"
|
||||
t0 = f"array[{array_nums[0]}, array[{array_nums[1]}, {nim_type}]]"
|
||||
l(f" {field_name}:{t0}")
|
||||
else:
|
||||
l(f"// FIXME: {field_name}:{field_type};")
|
||||
l("")
|
||||
|
||||
def gen_consts(decl, prefix):
|
||||
l("const")
|
||||
for item in decl['items']:
|
||||
l(f" {trim_prefix(item['name'], prefix)}* = {item['value']}")
|
||||
l("")
|
||||
|
||||
def gen_enum(decl, prefix):
|
||||
item_names_by_value = {}
|
||||
value = -1
|
||||
hasForceU32 = False
|
||||
hasExplicitValues = False
|
||||
for item in decl['items']:
|
||||
itemName = item['name']
|
||||
if itemName.endswith("_FORCE_U32"):
|
||||
hasForceU32 = True
|
||||
elif itemName.endswith("_NUM"):
|
||||
continue
|
||||
else:
|
||||
if 'value' in item:
|
||||
hasExplicitValues = True
|
||||
value = int(item['value'])
|
||||
else:
|
||||
value += 1
|
||||
item_names_by_value[value] = as_enum_item_name(item['name']);
|
||||
if hasForceU32:
|
||||
l(f"type {as_nim_enum_type(decl['name'], prefix)}* {{.pure, size:4.}} = enum")
|
||||
else:
|
||||
l(f"type {as_nim_enum_type(decl['name'], prefix)}* {{.pure.}} = enum")
|
||||
if hasExplicitValues:
|
||||
# Nim requires explicit enum values to be declared in ascending order
|
||||
for value in sorted(item_names_by_value):
|
||||
name = item_names_by_value[value]
|
||||
l(f" {name} = {value},")
|
||||
else:
|
||||
for name in item_names_by_value.values():
|
||||
l(f" {name},")
|
||||
l("")
|
||||
|
||||
def gen_func_nim(decl, prefix):
|
||||
c_func_name = decl['name']
|
||||
nim_func_name = as_camel_case(decl['name'], prefix)
|
||||
nim_res_type = funcdecl_res_nim(decl, prefix)
|
||||
l(f"proc {nim_func_name}*({funcdecl_args_nim(decl, prefix)}):{funcdecl_res_nim(decl, prefix)} {{.cdecl, importc:\"{decl['name']}\".}}")
|
||||
l("")
|
||||
|
||||
def pre_parse(inp):
|
||||
global struct_types
|
||||
global enum_types
|
||||
for decl in inp['decls']:
|
||||
kind = decl['kind']
|
||||
if kind == 'struct':
|
||||
struct_types.append(decl['name'])
|
||||
elif kind == 'enum':
|
||||
enum_name = decl['name']
|
||||
enum_types.append(enum_name)
|
||||
enum_items[enum_name] = []
|
||||
for item in decl['items']:
|
||||
enum_items[enum_name].append(as_enum_item_name(item['name']))
|
||||
|
||||
def gen_imports(inp, dep_prefixes):
|
||||
for dep_prefix in dep_prefixes:
|
||||
dep_module_name = module_names[dep_prefix]
|
||||
l(f'import {dep_module_name}')
|
||||
l('')
|
||||
|
||||
def gen_module(inp, dep_prefixes):
|
||||
l('## machine generated, do not edit')
|
||||
l('')
|
||||
gen_imports(inp, dep_prefixes)
|
||||
pre_parse(inp)
|
||||
prefix = inp['prefix']
|
||||
for decl in inp['decls']:
|
||||
if not decl['is_dep']:
|
||||
kind = decl['kind']
|
||||
if kind == 'consts':
|
||||
gen_consts(decl, prefix)
|
||||
elif kind == 'enum':
|
||||
gen_enum(decl, prefix)
|
||||
elif kind == 'struct':
|
||||
gen_struct(decl, prefix)
|
||||
elif kind == 'func':
|
||||
if not check_func_name_ignore(decl['name']):
|
||||
gen_func_nim(decl, prefix)
|
||||
|
||||
def prepare():
|
||||
print('Generating nim bindings:')
|
||||
if not os.path.isdir('sokol-nim/src/sokol'):
|
||||
os.makedirs('sokol-nim/src/sokol')
|
||||
if not os.path.isdir('sokol-nim/src/sokol/c'):
|
||||
os.makedirs('sokol-nim/src/sokol/c')
|
||||
|
||||
def gen(c_header_path, c_prefix, dep_c_prefixes):
|
||||
global out_lines
|
||||
module_name = module_names[c_prefix]
|
||||
c_source_path = c_source_paths[c_prefix]
|
||||
print(f' {c_header_path} => {module_name}')
|
||||
reset_globals()
|
||||
shutil.copyfile(c_header_path, f'sokol-nim/src/sokol/c/{os.path.basename(c_header_path)}')
|
||||
ir = gen_ir.gen(c_header_path, c_source_path, module_name, c_prefix, dep_c_prefixes)
|
||||
gen_module(ir, dep_c_prefixes)
|
||||
output_path = f"sokol-nim/src/sokol/{ir['module']}.nim"
|
||||
|
||||
## some changes for readability
|
||||
out_lines = out_lines.replace("PixelformatInfo", "PixelFormatInfo")
|
||||
out_lines = out_lines.replace(" Dontcare,", " DontCare,")
|
||||
out_lines = out_lines.replace(" Vertexbuffer,", " VertexBuffer,")
|
||||
out_lines = out_lines.replace(" Indexbuffer,", " IndexBuffer,")
|
||||
out_lines = out_lines.replace(" N2d,", " Plane,")
|
||||
out_lines = out_lines.replace(" N3d,", " Volume,")
|
||||
out_lines = out_lines.replace(" Vs,", " Vertex,")
|
||||
out_lines = out_lines.replace(" Fs,", " Fragment,")
|
||||
|
||||
## include extensions in generated code
|
||||
l("# Nim-specific API extensions")
|
||||
l(f"include nim/{ir['module']}")
|
||||
|
||||
with open(output_path, 'w', newline='\n') as f_outp:
|
||||
f_outp.write(out_lines)
|
||||
Vendored
+576
@@ -0,0 +1,576 @@
|
||||
#-------------------------------------------------------------------------------
|
||||
# Read output of gen_json.py and generate Zig language bindings.
|
||||
#
|
||||
# Zig coding style:
|
||||
# - types are PascalCase
|
||||
# - functions are camelCase
|
||||
# - otherwise snake_case
|
||||
#-------------------------------------------------------------------------------
|
||||
import gen_ir
|
||||
import json, re, os, shutil
|
||||
|
||||
module_names = {
|
||||
'sg_': 'gfx',
|
||||
'sapp_': 'app',
|
||||
'stm_': 'time',
|
||||
'saudio_': 'audio',
|
||||
'sgl_': 'gl',
|
||||
'sdtx_': 'debugtext',
|
||||
'sshape_': 'shape',
|
||||
}
|
||||
|
||||
c_source_paths = {
|
||||
'sg_': 'sokol-zig/src/sokol/c/sokol_gfx.c',
|
||||
'sapp_': 'sokol-zig/src/sokol/c/sokol_app.c',
|
||||
'stm_': 'sokol-zig/src/sokol/c/sokol_time.c',
|
||||
'saudio_': 'sokol-zig/src/sokol/c/sokol_audio.c',
|
||||
'sgl_': 'sokol-zig/src/sokol/c/sokol_gl.c',
|
||||
'sdtx_': 'sokol-zig/src/sokol/c/sokol_debugtext.c',
|
||||
'sshape_': 'sokol-zig/src/sokol/c/sokol_shape.c',
|
||||
}
|
||||
|
||||
name_ignores = [
|
||||
'sdtx_printf',
|
||||
'sdtx_vprintf',
|
||||
'sg_install_trace_hooks',
|
||||
'sg_trace_hooks',
|
||||
]
|
||||
|
||||
name_overrides = {
|
||||
'sgl_error': 'sgl_get_error', # 'error' is reserved in Zig
|
||||
'sgl_deg': 'sgl_as_degrees',
|
||||
'sgl_rad': 'sgl_as_radians'
|
||||
}
|
||||
|
||||
# NOTE: syntax for function results: "func_name.RESULT"
|
||||
type_overrides = {
|
||||
'sg_context_desc.color_format': 'int',
|
||||
'sg_context_desc.depth_format': 'int',
|
||||
'sg_apply_uniforms.ub_index': 'uint32_t',
|
||||
'sg_draw.base_element': 'uint32_t',
|
||||
'sg_draw.num_elements': 'uint32_t',
|
||||
'sg_draw.num_instances': 'uint32_t',
|
||||
'sshape_element_range_t.base_element': 'uint32_t',
|
||||
'sshape_element_range_t.num_elements': 'uint32_t',
|
||||
'sdtx_font.font_index': 'uint32_t',
|
||||
}
|
||||
|
||||
prim_types = {
|
||||
'int': 'i32',
|
||||
'bool': 'bool',
|
||||
'char': 'u8',
|
||||
'int8_t': 'i8',
|
||||
'uint8_t': 'u8',
|
||||
'int16_t': 'i16',
|
||||
'uint16_t': 'u16',
|
||||
'int32_t': 'i32',
|
||||
'uint32_t': 'u32',
|
||||
'int64_t': 'i64',
|
||||
'uint64_t': 'u64',
|
||||
'float': 'f32',
|
||||
'double': 'f64',
|
||||
'uintptr_t': 'usize',
|
||||
'intptr_t': 'isize',
|
||||
'size_t': 'usize'
|
||||
}
|
||||
|
||||
prim_defaults = {
|
||||
'int': '0',
|
||||
'bool': 'false',
|
||||
'int8_t': '0',
|
||||
'uint8_t': '0',
|
||||
'int16_t': '0',
|
||||
'uint16_t': '0',
|
||||
'int32_t': '0',
|
||||
'uint32_t': '0',
|
||||
'int64_t': '0',
|
||||
'uint64_t': '0',
|
||||
'float': '0.0',
|
||||
'double': '0.0',
|
||||
'uintptr_t': '0',
|
||||
'intptr_t': '0',
|
||||
'size_t': '0'
|
||||
}
|
||||
|
||||
struct_types = []
|
||||
enum_types = []
|
||||
enum_items = {}
|
||||
out_lines = ''
|
||||
|
||||
def reset_globals():
|
||||
global struct_types
|
||||
global enum_types
|
||||
global enum_items
|
||||
global out_lines
|
||||
struct_types = []
|
||||
enum_types = []
|
||||
enum_items = {}
|
||||
out_lines = ''
|
||||
|
||||
re_1d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]$")
|
||||
re_2d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]\[\d*\]$")
|
||||
|
||||
def l(s):
|
||||
global out_lines
|
||||
out_lines += s + '\n'
|
||||
|
||||
def as_zig_prim_type(s):
|
||||
return prim_types[s]
|
||||
|
||||
# prefix_bla_blub(_t) => (dep.)BlaBlub
|
||||
def as_zig_struct_type(s, prefix):
|
||||
parts = s.lower().split('_')
|
||||
outp = '' if s.startswith(prefix) else f'{parts[0]}.'
|
||||
for part in parts[1:]:
|
||||
if (part != 't'):
|
||||
outp += part.capitalize()
|
||||
return outp
|
||||
|
||||
# prefix_bla_blub(_t) => (dep.)BlaBlub
|
||||
def as_zig_enum_type(s, prefix):
|
||||
parts = s.lower().split('_')
|
||||
outp = '' if s.startswith(prefix) else f'{parts[0]}.'
|
||||
for part in parts[1:]:
|
||||
if (part != 't'):
|
||||
outp += part.capitalize()
|
||||
return outp
|
||||
|
||||
def check_type_override(func_or_struct_name, field_or_arg_name, orig_type):
|
||||
s = f"{func_or_struct_name}.{field_or_arg_name}"
|
||||
if s in type_overrides:
|
||||
return type_overrides[s]
|
||||
else:
|
||||
return orig_type
|
||||
|
||||
def check_name_override(name):
|
||||
if name in name_overrides:
|
||||
return name_overrides[name]
|
||||
else:
|
||||
return name
|
||||
|
||||
def check_name_ignore(name):
|
||||
return name in name_ignores
|
||||
|
||||
# PREFIX_BLA_BLUB to bla_blub
|
||||
def as_snake_case(s, prefix):
|
||||
outp = s.lower()
|
||||
if outp.startswith(prefix):
|
||||
outp = outp[len(prefix):]
|
||||
return outp
|
||||
|
||||
# prefix_bla_blub => blaBlub
|
||||
def as_camel_case(s):
|
||||
parts = s.lower().split('_')[1:]
|
||||
outp = parts[0]
|
||||
for part in parts[1:]:
|
||||
outp += part.capitalize()
|
||||
return outp
|
||||
|
||||
# PREFIX_ENUM_BLA => Bla, _PREFIX_ENUM_BLA => Bla
|
||||
def as_enum_item_name(s):
|
||||
outp = s
|
||||
if outp.startswith('_'):
|
||||
outp = outp[1:]
|
||||
parts = outp.split('_')[2:]
|
||||
outp = '_'.join(parts)
|
||||
if outp[0].isdigit():
|
||||
outp = '_' + outp
|
||||
return outp
|
||||
|
||||
def enum_default_item(enum_name):
|
||||
return enum_items[enum_name][0]
|
||||
|
||||
def is_prim_type(s):
|
||||
return s in prim_types
|
||||
|
||||
def is_struct_type(s):
|
||||
return s in struct_types
|
||||
|
||||
def is_enum_type(s):
|
||||
return s in enum_types
|
||||
|
||||
def is_string_ptr(s):
|
||||
return s == "const char *"
|
||||
|
||||
def is_const_void_ptr(s):
|
||||
return s == "const void *"
|
||||
|
||||
def is_void_ptr(s):
|
||||
return s == "void *"
|
||||
|
||||
def is_const_prim_ptr(s):
|
||||
for prim_type in prim_types:
|
||||
if s == f"const {prim_type} *":
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_prim_ptr(s):
|
||||
for prim_type in prim_types:
|
||||
if s == f"{prim_type} *":
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_const_struct_ptr(s):
|
||||
for struct_type in struct_types:
|
||||
if s == f"const {struct_type} *":
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_func_ptr(s):
|
||||
return '(*)' in s
|
||||
|
||||
def is_1d_array_type(s):
|
||||
return re_1d_array.match(s)
|
||||
|
||||
def is_2d_array_type(s):
|
||||
return re_2d_array.match(s)
|
||||
|
||||
def type_default_value(s):
|
||||
return prim_defaults[s]
|
||||
|
||||
def extract_array_type(s):
|
||||
return s[:s.index('[')].strip()
|
||||
|
||||
def extract_array_nums(s):
|
||||
return s[s.index('['):].replace('[', ' ').replace(']', ' ').split()
|
||||
|
||||
def extract_ptr_type(s):
|
||||
tokens = s.split()
|
||||
if tokens[0] == 'const':
|
||||
return tokens[1]
|
||||
else:
|
||||
return tokens[0]
|
||||
|
||||
def as_extern_c_arg_type(arg_type, prefix):
|
||||
if arg_type == "void":
|
||||
return "void"
|
||||
elif is_prim_type(arg_type):
|
||||
return as_zig_prim_type(arg_type)
|
||||
elif is_struct_type(arg_type):
|
||||
return as_zig_struct_type(arg_type, prefix)
|
||||
elif is_enum_type(arg_type):
|
||||
return as_zig_enum_type(arg_type, prefix)
|
||||
elif is_void_ptr(arg_type):
|
||||
return "?*c_void"
|
||||
elif is_const_void_ptr(arg_type):
|
||||
return "?*const c_void"
|
||||
elif is_string_ptr(arg_type):
|
||||
return "[*c]const u8"
|
||||
elif is_const_struct_ptr(arg_type):
|
||||
return f"[*c]const {as_zig_struct_type(extract_ptr_type(arg_type), prefix)}"
|
||||
elif is_prim_ptr(arg_type):
|
||||
return f"[*c] {as_zig_prim_type(extract_ptr_type(arg_type))}"
|
||||
elif is_const_prim_ptr(arg_type):
|
||||
return f"[*c]const {as_zig_prim_type(extract_ptr_type(arg_type))}"
|
||||
else:
|
||||
return '??? (as_extern_c_arg_type)'
|
||||
|
||||
def as_zig_arg_type(arg_prefix, arg_type, prefix):
|
||||
# NOTE: if arg_prefix is None, the result is used as return value
|
||||
pre = "" if arg_prefix is None else arg_prefix
|
||||
if arg_type == "void":
|
||||
if arg_prefix is None:
|
||||
return "void"
|
||||
else:
|
||||
return ""
|
||||
elif is_prim_type(arg_type):
|
||||
return pre + as_zig_prim_type(arg_type)
|
||||
elif is_struct_type(arg_type):
|
||||
return pre + as_zig_struct_type(arg_type, prefix)
|
||||
elif is_enum_type(arg_type):
|
||||
return pre + as_zig_enum_type(arg_type, prefix)
|
||||
elif is_void_ptr(arg_type):
|
||||
return pre + "?*c_void"
|
||||
elif is_const_void_ptr(arg_type):
|
||||
return pre + "?*const c_void"
|
||||
elif is_string_ptr(arg_type):
|
||||
return pre + "[:0]const u8"
|
||||
elif is_const_struct_ptr(arg_type):
|
||||
# not a bug, pass const structs by value
|
||||
return pre + f"{as_zig_struct_type(extract_ptr_type(arg_type), prefix)}"
|
||||
elif is_prim_ptr(arg_type):
|
||||
return pre + f"* {as_zig_prim_type(extract_ptr_type(arg_type))}"
|
||||
elif is_const_prim_ptr(arg_type):
|
||||
return pre + f"*const {as_zig_prim_type(extract_ptr_type(arg_type))}"
|
||||
else:
|
||||
return arg_prefix + "??? (as_zig_arg_type)"
|
||||
|
||||
# get C-style arguments of a function pointer as string
|
||||
def funcptr_args_c(field_type, prefix):
|
||||
tokens = field_type[field_type.index('(*)')+4:-1].split(',')
|
||||
s = ""
|
||||
for token in tokens:
|
||||
arg_type = token.strip()
|
||||
if s != "":
|
||||
s += ", "
|
||||
c_arg = as_extern_c_arg_type(arg_type, prefix)
|
||||
if (c_arg == "void"):
|
||||
return ""
|
||||
else:
|
||||
s += c_arg
|
||||
return s
|
||||
|
||||
# get C-style result of a function pointer as string
|
||||
def funcptr_res_c(field_type):
|
||||
res_type = field_type[:field_type.index('(*)')].strip()
|
||||
if res_type == 'void':
|
||||
return 'void'
|
||||
elif is_const_void_ptr(res_type):
|
||||
return '?*const c_void'
|
||||
else:
|
||||
return '???'
|
||||
|
||||
def funcdecl_args_c(decl, prefix):
|
||||
s = ""
|
||||
func_name = decl['name']
|
||||
for param_decl in decl['params']:
|
||||
if s != "":
|
||||
s += ", "
|
||||
param_name = param_decl['name']
|
||||
param_type = check_type_override(func_name, param_name, param_decl['type'])
|
||||
s += as_extern_c_arg_type(param_type, prefix)
|
||||
return s
|
||||
|
||||
def funcdecl_args_zig(decl, prefix):
|
||||
s = ""
|
||||
func_name = decl['name']
|
||||
for param_decl in decl['params']:
|
||||
if s != "":
|
||||
s += ", "
|
||||
param_name = param_decl['name']
|
||||
param_type = check_type_override(func_name, param_name, param_decl['type'])
|
||||
s += f"{as_zig_arg_type(f'{param_name}: ', param_type, prefix)}"
|
||||
return s
|
||||
|
||||
def funcdecl_result_c(decl, prefix):
|
||||
func_name = decl['name']
|
||||
decl_type = decl['type']
|
||||
result_type = check_type_override(func_name, 'RESULT', decl_type[:decl_type.index('(')].strip())
|
||||
return as_extern_c_arg_type(result_type, prefix)
|
||||
|
||||
def funcdecl_result_zig(decl, prefix):
|
||||
func_name = decl['name']
|
||||
decl_type = decl['type']
|
||||
result_type = check_type_override(func_name, 'RESULT', decl_type[:decl_type.index('(')].strip())
|
||||
zig_res_type = as_zig_arg_type(None, result_type, prefix)
|
||||
if zig_res_type == "":
|
||||
zig_res_type = "void"
|
||||
return zig_res_type
|
||||
|
||||
def gen_struct(decl, prefix, callconvc_funcptrs = True, use_raw_name=False, use_extern=True):
|
||||
struct_name = decl['name']
|
||||
zig_type = struct_name if use_raw_name else as_zig_struct_type(struct_name, prefix)
|
||||
l(f"pub const {zig_type} = {'extern ' if use_extern else ''}struct {{")
|
||||
for field in decl['fields']:
|
||||
field_name = field['name']
|
||||
field_type = field['type']
|
||||
field_type = check_type_override(struct_name, field_name, field_type)
|
||||
if is_prim_type(field_type):
|
||||
l(f" {field_name}: {as_zig_prim_type(field_type)} = {type_default_value(field_type)},")
|
||||
elif is_struct_type(field_type):
|
||||
l(f" {field_name}: {as_zig_struct_type(field_type, prefix)} = .{{ }},")
|
||||
elif is_enum_type(field_type):
|
||||
l(f" {field_name}: {as_zig_enum_type(field_type, prefix)} = .{enum_default_item(field_type)},")
|
||||
elif is_string_ptr(field_type):
|
||||
l(f" {field_name}: [*c]const u8 = null,")
|
||||
elif is_const_void_ptr(field_type):
|
||||
l(f" {field_name}: ?*const c_void = null,")
|
||||
elif is_void_ptr(field_type):
|
||||
l(f" {field_name}: ?*c_void = null,")
|
||||
elif is_const_prim_ptr(field_type):
|
||||
l(f" {field_name}: ?[*]const {as_zig_prim_type(extract_ptr_type(field_type))} = null,")
|
||||
elif is_func_ptr(field_type):
|
||||
if callconvc_funcptrs:
|
||||
l(f" {field_name}: ?fn({funcptr_args_c(field_type, prefix)}) callconv(.C) {funcptr_res_c(field_type)} = null,")
|
||||
else:
|
||||
l(f" {field_name}: ?fn({funcptr_args_c(field_type, prefix)}) {funcptr_res_c(field_type)} = null,")
|
||||
elif is_1d_array_type(field_type):
|
||||
array_type = extract_array_type(field_type)
|
||||
array_nums = extract_array_nums(field_type)
|
||||
if is_prim_type(array_type) or is_struct_type(array_type):
|
||||
if is_prim_type(array_type):
|
||||
zig_type = as_zig_prim_type(array_type)
|
||||
def_val = type_default_value(array_type)
|
||||
elif is_struct_type(array_type):
|
||||
zig_type = as_zig_struct_type(array_type, prefix)
|
||||
def_val = '.{}'
|
||||
elif is_enum_type(array_type):
|
||||
zig_type = as_zig_enum_type(array_type, prefix)
|
||||
def_val = '.{}'
|
||||
else:
|
||||
zig_type = '??? (array type)'
|
||||
def_val = '???'
|
||||
t0 = f"[{array_nums[0]}]{zig_type}"
|
||||
t0_slice = f"[]const {zig_type}"
|
||||
t1 = f"[_]{zig_type}"
|
||||
l(f" {field_name}: {t0} = {t1}{{{def_val}}} ** {array_nums[0]},")
|
||||
elif is_const_void_ptr(array_type):
|
||||
l(f" {field_name}: [{array_nums[0]}]?*const c_void = [_]?*const c_void {{ null }} ** {array_nums[0]},")
|
||||
else:
|
||||
l(f"// FIXME: ??? array {field_name}: {field_type} => {array_type} [{array_nums[0]}]")
|
||||
elif is_2d_array_type(field_type):
|
||||
array_type = extract_array_type(field_type)
|
||||
array_nums = extract_array_nums(field_type)
|
||||
if is_prim_type(array_type):
|
||||
zig_type = as_zig_prim_type(array_type)
|
||||
def_val = type_default_value(array_type)
|
||||
elif is_struct_type(array_type):
|
||||
zig_type = as_zig_struct_type(array_type, prefix)
|
||||
def_val = ".{ }"
|
||||
else:
|
||||
zig_type = "???"
|
||||
def_val = "???"
|
||||
t0 = f"[{array_nums[0]}][{array_nums[1]}]{zig_type}"
|
||||
l(f" {field_name}: {t0} = [_][{array_nums[1]}]{zig_type}{{[_]{zig_type}{{ {def_val} }}**{array_nums[1]}}}**{array_nums[0]},")
|
||||
else:
|
||||
l(f"// FIXME: {field_name}: {field_type};")
|
||||
l("};")
|
||||
|
||||
def gen_consts(decl, prefix):
|
||||
for item in decl['items']:
|
||||
l(f"pub const {as_snake_case(item['name'], prefix)} = {item['value']};")
|
||||
|
||||
def gen_enum(decl, prefix):
|
||||
l(f"pub const {as_zig_enum_type(decl['name'], prefix)} = enum(i32) {{")
|
||||
for item in decl['items']:
|
||||
item_name = as_enum_item_name(item['name'])
|
||||
if item_name != "FORCE_U32":
|
||||
if 'value' in item:
|
||||
l(f" {item_name} = {item['value']},")
|
||||
else:
|
||||
l(f" {item_name},")
|
||||
l("};")
|
||||
|
||||
def gen_func_c(decl, prefix):
|
||||
l(f"pub extern fn {decl['name']}({funcdecl_args_c(decl, prefix)}) {funcdecl_result_c(decl, prefix)};")
|
||||
|
||||
def gen_func_zig(decl, prefix):
|
||||
c_func_name = decl['name']
|
||||
zig_func_name = as_camel_case(check_name_override(decl['name']))
|
||||
zig_res_type = funcdecl_result_zig(decl, prefix)
|
||||
l(f"pub fn {zig_func_name}({funcdecl_args_zig(decl, prefix)}) {zig_res_type} {{")
|
||||
if zig_res_type != 'void':
|
||||
s = f" return {c_func_name}("
|
||||
else:
|
||||
s = f" {c_func_name}("
|
||||
for i, param_decl in enumerate(decl['params']):
|
||||
if i > 0:
|
||||
s += ", "
|
||||
arg_name = param_decl['name']
|
||||
arg_type = param_decl['type']
|
||||
if is_const_struct_ptr(arg_type):
|
||||
s += f"&{arg_name}"
|
||||
elif is_string_ptr(arg_type):
|
||||
s += f"@ptrCast([*c]const u8,{arg_name})"
|
||||
else:
|
||||
s += arg_name
|
||||
s += ");"
|
||||
l(s)
|
||||
l("}")
|
||||
|
||||
def pre_parse(inp):
|
||||
global struct_types
|
||||
global enum_types
|
||||
for decl in inp['decls']:
|
||||
kind = decl['kind']
|
||||
if kind == 'struct':
|
||||
struct_types.append(decl['name'])
|
||||
elif kind == 'enum':
|
||||
enum_name = decl['name']
|
||||
enum_types.append(enum_name)
|
||||
enum_items[enum_name] = []
|
||||
for item in decl['items']:
|
||||
enum_items[enum_name].append(as_enum_item_name(item['name']))
|
||||
|
||||
def gen_imports(inp, dep_prefixes):
|
||||
for dep_prefix in dep_prefixes:
|
||||
dep_module_name = module_names[dep_prefix]
|
||||
l(f'const {dep_prefix[:-1]} = @import("{dep_module_name}.zig");')
|
||||
l('')
|
||||
|
||||
def gen_helpers(inp):
|
||||
if inp['prefix'] in ['sg_', 'sdtx_', 'sshape_']:
|
||||
l('// helper function to convert "anything" to a Range struct')
|
||||
l('pub fn asRange(val: anytype) Range {')
|
||||
l(' const type_info = @typeInfo(@TypeOf(val));')
|
||||
l(' switch (type_info) {')
|
||||
l(' .Pointer => {')
|
||||
l(' switch (type_info.Pointer.size) {')
|
||||
l(' .One => return .{ .ptr = val, .size = @sizeOf(type_info.Pointer.child) },')
|
||||
l(' .Slice => return .{ .ptr = val.ptr, .size = @sizeOf(type_info.Pointer.child) * val.len },')
|
||||
l(' else => @compileError("FIXME: Pointer type!"),')
|
||||
l(' }')
|
||||
l(' },')
|
||||
l(' .Struct, .Array => {')
|
||||
l(' return .{ .ptr = &val, .size = @sizeOf(@TypeOf(val)) };')
|
||||
l(' },')
|
||||
l(' else => {')
|
||||
l(' @compileError("Cannot convert to range!");')
|
||||
l(' }')
|
||||
l(' }')
|
||||
l('}')
|
||||
l('')
|
||||
if inp['prefix'] == 'sdtx_':
|
||||
l('// std.fmt compatible Writer')
|
||||
l('pub const Writer = struct {')
|
||||
l(' pub const Error = error { };')
|
||||
l(' pub fn writeAll(self: Writer, bytes: []const u8) Error!void {')
|
||||
l(' _ = self;')
|
||||
l(' for (bytes) |byte| {')
|
||||
l(' putc(byte);')
|
||||
l(' }')
|
||||
l(' }')
|
||||
l(' pub fn writeByteNTimes(self: Writer, byte: u8, n: u64) Error!void {')
|
||||
l(' _ = self;')
|
||||
l(' var i: u64 = 0;')
|
||||
l(' while (i < n): (i += 1) {')
|
||||
l(' putc(byte);')
|
||||
l(' }')
|
||||
l(' }')
|
||||
l('};')
|
||||
l('// std.fmt-style formatted print')
|
||||
l('pub fn print(comptime fmt: anytype, args: anytype) void {')
|
||||
l(' var writer: Writer = .{};')
|
||||
l(' @import("std").fmt.format(writer, fmt, args) catch {};')
|
||||
l('}')
|
||||
l('')
|
||||
|
||||
def gen_module(inp, dep_prefixes):
|
||||
l('// machine generated, do not edit')
|
||||
l('')
|
||||
gen_imports(inp, dep_prefixes)
|
||||
gen_helpers(inp)
|
||||
pre_parse(inp)
|
||||
prefix = inp['prefix']
|
||||
for decl in inp['decls']:
|
||||
if not decl['is_dep']:
|
||||
kind = decl['kind']
|
||||
if kind == 'consts':
|
||||
gen_consts(decl, prefix)
|
||||
elif not check_name_ignore(decl['name']):
|
||||
if kind == 'struct':
|
||||
gen_struct(decl, prefix)
|
||||
elif kind == 'enum':
|
||||
gen_enum(decl, prefix)
|
||||
elif kind == 'func':
|
||||
gen_func_c(decl, prefix)
|
||||
gen_func_zig(decl, prefix)
|
||||
|
||||
def prepare():
|
||||
print('Generating zig bindings:')
|
||||
if not os.path.isdir('sokol-zig/src/sokol'):
|
||||
os.makedirs('sokol-zig/src/sokol')
|
||||
if not os.path.isdir('sokol-zig/src/sokol/c'):
|
||||
os.makedirs('sokol-zig/src/sokol/c')
|
||||
|
||||
def gen(c_header_path, c_prefix, dep_c_prefixes):
|
||||
module_name = module_names[c_prefix]
|
||||
c_source_path = c_source_paths[c_prefix]
|
||||
print(f' {c_header_path} => {module_name}')
|
||||
reset_globals()
|
||||
shutil.copyfile(c_header_path, f'sokol-zig/src/sokol/c/{os.path.basename(c_header_path)}')
|
||||
ir = gen_ir.gen(c_header_path, c_source_path, module_name, c_prefix, dep_c_prefixes)
|
||||
gen_module(ir, dep_c_prefixes)
|
||||
output_path = f"sokol-zig/src/sokol/{ir['module']}.zig"
|
||||
with open(output_path, 'w', newline='\n') as f_outp:
|
||||
f_outp.write(out_lines)
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
exports:
|
||||
header-dirs: [ ".", "util" ]
|
||||
Vendored
+11133
File diff suppressed because it is too large
Load Diff
Vendored
+770
@@ -0,0 +1,770 @@
|
||||
#if defined(SOKOL_IMPL) && !defined(SOKOL_ARGS_IMPL)
|
||||
#define SOKOL_ARGS_IMPL
|
||||
#endif
|
||||
#ifndef SOKOL_ARGS_INCLUDED
|
||||
/*
|
||||
sokol_args.h -- cross-platform key/value arg-parsing for web and native
|
||||
|
||||
Project URL: https://github.com/floooh/sokol
|
||||
|
||||
Do this:
|
||||
#define SOKOL_IMPL or
|
||||
#define SOKOL_ARGS_IMPL
|
||||
before you include this file in *one* C or C++ file to create the
|
||||
implementation.
|
||||
|
||||
Optionally provide the following defines with your own implementations:
|
||||
|
||||
SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
|
||||
SOKOL_LOG(msg) - your own logging functions (default: puts(msg))
|
||||
SOKOL_CALLOC(n,s) - your own calloc() implementation (default: calloc(n,s))
|
||||
SOKOL_FREE(p) - your own free() implementation (default: free(p))
|
||||
SOKOL_ARGS_API_DECL - public function declaration prefix (default: extern)
|
||||
SOKOL_API_DECL - same as SOKOL_ARGS_API_DECL
|
||||
SOKOL_API_IMPL - public function implementation prefix (default: -)
|
||||
|
||||
If sokol_args.h is compiled as a DLL, define the following before
|
||||
including the declaration or implementation:
|
||||
|
||||
SOKOL_DLL
|
||||
|
||||
On Windows, SOKOL_DLL will define SOKOL_ARGS_API_DECL as __declspec(dllexport)
|
||||
or __declspec(dllimport) as needed.
|
||||
|
||||
OVERVIEW
|
||||
========
|
||||
sokol_args.h provides a simple unified argument parsing API for WebAssembly and
|
||||
native apps.
|
||||
|
||||
When running as WebAssembly app, arguments are taken from the page URL:
|
||||
|
||||
https://floooh.github.io/tiny8bit/kc85.html?type=kc85_3&mod=m022&snapshot=kc85/jungle.kcc
|
||||
|
||||
The same arguments provided to a command line app:
|
||||
|
||||
kc85 type=kc85_3 mod=m022 snapshot=kc85/jungle.kcc
|
||||
|
||||
ARGUMENT FORMATTING
|
||||
===================
|
||||
On the web platform, arguments must be formatted as a valid URL query string
|
||||
with 'percent encoding' used for special characters.
|
||||
|
||||
Strings are expected to be UTF-8 encoded (although sokol_args.h doesn't
|
||||
contain any special UTF-8 handling). See below on how to obtain
|
||||
UTF-8 encoded argc/argv values on Windows when using WinMain() as
|
||||
entry point.
|
||||
|
||||
On native platforms the following rules must be followed:
|
||||
|
||||
Arguments have the general form
|
||||
|
||||
key=value
|
||||
|
||||
Key/value pairs are separated by 'whitespace', valid whitespace
|
||||
characters are space and tab.
|
||||
|
||||
Whitespace characters in front and after the separating '=' character
|
||||
are ignored:
|
||||
|
||||
key = value
|
||||
|
||||
...is the same as
|
||||
|
||||
key=value
|
||||
|
||||
The 'key' string must be a simple string without escape sequences or whitespace.
|
||||
|
||||
Currently 'single keys' without values are not allowed, but may be
|
||||
in the future.
|
||||
|
||||
The 'value' string can be quoted, and quoted value strings can contain
|
||||
whitespace:
|
||||
|
||||
key = 'single-quoted value'
|
||||
key = "double-quoted value"
|
||||
|
||||
Single-quoted value strings can contain double quotes, and vice-versa:
|
||||
|
||||
key = 'single-quoted value "can contain double-quotes"'
|
||||
key = "double-quoted value 'can contain single-quotes'"
|
||||
|
||||
Note that correct quoting can be tricky on some shells, since command
|
||||
shells may remove quotes, unless they're escaped.
|
||||
|
||||
Value strings can contain a small selection of escape sequences:
|
||||
|
||||
\n - newline
|
||||
\r - carriage return
|
||||
\t - tab
|
||||
\\ - escaped backslash
|
||||
|
||||
(more escape codes may be added in the future).
|
||||
|
||||
CODE EXAMPLE
|
||||
============
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
// initialize sokol_args with default parameters
|
||||
sargs_setup(&(sargs_desc){
|
||||
.argc = argc,
|
||||
.argv = argv
|
||||
});
|
||||
|
||||
// check if a key exists...
|
||||
if (sargs_exists("bla")) {
|
||||
...
|
||||
}
|
||||
|
||||
// get value string for key, if not found, return empty string ""
|
||||
const char* val0 = sargs_value("bla");
|
||||
|
||||
// get value string for key, or default string if key not found
|
||||
const char* val1 = sargs_value_def("bla", "default_value");
|
||||
|
||||
// check if a key matches expected value
|
||||
if (sargs_equals("type", "kc85_4")) {
|
||||
...
|
||||
}
|
||||
|
||||
// check if a key's value is "true", "yes" or "on"
|
||||
if (sargs_boolean("joystick_enabled")) {
|
||||
...
|
||||
}
|
||||
|
||||
// iterate over keys and values
|
||||
for (int i = 0; i < sargs_num_args(); i++) {
|
||||
printf("key: %s, value: %s\n", sargs_key_at(i), sargs_value_at(i));
|
||||
}
|
||||
|
||||
// lookup argument index by key string, will return -1 if key
|
||||
// is not found, sargs_key_at() and sargs_value_at() will return
|
||||
// an empty string for invalid indices
|
||||
int index = sargs_find("bla");
|
||||
printf("key: %s, value: %s\n", sargs_key_at(index), sargs_value_at(index));
|
||||
|
||||
// shutdown sokol-args
|
||||
sargs_shutdown();
|
||||
}
|
||||
|
||||
WINMAIN AND ARGC / ARGV
|
||||
=======================
|
||||
On Windows with WinMain() based apps, getting UTF8-encoded command line
|
||||
arguments is a bit more complicated:
|
||||
|
||||
First call GetCommandLineW(), this returns the entire command line
|
||||
as UTF-16 string. Then call CommandLineToArgvW(), this parses the
|
||||
command line string into the usual argc/argv format (but in UTF-16).
|
||||
Finally convert the UTF-16 strings in argv[] into UTF-8 via
|
||||
WideCharToMultiByte().
|
||||
|
||||
See the function _sapp_win32_command_line_to_utf8_argv() in sokol_app.h
|
||||
for example code how to do this (if you're using sokol_app.h, it will
|
||||
already convert the command line arguments to UTF-8 for you of course,
|
||||
so you can plug them directly into sokol_app.h).
|
||||
|
||||
API DOCUMENTATION
|
||||
=================
|
||||
void sargs_setup(const sargs_desc* desc)
|
||||
Initialize sokol_args, desc contains the following configuration
|
||||
parameters:
|
||||
int argc - the main function's argc parameter
|
||||
char** argv - the main function's argv parameter
|
||||
int max_args - max number of key/value pairs, default is 16
|
||||
int buf_size - size of the internal string buffer, default is 16384
|
||||
|
||||
Note that on the web, argc and argv will be ignored and the arguments
|
||||
will be taken from the page URL instead.
|
||||
|
||||
sargs_setup() will allocate 2 memory chunks: one for keeping track
|
||||
of the key/value args of size 'max_args*8', and a string buffer
|
||||
of size 'buf_size'.
|
||||
|
||||
void sargs_shutdown(void)
|
||||
Shutdown sokol-args and free any allocated memory.
|
||||
|
||||
bool sargs_isvalid(void)
|
||||
Return true between sargs_setup() and sargs_shutdown()
|
||||
|
||||
bool sargs_exists(const char* key)
|
||||
Test if a key arg exists.
|
||||
|
||||
const char* sargs_value(const char* key)
|
||||
Return value associated with key. Returns an empty
|
||||
string ("") if the key doesn't exist.
|
||||
|
||||
const char* sargs_value_def(const char* key, const char* default)
|
||||
Return value associated with key, or the provided default
|
||||
value if the value doesn't exist.
|
||||
|
||||
bool sargs_equals(const char* key, const char* val);
|
||||
Return true if the value associated with key matches
|
||||
the 'val' argument.
|
||||
|
||||
bool sargs_boolean(const char* key)
|
||||
Return true if the value string of 'key' is one
|
||||
of 'true', 'yes', 'on'.
|
||||
|
||||
int sargs_find(const char* key)
|
||||
Find argument by key name and return its index, or -1 if not found.
|
||||
|
||||
int sargs_num_args(void)
|
||||
Return number of key/value pairs.
|
||||
|
||||
const char* sargs_key_at(int index)
|
||||
Return the key name of argument at index. Returns empty string if
|
||||
is index is outside range.
|
||||
|
||||
const char* sargs_value_at(int index)
|
||||
Return the value of argument at index. Returns empty string
|
||||
if index is outside range.
|
||||
|
||||
TODO
|
||||
====
|
||||
- parsing errors?
|
||||
|
||||
LICENSE
|
||||
=======
|
||||
|
||||
zlib/libpng license
|
||||
|
||||
Copyright (c) 2018 Andre Weissflog
|
||||
|
||||
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.
|
||||
*/
|
||||
#define SOKOL_ARGS_INCLUDED (1)
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#if defined(SOKOL_API_DECL) && !defined(SOKOL_ARGS_API_DECL)
|
||||
#define SOKOL_ARGS_API_DECL SOKOL_API_DECL
|
||||
#endif
|
||||
#ifndef SOKOL_ARGS_API_DECL
|
||||
#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_ARGS_IMPL)
|
||||
#define SOKOL_ARGS_API_DECL __declspec(dllexport)
|
||||
#elif defined(_WIN32) && defined(SOKOL_DLL)
|
||||
#define SOKOL_ARGS_API_DECL __declspec(dllimport)
|
||||
#else
|
||||
#define SOKOL_ARGS_API_DECL extern
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct sargs_desc {
|
||||
int argc;
|
||||
char** argv;
|
||||
int max_args;
|
||||
int buf_size;
|
||||
} sargs_desc;
|
||||
|
||||
/* setup sokol-args */
|
||||
SOKOL_ARGS_API_DECL void sargs_setup(const sargs_desc* desc);
|
||||
/* shutdown sokol-args */
|
||||
SOKOL_ARGS_API_DECL void sargs_shutdown(void);
|
||||
/* true between sargs_setup() and sargs_shutdown() */
|
||||
SOKOL_ARGS_API_DECL bool sargs_isvalid(void);
|
||||
/* test if an argument exists by key name */
|
||||
SOKOL_ARGS_API_DECL bool sargs_exists(const char* key);
|
||||
/* get value by key name, return empty string if key doesn't exist */
|
||||
SOKOL_ARGS_API_DECL const char* sargs_value(const char* key);
|
||||
/* get value by key name, return provided default if key doesn't exist */
|
||||
SOKOL_ARGS_API_DECL const char* sargs_value_def(const char* key, const char* def);
|
||||
/* return true if val arg matches the value associated with key */
|
||||
SOKOL_ARGS_API_DECL bool sargs_equals(const char* key, const char* val);
|
||||
/* return true if key's value is "true", "yes" or "on" */
|
||||
SOKOL_ARGS_API_DECL bool sargs_boolean(const char* key);
|
||||
/* get index of arg by key name, return -1 if not exists */
|
||||
SOKOL_ARGS_API_DECL int sargs_find(const char* key);
|
||||
/* get number of parsed arguments */
|
||||
SOKOL_ARGS_API_DECL int sargs_num_args(void);
|
||||
/* get key name of argument at index, or empty string */
|
||||
SOKOL_ARGS_API_DECL const char* sargs_key_at(int index);
|
||||
/* get value string of argument at index, or empty string */
|
||||
SOKOL_ARGS_API_DECL const char* sargs_value_at(int index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
/* reference-based equivalents for c++ */
|
||||
inline void sargs_setup(const sargs_desc& desc) { return sargs_setup(&desc); }
|
||||
|
||||
#endif
|
||||
#endif // SOKOL_ARGS_INCLUDED
|
||||
|
||||
/*--- IMPLEMENTATION ---------------------------------------------------------*/
|
||||
#ifdef SOKOL_ARGS_IMPL
|
||||
#define SOKOL_ARGS_IMPL_INCLUDED (1)
|
||||
#include <string.h> /* memset, strcmp */
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
#ifndef SOKOL_API_IMPL
|
||||
#define SOKOL_API_IMPL
|
||||
#endif
|
||||
#ifndef SOKOL_DEBUG
|
||||
#ifndef NDEBUG
|
||||
#define SOKOL_DEBUG (1)
|
||||
#endif
|
||||
#endif
|
||||
#ifndef SOKOL_ASSERT
|
||||
#include <assert.h>
|
||||
#define SOKOL_ASSERT(c) assert(c)
|
||||
#endif
|
||||
#if !defined(SOKOL_CALLOC) && !defined(SOKOL_FREE)
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#if !defined(SOKOL_CALLOC)
|
||||
#define SOKOL_CALLOC(n,s) calloc(n,s)
|
||||
#endif
|
||||
#if !defined(SOKOL_FREE)
|
||||
#define SOKOL_FREE(p) free(p)
|
||||
#endif
|
||||
#ifndef SOKOL_LOG
|
||||
#ifdef SOKOL_DEBUG
|
||||
#include <stdio.h>
|
||||
#define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); }
|
||||
#else
|
||||
#define SOKOL_LOG(s)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef _SOKOL_PRIVATE
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define _SOKOL_PRIVATE __attribute__((unused)) static
|
||||
#else
|
||||
#define _SOKOL_PRIVATE static
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define _sargs_def(val, def) (((val) == 0) ? (def) : (val))
|
||||
|
||||
#define _SARGS_MAX_ARGS_DEF (16)
|
||||
#define _SARGS_BUF_SIZE_DEF (16*1024)
|
||||
|
||||
/* parser state */
|
||||
#define _SARGS_EXPECT_KEY (1<<0)
|
||||
#define _SARGS_EXPECT_SEP (1<<1)
|
||||
#define _SARGS_EXPECT_VAL (1<<2)
|
||||
#define _SARGS_PARSING_KEY (1<<3)
|
||||
#define _SARGS_PARSING_VAL (1<<4)
|
||||
#define _SARGS_ERROR (1<<5)
|
||||
|
||||
/* a key/value pair struct */
|
||||
typedef struct {
|
||||
int key; /* index to start of key string in buf */
|
||||
int val; /* index to start of value string in buf */
|
||||
} _sargs_kvp_t;
|
||||
|
||||
/* sokol-args state */
|
||||
typedef struct {
|
||||
int max_args; /* number of key/value pairs in args array */
|
||||
int num_args; /* number of valid items in args array */
|
||||
_sargs_kvp_t* args; /* key/value pair array */
|
||||
int buf_size; /* size of buffer in bytes */
|
||||
int buf_pos; /* current buffer position */
|
||||
char* buf; /* character buffer, first char is reserved and zero for 'empty string' */
|
||||
bool valid;
|
||||
uint32_t parse_state;
|
||||
char quote; /* current quote char, 0 if not in a quote */
|
||||
bool in_escape; /* currently in an escape sequence */
|
||||
} _sargs_state_t;
|
||||
static _sargs_state_t _sargs;
|
||||
|
||||
/*== PRIVATE IMPLEMENTATION FUNCTIONS ========================================*/
|
||||
|
||||
_SOKOL_PRIVATE void _sargs_putc(char c) {
|
||||
if ((_sargs.buf_pos+2) < _sargs.buf_size) {
|
||||
_sargs.buf[_sargs.buf_pos++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE const char* _sargs_str(int index) {
|
||||
SOKOL_ASSERT((index >= 0) && (index < _sargs.buf_size));
|
||||
return &_sargs.buf[index];
|
||||
}
|
||||
|
||||
/*-- argument parser functions ------------------*/
|
||||
_SOKOL_PRIVATE void _sargs_expect_key(void) {
|
||||
_sargs.parse_state = _SARGS_EXPECT_KEY;
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE bool _sargs_key_expected(void) {
|
||||
return 0 != (_sargs.parse_state & _SARGS_EXPECT_KEY);
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE void _sargs_expect_val(void) {
|
||||
_sargs.parse_state = _SARGS_EXPECT_VAL;
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE bool _sargs_val_expected(void) {
|
||||
return 0 != (_sargs.parse_state & _SARGS_EXPECT_VAL);
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE void _sargs_expect_sep(void) {
|
||||
_sargs.parse_state = _SARGS_EXPECT_SEP;
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE bool _sargs_any_expected(void) {
|
||||
return 0 != (_sargs.parse_state & (_SARGS_EXPECT_KEY | _SARGS_EXPECT_VAL | _SARGS_EXPECT_SEP));
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE bool _sargs_is_separator(char c) {
|
||||
return c == '=';
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE bool _sargs_is_quote(char c) {
|
||||
if (0 == _sargs.quote) {
|
||||
return (c == '\'') || (c == '"');
|
||||
}
|
||||
else {
|
||||
return c == _sargs.quote;
|
||||
}
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE void _sargs_begin_quote(char c) {
|
||||
_sargs.quote = c;
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE void _sargs_end_quote(void) {
|
||||
_sargs.quote = 0;
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE bool _sargs_in_quotes(void) {
|
||||
return 0 != _sargs.quote;
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE bool _sargs_is_whitespace(char c) {
|
||||
return !_sargs_in_quotes() && ((c == ' ') || (c == '\t'));
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE void _sargs_start_key(void) {
|
||||
SOKOL_ASSERT(_sargs.num_args < _sargs.max_args);
|
||||
_sargs.parse_state = _SARGS_PARSING_KEY;
|
||||
_sargs.args[_sargs.num_args].key = _sargs.buf_pos;
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE void _sargs_end_key(void) {
|
||||
SOKOL_ASSERT(_sargs.num_args < _sargs.max_args);
|
||||
_sargs_putc(0);
|
||||
_sargs.parse_state = 0;
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE bool _sargs_parsing_key(void) {
|
||||
return 0 != (_sargs.parse_state & _SARGS_PARSING_KEY);
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE void _sargs_start_val(void) {
|
||||
SOKOL_ASSERT(_sargs.num_args < _sargs.max_args);
|
||||
_sargs.parse_state = _SARGS_PARSING_VAL;
|
||||
_sargs.args[_sargs.num_args].val = _sargs.buf_pos;
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE void _sargs_end_val(void) {
|
||||
SOKOL_ASSERT(_sargs.num_args < _sargs.max_args);
|
||||
_sargs_putc(0);
|
||||
_sargs.num_args++;
|
||||
_sargs.parse_state = 0;
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE bool _sargs_is_escape(char c) {
|
||||
return '\\' == c;
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE void _sargs_start_escape(void) {
|
||||
_sargs.in_escape = true;
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE bool _sargs_in_escape(void) {
|
||||
return _sargs.in_escape;
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE char _sargs_escape(char c) {
|
||||
switch (c) {
|
||||
case 'n': return '\n';
|
||||
case 't': return '\t';
|
||||
case 'r': return '\r';
|
||||
case '\\': return '\\';
|
||||
default: return c;
|
||||
}
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE void _sargs_end_escape(void) {
|
||||
_sargs.in_escape = false;
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE bool _sargs_parsing_val(void) {
|
||||
return 0 != (_sargs.parse_state & _SARGS_PARSING_VAL);
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE bool _sargs_parse_carg(const char* src) {
|
||||
char c;
|
||||
while (0 != (c = *src++)) {
|
||||
if (_sargs_in_escape()) {
|
||||
c = _sargs_escape(c);
|
||||
_sargs_end_escape();
|
||||
}
|
||||
else if (_sargs_is_escape(c)) {
|
||||
_sargs_start_escape();
|
||||
continue;
|
||||
}
|
||||
if (_sargs_any_expected()) {
|
||||
if (!_sargs_is_whitespace(c)) {
|
||||
/* start of key, value or separator */
|
||||
if (_sargs_key_expected()) {
|
||||
/* start of new key */
|
||||
_sargs_start_key();
|
||||
}
|
||||
else if (_sargs_val_expected()) {
|
||||
/* start of value */
|
||||
if (_sargs_is_quote(c)) {
|
||||
_sargs_begin_quote(c);
|
||||
continue;
|
||||
}
|
||||
_sargs_start_val();
|
||||
}
|
||||
else {
|
||||
/* separator */
|
||||
if (_sargs_is_separator(c)) {
|
||||
_sargs_expect_val();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* skip white space */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (_sargs_parsing_key()) {
|
||||
if (_sargs_is_whitespace(c) || _sargs_is_separator(c)) {
|
||||
/* end of key string */
|
||||
_sargs_end_key();
|
||||
if (_sargs_is_separator(c)) {
|
||||
_sargs_expect_val();
|
||||
}
|
||||
else {
|
||||
_sargs_expect_sep();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (_sargs_parsing_val()) {
|
||||
if (_sargs_in_quotes()) {
|
||||
/* when in quotes, whitespace is a normal character
|
||||
and a matching quote ends the value string
|
||||
*/
|
||||
if (_sargs_is_quote(c)) {
|
||||
_sargs_end_quote();
|
||||
_sargs_end_val();
|
||||
_sargs_expect_key();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (_sargs_is_whitespace(c)) {
|
||||
/* end of value string (no quotes) */
|
||||
_sargs_end_val();
|
||||
_sargs_expect_key();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_sargs_putc(c);
|
||||
}
|
||||
if (_sargs_parsing_key()) {
|
||||
_sargs_end_key();
|
||||
_sargs_expect_sep();
|
||||
}
|
||||
else if (_sargs_parsing_val() && !_sargs_in_quotes()) {
|
||||
_sargs_end_val();
|
||||
_sargs_expect_key();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE bool _sargs_parse_cargs(int argc, const char** argv) {
|
||||
_sargs_expect_key();
|
||||
bool retval = true;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
retval &= _sargs_parse_carg(argv[i]);
|
||||
}
|
||||
_sargs.parse_state = 0;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*-- EMSCRIPTEN IMPLEMENTATION -----------------------------------------------*/
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
EMSCRIPTEN_KEEPALIVE void _sargs_add_kvp(const char* key, const char* val) {
|
||||
SOKOL_ASSERT(_sargs.valid && key && val);
|
||||
if (_sargs.num_args >= _sargs.max_args) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* copy key string */
|
||||
char c;
|
||||
_sargs.args[_sargs.num_args].key = _sargs.buf_pos;
|
||||
const char* ptr = key;
|
||||
while (0 != (c = *ptr++)) {
|
||||
_sargs_putc(c);
|
||||
}
|
||||
_sargs_putc(0);
|
||||
|
||||
/* copy value string */
|
||||
_sargs.args[_sargs.num_args].val = _sargs.buf_pos;
|
||||
ptr = val;
|
||||
while (0 != (c = *ptr++)) {
|
||||
_sargs_putc(c);
|
||||
}
|
||||
_sargs_putc(0);
|
||||
|
||||
_sargs.num_args++;
|
||||
}
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
/* JS function to extract arguments from the page URL */
|
||||
EM_JS(void, sargs_js_parse_url, (void), {
|
||||
var params = new URLSearchParams(window.location.search).entries();
|
||||
for (var p = params.next(); !p.done; p = params.next()) {
|
||||
var key = p.value[0];
|
||||
var val = p.value[1];
|
||||
var res = ccall('_sargs_add_kvp', 'void', ['string','string'], [key,val]);
|
||||
}
|
||||
});
|
||||
|
||||
#endif /* EMSCRIPTEN */
|
||||
|
||||
/*== PUBLIC IMPLEMENTATION FUNCTIONS =========================================*/
|
||||
SOKOL_API_IMPL void sargs_setup(const sargs_desc* desc) {
|
||||
SOKOL_ASSERT(desc);
|
||||
memset(&_sargs, 0, sizeof(_sargs));
|
||||
_sargs.max_args = _sargs_def(desc->max_args, _SARGS_MAX_ARGS_DEF);
|
||||
_sargs.buf_size = _sargs_def(desc->buf_size, _SARGS_BUF_SIZE_DEF);
|
||||
SOKOL_ASSERT(_sargs.buf_size > 8);
|
||||
_sargs.args = (_sargs_kvp_t*) SOKOL_CALLOC((size_t)_sargs.max_args, sizeof(_sargs_kvp_t));
|
||||
_sargs.buf = (char*) SOKOL_CALLOC((size_t)_sargs.buf_size, sizeof(char));
|
||||
/* the first character in buf is reserved and always zero, this is the 'empty string' */
|
||||
_sargs.buf_pos = 1;
|
||||
_sargs.valid = true;
|
||||
|
||||
/* parse argc/argv */
|
||||
_sargs_parse_cargs(desc->argc, (const char**) desc->argv);
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
/* on emscripten, also parse the page URL*/
|
||||
sargs_js_parse_url();
|
||||
#endif
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL void sargs_shutdown(void) {
|
||||
SOKOL_ASSERT(_sargs.valid);
|
||||
if (_sargs.args) {
|
||||
SOKOL_FREE(_sargs.args);
|
||||
_sargs.args = 0;
|
||||
}
|
||||
if (_sargs.buf) {
|
||||
SOKOL_FREE(_sargs.buf);
|
||||
_sargs.buf = 0;
|
||||
}
|
||||
_sargs.valid = false;
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL bool sargs_isvalid(void) {
|
||||
return _sargs.valid;
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL int sargs_find(const char* key) {
|
||||
SOKOL_ASSERT(_sargs.valid && key);
|
||||
for (int i = 0; i < _sargs.num_args; i++) {
|
||||
if (0 == strcmp(_sargs_str(_sargs.args[i].key), key)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL int sargs_num_args(void) {
|
||||
SOKOL_ASSERT(_sargs.valid);
|
||||
return _sargs.num_args;
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL const char* sargs_key_at(int index) {
|
||||
SOKOL_ASSERT(_sargs.valid);
|
||||
if ((index >= 0) && (index < _sargs.num_args)) {
|
||||
return _sargs_str(_sargs.args[index].key);
|
||||
}
|
||||
else {
|
||||
/* index 0 is always the empty string */
|
||||
return _sargs_str(0);
|
||||
}
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL const char* sargs_value_at(int index) {
|
||||
SOKOL_ASSERT(_sargs.valid);
|
||||
if ((index >= 0) && (index < _sargs.num_args)) {
|
||||
return _sargs_str(_sargs.args[index].val);
|
||||
}
|
||||
else {
|
||||
/* index 0 is always the empty string */
|
||||
return _sargs_str(0);
|
||||
}
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL bool sargs_exists(const char* key) {
|
||||
SOKOL_ASSERT(_sargs.valid && key);
|
||||
return -1 != sargs_find(key);
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL const char* sargs_value(const char* key) {
|
||||
SOKOL_ASSERT(_sargs.valid && key);
|
||||
return sargs_value_at(sargs_find(key));
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL const char* sargs_value_def(const char* key, const char* def) {
|
||||
SOKOL_ASSERT(_sargs.valid && key && def);
|
||||
int arg_index = sargs_find(key);
|
||||
if (-1 != arg_index) {
|
||||
return sargs_value_at(arg_index);
|
||||
}
|
||||
else {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL bool sargs_equals(const char* key, const char* val) {
|
||||
SOKOL_ASSERT(_sargs.valid && key && val);
|
||||
return 0 == strcmp(sargs_value(key), val);
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL bool sargs_boolean(const char* key) {
|
||||
const char* val = sargs_value(key);
|
||||
return (0 == strcmp("true", val)) ||
|
||||
(0 == strcmp("yes", val)) ||
|
||||
(0 == strcmp("on", val));
|
||||
}
|
||||
|
||||
#endif /* SOKOL_ARGS_IMPL */
|
||||
Vendored
+2147
File diff suppressed because it is too large
Load Diff
Vendored
+2523
File diff suppressed because it is too large
Load Diff
Vendored
+15732
File diff suppressed because it is too large
Load Diff
Vendored
+137
@@ -0,0 +1,137 @@
|
||||
#if defined(SOKOL_IMPL) && !defined(SOKOL_GLUE_IMPL)
|
||||
#define SOKOL_GLUE_IMPL
|
||||
#endif
|
||||
#ifndef SOKOL_GLUE_INCLUDED
|
||||
/*
|
||||
sokol_glue.h -- glue helper functions for sokol headers
|
||||
|
||||
Project URL: https://github.com/floooh/sokol
|
||||
|
||||
Do this:
|
||||
#define SOKOL_IMPL or
|
||||
#define SOKOL_GLUE_IMPL
|
||||
before you include this file in *one* C or C++ file to create the
|
||||
implementation.
|
||||
|
||||
...optionally provide the following macros to override defaults:
|
||||
|
||||
SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
|
||||
SOKOL_GLUE_API_DECL - public function declaration prefix (default: extern)
|
||||
SOKOL_API_DECL - same as SOKOL_GLUE_API_DECL
|
||||
SOKOL_API_IMPL - public function implementation prefix (default: -)
|
||||
|
||||
If sokol_glue.h is compiled as a DLL, define the following before
|
||||
including the declaration or implementation:
|
||||
|
||||
SOKOL_DLL
|
||||
|
||||
On Windows, SOKOL_DLL will define SOKOL_GLUE_API_DECL as __declspec(dllexport)
|
||||
or __declspec(dllimport) as needed.
|
||||
|
||||
OVERVIEW
|
||||
========
|
||||
The sokol core headers should not depend on each other, but sometimes
|
||||
it's useful to have a set of helper functions as "glue" between
|
||||
two or more sokol headers.
|
||||
|
||||
This is what sokol_glue.h is for. Simply include the header after other
|
||||
sokol headers (both for the implementation and declaration), and
|
||||
depending on what headers have been included before, sokol_glue.h
|
||||
will make available "glue functions".
|
||||
|
||||
PROVIDED FUNCTIONS
|
||||
==================
|
||||
|
||||
- if sokol_app.h and sokol_gfx.h is included:
|
||||
|
||||
sg_context_desc sapp_sgcontext(void):
|
||||
|
||||
Returns an initialized sg_context_desc function initialized
|
||||
by calling sokol_app.h functions.
|
||||
|
||||
LICENSE
|
||||
=======
|
||||
zlib/libpng license
|
||||
|
||||
Copyright (c) 2018 Andre Weissflog
|
||||
|
||||
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.
|
||||
*/
|
||||
#define SOKOL_GLUE_INCLUDED
|
||||
|
||||
#if defined(SOKOL_API_DECL) && !defined(SOKOL_GLUE_API_DECL)
|
||||
#define SOKOL_GLUE_API_DECL SOKOL_API_DECL
|
||||
#endif
|
||||
#ifndef SOKOL_GLUE_API_DECL
|
||||
#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GLUE_IMPL)
|
||||
#define SOKOL_GLUE_API_DECL __declspec(dllexport)
|
||||
#elif defined(_WIN32) && defined(SOKOL_DLL)
|
||||
#define SOKOL_GLUE_API_DECL __declspec(dllimport)
|
||||
#else
|
||||
#define SOKOL_GLUE_API_DECL extern
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(SOKOL_GFX_INCLUDED) && defined(SOKOL_APP_INCLUDED)
|
||||
SOKOL_GLUE_API_DECL sg_context_desc sapp_sgcontext(void);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
#endif /* SOKOL_GLUE_INCLUDED */
|
||||
|
||||
/*-- IMPLEMENTATION ----------------------------------------------------------*/
|
||||
#ifdef SOKOL_GLUE_IMPL
|
||||
#define SOKOL_GLUE_IMPL_INCLUDED (1)
|
||||
#include <string.h> /* memset */
|
||||
|
||||
#ifndef SOKOL_API_IMPL
|
||||
#define SOKOL_API_IMPL
|
||||
#endif
|
||||
|
||||
#if defined(SOKOL_GFX_INCLUDED) && defined(SOKOL_APP_INCLUDED)
|
||||
SOKOL_API_IMPL sg_context_desc sapp_sgcontext(void) {
|
||||
sg_context_desc desc;
|
||||
memset(&desc, 0, sizeof(desc));
|
||||
desc.color_format = (sg_pixel_format) sapp_color_format();
|
||||
desc.depth_format = (sg_pixel_format) sapp_depth_format();
|
||||
desc.sample_count = sapp_sample_count();
|
||||
desc.gl.force_gles2 = sapp_gles2();
|
||||
desc.metal.device = sapp_metal_get_device();
|
||||
desc.metal.renderpass_descriptor_cb = sapp_metal_get_renderpass_descriptor;
|
||||
desc.metal.drawable_cb = sapp_metal_get_drawable;
|
||||
desc.d3d11.device = sapp_d3d11_get_device();
|
||||
desc.d3d11.device_context = sapp_d3d11_get_device_context();
|
||||
desc.d3d11.render_target_view_cb = sapp_d3d11_get_render_target_view;
|
||||
desc.d3d11.depth_stencil_view_cb = sapp_d3d11_get_depth_stencil_view;
|
||||
desc.wgpu.device = sapp_wgpu_get_device();
|
||||
desc.wgpu.render_view_cb = sapp_wgpu_get_render_view;
|
||||
desc.wgpu.resolve_view_cb = sapp_wgpu_get_resolve_view;
|
||||
desc.wgpu.depth_stencil_view_cb = sapp_wgpu_get_depth_stencil_view;
|
||||
return desc;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SOKOL_GLUE_IMPL */
|
||||
Vendored
+324
@@ -0,0 +1,324 @@
|
||||
#if defined(SOKOL_IMPL) && !defined(SOKOL_TIME_IMPL)
|
||||
#define SOKOL_TIME_IMPL
|
||||
#endif
|
||||
#ifndef SOKOL_TIME_INCLUDED
|
||||
/*
|
||||
sokol_time.h -- simple cross-platform time measurement
|
||||
|
||||
Project URL: https://github.com/floooh/sokol
|
||||
|
||||
Do this:
|
||||
#define SOKOL_IMPL or
|
||||
#define SOKOL_TIME_IMPL
|
||||
before you include this file in *one* C or C++ file to create the
|
||||
implementation.
|
||||
|
||||
Optionally provide the following defines with your own implementations:
|
||||
SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
|
||||
SOKOL_TIME_API_DECL - public function declaration prefix (default: extern)
|
||||
SOKOL_API_DECL - same as SOKOL_TIME_API_DECL
|
||||
SOKOL_API_IMPL - public function implementation prefix (default: -)
|
||||
|
||||
If sokol_time.h is compiled as a DLL, define the following before
|
||||
including the declaration or implementation:
|
||||
|
||||
SOKOL_DLL
|
||||
|
||||
On Windows, SOKOL_DLL will define SOKOL_TIME_API_DECL as __declspec(dllexport)
|
||||
or __declspec(dllimport) as needed.
|
||||
|
||||
void stm_setup();
|
||||
Call once before any other functions to initialize sokol_time
|
||||
(this calls for instance QueryPerformanceFrequency on Windows)
|
||||
|
||||
uint64_t stm_now();
|
||||
Get current point in time in unspecified 'ticks'. The value that
|
||||
is returned has no relation to the 'wall-clock' time and is
|
||||
not in a specific time unit, it is only useful to compute
|
||||
time differences.
|
||||
|
||||
uint64_t stm_diff(uint64_t new, uint64_t old);
|
||||
Computes the time difference between new and old. This will always
|
||||
return a positive, non-zero value.
|
||||
|
||||
uint64_t stm_since(uint64_t start);
|
||||
Takes the current time, and returns the elapsed time since start
|
||||
(this is a shortcut for "stm_diff(stm_now(), start)")
|
||||
|
||||
uint64_t stm_laptime(uint64_t* last_time);
|
||||
This is useful for measuring frame time and other recurring
|
||||
events. It takes the current time, returns the time difference
|
||||
to the value in last_time, and stores the current time in
|
||||
last_time for the next call. If the value in last_time is 0,
|
||||
the return value will be zero (this usually happens on the
|
||||
very first call).
|
||||
|
||||
uint64_t stm_round_to_common_refresh_rate(uint64_t duration)
|
||||
This oddly named function takes a measured frame time and
|
||||
returns the closest "nearby" common display refresh rate frame duration
|
||||
in ticks. If the input duration isn't close to any common display
|
||||
refresh rate, the input duration will be returned unchanged as a fallback.
|
||||
The main purpose of this function is to remove jitter/inaccuracies from
|
||||
measured frame times, and instead use the display refresh rate as
|
||||
frame duration.
|
||||
|
||||
Use the following functions to convert a duration in ticks into
|
||||
useful time units:
|
||||
|
||||
double stm_sec(uint64_t ticks);
|
||||
double stm_ms(uint64_t ticks);
|
||||
double stm_us(uint64_t ticks);
|
||||
double stm_ns(uint64_t ticks);
|
||||
Converts a tick value into seconds, milliseconds, microseconds
|
||||
or nanoseconds. Note that not all platforms will have nanosecond
|
||||
or even microsecond precision.
|
||||
|
||||
Uses the following time measurement functions under the hood:
|
||||
|
||||
Windows: QueryPerformanceFrequency() / QueryPerformanceCounter()
|
||||
MacOS/iOS: mach_absolute_time()
|
||||
emscripten: performance.now()
|
||||
Linux+others: clock_gettime(CLOCK_MONOTONIC)
|
||||
|
||||
zlib/libpng license
|
||||
|
||||
Copyright (c) 2018 Andre Weissflog
|
||||
|
||||
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.
|
||||
*/
|
||||
#define SOKOL_TIME_INCLUDED (1)
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(SOKOL_API_DECL) && !defined(SOKOL_TIME_API_DECL)
|
||||
#define SOKOL_TIME_API_DECL SOKOL_API_DECL
|
||||
#endif
|
||||
#ifndef SOKOL_TIME_API_DECL
|
||||
#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_TIME_IMPL)
|
||||
#define SOKOL_TIME_API_DECL __declspec(dllexport)
|
||||
#elif defined(_WIN32) && defined(SOKOL_DLL)
|
||||
#define SOKOL_TIME_API_DECL __declspec(dllimport)
|
||||
#else
|
||||
#define SOKOL_TIME_API_DECL extern
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
SOKOL_TIME_API_DECL void stm_setup(void);
|
||||
SOKOL_TIME_API_DECL uint64_t stm_now(void);
|
||||
SOKOL_TIME_API_DECL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks);
|
||||
SOKOL_TIME_API_DECL uint64_t stm_since(uint64_t start_ticks);
|
||||
SOKOL_TIME_API_DECL uint64_t stm_laptime(uint64_t* last_time);
|
||||
SOKOL_TIME_API_DECL uint64_t stm_round_to_common_refresh_rate(uint64_t frame_ticks);
|
||||
SOKOL_TIME_API_DECL double stm_sec(uint64_t ticks);
|
||||
SOKOL_TIME_API_DECL double stm_ms(uint64_t ticks);
|
||||
SOKOL_TIME_API_DECL double stm_us(uint64_t ticks);
|
||||
SOKOL_TIME_API_DECL double stm_ns(uint64_t ticks);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
#endif // SOKOL_TIME_INCLUDED
|
||||
|
||||
/*-- IMPLEMENTATION ----------------------------------------------------------*/
|
||||
#ifdef SOKOL_TIME_IMPL
|
||||
#define SOKOL_TIME_IMPL_INCLUDED (1)
|
||||
#include <string.h> /* memset */
|
||||
|
||||
#ifndef SOKOL_API_IMPL
|
||||
#define SOKOL_API_IMPL
|
||||
#endif
|
||||
#ifndef SOKOL_ASSERT
|
||||
#include <assert.h>
|
||||
#define SOKOL_ASSERT(c) assert(c)
|
||||
#endif
|
||||
#ifndef _SOKOL_PRIVATE
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define _SOKOL_PRIVATE __attribute__((unused)) static
|
||||
#else
|
||||
#define _SOKOL_PRIVATE static
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
typedef struct {
|
||||
uint32_t initialized;
|
||||
LARGE_INTEGER freq;
|
||||
LARGE_INTEGER start;
|
||||
} _stm_state_t;
|
||||
#elif defined(__APPLE__) && defined(__MACH__)
|
||||
#include <mach/mach_time.h>
|
||||
typedef struct {
|
||||
uint32_t initialized;
|
||||
mach_timebase_info_data_t timebase;
|
||||
uint64_t start;
|
||||
} _stm_state_t;
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
#include <emscripten/emscripten.h>
|
||||
typedef struct {
|
||||
uint32_t initialized;
|
||||
double start;
|
||||
} _stm_state_t;
|
||||
#else /* anything else, this will need more care for non-Linux platforms */
|
||||
#ifdef ESP8266
|
||||
// On the ESP8266, clock_gettime ignores the first argument and CLOCK_MONOTONIC isn't defined
|
||||
#define CLOCK_MONOTONIC 0
|
||||
#endif
|
||||
#include <time.h>
|
||||
typedef struct {
|
||||
uint32_t initialized;
|
||||
uint64_t start;
|
||||
} _stm_state_t;
|
||||
#endif
|
||||
static _stm_state_t _stm;
|
||||
|
||||
/* prevent 64-bit overflow when computing relative timestamp
|
||||
see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3
|
||||
*/
|
||||
#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__))
|
||||
_SOKOL_PRIVATE int64_t int64_muldiv(int64_t value, int64_t numer, int64_t denom) {
|
||||
int64_t q = value / denom;
|
||||
int64_t r = value % denom;
|
||||
return q * numer + r * numer / denom;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
EM_JS(double, stm_js_perfnow, (void), {
|
||||
return performance.now();
|
||||
});
|
||||
#endif
|
||||
|
||||
SOKOL_API_IMPL void stm_setup(void) {
|
||||
memset(&_stm, 0, sizeof(_stm));
|
||||
_stm.initialized = 0xABCDABCD;
|
||||
#if defined(_WIN32)
|
||||
QueryPerformanceFrequency(&_stm.freq);
|
||||
QueryPerformanceCounter(&_stm.start);
|
||||
#elif defined(__APPLE__) && defined(__MACH__)
|
||||
mach_timebase_info(&_stm.timebase);
|
||||
_stm.start = mach_absolute_time();
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
_stm.start = stm_js_perfnow();
|
||||
#else
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
_stm.start = (uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec;
|
||||
#endif
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL uint64_t stm_now(void) {
|
||||
SOKOL_ASSERT(_stm.initialized == 0xABCDABCD);
|
||||
uint64_t now;
|
||||
#if defined(_WIN32)
|
||||
LARGE_INTEGER qpc_t;
|
||||
QueryPerformanceCounter(&qpc_t);
|
||||
now = (uint64_t) int64_muldiv(qpc_t.QuadPart - _stm.start.QuadPart, 1000000000, _stm.freq.QuadPart);
|
||||
#elif defined(__APPLE__) && defined(__MACH__)
|
||||
const uint64_t mach_now = mach_absolute_time() - _stm.start;
|
||||
now = (uint64_t) int64_muldiv((int64_t)mach_now, (int64_t)_stm.timebase.numer, (int64_t)_stm.timebase.denom);
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
double js_now = stm_js_perfnow() - _stm.start;
|
||||
SOKOL_ASSERT(js_now >= 0.0);
|
||||
now = (uint64_t) (js_now * 1000000.0);
|
||||
#else
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
now = ((uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec) - _stm.start;
|
||||
#endif
|
||||
return now;
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks) {
|
||||
if (new_ticks > old_ticks) {
|
||||
return new_ticks - old_ticks;
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL uint64_t stm_since(uint64_t start_ticks) {
|
||||
return stm_diff(stm_now(), start_ticks);
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL uint64_t stm_laptime(uint64_t* last_time) {
|
||||
SOKOL_ASSERT(last_time);
|
||||
uint64_t dt = 0;
|
||||
uint64_t now = stm_now();
|
||||
if (0 != *last_time) {
|
||||
dt = stm_diff(now, *last_time);
|
||||
}
|
||||
*last_time = now;
|
||||
return dt;
|
||||
}
|
||||
|
||||
// first number is frame duration in ns, second number is tolerance in ns,
|
||||
// the resulting min/max values must not overlap!
|
||||
static const uint64_t _stm_refresh_rates[][2] = {
|
||||
{ 16666667, 1000000 }, // 60 Hz: 16.6667 +- 1ms
|
||||
{ 13888889, 250000 }, // 72 Hz: 13.8889 +- 0.25ms
|
||||
{ 13333333, 250000 }, // 75 Hz: 13.3333 +- 0.25ms
|
||||
{ 11764706, 250000 }, // 85 Hz: 11.7647 +- 0.25
|
||||
{ 11111111, 250000 }, // 90 Hz: 11.1111 +- 0.25ms
|
||||
{ 10000000, 500000 }, // 100 Hz: 10.0000 +- 0.5ms
|
||||
{ 8333333, 500000 }, // 120 Hz: 8.3333 +- 0.5ms
|
||||
{ 6944445, 500000 }, // 144 Hz: 6.9445 +- 0.5ms
|
||||
{ 4166667, 1000000 }, // 240 Hz: 4.1666 +- 1ms
|
||||
{ 0, 0 }, // keep the last element always at zero
|
||||
};
|
||||
|
||||
SOKOL_API_IMPL uint64_t stm_round_to_common_refresh_rate(uint64_t ticks) {
|
||||
uint64_t ns;
|
||||
int i = 0;
|
||||
while (0 != (ns = _stm_refresh_rates[i][0])) {
|
||||
uint64_t tol = _stm_refresh_rates[i][1];
|
||||
if ((ticks > (ns - tol)) && (ticks < (ns + tol))) {
|
||||
return ns;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// fallthough: didn't fit into any buckets
|
||||
return ticks;
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL double stm_sec(uint64_t ticks) {
|
||||
return (double)ticks / 1000000000.0;
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL double stm_ms(uint64_t ticks) {
|
||||
return (double)ticks / 1000000.0;
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL double stm_us(uint64_t ticks) {
|
||||
return (double)ticks / 1000.0;
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL double stm_ns(uint64_t ticks) {
|
||||
return (double)ticks;
|
||||
}
|
||||
#endif /* SOKOL_TIME_IMPL */
|
||||
|
||||
+500
@@ -0,0 +1,500 @@
|
||||
#-------------------------------------------------------------------------------
|
||||
# Generate the sokol_color.h header from a predefined palette
|
||||
#-------------------------------------------------------------------------------
|
||||
# LICENSE
|
||||
# =======
|
||||
#
|
||||
# zlib/libpng license
|
||||
#
|
||||
# Copyright (c) 2020 Stuart Adams
|
||||
#
|
||||
# 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.
|
||||
|
||||
colors = [
|
||||
("Alice Blue", 0xF0F8FFFF),
|
||||
("Antique White", 0xFAEBD7FF),
|
||||
("Aqua", 0x00FFFFFF),
|
||||
("Aquamarine", 0x7FFFD4FF),
|
||||
("Azure", 0xF0FFFFFF),
|
||||
("Beige", 0xF5F5DCFF),
|
||||
("Bisque", 0xFFE4C4FF),
|
||||
("Black", 0x000000FF),
|
||||
("Blanched Almond", 0xFFEBCDFF),
|
||||
("Blue", 0x0000FFFF),
|
||||
("Blue Violet", 0x8A2BE2FF),
|
||||
("Brown", 0xA52A2AFF),
|
||||
("Burlywood", 0xDEB887FF),
|
||||
("Cadet Blue", 0x5F9EA0FF),
|
||||
("Chartreuse", 0x7FFF00FF),
|
||||
("Chocolate", 0xD2691EFF),
|
||||
("Coral", 0xFF7F50FF),
|
||||
("Cornflower Blue", 0x6495EDFF),
|
||||
("Cornsilk", 0xFFF8DCFF),
|
||||
("Crimson", 0xDC143CFF),
|
||||
("Cyan", 0x00FFFFFF),
|
||||
("Dark Blue", 0x00008BFF),
|
||||
("Dark Cyan", 0x008B8BFF),
|
||||
("Dark Goldenrod", 0xB8860BFF),
|
||||
("Dark Gray", 0xA9A9A9FF),
|
||||
("Dark Green", 0x006400FF),
|
||||
("Dark Khaki", 0xBDB76BFF),
|
||||
("Dark Magenta", 0x8B008BFF),
|
||||
("Dark Olive Green", 0x556B2FFF),
|
||||
("Dark Orange", 0xFF8C00FF),
|
||||
("Dark Orchid", 0x9932CCFF),
|
||||
("Dark Red", 0x8B0000FF),
|
||||
("Dark Salmon", 0xE9967AFF),
|
||||
("Dark Sea Green", 0x8FBC8FFF),
|
||||
("Dark Slate Blue", 0x483D8BFF),
|
||||
("Dark Slate Gray", 0x2F4F4FFF),
|
||||
("Dark Turquoise", 0x00CED1FF),
|
||||
("Dark Violet", 0x9400D3FF),
|
||||
("Deep Pink", 0xFF1493FF),
|
||||
("Deep Sky Blue", 0x00BFFFFF),
|
||||
("Dim Gray", 0x696969FF),
|
||||
("Dodger Blue", 0x1E90FFFF),
|
||||
("Firebrick", 0xB22222FF),
|
||||
("Floral White", 0xFFFAF0FF),
|
||||
("Forest Green", 0x228B22FF),
|
||||
("Fuchsia", 0xFF00FFFF),
|
||||
("Gainsboro", 0xDCDCDCFF),
|
||||
("Ghost White", 0xF8F8FFFF),
|
||||
("Gold", 0xFFD700FF),
|
||||
("Goldenrod", 0xDAA520FF),
|
||||
("Gray", 0xBEBEBEFF),
|
||||
("Web Gray", 0x808080FF),
|
||||
("Green", 0x00FF00FF),
|
||||
("Web Green", 0x008000FF),
|
||||
("Green Yellow", 0xADFF2FFF),
|
||||
("Honeydew", 0xF0FFF0FF),
|
||||
("Hot Pink", 0xFF69B4FF),
|
||||
("Indian Red", 0xCD5C5CFF),
|
||||
("Indigo", 0x4B0082FF),
|
||||
("Ivory", 0xFFFFF0FF),
|
||||
("Khaki", 0xF0E68CFF),
|
||||
("Lavender", 0xE6E6FAFF),
|
||||
("Lavender Blush", 0xFFF0F5FF),
|
||||
("Lawn Green", 0x7CFC00FF),
|
||||
("Lemon Chiffon", 0xFFFACDFF),
|
||||
("Light Blue", 0xADD8E6FF),
|
||||
("Light Coral", 0xF08080FF),
|
||||
("Light Cyan", 0xE0FFFFFF),
|
||||
("Light Goldenrod", 0xFAFAD2FF),
|
||||
("Light Gray", 0xD3D3D3FF),
|
||||
("Light Green", 0x90EE90FF),
|
||||
("Light Pink", 0xFFB6C1FF),
|
||||
("Light Salmon", 0xFFA07AFF),
|
||||
("Light Sea Green", 0x20B2AAFF),
|
||||
("Light Sky Blue", 0x87CEFAFF),
|
||||
("Light Slate Gray", 0x778899FF),
|
||||
("Light Steel Blue", 0xB0C4DEFF),
|
||||
("Light Yellow", 0xFFFFE0FF),
|
||||
("Lime", 0x00FF00FF),
|
||||
("Lime Green", 0x32CD32FF),
|
||||
("Linen", 0xFAF0E6FF),
|
||||
("Magenta", 0xFF00FFFF),
|
||||
("Maroon", 0xB03060FF),
|
||||
("Web Maroon", 0x800000FF),
|
||||
("Medium Aquamarine", 0x66CDAAFF),
|
||||
("Medium Blue", 0x0000CDFF),
|
||||
("Medium Orchid", 0xBA55D3FF),
|
||||
("Medium Purple", 0x9370DBFF),
|
||||
("Medium Sea Green", 0x3CB371FF),
|
||||
("Medium Slate Blue", 0x7B68EEFF),
|
||||
("Medium Spring Green", 0x00FA9AFF),
|
||||
("Medium Turquoise", 0x48D1CCFF),
|
||||
("Medium Violet Red", 0xC71585FF),
|
||||
("Midnight Blue", 0x191970FF),
|
||||
("Mint Cream", 0xF5FFFAFF),
|
||||
("Misty Rose", 0xFFE4E1FF),
|
||||
("Moccasin", 0xFFE4B5FF),
|
||||
("Navajo White", 0xFFDEADFF),
|
||||
("Navy Blue", 0x000080FF),
|
||||
("Old Lace", 0xFDF5E6FF),
|
||||
("Olive", 0x808000FF),
|
||||
("Olive Drab", 0x6B8E23FF),
|
||||
("Orange", 0xFFA500FF),
|
||||
("Orange Red", 0xFF4500FF),
|
||||
("Orchid", 0xDA70D6FF),
|
||||
("Pale Goldenrod", 0xEEE8AAFF),
|
||||
("Pale Green", 0x98FB98FF),
|
||||
("Pale Turquoise", 0xAFEEEEFF),
|
||||
("Pale Violet Red", 0xDB7093FF),
|
||||
("Papaya Whip", 0xFFEFD5FF),
|
||||
("Peach Puff", 0xFFDAB9FF),
|
||||
("Peru", 0xCD853FFF),
|
||||
("Pink", 0xFFC0CBFF),
|
||||
("Plum", 0xDDA0DDFF),
|
||||
("Powder Blue", 0xB0E0E6FF),
|
||||
("Purple", 0xA020F0FF),
|
||||
("Web Purple", 0x800080FF),
|
||||
("Rebecca Purple", 0x663399FF),
|
||||
("Red", 0xFF0000FF),
|
||||
("Rosy Brown", 0xBC8F8FFF),
|
||||
("Royal Blue", 0x4169E1FF),
|
||||
("Saddle Brown", 0x8B4513FF),
|
||||
("Salmon", 0xFA8072FF),
|
||||
("Sandy Brown", 0xF4A460FF),
|
||||
("Sea Green", 0x2E8B57FF),
|
||||
("Seashell", 0xFFF5EEFF),
|
||||
("Sienna", 0xA0522DFF),
|
||||
("Silver", 0xC0C0C0FF),
|
||||
("Sky Blue", 0x87CEEBFF),
|
||||
("Slate Blue", 0x6A5ACDFF),
|
||||
("Slate Gray", 0x708090FF),
|
||||
("Snow", 0xFFFAFAFF),
|
||||
("Spring Green", 0x00FF7FFF),
|
||||
("Steel Blue", 0x4682B4FF),
|
||||
("Tan", 0xD2B48CFF),
|
||||
("Teal", 0x008080FF),
|
||||
("Thistle", 0xD8BFD8FF),
|
||||
("Tomato", 0xFF6347FF),
|
||||
("Transparent", 0x00000000),
|
||||
("Turquoise", 0x40E0D0FF),
|
||||
("Violet", 0xEE82EEFF),
|
||||
("Wheat", 0xF5DEB3FF),
|
||||
("White", 0xFFFFFFFF),
|
||||
("White Smoke", 0xF5F5F5FF),
|
||||
("Yellow", 0xFFFF00FF),
|
||||
("Yellow Green", 0x9ACD32FF)
|
||||
]
|
||||
|
||||
header = open("sokol_color.h", "w")
|
||||
|
||||
header.write("""#if defined(SOKOL_IMPL) && !defined(SOKOL_COLOR_IMPL)
|
||||
#define SOKOL_COLOR_IMPL
|
||||
#endif
|
||||
#ifndef SOKOL_COLOR_INCLUDED
|
||||
/*
|
||||
sokol_color.h -- sg_color utilities
|
||||
|
||||
This header was generated by gen_sokol_color.py. Do not modify it.
|
||||
|
||||
Project URL: https://github.com/floooh/sokol
|
||||
|
||||
Include the following headers before including sokol_color.h:
|
||||
|
||||
sokol_gfx.h
|
||||
|
||||
FEATURE OVERVIEW
|
||||
================
|
||||
sokol_color.h defines preset colors based on the X11 color names,
|
||||
alongside utility functions to create and modify sg_color objects.
|
||||
|
||||
The predefined colors are based on the X11 color names:
|
||||
|
||||
https://en.wikipedia.org/wiki/X11_color_names
|
||||
|
||||
This palette is useful for prototyping - lots of programmers are familiar with
|
||||
these colours due to their use in X11, web development and XNA / MonoGame. They
|
||||
are also handy when you want to reference a familiar color, but don't want to
|
||||
write it out by hand.
|
||||
|
||||
COLORS
|
||||
======
|
||||
The palette is defined using static const (or constexpr if you are using a
|
||||
C++ compiler) objects. These objects use lowercase names:
|
||||
|
||||
static SOKOL_COLOR_CONSTEXPR sg_color sg_red = SG_RED;
|
||||
static SOKOL_COLOR_CONSTEXPR sg_color sg_green = SG_GREEN;
|
||||
static SOKOL_COLOR_CONSTEXPR sg_color sg_blue = SG_BLUE;
|
||||
|
||||
An sg_color preset object like sg_red can be used to initialize
|
||||
an sg_pass_action:
|
||||
|
||||
sg_pass_action pass_action = {
|
||||
.colors[0] = { .action=SG_ACTION_CLEAR, .value = sg_red }
|
||||
};
|
||||
|
||||
Initializing an object with static storage duration is more complicated
|
||||
because of C language rules. Technically, a static const is not a
|
||||
compile-time constant in C. To work around this, the palette is also
|
||||
defined as a series of brace-enclosed list macro definitions. These
|
||||
definitions use uppercase names:
|
||||
|
||||
#define SG_RED { 1.0f, 0.0f, 0.0f, 1.0f }
|
||||
#define SG_GREEN { 0.0f, 1.0f, 0.0f, 1.0f }
|
||||
#define SG_BLUE { 0.0f, 0.0f, 1.0f, 1.0f }
|
||||
|
||||
A preset macro like SG_RED can be used to initialize objects with static
|
||||
storage duration:
|
||||
|
||||
static struct {
|
||||
sg_pass_action pass_action;
|
||||
} state = {
|
||||
.pass_action = {
|
||||
.colors[0] = { .action = SG_ACTION_CLEAR, .value = SG_RED }
|
||||
}
|
||||
};
|
||||
|
||||
A second set of macro definitions exists for colors packed as 32 bit integer
|
||||
values. These definitions are also uppercase, but use the _RGBA32 suffix:
|
||||
|
||||
#define SG_RED_RGBA32 0xFF0000FF
|
||||
#define SG_GREEN_RGBA32 0x00FF00FF
|
||||
#define SG_BLUE_RGBA32 0x0000FFFF
|
||||
|
||||
This is useful if your code makes use of packed colors, as sokol_gl.h does for its
|
||||
internal vertex format:
|
||||
|
||||
sgl_begin_triangles();
|
||||
sgl_v2f_c1i( 0.0f, 0.5f, SG_RED_RGBA32);
|
||||
sgl_v2f_c1i( 0.5f, -0.5f, SG_GREEN_RGBA32);
|
||||
sgl_v2f_c1i(-0.5f, -0.5f, SG_BLUE_RGBA32);
|
||||
sgl_end();
|
||||
|
||||
UTILITY FUNCTIONS
|
||||
=================
|
||||
|
||||
Utility functions for creating colours are provided:
|
||||
|
||||
- sg_make_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
Create a sg_color object from separate R, G, B, A bytes.
|
||||
|
||||
- sg_make_color_1i(uint32_t rgba)
|
||||
Create a sg_color object from RGBA bytes packed into a 32-bit unsigned integer.
|
||||
|
||||
- sg_color_lerp(const sg_color* color_a, const sg_color* color_b, float amount)
|
||||
Linearly interpolate a color.
|
||||
|
||||
- sg_color_lerp_precise(const sg_color* color_a, const sg_color* color_b, float amount)
|
||||
Linearly interpolate a color. Less efficient but more precise than sg_color_lerp.
|
||||
|
||||
- sg_color_multiply(const sg_color* color, float scale)
|
||||
Multiply each color component by the scale factor.
|
||||
|
||||
LICENSE
|
||||
=======
|
||||
|
||||
zlib/libpng license
|
||||
|
||||
Copyright (c) 2020 Stuart Adams
|
||||
|
||||
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.
|
||||
*/
|
||||
#define SOKOL_COLOR_INCLUDED (1)
|
||||
|
||||
#if !defined(SOKOL_GFX_INCLUDED)
|
||||
#error "Please include sokol_gfx.h before sokol_color.h"
|
||||
#endif
|
||||
|
||||
#if defined(SOKOL_API_DECL) && !defined(SOKOL_GL_API_DECL)
|
||||
#define SOKOL_COLOR_API_DECL SOKOL_API_DECL
|
||||
#endif
|
||||
#ifndef SOKOL_COLOR_API_DECL
|
||||
#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_COLOR_IMPL)
|
||||
#define SOKOL_COLOR_API_DECL __declspec(dllexport)
|
||||
#elif defined(_WIN32) && defined(SOKOL_DLL)
|
||||
#define SOKOL_COLOR_API_DECL __declspec(dllimport)
|
||||
#else
|
||||
#define SOKOL_COLOR_API_DECL extern
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define SOKOL_COLOR_CONSTEXPR constexpr
|
||||
extern "C" {
|
||||
#else
|
||||
#define SOKOL_COLOR_CONSTEXPR const
|
||||
#endif
|
||||
|
||||
SOKOL_COLOR_API_DECL sg_color sg_make_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||
SOKOL_COLOR_API_DECL sg_color sg_make_color_1i(uint32_t rgba);
|
||||
SOKOL_COLOR_API_DECL sg_color sg_color_lerp(const sg_color* color_a, const sg_color* color_b, float amount);
|
||||
SOKOL_COLOR_API_DECL sg_color sg_color_lerp_precise(const sg_color* color_a, const sg_color* color_b, float amount);
|
||||
SOKOL_COLOR_API_DECL sg_color sg_color_multiply(const sg_color* color, float scale);
|
||||
|
||||
""")
|
||||
|
||||
def unpack_rgba(color):
|
||||
red = (color & 0xFF000000) >> 24
|
||||
green = (color & 0xFF0000) >> 16
|
||||
blue = (color & 0xFF00) >> 8
|
||||
alpha = (color & 0xFF)
|
||||
return (red, green, blue, alpha)
|
||||
|
||||
def add_documentation(color):
|
||||
documentation = "/* {name} color {{ R:{r}, G:{g}, B:{b}, A:{a} }} */\n"
|
||||
rgba = unpack_rgba(color[1])
|
||||
header.write(documentation.format(
|
||||
name = color[0], r = rgba[0], g = rgba[1], b = rgba[2], a = rgba[3]))
|
||||
|
||||
for color in colors:
|
||||
add_documentation(color)
|
||||
init_color = "SG_" + color[0].upper().replace(" ", "_")
|
||||
init_color_definition = "#define {name} {{ {r}f, {g}f, {b}f, {a}f }}\n"
|
||||
rgba = unpack_rgba(color[1])
|
||||
r = rgba[0] / 255
|
||||
g = rgba[1] / 255
|
||||
b = rgba[2] / 255
|
||||
a = rgba[3] / 255
|
||||
r_text = "{:.1f}".format(r) if r.is_integer() else "{:.9g}".format(r)
|
||||
g_text = "{:.1f}".format(g) if g.is_integer() else "{:.9g}".format(g)
|
||||
b_text = "{:.1f}".format(b) if b.is_integer() else "{:.9g}".format(b)
|
||||
a_text = "{:.1f}".format(a) if a.is_integer() else "{:.9g}".format(a)
|
||||
header.write(init_color_definition.format(
|
||||
name = init_color, r = r_text, g = g_text, b = b_text, a = a_text))
|
||||
|
||||
header.write("\n")
|
||||
|
||||
for color in colors:
|
||||
add_documentation(color)
|
||||
init_color = "sg_" + color[0].lower().replace(" ", "_")
|
||||
init_color_definition = "static SOKOL_COLOR_CONSTEXPR sg_color {name} = {init};\n"
|
||||
init_color_name = "SG_" + color[0].upper().replace(" ", "_")
|
||||
header.write(init_color_definition.format(name = init_color, init = init_color_name))
|
||||
|
||||
header.write("\n")
|
||||
|
||||
for color in colors:
|
||||
add_documentation(color)
|
||||
hex_color = "0x{0:08X}".format(color[1])
|
||||
packed_color = "SG_" + color[0].upper().replace(" ", "_") + "_RGBA32"
|
||||
packed_color_definition = "#define {name} {rgba}\n"
|
||||
header.write(packed_color_definition.format(name = packed_color, rgba = hex_color))
|
||||
|
||||
header.write("""
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
inline sg_color sg_make_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
||||
return sg_make_color_4b(r, g, b, a);
|
||||
}
|
||||
|
||||
inline sg_color sg_make_color(uint32_t rgba) {
|
||||
return sg_make_color_1i(rgba);
|
||||
}
|
||||
|
||||
inline sg_color sg_color_lerp(const sg_color& color_a, const sg_color& color_b, float amount) {
|
||||
return sg_color_lerp(&color_a, &color_b, amount);
|
||||
}
|
||||
|
||||
inline sg_color sg_color_lerp_precise(const sg_color& color_a, const sg_color& color_b, float amount) {
|
||||
return sg_color_lerp_precise(&color_a, &color_b, amount);
|
||||
}
|
||||
|
||||
inline sg_color sg_color_multiply(const sg_color& color, float scale) {
|
||||
return sg_color_multiply(&color, scale);
|
||||
}
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* SOKOL_COLOR_INCLUDED */
|
||||
|
||||
/*-- IMPLEMENTATION ----------------------------------------------------------*/
|
||||
#ifdef SOKOL_COLOR_IMPL
|
||||
#define SOKOL_COLOR_IMPL_INCLUDED (1)
|
||||
|
||||
#ifndef SOKOL_API_IMPL
|
||||
#define SOKOL_API_IMPL
|
||||
#endif
|
||||
#ifndef SOKOL_ASSERT
|
||||
#include <assert.h>
|
||||
#define SOKOL_ASSERT(c) assert(c)
|
||||
#endif
|
||||
|
||||
static inline float _sg_color_clamp(float v, float low, float high) {
|
||||
if (v < low) {
|
||||
return low;
|
||||
} else if (v > high) {
|
||||
return high;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline float _sg_color_lerp(float a, float b, float amount) {
|
||||
return a + (b - a) * amount;
|
||||
}
|
||||
|
||||
static inline float _sg_color_lerp_precise(float a, float b, float amount) {
|
||||
return ((1.0f - amount) * a) + (b * amount);
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL sg_color sg_make_color_4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
||||
sg_color result;
|
||||
result.r = r / 255.0f;
|
||||
result.g = g / 255.0f;
|
||||
result.b = b / 255.0f;
|
||||
result.a = a / 255.0f;
|
||||
return result;
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL sg_color sg_make_color_1i(uint32_t rgba) {
|
||||
return sg_make_color_4b(
|
||||
(uint8_t)(rgba >> 24),
|
||||
(uint8_t)(rgba >> 16),
|
||||
(uint8_t)(rgba >> 8),
|
||||
(uint8_t)(rgba >> 0)
|
||||
);
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL sg_color sg_color_lerp(const sg_color* color_a, const sg_color* color_b, float amount) {
|
||||
SOKOL_ASSERT(color_a);
|
||||
SOKOL_ASSERT(color_b);
|
||||
amount = _sg_color_clamp(amount, 0.0f, 1.0f);
|
||||
sg_color result;
|
||||
result.r = _sg_color_lerp(color_a->r, color_b->r, amount);
|
||||
result.g = _sg_color_lerp(color_a->g, color_b->g, amount);
|
||||
result.b = _sg_color_lerp(color_a->b, color_b->b, amount);
|
||||
result.a = _sg_color_lerp(color_a->a, color_b->a, amount);
|
||||
return result;
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL sg_color sg_color_lerp_precise(const sg_color* color_a, const sg_color* color_b, float amount) {
|
||||
SOKOL_ASSERT(color_a);
|
||||
SOKOL_ASSERT(color_b);
|
||||
amount = _sg_color_clamp(amount, 0.0f, 1.0f);
|
||||
sg_color result;
|
||||
result.r = _sg_color_lerp_precise(color_a->r, color_b->r, amount);
|
||||
result.g = _sg_color_lerp_precise(color_a->g, color_b->g, amount);
|
||||
result.b = _sg_color_lerp_precise(color_a->b, color_b->b, amount);
|
||||
result.a = _sg_color_lerp_precise(color_a->a, color_b->a, amount);
|
||||
return result;
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL sg_color sg_color_multiply(const sg_color* color, float scale) {
|
||||
SOKOL_ASSERT(color);
|
||||
sg_color result;
|
||||
result.r = color->r * scale;
|
||||
result.g = color->g * scale;
|
||||
result.b = color->b * scale;
|
||||
result.a = color->a * scale;
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* SOKOL_COLOR_IMPL */
|
||||
""")
|
||||
Vendored
+1148
File diff suppressed because it is too large
Load Diff
+4215
File diff suppressed because it is too large
Load Diff
+1785
File diff suppressed because it is too large
Load Diff
+4059
File diff suppressed because it is too large
Load Diff
Vendored
+3934
File diff suppressed because it is too large
Load Diff
Vendored
+2168
File diff suppressed because it is too large
Load Diff
Vendored
+171
@@ -0,0 +1,171 @@
|
||||
#if defined(SOKOL_IMPL) && !defined(SOKOL_MEMTRACK_IMPL)
|
||||
#define SOKOL_MEMTRACK_IMPL
|
||||
#endif
|
||||
#ifndef SOKOL_MEMTRACK_INCLUDED
|
||||
/*
|
||||
sokol_memtrack.h -- memory allocation wrapper to track memory usage
|
||||
of sokol libraries
|
||||
|
||||
Project URL: https://github.com/floooh/sokol
|
||||
|
||||
Simply include this file before the sokol header implementation includes
|
||||
and after the SOKOL_IMPL macro:
|
||||
|
||||
#define SOKOL_IMPL
|
||||
#include "sokol_memtrack.h"
|
||||
#include "sokol_app.h"
|
||||
#include "sokol_gfx.h"
|
||||
...
|
||||
|
||||
Optionally provide the following defines with your own implementations:
|
||||
|
||||
SOKOL_MEMTRACK_API_DECL - public function declaration prefix (default: extern)
|
||||
SOKOL_API_DECL - same as SOKOL_MEMTRACK_API_DECL
|
||||
SOKOL_API_IMPL - public function implementation prefix (default: -)
|
||||
|
||||
If sokol_memtrack.h is compiled as a DLL, define the following before
|
||||
including the declaration or implementation:
|
||||
|
||||
SOKOL_DLL
|
||||
|
||||
The sokol_memtrack.h header will redirect the macros SOKOL_MALLOC,
|
||||
SOKOL_FREE and SOKOL_CALLOC to use its own function wrappers which
|
||||
keep track of number and size of allocations. The wrapper functions
|
||||
then call the CRT functions malloc(), calloc() or free().
|
||||
|
||||
Naturally, only the memory management calls in sokol header code are tracked,
|
||||
not in underlying APIs such as D3D11 or OpenGL.
|
||||
|
||||
To get the current number and overall size of allocations, call:
|
||||
|
||||
smemtrack_info_t alloc_info = smemtrack_info();
|
||||
|
||||
int num_allocations = info.num_alloc;
|
||||
int allocation_size = info.num_bytes;
|
||||
|
||||
The allocation wrapper functions are *not* thread-safe (which shouldn't
|
||||
be a problem because the sokol headers don't allocate or deallocate
|
||||
in threads).
|
||||
|
||||
LICENSE
|
||||
=======
|
||||
|
||||
zlib/libpng license
|
||||
|
||||
Copyright (c) 2018 Andre Weissflog
|
||||
|
||||
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.
|
||||
*/
|
||||
#define SOKOL_MEMTRACK_INCLUDED (1)
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(SOKOL_API_DECL) && !defined(SOKOL_MEMTRACK_API_DECL)
|
||||
#define SOKOL_MEMTRACK_API_DECL SOKOL_API_DECL
|
||||
#endif
|
||||
#ifndef SOKOL_MEMTRACK_API_DECL
|
||||
#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_MEMTRACK_IMPL)
|
||||
#define SOKOL_MEMTRACK_API_DECL __declspec(dllexport)
|
||||
#elif defined(_WIN32) && defined(SOKOL_DLL)
|
||||
#define SOKOL_MEMTRACK_API_DECL __declspec(dllimport)
|
||||
#else
|
||||
#define SOKOL_MEMTRACK_API_DECL extern
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct smemtrack_info_t {
|
||||
int num_allocs;
|
||||
int num_bytes;
|
||||
} smemtrack_info_t;
|
||||
|
||||
SOKOL_MEMTRACK_API_DECL smemtrack_info_t smemtrack_info(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
#endif /* SOKOL_MEMTRACK_INCLUDED */
|
||||
|
||||
/*=== IMPLEMENTATION =========================================================*/
|
||||
#ifdef SOKOL_MEMTRACK_IMPL
|
||||
#define SOKOL_MEMTRACK_IMPL_INCLUDED (1)
|
||||
#include <stdlib.h> /* malloc, free, calloc */
|
||||
#include <string.h> /* memset */
|
||||
#include <stddef.h> /* size_t */
|
||||
|
||||
#ifndef SOKOL_API_IMPL
|
||||
#define SOKOL_API_IMPL
|
||||
#endif
|
||||
#ifndef SOKOL_DEBUG
|
||||
#ifndef NDEBUG
|
||||
#define SOKOL_DEBUG (1)
|
||||
#endif
|
||||
#endif
|
||||
#ifndef _SOKOL_PRIVATE
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define _SOKOL_PRIVATE __attribute__((unused)) static
|
||||
#else
|
||||
#define _SOKOL_PRIVATE static
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define SOKOL_MALLOC(s) _smemtrack_malloc(s)
|
||||
#define SOKOL_FREE(p) _smemtrack_free(p)
|
||||
#define SOKOL_CALLOC(n,s) _smemtrack_calloc(n,s)
|
||||
|
||||
#define _SMEMTRACK_HEADER_SIZE (16)
|
||||
|
||||
static struct {
|
||||
smemtrack_info_t state;
|
||||
} _smemtrack;
|
||||
|
||||
_SOKOL_PRIVATE void* _smemtrack_malloc(size_t size) {
|
||||
_smemtrack.state.num_allocs++;
|
||||
_smemtrack.state.num_bytes += (int) size;
|
||||
uint8_t* ptr = (uint8_t*) malloc(size + _SMEMTRACK_HEADER_SIZE);
|
||||
*(size_t*)ptr = size;
|
||||
return ptr + _SMEMTRACK_HEADER_SIZE;
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE void _smemtrack_free(void* ptr) {
|
||||
uint8_t* alloc_ptr = ((uint8_t*)ptr) - _SMEMTRACK_HEADER_SIZE;
|
||||
size_t size = *(size_t*)alloc_ptr;
|
||||
_smemtrack.state.num_allocs--;
|
||||
_smemtrack.state.num_bytes -= (int) size;
|
||||
free(alloc_ptr);
|
||||
}
|
||||
|
||||
_SOKOL_PRIVATE void* _smemtrack_calloc(size_t num, size_t size) {
|
||||
size_t mem_size = num * size;
|
||||
_smemtrack.state.num_allocs++;
|
||||
_smemtrack.state.num_bytes += (int) mem_size;
|
||||
uint8_t* ptr = (uint8_t*) malloc(mem_size + _SMEMTRACK_HEADER_SIZE);
|
||||
memset(ptr + _SMEMTRACK_HEADER_SIZE, 0, mem_size);
|
||||
*(size_t*)ptr = size;
|
||||
return ptr + _SMEMTRACK_HEADER_SIZE;
|
||||
}
|
||||
|
||||
SOKOL_API_IMPL smemtrack_info_t smemtrack_info(void) {
|
||||
return _smemtrack.state;
|
||||
}
|
||||
|
||||
#endif /* SOKOL_MEMTRACK_IMPL */
|
||||
Vendored
+2168
File diff suppressed because it is too large
Load Diff
Vendored
+1438
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user