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}
|
||||
|
||||
AM_YFLAGS = -d
|
||||
|
||||
noinst_LIBRARIES =
|
||||
include $(top_srcdir)/libgwater-xcb-nolibtool.mk
|
||||
|
||||
|
@ -27,6 +29,7 @@ rofi_SOURCES=\
|
|||
source/helper.c\
|
||||
source/timings.c\
|
||||
source/history.c\
|
||||
source/theme.c\
|
||||
source/widgets/box.c\
|
||||
source/widgets/widget.c\
|
||||
source/widgets/textbox.c\
|
||||
|
@ -43,6 +46,8 @@ rofi_SOURCES=\
|
|||
source/dialogs/window.c\
|
||||
source/dialogs/script.c\
|
||||
source/dialogs/help-keys.c\
|
||||
lexer/theme-parser.y\
|
||||
lexer/theme-lexer.l\
|
||||
include/xcb.h\
|
||||
include/xcb-internal.h\
|
||||
include/rofi.h\
|
||||
|
@ -55,6 +60,7 @@ rofi_SOURCES=\
|
|||
include/helper.h\
|
||||
include/timings.h\
|
||||
include/history.h\
|
||||
include/theme.h\
|
||||
include/widgets/box.h\
|
||||
include/widgets/widget.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_HEADER([config.h])
|
||||
|
||||
|
||||
AC_PROG_LEX
|
||||
AC_PROG_YACC
|
||||
|
||||
dnl ---------------------------------------------------------------------
|
||||
dnl Setup automake to be silent and in foreign mode.
|
||||
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 "theme.h"
|
||||
|
||||
// Pidfile.
|
||||
char *pidfile = 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.
|
||||
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.
|
||||
// catch help request
|
||||
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