URI: 
       tImprove error reporting - ltk - Socket-based GUI for X11 (WIP)
  HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 236104387a38581c2d7e0b50dd208d6f2db4e312
   DIR parent ffdfc8d4bc5e24eab86b857e637d60c7521bf0b0
  HTML 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(-)
       ---
   DIR diff --git a/button.c b/button.c
       t@@ -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,
                    &ltk_button_draw, &ltk_button_change_state, &ltk_button_destroy, 1, LTK_BUTTON);
                button->widget.mouse_release = &ltk_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;
       t@@ -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);
       t@@ -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;
        }
   DIR diff --git a/button.h b/button.h
       t@@ -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_ */
   DIR diff --git a/color.c b/color.c
       t@@ -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);
   DIR diff --git a/draw.c b/draw.c
       t@@ -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) {
       t@@ -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,
                    &ltk_draw_draw, NULL, &ltk_draw_destroy, 1, LTK_DRAW);
       t@@ -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);
       t@@ -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);
       t@@ -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;
       t@@ -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;
        }
   DIR diff --git a/draw.h b/draw.h
       t@@ -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_ */
   DIR diff --git a/grid.c b/grid.c
       t@@ -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) {
       t@@ -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;
       t@@ -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++) {
       t@@ -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
       t@@ -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... */
        }
   DIR diff --git a/grid.h b/grid.h
       t@@ -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
   DIR diff --git a/ltk.h b/ltk.h
       t@@ -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);
       t@@ -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
   DIR diff --git a/ltkd.c b/ltkd.c
       t@@ -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);
       t@@ -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++) {
       t@@ -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;
       t@@ -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);
       t@@ -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;
       t@@ -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);
       t@@ -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();
       t@@ -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));
       t@@ -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;
       t@@ -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
       t@@ -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;
       t@@ -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);
       t@@ -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);
        
       t@@ -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);
        }
        
       t@@ -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);
                }
       t@@ -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;
       t@@ -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;
       t@@ -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;
       t@@ -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;
        }
       t@@ -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]);
   DIR diff --git a/text_pango.c b/text_pango.c
       t@@ -8,6 +8,7 @@
        #include <pango/pangoxft.h>
        #include "util.h"
        #include "text.h"
       +#include "ltk.h"
        
        struct LtkTextLine {
                char *text;
       t@@ -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;
   DIR diff --git a/text_stb.c b/text_stb.c
       t@@ -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;
        }
   DIR diff --git a/util.c b/util.c
       t@@ -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);