commit 6d318819d95cceedcc5bca92e45cee03142ed3b5
Author: lumidify <nobody@lumidify.org>
Date: Sun, 1 Jan 2017 09:45:55 +0100
Initial commit
Diffstat:
A | LICENSE | | | 22 | ++++++++++++++++++++++ |
A | Makefile | | | 7 | +++++++ |
A | README.md | | | 11 | +++++++++++ |
A | button.c | | | 257 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | button.h | | | 78 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | cJSON.c | | | 1492 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | cJSON.h | | | 160 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | common.c | | | 80 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | common.h | | | 52 | ++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | event.c | | | 42 | ++++++++++++++++++++++++++++++++++++++++++ |
A | event.h | | | 31 | +++++++++++++++++++++++++++++++ |
A | grid.c | | | 313 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | grid.h | | | 53 | +++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | ltk.c | | | 79 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | ltk.h | | | 57 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | main.c | | | 52 | ++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | test1.c | | | 41 | +++++++++++++++++++++++++++++++++++++++++ |
A | theme.c | | | 64 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | theme.h | | | 39 | +++++++++++++++++++++++++++++++++++++++ |
A | themes/default.json | | | 34 | ++++++++++++++++++++++++++++++++++ |
A | uthash.h | | | 1074 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | widget.c | | | 23 | +++++++++++++++++++++++ |
A | widget.h | | | 68 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | window.c | | | 165 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | window.h | | | 56 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
25 files changed, 4350 insertions(+), 0 deletions(-)
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,22 @@
+MIT/X Consortium License
+
+The Lumidify ToolKit (LTK)
+Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Makefile b/Makefile
@@ -0,0 +1,7 @@
+LIBS = -lX11 -lm -ldl
+STD = -std=c89
+FLAGS = -g -w -Wall -Werror -Wextra -pedantic
+CFILES = ltk.c event.c cJSON.c common.c widget.c grid.c window.c theme.c button.c test1.c
+
+all: test1.c
+ gcc $(STD) $(FLAGS) $(LIBS) $(CFILES) -o test
diff --git a/README.md b/README.md
@@ -0,0 +1,11 @@
+# LTK - Lumidify Toolkit
+
+This is work in progress.
+
+Please do not attempt to actually use any of the code.
+
+## Licenses of Other Libraries Used
+
+[uthash](https://troydhanson.github.io/uthash/): [BSD Revised](https://troydhanson.github.io/uthash/license.html)
+
+[cJSON](https://github.com/DaveGamble/cJSON): [MIT/X](https://github.com/DaveGamble/cJSON/blob/master/LICENSE)
diff --git a/button.c b/button.c
@@ -0,0 +1,257 @@
+/*
+ * This file is part of the Lumidify ToolKit (LTK)
+ * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "ltk.h"
+
+LtkButtonTheme *ltk_parse_button_theme(cJSON *button_json)
+{
+ LtkButtonTheme *button_theme = malloc(sizeof(LtkButtonTheme));
+ cJSON *normal_json = cJSON_GetObjectItem(button_json, "normal");
+ if (!normal_json)
+ {
+ printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
+ }
+ cJSON *border_width = cJSON_GetObjectItem(normal_json, "border-width");
+ cJSON *font_size = cJSON_GetObjectItem(normal_json, "font-size");
+ cJSON *border_color = cJSON_GetObjectItem(normal_json, "border-color");
+ cJSON *fill_color = cJSON_GetObjectItem(normal_json, "fill-color");
+ cJSON *padding_left = cJSON_GetObjectItem(normal_json, "padding-left");
+ cJSON *padding_right = cJSON_GetObjectItem(normal_json, "padding-right");
+ cJSON *padding_top = cJSON_GetObjectItem(normal_json, "padding-top");
+ cJSON *padding_bottom = cJSON_GetObjectItem(normal_json, "padding-bottom");
+
+ button_theme->border_width = border_width != NULL ? border_width->valueint : 0;
+ button_theme->font_size = font_size != NULL ? font_size->valueint : 20;
+ button_theme->border_color = ltk_create_xcolor(border_color->valuestring);
+ button_theme->fill_color = ltk_create_xcolor(fill_color->valuestring);
+ button_theme->padding_left = padding_left != NULL ? padding_left->valueint : 0;
+ button_theme->padding_right = padding_right != NULL ? padding_right->valueint : 0;
+ button_theme->padding_top = padding_top != NULL ? padding_top->valueint : 0;
+ button_theme->padding_bottom = padding_bottom != NULL ? padding_bottom->valueint : 0;
+
+ cJSON *hover_json = cJSON_GetObjectItem(button_json, "hover");
+ if (!hover_json)
+ {
+ printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
+ }
+ cJSON *border_width_hover = cJSON_GetObjectItem(hover_json, "border-width");
+ cJSON *font_size_hover = cJSON_GetObjectItem(hover_json, "font-size");
+ cJSON *border_color_hover = cJSON_GetObjectItem(hover_json, "border-color");
+ cJSON *fill_color_hover = cJSON_GetObjectItem(hover_json, "fill-color");
+
+ button_theme->border_width_hover = border_width_hover != NULL ? border_width_hover->valueint : button_theme->border_width;
+ button_theme->font_size_hover = font_size_hover != NULL ? font_size_hover->valueint : button_theme->font_size;
+ button_theme->border_color_hover = border_color_hover != NULL ? ltk_create_xcolor(border_color_hover->valuestring) : button_theme->border_color;
+ button_theme->fill_color_hover = fill_color_hover != NULL ? ltk_create_xcolor(fill_color_hover->valuestring) : button_theme->fill_color;
+
+ cJSON *pressed_json = cJSON_GetObjectItem(button_json, "pressed");
+ if (!pressed_json)
+ {
+ printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
+ }
+ cJSON *border_width_pressed = cJSON_GetObjectItem(pressed_json, "border-width");
+ cJSON *font_size_pressed = cJSON_GetObjectItem(pressed_json, "font-size");
+ cJSON *border_color_pressed = cJSON_GetObjectItem(pressed_json, "border-color");
+ cJSON *fill_color_pressed = cJSON_GetObjectItem(pressed_json, "fill-color");
+
+ button_theme->border_width_pressed = border_width_pressed != NULL ? border_width_pressed->valueint : button_theme->border_width;
+ button_theme->font_size_pressed = font_size_hover != NULL ? font_size_pressed->valueint : button_theme->font_size;
+ button_theme->border_color_pressed = border_color_pressed != NULL ? ltk_create_xcolor(border_color_pressed->valuestring) : button_theme->border_color;
+ button_theme->fill_color_pressed = fill_color_pressed != NULL ? ltk_create_xcolor(fill_color_pressed->valuestring) : button_theme->fill_color;
+
+ cJSON *active_json = cJSON_GetObjectItem(button_json, "active");
+ if (!active_json)
+ {
+ printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
+ }
+ cJSON *border_width_active = cJSON_GetObjectItem(active_json, "border-width");
+ cJSON *font_size_active = cJSON_GetObjectItem(active_json, "font-size");
+ cJSON *border_color_active = cJSON_GetObjectItem(active_json, "border-color");
+ cJSON *fill_color_active = cJSON_GetObjectItem(active_json, "fill-color");
+
+ button_theme->border_width_active = border_width_active != NULL ? border_width_active->valueint : button_theme->border_width;
+ button_theme->font_size_active = font_size_active != NULL ? font_size_active->valueint : button_theme->font_size;
+ button_theme->border_color_active = border_color_active != NULL ? ltk_create_xcolor(border_color_active->valuestring) : button_theme->border_color;
+ button_theme->fill_color_active = fill_color_active != NULL ? ltk_create_xcolor(fill_color_active->valuestring) : button_theme->fill_color;
+
+ cJSON *disabled_json = cJSON_GetObjectItem(button_json, "disabled");
+ if (!disabled_json)
+ {
+ printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
+ }
+ cJSON *border_width_disabled = cJSON_GetObjectItem(disabled_json, "border-width");
+ cJSON *font_size_disabled = cJSON_GetObjectItem(disabled_json, "font-size");
+ cJSON *border_color_disabled = cJSON_GetObjectItem(disabled_json, "border-color");
+ cJSON *fill_color_disabled = cJSON_GetObjectItem(disabled_json, "fill-color");
+
+ button_theme->border_width_disabled = border_width_disabled != NULL ? border_width_disabled->valueint : button_theme->border_width;
+ button_theme->font_size_disabled = font_size_disabled != NULL ? font_size_disabled->valueint : button_theme->font_size;
+ button_theme->border_color_disabled = border_color_disabled != NULL ? ltk_create_xcolor(border_color_disabled->valuestring) : button_theme->border_color;
+ button_theme->fill_color_disabled = fill_color_disabled != NULL ? ltk_create_xcolor(fill_color_disabled->valuestring) : button_theme->fill_color;
+
+ return button_theme;
+}
+
+void ltk_draw_button(void *widget)
+{
+ LtkButton *button = widget;
+ LtkButtonTheme *theme = ltk_global->theme->button;
+ LtkWindow *window = button->widget.window;
+ LtkRect rect = button->widget.rect;
+ XColor border_color;
+ XColor fill_color;
+ int border_width;
+ switch (button->widget.state)
+ {
+ case NORMAL:
+ border_color = theme->border_color;
+ fill_color = theme->fill_color;
+ border_width = theme->border_width;
+ break;
+ case HOVERACTIVE:
+ case HOVER:
+ border_color = theme->border_color_hover;
+ fill_color = theme->fill_color_hover;
+ border_width = theme->border_width_hover;
+ break;
+ case PRESSED:
+ border_color = theme->border_color_pressed;
+ fill_color = theme->fill_color_pressed;
+ border_width = theme->border_width_pressed;
+ break;
+ case ACTIVE:
+ border_color = theme->border_color_active;
+ fill_color = theme->fill_color_active;
+ border_width = theme->border_width_active;
+ break;
+ case DISABLED:
+ border_color = theme->border_color_disabled;
+ fill_color = theme->fill_color_disabled;
+ border_width = theme->border_width_disabled;
+ break;
+ default:
+ ltk_fatal("No style found for button!\n");
+ }
+ XSetForeground(ltk_global->display, window->gc, fill_color.pixel);
+ XFillRectangle(ltk_global->display, window->xwindow, window->gc, rect.x, rect.y, rect.w, rect.h);
+ XSetForeground(ltk_global->display, window->gc, border_color.pixel);
+ XSetLineAttributes(ltk_global->display, window->gc, border_width, LineSolid, CapButt, JoinMiter);
+ XDrawRectangle(ltk_global->display, window->xwindow, window->gc, rect.x, rect.y, rect.w, rect.h);
+}
+
+LtkButton *ltk_create_button(LtkWindow *window, const char *text, void (*callback)(void))
+{
+ LtkButton *button = malloc(sizeof(LtkButton));
+
+ if (button == NULL)
+ {
+ printf("Button could not be created.\n");
+ exit(1);
+ }
+
+ button->widget.window = window;
+ button->widget.parent = NULL;
+ button->widget.active_widget = NULL;
+ button->widget.hover_widget = NULL;
+ button->widget.key_func = <k_button_key_event;
+ button->widget.mouse_func = <k_button_mouse_event;
+ button->widget.update_function = NULL;
+ button->widget.draw_function = <k_draw_button;
+ button->widget.destroy_function = <k_destroy_button;
+ button->widget.rect.x = 0;
+ button->widget.rect.y = 0;
+ /* For testing, will default to size of text once text is implemented */
+ button->widget.rect.w = 100;
+ button->widget.rect.h = 100;
+ button->widget.state = NORMAL;
+
+ button->callback = callback;
+ button->text = strdup(text);
+
+ return button;
+}
+
+void ltk_destroy_button(void *widget)
+{
+ LtkButton *button = (LtkButton *)widget;
+ if (!button)
+ {
+ printf("Tried to destroy NULL button.\n");
+ }
+ free(button->text);
+ free(button);
+}
+
+void ltk_button_key_event(void *widget, XEvent event)
+{
+}
+
+void ltk_button_mouse_event(void *widget, XEvent event)
+{
+ LtkButton *button = widget;
+ if (button->widget.state == DISABLED)
+ {
+ return;
+ }
+ if (event.type == ButtonPress && event.xbutton.button == 1)
+ {
+ LtkWidget *parent = button->widget.parent;
+ if (parent)
+ {
+ ltk_remove_active_widget(parent);
+ parent->active_widget = button;
+ }
+ button->widget.state = PRESSED;
+ ltk_draw_button(button);
+ }
+ else if (event.type == ButtonRelease)
+ {
+ if (button->widget.state == PRESSED)
+ {
+ button->widget.state = HOVERACTIVE;
+ ltk_draw_button(button);
+ }
+ }
+ else if (event.type == MotionNotify)
+ {
+ if (button->widget.state == NORMAL || button->widget.state == ACTIVE)
+ {
+ if (button->widget.state == ACTIVE)
+ {
+ button->widget.state = HOVERACTIVE;
+ }
+ else
+ {
+ button->widget.state = HOVER;
+ }
+ LtkWidget *parent = button->widget.parent;
+ LtkWidget *hover_widget;
+ if (parent)
+ {
+ ltk_remove_hover_widget(parent);
+ parent->hover_widget = button;
+ }
+ ltk_draw_button(button);
+ }
+ }
+}
diff --git a/button.h b/button.h
@@ -0,0 +1,78 @@
+/*
+ * This file is part of the Lumidify ToolKit (LTK)
+ * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _LTK_BUTTON_H_
+#define _LTK_BUTTON_H_
+
+#include "widget.h"
+
+typedef struct
+{
+ LtkWidget widget;
+ void (*callback)(void);
+ char *text;
+} LtkButton;
+
+typedef struct LtkButtonTheme
+{
+ int border_width;
+ int font_size;
+ XColor border_color;
+ XColor fill_color;
+ int padding_left;
+ int padding_right;
+ int padding_top;
+ int padding_bottom;
+
+ int border_width_hover;
+ int font_size_hover;
+ XColor border_color_hover;
+ XColor fill_color_hover;
+
+ int border_width_pressed;
+ int font_size_pressed;
+ XColor border_color_pressed;
+ XColor fill_color_pressed;
+
+ int border_width_active;
+ int font_size_active;
+ XColor border_color_active;
+ XColor fill_color_active;
+
+ int border_width_disabled;
+ int font_size_disabled;
+ XColor border_color_disabled;
+ XColor fill_color_disabled;
+
+} LtkButtonTheme;
+
+LtkButtonTheme *ltk_parse_button_theme(cJSON *button_json);
+void ltk_draw_button(void *widget);
+LtkButton *ltk_create_button(LtkWindow *window, const char *text, void (*callback)(void));
+void ltk_button_key_event(void *widget, XEvent event);
+void ltk_button_mouse_event(void *widget, XEvent event);
+void ltk_destroy_button(void *widget);
+void ltk_button_key_event(void *widget, XEvent event);
+void ltk_button_mouse_event(void *widget, XEvent event);
+
+#endif
diff --git a/cJSON.c b/cJSON.c
@@ -0,0 +1,1492 @@
+/*
+ Copyright (c) 2009 Dave Gamble
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+/* cJSON */
+/* JSON parser in C. */
+
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <float.h>
+#include <limits.h>
+#include <ctype.h>
+#include "cJSON.h"
+
+static const char *global_ep;
+
+const char *cJSON_GetErrorPtr(void)
+{
+ return global_ep;
+}
+
+static int cJSON_strcasecmp(const char *s1, const char *s2)
+{
+ if (!s1)
+ return (s1 == s2) ? 0 : 1;
+ if (!s2)
+ return 1;
+ for (; tolower(*s1) == tolower(*s2); ++s1, ++s2)
+ if (*s1 == 0)
+ return 0;
+ return tolower(*(const unsigned char *)s1) -
+ tolower(*(const unsigned char *)s2);
+}
+
+static void *(*cJSON_malloc) (size_t sz) = malloc;
+static void (*cJSON_free) (void *ptr) = free;
+
+static char *cJSON_strdup(const char *str)
+{
+ size_t len;
+ char *copy;
+
+ len = strlen(str) + 1;
+ if (!(copy = (char *)cJSON_malloc(len)))
+ return 0;
+ memcpy(copy, str, len);
+ return copy;
+}
+
+void cJSON_InitHooks(cJSON_Hooks * hooks)
+{
+ if (!hooks) { /* Reset hooks */
+ cJSON_malloc = malloc;
+ cJSON_free = free;
+ return;
+ }
+
+ cJSON_malloc = (hooks->malloc_fn) ? hooks->malloc_fn : malloc;
+ cJSON_free = (hooks->free_fn) ? hooks->free_fn : free;
+}
+
+/* Internal constructor. */
+static cJSON *cJSON_New_Item(void)
+{
+ cJSON *node = (cJSON *) cJSON_malloc(sizeof(cJSON));
+ if (node)
+ memset(node, 0, sizeof(cJSON));
+ return node;
+}
+
+/* Delete a cJSON structure. */
+void cJSON_Delete(cJSON * c)
+{
+ cJSON *next;
+ while (c) {
+ next = c->next;
+ if (!(c->type & cJSON_IsReference) && c->child)
+ cJSON_Delete(c->child);
+ if (!(c->type & cJSON_IsReference) && c->valuestring)
+ cJSON_free(c->valuestring);
+ if (!(c->type & cJSON_StringIsConst) && c->string)
+ cJSON_free(c->string);
+ cJSON_free(c);
+ c = next;
+ }
+}
+
+/* Parse the input text to generate a number, and populate the result into item. */
+static const char *parse_number(cJSON * item, const char *num)
+{
+ double n = 0, sign = 1, scale = 0;
+ int subscale = 0, signsubscale = 1;
+
+ if (*num == '-')
+ sign = -1, num++; /* Has sign? */
+ if (*num == '0')
+ num++; /* is zero */
+ if (*num >= '1' && *num <= '9')
+ do
+ n = (n * 10.0) + (*num++ - '0');
+ while (*num >= '0' && *num <= '9'); /* Number? */
+ if (*num == '.' && num[1] >= '0' && num[1] <= '9') {
+ num++;
+ do
+ n = (n * 10.0) + (*num++ - '0'), scale--;
+ while (*num >= '0' && *num <= '9');
+ } /* Fractional part? */
+ if (*num == 'e' || *num == 'E') { /* Exponent? */
+ num++;
+ if (*num == '+')
+ num++;
+ else if (*num == '-')
+ signsubscale = -1, num++; /* With sign? */
+ while (*num >= '0' && *num <= '9')
+ subscale = (subscale * 10) + (*num++ - '0'); /* Number? */
+ }
+
+ n = sign * n * pow(10.0, (scale + subscale * signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */
+
+ item->valuedouble = n;
+ item->valueint = (int)n;
+ item->type = cJSON_Number;
+ return num;
+}
+
+static int pow2gt(int x)
+{
+ --x;
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+ x |= x >> 16;
+ return x + 1;
+}
+
+typedef struct {
+ char *buffer;
+ int length;
+ int offset;
+} printbuffer;
+
+static char *ensure(printbuffer * p, int needed)
+{
+ char *newbuffer;
+ int newsize;
+ if (!p || !p->buffer)
+ return 0;
+ needed += p->offset;
+ if (needed <= p->length)
+ return p->buffer + p->offset;
+
+ newsize = pow2gt(needed);
+ newbuffer = (char *)cJSON_malloc(newsize);
+ if (!newbuffer) {
+ cJSON_free(p->buffer);
+ p->length = 0, p->buffer = 0;
+ return 0;
+ }
+ if (newbuffer)
+ memcpy(newbuffer, p->buffer, p->length);
+ cJSON_free(p->buffer);
+ p->length = newsize;
+ p->buffer = newbuffer;
+ return newbuffer + p->offset;
+}
+
+static int update(printbuffer * p)
+{
+ char *str;
+ if (!p || !p->buffer)
+ return 0;
+ str = p->buffer + p->offset;
+ return p->offset + strlen(str);
+}
+
+/* Render the number nicely from the given item into a string. */
+static char *print_number(cJSON * item, printbuffer * p)
+{
+ char *str = 0;
+ double d = item->valuedouble;
+ if (d == 0) {
+ if (p)
+ str = ensure(p, 2);
+ else
+ str = (char *)cJSON_malloc(2); /* special case for 0. */
+ if (str)
+ strcpy(str, "0");
+ } else if (fabs(((double)item->valueint) - d) <= DBL_EPSILON
+ && d <= INT_MAX && d >= INT_MIN) {
+ if (p)
+ str = ensure(p, 21);
+ else
+ str = (char *)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */
+ if (str)
+ sprintf(str, "%d", item->valueint);
+ } else {
+ if (p)
+ str = ensure(p, 64);
+ else
+ str = (char *)cJSON_malloc(64); /* This is a nice tradeoff. */
+ if (str) {
+ if (d * 0 != 0)
+ sprintf(str, "null"); /* This checks for NaN and Infinity */
+ else if (fabs(floor(d) - d) <= DBL_EPSILON
+ && fabs(d) < 1.0e60)
+ sprintf(str, "%.0f", d);
+ else if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9)
+ sprintf(str, "%e", d);
+ else
+ sprintf(str, "%f", d);
+ }
+ }
+ return str;
+}
+
+static unsigned parse_hex4(const char *str)
+{
+ unsigned h = 0;
+ if (*str >= '0' && *str <= '9')
+ h += (*str) - '0';
+ else if (*str >= 'A' && *str <= 'F')
+ h += 10 + (*str) - 'A';
+ else if (*str >= 'a' && *str <= 'f')
+ h += 10 + (*str) - 'a';
+ else
+ return 0;
+ h = h << 4;
+ str++;
+ if (*str >= '0' && *str <= '9')
+ h += (*str) - '0';
+ else if (*str >= 'A' && *str <= 'F')
+ h += 10 + (*str) - 'A';
+ else if (*str >= 'a' && *str <= 'f')
+ h += 10 + (*str) - 'a';
+ else
+ return 0;
+ h = h << 4;
+ str++;
+ if (*str >= '0' && *str <= '9')
+ h += (*str) - '0';
+ else if (*str >= 'A' && *str <= 'F')
+ h += 10 + (*str) - 'A';
+ else if (*str >= 'a' && *str <= 'f')
+ h += 10 + (*str) - 'a';
+ else
+ return 0;
+ h = h << 4;
+ str++;
+ if (*str >= '0' && *str <= '9')
+ h += (*str) - '0';
+ else if (*str >= 'A' && *str <= 'F')
+ h += 10 + (*str) - 'A';
+ else if (*str >= 'a' && *str <= 'f')
+ h += 10 + (*str) - 'a';
+ else
+ return 0;
+ return h;
+}
+
+/* Parse the input text into an unescaped cstring, and populate item. */
+static const unsigned char firstByteMark[7] =
+ { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+static const char *parse_string(cJSON * item, const char *str, const char **ep)
+{
+ const char *ptr = str + 1, *end_ptr = str + 1;
+ char *ptr2;
+ char *out;
+ int len = 0;
+ unsigned uc, uc2;
+ if (*str != '\"') {
+ *ep = str;
+ return 0;
+ }
+ /* not a string! */
+ while (*end_ptr != '\"' && *end_ptr && ++len)
+ if (*end_ptr++ == '\\')
+ end_ptr++; /* Skip escaped quotes. */
+
+ out = (char *)cJSON_malloc(len + 1); /* This is how long we need for the string, roughly. */
+ if (!out)
+ return 0;
+ item->valuestring = out; /* assign here so out will be deleted during cJSON_Delete() later */
+ item->type = cJSON_String;
+
+ ptr = str + 1;
+ ptr2 = out;
+ while (ptr < end_ptr) {
+ if (*ptr != '\\')
+ *ptr2++ = *ptr++;
+ else {
+ ptr++;
+ switch (*ptr) {
+ case 'b':
+ *ptr2++ = '\b';
+ break;
+ case 'f':
+ *ptr2++ = '\f';
+ break;
+ case 'n':
+ *ptr2++ = '\n';
+ break;
+ case 'r':
+ *ptr2++ = '\r';
+ break;
+ case 't':
+ *ptr2++ = '\t';
+ break;
+ case 'u': /* transcode utf16 to utf8. */
+ uc = parse_hex4(ptr + 1);
+ ptr += 4; /* get the unicode char. */
+ if (ptr >= end_ptr) {
+ *ep = str;
+ return 0;
+ }
+ /* invalid */
+ if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) {
+ *ep = str;
+ return 0;
+ }
+ /* check for invalid. */
+ if (uc >= 0xD800 && uc <= 0xDBFF) { /* UTF16 surrogate pairs. */
+ if (ptr + 6 > end_ptr) {
+ *ep = str;
+ return 0;
+ } /* invalid */
+ if (ptr[1] != '\\' || ptr[2] != 'u') {
+ *ep = str;
+ return 0;
+ } /* missing second-half of surrogate. */
+ uc2 = parse_hex4(ptr + 3);
+ ptr += 6;
+ if (uc2 < 0xDC00 || uc2 > 0xDFFF) {
+ *ep = str;
+ return 0;
+ } /* invalid second-half of surrogate. */
+ uc = 0x10000 +
+ (((uc & 0x3FF) << 10) |
+ (uc2 & 0x3FF));
+ }
+
+ len = 4;
+ if (uc < 0x80)
+ len = 1;
+ else if (uc < 0x800)
+ len = 2;
+ else if (uc < 0x10000)
+ len = 3;
+ ptr2 += len;
+
+ switch (len) {
+ case 4:
+ *--ptr2 = ((uc | 0x80) & 0xBF);
+ uc >>= 6;
+ case 3:
+ *--ptr2 = ((uc | 0x80) & 0xBF);
+ uc >>= 6;
+ case 2:
+ *--ptr2 = ((uc | 0x80) & 0xBF);
+ uc >>= 6;
+ case 1:
+ *--ptr2 = (uc | firstByteMark[len]);
+ }
+ ptr2 += len;
+ break;
+ default:
+ *ptr2++ = *ptr;
+ break;
+ }
+ ptr++;
+ }
+ }
+ *ptr2 = 0;
+ if (*ptr == '\"')
+ ptr++;
+ return ptr;
+}
+
+/* Render the cstring provided to an escaped version that can be printed. */
+static char *print_string_ptr(const char *str, printbuffer * p)
+{
+ const char *ptr;
+ char *ptr2, *out;
+ int len = 0, flag = 0;
+ unsigned char token;
+
+ if (!str) {
+ if (p)
+ out = ensure(p, 3);
+ else
+ out = (char *)cJSON_malloc(3);
+ if (!out)
+ return 0;
+ strcpy(out, "\"\"");
+ return out;
+ }
+
+ for (ptr = str; *ptr; ptr++)
+ flag |= ((*ptr > 0 && *ptr < 32) || (*ptr == '\"')
+ || (*ptr == '\\')) ? 1 : 0;
+ if (!flag) {
+ len = ptr - str;
+ if (p)
+ out = ensure(p, len + 3);
+ else
+ out = (char *)cJSON_malloc(len + 3);
+ if (!out)
+ return 0;
+ ptr2 = out;
+ *ptr2++ = '\"';
+ strcpy(ptr2, str);
+ ptr2[len] = '\"';
+ ptr2[len + 1] = 0;
+ return out;
+ }
+
+ ptr = str;
+ while ((token = *ptr) && ++len) {
+ if (strchr("\"\\\b\f\n\r\t", token))
+ len++;
+ else if (token < 32)
+ len += 5;
+ ptr++;
+ }
+
+ if (p)
+ out = ensure(p, len + 3);
+ else
+ out = (char *)cJSON_malloc(len + 3);
+ if (!out)
+ return 0;
+
+ ptr2 = out;
+ ptr = str;
+ *ptr2++ = '\"';
+ while (*ptr) {
+ if ((unsigned char)*ptr > 31 && *ptr != '\"' && *ptr != '\\')
+ *ptr2++ = *ptr++;
+ else {
+ *ptr2++ = '\\';
+ switch (token = *ptr++) {
+ case '\\':
+ *ptr2++ = '\\';
+ break;
+ case '\"':
+ *ptr2++ = '\"';
+ break;
+ case '\b':
+ *ptr2++ = 'b';
+ break;
+ case '\f':
+ *ptr2++ = 'f';
+ break;
+ case '\n':
+ *ptr2++ = 'n';
+ break;
+ case '\r':
+ *ptr2++ = 'r';
+ break;
+ case '\t':
+ *ptr2++ = 't';
+ break;
+ default:
+ sprintf(ptr2, "u%04x", token);
+ ptr2 += 5;
+ break; /* escape and print */
+ }
+ }
+ }
+ *ptr2++ = '\"';
+ *ptr2++ = 0;
+ return out;
+}
+
+/* Invote print_string_ptr (which is useful) on an item. */
+static char *print_string(cJSON * item, printbuffer * p)
+{
+ return print_string_ptr(item->valuestring, p);
+}
+
+/* Predeclare these prototypes. */
+static const char *parse_value(cJSON * item, const char *value,
+ const char **ep);
+static char *print_value(cJSON * item, int depth, int fmt, printbuffer * p);
+static const char *parse_array(cJSON * item, const char *value,
+ const char **ep);
+static char *print_array(cJSON * item, int depth, int fmt, printbuffer * p);
+static const char *parse_object(cJSON * item, const char *value,
+ const char **ep);
+static char *print_object(cJSON * item, int depth, int fmt, printbuffer * p);
+
+/* Utility to jump whitespace and cr/lf */
+static const char *skip(const char *in)
+{
+ while (in && *in && (unsigned char)*in <= 32)
+ in++;
+ return in;
+}
+
+/* Parse an object - create a new root, and populate. */
+cJSON *cJSON_ParseWithOpts(const char *value, const char **return_parse_end,
+ int require_null_terminated)
+{
+ const char *end = 0, **ep =
+ return_parse_end ? return_parse_end : &global_ep;
+ cJSON *c = cJSON_New_Item();
+ *ep = 0;
+ if (!c)
+ return 0; /* memory fail */
+
+ end = parse_value(c, skip(value), ep);
+ if (!end) {
+ cJSON_Delete(c);
+ return 0;
+ }
+
+ /* parse failure. ep is set. */
+ /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
+ if (require_null_terminated) {
+ end = skip(end);
+ if (*end) {
+ cJSON_Delete(c);
+ *ep = end;
+ return 0;
+ }
+ }
+ if (return_parse_end)
+ *return_parse_end = end;
+ return c;
+}
+
+/* Default options for cJSON_Parse */
+cJSON *cJSON_Parse(const char *value)
+{
+ return cJSON_ParseWithOpts(value, 0, 0);
+}
+
+/* Render a cJSON item/entity/structure to text. */
+char *cJSON_Print(cJSON * item)
+{
+ return print_value(item, 0, 1, 0);
+}
+
+char *cJSON_PrintUnformatted(cJSON * item)
+{
+ return print_value(item, 0, 0, 0);
+}
+
+char *cJSON_PrintBuffered(cJSON * item, int prebuffer, int fmt)
+{
+ printbuffer p;
+ p.buffer = (char *)cJSON_malloc(prebuffer);
+ p.length = prebuffer;
+ p.offset = 0;
+ return print_value(item, 0, fmt, &p);
+}
+
+/* Parser core - when encountering text, process appropriately. */
+static const char *parse_value(cJSON * item, const char *value, const char **ep)
+{
+ if (!value)
+ return 0; /* Fail on null. */
+ if (!strncmp(value, "null", 4)) {
+ item->type = cJSON_NULL;
+ return value + 4;
+ }
+ if (!strncmp(value, "false", 5)) {
+ item->type = cJSON_False;
+ return value + 5;
+ }
+ if (!strncmp(value, "true", 4)) {
+ item->type = cJSON_True;
+ item->valueint = 1;
+ return value + 4;
+ }
+ if (*value == '\"') {
+ return parse_string(item, value, ep);
+ }
+ if (*value == '-' || (*value >= '0' && *value <= '9')) {
+ return parse_number(item, value);
+ }
+ if (*value == '[') {
+ return parse_array(item, value, ep);
+ }
+ if (*value == '{') {
+ return parse_object(item, value, ep);
+ }
+
+ *ep = value;
+ return 0; /* failure. */
+}
+
+/* Render a value to text. */
+static char *print_value(cJSON * item, int depth, int fmt, printbuffer * p)
+{
+ char *out = 0;
+ if (!item)
+ return 0;
+ if (p) {
+ switch ((item->type) & 255) {
+ case cJSON_NULL:
+ {
+ out = ensure(p, 5);
+ if (out)
+ strcpy(out, "null");
+ break;
+ }
+ case cJSON_False:
+ {
+ out = ensure(p, 6);
+ if (out)
+ strcpy(out, "false");
+ break;
+ }
+ case cJSON_True:
+ {
+ out = ensure(p, 5);
+ if (out)
+ strcpy(out, "true");
+ break;
+ }
+ case cJSON_Number:
+ out = print_number(item, p);
+ break;
+ case cJSON_String:
+ out = print_string(item, p);
+ break;
+ case cJSON_Array:
+ out = print_array(item, depth, fmt, p);
+ break;
+ case cJSON_Object:
+ out = print_object(item, depth, fmt, p);
+ break;
+ }
+ } else {
+ switch ((item->type) & 255) {
+ case cJSON_NULL:
+ out = cJSON_strdup("null");
+ break;
+ case cJSON_False:
+ out = cJSON_strdup("false");
+ break;
+ case cJSON_True:
+ out = cJSON_strdup("true");
+ break;
+ case cJSON_Number:
+ out = print_number(item, 0);
+ break;
+ case cJSON_String:
+ out = print_string(item, 0);
+ break;
+ case cJSON_Array:
+ out = print_array(item, depth, fmt, 0);
+ break;
+ case cJSON_Object:
+ out = print_object(item, depth, fmt, 0);
+ break;
+ }
+ }
+ return out;
+}
+
+/* Build an array from input text. */
+static const char *parse_array(cJSON * item, const char *value, const char **ep)
+{
+ cJSON *child;
+ if (*value != '[') {
+ *ep = value;
+ return 0;
+ }
+ /* not an array! */
+ item->type = cJSON_Array;
+ value = skip(value + 1);
+ if (*value == ']')
+ return value + 1; /* empty array. */
+
+ item->child = child = cJSON_New_Item();
+ if (!item->child)
+ return 0; /* memory fail */
+ value = skip(parse_value(child, skip(value), ep)); /* skip any spacing, get the value. */
+ if (!value)
+ return 0;
+
+ while (*value == ',') {
+ cJSON *new_item;
+ if (!(new_item = cJSON_New_Item()))
+ return 0; /* memory fail */
+ child->next = new_item;
+ new_item->prev = child;
+ child = new_item;
+ value = skip(parse_value(child, skip(value + 1), ep));
+ if (!value)
+ return 0; /* memory fail */
+ }
+
+ if (*value == ']')
+ return value + 1; /* end of array */
+ *ep = value;
+ return 0; /* malformed. */
+}
+
+/* Render an array to text */
+static char *print_array(cJSON * item, int depth, int fmt, printbuffer * p)
+{
+ char **entries;
+ char *out = 0, *ptr, *ret;
+ int len = 5;
+ cJSON *child = item->child;
+ int numentries = 0, i = 0, fail = 0;
+ size_t tmplen = 0;
+
+ /* How many entries in the array? */
+ while (child)
+ numentries++, child = child->next;
+ /* Explicitly handle numentries==0 */
+ if (!numentries) {
+ if (p)
+ out = ensure(p, 3);
+ else
+ out = (char *)cJSON_malloc(3);
+ if (out)
+ strcpy(out, "[]");
+ return out;
+ }
+
+ if (p) {
+ /* Compose the output array. */
+ i = p->offset;
+ ptr = ensure(p, 1);
+ if (!ptr)
+ return 0;
+ *ptr = '[';
+ p->offset++;
+ child = item->child;
+ while (child && !fail) {
+ print_value(child, depth + 1, fmt, p);
+ p->offset = update(p);
+ if (child->next) {
+ len = fmt ? 2 : 1;
+ ptr = ensure(p, len + 1);
+ if (!ptr)
+ return 0;
+ *ptr++ = ',';
+ if (fmt)
+ *ptr++ = ' ';
+ *ptr = 0;
+ p->offset += len;
+ }
+ child = child->next;
+ }
+ ptr = ensure(p, 2);
+ if (!ptr)
+ return 0;
+ *ptr++ = ']';
+ *ptr = 0;
+ out = (p->buffer) + i;
+ } else {
+ /* Allocate an array to hold the values for each */
+ entries = (char **)cJSON_malloc(numentries * sizeof(char *));
+ if (!entries)
+ return 0;
+ memset(entries, 0, numentries * sizeof(char *));
+ /* Retrieve all the results: */
+ child = item->child;
+ while (child && !fail) {
+ ret = print_value(child, depth + 1, fmt, 0);
+ entries[i++] = ret;
+ if (ret)
+ len += strlen(ret) + 2 + (fmt ? 1 : 0);
+ else
+ fail = 1;
+ child = child->next;
+ }
+
+ /* If we didn't fail, try to malloc the output string */
+ if (!fail)
+ out = (char *)cJSON_malloc(len);
+ /* If that fails, we fail. */
+ if (!out)
+ fail = 1;
+
+ /* Handle failure. */
+ if (fail) {
+ for (i = 0; i < numentries; i++)
+ if (entries[i])
+ cJSON_free(entries[i]);
+ cJSON_free(entries);
+ return 0;
+ }
+
+ /* Compose the output array. */
+ *out = '[';
+ ptr = out + 1;
+ *ptr = 0;
+ for (i = 0; i < numentries; i++) {
+ tmplen = strlen(entries[i]);
+ memcpy(ptr, entries[i], tmplen);
+ ptr += tmplen;
+ if (i != numentries - 1) {
+ *ptr++ = ',';
+ if (fmt)
+ *ptr++ = ' ';
+ *ptr = 0;
+ }
+ cJSON_free(entries[i]);
+ }
+ cJSON_free(entries);
+ *ptr++ = ']';
+ *ptr++ = 0;
+ }
+ return out;
+}
+
+/* Build an object from the text. */
+static const char *parse_object(cJSON * item, const char *value,
+ const char **ep)
+{
+ cJSON *child;
+ if (*value != '{') {
+ *ep = value;
+ return 0;
+ }
+ /* not an object! */
+ item->type = cJSON_Object;
+ value = skip(value + 1);
+ if (*value == '}')
+ return value + 1; /* empty array. */
+
+ item->child = child = cJSON_New_Item();
+ if (!item->child)
+ return 0;
+ value = skip(parse_string(child, skip(value), ep));
+ if (!value)
+ return 0;
+ child->string = child->valuestring;
+ child->valuestring = 0;
+ if (*value != ':') {
+ *ep = value;
+ return 0;
+ } /* fail! */
+ value = skip(parse_value(child, skip(value + 1), ep)); /* skip any spacing, get the value. */
+ if (!value)
+ return 0;
+
+ while (*value == ',') {
+ cJSON *new_item;
+ if (!(new_item = cJSON_New_Item()))
+ return 0; /* memory fail */
+ child->next = new_item;
+ new_item->prev = child;
+ child = new_item;
+ value = skip(parse_string(child, skip(value + 1), ep));
+ if (!value)
+ return 0;
+ child->string = child->valuestring;
+ child->valuestring = 0;
+ if (*value != ':') {
+ *ep = value;
+ return 0;
+ } /* fail! */
+ value = skip(parse_value(child, skip(value + 1), ep)); /* skip any spacing, get the value. */
+ if (!value)
+ return 0;
+ }
+
+ if (*value == '}')
+ return value + 1; /* end of array */
+ *ep = value;
+ return 0; /* malformed. */
+}
+
+/* Render an object to text. */
+static char *print_object(cJSON * item, int depth, int fmt, printbuffer * p)
+{
+ char **entries = 0, **names = 0;
+ char *out = 0, *ptr, *ret, *str;
+ int len = 7, i = 0, j;
+ cJSON *child = item->child;
+ int numentries = 0, fail = 0;
+ size_t tmplen = 0;
+ /* Count the number of entries. */
+ while (child)
+ numentries++, child = child->next;
+ /* Explicitly handle empty object case */
+ if (!numentries) {
+ if (p)
+ out = ensure(p, fmt ? depth + 4 : 3);
+ else
+ out = (char *)cJSON_malloc(fmt ? depth + 4 : 3);
+ if (!out)
+ return 0;
+ ptr = out;
+ *ptr++ = '{';
+ if (fmt) {
+ *ptr++ = '\n';
+ for (i = 0; i < depth; i++)
+ *ptr++ = '\t';
+ }
+ *ptr++ = '}';
+ *ptr++ = 0;
+ return out;
+ }
+ if (p) {
+ /* Compose the output: */
+ i = p->offset;
+ len = fmt ? 2 : 1;
+ ptr = ensure(p, len + 1);
+ if (!ptr)
+ return 0;
+ *ptr++ = '{';
+ if (fmt)
+ *ptr++ = '\n';
+ *ptr = 0;
+ p->offset += len;
+ child = item->child;
+ depth++;
+ while (child) {
+ if (fmt) {
+ ptr = ensure(p, depth);
+ if (!ptr)
+ return 0;
+ for (j = 0; j < depth; j++)
+ *ptr++ = '\t';
+ p->offset += depth;
+ }
+ print_string_ptr(child->string, p);
+ p->offset = update(p);
+
+ len = fmt ? 2 : 1;
+ ptr = ensure(p, len);
+ if (!ptr)
+ return 0;
+ *ptr++ = ':';
+ if (fmt)
+ *ptr++ = '\t';
+ p->offset += len;
+
+ print_value(child, depth, fmt, p);
+ p->offset = update(p);
+
+ len = (fmt ? 1 : 0) + (child->next ? 1 : 0);
+ ptr = ensure(p, len + 1);
+ if (!ptr)
+ return 0;
+ if (child->next)
+ *ptr++ = ',';
+ if (fmt)
+ *ptr++ = '\n';
+ *ptr = 0;
+ p->offset += len;
+ child = child->next;
+ }
+ ptr = ensure(p, fmt ? (depth + 1) : 2);
+ if (!ptr)
+ return 0;
+ if (fmt)
+ for (i = 0; i < depth - 1; i++)
+ *ptr++ = '\t';
+ *ptr++ = '}';
+ *ptr = 0;
+ out = (p->buffer) + i;
+ } else {
+ /* Allocate space for the names and the objects */
+ entries = (char **)cJSON_malloc(numentries * sizeof(char *));
+ if (!entries)
+ return 0;
+ names = (char **)cJSON_malloc(numentries * sizeof(char *));
+ if (!names) {
+ cJSON_free(entries);
+ return 0;
+ }
+ memset(entries, 0, sizeof(char *) * numentries);
+ memset(names, 0, sizeof(char *) * numentries);
+
+ /* Collect all the results into our arrays: */
+ child = item->child;
+ depth++;
+ if (fmt)
+ len += depth;
+ while (child && !fail) {
+ names[i] = str = print_string_ptr(child->string, 0);
+ entries[i++] = ret = print_value(child, depth, fmt, 0);
+ if (str && ret)
+ len +=
+ strlen(ret) + strlen(str) + 2 + (fmt ? 2 +
+ depth : 0);
+ else
+ fail = 1;
+ child = child->next;
+ }
+
+ /* Try to allocate the output string */
+ if (!fail)
+ out = (char *)cJSON_malloc(len);
+ if (!out)
+ fail = 1;
+
+ /* Handle failure */
+ if (fail) {
+ for (i = 0; i < numentries; i++) {
+ if (names[i])
+ cJSON_free(names[i]);
+ if (entries[i])
+ cJSON_free(entries[i]);
+ }
+ cJSON_free(names);
+ cJSON_free(entries);
+ return 0;
+ }
+
+ /* Compose the output: */
+ *out = '{';
+ ptr = out + 1;
+ if (fmt)
+ *ptr++ = '\n';
+ *ptr = 0;
+ for (i = 0; i < numentries; i++) {
+ if (fmt)
+ for (j = 0; j < depth; j++)
+ *ptr++ = '\t';
+ tmplen = strlen(names[i]);
+ memcpy(ptr, names[i], tmplen);
+ ptr += tmplen;
+ *ptr++ = ':';
+ if (fmt)
+ *ptr++ = '\t';
+ strcpy(ptr, entries[i]);
+ ptr += strlen(entries[i]);
+ if (i != numentries - 1)
+ *ptr++ = ',';
+ if (fmt)
+ *ptr++ = '\n';
+ *ptr = 0;
+ cJSON_free(names[i]);
+ cJSON_free(entries[i]);
+ }
+
+ cJSON_free(names);
+ cJSON_free(entries);
+ if (fmt)
+ for (i = 0; i < depth - 1; i++)
+ *ptr++ = '\t';
+ *ptr++ = '}';
+ *ptr++ = 0;
+ }
+ return out;
+}
+
+/* Get Array size/item / object item. */
+int cJSON_GetArraySize(cJSON * array)
+{
+ cJSON *c = array->child;
+ int i = 0;
+ while (c)
+ i++, c = c->next;
+ return i;
+}
+
+cJSON *cJSON_GetArrayItem(cJSON * array, int item)
+{
+ cJSON *c = array ? array->child : 0;
+ while (c && item > 0)
+ item--, c = c->next;
+ return c;
+}
+
+cJSON *cJSON_GetObjectItem(cJSON * object, const char *string)
+{
+ cJSON *c = object ? object->child : 0;
+ while (c && cJSON_strcasecmp(c->string, string))
+ c = c->next;
+ return c;
+}
+
+int cJSON_HasObjectItem(cJSON * object, const char *string)
+{
+ return cJSON_GetObjectItem(object, string) ? 1 : 0;
+}
+
+/* Utility for array list handling. */
+static void suffix_object(cJSON * prev, cJSON * item)
+{
+ prev->next = item;
+ item->prev = prev;
+}
+
+/* Utility for handling references. */
+static cJSON *create_reference(cJSON * item)
+{
+ cJSON *ref = cJSON_New_Item();
+ if (!ref)
+ return 0;
+ memcpy(ref, item, sizeof(cJSON));
+ ref->string = 0;
+ ref->type |= cJSON_IsReference;
+ ref->next = ref->prev = 0;
+ return ref;
+}
+
+/* Add item to array/object. */
+void cJSON_AddItemToArray(cJSON * array, cJSON * item)
+{
+ cJSON *c = array->child;
+ if (!item)
+ return;
+ if (!c) {
+ array->child = item;
+ } else {
+ while (c && c->next)
+ c = c->next;
+ suffix_object(c, item);
+ }
+}
+
+void cJSON_AddItemToObject(cJSON * object, const char *string, cJSON * item)
+{
+ if (!item)
+ return;
+ if (item->string)
+ cJSON_free(item->string);
+ item->string = cJSON_strdup(string);
+ cJSON_AddItemToArray(object, item);
+}
+
+void cJSON_AddItemToObjectCS(cJSON * object, const char *string, cJSON * item)
+{
+ if (!item)
+ return;
+ if (!(item->type & cJSON_StringIsConst) && item->string)
+ cJSON_free(item->string);
+ item->string = (char *)string;
+ item->type |= cJSON_StringIsConst;
+ cJSON_AddItemToArray(object, item);
+}
+
+void cJSON_AddItemReferenceToArray(cJSON * array, cJSON * item)
+{
+ cJSON_AddItemToArray(array, create_reference(item));
+}
+
+void
+cJSON_AddItemReferenceToObject(cJSON * object, const char *string, cJSON * item)
+{
+ cJSON_AddItemToObject(object, string, create_reference(item));
+}
+
+cJSON *cJSON_DetachItemFromArray(cJSON * array, int which)
+{
+ cJSON *c = array->child;
+ while (c && which > 0)
+ c = c->next, which--;
+ if (!c)
+ return 0;
+ if (c->prev)
+ c->prev->next = c->next;
+ if (c->next)
+ c->next->prev = c->prev;
+ if (c == array->child)
+ array->child = c->next;
+ c->prev = c->next = 0;
+ return c;
+}
+
+void cJSON_DeleteItemFromArray(cJSON * array, int which)
+{
+ cJSON_Delete(cJSON_DetachItemFromArray(array, which));
+}
+
+cJSON *cJSON_DetachItemFromObject(cJSON * object, const char *string)
+{
+ int i = 0;
+ cJSON *c = object->child;
+ while (c && cJSON_strcasecmp(c->string, string))
+ i++, c = c->next;
+ if (c)
+ return cJSON_DetachItemFromArray(object, i);
+ return 0;
+}
+
+void cJSON_DeleteItemFromObject(cJSON * object, const char *string)
+{
+ cJSON_Delete(cJSON_DetachItemFromObject(object, string));
+}
+
+/* Replace array/object items with new ones. */
+void cJSON_InsertItemInArray(cJSON * array, int which, cJSON * newitem)
+{
+ cJSON *c = array->child;
+ while (c && which > 0)
+ c = c->next, which--;
+ if (!c) {
+ cJSON_AddItemToArray(array, newitem);
+ return;
+ }
+ newitem->next = c;
+ newitem->prev = c->prev;
+ c->prev = newitem;
+ if (c == array->child)
+ array->child = newitem;
+ else
+ newitem->prev->next = newitem;
+}
+
+void cJSON_ReplaceItemInArray(cJSON * array, int which, cJSON * newitem)
+{
+ cJSON *c = array->child;
+ while (c && which > 0)
+ c = c->next, which--;
+ if (!c)
+ return;
+ newitem->next = c->next;
+ newitem->prev = c->prev;
+ if (newitem->next)
+ newitem->next->prev = newitem;
+ if (c == array->child)
+ array->child = newitem;
+ else
+ newitem->prev->next = newitem;
+ c->next = c->prev = 0;
+ cJSON_Delete(c);
+}
+
+void
+cJSON_ReplaceItemInObject(cJSON * object, const char *string, cJSON * newitem)
+{
+ int i = 0;
+ cJSON *c = object->child;
+ while (c && cJSON_strcasecmp(c->string, string))
+ i++, c = c->next;
+ if (c) {
+ newitem->string = cJSON_strdup(string);
+ cJSON_ReplaceItemInArray(object, i, newitem);
+ }
+}
+
+/* Create basic types: */
+cJSON *cJSON_CreateNull(void)
+{
+ cJSON *item = cJSON_New_Item();
+ if (item)
+ item->type = cJSON_NULL;
+ return item;
+}
+
+cJSON *cJSON_CreateTrue(void)
+{
+ cJSON *item = cJSON_New_Item();
+ if (item)
+ item->type = cJSON_True;
+ return item;
+}
+
+cJSON *cJSON_CreateFalse(void)
+{
+ cJSON *item = cJSON_New_Item();
+ if (item)
+ item->type = cJSON_False;
+ return item;
+}
+
+cJSON *cJSON_CreateBool(int b)
+{
+ cJSON *item = cJSON_New_Item();
+ if (item)
+ item->type = b ? cJSON_True : cJSON_False;
+ return item;
+}
+
+cJSON *cJSON_CreateNumber(double num)
+{
+ cJSON *item = cJSON_New_Item();
+ if (item) {
+ item->type = cJSON_Number;
+ item->valuedouble = num;
+ item->valueint = (int)num;
+ }
+ return item;
+}
+
+cJSON *cJSON_CreateString(const char *string)
+{
+ cJSON *item = cJSON_New_Item();
+ if (item) {
+ item->type = cJSON_String;
+ item->valuestring = cJSON_strdup(string);
+ if (!item->valuestring) {
+ cJSON_Delete(item);
+ return 0;
+ }
+ }
+ return item;
+}
+
+cJSON *cJSON_CreateArray(void)
+{
+ cJSON *item = cJSON_New_Item();
+ if (item)
+ item->type = cJSON_Array;
+ return item;
+}
+
+cJSON *cJSON_CreateObject(void)
+{
+ cJSON *item = cJSON_New_Item();
+ if (item)
+ item->type = cJSON_Object;
+ return item;
+}
+
+/* Create Arrays: */
+cJSON *cJSON_CreateIntArray(const int *numbers, int count)
+{
+ int i;
+ cJSON *n = 0, *p = 0, *a = cJSON_CreateArray();
+ for (i = 0; a && i < count; i++) {
+ n = cJSON_CreateNumber(numbers[i]);
+ if (!n) {
+ cJSON_Delete(a);
+ return 0;
+ }
+ if (!i)
+ a->child = n;
+ else
+ suffix_object(p, n);
+ p = n;
+ }
+ return a;
+}
+
+cJSON *cJSON_CreateFloatArray(const float *numbers, int count)
+{
+ int i;
+ cJSON *n = 0, *p = 0, *a = cJSON_CreateArray();
+ for (i = 0; a && i < count; i++) {
+ n = cJSON_CreateNumber(numbers[i]);
+ if (!n) {
+ cJSON_Delete(a);
+ return 0;
+ }
+ if (!i)
+ a->child = n;
+ else
+ suffix_object(p, n);
+ p = n;
+ }
+ return a;
+}
+
+cJSON *cJSON_CreateDoubleArray(const double *numbers, int count)
+{
+ int i;
+ cJSON *n = 0, *p = 0, *a = cJSON_CreateArray();
+ for (i = 0; a && i < count; i++) {
+ n = cJSON_CreateNumber(numbers[i]);
+ if (!n) {
+ cJSON_Delete(a);
+ return 0;
+ }
+ if (!i)
+ a->child = n;
+ else
+ suffix_object(p, n);
+ p = n;
+ }
+ return a;
+}
+
+cJSON *cJSON_CreateStringArray(const char **strings, int count)
+{
+ int i;
+ cJSON *n = 0, *p = 0, *a = cJSON_CreateArray();
+ for (i = 0; a && i < count; i++) {
+ n = cJSON_CreateString(strings[i]);
+ if (!n) {
+ cJSON_Delete(a);
+ return 0;
+ }
+ if (!i)
+ a->child = n;
+ else
+ suffix_object(p, n);
+ p = n;
+ }
+ return a;
+}
+
+/* Duplication */
+cJSON *cJSON_Duplicate(cJSON * item, int recurse)
+{
+ cJSON *newitem, *cptr, *nptr = 0, *newchild;
+ /* Bail on bad ptr */
+ if (!item)
+ return 0;
+ /* Create new item */
+ newitem = cJSON_New_Item();
+ if (!newitem)
+ return 0;
+ /* Copy over all vars */
+ newitem->type = item->type & (~cJSON_IsReference), newitem->valueint =
+ item->valueint, newitem->valuedouble = item->valuedouble;
+ if (item->valuestring) {
+ newitem->valuestring = cJSON_strdup(item->valuestring);
+ if (!newitem->valuestring) {
+ cJSON_Delete(newitem);
+ return 0;
+ }
+ }
+ if (item->string) {
+ newitem->string = cJSON_strdup(item->string);
+ if (!newitem->string) {
+ cJSON_Delete(newitem);
+ return 0;
+ }
+ }
+ /* If non-recursive, then we're done! */
+ if (!recurse)
+ return newitem;
+ /* Walk the ->next chain for the child. */
+ cptr = item->child;
+ while (cptr) {
+ newchild = cJSON_Duplicate(cptr, 1); /* Duplicate (with recurse) each item in the ->next chain */
+ if (!newchild) {
+ cJSON_Delete(newitem);
+ return 0;
+ }
+ if (nptr) {
+ nptr->next = newchild, newchild->prev = nptr;
+ nptr = newchild;
+ } /* If newitem->child already set, then crosswire ->prev and ->next and move on */
+ else {
+ newitem->child = newchild;
+ nptr = newchild;
+ } /* Set newitem->child and move to it */
+ cptr = cptr->next;
+ }
+ return newitem;
+}
+
+void cJSON_Minify(char *json)
+{
+ char *into = json;
+ while (*json) {
+ if (*json == ' ')
+ json++;
+ else if (*json == '\t')
+ json++; /* Whitespace characters. */
+ else if (*json == '\r')
+ json++;
+ else if (*json == '\n')
+ json++;
+ else if (*json == '/' && json[1] == '/')
+ while (*json && *json != '\n')
+ json++; /* double-slash comments, to end of line. */
+ else if (*json == '/' && json[1] == '*') {
+ while (*json && !(*json == '*' && json[1] == '/'))
+ json++;
+ json += 2;
+ } /* multiline comments. */
+ else if (*json == '\"') {
+ *into++ = *json++;
+ while (*json && *json != '\"') {
+ if (*json == '\\')
+ *into++ = *json++;
+ *into++ = *json++;
+ }
+ *into++ = *json++;
+ } /* string literals, which are \" sensitive. */
+ else
+ *into++ = *json++; /* All other characters. */
+ }
+ *into = 0; /* and null-terminate. */
+}
diff --git a/cJSON.h b/cJSON.h
@@ -0,0 +1,160 @@
+/*
+ Copyright (c) 2009 Dave Gamble
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#ifndef cJSON__h
+#define cJSON__h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* cJSON Types: */
+#define cJSON_False (1 << 0)
+#define cJSON_True (1 << 1)
+#define cJSON_NULL (1 << 2)
+#define cJSON_Number (1 << 3)
+#define cJSON_String (1 << 4)
+#define cJSON_Array (1 << 5)
+#define cJSON_Object (1 << 6)
+
+#define cJSON_IsReference 256
+#define cJSON_StringIsConst 512
+
+/* The cJSON structure: */
+ typedef struct cJSON {
+ struct cJSON *next, *prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
+ struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
+
+ int type; /* The type of the item, as above. */
+
+ char *valuestring; /* The item's string, if type==cJSON_String */
+ int valueint; /* The item's number, if type==cJSON_Number */
+ double valuedouble; /* The item's number, if type==cJSON_Number */
+
+ char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
+ } cJSON;
+
+ typedef struct cJSON_Hooks {
+ void *(*malloc_fn) (size_t sz);
+ void (*free_fn) (void *ptr);
+ } cJSON_Hooks;
+
+/* Supply malloc, realloc and free functions to cJSON */
+ extern void cJSON_InitHooks(cJSON_Hooks * hooks);
+
+/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
+ extern cJSON *cJSON_Parse(const char *value);
+/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
+ extern char *cJSON_Print(cJSON * item);
+/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
+ extern char *cJSON_PrintUnformatted(cJSON * item);
+/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
+ extern char *cJSON_PrintBuffered(cJSON * item, int prebuffer, int fmt);
+/* Delete a cJSON entity and all subentities. */
+ extern void cJSON_Delete(cJSON * c);
+
+/* Returns the number of items in an array (or object). */
+ extern int cJSON_GetArraySize(cJSON * array);
+/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
+ extern cJSON *cJSON_GetArrayItem(cJSON * array, int item);
+/* Get item "string" from object. Case insensitive. */
+ extern cJSON *cJSON_GetObjectItem(cJSON * object, const char *string);
+ extern int cJSON_HasObjectItem(cJSON * object, const char *string);
+/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
+ extern const char *cJSON_GetErrorPtr(void);
+
+/* These calls create a cJSON item of the appropriate type. */
+ extern cJSON *cJSON_CreateNull(void);
+ extern cJSON *cJSON_CreateTrue(void);
+ extern cJSON *cJSON_CreateFalse(void);
+ extern cJSON *cJSON_CreateBool(int b);
+ extern cJSON *cJSON_CreateNumber(double num);
+ extern cJSON *cJSON_CreateString(const char *string);
+ extern cJSON *cJSON_CreateArray(void);
+ extern cJSON *cJSON_CreateObject(void);
+
+/* These utilities create an Array of count items. */
+ extern cJSON *cJSON_CreateIntArray(const int *numbers, int count);
+ extern cJSON *cJSON_CreateFloatArray(const float *numbers, int count);
+ extern cJSON *cJSON_CreateDoubleArray(const double *numbers, int count);
+ extern cJSON *cJSON_CreateStringArray(const char **strings, int count);
+
+/* Append item to the specified array/object. */
+ extern void cJSON_AddItemToArray(cJSON * array, cJSON * item);
+ extern void cJSON_AddItemToObject(cJSON * object, const char *string,
+ cJSON * item);
+ extern void cJSON_AddItemToObjectCS(cJSON * object, const char *string, cJSON * item); /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */
+/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
+ extern void cJSON_AddItemReferenceToArray(cJSON * array, cJSON * item);
+ extern void cJSON_AddItemReferenceToObject(cJSON * object,
+ const char *string,
+ cJSON * item);
+
+/* Remove/Detatch items from Arrays/Objects. */
+ extern cJSON *cJSON_DetachItemFromArray(cJSON * array, int which);
+ extern void cJSON_DeleteItemFromArray(cJSON * array, int which);
+ extern cJSON *cJSON_DetachItemFromObject(cJSON * object,
+ const char *string);
+ extern void cJSON_DeleteItemFromObject(cJSON * object,
+ const char *string);
+
+/* Update array items. */
+ extern void cJSON_InsertItemInArray(cJSON * array, int which, cJSON * newitem); /* Shifts pre-existing items to the right. */
+ extern void cJSON_ReplaceItemInArray(cJSON * array, int which,
+ cJSON * newitem);
+ extern void cJSON_ReplaceItemInObject(cJSON * object,
+ const char *string,
+ cJSON * newitem);
+
+/* Duplicate a cJSON item */
+ extern cJSON *cJSON_Duplicate(cJSON * item, int recurse);
+/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
+need to be released. With recurse!=0, it will duplicate any children connected to the item.
+The item->next and ->prev pointers are always zero on return from Duplicate. */
+
+/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
+/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error. If not, then cJSON_GetErrorPtr() does the job. */
+ extern cJSON *cJSON_ParseWithOpts(const char *value,
+ const char **return_parse_end,
+ int require_null_terminated);
+
+ extern void cJSON_Minify(char *json);
+
+/* Macros for creating things quickly. */
+#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
+#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
+#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
+#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
+#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
+#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
+
+/* When assigning an integer value, it needs to be propagated to valuedouble too. */
+#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
+#define cJSON_SetNumberValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
+
+/* Macro for iterating over an array */
+#define cJSON_ArrayForEach(pos, head) for(pos = (head)->child; pos != NULL; pos = pos->next)
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/common.c b/common.c
@@ -0,0 +1,80 @@
+/*
+ * This file is part of the Lumidify ToolKit (LTK)
+ * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "ltk.h"
+
+char *ltk_read_file(const char *path)
+{
+ FILE *f;
+ long len;
+ char *file_contents;
+ f = fopen(path, "rb");
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ file_contents = malloc(len + 1);
+ fread(file_contents, 1, len, f);
+ file_contents[len] = '\0';
+ fclose(f);
+
+ return file_contents;
+}
+
+int ltk_collide_rect(LtkRect rect, int x, int y)
+{
+ return (rect.x <= x && (rect.x + rect.w) >= x && rect.y <= y && (rect.y + rect.h) >= y);
+}
+
+/* Recursively set all active_widget states to NORMAL, redraw them,
+ * and remove the references to them in their parent functions */
+void ltk_remove_active_widget(void *widget)
+{
+ if (!widget) return;
+ LtkWidget *parent = widget;
+ LtkWidget *child;
+ while (parent->active_widget)
+ {
+ child = parent->active_widget;
+ child->state = NORMAL;
+ child->draw_function(child);
+ parent->active_widget = NULL;
+ parent = child;
+ }
+}
+
+/* Recursively set all hover_widget states to NORMAL, redraw them,
+ * and remove the references to them in their parent functions */
+void ltk_remove_hover_widget(void *widget)
+{
+ if (!widget) return;
+ LtkWidget *parent = widget;
+ LtkWidget *child;
+ while (parent->hover_widget)
+ {
+ child = parent->hover_widget;
+ child->state = child->state == HOVERACTIVE ? ACTIVE : NORMAL;
+ child->draw_function(child);
+ parent->hover_widget = NULL;
+ parent = child;
+ }
+}
diff --git a/common.h b/common.h
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the Lumidify ToolKit (LTK)
+ * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _LTK_COMMON_H_
+#define _LTK_COMMON_H_
+
+typedef struct LtkWidget LtkWidget;
+
+typedef enum
+{
+ NORMAL,
+ HOVER,
+ PRESSED,
+ ACTIVE,
+ HOVERACTIVE,
+ DISABLED
+} LtkWidgetState;
+
+typedef struct
+{
+ int x;
+ int y;
+ int w;
+ int h;
+} LtkRect;
+
+int ltk_collide_rect(LtkRect rect, int x, int y);
+char *ltk_read_file(const char *path);
+void ltk_remove_active_widget(void *widget);
+void ltk_remove_hover_widget(void *widget);
+
+#endif
diff --git a/event.c b/event.c
@@ -0,0 +1,42 @@
+/*
+ * This file is part of the Lumidify ToolKit (LTK)
+ * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "ltk.h"
+
+void ltk_handle_event(XEvent event)
+{
+ LtkWindow *window;
+ HASH_FIND_INT(ltk_global->window_hash, &event.xany.window, window);
+ if ((event.type == KeyPress || event.type == KeyRelease) && window->key_func)
+ {
+ window->key_func(window, event);
+ }
+ else if ((event.type == ButtonPress || event.type == ButtonRelease || event.type == MotionNotify) && window->mouse_func)
+ {
+ window->mouse_func(window, event);
+ }
+ else if (window->other_func)
+ {
+ window->other_func(window, event);
+ }
+}
diff --git a/event.h b/event.h
@@ -0,0 +1,31 @@
+/*
+ * This file is part of the Lumidify ToolKit (LTK)
+ * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "ltk.h"
+
+#ifndef _LTK_EVENT_H_
+#define _LTK_EVENT_H_
+
+typedef void (*LTK_EVENT_FUNC)(void *widget, XEvent event);
+
+#endif
diff --git a/grid.c b/grid.c
@@ -0,0 +1,313 @@
+/*
+ * This file is part of the Lumidify ToolKit (LTK)
+ * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/* TODO: remove_widget function that also adjusts static width */
+
+#include "ltk.h"
+
+void ltk_set_row_weight(LtkGrid *grid, int row, int weight)
+{
+ grid->row_weights[row] = weight;
+ ltk_recalculate_grid(grid);
+}
+
+void ltk_set_column_weight(LtkGrid *grid, int column, int weight)
+{
+ grid->column_weights[column] = weight;
+ ltk_recalculate_grid(grid);
+}
+
+void ltk_draw_grid(LtkGrid *grid)
+{
+ int i;
+ for (i = 0; i < grid->rows * grid->columns; i++)
+ {
+ if (!grid->widget_grid[i])
+ {
+ continue;
+ }
+ LtkWidget *ptr = grid->widget_grid[i];
+ ptr->draw_function(ptr);
+ }
+}
+
+LtkGrid *ltk_create_grid(LtkWindow *window, int rows, int columns)
+{
+ LtkGrid *grid = malloc(sizeof(LtkGrid));
+
+ grid->widget.window = window;
+ grid->widget.parent = NULL;
+ grid->widget.key_func = <k_grid_key_event;
+ grid->widget.mouse_func = <k_grid_mouse_event;
+ grid->widget.update_function = <k_recalculate_grid;
+ grid->widget.draw_function = <k_draw_grid;
+ grid->widget.destroy_function = <k_destroy_grid;
+ grid->widget.rect.x = 0;
+ grid->widget.rect.y = 0;
+ grid->widget.rect.w = 0;
+ grid->widget.rect.h = 0;
+ grid->widget.active_widget = NULL;
+ grid->widget.state = NORMAL;
+
+ grid->rows = rows;
+ grid->columns = columns;
+ grid->widget_grid = malloc(rows * columns * sizeof(LtkWidget));
+ grid->row_heights = malloc(rows * sizeof(int));
+ grid->column_widths = malloc(rows * sizeof(int));
+ grid->row_weights = malloc(rows * sizeof(int));
+ grid->column_weights = malloc(columns * sizeof(int));
+ /* Positions have one extra for the end */
+ grid->row_pos = malloc((rows + 1) * sizeof(int));
+ grid->column_pos = malloc((columns + 1) * sizeof(int));
+ int i;
+ for (i = 0; i < rows; i++)
+ {
+ grid->row_heights[i] = 0;
+ grid->row_weights[i] = 0;
+ grid->row_pos[i] = 0;
+ }
+ grid->row_pos[rows] = 0;
+ for (i = 0; i < columns; i++)
+ {
+ grid->column_widths[i] = 0;
+ grid->column_weights[i] = 0;
+ grid->column_pos[i] = 0;
+ }
+ grid->column_pos[columns] = 0;
+ for (i = 0; i < rows * columns; i++)
+ {
+ grid->widget_grid[i] = NULL;
+ }
+
+ ltk_recalculate_grid(grid);
+ return grid;
+}
+
+void ltk_destroy_grid(void *widget)
+{
+ LtkGrid *grid = widget;
+ LtkWidget *ptr;
+ int i;
+ for (i = 0; i < grid->rows * grid->columns; i++)
+ {
+ if (grid->widget_grid[i])
+ {
+ ptr = grid->widget_grid[i];
+ ptr->destroy_function(ptr);
+ }
+ }
+ free(grid->widget_grid);
+ free(grid->row_heights);
+ free(grid->column_widths);
+ free(grid->row_weights);
+ free(grid->column_weights);
+ free(grid->row_pos);
+ free(grid->column_pos);
+ free(grid);
+}
+
+void ltk_recalculate_grid(void *widget)
+{
+ LtkGrid *grid = widget;
+ unsigned int height_static = 0, width_static = 0;
+ unsigned int total_row_weight = 0, total_column_weight = 0;
+ float height_unit = 0, width_unit = 0;
+ unsigned int currentx = 0, currenty = 0;
+ int i, j;
+ for (i = 0; i < grid->rows; i++)
+ {
+ total_row_weight += grid->row_weights[i];
+ if (grid->row_weights[i] == 0)
+ {
+ height_static += grid->row_heights[i];
+ }
+ }
+ for (i = 0; i < grid->columns; i++)
+ {
+ total_column_weight += grid->column_weights[i];
+ if (grid->column_weights[i] == 0)
+ {
+ width_static += grid->column_widths[i];
+ }
+ }
+ if (total_row_weight > 0)
+ {
+ height_unit = (float)(grid->widget.rect.h - height_static) / (float)total_row_weight;
+ }
+ if (total_column_weight > 0)
+ {
+ width_unit = (float)(grid->widget.rect.w - width_static) / (float)total_column_weight;
+ }
+ for (i = 0; i < grid->rows; i++)
+ {
+ grid->row_pos[i] = currenty;
+ if (grid->row_weights[i] > 0)
+ {
+ grid->row_heights[i] = grid->row_weights[i] * height_unit;
+ }
+ currenty += grid->row_heights[i];
+ }
+ grid->row_pos[grid->rows] = currenty;
+ for (i = 0; i < grid->columns; i++)
+ {
+ grid->column_pos[i] = currentx;
+ if (grid->column_weights[i] > 0)
+ {
+ grid->column_widths[i] = grid->column_weights[i] * width_unit;
+ }
+ currentx += grid->column_widths[i];
+ }
+ grid->column_pos[grid->columns] = currentx;
+ int orig_width, orig_height;
+ int end_column, end_row;
+ for (i = 0; i < grid->rows; i++)
+ {
+ for (j = 0; j < grid->columns; j++)
+ {
+ if (!grid->widget_grid[i * grid->columns + j])
+ {
+ continue;
+ }
+ LtkWidget *ptr = grid->widget_grid[i * grid->columns + j];
+ orig_width = ptr->rect.w;
+ orig_height = ptr->rect.h;
+ end_row = i + ptr->row_span;
+ end_column = j + ptr->column_span;
+ if (ptr->sticky[1] == 1 && ptr->sticky[3] == 1)
+ {
+ ptr->rect.w = grid->column_pos[end_column] - grid->column_pos[j];
+ }
+ if (ptr->sticky[0] == 1 && ptr->sticky[2] == 1)
+ {
+ ptr->rect.h = grid->row_pos[end_row] - grid->row_pos[i];
+ }
+ if (orig_width != ptr->rect.w || orig_height != ptr->rect.h)
+ {
+ if (ptr->update_function)
+ {
+ ptr->update_function(ptr);
+ }
+ }
+
+ if (ptr->sticky[1] == 1)
+ {
+ ptr->rect.x = grid->column_pos[end_column] - ptr->rect.w;
+ }
+ else if (ptr->sticky[3] == 1)
+ {
+ ptr->rect.x = grid->column_pos[j];
+ }
+ else
+ {
+ ptr->rect.x = grid->column_pos[j] + ((grid->column_pos[end_column] - grid->column_pos[j]) / 2 - ptr->rect.w / 2);
+ }
+
+ if (ptr->sticky[2] == 1)
+ {
+ ptr->rect.y = grid->row_pos[end_row] - ptr->rect.h;
+ }
+ else if (ptr->sticky[0] == 1)
+ {
+ ptr->rect.y = grid->row_pos[i];
+ }
+ else
+ {
+ ptr->rect.y = grid->row_pos[i] + ((grid->row_pos[end_row] - grid->row_pos[i]) / 2 - ptr->rect.h / 2);
+ }
+ }
+ }
+}
+
+void ltk_grid_widget(void *ptr, LtkGrid *grid, int row, int column, int row_span, int column_span, int sticky[4])
+{
+ LtkWidget *widget = ptr;
+ memcpy(widget->sticky, sticky, 4 * sizeof(int));
+ widget->row = row;
+ widget->column = column;
+ widget->row_span = row_span;
+ widget->column_span = column_span;
+ if (grid->column_weights[column] == 0 && widget->rect.w > grid->column_widths[column]) {
+ grid->column_widths[column] = widget->rect.w;
+ }
+ if (grid->row_weights[row] == 0 && widget->rect.h > grid->row_heights[row]) {
+ grid->row_heights[row] = widget->rect.h;
+ }
+ grid->widget_grid[widget->row * grid->columns + widget->column] = widget;
+ widget->parent = grid;
+ ltk_recalculate_grid(grid);
+}
+
+void ltk_grid_key_event(void *widget, XEvent event)
+{
+ LtkGrid *grid = widget;
+ LtkWidget *ptr = grid->widget.active_widget;
+ if (ptr && ptr->key_func)
+ {
+ ptr->key_func(ptr, event);
+ }
+}
+
+void ltk_grid_mouse_event(void *widget, XEvent event)
+{
+ LtkGrid *grid = widget;
+ LtkWidget *ptr;
+ int i;
+ int x, y;
+ int row, column;
+ if (event.type == ButtonPress || event.type == ButtonRelease)
+ {
+ x = event.xbutton.x;
+ y = event.xbutton.y;
+ }
+ else if (event.type == MotionNotify)
+ {
+ x = event.xmotion.x;
+ y = event.xmotion.y;
+ }
+ for (i = 0; i < grid->columns; i++)
+ {
+ if (grid->column_pos[i] <= x && grid->column_pos[i + 1] >= x)
+ {
+ column = i;
+ }
+ }
+ for (i = 0; i < grid->rows; i++)
+ {
+ if (grid->row_pos[i] <= y && grid->row_pos[i + 1] >= y)
+ {
+ row = i;
+ }
+ }
+ ptr = grid->widget_grid[row * grid->columns + column];
+ if (ptr && ptr->mouse_func)
+ {
+ if (ltk_collide_rect(ptr->rect, x, y))
+ {
+ ptr->mouse_func(ptr, event);
+ }
+ else if (grid->widget.hover_widget)
+ {
+ ltk_remove_hover_widget(grid);
+ }
+ }
+}
diff --git a/grid.h b/grid.h
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the Lumidify ToolKit (LTK)
+ * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _LTK_GRID_H_
+#define _LTK_GRID_H_
+
+#include "ltk.h"
+
+typedef struct LtkGrid
+{
+ LtkWidget widget;
+ unsigned int rows;
+ unsigned int columns;
+ void **widget_grid;
+ unsigned int *row_heights;
+ unsigned int *column_widths;
+ unsigned int *row_weights;
+ unsigned int *column_weights;
+ unsigned int *row_pos;
+ unsigned int *column_pos;
+} LtkGrid;
+
+void ltk_set_row_weight(LtkGrid *grid, int row, int weight);
+void ltk_set_column_weight(LtkGrid *grid, int column, int weight);
+void ltk_draw_grid(LtkGrid *grid);
+LtkGrid *ltk_create_grid(LtkWindow *window, int rows, int columns);
+void ltk_destroy_grid(void *widget);
+void ltk_recalculate_grid(void *widget);
+void ltk_grid_key_event(void *widget, XEvent event);
+void ltk_grid_mouse_event(void *widget, XEvent event);
+void ltk_grid_widget(void *ptr, LtkGrid *grid, int row, int column, int rowspan, int columnspan, int sticky[4]);
+
+#endif
diff --git a/ltk.c b/ltk.c
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the Lumidify ToolKit (LTK)
+ * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "ltk.h"
+
+void ltk_init(const char *theme_path)
+{
+ ltk_global = malloc(sizeof(Ltk));
+ Ltk *ltk = ltk_global; /* For convenience */
+ ltk->display = XOpenDisplay(NULL);
+ ltk->screen = DefaultScreen(ltk->display);
+ ltk->colormap = DefaultColormap(ltk->display, ltk->screen);
+ ltk->theme = ltk_load_theme(theme_path);
+ ltk->window_hash = NULL;
+}
+
+void ltk_quit(void)
+{
+ printf("CLEAN UP!\n");
+ exit(1);
+}
+
+void ltk_fatal(const char *msg)
+{
+ printf(msg);
+ ltk_quit();
+};
+
+XColor ltk_create_xcolor(const char *hex)
+{
+ XColor color;
+ XParseColor(ltk_global->display, ltk_global->colormap, hex, &color);
+ XAllocColor(ltk_global->display, ltk_global->colormap, &color);
+
+ return color;
+}
+
+void ltk_mainloop(void)
+{
+ XEvent event;
+ KeySym key;
+ char text[255];
+
+ while(1)
+ {
+ XNextEvent(ltk_global->display, &event);
+ ltk_handle_event(event);
+ /*
+ if (event.type == KeyPress && XLookupString(&event.xkey, text, 255, &key, 0) == 1)
+ {
+ if (text[0] == 'q')
+ {
+ XCloseDisplay(ltk_global->display);
+ exit(0);
+ }
+ }
+ */
+ }
+}
diff --git a/ltk.h b/ltk.h
@@ -0,0 +1,57 @@
+/*
+ * This file is part of the Lumidify ToolKit (LTK)
+ * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _LTK_H_
+#define _LTK_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include "cJSON.h"
+#include "uthash.h"
+#include "common.h"
+#include "widget.h"
+#include "event.h"
+#include "window.h"
+#include "theme.h"
+#include "grid.h"
+#include "button.h"
+
+typedef struct
+{
+ LtkTheme *theme;
+ Display *display;
+ int screen;
+ Colormap colormap;
+ LtkWindow *window_hash;
+} Ltk;
+
+Ltk *ltk_global;
+
+void ltk_init(const char *theme_path);
+void ltk_fatal(const char *msg);
+XColor ltk_create_xcolor(const char *hex);
+void ltk_mainloop(void);
+
+#endif
diff --git a/main.c b/main.c
@@ -0,0 +1,52 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xos.h>
+
+int main(int argc, char *argv[])
+{
+ Display *display;
+ int screen;
+ Window window;
+ GC gc;
+
+ unsigned long black, white;
+ XColor green;
+ Colormap colormap;
+ display = XOpenDisplay((char *)0);
+ screen = DefaultScreen(display);
+ colormap = DefaultColormap(display, screen);
+ black = BlackPixel(display, screen);
+ white = WhitePixel(display, screen);
+ XParseColor(display, colormap, "#00FF00", &green);
+ XAllocColor(display, colormap, &green);
+ window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, 200, 300, 0, white, green.pixel);
+ XSetStandardProperties(display, window, "Random Window", NULL, None, NULL, 0, NULL);
+ XSelectInput(display, window, ExposureMask|ButtonPressMask|KeyPressMask);
+ gc = XCreateGC(display, window, 0, 0);
+ XSetBackground(display, gc, white);
+ XSetForeground(display, gc, black);
+ XClearWindow(display, window);
+ XMapRaised(display, window);
+
+ XEvent event;
+ KeySym key;
+ char text[255];
+
+ while(1)
+ {
+ XNextEvent(display, &event);
+ if (event.type == KeyPress && XLookupString(&event.xkey, text, 255, &key, 0) == 1)
+ {
+ if (text[0] == 'q')
+ {
+ XFreeGC(display, gc);
+ XFreeColormap(display, colormap);
+ XDestroyWindow(display, window);
+ XCloseDisplay(display);
+ exit(0);
+ }
+ }
+ }
+}
diff --git a/test1.c b/test1.c
@@ -0,0 +1,41 @@
+#include "ltk.h"
+
+void bob(void *window, XEvent event)
+{
+ KeySym key;
+ char text[255];
+ if (XLookupString(&event.xkey, text, 255, &key, 0) == 1)
+ {
+ if (text[0] == 'q')
+ {
+ ltk_quit();
+ }
+ }
+}
+
+void bob1(void *window, XEvent event)
+{
+ printf("mouse\n");
+}
+
+int main(int argc, char *argv[])
+{
+ ltk_init("themes/default.json");
+ LtkWindow *window1 = ltk_create_window("Cool Window!", 0, 0, 500, 500);
+ LtkGrid *grid1 = ltk_create_grid(window1, 2, 2);
+ window1->root_widget = grid1;
+ ltk_set_row_weight(grid1, 0, 1);
+ ltk_set_row_weight(grid1, 1, 1);
+ ltk_set_column_weight(grid1, 0, 1);
+ ltk_set_column_weight(grid1, 1, 1);
+ LtkButton *button1 = ltk_create_button(window1, "I'm a button!", NULL);
+ int sticky1[4] = {0, 1, 0, 1};
+ ltk_grid_widget(button1, grid1, 0, 0, 1, 1, sticky1);
+ LtkButton *button2 = ltk_create_button(window1, "I'm a button!", NULL);
+ ltk_grid_widget(button2, grid1, 0, 1, 1, 1, sticky1);
+ LtkButton *button3 = ltk_create_button(window1, "I'm a button!", NULL);
+ ltk_grid_widget(button3, grid1, 1, 0, 1, 1, sticky1);
+ LtkButton *button4 = ltk_create_button(window1, "I'm a button!", NULL);
+ ltk_grid_widget(button4, grid1, 1, 1, 1, 1, sticky1);
+ ltk_mainloop();
+}
diff --git a/theme.c b/theme.c
@@ -0,0 +1,64 @@
+/*
+ * This file is part of the Lumidify ToolKit (LTK)
+ * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "ltk.h"
+
+LtkTheme *ltk_load_theme(const char *path)
+{
+ char *file_contents = ltk_read_file(path);
+
+ cJSON *json = cJSON_Parse(file_contents);
+ if (!json)
+ {
+ printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
+ return NULL;
+ }
+ cJSON *button_json = cJSON_GetObjectItem(json, "button");
+ if (!button_json)
+ {
+ printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
+ return NULL;
+ }
+ cJSON *window_json = cJSON_GetObjectItem(json, "window");
+ if (!window_json)
+ {
+ printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
+ return NULL;
+ }
+
+ LtkTheme *theme = malloc(sizeof(LtkTheme));
+ theme->button = ltk_parse_button_theme(button_json);
+ theme->window = ltk_parse_window_theme(window_json);
+
+ free(file_contents);
+ cJSON_Delete(json);
+
+ return theme;
+}
+
+void ltk_destroy_theme(LtkTheme *theme)
+{
+ free(theme->button);
+ free(theme->window);
+ free(theme);
+}
diff --git a/theme.h b/theme.h
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the Lumidify ToolKit (LTK)
+ * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _LTK_THEME_H_
+#define _LTK_THEME_H_
+
+typedef struct LtkWindowTheme LtkWindowTheme;
+typedef struct LtkButtonTheme LtkButtonTheme;
+
+typedef struct
+{
+ LtkWindowTheme *window;
+ LtkButtonTheme *button;
+} LtkTheme;
+
+LtkTheme *ltk_load_theme(const char *path);
+void ltk_destroy_theme(LtkTheme *theme);
+
+#endif
diff --git a/themes/default.json b/themes/default.json
@@ -0,0 +1,34 @@
+{
+ "window": {
+ "border-width": 0,
+ "background": "#000000",
+ "foreground": "#FFFFFF"
+ },
+ "button": {
+ "normal": {
+ "border-width": 2,
+ "font-size": 20,
+ "border-color": "#339999",
+ "fill-color": "#113355",
+ "padding-left": 5,
+ "padding-right": 5,
+ "padding-top": 5,
+ "padding-bottom": 5
+ },
+ "hover": {
+ "fill-color": "#738194"
+ },
+ "pressed": {
+ "border-color": "#FFFFFF",
+ "fill-color": "#738194"
+ },
+ "active": {
+ "border-color": "#FFFFFF",
+ "fill-color": "#113355"
+ },
+ "disabled": {
+ "border-color": "#FFFFFF",
+ "fill-color": "#292929"
+ }
+ }
+}
diff --git a/uthash.h b/uthash.h
@@ -0,0 +1,1074 @@
+/*
+Copyright (c) 2003-2016, Troy D. Hanson http://troydhanson.github.com/uthash/
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef UTHASH_H
+#define UTHASH_H
+
+#define UTHASH_VERSION 2.0.1
+
+#include <string.h> /* memcmp,strlen */
+#include <stddef.h> /* ptrdiff_t */
+#include <stdlib.h> /* exit() */
+
+/* These macros use decltype or the earlier __typeof GNU extension.
+ As decltype is only available in newer compilers (VS2010 or gcc 4.3+
+ when compiling c++ source) this code uses whatever method is needed
+ or, for VS2008 where neither is available, uses casting workarounds. */
+#if defined(_MSC_VER) /* MS compiler */
+#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
+#define DECLTYPE(x) (decltype(x))
+#else /* VS2008 or older (or VS2010 in C mode) */
+#define NO_DECLTYPE
+#define DECLTYPE(x)
+#endif
+#elif defined(__BORLANDC__) || defined(__LCC__) || defined(__WATCOMC__)
+#define NO_DECLTYPE
+#define DECLTYPE(x)
+#else /* GNU, Sun and other compilers */
+#define DECLTYPE(x) (__typeof(x))
+#endif
+
+#ifdef NO_DECLTYPE
+#define DECLTYPE_ASSIGN(dst,src) \
+do { \
+ char **_da_dst = (char**)(&(dst)); \
+ *_da_dst = (char*)(src); \
+} while (0)
+#else
+#define DECLTYPE_ASSIGN(dst,src) \
+do { \
+ (dst) = DECLTYPE(dst)(src); \
+} while (0)
+#endif
+
+/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */
+#if defined(_WIN32)
+#if defined(_MSC_VER) && _MSC_VER >= 1600
+#include <stdint.h>
+#elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__)
+#include <stdint.h>
+#else
+typedef unsigned int uint32_t;
+typedef unsigned char uint8_t;
+#endif
+#elif defined(__GNUC__) && !defined(__VXWORKS__)
+#include <stdint.h>
+#else
+typedef unsigned int uint32_t;
+typedef unsigned char uint8_t;
+#endif
+
+#ifndef uthash_fatal
+#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */
+#endif
+#ifndef uthash_malloc
+#define uthash_malloc(sz) malloc(sz) /* malloc fcn */
+#endif
+#ifndef uthash_free
+#define uthash_free(ptr,sz) free(ptr) /* free fcn */
+#endif
+#ifndef uthash_strlen
+#define uthash_strlen(s) strlen(s)
+#endif
+#ifndef uthash_memcmp
+#define uthash_memcmp(a,b,n) memcmp(a,b,n)
+#endif
+
+#ifndef uthash_noexpand_fyi
+#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */
+#endif
+#ifndef uthash_expand_fyi
+#define uthash_expand_fyi(tbl) /* can be defined to log expands */
+#endif
+
+/* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */
+#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */
+
+/* calculate the element whose hash handle address is hhp */
+#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
+/* calculate the hash handle from element address elp */
+#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle *)(((char*)(elp)) + ((tbl)->hho)))
+
+#define HASH_VALUE(keyptr,keylen,hashv) \
+do { \
+ HASH_FCN(keyptr, keylen, hashv); \
+} while (0)
+
+#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \
+do { \
+ (out) = NULL; \
+ if (head) { \
+ unsigned _hf_bkt; \
+ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \
+ if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \
+ HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \
+ } \
+ } \
+} while (0)
+
+#define HASH_FIND(hh,head,keyptr,keylen,out) \
+do { \
+ unsigned _hf_hashv; \
+ HASH_VALUE(keyptr, keylen, _hf_hashv); \
+ HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \
+} while (0)
+
+#ifdef HASH_BLOOM
+#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM)
+#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL)
+#define HASH_BLOOM_MAKE(tbl) \
+do { \
+ (tbl)->bloom_nbits = HASH_BLOOM; \
+ (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \
+ if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \
+ memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \
+ (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \
+} while (0)
+
+#define HASH_BLOOM_FREE(tbl) \
+do { \
+ uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \
+} while (0)
+
+#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U)))
+#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U)))
+
+#define HASH_BLOOM_ADD(tbl,hashv) \
+ HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U)))
+
+#define HASH_BLOOM_TEST(tbl,hashv) \
+ HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U)))
+
+#else
+#define HASH_BLOOM_MAKE(tbl)
+#define HASH_BLOOM_FREE(tbl)
+#define HASH_BLOOM_ADD(tbl,hashv)
+#define HASH_BLOOM_TEST(tbl,hashv) (1)
+#define HASH_BLOOM_BYTELEN 0U
+#endif
+
+#define HASH_MAKE_TABLE(hh,head) \
+do { \
+ (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \
+ sizeof(UT_hash_table)); \
+ if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \
+ memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \
+ (head)->hh.tbl->tail = &((head)->hh); \
+ (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \
+ (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \
+ (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \
+ (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \
+ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
+ if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \
+ memset((head)->hh.tbl->buckets, 0, \
+ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
+ HASH_BLOOM_MAKE((head)->hh.tbl); \
+ (head)->hh.tbl->signature = HASH_SIGNATURE; \
+} while (0)
+
+#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \
+do { \
+ (replaced) = NULL; \
+ HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
+ if (replaced) { \
+ HASH_DELETE(hh, head, replaced); \
+ } \
+ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \
+} while (0)
+
+#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \
+do { \
+ (replaced) = NULL; \
+ HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
+ if (replaced) { \
+ HASH_DELETE(hh, head, replaced); \
+ } \
+ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \
+} while (0)
+
+#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \
+do { \
+ unsigned _hr_hashv; \
+ HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \
+ HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \
+} while (0)
+
+#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \
+do { \
+ unsigned _hr_hashv; \
+ HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \
+ HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \
+} while (0)
+
+#define HASH_APPEND_LIST(hh, head, add) \
+do { \
+ (add)->hh.next = NULL; \
+ (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \
+ (head)->hh.tbl->tail->next = (add); \
+ (head)->hh.tbl->tail = &((add)->hh); \
+} while (0)
+
+#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \
+do { \
+ unsigned _ha_bkt; \
+ (add)->hh.hashv = (hashval); \
+ (add)->hh.key = (char*) (keyptr); \
+ (add)->hh.keylen = (unsigned) (keylen_in); \
+ if (!(head)) { \
+ (add)->hh.next = NULL; \
+ (add)->hh.prev = NULL; \
+ (head) = (add); \
+ HASH_MAKE_TABLE(hh, head); \
+ } else { \
+ struct UT_hash_handle *_hs_iter = &(head)->hh; \
+ (add)->hh.tbl = (head)->hh.tbl; \
+ do { \
+ if (cmpfcn(DECLTYPE(head) ELMT_FROM_HH((head)->hh.tbl, _hs_iter), add) > 0) \
+ break; \
+ } while ((_hs_iter = _hs_iter->next)); \
+ if (_hs_iter) { \
+ (add)->hh.next = _hs_iter; \
+ if (((add)->hh.prev = _hs_iter->prev)) { \
+ HH_FROM_ELMT((head)->hh.tbl, _hs_iter->prev)->next = (add); \
+ } else { \
+ (head) = (add); \
+ } \
+ _hs_iter->prev = (add); \
+ } else { \
+ HASH_APPEND_LIST(hh, head, add); \
+ } \
+ } \
+ (head)->hh.tbl->num_items++; \
+ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \
+ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], &(add)->hh); \
+ HASH_BLOOM_ADD((head)->hh.tbl, hashval); \
+ HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \
+ HASH_FSCK(hh, head); \
+} while (0)
+
+#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \
+do { \
+ unsigned _hs_hashv; \
+ HASH_VALUE(keyptr, keylen_in, _hs_hashv); \
+ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \
+} while (0)
+
+#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \
+ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn)
+
+#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \
+ HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn)
+
+#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \
+do { \
+ unsigned _ha_bkt; \
+ (add)->hh.hashv = (hashval); \
+ (add)->hh.key = (char*) (keyptr); \
+ (add)->hh.keylen = (unsigned) (keylen_in); \
+ if (!(head)) { \
+ (add)->hh.next = NULL; \
+ (add)->hh.prev = NULL; \
+ (head) = (add); \
+ HASH_MAKE_TABLE(hh, head); \
+ } else { \
+ (add)->hh.tbl = (head)->hh.tbl; \
+ HASH_APPEND_LIST(hh, head, add); \
+ } \
+ (head)->hh.tbl->num_items++; \
+ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \
+ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], &(add)->hh); \
+ HASH_BLOOM_ADD((head)->hh.tbl, hashval); \
+ HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \
+ HASH_FSCK(hh, head); \
+} while (0)
+
+#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \
+do { \
+ unsigned _ha_hashv; \
+ HASH_VALUE(keyptr, keylen_in, _ha_hashv); \
+ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \
+} while (0)
+
+#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \
+ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add)
+
+#define HASH_ADD(hh,head,fieldname,keylen_in,add) \
+ HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add)
+
+#define HASH_TO_BKT(hashv,num_bkts,bkt) \
+do { \
+ bkt = ((hashv) & ((num_bkts) - 1U)); \
+} while (0)
+
+/* delete "delptr" from the hash table.
+ * "the usual" patch-up process for the app-order doubly-linked-list.
+ * The use of _hd_hh_del below deserves special explanation.
+ * These used to be expressed using (delptr) but that led to a bug
+ * if someone used the same symbol for the head and deletee, like
+ * HASH_DELETE(hh,users,users);
+ * We want that to work, but by changing the head (users) below
+ * we were forfeiting our ability to further refer to the deletee (users)
+ * in the patch-up process. Solution: use scratch space to
+ * copy the deletee pointer, then the latter references are via that
+ * scratch pointer rather than through the repointed (users) symbol.
+ */
+#define HASH_DELETE(hh,head,delptr) \
+do { \
+ struct UT_hash_handle *_hd_hh_del; \
+ if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \
+ uthash_free((head)->hh.tbl->buckets, \
+ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
+ HASH_BLOOM_FREE((head)->hh.tbl); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ head = NULL; \
+ } else { \
+ unsigned _hd_bkt; \
+ _hd_hh_del = &((delptr)->hh); \
+ if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \
+ (head)->hh.tbl->tail = \
+ (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \
+ (head)->hh.tbl->hho); \
+ } \
+ if ((delptr)->hh.prev != NULL) { \
+ ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \
+ (head)->hh.tbl->hho))->next = (delptr)->hh.next; \
+ } else { \
+ DECLTYPE_ASSIGN(head,(delptr)->hh.next); \
+ } \
+ if (_hd_hh_del->next != NULL) { \
+ ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \
+ (head)->hh.tbl->hho))->prev = \
+ _hd_hh_del->prev; \
+ } \
+ HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
+ HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \
+ (head)->hh.tbl->num_items--; \
+ } \
+ HASH_FSCK(hh,head); \
+} while (0)
+
+
+/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
+#define HASH_FIND_STR(head,findstr,out) \
+ HASH_FIND(hh,head,findstr,(unsigned)uthash_strlen(findstr),out)
+#define HASH_ADD_STR(head,strfield,add) \
+ HASH_ADD(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add)
+#define HASH_REPLACE_STR(head,strfield,add,replaced) \
+ HASH_REPLACE(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add,replaced)
+#define HASH_FIND_INT(head,findint,out) \
+ HASH_FIND(hh,head,findint,sizeof(int),out)
+#define HASH_ADD_INT(head,intfield,add) \
+ HASH_ADD(hh,head,intfield,sizeof(int),add)
+#define HASH_REPLACE_INT(head,intfield,add,replaced) \
+ HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced)
+#define HASH_FIND_PTR(head,findptr,out) \
+ HASH_FIND(hh,head,findptr,sizeof(void *),out)
+#define HASH_ADD_PTR(head,ptrfield,add) \
+ HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
+#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \
+ HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)
+#define HASH_DEL(head,delptr) \
+ HASH_DELETE(hh,head,delptr)
+
+/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
+ * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
+ */
+#ifdef HASH_DEBUG
+#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
+#define HASH_FSCK(hh,head) \
+do { \
+ struct UT_hash_handle *_thh; \
+ if (head) { \
+ unsigned _bkt_i; \
+ unsigned _count; \
+ char *_prev; \
+ _count = 0; \
+ for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \
+ unsigned _bkt_count = 0; \
+ _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \
+ _prev = NULL; \
+ while (_thh) { \
+ if (_prev != (char*)(_thh->hh_prev)) { \
+ HASH_OOPS("invalid hh_prev %p, actual %p\n", \
+ _thh->hh_prev, _prev ); \
+ } \
+ _bkt_count++; \
+ _prev = (char*)(_thh); \
+ _thh = _thh->hh_next; \
+ } \
+ _count += _bkt_count; \
+ if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \
+ HASH_OOPS("invalid bucket count %u, actual %u\n", \
+ (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \
+ } \
+ } \
+ if (_count != (head)->hh.tbl->num_items) { \
+ HASH_OOPS("invalid hh item count %u, actual %u\n", \
+ (head)->hh.tbl->num_items, _count ); \
+ } \
+ /* traverse hh in app order; check next/prev integrity, count */ \
+ _count = 0; \
+ _prev = NULL; \
+ _thh = &(head)->hh; \
+ while (_thh) { \
+ _count++; \
+ if (_prev !=(char*)(_thh->prev)) { \
+ HASH_OOPS("invalid prev %p, actual %p\n", \
+ _thh->prev, _prev ); \
+ } \
+ _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \
+ _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \
+ (head)->hh.tbl->hho) : NULL ); \
+ } \
+ if (_count != (head)->hh.tbl->num_items) { \
+ HASH_OOPS("invalid app item count %u, actual %u\n", \
+ (head)->hh.tbl->num_items, _count ); \
+ } \
+ } \
+} while (0)
+#else
+#define HASH_FSCK(hh,head)
+#endif
+
+/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
+ * the descriptor to which this macro is defined for tuning the hash function.
+ * The app can #include <unistd.h> to get the prototype for write(2). */
+#ifdef HASH_EMIT_KEYS
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \
+do { \
+ unsigned _klen = fieldlen; \
+ write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \
+ write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \
+} while (0)
+#else
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
+#endif
+
+/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
+#ifdef HASH_FUNCTION
+#define HASH_FCN HASH_FUNCTION
+#else
+#define HASH_FCN HASH_JEN
+#endif
+
+/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */
+#define HASH_BER(key,keylen,hashv) \
+do { \
+ unsigned _hb_keylen=(unsigned)keylen; \
+ const unsigned char *_hb_key=(const unsigned char*)(key); \
+ (hashv) = 0; \
+ while (_hb_keylen-- != 0U) { \
+ (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \
+ } \
+} while (0)
+
+
+/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
+ * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
+#define HASH_SAX(key,keylen,hashv) \
+do { \
+ unsigned _sx_i; \
+ const unsigned char *_hs_key=(const unsigned char*)(key); \
+ hashv = 0; \
+ for(_sx_i=0; _sx_i < keylen; _sx_i++) { \
+ hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \
+ } \
+} while (0)
+/* FNV-1a variation */
+#define HASH_FNV(key,keylen,hashv) \
+do { \
+ unsigned _fn_i; \
+ const unsigned char *_hf_key=(const unsigned char*)(key); \
+ hashv = 2166136261U; \
+ for(_fn_i=0; _fn_i < keylen; _fn_i++) { \
+ hashv = hashv ^ _hf_key[_fn_i]; \
+ hashv = hashv * 16777619U; \
+ } \
+} while (0)
+
+#define HASH_OAT(key,keylen,hashv) \
+do { \
+ unsigned _ho_i; \
+ const unsigned char *_ho_key=(const unsigned char*)(key); \
+ hashv = 0; \
+ for(_ho_i=0; _ho_i < keylen; _ho_i++) { \
+ hashv += _ho_key[_ho_i]; \
+ hashv += (hashv << 10); \
+ hashv ^= (hashv >> 6); \
+ } \
+ hashv += (hashv << 3); \
+ hashv ^= (hashv >> 11); \
+ hashv += (hashv << 15); \
+} while (0)
+
+#define HASH_JEN_MIX(a,b,c) \
+do { \
+ a -= b; a -= c; a ^= ( c >> 13 ); \
+ b -= c; b -= a; b ^= ( a << 8 ); \
+ c -= a; c -= b; c ^= ( b >> 13 ); \
+ a -= b; a -= c; a ^= ( c >> 12 ); \
+ b -= c; b -= a; b ^= ( a << 16 ); \
+ c -= a; c -= b; c ^= ( b >> 5 ); \
+ a -= b; a -= c; a ^= ( c >> 3 ); \
+ b -= c; b -= a; b ^= ( a << 10 ); \
+ c -= a; c -= b; c ^= ( b >> 15 ); \
+} while (0)
+
+#define HASH_JEN(key,keylen,hashv) \
+do { \
+ unsigned _hj_i,_hj_j,_hj_k; \
+ unsigned const char *_hj_key=(unsigned const char*)(key); \
+ hashv = 0xfeedbeefu; \
+ _hj_i = _hj_j = 0x9e3779b9u; \
+ _hj_k = (unsigned)(keylen); \
+ while (_hj_k >= 12U) { \
+ _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \
+ + ( (unsigned)_hj_key[2] << 16 ) \
+ + ( (unsigned)_hj_key[3] << 24 ) ); \
+ _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \
+ + ( (unsigned)_hj_key[6] << 16 ) \
+ + ( (unsigned)_hj_key[7] << 24 ) ); \
+ hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \
+ + ( (unsigned)_hj_key[10] << 16 ) \
+ + ( (unsigned)_hj_key[11] << 24 ) ); \
+ \
+ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
+ \
+ _hj_key += 12; \
+ _hj_k -= 12U; \
+ } \
+ hashv += (unsigned)(keylen); \
+ switch ( _hj_k ) { \
+ case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \
+ case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \
+ case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \
+ case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \
+ case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \
+ case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \
+ case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \
+ case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \
+ case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \
+ case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \
+ case 1: _hj_i += _hj_key[0]; \
+ } \
+ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
+} while (0)
+
+/* The Paul Hsieh hash function */
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
+ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
+ +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+#define HASH_SFH(key,keylen,hashv) \
+do { \
+ unsigned const char *_sfh_key=(unsigned const char*)(key); \
+ uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \
+ \
+ unsigned _sfh_rem = _sfh_len & 3U; \
+ _sfh_len >>= 2; \
+ hashv = 0xcafebabeu; \
+ \
+ /* Main loop */ \
+ for (;_sfh_len > 0U; _sfh_len--) { \
+ hashv += get16bits (_sfh_key); \
+ _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \
+ hashv = (hashv << 16) ^ _sfh_tmp; \
+ _sfh_key += 2U*sizeof (uint16_t); \
+ hashv += hashv >> 11; \
+ } \
+ \
+ /* Handle end cases */ \
+ switch (_sfh_rem) { \
+ case 3: hashv += get16bits (_sfh_key); \
+ hashv ^= hashv << 16; \
+ hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \
+ hashv += hashv >> 11; \
+ break; \
+ case 2: hashv += get16bits (_sfh_key); \
+ hashv ^= hashv << 11; \
+ hashv += hashv >> 17; \
+ break; \
+ case 1: hashv += *_sfh_key; \
+ hashv ^= hashv << 10; \
+ hashv += hashv >> 1; \
+ } \
+ \
+ /* Force "avalanching" of final 127 bits */ \
+ hashv ^= hashv << 3; \
+ hashv += hashv >> 5; \
+ hashv ^= hashv << 4; \
+ hashv += hashv >> 17; \
+ hashv ^= hashv << 25; \
+ hashv += hashv >> 6; \
+} while (0)
+
+#ifdef HASH_USING_NO_STRICT_ALIASING
+/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads.
+ * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.
+ * MurmurHash uses the faster approach only on CPU's where we know it's safe.
+ *
+ * Note the preprocessor built-in defines can be emitted using:
+ *
+ * gcc -m64 -dM -E - < /dev/null (on gcc)
+ * cc -## a.c (where a.c is a simple test file) (Sun Studio)
+ */
+#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86))
+#define MUR_GETBLOCK(p,i) p[i]
+#else /* non intel */
+#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 3UL) == 0UL)
+#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 3UL) == 1UL)
+#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 3UL) == 2UL)
+#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 3UL) == 3UL)
+#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL))
+#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__))
+#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24))
+#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16))
+#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8))
+#else /* assume little endian non-intel */
+#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24))
+#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16))
+#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8))
+#endif
+#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \
+ (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \
+ (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \
+ MUR_ONE_THREE(p))))
+#endif
+#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))
+#define MUR_FMIX(_h) \
+do { \
+ _h ^= _h >> 16; \
+ _h *= 0x85ebca6bu; \
+ _h ^= _h >> 13; \
+ _h *= 0xc2b2ae35u; \
+ _h ^= _h >> 16; \
+} while (0)
+
+#define HASH_MUR(key,keylen,hashv) \
+do { \
+ const uint8_t *_mur_data = (const uint8_t*)(key); \
+ const int _mur_nblocks = (int)(keylen) / 4; \
+ uint32_t _mur_h1 = 0xf88D5353u; \
+ uint32_t _mur_c1 = 0xcc9e2d51u; \
+ uint32_t _mur_c2 = 0x1b873593u; \
+ uint32_t _mur_k1 = 0; \
+ const uint8_t *_mur_tail; \
+ const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+(_mur_nblocks*4)); \
+ int _mur_i; \
+ for(_mur_i = -_mur_nblocks; _mur_i!=0; _mur_i++) { \
+ _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \
+ _mur_k1 *= _mur_c1; \
+ _mur_k1 = MUR_ROTL32(_mur_k1,15); \
+ _mur_k1 *= _mur_c2; \
+ \
+ _mur_h1 ^= _mur_k1; \
+ _mur_h1 = MUR_ROTL32(_mur_h1,13); \
+ _mur_h1 = (_mur_h1*5U) + 0xe6546b64u; \
+ } \
+ _mur_tail = (const uint8_t*)(_mur_data + (_mur_nblocks*4)); \
+ _mur_k1=0; \
+ switch((keylen) & 3U) { \
+ case 3: _mur_k1 ^= (uint32_t)_mur_tail[2] << 16; /* FALLTHROUGH */ \
+ case 2: _mur_k1 ^= (uint32_t)_mur_tail[1] << 8; /* FALLTHROUGH */ \
+ case 1: _mur_k1 ^= (uint32_t)_mur_tail[0]; \
+ _mur_k1 *= _mur_c1; \
+ _mur_k1 = MUR_ROTL32(_mur_k1,15); \
+ _mur_k1 *= _mur_c2; \
+ _mur_h1 ^= _mur_k1; \
+ } \
+ _mur_h1 ^= (uint32_t)(keylen); \
+ MUR_FMIX(_mur_h1); \
+ hashv = _mur_h1; \
+} while (0)
+#endif /* HASH_USING_NO_STRICT_ALIASING */
+
+/* iterate over items in a known bucket to find desired item */
+#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \
+do { \
+ if ((head).hh_head != NULL) { \
+ DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \
+ } else { \
+ (out) = NULL; \
+ } \
+ while ((out) != NULL) { \
+ if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \
+ if (uthash_memcmp((out)->hh.key, keyptr, keylen_in) == 0) { \
+ break; \
+ } \
+ } \
+ if ((out)->hh.hh_next != NULL) { \
+ DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \
+ } else { \
+ (out) = NULL; \
+ } \
+ } \
+} while (0)
+
+/* add an item to a bucket */
+#define HASH_ADD_TO_BKT(head,addhh) \
+do { \
+ head.count++; \
+ (addhh)->hh_next = head.hh_head; \
+ (addhh)->hh_prev = NULL; \
+ if (head.hh_head != NULL) { (head).hh_head->hh_prev = (addhh); } \
+ (head).hh_head=addhh; \
+ if ((head.count >= ((head.expand_mult+1U) * HASH_BKT_CAPACITY_THRESH)) \
+ && ((addhh)->tbl->noexpand != 1U)) { \
+ HASH_EXPAND_BUCKETS((addhh)->tbl); \
+ } \
+} while (0)
+
+/* remove an item from a given bucket */
+#define HASH_DEL_IN_BKT(hh,head,hh_del) \
+ (head).count--; \
+ if ((head).hh_head == hh_del) { \
+ (head).hh_head = hh_del->hh_next; \
+ } \
+ if (hh_del->hh_prev) { \
+ hh_del->hh_prev->hh_next = hh_del->hh_next; \
+ } \
+ if (hh_del->hh_next) { \
+ hh_del->hh_next->hh_prev = hh_del->hh_prev; \
+ }
+
+/* Bucket expansion has the effect of doubling the number of buckets
+ * and redistributing the items into the new buckets. Ideally the
+ * items will distribute more or less evenly into the new buckets
+ * (the extent to which this is true is a measure of the quality of
+ * the hash function as it applies to the key domain).
+ *
+ * With the items distributed into more buckets, the chain length
+ * (item count) in each bucket is reduced. Thus by expanding buckets
+ * the hash keeps a bound on the chain length. This bounded chain
+ * length is the essence of how a hash provides constant time lookup.
+ *
+ * The calculation of tbl->ideal_chain_maxlen below deserves some
+ * explanation. First, keep in mind that we're calculating the ideal
+ * maximum chain length based on the *new* (doubled) bucket count.
+ * In fractions this is just n/b (n=number of items,b=new num buckets).
+ * Since the ideal chain length is an integer, we want to calculate
+ * ceil(n/b). We don't depend on floating point arithmetic in this
+ * hash, so to calculate ceil(n/b) with integers we could write
+ *
+ * ceil(n/b) = (n/b) + ((n%b)?1:0)
+ *
+ * and in fact a previous version of this hash did just that.
+ * But now we have improved things a bit by recognizing that b is
+ * always a power of two. We keep its base 2 log handy (call it lb),
+ * so now we can write this with a bit shift and logical AND:
+ *
+ * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
+ *
+ */
+#define HASH_EXPAND_BUCKETS(tbl) \
+do { \
+ unsigned _he_bkt; \
+ unsigned _he_bkt_i; \
+ struct UT_hash_handle *_he_thh, *_he_hh_nxt; \
+ UT_hash_bucket *_he_new_buckets, *_he_newbkt; \
+ _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \
+ 2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
+ if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \
+ memset(_he_new_buckets, 0, \
+ 2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
+ tbl->ideal_chain_maxlen = \
+ (tbl->num_items >> (tbl->log2_num_buckets+1U)) + \
+ (((tbl->num_items & ((tbl->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \
+ tbl->nonideal_items = 0; \
+ for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \
+ { \
+ _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \
+ while (_he_thh != NULL) { \
+ _he_hh_nxt = _he_thh->hh_next; \
+ HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2U, _he_bkt); \
+ _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \
+ if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \
+ tbl->nonideal_items++; \
+ _he_newbkt->expand_mult = _he_newbkt->count / \
+ tbl->ideal_chain_maxlen; \
+ } \
+ _he_thh->hh_prev = NULL; \
+ _he_thh->hh_next = _he_newbkt->hh_head; \
+ if (_he_newbkt->hh_head != NULL) { _he_newbkt->hh_head->hh_prev = \
+ _he_thh; } \
+ _he_newbkt->hh_head = _he_thh; \
+ _he_thh = _he_hh_nxt; \
+ } \
+ } \
+ uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
+ tbl->num_buckets *= 2U; \
+ tbl->log2_num_buckets++; \
+ tbl->buckets = _he_new_buckets; \
+ tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \
+ (tbl->ineff_expands+1U) : 0U; \
+ if (tbl->ineff_expands > 1U) { \
+ tbl->noexpand=1; \
+ uthash_noexpand_fyi(tbl); \
+ } \
+ uthash_expand_fyi(tbl); \
+} while (0)
+
+
+/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
+/* Note that HASH_SORT assumes the hash handle name to be hh.
+ * HASH_SRT was added to allow the hash handle name to be passed in. */
+#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
+#define HASH_SRT(hh,head,cmpfcn) \
+do { \
+ unsigned _hs_i; \
+ unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \
+ struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \
+ if (head != NULL) { \
+ _hs_insize = 1; \
+ _hs_looping = 1; \
+ _hs_list = &((head)->hh); \
+ while (_hs_looping != 0U) { \
+ _hs_p = _hs_list; \
+ _hs_list = NULL; \
+ _hs_tail = NULL; \
+ _hs_nmerges = 0; \
+ while (_hs_p != NULL) { \
+ _hs_nmerges++; \
+ _hs_q = _hs_p; \
+ _hs_psize = 0; \
+ for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \
+ _hs_psize++; \
+ _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \
+ ((void*)((char*)(_hs_q->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ if (! (_hs_q) ) { break; } \
+ } \
+ _hs_qsize = _hs_insize; \
+ while ((_hs_psize > 0U) || ((_hs_qsize > 0U) && (_hs_q != NULL))) {\
+ if (_hs_psize == 0U) { \
+ _hs_e = _hs_q; \
+ _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \
+ ((void*)((char*)(_hs_q->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ _hs_qsize--; \
+ } else if ( (_hs_qsize == 0U) || (_hs_q == NULL) ) { \
+ _hs_e = _hs_p; \
+ if (_hs_p != NULL){ \
+ _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ? \
+ ((void*)((char*)(_hs_p->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ } \
+ _hs_psize--; \
+ } else if (( \
+ cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
+ DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
+ ) <= 0) { \
+ _hs_e = _hs_p; \
+ if (_hs_p != NULL){ \
+ _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ? \
+ ((void*)((char*)(_hs_p->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ } \
+ _hs_psize--; \
+ } else { \
+ _hs_e = _hs_q; \
+ _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \
+ ((void*)((char*)(_hs_q->next) + \
+ (head)->hh.tbl->hho)) : NULL); \
+ _hs_qsize--; \
+ } \
+ if ( _hs_tail != NULL ) { \
+ _hs_tail->next = ((_hs_e != NULL) ? \
+ ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \
+ } else { \
+ _hs_list = _hs_e; \
+ } \
+ if (_hs_e != NULL) { \
+ _hs_e->prev = ((_hs_tail != NULL) ? \
+ ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \
+ } \
+ _hs_tail = _hs_e; \
+ } \
+ _hs_p = _hs_q; \
+ } \
+ if (_hs_tail != NULL){ \
+ _hs_tail->next = NULL; \
+ } \
+ if ( _hs_nmerges <= 1U ) { \
+ _hs_looping=0; \
+ (head)->hh.tbl->tail = _hs_tail; \
+ DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \
+ } \
+ _hs_insize *= 2U; \
+ } \
+ HASH_FSCK(hh,head); \
+ } \
+} while (0)
+
+/* This function selects items from one hash into another hash.
+ * The end result is that the selected items have dual presence
+ * in both hashes. There is no copy of the items made; rather
+ * they are added into the new hash through a secondary hash
+ * hash handle that must be present in the structure. */
+#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \
+do { \
+ unsigned _src_bkt, _dst_bkt; \
+ void *_last_elt=NULL, *_elt; \
+ UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \
+ ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \
+ if (src != NULL) { \
+ for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \
+ for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \
+ _src_hh != NULL; \
+ _src_hh = _src_hh->hh_next) { \
+ _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \
+ if (cond(_elt)) { \
+ _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \
+ _dst_hh->key = _src_hh->key; \
+ _dst_hh->keylen = _src_hh->keylen; \
+ _dst_hh->hashv = _src_hh->hashv; \
+ _dst_hh->prev = _last_elt; \
+ _dst_hh->next = NULL; \
+ if (_last_elt_hh != NULL) { _last_elt_hh->next = _elt; } \
+ if (dst == NULL) { \
+ DECLTYPE_ASSIGN(dst,_elt); \
+ HASH_MAKE_TABLE(hh_dst,dst); \
+ } else { \
+ _dst_hh->tbl = (dst)->hh_dst.tbl; \
+ } \
+ HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \
+ HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \
+ (dst)->hh_dst.tbl->num_items++; \
+ _last_elt = _elt; \
+ _last_elt_hh = _dst_hh; \
+ } \
+ } \
+ } \
+ } \
+ HASH_FSCK(hh_dst,dst); \
+} while (0)
+
+#define HASH_CLEAR(hh,head) \
+do { \
+ if (head != NULL) { \
+ uthash_free((head)->hh.tbl->buckets, \
+ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \
+ HASH_BLOOM_FREE((head)->hh.tbl); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ (head)=NULL; \
+ } \
+} while (0)
+
+#define HASH_OVERHEAD(hh,head) \
+ ((head != NULL) ? ( \
+ (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \
+ ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \
+ sizeof(UT_hash_table) + \
+ (HASH_BLOOM_BYTELEN))) : 0U)
+
+#ifdef NO_DECLTYPE
+#define HASH_ITER(hh,head,el,tmp) \
+for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \
+ (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL)))
+#else
+#define HASH_ITER(hh,head,el,tmp) \
+for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \
+ (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL)))
+#endif
+
+/* obtain a count of items in the hash */
+#define HASH_COUNT(head) HASH_CNT(hh,head)
+#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U)
+
+typedef struct UT_hash_bucket {
+ struct UT_hash_handle *hh_head;
+ unsigned count;
+
+ /* expand_mult is normally set to 0. In this situation, the max chain length
+ * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
+ * the bucket's chain exceeds this length, bucket expansion is triggered).
+ * However, setting expand_mult to a non-zero value delays bucket expansion
+ * (that would be triggered by additions to this particular bucket)
+ * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
+ * (The multiplier is simply expand_mult+1). The whole idea of this
+ * multiplier is to reduce bucket expansions, since they are expensive, in
+ * situations where we know that a particular bucket tends to be overused.
+ * It is better to let its chain length grow to a longer yet-still-bounded
+ * value, than to do an O(n) bucket expansion too often.
+ */
+ unsigned expand_mult;
+
+} UT_hash_bucket;
+
+/* random signature used only to find hash tables in external analysis */
+#define HASH_SIGNATURE 0xa0111fe1u
+#define HASH_BLOOM_SIGNATURE 0xb12220f2u
+
+typedef struct UT_hash_table {
+ UT_hash_bucket *buckets;
+ unsigned num_buckets, log2_num_buckets;
+ unsigned num_items;
+ struct UT_hash_handle *tail; /* tail hh in app order, for fast append */
+ ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
+
+ /* in an ideal situation (all buckets used equally), no bucket would have
+ * more than ceil(#items/#buckets) items. that's the ideal chain length. */
+ unsigned ideal_chain_maxlen;
+
+ /* nonideal_items is the number of items in the hash whose chain position
+ * exceeds the ideal chain maxlen. these items pay the penalty for an uneven
+ * hash distribution; reaching them in a chain traversal takes >ideal steps */
+ unsigned nonideal_items;
+
+ /* ineffective expands occur when a bucket doubling was performed, but
+ * afterward, more than half the items in the hash had nonideal chain
+ * positions. If this happens on two consecutive expansions we inhibit any
+ * further expansion, as it's not helping; this happens when the hash
+ * function isn't a good fit for the key domain. When expansion is inhibited
+ * the hash will still work, albeit no longer in constant time. */
+ unsigned ineff_expands, noexpand;
+
+ uint32_t signature; /* used only to find hash tables in external analysis */
+#ifdef HASH_BLOOM
+ uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
+ uint8_t *bloom_bv;
+ uint8_t bloom_nbits;
+#endif
+
+} UT_hash_table;
+
+typedef struct UT_hash_handle {
+ struct UT_hash_table *tbl;
+ void *prev; /* prev element in app order */
+ void *next; /* next element in app order */
+ struct UT_hash_handle *hh_prev; /* previous hh in bucket order */
+ struct UT_hash_handle *hh_next; /* next hh in bucket order */
+ void *key; /* ptr to enclosing struct's key */
+ unsigned keylen; /* enclosing struct's key len */
+ unsigned hashv; /* result of hash-fcn(key) */
+} UT_hash_handle;
+
+#endif /* UTHASH_H */
diff --git a/widget.c b/widget.c
@@ -0,0 +1,23 @@
+/*
+ * This file is part of the Lumidify ToolKit (LTK)
+ * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
diff --git a/widget.h b/widget.h
@@ -0,0 +1,68 @@
+/*
+ * This file is part of the Lumidify ToolKit (LTK)
+ * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _LTK_WIDGET_H_
+#define _LTK_WIDGET_H_
+
+#include "event.h"
+
+typedef struct LtkWindow LtkWindow;
+
+typedef struct LtkWidget
+{
+ /* The window the widget will be displayed on */
+ LtkWindow *window;
+ /* For container widgets; the widget that is currently active */
+ void *active_widget;
+ /* For container widgets; the widget that is currently highlighted */
+ void *hover_widget;
+ /* Parent widget */
+ void *parent;
+ /* Function to be called on a KeyPress or KeyRelease event */
+ LTK_EVENT_FUNC key_func;
+ /* Function to be called on a ButtonPress, ButtonRelease, or MotionNotify event */
+ LTK_EVENT_FUNC mouse_func;
+ /* For container widgets; function to be called when the widget is resized */
+ void (*update_function)(void *);
+ /* Function to draw the widget */
+ void (*draw_function)(void *);
+ /* State of the widget; NORMAL, PRESSED, ACTIVE, HOVER, or DISABLED */
+ LtkWidgetState state;
+ /* Function to destroy the widget; used by containers to destroy child widgets */
+ void (*destroy_function)(void *);
+ /* Position and size of the widget */
+ LtkRect rect;
+ /* Row of widget if gridded */
+ unsigned int row;
+ /* Column of widget if gridded */
+ unsigned int column;
+ /* Row span of widget if gridded */
+ unsigned int row_span;
+ /* Column span of widget if gridded */
+ unsigned int column_span;
+ /* Similar to sticky in tk */
+ /* -y, +x, +y, -x */
+ int sticky[4];
+} LtkWidget;
+
+#endif
diff --git a/window.c b/window.c
@@ -0,0 +1,165 @@
+/*
+ * This file is part of the Lumidify ToolKit (LTK)
+ * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "ltk.h"
+
+LtkWindowTheme *ltk_parse_window_theme(cJSON *window_json)
+{
+ LtkWindowTheme *window_theme = malloc(sizeof(LtkWindowTheme));
+ if (!window_theme) ltk_fatal("No memory for new LtkWindowTheme\n");
+ cJSON *border_width = cJSON_GetObjectItem(window_json, "border-width");
+ cJSON *fg = cJSON_GetObjectItem(window_json, "foreground");
+ cJSON *bg = cJSON_GetObjectItem(window_json, "background");
+ window_theme->border_width = border_width ? border_width->valueint : 0;
+ window_theme->fg = ltk_create_xcolor(fg->valuestring);
+ window_theme->bg = ltk_create_xcolor(bg->valuestring);
+
+ return window_theme;
+}
+
+void ltk_redraw_window(LtkWindow *window)
+{
+ LtkWidget *ptr;
+ if (!window)
+ {
+ return;
+ }
+ if (!window->root_widget)
+ {
+ return;
+ }
+ ptr = window->root_widget;
+ ptr->draw_function(ptr);
+}
+
+LtkWindow *ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int h)
+{
+ LtkWindow *window = malloc(sizeof(LtkWindow));
+ if (!window) ltk_fatal("Not enough memory left for window!\n");
+ LtkWindowTheme *wtheme = ltk_global->theme->window; /* For convenience */
+ Display *display = ltk_global->display; /* For convenience */
+ window->xwindow = XCreateSimpleWindow(display, DefaultRootWindow(display), x, y, w, h, wtheme->border_width, wtheme->fg.pixel, wtheme->bg.pixel);
+ window->gc = XCreateGC(display, window->xwindow, 0, 0);
+ XSetForeground(display, window->gc, wtheme->fg.pixel);
+ XSetBackground(display, window->gc, wtheme->bg.pixel);
+ XSetStandardProperties(display, window->xwindow, title, NULL, None, NULL, 0, NULL);
+ window->root_widget = NULL;
+
+ window->key_func = <k_window_key_event;
+ window->mouse_func = <k_window_mouse_event;
+ window->other_func = <k_window_other_event;
+
+ window->rect.w = 0;
+ window->rect.h = 0;
+ window->rect.x = 0;
+ window->rect.y = 0;
+
+ XClearWindow(display, window->xwindow);
+ XMapRaised(display, window->xwindow);
+ XSelectInput(display, window->xwindow, ExposureMask|KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|StructureNotifyMask|PointerMotionMask);
+
+ HASH_ADD_INT(ltk_global->window_hash, xwindow, window);
+
+ return window;
+}
+
+void ltk_destroy_window(LtkWindow *window)
+{
+ LtkWidget *ptr = window->root_widget;
+ if (ptr)
+ {
+ ptr->destroy_function(ptr);
+ }
+ free(window);
+}
+
+void ltk_window_key_event(void *widget, XEvent event)
+{
+ LtkWindow *window = widget;
+ LtkWidget *ptr = window->root_widget;
+ if (ptr && ptr->key_func)
+ {
+ ptr->key_func(ptr, event);
+ }
+}
+
+void ltk_window_mouse_event(void *widget, XEvent event)
+{
+ LtkWindow *window = widget;
+ LtkWidget *ptr = window->root_widget;
+ if (ptr && ptr->mouse_func)
+ {
+ ptr->mouse_func(ptr, event);
+ }
+}
+
+void ltk_window_other_event(void *widget, XEvent event)
+{
+ LtkWindow *window = widget;
+ LtkWidget *ptr = window->root_widget;
+ if (event.type == ConfigureNotify)
+ {
+ unsigned int w, h;
+ w = event.xconfigure.width;
+ h = event.xconfigure.height;
+ if (ptr && ptr->update_function && (window->rect.w != w || window->rect.h != h))
+ {
+ window->rect.w = w;
+ window->rect.h = h;
+ ptr->rect.w = w;
+ ptr->rect.h = h;
+ ptr->update_function(ptr);
+ ltk_redraw_window(window);
+ }
+ }
+ if (event.type == Expose && event.xexpose.count == 0)
+ {
+ ltk_redraw_window(window);
+ }
+}
+
+/*
+void ltk_resize_window(Uint32 id, int w, int h)
+{
+ LtkWindow *window;
+ LtkWidget *ptr;
+ HASH_FIND_INT(ltk_window_hash, &id, window);
+ if (!window)
+ {
+ return;
+ }
+ ptr = window->root_widget;
+ if (!ptr)
+ {
+ return;
+ }
+ ptr->rect.w = w;
+ ptr->rect.h = h;
+ ptr->update_function(ptr);
+}
+
+void ltk_window_set_root_widget(LtkWindow *window, void *root_widget)
+{
+ window->root_widget = root_widget;
+}
+*/
diff --git a/window.h b/window.h
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the Lumidify ToolKit (LTK)
+ * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _LTK_WINDOW_H_
+#define _LTK_WINDOW_H_
+
+#include <X11/Xlib.h>
+#include "event.h"
+
+typedef struct LtkWindow
+{
+ Window xwindow;
+ GC gc;
+ void *root_widget;
+ LTK_EVENT_FUNC key_func; /* Called on any keyboard event */
+ LTK_EVENT_FUNC mouse_func; /* Called on any mouse event */
+ LTK_EVENT_FUNC other_func; /* Called on any other event */
+ LtkRect rect;
+ UT_hash_handle hh;
+} LtkWindow;
+
+typedef struct LtkWindowTheme
+{
+ int border_width;
+ XColor fg;
+ XColor bg;
+} LtkWindowTheme;
+
+LtkWindow *ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int h);
+void ltk_redraw_window(LtkWindow *window);
+void ltk_destroy_window(LtkWindow *window);
+void ltk_window_key_event(void *widget, XEvent event);
+void ltk_window_mouse_event(void *widget, XEvent event);
+void ltk_window_other_event(void *widget, XEvent event);
+
+#endif