--- /dev/null
+*.o
+*.d
+*.swp
+example
--- /dev/null
+src = $(wildcard src/*.c) example.c
+obj = $(src:.c=.o)
+dep = $(src:.c=.d)
+bin = example
+
+CFLAGS = -pedantic -Wall -g -Isrc -MMD
+LDFLAGS = -lGL -lglut
+
+$(bin): $(obj)
+ $(CC) -o $@ $(obj) $(LDFLAGS)
+
+-include $(dep)
+
+.PHONY: clean
+clean:
+ rm -f $(obj)
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <GL/glut.h>
+#include "windtk.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 int gfx_newcol(int r, int g, int b);
+static void gfx_color(int c);
+static void gfx_fillrect(struct wt_rect *r);
+static void gfx_line(int x0, int y0, int x1, int y1);
+
+static struct wt_graphics gfx = {
+ gfx_newcol, 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);
+
+ if(wt_init(800, 600, &gfx)) {
+ return 1;
+ }
+
+ glutMainLoop();
+ return 0;
+}
+
+static void display(void)
+{
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glutSwapBuffers();
+}
+
+static void reshape(int x, int y)
+{
+ glViewport(0, 0, x, y);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, x, y, 0, -1, 1);
+
+ wt_viewport(0, 0, x, y);
+}
+
+static void keypress(unsigned char key, int x, int y)
+{
+ if(key == 27) exit(0);
+
+ wt_inp_key(key, 1);
+}
+
+static void keyrelease(unsigned char key, int x, int y)
+{
+ wt_inp_key(key, 0);
+}
+
+static void mouse(int bn, int st, int x, int y)
+{
+ wt_inp_mouse(bn - GLUT_LEFT_BUTTON, st == GLUT_DOWN, x, y);
+}
+
+static void motion(int x, int y)
+{
+ wt_inp_motion(x, y);
+}
+
+/* ------ graphics callbacks -------- */
+static float colors[32][3];
+static int maxcol;
+
+static int gfx_newcol(int r, int g, int b)
+{
+ if(maxcol >= 32) return -1;
+ colors[maxcol][0] = r / 255.0f;
+ colors[maxcol][1] = g / 255.0f;
+ colors[maxcol][2] = b / 255.0f;
+ return maxcol++;
+}
+
+static void gfx_color(int c)
+{
+ glColor3fv(colors[c]);
+}
+
+static void gfx_fillrect(struct wt_rect *r)
+{
+ glBegin(GL_QUADS);
+ glVertex2f(r->x, r->y);
+ glVertex2f(r->x + r->width, r->y);
+ glVertex2f(r->x + r->width, r->y + r->height);
+ glVertex2f(r->x, r->y + r->height);
+ glEnd();
+}
+
+static void gfx_line(int x0, int y0, int x1, int y1)
+{
+ glBegin(GL_LINES);
+ glVertex2f(x0, y0);
+ glVertex2f(x1, y1);
+ glEnd();
+}
--- /dev/null
+#include <stdlib.h>
+#include <string.h>
+#include "wtimpl.h"
+
+wt_widget *wt_alloc_widget(wt_widget *par)
+{
+ wt_widget *w;
+ if(!(w = wt_zalloc(sizeof *w))) {
+ return 0;
+ }
+ w->type = WT_TYPE_WIDGET;
+
+ if(par) {
+ wt_add_child(par, w);
+ }
+ return w;
+}
+
+void wt_free_widget(wt_widget *w)
+{
+ if(w) {
+ wt_free(w->text);
+ wt_free(w->child);
+ wt_free(w);
+ }
+}
+
+void wt_free_tree(wt_widget *tree)
+{
+ int i;
+
+ if(!tree) return;
+
+ for(i=0; i<tree->num_child; i++) {
+ wt_free_tree(tree->child[i]);
+ }
+ wt_free_widget(tree);
+}
+
+int wt_type(wt_widget *w)
+{
+ return w->type;
+}
+
+int wt_set_text(wt_widget *w, const char *text)
+{
+ char *s;
+
+ if(!(s = wt_alloc(strlen(text) + 1))) {
+ return -1;
+ }
+ strcpy(s, text);
+ wt_free(w->text);
+ w->text = s;
+ return 0;
+}
+
+const char *wt_text(wt_widget *w)
+{
+ return w->text;
+}
+
+static int find_child(wt_widget *w, wt_widget *c)
+{
+ int i;
+ for(i=0; i<w->num_child; i++) {
+ if(w->child[i] == c) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int wt_add_child(wt_widget *w, wt_widget *c)
+{
+ if(find_child(w, c) != -1) {
+ return 0;
+ }
+ if(c->parent) {
+ wt_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 = wt_alloc(newsz * sizeof *w->child))) {
+ return -1;
+ }
+ memcpy(newarr, w->child, w->num_child * sizeof *w->child);
+ wt_free(w->child);
+ w->child = newarr;
+ w->max_child = newsz;
+ }
+
+ w->child[w->num_child++] = c;
+ return 0;
+}
+
+int wt_remove_child(wt_widget *w, wt_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 = wt_alloc(newsz * sizeof *w->child))) {
+ return -1;
+ }
+ memcpy(newarr, w->child, w->num_child * sizeof *w->child);
+ wt_free(w->child);
+ w->child = newarr;
+ w->max_child = newsz;
+ }
+ return 0;
+}
+
+wt_widget *wt_parent(wt_widget *w)
+{
+ return w->parent;
+}
+
+wt_widget *wt_widget_window(wt_widget *w)
+{
+ wt_widget *par = w;
+ while(par && par->type != WT_TYPE_WINDOW) {
+ par = par->parent;
+ }
+ return par;
+}
+
+int wt_child_count(wt_widget *w)
+{
+ return w->num_child;
+}
+
+wt_widget *wt_child(wt_widget *w, int idx)
+{
+ if(idx < 0 || idx >= w->num_child) {
+ return 0;
+ }
+ return w->child[idx];
+}
+
+void wt_move(wt_widget *w, int x, int y)
+{
+ w->rect.x = x;
+ w->rect.y = y;
+ /* TODO: invalidate something */
+}
+
+void wt_resize(wt_widget *w, int x, int y)
+{
+ w->rect.width = x;
+ w->rect.height = y;
+ /* TODO: invalidate something */
+}
+
+int *wt_position(wt_widget *w, int *xret, int *yret)
+{
+ if(xret) *xret = w->rect.x;
+ if(yret) *yret = w->rect.y;
+ return &w->rect.x;
+}
+
+int *wt_size(wt_widget *w, int *xret, int *yret)
+{
+ if(xret) *xret = w->rect.width;
+ if(yret) *yret = w->rect.height;
+ return &w->rect.width;
+}
+
+int wt_hittest(wt_widget *w, int x, int y)
+{
+ return x >= w->rect.x && y >= w->rect.y && x < w->rect.x + w->rect.width &&
+ y < w->rect.y + w->rect.height;
+}
+
+wt_widget *wt_widget_at(int x, int y)
+{
+ int i;
+ wt_widget *w, *c;
+
+ if(!wt_hittest(wt->root, x, y)) {
+ return 0;
+ }
+
+ w = 0;
+ c = wt->root;
+ do {
+ w = c;
+ c = 0;
+ for(i=0; i<w->num_child; i++) {
+ if(wt_hittest(w->child[i], x, y)) {
+ c = w->child[i];
+ break;
+ }
+ }
+ } while(c);
+
+ return w;
+}
+
+/*
+void wt_layout(wt_widget *w, int layout);
+void wt_padding(wt_widget *w, int pad);
+void wt_relayout(wt_widget *w);
+
+void wt_focus(wt_widget *w);
+void wt_unfocus(wt_widget *w);
+int wt_isfocused(wt_widget *w);
+
+void wt_hover(wt_widget *w);
+void wt_unhover(wt_widget *w);
+int wt_ishover(wt_widget *w);
+
+void wt_enable(wt_widget *w);
+void wt_disable(wt_widget *w);
+int wt_isenabled(wt_widget *w);
+*/
+
+void wt_callback(wt_widget *w, int type, wt_callback_func func, void *cls)
+{
+ w->cb[type] = func;
+ w->cbcls[type] = cls;
+}
--- /dev/null
+#ifndef WIDGET_H_
+#define WIDGET_H_
+
+#include "windtk.h"
+
+struct wt_widget {
+ int type;
+ char *text;
+ struct wt_rect rect;
+
+ struct wt_widget *parent;
+ struct wt_widget **child; /* dynamic array */
+ int num_child, max_child;
+ int layout;
+
+ wt_callback_func cb[WT_NUM_CALLBACKS];
+ void *cbcls[WT_NUM_CALLBACKS];
+
+ void (*draw)(struct wt_widget *w);
+ void (*click)(struct wt_widget *w);
+ void (*keypress)(struct wt_widget *w, int key);
+ void (*mbutton)(struct wt_widget *w, int bn, int st, int x, int y);
+ void (*mmotion)(struct wt_widget *w, int x, int y);
+};
+
+#endif /* WIDGET_H_ */
--- /dev/null
+#include "wtimpl.h"
+
+static void draw_win(wt_widget *w);
+
+wt_widget *wt_window(wt_widget *par, const char *title, int style, int x, int y, int xsz, int ysz)
+{
+ wt_widget *w;
+
+ if(!(w = wt_alloc_widget(par))) {
+ return 0;
+ }
+ w->type = WT_TYPE_WINDOW;
+ wt_set_text(w, title);
+ wt_move(w, x, y);
+ wt_resize(w, xsz, ysz);
+ /* TODO: style */
+
+ w->draw = draw_win;
+ return w;
+}
+
+static void draw_win(wt_widget *w)
+{
+ wt_gfx_color(COL_BG);
+ wt_gfx_fillrect(&w->rect);
+}
--- /dev/null
+#include <stdlib.h>
+#include <string.h>
+#include "wtimpl.h"
+
+void *(*wt_alloc)(size_t sz) = malloc;
+void (*wt_free)(void *p) = free;
+
+/* one day I may want to allow multiple contexts */
+static struct wt_context defctx;
+struct wt_context *wt_curctx_ = &defctx;
+
+static unsigned int def_colors[] = {
+ 0xf0f0f0, /* font/foreground */
+ 0x333333, /* background */
+ 0x444444, /* highlight */
+ 0x222222, /* 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 *wt_zalloc(size_t sz)
+{
+ void *p = wt_alloc(sz);
+ if(p) {
+ memset(p, 0, sz);
+ }
+ return p;
+}
+
+void wt_allocator(void *(*allocfunc)(size_t), void (*freefunc)(void *p))
+{
+ wt_alloc = allocfunc ? allocfunc : malloc;
+ wt_free = freefunc ? freefunc : free;
+}
+
+
+int wt_init(int w, int h, struct wt_graphics *gfx)
+{
+ if(!(wt->root = wt_alloc_widget(0))) {
+ return -1;
+ }
+
+ wt_viewport(0, 0, w, h);
+ wt_graphics(gfx);
+ return 0;
+}
+
+void wt_destroy(void)
+{
+ wt_free_tree(wt->root);
+ wt->root = 0;
+}
+
+void wt_viewport(int x, int y, int w, int h)
+{
+ wt_setrect(&wt->vp, x, y, w, h);
+ wt_move(wt->root, x, y);
+ wt_resize(wt->root, w, h);
+}
+
+void wt_graphics(struct wt_graphics *gfx)
+{
+ int i, r, g, b;
+ wt->gfx = *gfx;
+
+ for(i=0; i<NUM_COLORS; i++) {
+ r = def_colors[i] >> 16;
+ g = (def_colors[i] >> 8) & 0xff;
+ b = def_colors[i] & 0xff;
+ if((wt->colors[i] = gfx->newcolor(r, g, b)) == -1) {
+ wt->colors[i] = i ? wt->colors[i - 1] : 0;
+ }
+ }
+}
+
+void wt_inp_key(int key, int press)
+{
+}
+
+void wt_inp_mouse(int bn, int st, int x, int y)
+{
+}
+
+void wt_inp_motion(int x, int y)
+{
+}
+
+void wt_gfx_color(int cidx)
+{
+ wt->gfx.color(wt->colors[cidx]);
+}
+
+void wt_gfx_fillrect(struct wt_rect *r)
+{
+ wt->gfx.fillrect(r);
+}
+
+void wt_gfx_frame(struct wt_rect *r, int style, int basecol)
+{
+ wt_gfx_color(basecol);
+ wt_gfx_fillrect(r);
+ wt_gfx_color(style == FRM_OUT ? basecol + 2 : basecol + 1);
+ wt_gfx_line(r->x, r->y + r->height, r->x + r->width - 1, r->y + r->height);
+ wt_gfx_line(r->x + r->width, r->y + 1, r->x + r->width, r->y + r->height);
+ wt_gfx_color(style == FRM_OUT ? basecol + 1 : basecol + 2);
+ wt_gfx_line(r->x, r->y, r->x + r->width, r->y);
+ wt_gfx_line(r->x, r->y + 1, r->x, r->y + r->height - 1);
+}
+
+void wt_gfx_line(int x0, int y0, int x1, int y1)
+{
+ wt->gfx.line(x0, y0, x1, y1);
+}
+
+void wt_setrect(struct wt_rect *r, int x, int y, int w, int h)
+{
+ r->x = x;
+ r->y = y;
+ r->width = w;
+ r->height = h;
+}
--- /dev/null
+#ifndef WINDTK_H_
+#define WINDTK_H_
+
+enum {
+ WT_TYPE_WIDGET,
+ WT_TYPE_WINDOW,
+ WT_TYPE_LABEL,
+ WT_TYPE_BUTTON,
+ WT_TYPE_CHECKBOX,
+ WT_TYPE_TEXTFIELD
+};
+
+enum {
+ WT_KEY_UP = 0x100,
+ WT_KEY_DOWN,
+ WT_KEY_LEFT,
+ WT_KEY_RIGHT,
+ WT_KEY_HOME,
+ WT_KEY_END,
+ WT_KEY_PGUP,
+ WT_KEY_PGDN
+};
+
+enum {
+ WT_WS_DEFAULT,
+ WT_WS_NOFRM
+};
+
+enum {
+ WT_CB_FOCUS,
+ WT_CB_UNFOCUS,
+ WT_CB_MODIFY,
+ WT_CB_CLICK,
+ WT_CB_MBUTTON,
+ WT_CB_MMOTION,
+
+ WT_NUM_CALLBACKS
+};
+
+typedef struct wt_widget wt_widget;
+typedef void (*wt_callback_func)(wt_widget*, void*);
+
+struct wt_image {
+ int width, height;
+ int bpp;
+ int pitch;
+ void *pixels;
+ void *udata;
+};
+
+struct wt_rect {
+ int x, y, width, height;
+};
+
+struct wt_graphics {
+ int (*newcolor)(int r, int g, int b);
+ void (*color)(int c);
+
+ void (*fillrect)(struct wt_rect *rect);
+ void (*line)(int x0, int y0, int x1, int y1);
+
+ int (*newimage)(struct wt_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 wt_rect *boxret);
+ int (*lineheight)(int font);
+ int (*baseline)(int font);
+};
+
+void wt_allocator(void *(*allocfunc)(size_t), void (*freefunc)(void*));
+
+int wt_init(int w, int h, struct wt_graphics *gfx);
+void wt_destroy(void);
+
+void wt_viewport(int x, int y, int w, int h);
+void wt_graphics(struct wt_graphics *gfx);
+
+void wt_inp_key(int key, int press);
+void wt_inp_mouse(int bn, int st, int x, int y);
+void wt_inp_motion(int x, int y);
+
+wt_widget *wt_alloc_widget(wt_widget *par);
+void wt_free_widget(wt_widget *w);
+void wt_free_tree(wt_widget *tree);
+
+wt_widget *wt_window(wt_widget *par, const char *title, int style, int x, int y, int width, int height);
+wt_widget *wt_label(wt_widget *par, const char *text, int x, int y);
+wt_widget *wt_button(wt_widget *par, const char *text, int x, int y, int width, int height);
+wt_widget *wt_button_cb(wt_widget *par, const char *text, int x, int y, int width,
+ int height, wt_callback_func cbclick, void *cls);
+wt_widget *wt_checkbox(wt_widget *par, const char *text, int chk, int x, int y, int width, int height);
+wt_widget *wt_checkbox_cb(wt_widget *par, const char *text, int chk, int x, int y,
+ int width, int height, wt_callback_func cbtoggle, void *cls);
+wt_widget *wt_textfield(wt_widget *par, const char *text, int x, int y, int width, int height);
+
+int wt_type(wt_widget *w);
+
+int wt_set_text(wt_widget *w, const char *text);
+const char *wt_text(wt_widget *w);
+
+int wt_add_child(wt_widget *w, wt_widget *c);
+int wt_remove_child(wt_widget *w, wt_widget *c);
+wt_widget *wt_parent(wt_widget *w); /* parent widget */
+wt_widget *wt_widget_window(wt_widget *w); /* first ancestor of type window */
+int wt_child_count(wt_widget *w); /* number of children */
+wt_widget *wt_child(wt_widget *w, int idx); /* get child idx */
+
+void wt_move(wt_widget *w, int x, int y);
+void wt_resize(wt_widget *w, int x, int y);
+int *wt_position(wt_widget *w, int *xret, int *yret);
+int *wt_size(wt_widget *w, int *xret, int *yret);
+
+void wt_layout(wt_widget *w, int layout);
+void wt_padding(wt_widget *w, int pad);
+/* calculates layout of child widgets and updates dimensions */
+void wt_relayout(wt_widget *w);
+
+int wt_hittest(wt_widget *w, int x, int y);
+wt_widget *wt_widget_at(int x, int y);
+
+void wt_focus(wt_widget *w);
+void wt_unfocus(wt_widget *w);
+int wt_isfocused(wt_widget *w);
+
+void wt_hover(wt_widget *w);
+void wt_unhover(wt_widget *w);
+int wt_ishover(wt_widget *w);
+
+void wt_enable(wt_widget *w);
+void wt_disable(wt_widget *w);
+int wt_isenabled(wt_widget *w);
+
+void wt_callback(wt_widget *w, int type, wt_callback_func func, void *cls);
+
+void wt_setrect(struct wt_rect *r, int x, int y, int w, int h);
+
+#endif /* WINDTK_H_ */
--- /dev/null
+#ifndef WTIMPL_H_
+#define WTIMPL_H_
+
+#include <stdlib.h>
+#include "windtk.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 };
+
+struct wt_context {
+ struct wt_graphics gfx;
+ struct wt_rect vp;
+ wt_widget *root;
+
+ int colors[NUM_COLORS];
+};
+
+extern struct wt_context *wt_curctx_;
+#define wt wt_curctx_
+
+extern void *(*wt_alloc)(size_t sz);
+extern void (*wt_free)(void *p);
+
+void *wt_zalloc(size_t sz);
+
+void wt_gfx_color(int cidx);
+void wt_gfx_fillrect(struct wt_rect *r);
+void wt_gfx_frame(struct wt_rect *r, int style, int basecol);
+void wt_gfx_line(int x0, int y0, int x1, int y1);
+
+#endif /* WTIMPL_H_ */