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:
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", <k_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", <k_general_get_theme_parseinfo, NULL},
+ {"theme:window", <k_window_get_theme_parseinfo, NULL},
+ {"theme:button", <k_button_get_theme_parseinfo, "window"},
+ {"theme:entry", <k_entry_get_theme_parseinfo, "window"},
+ {"theme:label", <k_label_get_theme_parseinfo, "window"},
+ {"theme:scrollbar", <k_scrollbar_get_theme_parseinfo, "window"},
+ {"theme:menu", <k_menu_get_theme_parseinfo, "window"},
+ {"theme:menuentry", <k_menuentry_get_theme_parseinfo, "window"},
+ {"theme:submenu", <k_submenu_get_theme_parseinfo, "window"},
+ {"theme:submenuentry", <k_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 = <k_button_ini_handler,
- .fill_theme_defaults = <k_button_fill_theme_defaults,
- .uninitialize_theme = <k_button_uninitialize_theme,
.cleanup = NULL,
},
{
.name = "entry",
- .ini_handler = <k_entry_ini_handler,
- .fill_theme_defaults = <k_entry_fill_theme_defaults,
- .uninitialize_theme = <k_entry_uninitialize_theme,
.cleanup = <k_entry_cleanup,
},
{
.name = "grid",
- .ini_handler = NULL,
- .fill_theme_defaults = NULL,
- .uninitialize_theme = NULL,
.cleanup = NULL,
},
{
.name = "label",
- .ini_handler = <k_label_ini_handler,
- .fill_theme_defaults = <k_label_fill_theme_defaults,
- .uninitialize_theme = <k_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 = <k_menu_ini_handler,
- .fill_theme_defaults = <k_menu_fill_theme_defaults,
- .uninitialize_theme = <k_menu_uninitialize_theme,
.cleanup = NULL,
},
{
.name = "menuentry",
- .ini_handler = <k_menuentry_ini_handler,
- .fill_theme_defaults = <k_menuentry_fill_theme_defaults,
- .uninitialize_theme = <k_menuentry_uninitialize_theme,
.cleanup = NULL,
},
{
.name = "submenu",
- .ini_handler = <k_submenu_ini_handler,
- .fill_theme_defaults = <k_submenu_fill_theme_defaults,
- .uninitialize_theme = <k_submenu_uninitialize_theme,
.cleanup = NULL,
},
{
.name = "submenuentry",
- .ini_handler = <k_submenuentry_ini_handler,
- .fill_theme_defaults = <k_submenuentry_fill_theme_defaults,
- .uninitialize_theme = <k_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 = <k_scrollbar_ini_handler,
- .fill_theme_defaults = <k_scrollbar_fill_theme_defaults,
- .uninitialize_theme = <k_scrollbar_uninitialize_theme,
.cleanup = NULL,
},
{
/* Handler for window theme. */
.name = "window",
- .ini_handler = <k_window_ini_handler,
- .fill_theme_defaults = <k_window_fill_theme_defaults,
- .uninitialize_theme = <k_window_uninitialize_theme,
.cleanup = <k_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 */