added opengl demo
This commit is contained in:
parent
51bb40dcc2
commit
e135e1424f
11
Readme.md
11
Readme.md
|
@ -16,7 +16,7 @@ possible with fast streamlined development speed in mind.
|
|||
- Full memory management control
|
||||
- Renderer and platform independent
|
||||
- Configurable
|
||||
- UTF-8 supported
|
||||
- UTF-8 support
|
||||
|
||||
## Functionality
|
||||
+ Label
|
||||
|
@ -247,9 +247,12 @@ while (1) {
|
|||
|
||||
## FAQ
|
||||
#### Where is the demo/example code?
|
||||
The demo and example code can be found in the demo folder. For now there is
|
||||
only example code for Linux(X11) and Windows(Win32), OpenGL and Directx
|
||||
demo is in the working.
|
||||
The demo and example code can be found in the demo folder.
|
||||
There is demo code for Linux(X11), Windows(win32) and OpenGL(SDL2, freetype).
|
||||
As for now there will be no DirectX demo since I don't have expierence
|
||||
programming in DirectX(9, 10, 11) but you are more than welcome to provide one.
|
||||
Depending on the demand I will write a Directx 12 and Vulkan demo once they are
|
||||
released as well as stable.
|
||||
|
||||
#### Why did you use ANSI C and not C99 or C++?
|
||||
Personally I stay out of all "discussions" about C vs C++ since they are totally
|
||||
|
|
|
@ -0,0 +1,701 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glu.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_GLYPH_H
|
||||
|
||||
#include "../gui.h"
|
||||
|
||||
/* macros */
|
||||
#define MAX_BUFFER 64
|
||||
#define MAX_MEMORY (8 * 1024)
|
||||
#define MAX_PANELS 4
|
||||
#define WIN_WIDTH 800
|
||||
#define WIN_HEIGHT 600
|
||||
#define DTIME 16
|
||||
#define FONT_ATLAS_DEPTH 4
|
||||
#define CIRCLE_SEGMENTS 22
|
||||
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
#define MAX(a,b) ((a) < (b) ? (b) : (a))
|
||||
#define CLAMP(i,v,x) (MAX(MIN(v,x), i))
|
||||
#define LEN(a) (sizeof(a)/sizeof(a)[0])
|
||||
#define UNUSED(a) ((void)(a))
|
||||
|
||||
struct texCoord {
|
||||
float u;
|
||||
float v;
|
||||
};
|
||||
|
||||
enum font_atlas_dimension {
|
||||
FONT_ATLAS_DIM_64 = 64,
|
||||
FONT_ATLAS_DIM_128 = 128,
|
||||
FONT_ATLAS_DIM_256 = 256,
|
||||
FONT_ATLAS_DIM_512 = 512,
|
||||
FONT_ATLAS_DIM_1024 = 1024,
|
||||
FONT_ATLAS_DIM_2048 = 2048
|
||||
};
|
||||
|
||||
struct font_atlas {
|
||||
enum font_atlas_dimension dim;
|
||||
gui_size range;
|
||||
gui_size size;
|
||||
gui_byte *memory;
|
||||
};
|
||||
|
||||
struct font_glyph {
|
||||
unsigned int code;
|
||||
float xadvance;
|
||||
short width, height;
|
||||
float xoff, yoff;
|
||||
struct texCoord uv[2];
|
||||
};
|
||||
|
||||
struct font {
|
||||
float height;
|
||||
float scale;
|
||||
GLuint texture;
|
||||
unsigned int glyph_count;
|
||||
struct font_glyph *glyphes;
|
||||
const struct font_glyph *fallback;
|
||||
};
|
||||
|
||||
struct demo {
|
||||
gui_char in_buf[MAX_BUFFER];
|
||||
gui_size in_len;
|
||||
gui_bool in_act;
|
||||
gui_bool check;
|
||||
gui_int option;
|
||||
gui_float slider;
|
||||
gui_size prog;
|
||||
gui_int spinner;
|
||||
gui_bool spin_act;
|
||||
gui_size item_cur;
|
||||
gui_size cur;
|
||||
gui_bool tab_min;
|
||||
gui_float group_off;
|
||||
gui_float shelf_off;
|
||||
gui_bool toggle;
|
||||
};
|
||||
|
||||
static char*
|
||||
file_load(const char* path, size_t* siz)
|
||||
{
|
||||
char *buf;
|
||||
FILE *fd = fopen(path, "rb");
|
||||
if (!fd) {
|
||||
fprintf(stderr, "Failed to open file: %s\n", path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
fseek(fd, 0, SEEK_END);
|
||||
*siz = (size_t)ftell(fd);
|
||||
fseek(fd, 0, SEEK_SET);
|
||||
buf = calloc(*siz, 1);
|
||||
fread(buf, *siz, 1, fd);
|
||||
fclose(fd);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void
|
||||
font_load_glyph(unsigned int code, struct font_glyph *glyph, FT_GlyphSlot slot)
|
||||
{
|
||||
glyph->code = code;
|
||||
glyph->width = (short)slot->bitmap.width;
|
||||
glyph->height = (short)slot->bitmap.rows;
|
||||
glyph->xoff = (float)slot->bitmap_left;
|
||||
glyph->yoff = (float)slot->bitmap_top;
|
||||
glyph->xadvance = (float)(slot->advance.x >> 6);
|
||||
}
|
||||
|
||||
static void
|
||||
font_load_glyphes(FT_Face face, struct font *font, size_t range)
|
||||
{
|
||||
size_t i;
|
||||
int ft_err;
|
||||
for (i = 0; i < range; ++i) {
|
||||
unsigned int index = FT_Get_Char_Index(face, i);
|
||||
if (!index) continue;
|
||||
ft_err = FT_Load_Glyph(face, index, 0);
|
||||
if (ft_err) continue;
|
||||
ft_err = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
|
||||
if (ft_err) continue;
|
||||
font_load_glyph(index, &font->glyphes[i], face->glyph);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
font_pack_glyphes(struct font *font, float width, float height, size_t range)
|
||||
{
|
||||
size_t i;
|
||||
float xoff = 0, yoff = 0;
|
||||
float max_height = 0.0f;
|
||||
for (i = 0; i < range; ++i) {
|
||||
struct font_glyph *glyph = &font->glyphes[i];
|
||||
if ((xoff + glyph->width) > width) {
|
||||
yoff += max_height;
|
||||
max_height = 0.0f;
|
||||
xoff = 0.0f;
|
||||
}
|
||||
|
||||
glyph->uv[0].u = xoff / width;
|
||||
glyph->uv[0].v = yoff / height;
|
||||
glyph->uv[1].u = (xoff + glyph->width) / width;
|
||||
glyph->uv[1].v = (yoff + glyph->height) / height;
|
||||
if (glyph->height > max_height)
|
||||
max_height = glyph->height;
|
||||
xoff += glyph->width;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
font_atlas_blit(struct font_atlas *atlas, FT_GlyphSlot glyph,
|
||||
size_t off_x, size_t off_y)
|
||||
{
|
||||
size_t y, x;
|
||||
size_t width = glyph->bitmap.width;
|
||||
size_t height = glyph->bitmap.rows;
|
||||
const size_t pitch = atlas->dim * FONT_ATLAS_DEPTH;
|
||||
for (y = 0; y < height; y++) {
|
||||
size_t x_off = off_x * FONT_ATLAS_DEPTH;
|
||||
size_t y_off = (off_y + y) * pitch;
|
||||
unsigned char *dst = &atlas->memory[y_off + x_off];
|
||||
for (x = 0; x < width; ++x) {
|
||||
dst[0] = 255;
|
||||
dst[1] = 255;
|
||||
dst[2] = 255;
|
||||
dst[3] = glyph->bitmap.buffer[y * width + x];
|
||||
dst += FONT_ATLAS_DEPTH;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
font_bake_glyphes(FT_Face face, struct font_atlas *atlas,
|
||||
const struct font *font)
|
||||
{
|
||||
size_t i;
|
||||
int ft_err;
|
||||
for (i = 0; i < atlas->range; ++i) {
|
||||
size_t x, y;
|
||||
struct font_glyph *glyph = &font->glyphes[i];
|
||||
unsigned int index = FT_Get_Char_Index(face, i);
|
||||
|
||||
if (!index) continue;
|
||||
ft_err = FT_Load_Glyph(face, index, 0);
|
||||
if (ft_err) continue;
|
||||
ft_err = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
|
||||
if (ft_err) continue;
|
||||
|
||||
x = (gui_size)(glyph->uv[0].u * (gui_float)atlas->dim);
|
||||
y = (gui_size)(glyph->uv[0].v * (gui_float)atlas->dim);
|
||||
font_atlas_blit(atlas, face->glyph, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
font_load(struct font *font, struct font_atlas *atlas, unsigned int height,
|
||||
const unsigned char *data, size_t size)
|
||||
{
|
||||
int ret = 1;
|
||||
FT_Library ft_lib;
|
||||
FT_Face ft_face;
|
||||
|
||||
assert(font);
|
||||
assert(atlas);
|
||||
assert(font->glyphes);
|
||||
assert(atlas->memory);
|
||||
|
||||
if (!font || !atlas)
|
||||
return gui_false;
|
||||
if (!font->glyphes || !atlas->memory)
|
||||
return gui_false;
|
||||
if (FT_Init_FreeType(&ft_lib))
|
||||
return gui_false;
|
||||
if (FT_New_Memory_Face(ft_lib, data, (FT_Long)size, 0, &ft_face))
|
||||
goto cleanup;
|
||||
if (FT_Select_Charmap(ft_face, FT_ENCODING_UNICODE))
|
||||
goto failed;
|
||||
if (FT_Set_Char_Size(ft_face, height << 6, height << 6, 96, 96))
|
||||
goto failed;
|
||||
|
||||
font_load_glyphes(ft_face, font, atlas->range);
|
||||
font_pack_glyphes(font, atlas->dim, atlas->dim, atlas->range);
|
||||
font_bake_glyphes(ft_face, atlas, font);
|
||||
|
||||
failed:
|
||||
FT_Done_Face(ft_face);
|
||||
cleanup:
|
||||
FT_Done_FreeType(ft_lib);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gui_size
|
||||
font_get_text_width(void *handle, const gui_char *t, gui_size l)
|
||||
{
|
||||
long unicode;
|
||||
size_t text_width = 0;
|
||||
const struct font_glyph *glyph;
|
||||
size_t text_len = 0;
|
||||
size_t glyph_len;
|
||||
struct font *font = handle;
|
||||
|
||||
assert(font);
|
||||
if (!t || !l) return 0;
|
||||
|
||||
glyph_len = gui_utf_decode(t, &unicode, l);
|
||||
while (text_len <= l && glyph_len) {
|
||||
if (unicode == GUI_UTF_INVALID) return 0;
|
||||
glyph = (unicode < font->glyph_count) ? &font->glyphes[unicode] : font->fallback;
|
||||
glyph = (glyph->code == 0) ? font->fallback : glyph;
|
||||
|
||||
text_width += (gui_size)(glyph->xadvance * font->scale);
|
||||
glyph_len = gui_utf_decode(t + text_len, &unicode, l - text_len);
|
||||
text_len += glyph_len;
|
||||
}
|
||||
return text_width;
|
||||
}
|
||||
|
||||
static void
|
||||
font_draw_text(const struct font *font, float x, float y,
|
||||
struct gui_color color, const unsigned char *text, size_t len)
|
||||
{
|
||||
struct gui_rect clip;
|
||||
gui_size text_len;
|
||||
gui_long unicode;
|
||||
const struct font_glyph *g;
|
||||
|
||||
text_len = gui_utf_decode(text, &unicode, len);
|
||||
glBindTexture(GL_TEXTURE_2D, font->texture);
|
||||
glColor4ub(color.r, color.g, color.b, color.a);
|
||||
glBegin(GL_QUADS);
|
||||
while (text_len <= len) {
|
||||
float gx, gy, gh, gw, char_width = 0;
|
||||
if (unicode == GUI_UTF_INVALID) break;
|
||||
g = (unicode < font->glyph_count) ?
|
||||
&font->glyphes[unicode] :
|
||||
font->fallback;
|
||||
g = (g->code == 0) ? font->fallback : g;
|
||||
|
||||
gw = g->width * font->scale;
|
||||
gh = g->height * font->scale;
|
||||
gx = x + g->xoff * font->scale;
|
||||
gy = y + (font->height - g->yoff * font->scale);
|
||||
char_width = g->xadvance * font->scale;
|
||||
|
||||
glTexCoord2f(g->uv[0].u, g->uv[0].v);
|
||||
glVertex2f(gx, gy);
|
||||
glTexCoord2f(g->uv[1].u, g->uv[0].v);
|
||||
glVertex2f(gx + gw, gy);
|
||||
glTexCoord2f(g->uv[1].u, g->uv[1].v);
|
||||
glVertex2f(gx + gw, gy + gh);
|
||||
glTexCoord2f(g->uv[0].u, g->uv[1].v);
|
||||
glVertex2f(gx, gy + gh);
|
||||
text_len += gui_utf_decode(text + text_len, &unicode, len - text_len);
|
||||
x += char_width;
|
||||
}
|
||||
glEnd();
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
font_del(struct font *font)
|
||||
{
|
||||
glDeleteTextures(1, &font->texture);
|
||||
free(font->glyphes);
|
||||
free(font);
|
||||
}
|
||||
|
||||
static struct font*
|
||||
font_new(const char *path, unsigned int font_height, unsigned int bake_height,
|
||||
size_t range, enum font_atlas_dimension dim)
|
||||
{
|
||||
gui_byte *ttf_blob;
|
||||
gui_size ttf_blob_size;
|
||||
struct font_atlas atlas;
|
||||
struct font *font = calloc(sizeof(struct font), 1);
|
||||
|
||||
atlas.dim = dim;
|
||||
atlas.range = range;
|
||||
atlas.size = atlas.dim * atlas.dim * FONT_ATLAS_DEPTH;
|
||||
atlas.memory = calloc((gui_size)atlas.size, 1);
|
||||
|
||||
memset(font, 0, sizeof(*font));
|
||||
font->glyph_count = (unsigned int)atlas.range;
|
||||
font->glyphes = calloc(atlas.range, sizeof(struct font_glyph));
|
||||
font->fallback = &font->glyphes['?'];
|
||||
font->scale = (float)font_height / (gui_float)bake_height;
|
||||
font->height = (float)font_height;
|
||||
|
||||
ttf_blob = (unsigned char*)file_load(path, &ttf_blob_size);
|
||||
if (!font_load(font, &atlas, bake_height, ttf_blob, ttf_blob_size))
|
||||
goto failed;
|
||||
|
||||
glGenTextures(1, &font->texture);
|
||||
glBindTexture(GL_TEXTURE_2D, font->texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dim, dim, 0,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, atlas.memory);
|
||||
|
||||
free(atlas.memory);
|
||||
free(ttf_blob);
|
||||
return font;
|
||||
|
||||
failed:
|
||||
free(atlas.memory);
|
||||
free(ttf_blob);
|
||||
font_del(font);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
draw_line(float x0, float y0, float x1, float y1, struct gui_color color)
|
||||
{
|
||||
glColor4ub(color.r, color.g, color.b, color.a);
|
||||
glBegin(GL_LINES);
|
||||
glVertex2f(x0, y0);
|
||||
glVertex2f(x1, y1);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
static void
|
||||
draw_rect(float x, float y, float w, float h, struct gui_color color)
|
||||
{
|
||||
glColor4ub(color.r, color.g, color.b, color.a);
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2f(x, y);
|
||||
glVertex2f(x + w, y);
|
||||
glVertex2f(x + w, y + h);
|
||||
glVertex2f(x, y + h);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
static void
|
||||
draw_circle(float x, float y, float r, struct gui_color color)
|
||||
{
|
||||
int i;
|
||||
float a0 = 0.0f;
|
||||
const float a_step = (2 * 3.141592654f)/22.0f;
|
||||
x += r; y += r;
|
||||
glColor4ub(color.r, color.g, color.b, color.a);
|
||||
glBegin(GL_TRIANGLES);
|
||||
for (i = 0; i < CIRCLE_SEGMENTS; i++) {
|
||||
const float a1 = ((i + 1) == CIRCLE_SEGMENTS) ? 0.0f : a0 + a_step;
|
||||
const float p0x = x + (float)cos(a0) * r;
|
||||
const float p0y = y + (float)sin(a0) * r;
|
||||
const float p1x = x + (float)cos(a1) * r;
|
||||
const float p1y = y + (float)sin(a1) * r;
|
||||
|
||||
glVertex2f(p0x, p0y);
|
||||
glVertex2f(p1x, p1y);
|
||||
glVertex2f(x, y);
|
||||
a0 = a1;
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
static void
|
||||
demo_panel(struct gui_panel_layout *panel, struct demo *demo)
|
||||
{
|
||||
gui_int i = 0;
|
||||
enum {HISTO, PLOT};
|
||||
const char *shelfs[] = {"Histogram", "Lines"};
|
||||
const gui_float values[] = {8.0f, 15.0f, 20.0f, 12.0f, 30.0f};
|
||||
const char *items[] = {"Fist", "Pistol", "Shotgun", "Railgun", "BFG"};
|
||||
const char *options[] = {"easy", "normal", "hard", "hell", "doom", "godlike"};
|
||||
struct gui_panel_layout tab;
|
||||
|
||||
/* Tabs */
|
||||
demo->tab_min = gui_panel_tab_begin(panel, &tab, "Difficulty", demo->tab_min);
|
||||
gui_panel_row(&tab, 30, 3);
|
||||
for (i = 0; i < (gui_int)LEN(options); i++) {
|
||||
if (gui_panel_option(&tab, options[i], demo->option == i))
|
||||
demo->option = i;
|
||||
}
|
||||
gui_panel_tab_end(panel, &tab);
|
||||
|
||||
/* Shelf */
|
||||
gui_panel_row(panel, 200, 2);
|
||||
demo->cur = gui_panel_shelf_begin(panel,&tab,shelfs,LEN(shelfs),demo->cur,demo->shelf_off);
|
||||
gui_panel_row(&tab, 100, 1);
|
||||
if (demo->cur == HISTO) {
|
||||
gui_panel_histo(&tab, values, LEN(values));
|
||||
} else {
|
||||
gui_panel_plot(&tab, values, LEN(values));
|
||||
}
|
||||
demo->shelf_off = gui_panel_shelf_end(panel, &tab);
|
||||
|
||||
/* Group */
|
||||
gui_panel_group_begin(panel, &tab, "Options", demo->group_off);
|
||||
gui_panel_row(&tab, 30, 1);
|
||||
if (gui_panel_button_text(&tab, "button", GUI_BUTTON_DEFAULT))
|
||||
fprintf(stdout, "button pressed!\n");
|
||||
demo->toggle = gui_panel_button_toggle(&tab, "toggle", demo->toggle);
|
||||
demo->check = gui_panel_check(&tab, "advanced", demo->check);
|
||||
demo->slider = gui_panel_slider(&tab, 0, demo->slider, 10, 1.0f);
|
||||
demo->prog = gui_panel_progress(&tab, demo->prog, 100, gui_true);
|
||||
demo->item_cur = gui_panel_selector(&tab, items, LEN(items), demo->item_cur);
|
||||
demo->spinner = gui_panel_spinner(&tab, 0, demo->spinner, 250, 10, &demo->spin_act);
|
||||
demo->in_len = gui_panel_input(&tab,demo->in_buf,demo->in_len,
|
||||
MAX_BUFFER,&demo->in_act,GUI_INPUT_DEFAULT);
|
||||
demo->group_off = gui_panel_group_end(panel, &tab);
|
||||
}
|
||||
|
||||
#define glerror() glerror_(__FILE__, __LINE__)
|
||||
static void
|
||||
glerror_(const char *file, int line)
|
||||
{
|
||||
GLenum code = glGetError();
|
||||
if (code == GL_INVALID_ENUM)
|
||||
fprintf(stdout, "[GL] Error: (%s:%d) invalid value!\n", file, line);
|
||||
else if (code == GL_INVALID_OPERATION)
|
||||
fprintf(stdout, "[GL] Error: (%s:%d) invalid operation!\n", file, line);
|
||||
else if (code == GL_INVALID_FRAMEBUFFER_OPERATION)
|
||||
fprintf(stdout, "[GL] Error: (%s:%d) invalid frame op!\n", file, line);
|
||||
else if (code == GL_OUT_OF_MEMORY)
|
||||
fprintf(stdout, "[GL] Error: (%s:%d) out of memory!\n", file, line);
|
||||
else if (code == GL_STACK_UNDERFLOW)
|
||||
fprintf(stdout, "[GL] Error: (%s:%d) stack underflow!\n", file, line);
|
||||
else if (code == GL_STACK_OVERFLOW)
|
||||
fprintf(stdout, "[GL] Error: (%s:%d) stack overflow!\n", file, line);
|
||||
}
|
||||
|
||||
static void
|
||||
draw(struct gui_command_list *list, int width, int height)
|
||||
{
|
||||
static const struct gui_color col = {255, 0, 0, 255};
|
||||
struct gui_command *cmd;
|
||||
if (!list->count) return;
|
||||
|
||||
glPushAttrib(GL_ENABLE_BIT|GL_COLOR_BUFFER_BIT|GL_TRANSFORM_BIT);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glOrtho(0.0f, width, height, 0.0f, 0.0f, 1.0f);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glerror();
|
||||
|
||||
cmd = list->begin;
|
||||
while (cmd != list->end) {
|
||||
switch (cmd->type) {
|
||||
case GUI_COMMAND_NOP: break;
|
||||
case GUI_COMMAND_SCISSOR: {
|
||||
struct gui_command_scissor *s = (void*)cmd;
|
||||
glScissor(s->x, height - (s->y + s->h), s->w, s->h);
|
||||
} break;
|
||||
case GUI_COMMAND_LINE: {
|
||||
struct gui_command_line *l = (void*)cmd;
|
||||
draw_line(l->begin[0], l->begin[1], l->end[0], l->end[1], l->color);
|
||||
} break;
|
||||
case GUI_COMMAND_RECT: {
|
||||
struct gui_command_rect *r = (void*)cmd;
|
||||
draw_rect(r->x, r->y, r->w, r->h, r->color);
|
||||
} break;
|
||||
case GUI_COMMAND_CIRCLE: {
|
||||
unsigned i;
|
||||
struct gui_command_circle *c = (void*)cmd;
|
||||
draw_circle(c->x, c->y, (float)c->w / 2.0f, c->color);
|
||||
} break;
|
||||
case GUI_COMMAND_TRIANGLE: {
|
||||
struct gui_command_triangle *t = (void*)cmd;
|
||||
glColor4ub(t->color.r, t->color.g, t->color.b, t->color.a);
|
||||
glBegin(GL_TRIANGLES);
|
||||
glVertex2f(t->a[0], t->a[1]);
|
||||
glVertex2f(t->b[0], t->b[1]);
|
||||
glVertex2f(t->c[0], t->c[1]);
|
||||
glEnd();
|
||||
} break;
|
||||
case GUI_COMMAND_TEXT: {
|
||||
struct gui_command_text *t = (void*)cmd;
|
||||
font_draw_text(t->font, t->x, t->y, t->fg, t->string, t->length);
|
||||
} break;
|
||||
default: break;
|
||||
}
|
||||
cmd = cmd->next;
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glPopAttrib();
|
||||
}
|
||||
|
||||
static void
|
||||
key(struct gui_input *in, SDL_Event *evt, gui_bool down)
|
||||
{
|
||||
SDL_Keycode sym = evt->key.keysym.sym;
|
||||
if (sym == SDLK_LCTRL || sym == SDLK_RCTRL)
|
||||
gui_input_key(in, GUI_KEY_CTRL, down);
|
||||
else if (sym == SDLK_RSHIFT || sym == SDLK_LSHIFT)
|
||||
gui_input_key(in, GUI_KEY_SHIFT, down);
|
||||
else if (sym == SDLK_DELETE)
|
||||
gui_input_key(in, GUI_KEY_DEL, down);
|
||||
else if (sym == SDLK_RETURN)
|
||||
gui_input_key(in, GUI_KEY_ENTER, down);
|
||||
else if (sym == SDLK_SPACE)
|
||||
gui_input_key(in, GUI_KEY_SPACE, down);
|
||||
else if (sym == SDLK_BACKSPACE)
|
||||
gui_input_key(in, GUI_KEY_BACKSPACE, down);
|
||||
}
|
||||
|
||||
static void
|
||||
motion(struct gui_input *in, SDL_Event *evt)
|
||||
{
|
||||
const gui_int x = evt->motion.x;
|
||||
const gui_int y = evt->motion.y;
|
||||
gui_input_motion(in, x, y);
|
||||
}
|
||||
|
||||
static void
|
||||
btn(struct gui_input *in, SDL_Event *evt, gui_bool down)
|
||||
{
|
||||
const gui_int x = evt->button.x;
|
||||
const gui_int y = evt->button.y;
|
||||
if (evt->button.button == SDL_BUTTON_LEFT)
|
||||
gui_input_button(in, x, y, down);
|
||||
}
|
||||
|
||||
static void
|
||||
resize(SDL_Event *evt)
|
||||
{
|
||||
if (evt->window.event != SDL_WINDOWEVENT_RESIZED) return;
|
||||
glViewport(0, 0, evt->window.data1, evt->window.data2);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
/* Platform */
|
||||
const char *font_path;
|
||||
SDL_Window *win;
|
||||
SDL_GLContext glContext;
|
||||
struct font *glfont;
|
||||
gui_bool running = gui_true;
|
||||
int win_width, win_height;
|
||||
unsigned int started;
|
||||
unsigned int dt;
|
||||
int width = 0, height = 0;
|
||||
struct demo demo;
|
||||
|
||||
/* GUI */
|
||||
struct gui_input in;
|
||||
struct gui_font font;
|
||||
struct gui_memory memory;
|
||||
struct gui_memory_status status;
|
||||
struct gui_config config;
|
||||
struct gui_canvas canvas;
|
||||
struct gui_command_buffer buffer;
|
||||
struct gui_command_list list;
|
||||
struct gui_panel panel;
|
||||
struct gui_panel_layout layout;
|
||||
|
||||
font_path = argv[1];
|
||||
if (argc < 2) {
|
||||
fprintf(stdout, "Missing TTF Font file argument!");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* SDL */
|
||||
SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_EVENTS);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
win = SDL_CreateWindow("Demo",
|
||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||
WIN_WIDTH, WIN_HEIGHT, SDL_WINDOW_OPENGL|SDL_WINDOW_SHOWN);
|
||||
glContext = SDL_GL_CreateContext(win);
|
||||
SDL_GetWindowSize(win, &win_width, &win_height);
|
||||
glViewport(0, 0, WIN_WIDTH, WIN_HEIGHT);
|
||||
glfont = font_new(font_path, 10, 16, 255, FONT_ATLAS_DIM_256);
|
||||
|
||||
/* GUI */
|
||||
memset(&in, 0, sizeof in);
|
||||
memory.memory = calloc(MAX_MEMORY, 1);
|
||||
memory.size = MAX_MEMORY;
|
||||
gui_buffer_init_fixed(&buffer, &memory, GUI_CLIP);
|
||||
|
||||
font.userdata = glfont;
|
||||
font.height = glfont->height;
|
||||
font.width = font_get_text_width;
|
||||
|
||||
gui_default_config(&config);
|
||||
gui_panel_init(&panel, 50, 50, 420, 300,
|
||||
GUI_PANEL_BORDER|GUI_PANEL_MOVEABLE|
|
||||
GUI_PANEL_CLOSEABLE|GUI_PANEL_SCALEABLE|
|
||||
GUI_PANEL_MINIMIZABLE, &config, &font);
|
||||
|
||||
/* Demo */
|
||||
memset(&demo, 0, sizeof(demo));
|
||||
demo.tab_min = gui_true;
|
||||
demo.spinner = 100;
|
||||
demo.slider = 2.0f;
|
||||
demo.prog = 60;
|
||||
|
||||
while (running) {
|
||||
/* Input */
|
||||
SDL_Event evt;
|
||||
started = SDL_GetTicks();
|
||||
gui_input_begin(&in);
|
||||
while (SDL_PollEvent(&evt)) {
|
||||
if (evt.type == SDL_WINDOWEVENT) resize(&evt);
|
||||
else if (evt.type == SDL_QUIT) goto cleanup;
|
||||
else if (evt.type == SDL_KEYUP) key(&in, &evt, gui_false);
|
||||
else if (evt.type == SDL_KEYDOWN) key(&in, &evt, gui_true);
|
||||
else if (evt.type == SDL_MOUSEBUTTONDOWN) btn(&in, &evt, gui_true);
|
||||
else if (evt.type == SDL_MOUSEBUTTONUP) btn(&in, &evt, gui_false);
|
||||
else if (evt.type == SDL_MOUSEMOTION) motion(&in, &evt);
|
||||
}
|
||||
gui_input_end(&in);
|
||||
|
||||
/* GUI */
|
||||
SDL_GetWindowSize(win, &width, &height);
|
||||
gui_buffer_begin(&canvas, &buffer, (gui_size)width, (gui_size)height);
|
||||
running = gui_panel_begin(&layout, &panel , "Demo", &canvas, &in);
|
||||
demo_panel(&layout, &demo);
|
||||
gui_panel_end(&layout, &panel);
|
||||
gui_buffer_end(&list, &buffer, &canvas, &status);
|
||||
|
||||
/* Draw */
|
||||
glClearColor(0.4f, 0.4f, 0.4f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
draw(&list, width, height);
|
||||
SDL_GL_SwapWindow(win);
|
||||
|
||||
/* Timing */
|
||||
dt = SDL_GetTicks() - started;
|
||||
if (dt < DTIME)
|
||||
SDL_Delay(DTIME - dt);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
/* Cleanup */
|
||||
free(memory.memory);
|
||||
font_del(glfont);
|
||||
SDL_GL_DeleteContext(glContext);
|
||||
SDL_DestroyWindow(win);
|
||||
SDL_Quit();
|
||||
return 0;
|
||||
}
|
||||
|
45
gui.c
45
gui.c
|
@ -12,7 +12,6 @@
|
|||
#endif
|
||||
|
||||
#define NULL (void*)0
|
||||
#define UTF_INVALID 0xFFFD
|
||||
#define MAX_NUMBER_BUFFER 64
|
||||
#define PASTE(a,b) a##b
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
|
@ -123,18 +122,18 @@ unify(struct gui_rect *clip, const struct gui_rect *a, gui_float x0, gui_float y
|
|||
}
|
||||
|
||||
static gui_size
|
||||
utf_validate(long *u, gui_size i)
|
||||
gui_utf_validate(long *u, gui_size i)
|
||||
{
|
||||
if (!u) return 0;
|
||||
if (!BETWEEN(*u, utfmin[i], utfmax[i]) ||
|
||||
BETWEEN(*u, 0xD800, 0xDFFF))
|
||||
*u = UTF_INVALID;
|
||||
*u = GUI_UTF_INVALID;
|
||||
for (i = 1; *u > utfmax[i]; ++i);
|
||||
return i;
|
||||
}
|
||||
|
||||
static gui_long
|
||||
utf_decode_byte(gui_char c, gui_size *i)
|
||||
gui_utf_decode_byte(gui_char c, gui_size *i)
|
||||
{
|
||||
if (!i) return 0;
|
||||
for(*i = 0; *i < LEN(utfmask); ++(*i)) {
|
||||
|
@ -144,49 +143,49 @@ utf_decode_byte(gui_char c, gui_size *i)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static gui_size
|
||||
utf_decode(const gui_char *c, gui_long *u, gui_size clen)
|
||||
gui_size
|
||||
gui_utf_decode(const gui_char *c, gui_long *u, gui_size clen)
|
||||
{
|
||||
gui_size i, j, len, type;
|
||||
gui_long udecoded;
|
||||
|
||||
*u = UTF_INVALID;
|
||||
*u = GUI_UTF_INVALID;
|
||||
if (!c || !u) return 0;
|
||||
if (!clen) return 0;
|
||||
udecoded = utf_decode_byte(c[0], &len);
|
||||
udecoded = gui_utf_decode_byte(c[0], &len);
|
||||
if (!BETWEEN(len, 1, GUI_UTF_SIZE))
|
||||
return 1;
|
||||
|
||||
for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
|
||||
udecoded = (udecoded << 6) | utf_decode_byte(c[i], &type);
|
||||
udecoded = (udecoded << 6) | gui_utf_decode_byte(c[i], &type);
|
||||
if (type != 0)
|
||||
return j;
|
||||
}
|
||||
if (j < len)
|
||||
return 0;
|
||||
*u = udecoded;
|
||||
utf_validate(u, len);
|
||||
gui_utf_validate(u, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
static gui_char
|
||||
utf_encode_byte(gui_long u, gui_size i)
|
||||
gui_utf_encode_byte(gui_long u, gui_size i)
|
||||
{
|
||||
return (gui_char)(utfbyte[i] | (u & ~utfmask[i]));
|
||||
}
|
||||
|
||||
static gui_size
|
||||
utf_encode(gui_long u, gui_char *c, gui_size clen)
|
||||
gui_size
|
||||
gui_utf_encode(gui_long u, gui_char *c, gui_size clen)
|
||||
{
|
||||
gui_size len, i;
|
||||
len = utf_validate(&u, 0);
|
||||
len = gui_utf_validate(&u, 0);
|
||||
if (clen < len || !len)
|
||||
return 0;
|
||||
for (i = len - 1; i != 0; --i) {
|
||||
c[i] = utf_encode_byte(u, 0);
|
||||
c[i] = gui_utf_encode_byte(u, 0);
|
||||
u >>= 6;
|
||||
}
|
||||
c[0] = utf_encode_byte(u, len);
|
||||
c[0] = gui_utf_encode_byte(u, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
@ -202,10 +201,10 @@ gui_triangle_from_direction(struct gui_vec2 *result, gui_float x, gui_float y,
|
|||
h = MAX(4 * pad_y, h);
|
||||
w = w - 2 * pad_x;
|
||||
h = h - 2 * pad_y;
|
||||
w_half = w / 2.0f;
|
||||
h_half = h / 2.0f;
|
||||
x = x + pad_x;
|
||||
y = y + pad_y;
|
||||
w_half = w / 2.0f;
|
||||
h_half = h / 2.0f;
|
||||
|
||||
if (direction == GUI_UP) {
|
||||
result[0].x = x + w_half;
|
||||
|
@ -289,9 +288,9 @@ gui_input_char(struct gui_input *in, const gui_glyph glyph)
|
|||
gui_long unicode;
|
||||
assert(in);
|
||||
if (!in) return;
|
||||
len = utf_decode(glyph, &unicode, GUI_UTF_SIZE);
|
||||
len = gui_utf_decode(glyph, &unicode, GUI_UTF_SIZE);
|
||||
if (len && ((in->text_len + len) < GUI_INPUT_MAX)) {
|
||||
utf_encode(unicode, &in->text[in->text_len], GUI_INPUT_MAX - in->text_len);
|
||||
gui_utf_encode(unicode, &in->text[in->text_len], GUI_INPUT_MAX - in->text_len);
|
||||
in->text_len += len;
|
||||
}
|
||||
}
|
||||
|
@ -636,7 +635,7 @@ gui_buffer_input(gui_char *buffer, gui_size length, gui_size max,
|
|||
assert(buffer);
|
||||
assert(in);
|
||||
|
||||
glyph_len = utf_decode(in->text, &unicode, in->text_len);
|
||||
glyph_len = gui_utf_decode(in->text, &unicode, in->text_len);
|
||||
while (glyph_len && ((text_len + glyph_len) <= in->text_len) && (length + text_len) < max) {
|
||||
if (gui_filter_input(unicode, glyph_len, filter)) {
|
||||
gui_size i = 0;
|
||||
|
@ -644,7 +643,7 @@ gui_buffer_input(gui_char *buffer, gui_size length, gui_size max,
|
|||
buffer[length++] = in->text[text_len + i];
|
||||
}
|
||||
text_len = text_len + glyph_len;
|
||||
glyph_len = utf_decode(in->text + text_len, &unicode, in->text_len - text_len);
|
||||
glyph_len = gui_utf_decode(in->text + text_len, &unicode, in->text_len - text_len);
|
||||
}
|
||||
return text_len;
|
||||
}
|
||||
|
@ -698,7 +697,7 @@ gui_input(const struct gui_canvas *canvas, gui_float x, gui_float y, gui_float w
|
|||
gui_size text_width = font->width(font->userdata, buffer, text_len);
|
||||
while (text_len && (text_width + cursor_width) > (gui_size)label_w) {
|
||||
gui_long unicode;
|
||||
offset += utf_decode(&buffer[offset], &unicode, text_len);
|
||||
offset += gui_utf_decode(&buffer[offset], &unicode, text_len);
|
||||
text_len = len - offset;
|
||||
text_width = font->width(font->userdata, &buffer[offset], text_len);
|
||||
}
|
||||
|
|
3
gui.h
3
gui.h
|
@ -12,6 +12,7 @@ extern "C" {
|
|||
|
||||
#define GUI_UTF_SIZE 4
|
||||
#define GUI_INPUT_MAX 16
|
||||
#define GUI_UTF_INVALID 0xFFFD
|
||||
|
||||
#ifdef GUI_USE_FIXED_TYPES
|
||||
#include <stdint.h>
|
||||
|
@ -401,6 +402,8 @@ struct gui_panel_stack {
|
|||
};
|
||||
|
||||
/* Input */
|
||||
gui_size gui_utf_decode(const gui_char*, gui_long*, gui_size);
|
||||
gui_size gui_utf_encode(gui_long, gui_char*, gui_size);
|
||||
void gui_input_begin(struct gui_input*);
|
||||
void gui_input_motion(struct gui_input*, gui_int x, gui_int y);
|
||||
void gui_input_key(struct gui_input*, enum gui_keys, gui_bool down);
|
||||
|
|
Loading…
Reference in New Issue