summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Davenport <DaveDavenport@users.noreply.github.com>2019-09-20 15:05:36 +0200
committerGitHub <noreply@github.com>2019-09-20 15:05:36 +0200
commit7c613f6a4ca4354165e2cab987d0db1e7ce32c5a (patch)
tree7cede72023c550462342a9eb13123580a6e5ddaa
parentf28cf0207e7641890617e8b946977dc204e05ce2 (diff)
Issue893: Add support for @media in the theme format. (#1015)
* [Lexer] Add support for @media. Issue: #893 * [Theme] @media limit to px. * [Theme@Media] add *{} support. * [Theme@Media] Add support for monitor-id media. * [Theme@Media] Code cleanup. * [Theme@Media] Add min/max-aspect-ratio. * [Theme@Media] Remove some debug output Fixes: #893
-rw-r--r--include/theme.h27
-rw-r--r--lexer/theme-lexer.l64
-rw-r--r--lexer/theme-parser.y102
-rw-r--r--source/rofi.c3
-rw-r--r--source/theme.c128
5 files changed, 319 insertions, 5 deletions
diff --git a/include/theme.h b/include/theme.h
index 0a7c5fc3..2196887e 100644
--- a/include/theme.h
+++ b/include/theme.h
@@ -32,6 +32,24 @@
#include <widgets/widget.h>
#include "rofi-types.h"
+
+typedef enum {
+ THEME_MEDIA_TYPE_MIN_WIDTH,
+ THEME_MEDIA_TYPE_MAX_WIDTH,
+ THEME_MEDIA_TYPE_MIN_HEIGHT,
+ THEME_MEDIA_TYPE_MAX_HEIGHT,
+ THEME_MEDIA_TYPE_MON_ID,
+ THEME_MEDIA_TYPE_MIN_ASPECT_RATIO,
+ THEME_MEDIA_TYPE_MAX_ASPECT_RATIO,
+ THEME_MEDIA_TYPE_INVALID,
+} ThemeMediaType;
+
+
+typedef struct ThemeMedia {
+ ThemeMediaType type;
+ double value;
+} ThemeMedia;
+
/**
* ThemeWidget.
*/
@@ -43,6 +61,8 @@ typedef struct ThemeWidget
unsigned int num_widgets;
struct ThemeWidget **widgets;
+ ThemeMedia *media;
+
GHashTable *properties;
struct ThemeWidget *parent;
@@ -323,4 +343,11 @@ char *helper_get_theme_path ( const char *file );
* @returns full path to file.
*/
char * rofi_theme_parse_prepare_file ( const char *file, const char *parent_file );
+
+/**
+ * Process conditionals.
+ */
+void rofi_theme_parse_process_conditionals ( void );
+void rofi_theme_parse_merge_widgets ( ThemeWidget *parent, ThemeWidget *child );
+ThemeMediaType rofi_theme_parse_media_type ( const char *type );
#endif
diff --git a/lexer/theme-lexer.l b/lexer/theme-lexer.l
index 4ebeff7f..50a2c4e7 100644
--- a/lexer/theme-lexer.l
+++ b/lexer/theme-lexer.l
@@ -239,6 +239,8 @@ C_COMMENT_OPEN "/*"
INCLUDE "@import"
THEME "@theme"
+MEDIA "@media"
+
CONFIGURATION (?i:configuration)
%x INCLUDE
@@ -250,6 +252,8 @@ CONFIGURATION (?i:configuration)
%x NAMESTR
%x SECTION
%x DEFAULTS
+%x MEDIA
+%x MEDIA_CONTENT
%%
%{
@@ -424,13 +428,13 @@ if ( queue == NULL ){
/* After Namestr/Classstr we want to go to state str, then to { */
<INITIAL,SECTION>{WHITESPACE}+ ; // ignore all whitespace
-<PROPERTIES,PROPERTIES_ENV,PROPERTIES_VAR_DEFAULT,PROPERTIES_LIST,PROPERTIES_VAR>{WHITESPACE}+ ; // ignore all whitespace
+<PROPERTIES,PROPERTIES_ENV,PROPERTIES_VAR_DEFAULT,PROPERTIES_LIST,PROPERTIES_VAR,MEDIA_CONTENT>{WHITESPACE}+ ; // ignore all whitespace
<SECTION>":" { g_queue_push_head ( queue, GINT_TO_POINTER (YY_START) ); BEGIN(PROPERTIES); return T_PSEP; }
<PROPERTIES>";" { BEGIN(GPOINTER_TO_INT ( g_queue_pop_head ( queue ))); return T_PCLOSE;}
<PROPERTIES,PROPERTIES_ENV,PROPERTIES_VAR_DEFAULT>(true|false) { yylval->bval= g_strcmp0(yytext, "true") == 0; return T_BOOLEAN;}
-<PROPERTIES,PROPERTIES_ENV,PROPERTIES_VAR_DEFAULT>{PNNUMBER}\.{NUMBER}+ { yylval->fval = g_ascii_strtod(yytext, NULL); return T_DOUBLE;}
-<PROPERTIES,PROPERTIES_ENV,PROPERTIES_VAR_DEFAULT>{PNNUMBER} { yylval->ival = (int)g_ascii_strtoll(yytext, NULL, 10); return T_INT;}
+<PROPERTIES,PROPERTIES_ENV,PROPERTIES_VAR_DEFAULT,MEDIA_CONTENT>{PNNUMBER}\.{NUMBER}+ { yylval->fval = g_ascii_strtod(yytext, NULL); return T_DOUBLE;}
+<PROPERTIES,PROPERTIES_ENV,PROPERTIES_VAR_DEFAULT,MEDIA_CONTENT>{PNNUMBER} { yylval->ival = (int)g_ascii_strtoll(yytext, NULL, 10); return T_INT;}
<PROPERTIES,PROPERTIES_ENV,PROPERTIES_VAR_DEFAULT>{STRING} { yytext[yyleng-1] = '\0'; yylval->sval = g_strcompress(&yytext[1]); return T_STRING;}
<PROPERTIES,PROPERTIES_ENV,PROPERTIES_VAR_DEFAULT>@{WORD} {
@@ -440,7 +444,7 @@ if ( queue == NULL ){
<PROPERTIES,PROPERTIES_ENV,PROPERTIES_VAR_DEFAULT>{EM} { return T_UNIT_EM; }
<PROPERTIES,PROPERTIES_ENV,PROPERTIES_VAR_DEFAULT>{CH} { return T_UNIT_CH; }
-<PROPERTIES,PROPERTIES_ENV,PROPERTIES_VAR_DEFAULT>{PX} { return T_UNIT_PX; }
+<PROPERTIES,PROPERTIES_ENV,PROPERTIES_VAR_DEFAULT,MEDIA_CONTENT>{PX} { return T_UNIT_PX; }
<PROPERTIES,PROPERTIES_ENV,PROPERTIES_VAR_DEFAULT>{PERCENT} { return T_PERCENT; }
<PROPERTIES,PROPERTIES_ENV,PROPERTIES_VAR_DEFAULT>{LS_SOLID} { return T_SOLID; }
<PROPERTIES,PROPERTIES_ENV,PROPERTIES_VAR_DEFAULT>{LS_DASH} { return T_DASH; }
@@ -619,6 +623,50 @@ if ( queue == NULL ){
yylloc->last_line ++;
};
+
+<INITIAL>{MEDIA} {
+ g_queue_push_head ( queue, GINT_TO_POINTER (YY_START) );
+ BEGIN(MEDIA);
+ return T_MEDIA;
+}
+
+<MEDIA>{S_T_PARENT_LEFT} {
+ g_queue_push_head ( queue, GINT_TO_POINTER (YY_START) );
+ BEGIN(MEDIA_CONTENT);
+ return T_PARENT_LEFT;
+}
+<MEDIA_CONTENT>{WORD} {
+ yylval->sval = g_strdup(yytext);
+ return T_STRING;
+}
+<MEDIA_CONTENT>":" {
+ return T_PSEP;
+}
+<MEDIA_CONTENT>{S_T_PARENT_RIGHT} {
+ int id = GPOINTER_TO_INT(g_queue_pop_head ( queue ));
+ BEGIN(id);
+ return T_PARENT_RIGHT;
+}
+<MEDIA>"\{" {
+ g_queue_push_head ( queue, GINT_TO_POINTER (YY_START) );
+ BEGIN(INITIAL);
+ return T_BOPEN;
+}
+
+<INITIAL>"\}" {
+ g_queue_pop_head ( queue );
+ BEGIN(GPOINTER_TO_INT(g_queue_pop_head ( queue )));
+ return T_BCLOSE;
+}
+<MEDIA>{WHITESPACE}+ ; // ignore all whitespace
+
+
+<MEDIA,MEDIA_CONTENT>. {
+ yytext[yyleng-1] = '\0';
+ fprintf(stderr,"found: |%s|\n", yytext);
+ return T_ERROR;
+}
+
/**
* If we just encounter a word, we assume it is a Widget name.
* This makes include,theme, configuration a reserved keyword.
@@ -630,9 +678,13 @@ if ( queue == NULL ){
return T_NAME_ELEMENT;
}
<INITIAL>. {
+ yytext[yyleng-1] = '\0';
+ fprintf(stderr,"initial found: |%s|\n", yytext);
return T_ERROR;
}
<SECTION>. {
+ yytext[yyleng-1] = '\0';
+ fprintf(stderr,"section found: |%s|\n", yytext);
return T_ERROR_SECTION;
}
<PROPERTIES_LIST,PROPERTIES_VAR>{WORD} {
@@ -646,9 +698,13 @@ if ( queue == NULL ){
}
<PROPERTIES,PROPERTIES_ENV,PROPERTIES_VAR_DEFAULT,PROPERTIES_LIST>. {
+ yytext[yyleng-1] = '\0';
+ fprintf(stderr,"prop found: |%s|\n", yytext);
return T_ERROR_PROPERTY;
}
<NAMESTR>. {
+ yytext[yyleng-1] = '\0';
+ fprintf(stderr,"namestr found: |%s|\n", yytext);
return T_ERROR_NAMESTRING;
}
%%
diff --git a/lexer/theme-parser.y b/lexer/theme-parser.y
index 23b0a5dc..c85a5bbe 100644
--- a/lexer/theme-parser.y
+++ b/lexer/theme-parser.y
@@ -172,6 +172,8 @@ static ThemeColor hwb_to_rgb ( double h, double w, double b)
%token T_POS_NORTH "North"
%token T_POS_SOUTH "South"
+%token T_MEDIA "@media"
+
%token T_NONE "None"
%token T_BOLD "Bold"
%token T_ITALIC "Italic"
@@ -223,8 +225,17 @@ static ThemeColor hwb_to_rgb ( double h, double w, double b)
%token T_INHERIT "Inherit"
+%token T_MEDIA_WIDTH "Width"
+%token T_MEDIA_HEIGHT "Height"
+
+%token T_MEDIA_MIN "Min"
+%token T_MEDIA_MONITOR_ID "Monitor-ID"
+%token T_MEDIA_MAX "Max"
+%token T_MEDIA_SEP "-"
+
%type <sval> t_entry
%type <theme> t_entry_list
+%type <theme> t_media_entry_list
%type <list> t_entry_name_path
%type <list> t_entry_name_path_selectors
%type <property> t_property
@@ -280,6 +291,56 @@ t_entry_list:
}
;
+t_media_entry_list:
+t_name_prefix_optional t_entry_name_path_selectors T_BOPEN t_property_list_optional T_BCLOSE {
+ ThemeWidget *widget = $$ = g_slice_new0 ( ThemeWidget );
+ for ( GList *liter = g_list_first ( $2); liter; liter = g_list_next ( liter ) ) {
+ for ( GList *iter = g_list_first ( (GList*)liter->data ); widget && iter ; iter = g_list_next ( iter ) ) {
+ widget = rofi_theme_find_or_create_name ( widget, iter->data );
+ }
+ g_list_free_full ( (GList*)liter->data, g_free );
+ widget->set = TRUE;
+ rofi_theme_widget_add_properties ( widget, $4);
+ }
+ if ( $4 ) {
+ g_hash_table_destroy ( $4 );
+ }
+ g_list_free ( $2 );
+}
+| T_PDEFAULTS T_BOPEN t_property_list_optional T_BCLOSE {
+ ThemeWidget *widget = $$ = g_slice_new0( ThemeWidget ) ;
+ widget = rofi_theme_find_or_create_name ( widget, "*" );
+ widget->set = TRUE;
+ rofi_theme_widget_add_properties ( widget, $3);
+ if ( $3 ) {
+ g_hash_table_destroy ( $3 );
+ }
+}
+| t_media_entry_list T_PDEFAULTS T_BOPEN t_property_list_optional T_BCLOSE {
+ ThemeWidget *widget = $$ = $1 ;
+ widget = rofi_theme_find_or_create_name ( widget, "*" );
+ widget->set = TRUE;
+ rofi_theme_widget_add_properties ( widget, $4);
+ if ( $4 ) {
+ g_hash_table_destroy ( $4 );
+ }
+}
+| t_media_entry_list t_name_prefix_optional t_entry_name_path_selectors T_BOPEN t_property_list_optional T_BCLOSE {
+ ThemeWidget *widget = $$ = $1 ;
+ for ( GList *liter = g_list_first ( $3); liter; liter = g_list_next ( liter ) ) {
+ for ( GList *iter = g_list_first ( (GList*)liter->data ); widget && iter ; iter = g_list_next ( iter ) ) {
+ widget = rofi_theme_find_or_create_name ( widget, iter->data );
+ }
+ g_list_free_full ( (GList*)liter->data, g_free );
+ widget->set = TRUE;
+ rofi_theme_widget_add_properties ( widget, $5);
+ }
+ if ( $5 ) {
+ g_hash_table_destroy ( $5 );
+ }
+ g_list_free ( $3 );
+};
+
/**
* Small dummy object to make the prefix optional.
*/
@@ -293,7 +354,7 @@ t_name_prefix_optional t_entry_name_path_selectors T_BOPEN t_property_list_optio
{
for ( GList *liter = g_list_first ( $2); liter; liter = g_list_next ( liter ) ) {
ThemeWidget *widget = rofi_theme;
- for ( GList *iter = g_list_first ( (GList*)liter->data ); iter ; iter = g_list_next ( iter ) ) {
+ for ( GList *iter = g_list_first ( (GList*)liter->data ); widget && iter ; iter = g_list_next ( iter ) ) {
widget = rofi_theme_find_or_create_name ( widget, iter->data );
}
g_list_free_full ( (GList*)liter->data, g_free );
@@ -312,6 +373,45 @@ t_name_prefix_optional t_entry_name_path_selectors T_BOPEN t_property_list_optio
g_hash_table_destroy ( $3 );
}
}
+| T_MEDIA T_PARENT_LEFT T_STRING T_PSEP T_INT T_PARENT_RIGHT T_BOPEN t_media_entry_list T_BCLOSE {
+ gchar *name = g_strdup_printf("@media ( %s: %d )",$3, $5);
+ ThemeWidget *widget = rofi_theme_find_or_create_name ( rofi_theme, name );
+ widget->set = TRUE;
+ widget->media = g_malloc0(sizeof(ThemeMedia));
+ widget->media->type = rofi_theme_parse_media_type ( $3 );
+ widget->media->value = (double)$5;
+ for ( unsigned int i = 0; i < $8->num_widgets;i++) {
+ ThemeWidget *d = $8->widgets[i];
+ rofi_theme_parse_merge_widgets(widget, d);
+ }
+ g_free ( name );
+}
+| T_MEDIA T_PARENT_LEFT T_STRING T_PSEP T_DOUBLE T_PARENT_RIGHT T_BOPEN t_media_entry_list T_BCLOSE {
+ gchar *name = g_strdup_printf("@media ( %s: %f )",$3, $5);
+ ThemeWidget *widget = rofi_theme_find_or_create_name ( rofi_theme, name );
+ widget->set = TRUE;
+ widget->media = g_malloc0(sizeof(ThemeMedia));
+ widget->media->type = rofi_theme_parse_media_type ( $3 );
+ widget->media->value = $5;
+ for ( unsigned int i = 0; i < $8->num_widgets;i++) {
+ ThemeWidget *d = $8->widgets[i];
+ rofi_theme_parse_merge_widgets(widget, d);
+ }
+ g_free ( name );
+}
+| T_MEDIA T_PARENT_LEFT T_STRING T_PSEP T_INT T_UNIT_PX T_PARENT_RIGHT T_BOPEN t_media_entry_list T_BCLOSE {
+ gchar *name = g_strdup_printf("@media ( %s: %d px )",$3, $5);
+ ThemeWidget *widget = rofi_theme_find_or_create_name ( rofi_theme, name );
+ widget->set = TRUE;
+ widget->media = g_malloc0(sizeof(ThemeMedia));
+ widget->media->type = rofi_theme_parse_media_type ( $3 );
+ widget->media->value = (double)$5;
+ for ( unsigned int i = 0; i < $9->num_widgets;i++) {
+ ThemeWidget *d = $9->widgets[i];
+ rofi_theme_parse_merge_widgets(widget, d);
+ }
+ g_free ( name );
+}
;
t_config_property_list_optional
diff --git a/source/rofi.c b/source/rofi.c
index 650e788d..1bc09ebc 100644
--- a/source/rofi.c
+++ b/source/rofi.c
@@ -999,6 +999,9 @@ int main ( int argc, char *argv[] )
}
TICK_N ( "Setup late Display" );
+ rofi_theme_parse_process_conditionals ();
+ TICK_N ( "Theme setup" );
+
// Setup signal handling sources.
// SIGINT
g_unix_signal_add ( SIGINT, main_loop_signal_handler_int, NULL );
diff --git a/source/theme.c b/source/theme.c
index da3b726b..f0a71c32 100644
--- a/source/theme.c
+++ b/source/theme.c
@@ -1021,3 +1021,131 @@ char * rofi_theme_parse_prepare_file ( const char *file, const char *parent_file
return filename;
}
+
+
+void rofi_theme_parse_merge_widgets ( ThemeWidget *parent, ThemeWidget *child )
+{
+ g_assert ( parent != NULL );
+ g_assert ( child != NULL );
+
+ if ( parent == rofi_theme && g_strcmp0(child->name, "*") == 0 ){
+ rofi_theme_widget_add_properties ( parent, child->properties);
+ return;
+ }
+
+ ThemeWidget *w = rofi_theme_find_or_create_name ( parent, child->name);
+ rofi_theme_widget_add_properties ( w, child->properties);
+ for ( unsigned int i =0; i < child->num_widgets; i++) {
+ rofi_theme_parse_merge_widgets ( w, child->widgets[i]);
+ }
+}
+
+void rofi_theme_parse_process_conditionals ( void )
+{
+ workarea mon;
+ monitor_active ( &mon );
+ if ( rofi_theme == NULL ) return;
+ for ( unsigned int i = 0; i < rofi_theme->num_widgets; i++ ) {
+ ThemeWidget *widget = rofi_theme->widgets[i];
+ if ( widget->media != NULL ) {
+ switch ( widget->media->type )
+ {
+ case THEME_MEDIA_TYPE_MIN_WIDTH:
+ {
+ int w = widget->media->value;
+ if ( mon.w >= w ){
+ for ( unsigned int x =0; x < widget->num_widgets; x++) {
+ rofi_theme_parse_merge_widgets ( rofi_theme, widget->widgets[x] );
+ }
+ }
+ break;
+ }
+ case THEME_MEDIA_TYPE_MAX_WIDTH:
+ {
+ int w = widget->media->value;
+ if ( mon.w < w ){
+ for ( unsigned int x =0; x < widget->num_widgets; x++) {
+ rofi_theme_parse_merge_widgets ( rofi_theme, widget->widgets[x] );
+ }
+ }
+ break;
+ }
+ case THEME_MEDIA_TYPE_MIN_HEIGHT:
+ {
+ int h = widget->media->value;
+ if ( mon.h >= h ){
+ for ( unsigned int x =0; x < widget->num_widgets; x++) {
+ rofi_theme_parse_merge_widgets ( rofi_theme, widget->widgets[x] );
+ }
+ }
+ break;
+ }
+ case THEME_MEDIA_TYPE_MAX_HEIGHT:
+ {
+ int h = widget->media->value;
+ if ( mon.h < h ){
+ for ( unsigned int x =0; x < widget->num_widgets; x++) {
+ rofi_theme_parse_merge_widgets ( rofi_theme, widget->widgets[x] );
+ }
+ }
+ break;
+ }
+ case THEME_MEDIA_TYPE_MON_ID:
+ {
+ if ( mon.monitor_id == widget->media->value ){
+ for ( unsigned int x =0; x < widget->num_widgets; x++) {
+ rofi_theme_parse_merge_widgets ( rofi_theme, widget->widgets[x] );
+ }
+ }
+ break;
+ }
+ case THEME_MEDIA_TYPE_MIN_ASPECT_RATIO:
+ {
+ double r = widget->media->value;
+ if ( (mon.w/(double)mon.h) >= r ){
+ for ( unsigned int x =0; x < widget->num_widgets; x++) {
+ rofi_theme_parse_merge_widgets ( rofi_theme, widget->widgets[x] );
+ }
+ }
+ break;
+ }
+ case THEME_MEDIA_TYPE_MAX_ASPECT_RATIO:
+ {
+ double r = widget->media->value;
+ if ( (mon.w/(double)mon.h) < r ){
+ for ( unsigned int x =0; x < widget->num_widgets; x++) {
+ rofi_theme_parse_merge_widgets ( rofi_theme, widget->widgets[x] );
+ }
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+ThemeMediaType rofi_theme_parse_media_type ( const char *type )
+{
+ if ( g_strcmp0( type, "monitor-id" ) == 0 ) {
+ return THEME_MEDIA_TYPE_MON_ID;
+ } else if ( g_strcmp0 ( type, "min-width") == 0 ) {
+ return THEME_MEDIA_TYPE_MIN_WIDTH;
+ } else if ( g_strcmp0 ( type, "min-height") == 0 ) {
+ return THEME_MEDIA_TYPE_MIN_HEIGHT;
+ } else if ( g_strcmp0 ( type, "max-width") == 0 ) {
+ return THEME_MEDIA_TYPE_MAX_WIDTH;
+ } else if ( g_strcmp0 ( type, "max-height") == 0 ) {
+ return THEME_MEDIA_TYPE_MAX_HEIGHT;
+ } else if ( g_strcmp0 ( type, "min-aspect-ratio") == 0 ) {
+ return THEME_MEDIA_TYPE_MIN_ASPECT_RATIO;
+ } else if ( g_strcmp0 ( type, "max-aspect-ratio") == 0 ) {
+ return THEME_MEDIA_TYPE_MAX_ASPECT_RATIO;
+ }
+ return THEME_MEDIA_TYPE_INVALID;
+}
+