From 1fe0e3b44dd7dbf7545e330da0b8fe186a3f3143 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sat, 19 May 2018 23:03:52 -0400 Subject: [PATCH 01/37] Add criteria --- criteria.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++ include/criteria.h | 45 +++++++++++++++++++++++++++++++++ meson.build | 1 + 3 files changed, 109 insertions(+) create mode 100644 criteria.c create mode 100644 include/criteria.h diff --git a/criteria.c b/criteria.c new file mode 100644 index 00000000..4081ab2d --- /dev/null +++ b/criteria.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include "mako.h" +#include "criteria.h" +#include "notification.h" + +struct mako_criteria *create_criteria(struct mako_state *state) { + struct mako_criteria *criteria = calloc(1, sizeof(struct mako_criteria)); + if (criteria == NULL) { + fprintf(stderr, "allocation failed\n"); + return NULL; + } + + return criteria; +} + +void destroy_criteria(struct mako_criteria *criteria) { + free(criteria->app_name); + free(criteria->app_icon); + free(criteria->category); + free(criteria->desktop_entry); + free(criteria); +} + +bool match_criteria(struct mako_criteria *criteria, + struct mako_notification *notif) { + struct mako_criteria_spec spec = criteria->spec; + + if (spec.app_name && + strcmp(criteria->app_name, notif->app_name) != 0) { + return false; + } + + if (spec.app_icon && + strcmp(criteria->app_icon, notif->app_icon) != 0) { + return false; + } + + if (spec.actionable && + criteria->actionable == wl_list_empty(¬if->actions)) { + return false; + } + + if (spec.urgency && + criteria->urgency != notif->urgency) { + return false; + } + + if (spec.category && + strcmp(criteria->category, notif->category) != 0) { + return false; + } + + if (spec.desktop_entry && + strcmp(criteria->desktop_entry, notif->desktop_entry) != 0) { + return false; + } + + return true; +} diff --git a/include/criteria.h b/include/criteria.h new file mode 100644 index 00000000..3bb5d95d --- /dev/null +++ b/include/criteria.h @@ -0,0 +1,45 @@ +#ifndef _MAKO_CRITERIA_H +#define _MAKO_CRITERIA_H + +#include +#include +#include "config.h" +#include "notification.h" + +struct mako_config; + +// Stores whether or not each field was part of the criteria specification, so +// that, for example, "not actionable" can be distinguished from "don't care". +// This is unnecessary for string fields, but it's best to just keep it +// consistent. +struct mako_criteria_spec { + bool app_name; + bool app_icon; + bool actionable; + bool urgency; + bool category; + bool desktop_entry; +}; + +struct mako_criteria { + struct mako_criteria_spec spec; + + // Style to apply to matches: + struct mako_style style; + + // Fields that can be matched: + char *app_name; + char *app_icon; + bool actionable; // Whether mako_notification.actions is nonempty + + enum mako_notification_urgency urgency; + char *category; + char *desktop_entry; +}; + +struct mako_criteria *create_criteria(struct mako_state *state); +void destroy_criteria(struct mako_criteria *criteria); +bool match_criteria(struct mako_criteria *criteria, + struct mako_notification *notif); + +#endif diff --git a/meson.build b/meson.build index a65ecb4a..d163aa18 100644 --- a/meson.build +++ b/meson.build @@ -38,6 +38,7 @@ executable( 'pool-buffer.c', 'render.c', 'wayland.c', + 'criteria.c', ]), dependencies: [ cairo, From ab93887945c3e1ce01b6850e333ddcfb8759a00b Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sat, 9 Jun 2018 19:22:14 -0400 Subject: [PATCH 02/37] First pass at criteria string parser --- criteria.c | 194 +++++++++++++++++++++++++++++++++++++++++++++ include/criteria.h | 15 ++++ 2 files changed, 209 insertions(+) diff --git a/criteria.c b/criteria.c index 4081ab2d..1a2b26f3 100644 --- a/criteria.c +++ b/criteria.c @@ -1,7 +1,9 @@ +#define _POSIX_C_SOURCE 200809L #include #include #include #include +#include #include #include "mako.h" #include "criteria.h" @@ -61,3 +63,195 @@ bool match_criteria(struct mako_criteria *criteria, return true; } + +bool parse_boolean(const char *string, bool *out) { + if (strcasecmp(string, "true") == 0 || strcmp(string, "1") == 0) { + *out = true; + return true; + } + else if (strcasecmp(string, "false") == 0 || strcmp(string, "0") == 0) { + *out = false; + return true; + } + + return false; +} + +bool parse_urgency(const char *string, enum mako_notification_urgency *out) { + if (strcasecmp(string, "low") == 0) { + *out = MAKO_NOTIFICATION_URGENCY_LOW; + return true; + } + else if (strcasecmp(string, "normal") == 0) { + *out = MAKO_NOTIFICATION_URGENCY_NORMAL; + return true; + } + else if (strcasecmp(string, "high") == 0) { + *out = MAKO_NOTIFICATION_URGENCY_HIGH; + return true; + } + + return false; +} + +bool parse_criteria(const char *string, struct mako_criteria *criteria) { + // Create space to build up the current token that we're reading. We know + // that no single token can ever exceed the length of the entire criteria + // string, so that's a safe length to use for the buffer. + int token_max_length = strlen(string) + 1; + char token[token_max_length]; + memset(token, 0, token_max_length); + int token_location = 0; + + enum mako_parse_state state = MAKO_PARSE_STATE_NORMAL; + const char *location = string; + + char ch; + while ((ch = *location++) != '\0') { + switch (state) { + case MAKO_PARSE_STATE_ESCAPE: + case MAKO_PARSE_STATE_QUOTE_ESCAPE: + token[token_location] = ch; + ++token_location; + state &= ~MAKO_PARSE_STATE_ESCAPE; // These work as a bitmask. + break; + + case MAKO_PARSE_STATE_QUOTE: + switch (ch) { + case '\\': + state = MAKO_PARSE_STATE_QUOTE_ESCAPE; + break; + case '"': + state = MAKO_PARSE_STATE_NORMAL; + break; + case ' ': + token[token_location] = ch; + ++token_location; + break; + default: + token[token_location] = ch; + ++token_location; + } + break; + + case MAKO_PARSE_STATE_NORMAL: + switch (ch) { + case '\\': + state = MAKO_PARSE_STATE_ESCAPE; + break; + case '"': + state = MAKO_PARSE_STATE_QUOTE; + break; + case ' ': + // New token, apply the old one and reset our state. + if (!apply_criteria_field(criteria, token)) { + // TODO: Error handling, I guess. + return false; + } + memset(token, 0, token_max_length); + token_location = 0; + break; + default: + token[token_location] = ch; + ++token_location; + } + break; + } + } + + if (state != MAKO_PARSE_STATE_NORMAL) { + if (state & MAKO_PARSE_STATE_QUOTE) { + // TODO: Incomplete quote message + return false; + } + else if (state & MAKO_PARSE_STATE_ESCAPE) { + // TODO: Incomplete escape message + return false; + } + else { + // TODO: Generic message, someone forgot to update this block with + // a new state mask. + return false; + } + } + + return true; +} + +// Takes a token from the criteria string that looks like "key=value", figures +// out which field of the criteria "key" refers to, and sets it to "value". +// Any further equal signs are assumed to be part of the value. If there is no . +// equal sign present, the field is treated as a boolean, with a leading +// exclamation point signifying negation. +bool apply_criteria_field(struct mako_criteria *criteria, const char *token) { + const char *key = token; + const char *value = strstr(token, "="); + bool bare_key = !value; + + if (value) { + // Skip past the equal sign to the value itself. + ++value; + } + else { + // If there's no value, assume it's a boolean, and set the value + // appropriately. This allows uniform parsing later on. + if (*key == '!') { + // Negated boolean, skip past the exclamation point. + ++key; + value = "false"; + } + else { + value = "true"; + } + } + + // Now apply the value to the appropriate member of the criteria. + // If the value was omitted, only try to match against boolean fields. + // Otherwise, anything is fair game. This helps to return a better error + // message. + + if (!bare_key) { + if (strcmp(key, "app-name") == 0) { + criteria->app_name = strdup(value); + criteria->spec.app_name = true; + } + else if (strcmp(key, "app-icon") == 0) { + criteria->app_icon = strdup(value); + criteria->spec.app_icon = true; + } + else if (strcmp(key, "urgency") == 0) { + if (!parse_urgency(value, &criteria->urgency)) { + // TODO + return false; + } + criteria->spec.urgency = true; + } + else if (strcmp(key, "category") == 0) { + criteria->category = strdup(value); + criteria->spec.category = true; + } + else if (strcmp(key, "desktop-entry") == 0) { + criteria->desktop_entry = strdup(value); + criteria->spec.desktop_entry = true; + } + else { + // Anything left must be one of the boolean fields, defined using + // standard syntax. Continue on. + } + } + + if (strcmp(key, "actionable") == 0) { + if (!parse_boolean(value, &criteria->actionable)) { + // TODO + return false; + } + criteria->spec.actionable = true; + } + else { + // TODO + // if bare_key "invalid boolean field" else "invalid field" + return false; + } + + return true; +} diff --git a/include/criteria.h b/include/criteria.h index 3bb5d95d..7b8a8195 100644 --- a/include/criteria.h +++ b/include/criteria.h @@ -8,6 +8,15 @@ struct mako_config; +// State is intended to work as a bitmask, so if more need to be added in the +// future, this should be taken into account. +enum mako_parse_state { + MAKO_PARSE_STATE_NORMAL = 0, + MAKO_PARSE_STATE_ESCAPE = 1, + MAKO_PARSE_STATE_QUOTE = 2, + MAKO_PARSE_STATE_QUOTE_ESCAPE = 3, +}; + // Stores whether or not each field was part of the criteria specification, so // that, for example, "not actionable" can be distinguished from "don't care". // This is unnecessary for string fields, but it's best to just keep it @@ -42,4 +51,10 @@ void destroy_criteria(struct mako_criteria *criteria); bool match_criteria(struct mako_criteria *criteria, struct mako_notification *notif); +bool parse_boolean(const char *string, bool *out); +bool parse_urgency(const char *string, enum mako_notification_urgency *out); + +bool parse_criteria(const char *string, struct mako_criteria *criteria); +bool apply_criteria_field(struct mako_criteria *criteria, const char *token); + #endif From 7182f0ede129b4a60a534dbfeb400f7ec45b34e4 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sat, 9 Jun 2018 21:16:01 -0400 Subject: [PATCH 03/37] Add missing mako_style_spec.ignore_timeout --- include/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/config.h b/include/config.h index c5bc4290..d97f0f80 100644 --- a/include/config.h +++ b/include/config.h @@ -28,7 +28,7 @@ enum mako_sort_criteria { // structs are also mirrored. struct mako_style_spec { bool width, height, margin, padding, border_size, font, markup, format, - actions, default_timeout; + actions, default_timeout, ignore_timeout; struct { bool background, text, border; From 5a59247e90aedd51e73fcb043d93ad8c79786109 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 09:20:51 -0400 Subject: [PATCH 04/37] create_criteria takes a config instead of state --- criteria.c | 2 +- include/criteria.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/criteria.c b/criteria.c index 1a2b26f3..16591304 100644 --- a/criteria.c +++ b/criteria.c @@ -9,7 +9,7 @@ #include "criteria.h" #include "notification.h" -struct mako_criteria *create_criteria(struct mako_state *state) { +struct mako_criteria *create_criteria(struct mako_config *config) { struct mako_criteria *criteria = calloc(1, sizeof(struct mako_criteria)); if (criteria == NULL) { fprintf(stderr, "allocation failed\n"); diff --git a/include/criteria.h b/include/criteria.h index 7b8a8195..59eabb60 100644 --- a/include/criteria.h +++ b/include/criteria.h @@ -46,7 +46,7 @@ struct mako_criteria { char *desktop_entry; }; -struct mako_criteria *create_criteria(struct mako_state *state); +struct mako_criteria *create_criteria(struct mako_config *config); void destroy_criteria(struct mako_criteria *criteria); bool match_criteria(struct mako_criteria *criteria, struct mako_notification *notif); From 2f8c9821dcbf7eb38a8e1b44ae82eb05e0cf15eb Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 09:22:55 -0400 Subject: [PATCH 05/37] apply_criteria_field consumes the token --- criteria.c | 9 ++++++--- include/criteria.h | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/criteria.c b/criteria.c index 16591304..309f4f3b 100644 --- a/criteria.c +++ b/criteria.c @@ -183,13 +183,16 @@ bool parse_criteria(const char *string, struct mako_criteria *criteria) { // Any further equal signs are assumed to be part of the value. If there is no . // equal sign present, the field is treated as a boolean, with a leading // exclamation point signifying negation. -bool apply_criteria_field(struct mako_criteria *criteria, const char *token) { - const char *key = token; - const char *value = strstr(token, "="); +// +// Note that the token will be consumed. +bool apply_criteria_field(struct mako_criteria *criteria, char *token) { + char *key = token; + char *value = strstr(key, "="); bool bare_key = !value; if (value) { // Skip past the equal sign to the value itself. + *value = '\0'; ++value; } else { diff --git a/include/criteria.h b/include/criteria.h index 59eabb60..ce61b980 100644 --- a/include/criteria.h +++ b/include/criteria.h @@ -55,6 +55,6 @@ bool parse_boolean(const char *string, bool *out); bool parse_urgency(const char *string, enum mako_notification_urgency *out); bool parse_criteria(const char *string, struct mako_criteria *criteria); -bool apply_criteria_field(struct mako_criteria *criteria, const char *token); +bool apply_criteria_field(struct mako_criteria *criteria, char *token); #endif From 723593d6d554cef9026f87e70a0e9c4d2f6b9f27 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 09:24:32 -0400 Subject: [PATCH 06/37] criteria are stored in a list on mako_config --- config.c | 4 +++- criteria.c | 9 +++++++++ include/config.h | 3 ++- include/criteria.h | 3 +++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/config.c b/config.c index 1f5b3efa..f4c4c216 100644 --- a/config.c +++ b/config.c @@ -9,9 +9,11 @@ #include #include "config.h" +#include "criteria.h" void init_default_config(struct mako_config *config) { - init_default_style(&config->style); + wl_list_init(&config->criteria); + create_criteria(config); // Create the global, empty criteria. config->hidden_format = strdup("(%h more)"); config->output = strdup(""); diff --git a/criteria.c b/criteria.c index 309f4f3b..24f61e26 100644 --- a/criteria.c +++ b/criteria.c @@ -16,6 +16,7 @@ struct mako_criteria *create_criteria(struct mako_config *config) { return NULL; } + wl_list_insert(config->criteria.prev, &criteria->link); return criteria; } @@ -258,3 +259,11 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) { return true; } + +// Retreive the global critiera from a given mako_config. This just so happens +// to be the first criteria in the list. +struct mako_criteria *global_criteria(struct mako_config *config) { + struct mako_criteria *criteria = + wl_container_of(config->criteria.next, criteria, link); + return criteria; +} diff --git a/include/config.h b/include/config.h index d97f0f80..aef5248d 100644 --- a/include/config.h +++ b/include/config.h @@ -3,6 +3,7 @@ #include #include +#include struct mako_directional { int32_t top; @@ -60,7 +61,7 @@ struct mako_style { }; struct mako_config { - struct mako_style style; + struct wl_list criteria; // mako_criteria::link int32_t max_visible; char *output; diff --git a/include/criteria.h b/include/criteria.h index ce61b980..c1659809 100644 --- a/include/criteria.h +++ b/include/criteria.h @@ -32,6 +32,7 @@ struct mako_criteria_spec { struct mako_criteria { struct mako_criteria_spec spec; + struct wl_list link; // mako_config::criteria // Style to apply to matches: struct mako_style style; @@ -57,4 +58,6 @@ bool parse_urgency(const char *string, enum mako_notification_urgency *out); bool parse_criteria(const char *string, struct mako_criteria *criteria); bool apply_criteria_field(struct mako_criteria *criteria, char *token); +struct mako_criteria *global_criteria(struct mako_config *config); + #endif From 1df70a57e5cfe9478f3301e049ed0683339081a7 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 09:25:05 -0400 Subject: [PATCH 07/37] Split apply_style_option from apply_config_option - Style options also mark the style's spec - hidden section is currently broken --- config.c | 142 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 79 insertions(+), 63 deletions(-) diff --git a/config.c b/config.c index f4c4c216..617d68d2 100644 --- a/config.c +++ b/config.c @@ -152,89 +152,86 @@ static bool parse_color(const char *color, uint32_t *out) { return true; } -static bool apply_config_option(struct mako_config *config, const char *section, - const char *name, const char *value) { - // First try to parse this as a global option. - if (section == NULL) { - if (strcmp(name, "max-visible") == 0) { - return parse_int(value, &config->max_visible); - } else if (strcmp(name, "output") == 0) { - free(config->output); - config->output = strdup(value); - return true; - } else if (strcmp(name, "sort") == 0) { - if (strcmp(value, "+priority") == 0) { - config->sort_criteria |= MAKO_SORT_CRITERIA_URGENCY; - config->sort_asc |= MAKO_SORT_CRITERIA_URGENCY; - } else if (strcmp(value, "-priority") == 0) { - config->sort_criteria |= MAKO_SORT_CRITERIA_URGENCY; - config->sort_asc &= ~MAKO_SORT_CRITERIA_URGENCY; - } else if (strcmp(value, "+time") == 0) { - config->sort_criteria |= MAKO_SORT_CRITERIA_TIME; - config->sort_asc |= MAKO_SORT_CRITERIA_TIME; - } else if (strcmp(value, "-time") == 0) { - config->sort_criteria |= MAKO_SORT_CRITERIA_TIME; - config->sort_asc &= ~MAKO_SORT_CRITERIA_TIME; - } - return true; - } else { - // We want to try the style options now, so keep going. - } - } else { - // TODO: criteria support - if (strcmp(section, "hidden") != 0) { - fprintf(stderr, "Only the 'hidden' section is currently supported\n"); - return false; - } - - if (strcmp(name, "format") == 0) { - free(config->hidden_format); - config->hidden_format = strdup(value); - return true; - } else { - fprintf(stderr, "Only 'format' is supported in the 'hidden' section\n"); - return false; +static bool apply_config_option(struct mako_config *config, const char *name, + const char *value) { + if (strcmp(name, "max-visible") == 0) { + return parse_int(value, &config->max_visible); + } else if (strcmp(name, "output") == 0) { + free(config->output); + config->output = strdup(value); + return true; + } else if (strcmp(name, "sort") == 0) { + if (strcmp(value, "+priority") == 0) { + config->sort_criteria |= MAKO_SORT_CRITERIA_URGENCY; + config->sort_asc |= MAKO_SORT_CRITERIA_URGENCY; + } else if (strcmp(value, "-priority") == 0) { + config->sort_criteria |= MAKO_SORT_CRITERIA_URGENCY; + config->sort_asc &= ~MAKO_SORT_CRITERIA_URGENCY; + } else if (strcmp(value, "+time") == 0) { + config->sort_criteria |= MAKO_SORT_CRITERIA_TIME; + config->sort_asc |= MAKO_SORT_CRITERIA_TIME; + } else if (strcmp(value, "-time") == 0) { + config->sort_criteria |= MAKO_SORT_CRITERIA_TIME; + config->sort_asc &= ~MAKO_SORT_CRITERIA_TIME; } + return true; } - // Now try to match on style options. - struct mako_style *style = &config->style; + return false; +} + + // The hidden criteria is a bit of a lie... + // TODO: Convert it to a style. + //if (strcmp(section, "hidden") == 0) { + // if (strcmp(name, "format") == 0) { + // free(config->hidden_format); + // config->hidden_format = strdup(value); + // return true; + // } + //} + +static bool apply_style_option(struct mako_style *style, const char *name, + const char *value) { + struct mako_style_spec *spec = &style->spec; if (strcmp(name, "font") == 0) { free(style->font); - style->font = strdup(value); - return true; + return spec->font = !!(style->font = strdup(value)); } else if (strcmp(name, "background-color") == 0) { - return parse_color(value, &style->colors.background); + return spec->colors.background = + parse_color(value, &style->colors.background); } else if (strcmp(name, "text-color") == 0) { - return parse_color(value, &style->colors.text); + return spec->colors.text = parse_color(value, &style->colors.text); } else if (strcmp(name, "width") == 0) { - return parse_int(value, &style->width); + return spec->width = parse_int(value, &style->width); } else if (strcmp(name, "height") == 0) { - return parse_int(value, &style->height); + return spec->height = parse_int(value, &style->height); } else if (strcmp(name, "margin") == 0) { - return parse_directional(value, &style->margin); + return spec->margin = parse_directional(value, &style->margin); } else if (strcmp(name, "padding") == 0) { - return parse_int(value, &style->padding); + if (!parse_int(value, &style->padding)) return false; } else if (strcmp(name, "border-size") == 0) { - return parse_int(value, &style->border_size); + return spec->border_size = parse_int(value, &style->border_size); } else if (strcmp(name, "border-color") == 0) { - return parse_color(value, &style->colors.border); + return spec->colors.border = parse_color(value, &style->colors.border); } else if (strcmp(name, "markup") == 0) { + // TODO: Move parse_boolean somewhere accessible. style->markup = strcmp(value, "1") == 0; - return style->markup || strcmp(value, "0") == 0; + return spec->markup = style->markup || strcmp(value, "0") == 0; } else if (strcmp(name, "format") == 0) { free(style->format); - style->format = strdup(value); - return true; + return spec->format = !!(style->format = strdup(value)); } else if (strcmp(name, "default-timeout") == 0) { - return parse_int(value, &style->default_timeout); + return spec->default_timeout = + parse_int(value, &style->default_timeout); } else if (strcmp(name, "ignore-timeout") == 0) { + // TODO style->ignore_timeout = strcmp(value, "1") == 0; - return style->ignore_timeout || strcmp(value, "0") == 0; - } else { - return false; + return spec->ignore_timeout = ( + style->ignore_timeout || strcmp(value, "0") == 0); } + + return false; } static bool file_exists(const char *path) { @@ -291,34 +288,52 @@ int load_config_file(struct mako_config *config) { int lineno = 0; char *line = NULL; char *section = NULL; + + struct mako_criteria *criteria = global_criteria(config); + size_t n = 0; while (getline(&line, &n, f) > 0) { ++lineno; if (line[0] == '\0' || line[0] == '\n' || line[0] == '#') { continue; } + if (line[strlen(line) - 1] == '\n') { line[strlen(line) - 1] = '\0'; } + if (line[0] == '[' && line[strlen(line) - 1] == ']') { free(section); section = strndup(line + 1, strlen(line) - 2); + criteria = create_criteria(config); + parse_criteria(section, criteria); continue; } + char *eq = strchr(line, '='); if (!eq) { fprintf(stderr, "[%s:%d] Expected key=value\n", base, lineno); ret = -1; break; } + + bool valid_option = false; eq[0] = '\0'; - if (!apply_config_option(config, section, line, eq + 1)) { + + valid_option = apply_style_option(&criteria->style, line, eq + 1); + + if (section == NULL) { + valid_option = apply_config_option(config, line, eq + 1); + } + + if (!valid_option) { fprintf(stderr, "[%s:%d] Failed to parse option '%s'\n", base, lineno, line); ret = -1; break; } } + free(section); free(line); fclose(f); @@ -364,7 +379,8 @@ int parse_config_arguments(struct mako_config *config, int argc, char **argv) { } const char *name = long_options[option_index].name; - if (!apply_config_option(config, NULL, name, optarg)) { + if (!apply_style_option(&global_criteria(config)->style, name, optarg) + || apply_config_option(config, name, optarg)) { fprintf(stderr, "Failed to parse option '%s'\n", name); return -1; } From a3a93b460924ff62b082c9c7da22b25f3a3e66b0 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 09:31:56 -0400 Subject: [PATCH 08/37] Clean up criteria when destroying config --- config.c | 6 +++++- criteria.c | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/config.c b/config.c index 617d68d2..8695b3d9 100644 --- a/config.c +++ b/config.c @@ -28,7 +28,11 @@ void init_default_config(struct mako_config *config) { } void finish_config(struct mako_config *config) { - finish_style(&config->style); + struct mako_criteria *criteria, *tmp; + wl_list_for_each_safe(criteria, tmp, &config->criteria, link) { + destroy_criteria(criteria); + } + free(config->hidden_format); free(config->output); } diff --git a/criteria.c b/criteria.c index 24f61e26..f800657b 100644 --- a/criteria.c +++ b/criteria.c @@ -21,6 +21,8 @@ struct mako_criteria *create_criteria(struct mako_config *config) { } void destroy_criteria(struct mako_criteria *criteria) { + wl_list_remove(&criteria->link); + free(criteria->app_name); free(criteria->app_icon); free(criteria->category); From 6834e42bca9702832c3c89c99f81374ef2703b40 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 10:31:33 -0400 Subject: [PATCH 09/37] Apply style from matched criteria to notifications --- config.c | 100 +++++++++++++++++++++++++++++++++++++++++ criteria.c | 26 +++++++++++ dbus/xdg.c | 20 ++++++++- include/config.h | 1 + include/criteria.h | 2 + include/notification.h | 4 ++ 6 files changed, 151 insertions(+), 2 deletions(-) diff --git a/config.c b/config.c index 8695b3d9..b93a766a 100644 --- a/config.c +++ b/config.c @@ -70,6 +70,106 @@ void finish_style(struct mako_style *style) { free(style->format); } +// Update `target` with the values specified in `style`. If a failure occurs, +// `target` will remain unchanged. +bool apply_style(struct mako_style *style, struct mako_style *target) { + // Try to duplicate strings up front in case allocation fails and we have + // to bail without changing `target`. + char *new_font, *new_format; + + if (style->spec.font) { + new_font = strdup(style->font); + if (new_font == NULL) { + fprintf(stderr, "allocation failed\n"); + return false; + } + } + + if (style->spec.format) { + new_format = strdup(style->format); + if (new_format == NULL) { + fprintf(stderr, "allocation failed\n"); + return false; + } + } + + // Now on to actually setting things! + + if (style->spec.width) { + target->width = style->width; + target->spec.width = true; + } + + if (style->spec.height) { + target->height = style->height; + target->spec.height = true; + } + + if (style->spec.margin) { + target->margin = style->margin; + target->spec.margin = true; + } + + if (style->spec.padding) { + target->padding = style->padding; + target->spec.padding = true; + } + + if (style->spec.border_size) { + target->border_size = style->border_size; + target->spec.border_size = true; + } + + if (style->spec.font) { + free(target->font); + target->font = new_font; + target->spec.font = true; + } + + if (style->spec.markup) { + target->markup = style->markup; + target->spec.markup = true; + } + + if (style->spec.format) { + free(target->format); + target->format = new_format; + target->spec.format = true; + } + + if (style->spec.actions) { + target->actions = style->actions; + target->spec.actions = true; + } + + if (style->spec.default_timeout) { + target->default_timeout = style->default_timeout; + target->spec.default_timeout = true; + } + + if (style->spec.ignore_timeout) { + target->ignore_timeout = style->ignore_timeout; + target->spec.ignore_timeout = true; + } + + if (style->spec.colors.background) { + target->colors.background = style->colors.background; + target->spec.colors.background = true; + } + + if (style->spec.colors.text) { + target->colors.text = style->colors.text; + target->spec.colors.text = true; + } + + if (style->spec.colors.border) { + target->colors.border = style->colors.border; + target->spec.colors.border = true; + } + + return true; +} + static bool parse_int(const char *s, int *out) { errno = 0; char *end; diff --git a/criteria.c b/criteria.c index f800657b..9e30d171 100644 --- a/criteria.c +++ b/criteria.c @@ -9,6 +9,9 @@ #include "criteria.h" #include "notification.h" +struct mako_style; +struct mako_style_spec; + struct mako_criteria *create_criteria(struct mako_config *config) { struct mako_criteria *criteria = calloc(1, sizeof(struct mako_criteria)); if (criteria == NULL) { @@ -269,3 +272,26 @@ struct mako_criteria *global_criteria(struct mako_config *config) { wl_container_of(config->criteria.next, criteria, link); return criteria; } + + +// Iterate through `criteria_list`, applying the style from each matching +// criteria to `notif`. Returns the number of criteria that matched, or -1 if +// a failure occurs. +int apply_each_criteria(struct wl_list *criteria_list, + struct mako_notification *notif) { + int match_count = 0; + + struct mako_criteria *criteria; + wl_list_for_each(criteria, criteria_list, link) { + if (!match_criteria(criteria, notif)) { + continue; + } + ++match_count; + + if (!apply_style(&criteria->style, ¬if->style)) { + return -1; + } + } + + return match_count; +} diff --git a/dbus/xdg.c b/dbus/xdg.c index 281c1d39..64ebd9d2 100644 --- a/dbus/xdg.c +++ b/dbus/xdg.c @@ -4,6 +4,7 @@ #include #include +#include "criteria.h" #include "dbus.h" #include "mako.h" #include "notification.h" @@ -212,8 +213,23 @@ static int handle_notify(sd_bus_message *msg, void *data, return ret; } - if (expire_timeout < 0 || state->config.style.ignore_timeout) { - expire_timeout = state->config.style.default_timeout; + int match_count = apply_each_criteria(&state->config.criteria, notif); + if (match_count == -1) { + // We encountered an allocation failure or similar while applying + // criteria. The notification may be partially matched, but the worst + // case is that it has an empty style, so bail. + fprintf(stderr, "Failed to apply criteria\n"); + return -1; + } + else if (match_count == 0) { + // This should be impossible, since the global criteria is always + // present in a mako_config and matches everything. + fprintf(stderr, "Notification matched zero criteria?!\n"); + return -1; + } + + if (expire_timeout < 0 || notif->style.ignore_timeout) { + expire_timeout = notif->style.default_timeout; } insert_notification(state, notif); diff --git a/include/config.h b/include/config.h index aef5248d..e79856b8 100644 --- a/include/config.h +++ b/include/config.h @@ -79,6 +79,7 @@ void finish_config(struct mako_config *config); void init_default_style(struct mako_style *style); void finish_style(struct mako_style *style); +bool apply_style(struct mako_style *style, struct mako_style *target); int parse_config_arguments(struct mako_config *config, int argc, char **argv); int load_config_file(struct mako_config *config); diff --git a/include/criteria.h b/include/criteria.h index c1659809..7efc11eb 100644 --- a/include/criteria.h +++ b/include/criteria.h @@ -59,5 +59,7 @@ bool parse_criteria(const char *string, struct mako_criteria *criteria); bool apply_criteria_field(struct mako_criteria *criteria, char *token); struct mako_criteria *global_criteria(struct mako_config *config); +int apply_each_criteria(struct wl_list *criteria_list, + struct mako_notification *notif); #endif diff --git a/include/notification.h b/include/notification.h index 1bd3cb67..2296fa90 100644 --- a/include/notification.h +++ b/include/notification.h @@ -5,6 +5,8 @@ #include #include +#include "config.h" + enum mako_notification_urgency { MAKO_NOTIFICATION_URGENCY_LOW = 0, MAKO_NOTIFICATION_URGENCY_NORMAL = 1, @@ -24,6 +26,8 @@ struct mako_notification { struct mako_state *state; struct wl_list link; // mako_state::notifications + struct mako_style style; + uint32_t id; char *app_name; char *app_icon; From 028c6f6a920171432af7952457239a4f98266a0d Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 12:20:41 -0400 Subject: [PATCH 10/37] hidden_format -> hidden_style --- config.c | 21 ++++++++++++++++++--- include/config.h | 3 ++- render.c | 7 ++++--- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/config.c b/config.c index b93a766a..8e0f5a61 100644 --- a/config.c +++ b/config.c @@ -15,7 +15,10 @@ void init_default_config(struct mako_config *config) { wl_list_init(&config->criteria); create_criteria(config); // Create the global, empty criteria. - config->hidden_format = strdup("(%h more)"); + init_default_style(&config->hidden_style); + free(config->hidden_style.format); + config->hidden_style.format = strdup("(%h more)"); + config->output = strdup(""); config->max_visible = 5; @@ -33,7 +36,7 @@ void finish_config(struct mako_config *config) { destroy_criteria(criteria); } - free(config->hidden_format); + finish_style(&config->hidden_style); free(config->output); } @@ -424,7 +427,19 @@ int load_config_file(struct mako_config *config) { bool valid_option = false; eq[0] = '\0'; - valid_option = apply_style_option(&criteria->style, line, eq + 1); + struct mako_style *target_style; + if (strcmp(section, "hidden") == 0) { + // The hidden criteria is a lie, we store the associated style + // directly on the config because there's no "real" notification + // object to match against it later. + // TODO: Raise an error if "hidden" occurs with any other criteria. + target_style = &config->hidden_style; + } + else { + target_style = &criteria->style; + } + + valid_option = apply_style_option(target_style, line, eq + 1); if (section == NULL) { valid_option = apply_config_option(config, line, eq + 1); diff --git a/include/config.h b/include/config.h index e79856b8..7ba1f90b 100644 --- a/include/config.h +++ b/include/config.h @@ -65,10 +65,11 @@ struct mako_config { int32_t max_visible; char *output; - char *hidden_format; uint32_t sort_criteria; //enum mako_sort_criteria uint32_t sort_asc; + struct mako_style hidden_style; + struct { enum mako_button_binding left, right, middle; } button_bindings; diff --git a/render.c b/render.c index f403df19..746fd895 100644 --- a/render.c +++ b/render.c @@ -171,16 +171,17 @@ int render(struct mako_state *state, struct pool_buffer *buffer, int scale) { } if (wl_list_length(&state->notifications) > config->max_visible) { - total_height += inner_margin; + struct mako_style *style = &config->hidden_style; + total_height += style->margin.top; // TODO: inner margin size_t text_ln = - format_text(config->hidden_format, NULL, format_state_text, state); + format_text(style->format, NULL, format_state_text, state); char *text = malloc(text_ln + 1); if (text == NULL) { fprintf(stderr, "allocation failed"); return 0; } - format_text(config->hidden_format, text, format_state_text, state); + format_text(style->format, text, format_state_text, state); int hidden_height = render_notification( cairo, state, style, text, total_height, scale); From 585479a5539d36edfd00720e0a296cb682d478be Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 12:20:55 -0400 Subject: [PATCH 11/37] Use notification's style in render --- render.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/render.c b/render.c index 746fd895..4846ff3e 100644 --- a/render.c +++ b/render.c @@ -114,7 +114,6 @@ static int render_notification(cairo_t *cairo, struct mako_state *state, int render(struct mako_state *state, struct pool_buffer *buffer, int scale) { struct mako_config *config = &state->config; - struct mako_style *style = &config->style; cairo_t *cairo = buffer->cairo; if (wl_list_empty(&state->notifications)) { @@ -128,17 +127,14 @@ int render(struct mako_state *state, struct pool_buffer *buffer, int scale) { cairo_paint(cairo); cairo_restore(cairo); - int inner_margin = style->margin.top; - if (style->margin.bottom > style->margin.top) { - inner_margin = style->margin.bottom; - } - int notif_width = state->width; size_t i = 0; int total_height = 0; struct mako_notification *notif; wl_list_for_each(notif, &state->notifications, link) { + struct mako_style *style = ¬if->style; + size_t text_len = format_text(style->format, NULL, format_notif_text, notif); char *text = malloc(text_len + 1); @@ -148,7 +144,7 @@ int render(struct mako_state *state, struct pool_buffer *buffer, int scale) { format_text(style->format, text, format_notif_text, notif); if (i > 0) { - total_height += inner_margin; + total_height += style->margin.top; // TODO: inner margin } int notif_height = render_notification( From 53e7bb186fd34bb38866c85e9ae04aaad07ad7a9 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 13:47:45 -0400 Subject: [PATCH 12/37] Use global criteria when sending capabilities This means that if the global criteria disables a feature, later critiera cannot reenable it. I'll deal with this later. --- dbus/xdg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dbus/xdg.c b/dbus/xdg.c index 64ebd9d2..e3c241e6 100644 --- a/dbus/xdg.c +++ b/dbus/xdg.c @@ -28,21 +28,21 @@ static int handle_get_capabilities(sd_bus_message *msg, void *data, return ret; } - if (strstr(state->config.style.format, "%b") != NULL) { + if (strstr(global_criteria(&state->config)->style.format, "%b") != NULL) { ret = sd_bus_message_append(reply, "s", "body"); if (ret < 0) { return ret; } } - if (state->config.style.markup) { + if (global_criteria(&state->config)->style.markup) { ret = sd_bus_message_append(reply, "s", "body-markup"); if (ret < 0) { return ret; } } - if (state->config.style.actions) { + if (global_criteria(&state->config)->style.actions) { ret = sd_bus_message_append(reply, "s", "actions"); if (ret < 0) { return ret; From abfa95c4acf4d064e00643a0ebf460b4ee0b9f08 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 13:53:41 -0400 Subject: [PATCH 13/37] Use global criteria to set surface size This definitely can't stay around either, but good enough for now. --- wayland.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/wayland.c b/wayland.c index 2a235c4c..699237e6 100644 --- a/wayland.c +++ b/wayland.c @@ -3,6 +3,7 @@ #include #include +#include "criteria.h" #include "mako.h" #include "notification.h" #include "render.h" @@ -407,14 +408,15 @@ void send_frame(struct mako_state *state) { zwlr_layer_surface_v1_add_listener(state->layer_surface, &layer_surface_listener, state); - struct mako_config *config = &state->config; - zwlr_layer_surface_v1_set_size(state->layer_surface, - config->style.width, height); + struct mako_style *style = &global_criteria(&state->config)->style; + + zwlr_layer_surface_v1_set_size(state->layer_surface, style->width, + height); zwlr_layer_surface_v1_set_anchor(state->layer_surface, ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT); zwlr_layer_surface_v1_set_margin(state->layer_surface, - config->style.margin.top, config->style.margin.right, - config->style.margin.bottom, config->style.margin.left); + style->margin.top, style->margin.right, + style->margin.bottom, style->margin.left); wl_surface_commit(state->surface); return; } @@ -427,7 +429,7 @@ void send_frame(struct mako_state *state) { // requested, we'll enter an infinite loop if (state->height != height) { zwlr_layer_surface_v1_set_size(state->layer_surface, - state->config.style.width, height); + global_criteria(&state->config)->style.width, height); wl_surface_commit(state->surface); return; } From 5f2e5418f6f2540391f4f9557f7223f21057a552 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 13:59:46 -0400 Subject: [PATCH 14/37] Only try to look at the section if it exists --- config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.c b/config.c index 8e0f5a61..1e33603e 100644 --- a/config.c +++ b/config.c @@ -428,7 +428,7 @@ int load_config_file(struct mako_config *config) { eq[0] = '\0'; struct mako_style *target_style; - if (strcmp(section, "hidden") == 0) { + if (section != NULL && strcmp(section, "hidden") == 0) { // The hidden criteria is a lie, we store the associated style // directly on the config because there's no "real" notification // object to match against it later. From 5a0fa33d925280db11b8d13bfd0913e0a4fae3a8 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 14:02:24 -0400 Subject: [PATCH 15/37] Only try config options if it wasn't a valid style --- config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.c b/config.c index 1e33603e..2f294dad 100644 --- a/config.c +++ b/config.c @@ -441,7 +441,7 @@ int load_config_file(struct mako_config *config) { valid_option = apply_style_option(target_style, line, eq + 1); - if (section == NULL) { + if (!valid_option && section == NULL) { valid_option = apply_config_option(config, line, eq + 1); } From 5ea01cd01e8c20014086c075e690cff05b4d45ce Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 14:10:24 -0400 Subject: [PATCH 16/37] Actually init the default style whoops --- config.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config.c b/config.c index 2f294dad..b8ee2774 100644 --- a/config.c +++ b/config.c @@ -13,7 +13,8 @@ void init_default_config(struct mako_config *config) { wl_list_init(&config->criteria); - create_criteria(config); // Create the global, empty criteria. + struct mako_criteria *global_criteria = create_criteria(config); + init_default_style(&global_criteria->style); init_default_style(&config->hidden_style); free(config->hidden_style.format); From b04b73e45d9972278875d7cb510ea622bfbb7ebf Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 14:34:03 -0400 Subject: [PATCH 17/37] Apply the final criteria field after loop exits --- criteria.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/criteria.c b/criteria.c index 9e30d171..28aab730 100644 --- a/criteria.c +++ b/criteria.c @@ -181,6 +181,13 @@ bool parse_criteria(const char *string, struct mako_criteria *criteria) { } } + // Apply the last token, which will be left in the buffer after we hit the + // final NULL. We know it's valid since we just checked for that. + if (!apply_criteria_field(criteria, token)) { + // TODO: Error handling, I guess. + return false; + } + return true; } From 97a7d71f548f9fa5e9df0db03fc63e4632839eac Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 15:04:27 -0400 Subject: [PATCH 18/37] Properly calculate inner margin again --- render.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/render.c b/render.c index 4846ff3e..cb9a572b 100644 --- a/render.c +++ b/render.c @@ -131,8 +131,11 @@ int render(struct mako_state *state, struct pool_buffer *buffer, int scale) { size_t i = 0; int total_height = 0; + int pending_bottom_margin = 0; struct mako_notification *notif; wl_list_for_each(notif, &state->notifications, link) { + // Note that by this point, everything in the style is guaranteed to + // be specified, so we don't need to check. struct mako_style *style = ¬if->style; size_t text_len = @@ -144,7 +147,12 @@ int render(struct mako_state *state, struct pool_buffer *buffer, int scale) { format_text(style->format, text, format_notif_text, notif); if (i > 0) { - total_height += style->margin.top; // TODO: inner margin + if (style->margin.top > pending_bottom_margin) { + total_height += style->margin.top; + } + else { + total_height += pending_bottom_margin; + } } int notif_height = render_notification( @@ -158,6 +166,7 @@ int render(struct mako_state *state, struct pool_buffer *buffer, int scale) { notif->hotspot.height = notif_height; total_height += notif_height; + pending_bottom_margin = style->margin.bottom; ++i; if (config->max_visible >= 0 && @@ -168,7 +177,12 @@ int render(struct mako_state *state, struct pool_buffer *buffer, int scale) { if (wl_list_length(&state->notifications) > config->max_visible) { struct mako_style *style = &config->hidden_style; - total_height += style->margin.top; // TODO: inner margin + if (style->margin.top > pending_bottom_margin) { + total_height += style->margin.top; + } + else { + total_height += pending_bottom_margin; + } size_t text_ln = format_text(style->format, NULL, format_state_text, state); From e6e0765f66c3d3067196cecdbb7080c22afee2af Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 16:33:55 -0400 Subject: [PATCH 19/37] Print error for invalid criteria --- config.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/config.c b/config.c index b8ee2774..a3139427 100644 --- a/config.c +++ b/config.c @@ -414,7 +414,12 @@ int load_config_file(struct mako_config *config) { free(section); section = strndup(line + 1, strlen(line) - 2); criteria = create_criteria(config); - parse_criteria(section, criteria); + if (!parse_criteria(section, criteria)) { + fprintf(stderr, "[%s:%d] Invalid criteria definition\n", base, + lineno); + ret = -1; + break; + } continue; } From 29fc3c620c28c32db6de0e4f0c756c288e594b1b Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 16:40:32 -0400 Subject: [PATCH 20/37] Don't make a criteria for hidden --- config.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/config.c b/config.c index a3139427..cc8932e6 100644 --- a/config.c +++ b/config.c @@ -413,6 +413,11 @@ int load_config_file(struct mako_config *config) { if (line[0] == '[' && line[strlen(line) - 1] == ']') { free(section); section = strndup(line + 1, strlen(line) - 2); + if (strcmp(section, "hidden") == 0) { + // Skip making a criteria for the hidden section. + criteria = NULL; + continue; + } criteria = create_criteria(config); if (!parse_criteria(section, criteria)) { fprintf(stderr, "[%s:%d] Invalid criteria definition\n", base, From bbe53bea725b019e5c48836a5e9e5d547b6563ec Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 16:49:45 -0400 Subject: [PATCH 21/37] Fix reporting of criteria parsing success Also add appropriate error messages --- criteria.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/criteria.c b/criteria.c index 28aab730..f2610632 100644 --- a/criteria.c +++ b/criteria.c @@ -230,25 +230,30 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) { if (strcmp(key, "app-name") == 0) { criteria->app_name = strdup(value); criteria->spec.app_name = true; + return true; } else if (strcmp(key, "app-icon") == 0) { criteria->app_icon = strdup(value); criteria->spec.app_icon = true; + return true; } else if (strcmp(key, "urgency") == 0) { if (!parse_urgency(value, &criteria->urgency)) { - // TODO + fprintf(stderr, "Invalid urgency value '%s'", value); return false; } criteria->spec.urgency = true; + return true; } else if (strcmp(key, "category") == 0) { criteria->category = strdup(value); criteria->spec.category = true; + return true; } else if (strcmp(key, "desktop-entry") == 0) { criteria->desktop_entry = strdup(value); criteria->spec.desktop_entry = true; + return true; } else { // Anything left must be one of the boolean fields, defined using @@ -258,14 +263,20 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) { if (strcmp(key, "actionable") == 0) { if (!parse_boolean(value, &criteria->actionable)) { - // TODO + fprintf(stderr, "Invalid value '%s' for boolean field '%s'\n", + value, key); return false; } criteria->spec.actionable = true; + return true; } else { - // TODO - // if bare_key "invalid boolean field" else "invalid field" + if (bare_key) { + fprintf(stderr, "Invalid boolean criteria field '%s'\n", key); + } + else { + fprintf(stderr, "Invalid criteria field '%s'\n", key); + } return false; } From 22878fcdb21e06b244000cde47a9d0a4641a2e4f Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 16:54:25 -0400 Subject: [PATCH 22/37] Finish up error message TODOs in criteria --- criteria.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/criteria.c b/criteria.c index f2610632..73d1facb 100644 --- a/criteria.c +++ b/criteria.c @@ -151,7 +151,7 @@ bool parse_criteria(const char *string, struct mako_criteria *criteria) { case ' ': // New token, apply the old one and reset our state. if (!apply_criteria_field(criteria, token)) { - // TODO: Error handling, I guess. + // An error should have been printed already. return false; } memset(token, 0, token_max_length); @@ -167,16 +167,15 @@ bool parse_criteria(const char *string, struct mako_criteria *criteria) { if (state != MAKO_PARSE_STATE_NORMAL) { if (state & MAKO_PARSE_STATE_QUOTE) { - // TODO: Incomplete quote message + fprintf(stderr, "Unmatched quote in criteria definition\n"); return false; } else if (state & MAKO_PARSE_STATE_ESCAPE) { - // TODO: Incomplete escape message + fprintf(stderr, "Trailing backslash in criteria definition\n"); return false; } else { - // TODO: Generic message, someone forgot to update this block with - // a new state mask. + fprintf(stderr, "Got confused parsing criteria definition\n"); return false; } } @@ -184,7 +183,7 @@ bool parse_criteria(const char *string, struct mako_criteria *criteria) { // Apply the last token, which will be left in the buffer after we hit the // final NULL. We know it's valid since we just checked for that. if (!apply_criteria_field(criteria, token)) { - // TODO: Error handling, I guess. + // An error should have been printed by this point, we don't need to. return false; } From db75066345017d6bcd9e7dc4fb3c9e5b01fff428 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 16:55:45 -0400 Subject: [PATCH 23/37] hidden TODO is already done --- config.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/config.c b/config.c index cc8932e6..c7ceb8d8 100644 --- a/config.c +++ b/config.c @@ -288,16 +288,6 @@ static bool apply_config_option(struct mako_config *config, const char *name, return false; } - // The hidden criteria is a bit of a lie... - // TODO: Convert it to a style. - //if (strcmp(section, "hidden") == 0) { - // if (strcmp(name, "format") == 0) { - // free(config->hidden_format); - // config->hidden_format = strdup(value); - // return true; - // } - //} - static bool apply_style_option(struct mako_style *style, const char *name, const char *value) { struct mako_style_spec *spec = &style->spec; From f71b620cd9b57952f6eb10925f6ee005542dce06 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 16:56:06 -0400 Subject: [PATCH 24/37] Saving this for a future PR --- config.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/config.c b/config.c index c7ceb8d8..9228dc31 100644 --- a/config.c +++ b/config.c @@ -313,7 +313,6 @@ static bool apply_style_option(struct mako_style *style, const char *name, } else if (strcmp(name, "border-color") == 0) { return spec->colors.border = parse_color(value, &style->colors.border); } else if (strcmp(name, "markup") == 0) { - // TODO: Move parse_boolean somewhere accessible. style->markup = strcmp(value, "1") == 0; return spec->markup = style->markup || strcmp(value, "0") == 0; } else if (strcmp(name, "format") == 0) { @@ -323,7 +322,6 @@ static bool apply_style_option(struct mako_style *style, const char *name, return spec->default_timeout = parse_int(value, &style->default_timeout); } else if (strcmp(name, "ignore-timeout") == 0) { - // TODO style->ignore_timeout = strcmp(value, "1") == 0; return spec->ignore_timeout = ( style->ignore_timeout || strcmp(value, "0") == 0); From d676406ca009d60b6653d17ec07947dec4557de8 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 16:57:43 -0400 Subject: [PATCH 25/37] This happens automatically, though could be nicer --- config.c | 1 - 1 file changed, 1 deletion(-) diff --git a/config.c b/config.c index 9228dc31..f6640ab0 100644 --- a/config.c +++ b/config.c @@ -431,7 +431,6 @@ int load_config_file(struct mako_config *config) { // The hidden criteria is a lie, we store the associated style // directly on the config because there's no "real" notification // object to match against it later. - // TODO: Raise an error if "hidden" occurs with any other criteria. target_style = &config->hidden_style; } else { From 8a13aadc286089d87d09def321aeb370e85c176a Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 17:05:02 -0400 Subject: [PATCH 26/37] Missed converting padding --- config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.c b/config.c index f6640ab0..e35b8535 100644 --- a/config.c +++ b/config.c @@ -307,7 +307,7 @@ static bool apply_style_option(struct mako_style *style, const char *name, } else if (strcmp(name, "margin") == 0) { return spec->margin = parse_directional(value, &style->margin); } else if (strcmp(name, "padding") == 0) { - if (!parse_int(value, &style->padding)) return false; + return spec->padding = parse_int(value, &style->padding); } else if (strcmp(name, "border-size") == 0) { return spec->border_size = parse_int(value, &style->border_size); } else if (strcmp(name, "border-color") == 0) { From 8486d1904134452a2963ff7ba4d527d01505ce23 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 17:44:42 -0400 Subject: [PATCH 27/37] Fix up hidden_style correctly I want to refactor this eventually, but I don't know exactly how yet. --- config.c | 3 +-- render.c | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/config.c b/config.c index e35b8535..43b1757a 100644 --- a/config.c +++ b/config.c @@ -16,9 +16,8 @@ void init_default_config(struct mako_config *config) { struct mako_criteria *global_criteria = create_criteria(config); init_default_style(&global_criteria->style); - init_default_style(&config->hidden_style); - free(config->hidden_style.format); config->hidden_style.format = strdup("(%h more)"); + config->hidden_style.spec.format = true; config->output = strdup(""); config->max_visible = 5; diff --git a/render.c b/render.c index cb9a572b..115aea1a 100644 --- a/render.c +++ b/render.c @@ -3,6 +3,8 @@ #include #include +#include "config.h" +#include "criteria.h" #include "mako.h" #include "notification.h" #include "render.h" @@ -176,25 +178,31 @@ int render(struct mako_state *state, struct pool_buffer *buffer, int scale) { } if (wl_list_length(&state->notifications) > config->max_visible) { - struct mako_style *style = &config->hidden_style; - if (style->margin.top > pending_bottom_margin) { - total_height += style->margin.top; + // Apply the hidden_style on top of the global style. This has to be + // done here since this notification isn't "real" and wasn't processed + // by apply_each_criteria. + struct mako_style style = {0}; + apply_style(&global_criteria(config)->style, &style); + apply_style(&config->hidden_style, &style); + + if (style.margin.top > pending_bottom_margin) { + total_height += style.margin.top; } else { total_height += pending_bottom_margin; } size_t text_ln = - format_text(style->format, NULL, format_state_text, state); + format_text(style.format, NULL, format_state_text, state); char *text = malloc(text_ln + 1); if (text == NULL) { fprintf(stderr, "allocation failed"); return 0; } - format_text(style->format, text, format_state_text, state); + format_text(style.format, text, format_state_text, state); int hidden_height = render_notification( - cairo, state, style, text, total_height, scale); + cairo, state, &style, text, total_height, scale); free(text); total_height += hidden_height; From 36ef70c7ff19da4ab89d0cc35991999e9bb69e2d Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 18:19:25 -0400 Subject: [PATCH 28/37] Plug a few leaks --- config.c | 3 ++- criteria.c | 5 ++--- notification.c | 4 +++- render.c | 1 + 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/config.c b/config.c index 43b1757a..645d24d4 100644 --- a/config.c +++ b/config.c @@ -78,7 +78,8 @@ void finish_style(struct mako_style *style) { bool apply_style(struct mako_style *style, struct mako_style *target) { // Try to duplicate strings up front in case allocation fails and we have // to bail without changing `target`. - char *new_font, *new_format; + char *new_font = NULL; + char *new_format = NULL; if (style->spec.font) { new_font = strdup(style->font); diff --git a/criteria.c b/criteria.c index 73d1facb..d15a3075 100644 --- a/criteria.c +++ b/criteria.c @@ -6,12 +6,10 @@ #include #include #include "mako.h" +#include "config.h" #include "criteria.h" #include "notification.h" -struct mako_style; -struct mako_style_spec; - struct mako_criteria *create_criteria(struct mako_config *config) { struct mako_criteria *criteria = calloc(1, sizeof(struct mako_criteria)); if (criteria == NULL) { @@ -26,6 +24,7 @@ struct mako_criteria *create_criteria(struct mako_config *config) { void destroy_criteria(struct mako_criteria *criteria) { wl_list_remove(&criteria->link); + finish_style(&criteria->style); free(criteria->app_name); free(criteria->app_icon); free(criteria->category); diff --git a/notification.c b/notification.c index 92f1c120..50ce528f 100644 --- a/notification.c +++ b/notification.c @@ -10,6 +10,7 @@ #include #endif +#include "config.h" #include "dbus.h" #include "event-loop.h" #include "mako.h" @@ -49,6 +50,7 @@ void destroy_notification(struct mako_notification *notif) { free(action); } destroy_timer(notif->timer); + finish_style(¬if->style); free(notif->app_name); free(notif->app_icon); free(notif->summary); @@ -212,7 +214,7 @@ size_t format_text(const char *format, char *buf, mako_format_func_t format_func char *value = NULL; bool markup = false; - if (current[1] == '%') { + if (current[1] == '%') { value = strdup("%"); } else { value = format_func(current[1], &markup, data); diff --git a/render.c b/render.c index 115aea1a..d1807fba 100644 --- a/render.c +++ b/render.c @@ -204,6 +204,7 @@ int render(struct mako_state *state, struct pool_buffer *buffer, int scale) { int hidden_height = render_notification( cairo, state, &style, text, total_height, scale); free(text); + finish_style(&style); total_height += hidden_height; } From 6119476ec86a159c024e5c79946af510a823b7c0 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 20:14:18 -0400 Subject: [PATCH 29/37] Fix double-free in hidden_style when reloading --- config.c | 5 +++++ include/config.h | 1 + render.c | 3 ++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/config.c b/config.c index 645d24d4..d86ffcf3 100644 --- a/config.c +++ b/config.c @@ -16,6 +16,7 @@ void init_default_config(struct mako_config *config) { struct mako_criteria *global_criteria = create_criteria(config); init_default_style(&global_criteria->style); + init_empty_style(&config->hidden_style); config->hidden_style.format = strdup("(%h more)"); config->hidden_style.spec.format = true; @@ -68,6 +69,10 @@ void init_default_style(struct mako_style *style) { memset(&style->spec, true, sizeof(struct mako_style_spec)); } +void init_empty_style(struct mako_style *style) { + memset(style, 0, sizeof(struct mako_style)); +} + void finish_style(struct mako_style *style) { free(style->font); free(style->format); diff --git a/include/config.h b/include/config.h index 7ba1f90b..b2de30b4 100644 --- a/include/config.h +++ b/include/config.h @@ -79,6 +79,7 @@ void init_default_config(struct mako_config *config); void finish_config(struct mako_config *config); void init_default_style(struct mako_style *style); +void init_empty_style(struct mako_style *style); void finish_style(struct mako_style *style); bool apply_style(struct mako_style *style, struct mako_style *target); diff --git a/render.c b/render.c index d1807fba..2e9e7620 100644 --- a/render.c +++ b/render.c @@ -181,7 +181,8 @@ int render(struct mako_state *state, struct pool_buffer *buffer, int scale) { // Apply the hidden_style on top of the global style. This has to be // done here since this notification isn't "real" and wasn't processed // by apply_each_criteria. - struct mako_style style = {0}; + struct mako_style style; + init_empty_style(&style); apply_style(&global_criteria(config)->style, &style); apply_style(&config->hidden_style, &style); From fa7f9f8731d27ea79eecee9b02c557243b23e6cb Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 10 Jun 2018 20:49:55 -0400 Subject: [PATCH 30/37] Don't try to parse empty criteria field --- criteria.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/criteria.c b/criteria.c index d15a3075..5678b0e7 100644 --- a/criteria.c +++ b/criteria.c @@ -201,6 +201,10 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) { char *value = strstr(key, "="); bool bare_key = !value; + if (*key == '\0') { + return true; + } + if (value) { // Skip past the equal sign to the value itself. *value = '\0'; From ae22b950edd738f486d3b9ee91fc25ddcad58c1f Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Mon, 11 Jun 2018 21:28:50 -0400 Subject: [PATCH 31/37] Squish up all those elses --- config.c | 3 +-- criteria.c | 42 ++++++++++++++---------------------------- dbus/xdg.c | 3 +-- render.c | 6 ++---- 4 files changed, 18 insertions(+), 36 deletions(-) diff --git a/config.c b/config.c index d86ffcf3..7718f2bf 100644 --- a/config.c +++ b/config.c @@ -437,8 +437,7 @@ int load_config_file(struct mako_config *config) { // directly on the config because there's no "real" notification // object to match against it later. target_style = &config->hidden_style; - } - else { + } else { target_style = &criteria->style; } diff --git a/criteria.c b/criteria.c index 5678b0e7..89727783 100644 --- a/criteria.c +++ b/criteria.c @@ -73,8 +73,7 @@ bool parse_boolean(const char *string, bool *out) { if (strcasecmp(string, "true") == 0 || strcmp(string, "1") == 0) { *out = true; return true; - } - else if (strcasecmp(string, "false") == 0 || strcmp(string, "0") == 0) { + } else if (strcasecmp(string, "false") == 0 || strcmp(string, "0") == 0) { *out = false; return true; } @@ -86,12 +85,10 @@ bool parse_urgency(const char *string, enum mako_notification_urgency *out) { if (strcasecmp(string, "low") == 0) { *out = MAKO_NOTIFICATION_URGENCY_LOW; return true; - } - else if (strcasecmp(string, "normal") == 0) { + } else if (strcasecmp(string, "normal") == 0) { *out = MAKO_NOTIFICATION_URGENCY_NORMAL; return true; - } - else if (strcasecmp(string, "high") == 0) { + } else if (strcasecmp(string, "high") == 0) { *out = MAKO_NOTIFICATION_URGENCY_HIGH; return true; } @@ -168,12 +165,10 @@ bool parse_criteria(const char *string, struct mako_criteria *criteria) { if (state & MAKO_PARSE_STATE_QUOTE) { fprintf(stderr, "Unmatched quote in criteria definition\n"); return false; - } - else if (state & MAKO_PARSE_STATE_ESCAPE) { + } else if (state & MAKO_PARSE_STATE_ESCAPE) { fprintf(stderr, "Trailing backslash in criteria definition\n"); return false; - } - else { + } else { fprintf(stderr, "Got confused parsing criteria definition\n"); return false; } @@ -209,16 +204,14 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) { // Skip past the equal sign to the value itself. *value = '\0'; ++value; - } - else { + } else { // If there's no value, assume it's a boolean, and set the value // appropriately. This allows uniform parsing later on. if (*key == '!') { // Negated boolean, skip past the exclamation point. ++key; value = "false"; - } - else { + } else { value = "true"; } } @@ -233,31 +226,26 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) { criteria->app_name = strdup(value); criteria->spec.app_name = true; return true; - } - else if (strcmp(key, "app-icon") == 0) { + } else if (strcmp(key, "app-icon") == 0) { criteria->app_icon = strdup(value); criteria->spec.app_icon = true; return true; - } - else if (strcmp(key, "urgency") == 0) { + } else if (strcmp(key, "urgency") == 0) { if (!parse_urgency(value, &criteria->urgency)) { fprintf(stderr, "Invalid urgency value '%s'", value); return false; } criteria->spec.urgency = true; return true; - } - else if (strcmp(key, "category") == 0) { + } else if (strcmp(key, "category") == 0) { criteria->category = strdup(value); criteria->spec.category = true; return true; - } - else if (strcmp(key, "desktop-entry") == 0) { + } else if (strcmp(key, "desktop-entry") == 0) { criteria->desktop_entry = strdup(value); criteria->spec.desktop_entry = true; return true; - } - else { + } else { // Anything left must be one of the boolean fields, defined using // standard syntax. Continue on. } @@ -271,12 +259,10 @@ bool apply_criteria_field(struct mako_criteria *criteria, char *token) { } criteria->spec.actionable = true; return true; - } - else { + } else { if (bare_key) { fprintf(stderr, "Invalid boolean criteria field '%s'\n", key); - } - else { + } else { fprintf(stderr, "Invalid criteria field '%s'\n", key); } return false; diff --git a/dbus/xdg.c b/dbus/xdg.c index e3c241e6..e5739a15 100644 --- a/dbus/xdg.c +++ b/dbus/xdg.c @@ -220,8 +220,7 @@ static int handle_notify(sd_bus_message *msg, void *data, // case is that it has an empty style, so bail. fprintf(stderr, "Failed to apply criteria\n"); return -1; - } - else if (match_count == 0) { + } else if (match_count == 0) { // This should be impossible, since the global criteria is always // present in a mako_config and matches everything. fprintf(stderr, "Notification matched zero criteria?!\n"); diff --git a/render.c b/render.c index 2e9e7620..027b362c 100644 --- a/render.c +++ b/render.c @@ -151,8 +151,7 @@ int render(struct mako_state *state, struct pool_buffer *buffer, int scale) { if (i > 0) { if (style->margin.top > pending_bottom_margin) { total_height += style->margin.top; - } - else { + } else { total_height += pending_bottom_margin; } } @@ -188,8 +187,7 @@ int render(struct mako_state *state, struct pool_buffer *buffer, int scale) { if (style.margin.top > pending_bottom_margin) { total_height += style.margin.top; - } - else { + } else { total_height += pending_bottom_margin; } From 0840c931bec50dfc2ed6fe79c07a22b646bad855 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Mon, 11 Jun 2018 21:31:03 -0400 Subject: [PATCH 32/37] Combine unnecessary case --- criteria.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/criteria.c b/criteria.c index 89727783..6511d5b4 100644 --- a/criteria.c +++ b/criteria.c @@ -127,9 +127,6 @@ bool parse_criteria(const char *string, struct mako_criteria *criteria) { state = MAKO_PARSE_STATE_NORMAL; break; case ' ': - token[token_location] = ch; - ++token_location; - break; default: token[token_location] = ch; ++token_location; From fa26e3b422866ed92edf31bbc608a9a6f56bd9bb Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Mon, 11 Jun 2018 21:33:28 -0400 Subject: [PATCH 33/37] token_location is a size_t --- criteria.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/criteria.c b/criteria.c index 6511d5b4..b01bc951 100644 --- a/criteria.c +++ b/criteria.c @@ -103,7 +103,7 @@ bool parse_criteria(const char *string, struct mako_criteria *criteria) { int token_max_length = strlen(string) + 1; char token[token_max_length]; memset(token, 0, token_max_length); - int token_location = 0; + size_t token_location = 0; enum mako_parse_state state = MAKO_PARSE_STATE_NORMAL; const char *location = string; From 172cb7d69c1ca0b1db7d5aa3dd347e3150180e86 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Mon, 11 Jun 2018 21:35:43 -0400 Subject: [PATCH 34/37] apply_each_criteria returns ssize_t --- criteria.c | 4 ++-- include/criteria.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/criteria.c b/criteria.c index b01bc951..cf482d6f 100644 --- a/criteria.c +++ b/criteria.c @@ -280,9 +280,9 @@ struct mako_criteria *global_criteria(struct mako_config *config) { // Iterate through `criteria_list`, applying the style from each matching // criteria to `notif`. Returns the number of criteria that matched, or -1 if // a failure occurs. -int apply_each_criteria(struct wl_list *criteria_list, +ssize_t apply_each_criteria(struct wl_list *criteria_list, struct mako_notification *notif) { - int match_count = 0; + ssize_t match_count = 0; struct mako_criteria *criteria; wl_list_for_each(criteria, criteria_list, link) { diff --git a/include/criteria.h b/include/criteria.h index 7efc11eb..8f0d8ae5 100644 --- a/include/criteria.h +++ b/include/criteria.h @@ -59,7 +59,7 @@ bool parse_criteria(const char *string, struct mako_criteria *criteria); bool apply_criteria_field(struct mako_criteria *criteria, char *token); struct mako_criteria *global_criteria(struct mako_config *config); -int apply_each_criteria(struct wl_list *criteria_list, +ssize_t apply_each_criteria(struct wl_list *criteria_list, struct mako_notification *notif); #endif From 93264c8cc518b1c210a5b7a42b4e84c349633083 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Mon, 11 Jun 2018 21:44:21 -0400 Subject: [PATCH 35/37] Flip order of arguments to apply_style Also make the source style const --- config.c | 2 +- criteria.c | 2 +- include/config.h | 2 +- render.c | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/config.c b/config.c index 7718f2bf..94b31b70 100644 --- a/config.c +++ b/config.c @@ -80,7 +80,7 @@ void finish_style(struct mako_style *style) { // Update `target` with the values specified in `style`. If a failure occurs, // `target` will remain unchanged. -bool apply_style(struct mako_style *style, struct mako_style *target) { +bool apply_style(struct mako_style *target, const struct mako_style *style) { // Try to duplicate strings up front in case allocation fails and we have // to bail without changing `target`. char *new_font = NULL; diff --git a/criteria.c b/criteria.c index cf482d6f..4d1ebb61 100644 --- a/criteria.c +++ b/criteria.c @@ -291,7 +291,7 @@ ssize_t apply_each_criteria(struct wl_list *criteria_list, } ++match_count; - if (!apply_style(&criteria->style, ¬if->style)) { + if (!apply_style(¬if->style, &criteria->style)) { return -1; } } diff --git a/include/config.h b/include/config.h index b2de30b4..fbf9a389 100644 --- a/include/config.h +++ b/include/config.h @@ -81,7 +81,7 @@ void finish_config(struct mako_config *config); void init_default_style(struct mako_style *style); void init_empty_style(struct mako_style *style); void finish_style(struct mako_style *style); -bool apply_style(struct mako_style *style, struct mako_style *target); +bool apply_style(struct mako_style *target, const struct mako_style *style); int parse_config_arguments(struct mako_config *config, int argc, char **argv); int load_config_file(struct mako_config *config); diff --git a/render.c b/render.c index 027b362c..9da10e72 100644 --- a/render.c +++ b/render.c @@ -182,8 +182,8 @@ int render(struct mako_state *state, struct pool_buffer *buffer, int scale) { // by apply_each_criteria. struct mako_style style; init_empty_style(&style); - apply_style(&global_criteria(config)->style, &style); - apply_style(&config->hidden_style, &style); + apply_style(&style, &global_criteria(config)->style); + apply_style(&style, &config->hidden_style); if (style.margin.top > pending_bottom_margin) { total_height += style.margin.top; From 9c7fe4777cbd7a97f31959e0847134d0a8840591 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Tue, 12 Jun 2018 19:54:00 -0400 Subject: [PATCH 36/37] Update man page --- mako.1.scd | 93 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 67 insertions(+), 26 deletions(-) diff --git a/mako.1.scd b/mako.1.scd index c9da007b..08d93306 100644 --- a/mako.1.scd +++ b/mako.1.scd @@ -19,6 +19,31 @@ dismissed with a click or via *makoctl*(1). *-h, --help* Show help message and quit. +# GLOBAL CONFIGURATION OPTIONS + +*--max-visible* _n_ + Set maximum number of visible notifications to _n_. Older notifications will + be hidden. If -1, all notifications are visible. + + Default: 5 + +*--sort* _+/-time_ | _+/-priority_ + Sorts incoming notifications by time and/or priority in ascending(+) + or descending(-) order. + + Default: -time + +*--output* _name_ + Show notifications on the specified output. If empty, notifications will + appear on the focused output. + + Requires the compositor to support the Wayland protocol + xdg-output-unstable-v1 version 2. + + Default: "" + +# STYLE OPTIONS + *--font* _font_ Set font to _font_, in Pango format. @@ -79,19 +104,6 @@ dismissed with a click or via *makoctl*(1). Default: %s\\n%b -*--max-visible* _n_ - Set maximum number of visible notifications to _n_. Older notifications will - be hidden. If -1, all notifications are visible. - - Default: 5 - -*--sort* _+/-time_ | _+/-priority_ - Sorts incoming notifications by time and/or priority in ascending(+) - or descending(-) order. - - Default: -time - - *--default-timeout* _timeout_ Set the default timeout to _timeout_ in milliseconds. To disable the timeout, set it to zero. @@ -104,15 +116,6 @@ dismissed with a click or via *makoctl*(1). Default: 0 -*--output* _name_ - Show notifications on the specified output. If empty, notifications will - appear on the focused output. - - Requires the compositor to support the Wayland protocol - xdg-output-unstable-v1 version 2. - - Default: "" - # CONFIG FILE The config file is located at *~/.config/mako/config* or at @@ -124,11 +127,49 @@ Is equivalent to passing *--key=value* to mako from the command line. Empty lines and lines that begin with # are ignored. -The hidden notifications placeholder can be configured after a *[hidden]* -section: +# CRITERIA + +In addition to the set of options at the top of the file, the config file may +contain zero or more sections, each containing any combination of the +*STYLE OPTIONS*. The sections, called criteria, are defined with an INI-like +square bracket syntax. The brackets may contain any number of fields, like so: + + \[field=value field2=value2 ...\] + +When a notification is received, it will be compared to the fields defined in +each criteria. If all of the fields match, the style options within will be +applied to the notification. Fields not included in the criteria are not +considered during the match. A notification may match any number of criteria. +This matching occurs in the order the criteria are defined in the config file, +meaning that if multiple criteria match a notification, the last occurrence of +any given style option will "win". + +The following fields are available in critiera: + +- _app-name_ (string) +- _app-icon_ (string) +- _urgency_ (one of "low", "normal", "high") +- _category_ (string) +- _desktop-entry_ (string) +- _actionable_ (boolean) + +If a field's value contains special characters, they may be escaped with a +backslash, or quoted: + + \[app-name="Google Chrome"\] + + \[app-name=Google\\ Chrome\] + +Quotes within quotes may also be escaped, and a literal backslack may be +specified as \\\\. No spaces are allowed around the equal sign. Escaping equal +signs within values is unnecessary. + +Additionally, boolean values may be specified using any of true/false, 0/1, or +as bare words: + + \[actionable=true\] \[actionable=1\] \[actionable\] - \[hidden\] - format=(%h more) + \[actionable=false\] \[actionable=0\] \[!actionable\] # COLORS From 9d89ba218e4a8538ca29833660f5e06d6735dd9a Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Wed, 13 Jun 2018 16:32:45 -0400 Subject: [PATCH 37/37] Add hidden back to man page --- mako.1.scd | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mako.1.scd b/mako.1.scd index 08d93306..6586223e 100644 --- a/mako.1.scd +++ b/mako.1.scd @@ -152,6 +152,9 @@ The following fields are available in critiera: - _category_ (string) - _desktop-entry_ (string) - _actionable_ (boolean) +- _hidden_ (boolean) + - _hidden_ is special, it defines the style for the placeholder shown when + the number of notifications exceeds _max-visible_. If a field's value contains special characters, they may be escaped with a backslash, or quoted: