commit eaee011ce47b2f57527a241a53dded691735770f
parent 1207520e0aa5ec1696924646f850b1c4a8bc57ed
Author: lumidify <nobody@lumidify.org>
Date:   Fri, 29 May 2020 20:22:21 +0200
Add preparations for adding dropdowns
Diffstat:
| M | button.c |  |  | 23 | ++++++++++++----------- | 
| M | grid.c |  |  | 6 | +++--- | 
| M | ltk.c |  |  | 220 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------ | 
| M | ltk.h |  |  | 38 | ++++++++++++++++++-------------------- | 
| M | text_buffer.c |  |  | 28 | ++++++++++++++-------------- | 
| M | text_common.c |  |  | 2 | -- | 
| M | text_edit.c |  |  | 21 | ++++++++++++--------- | 
7 files changed, 229 insertions(+), 109 deletions(-)
diff --git a/button.c b/button.c
@@ -36,8 +36,6 @@
 #include "text_buffer.h"
 #include "button.h"
 
-extern Ltk *ltk_global;
-
 void
 ltk_button_ini_handler(LtkTheme *theme, const char *prop, const char *value) {
 	if (theme->button == NULL) {
@@ -78,7 +76,8 @@ ltk_button_ini_handler(LtkTheme *theme, const char *prop, const char *value) {
 
 void
 ltk_button_draw(LtkButton *button) {
-	LtkButtonTheme *theme = ltk_global->theme->button;
+	LtkTheme *global_theme = ltk_get_theme();
+	LtkButtonTheme *theme = global_theme->button;
 	LtkWindow *window = button->widget.window;
 	LtkRect rect = button->widget.rect;
 	int bw = theme->border_width;
@@ -116,15 +115,17 @@ ltk_button_draw(LtkButton *button) {
 	default:
 		ltk_fatal("No style found for button!\n");
 	}
-	XSetForeground(ltk_global->display, window->gc, fill.pixel);
-	XFillRectangle(ltk_global->display, window->xwindow, window->gc, rect.x, rect.y, rect.w, rect.h);
+	Display *display = ltk_get_display();
+	Colormap colormap = ltk_get_colormap();
+	XSetForeground(display, window->gc, fill.pixel);
+	XFillRectangle(display, window->xwindow, window->gc, rect.x, rect.y, rect.w, rect.h);
 	/* FIXME: Why did I do this? */
 	if (bw < 1) return;
-	XSetForeground(ltk_global->display, window->gc, border.pixel);
-	XSetLineAttributes(ltk_global->display, window->gc, bw, LineSolid, CapButt, JoinMiter);
-	XDrawRectangle(ltk_global->display, window->xwindow, window->gc, rect.x + bw / 2, rect.y + bw / 2, rect.w - bw, rect.h - bw);
+	XSetForeground(display, window->gc, border.pixel);
+	XSetLineAttributes(display, window->gc, bw, LineSolid, CapButt, JoinMiter);
+	XDrawRectangle(display, window->xwindow, window->gc, rect.x + bw / 2, rect.y + bw / 2, rect.w - bw, rect.h - bw);
 	if (!img) {
-		img = ltk_text_line_render(button->tl, ltk_global->display, window->xwindow, window->gc, ltk_global->colormap, theme->text_color, fill);
+		img = ltk_text_line_render(button->tl, display, window->xwindow, window->gc, colormap, theme->text_color, fill);
 		/* FIXME: any nicer way to do this? */
 		switch (button->widget.state) {
 			case LTK_NORMAL:
@@ -147,7 +148,7 @@ ltk_button_draw(LtkButton *button) {
 	}
 	text_x = rect.x + (rect.w - button->tl->w) / 2;
 	text_y = rect.y + (rect.h - button->tl->h) / 2;
-	XPutImage(ltk_global->display, window->xwindow, window->gc, img, 0, 0, text_x, text_y, button->tl->w, button->tl->h);
+	XPutImage(display, window->xwindow, window->gc, img, 0, 0, text_x, text_y, button->tl->w, button->tl->h);
 }
 
 LtkButton *
@@ -163,7 +164,7 @@ ltk_button_create(LtkWindow *window, const char *text, void (*callback) (void *,
 
 	button->callback = callback;
 	button->data = data;
-	LtkTheme *theme = ltk_global->theme;
+	LtkTheme *theme = ltk_get_theme();
 	button->tl = ltk_text_line_create(theme->button->font_size);
 	/* FIXME: support font size */
 	ltk_text_line_insert_utf8(button->tl, 0, text);
diff --git a/grid.c b/grid.c
@@ -247,7 +247,7 @@ void ltk_grid_mouse_press(LtkGrid *grid, XEvent event)
 		return;
 	LtkWidget *ptr = grid->widget_grid[row * grid->columns + column];
 	if (ptr && ltk_collide_rect(ptr->rect, x, y)) {
-		ltk_mouse_press_event(ptr, event);
+		ltk_widget_mouse_press_event(ptr, event);
 	}
 }
 
@@ -262,7 +262,7 @@ void ltk_grid_mouse_release(LtkGrid *grid, XEvent event)
 	LtkWidget *ptr = grid->widget_grid[row * grid->columns + column];
 	if (ptr) {
 		if (ltk_collide_rect(ptr->rect, x, y)) {
-			ltk_mouse_release_event(ptr, event);
+			ltk_widget_mouse_release_event(ptr, event);
 		} else {
 			ltk_remove_hover_widget(grid);
 			ltk_change_active_widget_state(grid, LTK_ACTIVE);
@@ -284,7 +284,7 @@ void ltk_grid_motion_notify(LtkGrid *grid, XEvent event)
 	LtkWidget *ptr = grid->widget_grid[row * grid->columns + column];
 	if (ptr) {
 		if (ltk_collide_rect(ptr->rect, x, y))
-			ltk_motion_notify_event(ptr, event);
+			ltk_widget_motion_notify_event(ptr, event);
 		else if (!pressed)
 			ltk_remove_hover_widget(grid);
 	}
diff --git a/ltk.c b/ltk.c
@@ -27,22 +27,54 @@
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #include "khash.h"
-#include "ltk.h"
 #include "stb_truetype.h"
 #include <fribidi.h>
 #include <harfbuzz/hb.h>
 #include <fontconfig/fontconfig.h>
 #include "array.h"
 #include "text_common.h"
+#include "ltk.h"
 #include "text_buffer.h"
 
-Ltk *ltk_global;
+/* Window hash */
+KHASH_MAP_INIT_INT(winhash, LtkWindow*)
+
+static struct {
+	LtkTheme *theme;
+	LtkTextManager *tm;
+	Display *display;
+	int screen;
+	Colormap colormap;
+	khash_t(winhash) *window_hash;
+	int window_num;
+	Atom wm_delete_msg;
+} ltk_global;
 
 struct ltk_redraw_queue {
 	LtkWindow *window;
 	LtkRect rect;
 };
 
+LtkTextManager *
+ltk_get_text_manager(void) {
+	return ltk_global.tm;
+}
+
+LtkTheme *
+ltk_get_theme(void) {
+	return ltk_global.theme;
+}
+
+Display *
+ltk_get_display(void) {
+	return ltk_global.display;
+}
+
+Colormap
+ltk_get_colormap(void) {
+	return ltk_global.colormap;
+}
+
 static struct ltk_redraw_queue *redraw_queue = NULL;
 static int num_redraws = 0;
 static int redraw_queue_bufsize = 0;
@@ -93,31 +125,65 @@ error:
 }
 
 void
+ltk_widget_set_floating_collision(LtkWidget *widget) {
+	LtkWindow *window = widget->window;
+	for (int i = 0; i < window->num_float_widgets; i++) {
+		if (window->float_widgets[i] == widget)
+			return;
+	}
+	if (window->num_float_widgets + 1 > window->max_float_widgets) {
+		LtkWidget **new = realloc(
+		    window->float_widgets,
+		    sizeof(LtkWidget *) * (window->max_float_widgets + 1));
+		if (!new) goto error;
+		window->float_widgets = new;
+		window->max_float_widgets++;
+	}
+	window->float_widgets[window->num_float_widgets] = widget;
+	window->num_float_widgets++;
+	return;
+error:
+	(void)fprintf(stderr, "Out of memory\n");
+	exit(1);
+}
+
+void
+ltk_widget_unset_floating_collision(LtkWidget *widget) {
+	LtkWindow *window = widget->window;
+	for (int i = 0; i < window->num_float_widgets; i++) {
+		if (window->float_widgets[i] == widget) {
+			for (int j = i + 1; j < window->num_float_widgets; j++) {
+				window->float_widgets[j - 1] = window->float_widgets[j];
+			}
+			window->num_float_widgets--;
+			break;
+		}
+	}
+}
+
+void
 ltk_init(const char *theme_path) {
-	ltk_global = malloc(sizeof(Ltk));
-	Ltk *ltk = ltk_global;	/* For convenience */
-	ltk->display = XOpenDisplay(NULL);
-	ltk->screen = DefaultScreen(ltk->display);
-	ltk->colormap = DefaultColormap(ltk->display, ltk->screen);
-	ltk->theme = ltk_load_theme(theme_path);
-	ltk->window_hash = kh_init(winhash);
-	ltk->wm_delete_msg = XInternAtom(ltk->display, "WM_DELETE_WINDOW", False);
-	ltk->tm = ltk_init_text(ltk->theme->window->font);
+	ltk_global.display = XOpenDisplay(NULL);
+	ltk_global.screen = DefaultScreen(ltk_global.display);
+	ltk_global.colormap = DefaultColormap(ltk_global.display, ltk_global.screen);
+	ltk_global.theme = ltk_load_theme(theme_path);
+	ltk_global.window_hash = kh_init(winhash);
+	ltk_global.wm_delete_msg = XInternAtom(ltk_global.display, "WM_DELETE_WINDOW", False);
+	ltk_global.tm = ltk_init_text(ltk_global.theme->window->font);
 }
 
 void
 ltk_clean_up(void) {
 	LtkWindow *window;
-	for (int k = kh_begin(ltk_global->window_hash); k != kh_end(ltk_global->window_hash); k++) {
-		if (kh_exist(ltk_global->window_hash, k)) {
-			ltk_destroy_window(kh_value(ltk_global->window_hash, k));
+	for (int k = kh_begin(ltk_global.window_hash); k != kh_end(ltk_global.window_hash); k++) {
+		if (kh_exist(ltk_global.window_hash, k)) {
+			ltk_destroy_window(kh_value(ltk_global.window_hash, k));
 		}
 	}
-	kh_destroy(winhash, ltk_global->window_hash);
-	XCloseDisplay(ltk_global->display);
-	ltk_destroy_theme(ltk_global->theme);
-	ltk_destroy_text_manager(ltk_global->tm);
-	free(ltk_global);
+	kh_destroy(winhash, ltk_global.window_hash);
+	XCloseDisplay(ltk_global.display);
+	ltk_destroy_theme(ltk_global.theme);
+	ltk_destroy_text_manager(ltk_global.tm);
 	if (redraw_queue) free(redraw_queue);
 }
 
@@ -137,9 +203,9 @@ ltk_fatal(const char *msg) {
 XColor
 ltk_create_xcolor(const char *hex) {
 	XColor color;
-	XParseColor(ltk_global->display, ltk_global->colormap, hex,
+	XParseColor(ltk_global.display, ltk_global.colormap, hex,
 		    &color);
-	XAllocColor(ltk_global->display, ltk_global->colormap, &color);
+	XAllocColor(ltk_global.display, ltk_global.colormap, &color);
 
 	return color;
 }
@@ -152,8 +218,8 @@ ltk_mainloop(void) {
 
 	/* FIXME: compress motion events */
 	while (1) {
-		if (XPending(ltk_global->display) || !num_redraws) {
-			XNextEvent(ltk_global->display, &event);
+		if (XPending(ltk_global.display) || !num_redraws) {
+			XNextEvent(ltk_global.display, &event);
 			ltk_handle_event(event);
 		} else if (num_redraws) {
 			for (int i = 0; i < num_redraws; i++)
@@ -173,7 +239,7 @@ ltk_redraw_window(LtkWindow *window, LtkRect rect) {
 		rect.w -= rect.x + rect.w - window->rect.w;
 	if (rect.y + rect.h > window->rect.h)
 		rect.h -= rect.y + rect.h - window->rect.h;
-	XClearArea(ltk_global->display, window->xwindow, rect.x, rect.y, rect.w, rect.h, False);
+	XClearArea(ltk_global.display, window->xwindow, rect.x, rect.y, rect.w, rect.h, False);
 	if (!window->root_widget) return;
 	ptr = window->root_widget;
 	ptr->draw(ptr);
@@ -184,8 +250,8 @@ ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int 
 	LtkWindow *window = malloc(sizeof(LtkWindow));
 	if (!window)
 		ltk_fatal("Not enough memory left for window!\n");
-	LtkWindowTheme *wtheme = ltk_global->theme->window;	/* For convenience */
-	Display *display = ltk_global->display;	/* For convenience */
+	LtkWindowTheme *wtheme = ltk_global.theme->window;	/* For convenience */
+	Display *display = ltk_global.display;	/* For convenience */
 	window->xwindow =
 	    XCreateSimpleWindow(display, DefaultRootWindow(display), x, y,
 				w, h, wtheme->border_width,
@@ -196,7 +262,7 @@ ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int 
 	XSetStandardProperties(display, window->xwindow, title, NULL, None,
 			       NULL, 0, NULL);
 	XSetWMProtocols(display, window->xwindow,
-			<k_global->wm_delete_msg, 1);
+			<k_global.wm_delete_msg, 1);
 	window->root_widget = NULL;
 
 	window->other_event = <k_window_other_event;
@@ -206,6 +272,10 @@ ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int 
 	window->rect.x = 0;
 	window->rect.y = 0;
 
+	window->num_float_widgets = 0;
+	window->max_float_widgets = 0;
+	window->float_widgets = NULL;
+
 	XClearWindow(display, window->xwindow);
 	XMapRaised(display, window->xwindow);
 	XSelectInput(display, window->xwindow,
@@ -214,9 +284,9 @@ ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int 
 		     StructureNotifyMask | PointerMotionMask);
 
 	int ret;
-	int k = kh_put(winhash, ltk_global->window_hash, window->xwindow, &ret);
-	kh_value(ltk_global->window_hash, k) = window;
-	ltk_global->window_num++;
+	int k = kh_put(winhash, ltk_global.window_hash, window->xwindow, &ret);
+	kh_value(ltk_global.window_hash, k) = window;
+	ltk_global.window_num++;
 
 	return window;
 }
@@ -225,20 +295,20 @@ void
 ltk_remove_window(LtkWindow *window) {
 	/* FIXME: also remove from event queue */
 	ltk_destroy_window(window);
-	if (ltk_global->window_num == 0)
+	if (ltk_global.window_num == 0)
 		ltk_quit();
 }
 
 void
 ltk_destroy_window(LtkWindow * window) {
-	int k = kh_get(winhash, ltk_global->window_hash, window->xwindow);
-	kh_del(winhash, ltk_global->window_hash, k);
+	int k = kh_get(winhash, ltk_global.window_hash, window->xwindow);
+	kh_del(winhash, ltk_global.window_hash, k);
 	LtkWidget *ptr = window->root_widget;
 	if (ptr)
 		ptr->destroy(ptr);
-	XDestroyWindow(ltk_global->display, window->xwindow);
+	XDestroyWindow(ltk_global.display, window->xwindow);
 	free(window);
-	ltk_global->window_num--;
+	ltk_global.window_num--;
 }
 
 void
@@ -267,7 +337,7 @@ ltk_window_other_event(LtkWindow *window, XEvent event) {
 		r.h = event.xexpose.height;
 		ltk_window_invalidate_rect(window, r);
 	} else if (event.type == ClientMessage
-	    && event.xclient.data.l[0] == ltk_global->wm_delete_msg) {
+	    && event.xclient.data.l[0] == ltk_global.wm_delete_msg) {
 		ltk_remove_window(window);
 	}
 }
@@ -405,6 +475,10 @@ ltk_fill_widget_defaults(LtkWidget *widget, LtkWindow *window,
 	widget->rect.y = 0;
 	widget->rect.w = 100;
 	widget->rect.h = 100;
+	widget->collision_rect.x = 0;
+	widget->collision_rect.y = 0;
+	widget->collision_rect.w = 0;
+	widget->collision_rect.h = 0;
 
 	widget->row = NULL;
 	widget->column = NULL;
@@ -414,7 +488,7 @@ ltk_fill_widget_defaults(LtkWidget *widget, LtkWindow *window,
 }
 
 void
-ltk_mouse_press_event(void *widget, XEvent event) {
+ltk_widget_mouse_press_event(void *widget, XEvent event) {
 	LtkWidget *ptr = widget;
 	if (!ptr || ptr->state == LTK_DISABLED)
 		return;
@@ -434,7 +508,24 @@ ltk_mouse_press_event(void *widget, XEvent event) {
 }
 
 void
-ltk_mouse_release_event(void *widget, XEvent event) {
+ltk_window_mouse_press_event(LtkWindow *window, XEvent event) {
+	if (window->float_widgets) {
+		int x = event.xbutton.x;
+		int y = event.xbutton.y;
+		for (int i = 0; i < window->num_float_widgets; i++) {
+			if (ltk_collide_rect(window->float_widgets[i]->collision_rect, x, y)) {
+				ltk_widget_mouse_press_event(window->float_widgets[i], event);
+				return;
+			}
+		}
+	}
+	if (window->root_widget) {
+		ltk_widget_mouse_press_event(window->root_widget, event);
+	}
+}
+
+void
+ltk_widget_mouse_release_event(void *widget, XEvent event) {
 	LtkWidget *ptr = widget;
 	if (!ptr || ptr->state == LTK_DISABLED)
 		return;
@@ -449,11 +540,27 @@ ltk_mouse_release_event(void *widget, XEvent event) {
 }
 
 void
-ltk_motion_notify_event(void *widget, XEvent event) {
+ltk_window_mouse_release_event(LtkWindow *window, XEvent event) {
+	if (window->float_widgets) {
+		int x = event.xbutton.x;
+		int y = event.xbutton.y;
+		for (int i = 0; i < window->num_float_widgets; i++) {
+			if (ltk_collide_rect(window->float_widgets[i]->collision_rect, x, y)) {
+				ltk_widget_mouse_release_event(window->float_widgets[i], event);
+				return;
+			}
+		}
+	}
+	if (window->root_widget) {
+		ltk_widget_mouse_release_event(window->root_widget, event);
+	}
+}
+
+void
+ltk_widget_motion_notify_event(void *widget, XEvent event) {
 	LtkWidget *ptr = widget;
 	LtkWidget *parent;
-	if (!ptr)
-		return;
+	if (!ptr) return;
 	short pressed = (event.xmotion.state & Button1Mask) == Button1Mask;
 	if ((ptr->state == LTK_NORMAL || ptr->state == LTK_ACTIVE)
 	    && !pressed) {
@@ -472,28 +579,41 @@ ltk_motion_notify_event(void *widget, XEvent event) {
 }
 
 void
+ltk_window_motion_notify_event(LtkWindow *window, XEvent event) {
+	if (window->float_widgets) {
+		int x = event.xbutton.x;
+		int y = event.xbutton.y;
+		for (int i = 0; i < window->num_float_widgets; i++) {
+			if (ltk_collide_rect(window->float_widgets[i]->collision_rect, x, y)) {
+				ltk_widget_motion_notify_event(window->float_widgets[i], event);
+				return;
+			}
+		}
+	}
+	if (window->root_widget) {
+		ltk_widget_motion_notify_event(window->root_widget, event);
+	}
+}
+
+void
 ltk_handle_event(XEvent event) {
 	LtkWidget *root_widget;
-	int k = kh_get(winhash, ltk_global->window_hash, event.xany.window);
-	LtkWindow *window = kh_value(ltk_global->window_hash, k);
+	int k = kh_get(winhash, ltk_global.window_hash, event.xany.window);
+	LtkWindow *window = kh_value(ltk_global.window_hash, k);
 	if (!window) return;
-	root_widget = window->root_widget;
 	switch (event.type) {
 	case KeyPress:
 		break;
 	case KeyRelease:
 		break;
 	case ButtonPress:
-		if (root_widget)
-			ltk_mouse_press_event(root_widget, event);
+		ltk_window_mouse_press_event(window, event);
 		break;
 	case ButtonRelease:
-		if (root_widget)
-			ltk_mouse_release_event(root_widget, event);
+		ltk_window_mouse_release_event(window, event);
 		break;
 	case MotionNotify:
-		if (root_widget)
-			ltk_motion_notify_event(root_widget, event);
+		ltk_window_motion_notify_event(window, event);
 		break;
 	default:
 		/* FIXME: users should be able to register other events like closing the window */
diff --git a/ltk.h b/ltk.h
@@ -24,7 +24,7 @@
 #ifndef _LTK_H_
 #define _LTK_H_
 
-/* Requires the following includes: <X11/Xlib.h>, <X11/Xutil.h>, "khash.h" */
+/* Requires the following includes: <X11/Xlib.h>, <X11/Xutil.h> */
 
 typedef struct {
 	int x;
@@ -64,12 +64,15 @@ typedef struct LtkWidget {
 	void (*mouse_press) (void *, XEvent event);
 	void (*mouse_release) (void *, XEvent event);
 	void (*motion_notify) (void *, XEvent event);
+	void (*mouse_leave) (void *, XEvent event);
+	void (*mouse_enter) (void *, XEvent event);
 
 	void (*resize) (void *, int, int);
 	void (*draw) (void *);
 	void (*destroy) (void *);
 
-	LtkRect rect;
+	LtkRect collision_rect; /* rect for collision with mouse */
+	LtkRect rect; /* rect in grid - this is only different when the widget is floating */
 	unsigned int row;
 	unsigned int column;
 	unsigned int row_span;
@@ -82,9 +85,12 @@ typedef struct LtkWidget {
 typedef struct LtkWindow {
 	Window xwindow;
 	GC gc;
-	void *root_widget;
+	LtkWidget *root_widget;
 	int (*other_event) (LtkWindow *, XEvent event);
 	LtkRect rect;
+	LtkWidget **float_widgets;
+	int num_float_widgets;
+	int max_float_widgets;
 } LtkWindow;
 
 typedef struct LtkWindowTheme {
@@ -101,23 +107,15 @@ typedef struct {
 	LtkButtonTheme *button;
 } LtkTheme;
 
-LtkTheme *ltk_load_theme(const char *path);
-
 typedef struct LtkTextManager LtkTextManager;
+LtkTextManager *ltk_get_text_manager(void);
+LtkTheme *ltk_get_theme(void);
+Display *ltk_get_display(void);
+Colormap ltk_get_colormap(void);
 
-/* Window hash */
-KHASH_MAP_INIT_INT(winhash, LtkWindow*)
+LtkTheme *ltk_load_theme(const char *path);
 
-typedef struct {
-	LtkTheme *theme;
-	LtkTextManager *tm;
-	Display *display;
-	int screen;
-	Colormap colormap;
-	khash_t(winhash) *window_hash;
-	int window_num;
-	Atom wm_delete_msg;
-} Ltk;
+typedef struct LtkTextManager LtkTextManager;
 
 void ltk_window_invalidate_rect(LtkWindow *window, LtkRect rect);
 
@@ -155,11 +153,11 @@ void ltk_remove_hover_widget(void *widget);
 void ltk_fill_widget_defaults(LtkWidget *widget, LtkWindow * window,
 	void (*draw) (void *), void (*destroy) (void *), unsigned int needs_redraw);
 
-void ltk_mouse_press_event(void *widget, XEvent event);
+void ltk_widget_mouse_press_event(void *widget, XEvent event);
 
-void ltk_mouse_release_event(void *widget, XEvent event);
+void ltk_widget_mouse_release_event(void *widget, XEvent event);
 
-void ltk_motion_notify_event(void *widget, XEvent event);
+void ltk_widget_motion_notify_event(void *widget, XEvent event);
 
 void ltk_handle_event(XEvent event);
 
diff --git a/text_buffer.c b/text_buffer.c
@@ -38,8 +38,6 @@
 #include "text_buffer.h"
 #include "ltk.h"
 
-extern Ltk *ltk_global;
-
 LTK_ARRAY_INIT_IMPL(uint32, uint32_t)
 LTK_ARRAY_INIT_IMPL(script, hb_script_t)
 LTK_ARRAY_INIT_IMPL(level, FriBidiLevel)
@@ -623,8 +621,9 @@ ltk_text_line_itemize(struct ltk_text_line *tl) {
 }
 
 static void
-ltk_text_run_shape(LtkTextManager *tm, struct ltk_text_run *tr,
+ltk_text_run_shape(struct ltk_text_run *tr,
     struct ltk_text_line *tl, uint16_t font_size, uint16_t font_id, int *ret_y_max) {
+	LtkTextManager *tm = ltk_get_text_manager();
 	khash_t(glyphinfo) *glyph_cache;
 	khint_t k;
 
@@ -707,7 +706,8 @@ ltk_text_run_shape(LtkTextManager *tm, struct ltk_text_run *tr,
 }
 
 static void
-ltk_text_line_shape(LtkTextManager *tm, struct ltk_text_line *tl) {
+ltk_text_line_shape(struct ltk_text_line *tl) {
+	LtkTextManager *tm = ltk_get_text_manager();
 	struct ltk_text_run *run = tl->first_run;
 	tl->w = tl->h = 0;
 	int y_max;
@@ -735,7 +735,7 @@ ltk_text_line_shape(LtkTextManager *tm, struct ltk_text_line *tl) {
 		run->font = kh_value(tm->font_cache, k);
 		FcPatternDestroy(match);
 		FcPatternDestroy(pat);
-		ltk_text_run_shape(tm, run, tl, tl->font_size, font_id, &y_max);
+		ltk_text_run_shape(run, tl, tl->font_size, font_id, &y_max);
 		if (y_max_overall < y_max)
 			y_max_overall = y_max;
 		/* tr->start_y is -y_min */
@@ -771,8 +771,9 @@ ltk_text_run_destroy(struct ltk_text_run *tr) {
 	LtkFont *font;
 	LtkGlyph *glyph;
 	khint_t k;
-	k = kh_get(glyphinfo, ltk_global->tm->glyph_cache, tr->font_id << 16 + tr->font_size);
-	gcache = kh_value(ltk_global->tm->glyph_cache, k);
+	LtkTextManager *tm = ltk_get_text_manager();
+	k = kh_get(glyphinfo, tm->glyph_cache, tr->font_id << 16 + tr->font_size);
+	gcache = kh_value(tm->glyph_cache, k);
 	for (int i = 0; i < tr->num_glyphs; i++) {
 		glyph = &tr->glyphs[i];
 		if (--glyph->info->refs < 1) {
@@ -781,10 +782,10 @@ ltk_text_run_destroy(struct ltk_text_run *tr) {
 			ltk_destroy_glyph_info(glyph->info);
 		}
 	}
-	k = kh_get(fontstruct, ltk_global->tm->font_cache, tr->font_id);
-	font = kh_value(ltk_global->tm->font_cache, k);
+	k = kh_get(fontstruct, tm->font_cache, tr->font_id);
+	font = kh_value(tm->font_cache, k);
 	if (--font->refs < 1) {
-		kh_del(fontstruct, ltk_global->tm->font_cache, k);
+		kh_del(fontstruct, tm->font_cache, k);
 		ltk_destroy_font(font);
 	}
 	free(tr->glyphs);
@@ -803,7 +804,7 @@ ltk_text_line_destroy_runs(struct ltk_text_run *runs) {
 }
 
 static void
-ltk_text_line_recalculate(LtkTextManager *tm, struct ltk_text_line *tl) {
+ltk_text_line_recalculate(struct ltk_text_line *tl) {
 	FriBidiCharType par_dir = FRIBIDI_TYPE_ON;
 	fribidi_log2vis(
 	    tl->log_buf->buf, tl->log_buf->len,
@@ -816,7 +817,7 @@ ltk_text_line_recalculate(LtkTextManager *tm, struct ltk_text_line *tl) {
 	struct ltk_text_run *old_runs = tl->first_run;
 	ltk_text_line_itemize(tl);
 	struct ltk_text_run *cur = tl->first_run;
-	ltk_text_line_shape(tm, tl);
+	ltk_text_line_shape(tl);
 	/* this needs to be done after shaping so the fonts, etc. aren't
 	   removed if their reference counts drop and then loaded again
 	   right afterwards */
@@ -837,8 +838,7 @@ ltk_text_line_insert_utf32(struct ltk_text_line *tl, size_t index, uint32_t *tex
 	ltk_array_resize_int(tl->vis2log, tl->len + len);
 	ltk_array_resize_level(tl->bidi_levels, tl->len + len);
 	tl->len += len;
-	/* FIXME: Why am I passing tm? It's global anyways */
-	ltk_text_line_recalculate(ltk_global->tm, tl);
+	ltk_text_line_recalculate(tl);
 }
 
 /* must be NULL-terminated */
diff --git a/text_common.c b/text_common.c
@@ -36,8 +36,6 @@
 #include "text_common.h"
 #include "ltk.h"
 
-extern Ltk *ltk_global;
-
 /* These unicode routines are taken from
  * https://github.com/JeffBezanson/cutef8 */
 
diff --git a/text_edit.c b/text_edit.c
@@ -26,21 +26,21 @@
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #include "khash.h"
-#include "ltk.h"
 #include "stb_truetype.h"
 #include <fribidi.h>
 #include <harfbuzz/hb.h>
 #include <fontconfig/fontconfig.h>
 #include "text_common.h"
+#include "ltk.h"
 #include "array.h"
 #include "text_buffer.h"
 #include "text_edit.h"
 #include "grid.h"
 
-extern Ltk *ltk_global;
-
 void
 ltk_text_edit_draw(LtkTextEdit *te) {
+	Display *display = ltk_get_display();
+	LtkTheme *theme = ltk_get_theme();
 	if (!te->img)
 		ltk_text_edit_resize(te, 0, 0);
 	LtkRect rect = te->widget.rect;
@@ -48,10 +48,10 @@ ltk_text_edit_draw(LtkTextEdit *te) {
 	int x = rect.x;
 	if (te->tl->dir == HB_DIRECTION_RTL)
 		x += rect.w - te->img->width;
-	XSetForeground(ltk_global->display, window->gc, ltk_global->theme->window->bg.pixel);
-	XFillRectangle(ltk_global->display, window->xwindow, window->gc, rect.x, rect.y, rect.w, rect.h);
+	XSetForeground(display, window->gc, theme->window->bg.pixel);
+	XFillRectangle(display, window->xwindow, window->gc, rect.x, rect.y, rect.w, rect.h);
 	XPutImage(
-	    ltk_global->display,
+	    display,
 	    window->xwindow,
 	    window->gc,
 	    te->img,
@@ -67,12 +67,15 @@ ltk_text_edit_resize(LtkTextEdit *te, int orig_w, int orig_h) {
 	if (te->tl->soft_lines->len == 1 &&
 	    te->widget.rect.w >= te->img->width && orig_w >= te->img->width)
 		return;
-	XColor fg = ltk_global->theme->window->fg;
-	XColor bg = ltk_global->theme->window->bg;
+	LtkTheme *theme = ltk_get_theme();
+	Display *display = ltk_get_display();
+	Colormap colormap = ltk_get_colormap();
+	XColor fg = theme->window->fg;
+	XColor bg = theme->window->bg;
 	LtkWindow *window = te->widget.window;
 	ltk_text_line_wrap(te->tl, te->widget.rect.w);
 	if (te->img) XDestroyImage(te->img);
-	te->img = ltk_text_line_render(te->tl, ltk_global->display, window->xwindow, window->gc, ltk_global->colormap, fg, bg);
+	te->img = ltk_text_line_render(te->tl, display, window->xwindow, window->gc, colormap, fg, bg);
 }
 
 #if 0