commit d1f0903f23c0887590329483d8993cbefb1b6189
parent 00cefe189580f8332951122af1f6d6230d9652c3
Author: lumidify <nobody@lumidify.org>
Date:   Thu,  5 May 2022 13:14:22 +0200
Slightly improve graphics and text interface
Diffstat:
14 files changed, 311 insertions(+), 311 deletions(-)
diff --git a/Makefile b/Makefile
@@ -18,7 +18,8 @@ LDFLAGS += -lm `pkg-config --libs x11 fontconfig xext`
 # Note: this macro magic for debugging and pango rendering seems ugly; it should probably be changed
 
 # debug
-DEV_1 = -g -Wall -Werror -Wextra -pedantic
+DEV_1 = -g -Wall -Wextra -pedantic
+#-Werror
 
 # stb rendering
 EXTRA_OBJ_0 = src/stb_truetype.o src/text_stb.o
diff --git a/src/box.h b/src/box.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 lumidify <nobody@lumidify.org>
+ * 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
@@ -22,6 +22,8 @@
 typedef struct {
 	ltk_widget widget;
 	ltk_scrollbar *sc;
+	ltk_widget *pressed_widget;
+	ltk_widget *active_widget;
 	ltk_widget **widgets;
 	size_t num_alloc;
 	size_t num_widgets;
diff --git a/src/button.c b/src/button.c
@@ -37,7 +37,7 @@
 static void ltk_button_draw(ltk_widget *self, ltk_rect clip);
 static int ltk_button_mouse_release(ltk_widget *self, XEvent event);
 static ltk_button *ltk_button_create(ltk_window *window,
-    const char *id, const char *text);
+    const char *id, char *text);
 static void ltk_button_destroy(ltk_widget *self, int shallow);
 static void ltk_button_change_state(ltk_widget *self);
 static void ltk_button_redraw_surface(ltk_button *button, ltk_surface *s);
@@ -142,12 +142,11 @@ ltk_button_draw(ltk_widget *self, ltk_rect clip) {
 	ltk_surface *s;
 	if (!ltk_surface_cache_get_surface(self->surface_key, &s) || self->dirty)
 		ltk_button_redraw_surface(button, s);
-	ltk_surface_copy_to_window(s, self->window, ltk_rect_relative(rect, clip_final), clip_final.x, clip_final.y);
+	ltk_surface_copy(s, self->window->surface, ltk_rect_relative(rect, clip_final), clip_final.x, clip_final.y);
 }
 
 static void
 ltk_button_redraw_surface(ltk_button *button, ltk_surface *s) {
-	ltk_window *window = button->widget.window;
 	ltk_rect rect = button->widget.rect;
 	int bw = theme.border_width;
 	ltk_color *border = NULL, *fill = NULL;
@@ -181,33 +180,12 @@ ltk_button_redraw_surface(ltk_button *button, ltk_surface *s) {
 	ltk_text_line_get_size(button->tl, &text_w, &text_h);
 	int text_x = (rect.w - text_w) / 2;
 	int text_y = (rect.h - text_h) / 2;
-	/* FIXME: Remove clipping rect from text line - this is just used here as a dummy
-	   because it is completely ignored */
-	ltk_text_line_draw(button->tl, s, window->gc, text_x, text_y, rect);
+	ltk_text_line_draw(button->tl, s, &theme.text_color, text_x, text_y);
 	button->widget.dirty = 0;
 }
 
 static void
 ltk_button_change_state(ltk_widget *self) {
-	ltk_button *button = (ltk_button *)self;
-	ltk_color *fill = NULL;
-	switch (button->widget.state) {
-	case LTK_NORMAL:
-		fill = &theme.fill;
-		break;
-	case LTK_PRESSED:
-		fill = &theme.fill_pressed;
-		break;
-	case LTK_ACTIVE:
-		fill = &theme.fill_active;
-		break;
-	case LTK_DISABLED:
-		fill = &theme.fill_disabled;
-		break;
-	default:
-		ltk_fatal("No style found for button!\n");
-	}
-	ltk_text_line_change_colors(button->tl, &theme.text_color, fill);
 	self->dirty = 1;
 }
 
@@ -220,20 +198,17 @@ ltk_button_mouse_release(ltk_widget *self, XEvent event) {
 }
 
 static ltk_button *
-ltk_button_create(ltk_window *window, const char *id, const char *text) {
-	char *text_copy;
+ltk_button_create(ltk_window *window, const char *id, char *text) {
 	ltk_button *button = ltk_malloc(sizeof(ltk_button));
 
 	uint16_t font_size = window->theme.font_size;
-	text_copy = ltk_strdup(text);
-	button->tl = ltk_text_line_create(window->xwindow, font_size, text_copy, -1, &theme.text_color, &theme.fill);
+	button->tl = ltk_text_line_create(window->text_context, font_size, text, 0, -1);
 	int text_w, text_h;
 	ltk_text_line_get_size(button->tl, &text_w, &text_h);
 	button->widget.ideal_w = text_w + theme.border_width * 2 + theme.pad * 2;
 	button->widget.ideal_h = text_h + theme.border_width * 2 + theme.pad * 2;
 	ltk_fill_widget_defaults(&button->widget, id, window, &vtable, button->widget.ideal_w, button->widget.ideal_h);
-	/* render text */
-	ltk_button_change_state((ltk_widget *)button);
+	button->widget.dirty = 1;
 
 	return button;
 }
diff --git a/src/color.h b/src/color.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 lumidify <nobody@lumidify.org>
+ * 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
@@ -17,13 +17,15 @@
 #ifndef _LTK_COLOR_H_
 #define _LTK_COLOR_H_
 
-#if USE_PANGO == 1
+#include "compat.h"
+
+#if USE_XFT == 1
   #include <X11/Xft/Xft.h>
 #endif
 
 typedef struct {
 	XColor xcolor;
-	#if USE_PANGO == 1
+	#if USE_XFT == 1
 	XftColor xftcolor;
 	#endif
 } ltk_color;
diff --git a/src/compat.h b/src/compat.h
@@ -0,0 +1,9 @@
+#ifdef _LTK_COMPAT_H_
+#define _LTK_COMPAT_H_
+
+#if USE_PANGO == 1
+#undef USE_XFT
+#define USE_XFT 1
+#endif
+
+#endif
diff --git a/src/graphics.h b/src/graphics.h
@@ -17,30 +17,31 @@
 #ifndef _LTK_GRAPHICS_H_
 #define _LTK_GRAPHICS_H_
 
+/* FIXME: make this global and use it elsewhere */
+#define USE_XLIB_GRAPHICS 1
+
 /* FIXME: Is it faster to take ltk_color* or ltk_color? */
 
 #include <X11/Xft/Xft.h>
 #include "rect.h"
 #include "color.h"
 #include "ltk.h"
+#include "compat.h"
 
-typedef struct ltk_surface ltk_surface;
+/* typedef struct ltk_surface ltk_surface; */
 
 /* FIXME: graphics context */
 ltk_surface *ltk_surface_create(ltk_window *window, int w, int h);
 void ltk_surface_destroy(ltk_surface *s);
-void ltk_surface_resize(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_copy_to_window(ltk_surface *src, ltk_window *dst, ltk_rect src_rect, int dst_x, int dst_y);
+/* 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);
 void ltk_surface_get_size(ltk_surface *s, int *w, int *h);
-
-/* The ltk_surface* and ltk_window* functions do the same things, but a window cannot
-   be wrapped in an ltk_surface since the resize function wouldn't make sense there */
-/* FIXME: avoid this ugliness */
+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);
 void ltk_surface_fill_rect(ltk_surface *s, ltk_color *c, ltk_rect rect);
-void ltk_window_draw_rect(ltk_window *window, ltk_color *c, ltk_rect rect, int line_width);
-void ltk_window_fill_rect(ltk_window *window, ltk_color *c, ltk_rect rect);
 
 /* TODO */
 /*
@@ -50,10 +51,12 @@ void ltk_surface_draw_circle(ltk_surface *s, ltk_color *c, int xc, int yc, int r
 void ltk_surface_fill_circle(ltk_surface *s, ltk_color *c, int xc, int yc, int r);
 */
 
-#if USE_PANGO == 1
+/* FIXME: only generate draw if needed */
+#if USE_XFT == 1
 XftDraw *ltk_surface_get_xft_draw(ltk_surface *s);
 #endif
-/* FIXME: only expose this when needed */
-Pixmap ltk_surface_get_pixmap(ltk_surface *s);
+#if USE_XLIB_GRAPHICS == 1
+Drawable ltk_surface_get_drawable(ltk_surface *s);
+#endif
 
 #endif /* _LTK_GRAPHICS_H_ */
diff --git a/src/graphics_xlib.c b/src/graphics_xlib.c
@@ -23,14 +23,16 @@
 #include "widget.h"
 #include "ltk.h"
 #include "memory.h"
+#include "compat.h"
 
 struct ltk_surface {
-        int w, h;
-        ltk_window *window;
-        Pixmap p;
-        #if USE_PANGO == 1
-        XftDraw *xftdraw;
-        #endif
+	int w, h;
+	ltk_window *window;
+	Drawable d;
+	#if USE_XFT == 1
+	XftDraw *xftdraw;
+	#endif
+	char resizable;
 };
 
 ltk_surface *
@@ -39,31 +41,56 @@ ltk_surface_create(ltk_window *window, int w, int h) {
 	s->w = w;
 	s->h = h;
 	s->window = window;
-	s->p = XCreatePixmap(window->dpy, window->xwindow, w, h, window->depth);
-	#if USE_PANGO == 1
-	s->xftdraw = XftDrawCreate(window->dpy, s->p, window->vis, window->cm);
+	s->d = XCreatePixmap(window->dpy, window->xwindow, w, h, window->depth);
+	#if USE_XFT == 1
+	s->xftdraw = XftDrawCreate(window->dpy, s->d, window->vis, window->cm);
 	#endif
+	s->resizable = 1;
+	return s;
+}
+
+ltk_surface *
+ltk_surface_from_window(ltk_window *window) {
+	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;
+	#if USE_XFT == 1
+	s->xftdraw = XftDrawCreate(window->dpy, s->d, window->vis, window->cm);
+	#endif
+	s->resizable = 0;
 	return s;
 }
 
 void
 ltk_surface_destroy(ltk_surface *s) {
-	#if USE_PANGO == 1
+	#if USE_XFT == 1
 	XftDrawDestroy(s->xftdraw);
 	#endif
-	XFreePixmap(s->window->dpy, s->p);
+	if (s->resizable)
+		XFreePixmap(s->window->dpy, (Pixmap)s->d);
 	ltk_free(s);
 }
 
 void
+ltk_surface_update_size(ltk_surface *s, int w, int h) {
+	s->w = w;
+	s->h = h;
+}
+
+int
 ltk_surface_resize(ltk_surface *s, int w, int h) {
+	if (!s->resizable)
+		return 1;
 	s->w = w;
 	s->h = h;
-	XFreePixmap(s->window->dpy, s->p);
-	s->p = XCreatePixmap(s->window->dpy, s->window->xwindow, w, h, s->window->depth);
-	#if USE_PANGO == 1
-	XftDrawChange(s->xftdraw, s->p);
+	XFreePixmap(s->window->dpy, (Pixmap)s->d);
+	s->d = XCreatePixmap(s->window->dpy, s->window->xwindow, w, h, s->window->depth);
+	#if USE_XFT == 1
+	XftDrawChange(s->xftdraw, s->d);
 	#endif
+	return 0;
 }
 
 void
@@ -76,13 +103,13 @@ 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->p, s->window->gc, rect.x, rect.y, rect.w, rect.h);
+	XDrawRectangle(s->window->dpy, s->d, s->window->gc, rect.x, rect.y, rect.w, 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->p, s->window->gc, rect.x, rect.y, rect.w, rect.h);
+	XFillRectangle(s->window->dpy, s->d, s->window->gc, rect.x, rect.y, rect.w, rect.h);
 }
 
 void
@@ -98,6 +125,14 @@ ltk_window_fill_rect(ltk_window *window, ltk_color *c, ltk_rect rect) {
 	XFillRectangle(window->dpy, window->drawable, window->gc, rect.x, rect.y, rect.w, rect.h);
 }
 
+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_rect.x, src_rect.y, src_rect.w, src_rect.h, dst_x, dst_y
+	);
+}
+
 /* TODO */
 /*
 void
@@ -117,30 +152,16 @@ ltk_surface_fill_circle(ltk_surface *s, ltk_color *c, int xc, int yc, int r) {
 }
 */
 
-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->p, dst->p, src->window->gc,
-	    src_rect.x, src_rect.y, src_rect.w, src_rect.h, dst_x, dst_y
-	);
-}
-
-void
-ltk_surface_copy_to_window(ltk_surface *src, ltk_window *dst, ltk_rect src_rect, int dst_x, int dst_y) {
-	XCopyArea(
-	    src->window->dpy, src->p, dst->drawable, src->window->gc,
-	    src_rect.x, src_rect.y, src_rect.w, src_rect.h, dst_x, dst_y
-	);
-}
-
-#if USE_PANGO == 1
+#if USE_XFT == 1
 XftDraw *
 ltk_surface_get_xft_draw(ltk_surface *s) {
 	return s->xftdraw;
 }
 #endif
 
-Pixmap
-ltk_surface_get_pixmap(ltk_surface *s) {
-	return s->p;
+#if USE_XLIB_GRAPHICS == 1
+Drawable
+ltk_surface_get_drawable(ltk_surface *s) {
+	return s->d;
 }
+#endif
diff --git a/src/label.c b/src/label.c
@@ -36,7 +36,7 @@
 
 static void ltk_label_draw(ltk_widget *self, ltk_rect clip);
 static ltk_label *ltk_label_create(ltk_window *window,
-    const char *id, const char *text);
+    const char *id, char *text);
 static void ltk_label_destroy(ltk_widget *self, int shallow);
 static void ltk_label_redraw_surface(ltk_label *label, ltk_surface *s);
 
@@ -86,7 +86,7 @@ ltk_label_draw(ltk_widget *self, ltk_rect clip) {
 	ltk_surface *s;
 	if (!ltk_surface_cache_get_surface(self->surface_key, &s) || self->dirty)
 		ltk_label_redraw_surface(label, s);
-	ltk_surface_copy_to_window(s, self->window, ltk_rect_relative(rect, clip_final), clip_final.x, clip_final.y);
+	ltk_surface_copy(s, self->window->surface, ltk_rect_relative(rect, clip_final), clip_final.x, clip_final.y);
 }
 
 static void
@@ -100,17 +100,15 @@ ltk_label_redraw_surface(ltk_label *label, ltk_surface *s) {
 	ltk_text_line_get_size(label->tl, &text_w, &text_h);
 	int text_x = (r.w - text_w) / 2;
 	int text_y = (r.h - text_h) / 2;
-	ltk_text_line_draw(label->tl, s, label->widget.window->gc, text_x, text_y, r);
+	ltk_text_line_draw(label->tl, s, &theme.text_color, text_x, text_y);
 }
 
 static ltk_label *
-ltk_label_create(ltk_window *window, const char *id, const char *text) {
-	char *text_copy;
+ltk_label_create(ltk_window *window, const char *id, char *text) {
 	ltk_label *label = ltk_malloc(sizeof(ltk_label));
 
 	uint16_t font_size = window->theme.font_size;
-	text_copy = ltk_strdup(text);
-	label->tl = ltk_text_line_create(window->xwindow, font_size, text_copy, -1, &theme.text_color, &theme.bg_color);
+	label->tl = ltk_text_line_create(window->text_context, font_size, text, 0, -1);
 	int text_w, text_h;
 	ltk_text_line_get_size(label->tl, &text_w, &text_h);
 	label->widget.ideal_w = text_w + theme.pad * 2;
diff --git a/src/ltk.h b/src/ltk.h
@@ -56,10 +56,16 @@ struct ltk_event_queue {
   though, so it's just going to stay that way.
 */
 
+/* FIXME: fix this ugliness; remove circular dependencies */
+typedef struct ltk_text_context ltk_text_context;
+typedef struct ltk_surface ltk_surface;
+
 typedef struct ltk_window {
 	Display *dpy;
 	Visual *vis;
 	ltk_surface_cache *surface_cache;
+	ltk_text_context *text_context;
+	ltk_surface *surface;
 	Colormap cm;
 	GC gc;
 	int screen;
diff --git a/src/ltkd.c b/src/ltkd.c
@@ -506,6 +506,7 @@ ltk_window_other_event(ltk_window *window, XEvent event) {
 			window->rect.w = w;
 			window->rect.h = h;
 			ltk_window_invalidate_rect(window, window->rect);
+			ltk_surface_update_size(window->surface, w, h);
 			if (ptr) {
 				ptr->rect.w = w;
 				ptr->rect.h = h;
@@ -616,10 +617,7 @@ ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int 
 
 	window->surface_cache = ltk_surface_cache_create(window);
 
-	ltk_init_text(window->theme.font, window->dpy, window->screen, window->cm);
-
 	window->other_event = <k_window_other_event;
-
 	window->first_event = window->last_event = NULL;
 
 	window->rect.w = 0;
@@ -630,6 +628,9 @@ ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int 
 	window->dirty_rect.h = 0;
 	window->dirty_rect.x = 0;
 	window->dirty_rect.y = 0;
+	window->surface = ltk_surface_from_window(window);
+
+	window->text_context = ltk_text_context_create(window, window->theme.font);
 
 	XClearWindow(window->dpy, window->xwindow);
 	XMapRaised(window->dpy, window->xwindow);
@@ -639,8 +640,8 @@ ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int 
 
 static void
 ltk_destroy_window(ltk_window *window) {
+	ltk_text_context_destroy(window->text_context);
 	XDestroyWindow(window->dpy, window->xwindow);
-	ltk_cleanup_text();
 	XCloseDisplay(window->dpy);
 	/* FIXME: This doesn't work because it can sometimes be a readonly
 	   string from ltk_window_setup_theme_defaults! */
diff --git a/src/scrollbar.c b/src/scrollbar.c
@@ -160,7 +160,7 @@ ltk_scrollbar_draw(ltk_widget *self, ltk_rect clip) {
 	}
 	ltk_surface_fill_rect(s, fg, (ltk_rect){handle_x, handle_y, handle_w, handle_h});
 	ltk_rect clip_final = ltk_rect_intersect(clip, rect);
-	ltk_surface_copy_to_window(s, self->window, ltk_rect_relative(rect, clip_final), clip_final.x, clip_final.y);
+	ltk_surface_copy(s, self->window->surface, ltk_rect_relative(rect, clip_final), clip_final.x, clip_final.y);
 }
 
 static int
@@ -200,11 +200,12 @@ static int
 ltk_scrollbar_motion_notify(ltk_widget *self, XEvent event) {
 	(void)self;
 	(void)event;
-	/*
+	ltk_scrollbar *sc = (ltk_scrollbar *)self;
 	double scale;
 	int delta, max_pos;
-	if (sc->widget.state != LTK_PRESSED)
+	if (self->state != LTK_PRESSED) {
 		return 1;
+	}
 	if (sc->orient == LTK_HORIZONTAL) {
 		delta = event.xbutton.x - sc->last_mouse_x;
 		max_pos = sc->virtual_size > sc->widget.rect.w ? sc->virtual_size - sc->widget.rect.w : 0;
@@ -223,7 +224,6 @@ ltk_scrollbar_motion_notify(ltk_widget *self, XEvent event) {
 	sc->last_mouse_y = event.xbutton.y;
 	if (delta > 0)
 		sc->callback(sc->callback_data);
-	*/
 	return 1;
 }
 
diff --git a/src/text.h b/src/text.h
@@ -19,22 +19,39 @@
 
 #include "color.h"
 #include "graphics.h"
+#include "ltk.h"
 
 typedef struct ltk_text_line ltk_text_line;
+/* typedef struct ltk_text_context ltk_text_context; */
 
-/* FIXME: remove x-specific stuff from interface */
-void ltk_init_text(const char *default_font, Display *dpy, int screen, Colormap cm);
-void ltk_cleanup_text(void);
-ltk_text_line *ltk_text_line_create(Window window, uint16_t font_size, char *text, int width, ltk_color *fg, ltk_color *bg);
-/* FIXME: either implement clip rect or remove it from arguments */
-void ltk_text_line_draw(ltk_text_line *tl, ltk_surface *s, GC gc, int x, int y, ltk_rect clip);
+/* FIXME: something to hold display, etc. to avoid circular reference between window and text context */
+
+ltk_text_context *ltk_text_context_create(ltk_window *window, char *default_font);
+void ltk_text_context_destroy(ltk_text_context *ctx);
+
+/* FIXME: allow to give length of text */
+ltk_text_line *ltk_text_line_create(ltk_text_context *ctx, uint16_t font_size, char *text, int take_over_text, int width);
 void ltk_text_line_set_width(ltk_text_line *tl, int width);
 void ltk_text_line_get_size(ltk_text_line *tl, int *w, int *h);
 void ltk_text_line_destroy(ltk_text_line *tl);
-void ltk_text_line_change_colors(ltk_text_line *tl, ltk_color *fg, ltk_color *bg);
 
-#if USE_PANGO == 1
-  #include <pango/pangoxft.h>
-#endif
+/* Draw the entire line to a surface. */
+void ltk_text_line_draw(ltk_text_line *tl, ltk_surface *s, ltk_color *color, int x, int y);
+
+/* Get the smallest rectangle of the line that can be drawn while covering 'clip'.
+ * The Pango backend will currently always return the full rectangle for the line
+ * because it doesn't support clipping. Other backends may just return a slightly
+ * larger rectangle so they can draw full glyphs. */
+ltk_rect ltk_text_line_get_minimal_clip_rect(ltk_text_line *tl, ltk_rect clip);
+
+/* Draw a line onto a surface at position x,y and clipped to 'clip'. Note that
+ * clipping isn't supported properly, so the drawn part of the line may be
+ * larger than 'clip'. In order to find out the exact size of the drawn section,
+ * use ltk_rect_line_get_minimal_clip_rect. */
+void ltk_text_line_draw_clipped(ltk_text_line *tl, ltk_surface *s, ltk_color *color, int x, int y, ltk_rect clip);
+
+/* FIXME: Any way to implement clipping properly? The text would need to be drawn
+   to an intermediate surface, but then we lose alpha blending with the background
+   (unless another library like cairo is used, which I want to avoid). */
 
 #endif /* _LTK_TEXT_H_ */
diff --git a/src/text_pango.c b/src/text_pango.c
@@ -35,96 +35,87 @@
 #include "text.h"
 
 struct ltk_text_line {
+	ltk_text_context *ctx;
 	char *text;
-	uint16_t font_size;
-	int w;
-	int h;
-	Window window;
 	PangoLayout *layout;
-	ltk_color *fg;
-	ltk_color *bg;
+	uint16_t font_size;
 };
 
-struct {
+struct ltk_text_context {
+	ltk_window *window;
 	PangoFontMap *fontmap;
 	PangoContext *context;
 	char *default_font;
-	Display *dpy;
-	int screen;
-	Colormap cm;
-} tm = {NULL, NULL, NULL, NULL, 0, 0};
+};
 
 void
-ltk_init_text(const char *default_font, Display *dpy, int screen, Colormap cm) {
-	tm.fontmap = pango_xft_get_font_map(dpy, screen);
-	tm.context = pango_font_map_create_context(tm.fontmap);
-	tm.default_font = ltk_strdup(default_font);
-	tm.dpy = dpy;
-	tm.screen = screen;
-	tm.cm = cm;
+ltk_text_context_create(ltk_window *window, const 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->context = pango_font_map_create_context(ctx->fontmap);
+	ctx->default_font = ltk_strdup(default_font);
 }
 
 void
-ltk_cleanup_text(void) {
-	if (tm.default_font) ltk_free(tm.default_font);
-	/* FIXME: destroy fontmap and context */
+ltk_text_context_destroy(ltk_text_context *ctx) {
+	ltk_free(ctx->default_font);
+	g_object_unref(ctx->fontmap);
+	g_object_unref(ctx->context);
+	ltk_free(ctx);
 }
 
 void
 ltk_text_line_set_width(ltk_text_line *tl, int width) {
 	pango_layout_set_width(tl->layout, width * PANGO_SCALE);
-	/* FIXME: this is very inefficient because it immediately
-	   accesses the size, forcing pango to recalculate it even
-	   if it may never be needed before e.g. changing text */
-	pango_layout_get_size(tl->layout, &tl->w, &tl->h);
-	tl->w /= PANGO_SCALE;
-	tl->h /= PANGO_SCALE;
-	tl->w = tl->w == 0 ? 1 : tl->w;
-	tl->h = tl->h == 0 ? 1 : tl->h;
-}
-
-void
-ltk_text_line_change_colors(ltk_text_line *tl, ltk_color *fg, ltk_color *bg) {
-	tl->fg = fg;
-	tl->bg = bg;
 }
 
 ltk_text_line *
-ltk_text_line_create(Window window, uint16_t font_size, char *text, int width, ltk_color *fg, ltk_color *bg) {
-	if (!tm.context)
-		ltk_fatal("ltk_text_line_create (pango): text not initialized yet");
-	ltk_text_line *line = ltk_malloc(sizeof(ltk_text_line));
-	line->text = text;
-	line->font_size = font_size;
-	line->layout = pango_layout_new(tm.context);
-
-	PangoFontDescription *desc = pango_font_description_from_string(tm.default_font);
+ltk_text_line_create(ltk_text_context *ctx, uint16_t font_size, char *text, int take_over_text, int width) {
+	ltk_text_line *tl = ltk_malloc(sizeof(ltk_text_line));
+	if (take_over_text)
+		tl->text = text;
+	else
+		tl->text = ltk_strdup(text);
+	tl->font_size = font_size;
+	tl->layout = pango_layout_new(ctx->context);
+
+	PangoFontDescription *desc = pango_font_description_from_string(ctx->default_font);
 	pango_font_description_set_size(desc, font_size * PANGO_SCALE);
-	pango_layout_set_font_description(line->layout, desc);
+	pango_layout_set_font_description(tl->layout, desc);
 	pango_font_description_free(desc);
-	line->window = window;
-	pango_layout_set_wrap(line->layout, PANGO_WRAP_WORD_CHAR);
-	pango_layout_set_text(line->layout, text, -1);
-	ltk_text_line_set_width(line, width);
-	line->fg = fg;
-	line->bg = bg;
-
-	return line;
+	tl->ctx = ctx;
+	pango_layout_set_wrap(tl->layout, PANGO_WRAP_WORD_CHAR);
+	pango_layout_set_text(tl->layout, text, -1);
+	ltk_text_line_set_width(tl, width * PANGO_SCALE);
+
+	return tl;
 }
 
-/* FIXME: bg isn't used right now */
 void
-ltk_text_line_draw(ltk_text_line *tl, ltk_surface *s, GC gc, int x, int y, ltk_rect clip) {
-	(void)clip; /* FIXME: use this */
-	(void)gc;
+ltk_text_line_draw(ltk_text_line *tl, ltk_surface *s, ltk_color *color, int x, int y) {
 	XftDraw *d = ltk_surface_get_xft_draw(s);
-	pango_xft_render_layout(d, &tl->fg->xftcolor, tl->layout, x * PANGO_SCALE, y * PANGO_SCALE);
+	pango_xft_render_layout(d, &color->xftcolor, tl->layout, x * PANGO_SCALE, y * PANGO_SCALE);
+}
+
+/* FIXME: any way to actually implement clipping with pango? */
+void
+ltk_text_line_draw_clipped(ltk_text_line *tl, ltk_surface *s, ltk_color *color, int x, int y, ltk_rect clip) {
+	(void)clip;
+	ltk_text_line_draw(tl, s, color, x, y);
+}
+
+ltk_rect
+ltk_text_line_get_minimal_clip_rect(ltk_text_line *tl, ltk_rect clip) {
+	(void)clip;
+	int w, h;
+	ltk_text_line_get_size(tl, &w, &h);
+	return (ltk_rect){0, 0, w, h};
 }
 
 void
 ltk_text_line_get_size(ltk_text_line *tl, int *w, int *h) {
-	*w = tl->w;
-	*h = tl->h;
+	pango_layout_get_pixel_size(tl->layout, w, h);
 }
 
 void
diff --git a/src/text_stb.c b/src/text_stb.c
@@ -1,5 +1,3 @@
-/* FIXME: more dirty flags; cache ximages so not too much ram is used
-   when a lot of text is displayed */
 /*
  * Copyright (c) 2017, 2018, 2020, 2022 lumidify <nobody@lumidify.org>
  *
@@ -70,7 +68,7 @@ typedef struct {
 } ltk_glyph;
 
 struct ltk_text_line {
-	XImage *img;
+	ltk_text_context *ctx;
 	char *text;
 	ltk_glyph *glyphs;
 	size_t glyph_len;
@@ -79,18 +77,14 @@ struct ltk_text_line {
 	int lines;
 	int lines_alloc;
 
-	Window window;
-
 	int w_max;
 	int w;
 	int h;
 	int line_h;
 	int x_min;
 	int y_min;
-	uint16_t font_size;
 	int dirty;
-	ltk_color *fg;
-	ltk_color *bg;
+	uint16_t font_size;
 };
 
 /* Hash definitions */
@@ -99,7 +93,8 @@ KHASH_MAP_INIT_INT(glyphinfo, ltk_glyph_info*)
 /* font path, size -> glyph cache hash */
 KHASH_MAP_INIT_INT(glyphcache, khash_t(glyphinfo)*)
 
-static struct {
+struct ltk_text_context {
+	ltk_window *window;
 	khash_t(glyphcache) *glyph_cache;
 	ltk_font **fonts;
 	int num_fonts;
@@ -107,33 +102,28 @@ static struct {
 	FcPattern *fcpattern;
 	ltk_font *default_font;
 	uint16_t font_id_cur;
-	Display *dpy;
-	int screen;
-	Colormap cm;
-} tm = {NULL, NULL, 0, 0, NULL, NULL, 1, NULL, 0, 0};
-
+};
 
-static ltk_font *ltk_get_font(char *path, int index);
+static ltk_font *ltk_get_font(ltk_text_context *ctx, char *path, int index);
 static ltk_glyph_info *ltk_create_glyph_info(ltk_font *font, int id,
     float scale);
 static void ltk_destroy_glyph_info(ltk_glyph_info *gi);
 static ltk_glyph_info *ltk_get_glyph_info(ltk_font *font, int id,
     float scale, khash_t(glyphinfo) *cache);
-static khash_t(glyphinfo) *ltk_get_glyph_cache(uint16_t font_id,
+static khash_t(glyphinfo) *ltk_get_glyph_cache(ltk_text_context *ctx, uint16_t font_id,
     uint16_t font_size);
-static khint_t ltk_create_glyph_cache(uint16_t font_id, uint16_t font_size);
+static khint_t ltk_create_glyph_cache(ltk_text_context *ctx, uint16_t font_id, uint16_t font_size);
 static void ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache);
-static void ltk_load_default_font(const char *name);
 static ltk_font *ltk_create_font(char *path, uint16_t id, int index);
 static void ltk_destroy_font(ltk_font *font);
-static ltk_font *ltk_load_font(char *path, int index);
-static ltk_font *ltk_get_font(char *path, int index);
-static void ltk_text_to_glyphs(ltk_glyph *glyphs, int num_glyphs, char *text,
+static ltk_font *ltk_load_font(ltk_text_context *ctx, char *path, int index);
+static void ltk_load_default_font(ltk_text_context *ctx, char *default_font);
+static void ltk_text_to_glyphs(ltk_text_context *ctx, ltk_glyph *glyphs, int num_glyphs, char *text,
     uint16_t font_size, int *x_min, int *y_min, int *x_max, int *y_max);
 static void ltk_text_line_create_glyphs(ltk_text_line *tl);
 static void ltk_text_line_draw_glyph(ltk_glyph *glyph, int x, int y,
     XImage *img, XColor fg);
-static XImage *ltk_create_ximage(int w, int h, int depth, XColor bg);
+/*static XImage *ltk_create_ximage(int w, int h, int depth, XColor bg);*/
 
 
 /* These unicode routines are taken from
@@ -210,29 +200,33 @@ static size_t u8_wc_toutf8(char *dest, uint32_t ch) {
 }
 */
 
-void
-ltk_init_text(const char *default_font, Display *dpy, int screen, Colormap cm) {
-	tm.fonts_bufsize = 1;
-	tm.glyph_cache = kh_init(glyphcache);
-	tm.fonts = ltk_malloc(sizeof(ltk_font *));
-	ltk_load_default_font(default_font);
-	tm.dpy = dpy;
-	tm.screen = screen;
-	tm.cm = cm;
+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->glyph_cache = kh_init(glyphcache);
+	ctx->fonts = ltk_malloc(sizeof(ltk_font *));
+	ctx->num_fonts = 0;
+	ctx->fonts_bufsize = 1;
+	ctx->fcpattern = NULL;
+	ltk_load_default_font(ctx, default_font);
+	ctx->font_id_cur = 1;
+	return ctx;
 }
 
 void
-ltk_cleanup_text(void) {
-	for (int i = 0; i < tm.num_fonts; i++) {
-		ltk_destroy_font(tm.fonts[i]);
+ltk_text_context_destroy(ltk_text_context *ctx) {
+	/* FIXME: destroy fcpattern */
+	for (int i = 0; i < ctx->num_fonts; i++) {
+		ltk_destroy_font(ctx->fonts[i]);
 	}
-	if (!tm.glyph_cache) return;
-	for (khint_t k = kh_begin(tm.glyph_cache); k != kh_end(tm.glyph_cache); k++) {
-		if (kh_exist(tm.glyph_cache, k)) {
-			ltk_destroy_glyph_cache(kh_value(tm.glyph_cache, k));
+	if (!ctx->glyph_cache) return;
+	for (khint_t k = kh_begin(ctx->glyph_cache); k != kh_end(ctx->glyph_cache); k++) {
+		if (kh_exist(ctx->glyph_cache, k)) {
+			ltk_destroy_glyph_cache(kh_value(ctx->glyph_cache, k));
 		}
 	}
-	kh_destroy(glyphcache, tm.glyph_cache);
+	kh_destroy(glyphcache, ctx->glyph_cache);
 }
 
 static ltk_glyph_info *
@@ -274,24 +268,24 @@ ltk_get_glyph_info(ltk_font *font, int id, float scale, khash_t(glyphinfo) *cach
 }
 
 static khash_t(glyphinfo) *
-ltk_get_glyph_cache(uint16_t font_id, uint16_t font_size) {
+ltk_get_glyph_cache(ltk_text_context *ctx, uint16_t font_id, uint16_t font_size) {
 	khint_t k;
 	uint32_t attr = ((uint32_t)font_id << 16) + font_size;
-	k = kh_get(glyphcache, tm.glyph_cache, attr);
-	if (k == kh_end(tm.glyph_cache)) {
-		k = ltk_create_glyph_cache(font_id, font_size);
+	k = kh_get(glyphcache, ctx->glyph_cache, attr);
+	if (k == kh_end(ctx->glyph_cache)) {
+		k = ltk_create_glyph_cache(ctx, font_id, font_size);
 	}
-	return kh_value(tm.glyph_cache, k);
+	return kh_value(ctx->glyph_cache, k);
 }
 
 static khint_t
-ltk_create_glyph_cache(uint16_t font_id, uint16_t font_size) {
+ltk_create_glyph_cache(ltk_text_context *ctx, uint16_t font_id, uint16_t font_size) {
 	khash_t(glyphinfo) *cache = kh_init(glyphinfo);
 	int ret;
 	khint_t k;
 	/* I guess I can just ignore ret for now */
-	k = kh_put(glyphcache, tm.glyph_cache, ((uint32_t)font_id << 16) + font_size, &ret);
-	kh_value(tm.glyph_cache, k) = cache;
+	k = kh_put(glyphcache, ctx->glyph_cache, ((uint32_t)font_id << 16) + font_size, &ret);
+	kh_value(ctx->glyph_cache, k) = cache;
 
 	return k;
 }
@@ -307,24 +301,24 @@ ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache) {
 }
 
 static void
-ltk_load_default_font(const char *name) {
+ltk_load_default_font(ltk_text_context *ctx, char *name) {
 	FcPattern *match;
 	FcResult result;
 	char *file;
 	int index;
 
 	/* FIXME: Get rid of this stupid cast somehow */
-	tm.fcpattern = FcNameParse((const FcChar8 *)name);
-	/*tm.fcpattern = FcPatternCreate();*/
-	FcPatternAddString(tm.fcpattern, FC_FONTFORMAT, (const FcChar8 *)"truetype");
-	FcConfigSubstitute(NULL, tm.fcpattern, FcMatchPattern);
-	FcDefaultSubstitute(tm.fcpattern);
-	match = FcFontMatch(NULL, tm.fcpattern, &result);
+	ctx->fcpattern = FcNameParse((const FcChar8 *)name);
+	/*ctx->fcpattern = FcPatternCreate();*/
+	FcPatternAddString(ctx->fcpattern, FC_FONTFORMAT, (const FcChar8 *)"truetype");
+	FcConfigSubstitute(NULL, ctx->fcpattern, FcMatchPattern);
+	FcDefaultSubstitute(ctx->fcpattern);
+	match = FcFontMatch(NULL, ctx->fcpattern, &result);
 
 	FcPatternGetString(match, FC_FILE, 0, (FcChar8 **) &file);
 	FcPatternGetInteger(match, FC_INDEX, 0, &index);
 
-	tm.default_font = ltk_get_font(file, index);
+	ctx->default_font = ltk_get_font(ctx, file, index);
 
 	FcPatternDestroy(match);
 }
@@ -354,35 +348,35 @@ ltk_destroy_font(ltk_font *font) {
 }
 
 static ltk_font *
-ltk_load_font(char *path, int index) {
-	ltk_font *font = ltk_create_font(path, tm.font_id_cur++, index);
-	if (tm.num_fonts == tm.fonts_bufsize) {
-		ltk_font **new = ltk_realloc(tm.fonts, tm.fonts_bufsize * 2 * sizeof(ltk_font *));
-		tm.fonts = new;
-		tm.fonts_bufsize *= 2;
+ltk_load_font(ltk_text_context *ctx, char *path, int index) {
+	ltk_font *font = ltk_create_font(path, ctx->font_id_cur++, index);
+	if (ctx->num_fonts == ctx->fonts_bufsize) {
+		ltk_font **new = ltk_realloc(ctx->fonts, ctx->fonts_bufsize * 2 * sizeof(ltk_font *));
+		ctx->fonts = new;
+		ctx->fonts_bufsize *= 2;
 	}
-	tm.fonts[tm.num_fonts] = font;
-	tm.num_fonts++;
+	ctx->fonts[ctx->num_fonts] = font;
+	ctx->num_fonts++;
 	return font;
 }
 
 static ltk_font *
-ltk_get_font(char *path, int index) {
+ltk_get_font(ltk_text_context *ctx, char *path, int index) {
 	ltk_font *font = NULL;
-	for (int i = 0; i < tm.num_fonts; i++) {
-		if (tm.fonts[i]->index == index &&
-		    strcmp(tm.fonts[i]->path, path) == 0) {
-			font = tm.fonts[i];
+	for (int i = 0; i < ctx->num_fonts; i++) {
+		if (ctx->fonts[i]->index == index &&
+		    strcmp(ctx->fonts[i]->path, path) == 0) {
+			font = ctx->fonts[i];
 			break;
 		}
 	}
 	if (!font)
-		font = ltk_load_font(path, index);
+		font = ltk_load_font(ctx, path, index);
 	return font;
 }
 
 static void
-ltk_text_to_glyphs(ltk_glyph *glyphs, int num_glyphs, char *text, uint16_t font_size,
+ltk_text_to_glyphs(ltk_text_context *ctx, ltk_glyph *glyphs, int num_glyphs, char *text, uint16_t font_size,
     int *x_min, int *y_min, int *x_max, int *y_max) {
         uint32_t c1, c2 = 0;
         int gid;
@@ -396,8 +390,8 @@ ltk_text_to_glyphs(ltk_glyph *glyphs, int num_glyphs, char *text, uint16_t font_
         *x_min = INT_MAX, *x_max = INT_MIN, *y_min = INT_MAX, *y_max = INT_MIN;
         ltk_glyph_info *ginfo;
 
-	ltk_font *font = tm.default_font;
-	khash_t(glyphinfo) *glyph_cache = ltk_get_glyph_cache(font->id, font_size);
+	ltk_font *font = ctx->default_font;
+	khash_t(glyphinfo) *glyph_cache = ltk_get_glyph_cache(ctx, font->id, font_size);
 
         scale = stbtt_ScaleForPixelHeight(&font->info, font_size);
         stbtt_GetFontVMetrics(&font->info, &ascent, &descent, &line_gap);
@@ -421,8 +415,8 @@ ltk_text_to_glyphs(ltk_glyph *glyphs, int num_glyphs, char *text, uint16_t font_
 			match = FcFontMatch(NULL, pat, &result);
 			FcPatternGetString(match, FC_FILE, 0, &file);
 			FcPatternGetInteger(match, FC_INDEX, 0, &index);
-			font = ltk_get_font((char *)file, index);
-			glyph_cache = ltk_get_glyph_cache(font->id, font_size);
+			font = ltk_get_font(ctx, (char *)file, index);
+			glyph_cache = ltk_get_glyph_cache(ctx, font->id, font_size);
 			FcPatternDestroy(match);
 			FcPatternDestroy(pat);
 			gid = stbtt_FindGlyphIndex(&font->info, c1);
@@ -477,8 +471,7 @@ ltk_unref_glyphs(ltk_glyph *glyphs, int num_glyphs) {
 }
 */
 
-/* FIXME: Error checking that tm has been initialized */
-
+/*
 static void
 ltk_fill_ximage(XImage *img, int w, int h, XColor bg) {
 	int b;
@@ -502,9 +495,11 @@ ltk_create_ximage(int w, int h, int depth, XColor bg) {
 
 	return img;
 }
+*/
 
 /* based on http://codemadness.org/git/dwm-font/file/drw.c.html#l315 */
 /* FIXME: make this work with other pixel representations */
+/* FIXME: will fail horribly if depth is not 32 */
 static void
 ltk_text_line_draw_glyph(ltk_glyph *glyph, int x, int y, XImage *img, XColor fg) {
 	double a;
@@ -562,26 +557,20 @@ ltk_text_line_break_lines(ltk_text_line *tl) {
 		i++;
 	}
 	tl->h = tl->line_h * tl->lines;
+	tl->dirty = 0;
 }
 
-static void
-ltk_text_line_render(
-	ltk_text_line *tl,
-	ltk_color *bg,
-	ltk_color *fg)
-{
-	/* FIXME: just keep reference to ltk_window so this isn't necessary */
-	XWindowAttributes attrs;
-	XGetWindowAttributes(tm.dpy, tl->window, &attrs);
-	int depth = attrs.depth;
-	/* FIXME: maybe don't destroy if old image is big enough? */
-	if (!tl->img || tl->img->width != tl->w || tl->img->height != tl->h) {
-		if (tl->img)
-			XDestroyImage(tl->img);
-		tl->img = ltk_create_ximage(tl->w, tl->h, depth, bg->xcolor);
-	} else {
-		ltk_fill_ximage(tl->img, tl->w, tl->h, bg->xcolor);
-	}
+/* FIXME: improve color handling - where passed as pointer, where as value?
+   In certain cases, it's important to deallocate the color in the end
+   (if the x server doesn't support truecolor - I'm not sure right now if
+   this is the right terminology, but it's something like that) */
+
+void
+ltk_text_line_draw(ltk_text_line *tl, ltk_surface *s, ltk_color *color, int x, int y) {
+	if (tl->dirty)
+		ltk_text_line_break_lines(tl);
+	Drawable d = ltk_surface_get_drawable(s);
+	XImage *img = XGetImage(tl->ctx->window->dpy, d, x, y, tl->w, tl->h, 0xFFFFFF, ZPixmap);
 
 	int last_break = 0;
 	for (int i = 0; i < tl->lines; i++) {
@@ -593,55 +582,42 @@ ltk_text_line_render(
 		for (int j = last_break; j < next_break; j++) {
 			int x = tl->glyphs[j].x - tl->glyphs[last_break].x;
 			int y = tl->glyphs[j].y - tl->y_min + tl->line_h * i;
-			ltk_text_line_draw_glyph(&tl->glyphs[j], x, y, tl->img, fg->xcolor);
+			ltk_text_line_draw_glyph(&tl->glyphs[j], x, y, img, color->xcolor);
 		}
 		last_break = next_break;
 	}
-	tl->dirty = 0;
+	XPutImage(tl->ctx->window->dpy, d, tl->ctx->window->gc, img, 0, 0, x, y, tl->w, tl->h);
+	XDestroyImage(img);
 }
 
-/* FIXME: improve color handling - where passed as pointer, where as value?
-   In certain cases, it's important to deallocate the color in the end
-   (if the x server doesn't support truecolor - I'm not sure right now if
-   this is the right terminology, but it's something like that) */
+/* FIXME: ACTUALLY IMPLEMENT!!! */
 void
-ltk_text_line_change_colors(ltk_text_line *tl, ltk_color *fg, ltk_color *bg) {
-	tl->fg = fg;
-	tl->bg = bg;
-	tl->dirty = 1;
-
+ltk_text_line_draw_clipped(ltk_text_line *tl, ltk_surface *s, ltk_color *color, int x, int y, ltk_rect clip) {
+	(void)clip;
+	ltk_text_line_draw(tl, s, color, x, y);
 }
 
-/* FIXME: error checking if img is rendered yet, tm initialized, etc. */
-void
-ltk_text_line_draw(ltk_text_line *tl, ltk_surface *s, GC gc, int x, int y, ltk_rect clip) {
+ltk_rect
+ltk_text_line_get_minimal_clip_rect(ltk_text_line *tl, ltk_rect clip) {
 	(void)clip;
 	if (tl->dirty)
-		ltk_text_line_render(tl, tl->bg, tl->fg);
-	/*
-	int xoff = clip.x - x;
-	int yoff = clip.y - y;
-	xoff = xoff >= 0 ? xoff : 0;
-	yoff = yoff >= 0 ? yoff : 0;
-	int w = clip.w > tl->w - xoff ? tl->w - xoff : clip.w;
-	int h = clip.h > tl->h - yoff ? tl->h - yoff : clip.h;
-	XPutImage(tm.dpy, tl->window, gc, tl->img, xoff, yoff, x + xoff, y + yoff, w, h);
-	*/
-	Pixmap p = ltk_surface_get_pixmap(s);
-	XPutImage(tm.dpy, p, gc, tl->img, 0, 0, x, y, tl->w, tl->h);
+		ltk_text_line_break_lines(tl);
+	return (ltk_rect){0, 0, tl->w, tl->h};
 }
 
 void
 ltk_text_line_set_width(ltk_text_line *tl, int width) {
 	/* FIXME: clarify what the difference between w_max and w is */
+	/* FIXME: tl->w could be made slightly less than tl->w_max depending on breaking */
 	tl->w_max = width;
 	tl->w = width;
-	ltk_text_line_break_lines(tl);
 	tl->dirty = 1;
 }
 
 void
 ltk_text_line_get_size(ltk_text_line *tl, int *w, int *h) {
+	if (tl->dirty)
+		ltk_text_line_break_lines(tl);
 	*w = tl->w;
 	*h = tl->h;
 }
@@ -649,7 +625,7 @@ ltk_text_line_get_size(ltk_text_line *tl, int *w, int *h) {
 static void
 ltk_text_line_create_glyphs(ltk_text_line *tl) {
 	int x_min, x_max, y_min, y_max;
-	ltk_text_to_glyphs(tl->glyphs, tl->glyph_len, tl->text, tl->font_size,
+	ltk_text_to_glyphs(tl->ctx, tl->glyphs, tl->glyph_len, tl->text, tl->font_size,
 	    &x_min, &y_min, &x_max, &y_max);
 	/* for drawing the glyphs at the right position on the image */
 	tl->x_min = x_min;
@@ -659,28 +635,26 @@ ltk_text_line_create_glyphs(ltk_text_line *tl) {
 }
 
 ltk_text_line *
-ltk_text_line_create(Window window, uint16_t font_size, char *text, int width, ltk_color *fg, ltk_color *bg) {
-	ltk_text_line *line = ltk_malloc(sizeof(ltk_text_line));
-	line->window = window;
-	line->img = NULL;
-	line->text = text;
-	line->glyph_len = u8_strlen(text);
-	line->glyphs = ltk_malloc(line->glyph_len * sizeof(ltk_glyph));
-	line->font_size = font_size;
-	line->w_max = width;
-	ltk_text_line_create_glyphs(line);
-	line->lines_alloc = line->lines = 0;
-	line->line_indeces = NULL;
-	ltk_text_line_break_lines(line);
-	line->dirty = 1;
-	line->fg = fg;
-	line->bg = bg;
-	return line;
+ltk_text_line_create(ltk_text_context *ctx, uint16_t font_size, char *text, int take_over_text, int width) {
+	ltk_text_line *tl = ltk_malloc(sizeof(ltk_text_line));
+	tl->ctx = ctx;
+	if (take_over_text)
+		tl->text = text;
+	else
+		tl->text = ltk_strdup(text);
+	tl->glyph_len = u8_strlen(text);
+	tl->glyphs = ltk_malloc(tl->glyph_len * sizeof(ltk_glyph));
+	tl->font_size = font_size;
+	tl->w_max = width;
+	ltk_text_line_create_glyphs(tl);
+	tl->lines_alloc = tl->lines = 0;
+	tl->line_indeces = NULL;
+	tl->dirty = 1;
+	return tl;
 }
 
 void
 ltk_text_line_destroy(ltk_text_line *tl) {
-	XDestroyImage(tl->img);
 	ltk_free(tl->text);
 	/* FIXME: Reference count glyph infos */
 	ltk_free(tl->glyphs);