commit 236104387a38581c2d7e0b50dd208d6f2db4e312
parent ffdfc8d4bc5e24eab86b857e637d60c7521bf0b0
Author: lumidify <nobody@lumidify.org>
Date:   Wed, 30 Dec 2020 21:37:51 +0100
Improve error reporting
Diffstat:
| M | button.c |  |  | 44 | ++++++++++++++++++++++++++++---------------- | 
| M | button.h |  |  | 5 | +++-- | 
| M | color.c |  |  | 3 | ++- | 
| M | draw.c |  |  | 230 | ++++++++++++++++++++++++++++++++++++++++++++----------------------------------- | 
| M | draw.h |  |  | 5 | +++-- | 
| M | grid.c |  |  | 264 | +++++++++++++++++++++++++++++++++++++++++++------------------------------------ | 
| M | grid.h |  |  | 2 | +- | 
| M | ltk.h |  |  | 7 | ++++--- | 
| M | ltkd.c |  |  | 116 | +++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------- | 
| M | text_pango.c |  |  | 4 | +++- | 
| M | text_stb.c |  |  | 12 | ++++++------ | 
| M | util.c |  |  | 3 | ++- | 
12 files changed, 403 insertions(+), 292 deletions(-)
diff --git a/button.c b/button.c
@@ -196,15 +196,18 @@ ltk_button_mouse_release(ltk_button *button, XEvent event) {
 
 static ltk_button *
 ltk_button_create(ltk_window *window, const char *id, const char *text) {
+	char *text_copy;
 	ltk_button *button = malloc(sizeof(ltk_button));
-	if (!button) ltk_fatal("ERROR: Unable to allocate memory for ltk_button.\n");
+	if (!button) ltk_fatal_errno("Unable to allocate memory for ltk_button.\n");
 
 	ltk_fill_widget_defaults(&button->widget, id, window,
 	    <k_button_draw, <k_button_change_state, <k_button_destroy, 1, LTK_BUTTON);
 	button->widget.mouse_release = <k_button_mouse_release;
 	uint16_t font_size = window->theme.font_size;
-	/* FIXME: check return of strdup */
-	button->tl = ltk_text_line_create(window->xwindow, font_size, strdup(text), -1);
+	text_copy = strdup(text);
+	if (!text_copy)
+		ltk_fatal_errno("Unable to allocate button text.\n");
+	button->tl = ltk_text_line_create(window->xwindow, font_size, text_copy, -1);
 	int text_w, text_h;
 	ltk_text_line_get_size(button->tl, &text_w, &text_h);
 	button->widget.rect.w = text_w + theme.border_width * 2 + theme.pad * 2;
@@ -218,7 +221,7 @@ ltk_button_create(ltk_window *window, const char *id, const char *text) {
 static void
 ltk_button_destroy(ltk_button *button, int shallow) {
 	if (!button) {
-		(void)printf("WARNING: Tried to destroy NULL button.\n");
+		ltk_warn("Tried to destroy NULL button.\n");
 		return;
 	}
 	ltk_text_line_destroy(button->tl);
@@ -228,35 +231,44 @@ ltk_button_destroy(ltk_button *button, int shallow) {
 }
 
 /* button <button id> create <text> */
-static void
+static int
 ltk_button_cmd_create(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens) {
+    size_t num_tokens,
+    char **errstr) {
 	ltk_button *button;
 	if (num_tokens != 4) {
-		(void)fprintf(stderr, "button create: Invalid number of arguments.\n");
-		return;
+		*errstr = "Invalid number of arguments.\n";
+		return 1;
+	}
+	if (!ltk_widget_id_free(window, tokens[1])) {
+		*errstr = "Widget ID already taken.\n";
+		return 1;
 	}
-	if (!ltk_check_widget_id_free(window, tokens[1], "button create"))
-		return;
 	button = ltk_button_create(window, tokens[1], tokens[3]);
 	ltk_set_widget(window, button, tokens[1]);
+
+	return 0;
 }
 
 /* button <button id> <command> ... */
-void
+int
 ltk_button_cmd(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens) {
+    size_t num_tokens,
+    char **errstr) {
 	if (num_tokens < 3) {
-		(void)fprintf(stderr, "button: Invalid number of arguments.\n");
-		return;
+		*errstr = "Invalid number of arguments.\n";
+		return 1;
 	}
 	if (strcmp(tokens[2], "create") == 0) {
-		ltk_button_cmd_create(window, tokens, num_tokens);
+		return ltk_button_cmd_create(window, tokens, num_tokens, errstr);
 	} else {
-		(void)fprintf(stderr, "button: Invalid command.\n");
+		*errstr = "Invalid command.\n";
+		return 1;
 	}
+
+	return 0;
 }
diff --git a/button.h b/button.h
@@ -35,9 +35,10 @@ typedef struct {
 void ltk_button_setup_theme_defaults(ltk_window *window);
 void ltk_button_ini_handler(ltk_window *window, const char *prop, const char *value);
 
-void ltk_button_cmd(
+int ltk_button_cmd(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens);
+    size_t num_tokens,
+    char **errstr);
 
 #endif /* _LTK_BUTTON_H_ */
diff --git a/color.c b/color.c
@@ -7,7 +7,8 @@
 void
 ltk_color_create(Display *dpy, int screen, Colormap cm, const char *hex, LtkColor *col) {
 	if (!XParseColor(dpy, cm, hex, &col->xcolor)) {
-		/* FIXME: better error reporting */
+		/* FIXME: better error reporting!!! */
+		/* FIXME: remove obsolete ltk_err function */
 		ltk_err("ltk_color_create");
 	}
 	XAllocColor(dpy, cm, &col->xcolor);
diff --git a/draw.c b/draw.c
@@ -40,26 +40,31 @@ static void ltk_draw_clear(ltk_window *window, ltk_draw *draw);
 static void ltk_draw_set_color(ltk_window *window, ltk_draw *draw, const char *color);
 static void ltk_draw_line(ltk_window *window, ltk_draw *draw, int x1, int y1, int x2, int y2);
 static void ltk_draw_rect(ltk_window *window, ltk_draw *draw, int x, int y, int w, int h, int fill);
-static void ltk_draw_cmd_clear(
+static int ltk_draw_cmd_clear(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens);
-static void ltk_draw_cmd_set_color(
+    size_t num_tokens,
+    char **errstr);
+static int ltk_draw_cmd_set_color(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens);
-static void ltk_draw_cmd_line(
+    size_t num_tokens,
+    char **errstr);
+static int ltk_draw_cmd_line(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens);
-static void ltk_draw_cmd_rect(
+    size_t num_tokens,
+    char **errstr);
+static int ltk_draw_cmd_rect(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens);
-static void ltk_draw_cmd_create(
+    size_t num_tokens,
+    char **errstr);
+static int ltk_draw_cmd_create(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens);
+    size_t num_tokens,
+    char **errstr);
 
 static void
 ltk_draw_draw(ltk_draw *draw) {
@@ -73,7 +78,7 @@ static ltk_draw *
 ltk_draw_create(ltk_window *window, const char *id, int w, int h, const char *color) {
 	XWindowAttributes attrs;
 	ltk_draw *draw = malloc(sizeof(ltk_draw));
-	if (!draw) ltk_fatal("ERROR: Unable to allocate memory for ltk_draw.\n");
+	if (!draw) ltk_fatal_errno("Unable to allocate memory for ltk_draw.\n");
 
 	ltk_fill_widget_defaults(&draw->widget, id, window,
 	    <k_draw_draw, NULL, <k_draw_destroy, 1, LTK_DRAW);
@@ -85,7 +90,7 @@ ltk_draw_create(ltk_window *window, const char *id, int w, int h, const char *co
 	draw->pix = XCreatePixmap(window->dpy, window->xwindow, w, h, draw->depth);
 	if (!ltk_create_xcolor(window, color, &draw->bg)) {
 		free(draw);
-		return NULL;
+		ltk_fatal_errno("Unable to allocate XColor.\n");
 	}
 	draw->fg = draw->bg;
 	XSetForeground(window->dpy, window->gc, draw->bg.pixel);
@@ -120,7 +125,7 @@ ltk_draw_resize(ltk_draw *draw, int orig_w, int orig_h) {
 static void
 ltk_draw_destroy(ltk_draw *draw, int shallow) {
 	if (!draw) {
-		(void)printf("WARNING: Tried to destroy NULL draw.\n");
+		ltk_warn("Tried to destroy NULL draw.\n");
 		return;
 	}
 	ltk_remove_widget(draw->widget.window, draw->widget.id);
@@ -139,6 +144,7 @@ ltk_draw_clear(ltk_window *window, ltk_draw *draw) {
 	ltk_draw_draw(draw);
 }
 
+/* FIXME: Error return */
 static void
 ltk_draw_set_color(ltk_window *window, ltk_draw *draw, const char *color) {
 	XColor tmp;
@@ -183,166 +189,186 @@ ltk_draw_rect(ltk_window *window, ltk_draw *draw, int x, int y, int w, int h, in
 	}
 }
 
-static void
+static int
 ltk_draw_cmd_clear(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens) {
+    size_t num_tokens,
+    char **errstr) {
 	ltk_draw *draw;
 	if (num_tokens != 3) {
-		(void)fprintf(stderr, "draw clear: Invalid number of arguments.\n");
-		return;
+		*errstr = "Invalid number of arguments.\n";
+		return 1;
 	}
-	draw = ltk_get_widget(window, tokens[1], LTK_DRAW, "draw clear");
-	if (!draw) return;
+	draw = ltk_get_widget(window, tokens[1], LTK_DRAW, errstr);
+	if (!draw) return 1;
 	ltk_draw_clear(window, draw);
+
+	return 0;
 }
 
-static void
+static int
 ltk_draw_cmd_set_color(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens) {
+    size_t num_tokens,
+    char **errstr) {
 	ltk_draw *draw;
 	if (num_tokens != 4) {
-		(void)fprintf(stderr, "draw set-color: Invalid number of arguments.\n");
-		return;
+		*errstr = "Invalid number of arguments.\n";
+		return 1;
 	}
-	draw = ltk_get_widget(window, tokens[1], LTK_DRAW, "draw set-color");
-	if (!draw) return;
+	draw = ltk_get_widget(window, tokens[1], LTK_DRAW, errstr);
+	if (!draw) return 1;
 	ltk_draw_set_color(window, draw, tokens[3]);
+
+	return 0;
 }
 
-static void
+static int
 ltk_draw_cmd_line(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens) {
+    size_t num_tokens,
+    char **errstr) {
 	ltk_draw *draw;
 	int x1, y1, x2, y2;
-	const char *errstr;
+	const char *errstr_num;
 	if (num_tokens != 7) {
-		(void)fprintf(stderr, "draw line: Invalid number of arguments.\n");
-		return;
+		*errstr = "Invalid number of arguments.\n";
+		return 1;
 	}
-	draw = ltk_get_widget(window, tokens[1], LTK_DRAW, "draw line");
-	if (!draw) return;
-	x1 = strtonum(tokens[3], 0, 100000, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "draw line: Invalid x1: %s\n", errstr);
-		return;
+	draw = ltk_get_widget(window, tokens[1], LTK_DRAW, errstr);
+	if (!draw) return 1;
+	x1 = strtonum(tokens[3], 0, 100000, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid x1.\n";
+		return 1;
 	}
-	y1 = strtonum(tokens[4], 0, 100000, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "draw line: Invalid y1: %s\n", errstr);
-		return;
+	y1 = strtonum(tokens[4], 0, 100000, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid y1.\n";
+		return 1;
 	}
-	x2 = strtonum(tokens[5], 0, 100000, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "draw line: Invalid x2: %s\n", errstr);
-		return;
+	x2 = strtonum(tokens[5], 0, 100000, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid x2.\n";
+		return 1;
 	}
-	y2 = strtonum(tokens[6], 0, 100000, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "draw line: Invalid y2: %s\n", errstr);
-		return;
+	y2 = strtonum(tokens[6], 0, 100000, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid y2.\n";
+		return 1;
 	}
 	ltk_draw_line(window, draw, x1, y1, x2, y2);
+
+	return 0;
 }
 
-static void
+static int
 ltk_draw_cmd_rect(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens) {
+    size_t num_tokens,
+    char **errstr) {
 	ltk_draw *draw;
-	const char *errstr;
+	const char *errstr_num;
 	int x, y, w, h, fill;
 	if (num_tokens != 8) {
-		(void)fprintf(stderr, "draw clear: Invalid number of arguments.\n");
-		return;
+		*errstr = "Invalid number of arguments.\n";
+		return 1;
 	}
-	draw = ltk_get_widget(window, tokens[1], LTK_DRAW, "draw clear");
-	if (!draw) return;
-	x = strtonum(tokens[3], 0, 100000, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "draw rect: Invalid x: %s\n", errstr);
-		return;
+	draw = ltk_get_widget(window, tokens[1], LTK_DRAW, errstr);
+	if (!draw) return 1;
+	x = strtonum(tokens[3], 0, 100000, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid x.\n";
+		return 1;
 	}
-	y = strtonum(tokens[4], 0, 100000, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "draw rect: Invalid y: %s\n", errstr);
-		return;
+	y = strtonum(tokens[4], 0, 100000, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid y.\n";
+		return 1;
 	}
-	w = strtonum(tokens[5], 1, 100000, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "draw rect: Invalid width: %s\n", errstr);
-		return;
+	w = strtonum(tokens[5], 1, 100000, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid width.\n";
+		return 1;
 	}
-	h = strtonum(tokens[6], 1, 100000, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "draw rect: Invalid height: %s\n", errstr);
-		return;
+	h = strtonum(tokens[6], 1, 100000, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid height.\n";
+		return 1;
 	}
-	fill = strtonum(tokens[7], 0, 1, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "draw rect: Invalid fill bool: %s\n", errstr);
-		return;
+	fill = strtonum(tokens[7], 0, 1, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid fill bool.\n";
+		return 1;
 	}
 	ltk_draw_rect(window, draw, x, y, w, h, fill);
+
+	return 0;
 }
 
 /* draw <draw id> create <width> <height> <color> */
-static void
+static int
 ltk_draw_cmd_create(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens) {
+    size_t num_tokens,
+    char **errstr) {
 	ltk_draw *draw;
 	int w, h;
-	const char *errstr;
+	const char *errstr_num;
 	if (num_tokens != 6) {
-		(void)fprintf(stderr, "draw create: Invalid number of arguments.\n");
-		return;
+		*errstr = "Invalid number of arguments.\n";
+		return 1;
 	}
-	if (!ltk_check_widget_id_free(window, tokens[1], "draw create"))
-		return;
-	w = strtonum(tokens[3], 1, 100000, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "draw create: Invalid width: %s\n", errstr);
-		return;
+	if (!ltk_widget_id_free(window, tokens[1])) {
+		*errstr = "Widget ID already taken.\n";
+		return 1;
 	}
-	h = strtonum(tokens[4], 1, 100000, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "draw create: Invalid height: %s\n", errstr);
-		return;
+	w = strtonum(tokens[3], 1, 100000, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid width.\n";
+		return 1;
+	}
+	h = strtonum(tokens[4], 1, 100000, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid height.\n";
+		return 1;
 	}
 	draw = ltk_draw_create(window, tokens[1], w, h, tokens[5]);
-	if (draw)
-		ltk_set_widget(window, draw, tokens[1]);
+	ltk_set_widget(window, draw, tokens[1]);
+
+	return 0;
 }
 
 /* draw <draw id> <command> ... */
-void
+int
 ltk_draw_cmd(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens) {
+    size_t num_tokens,
+    char **errstr) {
 	if (num_tokens < 3) {
-		(void)fprintf(stderr, "draw: Invalid number of arguments.\n");
-		return;
+		*errstr = "Invalid number of arguments.\n";
+		return 1;
 	}
 	if (strcmp(tokens[2], "create") == 0) {
-		ltk_draw_cmd_create(window, tokens, num_tokens);
+		return ltk_draw_cmd_create(window, tokens, num_tokens, errstr);
 	} else if (strcmp(tokens[2], "clear") == 0) {
-		ltk_draw_cmd_clear(window, tokens, num_tokens);
+		return ltk_draw_cmd_clear(window, tokens, num_tokens, errstr);
 	} else if (strcmp(tokens[2], "set-color") == 0) {
-		ltk_draw_cmd_set_color(window, tokens, num_tokens);
+		return ltk_draw_cmd_set_color(window, tokens, num_tokens, errstr);
 	} else if (strcmp(tokens[2], "line") == 0) {
-		ltk_draw_cmd_line(window, tokens, num_tokens);
+		return ltk_draw_cmd_line(window, tokens, num_tokens, errstr);
 	} else if (strcmp(tokens[2], "rect") == 0) {
-		ltk_draw_cmd_rect(window, tokens, num_tokens);
+		return ltk_draw_cmd_rect(window, tokens, num_tokens, errstr);
 	} else {
-		(void)fprintf(stderr, "draw: Invalid command.\n");
+		*errstr = "Invalid command.\n";
+		return 1;
 	}
+
+	return 0;
 }
diff --git a/draw.h b/draw.h
@@ -34,9 +34,10 @@ typedef struct {
 	XColor bg;
 } ltk_draw;
 
-void ltk_draw_cmd(
+int ltk_draw_cmd(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens);
+    size_t num_tokens,
+    char **errstr);
 
 #endif /* _LTK_DRAW_H_ */
diff --git a/grid.c b/grid.c
@@ -41,35 +41,40 @@ static ltk_grid *ltk_grid_create(ltk_window *window, const char *id,
     int rows, int columns);
 static void ltk_grid_destroy(ltk_grid *grid, int shallow);
 static void ltk_recalculate_grid(ltk_grid *grid);
-static void ltk_grid_add(ltk_window *window, ltk_widget *widget, ltk_grid *grid,
-    int row, int column, int row_span, int column_span, unsigned short sticky);
-static void ltk_grid_ungrid(ltk_window *window, ltk_widget *widget, ltk_grid *grid);
+static int ltk_grid_add(ltk_window *window, ltk_widget *widget, ltk_grid *grid,
+    int row, int column, int row_span, int column_span, unsigned short sticky, char **errstr);
+static int ltk_grid_ungrid(ltk_window *window, ltk_widget *widget, ltk_grid *grid, 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 void ltk_grid_mouse_press(ltk_grid *grid, XEvent event);
 static void ltk_grid_mouse_release(ltk_grid *grid, XEvent event);
 static void ltk_grid_motion_notify(ltk_grid *grid, XEvent event);
 
-static void ltk_grid_cmd_add(
+static int ltk_grid_cmd_add(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens);
-static void ltk_grid_cmd_ungrid(
+    size_t num_tokens,
+    char **errstr);
+static int ltk_grid_cmd_ungrid(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens);
-static void ltk_grid_cmd_create(
+    size_t num_tokens,
+    char **errstr);
+static int ltk_grid_cmd_create(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens);
-static void ltk_grid_cmd_set_row_weight(
+    size_t num_tokens,
+    char **errstr);
+static int ltk_grid_cmd_set_row_weight(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens);
-static void ltk_grid_cmd_set_column_weight(
+    size_t num_tokens,
+    char **errstr);
+static int ltk_grid_cmd_set_column_weight(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens);
+    size_t num_tokens,
+    char **errstr);
 
 static void
 ltk_grid_set_row_weight(ltk_grid *grid, int row, int weight) {
@@ -251,12 +256,12 @@ ltk_recalculate_grid(ltk_grid *grid) {
 	}
 }
 
-static void
+static int
 ltk_grid_add(ltk_window *window, ltk_widget *widget, ltk_grid *grid,
-    int row, int column, int row_span, int column_span, unsigned short sticky) {
+    int row, int column, int row_span, int column_span, unsigned short sticky, char **errstr) {
 	if (row + row_span > grid->rows || column + column_span > grid->columns) {
-		(void)fprintf(stderr, "Invalid row or column.\n");
-		return;
+		*errstr = "Invalid row or column.\n";
+		return 1;
 	}
 	widget->sticky = sticky;
 	widget->row = row;
@@ -277,13 +282,15 @@ ltk_grid_add(ltk_window *window, ltk_widget *widget, ltk_grid *grid,
 	widget->parent = grid;
 	ltk_recalculate_grid(grid);
 	ltk_window_invalidate_rect(window, grid->widget.rect);
+
+	return 0;
 }
 
-static void
-ltk_grid_ungrid(ltk_window *window, ltk_widget *widget, ltk_grid *grid) {
+static int
+ltk_grid_ungrid(ltk_window *window, ltk_widget *widget, ltk_grid *grid, char **errstr) {
 	if (widget->parent != grid) {
-		(void)fprintf(stderr, "grid remove: Widget isn't gridded in given grid.\n");
-		return;
+		*errstr = "Widget isn't gridded in given grid.\n";
+		return 1;
 	}
 	widget->parent = NULL;
 	for (int i = widget->row; i < widget->row + widget->row_span; i++) {
@@ -292,6 +299,8 @@ ltk_grid_ungrid(ltk_window *window, ltk_widget *widget, ltk_grid *grid) {
 		}
 	}
 	ltk_window_invalidate_rect(window, grid->widget.rect);
+
+	return 0;
 }
 
 static int
@@ -359,175 +368,192 @@ ltk_grid_motion_notify(ltk_grid *grid, XEvent event) {
 }
 
 /* grid <grid id> add <widget id> <row> <column> <row_span> <column_span> <sticky> */
-static void
+static int
 ltk_grid_cmd_add(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens) {
+    size_t num_tokens,
+    char **errstr) {
 	ltk_grid *grid;
 	ltk_widget *widget;
-	const char *errstr;
+	const char *errstr_num;
 
 	int row, column, row_span, column_span, sticky;
 	if (num_tokens != 9) {
-		(void)fprintf(stderr, "grid: Invalid number of arguments.\n");
-		return;
-	}
-	grid = ltk_get_widget(window, tokens[1], LTK_GRID, "grid add");
-	widget = ltk_get_widget(window, tokens[3], LTK_WIDGET, "grid add");
-	if (!grid || !widget) return;
-	row         = strtonum(tokens[4], 0, grid->rows - 1, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "grid add: Invalid row number: %s\n", errstr);
-		return;
-	}
-	column      = strtonum(tokens[5], 0, grid->columns - 1, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "grid add: Invalid column number: %s\n", errstr);
-		return;
-	}
-	row_span    = strtonum(tokens[6], 1, grid->rows, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "grid add: Invalid row_span: %s\n", errstr);
-		return;
-	}
-	column_span = strtonum(tokens[7], 1, grid->columns, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "grid add: Invalid column_span: %s\n", errstr);
-		return;
-	}
-	sticky      = strtonum(tokens[8], 0, 15, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "grid add: Invalid sticky setting: %s\n", errstr);
-		return;
-	}
-	ltk_grid_add(window, widget, grid, row, column, row_span, column_span, sticky);
+		*errstr = "Invalid number of arguments.\n";
+		return 1;
+	}
+	grid = ltk_get_widget(window, tokens[1], LTK_GRID, errstr);
+	widget = ltk_get_widget(window, tokens[3], LTK_WIDGET, errstr);
+	if (!grid || !widget) return 1;
+	row         = strtonum(tokens[4], 0, grid->rows - 1, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid row number.\n";
+		return 1;
+	}
+	column      = strtonum(tokens[5], 0, grid->columns - 1, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid row number.\n";
+		return 1;
+	}
+	row_span    = strtonum(tokens[6], 1, grid->rows, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid row span.\n";
+		return 1;
+	}
+	column_span = strtonum(tokens[7], 1, grid->columns, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid column span.\n";
+		return 1;
+	}
+	sticky      = strtonum(tokens[8], 0, 15, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid sticky setting.\n";
+		return 1;
+	}
+	return ltk_grid_add(window, widget, grid, row, column, row_span, column_span, sticky, errstr);
 }
 
 /* grid <grid id> remove <widget id> */
-static void
+static int
 ltk_grid_cmd_ungrid(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens) {
+    size_t num_tokens,
+    char **errstr) {
 	ltk_grid *grid;
 	ltk_widget *widget;
 	if (num_tokens != 4) {
-		(void)fprintf(stderr, "grid ungrid: Invalid number of arguments.\n");
-		return;
+		*errstr = "Invalid number of arguments.\n";
+		return 1;
 	}
-	grid = ltk_get_widget(window, tokens[1], LTK_GRID, "grid remove");
-	widget = ltk_get_widget(window, tokens[3], LTK_WIDGET, "grid ungrid");
-	if (!grid || !widget) return;
-	ltk_grid_ungrid(window, widget, grid);
+	grid = ltk_get_widget(window, tokens[1], LTK_GRID, errstr);
+	widget = ltk_get_widget(window, tokens[3], LTK_WIDGET, errstr);
+	if (!grid || !widget) return 1;
+	return ltk_grid_ungrid(window, widget, grid, errstr);
 }
 
 /* grid <grid id> create <rows> <columns> */
-static void
+static int
 ltk_grid_cmd_create(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens) {
+    size_t num_tokens,
+    char **errstr) {
 	int rows, columns;
 	ltk_grid *grid;
-	const char *errstr;
+	const char *errstr_num;
 	if (num_tokens != 5) {
-		(void)fprintf(stderr, "grid create: Invalid number of arguments.\n");
-		return;
+		*errstr = "Invalid number of arguments.\n";
+		return 1;
 	}
-	if (!ltk_check_widget_id_free(window, tokens[1], "grid create"))
-		return;
-	rows    = strtonum(tokens[3], 1, 64, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "grid create: Invalid number of rows: %s\n ", errstr);
-		return;
+	if (!ltk_widget_id_free(window, tokens[1])) {
+		*errstr = "Widget ID already taken.\n";
+		return 1;
 	}
-	columns = strtonum(tokens[4], 1, 64, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "grid create: Invalid number of columns: %s\n ", errstr);
-		return;
+	rows    = strtonum(tokens[3], 1, 64, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid number of rows.\n";
+		return 1;
+	}
+	columns = strtonum(tokens[4], 1, 64, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid number of columns.\n";
+		return 1;
 	}
 	grid = ltk_grid_create(window, tokens[1], rows, columns);
 	ltk_set_widget(window, grid, tokens[1]);
+
+	return 0;
 }
 
 /* grid <grid id> set-row-weight <row> <weight> */
-static void
+static int
 ltk_grid_cmd_set_row_weight(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens) {
+    size_t num_tokens,
+    char **errstr) {
 	ltk_grid *grid;
 	int row, weight;
-	const char *errstr;
+	const char *errstr_num;
 	if (num_tokens != 5) {
-		(void)fprintf(stderr, "grid set-row-weight: Invalid number of arguments.\n");
-		return;
+		*errstr = "Invalid number of arguments.\n";
+		return 1;
 	}
-	grid = ltk_get_widget(window, tokens[1], LTK_GRID, "grid set-row-weight");
-	if (!grid) return;
-	row    = strtonum(tokens[3], 0, grid->rows, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "grid set-row-weight: Invalid row number: %s\n ", errstr);
-		return;
+	grid = ltk_get_widget(window, tokens[1], LTK_GRID, errstr);
+	if (!grid) return 1;
+	row    = strtonum(tokens[3], 0, grid->rows, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid row number.\n";
+		return 1;
 	}
-	weight = strtonum(tokens[4], 0, 64, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "grid set-row-weight: Invalid weight: %s\n ", errstr);
-		return;
+	weight = strtonum(tokens[4], 0, 64, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid row weight.\n";
+		return 1;
 	}
 	ltk_grid_set_row_weight(grid, row, weight);
+
+	return 0;
 }
 
 /* grid <grid id> set-column-weight <column> <weight> */
-static void
+static int
 ltk_grid_cmd_set_column_weight(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens) {
+    size_t num_tokens,
+    char **errstr) {
 	ltk_grid *grid;
 	int column, weight;
-	const char *errstr;
+	const char *errstr_num;
 	if (num_tokens != 5) {
-		(void)fprintf(stderr, "grid set-column-weight: Invalid number of arguments.\n");
-		return;
+		*errstr = "Invalid number of arguments.\n";
+		return 1;
 	}
-	grid = ltk_get_widget(window, tokens[1], LTK_GRID, "grid set-column-weight");
-	if (!grid) return;
-	column = strtonum(tokens[3], 0, grid->columns, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "grid set-column-weight: Invalid column number: %s\n ", errstr);
-		return;
+	grid = ltk_get_widget(window, tokens[1], LTK_GRID, errstr);
+	if (!grid) return 1;
+	column = strtonum(tokens[3], 0, grid->columns, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid column number.\n";
+		return 1;
 	}
-	weight = strtonum(tokens[4], 0, 64, &errstr);
-	if (errstr) {
-		(void)fprintf(stderr, "grid set-column-weight: Invalid weight: %s\n ", errstr);
-		return;
+	weight = strtonum(tokens[4], 0, 64, &errstr_num);
+	if (errstr_num) {
+		*errstr = "Invalid column weight.\n";
+		return 1;
 	}
 	ltk_grid_set_column_weight(grid, column, weight);
+
+	return 0;
 }
 
 /* grid <grid id> <command> ... */
-void
+int
 ltk_grid_cmd(
     ltk_window *window,
     char **tokens,
-    size_t num_tokens) {
+    size_t num_tokens,
+    char **errstr) {
 	if (num_tokens < 3) {
-		(void)fprintf(stderr, "grid: Invalid number of arguments.\n");
-		return;
+		*errstr = "Invalid number of arguments.\n";
+		return 1;
 	}
 	if (strcmp(tokens[2], "add") == 0) {
-		ltk_grid_cmd_add(window, tokens, num_tokens);
+		return ltk_grid_cmd_add(window, tokens, num_tokens, errstr);
 	} else if (strcmp(tokens[2], "ungrid") == 0) {
-		ltk_grid_cmd_ungrid(window, tokens, num_tokens);
+		return ltk_grid_cmd_ungrid(window, tokens, num_tokens, errstr);
 	} else if (strcmp(tokens[2], "create") == 0) {
-		ltk_grid_cmd_create(window, tokens, num_tokens);
+		return ltk_grid_cmd_create(window, tokens, num_tokens, errstr);
 	} else if (strcmp(tokens[2], "set-row-weight") == 0) {
-		ltk_grid_cmd_set_row_weight(window, tokens, num_tokens);
+		return ltk_grid_cmd_set_row_weight(window, tokens, num_tokens, errstr);
 	} else if (strcmp(tokens[2], "set-column-weight") == 0) {
-		ltk_grid_cmd_set_column_weight(window, tokens, num_tokens);
+		return ltk_grid_cmd_set_column_weight(window, tokens, num_tokens, errstr);
 	} else {
-		(void)fprintf(stderr, "button: Invalid command.\n");
+		*errstr = "Invalid command.\n";
+		return 1;
 	}
+
+	return 0; /* Well, I guess this is impossible anyways... */
 }
diff --git a/grid.h b/grid.h
@@ -42,6 +42,6 @@ typedef struct {
 	unsigned int *column_pos;
 } ltk_grid;
 
-void ltk_grid_cmd(ltk_window *window, char **tokens, size_t num_tokens);
+int ltk_grid_cmd(ltk_window *window, char **tokens, size_t num_tokens, char **errstr);
 
 #endif
diff --git a/ltk.h b/ltk.h
@@ -137,6 +137,8 @@ typedef struct ltk_window {
 void ltk_window_invalidate_rect(ltk_window *window, ltk_rect rect);
 void ltk_warn(const char *format, ...);
 void ltk_fatal(const char *format, ...);
+void ltk_warn_errno(const char *format, ...);
+void ltk_fatal_errno(const char *format, ...);
 int ltk_create_xcolor(ltk_window *window, const char *hex, XColor *col);
 void ltk_queue_event(ltk_window *window, ltk_event_type type, const char *id, const char *data);
 int ltk_collide_rect(ltk_rect rect, int x, int y);
@@ -149,10 +151,9 @@ void ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window * w
 void ltk_widget_mouse_press_event(ltk_widget *widget, XEvent event);
 void ltk_widget_mouse_release_event(ltk_widget *widget, XEvent event);
 void ltk_widget_motion_notify_event(ltk_widget *widget, XEvent event);
-int ltk_check_widget_id_free(ltk_window *window, const char *id,
-    const char *caller);
+int ltk_check_widget_id_free(ltk_window *window, const char *id);
 ltk_widget *ltk_get_widget(ltk_window *window, const char *id,
-    ltk_widget_type type, const char *caller);
+    ltk_widget_type type, char **errstr);
 void ltk_set_widget(ltk_window *window, ltk_widget *widget, const char *id);
 
 #endif
diff --git a/ltkd.c b/ltkd.c
@@ -94,7 +94,7 @@ static int read_sock(struct ltk_sock_info *sock);
 static int write_sock(struct ltk_sock_info *sock);
 static int queue_sock_write(struct ltk_sock_info *sock, const char *str, int len);
 static int tokenize_command(struct ltk_sock_info *sock);
-static void ltk_set_root_widget_cmd(ltk_window *window, char **tokens, int num_tokens);
+static int ltk_set_root_widget_cmd(ltk_window *window, char **tokens, int num_tokens, char **errstr);
 static void process_commands(ltk_window *window, struct ltk_sock_info *sock);
 static ltk_rect ltk_rect_union(ltk_rect r1, ltk_rect r2);
 static int add_client(int fd);
@@ -126,16 +126,16 @@ int main(int argc, char *argv[]) {
 	}
 
 	ltk_dir = ltk_setup_directory();
-	if (!ltk_dir) ltk_fatal("Unable to setup ltk directory.\n");
+	if (!ltk_dir) ltk_fatal_errno("Unable to setup ltk directory.\n");
 	ltk_logfile = open_log(ltk_dir);
-	if (!ltk_logfile) ltk_fatal("Unable to open log file.\n");
+	if (!ltk_logfile) ltk_fatal_errno("Unable to open log file.\n");
 
 	/* FIXME: set window size properly - I only run it in a tiling WM
 	   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);
-	if (!sock_path) ltk_fatal("Unable to allocate memory for socket path.\n");
+	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 */
 	for (int i = 0; i < MAX_SOCK_CONNS; i++) {
@@ -164,7 +164,7 @@ ltk_mainloop(ltk_window *window) {
 	FD_ZERO(&wallfds);
 
 	if ((listenfd = listen_sock(sock_path)) < 0)
-		ltk_fatal("Error listening on socket.\n");
+		ltk_fatal_errno("Error listening on socket.\n");
 
 	FD_SET(listenfd, &rallfds);
 	maxfd = listenfd;
@@ -191,7 +191,7 @@ ltk_mainloop(ltk_window *window) {
 			if (FD_ISSET(listenfd, &rfds)) {
 				if ((clifd = accept_sock(listenfd)) < 0) {
 					/* FIXME: Just log this! */
-					ltk_fatal("Error accepting socket connection.\n");
+					ltk_fatal_errno("Error accepting socket connection.\n");
 				}
 				int i = add_client(clifd);
 				FD_SET(clifd, &rallfds);
@@ -207,7 +207,6 @@ ltk_mainloop(ltk_window *window) {
 					continue;
 				if (FD_ISSET(clifd, &rfds)) {
 					if (read_sock(&sockets[i]) == 0) {
-						fprintf(stderr, "Closed socket fd %d\n", clifd); /* FIXME */
 						FD_CLR(clifd, &rallfds);
 						FD_CLR(clifd, &wallfds);
 						sockets[i].fd = -1;
@@ -236,7 +235,7 @@ ltk_mainloop(ltk_window *window) {
 				for (int i = 0; i <= maxsocket; i++) {
 					if (sockets[i].fd != -1 && sockets[i].event_mask & cur->event_type) {
 						if (queue_sock_write(&sockets[i], cur->data, event_len) < 0)
-							ltk_fatal("Unable to queue event.\n");
+							ltk_fatal_errno("Unable to queue event.\n");
 					}
 				}
 				free(cur->data);
@@ -267,7 +266,7 @@ daemonize(void) {
 	fflush(ltk_logfile);
 
 	if ((pid = fork()) < 0)
-		ltk_fatal("Can't fork.\n");
+		ltk_fatal_errno("Can't fork.\n");
 	else if (pid != 0)
 		exit(0);
 	setsid();
@@ -276,14 +275,14 @@ daemonize(void) {
 	sigemptyset(&sa.sa_mask);
 	sa.sa_flags = 0;
 	if (sigaction(SIGHUP, &sa, NULL) < 0)
-		ltk_fatal("Unable to ignore SIGHUP.\n");
+		ltk_fatal_errno("Unable to ignore SIGHUP.\n");
 	if ((pid  = fork()) < 0)
-		ltk_fatal("Can't fork.\n");
+		ltk_fatal_errno("Can't fork.\n");
 	else if (pid != 0)
 		exit(0);
 
 	if (chdir("/") < 0)
-		ltk_fatal("Can't change directory to root.\n");
+		ltk_fatal_errno("Can't change directory to root.\n");
 
 	close(fileno(stdin));
 	close(fileno(stdout));
@@ -322,6 +321,10 @@ open_log(char *dir) {
 	if (!path)
 		return NULL;
 	f = fopen(path, "a");
+	if (!f) {
+		free(path);
+		return NULL;
+	}
 	free(path);
 
 	return f;
@@ -362,26 +365,28 @@ ltk_quit(ltk_window *window) {
 	running = 0;
 }
 
-static void
+static int
 ltk_set_root_widget_cmd(
     ltk_window *window,
     char **tokens,
-    int num_tokens) {
+    int num_tokens,
+    char **errstr) {
 	ltk_widget *widget;
 	if (num_tokens != 2) {
-		(void)fprintf(stderr, "set-root-widget: Invalid number of arguments.\n");
-		return;
+		*errstr = "Invalid number of arguments.\n";
+		return 1;
 	}
-	widget = ltk_get_widget(window, tokens[1], LTK_WIDGET, "set-root-widget");
-	if (!widget) return;
+	widget = ltk_get_widget(window, tokens[1], LTK_WIDGET, errstr);
+	if (!widget) return 1;
 	window->root_widget = widget;
 	int w = widget->rect.w;
 	int h = widget->rect.h;
 	widget->rect.w = window->rect.w;
 	widget->rect.h = window->rect.h;
-	if (widget->resize) {
+	if (widget->resize)
 		widget->resize(widget, w, h);
-	}
+
+	return 0;
 }
 
 static ltk_rect
@@ -446,12 +451,31 @@ ltk_fatal(const char *format, ...) {
 	exit(1);
 };
 
+void
+ltk_warn_errno(const char *format, ...) {
+	va_list args;
+	char *errstr = strerror(errno);
+	va_start(args, format);
+	print_log("Warning", format, args);
+	va_end(args);
+	ltk_warn("system error: %s\n", errstr);
+}
+
+void
+ltk_fatal_errno(const char *format, ...) {
+	va_list args;
+	char *errstr = strerror(errno);
+	va_start(args, format);
+	print_log("Fatal", format, args);
+	va_end(args);
+	ltk_fatal("system error: %s\n", errstr);
+}
+
+/* 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)) {
-		(void)fprintf(stderr, "Invalid color: %s\n", hex);
+	if (!XParseColor(window->dpy, window->cm, hex, col))
 		return 0;
-	}
 	XAllocColor(window->dpy, window->cm, col);
 
 	return 1;
@@ -461,12 +485,12 @@ void
 ltk_queue_event(ltk_window *window, ltk_event_type type, const char *id, const char *data) {
 	/* FIXME: make it nicer and safer */
 	struct ltk_event_queue *new = malloc(sizeof(struct ltk_event_queue));
-	if (!new) ltk_fatal("Unable to queue event.\n");
+	if (!new) ltk_fatal_errno("Unable to queue event.\n");
 	new->event_type = type;
 	int id_len = strlen(id);
 	int data_len = strlen(data);
 	new->data = malloc(id_len + data_len + 3);
-	if (!new->data) ltk_fatal("Unable to queue event.\n");
+	if (!new->data) ltk_fatal_errno("Unable to queue event.\n");
 	strcpy(new->data, id);
 	new->data[id_len] = ' ';
 	strcpy(new->data + id_len + 1, data);
@@ -533,14 +557,15 @@ ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int 
 
 	ltk_window *window = malloc(sizeof(ltk_window));
 	if (!window)
-		ltk_fatal("Not enough memory left for window!\n");
+		ltk_fatal_errno("Not enough memory left for window!\n");
 
 	window->dpy = XOpenDisplay(NULL);
 	window->screen = DefaultScreen(window->dpy);
 	window->cm = DefaultColormap(window->dpy, window->screen);
 	theme_path = ltk_strcat_useful(ltk_dir, "/theme.ini");
+	window->theme.font = NULL;
 	if (!theme_path)
-		ltk_fatal("Not enough memory for theme path.\n");
+		ltk_fatal_errno("Not enough memory for theme path.\n");
 	ltk_load_theme(window, theme_path);
 	window->wm_delete_msg = XInternAtom(window->dpy, "WM_DELETE_WINDOW", False);
 
@@ -595,6 +620,8 @@ ltk_destroy_window(ltk_window *window) {
 	kh_destroy(widget, window->widget_hash);
 	ltk_cleanup_text();
 	XCloseDisplay(window->dpy);
+	if (window->theme.font)
+		free(window->theme.font);
 	free(window);
 }
 
@@ -608,6 +635,8 @@ ltk_window_ini_handler(ltk_window *window, const char *prop, const char *value) 
 		ltk_create_xcolor(window, value, &window->theme.fg);
 	} else if (strcmp(prop, "font") == 0) {
 		window->theme.font = strdup(value);
+		if (!window->theme.font)
+			ltk_fatal_errno("Unable to allocate copy of font name.\n");
 	} else if (strcmp(prop, "font_size") == 0) {
 		window->theme.font_size = atoi(value);
 	}
@@ -685,6 +714,8 @@ ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window *window,
     void (*destroy) (void *, int), unsigned int needs_redraw,
     ltk_widget_type type) {
 	widget->id = strdup(id);
+	if (!widget->id)
+		ltk_fatal_errno("Unable to allocate copy of widget ID.\n");
 	widget->window = window;
 	widget->active_widget = NULL;
 	widget->parent = NULL;
@@ -800,11 +831,10 @@ ltk_handle_event(ltk_window *window, XEvent event) {
 }
 
 int
-ltk_check_widget_id_free(ltk_window *window, const char *id, const char *caller) {
+ltk_widget_id_free(ltk_window *window, const char *id) {
 	khint_t k;
 	k = kh_get(widget, window->widget_hash, id);
 	if (k != kh_end(window->widget_hash)) {
-		(void)fprintf(stderr, "%s: Widget \"%s\" already exists.\n", caller, id);
 		return 0;
 	}
 	return 1;
@@ -812,17 +842,17 @@ ltk_check_widget_id_free(ltk_window *window, const char *id, const char *caller)
 
 ltk_widget *
 ltk_get_widget(ltk_window *window, const char *id, ltk_widget_type type,
-    const char *caller) {
+    char **errstr) {
 	khint_t k;
 	ltk_widget *widget;
 	k = kh_get(widget, window->widget_hash, id);
 	if (k == kh_end(window->widget_hash)) {
-		(void)fprintf(stderr, "%s: Widget \"%s\" doesn't exist.\n", caller, id);
+		*errstr = "Widget with given ID doesn't exist.\n";
 		return NULL;
 	}
 	widget = kh_value(window->widget_hash, k);
 	if (type != LTK_WIDGET && widget->type != type) {
-		(void)fprintf(stderr, "%s: Widget \"%s\" has wrong type.\n", caller, id);
+		*errstr = "Widget with given ID has wrong type.\n";
 		return NULL;
 	}
 	return widget;
@@ -833,7 +863,10 @@ ltk_set_widget(ltk_window *window, ltk_widget *widget, const char *id) {
 	int ret;
 	khint_t k;
 	/* apparently, khash requires the string to stay accessible */
+	/* FIXME: How is this freed? */
 	char *tmp = strdup(id);
+	if (!tmp)
+		ltk_fatal_errno("Unable to add widget to hash table.\n");
 	k = kh_put(widget, window->widget_hash, tmp, &ret);
 	kh_value(window->widget_hash, k) = widget;
 }
@@ -1079,26 +1112,33 @@ static void
 process_commands(ltk_window *window, struct ltk_sock_info *sock) {
 	char **tokens;
 	int num_tokens;
+	char *errstr;
+	int err;
 	while (!tokenize_command(sock)) {
+		err = 0;
 		tokens = sock->tokens.tokens;
 		num_tokens = sock->tokens.num_tokens;
 		if (num_tokens < 1)
 			continue;
 		if (strcmp(tokens[0], "grid") == 0) {
-			ltk_grid_cmd(window, tokens, num_tokens);
+			err = ltk_grid_cmd(window, tokens, num_tokens, &errstr);
 		} else if (strcmp(tokens[0], "button") == 0) {
-			ltk_button_cmd(window, tokens, num_tokens);
+			err = ltk_button_cmd(window, tokens, num_tokens, &errstr);
 		} else if (strcmp(tokens[0], "set-root-widget") == 0) {
-			ltk_set_root_widget_cmd(window, tokens, num_tokens);
+			err = ltk_set_root_widget_cmd(window, tokens, num_tokens, &errstr);
 		} else if (strcmp(tokens[0], "draw") == 0) {
-			ltk_draw_cmd(window, tokens, num_tokens);
+			err = ltk_draw_cmd(window, tokens, num_tokens, &errstr);
 		} else if (strcmp(tokens[0], "quit") == 0) {
 			ltk_quit(window);
 		} else {
-			/* FIXME... */
-			(void)fprintf(stderr, "Invalid command.\n");
+			errstr = "Invalid command.\n";
+			err = 1;
 		}
 		sock->tokens.num_tokens = 0;
+		if (err) {
+			if (queue_sock_write(sock, errstr, -1) < 0)
+				ltk_fatal("Unable to queue socket write.\n");
+		}
 	}
 	if (sock->tokens.num_tokens > 0 && sock->tokens.tokens[0] != sock->read) {
 		memmove(sock->read, sock->tokens.tokens[0], sock->read + sock->read_len - sock->tokens.tokens[0]);
diff --git a/text_pango.c b/text_pango.c
@@ -8,6 +8,7 @@
 #include <pango/pangoxft.h>
 #include "util.h"
 #include "text.h"
+#include "ltk.h"
 
 struct LtkTextLine {
 	char *text;
@@ -34,7 +35,8 @@ 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 = strdup(default_font);
-	if (!tm.default_font) ltk_err("ltk_init_text (pango)");
+	if (!tm.default_font)
+		ltk_fatal_errno("Unable to allocate copy of font path.\n");
 	tm.dpy = dpy;
 	tm.screen = screen;
 	tm.cm = cm;
diff --git a/text_stb.c b/text_stb.c
@@ -318,17 +318,17 @@ ltk_create_font(char *path, uint16_t id, int index) {
 	LtkFont *font = malloc(sizeof(LtkFont));
 	if (!font) ltk_err("ltk_create_font (stb)");
 	char *contents = ltk_read_file(path, &len);
-	/* FIXME: error checking */
+	if (!contents)
+		ltk_fatal_errno("Unable to read font file %s\n", path);
 	int offset = stbtt_GetFontOffsetForIndex(contents, index);
-	if (!stbtt_InitFont(&font->info, contents, offset)) {
-		(void)fprintf(stderr, "Failed to load font %s\n", path);
-		exit(1);
-	}
+	if (!stbtt_InitFont(&font->info, contents, offset))
+		ltk_fatal("Failed to load font %s\n", path);
 	font->id = id;
 	font->refs = 0;
 	font->index = index;
 	font->path = strdup(path);
-	if (!font->path) ltk_err("ltk_create_font");
+	if (!font->path)
+		ltk_fatal_errno("Unable to allocate copy of font path.\n");
 
 	return font;
 }
diff --git a/util.c b/util.c
@@ -38,11 +38,12 @@ ltk_read_file(const char *path, unsigned long *len) {
 	FILE *f;
 	char *file_contents;
 	f = fopen(path, "rb");
+	if (!f) return NULL;
 	fseek(f, 0, SEEK_END);
 	*len = ftell(f);
 	fseek(f, 0, SEEK_SET);
-	/* FIXME: error checking */
 	file_contents = malloc(*len + 1);
+	if (!file_contents) return NULL;
 	fread(file_contents, 1, *len, f);
 	file_contents[*len] = '\0';
 	fclose(f);