add the new theme framework
authorDerek Foreman <foremande@gmail.com>
Fri, 21 May 2010 15:56:19 +0000 (11:56 -0400)
committerDana Jansens <danakj@orodu.net>
Sat, 26 Jun 2010 23:30:45 +0000 (01:30 +0200)
obtheme/.gitignore [new file with mode: 0644]
obtheme/main.c [new file with mode: 0644]
obtheme/obtheme.c [new file with mode: 0644]
obtheme/obtheme.h [new file with mode: 0644]
obtheme/obtheme.l [new file with mode: 0644]
obtheme/obtheme.y [new file with mode: 0644]
obtheme/theme.obtheme [new file with mode: 0644]

diff --git a/obtheme/.gitignore b/obtheme/.gitignore
new file mode 100644 (file)
index 0000000..75b5c16
--- /dev/null
@@ -0,0 +1,4 @@
+*.o
+obtheme
+obtheme.lex.*
+obtheme.tab.*
diff --git a/obtheme/main.c b/obtheme/main.c
new file mode 100644 (file)
index 0000000..8fd3794
--- /dev/null
@@ -0,0 +1,35 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+#include "obtheme.h"
+
+struct obthemedata themedata;
+
+int main(int argc, char **argv)
+{
+       int err;
+       struct theme *thm;
+       Rect br;
+       Strut bstrut;
+
+       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]);
+       if (err) {
+               printf("Fix the script\n");
+               exit(1);
+       }
+       printf("err = %d\n", err);
+
+//     g_hash_table_foreach(themedata.materials, material_print, NULL);
+
+//     g_hash_table_foreach(themedata.themes, theme_print, NULL);
+
+       thm = g_hash_table_lookup(themedata.themes, "awesome");
+       obtheme_decorate_window(thm, "regular_window");
+       obtheme_calc_bound(thm, "regular_window", &br, &bstrut);
+       printf("bounding rectangle: (%d %d) - (%d %d)\n", br.x, br.y, br.x + br.width, br.y + br.height);
+
+       return 0;
+}
diff --git a/obtheme/obtheme.c b/obtheme/obtheme.c
new file mode 100644 (file)
index 0000000..dcafd84
--- /dev/null
@@ -0,0 +1,156 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+#include "obtheme.h"
+#include "geom.h"
+
+static double variable_lookup(struct variable *in)
+{
+       if (strcmp("client", in->base) == 0) {
+               if (strcmp("width", in->member) == 0) {
+                       return 640;
+               } else
+                       return 480;
+       }
+       if (strcmp("font", in->base) == 0) {
+               return 15.0;
+       }
+       return 0.0;
+}
+
+static double expression_eval(struct expression *in)
+{
+       switch (in->op) {
+               case OB_THEME_INV:
+                       return - expression_eval(in->a);
+                       break;
+               case OB_THEME_MUL:
+                       return expression_eval(in->a) * expression_eval(in->b);
+                       break;
+               case OB_THEME_DIV:
+                       return expression_eval(in->a) / expression_eval(in->b);
+                       break;
+               case OB_THEME_ADD:
+                       return expression_eval(in->a) + expression_eval(in->b);
+                       break;
+               case OB_THEME_SUB:
+                       return expression_eval(in->a) - expression_eval(in->b);
+                       break;
+               case OB_THEME_EQL:
+                       if (in->v.base == NULL) {
+                               return in->v.number;
+                       } else return variable_lookup(&in->v);
+                       break;
+               default:
+                       assert(!!!"OH NOES!!!");
+       }
+       return 0;
+}
+
+static void decor_print(gpointer data, gpointer user_data)
+{
+       struct decor *decor = data;
+       printf("    decor id %s\n", decor->name);
+printf("   anchor.x = %f\n", expression_eval(&decor->space.anchor.x));
+       if (decor->texture.present) {
+               if (decor->texture.internal)
+                       printf("    texture internal: %s\n", decor->texture.name);
+               else
+                       printf("    texture file: %s\n", decor->texture.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 style_print(gpointer key, gpointer value, gpointer user_data)
+{
+       char *stylename = key;
+       struct style *style = value;
+
+       printf("  style %s\n", stylename);
+       g_slist_foreach(style->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->styles, style_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);
+}
+
+static void decor_render(gpointer data, gpointer user_data)
+{
+       struct decor *decor = data;
+       struct box *box = &decor->geometry.box;
+       struct vector *a = &decor->space.anchor;
+       double x, y;
+
+       printf("%s:", decor->name);
+       x = expression_eval(&a->x);
+       y = expression_eval(&a->y);
+       printf("rectangle(%f %f) - (%f %f)\n", x + expression_eval(&box->start.x), y + expression_eval(&box->start.y),
+                                   x + expression_eval(&box->end.x), y + expression_eval(&box->end.y));
+       if (decor->children)
+               g_slist_foreach(decor->children, decor_render, NULL);
+}
+
+struct boundstuff {
+       Rect r;
+       Strut s;
+};
+
+static void decor_bound(gpointer data, gpointer user_data)
+{
+       struct boundstuff *bs = user_data;
+       struct decor *decor = data;
+       struct box *box = &decor->geometry.box;
+       struct vector *a = &decor->space.anchor;
+       Rect newrect, temprect;
+       double x1, y1, x2, y2;
+
+       x1 = expression_eval(&a->x);
+       y1 = expression_eval(&a->y);
+       x2 = x1;
+       y2 = y1;
+       x1 += expression_eval(&box->start.x);
+       y1 += expression_eval(&box->start.y);
+       x2 += expression_eval(&box->end.x);
+       y2 += expression_eval(&box->end.y);
+       RECT_SET(temprect, x1, y1, x2-x1, y2-y2);
+       RECT_ADD(newrect, temprect, bs->r);
+       bs->r = newrect;
+
+       if (decor->children)
+               g_slist_foreach(decor->children, decor_bound, bs);
+}
+
+void obtheme_calc_bound(struct theme *thm, char *name, Rect *r, Strut *s)
+{
+       struct style *sty;
+       struct boundstuff bs;
+
+       RECT_SET(bs.r, 0, 0 ,0 ,0);
+       sty = g_hash_table_lookup(thm->styles, name);
+       assert(sty);
+       g_slist_foreach(sty->tree, decor_bound, &bs);
+       *r = bs.r;
+}
+
+void obtheme_decorate_window(struct theme *thm, char *name)
+{
+       struct style *sty;
+       sty = g_hash_table_lookup(thm->styles, name);
+       g_slist_foreach(sty->tree, decor_render, NULL);
+}
diff --git a/obtheme/obtheme.h b/obtheme/obtheme.h
new file mode 100644 (file)
index 0000000..59bf310
--- /dev/null
@@ -0,0 +1,118 @@
+#ifndef __THEME_PARSE_H__
+#define __THEME_PARSE_H__
+
+#include <glib.h>
+#include "frame.h"
+#include "misc.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 boundrect {
+       double x1, y1, x2, y2;
+};
+
+typedef enum {
+       OB_THEME_INV,
+       OB_THEME_ADD,
+       OB_THEME_SUB,
+       OB_THEME_MUL,
+       OB_THEME_DIV,
+       OB_THEME_EQL
+} ObThemeOp;
+
+struct variable {
+       char *base;
+       char *member;
+       double number;
+};
+
+struct expression {
+       ObThemeOp op;
+       struct expression *a;
+       struct expression *b;
+       struct variable v;
+};
+
+struct material {
+       float opacity;
+};
+
+struct style {
+       char *name;
+       GSList *tree;
+};
+
+struct obthemedata {
+       GHashTable *themes;
+       GHashTable *materials;
+};
+
+struct theme {
+       char *name;
+       GHashTable *styles;
+};
+
+struct vector {
+       struct expression x;
+       struct expression y;
+       struct expression z;
+};
+
+struct space {
+       struct vector anchor;
+       struct vector up;
+};
+
+struct texture {
+       gboolean present;
+       gboolean internal;
+       char *name;
+};
+
+struct box {
+       struct vector start;
+       struct vector end;
+};
+
+struct geometry {
+       struct box box;
+};
+
+struct decor {
+       char *name;
+       GSList *children;
+       struct space space;
+       ObDirection cursor;
+       ObFrameContext context;
+       struct texture texture;
+       struct material *material;
+       struct geometry geometry;
+};
+
+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 *);
+void obtheme_calc_bound(struct theme *thm, char *name, Rect *r, Strut *s);
+void obtheme_decorate_window(struct theme *thm, char *name);
+
+#endif /* __THEME_PARSE_H__ */
diff --git a/obtheme/obtheme.l b/obtheme/obtheme.l
new file mode 100644 (file)
index 0000000..0806e10
--- /dev/null
@@ -0,0 +1,176 @@
+%{
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include "frame.h"
+#include "misc.h"
+#include "render.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;
+style                  return STYLE;
+up                     return UP;
+anchor                 return ANCHOR;
+opacity                        return OPACITY;
+shapeof                        return SHAPEOF;
+texture                        return TEXTURE;
+image                  return IMAGE;
+to                     return TO;
+box                    return BOX;
+NORTH                  return NORTH;
+NORTHEAST              return NORTHEAST;
+EAST                   return EAST;
+SOUTHEAST              return SOUTHEAST;
+SOUTH                  return SOUTH;
+SOUTHWEST              return SOUTHWEST;
+WEST                   return WEST;
+NORTHWEST              return NORTHWEST;
+NONE                   return NONE;
+UNCHANGED              return UNCHANGED;
+
+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;
+"."                    return DOT;
+"*"                    return STAR;
+"/"                    return SLASH;
+","                    return COMMA;
+\n                     LINE++; /* zap EOL */
+[ \t]+                 ; /* and whitespace */
+.                      return 0;
+%%
diff --git a/obtheme/obtheme.y b/obtheme/obtheme.y
new file mode 100644 (file)
index 0000000..e4e07dc
--- /dev/null
@@ -0,0 +1,429 @@
+%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 "frame.h"
+#include "misc.h"
+#include "render.h"
+#include "obtheme.h"
+#include "obtheme.tab.h"
+
+YY_DECL;
+
+struct context_table_item {
+       char *name;
+       ObFrameContext val;
+};
+
+/* some of these contexts don't make any sense for a theme... */
+static struct context_table_item contexts[OB_FRAME_NUM_CONTEXTS] = {
+       {"none", OB_FRAME_CONTEXT_NONE},
+       {"desktop", OB_FRAME_CONTEXT_DESKTOP},
+       {"root", OB_FRAME_CONTEXT_ROOT},
+       {"client", OB_FRAME_CONTEXT_CLIENT},
+       {"titlebar", OB_FRAME_CONTEXT_TITLEBAR},
+       {"frame", OB_FRAME_CONTEXT_FRAME},
+       {"blcorner", OB_FRAME_CONTEXT_BLCORNER},
+       {"brcorner", OB_FRAME_CONTEXT_BRCORNER},
+       {"tlcorner", OB_FRAME_CONTEXT_TLCORNER},
+       {"trcorner", OB_FRAME_CONTEXT_TRCORNER},
+       {"top", OB_FRAME_CONTEXT_TOP},
+       {"bottom", OB_FRAME_CONTEXT_BOTTOM},
+       {"left", OB_FRAME_CONTEXT_LEFT},
+       {"right", OB_FRAME_CONTEXT_RIGHT},
+       {"maximize", OB_FRAME_CONTEXT_MAXIMIZE},
+       {"alldesktops", OB_FRAME_CONTEXT_ALLDESKTOPS},
+       {"shade", OB_FRAME_CONTEXT_SHADE},
+       {"iconify", OB_FRAME_CONTEXT_ICONIFY},
+       {"icon", OB_FRAME_CONTEXT_ICON},
+       {"close", OB_FRAME_CONTEXT_CLOSE},
+       {"moveresize", OB_FRAME_CONTEXT_MOVE_RESIZE}
+};
+
+static ObFrameContext context_from_string(char *str)
+{
+       int i;
+       for (i = 0; i < OB_FRAME_NUM_CONTEXTS; i++)
+               if (strcmp(contexts[i].name, str) == 1)
+                       return contexts[i].val;
+       return -1;
+}
+
+struct gradient_table_item {
+       char *name;
+       RrSurfaceColorType val;
+};
+
+struct gradient_table_item gradients[RR_SURFACE_NUM_TYPES] = {
+       {"none", RR_SURFACE_NONE},
+       {"parentrel", RR_SURFACE_PARENTREL},
+       {"solid", RR_SURFACE_SOLID},
+       {"split", RR_SURFACE_SPLIT_VERTICAL},
+       {"horizontal", RR_SURFACE_HORIZONTAL},
+       {"vertical", RR_SURFACE_VERTICAL},
+       {"diagonal", RR_SURFACE_DIAGONAL},
+       {"cross_diagonal", RR_SURFACE_CROSS_DIAGONAL},
+       {"pyramid", RR_SURFACE_PYRAMID},
+       {"mirror_horizontal", RR_SURFACE_MIRROR_HORIZONTAL},
+};
+
+static RrSurfaceColorType gradient_from_string(char *str)
+{
+       int i;
+
+       for (i = 0; i < RR_SURFACE_NUM_TYPES; i++)
+               if (strcmp(gradients[i].name, str) == 1)
+                       return gradients[i].val;
+
+       return -1;
+}
+
+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);
+}
+
+struct expression *exdup(struct expression in)
+{
+       struct expression *out;
+       out = malloc(sizeof(struct expression));
+       *out = in;
+       return out;
+}
+
+%}
+%start theme_objects
+
+%union {
+       int integer;
+       float realnum;
+       char *string;
+       struct decor decor;
+       struct space space;
+       struct theme theme;
+       struct material material;
+       struct style style;
+       GSList *list;
+       GHashTable *hash;
+       struct vector vector;
+       ObCursor dir;
+       ObFrameContext context;
+       RrSurfaceColorType gradient;
+       struct expression expr;
+       struct variable var;
+       struct texture tex;
+       struct material *matptr;
+       struct box box;
+       struct geometry geometry;
+}
+
+%token NORTH NORTHEAST EAST SOUTHEAST SOUTH SOUTHWEST
+%token WEST NORTHWEST NONE UNCHANGED
+%token LCB RCB LB RB TO
+%token LEFT_ARROW RIGHT_ARROW DOUBLE_ARROW
+%token SEMICOLON AT COLON DEFAULT NOT
+%token PLUS MINUS STAR SLASH COMMA BOX
+%token <string> STRING ID
+%token <integer> NUMBER SUBST BULK BIG LITTLE
+%token THEME FRAME SPACE GEOMETRY MATERIAL GRADIENT
+%token CONTEXT CURSOR UP ANCHOR STYLE TEXTURE OPACITY
+%token SHAPEOF DECOR DOT IMAGE
+%type <decor> decor
+%type <decor> decoritems styleitem
+%type <space> space
+%type <style> style styleitems
+%type <hash> styles
+%type <material> material_props
+%type <realnum> opacity
+%type <space> spaceconstraints
+%type <vector> up anchor
+%type <dir> cursor
+%type <context> context
+%type <gradient> gradient
+%type <expr> expression
+%type <var> variable
+%type <tex> texture image texitems
+%type <matptr> material_use
+%type <box> box
+%type <geometry> geometry geometry_item
+%left PLUS MINUS
+%left STAR SLASH
+%left LB
+%%
+
+theme_object   : material_decl
+               | theme
+               ;
+
+theme_objects  : /* empty */
+               | theme_objects theme_object
+               ;
+
+theme          : THEME ID LCB styles RCB {
+                       struct theme *out;
+                       out = malloc(sizeof(struct theme));
+                       out->name = $2;
+                       out->styles = $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_props gradient
+               ;
+
+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 expression COMMA expression COMMA expression RB {
+                       $$.x = $3;
+                       $$.y = $5;
+                       $$.z = $7;
+               }
+               ;
+
+up             : UP LB expression COMMA expression COMMA expression RB {
+                       $$.x = $3;
+                       $$.y = $5;
+                       $$.z = $7;
+               }
+               ;
+
+spaceconstraints: /* empty */ { memset(&$$, 0, sizeof($$));
+                       $$.anchor.x.op = OB_THEME_EQL;
+                       $$.anchor.x.v.number = 0;
+                       $$.anchor.y.op = OB_THEME_EQL;
+                       $$.anchor.y.v.number = 0;
+                       $$.anchor.z.op = OB_THEME_EQL;
+                       $$.anchor.z.v.number = 0;
+                       $$.up.x.op = OB_THEME_EQL;
+                       $$.up.x.v.number = 0;
+                       $$.up.y.op = OB_THEME_EQL;
+                       $$.up.y.v.number = 1;
+                       $$.up.z.op = OB_THEME_EQL;
+                       $$.up.z.v.number = 0;
+               }
+               | spaceconstraints anchor { $1.anchor = $2; $$ = $1; }
+               | spaceconstraints up     { $1.up = $2; $$ = $1; }
+               ;
+
+space          : SPACE LCB spaceconstraints RCB {
+                       $$ = $3;
+               }
+               ;
+
+shape          : SHAPEOF LB ID RB
+               ;
+
+box            : BOX LB expression COMMA expression COMMA expression RB TO
+                  LB expression COMMA expression COMMA expression RB {
+                       $$.start.x = $3;
+                       $$.start.y = $5;
+                       $$.start.z = $7;
+                       $$.end.x = $11;
+                       $$.end.y = $13;
+                       $$.end.z = $15;
+               }
+               ;
+
+geometry_item  : /* empty */
+               | shape
+               | box { $$.box = $1; }
+               ;
+
+geometry       : GEOMETRY LCB geometry_item RCB {
+                       $$ = $3;
+               }
+               ;
+
+material_use   : MATERIAL LB ID RB {
+                       $$ = g_hash_table_lookup(pc->target->materials, $3);
+                       if ($$ == NULL) {
+                               snprintf(pc->error_buf, 4000, "No definition for material '%s'\n", $3);
+                               obthemeerror(pc, pc->error_buf);
+                               return 1;
+                       }
+               }
+               ;
+
+styleitem      : decor
+               ;
+
+styleitems     : /* empty */ { $$.tree = NULL; $$.name = NULL; }
+               | styleitems styleitem {
+                       struct decor *out = malloc(sizeof(struct decor));
+                       *out = $2;
+                       $1.tree = g_slist_prepend($1.tree, out);
+                       $$ = $1;
+               }
+               ;
+
+styles         : /* empty */ { $$ = g_hash_table_new(g_str_hash, g_str_equal); }
+               | styles style {
+                       struct style *out = malloc(sizeof(struct style));
+                       *out = $2;
+                       $$ = $1;
+                       g_hash_table_insert($1, out->name, out);
+               }
+               ;
+
+style          : STYLE ID LCB styleitems RCB {
+                       $$ = $4;
+                       $$.name = $2;
+               }
+               ;
+
+decoritems     : /* empty */ {
+                       memset(&$$, 0, sizeof($$));
+//                     $$.space.up.y = -1;  XXX need a sane default!
+                       $$.cursor = OB_CURSOR_NONE;
+                       $$.space.anchor.x.op = OB_THEME_EQL;
+                       $$.space.anchor.x.v.number = 1;
+                       $$.space.anchor.y.op = OB_THEME_EQL;
+                       $$.space.anchor.y.v.number = 1;
+                       $$.space.anchor.z.op = OB_THEME_EQL;
+                       $$.space.anchor.z.v.number = 1;
+                       $$.space.up.x.op = OB_THEME_EQL;
+                       $$.space.up.x.v.number = 1;
+                       $$.space.up.y.op = OB_THEME_EQL;
+                       $$.space.up.y.v.number = 1;
+                       $$.space.up.z.op = OB_THEME_EQL;
+                       $$.space.up.z.v.number = 1;
+                       $$.texture.present = 0;
+                       $$.material = NULL;
+               }
+               | 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 { $1.material = $2; $$ = $1; }
+               | decoritems geometry { $1.geometry = $2; $$ = $1;}
+               | decoritems texture { $1.texture = $2; $$ = $1; } //XXX multitexture?
+               | decoritems context { $1.context = $2; $$ = $1; }
+               | decoritems cursor { $1.cursor = $2; $$ = $1; }
+               ;
+
+               ;
+
+decor          : DECOR ID LCB decoritems RCB {
+                       $$ = $4;
+                       $$.name = $2;
+               }
+               ;
+
+image          : IMAGE LB STRING RB {
+                       $$.present = 1;
+                       $$.internal = 0;
+                       $$.name = $3;
+               }
+               | IMAGE LB ID RB {
+                       $$.present = 1;
+                       $$.internal = 1;
+                       $$.name = $3;
+               }
+               ;
+
+texitems       : image { $$ = $1; }
+               ;
+
+texture                : TEXTURE LCB texitems RCB { $$ = $3; }
+               ;
+
+context                : CONTEXT LB ID RB {
+                       ObFrameContext frc;
+                       frc = context_from_string($3);
+                       if (frc == -1) {
+                               snprintf(pc->error_buf, 4000, "Illegal context '%s'\n", $3);
+                               obthemeerror(pc, pc->error_buf);
+                               return 1;
+                       }
+               }
+               ;
+
+cursor         : CURSOR LB NORTH RB { $$ = OB_CURSOR_NORTH; }
+               | CURSOR LB NORTHEAST RB { $$ = OB_CURSOR_NORTHEAST; }
+               | CURSOR LB EAST RB { $$ = OB_CURSOR_EAST; }
+               | CURSOR LB SOUTHEAST RB { $$ = OB_CURSOR_SOUTHEAST; }
+               | CURSOR LB SOUTH RB { $$ = OB_CURSOR_SOUTH; }
+               | CURSOR LB SOUTHWEST RB { $$ = OB_CURSOR_SOUTHWEST; }
+               | CURSOR LB WEST RB { $$ = OB_CURSOR_WEST; }
+               | CURSOR LB NORTHWEST RB { $$ = OB_CURSOR_NORTHWEST; }
+//             | CURSOR LB NONE RB { $$ = OB_CURSOR_NORTHEAST; }
+               | CURSOR LB UNCHANGED RB { $$ = OB_CURSOR_NONE; }
+               ;
+gradient       : GRADIENT LB ID RB {
+                       $$ = gradient_from_string($3);
+                       if ($$ == -1) {
+                               snprintf(pc->error_buf, 4000, "No gradient named '%s'\n", $3);
+                               obthemeerror(pc, pc->error_buf);
+                               return 1;
+                       }
+               }
+               ;
+
+variable       : ID DOT ID { $$.base = $1; $$.member = $3; }
+               | ID        { $$.base = $1; $$.member = NULL; }
+               | NUMBER    { $$.base = NULL; $$.number = $1; }
+               ;
+
+expression     : MINUS expression {
+                       $$.op = OB_THEME_INV;
+                       $$.a = exdup($2);
+               }
+               | expression STAR expression {
+                       $$.op = OB_THEME_MUL;
+                       $$.a = exdup($1);
+                       $$.b = exdup($3);
+               }
+               | expression SLASH expression {
+                       $$.op = OB_THEME_DIV;
+                       $$.a = exdup($1);
+                       $$.b = exdup($3);
+               }
+               | expression PLUS expression {
+                       $$.op = OB_THEME_ADD;
+                       $$.a = exdup($1);
+                       $$.b = exdup($3);
+               }
+               | expression MINUS expression {
+                       $$.op = OB_THEME_SUB;
+                       $$.a = exdup($1);
+                       $$.b = exdup($3);
+               }
+               | variable {
+                       $$.op = OB_THEME_EQL;
+                       $$.v = $1;
+               }
+               | LB expression RB { $$ = $2; }
+               ;
+
+%%
diff --git a/obtheme/theme.obtheme b/obtheme/theme.obtheme
new file mode 100644 (file)
index 0000000..6946cec
--- /dev/null
@@ -0,0 +1,106 @@
+//#include materials.obtheme
+
+//version(1)
+
+material flat {
+       opacity(1)
+}
+
+material grad {
+       opacity(1)
+       gradient(split)
+}
+
+material none {
+}
+
+theme awesome {
+       style regular_window {
+               decor clientwindow {
+                       space {
+                               anchor(0, 0, 0)
+                               up(0, 1, 0)
+                       }
+                       geometry {
+//                             shapeof(client)
+                               box (0, 0, 0) to (client.width, client.height, 0)
+                       }
+                       material(flat)
+                       texture {
+                               image(client)
+                       }
+
+                       decor titlebar {
+                               context(titlebar)
+                               space {
+                                       anchor(0, -font.height + 5, 0)
+                               }
+                               geometry {
+                                       box (0, 0, 0) to (client.width, font.height + 5, 0)
+                               }
+                               material(flat)
+
+                               decor title {
+                                       geometry {
+                                               box (client.width / 4, font.height + 5, 0) to (3*client.width / 4, font.height + 5, 0)
+//                                             text(client.title)
+//                                             justify(center)
+                                       }
+                               }
+                               decor minbutton {
+                                       space {
+                                               anchor(client.width - 20, -10, 0)
+                                       }
+                                       geometry {
+                                               box (0, 0, 0) to (10, 10, 0)
+                                       }
+                                       material(none)
+                                       context(minimize)
+                               }
+                               decor maxbutton {
+                                       space {
+                                               anchor(client.width - 40, -10, 0)
+                                       }
+                                       geometry {
+                                               box (0, 0, 0) to (10, 10, 0)
+                                       }
+                                       material(none)
+                                       context(maximize)
+                               }
+                       }
+                       decor handle {
+                               geometry {
+                                       box (0, 0, 0) to (client.width, 10, 0)
+                               }
+                               space {
+                                       anchor(0, client.height, 0)
+                                       up(0, 1, 0)
+                               }
+                               decor leftgrip {
+                                       space {
+                                               anchor(0, client.height, 0)
+                                               up(0, 1, 0)
+                                       }
+                                       geometry {
+                                               box (0, 0, 0) to (30, 10, 0)
+                                       }
+                                       context(blcorner)
+                                       cursor(SOUTHWEST)
+                               }
+                               decor rightgrip {
+                                       space {
+                                               anchor(client.width - 30, client.height, 0)
+                                               up(0, 1, 0)
+                                       }
+                                       geometry {
+                                               box (0, 0, 0) to (30, 10, 0)
+                                       }
+                                       context(brcorner)
+                                       cursor(SOUTHEAST)
+                               }
+                               context(bottom)
+                               cursor(SOUTH)
+                       }
+               }
+       }
+}