This commit is contained in:
vurtun 2015-03-03 17:24:02 +01:00
commit e3aa8df99d
6 changed files with 972 additions and 0 deletions

32
LICENSE Normal file
View File

@ -0,0 +1,32 @@
The MIT License (MIT)
Copyright (c) 2015 vurtun
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

35
Makefile Normal file
View File

@ -0,0 +1,35 @@
# Install
BIN = x11
# Compiler
CC = gcc
DCC = clang
# Flags
CFLAGS = -std=c89 -pedantic -Wno-deprecated-declarations -D_POSIX_C_SOURCE=200809L
CFLAGS += -g -Wall -Wextra -Werror -Wformat -Wunreachable-code
CFLAGS += -Winline -Wshadow -Wwrite-strings -Wno-unused-variable -Wno-unused-parameter -Wno-unused-function
CFLAGS += -Wstrict-prototypes -Wold-style-definition
CFLAGS += -Wredundant-decls -Wnested-externs -Wmissing-include-dirs
CFLAGS += -Wshadow -Wpointer-arith -Wcast-qual -Wmissing-prototypes
CFLAGS += -Wswitch-default -Wundef -Wstrict-overflow=5
CFLAGS += -Winit-self -Wstrict-aliasing -Wunused
SRC = gui.c x11.c
OBJ = $(SRC:.c=.o)
# Modes
.PHONY: gcc
gcc: CC = gcc
gcc: CFLAGS += -Wno-unused-local-typedefs
gcc: $(BIN)
.PHONY: clang
clang: CC = clang
clang: $(BIN)
$(BIN):
@mkdir -p bin
rm -f bin/$(BIN) $(OBJS)
$(CC) $(SRC) $(CFLAGS) -o bin/$(BIN) -lX11 -lGL -lGLU

3
Readme.md Normal file
View File

@ -0,0 +1,3 @@
# TINYGUI
# License
(The MIT License)

443
gui.c Normal file
View File

@ -0,0 +1,443 @@
/*
Copyright (c) 2015
vurtun <polygone@gmx.net>
MIT licence
*/
#include "gui.h"
#define NULL (void*)0
#define UTF_INVALID 0xFFFD
#define PI 3.141592654f
#define PASTE(a,b) a##b
#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 ABS(a) (((a) < 0) ? -(a) : (a))
#define UNUSED(a) ((void)(a))
#define DEG2RAD(a) ((a) * (PI / 180.0f))
#define RAD2DEG(a) ((a) * (180.0f / PI))
#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
#define INBOX(x, y, x0, y0, x1, y1) (BETWEEN(x, x0, x1) && BETWEEN(y, y0, y1))
#define ASSERT_LINE(p, l, f) \
typedef char PASTE(assertion_failed_##f##_,l)[2*!!(p)-1];
#define ASSERT(predicate) ASSERT_LINE(predicate,__LINE__,__FILE__)
#define vec2_load(v,a,b) (v).x = (a), (v).y = (b)
#define vec2_cpy(to,from) (to).x = (from).x, (to).y = (from).y
#define vec2_len(v) ((float)fsqrt((v).x*(v).x+(v).y*(v).y))
#define vec2_sub(r,a,b) do {(r).x=(a).x-(b).x; (r).y=(a).y-(b).y;} while(0)
#define vec2_add(r,a,b) do {(r).x=(a).x+(b).x; (r).y=(a).y+(b).y;} while(0)
#define vec2_muls(r, v, s) do {(r).x=(v).x*(s); (r).y=(v).y*(s);} while(0)
#define vec2_norm(r, v) do {float _ = vec2_len(v); if (_) vec2_muls(r, v, 1.0f/_);} while(0)
static const gui_texture null_tex = (void*)0;
static const struct gui_rect null_rect = {-9999, -9999, 9999 * 2, 9999 * 2};
static gui_char utfbyte[GUI_UTF_SIZE+1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
static gui_char utfmask[GUI_UTF_SIZE+1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
static long utfmin[GUI_UTF_SIZE+1] = {0, 0, 0x80, 0x800, 0x100000};
static long utfmax[GUI_UTF_SIZE+1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
static gui_float
fsqrt(float x)
{
float xhalf = 0.5f*x;
union {float f; int i;} val;
ASSERT(sizeof(int) == sizeof(float));
val.f = x;
val.i = 0x5f375a86 - (val.i>>1);
val.f = val.f*(1.5f-xhalf*val.f*val.f);
return 1.0f/val.f;
}
static gui_size
utf_validate(long *u, gui_size i)
{
if (!BETWEEN(*u, utfmin[i], utfmax[i]) ||
BETWEEN(*u, 0xD800, 0xDFFF))
*u = UTF_INVALID;
for (i = 1; *u > utfmax[i]; ++i);
return i;
}
static long
utf_decode_byte(gui_char c, gui_size *i)
{
for(*i = 0; *i < LEN(utfmask); ++(*i)) {
if ((c & utfmask[*i]) == utfbyte[*i])
return c & ~utfmask[*i];
}
return 0;
}
static gui_size
utf_decode(gui_char *c, long *u, gui_size clen)
{
gui_size i, j, len, type;
long udecoded;
*u = UTF_INVALID;
if (!clen) return 0;
udecoded = 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);
if (type != 0)
return j;
}
if (j < len)
return 0;
*u = udecoded;
utf_validate(u, len);
return len;
}
static gui_char
utf_encode_byte(long u, gui_size i)
{
return utfbyte[i] | (u & ~utfmask[i]);
}
static gui_size
utf_encode(long u, gui_char *c, gui_size clen)
{
gui_size len, i;
len = utf_validate(&u, 0);
if (clen < len)
return 0;
for (i = len - 1; i != 0; --i) {
c[i] = utf_encode_byte(u, 0);
u >>= 6;
}
c[0] = utf_encode_byte(u, len);
return len;
}
void
gui_input_begin(struct gui_input *in)
{
in->mouse_clicked = 0;
in->glyph_count = 0;
vec2_cpy(in->mouse_prev, in->mouse_pos);
}
void
gui_input_motion(struct gui_input *in, gui_int x, gui_int y)
{
vec2_load(in->mouse_pos, x, y);
}
void
gui_input_key(struct gui_input *in, enum gui_keys key, gui_int down)
{
in->keys[key] = down;
if (key == GUI_KEY_SHIFT)
in->shift = gui_true;
if (key == GUI_KEY_CTRL)
in->ctrl = gui_true;
}
void
gui_input_button(struct gui_input *in, gui_int x, gui_int y, gui_int down)
{
if (in->mouse_down == down) return;
vec2_load(in->mouse_clicked_pos, x, y);
in->mouse_down = down;
in->mouse_clicked++;
}
void
gui_input_char(struct gui_input *in, gui_glyph glyph)
{
gui_size len = 0;
long unicode;
len = utf_decode(glyph, &unicode, GUI_UTF_SIZE);
if (len && in->glyph_count < GUI_INPUT_MAX) {
utf_encode(unicode, in->text[in->glyph_count], GUI_UTF_SIZE);
in->glyph_count++;
}
}
void
gui_input_end(struct gui_input *in)
{
vec2_sub(in->mouse_delta, in->mouse_pos, in->mouse_prev);
}
static struct gui_draw_command*
gui_push_command(struct gui_draw_list *list, gui_int count,
const struct gui_rect *rect, gui_texture tex)
{
gui_size memory = 0;
gui_size current;
struct gui_draw_command *cmd = NULL;
memory += sizeof(struct gui_draw_command);
memory += sizeof(struct gui_vertex) * count;
list->needed += memory;
current = (gui_byte*)list->end - (gui_byte*)list->begin;
if (list->size < (current + memory))
return NULL;
cmd = list->end;
list->end = (struct gui_draw_command*)((gui_byte*)list->end + memory);
cmd->vertexes = (struct gui_vertex*)(cmd + 1);
cmd->vertex_write = 0;
cmd->vertex_count = count;
cmd->clip_rect = *rect;
cmd->texture = tex;
return cmd;
}
static void
gui_vertex(struct gui_draw_command *cmd, gui_float x, gui_float y,
struct gui_color col)
{
const gui_int i = cmd->vertex_write;
if (i < 0 || i >= cmd->vertex_count) return;
cmd->vertexes[i].pos.x = x;
cmd->vertexes[i].pos.y = y;
cmd->vertexes[i].color = col;
cmd->vertexes[i].uv.u = 0.0f;
cmd->vertexes[i].uv.v = 0.0f;
cmd->vertex_write++;
}
static void
gui_vertex_line(struct gui_draw_command* cmd, gui_float x0, gui_float y0,
gui_float x1, gui_float y1, struct gui_color col)
{
gui_float len;
struct gui_vec2 a, b, d;
struct gui_vec2 hn, hp0, hp1;
vec2_load(a, x0, y0);
vec2_load(b, x1, y1);
vec2_sub(d, b, a);
len = 0.5f / vec2_len(d);
vec2_muls(hn, d, len);
vec2_load(hp0, +hn.y, -hn.x);
vec2_load(hp1, -hn.y, +hn.x);
gui_vertex(cmd, a.x + hp0.x, a.y + hp0.y, col);
gui_vertex(cmd, b.x + hp0.x, b.y + hp0.y, col);
gui_vertex(cmd, a.x + hp1.x, a.y + hp1.y, col);
gui_vertex(cmd, b.x + hp0.x, b.y + hp0.y, col);
gui_vertex(cmd, b.x + hp1.x, b.y + hp1.y, col);
gui_vertex(cmd, a.x + hp1.x, a.y + hp1.y, col);
}
static void
gui_line(struct gui_draw_list *list, gui_float x0, gui_float y0,
gui_float x1, gui_float y1, struct gui_color col)
{
struct gui_draw_command *cmd;
if (col.a == 0) return;
cmd = gui_push_command(list, 6, &null_rect, null_tex);
if (!cmd) return;
gui_vertex_line(cmd, x0, y0, x1, y1, col);
}
static void
gui_trianglef(struct gui_draw_list *list, gui_float x0, gui_float y0,
gui_float x1, gui_float y1, gui_float x2, gui_float y2, struct gui_color c)
{
struct gui_draw_command *cmd;
if (c.a == 0) return;
cmd = gui_push_command(list, 3, &null_rect, null_tex);
if (!cmd) return;
gui_vertex(cmd, x0, y0, c);
gui_vertex(cmd, x1, y1, c);
gui_vertex(cmd, x2, y2, c);
}
static void
gui_rect(struct gui_draw_list *list, gui_float x, gui_float y,
gui_float w, gui_float h, struct gui_color col)
{
struct gui_draw_command *cmd;
if (col.a == 0) return;
cmd = gui_push_command(list, 6 * 4, &null_rect, null_tex);
if (!cmd) return;
gui_vertex_line(cmd, x, y, x + w, y, col);
gui_vertex_line(cmd, x + w, y, x + w, y + h, col);
gui_vertex_line(cmd, x + w, y + h, x, y + h, col);
gui_vertex_line(cmd, x, y + h, x, y, col);
}
static void
gui_rectf(struct gui_draw_list *list, gui_float x, gui_float y,
gui_float w, gui_float h, struct gui_color col)
{
struct gui_draw_command *cmd;
if (col.a == 0) return;
cmd = gui_push_command(list, 6, &null_rect, null_tex);
if (!cmd) return;
gui_vertex(cmd, x, y, col);
gui_vertex(cmd, x + w, y, col);
gui_vertex(cmd, x + w, y + h, col);
gui_vertex(cmd, x, y, col);
gui_vertex(cmd, x + w, y + h, col);
gui_vertex(cmd, x, y + h, col);
}
void
gui_begin(struct gui_draw_list *list, gui_byte *memory, gui_size size)
{
if (!list || !memory || !size) return;
list->begin = (struct gui_draw_command*)memory;
list->end = (struct gui_draw_command*)memory;
list->memory = memory;
list->size = size;
list->needed = 0;
}
const struct gui_draw_command*
gui_next(const struct gui_draw_list *list, const struct gui_draw_command *iter)
{
gui_size size = 0;
const struct gui_draw_command *cmd = NULL;
if (!list || !list->memory || !list->begin || !list->end || !list->size)
return NULL;
if (!iter || !iter->vertexes || iter < list->begin || iter > list->end)
return NULL;
size += sizeof(struct gui_draw_command);
size += sizeof(struct gui_vertex) * iter->vertex_count;
cmd = (const struct gui_draw_command*)((const gui_byte*)iter + size);
if (cmd >= list->end) return NULL;
return cmd;
}
gui_size
gui_end(struct gui_draw_list *list)
{
gui_size needed;
if (!list) return 0;
needed = list->needed;
list->needed = 0;
return needed;
}
gui_int
gui_button(struct gui_draw_list *list, const struct gui_input *in,
struct gui_color bg, struct gui_color fg,
gui_int x, gui_int y, gui_int w, gui_int h, gui_int pad,
const char *str, gui_int len)
{
gui_int ret = gui_false;
if (!list || !in) return gui_false;
if (!in->mouse_down && in->mouse_clicked) {
const gui_int mx = in->mouse_clicked_pos.x;
const gui_int my = in->mouse_clicked_pos.y;
if (INBOX(mx, my, x, y, x + w, y + h))
ret = gui_true;
}
gui_rectf(list, x, y, w, h, bg);
gui_rect(list, x, y, w, h, fg);
return ret;
}
gui_int
gui_slider(struct gui_draw_list *list, const struct gui_input *in,
struct gui_color bg, struct gui_color fg,
gui_int x, gui_int y, gui_int w, gui_int h, gui_int pad,
gui_float min, gui_float cur, gui_float max, gui_float step)
{
const gui_float cw = (w - 2 * pad) / (((max - min) + 1) / step);
const gui_int ch = h - 2 * pad;
gui_int cx = x + pad + (cw * (cur - min));
gui_int cy = y + pad;
const gui_int mx = in->mouse_pos.x;
const gui_int my = in->mouse_pos.y;
const gui_int dx = in->mouse_clicked_pos.x;
const gui_int dy = in->mouse_clicked_pos.y;
if (!list || !in) return gui_false;
if (in->mouse_down &&
INBOX(dx, dy, x, y, x + w, y + h) &&
INBOX(mx, my, x, y, x + w, y + h))
{
gui_float cnx;
const gui_float tmp = mx - cx + (cw / 2);
gui_float next = cur + step * ((tmp < 0) ? -1.0f : 1.0f);
next = CLAMP(min, next, max);
cnx = x + pad + (cw * (next - min));
if (INBOX(mx, my, cnx, cy, cnx + cw, cy + ch)) {
cur = next;
cx = cnx;
}
}
gui_rectf(list, x, y, w, h, bg);
gui_rectf(list, cx, cy, cw, ch, fg);
return cur;
}
gui_int
gui_progress(struct gui_draw_list *list, const struct gui_input *in,
struct gui_color bg, struct gui_color fg,
gui_int x, gui_int y, gui_int w, gui_int h, gui_int pad,
gui_float cur, gui_float max, gui_bool modifyable)
{
const gui_int mx = in->mouse_pos.x;
const gui_int my = in->mouse_pos.y;
gui_int cx, cy, cw, ch;
gui_float scale;
if (modifyable && in->mouse_down && INBOX(mx, my, x, y, x + w, y + h))
cur = max * (((gui_float)mx - (gui_float)x) / (gui_float)w);
if (!max) return cur;
cur = CLAMP(0, cur, max);
scale = (cur/max);
ch = h - 2 * pad;
cw = (w - 2 * pad) * scale;
cx = x + pad;
cy = y + pad;
gui_rectf(list, x, y, w, h, bg);
gui_rectf(list, cx, cy, cw, ch, fg);
return cur;
}
gui_int
gui_scroll(struct gui_draw_list *list, const struct gui_input *in,
struct gui_color bg, struct gui_color fg,
gui_int x, gui_int y, gui_int w, gui_int h,
gui_int offset, gui_int dst)
{
const gui_int p = w / 4;
const gui_int bs = w;
const gui_int by = y + h - bs;
const gui_int barh = h - 2 * bs;
const gui_int bary = y + bs;
const gui_float ratio = (gui_float)barh/(gui_float)dst;
const gui_float off = (gui_float)offset/(gui_float)dst;
const gui_int ch = (gui_int)(ratio * (gui_float)barh);
const gui_int cy = bary + (gui_int)(off * barh);
const gui_int cw = w;
const gui_int cx = x;
static const struct gui_color col = {255, 0, 0, 255};
gui_rectf(list, x, y, w, h, bg);
if (dst <= h) return 0;
gui_button(list, in, fg, bg, x, y, bs, bs, 0, "", 0);
gui_button(list, in, fg, bg, x, by, bs, bs, 0, "", 0);
gui_trianglef(list, x + (bs/2), y+p, x+(bs-p), y+(bs-p), x+p, y+(bs-p), bg);
gui_trianglef(list, x+p, by + p, x + bs - p, by+p, x + (bs/2) , by + bs-p, bg);
gui_rectf(list, cx, cy, cw, ch, fg);
gui_rect(list, cx, cy, cw, ch, bg);
return offset;
}

121
gui.h Normal file
View File

@ -0,0 +1,121 @@
/*
Copyright (c) 2015
vurtun <polygone@gmx.net>
MIT licence
*/
#ifndef TINY_GUI_H_
#define TINY_GUI_H_
#define GUI_UTF_SIZE 4
#define GUI_INPUT_MAX 16
#define GUI_GLYPHES_MAX 512
typedef int gui_int;
typedef int gui_bool;
typedef unsigned char gui_char;
typedef float gui_float;
typedef void* gui_texture;
typedef unsigned char gui_byte;
typedef unsigned long gui_flag;
typedef unsigned long gui_size;
typedef gui_char gui_glyph[GUI_UTF_SIZE];
enum {gui_false, gui_true};
struct gui_color {gui_byte r,g,b,a;};
struct gui_texCoord {gui_float u,v;};
struct gui_vec2 {gui_float x,y;};
struct gui_rect {gui_float x,y,w,h;};
struct gui_vertex {
struct gui_vec2 pos;
struct gui_texCoord uv;
struct gui_color color;
};
struct gui_draw_command {
struct gui_vertex *vertexes;
gui_int vertex_count;
gui_int vertex_write;
struct gui_rect clip_rect;
gui_texture texture;
};
struct gui_draw_list {
struct gui_draw_command *begin;
struct gui_draw_command *end;
gui_byte *memory;
gui_size size;
gui_size needed;
};
enum gui_keys {
GUI_KEY_SHIFT,
GUI_KEY_CTRL,
GUI_KEY_DEL,
GUI_KEY_ENTER,
GUI_KEY_BACKSPACE,
GUI_KEY_ESCAPE,
GUI_KEY_SPACE,
GUI_KEY_MAX
};
struct gui_input {
gui_int keys[GUI_KEY_MAX];
gui_glyph text[GUI_INPUT_MAX];
gui_size glyph_count;
gui_bool shift;
gui_bool ctrl;
struct gui_vec2 mouse_pos;
struct gui_vec2 mouse_prev;
struct gui_vec2 mouse_delta;
gui_bool mouse_down;
gui_int mouse_clicked;
struct gui_vec2 mouse_clicked_pos;
};
struct gui_font_glyph {
gui_glyph glpyh;
};
struct gui_font {
struct gui_font_glyph glyphes[GUI_GLYPHES_MAX];
};
void gui_begin(struct gui_draw_list *list, gui_byte *memory, gui_size size);
gui_size gui_end(struct gui_draw_list *list);
const struct gui_draw_command *gui_next(const struct gui_draw_list *list,
const struct gui_draw_command*);
void gui_input_begin(struct gui_input *in);
void gui_input_motion(struct gui_input *in, gui_int x, gui_int y);
void gui_input_key(struct gui_input *in, enum gui_keys key, gui_int down);
void gui_input_button(struct gui_input *in, gui_int x, gui_int y, gui_int down);
void gui_input_char(struct gui_input *in, gui_glyph glyph);
void gui_input_end(struct gui_input *in);
gui_int gui_button(struct gui_draw_list *list, const struct gui_input *in,
struct gui_color bg, struct gui_color fg,
gui_int x, gui_int y, gui_int w, gui_int h, gui_int pad,
const char *str, gui_int len);
gui_int gui_toggle(struct gui_draw_list *list, const struct gui_input *in,
struct gui_color bg, struct gui_color fg,
gui_int x, gui_int y, gui_int w, gui_int h, gui_int pad,
const char *str, gui_int len, gui_int active);
gui_int gui_slider(struct gui_draw_list *list, const struct gui_input *in,
struct gui_color bg, struct gui_color fg,
gui_int x, gui_int y, gui_int w, gui_int h, gui_int pad,
gui_float min, gui_float v, gui_float max, gui_float step);
gui_int gui_progress(struct gui_draw_list *list, const struct gui_input *in,
struct gui_color bg, struct gui_color fg,
gui_int x, gui_int y, gui_int w, gui_int h, gui_int pad,
gui_float cur, gui_float max, gui_bool modifyable);
gui_int gui_scroll(struct gui_draw_list *list, const struct gui_input *in,
struct gui_color bg, struct gui_color fg,
gui_int x, gui_int y, gui_int w, gui_int h,
gui_int offset, gui_int dst);
gui_int gui_input(struct gui_draw_list *list,
const struct gui_input *in, gui_char *buffer, gui_int *len,
struct gui_color bg, struct gui_color fg,
gui_int x, gui_int y, gui_int w, gui_int h, gui_int active);
#endif

338
x11.c Normal file
View File

@ -0,0 +1,338 @@
/*
Copyright (c) 2015
vurtun <polygone@gmx.net>
MIT licence
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <limits.h>
#include <errno.h>
#include <string.h>
#include <memory.h>
#include <assert.h>
#include <time.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include <GL/glu.h>
#include "gui.h"
/* macros */
#define internal static
#define global static
#define persistent static
#define WIN_WIDTH 800
#define WIN_HEIGHT 600
#define DTIME 33
#define MAX_VERTEX_BUFFER (16 * 1024)
#define CONSOLE_MAX_CMDLINE 1024
#define CONSOLE_MAX_TEXT (16 * 1024)
#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))
/* types */
struct XWindow {
Display *dpy;
Window root;
XVisualInfo *vi;
Colormap cmap;
XSetWindowAttributes swa;
XWindowAttributes gwa;
Window win;
GLXContext glc;
int running;
};
struct Console {
struct XWindow *win;
struct gui_draw_list gui;
struct gui_input input;
struct gui_font font;
/* text */
int output_len;
char output[CONSOLE_MAX_TEXT];
char line[CONSOLE_MAX_CMDLINE];
int line_len;
};
/* functions */
internal void die(const char*,...);
internal long timestamp(void);
internal void sleep_for(long ms);
internal void kpress(struct Console*, XEvent*);
internal void bpress(struct Console*, XEvent*);
internal void brelease(struct Console*, XEvent*);
internal void bmotion(struct Console*, XEvent*);
internal void resize(struct Console*, XEvent*);
internal void
die(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
exit(EXIT_FAILURE);
}
internal void*
xcalloc(size_t nmemb, size_t size)
{
void *p = calloc(nmemb, size);
if (!p)
die("out of memory\n");
return p;
}
internal long
timestamp(void)
{
struct timeval tv;
if (gettimeofday(&tv, NULL) < 0) return 0;
return (long)((long)tv.tv_sec * 1000 + (long)tv.tv_usec/1000);
}
internal void
sleep_for(long t)
{
const time_t sec = (int)(t/1000);
const long ms = t - (sec * 1000);
struct timespec req;
req.tv_sec = sec;
req.tv_nsec = ms * 1000000L;
while(-1 == nanosleep(&req, &req));
}
static void
kpress(struct Console *con, XEvent* e)
{
int ret;
struct XWindow *xw = con->win;
KeySym *keysym = XGetKeyboardMapping(xw->dpy, e->xkey.keycode, 1, &ret);
if (*keysym == XK_Escape) xw->running = 0;
else if (*keysym == XK_Shift_L || *keysym == XK_Shift_R) gui_input_key(&con->input, GUI_KEY_SHIFT, gui_true);
else if (*keysym == XK_Control_L || *keysym == XK_Control_L) gui_input_key(&con->input, GUI_KEY_CTRL, gui_true);
else if (*keysym == XK_Delete) gui_input_key(&con->input, GUI_KEY_DEL, gui_true);
else if (*keysym == XK_Return) gui_input_key(&con->input, GUI_KEY_ENTER, gui_true);
else if (*keysym == XK_BackSpace) gui_input_key(&con->input, GUI_KEY_BACKSPACE, gui_true);
}
static void
krelease(struct Console *con, XEvent* e)
{
int ret;
struct XWindow *xw = con->win;
KeySym *keysym = XGetKeyboardMapping(xw->dpy, e->xkey.keycode, 1, &ret);
if (*keysym == XK_Shift_L || *keysym == XK_Shift_R) gui_input_key(&con->input, GUI_KEY_SHIFT, gui_false);
else if (*keysym == XK_Control_L || *keysym == XK_Control_L) gui_input_key(&con->input, GUI_KEY_CTRL, gui_false);
else if (*keysym == XK_Delete) gui_input_key(&con->input, GUI_KEY_DEL, gui_false);
else if (*keysym == XK_Return) gui_input_key(&con->input, GUI_KEY_ENTER, gui_false);
else if (*keysym == XK_BackSpace) gui_input_key(&con->input, GUI_KEY_BACKSPACE, gui_false);
}
static void
bpress(struct Console *con, XEvent *evt)
{
const float x = evt->xbutton.x;
const float y = evt->xbutton.y;
if (evt->xbutton.button == Button1)
gui_input_button(&con->input, x, y, gui_true);
}
static void
brelease(struct Console *con, XEvent *evt)
{
const float x = evt->xbutton.x;
const float y = evt->xbutton.y;
struct XWindow *xw = con->win;
if (evt->xbutton.button == Button1)
gui_input_button(&con->input, x, y, gui_false);
}
static void
bmotion(struct Console *con, XEvent *evt)
{
const float x = evt->xbutton.x;
const float y = evt->xbutton.y;
struct XWindow *xw = con->win;
gui_input_motion(&con->input, x, y);
}
static void
resize(struct Console *con, XEvent* evt)
{
struct XWindow *xw = con->win;
XGetWindowAttributes(xw->dpy, xw->win, &xw->gwa);
glViewport(0, 0, xw->gwa.width, xw->gwa.height);
}
static void
draw(int width, int height, const struct gui_draw_list *list)
{
const struct gui_draw_command *cmd;
persistent const size_t v = sizeof(struct gui_vertex);
persistent const size_t p = offsetof(struct gui_vertex, pos);
persistent const size_t t = offsetof(struct gui_vertex, uv);
persistent const size_t c = offsetof(struct gui_vertex, color);
if (!list) 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);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
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();
cmd = list->begin;
while (cmd) {
const int x = (int)cmd->clip_rect.x;
const int y = height - (int)(cmd->clip_rect.y + cmd->clip_rect.h);
const int w = (int)cmd->clip_rect.w;
const int h = (int)cmd->clip_rect.h;
gui_byte *buffer = (gui_byte*)cmd->vertexes;
glVertexPointer(2, GL_FLOAT, v, (void*)(buffer + p));
glTexCoordPointer(2, GL_FLOAT, v, (void*)(buffer + t));
glColorPointer(4, GL_UNSIGNED_BYTE, v, (void*)(buffer + c));
glBindTexture(GL_TEXTURE_2D, (unsigned long)cmd->texture);
glScissor(x, y, w, h);
glDrawArrays(GL_TRIANGLES, 0, cmd->vertex_count);
cmd = gui_next(list, cmd);
}
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glPopAttrib();
}
int
main(int argc, char *argv[])
{
struct XWindow xw;
struct Console con;
long dt, started;
gui_byte *buffer;
gui_size buffer_size = MAX_VERTEX_BUFFER;
const struct gui_color colorA = {100, 100, 100, 255};
const struct gui_color colorB = {45, 45, 45, 255};
const struct gui_color colorC = {0, 0, 0, 255};
static GLint att[] = {GLX_RGBA, GLX_DEPTH_SIZE,24, GLX_DOUBLEBUFFER, None};
gui_float slider = 5.0f;
gui_float prog = 60.0f;
gui_float offset = 300;
/* x11 */
UNUSED(argc); UNUSED(argv);
memset(&xw, 0, sizeof xw);
memset(&con, 0, sizeof con);
xw.dpy = XOpenDisplay(NULL);
if (!xw.dpy)
die("XOpenDisplay failed\n");
xw.root = DefaultRootWindow(xw.dpy);
xw.vi = glXChooseVisual(xw.dpy, 0, att);
if (!xw.vi)
die("Failed to find appropriate visual\n");
xw.cmap = XCreateColormap(xw.dpy,xw.root,xw.vi->visual,AllocNone);
xw.swa.colormap = xw.cmap;
xw.swa.event_mask =
ExposureMask | KeyPressMask | ButtonPress |
ButtonReleaseMask | ButtonMotionMask |
Button1MotionMask | Button2MotionMask | Button3MotionMask;
xw.win = XCreateWindow(
xw.dpy, xw.root, 0, 0,WIN_WIDTH,WIN_HEIGHT, 0,
xw.vi->depth, InputOutput, xw.vi->visual,
CWColormap | CWEventMask, &xw.swa);
XGetWindowAttributes(xw.dpy, xw.win, &xw.gwa);
XStoreName(xw.dpy, xw.win, "Demo");
XMapWindow(xw.dpy, xw.win);
XFlush(xw.dpy);
XSync(xw.dpy, False);
con.win = &xw;
/* OpenGL */
xw.glc = glXCreateContext(xw.dpy, xw.vi, NULL, GL_TRUE);
glXMakeCurrent(xw.dpy, xw.win, xw.glc);
buffer = xcalloc(MAX_VERTEX_BUFFER, 1);
xw.running = 1;
while (xw.running) {
/* Input */
XEvent ev;
started = timestamp();
gui_input_begin(&con.input);
while (XCheckWindowEvent(xw.dpy, xw.win, xw.swa.event_mask, &ev)) {
if (ev.type == Expose || ev.type == ConfigureNotify) resize(&con, &ev);
else if (ev.type == MotionNotify) bmotion(&con, &ev);
else if (ev.type == ButtonPress) bpress(&con, &ev);
else if (ev.type == ButtonRelease) brelease(&con, &ev);
else if (ev.type == KeyPress) kpress(&con, &ev);
else if (ev.type == KeyRelease) kpress(&con, &ev);
}
gui_input_end(&con.input);
/* ------------------------- GUI --------------------------*/
gui_begin(&con.gui, buffer, MAX_VERTEX_BUFFER);
if (gui_button(&con.gui, &con.input, colorA, colorC, 50,50,150,30,5,"",0))
fprintf(stdout, "Button pressed!\n");
slider = gui_slider(&con.gui, &con.input, colorA, colorB, 50,100,150,30,2, 0.0f, slider, 10.0f, 1.0f);
prog = gui_progress(&con.gui, &con.input, colorA, colorB, 50,150,150,30,2, prog, 100.0f, gui_true);
offset = gui_scroll(&con.gui, &con.input, colorA, colorB, 250,50,16,332, offset, 600);
gui_end(&con.gui);
/* ---------------------------------------------------------*/
/* Draw */
glClearColor(45.0f/255.0f,45.0f/255.0f,45.0f/255.0f,1);
glClear(GL_COLOR_BUFFER_BIT);
draw(xw.gwa.width, xw.gwa.height, &con.gui);
glXSwapBuffers(xw.dpy, xw.win);
/* Timing */
dt = timestamp() - started;
if (dt < DTIME) {
sleep_for(DTIME - dt);
dt = DTIME;
}
}
/* Cleanup */
glXMakeCurrent(xw.dpy, None, NULL);
glXDestroyContext(xw.dpy, xw.glc);
XDestroyWindow(xw.dpy, xw.win);
XCloseDisplay(xw.dpy);
return 0;
}