remove sokol, use sdl3

This commit is contained in:
2024-11-30 12:12:13 -06:00
parent dbb1bf0630
commit 48904b10f0
55 changed files with 1462 additions and 72423 deletions

View File

@@ -60,10 +60,23 @@ if storefront == 'steam'
deps += dependency('qjs-steam',static:false)
endif
cmake = import('cmake')
#cmake_ozz = cmake.subproject('ozz')
deps += dependency('qjs-layout',static:true)
deps += dependency('qjs-nota',static:true)
deps += dependency('qjs-miniz',static:true)
deps += dependency('qjs-soloud',static:true)
deps += dependency('sdl3')
#deps += cc.find_library('openblas')
deps += dependency('cblas')
deps += dependency('physfs',static:true)
#deps += cmake_ozz.dependency('ozz_base')
#deps += dependency('sdl2_image')
#deps += dependency('ogg') # for sdl2_mixer
#deps += dependency('sdl2_mixer')
#deps += dependency('sdl2_ttf')
deps += dependency('threads')
@@ -76,14 +89,14 @@ if get_option('enet')
endif
sources = []
src += ['anim.c', 'config.c', 'datastream.c','font.c','gameobject.c','HandmadeMath.c','jsffi.c','model.c','render.c','render_trace.cpp','script.c','simplex.c','spline.c','texture.c', 'timer.c', 'transform.c','warp.c','yugine.c', 'glad.c', 'wildmatch.c']
src += ['anim.c', 'config.c', 'datastream.c','font.c','gameobject.c','HandmadeMath.c','jsffi.c','model.c','render.c','script.c','simplex.c','spline.c','texture.c', 'timer.c', 'transform.c','warp.c','yugine.c', 'glad.c', 'wildmatch.c']
imsrc = ['GraphEditor.cpp','ImCurveEdit.cpp','ImGradient.cpp','imgui_draw.cpp','imgui_tables.cpp','imgui_widgets.cpp','imgui.cpp','ImGuizmo.cpp','imnodes.cpp','implot_items.cpp','implot.cpp']
srceng = 'source'
tp = srceng / 'thirdparty'
includes = [srceng,tp / 'cgltf',tp / 'imgui',tp / 'par',tp / 'sokol',tp / 'stb',tp,tp / 'pl_mpeg/include']
includes = [srceng,tp / 'cgltf',tp / 'imgui',tp / 'par',tp / 'stb',tp,tp / 'pl_mpeg/include']
foreach file : src
full_path = join_paths('source', file)
@@ -91,10 +104,10 @@ foreach file : src
endforeach
if get_option('editor')
sources += 'source/qjs_imgui.cpp'
foreach imgui : imsrc
sources += tp / 'imgui' / imgui
endforeach
# sources += 'source/qjs_imgui.cpp'
# foreach imgui : imsrc
# sources += tp / 'imgui' / imgui
# endforeach
deps += dependency('qjs-dmon',static:true)
endif

View File

@@ -5,6 +5,7 @@ ar = 'x86_64-w64-mingw32-ar'
windres = 'x86_64-w64-mingw32-windres'
strip = 'x86_64-w64-mingw32-strip'
exe_wrapper = 'wine'
pkgconfig = 'x86_64-w64-mingw32-pkg-config'
[host_machine]
system = 'windows'
@@ -14,4 +15,5 @@ endian = 'little'
[properties]
link_args = ['-static']
needs_exe_wrapper = false
needs_exe_wrapper = false
pkg_config_libdir = '/usr/local/lib64/pkgconfig'

View File

@@ -838,6 +838,7 @@ function make_swizz() {
function arrsetelem(str, n) {
Object.defineProperty(Array.prototype, str, setelem(n));
// Object.defineProperty(Float32Array.prototype, setelem(n));
}
var arr_elems = ["x", "y", "z", "w"];

View File

@@ -323,16 +323,6 @@ globalThis.use = function use(file) {
return ret;
};
var allpaths = io.ls();
allpaths = [...new Set(allpaths)]
//console.log(`found ${allpaths.length} files`);
//console.log(json.encode(allpaths))
io.exists = function(path)
{
return allpaths.includes(path);// || core_db.exists(path);
}
var tmpslurp = io.slurp;
io.slurp = function slurp(path)
{
@@ -349,43 +339,14 @@ io.slurpbytes = function(path)
return ret;
}
var ignore = io.slurp('.prosperonignore')
if (ignore) {
ignore = ignore.split('\n');
for (var ig of ignore) {
if (!ig) continue;
allpaths = allpaths.filter(x => !x.startsWith(ig));
}
}
//var coredata = tmpslurp("core.zip");
//var core_db = miniz.read(coredata);
io.globToRegex = function globToRegex(glob) {
// Escape special regex characters
// Replace glob characters with regex equivalents
let regexStr = glob
.replace(/[\.\\]/g, "\\$&") // Escape literal backslashes and dots
.replace(/([^\*])\*/g, "$1[^/]*") // * matches any number of characters except /
.replace(/\*\*/g, ".*") // ** matches any number of characters, including none
.replace(/\[(.*?)\]/g, "[$1]") // Character sets
.replace(/\?/g, "."); // ? matches any single character
// Ensure the regex matches the whole string
regexStr = "^" + regexStr + "$";
// Create and return the regex object
return new RegExp(regexStr);
};
var ignore = io.slurp('.prosperonignore').split('\n');
var allpaths = io.globfs(ignore);
var tmpglob = io.glob;
io.glob = function glob(pat) {
return allpaths.filter(str => tmpglob(pat,str)).sort();
// var allpaths = io.globfs(ignore);
return allpaths.filter(str => game.glob(pat,str)).sort();
}
console.log(io.glob("sprites/*.png"))
console.log(io.glob("**/render.js"))
function splitPath(path) {
return path.split('/').filter(part => part.length > 0);
}
@@ -441,25 +402,6 @@ function matchPath(pathParts, patternParts) {
return patternIndex === patternParts.length;
}
function doglob(pattern) {
const patternParts = splitPattern(pattern);
const matches = [];
console.log("DOGLOB");
for (let i = 0; i < allpaths.length; i++) {
const path = allpaths[i];
const pathParts = splitPath(path);
console.log(`testing ${json.encode(pathParts)} to ${json.encode(patternParts)}`);
if (matchPath(pathParts, patternParts)) {
matches.push(path);
}
}
// Optional: Sort the matches if needed
return matches.sort();
}
function stripped_use(file, script) {
file = Resources.find_script(file);
@@ -477,11 +419,15 @@ function stripped_use(file, script) {
}
function bare_use(file) {
try {
var script = io.slurp(file);
if (!script) return;
var fnname = file.replace(/[^a-zA-Z0-9_$]/g, "_");
script = `(function ${fnname}() { var self = this; ${script}; })`;
Object.assign(globalThis, os.eval(file, script)());
} catch(e) {
console.log(e);
}
}
profile.enabled = true;

View File

@@ -74,8 +74,6 @@ clay.draw = function draw(size, fn)
box.marginbox.y -= margin.t;
box.marginbox.width += margin.l+margin.r;
box.marginbox.height += margin.t+margin.b;
box.content.y *= -1;
box.boundingbox.y *= -1
box.content.anchor_y = 1;
box.boundingbox.anchor_y = 1;
}
@@ -181,7 +179,7 @@ clay.image = function image(path, ...configs)
{
var config = rectify_configs(configs);
var image = game.texture(path);
config.image = path;
config.image = image;
config.size ??= [image.texture.width, image.texture.height];
add_item(config);
}
@@ -223,9 +221,13 @@ layout.draw_commands = function draw_commands(cmds, pos = [0,0], mousepos)
{
for (var cmd of cmds) {
var boundingbox = geometry.rect_move(cmd.boundingbox,pos);
// boundingbox.x -= boundingbox.width*pos.anchor_x;
// boundingbox.y += boundingbox.height*pos.anchor_y;
var content = geometry.rect_move(cmd.content,pos);
// content.x -= content.width*pos.anchor_x;
// content.y += content.height*pos.anchor_y;
var config = cmd.config;
if (config.hovered && geometry.rect_point_inside(boundingbox, mousepos)) {
config.hovered.__proto__ = config;
config = config.hovered;
@@ -256,8 +258,14 @@ layout.draw_debug = function draw_debug(cmds, pos = [0,0])
{
for (var i = 0; i < cmds.length; i++) {
var cmd = cmds[i];
render.rectangle(geometry.rect_move(cmd.content,pos), dbg_colors.content);
render.rectangle(geometry.rect_move(cmd.boundingbox,pos), dbg_colors.boundingbox);
var boundingbox = geometry.rect_move(cmd.boundingbox,pos);
// boundingbox.x -= boundingbox.width*pos.anchor_x;
// boundingbox.y += boundingbox.height*pos.anchor_y;
var content = geometry.rect_move(cmd.content,pos);
// content.x -= content.width*pos.anchor_x;
// content.y += content.height*pos.anchor_y;
render.rectangle(content, dbg_colors.content);
render.rectangle(boundingbox, dbg_colors.boundingbox);
// render.rectangle(geometry.rect_move(cmd.marginbox,pos), dbg_colors.margin);
}
}

View File

@@ -86,7 +86,6 @@ function add_callgraph(fn, line, time, alone) {
var hittar = 500; // number of call instructions before getting a new frame
var hitpct = 0.2; // amount to randomize it
var start_gather = profile.now();
profile.cpu_start = undefined;

View File

@@ -61,7 +61,7 @@ sim.stepping = function () {
return this.mode === "step";
};
var frame_t = profile.secs(profile.now());
var frame_t = profile.now();
var physlag = 0;
@@ -79,7 +79,7 @@ prosperon.SIGSEGV = function()
prosperon.init = function () {
render.init();
imgui.init();
// imgui.init();
tracy.gpu_init();
globalThis.audio = use("sound.js");
@@ -114,6 +114,8 @@ prosperon.init = function () {
};
if (io.exists("game.js")) global.app = actor.spawn("game.js");
else global.app = actor.spawn("nogame.js");
console.log(io.exists("game.js"))
};
prosperon.release_mode = function () {
@@ -320,6 +322,16 @@ game.texture = function texture(path) {
var parts = path.split(':');
path = Resources.find_image(parts[0]);
if (game.texture.cache[path]) return game.texture.cache[path];
var newimg = {};
var data = io.slurpbytes(path);
newimg.surface = os.make_texture(data);
newimg.texture = render._main.load_texture(newimg.surface);
game.texture.cache[path] = newimg;
console.log(newimg.texture.width);
console.log(newimg.texture.height);
return newimg;
// Look for a cached version
var frame;
var anim_str;

View File

@@ -644,6 +644,7 @@ var polyssboshader;
var sprite_ssbo;
render.init = function () {
return;
textshader = make_shader("text_base.cg");
render.spriteshader = make_shader("sprite.cg");
spritessboshader = make_shader("sprite_ssbo.cg");
@@ -828,24 +829,12 @@ function flush_poly() {
}
render.line = function render_line(points, color = Color.white, thickness = 1, shader = polyssboshader, pipe = base_pipeline) {
for (var i = 0; i < points.length - 1; i++) {
var a = points[i];
var b = points[i + 1];
var poly = poly_e();
var dist = vector.distance(a, b);
poly.transform.move(vector.midpoint(a, b));
poly.transform.rotate([0, 0, 1], vector.angle([b.x - a.x, b.y - a.y]));
poly.transform.scale = [dist, thickness, 1];
poly.color = color;
}
queued_shader = shader;
queued_pipe = pipe;
check_flush(flush_poly);
render._main.line(points, color);
};
/* All draw in screen space */
render.point = function (pos, size, color = Color.blue) {
render.circle(pos, size, size, color);
render._main.point(pos,color);
};
render.cross = function render_cross(pos, size, color = Color.red, thickness = 1) {
@@ -872,15 +861,14 @@ render.coordinate = function render_coordinate(pos, size, color) {
var queued_shader;
var queued_pipe;
render.rectangle = function render_rectangle(rect, color = Color.white, shader = polyssboshader, pipe = base_pipeline) {
var poly = poly_e();
poly.transform.rect(rect);
poly.color = color;
queued_shader = shader;
queued_pipe = pipe;
check_flush(flush_poly);
render._main.fillrect(rect,color);
};
render.text = function text(str, rect, font = cur_font, size = 0, color = Color.white, wrap = -1, ) {
var pos = [rect.x,rect.y];
render._main.fasttext(str, pos, color);
return;
if (typeof font === 'string')
font = render.get_font(font);
@@ -889,7 +877,7 @@ render.text = function text(str, rect, font = cur_font, size = 0, color = Color.
pos.y -= font.descent;
if (rect.anchor_y)
pos.y -= rect.anchor_y*(font.ascent-font.descent);
gui.text(str, pos, size, color, wrap, font); // this puts text into buffer
os.make_text_buffer(str, pos, size, color, wrap, font); // this puts text into buffer
cur_font = font;
check_flush(render.flush_text);
};
@@ -1018,7 +1006,8 @@ function calc_image_size(img)
return [img.texture.width*img.rect.width, img.texture.height*img.rect.height];
}
render.image = function image(image, rect = [0,0], rotation = 0, color = Color.white) {
render.tile = function tile(image, rect = [0,0], color = Color.white)
{
if (!image) throw Error ('Need an image to render.')
if (typeof image === "string")
image = game.texture(image);
@@ -1040,6 +1029,39 @@ render.image = function image(image, rect = [0,0], rotation = 0, color = Color.w
lasttex = tex;
}
render._main.tile(image.texture, rect, image.rect, 1);
return;
}
render.image = function image(image, rect = [0,0], rotation = 0, color) {
if (!image) throw Error ('Need an image to render.')
if (typeof image === "string")
image = game.texture(image);
rect.__proto__ = image.texture;
render._main.texture(image.texture, rect, image.rect, color);
return;
var tex = image.texture;
if (!tex) return;
var image_size = calc_image_size(image); //image.size;
var size = [rect.width ? rect.width : image_size.x, rect.height ? rect.height : image_size.y];
if (!lasttex) {
check_flush(flush_img);
lasttex = tex;
}
if (lasttex !== tex) {
flush_img();
lasttex = tex;
}
render._main.texture(image.texture, rect, image.rect);
return;
var e = img_e();
var pos = [rect.x,rect.y].sub(size.scale([rect.anchor_x, rect.anchor_y]));
e.transform.trs(pos, undefined, size);
@@ -1149,11 +1171,13 @@ render.get_font = function get_font(path,size)
size = Number(parts[1]);
}
path = Resources.find_font(path);
var fontstr = `${path}.${size}`;
var fontstr = `${path}.${size}`;
console.log(`getting ${fontstr}`);
if (fontcache[fontstr]) return fontcache[fontstr];
var data = io.slurpbytes(path);
fontcache[fontstr] = os.make_font(data,size);
fontcache[fontstr].texture = render._main.load_texture(fontcache[fontstr].surface);
return fontcache[fontstr];
}
@@ -1396,10 +1420,13 @@ var imgui_fn = function imgui_fn() {
prosperon.window_render(basesize.scale(mult));
*/
var clearcolor = [100,149,237,255].scale(1/255);
prosperon.render = function prosperon_render() {
try{
render.glue_pass();
render.set_view(prosperon.camera.transform);
// render.glue_pass();
render._main.draw_color(clearcolor);
render._main.clear();
/* render.set_view(prosperon.camera.transform);
render.set_projection_ortho({
l:-prosperon.camera.size.x/2,
r:prosperon.camera.size.x/2,
@@ -1409,22 +1436,24 @@ try{
render.viewport(prosperon.camera.view(), false);
if (render.draw_sprites) render.sprites();
prosperon.draw();
if (render.draw_particles) draw_emitters();
if (render.draw_particles) draw_emitters();
*/
// render.fillmask(0);
render.forceflush();
render.set_projection_ortho({
// render.forceflush();
/* render.set_projection_ortho({
l:0,
r:prosperon.camera.size.x,
b:-prosperon.camera.size.y,
t:0
},-1,1);
render.set_view(unit_transform);
*/
// render.set_view(unit_transform);
prosperon.draw();
if (render.draw_hud) prosperon.hud();
render.forceflush();
// render.forceflush();
render.set_projection_ortho({
/* render.set_projection_ortho({
l:0,
r:prosperon.size.x,
b:-prosperon.size.y,
@@ -1436,9 +1465,10 @@ try{
width:prosperon.size.x,
l:0
}, false);
prosperon.app();
render.forceflush();
if (debug.show) imgui_fn();
*/
// prosperon.app();
// render.forceflush();
// if (debug.show) imgui_fn();
} catch(e) {
throw e;
} finally {
@@ -1448,8 +1478,9 @@ try{
var texdata = os.tex_data(tex)
tracy.image(texdata, tex.width, tex.height);
*/
render.end_pass();
render.commit();
// render.end_pass();
// render.commit();
render._main.present();
endframe();
tracy.gpu_collect();
tracy.end_frame();
@@ -1482,10 +1513,8 @@ prosperon.process = function process() {
layout.newframe();
// check for hot reloading
if (dmon) dmon.poll(dmon_cb);
var dt = profile.secs(profile.now()) - frame_t;
frame_t = profile.secs(profile.now());
var sst = profile.now();
var dt = profile.now() - frame_t;
frame_t = profile.now();
prosperon.appupdate(dt);
input.procdown();
@@ -1496,9 +1525,6 @@ prosperon.process = function process() {
if (sim.mode === "step") sim.pause();
}
profile.pushdata(profile.data.cpu.scripts, profile.now() - sst);
sst = profile.now();
if (sim.mode === "play" || sim.mode === "step") {
/*
physlag += dt;
@@ -1508,14 +1534,10 @@ prosperon.process = function process() {
prosperon.phys2d_step(physics.delta * game.timescale);
prosperon.physupdate(physics.delta * game.timescale);
}
profile.pushdata(profile.data.cpu.physics, profile.now() - sst);
sst = profile.now();
*/
}
tracy.gpu_zone(prosperon.render);
// prosperon.render();
};
return { render };

View File

@@ -301,8 +301,15 @@ Cmdline.register_order(
if (io.exists("config.js")) global.mixin("config.js");
else console.warn("No config.js file found. Starting with default parameters.");
game.engine_start(prosperon);
var window = game.engine_start(prosperon);
console.log(game.renderers());
console.log(game.cameras());
var renderer = window.make_renderer("gpu");
render._main = renderer;
prosperon.init();
while(1) prosperon.process();
},
"Play the game present in this folder.",
);

View File

@@ -65,7 +65,7 @@
LICENSE
This software is in the public domain. Where that dedication is not
This software is in the public domain. Where that dedictaion is not
recognized, you are granted a perpetual, irrevocable license to copy,
distribute, and modify this file as you see fit.

View File

@@ -1,14 +1,5 @@
#include "render.h"
#define SOKOL_TRACE_HOOKS
#define SOKOL_IMPL
#define SOKOL_NO_ENTRY
#include "sokol/sokol_time.h"
#include "sokol/sokol_gfx.h"
#include "sokol/sokol_app.h"
#include "sokol/sokol_log.h"
#include "sokol/sokol_glue.h"
#define CUTE_ASEPRITE_IMPLEMENTATION
#include "cute_aseprite.h"

View File

@@ -10,11 +10,9 @@
#include "cbuf.h"
#include "sokol/sokol_gfx.h"
void datastream_free(JSRuntime *rt,datastream *ds)
{
sg_destroy_image(ds->img);
// sg_destroy_image(ds->img);
plm_destroy(ds->plm);
free(ds);
}
@@ -29,9 +27,9 @@ static void render_frame(plm_t *mpeg, plm_frame_t *frame, struct datastream *ds)
uint8_t rgb[frame->height*frame->width*4];
memset(rgb,255,frame->height*frame->width*4);
plm_frame_to_rgba(frame, rgb, frame->width*4);
sg_image_data imgd = {0};
imgd.subimage[0][0] = SG_RANGE(rgb);
sg_update_image(ds->img, &imgd);
// sg_image_data imgd = {0};
// imgd.subimage[0][0] = SG_RANGE(rgb);
// sg_update_image(ds->img, &imgd);
ds->dirty = true;
}
@@ -49,14 +47,14 @@ struct datastream *ds_openvideo(void *raw, size_t rawlen)
if (!ds->plm)
return NULL;
ds->img = sg_make_image(&(sg_image_desc){
/* ds->img = sg_make_image(&(sg_image_desc){
.width = plm_get_width(ds->plm),
.height = plm_get_height(ds->plm),
.usage = SG_USAGE_STREAM,
.type = SG_IMAGETYPE_2D,
.pixel_format = SG_PIXELFORMAT_RGBA8,
});
*/
plm_set_video_decode_callback(ds->plm, render_frame, ds);
return ds;

View File

@@ -5,16 +5,10 @@
#include <stdint.h>
#include <quickjs.h>
#include "sokol/sokol_gfx.h"
struct soundstream;
struct datastream {
plm_t *plm;
sg_image img;
sg_image y;
sg_image cr;
sg_image cb;
int width;
int height;
int dirty;

View File

@@ -16,19 +16,8 @@
struct sFont *use_font;
struct text_vert {
HMM_Vec2 pos;
HMM_Vec2 wh;
HMM_Vec2 uv;
HMM_Vec2 st;
HMM_Vec4 color;
};
static struct text_vert *text_buffer;
void font_free(JSRuntime *rt, font *f)
{
sg_destroy_image(f->texID);
free(f);
}
@@ -61,7 +50,7 @@ struct sFont *MakeFont(void *ttf_buffer, size_t len, int height) {
if (!ttf_buffer)
return NULL;
int packsize = 2048;
int packsize = 1024;
struct sFont *newfont = calloc(1, sizeof(struct sFont));
newfont->height = height;
@@ -90,21 +79,14 @@ struct sFont *MakeFont(void *ttf_buffer, size_t len, int height) {
newfont->ascent = ascent*emscale;
newfont->descent = descent*emscale;
newfont->linegap = linegap*emscale;
newfont->texture = malloc(sizeof(texture));
newfont->texture->id = sg_make_image(&(sg_image_desc){
.type = SG_IMAGETYPE_2D,
.width = packsize,
.height = packsize,
.pixel_format = SG_PIXELFORMAT_R8,
.usage = SG_USAGE_IMMUTABLE,
.data.subimage[0][0] = {
.ptr = bitmap,
.size = packsize * packsize
}
});
newfont->surface = SDL_CreateSurface(packsize,packsize, SDL_PIXELFORMAT_RGBA32);
if (!newfont->surface) printf("SDL ERROR: %s\n", SDL_GetError());
for (int i = 0; i < packsize; i++)
for (int j = 0; j < packsize; j++)
if (!SDL_WriteSurfacePixel(newfont->surface, j, i, 255,255,255,bitmap[i*packsize+j]))
printf("SDLERROR: %s\n", SDL_GetError());
newfont->texture->width = packsize;
newfont->texture->height = packsize;
printf("FONT SURFACE IS %p\n", newfont->surface);
for (unsigned char c = 32; c < 127; c++) {
stbtt_packedchar glyph = glyphs[c - 32];
@@ -131,8 +113,8 @@ struct sFont *MakeFont(void *ttf_buffer, size_t len, int height) {
return newfont;
}
int text_flush(sg_buffer *buf) {
if (arrlen(text_buffer) == 0) return 0;
int text_flush() {
/* if (arrlen(text_buffer) == 0) return 0;
sg_range verts;
verts.ptr = text_buffer;
@@ -151,24 +133,52 @@ int text_flush(sg_buffer *buf) {
int n = arrlen(text_buffer);
arrsetlen(text_buffer, 0);
return n;
*/
}
void sdrawCharacter(struct Character c, HMM_Vec2 cursor, float scale, struct rgba color) {
void sdrawCharacter(struct text_vert **buffer, struct Character c, HMM_Vec2 cursor, float scale, struct rgba color) {
struct text_vert vert;
vert.pos.x = cursor.X + c.leftbearing;
vert.pos.y = cursor.Y + c.topbearing;
vert.wh = c.size;
// vert.wh = c.size;
// if (vert.pos.x > frame.l || vert.pos.y > frame.t || (vert.pos.y + vert.wh.y) < frame.b || (vert.pos.x + vert.wh.x) < frame.l) return;
vert.uv.x = c.rect.x;
vert.uv.y = c.rect.y;
vert.st.x = c.rect.w;
vert.st.y = c.rect.h;
// vert.st.x = c.rect.w;
// vert.st.y = c.rect.h;
rgba2floats(vert.color.e, color);
arrput(text_buffer, vert);
arrput(*buffer, vert);
}
void draw_char_verts(struct text_vert **buffer, struct Character c, HMM_Vec2 cursor, float scale, struct rgba color)
{
// Adds four verts: bottom left, bottom right, top left, top right
text_vert bl;
bl.pos.x = cursor.X + c.leftbearing;
bl.pos.y = cursor.Y + c.topbearing;
bl.uv.x = c.rect.x;
bl.uv.y = c.rect.y+c.rect.h;
rgba2floats(bl.color.e, color);
arrput(*buffer, bl);
text_vert br = bl;
br.pos.x += c.size.x;
br.uv.x += c.rect.w;
arrput(*buffer, br);
text_vert ul = bl;
ul.pos.y -= c.size.y;
ul.uv.y = c.rect.y;
arrput(*buffer, ul);
text_vert ur = ul;
ur.pos.x += c.size.x;
ur.uv.x += c.rect.w;
arrput(*buffer, ur);
}
const char *esc_color(const char *c, struct rgba *color, struct rgba defc)
@@ -229,7 +239,8 @@ HMM_Vec2 measure_text(const char *text, font *f, float size, float letterSpacing
}
/* pos given in screen coordinates */
void renderText(const char *text, HMM_Vec2 pos, font *f, float scale, struct rgba color, float wrap) {
struct text_vert *renderText(const char *text, HMM_Vec2 pos, font *f, float scale, struct rgba color, float wrap) {
text_vert *buffer = NULL;
int len = strlen(text);
HMM_Vec2 cursor = pos;
@@ -243,11 +254,11 @@ void renderText(const char *text, HMM_Vec2 pos, font *f, float scale, struct rgb
continue;
}
sdrawCharacter(f->Characters[*c], cursor, scale, color);
draw_char_verts(&buffer, f->Characters[*c], cursor, scale, color);
cursor.x += f->Characters[*c].Advance;
}
return;
return buffer;
/*
const char *line, *wordstart, *drawstart;
line = drawstart = text;
@@ -291,5 +302,5 @@ void renderText(const char *text, HMM_Vec2 pos, font *f, float scale, struct rgb
wordstart++;
}
}
}
}*/
}

View File

@@ -1,11 +1,11 @@
#ifndef FONT_H
#define FONT_H
#include "sokol/sokol_gfx.h"
#include "render.h"
#include "HandmadeMath.h"
#include <quickjs.h>
#include "texture.h"
#include <SDL3/SDL.h>
typedef enum {
LEFT,
@@ -14,11 +14,17 @@ typedef enum {
JUSTIFY
} ALIGN;
struct text_vert {
HMM_Vec2 pos;
HMM_Vec2 uv;
HMM_Vec4 color;
};
typedef struct text_vert text_vert;
struct shader;
struct window;
extern sg_buffer text_ssbo;
/// Holds all state information relevant to a character as loaded using FreeType
struct Character {
float Advance; // Horizontal offset to advance to next glyph
@@ -28,14 +34,14 @@ struct Character {
HMM_Vec2 size; // The pixel size of this letter
};
// text data
struct sFont {
uint32_t height; /* in pixels */
float ascent; // pixels
float descent; // pixels
float linegap; //pixels
struct Character Characters[256];
sg_image texID;
texture *texture;
SDL_Surface *surface;
};
typedef struct sFont font;
@@ -44,11 +50,10 @@ typedef struct Character glyph;
void font_free(JSRuntime *rt,font *f);
struct sFont *MakeFont(void *data, size_t len, int height);
void sdrawCharacter(struct Character c, HMM_Vec2 cursor, float scale, struct rgba color);
void renderText(const char *text, HMM_Vec2 pos, font *f, float scale, struct rgba color, float wrap);
struct text_vert *renderText(const char *text, HMM_Vec2 pos, font *f, float scale, struct rgba color, float wrap);
HMM_Vec2 measure_text(const char *text, font *f, float scale, float letterSpacing, float wrap);
// Flushes all letters from renderText calls into the provided buffer
int text_flush(sg_buffer *buf);
int text_flush();
#endif

View File

@@ -1,299 +0,0 @@
#ifndef GIF_LOAD_H
#define GIF_LOAD_H
/** gif_load: A slim, fast and header-only GIF loader written in C.
Original author: hidefromkgb (hidefromkgb@gmail.com)
_________________________________________________________________________
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
_________________________________________________________________________
**/
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h> /** imports uint8_t, uint16_t and uint32_t **/
#ifndef GIF_MGET
#include <stdlib.h>
#define GIF_MGET(m,s,a,c) m = (uint8_t*)realloc((c)? 0 : m, (c)? s : 0UL);
#endif
#ifndef GIF_BIGE
#define GIF_BIGE 0
#endif
#ifndef GIF_EXTR
#define GIF_EXTR static
#endif
#define _GIF_SWAP(h) ((GIF_BIGE)? ((uint16_t)(h << 8) | (h >> 8)) : h)
#pragma pack(push, 1)
struct GIF_WHDR { /** ======== frame writer info: ======== **/
long xdim, ydim, clrs, /** global dimensions, palette size **/
bkgd, tran, /** background index, transparent index **/
intr, mode, /** interlace flag, frame blending mode **/
frxd, fryd, frxo, fryo, /** current frame dimensions and offset **/
time, ifrm, nfrm; /** delay, frame number, frame count **/
uint8_t *bptr; /** frame pixel indices or metadata **/
struct { /** [==== GIF RGB palette element: ====] **/
uint8_t R, G, B; /** [color values - red, green, blue ] **/
} *cpal; /** current palette **/
};
#pragma pack(pop)
enum {GIF_NONE = 0, GIF_CURR = 1, GIF_BKGD = 2, GIF_PREV = 3};
/** [ internal function, do not use ] **/
static long _GIF_SkipChunk(uint8_t **buff, long size) {
long skip;
for (skip = 2, ++size, ++(*buff); ((size -= skip) > 0) && (skip > 1);
*buff += (skip = 1 + **buff));
return size;
}
/** [ internal function, do not use ] **/
static long _GIF_LoadHeader(unsigned gflg, uint8_t **buff, void **rpal,
unsigned fflg, long *size, long flen) {
if (flen && (!(*buff += flen) || ((*size -= flen) <= 0)))
return -2; /** v--[ 0x80: "palette is present" flag ]--, **/
if (flen && (fflg & 0x80)) { /** local palette has priority | **/
*rpal = *buff; /** [ 3L: 3 uint8_t color channels ]--, | **/
*buff += (flen = 2 << (fflg & 7)) * 3L; /** <--| | **/
return ((*size -= flen * 3L) > 0)? flen : -1; /** <--' | **/
} /** no local palette found, checking for the global one | **/
return (gflg & 0x80)? (2 << (gflg & 7)) : 0; /** <-----' **/
}
/** [ internal function, do not use ] **/
static long _GIF_LoadFrame(uint8_t **buff, long *size,
uint8_t *bptr, uint8_t *blen) {
typedef uint16_t GIF_H;
const long GIF_HLEN = sizeof(GIF_H), /** to rid the scope of sizeof **/
GIF_CLEN = 1 << 12; /** code table length: 4096 items **/
GIF_H accu, mask; /** bit accumulator / bit mask **/
long ctbl, iter, /** last code table index / index string iterator **/
prev, curr, /** codes from the stream: previous / current **/
ctsz, ccsz, /** code table bit sizes: min LZW / current **/
bseq, bszc; /** counters: block sequence / bit size **/
uint32_t *code = (uint32_t*)bptr - GIF_CLEN; /** code table pointer **/
/** preparing initial values **/
if ((--(*size) <= GIF_HLEN) || !*++(*buff))
return -4; /** unexpected end of the stream: insufficient size **/
mask = (GIF_H)((1 << (ccsz = (ctsz = *(*buff - 1)) + 1)) - 1);
if ((ctsz < 2) || (ctsz > 8))
return -3; /** min LZW size is out of its nominal [2; 8] bounds **/
if ((ctbl = (1L << ctsz)) != (mask & _GIF_SWAP(*(GIF_H*)(*buff + 1))))
return -2; /** initial code is not equal to min LZW size **/
for (curr = ++ctbl; curr; code[--curr] = 0); /** actual color codes **/
/** getting codes from stream (--size makes up for end-of-stream mark) **/
for (--(*size), bszc = -ccsz, prev = curr = 0;
((*size -= (bseq = *(*buff)++) + 1) >= 0) && bseq; *buff += bseq)
for (; bseq > 0; bseq -= GIF_HLEN, *buff += GIF_HLEN)
for (accu = (GIF_H)(_GIF_SWAP(*(GIF_H*)*buff)
& ((bseq < GIF_HLEN)? ((1U << (8 * bseq)) - 1U) : ~0U)),
curr |= accu << (ccsz + bszc), accu = (GIF_H)(accu >> -bszc),
bszc += 8 * ((bseq < GIF_HLEN)? bseq : GIF_HLEN);
bszc >= 0; bszc -= ccsz, prev = curr, curr = accu,
accu = (GIF_H)(accu >> ccsz))
if (((curr &= mask) & ~1L) == (1L << ctsz)) {
if (~(ctbl = curr + 1) & 1) /** end-of-data code (ED). **/
/** -1: no end-of-stream mark after ED; 1: decoded **/
return (*((*buff += bseq + 1) - 1))? -1 : 1;
mask = (GIF_H)((1 << (ccsz = ctsz + 1)) - 1);
} /** ^- table drop code (TD). TD = 1 << ctsz, ED = TD + 1 **/
else { /** single-pixel (SP) or multi-pixel (MP) code. **/
if (ctbl < GIF_CLEN) { /** is the code table full? **/
if ((ctbl == mask) && (ctbl < GIF_CLEN - 1)) {
mask = (GIF_H)(mask + mask + 1);
ccsz++; /** yes; extending **/
} /** prev = TD? => curr < ctbl = prev **/
code[ctbl] = (uint32_t)prev + (code[prev] & 0xFFF000);
} /** appending SP / MP decoded pixels to the frame **/
prev = (long)code[iter = (ctbl > curr)? curr : prev];
if ((bptr += (prev = (prev >> 12) & 0xFFF)) > blen)
continue; /** skipping pixels above frame capacity **/
for (prev++; (iter &= 0xFFF) >> ctsz;
*bptr-- = (uint8_t)((iter = (long)code[iter]) >> 24));
(bptr += prev)[-prev] = (uint8_t)iter;
if (ctbl < GIF_CLEN) { /** appending the code table **/
if (ctbl == curr)
*bptr++ = (uint8_t)iter;
else if (ctbl < curr)
return -5; /** wrong code in the stream **/
code[ctbl++] += ((uint32_t)iter << 24) + 0x1000;
}
} /** 0: no ED before end-of-stream mark; -4: see above **/
return (++(*size) >= 0)? 0 : -4; /** ^- N.B.: 0 error is recoverable **/
}
/** _________________________________________________________________________
The main loading function. Returns the total number of frames if the data
includes proper GIF ending, and otherwise it returns the number of frames
loaded per current call, multiplied by -1. So, the data may be incomplete
and in this case the function can be called again when more data arrives,
just remember to keep SKIP up to date.
_________________________________________________________________________
DATA: raw data chunk, may be partial
SIZE: size of the data chunk that`s currently present
GWFR: frame writer function, MANDATORY
EAMF: metadata reader function, set to 0 if not needed
ANIM: implementation-specific data (e.g. a structure or a pointer to it)
SKIP: number of frames to skip before resuming
**/
GIF_EXTR long GIF_Load(void *data, long size,
void (*gwfr)(void*, struct GIF_WHDR*),
void (*eamf)(void*, struct GIF_WHDR*),
void *anim, long skip) {
const long GIF_BLEN = (1 << 12) * sizeof(uint32_t);
const uint8_t GIF_EHDM = 0x21, /** extension header mark **/
GIF_FHDM = 0x2C, /** frame header mark **/
GIF_EOFM = 0x3B, /** end-of-file mark **/
GIF_EGCM = 0xF9, /** extension: graphics control mark **/
GIF_EAMM = 0xFF; /** extension: app metadata mark **/
#pragma pack(push, 1)
struct GIF_GHDR { /** ========== GLOBAL GIF HEADER: ========== **/
uint8_t head[6]; /** 'GIF87a' / 'GIF89a' header signature **/
uint16_t xdim, ydim; /** total image width, total image height **/
uint8_t flgs; /** FLAGS:
GlobalPlt bit 7 1: global palette exists
0: local in each frame
ClrRes bit 6-4 bits/channel = ClrRes+1
[reserved] bit 3 0
PixelBits bit 2-0 |Plt| = 2 * 2^PixelBits
**/
uint8_t bkgd, aspr; /** background color index, aspect ratio **/
} *ghdr = (struct GIF_GHDR*)data;
struct GIF_FHDR { /** ======= GIF FRAME MASTER HEADER: ======= **/
uint16_t frxo, fryo; /** offset of this frame in a "full" image **/
uint16_t frxd, fryd; /** frame width, frame height **/
uint8_t flgs; /** FLAGS:
LocalPlt bit 7 1: local palette exists
0: global is used
Interlaced bit 6 1: interlaced frame
0: non-interlaced frame
Sorted bit 5 usually 0
[reserved] bit 4-3 [undefined]
PixelBits bit 2-0 |Plt| = 2 * 2^PixelBits
**/
} *fhdr;
struct GIF_EGCH { /** ==== [EXT] GRAPHICS CONTROL HEADER: ==== **/
uint8_t flgs; /** FLAGS:
[reserved] bit 7-5 [undefined]
BlendMode bit 4-2 000: not set; static GIF
001: leave result as is
010: restore background
011: restore previous
1--: [undefined]
UserInput bit 1 1: show frame till input
0: default; ~99% of GIFs
TransColor bit 0 1: got transparent color
0: frame is fully opaque
**/
uint16_t time; /** delay in GIF time units; 1 unit = 10 ms **/
uint8_t tran; /** transparent color index **/
} *egch = 0;
#pragma pack(pop)
struct GIF_WHDR wtmp, whdr = {0};
long desc, blen;
uint8_t *buff;
/** checking if the stream is not empty and has a 'GIF8[79]a' signature,
the data has sufficient size and frameskip value is non-negative **/
if (!ghdr || (size <= (long)sizeof(*ghdr)) || (*(buff = ghdr->head) != 71)
|| (buff[1] != 73) || (buff[2] != 70) || (buff[3] != 56) || (skip < 0)
|| ((buff[4] != 55) && (buff[4] != 57)) || (buff[5] != 97) || !gwfr)
return 0;
buff = (uint8_t*)(ghdr + 1) /** skipping the global header and palette **/
+ _GIF_LoadHeader(ghdr->flgs, 0, 0, 0, 0, 0L) * 3L;
if ((size -= buff - (uint8_t*)ghdr) <= 0)
return 0;
whdr.xdim = _GIF_SWAP(ghdr->xdim);
whdr.ydim = _GIF_SWAP(ghdr->ydim);
for (whdr.bptr = buff, whdr.bkgd = ghdr->bkgd, blen = --size;
(blen >= 0) && ((desc = *whdr.bptr++) != GIF_EOFM); /** sic: '>= 0' **/
blen = _GIF_SkipChunk(&whdr.bptr, blen) - 1) /** count all frames **/
if (desc == GIF_FHDM) {
fhdr = (struct GIF_FHDR*)whdr.bptr;
if (_GIF_LoadHeader(ghdr->flgs, &whdr.bptr, (void**)&whdr.cpal,
fhdr->flgs, &blen, sizeof(*fhdr)) <= 0)
break;
whdr.frxd = _GIF_SWAP(fhdr->frxd);
whdr.fryd = _GIF_SWAP(fhdr->fryd);
whdr.frxo = (whdr.frxd > whdr.frxo)? whdr.frxd : whdr.frxo;
whdr.fryo = (whdr.fryd > whdr.fryo)? whdr.fryd : whdr.fryo;
whdr.ifrm++;
}
blen = whdr.frxo * whdr.fryo * (long)sizeof(*whdr.bptr);
GIF_MGET(whdr.bptr, (unsigned long)(blen + GIF_BLEN + 2), anim, 1)
whdr.nfrm = (desc != GIF_EOFM)? -whdr.ifrm : whdr.ifrm;
for (whdr.bptr += GIF_BLEN, whdr.ifrm = -1; blen /** load all frames **/
&& (skip < ((whdr.nfrm < 0)? -whdr.nfrm : whdr.nfrm)) && (size >= 0);
size = (desc != GIF_EOFM)? ((desc != GIF_FHDM) || (skip > whdr.ifrm))?
_GIF_SkipChunk(&buff, size) - 1 : size - 1 : -1)
if ((desc = *buff++) == GIF_FHDM) { /** found a frame **/
whdr.intr = !!((fhdr = (struct GIF_FHDR*)buff)->flgs & 0x40);
*(void**)&whdr.cpal = (void*)(ghdr + 1); /** interlaced? -^ **/
whdr.clrs = _GIF_LoadHeader(ghdr->flgs, &buff, (void**)&whdr.cpal,
fhdr->flgs, &size, sizeof(*fhdr));
if ((skip <= ++whdr.ifrm) && ((whdr.clrs <= 0)
|| (_GIF_LoadFrame(&buff, &size,
whdr.bptr, whdr.bptr + blen) < 0)))
size = -(whdr.ifrm--) - 1; /** failed to load the frame **/
else if (skip <= whdr.ifrm) {
whdr.frxd = _GIF_SWAP(fhdr->frxd);
whdr.fryd = _GIF_SWAP(fhdr->fryd);
whdr.frxo = _GIF_SWAP(fhdr->frxo);
whdr.fryo = _GIF_SWAP(fhdr->fryo);
whdr.time = (egch)? _GIF_SWAP(egch->time) : 0;
whdr.tran = (egch && (egch->flgs & 0x01))? egch->tran : -1;
whdr.time = (egch && (egch->flgs & 0x02))? -whdr.time - 1
: whdr.time;
whdr.mode = (egch && !(egch->flgs & 0x10))?
(egch->flgs & 0x0C) >> 2 : GIF_NONE;
egch = 0;
wtmp = whdr;
gwfr(anim, &wtmp); /** passing the frame to the caller **/
}
}
else if (desc == GIF_EHDM) { /** found an extension **/
if (*buff == GIF_EGCM) /** graphics control ext. **/
egch = (struct GIF_EGCH*)(buff + 1 + 1);
else if ((*buff == GIF_EAMM) && eamf) { /** app metadata ext. **/
wtmp = whdr;
wtmp.bptr = buff + 1 + 1; /** just passing the raw chunk **/
eamf(anim, &wtmp);
}
}
whdr.bptr -= GIF_BLEN; /** for excess pixel codes ----v (here & above) **/
GIF_MGET(whdr.bptr, (unsigned long)(blen + GIF_BLEN + 2), anim, 0)
return (whdr.nfrm < 0)? (skip - whdr.ifrm - 1) : (whdr.ifrm + 1);
}
#undef _GIF_SWAP
#ifdef __cplusplus
}
#endif
#endif /** GIF_LOAD_H **/

File diff suppressed because it is too large Load Diff

View File

@@ -21,13 +21,11 @@
#include "texture.h"
#include "sokol/sokol_gfx.h"
#include "jsffi.h"
unsigned short pack_short_tex(float c) { return c * USHRT_MAX; }
sg_buffer texcoord_floats(float *f, int n)
SDL_GPUBuffer *texcoord_floats(float *f, int n)
{
unsigned short packed[n];
for (int i = 0; i < n; i++) {
@@ -37,43 +35,47 @@ sg_buffer texcoord_floats(float *f, int n)
packed[i] = pack_short_tex(v);
}
return sg_make_buffer(&(sg_buffer_desc){
/* return sg_make_buffer(&(sg_buffer_desc){
.data = SG_RANGE(packed),
.label = "tex coord vert buffer",
});
});*/
return NULL;
}
sg_buffer par_idx_buffer(uint32_t *p, int v)
SDL_GPUBuffer *par_idx_buffer(uint32_t *p, int v)
{
uint16_t idx[v];
for (int i = 0; i < v; i++) idx[i] = p[i];
return sg_make_buffer(&(sg_buffer_desc){
/* return sg_make_buffer(&(sg_buffer_desc){
.data = SG_RANGE(idx),
.type = SG_BUFFERTYPE_INDEXBUFFER
});
});*/
return NULL;
}
sg_buffer float_buffer(float *f, int v)
SDL_GPUBuffer *float_buffer(float *f, int v)
{
return sg_make_buffer(&(sg_buffer_desc){
return NULL;
/* return sg_make_buffer(&(sg_buffer_desc){
.data = (sg_range){
.ptr = f,
.size = sizeof(*f)*v
}
});
});*/
}
sg_buffer index_buffer(float *f, int verts)
SDL_GPUBuffer *index_buffer(float *f, int verts)
{
uint16_t idxs[verts];
return NULL;
/* uint16_t idxs[verts];
for (int i = 0; i < verts; i++)
idxs[i] = f[i];
return sg_make_buffer(&(sg_buffer_desc){
.data = SG_RANGE(idxs),
.type = SG_BUFFERTYPE_INDEXBUFFER,
});
});*/
}
uint32_t pack_int10_n2(float *norm)
@@ -87,40 +89,44 @@ uint32_t pack_int10_n2(float *norm)
}
// Pack an array of normals into
sg_buffer normal_floats(float *f, int n)
SDL_GPUBuffer *normal_floats(float *f, int n)
{
return float_buffer(f, n);
uint32_t packed_norms[n/3];
/* uint32_t packed_norms[n/3];
for (int v = 0, i = 0; v < n/3; v++, i+= 3)
packed_norms[v] = pack_int10_n2(f+i);
return sg_make_buffer(&(sg_buffer_desc){
.data = SG_RANGE(packed_norms),
.label = "normal vert buffer",
});
});*/
}
sg_buffer ubyten_buffer(float *f, int v)
SDL_GPUBuffer *ubyten_buffer(float *f, int v)
{
unsigned char b[v];
return NULL;
/* unsigned char b[v];
for (int i = 0; i < (v); i++)
b[i] = f[i]*255;
return sg_make_buffer(&(sg_buffer_desc){.data=SG_RANGE(b)});
return sg_make_buffer(&(sg_buffer_desc){.data=SG_RANGE(b)});*/
}
sg_buffer ubyte_buffer(float *f, int v)
SDL_GPUBuffer *ubyte_buffer(float *f, int v)
{
unsigned char b[v];
return NULL;
/* unsigned char b[v];
for (int i = 0; i < (v); i++)
b[i] = f[i];
return sg_make_buffer(&(sg_buffer_desc){.data=SG_RANGE(b)});
*/
}
sg_buffer accessor2buffer(cgltf_accessor *a, int type)
SDL_GPUBuffer *accessor2buffer(cgltf_accessor *a, int type)
{
int n = cgltf_accessor_unpack_floats(a, NULL, 0);
return NULL;
/* int n = cgltf_accessor_unpack_floats(a, NULL, 0);
float vs[n];
cgltf_accessor_unpack_floats(a, vs, n);
@@ -150,6 +156,7 @@ sg_buffer accessor2buffer(cgltf_accessor *a, int type)
.data.size = 4,
.usage = SG_USAGE_STREAM
});
*/
}
void packFloats(float *src, float *dest, int srcLength) {

View File

@@ -3,7 +3,6 @@
#include "HandmadeMath.h"
#include "transform.h"
#include "sokol/sokol_gfx.h"
#include "gameobject.h"
#include "anim.h"
#include "texture.h"
@@ -38,11 +37,11 @@ typedef struct skin {
animation *anim;
} skin;
sg_buffer accessor2buffer(cgltf_accessor *a, int type);
//sg_buffer accessor2buffer(cgltf_accessor *a, int type);
skin *make_gltf_skin(cgltf_skin *skin, cgltf_data *data);
void skin_calculate(skin *sk);
sg_buffer float_buffer(float *f, int v);
/*sg_buffer float_buffer(float *f, int v);
sg_buffer index_buffer(float *f, int verts);
sg_buffer texcoord_floats(float *f, int n);
sg_buffer par_idx_buffer(uint32_t *i, int v);
@@ -51,5 +50,5 @@ sg_buffer ubyten_buffer(float *f, int v);
sg_buffer ubyte_buffer(float *f, int v);
sg_buffer joint_buf(float *f, int v);
sg_buffer weight_buf(float *f, int v);
*/
#endif

View File

@@ -81,31 +81,27 @@ JSValue js_global_get_##ENTRY (JSContext *js, JSValue self) { \
JSValue js_##ID##_get_##ENTRY (JSContext *js, JSValue self) { \
return TYPE##2js(js,js2##ID (js, self)->ENTRY); } \
#define QJSCLASS(TYPE)\
#define QJSCLASS(TYPE, ...)\
static JSClassID js_##TYPE##_id;\
static int js_##TYPE##_count = 0; \
static void js_##TYPE##_finalizer(JSRuntime *rt, JSValue val){\
TYPE *n = JS_GetOpaque(val, js_##TYPE##_id);\
js_##TYPE##_count--; \
TYPE##_free(rt,n);}\
static JSClassDef js_##TYPE##_class = {\
static inline JSClassDef js_##TYPE##_class = {\
#TYPE,\
.finalizer = js_##TYPE##_finalizer,\
};\
TYPE *js2##TYPE (JSContext *js, JSValue val) { \
static inline TYPE *js2##TYPE (JSContext *js, JSValue val) { \
if (JS_IsUndefined(val)) return NULL; \
assert(JS_GetClassID(val) == js_##TYPE##_id); \
if (JS_GetClassID(val) != js_##TYPE##_id) return NULL; \
return JS_GetOpaque(val,js_##TYPE##_id); \
}\
JSValue TYPE##2js(JSContext *js, TYPE *n) { \
static inline JSValue TYPE##2js(JSContext *js, TYPE *n) { \
__VA_ARGS__ \
JSValue j = JS_NewObjectClass(js,js_##TYPE##_id);\
JS_SetOpaque(j,n);\
js_##TYPE##_count++; \
return j; }\
\
static JSValue js_##TYPE##_memid (JSContext *js, JSValue self) { return JS_NewString(js,"p"); } \
static JSValue js_##TYPE##_memsize (JSContext *js, JSValue self) { return number2js(js,sizeof(TYPE)); } \
static JSValue js_##TYPE##__count (JSContext *js, JSValue self) { return number2js(js,js_##TYPE##_count); } \
#define QJSGLOBALCLASS(NAME) \
JSValue NAME = JS_NewObject(js); \
@@ -119,8 +115,6 @@ JS_NewClass(JS_GetRuntime(js), js_##TYPE##_id, &js_##TYPE##_class);\
JSValue TYPE##_proto = JS_NewObject(js); \
JS_SetPropertyFunctionList(js, TYPE##_proto, js_##TYPE##_funcs, countof(js_##TYPE##_funcs)); \
JS_SetPropertyStr(js, TYPE##_proto, "memid", JS_NewCFunction(js, &js_##TYPE##_memid, "memid", 0)); \
JS_SetPropertyStr(js, TYPE##_proto, "memsize", JS_NewCFunction(js, &js_##TYPE##_memsize, "memsize", 0)); \
JS_SetPropertyStr(js, TYPE##_proto, "_count", JS_NewCFunction(js, &js_##TYPE##__count, "_count", 0)); \
JS_SetPropertyStr(js, globalThis, #TYPE "_proto", JS_DupValue(js,TYPE##_proto)); \
JS_SetClassProto(js, js_##TYPE##_id, TYPE##_proto); \

View File

@@ -3,9 +3,7 @@
#include <string.h>
#include <stdlib.h>
#include "render.h"
#include <sokol_app.h>
#include <sokol/sokol_gfx.h>
#include <SDL3/SDL.h>
static JSValue js_tracy_fiber_enter(JSContext *js, JSValue self, int argc, JSValue *argv)
{
@@ -474,7 +472,11 @@ static JSValue js_tracy_gpu_collect(JSContext *js, JSValue self, int argc, JSVal
static JSValue js_tracy_image(JSContext *js, JSValue self, int argc, JSValue *argv)
{
return JS_UNDEFINED;
/* SDL_Surface *img = js2SDL_Surface(js,argv[0]);
SDL_Surface *scaled = SDL_ScaleSurface(img, 320,180,SDL_SCALEMODE_LINEAR);
___tracy_emit_frame_image(scaled->pixels, scaled->w,scaled->h, 0,0);
SDL_DestroySurface(scaled);
return JS_UNDEFINED;*/
}
#endif

View File

@@ -1,73 +1,7 @@
#include "render.h"
#include "sokol/sokol_gfx.h"
#include "sokol/sokol_glue.h"
#include "HandmadeMath.h"
#include <stdio.h>
sg_sampler std_sampler;
sg_sampler tex_sampler;
viewstate globalview = {0};
#ifdef TRACY_ENABLE
#include <tracy/TracyC.h>
#define DUMPSTAT(NAME) TracyCPlotI(#NAME, stats.NAME);
void render_dump_trace()
{
sg_frame_stats stats = sg_query_frame_stats();
DUMPSTAT(num_passes)
DUMPSTAT(num_draw)
DUMPSTAT(num_tris)
DUMPSTAT(num_verts)
DUMPSTAT(num_apply_pipeline)
DUMPSTAT(num_apply_bindings)
DUMPSTAT(num_apply_uniforms)
DUMPSTAT(size_apply_uniforms)
DUMPSTAT(size_update_buffer)
DUMPSTAT(size_append_buffer)
DUMPSTAT(size_update_image)
DUMPSTAT(num_apply_viewport)
DUMPSTAT(num_apply_scissor_rect)
}
#endif
void render_trace_init();
void render_init() {
sg_setup(&(sg_desc){
.environment = sglue_environment(),
// .logger = { .func = sg_logging },
.buffer_pool_size = 1024,
.image_pool_size = 1024,
});
#ifdef TRACY_ENABLE
sg_enable_frame_stats();
TracyCPlotConfig("size_apply_uniforms", TracyPlotFormatMemory, 1, 1, 5474985);
TracyCPlotConfig("size_update_buffer", TracyPlotFormatMemory, 1, 1, 547498235);
TracyCPlotConfig("size_append_buffer", TracyPlotFormatMemory, 1, 1, 12345615);
TracyCPlotConfig("size_update_image", TracyPlotFormatMemory, 1, 1, 49831049);
// render_trace_init();
#endif
std_sampler = sg_make_sampler(&(sg_sampler_desc){});
tex_sampler = sg_make_sampler(&(sg_sampler_desc){
.min_filter = SG_FILTER_LINEAR,
.mag_filter = SG_FILTER_LINEAR,
.mipmap_filter = SG_FILTER_LINEAR,
.wrap_u = SG_WRAP_REPEAT,
.wrap_v = SG_WRAP_REPEAT
});
}
float *rgba2floats(float *r, struct rgba c)
{
r[0] = (float)c.r / RGBA_MAX;

View File

@@ -1,18 +1,8 @@
#ifndef OPENGL_RENDER_H
#define OPENGL_RENDER_H
#if defined __linux__
#define SOKOL_GLCORE
#elif __EMSCRIPTEN__
#define SOKOL_WGPU
#elif __WIN32
#define SOKOL_D3D11
#elif __APPLE__
#define SOKOL_METAL
#endif
#include "sokol/sokol_gfx.h"
#include "HandmadeMath.h"
#include <SDL3/SDL.h>
#define RGBA_MAX 255
@@ -21,9 +11,6 @@ extern struct rgba color_black;
extern struct rgba color_clear;
extern int TOPLEFT;
extern sg_sampler std_sampler;
extern sg_sampler tex_sampler;
typedef struct viewstate {
HMM_Mat4 v;
HMM_Mat4 p;
@@ -56,8 +43,6 @@ struct rgba {
unsigned char a;
};
void render_dump_trace();
typedef struct rgba rgba;
static inline rgba vec2rgba(HMM_Vec4 v) {
@@ -68,7 +53,6 @@ static inline rgba vec2rgba(HMM_Vec4 v) {
struct rect {
float x,y,w,h;
};
typedef struct rect rect;
float *rgba2floats(float *r, struct rgba c);

View File

@@ -1,5 +1,4 @@
#include "glad.h"
#include "sokol/sokol_gfx.h"
#include <tracy/Tracy.hpp>
#include <tracy/TracyOpenGL.hpp>

View File

@@ -1,7 +1,6 @@
#include "script.h"
#include "jsffi.h"
#include "stb_ds.h"
#include <sokol/sokol_time.h>
#include <inttypes.h>
#include <limits.h>
#include <sys/stat.h>
@@ -11,8 +10,6 @@
#include <stdlib.h>
#include <assert.h>
#include <tracy/TracyC.h>
static JSContext *js = NULL;
static JSRuntime *rt = NULL;

View File

@@ -1,7 +1,6 @@
#include "texture.h"
#include "render.h"
#include "sokol/sokol_gfx.h"
#include <math.h>
#include <stb_image.h>
#include <stb_image_write.h>
@@ -21,97 +20,6 @@
struct rect ST_UNIT = {0.f, 0.f, 1.f, 1.f};
static inline void write_pixel(unsigned char *data, int idx, rgba color)
{
memcpy(data+idx, &color, sizeof(color));
}
static inline rgba get_pixel(unsigned char *data, int idx)
{
rgba color;
memcpy(&color, data+idx, sizeof(color));
return color;
}
static inline unsigned char c_clamp(float value) { return (unsigned char) fmaxf(0.0f, fminf(255.0f, roundf(value))); }
static inline rgba blend_colors(rgba a, rgba b)
{
float a_a = a.a / 255.0f;
float b_a = b.a / 255.0f;
float out_a = a_a + b_a * (1.0f - a_a);
rgba result;
if (out_a == 0.0f) {
result.r = result.g = result.b = result.a = 0;
return result;
}
// Use the c_clamp function to safely clamp the values within the range [0, 255]
result.r = c_clamp(((a.r * a_a) + (b.r * b_a * (1.0f - a_a))) / out_a);
result.g = c_clamp(((a.g * a_a) + (b.g * b_a * (1.0f - a_a))) / out_a);
result.b = c_clamp(((a.b * a_a) + (b.b * b_a * (1.0f - a_a))) / out_a);
result.a = c_clamp(out_a * 255.0f);
return result;
}
static inline rgba additive_blend(rgba a, rgba b) {
rgba result;
result.r = c_clamp(a.r + b.r);
result.g = c_clamp(a.g + b.g);
result.b = c_clamp(a.b + b.b);
result.a = c_clamp((a.a + b.a) * 0.5f); // Blend alpha channels evenly
return result;
}
static inline rgba subtractive_blend(rgba a, rgba b) {
rgba result;
result.r = c_clamp(a.r - b.r);
result.g = c_clamp(a.g - b.g);
result.b = c_clamp(a.b - b.b);
result.a = c_clamp((a.a + b.a) * 0.5f); // Blend alpha channels evenly
return result;
}
static inline rgba multiplicative_blend(rgba a, rgba b) {
rgba result;
result.r = c_clamp((a.r * b.r) / 255.0f);
result.g = c_clamp((a.g * b.g) / 255.0f);
result.b = c_clamp((a.b * b.b) / 255.0f);
result.a = c_clamp((a.a + b.a) * 0.5f); // Blend alpha channels evenly
return result;
}
static inline rgba dodge_blend(rgba a, rgba b) {
rgba result;
result.r = c_clamp(a.r == 255 ? 255 : (b.r * 255) / (255 - a.r));
result.g = c_clamp(a.g == 255 ? 255 : (b.g * 255) / (255 - a.g));
result.b = c_clamp(a.b == 255 ? 255 : (b.b * 255) / (255 - a.b));
result.a = c_clamp((a.a + b.a) * 0.5f); // Blend alpha channels evenly
return result;
}
static inline rgba burn_blend(rgba a, rgba b) {
rgba result;
result.r = c_clamp(a.r == 0 ? 0 : 255 - ((255 - b.r) * 255) / a.r);
result.g = c_clamp(a.g == 0 ? 0 : 255 - ((255 - b.g) * 255) / a.g);
result.b = c_clamp(a.b == 0 ? 0 : 255 - ((255 - b.b) * 255) / a.b);
result.a = c_clamp((a.a + b.a) * 0.5f); // Blend alpha channels evenly
return result;
}
unsigned int next_pow2(unsigned int v)
{
v--;
@@ -196,36 +104,6 @@ struct texture *texture_fromdata(void *raw, long size)
return tex;
}
static double fade (double t) { return t*t*t*(t*(t*6-15)+10); }
double grad (int hash, double x, double y, double z)
{
int h = hash&15;
double u = h<8 ? x : y;
double v = h<4 ? y : h==12||h==14 ? x : z;
return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
/* alt */
/* switch(hash & 0xF)
{
case 0x0: return x + y;
case 0x1: return -x + y;
case 0x2: return x - y;
case 0x3: return -x - y;
case 0x4: return x + z;
case 0x5: return -x + z;
case 0x6: return x - z;
case 0x7: return -x - z;
case 0x8: return y + z;
case 0x9: return -y + z;
case 0xA: return y - z;
case 0xB: return -y - z;
case 0xC: return y + x;
case 0xD: return -y + z;
case 0xE: return y - x;
case 0xF: return -y - z;
default: return 0; // never happens
}*/
}
void texture_save(texture *tex, const char *file)
{
if (!tex->data) return;
@@ -248,74 +126,6 @@ void texture_save(texture *tex, const char *file)
});
}
// copy texture src to dest
// sx and sy are the destination coordinates to copy to
// sw the width of the destination to take in pixels
// sh the height of the destination to take in pixels
int texture_blit(texture *dst, texture *src, rect dstrect, rect srcrect, int tile) {
if (!src || !dst || !src->data || !dst->data) return 0;
float scaleX = srcrect.w / dstrect.w;
float scaleY = srcrect.h / dstrect.h;
if (srcrect.x < 0 || srcrect.y < 0 || srcrect.x + srcrect.w > src->width ||
dstrect.x < 0 || dstrect.y < 0 || dstrect.x + dstrect.w > dst->width ||
srcrect.y + srcrect.h > src->height || dstrect.y + dstrect.h > dst->height) {
return false; // Rectangles exceed texture bounds
}
for (int dstY = 0; dstY < dstrect.h; ++dstY) {
for (int dstX = 0; dstX < dstrect.w; ++dstX) {
int srcX;
int srcY;
if (tile) {
srcX = srcrect.x + (dstX % (int)srcrect.w);
srcY = srcrect.y + (dstY % (int)srcrect.h);
} else {
srcX = srcrect.x + (int)(dstX * scaleX);
srcY = srcrect.y + (int)(dstY * scaleY);
}
int srcIndex = (srcY * src->width + srcX) * 4;
int dstIndex = ((dstrect.y + dstY) * dst->width + (dstrect.x + dstX)) * 4;
rgba srccolor = get_pixel(src->data, srcIndex);
rgba dstcolor = get_pixel(dst->data, dstIndex);
rgba color = blend_colors(srccolor, dstcolor);
write_pixel(dst->data, dstIndex, color);
}
}
return 1;
}
int texture_fill_rect(texture *tex, struct rect rect, struct rgba color)
{
if (!tex || !tex->data) return 0;
int x_end = rect.x+rect.w;
int y_end = rect.y+rect.h;
if (rect.x < 0 || rect.y < 0 || x_end > tex->width || y_end > tex->height) return 0;
for (int j = rect.y; j < y_end; ++j)
for (int i = rect.x; i < x_end; ++i) {
int index = (j*tex->width+i)*4;
write_pixel(tex->data, index, color);
}
return 1;
}
void swap_pixels(unsigned char *p1, unsigned char *p2) {
for (int i = 0; i < 4; ++i) {
unsigned char tmp = p1[i];
p1[i] = p2[i];
p2[i] = tmp;
}
}
texture *texture_scale(texture *tex, int width, int height)
{
texture *new = calloc(1, sizeof(*new));
@@ -327,125 +137,7 @@ texture *texture_scale(texture *tex, int width, int height)
return new;
}
int texture_flip(texture *tex, int y)
{
if (!tex || !tex->data) return -1;
int width = tex->width;
int height = tex->height;
if (y) {
for (int row = 0; row < height / 2; ++row) {
for (int col = 0; col < width; ++col) {
unsigned char *top = tex->data+((row*width+col)*4);
unsigned char *bottom = tex->data+(((height-row-1)*width+col)*4);
swap_pixels(top,bottom);
}
}
} else {
for (int row = 0; row < height; ++row) {
for (int col = 0; col < width / 2; ++col) {
unsigned char *left = tex->data+((row*width+col)*4);
unsigned char *right = tex->data+((row*width+(width-col-1))*4);
swap_pixels(left,right);
}
}
}
return 0;
}
int texture_write_pixel(texture *tex, int x, int y, rgba color)
{
if (x < 0 || x >= tex->width || y < 0 || y >= tex->height) return 0;
int i = (y * tex->width + x) * 4;
write_pixel(tex->data, i, color);
return 1;
}
texture *texture_dup(texture *tex)
{
texture *new = calloc(1, sizeof(*new));
*new = *tex;
new->data = malloc(new->width*new->height*4);
memcpy(new->data, tex->data, new->width*new->height*4*sizeof(new->data));
return new;
}
sg_image_data tex_img_data(texture *tex, int mipmaps)
{
if (!mipmaps) {
sg_image_data sg_img_data = {0};
sg_img_data.subimage[0][0] = (sg_range) {.ptr = tex->data, .size = tex->width*tex->height*4};
return sg_img_data;
}
sg_image_data sg_img_data = {0};
int mips = mip_levels(tex->width, tex->height)+1;
int mipw, miph;
mipw = tex->width;
miph = tex->height;
sg_img_data.subimage[0][0] = (sg_range){ .ptr = tex->data, .size = mipw*miph*4 };
unsigned char *mipdata[mips];
mipdata[0] = tex->data;
for (int i = 1; i < mips; i++) {
int w, h, mipw, miph;
mip_wh(tex->width, tex->height, &mipw, &miph, i-1); // mipw miph are previous iteration
mip_wh(tex->width, tex->height, &w, &h, i);
mipdata[i] = malloc(w * h * 4);
stbir_resize_uint8_linear(mipdata[i-1], mipw, miph, 0, mipdata[i], w, h, 0, 4);
sg_img_data.subimage[0][i] = (sg_range){ .ptr = mipdata[i], .size = w*h*4 };
tex->vram += w*h*4;
mipw = w;
miph = h;
}
}
int texture_fill(texture *tex, struct rgba color)
{
if (!tex || !tex->data) return 0; // Ensure valid texture and pixel data
// Loop through every pixel in the texture
for (int y = 0; y < tex->height; ++y) {
for (int x = 0; x < tex->width; ++x) {
int index = (y * tex->width + x) * 4;
write_pixel(tex->data, index, color);
}
}
return 1;
}
void texture_load_gpu(texture *tex)
{
if (!tex->data) return;
if (tex->id.id == 0) {
// Doesn't exist, so make a new one
sg_image_data img_data = tex_img_data(tex, 0);
tex->id = sg_make_image(&(sg_image_desc){
.type = SG_IMAGETYPE_2D,
.width = tex->width,
.height = tex->height,
.usage = SG_USAGE_DYNAMIC,
.num_mipmaps = 1
});
sg_update_image(tex->id, &img_data);
} else {
sg_image_data img_data = tex_img_data(tex,0);
sg_update_image(tex->id, &img_data);
}
}
sapp_icon_desc texture2icon(texture *tex)
/*sapp_icon_desc texture2icon(texture *tex)
{
sapp_icon_desc desc = {0};
if (!tex->data) return desc;
@@ -476,3 +168,4 @@ sapp_icon_desc texture2icon(texture *tex)
return desc;
}
*/

View File

@@ -1,15 +1,11 @@
#ifndef TEXTURE_H
#define TEXTURE_H
#include "sokol/sokol_gfx.h"
#include "HandmadeMath.h"
#include "render.h"
#include <quickjs.h>
#include "stb_rect_pack.h"
#include "sokol_app.h"
#include "sokol/util/sokol_imgui.h"
#include <SDL3/SDL_render.h>
#define TEX_SPEC 0
#define TEX_NORM 1
@@ -24,7 +20,8 @@ extern struct rect ST_UNIT;
/* Represents an actual texture on the GPU */
struct texture {
sg_image id; /* ID reference for the GPU memory location of the texture */
SDL_Texture *id;
SDL_Surface *surface;
int width;
int height;
HMM_Vec3 dimensions;
@@ -50,15 +47,6 @@ texture *texture_scale(texture *tex, int width, int height); // dup and scale th
void texture_free(JSRuntime *rt,texture *tex);
void texture_offload(texture *tex); // Remove the data from this texture
void texture_load_gpu(texture *tex); // Upload this data to the GPU if it isn't already there. Replace it if it is.
int texture_write_pixel(texture *tex, int x, int y, struct rgba color);
int texture_fill(texture *tex, struct rgba color);
int texture_fill_rect(texture *tex, struct rect rect, struct rgba color);
int texture_blit(texture *dst, texture *src, struct rect dstrect, struct rect srcrect, int tile); // copies src into dst, using their respective squares, scaling if necessary
int texture_flip(texture *tex, int y);
sapp_icon_desc texture2icon(texture *tex);
void texture_save(texture *tex, const char *file); // save the texture data to the given file

View File

@@ -1,9 +0,0 @@
root=true
[**]
indent_style=space
indent_size=4
trim_trailing_whitespace=true
insert_final_newline=true
[*.yml]
indent_size=2

View File

@@ -1,331 +0,0 @@
name: Bindings
on: [push, pull_request]
jobs:
test-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: test_win
run: |
cd tests
test_win.cmd
shell: cmd
test-mac:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: seanmiddleditch/gha-setup-ninja@master
- name: test_macos
run: |
cd tests
./test_macos.sh
test-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: seanmiddleditch/gha-setup-ninja@master
- name: prepare
run: |
sudo apt-get update
sudo apt-get install libgl1-mesa-dev libegl1-mesa-dev mesa-common-dev xorg-dev libasound-dev
- name: test_linux
run: |
cd tests
./test_linux.sh
gen-bindings:
needs: [ test-windows, test-mac, test-linux ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
repository: floooh/sokol-zig
path: bindgen/sokol-zig
- uses: actions/checkout@v4
with:
repository: floooh/sokol-nim
path: bindgen/sokol-nim
- uses: actions/checkout@v4
with:
repository: floooh/sokol-odin
path: bindgen/sokol-odin
- uses: actions/checkout@v4
with:
repository: floooh/sokol-rust
path: bindgen/sokol-rust
- name: generate
run: |
cd bindgen
python3 gen_all.py
- name: upload-zig-artifact
uses: actions/upload-artifact@v4
with:
name: ignore-me-zig
retention-days: 1
path: bindgen/sokol-zig/src/sokol
- name: upload-nim-artifact
uses: actions/upload-artifact@v4
with:
name: ignore-me-nim
retention-days: 1
path: bindgen/sokol-nim/src/sokol
- name: upload-odin-artifact
uses: actions/upload-artifact@v4
with:
name: ignore-me-odin
retention-days: 1
path: |
bindgen/sokol-odin/sokol
bindgen/sokol-odin/c
- name: upload-rust-artifact
uses: actions/upload-artifact@v4
with:
name: ignore-me-rust
retention-days: 1
path: bindgen/sokol-rust/src
test-zig:
needs: gen-bindings
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v4
with:
repository: floooh/sokol-zig
- uses: goto-bus-stop/setup-zig@v2
- uses: actions/download-artifact@v4
with:
name: ignore-me-zig
path: src/sokol
- name: prepare
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install libgl1-mesa-dev libegl1-mesa-dev mesa-common-dev xorg-dev libasound-dev
- name: build
run: zig build
test-nim:
needs: gen-bindings
strategy:
fail-fast: false
matrix:
#os: [ubuntu-latest, macos-latest, windows-latest]
os: [ubuntu-latest, macos-latest ]
runs-on: ${{matrix.os}}
steps:
- uses: jiro4989/setup-nim-action@v1
with:
nim-version: '2.x'
repo-token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/checkout@v4
with:
repository: floooh/sokol-nim
- uses: actions/download-artifact@v4
with:
name: ignore-me-nim
path: src/sokol
- if: runner.os == 'Linux'
name: prepare
run: |
sudo apt-get update
sudo apt-get install libgl1-mesa-dev libegl1-mesa-dev mesa-common-dev xorg-dev libasound-dev
- name: build
run: |
nimble install -Y
nimble install glm -Y
nimble build_all
test-odin:
needs: gen-bindings
strategy:
fail-fast: false
matrix:
# FIXME: macOS Odin vs Homebrew LLVM currently seems broken
# os: [ubuntu-latest, macos-latest, windows-latest]
os: [ubuntu-latest, windows-latest]
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v4
with:
repository: floooh/sokol-odin
- uses: actions/download-artifact@v4
with:
name: ignore-me-odin
# NOTE: see https://github.com/floooh/sokol-odin/blob/main/.github/workflows/main.yml
- uses: ilammy/msvc-dev-cmd@v1
- if: runner.os == 'Linux'
name: prepare-linux
run: |
sudo apt-get update
sudo apt-get install libglu1-mesa-dev mesa-common-dev xorg-dev libasound-dev llvm-14
curl -L https://github.com/odin-lang/Odin/releases/download/dev-2023-08/odin-ubuntu-amd64-dev-2023-08.zip --output odin.zip
unzip odin.zip
chmod a+x ./odin
./build_clibs_linux.sh
- if: runner.os == 'macOS'
name: prepare-macos
run: |
brew install llvm@14
curl -L https://github.com/odin-lang/Odin/releases/download/dev-2023-08/odin-macos-amd64-dev-2023-08.zip --output odin.zip
unzip odin.zip
chmod a+x ./odin
./build_clibs_macos.sh
- if: runner.os == 'Windows'
name: prepare-windows
shell: cmd
run: |
curl -L https://github.com/odin-lang/Odin/releases/download/dev-2023-08/odin-windows-amd64-dev-2023-08.zip --output odin.zip
unzip odin.zip
build_clibs_windows.cmd
- name: build
run: |
./odin build examples/clear -debug
./odin build examples/triangle -debug
./odin build examples/quad -debug
./odin build examples/bufferoffsets -debug
./odin build examples/cube -debug
./odin build examples/noninterleaved -debug
./odin build examples/texcube -debug
./odin build examples/shapes -debug
./odin build examples/offscreen -debug
./odin build examples/instancing -debug
./odin build examples/mrt -debug
./odin build examples/blend -debug
./odin build examples/debugtext -debug
./odin build examples/debugtext-print -debug
./odin build examples/debugtext-userfont -debug
./odin build examples/saudio -debug
./odin build examples/sgl -debug
./odin build examples/sgl-points -debug
./odin build examples/sgl-context -debug
test-rust:
needs: gen-bindings
env:
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
strategy:
fail-fast: false
matrix:
# os: [ubuntu-latest, macos-latest, windows-latest]
os: [ubuntu-latest, windows-latest]
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v4
with:
repository: floooh/sokol-rust
- uses: actions/download-artifact@v4
with:
name: ignore-me-rust
path: src
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
- name: prepare-linux
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install libglu1-mesa-dev mesa-common-dev xorg-dev libasound-dev
- name: build
run: |
cargo --version
cargo build --examples --verbose
# only deploy the bindings for commits on the main branch
deploy-zig:
needs: test-zig
if: github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
repository: floooh/sokol-zig
ssh-key: ${{ secrets.GHACTIONS_ZIG_PUSH }}
- uses: actions/download-artifact@v4
with:
name: ignore-me-zig
path: src/sokol
- name: "commit and push"
run: |
git config user.email "none"
git config user.name "GH Action"
git add -A
git diff-index --quiet HEAD || git commit -m "updated (https://github.com/floooh/sokol/commit/${{ github.sha }})"
git push
deploy-nim:
needs: test-nim
if: github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
repository: floooh/sokol-nim
ssh-key: ${{ secrets.GHACTIONS_NIM_PUSH }}
- uses: actions/download-artifact@v4
with:
name: ignore-me-nim
path: src/sokol
- name: "commit and push"
run: |
git config user.email "none"
git config user.name "GH Action"
git add -A
git diff-index --quiet HEAD || git commit -m "updated (https://github.com/floooh/sokol/commit/${{ github.sha }})"
git push
deploy-odin:
needs: test-odin
if: github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
repository: floooh/sokol-odin
ssh-key: ${{ secrets.GHACTIONS_ODIN_PUSH }}
- uses: actions/download-artifact@v4
with:
name: ignore-me-odin
- name: "commit and push"
run: |
git config user.email "none"
git config user.name "GH Action"
git add -A
git diff-index --quiet HEAD || git commit -m "updated (https://github.com/floooh/sokol/commit/${{ github.sha }})"
git push
deploy-rust:
needs: test-rust
env:
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
if: github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
repository: floooh/sokol-rust
ssh-key: ${{ secrets.GHACTIONS_RUST_PUSH }}
- uses: actions/download-artifact@v4
with:
name: ignore-me-rust
path: src
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
- name: "cargo fmt"
run: cargo fmt
- name: "commit and push"
run: |
git config user.email "none"
git config user.name "GH Action"
git status -vv
git add -A
git diff-index --quiet HEAD || git commit -m "updated (https://github.com/floooh/sokol/commit/${{ github.sha }})"
git push

View File

@@ -1,65 +0,0 @@
name: "Build & Test"
on: [push, pull_request]
jobs:
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: test_win
run: |
cd tests
test_win.cmd
shell: cmd
mac:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: seanmiddleditch/gha-setup-ninja@master
- name: test_macos
run: |
cd tests
./test_macos.sh
ios:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: test_ios
run: |
cd tests
./test_ios.sh
linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: seanmiddleditch/gha-setup-ninja@master
- name: prepare
run: |
sudo apt-get update
sudo apt-get install libgl1-mesa-dev libegl1-mesa-dev mesa-common-dev xorg-dev libasound-dev
- name: test_linux
run: |
cd tests
./test_linux.sh
emscripten:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: seanmiddleditch/gha-setup-ninja@master
- name: test_emscripten
run: |
cd tests
./test_emscripten.sh
android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: seanmiddleditch/gha-setup-ninja@master
- uses: actions/setup-java@v1
with:
java-version: '8'
- name: test_android
run: |
cd tests
./test_android.sh

View File

@@ -1,7 +0,0 @@
.vscode/
build/
#>fips
# this area is managed by fips, do not edit
.fips-*
*.pyc
#<fips

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +0,0 @@
zlib/libpng license
Copyright (c) 2018 Andre Weissflog
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the
use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.

View File

@@ -1,406 +0,0 @@
# Sokol
Simple
[STB-style](https://github.com/nothings/stb/blob/master/docs/stb_howto.txt)
cross-platform libraries for C and C++, written in C.
[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**29-Feb-2024**: **BREAKING CHANGES** 'unified render pass'
cleanup in sokol_gfx.h)
[![Build](/../../actions/workflows/main.yml/badge.svg)](/../../actions/workflows/main.yml) [![Bindings](/../../actions/workflows/gen_bindings.yml/badge.svg)](/../../actions/workflows/gen_bindings.yml) [![build](https://github.com/floooh/sokol-zig/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-zig/actions/workflows/main.yml) [![build](https://github.com/floooh/sokol-nim/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-nim/actions/workflows/main.yml) [![Odin](https://github.com/floooh/sokol-odin/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-odin/actions/workflows/main.yml)[![Rust](https://github.com/floooh/sokol-rust/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-rust/actions/workflows/main.yml)
## Examples and Related Projects
- [Live Samples](https://floooh.github.io/sokol-html5/index.html) via WASM ([source](https://github.com/floooh/sokol-samples))
- [Doom Shareware](https://floooh.github.io/doom-sokol/) ported to the Sokol headers ([source](https://github.com/floooh/doom-sokol))
- [sokol_gp.h](https://github.com/edubart/sokol_gp) a 2D shape drawing library on top of sokol_gfx.h
- [LearnOpenGL examples ported to sokol-gfx](https://zeromake.github.io/learnopengl-examples/) ([git repo](https://github.com/zeromake/learnopengl-examples))
- [Dear ImGui starterkit](https://github.com/floooh/cimgui-sokol-starterkit) a self-contained starterkit for writing Dear ImGui apps in C.
- [qoiview](https://github.com/floooh/qoiview) a basic viewer for the new QOI image file format
- [Tiny 8-bit emulators](https://floooh.github.io/tiny8bit/)
- A 'single-file' [Pacman clone in C99](https://github.com/floooh/pacman.c/), also available in [Zig](https://github.com/floooh/pacman.zig/)
- [MEG-4](https://bztsrc.gitlab.io/meg4) a virtual fantasy console emulator in C89, ported to sokol
- A [Minigolf game](https://mgerdes.github.io/minigolf.html) ([source](https://github.com/mgerdes/minigolf)).
- ['Dealer's Dungeon'](https://dealers-dungeon.com/demo/) ([lower graphics quality](https://dealers-dungeon.com/demo/?q=3),
[source](https://github.com/bqqbarbhg/spear))
- [Command line tools](https://github.com/floooh/sokol-tools) (shader compiler)
- [How to build without a build system](https://github.com/floooh/sokol-samples#how-to-build-without-a-build-system):
useful details for integrating the Sokol headers into your own project with your favourite C/C++ build system
## Core libraries
- [**sokol\_gfx.h**](https://github.com/floooh/sokol/blob/master/sokol_gfx.h): 3D-API wrapper (GL/GLES3/WebGL2 + Metal + D3D11 + WebGPU)
- [**sokol\_app.h**](https://github.com/floooh/sokol/blob/master/sokol_app.h): app framework wrapper (entry + window + 3D-context + input)
- [**sokol\_time.h**](https://github.com/floooh/sokol/blob/master/sokol_time.h): time measurement
- [**sokol\_audio.h**](https://github.com/floooh/sokol/blob/master/sokol_audio.h): minimal buffer-streaming audio playback
- [**sokol\_fetch.h**](https://github.com/floooh/sokol/blob/master/sokol_fetch.h): asynchronous data streaming from HTTP and local filesystem
- [**sokol\_args.h**](https://github.com/floooh/sokol/blob/master/sokol_args.h): unified cmdline/URL arg parser for web and native apps
- [**sokol\_log.h**](https://github.com/floooh/sokol/blob/master/sokol_log.h): provides a standard logging callback for the other sokol headers
## Utility libraries
- [**sokol\_imgui.h**](https://github.com/floooh/sokol/blob/master/util/sokol_imgui.h): sokol_gfx.h rendering backend for [Dear ImGui](https://github.com/ocornut/imgui)
- [**sokol\_nuklear.h**](https://github.com/floooh/sokol/blob/master/util/sokol_nuklear.h): sokol_gfx.h rendering backend for [Nuklear](https://github.com/Immediate-Mode-UI/Nuklear)
- [**sokol\_gl.h**](https://github.com/floooh/sokol/blob/master/util/sokol_gl.h): OpenGL 1.x style immediate-mode rendering API on top of sokol_gfx.h
- [**sokol\_fontstash.h**](https://github.com/floooh/sokol/blob/master/util/sokol_fontstash.h): sokol_gl.h rendering backend for [fontstash](https://github.com/memononen/fontstash)
- [**sokol\_gfx\_imgui.h**](https://github.com/floooh/sokol/blob/master/util/sokol_gfx_imgui.h): debug-inspection UI for sokol_gfx.h (implemented with Dear ImGui)
- [**sokol\_debugtext.h**](https://github.com/floooh/sokol/blob/master/util/sokol_debugtext.h): a simple text renderer using vintage home computer fonts
- [**sokol\_memtrack.h**](https://github.com/floooh/sokol/blob/master/util/sokol_memtrack.h): easily track memory allocations in sokol headers
- [**sokol\_shape.h**](https://github.com/floooh/sokol/blob/master/util/sokol_shape.h): generate simple shapes and plug them into sokol-gfx resource creation structs
- [**sokol\_color.h**](https://github.com/floooh/sokol/blob/master/util/sokol_color.h): X11 style color constants and functions for creating sg_color objects
- [**sokol\_spine.h**](https://github.com/floooh/sokol/blob/master/util/sokol_spine.h): a sokol-style wrapper around the Spine C runtime (http://en.esotericsoftware.com/spine-in-depth)
## 'Official' Language Bindings
These are automatically updated on changes to the C headers:
- [sokol-zig](https://github.com/floooh/sokol-zig)
- [sokol-odin](https://github.com/floooh/sokol-odin)
- [sokol-nim](https://github.com/floooh/sokol-nim)
- [sokol-rust](https://github.com/floooh/sokol-rust)
## Notes
WebAssembly is a 'first-class citizen', one important motivation for the
Sokol headers is to provide a collection of cross-platform APIs with a
minimal footprint on the web platform while still being useful.
The core headers are standalone and can be used independently from each other.
### Why C:
- easier integration with other languages
- easier integration into other projects
- adds only minimal size overhead to executables
A blog post with more background info: [A Tour of sokol_gfx.h](http://floooh.github.io/2017/07/29/sokol-gfx-tour.html)
# sokol_gfx.h:
- simple, modern wrapper around GLES3/WebGL2, GL3.3, D3D11, Metal, and WebGPU
- buffers, images, shaders, pipeline-state-objects and render-passes
- does *not* handle window creation or 3D API context initialization
- does *not* provide shader dialect cross-translation (**BUT** there's now an 'official' shader-cross-compiler solution which
seamlessly integrates with sokol_gfx.h and IDEs: [see here for details](https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md)
# sokol_app.h
A minimal cross-platform application-wrapper library:
- unified application entry
- single window or canvas for 3D rendering
- 3D context initialization
- event-based keyboard, mouse and touch input
- supported platforms: Win32, MacOS, Linux (X11), iOS, WASM, Android, UWP
- supported 3D-APIs: GL3.3 (GLX/WGL), Metal, D3D11, GLES3/WebGL2
The vanilla Hello-Triangle using sokol_gfx.h, sokol_app.h and the
sokol-shdc shader compiler (shader code not shown):
```c
#include "sokol_app.h"
#include "sokol_gfx.h"
#include "sokol_log.h"
#include "sokol_glue.h"
#include "triangle-sapp.glsl.h"
static struct {
sg_pipeline pip;
sg_bindings bind;
sg_pass_action pass_action;
} state;
static void init(void) {
sg_setup(&(sg_desc){
.environment = sglue_environment(),
.logger.func = slog_func,
});
float vertices[] = {
0.0f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f
};
state.bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
.data = SG_RANGE(vertices),
});
state.pip = sg_make_pipeline(&(sg_pipeline_desc){
.shader = sg_make_shader(triangle_shader_desc(sg_query_backend())),
.layout = {
.attrs = {
[ATTR_vs_position].format = SG_VERTEXFORMAT_FLOAT3,
[ATTR_vs_color0].format = SG_VERTEXFORMAT_FLOAT4
}
},
});
state.pass_action = (sg_pass_action) {
.colors[0] = { .load_action=SG_LOADACTION_CLEAR, .clear_value={0.0f, 0.0f, 0.0f, 1.0f } }
};
}
void frame(void) {
sg_begin_pass(&(sg_pass){ .action = state.pass_action, .swapchain = sglue_swapchain() });
sg_apply_pipeline(state.pip);
sg_apply_bindings(&state.bind);
sg_draw(0, 3, 1);
sg_end_pass();
sg_commit();
}
void cleanup(void) {
sg_shutdown();
}
sapp_desc sokol_main(int argc, char* argv[]) {
(void)argc; (void)argv;
return (sapp_desc){
.init_cb = init,
.frame_cb = frame,
.cleanup_cb = cleanup,
.width = 640,
.height = 480,
.window_title = "Triangle",
.icon.sokol_default = true,
.logger.func = slog_func,
};
}
```
# sokol_audio.h
A minimal audio-streaming API:
- you provide a mono- or stereo-stream of 32-bit float samples which sokol_audio.h forwards into platform-specific backends
- two ways to provide the data:
1. directly fill backend audio buffer from your callback function running in the audio thread
2. alternatively push small packets of audio data from your main loop,
or a separate thread created by you
- platform backends:
- Windows: WASAPI
- macOS/iOS: CoreAudio
- Linux: ALSA
- emscripten: WebAudio + ScriptProcessorNode (doesn't use the emscripten-provided OpenAL or SDL Audio wrappers)
A simple mono square-wave generator using the callback model:
```c
// the sample callback, running in audio thread
static void stream_cb(float* buffer, int num_frames, int num_channels) {
assert(1 == num_channels);
static uint32_t count = 0;
for (int i = 0; i < num_frames; i++) {
buffer[i] = (count++ & (1<<3)) ? 0.5f : -0.5f;
}
}
int main() {
// init sokol-audio with default params
saudio_setup(&(saudio_desc){
.stream_cb = stream_cb,
.logger.func = slog_func,
});
// run main loop
...
// shutdown sokol-audio
saudio_shutdown();
return 0;
```
The same code using the push-model
```c
#define BUF_SIZE (32)
int main() {
// init sokol-audio with default params, no callback
saudio_setup(&(saudio_desc){
.logger.func = slog_func,
});
assert(saudio_channels() == 1);
// a small intermediate buffer so we don't need to push
// individual samples, which would be quite inefficient
float buf[BUF_SIZE];
int buf_pos = 0;
uint32_t count = 0;
// push samples from main loop
bool done = false;
while (!done) {
// generate and push audio samples...
int num_frames = saudio_expect();
for (int i = 0; i < num_frames; i++) {
// simple square wave generator
buf[buf_pos++] = (count++ & (1<<3)) ? 0.5f : -0.5f;
if (buf_pos == BUF_SIZE) {
buf_pos = 0;
saudio_push(buf, BUF_SIZE);
}
}
// handle other per-frame stuff...
...
}
// shutdown sokol-audio
saudio_shutdown();
return 0;
}
```
# sokol_fetch.h
Load entire files, or stream data asynchronously over HTTP (emscripten/wasm)
or the local filesystem (all native platforms).
Simple C99 example loading a file into a static buffer:
```c
#include "sokol_fetch.h"
#include "sokol_log.h"
static void response_callback(const sfetch_response*);
#define MAX_FILE_SIZE (1024*1024)
static uint8_t buffer[MAX_FILE_SIZE];
// application init
static void init(void) {
...
// setup sokol-fetch with default config:
sfetch_setup(&(sfetch_desc_t){ .logger.func = slog_func });
// start loading a file into a statically allocated buffer:
sfetch_send(&(sfetch_request_t){
.path = "hello_world.txt",
.callback = response_callback
.buffer_ptr = buffer,
.buffer_size = sizeof(buffer)
});
}
// per frame...
static void frame(void) {
...
// need to call sfetch_dowork() once per frame to 'turn the gears':
sfetch_dowork();
...
}
// the response callback is where the interesting stuff happens:
static void response_callback(const sfetch_response_t* response) {
if (response->fetched) {
// data has been loaded into the provided buffer, do something
// with the data...
const void* data = response->buffer_ptr;
uint64_t data_size = response->fetched_size;
}
// the finished flag is set both on success and failure
if (response->failed) {
// oops, something went wrong
switch (response->error_code) {
SFETCH_ERROR_FILE_NOT_FOUND: ...
SFETCH_ERROR_BUFFER_TOO_SMALL: ...
...
}
}
}
// application shutdown
static void shutdown(void) {
...
sfetch_shutdown();
...
}
```
# sokol_time.h:
Simple cross-platform time measurement:
```c
#include "sokol_time.h"
...
/* initialize sokol_time */
stm_setup();
/* take start timestamp */
uint64_t start = stm_now();
...some code to measure...
/* compute elapsed time */
uint64_t elapsed = stm_since(start);
/* convert to time units */
double seconds = stm_sec(elapsed);
double milliseconds = stm_ms(elapsed);
double microseconds = stm_us(elapsed);
double nanoseconds = stm_ns(elapsed);
/* difference between 2 time stamps */
uint64_t start = stm_now();
...
uint64_t end = stm_now();
uint64_t elapsed = stm_diff(end, start);
/* compute a 'lap time' (e.g. for fps) */
uint64_t last_time = 0;
while (!done) {
...render something...
double frame_time_ms = stm_ms(stm_laptime(&last_time));
}
```
# sokol_args.h
Unified argument parsing for web and native apps. Uses argc/argv on native
platforms and the URL query string on the web.
Example URL with one arg:
https://floooh.github.io/tiny8bit/kc85.html?type=kc85_4
The same as command line app:
> kc85 type=kc85_4
Parsed like this:
```c
#include "sokol_args.h"
int main(int argc, char* argv[]) {
sargs_setup(&(sargs_desc){ .argc=argc, .argv=argv });
if (sargs_exists("type")) {
if (sargs_equals("type", "kc85_4")) {
// start as KC85/4
}
else if (sargs_equals("type", "kc85_3")) {
// start as KC85/3
}
else {
// start as KC85/2
}
}
sargs_shutdown();
return 0;
}
```
See the sokol_args.h header for a more complete documentation, and the [Tiny
Emulators](https://floooh.github.io/tiny8bit/) for more interesting usage examples.

View File

@@ -1,2 +0,0 @@
exports:
header-dirs: [ ".", "util" ]

File diff suppressed because it is too large Load Diff

View File

@@ -1,867 +0,0 @@
#if defined(SOKOL_IMPL) && !defined(SOKOL_ARGS_IMPL)
#define SOKOL_ARGS_IMPL
#endif
#ifndef SOKOL_ARGS_INCLUDED
/*
sokol_args.h -- cross-platform key/value arg-parsing for web and native
Project URL: https://github.com/floooh/sokol
Do this:
#define SOKOL_IMPL or
#define SOKOL_ARGS_IMPL
before you include this file in *one* C or C++ file to create the
implementation.
Optionally provide the following defines with your own implementations:
SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
SOKOL_ARGS_API_DECL - public function declaration prefix (default: extern)
SOKOL_API_DECL - same as SOKOL_ARGS_API_DECL
SOKOL_API_IMPL - public function implementation prefix (default: -)
If sokol_args.h is compiled as a DLL, define the following before
including the declaration or implementation:
SOKOL_DLL
On Windows, SOKOL_DLL will define SOKOL_ARGS_API_DECL as __declspec(dllexport)
or __declspec(dllimport) as needed.
OVERVIEW
========
sokol_args.h provides a simple unified argument parsing API for WebAssembly and
native apps.
When running as a WebAssembly app, arguments are taken from the page URL:
https://floooh.github.io/tiny8bit/kc85.html?type=kc85_3&mod=m022&snapshot=kc85/jungle.kcc
The same arguments provided to a command line app:
kc85 type=kc85_3 mod=m022 snapshot=kc85/jungle.kcc
You can also use standalone keys without value:
https://floooh.github.io/tiny8bit/kc85.html?bla&blub
On the command line:
kc85 bla blub
Such value-less keys are reported as the value being an empty string, but they
can be tested with `sapp_exists("bla")` or `sapp_boolean("blub")`.
ARGUMENT FORMATTING
===================
On the web platform, arguments must be formatted as a valid URL query string
with 'percent encoding' used for special characters.
Strings are expected to be UTF-8 encoded (although sokol_args.h doesn't
contain any special UTF-8 handling). See below on how to obtain
UTF-8 encoded argc/argv values on Windows when using WinMain() as
entry point.
On native platforms the following rules must be followed:
Arguments have the general form
key=value
or
key
When a key has no value, the value will be assigned an empty string.
Key/value pairs are separated by 'whitespace', valid whitespace
characters are space and tab.
Whitespace characters in front and after the separating '=' character
are ignored:
key = value
...is the same as
key=value
The 'key' string must be a simple string without escape sequences or whitespace.
The 'value' string can be quoted, and quoted value strings can contain
whitespace:
key = 'single-quoted value'
key = "double-quoted value"
Single-quoted value strings can contain double quotes, and vice-versa:
key = 'single-quoted value "can contain double-quotes"'
key = "double-quoted value 'can contain single-quotes'"
Note that correct quoting can be tricky on some shells, since command
shells may remove quotes, unless they're escaped.
Value strings can contain a small selection of escape sequences:
\n - newline
\r - carriage return
\t - tab
\\ - escaped backslash
(more escape codes may be added in the future).
CODE EXAMPLE
============
int main(int argc, char* argv[]) {
// initialize sokol_args with default parameters
sargs_setup(&(sargs_desc){
.argc = argc,
.argv = argv
});
// check if a key exists...
if (sargs_exists("bla")) {
...
}
// get value string for key, if not found, return empty string ""
const char* val0 = sargs_value("bla");
// get value string for key, or default string if key not found
const char* val1 = sargs_value_def("bla", "default_value");
// check if a key matches expected value
if (sargs_equals("type", "kc85_4")) {
...
}
// check if a key's value is "true", "yes" or "on" or if this is a standalone key
if (sargs_boolean("joystick_enabled")) {
...
}
// iterate over keys and values
for (int i = 0; i < sargs_num_args(); i++) {
printf("key: %s, value: %s\n", sargs_key_at(i), sargs_value_at(i));
}
// lookup argument index by key string, will return -1 if key
// is not found, sargs_key_at() and sargs_value_at() will return
// an empty string for invalid indices
int index = sargs_find("bla");
printf("key: %s, value: %s\n", sargs_key_at(index), sargs_value_at(index));
// shutdown sokol-args
sargs_shutdown();
}
WINMAIN AND ARGC / ARGV
=======================
On Windows with WinMain() based apps, getting UTF8-encoded command line
arguments is a bit more complicated:
First call GetCommandLineW(), this returns the entire command line
as UTF-16 string. Then call CommandLineToArgvW(), this parses the
command line string into the usual argc/argv format (but in UTF-16).
Finally convert the UTF-16 strings in argv[] into UTF-8 via
WideCharToMultiByte().
See the function _sapp_win32_command_line_to_utf8_argv() in sokol_app.h
for example code how to do this (if you're using sokol_app.h, it will
already convert the command line arguments to UTF-8 for you of course,
so you can plug them directly into sokol_app.h).
API DOCUMENTATION
=================
void sargs_setup(const sargs_desc* desc)
Initialize sokol_args, desc contains the following configuration
parameters:
int argc - the main function's argc parameter
char** argv - the main function's argv parameter
int max_args - max number of key/value pairs, default is 16
int buf_size - size of the internal string buffer, default is 16384
Note that on the web, argc and argv will be ignored and the arguments
will be taken from the page URL instead.
sargs_setup() will allocate 2 memory chunks: one for keeping track
of the key/value args of size 'max_args*8', and a string buffer
of size 'buf_size'.
void sargs_shutdown(void)
Shutdown sokol-args and free any allocated memory.
bool sargs_isvalid(void)
Return true between sargs_setup() and sargs_shutdown()
bool sargs_exists(const char* key)
Test if an argument exists by its key name.
const char* sargs_value(const char* key)
Return value associated with key. Returns an empty string ("") if the
key doesn't exist, or if the key doesn't have a value.
const char* sargs_value_def(const char* key, const char* default)
Return value associated with key, or the provided default value if the
key doesn't exist, or this is a value-less key.
bool sargs_equals(const char* key, const char* val);
Return true if the value associated with key matches
the 'val' argument.
bool sargs_boolean(const char* key)
Return true if the value string of 'key' is one of 'true', 'yes', 'on',
or this is a key without value.
int sargs_find(const char* key)
Find argument by key name and return its index, or -1 if not found.
int sargs_num_args(void)
Return number of key/value pairs.
const char* sargs_key_at(int index)
Return the key name of argument at index. Returns empty string if
is index is outside range.
const char* sargs_value_at(int index)
Return the value of argument at index. Returns empty string
if the key at index has no value, or the index is out-of-range.
MEMORY ALLOCATION OVERRIDE
==========================
You can override the memory allocation functions at initialization time
like this:
void* my_alloc(size_t size, void* user_data) {
return malloc(size);
}
void my_free(void* ptr, void* user_data) {
free(ptr);
}
...
sargs_setup(&(sargs_desc){
// ...
.allocator = {
.alloc_fn = my_alloc,
.free_fn = my_free,
.user_data = ...,
}
});
...
If no overrides are provided, malloc and free will be used.
This only affects memory allocation calls done by sokol_args.h
itself though, not any allocations in OS libraries.
TODO
====
- parsing errors?
LICENSE
=======
zlib/libpng license
Copyright (c) 2018 Andre Weissflog
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the
use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#define SOKOL_ARGS_INCLUDED (1)
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h> // size_t
#if defined(SOKOL_API_DECL) && !defined(SOKOL_ARGS_API_DECL)
#define SOKOL_ARGS_API_DECL SOKOL_API_DECL
#endif
#ifndef SOKOL_ARGS_API_DECL
#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_ARGS_IMPL)
#define SOKOL_ARGS_API_DECL __declspec(dllexport)
#elif defined(_WIN32) && defined(SOKOL_DLL)
#define SOKOL_ARGS_API_DECL __declspec(dllimport)
#else
#define SOKOL_ARGS_API_DECL extern
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
/*
sargs_allocator
Used in sargs_desc to provide custom memory-alloc and -free functions
to sokol_args.h. If memory management should be overridden, both the
alloc_fn and free_fn function must be provided (e.g. it's not valid to
override one function but not the other).
*/
typedef struct sargs_allocator {
void* (*alloc_fn)(size_t size, void* user_data);
void (*free_fn)(void* ptr, void* user_data);
void* user_data;
} sargs_allocator;
typedef struct sargs_desc {
int argc;
char** argv;
int max_args;
int buf_size;
sargs_allocator allocator;
} sargs_desc;
/* setup sokol-args */
SOKOL_ARGS_API_DECL void sargs_setup(const sargs_desc* desc);
/* shutdown sokol-args */
SOKOL_ARGS_API_DECL void sargs_shutdown(void);
/* true between sargs_setup() and sargs_shutdown() */
SOKOL_ARGS_API_DECL bool sargs_isvalid(void);
/* test if an argument exists by key name */
SOKOL_ARGS_API_DECL bool sargs_exists(const char* key);
/* get value by key name, return empty string if key doesn't exist or an existing key has no value */
SOKOL_ARGS_API_DECL const char* sargs_value(const char* key);
/* get value by key name, return provided default if key doesn't exist or has no value */
SOKOL_ARGS_API_DECL const char* sargs_value_def(const char* key, const char* def);
/* return true if val arg matches the value associated with key */
SOKOL_ARGS_API_DECL bool sargs_equals(const char* key, const char* val);
/* return true if key's value is "true", "yes", "on" or an existing key has no value */
SOKOL_ARGS_API_DECL bool sargs_boolean(const char* key);
/* get index of arg by key name, return -1 if not exists */
SOKOL_ARGS_API_DECL int sargs_find(const char* key);
/* get number of parsed arguments */
SOKOL_ARGS_API_DECL int sargs_num_args(void);
/* get key name of argument at index, or empty string */
SOKOL_ARGS_API_DECL const char* sargs_key_at(int index);
/* get value string of argument at index, or empty string */
SOKOL_ARGS_API_DECL const char* sargs_value_at(int index);
#ifdef __cplusplus
} /* extern "C" */
/* reference-based equivalents for c++ */
inline void sargs_setup(const sargs_desc& desc) { return sargs_setup(&desc); }
#endif
#endif // SOKOL_ARGS_INCLUDED
/*--- IMPLEMENTATION ---------------------------------------------------------*/
#ifdef SOKOL_ARGS_IMPL
#define SOKOL_ARGS_IMPL_INCLUDED (1)
#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE)
#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sargs_desc.allocator to override memory allocation functions"
#endif
#include <string.h> // memset, strcmp
#include <stdlib.h> // malloc, free
#if defined(__EMSCRIPTEN__)
#include <emscripten/emscripten.h>
#endif
#ifndef SOKOL_API_IMPL
#define SOKOL_API_IMPL
#endif
#ifndef SOKOL_DEBUG
#ifndef NDEBUG
#define SOKOL_DEBUG
#endif
#endif
#ifndef SOKOL_ASSERT
#include <assert.h>
#define SOKOL_ASSERT(c) assert(c)
#endif
#ifndef _SOKOL_PRIVATE
#if defined(__GNUC__) || defined(__clang__)
#define _SOKOL_PRIVATE __attribute__((unused)) static
#else
#define _SOKOL_PRIVATE static
#endif
#endif
#define _sargs_def(val, def) (((val) == 0) ? (def) : (val))
#define _SARGS_MAX_ARGS_DEF (16)
#define _SARGS_BUF_SIZE_DEF (16*1024)
/* parser state */
#define _SARGS_EXPECT_KEY (1<<0)
#define _SARGS_EXPECT_SEP (1<<1)
#define _SARGS_EXPECT_VAL (1<<2)
#define _SARGS_PARSING_KEY (1<<3)
#define _SARGS_PARSING_VAL (1<<4)
#define _SARGS_ERROR (1<<5)
/* a key/value pair struct */
typedef struct {
int key; /* index to start of key string in buf */
int val; /* index to start of value string in buf */
} _sargs_kvp_t;
/* sokol-args state */
typedef struct {
int max_args; /* number of key/value pairs in args array */
int num_args; /* number of valid items in args array */
_sargs_kvp_t* args; /* key/value pair array */
int buf_size; /* size of buffer in bytes */
int buf_pos; /* current buffer position */
char* buf; /* character buffer, first char is reserved and zero for 'empty string' */
bool valid;
uint32_t parse_state;
char quote; /* current quote char, 0 if not in a quote */
bool in_escape; /* currently in an escape sequence */
sargs_allocator allocator;
} _sargs_state_t;
static _sargs_state_t _sargs;
/*== PRIVATE IMPLEMENTATION FUNCTIONS ========================================*/
_SOKOL_PRIVATE void _sargs_clear(void* ptr, size_t size) {
SOKOL_ASSERT(ptr && (size > 0));
memset(ptr, 0, size);
}
_SOKOL_PRIVATE void* _sargs_malloc(size_t size) {
SOKOL_ASSERT(size > 0);
void* ptr;
if (_sargs.allocator.alloc_fn) {
ptr = _sargs.allocator.alloc_fn(size, _sargs.allocator.user_data);
} else {
ptr = malloc(size);
}
SOKOL_ASSERT(ptr);
return ptr;
}
_SOKOL_PRIVATE void* _sargs_malloc_clear(size_t size) {
void* ptr = _sargs_malloc(size);
_sargs_clear(ptr, size);
return ptr;
}
_SOKOL_PRIVATE void _sargs_free(void* ptr) {
if (_sargs.allocator.free_fn) {
_sargs.allocator.free_fn(ptr, _sargs.allocator.user_data);
} else {
free(ptr);
}
}
_SOKOL_PRIVATE void _sargs_putc(char c) {
if ((_sargs.buf_pos+2) < _sargs.buf_size) {
_sargs.buf[_sargs.buf_pos++] = c;
}
}
_SOKOL_PRIVATE const char* _sargs_str(int index) {
SOKOL_ASSERT((index >= 0) && (index < _sargs.buf_size));
return &_sargs.buf[index];
}
/*-- argument parser functions ------------------*/
_SOKOL_PRIVATE void _sargs_expect_key(void) {
_sargs.parse_state = _SARGS_EXPECT_KEY;
}
_SOKOL_PRIVATE bool _sargs_key_expected(void) {
return 0 != (_sargs.parse_state & _SARGS_EXPECT_KEY);
}
_SOKOL_PRIVATE void _sargs_expect_val(void) {
_sargs.parse_state = _SARGS_EXPECT_VAL;
}
_SOKOL_PRIVATE bool _sargs_val_expected(void) {
return 0 != (_sargs.parse_state & _SARGS_EXPECT_VAL);
}
_SOKOL_PRIVATE void _sargs_expect_sep_or_key(void) {
_sargs.parse_state = _SARGS_EXPECT_SEP | _SARGS_EXPECT_KEY;
}
_SOKOL_PRIVATE bool _sargs_any_expected(void) {
return 0 != (_sargs.parse_state & (_SARGS_EXPECT_KEY | _SARGS_EXPECT_VAL | _SARGS_EXPECT_SEP));
}
_SOKOL_PRIVATE bool _sargs_is_separator(char c) {
return c == '=';
}
_SOKOL_PRIVATE bool _sargs_is_quote(char c) {
if (0 == _sargs.quote) {
return (c == '\'') || (c == '"');
}
else {
return c == _sargs.quote;
}
}
_SOKOL_PRIVATE void _sargs_begin_quote(char c) {
_sargs.quote = c;
}
_SOKOL_PRIVATE void _sargs_end_quote(void) {
_sargs.quote = 0;
}
_SOKOL_PRIVATE bool _sargs_in_quotes(void) {
return 0 != _sargs.quote;
}
_SOKOL_PRIVATE bool _sargs_is_whitespace(char c) {
return !_sargs_in_quotes() && ((c == ' ') || (c == '\t'));
}
_SOKOL_PRIVATE void _sargs_start_key(void) {
SOKOL_ASSERT((_sargs.num_args >= 0) && (_sargs.num_args < _sargs.max_args));
_sargs.parse_state = _SARGS_PARSING_KEY;
_sargs.args[_sargs.num_args].key = _sargs.buf_pos;
}
_SOKOL_PRIVATE void _sargs_end_key(void) {
SOKOL_ASSERT((_sargs.num_args >= 0) && (_sargs.num_args < _sargs.max_args));
_sargs_putc(0);
// declare val as empty string in case this is a key-only arg
_sargs.args[_sargs.num_args].val = _sargs.buf_pos - 1;
_sargs.num_args++;
_sargs.parse_state = 0;
}
_SOKOL_PRIVATE bool _sargs_parsing_key(void) {
return 0 != (_sargs.parse_state & _SARGS_PARSING_KEY);
}
_SOKOL_PRIVATE void _sargs_start_val(void) {
SOKOL_ASSERT((_sargs.num_args > 0) && (_sargs.num_args <= _sargs.max_args));
_sargs.parse_state = _SARGS_PARSING_VAL;
_sargs.args[_sargs.num_args - 1].val = _sargs.buf_pos;
}
_SOKOL_PRIVATE void _sargs_end_val(void) {
_sargs_putc(0);
_sargs.parse_state = 0;
}
_SOKOL_PRIVATE bool _sargs_is_escape(char c) {
return '\\' == c;
}
_SOKOL_PRIVATE void _sargs_start_escape(void) {
_sargs.in_escape = true;
}
_SOKOL_PRIVATE bool _sargs_in_escape(void) {
return _sargs.in_escape;
}
_SOKOL_PRIVATE char _sargs_escape(char c) {
switch (c) {
case 'n': return '\n';
case 't': return '\t';
case 'r': return '\r';
case '\\': return '\\';
default: return c;
}
}
_SOKOL_PRIVATE void _sargs_end_escape(void) {
_sargs.in_escape = false;
}
_SOKOL_PRIVATE bool _sargs_parsing_val(void) {
return 0 != (_sargs.parse_state & _SARGS_PARSING_VAL);
}
_SOKOL_PRIVATE bool _sargs_parse_carg(const char* src) {
char c;
while (0 != (c = *src++)) {
if (_sargs_in_escape()) {
c = _sargs_escape(c);
_sargs_end_escape();
}
else if (_sargs_is_escape(c)) {
_sargs_start_escape();
continue;
}
if (_sargs_any_expected()) {
if (!_sargs_is_whitespace(c)) {
/* start of key, value or separator */
if (_sargs_is_separator(c)) {
/* skip separator and expect value */
_sargs_expect_val();
continue;
}
else if (_sargs_key_expected()) {
/* start of new key */
_sargs_start_key();
}
else if (_sargs_val_expected()) {
/* start of value */
if (_sargs_is_quote(c)) {
_sargs_begin_quote(c);
continue;
}
_sargs_start_val();
}
}
else {
/* skip white space */
continue;
}
}
else if (_sargs_parsing_key()) {
if (_sargs_is_whitespace(c) || _sargs_is_separator(c)) {
/* end of key string */
_sargs_end_key();
if (_sargs_is_separator(c)) {
_sargs_expect_val();
}
else {
_sargs_expect_sep_or_key();
}
continue;
}
}
else if (_sargs_parsing_val()) {
if (_sargs_in_quotes()) {
/* when in quotes, whitespace is a normal character
and a matching quote ends the value string
*/
if (_sargs_is_quote(c)) {
_sargs_end_quote();
_sargs_end_val();
_sargs_expect_key();
continue;
}
}
else if (_sargs_is_whitespace(c)) {
/* end of value string (no quotes) */
_sargs_end_val();
_sargs_expect_key();
continue;
}
}
_sargs_putc(c);
}
if (_sargs_parsing_key()) {
_sargs_end_key();
_sargs_expect_sep_or_key();
}
else if (_sargs_parsing_val() && !_sargs_in_quotes()) {
_sargs_end_val();
_sargs_expect_key();
}
return true;
}
_SOKOL_PRIVATE bool _sargs_parse_cargs(int argc, const char** argv) {
_sargs_expect_key();
bool retval = true;
for (int i = 1; i < argc; i++) {
retval &= _sargs_parse_carg(argv[i]);
}
_sargs.parse_state = 0;
return retval;
}
/*-- EMSCRIPTEN IMPLEMENTATION -----------------------------------------------*/
#if defined(__EMSCRIPTEN__)
#ifdef __cplusplus
extern "C" {
#endif
#if defined(EM_JS_DEPS)
EM_JS_DEPS(sokol_audio, "$withStackSave,$stringToUTF8OnStack");
#endif
EMSCRIPTEN_KEEPALIVE void _sargs_add_kvp(const char* key, const char* val) {
SOKOL_ASSERT(_sargs.valid && key && val);
if (_sargs.num_args >= _sargs.max_args) {
return;
}
/* copy key string */
char c;
_sargs.args[_sargs.num_args].key = _sargs.buf_pos;
const char* ptr = key;
while (0 != (c = *ptr++)) {
_sargs_putc(c);
}
_sargs_putc(0);
/* copy value string */
_sargs.args[_sargs.num_args].val = _sargs.buf_pos;
ptr = val;
while (0 != (c = *ptr++)) {
_sargs_putc(c);
}
_sargs_putc(0);
_sargs.num_args++;
}
#ifdef __cplusplus
} /* extern "C" */
#endif
/* JS function to extract arguments from the page URL */
EM_JS(void, sargs_js_parse_url, (void), {
const params = new URLSearchParams(window.location.search).entries();
for (let p = params.next(); !p.done; p = params.next()) {
const key = p.value[0];
const val = p.value[1];
withStackSave(() => {
const key_cstr = stringToUTF8OnStack(key);
const val_cstr = stringToUTF8OnStack(val);
__sargs_add_kvp(key_cstr, val_cstr)
});
}
});
#endif /* EMSCRIPTEN */
/*== PUBLIC IMPLEMENTATION FUNCTIONS =========================================*/
SOKOL_API_IMPL void sargs_setup(const sargs_desc* desc) {
SOKOL_ASSERT(desc);
_sargs_clear(&_sargs, sizeof(_sargs));
_sargs.max_args = _sargs_def(desc->max_args, _SARGS_MAX_ARGS_DEF);
_sargs.buf_size = _sargs_def(desc->buf_size, _SARGS_BUF_SIZE_DEF);
SOKOL_ASSERT(_sargs.buf_size > 8);
_sargs.args = (_sargs_kvp_t*) _sargs_malloc_clear((size_t)_sargs.max_args * sizeof(_sargs_kvp_t));
_sargs.buf = (char*) _sargs_malloc_clear((size_t)_sargs.buf_size * sizeof(char));
/* the first character in buf is reserved and always zero, this is the 'empty string' */
_sargs.buf_pos = 1;
_sargs.allocator = desc->allocator;
_sargs.valid = true;
/* parse argc/argv */
_sargs_parse_cargs(desc->argc, (const char**) desc->argv);
#if defined(__EMSCRIPTEN__)
/* on emscripten, also parse the page URL*/
sargs_js_parse_url();
#endif
}
SOKOL_API_IMPL void sargs_shutdown(void) {
SOKOL_ASSERT(_sargs.valid);
if (_sargs.args) {
_sargs_free(_sargs.args);
_sargs.args = 0;
}
if (_sargs.buf) {
_sargs_free(_sargs.buf);
_sargs.buf = 0;
}
_sargs.valid = false;
}
SOKOL_API_IMPL bool sargs_isvalid(void) {
return _sargs.valid;
}
SOKOL_API_IMPL int sargs_find(const char* key) {
SOKOL_ASSERT(_sargs.valid && key);
for (int i = 0; i < _sargs.num_args; i++) {
if (0 == strcmp(_sargs_str(_sargs.args[i].key), key)) {
return i;
}
}
return -1;
}
SOKOL_API_IMPL int sargs_num_args(void) {
SOKOL_ASSERT(_sargs.valid);
return _sargs.num_args;
}
SOKOL_API_IMPL const char* sargs_key_at(int index) {
SOKOL_ASSERT(_sargs.valid);
if ((index >= 0) && (index < _sargs.num_args)) {
return _sargs_str(_sargs.args[index].key);
}
else {
/* index 0 is always the empty string */
return _sargs_str(0);
}
}
SOKOL_API_IMPL const char* sargs_value_at(int index) {
SOKOL_ASSERT(_sargs.valid);
if ((index >= 0) && (index < _sargs.num_args)) {
return _sargs_str(_sargs.args[index].val);
}
else {
/* index 0 is always the empty string */
return _sargs_str(0);
}
}
SOKOL_API_IMPL bool sargs_exists(const char* key) {
SOKOL_ASSERT(_sargs.valid && key);
return -1 != sargs_find(key);
}
SOKOL_API_IMPL const char* sargs_value(const char* key) {
SOKOL_ASSERT(_sargs.valid && key);
return sargs_value_at(sargs_find(key));
}
SOKOL_API_IMPL const char* sargs_value_def(const char* key, const char* def) {
SOKOL_ASSERT(_sargs.valid && key && def);
int arg_index = sargs_find(key);
if (-1 != arg_index) {
const char* res = sargs_value_at(arg_index);
SOKOL_ASSERT(res);
if (res[0] == 0) {
return def;
} else {
return res;
}
}
else {
return def;
}
}
SOKOL_API_IMPL bool sargs_equals(const char* key, const char* val) {
SOKOL_ASSERT(_sargs.valid && key && val);
return 0 == strcmp(sargs_value(key), val);
}
SOKOL_API_IMPL bool sargs_boolean(const char* key) {
if (sargs_exists(key)) {
const char* val = sargs_value(key);
return (0 == strcmp("true", val)) ||
(0 == strcmp("yes", val)) ||
(0 == strcmp("on", val)) ||
(0 == strcmp("", val));
} else {
return false;
}
}
#endif /* SOKOL_ARGS_IMPL */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,162 +0,0 @@
#if defined(SOKOL_IMPL) && !defined(SOKOL_GLUE_IMPL)
#define SOKOL_GLUE_IMPL
#endif
#ifndef SOKOL_GLUE_INCLUDED
/*
sokol_glue.h -- glue helper functions for sokol headers
Project URL: https://github.com/floooh/sokol
Do this:
#define SOKOL_IMPL or
#define SOKOL_GLUE_IMPL
before you include this file in *one* C or C++ file to create the
implementation.
...optionally provide the following macros to override defaults:
SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
SOKOL_GLUE_API_DECL - public function declaration prefix (default: extern)
SOKOL_API_DECL - same as SOKOL_GLUE_API_DECL
SOKOL_API_IMPL - public function implementation prefix (default: -)
If sokol_glue.h is compiled as a DLL, define the following before
including the declaration or implementation:
SOKOL_DLL
On Windows, SOKOL_DLL will define SOKOL_GLUE_API_DECL as __declspec(dllexport)
or __declspec(dllimport) as needed.
OVERVIEW
========
sokol_glue.h provides glue helper functions between sokol_gfx.h and sokol_app.h,
so that sokol_gfx.h doesn't need to depend on sokol_app.h but can be
used with different window system glue libraries.
PROVIDED FUNCTIONS
==================
sg_environment sglue_environment(void)
Returns an sg_environment struct initialized by calling sokol_app.h
functions. Use this in the sg_setup() call like this:
sg_setup(&(sg_desc){
.environment = sglue_environment(),
...
});
sg_swapchain sglue_swapchain(void)
Returns an sg_swapchain struct initialized by calling sokol_app.h
functions. Use this in sg_begin_pass() for a 'swapchain pass' like
this:
sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain(), ... });
LICENSE
=======
zlib/libpng license
Copyright (c) 2018 Andre Weissflog
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the
use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#define SOKOL_GLUE_INCLUDED
#if defined(SOKOL_API_DECL) && !defined(SOKOL_GLUE_API_DECL)
#define SOKOL_GLUE_API_DECL SOKOL_API_DECL
#endif
#ifndef SOKOL_GLUE_API_DECL
#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GLUE_IMPL)
#define SOKOL_GLUE_API_DECL __declspec(dllexport)
#elif defined(_WIN32) && defined(SOKOL_DLL)
#define SOKOL_GLUE_API_DECL __declspec(dllimport)
#else
#define SOKOL_GLUE_API_DECL extern
#endif
#endif
#ifndef SOKOL_GFX_INCLUDED
#error "Please include sokol_gfx.h before sokol_glue.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
SOKOL_GLUE_API_DECL sg_environment sglue_environment(void);
SOKOL_GLUE_API_DECL sg_swapchain sglue_swapchain(void);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* SOKOL_GLUE_INCLUDED */
/*-- IMPLEMENTATION ----------------------------------------------------------*/
#ifdef SOKOL_GLUE_IMPL
#define SOKOL_GLUE_IMPL_INCLUDED (1)
#include <string.h> /* memset */
#ifndef SOKOL_APP_INCLUDED
#error "Please include sokol_app.h before the sokol_glue.h implementation"
#endif
#ifndef SOKOL_API_IMPL
#define SOKOL_API_IMPL
#endif
SOKOL_API_IMPL sg_environment sglue_environment(void) {
sg_environment env;
memset(&env, 0, sizeof(env));
env.defaults.color_format = (sg_pixel_format) sapp_color_format();
env.defaults.depth_format = (sg_pixel_format) sapp_depth_format();
env.defaults.sample_count = sapp_sample_count();
env.metal.device = sapp_metal_get_device();
env.d3d11.device = sapp_d3d11_get_device();
env.d3d11.device_context = sapp_d3d11_get_device_context();
env.wgpu.device = sapp_wgpu_get_device();
return env;
}
SOKOL_API_IMPL sg_swapchain sglue_swapchain(void) {
sg_swapchain swapchain;
memset(&swapchain, 0, sizeof(swapchain));
swapchain.width = sapp_width();
swapchain.height = sapp_height();
swapchain.sample_count = sapp_sample_count();
swapchain.color_format = (sg_pixel_format)sapp_color_format();
swapchain.depth_format = (sg_pixel_format)sapp_depth_format();
swapchain.metal.current_drawable = sapp_metal_get_current_drawable();
swapchain.metal.depth_stencil_texture = sapp_metal_get_depth_stencil_texture();
swapchain.metal.msaa_color_texture = sapp_metal_get_msaa_color_texture();
swapchain.d3d11.render_view = sapp_d3d11_get_render_view();
swapchain.d3d11.resolve_view = sapp_d3d11_get_resolve_view();
swapchain.d3d11.depth_stencil_view = sapp_d3d11_get_depth_stencil_view();
swapchain.wgpu.render_view = sapp_wgpu_get_render_view();
swapchain.wgpu.resolve_view = sapp_wgpu_get_resolve_view();
swapchain.wgpu.depth_stencil_view = sapp_wgpu_get_depth_stencil_view();
swapchain.gl.framebuffer = sapp_gl_get_framebuffer();
return swapchain;
}
#endif /* SOKOL_GLUE_IMPL */

View File

@@ -1,343 +0,0 @@
#if defined(SOKOL_IMPL) && !defined(SOKOL_LOG_IMPL)
#define SOKOL_LOG_IMPL
#endif
#ifndef SOKOL_LOG_INCLUDED
/*
sokol_log.h -- common logging callback for sokol headers
Project URL: https://github.com/floooh/sokol
Example code: https://github.com/floooh/sokol-samples
Do this:
#define SOKOL_IMPL or
#define SOKOL_LOG_IMPL
before you include this file in *one* C or C++ file to create the
implementation.
Optionally provide the following defines when building the implementation:
SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false))
SOKOL_LOG_API_DECL - public function declaration prefix (default: extern)
SOKOL_API_DECL - same as SOKOL_GFX_API_DECL
SOKOL_API_IMPL - public function implementation prefix (default: -)
Optionally define the following for verbose output:
SOKOL_DEBUG - by default this is defined if _DEBUG is defined
OVERVIEW
========
sokol_log.h provides a default logging callback for other sokol headers.
To use the default log callback, just include sokol_log.h and provide
a function pointer to the 'slog_func' function when setting up the
sokol library:
For instance with sokol_audio.h:
#include "sokol_log.h"
...
saudio_setup(&(saudio_desc){ .logger.func = slog_func });
Logging output goes to stderr and/or a platform specific logging subsystem
(which means that in some scenarios you might see logging messages duplicated):
- Windows: stderr + OutputDebugStringA()
- macOS/iOS/Linux: stderr + syslog()
- Emscripten: console.info()/warn()/error()
- Android: __android_log_write()
On Windows with sokol_app.h also note the runtime config items to make
stdout/stderr output visible on the console for WinMain() applications
via sapp_desc.win32_console_attach or sapp_desc.win32_console_create,
however when running in a debugger on Windows, the logging output should
show up on the debug output UI panel.
In debug mode, a log message might look like this:
[sspine][error][id:12] /Users/floh/projects/sokol/util/sokol_spine.h:3472:0:
SKELETON_DESC_NO_ATLAS: no atlas object provided in sspine_skeleton_desc.atlas
The source path and line number is formatted like compiler errors, in some IDEs (like VSCode)
such error messages are clickable.
In release mode, logging is less verbose as to not bloat the executable with string data, but you still get
enough information to identify the type and location of an error:
[sspine][error][id:12][line:3472]
RULES FOR WRITING YOUR OWN LOGGING FUNCTION
===========================================
- must be re-entrant because it might be called from different threads
- must treat **all** provided string pointers as optional (can be null)
- don't store the string pointers, copy the string data instead
- must not return for log level panic
LICENSE
=======
zlib/libpng license
Copyright (c) 2023 Andre Weissflog
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the
use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#define SOKOL_LOG_INCLUDED (1)
#include <stdint.h>
#if defined(SOKOL_API_DECL) && !defined(SOKOL_LOG_API_DECL)
#define SOKOL_LOG_API_DECL SOKOL_API_DECL
#endif
#ifndef SOKOL_LOG_API_DECL
#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_LOG_IMPL)
#define SOKOL_LOG_API_DECL __declspec(dllexport)
#elif defined(_WIN32) && defined(SOKOL_DLL)
#define SOKOL_LOG_API_DECL __declspec(dllimport)
#else
#define SOKOL_LOG_API_DECL extern
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
/*
Plug this function into the 'logger.func' struct item when initializing any of the sokol
headers. For instance for sokol_audio.h it would loom like this:
saudio_setup(&(saudio_desc){
.logger = {
.func = slog_func
}
});
*/
SOKOL_LOG_API_DECL void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // SOKOL_LOG_INCLUDED
// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██
// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██
// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████
//
// >>implementation
#ifdef SOKOL_LOG_IMPL
#define SOKOL_LOG_IMPL_INCLUDED (1)
#ifndef SOKOL_API_IMPL
#define SOKOL_API_IMPL
#endif
#ifndef SOKOL_DEBUG
#ifndef NDEBUG
#define SOKOL_DEBUG
#endif
#endif
#ifndef SOKOL_ASSERT
#include <assert.h>
#define SOKOL_ASSERT(c) assert(c)
#endif
#ifndef _SOKOL_PRIVATE
#if defined(__GNUC__) || defined(__clang__)
#define _SOKOL_PRIVATE __attribute__((unused)) static
#else
#define _SOKOL_PRIVATE static
#endif
#endif
#ifndef _SOKOL_UNUSED
#define _SOKOL_UNUSED(x) (void)(x)
#endif
// platform detection
#if defined(__APPLE__)
#define _SLOG_APPLE (1)
#elif defined(__EMSCRIPTEN__)
#define _SLOG_EMSCRIPTEN (1)
#elif defined(_WIN32)
#define _SLOG_WINDOWS (1)
#elif defined(__ANDROID__)
#define _SLOG_ANDROID (1)
#elif defined(__linux__) || defined(__unix__)
#define _SLOG_LINUX (1)
#else
#error "sokol_log.h: unknown platform"
#endif
#include <stdlib.h> // abort
#include <stdio.h> // fputs
#include <stddef.h> // size_t
#if defined(_SLOG_EMSCRIPTEN)
#include <emscripten/emscripten.h>
#elif defined(_SLOG_WINDOWS)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#elif defined(_SLOG_ANDROID)
#include <android/log.h>
#elif defined(_SLOG_LINUX) || defined(_SLOG_APPLE)
#include <syslog.h>
#endif
// size of line buffer (on stack!) in bytes including terminating zero
#define _SLOG_LINE_LENGTH (512)
_SOKOL_PRIVATE char* _slog_append(const char* str, char* dst, char* end) {
if (str) {
char c;
while (((c = *str++) != 0) && (dst < (end - 1))) {
*dst++ = c;
}
}
*dst = 0;
return dst;
}
_SOKOL_PRIVATE char* _slog_itoa(uint32_t x, char* buf, size_t buf_size) {
const size_t max_digits_and_null = 11;
if (buf_size < max_digits_and_null) {
return 0;
}
char* p = buf + max_digits_and_null;
*--p = 0;
do {
*--p = '0' + (x % 10);
x /= 10;
} while (x != 0);
return p;
}
#if defined(_SLOG_EMSCRIPTEN)
EM_JS(void, slog_js_log, (uint32_t level, const char* c_str), {
const str = UTF8ToString(c_str);
switch (level) {
case 0: console.error(str); break;
case 1: console.error(str); break;
case 2: console.warn(str); break;
default: console.info(str); break;
}
});
#endif
SOKOL_API_IMPL void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data) {
_SOKOL_UNUSED(user_data);
const char* log_level_str;
switch (log_level) {
case 0: log_level_str = "panic"; break;
case 1: log_level_str = "error"; break;
case 2: log_level_str = "warning"; break;
default: log_level_str = "info"; break;
}
// build log output line
char line_buf[_SLOG_LINE_LENGTH];
char* str = line_buf;
char* end = line_buf + sizeof(line_buf);
char num_buf[32];
if (tag) {
str = _slog_append("[", str, end);
str = _slog_append(tag, str, end);
str = _slog_append("]", str, end);
}
str = _slog_append("[", str, end);
str = _slog_append(log_level_str, str, end);
str = _slog_append("]", str, end);
str = _slog_append("[id:", str, end);
str = _slog_append(_slog_itoa(log_item, num_buf, sizeof(num_buf)), str, end);
str = _slog_append("]", str, end);
// if a filename is provided, build a clickable log message that's compatible with compiler error messages
if (filename) {
str = _slog_append(" ", str, end);
#if defined(_MSC_VER)
// MSVC compiler error format
str = _slog_append(filename, str, end);
str = _slog_append("(", str, end);
str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end);
str = _slog_append("): ", str, end);
#else
// gcc/clang compiler error format
str = _slog_append(filename, str, end);
str = _slog_append(":", str, end);
str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end);
str = _slog_append(":0: ", str, end);
#endif
}
else {
str = _slog_append("[line:", str, end);
str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end);
str = _slog_append("] ", str, end);
}
if (message) {
str = _slog_append("\n\t", str, end);
str = _slog_append(message, str, end);
}
str = _slog_append("\n\n", str, end);
if (0 == log_level) {
str = _slog_append("ABORTING because of [panic]\n", str, end);
(void)str;
}
// print to stderr?
#if defined(_SLOG_LINUX) || defined(_SLOG_WINDOWS) || defined(_SLOG_APPLE)
fputs(line_buf, stderr);
#endif
// platform specific logging calls
#if defined(_SLOG_WINDOWS)
OutputDebugStringA(line_buf);
#elif defined(_SLOG_ANDROID)
int prio;
switch (log_level) {
case 0: prio = ANDROID_LOG_FATAL; break;
case 1: prio = ANDROID_LOG_ERROR; break;
case 2: prio = ANDROID_LOG_WARN; break;
default: prio = ANDROID_LOG_INFO; break;
}
__android_log_write(prio, "SOKOL", line_buf);
#elif defined(_SLOG_EMSCRIPTEN)
slog_js_log(log_level, line_buf);
#elif defined(_SLOG_LINUX) || defined(_SLOG_APPLE)
int prio;
switch (log_level) {
case 0: prio = LOG_CRIT; break;
case 1: prio = LOG_ERR; break;
case 2: prio = LOG_WARNING; break;
default: prio = LOG_INFO; break;
}
syslog(prio, "%s", line_buf);
#endif
if (0 == log_level) {
abort();
}
}
#endif // SOKOL_LOG_IMPL

View File

@@ -1,319 +0,0 @@
#if defined(SOKOL_IMPL) && !defined(SOKOL_TIME_IMPL)
#define SOKOL_TIME_IMPL
#endif
#ifndef SOKOL_TIME_INCLUDED
/*
sokol_time.h -- simple cross-platform time measurement
Project URL: https://github.com/floooh/sokol
Do this:
#define SOKOL_IMPL or
#define SOKOL_TIME_IMPL
before you include this file in *one* C or C++ file to create the
implementation.
Optionally provide the following defines with your own implementations:
SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
SOKOL_TIME_API_DECL - public function declaration prefix (default: extern)
SOKOL_API_DECL - same as SOKOL_TIME_API_DECL
SOKOL_API_IMPL - public function implementation prefix (default: -)
If sokol_time.h is compiled as a DLL, define the following before
including the declaration or implementation:
SOKOL_DLL
On Windows, SOKOL_DLL will define SOKOL_TIME_API_DECL as __declspec(dllexport)
or __declspec(dllimport) as needed.
void stm_setup();
Call once before any other functions to initialize sokol_time
(this calls for instance QueryPerformanceFrequency on Windows)
uint64_t stm_now();
Get current point in time in unspecified 'ticks'. The value that
is returned has no relation to the 'wall-clock' time and is
not in a specific time unit, it is only useful to compute
time differences.
uint64_t stm_diff(uint64_t new, uint64_t old);
Computes the time difference between new and old. This will always
return a positive, non-zero value.
uint64_t stm_since(uint64_t start);
Takes the current time, and returns the elapsed time since start
(this is a shortcut for "stm_diff(stm_now(), start)")
uint64_t stm_laptime(uint64_t* last_time);
This is useful for measuring frame time and other recurring
events. It takes the current time, returns the time difference
to the value in last_time, and stores the current time in
last_time for the next call. If the value in last_time is 0,
the return value will be zero (this usually happens on the
very first call).
uint64_t stm_round_to_common_refresh_rate(uint64_t duration)
This oddly named function takes a measured frame time and
returns the closest "nearby" common display refresh rate frame duration
in ticks. If the input duration isn't close to any common display
refresh rate, the input duration will be returned unchanged as a fallback.
The main purpose of this function is to remove jitter/inaccuracies from
measured frame times, and instead use the display refresh rate as
frame duration.
NOTE: for more robust frame timing, consider using the
sokol_app.h function sapp_frame_duration()
Use the following functions to convert a duration in ticks into
useful time units:
double stm_sec(uint64_t ticks);
double stm_ms(uint64_t ticks);
double stm_us(uint64_t ticks);
double stm_ns(uint64_t ticks);
Converts a tick value into seconds, milliseconds, microseconds
or nanoseconds. Note that not all platforms will have nanosecond
or even microsecond precision.
Uses the following time measurement functions under the hood:
Windows: QueryPerformanceFrequency() / QueryPerformanceCounter()
MacOS/iOS: mach_absolute_time()
emscripten: emscripten_get_now()
Linux+others: clock_gettime(CLOCK_MONOTONIC)
zlib/libpng license
Copyright (c) 2018 Andre Weissflog
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the
use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#define SOKOL_TIME_INCLUDED (1)
#include <stdint.h>
#if defined(SOKOL_API_DECL) && !defined(SOKOL_TIME_API_DECL)
#define SOKOL_TIME_API_DECL SOKOL_API_DECL
#endif
#ifndef SOKOL_TIME_API_DECL
#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_TIME_IMPL)
#define SOKOL_TIME_API_DECL __declspec(dllexport)
#elif defined(_WIN32) && defined(SOKOL_DLL)
#define SOKOL_TIME_API_DECL __declspec(dllimport)
#else
#define SOKOL_TIME_API_DECL extern
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
SOKOL_TIME_API_DECL void stm_setup(void);
SOKOL_TIME_API_DECL uint64_t stm_now(void);
SOKOL_TIME_API_DECL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks);
SOKOL_TIME_API_DECL uint64_t stm_since(uint64_t start_ticks);
SOKOL_TIME_API_DECL uint64_t stm_laptime(uint64_t* last_time);
SOKOL_TIME_API_DECL uint64_t stm_round_to_common_refresh_rate(uint64_t frame_ticks);
SOKOL_TIME_API_DECL double stm_sec(uint64_t ticks);
SOKOL_TIME_API_DECL double stm_ms(uint64_t ticks);
SOKOL_TIME_API_DECL double stm_us(uint64_t ticks);
SOKOL_TIME_API_DECL double stm_ns(uint64_t ticks);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif // SOKOL_TIME_INCLUDED
/*-- IMPLEMENTATION ----------------------------------------------------------*/
#ifdef SOKOL_TIME_IMPL
#define SOKOL_TIME_IMPL_INCLUDED (1)
#include <string.h> /* memset */
#ifndef SOKOL_API_IMPL
#define SOKOL_API_IMPL
#endif
#ifndef SOKOL_ASSERT
#include <assert.h>
#define SOKOL_ASSERT(c) assert(c)
#endif
#ifndef _SOKOL_PRIVATE
#if defined(__GNUC__) || defined(__clang__)
#define _SOKOL_PRIVATE __attribute__((unused)) static
#else
#define _SOKOL_PRIVATE static
#endif
#endif
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
typedef struct {
uint32_t initialized;
LARGE_INTEGER freq;
LARGE_INTEGER start;
} _stm_state_t;
#elif defined(__APPLE__) && defined(__MACH__)
#include <mach/mach_time.h>
typedef struct {
uint32_t initialized;
mach_timebase_info_data_t timebase;
uint64_t start;
} _stm_state_t;
#elif defined(__EMSCRIPTEN__)
#include <emscripten/emscripten.h>
typedef struct {
uint32_t initialized;
double start;
} _stm_state_t;
#else /* anything else, this will need more care for non-Linux platforms */
#ifdef ESP8266
// On the ESP8266, clock_gettime ignores the first argument and CLOCK_MONOTONIC isn't defined
#define CLOCK_MONOTONIC 0
#endif
#include <time.h>
typedef struct {
uint32_t initialized;
uint64_t start;
} _stm_state_t;
#endif
static _stm_state_t _stm;
/* prevent 64-bit overflow when computing relative timestamp
see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3
*/
#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__))
_SOKOL_PRIVATE int64_t _stm_int64_muldiv(int64_t value, int64_t numer, int64_t denom) {
int64_t q = value / denom;
int64_t r = value % denom;
return q * numer + r * numer / denom;
}
#endif
SOKOL_API_IMPL void stm_setup(void) {
memset(&_stm, 0, sizeof(_stm));
_stm.initialized = 0xABCDABCD;
#if defined(_WIN32)
QueryPerformanceFrequency(&_stm.freq);
QueryPerformanceCounter(&_stm.start);
#elif defined(__APPLE__) && defined(__MACH__)
mach_timebase_info(&_stm.timebase);
_stm.start = mach_absolute_time();
#elif defined(__EMSCRIPTEN__)
_stm.start = emscripten_get_now();
#else
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
_stm.start = (uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec;
#endif
}
SOKOL_API_IMPL uint64_t stm_now(void) {
SOKOL_ASSERT(_stm.initialized == 0xABCDABCD);
uint64_t now;
#if defined(_WIN32)
LARGE_INTEGER qpc_t;
QueryPerformanceCounter(&qpc_t);
now = (uint64_t) _stm_int64_muldiv(qpc_t.QuadPart - _stm.start.QuadPart, 1000000000, _stm.freq.QuadPart);
#elif defined(__APPLE__) && defined(__MACH__)
const uint64_t mach_now = mach_absolute_time() - _stm.start;
now = (uint64_t) _stm_int64_muldiv((int64_t)mach_now, (int64_t)_stm.timebase.numer, (int64_t)_stm.timebase.denom);
#elif defined(__EMSCRIPTEN__)
double js_now = emscripten_get_now() - _stm.start;
now = (uint64_t) (js_now * 1000000.0);
#else
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
now = ((uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec) - _stm.start;
#endif
return now;
}
SOKOL_API_IMPL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks) {
if (new_ticks > old_ticks) {
return new_ticks - old_ticks;
}
else {
return 1;
}
}
SOKOL_API_IMPL uint64_t stm_since(uint64_t start_ticks) {
return stm_diff(stm_now(), start_ticks);
}
SOKOL_API_IMPL uint64_t stm_laptime(uint64_t* last_time) {
SOKOL_ASSERT(last_time);
uint64_t dt = 0;
uint64_t now = stm_now();
if (0 != *last_time) {
dt = stm_diff(now, *last_time);
}
*last_time = now;
return dt;
}
// first number is frame duration in ns, second number is tolerance in ns,
// the resulting min/max values must not overlap!
static const uint64_t _stm_refresh_rates[][2] = {
{ 16666667, 1000000 }, // 60 Hz: 16.6667 +- 1ms
{ 13888889, 250000 }, // 72 Hz: 13.8889 +- 0.25ms
{ 13333333, 250000 }, // 75 Hz: 13.3333 +- 0.25ms
{ 11764706, 250000 }, // 85 Hz: 11.7647 +- 0.25
{ 11111111, 250000 }, // 90 Hz: 11.1111 +- 0.25ms
{ 10000000, 500000 }, // 100 Hz: 10.0000 +- 0.5ms
{ 8333333, 500000 }, // 120 Hz: 8.3333 +- 0.5ms
{ 6944445, 500000 }, // 144 Hz: 6.9445 +- 0.5ms
{ 4166667, 1000000 }, // 240 Hz: 4.1666 +- 1ms
{ 0, 0 }, // keep the last element always at zero
};
SOKOL_API_IMPL uint64_t stm_round_to_common_refresh_rate(uint64_t ticks) {
uint64_t ns;
int i = 0;
while (0 != (ns = _stm_refresh_rates[i][0])) {
uint64_t tol = _stm_refresh_rates[i][1];
if ((ticks > (ns - tol)) && (ticks < (ns + tol))) {
return ns;
}
i++;
}
// fallthrough: didn't fit into any buckets
return ticks;
}
SOKOL_API_IMPL double stm_sec(uint64_t ticks) {
return (double)ticks / 1000000000.0;
}
SOKOL_API_IMPL double stm_ms(uint64_t ticks) {
return (double)ticks / 1000000.0;
}
SOKOL_API_IMPL double stm_us(uint64_t ticks) {
return (double)ticks / 1000.0;
}
SOKOL_API_IMPL double stm_ns(uint64_t ticks) {
return (double)ticks;
}
#endif /* SOKOL_TIME_IMPL */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,167 +0,0 @@
#if defined(SOKOL_IMPL) && !defined(SOKOL_MEMTRACK_IMPL)
#define SOKOL_MEMTRACK_IMPL
#endif
#ifndef SOKOL_MEMTRACK_INCLUDED
/*
sokol_memtrack.h -- memory allocation wrapper to track memory usage
of sokol libraries
Project URL: https://github.com/floooh/sokol
Optionally provide the following defines with your own implementations:
SOKOL_MEMTRACK_API_DECL - public function declaration prefix (default: extern)
SOKOL_API_DECL - same as SOKOL_MEMTRACK_API_DECL
SOKOL_API_IMPL - public function implementation prefix (default: -)
If sokol_memtrack.h is compiled as a DLL, define the following before
including the declaration or implementation:
SOKOL_DLL
USAGE
=====
Just plug the malloc/free wrapper functions into the desc.allocator
struct provided by most sokol header setup functions:
sg_setup(&(sg_desc){
//...
.allocator = {
.alloc_fn = smemtrack_alloc,
.free_fn = smemtrack_free,
}
});
Then call smemtrack_info() to get information about current number
of allocations and overall allocation size:
const smemtrack_info_t info = smemtrack_info();
const int num_allocs = info.num_allocs;
const int num_bytes = info.num_bytes;
Note the sokol_memtrack.h can only track allocations issued by
the sokol headers, not allocations that happen under the hood
in system libraries.
LICENSE
=======
zlib/libpng license
Copyright (c) 2018 Andre Weissflog
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the
use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#define SOKOL_MEMTRACK_INCLUDED (1)
#include <stdint.h>
#include <stddef.h> // size_t
#if defined(SOKOL_API_DECL) && !defined(SOKOL_MEMTRACK_API_DECL)
#define SOKOL_MEMTRACK_API_DECL SOKOL_API_DECL
#endif
#ifndef SOKOL_MEMTRACK_API_DECL
#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_MEMTRACK_IMPL)
#define SOKOL_MEMTRACK_API_DECL __declspec(dllexport)
#elif defined(_WIN32) && defined(SOKOL_DLL)
#define SOKOL_MEMTRACK_API_DECL __declspec(dllimport)
#else
#define SOKOL_MEMTRACK_API_DECL extern
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct smemtrack_info_t {
int num_allocs;
int num_bytes;
} smemtrack_info_t;
SOKOL_MEMTRACK_API_DECL smemtrack_info_t smemtrack_info(void);
SOKOL_MEMTRACK_API_DECL void* smemtrack_alloc(size_t size, void* user_data);
SOKOL_MEMTRACK_API_DECL void smemtrack_free(void* ptr, void* user_data);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* SOKOL_MEMTRACK_INCLUDED */
/*=== IMPLEMENTATION =========================================================*/
#ifdef SOKOL_MEMTRACK_IMPL
#define SOKOL_MEMTRACK_IMPL_INCLUDED (1)
#include <stdlib.h> // malloc, free
#include <string.h> // memset
#ifndef SOKOL_API_IMPL
#define SOKOL_API_IMPL
#endif
#ifndef SOKOL_DEBUG
#ifndef NDEBUG
#define SOKOL_DEBUG
#endif
#endif
#ifndef _SOKOL_PRIVATE
#if defined(__GNUC__) || defined(__clang__)
#define _SOKOL_PRIVATE __attribute__((unused)) static
#else
#define _SOKOL_PRIVATE static
#endif
#endif
// per-allocation header used to keep track of the allocation size
#define _SMEMTRACK_HEADER_SIZE (16)
static struct {
smemtrack_info_t state;
} _smemtrack;
SOKOL_API_IMPL void* smemtrack_alloc(size_t size, void* user_data) {
(void)user_data;
uint8_t* ptr = (uint8_t*) malloc(size + _SMEMTRACK_HEADER_SIZE);
if (ptr) {
// store allocation size (for allocation size tracking)
*(size_t*)ptr = size;
_smemtrack.state.num_allocs++;
_smemtrack.state.num_bytes += (int) size;
return ptr + _SMEMTRACK_HEADER_SIZE;
}
else {
// allocation failed, return null pointer
return ptr;
}
}
SOKOL_API_IMPL void smemtrack_free(void* ptr, void* user_data) {
(void)user_data;
if (ptr) {
uint8_t* alloc_ptr = ((uint8_t*)ptr) - _SMEMTRACK_HEADER_SIZE;
size_t size = *(size_t*)alloc_ptr;
_smemtrack.state.num_allocs--;
_smemtrack.state.num_bytes -= (int) size;
free(alloc_ptr);
}
}
SOKOL_API_IMPL smemtrack_info_t smemtrack_info(void) {
return _smemtrack.state;
}
#endif /* SOKOL_MEMTRACK_IMPL */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,848 +0,0 @@
/*
Copyright (c) 2013-2021, tinydir authors:
- Cong Xu
- Lautis Sun
- Baudouin Feildel
- Andargor <andargor@yahoo.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TINYDIR_H
#define TINYDIR_H
#ifdef __cplusplus
extern "C" {
#endif
#if ((defined _UNICODE) && !(defined UNICODE))
#define UNICODE
#endif
#if ((defined UNICODE) && !(defined _UNICODE))
#define _UNICODE
#endif
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#ifdef _MSC_VER
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <windows.h>
# include <tchar.h>
# pragma warning(push)
# pragma warning (disable : 4996)
#else
# include <dirent.h>
# include <libgen.h>
# include <sys/stat.h>
# include <stddef.h>
#endif
#ifdef __MINGW32__
# include <tchar.h>
#endif
/* types */
/* Windows UNICODE wide character support */
#if defined _MSC_VER || defined __MINGW32__
# define _tinydir_char_t TCHAR
# define TINYDIR_STRING(s) _TEXT(s)
# define _tinydir_strlen _tcslen
# define _tinydir_strcpy _tcscpy
# define _tinydir_strcat _tcscat
# define _tinydir_strcmp _tcscmp
# define _tinydir_strrchr _tcsrchr
# define _tinydir_strncmp _tcsncmp
#else
# define _tinydir_char_t char
# define TINYDIR_STRING(s) s
# define _tinydir_strlen strlen
# define _tinydir_strcpy strcpy
# define _tinydir_strcat strcat
# define _tinydir_strcmp strcmp
# define _tinydir_strrchr strrchr
# define _tinydir_strncmp strncmp
#endif
#if (defined _MSC_VER || defined __MINGW32__)
# include <windows.h>
# define _TINYDIR_PATH_MAX MAX_PATH
#elif defined __linux__
# include <limits.h>
# ifdef PATH_MAX
# define _TINYDIR_PATH_MAX PATH_MAX
# endif
#elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
# include <sys/param.h>
# if defined(BSD)
# include <limits.h>
# ifdef PATH_MAX
# define _TINYDIR_PATH_MAX PATH_MAX
# endif
# endif
#endif
#ifndef _TINYDIR_PATH_MAX
#define _TINYDIR_PATH_MAX 4096
#endif
#ifdef _MSC_VER
/* extra chars for the "\\*" mask */
# define _TINYDIR_PATH_EXTRA 2
#else
# define _TINYDIR_PATH_EXTRA 0
#endif
#define _TINYDIR_FILENAME_MAX 256
#if (defined _MSC_VER || defined __MINGW32__)
#define _TINYDIR_DRIVE_MAX 3
#endif
#ifdef _MSC_VER
# define _TINYDIR_FUNC static __inline
#elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
# define _TINYDIR_FUNC static __inline__
#elif defined(__cplusplus)
# define _TINYDIR_FUNC static inline
#elif defined(__GNUC__)
/* Suppress unused function warning */
# define _TINYDIR_FUNC __attribute__((unused)) static
#else
# define _TINYDIR_FUNC static
#endif
#if defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86)
#ifdef _MSC_VER
# define _TINYDIR_CDECL __cdecl
#else
# define _TINYDIR_CDECL __attribute__((cdecl))
#endif
#else
# define _TINYDIR_CDECL
#endif
/* readdir_r usage; define TINYDIR_USE_READDIR_R to use it (if supported) */
#ifdef TINYDIR_USE_READDIR_R
/* readdir_r is a POSIX-only function, and may not be available under various
* environments/settings, e.g. MinGW. Use readdir fallback */
#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE ||\
_POSIX_SOURCE
# define _TINYDIR_HAS_READDIR_R
#endif
#if _POSIX_C_SOURCE >= 200112L
# define _TINYDIR_HAS_FPATHCONF
# include <unistd.h>
#endif
#if _BSD_SOURCE || _SVID_SOURCE || \
(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
# define _TINYDIR_HAS_DIRFD
# include <sys/types.h>
#endif
#if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD &&\
defined _PC_NAME_MAX
# define _TINYDIR_USE_FPATHCONF
#endif
#if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R ||\
!(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX)
# define _TINYDIR_USE_READDIR
#endif
/* Use readdir by default */
#else
# define _TINYDIR_USE_READDIR
#endif
/* MINGW32 has two versions of dirent, ASCII and UNICODE*/
#ifndef _MSC_VER
#if (defined __MINGW32__) && (defined _UNICODE)
#define _TINYDIR_DIR _WDIR
#define _tinydir_dirent _wdirent
#define _tinydir_opendir _wopendir
#define _tinydir_readdir _wreaddir
#define _tinydir_closedir _wclosedir
#else
#define _TINYDIR_DIR DIR
#define _tinydir_dirent dirent
#define _tinydir_opendir opendir
#define _tinydir_readdir readdir
#define _tinydir_closedir closedir
#endif
#endif
/* Allow user to use a custom allocator by defining _TINYDIR_MALLOC and _TINYDIR_FREE. */
#if defined(_TINYDIR_MALLOC) && defined(_TINYDIR_FREE)
#elif !defined(_TINYDIR_MALLOC) && !defined(_TINYDIR_FREE)
#else
#error "Either define both alloc and free or none of them!"
#endif
#if !defined(_TINYDIR_MALLOC)
#define _TINYDIR_MALLOC(_size) malloc(_size)
#define _TINYDIR_FREE(_ptr) free(_ptr)
#endif /* !defined(_TINYDIR_MALLOC) */
typedef struct tinydir_file
{
_tinydir_char_t path[_TINYDIR_PATH_MAX];
_tinydir_char_t name[_TINYDIR_FILENAME_MAX];
_tinydir_char_t *extension;
int is_dir;
int is_reg;
#ifndef _MSC_VER
#ifdef __MINGW32__
struct _stat _s;
#else
struct stat _s;
#endif
#endif
} tinydir_file;
typedef struct tinydir_dir
{
_tinydir_char_t path[_TINYDIR_PATH_MAX];
int has_next;
size_t n_files;
tinydir_file *_files;
#ifdef _MSC_VER
HANDLE _h;
WIN32_FIND_DATA _f;
#else
_TINYDIR_DIR *_d;
struct _tinydir_dirent *_e;
#ifndef _TINYDIR_USE_READDIR
struct _tinydir_dirent *_ep;
#endif
#endif
} tinydir_dir;
/* declarations */
_TINYDIR_FUNC
int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path);
_TINYDIR_FUNC
int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path);
_TINYDIR_FUNC
void tinydir_close(tinydir_dir *dir);
_TINYDIR_FUNC
int tinydir_next(tinydir_dir *dir);
_TINYDIR_FUNC
int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file);
_TINYDIR_FUNC
int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i);
_TINYDIR_FUNC
int tinydir_open_subdir_n(tinydir_dir *dir, size_t i);
_TINYDIR_FUNC
int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path);
_TINYDIR_FUNC
void _tinydir_get_ext(tinydir_file *file);
_TINYDIR_FUNC
int _TINYDIR_CDECL _tinydir_file_cmp(const void *a, const void *b);
#ifndef _MSC_VER
#ifndef _TINYDIR_USE_READDIR
_TINYDIR_FUNC
size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp);
#endif
#endif
/* definitions*/
_TINYDIR_FUNC
int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path)
{
#ifndef _MSC_VER
#ifndef _TINYDIR_USE_READDIR
int error;
int size; /* using int size */
#endif
#else
_tinydir_char_t path_buf[_TINYDIR_PATH_MAX];
#endif
_tinydir_char_t *pathp;
if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0)
{
errno = EINVAL;
return -1;
}
if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
{
errno = ENAMETOOLONG;
return -1;
}
/* initialise dir */
dir->_files = NULL;
#ifdef _MSC_VER
dir->_h = INVALID_HANDLE_VALUE;
#else
dir->_d = NULL;
#ifndef _TINYDIR_USE_READDIR
dir->_ep = NULL;
#endif
#endif
tinydir_close(dir);
_tinydir_strcpy(dir->path, path);
/* Remove trailing slashes */
pathp = &dir->path[_tinydir_strlen(dir->path) - 1];
while (pathp != dir->path && (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/')))
{
*pathp = TINYDIR_STRING('\0');
pathp++;
}
#ifdef _MSC_VER
_tinydir_strcpy(path_buf, dir->path);
_tinydir_strcat(path_buf, TINYDIR_STRING("\\*"));
#if (defined WINAPI_FAMILY) && (WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP)
dir->_h = FindFirstFileEx(path_buf, FindExInfoStandard, &dir->_f, FindExSearchNameMatch, NULL, 0);
#else
dir->_h = FindFirstFile(path_buf, &dir->_f);
#endif
if (dir->_h == INVALID_HANDLE_VALUE)
{
errno = ENOENT;
#else
dir->_d = _tinydir_opendir(path);
if (dir->_d == NULL)
{
#endif
goto bail;
}
/* read first file */
dir->has_next = 1;
#ifndef _MSC_VER
#ifdef _TINYDIR_USE_READDIR
dir->_e = _tinydir_readdir(dir->_d);
#else
/* allocate dirent buffer for readdir_r */
size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */
if (size == -1) return -1;
dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size);
if (dir->_ep == NULL) return -1;
error = readdir_r(dir->_d, dir->_ep, &dir->_e);
if (error != 0) return -1;
#endif
if (dir->_e == NULL)
{
dir->has_next = 0;
}
#endif
return 0;
bail:
tinydir_close(dir);
return -1;
}
_TINYDIR_FUNC
int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path)
{
/* Count the number of files first, to pre-allocate the files array */
size_t n_files = 0;
if (tinydir_open(dir, path) == -1)
{
return -1;
}
while (dir->has_next)
{
n_files++;
if (tinydir_next(dir) == -1)
{
goto bail;
}
}
tinydir_close(dir);
if (n_files == 0 || tinydir_open(dir, path) == -1)
{
return -1;
}
dir->n_files = 0;
dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files);
if (dir->_files == NULL)
{
goto bail;
}
while (dir->has_next)
{
tinydir_file *p_file;
dir->n_files++;
p_file = &dir->_files[dir->n_files - 1];
if (tinydir_readfile(dir, p_file) == -1)
{
goto bail;
}
if (tinydir_next(dir) == -1)
{
goto bail;
}
/* Just in case the number of files has changed between the first and
second reads, terminate without writing into unallocated memory */
if (dir->n_files == n_files)
{
break;
}
}
qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp);
return 0;
bail:
tinydir_close(dir);
return -1;
}
_TINYDIR_FUNC
void tinydir_close(tinydir_dir *dir)
{
if (dir == NULL)
{
return;
}
memset(dir->path, 0, sizeof(dir->path));
dir->has_next = 0;
dir->n_files = 0;
_TINYDIR_FREE(dir->_files);
dir->_files = NULL;
#ifdef _MSC_VER
if (dir->_h != INVALID_HANDLE_VALUE)
{
FindClose(dir->_h);
}
dir->_h = INVALID_HANDLE_VALUE;
#else
if (dir->_d)
{
_tinydir_closedir(dir->_d);
}
dir->_d = NULL;
dir->_e = NULL;
#ifndef _TINYDIR_USE_READDIR
_TINYDIR_FREE(dir->_ep);
dir->_ep = NULL;
#endif
#endif
}
_TINYDIR_FUNC
int tinydir_next(tinydir_dir *dir)
{
if (dir == NULL)
{
errno = EINVAL;
return -1;
}
if (!dir->has_next)
{
errno = ENOENT;
return -1;
}
#ifdef _MSC_VER
if (FindNextFile(dir->_h, &dir->_f) == 0)
#else
#ifdef _TINYDIR_USE_READDIR
dir->_e = _tinydir_readdir(dir->_d);
#else
if (dir->_ep == NULL)
{
return -1;
}
if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0)
{
return -1;
}
#endif
if (dir->_e == NULL)
#endif
{
dir->has_next = 0;
#ifdef _MSC_VER
if (GetLastError() != ERROR_SUCCESS &&
GetLastError() != ERROR_NO_MORE_FILES)
{
tinydir_close(dir);
errno = EIO;
return -1;
}
#endif
}
return 0;
}
_TINYDIR_FUNC
int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
{
const _tinydir_char_t *filename;
if (dir == NULL || file == NULL)
{
errno = EINVAL;
return -1;
}
#ifdef _MSC_VER
if (dir->_h == INVALID_HANDLE_VALUE)
#else
if (dir->_e == NULL)
#endif
{
errno = ENOENT;
return -1;
}
filename =
#ifdef _MSC_VER
dir->_f.cFileName;
#else
dir->_e->d_name;
#endif
if (_tinydir_strlen(dir->path) +
_tinydir_strlen(filename) + 1 + _TINYDIR_PATH_EXTRA >=
_TINYDIR_PATH_MAX)
{
/* the path for the file will be too long */
errno = ENAMETOOLONG;
return -1;
}
if (_tinydir_strlen(filename) >= _TINYDIR_FILENAME_MAX)
{
errno = ENAMETOOLONG;
return -1;
}
_tinydir_strcpy(file->path, dir->path);
if (_tinydir_strcmp(dir->path, TINYDIR_STRING("/")) != 0)
_tinydir_strcat(file->path, TINYDIR_STRING("/"));
_tinydir_strcpy(file->name, filename);
_tinydir_strcat(file->path, filename);
#ifndef _MSC_VER
#ifdef __MINGW32__
if (_tstat(
#elif (defined _BSD_SOURCE) || (defined _DEFAULT_SOURCE) \
|| ((defined _XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500)) \
|| ((defined _POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)) \
|| ((defined __APPLE__) && (defined __MACH__)) \
|| (defined BSD)
if (lstat(
#else
if (stat(
#endif
file->path, &file->_s) == -1)
{
return -1;
}
#endif
_tinydir_get_ext(file);
file->is_dir =
#ifdef _MSC_VER
!!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
#else
S_ISDIR(file->_s.st_mode);
#endif
file->is_reg =
#ifdef _MSC_VER
!!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
(
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) &&
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) &&
#endif
#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
#endif
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
#else
S_ISREG(file->_s.st_mode);
#endif
return 0;
}
_TINYDIR_FUNC
int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
{
if (dir == NULL || file == NULL)
{
errno = EINVAL;
return -1;
}
if (i >= dir->n_files)
{
errno = ENOENT;
return -1;
}
memcpy(file, &dir->_files[i], sizeof(tinydir_file));
_tinydir_get_ext(file);
return 0;
}
_TINYDIR_FUNC
int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
{
_tinydir_char_t path[_TINYDIR_PATH_MAX];
if (dir == NULL)
{
errno = EINVAL;
return -1;
}
if (i >= dir->n_files || !dir->_files[i].is_dir)
{
errno = ENOENT;
return -1;
}
_tinydir_strcpy(path, dir->_files[i].path);
tinydir_close(dir);
if (tinydir_open_sorted(dir, path) == -1)
{
return -1;
}
return 0;
}
/* Open a single file given its path */
_TINYDIR_FUNC
int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
{
tinydir_dir dir;
int result = 0;
int found = 0;
_tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX];
_tinydir_char_t file_name_buf[_TINYDIR_PATH_MAX];
_tinydir_char_t *dir_name;
_tinydir_char_t *base_name;
#if (defined _MSC_VER || defined __MINGW32__)
_tinydir_char_t drive_buf[_TINYDIR_PATH_MAX];
_tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX];
#endif
if (file == NULL || path == NULL || _tinydir_strlen(path) == 0)
{
errno = EINVAL;
return -1;
}
if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
{
errno = ENAMETOOLONG;
return -1;
}
/* Get the parent path */
#if (defined _MSC_VER || defined __MINGW32__)
#if ((defined _MSC_VER) && (_MSC_VER >= 1400))
errno = _tsplitpath_s(
path,
drive_buf, _TINYDIR_DRIVE_MAX,
dir_name_buf, _TINYDIR_FILENAME_MAX,
file_name_buf, _TINYDIR_FILENAME_MAX,
ext_buf, _TINYDIR_FILENAME_MAX);
#else
_tsplitpath(
path,
drive_buf,
dir_name_buf,
file_name_buf,
ext_buf);
#endif
if (errno)
{
return -1;
}
/* _splitpath_s not work fine with only filename and widechar support */
#ifdef _UNICODE
if (drive_buf[0] == L'\xFEFE')
drive_buf[0] = '\0';
if (dir_name_buf[0] == L'\xFEFE')
dir_name_buf[0] = '\0';
#endif
/* Emulate the behavior of dirname by returning "." for dir name if it's
empty */
if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0')
{
_tinydir_strcpy(dir_name_buf, TINYDIR_STRING("."));
}
/* Concatenate the drive letter and dir name to form full dir name */
_tinydir_strcat(drive_buf, dir_name_buf);
dir_name = drive_buf;
/* Concatenate the file name and extension to form base name */
_tinydir_strcat(file_name_buf, ext_buf);
base_name = file_name_buf;
#else
_tinydir_strcpy(dir_name_buf, path);
dir_name = dirname(dir_name_buf);
_tinydir_strcpy(file_name_buf, path);
base_name = basename(file_name_buf);
#endif
/* Special case: if the path is a root dir, open the parent dir as the file */
#if (defined _MSC_VER || defined __MINGW32__)
if (_tinydir_strlen(base_name) == 0)
#else
if ((_tinydir_strcmp(base_name, TINYDIR_STRING("/"))) == 0)
#endif
{
memset(file, 0, sizeof * file);
file->is_dir = 1;
file->is_reg = 0;
_tinydir_strcpy(file->path, dir_name);
file->extension = file->path + _tinydir_strlen(file->path);
return 0;
}
/* Open the parent directory */
if (tinydir_open(&dir, dir_name) == -1)
{
return -1;
}
/* Read through the parent directory and look for the file */
while (dir.has_next)
{
if (tinydir_readfile(&dir, file) == -1)
{
result = -1;
goto bail;
}
if (_tinydir_strcmp(file->name, base_name) == 0)
{
/* File found */
found = 1;
break;
}
tinydir_next(&dir);
}
if (!found)
{
result = -1;
errno = ENOENT;
}
bail:
tinydir_close(&dir);
return result;
}
_TINYDIR_FUNC
void _tinydir_get_ext(tinydir_file *file)
{
_tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.'));
if (period == NULL)
{
file->extension = &(file->name[_tinydir_strlen(file->name)]);
}
else
{
file->extension = period + 1;
}
}
_TINYDIR_FUNC
int _TINYDIR_CDECL _tinydir_file_cmp(const void *a, const void *b)
{
const tinydir_file *fa = (const tinydir_file *)a;
const tinydir_file *fb = (const tinydir_file *)b;
if (fa->is_dir != fb->is_dir)
{
return -(fa->is_dir - fb->is_dir);
}
return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
}
#ifndef _MSC_VER
#ifndef _TINYDIR_USE_READDIR
/*
The following authored by Ben Hutchings <ben@decadent.org.uk>
from https://womble.decadent.org.uk/readdir_r-advisory.html
*/
/* Calculate the required buffer size (in bytes) for directory *
* entries read from the given directory handle. Return -1 if this *
* this cannot be done. *
* *
* This code does not trust values of NAME_MAX that are less than *
* 255, since some systems (including at least HP-UX) incorrectly *
* define it to be a smaller value. */
_TINYDIR_FUNC
size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp)
{
long name_max;
size_t name_end;
/* parameter may be unused */
(void)dirp;
#if defined _TINYDIR_USE_FPATHCONF
name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
if (name_max == -1)
#if defined(NAME_MAX)
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
#else
return (size_t)(-1);
#endif
#elif defined(NAME_MAX)
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
#else
#error "buffer size for readdir_r cannot be determined"
#endif
name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1;
return (name_end > sizeof(struct _tinydir_dirent) ?
name_end : sizeof(struct _tinydir_dirent));
}
#endif
#endif
#ifdef __cplusplus
}
#endif
# if defined (_MSC_VER)
# pragma warning(pop)
# endif
#endif

View File

@@ -2,7 +2,13 @@
#include "script.h"
#include <string.h>
#include "physfs.h"
int main(int argc, char **argv) {
PHYSFS_init(argv[0]);
char *base = PHYSFS_getBaseDir();
PHYSFS_setWriteDir(base);
PHYSFS_mount(base,NULL,0);
script_startup(); // runs engine.js
int argsize = 0;

View File

@@ -0,0 +1,2 @@
[wrap-redirect]
filename = qjs-chipmunk2d/subprojects/chipmunk.wrap