diff options
| author | Federico Angelilli <code@fedang.net> | 2024-09-09 00:23:15 +0200 |
|---|---|---|
| committer | Federico Angelilli <code@fedang.net> | 2024-09-09 00:23:15 +0200 |
| commit | bdfa5a92e2bededb520dad6741e81128c67fb502 (patch) | |
| tree | 989e98319e8cd62db890c65680b4eb2635e7d9b5 | |
| parent | f1f89a745fb46c4b387f3cb6c094e9dd38edf9f6 (diff) | |
Parse block list and resolve groups
| -rw-r--r-- | comet.conf | 10 | ||||
| -rw-r--r-- | src/block.h | 2 | ||||
| -rw-r--r-- | src/config.c | 271 | ||||
| -rw-r--r-- | src/config.h | 8 |
4 files changed, 206 insertions, 85 deletions
@@ -6,9 +6,17 @@ height = 40 background = #abc + blocks = date, perf + [jib] sus = false +[block.perf] + type = group + color = #DC143C + spacing = 20 + blocks = cpu, ram, disk + [block.cpu] type = text text = CPU @@ -28,7 +36,7 @@ text-color = #fff path = / -[block.disk] +[block.date] type = date text = "%A %d %B %H:%M" color = #18baf2 diff --git a/src/block.h b/src/block.h index 715e6c4..845ccfd 100644 --- a/src/block.h +++ b/src/block.h @@ -44,7 +44,7 @@ typedef void (*block_finalize_t)(block_t *block); struct block { block_type_t type; char *label; - bool active; + bool resolved; bool hidden; void *state; struct timespec update_interval; diff --git a/src/config.c b/src/config.c index cea99af..93d123f 100644 --- a/src/config.c +++ b/src/config.c @@ -23,10 +23,12 @@ typedef enum { static const config_entry_t bar_entries[] = { { "width", CONFIG_UINT, NULL, offsetof(config_t, width) }, { "height", CONFIG_UINT, NULL, offsetof(config_t, height) }, + { "spacing", CONFIG_UINT, NULL, offsetof(config_t, spacing) }, { "font", CONFIG_STRING, NULL, offsetof(config_t, font) }, { "monitor", CONFIG_STRING, NULL, offsetof(config_t, monitor) }, { "override-redirect", CONFIG_BOOL, NULL, offsetof(config_t, override_redirect) }, { "background", CONFIG_COLOR, NULL, offsetof(config_t, background) }, + { "blocks", CONFIG_LIST, NULL, offsetof(config_t, children) }, { 0 }, }; @@ -44,7 +46,8 @@ static const config_entry_t block_entries[] = { }; static const config_entry_t block_group_entries[] = { - { "spacing", CONFIG_INT, NULL, offsetof(block_t, group.spacing) }, + { "spacing", CONFIG_INT, NULL, offsetof(block_t, group.spacing) }, + { "blocks", CONFIG_LIST, NULL, offsetof(block_t, group.children) }, { 0 }, }; @@ -197,10 +200,124 @@ static bool config_read_time(const char *value, struct timespec *result) return false; } +// TODO: Handle quotation marks +static bool config_read_list(const char *value, char ***result) +{ + size_t count = 0; + for (size_t i = 0; value[i] != '\0'; ++i) + count += value[i] == ','; + + char **list = calloc(count + 2, sizeof(char *)); + size_t n = 0; + + char *state, *copy = strcopy(value); + for (char *string = copy; ; string = NULL) { + char *token = strtok_r(string, ",", &state); + + if (token == NULL) + break; + + while (isspace(*token)) token++; + char *end = token + strlen(token); + while (isspace(*end)) end--; + *end = '\0'; + + if (!config_read_string(token, &list[n++])) { + free(list); + return false; + } + } + + list[n] = NULL; + *result = list; + return true; +} + +static config_status_t config_read_any(void *result, config_type_t type, void *data, + const char *section, const char *key, const char *value) +{ + bool nullable = type == CONFIG_STRING; + if ((value == NULL || *value == '\0') && !nullable) + return CONFIG_INVALID; + + switch (type) { + case CONFIG_STRING: + // NOTE: The previous string is freed by config_read_string + if (config_read_string(value, (char **)result)) { + log_debug("Set '%s.%s' to '%s'", section, key, *(char **)result); + return CONFIG_SUCCESS; + } + break; + + case CONFIG_INT: + if (config_read_int(value, (int *)result)) { + log_debug("Set '%s.%s' to '%d'", section, key, *(int *)result); + return CONFIG_SUCCESS; + } + break; + + case CONFIG_UINT: + if (config_read_uint(value, (unsigned int *)result)) { + log_debug("Set '%s.%s' to '%u'", section, key, *(unsigned int *)result); + return CONFIG_SUCCESS; + } + break; + + case CONFIG_DOUBLE: + if (config_read_double(value, (double *)result)) { + log_debug("Set '%s.%s' to '%lf'", section, key, *(double *)result); + return CONFIG_SUCCESS; + } + break; + + case CONFIG_BOOL: + if (config_read_bool(value, (bool *)result)) { + log_debug("Set '%s.%s' to '%s'", section, key, *(bool *)result ? "true" : "false"); + return CONFIG_SUCCESS; + } + break; + + case CONFIG_COLOR: + if (config_read_color(value, (color_t *)result)) { + char *color = color_to_string((color_t *)result); + log_debug("Set '%s.%s' to '%s'", section, key, color); + free(color); + return CONFIG_SUCCESS; + } + break; + + case CONFIG_ENUM: + if (config_read_enum(value, (config_enum_t *)data, (int *)result)) { + log_debug("Set '%s.%s' to '%d'", section, key, *(int *)result); + return CONFIG_SUCCESS; + } + break; + + case CONFIG_TIME: + if (config_read_time(value, (struct timespec *)result)) { + log_debug("Set '%s.%s' to '%s'", section, key, value); + return CONFIG_SUCCESS; + } + break; + + case CONFIG_LIST: + if (config_read_list(value, (char ***)result)) { + log_debug("Set '%s.%s' to '%s'", section, key, value); + return CONFIG_SUCCESS; + } + break; + + default: + unreachable(); + } + + return CONFIG_INVALID; +} + static config_status_t config_read_entry(const config_entry_t *entries, void *result, const char **type, const char *section, const char *key, const char *value) { - const char *types[] = { + static const char *types[] = { "string", "integer", "unsigned integer", @@ -209,6 +326,7 @@ static config_status_t config_read_entry(const config_entry_t *entries, void *re "color", "enum", "time", + "list", }; for (int i = 0; entries[i].key != NULL; i++) { @@ -216,76 +334,8 @@ static config_status_t config_read_entry(const config_entry_t *entries, void *re if (type != NULL) *type = types[entries[i].type]; - bool nullable = entries[i].type == CONFIG_STRING; - if ((value == NULL || *value == '\0') && !nullable) - return CONFIG_INVALID; - result += entries[i].offset; - switch (entries[i].type) { - case CONFIG_STRING: - // NOTE: The previous string is freed by config_read_string - if (config_read_string(value, (char **)result)) { - log_debug("Set '%s.%s' to '%s'", section, key, *(char **)result); - return CONFIG_SUCCESS; - } - break; - - case CONFIG_INT: - if (config_read_int(value, (int *)result)) { - log_debug("Set '%s.%s' to '%d'", section, key, *(int *)result); - return CONFIG_SUCCESS; - } - break; - - case CONFIG_UINT: - if (config_read_uint(value, (unsigned int *)result)) { - log_debug("Set '%s.%s' to '%u'", section, key, *(unsigned int *)result); - return CONFIG_SUCCESS; - } - break; - - case CONFIG_DOUBLE: - if (config_read_double(value, (double *)result)) { - log_debug("Set '%s.%s' to '%lf'", section, key, *(double *)result); - return CONFIG_SUCCESS; - } - break; - - case CONFIG_BOOL: - if (config_read_bool(value, (bool *)result)) { - log_debug("Set '%s.%s' to '%s'", section, key, *(bool *)result ? "true" : "false"); - return CONFIG_SUCCESS; - } - break; - - case CONFIG_COLOR: - if (config_read_color(value, (color_t *)result)) { - char *color = color_to_string((color_t *)result); - log_debug("Set '%s.%s' to '%s'", section, key, color); - free(color); - return CONFIG_SUCCESS; - } - break; - - case CONFIG_ENUM: - if (config_read_enum(value, (config_enum_t *)entries[i].data, (int *)result)) { - log_debug("Set '%s.%s' to '%d'", section, key, *(int *)result); - return CONFIG_SUCCESS; - } - break; - - case CONFIG_TIME: - if (config_read_time(value, (struct timespec *)result)) { - log_debug("Set '%s.%s' to '%s'", section, key, value); - return CONFIG_SUCCESS; - } - break; - - default: - unreachable(); - } - - return CONFIG_INVALID; + return config_read_any(result, entries[i].type, entries[i].data, section, key, value); } } @@ -313,6 +363,7 @@ void config_init(config_t *config) .monitor = NULL, .height = 50, .width = 100, + .spacing = 10, .override_redirect = false, }; @@ -344,12 +395,20 @@ void config_read(config_t *config, FILE *file) block = &config->blocks[config->n_blocks - 1]; char *label = strcopy(section + 6); - if (label == NULL || *label == '\0') { + if (label == NULL || *label == '\0' || !strcmp(label, "bar")) { ++errors; - log_value_error("Block section must have a non-empty label", + log_value_error("Block section must have a valid label", "s:section", section, "i:line", ini.line); - free(label); + } else { + for (size_t i = 0; i < config->n_blocks - 1; i++) { + if (!strcmp(label, config->blocks[i].label)) { + ++errors; + log_value_error("Block section must have a unique label", + "s:section", section, + "i:line", ini.line); + } + } } if ((key = any_ini_stream_next_key(&ini)) == NULL || strcmp(key, "type")) { @@ -468,19 +527,69 @@ skip_pair: log_panic("Config file contained %d errors", n_errors); } +static bool config_resolve_children(config_t *config, block_t *block) +{ + block->resolved = true; + if (block->type == BLOCK_TEXT) + return true; + + char **children = (char **)block->group.children; + block->group.children = NULL; + block->group.n_children = 0; + + if (children == NULL) + return true; + + size_t n = 0; + for ( ; children[n] != NULL; n++); + + block->group.n_children = n; + block->group.children = malloc(n * sizeof(block_t *)); + + for (size_t i = 0; i < n; i++) { + for (size_t j = 0; j < config->n_blocks; j++) { + if (&config->blocks[j] == block) + continue; + + if (!strcmp(children[i], config->blocks[j].label)) { + if (config->blocks[j].resolved) { + log_error("Block '%s' can only be referenced by one block", config->blocks[j].label); + return false; + } + + log_debug("Block '%s' is parent to '%s'", block->label, config->blocks[j].label); + block->group.children[i] = &config->blocks[j]; + + if (!config_resolve_children(config, &config->blocks[j])) + return false; + goto next; + } + } + + log_error("Block '%s' not found", children[i]); + return false; +next: + } + + return true; +} + void config_resolve(config_t *config, block_t *block) { int errors = 0; - block->label = strcopy("main"); + block->label = strcopy("bar"); block->type = BLOCK_GROUP; block->min_width = block->max_width = config->width; - block->group.spacing = 10; - block->group.n_children = config->n_blocks; - block->group.children = malloc(config->n_blocks * sizeof(block_t *)); - - for (int i = 0; i < config->n_blocks; i++) - block->group.children[i] = &config->blocks[i]; + block->group.spacing = config->spacing; + block->group.children = (void *)config->children; + + if (config->children == NULL) { + errors++; + log_error("Config should specify at least one block"); + } else { + errors += !config_resolve_children(config, block); + } if (errors > 0) log_panic("Config could not be resolved"); diff --git a/src/config.h b/src/config.h index 1e674d9..726006e 100644 --- a/src/config.h +++ b/src/config.h @@ -17,6 +17,7 @@ typedef enum { CONFIG_COLOR, CONFIG_ENUM, CONFIG_TIME, + CONFIG_LIST, } config_type_t; typedef struct { @@ -31,10 +32,13 @@ typedef struct { block_t *blocks; char *font; char *monitor; - uint32_t height; - uint32_t width; bool override_redirect; color_t background; + + uint32_t height; + uint32_t width; + uint32_t spacing; + char **children; } config_t; typedef struct { |
