├── .editorconfig ├── .github └── workflows │ └── main.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bindgen ├── .gitignore ├── README.md ├── gen_all.py ├── gen_ir.py ├── gen_nim.py └── gen_zig.py ├── fips.yml ├── sokol_app.h ├── sokol_args.h ├── sokol_audio.h ├── sokol_fetch.h ├── sokol_gfx.h ├── sokol_glue.h ├── sokol_time.h └── util ├── simgui.glsl ├── sokol_debugtext.h ├── sokol_fontstash.h ├── sokol_gfx_imgui.h ├── sokol_gl.h ├── sokol_imgui.h ├── sokol_memtrack.h ├── sokol_nuklear.h └── sokol_shape.h /.editorconfig: -------------------------------------------------------------------------------- 1 | root=true 2 | [**] 3 | indent_style=space 4 | indent_size=4 5 | trim_trailing_whitespace=true 6 | insert_final_newline=true 7 | 8 | 9 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: build_and_test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | windows: 7 | runs-on: windows-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - name: prepare 11 | run: | 12 | mkdir workspace 13 | cd workspace 14 | git clone https://github.com/floooh/sokol-samples 15 | cd sokol-samples 16 | - name: win64-vstudio-debug 17 | run: | 18 | cd workspace/sokol-samples 19 | python fips build win64-vstudio-debug 20 | - name: win64-vstudio-release 21 | run: | 22 | cd workspace/sokol-samples 23 | python fips build win64-vstudio-release 24 | - name: d3d11-win64-vstudio-debug 25 | run: | 26 | cd workspace/sokol-samples 27 | python fips build d3d11-win64-vstudio-debug 28 | - name: d3d11-win64-vstudio-release 29 | run: | 30 | cd workspace/sokol-samples 31 | python fips build d3d11-win64-vstudio-release 32 | - name: sapp-win64-vstudio-debug 33 | run: | 34 | cd workspace/sokol-samples 35 | python fips build sapp-win64-vstudio-debug 36 | - name: sapp-win64-vstudio-release 37 | run: | 38 | cd workspace/sokol-samples 39 | python fips build sapp-win64-vstudio-release 40 | - name: sapp-d3d11-win64-vstudio-debug 41 | run: | 42 | cd workspace/sokol-samples 43 | python fips build sapp-d3d11-win64-vstudio-debug 44 | - name: sapp-d3d11-win64-vstudio-release 45 | run: | 46 | cd workspace/sokol-samples 47 | python fips build sapp-d3d11-win64-vstudio-release 48 | - name: sokol-test sapp-win64-vstudio-debug 49 | run: | 50 | cd workspace/sokol-samples 51 | python fips run sokol-test sapp-win64-vstudio-debug 52 | mac: 53 | runs-on: macos-latest 54 | steps: 55 | - uses: actions/checkout@v1 56 | - name: prepare 57 | run: | 58 | mkdir workspace 59 | cd workspace 60 | git clone https://github.com/floooh/sokol-samples 61 | cd sokol-samples 62 | - name: osx-make-debug 63 | run: | 64 | cd workspace/sokol-samples 65 | python fips build osx-make-debug 66 | - name: osx-make-release 67 | run: | 68 | cd workspace/sokol-samples 69 | python fips build osx-make-release 70 | - name: metal-osx-make-debug 71 | run: | 72 | cd workspace/sokol-samples 73 | python fips build metal-osx-make-debug 74 | - name: metal-osx-make-release 75 | run: | 76 | cd workspace/sokol-samples 77 | python fips build metal-osx-make-release 78 | - name: sapp-metal-osx-make-debug 79 | run: | 80 | cd workspace/sokol-samples 81 | python fips build sapp-metal-osx-make-debug 82 | - name: sapp-metal-osx-make-release 83 | run: | 84 | cd workspace/sokol-samples 85 | python fips build sapp-metal-osx-make-release 86 | - name: sokol-test sapp-metal-osx-make-debug 87 | run: | 88 | cd workspace/sokol-samples 89 | python fips run sokol-test sapp-metal-osx-make-debug 90 | ios: 91 | runs-on: macos-latest 92 | steps: 93 | - uses: actions/checkout@v1 94 | - name: prepare 95 | run: | 96 | mkdir workspace 97 | cd workspace 98 | git clone https://github.com/floooh/sokol-samples 99 | cd sokol-samples 100 | - name: ios-xcode-debug 101 | run: | 102 | cd workspace/sokol-samples 103 | python fips build ios-xcode-debug -- CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO 104 | - name: ios-xcode-release 105 | run: | 106 | cd workspace/sokol-samples 107 | python fips build ios-xcode-release -- CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO 108 | - name: metal-ios-xcode-debug 109 | run: | 110 | cd workspace/sokol-samples 111 | python fips build metal-ios-xcode-debug -- CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO 112 | - name: metal-ios-xcode-release 113 | run: | 114 | cd workspace/sokol-samples 115 | python fips build metal-ios-xcode-release -- CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO 116 | - name: sapp-ios-xcode-debug 117 | run: | 118 | cd workspace/sokol-samples 119 | python fips build sapp-ios-xcode-debug -- CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO 120 | - name: sapp-metal-ios-xcode-release 121 | run: | 122 | cd workspace/sokol-samples 123 | python fips build sapp-metal-ios-xcode-debug -- CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO 124 | linux: 125 | runs-on: ubuntu-latest 126 | steps: 127 | - uses: actions/checkout@v1 128 | - name: prepare 129 | run: | 130 | sudo apt-get update 131 | sudo apt-get install libglu1-mesa-dev mesa-common-dev xorg-dev libasound-dev 132 | mkdir workspace 133 | cd workspace 134 | git clone https://github.com/floooh/sokol-samples 135 | cd sokol-samples 136 | - name: linux-make-debug 137 | run: | 138 | cd workspace/sokol-samples 139 | python fips build linux-make-debug 140 | - name: linux-make-release 141 | run: | 142 | cd workspace/sokol-samples 143 | python fips build linux-make-release 144 | - name: sapp-linux-make-debug 145 | run: | 146 | cd workspace/sokol-samples 147 | python fips build sapp-linux-make-debug 148 | - name: sapp-linux-make-release 149 | run: | 150 | cd workspace/sokol-samples 151 | python fips build sapp-linux-make-release 152 | - name: sapp-linux-make-debug 153 | run: | 154 | cd workspace/sokol-samples 155 | python fips run sokol-test sapp-linux-make-debug 156 | emscripten: 157 | runs-on: ubuntu-latest 158 | steps: 159 | - uses: actions/checkout@v1 160 | - name: prepare 161 | run: | 162 | sudo apt-get install ninja-build 163 | mkdir workspace 164 | cd workspace 165 | git clone https://github.com/floooh/sokol-samples 166 | cd sokol-samples 167 | python fips emsdk install latest 168 | - name: sapp-webgl2-wasm-ninja-debug 169 | run: | 170 | cd workspace/sokol-samples 171 | python fips build sapp-webgl2-wasm-ninja-debug 172 | - name: sapp-webgl2-wasm-ninja-release 173 | run: | 174 | cd workspace/sokol-samples 175 | python fips build sapp-webgl2-wasm-ninja-release 176 | - name: wasm-ninja-debug 177 | run: | 178 | cd workspace/sokol-samples 179 | python fips make cube-emsc wasm-ninja-debug 180 | - name: wasm-ninja-release 181 | run: | 182 | cd workspace/sokol-samples 183 | python fips make cube-emsc wasm-ninja-release 184 | android: 185 | runs-on: ubuntu-latest 186 | steps: 187 | - uses: actions/checkout@v1 188 | - uses: actions/setup-java@v1 189 | with: 190 | java-version: '8' 191 | - name: prepare 192 | run: | 193 | mkdir workspace 194 | cd workspace 195 | git clone https://github.com/floooh/sokol-samples 196 | cd sokol-samples 197 | yes | python fips setup android 198 | - name: sapp-android-make-debug 199 | run: | 200 | cd workspace/sokol-samples 201 | python fips build sapp-android-make-debug 202 | - name: sapp-android-make-release 203 | run: | 204 | cd workspace/sokol-samples 205 | python fips build sapp-android-make-release 206 | uwp: 207 | runs-on: windows-latest 208 | steps: 209 | - uses: actions/checkout@v1 210 | - name: prepare 211 | run: | 212 | mkdir workspace 213 | cd workspace 214 | git clone https://github.com/floooh/sokol-samples 215 | cd sokol-samples 216 | - name: sapp-uwp-vstudio-debug 217 | run: | 218 | cd workspace/sokol-samples 219 | python fips build sapp-uwp-vstudio-debug 220 | - name: sapp-uwp-vstudio-release 221 | run: | 222 | cd workspace/sokol-samples 223 | python fips build sapp-uwp-vstudio-release 224 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | #>fips 3 | # this area is managed by fips, do not edit 4 | .fips-* 5 | *.pyc 6 | # 1.0f) ? 0.0f : g; 215 | sg_begin_default_pass(&pass_action, sapp_width(), sapp_height()); 216 | sg_end_pass(); 217 | sg_commit(); 218 | } 219 | 220 | void cleanup(void) { 221 | sg_shutdown(); 222 | } 223 | 224 | sapp_desc sokol_main(int argc, char* argv[]) { 225 | return (sapp_desc){ 226 | .init_cb = init, 227 | .frame_cb = frame, 228 | .cleanup_cb = cleanup, 229 | .width = 400, 230 | .height = 300, 231 | .window_title = "Clear Sample", 232 | }; 233 | } 234 | ``` 235 | 236 | # sokol_audio.h 237 | 238 | A minimal audio-streaming API: 239 | 240 | - you provide a mono- or stereo-stream of 32-bit float samples which sokol_audio.h forwards into platform-specific backends 241 | - two ways to provide the data: 242 | 1. directly fill backend audio buffer from your callback function running in the audio thread 243 | 2. alternatively push small packets of audio data from your main loop, 244 | or a separate thread created by you 245 | - platform backends: 246 | - Windows: WASAPI 247 | - macOS/iOS: CoreAudio 248 | - Linux: ALSA 249 | - emscripten: WebAudio + ScriptProcessorNode (doesn't use the emscripten-provided OpenAL or SDL Audio wrappers) 250 | 251 | A simple mono square-wave generator using the callback model: 252 | 253 | ```c 254 | // the sample callback, running in audio thread 255 | static void stream_cb(float* buffer, int num_frames, int num_channels) { 256 | assert(1 == num_channels); 257 | static uint32_t count = 0; 258 | for (int i = 0; i < num_frames; i++) { 259 | buffer[i] = (count++ & (1<<3)) ? 0.5f : -0.5f; 260 | } 261 | } 262 | 263 | int main() { 264 | // init sokol-audio with default params 265 | saudio_setup(&(saudio_desc){ 266 | .stream_cb = stream_cb 267 | }); 268 | 269 | // run main loop 270 | ... 271 | 272 | // shutdown sokol-audio 273 | saudio_shutdown(); 274 | return 0; 275 | ``` 276 | 277 | The same code using the push-model 278 | 279 | ```c 280 | #define BUF_SIZE (32) 281 | int main() { 282 | // init sokol-audio with default params, no callback 283 | saudio_setup(&(saudio_desc){0}); 284 | assert(saudio_channels() == 1); 285 | 286 | // a small intermediate buffer so we don't need to push 287 | // individual samples, which would be quite inefficient 288 | float buf[BUF_SIZE]; 289 | int buf_pos = 0; 290 | uint32_t count = 0; 291 | 292 | // push samples from main loop 293 | bool done = false; 294 | while (!done) { 295 | // generate and push audio samples... 296 | int num_frames = saudio_expect(); 297 | for (int i = 0; i < num_frames; i++) { 298 | // simple square wave generator 299 | buf[buf_pos++] = (count++ & (1<<3)) ? 0.5f : -0.5f; 300 | if (buf_pos == BUF_SIZE) { 301 | buf_pos = 0; 302 | saudio_push(buf, BUF_SIZE); 303 | } 304 | } 305 | // handle other per-frame stuff... 306 | ... 307 | } 308 | 309 | // shutdown sokol-audio 310 | saudio_shutdown(); 311 | return 0; 312 | } 313 | ``` 314 | 315 | # sokol_fetch.h 316 | 317 | Load entire files, or stream data asynchronously over HTTP (emscripten/wasm) 318 | or the local filesystem (all native platforms). 319 | 320 | Simple C99 example loading a file into a static buffer: 321 | 322 | ```c 323 | #include "sokol_fetch.h" 324 | 325 | static void response_callback(const sfetch_response*); 326 | 327 | #define MAX_FILE_SIZE (1024*1024) 328 | static uint8_t buffer[MAX_FILE_SIZE]; 329 | 330 | // application init 331 | static void init(void) { 332 | ... 333 | // setup sokol-fetch with default config: 334 | sfetch_setup(&(sfetch_desc_t){0}); 335 | 336 | // start loading a file into a statically allocated buffer: 337 | sfetch_send(&(sfetch_request_t){ 338 | .path = "hello_world.txt", 339 | .callback = response_callback 340 | .buffer_ptr = buffer, 341 | .buffer_size = sizeof(buffer) 342 | }); 343 | } 344 | 345 | // per frame... 346 | static void frame(void) { 347 | ... 348 | // need to call sfetch_dowork() once per frame to 'turn the gears': 349 | sfetch_dowork(); 350 | ... 351 | } 352 | 353 | // the response callback is where the interesting stuff happens: 354 | static void response_callback(const sfetch_response_t* response) { 355 | if (response->fetched) { 356 | // data has been loaded into the provided buffer, do something 357 | // with the data... 358 | const void* data = response->buffer_ptr; 359 | uint64_t data_size = response->fetched_size; 360 | } 361 | // the finished flag is set both on success and failure 362 | if (response->failed) { 363 | // oops, something went wrong 364 | switch (response->error_code) { 365 | SFETCH_ERROR_FILE_NOT_FOUND: ... 366 | SFETCH_ERROR_BUFFER_TOO_SMALL: ... 367 | ... 368 | } 369 | } 370 | } 371 | 372 | // application shutdown 373 | static void shutdown(void) { 374 | ... 375 | sfetch_shutdown(); 376 | ... 377 | } 378 | ``` 379 | 380 | # sokol_time.h: 381 | 382 | Simple cross-platform time measurement: 383 | 384 | ```c 385 | #include "sokol_time.h" 386 | ... 387 | /* initialize sokol_time */ 388 | stm_setup(); 389 | 390 | /* take start timestamp */ 391 | uint64_t start = stm_now(); 392 | 393 | ...some code to measure... 394 | 395 | /* compute elapsed time */ 396 | uint64_t elapsed = stm_since(start); 397 | 398 | /* convert to time units */ 399 | double seconds = stm_sec(elapsed); 400 | double milliseconds = stm_ms(elapsed); 401 | double microseconds = stm_us(elapsed); 402 | double nanoseconds = stm_ns(elapsed); 403 | 404 | /* difference between 2 time stamps */ 405 | uint64_t start = stm_now(); 406 | ... 407 | uint64_t end = stm_now(); 408 | uint64_t elapsed = stm_diff(end, start); 409 | 410 | /* compute a 'lap time' (e.g. for fps) */ 411 | uint64_t last_time = 0; 412 | while (!done) { 413 | ...render something... 414 | double frame_time_ms = stm_ms(stm_laptime(&last_time)); 415 | } 416 | ``` 417 | 418 | # sokol_args.h 419 | 420 | Unified argument parsing for web and native apps. Uses argc/argv on native 421 | platforms and the URL query string on the web. 422 | 423 | Example URL with one arg: 424 | 425 | https://floooh.github.io/tiny8bit/kc85.html?type=kc85_4 426 | 427 | The same as command line app: 428 | 429 | > kc85 type=kc85_4 430 | 431 | Parsed like this: 432 | 433 | ```c 434 | #include "sokol_args.h" 435 | 436 | int main(int argc, char* argv[]) { 437 | sargs_setup(&(sargs_desc){ .argc=argc, .argv=argv }); 438 | if (sargs_exists("type")) { 439 | if (sargs_equals("type", "kc85_4")) { 440 | // start as KC85/4 441 | } 442 | else if (sargs_equals("type", "kc85_3")) { 443 | // start as KC85/3 444 | } 445 | else { 446 | // start as KC85/2 447 | } 448 | } 449 | sargs_shutdown(); 450 | return 0; 451 | } 452 | ``` 453 | 454 | See the sokol_args.h header for a more complete documentation, and the [Tiny 455 | Emulators](https://floooh.github.io/tiny8bit/) for more interesting usage examples. 456 | -------------------------------------------------------------------------------- /bindgen/.gitignore: -------------------------------------------------------------------------------- 1 | *.json 2 | *.nim 3 | *.zig 4 | __pycache__/ 5 | sokol-nim/ 6 | sokol-zig/ 7 | -------------------------------------------------------------------------------- /bindgen/README.md: -------------------------------------------------------------------------------- 1 | ## Language Binding Generation Scripts 2 | 3 | ### Zig 4 | 5 | To update the Zig bindings: 6 | 7 | ``` 8 | > cd sokol/bindgen 9 | > git clone https://github.com/floooh/sokol-zig 10 | > git clone https://github.com/floooh/sokol-nim 11 | > python3 gen_all.py 12 | ``` 13 | 14 | Test and run samples: 15 | 16 | ``` 17 | > cd sokol/bindgen/sokol-zig 18 | > zig build run-clear 19 | > zig build run-triangle 20 | > zig build run-cube 21 | ... 22 | ``` 23 | -------------------------------------------------------------------------------- /bindgen/gen_all.py: -------------------------------------------------------------------------------- 1 | import os, gen_nim, gen_zig 2 | 3 | tasks = [ 4 | [ '../sokol_gfx.h', 'sg_', [] ], 5 | [ '../sokol_app.h', 'sapp_', [] ], 6 | [ '../sokol_time.h', 'stm_', [] ], 7 | [ '../sokol_audio.h', 'saudio_', [] ], 8 | [ '../util/sokol_gl.h', 'sgl_', ['sg_'] ], 9 | [ '../util/sokol_debugtext.h', 'sdtx_', ['sg_'] ], 10 | [ '../util/sokol_shape.h', 'sshape_', ['sg_'] ], 11 | ] 12 | 13 | # Nim 14 | gen_nim.prepare() 15 | for task in tasks: 16 | c_header_path = task[0] 17 | main_prefix = task[1] 18 | dep_prefixes = task[2] 19 | gen_nim.gen(c_header_path, main_prefix, dep_prefixes) 20 | 21 | # Zig 22 | gen_zig.prepare() 23 | for task in tasks: 24 | c_header_path = task[0] 25 | main_prefix = task[1] 26 | dep_prefixes = task[2] 27 | gen_zig.gen(c_header_path, main_prefix, dep_prefixes) 28 | -------------------------------------------------------------------------------- /bindgen/gen_ir.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Generate an intermediate representation of a clang AST dump. 3 | #------------------------------------------------------------------------------- 4 | import json, sys, subprocess 5 | 6 | def is_api_decl(decl, prefix): 7 | if 'name' in decl: 8 | return decl['name'].startswith(prefix) 9 | elif decl['kind'] == 'EnumDecl': 10 | # an anonymous enum, check if the items start with the prefix 11 | return decl['inner'][0]['name'].lower().startswith(prefix) 12 | else: 13 | return False 14 | 15 | def is_dep_decl(decl, dep_prefixes): 16 | for prefix in dep_prefixes: 17 | if is_api_decl(decl, prefix): 18 | return True 19 | return False 20 | 21 | def dep_prefix(decl, dep_prefixes): 22 | for prefix in dep_prefixes: 23 | if is_api_decl(decl, prefix): 24 | return prefix 25 | return None 26 | 27 | def filter_types(str): 28 | return str.replace('_Bool', 'bool') 29 | 30 | def parse_struct(decl): 31 | outp = {} 32 | outp['kind'] = 'struct' 33 | outp['name'] = decl['name'] 34 | outp['fields'] = [] 35 | for item_decl in decl['inner']: 36 | if item_decl['kind'] != 'FieldDecl': 37 | sys.exit(f"ERROR: Structs must only contain simple fields ({decl['name']})") 38 | item = {} 39 | if 'name' in item_decl: 40 | item['name'] = item_decl['name'] 41 | item['type'] = filter_types(item_decl['type']['qualType']) 42 | outp['fields'].append(item) 43 | return outp 44 | 45 | def parse_enum(decl): 46 | outp = {} 47 | if 'name' in decl: 48 | outp['kind'] = 'enum' 49 | outp['name'] = decl['name'] 50 | needs_value = False 51 | else: 52 | outp['kind'] = 'consts' 53 | needs_value = True 54 | outp['items'] = [] 55 | for item_decl in decl['inner']: 56 | if item_decl['kind'] == 'EnumConstantDecl': 57 | item = {} 58 | item['name'] = item_decl['name'] 59 | if 'inner' in item_decl: 60 | const_expr = item_decl['inner'][0] 61 | if const_expr['kind'] != 'ConstantExpr': 62 | sys.exit(f"ERROR: Enum values must be a ConstantExpr ({decl['name']})") 63 | if const_expr['valueCategory'] != 'rvalue': 64 | sys.exit(f"ERROR: Enum value ConstantExpr must be 'rvalue' ({decl['name']})") 65 | if not ((len(const_expr['inner']) == 1) and (const_expr['inner'][0]['kind'] == 'IntegerLiteral')): 66 | sys.exit(f"ERROR: Enum value ConstantExpr must have exactly one IntegerLiteral ({decl['name']})") 67 | item['value'] = const_expr['inner'][0]['value'] 68 | if needs_value and 'value' not in item: 69 | sys.exit(f"ERROR: anonymous enum items require an explicit value") 70 | outp['items'].append(item) 71 | return outp 72 | 73 | def parse_func(decl): 74 | outp = {} 75 | outp['kind'] = 'func' 76 | outp['name'] = decl['name'] 77 | outp['type'] = filter_types(decl['type']['qualType']) 78 | outp['params'] = [] 79 | if 'inner' in decl: 80 | for param in decl['inner']: 81 | if param['kind'] != 'ParmVarDecl': 82 | print(f"warning: ignoring func {decl['name']} (unsupported parameter type)") 83 | return None 84 | outp_param = {} 85 | outp_param['name'] = param['name'] 86 | outp_param['type'] = filter_types(param['type']['qualType']) 87 | outp['params'].append(outp_param) 88 | return outp 89 | 90 | def parse_decl(decl): 91 | kind = decl['kind'] 92 | if kind == 'RecordDecl': 93 | return parse_struct(decl) 94 | elif kind == 'EnumDecl': 95 | return parse_enum(decl) 96 | elif kind == 'FunctionDecl': 97 | return parse_func(decl) 98 | else: 99 | return None 100 | 101 | def clang(csrc_path): 102 | cmd = ['clang', '-Xclang', '-ast-dump=json', '-c' ] 103 | cmd.append(csrc_path) 104 | return subprocess.check_output(cmd) 105 | 106 | def gen(header_path, source_path, module, main_prefix, dep_prefixes): 107 | ast = clang(source_path) 108 | inp = json.loads(ast) 109 | outp = {} 110 | outp['module'] = module 111 | outp['prefix'] = main_prefix 112 | outp['dep_prefixes'] = dep_prefixes 113 | outp['decls'] = [] 114 | for decl in inp['inner']: 115 | is_dep = is_dep_decl(decl, dep_prefixes) 116 | if is_api_decl(decl, main_prefix) or is_dep: 117 | outp_decl = parse_decl(decl) 118 | if outp_decl is not None: 119 | outp_decl['is_dep'] = is_dep 120 | outp_decl['dep_prefix'] = dep_prefix(decl, dep_prefixes) 121 | outp['decls'].append(outp_decl) 122 | return outp 123 | -------------------------------------------------------------------------------- /bindgen/gen_nim.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Read output of gen_json.py and generate Zig language bindings. 3 | # 4 | # Nim coding style: 5 | # - types and constants are PascalCase 6 | # - functions, parameters, and fields are camelCase 7 | #------------------------------------------------------------------------------- 8 | import gen_ir 9 | import json, re, os, shutil 10 | 11 | module_names = { 12 | 'sg_': 'gfx', 13 | 'sapp_': 'app', 14 | 'stm_': 'time', 15 | 'saudio_': 'audio', 16 | 'sgl_': 'gl', 17 | 'sdtx_': 'debugtext', 18 | 'sshape_': 'shape', 19 | } 20 | 21 | c_source_paths = { 22 | 'sg_': 'sokol-zig/src/sokol/c/sokol_app_gfx.c', 23 | 'sapp_': 'sokol-zig/src/sokol/c/sokol_app_gfx.c', 24 | 'stm_': 'sokol-zig/src/sokol/c/sokol_time.c', 25 | 'saudio_': 'sokol-zig/src/sokol/c/sokol_audio.c', 26 | 'sgl_': 'sokol-zig/src/sokol/c/sokol_gl.c', 27 | 'sdtx_': 'sokol-zig/src/sokol/c/sokol_debugtext.c', 28 | 'sshape_': 'sokol-zig/src/sokol/c/sokol_shape.c', 29 | } 30 | 31 | func_name_ignores = [ 32 | 'sdtx_printf', 33 | 'sdtx_vprintf', 34 | ] 35 | 36 | func_name_overrides = { 37 | 'sgl_error': 'sgl_get_error', # 'error' is reserved in Zig 38 | 'sgl_deg': 'sgl_as_degrees', 39 | 'sgl_rad': 'sgl_as_radians', 40 | } 41 | 42 | struct_field_type_overrides = { 43 | 'sg_context_desc.color_format': 'int', 44 | 'sg_context_desc.depth_format': 'int', 45 | } 46 | 47 | prim_types = { 48 | 'int': 'int32', 49 | 'bool': 'bool', 50 | 'char': 'char', 51 | 'int8_t': 'int8', 52 | 'uint8_t': 'uint8', 53 | 'int16_t': 'int16', 54 | 'uint16_t': 'uint16', 55 | 'int32_t': 'int32', 56 | 'uint32_t': 'uint32', 57 | 'int64_t': 'int64', 58 | 'uint64_t': 'uint64', 59 | 'float': 'float32', 60 | 'double': 'float64', 61 | 'uintptr_t': 'uint', 62 | 'intptr_t': 'int', 63 | 'size_t': 'int', 64 | } 65 | 66 | prim_defaults = { 67 | 'int': '0', 68 | 'bool': 'false', 69 | 'int8_t': '0', 70 | 'uint8_t': '0', 71 | 'int16_t': '0', 72 | 'uint16_t': '0', 73 | 'int32_t': '0', 74 | 'uint32_t': '0', 75 | 'int64_t': '0', 76 | 'uint64_t': '0', 77 | 'float': '0.0', 78 | 'double': '0.0', 79 | 'uintptr_t': '0', 80 | 'intptr_t': '0', 81 | 'size_t': '0' 82 | } 83 | 84 | struct_types = [] 85 | enum_types = [] 86 | enum_items = {} 87 | out_lines = '' 88 | 89 | def reset_globals(): 90 | global struct_types 91 | global enum_types 92 | global enum_items 93 | global out_lines 94 | struct_types = [] 95 | enum_types = [] 96 | enum_items = {} 97 | out_lines = '' 98 | 99 | re_1d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]$") 100 | re_2d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]\[\d*\]$") 101 | 102 | def l(s): 103 | global out_lines 104 | out_lines += s + '\n' 105 | 106 | def as_nim_prim_type(s): 107 | return prim_types[s] 108 | 109 | # prefix_bla_blub(_t) => (dep.)BlaBlub 110 | def as_nim_struct_type(s, prefix): 111 | parts = s.lower().split('_') 112 | outp = '' if s.startswith(prefix) else f'{parts[0]}.' 113 | for part in parts[1:]: 114 | if (part != 't'): 115 | outp += part.capitalize() 116 | return outp 117 | 118 | # prefix_bla_blub(_t) => (dep.)BlaBlub 119 | def as_nim_enum_type(s, prefix): 120 | parts = s.lower().split('_') 121 | outp = '' if s.startswith(prefix) else f'{parts[0]}.' 122 | for part in parts[1:]: 123 | if (part != 't'): 124 | outp += part.capitalize() 125 | return outp 126 | 127 | # prefix_bla_blub(_t) => (dep.)BlaBlub 128 | def as_nim_const_type(s, prefix): 129 | parts = s.lower().split('_') 130 | outp = '' if s.startswith(prefix) else f'{parts[0]}.' 131 | for part in parts[1:]: 132 | if (part != 't'): 133 | outp += part.capitalize() 134 | return outp 135 | 136 | def check_struct_field_type_override(struct_name, field_name, orig_type): 137 | s = f"{struct_name}.{field_name}" 138 | if s in struct_field_type_overrides: 139 | return struct_field_type_overrides[s] 140 | else: 141 | return orig_type 142 | 143 | def check_func_name_ignore(func_name): 144 | return func_name in func_name_ignores 145 | 146 | def check_func_name_override(func_name): 147 | if func_name in func_name_overrides: 148 | return func_name_overrides[func_name] 149 | else: 150 | return func_name 151 | 152 | def trim_prefix(s, prefix): 153 | outp = s; 154 | if outp.lower().startswith(prefix.lower()): 155 | outp = outp[len(prefix):] 156 | return outp 157 | 158 | # PREFIX_BLA_BLUB to bla_blub 159 | def as_snake_case(s, prefix = ""): 160 | return trim_prefix(s, prefix).lower() 161 | 162 | # prefix_bla_blub => blaBlub 163 | def as_camel_case(s, prefix = ""): 164 | parts = trim_prefix(s, prefix).lower().split('_') 165 | outp = parts[0] 166 | for part in parts[1:]: 167 | outp += part.capitalize() 168 | return outp 169 | 170 | # prefix_bla_blub => BlaBlub 171 | def as_pascal_case(s, prefix): 172 | parts = trim_prefix(s, prefix).lower().split('_') 173 | outp = "" 174 | for part in parts: 175 | outp += part.capitalize() 176 | return outp 177 | 178 | # PREFIX_ENUM_BLA => Bla, _PREFIX_ENUM_BLA => Bla 179 | def as_enum_item_name(s): 180 | outp = s 181 | if outp.startswith('_'): 182 | outp = outp[1:] 183 | parts = outp.lower().split('_')[2:] 184 | outp = "" 185 | for part in parts: 186 | outp += part.capitalize() 187 | if outp[0].isdigit(): 188 | outp = 'N' + outp 189 | return outp 190 | 191 | def enum_default_item(enum_name): 192 | return enum_items[enum_name][0] 193 | 194 | def is_prim_type(s): 195 | return s in prim_types 196 | 197 | def is_struct_type(s): 198 | return s in struct_types 199 | 200 | def is_enum_type(s): 201 | return s in enum_types 202 | 203 | def is_string_ptr(s): 204 | return s == "const char *" 205 | 206 | def is_const_void_ptr(s): 207 | return s == "const void *" 208 | 209 | def is_void_ptr(s): 210 | return s == "void *" 211 | 212 | def is_const_prim_ptr(s): 213 | for prim_type in prim_types: 214 | if s == f"const {prim_type} *": 215 | return True 216 | return False 217 | 218 | def is_prim_ptr(s): 219 | for prim_type in prim_types: 220 | if s == f"{prim_type} *": 221 | return True 222 | return False 223 | 224 | def is_const_struct_ptr(s): 225 | for struct_type in struct_types: 226 | if s == f"const {struct_type} *": 227 | return True 228 | return False 229 | 230 | def is_func_ptr(s): 231 | return '(*)' in s 232 | 233 | def is_1d_array_type(s): 234 | return re_1d_array.match(s) 235 | 236 | def is_2d_array_type(s): 237 | return re_2d_array.match(s) 238 | 239 | def type_default_value(s): 240 | return prim_defaults[s] 241 | 242 | def extract_array_type(s): 243 | return s[:s.index('[')].strip() 244 | 245 | def extract_array_nums(s): 246 | return s[s.index('['):].replace('[', ' ').replace(']', ' ').split() 247 | 248 | def extract_ptr_type(s): 249 | tokens = s.split() 250 | if tokens[0] == 'const': 251 | return tokens[1] 252 | else: 253 | return tokens[0] 254 | 255 | def as_extern_c_arg_type(arg_type, prefix): 256 | if arg_type == "void": 257 | return "void" 258 | elif is_prim_type(arg_type): 259 | return as_nim_prim_type(arg_type) 260 | elif is_struct_type(arg_type): 261 | return as_nim_struct_type(arg_type, prefix) 262 | elif is_enum_type(arg_type): 263 | return as_nim_enum_type(arg_type, prefix) 264 | elif is_void_ptr(arg_type): 265 | return "pointer" 266 | elif is_const_void_ptr(arg_type): 267 | return "pointer" 268 | elif is_string_ptr(arg_type): 269 | return "ptr uint8" 270 | elif is_const_struct_ptr(arg_type): 271 | return f"ptr {as_nim_struct_type(extract_ptr_type(arg_type), prefix)}" 272 | elif is_prim_ptr(arg_type): 273 | return f"[*c] {as_nim_prim_type(extract_ptr_type(arg_type))}" 274 | elif is_const_prim_ptr(arg_type): 275 | return f"ptr {as_nim_prim_type(extract_ptr_type(arg_type))}" 276 | else: 277 | return '??? (as_extern_c_arg_type)' 278 | 279 | def as_nim_arg_type(arg_prefix, arg_type, prefix): 280 | # NOTE: if arg_prefix is None, the result is used as return value 281 | pre = "" if arg_prefix is None else arg_prefix 282 | if arg_type == "void": 283 | if arg_prefix is None: 284 | return "void" 285 | else: 286 | return "" 287 | elif is_prim_type(arg_type): 288 | return pre + as_nim_prim_type(arg_type) 289 | elif is_struct_type(arg_type): 290 | return pre + as_nim_struct_type(arg_type, prefix) 291 | elif is_enum_type(arg_type): 292 | return pre + as_nim_enum_type(arg_type, prefix) 293 | elif is_void_ptr(arg_type): 294 | return pre + "pointer" 295 | elif is_const_void_ptr(arg_type): 296 | return pre + "pointer" 297 | elif is_string_ptr(arg_type): 298 | return pre + "cstring" 299 | elif is_const_struct_ptr(arg_type): 300 | return pre + f"ptr {as_nim_struct_type(extract_ptr_type(arg_type), prefix)}" 301 | elif is_prim_ptr(arg_type): 302 | return pre + f"ptr {as_nim_prim_type(extract_ptr_type(arg_type))}" 303 | elif is_const_prim_ptr(arg_type): 304 | return pre + f"ptr {as_nim_prim_type(extract_ptr_type(arg_type))}" 305 | else: 306 | return arg_prefix + "??? (as_nim_arg_type)" 307 | 308 | # get C-style arguments of a function pointer as string 309 | def funcptr_args_c(field_type, prefix): 310 | tokens = field_type[field_type.index('(*)')+4:-1].split(',') 311 | s = "" 312 | n = 0 313 | for token in tokens: 314 | n += 1 315 | arg_type = token.strip() 316 | if s != "": 317 | s += ", " 318 | c_arg = f"a{n}:" + as_extern_c_arg_type(arg_type, prefix) 319 | if (c_arg == "void"): 320 | return "" 321 | else: 322 | s += c_arg 323 | if s == "a1:void": 324 | s = "" 325 | return s 326 | 327 | # get C-style result of a function pointer as string 328 | def funcptr_res_c(field_type): 329 | res_type = field_type[:field_type.index('(*)')].strip() 330 | if res_type == 'void': 331 | return '' 332 | elif is_const_void_ptr(res_type): 333 | return ':pointer' 334 | else: 335 | return '???' 336 | 337 | def funcdecl_args_c(decl, prefix): 338 | s = "" 339 | for param_decl in decl['params']: 340 | if s != "": 341 | s += ", " 342 | arg_type = param_decl['type'] 343 | s += as_extern_c_arg_type(arg_type, prefix) 344 | return s 345 | 346 | def funcdecl_args_nim(decl, prefix): 347 | s = "" 348 | for param_decl in decl['params']: 349 | if s != "": 350 | s += ", " 351 | arg_name = param_decl['name'] 352 | arg_type = param_decl['type'] 353 | s += f"{as_nim_arg_type(f'{arg_name}:', arg_type, prefix)}" 354 | return s 355 | 356 | def funcdecl_res_c(decl, prefix): 357 | decl_type = decl['type'] 358 | res_type = decl_type[:decl_type.index('(')].strip() 359 | return as_extern_c_arg_type(res_type, prefix) 360 | 361 | def funcdecl_res_nim(decl, prefix): 362 | decl_type = decl['type'] 363 | res_type = decl_type[:decl_type.index('(')].strip() 364 | nim_res_type = as_nim_arg_type(None, res_type, prefix) 365 | if nim_res_type == "": 366 | nim_res_type = "void" 367 | return nim_res_type 368 | 369 | def gen_struct(decl, prefix, use_raw_name=False): 370 | struct_name = decl['name'] 371 | nim_type = struct_name if use_raw_name else as_nim_struct_type(struct_name, prefix) 372 | l(f"type {nim_type}* = object") 373 | isPublic = True 374 | for field in decl['fields']: 375 | field_name = field['name'] 376 | if field_name == "__pad": 377 | # FIXME: these should be guarded by SOKOL_ZIG_BINDINGS, but aren't? 378 | continue 379 | isPublic = not field_name.startswith("_") 380 | field_name = as_camel_case(field_name, "_") 381 | if field_name == "ptr": 382 | field_name = "source" 383 | if field_name == "ref": 384 | field_name = "`ref`" 385 | if field_name == "type": 386 | field_name = "`type`" 387 | if isPublic: 388 | field_name += "*" 389 | field_type = field['type'] 390 | field_type = check_struct_field_type_override(struct_name, field_name, field_type) 391 | if is_prim_type(field_type): 392 | l(f" {field_name}:{as_nim_prim_type(field_type)}") 393 | elif is_struct_type(field_type): 394 | l(f" {field_name}:{as_nim_struct_type(field_type, prefix)}") 395 | elif is_enum_type(field_type): 396 | l(f" {field_name}:{as_nim_enum_type(field_type, prefix)}") 397 | elif is_string_ptr(field_type): 398 | l(f" {field_name}:cstring") 399 | elif is_const_void_ptr(field_type): 400 | l(f" {field_name}:pointer") 401 | elif is_void_ptr(field_type): 402 | l(f" {field_name}:pointer") 403 | elif is_const_prim_ptr(field_type): 404 | l(f" {field_name}:ptr {as_nim_prim_type(extract_ptr_type(field_type))}") 405 | elif is_func_ptr(field_type): 406 | l(f" {field_name}:proc({funcptr_args_c(field_type, prefix)}){funcptr_res_c(field_type)} {{.cdecl.}}") 407 | elif is_1d_array_type(field_type): 408 | array_type = extract_array_type(field_type) 409 | array_nums = extract_array_nums(field_type) 410 | if is_prim_type(array_type) or is_struct_type(array_type): 411 | if is_prim_type(array_type): 412 | nim_type = as_nim_prim_type(array_type) 413 | elif is_struct_type(array_type): 414 | nim_type = as_nim_struct_type(array_type, prefix) 415 | elif is_enum_type(array_type): 416 | nim_type = as_nim_enum_type(array_type, prefix) 417 | else: 418 | nim_type = '??? (array type)' 419 | t0 = f"array[{array_nums[0]}, {nim_type}]" 420 | t0_slice = f"[]const {nim_type}" 421 | t1 = f"[_]{nim_type}" 422 | l(f" {field_name}:{t0}") 423 | elif is_const_void_ptr(array_type): 424 | l(f" {field_name}:array[{array_nums[0]}, pointer]") 425 | else: 426 | l(f"// FIXME: ??? array {field_name}:{field_type} => {array_type} [{array_nums[0]}]") 427 | elif is_2d_array_type(field_type): 428 | array_type = extract_array_type(field_type) 429 | array_nums = extract_array_nums(field_type) 430 | if is_prim_type(array_type): 431 | nim_type = as_nim_prim_type(array_type) 432 | def_val = type_default_value(array_type) 433 | elif is_struct_type(array_type): 434 | nim_type = as_nim_struct_type(array_type, prefix) 435 | def_val = ".{ }" 436 | else: 437 | nim_type = "???" 438 | def_val = "???" 439 | t0 = f"array[{array_nums[0]}, array[{array_nums[1]}, {nim_type}]]" 440 | l(f" {field_name}:{t0}") 441 | else: 442 | l(f"// FIXME: {field_name}:{field_type};") 443 | l("") 444 | 445 | def gen_consts(decl, prefix): 446 | l("const") 447 | for item in decl['items']: 448 | l(f" {trim_prefix(item['name'], prefix)}* = {item['value']}") 449 | l("") 450 | 451 | def gen_enum(decl, prefix): 452 | item_names_by_value = {} 453 | value = -1 454 | hasForceU32 = False 455 | hasExplicitValues = False 456 | for item in decl['items']: 457 | if item['name'].endswith("_FORCE_U32"): 458 | hasForceU32 = True 459 | else: 460 | if 'value' in item: 461 | hasExplicitValues = True 462 | value = int(item['value']) 463 | else: 464 | value += 1 465 | item_names_by_value[value] = as_enum_item_name(item['name']); 466 | if hasForceU32: 467 | l(f"type {as_nim_enum_type(decl['name'], prefix)}* {{.pure, size:4.}} = enum") 468 | else: 469 | l(f"type {as_nim_enum_type(decl['name'], prefix)}* {{.pure.}} = enum") 470 | if hasExplicitValues: 471 | # Nim requires explicit enum values to be declared in ascending order 472 | for value in sorted(item_names_by_value): 473 | name = item_names_by_value[value] 474 | l(f" {name} = {value},") 475 | else: 476 | for name in item_names_by_value.values(): 477 | l(f" {name},") 478 | l("") 479 | 480 | def gen_func_nim(decl, prefix): 481 | c_func_name = decl['name'] 482 | nim_func_name = as_camel_case(decl['name'], prefix) 483 | nim_res_type = funcdecl_res_nim(decl, prefix) 484 | l(f"proc {nim_func_name}*({funcdecl_args_nim(decl, prefix)}):{funcdecl_res_nim(decl, prefix)} {{.cdecl, importc:\"{decl['name']}\".}}") 485 | l("") 486 | 487 | def pre_parse(inp): 488 | global struct_types 489 | global enum_types 490 | for decl in inp['decls']: 491 | kind = decl['kind'] 492 | if kind == 'struct': 493 | struct_types.append(decl['name']) 494 | elif kind == 'enum': 495 | enum_name = decl['name'] 496 | enum_types.append(enum_name) 497 | enum_items[enum_name] = [] 498 | for item in decl['items']: 499 | enum_items[enum_name].append(as_enum_item_name(item['name'])) 500 | 501 | def gen_imports(inp, dep_prefixes): 502 | for dep_prefix in dep_prefixes: 503 | dep_module_name = module_names[dep_prefix] 504 | l(f'import {dep_module_name}') 505 | l('') 506 | 507 | def gen_module(inp, dep_prefixes): 508 | l('## machine generated, do not edit') 509 | l('') 510 | gen_imports(inp, dep_prefixes) 511 | pre_parse(inp) 512 | prefix = inp['prefix'] 513 | for decl in inp['decls']: 514 | if not decl['is_dep']: 515 | kind = decl['kind'] 516 | if kind == 'consts': 517 | gen_consts(decl, prefix) 518 | elif kind == 'enum': 519 | gen_enum(decl, prefix) 520 | elif kind == 'struct': 521 | gen_struct(decl, prefix) 522 | elif kind == 'func': 523 | if not check_func_name_ignore(decl['name']): 524 | gen_func_nim(decl, prefix) 525 | 526 | def prepare(): 527 | print('Generating nim bindings:') 528 | if not os.path.isdir('sokol-nim/src/sokol'): 529 | os.makedirs('sokol-nim/src/sokol') 530 | if not os.path.isdir('sokol-nim/src/sokol/c'): 531 | os.makedirs('sokol-nim/src/sokol/c') 532 | 533 | def gen(c_header_path, c_prefix, dep_c_prefixes): 534 | global out_lines 535 | module_name = module_names[c_prefix] 536 | c_source_path = c_source_paths[c_prefix] 537 | print(f' {c_header_path} => {module_name}') 538 | reset_globals() 539 | shutil.copyfile(c_header_path, f'sokol-nim/src/sokol/c/{os.path.basename(c_header_path)}') 540 | ir = gen_ir.gen(c_header_path, c_source_path, module_name, c_prefix, dep_c_prefixes) 541 | gen_module(ir, dep_c_prefixes) 542 | output_path = f"sokol-nim/src/sokol/{ir['module']}.nim" 543 | 544 | ## some changes for readability 545 | out_lines = out_lines.replace("PixelformatInfo", "PixelFormatInfo") 546 | out_lines = out_lines.replace(" Dontcare,", " DontCare,") 547 | out_lines = out_lines.replace(" Vertexbuffer,", " VertexBuffer,") 548 | out_lines = out_lines.replace(" Indexbuffer,", " IndexBuffer,") 549 | out_lines = out_lines.replace(" N2d,", " Plane,") 550 | out_lines = out_lines.replace(" N3d,", " Volume,") 551 | out_lines = out_lines.replace(" Vs,", " Vertex,") 552 | out_lines = out_lines.replace(" Fs,", " Fragment,") 553 | 554 | ## include extensions in generated code 555 | l("# Nim-specific API extensions") 556 | l(f"include ext/{ir['module']}") 557 | 558 | ## copy extensions into generated code 559 | # ext_path = f"sokol-nim/src/sokol/ext/{ir['module']}.nim" 560 | # if os.path.isfile(ext_path): 561 | # with open(ext_path, 'r') as f_ext: 562 | # out_lines += f_ext.read() 563 | 564 | with open(output_path, 'w', newline='\n') as f_outp: 565 | f_outp.write(out_lines) 566 | -------------------------------------------------------------------------------- /bindgen/gen_zig.py: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # Read output of gen_json.py and generate Zig language bindings. 3 | # 4 | # Zig coding style: 5 | # - types are PascalCase 6 | # - functions are camelCase 7 | # - otherwise snake_case 8 | #------------------------------------------------------------------------------- 9 | import gen_ir 10 | import json, re, os, shutil 11 | 12 | module_names = { 13 | 'sg_': 'gfx', 14 | 'sapp_': 'app', 15 | 'stm_': 'time', 16 | 'saudio_': 'audio', 17 | 'sgl_': 'gl', 18 | 'sdtx_': 'debugtext', 19 | 'sshape_': 'shape', 20 | } 21 | 22 | c_source_paths = { 23 | 'sg_': 'sokol-zig/src/sokol/c/sokol_app_gfx.c', 24 | 'sapp_': 'sokol-zig/src/sokol/c/sokol_app_gfx.c', 25 | 'stm_': 'sokol-zig/src/sokol/c/sokol_time.c', 26 | 'saudio_': 'sokol-zig/src/sokol/c/sokol_audio.c', 27 | 'sgl_': 'sokol-zig/src/sokol/c/sokol_gl.c', 28 | 'sdtx_': 'sokol-zig/src/sokol/c/sokol_debugtext.c', 29 | 'sshape_': 'sokol-zig/src/sokol/c/sokol_shape.c', 30 | } 31 | 32 | name_ignores = [ 33 | 'sdtx_printf', 34 | 'sdtx_vprintf', 35 | 'sg_install_trace_hooks', 36 | 'sg_trace_hooks', 37 | ] 38 | 39 | name_overrides = { 40 | 'sgl_error': 'sgl_get_error', # 'error' is reserved in Zig 41 | 'sgl_deg': 'sgl_as_degrees', 42 | 'sgl_rad': 'sgl_as_radians' 43 | } 44 | 45 | # NOTE: syntax for function results: "func_name.RESULT" 46 | type_overrides = { 47 | 'sg_context_desc.color_format': 'int', 48 | 'sg_context_desc.depth_format': 'int', 49 | 'sg_apply_uniforms.ub_index': 'uint32_t', 50 | 'sg_draw.base_element': 'uint32_t', 51 | 'sg_draw.num_elements': 'uint32_t', 52 | 'sg_draw.num_instances': 'uint32_t', 53 | 'sshape_element_range_t.base_element': 'uint32_t', 54 | 'sshape_element_range_t.num_elements': 'uint32_t', 55 | 'sdtx_font.font_index': 'uint32_t', 56 | } 57 | 58 | prim_types = { 59 | 'int': 'i32', 60 | 'bool': 'bool', 61 | 'char': 'u8', 62 | 'int8_t': 'i8', 63 | 'uint8_t': 'u8', 64 | 'int16_t': 'i16', 65 | 'uint16_t': 'u16', 66 | 'int32_t': 'i32', 67 | 'uint32_t': 'u32', 68 | 'int64_t': 'i64', 69 | 'uint64_t': 'u64', 70 | 'float': 'f32', 71 | 'double': 'f64', 72 | 'uintptr_t': 'usize', 73 | 'intptr_t': 'isize', 74 | 'size_t': 'usize' 75 | } 76 | 77 | prim_defaults = { 78 | 'int': '0', 79 | 'bool': 'false', 80 | 'int8_t': '0', 81 | 'uint8_t': '0', 82 | 'int16_t': '0', 83 | 'uint16_t': '0', 84 | 'int32_t': '0', 85 | 'uint32_t': '0', 86 | 'int64_t': '0', 87 | 'uint64_t': '0', 88 | 'float': '0.0', 89 | 'double': '0.0', 90 | 'uintptr_t': '0', 91 | 'intptr_t': '0', 92 | 'size_t': '0' 93 | } 94 | 95 | struct_types = [] 96 | enum_types = [] 97 | enum_items = {} 98 | out_lines = '' 99 | 100 | def reset_globals(): 101 | global struct_types 102 | global enum_types 103 | global enum_items 104 | global out_lines 105 | struct_types = [] 106 | enum_types = [] 107 | enum_items = {} 108 | out_lines = '' 109 | 110 | re_1d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]$") 111 | re_2d_array = re.compile("^(?:const )?\w*\s\*?\[\d*\]\[\d*\]$") 112 | 113 | def l(s): 114 | global out_lines 115 | out_lines += s + '\n' 116 | 117 | def as_zig_prim_type(s): 118 | return prim_types[s] 119 | 120 | # prefix_bla_blub(_t) => (dep.)BlaBlub 121 | def as_zig_struct_type(s, prefix): 122 | parts = s.lower().split('_') 123 | outp = '' if s.startswith(prefix) else f'{parts[0]}.' 124 | for part in parts[1:]: 125 | if (part != 't'): 126 | outp += part.capitalize() 127 | return outp 128 | 129 | # prefix_bla_blub(_t) => (dep.)BlaBlub 130 | def as_zig_enum_type(s, prefix): 131 | parts = s.lower().split('_') 132 | outp = '' if s.startswith(prefix) else f'{parts[0]}.' 133 | for part in parts[1:]: 134 | if (part != 't'): 135 | outp += part.capitalize() 136 | return outp 137 | 138 | def check_type_override(func_or_struct_name, field_or_arg_name, orig_type): 139 | s = f"{func_or_struct_name}.{field_or_arg_name}" 140 | if s in type_overrides: 141 | return type_overrides[s] 142 | else: 143 | return orig_type 144 | 145 | def check_name_override(name): 146 | if name in name_overrides: 147 | return name_overrides[name] 148 | else: 149 | return name 150 | 151 | def check_name_ignore(name): 152 | return name in name_ignores 153 | 154 | # PREFIX_BLA_BLUB to bla_blub 155 | def as_snake_case(s, prefix): 156 | outp = s.lower() 157 | if outp.startswith(prefix): 158 | outp = outp[len(prefix):] 159 | return outp 160 | 161 | # prefix_bla_blub => blaBlub 162 | def as_camel_case(s): 163 | parts = s.lower().split('_')[1:] 164 | outp = parts[0] 165 | for part in parts[1:]: 166 | outp += part.capitalize() 167 | return outp 168 | 169 | # PREFIX_ENUM_BLA => Bla, _PREFIX_ENUM_BLA => Bla 170 | def as_enum_item_name(s): 171 | outp = s 172 | if outp.startswith('_'): 173 | outp = outp[1:] 174 | parts = outp.split('_')[2:] 175 | outp = '_'.join(parts) 176 | if outp[0].isdigit(): 177 | outp = '_' + outp 178 | return outp 179 | 180 | def enum_default_item(enum_name): 181 | return enum_items[enum_name][0] 182 | 183 | def is_prim_type(s): 184 | return s in prim_types 185 | 186 | def is_struct_type(s): 187 | return s in struct_types 188 | 189 | def is_enum_type(s): 190 | return s in enum_types 191 | 192 | def is_string_ptr(s): 193 | return s == "const char *" 194 | 195 | def is_const_void_ptr(s): 196 | return s == "const void *" 197 | 198 | def is_void_ptr(s): 199 | return s == "void *" 200 | 201 | def is_const_prim_ptr(s): 202 | for prim_type in prim_types: 203 | if s == f"const {prim_type} *": 204 | return True 205 | return False 206 | 207 | def is_prim_ptr(s): 208 | for prim_type in prim_types: 209 | if s == f"{prim_type} *": 210 | return True 211 | return False 212 | 213 | def is_const_struct_ptr(s): 214 | for struct_type in struct_types: 215 | if s == f"const {struct_type} *": 216 | return True 217 | return False 218 | 219 | def is_func_ptr(s): 220 | return '(*)' in s 221 | 222 | def is_1d_array_type(s): 223 | return re_1d_array.match(s) 224 | 225 | def is_2d_array_type(s): 226 | return re_2d_array.match(s) 227 | 228 | def type_default_value(s): 229 | return prim_defaults[s] 230 | 231 | def extract_array_type(s): 232 | return s[:s.index('[')].strip() 233 | 234 | def extract_array_nums(s): 235 | return s[s.index('['):].replace('[', ' ').replace(']', ' ').split() 236 | 237 | def extract_ptr_type(s): 238 | tokens = s.split() 239 | if tokens[0] == 'const': 240 | return tokens[1] 241 | else: 242 | return tokens[0] 243 | 244 | def as_extern_c_arg_type(arg_type, prefix): 245 | if arg_type == "void": 246 | return "void" 247 | elif is_prim_type(arg_type): 248 | return as_zig_prim_type(arg_type) 249 | elif is_struct_type(arg_type): 250 | return as_zig_struct_type(arg_type, prefix) 251 | elif is_enum_type(arg_type): 252 | return as_zig_enum_type(arg_type, prefix) 253 | elif is_void_ptr(arg_type): 254 | return "?*c_void" 255 | elif is_const_void_ptr(arg_type): 256 | return "?*const c_void" 257 | elif is_string_ptr(arg_type): 258 | return "[*c]const u8" 259 | elif is_const_struct_ptr(arg_type): 260 | return f"[*c]const {as_zig_struct_type(extract_ptr_type(arg_type), prefix)}" 261 | elif is_prim_ptr(arg_type): 262 | return f"[*c] {as_zig_prim_type(extract_ptr_type(arg_type))}" 263 | elif is_const_prim_ptr(arg_type): 264 | return f"[*c]const {as_zig_prim_type(extract_ptr_type(arg_type))}" 265 | else: 266 | return '??? (as_extern_c_arg_type)' 267 | 268 | def as_zig_arg_type(arg_prefix, arg_type, prefix): 269 | # NOTE: if arg_prefix is None, the result is used as return value 270 | pre = "" if arg_prefix is None else arg_prefix 271 | if arg_type == "void": 272 | if arg_prefix is None: 273 | return "void" 274 | else: 275 | return "" 276 | elif is_prim_type(arg_type): 277 | return pre + as_zig_prim_type(arg_type) 278 | elif is_struct_type(arg_type): 279 | return pre + as_zig_struct_type(arg_type, prefix) 280 | elif is_enum_type(arg_type): 281 | return pre + as_zig_enum_type(arg_type, prefix) 282 | elif is_void_ptr(arg_type): 283 | return pre + "?*c_void" 284 | elif is_const_void_ptr(arg_type): 285 | return pre + "?*const c_void" 286 | elif is_string_ptr(arg_type): 287 | return pre + "[:0]const u8" 288 | elif is_const_struct_ptr(arg_type): 289 | # not a bug, pass const structs by value 290 | return pre + f"{as_zig_struct_type(extract_ptr_type(arg_type), prefix)}" 291 | elif is_prim_ptr(arg_type): 292 | return pre + f"* {as_zig_prim_type(extract_ptr_type(arg_type))}" 293 | elif is_const_prim_ptr(arg_type): 294 | return pre + f"*const {as_zig_prim_type(extract_ptr_type(arg_type))}" 295 | else: 296 | return arg_prefix + "??? (as_zig_arg_type)" 297 | 298 | # get C-style arguments of a function pointer as string 299 | def funcptr_args_c(field_type, prefix): 300 | tokens = field_type[field_type.index('(*)')+4:-1].split(',') 301 | s = "" 302 | for token in tokens: 303 | arg_type = token.strip() 304 | if s != "": 305 | s += ", " 306 | c_arg = as_extern_c_arg_type(arg_type, prefix) 307 | if (c_arg == "void"): 308 | return "" 309 | else: 310 | s += c_arg 311 | return s 312 | 313 | # get C-style result of a function pointer as string 314 | def funcptr_res_c(field_type): 315 | res_type = field_type[:field_type.index('(*)')].strip() 316 | if res_type == 'void': 317 | return 'void' 318 | elif is_const_void_ptr(res_type): 319 | return '?*const c_void' 320 | else: 321 | return '???' 322 | 323 | def funcdecl_args_c(decl, prefix): 324 | s = "" 325 | func_name = decl['name'] 326 | for param_decl in decl['params']: 327 | if s != "": 328 | s += ", " 329 | param_name = param_decl['name'] 330 | param_type = check_type_override(func_name, param_name, param_decl['type']) 331 | s += as_extern_c_arg_type(param_type, prefix) 332 | return s 333 | 334 | def funcdecl_args_zig(decl, prefix): 335 | s = "" 336 | func_name = decl['name'] 337 | for param_decl in decl['params']: 338 | if s != "": 339 | s += ", " 340 | param_name = param_decl['name'] 341 | param_type = check_type_override(func_name, param_name, param_decl['type']) 342 | s += f"{as_zig_arg_type(f'{param_name}: ', param_type, prefix)}" 343 | return s 344 | 345 | def funcdecl_result_c(decl, prefix): 346 | func_name = decl['name'] 347 | decl_type = decl['type'] 348 | result_type = check_type_override(func_name, 'RESULT', decl_type[:decl_type.index('(')].strip()) 349 | return as_extern_c_arg_type(result_type, prefix) 350 | 351 | def funcdecl_result_zig(decl, prefix): 352 | func_name = decl['name'] 353 | decl_type = decl['type'] 354 | result_type = check_type_override(func_name, 'RESULT', decl_type[:decl_type.index('(')].strip()) 355 | zig_res_type = as_zig_arg_type(None, result_type, prefix) 356 | if zig_res_type == "": 357 | zig_res_type = "void" 358 | return zig_res_type 359 | 360 | def gen_struct(decl, prefix, callconvc_funcptrs = True, use_raw_name=False, use_extern=True): 361 | struct_name = decl['name'] 362 | zig_type = struct_name if use_raw_name else as_zig_struct_type(struct_name, prefix) 363 | l(f"pub const {zig_type} = {'extern ' if use_extern else ''}struct {{") 364 | for field in decl['fields']: 365 | field_name = field['name'] 366 | field_type = field['type'] 367 | field_type = check_type_override(struct_name, field_name, field_type) 368 | if is_prim_type(field_type): 369 | l(f" {field_name}: {as_zig_prim_type(field_type)} = {type_default_value(field_type)},") 370 | elif is_struct_type(field_type): 371 | l(f" {field_name}: {as_zig_struct_type(field_type, prefix)} = .{{ }},") 372 | elif is_enum_type(field_type): 373 | l(f" {field_name}: {as_zig_enum_type(field_type, prefix)} = .{enum_default_item(field_type)},") 374 | elif is_string_ptr(field_type): 375 | l(f" {field_name}: [*c]const u8 = null,") 376 | elif is_const_void_ptr(field_type): 377 | l(f" {field_name}: ?*const c_void = null,") 378 | elif is_void_ptr(field_type): 379 | l(f" {field_name}: ?*c_void = null,") 380 | elif is_const_prim_ptr(field_type): 381 | l(f" {field_name}: ?[*]const {as_zig_prim_type(extract_ptr_type(field_type))} = null,") 382 | elif is_func_ptr(field_type): 383 | if callconvc_funcptrs: 384 | l(f" {field_name}: ?fn({funcptr_args_c(field_type, prefix)}) callconv(.C) {funcptr_res_c(field_type)} = null,") 385 | else: 386 | l(f" {field_name}: ?fn({funcptr_args_c(field_type, prefix)}) {funcptr_res_c(field_type)} = null,") 387 | elif is_1d_array_type(field_type): 388 | array_type = extract_array_type(field_type) 389 | array_nums = extract_array_nums(field_type) 390 | if is_prim_type(array_type) or is_struct_type(array_type): 391 | if is_prim_type(array_type): 392 | zig_type = as_zig_prim_type(array_type) 393 | def_val = type_default_value(array_type) 394 | elif is_struct_type(array_type): 395 | zig_type = as_zig_struct_type(array_type, prefix) 396 | def_val = '.{}' 397 | elif is_enum_type(array_type): 398 | zig_type = as_zig_enum_type(array_type, prefix) 399 | def_val = '.{}' 400 | else: 401 | zig_type = '??? (array type)' 402 | def_val = '???' 403 | t0 = f"[{array_nums[0]}]{zig_type}" 404 | t0_slice = f"[]const {zig_type}" 405 | t1 = f"[_]{zig_type}" 406 | l(f" {field_name}: {t0} = {t1}{{{def_val}}} ** {array_nums[0]},") 407 | elif is_const_void_ptr(array_type): 408 | l(f" {field_name}: [{array_nums[0]}]?*const c_void = [_]?*const c_void {{ null }} ** {array_nums[0]},") 409 | else: 410 | l(f"// FIXME: ??? array {field_name}: {field_type} => {array_type} [{array_nums[0]}]") 411 | elif is_2d_array_type(field_type): 412 | array_type = extract_array_type(field_type) 413 | array_nums = extract_array_nums(field_type) 414 | if is_prim_type(array_type): 415 | zig_type = as_zig_prim_type(array_type) 416 | def_val = type_default_value(array_type) 417 | elif is_struct_type(array_type): 418 | zig_type = as_zig_struct_type(array_type, prefix) 419 | def_val = ".{ }" 420 | else: 421 | zig_type = "???" 422 | def_val = "???" 423 | t0 = f"[{array_nums[0]}][{array_nums[1]}]{zig_type}" 424 | l(f" {field_name}: {t0} = [_][{array_nums[1]}]{zig_type}{{[_]{zig_type}{{ {def_val} }}**{array_nums[1]}}}**{array_nums[0]},") 425 | else: 426 | l(f"// FIXME: {field_name}: {field_type};") 427 | l("};") 428 | 429 | def gen_consts(decl, prefix): 430 | for item in decl['items']: 431 | l(f"pub const {as_snake_case(item['name'], prefix)} = {item['value']};") 432 | 433 | def gen_enum(decl, prefix): 434 | l(f"pub const {as_zig_enum_type(decl['name'], prefix)} = extern enum(i32) {{") 435 | for item in decl['items']: 436 | item_name = as_enum_item_name(item['name']) 437 | if item_name != "FORCE_U32": 438 | if 'value' in item: 439 | l(f" {item_name} = {item['value']},") 440 | else: 441 | l(f" {item_name},") 442 | l("};") 443 | 444 | def gen_func_c(decl, prefix): 445 | l(f"pub extern fn {decl['name']}({funcdecl_args_c(decl, prefix)}) {funcdecl_result_c(decl, prefix)};") 446 | 447 | def gen_func_zig(decl, prefix): 448 | c_func_name = decl['name'] 449 | zig_func_name = as_camel_case(check_name_override(decl['name'])) 450 | zig_res_type = funcdecl_result_zig(decl, prefix) 451 | l(f"pub fn {zig_func_name}({funcdecl_args_zig(decl, prefix)}) {zig_res_type} {{") 452 | if zig_res_type != 'void': 453 | s = f" return {c_func_name}(" 454 | else: 455 | s = f" {c_func_name}(" 456 | for i, param_decl in enumerate(decl['params']): 457 | if i > 0: 458 | s += ", " 459 | arg_name = param_decl['name'] 460 | arg_type = param_decl['type'] 461 | if is_const_struct_ptr(arg_type): 462 | s += f"&{arg_name}" 463 | elif is_string_ptr(arg_type): 464 | s += f"@ptrCast([*c]const u8,{arg_name})" 465 | else: 466 | s += arg_name 467 | s += ");" 468 | l(s) 469 | l("}") 470 | 471 | def pre_parse(inp): 472 | global struct_types 473 | global enum_types 474 | for decl in inp['decls']: 475 | kind = decl['kind'] 476 | if kind == 'struct': 477 | struct_types.append(decl['name']) 478 | elif kind == 'enum': 479 | enum_name = decl['name'] 480 | enum_types.append(enum_name) 481 | enum_items[enum_name] = [] 482 | for item in decl['items']: 483 | enum_items[enum_name].append(as_enum_item_name(item['name'])) 484 | 485 | def gen_imports(inp, dep_prefixes): 486 | for dep_prefix in dep_prefixes: 487 | dep_module_name = module_names[dep_prefix] 488 | l(f'const {dep_prefix[:-1]} = @import("{dep_module_name}.zig");') 489 | l('') 490 | 491 | def gen_helpers(inp): 492 | if inp['prefix'] in ['sg_', 'sdtx_', 'sshape_']: 493 | l('// helper function to convert "anything" to a Range struct') 494 | l('pub fn asRange(val: anytype) Range {') 495 | l(' const type_info = @typeInfo(@TypeOf(val));') 496 | l(' switch (type_info) {') 497 | l(' .Pointer => {') 498 | l(' switch (type_info.Pointer.size) {') 499 | l(' .One => return .{ .ptr = val, .size = @sizeOf(type_info.Pointer.child) },') 500 | l(' .Slice => return .{ .ptr = val.ptr, .size = @sizeOf(type_info.Pointer.child) * val.len },') 501 | l(' else => @compileError("FIXME: Pointer type!"),') 502 | l(' }') 503 | l(' },') 504 | l(' .Struct, .Array => {') 505 | l(' return .{ .ptr = &val, .size = @sizeOf(@TypeOf(val)) };') 506 | l(' },') 507 | l(' else => {') 508 | l(' @compileError("Cannot convert to range!");') 509 | l(' }') 510 | l(' }') 511 | l('}') 512 | l('') 513 | if inp['prefix'] == 'sdtx_': 514 | l('// std.fmt compatible Writer') 515 | l('pub const Writer = struct {') 516 | l(' pub const Error = error { };') 517 | l(' pub fn writeAll(self: Writer, bytes: []const u8) Error!void {') 518 | l(' for (bytes) |byte| {') 519 | l(' putc(byte);') 520 | l(' }') 521 | l(' }') 522 | l(' pub fn writeByteNTimes(self: Writer, byte: u8, n: u64) Error!void {') 523 | l(' var i: u64 = 0;') 524 | l(' while (i < n): (i += 1) {') 525 | l(' putc(byte);') 526 | l(' }') 527 | l(' }') 528 | l('};') 529 | l('// std.fmt-style formatted print') 530 | l('pub fn print(comptime fmt: anytype, args: anytype) void {') 531 | l(' var writer: Writer = .{};') 532 | l(' @import("std").fmt.format(writer, fmt, args) catch {};') 533 | l('}') 534 | l('') 535 | 536 | def gen_module(inp, dep_prefixes): 537 | l('// machine generated, do not edit') 538 | l('') 539 | gen_imports(inp, dep_prefixes) 540 | gen_helpers(inp) 541 | pre_parse(inp) 542 | prefix = inp['prefix'] 543 | for decl in inp['decls']: 544 | if not decl['is_dep']: 545 | kind = decl['kind'] 546 | if kind == 'consts': 547 | gen_consts(decl, prefix) 548 | elif not check_name_ignore(decl['name']): 549 | if kind == 'struct': 550 | gen_struct(decl, prefix) 551 | elif kind == 'enum': 552 | gen_enum(decl, prefix) 553 | elif kind == 'func': 554 | gen_func_c(decl, prefix) 555 | gen_func_zig(decl, prefix) 556 | 557 | def prepare(): 558 | print('Generating zig bindings:') 559 | if not os.path.isdir('sokol-zig/src/sokol'): 560 | os.makedirs('sokol-zig/src/sokol') 561 | if not os.path.isdir('sokol-zig/src/sokol/c'): 562 | os.makedirs('sokol-zig/src/sokol/c') 563 | 564 | def gen(c_header_path, c_prefix, dep_c_prefixes): 565 | module_name = module_names[c_prefix] 566 | c_source_path = c_source_paths[c_prefix] 567 | print(f' {c_header_path} => {module_name}') 568 | reset_globals() 569 | shutil.copyfile(c_header_path, f'sokol-zig/src/sokol/c/{os.path.basename(c_header_path)}') 570 | ir = gen_ir.gen(c_header_path, c_source_path, module_name, c_prefix, dep_c_prefixes) 571 | gen_module(ir, dep_c_prefixes) 572 | output_path = f"sokol-zig/src/sokol/{ir['module']}.zig" 573 | with open(output_path, 'w', newline='\n') as f_outp: 574 | f_outp.write(out_lines) 575 | -------------------------------------------------------------------------------- /fips.yml: -------------------------------------------------------------------------------- 1 | exports: 2 | header-dirs: [ ".", "util" ] 3 | -------------------------------------------------------------------------------- /sokol_args.h: -------------------------------------------------------------------------------- 1 | #if defined(SOKOL_IMPL) && !defined(SOKOL_ARGS_IMPL) 2 | #define SOKOL_ARGS_IMPL 3 | #endif 4 | #ifndef SOKOL_ARGS_INCLUDED 5 | /* 6 | sokol_args.h -- cross-platform key/value arg-parsing for web and native 7 | 8 | Project URL: https://github.com/floooh/sokol 9 | 10 | Do this: 11 | #define SOKOL_IMPL or 12 | #define SOKOL_ARGS_IMPL 13 | before you include this file in *one* C or C++ file to create the 14 | implementation. 15 | 16 | Optionally provide the following defines with your own implementations: 17 | 18 | SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) 19 | SOKOL_LOG(msg) - your own logging functions (default: puts(msg)) 20 | SOKOL_CALLOC(n,s) - your own calloc() implementation (default: calloc(n,s)) 21 | SOKOL_FREE(p) - your own free() implementation (default: free(p)) 22 | SOKOL_ARGS_API_DECL - public function declaration prefix (default: extern) 23 | SOKOL_API_DECL - same as SOKOL_ARGS_API_DECL 24 | SOKOL_API_IMPL - public function implementation prefix (default: -) 25 | 26 | If sokol_args.h is compiled as a DLL, define the following before 27 | including the declaration or implementation: 28 | 29 | SOKOL_DLL 30 | 31 | On Windows, SOKOL_DLL will define SOKOL_ARGS_API_DECL as __declspec(dllexport) 32 | or __declspec(dllimport) as needed. 33 | 34 | OVERVIEW 35 | ======== 36 | sokol_args.h provides a simple unified argument parsing API for WebAssembly and 37 | native apps. 38 | 39 | When running as WebAssembly app, arguments are taken from the page URL: 40 | 41 | https://floooh.github.io/tiny8bit/kc85.html?type=kc85_3&mod=m022&snapshot=kc85/jungle.kcc 42 | 43 | The same arguments provided to a command line app: 44 | 45 | kc85 type=kc85_3 mod=m022 snapshot=kc85/jungle.kcc 46 | 47 | ARGUMENT FORMATTING 48 | =================== 49 | On the web platform, arguments must be formatted as a valid URL query string 50 | with 'percent encoding' used for special characters. 51 | 52 | Strings are expected to be UTF-8 encoded (although sokol_args.h doesn't 53 | contain any special UTF-8 handling). See below on how to obtain 54 | UTF-8 encoded argc/argv values on Windows when using WinMain() as 55 | entry point. 56 | 57 | On native platforms the following rules must be followed: 58 | 59 | Arguments have the general form 60 | 61 | key=value 62 | 63 | Key/value pairs are separated by 'whitespace', valid whitespace 64 | characters are space and tab. 65 | 66 | Whitespace characters in front and after the separating '=' character 67 | are ignored: 68 | 69 | key = value 70 | 71 | ...is the same as 72 | 73 | key=value 74 | 75 | The 'key' string must be a simple string without escape sequences or whitespace. 76 | 77 | Currently 'single keys' without values are not allowed, but may be 78 | in the future. 79 | 80 | The 'value' string can be quoted, and quoted value strings can contain 81 | whitespace: 82 | 83 | key = 'single-quoted value' 84 | key = "double-quoted value" 85 | 86 | Single-quoted value strings can contain double quotes, and vice-versa: 87 | 88 | key = 'single-quoted value "can contain double-quotes"' 89 | key = "double-quoted value 'can contain single-quotes'" 90 | 91 | Note that correct quoting can be tricky on some shells, since command 92 | shells may remove quotes, unless they're escaped. 93 | 94 | Value strings can contain a small selection of escape sequences: 95 | 96 | \n - newline 97 | \r - carriage return 98 | \t - tab 99 | \\ - escaped backslash 100 | 101 | (more escape codes may be added in the future). 102 | 103 | CODE EXAMPLE 104 | ============ 105 | 106 | int main(int argc, char* argv[]) { 107 | // initialize sokol_args with default parameters 108 | sargs_setup(&(sargs_desc){ 109 | .argc = argc, 110 | .argv = argv 111 | }); 112 | 113 | // check if a key exists... 114 | if (sargs_exists("bla")) { 115 | ... 116 | } 117 | 118 | // get value string for key, if not found, return empty string "" 119 | const char* val0 = sargs_value("bla"); 120 | 121 | // get value string for key, or default string if key not found 122 | const char* val1 = sargs_value_def("bla", "default_value"); 123 | 124 | // check if a key matches expected value 125 | if (sargs_equals("type", "kc85_4")) { 126 | ... 127 | } 128 | 129 | // check if a key's value is "true", "yes" or "on" 130 | if (sargs_boolean("joystick_enabled")) { 131 | ... 132 | } 133 | 134 | // iterate over keys and values 135 | for (int i = 0; i < sargs_num_args(); i++) { 136 | printf("key: %s, value: %s\n", sargs_key_at(i), sargs_value_at(i)); 137 | } 138 | 139 | // lookup argument index by key string, will return -1 if key 140 | // is not found, sargs_key_at() and sargs_value_at() will return 141 | // an empty string for invalid indices 142 | int index = sargs_find("bla"); 143 | printf("key: %s, value: %s\n", sargs_key_at(index), sargs_value_at(index)); 144 | 145 | // shutdown sokol-args 146 | sargs_shutdown(); 147 | } 148 | 149 | WINMAIN AND ARGC / ARGV 150 | ======================= 151 | On Windows with WinMain() based apps, getting UTF8-encoded command line 152 | arguments is a bit more complicated: 153 | 154 | First call GetCommandLineW(), this returns the entire command line 155 | as UTF-16 string. Then call CommandLineToArgvW(), this parses the 156 | command line string into the usual argc/argv format (but in UTF-16). 157 | Finally convert the UTF-16 strings in argv[] into UTF-8 via 158 | WideCharToMultiByte(). 159 | 160 | See the function _sapp_win32_command_line_to_utf8_argv() in sokol_app.h 161 | for example code how to do this (if you're using sokol_app.h, it will 162 | already convert the command line arguments to UTF-8 for you of course, 163 | so you can plug them directly into sokol_app.h). 164 | 165 | API DOCUMENTATION 166 | ================= 167 | void sargs_setup(const sargs_desc* desc) 168 | Initialize sokol_args, desc contains the following configuration 169 | parameters: 170 | int argc - the main function's argc parameter 171 | char** argv - the main function's argv parameter 172 | int max_args - max number of key/value pairs, default is 16 173 | int buf_size - size of the internal string buffer, default is 16384 174 | 175 | Note that on the web, argc and argv will be ignored and the arguments 176 | will be taken from the page URL instead. 177 | 178 | sargs_setup() will allocate 2 memory chunks: one for keeping track 179 | of the key/value args of size 'max_args*8', and a string buffer 180 | of size 'buf_size'. 181 | 182 | void sargs_shutdown(void) 183 | Shutdown sokol-args and free any allocated memory. 184 | 185 | bool sargs_isvalid(void) 186 | Return true between sargs_setup() and sargs_shutdown() 187 | 188 | bool sargs_exists(const char* key) 189 | Test if a key arg exists. 190 | 191 | const char* sargs_value(const char* key) 192 | Return value associated with key. Returns an empty 193 | string ("") if the key doesn't exist. 194 | 195 | const char* sargs_value_def(const char* key, const char* default) 196 | Return value associated with key, or the provided default 197 | value if the value doesn't exist. 198 | 199 | bool sargs_equals(const char* key, const char* val); 200 | Return true if the value associated with key matches 201 | the 'val' argument. 202 | 203 | bool sargs_boolean(const char* key) 204 | Return true if the value string of 'key' is one 205 | of 'true', 'yes', 'on'. 206 | 207 | int sargs_find(const char* key) 208 | Find argument by key name and return its index, or -1 if not found. 209 | 210 | int sargs_num_args(void) 211 | Return number of key/value pairs. 212 | 213 | const char* sargs_key_at(int index) 214 | Return the key name of argument at index. Returns empty string if 215 | is index is outside range. 216 | 217 | const char* sargs_value_at(int index) 218 | Return the value of argument at index. Returns empty string 219 | if index is outside range. 220 | 221 | TODO 222 | ==== 223 | - parsing errors? 224 | 225 | LICENSE 226 | ======= 227 | 228 | zlib/libpng license 229 | 230 | Copyright (c) 2018 Andre Weissflog 231 | 232 | This software is provided 'as-is', without any express or implied warranty. 233 | In no event will the authors be held liable for any damages arising from the 234 | use of this software. 235 | 236 | Permission is granted to anyone to use this software for any purpose, 237 | including commercial applications, and to alter it and redistribute it 238 | freely, subject to the following restrictions: 239 | 240 | 1. The origin of this software must not be misrepresented; you must not 241 | claim that you wrote the original software. If you use this software in a 242 | product, an acknowledgment in the product documentation would be 243 | appreciated but is not required. 244 | 245 | 2. Altered source versions must be plainly marked as such, and must not 246 | be misrepresented as being the original software. 247 | 248 | 3. This notice may not be removed or altered from any source 249 | distribution. 250 | */ 251 | #define SOKOL_ARGS_INCLUDED (1) 252 | #include 253 | #include 254 | 255 | #if defined(SOKOL_API_DECL) && !defined(SOKOL_ARGS_API_DECL) 256 | #define SOKOL_ARGS_API_DECL SOKOL_API_DECL 257 | #endif 258 | #ifndef SOKOL_ARGS_API_DECL 259 | #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_ARGS_IMPL) 260 | #define SOKOL_ARGS_API_DECL __declspec(dllexport) 261 | #elif defined(_WIN32) && defined(SOKOL_DLL) 262 | #define SOKOL_ARGS_API_DECL __declspec(dllimport) 263 | #else 264 | #define SOKOL_ARGS_API_DECL extern 265 | #endif 266 | #endif 267 | 268 | #ifdef __cplusplus 269 | extern "C" { 270 | #endif 271 | 272 | typedef struct sargs_desc { 273 | int argc; 274 | char** argv; 275 | int max_args; 276 | int buf_size; 277 | } sargs_desc; 278 | 279 | /* setup sokol-args */ 280 | SOKOL_ARGS_API_DECL void sargs_setup(const sargs_desc* desc); 281 | /* shutdown sokol-args */ 282 | SOKOL_ARGS_API_DECL void sargs_shutdown(void); 283 | /* true between sargs_setup() and sargs_shutdown() */ 284 | SOKOL_ARGS_API_DECL bool sargs_isvalid(void); 285 | /* test if an argument exists by key name */ 286 | SOKOL_ARGS_API_DECL bool sargs_exists(const char* key); 287 | /* get value by key name, return empty string if key doesn't exist */ 288 | SOKOL_ARGS_API_DECL const char* sargs_value(const char* key); 289 | /* get value by key name, return provided default if key doesn't exist */ 290 | SOKOL_ARGS_API_DECL const char* sargs_value_def(const char* key, const char* def); 291 | /* return true if val arg matches the value associated with key */ 292 | SOKOL_ARGS_API_DECL bool sargs_equals(const char* key, const char* val); 293 | /* return true if key's value is "true", "yes" or "on" */ 294 | SOKOL_ARGS_API_DECL bool sargs_boolean(const char* key); 295 | /* get index of arg by key name, return -1 if not exists */ 296 | SOKOL_ARGS_API_DECL int sargs_find(const char* key); 297 | /* get number of parsed arguments */ 298 | SOKOL_ARGS_API_DECL int sargs_num_args(void); 299 | /* get key name of argument at index, or empty string */ 300 | SOKOL_ARGS_API_DECL const char* sargs_key_at(int index); 301 | /* get value string of argument at index, or empty string */ 302 | SOKOL_ARGS_API_DECL const char* sargs_value_at(int index); 303 | 304 | #ifdef __cplusplus 305 | } /* extern "C" */ 306 | 307 | /* reference-based equivalents for c++ */ 308 | inline void sargs_setup(const sargs_desc& desc) { return sargs_setup(&desc); } 309 | 310 | #endif 311 | #endif // SOKOL_ARGS_INCLUDED 312 | 313 | /*--- IMPLEMENTATION ---------------------------------------------------------*/ 314 | #ifdef SOKOL_ARGS_IMPL 315 | #define SOKOL_ARGS_IMPL_INCLUDED (1) 316 | #include /* memset, strcmp */ 317 | 318 | #if defined(__EMSCRIPTEN__) 319 | #include 320 | #endif 321 | 322 | #ifndef SOKOL_API_IMPL 323 | #define SOKOL_API_IMPL 324 | #endif 325 | #ifndef SOKOL_DEBUG 326 | #ifndef NDEBUG 327 | #define SOKOL_DEBUG (1) 328 | #endif 329 | #endif 330 | #ifndef SOKOL_ASSERT 331 | #include 332 | #define SOKOL_ASSERT(c) assert(c) 333 | #endif 334 | #if !defined(SOKOL_CALLOC) && !defined(SOKOL_FREE) 335 | #include 336 | #endif 337 | #if !defined(SOKOL_CALLOC) 338 | #define SOKOL_CALLOC(n,s) calloc(n,s) 339 | #endif 340 | #if !defined(SOKOL_FREE) 341 | #define SOKOL_FREE(p) free(p) 342 | #endif 343 | #ifndef SOKOL_LOG 344 | #ifdef SOKOL_DEBUG 345 | #include 346 | #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } 347 | #else 348 | #define SOKOL_LOG(s) 349 | #endif 350 | #endif 351 | 352 | #ifndef _SOKOL_PRIVATE 353 | #if defined(__GNUC__) || defined(__clang__) 354 | #define _SOKOL_PRIVATE __attribute__((unused)) static 355 | #else 356 | #define _SOKOL_PRIVATE static 357 | #endif 358 | #endif 359 | 360 | #define _sargs_def(val, def) (((val) == 0) ? (def) : (val)) 361 | 362 | #define _SARGS_MAX_ARGS_DEF (16) 363 | #define _SARGS_BUF_SIZE_DEF (16*1024) 364 | 365 | /* parser state */ 366 | #define _SARGS_EXPECT_KEY (1<<0) 367 | #define _SARGS_EXPECT_SEP (1<<1) 368 | #define _SARGS_EXPECT_VAL (1<<2) 369 | #define _SARGS_PARSING_KEY (1<<3) 370 | #define _SARGS_PARSING_VAL (1<<4) 371 | #define _SARGS_ERROR (1<<5) 372 | 373 | /* a key/value pair struct */ 374 | typedef struct { 375 | int key; /* index to start of key string in buf */ 376 | int val; /* index to start of value string in buf */ 377 | } _sargs_kvp_t; 378 | 379 | /* sokol-args state */ 380 | typedef struct { 381 | int max_args; /* number of key/value pairs in args array */ 382 | int num_args; /* number of valid items in args array */ 383 | _sargs_kvp_t* args; /* key/value pair array */ 384 | int buf_size; /* size of buffer in bytes */ 385 | int buf_pos; /* current buffer position */ 386 | char* buf; /* character buffer, first char is reserved and zero for 'empty string' */ 387 | bool valid; 388 | uint32_t parse_state; 389 | char quote; /* current quote char, 0 if not in a quote */ 390 | bool in_escape; /* currently in an escape sequence */ 391 | } _sargs_state_t; 392 | static _sargs_state_t _sargs; 393 | 394 | /*== PRIVATE IMPLEMENTATION FUNCTIONS ========================================*/ 395 | 396 | _SOKOL_PRIVATE void _sargs_putc(char c) { 397 | if ((_sargs.buf_pos+2) < _sargs.buf_size) { 398 | _sargs.buf[_sargs.buf_pos++] = c; 399 | } 400 | } 401 | 402 | _SOKOL_PRIVATE const char* _sargs_str(int index) { 403 | SOKOL_ASSERT((index >= 0) && (index < _sargs.buf_size)); 404 | return &_sargs.buf[index]; 405 | } 406 | 407 | /*-- argument parser functions ------------------*/ 408 | _SOKOL_PRIVATE void _sargs_expect_key(void) { 409 | _sargs.parse_state = _SARGS_EXPECT_KEY; 410 | } 411 | 412 | _SOKOL_PRIVATE bool _sargs_key_expected(void) { 413 | return 0 != (_sargs.parse_state & _SARGS_EXPECT_KEY); 414 | } 415 | 416 | _SOKOL_PRIVATE void _sargs_expect_val(void) { 417 | _sargs.parse_state = _SARGS_EXPECT_VAL; 418 | } 419 | 420 | _SOKOL_PRIVATE bool _sargs_val_expected(void) { 421 | return 0 != (_sargs.parse_state & _SARGS_EXPECT_VAL); 422 | } 423 | 424 | _SOKOL_PRIVATE void _sargs_expect_sep(void) { 425 | _sargs.parse_state = _SARGS_EXPECT_SEP; 426 | } 427 | 428 | _SOKOL_PRIVATE bool _sargs_any_expected(void) { 429 | return 0 != (_sargs.parse_state & (_SARGS_EXPECT_KEY | _SARGS_EXPECT_VAL | _SARGS_EXPECT_SEP)); 430 | } 431 | 432 | _SOKOL_PRIVATE bool _sargs_is_separator(char c) { 433 | return c == '='; 434 | } 435 | 436 | _SOKOL_PRIVATE bool _sargs_is_quote(char c) { 437 | if (0 == _sargs.quote) { 438 | return (c == '\'') || (c == '"'); 439 | } 440 | else { 441 | return c == _sargs.quote; 442 | } 443 | } 444 | 445 | _SOKOL_PRIVATE void _sargs_begin_quote(char c) { 446 | _sargs.quote = c; 447 | } 448 | 449 | _SOKOL_PRIVATE void _sargs_end_quote(void) { 450 | _sargs.quote = 0; 451 | } 452 | 453 | _SOKOL_PRIVATE bool _sargs_in_quotes(void) { 454 | return 0 != _sargs.quote; 455 | } 456 | 457 | _SOKOL_PRIVATE bool _sargs_is_whitespace(char c) { 458 | return !_sargs_in_quotes() && ((c == ' ') || (c == '\t')); 459 | } 460 | 461 | _SOKOL_PRIVATE void _sargs_start_key(void) { 462 | SOKOL_ASSERT(_sargs.num_args < _sargs.max_args); 463 | _sargs.parse_state = _SARGS_PARSING_KEY; 464 | _sargs.args[_sargs.num_args].key = _sargs.buf_pos; 465 | } 466 | 467 | _SOKOL_PRIVATE void _sargs_end_key(void) { 468 | SOKOL_ASSERT(_sargs.num_args < _sargs.max_args); 469 | _sargs_putc(0); 470 | _sargs.parse_state = 0; 471 | } 472 | 473 | _SOKOL_PRIVATE bool _sargs_parsing_key(void) { 474 | return 0 != (_sargs.parse_state & _SARGS_PARSING_KEY); 475 | } 476 | 477 | _SOKOL_PRIVATE void _sargs_start_val(void) { 478 | SOKOL_ASSERT(_sargs.num_args < _sargs.max_args); 479 | _sargs.parse_state = _SARGS_PARSING_VAL; 480 | _sargs.args[_sargs.num_args].val = _sargs.buf_pos; 481 | } 482 | 483 | _SOKOL_PRIVATE void _sargs_end_val(void) { 484 | SOKOL_ASSERT(_sargs.num_args < _sargs.max_args); 485 | _sargs_putc(0); 486 | _sargs.num_args++; 487 | _sargs.parse_state = 0; 488 | } 489 | 490 | _SOKOL_PRIVATE bool _sargs_is_escape(char c) { 491 | return '\\' == c; 492 | } 493 | 494 | _SOKOL_PRIVATE void _sargs_start_escape(void) { 495 | _sargs.in_escape = true; 496 | } 497 | 498 | _SOKOL_PRIVATE bool _sargs_in_escape(void) { 499 | return _sargs.in_escape; 500 | } 501 | 502 | _SOKOL_PRIVATE char _sargs_escape(char c) { 503 | switch (c) { 504 | case 'n': return '\n'; 505 | case 't': return '\t'; 506 | case 'r': return '\r'; 507 | case '\\': return '\\'; 508 | default: return c; 509 | } 510 | } 511 | 512 | _SOKOL_PRIVATE void _sargs_end_escape(void) { 513 | _sargs.in_escape = false; 514 | } 515 | 516 | _SOKOL_PRIVATE bool _sargs_parsing_val(void) { 517 | return 0 != (_sargs.parse_state & _SARGS_PARSING_VAL); 518 | } 519 | 520 | _SOKOL_PRIVATE bool _sargs_parse_carg(const char* src) { 521 | char c; 522 | while (0 != (c = *src++)) { 523 | if (_sargs_in_escape()) { 524 | c = _sargs_escape(c); 525 | _sargs_end_escape(); 526 | } 527 | else if (_sargs_is_escape(c)) { 528 | _sargs_start_escape(); 529 | continue; 530 | } 531 | if (_sargs_any_expected()) { 532 | if (!_sargs_is_whitespace(c)) { 533 | /* start of key, value or separator */ 534 | if (_sargs_key_expected()) { 535 | /* start of new key */ 536 | _sargs_start_key(); 537 | } 538 | else if (_sargs_val_expected()) { 539 | /* start of value */ 540 | if (_sargs_is_quote(c)) { 541 | _sargs_begin_quote(c); 542 | continue; 543 | } 544 | _sargs_start_val(); 545 | } 546 | else { 547 | /* separator */ 548 | if (_sargs_is_separator(c)) { 549 | _sargs_expect_val(); 550 | continue; 551 | } 552 | } 553 | } 554 | else { 555 | /* skip white space */ 556 | continue; 557 | } 558 | } 559 | else if (_sargs_parsing_key()) { 560 | if (_sargs_is_whitespace(c) || _sargs_is_separator(c)) { 561 | /* end of key string */ 562 | _sargs_end_key(); 563 | if (_sargs_is_separator(c)) { 564 | _sargs_expect_val(); 565 | } 566 | else { 567 | _sargs_expect_sep(); 568 | } 569 | continue; 570 | } 571 | } 572 | else if (_sargs_parsing_val()) { 573 | if (_sargs_in_quotes()) { 574 | /* when in quotes, whitespace is a normal character 575 | and a matching quote ends the value string 576 | */ 577 | if (_sargs_is_quote(c)) { 578 | _sargs_end_quote(); 579 | _sargs_end_val(); 580 | _sargs_expect_key(); 581 | continue; 582 | } 583 | } 584 | else if (_sargs_is_whitespace(c)) { 585 | /* end of value string (no quotes) */ 586 | _sargs_end_val(); 587 | _sargs_expect_key(); 588 | continue; 589 | } 590 | } 591 | _sargs_putc(c); 592 | } 593 | if (_sargs_parsing_key()) { 594 | _sargs_end_key(); 595 | _sargs_expect_sep(); 596 | } 597 | else if (_sargs_parsing_val() && !_sargs_in_quotes()) { 598 | _sargs_end_val(); 599 | _sargs_expect_key(); 600 | } 601 | return true; 602 | } 603 | 604 | _SOKOL_PRIVATE bool _sargs_parse_cargs(int argc, const char** argv) { 605 | _sargs_expect_key(); 606 | bool retval = true; 607 | for (int i = 1; i < argc; i++) { 608 | retval &= _sargs_parse_carg(argv[i]); 609 | } 610 | _sargs.parse_state = 0; 611 | return retval; 612 | } 613 | 614 | /*-- EMSCRIPTEN IMPLEMENTATION -----------------------------------------------*/ 615 | #if defined(__EMSCRIPTEN__) 616 | 617 | #ifdef __cplusplus 618 | extern "C" { 619 | #endif 620 | EMSCRIPTEN_KEEPALIVE void _sargs_add_kvp(const char* key, const char* val) { 621 | SOKOL_ASSERT(_sargs.valid && key && val); 622 | if (_sargs.num_args >= _sargs.max_args) { 623 | return; 624 | } 625 | 626 | /* copy key string */ 627 | char c; 628 | _sargs.args[_sargs.num_args].key = _sargs.buf_pos; 629 | const char* ptr = key; 630 | while (0 != (c = *ptr++)) { 631 | _sargs_putc(c); 632 | } 633 | _sargs_putc(0); 634 | 635 | /* copy value string */ 636 | _sargs.args[_sargs.num_args].val = _sargs.buf_pos; 637 | ptr = val; 638 | while (0 != (c = *ptr++)) { 639 | _sargs_putc(c); 640 | } 641 | _sargs_putc(0); 642 | 643 | _sargs.num_args++; 644 | } 645 | #ifdef __cplusplus 646 | } /* extern "C" */ 647 | #endif 648 | 649 | /* JS function to extract arguments from the page URL */ 650 | EM_JS(void, sargs_js_parse_url, (void), { 651 | var params = new URLSearchParams(window.location.search).entries(); 652 | for (var p = params.next(); !p.done; p = params.next()) { 653 | var key = p.value[0]; 654 | var val = p.value[1]; 655 | var res = ccall('_sargs_add_kvp', 'void', ['string','string'], [key,val]); 656 | } 657 | }); 658 | 659 | #endif /* EMSCRIPTEN */ 660 | 661 | /*== PUBLIC IMPLEMENTATION FUNCTIONS =========================================*/ 662 | SOKOL_API_IMPL void sargs_setup(const sargs_desc* desc) { 663 | SOKOL_ASSERT(desc); 664 | memset(&_sargs, 0, sizeof(_sargs)); 665 | _sargs.max_args = _sargs_def(desc->max_args, _SARGS_MAX_ARGS_DEF); 666 | _sargs.buf_size = _sargs_def(desc->buf_size, _SARGS_BUF_SIZE_DEF); 667 | SOKOL_ASSERT(_sargs.buf_size > 8); 668 | _sargs.args = (_sargs_kvp_t*) SOKOL_CALLOC((size_t)_sargs.max_args, sizeof(_sargs_kvp_t)); 669 | _sargs.buf = (char*) SOKOL_CALLOC((size_t)_sargs.buf_size, sizeof(char)); 670 | /* the first character in buf is reserved and always zero, this is the 'empty string' */ 671 | _sargs.buf_pos = 1; 672 | _sargs.valid = true; 673 | 674 | /* parse argc/argv */ 675 | _sargs_parse_cargs(desc->argc, (const char**) desc->argv); 676 | 677 | #if defined(__EMSCRIPTEN__) 678 | /* on emscripten, also parse the page URL*/ 679 | sargs_js_parse_url(); 680 | #endif 681 | } 682 | 683 | SOKOL_API_IMPL void sargs_shutdown(void) { 684 | SOKOL_ASSERT(_sargs.valid); 685 | if (_sargs.args) { 686 | SOKOL_FREE(_sargs.args); 687 | _sargs.args = 0; 688 | } 689 | if (_sargs.buf) { 690 | SOKOL_FREE(_sargs.buf); 691 | _sargs.buf = 0; 692 | } 693 | _sargs.valid = false; 694 | } 695 | 696 | SOKOL_API_IMPL bool sargs_isvalid(void) { 697 | return _sargs.valid; 698 | } 699 | 700 | SOKOL_API_IMPL int sargs_find(const char* key) { 701 | SOKOL_ASSERT(_sargs.valid && key); 702 | for (int i = 0; i < _sargs.num_args; i++) { 703 | if (0 == strcmp(_sargs_str(_sargs.args[i].key), key)) { 704 | return i; 705 | } 706 | } 707 | return -1; 708 | } 709 | 710 | SOKOL_API_IMPL int sargs_num_args(void) { 711 | SOKOL_ASSERT(_sargs.valid); 712 | return _sargs.num_args; 713 | } 714 | 715 | SOKOL_API_IMPL const char* sargs_key_at(int index) { 716 | SOKOL_ASSERT(_sargs.valid); 717 | if ((index >= 0) && (index < _sargs.num_args)) { 718 | return _sargs_str(_sargs.args[index].key); 719 | } 720 | else { 721 | /* index 0 is always the empty string */ 722 | return _sargs_str(0); 723 | } 724 | } 725 | 726 | SOKOL_API_IMPL const char* sargs_value_at(int index) { 727 | SOKOL_ASSERT(_sargs.valid); 728 | if ((index >= 0) && (index < _sargs.num_args)) { 729 | return _sargs_str(_sargs.args[index].val); 730 | } 731 | else { 732 | /* index 0 is always the empty string */ 733 | return _sargs_str(0); 734 | } 735 | } 736 | 737 | SOKOL_API_IMPL bool sargs_exists(const char* key) { 738 | SOKOL_ASSERT(_sargs.valid && key); 739 | return -1 != sargs_find(key); 740 | } 741 | 742 | SOKOL_API_IMPL const char* sargs_value(const char* key) { 743 | SOKOL_ASSERT(_sargs.valid && key); 744 | return sargs_value_at(sargs_find(key)); 745 | } 746 | 747 | SOKOL_API_IMPL const char* sargs_value_def(const char* key, const char* def) { 748 | SOKOL_ASSERT(_sargs.valid && key && def); 749 | int arg_index = sargs_find(key); 750 | if (-1 != arg_index) { 751 | return sargs_value_at(arg_index); 752 | } 753 | else { 754 | return def; 755 | } 756 | } 757 | 758 | SOKOL_API_IMPL bool sargs_equals(const char* key, const char* val) { 759 | SOKOL_ASSERT(_sargs.valid && key && val); 760 | return 0 == strcmp(sargs_value(key), val); 761 | } 762 | 763 | SOKOL_API_IMPL bool sargs_boolean(const char* key) { 764 | const char* val = sargs_value(key); 765 | return (0 == strcmp("true", val)) || 766 | (0 == strcmp("yes", val)) || 767 | (0 == strcmp("on", val)); 768 | } 769 | 770 | #endif /* SOKOL_ARGS_IMPL */ 771 | -------------------------------------------------------------------------------- /sokol_glue.h: -------------------------------------------------------------------------------- 1 | #if defined(SOKOL_IMPL) && !defined(SOKOL_GLUE_IMPL) 2 | #define SOKOL_GLUE_IMPL 3 | #endif 4 | #ifndef SOKOL_GLUE_INCLUDED 5 | /* 6 | sokol_glue.h -- glue helper functions for sokol headers 7 | 8 | Project URL: https://github.com/floooh/sokol 9 | 10 | Do this: 11 | #define SOKOL_IMPL or 12 | #define SOKOL_GLUE_IMPL 13 | before you include this file in *one* C or C++ file to create the 14 | implementation. 15 | 16 | ...optionally provide the following macros to override defaults: 17 | 18 | SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) 19 | SOKOL_GLUE_API_DECL - public function declaration prefix (default: extern) 20 | SOKOL_API_DECL - same as SOKOL_GLUE_API_DECL 21 | SOKOL_API_IMPL - public function implementation prefix (default: -) 22 | 23 | If sokol_glue.h is compiled as a DLL, define the following before 24 | including the declaration or implementation: 25 | 26 | SOKOL_DLL 27 | 28 | On Windows, SOKOL_DLL will define SOKOL_GLUE_API_DECL as __declspec(dllexport) 29 | or __declspec(dllimport) as needed. 30 | 31 | OVERVIEW 32 | ======== 33 | The sokol core headers should not depend on each other, but sometimes 34 | it's useful to have a set of helper functions as "glue" between 35 | two or more sokol headers. 36 | 37 | This is what sokol_glue.h is for. Simply include the header after other 38 | sokol headers (both for the implementation and declaration), and 39 | depending on what headers have been included before, sokol_glue.h 40 | will make available "glue functions". 41 | 42 | PROVIDED FUNCTIONS 43 | ================== 44 | 45 | - if sokol_app.h and sokol_gfx.h is included: 46 | 47 | sg_context_desc sapp_sgcontext(void): 48 | 49 | Returns an initialized sg_context_desc function initialized 50 | by calling sokol_app.h functions. 51 | 52 | LICENSE 53 | ======= 54 | zlib/libpng license 55 | 56 | Copyright (c) 2018 Andre Weissflog 57 | 58 | This software is provided 'as-is', without any express or implied warranty. 59 | In no event will the authors be held liable for any damages arising from the 60 | use of this software. 61 | 62 | Permission is granted to anyone to use this software for any purpose, 63 | including commercial applications, and to alter it and redistribute it 64 | freely, subject to the following restrictions: 65 | 66 | 1. The origin of this software must not be misrepresented; you must not 67 | claim that you wrote the original software. If you use this software in a 68 | product, an acknowledgment in the product documentation would be 69 | appreciated but is not required. 70 | 71 | 2. Altered source versions must be plainly marked as such, and must not 72 | be misrepresented as being the original software. 73 | 74 | 3. This notice may not be removed or altered from any source 75 | distribution. 76 | */ 77 | #define SOKOL_GLUE_INCLUDED 78 | 79 | #if defined(SOKOL_API_DECL) && !defined(SOKOL_GLUE_API_DECL) 80 | #define SOKOL_GLUE_API_DECL SOKOL_API_DECL 81 | #endif 82 | #ifndef SOKOL_GLUE_API_DECL 83 | #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GLUE_IMPL) 84 | #define SOKOL_GLUE_API_DECL __declspec(dllexport) 85 | #elif defined(_WIN32) && defined(SOKOL_DLL) 86 | #define SOKOL_GLUE_API_DECL __declspec(dllimport) 87 | #else 88 | #define SOKOL_GLUE_API_DECL extern 89 | #endif 90 | #endif 91 | 92 | #ifdef __cplusplus 93 | extern "C" { 94 | #endif 95 | 96 | #if defined(SOKOL_GFX_INCLUDED) && defined(SOKOL_APP_INCLUDED) 97 | SOKOL_GLUE_API_DECL sg_context_desc sapp_sgcontext(void); 98 | SOKOL_GLUE_API_DECL sg_context_desc sapp_sgcontext_window(sapp_window window); 99 | #endif 100 | 101 | #ifdef __cplusplus 102 | } /* extern "C" */ 103 | #endif 104 | #endif /* SOKOL_GLUE_INCLUDED */ 105 | 106 | /*-- IMPLEMENTATION ----------------------------------------------------------*/ 107 | #ifdef SOKOL_GLUE_IMPL 108 | #define SOKOL_GLUE_IMPL_INCLUDED (1) 109 | #include /* memset */ 110 | 111 | #ifndef SOKOL_API_IMPL 112 | #define SOKOL_API_IMPL 113 | #endif 114 | 115 | #if defined(SOKOL_GFX_INCLUDED) && defined(SOKOL_APP_INCLUDED) 116 | 117 | _SOKOL_PRIVATE const void* _sapp_d3d11_window_get_render_target_view_cb(void* user_data) { 118 | sapp_window window_id; 119 | window_id.id = (uint32_t)(uintptr_t)user_data; 120 | return sapp_d3d11_window_get_render_target_view(window_id); 121 | } 122 | 123 | _SOKOL_PRIVATE const void* _sapp_d3d11_window_get_depth_stencil_view_cb(void* user_data) { 124 | sapp_window window_id; 125 | window_id.id = (uint32_t)(uintptr_t)user_data; 126 | return sapp_d3d11_window_get_depth_stencil_view(window_id); 127 | } 128 | 129 | SOKOL_API_IMPL sg_context_desc sapp_sgcontext_window(sapp_window window) { 130 | sg_context_desc desc; 131 | memset(&desc, 0, sizeof(desc)); 132 | desc.color_format = (sg_pixel_format) sapp_color_format(); 133 | desc.depth_format = (sg_pixel_format) sapp_depth_format(); 134 | desc.sample_count = sapp_sample_count(); 135 | desc.gl.force_gles2 = sapp_gles2(); 136 | desc.metal.device = sapp_metal_get_device(); 137 | desc.metal.renderpass_descriptor_cb = sapp_metal_get_renderpass_descriptor; 138 | desc.metal.drawable_cb = sapp_metal_get_drawable; 139 | desc.d3d11.device = sapp_d3d11_get_device(); 140 | desc.d3d11.device_context = sapp_d3d11_get_device_context(); 141 | desc.d3d11.render_target_view_userdata_cb = _sapp_d3d11_window_get_render_target_view_cb; 142 | desc.d3d11.depth_stencil_view_userdata_cb = _sapp_d3d11_window_get_depth_stencil_view_cb; 143 | desc.d3d11.user_data = (void*) (uintptr_t)window.id; 144 | desc.wgpu.device = sapp_wgpu_get_device(); 145 | desc.wgpu.render_view_cb = sapp_wgpu_get_render_view; 146 | desc.wgpu.resolve_view_cb = sapp_wgpu_get_resolve_view; 147 | desc.wgpu.depth_stencil_view_cb = sapp_wgpu_get_depth_stencil_view; 148 | return desc; 149 | } 150 | 151 | SOKOL_API_IMPL sg_context_desc sapp_sgcontext(void) { 152 | return sapp_sgcontext_window(SOKOL_APP_WINDOW_MAIN_ID()); 153 | } 154 | #endif 155 | 156 | #endif /* SOKOL_GLUE_IMPL */ 157 | -------------------------------------------------------------------------------- /sokol_time.h: -------------------------------------------------------------------------------- 1 | #if defined(SOKOL_IMPL) && !defined(SOKOL_TIME_IMPL) 2 | #define SOKOL_TIME_IMPL 3 | #endif 4 | #ifndef SOKOL_TIME_INCLUDED 5 | /* 6 | sokol_time.h -- simple cross-platform time measurement 7 | 8 | Project URL: https://github.com/floooh/sokol 9 | 10 | Do this: 11 | #define SOKOL_IMPL or 12 | #define SOKOL_TIME_IMPL 13 | before you include this file in *one* C or C++ file to create the 14 | implementation. 15 | 16 | Optionally provide the following defines with your own implementations: 17 | SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) 18 | SOKOL_TIME_API_DECL - public function declaration prefix (default: extern) 19 | SOKOL_API_DECL - same as SOKOL_TIME_API_DECL 20 | SOKOL_API_IMPL - public function implementation prefix (default: -) 21 | 22 | If sokol_time.h is compiled as a DLL, define the following before 23 | including the declaration or implementation: 24 | 25 | SOKOL_DLL 26 | 27 | On Windows, SOKOL_DLL will define SOKOL_TIME_API_DECL as __declspec(dllexport) 28 | or __declspec(dllimport) as needed. 29 | 30 | void stm_setup(); 31 | Call once before any other functions to initialize sokol_time 32 | (this calls for instance QueryPerformanceFrequency on Windows) 33 | 34 | uint64_t stm_now(); 35 | Get current point in time in unspecified 'ticks'. The value that 36 | is returned has no relation to the 'wall-clock' time and is 37 | not in a specific time unit, it is only useful to compute 38 | time differences. 39 | 40 | uint64_t stm_diff(uint64_t new, uint64_t old); 41 | Computes the time difference between new and old. This will always 42 | return a positive, non-zero value. 43 | 44 | uint64_t stm_since(uint64_t start); 45 | Takes the current time, and returns the elapsed time since start 46 | (this is a shortcut for "stm_diff(stm_now(), start)") 47 | 48 | uint64_t stm_laptime(uint64_t* last_time); 49 | This is useful for measuring frame time and other recurring 50 | events. It takes the current time, returns the time difference 51 | to the value in last_time, and stores the current time in 52 | last_time for the next call. If the value in last_time is 0, 53 | the return value will be zero (this usually happens on the 54 | very first call). 55 | 56 | uint64_t stm_round_to_common_refresh_rate(uint64_t duration) 57 | This oddly named function takes a measured frame time and 58 | returns the closest "nearby" common display refresh rate frame duration 59 | in ticks. If the input duration isn't close to any common display 60 | refresh rate, the input duration will be returned unchanged as a fallback. 61 | The main purpose of this function is to remove jitter/inaccuracies from 62 | measured frame times, and instead use the display refresh rate as 63 | frame duration. 64 | 65 | Use the following functions to convert a duration in ticks into 66 | useful time units: 67 | 68 | double stm_sec(uint64_t ticks); 69 | double stm_ms(uint64_t ticks); 70 | double stm_us(uint64_t ticks); 71 | double stm_ns(uint64_t ticks); 72 | Converts a tick value into seconds, milliseconds, microseconds 73 | or nanoseconds. Note that not all platforms will have nanosecond 74 | or even microsecond precision. 75 | 76 | Uses the following time measurement functions under the hood: 77 | 78 | Windows: QueryPerformanceFrequency() / QueryPerformanceCounter() 79 | MacOS/iOS: mach_absolute_time() 80 | emscripten: performance.now() 81 | Linux+others: clock_gettime(CLOCK_MONOTONIC) 82 | 83 | zlib/libpng license 84 | 85 | Copyright (c) 2018 Andre Weissflog 86 | 87 | This software is provided 'as-is', without any express or implied warranty. 88 | In no event will the authors be held liable for any damages arising from the 89 | use of this software. 90 | 91 | Permission is granted to anyone to use this software for any purpose, 92 | including commercial applications, and to alter it and redistribute it 93 | freely, subject to the following restrictions: 94 | 95 | 1. The origin of this software must not be misrepresented; you must not 96 | claim that you wrote the original software. If you use this software in a 97 | product, an acknowledgment in the product documentation would be 98 | appreciated but is not required. 99 | 100 | 2. Altered source versions must be plainly marked as such, and must not 101 | be misrepresented as being the original software. 102 | 103 | 3. This notice may not be removed or altered from any source 104 | distribution. 105 | */ 106 | #define SOKOL_TIME_INCLUDED (1) 107 | #include 108 | 109 | #if defined(SOKOL_API_DECL) && !defined(SOKOL_TIME_API_DECL) 110 | #define SOKOL_TIME_API_DECL SOKOL_API_DECL 111 | #endif 112 | #ifndef SOKOL_TIME_API_DECL 113 | #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_TIME_IMPL) 114 | #define SOKOL_TIME_API_DECL __declspec(dllexport) 115 | #elif defined(_WIN32) && defined(SOKOL_DLL) 116 | #define SOKOL_TIME_API_DECL __declspec(dllimport) 117 | #else 118 | #define SOKOL_TIME_API_DECL extern 119 | #endif 120 | #endif 121 | 122 | #ifdef __cplusplus 123 | extern "C" { 124 | #endif 125 | 126 | SOKOL_TIME_API_DECL void stm_setup(void); 127 | SOKOL_TIME_API_DECL uint64_t stm_now(void); 128 | SOKOL_TIME_API_DECL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks); 129 | SOKOL_TIME_API_DECL uint64_t stm_since(uint64_t start_ticks); 130 | SOKOL_TIME_API_DECL uint64_t stm_laptime(uint64_t* last_time); 131 | SOKOL_TIME_API_DECL uint64_t stm_round_to_common_refresh_rate(uint64_t frame_ticks); 132 | SOKOL_TIME_API_DECL double stm_sec(uint64_t ticks); 133 | SOKOL_TIME_API_DECL double stm_ms(uint64_t ticks); 134 | SOKOL_TIME_API_DECL double stm_us(uint64_t ticks); 135 | SOKOL_TIME_API_DECL double stm_ns(uint64_t ticks); 136 | 137 | #ifdef __cplusplus 138 | } /* extern "C" */ 139 | #endif 140 | #endif // SOKOL_TIME_INCLUDED 141 | 142 | /*-- IMPLEMENTATION ----------------------------------------------------------*/ 143 | #ifdef SOKOL_TIME_IMPL 144 | #define SOKOL_TIME_IMPL_INCLUDED (1) 145 | #include /* memset */ 146 | 147 | #ifndef SOKOL_API_IMPL 148 | #define SOKOL_API_IMPL 149 | #endif 150 | #ifndef SOKOL_ASSERT 151 | #include 152 | #define SOKOL_ASSERT(c) assert(c) 153 | #endif 154 | #ifndef _SOKOL_PRIVATE 155 | #if defined(__GNUC__) || defined(__clang__) 156 | #define _SOKOL_PRIVATE __attribute__((unused)) static 157 | #else 158 | #define _SOKOL_PRIVATE static 159 | #endif 160 | #endif 161 | 162 | #if defined(_WIN32) 163 | #ifndef WIN32_LEAN_AND_MEAN 164 | #define WIN32_LEAN_AND_MEAN 165 | #endif 166 | #include 167 | typedef struct { 168 | uint32_t initialized; 169 | LARGE_INTEGER freq; 170 | LARGE_INTEGER start; 171 | } _stm_state_t; 172 | #elif defined(__APPLE__) && defined(__MACH__) 173 | #include 174 | typedef struct { 175 | uint32_t initialized; 176 | mach_timebase_info_data_t timebase; 177 | uint64_t start; 178 | } _stm_state_t; 179 | #elif defined(__EMSCRIPTEN__) 180 | #include 181 | typedef struct { 182 | uint32_t initialized; 183 | double start; 184 | } _stm_state_t; 185 | #else /* anything else, this will need more care for non-Linux platforms */ 186 | #ifdef ESP8266 187 | // On the ESP8266, clock_gettime ignores the first argument and CLOCK_MONOTONIC isn't defined 188 | #define CLOCK_MONOTONIC 0 189 | #endif 190 | #include 191 | typedef struct { 192 | uint32_t initialized; 193 | uint64_t start; 194 | } _stm_state_t; 195 | #endif 196 | static _stm_state_t _stm; 197 | 198 | /* prevent 64-bit overflow when computing relative timestamp 199 | see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3 200 | */ 201 | #if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) 202 | _SOKOL_PRIVATE int64_t int64_muldiv(int64_t value, int64_t numer, int64_t denom) { 203 | int64_t q = value / denom; 204 | int64_t r = value % denom; 205 | return q * numer + r * numer / denom; 206 | } 207 | #endif 208 | 209 | #if defined(__EMSCRIPTEN__) 210 | EM_JS(double, stm_js_perfnow, (void), { 211 | return performance.now(); 212 | }); 213 | #endif 214 | 215 | SOKOL_API_IMPL void stm_setup(void) { 216 | memset(&_stm, 0, sizeof(_stm)); 217 | _stm.initialized = 0xABCDABCD; 218 | #if defined(_WIN32) 219 | QueryPerformanceFrequency(&_stm.freq); 220 | QueryPerformanceCounter(&_stm.start); 221 | #elif defined(__APPLE__) && defined(__MACH__) 222 | mach_timebase_info(&_stm.timebase); 223 | _stm.start = mach_absolute_time(); 224 | #elif defined(__EMSCRIPTEN__) 225 | _stm.start = stm_js_perfnow(); 226 | #else 227 | struct timespec ts; 228 | clock_gettime(CLOCK_MONOTONIC, &ts); 229 | _stm.start = (uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec; 230 | #endif 231 | } 232 | 233 | SOKOL_API_IMPL uint64_t stm_now(void) { 234 | SOKOL_ASSERT(_stm.initialized == 0xABCDABCD); 235 | uint64_t now; 236 | #if defined(_WIN32) 237 | LARGE_INTEGER qpc_t; 238 | QueryPerformanceCounter(&qpc_t); 239 | now = (uint64_t) int64_muldiv(qpc_t.QuadPart - _stm.start.QuadPart, 1000000000, _stm.freq.QuadPart); 240 | #elif defined(__APPLE__) && defined(__MACH__) 241 | const uint64_t mach_now = mach_absolute_time() - _stm.start; 242 | now = (uint64_t) int64_muldiv((int64_t)mach_now, (int64_t)_stm.timebase.numer, (int64_t)_stm.timebase.denom); 243 | #elif defined(__EMSCRIPTEN__) 244 | double js_now = stm_js_perfnow() - _stm.start; 245 | SOKOL_ASSERT(js_now >= 0.0); 246 | now = (uint64_t) (js_now * 1000000.0); 247 | #else 248 | struct timespec ts; 249 | clock_gettime(CLOCK_MONOTONIC, &ts); 250 | now = ((uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec) - _stm.start; 251 | #endif 252 | return now; 253 | } 254 | 255 | SOKOL_API_IMPL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks) { 256 | if (new_ticks > old_ticks) { 257 | return new_ticks - old_ticks; 258 | } 259 | else { 260 | return 1; 261 | } 262 | } 263 | 264 | SOKOL_API_IMPL uint64_t stm_since(uint64_t start_ticks) { 265 | return stm_diff(stm_now(), start_ticks); 266 | } 267 | 268 | SOKOL_API_IMPL uint64_t stm_laptime(uint64_t* last_time) { 269 | SOKOL_ASSERT(last_time); 270 | uint64_t dt = 0; 271 | uint64_t now = stm_now(); 272 | if (0 != *last_time) { 273 | dt = stm_diff(now, *last_time); 274 | } 275 | *last_time = now; 276 | return dt; 277 | } 278 | 279 | // first number is frame duration in ns, second number is tolerance in ns, 280 | // the resulting min/max values must not overlap! 281 | static const uint64_t _stm_refresh_rates[][2] = { 282 | { 16666667, 1000000 }, // 60 Hz: 16.6667 +- 1ms 283 | { 13888889, 250000 }, // 72 Hz: 13.8889 +- 0.25ms 284 | { 13333333, 250000 }, // 75 Hz: 13.3333 +- 0.25ms 285 | { 11764706, 250000 }, // 85 Hz: 11.7647 +- 0.25 286 | { 11111111, 250000 }, // 90 Hz: 11.1111 +- 0.25ms 287 | { 8333333, 500000 }, // 120 Hz: 8.3333 +- 0.5ms 288 | { 6944445, 500000 }, // 144 Hz: 6.9445 +- 0.5ms 289 | { 4166667, 1000000 }, // 240 Hz: 4.1666 +- 1ms 290 | { 0, 0 }, // keep the last element always at zero 291 | }; 292 | 293 | SOKOL_API_IMPL uint64_t stm_round_to_common_refresh_rate(uint64_t ticks) { 294 | uint64_t ns; 295 | int i = 0; 296 | while (0 != (ns = _stm_refresh_rates[i][0])) { 297 | uint64_t tol = _stm_refresh_rates[i][1]; 298 | if ((ticks > (ns - tol)) && (ticks < (ns + tol))) { 299 | return ns; 300 | } 301 | i++; 302 | } 303 | // fallthough: didn't fit into any buckets 304 | return ticks; 305 | } 306 | 307 | SOKOL_API_IMPL double stm_sec(uint64_t ticks) { 308 | return (double)ticks / 1000000000.0; 309 | } 310 | 311 | SOKOL_API_IMPL double stm_ms(uint64_t ticks) { 312 | return (double)ticks / 1000000.0; 313 | } 314 | 315 | SOKOL_API_IMPL double stm_us(uint64_t ticks) { 316 | return (double)ticks / 1000.0; 317 | } 318 | 319 | SOKOL_API_IMPL double stm_ns(uint64_t ticks) { 320 | return (double)ticks; 321 | } 322 | #endif /* SOKOL_TIME_IMPL */ 323 | 324 | -------------------------------------------------------------------------------- /util/simgui.glsl: -------------------------------------------------------------------------------- 1 | // D:\ikrima\src\personal\tolva\tools\sokol-tools-bin\sokol-shdc.exe --input BoneViz.glsl --output BoneViz.glsl.h --slang hlsl5 -f sokol_impl -e msvc 2 | // D:\ikrima\src\personal\tolva\tools\sokol-tools-bin\sokol-shdc.exe -i simgui.glsl -o simgui.h -l glsl330:glsl100:hlsl4:metal_macos:metal_ios:metal_sim:wgpu -b 3 | 4 | @module _simgui 5 | 6 | @vs vs 7 | uniform vs_params { 8 | vec4 disp_rect; 9 | }; 10 | in vec2 position; 11 | in vec2 texcoord0; 12 | in vec4 color0; 13 | out vec2 uv; 14 | out vec4 color; 15 | void main() { 16 | gl_Position = vec4((((position-disp_rect.xy)/disp_rect.zw)-0.5)*vec2(2.0,-2.0), 0.5, 1.0); 17 | uv = texcoord0; 18 | color = color0; 19 | } 20 | @end 21 | 22 | @fs fs 23 | uniform sampler2D tex; 24 | in vec2 uv; 25 | in vec4 color; 26 | out vec4 frag_color; 27 | void main() { 28 | frag_color = texture(tex, uv) * color; 29 | } 30 | @end 31 | 32 | @program simgui vs fs 33 | -------------------------------------------------------------------------------- /util/sokol_memtrack.h: -------------------------------------------------------------------------------- 1 | #if defined(SOKOL_IMPL) && !defined(SOKOL_MEMTRACK_IMPL) 2 | #define SOKOL_MEMTRACK_IMPL 3 | #endif 4 | #ifndef SOKOL_MEMTRACK_INCLUDED 5 | /* 6 | sokol_memtrack.h -- memory allocation wrapper to track memory usage 7 | of sokol libraries 8 | 9 | Project URL: https://github.com/floooh/sokol 10 | 11 | Simply include this file before the sokol header implementation includes 12 | and after the SOKOL_IMPL macro: 13 | 14 | #define SOKOL_IMPL 15 | #include "sokol_memtrack.h" 16 | #include "sokol_app.h" 17 | #include "sokol_gfx.h" 18 | ... 19 | 20 | Optionally provide the following defines with your own implementations: 21 | 22 | SOKOL_MEMTRACK_API_DECL - public function declaration prefix (default: extern) 23 | SOKOL_API_DECL - same as SOKOL_MEMTRACK_API_DECL 24 | SOKOL_API_IMPL - public function implementation prefix (default: -) 25 | 26 | If sokol_memtrack.h is compiled as a DLL, define the following before 27 | including the declaration or implementation: 28 | 29 | SOKOL_DLL 30 | 31 | The sokol_memtrack.h header will redirect the macros SOKOL_MALLOC, 32 | SOKOL_FREE and SOKOL_CALLOC to use its own function wrappers which 33 | keep track of number and size of allocations. The wrapper functions 34 | then call the CRT functions malloc(), calloc() or free(). 35 | 36 | Naturally, only the memory management calls in sokol header code are tracked, 37 | not in underlying APIs such as D3D11 or OpenGL. 38 | 39 | To get the current number and overall size of allocations, call: 40 | 41 | smemtrack_info_t alloc_info = smemtrack_info(); 42 | 43 | int num_allocations = info.num_alloc; 44 | int allocation_size = info.num_bytes; 45 | 46 | The allocation wrapper functions are *not* thread-safe (which shouldn't 47 | be a problem because the sokol headers don't allocate or deallocate 48 | in threads). 49 | 50 | LICENSE 51 | ======= 52 | 53 | zlib/libpng license 54 | 55 | Copyright (c) 2018 Andre Weissflog 56 | 57 | This software is provided 'as-is', without any express or implied warranty. 58 | In no event will the authors be held liable for any damages arising from the 59 | use of this software. 60 | 61 | Permission is granted to anyone to use this software for any purpose, 62 | including commercial applications, and to alter it and redistribute it 63 | freely, subject to the following restrictions: 64 | 65 | 1. The origin of this software must not be misrepresented; you must not 66 | claim that you wrote the original software. If you use this software in a 67 | product, an acknowledgment in the product documentation would be 68 | appreciated but is not required. 69 | 70 | 2. Altered source versions must be plainly marked as such, and must not 71 | be misrepresented as being the original software. 72 | 73 | 3. This notice may not be removed or altered from any source 74 | distribution. 75 | */ 76 | #define SOKOL_MEMTRACK_INCLUDED (1) 77 | #include 78 | 79 | #if defined(SOKOL_API_DECL) && !defined(SOKOL_MEMTRACK_API_DECL) 80 | #define SOKOL_MEMTRACK_API_DECL SOKOL_API_DECL 81 | #endif 82 | #ifndef SOKOL_MEMTRACK_API_DECL 83 | #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_MEMTRACK_IMPL) 84 | #define SOKOL_MEMTRACK_API_DECL __declspec(dllexport) 85 | #elif defined(_WIN32) && defined(SOKOL_DLL) 86 | #define SOKOL_MEMTRACK_API_DECL __declspec(dllimport) 87 | #else 88 | #define SOKOL_MEMTRACK_API_DECL extern 89 | #endif 90 | #endif 91 | 92 | #ifdef __cplusplus 93 | extern "C" { 94 | #endif 95 | 96 | typedef struct smemtrack_info_t { 97 | int num_allocs; 98 | int num_bytes; 99 | } smemtrack_info_t; 100 | 101 | SOKOL_MEMTRACK_API_DECL smemtrack_info_t smemtrack_info(void); 102 | 103 | #ifdef __cplusplus 104 | } /* extern "C" */ 105 | #endif 106 | #endif /* SOKOL_MEMTRACK_INCLUDED */ 107 | 108 | /*=== IMPLEMENTATION =========================================================*/ 109 | #ifdef SOKOL_MEMTRACK_IMPL 110 | #define SOKOL_MEMTRACK_IMPL_INCLUDED (1) 111 | #include /* malloc, free, calloc */ 112 | #include /* memset */ 113 | #include /* size_t */ 114 | 115 | #ifndef SOKOL_API_IMPL 116 | #define SOKOL_API_IMPL 117 | #endif 118 | #ifndef SOKOL_DEBUG 119 | #ifndef NDEBUG 120 | #define SOKOL_DEBUG (1) 121 | #endif 122 | #endif 123 | #ifndef _SOKOL_PRIVATE 124 | #if defined(__GNUC__) || defined(__clang__) 125 | #define _SOKOL_PRIVATE __attribute__((unused)) static 126 | #else 127 | #define _SOKOL_PRIVATE static 128 | #endif 129 | #endif 130 | 131 | #define SOKOL_MALLOC(s) _smemtrack_malloc(s) 132 | #define SOKOL_FREE(p) _smemtrack_free(p) 133 | #define SOKOL_CALLOC(n,s) _smemtrack_calloc(n,s) 134 | 135 | #define _SMEMTRACK_HEADER_SIZE (16) 136 | 137 | static struct { 138 | smemtrack_info_t state; 139 | } _smemtrack; 140 | 141 | _SOKOL_PRIVATE void* _smemtrack_malloc(size_t size) { 142 | _smemtrack.state.num_allocs++; 143 | _smemtrack.state.num_bytes += (int) size; 144 | uint8_t* ptr = (uint8_t*) malloc(size + _SMEMTRACK_HEADER_SIZE); 145 | *(size_t*)ptr = size; 146 | return ptr + _SMEMTRACK_HEADER_SIZE; 147 | } 148 | 149 | _SOKOL_PRIVATE void _smemtrack_free(void* ptr) { 150 | uint8_t* alloc_ptr = ((uint8_t*)ptr) - _SMEMTRACK_HEADER_SIZE; 151 | size_t size = *(size_t*)alloc_ptr; 152 | _smemtrack.state.num_allocs--; 153 | _smemtrack.state.num_bytes -= (int) size; 154 | free(alloc_ptr); 155 | } 156 | 157 | _SOKOL_PRIVATE void* _smemtrack_calloc(size_t num, size_t size) { 158 | size_t mem_size = num * size; 159 | _smemtrack.state.num_allocs++; 160 | _smemtrack.state.num_bytes += (int) mem_size; 161 | uint8_t* ptr = (uint8_t*) malloc(mem_size + _SMEMTRACK_HEADER_SIZE); 162 | memset(ptr + _SMEMTRACK_HEADER_SIZE, 0, mem_size); 163 | *(size_t*)ptr = size; 164 | return ptr + _SMEMTRACK_HEADER_SIZE; 165 | } 166 | 167 | SOKOL_API_IMPL smemtrack_info_t smemtrack_info(void) { 168 | return _smemtrack.state; 169 | } 170 | 171 | #endif /* SOKOL_MEMTRACK_IMPL */ 172 | --------------------------------------------------------------------------------