ltkx

GUI toolkit for X11 (old)
git clone git://lumidify.org/ltkx.git (fast, but not encrypted)
git clone https://lumidify.org/ltkx.git (encrypted, but very slow)
git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltkx.git (over tor)
Log | Files | Refs | README | LICENSE

text_common.c (8381B)


      1 /*
      2  * This file is part of the Lumidify ToolKit (LTK)
      3  * Copyright (c) 2017, 2018, 2020 lumidify <nobody@lumidify.org>
      4  *
      5  * Permission is hereby granted, free of charge, to any person obtaining a copy
      6  * of this software and associated documentation files (the "Software"), to deal
      7  * in the Software without restriction, including without limitation the rights
      8  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      9  * copies of the Software, and to permit persons to whom the Software is
     10  * furnished to do so, subject to the following conditions:
     11  *
     12  * The above copyright notice and this permission notice shall be included in all
     13  * copies or substantial portions of the Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     21  * SOFTWARE.
     22  */
     23 
     24 #include <stdio.h>
     25 #include <stdlib.h>
     26 #include <stdint.h>
     27 #include <limits.h>
     28 #include <X11/Xlib.h>
     29 #include <X11/Xutil.h>
     30 #include "stb_truetype.h" /* http://nothings.org/stb/stb_truetype.h */
     31 #include <fontconfig/fontconfig.h>
     32 #include "khash.h"
     33 #include <fribidi.h>
     34 #include <harfbuzz/hb.h>
     35 #include <harfbuzz/hb-ot.h>
     36 #include "text_common.h"
     37 #include "ltk.h"
     38 
     39 /* These unicode routines are taken from
     40  * https://github.com/JeffBezanson/cutef8 */
     41 
     42 /* is c the start of a utf8 sequence? */
     43 #define isutf(c) (((c)&0xC0)!=0x80)
     44 
     45 static const uint32_t offsetsFromUTF8[6] = {
     46     0x00000000UL, 0x00003080UL, 0x000E2080UL,
     47     0x03C82080UL, 0xFA082080UL, 0x82082080UL
     48 };
     49 
     50 /* next character without NUL character terminator */
     51 uint32_t u8_nextmemchar(const char *s, size_t *i)
     52 {
     53     uint32_t ch = 0;
     54     size_t sz = 0;
     55     do {
     56         ch <<= 6;
     57         ch += (unsigned char)s[(*i)++];
     58         sz++;
     59     } while (!isutf(s[*i]));
     60     ch -= offsetsFromUTF8[sz-1];
     61 
     62     return ch;
     63 }
     64 
     65 /* number of characters in NUL-terminated string */
     66 size_t u8_strlen(const char *s)
     67 {
     68     size_t count = 0;
     69     size_t i = 0, lasti;
     70 
     71     while (1) {
     72         lasti = i;
     73         while (s[i] > 0)
     74             i++;
     75         count += (i-lasti);
     76         if (s[i++]==0) break;
     77         (void)(isutf(s[++i]) || isutf(s[++i]) || ++i);
     78         count++;
     79     }
     80     return count;
     81 }
     82 
     83 size_t u8_wc_toutf8(char *dest, uint32_t ch)
     84 {
     85     if (ch < 0x80) {
     86         dest[0] = (char)ch;
     87         return 1;
     88     }
     89     if (ch < 0x800) {
     90         dest[0] = (ch>>6) | 0xC0;
     91         dest[1] = (ch & 0x3F) | 0x80;
     92         return 2;
     93     }
     94     if (ch < 0x10000) {
     95         dest[0] = (ch>>12) | 0xE0;
     96         dest[1] = ((ch>>6) & 0x3F) | 0x80;
     97         dest[2] = (ch & 0x3F) | 0x80;
     98         return 3;
     99     }
    100     if (ch < 0x110000) {
    101         dest[0] = (ch>>18) | 0xF0;
    102         dest[1] = ((ch>>12) & 0x3F) | 0x80;
    103         dest[2] = ((ch>>6) & 0x3F) | 0x80;
    104         dest[3] = (ch & 0x3F) | 0x80;
    105         return 4;
    106     }
    107     return 0;
    108 }
    109 
    110 LtkTextManager *
    111 ltk_init_text(char *font_name)
    112 {
    113 	LtkTextManager *tm = malloc(sizeof(LtkTextManager));
    114 	if (!tm) {
    115 		(void)printf("Memory exhausted when trying to create text manager.");
    116 		exit(1);
    117 	}
    118 	tm->font_paths = kh_init(fontid);
    119 	tm->font_cache = kh_init(fontstruct);
    120 	tm->glyph_cache = kh_init(glyphcache);
    121 	/* FIXME: THIS REALLY SHOULD NOT BE UINT16_T! IT GETS MESSY WITH BIT-SHIFTING */
    122 	tm->font_id_cur = 1;
    123 	ltk_load_default_font(tm, font_name);
    124 
    125 	return tm;
    126 }
    127 
    128 void
    129 ltk_destroy_text_manager(LtkTextManager *tm)
    130 {
    131 	int k;
    132 
    133 	kh_destroy(fontid, tm->font_paths);
    134 
    135 	for (k = kh_begin(tm->font_cache); k != kh_end(tm->font_cache); k++) {
    136 		if (kh_exist(tm->font_cache, k)) {
    137 			ltk_destroy_font(kh_value(tm->font_cache, k));
    138 		}
    139 	}
    140 	kh_destroy(fontstruct, tm->font_cache);
    141 
    142 	for (k = kh_begin(tm->glyph_cache); k != kh_end(tm->glyph_cache); k++) {
    143 		if (kh_exist(tm->glyph_cache, k)) {
    144 			ltk_destroy_glyph_cache(kh_value(tm->glyph_cache, k));
    145 		}
    146 	}
    147 	kh_destroy(glyphcache, tm->glyph_cache);
    148 
    149 	free(tm);
    150 }
    151 
    152 LtkGlyphInfo *
    153 ltk_create_glyph_info(LtkFont *font, unsigned int id, float scale)
    154 {
    155 	LtkGlyphInfo *glyph = malloc(sizeof(LtkGlyphInfo));
    156 	if (!glyph) {
    157 		(void)printf("Out of memory!\n");
    158 		exit(1);
    159 	}
    160 
    161 	glyph->id = id;
    162 	glyph->refs = 0;
    163 	glyph->alphamap = stbtt_GetGlyphBitmap(
    164 		&font->info, scale, scale, id, &glyph->w,
    165 		&glyph->h, &glyph->xoff, &glyph->yoff
    166 	);
    167 
    168 	return glyph;
    169 }
    170 
    171 void
    172 ltk_destroy_glyph_info(LtkGlyphInfo *gi)
    173 {
    174 	free(gi->alphamap);
    175 	free(gi);
    176 }
    177 
    178 LtkGlyphInfo *
    179 ltk_get_glyph_info(LtkFont *font, unsigned int id, float scale, khash_t(glyphinfo) *cache)
    180 {
    181 	int ret;
    182 	khint_t k;
    183 	LtkGlyphInfo *glyph;
    184 	k = kh_get(glyphinfo, cache, id);
    185 	if (k == kh_end(cache)) {
    186 		glyph = ltk_create_glyph_info(font, id, scale);
    187 		/* FIXME: error checking with ret */
    188 		k = kh_put(glyphinfo, cache, id, &ret);
    189 		kh_value(cache, k) = glyph;
    190 	} else {
    191 		glyph = kh_value(cache, k);
    192 	}
    193 
    194 	return glyph;
    195 }
    196 
    197 khint_t
    198 ltk_create_glyph_cache(LtkTextManager *tm, uint16_t font_id, uint16_t font_size)
    199 {
    200 	khash_t(glyphinfo) *cache = kh_init(glyphinfo);
    201 	int ret;
    202 	khint_t k;
    203 	/* I guess I can just ignore ret for now */
    204 	k = kh_put(glyphcache, tm->glyph_cache, font_id << 16 + font_size, &ret);
    205 	kh_value(tm->glyph_cache, k) = cache;
    206 
    207 	return k;
    208 }
    209 
    210 void
    211 ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache)
    212 {
    213 	int k;
    214 	for (k = kh_begin(cache); k != kh_end(cache); k++) {
    215 		if (kh_exist(cache, k)) {
    216 			ltk_destroy_glyph_info(kh_value(cache, k));
    217 		}
    218 	}
    219 	kh_destroy(glyphinfo, cache);
    220 }
    221 
    222 void
    223 ltk_load_default_font(LtkTextManager *tm, char *name)
    224 {
    225 	FcPattern *match;
    226 	FcResult result;
    227 	char *file;
    228 	int index;
    229 	uint16_t font;
    230 
    231 	tm->fcpattern = FcNameParse(name);
    232 	FcPatternAddString(tm->fcpattern, FC_FONTFORMAT, "truetype");
    233 	FcConfigSubstitute(NULL, tm->fcpattern, FcMatchPattern);
    234 	FcDefaultSubstitute(tm->fcpattern);
    235 	match = FcFontMatch(NULL, tm->fcpattern, &result);
    236 
    237 	FcPatternGetString (match, FC_FILE, 0, (FcChar8 **) &file);
    238 	/* FIXME: Why is index never used? This is the index within the font file,
    239 	   so it might be important, although I'm not sure if stb_truetype even
    240 	   supports it */
    241 	FcPatternGetInteger (match, FC_INDEX, 0, &index);
    242 
    243 	tm->default_font = ltk_get_font(tm, file);
    244 
    245 	FcPatternDestroy (match);
    246 }
    247 
    248 LtkFont *
    249 ltk_create_font(char *path, uint16_t id)
    250 {
    251 	long len;
    252 	LtkFont *font = malloc(sizeof(LtkFont));
    253 	if (!font) {
    254 		(void)fprintf(stderr, "Out of memory!\n");
    255 		exit(1);
    256 	}
    257 	char *contents = ltk_read_file(path, &len);
    258 	if (!stbtt_InitFont(&font->info, contents, 0))
    259 	{
    260 		(void)fprintf(stderr, "Failed to load font %s\n", path);
    261 		exit(1);
    262 	}
    263 	/* FIXME: make use of the destroy function (last argument to hb_blob_create - see hb-blob.cc in harfbuzz source) */
    264 	hb_blob_t *blob = hb_blob_create(contents, len, HB_MEMORY_MODE_READONLY, NULL, NULL);
    265 	hb_face_t *face = hb_face_create(blob, 0);
    266 	/* FIXME: need to use destroy function in order for the original file data to be freed? */
    267 	hb_blob_destroy(blob);
    268 	font->hb = hb_font_create(face);
    269 	hb_face_destroy(face);
    270 	hb_ot_font_set_funcs(font->hb);
    271 	font->id = id;
    272 	font->refs = 0;
    273 
    274 	return font;
    275 }
    276 
    277 void
    278 ltk_destroy_font(LtkFont *font)
    279 {
    280 	free(font->info.data);
    281 	hb_font_destroy(font->hb);
    282 	free(font);
    283 }
    284 
    285 uint16_t
    286 ltk_load_font(LtkTextManager *tm, char *path)
    287 {
    288 	LtkFont *font = ltk_create_font(path, tm->font_id_cur++);
    289 	int ret;
    290 	khint_t k;
    291 	/* FIXME: does kh_destroy also free these copied strings properly? */
    292 	char *key = strdup(path);
    293 	k = kh_put(fontid, tm->font_paths, key, &ret);
    294 	kh_value(tm->font_paths, k) = font->id;
    295 	k = kh_put(fontstruct, tm->font_cache, (khint_t) font->id, &ret);
    296 	kh_value(tm->font_cache, k) = font;
    297 	k = kh_get(fontid, tm->font_paths, path);
    298 
    299 	return font->id;
    300 }
    301 
    302 uint16_t
    303 ltk_get_font(LtkTextManager *tm, char *path)
    304 {
    305 	int ret;
    306 	khint_t k;
    307 	uint16_t id;
    308 	k = kh_get(fontid, tm->font_paths, path);
    309 	if (k == kh_end(tm->font_paths)) {
    310 		id = ltk_load_font(tm, path);
    311 	} else {
    312 		id = kh_value(tm->font_paths, k);
    313 	}
    314 
    315 	return id;
    316 }
    317 
    318 void
    319 ltk_destroy_glyph(LtkGlyph *glyph, khash_t(glyphinfo) *cache)
    320 {
    321 	int k;
    322 	if (--glyph->info->refs < 1) {
    323 		k = kh_get(glyphinfo, cache, glyph->info->id);
    324 		kh_del(glyphinfo, cache, k);
    325 		ltk_destroy_glyph_info(glyph->info);
    326 	}
    327 	free(glyph);
    328 }