commit 8f46b88f1d628a634a5c31d5e3b58c253434bc1b Author: Alessandro Mauri Date: Wed Nov 30 22:14:55 2022 +0100 lol diff --git a/renderer.c b/renderer.c new file mode 100644 index 0000000..e69de29 diff --git a/ugui.c b/ugui.c new file mode 100644 index 0000000..cc917e0 --- /dev/null +++ b/ugui.c @@ -0,0 +1,217 @@ +#include +#include +#include +#include + +#include "ugui.h" + +#define SALT 0xbabb0cac +#define DEF_SCALE 1.0 +#define DEF_DPI 96.0 +#define E_STACK_STEP (1024 * 128) + +#define TO_PPM(dpi, scale) (dpi * scale * 0.03937008) + +// https://en.wikipedia.org/wiki/Jenkins_hash_function +unsigned int hash(void *data, unsigned int size) +{ + if (!size) + return 0; + unsigned int hash = SALT; + unsigned char *v = (unsigned char *)data; + + for (; size; size--) { + hash += v[size-1]; + hash += hash << 10; + hash ^= hash >> 6; + } + hash += hash << 3; + hash ^= hash >> 11; + hash += hash << 15; + + return hash; +} + + +static void grow_stack(ug_ctx_t *ctx) +{ + if (!ctx) + err(EXIT_FAILURE, "__FUNCTION__:" "Cannot grow null context"); + ctx->e_size += E_STACK_STEP; + ctx->e_stack = realloc(ctx->e_stack, ctx->e_size); + if (!ctx->e_stack) + err(errno, "__FUNCTION__:" "Could not grow stack: %s", strerror(errno)); +} + + +// creates a new context, fills with default values, ctx is ready for ug_start() +ug_ctx_t *ug_new_ctx(void) +{ + ug_ctx_t *ctx = malloc(sizeof(ug_ctx_t)); + if (!ctx) + err(errno, "__FUNCTION__:" "Could not allocate context: %s", strerror(errno)); + memset(ctx, 0, sizeof(ug_ctx_t)); + + ctx->scale = DEF_SCALE; + ctx->dpi = DEF_DPI; + ctx->ppm = TO_PPM(DEF_SCALE, DEF_DPI); + + return ctx; +} + + +void ug_free_ctx(ug_ctx_t *ctx) +{ + if (!ctx) { + warn("__FUNCTION__:" "Trying to free a null context"); + return; + } + + if (!ctx->e_stack) { + warn("__FUNCTION__:" "Context has null element stack"); + return; + } + + free(ctx->e_stack); + free(ctx); +} + + +// updates the context with user information +int ug_update_ctx(ug_ctx_t *ctx, + unsigned int window_id, + float scale, + float dpi, + float ppm + ) +{ + if (!ctx) + return -1; + ctx->window_id = window_id; + + if (scale) + ctx->scale = scale; + if (dpi) + ctx->dpi = dpi; + if (!ppm) + ctx->ppm = TO_PPM(ctx->dpi, ctx->scale); + else + ctx->ppm = ppm; + + return 0; +} + + +/*=============================================================================* + * INPUT FUNCTIONS * + *=============================================================================*/ + +#define TEST_CTX(ctx) { \ + if (!ctx) { \ + warn("__FUNCTION__:" "trying to use a null context"); \ + return; \ + } \ +} + +void ug_input_mousemove(ug_ctx_t *ctx, int x, int y) +{ + TEST_CTX(ctx); + ctx->mouse.pos = (struct ug_vec2){ .x = x, .y = y }; +} + +void ug_input_mousedown(ug_ctx_t *ctx, int x, int y, unsigned char btn) +{ + TEST_CTX(ctx); + ug_input_mousemove(ctx, x, y); + ctx->mouse.down_mask |= btn; + ctx->mouse.press_mask |= btn; +} + + +void ug_input_mouseup(ug_ctx_t *ctx, int x, int y, unsigned char btn) +{ + TEST_CTX(ctx); + ug_input_mousemove(ctx, x, y); + ctx->mouse.down_mask &= ~btn; +} + + +void ug_input_scroll(ug_ctx_t *ctx, int x, int y) +{ + TEST_CTX(ctx); + ctx->mouse.scroll_delta.x += x; + ctx->mouse.scroll_delta.y += y; +} + + +void ug_input_keydown(ug_ctx_t *ctx, int key) +{ + TEST_CTX(ctx); + ctx->key.down_mask |= key; + ctx->key.press_mask |= key; +} + + +void ug_input_keyup(ug_ctx_t *ctx, int key) +{ + TEST_CTX(ctx); + ctx->key.down_mask &= ~key; +} + + +// append in input text +void ug_input_text(ug_ctx_t *ctx, const char *text) +{ + TEST_CTX(ctx); + int size = strlen(ctx->input_text); + int len = strlen(text); + if ((unsigned long)(size+len) >= sizeof(ctx->input_text)) { + warn("__FUNCTION__" "Input text exceeds context buffer"); + return; + } + memcpy(ctx->input_text + size, text, len); +} + + +/*=============================================================================* + * BEGIN AND END * + *=============================================================================*/ + +#undef TEST_CTX +#define TEST_CTX(ctx) { \ + if (!ctx) { \ + warn("__FUNCTION__:" "trying to use a null context"); \ + return -1; \ + } \ +} + +int ug_begin(ug_ctx_t *ctx) +{ + // it is the beginning of a new frame + TEST_CTX(ctx); + + // reset the command stack + ctx->e_idx = 0; + // next frame + ctx->frame++; + // update the mouse delta + ctx->mouse.delta.x = ctx->mouse.pos.x - ctx->mouse.last_pos.x; + ctx->mouse.delta.y = ctx->mouse.pos.y - ctx->mouse.last_pos.y; + // TODO: other stuff + return 0; +} + + +int ug_end(ug_ctx_t *ctx) +{ + // end of a frame, check that all went well + TEST_CTX(ctx); + + // reset the inputs + ctx->mouse.press_mask = 0; + ctx->mouse.scroll_delta = (struct ug_vec2){0}; + ctx->mouse.last_pos = ctx->mouse.pos; + ctx->key.press_mask = 0; + ctx->input_text[0] = '\0'; + return 0; +} diff --git a/ugui.h b/ugui.h new file mode 100644 index 0000000..12b31d3 --- /dev/null +++ b/ugui.h @@ -0,0 +1,266 @@ +#ifndef _UGUI_H +#define _UGUI_H + +/* Point Coordinate */ + +// coordinate type, or how it is measured +enum { + UG_CTYPE_PX = 0x0, + UG_CTYPE_MM = 0x1, + UG_CTYPE_REL = 0x2, +}; + +// coordinate anchor, or where it refers +enum { + UG_CANCHOR_TOP = 0x0, // x coordinates + UG_CANCHOR_LEFT = 0x0, + UG_CANCHOR_BOTTOM = 0x1, // y coordinates + UG_CANCHOR_RIGHT = 0x1, + UG_CANCHOR_VCENTER = 0x2, // for positions it should be centered + UG_CANCHOR_HCENTER = 0x2, +}; + +struct _ug_pt_flags { + unsigned long int _:56; + unsigned long int t:4; + unsigned long int a:4; +}; + +typedef struct { + // first coordinate + union { + union { + int x; + struct _ug_pt_flags fx; + }; + union { + int w; + struct _ug_pt_flags fw; + }; + union { + int m; + struct _ug_pt_flags fm; + }; + }; + // second coordinate + union { + union { + int y; + struct _ug_pt_flags fy; + }; + union { + int h; + struct _ug_pt_flags fh; + }; + union { + int a; + struct _ug_pt_flags fa; + }; + }; +} ug_coord_t; + +// TODO: define stroke style (f.s) +typedef struct { + int size; + struct { + // type of size + unsigned char t:4; + // stroke style + unsigned char s:4; + } f; +} ug_stroke_t; +//---// + +/* RGBA Color type */ +typedef struct { + unsigned char a; + unsigned char b; + unsigned char g; + unsigned char r; +} ug_color_t; +//---// + +/* simple position structure */ +struct ug_vec2 { + int x, y; +}; +//---// + +/* Mouse button mask */ +enum ug_mouse_btn { + UG_MOUSEBTN_LEFT = 1, + UG_MOUSEBTN_MIDDLE = 2, + UG_MOUSEBTN_RIGHT = 4, + UG_MOUSEBTN_4 = 8, + UG_MOUSEBTN_5 = 16, +}; +//---// + +/* event structure */ +typedef enum { + UG_EV_WINDOWMOVE, + UG_EV_WINDOWRESIZE, + UG_EV_WINDOWSHOW, + UG_EV_MOUSEMOVE, + UG_EV_MOUSEDOWN, + UG_EV_MOUSEUP, + UG_EV_KEYDOWN, + UG_EV_KEYUP, +} ug_event_type_t; + +typedef struct { + ug_event_type_t type; + union { + int x; + int w; + unsigned int code; + }; + union { + int y; + int h; + unsigned int mask; + }; +} ug_event_t; +//---// + +/* Primitive element type */ +typedef enum { + UG_E_RECT = 0, + UG_E_TRIANGLE, + UG_E_DOT, + UG_E_LINE, + UG_E_ARC, + UG_E_TEXT, + UG_E_SPRITE, +} ug_element_type_t; + +typedef struct { + ug_coord_t size; + ug_coord_t pos; + ug_color_t color; +} ug_rect_t; + +typedef struct { + ug_coord_t size; + ug_coord_t pos; + ug_color_t color; +} ug_triangle_t; + +typedef struct { + ug_stroke_t stroke; + ug_coord_t pos; + ug_color_t color; +} ug_dot_t; + +typedef struct { + ug_stroke_t stroke; + ug_coord_t vert[2]; + ug_color_t color; +} ug_line_t; + +typedef struct { + ug_stroke_t stroke; + ug_coord_t vert[3]; + ug_color_t color; +} ug_arc_t; + +// draw text from view[start] to view[end] +typedef struct { + ug_stroke_t stroke; + ug_coord_t pos; + ug_color_t color; + const char *view; + int start; + int end; +} ug_text_t; + +// all sprites have to be mapped into memory, map is an array of width x height +// bytes, the type is specified by the user +typedef struct { + ug_coord_t pos; + void *map; + int width; + int height; +} ug_sprite_t; + + +// data is the actual element data, it has to be cast to the right element specified +// by type and is variable size so that no memory is wasted, size is the size in +// bytes of data + id + size + type +typedef struct { + ug_element_type_t type; + unsigned int size; + unsigned int id; + unsigned char data[]; +} ug_element_t; +//---// + +/* Global context */ +// one context per phisical window +// a phisical window can be anything, but here it basically represents the true +// surface where things are drawn into, a window has a scale, dpi and ppm +// (pixel per mm), only the latter is truly needed, the user sets these variables +// and has to take care of updating them when needed +typedef struct { + unsigned int window_id; + float scale, dpi, ppm; + // the user pushes the things that he wants to draw onto the element stack, + // which contains all the primitives needed to draw the wanted element + unsigned char *e_stack; + unsigned int e_size; + unsigned int e_idx; + // the element which has focus + unsigned int focus_id; + // the element that we are hovering + unsigned int hover_id; + // count the frames for fun + unsigned long int frame; + // mouse data + struct { + struct ug_vec2 pos; + struct ug_vec2 last_pos; + struct ug_vec2 delta; + struct ug_vec2 scroll_delta; + // down/pressed masks get updated on mousedown, whereas down_mask + // only on mouseup, so the masks differ by the buttons that were + // released + // FIXME: is this the best way to approach this? + unsigned char down_mask; + unsigned char press_mask; + } mouse; + // keyboard key pressed + struct { + unsigned char down_mask; + unsigned char press_mask; + } key; + // input text buffer + char input_text[32]; +} ug_ctx_t; +//---// + +// creates a new context, fills with default values, ctx is ready for ug_start() +ug_ctx_t *ug_new_ctx(void); +void ug_free_ctx(ug_ctx_t *ctx); +// updates the context with user information +int ug_update_ctx(ug_ctx_t *ctx, + unsigned int window_id, + float scale, + float dpi, + float ppm + ); + +// register inputs +void ug_input_mousedown(ug_ctx_t *ctx, int x, int y, unsigned char btn); +void ug_input_mouseup(ug_ctx_t *ctx, int x, int y, unsigned char btn); +void ug_input_mousemove(ug_ctx_t *ctx, int x, int y); +void ug_input_scroll(ug_ctx_t *ctx, int x, int y); +void ug_input_keydown(ug_ctx_t *ctx, int key); +void ug_input_keyup(ug_ctx_t *ctx, int key); +void ug_input_text(ug_ctx_t *ctx, const char *text); + +// between begin and end the user draws +int ug_begin(ug_ctx_t *ctx); +int ug_end(ug_ctx_t *ctx); + + +#endif