URI: 
       tMove some functions from ltkd.c to widget.c and rect.c - ltk - Socket-based GUI for X11 (WIP)
  HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 4f891285a531b95894eb8ecb397d91f84285bfaa
   DIR parent 7d8235cc4e3c173b19461ff5eaf5991a43f15994
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Mon, 22 Feb 2021 22:05:56 +0100
       
       Move some functions from ltkd.c to widget.c and rect.c
       
       Diffstat:
         M Makefile                            |       4 ++++
         M src/box.c                           |       2 ++
         M src/box.h                           |       2 +-
         M src/button.c                        |       2 ++
         M src/button.h                        |       2 +-
         M src/draw.c                          |       2 ++
         M src/draw.h                          |       2 +-
         M src/grid.c                          |       2 ++
         M src/grid.h                          |       2 +-
         M src/label.c                         |       2 ++
         M src/label.h                         |       2 +-
         M src/ltk.h                           |      96 +------------------------------
         M src/ltkd.c                          |     239 +------------------------------
         A src/rect.c                          |      33 +++++++++++++++++++++++++++++++
         A src/rect.h                          |      15 +++++++++++++++
         M src/scrollbar.c                     |       2 ++
         M src/scrollbar.h                     |       2 +-
         M src/text_pango.c                    |       2 ++
         M src/text_stb.c                      |       2 ++
         A src/widget.c                        |     221 +++++++++++++++++++++++++++++++
         A src/widget.h                        |      95 ++++++++++++++++++++++++++++++
       
       21 files changed, 396 insertions(+), 335 deletions(-)
       ---
   DIR diff --git a/Makefile b/Makefile
       t@@ -37,6 +37,8 @@ OBJ = \
                src/util.o \
                src/memory.o \
                src/color.o \
       +        src/rect.o \
       +        src/widget.o \
                src/ltkd.o \
                src/ini.o \
                src/grid.o \
       t@@ -59,6 +61,8 @@ HDR = \
                src/ini.h \
                src/khash.h \
                src/label.h \
       +        src/rect.h \
       +        src/widget.h \
                src/ltk.h \
                src/memory.h \
                src/scrollbar.h \
   DIR diff --git a/src/box.c b/src/box.c
       t@@ -32,6 +32,8 @@
        
        #include "memory.h"
        #include "color.h"
       +#include "rect.h"
       +#include "widget.h"
        #include "ltk.h"
        #include "util.h"
        #include "scrollbar.h"
   DIR diff --git a/src/box.h b/src/box.h
       t@@ -24,7 +24,7 @@
        #ifndef _LTK_BOX_H_
        #define _LTK_BOX_H_
        
       -/* Requires the following includes: "scrollbar.h" "ltk.h" */
       +/* Requires the following includes: "scrollbar.h", "rect.h", "widget.h", "ltk.h" */
        
        typedef struct {
                ltk_widget widget;
   DIR diff --git a/src/button.c b/src/button.c
       t@@ -32,6 +32,8 @@
        
        #include "memory.h"
        #include "color.h"
       +#include "rect.h"
       +#include "widget.h"
        #include "ltk.h"
        #include "util.h"
        #include "text.h"
   DIR diff --git a/src/button.h b/src/button.h
       t@@ -24,7 +24,7 @@
        #ifndef _LTK_BUTTON_H_
        #define _LTK_BUTTON_H_
        
       -/* Requires the following includes: <X11/Xlib.h>, "ltk.h", "color.h", "text.h" */
       +/* Requires the following includes: <X11/Xlib.h>, "rect.h", "widget.h", "ltk.h", "color.h", "text.h" */
        
        typedef struct {
                ltk_widget widget;
   DIR diff --git a/src/draw.c b/src/draw.c
       t@@ -32,6 +32,8 @@
        
        #include "memory.h"
        #include "color.h"
       +#include "rect.h"
       +#include "widget.h"
        #include "ltk.h"
        #include "util.h"
        #include "draw.h"
   DIR diff --git a/src/draw.h b/src/draw.h
       t@@ -24,7 +24,7 @@
        #ifndef _LTK_DRAW_H_
        #define _LTK_DRAW_H_
        
       -/* Requires the following includes: <X11/Xlib.h>, "ltk.h" */
       +/* Requires the following includes: <X11/Xlib.h>, "rect.h", "widget.h", "ltk.h" */
        
        typedef struct {
                ltk_widget widget;
   DIR diff --git a/src/grid.c b/src/grid.c
       t@@ -39,6 +39,8 @@
        
        #include "memory.h"
        #include "color.h"
       +#include "rect.h"
       +#include "widget.h"
        #include "ltk.h"
        #include "util.h"
        #include "grid.h"
   DIR diff --git a/src/grid.h b/src/grid.h
       t@@ -24,7 +24,7 @@
        #ifndef _LTK_GRID_H_
        #define _LTK_GRID_H_
        
       -/* Requires the following includes: "ltk.h" */
       +/* Requires the following includes: "rect.h", "widget.h", "ltk.h" */
        
        /*
         * Struct to represent a grid widget.
   DIR diff --git a/src/label.c b/src/label.c
       t@@ -32,6 +32,8 @@
        
        #include "memory.h"
        #include "color.h"
       +#include "rect.h"
       +#include "widget.h"
        #include "ltk.h"
        #include "util.h"
        #include "text.h"
   DIR diff --git a/src/label.h b/src/label.h
       t@@ -24,7 +24,7 @@
        #ifndef _LTK_LABEL_H_
        #define _LTK_LABEL_H_
        
       -/* Requires the following includes: <X11/Xlib.h>, "ltk.h", "color.h", "text.h" */
       +/* Requires the following includes: <X11/Xlib.h>, "rect.h", "widget.h", "ltk.h", "color.h", "text.h" */
        
        typedef struct {
                ltk_widget widget;
   DIR diff --git a/src/ltk.h b/src/ltk.h
       t@@ -24,7 +24,7 @@
        #ifndef _LTK_H_
        #define _LTK_H_
        
       -/* Requires the following includes: <X11/Xlib.h>, <X11/Xutil.h>, <stdarg.h> */
       +/* Requires the following includes: <X11/Xlib.h>, <X11/Xutil.h>, "widget.h" */
        
        typedef enum {
                LTK_EVENT_RESIZE = 1 << 0,
       t@@ -33,88 +33,6 @@ typedef enum {
        } ltk_event_type;
        
        typedef struct {
       -        int x;
       -        int y;
       -        int w;
       -        int h;
       -} ltk_rect;
       -
       -typedef enum {
       -        LTK_STICKY_LEFT = 1 << 0,
       -        LTK_STICKY_RIGHT = 1 << 1,
       -        LTK_STICKY_TOP = 1 << 2,
       -        LTK_STICKY_BOTTOM = 1 << 3
       -} ltk_sticky_mask;
       -
       -typedef enum {
       -        LTK_VERTICAL,
       -        LTK_HORIZONTAL
       -} ltk_orientation;
       -
       -typedef enum {
       -        LTK_NORMAL,
       -        LTK_PRESSED,
       -        LTK_ACTIVE,
       -        LTK_DISABLED
       -} ltk_widget_state;
       -
       -typedef enum {
       -        /* for e.g. scrollbar, which can't be directly accessed by users */
       -        LTK_UNKNOWN,
       -        LTK_GRID,
       -        LTK_BUTTON,
       -        LTK_DRAW,
       -        LTK_LABEL,
       -        LTK_WIDGET,
       -        LTK_BOX
       -} ltk_widget_type;
       -
       -struct ltk_window;
       -
       -struct ltk_widget_vtable;
       -
       -typedef struct ltk_widget {
       -        struct ltk_window *window;
       -        struct ltk_widget *active_widget;
       -        struct ltk_widget *parent;
       -        char *id;
       -
       -        struct ltk_widget_vtable *vtable;
       -
       -        ltk_rect rect;
       -        unsigned int ideal_w;
       -        unsigned int ideal_h;
       -
       -        ltk_widget_type type;
       -        ltk_widget_state state;
       -        unsigned int sticky;
       -        unsigned short row;
       -        unsigned short column;
       -        unsigned short row_span;
       -        unsigned short column_span;
       -        unsigned char needs_redraw;
       -} ltk_widget;
       -
       -struct ltk_widget_vtable {
       -        void (*key_press) (struct ltk_widget *, XEvent);
       -        void (*key_release) (struct ltk_widget *, XEvent);
       -        int (*mouse_press) (struct ltk_widget *, XEvent);
       -        int (*mouse_release) (struct ltk_widget *, XEvent);
       -        int (*mouse_wheel) (struct ltk_widget *, XEvent);
       -        int (*motion_notify) (struct ltk_widget *, XEvent);
       -        void (*mouse_leave) (struct ltk_widget *, XEvent);
       -        void (*mouse_enter) (struct ltk_widget *, XEvent);
       -
       -        void (*resize) (struct ltk_widget *);
       -        void (*draw) (struct ltk_widget *, ltk_rect);
       -        void (*change_state) (struct ltk_widget *);
       -        void (*destroy) (struct ltk_widget *, int);
       -
       -        void (*child_size_change) (struct ltk_widget *, struct ltk_widget *);
       -        int (*remove_child) (struct ltk_window *, struct ltk_widget *, struct ltk_widget *, char **);
       -};
       -
       -typedef struct {
                int border_width;
                uint16_t font_size;
                char *font;
       t@@ -158,23 +76,11 @@ typedef struct ltk_window {
                struct ltk_event_queue *last_event;
        } ltk_window;
        
       -ltk_rect ltk_rect_intersect(ltk_rect r1, ltk_rect r2);
       -ltk_rect ltk_rect_union(ltk_rect r1, ltk_rect r2);
        void ltk_window_invalidate_rect(ltk_window *window, ltk_rect rect);
        int ltk_create_xcolor(ltk_window *window, const char *hex, XColor *col);
        void ltk_queue_event(ltk_window *window, ltk_event_type type, const char *id, const char *data);
       -int ltk_collide_rect(ltk_rect rect, int x, int y);
        void ltk_window_set_active_widget(ltk_window *window, ltk_widget *widget);
        void ltk_window_set_pressed_widget(ltk_window *window, ltk_widget *widget);
       -void ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window *window,
       -    struct ltk_widget_vtable *vtable, unsigned int needs_redraw, ltk_widget_type type);
       -void ltk_widget_mouse_press_event(ltk_widget *widget, XEvent event);
       -void ltk_widget_mouse_release_event(ltk_widget *widget, XEvent event);
       -void ltk_widget_motion_notify_event(ltk_widget *widget, XEvent event);
       -int ltk_widget_id_free(const char *id);
       -ltk_widget *ltk_get_widget(const char *id, ltk_widget_type type, char **errstr);
       -void ltk_set_widget(ltk_widget *widget, const char *id);
       -void ltk_remove_widget(const char *id);
        void ltk_quit(ltk_window *window);
        
        #endif
   DIR diff --git a/src/ltkd.c b/src/ltkd.c
       t@@ -1,5 +1,5 @@
        /* FIXME: backslashes should be parsed properly! */
       -/* FIXME Figure out how to properly print window id */
       +/* FIXME: Figure out how to properly print window id */
        /* FIXME: PROPERLY CLEANUP ALL THEMES */
        /* FIXME: error checking in tokenizer (is this necessary?) */
        /* FIXME: parsing doesn't work properly with bs? */
       t@@ -51,6 +51,8 @@
        
        #include "memory.h"
        #include "color.h"
       +#include "rect.h"
       +#include "widget.h"
        #include "ltk.h"
        #include "util.h"
        #include "text.h"
       t@@ -90,16 +92,12 @@ static struct ltk_sock_info {
                struct token_list tokens;  /* current tokens */
        } sockets[MAX_SOCK_CONNS];
        
       -KHASH_MAP_INIT_STR(widget, ltk_widget *)
       -static khash_t(widget) *widget_hash = NULL;
       -
        static int ltk_mainloop(ltk_window *window);
        static char *get_sock_path(char *basedir, Window id);
        static FILE *open_log(char *dir);
        static void daemonize(void);
        static ltk_window *ltk_create_window(const char *title, int x, int y,
            unsigned int w, unsigned int h);
       -static void ltk_destroy_widget_hash(void);
        static void ltk_destroy_window(ltk_window *window);
        static void ltk_redraw_window(ltk_window *window);
        static void ltk_window_other_event(ltk_window *window, XEvent event);
       t@@ -116,7 +114,6 @@ static void process_commands(ltk_window *window, struct ltk_sock_info *sock);
        static int add_client(int fd);
        static int listen_sock(const char *sock_path);
        static int accept_sock(int listenfd);
       -static int ltk_widget_destroy(ltk_window *window, char **tokens, size_t num_tokens, char **errstr);
        
        static short maxsocket = -1;
        static short running = 1;
       t@@ -147,8 +144,7 @@ int main(int argc, char *argv[]) {
                ltk_logfile = open_log(ltk_dir);
                if (!ltk_logfile) ltk_fatal_errno("Unable to open log file.\n");
        
       -        widget_hash = kh_init(widget);
       -        if (!widget_hash) ltk_fatal_errno("Unable to initialize widget hash table.\n");
       +        ltk_widgets_init();
        
                /* FIXME: set window size properly - I only run it in a tiling WM
                   anyways, so it doesn't matter, but still... */
       t@@ -386,8 +382,7 @@ ltk_cleanup(void) {
                                ltk_free(sockets[i].tokens.tokens);
                }
        
       -        if (widget_hash)
       -                ltk_destroy_widget_hash();
       +        ltk_widgets_cleanup();
                if (main_window)
                        ltk_destroy_window(main_window);
        }
       t@@ -438,32 +433,6 @@ ltk_set_root_widget_cmd(
                return 0;
        }
        
       -/* FIXME */
       -#undef MAX
       -#undef MIN
       -#define MAX(a, b) ((a) > (b) ? (a) : (b))
       -#define MIN(a, b) ((a) < (b) ? (a) : (b))
       -
       -ltk_rect
       -ltk_rect_intersect(ltk_rect r1, ltk_rect r2) {
       -        ltk_rect i;
       -        i.x = MAX(r1.x, r2.x);
       -        i.y = MAX(r1.y, r2.y);
       -        i.w = MIN(r1.x + r1.w, r2.x + r2.w) - i.x;
       -        i.h = MIN(r1.y + r1.h, r2.y + r2.h) - i.y;
       -        return i;
       -}
       -
       -ltk_rect
       -ltk_rect_union(ltk_rect r1, ltk_rect r2) {
       -        ltk_rect u;
       -        u.x = MIN(r1.x, r2.x);
       -        u.y = MIN(r1.y, r2.y);
       -        u.w = MAX(r1.x + r1.w, r2.x + r2.w) - u.x;
       -        u.h = MAX(r1.y + r1.h, r2.y + r2.h) - u.y;
       -        return u;
       -}
       -
        void
        ltk_window_invalidate_rect(ltk_window *window, ltk_rect rect) {
                if (window->dirty_rect.w == 0 && window->dirty_rect.h == 0)
       t@@ -611,19 +580,6 @@ ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int 
        }
        
        static void
       -ltk_destroy_widget_hash(void) {
       -        khint_t k;
       -        ltk_widget *ptr;
       -        for (k = kh_begin(widget_hash); k != kh_end(widget_hash); k++) {
       -                if (kh_exist(widget_hash, k)) {
       -                        ptr = kh_value(widget_hash, k);
       -                        ptr->vtable->destroy(ptr, 1);
       -                }
       -        }
       -        kh_destroy(widget, widget_hash);
       -}
       -
       -static void
        ltk_destroy_window(ltk_window *window) {
                XDestroyWindow(window->dpy, window->xwindow);
                ltk_cleanup_text();
       t@@ -691,52 +647,6 @@ ltk_load_theme(ltk_window *window, const char *path) {
                }
        }
        
       -int
       -ltk_collide_rect(ltk_rect rect, int x, int y) {
       -        return (rect.x <= x && (rect.x + rect.w) >= x && rect.y <= y
       -                && (rect.y + rect.h) >= y);
       -}
       -
       -void
       -ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window *window,
       -    struct ltk_widget_vtable *vtable, unsigned int needs_redraw, ltk_widget_type type) {
       -        if (id)
       -                widget->id = ltk_strdup(id);
       -        else
       -                widget->id = NULL;
       -        widget->window = window;
       -        widget->active_widget = NULL;
       -        widget->parent = NULL;
       -        widget->type = type;
       -
       -        /* FIXME: possibly check that draw and destroy aren't NULL */
       -        widget->vtable = vtable;
       -
       -        widget->needs_redraw = needs_redraw;
       -        widget->state = LTK_NORMAL;
       -        widget->row = 0;
       -        widget->rect.x = 0;
       -        widget->rect.y = 0;
       -        widget->rect.w = 0;
       -        widget->rect.h = 0;
       -        widget->ideal_w = 0;
       -        widget->ideal_h = 0;
       -
       -        widget->row = 0;
       -        widget->column = 0;
       -        widget->row_span = 0;
       -        widget->column_span = 0;
       -        widget->sticky = 0;
       -}
       -
       -static void
       -ltk_widget_change_state(ltk_widget *widget) {
       -        if (widget->vtable->change_state)
       -                widget->vtable->change_state(widget);
       -        if (widget->needs_redraw)
       -                ltk_window_invalidate_rect(widget->window, widget->rect);
       -}
       -
        void
        ltk_window_set_active_widget(ltk_window *window, ltk_widget *widget) {
                if (window->active_widget == widget)
       t@@ -772,59 +682,6 @@ ltk_window_set_pressed_widget(ltk_window *window, ltk_widget *widget) {
                }
        }
        
       -void
       -ltk_widget_mouse_press_event(ltk_widget *widget, XEvent event) {
       -        if (!widget || widget->state == LTK_DISABLED)
       -                return;
       -        int default_handler = 1;
       -        if (widget->vtable->mouse_press)
       -                default_handler = widget->vtable->mouse_press(widget, event);
       -        if (default_handler) {
       -                if (event.xbutton.button == 1)
       -                        ltk_window_set_pressed_widget(widget->window, widget);
       -        }
       -}
       -
       -void
       -ltk_widget_mouse_release_event(ltk_widget *widget, XEvent event) {
       -        if (!widget || widget->state == LTK_DISABLED)
       -                return;
       -        int default_handler = 1;
       -        if (widget->vtable->mouse_release)
       -                default_handler = widget->vtable->mouse_release(widget, event);
       -        if (default_handler)
       -                ltk_window_set_pressed_widget(widget->window, NULL);
       -}
       -
       -/* FIXME: ONLY SET ACTIVE WIDGET AT BOTTOM OF HIERARCHY
       -   -> Don't first set parent as active widget and then child */
       -void
       -ltk_widget_motion_notify_event(ltk_widget *widget, XEvent event) {
       -        if (!widget || widget->state == LTK_DISABLED)
       -                return;
       -        /* FIXME: THIS WHOLE STATE HANDLING IS STILL PARTIALLY BROKEN */
       -        /*
       -        if (((widget->state == LTK_NORMAL) ||
       -             (widget->state == LTK_ACTIVE && widget->window->active_widget != widget)) &&
       -             !widget->window->pressed_widget) {
       -                widget->state = LTK_ACTIVE;
       -                if (widget->vtable->change_state)
       -                        widget->change_statevtable->(widget);
       -                if (widget->vtable->mouse_enter)
       -                        widget->mouse_entervtable->(widget, event);
       -                ltk_window_remove_active_widget(widget->window);
       -                ltk_window_set_active_widget(widget);
       -        }
       -        */
       -        int default_handler = 1;
       -        if (widget->window->pressed_widget && widget->window->pressed_widget->vtable->motion_notify)
       -                default_handler = widget->window->pressed_widget->vtable->motion_notify(widget->window->pressed_widget, event);
       -        else if (widget->vtable->motion_notify)
       -                default_handler = widget->vtable->motion_notify(widget, event);
       -        if (default_handler)
       -                ltk_window_set_active_widget(widget->window, widget);
       -}
       -
        static void
        ltk_handle_event(ltk_window *window, XEvent event) {
                ltk_widget *root_widget = window->root_widget;
       t@@ -851,53 +708,6 @@ ltk_handle_event(ltk_window *window, XEvent event) {
                }
        }
        
       -int
       -ltk_widget_id_free(const char *id) {
       -        khint_t k;
       -        k = kh_get(widget, widget_hash, id);
       -        if (k != kh_end(widget_hash)) {
       -                return 0;
       -        }
       -        return 1;
       -}
       -
       -ltk_widget *
       -ltk_get_widget(const char *id, ltk_widget_type type, char **errstr) {
       -        khint_t k;
       -        ltk_widget *widget;
       -        k = kh_get(widget, widget_hash, id);
       -        if (k == kh_end(widget_hash)) {
       -                *errstr = "Widget with given ID doesn't exist.\n";
       -                return NULL;
       -        }
       -        widget = kh_value(widget_hash, k);
       -        if (type != LTK_WIDGET && widget->type != type) {
       -                *errstr = "Widget with given ID has wrong type.\n";
       -                return NULL;
       -        }
       -        return widget;
       -}
       -
       -void
       -ltk_set_widget(ltk_widget *widget, const char *id) {
       -        int ret;
       -        khint_t k;
       -        /* apparently, khash requires the string to stay accessible */
       -        /* FIXME: How is this freed? */
       -        char *tmp = ltk_strdup(id);
       -        k = kh_put(widget, widget_hash, tmp, &ret);
       -        kh_value(widget_hash, k) = widget;
       -}
       -
       -void
       -ltk_remove_widget(const char *id) {
       -        khint_t k;
       -        k = kh_get(widget, widget_hash, id);
       -        if (k != kh_end(widget_hash)) {
       -                kh_del(widget, widget_hash, k);
       -        }
       -}
       -
        /* Push a token onto `token_list`, resizing the buffer if necessary.
           Returns -1 on error, 0 otherwise.
           Note: The token is not copied, it is only added directly. */
       t@@ -1178,42 +988,3 @@ process_commands(ltk_window *window, struct ltk_sock_info *sock) {
                        sock->read_cur = 0;
                }
        }
       -
       -static int
       -ltk_widget_destroy(
       -    ltk_window *window,
       -    char **tokens,
       -    size_t num_tokens,
       -    char **errstr) {
       -        int err = 0, shallow = 1;
       -        if (num_tokens != 2 && num_tokens != 3) {
       -                *errstr = "Invalid number of arguments.\n";
       -                return 1;
       -        }
       -        if (num_tokens == 3) {
       -                if (strcmp(tokens[2], "deep") == 0) {
       -                        shallow = 0;
       -                } else if (strcmp(tokens[2], "shallow") == 0) {
       -                        shallow = 1;
       -                } else {
       -                        *errstr = "Invalid argument: must be 'shallow' or 'deep'.\n";
       -                        return 1;
       -                }
       -        }
       -        ltk_widget *widget = ltk_get_widget(tokens[1], LTK_WIDGET, errstr);
       -        if (!widget) {
       -                *errstr = "Invalid widget ID.\n";
       -                return 1;
       -        }
       -        ltk_remove_widget(tokens[1]);
       -        /* widget->parent->remove_child should never be NULL because of the fact that
       -           the widget is set as parent, but let's just check anyways... */
       -        if (widget->parent && widget->parent->vtable->remove_child) {
       -                err = widget->parent->vtable->remove_child(
       -                    window, widget, widget->parent, errstr
       -                );
       -        }
       -        widget->vtable->destroy(widget, shallow);
       -
       -        return err;
       -}
   DIR diff --git a/src/rect.c b/src/rect.c
       t@@ -0,0 +1,33 @@
       +#include "rect.h"
       +
       +/* FIXME */
       +#undef MAX
       +#undef MIN
       +#define MAX(a, b) ((a) > (b) ? (a) : (b))
       +#define MIN(a, b) ((a) < (b) ? (a) : (b))
       +
       +ltk_rect
       +ltk_rect_intersect(ltk_rect r1, ltk_rect r2) {
       +        ltk_rect i;
       +        i.x = MAX(r1.x, r2.x);
       +        i.y = MAX(r1.y, r2.y);
       +        i.w = MIN(r1.x + r1.w, r2.x + r2.w) - i.x;
       +        i.h = MIN(r1.y + r1.h, r2.y + r2.h) - i.y;
       +        return i;
       +}
       +
       +ltk_rect
       +ltk_rect_union(ltk_rect r1, ltk_rect r2) {
       +        ltk_rect u;
       +        u.x = MIN(r1.x, r2.x);
       +        u.y = MIN(r1.y, r2.y);
       +        u.w = MAX(r1.x + r1.w, r2.x + r2.w) - u.x;
       +        u.h = MAX(r1.y + r1.h, r2.y + r2.h) - u.y;
       +        return u;
       +}
       +
       +int
       +ltk_collide_rect(ltk_rect rect, int x, int y) {
       +        return (rect.x <= x && (rect.x + rect.w) >= x && rect.y <= y
       +                && (rect.y + rect.h) >= y);
       +}
   DIR diff --git a/src/rect.h b/src/rect.h
       t@@ -0,0 +1,15 @@
       +#ifndef _LTK_RECT_H_
       +#define _LTK_RECT_H_
       +
       +typedef struct {
       +        int x;
       +        int y;
       +        int w;
       +        int h;
       +} ltk_rect;
       +
       +ltk_rect ltk_rect_intersect(ltk_rect r1, ltk_rect r2);
       +ltk_rect ltk_rect_union(ltk_rect r1, ltk_rect r2);
       +int ltk_collide_rect(ltk_rect rect, int x, int y);
       +
       +#endif /* _LTK_RECT_H_ */
   DIR diff --git a/src/scrollbar.c b/src/scrollbar.c
       t@@ -32,6 +32,8 @@
        
        #include "memory.h"
        #include "color.h"
       +#include "rect.h"
       +#include "widget.h"
        #include "ltk.h"
        #include "util.h"
        #include "scrollbar.h"
   DIR diff --git a/src/scrollbar.h b/src/scrollbar.h
       t@@ -24,7 +24,7 @@
        #ifndef _LTK_SCROLLBAR_H_
        #define _LTK_SCROLLBAR_H_
        
       -/* Requires: "ltk.h" */
       +/* Requires: "rect.h", "widget.h", "ltk.h" */
        
        typedef struct {
                ltk_widget widget;
   DIR diff --git a/src/text_pango.c b/src/text_pango.c
       t@@ -12,6 +12,8 @@
        
        #include "memory.h"
        #include "color.h"
       +#include "rect.h"
       +#include "widget.h"
        #include "ltk.h"
        #include "util.h"
        #include "text.h"
   DIR diff --git a/src/text_stb.c b/src/text_stb.c
       t@@ -37,6 +37,8 @@
        
        #include "memory.h"
        #include "color.h"
       +#include "rect.h"
       +#include "widget.h"
        #include "ltk.h"
        #include "util.h"
        #include "text.h"
   DIR diff --git a/src/widget.c b/src/widget.c
       t@@ -0,0 +1,221 @@
       +#include <stdarg.h>
       +
       +#include <X11/Xlib.h>
       +#include <X11/Xutil.h>
       +
       +#include "rect.h"
       +#include "widget.h"
       +#include "color.h"
       +#include "ltk.h"
       +#include "memory.h"
       +#include "util.h"
       +#include "khash.h"
       +
       +static void ltk_destroy_widget_hash(void);
       +
       +KHASH_MAP_INIT_STR(widget, ltk_widget *)
       +static khash_t(widget) *widget_hash = NULL;
       +
       +static void
       +ltk_destroy_widget_hash(void) {
       +        khint_t k;
       +        ltk_widget *ptr;
       +        for (k = kh_begin(widget_hash); k != kh_end(widget_hash); k++) {
       +                if (kh_exist(widget_hash, k)) {
       +                        ptr = kh_value(widget_hash, k);
       +                        ptr->vtable->destroy(ptr, 1);
       +                }
       +        }
       +        kh_destroy(widget, widget_hash);
       +}
       +
       +void
       +ltk_widgets_init() {
       +        widget_hash = kh_init(widget);
       +        if (!widget_hash) ltk_fatal_errno("Unable to initialize widget hash table.\n");
       +}
       +
       +void
       +ltk_widgets_cleanup() {
       +        if (widget_hash)
       +                ltk_destroy_widget_hash();
       +}
       +
       +void
       +ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window *window,
       +    struct ltk_widget_vtable *vtable, unsigned int needs_redraw, ltk_widget_type type) {
       +        if (id)
       +                widget->id = ltk_strdup(id);
       +        else
       +                widget->id = NULL;
       +        widget->window = window;
       +        widget->active_widget = NULL;
       +        widget->parent = NULL;
       +        widget->type = type;
       +
       +        /* FIXME: possibly check that draw and destroy aren't NULL */
       +        widget->vtable = vtable;
       +
       +        widget->needs_redraw = needs_redraw;
       +        widget->state = LTK_NORMAL;
       +        widget->row = 0;
       +        widget->rect.x = 0;
       +        widget->rect.y = 0;
       +        widget->rect.w = 0;
       +        widget->rect.h = 0;
       +        widget->ideal_w = 0;
       +        widget->ideal_h = 0;
       +
       +        widget->row = 0;
       +        widget->column = 0;
       +        widget->row_span = 0;
       +        widget->column_span = 0;
       +        widget->sticky = 0;
       +}
       +
       +void
       +ltk_widget_change_state(ltk_widget *widget) {
       +        if (widget->vtable->change_state)
       +                widget->vtable->change_state(widget);
       +        if (widget->needs_redraw)
       +                ltk_window_invalidate_rect(widget->window, widget->rect);
       +}
       +
       +void
       +ltk_widget_mouse_press_event(ltk_widget *widget, XEvent event) {
       +        if (!widget || widget->state == LTK_DISABLED)
       +                return;
       +        int default_handler = 1;
       +        if (widget->vtable->mouse_press)
       +                default_handler = widget->vtable->mouse_press(widget, event);
       +        if (default_handler) {
       +                if (event.xbutton.button == 1)
       +                        ltk_window_set_pressed_widget(widget->window, widget);
       +        }
       +}
       +
       +void
       +ltk_widget_mouse_release_event(ltk_widget *widget, XEvent event) {
       +        if (!widget || widget->state == LTK_DISABLED)
       +                return;
       +        int default_handler = 1;
       +        if (widget->vtable->mouse_release)
       +                default_handler = widget->vtable->mouse_release(widget, event);
       +        if (default_handler)
       +                ltk_window_set_pressed_widget(widget->window, NULL);
       +}
       +
       +/* FIXME: ONLY SET ACTIVE WIDGET AT BOTTOM OF HIERARCHY
       +   -> Don't first set parent as active widget and then child */
       +void
       +ltk_widget_motion_notify_event(ltk_widget *widget, XEvent event) {
       +        if (!widget || widget->state == LTK_DISABLED)
       +                return;
       +        /* FIXME: THIS WHOLE STATE HANDLING IS STILL PARTIALLY BROKEN */
       +        /*
       +        if (((widget->state == LTK_NORMAL) ||
       +             (widget->state == LTK_ACTIVE && widget->window->active_widget != widget)) &&
       +             !widget->window->pressed_widget) {
       +                widget->state = LTK_ACTIVE;
       +                if (widget->vtable->change_state)
       +                        widget->vtable->change_state(widget);
       +                if (widget->vtable->mouse_enter)
       +                        widget->vtable->mouse_enter(widget, event);
       +                ltk_window_remove_active_widget(widget->window);
       +                ltk_window_set_active_widget(widget);
       +        }
       +        */
       +        int default_handler = 1;
       +        if (widget->window->pressed_widget && widget->window->pressed_widget->vtable->motion_notify)
       +                default_handler = widget->window->pressed_widget->vtable->motion_notify(widget->window->pressed_widget, event);
       +        else if (widget->vtable->motion_notify)
       +                default_handler = widget->vtable->motion_notify(widget, event);
       +        if (default_handler)
       +                ltk_window_set_active_widget(widget->window, widget);
       +}
       +
       +int
       +ltk_widget_id_free(const char *id) {
       +        khint_t k;
       +        k = kh_get(widget, widget_hash, id);
       +        if (k != kh_end(widget_hash)) {
       +                return 0;
       +        }
       +        return 1;
       +}
       +
       +ltk_widget *
       +ltk_get_widget(const char *id, ltk_widget_type type, char **errstr) {
       +        khint_t k;
       +        ltk_widget *widget;
       +        k = kh_get(widget, widget_hash, id);
       +        if (k == kh_end(widget_hash)) {
       +                *errstr = "Widget with given ID doesn't exist.\n";
       +                return NULL;
       +        }
       +        widget = kh_value(widget_hash, k);
       +        if (type != LTK_WIDGET && widget->type != type) {
       +                *errstr = "Widget with given ID has wrong type.\n";
       +                return NULL;
       +        }
       +        return widget;
       +}
       +
       +void
       +ltk_set_widget(ltk_widget *widget, const char *id) {
       +        int ret;
       +        khint_t k;
       +        /* apparently, khash requires the string to stay accessible */
       +        /* FIXME: How is this freed? */
       +        char *tmp = ltk_strdup(id);
       +        k = kh_put(widget, widget_hash, tmp, &ret);
       +        kh_value(widget_hash, k) = widget;
       +}
       +
       +void
       +ltk_remove_widget(const char *id) {
       +        khint_t k;
       +        k = kh_get(widget, widget_hash, id);
       +        if (k != kh_end(widget_hash)) {
       +                kh_del(widget, widget_hash, k);
       +        }
       +}
       +
       +int
       +ltk_widget_destroy(
       +    ltk_window *window,
       +    char **tokens,
       +    size_t num_tokens,
       +    char **errstr) {
       +        int err = 0, shallow = 1;
       +        if (num_tokens != 2 && num_tokens != 3) {
       +                *errstr = "Invalid number of arguments.\n";
       +                return 1;
       +        }
       +        if (num_tokens == 3) {
       +                if (strcmp(tokens[2], "deep") == 0) {
       +                        shallow = 0;
       +                } else if (strcmp(tokens[2], "shallow") == 0) {
       +                        shallow = 1;
       +                } else {
       +                        *errstr = "Invalid argument: must be 'shallow' or 'deep'.\n";
       +                        return 1;
       +                }
       +        }
       +        ltk_widget *widget = ltk_get_widget(tokens[1], LTK_WIDGET, errstr);
       +        if (!widget) {
       +                *errstr = "Invalid widget ID.\n";
       +                return 1;
       +        }
       +        ltk_remove_widget(tokens[1]);
       +        /* widget->parent->remove_child should never be NULL because of the fact that
       +           the widget is set as parent, but let's just check anyways... */
       +        if (widget->parent && widget->parent->vtable->remove_child) {
       +                err = widget->parent->vtable->remove_child(
       +                    window, widget, widget->parent, errstr
       +                );
       +        }
       +        widget->vtable->destroy(widget, shallow);
       +
       +        return err;
       +}
   DIR diff --git a/src/widget.h b/src/widget.h
       t@@ -0,0 +1,95 @@
       +/* Requires the following includes: <X11/Xlib.h>, <X11/Xutil.h>, "rect.h" */
       +
       +#ifndef _LTK_WIDGET_H_
       +#define _LTK_WIDGET_H_
       +
       +typedef enum {
       +        LTK_STICKY_LEFT = 1 << 0,
       +        LTK_STICKY_RIGHT = 1 << 1,
       +        LTK_STICKY_TOP = 1 << 2,
       +        LTK_STICKY_BOTTOM = 1 << 3
       +} ltk_sticky_mask;
       +
       +typedef enum {
       +        LTK_VERTICAL,
       +        LTK_HORIZONTAL
       +} ltk_orientation;
       +
       +typedef enum {
       +        LTK_NORMAL,
       +        LTK_PRESSED,
       +        LTK_ACTIVE,
       +        LTK_DISABLED
       +} ltk_widget_state;
       +
       +typedef enum {
       +        /* for e.g. scrollbar, which can't be directly accessed by users */
       +        LTK_UNKNOWN,
       +        LTK_GRID,
       +        LTK_BUTTON,
       +        LTK_DRAW,
       +        LTK_LABEL,
       +        LTK_WIDGET,
       +        LTK_BOX
       +} ltk_widget_type;
       +
       +struct ltk_window;
       +
       +struct ltk_widget_vtable;
       +
       +typedef struct ltk_widget {
       +        struct ltk_window *window;
       +        struct ltk_widget *active_widget;
       +        struct ltk_widget *parent;
       +        char *id;
       +
       +        struct ltk_widget_vtable *vtable;
       +
       +        ltk_rect rect;
       +        unsigned int ideal_w;
       +        unsigned int ideal_h;
       +
       +        ltk_widget_type type;
       +        ltk_widget_state state;
       +        unsigned int sticky;
       +        unsigned short row;
       +        unsigned short column;
       +        unsigned short row_span;
       +        unsigned short column_span;
       +        unsigned char needs_redraw;
       +} ltk_widget;
       +
       +struct ltk_widget_vtable {
       +        void (*key_press) (struct ltk_widget *, XEvent);
       +        void (*key_release) (struct ltk_widget *, XEvent);
       +        int (*mouse_press) (struct ltk_widget *, XEvent);
       +        int (*mouse_release) (struct ltk_widget *, XEvent);
       +        int (*mouse_wheel) (struct ltk_widget *, XEvent);
       +        int (*motion_notify) (struct ltk_widget *, XEvent);
       +        void (*mouse_leave) (struct ltk_widget *, XEvent);
       +        void (*mouse_enter) (struct ltk_widget *, XEvent);
       +
       +        void (*resize) (struct ltk_widget *);
       +        void (*draw) (struct ltk_widget *, ltk_rect);
       +        void (*change_state) (struct ltk_widget *);
       +        void (*destroy) (struct ltk_widget *, int);
       +
       +        void (*child_size_change) (struct ltk_widget *, struct ltk_widget *);
       +        int (*remove_child) (struct ltk_window *, struct ltk_widget *, struct ltk_widget *, char **);
       +};
       +
       +int ltk_widget_destroy(struct ltk_window *window, char **tokens, size_t num_tokens, char **errstr);
       +void ltk_fill_widget_defaults(ltk_widget *widget, const char *id, struct ltk_window *window,
       +    struct ltk_widget_vtable *vtable, unsigned int needs_redraw, ltk_widget_type type);
       +void ltk_widget_change_state(ltk_widget *widget);
       +void ltk_widget_mouse_press_event(ltk_widget *widget, XEvent event);
       +void ltk_widget_mouse_release_event(ltk_widget *widget, XEvent event);
       +void ltk_widget_motion_notify_event(ltk_widget *widget, XEvent event);
       +int ltk_widget_id_free(const char *id);
       +ltk_widget *ltk_get_widget(const char *id, ltk_widget_type type, char **errstr);
       +void ltk_set_widget(ltk_widget *widget, const char *id);
       +void ltk_remove_widget(const char *id);
       +void ltk_widgets_cleanup();
       +void ltk_widgets_init();
       +
       +#endif /* _LTK_WIDGET_H_ */