--- /dev/null
+*.o
+*.d
+*.swp
+rbench
--- /dev/null
+src = $(wildcard src/*.c) $(wildcard src/x11/*.c)
+obj = $(src:.c=.o)
+dep = $(src:.c=.d)
+bin = rbench
+
+warn = -pedantic -Wall -Wno-deprecated-declarations
+dbg = -g
+#opt = -O3 -ffast-math
+inc = -Isrc
+
+CFLAGS = -pedantic $(warn) $(dbg) $(opt) $(inc) -fcommon -MMD
+LDFLAGS = -lX11 -lm
+
+$(bin): $(obj)
+ $(CC) -o $@ $(obj) $(LDFLAGS)
+
+-include $(dep)
+
+.PHONY: clean
+clean:
+ $(RM) $(obj) $(bin)
+
+.PHONY: cleandep
+cleandep:
+ $(RM) $(dep)
--- /dev/null
+/* dynarr - dynamic resizable C array data structure
+ * author: John Tsiombikas <nuclear@member.fsf.org>
+ * license: public domain
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "dynarr.h"
+
+/* The array descriptor keeps auxilliary information needed to manipulate
+ * the dynamic array. It's allocated adjacent to the array buffer.
+ */
+struct arrdesc {
+ int nelem, szelem;
+ int max_elem;
+ int bufsz; /* not including the descriptor */
+};
+
+#define DESC(x) ((struct arrdesc*)((char*)(x) - sizeof(struct arrdesc)))
+
+void *dynarr_alloc(int elem, int szelem)
+{
+ struct arrdesc *desc;
+
+ if(!(desc = malloc(elem * szelem + sizeof *desc))) {
+ return 0;
+ }
+ desc->nelem = desc->max_elem = elem;
+ desc->szelem = szelem;
+ desc->bufsz = elem * szelem;
+ return (char*)desc + sizeof *desc;
+}
+
+void dynarr_free(void *da)
+{
+ if(da) {
+ free(DESC(da));
+ }
+}
+
+void *dynarr_resize(void *da, int elem)
+{
+ int newsz;
+ void *tmp;
+ struct arrdesc *desc;
+
+ if(!da) return 0;
+ desc = DESC(da);
+
+ newsz = desc->szelem * elem;
+
+ if(!(tmp = realloc(desc, newsz + sizeof *desc))) {
+ return 0;
+ }
+ desc = tmp;
+
+ desc->nelem = desc->max_elem = elem;
+ desc->bufsz = newsz;
+ return (char*)desc + sizeof *desc;
+}
+
+int dynarr_empty(void *da)
+{
+ return DESC(da)->nelem ? 0 : 1;
+}
+
+int dynarr_size(void *da)
+{
+ return DESC(da)->nelem;
+}
+
+
+void *dynarr_clear(void *da)
+{
+ return dynarr_resize(da, 0);
+}
+
+/* stack semantics */
+void *dynarr_push(void *da, void *item)
+{
+ struct arrdesc *desc;
+ int nelem;
+
+ desc = DESC(da);
+ nelem = desc->nelem;
+
+ if(nelem >= desc->max_elem) {
+ /* need to resize */
+ struct arrdesc *tmp;
+ int newsz = desc->max_elem ? desc->max_elem * 2 : 1;
+
+ if(!(tmp = dynarr_resize(da, newsz))) {
+ fprintf(stderr, "failed to resize\n");
+ return da;
+ }
+ da = tmp;
+ desc = DESC(da);
+ desc->nelem = nelem;
+ }
+
+ if(item) {
+ memcpy((char*)da + desc->nelem++ * desc->szelem, item, desc->szelem);
+ }
+ return da;
+}
+
+void *dynarr_pop(void *da)
+{
+ struct arrdesc *desc;
+ int nelem;
+
+ desc = DESC(da);
+ nelem = desc->nelem;
+
+ if(!nelem) return da;
+
+ if(nelem <= desc->max_elem / 3) {
+ /* reclaim space */
+ struct arrdesc *tmp;
+ int newsz = desc->max_elem / 2;
+
+ if(!(tmp = dynarr_resize(da, newsz))) {
+ fprintf(stderr, "failed to resize\n");
+ return da;
+ }
+ da = tmp;
+ desc = DESC(da);
+ desc->nelem = nelem;
+ }
+ desc->nelem--;
+
+ return da;
+}
+
+void *dynarr_finalize(void *da)
+{
+ struct arrdesc *desc = DESC(da);
+ memmove(desc, da, desc->bufsz);
+ return desc;
+}
--- /dev/null
+/* dynarr - dynamic resizable C array data structure
+ * author: John Tsiombikas <nuclear@member.fsf.org>
+ * license: public domain
+ */
+#ifndef DYNARR_H_
+#define DYNARR_H_
+
+/* usage example:
+ * -------------
+ * int *arr = dynarr_alloc(0, sizeof *arr);
+ *
+ * int x = 10;
+ * arr = dynarr_push(arr, &x);
+ * x = 5;
+ * arr = dynarr_push(arr, &x);
+ * x = 42;
+ * arr = dynarr_push(arr, &x);
+ *
+ * for(i=0; i<dynarr_size(arr); i++) {
+ * printf("%d\n", arr[i]);
+ * }
+ * dynarr_free(arr);
+ */
+
+void *dynarr_alloc(int elem, int szelem);
+void dynarr_free(void *da);
+void *dynarr_resize(void *da, int elem);
+
+/* dynarr_empty returns non-zero if the array is empty
+ * Complexity: O(1) */
+int dynarr_empty(void *da);
+/* dynarr_size returns the number of elements in the array
+ * Complexity: O(1) */
+int dynarr_size(void *da);
+
+void *dynarr_clear(void *da);
+
+/* stack semantics */
+void *dynarr_push(void *da, void *item);
+void *dynarr_pop(void *da);
+
+/* Finalize the array. No more resizing is possible after this call.
+ * Use free() instead of dynarr_free() to deallocate a finalized array.
+ * Returns pointer to the finalized array.
+ * dynarr_finalize can't fail.
+ * Complexity: O(n)
+ */
+void *dynarr_finalize(void *da);
+
+/* helper macros */
+#define DYNARR_RESIZE(da, n) \
+ do { (da) = dynarr_resize((da), (n)); } while(0)
+#define DYNARR_CLEAR(da) \
+ do { (da) = dynarr_clear(da); } while(0)
+#define DYNARR_PUSH(da, item) \
+ do { (da) = dynarr_push((da), (item)); } while(0)
+#define DYNARR_POP(da) \
+ do { (da) = dynarr_pop(da); } while(0)
+
+/* utility macros to push characters to a string. assumes and maintains
+ * the invariant that the last element is always a zero
+ */
+#define DYNARR_STRPUSH(da, c) \
+ do { \
+ char cnull = 0, ch = (char)(c); \
+ (da) = dynarr_pop(da); \
+ (da) = dynarr_push((da), &ch); \
+ (da) = dynarr_push((da), &cnull); \
+ } while(0)
+
+#define DYNARR_STRPOP(da) \
+ do { \
+ char cnull = 0; \
+ (da) = dynarr_pop(da); \
+ (da) = dynarr_pop(da); \
+ (da) = dynarr_push((da), &cnull); \
+ } while(0)
+
+
+#endif /* DYNARR_H_ */
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "rbench.h"
+#include "treestor.h"
+
+#define DEF_WIDTH 640
+#define DEF_HEIGHT 480
+#define DEF_BPP 24
+
+struct options opt = {
+ DEF_WIDTH, DEF_HEIGHT, DEF_BPP
+};
+
+void redraw(void)
+{
+}
+
+void key_event(int key, int press)
+{
+}
+
+int read_config(const char *fname)
+{
+ FILE *fp;
+ struct ts_node *ts;
+
+ if(!(fp = fopen(fname, "rb"))) {
+ return -1;
+ }
+ fclose(fp);
+
+ if(!(ts = ts_load(fname))) {
+ return -1;
+ }
+
+ opt.width = ts_lookup_int(ts, "rbench.width", DEF_WIDTH);
+ opt.height = ts_lookup_int(ts, "rbench.height", DEF_HEIGHT);
+ opt.bpp = ts_lookup_int(ts, "rbench.bpp", DEF_BPP);
+
+ ts_free_tree(ts);
+ return 0;
+}
--- /dev/null
+#ifndef RETROBENCH_H_
+#define RETROBENCH_H_
+
+struct options {
+ int width, height, bpp;
+};
+
+extern struct options opt;
+
+void redraw(void);
+void key_event(int key, int press);
+
+int read_config(const char *fname);
+
+
+#endif /* RETROBENCH_H_ */
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "treestor.h"
+
+#if defined(__WATCOMC__) || defined(_WIN32) || defined(__DJGPP__)
+#include <malloc.h>
+#else
+#include <alloca.h>
+#endif
+
+struct ts_node *ts_text_load(FILE *fp);
+int ts_text_save(struct ts_node *tree, FILE *fp);
+
+/* ---- ts_value implementation ---- */
+
+int ts_init_value(struct ts_value *tsv)
+{
+ memset(tsv, 0, sizeof *tsv);
+ return 0;
+}
+
+void ts_destroy_value(struct ts_value *tsv)
+{
+ int i;
+
+ free(tsv->str);
+ free(tsv->vec);
+
+ for(i=0; i<tsv->array_size; i++) {
+ ts_destroy_value(tsv->array + i);
+ }
+ free(tsv->array);
+}
+
+
+struct ts_value *ts_alloc_value(void)
+{
+ struct ts_value *v = malloc(sizeof *v);
+ if(!v || ts_init_value(v) == -1) {
+ free(v);
+ return 0;
+ }
+ return v;
+}
+
+void ts_free_value(struct ts_value *tsv)
+{
+ ts_destroy_value(tsv);
+ free(tsv);
+}
+
+
+int ts_copy_value(struct ts_value *dest, struct ts_value *src)
+{
+ int i;
+
+ if(dest == src) return 0;
+
+ *dest = *src;
+
+ dest->str = 0;
+ dest->vec = 0;
+ dest->array = 0;
+
+ if(src->str) {
+ if(!(dest->str = malloc(strlen(src->str) + 1))) {
+ goto fail;
+ }
+ strcpy(dest->str, src->str);
+ }
+ if(src->vec && src->vec_size > 0) {
+ if(!(dest->vec = malloc(src->vec_size * sizeof *src->vec))) {
+ goto fail;
+ }
+ memcpy(dest->vec, src->vec, src->vec_size * sizeof *src->vec);
+ }
+ if(src->array && src->array_size > 0) {
+ if(!(dest->array = calloc(src->array_size, sizeof *src->array))) {
+ goto fail;
+ }
+ for(i=0; i<src->array_size; i++) {
+ if(ts_copy_value(dest->array + i, src->array + i) == -1) {
+ goto fail;
+ }
+ }
+ }
+ return 0;
+
+fail:
+ free(dest->str);
+ free(dest->vec);
+ if(dest->array) {
+ for(i=0; i<dest->array_size; i++) {
+ ts_destroy_value(dest->array + i);
+ }
+ free(dest->array);
+ }
+ return -1;
+}
+
+#define MAKE_NUMSTR_FUNC(type, fmt) \
+ static char *make_##type##str(type x) \
+ { \
+ static char scrap[128]; \
+ char *str; \
+ int sz = sprintf(scrap, fmt, x); \
+ if(!(str = malloc(sz + 1))) return 0; \
+ sprintf(str, fmt, x); \
+ return str; \
+ }
+
+MAKE_NUMSTR_FUNC(int, "%d")
+MAKE_NUMSTR_FUNC(float, "%g")
+
+
+struct val_list_node {
+ struct ts_value val;
+ struct val_list_node *next;
+};
+
+int ts_set_value_str(struct ts_value *tsv, const char *str)
+{
+ /*char *endp;*/
+
+ if(tsv->str) {
+ ts_destroy_value(tsv);
+ if(ts_init_value(tsv) == -1) {
+ return -1;
+ }
+ }
+
+ tsv->type = TS_STRING;
+ if(!(tsv->str = malloc(strlen(str) + 1))) {
+ return -1;
+ }
+ strcpy(tsv->str, str);
+
+#if 0
+ /* try to parse the string and see if it fits any of the value types */
+ if(*str == '[' || *str == '{') {
+ /* try to parse as a vector */
+ struct val_list_node *list = 0, *tail = 0, *node;
+ int nelem = 0;
+ char endsym = *str++ + 2; /* ']' is '[' + 2 and '}' is '{' + 2 */
+
+ while(*str && *str != endsym) {
+ float val = strtod(str, &endp);
+ if(endp == str || !(node = malloc(sizeof *node))) {
+ break;
+ }
+ ts_init_value(&node->val);
+ ts_set_valuef(&node->val, val);
+ node->next = 0;
+
+ if(list) {
+ tail->next = node;
+ tail = node;
+ } else {
+ list = tail = node;
+ }
+ ++nelem;
+ str = endp;
+ }
+
+ if(nelem && (tsv->array = malloc(nelem * sizeof *tsv->array)) &&
+ (tsv->vec = malloc(nelem * sizeof *tsv->vec))) {
+ int idx = 0;
+ while(list) {
+ node = list;
+ list = list->next;
+
+ tsv->array[idx] = node->val;
+ tsv->vec[idx] = node->val.fnum;
+ ++idx;
+ free(node);
+ }
+ tsv->type = TS_VECTOR;
+ }
+
+ } else if((tsv->fnum = strtod(str, &endp)), endp != str) {
+ /* it's a number I guess... */
+ tsv->type = TS_NUMBER;
+ }
+#endif
+
+ return 0;
+}
+
+int ts_set_valuei_arr(struct ts_value *tsv, int count, const int *arr)
+{
+ int i;
+
+ if(count < 1) return -1;
+ if(count == 1) {
+ if(!(tsv->str = make_intstr(*arr))) {
+ return -1;
+ }
+
+ tsv->type = TS_NUMBER;
+ tsv->fnum = (float)*arr;
+ tsv->inum = *arr;
+ return 0;
+ }
+
+ /* otherwise it's an array, we need to create the ts_value array, and
+ * the simplified vector
+ */
+ if(!(tsv->vec = malloc(count * sizeof *tsv->vec))) {
+ return -1;
+ }
+ tsv->vec_size = count;
+
+ for(i=0; i<count; i++) {
+ tsv->vec[i] = arr[i];
+ }
+
+ if(!(tsv->array = malloc(count * sizeof *tsv->array))) {
+ free(tsv->vec);
+ }
+ tsv->array_size = count;
+
+ for(i=0; i<count; i++) {
+ ts_init_value(tsv->array + i);
+ ts_set_valuef(tsv->array + i, arr[i]);
+ }
+
+ tsv->type = TS_VECTOR;
+ return 0;
+}
+
+int ts_set_valueiv(struct ts_value *tsv, int count, ...)
+{
+ int res;
+ va_list ap;
+ va_start(ap, count);
+ res = ts_set_valueiv_va(tsv, count, ap);
+ va_end(ap);
+ return res;
+}
+
+int ts_set_valueiv_va(struct ts_value *tsv, int count, va_list ap)
+{
+ int i, *vec;
+
+ if(count < 1) return -1;
+ if(count == 1) {
+ int num = va_arg(ap, int);
+ ts_set_valuei(tsv, num);
+ return 0;
+ }
+
+ vec = alloca(count * sizeof *vec);
+ for(i=0; i<count; i++) {
+ vec[i] = va_arg(ap, int);
+ }
+ return ts_set_valuei_arr(tsv, count, vec);
+}
+
+int ts_set_valuei(struct ts_value *tsv, int inum)
+{
+ return ts_set_valuei_arr(tsv, 1, &inum);
+}
+
+int ts_set_valuef_arr(struct ts_value *tsv, int count, const float *arr)
+{
+ int i;
+
+ if(count < 1) return -1;
+ if(count == 1) {
+ if(!(tsv->str = make_floatstr(*arr))) {
+ return -1;
+ }
+
+ tsv->type = TS_NUMBER;
+ tsv->fnum = *arr;
+ tsv->inum = (int)*arr;
+ return 0;
+ }
+
+ /* otherwise it's an array, we need to create the ts_value array, and
+ * the simplified vector
+ */
+ if(!(tsv->vec = malloc(count * sizeof *tsv->vec))) {
+ return -1;
+ }
+ tsv->vec_size = count;
+
+ for(i=0; i<count; i++) {
+ tsv->vec[i] = arr[i];
+ }
+
+ if(!(tsv->array = malloc(count * sizeof *tsv->array))) {
+ free(tsv->vec);
+ }
+ tsv->array_size = count;
+
+ for(i=0; i<count; i++) {
+ ts_init_value(tsv->array + i);
+ ts_set_valuef(tsv->array + i, arr[i]);
+ }
+
+ tsv->type = TS_VECTOR;
+ return 0;
+}
+
+int ts_set_valuefv(struct ts_value *tsv, int count, ...)
+{
+ int res;
+ va_list ap;
+ va_start(ap, count);
+ res = ts_set_valuefv_va(tsv, count, ap);
+ va_end(ap);
+ return res;
+}
+
+int ts_set_valuefv_va(struct ts_value *tsv, int count, va_list ap)
+{
+ int i;
+ float *vec;
+
+ if(count < 1) return -1;
+ if(count == 1) {
+ float num = va_arg(ap, double);
+ ts_set_valuef(tsv, num);
+ return 0;
+ }
+
+ vec = alloca(count * sizeof *vec);
+ for(i=0; i<count; i++) {
+ vec[i] = va_arg(ap, double);
+ }
+ return ts_set_valuef_arr(tsv, count, vec);
+}
+
+int ts_set_valuef(struct ts_value *tsv, float fnum)
+{
+ return ts_set_valuef_arr(tsv, 1, &fnum);
+}
+
+int ts_set_value_arr(struct ts_value *tsv, int count, const struct ts_value *arr)
+{
+ int i, allnum = 1;
+
+ if(count <= 1) return -1;
+
+ if(!(tsv->array = malloc(count * sizeof *tsv->array))) {
+ return -1;
+ }
+ tsv->array_size = count;
+
+ for(i=0; i<count; i++) {
+ if(arr[i].type != TS_NUMBER) {
+ allnum = 0;
+ }
+ if(ts_copy_value(tsv->array + i, (struct ts_value*)arr + i) == -1) {
+ while(--i >= 0) {
+ ts_destroy_value(tsv->array + i);
+ }
+ free(tsv->array);
+ tsv->array = 0;
+ return -1;
+ }
+ }
+
+ if(allnum) {
+ if(!(tsv->vec = malloc(count * sizeof *tsv->vec))) {
+ ts_destroy_value(tsv);
+ return -1;
+ }
+ tsv->type = TS_VECTOR;
+
+ for(i=0; i<count; i++) {
+ tsv->vec[i] = tsv->array[i].fnum;
+ }
+ } else {
+ tsv->type = TS_ARRAY;
+ }
+ return 0;
+}
+
+int ts_set_valuev(struct ts_value *tsv, int count, ...)
+{
+ int res;
+ va_list ap;
+ va_start(ap, count);
+ res = ts_set_valuev_va(tsv, count, ap);
+ va_end(ap);
+ return res;
+}
+
+int ts_set_valuev_va(struct ts_value *tsv, int count, va_list ap)
+{
+ int i;
+
+ if(count <= 1) return -1;
+
+ if(!(tsv->array = malloc(count * sizeof *tsv->array))) {
+ return -1;
+ }
+ tsv->array_size = count;
+
+ for(i=0; i<count; i++) {
+ struct ts_value *src = va_arg(ap, struct ts_value*);
+ if(ts_copy_value(tsv->array + i, src) == -1) {
+ while(--i >= 0) {
+ ts_destroy_value(tsv->array + i);
+ }
+ free(tsv->array);
+ tsv->array = 0;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+/* ---- ts_attr implementation ---- */
+
+int ts_init_attr(struct ts_attr *attr)
+{
+ memset(attr, 0, sizeof *attr);
+ return ts_init_value(&attr->val);
+}
+
+void ts_destroy_attr(struct ts_attr *attr)
+{
+ free(attr->name);
+ ts_destroy_value(&attr->val);
+}
+
+struct ts_attr *ts_alloc_attr(void)
+{
+ struct ts_attr *attr = malloc(sizeof *attr);
+ if(!attr || ts_init_attr(attr) == -1) {
+ free(attr);
+ return 0;
+ }
+ return attr;
+}
+
+void ts_free_attr(struct ts_attr *attr)
+{
+ ts_destroy_attr(attr);
+ free(attr);
+}
+
+int ts_copy_attr(struct ts_attr *dest, struct ts_attr *src)
+{
+ if(dest == src) return 0;
+
+ if(ts_set_attr_name(dest, src->name) == -1) {
+ return -1;
+ }
+
+ if(ts_copy_value(&dest->val, &src->val) == -1) {
+ ts_destroy_attr(dest);
+ return -1;
+ }
+ return 0;
+}
+
+int ts_set_attr_name(struct ts_attr *attr, const char *name)
+{
+ char *n = malloc(strlen(name) + 1);
+ if(!n) return -1;
+ strcpy(n, name);
+
+ free(attr->name);
+ attr->name = n;
+ return 0;
+}
+
+
+/* ---- ts_node implementation ---- */
+
+int ts_init_node(struct ts_node *node)
+{
+ memset(node, 0, sizeof *node);
+ return 0;
+}
+
+void ts_destroy_node(struct ts_node *node)
+{
+ if(!node) return;
+
+ free(node->name);
+
+ while(node->attr_list) {
+ struct ts_attr *attr = node->attr_list;
+ node->attr_list = node->attr_list->next;
+ ts_free_attr(attr);
+ }
+}
+
+struct ts_node *ts_alloc_node(void)
+{
+ struct ts_node *node = malloc(sizeof *node);
+ if(!node || ts_init_node(node) == -1) {
+ free(node);
+ return 0;
+ }
+ return node;
+}
+
+void ts_free_node(struct ts_node *node)
+{
+ ts_destroy_node(node);
+ free(node);
+}
+
+void ts_free_tree(struct ts_node *tree)
+{
+ if(!tree) return;
+
+ while(tree->child_list) {
+ struct ts_node *child = tree->child_list;
+ tree->child_list = tree->child_list->next;
+ ts_free_tree(child);
+ }
+
+ ts_free_node(tree);
+}
+
+void ts_add_attr(struct ts_node *node, struct ts_attr *attr)
+{
+ attr->next = 0;
+ if(node->attr_list) {
+ node->attr_tail->next = attr;
+ node->attr_tail = attr;
+ } else {
+ node->attr_list = node->attr_tail = attr;
+ }
+}
+
+struct ts_attr *ts_get_attr(struct ts_node *node, const char *name)
+{
+ struct ts_attr *attr = node->attr_list;
+ while(attr) {
+ if(strcmp(attr->name, name) == 0) {
+ return attr;
+ }
+ attr = attr->next;
+ }
+ return 0;
+}
+
+const char *ts_get_attr_str(struct ts_node *node, const char *aname, const char *def_val)
+{
+ struct ts_attr *attr = ts_get_attr(node, aname);
+ if(!attr || !attr->val.str) {
+ return def_val;
+ }
+ return attr->val.str;
+}
+
+float ts_get_attr_num(struct ts_node *node, const char *aname, float def_val)
+{
+ struct ts_attr *attr = ts_get_attr(node, aname);
+ if(!attr || attr->val.type != TS_NUMBER) {
+ return def_val;
+ }
+ return attr->val.fnum;
+}
+
+int ts_get_attr_int(struct ts_node *node, const char *aname, int def_val)
+{
+ struct ts_attr *attr = ts_get_attr(node, aname);
+ if(!attr || attr->val.type != TS_NUMBER) {
+ return def_val;
+ }
+ return attr->val.inum;
+}
+
+float *ts_get_attr_vec(struct ts_node *node, const char *aname, float *def_val)
+{
+ struct ts_attr *attr = ts_get_attr(node, aname);
+ if(!attr || !attr->val.vec) {
+ return def_val;
+ }
+ return attr->val.vec;
+}
+
+struct ts_value *ts_get_attr_array(struct ts_node *node, const char *aname, struct ts_value *def_val)
+{
+ struct ts_attr *attr = ts_get_attr(node, aname);
+ if(!attr || !attr->val.array) {
+ return def_val;
+ }
+ return attr->val.array;
+}
+
+void ts_add_child(struct ts_node *node, struct ts_node *child)
+{
+ if(child->parent) {
+ if(child->parent == node) return;
+ ts_remove_child(child->parent, child);
+ }
+ child->parent = node;
+ child->next = 0;
+
+ if(node->child_list) {
+ node->child_tail->next = child;
+ node->child_tail = child;
+ } else {
+ node->child_list = node->child_tail = child;
+ }
+}
+
+int ts_remove_child(struct ts_node *node, struct ts_node *child)
+{
+ struct ts_node dummy, *iter = &dummy;
+ dummy.next = node->child_list;
+
+ while(iter->next && iter->next != child) {
+ iter = iter->next;
+ }
+ if(!iter->next) {
+ return -1;
+ }
+
+ child->parent = 0;
+
+ iter->next = child->next;
+ if(!iter->next) {
+ node->child_tail = iter;
+ }
+ node->child_list = dummy.next;
+ return 0;
+}
+
+struct ts_node *ts_get_child(struct ts_node *node, const char *name)
+{
+ struct ts_node *res = node->child_list;
+ while(res) {
+ if(strcmp(res->name, name) == 0) {
+ return res;
+ }
+ res = res->next;
+ }
+ return 0;
+}
+
+struct ts_node *ts_load(const char *fname)
+{
+ FILE *fp;
+ struct ts_node *root;
+
+ if(!(fp = fopen(fname, "rb"))) {
+ fprintf(stderr, "ts_load: failed to open file: %s: %s\n", fname, strerror(errno));
+ return 0;
+ }
+
+ root = ts_text_load(fp);
+ fclose(fp);
+ return root;
+}
+
+int ts_save(struct ts_node *tree, const char *fname)
+{
+ FILE *fp;
+ int res;
+
+ if(!(fp = fopen(fname, "wb"))) {
+ fprintf(stderr, "ts_save: failed to open file: %s: %s\n", fname, strerror(errno));
+ return 0;
+ }
+ res = ts_text_save(tree, fp);
+ fclose(fp);
+ return res;
+}
+
+static const char *pathtok(const char *path, char *tok)
+{
+ int len;
+ const char *dot = strchr(path, '.');
+ if(!dot) {
+ strcpy(tok, path);
+ return 0;
+ }
+
+ len = dot - path;
+ memcpy(tok, path, len);
+ tok[len] = 0;
+ return dot + 1;
+}
+
+struct ts_node *ts_lookup_node(struct ts_node *node, const char *path)
+{
+ char *name = alloca(strlen(path) + 1);
+
+ if(!node) return 0;
+
+ if(!(path = pathtok(path, name)) || strcmp(name, node->name) != 0) {
+ return 0;
+ }
+
+ do {
+ path = pathtok(path, name);
+ } while((node = ts_get_child(node, name)) && path);
+
+ if(path || !node) return 0;
+ return node;
+}
+
+struct ts_attr *ts_lookup(struct ts_node *node, const char *path)
+{
+ char *name = alloca(strlen(path) + 1);
+
+ if(!node) return 0;
+
+ if(!(path = pathtok(path, name)) || strcmp(name, node->name) != 0) {
+ return 0;
+ }
+
+ while((path = pathtok(path, name)) && (node = ts_get_child(node, name)));
+
+ if(path || !node) return 0;
+ return ts_get_attr(node, name);
+}
+
+const char *ts_lookup_str(struct ts_node *root, const char *path, const char *def_val)
+{
+ struct ts_attr *attr = ts_lookup(root, path);
+ if(!attr || !attr->val.str) {
+ return def_val;
+ }
+ return attr->val.str;
+}
+
+float ts_lookup_num(struct ts_node *root, const char *path, float def_val)
+{
+ struct ts_attr *attr = ts_lookup(root, path);
+ if(!attr || attr->val.type != TS_NUMBER) {
+ return def_val;
+ }
+ return attr->val.fnum;
+}
+
+int ts_lookup_int(struct ts_node *root, const char *path, int def_val)
+{
+ struct ts_attr *attr = ts_lookup(root, path);
+ if(!attr || attr->val.type != TS_NUMBER) {
+ return def_val;
+ }
+ return attr->val.inum;
+}
+
+float *ts_lookup_vec(struct ts_node *root, const char *path, float *def_val)
+{
+ struct ts_attr *attr = ts_lookup(root, path);
+ if(!attr || !attr->val.vec) {
+ return def_val;
+ }
+ return attr->val.vec;
+}
+
+struct ts_value *ts_lookup_array(struct ts_node *node, const char *path, struct ts_value *def_val)
+{
+ struct ts_attr *attr = ts_lookup(node, path);
+ if(!attr || !attr->val.array) {
+ return def_val;
+ }
+ return attr->val.array;
+}
--- /dev/null
+#ifndef TREESTORE_H_
+#define TREESTORE_H_
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+#define TS_DEFVAL(x) =(x)
+extern "C" {
+#else
+#define TS_DEFVAL(x)
+#endif
+
+enum ts_value_type { TS_STRING, TS_NUMBER, TS_VECTOR, TS_ARRAY };
+
+/** treestore node attribute value */
+struct ts_value {
+ enum ts_value_type type;
+
+ char *str; /**< string values will have this set */
+ int inum; /**< numeric values will have this set */
+ float fnum; /**< numeric values will have this set */
+
+ /** vector values (arrays containing ONLY numbers) will have this set */
+ float *vec; /**< elements of the vector */
+ int vec_size; /**< size of the vector (in elements), same as array_size */
+
+ /** array values (including vectors) will have this set */
+ struct ts_value *array; /**< elements of the array */
+ int array_size; /**< size of the array (in elements) */
+};
+
+int ts_init_value(struct ts_value *tsv);
+void ts_destroy_value(struct ts_value *tsv);
+
+struct ts_value *ts_alloc_value(void); /**< also calls ts_init_value */
+void ts_free_value(struct ts_value *tsv); /**< also calls ts_destroy_value */
+
+/** perform a deep-copy of a ts_value */
+int ts_copy_value(struct ts_value *dest, struct ts_value *src);
+
+/** set a ts_value as a string */
+int ts_set_value_str(struct ts_value *tsv, const char *str);
+
+/** set a ts_value from a list of integers */
+int ts_set_valuei_arr(struct ts_value *tsv, int count, const int *arr);
+int ts_set_valueiv(struct ts_value *tsv, int count, ...);
+int ts_set_valueiv_va(struct ts_value *tsv, int count, va_list ap);
+int ts_set_valuei(struct ts_value *tsv, int inum); /**< equiv: ts_set_valueiv(val, 1, inum) */
+
+/** set a ts_value from a list of floats */
+int ts_set_valuef_arr(struct ts_value *tsv, int count, const float *arr);
+int ts_set_valuefv(struct ts_value *tsv, int count, ...);
+int ts_set_valuefv_va(struct ts_value *tsv, int count, va_list ap);
+int ts_set_valuef(struct ts_value *tsv, float fnum); /**< equiv: ts_set_valuefv(val, 1, fnum) */
+
+/** set a ts_value from a list of ts_value pointers. they are deep-copied as per ts_copy_value */
+int ts_set_value_arr(struct ts_value *tsv, int count, const struct ts_value *arr);
+int ts_set_valuev(struct ts_value *tsv, int count, ...);
+int ts_set_valuev_va(struct ts_value *tsv, int count, va_list ap);
+
+
+/** treestore node attribute */
+struct ts_attr {
+ char *name;
+ struct ts_value val;
+
+ struct ts_attr *next;
+};
+
+int ts_init_attr(struct ts_attr *attr);
+void ts_destroy_attr(struct ts_attr *attr);
+
+struct ts_attr *ts_alloc_attr(void); /**< also calls ts_init_attr */
+void ts_free_attr(struct ts_attr *attr); /**< also calls ts_destroy_attr */
+
+/** perform a deep-copy of a ts_attr */
+int ts_copy_attr(struct ts_attr *dest, struct ts_attr *src);
+
+int ts_set_attr_name(struct ts_attr *attr, const char *name);
+
+
+
+/** treestore node */
+struct ts_node {
+ char *name;
+
+ int attr_count;
+ struct ts_attr *attr_list, *attr_tail;
+
+ int child_count;
+ struct ts_node *child_list, *child_tail;
+ struct ts_node *parent;
+
+ struct ts_node *next; /* next sibling */
+};
+
+int ts_init_node(struct ts_node *node);
+void ts_destroy_node(struct ts_node *node);
+
+struct ts_node *ts_alloc_node(void); /**< also calls ts_init_node */
+void ts_free_node(struct ts_node *n); /**< also calls ts_destroy_node */
+
+/** recursively destroy all the nodes of the tree */
+void ts_free_tree(struct ts_node *tree);
+
+void ts_add_attr(struct ts_node *node, struct ts_attr *attr);
+struct ts_attr *ts_get_attr(struct ts_node *node, const char *name);
+
+const char *ts_get_attr_str(struct ts_node *node, const char *aname,
+ const char *def_val TS_DEFVAL(0));
+float ts_get_attr_num(struct ts_node *node, const char *aname,
+ float def_val TS_DEFVAL(0.0f));
+int ts_get_attr_int(struct ts_node *node, const char *aname,
+ int def_val TS_DEFVAL(0.0f));
+float *ts_get_attr_vec(struct ts_node *node, const char *aname,
+ float *def_val TS_DEFVAL(0));
+struct ts_value *ts_get_attr_array(struct ts_node *node, const char *aname,
+ struct ts_value *def_val TS_DEFVAL(0));
+
+
+void ts_add_child(struct ts_node *node, struct ts_node *child);
+int ts_remove_child(struct ts_node *node, struct ts_node *child);
+struct ts_node *ts_get_child(struct ts_node *node, const char *name);
+
+struct ts_node *ts_load(const char *fname);
+int ts_save(struct ts_node *tree, const char *fname);
+
+struct ts_node *ts_lookup_node(struct ts_node *root, const char *path);
+
+struct ts_attr *ts_lookup(struct ts_node *root, const char *path);
+const char *ts_lookup_str(struct ts_node *root, const char *path,
+ const char *def_val TS_DEFVAL(0));
+float ts_lookup_num(struct ts_node *root, const char *path,
+ float def_val TS_DEFVAL(0.0f));
+int ts_lookup_int(struct ts_node *root, const char *path,
+ int def_val TS_DEFVAL(0));
+float *ts_lookup_vec(struct ts_node *root, const char *path,
+ float *def_val TS_DEFVAL(0));
+struct ts_value *ts_lookup_array(struct ts_node *root, const char *path,
+ struct ts_value *def_val TS_DEFVAL(0));
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TREESTORE_H_ */
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "treestor.h"
+#include "dynarr.h"
+
+struct parser {
+ FILE *fp;
+ int nline;
+ char *token;
+};
+
+enum { TOK_SYM, TOK_ID, TOK_NUM, TOK_STR };
+
+static struct ts_node *read_node(struct parser *pstate);
+static int read_array(struct parser *pstate, struct ts_value *tsv, char endsym);
+static int next_token(struct parser *pstate);
+
+static void print_attr(struct ts_attr *attr, FILE *fp, int level);
+static void print_value(struct ts_value *value, FILE *fp);
+static int tree_level(struct ts_node *n);
+static const char *indent(int x);
+static const char *toktypestr(int type);
+
+#define EXPECT(type) \
+ do { \
+ if(next_token(pst) != (type)) { \
+ fprintf(stderr, "expected %s token\n", toktypestr(type)); \
+ goto err; \
+ } \
+ } while(0)
+
+#define EXPECT_SYM(c) \
+ do { \
+ if(next_token(pst) != TOK_SYM || pst->token[0] != (c)) { \
+ fprintf(stderr, "expected symbol: %c\n", c); \
+ goto err; \
+ } \
+ } while(0)
+
+
+struct ts_node *ts_text_load(FILE *fp)
+{
+ char *root_name;
+ struct parser pstate, *pst = &pstate;
+ struct ts_node *node = 0;
+
+ pstate.fp = fp;
+ pstate.nline = 0;
+ if(!(pstate.token = dynarr_alloc(0, 1))) {
+ perror("failed to allocate token string");
+ return 0;
+ }
+
+ EXPECT(TOK_ID);
+ if(!(root_name = strdup(pst->token))) {
+ perror("failed to allocate root node name");
+ dynarr_free(pst->token);
+ return 0;
+ }
+ EXPECT_SYM('{');
+ if(!(node = read_node(pst))) {
+ dynarr_free(pst->token);
+ return 0;
+ }
+ node->name = root_name;
+
+err:
+ dynarr_free(pst->token);
+ return node;
+}
+
+static int read_value(struct parser *pst, int toktype, struct ts_value *val)
+{
+ switch(toktype) {
+ case TOK_NUM:
+ ts_set_valuef(val, atof(pst->token));
+ break;
+
+ case TOK_SYM:
+ if(pst->token[0] == '[' || pst->token[0] == '{') {
+ char endsym = pst->token[0] + 2; /* end symbol is dist 2 from either '[' or '{' */
+ if(read_array(pst, val, endsym) == -1) {
+ return -1;
+ }
+ } else {
+ fprintf(stderr, "read_node: unexpected rhs symbol: %c\n", pst->token[0]);
+ }
+ break;
+
+ case TOK_ID:
+ case TOK_STR:
+ default:
+ ts_set_value_str(val, pst->token);
+ }
+
+ return 0;
+}
+
+static struct ts_node *read_node(struct parser *pst)
+{
+ int type;
+ struct ts_node *node;
+
+ if(!(node = ts_alloc_node())) {
+ perror("failed to allocate treestore node");
+ return 0;
+ }
+
+ while((type = next_token(pst)) == TOK_ID) {
+ char *id;
+
+ if(!(id = strdup(pst->token))) {
+ goto err;
+ }
+
+ EXPECT(TOK_SYM);
+
+ if(pst->token[0] == '=') {
+ /* attribute */
+ struct ts_attr *attr;
+ int type;
+
+ if(!(attr = ts_alloc_attr())) {
+ goto err;
+ }
+
+ if((type = next_token(pst)) == -1) {
+ ts_free_attr(attr);
+ fprintf(stderr, "read_node: unexpected EOF\n");
+ goto err;
+ }
+
+ if(read_value(pst, type, &attr->val) == -1) {
+ ts_free_attr(attr);
+ fprintf(stderr, "failed to read value\n");
+ goto err;
+ }
+ attr->name = id;
+ ts_add_attr(node, attr);
+
+ } else if(pst->token[0] == '{') {
+ /* child */
+ struct ts_node *child;
+
+ if(!(child = read_node(pst))) {
+ ts_free_node(node);
+ return 0;
+ }
+
+ child->name = id;
+ ts_add_child(node, child);
+
+ } else {
+ fprintf(stderr, "unexpected token: %s\n", pst->token);
+ goto err;
+ }
+ }
+
+ if(type != TOK_SYM || pst->token[0] != '}') {
+ fprintf(stderr, "expected closing brace\n");
+ goto err;
+ }
+ return node;
+
+err:
+ fprintf(stderr, "treestore read_node failed\n");
+ ts_free_node(node);
+ return 0;
+}
+
+static int read_array(struct parser *pst, struct ts_value *tsv, char endsym)
+{
+ int type;
+ struct ts_value values[32];
+ int i, nval = 0;
+ int res;
+
+ while((type = next_token(pst)) != -1) {
+ ts_init_value(values + nval);
+ if(read_value(pst, type, values + nval) == -1) {
+ return -1;
+ }
+ if(nval < 31) {
+ ++nval;
+ } else {
+ ts_destroy_value(values + nval);
+ }
+
+ type = next_token(pst);
+ if(!(type == TOK_SYM && (pst->token[0] == ',' || pst->token[0] == endsym))) {
+ fprintf(stderr, "read_array: expected comma or end symbol ('%c')\n", endsym);
+ return -1;
+ }
+ if(pst->token[0] == endsym) {
+ break; /* we're done */
+ }
+ }
+
+ if(!nval) {
+ return -1;
+ }
+
+ res = ts_set_value_arr(tsv, nval, values);
+
+ for(i=0; i<nval; i++) {
+ ts_destroy_value(values + i);
+ }
+ return res;
+}
+
+static int next_token(struct parser *pst)
+{
+ int c;
+
+ DYNARR_CLEAR(pst->token);
+
+ /* skip whitespace */
+ while((c = fgetc(pst->fp)) != -1) {
+ if(c == '#') { /* skip to end of line */
+ while((c = fgetc(pst->fp)) != -1 && c != '\n');
+ if(c == -1) return -1;
+ }
+ if(!isspace(c)) break;
+ if(c == '\n') ++pst->nline;
+ }
+ if(c == -1) return -1;
+
+ DYNARR_STRPUSH(pst->token, c);
+
+ if(isdigit(c) || c == '-' || c == '+') {
+ /* token is a number */
+ int found_dot = 0;
+ while((c = fgetc(pst->fp)) != -1 &&
+ (isdigit(c) || (c == '.' && !found_dot))) {
+ DYNARR_STRPUSH(pst->token, c);
+ if(c == '.') found_dot = 1;
+ }
+ if(c != -1) ungetc(c, pst->fp);
+ return TOK_NUM;
+ }
+ if(isalpha(c)) {
+ /* token is an identifier */
+ while((c = fgetc(pst->fp)) != -1 && (isalnum(c) || c == '_')) {
+ DYNARR_STRPUSH(pst->token, c);
+ }
+ if(c != -1) ungetc(c, pst->fp);
+ return TOK_ID;
+ }
+ if(c == '"') {
+ /* token is a string constant */
+ /* remove the opening quote */
+ DYNARR_STRPOP(pst->token);
+ while((c = fgetc(pst->fp)) != -1 && c != '"') {
+ DYNARR_STRPUSH(pst->token, c);
+ if(c == '\n') ++pst->nline;
+ }
+ if(c != '"') {
+ return -1;
+ }
+ return TOK_STR;
+ }
+ return TOK_SYM;
+}
+
+int ts_text_save(struct ts_node *tree, FILE *fp)
+{
+ struct ts_node *c;
+ struct ts_attr *attr;
+ int lvl = tree_level(tree);
+
+ fprintf(fp, "%s%s {\n", indent(lvl), tree->name);
+
+ attr = tree->attr_list;
+ while(attr) {
+ print_attr(attr, fp, lvl);
+ attr = attr->next;
+ }
+
+ c = tree->child_list;
+ while(c) {
+ ts_text_save(c, fp);
+ c = c->next;
+ }
+
+ fprintf(fp, "%s}\n", indent(lvl));
+ return 0;
+}
+
+static void print_attr(struct ts_attr *attr, FILE *fp, int level)
+{
+ fprintf(fp, "%s%s = ", indent(level + 1), attr->name);
+ print_value(&attr->val, fp);
+ fputc('\n', fp);
+}
+
+static void print_value(struct ts_value *value, FILE *fp)
+{
+ int i;
+
+ switch(value->type) {
+ case TS_NUMBER:
+ fprintf(fp, "%g", value->fnum);
+ break;
+
+ case TS_VECTOR:
+ fputc('[', fp);
+ for(i=0; i<value->vec_size; i++) {
+ if(i == 0) {
+ fprintf(fp, "%g", value->vec[i]);
+ } else {
+ fprintf(fp, ", %g", value->vec[i]);
+ }
+ }
+ fputc(']', fp);
+ break;
+
+ case TS_ARRAY:
+ fputc('[', fp);
+ for(i=0; i<value->array_size; i++) {
+ if(i > 0) {
+ fprintf(fp, ", ");
+ }
+ print_value(value->array + i, fp);
+ }
+ fputc(']', fp);
+ break;
+
+ default:
+ fprintf(fp, "\"%s\"", value->str);
+ }
+}
+
+static int tree_level(struct ts_node *n)
+{
+ if(!n->parent) return 0;
+ return tree_level(n->parent) + 1;
+}
+
+static const char *indent(int x)
+{
+ static const char buf[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
+ const char *end = buf + sizeof buf - 1;
+ return x > sizeof buf - 1 ? buf : end - x;
+}
+
+static const char *toktypestr(int type)
+{
+ switch(type) {
+ case TOK_ID:
+ return "identifier";
+ case TOK_NUM:
+ return "number";
+ case TOK_STR:
+ return "string";
+ case TOK_SYM:
+ return "symbol";
+ }
+ return "unknown";
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include "rbench.h"
+
+enum { QUIT = 1, REDRAW = 2 };
+
+static Window create_win(int width, int height, int bpp);
+static void handle_event(XEvent *ev);
+static int translate_keysym(KeySym sym);
+static int parse_args(int argc, char **argv);
+
+static int win_width, win_height;
+static int mapped;
+static unsigned int pending;
+static Display *dpy;
+static Window win, root;
+static Atom xa_wm_proto, xa_wm_delwin;
+
+int main(int argc, char **argv)
+{
+ XEvent ev;
+
+ read_config("rbench.cfg");
+
+ if(parse_args(argc, argv) == -1) {
+ return 1;
+ }
+
+ if(!(dpy = XOpenDisplay(0))) {
+ fprintf(stderr, "failed to connect to the X server\n");
+ return 1;
+ }
+ root = DefaultRootWindow(dpy);
+ xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", 0);
+ xa_wm_delwin = XInternAtom(dpy, "WM_DELETE_WINDOW", 0);
+
+ if(!(win = create_win(opt.width, opt.height, opt.bpp))) {
+ return 1;
+ }
+
+ for(;;) {
+ if(mapped) {
+ while(XPending(dpy)) {
+ XNextEvent(dpy, &ev);
+ handle_event(&ev);
+ if(pending & QUIT) goto end;
+ }
+ redraw();
+ } else {
+ XNextEvent(dpy, &ev);
+ handle_event(&ev);
+ if(pending & QUIT) goto end;
+ }
+ }
+
+end:
+ XDestroyWindow(dpy, win);
+ XCloseDisplay(dpy);
+ return 0;
+}
+
+static Window create_win(int width, int height, int bpp)
+{
+ int scr, num_vis;
+ Window win;
+ XVisualInfo *vis, vinf;
+ unsigned int vinf_mask;
+ XSetWindowAttributes xattr;
+ XTextProperty txname;
+ Colormap cmap;
+ const char *name = "retrobench X11";
+
+ scr = DefaultScreen(dpy);
+
+ vinf.screen = scr;
+ vinf.depth = bpp;
+ vinf.class = bpp <= 8 ? PseudoColor : TrueColor;
+ vinf_mask = VisualScreenMask | VisualDepthMask | VisualClassMask;
+ if(!(vis = XGetVisualInfo(dpy, vinf_mask, &vinf, &num_vis))) {
+ fprintf(stderr, "failed to find appropriate visual for %d bpp\n", bpp);
+ return 0;
+ }
+
+ if(!(cmap = XCreateColormap(dpy, root, vis->visual, bpp <= 8 ? AllocAll : AllocNone))) {
+ fprintf(stderr, "failed to allocate colormap\n");
+ return 0;
+ }
+
+ xattr.background_pixel = BlackPixel(dpy, scr);
+ xattr.colormap = cmap;
+ win = XCreateWindow(dpy, root, 0, 0, width, height, 0, vis->depth,
+ InputOutput, vis->visual, CWColormap | CWBackPixel, &xattr);
+ if(!win) return 0;
+
+ XSelectInput(dpy, win, StructureNotifyMask | ExposureMask | KeyPressMask |
+ KeyReleaseMask);
+
+ XStringListToTextProperty((char**)&name, 1, &txname);
+ XSetWMName(dpy, win, &txname);
+ XSetWMIconName(dpy, win, &txname);
+ XFree(txname.value);
+
+ XSetWMProtocols(dpy, win, &xa_wm_delwin, 1);
+
+ XMapWindow(dpy, win);
+ return win;
+}
+
+static void handle_event(XEvent *ev)
+{
+ int key;
+ KeySym sym;
+
+ switch(ev->type) {
+ case MapNotify:
+ mapped = 1;
+ break;
+
+ case UnmapNotify:
+ mapped = 0;
+ break;
+
+ case Expose:
+ pending |= REDRAW;
+ break;
+
+ case ConfigureNotify:
+ if(ev->xconfigure.width != win_width || ev->xconfigure.height != win_height) {
+ win_width = ev->xconfigure.width;
+ win_height = ev->xconfigure.height;
+ /* TODO */
+ }
+ break;
+
+ case KeyPress:
+ case KeyRelease:
+ if((sym = XKeycodeToKeysym(dpy, ev->xkey.keycode, 0))) {
+ if(sym == XK_Escape) {
+ pending |= QUIT;
+ break;
+ }
+ if((key = translate_keysym(sym))) {
+ key_event(key, ev->xkey.type == KeyPress);
+ }
+ }
+ break;
+
+ case ClientMessage:
+ if(ev->xclient.message_type == xa_wm_proto) {
+ if(ev->xclient.data.l[0] == xa_wm_delwin) {
+ pending |= QUIT;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int translate_keysym(KeySym sym)
+{
+ if(sym < 128) return sym;
+
+ switch(sym) {
+ case XK_Escape:
+ return 27;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+static const char *usage_str =
+ "Usage: %s [options]\n"
+ "Options:\n"
+ " -s <WxH>: resolution\n"
+ " -b <bpp>: color depth\n"
+ " -h: print usage and exit\n";
+
+static int parse_args(int argc, char **argv)
+{
+ int i;
+
+ for(i=1; i<argc; i++) {
+ if(argv[i][0] == '-') {
+ if(argv[i][2]) {
+ goto inval_arg;
+ }
+ switch(argv[i][1]) {
+ case 's':
+ if(!argv[++i] || sscanf(argv[i], "%dx%d", &opt.width, &opt.height) != 2) {
+ fprintf(stderr, "-s must be followed by WxH\n");
+ return -1;
+ }
+ break;
+
+ case 'b':
+ if(!argv[++i] || !(opt.bpp = atoi(argv[i]))) {
+ fprintf(stderr, "-b must be followed by the color depth\n");
+ return -1;
+ }
+ break;
+
+ case 'h':
+ printf(usage_str, argv[0]);
+ exit(0);
+
+ default:
+ goto inval_arg;
+ }
+ } else {
+inval_arg: fprintf(stderr, "invalid argument: %s\n", argv[i]);
+ return -1;
+ }
+ }
+ return 0;
+}