sync with FWK

main
Dominik Madarász 2023-10-07 19:34:09 +02:00
parent 4e871b7612
commit 5edc8228c7
41 changed files with 6236 additions and 2980 deletions

View File

@ -590,6 +590,17 @@ if "!cc!"=="cl" (
echo build=!build!, type=!dll!, cc=!cc!, other=!other!, args=!args!
echo import=!import!, export=!export!
rem set BUILD_VERSION symbol
git describe --tags --abbrev=0 > info.obj
set /p VERSION=<info.obj
git rev-list --count --first-parent HEAD > info.obj
set /p GIT_REVISION=<info.obj
git rev-parse --abbrev-ref HEAD > info.obj
set /p GIT_BRANCH=<info.obj
date /t > info.obj
set /p LAST_MODIFIED=<info.obj
set args=-DBUILD_VERSION="\"!GIT_BRANCH!-!GIT_REVISION!-!build!-!dll!\"" !args!
if "!cc!"=="tcc" set "cc=call tools\tcc"
rem detect wether user-defined sources use single-header distro
@ -657,16 +668,28 @@ for %%f in ("workbench\plugins\*.c") do (
rem demos
if "!demos!"=="yes" (
!echo! 00-ui && !cc! !o! 00-ui.exe demos\00-ui.c !import! !args! || set rc=1
!echo! 01-sprite && !cc! !o! 01-sprite.exe demos\01-sprite.c !import! !args! || set rc=1
!echo! 02-ddraw && !cc! !o! 02-ddraw.exe demos\02-ddraw.c !import! !args! || set rc=1
!echo! 02-frustum && !cc! !o! 02-frustum.exe demos\02-frustum.c !import! !args! || set rc=1
!echo! 03-anims && !cc! !o! 03-anims.exe demos\03-anims.c !import! !args! || set rc=1
!echo! 04-actor && !cc! !o! 04-actor.exe demos\04-actor.c !import! !args! || set rc=1
!echo! 04-lod && !cc! !o! 04-lod.exe demos\04-lod.c !import! !args! || set rc=1
!echo! 05-scene && !cc! !o! 05-scene.exe demos\05-scene.c !import! !args! || set rc=1
!echo! 06-controller && !cc! !o! 06-controller.exe demos\06-controller.c !import! !args! || set rc=1
!echo! 07-network && !cc! !o! 07-network.exe demos\07-network.c !import! !args! || set rc=1
!echo! 00-ui && !cc! !o! 00-ui.exe demos\00-ui.c !import! !args! || set rc=1
!echo! 00-loop && !cc! !o! 00-loop.exe demos\00-loop.c !import! !args! || set rc=1
!echo! 00-script && !cc! !o! 00-script.exe demos\00-script.c !import! !args! || set rc=1
!echo! 00-hello && !cc! !o! 00-hello.exe demos\00-hello.c !import! !args! || set rc=1
!echo! 01-sprite && !cc! !o! 01-sprite.exe demos\01-sprite.c !import! !args! || set rc=1
!echo! 01-demo2d && !cc! !o! 01-demo2d.exe demos\01-demo2d.c !import! !args! || set rc=1
!echo! 01-easing && !cc! !o! 01-easing.exe demos\01-easing.c !import! !args! || set rc=1
!echo! 01-font && !cc! !o! 01-font.exe demos\01-font.c !import! !args! || set rc=1
!echo! 02-ddraw && !cc! !o! 02-ddraw.exe demos\02-ddraw.c !import! !args! || set rc=1
!echo! 02-frustum && !cc! !o! 02-frustum.exe demos\02-frustum.c !import! !args! || set rc=1
!echo! 03-anims && !cc! !o! 03-anims.exe demos\03-anims.c !import! !args! || set rc=1
!echo! 04-actor && !cc! !o! 04-actor.exe demos\04-actor.c !import! !args! || set rc=1
!echo! 04-lod && !cc! !o! 04-lod.exe demos\04-lod.c !import! !args! || set rc=1
!echo! 05-scene && !cc! !o! 05-scene.exe demos\05-scene.c !import! !args! || set rc=1
!echo! 06-controller && !cc! !o! 06-controller.exe demos\06-controller.c !import! !args! || set rc=1
!echo! 06-material && !cc! !o! 06-material.exe demos\06-material.c !import! !args! || set rc=1
!echo! 07-network && !cc! !o! 07-network.exe demos\07-network.c !import! !args! || set rc=1
!echo! 07-netsync && !cc! !o! 07-netsync.exe demos\07-netsync.c !import! !args! || set rc=1
!echo! 08-audio && !cc! !o! 08-audio.exe demos\08-audio.c !import! !args! || set rc=1
!echo! 08-video && !cc! !o! 08-video.exe demos\08-video.c !import! !args! || set rc=1
!echo! 09-cubemap && !cc! !o! 09-cubemap.exe demos\09-cubemap.c !import! !args! || set rc=1
!echo! 09-shadertoy && !cc! !o! 09-shadertoy.exe demos\09-shadertoy.c !import! !args! || set rc=1
)
rem lab

View File

@ -128,10 +128,6 @@ int main() {
// ui
if( ui_panel("Audio", 0)) {
static float bgm = 1, sfx = 1, master = 1;
if( ui_slider2("BGM", &bgm, va("%.2f", bgm))) audio_volume_stream(bgm);
if( ui_slider2("SFX", &sfx, va("%.2f", sfx))) audio_volume_clip(sfx);
if( ui_slider2("Master", &master, va("%.2f", master))) audio_volume_master(master);
if( ui_label2_toolbar("BGM: Track #1", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = stream1, AUDIO_SINGLE_INSTANCE);
if( ui_label2_toolbar("BGM: Track #2", ICON_MD_VOLUME_UP)) audio_stop(BGM), audio_play(BGM = stream2, AUDIO_SINGLE_INSTANCE);
if( ui_label2_toolbar("SFX: Coin", ICON_MD_VOLUME_UP)) audio_play(clip1, 0);

871
demos/99-nodes.c 100644
View File

@ -0,0 +1,871 @@
#define V4K_IMPLEMENTATION
#include "joint/v4k.h"
/*
A basic node-based UI built with Nuklear.
Builds on the node editor example included in Nuklear v1.00, with the aim of
being used as a prototype for implementing a functioning node editor.
Features:
- Nodes of different types. Currently their implementations are #included in
the main file, but they could easily be turned into eg. a plugin system.
- Pins/pins of different types -- currently float values and colors.
- Adding and removing nodes.
- Linking nodes, with validation (one link per input, only link similar pins).
- Detaching and moving links.
- Evaluation of output values of connected nodes.
- Memory management based on fixed size arrays for links and node pointers
- Multiple node types
- Multiple pin types
- Linking between pins of the same type
- Detaching and reattaching links
- Getting value from linked node if pin is connected
Todo:
- Complete pin types.
- Allow dragging from output to input pin.
- Cut link by CTRL+clicking input pin.
- Cut link by drawing intersect line on a link.
- Group elemnts together with mouse, or LSHIFT+clicking.
- Drag groups.
- DEL elements.
- DEL groups.
- CTRL-C/CTRL-V/CTRL-X elements.
- CTRL-C/CTRL-V/CTRL-X groups.
- CTRL-Z,CTRL-Y.
- CTRL-N.
- CTRL-L,CTRL-S.
- CTRL-F.
- CTRL-Wheel Zooming.
- Allow to extend node types from Lua.
Extra todo:
- Execution Flow (see: nk_stroke_triangle, nk_fill_triangle)
- Complete missing nodes (see: nk_draw_image, nk_draw_text)
- Right-click could visualize node/board diagram as Lua script.
- Once that done, copy/pasting scripts should work within editor.
Sources:
- https://github.com/Immediate-Mode-UI/Nuklear/pull/561
- https://github.com/vurtun/nuklear/blob/master/demo/node_editor.c
*/
typedef enum pin_type_t {
type_flow,
type_int,type_float,
type_block,type_texture,type_image,
type_color,
/*
type_bool,
type_char, type_string,
type_int2, type_int3, type_int4,
type_float2, type_float3, type_float4,
type_array, type_map,
*/
type_total
} pin_type_t;
struct node_pin {
pin_type_t pin_type;
nk_bool is_connected;
struct node* connected_node;
int connected_pin;
};
struct node {
int ID;
char name[32];
struct nk_rect bounds;
int input_count;
int output_count;
struct node_pin *inputs;
struct node_pin *outputs;
struct {
float in_padding_x;
float in_padding_y;
float in_spacing_y;
float out_padding_x;
float out_padding_y;
float out_spacing_y;
} pin_spacing; /* Maybe this should be called "node_layout" and include the bounds? */
struct node *next; /* Z ordering only */
struct node *prev; /* Z ordering only */
void* (*eval_func)(struct node*, int oIndex);
void (*display_func)(struct nk_context*, struct node*);
};
struct node_link {
struct node* input_node;
int input_pin;
struct node* output_node;
int output_pin;
nk_bool is_active;
};
struct node_linking {
int active;
struct node *node;
int input_id;
int input_pin;
};
struct node_editor {
int initialized;
struct node *node_buf[32];
struct node_link links[64];
struct node *output_node;
struct node *begin;
struct node *end;
int node_count;
int link_count;
struct nk_rect bounds;
struct node *selected;
int show_grid;
struct nk_vec2 scrolling;
struct node_linking linking;
};
/* === PROTOTYPES === */
/* The node implementations need these two functions. */
/* These could/should go in a header file along with the node and node_pin structs and be #included in the node implementations */
struct node* node_editor_add(struct node_editor *editor, size_t nodeSize, const char *name, struct nk_rect bounds, int in_count, int out_count);
void* node_editor_eval_connected(struct node *node, int input_pin_number);
/* ================== */
/* === NODE TYPE IMPLEMENTATIONS === */
#define NODE_DEFAULT_ROW_HEIGHT 25
// ----------------------------------------------------------------------------------------------------
// #include "node_output.h"
struct node_type_output {
struct node node;
struct nk_colorf input_val;
};
struct nk_colorf *node_output_get(struct node* node) {
struct node_type_output *output_node = (struct node_type_output*)node;
if (!node->inputs[0].is_connected) {
struct nk_colorf black = {0.0f, 0.0f, 0.0f, 0.0f};
output_node->input_val = black;
}
return &output_node->input_val;
}
static void node_output_display(struct nk_context *ctx, struct node *node) {
if (node->inputs[0].is_connected) {
struct node_type_output *output_node = (struct node_type_output*)node;
output_node->input_val = *(struct nk_colorf*)node_editor_eval_connected(node, 0);
nk_layout_row_dynamic(ctx, 60, 1);
nk_button_color(ctx, nk_rgba_cf(output_node->input_val));
}
}
struct node* node_output_create(struct node_editor *editor, struct nk_vec2 position) {
struct node_type_output *output_node = (struct node_type_output*)node_editor_add(editor, sizeof(struct node_type_output), "Output", nk_rect(position.x, position.y, 100, 100), 1, 0);
if (output_node){
output_node->node.inputs[0].pin_type = type_color;
output_node->node.display_func = node_output_display;
}
return (struct node*)output_node;
}
// ----------------------------------------------------------------------------------------------------
// #include "node_float.h"
struct node_type_float {
struct node node;
float output_val;
};
static float *node_float_eval(struct node* node, int oIndex) {
struct node_type_float *float_node = (struct node_type_float*)node;
NK_ASSERT(oIndex == 0);
return &float_node->output_val;
}
static void node_float_draw(struct nk_context *ctx, struct node *node) {
struct node_type_float *float_node = (struct node_type_float*)node;
nk_layout_row_dynamic(ctx, NODE_DEFAULT_ROW_HEIGHT, 1);
float_node->output_val = nk_propertyf(ctx, "#Value:", 0.0f, float_node->output_val, 1.0f, 0.01f, 0.01f);
}
void node_float_create(struct node_editor *editor, struct nk_vec2 position) {
struct node_type_float *float_node = (struct node_type_float*)node_editor_add(editor, sizeof(struct node_type_float), "Float", nk_rect(position.x, position.y, 180, 75), 0, 1);
if (float_node)
{
float_node->output_val = 1.0f;
float_node->node.display_func = node_float_draw;
float_node->node.eval_func = (void*(*)(struct node*, int)) node_float_eval;
}
}
// ----------------------------------------------------------------------------------------------------
// #include "node_color.h"
struct node_type_color {
struct node node;
float input_val[4];
struct nk_colorf output_val;
};
static struct nk_colorf *node_color_eval(struct node* node, int oIndex)
{
struct node_type_color *color_node = (struct node_type_color*)node;
NK_ASSERT(oIndex == 0); /* only one output connector */
return &color_node->output_val;
}
static void node_color_draw(struct nk_context *ctx, struct node *node)
{
struct node_type_color *color_node = (struct node_type_color*)node;
float eval_result; /* Get the values from connected nodes into this so the inputs revert on disconnect */
const char* labels[4] = {"#R:","#G:","#B:","#A:"};
float color_val[4]; /* Because we can't just loop through the struct... */
nk_layout_row_dynamic(ctx, NODE_DEFAULT_ROW_HEIGHT, 1);
nk_button_color(ctx, nk_rgba_cf(color_node->output_val));
for (int i = 0; i < 4; i++)
{
if (color_node->node.inputs[i].is_connected) {
eval_result = *(float*)node_editor_eval_connected(node, i);
eval_result = nk_propertyf(ctx, labels[i], eval_result, eval_result, eval_result, 0.01f, 0.01f);
color_val[i] = eval_result;
}
else {
color_node->input_val[i] = nk_propertyf(ctx, labels[i], 0.0f, color_node->input_val[i], 1.0f, 0.01f, 0.01f);
color_val[i] = color_node->input_val[i];
}
}
color_node->output_val.r = color_val[0];
color_node->output_val.g = color_val[1];
color_node->output_val.b = color_val[2];
color_node->output_val.a = color_val[3];
}
void node_color_create(struct node_editor *editor, struct nk_vec2 position)
{
struct node_type_color *color_node = (struct node_type_color*)node_editor_add(editor, sizeof(struct node_type_color), "Color", nk_rect(position.x, position.y, 180, 190), 4, 1);
if (color_node)
{
const struct nk_colorf black = {0.0f, 0.0f, 0.0f, 1.0f};
for (int i = 0; i < color_node->node.input_count; i++)
color_node->node.inputs[i].pin_type = type_float;
color_node->node.outputs[0].pin_type = type_color;
color_node->node.pin_spacing.in_padding_y += NODE_DEFAULT_ROW_HEIGHT;
color_node->input_val[0] = 0.0f;
color_node->input_val[1] = 0.0f;
color_node->input_val[2] = 0.0f;
color_node->input_val[3] = 1.0f;
color_node->output_val = black;
color_node->node.display_func = node_color_draw;
color_node->node.eval_func = (void*(*)(struct node*, int)) node_color_eval;
}
}
// ----------------------------------------------------------------------------------------------------
// #include "node_blend.h"
struct node_type_blend {
struct node node;
struct nk_colorf input_val[2];
struct nk_colorf output_val;
float blend_val;
};
static struct nk_colorf *node_blend_eval(struct node *node, int oIndex) {
struct node_type_blend* blend_node = (struct node_type_blend*)node;
return &blend_node->output_val;
}
static void node_blend_display(struct nk_context *ctx, struct node *node) {
struct node_type_blend *blend_node = (struct node_type_blend*)node;
const struct nk_colorf blank = {0.0f, 0.0f, 0.0f, 0.0f};
float blend_amnt;
nk_layout_row_dynamic(ctx, NODE_DEFAULT_ROW_HEIGHT, 1);
for (int i = 0; i < 2; i++){
if(node->inputs[i].is_connected) {
blend_node->input_val[i] = *(struct nk_colorf*)node_editor_eval_connected(node, i);
}
else {
blend_node->input_val[i] = blank;
}
nk_button_color(ctx, nk_rgba_cf(blend_node->input_val[i]));
}
if (node->inputs[2].is_connected) {
blend_amnt = *(float*)node_editor_eval_connected(node, 2);
blend_amnt = nk_propertyf(ctx, "#Blend", blend_amnt, blend_amnt, blend_amnt, 0.01f, 0.01f);
}
else {
blend_node->blend_val = nk_propertyf(ctx, "#Blend", 0.0f, blend_node->blend_val, 1.0f, 0.01f, 0.01f);
blend_amnt = blend_node->blend_val;
}
if(node->inputs[0].is_connected && node->inputs[1].is_connected) {
blend_node->output_val.r = blend_node->input_val[0].r * (1.0f-blend_amnt) + blend_node->input_val[1].r * blend_amnt;
blend_node->output_val.g = blend_node->input_val[0].g * (1.0f-blend_amnt) + blend_node->input_val[1].g * blend_amnt;
blend_node->output_val.b = blend_node->input_val[0].b * (1.0f-blend_amnt) + blend_node->input_val[1].b * blend_amnt;
blend_node->output_val.a = blend_node->input_val[0].a * (1.0f-blend_amnt) + blend_node->input_val[1].a * blend_amnt;
}
else {
blend_node->output_val = blank;
}
}
void node_blend_create(struct node_editor *editor, struct nk_vec2 position) {
struct node_type_blend* blend_node = (struct node_type_blend*)node_editor_add(editor, sizeof(struct node_type_blend), "Blend", nk_rect(position.x, position.y, 180, 130), 3, 1);
if (blend_node) {
const struct nk_colorf blank = {0.0f, 0.0f, 0.0f, 0.0f};
for (int i = 0; i < (int)NK_LEN(blend_node->input_val); i++)
blend_node->node.inputs[i].pin_type = type_color;
blend_node->node.outputs[0].pin_type = type_color;
// blend_node->node.pin_spacing.in_padding_y = 42.0f;
// blend_node->node.pin_spacing.in_spacing_y = 29.0f;
for (int i = 0; i < (int)NK_LEN(blend_node->input_val); i++)
blend_node->input_val[i] = blank;
blend_node->output_val = blank;
blend_node->blend_val = 0.5f;
blend_node->node.display_func = node_blend_display;
blend_node->node.eval_func = (void*(*)(struct node*, int)) node_blend_eval;
}
}
/* ================================= */
#define NK_RGB3(r,g,b) {r,g,b,255}
#define BG_COLOR ((struct nk_color){0,0,0,80}) // nk_rgba(0,0,0,192)
static
struct editor_node_style {
int pin_type;
const char *shape;
struct nk_color color_idle;
struct nk_color color_hover;
} styles[] = {
// order matters:
{ type_flow, "triangle_right", NK_RGB3(200,200,200), NK_RGB3(255,255,255) }, // if .num_links == 0
{ type_int, "circle", NK_RGB3(33,227,175), NK_RGB3(135,239,195) },
{ type_float, "circle", NK_RGB3(156,253,65), NK_RGB3(144,225,137) },
{ type_block, "circle", NK_RGB3(6,165,239), NK_RGB3(137,196,247) },
{ type_texture, "circle", NK_RGB3(148,0,0), NK_RGB3(183,137,137) },
{ type_image, "circle", NK_RGB3(200,130,255), NK_RGB3(220,170,255) },
{ type_color, "circle", NK_RGB3(252,200,35), NK_RGB3(255,217,140) },
};
#define COLOR_FLOW_HI styles[type_flow].color_hover
#define COLOR_FLOW_LO styles[type_flow].color_idle
#define GRID_SIZE 32.0f
#define GRID_COLOR ((struct nk_color)NK_RGB3(60,60,80))
#define GRID_THICKNESS 1.0f
#define LINK_THICKNESS 1.0f
#define LINK_DRAW(POINT_A,POINT_B,COLOR) do { \
vec2 a = (POINT_A); \
vec2 b = (POINT_B); \
nk_stroke_line(canvas, a.x, a.y, b.x, b.y, LINK_THICKNESS, COLOR); \
} while(0)
#undef LINK_DRAW
#define LINK_DRAW(POINT_A,POINT_B,COLOR) do { \
vec2 a = (POINT_A); \
vec2 b = (POINT_B); \
nk_stroke_curve(canvas, a.x, a.y, a.x+50, a.y, b.x-50, b.y, b.x, b.y, LINK_THICKNESS, COLOR); \
} while(0)
#undef LINK_DRAW
#define LINK_DRAW(POINT_A,POINT_B,COLOR) do { \
vec2 a = (POINT_A); \
vec2 b = (POINT_B); \
float dist2 = len2( sub2( ptr2(&b.x), ptr2(&a.x) ) ); \
vec2 mid_a = mix2( ptr2(&a.x), ptr2(&b.x), 0.25 ); mid_a.y += dist2/2; \
vec2 mid_b = mix2( ptr2(&a.x), ptr2(&b.x), 0.75 ); mid_b.y += dist2/3; \
nk_stroke_curve(canvas, a.x, a.y, mid_a.x, mid_a.y, mid_b.x, mid_b.y, b.x, b.y, LINK_THICKNESS, COLOR); \
} while(0)
#define PIN_RADIUS 12
#define PIN_THICKNESS 1.0f
#define PIN_DRAW(PIN_ADDR,POINT,RADIUS) do { \
circle.x = (POINT).x - (RADIUS) / 2; \
circle.y = (POINT).y - (RADIUS) / 2; \
circle.w = circle.h = (RADIUS); \
struct nk_color color = node_get_type_color((PIN_ADDR).pin_type); \
if((PIN_ADDR).is_connected) \
nk_fill_circle(canvas, circle, color); \
else \
nk_stroke_circle(canvas, circle, PIN_THICKNESS, color); \
} while(0)
static struct nk_color node_get_type_color(unsigned pin_type) {
for( int i = 0; i < type_total; ++i )
if( styles[i].pin_type == pin_type )
return styles[i].color_idle;
return ((struct nk_color)NK_RGB3(255,0,255));
}
static void node_editor_push(struct node_editor *editor, struct node *node) {
if (!editor->begin) {
node->next = NULL;
node->prev = NULL;
editor->begin = node;
editor->end = node;
} else {
node->prev = editor->end;
if (editor->end)
editor->end->next = node;
node->next = NULL;
editor->end = node;
}
}
static void node_editor_pop(struct node_editor *editor, struct node *node) {
if (node->next)
node->next->prev = node->prev;
if (node->prev)
node->prev->next = node->next;
if (editor->end == node)
editor->end = node->prev;
if (editor->begin == node)
editor->begin = node->next;
node->next = NULL;
node->prev = NULL;
}
static struct node* node_editor_find_by_id(struct node_editor *editor, int ID) {
struct node *iter = editor->begin;
while (iter) {
if (iter->ID == ID)
return iter;
iter = iter->next;
}
return NULL;
}
static struct node_link* node_editor_find_link_by_output(struct node_editor *editor, struct node *output_node, int node_input_connector) {
for( int i = 0; i < editor->link_count; i++ ) {
if (editor->links[i].output_node == output_node &&
editor->links[i].output_pin == node_input_connector &&
editor->links[i].is_active == nk_true) {
return &editor->links[i];
}
}
return NULL;
}
static struct node_link* node_editor_find_link_by_input(struct node_editor *editor, struct node *input_node, int node_output_connector) {
for( int i = 0; i < editor->link_count; i++ ) {
if (editor->links[i].input_node == input_node &&
editor->links[i].input_pin == node_output_connector &&
editor->links[i].is_active == nk_true) {
return &editor->links[i];
}
}
return NULL;
}
static void node_editor_delete_link(struct node_link *link) {
link->is_active = nk_false;
link->input_node->outputs[link->input_pin].is_connected = nk_false;
link->output_node->inputs[link->output_pin].is_connected = nk_false;
}
struct node* node_editor_add(struct node_editor *editor, size_t nodeSize, const char *name, struct nk_rect bounds, int in_count, int out_count) {
static int IDs = 0;
struct node *node = NULL;
if ((nk_size)editor->node_count < NK_LEN(editor->node_buf)) {
/* node_buf has unused pins */
node = MALLOC(nodeSize);
editor->node_buf[editor->node_count++] = node;
node->ID = IDs++;
} else {
/* check for freed up pins in node_buf */
for (int i = 0; i < editor->node_count; i++) {
if (editor->node_buf[i] == NULL) {
node = MALLOC(nodeSize);
editor->node_buf[i] = node;
node->ID = i;
break;
}
}
}
if (node == NULL) {
puts("Node creation failed");
return NULL;
}
node->bounds = bounds;
node->input_count = in_count;
node->output_count = out_count;
node->inputs = MALLOC(node->input_count * sizeof(struct node_pin));
node->outputs = MALLOC(node->output_count * sizeof(struct node_pin));
for (int i = 0; i < node->input_count; i++) {
node->inputs[i].is_connected = nk_false;
node->inputs[i].pin_type = type_float; /* default pin type */
}
for (int i = 0; i < node->output_count; i++) {
node->outputs[i].is_connected = nk_false;
node->outputs[i].pin_type = type_float; /* default pin type */
}
/* default pin spacing */
node->pin_spacing.in_padding_x = 2;
node->pin_spacing.in_padding_y = 32 + 25/2 + 6; // titlebar height + next half row + adjust
node->pin_spacing.in_spacing_y = 25; // row height+border
node->pin_spacing.out_padding_x = 3;
node->pin_spacing.out_padding_y = 32 + 25/2 + 6; // titlebar height + next half row + adjust
node->pin_spacing.out_spacing_y = 25; // row height+border
strcpy(node->name, name);
node_editor_push(editor, node);
return node;
}
void *node_editor_eval_connected(struct node* node, int input_pin_number) {
NK_ASSERT(node->inputs[input_pin_number].is_connected);
return node->inputs[input_pin_number].connected_node->eval_func(node->inputs[input_pin_number].connected_node, node->inputs[input_pin_number].connected_pin);
}
static void node_editor_link(struct node_editor *editor, struct node *in_node, int in_pin, struct node *out_node, int out_pin) {
/* Confusingly, in and out nodes/pins here refer to the inputs and outputs OF THE LINK ITSELF, not the nodes */
struct node_link *link = NULL;
if ((nk_size)editor->link_count < NK_LEN(editor->links)) {
link = &editor->links[editor->link_count++];
} else {
for (int i = 0; i < (int)NK_LEN(editor->links); i++)
{
if (editor->links[i].is_active == nk_false) {
link = &editor->links[i];
break;
}
}
}
if (link) {
out_node->inputs[out_pin].is_connected = nk_true;
in_node->outputs[in_pin].is_connected = nk_true;
out_node->inputs[out_pin].connected_node = in_node;
out_node->inputs[out_pin].connected_pin = in_pin;
link->input_node = in_node;
link->input_pin = in_pin;
link->output_node = out_node;
link->output_pin = out_pin;
link->is_active = nk_true;
} else {
puts("Too many links");
}
}
static void node_editor_init(struct node_editor *editor) {
if (editor->initialized) return;
struct nk_rect total_space = nk_window_get_content_region(ui_ctx);
struct nk_vec2 output_node_position = { total_space.w*2/3, total_space.h/3 };
struct nk_vec2 color_node_position = { total_space.w*1/4, total_space.h/3 };
memset(editor, 0, sizeof(*editor));
editor->output_node = node_output_create(editor, output_node_position);
node_color_create(editor, color_node_position);
editor->show_grid = nk_true;
editor->initialized = 1;
}
static int node_editor(struct node_editor *editor) {
int n = 0;
struct nk_rect total_space;
const struct nk_input *in = &ui_ctx->input;
struct nk_command_buffer *canvas = nk_window_get_canvas(ui_ctx);
struct node *updated = 0;
node_editor_init(editor);
{
/* allocate complete window space */
total_space = nk_window_get_content_region(ui_ctx);
nk_layout_space_begin(ui_ctx, NK_STATIC, total_space.h, editor->node_count);
{
struct node *it = editor->begin;
struct nk_rect size = nk_layout_space_bounds(ui_ctx);
struct nk_panel *nodePanel = 0;
if (editor->show_grid) {
/* display grid */
for (float x = (float)fmod(size.x - editor->scrolling.x, GRID_SIZE); x < size.w; x += GRID_SIZE)
nk_stroke_line(canvas, x+size.x, size.y, x+size.x, size.y+size.h, GRID_THICKNESS, GRID_COLOR);
for (float y = (float)fmod(size.y - editor->scrolling.y, GRID_SIZE); y < size.h; y += GRID_SIZE)
nk_stroke_line(canvas, size.x, y+size.y, size.x+size.w, y+size.y, GRID_THICKNESS, GRID_COLOR);
}
/* execute each node as a movable group */
/* loop through nodes */
while (it) {
/* Output node window should not have a close button */
nk_flags nodePanel_flags = NK_WINDOW_MOVABLE|NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER|NK_WINDOW_TITLE;
if (it != editor->output_node)
nodePanel_flags |= NK_WINDOW_CLOSABLE;
/* calculate scrolled node window position and size */
nk_layout_space_push(ui_ctx, nk_rect(it->bounds.x - editor->scrolling.x,
it->bounds.y - editor->scrolling.y, it->bounds.w, it->bounds.h));
/* execute node window */
char *name = va(" " ICON_MD_MENU " %s",it->name); //< @r-lyeh added some spacing+icon because of our UI customizations
struct nk_color bak = ui_ctx->style.window.fixed_background.data.color;
ui_ctx->style.window.fixed_background.data.color = BG_COLOR;
if (nk_group_begin(ui_ctx, name, nodePanel_flags))
{
/* always have last selected node on top */
nodePanel = nk_window_get_panel(ui_ctx);
if (nk_input_mouse_clicked(in, NK_BUTTON_LEFT, nodePanel->bounds) &&
(!(it->prev && nk_input_mouse_clicked(in, NK_BUTTON_LEFT,
nk_layout_space_rect_to_screen(ui_ctx, nodePanel->bounds)))) &&
editor->end != it)
{
updated = it;
}
if ((nodePanel->flags & NK_WINDOW_HIDDEN)) /* Node close button has been clicked */
{
/* Delete node */
struct node_link *link_remove;
node_editor_pop(editor, it);
for (int n = 0; n < it->input_count; n++) {
if ((link_remove = node_editor_find_link_by_output(editor, it, n)))
{
node_editor_delete_link(link_remove);
}
}
for (int n = 0; n < it -> output_count; n++) {
while((link_remove = node_editor_find_link_by_input(editor, it, n)))
{
node_editor_delete_link(link_remove);
}
}
NK_ASSERT(editor->node_buf[it->ID] == it);
editor->node_buf[it->ID] = NULL;
FREE(it->inputs);
FREE(it->outputs);
FREE(it);
}
else {
/* ================= NODE CONTENT ===================== */
it->display_func(ui_ctx, it);
/* ==================================================== */
}
nk_group_end(ui_ctx);
}
ui_ctx->style.window.fixed_background.data.color = bak;
if (!(nodePanel->flags & NK_WINDOW_HIDDEN))
{
/* node pin and linking */
struct nk_rect bounds;
bounds = nk_layout_space_rect_to_local(ui_ctx, nodePanel->bounds);
bounds.x += editor->scrolling.x;
bounds.y += editor->scrolling.y;
it->bounds = bounds;
/* output pins */
for (int n = 0; n < it->output_count; ++n) {
struct nk_rect circle;
struct nk_vec2 pt = {nodePanel->bounds.x, nodePanel->bounds.y};
pt.x += nodePanel->bounds.w - PIN_RADIUS / 2 + it->pin_spacing.out_padding_x;
pt.y += it->pin_spacing.out_padding_y + it->pin_spacing.out_spacing_y * (n);
PIN_DRAW(it->outputs[n],pt,PIN_RADIUS);
/* start linking process */
/* set linking active */
if (nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, circle, nk_true)) {
editor->linking.active = nk_true;
editor->linking.node = it;
editor->linking.input_id = it->ID;
editor->linking.input_pin = n;
}
/* draw link being dragged (from linked pin to mouse position) */
if (editor->linking.active && editor->linking.node == it &&
editor->linking.input_pin == n) {
LINK_DRAW(vec2(circle.x+3,circle.y+3),ptr2(&in->mouse.pos.x),COLOR_FLOW_HI);
}
}
/* input pins */
for (int n = 0; n < it->input_count; ++n) {
struct nk_rect circle;
struct nk_vec2 pt = {nodePanel->bounds.x, nodePanel->bounds.y};
pt.x += it->pin_spacing.in_padding_x;
pt.y += it->pin_spacing.in_padding_y + it->pin_spacing.in_spacing_y * (n);
PIN_DRAW(it->inputs[n],pt,PIN_RADIUS);
/* Detach link */
if (nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, circle, nk_true) &&
editor->linking.active == nk_false &&
it->inputs[n].is_connected == nk_true) {
struct node_link *node_relink = node_editor_find_link_by_output(editor, it, n);
editor->linking.active = nk_true;
editor->linking.node = node_relink->input_node;
editor->linking.input_id = node_relink->input_node->ID;
editor->linking.input_pin = node_relink->input_pin;
node_editor_delete_link(node_relink);
}
/* Create link */
if (nk_input_is_mouse_released(in, NK_BUTTON_LEFT) &&
nk_input_is_mouse_hovering_rect(in, circle) &&
editor->linking.active &&
editor->linking.node != it &&
it->inputs[n].pin_type == editor->linking.node->outputs[editor->linking.input_pin].pin_type &&
it->inputs[n].is_connected != nk_true) {
editor->linking.active = nk_false;
node_editor_link(editor, editor->linking.node,
editor->linking.input_pin, it, n);
}
}
}
it = it->next;
}
/* reset (output) linking connection */
if (editor->linking.active && (!!input(KEY_LCTRL) || !!input(KEY_RCTRL) || nk_input_is_mouse_released(in, NK_BUTTON_LEFT))) {
editor->linking.active = nk_false;
editor->linking.node = NULL;
}
/* draw each static link */
for (int n = 0; n < editor->link_count; ++n) {
struct node_link *link = &editor->links[n];
if (link->is_active == nk_true){
struct node *ni = link->input_node;
struct node *no = link->output_node;
struct nk_vec2 l0 = nk_layout_space_to_screen(ui_ctx, nk_vec2(ni->bounds.x + ni->bounds.w + ni->pin_spacing.out_padding_x, 3.0f + ni->bounds.y + ni->pin_spacing.out_padding_y + ni->pin_spacing.out_spacing_y * (link->input_pin)));
struct nk_vec2 l1 = nk_layout_space_to_screen(ui_ctx, nk_vec2(no->bounds.x + no->pin_spacing.in_padding_x, 3.0f + no->bounds.y + no->pin_spacing.in_padding_y + no->pin_spacing.in_spacing_y * (link->output_pin)));
l0.x -= editor->scrolling.x;
l0.y -= editor->scrolling.y;
l1.x -= editor->scrolling.x;
l1.y -= editor->scrolling.y;
struct nk_color color = node_get_type_color(no->inputs[link->output_pin].pin_type);
LINK_DRAW(ptr2(&l0.x), ptr2(&l1.x), color);
}
}
if (updated) {
/* reshuffle nodes to have least recently selected node on top */
node_editor_pop(editor, updated);
node_editor_push(editor, updated);
}
/* node selection */
if (nk_input_mouse_clicked(in, NK_BUTTON_LEFT, nk_layout_space_bounds(ui_ctx))) {
it = editor->begin;
editor->selected = NULL;
editor->bounds = nk_rect(in->mouse.pos.x, in->mouse.pos.y, 100, 200);
while (it) {
struct nk_rect b = nk_layout_space_rect_to_screen(ui_ctx, it->bounds);
b.x -= editor->scrolling.x;
b.y -= editor->scrolling.y;
if (nk_input_is_mouse_hovering_rect(in, b))
editor->selected = it;
it = it->next;
}
}
/* contextual menu */
if (nk_contextual_begin(ui_ctx, 0, nk_vec2(150, 220), nk_window_get_bounds(ui_ctx))) {
struct nk_vec2 wincoords = { in->mouse.pos.x-total_space.x-50, in->mouse.pos.y-total_space.y-32 };
#if 1
static char *filter = 0;
static int do_filter = 0;
if( input_down(KEY_F) ) if( input(KEY_LCTRL) || input(KEY_RCTRL) ) do_filter ^= 1;
int choice = ui_toolbar(ICON_MD_SEARCH ";");
if( choice == 1 ) do_filter = 1;
if( do_filter ) {
ui_string(ICON_MD_CLOSE " Filter " ICON_MD_SEARCH, &filter);
if( ui_label_icon_clicked_L.x > 0 && ui_label_icon_clicked_L.x <= 24 ) { // if clicked on CANCEL icon (1st icon)
do_filter = 0;
}
} else {
if( filter ) filter[0] = '\0';
}
char *filter_mask = filter && filter[0] ? va("*%s*", filter) : "*";
#endif
#define ui_label_filtered(lbl) (strmatchi(lbl,filter_mask) && ui_label(lbl))
int close = 0;
if (ui_label_filtered("=Add Color node")) close=1,node_color_create(editor, wincoords);
if (ui_label_filtered("=Add Float node")) close=1,node_float_create(editor, wincoords);
if (ui_label_filtered("=Add Blend Node")) close=1,node_blend_create(editor, wincoords);
if (ui_label_filtered(editor->show_grid ? "=Hide Grid" : "=Show Grid"))
close=1,editor->show_grid = !editor->show_grid;
if(close) do_filter = 0, (filter ? filter[0] = '\0' : '\0'), nk_contextual_close(ui_ctx);
nk_contextual_end(ui_ctx);
}
}
nk_layout_space_end(ui_ctx);
/* window content scrolling */
if (nk_input_is_mouse_hovering_rect(in, nk_window_get_bounds(ui_ctx)) &&
nk_input_is_mouse_down(in, NK_BUTTON_MIDDLE)) {
editor->scrolling.x += in->mouse.delta.x;
editor->scrolling.y += in->mouse.delta.y;
}
}
return !nk_window_is_closed(ui_ctx, "NodeEdit");
}
int main() {
static int open = 1;
window_create(0.75, 0);
while(window_swap() && open) {
if( ui_window("Node editor",&open) ) {
static struct node_editor nodeEditor = {0};
node_editor(&nodeEditor);
ui_window_end();
}
}
}

View File

@ -0,0 +1,78 @@
// fitting polynomials to matplotlib colormaps by mattz
//
// License CC0 (public domain)
// https://creativecommons.org/share-your-work/public-domain/cc0/
//
// feel free to use these in your own work!
//
// similar to https://www.shadertoy.com/view/XtGGzG but with a couple small differences:
//
// - use degree 6 instead of degree 5 polynomials
// - use nested horner representation for polynomials
// - polynomials were fitted to minimize maximum error (as opposed to least squares)
//
// data fitted from https://github.com/BIDS/colormap/blob/master/colormaps.py
// (which is licensed CC0)
uniform int use_colormap; /// set:2 min:0 max:4 tip:"0:off,1:inferno,2:viridis,3:plasma,4:magma"
vec3 inferno_color_map(float t) {
const vec3 c0 = vec3(0.0002189403691192265, 0.001651004631001012, -0.01948089843709184);
const vec3 c1 = vec3(0.1065134194856116, 0.5639564367884091, 3.932712388889277);
const vec3 c2 = vec3(11.60249308247187, -3.972853965665698, -15.9423941062914);
const vec3 c3 = vec3(-41.70399613139459, 17.43639888205313, 44.35414519872813);
const vec3 c4 = vec3(77.162935699427, -33.40235894210092, -81.80730925738993);
const vec3 c5 = vec3(-71.31942824499214, 32.62606426397723, 73.20951985803202);
const vec3 c6 = vec3(25.13112622477341, -12.24266895238567, -23.07032500287172);
return c0+t*(c1+t*(c2+t*(c3+t*(c4+t*(c5+t*c6)))));
}
vec3 viridis_color_map(float t) {
const vec3 c0 = vec3(0.2777273272234177, 0.005407344544966578, 0.3340998053353061);
const vec3 c1 = vec3(0.1050930431085774, 1.404613529898575, 1.384590162594685);
const vec3 c2 = vec3(-0.3308618287255563, 0.214847559468213, 0.09509516302823659);
const vec3 c3 = vec3(-4.634230498983486, -5.799100973351585, -19.33244095627987);
const vec3 c4 = vec3(6.228269936347081, 14.17993336680509, 56.69055260068105);
const vec3 c5 = vec3(4.776384997670288, -13.74514537774601, -65.35303263337234);
const vec3 c6 = vec3(-5.435455855934631, 4.645852612178535, 26.3124352495832);
return c0+t*(c1+t*(c2+t*(c3+t*(c4+t*(c5+t*c6)))));
}
vec3 plasma_color_map(float t) {
const vec3 c0 = vec3(0.05873234392399702, 0.02333670892565664, 0.5433401826748754);
const vec3 c1 = vec3(2.176514634195958, 0.2383834171260182, 0.7539604599784036);
const vec3 c2 = vec3(-2.689460476458034, -7.455851135738909, 3.110799939717086);
const vec3 c3 = vec3(6.130348345893603, 42.3461881477227, -28.51885465332158);
const vec3 c4 = vec3(-11.10743619062271, -82.66631109428045, 60.13984767418263);
const vec3 c5 = vec3(10.02306557647065, 71.41361770095349, -54.07218655560067);
const vec3 c6 = vec3(-3.658713842777788, -22.93153465461149, 18.19190778539828);
return c0+t*(c1+t*(c2+t*(c3+t*(c4+t*(c5+t*c6)))));
}
vec3 magma_color_map(float t) {
const vec3 c0 = vec3(-0.002136485053939582, -0.000749655052795221, -0.005386127855323933);
const vec3 c1 = vec3(0.2516605407371642, 0.6775232436837668, 2.494026599312351);
const vec3 c2 = vec3(8.353717279216625, -3.577719514958484, 0.3144679030132573);
const vec3 c3 = vec3(-27.66873308576866, 14.26473078096533, -13.64921318813922);
const vec3 c4 = vec3(52.17613981234068, -27.94360607168351, 12.94416944238394);
const vec3 c5 = vec3(-50.76852536473588, 29.04658282127291, 4.23415299384598);
const vec3 c6 = vec3(18.65570506591883, -11.48977351997711, -5.601961508734096);
return c0+t*(c1+t*(c2+t*(c3+t*(c4+t*(c5+t*c6)))));
}
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
vec2 uv = fragCoord.xy / iResolution.xy;
vec4 src = texture(iChannel0, uv);
if(use_colormap == 0) { fragColor = src; return; }
float luma = dot(src.rgb, vec3(0.299, 0.587, 0.114));
if(use_colormap == 1) { fragColor = vec4(inferno_color_map(luma), src.a); return; }
if(use_colormap == 2) { fragColor = vec4(viridis_color_map(luma), src.a); return; }
if(use_colormap == 3) { fragColor = vec4(plasma_color_map(luma), src.a); return; }
if(use_colormap == 4) { fragColor = vec4(magma_color_map(luma), src.a); return; }
}

Binary file not shown.

View File

@ -939,6 +939,12 @@ ffi.cdef([[
//lcpp INF [0000] vec3: macro name but used as C declaration in: bool unproject44(vec3 *out, vec3 xyd, vec4 viewport, mat44 mvp);
//lcpp INF [0000] vec3: macro name but used as C declaration in: bool unproject44(vec3 *out, vec3 xyd, vec4 viewport, mat44 mvp);
//lcpp INF [0000] vec4: macro name but used as C declaration in: bool unproject44(vec3 *out, vec3 xyd, vec4 viewport, mat44 mvp);
//lcpp INF [0000] vec2i: macro name but used as C declaration in:API void print2i( vec2i v );
//lcpp INF [0000] vec2i: macro name but used as C declaration in:STATIC void print2i( vec2i v );
//lcpp INF [0000] vec2i: macro name but used as C declaration in: void print2i( vec2i v );
//lcpp INF [0000] vec3i: macro name but used as C declaration in:API void print3i( vec3i v );
//lcpp INF [0000] vec3i: macro name but used as C declaration in:STATIC void print3i( vec3i v );
//lcpp INF [0000] vec3i: macro name but used as C declaration in: void print3i( vec3i v );
//lcpp INF [0000] vec2: macro name but used as C declaration in:API void print2( vec2 v );
//lcpp INF [0000] vec2: macro name but used as C declaration in:STATIC void print2( vec2 v );
//lcpp INF [0000] vec2: macro name but used as C declaration in: void print2( vec2 v );
@ -1215,12 +1221,6 @@ ffi.cdef([[
//lcpp INF [0000] vec2: macro name but used as C declaration in:API vec2 input_touch_delta_from_origin(unsigned button, float sensitivity);
//lcpp INF [0000] vec2: macro name but used as C declaration in:STATIC vec2 input_touch_delta_from_origin(unsigned button, float sensitivity);
//lcpp INF [0000] vec2: macro name but used as C declaration in: vec2 input_touch_delta_from_origin(unsigned button, float sensitivity);
//lcpp INF [0000] ctor: macro name but used as C declaration in:API extern void (*ctor[256])();
//lcpp INF [0000] ctor: macro name but used as C declaration in:STATIC extern void (*ctor[256])();
//lcpp INF [0000] ctor: macro name but used as C declaration in: extern void (*ctor[256])();
//lcpp INF [0000] dtor: macro name but used as C declaration in:API extern void (*dtor[256])();
//lcpp INF [0000] dtor: macro name but used as C declaration in:STATIC extern void (*dtor[256])();
//lcpp INF [0000] dtor: macro name but used as C declaration in: extern void (*dtor[256])();
//lcpp INF [0000] map: macro name but used as C declaration in:typedef struct { map base; struct { pair p; char * key; struct profile_t val; } tmp, *ptr; struct profile_t* tmpval; int (*typed_cmp)(char *, char *); uint64_t (*typed_hash)(char *); } * profiler_t;
//lcpp INF [0000] vec4: macro name but used as C declaration in:vec4 color;
//lcpp INF [0000] vec4: macro name but used as C declaration in:API void sprite_rect( texture_t t, vec4 rect, float zindex, vec3 pos, float tilt_deg, unsigned tint_rgba);
@ -1494,6 +1494,10 @@ ffi.cdef([[
//lcpp INF [0000] vec3: macro name but used as C declaration in:API void light_dir(light_t* l, vec3 dir);
//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC void light_dir(light_t* l, vec3 dir);
//lcpp INF [0000] vec3: macro name but used as C declaration in: void light_dir(light_t* l, vec3 dir);
//lcpp INF [0000] vec3i: macro name but used as C declaration in:typedef vec3i guid;
//lcpp INF [0000] test: macro name but used as C declaration in:API int (test)(const char *file, int line, const char *expr, bool result);
//lcpp INF [0000] test: macro name but used as C declaration in:STATIC int (test)(const char *file, int line, const char *expr, bool result);
//lcpp INF [0000] test: macro name but used as C declaration in: int (test)(const char *file, int line, const char *expr, bool result);
//lcpp INF [0000] vec2: macro name but used as C declaration in:API vec2 ui_get_dims();
//lcpp INF [0000] vec2: macro name but used as C declaration in:STATIC vec2 ui_get_dims();
//lcpp INF [0000] vec2: macro name but used as C declaration in: vec2 ui_get_dims();
@ -1567,10 +1571,13 @@ pair** sorted;
void (map_gc)(map *m);
bool (map_sort)(map* m);
void (map_clear)(map* m);
unsigned cc4(const char *id);
uint64_t cc8(const char *id);
char *cc4str(unsigned cc);
char *cc8str(uint64_t cc);
enum {
cc_1 = '1', cc_2, cc_3, cc_4, cc_5, cc_6,cc_7, cc_8, cc_9, cc_0, cc__, cc_ = ' ',
cc_A = 'A', cc_B, cc_C, cc_D, cc_E, cc_F,cc_G, cc_H, cc_I, cc_J, cc_K,cc_L, cc_M, cc_N, cc_O, cc_P,cc_Q, cc_R, cc_S, cc_T, cc_U,cc_V, cc_W, cc_X, cc_Y, cc_Z,
cc_a = 'a', cc_b, cc_c, cc_d, cc_e, cc_f,cc_g, cc_h, cc_i, cc_j, cc_k,cc_l, cc_m, cc_n, cc_o, cc_p,cc_q, cc_r, cc_s, cc_t, cc_u,cc_v, cc_w, cc_x, cc_y, cc_z,
};
typedef union vec2i{ struct { int X,Y; }; struct { int x,y; }; struct { int r,g; }; struct { int w,h; }; struct { int min,max; }; struct { int from,to; }; struct { int src,dst; }; int v2[2]; int array[1]; } vec2i;
typedef union vec3i{ struct { int X,Y,Z; }; struct { int x,y,z; }; struct { int r,g,b; }; struct { int w,h,d; }; struct { int min,max; }; struct { int from,to,step; }; struct { int src,dst; }; int v3[3]; int array[1]; } vec3i;
typedef union vec2 { struct { float X,Y; }; struct { float x,y; }; struct { float r,g; }; struct { float w,h; }; struct { float min,max; }; struct { float from,to; }; struct { float src,dst; }; float v2[2]; float array[1]; } vec2;
@ -1809,6 +1816,8 @@ EASE_OUT = 0,
vec3 transform344(const mat44 m, const vec3 p);
vec4 transform444(const mat44 m, const vec4 p);
bool unproject44(vec3 *out, vec3 xyd, vec4 viewport, mat44 mvp);
void print2i( vec2i v );
void print3i( vec3i v );
void print2( vec2 v );
void print3( vec3 v );
void print4( vec4 v );
@ -2289,39 +2298,23 @@ enum { NETWORK_USERID = 7, NETWORK_COUNT , NETWORK_CAPACITY };
void server_send_bin(int64_t handle, const void *ptr, int len);
void server_drop(int64_t handle);
int64_t client_join(const char *ip, int port);
void* obj_malloc( int sz, ... );
void* obj_calloc( int sz, ... );
void obj_free( void *obj );
bool obj_typeeq( const void *obj1, const void *obj2 );
const char* obj_typeof( const void *obj );
unsigned obj_typeid( const void *obj );
unsigned obj_typeid_from_name( const char *name );
void obj_new( const char *type, ... );
void obj_del( void *obj );
void* obj_ref( void *obj );
void* obj_unref( void *obj );
void obj_extend( const char *dstclass, const char *srcclass );
void obj_override( const char *objclass, void (**vtable)(), void(*fn)() );
unsigned obj_load(void *obj, const char* buffer);
unsigned obj_load_file(void *obj, FILE *fp);
unsigned obj_load_inplace(void *obj, const void *src, unsigned srclen);
char* obj_save(const void *obj);
unsigned obj_save_file(FILE *fp, const void *obj);
unsigned obj_save_inplace(void *dst, unsigned cap, const void *obj);
unsigned obj_instances( const void *obj );
void obj_zero( void *obj );
unsigned obj_sizeof( const void *obj );
void obj_hexdump( const void *obj );
void obj_hexdumpf( FILE *out, const void *obj );
void obj_printf( void *obj, const char *text );
const char* obj_output( const void *obj );
void * obj_clone(const void *obj);
void * obj_copy(void **dst, const void *src);
void * obj_mutate(void **dst, const void *src);
extern void (*ctor[256])();
extern void (*dtor[256])();
void* obj_initialize( void **ptr, char *type_and_info );
void *obj_tmpalloc;
int semver( int major, int minor, int patch );
int semvercmp( int v1, int v2 );
typedef struct byte2 { uint8_t x,y; } byte2;
typedef struct byte3 { uint8_t x,y,z; } byte3;
typedef struct byte4 { uint8_t x,y,z,w; } byte4;
typedef struct int2 { int x,y; } int2;
typedef struct int3 { int x,y,z; } int3;
typedef struct int4 { int x,y,z,w; } int4;
typedef struct uint2 { unsigned int x,y; } uint2;
typedef struct uint3 { unsigned int x,y,z; } uint3;
typedef struct uint4 { unsigned int x,y,z,w; } uint4;
typedef struct float2 { float x,y; } float2;
typedef struct float3 { float x,y,z; } float3;
typedef struct float4 { float x,y,z,w; } float4;
typedef struct double2 { double x,y; } double2;
typedef struct double3 { double x,y,z; } double3;
typedef struct double4 { double x,y,z,w; } double4;
int profiler_enable(bool on);
struct profile_t { double stat; int32_t cost, avg; };
typedef struct { map base; struct { pair p; char * key; struct profile_t val; } tmp, *ptr; struct profile_t* tmpval; int (*typed_cmp)(char *, char *); uint64_t (*typed_hash)(char *); } * profiler_t;
@ -2893,27 +2886,11 @@ int u_coefficients_sh;
char* strjoin(char** list, const char *separator);
char * string8(const wchar_t *str);
uint32_t* string32( const char *utf8 );
void* thread( int (*thread_func)(void* user_data), void* user_data );
void thread_destroy( void *thd );
int argc();
char* argv(int);
int flag(const char *commalist);
const char* option(const char *commalist, const char *defaults);
int optioni(const char *commalist, int defaults);
float optionf(const char *commalist, float defaults);
void tty_color(unsigned color);
void tty_reset();
void tty_attach();
void tty_detach();
const char* app_exec(const char *command);
int app_spawn(const char *command);
int app_cores();
int app_battery();
const char* app_name();
const char* app_path();
const char* app_cache();
const char* app_temp();
const char* app_cmdline();
unsigned intern( const char *string );
const char *quark( unsigned key );
typedef char* quarks_db;
unsigned quark_intern( quarks_db*, const char *string );
const char *quark_string( quarks_db*, unsigned key );
uint64_t date();
uint64_t date_epoch();
char* date_string();
@ -2929,6 +2906,34 @@ int u_coefficients_sh;
void sleep_ns(double us);
unsigned timer(unsigned ms, unsigned (*callback)(unsigned ms, void *arg), void *arg);
void timer_destroy(unsigned timer_handle);
typedef vec3i guid;
guid guid_create();
void* thread( int (*thread_func)(void* user_data), void* user_data );
void thread_destroy( void *thd );
int argc();
char* argv(int);
int flag(const char *commalist);
const char* option(const char *commalist, const char *defaults);
int optioni(const char *commalist, int defaults);
float optionf(const char *commalist, float defaults);
void tty_attach();
void tty_detach();
void tty_color(unsigned color);
void tty_reset();
const char* app_exec(const char *command);
int app_spawn(const char *command);
int app_cores();
int app_battery();
const char* app_name();
const char* app_path();
const char* app_cache();
const char* app_temp();
const char* app_cmdline();
void app_beep();
void app_hang();
void app_crash();
void app_singleton(const char *guid);
bool app_open(const char *folder_file_or_url);
char* callstack( int traces );
int callstackf( FILE *fp, int traces );
void die(const char *message);
@ -2937,12 +2942,12 @@ int u_coefficients_sh;
void hexdumpf( FILE *fp, const void *ptr, unsigned len, int width );
void breakpoint(const char *optional_reason);
bool has_debugger();
void signal_hooks(void);
void signal_handler_ignore(int signal);
void signal_handler_quit(int signal);
void signal_handler_abort(int signal);
void signal_handler_debug(int signal);
const char *signal_name(int signal);
void trap_install(void);
const char *trap_name(int signal);
void trap_on_ignore(int signal);
void trap_on_quit(int signal);
void trap_on_abort(int signal);
void trap_on_debug(int signal);
uint16_t lil16(uint16_t n);
uint32_t lil32(uint32_t n);
float lil32f(float n);
@ -2965,6 +2970,7 @@ int u_coefficients_sh;
double* big64pf(void *n, int sz);
int (PANIC)(const char *error, const char *file, int line);
int (PRINTF)(const char *text, const char *stack, const char *file, int line, const char *function);
int (test)(const char *file, int line, const char *expr, bool result);
enum PANEL_FLAGS {
PANEL_OPEN = 1,
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -29136,7 +29136,7 @@ nk_combo_begin_text(struct nk_context *ctx, const char *selected, int len,
else
label.w = header.w - 2 * style->combo.content_padding.x;
nk_widget_text(&win->buffer, label, selected, len, &text,
NK_TEXT_LEFT, ctx->style.font);
NK_TEXT_CENTERED, ctx->style.font); //< @r-lyeh NK_TEXT_LEFT>CENTERED
/* draw open/close button */
if (draw_button_symbol)

View File

@ -139,6 +139,8 @@
{{FILE:v4k_script.c}}
{{FILE:v4k_time.c}}
{{FILE:v4k_system.c}}
{{FILE:v4k_ui.c}}

View File

@ -144,6 +144,8 @@ extern "C" {
{{FILE:v4k_string.h}}
{{FILE:v4k_time.h}}
{{FILE:v4k_system.h}}
{{FILE:v4k_ui.h}}

View File

@ -7,7 +7,7 @@
#endif
{{FILE:3rd_glad.h}}
{{FILE:3rd_font_md.h}}
{{FILE:3rd_icon_md.h}}
#ifdef V4K_3RD
@ -175,4 +175,5 @@ errno_t fopen_s(
#undef Token
#undef Table
#undef rehash
#undef NB
#endif // V4K_3RD

View File

@ -20,7 +20,7 @@ bt_t bt(const char *ini_file, unsigned flags) {
array(char*) m = strsplit(vfs_read(ini_file), "\r\n");
bt_t *self = &root;
self->type = cc8(root);
self->type = cc4(r,o,o,t);
//self->parent = self;
for( int i = 0; i < array_count(m); ++i ) {
@ -56,20 +56,20 @@ bt_t bt(const char *ini_file, unsigned flags) {
int bt_run(bt_t *b) {
int rc = 0;
/**/ if( b->type == cc8( run) ) { return b->action ? b->action() : 0; }
else if( b->type == cc8( not) ) { return !bt_run(b->children + 0); }
else if( b->type == cc8( sleep) ) { return sleep_ss(b->argf), bt_run(b->children + 0); }
else if( b->type == cc8( defer) ) { rc = bt_run(b->children + 0); return sleep_ss(b->argf), rc; }
else if( b->type == cc8( loop) ) { int rc; for(int i = 0; i < b->argf; ++i) rc = bt_run(b->children + 0); return rc; }
else if( b->type == cc8( once) ) { return b->argf ? 0 : (b->argf = 1), bt_run(b->children + 0); }
else if( b->type == cc8( count) ) { return b->argf <= 0 ? 0 : --b->argf, bt_run(b->children + 0); }
else if( b->type == cc8( pass) ) { return bt_run(b->children + 0), 1; }
else if( b->type == cc8( fail) ) { return bt_run(b->children + 0), 0; }
else if( b->type == cc8( result) ) { return bt_run(b->children + 0), !!b->argf; }
else if( b->type == cc8( all) ) { for( int i = 0; i < array_count(b->children); ++i ) if(!bt_run(b->children+i)) return 0; return 1; }
else if( b->type == cc8( any) ) { for( int i = 0; i < array_count(b->children); ++i ) if( bt_run(b->children+i)) return 1; return 0; }
else if( b->type == cc8( root) ) { for( int i = 0; i < array_count(b->children); ++i ) rc|=bt_run(b->children+i); return rc; }
else if( b->type == cc8(rootfail) ) { rc = 1; for( int i = 0; i < array_count(b->children); ++i ) rc&=~bt_run(b->children+i); return rc; }
/**/ if( b->type == cc3( r,u,n) ) { return b->action ? b->action() : 0; }
else if( b->type == cc3( n,o,t) ) { return !bt_run(b->children + 0); }
else if( b->type == cc5( s,l,e,e,p) ) { return sleep_ss(b->argf), bt_run(b->children + 0); }
else if( b->type == cc5( d,e,f,e,r) ) { rc = bt_run(b->children + 0); return sleep_ss(b->argf), rc; }
else if( b->type == cc4( l,o,o,p) ) { int rc; for(int i = 0; i < b->argf; ++i) rc = bt_run(b->children + 0); return rc; }
else if( b->type == cc4( o,n,c,e) ) { return b->argf ? 0 : (b->argf = 1), bt_run(b->children + 0); }
else if( b->type == cc5( c,o,u,n,t) ) { return b->argf <= 0 ? 0 : --b->argf, bt_run(b->children + 0); }
else if( b->type == cc4( p,a,s,s) ) { return bt_run(b->children + 0), 1; }
else if( b->type == cc4( f,a,i,l) ) { return bt_run(b->children + 0), 0; }
else if( b->type == cc6( r,e,s,u,l,t) ) { return bt_run(b->children + 0), !!b->argf; }
else if( b->type == cc3( a,l,l) ) { for( int i = 0; i < array_count(b->children); ++i ) if(!bt_run(b->children+i)) return 0; return 1; }
else if( b->type == cc3( a,n,y) ) { for( int i = 0; i < array_count(b->children); ++i ) if( bt_run(b->children+i)) return 1; return 0; }
else if( b->type == cc4( r,o,o,t) ) { for( int i = 0; i < array_count(b->children); ++i ) rc|=bt_run(b->children+i); return rc; }
else if( b->type == cc8(r,o,o,t,f,a,i,l) ) { rc = 1; for( int i = 0; i < array_count(b->children); ++i ) rc&=~bt_run(b->children+i); return rc; }
return 0;
}

View File

@ -121,7 +121,7 @@
#define countof(x) (int)(sizeof (x) / sizeof 0[x])
#define concat(a,b) conc4t(a,b)
#define conc4t(a,b) a##b
#define conc4t(a,b) a##b ///-
#define macro(name) concat(name, __LINE__)
#define defer(begin,end) for(int macro(i) = ((begin), 0); !macro(i); macro(i) = ((end), 1))
@ -149,16 +149,16 @@
#define FILELINE __FILE__ ":" STRINGIZE(__LINE__)
#define STRINGIZE(x) STRINGIZ3(x)
#define STRINGIZ3(x) #x
#define STRINGIZ3(x) #x ///-
#define EXPAND(name, ...) EXPAND_QUOTE(EXPAND_JOIN(name, EXPAND_COUNT_ARGS(__VA_ARGS__)), (__VA_ARGS__))
#define EXPAND_QUOTE(x, y) x y
#define EXPAND_JOIN(name, count) EXPAND_J0IN(name, count)
#define EXPAND_J0IN(name, count) EXPAND_J01N(name, count)
#define EXPAND_J01N(name, count) name##count
#define EXPAND_COUNT_ARGS(...) EXPAND_ARGS((__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
#define EXPAND_ARGS(args) EXPAND_RETURN_COUNT args
#define EXPAND_RETURN_COUNT(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, count, ...) count
#define EXPAND_QUOTE(x, y) x y ///-
#define EXPAND_JOIN(name, count) EXPAND_J0IN(name, count) ///-
#define EXPAND_J0IN(name, count) EXPAND_J01N(name, count) ///-
#define EXPAND_J01N(name, count) name##count ///-
#define EXPAND_COUNT_ARGS(...) EXPAND_ARGS((__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) ///-
#define EXPAND_ARGS(args) EXPAND_RETURN_COUNT args ///-
#define EXPAND_RETURN_COUNT(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, count, ...) count ///-
#if is(cl) && !is(cpp)
#define INLINE __inline
@ -182,10 +182,13 @@
// usage: #define vec2(...) C_CAST(vec2, __VA_ARGS__)
// typedef union vec2 { float X,Y; }; vec2 a = {0,1}, b = vec2(0,1);
#if is(cpp)
#define C_CAST(type, ...) ( type { __VA_ARGS__ } )
#else
#define C_CAST(type, ...) ((type){ __VA_ARGS__ } )
#define C_CAST(type, ...) ( ifdef(c,(type),type) { __VA_ARGS__ } )
// -----------------------------------------------------------------------------
// build info
#ifndef BUILD_VERSION
#define BUILD_VERSION ""
#endif
// -----------------------------------------------------------------------------
@ -193,15 +196,9 @@
// win32 users would need to -DAPI=IMPORT/EXPORT as needed when using/building V4K as DLL.
#if is(win32)
#define IMPORT ifdef(gcc, __attribute__ ((dllimport)), __declspec(dllimport))
#define EXPORT ifdef(gcc, __attribute__ ((dllexport)), __declspec(dllexport))
#define IMPORT ifdef(win32, ifdef(gcc, __attribute__ ((dllimport)), __declspec(dllimport)))
#define EXPORT ifdef(win32, ifdef(gcc, __attribute__ ((dllexport)), __declspec(dllexport)))
#define STATIC
#else
#define IMPORT
#define EXPORT
#define STATIC
#endif
#ifndef API
#define API STATIC

View File

@ -428,13 +428,29 @@ API bool (map_sort)(map* m);
API void (map_clear)(map* m);
// -----------------------------------------------------------------------------
// four-cc, eight-cc
// compile-time fourcc, eightcc
API unsigned cc4(const char *id);
API uint64_t cc8(const char *id);
API char *cc4str(unsigned cc);
API char *cc8str(uint64_t cc);
// fast path
#define cc4(abcd) ( *(unsigned*) #abcd " " ) // lil32() ?
#define cc8(abcdefgh) ( *(uint64_t*) #abcdefgh " " ) // lil64() ?
enum {
# define _(a,b,c,d,e) cc_##a, cc_##b, cc_##c, cc_##d, cc_##e
cc_1 = '1', _(2,3,4,5,6),_(7,8,9,0,_), cc_ = ' ',
cc_A = 'A', _(B,C,D,E,F),_(G,H,I,J,K),_(L,M,N,O,P),_(Q,R,S,T,U),_(V,W,X,Y,Z),
cc_a = 'a', _(b,c,d,e,f),_(g,h,i,j,k),_(l,m,n,o,p),_(q,r,s,t,u),_(v,w,x,y,z),
# undef _
};
#ifdef BIG
#define cc4(a,b,c,d) ((uint32_t)(cc_##a<<24) | (cc_##b<<16) | (cc_##c<<8) | (cc_##d<<0))
#define cc8(a,b,c,d,e,f,g,h) (((uint64_t)cc4(a,b,c,d) << 32ULL) | cc4(e,f,g,h))
#else
#define cc4(a,b,c,d) ((uint32_t)(cc_##d<<24) | (cc_##c<<16) | (cc_##b<<8) | (cc_##a<<0))
#define cc8(a,b,c,d,e,f,g,h) (((uint64_t)cc4(e,f,g,h) << 32ULL) | cc4(a,b,c,d))
#endif
#define cc3(a,b,c) cc4(,a,b,c)
#define cc5(a,b,c,d,e) cc6(,a,b,c,d,e)
#define cc6(a,b,c,d,e,f) cc7(,a,b,c,d,e,f)
#define cc7(a,b,c,d,e,f,g) cc8(,a,b,c,d,e,f,g)

View File

@ -629,7 +629,7 @@ const char** vfs_list(const char *masks) {
}
static
char *vfs_unpack(const char *pathfile, int *size) { // must free() after use
char *vfs_unpack(const char *pathfile, int *size) { // must FREE() after use
// @todo: add cache here
char *data = NULL;
for(archive_dir *dir = dir_mount; dir && !data; dir = dir->next) {

View File

@ -65,7 +65,7 @@ void v4k_quit(void) {
void v4k_init() {
do_once {
// install signal handlers
ifdef(debug, signal_hooks());
ifdef(debug, trap_install());
// init panic handler
panic_oom_reserve = SYS_REALLOC(panic_oom_reserve, 1<<20); // 1MiB

View File

@ -978,7 +978,13 @@ vec3 transform_scaling (const mat44 m, const vec3 scaling) {
// ----------------------------------------------------------------------------
// !!! for debugging
#include <stdio.h>
void printi_( int *m, int ii, int jj ) {
for( int j = 0; j < jj; ++j ) {
for( int i = 0; i < ii; ++i ) printf("%10d ", *m++);
puts("");
}
// puts("---");
}
void print_( float *m, int ii, int jj ) {
for( int j = 0; j < jj; ++j ) {
for( int i = 0; i < ii; ++i ) printf("%8.3f", *m++);
@ -986,6 +992,8 @@ void print_( float *m, int ii, int jj ) {
}
// puts("---");
}
void print2i( vec2i v ) { printi_(&v.x,2,1); }
void print3i( vec3i v ) { printi_(&v.x,3,1); }
void print2( vec2 v ) { print_(&v.x,2,1); }
void print3( vec3 v ) { print_(&v.x,3,1); }
void print4( vec4 v ) { print_(&v.x,4,1); }

View File

@ -330,6 +330,8 @@ API bool unproject44(vec3 *out, vec3 xyd, vec4 viewport, mat44 mvp);
// ----------------------------------------------------------------------------
// debugging and utils
API void print2i( vec2i v );
API void print3i( vec3i v );
API void print2( vec2 v );
API void print3( vec3 v );
API void print4( vec4 v );

View File

@ -27,7 +27,7 @@ rpc_call rpc_new_call(const char *signature, rpc_function function) {
#if RPC_DEBUG
printf("%p %p %s `%s` %s(", function, (void*)hash, rettype, hash_sig, method); for(int i = 0, end = array_count(tokens); i < end; ++i) printf("%s%s", tokens[i], i == (end-1)? "":", "); puts(");");
#endif
return (rpc_call) { strdup(method), function, hash }; // LEAK
return (rpc_call) { STRDUP(method), function, hash }; // LEAK
}
}
return (rpc_call) {0};

View File

@ -1,504 +1,44 @@
void *obj_initialize( void **ptr, char *type ) {
unsigned payload = ((unsigned char)type[0]) << 8; type[0] = '\1'; // @fixme: actually use this '\1' custom tag
#ifndef NDEBUG
strcatf(&type, "%s", callstack(+16)); // debug: for debugging purposes only
#endif
ptr[0] = OBJBOX((void *)type, payload);
return &ptr[1];
}
void obj_free( void *obj ) {
void *ptr = *((void**)obj - 1);
FREE( OBJUNBOX(ptr) );
obj = (void**)obj - 1;
FREE( obj );
}
void obj_del( void *obj ) {
unsigned type = obj_typeid(obj);
(type[dtor])(obj);
obj_free( obj );
}
unsigned obj_instances( const void *obj ) {
return OBJPAYLOAD3(obj) + 1;
}
// ---
const char *obj_output( const void *obj ) {
void* ptr = *((void**)obj - 1);
char *str = OBJUNBOX(ptr);
while( *str != '\n' ) ++str;
return (const char *)str + 1;
}
void (obj_printf)( void *obj, const char *text ) {
void* ptr = *((void**)obj - 1);
char* str = OBJUNBOX(ptr);
unsigned payload = OBJPAYLOAD16(ptr);
strcatf(&str, "%s", text);
*((void**)obj - 1) = OBJBOX(str, payload);
}
// ---
const char *obj_typeof( const void *obj ) {
void* ptr = *((void**)obj - 1);
ptr = OBJUNBOX(ptr);
char name[256]; sscanf((const char*)ptr + 1, "%[^\n]", name); // @fixme: overflow
return va("%s", name);
}
unsigned obj_typeid(const void *obj) {
void* ptr = *((void**)obj - 1);
unsigned payload = OBJPAYLOAD16(ptr);
return payload >> 8;
}
bool obj_typeeq( const void *obj1, const void *obj2 ) {
if( obj_typeid(obj1) != obj_typeid(obj2) ) return false;
return !strcmp( obj_typeof(obj1), obj_typeof(obj2) );
}
unsigned obj_typeid_from_name(const char *name) {
name += strbegi(name, "struct ") ? 8 : strbegi(name, "union ") ? 7 : 0;
unsigned typeid = hash_str(name) & 255; // @fixme: increase bits / decrease colliders (256 types only!)
ASSERT( typeid, "Name of given class has an empty (zeroed) hash. Limitation by design" );
static map(unsigned, char *) registered; // @fixme: add mutex
do_once map_init(registered, less_int, hash_int);
do_once map_insert(registered, 1, "(typeless)");
char **found = map_find(registered, typeid);
if(!found) map_insert(registered, typeid, STRDUP(name));
else ASSERT( !strcmp(name, *found), "Uh-oh, types collided. Please rename one of these two classes '%s'/'%s'", name, *found);
return typeid;
}
// ---
unsigned obj_sizeof(const void *obj) {
void *ptr = (void**)obj - 1;
return ALLOCSIZE(ptr) - sizeof(void*);
}
void obj_zero(void *obj) {
// clear console log
void* ptr = *((void**)obj - 1);
char* str = OBJUNBOX(ptr);
if( str[0] ) {
unsigned payload = OBJPAYLOAD16(ptr);
unsigned namelen = strlen(obj_typeof(obj));
str = REALLOC(str, 1+namelen+2); // preserve \1+name+\n+\0
str[1+namelen+2-1] = '\0';
*((void**)obj - 1) = OBJBOX(str, payload);
}
// reset data
dtor(obj);
memset(obj, 0, obj_sizeof(obj));
ctor(obj);
}
void obj_hexdumpf(FILE *out, const void *obj) {
hexdumpf(out, obj, obj_sizeof(obj), 16);
const char *output = obj_output(obj);
fprintf( out, "; ptr=[%p] sizeof=%d typeof=%s typeid=%#x refs=%d\n%s%s\n",
obj, obj_sizeof(obj), obj_typeof(obj), obj_typeid(obj), (int)(OBJPAYLOAD16(obj) & 0xFF),
output[0] ? output : "(no output)", output[0] ? "---" : "");
}
void obj_hexdump(const void *obj) {
obj_hexdumpf( stdout, obj );
}
// object: load/save
unsigned obj_load_buffer(void *obj, const void *src, unsigned srclen) {
unsigned objlen = obj_sizeof(obj);
if( srclen > objlen ) return 0; // @fixme: do something clever
memcpy(obj, src, srclen); // @fixme: do something clever
return objlen;
}
unsigned obj_load(void *obj, const array(char) buffer) {
unsigned bytes = buffer ? obj_load_buffer(obj, buffer, array_count((char*)buffer)) : 0;
return bytes;
}
unsigned obj_load_file(void *obj, FILE *fp) {
unsigned len = obj_sizeof(obj);
char *buffer = va("%*.s", len, "");
unsigned read = fread(buffer, 1, len, fp);
if( read != (1*len) ) {
return 0;
}
unsigned bytes = obj_load_buffer(obj, buffer, len);
return bytes;
}
unsigned obj_save_buffer(void *dst, unsigned cap, const void *obj) {
unsigned len = obj_sizeof(obj);
if( len > cap ) return 0;
memcpy(dst, obj, len); // @fixme: do something clever
return len;
}
array(char) obj_save(const void *obj) { // empty if error. must array_free() after use
array(char) data = 0;
unsigned len = obj_sizeof(obj);
array_resize(data, len);
unsigned bytes = obj_save_buffer(data, len, obj);
array_resize(data, bytes);
return data;
}
unsigned obj_save_file(FILE *fp, const void *obj) {
unsigned len = obj_sizeof(obj);
char *buffer = va("%*.s", len, "");
unsigned bytes = obj_save_buffer(buffer, len, obj);
if( bytes > 0 ) {
unsigned written = fwrite(buffer, 1, len, fp);
if( written == (1*len) ) {
return written;
}
}
return 0; // error
}
// ---
static int __thread global_ref_count; // @fixme: make it atomic
static void objref_check_atexit(void) {
if(global_ref_count > 0) fprintf(stderr, "Warn! Possible memory_leaks: %d refs not destroyed\n", global_ref_count);
if(global_ref_count < 0) fprintf(stderr, "Warn! Possible double free: %d refs double destroyed\n", -global_ref_count);
}
void* obj_ref(void *obj) {
do_once atexit(objref_check_atexit);
if( obj ) {
void *ptr = *((void**)obj - 1);
unsigned payload = OBJPAYLOAD16(ptr);
unsigned ref_count = payload & 255;
ASSERT(ref_count < 255, "Object cannot hold more than 256 refs. Limitation by design.");
*((void**)obj - 1) = OBJBOX(OBJUNBOX(ptr), payload + 1);
global_ref_count++;
}
return obj;
}
void* obj_unref(void *obj) {
if( obj ) {
void *ptr = *((void**)obj - 1);
unsigned payload = OBJPAYLOAD16(ptr);
unsigned ref_count = payload & 255;
*((void**)obj - 1) = OBJBOX(OBJUNBOX(ptr), payload - 1);
global_ref_count--;
if( ref_count <= 1 ) {
obj_del(obj);
return 0;
}
}
return obj;
}
// ---
void dummy1() {}
#define dummy8 dummy1,dummy1,dummy1,dummy1,dummy1,dummy1,dummy1,dummy1
#define dummy64 dummy8,dummy8,dummy8,dummy8,dummy8,dummy8,dummy8,dummy8
#define dummy256 dummy64,dummy64,dummy64,dummy64
void (*dtor[256])() = { dummy256 };
void (*ctor[256])() = { dummy256 };
// ---
static set(uintptr_t) vtables; // @fixme: add mutex
void (obj_override)(const char *objclass, void (**vtable)(), void(*fn)()) {
do_once set_init(vtables, less_64, hash_64);
set_find_or_add(vtables, (uintptr_t)vtable);
vtable[ obj_typeid_from_name(objclass) ] = fn;
}
void (obj_extend)(const char *dstclass, const char *srcclass) { // wip, @testme
unsigned dst_id = obj_typeid_from_name(dstclass);
unsigned src_id = obj_typeid_from_name(srcclass);
// iterate src vtables, and assign them to dst
if(dst_id != src_id)
for each_set(vtables, void **, src_table) {
if( src_table[src_id] ) {
src_table[dst_id] = (void(*)()) (src_table[ src_id ]);
}
}
}
// ---
void *obj_clone(const void *obj) { // @fixme: clone object console as well?
unsigned sz = obj_sizeof(obj);
const char *nm = obj_typeof(obj);
unsigned id = obj_typeid(obj);
void *obj2 = obj_initialize((void**)MALLOC(sizeof(void*)+sz), stringf("%c%s\n" "cloned" "\n", id, nm)); // STRDUP( OBJUNBOX(*((void**)obj - 1)) ));
memcpy(obj2, obj, sz);
return obj2;
}
void *obj_copy(void **dst, const void *src) {
if(!*dst) return *dst = obj_clone(src);
if( obj_typeeq(*dst, src) ) {
return memcpy(*dst, src, obj_sizeof(src));
}
return NULL;
}
void *obj_mutate(void **dst_, const void *src) {
// mutate a class. ie, convert a given object class into a different one,
// while preserving the original metas and references as much as possible.
// -----------------------------------------------------------------------------
// semantic versioning in a single byte (octal)
// - rlyeh, public domain.
//
// @fixme: systems might be tracking objects in the future. the fact that we
// can reallocate a pointer (and hence, change its address) seems way too dangerous,
// as the tracking systems could crash when referencing a mutated object.
// solutions: do not reallocate if sizeof(new_class) > sizeof(old_class) maybe? good enough?
// also, optimization hint: no need to reallocate if both sizes matches, just copy contents.
// - single octal byte that represents semantic versioning (major.minor.patch).
// - allowed range [0000..0377] ( <-> [0..255] decimal )
// - comparison checks only major.minor tuple as per convention.
if(!*dst_) return *dst_ = obj_clone(src);
void *dst = *dst_;
dtor(dst);
unsigned src_sz = obj_sizeof(src);
unsigned src_id = obj_typeid(src);
void *dst_ptr = *((void**)dst - 1);
unsigned payload = (OBJPAYLOAD16(dst_ptr) & 255) | src_id << 8;
FREE( OBJUNBOX(dst_ptr) );
*((void**)dst - 1) = OBJBOX( STRDUP( OBJUNBOX(*((void**)src - 1)) ), payload);
void *base = (void*)((void**)dst - 1);
base = REALLOC(base, src_sz + sizeof(void*));
*dst_ = (char*)base + sizeof(void*);
dst = (char*)base + sizeof(void*);
memcpy(dst, src, src_sz);
ctor(dst);
return dst;
int semver( int major, int minor, int patch ) {
return SEMVER(major, minor, patch);
}
int semvercmp( int v1, int v2 ) {
return SEMVERCMP(v1, v2);
}
#if 0
AUTORUN {
for( int i= 0; i <= 255; ++i) printf(SEMVERFMT ",", i);
puts("");
#ifdef OBJ_DEMO
printf(SEMVERFMT "\n", semver(3,7,7));
printf(SEMVERFMT "\n", semver(2,7,7));
printf(SEMVERFMT "\n", semver(1,7,7));
printf(SEMVERFMT "\n", semver(0,7,7));
typedef struct MyObject {
char* id;
int x,y;
float rotation;
struct MyObject *next;
} MyObject;
void tests1() {
// Construct two objects
MyObject *root = obj_new(MyObject, 0);
MyObject *obj = obj_new(MyObject, "An identifier!", 0x11, 0x22, 3.1415f, root );
// Log some lines
obj_printf(root, "this is a logline #1\n");
obj_printf(root, "this is a logline #2\n");
obj_printf(root, "this is a logline #3\n");
obj_printf(obj, "yet another logline #1\n");
obj_printf(obj, "yet another logline #2\n");
// Dump contents of our objects
obj_hexdump(root);
obj_hexdump(obj);
// Save to mem
array(char) buffer = obj_save(obj);
printf("%d bytes\n", (int)array_count(buffer));
// Clear
obj_zero( obj );
obj_hexdump( obj );
// Reload
obj_load( obj, buffer );
obj_hexdump( obj );
// Copy tests
{
MyObject *clone = obj_clone(obj);
obj_hexdump(clone);
obj_del(clone);
}
{
MyObject *copy = 0;
obj_copy(&copy, obj);
obj_hexdump(copy);
obj_del(copy);
}
{
MyObject *copy = obj_new(MyObject, "A different identifier!", 0x33, 0x44, 0.0f, root );
obj_copy(&copy, obj);
obj_hexdump(copy);
obj_del(copy);
}
{
void *copy = obj_malloc(100, "an untyped class" );
obj_mutate(&copy, obj);
obj_hexdump(copy);
obj_copy(&copy, obj);
obj_hexdump(copy);
obj_del(copy);
}
// Benchmarking call overhead.
// We're here using dtor as a method to test. Since there is actually no
// destructor associated to this class, it will be safe to call it extensively (no double frees).
//
// results:
// 427 million calls/s @ old i5-4300/1.90Ghz laptop. compiled with "cl /Ox /Os /MT /DNDEBUG /GL /GF /arch:AVX2"
#ifndef N
#define N (INT32_MAX-1)
#endif
double t = (puts("benchmarking..."), -clock() / (double)CLOCKS_PER_SEC);
for( int i = 0; i < N; ++i ) {
dtor(root);
}
t += clock() / (double)CLOCKS_PER_SEC;
printf("Benchmark: %5.2f objcalls/s %5.2fM objcalls/s\n", N/(t), (N/1000)/(t*1000)); // ((N+N)*5) / (t) );
printf(SEMVERFMT "\n", semver(3,7,1));
printf(SEMVERFMT "\n", semver(2,5,3));
printf(SEMVERFMT "\n", semver(1,3,5));
printf(SEMVERFMT "\n", semver(0,1,7));
assert( semvercmp( 0357, 0300 ) > 0 );
assert( semvercmp( 0277, 0300 ) < 0 );
assert( semvercmp( 0277, 0200 ) > 0 );
assert( semvercmp( 0277, 0100 ) < 0 );
assert( semvercmp( 0076, 0070 ) == 0 );
assert( semvercmp( 0076, 0077 ) == 0 );
assert( semvercmp( 0176, 0170 ) == 0 );
assert( semvercmp( 0176, 0177 ) == 0 );
assert( semvercmp( 0276, 0270 ) == 0 );
assert( semvercmp( 0276, 0277 ) == 0 );
assert( semvercmp( 0376, 0370 ) == 0 );
assert( semvercmp( 0376, 0377 ) == 0 );
}
// --------------
#define dump(obj) dump[obj_typeid(obj)](obj)
#define area(obj) area[obj_typeid(obj)](obj)
extern void (*dump[256])();
extern float (*area[256])();
void (*dump[256])() = {0};
float (*area[256])() = {0};
// --------------
typedef struct box {
float w;
} box;
void box_ctor(box *b) { printf("box already constructed: box-w:%f\n", b->w); }
void box_dtor(box *b) { puts("deleting box..."); }
void box_dump(box *b) { printf("box-w:%f\n", b->w); }
float box_area(box *b) { return b->w * b->w; }
#define REGISTER_BOX \
obj_override(box, ctor); \
obj_override(box, dump); \
obj_override(box, area); \
obj_override(box, dtor);
typedef struct rect {
float w, h;
} rect;
void rect_dump(rect *r) { printf("rect-w:%f rect-h:%f\n", r->w, r->h); }
float rect_area(rect *r) { return r->w * r->h; }
void rect_dtor(rect *r) { puts("deleting rect..."); }
#define REGISTER_RECT \
obj_override(rect, dump); \
obj_override(rect, area); \
obj_override(rect, dtor);
void tests2() {
REGISTER_BOX
REGISTER_RECT
box *b = obj_new(box, 100);
rect *r = obj_new(rect, 100, 200);
dump(b);
dump(r);
printf("%f\n", area(b));
printf("%f\n", area(r));
obj_del(b);
obj_ref(r); obj_unref(r); //obj_del(r);
int *untyped = obj_malloc( sizeof(int) );
int *my_number = obj_malloc( sizeof(int), "a comment about my_number" );
char *my_text = obj_malloc( 32, "some debug info here" );
*untyped = 100;
*my_number = 123;
sprintf( my_text, "hello world" );
struct my_bitmap { int w, h, bpp; const char *pixels; };
struct my_bitmap *my_bitmap = obj_new(struct my_bitmap, 2,2,8, "\1\2\3\4");
printf( "%p(%s,%u)\n", my_bitmap, obj_typeof(my_bitmap), obj_typeid(my_bitmap) );
printf( "%d(%s,%d)\n", *untyped, obj_typeof(untyped), obj_typeid(untyped) );
printf( "%d(%s,%d)\n", *my_number, obj_typeof(my_number), obj_typeid(my_number) );
printf( "%s(%s,%d)\n", my_text, obj_typeof(my_text), obj_typeid(my_text) );
obj_printf(my_text, "hello world #1\n");
obj_printf(my_text, "hello world #2\n");
puts(obj_output(my_text));
printf( "%s(%s,%d)\n", my_text, obj_typeof(my_text), obj_typeid(my_text) );
printf( "equal?:%d\n", obj_typeeq(my_number, untyped) );
printf( "equal?:%d\n", obj_typeeq(my_number, my_number) );
printf( "equal?:%d\n", obj_typeeq(my_number, my_text) );
printf( "equal?:%d\n", obj_typeeq(my_number, my_bitmap) );
obj_free( untyped );
obj_free( my_text );
obj_free( my_bitmap );
obj_del( my_number ); // should not crash, even if allocated with obj_malloc()
}
int main() {
tests1();
tests2();
puts("ok");
}
/*
MEMBER( MyObject, char*, id );
MEMBER( MyObject, int, x );
MEMBER( MyObject, int, y );
MEMBER( MyObject, float, rotation, "(degrees)" );
MEMBER( MyObject, MyObject*, next, "(linked list)" );
*/
#define main main__
#endif

View File

@ -1,131 +1,95 @@
// -----------------------------------------------------------------------------
// C object framework (constructors/destructors, methods, rtti, refcounting)
// semantic versioning in a single byte (octal)
// - rlyeh, public domain.
//
// ## object api (low level)
// - single octal byte that represents semantic versioning (major.minor.patch).
// - allowed range [0000..0377] ( <-> [0..255] decimal )
// - comparison checks only major.minor tuple as per convention.
API int semver( int major, int minor, int patch );
API int semvercmp( int v1, int v2 );
#define SEMVER(major,minor,patch) (0100 * (major) + 010 * (minor) + (patch))
#define SEMVERCMP(v1,v2) (((v1) & 0110) - ((v2) & 0110))
#define SEMVERFMT "%03o"
// -----------------------------------------------------------------------------
// autorun initializers for C
// - rlyeh, public domain
//
// - [ ] make object from reflected type (factory)
// - [x] make object (if debug, include callstack as well)
// - [x] ctor method (optional, ref to constructor)
// - [x] dtor method (optional, ref to deleter)
// - [x] zero mem object
// - [x] object logger
// - [ ] iterate members in a struct
//
// - [x] clone/copy/mutate classes
// - [x] load/save objects from/to memory/disk
// - [ ] diff/patch objects
// - [ ] experimental: support for AoSoA layout (using objcnt, 3bits)
//
// ## object decomposition
//
// <---------|--------->
// OBJ-SHADOW (64-bits) | OBJ CONTENT (N bytes)
// +-----+-----+-------------+-----------+-----+-----+-----+-----+--
// |TYPE |REFS.| OBJ NAME | obj cnt | ... | ... | ... | ... | .
// +-----+-----+-------------+-----------+-----+-----+-----+-----+--
// \-16-bits--/\---45-bits--/\--3-bits--/\-------N-bytes-----------
//
// OBJ TYPE+NAME format:
// - [type] custom tags at 0x0
// - [1..N] name
// - [\n] blank separator
// - [comments, logger, infos, etc] << obj_printf();
//
// ## object limitations
// - 256 classes max
// - 256 references max
// - 8-byte overhead per object
// - 2 total allocs per object (could be flattened into 1 with some more work)
//
// @todo: obj_extend( "class_src", "class_dst" ); call[super(obj)]()
// @todo: preferred load/save format: [ver:1,user:2,type:1] ([eof|size:7/15/23/31][blob:N])+ [crc:1/2/3/4]
// @todo: more serious loading/saving spec
// note: based on code by Joe Lowe (public domain).
// note: XIU for C initializers, XCU for C++ initializers, XTU for C deinitializers
// object api (heap+rtti)
#ifdef __cplusplus
#define AUTORUN \
static void AUTORUN_U(f)(void); \
static const int AUTORUN_J(AUTORUN_U(f),__1) = (AUTORUN_U(f)(), 1); \
static void AUTORUN_U(f)(void)
#elif _MSC_VER
#define AUTORUN \
static void AUTORUN_U(f)(void); \
static int AUTORUN_J(AUTORUN_U(f),__1) (){ AUTORUN_U(f)(); return 0; } \
__pragma(section(".CRT$XIU", long, read)) \
__declspec(allocate(".CRT$XIU")) \
static int(* AUTORUN_J(AUTORUN_U(f),__2) )() = AUTORUN_J(AUTORUN_U(f),__1); \
static void AUTORUN_U(f)(void)
#else
#define AUTORUN \
__attribute__((constructor)) \
static void AUTORUN_U(f)(void)
#endif
API void* obj_malloc( int sz, ... );
API void* obj_calloc( int sz, ... );
API void obj_free( void *obj );
// join + unique macro utils
API bool obj_typeeq( const void *obj1, const void *obj2 );
API const char* obj_typeof( const void *obj );
API unsigned obj_typeid( const void *obj );
API unsigned obj_typeid_from_name( const char *name );
#define AUTORUN_j(a, b) a##b
#define AUTORUN_J(a, b) AUTORUN_j(a, b)
#define AUTORUN_U(x) AUTORUN_J(x, __LINE__)
// object api (ctor/dtor, refcounting, oop)
#if 0 // autorun demo
void byebye(void) { puts("seen after main()"); }
AUTORUN { puts("seen before main()"); }
AUTORUN { puts("seen before main() too"); atexit( byebye ); }
#endif
API void obj_new( const char *type, ... );
API void obj_del( void *obj );
// -----------------------------------------------------------------------------
// storage types. refer to vec2i/3i, vec2/3/4 if you plan to do math operations
API void* obj_ref( void *obj );
API void* obj_unref( void *obj );
typedef struct byte2 { uint8_t x,y; } byte2;
typedef struct byte3 { uint8_t x,y,z; } byte3;
typedef struct byte4 { uint8_t x,y,z,w; } byte4;
API void obj_extend( const char *dstclass, const char *srcclass );
API void obj_override( const char *objclass, void (**vtable)(), void(*fn)() );
typedef struct int2 { int x,y; } int2;
typedef struct int3 { int x,y,z; } int3;
typedef struct int4 { int x,y,z,w; } int4;
// object: serialize
typedef struct uint2 { unsigned int x,y; } uint2;
typedef struct uint3 { unsigned int x,y,z; } uint3;
typedef struct uint4 { unsigned int x,y,z,w; } uint4;
API unsigned obj_load(void *obj, const array(char) buffer);
API unsigned obj_load_file(void *obj, FILE *fp);
API unsigned obj_load_inplace(void *obj, const void *src, unsigned srclen);
typedef struct float2 { float x,y; } float2;
typedef struct float3 { float x,y,z; } float3;
typedef struct float4 { float x,y,z,w; } float4;
API array(char) obj_save(const void *obj); // empty if error. must array_free() after use
API unsigned obj_save_file(FILE *fp, const void *obj);
API unsigned obj_save_inplace(void *dst, unsigned cap, const void *obj);
typedef struct double2 { double x,y; } double2;
typedef struct double3 { double x,y,z; } double3;
typedef struct double4 { double x,y,z,w; } double4;
// object: utils
#define byte2(x,y) M_CAST(byte2, (uint8_t)(x), (uint8_t)(y) )
#define byte3(x,y,z) M_CAST(byte3, (uint8_t)(x), (uint8_t)(y), (uint8_t)(z) )
#define byte4(x,y,z,w) M_CAST(byte4, (uint8_t)(x), (uint8_t)(y), (uint8_t)(z), (uint8_t)(w) )
API unsigned obj_instances( const void *obj );
#define int2(x,y) M_CAST(int2, (int)(x), (int)(y) )
#define int3(x,y,z) M_CAST(int3, (int)(x), (int)(y), (int)(z) )
#define int4(x,y,z,w) M_CAST(int4, (int)(x), (int)(y), (int)(z), (int)(w) )
API void obj_zero( void *obj );
API unsigned obj_sizeof( const void *obj );
#define uint2(x,y) M_CAST(uint2, (unsigned)(x), (unsigned)(y) )
#define uint3(x,y,z) M_CAST(uint3, (unsigned)(x), (unsigned)(y), (unsigned)(z) )
#define uint4(x,y,z,w) M_CAST(uint4, (unsigned)(x), (unsigned)(y), (unsigned)(z), (unsigned)(w) )
API void obj_hexdump( const void *obj );
API void obj_hexdumpf( FILE *out, const void *obj );
#define float2(x,y) M_CAST(float2, (float)(x), (float)(y) )
#define float3(x,y,z) M_CAST(float3, (float)(x), (float)(y), (float)(z) )
#define float4(x,y,z,w) M_CAST(float4, (float)(x), (float)(y), (float)(z), (float)(w) )
API void obj_printf( void *obj, const char *text );
API const char* obj_output( const void *obj );
API void * obj_clone(const void *obj);
API void * obj_copy(void **dst, const void *src);
API void * obj_mutate(void **dst, const void *src);
// object: method dispatch tables
#define ctor(obj) obj_method0(obj, ctor) // ctor[obj_typeid(obj)](obj)
#define dtor(obj) obj_method0(obj, dtor) // dtor[obj_typeid(obj)](obj)
API extern void (*ctor[256])(); ///-
API extern void (*dtor[256])(); ///-
// object: syntax sugars
#define obj_malloc(sz, ...) obj_initialize((void**)MALLOC( sizeof(void*)+sz), stringf("\1untyped\n%s\n", "" #__VA_ARGS__))
#define obj_calloc(sz, ...) obj_initialize((void**)CALLOC(1,sizeof(void*)+sz), stringf("\1untyped\n%s\n", "" #__VA_ARGS__))
#define obj_new0(type) obj_new(type, 0)
#define obj_new(type, ...) ( \
obj_tmpalloc = obj_initialize((void**)CALLOC(1, sizeof(void*)+sizeof(type)), stringf("%c" #type "\n", (char)obj_typeid_from_name(#type))), \
(*(type*)obj_tmpalloc = (type){ __VA_ARGS__ }), \
ctor(obj_tmpalloc), \
(type*)obj_tmpalloc )
#define obj_override(class, method) obj_override(#class, (void(**)())method, (void(*)())class##_##method)
#define obj_method0(obj, method) method[obj_typeid(obj)]((obj))
#define obj_method(obj, method, ...) method[obj_typeid(obj)]((obj), __VA_ARGS__)
#define obj_printf(obj, ...) obj_printf(obj, va(__VA_ARGS__))
#define obj_extend(dstclass, srcclass) obj_extend(#dstclass, #srcclass)
// object: implementation details
// https://stackoverflow.com/questions/16198700/using-the-extra-16-bits-in-64-bit-pointers (note: using 19-bits here)
#define OBJBOX(ptr, payload16) (void*)(((long long unsigned)(payload16) << 48) | (long long unsigned)(ptr))
#define OBJUNBOX(ptr) (void*)((long long unsigned)(ptr) & 0x0000FFFFFFFFFFFFull)
#define OBJPAYLOAD16(ptr) (((long long unsigned)(ptr)) >> 48)
#define OBJPAYLOAD3(ptr) (((long long unsigned)(ptr)) & 7)
API void* obj_initialize( void **ptr, char *type_and_info );
static __thread void *obj_tmpalloc;
#define double2(x,y) M_CAST(double2, (double)(x), (double)(y) )
#define double3(x,y,z) M_CAST(double3, (double)(x), (double)(y), (double)(z) )
#define double4(x,y,z,w) M_CAST(double4, (double)(x), (double)(y), (double)(z), (double)(w) )

View File

@ -2188,7 +2188,7 @@ bool spine_(spine_t *t, const char *file_json, const char *file_atlas, unsigned
array_push(t->skins[i].rects, t->skins[0].rects[j]);
}
}
// @leak @fixme: free(t->skins[0])
// @leak @fixme: FREE(t->skins[0])
for( int i = 0; i < array_count(t->skins)-1; ++i ) {
t->skins[i] = t->skins[i+1];
}

View File

@ -182,23 +182,26 @@ void camera_orbit( camera_t *cam, float yaw, float pitch, float inc_distance ) {
int ui_camera( camera_t *cam ) {
int changed = 0;
changed |= ui_float("Speed", &cam->speed);
ui_separator();
changed |= ui_bool("Damping", &cam->damping);
if( !cam->damping ) ui_disable();
changed |= ui_slider2("Move friction", &cam->move_friction, va("%5.2f", cam->move_friction));
changed |= ui_slider2("Move damping", &cam->move_damping, va("%5.2f", cam->move_damping));
changed |= ui_slider2("View driction", &cam->look_friction, va("%5.2f", cam->look_friction));
changed |= ui_slider2("View damping", &cam->look_damping, va("%5.2f", cam->look_damping));
changed |= ui_slider2("Move friction", &cam->move_friction, va("%5.3f", cam->move_friction));
changed |= ui_slider2("Move damping", &cam->move_damping, va("%5.3f", cam->move_damping));
changed |= ui_slider2("View friction", &cam->look_friction, va("%5.3f", cam->look_friction));
changed |= ui_slider2("View damping", &cam->look_damping, va("%5.3f", cam->look_damping));
if( !cam->damping ) ui_enable();
ui_separator();
changed |= ui_float3("Position", &cam->position.x);
changed |= ui_float3("LookDir", &cam->lookdir.x);
changed |= ui_float3("UpDir", &cam->updir.x);
changed |= ui_float("Speed", &cam->speed);
changed |= ui_float3("Position", cam->position.v3);
changed |= ui_float3("LookDir", cam->lookdir.v3);
changed |= ui_float3("UpDir", cam->updir.v3);
ui_disable();
changed |= ui_mat44("View matrix", cam->view);
ui_enable();
ui_separator();
changed |= ui_float("FOV (degrees)", &cam->fov);
ui_disable();
changed |= ui_mat44("Projection matrix", cam->proj);
ui_enable();
return changed;
}

View File

@ -331,3 +331,50 @@ array(uint32_t) string32( const char *utf8 ) {
return out[slot];
}
// -----------------------------------------------------------------------------
// quarks
unsigned quark_intern( quarks_db *quarks, const char *string ) {
if( !*quarks ) {
// copy null string on init
array_push(*quarks, '\0');
}
if( string && string[0] ) {
int slen = strlen(string)+1;
int qlen = array_count(*quarks);
array_resize(*quarks, qlen + slen);
memcpy( array_back(*quarks) + 1 - slen, string, slen );
return qlen;
}
return 0;
}
const char *quark_string( quarks_db *quarks, unsigned key ) {
assert( *quarks );
return *quarks + key;
}
static __thread quarks_db qdb = 0;
unsigned intern( const char *string ) {
return quark_intern( &qdb, string );
}
const char *quark( unsigned key ) {
return quark_string( &qdb, key );
}
#if 0
AUTORUN {
assert( !intern(NULL) ); // quark #0, cannot intern null string
assert( !intern("") ); // quark #0, ok to intern empty string
assert( !quark(0)[0] ); // empty string for quark #0
unsigned q1 = intern("Hello"); // -> quark #1
unsigned q2 = intern("cruel"); // -> quark #2
unsigned q3 = intern("world."); // -> quark #3
char buf[256];
sprintf(buf, "%s %s %s", quark(q1), quark(q2), quark(q3));
assert( !strcmp("Hello cruel world.", buf) );
assert(~puts("Ok"));
}
#endif

View File

@ -73,3 +73,14 @@ API char* strjoin(array(char*) list, const char *separator);
API char * string8(const wchar_t *str); /// convert from wchar16(win) to utf8/ascii
API array(uint32_t) string32( const char *utf8 ); /// convert from utf8 to utf32
// -----------------------------------------------------------------------------
// ## string interning (quarks)
// - rlyeh, public domain.
unsigned intern( const char *string );
const char *quark( unsigned key );
typedef array(char) quarks_db;
unsigned quark_intern( quarks_db*, const char *string );
const char *quark_string( quarks_db*, unsigned key );

View File

@ -252,9 +252,9 @@ int callstackf( FILE *fp, int traces ) {
return 0;
}
// signals --------------------------------------------------------------------
// trap signals ---------------------------------------------------------------
const char *signal_name(int signal) {
const char *trap_name(int signal) {
if(signal == SIGABRT) return "SIGABRT - \"abort\", abnormal termination";
if(signal == SIGFPE) return "SIGFPE - floating point exception";
if(signal == SIGILL) return "SIGILL - \"illegal\", invalid instruction";
@ -266,49 +266,41 @@ const char *signal_name(int signal) {
ifndef(win32, if(signal == SIGQUIT) return "SIGQUIT");
return "??";
}
void signal_handler_ignore(int signal_) {
signal(signal_, signal_handler_ignore);
void trap_on_ignore(int signal_) {
signal(signal_, trap_on_ignore);
}
void signal_handler_quit(int signal) {
// fprintf(stderr, "Ok: caught signal %s (%d)\n", signal_name(signal), signal);
void trap_on_quit(int signal) {
// fprintf(stderr, "Ok: caught signal %s (%d)\n", trap_name(signal), signal);
exit(0);
}
void signal_handler_abort(int signal) {
fprintf(stderr, "Error: unexpected signal %s (%d)\n%s\n", signal_name(signal), signal, callstack(16));
void trap_on_abort(int signal) {
fprintf(stderr, "Error: unexpected signal %s (%d)\n%s\n", trap_name(signal), signal, callstack(16));
exit(-1);
}
void signal_handler_debug(int signal) {
void trap_on_debug(int signal) {
breakpoint("Error: unexpected signal");
fprintf(stderr, "Error: unexpected signal %s (%d)\n%s\n", signal_name(signal), signal, callstack(16));
fprintf(stderr, "Error: unexpected signal %s (%d)\n%s\n", trap_name(signal), signal, callstack(16));
exit(-1);
}
void signal_hooks(void) {
void trap_install(void) {
// expected signals
signal(SIGINT, signal_handler_quit);
signal(SIGTERM, signal_handler_quit);
ifndef(win32, signal(SIGQUIT, signal_handler_quit));
signal(SIGINT, trap_on_quit);
signal(SIGTERM, trap_on_quit);
ifndef(win32, signal(SIGQUIT, trap_on_quit));
// unexpected signals
signal(SIGABRT, signal_handler_abort);
signal(SIGFPE, signal_handler_abort);
signal(SIGILL, signal_handler_abort);
signal(SIGSEGV, signal_handler_abort);
ifndef(win32, signal(SIGBUS, signal_handler_abort));
ifdef(linux, signal(SIGSTKFLT, signal_handler_abort));
signal(SIGABRT, trap_on_abort);
signal(SIGFPE, trap_on_abort);
signal(SIGILL, trap_on_abort);
signal(SIGSEGV, trap_on_abort);
ifndef(win32, signal(SIGBUS, trap_on_abort));
ifdef(linux, signal(SIGSTKFLT, trap_on_abort));
}
#ifdef SIGNAL_DEMO
void crash() {
char *ptr = 0;
*ptr = 1;
#ifdef TRAP_DEMO
AUTORUN {
trap_install();
app_crash(); // app_hang();
}
void hang() {
for(;;);
}
int main(int argc, char **argv) {
signal_hooks();
crash(); // hang();
}
#define main main__
#endif
// endian ----------------------------------------------------------------------
@ -498,230 +490,6 @@ int app_battery() {
#endif
// ----------------------------------------------------------------------------
// time
#if 0
uint64_t time_gpu() {
GLint64 t = 123456789;
glGetInteger64v(GL_TIMESTAMP, &t);
return (uint64_t)t;
}
#endif
uint64_t date() {
time_t epoch = time(0);
struct tm *ti = localtime(&epoch);
return atoi64(va("%04d%02d%02d%02d%02d%02d",ti->tm_year+1900,ti->tm_mon+1,ti->tm_mday,ti->tm_hour,ti->tm_min,ti->tm_sec));
}
char *date_string() {
time_t epoch = time(0);
struct tm *ti = localtime(&epoch);
return va("%04d-%02d-%02d %02d:%02d:%02d",ti->tm_year+1900,ti->tm_mon+1,ti->tm_mday,ti->tm_hour,ti->tm_min,ti->tm_sec);
}
uint64_t date_epoch() {
time_t epoch = time(0);
return epoch;
}
#if 0
double time_ss() {
return glfwGetTime();
}
double time_ms() {
return glfwGetTime() * 1000.0;
}
uint64_t time_us() {
return (uint64_t)(glfwGetTime() * 1000000.0); // @fixme: use a high resolution timer instead, or time_gpu below
}
uint64_t sleep_us(uint64_t us) { // @fixme: use a high resolution sleeper instead
return sleep_ms( us / 1000.0 );
}
double sleep_ms(double ms) {
double now = time_ms();
if( ms <= 0 ) {
#if is(win32)
Sleep(0); // yield
#else
usleep(0);
#endif
} else {
#if is(win32)
Sleep(ms);
#else
usleep(ms * 1000);
#endif
}
return time_ms() - now;
}
double sleep_ss(double ss) {
return sleep_ms( ss * 1000 ) / 1000.0;
}
#endif
// high-perf functions
#define TIMER_E3 1000ULL
#define TIMER_E6 1000000ULL
#define TIMER_E9 1000000000ULL
#ifdef CLOCK_MONOTONIC_RAW
#define TIME_MONOTONIC CLOCK_MONOTONIC_RAW
#elif defined CLOCK_MONOTONIC
#define TIME_MONOTONIC CLOCK_MONOTONIC
#else
// #define TIME_MONOTONIC CLOCK_REALTIME // untested
#endif
static uint64_t nanotimer(uint64_t *out_freq) {
if( out_freq ) {
#if is(win32)
LARGE_INTEGER li;
QueryPerformanceFrequency(&li);
*out_freq = li.QuadPart;
//#elif is(ANDROID)
// *out_freq = CLOCKS_PER_SEC;
#elif defined TIME_MONOTONIC
*out_freq = TIMER_E9;
#else
*out_freq = TIMER_E6;
#endif
}
#if is(win32)
LARGE_INTEGER li;
QueryPerformanceCounter(&li);
return (uint64_t)li.QuadPart;
//#elif is(ANDROID)
// return (uint64_t)clock();
#elif defined TIME_MONOTONIC
struct timespec ts;
clock_gettime(TIME_MONOTONIC, &ts);
return (TIMER_E9 * (uint64_t)ts.tv_sec) + ts.tv_nsec;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
return (TIMER_E6 * (uint64_t)tv.tv_sec) + tv.tv_usec;
#endif
}
uint64_t time_ns() {
static uint64_t epoch = 0;
static uint64_t freq = 0;
if( !freq ) {
epoch = nanotimer(&freq);
}
uint64_t a = nanotimer(NULL) - epoch;
uint64_t b = TIMER_E9;
uint64_t c = freq;
// Computes (a*b)/c without overflow, as long as both (a*b) and the overall result fit into 64-bits.
// [ref] https://github.com/rust-lang/rust/blob/3809bbf47c8557bd149b3e52ceb47434ca8378d5/src/libstd/sys_common/mod.rs#L124
uint64_t q = a / c;
uint64_t r = a % c;
return q * b + r * b / c;
}
uint64_t time_us() {
return time_ns() / TIMER_E3;
}
uint64_t time_ms() {
return time_ns() / TIMER_E6;
}
double time_ss() {
return time_ns() / 1e9; // TIMER_E9;
}
double time_mm() {
return time_ss() / 60;
}
double time_hh() {
return time_mm() / 60;
}
void sleep_ns( double ns ) {
#if is(win32)
if( ns >= 100 ) {
LARGE_INTEGER li; // Windows sleep in 100ns units
HANDLE timer = CreateWaitableTimer(NULL, TRUE, NULL);
li.QuadPart = (LONGLONG)(__int64)(-ns/100); // Negative for relative time
SetWaitableTimer(timer, &li, 0, NULL, NULL, FALSE);
WaitForSingleObject(timer, INFINITE);
CloseHandle(timer);
#else
if( ns > 0 ) {
struct timespec wait = {0};
wait.tv_sec = ns / 1e9;
wait.tv_nsec = ns - wait.tv_sec * 1e9;
nanosleep(&wait, NULL);
#endif
} else {
#if is(win32)
Sleep(0); // yield, Sleep(0), SwitchToThread
#else
usleep(0);
#endif
}
}
void sleep_us( double us ) {
sleep_ns(us * 1e3);
}
void sleep_ms( double ms ) {
sleep_ns(ms * 1e6);
}
void sleep_ss( double ss ) {
sleep_ns(ss * 1e9);
}
// ----------------------------------------------------------------------------
// timer
struct timer_internal_t {
unsigned ms;
unsigned (*callback)(unsigned interval, void *arg);
void *arg;
thread_ptr_t thd;
};
static int timer_func(void *arg) {
struct timer_internal_t *p = (struct timer_internal_t*)arg;
sleep_ms( p->ms );
for( ;; ) {
unsigned then = time_ms();
p->ms = p->callback(p->ms, p->arg);
if( !p->ms ) break;
unsigned now = time_ms();
unsigned lapse = now - then;
int diff = p->ms - lapse;
sleep_ms( diff <= 0 ? 0 : diff );
}
thread_exit(0);
return 0;
}
static __thread array(struct timer_internal_t *) timers;
unsigned timer(unsigned ms, unsigned (*callback)(unsigned ms, void *arg), void *arg) {
struct timer_internal_t *p = MALLOC( sizeof(struct timer_internal_t) );
p->ms = ms;
p->callback = callback;
p->arg = arg;
p->thd = thread_init( timer_func, p, "", 0 );
array_push(timers, p);
return array_count(timers);
}
void timer_destroy(unsigned i) {
if( i-- ) {
thread_join(timers[i]->thd);
thread_term(timers[i]->thd);
FREE(timers[i]);
timers[i] = 0;
}
}
// ----------------------------------------------------------------------------
// argc/v
@ -1071,3 +839,75 @@ void thread_destroy( void *thd ) {
thread_term(thd);
}
void app_hang() {
for(;;);
}
void app_crash() {
int *p = 0;
*p = 42;
}
void app_beep() {
fputc('\x7', stdout);
}
void app_singleton(const char *guid) {
#ifdef _WIN32
do_once {
char buffer[128];
snprintf(buffer, 128, "Global\\{%s}", guid);
static HANDLE app_mutex = 0;
app_mutex = CreateMutexA(NULL, FALSE, buffer);
if( ERROR_ALREADY_EXISTS == GetLastError() ) {
exit(-1);
}
}
#endif
}
static
bool app_open_folder(const char *file) {
char buf[1024];
#ifdef _WIN32
snprintf(buf, sizeof(buf), "start \"\" \"%s\"", file);
#elif __APPLE__
snprintf(buf, sizeof(buf), "%s \"%s\"", is_directory(file) ? "open" : "open --reveal", file);
#else
snprintf(buf, sizeof(buf), "xdg-open \"%s\"", file);
#endif
return system(buf) == 0;
}
static
bool app_open_file(const char *file) {
char buf[1024];
#ifdef _WIN32
snprintf(buf, sizeof(buf), "start \"\" \"%s\"", file);
#elif __APPLE__
snprintf(buf, sizeof(buf), "open \"%s\"", file);
#else
snprintf(buf, sizeof(buf), "xdg-open \"%s\"", file);
#endif
return system(buf) == 0;
}
static
bool app_open_url(const char *url) {
return app_open_file(url);
}
bool app_open(const char *link) {
if( file_directory(link) ) return app_open_folder(link);
if( file_exist(link) ) return app_open_file(link);
return app_open_url(link);
}
// ----------------------------------------------------------------------------
// tests
static __thread int test_oks, test_errs, test_once;
static void test_exit(void) { printf("%d/%d tests passed\n", test_oks, test_oks+test_errs); }
int (test)(const char *file, int line, const char *expr, bool result) {
test_once = test_once || !(atexit)(test_exit);
test_oks += result, test_errs += !result;
return fputc("F."[result], stdout) && (result || fprintf(stderr, "(%s %s:%d)", expr, file, line) );
}

View File

@ -12,19 +12,19 @@ API int argc();
API char* argv(int);
API int flag(const char *commalist); // --arg // app_flag?
API const char* option(const char *commalist, const char *defaults); // --arg=value or --arg value
API int optioni(const char *commalist, int defaults); // argvi() ?
API float optionf(const char *commalist, float defaults); // app_option?
API const char* option(const char *commalist, const char *defaults); // --arg=string or --arg string
API int optioni(const char *commalist, int defaults); // --arg=integer or --arg integer // argvi() ?
API float optionf(const char *commalist, float defaults); // --arg=float or --arg float // flagf() ?
API void tty_color(unsigned color);
API void tty_reset();
API void tty_attach();
API void tty_detach();
API void tty_color(unsigned color);
API void tty_reset();
API const char* app_exec(const char *command); // returns ("%15d %s", retcode, output_last_line)
API int app_spawn(const char *command);
API int app_cores();
API int app_battery(); /// return battery level [1..100]. also positive if charging (+), negative if discharging (-), and 0 if no battery is present.
API int app_battery(); /// returns battery level [1..100]. also positive if charging (+), negative if discharging (-), and 0 if no battery is present.
API const char* app_name();
API const char* app_path();
@ -32,22 +32,11 @@ API const char* app_cache();
API const char* app_temp();
API const char* app_cmdline();
API uint64_t date(); // YYYYMMDDhhmmss
API uint64_t date_epoch(); // linux epoch
API char* date_string(); // "YYYY-MM-DD hh:mm:ss"
API double time_hh();
API double time_mm();
API double time_ss();
API uint64_t time_ms();
API uint64_t time_us();
API uint64_t time_ns();
API void sleep_ss(double ss);
API void sleep_ms(double ms);
API void sleep_us(double us);
API void sleep_ns(double us);
API unsigned timer(unsigned ms, unsigned (*callback)(unsigned ms, void *arg), void *arg);
API void timer_destroy(unsigned timer_handle);
API void app_beep();
API void app_hang();
API void app_crash();
API void app_singleton(const char *guid);
API bool app_open(const char *folder_file_or_url);
API char* callstack( int traces ); // write callstack into a temporary string. <0 traces to invert order. do not free().
API int callstackf( FILE *fp, int traces ); // write callstack to file. <0 traces to invert order.
@ -59,12 +48,12 @@ API void hexdumpf( FILE *fp, const void *ptr, unsigned len, int width );
API void breakpoint(const char *optional_reason);
API bool has_debugger();
API void signal_hooks(void);
API void signal_handler_ignore(int signal);
API void signal_handler_quit(int signal);
API void signal_handler_abort(int signal);
API void signal_handler_debug(int signal);
API const char *signal_name(int signal); // helper util
API void trap_install(void);
API const char *trap_name(int signal); // helper util
API void trap_on_ignore(int signal); // helper util
API void trap_on_quit(int signal); // helper util
API void trap_on_abort(int signal); // helper util
API void trap_on_debug(int signal); // helper util
API uint16_t lil16(uint16_t n); // swap16 as lil
API uint32_t lil32(uint32_t n); // swap32 as lil
@ -93,3 +82,7 @@ API int (PANIC)(const char *error, const char *file, int line);
#define PRINTF(...) PRINTF(va(__VA_ARGS__), 1[#__VA_ARGS__] == '!' ? callstack(+48) : "", __FILE__, __LINE__, __FUNCTION__)
API int (PRINTF)(const char *text, const char *stack, const char *file, int line, const char *function);
#define test(expr) test(__FILE__,__LINE__,#expr,!!(expr))
API int (test)(const char *file, int line, const char *expr, bool result);
// AUTORUN { test(1<2); }

View File

@ -0,0 +1,248 @@
// ----------------------------------------------------------------------------
// time
#if 0
uint64_t time_gpu() {
GLint64 t = 123456789;
glGetInteger64v(GL_TIMESTAMP, &t);
return (uint64_t)t;
}
#endif
uint64_t date() {
time_t epoch = time(0);
struct tm *ti = localtime(&epoch);
return atoi64(va("%04d%02d%02d%02d%02d%02d",ti->tm_year+1900,ti->tm_mon+1,ti->tm_mday,ti->tm_hour,ti->tm_min,ti->tm_sec));
}
char *date_string() {
time_t epoch = time(0);
struct tm *ti = localtime(&epoch);
return va("%04d-%02d-%02d %02d:%02d:%02d",ti->tm_year+1900,ti->tm_mon+1,ti->tm_mday,ti->tm_hour,ti->tm_min,ti->tm_sec);
}
uint64_t date_epoch() {
time_t epoch = time(0);
return epoch;
}
#if 0
double time_ss() {
return glfwGetTime();
}
double time_ms() {
return glfwGetTime() * 1000.0;
}
uint64_t time_us() {
return (uint64_t)(glfwGetTime() * 1000000.0); // @fixme: use a high resolution timer instead, or time_gpu below
}
uint64_t sleep_us(uint64_t us) { // @fixme: use a high resolution sleeper instead
return sleep_ms( us / 1000.0 );
}
double sleep_ms(double ms) {
double now = time_ms();
if( ms <= 0 ) {
#if is(win32)
Sleep(0); // yield
#else
usleep(0);
#endif
} else {
#if is(win32)
Sleep(ms);
#else
usleep(ms * 1000);
#endif
}
return time_ms() - now;
}
double sleep_ss(double ss) {
return sleep_ms( ss * 1000 ) / 1000.0;
}
#endif
// high-perf functions
#define TIMER_E3 1000ULL
#define TIMER_E6 1000000ULL
#define TIMER_E9 1000000000ULL
#ifdef CLOCK_MONOTONIC_RAW
#define TIME_MONOTONIC CLOCK_MONOTONIC_RAW
#elif defined CLOCK_MONOTONIC
#define TIME_MONOTONIC CLOCK_MONOTONIC
#else
// #define TIME_MONOTONIC CLOCK_REALTIME // untested
#endif
static uint64_t nanotimer(uint64_t *out_freq) {
if( out_freq ) {
#if is(win32)
LARGE_INTEGER li;
QueryPerformanceFrequency(&li);
*out_freq = li.QuadPart;
//#elif is(ANDROID)
// *out_freq = CLOCKS_PER_SEC;
#elif defined TIME_MONOTONIC
*out_freq = TIMER_E9;
#else
*out_freq = TIMER_E6;
#endif
}
#if is(win32)
LARGE_INTEGER li;
QueryPerformanceCounter(&li);
return (uint64_t)li.QuadPart;
//#elif is(ANDROID)
// return (uint64_t)clock();
#elif defined TIME_MONOTONIC
struct timespec ts;
clock_gettime(TIME_MONOTONIC, &ts);
return (TIMER_E9 * (uint64_t)ts.tv_sec) + ts.tv_nsec;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
return (TIMER_E6 * (uint64_t)tv.tv_sec) + tv.tv_usec;
#endif
}
uint64_t time_ns() {
static uint64_t epoch = 0;
static uint64_t freq = 0;
if( !freq ) {
epoch = nanotimer(&freq);
}
uint64_t a = nanotimer(NULL) - epoch;
uint64_t b = TIMER_E9;
uint64_t c = freq;
// Computes (a*b)/c without overflow, as long as both (a*b) and the overall result fit into 64-bits.
// [ref] https://github.com/rust-lang/rust/blob/3809bbf47c8557bd149b3e52ceb47434ca8378d5/src/libstd/sys_common/mod.rs#L124
uint64_t q = a / c;
uint64_t r = a % c;
return q * b + r * b / c;
}
uint64_t time_us() {
return time_ns() / TIMER_E3;
}
uint64_t time_ms() {
return time_ns() / TIMER_E6;
}
double time_ss() {
return time_ns() / 1e9; // TIMER_E9;
}
double time_mm() {
return time_ss() / 60;
}
double time_hh() {
return time_mm() / 60;
}
void sleep_ns( double ns ) {
#if is(win32)
if( ns >= 100 ) {
LARGE_INTEGER li; // Windows sleep in 100ns units
HANDLE timer = CreateWaitableTimer(NULL, TRUE, NULL);
li.QuadPart = (LONGLONG)(__int64)(-ns/100); // Negative for relative time
SetWaitableTimer(timer, &li, 0, NULL, NULL, FALSE);
WaitForSingleObject(timer, INFINITE);
CloseHandle(timer);
#else
if( ns > 0 ) {
struct timespec wait = {0};
wait.tv_sec = ns / 1e9;
wait.tv_nsec = ns - wait.tv_sec * 1e9;
nanosleep(&wait, NULL);
#endif
} else {
#if is(win32)
Sleep(0); // yield, Sleep(0), SwitchToThread
#else
usleep(0);
#endif
}
}
void sleep_us( double us ) {
sleep_ns(us * 1e3);
}
void sleep_ms( double ms ) {
sleep_ns(ms * 1e6);
}
void sleep_ss( double ss ) {
sleep_ns(ss * 1e9);
}
// ----------------------------------------------------------------------------
// timer
struct timer_internal_t {
unsigned ms;
unsigned (*callback)(unsigned interval, void *arg);
void *arg;
thread_ptr_t thd;
};
static int timer_func(void *arg) {
struct timer_internal_t *p = (struct timer_internal_t*)arg;
sleep_ms( p->ms );
for( ;; ) {
unsigned then = time_ms();
p->ms = p->callback(p->ms, p->arg);
if( !p->ms ) break;
unsigned now = time_ms();
unsigned lapse = now - then;
int diff = p->ms - lapse;
sleep_ms( diff <= 0 ? 0 : diff );
}
thread_exit(0);
return 0;
}
static __thread array(struct timer_internal_t *) timers;
unsigned timer(unsigned ms, unsigned (*callback)(unsigned ms, void *arg), void *arg) {
struct timer_internal_t *p = MALLOC( sizeof(struct timer_internal_t) );
p->ms = ms;
p->callback = callback;
p->arg = arg;
p->thd = thread_init( timer_func, p, "", 0 );
array_push(timers, p);
return array_count(timers);
}
void timer_destroy(unsigned i) {
if( i-- ) {
thread_join(timers[i]->thd);
thread_term(timers[i]->thd);
FREE(timers[i]);
timers[i] = 0;
}
}
// ----------------------------------------------------------------------------
// guid
//typedef vec3i guid;
guid guid_create() {
static __thread unsigned counter = 0;
static uint64_t appid = 0; do_once appid = hash_str(app_name());
union conv {
struct {
unsigned timestamp : 32;
unsigned threadid : 16; // inverted order in LE
unsigned appid : 16; //
unsigned counter : 32;
};
vec3i v3;
} c;
c.timestamp = date_epoch() - 0x65000000;
c.appid = (unsigned)appid;
c.threadid = (unsigned)(uintptr_t)thread_current_thread_id();
c.counter = ++counter;
return c.v3;
}

View File

@ -0,0 +1,44 @@
// -----------------------------------------------------------------------------
// time framework utils
// - rlyeh, public domain.
API uint64_t date(); // YYYYMMDDhhmmss
API uint64_t date_epoch(); // linux epoch
API char* date_string(); // "YYYY-MM-DD hh:mm:ss"
API double time_hh();
API double time_mm();
API double time_ss();
API uint64_t time_ms();
API uint64_t time_us();
API uint64_t time_ns();
API void sleep_ss(double ss);
API void sleep_ms(double ms);
API void sleep_us(double us);
API void sleep_ns(double us);
API unsigned timer(unsigned ms, unsigned (*callback)(unsigned ms, void *arg), void *arg);
API void timer_destroy(unsigned timer_handle);
// time sortable unique identifier (similar to ksuid/tuid; others: sno/xid/cuid/ulid)
// - rlyeh, public domain.
//
// also similar to a mongo object id, 12 bytes as follows:
// - 4-byte timestamp (ss). epoch: Tuesday, 12 September 2023 6:06:56
// - 2-byte (machine or app hash)
// - 2-byte (thread-id)
// - 4-byte (rand counter, that gets increased at every id creation)
typedef vec3i guid;
API guid guid_create();
/*
AUTORUN {
guid g1 = guid_create();
guid g2 = guid_create();
print3i(g1);
hexdump(&g1, sizeof(g1));
print3i(g2);
hexdump(&g2, sizeof(g2));
}
*/

View File

@ -194,6 +194,7 @@ table[NK_COLOR_CHART_COLOR_HIGHLIGHT] = hover_hue; // nk_rgba(255, 0, 0, 255);
s->window.scrollbar_size = nk_vec2(5,5);
s->property.rounding = 0;
s->combo.border = 0;
s->combo.button_padding.x = -18;
s->button.border = 1;
s->edit.border = 0;
@ -1905,11 +1906,16 @@ int ui_clampf(const char *label, float *v, float minf, float maxf) {
return prev != v[0];
}
static bool ui_float_sign = 0;
int ui_float2(const char *label, float *v) {
nk_layout_row_dynamic(ui_ctx, 0, 2);
ui_label_(label, NK_TEXT_LEFT);
char *buffer = va("%.2f, %.2f", v[0], v[1]);
char *buffer = ui_float_sign ?
--ui_float_sign, va("%+.3f %+.3f", v[0], v[1]) :
va("%.3f, %.3f", v[0], v[1]);
if (nk_combo_begin_label(ui_ctx, buffer, nk_vec2(200,200))) {
nk_layout_row_dynamic(ui_ctx, 0, 1);
float prev0 = v[0]; nk_property_float(ui_ctx, "#X:", -FLT_MAX, &v[0], FLT_MAX, 1,0.5f);
@ -1924,7 +1930,10 @@ int ui_float3(const char *label, float *v) {
nk_layout_row_dynamic(ui_ctx, 0, 2);
ui_label_(label, NK_TEXT_LEFT);
char *buffer = va("%.2f, %.2f, %.2f", v[0], v[1], v[2]);
char *buffer = ui_float_sign ?
--ui_float_sign, va("%+.2f %+.2f %+.2f", v[0], v[1], v[2]) :
va("%.2f, %.2f, %.2f", v[0], v[1], v[2]);
if (nk_combo_begin_label(ui_ctx, buffer, nk_vec2(200,200))) {
nk_layout_row_dynamic(ui_ctx, 0, 1);
float prev0 = v[0]; nk_property_float(ui_ctx, "#X:", -FLT_MAX, &v[0], FLT_MAX, 1,0.5f);
@ -1940,7 +1949,10 @@ int ui_float4(const char *label, float *v) {
nk_layout_row_dynamic(ui_ctx, 0, 2);
ui_label_(label, NK_TEXT_LEFT);
char *buffer = va("%.2f, %.2f, %.2f, %.2f", v[0], v[1], v[2], v[3]);
char *buffer = ui_float_sign ?
--ui_float_sign, va("%+.2f %+.2f %+.2f %+.2f", v[0], v[1], v[2], v[3]) :
va("%.2f,%.2f,%.2f,%.2f", v[0], v[1], v[2], v[3]);
if (nk_combo_begin_label(ui_ctx, buffer, nk_vec2(200,200))) {
nk_layout_row_dynamic(ui_ctx, 0, 1);
float prev0 = v[0]; nk_property_float(ui_ctx, "#X:", -FLT_MAX, &v[0], FLT_MAX, 1,0.5f);
@ -1950,10 +1962,12 @@ int ui_float4(const char *label, float *v) {
nk_combo_end(ui_ctx);
return prev0 != v[0] || prev1 != v[1] || prev2 != v[2] || prev3 != v[3];
}
return 0;
}
int ui_mat33(const char *label, float M[9]) {
ui_float_sign = 3;
int changed = 0;
changed |= ui_label(label);
changed |= ui_float3(NULL, M);
@ -1962,6 +1976,7 @@ int ui_mat33(const char *label, float M[9]) {
return changed;
}
int ui_mat34(const char *label, float M[12]) {
ui_float_sign = 3;
int changed = 0;
changed |= ui_label(label);
changed |= ui_float4(NULL, M);
@ -1970,6 +1985,7 @@ int ui_mat34(const char *label, float M[12]) {
return changed;
}
int ui_mat44(const char *label, float M[16]) {
ui_float_sign = 4;
int changed = 0;
changed |= ui_label(label);
changed |= ui_float4(NULL, M);

View File

@ -375,6 +375,7 @@ bool window_create_from_handle(void *handle, float scale, unsigned flags) {
glfwSwapInterval(interval);
const GLFWvidmode *mode = glfwGetVideoMode(monitor ? monitor : glfwGetPrimaryMonitor());
PRINTF("Build version: %s\n", BUILD_VERSION);
PRINTF("Monitor: %s (%dHz, vsync=%d)\n", glfwGetMonitorName(monitor ? monitor : glfwGetPrimaryMonitor()), mode->refreshRate, interval);
PRINTF("GPU device: %s\n", glGetString(GL_RENDERER));
PRINTF("GPU driver: %s\n", glGetString(GL_VERSION));
@ -482,7 +483,11 @@ char* window_stats() {
// @todo: print %used/%avail kib mem, %used/%avail objs as well
static char buf[256];
snprintf(buf, 256, "%s | boot %.2fs | %5.2ffps (%.2fms)%s%s", title, !boot_time ? now : boot_time, fps, (now - prev_frame) * 1000.f, cmdline[0] ? " | ":"", cmdline[0] ? cmdline:"");
snprintf(buf, 256, "%s%s%s%s | boot %.2fs | %5.2ffps (%.2fms)%s%s",
title, BUILD_VERSION[0] ? " (":"", BUILD_VERSION[0] ? BUILD_VERSION:"", BUILD_VERSION[0] ? ")":"",
!boot_time ? now : boot_time,
fps, (now - prev_frame) * 1000.f,
cmdline[0] ? " | ":"", cmdline[0] ? cmdline:"");
prev_frame = now;
++num_frames;

View File

@ -11688,7 +11688,7 @@ int gladLoadGL( GLADloadfunc load) {
#endif /* __EMSCRIPTEN__ */
#line 0
#line 1 "3rd_font_md.h"
#line 1 "3rd_icon_md.h"
// Generated by https://github.com/juliettef/IconFontCppHeaders script GenerateIconFontCppHeaders.py for languages C and C++
// from https://github.com/google/material-design-icons/raw/master/font/MaterialIcons-Regular.codepoints
// for use with https://github.com/google/material-design-icons/blob/master/font/MaterialIcons-Regular.ttf
@ -218881,7 +218881,7 @@ nk_combo_begin_text(struct nk_context *ctx, const char *selected, int len,
else
label.w = header.w - 2 * style->combo.content_padding.x;
nk_widget_text(&win->buffer, label, selected, len, &text,
NK_TEXT_LEFT, ctx->style.font);
NK_TEXT_CENTERED, ctx->style.font); //< @r-lyeh NK_TEXT_LEFT>CENTERED
/* draw open/close button */
if (draw_button_symbol)
@ -562506,4 +562506,5 @@ SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
#undef Token
#undef Table
#undef rehash
#undef NB
#endif // V4K_3RD

File diff suppressed because it is too large Load Diff

View File

@ -219,7 +219,7 @@ extern "C" {
#define countof(x) (int)(sizeof (x) / sizeof 0[x])
#define concat(a,b) conc4t(a,b)
#define conc4t(a,b) a##b
#define conc4t(a,b) a##b ///-
#define macro(name) concat(name, __LINE__)
#define defer(begin,end) for(int macro(i) = ((begin), 0); !macro(i); macro(i) = ((end), 1))
@ -247,16 +247,16 @@ extern "C" {
#define FILELINE __FILE__ ":" STRINGIZE(__LINE__)
#define STRINGIZE(x) STRINGIZ3(x)
#define STRINGIZ3(x) #x
#define STRINGIZ3(x) #x ///-
#define EXPAND(name, ...) EXPAND_QUOTE(EXPAND_JOIN(name, EXPAND_COUNT_ARGS(__VA_ARGS__)), (__VA_ARGS__))
#define EXPAND_QUOTE(x, y) x y
#define EXPAND_JOIN(name, count) EXPAND_J0IN(name, count)
#define EXPAND_J0IN(name, count) EXPAND_J01N(name, count)
#define EXPAND_J01N(name, count) name##count
#define EXPAND_COUNT_ARGS(...) EXPAND_ARGS((__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
#define EXPAND_ARGS(args) EXPAND_RETURN_COUNT args
#define EXPAND_RETURN_COUNT(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, count, ...) count
#define EXPAND_QUOTE(x, y) x y ///-
#define EXPAND_JOIN(name, count) EXPAND_J0IN(name, count) ///-
#define EXPAND_J0IN(name, count) EXPAND_J01N(name, count) ///-
#define EXPAND_J01N(name, count) name##count ///-
#define EXPAND_COUNT_ARGS(...) EXPAND_ARGS((__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) ///-
#define EXPAND_ARGS(args) EXPAND_RETURN_COUNT args ///-
#define EXPAND_RETURN_COUNT(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, count, ...) count ///-
#if is(cl) && !is(cpp)
#define INLINE __inline
@ -280,10 +280,13 @@ extern "C" {
// usage: #define vec2(...) C_CAST(vec2, __VA_ARGS__)
// typedef union vec2 { float X,Y; }; vec2 a = {0,1}, b = vec2(0,1);
#if is(cpp)
#define C_CAST(type, ...) ( type { __VA_ARGS__ } )
#else
#define C_CAST(type, ...) ((type){ __VA_ARGS__ } )
#define C_CAST(type, ...) ( ifdef(c,(type),type) { __VA_ARGS__ } )
// -----------------------------------------------------------------------------
// build info
#ifndef BUILD_VERSION
#define BUILD_VERSION ""
#endif
// -----------------------------------------------------------------------------
@ -291,15 +294,9 @@ extern "C" {
// win32 users would need to -DAPI=IMPORT/EXPORT as needed when using/building V4K as DLL.
#if is(win32)
#define IMPORT ifdef(gcc, __attribute__ ((dllimport)), __declspec(dllimport))
#define EXPORT ifdef(gcc, __attribute__ ((dllexport)), __declspec(dllexport))
#define IMPORT ifdef(win32, ifdef(gcc, __attribute__ ((dllimport)), __declspec(dllimport)))
#define EXPORT ifdef(win32, ifdef(gcc, __attribute__ ((dllexport)), __declspec(dllexport)))
#define STATIC
#else
#define IMPORT
#define EXPORT
#define STATIC
#endif
#ifndef API
#define API STATIC
@ -822,16 +819,32 @@ API bool (map_sort)(map* m);
API void (map_clear)(map* m);
// -----------------------------------------------------------------------------
// four-cc, eight-cc
// compile-time fourcc, eightcc
API unsigned cc4(const char *id);
API uint64_t cc8(const char *id);
API char *cc4str(unsigned cc);
API char *cc8str(uint64_t cc);
// fast path
#define cc4(abcd) ( *(unsigned*) #abcd " " ) // lil32() ?
#define cc8(abcdefgh) ( *(uint64_t*) #abcdefgh " " ) // lil64() ?
enum {
# define _(a,b,c,d,e) cc_##a, cc_##b, cc_##c, cc_##d, cc_##e
cc_1 = '1', _(2,3,4,5,6),_(7,8,9,0,_), cc_ = ' ',
cc_A = 'A', _(B,C,D,E,F),_(G,H,I,J,K),_(L,M,N,O,P),_(Q,R,S,T,U),_(V,W,X,Y,Z),
cc_a = 'a', _(b,c,d,e,f),_(g,h,i,j,k),_(l,m,n,o,p),_(q,r,s,t,u),_(v,w,x,y,z),
# undef _
};
#ifdef BIG
#define cc4(a,b,c,d) ((uint32_t)(cc_##a<<24) | (cc_##b<<16) | (cc_##c<<8) | (cc_##d<<0))
#define cc8(a,b,c,d,e,f,g,h) (((uint64_t)cc4(a,b,c,d) << 32ULL) | cc4(e,f,g,h))
#else
#define cc4(a,b,c,d) ((uint32_t)(cc_##d<<24) | (cc_##c<<16) | (cc_##b<<8) | (cc_##a<<0))
#define cc8(a,b,c,d,e,f,g,h) (((uint64_t)cc4(e,f,g,h) << 32ULL) | cc4(a,b,c,d))
#endif
#define cc3(a,b,c) cc4(,a,b,c)
#define cc5(a,b,c,d,e) cc6(,a,b,c,d,e)
#define cc6(a,b,c,d,e,f) cc7(,a,b,c,d,e,f)
#define cc7(a,b,c,d,e,f,g) cc8(,a,b,c,d,e,f,g)
#line 0
#line 1 "v4k_math.h"
@ -1167,6 +1180,8 @@ API bool unproject44(vec3 *out, vec3 xyd, vec4 viewport, mat44 mvp);
// ----------------------------------------------------------------------------
// debugging and utils
API void print2i( vec2i v );
API void print3i( vec3i v );
API void print2( vec2 v );
API void print3( vec3 v );
API void print4( vec4 v );
@ -2299,136 +2314,100 @@ API int64_t client_join(const char *ip, int port);
#line 1 "v4k_obj.h"
// -----------------------------------------------------------------------------
// C object framework (constructors/destructors, methods, rtti, refcounting)
// semantic versioning in a single byte (octal)
// - rlyeh, public domain.
//
// ## object api (low level)
// - single octal byte that represents semantic versioning (major.minor.patch).
// - allowed range [0000..0377] ( <-> [0..255] decimal )
// - comparison checks only major.minor tuple as per convention.
API int semver( int major, int minor, int patch );
API int semvercmp( int v1, int v2 );
#define SEMVER(major,minor,patch) (0100 * (major) + 010 * (minor) + (patch))
#define SEMVERCMP(v1,v2) (((v1) & 0110) - ((v2) & 0110))
#define SEMVERFMT "%03o"
// -----------------------------------------------------------------------------
// autorun initializers for C
// - rlyeh, public domain
//
// - [ ] make object from reflected type (factory)
// - [x] make object (if debug, include callstack as well)
// - [x] ctor method (optional, ref to constructor)
// - [x] dtor method (optional, ref to deleter)
// - [x] zero mem object
// - [x] object logger
// - [ ] iterate members in a struct
//
// - [x] clone/copy/mutate classes
// - [x] load/save objects from/to memory/disk
// - [ ] diff/patch objects
// - [ ] experimental: support for AoSoA layout (using objcnt, 3bits)
//
// ## object decomposition
//
// <---------|--------->
// OBJ-SHADOW (64-bits) | OBJ CONTENT (N bytes)
// +-----+-----+-------------+-----------+-----+-----+-----+-----+--
// |TYPE |REFS.| OBJ NAME | obj cnt | ... | ... | ... | ... | .
// +-----+-----+-------------+-----------+-----+-----+-----+-----+--
// \-16-bits--/\---45-bits--/\--3-bits--/\-------N-bytes-----------
//
// OBJ TYPE+NAME format:
// - [type] custom tags at 0x0
// - [1..N] name
// - [\n] blank separator
// - [comments, logger, infos, etc] << obj_printf();
//
// ## object limitations
// - 256 classes max
// - 256 references max
// - 8-byte overhead per object
// - 2 total allocs per object (could be flattened into 1 with some more work)
//
// @todo: obj_extend( "class_src", "class_dst" ); call[super(obj)]()
// @todo: preferred load/save format: [ver:1,user:2,type:1] ([eof|size:7/15/23/31][blob:N])+ [crc:1/2/3/4]
// @todo: more serious loading/saving spec
// note: based on code by Joe Lowe (public domain).
// note: XIU for C initializers, XCU for C++ initializers, XTU for C deinitializers
// object api (heap+rtti)
#ifdef __cplusplus
#define AUTORUN \
static void AUTORUN_U(f)(void); \
static const int AUTORUN_J(AUTORUN_U(f),__1) = (AUTORUN_U(f)(), 1); \
static void AUTORUN_U(f)(void)
#elif _MSC_VER
#define AUTORUN \
static void AUTORUN_U(f)(void); \
static int AUTORUN_J(AUTORUN_U(f),__1) (){ AUTORUN_U(f)(); return 0; } \
__pragma(section(".CRT$XIU", long, read)) \
__declspec(allocate(".CRT$XIU")) \
static int(* AUTORUN_J(AUTORUN_U(f),__2) )() = AUTORUN_J(AUTORUN_U(f),__1); \
static void AUTORUN_U(f)(void)
#else
#define AUTORUN \
__attribute__((constructor)) \
static void AUTORUN_U(f)(void)
#endif
API void* obj_malloc( int sz, ... );
API void* obj_calloc( int sz, ... );
API void obj_free( void *obj );
// join + unique macro utils
API bool obj_typeeq( const void *obj1, const void *obj2 );
API const char* obj_typeof( const void *obj );
API unsigned obj_typeid( const void *obj );
API unsigned obj_typeid_from_name( const char *name );
#define AUTORUN_j(a, b) a##b
#define AUTORUN_J(a, b) AUTORUN_j(a, b)
#define AUTORUN_U(x) AUTORUN_J(x, __LINE__)
// object api (ctor/dtor, refcounting, oop)
#if 0 // autorun demo
void byebye(void) { puts("seen after main()"); }
AUTORUN { puts("seen before main()"); }
AUTORUN { puts("seen before main() too"); atexit( byebye ); }
#endif
API void obj_new( const char *type, ... );
API void obj_del( void *obj );
// -----------------------------------------------------------------------------
// storage types. refer to vec2i/3i, vec2/3/4 if you plan to do math operations
API void* obj_ref( void *obj );
API void* obj_unref( void *obj );
typedef struct byte2 { uint8_t x,y; } byte2;
typedef struct byte3 { uint8_t x,y,z; } byte3;
typedef struct byte4 { uint8_t x,y,z,w; } byte4;
API void obj_extend( const char *dstclass, const char *srcclass );
API void obj_override( const char *objclass, void (**vtable)(), void(*fn)() );
typedef struct int2 { int x,y; } int2;
typedef struct int3 { int x,y,z; } int3;
typedef struct int4 { int x,y,z,w; } int4;
// object: serialize
typedef struct uint2 { unsigned int x,y; } uint2;
typedef struct uint3 { unsigned int x,y,z; } uint3;
typedef struct uint4 { unsigned int x,y,z,w; } uint4;
API unsigned obj_load(void *obj, const array(char) buffer);
API unsigned obj_load_file(void *obj, FILE *fp);
API unsigned obj_load_inplace(void *obj, const void *src, unsigned srclen);
typedef struct float2 { float x,y; } float2;
typedef struct float3 { float x,y,z; } float3;
typedef struct float4 { float x,y,z,w; } float4;
API array(char) obj_save(const void *obj); // empty if error. must array_free() after use
API unsigned obj_save_file(FILE *fp, const void *obj);
API unsigned obj_save_inplace(void *dst, unsigned cap, const void *obj);
typedef struct double2 { double x,y; } double2;
typedef struct double3 { double x,y,z; } double3;
typedef struct double4 { double x,y,z,w; } double4;
// object: utils
#define byte2(x,y) M_CAST(byte2, (uint8_t)(x), (uint8_t)(y) )
#define byte3(x,y,z) M_CAST(byte3, (uint8_t)(x), (uint8_t)(y), (uint8_t)(z) )
#define byte4(x,y,z,w) M_CAST(byte4, (uint8_t)(x), (uint8_t)(y), (uint8_t)(z), (uint8_t)(w) )
API unsigned obj_instances( const void *obj );
#define int2(x,y) M_CAST(int2, (int)(x), (int)(y) )
#define int3(x,y,z) M_CAST(int3, (int)(x), (int)(y), (int)(z) )
#define int4(x,y,z,w) M_CAST(int4, (int)(x), (int)(y), (int)(z), (int)(w) )
API void obj_zero( void *obj );
API unsigned obj_sizeof( const void *obj );
#define uint2(x,y) M_CAST(uint2, (unsigned)(x), (unsigned)(y) )
#define uint3(x,y,z) M_CAST(uint3, (unsigned)(x), (unsigned)(y), (unsigned)(z) )
#define uint4(x,y,z,w) M_CAST(uint4, (unsigned)(x), (unsigned)(y), (unsigned)(z), (unsigned)(w) )
API void obj_hexdump( const void *obj );
API void obj_hexdumpf( FILE *out, const void *obj );
#define float2(x,y) M_CAST(float2, (float)(x), (float)(y) )
#define float3(x,y,z) M_CAST(float3, (float)(x), (float)(y), (float)(z) )
#define float4(x,y,z,w) M_CAST(float4, (float)(x), (float)(y), (float)(z), (float)(w) )
API void obj_printf( void *obj, const char *text );
API const char* obj_output( const void *obj );
API void * obj_clone(const void *obj);
API void * obj_copy(void **dst, const void *src);
API void * obj_mutate(void **dst, const void *src);
// object: method dispatch tables
#define ctor(obj) obj_method0(obj, ctor) // ctor[obj_typeid(obj)](obj)
#define dtor(obj) obj_method0(obj, dtor) // dtor[obj_typeid(obj)](obj)
API extern void (*ctor[256])(); ///-
API extern void (*dtor[256])(); ///-
// object: syntax sugars
#define obj_malloc(sz, ...) obj_initialize((void**)MALLOC( sizeof(void*)+sz), stringf("\1untyped\n%s\n", "" #__VA_ARGS__))
#define obj_calloc(sz, ...) obj_initialize((void**)CALLOC(1,sizeof(void*)+sz), stringf("\1untyped\n%s\n", "" #__VA_ARGS__))
#define obj_new0(type) obj_new(type, 0)
#define obj_new(type, ...) ( \
obj_tmpalloc = obj_initialize((void**)CALLOC(1, sizeof(void*)+sizeof(type)), stringf("%c" #type "\n", (char)obj_typeid_from_name(#type))), \
(*(type*)obj_tmpalloc = (type){ __VA_ARGS__ }), \
ctor(obj_tmpalloc), \
(type*)obj_tmpalloc )
#define obj_override(class, method) obj_override(#class, (void(**)())method, (void(*)())class##_##method)
#define obj_method0(obj, method) method[obj_typeid(obj)]((obj))
#define obj_method(obj, method, ...) method[obj_typeid(obj)]((obj), __VA_ARGS__)
#define obj_printf(obj, ...) obj_printf(obj, va(__VA_ARGS__))
#define obj_extend(dstclass, srcclass) obj_extend(#dstclass, #srcclass)
// object: implementation details
// https://stackoverflow.com/questions/16198700/using-the-extra-16-bits-in-64-bit-pointers (note: using 19-bits here)
#define OBJBOX(ptr, payload16) (void*)(((long long unsigned)(payload16) << 48) | (long long unsigned)(ptr))
#define OBJUNBOX(ptr) (void*)((long long unsigned)(ptr) & 0x0000FFFFFFFFFFFFull)
#define OBJPAYLOAD16(ptr) (((long long unsigned)(ptr)) >> 48)
#define OBJPAYLOAD3(ptr) (((long long unsigned)(ptr)) & 7)
API void* obj_initialize( void **ptr, char *type_and_info );
static __thread void *obj_tmpalloc;
#define double2(x,y) M_CAST(double2, (double)(x), (double)(y) )
#define double3(x,y,z) M_CAST(double3, (double)(x), (double)(y), (double)(z) )
#define double4(x,y,z,w) M_CAST(double4, (double)(x), (double)(y), (double)(z), (double)(w) )
#line 0
#line 1 "v4k_profile.h"
@ -3461,42 +3440,23 @@ API char* strjoin(array(char*) list, const char *separator);
API char * string8(const wchar_t *str); /// convert from wchar16(win) to utf8/ascii
API array(uint32_t) string32( const char *utf8 ); /// convert from utf8 to utf32
// -----------------------------------------------------------------------------
// ## string interning (quarks)
// - rlyeh, public domain.
unsigned intern( const char *string );
const char *quark( unsigned key );
typedef array(char) quarks_db;
unsigned quark_intern( quarks_db*, const char *string );
const char *quark_string( quarks_db*, unsigned key );
#line 0
#line 1 "v4k_system.h"
#line 1 "v4k_time.h"
// -----------------------------------------------------------------------------
// system framework utils
// time framework utils
// - rlyeh, public domain.
//
// Note: Windows users add `/Zi` compilation flags, else add `-g` and/or `-ldl` flags
// Note: If you are linking your binary using GNU ld you need to add --export-dynamic
API void* thread( int (*thread_func)(void* user_data), void* user_data );
API void thread_destroy( void *thd );
API int argc();
API char* argv(int);
API int flag(const char *commalist); // --arg // app_flag?
API const char* option(const char *commalist, const char *defaults); // --arg=value or --arg value
API int optioni(const char *commalist, int defaults); // argvi() ?
API float optionf(const char *commalist, float defaults); // app_option?
API void tty_color(unsigned color);
API void tty_reset();
API void tty_attach();
API void tty_detach();
API const char* app_exec(const char *command); // returns ("%15d %s", retcode, output_last_line)
API int app_spawn(const char *command);
API int app_cores();
API int app_battery(); /// return battery level [1..100]. also positive if charging (+), negative if discharging (-), and 0 if no battery is present.
API const char* app_name();
API const char* app_path();
API const char* app_cache();
API const char* app_temp();
API const char* app_cmdline();
API uint64_t date(); // YYYYMMDDhhmmss
API uint64_t date_epoch(); // linux epoch
@ -3515,6 +3475,72 @@ API void sleep_ns(double us);
API unsigned timer(unsigned ms, unsigned (*callback)(unsigned ms, void *arg), void *arg);
API void timer_destroy(unsigned timer_handle);
// time sortable unique identifier (similar to ksuid/tuid; others: sno/xid/cuid/ulid)
// - rlyeh, public domain.
//
// also similar to a mongo object id, 12 bytes as follows:
// - 4-byte timestamp (ss). epoch: Tuesday, 12 September 2023 6:06:56
// - 2-byte (machine or app hash)
// - 2-byte (thread-id)
// - 4-byte (rand counter, that gets increased at every id creation)
typedef vec3i guid;
API guid guid_create();
/*
AUTORUN {
guid g1 = guid_create();
guid g2 = guid_create();
print3i(g1);
hexdump(&g1, sizeof(g1));
print3i(g2);
hexdump(&g2, sizeof(g2));
}
*/
#line 0
#line 1 "v4k_system.h"
// -----------------------------------------------------------------------------
// system framework utils
// - rlyeh, public domain.
//
// Note: Windows users add `/Zi` compilation flags, else add `-g` and/or `-ldl` flags
// Note: If you are linking your binary using GNU ld you need to add --export-dynamic
API void* thread( int (*thread_func)(void* user_data), void* user_data );
API void thread_destroy( void *thd );
API int argc();
API char* argv(int);
API int flag(const char *commalist); // --arg // app_flag?
API const char* option(const char *commalist, const char *defaults); // --arg=string or --arg string
API int optioni(const char *commalist, int defaults); // --arg=integer or --arg integer // argvi() ?
API float optionf(const char *commalist, float defaults); // --arg=float or --arg float // flagf() ?
API void tty_attach();
API void tty_detach();
API void tty_color(unsigned color);
API void tty_reset();
API const char* app_exec(const char *command); // returns ("%15d %s", retcode, output_last_line)
API int app_spawn(const char *command);
API int app_cores();
API int app_battery(); /// returns battery level [1..100]. also positive if charging (+), negative if discharging (-), and 0 if no battery is present.
API const char* app_name();
API const char* app_path();
API const char* app_cache();
API const char* app_temp();
API const char* app_cmdline();
API void app_beep();
API void app_hang();
API void app_crash();
API void app_singleton(const char *guid);
API bool app_open(const char *folder_file_or_url);
API char* callstack( int traces ); // write callstack into a temporary string. <0 traces to invert order. do not free().
API int callstackf( FILE *fp, int traces ); // write callstack to file. <0 traces to invert order.
@ -3525,12 +3551,12 @@ API void hexdumpf( FILE *fp, const void *ptr, unsigned len, int width );
API void breakpoint(const char *optional_reason);
API bool has_debugger();
API void signal_hooks(void);
API void signal_handler_ignore(int signal);
API void signal_handler_quit(int signal);
API void signal_handler_abort(int signal);
API void signal_handler_debug(int signal);
API const char *signal_name(int signal); // helper util
API void trap_install(void);
API const char *trap_name(int signal); // helper util
API void trap_on_ignore(int signal); // helper util
API void trap_on_quit(int signal); // helper util
API void trap_on_abort(int signal); // helper util
API void trap_on_debug(int signal); // helper util
API uint16_t lil16(uint16_t n); // swap16 as lil
API uint32_t lil32(uint32_t n); // swap32 as lil
@ -3559,6 +3585,10 @@ API int (PANIC)(const char *error, const char *file, int line);
#define PRINTF(...) PRINTF(va(__VA_ARGS__), 1[#__VA_ARGS__] == '!' ? callstack(+48) : "", __FILE__, __LINE__, __FUNCTION__)
API int (PRINTF)(const char *text, const char *stack, const char *file, int line, const char *function);
#define test(expr) test(__FILE__,__LINE__,#expr,!!(expr))
API int (test)(const char *file, int line, const char *expr, bool result);
// AUTORUN { test(1<2); }
#line 0
#line 1 "v4k_ui.h"

75
labs/slayer/main.c 100644
View File

@ -0,0 +1,75 @@
#include "v4k.h"
int main() {
float app_volume = 1.00f;
unsigned app_size = flag("--fullscreen") ? 100 : 75;
unsigned app_flags = flag("--msaa") ? WINDOW_MSAA4 : 0;
unsigned app_target_fps = optioni("--fps", 60); // --fps=30, --fps=45, etc. defaults to 60.
// window api (fullscreen or 75% sized, optional MSAA flags)
window_create(app_size, app_flags);
window_title(__FILE__);
window_fps_lock(app_target_fps);
// load video
video_t *v = video( "pexels-pachon-in-motion-17486489.mp4", VIDEO_RGB | VIDEO_LOOP );
// app loop
while( window_swap() ) {
// input controls
if( input(KEY_ESC) ) break;
// profile api
texture_t *textures;
profile( "Video decoder" ) {
// video api: decode frame and get associated textures (audio is sent to audiomixer automatically)
textures = video_decode( v );
// fullscreen video
// if(video_is_rgb(v)) fullscreen_quad_rgb( textures[0], 1.3f );
// else fullscreen_quad_ycbcr( textures, 1.3f );
}
// create menubar on top
int choice1 = ui_menu("File;Shell;Exit");
int choice2 = ui_menu("Help;About");
if( choice1 == 1 ) system(ifdef(win32, "start \"\" cmd", ifdef(osx, "open sh", "xdg-open sh")));
if( choice1 == 2 ) exit(0);
// showcase a few ui widgets
ui_demo(0);
// create ui panel
if( ui_panel("myPanel", PANEL_OPEN) ) {
// Print some numbers
ui_section("Stats");
ui_label2("FPS", va("%5.2f", window_fps()));
ui_separator();
// add some buttons
ui_section("Buttons");
if( ui_button("Screenshot") ) window_screenshot(__FILE__ ".png"), ui_notify(0,ICON_MD_WARNING "Screenshot");
if( ui_button("Record Video") ) window_record(__FILE__ ".mp4"), ui_notify(0,ICON_MD_WARNING "Recoding video");
if( ui_button("Toggle fullscreen") ) window_fullscreen( !window_has_fullscreen() );
ui_separator();
// some more video controls
ui_section("Video");
if( ui_button("Rewind") ) video_seek(v, video_position(v) - 3);
if( ui_button("Pause") ) video_pause(v, video_is_paused(v) ^ 1);
if( ui_button("Forward") ) video_seek(v, video_position(v) + 3);
if( ui_slider2("Volume", &app_volume, va("%.2f", app_volume))) audio_volume_master(app_volume);
// end of panel. must be enclosed within same if() branch.
ui_panel_end();
}
// create window
static int open = 1;
if( ui_window("myWindow", &open) ) {
// present decoded texture in a widget, without any label (NULL)
ui_texture( NULL, textures[0] );
// end of window. must be enclosed within same if() branch.
ui_window_end();
}
}
}

Binary file not shown.

View File

@ -7,7 +7,7 @@
; syntax: symbols are defined in KEY=value form, as seen below.
TOOLS=./ ; folder where our pipeline tools are located
ART=../demos/art/,../engine/art/ ; comma-separated folder(s) that store all our asset files
ART=../demos/art/,../engine/art/,../labs/slayer/art ; comma-separated folder(s) that store all our asset files
; lines starting with @windows, @linux or @osx will be processed only where OS matches.
; we are defining here some symbols differently for each platform.

740
tools/ctime.c 100644
View File

@ -0,0 +1,740 @@
#pragma comment(lib, "winmm")
/* ========================================================================
$File: tools/ctime/ctime.c $
$Date: 2016/05/08 04:16:55PM $
$Revision: 7 $
$Creator: Casey Muratori $
$Notice:
The author of this software MAKES NO WARRANTY as to the RELIABILITY,
SUITABILITY, or USABILITY of this software. USE IT AT YOUR OWN RISK.
This is a simple timing utility. It is in the public domain.
Anyone can use it, modify it, roll'n'smoke hardcopies of the source
code, sell it to the terrorists, etc.
But the author makes absolutely no warranty as to the reliability,
suitability, or usability of the software. There might be bad bugs
in here. It could delete all your files. It could format your
hard drive. I have no idea. If you lose all your files from using
it, it is your fault.
$
ctime is a simple utility that helps you keep track of how much time
you spend building your projects. You use it the same way you would
use a begin/end block profiler in your normal code, only instead of
profiling your code, you profile your build.
BASIC INSTRUCTIONS
------------------
On the very first line of your build script, you do something like this:
ctime -begin timings_file_for_this_build.ctm
and then on the very last line of your build script, you do
ctime -end timings_file_for_this_build.ctm
That's all there is to it! ctime will keep track of every build you
do, when you did it, and how long it took. Later, when you'd like to
get a feel for how your build times have evolved, you can type
ctime -stats timings_file_for_this_build.ctm
and it will tell you a number of useful statistics!
ADVANCED INSTRUCTIONS
---------------------
ctime has the ability to track the difference between _failed_ builds
and _successful_ builds. If you would like it to do so, you can capture
the error status in your build script at whatever point you want,
for example:
set LastError=%ERRORLEVEL%
and then when you eventually call ctime to end the profiling, you simply
pass that error code to it:
ctime -end timings_file_for_this_build.ctm %LastError%
ctime can also dump all timings from a timing file into a textual
format for use in other types of tools. To get a CSV you can import
into a graphing program or database, use:
ctime -csv timings_file_for_this_build.ctm
Also, you may want to do things like timing multiple builds separately,
or timing builds based on what compiler flags are active. To do this,
you can use separate timing files for each configuration by using
the shell variables for the build at the filename, eg.:
ctime -begin timings_for_%BUILD_NAME%.ctm
...
ctime -end timings_for_%BUILD_NAME%.ctm
======================================================================== */
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#pragma pack(push,1)
#define MAGIC_VALUE 0xCA5E713F
typedef struct timing_file_header
{
int unsigned MagicValue;
} timing_file_header;
typedef struct timing_file_date
{
long long unsigned E;
} timing_file_date;
enum timing_file_entry_flag
{
TFEF_Complete = 0x1,
TFEF_NoErrors = 0x2,
};
typedef struct timing_file_entry
{
timing_file_date StartDate;
int unsigned Flags;
int unsigned MillisecondsElapsed;
} timing_file_entry;
#pragma pack(pop)
typedef struct timing_entry_array
{
int EntryCount;
timing_file_entry *Entries;
} timing_entry_array;
//
// TODO(casey): More platforms? Sadly, ANSI C doesn't support high-resolution timing across runs of a process AFAICT :(
//
#ifdef _WIN32
#include <windows.h>
static int unsigned
GetClock(void)
{
if(sizeof(int unsigned) != sizeof(DWORD))
{
fprintf(stderr, "ERROR: Unexpected integer size - timing will not work on this platform!\n");
}
return(timeGetTime());
}
#else
// This was written for Linux
static int unsigned
GetClock(void)
{
struct timespec TimeSpec;
int unsigned Result;
clock_gettime(CLOCK_REALTIME, &TimeSpec);
Result = TimeSpec.tv_sec * 1000 + TimeSpec.tv_nsec / 1000000;
return Result;
}
#endif
//
//
//
static timing_file_date
GetDate(void)
{
timing_file_date Result = {0};
Result.E = time(NULL);
return(Result);
}
static void
PrintDate(timing_file_date Date)
{
time_t Time;
struct tm *LocalTime;
char Str[256];
Time = Date.E;
LocalTime = localtime(&Time);
strftime(Str, 256, "%Y-%m-%d %H:%M:%S", LocalTime);
fprintf(stdout, "%s", Str);
}
static long long unsigned
SecondDifference(timing_file_date A, timing_file_date B)
{
long long unsigned Result = A.E - B.E;
return Result;
}
static int unsigned
DayIndex(timing_file_date A)
{
time_t Time;
struct tm *LocalTime;
Time = A.E;
LocalTime = localtime(&Time);
return LocalTime->tm_yday;
}
static void
Usage(void)
{
fprintf(stderr, "CTime v1.0 by Casey Muratori\n");
fprintf(stderr, "Usage:\n");
fprintf(stderr, " ctime -begin <timing file>\n");
fprintf(stderr, " ctime -end <timing file> [error level]\n");
fprintf(stderr, " ctime -stats <timing file>\n");
fprintf(stderr, " ctime -csv <timing file>\n");
}
static timing_entry_array
ReadAllEntries(FILE* Handle)
{
timing_entry_array Result = {0};
int EntriesBegin = sizeof(timing_file_header);
int FileSize;
if((fseek(Handle, 0, SEEK_END) == 0) && ((FileSize = ftell(Handle)) >= 0))
{
int EntriesSize = FileSize - EntriesBegin;
Result.Entries = (timing_file_entry *)malloc(EntriesSize);
if(Result.Entries)
{
fseek(Handle, EntriesBegin, SEEK_SET);
int ReadSize = (int)fread(Result.Entries, 1, EntriesSize, Handle);
if(ReadSize == EntriesSize)
{
Result.EntryCount = EntriesSize / sizeof(timing_file_entry);
}
else
{
fprintf(stderr, "ERROR: Unable to read timing entries from file.\n");
}
}
else
{
fprintf(stderr, "ERROR: Unable to allocate %d for storing timing entries.\n", EntriesSize);
}
}
else
{
fprintf(stderr, "ERROR: Unable to determine file size of timing file.\n");
}
return(Result);
}
static void
FreeAllEntries(timing_entry_array Array)
{
if(Array.Entries)
{
free(Array.Entries);
Array.EntryCount = 0;
Array.Entries = 0;
}
}
static void
CSV(timing_entry_array Array, char *TimingFileName)
{
int EntryIndex;
timing_file_entry *Entry = Array.Entries;
fprintf(stdout, "%s Timings\n", TimingFileName);
fprintf(stdout, "ordinal, date, duration, status\n");
{for(EntryIndex = 0;
EntryIndex < Array.EntryCount;
++EntryIndex, ++Entry)
{
fprintf(stdout, "%d, ", EntryIndex);
PrintDate(Entry->StartDate);
if(Entry->Flags & TFEF_Complete)
{
fprintf(stdout, ", %0.3fs, %s", (double)Entry->MillisecondsElapsed / 1000.0,
(Entry->Flags & TFEF_NoErrors) ? "succeeded" : "failed");
}
else
{
fprintf(stdout, ", (never completed), failed");
}
fprintf(stdout, "\n");
}}
}
typedef struct time_part
{
char *Name;
double MillisecondsPer;
} time_part;
static void
PrintTime(double Milliseconds)
{
double MillisecondsPerSecond = 1000;
double MillisecondsPerMinute = 60*MillisecondsPerSecond;
double MillisecondsPerHour = 60*MillisecondsPerMinute;
double MillisecondsPerDay = 24*MillisecondsPerHour;
double MillisecondsPerWeek = 7*MillisecondsPerDay;
time_part Parts[] =
{
{"week", MillisecondsPerWeek},
{"day", MillisecondsPerDay},
{"hour", MillisecondsPerHour},
{"minute", MillisecondsPerMinute},
};
int unsigned PartIndex;
double Q = Milliseconds;
for(PartIndex = 0;
PartIndex < (sizeof(Parts)/sizeof(Parts[0]));
++PartIndex)
{
double MsPer = Parts[PartIndex].MillisecondsPer;
double This = (double)(int)(Q / MsPer);
if(This > 0)
{
fprintf(stdout, "%d %s%s, ", (int)This, Parts[PartIndex].Name,
(This != 1) ? "s" : "");
}
Q -= This*MsPer;
}
fprintf(stdout, "%0.3f seconds", (double)Q / 1000.0);
}
static void
PrintTimeStat(char *Name, int unsigned Milliseconds)
{
fprintf(stdout, "%s: ", Name);
PrintTime((double)Milliseconds);
fprintf(stdout, "\n");
}
typedef struct stat_group
{
int unsigned Count;
int unsigned SlowestMs;
int unsigned FastestMs;
double TotalMs;
} stat_group;
#define GRAPH_HEIGHT 10
#define GRAPH_WIDTH 30
typedef struct graph
{
stat_group Buckets[GRAPH_WIDTH];
} graph;
static void
PrintStatGroup(char *Title, stat_group *Group)
{
int unsigned AverageMs = 0;
if(Group->Count >= 1)
{
AverageMs = (int unsigned)(Group->TotalMs / (double)Group->Count);
}
if(Group->Count > 0)
{
fprintf(stdout, "%s (%d):\n", Title, Group->Count);
PrintTimeStat(" Slowest", Group->SlowestMs);
PrintTimeStat(" Fastest", Group->FastestMs);
PrintTimeStat(" Average", AverageMs);
PrintTimeStat(" Total", (int unsigned)Group->TotalMs);
}
}
static void
UpdateStatGroup(stat_group *Group, timing_file_entry *Entry)
{
if(Group->SlowestMs < Entry->MillisecondsElapsed)
{
Group->SlowestMs = Entry->MillisecondsElapsed;
}
if(Group->FastestMs > Entry->MillisecondsElapsed)
{
Group->FastestMs = Entry->MillisecondsElapsed;
}
Group->TotalMs += (double)Entry->MillisecondsElapsed;
++Group->Count;
}
static int
MapToDiscrete(double Value, double InMax, double OutMax)
{
int Result;
if(InMax == 0)
{
InMax = 1;
}
Result = (int)((Value / InMax) * OutMax);
return(Result);
}
static void
PrintGraph(char *Title, double DaySpan, graph *Graph)
{
int BucketIndex;
int LineIndex;
int unsigned MaxCountInBucket = 0;
int unsigned SlowestMs = 0;
double DPB = DaySpan / (double)GRAPH_WIDTH;
for(BucketIndex = 0;
BucketIndex < GRAPH_WIDTH;
++BucketIndex)
{
stat_group *Group = Graph->Buckets + BucketIndex;
if(Group->Count)
{
// double AverageMs = Group->TotalMs / (double)Group->Count;
if(MaxCountInBucket < Group->Count)
{
MaxCountInBucket = Group->Count;
}
if(SlowestMs < Group->SlowestMs)
{
SlowestMs = Group->SlowestMs;
}
}
}
fprintf(stdout, "\n%s (%f day%s/bucket):\n", Title, DPB, (DPB == 1) ? "" : "s");
for(LineIndex = GRAPH_HEIGHT - 1;
LineIndex >= 0;
--LineIndex)
{
fputc('|', stdout);
for(BucketIndex = 0;
BucketIndex < GRAPH_WIDTH;
++BucketIndex)
{
stat_group *Group = Graph->Buckets + BucketIndex;
int This = -1;
if(Group->Count)
{
// double AverageMs = Group->TotalMs / (double)Group->Count;
This = MapToDiscrete(Group->SlowestMs, SlowestMs, GRAPH_HEIGHT - 1);
}
fputc((This >= LineIndex) ? '*' : ' ', stdout);
}
if(LineIndex == (GRAPH_HEIGHT - 1))
{
fputc(' ', stdout);
PrintTime(SlowestMs);
}
fputc('\n', stdout);
}
fputc('+', stdout);
for(BucketIndex = 0; BucketIndex < GRAPH_WIDTH; ++BucketIndex) {fputc('-', stdout);}
fputc(' ', stdout);
PrintTime(0);
fputc('\n', stdout);
fputc('\n', stdout);
for(LineIndex = GRAPH_HEIGHT - 1;
LineIndex >= 0;
--LineIndex)
{
fputc('|', stdout);
for(BucketIndex = 0;
BucketIndex < GRAPH_WIDTH;
++BucketIndex)
{
stat_group *Group = Graph->Buckets + BucketIndex;
int This = -1;
if(Group->Count)
{
This = MapToDiscrete(Group->Count, MaxCountInBucket, GRAPH_HEIGHT - 1);
}
fputc((This >= LineIndex) ? '*' : ' ', stdout);
}
if(LineIndex == (GRAPH_HEIGHT - 1))
{
fprintf(stdout, " %u", MaxCountInBucket);
}
fputc('\n', stdout);
}
fputc('+', stdout);
for(BucketIndex = 0; BucketIndex < GRAPH_WIDTH; ++BucketIndex) {fputc('-', stdout);}
fprintf(stdout, " 0\n");
}
static void
Stats(timing_entry_array Array, char *TimingFileName)
{
stat_group WithErrors = {0};
stat_group NoErrors = {0};
stat_group AllStats = {0};
int unsigned IncompleteCount = 0;
int unsigned DaysWithTimingCount = 0;
int unsigned DaySpanCount = 0;
int EntryIndex;
timing_file_entry *Entry = Array.Entries;
int unsigned LastDayIndex = 0;
double AllMs = 0;
double FirstDayAt = 0;
double LastDayAt = 0;
double DaySpan = 0;
graph TotalGraph = {0};
graph RecentGraph = {0};
WithErrors.FastestMs = 0xFFFFFFFF;
NoErrors.FastestMs = 0xFFFFFFFF;
if(Array.EntryCount >= 2)
{
long long unsigned SecondD = SecondDifference(Array.Entries[Array.EntryCount - 1].StartDate, Array.Entries[0].StartDate);
DaySpanCount = (int unsigned)(SecondD / (60 * 60 * 24));
FirstDayAt = (double)DayIndex(Array.Entries[0].StartDate);
LastDayAt = (double)DayIndex(Array.Entries[Array.EntryCount - 1].StartDate);
DaySpan = (LastDayAt - FirstDayAt);
}
DaySpan += 1;
for(EntryIndex = 0;
EntryIndex < Array.EntryCount;
++EntryIndex, ++Entry)
{
if(Entry->Flags & TFEF_Complete)
{
stat_group *Group = (Entry->Flags & TFEF_NoErrors) ? &NoErrors : &WithErrors;
int unsigned ThisDayIndex = DayIndex(Entry->StartDate);
if(LastDayIndex != ThisDayIndex)
{
LastDayIndex = ThisDayIndex;
++DaysWithTimingCount;
}
UpdateStatGroup(Group, Entry);
UpdateStatGroup(&AllStats, Entry);
AllMs += (double)Entry->MillisecondsElapsed;
{
int GraphIndex = (int)(((double)(ThisDayIndex-FirstDayAt)/DaySpan)*(double)GRAPH_WIDTH);
UpdateStatGroup(TotalGraph.Buckets + GraphIndex, Entry);
}
{
int GraphIndex = (int)(ThisDayIndex - (LastDayAt - GRAPH_WIDTH + 1));
if(GraphIndex >= 0)
{
UpdateStatGroup(RecentGraph.Buckets + GraphIndex, Entry);
}
}
}
else
{
++IncompleteCount;
}
}
fprintf(stdout, "\n%s Statistics\n\n", TimingFileName);
fprintf(stdout, "Total complete timings: %d\n", WithErrors.Count + NoErrors.Count);
fprintf(stdout, "Total incomplete timings: %d\n", IncompleteCount);
fprintf(stdout, "Days with timings: %d\n", DaysWithTimingCount);
fprintf(stdout, "Days between first and last timing: %d\n", DaySpanCount);
PrintStatGroup("Timings marked successful", &NoErrors);
PrintStatGroup("Timings marked failed", &WithErrors);
PrintGraph("All", (LastDayAt - FirstDayAt), &TotalGraph);
PrintGraph("Recent", GRAPH_WIDTH, &RecentGraph);
fprintf(stdout, "\nTotal time spent: ");
PrintTime(AllMs);
fprintf(stdout, "\n");
}
int
main(int ArgCount, char **Args)
{
// TODO(casey): It would be nice if this supported 64-bit file sizes, but I can't really
// tell right now if "ANSI C" supports this. I feel like it should by now, but the
// MSVC docs seem to suggest you have to use __int64 to do 64-bit stuff with the CRT
// low-level IO routines, and I'm pretty sure that isn't a portable type :(
// NOTE(casey): We snap the clock time right on entry, to minimize any overhead on
// "end" times that might occur from opening the file.
int unsigned EntryClock = GetClock();
if((ArgCount == 3) || (ArgCount == 4))
{
char *Mode = Args[1];
int ModeIsBegin = (strcmp(Mode, "-begin") == 0);
char *TimingFileName = Args[2];
timing_file_header Header = {0};
FILE* Handle = fopen(TimingFileName, "r+b");
if(Handle != NULL)
{
// NOTE(casey): The file exists - check the magic value
fread(&Header, sizeof(Header), 1, Handle);
if(Header.MagicValue == MAGIC_VALUE)
{
// NOTE(casey): The file is at least nominally valid.
}
else
{
fprintf(stderr, "ERROR: Unable to verify that \"%s\" is actually a ctime-compatible file.\n", TimingFileName);
fclose(Handle);
Handle = NULL;
}
}
else if(ModeIsBegin)
{
// NOTE(casey): The file doesn't exist and we're starting a new timing, so create it.
Handle = fopen(TimingFileName, "w+b");
if(Handle != NULL)
{
Header.MagicValue = MAGIC_VALUE;
if(fwrite(&Header, sizeof(Header), 1, Handle) == 1)
{
// NOTE(casey): File creation was (presumably) successful.
}
else
{
fprintf(stderr, "ERROR: Unable to write header to \"%s\".\n", TimingFileName);
}
}
else
{
fprintf(stderr, "ERROR: Unable to create timing file \"%s\".\n", TimingFileName);
}
}
if(Handle != NULL)
{
if(ModeIsBegin)
{
timing_file_entry NewEntry = {0};
NewEntry.StartDate = GetDate();
NewEntry.MillisecondsElapsed = GetClock();
if((fseek(Handle, 0, SEEK_END) == 0) &&
(fwrite(&NewEntry, sizeof(NewEntry), 1, Handle) == 1))
{
// NOTE(casey): Timer begin entry was written successfully.
}
else
{
fprintf(stderr, "ERROR: Unable to append new entry to file \"%s\".\n", TimingFileName);
}
}
else if(strcmp(Mode, "-end") == 0)
{
timing_file_entry LastEntry = {0};
if((fseek(Handle, -(int)sizeof(timing_file_entry), SEEK_END) == 0) &&
(fread(&LastEntry, sizeof(LastEntry), 1, Handle) == 1))
{
if(!(LastEntry.Flags & TFEF_Complete))
{
int unsigned StartClockD = LastEntry.MillisecondsElapsed;
int unsigned EndClockD = EntryClock;
LastEntry.Flags |= TFEF_Complete;
LastEntry.MillisecondsElapsed = 0;
if(StartClockD < EndClockD)
{
LastEntry.MillisecondsElapsed = (EndClockD - StartClockD);
}
if((ArgCount == 3) ||
((ArgCount == 4) && (atoi(Args[3]) == 0)))
{
LastEntry.Flags |= TFEF_NoErrors;
}
if((fseek(Handle, -(int)sizeof(timing_file_entry), SEEK_END) == 0) &&
(fwrite(&LastEntry, sizeof(LastEntry), 1, Handle) == 1))
{
fprintf(stdout, "CTIME: ");
PrintTime(LastEntry.MillisecondsElapsed);
fprintf(stdout, " (%s)\n", TimingFileName);
}
else
{
fprintf(stderr, "ERROR: Unable to rewrite last entry to file \"%s\".\n", TimingFileName);
}
}
else
{
fprintf(stderr, "ERROR: Last entry in file \"%s\" is already closed - unbalanced/overlapped calls?\n", TimingFileName);
}
}
else
{
fprintf(stderr, "ERROR: Unable to read last entry from file \"%s\".\n", TimingFileName);
}
}
else if(strcmp(Mode, "-stats") == 0)
{
timing_entry_array Array = ReadAllEntries(Handle);
Stats(Array, TimingFileName);
FreeAllEntries(Array);
}
else if(strcmp(Mode, "-csv") == 0)
{
timing_entry_array Array = ReadAllEntries(Handle);
CSV(Array, TimingFileName);
FreeAllEntries(Array);
}
else
{
fprintf(stderr, "ERROR: Unrecognized command \"%s\".\n", Mode);
}
fclose(Handle);
Handle = NULL;
}
else
{
fprintf(stderr, "ERROR: Cannnot open file \"%s\".\n", TimingFileName);
}
}
else
{
Usage();
}
}

View File

@ -830,7 +830,7 @@ int main(int argc, char **argv) {
section = NULL;
}
if(strendi(line, "/""//-")) continue; // discard explicitly excluded lines
if(strstr/*strendi*/(line, "/""//-")) continue; // discard explicitly excluded lines
learn(line);

View File

@ -173,7 +173,7 @@ static void* editor_selected_obj = 0;
static int editor_key = 0;
static vec2 editor_mouse = {0}; // 2d coord for ray/picking
static bool editor_gamepad = 1;
static int editor_hz = 144;
static int editor_hz = 60;
static int editor_hz_mid = 18;
static int editor_hz_low = 5;
static bool editor_power_saving = 0;
@ -1014,7 +1014,7 @@ void editor_render_menubar() {
break; case key_outliner: ui_show("Outliner", ui_visible("Outliner") ^ true);
break; case key_recording: name = file_counter(va("%s.mp4",app_name())), window_record(name), ui_notify(va("Video capturing: %s", name), date_string());
break; case key_screenshot: name = file_counter(va("%s.png",app_name())), window_screenshot(name), ui_notify(va("Screenshot: %s", name), date_string());
break; case key_profiler: ui_show("Profiler", profile_enable(ui_visible("Profiler") ^ true));
break; case key_profiler: ui_show("Profiler", profiler_enable(ui_visible("Profiler") ^ true));
break; case key_fullscreen: record_stop(), window_fullscreen( window_has_fullscreen() ^ 1 ); // framebuffer resizing corrupts video stream, so stop any recording beforehand
break; case key_gamepad: editor_gamepad ^= 1;
break; case key_lit: editor_lit ^= 1;
@ -1040,12 +1040,12 @@ void editor_obj_render_properties_recursively(void *obj, const char *mask) {
// contextual menu (open)
if( ui_contextual() ) {
if( ui_button_transparent("<Load" ) ) do_context_obj = obj, do_context_cmd = cc4(load);
if( ui_button_transparent("<Save" ) ) do_context_obj = obj, do_context_cmd = cc4(save);
if( ui_button_transparent("<Merge") ) do_context_obj = obj, do_context_cmd = cc4(merge);
if( ui_button_transparent("<Cut" ) ) do_context_obj = obj, do_context_cmd = cc4(cut);
if( ui_button_transparent("<Copy" ) ) do_context_obj = obj, do_context_cmd = cc4(copy);
if( ui_button_transparent("<Paste") ) do_context_obj = obj, do_context_cmd = cc4(paste);
if( ui_button_transparent("<Load" ) ) do_context_obj = obj, do_context_cmd = cc4(l,o,a,d);
if( ui_button_transparent("<Save" ) ) do_context_obj = obj, do_context_cmd = cc4(s,a,v,e);
if( ui_button_transparent("<Merge") ) do_context_obj = obj, do_context_cmd = cc5(m,e,r,g,e);
if( ui_button_transparent("<Cut" ) ) do_context_obj = obj, do_context_cmd = cc3(c,u,t);
if( ui_button_transparent("<Copy" ) ) do_context_obj = obj, do_context_cmd = cc4(c,o,p,y);
if( ui_button_transparent("<Paste") ) do_context_obj = obj, do_context_cmd = cc5(p,a,s,t,e);
ui_contextual_end();
}
@ -1061,12 +1061,12 @@ void editor_obj_render_properties_recursively(void *obj, const char *mask) {
// contextual menu (close)
if( !open && ui_contextual() ) {
if( ui_button_transparent("<Load" ) ) do_context_obj = obj, do_context_cmd = cc4(load);
if( ui_button_transparent("<Save" ) ) do_context_obj = obj, do_context_cmd = cc4(save);
if( ui_button_transparent("<Merge") ) do_context_obj = obj, do_context_cmd = cc4(merge);
if( ui_button_transparent("<Cut" ) ) do_context_obj = obj, do_context_cmd = cc4(cut);
if( ui_button_transparent("<Copy" ) ) do_context_obj = obj, do_context_cmd = cc4(copy);
if( ui_button_transparent("<Paste") ) do_context_obj = obj, do_context_cmd = cc4(paste);
if( ui_button_transparent("<Load" ) ) do_context_obj = obj, do_context_cmd = cc4(l,o,a,d);
if( ui_button_transparent("<Save" ) ) do_context_obj = obj, do_context_cmd = cc4(s,a,v,e);
if( ui_button_transparent("<Merge") ) do_context_obj = obj, do_context_cmd = cc5(m,e,r,g,e);
if( ui_button_transparent("<Cut" ) ) do_context_obj = obj, do_context_cmd = cc3(c,u,t);
if( ui_button_transparent("<Copy" ) ) do_context_obj = obj, do_context_cmd = cc4(c,o,p,y);
if( ui_button_transparent("<Paste") ) do_context_obj = obj, do_context_cmd = cc5(p,a,s,t,e);
ui_contextual_end();
}
@ -1218,7 +1218,7 @@ void editor_render_windows() {
void *k = *o;
editor_obj_render_properties_recursively(k, mask);
}
if( do_context_cmd == cc4(list) && do_context_obj ) {
if( do_context_cmd == cc4(l,i,s,t) && do_context_obj ) {
printf("list [%p]\n", do_context_obj);
}
// draw: depth + state (alpha0=off)
@ -1330,7 +1330,7 @@ ray *editor_pickup() {
static
void editor_init() {
// enable outlines
do_once fx_load("editor/art/fx/fxOutline.fs");
do_once fx_load("editorOutline.fs");
do_once fx_enable(0, 1);
// init editor
@ -1400,7 +1400,7 @@ void editor_camera_fps(void) {
view = add2(view, vec2(touch_view.x, -touch_view.y));
// apply inputs
camera_move(&cam, move.x,move.y,move.z);
camera_moveby(&cam, move);
camera_fps(&cam, view.x,view.y);
}