URI: 
       tSwitch from gap buffers to arrays - ltkx - GUI toolkit for X11 (WIP)
  HTML git clone git://lumidify.org/ltkx.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit e6a8ecf9116eb9cec8ed1da33d69c91988f55e86
   DIR parent 1242d3b3fcdb252a60c422b8eb15396e641fd656
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Thu, 14 May 2020 11:37:48 +0200
       
       Switch from gap buffers to arrays
       
       Diffstat:
         M Makefile                            |       2 +-
         M array.h                             |      39 +++++++++++++++++++++++++++++++
         M text_buffer.c                       |     187 +++++++++++--------------------
         M text_buffer.h                       |      26 +++++++++++++++-----------
         M text_edit.c                         |      20 ++++++++++++++++++--
         M text_edit.h                         |       6 ++++++
       
       6 files changed, 143 insertions(+), 137 deletions(-)
       ---
   DIR diff --git a/Makefile b/Makefile
       t@@ -1,7 +1,7 @@
        LIBS = -lm `pkg-config --libs x11 harfbuzz fontconfig fribidi`
        STD = -std=c99
        CFLAGS = -g -w -fcommon -Wall -Werror -Wextra `pkg-config --cflags x11 harfbuzz fontconfig fribidi` -pedantic
       -OBJ = stb_truetype.o text-common.o text_buffer.o text-hb.o ltk.o ini.o grid.o button.o test1.o
       +OBJ = stb_truetype.o text_edit.o text-common.o text_buffer.o text-hb.o ltk.o ini.o grid.o button.o test1.o
        
        test1: $(OBJ)
                gcc $(STD) -o $@ $(OBJ) $(LIBS)
   DIR diff --git a/array.h b/array.h
       t@@ -33,7 +33,13 @@ struct ltk_array_##name {                                                                \
                size_t buf_size;                                                                \
                size_t len;                                                                        \
        };                                                                                        \
       +                                                                                        \
        struct ltk_array_##name *ltk_array_create_##name(size_t initial_len);                        \
       +type ltk_array_pop_##name(struct ltk_array_##name *ar);                                        \
       +void ltk_array_prepare_gap_##name(                                                        \
       +    struct ltk_array_##name *ar, size_t index, size_t len);                                \
       +void ltk_array_insert_##name(struct ltk_array_##name *ar, size_t index,                        \
       +    type *elem, size_t len);                                                                \
        void ltk_array_resize_##name(struct ltk_array_##name *ar, size_t size);                        \
        void ltk_array_destroy_##name(struct ltk_array_##name *ar);                                \
        void ltk_array_clear_##name(struct ltk_array_##name *ar);                                \
       t@@ -57,6 +63,39 @@ error:                                                                                        \
                exit(1);                                                                        \
        }                                                                                        \
                                                                                                \
       +type                                                                                        \
       +ltk_array_pop_##name(struct ltk_array_##name *ar) {                                        \
       +        if (ar->len == 0) {                                                                \
       +                (void)fprintf(stderr, "Array empty; cannot pop.\n");                        \
       +                exit(1);                                                                \
       +        }                                                                                \
       +        ar->len--;                                                                        \
       +        return ar->buf[ar->len];                                                        \
       +}                                                                                        \
       +                                                                                        \
       +void                                                                                        \
       +ltk_array_prepare_gap_##name(struct ltk_array_##name *ar, size_t index, size_t len) {        \
       +        if (index > ar->len) {                                                                \
       +                (void)fprintf(stderr, "Array index out of bounds\n");                        \
       +                exit(1);                                                                \
       +        }                                                                                \
       +        ltk_array_resize_##name(ar, ar->len + len);                                        \
       +        ar->len += len;                                                                        \
       +        if (ar->len == index)                                                                \
       +                return;                                                                        \
       +        memmove(ar->buf + index + len, ar->buf + index, ar->len - index);                \
       +}                                                                                        \
       +                                                                                        \
       +void                                                                                        \
       +ltk_array_insert_##name(struct ltk_array_##name *ar, size_t index,                        \
       +    type *elem, size_t len) {                                                                \
       +        ltk_array_prepare_gap_##name(ar, index, len);                                        \
       +        ltk_array_resize_##name(ar, ar->len + len);                                        \
       +        for (int i = 0; i < len; i++) {                                                        \
       +                ar->buf[index + i] = elem[i];                                                \
       +        }                                                                                \
       +}                                                                                        \
       +                                                                                        \
        void                                                                                        \
        ltk_array_append_##name(struct ltk_array_##name *ar, type elem) {                        \
                if (ar->len == ar->buf_size)                                                        \
   DIR diff --git a/text_buffer.c b/text_buffer.c
       t@@ -34,25 +34,21 @@
        #include <harfbuzz/hb.h>
        #include <harfbuzz/hb-ot.h>
        #include "text-common.h"
       -#include "gap_buffer.h"
        #include "array.h"
       -#include "stack.h"
        #include "text_buffer.h"
        #include "ltk.h"
        
        extern Ltk *ltk_global;
        
       -LTK_GAP_BUFFER_INIT_IMPL(uint32, uint32_t)
       -LTK_GAP_BUFFER_INIT_IMPL(script, hb_script_t)
       -LTK_GAP_BUFFER_INIT_IMPL(int, int)
       +LTK_ARRAY_INIT_IMPL(uint32, uint32_t)
       +LTK_ARRAY_INIT_IMPL(script, hb_script_t)
        LTK_ARRAY_INIT_IMPL(level, FriBidiLevel)
        LTK_ARRAY_INIT_IMPL(int, int)
       -LTK_STACK_INIT_IMPL(script, int, hb_script_t, pair_index, script)
        
        /* based on http://codemadness.org/git/dwm-font/file/drw.c.html#l315 */
        /* FIXME: rename this once everything is cleaned up (currently conflicts with the
           old render function */
       -void
       +XImage *
        ltk_render_text_line_new(
                struct ltk_text_line *tl,
                int max_width,
       t@@ -63,6 +59,7 @@ ltk_render_text_line_new(
                XColor fg,
                XColor bg)
        {
       +        XImage *img;
                int cur_x = 0, cur_y = 0;
                int par_is_rtl = FRIBIDI_IS_RTL(tl->dir);
                ltk_array_clear_int(tl->wrap_indeces);
       t@@ -91,21 +88,20 @@ ltk_render_text_line_new(
                        }
                } while (cur = cur->next);
        
       -        if (tl->img) XDestroyImage(tl->img);
                XWindowAttributes attrs;
                XGetWindowAttributes(dpy, window, &attrs);
                int depth = attrs.depth;
       -        tl->img = XCreateImage(dpy, CopyFromParent, depth, ZPixmap, 0, NULL, max_width, tl->h * tl->wrap_indeces->len, 32, 0);
       -        tl->img->data = calloc(tl->img->bytes_per_line, tl->img->height);
       -        XInitImage(tl->img);
       +        img = XCreateImage(dpy, CopyFromParent, depth, ZPixmap, 0, NULL, max_width, tl->h * tl->wrap_indeces->len, 32, 0);
       +        img->data = calloc(img->bytes_per_line, img->height);
       +        XInitImage(img);
        
                int b;
                for (int i = 0; i < tl->h * tl->wrap_indeces->len; i++) {
       -                b = tl->img->bytes_per_line * i;
       +                b = img->bytes_per_line * i;
                        for (int j = 0; j < max_width; j++) {
       -                        tl->img->data[b++] = bg.blue / 257;
       -                        tl->img->data[b++] = bg.green / 257;
       -                        tl->img->data[b++] = bg.red / 257;
       +                        img->data[b++] = bg.blue / 257;
       +                        img->data[b++] = bg.green / 257;
       +                        img->data[b++] = bg.red / 257;
                                b++;
                        }
                }
       t@@ -132,11 +128,11 @@ ltk_render_text_line_new(
                                y = glyph->y_abs + tl->h * cur_line;
                                for (int i = 0; i < glyph->info->h; i++) {
                                        for (int j = 0; j < glyph->info->w; j++) {
       -                                        b = (y + i) * tl->img->bytes_per_line + (x + j) * 4;
       +                                        b = (y + i) * img->bytes_per_line + (x + j) * 4;
                                                a = glyph->info->alphamap[i * glyph->info->w + j] / 255.0;
       -                                        tl->img->data[b] = (fg.blue * a + (1 - a) * (uint16_t)tl->img->data[b] * 257) / 257;
       -                                        tl->img->data[b + 1] = (fg.green * a + (1 - a) * (uint16_t)tl->img->data[b + 1] * 257) / 257;
       -                                        tl->img->data[b + 2] = (fg.red * a + (1 - a) * (uint16_t)tl->img->data[b + 2] * 257) / 257;
       +                                        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;
       +                                        img->data[b + 2] = (fg.red * a + (1 - a) * (uint16_t)img->data[b + 2] * 257) / 257;
                                        }
                                }
                        }
       t@@ -144,31 +140,6 @@ ltk_render_text_line_new(
                }
        }
        
       -/*
       -NOTE: The following notes are outdated.
       -Notes: When inserting a character, check what the direction of the surrounding
       -script is - if it is the same (or a weak direction), then just insert the char
       -without re-doing the bidi algorithm. Only redo the whole line/paragraph is the
       -dir of the inserted character is different. Assume the text is LTR until a strong
       -character is inserted, i.e. the line should keep a flag if it already has a strong
       -dir set.
       -When reordering, the cursor needs to be kept in the right place:
       -a) The logical to visual mapping from FriBidi needs to be used to find the position
       -of the cursor in the text that gets passed to Harfbuzz.
       -b) The cluster values from Harfbuzz need to be used to figure out where the cursor
       -is on the screen.
       -Harfbuzz also should be given some context - maybe even completely reshape the
       -text until the last and next space? That would only be a problem with weird very
       -long words (mainly nonsense). At least maybe take up to five or so chars on each
       -side and pass even more with the optional context-passing feature for hb_shape.
       -Question: Does both the logical and visual text need to be stored? The logical text
       -is technically the master copy, but the visual text is what gets passed to harfbuzz.
       --> Actually, since the visual text has to be split into script runs anyways, maybe
       -just append text to that (and also add it to the master buffer simultaneously). As
       -soon as a different script is inserted, everything has to be redone anyways. Also,
       -when reshaping with context, only the text in the current run has to be passed at all.
       -*/
       -
        /* Begin stuff stolen from raqm */
        
        /* Special paired characters for script detection */
       t@@ -211,7 +182,7 @@ get_pair_index (const FriBidiChar ch) {
                return -1;
        }
        
       -#define STACK_IS_EMPTY(stack) ((stack)->size <= 0)
       +#define STACK_IS_EMPTY(stack) ((stack)->len <= 0)
        #define IS_OPEN(pair_index) (((pair_index) & 1) == 0)
        
        /* Resolve the script for each character in the input string, if the character
       t@@ -225,50 +196,52 @@ ltk_resolve_scripts(struct ltk_text_line *tl) {
                int last_set_index = -1;
                hb_script_t last_script = HB_SCRIPT_INVALID;
                hb_script_t cur_script;
       -        struct ltk_stack_script *stack = NULL;
       +        struct ltk_array_script *script_stack = NULL;
       +        struct ltk_array_int *pair_indeces = NULL;
                hb_unicode_funcs_t* unicode_funcs = hb_unicode_funcs_get_default();
        
       -        stack = ltk_stack_create_script(tl->len);
       +        script_stack = ltk_array_create_script(4);
       +        pair_indeces = ltk_array_create_int(4);
        
                for (int i = 0; i < (int) tl->len; i++) {
       -                cur_script = ltk_gap_buffer_get_script(tl->scripts, i);
       +                cur_script = tl->scripts->buf[i];
                        if (cur_script == HB_SCRIPT_COMMON && last_script_index != -1) {
       -                        int pair_index = get_pair_index(ltk_gap_buffer_get_uint32(tl->log_buf, i));
       +                        int pair_index = get_pair_index(tl->log_buf->buf[i]);
                                if (pair_index >= 0) {
                                        if (IS_OPEN (pair_index)) {
                                                /* is a paired character */
       -                                        ltk_gap_buffer_set_script(tl->scripts, i, last_script);
       +                                        tl->scripts->buf[i] = last_script;
                                                last_set_index = i;
       -                                        ltk_stack_push_script(stack, cur_script, pair_index);
       +                                        ltk_array_append_script(script_stack, cur_script);
       +                                        ltk_array_append_int(pair_indeces, pair_index);
                                        } else {
                                                /* is a close paired character */
                                                /* find matching opening (by getting the last
                                                 * even index for current odd index) */
       -                                        while (!STACK_IS_EMPTY(stack) &&
       -                                            stack->pair_index[stack->size] != (pair_index & ~1)) {
       -                                                ltk_stack_pop_script(stack);
       +                                        while (!STACK_IS_EMPTY(pair_indeces) &&
       +                                            pair_indeces->buf[pair_indeces->len-1] != (pair_index & ~1)) {
       +                                                ltk_array_pop_script(script_stack);
       +                                                ltk_array_pop_int(pair_indeces);
                                                }
       -                                        if (!STACK_IS_EMPTY(stack)) {
       -                                                ltk_gap_buffer_set_script(
       -                                                    tl->scripts, i,
       -                                                    ltk_stack_top2_script(stack, HB_SCRIPT_INVALID));
       +                                        if (!STACK_IS_EMPTY(script_stack)) {
       +                                                tl->scripts->buf[i] = script_stack->buf[script_stack->len-1];
                                                        last_script = cur_script;
                                                        last_set_index = i;
                                                } else {
       -                                                ltk_gap_buffer_set_script(tl->scripts, i, last_script);
       +                                                tl->scripts->buf[i] = last_script;
                                                        last_set_index = i;
                                                }
                                        }
                                } else {
       -                                ltk_gap_buffer_set_script(tl->scripts, i, last_script);
       +                                tl->scripts->buf[i] = last_script;
                                        last_set_index = i;
                                }
                        } else if (cur_script == HB_SCRIPT_INHERITED && last_script_index != -1) {
       -                        ltk_gap_buffer_set_script(tl->scripts, i, last_script);
       +                        tl->scripts->buf[i] = last_script;
                                last_set_index = i;
                        } else {
                                for (int j = last_set_index + 1; j < i; ++j)
       -                                ltk_gap_buffer_set_script(tl->scripts, j, cur_script);
       +                                tl->scripts->buf[j] = cur_script;
                                last_script = cur_script;
                                last_script_index = i;
                                last_set_index = i;
       t@@ -281,14 +254,14 @@ ltk_resolve_scripts(struct ltk_text_line *tl) {
                */
                hb_script_t scr;
                for (int i = tl->len - 2; i >= 0;  --i) {
       -                scr = ltk_gap_buffer_get_script(tl->scripts, i);
       +                scr = tl->scripts->buf[i];
                        if (scr == HB_SCRIPT_INHERITED || scr == HB_SCRIPT_COMMON) {
       -                        ltk_gap_buffer_set_script(tl->scripts, i,
       -                            ltk_gap_buffer_get_script(tl->scripts, i + 1));
       +                        tl->scripts->buf[i] = tl->scripts->buf[i + 1];
                        }
                }
        
       -        ltk_stack_destroy_script(stack);
       +        ltk_array_destroy_script(script_stack);
       +        ltk_array_destroy_int(pair_indeces);
        
                return 1;
        }
       t@@ -327,12 +300,12 @@ ltk_text_line_itemize(struct ltk_text_line *tl) {
                while (start_index < tl->len) {
                        end_index = start_index;
                        cur_level = last_level = tl->bidi_levels->buf[start_index];
       -                cur_script = last_script = ltk_gap_buffer_get_script(tl->scripts, start_index);
       +                cur_script = last_script = tl->scripts->buf[start_index];
                        while (end_index < tl->len &&
                            cur_level == last_level && cur_script == last_script) {
                                end_index++;
                                cur_level = tl->bidi_levels->buf[end_index];
       -                        cur_script = ltk_gap_buffer_get_script(tl->scripts, end_index);
       +                        cur_script = tl->scripts->buf[end_index];
                        }
                        dir = HB_DIRECTION_LTR;
                        if (FRIBIDI_LEVEL_IS_RTL(last_level))
       t@@ -466,7 +439,7 @@ ltk_text_line_shape(LtkTextManager *tm, struct ltk_text_line *tl) {
                        FcDefaultSubstitute(pat);
                        FcCharSet *cs = FcCharSetCreate();
                        for (int i = run->start_index; i < run->start_index + run->len; i++) {
       -                        FcCharSetAddChar(cs, ltk_gap_buffer_get_uint32(tl->vis_buf, i));
       +                        FcCharSetAddChar(cs, tl->vis_buf->buf[i]);
                        }
                        FcPatternAddCharSet(pat, FC_CHARSET, cs);
                        match = FcFontMatch(NULL, pat, &result);
       t@@ -546,21 +519,10 @@ ltk_text_line_destroy_runs(struct ltk_text_run *runs) {
        
        static void
        ltk_text_line_recalculate(LtkTextManager *tm, struct ltk_text_line *tl) {
       -        ltk_gap_buffer_clear_uint32(tl->vis_buf);
       -        ltk_gap_buffer_clear_int(tl->log2vis);
       -        ltk_gap_buffer_clear_int(tl->vis2log);
       -        /* yeah, this is hacky */
       -        tl->vis_buf->len = tl->vis_buf->buf_size;
       -        tl->log2vis->len = tl->log2vis->buf_size;
       -        tl->vis2log->len = tl->vis2log->buf_size;
       -        size_t gap_pos = tl->log_buf->gap_left;
       -        size_t gap_end = tl->log_buf->buf_size - tl->log_buf->gap_size;
       -        ltk_gap_buffer_move_gap_uint32(tl->log_buf, gap_end);
                fribidi_log2vis(
       -            tl->log_buf, tl->log_buf->buf_size - tl->log_buf->gap_size,
       +            tl->log_buf, tl->log_buf->len,
                    &tl->dir, tl->vis_buf, tl->log2vis, tl->vis2log, tl->bidi_levels
                );
       -        ltk_gap_buffer_move_gap_uint32(tl->log_buf, gap_pos);
                struct ltk_text_run *old_runs = tl->first_run;
                ltk_text_line_itemize(tl);
                ltk_text_line_shape(tm, tl);
       t@@ -573,38 +535,17 @@ ltk_text_line_recalculate(LtkTextManager *tm, struct ltk_text_line *tl) {
        
        void
        ltk_text_line_insert_text(struct ltk_text_line *tl, uint32_t *text, size_t len) {
       -        /* check if any characters have a different script, only recalc then */
       -        /*
       -        hb_unicode_funcs_t *uf= hb_unicode_funcs_get_default();
       -        struct ltk_text_run *run = tl->cur_run;
       -        int recalc = 0;
       -        hb_script_t script;
       -        for (int i = 0; i < len; i++) {
       -                scr = hb_unicode_script(uf, text[i]);
       -                if (script != run->script &&
       -                    script != HB_SCRIPT_INHERITED &&
       -                    script != HB_SCRIPT_COMMON) {
       -                        recalc = 1;
       -                }
       -        }
       -        */
       -        ltk_gap_buffer_insert_uint32(tl->log_buf, text, 0, len);
       -        if (len > tl->scripts->gap_size)
       -                ltk_gap_buffer_resize_gap_script(tl->scripts, len + 8);
       +        ltk_array_insert_uint32(tl->log_buf, tl->cursor_pos, text, len);
       +        ltk_array_prepare_gap_script(tl->scripts, tl->cursor_pos, len);
                hb_unicode_funcs_t *ufuncs = hb_unicode_funcs_get_default();
       -        for (int i = 0; i < len; i++) {
       -                ltk_gap_buffer_insert_single_script(
       -                    tl->scripts, hb_unicode_script(ufuncs, text[i]));
       -        }
       -        if (len > tl->vis_buf->gap_size)
       -                ltk_gap_buffer_resize_gap_uint32(tl->vis_buf, len + 8);
       -        if (len > tl->log2vis->gap_size)
       -                ltk_gap_buffer_resize_gap_int(tl->log2vis, len + 8);
       -        if (len > tl->vis2log->gap_size)
       -                ltk_gap_buffer_resize_gap_int(tl->vis2log, len + 8);
       -        if (len + tl->bidi_levels->len > tl->bidi_levels->buf_size)
       -                ltk_array_resize_level(tl->bidi_levels, tl->bidi_levels->len + len + 8);
       +        for (int i = 0; i < len; i++)
       +                tl->scripts->buf[tl->cursor_pos + i] = hb_unicode_script(ufuncs, text[i]);
       +        ltk_array_resize_uint32(tl->vis_buf, tl->vis_buf->len + len);
       +        ltk_array_resize_int(tl->log2vis, tl->log2vis->len + len);
       +        ltk_array_resize_int(tl->vis2log, tl->vis2log->len + len);
       +        ltk_array_resize_level(tl->bidi_levels, tl->bidi_levels->len + len);
                tl->len += len;
       +        tl->cursor_pos += len;
                /* FIXME: Why am I passing tm? It's global anyways */
                ltk_text_line_recalculate(tl, ltk_global->tm);
        }
       t@@ -629,12 +570,12 @@ struct ltk_text_line *
        ltk_text_line_create(void) {
                struct ltk_text_line *line = malloc(sizeof(struct ltk_text_line));
                if (!line) goto error;
       -        line->log_buf = ltk_gap_buffer_create_uint32();
       -        line->scripts = ltk_gap_buffer_create_script();
       -        line->vis_buf = ltk_gap_buffer_create_uint32();
       -        line->log2vis = ltk_gap_buffer_create_int();
       -        line->vis2log = ltk_gap_buffer_create_int();
       -        line->bidi_levels = ltk_array_create_level(8);
       +        line->log_buf = ltk_array_create_uint32(4);
       +        line->vis_buf = ltk_array_create_uint32(4);
       +        line->log2vis = ltk_array_create_int(4);
       +        line->vis2log = ltk_array_create_int(4);
       +        line->scripts = ltk_array_create_script(4);
       +        line->bidi_levels = ltk_array_create_level(4);
                line->wrap_indeces = ltk_array_create_int(1);
                line->first_run = NULL;
                line->cur_run = NULL;
       t@@ -642,6 +583,7 @@ ltk_text_line_create(void) {
                line->height = 0;
                line->dir = FRIBIDI_TYPE_ON;
                line->len = 0;
       +        line->cursor_pos = 0;
        error:
                (void)fprintf(stderr, "No memory left while creating text line\n");
                exit(1);
       t@@ -661,14 +603,13 @@ ltk_text_buffer_create(void) {
        
        void
        ltk_text_line_destroy(struct ltk_text_line *tl) {
       -        ltk_gap_buffer_destroy_uint32(tl->log_buf);
       -        ltk_gap_buffer_destroy_uint32(tl->vis_buf);
       -        ltk_gap_buffer_destroy_script(tl->scripts);
       -        ltk_gap_buffer_destroy_int(tl->log2vis);
       -        ltk_gap_buffer_destroy_int(tl->vis2log);
       +        ltk_array_destroy_uint32(tl->log_buf);
       +        ltk_array_destroy_uint32(tl->vis_buf);
       +        ltk_array_destroy_script(tl->scripts);
       +        ltk_array_destroy_int(tl->log2vis);
       +        ltk_array_destroy_int(tl->vis2log);
                ltk_array_destroy_level(tl->bidi_levels);
                ltk_array_destroy_int(tl->wrap_indeces);
                ltk_text_line_destroy_runs(tl->first_run);
       -        if (tl->img) XDestroyImage(tl->img);
                free(tl);
        }
   DIR diff --git a/text_buffer.h b/text_buffer.h
       t@@ -33,12 +33,10 @@ Requires the following includes:
        
        /* Note: hb_script_t and FriBidiLevel are really just uint32_t's, but
           I'm not sure if I should rely on that, so they're separate here */
       -LTK_GAP_BUFFER_INIT_DECL(uint32, uint32_t)
       -LTK_GAP_BUFFER_INIT_DECL(script, hb_script_t)
       -LTK_GAP_BUFFER_INIT_DECL(int, int)
       +LTK_ARRAY_INIT_DECL(uint32, uint32_t)
       +LTK_ARRAY_INIT_DECL(script, hb_script_t)
        LTK_ARRAY_INIT_DECL(level, FriBidiLevel)
        LTK_ARRAY_INIT_DECL(int, int)
       -LTK_STACK_INIT_DECL(script, int, hb_script_t, pair_index, script)
        
        struct ltk_text_run {
                LtkGlyph *glyphs;
       t@@ -58,26 +56,26 @@ struct ltk_text_run {
        };
        
        struct ltk_text_line {
       -        struct ltk_gap_buffer_uint32 *log_buf; /* buffer of the logical text */
       -        struct ltk_gap_buffer_script *scripts;
       -        struct ltk_gap_buffer_uint32 *vis_buf; /* buffer of visual text */
       -        struct ltk_gap_buffer_int *log2vis;
       -        struct ltk_gap_buffer_int *vis2log;
       +        struct ltk_array_uint32 *log_buf; /* buffer of the logical text */
       +        struct ltk_array_script *scripts;
       +        struct ltk_array_uint32 *vis_buf; /* buffer of visual text */
       +        struct ltk_array_int *log2vis;
       +        struct ltk_array_int *vis2log;
                struct ltk_array_level *bidi_levels;
       +        struct ltk_array_int *wrap_indeces;
                struct ltk_text_run *first_run; /* first node in the linked list of runs */
                struct ltk_text_run *last_run; /* last node in the linked list of runs */
                struct ltk_text_run *cur_run; /* current node in the linked list of runs */
                struct ltk_text_line *next; /* next text line in the buffer */
                unsigned int height; /* height of the line (including wrapping) */
                FriBidiCharType dir; /* overall paragraph direction */
       -        struct ltk_array_int *wrap_indeces;
                size_t len;
                uint16_t font_size;
                int y_max;
                int y_min;
                int w;
                int h;
       -        XImage *img;
       +        size_t cursor_pos;
        };
        
        struct ltk_text_buffer {
       t@@ -86,4 +84,10 @@ struct ltk_text_buffer {
                unsigned int line_gap;
        };
        
       +XImage *ltk_render_text_line_new(struct ltk_text_line *tl, int max_width,
       +    Display *dpy, Window window, GC gc, Colormap colormap, XColor fg, XColor bg);
       +void ltk_text_line_insert_text(struct ltk_text_line *tl, uint32_t *text, size_t len);
       +struct ltk_text_line *ltk_text_line_create(void);
       +void ltk_text_line_destroy(struct ltk_text_line *tl);
       +
        #endif /* _TEXT_BUFFER_H_ */
   DIR diff --git a/text_edit.c b/text_edit.c
       t@@ -21,6 +21,20 @@
         * SOFTWARE.
         */
        
       +#include <stdint.h>
       +#include <X11/Xlib.h>
       +#include <X11/Xutil.h>
       +#include "khash.h"
       +#include "ltk.h"
       +#include "stb_truetype.h"
       +#include <fribidi.h>
       +#include <harfbuzz/hb.h>
       +#include <fontconfig/fontconfig.h>
       +#include "text-common.h"
       +#include "array.h"
       +#include "text_buffer.h"
       +#include "text_edit.h"
       +
        extern Ltk *ltk_global;
        
        void
       t@@ -29,9 +43,9 @@ ltk_draw_text_edit(LtkTextEdit *te) {
                XColor bg = ltk_global->theme->window->bg;
                LtkRect rect = te->widget.rect;
                LtkWindow *window = te->widget.window;
       -        if (!te->tl->img)
       +        if (!te->img)
                        ltk_render_text_line_new(te->tl, rect.w, ltk_global->display, window->xwindow, window->gc, ltk_global->colormap, fg, bg);
       -        XPutImage(ltk_global->display, window->xwindow, window->gc, te->tl->img, 0, 0, rect.x, rect.y, te->tl->w, te->tl->h);
       +        XPutImage(ltk_global->display, window->xwindow, window->gc, te->img, 0, 0, rect.x, rect.y, te->tl->w, te->tl->h);
        }
        
        LtkTextEdit *ltk_create_text_edit(LtkWindow *window, const char *text) {
       t@@ -41,8 +55,10 @@ LtkTextEdit *ltk_create_text_edit(LtkWindow *window, const char *text) {
                ltk_fill_widget_defaults(&te->widget, window, &ltk_draw_text_edit, &ltk_destroy_text_edit, 1);
                te->tl = ltk_text_line_create();
                ltk_text_line_insert_utf8(te->tl, text);
       +        te->img = NULL;
        }
        void ltk_destroy_text_edit(LtkTextEdit *te) {
                ltk_text_line_destroy(te->tl);
       +        if (te->img) XDestroyImage(te->img);
                free(te);
        }
   DIR diff --git a/text_edit.h b/text_edit.h
       t@@ -21,8 +21,12 @@
         * SOFTWARE.
         */
        
       +#ifndef _LTK_TEXT_EDIT_H_
       +#define _LTK_TEXT_EDIT_H_
       +
        typedef struct {
                LtkWidget widget;
       +        XImage *img;
                struct ltk_text_line *tl;
        } LtkTextEdit;
        
       t@@ -30,3 +34,5 @@ typedef struct {
        void ltk_draw_text_edit(LtkTextEdit *te);
        LtkTextEdit *ltk_create_text_edit(LtkWindow *window, const char *text);
        void ltk_destroy_text_edit(LtkTextEdit *te);
       +
       +#endif /* _LTK_TEXT_EDIT_H_ */