mirror of
https://github.com/davatorium/rofi.git
synced 2024-11-18 13:54:36 -05:00
Add lex/bison parser for theme.
This commit is contained in:
parent
ea28bcdc74
commit
d18f037d1c
8 changed files with 455 additions and 0 deletions
|
@ -3,6 +3,8 @@ AUTOMAKE_OPTIONS = 1.11.3
|
||||||
|
|
||||||
ACLOCAL_AMFLAGS = -I libgwater ${ACLOCAL_FLAGS}
|
ACLOCAL_AMFLAGS = -I libgwater ${ACLOCAL_FLAGS}
|
||||||
|
|
||||||
|
AM_YFLAGS = -d
|
||||||
|
|
||||||
noinst_LIBRARIES =
|
noinst_LIBRARIES =
|
||||||
include $(top_srcdir)/libgwater-xcb-nolibtool.mk
|
include $(top_srcdir)/libgwater-xcb-nolibtool.mk
|
||||||
|
|
||||||
|
@ -27,6 +29,7 @@ rofi_SOURCES=\
|
||||||
source/helper.c\
|
source/helper.c\
|
||||||
source/timings.c\
|
source/timings.c\
|
||||||
source/history.c\
|
source/history.c\
|
||||||
|
source/theme.c\
|
||||||
source/widgets/box.c\
|
source/widgets/box.c\
|
||||||
source/widgets/widget.c\
|
source/widgets/widget.c\
|
||||||
source/widgets/textbox.c\
|
source/widgets/textbox.c\
|
||||||
|
@ -43,6 +46,8 @@ rofi_SOURCES=\
|
||||||
source/dialogs/window.c\
|
source/dialogs/window.c\
|
||||||
source/dialogs/script.c\
|
source/dialogs/script.c\
|
||||||
source/dialogs/help-keys.c\
|
source/dialogs/help-keys.c\
|
||||||
|
lexer/theme-parser.y\
|
||||||
|
lexer/theme-lexer.l\
|
||||||
include/xcb.h\
|
include/xcb.h\
|
||||||
include/xcb-internal.h\
|
include/xcb-internal.h\
|
||||||
include/rofi.h\
|
include/rofi.h\
|
||||||
|
@ -55,6 +60,7 @@ rofi_SOURCES=\
|
||||||
include/helper.h\
|
include/helper.h\
|
||||||
include/timings.h\
|
include/timings.h\
|
||||||
include/history.h\
|
include/history.h\
|
||||||
|
include/theme.h\
|
||||||
include/widgets/box.h\
|
include/widgets/box.h\
|
||||||
include/widgets/widget.h\
|
include/widgets/widget.h\
|
||||||
include/widgets/widget-internal.h\
|
include/widgets/widget-internal.h\
|
||||||
|
|
|
@ -3,6 +3,10 @@ AC_INIT([rofi], [1.2.0], [https://github.com/DaveDavenport/rofi/],[],[https://fo
|
||||||
AC_CONFIG_SRCDIR([source/rofi.c])
|
AC_CONFIG_SRCDIR([source/rofi.c])
|
||||||
AC_CONFIG_HEADER([config.h])
|
AC_CONFIG_HEADER([config.h])
|
||||||
|
|
||||||
|
|
||||||
|
AC_PROG_LEX
|
||||||
|
AC_PROG_YACC
|
||||||
|
|
||||||
dnl ---------------------------------------------------------------------
|
dnl ---------------------------------------------------------------------
|
||||||
dnl Setup automake to be silent and in foreign mode.
|
dnl Setup automake to be silent and in foreign mode.
|
||||||
dnl We want xz distribution
|
dnl We want xz distribution
|
||||||
|
|
67
doc/themer.md
Normal file
67
doc/themer.md
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
|
||||||
|
Each widget has:
|
||||||
|
|
||||||
|
* Class: Type of widget.
|
||||||
|
|
||||||
|
Example: textbox, scrollbar, separator
|
||||||
|
|
||||||
|
Class are prefixed with a `@`
|
||||||
|
|
||||||
|
* Name: Internal name of the widget.
|
||||||
|
|
||||||
|
Sub-widgets are {Parent}.{Child}.
|
||||||
|
|
||||||
|
Example: Listview, Listview.Even, Listview.Uneven, Listview.Scrollbar
|
||||||
|
|
||||||
|
Names are prefixed with a `#`
|
||||||
|
|
||||||
|
* State: State of widget
|
||||||
|
|
||||||
|
Optional flag(s) indicating state.
|
||||||
|
Multiple flags can be set.
|
||||||
|
|
||||||
|
Example: Highlight Active Urgent
|
||||||
|
|
||||||
|
States are prefixed with a `!`
|
||||||
|
|
||||||
|
So to set color of Even entry in listview that is highlighted and urgent:
|
||||||
|
|
||||||
|
`@textbox #Listview.Even !Urgent !Highlight`
|
||||||
|
|
||||||
|
Or to indicate all textboxes
|
||||||
|
|
||||||
|
`@textbox !Highlight`
|
||||||
|
|
||||||
|
Class is manditory, name is optional. Name is split on .s.
|
||||||
|
|
||||||
|
|
||||||
|
# Internally:
|
||||||
|
|
||||||
|
The theme is represented like a tree:
|
||||||
|
|
||||||
|
class --> name --> name --> state -> state
|
||||||
|
|
||||||
|
The states are sorted alphabetically
|
||||||
|
|
||||||
|
So `@textbox #Listview.Even !Urgent !Highlight` becomes:
|
||||||
|
|
||||||
|
textbox->listview->even -> highlight -> urgent.
|
||||||
|
|
||||||
|
When searching for entries the tree is traversed until deepest node is found.
|
||||||
|
Missing states are skipped.
|
||||||
|
Then from there properties are searched going up again.
|
||||||
|
|
||||||
|
Properties are in the form of:
|
||||||
|
|
||||||
|
`name: value`
|
||||||
|
Each property ends with `;`
|
||||||
|
Each property has a type. (Boolean, Integer, String, Color)
|
||||||
|
|
||||||
|
A block is enclosed by `{}`
|
||||||
|
|
||||||
|
```
|
||||||
|
@textbox #Listview.Even !Urgent !Highlight {
|
||||||
|
padding: 3;
|
||||||
|
foreground: #aarrggbb;
|
||||||
|
}
|
||||||
|
```
|
46
include/theme.h
Normal file
46
include/theme.h
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#ifndef THEME_H
|
||||||
|
#define THEME_H
|
||||||
|
#include <glib.h>
|
||||||
|
typedef enum {
|
||||||
|
P_INTEGER,
|
||||||
|
P_FLOAT,
|
||||||
|
P_STRING,
|
||||||
|
P_BOOLEAN,
|
||||||
|
P_COLOR
|
||||||
|
} PropertyType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *name;
|
||||||
|
PropertyType type;
|
||||||
|
union {
|
||||||
|
int i;
|
||||||
|
double f;
|
||||||
|
char *s;
|
||||||
|
int b;
|
||||||
|
unsigned int color;
|
||||||
|
} value;
|
||||||
|
} Property;
|
||||||
|
|
||||||
|
typedef struct _Widget {
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
unsigned int num_widgets;
|
||||||
|
struct _Widget **widgets;
|
||||||
|
|
||||||
|
GHashTable *properties;
|
||||||
|
|
||||||
|
struct _Widget *parent;
|
||||||
|
} Widget;
|
||||||
|
|
||||||
|
extern Widget *rofi_theme;
|
||||||
|
|
||||||
|
Widget *rofi_theme_find_or_create_class ( Widget *base, const char *class );
|
||||||
|
|
||||||
|
|
||||||
|
void rofi_theme_print ( Widget *widget );
|
||||||
|
|
||||||
|
Property *rofi_theme_property_create ( PropertyType type );
|
||||||
|
void rofi_theme_property_free ( Property *p );
|
||||||
|
void rofi_theme_free ( Widget * );
|
||||||
|
void rofi_theme_parse_file ( const char *file );
|
||||||
|
#endif
|
33
lexer/theme-lexer.l
Normal file
33
lexer/theme-lexer.l
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
%option noyywrap
|
||||||
|
|
||||||
|
%{
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "lexer/theme-parser.tab.h"
|
||||||
|
int yylex(void);
|
||||||
|
#define YY_DECL int yylex()
|
||||||
|
|
||||||
|
%}
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
"@" { return CLASS;}
|
||||||
|
"\{" { return BOPEN;}
|
||||||
|
"\}" { return BCLOSE;}
|
||||||
|
":" { return PSEP; }
|
||||||
|
";" { return PCLOSE;}
|
||||||
|
"." { return NSEP; }
|
||||||
|
[ \t] ; // ignore all whitespace
|
||||||
|
[0-9]+\.[0-9]+ { yylval.fval = g_ascii_strtod(yytext, NULL); return T_FLOAT;}
|
||||||
|
[0-9]+ { yylval.ival = (int)g_ascii_strtoll(yytext, NULL, 10); return T_INT;}
|
||||||
|
(true|false) { yylval.bval= g_strcmp0(yytext, "true") == 0; return T_BOOLEAN;}
|
||||||
|
[a-zA-Z0-9]+ { yylval.sval = g_strdup(yytext); return N_STRING;}
|
||||||
|
\"[a-zA-Z0-9]+\" { yylval.sval = g_strdup(yytext); return T_STRING;}
|
||||||
|
#[0-9A-Fa-f]+ { yylval.colorval = (unsigned int)strtoull ( &yytext[1], NULL, 16); return T_COLOR;}
|
||||||
|
[\r\n]+ ;
|
||||||
|
|
||||||
|
<*><<EOF>> {
|
||||||
|
yyterminate();
|
||||||
|
}
|
||||||
|
%%
|
163
lexer/theme-parser.y
Normal file
163
lexer/theme-parser.y
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
%locations
|
||||||
|
%debug
|
||||||
|
%error-verbose
|
||||||
|
|
||||||
|
%code requires {
|
||||||
|
#include "theme.h"
|
||||||
|
}
|
||||||
|
%{
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void yyerror(const char* s);
|
||||||
|
int yylex (void );
|
||||||
|
|
||||||
|
#include "theme.h"
|
||||||
|
Widget *rofi_theme = NULL;
|
||||||
|
%}
|
||||||
|
|
||||||
|
%union {
|
||||||
|
int ival;
|
||||||
|
double fval;
|
||||||
|
char *sval;
|
||||||
|
int bval;
|
||||||
|
unsigned int colorval;
|
||||||
|
Widget *theme;
|
||||||
|
GList *name_path;
|
||||||
|
Property *property;
|
||||||
|
GHashTable *property_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
%token <ival> T_INT
|
||||||
|
%token <fval> T_FLOAT
|
||||||
|
%token <sval> T_STRING
|
||||||
|
%token <sval> N_STRING
|
||||||
|
%token <bval> T_BOOLEAN
|
||||||
|
%token <colorval> T_COLOR
|
||||||
|
|
||||||
|
%token CLASS "class";
|
||||||
|
%token BOPEN "bracket open";
|
||||||
|
%token BCLOSE "bracket close";
|
||||||
|
%token PSEP "property separator";
|
||||||
|
%token PCLOSE "property close";
|
||||||
|
%token NSEP "Name separator";
|
||||||
|
|
||||||
|
%type <sval> class
|
||||||
|
%type <sval> entry
|
||||||
|
%type <sval> pvalue
|
||||||
|
%type <theme> entries
|
||||||
|
%type <theme> start
|
||||||
|
%type <name_path> name_path
|
||||||
|
%type <property> property
|
||||||
|
%type <property_list> property_list
|
||||||
|
%type <property_list> properties
|
||||||
|
%type <property_list> optional_properties
|
||||||
|
%start start
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
start:
|
||||||
|
optional_properties
|
||||||
|
entries {
|
||||||
|
$$ = $2;
|
||||||
|
if ( $1 != NULL ) {
|
||||||
|
$$->properties = $1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
entries:
|
||||||
|
%empty {
|
||||||
|
// There is always a base widget.
|
||||||
|
$$ = rofi_theme = (Widget*)g_malloc0 (sizeof(Widget));
|
||||||
|
rofi_theme->name = g_strdup ( "Window" );
|
||||||
|
}
|
||||||
|
| entries entry { $$ = $1; }
|
||||||
|
;
|
||||||
|
|
||||||
|
entry:
|
||||||
|
class
|
||||||
|
name_path
|
||||||
|
properties
|
||||||
|
{
|
||||||
|
Widget *widget = rofi_theme_find_or_create_class ( rofi_theme , $1 );
|
||||||
|
g_free($1);
|
||||||
|
for ( GList *iter = g_list_first ( $2 ); iter ; iter = g_list_next ( iter ) ) {
|
||||||
|
widget = rofi_theme_find_or_create_class ( widget, iter->data );
|
||||||
|
}
|
||||||
|
g_list_foreach ( $2, (GFunc)g_free , NULL );
|
||||||
|
g_list_free ( $2 );
|
||||||
|
if ( widget->properties != NULL ) {
|
||||||
|
fprintf(stderr, "Properties already set on this widget.\n");
|
||||||
|
exit ( EXIT_FAILURE );
|
||||||
|
}
|
||||||
|
widget->properties = $3;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* properties
|
||||||
|
*/
|
||||||
|
optional_properties
|
||||||
|
: %empty { $$ = NULL; }
|
||||||
|
| property_list { $$ = $1; }
|
||||||
|
;
|
||||||
|
properties: BOPEN property_list BCLOSE { $$ = $2;}
|
||||||
|
| BOPEN BCLOSE { $$ = NULL; }
|
||||||
|
| %empty { $$ = NULL; }
|
||||||
|
;
|
||||||
|
|
||||||
|
property_list:
|
||||||
|
property {
|
||||||
|
$$ = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, (GDestroyNotify)rofi_theme_property_free );
|
||||||
|
g_hash_table_replace ( $$, $1->name, $1 );
|
||||||
|
}
|
||||||
|
| property_list property {
|
||||||
|
// Old will be free'ed, and key/value will be replaced.
|
||||||
|
g_hash_table_replace ( $$, $2->name, $2 );
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
property
|
||||||
|
: pvalue PSEP T_INT PCLOSE {
|
||||||
|
$$ = rofi_theme_property_create ( P_INTEGER );
|
||||||
|
$$->name = $1;
|
||||||
|
$$->value.i = $3;
|
||||||
|
}
|
||||||
|
| pvalue PSEP T_FLOAT PCLOSE {
|
||||||
|
$$ = rofi_theme_property_create ( P_FLOAT );
|
||||||
|
$$->name = $1;
|
||||||
|
$$->value.f = $3;
|
||||||
|
}
|
||||||
|
| pvalue PSEP T_COLOR PCLOSE {
|
||||||
|
$$ = rofi_theme_property_create ( P_COLOR );
|
||||||
|
$$->name = $1;
|
||||||
|
$$->value.color = $3;
|
||||||
|
}
|
||||||
|
| pvalue PSEP T_STRING PCLOSE {
|
||||||
|
$$ = rofi_theme_property_create ( P_STRING );
|
||||||
|
$$->name = $1;
|
||||||
|
$$->value.s = $3;
|
||||||
|
}
|
||||||
|
| pvalue PSEP T_BOOLEAN PCLOSE {
|
||||||
|
$$ = rofi_theme_property_create ( P_BOOLEAN );
|
||||||
|
$$->name = $1;
|
||||||
|
$$->value.b = $3;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
pvalue: N_STRING { $$ = $1; }
|
||||||
|
|
||||||
|
class:
|
||||||
|
CLASS N_STRING { $$ = $2; }
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
name_path:
|
||||||
|
%empty { $$ = NULL; }
|
||||||
|
| N_STRING { $$ = g_list_append ( NULL, $1 );}
|
||||||
|
| name_path NSEP N_STRING { $$ = g_list_append ( $1, $3);}
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
|
@ -64,6 +64,8 @@
|
||||||
|
|
||||||
#include "gitconfig.h"
|
#include "gitconfig.h"
|
||||||
|
|
||||||
|
#include "theme.h"
|
||||||
|
|
||||||
// Pidfile.
|
// Pidfile.
|
||||||
char *pidfile = NULL;
|
char *pidfile = NULL;
|
||||||
const char *cache_dir = NULL;
|
const char *cache_dir = NULL;
|
||||||
|
@ -896,6 +898,12 @@ int main ( int argc, char *argv[] )
|
||||||
// Parse command line for settings, independent of other -no-config.
|
// Parse command line for settings, independent of other -no-config.
|
||||||
config_parse_cmd_options_dynamic ( );
|
config_parse_cmd_options_dynamic ( );
|
||||||
|
|
||||||
|
char *theme = NULL;
|
||||||
|
if ( find_arg_str ( "-theme", &theme ) > 0 ){
|
||||||
|
rofi_theme_parse_file ( theme );
|
||||||
|
rofi_theme_print ( rofi_theme );
|
||||||
|
}
|
||||||
|
|
||||||
// Dump.
|
// Dump.
|
||||||
// catch help request
|
// catch help request
|
||||||
if ( find_arg ( "-h" ) >= 0 || find_arg ( "-help" ) >= 0 || find_arg ( "--help" ) >= 0 ) {
|
if ( find_arg ( "-h" ) >= 0 || find_arg ( "-help" ) >= 0 || find_arg ( "--help" ) >= 0 ) {
|
||||||
|
|
128
source/theme.c
Normal file
128
source/theme.c
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "theme.h"
|
||||||
|
|
||||||
|
void yyerror ( const char *);
|
||||||
|
Widget *rofi_theme_find_or_create_class ( Widget *base, const char *class )
|
||||||
|
{
|
||||||
|
for ( unsigned int i = 0; i < base->num_widgets;i++){
|
||||||
|
if ( g_strcmp0(base->widgets[i]->name, class) == 0 ){
|
||||||
|
return base->widgets[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base->widgets = g_realloc ( base->widgets, sizeof(Widget*)*(base->num_widgets+1));
|
||||||
|
base->widgets[base->num_widgets] = g_malloc0(sizeof(Widget));
|
||||||
|
Widget *retv = base->widgets[base->num_widgets];
|
||||||
|
retv->parent = base;
|
||||||
|
retv->name = g_strdup(class);
|
||||||
|
base->num_widgets++;
|
||||||
|
return retv;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Properties
|
||||||
|
*/
|
||||||
|
Property *rofi_theme_property_create ( PropertyType type )
|
||||||
|
{
|
||||||
|
Property *retv = g_malloc0 ( sizeof(Property) );
|
||||||
|
retv->type = type;
|
||||||
|
return retv;
|
||||||
|
}
|
||||||
|
void rofi_theme_property_free ( Property *p )
|
||||||
|
{
|
||||||
|
if ( p == NULL ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_free ( p->name );
|
||||||
|
if ( p->type == P_STRING ) {
|
||||||
|
g_free ( p->value.s );
|
||||||
|
}
|
||||||
|
g_free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rofi_theme_free ( Widget *widget )
|
||||||
|
{
|
||||||
|
if ( widget == NULL ){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( widget->properties ) {
|
||||||
|
g_hash_table_destroy ( widget->properties );
|
||||||
|
}
|
||||||
|
for ( unsigned int i = 0; i < widget->num_widgets; i++ ){
|
||||||
|
rofi_theme_free ( widget->widgets[i] );
|
||||||
|
}
|
||||||
|
g_free ( widget->widgets );
|
||||||
|
g_free ( widget->name );
|
||||||
|
g_free ( widget );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* print
|
||||||
|
*/
|
||||||
|
static void rofi_theme_print_property_index ( int depth, Property *p )
|
||||||
|
{
|
||||||
|
printf("%*s %s: ", depth, "", p->name );
|
||||||
|
switch ( p->type )
|
||||||
|
{
|
||||||
|
case P_STRING:
|
||||||
|
printf("\"%s\"", p->value.s);
|
||||||
|
break;
|
||||||
|
case P_INTEGER:
|
||||||
|
printf("%d", p->value.i);
|
||||||
|
break;
|
||||||
|
case P_FLOAT:
|
||||||
|
printf("%.2f", p->value.f);
|
||||||
|
break;
|
||||||
|
case P_BOOLEAN:
|
||||||
|
printf("%s", p->value.b?"true":"false");
|
||||||
|
break;
|
||||||
|
case P_COLOR:
|
||||||
|
printf("#%08X", p->value.color);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
putchar ( '\n' );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rofi_theme_print_index ( int depth, Widget *widget )
|
||||||
|
{
|
||||||
|
printf ( "%*sName: %s \n", depth, "", widget->name );
|
||||||
|
|
||||||
|
GHashTableIter iter;
|
||||||
|
gpointer key, value;
|
||||||
|
if ( widget->properties ){
|
||||||
|
g_hash_table_iter_init (&iter, widget->properties);
|
||||||
|
while (g_hash_table_iter_next (&iter, &key, &value))
|
||||||
|
{
|
||||||
|
Property *p = (Property*)value;
|
||||||
|
rofi_theme_print_property_index ( depth, p );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ( unsigned int i = 0; i < widget->num_widgets;i++){
|
||||||
|
rofi_theme_print_index ( depth+2, widget->widgets[i] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void rofi_theme_print ( Widget *widget )
|
||||||
|
{
|
||||||
|
rofi_theme_print_index ( 0, widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern int yyparse();
|
||||||
|
extern FILE* yyin;
|
||||||
|
extern Widget *rofi_theme;
|
||||||
|
|
||||||
|
void rofi_theme_parse_file ( const char *file )
|
||||||
|
{
|
||||||
|
yyin = fopen ( file, "rb");
|
||||||
|
if ( yyin == NULL ){
|
||||||
|
fprintf(stderr, "Failed to open file: '%s'\n", strerror ( errno ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while ( yyparse() );
|
||||||
|
}
|
||||||
|
|
||||||
|
void yyerror(const char* s) {
|
||||||
|
fprintf(stderr, "Parse error: %s\n", s);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
Loading…
Reference in a new issue