start by copying windtk and dropping dirty rects and indexed colors master
authorJohn Tsiombikas <nuclear@member.fsf.org>
Tue, 15 Feb 2022 18:05:51 +0000 (20:05 +0200)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Tue, 15 Feb 2022 18:05:51 +0000 (20:05 +0200)
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
src/agimpl.h [new file with mode: 0644]
src/anigui.c [new file with mode: 0644]
src/anigui.h [new file with mode: 0644]
src/widget.c [new file with mode: 0644]
src/widget.h [new file with mode: 0644]
src/window.c [new file with mode: 0644]
test.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..0932ecf
--- /dev/null
@@ -0,0 +1,4 @@
+*.o
+*.d
+*.swp
+test
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..2423d12
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,20 @@
+src = test.c $(wildcard src/*.c)
+obj = $(src:.c=.o)
+dep = $(src:.c=.d)
+bin = test
+
+CFLAGS = -pedantic -Wall -Isrc -MMD
+LDFLAGS = -lGL -lGLU -lglut
+
+$(bin): $(obj)
+       $(CC) -o $@ $(obj) $(LDFLAGS)
+
+-include $(dep)
+
+.PHONY: clean
+clean:
+       rm -f $(obj) $(bin)
+
+.PHONY: cleandep
+cleandep:
+       rm -f $(dep)
diff --git a/src/agimpl.h b/src/agimpl.h
new file mode 100644 (file)
index 0000000..2e5ab0f
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef AGIMPL_H_
+#define AGIMPL_H_
+
+#include <stdlib.h>
+#include "anigui.h"
+#include "widget.h"
+
+enum {
+       COL_FG,
+       COL_BG,
+       COL_BGHI,
+       COL_BGLO,
+       COL_FRM,
+       COL_FRMHI,
+       COL_FRMLO,
+       COL_FFRM,
+       COL_FFRMHI,
+       COL_FFRMLO,
+
+       NUM_COLORS
+};
+
+enum { FRM_OUT, FRM_IN, FRM_NOFILL = 0x8000 };
+#define FRMSTYLE(x)    ((x) & 0xff)
+
+#define MAX_UPD_RECTS  16
+
+struct ag_context {
+       struct ag_graphics gfx;
+       struct ag_rect vp;
+       ag_widget *root;
+       struct ag_theme *theme;
+
+       unsigned int colors[NUM_COLORS];
+
+       ag_widget *focuswin;
+};
+
+extern struct ag_context *ag_curctx_;
+#define ag     ag_curctx_
+
+extern void *(*ag_alloc)(size_t sz);
+extern void (*ag_free)(void *p);
+
+void *ag_zalloc(size_t sz);
+
+void ag_gfx_color(int cidx);
+void ag_gfx_fillrect(struct ag_rect *r);
+void ag_gfx_frame(struct ag_rect *r, int style, int basecol);
+void ag_gfx_line(int x0, int y0, int x1, int y1);
+
+void ag_calc_window_rect(struct ag_rect *r, ag_widget *w);
+
+#endif /* AGIMPL_H_ */
diff --git a/src/anigui.c b/src/anigui.c
new file mode 100644 (file)
index 0000000..1c4ab26
--- /dev/null
@@ -0,0 +1,201 @@
+#include <stdlib.h>
+#include <string.h>
+#include "agimpl.h"
+
+void *(*ag_alloc)(size_t sz) = malloc;
+void (*ag_free)(void *p) = free;
+
+/* one day I may want to allow multiple contexts */
+static struct ag_context defctx;
+struct ag_context *ag_curctx_ = &defctx;
+
+static unsigned int def_colors[] = {
+       0xf0f0f0,       /* font/foreground */
+       0x444444,       /* background */
+       0x555555,       /* highlight */
+       0x333333,       /* shadow */
+       0x403065,       /* inactive frame */
+       0x54349c,       /* inactive frame highlight */
+       0x221e2c,       /* inactive frame shadow */
+       0x8032aa,       /* active frame */
+       0xb14de8,       /* active frame highlight */
+       0x3b2846,       /* active frame shadow */
+       0
+};
+
+void *ag_zalloc(size_t sz)
+{
+       void *p = ag_alloc(sz);
+       if(p) {
+               memset(p, 0, sz);
+       }
+       return p;
+}
+
+void ag_allocator(void *(*allocfunc)(size_t), void (*freefunc)(void *p))
+{
+       ag_alloc = allocfunc ? allocfunc : malloc;
+       ag_free = freefunc ? freefunc : free;
+}
+
+
+int ag_init(int w, int h, struct ag_graphics *gfx)
+{
+       ag->root = 0;
+       if(!(ag->root = ag_alloc_widget(0))) {
+               return -1;
+       }
+
+       ag_viewport(0, 0, w, h);
+       ag_graphics(gfx);
+       return 0;
+}
+
+void ag_destroy(void)
+{
+       ag_free_tree(ag->root);
+       ag->root = 0;
+}
+
+struct ag_theme *ag_load_theme(const char *path)
+{
+       return 0;       /* TODO */
+}
+
+void ag_unload_theme(struct ag_theme *theme)
+{
+}
+
+static void use_theme(ag_widget *w, struct ag_theme *theme)
+{
+       int i;
+
+       if(w->use_theme) {
+               w->use_theme(w, theme);
+       }
+
+       for(i=0; i<w->num_child; i++) {
+               use_theme(w->child[i], theme);
+       }
+}
+
+void ag_use_theme(struct ag_theme *theme)
+{
+       ag->theme = theme;
+       use_theme(ag->root, theme);
+}
+
+void ag_viewport(int x, int y, int w, int h)
+{
+       ag_rect(&ag->vp, x, y, w, h);
+       ag_move(ag->root, x, y);
+       ag_resize(ag->root, w, h);
+}
+
+void ag_graphics(struct ag_graphics *gfx)
+{
+       ag->gfx = *gfx;
+       memcpy(ag->colors, def_colors, sizeof ag->colors);
+}
+
+void ag_inp_key(int key, int press)
+{
+}
+
+void ag_inp_mouse(int bn, int st, int x, int y)
+{
+}
+
+void ag_inp_motion(int x, int y)
+{
+}
+
+void ag_draw_tree(ag_widget *tree)
+{
+       int i;
+
+       if(tree->draw) {
+               tree->draw(tree, &ag->gfx);
+       }
+
+       for(i=0; i<tree->num_child; i++) {
+               ag_draw_tree(tree->child[i]);
+       }
+}
+
+void ag_draw(void)
+{
+       ag_draw_tree(ag->root);
+}
+
+void ag_gfx_color(int cidx)
+{
+       int r = (ag->colors[cidx] >> 16) & 0xff;
+       int g = (ag->colors[cidx] >> 8) & 0xff;
+       int b = ag->colors[cidx] & 0xff;
+       ag->gfx.color(r, g, b);
+}
+
+void ag_gfx_fillrect(struct ag_rect *r)
+{
+       ag->gfx.fillrect(r);
+}
+
+void ag_gfx_fillrect4i(int x, int y, int w, int h)
+{
+       struct ag_rect r;
+       ag_rect(&r, x, y, w, h);
+       ag->gfx.fillrect(&r);
+}
+
+void ag_gfx_frame(struct ag_rect *r, int style, int basecol)
+{
+       if((style & FRM_NOFILL) == 0) {
+               ag_gfx_color(basecol);
+               ag_gfx_fillrect(r);
+       }
+       ag_gfx_color(FRMSTYLE(style) == FRM_OUT ? basecol + 2 : basecol + 1);
+       ag_gfx_fillrect4i(r->x + 1, r->y + r->h - 1, r->w - 2, 1);
+       ag_gfx_fillrect4i(r->x + r->w - 1, r->y, 1, r->h);
+       ag_gfx_color(FRMSTYLE(style) == FRM_OUT ? basecol + 1 : basecol + 2);
+       ag_gfx_fillrect4i(r->x + 1, r->y, r->w - 2, 1);
+       ag_gfx_fillrect4i(r->x, r->y, 1, r->h);
+}
+
+void ag_gfx_line(int x0, int y0, int x1, int y1)
+{
+       ag->gfx.line(x0, y0, x1, y1);
+}
+
+void ag_rect(struct ag_rect *r, int x, int y, int w, int h)
+{
+       r->x = x;
+       r->y = y;
+       r->w = w;
+       r->h = h;
+}
+
+void ag_rect_union(struct ag_rect *a, struct ag_rect *b)
+{
+       int x1, y1;
+
+       x1 = a->x + a->w;
+       y1 = a->y + a->h;
+
+       if(b->x < a->x) a->x = b->x;
+       if(b->y < a->y) a->y = b->y;
+       if(b->x + b->w > x1) x1 = b->x + b->w;
+       if(b->y + b->h > y1) y1 = b->y + b->h;
+
+       a->w = x1 - a->x;
+       a->h = y1 - a->y;
+}
+
+int ag_rect_overlap(struct ag_rect *a, struct ag_rect *b)
+{
+       if(a->x > b->x + b->w) return 0;
+       if(b->x > a->x + a->w) return 0;
+       if(a->y > b->y + b->h) return 0;
+       if(b->y > a->x + a->h) return 0;
+       return 1;
+}
diff --git a/src/anigui.h b/src/anigui.h
new file mode 100644 (file)
index 0000000..e9537dc
--- /dev/null
@@ -0,0 +1,161 @@
+#ifndef ANIGUI_H_
+#define ANIGUI_H_
+
+enum {
+       AG_TYPE_WIDGET,
+       AG_TYPE_WINDOW,
+       AG_TYPE_LABEL,
+       AG_TYPE_BUTTON,
+       AG_TYPE_CHECKBOX,
+       AG_TYPE_TEXTFIELD
+};
+
+enum {
+       AG_KEY_UP = 0x100,
+       AG_KEY_DOWN,
+       AG_KEY_LEFT,
+       AG_KEY_RIGHT,
+       AG_KEY_HOME,
+       AG_KEY_END,
+       AG_KEY_PGUP,
+       AG_KEY_PGDN
+};
+
+enum {
+       AG_WS_DEFAULT,
+       AG_WS_NOFRM
+};
+
+enum {
+       AG_CB_FOCUS,
+       AG_CB_UNFOCUS,
+       AG_CB_MODIFY,
+       AG_CB_CLICK,
+       AG_CB_MBUTTON,
+       AG_CB_MMOTION,
+
+       AG_NUM_CALLBACKS
+};
+
+typedef struct ag_widget ag_widget;
+typedef void (*ag_callback_func)(ag_widget*, void*);
+
+struct ag_image {
+       int width, height;
+       int bpp;
+       int pitch;
+       void *pixels;
+       void *udata;
+};
+
+struct ag_rect {
+       int x, y, w, h;
+};
+
+struct ag_graphics {
+       unsigned int flags;
+
+       void (*color)(int r, int g, int b);
+
+       void (*fillrect)(struct ag_rect *rect);
+       void (*line)(int x0, int y0, int x1, int y1);
+
+       int (*newimage)(struct ag_image *img);
+       void (*blit)(int img, int x, int y, int w, int h);
+
+       void (*text)(int font, const char *s, int x, int y);
+       void (*textbox)(int font, const char *s, int x, int y, struct ag_rect *boxret);
+       int (*lineheight)(int font);
+       int (*baseline)(int font);
+};
+
+typedef void (*ag_draw_func)(ag_widget*, struct ag_graphics*);
+
+struct ag_theme {
+       char *name;
+       void *so;
+       ag_draw_func draw_window;
+       ag_draw_func draw_label;
+       ag_draw_func draw_button;
+       ag_draw_func draw_checkbox;
+       ag_draw_func draw_textfield;
+
+       struct ag_theme *next;
+};
+
+void ag_allocator(void *(*allocfunc)(size_t), void (*freefunc)(void*));
+
+int ag_init(int w, int h, struct ag_graphics *gfx);
+void ag_destroy(void);
+
+struct ag_theme *ag_load_theme(const char *path);      /* load dynamically, where applicable */
+void ag_unload_theme(struct ag_theme *theme);
+void ag_use_theme(struct ag_theme *theme);
+
+void ag_viewport(int x, int y, int w, int h);
+void ag_graphics(struct ag_graphics *gfx);
+
+void ag_inp_key(int key, int press);
+void ag_inp_mouse(int bn, int st, int x, int y);
+void ag_inp_motion(int x, int y);
+
+void ag_draw(void);
+
+ag_widget *ag_alloc_widget(ag_widget *par);
+void ag_free_widget(ag_widget *w);
+void ag_free_tree(ag_widget *tree);
+
+ag_widget *ag_window(ag_widget *par, const char *title, int style, int x, int y, int width, int height);
+ag_widget *ag_label(ag_widget *par, const char *text, int x, int y);
+ag_widget *ag_button(ag_widget *par, const char *text, int x, int y, int width, int height);
+ag_widget *ag_button_cb(ag_widget *par, const char *text, int x, int y, int width,
+               int height,     ag_callback_func cbclick, void *cls);
+ag_widget *ag_checkbox(ag_widget *par, const char *text, int chk, int x, int y, int width, int height);
+ag_widget *ag_checkbox_cb(ag_widget *par, const char *text, int chk, int x, int y,
+               int width, int height, ag_callback_func cbtoggle, void *cls);
+ag_widget *ag_textfield(ag_widget *par, const char *text, int x, int y, int width, int height);
+
+int ag_type(ag_widget *w);
+
+int ag_set_text(ag_widget *w, const char *text);
+const char *ag_text(ag_widget *w);
+
+int ag_add_child(ag_widget *w, ag_widget *c);
+int ag_remove_child(ag_widget *w, ag_widget *c);
+ag_widget *ag_parent(ag_widget *w);                    /* parent widget */
+ag_widget *ag_widget_window(ag_widget *w);     /* first ancestor of type window */
+int ag_child_count(ag_widget *w);                      /* number of children */
+ag_widget *ag_child(ag_widget *w, int idx);    /* get child idx */
+
+void ag_move(ag_widget *w, int x, int y);
+void ag_resize(ag_widget *w, int x, int y);
+int *ag_position(ag_widget *w, int *xret, int *yret);
+int *ag_size(ag_widget *w, int *xret, int *yret);
+
+int ag_hittest(ag_widget *w, int x, int y);
+ag_widget *ag_widget_at(int x, int y);
+
+void ag_layout(ag_widget *w, int layout);
+void ag_padding(ag_widget *w, int pad);
+/* calculates layout of child widgets and updates dimensions */
+void ag_relayout(ag_widget *w);
+
+void ag_focus(ag_widget *w);
+void ag_unfocus(ag_widget *w);
+int ag_isfocused(ag_widget *w);
+
+void ag_hover(ag_widget *w);
+void ag_unhover(ag_widget *w);
+int ag_ishover(ag_widget *w);
+
+void ag_enable(ag_widget *w);
+void ag_disable(ag_widget *w);
+int ag_isenabled(ag_widget *w);
+
+void ag_callback(ag_widget *w, int type, ag_callback_func func, void *cls);
+
+void ag_rect(struct ag_rect *r, int x, int y, int w, int h);
+void ag_rect_union(struct ag_rect *a, struct ag_rect *b);
+int ag_rect_overlap(struct ag_rect *a, struct ag_rect *b);
+
+#endif /* ANIGUI_H_ */
diff --git a/src/widget.c b/src/widget.c
new file mode 100644 (file)
index 0000000..363f048
--- /dev/null
@@ -0,0 +1,243 @@
+#include <stdlib.h>
+#include <string.h>
+#include "agimpl.h"
+
+ag_widget *ag_alloc_widget(ag_widget *par)
+{
+       ag_widget *w;
+       if(!(w = ag_zalloc(sizeof *w))) {
+               return 0;
+       }
+       w->type = AG_TYPE_WIDGET;
+       w->dirty = 1;
+
+       if(!par) par = ag->root;
+       ag_add_child(par, w);
+       return w;
+}
+
+void ag_free_widget(ag_widget *w)
+{
+       if(w) {
+               ag_free(w->text);
+               ag_free(w->child);
+               ag_free(w);
+       }
+}
+
+void ag_free_tree(ag_widget *tree)
+{
+       int i;
+
+       if(!tree) return;
+
+       for(i=0; i<tree->num_child; i++) {
+               ag_free_tree(tree->child[i]);
+       }
+       ag_free_widget(tree);
+}
+
+void ag_dirty_widget(ag_widget *w)
+{
+       int i;
+
+       w->dirty = 1;
+
+       for(i=0; i<w->num_child; i++) {
+               ag_dirty_widget(w->child[i]);
+       }
+}
+
+int ag_type(ag_widget *w)
+{
+       return w->type;
+}
+
+int ag_set_text(ag_widget *w, const char *text)
+{
+       char *s;
+
+       if(!(s = ag_alloc(strlen(text) + 1))) {
+               return -1;
+       }
+       strcpy(s, text);
+       ag_free(w->text);
+       w->text = s;
+       return 0;
+}
+
+const char *ag_text(ag_widget *w)
+{
+       return w->text;
+}
+
+static int find_child(ag_widget *w, ag_widget *c)
+{
+       int i;
+       for(i=0; i<w->num_child; i++) {
+               if(w->child[i] == c) {
+                       return i;
+               }
+       }
+       return -1;
+}
+
+int ag_add_child(ag_widget *w, ag_widget *c)
+{
+       if(!w || !c) return -1;
+
+       if(find_child(w, c) != -1) {
+               return 0;
+       }
+       if(c->parent) {
+               ag_remove_child(c->parent, c);
+       }
+
+       if(w->num_child >= w->max_child) {
+               void *newarr;
+               int newsz = w->max_child ? w->max_child << 1 : 8;
+               if(!(newarr = ag_alloc(newsz * sizeof *w->child))) {
+                       return -1;
+               }
+               memcpy(newarr, w->child, w->num_child * sizeof *w->child);
+               ag_free(w->child);
+               w->child = newarr;
+               w->max_child = newsz;
+       }
+
+       w->child[w->num_child++] = c;
+       return 0;
+}
+
+int ag_remove_child(ag_widget *w, ag_widget *c)
+{
+       int idx;
+
+       if(!w->num_child || (idx = find_child(w, c)) == -1) {
+               return -1;
+       }
+       w->child[idx] = w->child[--w->num_child];
+
+       if(w->max_child > 8 && w->num_child < w->max_child / 3) {
+               void *newarr;
+               int newsz = w->max_child >> 1;
+               if(!(newarr = ag_alloc(newsz * sizeof *w->child))) {
+                       return -1;
+               }
+               memcpy(newarr, w->child, w->num_child * sizeof *w->child);
+               ag_free(w->child);
+               w->child = newarr;
+               w->max_child = newsz;
+       }
+       return 0;
+}
+
+ag_widget *ag_parent(ag_widget *w)
+{
+       return w->parent;
+}
+
+ag_widget *ag_widget_window(ag_widget *w)
+{
+       ag_widget *par = w;
+       while(par && par->type != AG_TYPE_WINDOW) {
+               par = par->parent;
+       }
+       return par;
+}
+
+int ag_child_count(ag_widget *w)
+{
+       return w->num_child;
+}
+
+ag_widget *ag_child(ag_widget *w, int idx)
+{
+       if(idx < 0 || idx >= w->num_child) {
+               return 0;
+       }
+       return w->child[idx];
+}
+
+void ag_move(ag_widget *w, int x, int y)
+{
+       w->rect.x = x;
+       w->rect.y = y;
+       /* TODO: invalidate something */
+}
+
+void ag_resize(ag_widget *w, int x, int y)
+{
+       w->rect.w = x;
+       w->rect.h = y;
+       /* TODO: invalidate something */
+}
+
+int *ag_position(ag_widget *w, int *xret, int *yret)
+{
+       if(xret) *xret = w->rect.x;
+       if(yret) *yret = w->rect.y;
+       return &w->rect.x;
+}
+
+int *ag_size(ag_widget *w, int *xret, int *yret)
+{
+       if(xret) *xret = w->rect.w;
+       if(yret) *yret = w->rect.h;
+       return &w->rect.w;
+}
+
+int ag_hittest(ag_widget *w, int x, int y)
+{
+       return x >= w->rect.x && y >= w->rect.y && x < w->rect.x + w->rect.w &&
+               y < w->rect.y + w->rect.h;
+}
+
+ag_widget *ag_widget_at(int x, int y)
+{
+       int i;
+       ag_widget *w, *c;
+
+       if(!ag_hittest(ag->root, x, y)) {
+               return 0;
+       }
+
+       w = 0;
+       c = ag->root;
+       do {
+               w = c;
+               c = 0;
+               for(i=0; i<w->num_child; i++) {
+                       if(ag_hittest(w->child[i], x, y)) {
+                               c = w->child[i];
+                               break;
+                       }
+               }
+       } while(c);
+
+       return w;
+}
+
+/*
+void ag_layout(ag_widget *w, int layout);
+void ag_padding(ag_widget *w, int pad);
+void ag_relayout(ag_widget *w);
+
+void ag_focus(ag_widget *w);
+void ag_unfocus(ag_widget *w);
+int ag_isfocused(ag_widget *w);
+
+void ag_hover(ag_widget *w);
+void ag_unhover(ag_widget *w);
+int ag_ishover(ag_widget *w);
+
+void ag_enable(ag_widget *w);
+void ag_disable(ag_widget *w);
+int ag_isenabled(ag_widget *w);
+*/
+
+void ag_callback(ag_widget *w, int type, ag_callback_func func, void *cls)
+{
+       w->cb[type] = func;
+       w->cbcls[type] = cls;
+}
diff --git a/src/widget.h b/src/widget.h
new file mode 100644 (file)
index 0000000..60b384d
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef WIDGET_H_
+#define WIDGET_H_
+
+#include "anigui.h"
+
+struct ag_widget {
+       int type;
+       char *text;
+       struct ag_rect rect;
+
+       struct ag_widget *parent;
+       struct ag_widget **child;       /* dynamic array */
+       int num_child, max_child;
+       int layout;
+
+       ag_callback_func cb[AG_NUM_CALLBACKS];
+       void *cbcls[AG_NUM_CALLBACKS];
+
+       int dirty;      /* redraw pending */
+
+       ag_draw_func draw;
+       void (*click)(struct ag_widget *w);
+       void (*keypress)(struct ag_widget *w, int key);
+       void (*mbutton)(struct ag_widget *w, int bn, int st, int x, int y);
+       void (*mmotion)(struct ag_widget *w, int x, int y);
+
+       void (*use_theme)(struct ag_widget *w, struct ag_theme *theme);
+};
+
+#endif /* WIDGET_H_ */
diff --git a/src/window.c b/src/window.c
new file mode 100644 (file)
index 0000000..b715f99
--- /dev/null
@@ -0,0 +1,89 @@
+#include "agimpl.h"
+
+static void draw_win(ag_widget *w, struct ag_graphics *gfx);
+static void use_theme(ag_widget *w, struct ag_theme *theme);
+
+ag_widget *ag_window(ag_widget *par, const char *title, int style, int x, int y, int xsz, int ysz)
+{
+       ag_widget *w;
+
+       if(!(w = ag_alloc_widget(par))) {
+               return 0;
+       }
+       w->type = AG_TYPE_WINDOW;
+       ag_set_text(w, title);
+       ag_move(w, x, y);
+       ag_resize(w, xsz, ysz);
+       /* TODO: style */
+
+       use_theme(w, ag->theme);
+       return w;
+}
+
+#define FRMTHICK       1
+#define FRMBEVEL       1
+#define FRMOFFS                (FRMTHICK + FRMBEVEL * 2)
+#define TBARTHICK      12      /* TODO: base it on font metrics */
+#define TBAROFFS       (TBARTHICK + FRMBEVEL * 2)
+
+void ag_calc_window_rect(struct ag_rect *r, ag_widget *w)
+{
+       r->x = w->rect.x - FRMOFFS;
+       r->y = w->rect.y - TBAROFFS - FRMOFFS;
+       r->w = w->rect.w + FRMOFFS * 2;
+       r->h = w->rect.h + TBAROFFS + FRMOFFS * 2;
+}
+
+static void draw_win(ag_widget *w, struct ag_graphics *gfx)
+{
+       struct ag_rect fr, tmprect;
+       int frmcol = COL_FFRM;//ag->focuswin == w ? COL_FFRM : COL_FRM;
+
+       ag_gfx_color(COL_BG);
+       ag_gfx_fillrect(&w->rect);
+
+       ag_calc_window_rect(&fr, w);
+
+       ag_gfx_frame(&fr, FRM_OUT | FRM_NOFILL, frmcol);        /* outer bevel */
+
+       /* draw main frame */
+       ag_gfx_color(frmcol);
+
+       fr.x += FRMBEVEL;
+       fr.y += FRMBEVEL;
+       fr.w -= FRMBEVEL * 2;
+       fr.h -= FRMBEVEL * 2;
+
+       tmprect = fr;
+       tmprect.w = FRMTHICK;
+       ag_gfx_fillrect(&tmprect);      /* left bar */
+       tmprect.x += fr.w - FRMTHICK;
+       ag_gfx_fillrect(&tmprect);      /* right bar */
+
+       tmprect = fr;
+       tmprect.h = FRMTHICK;
+       tmprect.x += FRMTHICK;
+       tmprect.w -= FRMTHICK * 2;
+       ag_gfx_fillrect(&tmprect);      /* top bar */
+       tmprect.y += fr.h - FRMTHICK;
+       ag_gfx_fillrect(&tmprect);      /* bottom bar */
+
+       /* inner bevel */
+       fr.x += FRMTHICK;
+       fr.y += FRMTHICK;
+       fr.w -= FRMTHICK * 2;
+       fr.h -= FRMTHICK * 2;
+       ag_gfx_frame(&fr, FRM_IN | FRM_NOFILL, frmcol);
+
+       /* titlebar */
+       fr.x = w->rect.x;
+       fr.w = w->rect.w;
+       fr.y += FRMBEVEL;
+       fr.h = TBARTHICK + FRMBEVEL * 2;
+       ag_gfx_frame(&fr, FRM_OUT, frmcol);
+}
+
+static void use_theme(ag_widget *w, struct ag_theme *theme)
+{
+       w->draw = theme && theme->draw_window ? theme->draw_window : draw_win;
+}
diff --git a/test.c b/test.c
new file mode 100644 (file)
index 0000000..a575a53
--- /dev/null
+++ b/test.c
@@ -0,0 +1,113 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <GL/glut.h>
+#include "anigui.h"
+
+static void display(void);
+static void reshape(int x, int y);
+static void keypress(unsigned char key, int x, int y);
+static void keyrelease(unsigned char key, int x, int y);
+static void mouse(int bn, int st, int x, int y);
+static void motion(int x, int y);
+
+static void gfx_color(int r, int g, int b);
+static void gfx_fillrect(struct ag_rect *r);
+static void gfx_line(int x0, int y0, int x1, int y1);
+
+static struct ag_graphics gfx = {
+       0,
+       gfx_color,
+       gfx_fillrect,
+       gfx_line
+};
+
+int main(int argc, char **argv)
+{
+       glutInit(&argc, argv);
+       glutInitWindowSize(800, 600);
+       glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
+       glutCreateWindow("windtk example");
+
+       glutDisplayFunc(display);
+       glutReshapeFunc(reshape);
+       glutKeyboardFunc(keypress);
+       glutKeyboardUpFunc(keyrelease);
+       glutMouseFunc(mouse);
+       glutMotionFunc(motion);
+       glutPassiveMotionFunc(motion);
+
+       glClearColor(0.6, 0.6, 0.6, 1);
+
+       if(ag_init(800, 600, &gfx) == -1) {
+               return 1;
+       }
+       ag_window(0, "foo", AG_WS_DEFAULT, 100, 100, 200, 200);
+
+       glutMainLoop();
+       return 0;
+}
+
+static void display(void)
+{
+       glClear(GL_COLOR_BUFFER_BIT);
+
+       ag_draw();
+
+       glutSwapBuffers();
+}
+
+static void reshape(int x, int y)
+{
+       glViewport(0, 0, x, y);
+       glMatrixMode(GL_PROJECTION);
+       glLoadIdentity();
+       glOrtho(0, x, y, 0, -1, 1);
+
+       ag_viewport(0, 0, x, y);
+}
+
+static void keypress(unsigned char key, int x, int y)
+{
+       if(key == 27) exit(0);
+
+       ag_inp_key(key, 1);
+}
+
+static void keyrelease(unsigned char key, int x, int y)
+{
+       ag_inp_key(key, 0);
+}
+
+static void mouse(int bn, int st, int x, int y)
+{
+       ag_inp_mouse(bn - GLUT_LEFT_BUTTON, st == GLUT_DOWN, x, y);
+}
+
+static void motion(int x, int y)
+{
+       ag_inp_motion(x, y);
+}
+
+/* ------ graphics callbacks -------- */
+static void gfx_color(int r, int g, int b)
+{
+       glColor3ub(r, g, b);
+}
+
+static void gfx_fillrect(struct ag_rect *r)
+{
+       glBegin(GL_QUADS);
+       glVertex2f(r->x, r->y);
+       glVertex2f(r->x + r->w, r->y);
+       glVertex2f(r->x + r->w, r->y + r->h);
+       glVertex2f(r->x, r->y + r->h);
+       glEnd();
+}
+
+static void gfx_line(int x0, int y0, int x1, int y1)
+{
+       glBegin(GL_LINES);
+       glVertex2f(x0, y0);
+       glVertex2f(x1, y1);
+       glEnd();
+}