--- /dev/null
+*.o
+*.d
+*.swp
+test
--- /dev/null
+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)
--- /dev/null
+#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_ */
--- /dev/null
+#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;
+}
--- /dev/null
+#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_ */
--- /dev/null
+#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;
+}
--- /dev/null
+#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_ */
--- /dev/null
+#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;
+}
--- /dev/null
+#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();
+}