#include #include #include "block.h" #include "util.h" #include "action.h" #include "config.h" #include "any_log.h" extern const block_scheme_t block_text_scheme; extern const block_scheme_t block_group_scheme; extern const block_scheme_t block_ram_scheme; extern const block_scheme_t block_fs_scheme; extern const block_scheme_t block_date_scheme; extern const block_scheme_t block_script_scheme; extern const block_scheme_t block_slider_scheme; const block_scheme_t *block_schemes[] = { &block_text_scheme, &block_group_scheme, &block_ram_scheme, &block_fs_scheme, &block_date_scheme, &block_script_scheme, &block_slider_scheme, NULL, }; void block_update(block_t *block) { if (block->update_fn != NULL) { struct timespec now, diff; timespec_get(&now, TIME_UTC); diff = timespec_diff(now, block->update_last); if (timespec_greater(diff, block->update_interval)) { log_value_trace("Updating block", "s:label", block->label, "g:ts", ANY_LOG_FORMATTER(timespec_print), &now); block->update_fn(block); block->update_last = now; } } // NOTE: Block spec should handle its children by itself... // if (block->type == BLOCK_GROUP) { block_group_t *group = (block_group_t *)block; for (size_t i = 0; i < group->n_children; i++) block_update(group->children[i]); } } int block_validate_entry(block_t *block, const config_entry_t *entry) { if (entry->checks == NULL) return 0; int errors = 0; for (size_t i = 0; entry->checks[i].type != CHECK_NONE; i++) { void *result = (void *)block + entry->offset; switch (entry->checks[i].type) { case CHECK_POSITIVE: { assert(entry->type == CONFIG_INT || entry->type == CONFIG_DOUBLE); bool positive = entry->type == CONFIG_INT ? *(int *)result >= 0 : *(double *)result >= 0.0; if (!positive) { log_error("Value 'block.%s.%s' violates constraint '%s'", block->label, entry->key, "positive"); errors++; } break; } case CHECK_IN_RANGE: { assert(entry->type == CONFIG_INT || entry->type == CONFIG_UINT || entry->type == CONFIG_DOUBLE); double min = entry->checks[i].range.min, max = entry->checks[i].range.max, value = entry->type == CONFIG_INT ? *(int *)result : entry->type == CONFIG_UINT ? *(unsigned int *)result : *(double *)result; if (value <= min || value >= max) { log_error("Value 'block.%s.%s' violates constraint '%s(%g, %g)'", block->label, entry->key, "in_range", min, max); errors++; } break; } case CHECK_GT_OTHER: { void *other = (void *)block + entry->checks[i].compare.offset; assert(entry->type == CONFIG_INT || entry->type == CONFIG_UINT || entry->type == CONFIG_DOUBLE); assert(entry->type == entry->checks[i].compare.type); double value1 = entry->type == CONFIG_INT ? *(int *)result : entry->type == CONFIG_UINT ? *(unsigned int *)result : *(double *)result; double value2 = entry->type == CONFIG_INT ? *(int *)other : entry->type == CONFIG_UINT ? *(unsigned int *)other : *(double *)other; if (value1 <= value2) { log_error("Value 'block.%s.%s' violates constraint '%s(%p)'", block->label, entry->key, "gt_memebr", other); errors++; } break; } default: unreachable(); } } return errors; } int block_validate(block_t *block, config_t *config) { if (block->scheme->validate_fn != NULL) return block->scheme->validate_fn(block, config); if (block->scheme->entries == NULL) return 0; int errors = 0; for (size_t i = 0; block->scheme->entries[i].key != NULL; i++) { errors += block_validate_entry(block, &block->scheme->entries[i]); } return errors; } bool block_resolve_action(block_t *block, config_t *config, action_t **action) { if (action == NULL || *action == NULL) return true; char *label = (char *)*action; bool result = config_resolve_action(config, label, action); if (!result) { log_error("Action '%s' not found (referenced by block '%s')", label, block->label); } free(label); return result; } bool block_resolve(block_t *block, config_t *config) { if (block->resolved) return true; block->resolved = true; for (size_t i = 0; i < EVENT_MAX; i++) { if (!block_resolve_action(block, config, &block->actions[i])) return false; } return block->scheme->resolve_fn == NULL || block->scheme->resolve_fn(block, config); } bool block_change(block_t *block, config_t *config, const char *key, const char *value) { extern const config_entry_t block_entries[]; // Ignore notify invocations config_status_t status = key == NULL ? CONFIG_UNKNOWN : config_read_entry(block_entries, block, NULL, key, value); if (status == CONFIG_UNKNOWN && block->scheme->change_fn != NULL) status = block->scheme->change_fn(block, config, key, value); return status == CONFIG_SUCCESS || (!config->action_strict_set && status == CONFIG_UNKNOWN); } void block_free(block_t *block) { if (block->scheme->clean_fn != NULL) block->scheme->clean_fn(block); gradient_clean(&block->bg_color); gradient_clean(&block->line_color); free(block->label); free(block); }