URI: 
       tFix basic 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 55009a9b2fc3477235ddf5cae6c45446e0117802
   DIR parent 38fe277a39863666574934ce221dcf9bd9ee4cad
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Sun, 13 Sep 2020 21:28:30 +0200
       
       Fix basic rendering
       
       Diffstat:
         M README.md                           |       5 ++++-
         M color.c                             |       1 +
         M config.mk                           |      12 +++++++++---
         M defs.h                              |       7 +++++++
         M draw.c                              |       4 ----
         M grid.c                              |       1 -
         M ltk.h                               |       4 ++--
         A socket_format.txt                   |      25 +++++++++++++++++++++++++
         M text.h                              |      18 +++++++++++-------
         D text_common.c                       |     452 -------------------------------
         D text_common.h                       |      66 -------------------------------
         M text_line.c                         |      30 +++++++++++++++++-------------
         M text_line.h                         |      16 ++++++++++------
         M text_pango.c                        |      32 +++++++++++++++++++++++--------
         D text_pango.h                        |      51 -------------------------------
         A text_stb.c                          |     579 +++++++++++++++++++++++++++++++
         M util.c                              |       2 ++
       
       17 files changed, 691 insertions(+), 614 deletions(-)
       ---
   DIR diff --git a/README.md b/README.md
       t@@ -3,7 +3,10 @@ Not much to see here.
        WARNING: DON'T TRY TO USE THIS! IT IS ONLY A PLACE FOR ME TO TRY OUT MY
        WILDEST FANTASIES, NOT ACTUAL WORKING CODE.
        
       -Also, it currently only works with pango, until I fix the basic text again.
       +To build with or without pango: Follow instructions in config.mk and defs.h.
       +
       +Note: The basic (non-pango) text doesn't work properly on my i386 machine
       +because it's a bit of a hack.
        
        To test:
        
   DIR diff --git a/color.c b/color.c
       t@@ -1,5 +1,6 @@
        #include <X11/Xlib.h>
        #include <X11/Xutil.h>
       +#include "defs.h"
        #include "util.h"
        #include "color.h"
        
   DIR diff --git a/config.mk b/config.mk
       t@@ -1,8 +1,14 @@
        VERSION = -999
        
       -CFLAGS = -g -std=c99 -w -fcommon -Wall -Werror -Wextra `pkg-config --cflags x11 fontconfig pangoxft` -pedantic
       -LDFLAGS = -lm `pkg-config --libs x11 fontconfig pangoxft`
       -OBJ = text_pango.o
       +CFLAGS = -g -std=c99 -w -fcommon -Wall -Werror -Wextra `pkg-config --cflags x11 fontconfig` -pedantic
       +LDFLAGS = -lm `pkg-config --libs x11 fontconfig`
       +# Comment when enabling pango rendering:
       +OBJ = stb_truetype.o text_stb.o
       +
       +# Uncomment to enable pango rendering:
       +#OBJ = text_pango.o
       +#CFLAGS += `pkg-config --cflags pangoxft`
       +#LDFLAGS += `pkg-config --libs pangoxft`
        
        # OpenBSD
        COMPATOBJ = 
   DIR diff --git a/defs.h b/defs.h
       t@@ -1,2 +1,9 @@
       +/*
       + Uncomment USE_PANGO and USE_XFT and comment USE_STB to enable pango,
       + and vice versa to enable basic rendering.
       +*/
       +/*
        #define USE_PANGO
        #define USE_XFT
       +*/
       +#define USE_STB
   DIR diff --git a/draw.c b/draw.c
       t@@ -28,10 +28,6 @@
        #include <X11/Xlib.h>
        #include <X11/Xutil.h>
        #include "util.h"
       -#include "khash.h"
       -#include "stb_truetype.h"
       -#include <fontconfig/fontconfig.h>
       -#include "text_common.h"
        #include "ltk.h"
        #include "draw.h"
        
   DIR diff --git a/grid.c b/grid.c
       t@@ -31,7 +31,6 @@
        #include <X11/Xutil.h>
        #include "util.h"
        #include "khash.h"
       -#include "text_common.h"
        #include "ltk.h"
        #include "grid.h"
        
   DIR diff --git a/ltk.h b/ltk.h
       t@@ -26,6 +26,8 @@
        
        /* Requires the following includes: <X11/Xlib.h>, <X11/Xutil.h> */
        
       +#include "khash.h"
       +
        typedef struct {
                int x;
                int y;
       t@@ -128,8 +130,6 @@ typedef struct ltk_window {
                khash_t(widget) *widget_hash;
        } ltk_window;
        
       -char *ltk_read_file(const char *path, unsigned long *len);
       -
        void ltk_window_invalidate_rect(ltk_window *window, ltk_rect rect);
        
        void ltk_fatal(const char *msg);
   DIR diff --git a/socket_format.txt b/socket_format.txt
       t@@ -0,0 +1,25 @@
       +Note: This is not implemented yet; it is just here to collect
       +my thoughts while I keep working.
       +
       +<widget type> <widget id> <command> <args>
       +> grid grd1 create 2 2
       +
       +If the command takes a string, the string may contain newlines:
       +> button btn1 create "I'm a
       +> button!"
       +
       +The command line is read until the first newline that is not
       +within a string.
       +
       +Double quotes must be escaped in strings, like so:
       +> button btn1 create "Bla\"bla"
       +
       +When the server sends a reply, the format is the same, but
       +there are some special cases, such as "get-text". When the
       +client asks to get the text for a widget, only the text is
       +sent back, but still inside double quotes, with double quotes
       +belonging to the text escaped.
       +
       +Essentially, the individual messages are separated by line
       +breaks (\n), but line breaks within strings don't break the
       +message.
   DIR diff --git a/text.h b/text.h
       t@@ -4,13 +4,17 @@
        #include "defs.h"
        #include "color.h"
        
       +typedef struct LtkTextLine LtkTextLine;
       +
       +void ltk_init_text(const char *default_font, Display *dpy, int screen, Colormap cm);
       +void ltk_cleanup_text(void);
       +LtkTextLine *ltk_text_line_create(Window window, uint16_t font_size, char *text, int width);
       +void ltk_text_line_render(LtkTextLine *tl, LtkColor *bg, LtkColor *fg);
       +void ltk_text_line_draw(LtkTextLine *tl, GC gc, int x, int y);
       +void ltk_text_line_set_width(LtkTextLine *tl, int width);
       +void ltk_text_line_get_size(LtkTextLine *tl, int *w, int *h);
       +void ltk_text_line_destroy(LtkTextLine *tl);
       +
        #ifdef USE_PANGO
          #include <pango/pangoxft.h>
       -  #include "text_pango.h"
       -#endif
       -
       -/* Basic */
       -#ifdef USE_BASIC_TEXT
       -  #include "text_common.h"
       -  #include "text_line.h"
        #endif
   DIR diff --git a/text_common.c b/text_common.c
       t@@ -1,452 +0,0 @@
       -/*
       - * This file is part of the Lumidify ToolKit (LTK)
       - * Copyright (c) 2017, 2018, 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
       - * in the Software without restriction, including without limitation the rights
       - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       - * copies of the Software, and to permit persons to whom the Software is
       - * furnished to do so, subject to the following conditions:
       - *
       - * The above copyright notice and this permission notice shall be included in all
       - * copies or substantial portions of the Software.
       - *
       - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       - * SOFTWARE.
       - */
       -
       -#include <stdio.h>
       -#include <stdlib.h>
       -#include <stdint.h>
       -#include <limits.h>
       -#include <X11/Xlib.h>
       -#include <X11/Xutil.h>
       -#include "stb_truetype.h" /* http://nothings.org/stb/stb_truetype.h */
       -#include "khash.h"
       -#include <fontconfig/fontconfig.h>
       -#include "text_common.h"
       -#include "ltk.h"
       -
       -typedef struct ltk_font {
       -        stbtt_fontinfo info;
       -        char *path;
       -        int index; /* index in font file */
       -        uint16_t id;
       -        unsigned int refs;
       -} ltk_font;
       -
       -/* Hash definitions */
       -/* glyph id -> glyph info struct */
       -KHASH_MAP_INIT_INT(glyphinfo, ltk_glyph_info*)
       -/* font path, size -> glyph cache hash */
       -KHASH_MAP_INIT_INT(glyphcache, khash_t(glyphinfo)*)
       -
       -static struct {
       -        khash_t(glyphcache) *glyph_cache;
       -        ltk_font **fonts;
       -        int num_fonts;
       -        int fonts_bufsize;
       -        FcPattern *fcpattern;
       -        ltk_font *default_font;
       -        uint16_t font_id_cur;
       -} tm = {NULL, NULL, 0, 0, NULL, NULL, 1};
       -
       -static const char *default_font;
       -
       -static void err(const char *msg);
       -static char *read_file(const char *path, unsigned long *len);
       -static ltk_font *ltk_get_font(char *path, int index);
       -static void ltk_init_text(void);
       -static ltk_glyph_info *ltk_create_glyph_info(ltk_font *font, int id,
       -    float scale);
       -static voidltk_destroy_glyph_info(ltk_glyph_info *gi);
       -static ltk_glyph_info *ltk_get_glyph_info(ltk_font *font, int id,
       -    float scale, khash_t(glyphinfo) *cache);
       -static khash_t(glyphinfo) *ltk_get_glyph_cache(uint16_t font_id,
       -    uint16_t font_size);
       -static khint_t ltk_create_glyph_cache(uint16_t font_id, uint16_t font_size);
       -static void ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache);
       -static void ltk_load_default_font(void);
       -static ltk_font *ltk_create_font(char *path, uint16_t id, int index);
       -static void ltk_destroy_font(ltk_font *font);
       -static ltk_font *ltk_load_font(char *path, int index);
       -static ltk_font *ltk_get_font(char *path, int index);
       -
       -static void
       -err(const char *msg) {
       -        perror(msg);
       -        exit(1);
       -}
       -
       -static char *
       -read_file(const char *path, unsigned long *len) {
       -        FILE *f;
       -        char *file_contents;
       -        f = fopen(path, "rb");
       -        fseek(f, 0, SEEK_END);
       -        *len = ftell(f);
       -        fseek(f, 0, SEEK_SET);
       -        file_contents = malloc(*len + 1);
       -        fread(file_contents, 1, *len, f);
       -        file_contents[*len] = '\0';
       -        fclose(f);
       -
       -        return file_contents;
       -}
       -
       -/* 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;
       -}
       -
       -void
       -ltk_init_default_font(const char *font_name) {
       -        default_font = strdup(font_name);
       -        if (!default_font) err("ltk_init_default_font");
       -}
       -
       -static void
       -ltk_init_text(void) {
       -        tm.fonts_bufsize = 1;
       -        tm.glyph_cache = kh_init(glyphcache);
       -        tm.fonts = malloc(sizeof(ltk_font *));
       -        if (!tm.fonts) err("ltk_init_text");
       -        ltk_load_default_font();
       -}
       -
       -void
       -ltk_cleanup_text(void) {
       -        if (default_font) free(default_font);
       -        for (int i = 0; i < tm.num_fonts; i++) {
       -                ltk_destroy_font(&tm.fonts[i]);
       -        }
       -        if (!tm.glyph_cache) return;
       -        for (int 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);
       -}
       -
       -static ltk_glyph_info *
       -ltk_create_glyph_info(ltk_font *font, int id, float scale) {
       -        ltk_glyph_info *glyph = malloc(sizeof(ltk_glyph_info));
       -        if (!glyph) err("ltk_create_glyph_info");
       -
       -        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;
       -}
       -
       -static void
       -ltk_destroy_glyph_info(ltk_glyph_info *gi) {
       -        free(gi->alphamap);
       -        free(gi);
       -}
       -
       -static ltk_glyph_info *
       -ltk_get_glyph_info(ltk_font *font, int id, float scale, khash_t(glyphinfo) *cache) {
       -        int ret;
       -        khint_t k;
       -        ltk_glyph_info *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);
       -        }
       -
       -        return glyph;
       -}
       -
       -static khash_t(glyphinfo) *
       -ltk_get_glyph_cache(uint16_t font_id, uint16_t font_size) {
       -        if (!tm.glyph_cache) ltk_init_text();
       -        khint_t k;
       -        uint32_t attr = (uint32_t)font_id << 16 + font_size;
       -        k = kh_get(glyphcache, tm.glyph_cache, attr);
       -        if (k == kh_end(tm.glyph_cache)) {
       -                k = ltk_create_glyph_cache(font_id, font_size);
       -        }
       -        return kh_value(tm.glyph_cache, k);
       -}
       -
       -static khint_t
       -ltk_create_glyph_cache(uint16_t font_id, uint16_t font_size) {
       -        if (!tm.glyph_cache) ltk_init_text();
       -        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;
       -
       -        return k;
       -}
       -
       -static 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));
       -                }
       -        }
       -        kh_destroy(glyphinfo, cache);
       -}
       -
       -static void
       -ltk_load_default_font(void) {
       -        FcPattern *match;
       -        FcResult result;
       -        char *file;
       -        int index;
       -        uint16_t font;
       -
       -        if (default_font)
       -                tm.fcpattern = FcNameParse(default_font);
       -        else
       -                tm.fcpattern = FcPatternCreate();
       -        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);
       -        FcPatternGetInteger(match, FC_INDEX, 0, &index);
       -
       -        tm.default_font = ltk_get_font(file, index);
       -
       -        FcPatternDestroy(match);
       -}
       -
       -static ltk_font *
       -ltk_create_font(char *path, uint16_t id, int index) {
       -        long len;
       -        ltk_font *font = malloc(sizeof(ltk_font));
       -        if (!font) err("ltk_create_font");
       -        char *contents = read_file(path, &len);
       -        /* FIXME: error checking */
       -        int offset = stbtt_GetFontOffsetForIndex(contents, index);
       -        if (!stbtt_InitFont(&font->info, contents, offset)) {
       -                (void)fprintf(stderr, "Failed to load font %s\n", path);
       -                exit(1);
       -        }
       -        font->id = id;
       -        font->refs = 0;
       -        font->index = index;
       -        font->path = strdup(path);
       -        if (!font->path) err("ltk_create_font");
       -
       -        return font;
       -}
       -
       -static void
       -ltk_destroy_font(ltk_font *font) {
       -        free(font->info.data);
       -        free(font);
       -}
       -
       -static ltk_font *
       -ltk_load_font(char *path, int index) {
       -        ltk_font *font = ltk_create_font(path, tm.font_id_cur++, index);
       -        if (tm.num_fonts == tm.fonts_bufsize) {
       -                ltk_font *new = realloc(tm.fonts, tm.fonts_bufsize * 2 * sizeof(ltk_font *));
       -                if (!new) err("ltk_load_font");
       -                tm.fonts = new;
       -                tm.fonts_bufsize *= 2;
       -        }
       -        tm.fonts[tm.num_fonts] = font;
       -        tm.num_fonts++;
       -        return font;
       -}
       -
       -static ltk_font *
       -ltk_get_font(char *path, int index) {
       -        ltk_font *font = NULL;
       -        for (int i = 0; i < tm.num_fonts; i++) {
       -                if (tm.fonts[i]->index == index &&
       -                    strcmp(tm.fonts[i]->path, path) == 0) {
       -                        font = &tm.fonts[i];
       -                        break;
       -                }
       -        }
       -        if (!font)
       -                font = ltk_load_font(path, index);
       -        return font;
       -}
       -
       -void
       -ltk_text_to_glyphs(ltk_glyph *glyphs, int num_glyphs, char *text, uint16_t font_size,
       -    int *x_min, int *y_min, int *x_max, int *y_max) {
       -        uint32_t c1, c2 = 0;
       -        int gid;
       -        int index;
       -        char *file;
       -        size_t inc = 0;
       -        int x = 0, y, kern_advance, ax;
       -        int x1_abs, x2_abs;
       -        float scale;
       -        int ascent, descent, line_gap;
       -        *x_min = INT_MAX, *x_max = INT_MIN, *y_min = INT_MAX, *y_max = INT_MIN;
       -        ltk_glyph_info *ginfo;
       -        if (!tm.default_font)
       -                ltk_init_text();
       -
       -        ltk_font *font = tm.default_font;
       -        khash_t(glyphinfo) *glyph_cache = ltk_get_glyph_cache(font->id, font_size);
       -
       -        scale = stbtt_ScaleForPixelHeight(&font->info, font_size);
       -        stbtt_GetFontVMetrics(&font->info, &ascent, &descent, &line_gap);
       -        ascent *= scale;
       -        descent *= scale;
       -
       -        c1 = u8_nextmemchar(text, &inc);
       -        for (int i = 0; i < num_glyphs; i++) {
       -                gid = stbtt_FindGlyphIndex(&font->info, c1);
       -                if (!gid) {
       -                        /* Question: Why does this not work with FcPatternDuplicate? */
       -                        FcPattern *pat = FcPatternCreate();
       -                        FcPattern *match;
       -                        FcResult result;
       -                        FcPatternAddBool(pat, FC_SCALABLE, 1);
       -                        FcConfigSubstitute(NULL, pat, FcMatchPattern);
       -                        FcDefaultSubstitute(pat);
       -                        FcCharSet *cs = FcCharSetCreate();
       -                        FcCharSetAddChar(cs, c1);
       -                        FcPatternAddCharSet(pat, FC_CHARSET, cs);
       -                        match = FcFontMatch(NULL, pat, &result);
       -                        FcPatternGetString(match, FC_FILE, 0, &file);
       -                        FcPatternGetInteger(match, FC_INDEX, 0, &index);
       -                        font = ltk_get_font(file, index);
       -                        glyph_cache = ltk_get_glyph_cache(font->id, font_size);
       -                        FcPatternDestroy(match);
       -                        FcPatternDestroy(pat);
       -                        gid = stbtt_FindGlyphIndex(&font->info, c1);
       -                        scale = stbtt_ScaleForPixelHeight(&font->info, font_size);
       -                        stbtt_GetFontVMetrics(&font->info, &ascent, &descent, &line_gap);
       -                        ascent *= scale;
       -                        descent *= scale;
       -                }
       -                ginfo = ltk_get_glyph_info(font, gid, scale, glyph_cache);
       -                ginfo->refs++;
       -                y = ascent + ginfo->yoff;
       -                x1_abs = x + ginfo->xoff;
       -
       -                glyphs[i].x = x1_abs;
       -                glyphs[i].y = y;
       -
       -                stbtt_GetGlyphHMetrics(&font->info, gid, &ax, 0);
       -                x += (int) (ax * scale);
       -                x2_abs = x;
       -
       -                glyphs[i].info = ginfo;
       -                if (x1_abs < *x_min) *x_min = x1_abs;
       -                if (y < *y_min) *y_min = y;
       -                if (x2_abs > *x_max) *x_max = x2_abs;
       -                if (y + ginfo->h > *y_max) *y_max = y + ginfo->h;
       -
       -                if (i != num_glyphs - 1) {
       -                        c2 = u8_nextmemchar(text, &inc);
       -                        kern_advance = stbtt_GetCodepointKernAdvance(&font->info, c1, c2);
       -                        x += (int) (kern_advance * scale);
       -                }
       -                c1 = c2;
       -        }
       -}
       -
       -/*
       -void
       -ltk_unref_glyph(ltk_glyph *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);
       -        }
       -}
       -
       -void
       -ltk_unref_glyphs(ltk_glyph *glyphs, int num_glyphs) {
       -        for (int i = 0; i < num_glyphs; i++)
       -                ltk_unref_glyph(&glyphs[i]);
       -}
       -*/
   DIR diff --git a/text_common.h b/text_common.h
       t@@ -1,66 +0,0 @@
       -/*
       - * This file is part of the Lumidify ToolKit (LTK)
       - * Copyright (c) 2017, 2018, 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
       - * in the Software without restriction, including without limitation the rights
       - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       - * copies of the Software, and to permit persons to whom the Software is
       - * furnished to do so, subject to the following conditions:
       - *
       - * The above copyright notice and this permission notice shall be included in all
       - * copies or substantial portions of the Software.
       - *
       - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       - * SOFTWARE.
       - */
       -
       -#ifndef _TEXT_COMMON_H_
       -#define _TEXT_COMMON_H_
       -
       -/*
       -Requires: <stdint.h>
       -*/
       -
       -typedef struct ltk_font ltk_font;
       -
       -/* Contains general info on glyphs that doesn't change regardless of the context */
       -typedef struct {
       -        int id;
       -        unsigned char *alphamap;
       -        int w;
       -        int h;
       -        int xoff; /* x offset from origin to top left corner of glyph */
       -        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. */
       -} ltk_glyph_info;
       -
       -/* Contains glyph info specific to one run of text */
       -typedef struct {
       -        ltk_glyph_info *info;
       -        int x;
       -        int y;
       -} ltk_glyph;
       -
       -uint32_t u8_nextmemchar(const char *s, size_t *i);
       -
       -size_t u8_strlen(const char *s);
       -
       -size_t u8_wc_toutf8(char *dest, uint32_t ch);
       -
       -void ltk_init_default_font(const char *font_name);
       -
       -void ltk_cleanup_text(void);
       -
       -void ltk_text_to_glyphs(ltk_glyph *glyphs, int num_glyphs, char *text,
       -    uint16_t font_size, int *x_min, int *y_min, int *x_max, int *y_max);
       -
       -#endif /* _TEXT_COMMON_H_ */
   DIR diff --git a/text_line.c b/text_line.c
       t@@ -28,20 +28,24 @@
        #include <X11/Xutil.h>
        #include "text_common.h"
        #include "text_line.h"
       +#include "util.h"
       +
       +void ltk_init_text(const char *default_font, Display *dpy, int screen, Colormap cm);
       +void ltk_cleanup_text(void);
       +
       +LtkTextLine *ltk_text_line_create(Window window, uint16_t font_size, char *text, int width);
       +void ltk_text_line_render(LtkTextLine *tl, LtkColor *bg, LtkColor *fg);
       +void ltk_text_line_draw(LtkTextLine *tl, GC gc, int x, int y);
       +void ltk_text_line_set_width(LtkTextLine *tl, int width);
       +void ltk_text_line_get_size(LtkTextLine *tl, int *w, int *h);
       +void ltk_text_line_destroy(LtkTextLine *tl);
        
       -static void err(const char *msg);
        static void ltk_text_line_create_glyphs(struct ltk_text_line *tl);
        static void ltk_text_line_draw_glyph(ltk_glyph *glyph, int xoff, int yoff,
            XImage *img, XColor fg);
        static XImage *ltk_create_ximage(Display *dpy, int w, int h, int depth,
            XColor bg);
        
       -static void
       -err(const char *msg) {
       -        perror(msg);
       -        exit(1);
       -}
       -
        static XImage *
        ltk_create_ximage(Display *dpy, int w, int h, int depth, XColor bg) {
                XImage *img = XCreateImage(dpy, CopyFromParent, depth, ZPixmap, 0, NULL, w, h, 32, 0);
       t@@ -107,7 +111,7 @@ ltk_text_line_render(
        }
        
        static void
       -ltk_text_line_create_glyphs(struct ltk_text_line *tl) {
       +ltk_text_line_create_glyphs(LtkTextLine *tl) {
                int x_min, x_max, y_min, y_max;
                ltk_text_to_glyphs(tl->glyphs, tl->glyph_len, tl->text, tl->font_size,
                    &x_min, &y_min, &x_max, &y_max);
       t@@ -118,10 +122,10 @@ ltk_text_line_create_glyphs(struct ltk_text_line *tl) {
                tl->h = y_max - y_min;
        }
        
       -struct ltk_text_line *
       -ltk_text_line_create(uint16_t font_size, char *text) {
       -        struct ltk_text_line *line = malloc(sizeof(struct ltk_text_line));
       -        if (!line) err("ltk_text_line_create");
       +LtkTextLine *
       +ltk_text_line_create(uint16_t font_size, char *text, int width) {
       +        LtkTextLine *line = malloc(sizeof(LtkTextLine));
       +        if (!line) ltk_err("ltk_text_line_create (basic)");
                line->text = text;
                line->glyph_len = u8_strlen(text);
                line->glyphs = malloc(line->glyph_len * sizeof(ltk_glyph));
       t@@ -131,7 +135,7 @@ ltk_text_line_create(uint16_t font_size, char *text) {
        }
        
        void
       -ltk_text_line_destroy(struct ltk_text_line *tl) {
       +ltk_text_line_destroy(LtkTextLine *tl) {
                free(tl->text);
                /* FIXME: Reference count glyph infos */
                free(tl->glyphs);
   DIR diff --git a/text_line.h b/text_line.h
       t@@ -29,7 +29,7 @@ Requires the following includes:
        <X11/Xlib.h>, <X11/Xutil.h>, <stdint.h>, "text_common.h",
        */
        
       -struct ltk_text_line {
       +typedef struct {
                char *text;
                ltk_glyph *glyphs;
                size_t glyph_len;
       t@@ -38,11 +38,15 @@ struct ltk_text_line {
                int h;
                int x_min;
                int y_min;
       -};
       +} LtkTextLine;
        
       -XImage *ltk_text_line_render(struct ltk_text_line *tl, Display *dpy,
       -    Window window, GC gc, Colormap colormap, XColor fg, XColor bg);
       -struct ltk_text_line *ltk_text_line_create(uint16_t font_size, char *text);
       -void ltk_text_line_destroy(struct ltk_text_line *tl);
       +void ltk_init_text(const char *default_font, Display *dpy, int screen, Colormap cm);
       +void ltk_cleanup_text(void);
       +LtkTextLine *ltk_text_line_create(Window window, uint16_t font_size, char *text, int width);
       +void ltk_text_line_render(LtkTextLine *tl, LtkColor *bg, LtkColor *fg);
       +void ltk_text_line_draw(LtkTextLine *tl, GC gc, int x, int y);
       +void ltk_text_line_set_width(LtkTextLine *tl, int width);
       +void ltk_text_line_get_size(LtkTextLine *tl, int *w, int *h);
       +void ltk_text_line_destroy(LtkTextLine *tl);
        
        #endif /* _TEXT_LINE_H_ */
   DIR diff --git a/text_pango.c b/text_pango.c
       t@@ -6,9 +6,19 @@
        #include <X11/Xutil.h>
        #include <X11/Xos.h>
        #include <pango/pangoxft.h>
       -#include "color.h"
       -#include "text_pango.h"
        #include "util.h"
       +#include "text.h"
       +
       +struct LtkTextLine {
       +        char *text;
       +        uint16_t font_size;
       +        int w;
       +        int h;
       +        Window window;
       +        PangoLayout *layout;
       +        XftDraw *draw;
       +        Pixmap pixmap;
       +};
        
        struct {
                PangoFontMap *fontmap;
       t@@ -36,25 +46,31 @@ ltk_cleanup_text(void) {
                /* FIXME: destroy fontmap and context */
        }
        
       +void
       +ltk_text_line_set_width(LtkTextLine *tl, int width) {
       +        /* TODO: Implement! */
       +}
       +
        LtkTextLine *
        ltk_text_line_create(Window window, uint16_t font_size, char *text, int width) {
                if (!tm.context)
                        ltk_err("ltk_text_line_create (pango): text not initialized yet");
       -        /* FIXME: respect font size */
       -        /*
       -        PangoFontDescription *desc = pango_font_description_from_string("Sans Bold 27");
       -        pango_layout_set_font_description(layout, desc);
       -        pango_font_description_free(desc);
       -        */
                LtkTextLine *line = malloc(sizeof(LtkTextLine));
                if (!line) ltk_err("ltk_text_line_create (pango)");
                line->text = text;
                line->font_size = font_size;
                line->layout = pango_layout_new(tm.context);
       +
                if (width > 0) {
                        pango_layout_set_width(line->layout, width * PANGO_SCALE);
                        pango_layout_set_wrap(line->layout, PANGO_WRAP_WORD_CHAR);
                }
       +
       +        PangoFontDescription *desc = pango_font_description_from_string(tm.default_font);
       +        pango_font_description_set_size(desc, font_size * PANGO_SCALE);
       +        pango_layout_set_font_description(line->layout, desc);
       +        pango_font_description_free(desc);
       +
                pango_layout_set_text(line->layout, text, -1);
                pango_layout_get_size(line->layout, &line->w, &line->h);
                line->w /= PANGO_SCALE;
   DIR diff --git a/text_pango.h b/text_pango.h
       t@@ -1,51 +0,0 @@
       -/*
       - * This file is part of the Lumidify ToolKit (LTK)
       - * 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
       - * in the Software without restriction, including without limitation the rights
       - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       - * copies of the Software, and to permit persons to whom the Software is
       - * furnished to do so, subject to the following conditions:
       - *
       - * The above copyright notice and this permission notice shall be included in all
       - * copies or substantial portions of the Software.
       - *
       - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       - * SOFTWARE.
       - */
       -
       -#ifndef _TEXT_PANGO_H_
       -#define _TEXT_PANGO_H_
       -
       -/*
       -Requires the following includes:
       -<X11/Xlib.h>, <X11/Xutil.h>, <stdint.h>, <pango/pangoxft.h>
       -*/
       -
       -typedef struct {
       -        char *text;
       -        uint16_t font_size;
       -        int w;
       -        int h;
       -        Window window;
       -        PangoLayout *layout;
       -        XftDraw *draw;
       -        Pixmap pixmap;
       -} LtkTextLine;
       -
       -void ltk_init_text(const char *default_font, Display *dpy, int screen, Colormap cm);
       -void ltk_cleanup_text(void);
       -LtkTextLine *ltk_text_line_create(Window window, uint16_t font_size, char *text, int width);
       -void ltk_text_line_render(LtkTextLine *tl, LtkColor *bg, LtkColor *fg);
       -void ltk_text_line_draw(LtkTextLine *tl, GC gc, int x, int y);
       -void ltk_text_line_get_size(LtkTextLine *tl, int *w, int *h);
       -void ltk_text_line_destroy(LtkTextLine *tl);
       -
       -#endif /* _TEXT_PANGO_H_ */
   DIR diff --git a/text_stb.c b/text_stb.c
       t@@ -0,0 +1,579 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2017, 2018, 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
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <stdint.h>
       +#include <limits.h>
       +#include <X11/Xlib.h>
       +#include <X11/Xutil.h>
       +#include "stb_truetype.h" /* http://nothings.org/stb/stb_truetype.h */
       +#include "khash.h"
       +#include <fontconfig/fontconfig.h>
       +#include "util.h"
       +#include "text.h"
       +#include "ltk.h"
       +
       +typedef struct {
       +        stbtt_fontinfo info;
       +        char *path;
       +        int index; /* index in font file */
       +        uint16_t id;
       +        unsigned int refs;
       +} LtkFont;
       +
       +/* Contains general info on glyphs that doesn't change regardless of the context */
       +typedef struct {
       +        int id;
       +        unsigned char *alphamap;
       +        int w;
       +        int h;
       +        int xoff; /* x offset from origin to top left corner of glyph */
       +        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;
       +
       +/* Contains glyph info specific to one run of text */
       +typedef struct {
       +        LtkGlyphInfo *info;
       +        int x;
       +        int y;
       +} LtkGlyph;
       +
       +struct LtkTextLine {
       +        Window window;
       +        XImage *img;
       +        char *text;
       +        LtkGlyph *glyphs;
       +        size_t glyph_len;
       +        uint16_t font_size;
       +        int w;
       +        int h;
       +        int x_min;
       +        int y_min;
       +};
       +
       +/* 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)*)
       +
       +static struct {
       +        khash_t(glyphcache) *glyph_cache;
       +        LtkFont **fonts;
       +        int num_fonts;
       +        int fonts_bufsize;
       +        FcPattern *fcpattern;
       +        LtkFont *default_font;
       +        uint16_t font_id_cur;
       +        Display *dpy;
       +        int screen;
       +        Colormap cm;
       +} tm = {NULL, NULL, 0, 0, NULL, NULL, 1, NULL, 0, 0};
       +
       +
       +static LtkFont *ltk_get_font(char *path, int index);
       +static LtkGlyphInfo *ltk_create_glyph_info(LtkFont *font, int id,
       +    float scale);
       +static void ltk_destroy_glyph_info(LtkGlyphInfo *gi);
       +static LtkGlyphInfo *ltk_get_glyph_info(LtkFont *font, int id,
       +    float scale, khash_t(glyphinfo) *cache);
       +static khash_t(glyphinfo) *ltk_get_glyph_cache(uint16_t font_id,
       +    uint16_t font_size);
       +static khint_t ltk_create_glyph_cache(uint16_t font_id, uint16_t font_size);
       +static void ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache);
       +static void ltk_load_default_font(char *name);
       +static LtkFont *ltk_create_font(char *path, uint16_t id, int index);
       +static void ltk_destroy_font(LtkFont *font);
       +static LtkFont *ltk_load_font(char *path, int index);
       +static LtkFont *ltk_get_font(char *path, int index);
       +static void ltk_text_to_glyphs(LtkGlyph *glyphs, int num_glyphs, char *text,
       +    uint16_t font_size, int *x_min, int *y_min, int *x_max, int *y_max);
       +static void ltk_text_line_create_glyphs(LtkTextLine *tl);
       +static void ltk_text_line_draw_glyph(LtkGlyph *glyph, int xoff, int yoff,
       +    XImage *img, XColor fg);
       +static XImage *ltk_create_ximage(int w, int h, int depth, XColor bg);
       +
       +
       +/* These unicode routines are taken from
       + * https://github.com/JeffBezanson/cutef8 */
       +
       +static size_t u8_wc_toutf8(char *dest, uint32_t ch);
       +static size_t u8_strlen(const char *s);
       +static uint32_t u8_nextmemchar(const char *s, size_t *i);
       +
       +/* 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 */
       +static 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 */
       +static 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;
       +}
       +
       +static 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;
       +}
       +
       +void
       +ltk_init_text(const char *default_font, Display *dpy, int screen, Colormap cm) {
       +        tm.fonts_bufsize = 1;
       +        tm.glyph_cache = kh_init(glyphcache);
       +        tm.fonts = malloc(sizeof(LtkFont *));
       +        if (!tm.fonts) ltk_err("ltk_init_text");
       +        ltk_load_default_font(default_font);
       +        tm.dpy = dpy;
       +        tm.screen = screen;
       +        tm.cm = cm;
       +}
       +
       +void
       +ltk_cleanup_text(void) {
       +        for (int i = 0; i < tm.num_fonts; i++) {
       +                ltk_destroy_font(&tm.fonts[i]);
       +        }
       +        if (!tm.glyph_cache) return;
       +        for (int 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);
       +}
       +
       +static LtkGlyphInfo *
       +ltk_create_glyph_info(LtkFont *font, int id, float scale) {
       +        LtkGlyphInfo *glyph = malloc(sizeof(LtkGlyphInfo));
       +        if (!glyph) ltk_err("ltk_create_glyph_info");
       +
       +        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;
       +}
       +
       +static void
       +ltk_destroy_glyph_info(LtkGlyphInfo *gi) {
       +        free(gi->alphamap);
       +        free(gi);
       +}
       +
       +static LtkGlyphInfo *
       +ltk_get_glyph_info(LtkFont *font, 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);
       +        }
       +
       +        return glyph;
       +}
       +
       +static khash_t(glyphinfo) *
       +ltk_get_glyph_cache(uint16_t font_id, uint16_t font_size) {
       +        khint_t k;
       +        uint32_t attr = (uint32_t)font_id << 16 + font_size;
       +        k = kh_get(glyphcache, tm.glyph_cache, attr);
       +        if (k == kh_end(tm.glyph_cache)) {
       +                k = ltk_create_glyph_cache(font_id, font_size);
       +        }
       +        return kh_value(tm.glyph_cache, k);
       +}
       +
       +static khint_t
       +ltk_create_glyph_cache(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;
       +
       +        return k;
       +}
       +
       +static 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));
       +                }
       +        }
       +        kh_destroy(glyphinfo, cache);
       +}
       +
       +static void
       +ltk_load_default_font(char *name) {
       +        FcPattern *match;
       +        FcResult result;
       +        char *file;
       +        int index;
       +        uint16_t font;
       +
       +        tm.fcpattern = FcNameParse(name);
       +        /*tm.fcpattern = FcPatternCreate();*/
       +        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);
       +        FcPatternGetInteger(match, FC_INDEX, 0, &index);
       +
       +        tm.default_font = ltk_get_font(file, index);
       +
       +        FcPatternDestroy(match);
       +}
       +
       +static LtkFont *
       +ltk_create_font(char *path, uint16_t id, int index) {
       +        unsigned long len;
       +        LtkFont *font = malloc(sizeof(LtkFont));
       +        if (!font) ltk_err("ltk_create_font (stb)");
       +        char *contents = ltk_read_file(path, &len);
       +        /* FIXME: error checking */
       +        int offset = stbtt_GetFontOffsetForIndex(contents, index);
       +        if (!stbtt_InitFont(&font->info, contents, offset)) {
       +                (void)fprintf(stderr, "Failed to load font %s\n", path);
       +                exit(1);
       +        }
       +        font->id = id;
       +        font->refs = 0;
       +        font->index = index;
       +        font->path = strdup(path);
       +        if (!font->path) ltk_err("ltk_create_font");
       +
       +        return font;
       +}
       +
       +static void
       +ltk_destroy_font(LtkFont *font) {
       +        /* FIXME: why does this cause error? */
       +        free(font->info.data);
       +        free(font);
       +}
       +
       +static LtkFont *
       +ltk_load_font(char *path, int index) {
       +        LtkFont *font = ltk_create_font(path, tm.font_id_cur++, index);
       +        if (tm.num_fonts == tm.fonts_bufsize) {
       +                LtkFont *new = realloc(tm.fonts, tm.fonts_bufsize * 2 * sizeof(LtkFont *));
       +                if (!new) ltk_err("ltk_load_font");
       +                tm.fonts = new;
       +                tm.fonts_bufsize *= 2;
       +        }
       +        tm.fonts[tm.num_fonts] = font;
       +        tm.num_fonts++;
       +        return font;
       +}
       +
       +static LtkFont *
       +ltk_get_font(char *path, int index) {
       +        LtkFont *font = NULL;
       +        for (int i = 0; i < tm.num_fonts; i++) {
       +                if (tm.fonts[i]->index == index &&
       +                    strcmp(tm.fonts[i]->path, path) == 0) {
       +                        font = &tm.fonts[i];
       +                        break;
       +                }
       +        }
       +        if (!font)
       +                font = ltk_load_font(path, index);
       +        return font;
       +}
       +
       +static void
       +ltk_text_to_glyphs(LtkGlyph *glyphs, int num_glyphs, char *text, uint16_t font_size,
       +    int *x_min, int *y_min, int *x_max, int *y_max) {
       +        uint32_t c1, c2 = 0;
       +        int gid;
       +        int index;
       +        char *file;
       +        size_t inc = 0;
       +        int x = 0, y, kern_advance, ax;
       +        int x1_abs, x2_abs;
       +        float scale;
       +        int ascent, descent, line_gap;
       +        *x_min = INT_MAX, *x_max = INT_MIN, *y_min = INT_MAX, *y_max = INT_MIN;
       +        LtkGlyphInfo *ginfo;
       +
       +        LtkFont *font = tm.default_font;
       +        khash_t(glyphinfo) *glyph_cache = ltk_get_glyph_cache(font->id, font_size);
       +
       +        scale = stbtt_ScaleForPixelHeight(&font->info, font_size);
       +        stbtt_GetFontVMetrics(&font->info, &ascent, &descent, &line_gap);
       +        ascent *= scale;
       +        descent *= scale;
       +
       +        c1 = u8_nextmemchar(text, &inc);
       +        for (int i = 0; i < num_glyphs; i++) {
       +                gid = stbtt_FindGlyphIndex(&font->info, c1);
       +                if (!gid) {
       +                        /* Question: Why does this not work with FcPatternDuplicate? */
       +                        FcPattern *pat = FcPatternCreate();
       +                        FcPattern *match;
       +                        FcResult result;
       +                        FcPatternAddBool(pat, FC_SCALABLE, 1);
       +                        FcConfigSubstitute(NULL, pat, FcMatchPattern);
       +                        FcDefaultSubstitute(pat);
       +                        FcCharSet *cs = FcCharSetCreate();
       +                        FcCharSetAddChar(cs, c1);
       +                        FcPatternAddCharSet(pat, FC_CHARSET, cs);
       +                        match = FcFontMatch(NULL, pat, &result);
       +                        FcPatternGetString(match, FC_FILE, 0, &file);
       +                        FcPatternGetInteger(match, FC_INDEX, 0, &index);
       +                        font = ltk_get_font(file, index);
       +                        glyph_cache = ltk_get_glyph_cache(font->id, font_size);
       +                        FcPatternDestroy(match);
       +                        FcPatternDestroy(pat);
       +                        gid = stbtt_FindGlyphIndex(&font->info, c1);
       +                        scale = stbtt_ScaleForPixelHeight(&font->info, font_size);
       +                        stbtt_GetFontVMetrics(&font->info, &ascent, &descent, &line_gap);
       +                        ascent *= scale;
       +                        descent *= scale;
       +                }
       +                ginfo = ltk_get_glyph_info(font, gid, scale, glyph_cache);
       +                ginfo->refs++;
       +                y = ascent + ginfo->yoff;
       +                x1_abs = x + ginfo->xoff;
       +
       +                glyphs[i].x = x1_abs;
       +                glyphs[i].y = y;
       +
       +                stbtt_GetGlyphHMetrics(&font->info, gid, &ax, 0);
       +                x += (int) (ax * scale);
       +                x2_abs = x;
       +
       +                glyphs[i].info = ginfo;
       +                if (x1_abs < *x_min) *x_min = x1_abs;
       +                if (y < *y_min) *y_min = y;
       +                if (x2_abs > *x_max) *x_max = x2_abs;
       +                if (y + ginfo->h > *y_max) *y_max = y + ginfo->h;
       +
       +                if (i != num_glyphs - 1) {
       +                        c2 = u8_nextmemchar(text, &inc);
       +                        kern_advance = stbtt_GetCodepointKernAdvance(&font->info, c1, c2);
       +                        x += (int) (kern_advance * scale);
       +                }
       +                c1 = c2;
       +        }
       +}
       +
       +/*
       +void
       +ltk_unref_glyph(ltk_glyph *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);
       +        }
       +}
       +
       +void
       +ltk_unref_glyphs(ltk_glyph *glyphs, int num_glyphs) {
       +        for (int i = 0; i < num_glyphs; i++)
       +                ltk_unref_glyph(&glyphs[i]);
       +}
       +*/
       +
       +/* FIXME: Error checking that tm has been initialized */
       +
       +static XImage *
       +ltk_create_ximage(int w, int h, int depth, XColor bg) {
       +        XImage *img = XCreateImage(tm.dpy, CopyFromParent, depth, ZPixmap, 0, NULL, w, h, 32, 0);
       +        img->data = calloc(img->bytes_per_line, img->height);
       +        XInitImage(img);
       +
       +        int b;
       +        for (int i = 0; i < h; i++) {
       +                b = img->bytes_per_line * i;
       +                for (int j = 0; j < w; j++) {
       +                        img->data[b++] = bg.blue / 257;
       +                        img->data[b++] = bg.green / 257;
       +                        img->data[b++] = bg.red / 257;
       +                        b++;
       +                }
       +        }
       +
       +        return img;
       +}
       +
       +/* based on http://codemadness.org/git/dwm-font/file/drw.c.html#l315 */
       +static void
       +ltk_text_line_draw_glyph(LtkGlyph *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 (y + i >= img->height || x + j >= img->width ||
       +                            y + i < 0 || x + i < 0)
       +                                continue;
       +                        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;
       +                        img->data[b + 2] = (fg.red * a + (1 - a) * (uint16_t)img->data[b + 2] * 257) / 257;
       +                }
       +        }
       +}
       +
       +void
       +ltk_text_line_render(
       +        LtkTextLine *tl,
       +        LtkColor *bg,
       +        LtkColor *fg)
       +{
       +        LtkGlyph *glyph;
       +
       +        XWindowAttributes attrs;
       +        XGetWindowAttributes(tm.dpy, tl->window, &attrs);
       +        int depth = attrs.depth;
       +        /* FIXME: pass old image; if it has same dimensions, just clear it */
       +        if (tl->img)
       +                XDestroyImage(tl->img);
       +        tl->img = ltk_create_ximage(tl->w, tl->h, depth, bg->xcolor);
       +        for (int i = 0; i < tl->glyph_len; i++) {
       +                ltk_text_line_draw_glyph(&tl->glyphs[i], -tl->x_min, -tl->y_min, tl->img, fg->xcolor);
       +        }
       +}
       +
       +/* FIXME: error checking if img is rendered yet, tm initialized, etc. */
       +void
       +ltk_text_line_draw(LtkTextLine *tl, GC gc, int x, int y) {
       +        XPutImage(tm.dpy, tl->window, gc, tl->img, 0, 0, x, y, tl->w, tl->h);
       +}
       +
       +void
       +ltk_text_line_set_width(LtkTextLine *tl, int width) {
       +        /* FIXME: implement */
       +}
       +
       +void
       +ltk_text_line_get_size(LtkTextLine *tl, int *w, int *h) {
       +        *w = tl->w;
       +        *h = tl->h;
       +}
       +
       +static void
       +ltk_text_line_create_glyphs(LtkTextLine *tl) {
       +        int x_min, x_max, y_min, y_max;
       +        ltk_text_to_glyphs(tl->glyphs, tl->glyph_len, tl->text, tl->font_size,
       +            &x_min, &y_min, &x_max, &y_max);
       +        /* 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;
       +}
       +
       +LtkTextLine *
       +ltk_text_line_create(Window window, uint16_t font_size, char *text, int width) {
       +        LtkTextLine *line = malloc(sizeof(LtkTextLine));
       +        if (!line) ltk_err("ltk_text_line_create (basic)");
       +        line->window = window;
       +        line->img = NULL;
       +        line->text = text;
       +        line->glyph_len = u8_strlen(text);
       +        line->glyphs = malloc(line->glyph_len * sizeof(LtkGlyph));
       +        line->font_size = font_size;
       +        ltk_text_line_create_glyphs(line);
       +        return line;
       +}
       +
       +void
       +ltk_text_line_destroy(LtkTextLine *tl) {
       +        free(tl->text);
       +        /* FIXME: Reference count glyph infos */
       +        free(tl->glyphs);
       +        free(tl);
       +}
   DIR diff --git a/util.c b/util.c
       t@@ -22,6 +22,7 @@
         */
        
        #include <stdio.h>
       +#include <stdlib.h>
        
        void
        ltk_err(const char *msg) {
       t@@ -37,6 +38,7 @@ ltk_read_file(const char *path, unsigned long *len) {
                fseek(f, 0, SEEK_END);
                *len = ftell(f);
                fseek(f, 0, SEEK_SET);
       +        /* FIXME: error checking */
                file_contents = malloc(*len + 1);
                fread(file_contents, 1, *len, f);
                file_contents[*len] = '\0';