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