URI:
       tAbstract event handling a bit - 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 994e235d9939788238dd2c66cdefb4cc4ade1bce
   DIR parent 3a21eba1783e1d29204d54e006fe9f5efb016fc3
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Sun,  5 Jun 2022 23:45:33 +0200
       
       Abstract event handling a bit
       
       Diffstat:
         M Makefile                            |       7 +++++--
         M src/box.c                           |      36 +++++++++++++++----------------
         M src/button.c                        |      10 ++++------
         D src/color.c                         |      53 ------------------------------
         M src/color.h                         |       8 ++++++--
         A src/color_xlib.c                    |      47 +++++++++++++++++++++++++++++++
         A src/event.h                         |      96 +++++++++++++++++++++++++++++++
         A src/event_xlib.c                    |      80 +++++++++++++++++++++++++++++++
         M src/graphics.h                      |      20 +++++++++++++++-----
         M src/graphics_xlib.c                 |     179 +++++++++++++++++++++++++------
         M src/grid.c                          |      32 +++++++++++++------------------
         M src/label.c                         |       4 +---
         M src/ltk.h                           |      44 ++++++++++++-------------------
         M src/ltkd.c                          |     193 ++++++++-----------------------
         M src/memory.c                        |       3 ---
         M src/menu.c                          |      52 +++++++++++++++----------------
         M src/scrollbar.c                     |      25 +++++++++++++------------
         M src/surface_cache.c                 |      13 +++++++------
         M src/surface_cache.h                 |       2 +-
         M src/text.h                          |       1 +
         M src/text_pango.c                    |       3 ++-
         M src/text_stb.c                      |       5 +++--
         M src/theme.c                         |       8 +++++---
         M src/theme.h                         |       1 +
         M src/widget.c                        |      12 +++++-------
         M src/widget.h                        |      25 +++++++++++++------------
         A src/xlib_shared.h                   |      20 ++++++++++++++++++++
       
       27 files changed, 594 insertions(+), 385 deletions(-)
       ---
   DIR diff --git a/Makefile b/Makefile
       t@@ -39,7 +39,7 @@ OBJ = \
                src/strtonum.o \
                src/util.o \
                src/memory.o \
       -        src/color.o \
       +        src/color_xlib.o \
                src/rect.o \
                src/widget.o \
                src/ltkd.o \
       t@@ -53,6 +53,7 @@ OBJ = \
                src/theme.o \
                src/graphics_xlib.o \
                src/surface_cache.o \
       +        src/event_xlib.o \
                $(EXTRA_OBJ)
        #        src/draw.o \
        
       t@@ -79,7 +80,9 @@ HDR = \
                src/theme.h \
                src/graphics.h \
                src/surface_cache.h \
       -        src/macros.h
       +        src/macros.h \
       +        src/event.h \
       +        src/xlib_shared.h
        #        src/draw.h \
        
        all: src/ltkd src/ltkc
   DIR diff --git a/src/box.c b/src/box.c
       t@@ -20,9 +20,7 @@
        #include <stdint.h>
        #include <string.h>
        
       -#include <X11/Xlib.h>
       -#include <X11/Xutil.h>
       -
       +#include "event.h"
        #include "memory.h"
        #include "color.h"
        #include "rect.h"
       t@@ -42,10 +40,10 @@ static int ltk_box_add(ltk_window *window, ltk_widget *widget, ltk_box *box, uns
        static int ltk_box_remove(ltk_window *window, ltk_widget *widget, ltk_widget *self, char **errstr);
        /* static int ltk_box_clear(ltk_window *window, ltk_box *box, int shallow, char **errstr); */
        static void ltk_box_scroll(ltk_widget *self);
       -static int ltk_box_mouse_event(ltk_box *box, XEvent event, void (*handler)(ltk_widget *, XEvent));
       -static int ltk_box_mouse_press(ltk_widget *self, XEvent event);
       -static int ltk_box_mouse_release(ltk_widget *self, XEvent event);
       -static int ltk_box_motion_notify(ltk_widget *self, XEvent event);
       +static int ltk_box_mouse_event(ltk_box *box, int x, int y, ltk_event *event, void (*handler)(ltk_widget *, ltk_event *));
       +static int ltk_box_mouse_press(ltk_widget *self, ltk_event *event);
       +static int ltk_box_mouse_release(ltk_widget *self, ltk_event *event);
       +static int ltk_box_motion_notify(ltk_widget *self, ltk_event *event);
        
        static struct ltk_widget_vtable vtable = {
                .change_state = NULL,
       t@@ -324,10 +322,10 @@ ltk_box_scroll(ltk_widget *self) {
        }
        
        static int
       -ltk_box_mouse_event(ltk_box *box, XEvent event, void (*handler)(ltk_widget *, XEvent)) {
       +ltk_box_mouse_event(ltk_box *box, int x, int y, ltk_event *event, void (*handler)(ltk_widget *, ltk_event *)) {
                ltk_widget *widget;
        
       -        if (ltk_collide_rect(box->sc->widget.rect, event.xbutton.x, event.xbutton.y)) {
       +        if (ltk_collide_rect(box->sc->widget.rect, x, y)) {
                        handler((ltk_widget *)box->sc, event);
                        return 0;
                }
       t@@ -335,7 +333,7 @@ ltk_box_mouse_event(ltk_box *box, XEvent event, void (*handler)(ltk_widget *, XE
                /* FIXME: check only the currently visible items */
                for (size_t i = 0; i < box->num_widgets; i++) {
                        widget = box->widgets[i];
       -                if (ltk_collide_rect(widget->rect, event.xbutton.x, event.xbutton.y)) {
       +                if (ltk_collide_rect(widget->rect, x, y)) {
                                handler(widget, event);
                                return 0;
                        }
       t@@ -344,41 +342,41 @@ ltk_box_mouse_event(ltk_box *box, XEvent event, void (*handler)(ltk_widget *, XE
        }
        
        static int
       -ltk_box_mouse_press(ltk_widget *self, XEvent event) {
       +ltk_box_mouse_press(ltk_widget *self, ltk_event *event) {
                ltk_box *box = (ltk_box *)self;
                /* FIXME: combine multiple events into one for efficiency */
                /* FIXME: fix this whole state handling */
       -        if (event.xbutton.button == 4 || event.xbutton.button == 5) {
       +        if (event->button.button == LTK_BUTTON4 || event->button.button == LTK_BUTTON5) {
                        ltk_widget *widget;
                        int default_handler = 1;
                        for (size_t i = 0; i < box->num_widgets; i++) {
                                widget = box->widgets[i];
       -                        if (ltk_collide_rect(widget->rect, event.xbutton.x, event.xbutton.y)) {
       +                        if (ltk_collide_rect(widget->rect, event->button.x, event->button.y)) {
                                        if (widget->vtable->mouse_press)
                                                default_handler = widget->vtable->mouse_press(widget, event);
                                }
                        }
                        /* FIXME: configure scrollstep */
                        if (default_handler) {
       -                        int delta = event.xbutton.button == 4 ? -15 : 15;
       +                        int delta = event->button.button == LTK_BUTTON4 ? -15 : 15;
                                ltk_scrollbar_scroll((ltk_widget *)box->sc, delta, 0);
                        }
                        return 0;
                } else {
       -                return ltk_box_mouse_event(box, event, &ltk_widget_mouse_press_event);
       +                return ltk_box_mouse_event(box, event->button.x, event->button.y, event, &ltk_widget_mouse_press_event);
                }
        }
        
        static int
       -ltk_box_mouse_release(ltk_widget *self, XEvent event) {
       +ltk_box_mouse_release(ltk_widget *self, ltk_event *event) {
                ltk_box *box = (ltk_box *)self;
       -        return ltk_box_mouse_event(box, event, &ltk_widget_mouse_release_event);
       +        return ltk_box_mouse_event(box, event->button.x, event->button.y, event, &ltk_widget_mouse_release_event);
        }
        
        static int
       -ltk_box_motion_notify(ltk_widget *self, XEvent event) {
       +ltk_box_motion_notify(ltk_widget *self, ltk_event *event) {
                ltk_box *box = (ltk_box *)self;
       -        return ltk_box_mouse_event(box, event, &ltk_widget_motion_notify_event);
       +        return ltk_box_mouse_event(box, event->motion.x, event->motion.y, event, &ltk_widget_motion_notify_event);
        }
        
        /* box <box id> add <widget id> [sticky] */
   DIR diff --git a/src/button.c b/src/button.c
       t@@ -20,9 +20,7 @@
        #include <string.h>
        #include <stdarg.h>
        
       -#include <X11/Xlib.h>
       -#include <X11/Xutil.h>
       -
       +#include "event.h"
        #include "memory.h"
        #include "color.h"
        #include "rect.h"
       t@@ -39,7 +37,7 @@
        #define MAX_BUTTON_PADDING 500
        
        static void ltk_button_draw(ltk_widget *self, ltk_rect clip);
       -static int ltk_button_mouse_release(ltk_widget *self, XEvent event);
       +static int ltk_button_mouse_release(ltk_widget *self, ltk_event *event);
        static ltk_button *ltk_button_create(ltk_window *window,
            const char *id, char *text);
        static void ltk_button_destroy(ltk_widget *self, int shallow);
       t@@ -171,9 +169,9 @@ ltk_button_change_state(ltk_widget *self) {
        
        /* FIXME: only when pressed button was actually this one */
        static int
       -ltk_button_mouse_release(ltk_widget *self, XEvent event) {
       +ltk_button_mouse_release(ltk_widget *self, ltk_event *event) {
                ltk_button *button = (ltk_button *)self;
       -        if (event.xbutton.button == 1) {
       +        if (event->button.button == LTK_BUTTONL) {
                        ltk_queue_event(button->widget.window, LTK_EVENT_BUTTON, button->widget.id, "button_click");
                        return 1;
                }
   DIR diff --git a/src/color.c b/src/color.c
       t@@ -1,53 +0,0 @@
       -/*
       - * Copyright (c) 2021 lumidify <nobody@lumidify.org>
       - *
       - * Permission to use, copy, modify, and/or distribute this software for any
       - * purpose with or without fee is hereby granted, provided that the above
       - * copyright notice and this permission notice appear in all copies.
       - *
       - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
       - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
       - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
       - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
       - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
       - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
       - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
       - */
       -
       -#include <stdarg.h>
       -#include <X11/Xlib.h>
       -#include <X11/Xutil.h>
       -
       -#include "util.h"
       -#include "color.h"
       -#include "compat.h"
       -
       -/* FIXME: avoid initializing part of the struct and then error returning */
       -/* FIXME: better error codes */
       -/* FIXME: I think xcolor is unneeded when xft is enabled */
       -int
       -ltk_color_create(Display *dpy, Visual *vis, Colormap cm, const char *hex, ltk_color *col) {
       -        if (!XParseColor(dpy, cm, hex, &col->xcolor))
       -                return 1;
       -        if (!XAllocColor(dpy, cm, &col->xcolor))
       -                return 1;
       -        /* FIXME: replace with XftColorAllocValue */
       -        #if USE_XFT == 1
       -        if (!XftColorAllocName(dpy, vis, cm, hex, &col->xftcolor))
       -                return 1;
       -        #else
       -        (void)vis;
       -        #endif
       -        return 0;
       -}
       -
       -void
       -ltk_color_destroy(Display *dpy, Visual *vis, Colormap cm, ltk_color *col) {
       -        /* FIXME: what should the 'planes' argument be? */
       -        XFreeColors(dpy, cm, &col->xcolor.pixel, 1, 0);
       -        #if USE_XFT == 1
       -        XftColorFree(dpy, vis, cm, &col->xftcolor);
       -        #else
       -        (void)vis;
       -        #endif
       -}
   DIR diff --git a/src/color.h b/src/color.h
       t@@ -23,6 +23,9 @@
          #include <X11/Xft/Xft.h>
        #endif
        
       +/* FIXME: proper compilation option */
       +#include <X11/Xlib.h>
       +
        typedef struct {
                XColor xcolor;
                #if USE_XFT == 1
       t@@ -30,8 +33,9 @@ typedef struct {
                #endif
        } ltk_color;
        
       +#include "graphics.h"
        /* returns 1 on failure, 0 on success */
       -int ltk_color_create(Display *dpy, Visual *vis, Colormap cm, const char *hex, ltk_color *col);
       -void ltk_color_destroy(Display *dpy, Visual *vis, Colormap cm, ltk_color *col);
       +int ltk_color_create(ltk_renderdata *renderdata, const char *hex, ltk_color *col);
       +void ltk_color_destroy(ltk_renderdata *renderdata, ltk_color *col);
        
        #endif /* _LTK_COLOR_H_ */
   DIR diff --git a/src/color_xlib.c b/src/color_xlib.c
       t@@ -0,0 +1,47 @@
       +/*
       + * Copyright (c) 2021, 2022 lumidify <nobody@lumidify.org>
       + *
       + * Permission to use, copy, modify, and/or distribute this software for any
       + * purpose with or without fee is hereby granted, provided that the above
       + * copyright notice and this permission notice appear in all copies.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
       + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
       + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
       + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
       + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
       + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
       + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
       + */
       +
       +#include <stdarg.h>
       +#include "util.h"
       +#include "color.h"
       +#include "compat.h"
       +#include "xlib_shared.h"
       +
       +/* FIXME: avoid initializing part of the struct and then error returning */
       +/* FIXME: better error codes */
       +/* FIXME: I think xcolor is unneeded when xft is enabled */
       +int
       +ltk_color_create(ltk_renderdata *renderdata, const char *hex, ltk_color *col) {
       +        if (!XParseColor(renderdata->dpy, renderdata->cm, hex, &col->xcolor))
       +                return 1;
       +        if (!XAllocColor(renderdata->dpy, renderdata->cm, &col->xcolor))
       +                return 1;
       +        /* FIXME: replace with XftColorAllocValue */
       +        #if USE_XFT == 1
       +        if (!XftColorAllocName(renderdata->dpy, renderdata->vis, renderdata->cm, hex, &col->xftcolor))
       +                return 1;
       +        #endif
       +        return 0;
       +}
       +
       +void
       +ltk_color_destroy(ltk_renderdata *renderdata, ltk_color *col) {
       +        /* FIXME: what should the 'planes' argument be? */
       +        XFreeColors(renderdata->dpy, renderdata->cm, &col->xcolor.pixel, 1, 0);
       +        #if USE_XFT == 1
       +        XftColorFree(renderdata->dpy, renderdata->vis, renderdata->cm, &col->xftcolor);
       +        #endif
       +}
   DIR diff --git a/src/event.h b/src/event.h
       t@@ -0,0 +1,96 @@
       +#ifndef LTK_EVENT_H
       +#define LTK_EVENT_H
       +
       +typedef enum {
       +        LTK_UNKNOWN_EVENT, /* FIXME: a bit weird */
       +        LTK_BUTTONPRESS_EVENT,
       +        LTK_BUTTONRELEASE_EVENT,
       +        LTK_MOTION_EVENT,
       +        LTK_KEYPRESS_EVENT,
       +        LTK_KEYRELEASE_EVENT,
       +        LTK_CONFIGURE_EVENT,
       +        LTK_EXPOSE_EVENT,
       +        LTK_WINDOWCLOSE_EVENT
       +} ltk_event_type;
       +
       +/* FIXME: button mask also in motion */
       +
       +typedef enum {
       +        LTK_BUTTONL,
       +        LTK_BUTTONM,
       +        LTK_BUTTONR,
       +        /* FIXME: dedicated scroll event */
       +        LTK_BUTTON4,
       +        LTK_BUTTON5,
       +        LTK_BUTTON6,
       +        LTK_BUTTON7
       +} ltk_button_type;
       +
       +typedef struct {
       +        ltk_event_type type;
       +        ltk_button_type button;
       +        int x, y;
       +} ltk_button_event;
       +
       +typedef struct {
       +        ltk_event_type type;
       +        int x, y;
       +} ltk_motion_event;
       +
       +/* FIXME: just steal the definitions from X when using Xlib so no conversion is necessary? */
       +typedef enum {
       +        LTK_KEY_NONE = 0,
       +        LTK_KEY_LEFT,
       +        LTK_KEY_RIGHT,
       +        LTK_KEY_UP,
       +        LTK_KEY_DOWN,
       +        LTK_KEY_BACKSPACE,
       +        LTK_KEY_DELETE,
       +        LTK_KEY_SPACE,
       +        LTK_KEY_RETURN
       +} ltk_keysym;
       +
       +typedef enum {
       +        LTK_MOD_CTRL,
       +        LTK_MOD_SHIFT,
       +        LTK_MOD_ALT,
       +        LTK_MOD_SUPER
       +} ltk_mod_type;
       +
       +typedef struct {
       +        ltk_event_type type;
       +        int x, y;
       +        ltk_mod_type modmask;
       +        ltk_keysym sym;
       +        char *text;
       +        char *mapped;
       +} ltk_key_event;
       +
       +typedef struct {
       +        ltk_event_type type;
       +        int x, y;
       +        int w, h;
       +} ltk_configure_event;
       +
       +/* FIXME: should maybe be handled in backend with double buffering */
       +typedef struct {
       +        ltk_event_type type;
       +        int x, y;
       +        int w, h;
       +} ltk_expose_event;
       +
       +typedef union {
       +        ltk_event_type type;
       +        ltk_button_event button;
       +        ltk_motion_event motion;
       +        ltk_key_event key;
       +        ltk_configure_event configure;
       +        ltk_expose_event expose;
       +} ltk_event;
       +
       +#include "ltk.h"
       +
       +int ltk_events_pending(ltk_renderdata *renderdata);
       +void ltk_next_event(ltk_renderdata *renderdata, ltk_event *event);
       +
       +#endif /* LTK_EVENT_H */
   DIR diff --git a/src/event_xlib.c b/src/event_xlib.c
       t@@ -0,0 +1,80 @@
       +#include "graphics.h"
       +#include "xlib_shared.h"
       +
       +int
       +ltk_events_pending(ltk_renderdata *renderdata) {
       +        return XPending(renderdata->dpy);
       +}
       +
       +static ltk_button_type
       +get_button(unsigned int button) {
       +        switch (button) {
       +        case Button1: return LTK_BUTTONL;
       +        case Button2: return LTK_BUTTONM;
       +        case Button3: return LTK_BUTTONR;
       +        case 4: return LTK_BUTTON4;
       +        case 5: return LTK_BUTTON5;
       +        case 6: return LTK_BUTTON6;
       +        case 7: return LTK_BUTTON7;
       +        default: return LTK_BUTTONL; /* FIXME: what to do here? */
       +        }
       +}
       +
       +void
       +ltk_next_event(ltk_renderdata *renderdata, ltk_event *event) {
       +        XEvent xevent;
       +        XNextEvent(renderdata->dpy, &xevent);
       +        *event = (ltk_event){.type = LTK_UNKNOWN_EVENT};
       +        switch (xevent.type) {
       +        case ButtonPress:
       +                *event = (ltk_event){.button = {
       +                        .type = LTK_BUTTONPRESS_EVENT,
       +                        .button = get_button(xevent.xbutton.button),
       +                        .x = xevent.xbutton.x,
       +                        .y = xevent.xbutton.y
       +                }};
       +                break;
       +        case ButtonRelease:
       +                *event = (ltk_event){.button = {
       +                        .type = LTK_BUTTONRELEASE_EVENT,
       +                        .button = get_button(xevent.xbutton.button),
       +                        .x = xevent.xbutton.x,
       +                        .y = xevent.xbutton.y
       +                }};
       +                break;
       +        case MotionNotify:
       +                *event = (ltk_event){.motion = {
       +                        .type = LTK_MOTION_EVENT,
       +                        .x = xevent.xmotion.x,
       +                        .y = xevent.xmotion.y
       +                }};
       +                break;
       +        case ConfigureNotify:
       +                *event = (ltk_event){.configure = {
       +                        .type = LTK_CONFIGURE_EVENT,
       +                        .x = xevent.xconfigure.x,
       +                        .y = xevent.xconfigure.y,
       +                        .w = xevent.xconfigure.width,
       +                        .h = xevent.xconfigure.height
       +                }};
       +                break;
       +        case Expose:
       +                /* FIXME: ignoring all of these events would make it not
       +                   work anymore if the actual rectangle wasn't ignored
       +                   later anyways */
       +                if (xevent.xexpose.count == 0) {
       +                        *event = (ltk_event){.expose = {
       +                                .type = LTK_EXPOSE_EVENT,
       +                                .x = xevent.xexpose.x,
       +                                .y = xevent.xexpose.y,
       +                                .w = xevent.xexpose.width,
       +                                .h = xevent.xexpose.height
       +                        }};
       +                }
       +                break;
       +        case ClientMessage:
       +                if ((Atom)xevent.xclient.data.l[0] == renderdata->wm_delete_msg)
       +                        *event = (ltk_event){.type = LTK_WINDOWCLOSE_EVENT};
       +                break;
       +        }
       +}
   DIR diff --git a/src/graphics.h b/src/graphics.h
       t@@ -14,12 +14,14 @@
         * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
         */
        
       -#ifndef _LTK_GRAPHICS_H_
       -#define _LTK_GRAPHICS_H_
       +#ifndef LTK_GRAPHICS_H
       +#define LTK_GRAPHICS_H
        
        /* FIXME: make this global and use it elsewhere */
        #define USE_XLIB_GRAPHICS 1
        
       +typedef struct ltk_renderdata ltk_renderdata;
       +
        /* FIXME: Is it faster to take ltk_color* or ltk_color? */
        
        #include <X11/Xft/Xft.h>
       t@@ -45,13 +47,13 @@ typedef struct {
        /* typedef struct ltk_surface ltk_surface; */
        
        /* FIXME: graphics context */
       -ltk_surface *ltk_surface_create(ltk_window *window, int w, int h);
       +ltk_surface *ltk_surface_create(ltk_renderdata *renderdata, int w, int h);
        void ltk_surface_destroy(ltk_surface *s);
        /* returns 0 if successful, 1 if not resizable */
        int ltk_surface_resize(ltk_surface *s, int w, int h);
        /* FIXME: kind of hacky */
        void ltk_surface_update_size(ltk_surface *s, int w, int h);
       -ltk_surface *ltk_surface_from_window(ltk_window *window);
       +ltk_surface *ltk_surface_from_window(ltk_renderdata *renderdata, int w, int h);
        void ltk_surface_get_size(ltk_surface *s, int *w, int *h);
        void ltk_surface_copy(ltk_surface *src, ltk_surface *dst, ltk_rect src_rect, int dst_x, int dst_y);
        void ltk_surface_draw_rect(ltk_surface *s, ltk_color *c, ltk_rect rect, int line_width);
       t@@ -76,4 +78,12 @@ XftDraw *ltk_surface_get_xft_draw(ltk_surface *s);
        Drawable ltk_surface_get_drawable(ltk_surface *s);
        #endif
        
       -#endif /* _LTK_GRAPHICS_H_ */
       +ltk_renderdata *renderer_create_window(const char *title, int x, int y, unsigned int w, unsigned int h);
       +void renderer_destroy_window(ltk_renderdata *renderdata);
       +void renderer_set_window_properties(ltk_renderdata *renderdata, ltk_color *bg, ltk_color *border, unsigned int border_width);
       +/* FIXME: this is kind of out of place */
       +void renderer_swap_buffers(ltk_renderdata *renderdata);
       +/* FIXME: this is just for the socket name and is a bit weird */
       +unsigned long renderer_get_window_id(ltk_renderdata *renderdata);
       +
       +#endif /* LTK_GRAPHICS_H */
   DIR diff --git a/src/graphics_xlib.c b/src/graphics_xlib.c
       t@@ -16,18 +16,19 @@
        
        #include <X11/Xlib.h>
        #include <X11/Xutil.h>
       +#include <X11/extensions/Xdbe.h>
        #include <stdint.h>
        
        #include "color.h"
        #include "rect.h"
       -#include "widget.h"
       -#include "ltk.h"
       +#include "util.h"
        #include "memory.h"
        #include "compat.h"
       +#include "xlib_shared.h"
        
        struct ltk_surface {
                int w, h;
       -        ltk_window *window;
       +        ltk_renderdata *renderdata;
                Drawable d;
                #if USE_XFT == 1
                XftDraw *xftdraw;
       t@@ -36,7 +37,7 @@ struct ltk_surface {
        };
        
        ltk_surface *
       -ltk_surface_create(ltk_window *window, int w, int h) {
       +ltk_surface_create(ltk_renderdata *renderdata, int w, int h) {
                ltk_surface *s = ltk_malloc(sizeof(ltk_surface));
                if (w <= 0)
                        w = 1;
       t@@ -44,24 +45,24 @@ ltk_surface_create(ltk_window *window, int w, int h) {
                        h = 1;
                s->w = w;
                s->h = h;
       -        s->window = window;
       -        s->d = XCreatePixmap(window->dpy, window->xwindow, w, h, window->depth);
       +        s->renderdata = renderdata;
       +        s->d = XCreatePixmap(renderdata->dpy, renderdata->xwindow, w, h, renderdata->depth);
                #if USE_XFT == 1
       -        s->xftdraw = XftDrawCreate(window->dpy, s->d, window->vis, window->cm);
       +        s->xftdraw = XftDrawCreate(renderdata->dpy, s->d, renderdata->vis, renderdata->cm);
                #endif
                s->resizable = 1;
                return s;
        }
        
        ltk_surface *
       -ltk_surface_from_window(ltk_window *window) {
       +ltk_surface_from_window(ltk_renderdata *renderdata, int w, int h) {
                ltk_surface *s = ltk_malloc(sizeof(ltk_surface));
       -        s->w = window->rect.w;
       -        s->h = window->rect.h;
       -        s->window = window;
       -        s->d = window->drawable;
       +        s->w = w;
       +        s->h = h;
       +        s->renderdata = renderdata;
       +        s->d = renderdata->drawable;
                #if USE_XFT == 1
       -        s->xftdraw = XftDrawCreate(window->dpy, s->d, window->vis, window->cm);
       +        s->xftdraw = XftDrawCreate(renderdata->dpy, s->d, renderdata->vis, renderdata->cm);
                #endif
                s->resizable = 0;
                return s;
       t@@ -73,12 +74,13 @@ ltk_surface_destroy(ltk_surface *s) {
                XftDrawDestroy(s->xftdraw);
                #endif
                if (s->resizable)
       -                XFreePixmap(s->window->dpy, (Pixmap)s->d);
       +                XFreePixmap(s->renderdata->dpy, (Pixmap)s->d);
                ltk_free(s);
        }
        
        void
        ltk_surface_update_size(ltk_surface *s, int w, int h) {
       +        /* FIXME: maybe return directly if surface is resizable? */
                s->w = w;
                s->h = h;
        }
       t@@ -89,8 +91,8 @@ ltk_surface_resize(ltk_surface *s, int w, int h) {
                        return 1;
                s->w = w;
                s->h = h;
       -        XFreePixmap(s->window->dpy, (Pixmap)s->d);
       -        s->d = XCreatePixmap(s->window->dpy, s->window->xwindow, w, h, s->window->depth);
       +        XFreePixmap(s->renderdata->dpy, (Pixmap)s->d);
       +        s->d = XCreatePixmap(s->renderdata->dpy, s->renderdata->xwindow, w, h, s->renderdata->depth);
                #if USE_XFT == 1
                XftDrawChange(s->xftdraw, s->d);
                #endif
       t@@ -105,30 +107,30 @@ ltk_surface_get_size(ltk_surface *s, int *w, int *h) {
        
        void
        ltk_surface_draw_rect(ltk_surface *s, ltk_color *c, ltk_rect rect, int line_width) {
       -        XSetForeground(s->window->dpy, s->window->gc, c->xcolor.pixel);
       -        XSetLineAttributes(s->window->dpy, s->window->gc, line_width, LineSolid, CapButt, JoinMiter);
       -        XDrawRectangle(s->window->dpy, s->d, s->window->gc, rect.x, rect.y, rect.w, rect.h);
       +        XSetForeground(s->renderdata->dpy, s->renderdata->gc, c->xcolor.pixel);
       +        XSetLineAttributes(s->renderdata->dpy, s->renderdata->gc, line_width, LineSolid, CapButt, JoinMiter);
       +        XDrawRectangle(s->renderdata->dpy, s->d, s->renderdata->gc, rect.x, rect.y, rect.w, rect.h);
        }
        
        void
        ltk_surface_draw_border(ltk_surface *s, ltk_color *c, ltk_rect rect, int line_width, ltk_border_sides border_sides) {
                /* drawn as rectangles to have proper control over line width - I'm not sure how exactly
                   XDrawLine handles even line widths (i.e. on which side the extra pixel will be) */
       -        XSetForeground(s->window->dpy, s->window->gc, c->xcolor.pixel);
       +        XSetForeground(s->renderdata->dpy, s->renderdata->gc, c->xcolor.pixel);
                if (border_sides & LTK_BORDER_TOP)
       -                XFillRectangle(s->window->dpy, s->d, s->window->gc, rect.x, rect.y, rect.w, line_width);
       +                XFillRectangle(s->renderdata->dpy, s->d, s->renderdata->gc, rect.x, rect.y, rect.w, line_width);
                if (border_sides & LTK_BORDER_BOTTOM)
       -                XFillRectangle(s->window->dpy, s->d, s->window->gc, rect.x, rect.y + rect.h - line_width, rect.w, line_width);
       +                XFillRectangle(s->renderdata->dpy, s->d, s->renderdata->gc, rect.x, rect.y + rect.h - line_width, rect.w, line_width);
                if (border_sides & LTK_BORDER_LEFT)
       -                XFillRectangle(s->window->dpy, s->d, s->window->gc, rect.x, rect.y, line_width, rect.h);
       +                XFillRectangle(s->renderdata->dpy, s->d, s->renderdata->gc, rect.x, rect.y, line_width, rect.h);
                if (border_sides & LTK_BORDER_RIGHT)
       -                XFillRectangle(s->window->dpy, s->d, s->window->gc, rect.x + rect.w - line_width, rect.y, line_width, rect.h);
       +                XFillRectangle(s->renderdata->dpy, s->d, s->renderdata->gc, rect.x + rect.w - line_width, rect.y, line_width, rect.h);
        }
        
        void
        ltk_surface_fill_rect(ltk_surface *s, ltk_color *c, ltk_rect rect) {
       -        XSetForeground(s->window->dpy, s->window->gc, c->xcolor.pixel);
       -        XFillRectangle(s->window->dpy, s->d, s->window->gc, rect.x, rect.y, rect.w, rect.h);
       +        XSetForeground(s->renderdata->dpy, s->renderdata->gc, c->xcolor.pixel);
       +        XFillRectangle(s->renderdata->dpy, s->d, s->renderdata->gc, rect.x, rect.y, rect.w, rect.h);
        }
        
        void
       t@@ -147,8 +149,8 @@ ltk_surface_fill_polygon(ltk_surface *s, ltk_color *c, ltk_point *points, size_t
                        final_points[i].x = (short)points[i].x;
                        final_points[i].y = (short)points[i].y;
                }
       -        XSetForeground(s->window->dpy, s->window->gc, c->xcolor.pixel);
       -        XFillPolygon(s->window->dpy, s->d, s->window->gc, final_points, (int)npoints, Complex, CoordModeOrigin);
       +        XSetForeground(s->renderdata->dpy, s->renderdata->gc, c->xcolor.pixel);
       +        XFillPolygon(s->renderdata->dpy, s->d, s->renderdata->gc, final_points, (int)npoints, Complex, CoordModeOrigin);
                if (npoints > 6)
                        free(final_points);
        }
       t@@ -156,7 +158,7 @@ ltk_surface_fill_polygon(ltk_surface *s, ltk_color *c, ltk_point *points, size_t
        void
        ltk_surface_copy(ltk_surface *src, ltk_surface *dst, ltk_rect src_rect, int dst_x, int dst_y) {
                XCopyArea(
       -            src->window->dpy, src->d, dst->d, src->window->gc,
       +            src->renderdata->dpy, src->d, dst->d, src->renderdata->gc,
                    src_rect.x, src_rect.y, src_rect.w, src_rect.h, dst_x, dst_y
                );
        }
       t@@ -187,9 +189,124 @@ ltk_surface_get_xft_draw(ltk_surface *s) {
        }
        #endif
        
       -#if USE_XLIB_GRAPHICS == 1
        Drawable
        ltk_surface_get_drawable(ltk_surface *s) {
                return s->d;
        }
       -#endif
       +
       +ltk_renderdata *
       +renderer_create_window(const char *title, int x, int y, unsigned int w, unsigned int h) {
       +        XSetWindowAttributes attrs;
       +        ltk_renderdata *renderdata = ltk_malloc(sizeof(ltk_renderdata));
       +
       +        renderdata->dpy = XOpenDisplay(NULL);
       +        renderdata->screen = DefaultScreen(renderdata->dpy);
       +        /* based on http://wili.cc/blog/xdbe.html */
       +        int major, minor, found = 0;
       +        if (XdbeQueryExtension(renderdata->dpy, &major, &minor)) {
       +                int num_screens = 1;
       +                Drawable screens[] = {DefaultRootWindow(renderdata->dpy)};
       +                XdbeScreenVisualInfo *info = XdbeGetVisualInfo(
       +                    renderdata->dpy, screens, &num_screens
       +                );
       +                if (!info || num_screens < 1 || info->count < 1) {
       +                        ltk_fatal("No visuals support Xdbe.");
       +                }
       +                XVisualInfo xvisinfo_templ;
       +                /* we know there's at least one */
       +                xvisinfo_templ.visualid = info->visinfo[0].visual;
       +                /* FIXME: proper screen number? */
       +                xvisinfo_templ.screen = 0;
       +                xvisinfo_templ.depth = info->visinfo[0].depth;
       +                int matches;
       +                XVisualInfo *xvisinfo_match = XGetVisualInfo(
       +                    renderdata->dpy,
       +                    VisualIDMask | VisualScreenMask | VisualDepthMask,
       +                    &xvisinfo_templ, &matches
       +                );
       +                if (!xvisinfo_match || matches < 1) {
       +                        ltk_fatal("Couldn't match a Visual with double buffering.\n");
       +                }
       +                renderdata->vis = xvisinfo_match->visual;
       +                /* FIXME: is it legal to free this while keeping the visual? */
       +                XFree(xvisinfo_match);
       +                XdbeFreeVisualInfo(info);
       +                found = 1;
       +        } else {
       +                renderdata->vis = DefaultVisual(renderdata->dpy, renderdata->screen);
       +                ltk_warn("No Xdbe support.\n");
       +        }
       +        renderdata->cm = DefaultColormap(renderdata->dpy, renderdata->screen);
       +        renderdata->wm_delete_msg = XInternAtom(renderdata->dpy, "WM_DELETE_WINDOW", False);
       +
       +        memset(&attrs, 0, sizeof(attrs));
       +        attrs.background_pixel = BlackPixel(renderdata->dpy, renderdata->screen);
       +        attrs.colormap = renderdata->cm;
       +        attrs.border_pixel = WhitePixel(renderdata->dpy, renderdata->screen);
       +        /* this causes the window contents to be kept
       +         * when it is resized, leading to less flicker */
       +        attrs.bit_gravity = NorthWestGravity;
       +        attrs.event_mask =
       +                ExposureMask | KeyPressMask | KeyReleaseMask |
       +                ButtonPressMask | ButtonReleaseMask |
       +                StructureNotifyMask | PointerMotionMask;
       +        renderdata->depth = DefaultDepth(renderdata->dpy, renderdata->screen);
       +        /* FIXME: set border width */
       +        renderdata->xwindow = XCreateWindow(
       +            renderdata->dpy, DefaultRootWindow(renderdata->dpy), x, y,
       +            w, h, 0, renderdata->depth,
       +            InputOutput, renderdata->vis,
       +            CWBackPixel | CWColormap | CWBitGravity | CWEventMask | CWBorderPixel, &attrs
       +        );
       +
       +        if (found) {
       +                renderdata->back_buf = XdbeAllocateBackBufferName(
       +                        renderdata->dpy, renderdata->xwindow, XdbeBackground
       +                );
       +        } else {
       +                renderdata->back_buf = renderdata->xwindow;
       +        }
       +        renderdata->drawable = renderdata->back_buf;
       +        renderdata->gc = XCreateGC(renderdata->dpy, renderdata->xwindow, 0, 0);
       +        XSetStandardProperties(
       +                renderdata->dpy, renderdata->xwindow,
       +                title, NULL, None, NULL, 0, NULL
       +        );
       +        XSetWMProtocols(renderdata->dpy, renderdata->xwindow, &renderdata->wm_delete_msg, 1);
       +        XClearWindow(renderdata->dpy, renderdata->xwindow);
       +        XMapRaised(renderdata->dpy, renderdata->xwindow);
       +
       +        return renderdata;
       +}
       +
       +void
       +renderer_destroy_window(ltk_renderdata *renderdata) {
       +        XFreeGC(renderdata->dpy, renderdata->gc);
       +        XDestroyWindow(renderdata->dpy, renderdata->xwindow);
       +        XCloseDisplay(renderdata->dpy);
       +        free(renderdata);
       +}
       +
       +/* FIXME: this is a completely random collection of properties and should be
       +   changed to a more sensible list */
       +void
       +renderer_set_window_properties(ltk_renderdata *renderdata, ltk_color *bg, ltk_color *border, unsigned int border_width) {
       +        XSetWindowBorder(renderdata->dpy, renderdata->xwindow, border->xcolor.pixel);
       +        XSetWindowBackground(renderdata->dpy, renderdata->xwindow, bg->xcolor.pixel);
       +        XSetWindowBorderWidth(renderdata->dpy, renderdata->xwindow, border_width);
       +}
       +
       +void
       +renderer_swap_buffers(ltk_renderdata *renderdata) {
       +        XdbeSwapInfo swap_info;
       +        swap_info.swap_window = renderdata->xwindow;
       +        swap_info.swap_action = XdbeBackground;
       +        if (!XdbeSwapBuffers(renderdata->dpy, &swap_info, 1))
       +                ltk_fatal("Unable to swap buffers.\n");
       +        XFlush(renderdata->dpy);
       +}
       +
       +unsigned long
       +renderer_get_window_id(ltk_renderdata *renderdata) {
       +        return (unsigned long)renderdata->xwindow;
       +}
   DIR diff --git a/src/grid.c b/src/grid.c
       t@@ -28,9 +28,7 @@
        #include <stdarg.h>
        #include <stdint.h>
        
       -#include <X11/Xlib.h>
       -#include <X11/Xutil.h>
       -
       +#include "event.h"
        #include "memory.h"
        #include "color.h"
        #include "rect.h"
       t@@ -52,9 +50,9 @@ static int ltk_grid_add(ltk_window *window, ltk_widget *widget, ltk_grid *grid,
        static int ltk_grid_ungrid(ltk_window *window, ltk_widget *widget, ltk_widget *self, char **errstr);
        static int ltk_grid_find_nearest_column(ltk_grid *grid, int x);
        static int ltk_grid_find_nearest_row(ltk_grid *grid, int y);
       -static int ltk_grid_mouse_press(ltk_widget *self, XEvent event);
       -static int ltk_grid_mouse_release(ltk_widget *self, XEvent event);
       -static int ltk_grid_motion_notify(ltk_widget *self, XEvent event);
       +static int ltk_grid_mouse_press(ltk_widget *self, ltk_event *event);
       +static int ltk_grid_mouse_release(ltk_widget *self, ltk_event *event);
       +static int ltk_grid_motion_notify(ltk_widget *self, ltk_event *event);
        
        static struct ltk_widget_vtable vtable = {
                .draw = &ltk_grid_draw,
       t@@ -386,10 +384,10 @@ ltk_grid_find_nearest_row(ltk_grid *grid, int y) {
        }
        
        static int
       -ltk_grid_mouse_press(ltk_widget *self, XEvent event) {
       +ltk_grid_mouse_press(ltk_widget *self, ltk_event *event) {
                ltk_grid *grid = (ltk_grid *)self;
       -        int x = event.xbutton.x;
       -        int y = event.xbutton.y;
       +        int x = event->button.x;
       +        int y = event->button.y;
                int row = ltk_grid_find_nearest_row(grid, y);
                int column = ltk_grid_find_nearest_column(grid, x);
                if (row == -1 || column == -1)
       t@@ -403,10 +401,10 @@ ltk_grid_mouse_press(ltk_widget *self, XEvent event) {
        }
        
        static int
       -ltk_grid_mouse_release(ltk_widget *self, XEvent event) {
       +ltk_grid_mouse_release(ltk_widget *self, ltk_event *event) {
                ltk_grid *grid = (ltk_grid *)self;
       -        int x = event.xbutton.x;
       -        int y = event.xbutton.y;
       +        int x = event->button.x;
       +        int y = event->button.y;
                int row = ltk_grid_find_nearest_row(grid, y);
                int column = ltk_grid_find_nearest_column(grid, x);
                if (row == -1 || column == -1)
       t@@ -420,14 +418,10 @@ ltk_grid_mouse_release(ltk_widget *self, XEvent event) {
        }
        
        static int
       -ltk_grid_motion_notify(ltk_widget *self, XEvent event) {
       +ltk_grid_motion_notify(ltk_widget *self, ltk_event *event) {
                ltk_grid *grid = (ltk_grid *)self;
       -        /* FIXME: Why does it check this? */
       -        short pressed = (event.xmotion.state & Button1Mask) == Button1Mask;
       -        if (pressed)
       -                return 0;
       -        int x = event.xbutton.x;
       -        int y = event.xbutton.y;
       +        int x = event->motion.x;
       +        int y = event->motion.y;
                int row = ltk_grid_find_nearest_row(grid, y);
                int column = ltk_grid_find_nearest_column(grid, x);
                if (row == -1 || column == -1)
   DIR diff --git a/src/label.c b/src/label.c
       t@@ -20,9 +20,7 @@
        #include <string.h>
        #include <stdarg.h>
        
       -#include <X11/Xlib.h>
       -#include <X11/Xutil.h>
       -
       +#include "event.h"
        #include "memory.h"
        #include "color.h"
        #include "rect.h"
   DIR diff --git a/src/ltk.h b/src/ltk.h
       t@@ -17,29 +17,17 @@
        #ifndef _LTK_H_
        #define _LTK_H_
        
       -#include <X11/Xlib.h>
       -#include <X11/Xutil.h>
        #include <stdint.h>
       -#include <X11/extensions/Xdbe.h>
       -#include "color.h"
        
        typedef enum {
                LTK_EVENT_RESIZE = 1 << 0,
                LTK_EVENT_BUTTON = 1 << 1,
                LTK_EVENT_KEY = 1 << 2,
                LTK_EVENT_MENU = 1 << 3
       -} ltk_event_type;
       -
       -typedef struct {
       -        int border_width;
       -        int font_size;
       -        char *font;
       -        ltk_color fg;
       -        ltk_color bg;
       -} ltk_window_theme;
       +} ltk_userevent_type;
        
        struct ltk_event_queue {
       -        ltk_event_type event_type;
       +        ltk_userevent_type event_type;
                char *data;
                struct ltk_event_queue *prev;
                struct ltk_event_queue *next;
       t@@ -59,28 +47,21 @@ struct ltk_event_queue {
        typedef struct ltk_window ltk_window;
        typedef struct ltk_text_context ltk_text_context;
        typedef struct ltk_surface ltk_surface;
       +typedef struct ltk_window_theme ltk_window_theme;
        
        #include "widget.h"
        #include "surface_cache.h"
       +#include "event.h"
        
        struct ltk_window {
       -        Display *dpy;
       -        Visual *vis;
       +        ltk_renderdata *renderdata;
                ltk_surface_cache *surface_cache;
                ltk_text_context *text_context;
                ltk_surface *surface;
       -        Colormap cm;
       -        GC gc;
       -        int screen;
       -        Atom wm_delete_msg;
       -        Window xwindow;
       -        XdbeBackBuffer back_buf;
       -        Drawable drawable;
       -        int depth;
                ltk_widget *root_widget;
                ltk_widget *active_widget;
                ltk_widget *pressed_widget;
       -        void (*other_event) (struct ltk_window *, XEvent event);
       +        void (*other_event) (struct ltk_window *, ltk_event *event);
                ltk_rect rect;
                ltk_window_theme *theme;
                ltk_rect dirty_rect;
       t@@ -96,9 +77,18 @@ struct ltk_window {
                char popups_locked;
        };
        
       +#include "color.h"
       +
       +struct ltk_window_theme {
       +        int border_width;
       +        int font_size;
       +        char *font;
       +        ltk_color fg;
       +        ltk_color bg;
       +};
       +
        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);
       +void ltk_queue_event(ltk_window *window, ltk_userevent_type type, const char *id, const char *data);
        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_quit(ltk_window *window);
   DIR diff --git a/src/ltkd.c b/src/ltkd.c
       t@@ -36,12 +36,11 @@
        #include <sys/select.h>
        #include <sys/socket.h>
        
       -#include <X11/Xlib.h>
       -#include <X11/Xutil.h>
       -
        #include "ini.h"
        #include "khash.h"
        
       +#include "graphics.h"
       +#include "surface_cache.h"
        #include "theme.h"
        #include "memory.h"
        #include "color.h"
       t@@ -113,8 +112,8 @@ static ltk_window *ltk_create_window(const char *title, int x, int y,
            unsigned int w, unsigned int h);
        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);
       -static void ltk_handle_event(ltk_window *window, XEvent event);
       +static void ltk_window_other_event(ltk_window *window, ltk_event *event);
       +static void ltk_handle_event(ltk_window *window, ltk_event *event);
        
        static void ltk_load_theme(ltk_window *window, const char *path);
        static void ltk_uninitialize_theme(ltk_window *window);
       t@@ -172,7 +171,7 @@ int main(int argc, char *argv[]) {
                   anyways, so it doesn't matter, but still... */
                main_window = ltk_create_window(title, 0, 0, 500, 500);
        
       -        sock_path = get_sock_path(ltk_dir, main_window->xwindow);
       +        sock_path = get_sock_path(ltk_dir, renderer_get_window_id(main_window->renderdata));
                if (!sock_path) ltk_fatal_errno("Unable to allocate memory for socket path.\n");
        
                /* Note: sockets should be initialized to 0 because it is static */
       t@@ -189,7 +188,7 @@ int main(int argc, char *argv[]) {
        
        static int
        ltk_mainloop(ltk_window *window) {
       -        XEvent event;
       +        ltk_event event;
                fd_set rfds, wfds, rallfds, wallfds;
                int maxfd;
                int rretval, wretval;
       t@@ -207,7 +206,7 @@ ltk_mainloop(ltk_window *window) {
                FD_SET(listenfd, &rallfds);
                maxfd = listenfd;
        
       -        printf("%lu", window->xwindow);
       +        printf("%lu", renderer_get_window_id(main_window->renderdata));
                fflush(stdout);
                if (daemonize_flag)
                        daemonize();
       t@@ -218,6 +217,8 @@ ltk_mainloop(ltk_window *window) {
                struct timespec now, elapsed, last;
                clock_gettime(CLOCK_MONOTONIC, &last);
        
       +        /* FIXME: framerate limiting for draw */
       +
                while (running) {
                        rfds = rallfds;
                        wfds = wallfds;
       t@@ -228,9 +229,9 @@ ltk_mainloop(ltk_window *window) {
                        /* value of tv doesn't really matter anymore here because the
                           necessary framerate-limiting delay is already done */
                        wretval = select(maxfd + 1, NULL, &wfds, NULL, &tv);
       -                while (XPending(window->dpy)) {
       -                        XNextEvent(window->dpy, &event);
       -                        ltk_handle_event(window, event);
       +                while (ltk_events_pending(window->renderdata)) {
       +                        ltk_next_event(window->renderdata, &event);
       +                        ltk_handle_event(window, &event);
                        }
        
                        if (rretval > 0 || (sock_write_available && wretval > 0)) {
       t@@ -459,7 +460,7 @@ ltk_log_msg(const char *mode, const char *format, va_list args) {
                strftime(logtime, 25, "%Y-%m-%d %H:%M:%S", timeptr);
        
                if (main_window)
       -                fprintf(stderr, "%s ltkd(%lu) %s: ", logtime, main_window->xwindow, mode);
       +                fprintf(stderr, "%s ltkd(%lu) %s: ", logtime, renderer_get_window_id(main_window->renderdata), mode);
                else
                        fprintf(stderr, "%s ltkd(?) %s: ", logtime, mode);
                vfprintf(stderr, format, args);
       t@@ -495,18 +496,8 @@ ltk_window_invalidate_rect(ltk_window *window, ltk_rect rect) {
                        window->dirty_rect = ltk_rect_union(rect, window->dirty_rect);
        }
        
       -/* FIXME: Proper error checking by calling functions */
       -int
       -ltk_create_xcolor(ltk_window *window, const char *hex, XColor *col) {
       -        if (!XParseColor(window->dpy, window->cm, hex, col))
       -                return 0;
       -        XAllocColor(window->dpy, window->cm, col);
       -
       -        return 1;
       -}
       -
        void
       -ltk_queue_event(ltk_window *window, ltk_event_type type, const char *id, const char *data) {
       +ltk_queue_event(ltk_window *window, ltk_userevent_type type, const char *id, const char *data) {
                /* FIXME: make it nicer and safer */
                struct ltk_event_queue *new = ltk_malloc(sizeof(struct ltk_event_queue));
                new->event_type = type;
       t@@ -535,14 +526,9 @@ ltk_redraw_window(ltk_window *window) {
                        window->dirty_rect.w -= window->dirty_rect.x + window->dirty_rect.w - window->rect.w;
                if (window->dirty_rect.y + window->dirty_rect.h > window->rect.h)
                        window->dirty_rect.h -= window->dirty_rect.y + window->dirty_rect.h - window->rect.h;
       -        XSetForeground(window->dpy, window->gc, window->theme->bg.xcolor.pixel);
                /* FIXME: this should use window->dirty_rect, but that doesn't work
                   properly with double buffering */
       -        XFillRectangle(
       -                window->dpy, window->drawable, window->gc,
       -                window->rect.x, window->rect.y,
       -                window->rect.w, window->rect.h
       -        );
       +        ltk_surface_fill_rect(window->surface, &window->theme->bg, (ltk_rect){0, 0, window->rect.w, window->rect.h});
                if (window->root_widget) {
                        ptr = window->root_widget;
                        ptr->vtable->draw(ptr, window->rect);
       t@@ -552,22 +538,17 @@ ltk_redraw_window(ltk_window *window) {
                        ptr = window->popups[i];
                        ptr->vtable->draw(ptr, window->rect);
                }
       -        XdbeSwapInfo swap_info;
       -        swap_info.swap_window = window->xwindow;
       -        swap_info.swap_action = XdbeBackground;
       -        if (!XdbeSwapBuffers(window->dpy, &swap_info, 1))
       -                ltk_fatal("Unable to swap buffers.\n");
       -        XFlush(window->dpy);
       +        renderer_swap_buffers(window->renderdata);
        }
        
        static void
       -ltk_window_other_event(ltk_window *window, XEvent event) {
       +ltk_window_other_event(ltk_window *window, ltk_event *event) {
                ltk_widget *ptr = window->root_widget;
       -        if (event.type == ConfigureNotify) {
       +        if (event->type == LTK_CONFIGURE_EVENT) {
                        ltk_window_unregister_all_popups(window);
                        int w, h;
       -                w = event.xconfigure.width;
       -                h = event.xconfigure.height;
       +                w = event->configure.w;
       +                h = event->configure.h;
                        int orig_w = window->rect.w;
                        int orig_h = window->rect.h;
                        if (orig_w != w || orig_h != h) {
       t@@ -581,15 +562,14 @@ ltk_window_other_event(ltk_window *window, XEvent event) {
                                        ltk_widget_resize(ptr);
                                }
                        }
       -        } else if (event.type == Expose && event.xexpose.count == 0) {
       +        } else if (event->type == LTK_EXPOSE_EVENT) {
                        ltk_rect r;
       -                r.x = event.xexpose.x;
       -                r.y = event.xexpose.y;
       -                r.w = event.xexpose.width;
       -                r.h = event.xexpose.height;
       +                r.x = event->expose.x;
       +                r.y = event->expose.y;
       +                r.w = event->expose.w;
       +                r.h = event->expose.h;
                        ltk_window_invalidate_rect(window, r);
       -        } else if (event.type == ClientMessage
       -            && (Atom)event.xclient.data.l[0] == window->wm_delete_msg) {
       +        } else if (event->type == LTK_WINDOWCLOSE_EVENT) {
                        ltk_quit(window);
                }
        }
       t@@ -784,7 +764,6 @@ ltk_uninitialize_theme(ltk_window *window) {
        static ltk_window *
        ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int h) {
                char *theme_path;
       -        XSetWindowAttributes attrs;
        
                ltk_window *window = ltk_malloc(sizeof(ltk_window));
        
       t@@ -792,111 +771,39 @@ ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int 
                window->popups_num = window->popups_alloc = 0;
                window->popups_locked = 0;
        
       -        window->dpy = XOpenDisplay(NULL);
       -        window->screen = DefaultScreen(window->dpy);
       -        /* based on http://wili.cc/blog/xdbe.html */
       -        int major, minor, found = 0;
       -        if (XdbeQueryExtension(window->dpy, &major, &minor)) {
       -                int num_screens = 1;
       -                Drawable screens[] = {DefaultRootWindow(window->dpy)};
       -                XdbeScreenVisualInfo *info = XdbeGetVisualInfo(
       -                    window->dpy, screens, &num_screens
       -                );
       -                if (!info || num_screens < 1 || info->count < 1) {
       -                        ltk_fatal("No visuals support Xdbe.");
       -                }
       -                XVisualInfo xvisinfo_templ;
       -                /* we know there's at least one */
       -                xvisinfo_templ.visualid = info->visinfo[0].visual;
       -                /* FIXME: proper screen number? */
       -                xvisinfo_templ.screen = 0;
       -                xvisinfo_templ.depth = info->visinfo[0].depth;
       -                int matches;
       -                XVisualInfo *xvisinfo_match = XGetVisualInfo(
       -                    window->dpy,
       -                    VisualIDMask | VisualScreenMask | VisualDepthMask,
       -                    &xvisinfo_templ, &matches
       -                );
       -                if (!xvisinfo_match || matches < 1) {
       -                        ltk_fatal("Couldn't match a Visual with double buffering.\n");
       -                }
       -                window->vis = xvisinfo_match->visual;
       -                /* FIXME: is it legal to free this while keeping the visual? */
       -                XFree(xvisinfo_match);
       -                XdbeFreeVisualInfo(info);
       -                found = 1;
       -        } else {
       -                window->vis = DefaultVisual(window->dpy, window->screen);
       -                ltk_warn("No Xdbe support.\n");
       -        }
       -        //printf("%d   %d\n", WidthOfScreen(XDefaultScreenOfDisplay(window->dpy)), WidthMMOfScreen(XDefaultScreenOfDisplay(window->dpy)));
       -        window->cm = DefaultColormap(window->dpy, window->screen);
       +        ltk_renderdata *renderer_create_window(const char *title, int x, int y, unsigned int w, unsigned int h);
       +        void renderer_set_window_properties(ltk_renderdata *renderdata, ltk_color *bg_pixel, ltk_color *border_pixel, unsigned int border_width);
       +        window->renderdata = renderer_create_window(title, x, y, w, h);
                theme_path = ltk_strcat_useful(ltk_dir, "/theme.ini");
                window->theme = &window_theme;
                ltk_load_theme(window, theme_path);
                ltk_free(theme_path);
       +
                /* FIXME: fix theme memory leaks when exit happens between here and the end of this function */
       -        window->wm_delete_msg = XInternAtom(window->dpy, "WM_DELETE_WINDOW", False);
       -
       -        memset(&attrs, 0, sizeof(attrs));
       -        attrs.background_pixel = window->theme->bg.xcolor.pixel;
       -        attrs.colormap = window->cm;
       -        attrs.border_pixel = window->theme->fg.xcolor.pixel;
       -        /* this causes the window contents to be kept
       -         * when it is resized, leading to less flicker */
       -        attrs.bit_gravity = NorthWestGravity;
       -        attrs.event_mask =
       -                ExposureMask | KeyPressMask | KeyReleaseMask |
       -                ButtonPressMask | ButtonReleaseMask |
       -                StructureNotifyMask | PointerMotionMask;
       -        window->depth = DefaultDepth(window->dpy, window->screen);
       -        window->xwindow = XCreateWindow(
       -            window->dpy, DefaultRootWindow(window->dpy), x, y,
       -            w, h, window->theme->border_width, window->depth,
       -            InputOutput, window->vis,
       -            CWBackPixel | CWColormap | CWBitGravity | CWEventMask | CWBorderPixel, &attrs
       -        );
       -
       -        if (found) {
       -                window->back_buf = XdbeAllocateBackBufferName(
       -                        window->dpy, window->xwindow, XdbeBackground
       -                );
       -        } else {
       -                window->back_buf = window->xwindow;
       -        }
       -        window->drawable = window->back_buf;
       -        window->gc = XCreateGC(window->dpy, window->xwindow, 0, 0);
       -        XSetForeground(window->dpy, window->gc, window->theme->fg.xcolor.pixel);
       -        XSetBackground(window->dpy, window->gc, window->theme->bg.xcolor.pixel);
       -        XSetStandardProperties(
       -                window->dpy, window->xwindow,
       -                title, NULL, None, NULL, 0, NULL
       -        );
       -        XSetWMProtocols(window->dpy, window->xwindow, &window->wm_delete_msg, 1);
       +
       +        renderer_set_window_properties(window->renderdata, &window->theme->bg, &window->theme->fg, window->theme->border_width);
       +
                window->root_widget = NULL;
                window->active_widget = NULL;
                window->pressed_widget = NULL;
        
       -        window->surface_cache = ltk_surface_cache_create(window);
       +        window->surface_cache = ltk_surface_cache_create(window->renderdata);
        
                window->other_event = &ltk_window_other_event;
                window->first_event = window->last_event = NULL;
        
       -        window->rect.w = 0;
       -        window->rect.h = 0;
       +        window->rect.w = w;
       +        window->rect.h = h;
                window->rect.x = 0;
                window->rect.y = 0;
                window->dirty_rect.w = 0;
                window->dirty_rect.h = 0;
                window->dirty_rect.x = 0;
                window->dirty_rect.y = 0;
       -        window->surface = ltk_surface_from_window(window);
       +        window->surface = ltk_surface_from_window(window->renderdata, w, h);
        
                window->text_context = ltk_text_context_create(window, window->theme->font);
        
       -        XClearWindow(window->dpy, window->xwindow);
       -        XMapRaised(window->dpy, window->xwindow);
       -
                return window;
        }
        
       t@@ -905,11 +812,9 @@ ltk_destroy_window(ltk_window *window) {
                ltk_text_context_destroy(window->text_context);
                if (window->popups)
                        ltk_free(window->popups);
       -        XFreeGC(window->dpy, window->gc);
       -        XDestroyWindow(window->dpy, window->xwindow);
       -        XCloseDisplay(window->dpy);
       -        ltk_surface_destroy(window->surface);
                ltk_surface_cache_destroy(window->surface_cache);
       +        ltk_surface_destroy(window->surface);
       +        renderer_destroy_window(window->renderdata);
                ltk_free(window);
        }
        
       t@@ -958,16 +863,16 @@ get_hover_popup(ltk_window *window, int x, int y) {
        }
        
        static void
       -ltk_handle_event(ltk_window *window, XEvent event) {
       +ltk_handle_event(ltk_window *window, ltk_event *event) {
                ltk_widget *hover_popup;
                ltk_widget *root_widget = window->root_widget;
       -        switch (event.type) {
       -        case KeyPress:
       +        switch (event->type) {
       +        case LTK_KEYPRESS_EVENT:
                        break;
       -        case KeyRelease:
       +        case LTK_KEYRELEASE_EVENT:
                        break;
       -        case ButtonPress:
       -                hover_popup = get_hover_popup(window, event.xbutton.x, event.xbutton.y);
       +        case LTK_BUTTONPRESS_EVENT:
       +                hover_popup = get_hover_popup(window, event->button.x, event->button.y);
                        if (hover_popup) {
                                ltk_widget_mouse_press_event(hover_popup, event);
                        } else if (root_widget) {
       t@@ -975,15 +880,15 @@ ltk_handle_event(ltk_window *window, XEvent event) {
                                ltk_widget_mouse_press_event(root_widget, event);
                        }
                        break;
       -        case ButtonRelease:
       -                hover_popup = get_hover_popup(window, event.xbutton.x, event.xbutton.y);
       +        case LTK_BUTTONRELEASE_EVENT:
       +                hover_popup = get_hover_popup(window, event->button.x, event->button.y);
                        if (hover_popup)
                                ltk_widget_mouse_release_event(hover_popup, event);
                        else if (root_widget)
                                ltk_widget_mouse_release_event(root_widget, event);
                        break;
       -        case MotionNotify:
       -                hover_popup = get_hover_popup(window, event.xmotion.x, event.xmotion.y);
       +        case LTK_MOTION_EVENT:
       +                hover_popup = get_hover_popup(window, event->motion.x, event->motion.y);
                        if (hover_popup)
                                ltk_widget_motion_notify_event(hover_popup, event);
                        else if (root_widget)
   DIR diff --git a/src/memory.c b/src/memory.c
       t@@ -16,12 +16,9 @@
        
        #include <stdio.h>
        #include <stdlib.h>
       -#include <X11/Xlib.h>
       -#include <X11/Xutil.h>
        #include <stdarg.h>
        #include <string.h>
        #include <stdint.h>
       -#include "color.h"
        #include "util.h"
        #include "memory.h"
        
   DIR diff --git a/src/menu.c b/src/menu.c
       t@@ -21,9 +21,7 @@
        #include <stdarg.h>
        #include <math.h>
        
       -#include <X11/Xlib.h>
       -#include <X11/Xutil.h>
       -
       +#include "event.h"
        #include "memory.h"
        #include "color.h"
        #include "rect.h"
       t@@ -88,15 +86,15 @@ static void ltk_menu_scroll_callback(void *data);
        static void stop_scrolling(ltk_menu *menu);
        static size_t get_entry_at_point(ltk_menu *menu, int x, int y, ltk_rect *entry_rect_ret);
        static int set_scroll_timer(ltk_menu *menu, int x, int y);
       -static int ltk_menu_mouse_release(ltk_widget *self, XEvent event);
       -static int ltk_menu_mouse_press(ltk_widget *self, XEvent event);
       +static int ltk_menu_mouse_release(ltk_widget *self, ltk_event *event);
       +static int ltk_menu_mouse_press(ltk_widget *self, ltk_event *event);
        static void ltk_menu_hide(ltk_widget *self);
        static void popup_active_menu(ltk_menu *menu, ltk_rect r);
        static void unpopup_active_entry(ltk_menu *menu);
        static void handle_hover(ltk_menu *menu, int x, int y);
       -static int ltk_menu_motion_notify(ltk_widget *self, XEvent event);
       -static int ltk_menu_mouse_enter(ltk_widget *self, XEvent event);
       -static int ltk_menu_mouse_leave(ltk_widget *self, XEvent event);
       +static int ltk_menu_motion_notify(ltk_widget *self, ltk_event *event);
       +static int ltk_menu_mouse_enter(ltk_widget *self, ltk_event *event);
       +static int ltk_menu_mouse_leave(ltk_widget *self, ltk_event *event);
        static ltk_menu *ltk_menu_create(ltk_window *window, const char *id, int is_submenu);
        static ltk_menuentry *insert_entry(ltk_menu *menu, size_t idx);
        static void recalc_menu_size(ltk_menu *menu);
       t@@ -604,9 +602,9 @@ set_scroll_timer(ltk_menu *menu, int x, int y) {
        }
        
        static int
       -ltk_menu_mouse_release(ltk_widget *self, XEvent event) {
       +ltk_menu_mouse_release(ltk_widget *self, ltk_event *event) {
                ltk_menu *menu = (ltk_menu *)self;
       -        size_t idx = get_entry_at_point(menu, event.xbutton.x, event.xbutton.y, NULL);
       +        size_t idx = get_entry_at_point(menu, event->button.x, event->button.y, NULL);
                if (idx < menu->num_entries && idx == menu->pressed_entry) {
                        ltk_window_unregister_all_popups(self->window);
                        /* FIXME: give menu id and entry id */
       t@@ -622,33 +620,33 @@ ltk_menu_mouse_release(ltk_widget *self, XEvent event) {
        }
        
        static int
       -ltk_menu_mouse_press(ltk_widget *self, XEvent event) {
       +ltk_menu_mouse_press(ltk_widget *self, ltk_event *event) {
                ltk_menu *menu = (ltk_menu *)self;
                size_t idx;
                /* FIXME: configure scroll step */
       -        switch (event.xbutton.button) {
       -        case 1:
       -                idx = get_entry_at_point(menu, event.xbutton.x, event.xbutton.y, NULL);
       +        switch (event->button.button) {
       +        case LTK_BUTTONL:
       +                idx = get_entry_at_point(menu, event->button.x, event->button.y, NULL);
                        if (idx < menu->num_entries) {
                                menu->pressed_entry = idx;
                                self->dirty = 1;
                        }
                        break;
       -        case 4:
       +        case LTK_BUTTON4:
                        ltk_menu_scroll(menu, 1, 0, 0, 0, 10);
       -                handle_hover(menu, event.xbutton.x, event.xbutton.y);
       +                handle_hover(menu, event->button.x, event->button.y);
                        break;
       -        case 5:
       +        case LTK_BUTTON5:
                        ltk_menu_scroll(menu, 0, 1, 0, 0, 10);
       -                handle_hover(menu, event.xbutton.x, event.xbutton.y);
       +                handle_hover(menu, event->button.x, event->button.y);
                        break;
       -        case 6:
       +        case LTK_BUTTON6:
                        ltk_menu_scroll(menu, 0, 0, 1, 0, 10);
       -                handle_hover(menu, event.xbutton.x, event.xbutton.y);
       +                handle_hover(menu, event->button.x, event->button.y);
                        break;
       -        case 7:
       +        case LTK_BUTTON7:
                        ltk_menu_scroll(menu, 0, 0, 0, 1, 10);
       -                handle_hover(menu, event.xbutton.x, event.xbutton.y);
       +                handle_hover(menu, event->button.x, event->button.y);
                        break;
                default:
                        break;
       t@@ -807,19 +805,19 @@ handle_hover(ltk_menu *menu, int x, int y) {
        }
        
        static int
       -ltk_menu_motion_notify(ltk_widget *self, XEvent event) {
       -        handle_hover((ltk_menu *)self, event.xmotion.x, event.xmotion.y);
       +ltk_menu_motion_notify(ltk_widget *self, ltk_event *event) {
       +        handle_hover((ltk_menu *)self, event->motion.x, event->motion.y);
                return 1;
        }
        
        static int
       -ltk_menu_mouse_enter(ltk_widget *self, XEvent event) {
       -        handle_hover((ltk_menu *)self, event.xbutton.x, event.xbutton.y);
       +ltk_menu_mouse_enter(ltk_widget *self, ltk_event *event) {
       +        handle_hover((ltk_menu *)self, event->motion.x, event->motion.y);
                return 1;
        }
        
        static int
       -ltk_menu_mouse_leave(ltk_widget *self, XEvent event) {
       +ltk_menu_mouse_leave(ltk_widget *self, ltk_event *event) {
                (void)event;
                stop_scrolling((ltk_menu *)self);
                return 1;
   DIR diff --git a/src/scrollbar.c b/src/scrollbar.c
       t@@ -24,6 +24,7 @@
        #include <X11/Xlib.h>
        #include <X11/Xutil.h>
        
       +#include "event.h"
        #include "memory.h"
        #include "color.h"
        #include "rect.h"
       t@@ -36,8 +37,8 @@
        #define MAX_SCROLLBAR_WIDTH 100 /* completely arbitrary */
        
        static void ltk_scrollbar_draw(ltk_widget *self, ltk_rect clip);
       -static int ltk_scrollbar_mouse_press(ltk_widget *self, XEvent event);
       -static int ltk_scrollbar_motion_notify(ltk_widget *self, XEvent event);
       +static int ltk_scrollbar_mouse_press(ltk_widget *self, ltk_event *event);
       +static int ltk_scrollbar_motion_notify(ltk_widget *self, ltk_event *event);
        static void ltk_scrollbar_destroy(ltk_widget *self, int shallow);
        
        static struct ltk_widget_vtable vtable = {
       t@@ -164,12 +165,12 @@ ltk_scrollbar_draw(ltk_widget *self, ltk_rect clip) {
        }
        
        static int
       -ltk_scrollbar_mouse_press(ltk_widget *self, XEvent event) {
       +ltk_scrollbar_mouse_press(ltk_widget *self, ltk_event *event) {
                ltk_scrollbar *sc = (ltk_scrollbar *)self;
                int max_pos;
       -        if (event.xbutton.button != 1)
       +        if (event->button.button != LTK_BUTTONL)
                        return 0;
       -        int ex = event.xbutton.x, ey = event.xbutton.y;
       +        int ex = event->button.x, ey = event->button.y;
                ltk_rect handle_rect = get_handle_rect(sc);
                if (sc->orient == LTK_HORIZONTAL) {
                        if (ex < handle_rect.x || ex > handle_rect.x + handle_rect.w) {
       t@@ -187,8 +188,8 @@ ltk_scrollbar_mouse_press(ltk_widget *self, XEvent event) {
                else if (sc->cur_pos > max_pos)
                        sc->cur_pos = max_pos;
                sc->callback(sc->callback_data);
       -        sc->last_mouse_x = event.xbutton.x;
       -        sc->last_mouse_y = event.xbutton.y;
       +        sc->last_mouse_x = event->button.x;
       +        sc->last_mouse_y = event->button.y;
                return 1;
        }
        
       t@@ -218,19 +219,19 @@ ltk_scrollbar_scroll(ltk_widget *self, int delta, int scaled) {
        }
        
        static int
       -ltk_scrollbar_motion_notify(ltk_widget *self, XEvent event) {
       +ltk_scrollbar_motion_notify(ltk_widget *self, ltk_event *event) {
                ltk_scrollbar *sc = (ltk_scrollbar *)self;
                int delta;
                if (self->state != LTK_PRESSED) {
                        return 1;
                }
                if (sc->orient == LTK_HORIZONTAL)
       -                delta = event.xbutton.x - sc->last_mouse_x;
       +                delta = event->button.x - sc->last_mouse_x;
                else
       -                delta = event.xbutton.y - sc->last_mouse_y;
       +                delta = event->button.y - sc->last_mouse_y;
                ltk_scrollbar_scroll(self, delta, 1);
       -        sc->last_mouse_x = event.xbutton.x;
       -        sc->last_mouse_y = event.xbutton.y;
       +        sc->last_mouse_x = event->button.x;
       +        sc->last_mouse_y = event->button.y;
                return 0;
        }
        
   DIR diff --git a/src/surface_cache.c b/src/surface_cache.c
       t@@ -14,6 +14,7 @@
         * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
         */
        
       +#include "graphics.h"
        #include "surface_cache.h"
        #include "memory.h"
        
       t@@ -50,7 +51,7 @@ struct cache_surface {
        
        struct ltk_surface_cache {
                /* FIXME: many widgets won't use named keys anyways, so this is a bit wasteful */
       -        ltk_window *window;
       +        ltk_renderdata *renderdata;
                struct named_cache_widget_entry named_keys[LTK_NUM_WIDGETS];
                struct cache_surface **surfaces;
                size_t surfaces_num; /* total number of stored surfaces */
       t@@ -68,9 +69,9 @@ struct ltk_surface_cache {
         */
        
        ltk_surface_cache *
       -ltk_surface_cache_create(ltk_window *window) {
       +ltk_surface_cache_create(ltk_renderdata *renderdata) {
                ltk_surface_cache *sc = ltk_malloc(sizeof(ltk_surface_cache));
       -        sc->window = window;
       +        sc->renderdata = renderdata;
                for (int i = 0; i < LTK_NUM_WIDGETS; i++) {
                        sc->named_keys[i].entries = NULL;
                        sc->named_keys[i].entries_num = 0;
       t@@ -194,7 +195,7 @@ ltk_surface_cache_get_surface(ltk_surface_cache_key *key, ltk_surface **s_ret) {
                                c->surfaces[i] = NULL;
                        }
                        struct cache_surface *cs = ltk_malloc(sizeof(struct cache_surface));
       -                cs->s = ltk_surface_create(c->window, key->min_w, key->min_h);
       +                cs->s = ltk_surface_create(c->renderdata, key->min_w, key->min_h);
                        cs->key = key;
                        key->s = cs;
                        c->surfaces[0] = cs;
       t@@ -216,7 +217,7 @@ ltk_surface_cache_get_surface(ltk_surface_cache_key *key, ltk_surface **s_ret) {
                                struct cache_surface *cs = c->surfaces[c->surfaces_num];
                                c->surfaces_num++;
                                c->surfaces_realnum++;
       -                        cs->s = ltk_surface_create(c->window, key->min_w, key->min_h);
       +                        cs->s = ltk_surface_create(c->renderdata, key->min_w, key->min_h);
                                cs->key = key;
                                key->s = cs;
                                c->free_pixels -= (long)key->min_w * key->min_h;
       t@@ -309,7 +310,7 @@ ltk_surface_cache_get_surface(ltk_surface_cache_key *key, ltk_surface **s_ret) {
                                if (!c->surfaces[0])
                                        c->surfaces[0] = ltk_malloc(sizeof(struct cache_surface));
                                struct cache_surface *cs = c->surfaces[0];
       -                        cs->s = ltk_surface_create(c->window, key->min_w, key->min_h);
       +                        cs->s = ltk_surface_create(c->renderdata, key->min_w, key->min_h);
                                cs->key = key;
                                key->s = cs;
                                c->surfaces_num = 1;
   DIR diff --git a/src/surface_cache.h b/src/surface_cache.h
       t@@ -13,7 +13,7 @@ typedef struct ltk_surface_cache_key ltk_surface_cache_key;
        #include "ltk.h"
        #include "graphics.h"
        
       -ltk_surface_cache *ltk_surface_cache_create(ltk_window *window);
       +ltk_surface_cache *ltk_surface_cache_create(ltk_renderdata *renderdata);
        void ltk_surface_cache_destroy(ltk_surface_cache *cache);
        ltk_surface_cache_key *ltk_surface_cache_get_named_key(ltk_surface_cache *cache, ltk_widget_type type, int id, int min_w, int min_h);
        ltk_surface_cache_key *ltk_surface_cache_get_unnamed_key(ltk_surface_cache *cache, int min_w, int min_h);
   DIR diff --git a/src/text.h b/src/text.h
       t@@ -25,6 +25,7 @@ typedef struct ltk_text_line ltk_text_line;
        /* typedef struct ltk_text_context ltk_text_context; */
        
        /* FIXME: something to hold display, etc. to avoid circular reference between window and text context */
       +/* FIXME: this is done now (ltk_renderdata), just need to use it here... */
        
        ltk_text_context *ltk_text_context_create(ltk_window *window, char *default_font);
        void ltk_text_context_destroy(ltk_text_context *ctx);
   DIR diff --git a/src/text_pango.c b/src/text_pango.c
       t@@ -26,6 +26,7 @@
        
        #include <pango/pangoxft.h>
        
       +#include "xlib_shared.h"
        #include "memory.h"
        #include "color.h"
        #include "rect.h"
       t@@ -52,7 +53,7 @@ ltk_text_context *
        ltk_text_context_create(ltk_window *window, char *default_font) {
                ltk_text_context *ctx = ltk_malloc(sizeof(ltk_text_context));
                ctx->window = window;
       -        ctx->fontmap = pango_xft_get_font_map(window->dpy, window->screen);
       +        ctx->fontmap = pango_xft_get_font_map(window->renderdata->dpy, window->renderdata->screen);
                ctx->context = pango_font_map_create_context(ctx->fontmap);
                ctx->default_font = ltk_strdup(default_font);
                return ctx;
   DIR diff --git a/src/text_stb.c b/src/text_stb.c
       t@@ -29,6 +29,7 @@
        #include "khash.h"
        #include "stb_truetype.h" /* http://nothings.org/stb/stb_truetype.h */
        
       +#include "xlib_shared.h"
        #include "memory.h"
        #include "color.h"
        #include "rect.h"
       t@@ -591,7 +592,7 @@ ltk_text_line_draw(ltk_text_line *tl, ltk_surface *s, ltk_color *color, int x, i
                if (w <= 0 || h <= 0)
                        return;
                Drawable d = ltk_surface_get_drawable(s);
       -        XImage *img = XGetImage(tl->ctx->window->dpy, d, x, y, w, h, 0xFFFFFF, ZPixmap);
       +        XImage *img = XGetImage(tl->ctx->window->renderdata->dpy, d, x, y, w, h, 0xFFFFFF, ZPixmap);
        
                int last_break = 0;
                for (int i = 0; i < tl->lines; i++) {
       t@@ -607,7 +608,7 @@ ltk_text_line_draw(ltk_text_line *tl, ltk_surface *s, ltk_color *color, int x, i
                        }
                        last_break = next_break;
                }
       -        XPutImage(tl->ctx->window->dpy, d, tl->ctx->window->gc, img, 0, 0, x, y, w, h);
       +        XPutImage(tl->ctx->window->renderdata->dpy, d, tl->ctx->window->renderdata->gc, img, 0, 0, x, y, w, h);
                XDestroyImage(img);
        }
        
   DIR diff --git a/src/theme.c b/src/theme.c
       t@@ -1,3 +1,5 @@
       +#include "graphics.h"
       +#include "surface_cache.h"
        #include "util.h"
        #include "theme.h"
        #include "memory.h"
       t@@ -48,7 +50,7 @@ ltk_theme_handle_value(ltk_window *window, char *debug_name, const char *prop, c
                        entry->initialized = 1;
                        break;
                case THEME_COLOR:
       -                if (ltk_color_create(window->dpy, window->vis, window->cm, value, entry->ptr.color)) {
       +                if (ltk_color_create(window->renderdata, value, entry->ptr.color)) {
                                ltk_warn("Unable to create color '%s' for property '%s:%s'.\n", value, debug_name, prop);
                                return 1;
                        } else {
       t@@ -110,7 +112,7 @@ ltk_theme_fill_defaults(ltk_window *window, char *debug_name, ltk_theme_parseinf
                                e->initialized = 1;
                                break;
                        case THEME_COLOR:
       -                        if (ltk_color_create(window->dpy, window->vis, window->cm, e->defaultval.color, e->ptr.color)) {
       +                        if (ltk_color_create(window->renderdata, e->defaultval.color, e->ptr.color)) {
                                        ltk_warn("Unable to create default color '%s' for property '%s:%s'.\n", e->defaultval.color, debug_name, e->key);
                                        return 1;
                                } else {
       t@@ -145,7 +147,7 @@ ltk_theme_uninitialize(ltk_window *window, ltk_theme_parseinfo *parseinfo, size_
                                e->initialized = 0;
                                break;
                        case THEME_COLOR:
       -                        ltk_color_destroy(window->dpy, window->vis, window->cm, e->ptr.color);
       +                        ltk_color_destroy(window->renderdata, e->ptr.color);
                                e->initialized = 0;
                                break;
                        case THEME_INT:
   DIR diff --git a/src/theme.h b/src/theme.h
       t@@ -38,6 +38,7 @@ typedef struct {
                int initialized;
        } ltk_theme_parseinfo;
        
       +/* FIXME: this only needs renderdata, not window */
        /* Both return 1 on error, 0 on success */
        int ltk_theme_handle_value(ltk_window *window, char *debug_name, const char *prop, const char *value, ltk_theme_parseinfo *parseinfo, size_t len, int *sorted);
        int ltk_theme_fill_defaults(ltk_window *window, char *debug_name, ltk_theme_parseinfo *parseinfo, size_t len);
   DIR diff --git a/src/widget.c b/src/widget.c
       t@@ -22,11 +22,9 @@
         */
        
        #include <stdarg.h>
       -
       -#include <X11/Xlib.h>
       -#include <X11/Xutil.h>
        #include <stdint.h>
        
       +#include "event.h"
        #include "rect.h"
        #include "widget.h"
        #include "color.h"
       t@@ -129,20 +127,20 @@ ltk_widget_change_state(ltk_widget *widget) {
        }
        
        void
       -ltk_widget_mouse_press_event(ltk_widget *widget, XEvent event) {
       +ltk_widget_mouse_press_event(ltk_widget *widget, ltk_event *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)
       +                if (event->button.button == LTK_BUTTONL)
                                ltk_window_set_pressed_widget(widget->window, widget);
                }
        }
        
        void
       -ltk_widget_mouse_release_event(ltk_widget *widget, XEvent event) {
       +ltk_widget_mouse_release_event(ltk_widget *widget, ltk_event *event) {
                if (!widget || widget->state == LTK_DISABLED)
                        return;
                if (widget->vtable->mouse_release)
       t@@ -151,7 +149,7 @@ ltk_widget_mouse_release_event(ltk_widget *widget, XEvent event) {
        }
        
        void
       -ltk_widget_motion_notify_event(ltk_widget *widget, XEvent event) {
       +ltk_widget_motion_notify_event(ltk_widget *widget, ltk_event *event) {
                /* FIXME: THIS WHOLE STATE HANDLING IS STILL PARTIALLY BROKEN */
                /* FIXME: need to bring back hover state to make enter/leave work properly */
                /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
   DIR diff --git a/src/widget.h b/src/widget.h
       t@@ -17,9 +17,8 @@
        #ifndef _LTK_WIDGET_H_
        #define _LTK_WIDGET_H_
        
       -#include <X11/Xlib.h>
       -#include <X11/Xutil.h>
        #include "rect.h"
       +#include "event.h"
        
        /* FIXME: SORT OUT INCLUDES PROPERLY! */
        
       t@@ -85,14 +84,16 @@ struct ltk_widget {
                char hidden;
        };
        
       +/* FIXME: just give the structs for the actual event type here instead
       +   of the generic ltk_event */
        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 (*motion_notify) (struct ltk_widget *, XEvent);
       -        int (*mouse_leave) (struct ltk_widget *, XEvent);
       -        int (*mouse_enter) (struct ltk_widget *, XEvent);
       +        void (*key_press) (struct ltk_widget *, ltk_event *);
       +        void (*key_release) (struct ltk_widget *, ltk_event *);
       +        int (*mouse_press) (struct ltk_widget *, ltk_event *);
       +        int (*mouse_release) (struct ltk_widget *, ltk_event *);
       +        int (*motion_notify) (struct ltk_widget *, ltk_event *);
       +        int (*mouse_leave) (struct ltk_widget *, ltk_event *);
       +        int (*mouse_enter) (struct ltk_widget *, ltk_event *);
        
                void (*resize) (struct ltk_widget *);
                void (*hide) (struct ltk_widget *);
       t@@ -113,9 +114,9 @@ int ltk_widget_destroy_cmd(struct ltk_window *window, char **tokens, size_t num_
        void ltk_fill_widget_defaults(ltk_widget *widget, const char *id, struct ltk_window *window,
            struct ltk_widget_vtable *vtable, int w, int h);
        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);
       +void ltk_widget_mouse_press_event(ltk_widget *widget, ltk_event *event);
       +void ltk_widget_mouse_release_event(ltk_widget *widget, ltk_event *event);
       +void ltk_widget_motion_notify_event(ltk_widget *widget, ltk_event *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);
   DIR diff --git a/src/xlib_shared.h b/src/xlib_shared.h
       t@@ -0,0 +1,20 @@
       +#ifndef XLIB_SHARED_H
       +#define XLIB_SHARED_H
       +
       +#include <X11/Xlib.h>
       +#include <X11/extensions/Xdbe.h>
       +
       +struct ltk_renderdata {
       +        Display *dpy;
       +        Visual *vis;
       +        Colormap cm;
       +        GC gc;
       +        int screen;
       +        Atom wm_delete_msg;
       +        Window xwindow;
       +        XdbeBackBuffer back_buf;
       +        Drawable drawable;
       +        int depth;
       +};
       +
       +#endif /* XLIB_SHARED_H */