*.o
*.d
*.swp
-example
+example_gl
+example_sdl
-src = $(wildcard src/*.c) example.c
+src = $(wildcard src/*.c)
obj = $(src:.c=.o)
-dep = $(src:.c=.d)
-bin = example
+dep = $(src:.c=.d) example_gl.d example_sdl.d
CFLAGS = -pedantic -Wall -g -Isrc -MMD
-LDFLAGS = -lGL -lglut
-$(bin): $(obj)
- $(CC) -o $@ $(obj) $(LDFLAGS)
+.PHONY: all
+all: example_gl example_sdl
+
+example_gl: $(obj) example_gl.o
+ $(CC) -o $@ $^ $(LDFLAGS) -lGL -lglut
+
+example_sdl: $(obj) example_sdl.o
+ $(CC) -o $@ $^ $(LDFLAGS) -lSDL
-include $(dep)
.PHONY: clean
clean:
- rm -f $(obj)
+ rm -f $(obj) *.o example_gl example_sdl
+
+.PHONY: cleandep
+cleandep:
+ rm -f $(dep) *.d
+++ /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);
-
- glClearColor(0.6, 0.6, 0.6, 1);
-
- if(wt_init(800, 600, &gfx)) {
- return 1;
- }
- wt_window(0, "foo", WT_WS_DEFAULT, 100, 100, 200, 200);
-
- glutMainLoop();
- return 0;
-}
-
-static void display(void)
-{
- glClear(GL_COLOR_BUFFER_BIT);
-
- wt_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);
-
- 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 <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 = {
+ WT_GFX_NODIRTY,
+ 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);
+
+ glClearColor(0.6, 0.6, 0.6, 1);
+
+ if(wt_init(800, 600, &gfx) == -1) {
+ return 1;
+ }
+ wt_window(0, "foo", WT_WS_DEFAULT, 100, 100, 200, 200);
+
+ glutMainLoop();
+ return 0;
+}
+
+static void display(void)
+{
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ wt_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);
+
+ 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 unsigned char colors[32][3];
+static int maxcol;
+
+static int gfx_newcol(int r, int g, int b)
+{
+ int i;
+
+ for(i=0; i<maxcol; i++) {
+ if(colors[i][0] == r && colors[i][1] == g && colors[i][2] == b) {
+ return i;
+ }
+ }
+
+ if(maxcol >= 32) return -1;
+
+ colors[maxcol][0] = r;
+ colors[maxcol][1] = g;
+ colors[maxcol][2] = b;
+ return maxcol++;
+}
+
+static void gfx_color(int c)
+{
+ glColor3ubv(colors[c]);
+}
+
+static void gfx_fillrect(struct wt_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();
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <SDL/SDL.h>
+#include "windtk.h"
+
+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 = {
+ 0,
+ gfx_newcol, gfx_color,
+ gfx_fillrect,
+ gfx_line
+};
+
+static SDL_Surface *fbsurf;
+static SDL_PixelFormat *pfmt;
+static int fb_width = 800, fb_height = 600;
+static uint32_t *fb;
+
+int main(void)
+{
+ int i, num_upd;
+ struct wt_rect *rect;
+ SDL_Event ev;
+
+ if(SDL_Init(SDL_INIT_VIDEO) == -1) {
+ fprintf(stderr, "failed to initialize SDL\n");
+ return 1;
+ }
+
+ if(!(fbsurf = SDL_SetVideoMode(fb_width, fb_height, 32, SDL_SWSURFACE | SDL_RESIZABLE))) {
+ fprintf(stderr, "failed to set video mode\n");
+ SDL_Quit();
+ return 1;
+ }
+ pfmt = fbsurf->format;
+
+ SDL_FillRect(fbsurf, 0, 0);
+
+ if(wt_init(fb_width, fb_height, &gfx) == -1) {
+ return 1;
+ }
+ wt_window(0, "foo", WT_WS_DEFAULT, 100, 100, 200, 200);
+
+ while(SDL_WaitEvent(&ev)) {
+ switch(ev.type) {
+ case SDL_QUIT:
+ goto end;
+
+ case SDL_KEYDOWN:
+ case SDL_KEYUP:
+ if(ev.key.keysym.sym == SDLK_ESCAPE) {
+ goto end;
+ }
+ wt_inp_key(ev.key.keysym.sym, ev.key.state == SDL_PRESSED);
+ break;
+
+ case SDL_MOUSEBUTTONDOWN:
+ case SDL_MOUSEBUTTONUP:
+ wt_inp_mouse(ev.button.button - SDL_BUTTON_LEFT, ev.button.state == SDL_PRESSED,
+ ev.button.x, ev.button.y);
+ break;
+
+ case SDL_MOUSEMOTION:
+ wt_inp_motion(ev.motion.x, ev.motion.y);
+ break;
+
+ case SDL_VIDEORESIZE:
+ fb_width = ev.resize.w;
+ fb_height = ev.resize.h;
+ wt_viewport(0, 0, ev.resize.w, ev.resize.h);
+ break;
+
+ default:
+ break;
+ }
+
+ if(SDL_MUSTLOCK(fbsurf)) {
+ SDL_LockSurface(fbsurf);
+ }
+ fb = fbsurf->pixels;
+ wt_draw();
+ if(SDL_MUSTLOCK(fbsurf)) {
+ SDL_UnlockSurface(fbsurf);
+ }
+
+ num_upd = wt_num_upd();
+ for(i=0; i<num_upd; i++) {
+ rect = wt_upd_rect(i);
+ SDL_UpdateRect(fbsurf, rect->x, rect->y, rect->w, rect->h);
+ }
+ }
+
+end:
+ wt_destroy();
+ SDL_Quit();
+ return 0;
+}
+
+/* ------ graphics callbacks -------- */
+static uint32_t colors[32];
+static int maxcol, curcol;
+
+static int gfx_newcol(int r, int g, int b)
+{
+ int i;
+ uint32_t pcol;
+
+ pcol = ((r >> pfmt->Rloss) << pfmt->Rshift) & pfmt->Rmask;
+ pcol |= ((g >> pfmt->Gloss) << pfmt->Gshift) & pfmt->Gmask;
+ pcol |= ((b >> pfmt->Bloss) << pfmt->Bshift) & pfmt->Bmask;
+
+ for(i=0; i<maxcol; i++) {
+ if(colors[i] == pcol) {
+ return i;
+ }
+ }
+
+ if(maxcol >= 32) return -1;
+
+ colors[maxcol] = pcol;
+ return maxcol++;
+}
+
+static void gfx_color(int c)
+{
+ curcol = c;
+}
+
+static void gfx_fillrect(struct wt_rect *r)
+{
+ int i, j, x, y, w, h;
+ uint32_t pcol = colors[curcol];
+ uint32_t *fbptr;
+
+ w = r->w;
+ h = r->h;
+ x = r->x;
+ y = r->y;
+
+ if(x < 0) {
+ w += x;
+ x = 0;
+ }
+ if(y < 0) {
+ h += y;
+ y = 0;
+ }
+ if(x + w >= fb_width) {
+ w = fb_width - x;
+ }
+ if(y + h >= fb_height) {
+ h = fb_height - y;
+ }
+
+ fbptr = fb + y * (fbsurf->pitch >> 2) + x;
+ for(i=0; i<h; i++) {
+ for(j=0; j<w; j++) {
+ fbptr[j] = pcol;
+ }
+ fbptr += fbsurf->pitch >> 2;
+ }
+}
+
+static void gfx_line(int x0, int y0, int x1, int y1)
+{
+}
return 0;
}
w->type = WT_TYPE_WIDGET;
+ w->dirty = 1;
if(!par) par = wt->root;
wt_add_child(par, w);
wt_free_widget(tree);
}
+void wt_dirty_widget(wt_widget *w)
+{
+ int i;
+
+ w->dirty = 1;
+
+ for(i=0; i<w->num_child; i++) {
+ wt_dirty_widget(w->child[i]);
+ }
+}
+
int wt_type(wt_widget *w)
{
return w->type;
void wt_resize(wt_widget *w, int x, int y)
{
- w->rect.width = x;
- w->rect.height = y;
+ w->rect.w = x;
+ w->rect.h = y;
/* TODO: invalidate something */
}
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;
+ if(xret) *xret = w->rect.w;
+ if(yret) *yret = w->rect.h;
+ return &w->rect.w;
}
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;
+ return x >= w->rect.x && y >= w->rect.y && x < w->rect.x + w->rect.w &&
+ y < w->rect.y + w->rect.h;
}
wt_widget *wt_widget_at(int x, int y)
wt_callback_func cb[WT_NUM_CALLBACKS];
void *cbcls[WT_NUM_CALLBACKS];
+ int dirty; /* redraw pending */
+
wt_draw_func draw;
void (*click)(struct wt_widget *w);
void (*keypress)(struct wt_widget *w, int key);
#define TBARTHICK 12 /* TODO: base it on font metrics */
#define TBAROFFS (TBARTHICK + FRMBEVEL * 2)
+void calc_window_rect(struct wt_rect *r, wt_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(wt_widget *w, struct wt_graphics *gfx)
{
struct wt_rect fr, tmprect;
wt_gfx_color(COL_BG);
wt_gfx_fillrect(&w->rect);
- fr.x = w->rect.x - FRMOFFS;
- fr.y = w->rect.y - TBAROFFS - FRMOFFS;
- fr.width = w->rect.width + FRMOFFS * 2;
- fr.height = w->rect.height + TBAROFFS + FRMOFFS * 2;
+ calc_window_rect(&fr, w);
wt_gfx_frame(&fr, FRM_OUT | FRM_NOFILL, frmcol); /* outer bevel */
fr.x += FRMBEVEL;
fr.y += FRMBEVEL;
- fr.width -= FRMBEVEL * 2;
- fr.height -= FRMBEVEL * 2;
+ fr.w -= FRMBEVEL * 2;
+ fr.h -= FRMBEVEL * 2;
tmprect = fr;
- tmprect.width = FRMTHICK;
+ tmprect.w = FRMTHICK;
wt_gfx_fillrect(&tmprect); /* left bar */
- tmprect.x += fr.width - FRMTHICK;
+ tmprect.x += fr.w - FRMTHICK;
wt_gfx_fillrect(&tmprect); /* right bar */
tmprect = fr;
- tmprect.height = FRMTHICK;
+ tmprect.h = FRMTHICK;
tmprect.x += FRMTHICK;
- tmprect.width -= FRMTHICK * 2;
+ tmprect.w -= FRMTHICK * 2;
wt_gfx_fillrect(&tmprect); /* top bar */
- tmprect.y += fr.height - FRMTHICK;
+ tmprect.y += fr.h - FRMTHICK;
wt_gfx_fillrect(&tmprect); /* bottom bar */
/* inner bevel */
fr.x += FRMTHICK;
fr.y += FRMTHICK;
- fr.width -= FRMTHICK * 2;
- fr.height -= FRMTHICK * 2;
+ fr.w -= FRMTHICK * 2;
+ fr.h -= FRMTHICK * 2;
wt_gfx_frame(&fr, FRM_IN | FRM_NOFILL, frmcol);
/* titlebar */
fr.x = w->rect.x;
- fr.width = w->rect.width;
+ fr.w = w->rect.w;
fr.y += FRMBEVEL;
- fr.height = TBARTHICK + FRMBEVEL * 2;
+ fr.h = TBARTHICK + FRMBEVEL * 2;
wt_gfx_frame(&fr, FRM_OUT, frmcol);
}
int i;
if(tree->draw) {
- tree->draw(tree, &wt->gfx);
+ if(wt->gfx.flags & WT_GFX_NODIRTY) {
+ tree->draw(tree, &wt->gfx);
+ } else {
+ if(tree->dirty) {
+ tree->draw(tree, &wt->gfx);
+
+ if(!tree->parent || !tree->parent->dirty) {
+ /* topmost dirty widget should update rects */
+ if(tree->type == WT_TYPE_WINDOW) {
+ struct wt_rect r;
+ calc_window_rect(&r, tree);
+ wt_add_upd_rect(&r);
+ } else {
+ wt_add_upd_rect(&tree->rect);
+ }
+ }
+ }
+ }
}
for(i=0; i<tree->num_child; i++) {
wt_draw_tree(tree->child[i]);
}
+
+ tree->dirty = 0;
}
void wt_draw(void)
{
+ wt->num_upd = 0;
wt_draw_tree(wt->root);
}
+int wt_num_upd(void)
+{
+ return wt->num_upd;
+}
+
+struct wt_rect *wt_upd_rect(int idx)
+{
+ if(idx < 0 || idx >= wt->num_upd) {
+ return 0;
+ }
+ return wt->upd + idx;
+}
+
+void wt_add_upd_rect(struct wt_rect *r)
+{
+ int i;
+ struct wt_rect rect;
+
+ for(i=0; i<wt->num_upd; i++) {
+ if(wt_rect_overlap(wt->upd + i, r)) {
+ rect = wt->upd[i];
+ wt->upd[i] = wt->upd[--wt->num_upd];
+ wt_rect_union(&rect, r);
+ wt_add_upd_rect(&rect);
+ return;
+ }
+ }
+
+ /* no overlaps found, add it if there is space, or union with existing if not */
+ if(wt->num_upd >= MAX_UPD_RECTS) {
+ wt_rect_union(wt->upd, r);
+ } else {
+ wt->upd[wt->num_upd++] = *r;
+ }
+}
+
+
void wt_gfx_color(int cidx)
{
wt->gfx.color(wt->colors[cidx]);
wt_gfx_fillrect(r);
}
wt_gfx_color(FRMSTYLE(style) == FRM_OUT ? basecol + 2 : basecol + 1);
- wt_gfx_fillrect4i(r->x + 1, r->y + r->height - 1, r->width - 2, 1);
- wt_gfx_fillrect4i(r->x + r->width - 1, r->y, 1, r->height);
+ wt_gfx_fillrect4i(r->x + 1, r->y + r->h - 1, r->w - 2, 1);
+ wt_gfx_fillrect4i(r->x + r->w - 1, r->y, 1, r->h);
wt_gfx_color(FRMSTYLE(style) == FRM_OUT ? basecol + 1 : basecol + 2);
- wt_gfx_fillrect4i(r->x + 1, r->y, r->width - 2, 1);
- wt_gfx_fillrect4i(r->x, r->y, 1, r->height);
+ wt_gfx_fillrect4i(r->x + 1, r->y, r->w - 2, 1);
+ wt_gfx_fillrect4i(r->x, r->y, 1, r->h);
}
void wt_gfx_line(int x0, int y0, int x1, int y1)
{
r->x = x;
r->y = y;
- r->width = w;
- r->height = h;
+ r->w = w;
+ r->h = h;
+}
+
+void wt_rect_union(struct wt_rect *a, struct wt_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 wt_rect_overlap(struct wt_rect *a, struct wt_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;
}
};
struct wt_rect {
- int x, y, width, height;
+ int x, y, w, h;
+};
+
+/* graphics flags */
+enum {
+ WT_GFX_RGB = 1, /* RGB mode, calls newcolor to set current, not to allocate */
+ WT_GFX_NODIRTY = 2 /* don't use dirty rects, always redraw completely */
};
struct wt_graphics {
+ unsigned int flags;
+
int (*newcolor)(int r, int g, int b);
void (*color)(int c);
void wt_draw(void);
+/* screen regions updated by the last wt_draw */
+int wt_num_upd(void);
+struct wt_rect *wt_upd_rect(int idx);
+void wt_add_upd_rect(struct wt_rect *r);
+
wt_widget *wt_alloc_widget(wt_widget *par);
void wt_free_widget(wt_widget *w);
void wt_free_tree(wt_widget *tree);
+void wt_dirty_widget(wt_widget *w); /* propagates to children */
+
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);
void wt_callback(wt_widget *w, int type, wt_callback_func func, void *cls);
void wt_rect(struct wt_rect *r, int x, int y, int w, int h);
+void wt_rect_union(struct wt_rect *a, struct wt_rect *b);
+int wt_rect_overlap(struct wt_rect *a, struct wt_rect *b);
#endif /* WINDTK_H_ */
enum { FRM_OUT, FRM_IN, FRM_NOFILL = 0x8000 };
#define FRMSTYLE(x) ((x) & 0xff)
+#define MAX_UPD_RECTS 16
+
struct wt_context {
struct wt_graphics gfx;
struct wt_rect vp;
int colors[NUM_COLORS];
wt_widget *focuswin;
+
+ struct wt_rect upd[MAX_UPD_RECTS];
+ int num_upd;
};
extern struct wt_context *wt_curctx_;
void wt_gfx_frame(struct wt_rect *r, int style, int basecol);
void wt_gfx_line(int x0, int y0, int x1, int y1);
+void calc_window_rect(struct wt_rect *r, wt_widget *w);
+
#endif /* WTIMPL_H_ */