ltk

GUI toolkit for X11 (WIP)
git clone git://lumidify.org/ltk.git (fast, but not encrypted)
git clone https://lumidify.org/ltk.git (encrypted, but very slow)
git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltk.git (over tor)
Log | Files | Refs | README | LICENSE

commit b44497a0e20b249923f01284f85163b61b7e7609
parent 210aa3d51d8dcc1e36c73d3404e032937271e0d6
Author: lumidify <nobody@lumidify.org>
Date:   Thu,  2 May 2024 16:10:04 +0200

Move theme config to main config file

This is also still really ugly.

Diffstat:
MLICENSE | 2+-
MMakefile | 5+----
Mconfig.example/ltk.cfg | 32++++++++++++++++++++++++++++++++
Dconfig.example/theme.ini | 31-------------------------------
Msrc/ltk/button.c | 17+++--------------
Msrc/ltk/button.h | 4----
Msrc/ltk/config.c | 528+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Msrc/ltk/config.h | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/ltk/entry.c | 23++++++-----------------
Msrc/ltk/entry.h | 11-----------
Msrc/ltk/event_xlib.c | 6+++---
Dsrc/ltk/ini.c | 201-------------------------------------------------------------------------------
Dsrc/ltk/ini.h | 104-------------------------------------------------------------------------------
Msrc/ltk/label.c | 18+++---------------
Msrc/ltk/label.h | 4----
Msrc/ltk/ltk.c | 119+++++++++++--------------------------------------------------------------------
Msrc/ltk/menu.c | 74++++++++++++++++----------------------------------------------------------
Msrc/ltk/menu.h | 14--------------
Msrc/ltk/num.c | 2+-
Msrc/ltk/scrollbar.c | 17+++--------------
Msrc/ltk/scrollbar.h | 4----
Dsrc/ltk/theme.c | 211-------------------------------------------------------------------------------
Dsrc/ltk/theme.h | 71-----------------------------------------------------------------------
Asrc/ltk/widget_internal.h | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ltk/window.c | 37+++++++++++++------------------------
Msrc/ltk/window.h | 15+--------------
26 files changed, 584 insertions(+), 1097 deletions(-)

diff --git a/LICENSE b/LICENSE @@ -1,4 +1,4 @@ -See src/ltkd/khash.h, src/ltk/ini.*, src/ltk/stb_truetype.*, src/ltk/strtonum.c, +See src/ltkd/khash.h, src/ltk/stb_truetype.*, src/ltk/num.c, src/ltk/ctrlsel.*, and src/ltk/macros.h for third-party licenses. ISC License diff --git a/Makefile b/Makefile @@ -56,9 +56,7 @@ OBJ_LTK = \ src/ltk/rect.o \ src/ltk/widget.o \ src/ltk/ltk.o \ - src/ltk/ini.o \ src/ltk/button.o \ - src/ltk/theme.o \ src/ltk/graphics_xlib.o \ src/ltk/surface_cache.o \ src/ltk/event_xlib.o \ @@ -97,7 +95,6 @@ OBJ_TEST = examples/ltk/test.o HDR_LTK = \ src/ltk/button.h \ src/ltk/color.h \ - src/ltk/ini.h \ src/ltk/label.h \ src/ltk/rect.h \ src/ltk/widget.h \ @@ -107,7 +104,7 @@ HDR_LTK = \ src/ltk/stb_truetype.h \ src/ltk/text.h \ src/ltk/util.h \ - src/ltk/theme.h \ + src/ltk/widget_internal.h \ src/ltk/graphics.h \ src/ltk/surface_cache.h \ src/ltk/macros.h \ diff --git a/config.example/ltk.cfg b/config.example/ltk.cfg @@ -8,6 +8,38 @@ dpi-scale = 1.0 # In future: # text-editor = ... +[theme:window] +font-size = 12pt +bg = "#000000" +fg = "#FFFFFF" +font = "Liberation Mono" + +[theme:button] +border-width = 0.5mm +text-color = "#FFFFFF" +pad = 1mm +border = "#339999" +fill = "#113355" +border-pressed = "#FFFFFF" +fill-pressed = "#113355" +border-active = "#FFFFFF" +fill-active = "#738194" +border-disabled = "#FFFFFF" +fill-disabled = "#292929" + +[theme:label] +text-color = "#FFFFFF" +pad = 1mm + +[theme:scrollbar] +size = 3.5mm +bg = "#000000" +bg-disabled = "#555555" +fg = "#113355" +fg-pressed = "#113355" +fg-active = "#738194" +fg-disabled = "#292929" + [key-binding:window] # In future: # bind edit-text-external ... diff --git a/config.example/theme.ini b/config.example/theme.ini @@ -1,31 +0,0 @@ -[window] -font-size = 12pt -bg = #000000 -fg = #FFFFFF -font = Liberation Mono - -[button] -border-width = 0.5mm -text-color = #FFFFFF -pad = 1mm -border = #339999 -fill = #113355 -border-pressed = #FFFFFF -fill-pressed = #113355 -border-active = #FFFFFF -fill-active = #738194 -border-disabled = #FFFFFF -fill-disabled = #292929 - -[label] -text-color = #FFFFFF -pad = 1mm - -[scrollbar] -size = 3.5mm -bg = #000000 -bg-disabled = #555555 -fg = #113355 -fg-pressed = #113355 -fg-active = #738194 -fg-disabled = #292929 diff --git a/src/ltk/button.c b/src/ltk/button.c @@ -24,7 +24,6 @@ #include "memory.h" #include "rect.h" #include "text.h" -#include "theme.h" #include "util.h" #include "widget.h" @@ -97,21 +96,11 @@ static ltk_theme_parseinfo parseinfo[] = { {"pad", THEME_SIZE, {.size = &theme.pad}, {.size = {.val = 100, .unit = LTK_UNIT_MM}}, 0, MAX_BUTTON_PADDING, 0}, {"text-color", THEME_COLOR, {.color = &theme.text_color}, {.color = "#FFFFFF"}, 0, 0, 0}, }; -static int parseinfo_sorted = 0; - -int -ltk_button_ini_handler(ltk_renderdata *data, const char *prop, const char *value) { - return ltk_theme_handle_value(data, "button", prop, value, parseinfo, LENGTH(parseinfo), &parseinfo_sorted); -} - -int -ltk_button_fill_theme_defaults(ltk_renderdata *data) { - return ltk_theme_fill_defaults(data, "button", parseinfo, LENGTH(parseinfo)); -} void -ltk_button_uninitialize_theme(ltk_renderdata *data) { - ltk_theme_uninitialize(data, parseinfo, LENGTH(parseinfo)); +ltk_button_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len) { + *p = parseinfo; + *len = LENGTH(parseinfo); } static void diff --git a/src/ltk/button.h b/src/ltk/button.h @@ -30,10 +30,6 @@ typedef struct { ltk_text_line *tl; } ltk_button; -int ltk_button_ini_handler(ltk_renderdata *data, const char *prop, const char *value); -int ltk_button_fill_theme_defaults(ltk_renderdata *data); -void ltk_button_uninitialize_theme(ltk_renderdata *data); - ltk_button *ltk_button_create(ltk_window *window, char *text); #endif /* LTK_BUTTON_H */ diff --git a/src/ltk/config.c b/src/ltk/config.c @@ -24,15 +24,38 @@ #include "memory.h" #include "config.h" #include "sort_search.h" - -#include "entry.h" -#include "window.h" +#include "widget_internal.h" GEN_SORT_SEARCH_HELPERS(keybinding, ltk_keybinding_cb, text) -//GEN_SORT_SEARCH_HELPERS(theme, ltk_theme_parseinfo, key) +GEN_SORT_SEARCH_HELPERS(theme, ltk_theme_parseinfo, key) LTK_ARRAY_INIT_IMPL(keypress, ltk_keypress_cfg) LTK_ARRAY_INIT_IMPL(keyrelease, ltk_keyrelease_cfg) +static ltk_general_config general_config; +static ltk_language_mapping *mappings = NULL; +static size_t mappings_alloc = 0, mappings_len = 0; + +static ltk_theme_parseinfo general_parseinfo[] = { + {"line-editor", THEME_STRING, {.str = &general_config.line_editor}, {.str = NULL}, 0, 0, 0}, + {"dpi-scale", THEME_DOUBLE, {.d = &general_config.dpi_scale}, {.d = 1.0}, 10, 10000, 0}, + {"explicit-focus", THEME_BOOL, {.b = &general_config.explicit_focus}, {.b = 0}, 0, 0, 0}, + {"all-activatable", THEME_BOOL, {.b = &general_config.all_activatable}, {.b = 0}, 0, 0, 0}, + {"fixed-dpi", THEME_DOUBLE, {.d = &general_config.fixed_dpi}, {.d = 96.0}, 100, 400000, 0}, + /* FIXME: warning if set to true but xrandr not enabled */ +#if USE_XRANDR + {"mixed-dpi", THEME_BOOL, {.b = &general_config.mixed_dpi}, {.b = 1}, 0, 0, 0}, +#else + {"mixed-dpi", THEME_BOOL, {.b = &general_config.mixed_dpi}, {.b = 0}, 0, 0, 0}, +#endif +}; + +/* just to use the same interface for all theme sections */ +static void +ltk_general_get_theme_parseinfo(ltk_theme_parseinfo **parseinfo, size_t *len) { + *parseinfo = general_parseinfo; + *len = LENGTH(general_parseinfo); +} + static struct { const char *name; void (*get_parseinfo)( @@ -45,6 +68,232 @@ static struct { {"window", &ltk_window_get_keybinding_parseinfo}, }; +static struct theme_handlerinfo { + const char *name; + void (*get_parseinfo)(ltk_theme_parseinfo **parseinfo, size_t *len); + const char *parent; +} theme_handlers[] = { + {"general", &ltk_general_get_theme_parseinfo, NULL}, + {"theme:window", &ltk_window_get_theme_parseinfo, NULL}, + {"theme:button", &ltk_button_get_theme_parseinfo, "window"}, + {"theme:entry", &ltk_entry_get_theme_parseinfo, "window"}, + {"theme:label", &ltk_label_get_theme_parseinfo, "window"}, + {"theme:scrollbar", &ltk_scrollbar_get_theme_parseinfo, "window"}, + {"theme:menu", &ltk_menu_get_theme_parseinfo, "window"}, + {"theme:menuentry", &ltk_menuentry_get_theme_parseinfo, "window"}, + {"theme:submenu", &ltk_submenu_get_theme_parseinfo, "window"}, + {"theme:submenuentry", &ltk_submenuentry_get_theme_parseinfo, "window"}, +}; + +GEN_SORT_SEARCH_HELPERS(themehandler, struct theme_handlerinfo, name) + +static void +sort_themehandlers(void) { + ltk_theme_parseinfo *parseinfo; + size_t len; + themehandler_sort(theme_handlers, LENGTH(theme_handlers)); + for (size_t i = 0; i < LENGTH(theme_handlers); i++) { + theme_handlers[i].get_parseinfo(&parseinfo, &len); + theme_sort(parseinfo, len); + } +} + +/* FIXME: handle '#' or no '#' in color specification */ +static int +handle_theme_setting(ltk_renderdata *renderdata, ltk_theme_parseinfo *entry, const char *value) { + const char *errstr = NULL; + char *endptr = NULL; + /* FIXME: better warnings */ + long long ll; + switch (entry->type) { + case THEME_INT: + if (entry->max > INT_MAX) + entry->max = INT_MAX; + if (entry->min < INT_MIN) + entry->min = INT_MIN; + *(entry->ptr.i) = ltk_strtonum(value, entry->min, entry->max, &errstr); + if (errstr) + return 1; + entry->initialized = 1; + break; + case THEME_DOUBLE: + /* FIXME: maybe overflow prevention here as well */ + ll = ltk_strtoscalednum(value, entry->min, entry->max, &endptr, &errstr); + if (errstr || *endptr != '\0') + return 1; + *(entry->ptr.d) = ll / 100.0; + entry->initialized = 1; + break; + case THEME_UINT: + if (entry->max > INT_MAX) + entry->max = INT_MAX; + if (entry->min < 0) + entry->min = 0; + *(entry->ptr.u) = ltk_strtonum(value, entry->min, entry->max, &errstr); + if (errstr) + return 1; + entry->initialized = 1; + break; + case THEME_SIZE: + if (entry->max > INT_MAX) + entry->max = INT_MAX; + if (entry->min < INT_MIN) + entry->min = INT_MIN; + entry->ptr.size->unit = LTK_UNIT_PX; + entry->ptr.size->val = ltk_strtoscalednum(value, entry->min, entry->max, &endptr, &errstr); + if (errstr) + return 1; + if (*endptr == '\0') { + /* NOP */ + } else if (!strcmp(endptr, "px")) { + entry->ptr.size->unit = LTK_UNIT_PX; + } else if (!strcmp(endptr, "pt")) { + entry->ptr.size->unit = LTK_UNIT_PT; + } else if (!strcmp(endptr, "mm")) { + entry->ptr.size->unit = LTK_UNIT_MM; + } else { + return 1; + } + entry->initialized = 1; + break; + case THEME_STRING: + *(entry->ptr.str) = ltk_strdup(value); + entry->initialized = 1; + break; + case THEME_COLOR: + /* FIXME: warning message possibly misleading because this can fail for reasons + other than an invalid color specification */ + if (!(*(entry->ptr.color) = ltk_color_create(renderdata, value))) + return 1; + entry->initialized = 1; + break; + case THEME_BOOL: + if (strcmp(value, "true") == 0) { + *(entry->ptr.b) = 1; + } else if (strcmp(value, "false") == 0) { + *(entry->ptr.b) = 0; + } else { + return 1; + } + entry->initialized = 1; + break; + case THEME_BORDERSIDES: + *(entry->ptr.border) = LTK_BORDER_NONE; + for (const char *c = value; *c != '\0'; c++) { + switch (*c) { + case 't': + *(entry->ptr.border) |= LTK_BORDER_TOP; + break; + case 'b': + *(entry->ptr.border) |= LTK_BORDER_BOTTOM; + break; + case 'l': + *(entry->ptr.border) |= LTK_BORDER_LEFT; + break; + case 'r': + *(entry->ptr.border) |= LTK_BORDER_RIGHT; + break; + default: + return 1; + } + } + entry->initialized = 1; + break; + default: + ltk_fatal("Invalid theme setting type. This should not happen.\n"); + } + return 0; +} + +static int +fill_theme_defaults(ltk_renderdata *renderdata) { + ltk_theme_parseinfo *parseinfo; + size_t len; + for (size_t j = 0; j < LENGTH(theme_handlers); j++) { + theme_handlers[j].get_parseinfo(&parseinfo, &len); + for (size_t i = 0; i < len; i++) { + ltk_theme_parseinfo *e = &parseinfo[i]; + if (e->initialized) + continue; + switch (e->type) { + case THEME_INT: + *(e->ptr.i) = e->defaultval.i; + e->initialized = 1; + break; + case THEME_UINT: + *(e->ptr.u) = e->defaultval.u; + e->initialized = 1; + break; + case THEME_DOUBLE: + *(e->ptr.d) = e->defaultval.d; + e->initialized = 1; + break; + case THEME_SIZE: + *(e->ptr.size) = e->defaultval.size; + e->initialized = 1; + break; + case THEME_STRING: + if (e->defaultval.str) + *(e->ptr.str) = ltk_strdup(e->defaultval.str); + else + *(e->ptr.str) = NULL; + e->initialized = 1; + break; + case THEME_COLOR: + if (!(*(e->ptr.color) = ltk_color_create(renderdata, e->defaultval.color))) + return 1; + e->initialized = 1; + break; + case THEME_BOOL: + *(e->ptr.b) = e->defaultval.b; + e->initialized = 1; + break; + case THEME_BORDERSIDES: + *(e->ptr.border) = e->defaultval.border; + e->initialized = 1; + break; + default: + ltk_fatal("Invalid theme setting type. This should not happen.\n"); + } + } + } + return 0; +} + +static void +uninitialize_theme(ltk_renderdata *renderdata) { + ltk_theme_parseinfo *parseinfo; + size_t len; + for (size_t j = 0; j < LENGTH(theme_handlers); j++) { + theme_handlers[j].get_parseinfo(&parseinfo, &len); + for (size_t i = 0; i < len; i++) { + ltk_theme_parseinfo *e = &parseinfo[i]; + if (!e->initialized) + continue; + switch (e->type) { + case THEME_STRING: + ltk_free(*(e->ptr.str)); + e->initialized = 0; + break; + case THEME_COLOR: + ltk_color_destroy(renderdata, *(e->ptr.color)); + e->initialized = 0; + break; + case THEME_SIZE: + case THEME_INT: + case THEME_UINT: + case THEME_BOOL: + case THEME_BORDERSIDES: + case THEME_DOUBLE: + e->initialized = 0; + break; + default: + ltk_fatal("Invalid theme setting type. This should not happen.\n"); + } + } + } +} + static int register_keypress(ltk_array(keypress) *bindings, ltk_keybinding_cb *arr, size_t arrlen, const char *func_name, size_t func_len, ltk_keypress_binding b) { ltk_keybinding_cb *cb = keybinding_get_entry(arr, arrlen, func_name, func_len); @@ -139,8 +388,6 @@ ltk_keyrelease_bindings_destroy(ltk_array(keyrelease) *arr) { static void sort_keysyms(void); static int parse_keysym(char *text, size_t len, ltk_keysym *sym_ret); -ltk_config *global_config = NULL; - enum toktype { STRING, SECTION, @@ -524,23 +771,23 @@ error: } static void -push_lang_mapping(ltk_config *c) { - if (c->mappings_alloc == c->mappings_len) { - c->mappings_alloc = ideal_array_size(c->mappings_alloc, c->mappings_len + 1); - c->mappings = ltk_reallocarray(c->mappings, c->mappings_alloc, sizeof(ltk_language_mapping)); +push_lang_mapping(void) { + if (mappings_alloc == mappings_len) { + mappings_alloc = ideal_array_size(mappings_alloc, mappings_len + 1); + mappings = ltk_reallocarray(mappings, mappings_alloc, sizeof(ltk_language_mapping)); } - c->mappings[c->mappings_len].lang = NULL; - c->mappings[c->mappings_len].mappings = NULL; - c->mappings[c->mappings_len].mappings_alloc = 0; - c->mappings[c->mappings_len].mappings_len = 0; - c->mappings_len++; + mappings[mappings_len].lang = NULL; + mappings[mappings_len].mappings = NULL; + mappings[mappings_len].mappings_alloc = 0; + mappings[mappings_len].mappings_len = 0; + mappings_len++; } static void -push_text_mapping(ltk_config *c, char *text1, size_t len1, char *text2, size_t len2) { - if (c->mappings_len == 0) +push_text_mapping(char *text1, size_t len1, char *text2, size_t len2) { + if (mappings_len == 0) return; /* I guess just fail silently... */ - ltk_language_mapping *m = &c->mappings[c->mappings_len - 1]; + ltk_language_mapping *m = &mappings[mappings_len - 1]; if (m->mappings_alloc == m->mappings_len) { m->mappings_alloc = ideal_array_size(m->mappings_alloc, m->mappings_len + 1); m->mappings = ltk_reallocarray(m->mappings, m->mappings_alloc, sizeof(ltk_keytext_mapping)); @@ -550,39 +797,36 @@ push_text_mapping(ltk_config *c, char *text1, size_t len1, char *text2, size_t l m->mappings_len++; } -static void -destroy_config(ltk_config *c) { - for (size_t i = 0; i < c->mappings_len; i++) { - ltk_free(c->mappings[i].lang); - for (size_t j = 0; j < c->mappings[i].mappings_len; j++) { - ltk_free(c->mappings[i].mappings[j].from); - ltk_free(c->mappings[i].mappings[j].to); +void +ltk_config_cleanup(ltk_renderdata *renderdata) { + if (mappings) { + for (size_t i = 0; i < mappings_len; i++) { + ltk_free(mappings[i].lang); + for (size_t j = 0; j < mappings[i].mappings_len; j++) { + ltk_free(mappings[i].mappings[j].from); + ltk_free(mappings[i].mappings[j].to); + } + ltk_free(mappings[i].mappings); } - ltk_free(c->mappings[i].mappings); + ltk_free(mappings); + mappings = NULL; + mappings_len = mappings_alloc = 0; } - ltk_free(c->general.line_editor); - ltk_free(c->mappings); - ltk_free(c); + uninitialize_theme(renderdata); } -void -ltk_config_cleanup(void) { - if (global_config) - destroy_config(global_config); - global_config = NULL; -} - -ltk_config * -ltk_config_get(void) { - return global_config; +/* FIXME: error if not initialized */ +ltk_general_config * +ltk_config_get_general(void) { + return &general_config; } int ltk_config_get_language_index(char *lang, size_t *idx_ret) { - if (!global_config) + if (!mappings) return 1; - for (size_t i = 0; i < global_config->mappings_len; i++) { - if (!strcmp(lang, global_config->mappings[i].lang)) { + for (size_t i = 0; i < mappings_len; i++) { + if (!strcmp(lang, mappings[i].lang)) { *idx_ret = i; return 0; } @@ -592,9 +836,9 @@ ltk_config_get_language_index(char *lang, size_t *idx_ret) { ltk_language_mapping * ltk_config_get_language_mapping(size_t idx) { - if (!global_config || idx >= global_config->mappings_len) + if (idx >= mappings_len) return NULL; - return &global_config->mappings[idx]; + return &mappings[idx]; } int @@ -605,33 +849,32 @@ str_array_prefix(const char *str, const char *ar, size_t len) { return !strncmp(str, ar, slen); } +/* FIXME: The current model is kind of weird because most parts of the config + are stored in the other object files. This makes it difficult to support + reloading of the config since the old config needs to be kept until the + new config has been successfully loaded, but the parseinfos include direct + pointers to the config. It might be better to just have one huge config + struct that includes everything. That would also make it a bit clearer when + something hasn't been initialized yet. */ /* WARNING: errstr must be freed! */ /* FIXME: make ltk_load_file give size_t; handle errors there (copy from ledit) */ static int load_from_text( + ltk_renderdata *renderdata, const char *filename, char *file_contents, size_t len, char **errstr) { - ltk_config *config = ltk_malloc(sizeof(ltk_config)); - config->mappings = NULL; - config->mappings_alloc = config->mappings_len = 0; - config->general.explicit_focus = 0; - config->general.all_activatable = 0; - config->general.line_editor = NULL; - config->general.dpi_scale = 1.0; - config->general.fixed_dpi = 480; /* 5 * 96 */ -#if USE_XRANDR - config->general.mixed_dpi = 1; -#else - config->general.mixed_dpi = 0; -#endif + sort_keysyms(); + sort_keybindings(); + sort_themehandlers(); struct lexstate s = {filename, file_contents, len, 0, 1, 0}; struct token tok = next_token(&s); int start_of_line = 1; char *msg = NULL; struct token secttok; + txtbuf *themeval = txtbuf_new(); while (tok.type != END) { switch (tok.type) { case SECTION: @@ -645,94 +888,7 @@ load_from_text( msg = "Section must be alone on line"; goto error; } - /* FIXME: generalize (at least once more options are added) */ - if (str_array_equal("general", secttok.text, secttok.len)) { - struct token prev1tok, prev2tok; - while (1) { - tok = next_token(&s); - if (tok.type == SECTION || tok.type == END) - break; - else if (tok.type == NEWLINE) - continue; - prev2tok = tok; - tok = next_token(&s); - prev1tok = tok; - tok = next_token(&s); - if (prev2tok.type != STRING || prev1tok.type != EQUALS || tok.type != STRING) { - msg = "Invalid assignment statement"; - goto error; - } - if (str_array_equal("explicit-focus", prev2tok.text, prev2tok.len)) { - if (str_array_equal("true", tok.text, tok.len)) { - config->general.explicit_focus = 1; - } else if (str_array_equal("false", tok.text, tok.len)) { - config->general.explicit_focus = 0; - } else { - msg = "Invalid boolean setting"; - goto error; - } - } else if (str_array_equal("all-activatable", prev2tok.text, prev2tok.len)) { - if (str_array_equal("true", tok.text, tok.len)) { - config->general.all_activatable = 1; - } else if (str_array_equal("false", tok.text, tok.len)) { - config->general.all_activatable = 0; - } else { - msg = "Invalid boolean setting"; - goto error; - } - /* FIXME: warning if set to true but xrandr not enabled */ - } else if (str_array_equal("mixed-dpi", prev2tok.text, prev2tok.len)) { - if (str_array_equal("true", tok.text, tok.len)) { - config->general.mixed_dpi = 1; - } else if (str_array_equal("false", tok.text, tok.len)) { - config->general.mixed_dpi = 0; - } else { - msg = "Invalid boolean setting"; - goto error; - } - } else if (str_array_equal("line-editor", prev2tok.text, prev2tok.len)) { - config->general.line_editor = ltk_strndup(tok.text, tok.len); - } else if (str_array_equal("fixed-dpi", prev2tok.text, prev2tok.len)) { - /* FIXME: remove this allocation! */ - char *tmp = ltk_strndup(tok.text, tok.len); - /* FIXME: proper min/max values for dpi */ - const char *tmp_err = NULL; - config->general.fixed_dpi = ltk_strtonum(tmp, 10, 4000, &tmp_err); - ltk_free(tmp); - if (tmp_err) { - msg = "Invalid DPI setting"; - goto error; - } - /* because of weird scaling that is currently used, see event_xlib.c */ - config->general.fixed_dpi *= 5; - } else if (str_array_equal("dpi-scale", prev2tok.text, prev2tok.len)) { - /* FIXME: remove this allocation! */ - char *tmp = ltk_strndup(tok.text, tok.len); - const char *tmp_err = NULL; - char *ep = NULL; - /* FIXME: proper min/max values for scale */ - config->general.dpi_scale = ltk_strtoscalednum(tmp, 10, 10000, &ep, &tmp_err); - char c = *ep; - ltk_free(tmp); - if (tmp_err || c != '\0') { - msg = "Invalid DPI scale setting"; - goto error; - } - config->general.dpi_scale /= 100; - } else { - msg = "Invalid setting"; - goto error; - } - tok = next_token(&s); - if (tok.type == END) { - break; - } else if (tok.type != NEWLINE) { - msg = "Invalid assignment statement"; - goto error; - } - start_of_line = 1; - } - } else if (str_array_prefix("key-binding:", secttok.text, secttok.len)) { + if (str_array_prefix("key-binding:", secttok.text, secttok.len)) { int ret = 0; char *widget = secttok.text + strlen("key-binding:"); size_t len = secttok.len - strlen("key-binding:"); @@ -746,7 +902,7 @@ load_from_text( } } else if (str_array_equal("key-mapping", secttok.text, secttok.len)) { int lang_init = 0; - push_lang_mapping(config); + push_lang_mapping(); struct token prev1tok, prev2tok; while (1) { tok = next_token(&s); @@ -770,14 +926,14 @@ load_from_text( msg = "Language already set"; goto error; } - config->mappings[config->mappings_len - 1].lang = ltk_strndup(tok.text, tok.len); + mappings[mappings_len - 1].lang = ltk_strndup(tok.text, tok.len); lang_init = 1; } else if (str_array_equal("map", prev2tok.text, prev2tok.len)) { if (prev1tok.type != STRING || tok.type != STRING) { msg = "Invalid map statement"; goto error; } - push_text_mapping(config, prev1tok.text, prev1tok.len, tok.text, tok.len); + push_text_mapping(prev1tok.text, prev1tok.len, tok.text, tok.len); } else { msg = "Invalid statement in language mapping"; goto error; @@ -796,8 +952,58 @@ load_from_text( goto error; } } else { - msg = "Invalid section"; - goto error; + struct token prev1tok, prev2tok; + struct theme_handlerinfo *handler = themehandler_get_entry( + theme_handlers, LENGTH(theme_handlers), secttok.text, secttok.len + ); + if (!handler) { + msg = "Invalid section"; + goto error; + } + ltk_theme_parseinfo *parseinfo; + size_t parseinfo_len; + handler->get_parseinfo(&parseinfo, &parseinfo_len); + while (1) { + tok = next_token(&s); + if (tok.type == SECTION || tok.type == END) + break; + else if (tok.type == NEWLINE) + continue; + prev2tok = tok; + tok = next_token(&s); + prev1tok = tok; + tok = next_token(&s); + if (prev2tok.type != STRING || prev1tok.type != EQUALS || tok.type != STRING) { + msg = "Syntax error in assignment statement"; + goto error; + } + ltk_theme_parseinfo *parse_entry = theme_get_entry( + parseinfo, parseinfo_len, prev2tok.text, prev2tok.len + ); + if (!parse_entry) { + msg = "Invalid left-hand side in assignment statement"; + goto error; + } else if (parse_entry->initialized) { + msg = "Duplicate assignment"; + goto error; + } + /* temporarly copy to txtbuf so it is NUL-terminated (the alternative + would be to use replacements for ltk_strtonum, etc. that accept + a length parameter) */ + txtbuf_set_textn(themeval, tok.text, tok.len); + if (handle_theme_setting(renderdata, parse_entry, themeval->text)) { + msg = "Invalid right-hand side in assignment"; + goto error; + } + tok = next_token(&s); + if (tok.type == END) { + break; + } else if (tok.type != NEWLINE) { + msg = "Syntax error in assignment statement"; + goto error; + } + start_of_line = 1; + } } break; case NEWLINE: @@ -809,7 +1015,12 @@ load_from_text( break; } } - global_config = config; + /* FIXME: better error reporting */ + if (fill_theme_defaults(renderdata)) { + *errstr = ltk_strdup("Unable to load theme defaults"); + goto errornomsg; + } + txtbuf_destroy(themeval); return 0; error: if (msg) { @@ -818,22 +1029,21 @@ error: ); } errornomsg: - destroy_config(config); + ltk_config_cleanup(renderdata); + txtbuf_destroy(themeval); return 1; } int -ltk_config_parsefile(const char *filename, char **errstr) { +ltk_config_parsefile(ltk_renderdata *renderdata, const char *filename, char **errstr) { unsigned long len = 0; char *ferrstr = NULL; - sort_keysyms(); - sort_keybindings(); char *file_contents = ltk_read_file(filename, &len, &ferrstr); if (!file_contents) { *errstr = ltk_print_fmt("Unable to open file \"%s\": %s", filename, ferrstr); return 1; } - int ret = load_from_text(filename, file_contents, len, errstr); + int ret = load_from_text(renderdata, filename, file_contents, len, errstr); ltk_free(file_contents); return ret; } @@ -856,11 +1066,9 @@ const char *default_config = "[general]\n" /* FIXME: improve this configuration */ int -ltk_config_load_default(char **errstr) { - sort_keysyms(); - sort_keybindings(); +ltk_config_load_default(ltk_renderdata *renderdata, char **errstr) { char *config_copied = ltk_strdup(default_config); - int ret = load_from_text("<default config>", config_copied, strlen(config_copied), errstr); + int ret = load_from_text(renderdata, "<default config>", config_copied, strlen(config_copied), errstr); ltk_free(config_copied); return ret; } diff --git a/src/ltk/config.h b/src/ltk/config.h @@ -20,7 +20,9 @@ #include <stddef.h> #include "array.h" +#include "color.h" #include "widget.h" +#include "graphics.h" #include "eventdefs.h" typedef enum{ @@ -56,18 +58,12 @@ typedef struct { typedef struct { char *line_editor; double dpi_scale; - unsigned int fixed_dpi; - char mixed_dpi; - char explicit_focus; - char all_activatable; + double fixed_dpi; + int mixed_dpi; + int explicit_focus; + int all_activatable; } ltk_general_config; -typedef struct { - ltk_language_mapping *mappings; - size_t mappings_alloc, mappings_len; - ltk_general_config general; -} ltk_config; - typedef int (*ltk_keybinding_func)(ltk_widget *, ltk_key_event *); typedef struct { @@ -85,15 +81,68 @@ typedef struct { ltk_keybinding_cb cb; } ltk_keyrelease_cfg; +typedef enum { + THEME_STRING, + THEME_COLOR, + THEME_INT, + THEME_UINT, + THEME_BOOL, + THEME_BORDERSIDES, + THEME_SIZE, + THEME_DOUBLE, +} ltk_theme_datatype; + +typedef struct { + char *key; + ltk_theme_datatype type; + /* Note: Bool and int are both integers, but they are + separate just to make it a bit clearer */ + union { + char **str; + ltk_color **color; + int *i; + unsigned int *u; + int *b; + ltk_border_sides *border; + ltk_size *size; + double *d; + } ptr; + /* Note: The default color is also given as a string + because it has to be allocated first (it is only a + different entry in the union in order to make it + a bit clearer) */ + union { + char *str; + char *color; + int i; + unsigned int u; + int b; + ltk_border_sides border; + ltk_size size; + double d; + } defaultval; + /* FIXME: min/max doesn't make too much sense for sizes since they + can use different units, but that shouldn't matter for now because + min/max is only used as a sanity check to avoid extreme sizes or + negative sizes where that isn't allowed */ + /* only for integers, doubles, or sizes */ + /* doubles are weird at the moment since only two decimal places are + allowed. The min/max for doubles is the min/max given here, divided + by 100, i.e. to allow the range 1.0-10.0, min must be 100 and max + must be 1000. */ + long long min, max; + int initialized; +} ltk_theme_parseinfo; + LTK_ARRAY_INIT_DECL(keypress, ltk_keypress_cfg) LTK_ARRAY_INIT_DECL(keyrelease, ltk_keyrelease_cfg) -void ltk_config_cleanup(void); -ltk_config *ltk_config_get(void); +void ltk_config_cleanup(ltk_renderdata *renderdata); +ltk_general_config *ltk_config_get_general(void); int ltk_config_get_language_index(char *lang, size_t *idx_ret); ltk_language_mapping *ltk_config_get_language_mapping(size_t idx); -int ltk_config_parsefile(const char *filename, char **errstr); -int ltk_config_load_default(char **errstr); +int ltk_config_parsefile(ltk_renderdata *renderdata, const char *filename, char **errstr); +int ltk_config_load_default(ltk_renderdata *renderdata, char **errstr); void ltk_keypress_bindings_destroy(ltk_array(keypress) *arr); void ltk_keyrelease_bindings_destroy(ltk_array(keyrelease) *arr); diff --git a/src/ltk/entry.c b/src/ltk/entry.c @@ -35,7 +35,6 @@ #include "memory.h" #include "rect.h" #include "text.h" -#include "theme.h" #include "txtbuf.h" #include "util.h" #include "widget.h" @@ -193,21 +192,11 @@ static ltk_theme_parseinfo parseinfo[] = { {"text-color", THEME_COLOR, {.color = &theme.text_color}, {.color = "#FFFFFF"}, 0, 0, 0}, {"selection-color", THEME_COLOR, {.color = &theme.selection_color}, {.color = "#000000"}, 0, 0, 0}, }; -static int parseinfo_sorted = 0; - -int -ltk_entry_ini_handler(ltk_renderdata *data, const char *prop, const char *value) { - return ltk_theme_handle_value(data, "entry", prop, value, parseinfo, LENGTH(parseinfo), &parseinfo_sorted); -} - -int -ltk_entry_fill_theme_defaults(ltk_renderdata *data) { - return ltk_theme_fill_defaults(data, "entry", parseinfo, LENGTH(parseinfo)); -} void -ltk_entry_uninitialize_theme(ltk_renderdata *data) { - ltk_theme_uninitialize(data, parseinfo, LENGTH(parseinfo)); +ltk_entry_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len) { + *p = parseinfo; + *len = LENGTH(parseinfo); } /* FIXME: draw cursor in different color on selection side that will be expanded */ @@ -576,15 +565,15 @@ static int edit_external(ltk_widget *self, ltk_key_event *event) { (void)event; ltk_entry *entry = LTK_CAST_ENTRY(self); - ltk_config *config = ltk_config_get(); + ltk_general_config *config = ltk_config_get_general(); /* FIXME: allow arguments to key mappings - this would allow to have different key mappings for different editors instead of just one command */ - if (!config->general.line_editor) { + if (!config->line_editor) { ltk_warn("Unable to run external editing command: line editor not configured\n"); } else { /* FIXME: somehow show that there was an error if this returns 1? */ /* FIXME: change interface to not require length of cmd */ - ltk_call_cmd(LTK_CAST_WIDGET(entry), config->general.line_editor, strlen(config->general.line_editor), entry->text, entry->len); + ltk_call_cmd(LTK_CAST_WIDGET(entry), config->line_editor, strlen(config->line_editor), entry->text, entry->len); } return 0; } diff --git a/src/ltk/entry.h b/src/ltk/entry.h @@ -40,15 +40,4 @@ typedef struct { ltk_entry *ltk_entry_create(ltk_window *window, char *text); -/* FIXME: these should be private to ltk */ -void ltk_entry_cleanup(void); -void ltk_entry_get_keybinding_parseinfo( - ltk_keybinding_cb **press_cbs_ret, size_t *press_len_ret, - ltk_keybinding_cb **release_cbs_ret, size_t *release_len_ret, - ltk_array(keypress) **presses_ret, ltk_array(keyrelease) **releases_ret -); -int ltk_entry_ini_handler(ltk_renderdata *data, const char *prop, const char *value); -int ltk_entry_fill_theme_defaults(ltk_renderdata *data); -void ltk_entry_uninitialize_theme(ltk_renderdata *data); - #endif /* LTK_ENTRY_H */ diff --git a/src/ltk/event_xlib.c b/src/ltk/event_xlib.c @@ -235,8 +235,8 @@ ltk_recalc_renderwindow_dpi(ltk_renderwindow *window) { static void update_monitor_config(ltk_renderdata *renderdata, ltk_renderwindow **windows, size_t num_windows) { int nmon; - ltk_config *config = ltk_config_get(); - if (!config->general.mixed_dpi) + ltk_general_config *config = ltk_config_get_general(); + if (!config->mixed_dpi) return; XRRMonitorInfo *mi = XRRGetMonitors(renderdata->dpy, renderdata->root_window, 1, &nmon); if (nmon > 0 && !renderdata->monitors) @@ -246,7 +246,7 @@ update_monitor_config(ltk_renderdata *renderdata, ltk_renderwindow **windows, si /* FIXME: This only uses the width for the calculation. It should be the same if using the height, but is that guaranteed? */ /* FIXME: can width or mwidth ever by negative? */ - info.dpi = (unsigned int)round(config->general.dpi_scale * (info.width / (info.mwidth / 127.0))); + info.dpi = (unsigned int)round(config->dpi_scale * (info.width / (info.mwidth / 127.0))); /* FIXME: need to adjust default dpi and document */ /* -> config file dpi should still be regular dpi */ /* FIXME: check for overflows in the later pixel computation */ diff --git a/src/ltk/ini.c b/src/ltk/ini.c @@ -1,201 +0,0 @@ -/* inih -- simple .INI file parser - -inih is released under the New BSD license (see LICENSE.txt). Go to the project -home page for more info: - -https://github.com/benhoyt/inih - -*/ - -#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include <stdio.h> -#include <ctype.h> -#include <string.h> - -#include "ini.h" - -#if !INI_USE_STACK -#include <stdlib.h> -#endif - -#define MAX_SECTION 50 -#define MAX_NAME 50 - -/* Strip whitespace chars off end of given string, in place. Return s. */ -static char* rstrip(char* s) -{ - char* p = s + strlen(s); - while (p > s && isspace((unsigned char)(*--p))) - *p = '\0'; - return s; -} - -/* Return pointer to first non-whitespace char in given string. */ -static char* lskip(const char* s) -{ - while (*s && isspace((unsigned char)(*s))) - s++; - return (char*)s; -} - -/* Return pointer to first char (of chars) or inline comment in given string, - or pointer to null at end of string if neither found. Inline comment must - be prefixed by a whitespace character to register as a comment. */ -static char* find_chars_or_comment(const char* s, const char* chars) -{ -#if INI_ALLOW_INLINE_COMMENTS - int was_space = 0; - while (*s && (!chars || !strchr(chars, *s)) && - !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { - was_space = isspace((unsigned char)(*s)); - s++; - } -#else - while (*s && (!chars || !strchr(chars, *s))) { - s++; - } -#endif - return (char*)s; -} - -/* Version of strncpy that ensures dest (size bytes) is null-terminated. */ -static char* strncpy0(char* dest, const char* src, size_t size) -{ - strncpy(dest, src, size); - dest[size - 1] = '\0'; - return dest; -} - -/* See documentation in header file. */ -int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, - void* user) -{ - /* Uses a fair bit of stack (use heap instead if you need to) */ -#if INI_USE_STACK - char line[INI_MAX_LINE]; -#else - char* line; -#endif - char section[MAX_SECTION] = ""; - char prev_name[MAX_NAME] = ""; - - char* start; - char* end; - char* name; - char* value; - int lineno = 0; - int error = 0; - -#if !INI_USE_STACK - line = (char*)malloc(INI_MAX_LINE); - if (!line) { - return -2; - } -#endif - -#if INI_HANDLER_LINENO -#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno) -#else -#define HANDLER(u, s, n, v) handler(u, s, n, v) -#endif - - /* Scan through stream line by line */ - while (reader(line, INI_MAX_LINE, stream) != NULL) { - lineno++; - - start = line; -#if INI_ALLOW_BOM - if (lineno == 1 && (unsigned char)start[0] == 0xEF && - (unsigned char)start[1] == 0xBB && - (unsigned char)start[2] == 0xBF) { - start += 3; - } -#endif - start = lskip(rstrip(start)); - - if (*start == ';' || *start == '#') { - /* Per Python configparser, allow both ; and # comments at the - start of a line */ - } -#if INI_ALLOW_MULTILINE - else if (*prev_name && *start && start > line) { - /* Non-blank line with leading whitespace, treat as continuation - of previous name's value (as per Python configparser). */ - if (!HANDLER(user, section, prev_name, start) && !error) - error = lineno; - } -#endif - else if (*start == '[') { - /* A "[section]" line */ - end = find_chars_or_comment(start + 1, "]"); - if (*end == ']') { - *end = '\0'; - strncpy0(section, start + 1, sizeof(section)); - *prev_name = '\0'; - } - else if (!error) { - /* No ']' found on section line */ - error = lineno; - } - } - else if (*start) { - /* Not a comment, must be a name[=:]value pair */ - end = find_chars_or_comment(start, "=:"); - if (*end == '=' || *end == ':') { - *end = '\0'; - name = rstrip(start); - value = end + 1; -#if INI_ALLOW_INLINE_COMMENTS - end = find_chars_or_comment(value, NULL); - if (*end) - *end = '\0'; -#endif - value = lskip(value); - rstrip(value); - - /* Valid name[=:]value pair found, call handler */ - strncpy0(prev_name, name, sizeof(prev_name)); - if (!HANDLER(user, section, name, value) && !error) - error = lineno; - } - else if (!error) { - /* No '=' or ':' found on name[=:]value line */ - error = lineno; - } - } - -#if INI_STOP_ON_FIRST_ERROR - if (error) - break; -#endif - } - -#if !INI_USE_STACK - free(line); -#endif - - return error; -} - -/* See documentation in header file. */ -int ini_parse_file(FILE* file, ini_handler handler, void* user) -{ - return ini_parse_stream((ini_reader)fgets, file, handler, user); -} - -/* See documentation in header file. */ -int ini_parse(const char* filename, ini_handler handler, void* user) -{ - FILE* file; - int error; - - file = fopen(filename, "r"); - if (!file) - return -1; - error = ini_parse_file(file, handler, user); - fclose(file); - return error; -} diff --git a/src/ltk/ini.h b/src/ltk/ini.h @@ -1,104 +0,0 @@ -/* inih -- simple .INI file parser - -inih is released under the New BSD license (see LICENSE.txt). Go to the project -home page for more info: - -https://github.com/benhoyt/inih - -*/ - -#ifndef __INI_H__ -#define __INI_H__ - -/* Make this header file easier to include in C++ code */ -#ifdef __cplusplus -extern "C" { -#endif - -#include <stdio.h> - -/* Nonzero if ini_handler callback should accept lineno parameter. */ -#ifndef INI_HANDLER_LINENO -#define INI_HANDLER_LINENO 0 -#endif - -/* Typedef for prototype of handler function. */ -#if INI_HANDLER_LINENO -typedef int (*ini_handler)(void* user, const char* section, - const char* name, const char* value, - int lineno); -#else -typedef int (*ini_handler)(void* user, const char* section, - const char* name, const char* value); -#endif - -/* Typedef for prototype of fgets-style reader function. */ -typedef char* (*ini_reader)(char* str, int num, void* stream); - -/* Parse given INI-style file. May have [section]s, name=value pairs - (whitespace stripped), and comments starting with ';' (semicolon). Section - is "" if name=value pair parsed before any section heading. name:value - pairs are also supported as a concession to Python's configparser. - - For each name=value pair parsed, call handler function with given user - pointer as well as section, name, and value (data only valid for duration - of handler call). Handler should return nonzero on success, zero on error. - - Returns 0 on success, line number of first error on parse error (doesn't - stop on first error), -1 on file open error, or -2 on memory allocation - error (only when INI_USE_STACK is zero). -*/ -int ini_parse(const char* filename, ini_handler handler, void* user); - -/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't - close the file when it's finished -- the caller must do that. */ -int ini_parse_file(FILE* file, ini_handler handler, void* user); - -/* Same as ini_parse(), but takes an ini_reader function pointer instead of - filename. Used for implementing custom or string-based I/O. */ -int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, - void* user); - -/* Nonzero to allow multi-line value parsing, in the style of Python's - configparser. If allowed, ini_parse() will call the handler with the same - name for each subsequent line parsed. */ -#ifndef INI_ALLOW_MULTILINE -#define INI_ALLOW_MULTILINE 1 -#endif - -/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of - the file. See http://code.google.com/p/inih/issues/detail?id=21 */ -#ifndef INI_ALLOW_BOM -#define INI_ALLOW_BOM 1 -#endif - -/* Nonzero to allow inline comments (with valid inline comment characters - specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match - Python 3.2+ configparser behaviour. */ -#ifndef INI_ALLOW_INLINE_COMMENTS -#define INI_ALLOW_INLINE_COMMENTS 1 -#endif -#ifndef INI_INLINE_COMMENT_PREFIXES -#define INI_INLINE_COMMENT_PREFIXES ";" -#endif - -/* Nonzero to use stack, zero to use heap (malloc/free). */ -#ifndef INI_USE_STACK -#define INI_USE_STACK 1 -#endif - -/* Stop parsing on first error (default is to keep parsing). */ -#ifndef INI_STOP_ON_FIRST_ERROR -#define INI_STOP_ON_FIRST_ERROR 0 -#endif - -/* Maximum line length for any line in INI file. */ -#ifndef INI_MAX_LINE -#define INI_MAX_LINE 200 -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* __INI_H__ */ diff --git a/src/ltk/label.c b/src/ltk/label.c @@ -26,7 +26,6 @@ #include "text.h" #include "label.h" #include "graphics.h" -#include "theme.h" #define MAX_LABEL_PADDING 50000 @@ -63,8 +62,6 @@ static struct { ltk_size pad; } theme; -int parseinfo_sorted = 0; - static ltk_theme_parseinfo parseinfo[] = { {"bg-color", THEME_COLOR, {.color = &theme.bg_color}, {.color = "#000000"}, 0, 0, 0}, {"bg-color-active", THEME_COLOR, {.color = &theme.bg_color_active}, {.color = "#222222"}, 0, 0, 0}, @@ -72,19 +69,10 @@ static ltk_theme_parseinfo parseinfo[] = { {"text-color", THEME_COLOR, {.color = &theme.text_color}, {.color = "#FFFFFF"}, 0, 0, 0}, }; -int -ltk_label_ini_handler(ltk_renderdata *data, const char *prop, const char *value) { - return ltk_theme_handle_value(data, "label", prop, value, parseinfo, LENGTH(parseinfo), &parseinfo_sorted); -} - -int -ltk_label_fill_theme_defaults(ltk_renderdata *data) { - return ltk_theme_fill_defaults(data, "label", parseinfo, LENGTH(parseinfo)); -} - void -ltk_label_uninitialize_theme(ltk_renderdata *data) { - ltk_theme_uninitialize(data, parseinfo, LENGTH(parseinfo)); +ltk_label_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len) { + *p = parseinfo; + *len = LENGTH(parseinfo); } static void diff --git a/src/ltk/label.h b/src/ltk/label.h @@ -29,10 +29,6 @@ typedef struct { ltk_text_line *tl; } ltk_label; -int ltk_label_ini_handler(ltk_renderdata *data, const char *prop, const char *value); -int ltk_label_fill_theme_defaults(ltk_renderdata *data); -void ltk_label_uninitialize_theme(ltk_renderdata *data); - ltk_label *ltk_label_create(ltk_window *window, char *text); #endif /* LTK_LABEL_H */ diff --git a/src/ltk/ltk.c b/src/ltk/ltk.c @@ -33,7 +33,6 @@ #include "eventdefs.h" #include "graphics.h" #include "image.h" -#include "ini.h" #include "label.h" #include "macros.h" #include "memory.h" @@ -43,8 +42,7 @@ #include "text.h" #include "util.h" #include "widget.h" - -#define MAX_WINDOW_FONT_SIZE 200 +#include "widget_internal.h" typedef struct { char *tmpfile; @@ -87,91 +85,56 @@ static size_t timers_num = 0; static size_t timers_alloc = 0; static void ltk_handle_event(ltk_event *event); -static void ltk_load_theme(const char *path); -static void ltk_uninitialize_theme(void); -static int ltk_ini_handler(void *renderdata, const char *widget, const char *prop, const char *value); static short running = 1; typedef struct { char *name; - int (*ini_handler)(ltk_renderdata *, const char *, const char *); - int (*fill_theme_defaults)(ltk_renderdata *); - void (*uninitialize_theme)(ltk_renderdata *); void (*cleanup)(void); } ltk_widget_funcs; -/* FIXME: use binary search when searching for the widget */ +/* FIXME: I guess the names aren't needed anymore here, but who + knows if I'll need them again sometime... */ static ltk_widget_funcs widget_funcs[] = { { .name = "box", - .ini_handler = NULL, - .fill_theme_defaults = NULL, - .uninitialize_theme = NULL, .cleanup = NULL, }, { .name = "button", - .ini_handler = &ltk_button_ini_handler, - .fill_theme_defaults = &ltk_button_fill_theme_defaults, - .uninitialize_theme = &ltk_button_uninitialize_theme, .cleanup = NULL, }, { .name = "entry", - .ini_handler = &ltk_entry_ini_handler, - .fill_theme_defaults = &ltk_entry_fill_theme_defaults, - .uninitialize_theme = &ltk_entry_uninitialize_theme, .cleanup = &ltk_entry_cleanup, }, { .name = "grid", - .ini_handler = NULL, - .fill_theme_defaults = NULL, - .uninitialize_theme = NULL, .cleanup = NULL, }, { .name = "label", - .ini_handler = &ltk_label_ini_handler, - .fill_theme_defaults = &ltk_label_fill_theme_defaults, - .uninitialize_theme = &ltk_label_uninitialize_theme, .cleanup = NULL, }, { /* FIXME: this is actually image_widget */ .name = "image", - .ini_handler = NULL, - .fill_theme_defaults = NULL, - .uninitialize_theme = NULL, .cleanup = NULL, }, { .name = "menu", - .ini_handler = &ltk_menu_ini_handler, - .fill_theme_defaults = &ltk_menu_fill_theme_defaults, - .uninitialize_theme = &ltk_menu_uninitialize_theme, .cleanup = NULL, }, { .name = "menuentry", - .ini_handler = &ltk_menuentry_ini_handler, - .fill_theme_defaults = &ltk_menuentry_fill_theme_defaults, - .uninitialize_theme = &ltk_menuentry_uninitialize_theme, .cleanup = NULL, }, { .name = "submenu", - .ini_handler = &ltk_submenu_ini_handler, - .fill_theme_defaults = &ltk_submenu_fill_theme_defaults, - .uninitialize_theme = &ltk_submenu_uninitialize_theme, .cleanup = NULL, }, { .name = "submenuentry", - .ini_handler = &ltk_submenuentry_ini_handler, - .fill_theme_defaults = &ltk_submenuentry_fill_theme_defaults, - .uninitialize_theme = &ltk_submenuentry_uninitialize_theme, .cleanup = NULL, /* This "widget" is only needed to have separate styles for regular @@ -186,17 +149,11 @@ static ltk_widget_funcs widget_funcs[] = { }, { .name = "scrollbar", - .ini_handler = &ltk_scrollbar_ini_handler, - .fill_theme_defaults = &ltk_scrollbar_fill_theme_defaults, - .uninitialize_theme = &ltk_scrollbar_uninitialize_theme, .cleanup = NULL, }, { /* Handler for window theme. */ .name = "window", - .ini_handler = &ltk_window_ini_handler, - .fill_theme_defaults = &ltk_window_fill_theme_defaults, - .uninitialize_theme = &ltk_window_uninitialize_theme, .cleanup = &ltk_window_cleanup, } }; @@ -216,32 +173,29 @@ ltk_init(void) { ltk_fatal_errno("Unable to setup ltk directory.\n"); shared_data.cur_kbd = 0; + shared_data.renderdata = ltk_renderer_create(); + if (!shared_data.renderdata) + return 1; /* FIXME: clean up */ + /* FIXME: search different directories for config */ - /* FIXME: don't print error if config or theme file doesn't exist */ + /* FIXME: don't print error if config file doesn't exist */ char *config_path = ltk_strcat_useful(ltk_dir, "/ltk.cfg"); - char *theme_path; + ltk_free0(ltk_dir); char *errstr = NULL; - if (ltk_config_parsefile(config_path, &errstr)) { + if (ltk_config_parsefile(shared_data.renderdata, config_path, &errstr)) { if (errstr) { ltk_warn("Unable to load config: %s\n", errstr); ltk_free0(errstr); } - if (ltk_config_load_default(&errstr)) { + if (ltk_config_load_default(shared_data.renderdata, &errstr)) { /* FIXME: I guess errstr isn't freed here, but whatever */ /* FIXME: return error instead of dying */ ltk_fatal("Unable to load default config: %s\n", errstr); } } ltk_free0(config_path); - theme_path = ltk_strcat_useful(ltk_dir, "/theme.ini"); - ltk_free0(ltk_dir); - shared_data.renderdata = ltk_renderer_create(); - if (!shared_data.renderdata) - return 1; /* FIXME: clean up */ + ltk_events_init(shared_data.renderdata); - ltk_load_theme(theme_path); - ltk_free0(theme_path); - /* FIXME: maybe "general" theme instead of window theme? */ ltk_window_theme *window_theme = ltk_window_get_theme(); shared_data.text_context = ltk_text_context_create(shared_data.renderdata, window_theme->font); shared_data.clipboard = ltk_clipboard_create(shared_data.renderdata); @@ -382,7 +336,8 @@ ltk_mainloop(void) { void ltk_deinit(void) { - if (running) + /* if renderdata is NULL, the other initialization can't have happened either */ + if (running || !shared_data.renderdata) return; if (shared_data.cmds) { for (size_t i = 0; i < ltk_array_len(shared_data.cmds); i++) { @@ -403,11 +358,11 @@ ltk_deinit(void) { if (shared_data.rwindows) ltk_array_destroy(rwindow, shared_data.rwindows); shared_data.rwindows = NULL; - ltk_config_cleanup(); for (size_t i = 0; i < LENGTH(widget_funcs); i++) { if (widget_funcs[i].cleanup) widget_funcs[i].cleanup(); } + ltk_config_cleanup(shared_data.renderdata); if (shared_data.text_context) ltk_text_context_destroy(shared_data.text_context); shared_data.text_context = NULL; @@ -415,10 +370,7 @@ ltk_deinit(void) { ltk_clipboard_destroy(shared_data.clipboard); shared_data.clipboard = NULL; ltk_events_cleanup(); - if (shared_data.renderdata) { - ltk_uninitialize_theme(); - ltk_renderer_destroy(shared_data.renderdata); - } + ltk_renderer_destroy(shared_data.renderdata); shared_data.renderdata = NULL; } @@ -511,45 +463,6 @@ ltk_register_timer(long first, long repeat, void (*callback)(ltk_callback_arg da return id; } -/* FIXME: standardize return codes - usually, 0 is returned on success, but ini.h - uses 1 on success, so this is all a bit confusing */ -/* FIXME: switch away from ini.h */ -static int -ltk_ini_handler(void *renderdata, const char *widget, const char *prop, const char *value) { - for (size_t i = 0; i < LENGTH(widget_funcs); i++) { - if (widget_funcs[i].ini_handler && !strcmp(widget, widget_funcs[i].name)) { - widget_funcs[i].ini_handler(renderdata, prop, value); - return 1; - } - } - return 0; -} - -/* FIXME: don't call ltk_fatal, instead return error from ltk_init */ -static void -ltk_load_theme(const char *path) { - /* FIXME: give line number in error message */ - if (ini_parse(path, ltk_ini_handler, shared_data.renderdata) != 0) { - ltk_warn("Unable to load theme.\n"); - } - for (size_t i = 0; i < LENGTH(widget_funcs); i++) { - if (widget_funcs[i].fill_theme_defaults) { - if (widget_funcs[i].fill_theme_defaults(shared_data.renderdata)) { - ltk_uninitialize_theme(); - ltk_fatal("Unable to load theme defaults.\n"); - } - } - } -} - -static void -ltk_uninitialize_theme(void) { - for (size_t i = 0; i < LENGTH(widget_funcs); i++) { - if (widget_funcs[i].uninitialize_theme) - widget_funcs[i].uninitialize_theme(shared_data.renderdata); - } -} - int ltk_call_cmd(ltk_widget *caller, const char *cmd, size_t cmdlen, const char *text, size_t textlen) { /* FIXME: support environment variable $TMPDIR */ diff --git a/src/ltk/menu.c b/src/ltk/menu.c @@ -35,7 +35,6 @@ #include "text.h" #include "menu.h" #include "graphics.h" -#include "theme.h" #define MAX_MENU_BORDER_WIDTH 10000 #define MAX_MENU_PAD 50000 @@ -208,22 +207,6 @@ static ltk_theme_parseinfo menu_parseinfo[] = { {"scroll-background", THEME_COLOR, {.color = &menu_theme.scroll_background}, {.color = "#333333"}, 0, 0, 0}, {"scroll-arrow-color", THEME_COLOR, {.color = &menu_theme.scroll_arrow_color}, {.color = "#000000"}, 0, 0, 0}, }; -static int menu_parseinfo_sorted = 0; - -int -ltk_menu_ini_handler(ltk_renderdata *data, const char *prop, const char *value) { - return ltk_theme_handle_value(data, "menu", prop, value, menu_parseinfo, LENGTH(menu_parseinfo), &menu_parseinfo_sorted); -} - -int -ltk_menu_fill_theme_defaults(ltk_renderdata *data) { - return ltk_theme_fill_defaults(data, "menu", menu_parseinfo, LENGTH(menu_parseinfo)); -} - -void -ltk_menu_uninitialize_theme(ltk_renderdata *data) { - ltk_theme_uninitialize(data, menu_parseinfo, LENGTH(menu_parseinfo)); -} static ltk_theme_parseinfo menu_entry_parseinfo[] = { {"text-pad", THEME_SIZE, {.size = &menu_entry_theme.text_pad}, {.size = {.val = 100, .unit = LTK_UNIT_MM}}, 0, MAX_MENU_PAD, 0}, @@ -245,22 +228,6 @@ static ltk_theme_parseinfo menu_entry_parseinfo[] = { {"border-disabled", THEME_COLOR, {.color = &menu_entry_theme.border_disabled}, {.color = "#FFFFFF"}, 0, 0, 0}, {"fill-disabled", THEME_COLOR, {.color = &menu_entry_theme.fill_disabled}, {.color = "#292929"}, 0, 0, 0}, }; -static int menu_entry_parseinfo_sorted = 0; - -int -ltk_menuentry_ini_handler(ltk_renderdata *data, const char *prop, const char *value) { - return ltk_theme_handle_value(data, "menu-entry", prop, value, menu_entry_parseinfo, LENGTH(menu_entry_parseinfo), &menu_entry_parseinfo_sorted); -} - -int -ltk_menuentry_fill_theme_defaults(ltk_renderdata *data) { - return ltk_theme_fill_defaults(data, "menu-entry", menu_entry_parseinfo, LENGTH(menu_entry_parseinfo)); -} - -void -ltk_menuentry_uninitialize_theme(ltk_renderdata *data) { - ltk_theme_uninitialize(data, menu_entry_parseinfo, LENGTH(menu_entry_parseinfo)); -} static ltk_theme_parseinfo submenu_parseinfo[] = { {"pad", THEME_SIZE, {.size = &submenu_theme.pad}, {.size = {.val = 0, .unit = LTK_UNIT_PX}}, 0, MAX_MENU_PAD, 0}, @@ -273,22 +240,6 @@ static ltk_theme_parseinfo submenu_parseinfo[] = { {"scroll-background", THEME_COLOR, {.color = &submenu_theme.scroll_background}, {.color = "#333333"}, 0, 0, 0}, {"scroll-arrow-color", THEME_COLOR, {.color = &submenu_theme.scroll_arrow_color}, {.color = "#000000"}, 0, 0, 0}, }; -static int submenu_parseinfo_sorted = 0; - -int -ltk_submenu_ini_handler(ltk_renderdata *data, const char *prop, const char *value) { - return ltk_theme_handle_value(data, "submenu", prop, value, submenu_parseinfo, LENGTH(submenu_parseinfo), &submenu_parseinfo_sorted); -} - -int -ltk_submenu_fill_theme_defaults(ltk_renderdata *data) { - return ltk_theme_fill_defaults(data, "submenu", submenu_parseinfo, LENGTH(submenu_parseinfo)); -} - -void -ltk_submenu_uninitialize_theme(ltk_renderdata *data) { - ltk_theme_uninitialize(data, submenu_parseinfo, LENGTH(submenu_parseinfo)); -} static ltk_theme_parseinfo submenu_entry_parseinfo[] = { {"text-pad", THEME_SIZE, {.size = &submenu_entry_theme.text_pad}, {.size = {.val = 100, .unit = LTK_UNIT_MM}}, 0, MAX_MENU_PAD, 0}, @@ -310,21 +261,28 @@ static ltk_theme_parseinfo submenu_entry_parseinfo[] = { {"border-disabled", THEME_COLOR, {.color = &submenu_entry_theme.border_disabled}, {.color = "#FFFFFF"}, 0, 0, 0}, {"fill-disabled", THEME_COLOR, {.color = &submenu_entry_theme.fill_disabled}, {.color = "#292929"}, 0, 0, 0}, }; -static int submenu_entry_parseinfo_sorted = 0; +void +ltk_menu_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len) { + *p = menu_parseinfo; + *len = LENGTH(menu_parseinfo); +} -int -ltk_submenuentry_ini_handler(ltk_renderdata *data, const char *prop, const char *value) { - return ltk_theme_handle_value(data, "submenu-entry", prop, value, submenu_entry_parseinfo, LENGTH(submenu_entry_parseinfo), &submenu_entry_parseinfo_sorted); +void +ltk_submenu_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len) { + *p = submenu_parseinfo; + *len = LENGTH(submenu_parseinfo); } -int -ltk_submenuentry_fill_theme_defaults(ltk_renderdata *data) { - return ltk_theme_fill_defaults(data, "submenu-entry", submenu_entry_parseinfo, LENGTH(submenu_entry_parseinfo)); +void +ltk_menuentry_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len) { + *p = menu_entry_parseinfo; + *len = LENGTH(menu_entry_parseinfo); } void -ltk_submenuentry_uninitialize_theme(ltk_renderdata *data) { - ltk_theme_uninitialize(data, submenu_entry_parseinfo, LENGTH(submenu_entry_parseinfo)); +ltk_submenuentry_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len) { + *p = submenu_entry_parseinfo; + *len = LENGTH(submenu_entry_parseinfo); } static void diff --git a/src/ltk/menu.h b/src/ltk/menu.h @@ -67,20 +67,6 @@ struct ltk_menuentry { should submenus also allow setting orientation? -> would maybe look weird in some cases */ -int ltk_menu_ini_handler(ltk_renderdata *data, const char *prop, const char *value); -int ltk_menu_fill_theme_defaults(ltk_renderdata *data); -void ltk_menu_uninitialize_theme(ltk_renderdata *data); -int ltk_submenu_ini_handler(ltk_renderdata *data, const char *prop, const char *value); -int ltk_submenu_fill_theme_defaults(ltk_renderdata *data); -void ltk_submenu_uninitialize_theme(ltk_renderdata *data); - -int ltk_menuentry_ini_handler(ltk_renderdata *data, const char *prop, const char *value); -int ltk_menuentry_fill_theme_defaults(ltk_renderdata *data); -void ltk_menuentry_uninitialize_theme(ltk_renderdata *data); -int ltk_submenuentry_ini_handler(ltk_renderdata *data, const char *prop, const char *value); -int ltk_submenuentry_fill_theme_defaults(ltk_renderdata *data); -void ltk_submenuentry_uninitialize_theme(ltk_renderdata *data); - /* FIXME: allow orientation */ ltk_menu *ltk_menu_create(ltk_window *window); ltk_menu *ltk_submenu_create(ltk_window *window); diff --git a/src/ltk/num.c b/src/ltk/num.c @@ -249,7 +249,7 @@ ltk_strtoscalednum( /* FIXME: warn if there are any more digits */ } - /* FIXME: decrease code duplication */ + /* FIXME: decrease code duplication and check that this actually works */ long long cutoff = ll < 0 ? LLONG_MIN : LLONG_MAX; long long cutlim = cutoff % 100; cutoff /= 100; diff --git a/src/ltk/scrollbar.c b/src/ltk/scrollbar.c @@ -23,7 +23,6 @@ #include "widget.h" #include "util.h" #include "scrollbar.h" -#include "theme.h" #include "eventdefs.h" #define MAX_SCROLLBAR_WIDTH 10000 /* completely arbitrary */ @@ -74,21 +73,11 @@ static ltk_theme_parseinfo parseinfo[] = { {"fg-pressed", THEME_COLOR, {.color = &theme.fg_pressed}, {.color = "#113355"}, 0, 0, 0}, {"fg-disabled", THEME_COLOR, {.color = &theme.fg_disabled}, {.color = "#292929"}, 0, 0, 0}, }; -static int parseinfo_sorted = 0; - -int -ltk_scrollbar_ini_handler(ltk_renderdata *data, const char *prop, const char *value) { - return ltk_theme_handle_value(data, "scrollbar", prop, value, parseinfo, LENGTH(parseinfo), &parseinfo_sorted); -} - -int -ltk_scrollbar_fill_theme_defaults(ltk_renderdata *data) { - return ltk_theme_fill_defaults(data, "scrollbar", parseinfo, LENGTH(parseinfo)); -} void -ltk_scrollbar_uninitialize_theme(ltk_renderdata *data) { - ltk_theme_uninitialize(data, parseinfo, LENGTH(parseinfo)); +ltk_scrollbar_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len) { + *p = parseinfo; + *len = LENGTH(parseinfo); } void diff --git a/src/ltk/scrollbar.h b/src/ltk/scrollbar.h @@ -37,8 +37,4 @@ void ltk_scrollbar_set_virtual_size(ltk_scrollbar *scrollbar, int virtual_size); ltk_scrollbar *ltk_scrollbar_create(ltk_window *window, ltk_orientation orient); void ltk_scrollbar_scroll(ltk_widget *self, int delta, int scaled); -int ltk_scrollbar_ini_handler(ltk_renderdata *data, const char *prop, const char *value); -int ltk_scrollbar_fill_theme_defaults(ltk_renderdata *data); -void ltk_scrollbar_uninitialize_theme(ltk_renderdata *data); - #endif /* LTK_SCROLLBAR_H */ diff --git a/src/ltk/theme.c b/src/ltk/theme.c @@ -1,211 +0,0 @@ -/* - * Copyright (c) 2022-2024 lumidify <nobody@lumidify.org> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <stdlib.h> -#include <string.h> - -#include "graphics.h" -#include "util.h" -#include "theme.h" -#include "memory.h" - -/* FIXME: handle '#' or no '#' in color specification */ -static int -search_helper(const void *keyv, const void *entryv) { - char *key = (char *)keyv; - ltk_theme_parseinfo *entry = (ltk_theme_parseinfo *)entryv; - return strcmp(key, entry->key); -} - -static int -sort_helper(const void *entry1v, const void *entry2v) { - ltk_theme_parseinfo *entry1 = (ltk_theme_parseinfo *)entry1v; - ltk_theme_parseinfo *entry2 = (ltk_theme_parseinfo *)entry2v; - return strcmp(entry1->key, entry2->key); -} - -/* FIXME: more information for errors */ -int -ltk_theme_handle_value(ltk_renderdata *renderdata, char *debug_name, const char *prop, const char *value, ltk_theme_parseinfo *parseinfo, size_t len, int *sorted) { - if (!*sorted) { - qsort(parseinfo, len, sizeof(ltk_theme_parseinfo), &sort_helper); - *sorted = 1; - } - ltk_theme_parseinfo *entry = bsearch(prop, parseinfo, len, sizeof(ltk_theme_parseinfo), &search_helper); - if (!entry) { - ltk_warn("Invalid property '%s:%s'.\n", debug_name, prop); - return 1; - } else if (entry->initialized) { - ltk_warn("Duplicate setting for property '%s:%s'.\n", debug_name, prop); - return 1; - } - const char *errstr = NULL; - switch (entry->type) { - case THEME_INT: - *(entry->ptr.i) = ltk_strtonum(value, entry->min, entry->max, &errstr); - if (errstr) { - ltk_warn("Invalid value '%s' for property '%s:%s'.\n", value, debug_name, prop); - return 1; - } else { - entry->initialized = 1; - } - break; - case THEME_SIZE: - entry->ptr.size->unit = LTK_UNIT_PX; - char *endptr = NULL; - /* this already takes care of overflow prevention because entry->min and entry->max are int */ - entry->ptr.size->val = ltk_strtoscalednum(value, entry->min, entry->max, &endptr, &errstr); - if (errstr) { - ltk_warn("Invalid value '%s' for property '%s:%s': %s\n", value, debug_name, prop, errstr); - return 1; - } - if (*endptr == '\0') { - /* NOP */ - } else if (!strcmp(endptr, "px")) { - entry->ptr.size->unit = LTK_UNIT_PX; - } else if (!strcmp(endptr, "pt")) { - entry->ptr.size->unit = LTK_UNIT_PT; - } else if (!strcmp(endptr, "mm")) { - entry->ptr.size->unit = LTK_UNIT_MM; - } else { - ltk_warn("Invalid value '%s' for property '%s:%s'\n", value, debug_name, prop); - return 1; - } - entry->initialized = 1; - break; - case THEME_STRING: - /* FIXME: check if already set? */ - *(entry->ptr.str) = ltk_strdup(value); - entry->initialized = 1; - break; - case THEME_COLOR: - if (!(*(entry->ptr.color) = ltk_color_create(renderdata, value))) { - ltk_warn("Unable to create color '%s' for property '%s:%s'.\n", value, debug_name, prop); - return 1; - } else { - entry->initialized = 1; - } - break; - case THEME_BOOL: - if (strcmp(value, "true") == 0) { - *(entry->ptr.b) = 1; - } else if (strcmp(value, "false") == 0) { - *(entry->ptr.b) = 0; - } else { - ltk_warn("Invalid value '%s' for property '%s:%s'.\n", value, debug_name, prop); - return 1; - } - entry->initialized = 1; - break; - case THEME_BORDERSIDES: - *(entry->ptr.border) = LTK_BORDER_NONE; - for (const char *c = value; *c != '\0'; c++) { - switch (*c) { - case 't': - *(entry->ptr.border) |= LTK_BORDER_TOP; - break; - case 'b': - *(entry->ptr.border) |= LTK_BORDER_BOTTOM; - break; - case 'l': - *(entry->ptr.border) |= LTK_BORDER_LEFT; - break; - case 'r': - *(entry->ptr.border) |= LTK_BORDER_RIGHT; - break; - default: - ltk_warn("Invalid value '%s' for property '%s:%s'.\n", value, debug_name, prop); - return 1; - } - } - entry->initialized = 1; - break; - default: - ltk_fatal("Invalid theme setting type. This should not happen.\n"); - /* TODO: ltk_assert(0); */ - } - return 0; -} - -int -ltk_theme_fill_defaults(ltk_renderdata *renderdata, char *debug_name, ltk_theme_parseinfo *parseinfo, size_t len) { - for (size_t i = 0; i < len; i++) { - ltk_theme_parseinfo *e = &parseinfo[i]; - if (e->initialized) - continue; - switch (e->type) { - case THEME_INT: - *(e->ptr.i) = e->defaultval.i; - e->initialized = 1; - break; - case THEME_SIZE: - *(e->ptr.size) = e->defaultval.size; - e->initialized = 1; - break; - case THEME_STRING: - *(e->ptr.str) = ltk_strdup(e->defaultval.str); - e->initialized = 1; - break; - case THEME_COLOR: - if (!(*(e->ptr.color) = ltk_color_create(renderdata, e->defaultval.color))) { - ltk_warn("Unable to create default color '%s' for property '%s:%s'.\n", e->defaultval.color, debug_name, e->key); - return 1; - } else { - e->initialized = 1; - } - break; - case THEME_BOOL: - *(e->ptr.b) = e->defaultval.b; - e->initialized = 1; - break; - case THEME_BORDERSIDES: - *(e->ptr.border) = e->defaultval.border; - e->initialized = 1; - break; - default: - ltk_fatal("Invalid theme setting type. This should not happen.\n"); - /* TODO: ltk_assert(0); */ - } - } - return 0; -} - -void -ltk_theme_uninitialize(ltk_renderdata *renderdata, ltk_theme_parseinfo *parseinfo, size_t len) { - for (size_t i = 0; i < len; i++) { - ltk_theme_parseinfo *e = &parseinfo[i]; - if (!e->initialized) - continue; - switch (e->type) { - case THEME_STRING: - ltk_free(*(e->ptr.str)); - e->initialized = 0; - break; - case THEME_COLOR: - ltk_color_destroy(renderdata, *(e->ptr.color)); - e->initialized = 0; - break; - case THEME_SIZE: - case THEME_INT: - case THEME_BOOL: - case THEME_BORDERSIDES: - e->initialized = 0; - break; - default: - ltk_fatal("Invalid theme setting type. This should not happen.\n"); - } - } -} diff --git a/src/ltk/theme.h b/src/ltk/theme.h @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2022-2024 lumidify <nobody@lumidify.org> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef LTK_THEME_H -#define LTK_THEME_H - -#include <stddef.h> -#include "color.h" -#include "graphics.h" - -typedef enum { - THEME_STRING, - THEME_COLOR, - THEME_INT, - THEME_BOOL, - THEME_BORDERSIDES, - THEME_SIZE, -} ltk_theme_datatype; - -typedef struct { - char *key; - ltk_theme_datatype type; - /* Note: Bool and int are both integers, but they are - separate just to make it a bit clearer */ - union { - char **str; - ltk_color **color; - int *i; - int *b; - ltk_border_sides *border; - ltk_size *size; - } ptr; - /* Note: The default color is also given as a string - because it has to be allocated first (it is only a - different entry in the union in order to make it - a bit clearer) */ - union { - char *str; - char *color; - int i; - int b; - ltk_border_sides border; - ltk_size size; - } defaultval; - /* FIXME: min/max doesn't make too much sense for sizes since they - can use different units, but that shouldn't matter for now because - min/max is only used as a sanity check to avoid extreme sizes or - negative sizes where that isn't allowed */ - int min, max; /* only for integers or sizes */ - int initialized; -} ltk_theme_parseinfo; - -/* Both return 1 on error, 0 on success */ -int ltk_theme_handle_value(ltk_renderdata *renderdata, char *debug_name, const char *prop, const char *value, ltk_theme_parseinfo *parseinfo, size_t len, int *sorted); -int ltk_theme_fill_defaults(ltk_renderdata *renderdata, char *debug_name, ltk_theme_parseinfo *parseinfo, size_t len); -void ltk_theme_uninitialize(ltk_renderdata *renderdata, ltk_theme_parseinfo *parseinfo, size_t len); - -#endif /* LTK_THEME_H */ diff --git a/src/ltk/widget_internal.h b/src/ltk/widget_internal.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024 lumidify <nobody@lumidify.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef LTK_WIDGET_INTERNAL_H +#define LTK_WIDGET_INTERNAL_H + +#include <stddef.h> + +#include "array.h" +#include "config.h" +#include "window.h" +#include "graphics.h" + +void ltk_window_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len); +void ltk_button_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len); +void ltk_label_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len); +void ltk_menu_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len); +void ltk_menuentry_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len); +void ltk_submenu_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len); +void ltk_submenuentry_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len); +void ltk_scrollbar_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len); + +void ltk_entry_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len); +void ltk_entry_cleanup(void); +void ltk_entry_get_keybinding_parseinfo( + ltk_keybinding_cb **press_cbs_ret, size_t *press_len_ret, + ltk_keybinding_cb **release_cbs_ret, size_t *release_len_ret, + ltk_array(keypress) **presses_ret, ltk_array(keyrelease) **releases_ret +); + +void ltk_window_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len); +ltk_window *ltk_window_create_intern(ltk_renderdata *data, const char *title, int x, int y, unsigned int w, unsigned int h); +void ltk_window_destroy_intern(ltk_window *window); +void ltk_window_cleanup(void); +void ltk_window_get_keybinding_parseinfo( + ltk_keybinding_cb **press_cbs_ret, size_t *press_len_ret, + ltk_keybinding_cb **release_cbs_ret, size_t *release_len_ret, + ltk_array(keypress) **presses_ret, ltk_array(keyrelease) **releases_ret +); + +#endif /* LTK_WIDGET_INTERNAL_H */ diff --git a/src/ltk/window.c b/src/ltk/window.c @@ -22,7 +22,6 @@ #include "ltk.h" #include "util.h" #include "array.h" -#include "theme.h" #include "widget.h" #include "window.h" #include "memory.h" @@ -126,21 +125,11 @@ static ltk_theme_parseinfo theme_parseinfo[] = { {"font", THEME_STRING, {.str = &theme.font}, {.str = "Monospace"}, 0, 0, 0}, {"font-size", THEME_SIZE, {.size = &theme.font_size}, {.size = {.val = 1200, .unit = LTK_UNIT_PT}}, 0, MAX_WINDOW_FONT_SIZE, 0}, }; -static int theme_parseinfo_sorted = 0; - -int -ltk_window_fill_theme_defaults(ltk_renderdata *data) { - return ltk_theme_fill_defaults(data, "window", theme_parseinfo, LENGTH(theme_parseinfo)); -} - -int -ltk_window_ini_handler(ltk_renderdata *data, const char *prop, const char *value) { - return ltk_theme_handle_value(data, "window", prop, value, theme_parseinfo, LENGTH(theme_parseinfo), &theme_parseinfo_sorted); -} void -ltk_window_uninitialize_theme(ltk_renderdata *data) { - ltk_theme_uninitialize(data, theme_parseinfo, LENGTH(theme_parseinfo)); +ltk_window_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len) { + *p = theme_parseinfo; + *len = LENGTH(theme_parseinfo); } /* FIXME: maybe ltk_fatal if ltk not initialized? */ @@ -631,8 +620,8 @@ ltk_window_create_intern(ltk_renderdata *data, const char *title, int x, int y, window->popups_num = window->popups_alloc = 0; window->popups_locked = 0; - ltk_config *config = ltk_config_get(); - unsigned int dpi = (unsigned int)round(config->general.dpi_scale * config->general.fixed_dpi); + ltk_general_config *config = ltk_config_get_general(); + unsigned int dpi = (unsigned int)round(config->dpi_scale * config->fixed_dpi * 5); window->renderwindow = ltk_renderer_create_window(data, title, x, y, w, h, dpi); ltk_renderer_set_window_properties(window->renderwindow, theme.bg); window->theme = &theme; @@ -894,8 +883,8 @@ static int prev_child(ltk_window *window) { if (!window->root_widget) return 0; - ltk_config *config = ltk_config_get(); - ltk_widget_flags act_flags = config->general.all_activatable ? LTK_ACTIVATABLE_ALWAYS : LTK_ACTIVATABLE_NORMAL; + ltk_general_config *config = ltk_config_get_general(); + ltk_widget_flags act_flags = config->all_activatable ? LTK_ACTIVATABLE_ALWAYS : LTK_ACTIVATABLE_NORMAL; ltk_widget *new, *cur = window->active_widget; int changed = 0; ltk_widget *prevcur = cur; @@ -955,8 +944,8 @@ static int next_child(ltk_window *window) { if (!window->root_widget) return 0; - ltk_config *config = ltk_config_get(); - ltk_widget_flags act_flags = config->general.all_activatable ? LTK_ACTIVATABLE_ALWAYS : LTK_ACTIVATABLE_NORMAL; + ltk_general_config *config = ltk_config_get_general(); + ltk_widget_flags act_flags = config->all_activatable ? LTK_ACTIVATABLE_ALWAYS : LTK_ACTIVATABLE_NORMAL; ltk_widget *new, *cur = window->active_widget; int changed = 0; ltk_widget *prevcur = cur; @@ -1040,8 +1029,8 @@ static int left_top_child(ltk_window *window, int left) { if (!window->root_widget) return 0; - ltk_config *config = ltk_config_get(); - ltk_widget_flags act_flags = config->general.all_activatable ? LTK_ACTIVATABLE_ALWAYS : LTK_ACTIVATABLE_NORMAL; + ltk_general_config *config = ltk_config_get_general(); + ltk_widget_flags act_flags = config->all_activatable ? LTK_ACTIVATABLE_ALWAYS : LTK_ACTIVATABLE_NORMAL; ltk_widget *new, *cur = window->active_widget; ltk_rect old_rect = {0, 0, 0, 0}; ltk_widget *last_activatable = NULL; @@ -1103,8 +1092,8 @@ static int right_bottom_child(ltk_window *window, int right) { if (!window->root_widget) return 0; - ltk_config *config = ltk_config_get(); - ltk_widget_flags act_flags = config->general.all_activatable ? LTK_ACTIVATABLE_ALWAYS : LTK_ACTIVATABLE_NORMAL; + ltk_general_config *config = ltk_config_get_general(); + ltk_widget_flags act_flags = config->all_activatable ? LTK_ACTIVATABLE_ALWAYS : LTK_ACTIVATABLE_NORMAL; ltk_widget *new, *cur = window->active_widget; int changed = 0; ltk_rect old_rect = {0, 0, 0, 0}; diff --git a/src/ltk/window.h b/src/ltk/window.h @@ -61,10 +61,7 @@ typedef struct ltk_window { char popups_locked; } ltk_window; -/* FIXME: should be private to ltk */ -ltk_window *ltk_window_create_intern(ltk_renderdata *data, const char *title, int x, int y, unsigned int w, unsigned int h); -void ltk_window_destroy_intern(ltk_window *window); - +/* FIXME: which of these should be internal to LTK? */ void ltk_window_handle_event(ltk_window *window, ltk_event *event); void ltk_window_fake_motion_event(ltk_window *window, int x, int y); @@ -84,16 +81,6 @@ void ltk_window_register_popup(ltk_window *window, ltk_widget *popup); void ltk_window_unregister_popup(ltk_window *window, ltk_widget *popup); void ltk_window_unregister_all_popups(ltk_window *window); -/* FIXME: these should be private to ltk */ -void ltk_window_cleanup(void); -void ltk_window_get_keybinding_parseinfo( - ltk_keybinding_cb **press_cbs_ret, size_t *press_len_ret, - ltk_keybinding_cb **release_cbs_ret, size_t *release_len_ret, - ltk_array(keypress) **presses_ret, ltk_array(keyrelease) **releases_ret -); -int ltk_window_fill_theme_defaults(ltk_renderdata *data); -int ltk_window_ini_handler(ltk_renderdata *data, const char *prop, const char *value); -void ltk_window_uninitialize_theme(ltk_renderdata *data); ltk_window_theme *ltk_window_get_theme(void); #endif /* LTK_WINDOW_H */