commit
8f46b88f1d
@ -0,0 +1,217 @@ |
|||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include <err.h> |
||||||
|
#include <errno.h> |
||||||
|
|
||||||
|
#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; |
||||||
|
} |
@ -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 |
Loading…
Reference in new issue