Skip to content

Commit

Permalink
settings: rulify the settings
Browse files Browse the repository at this point in the history
This commit tries to make the setttings more consistent by implementing
the settings as rules everywhere where it's possible. This means the
rule code from settings.c can be removed and everything is defined in
settings_data.h. Rules are now also allowed in the global and urgency
sections. This means dunst-project#644 is now fixed.

The special sections are implemented as rules by giving them implied
filters. The global sections has no filters and the urgency sections
only have filters of their respective urgency. To avoid confusion, no
other filters are allowed in these sections.

Fixes dunst-project#644
  • Loading branch information
fwsmit committed Mar 8, 2021
1 parent 72c5890 commit 617a600
Show file tree
Hide file tree
Showing 7 changed files with 355 additions and 235 deletions.
83 changes: 43 additions & 40 deletions src/option_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,22 +267,18 @@ int str_to_bool(const char *value){
}
}

bool is_special_section(const char* s) {
for (size_t i = 0; i < G_N_ELEMENTS(special_sections); i++) {
if (STR_EQ(special_sections[i], s)) {
return true;
}
}
return false;
}

int get_setting_id(const char *key, const char *section) {
int error_code = 0;
int partial_match_id = -1;
bool is_rule = !is_special_section(section);
bool match_section = section && is_special_section(section);
if (!match_section) {
LOG_D("not matching section %s", section);
}
for (int i = 0; i < G_N_ELEMENTS(allowed_settings); i++) {
if (strcmp(allowed_settings[i].name, key) == 0) {
/* LOG_I("%s name exists with id %i", allowed_settings[i].name, i); */
bool is_rule = allowed_settings[i].rule_offset > 0;

// a rule matches every section
if (is_rule || strcmp(section, allowed_settings[i].section) == 0) {
return i;
} else {
Expand Down Expand Up @@ -403,9 +399,7 @@ int set_rule_value(struct rule* r, struct setting setting, char* value) {
bool set_rule(struct setting setting, char* value, char* section) {
struct rule *r = get_rule(section);
if (!r) {
r = rule_new();
rules = g_slist_insert(rules, r, -1);
r->name = g_strdup(section);
r = rule_new(section);
LOG_D("Creating new rule '%s'", section);
}

Expand All @@ -414,8 +408,7 @@ bool set_rule(struct setting setting, char* value, char* section) {

void set_defaults() {
for (int i = 0; i < G_N_ELEMENTS(allowed_settings); i++) {
bool is_rule = !allowed_settings[i].value;
if (is_rule) // don't set default if it's only a rule
if (!allowed_settings[i].value) // don't set default if it's only a rule
continue;

if(!set_setting(allowed_settings[i], allowed_settings[i].default_value)) {
Expand All @@ -427,34 +420,44 @@ void set_defaults() {
void save_settings() {
for (int i = 0; i < section_count; i++) {
const struct section curr_section = sections[i];
if (is_special_section(curr_section.name)) {
// special section, so don't interpret as rule
for (int j = 0; j < curr_section.entry_count; j++) {
const struct entry curr_entry = curr_section.entries[j];
int setting_id = get_setting_id(curr_entry.key, curr_section.name);
if (setting_id < 0){
if (setting_id == -1) {
LOG_W("Setting %s in section %s doesn't exist", curr_entry.key, curr_section.name);
}
continue;
}

struct setting curr_setting = allowed_settings[setting_id];
set_setting(curr_setting, curr_entry.value);
if (is_deprecated_section(curr_section.name)) {
LOG_W("Section %s is deprecated. Ignoring", curr_section.name);
continue;
}

for (int j = 0; j < curr_section.entry_count; j++) {
const struct entry curr_entry = curr_section.entries[j];
int setting_id = get_setting_id(curr_entry.key, curr_section.name);
struct setting curr_setting = allowed_settings[setting_id];
if (setting_id < 0){
if (setting_id == -1) {
LOG_W("Setting %s in section %s doesn't exist", curr_entry.key, curr_section.name);
}
continue;
}
} else {
// interpret this section as a rule
for (int j = 0; j < curr_section.entry_count; j++) {
const struct entry curr_entry = curr_section.entries[j];
int setting_id = get_setting_id(curr_entry.key, curr_section.name);
if (setting_id < 0){
if (setting_id == -1) {
LOG_W("%s is not a valid rule", curr_entry.key);

bool is_rule = curr_setting.rule_offset > 0;
if (is_special_section(curr_section.name)) {
if (is_rule) {
// set as a rule, but only if it's not a filter
if (rule_offset_is_action(curr_setting.rule_offset)) {
LOG_D("Adding rule '%s = %s' to special section %s",
curr_entry.key,
curr_entry.value,
curr_section.name);
set_rule(curr_setting, curr_entry.value, curr_section.name);
} else {
LOG_W("Cannot use filtering rules in special section. Ignoring %s in section %s.",
curr_entry.key,
curr_section.name);
}
continue;
} else {
// set as a regular setting
set_setting(curr_setting, curr_entry.value);
}

struct setting curr_setting = allowed_settings[setting_id];
} else {
// interpret this section as a rule
LOG_D("Adding rule '%s = %s' to section %s",
curr_entry.key,
curr_entry.value,
Expand Down
55 changes: 53 additions & 2 deletions src/rules.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

#include <fnmatch.h>
#include <glib.h>
#include <stddef.h>

#include "dunst.h"
#include "utils.h"
#include "settings_data.h"
#include "log.h"

GSList *rules = NULL;

Expand Down Expand Up @@ -75,10 +77,42 @@ void rule_apply_all(struct notification *n)
}
}

struct rule *rule_new(void)
bool rule_apply_special_filters(struct rule *r, const char *name) {
if (is_deprecated_section(name)) // shouldn't happen, but just in case
return false;

if (strcmp(name, "global") == 0) {
// no filters for global section
return true;
}
if (strcmp(name, "urgency_low") == 0) {
r->msg_urgency = URG_LOW;
return true;
}
if (strcmp(name, "urgency_normal") == 0) {
r->msg_urgency = URG_NORM;
return true;
}
if (strcmp(name, "urgency_critical") == 0) {
r->msg_urgency = URG_CRIT;
return true;
}

return false;
}

struct rule *rule_new(const char *name)
{
struct rule *r = g_malloc0(sizeof(struct rule));
*r = default_rule;
*r = empty_rule;
rules = g_slist_insert(rules, r, -1);
r->name = g_strdup(name);
if (is_special_section(name)) {
bool success = rule_apply_special_filters(r, name);
if (!success) {
LOG_M("Could not apply special filters for section %s", name);
}
}
return r;
}

Expand Down Expand Up @@ -115,4 +149,21 @@ struct rule *get_rule(const char* name) {
return NULL;
}

/**
* see rules.h
*/
bool rule_offset_is_action(const size_t offset) {
const size_t first_action = offsetof(struct rule, timeout);
const size_t last_action = offsetof(struct rule, set_stack_tag);
return (offset >= first_action) && (offset <= last_action);
}

/**
* see rules.h
*/
bool rule_offset_is_filter(const size_t offset) {
const size_t first_filter = offsetof(struct rule, appname);
return (offset >= first_filter) && !rule_offset_is_action(offset);
}

/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
34 changes: 29 additions & 5 deletions src/rules.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@
#include "settings.h"

struct rule {
// Since there's heavy use of offsets from this class, both in rules.c
// and in settings_data.h the layout of the class should not be
// changed, unless it's well considered and tested. See the comments
// below for what should not be changed.

// This has to be the first member, see struct setting.rule_offset.
char *name;

/* filters */
char *appname;
char *appname; // this has to be the first filter, see rules.c
char *summary;
char *body;
char *icon;
Expand All @@ -23,7 +28,7 @@ struct rule {
int msg_urgency;

/* actions */
gint64 timeout;
gint64 timeout; // this has to be the first action
enum urgency urgency;
enum markup_mode markup;
int history_ignore;
Expand All @@ -38,17 +43,22 @@ struct rule {
const char *format;
const char *script;
enum behavior_fullscreen fullscreen;
char *set_stack_tag;
char *set_stack_tag; // this has to be the last action
};

extern GSList *rules;

/**
* Allocate a new rule. The rule is fully initialised.
* Allocate a new rule with given name. The rule is fully initialised. If the
* name is one of a special section (see settings_data.h), the rule is
* initialized with some filters, and you should not add any filters after
* that.
*
* @param name Name of the rule.
*
* @returns A new initialised rule.
*/
struct rule *rule_new(void);
struct rule *rule_new(const char *name);

void rule_apply(struct rule *r, struct notification *n);
void rule_apply_all(struct notification *n);
Expand All @@ -61,5 +71,19 @@ bool rule_matches_notification(struct rule *r, struct notification *n);
*/
struct rule *get_rule(const char* name);

/**
* Check if a rule is an action
*
* @returns a boolean if the rule is an action
*/
bool rule_offset_is_action(const size_t offset);

/**
* Check if a rule is an filter
*
* @returns a boolean if the rule is an filter
*/
bool rule_offset_is_filter(const size_t offset);

#endif
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
1 change: 1 addition & 0 deletions src/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ void print_rule(struct rule* r) {
LOG_D("summary %s", r->summary);
LOG_D("appname %s", r->appname);
LOG_D("script %s", r->script);
LOG_D("frame %s", r->fc);
}

void check_and_correct_settings(struct settings *s) {
Expand Down
Loading

0 comments on commit 617a600

Please sign in to comment.