--- /dev/null
+CFLAGS=-g -Wall -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include
+
+.PRECIOUS: %.tab.h %.lex.c %.tab.c
+
+%.tab.h %.tab.c: %.y %.h
+ bison -t -p $* -d $<
+
+%.lex.c: %.l %.tab.h %.h
+ flex -P$* -o$@ $<
+
+all: obtheme
+
+obtheme: main.o obtheme.tab.o obtheme.lex.o obtheme.tab.h
+ $(CC) $(CFLAGS) -o obtheme main.o obtheme.tab.o obtheme.lex.o -lm -lglib-2.0
+
+clean:
+ rm -f obtheme *.lex.* *.tab.* *.o obtheme.c
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+#include "obtheme.h"
+
+static void decor_print(gpointer data, gpointer user_data)
+{
+ struct decor *decor = data;
+ printf(" decor id %s\n", decor->name);
+ printf(" anchor (%d %d %d)\n", decor->space.anchor.x, decor->space.anchor.y, decor->space.anchor.z);
+ printf(" up (%d %d %d)\n", decor->space.up.x, decor->space.up.y, decor->space.up.z);
+ if (decor->children)
+ g_slist_foreach(decor->children, decor_print, NULL);
+}
+
+static void class_print(gpointer key, gpointer value, gpointer user_data)
+{
+ char *classname = key;
+ struct class *class = value;
+
+ printf(" Class %s\n", classname);
+ g_slist_foreach(class->tree, decor_print, NULL);
+}
+
+static void theme_print(gpointer key, gpointer value, gpointer user_data)
+{
+ char *name = key;
+ struct theme *thm = value;
+ printf("name = %s\n", name);
+ g_hash_table_foreach(thm->classes, class_print, NULL);
+}
+
+static void material_print(gpointer key, gpointer value, gpointer user_data)
+{
+ char *name = key;
+ struct material *mat = value;
+ printf("name = %s\n", name);
+ printf(" opacity = %f\n", mat->opacity);
+}
+
+int main(int argc, char **argv)
+{
+ int err;
+ struct obthemedata themedata;
+
+ themedata.themes = g_hash_table_new(g_str_hash, g_str_equal);
+ themedata.materials = g_hash_table_new(g_str_hash, g_str_equal);
+ err = obtheme_parse(&themedata, argv[1]);
+ printf("err = %d\n", err);
+
+ g_hash_table_foreach(themedata.materials, material_print, NULL);
+
+ g_hash_table_foreach(themedata.themes, theme_print, NULL);
+
+ return 0;
+}
--- /dev/null
+#ifndef __THEME_PARSE_H__
+#define __THEME_PARSE_H__
+
+#include <glib.h>
+
+#undef YY_DECL
+#define YY_DECL int obthemelex(YYSTYPE *yylval, struct parser_control *pc)
+
+#define YYDEBUG 1
+#define YYLEX_PARAM pc
+
+#define MAX_INCLUDE_DEPTH 32
+#define LINE pc->currline[pc->include_stack_ptr]
+
+extern int themedebug;
+
+struct material {
+ float opacity;
+};
+
+struct class {
+ char *name;
+ GSList *tree;
+};
+
+struct obthemedata {
+ GHashTable *themes;
+ GHashTable *materials;
+};
+
+struct theme {
+ char *name;
+ GHashTable *classes;
+};
+
+struct vector {
+ int x;
+ int y;
+ int z;
+};
+
+struct space {
+ struct vector anchor;
+ struct vector up;
+};
+
+struct decor {
+ char *name;
+ GSList *children;
+ struct space space;
+};
+
+struct parser_control {
+ struct yy_buffer_state *include_stack[MAX_INCLUDE_DEPTH];
+ int currline[MAX_INCLUDE_DEPTH];
+ char currfile[MAX_INCLUDE_DEPTH][501];
+ int include_stack_ptr;
+ char error_buf[4096];
+ struct obthemedata *target;
+};
+
+void obthemeerror(struct parser_control *pc, char *s);
+int obtheme_parse(struct obthemedata *td, const char *filename);
+struct parser_control *parser_init(struct obthemedata *td);
+int obthemeparse(struct parser_control *);
+void parser_finish(struct parser_control *);
+
+#endif /* __THEME_PARSE_H__ */
--- /dev/null
+%{
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include "obtheme.h"
+#include "obtheme.tab.h"
+
+int yylex(YYSTYPE *yylval, struct parser_control *pc);
+
+void obthemeerror(struct parser_control *pc, char *s)
+{
+ printf("Parse error in file %s on line %d\n%s"
+ , pc->currfile[pc->include_stack_ptr]
+ , pc->currline[pc->include_stack_ptr] + 1
+ , pc->error_buf);
+}
+
+extern int parserparse(void *);
+
+int yywrap(void)
+{
+ return 1;
+}
+
+int obtheme_parse(struct obthemedata *td, const char *filename)
+{
+ FILE *input;
+ int ret;
+ struct parser_control *pc;
+//illdebug = 1;
+ pc = parser_init(td);
+ input = fopen(filename, "r");
+ if (!input)
+ return 2;
+ yyin = input;
+ yyrestart(input);
+ BEGIN 0;
+ pc->include_stack_ptr = 0;
+ pc->currline[pc->include_stack_ptr] = 0;
+ strncpy(pc->currfile[pc->include_stack_ptr], filename, 500);
+ ret = obthemeparse(pc);
+ if (ret != 0) {
+ //XXX I THINK I NEED TO CLOSE ALL INCLUDE FILES HERE
+ //probably also fclose input and call parser_finish?
+ return ret;
+ }
+ fclose(input);
+ parser_finish(pc);
+ return ret;
+}
+
+%}
+%x comment
+%x incl
+%%
+
+include BEGIN(incl);
+<incl>[ \t]* /* eat the whitespace */
+<incl>[^ \t\n]+ { /* got the include file name */
+ char *incfile;
+ if ( pc->include_stack_ptr >= MAX_INCLUDE_DEPTH) {
+ fprintf( stderr, "Includes nested too deeply" );
+ exit(1);
+ }
+
+ pc->include_stack[pc->include_stack_ptr++] = YY_CURRENT_BUFFER;
+ incfile = malloc(strlen(yytext) + strlen("include/")+ 1);
+ strcpy(incfile, "include/");
+ strcat(incfile, yytext);
+ yyin = fopen( incfile, "r" );
+ free(incfile);
+ strncpy(pc->currfile[pc->include_stack_ptr], yytext, 500);
+ pc->currline[pc->include_stack_ptr] = 0;
+ if ( ! yyin ) {
+ printf("Could not find include file %s (%s)\n", yytext, incfile);
+ exit(1);
+ }
+ yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
+
+ BEGIN(INITIAL);
+ }
+
+<<EOF>> {
+ if ( --(pc->include_stack_ptr) < 0 )
+ {
+ yy_delete_buffer(YY_CURRENT_BUFFER);
+ yyterminate();
+ }
+
+ else
+ {
+ yy_delete_buffer( YY_CURRENT_BUFFER );
+ fclose(yyin);
+ yy_switch_to_buffer(
+ pc->include_stack[pc->include_stack_ptr] );
+ }
+ }
+
+
+"//"+[^\n]* ;
+"/*" BEGIN(comment);
+<comment>[^*\n]* ;
+<comment>"*"+[^*/\n]* ;
+<comment>"*"+"/" BEGIN(INITIAL);
+<comment>\n LINE++;
+
+theme return THEME;
+frame return FRAME;
+decor return DECOR;
+space return SPACE;
+geometry return GEOMETRY;
+material return MATERIAL;
+gradient return GRADIENT;
+context return CONTEXT;
+cursor return CURSOR;
+class return CLASS;
+up return UP;
+anchor return ANCHOR;
+opacity return OPACITY;
+shapeof return SHAPEOF;
+texture return TEXTURE;
+
+0x[0-9A-Z]+ yylval->integer = strtol(yytext, (char **)NULL, 16); return NUMBER;
+[0-9]+ yylval->integer = atoi(yytext); return NUMBER;
+[a-zA-Z_][a-zA-Z0-9_]* yylval->string = g_strdup(yytext); return ID;
+\".*\" {
+ yylval->string = g_strdup(yytext+1);
+ yylval->string[strlen(yylval->string)-1] = 0;
+ return STRING;
+ }
+\`[^`]*\` {
+ yylval->string = g_strdup(yytext+1);
+ yylval->string[strlen(yylval->string)-1] = 0;
+ return STRING;
+ }
+
+\$[a-zA-Z_][a-zA-Z0-9_]* yylval->string = g_strdup(yytext+1); return ID;
+\$[0-9]* yylval->integer = strtol(yytext + 1, (char **)NULL, 16); return SUBST;
+\@ return AT;
+: return COLON;
+; return SEMICOLON;
+"+" return PLUS;
+"-" return MINUS;
+"}" return RCB;
+"{" return LCB;
+")" return RB;
+"(" return LB;
+"<-" return LEFT_ARROW;
+"->" return RIGHT_ARROW;
+"<->" return DOUBLE_ARROW;
+\n LINE++; /* zap EOL */
+[ \t]+ ; /* and whitespace */
+. return 0;
+%%
--- /dev/null
+%pure-parser
+%name-prefix "obtheme"
+%parse-param {struct parser_control *pc}
+%{
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <unistd.h>
+#include "obtheme.h"
+#include "obtheme.tab.h"
+
+YY_DECL;
+
+struct parser_control *parser_init(struct obthemedata *otd)
+{
+ struct parser_control *out;
+ out = calloc(1, sizeof(struct parser_control));
+ out->include_stack_ptr = 0;
+ out->target = otd;
+ return out;
+}
+
+void parser_finish(struct parser_control *c)
+{
+ free(c);
+}
+
+%}
+%start theme_objects
+
+%union {
+ int integer;
+ float realnum;
+ char *string;
+ struct decor decor;
+ struct space space;
+ struct theme theme;
+ struct material material;
+ struct class class;
+ GSList *list;
+ GHashTable *hash;
+ struct vector vector;
+}
+
+%token LCB RCB LB RB
+%token LEFT_ARROW RIGHT_ARROW DOUBLE_ARROW
+%token SEMICOLON AT COLON DEFAULT NOT
+%token PLUS MINUS
+%token <string> STRING ID
+%token <integer> NUMBER SUBST BULK BIG LITTLE
+%token THEME FRAME SPACE GEOMETRY MATERIAL GRADIENT
+%token CONTEXT CURSOR UP ANCHOR CLASS TEXTURE OPACITY
+%token SHAPEOF DECOR
+%type <decor> decor
+%type <decor> decoritems classitem
+%type <space> space
+%type <class> class classitems
+%type <hash> classes
+%type <material> material_props
+%type <realnum> opacity
+%type <space> spaceconstraints
+%type <vector> up anchor
+%%
+
+theme_object : material_decl
+ | theme
+ ;
+
+theme_objects : /* empty */
+ | theme_objects theme_object
+ ;
+
+theme : THEME ID LCB classes RCB {
+ struct theme *out;
+ out = malloc(sizeof(struct theme));
+ out->name = $2;
+ out->classes = $4;
+ g_hash_table_insert(pc->target->themes, $2, out);
+ }
+ ;
+
+opacity : OPACITY LB NUMBER RB { $$ = $3; }
+ ;
+
+material_props : /* empty */ { memset(&$$, 0, sizeof($$)); }
+ | material_props opacity {
+ $$ = $1;
+ $$.opacity = $2;
+ }
+ ;
+
+material_decl : MATERIAL ID LCB material_props RCB {
+ struct material *out;
+ out = malloc(sizeof(struct material));
+ *out = $4;
+ g_hash_table_insert(pc->target->materials, $2, out);
+ }
+ ;
+
+anchor : ANCHOR LB NUMBER NUMBER NUMBER RB {
+ $$.x = $3;
+ $$.y = $4;
+ $$.z = $5;
+ }
+ ;
+
+up : UP LB NUMBER NUMBER NUMBER RB {
+ $$.x = $3;
+ $$.y = $4;
+ $$.z = $5;
+ }
+ ;
+
+spaceconstraints: /* empty */ { memset(&$$, 0, sizeof($$)); }
+ | spaceconstraints anchor { $1.anchor = $2; $$ = $1; }
+ | spaceconstraints up { $1.up = $2; $$ = $1; }
+ ;
+
+space : SPACE LCB spaceconstraints RCB {
+ $$ = $3;
+ }
+ ;
+
+shape : SHAPEOF LB ID RB
+ ;
+
+geometry_item : /* empty */
+ | shape
+ ;
+
+geometry : GEOMETRY LCB geometry_item RCB
+ ;
+
+material_use : MATERIAL LB ID RB
+ ;
+
+classitem : decor
+ ;
+
+classitems : /* empty */ { $$.tree = NULL; $$.name = NULL; }
+ | classitems classitem {
+ struct decor *out = malloc(sizeof(struct decor));
+ *out = $2;
+ $1.tree = g_slist_prepend($1.tree, out);
+ $$ = $1;
+ }
+ ;
+
+classes : /* empty */ { $$ = g_hash_table_new(g_str_hash, g_str_equal); }
+ | classes class {
+ struct class *out = malloc(sizeof(struct class));
+ *out = $2;
+ $$ = $1;
+ g_hash_table_insert($1, out->name, out);
+ }
+ ;
+
+class : CLASS ID LCB classitems RCB {
+ $$ = $4;
+ $$.name = $2;
+ }
+ ;
+
+decoritems : /* empty */ {
+ memset(&$$, 0, sizeof($$));
+ $$.space.up.y = -1;
+ }
+ | decoritems decor {
+ struct decor *out = malloc(sizeof(struct decor));
+ *out = $2;
+ $$ = $1;
+ $$.children = g_slist_append($1.children, out);
+ }
+ | decoritems space { $1.space = $2; $$ = $1; }
+ | decoritems material_use
+ | decoritems geometry
+ | decoritems texture
+ | decoritems context
+ | decoritems gradient
+ | decoritems cursor
+ ;
+
+ ;
+
+decor : DECOR ID LCB decoritems RCB {
+ $$ = $4;
+ $$.name = $2;
+ }
+ ;
+
+texture : TEXTURE LCB RCB
+ ;
+
+context : CONTEXT LB ID RB
+ ;
+
+cursor : CURSOR LB ID RB
+
+gradient : GRADIENT LB ID RB
+ ;
+
+%%
--- /dev/null
+//#include materials.obtheme
+
+material flat {
+ opacity(1)
+}
+
+theme awesome {
+ class regular_window {
+ decor clientwindow {
+ space {
+ anchor(10 9 8)
+ up(1 0 0)
+ }
+ geometry {
+ shapeof(client)
+ }
+ material(flat)
+ texture { //$CLIENT.contents
+ }
+
+ decor titlebar {
+ context(OB_FRAME_CONTEXT_TITLEBAR)
+ geometry {
+ // box (0, 0, 0) to (100% of $CLIENT, 100%, 0)
+ }
+ material(flat)
+ gradient(split)
+//texture {
+ decor title {
+ geometry {
+// text(client.title)
+// justify(center)
+ }
+ }
+ decor minbutton {
+ material(none)
+ context(OB_FRAME_CONTEXT_MINIMIZE)
+ }
+ decor maxbutton {
+ material(none)
+ context(OB_FRAME_CONTEXT_MAXIMIZE)
+ }
+ }
+ decor handle {
+ space {
+ anchor(0 0 0)
+ up(0 0 0)
+ }
+ decor leftgrip {
+ context(OB_FRAME_CONTEXT_BLCORNER)
+ cursor(SOUTHWEST)
+ }
+ decor rightgrip {
+ context(OB_FRAME_CONTEXT_BRCORNER)
+ cursor(SOUTHEAST)
+ }
+ context(OB_FRAME_CONTEXT_BOTTOM)
+ cursor(SOUTH)
+ }
+ }
+ }
+}