URI: 
       tFix gap buffer and clean up textedit - 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 b05e1135200febcf01061df3e109943b6d828b8f
   DIR parent f344c3f28da7995f19ec0549d90f275d3fd1715a
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Fri,  8 May 2020 21:13:42 +0200
       
       Fix gap buffer and clean up textedit
       
       Diffstat:
         M gap_buffer.h                        |      64 ++++++++++++++++---------------
         M textedit_wip.c                      |     619 ++++++-------------------------
         M textedit_wip.h                      |     139 +++----------------------------
       
       3 files changed, 162 insertions(+), 660 deletions(-)
       ---
   DIR diff --git a/gap_buffer.h b/gap_buffer.h
       t@@ -27,31 +27,32 @@
        #include <stdio.h>
        #include <stdlib.h>
        
       -#define LTK_GAP_BUFFER_INIT_DECL(type)                                                \
       -struct ltk_gap_buffer_##type## {                                                \
       +#define LTK_GAP_BUFFER_INIT_DECL(name, type)                                        \
       +struct ltk_gap_buffer_##name## {                                                \
                type *buf;                                                                \
                size_t buf_size;                                                        \
                size_t gap_left;                                                        \
                size_t gap_size;                                                        \
        };                                                                                \
                                                                                        \
       -struct ltk_gap_buffer_##type## * ltk_gap_buffer_create_##type##(void);                \
       -struct ltk_gap_buffer_##type## *                                                \
       -ltk_gap_buffer_create_from_data_##type##(type *data, size_t len);                \
       -void ltk_gap_buffer_resize_gap_##type##(struct ltk_gap_buffer *gb, int len);        \
       -void ltk_gap_buffer_insert_##type##(struct ltk_gap_buffer_##type## *gb,                \
       +struct ltk_gap_buffer_##name## * ltk_gap_buffer_create_##name##(void);                \
       +struct ltk_gap_buffer_##name## *                                                \
       +ltk_gap_buffer_create_from_data_##name##(type *data, size_t len);                \
       +void ltk_gap_buffer_resize_gap_##name##(                                        \
       +    struct ltk_gap_buffer_##name## *gb, int len);                                \
       +void ltk_gap_buffer_insert_##name##(struct ltk_gap_buffer_##type## *gb,                \
            type *new, size_t start, size_t len);                                        \
       -void ltk_gap_buffer_insert_single_##type##(                                        \
       -    struct ltk_gap_buffer_##type## *gb, type new);                                \
       -void ltk_gap_buffer_move_gap_##type##(                                                \
       -    struct ltk_gap_buffer_##type## *gb, size_t pos);                                \
       -void ltk_gap_buffer_destroy_##type##(struct ltk_gap_buffer_##type## *gb);
       +void ltk_gap_buffer_insert_single_##name##(                                        \
       +    struct ltk_gap_buffer_##name## *gb, type new);                                \
       +void ltk_gap_buffer_move_gap_##name##(                                                \
       +    struct ltk_gap_buffer_##name## *gb, size_t pos);                                \
       +void ltk_gap_buffer_destroy_##name##(struct ltk_gap_buffer_##name## *gb);
        
       -#define LTK_GAP_BUFFER_INIT_IMPL(type)                                                \
       -struct ltk_gap_buffer_##type## *                                                \
       -ltk_gap_buffer_create_##type##(void) {                                                \
       -        struct ltk_gap_buffer_##type## *gb =                                        \
       -            malloc(sizeof(struct ltk_gap_buffer));                                \
       +#define LTK_GAP_BUFFER_INIT_IMPL(name, type)                                        \
       +struct ltk_gap_buffer_##name## *                                                \
       +ltk_gap_buffer_create_##name##(void) {                                                \
       +        struct ltk_gap_buffer_##name## *gb =                                        \
       +            malloc(sizeof(struct ltk_gap_buffer_##name##));                        \
                if (!gb)                                                                \
                        goto error;                                                        \
                gb->buf = malloc(8 * sizeof(type));                                        \
       t@@ -67,10 +68,10 @@ error:                                                                                \
                exit(1);                                                                \
        }                                                                                \
                                                                                        \
       -struct ltk_gap_buffer_##type## *                                                \
       -ltk_gap_buffer_create_from_data_##type##(type *data, size_t len) {                \
       -        struct ltk_gap_buffer_##type## *gb =                                        \
       -            malloc(sizeof(struct ltk_gap_buffer));                                \
       +struct ltk_gap_buffer_##name## *                                                \
       +ltk_gap_buffer_create_from_data_##name##(type *data, size_t len) {                \
       +        struct ltk_gap_buffer_##name## *gb =                                        \
       +            malloc(sizeof(struct ltk_gap_buffer_##name##));                        \
                if (!gb) {                                                                \
                        (void)fprintf(stderr, "Out of memory while trying to"                \
                            "allocate gap buffer\n");                                        \
       t@@ -84,14 +85,15 @@ ltk_gap_buffer_create_from_data_##type##(type *data, size_t len) {                \
        }                                                                                \
                                                                                        \
        void                                                                                \
       -ltk_gap_buffer_resize_gap_##type##(struct ltk_gap_buffer *gb, int len) {        \
       +ltk_gap_buffer_resize_gap_##name##(                                                \
       +    struct ltk_gap_buffer_##name## *gb, int len) {                                \
                /* FIXME: Should this use realloc? It's usually more efficient, but        \
                   in this case, I would still need to copy the part after the gap        \
                   manually, so it could potentially be copied twice, which really        \
                   wouldn't be good. Maybe use realloc if only a small part is after        \
                   the gap and just regular malloc otherwise? */                        \
                int new_size = gb->buf_size - gb->gap-size + len;                        \
       -        struct ltk_gap_buffer_##type## *new = malloc(new_size * sizeof(type));        \
       +        struct ltk_gap_buffer_##name## *new = malloc(new_size * sizeof(type));        \
                if (!new) {                                                                \
                        (void)fprintf(stderr, "Out of memory while trying to"                \
                            "resize gap buffer\n");                                        \
       t@@ -108,10 +110,10 @@ ltk_gap_buffer_resize_gap_##type##(struct ltk_gap_buffer *gb, int len) {        \
        }                                                                                \
                                                                                        \
        void                                                                                \
       -ltk_gap_buffer_insert_##type##(struct ltk_gap_buffer_##type## *gb,                \
       +ltk_gap_buffer_insert_##name##(struct ltk_gap_buffer_##name## *gb,                \
            type *new, size_t start, size_t len) {                                        \
                if (gb->gap_size < len)                                                        \
       -                ltk_gap_buffer_resize_gap_##type##(gb, len + 8);                \
       +                ltk_gap_buffer_resize_gap_##name##(gb, len + 8);                \
                for (int i = 0; i < len; i++) {                                                \
                        gb->buf[gb->gap_left + i] = new[start + i];                        \
                }                                                                        \
       t@@ -120,14 +122,14 @@ ltk_gap_buffer_insert_##type##(struct ltk_gap_buffer_##type## *gb,                \
        }                                                                                \
                                                                                        \
        void                                                                                \
       -ltk_gap_buffer_insert_single_##type##(                                                \
       -    struct ltk_gap_buffer_##type## *gb, type new) {                                \
       -        ltk_gap_buffer_insert_##type##(gb, &new, 0, 1);                                \
       +ltk_gap_buffer_insert_single_##name##(                                                \
       +    struct ltk_gap_buffer_##name## *gb, type new) {                                \
       +        ltk_gap_buffer_insert_##name##(gb, &new, 0, 1);                                \
        }                                                                                \
                                                                                        \
        void                                                                                \
       -ltk_gap_buffer_move_gap_##type##(                                                \
       -    struct ltk_gap_buffer_##type## *gb, size_t pos) {                                \
       +ltk_gap_buffer_move_gap_##name##(                                                \
       +    struct ltk_gap_buffer_##name## *gb, size_t pos) {                                \
                if (pos == gb->gap_left)                                                \
                        return;                                                                \
                if (pos < 0 || pos > gb->buf_size - gb->gap_size) {                        \
       t@@ -148,7 +150,7 @@ ltk_gap_buffer_move_gap_##type##(                                                \
        }                                                                                \
                                                                                        \
        void                                                                                \
       -ltk_gap_buffer_destroy_##type##(struct ltk_gap_buffer_##type## *gb) {                \
       +ltk_gap_buffer_destroy_##name##(struct ltk_gap_buffer_##name## *gb) {                \
                free(gb->buf);                                                                \
                free(gb);                                                                \
        }
   DIR diff --git a/textedit_wip.c b/textedit_wip.c
       t@@ -1,6 +1,6 @@
        /*
         * This file is part of the Lumidify ToolKit (LTK)
       - * Copyright (c) 2017, 2018, 2020 lumidify <nobody@lumidify.org>
       + * Copyright (c) 2020 lumidify <nobody@lumidify.org>
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
       t@@ -33,290 +33,145 @@
        #include <fribidi.h>
        #include <harfbuzz/hb.h>
        #include <harfbuzz/hb-ot.h>
       -#include "text-hb.h"
       +#include "textedit_wip.h"
       +#include "text-common.h"
        #include "ltk.h"
        
        extern Ltk *ltk_global;
        
       -/* These unicode routines are taken from
       - * https://github.com/JeffBezanson/cutef8 */
       -
       -/* is c the start of a utf8 sequence? */
       -#define isutf(c) (((c)&0xC0)!=0x80)
       -
       -static const uint32_t offsetsFromUTF8[6] = {
       -    0x00000000UL, 0x00003080UL, 0x000E2080UL,
       -    0x03C82080UL, 0xFA082080UL, 0x82082080UL
       -};
       -
       -/* next character without NUL character terminator */
       -uint32_t u8_nextmemchar(const char *s, size_t *i)
       -{
       -    uint32_t ch = 0;
       -    size_t sz = 0;
       -    do {
       -        ch <<= 6;
       -        ch += (unsigned char)s[(*i)++];
       -        sz++;
       -    } while (!isutf(s[*i]));
       -    ch -= offsetsFromUTF8[sz-1];
       -
       -    return ch;
       -}
       -
       -/* number of characters in NUL-terminated string */
       -size_t u8_strlen(const char *s)
       -{
       -    size_t count = 0;
       -    size_t i = 0, lasti;
       -
       -    while (1) {
       -        lasti = i;
       -        while (s[i] > 0)
       -            i++;
       -        count += (i-lasti);
       -        if (s[i++]==0) break;
       -        (void)(isutf(s[++i]) || isutf(s[++i]) || ++i);
       -        count++;
       -    }
       -    return count;
       -}
       -
       -size_t u8_wc_toutf8(char *dest, uint32_t ch)
       -{
       -    if (ch < 0x80) {
       -        dest[0] = (char)ch;
       -        return 1;
       -    }
       -    if (ch < 0x800) {
       -        dest[0] = (ch>>6) | 0xC0;
       -        dest[1] = (ch & 0x3F) | 0x80;
       -        return 2;
       -    }
       -    if (ch < 0x10000) {
       -        dest[0] = (ch>>12) | 0xE0;
       -        dest[1] = ((ch>>6) & 0x3F) | 0x80;
       -        dest[2] = (ch & 0x3F) | 0x80;
       -        return 3;
       -    }
       -    if (ch < 0x110000) {
       -        dest[0] = (ch>>18) | 0xF0;
       -        dest[1] = ((ch>>12) & 0x3F) | 0x80;
       -        dest[2] = ((ch>>6) & 0x3F) | 0x80;
       -        dest[3] = (ch & 0x3F) | 0x80;
       -        return 4;
       -    }
       -    return 0;
       -}
       -
       -LtkTextManager *
       -ltk_init_text(char *font_name)
       -{
       -        LtkTextManager *tm = malloc(sizeof(LtkTextManager));
       -        if (!tm) {
       -                (void)printf("Memory exhausted when trying to create text manager.");
       -                exit(1);
       -        }
       -        tm->font_paths = kh_init(fontid);
       -        tm->font_cache = kh_init(fontstruct);
       -        tm->glyph_cache = kh_init(glyphcache);
       -        tm->font_id_cur = 0;
       -        ltk_load_default_font(tm, font_name);
       -
       -        return tm;
       -}
       -
       -void
       -ltk_destroy_text_manager(LtkTextManager *tm)
       +/* FIXME: allow to either use fribidi for basic shaping and don't use harfbuzz then,
       +          or just use harfbuzz (then fribidi doesn't need to do any shaping) */
       +LtkTextLine *
       +ltk_create_text_line(LtkTextManager *tm, char *text, uint16_t fontid, uint16_t size)
        {
       +        /* NOTE: This doesn't actually take fontid into account right now - should it? */
       +        LtkTextLine *tl = malloc(sizeof(LtkTextLine));
       +        tl->start_segment = NULL;
       +        LtkTextSegment *cur_ts = NULL;
       +        LtkTextSegment *new_ts = NULL;
       +        uint16_t cur_font_id = fontid;
                int k;
       +        LtkFont *font;
        
       -        kh_destroy(fontid, tm->font_paths);
       -
       -        for (k = kh_begin(tm->font_cache); k != kh_end(tm->font_cache); k++) {
       -                if (kh_exist(tm->font_cache, k)) {
       -                        ltk_destroy_font(kh_value(tm->font_cache, k));
       -                }
       -        }
       -        kh_destroy(fontstruct, tm->font_cache);
       -
       -        for (k = kh_begin(tm->glyph_cache); k != kh_end(tm->glyph_cache); k++) {
       -                if (kh_exist(tm->glyph_cache, k)) {
       -                        ltk_destroy_glyph_cache(kh_value(tm->glyph_cache, k));
       -                }
       -        }
       -        kh_destroy(glyphcache, tm->glyph_cache);
       -
       -        free(tm);
       -}
       -
       -LtkGlyphInfo *
       -ltk_create_glyph_info(LtkFont *font, unsigned int id, float scale)
       -{
       -        LtkGlyphInfo *glyph = malloc(sizeof(LtkGlyphInfo));
       -        if (!glyph) {
       -                (void)printf("Out of memory!\n");
       -                exit(1);
       -        }
       -        
       -        glyph->id = id;
       -        glyph->refs = 0;
       -        glyph->alphamap = stbtt_GetGlyphBitmap(
       -                &font->info, scale, scale, id, &glyph->w,
       -                &glyph->h, &glyph->xoff, &glyph->yoff
       -        );
       -
       -        return glyph;
       -}
       -
       -void
       -ltk_destroy_glyph_info(LtkGlyphInfo *gi)
       -{
       -        free(gi->alphamap);
       -        free(gi);
       -}
       -
       -LtkGlyphInfo *
       -ltk_get_glyph_info(LtkFont *font, unsigned int id, float scale, khash_t(glyphinfo) *cache)
       -{
       -        int ret;
       -        khint_t k;
       -        LtkGlyphInfo *glyph;
       -        k = kh_get(glyphinfo, cache, id);
       -        if (k == kh_end(cache)) {
       -                glyph = ltk_create_glyph_info(font, id, scale);
       -                /* FIXME: error checking with ret */
       -                k = kh_put(glyphinfo, cache, id, &ret);
       -                kh_value(cache, k) = glyph;
       -        } else {
       -                glyph = kh_value(cache, k);
       +        unsigned int ulen = u8_strlen(text);
       +        uint32_t *log_str = malloc(sizeof(uint32_t) * ulen);
       +        size_t inc = 0;
       +        for (int i = 0; i < ulen; i++) {
       +                log_str[i] = u8_nextmemchar(text, &inc);
                }
       +        FriBidiChar *vis_str = malloc(sizeof(FriBidiChar) * ulen);
       +        ulen = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, text, strlen(text), log_str);
       +        fribidi_log2vis(log_str, ulen, pbase_dir, vis_str, NULL, NULL, NULL);
        
       -        return glyph;
       -}
       -
       -khint_t
       -ltk_create_glyph_cache(LtkTextManager *tm, uint16_t font_id, uint16_t font_size)
       -{
       -        khash_t(glyphinfo) *cache = kh_init(glyphinfo);
       -        int ret;
       -        khint_t k;
       -        /* I guess I can just ignore ret for now */
       -        k = kh_put(glyphcache, tm->glyph_cache, font_id << 16 + font_size, &ret);
       -        kh_value(tm->glyph_cache, k) = cache;
       +        hb_unicode_funcs_t *ufuncs = hb_unicode_funcs_get_default();
       +        hb_script_t cur_script = hb_unicode_script(ufuncs, vis_str[0]);
       +        hb_script_t last_script = cur_script;
       +        size_t pos = 0;
       +        size_t last_pos = 0;
       +        size_t start_pos = 0;
       +        uint32_t ch;
        
       -        return k;
       -}
       +        for (int p = 0; p <= ulen; p++) {
       +                cur_script = hb_unicode_script(ufuncs, vis_str[p]);
       +                if (p == ulen ||
       +                        (last_script != cur_script &&
       +                         cur_script != HB_SCRIPT_INHERITED &&
       +                         cur_script != HB_SCRIPT_COMMON)) {
       +                        FcPattern *pat = FcPatternDuplicate(tm->fcpattern);
       +                        FcPattern *match;
       +                        FcResult result;
       +                        FcPatternAddBool(pat, FC_SCALABLE, 1);
       +                        FcConfigSubstitute(NULL, pat, FcMatchPattern);
       +                        FcDefaultSubstitute(pat);
       +                        FcCharSet *cs = FcCharSetCreate();
       +                        for (int i = start_pos; i < p; i++) {
       +                                FcCharSetAddChar(cs, vis_str[i]);
       +                        }
       +                        FcPatternAddCharSet(pat, FC_CHARSET, cs);
       +                        match = FcFontMatch(NULL, pat, &result);
       +                        char *file;
       +                        FcPatternGetString(match, FC_FILE, 0, &file);
       +                        cur_font_id = ltk_get_font(tm, file);
       +                        k = kh_get(fontstruct, tm->font_cache, cur_font_id);
       +                        font = kh_value(tm->font_cache, k);
       +                        FcPatternDestroy(match);
       +                        FcPatternDestroy(pat);
       +                        // handle case that this is the last character
       +                        if (p == ulen) {
       +                                last_script = cur_script;
       +                        }
       +                        /* FIXME: There should be better handling for cases
       +                           where an error occurs while creating the segment */
       +                        new_ts = ltk_create_text_segment(
       +                                tm, vis_str + start_pos,
       +                                p - start_pos, cur_font_id,
       +                                size, last_script
       +                        );
       +                        if (!new_ts) continue;
       +                        new_ts->next = NULL;
       +                        if (!tl->start_segment) tl->start_segment = new_ts;
       +                        if (cur_ts) cur_ts->next = new_ts;
       +                        cur_ts = new_ts;
        
       -void
       -ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache)
       -{
       -        int k;
       -        for (k = kh_begin(cache); k != kh_end(cache); k++) {
       -                if (kh_exist(cache, k)) {
       -                        ltk_destroy_glyph_info(kh_value(cache, k));
       +                        start_pos = p;
       +                        last_script = cur_script;
                        }
                }
       -        kh_destroy(glyphinfo, cache);
       -}
       -
       -void
       -ltk_load_default_font(LtkTextManager *tm, char *name)
       -{
       -        FcPattern *match;
       -        FcResult result;
       -        char *file;
       -        int index;
       -        uint16_t font;
        
       -        tm->fcpattern = FcNameParse(name);
       -        FcPatternAddString(tm->fcpattern, FC_FONTFORMAT, "truetype");
       -        FcConfigSubstitute(NULL, tm->fcpattern, FcMatchPattern);
       -        FcDefaultSubstitute(tm->fcpattern);
       -        match = FcFontMatch(NULL, tm->fcpattern, &result);
       -
       -        FcPatternGetString (match, FC_FILE, 0, (FcChar8 **) &file);
       -        /* FIXME: Why is index never used? This is the index within the font file,
       -           so it might be important, although I'm not sure if stb_truetype even
       -           supports it */
       -        FcPatternGetInteger (match, FC_INDEX, 0, &index);
       -
       -        tm->default_font = ltk_get_font(tm, file);
       -
       -        FcPatternDestroy (match);
       -}
       +        free(vis_str);
       +        free(log_str);
        
       -LtkFont *
       -ltk_create_font(char *path, uint16_t id)
       -{
       -        long len;
       -        LtkFont *font = malloc(sizeof(LtkFont));
       -        if (!font) {
       -                (void)fprintf(stderr, "Out of memory!\n");
       -                exit(1);
       +        /* calculate width of text line
       +           NOTE: doesn't work with mixed horizontal and vertical text */
       +        LtkTextSegment *ts = tl->start_segment;
       +        int is_hor = HB_DIRECTION_IS_HORIZONTAL(ts->dir);
       +        tl->y_max = tl->x_max = INT_MIN;
       +        tl->y_min = tl->x_min = INT_MAX;
       +        tl->w = tl->h = 0;
       +        while (ts) {
       +                if (HB_DIRECTION_IS_HORIZONTAL(ts->dir) != is_hor) {
       +                        (void)fprintf(stderr, "WARNING: mixed horizontal/vertical text is not supported; ignoring\n");
       +                        continue;
       +                }
       +                if (is_hor) {
       +                        if (tl->y_max < ts->y_max) {
       +                                tl->y_max = ts->y_max;
       +                        }
       +                        if (tl->y_min > ts->y_min) {
       +                                tl->y_min = ts->y_min;
       +                        }
       +                        tl->w += ts->w;
       +                } else {
       +                        if (tl->x_max < ts->x_max) {
       +                                tl->x_max = ts->x_max;
       +                        }
       +                        if (tl->x_min > ts->x_min) {
       +                                tl->x_min = ts->x_min;
       +                        }
       +                        tl->h += ts->h;
       +                }
       +                ts = ts->next;
                }
       -        char *contents = ltk_read_file(path, &len);
       -        if (!stbtt_InitFont(&font->info, contents, 0))
       -        {
       -                (void)fprintf(stderr, "Failed to load font %s\n", path);
       -                exit(1);
       +        if (is_hor) {
       +                tl->h = tl->y_max - tl->y_min;
       +        } else {
       +                tl->w = tl->x_max - tl->x_min;
                }
       -        /* FIXME: make use of the destroy function (last argument to hb_blob_create - see hb-blob.cc in harfbuzz source) */
       -        hb_blob_t *blob = hb_blob_create(contents, len, HB_MEMORY_MODE_READONLY, NULL, NULL);
       -        hb_face_t *face = hb_face_create(blob, 0);
       -        /* FIXME: need to use destroy function in order for the original file data to be freed? */
       -        hb_blob_destroy(blob);
       -        font->hb = hb_font_create(face);
       -        hb_face_destroy(face);
       -        hb_ot_font_set_funcs(font->hb);
       -        font->id = id;
       -        font->refs = 0;
        
       -        return font;
       +        return tl;
        }
        
        void
       -ltk_destroy_font(LtkFont *font)
       -{
       -        free(font->info.data);
       -        hb_font_destroy(font->hb);
       -        free(font);
       -}
       -
       -uint16_t
       -ltk_load_font(LtkTextManager *tm, char *path)
       -{
       -        LtkFont *font = ltk_create_font(path, tm->font_id_cur++);
       -        int ret;
       -        khint_t k;
       -        /* FIXME: does kh_destroy also free these copied strings properly? */
       -        char *key = strdup(path);
       -        k = kh_put(fontid, tm->font_paths, key, &ret);
       -        kh_value(tm->font_paths, k) = font->id;
       -        k = kh_put(fontstruct, tm->font_cache, (khint_t) font->id, &ret);
       -        kh_value(tm->font_cache, k) = font;
       -        k = kh_get(fontid, tm->font_paths, path);
       -
       -        return font->id;
       -}
       -
       -uint16_t
       -ltk_get_font(LtkTextManager *tm, char *path)
       -{
       -        int ret;
       -        khint_t k;
       -        uint16_t id;
       -        k = kh_get(fontid, tm->font_paths, path);
       -        if (k == kh_end(tm->font_paths)) {
       -                id = ltk_load_font(tm, path);
       -        } else {
       -                id = kh_value(tm->font_paths, k);
       +ltk_destroy_text_line(LtkTextLine *tl) {
       +        LtkTextSegment *last_ts;
       +        LtkTextSegment *cur_ts = tl->start_segment;
       +        while (cur_ts) {
       +                last_ts = cur_ts;
       +                cur_ts = cur_ts->next;
       +                ltk_destroy_text_segment(last_ts);
                }
       -
       -        return id;
        }
        
       -
        /* FIXME: could use unsigned int for fontid and size as long as there is code to check neither of them become too large
           -> in case I want to get rid of uint_16_t, etc. */
        LtkTextSegment *
       t@@ -440,18 +295,6 @@ ltk_create_text_segment(LtkTextManager *tm, uint32_t *text, unsigned int len, ui
        }
        
        void
       -ltk_destroy_glyph(LtkGlyph *glyph, khash_t(glyphinfo) *cache)
       -{
       -        int k;
       -        if (--glyph->info->refs < 1) {
       -                k = kh_get(glyphinfo, cache, glyph->info->id);
       -                kh_del(glyphinfo, cache, k);
       -                ltk_destroy_glyph_info(glyph->info);
       -        }
       -        free(glyph);
       -}
       -
       -void
        ltk_destroy_text_segment(LtkTextSegment *ts)
        {
                LtkGlyph *glyph, *next_glyph;
       t@@ -554,6 +397,7 @@ ltk_render_text_segment(
        }
        
        /*
       +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
       t@@ -577,102 +421,6 @@ soon as a different script is inserted, everything has to be redone anyways. Als
        when reshaping with context, only the text in the current run has to be passed at all.
        */
        
       -struct ltk_gap_buffer *
       -ltk_gap_buffer_create(void) {
       -        struct ltk_gap_buffer *gb = malloc(sizeof(struct ltk_gap_buffer));
       -        if (!gb)
       -                goto error;
       -        gb->buf = malloc(8 * sizeof(uint32_t));
       -        if (!gb->buf)
       -                goto error;
       -        gb->buf_size = 8;
       -        gb->gap_left = 0;
       -        gb->gap_size = 8;
       -        return gb;
       -error:
       -        (void)fprintf(stderr, "Out of memory while trying to allocate gap buffer\n");
       -        exit(1);
       -}
       -
       -struct ltk_gap_buffer *
       -ltk_gap_buffer_create_from_data(uint32_t *data, size_t len) {
       -        struct ltk_gap_buffer *gb = malloc(sizeof(struct ltk_gap_buffer));
       -        if (!gb) {
       -                (void)fprintf(stderr, "Out of memory while trying to allocate gap buffer\n");
       -                exit(1);
       -        }
       -        gb->buf = data;
       -        gb->buf_size = len;
       -        gb->gap_left = 0;
       -        gb->gap_size = 0;
       -        return gb;
       -}
       -
       -void
       -ltk_gap_buffer_resize_gap(struct ltk_gap_buffer *gb, int len) {
       -        /* FIXME: Should this use realloc? It's usually more efficient, but
       -           in this case, I would still need to copy the part after the gap
       -           manually, so it could potentially be copied twice, which really
       -           wouldn't be good. Maybe use realloc if only a small part is after
       -           the gap and just regular malloc otherwise? */
       -        int new_size = gb->buf_size - gb->gap-size + len;
       -        struct ltk_gap_buffer *new = malloc(new_size * sizeof(uint32_t));
       -        if (!new) {
       -                (void)fprintf(stderr, "Out of memory while trying to resize gap buffer\n");
       -                exit(1);
       -        }
       -        for (int i = 0; i < gb->gap_left; i++) {
       -                new[i] = gb->buf[i];
       -        }
       -        for (int i = gb->gap_left + gb->gap_size; i < gb->buf_size) {
       -                new[i - gb->gap_size + len] = gb->buf[i];
       -        }
       -        free(gb->buf);
       -        gb->buf = new;
       -}
       -
       -void
       -ltk_gap_buffer_insert(struct ltk_gap_buffer *gb, uint32_t *new, size_t start, size_t len) {
       -        if (gb->gap_size < len)
       -                ltk_gap_buffer_resize_gap(gb, len + 8);
       -        for (int i = 0; i < len; i++) {
       -                gb->buf[gb->gap_left + i] = new[start + i];
       -        }
       -        gb->gap_left = gb->gap_left + len;
       -        gb->gap_size -= len;
       -}
       -
       -void
       -ltk_gap_buffer_insert_single(struct ltk_gap_buffer *gb, uint32_t new) {
       -        ltk_gap_buffer_insert(gb, &new, 0, 1);
       -}
       -
       -void
       -ltk_gap_buffer_move_gap(struct ltk_gap_buffer *gb, size_t pos) {
       -        if (pos == gb->gap_left)
       -                return;
       -        if (pos < 0 || pos > gb->buf_size - gb->gap_size) {
       -                (void)fprintf(stderr, "Index out of range while moving gap buffer gap\n");
       -                return;
       -        }
       -        if (pos >= gb->gap_left) {
       -                for (int i = gb->gap_left; i < pos) {
       -                        gb->buf[i] = gb->buf[i + gb->gap_size];
       -                }
       -        } else {
       -                for (int i = gb->gap_left - 1; i >= pos; i--) {
       -                        gb->buf[i + gb->gap_size] = gb->buf[i];
       -                }
       -        }
       -        gb->gap_left = pos;
       -}
       -
       -void
       -ltk_gap_buffer_destroy(struct ltk_gap_buffer *gb) {
       -        free(gb->buf);
       -        free(gb);
       -}
       -
        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 */
       t@@ -685,136 +433,3 @@ ltk_text_line_delete_text(struct ltk_text_line *tl, size_t len) {
        void
        ltk_text_line_delete_cur_cluster(struct ltk_text_line *tl) {
        }
       -
       -/* FIXME: allow to either use fribidi for basic shaping and don't use harfbuzz then,
       -          or just use harfbuzz (then fribidi doesn't need to do any shaping) */
       -LtkTextLine *
       -ltk_create_text_line(LtkTextManager *tm, char *text, uint16_t fontid, uint16_t size)
       -{
       -        /* NOTE: This doesn't actually take fontid into account right now - should it? */
       -        LtkTextLine *tl = malloc(sizeof(LtkTextLine));
       -        tl->start_segment = NULL;
       -        LtkTextSegment *cur_ts = NULL;
       -        LtkTextSegment *new_ts = NULL;
       -        uint16_t cur_font_id = fontid;
       -        int k;
       -        LtkFont *font;
       -
       -        unsigned int ulen = u8_strlen(text);
       -        uint32_t *log_str = malloc(sizeof(uint32_t) * ulen);
       -        size_t inc = 0;
       -        for (int i = 0; i < ulen; i++) {
       -                log_str[i] = u8_nextmemchar(text, &inc);
       -        }
       -        FriBidiChar *vis_str = malloc(sizeof(FriBidiChar) * ulen);
       -        ulen = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, text, strlen(text), log_str);
       -        fribidi_log2vis(log_str, ulen, pbase_dir, vis_str, NULL, NULL, NULL);
       -
       -        hb_unicode_funcs_t *ufuncs = hb_unicode_funcs_get_default();
       -        hb_script_t cur_script = hb_unicode_script(ufuncs, vis_str[0]);
       -        hb_script_t last_script = cur_script;
       -        size_t pos = 0;
       -        size_t last_pos = 0;
       -        size_t start_pos = 0;
       -        uint32_t ch;
       -
       -        for (int p = 0; p <= ulen; p++) {
       -                cur_script = hb_unicode_script(ufuncs, vis_str[p]);
       -                if (p == ulen ||
       -                        (last_script != cur_script &&
       -                         cur_script != HB_SCRIPT_INHERITED &&
       -                         cur_script != HB_SCRIPT_COMMON)) {
       -                        FcPattern *pat = FcPatternDuplicate(tm->fcpattern);
       -                        FcPattern *match;
       -                        FcResult result;
       -                        FcPatternAddBool(pat, FC_SCALABLE, 1);
       -                        FcConfigSubstitute(NULL, pat, FcMatchPattern);
       -                        FcDefaultSubstitute(pat);
       -                        FcCharSet *cs = FcCharSetCreate();
       -                        for (int i = start_pos; i < p; i++) {
       -                                FcCharSetAddChar(cs, vis_str[i]);
       -                        }
       -                        FcPatternAddCharSet(pat, FC_CHARSET, cs);
       -                        match = FcFontMatch(NULL, pat, &result);
       -                        char *file;
       -                        FcPatternGetString(match, FC_FILE, 0, &file);
       -                        cur_font_id = ltk_get_font(tm, file);
       -                        k = kh_get(fontstruct, tm->font_cache, cur_font_id);
       -                        font = kh_value(tm->font_cache, k);
       -                        FcPatternDestroy(match);
       -                        FcPatternDestroy(pat);
       -                        // handle case that this is the last character
       -                        if (p == ulen) {
       -                                last_script = cur_script;
       -                        }
       -                        /* FIXME: There should be better handling for cases
       -                           where an error occurs while creating the segment */
       -                        new_ts = ltk_create_text_segment(
       -                                tm, vis_str + start_pos,
       -                                p - start_pos, cur_font_id,
       -                                size, last_script
       -                        );
       -                        if (!new_ts) continue;
       -                        new_ts->next = NULL;
       -                        if (!tl->start_segment) tl->start_segment = new_ts;
       -                        if (cur_ts) cur_ts->next = new_ts;
       -                        cur_ts = new_ts;
       -
       -                        start_pos = p;
       -                        last_script = cur_script;
       -                }
       -        }
       -
       -        free(vis_str);
       -        free(log_str);
       -
       -        /* calculate width of text line
       -           NOTE: doesn't work with mixed horizontal and vertical text */
       -        LtkTextSegment *ts = tl->start_segment;
       -        int is_hor = HB_DIRECTION_IS_HORIZONTAL(ts->dir);
       -        tl->y_max = tl->x_max = INT_MIN;
       -        tl->y_min = tl->x_min = INT_MAX;
       -        tl->w = tl->h = 0;
       -        while (ts) {
       -                if (HB_DIRECTION_IS_HORIZONTAL(ts->dir) != is_hor) {
       -                        (void)fprintf(stderr, "WARNING: mixed horizontal/vertical text is not supported; ignoring\n");
       -                        continue;
       -                }
       -                if (is_hor) {
       -                        if (tl->y_max < ts->y_max) {
       -                                tl->y_max = ts->y_max;
       -                        }
       -                        if (tl->y_min > ts->y_min) {
       -                                tl->y_min = ts->y_min;
       -                        }
       -                        tl->w += ts->w;
       -                } else {
       -                        if (tl->x_max < ts->x_max) {
       -                                tl->x_max = ts->x_max;
       -                        }
       -                        if (tl->x_min > ts->x_min) {
       -                                tl->x_min = ts->x_min;
       -                        }
       -                        tl->h += ts->h;
       -                }
       -                ts = ts->next;
       -        }
       -        if (is_hor) {
       -                tl->h = tl->y_max - tl->y_min;
       -        } else {
       -                tl->w = tl->x_max - tl->x_min;
       -        }
       -
       -        return tl;
       -}
       -
       -void
       -ltk_destroy_text_line(LtkTextLine *tl) {
       -        LtkTextSegment *last_ts;
       -        LtkTextSegment *cur_ts = tl->start_segment;
       -        while (cur_ts) {
       -                last_ts = cur_ts;
       -                cur_ts = cur_ts->next;
       -                ltk_destroy_text_segment(last_ts);
       -        }
       -}
   DIR diff --git a/textedit_wip.h b/textedit_wip.h
       t@@ -1,6 +1,6 @@
        /*
         * This file is part of the Lumidify ToolKit (LTK)
       - * Copyright (c) 2017, 2018, 2020 lumidify <nobody@lumidify.org>
       + * Copyright (c) 2020 lumidify <nobody@lumidify.org>
         *
         * Permission is hereby granted, free of charge, to any person obtaining a copy
         * of this software and associated documentation files (the "Software"), to deal
       t@@ -31,8 +31,10 @@ Requires the following includes:
        <fontconfig/fontconfig.h>
        */
        
       +#include "gap_buffer.h"
       +
        /* Contains glyph info specific to one run of text */
       -typedef struct ltk_glyph {
       +struct ltk_glyph {
                LtkGlyphInfo *info;
                int x_offset; /* additional x offset given by harfbuzz */
                int y_offset; /* additional y offset given by harfbuzz */
       t@@ -43,18 +45,12 @@ typedef struct ltk_glyph {
                uint32_t cluster; /* index of char in original text - from harfbuzz */
        };
        
       -struct ltk_gap_buffer {
       -        uint32_t *buf;
       -        size_t buf_size;
       -        size_t gap_left;
       -        size_t gap_size;
       -};
       +LTK_GAP_BUFFER_INIT_DECL(uint32, uint32_t)
       +LTK_GAP_BUFFER_INIT_DECL(int, int)
       +LTK_GAP_BUFFER_INIT_DECL(glyph, struct ltk_glyph)
        
       -/* FIXME: macro version of gap buffer */
        struct ltk_text_run {
       -        /* maybe make gap buffer of glyphs? */
       -        struct ltk_glyph *head_glyph;
       -        struct ltk_glyph *cur_glyph;
       +        struct ltk_gap_buffer_glyph *glyphs;
                struct ltk_text_run *next;
                LtkFont *font;
                unsigned int w;
       t@@ -69,9 +65,10 @@ struct ltk_text_run {
        }
        
        struct ltk_text_line {
       -        struct ltk_gap_buffer *text_buf; /* buffer of the logical text */
       -        struct ltk_gap_buffer *visual_text; /* buffer of visual text */
       -        /* still need log2vis and vis2log */
       +        struct ltk_gap_buffer_uint32 *log_buf; /* buffer of the logical text */
       +        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_text_run *runs; /* first 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 */
       t@@ -84,115 +81,3 @@ struct ltk_text_buffer {
                struct ltk_text_line *cur_line;
                unsigned int line_gap;
        };
       -
       -typedef struct {
       -        stbtt_fontinfo info;
       -        hb_font_t *hb;
       -        uint16_t id;
       -        unsigned int refs;
       -} LtkFont;
       -
       -/* Contains general info on glyphs that doesn't change regardless of the context */
       -typedef struct _LtkGlyphInfo {
       -        unsigned int id;
       -        unsigned char *alphamap;
       -        unsigned int w;
       -        unsigned int h;
       -        unsigned int xoff; /* x offset from origin to top left corner of glyph */
       -        unsigned int yoff; /* y offset from origin to top left corner of glyph */
       -        unsigned int refs;
       -        /* FIXME: does refs need to be long? It could cause problems if a
       -        program tries to cache/"keep alive" a lot of pages of text. */
       -} LtkGlyphInfo;
       -
       -/* Single segment of text with same font */
       -typedef struct LtkTextSegment {
       -        uint16_t font_id;
       -        uint16_t font_size;
       -        unsigned int w;
       -        unsigned int h;
       -        int start_x;
       -        int start_y;
       -        int x_min;
       -        int y_min;
       -        int x_max;
       -        int y_max;
       -        hb_direction_t dir;
       -        uint32_t *str;
       -        LtkGlyph *start_glyph;
       -        struct LtkTextSegment *next;
       -} LtkTextSegment;
       -
       -/* Single line of text */
       -typedef struct {
       -        unsigned int w;
       -        unsigned int h;
       -        int x_max;
       -        int x_min;
       -        int y_max;
       -        int y_min;
       -        FriBidiParType dir;
       -        LtkTextSegment *start_segment;
       -} LtkTextLine;
       -
       -/* Hash definitions */
       -/* glyph id -> glyph info struct */
       -KHASH_MAP_INIT_INT(glyphinfo, LtkGlyphInfo*)
       -/* font path, size -> glyph cache hash */
       -KHASH_MAP_INIT_INT(glyphcache, khash_t(glyphinfo)*)
       -/* font path -> font id */
       -KHASH_MAP_INIT_STR(fontid, uint16_t)
       -/* font id -> font struct */
       -KHASH_MAP_INIT_INT(fontstruct, LtkFont*)
       -
       -typedef struct LtkTextManager {
       -        khash_t(fontid) *font_paths;
       -        khash_t(fontstruct) *font_cache;
       -        khash_t(glyphcache) *glyph_cache;
       -        FcPattern *fcpattern;
       -        uint16_t default_font;
       -        uint16_t font_id_cur;
       -} LtkTextManager;
       -
       -LtkTextManager *ltk_init_text(char *font_name);
       -
       -void ltk_destroy_text_manager(LtkTextManager *tm);
       -
       -LtkGlyphInfo *ltk_create_glyph_info(LtkFont *font, unsigned int id, float scale);
       -
       -void ltk_destroy_glyph_info(LtkGlyphInfo *gi);
       -
       -LtkGlyphInfo *ltk_get_glyph_info(LtkFont *font, unsigned int id, float scale, khash_t(glyphinfo) *cache);
       -
       -khint_t ltk_create_glyph_cache(LtkTextManager *tm, uint16_t font_id, uint16_t font_size);
       -
       -void ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache);
       -
       -void ltk_load_default_font(LtkTextManager *tm, char *name);
       -
       -LtkFont *ltk_create_font(char *path, uint16_t id);
       -
       -void ltk_destroy_font(LtkFont *font);
       -
       -/* FIXME: need to figure out how exactly the whole font system is going to work, especially with default fonts, etc. */
       -uint16_t ltk_load_font(LtkTextManager *tm, char *path);
       -
       -uint16_t ltk_get_font(LtkTextManager *tm, char *path);
       -
       -/* TODO: different sizes, colors, styles, etc. */
       -LtkTextLine *ltk_create_text_line(LtkTextManager *tm, char *text, uint16_t fontid, uint16_t size);
       -
       -/* FIXME: could use unsigned int for fontid and size as long as there is code to check neither of them become too large
       -   -> in case I want to get rid of uint_16_t, etc. */
       -LtkTextSegment *ltk_create_text_segment(LtkTextManager *tm, uint32_t *text, unsigned int text_len, uint16_t fontid, uint16_t size, hb_script_t script);
       -
       -void ltk_destroy_glyph(LtkGlyph *glyph, khash_t(glyphinfo) *cache);
       -
       -void ltk_destroy_text_segment(LtkTextSegment *ts);
       -
       -XImage *ltk_render_text_line(LtkTextLine *tl, Display *dpy, Window window, GC gc, Colormap colormap, XColor fg, XColor bg);
       -
       -void ltk_render_text_segment(LtkTextSegment *ts, unsigned int start_x, unsigned int start_y, XImage *img, XColor fg);
       -
       -
       -#endif