commit 994e235d9939788238dd2c66cdefb4cc4ade1bce
parent 3a21eba1783e1d29204d54e006fe9f5efb016fc3
Author: lumidify <nobody@lumidify.org>
Date:   Sun,  5 Jun 2022 23:45:33 +0200
Abstract event handling a bit
Diffstat:
27 files changed, 594 insertions(+), 385 deletions(-)
diff --git a/Makefile b/Makefile
@@ -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 \
@@ -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 \
 
@@ -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
diff --git a/src/box.c b/src/box.c
@@ -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"
@@ -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,
@@ -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;
 	}
@@ -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;
 		}
@@ -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, <k_widget_mouse_press_event);
+		return ltk_box_mouse_event(box, event->button.x, event->button.y, event, <k_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, <k_widget_mouse_release_event);
+	return ltk_box_mouse_event(box, event->button.x, event->button.y, event, <k_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, <k_widget_motion_notify_event);
+	return ltk_box_mouse_event(box, event->motion.x, event->motion.y, event, <k_widget_motion_notify_event);
 }
 
 /* box <box id> add <widget id> [sticky] */
diff --git a/src/button.c b/src/button.c
@@ -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"
@@ -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);
@@ -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;
 	}
diff --git a/src/color.c b/src/color.c
@@ -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
-}
diff --git a/src/color.h b/src/color.h
@@ -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
@@ -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_ */
diff --git a/src/color_xlib.c b/src/color_xlib.c
@@ -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
+}
diff --git a/src/event.h b/src/event.h
@@ -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 */
diff --git a/src/event_xlib.c b/src/event_xlib.c
@@ -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;
+	}
+}
diff --git a/src/graphics.h b/src/graphics.h
@@ -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>
@@ -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);
@@ -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 */
diff --git a/src/graphics_xlib.c b/src/graphics_xlib.c
@@ -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;
@@ -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;
@@ -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;
@@ -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;
 }
@@ -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
@@ -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
@@ -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);
 }
@@ -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
 	);
 }
@@ -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;
+}
diff --git a/src/grid.c b/src/grid.c
@@ -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"
@@ -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 = <k_grid_draw,
@@ -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)
@@ -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)
@@ -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)
diff --git a/src/label.c b/src/label.c
@@ -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"
diff --git a/src/ltk.h b/src/ltk.h
@@ -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;
@@ -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;
@@ -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);
diff --git a/src/ltkd.c b/src/ltkd.c
@@ -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"
@@ -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);
@@ -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 */
@@ -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;
@@ -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();
@@ -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;
@@ -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)) {
@@ -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);
@@ -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;
@@ -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);
@@ -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) {
@@ -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);
 	}
 }
@@ -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));
 
@@ -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 = <k_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;
 }
 
@@ -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);
 }
 
@@ -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) {
@@ -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)
diff --git a/src/memory.c b/src/memory.c
@@ -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"
 
diff --git a/src/menu.c b/src/menu.c
@@ -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"
@@ -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);
@@ -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 */
@@ -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;
@@ -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;
diff --git a/src/scrollbar.c b/src/scrollbar.c
@@ -24,6 +24,7 @@
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 
+#include "event.h"
 #include "memory.h"
 #include "color.h"
 #include "rect.h"
@@ -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 = {
@@ -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) {
@@ -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;
 }
 
@@ -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;
 }
 
diff --git a/src/surface_cache.c b/src/surface_cache.c
@@ -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"
 
@@ -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 */
@@ -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;
@@ -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;
@@ -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;
@@ -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;
diff --git a/src/surface_cache.h b/src/surface_cache.h
@@ -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);
diff --git a/src/text.h b/src/text.h
@@ -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);
diff --git a/src/text_pango.c b/src/text_pango.c
@@ -26,6 +26,7 @@
 
 #include <pango/pangoxft.h>
 
+#include "xlib_shared.h"
 #include "memory.h"
 #include "color.h"
 #include "rect.h"
@@ -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;
diff --git a/src/text_stb.c b/src/text_stb.c
@@ -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"
@@ -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++) {
@@ -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);
 }
 
diff --git a/src/theme.c b/src/theme.c
@@ -1,3 +1,5 @@
+#include "graphics.h"
+#include "surface_cache.h"
 #include "util.h"
 #include "theme.h"
 #include "memory.h"
@@ -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 {
@@ -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 {
@@ -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:
diff --git a/src/theme.h b/src/theme.h
@@ -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);
diff --git a/src/widget.c b/src/widget.c
@@ -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"
@@ -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)
@@ -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 */
 	/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
diff --git a/src/widget.h b/src/widget.h
@@ -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! */
 
@@ -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 *);
@@ -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);
diff --git a/src/xlib_shared.h b/src/xlib_shared.h
@@ -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 */