URI: 
       tAllow longer strings in commands; fix text rendering - 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 85b5c97bb7c4e1b85f9c322d1beccd1911f0bf09
   DIR parent 017e677d873e000aea1af57bb49d4be48ff89445
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Sat,  6 Jun 2020 19:17:19 +0200
       
       Allow longer strings in commands; fix text rendering
       
       Diffstat:
         M LICENSE                             |       2 +-
         M button.c                            |       6 +++---
         M button.h                            |       2 +-
         M grid.c                              |      22 +++++++++++-----------
         M grid.h                              |       2 +-
         M ltk.c                               |      90 +++++++++++++++++++++-----------
         M ltk.h                               |       4 ----
         M test.gui                            |       4 ++--
         M text_line.c                         |      26 ++++++++++++++++----------
         M text_line.h                         |       2 ++
         M theme.ini                           |       2 +-
       
       11 files changed, 98 insertions(+), 64 deletions(-)
       ---
   DIR diff --git a/LICENSE b/LICENSE
       t@@ -1,4 +1,4 @@
       -See khash.h, ini.*, and strtonum.c for third-party licenses.
       +See khash.h, ini.*, stb_truetype.*, and strtonum.c for third-party licenses.
        
        
        MIT/X Consortium License
   DIR diff --git a/button.c b/button.c
       t@@ -43,7 +43,7 @@ static void ltk_button_destroy(ltk_button *button, int shallow);
        
        static void ltk_grid_cmd_create(
            ltk_window *window,
       -    char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH],
       +    char **tokens,
            size_t num_tokens);
        
        void
       t@@ -187,7 +187,7 @@ ltk_button_destroy(ltk_button *button, int shallow) {
        static void
        ltk_button_cmd_create(
            ltk_window *window,
       -    char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH],
       +    char **tokens,
            size_t num_tokens) {
                ltk_button *button;
                if (num_tokens != 4) {
       t@@ -204,7 +204,7 @@ ltk_button_cmd_create(
        void
        ltk_button_cmd(
            ltk_window *window,
       -    char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH],
       +    char **tokens,
            size_t num_tokens) {
                if (num_tokens < 3) {
                        (void)fprintf(stderr, "button: Invalid number of arguments.\n");
   DIR diff --git a/button.h b/button.h
       t@@ -57,7 +57,7 @@ void ltk_button_ini_handler(ltk_window *window, const char *prop, const char *va
        
        void ltk_button_cmd(
            ltk_window *window,
       -    char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH],
       +    char **tokens,
            size_t num_tokens);
        
        #endif
   DIR diff --git a/grid.c b/grid.c
       t@@ -54,23 +54,23 @@ static void ltk_grid_motion_notify(ltk_grid *grid, XEvent event);
        
        static void ltk_grid_cmd_add(
            ltk_window *window,
       -    char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH],
       +    char **tokens,
            size_t num_tokens);
        static void ltk_grid_cmd_ungrid(
            ltk_window *window,
       -    char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH],
       +    char **tokens,
            size_t num_tokens);
        static void ltk_grid_cmd_create(
            ltk_window *window,
       -    char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH],
       +    char **tokens,
            size_t num_tokens);
        static void ltk_grid_cmd_set_row_weight(
            ltk_window *window,
       -    char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH],
       +    char **tokens,
            size_t num_tokens);
        static void ltk_grid_cmd_set_column_weight(
            ltk_window *window,
       -    char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH],
       +    char **tokens,
            size_t num_tokens);
        
        static void
       t@@ -364,7 +364,7 @@ ltk_grid_motion_notify(ltk_grid *grid, XEvent event) {
        static void
        ltk_grid_cmd_add(
            ltk_window *window,
       -    char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH],
       +    char **tokens,
            size_t num_tokens) {
                ltk_grid *grid;
                ltk_widget *widget;
       t@@ -410,7 +410,7 @@ ltk_grid_cmd_add(
        static void
        ltk_grid_cmd_ungrid(
            ltk_window *window,
       -    char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH],
       +    char **tokens,
            size_t num_tokens) {
                ltk_grid *grid;
                ltk_widget *widget;
       t@@ -428,7 +428,7 @@ ltk_grid_cmd_ungrid(
        static void
        ltk_grid_cmd_create(
            ltk_window *window,
       -    char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH],
       +    char **tokens,
            size_t num_tokens) {
                int rows, columns;
                ltk_grid *grid;
       t@@ -457,7 +457,7 @@ ltk_grid_cmd_create(
        static void
        ltk_grid_cmd_set_row_weight(
            ltk_window *window,
       -    char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH],
       +    char **tokens,
            size_t num_tokens) {
                ltk_grid *grid;
                int row, weight;
       t@@ -485,7 +485,7 @@ ltk_grid_cmd_set_row_weight(
        static void
        ltk_grid_cmd_set_column_weight(
            ltk_window *window,
       -    char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH],
       +    char **tokens,
            size_t num_tokens) {
                ltk_grid *grid;
                int column, weight;
       t@@ -513,7 +513,7 @@ ltk_grid_cmd_set_column_weight(
        void
        ltk_grid_cmd(
            ltk_window *window,
       -    char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH],
       +    char **tokens,
            size_t num_tokens) {
                if (num_tokens < 3) {
                        (void)fprintf(stderr, "grid: Invalid number of arguments.\n");
   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[MAX_TOKENS][MAX_TOKEN_LENGTH], size_t num_tokens);
       +void ltk_grid_cmd(ltk_window *window, char **tokens, size_t num_tokens);
        
        #endif
   DIR diff --git a/ltk.c b/ltk.c
       t@@ -46,6 +46,12 @@ static void ltk_load_theme(ltk_window *window, const char *path);
        static void ltk_destroy_theme(ltk_theme *theme);
        
        static int running = 1;
       +static char *cmd_input = NULL;
       +static char **tokens = NULL;
       +static size_t cmd_bufsize = 0;
       +static size_t tokens_bufsize = 0;
       +static size_t cmd_len = 0;
       +static size_t tokens_len = 0;
        
        char *
        ltk_read_file(const char *path, unsigned long *len) {
       t@@ -128,7 +134,7 @@ ltk_queue_event(ltk_window *window, const char *id, const char *name) {
        static void
        ltk_set_root_widget_cmd(
            ltk_window *window,
       -    char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH],
       +    char **tokens,
            int num_tokens) {
                ltk_widget *widget;
                if (num_tokens != 2) {
       t@@ -149,57 +155,82 @@ ltk_set_root_widget_cmd(
        
        /* copied from suckless ii */
        static int
       -read_line(int fd, char *buf, size_t bufsiz)
       -{
       -        size_t i = 0;
       +read_cmdline(int fd) {
                char c = '\0';
       +        char *tmp = NULL;
       +        cmd_len = 0;
                do {
       +                if (cmd_len >= cmd_bufsize) {
       +                        cmd_bufsize = !cmd_bufsize ? 1 : cmd_bufsize;
       +                        tmp = realloc(cmd_input, cmd_bufsize * 2 * sizeof(char));
       +                        if (!tmp) ltk_fatal("Out of memory while reading command.\n");
       +                        cmd_input = tmp;
       +                        cmd_bufsize *= 2;
       +                }
                        if (read(fd, &c, sizeof(char)) != sizeof(char))
                                return -1;
       -                buf[i++] = c;
       -        } while (c != '\n' && i < bufsiz);
       -        buf[i - 1] = '\0'; /* eliminates '\n' */
       +                cmd_input[cmd_len++] = c;
       +        } while (c != '\n');
       +        cmd_input[cmd_len - 1] = '\0'; /* eliminates '\n' */
                return 0;
        }
        
       -static size_t
       -tokenize(char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], char *buf) {
       +static int
       +tokenize(void) {
                char *c;
       -        if (!buf || buf[0] == '\0') return 0;
       -        for (c = buf; *c == ' '; c++)
       +        if (!cmd_input || cmd_input[0] == '\0') return 0;
       +        for (c = cmd_input; *c == ' '; c++)
                        ;
       +        tokens_len = 0;
                size_t cur_tok = 0;
                size_t cur_tok_len = 0;
       +        size_t cur_tok_bufsize = 0;
       +        int in_str = 0;
       +        char **tmp;
       +        char *tmp2;
                while (*c != '\0') {
       -                if (cur_tok >= MAX_TOKENS) {
       -                        return 0;
       -                } else if (*c == ' ') {
       +                if (cur_tok >= tokens_bufsize) {
       +                        tokens_bufsize = !tokens_bufsize ? 1 : tokens_bufsize;
       +                        tmp = realloc(tokens, tokens_bufsize * 2 * sizeof(char *));
       +                        if (!tmp) ltk_fatal("Out of memory while parsing command.\n");
       +                        tokens = tmp;
       +                        tokens_bufsize *= 2;
       +                }
       +                if (cur_tok_len + 1 >= cur_tok_bufsize) {
       +                        cur_tok_bufsize = !cur_tok_bufsize ? 1 : cur_tok_bufsize;
       +                        if (cur_tok_len == 0)
       +                                tokens[cur_tok] = NULL; /* so realloc works the first time */
       +                        tmp2 = realloc(tokens[cur_tok], cur_tok_bufsize * 2 * sizeof(char));
       +                        if (!tmp2) ltk_fatal("Out of memory while parsing command.\n");
       +                        tokens[cur_tok] = tmp2;
       +                        cur_tok_bufsize *= 2;
       +                }
       +                if (*c == '"') {
       +                        in_str = !in_str;
       +                } else if (*c == ' ' && !in_str) {
                                tokens[cur_tok][cur_tok_len] = '\0';
                                cur_tok++;
                                cur_tok_len = 0;
       -                } else if (cur_tok_len >= MAX_TOKEN_LENGTH - 1) {
       -                        return 0;
       +                        cur_tok_bufsize = 0;
                        } else {
                                tokens[cur_tok][cur_tok_len++] = *c;
                        }
                        c++;
                }
                tokens[cur_tok][cur_tok_len] = '\0';
       -        return cur_tok + 1;
       +        tokens_len = cur_tok + 1;
        }
        
        static void
       -proc_cmds(ltk_window *window, char *buf) {
       -        /* FIXME: resizable */
       -        char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH];
       -        int num_tokens;
       -        if (!(num_tokens = tokenize(tokens, buf))) return;
       +proc_cmds(ltk_window *window) {
       +        if (!tokenize()) return;
       +        if (!tokens_len) return;
                if (strcmp(tokens[0], "grid") == 0) {
       -                ltk_grid_cmd(window, tokens, num_tokens);
       +                ltk_grid_cmd(window, tokens, tokens_len);
                } else if (strcmp(tokens[0], "button") == 0) {
       -                ltk_button_cmd(window, tokens, num_tokens);
       +                ltk_button_cmd(window, tokens, tokens_len);
                } else if (strcmp(tokens[0], "set-root-widget") == 0) {
       -                ltk_set_root_widget_cmd(window, tokens, num_tokens);
       +                ltk_set_root_widget_cmd(window, tokens, tokens_len);
                } else {
                        (void)fprintf(stderr, "Invalid command.\n");
                }
       t@@ -213,7 +244,6 @@ ltk_mainloop(ltk_window *window) {
                struct timeval tv;
                fd_set rfds;
                int fd_in = fileno(stdin);
       -        char buf[MAX_CMD_LENGTH];
                int retval;
                tick.tv_sec = 0;
                tick.tv_nsec = 10000;
       t@@ -228,7 +258,9 @@ ltk_mainloop(ltk_window *window) {
                                XNextEvent(window->dpy, &event);
                                ltk_handle_event(window, event);
                        }
       -                if (window->dirty_rect.w != 0 && window->dirty_rect.h != 0) {
       +                if (retval > 0 && !read_cmdline(fd_in)) {
       +                        proc_cmds(window);
       +                } else if (window->dirty_rect.w != 0 && window->dirty_rect.h != 0) {
                                ltk_redraw_window(window);
                                window->dirty_rect.w = 0;
                                window->dirty_rect.h = 0;
       t@@ -244,9 +276,7 @@ ltk_mainloop(ltk_window *window) {
                                        free(last);
                                } while (cur);
                                window->first_event = window->last_event = NULL;
       -                } else if (retval > 0 && !read_line(fd_in, buf, sizeof(buf))) {
       -                        proc_cmds(window, buf);
       -                }
       +                } 
                        /* yes, this should be improved */
                        nanosleep(&tick, NULL);
        
   DIR diff --git a/ltk.h b/ltk.h
       t@@ -24,10 +24,6 @@
        #ifndef _LTK_H_
        #define _LTK_H_
        
       -#define MAX_TOKENS 20
       -#define MAX_TOKEN_LENGTH 20
       -#define MAX_CMD_LENGTH 400
       -
        /* Requires the following includes: <X11/Xlib.h>, <X11/Xutil.h>, "drw.h" */
        
        typedef struct {
   DIR diff --git a/test.gui b/test.gui
       t@@ -4,7 +4,7 @@ grid grd1 set-row-weight 1 1
        grid grd1 set-column-weight 0 1
        grid grd1 set-column-weight 1 1
        set-root-widget grd1
       -button btn1 create Button1
       +button btn1 create "I'm a button!"
        grid grd1 add btn1 0 0 1 1 0
       -button btn2 create Button2
       +button btn2 create "I'm also a button!"
        grid grd1 add btn2 1 0 1 2 3
   DIR diff --git a/text_line.c b/text_line.c
       t@@ -56,15 +56,17 @@ ltk_create_ximage(Display *dpy, int w, int h, int depth, XColor bg) {
        
        /* based on http://codemadness.org/git/dwm-font/file/drw.c.html#l315 */
        static void
       -ltk_text_line_draw_glyph(ltk_glyph *glyph, XImage *img, XColor fg) {
       +ltk_text_line_draw_glyph(ltk_glyph *glyph, int xoff, int yoff, XImage *img, XColor fg) {
       +        int x = glyph->x + xoff;
       +        int y = glyph->y + yoff;
                double a;
                int b;
                for (int i = 0; i < glyph->info->h; i++) {
                        for (int j = 0; j < glyph->info->w; j++) {
       -                        if (glyph->y + i >= img->height || glyph->x + j >= img->width ||
       -                            glyph->y + i < 0 || glyph->x + i < 0)
       +                        if (y + i >= img->height || x + j >= img->width ||
       +                            y + i < 0 || x + i < 0)
                                        continue;
       -                        b = (glyph->y + i) * img->bytes_per_line + (glyph->x + j) * 4;
       +                        b = (y + i) * img->bytes_per_line + (x + j) * 4;
                                a = glyph->info->alphamap[i * glyph->info->w + j] / 255.0;
                                img->data[b] = (fg.blue * a + (1 - a) * (uint16_t)img->data[b] * 257) / 257;
                                img->data[b + 1] = (fg.green * a + (1 - a) * (uint16_t)img->data[b + 1] * 257) / 257;
       t@@ -91,7 +93,7 @@ ltk_text_line_render(
                /* FIXME: pass old image; if it has same dimensions, just clear it */
                XImage *img = ltk_create_ximage(dpy, tl->w, tl->h, depth, bg);
                for (int i = 0; i < tl->glyph_len; i++) {
       -                ltk_text_line_draw_glyph(&tl->glyphs[i], img, fg);
       +                ltk_text_line_draw_glyph(&tl->glyphs[i], -tl->x_min, -tl->y_min, img, fg);
                }
                return img;
        }
       t@@ -103,7 +105,7 @@ ltk_text_line_create_glyphs(ltk_window *window, struct ltk_text_line *tl) {
                int index;
                char *file;
                size_t inc = 0;
       -        int x = 0, y, x1, x2, y1, y2, kern_advance, ax;
       +        int x = 0, y, kern_advance, ax;
                int x1_abs, x2_abs;
                float scale;
                int ascent, descent, line_gap;
       t@@ -145,16 +147,17 @@ ltk_text_line_create_glyphs(ltk_window *window, struct ltk_text_line *tl) {
                                ascent *= scale;
                                descent *= scale;
                        }
       -                stbtt_GetGlyphBitmapBox(&font->info, gid, scale, scale, &x1, &y1, &x2, &y2);
       -                y = ascent + y1;
       -                x1_abs = x + x1;
       +                ginfo = ltk_get_glyph_info(font, gid, scale, glyph_cache);
       +                y = ascent + ginfo->yoff;
       +                x1_abs = x + ginfo->xoff;
       +
                        tl->glyphs[i].x = x1_abs;
                        tl->glyphs[i].y = y;
       +
                        stbtt_GetGlyphHMetrics(&font->info, gid, &ax, 0);
                        x += (int) (ax * scale);
                        x2_abs = x;
        
       -                ginfo = ltk_get_glyph_info(font, gid, scale, glyph_cache);
                        tl->glyphs[i].info = ginfo;
                        if (x1_abs < x_min) x_min = x1_abs;
                        if (y < y_min) y_min = y;
       t@@ -168,6 +171,9 @@ ltk_text_line_create_glyphs(ltk_window *window, struct ltk_text_line *tl) {
                        }
                        c1 = c2;
                };
       +        /* for drawing the glyphs at the right position on the image */
       +        tl->x_min = x_min;
       +        tl->y_min = y_min;
                tl->w = x_max - x_min;
                tl->h = y_max - y_min;
        }
   DIR diff --git a/text_line.h b/text_line.h
       t@@ -38,6 +38,8 @@ struct ltk_text_line {
                uint16_t font_size;
                int w;
                int h;
       +        int x_min;
       +        int y_min;
        };
        
        XImage *ltk_text_line_render(struct ltk_text_line *tl,
   DIR diff --git a/theme.ini b/theme.ini
       t@@ -3,7 +3,7 @@ font_size = 15
        border_width = 0
        bg = #000000
        fg = #FFFFFF
       -font = Awami Nastaliq
       +font = Liberation Mono
        
        [button]
        border_width = 2