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_buffer.c (30537B)


      1 /*
      2  * This file is part of the Lumidify ToolKit (LTK)
      3  * Copyright (c) 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 "array.h"
     38 #include "text_buffer.h"
     39 #include "ltk.h"
     40 
     41 LTK_ARRAY_INIT_IMPL(uint32, uint32_t)
     42 LTK_ARRAY_INIT_IMPL(script, hb_script_t)
     43 LTK_ARRAY_INIT_IMPL(level, FriBidiLevel)
     44 LTK_ARRAY_INIT_IMPL(int, int)
     45 LTK_ARRAY_INIT_IMPL(line, struct ltk_soft_line *)
     46 
     47 void
     48 ltk_soft_line_destroy(struct ltk_soft_line *sl) {
     49 	if (sl->glyph_pos)
     50 		ltk_array_destroy_int(sl->glyph_pos);
     51 	if (sl->glyph_clusters)
     52 		ltk_array_destroy_uint32(sl->glyph_clusters);
     53 	free(sl);
     54 }
     55 
     56 struct ltk_soft_line *
     57 ltk_soft_line_create(void) {
     58 	struct ltk_soft_line *sl = malloc(sizeof(struct ltk_soft_line));
     59 	if (!sl) {
     60 		(void)fprintf(stderr, "Error creating soft line\n");
     61 		exit(1);
     62 	}
     63 	sl->glyph_pos = NULL;
     64 	sl->glyph_clusters = NULL;
     65 	return sl;
     66 }
     67 
     68 static void
     69 ltk_text_line_cleanup_soft_lines(struct ltk_array_line *soft_lines, int old_len) {
     70 	/* remove old soft lines that aren't needed anymore */
     71 	for (int i = soft_lines->len; i < old_len; i++) {
     72 		ltk_soft_line_destroy(soft_lines->buf[i]);
     73 	}
     74 	ltk_array_resize_line(soft_lines, soft_lines->len);
     75 }
     76 
     77 void
     78 ltk_text_line_wrap(struct ltk_text_line *tl, int max_width) {
     79 	tl->w_wrapped = max_width;
     80 	int old_len = tl->soft_lines->len;
     81 	tl->soft_lines->len = 0;
     82 	int par_is_rtl = tl->dir == HB_DIRECTION_RTL;
     83 
     84 	struct ltk_text_run *cur = par_is_rtl ? tl->last_run : tl->first_run;
     85 	LtkGlyph *glyph;
     86 	int cur_index = 0;
     87 	struct ltk_soft_line *sl = old_len > cur_index ? tl->soft_lines->buf[cur_index] : ltk_soft_line_create();
     88 	sl->glyph_index = cur->dir == HB_DIRECTION_RTL ? cur->num_glyphs - 1 : 0;
     89 	sl->run = cur;
     90 	sl->len = 0;
     91 	sl->w = 0;
     92 	ltk_array_append_line(tl->soft_lines, sl);
     93 	if (max_width == -1) {
     94 		cur = tl->first_run;
     95 		while (cur) {
     96 			sl->len += cur->num_glyphs;
     97 			sl->w += cur->w;
     98 			cur = cur->next;
     99 		}
    100 		ltk_text_line_cleanup_soft_lines(tl->soft_lines, old_len);
    101 		tl->w_wrapped = tl->w;
    102 		tl->h_wrapped = tl->h;
    103 		return;
    104 	}
    105 	int last_linebreak = par_is_rtl ? tl->w : 0;
    106 	int cur_start = 0;
    107 	/* FIXME: also calculate max height of each line */
    108 	/* Note: 0x20 is space */
    109 	/* Note: No, this doesn't do proper Unicode linebreaking */
    110 	/* Note: This is probably buggy */
    111 	while (cur) {
    112 		if (sl->w + cur->w <= max_width) {
    113 			sl->w += cur->w;
    114 			sl->len += cur->num_glyphs;
    115 			cur = par_is_rtl ? cur->last : cur->next;
    116 			continue;
    117 		}
    118 		if (cur->dir == HB_DIRECTION_RTL) {
    119 			cur_start = cur->glyphs[cur->num_glyphs - 1].x_abs + cur->glyphs[cur->num_glyphs - 1].x_advance;
    120 			int i = cur->num_glyphs - 1;
    121 			/* This is needed to properly break a run over multiple lines.
    122 			   We can't just reuse sl->glyph_index because that might be
    123 			   located in another run */
    124 			int cur_start_index = cur->num_glyphs - 1;
    125 			while (i >= 0) {
    126 				glyph = &cur->glyphs[i];
    127 				int cur_w = sl->w + cur_start - glyph->x_abs;
    128 				if (cur_w > max_width) {
    129 					int char_break = -1;
    130 					for (int j = i; j < cur->num_glyphs; j++) {
    131 						if (char_break == -1 && cur->glyphs[j].cluster != glyph->cluster)
    132 							char_break = j;
    133 						if ((j != i && tl->log_buf->buf[cur->glyphs[j].cluster] == 0x20) ||
    134 						    j == cur_start_index || sl->len == 0) {
    135 							if (j == cur_start_index &&
    136 							    tl->log_buf->buf[cur->glyphs[j].cluster] != 0x20) {
    137 								if (sl->len == 0) {
    138 									char_break = char_break == -1 ? j : char_break;
    139 									i = char_break - 1;
    140 									last_linebreak = cur->glyphs[char_break].x_abs;
    141 									sl->len += j - char_break + 1;
    142 								} else {
    143 									i = j;
    144 									last_linebreak = cur_start;
    145 								}
    146 							} else {
    147 								i = j - 1;
    148 								last_linebreak = cur->glyphs[j].x_abs;
    149 								sl->len++;
    150 							}
    151 							sl->w += cur_start - last_linebreak;
    152 							cur_start = last_linebreak;
    153 							cur_index++;
    154 							sl = old_len > cur_index ?
    155 							    tl->soft_lines->buf[cur_index] :
    156 							    ltk_soft_line_create();
    157 							sl->glyph_index = i;
    158 							cur_start_index = i;
    159 							sl->run = cur;
    160 							sl->len = 0;
    161 							sl->w = 0;
    162 							ltk_array_append_line(tl->soft_lines, sl);
    163 							break;
    164 						} else {
    165 							sl->len--;
    166 						}
    167 					}
    168 				} else {
    169 					sl->len++;
    170 					i--;
    171 				}
    172 			}
    173 			if (sl->run == cur)
    174 				sl->w = last_linebreak - cur->glyphs[0].x_abs;
    175 			else
    176 				sl->w += cur->w;
    177 		} else {
    178 			cur_start = cur->glyphs[0].x_abs;
    179 			int i = 0;
    180 			int cur_start_index = 0;
    181 			while (i < cur->num_glyphs) {
    182 				glyph = &cur->glyphs[i];
    183 				/* FIXME: This uses x_advance instead of glyph width so it works correctly
    184 				   together with the current shaping, but should it maybe take the largest
    185 				   of the two? What if the glyph width is actually larger than x_advance? */
    186 				int cur_w = sl->w + glyph->x_abs + glyph->x_advance - cur_start;
    187 				if (cur_w > max_width) {
    188 					int char_break = -1;
    189 					for (int j = i; j >= 0; j--) {
    190 						if (char_break == -1 && cur->glyphs[j].cluster != glyph->cluster)
    191 							char_break = j;
    192 						if ((j != i && tl->log_buf->buf[cur->glyphs[j].cluster] == 0x20) ||
    193 						    j == cur_start_index || sl->len == 0) {
    194 							if (j == cur_start_index &&
    195 							    tl->log_buf->buf[cur->glyphs[j].cluster] != 0x20) {
    196 								if (sl->len == 0) {
    197 									char_break = char_break == -1 ? j : char_break;
    198 									i = char_break + 1;
    199 									last_linebreak = cur->glyphs[char_break + 1].x_abs;
    200 									sl->len += char_break - j + 1;
    201 								} else {
    202 									i = j;
    203 									last_linebreak = cur_start;
    204 								}
    205 							} else {
    206 								i = j + 1;
    207 								last_linebreak = cur->glyphs[j + 1].x_abs;
    208 								sl->len++;
    209 							}
    210 							sl->w += last_linebreak - cur_start;
    211 							cur_start = last_linebreak;
    212 							cur_index++;
    213 							sl = old_len > cur_index ?
    214 							    tl->soft_lines->buf[cur_index] :
    215 							    ltk_soft_line_create();
    216 							sl->glyph_index = i;
    217 							cur_start_index = i;
    218 							sl->run = cur;
    219 							sl->len = 0;
    220 							sl->w = 0;
    221 							ltk_array_append_line(tl->soft_lines, sl);
    222 							break;
    223 						} else {
    224 							sl->len--;
    225 						}
    226 					}
    227 				} else {
    228 					sl->len++;
    229 					i++;
    230 				}
    231 
    232 			}
    233 			if (sl->run == cur)
    234 				sl->w = cur->glyphs[cur->num_glyphs - 1].x_abs + cur->glyphs[cur->num_glyphs - 1].x_advance - last_linebreak;
    235 			else
    236 				sl->w += cur->w;
    237 		}
    238 		cur = par_is_rtl ? cur->last : cur->next;
    239 	}
    240 	ltk_text_line_cleanup_soft_lines(tl->soft_lines, old_len);
    241 	/* if it fits on one line, don't waste all the space to the end of
    242 	   the line, just use the actual width of the text */
    243 	if (tl->soft_lines->len == 1)
    244 		tl->w_wrapped = tl->soft_lines->buf[0]->w;
    245 	else
    246 		tl->w_wrapped = max_width;
    247 	tl->h_wrapped = tl->soft_lines->len * tl->h;
    248 }
    249 
    250 XImage *
    251 ltk_create_ximage(Display *dpy, int w, int h, int depth, XColor bg) {
    252 	XImage *img = XCreateImage(dpy, CopyFromParent, depth, ZPixmap, 0, NULL, w, h, 32, 0);
    253 	img->data = calloc(img->bytes_per_line, img->height);
    254 	XInitImage(img);
    255 
    256 	int b;
    257 	for (int i = 0; i < h; i++) {
    258 		b = img->bytes_per_line * i;
    259 		for (int j = 0; j < w; j++) {
    260 			img->data[b++] = bg.blue / 257;
    261 			img->data[b++] = bg.green / 257;
    262 			img->data[b++] = bg.red / 257;
    263 			b++;
    264 		}
    265 	}
    266 
    267 	return img;
    268 }
    269 
    270 /* based on http://codemadness.org/git/dwm-font/file/drw.c.html#l315 */
    271 void
    272 ltk_soft_line_draw_glyph(LtkGlyph *glyph, XImage *img, struct ltk_soft_line *sl, int x, int y, XColor fg) {
    273 	ltk_array_append_int(sl->glyph_pos, x);
    274 	ltk_array_append_uint32(sl->glyph_clusters, glyph->cluster);
    275 	double a;
    276 	int b;
    277 	for (int i = 0; i < glyph->info->h; i++) {
    278 		for (int j = 0; j < glyph->info->w; j++) {
    279 			if (y + i >= img->height || x + j >= img->width || y + i < 0 || x + i < 0)
    280 				continue;
    281 			b = (y + i) * img->bytes_per_line + (x + j) * 4;
    282 			a = glyph->info->alphamap[i * glyph->info->w + j] / 255.0;
    283 			img->data[b] = (fg.blue * a + (1 - a) * (uint16_t)img->data[b] * 257) / 257;
    284 			img->data[b + 1] = (fg.green * a + (1 - a) * (uint16_t)img->data[b + 1] * 257) / 257;
    285 			img->data[b + 2] = (fg.red * a + (1 - a) * (uint16_t)img->data[b + 2] * 257) / 257;
    286 		}
    287 	}
    288 }
    289 
    290 /* FIXME: the glyph drawing function sometimes receives negative x positions */
    291 /* FIXME: Fix memory leaks... */
    292 XImage *
    293 ltk_text_line_render(
    294 	struct ltk_text_line *tl,
    295 	Display *dpy,
    296 	Window window,
    297 	GC gc,
    298 	Colormap colormap,
    299 	XColor fg,
    300 	XColor bg)
    301 {
    302 	LtkGlyph *glyph;
    303 	int par_is_rtl = tl->dir == HB_DIRECTION_RTL;
    304 
    305 	XWindowAttributes attrs;
    306 	XGetWindowAttributes(dpy, window, &attrs);
    307 	int depth = attrs.depth;
    308 	/* FIXME: pass old image; if it has same dimensions, just clear it */
    309 	XImage *img = ltk_create_ximage(dpy, tl->w_wrapped, tl->h_wrapped, depth, bg);
    310 
    311 	for (int i = 0; i < tl->soft_lines->len; i++) {
    312 		struct ltk_soft_line *sl = tl->soft_lines->buf[i];
    313 		/* FIXME: allow to disable this if selection isn't needed */
    314 		if (!sl->glyph_pos)
    315 			sl->glyph_pos = ltk_array_create_int(tl->len);
    316 		else
    317 			sl->glyph_pos->len = 0;
    318 		if (!sl->glyph_clusters)
    319 			sl->glyph_clusters = ltk_array_create_uint32(tl->len);
    320 		else
    321 			sl->glyph_clusters->len = 0;
    322 		struct ltk_text_run *cur = sl->run;
    323 		size_t cur_len = 0;
    324 		int cur_border = par_is_rtl ? sl->w : 0;
    325 		while (cur && cur_len < sl->len) {
    326 			int start_index;
    327 			if (cur->dir == HB_DIRECTION_RTL) {
    328 				start_index = cur == sl->run ? sl->glyph_index : cur->num_glyphs - 1;
    329 				int local_border = cur->glyphs[start_index].x_abs + cur->glyphs[start_index].x_advance;
    330 				int end_index;
    331 				if (start_index + 1 < sl->len - cur_len)
    332 					end_index = 0;
    333 				else
    334 					end_index = start_index - (sl->len - cur_len) + 1;
    335 				if (par_is_rtl) {
    336 					for (int j = start_index; j >= 0 && cur_len < sl->len; j--) {
    337 						cur_len++;
    338 						int x = cur_border - (local_border - cur->glyphs[j].x_abs) + img->width - sl->w;
    339 						ltk_soft_line_draw_glyph(&cur->glyphs[j], img, sl, x, cur->glyphs[j].y_abs + tl->h * i, fg);
    340 					}
    341 					cur_border -= local_border - cur->glyphs[end_index].x_abs;
    342 				} else {
    343 					for (int j = end_index; j <= start_index && cur_len < sl->len; j++) {
    344 						cur_len++;
    345 						int x = cur_border + (cur->glyphs[j].x_abs - cur->glyphs[end_index].x_abs);
    346 						ltk_soft_line_draw_glyph(&cur->glyphs[j], img, sl, x, cur->glyphs[j].y_abs + tl->h * i, fg);
    347 					}
    348 					cur_border += local_border - cur->glyphs[end_index].x_abs;
    349 				}
    350 			} else {
    351 				start_index = cur == sl->run ? sl->glyph_index : 0;
    352 				int local_border = cur->glyphs[start_index].x_abs;
    353 				int end_index;
    354 				if (cur->num_glyphs - start_index < sl->len - cur_len)
    355 					end_index = cur->num_glyphs - 1;
    356 				else
    357 					end_index = start_index + sl->len - cur_len - 1;
    358 				if (par_is_rtl) {
    359 					for (int j = end_index; j >= start_index && cur_len < sl->len; j--) {
    360 						cur_len++;
    361 						int x = cur_border - (cur->glyphs[end_index].x_abs + cur->glyphs[end_index].x_advance - cur->glyphs[j].x_abs) + img->width - sl->w;
    362 						ltk_soft_line_draw_glyph(&cur->glyphs[j], img, sl, x, cur->glyphs[j].y_abs + tl->h * i, fg);
    363 					}
    364 					cur_border -= cur->glyphs[cur->num_glyphs - 1].x_abs + cur->glyphs[cur->num_glyphs - 1].x_advance - local_border;
    365 				} else {
    366 					for (int j = start_index; j < cur->num_glyphs && cur_len < sl->len; j++) {
    367 						cur_len++;
    368 						int x = cur_border + (cur->glyphs[j].x_abs - local_border);
    369 						ltk_soft_line_draw_glyph(&cur->glyphs[j], img, sl, x, cur->glyphs[j].y_abs + tl->h * i, fg);
    370 					}
    371 					cur_border += cur->glyphs[cur->num_glyphs - 1].x_abs + cur->glyphs[cur->num_glyphs - 1].x_advance - local_border;
    372 				}
    373 			}
    374 			cur = par_is_rtl ? cur->last : cur->next;
    375 		}
    376 		ltk_array_resize_int(sl->glyph_pos, sl->glyph_pos->len);
    377 		ltk_array_resize_uint32(sl->glyph_clusters, sl->glyph_clusters->len);
    378 	}
    379 	return img;
    380 }
    381 
    382 uint32_t
    383 ltk_soft_line_get_index_from_pos(int x, struct ltk_text_line *tl, struct ltk_soft_line *sl, int *found_pos) {
    384 	/* FIXME: need par dir! for better guess! */
    385 	/* also need it to determine if insert should be before or after char */
    386 	/* FIXME: should I be messing around with casting between uint32_t and size_t? */
    387 	/* FIXME: handle negative x */
    388 	int guess = (int)(((double)x / sl->w) * (sl->len - 1));
    389 	guess = guess >= sl->len ? sl->len - 1 : guess;
    390 	int delta = 0;
    391 	int i = 0;
    392 	int last_dist;
    393 	/* FIXME: add more safety - sl->len and sl->glyph_pos->len *should* be the
    394 	   same, but who knows? */
    395 	if (sl->glyph_pos->len - 1 > guess &&
    396 	    abs(sl->glyph_pos->buf[guess + 1] - x) < abs(sl->glyph_pos->buf[guess] - x)) {
    397 		delta = 1;
    398 		i = guess + 1;
    399 		last_dist = abs(sl->glyph_pos->buf[guess + 1] - x);
    400 	} else if (guess > 0 &&
    401 	    abs(sl->glyph_pos->buf[guess - 1] - x) < abs(sl->glyph_pos->buf[guess] - x)) {
    402 		delta = -1;
    403 		i = guess - 1;
    404 		last_dist = abs(sl->glyph_pos->buf[guess - 1] - x);
    405 	}
    406 	int final_index;
    407 	if (delta == 0) {
    408 		final_index = guess;
    409 	} else {
    410 		int new_dist;
    411 		for (; i >= 0 && i < sl->len; i += delta) {
    412 			new_dist = abs(sl->glyph_pos->buf[i] - x);
    413 			if (last_dist < new_dist)
    414 				break;
    415 			last_dist = new_dist;
    416 		}
    417 		final_index = i - delta;
    418 	}
    419 	if (found_pos)
    420 		*found_pos = sl->glyph_pos->buf[final_index];
    421 	uint32_t cluster = sl->glyph_clusters->buf[final_index];
    422 	if (FRIBIDI_LEVEL_IS_RTL(tl->bidi_levels->buf[cluster]))
    423 		return cluster >= tl->log_buf->len - 1 ? tl->log_buf->len - 1 : cluster + 1;
    424 	else
    425 		return cluster;
    426 }
    427 
    428 /* Begin stuff stolen from raqm */
    429 
    430 /* Special paired characters for script detection */
    431 static size_t paired_len = 34;
    432 static const FriBidiChar paired_chars[] = {
    433 	0x0028, 0x0029, /* ascii paired punctuation */
    434 	0x003c, 0x003e,
    435 	0x005b, 0x005d,
    436 	0x007b, 0x007d,
    437 	0x00ab, 0x00bb, /* guillemets */
    438 	0x2018, 0x2019, /* general punctuation */
    439 	0x201c, 0x201d,
    440 	0x2039, 0x203a,
    441 	0x3008, 0x3009, /* chinese paired punctuation */
    442 	0x300a, 0x300b,
    443 	0x300c, 0x300d,
    444 	0x300e, 0x300f,
    445 	0x3010, 0x3011,
    446 	0x3014, 0x3015,
    447 	0x3016, 0x3017,
    448 	0x3018, 0x3019,
    449 	0x301a, 0x301b
    450 };
    451 
    452 static int
    453 get_pair_index (const FriBidiChar ch) {
    454 	int lower = 0;
    455 	int upper = paired_len - 1;
    456 
    457 	while (lower <= upper) {
    458 	int mid = (lower + upper) / 2;
    459 	if (ch < paired_chars[mid])
    460 		upper = mid - 1;
    461 	else if (ch > paired_chars[mid])
    462 		lower = mid + 1;
    463 	else
    464 		return mid;
    465 	}
    466 
    467 	return -1;
    468 }
    469 
    470 #define STACK_IS_EMPTY(stack) ((stack)->len <= 0)
    471 #define IS_OPEN(pair_index) (((pair_index) & 1) == 0)
    472 
    473 /* Resolve the script for each character in the input string, if the character
    474  * script is common or inherited it takes the script of the character before it
    475  * except paired characters which we try to make them use the same script. We
    476  * then split the BiDi runs, if necessary, on script boundaries.
    477  */
    478 static int
    479 ltk_resolve_scripts(struct ltk_text_line *tl) {
    480 	int last_script_index = -1;
    481 	int last_set_index = -1;
    482 	hb_script_t last_script = HB_SCRIPT_INVALID;
    483 	hb_script_t cur_script;
    484 	struct ltk_array_script *script_stack = NULL;
    485 	struct ltk_array_int *pair_indeces = NULL;
    486 	hb_unicode_funcs_t* unicode_funcs = hb_unicode_funcs_get_default();
    487 
    488 	script_stack = ltk_array_create_script(4);
    489 	pair_indeces = ltk_array_create_int(4);
    490 
    491 	for (int i = 0; i < (int) tl->len; i++) {
    492 		cur_script = tl->scripts->buf[i];
    493 		if (cur_script == HB_SCRIPT_COMMON && last_script_index != -1) {
    494 			int pair_index = get_pair_index(tl->log_buf->buf[i]);
    495 			if (pair_index >= 0) {
    496 				if (IS_OPEN (pair_index)) {
    497 					/* is a paired character */
    498 					tl->scripts->buf[i] = last_script;
    499 					last_set_index = i;
    500 					ltk_array_append_script(script_stack, cur_script);
    501 					ltk_array_append_int(pair_indeces, pair_index);
    502 				} else {
    503 					/* is a close paired character */
    504 					/* find matching opening (by getting the last
    505 					 * even index for current odd index) */
    506 					while (!STACK_IS_EMPTY(pair_indeces) &&
    507 					    pair_indeces->buf[pair_indeces->len-1] != (pair_index & ~1)) {
    508 						ltk_array_pop_script(script_stack);
    509 						ltk_array_pop_int(pair_indeces);
    510 					}
    511 					if (!STACK_IS_EMPTY(script_stack)) {
    512 						tl->scripts->buf[i] = script_stack->buf[script_stack->len-1];
    513 						last_script = cur_script;
    514 						last_set_index = i;
    515 					} else {
    516 						tl->scripts->buf[i] = last_script;
    517 						last_set_index = i;
    518 					}
    519 				}
    520 			} else {
    521 				tl->scripts->buf[i] = last_script;
    522 				last_set_index = i;
    523 			}
    524 		} else if (cur_script == HB_SCRIPT_INHERITED && last_script_index != -1) {
    525 			tl->scripts->buf[i] = last_script;
    526 			last_set_index = i;
    527 		} else {
    528 			for (int j = last_set_index + 1; j < i; ++j)
    529 				tl->scripts->buf[j] = cur_script;
    530 			last_script = cur_script;
    531 			last_script_index = i;
    532 			last_set_index = i;
    533 		}
    534 	}
    535 
    536 	/* Loop backwards and change any remaining Common or Inherit characters to
    537 	* take the script of the next character.
    538 	* https://github.com/HOST-Oman/libraqm/issues/95
    539 	*/
    540 	hb_script_t scr;
    541 	for (int i = tl->len - 2; i >= 0;  --i) {
    542 		scr = tl->scripts->buf[i];
    543 		if (scr == HB_SCRIPT_INHERITED || scr == HB_SCRIPT_COMMON) {
    544 			tl->scripts->buf[i] = tl->scripts->buf[i + 1];
    545 		}
    546 	}
    547 
    548 	ltk_array_destroy_script(script_stack);
    549 	ltk_array_destroy_int(pair_indeces);
    550 
    551 	return 1;
    552 }
    553 
    554 /* End stuff stolen from raqm */
    555 /* Update: That's a lie; much more is stolen from raqm. */
    556 
    557 static struct ltk_text_run *
    558 ltk_text_run_create(size_t start_index, size_t len, hb_script_t script, hb_direction_t dir) {
    559 	struct ltk_text_run *run = malloc(sizeof(struct ltk_text_run));
    560 	if (!run) {
    561 		(void)fprintf(stderr, "Cannot allocate memory for text run.\n");
    562 		exit(1);
    563 	}
    564 	run->start_index = start_index;
    565 	run->len = len;
    566 	run->script = script;
    567 	run->dir = dir;
    568 	run->next = NULL;
    569 	run->last = NULL;
    570 	return run;
    571 }
    572 
    573 static void
    574 ltk_text_line_itemize(struct ltk_text_line *tl) {
    575 	ltk_resolve_scripts(tl);
    576 	struct ltk_text_run *first_run = NULL;
    577 	struct ltk_text_run *cur_run = NULL;
    578 	struct ltk_text_run *new = NULL;
    579 	FriBidiLevel last_level;
    580 	FriBidiLevel cur_level;
    581 	hb_script_t last_script;
    582 	hb_script_t cur_script;
    583 	size_t start_index = 0;
    584 	size_t end_index;
    585 	hb_direction_t dir;
    586 	int par_is_rtl = FRIBIDI_IS_RTL(tl->dir);
    587 	while (start_index < tl->len) {
    588 		end_index = start_index;
    589 		cur_level = last_level = tl->bidi_levels->buf[tl->vis2log->buf[start_index]];
    590 		cur_script = last_script = tl->scripts->buf[tl->vis2log->buf[start_index]];
    591 		while (end_index < tl->len &&
    592 		    cur_level == last_level && cur_script == last_script) {
    593 			end_index++;
    594 			/* I should probably make this nicer at some point */
    595 			if (end_index < tl->len) {
    596 				cur_level = tl->bidi_levels->buf[tl->vis2log->buf[end_index]];
    597 				cur_script = tl->scripts->buf[tl->vis2log->buf[end_index]];
    598 			}
    599 		}
    600 		dir = HB_DIRECTION_LTR;
    601 		if (FRIBIDI_LEVEL_IS_RTL(last_level))
    602 			dir = HB_DIRECTION_RTL;
    603 		size_t start_log = tl->vis2log->buf[start_index];
    604 		size_t end_log = tl->vis2log->buf[end_index - 1];
    605 		if (start_log > end_log)
    606 			start_log = end_log;
    607 		new = ltk_text_run_create(
    608 		    start_log, end_index - start_index, last_script, dir);
    609 		if (!first_run) {
    610 			first_run = new;
    611 		} else {
    612 			cur_run->next = new;
    613 			new->last = cur_run;
    614 		}
    615 		cur_run = new;
    616 		start_index = end_index;
    617 	}
    618 	tl->first_run = first_run;
    619 	tl->last_run = cur_run;
    620 	cur_run = first_run;
    621 }
    622 
    623 static void
    624 ltk_text_run_shape(struct ltk_text_run *tr,
    625     struct ltk_text_line *tl, uint16_t font_size, uint16_t font_id, int *ret_y_max) {
    626 	LtkTextManager *tm = ltk_get_text_manager();
    627 	khash_t(glyphinfo) *glyph_cache;
    628 	khint_t k;
    629 
    630 	tr->font_id = font_id;
    631 	tr->font_size = font_size;
    632 	uint32_t attr = (uint32_t)font_id << 16 + font_size;
    633 	/* FIXME: turn this into ltk_get_glyph_cache */
    634 	k = kh_get(glyphcache, tm->glyph_cache, attr);
    635 	if (k == kh_end(tm->glyph_cache)) {
    636 		k = ltk_create_glyph_cache(tm, font_id, font_size);
    637 	}
    638 	glyph_cache = kh_value(tm->glyph_cache, k);
    639 
    640 	hb_buffer_t *buf;
    641 	hb_glyph_info_t *ginf, *gi;
    642 	hb_glyph_position_t *gpos, *gp;
    643 	tr->num_glyphs = 0;
    644 
    645 	buf = hb_buffer_create();
    646 	hb_buffer_add_utf32(buf, tl->log_buf->buf, tl->len, tr->start_index, tr->len);
    647 	hb_buffer_set_direction(buf, tr->dir);
    648 	hb_buffer_set_script(buf, tr->script);
    649 	/* According to https://harfbuzz.github.io/the-distinction-between-levels-0-and-1.html
    650 	 * this should be level 1 clustering instead of level 0 */
    651 	hb_buffer_set_cluster_level(buf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
    652 	hb_shape(tr->font->hb, buf, NULL, 0);
    653 	ginf = hb_buffer_get_glyph_infos(buf, &tr->num_glyphs);
    654 	gpos = hb_buffer_get_glyph_positions(buf, &tr->num_glyphs);
    655 	float scale = stbtt_ScaleForMappingEmToPixels(&tr->font->info, font_size);
    656 
    657 	int x_min = INT_MAX, x_max = INT_MIN, y_min = INT_MAX, y_max = INT_MIN;
    658 	int x_abs = 0, y_abs = 0, x1_abs, y1_abs, x2_abs, y2_abs;
    659 	tr->glyphs = malloc(sizeof(LtkGlyph) * tr->num_glyphs);
    660 	if (!tr->glyphs) {
    661 		(void)fprintf(stderr, "Cannot allocate space for glyphs.\n");
    662 		exit(1);
    663 	}
    664 	/* FIXME: should x_max be calculated using glyph->info->w?
    665 	   The advance might be different and not represent where pixels
    666 	   will actually have to be drawn. */
    667 	/* magic, do not touch */
    668 	LtkGlyph *glyph;
    669 	for (int i = 0; i < tr->num_glyphs; i++) {
    670 		gi = &ginf[i];
    671 		gp = &gpos[i];
    672 		glyph = &tr->glyphs[i];
    673 		glyph->cluster = gi->cluster;
    674 		glyph->info = ltk_get_glyph_info(tr->font, gi->codepoint, scale, glyph_cache);
    675 		glyph->info->refs++;
    676 		/* FIXME: round instead of just casting */
    677 		glyph->x_offset = (int)(gp->x_offset * scale);
    678 		glyph->y_offset = (int)(gp->y_offset * scale);
    679 		glyph->x_advance = (int)(gp->x_advance * scale);
    680 		glyph->y_advance = (int)(gp->y_advance * scale);
    681 
    682 		/* Calculate position in order to determine full size of text segment */
    683 		x1_abs = x_abs + glyph->info->xoff + glyph->x_offset;
    684 		y1_abs = y_abs + glyph->info->yoff - glyph->y_offset;
    685 		/* FIXME: THIS PROBABLY DOESN'T REALLY WORK */
    686 		if (HB_DIRECTION_IS_HORIZONTAL(tr->dir)) {
    687 			x2_abs = x1_abs + glyph->x_advance;
    688 			y2_abs = y1_abs + glyph->info->h;
    689 		} else {
    690 			x2_abs = x1_abs + glyph->info->w;
    691 			y2_abs = y1_abs - glyph->y_advance;
    692 		}
    693 		if (x1_abs < x_min) x_min = x1_abs;
    694 		if (y1_abs < y_min) y_min = y1_abs;
    695 		if (x2_abs > x_max) x_max = x2_abs;
    696 		if (y2_abs > y_max) y_max = y2_abs;
    697 		x_abs += glyph->x_advance;
    698 		y_abs -= glyph->y_advance;
    699 	}
    700         tr->start_x = -x_min;
    701         tr->start_y = -y_min;
    702 	tr->w = x_max - x_min;
    703 	*ret_y_max = y_max;
    704 
    705 	tr->font->refs++;
    706 }
    707 
    708 static void
    709 ltk_text_line_shape(struct ltk_text_line *tl) {
    710 	LtkTextManager *tm = ltk_get_text_manager();
    711 	struct ltk_text_run *run = tl->first_run;
    712 	tl->w = tl->h = 0;
    713 	int y_max;
    714 	int y_max_overall = INT_MIN;
    715 	int y_min_overall = INT_MAX;
    716 	while (run) {
    717 		/* Question: Why does this not work with FcPatternDuplicate? */
    718 		FcPattern *pat = FcPatternCreate();
    719 		FcPattern *match;
    720 		FcResult result;
    721 		FcPatternAddBool(pat, FC_SCALABLE, 1);
    722 		FcConfigSubstitute(NULL, pat, FcMatchPattern);
    723 		FcDefaultSubstitute(pat);
    724 		/* FIXME: make this more efficient */
    725 		FcCharSet *cs = FcCharSetCreate();
    726 		for (size_t i = run->start_index; i < run->start_index + run->len; i++) {
    727 			FcCharSetAddChar(cs, tl->log_buf->buf[i]);
    728 		}
    729 		FcPatternAddCharSet(pat, FC_CHARSET, cs);
    730 		match = FcFontMatch(NULL, pat, &result);
    731 		char *file;
    732 		FcPatternGetString(match, FC_FILE, 0, &file);
    733 		uint16_t font_id = ltk_get_font(tm, file);
    734 		khint_t k = kh_get(fontstruct, tm->font_cache, font_id);
    735 		run->font = kh_value(tm->font_cache, k);
    736 		FcPatternDestroy(match);
    737 		FcPatternDestroy(pat);
    738 		ltk_text_run_shape(run, tl, tl->font_size, font_id, &y_max);
    739 		if (y_max_overall < y_max)
    740 			y_max_overall = y_max;
    741 		/* tr->start_y is -y_min */
    742 		if (y_min_overall > -run->start_y)
    743 			y_min_overall = -run->start_y;
    744 		tl->w += run->w;
    745 		run = run->next;
    746 	};
    747 	tl->h = y_max_overall - y_min_overall;
    748 
    749 	/* calculate the actual position of the characters */
    750 	run = tl->first_run;
    751 	int x = 0;
    752 	LtkGlyph *glyph;
    753 	while (run) {
    754 		int cur_x = x + run->start_x;
    755 		int cur_y = tl->h - y_max_overall; /* baseline (I think?) */
    756 		for (int i = 0; i < run->len; i++) {
    757 			glyph = &run->glyphs[i];
    758 			glyph->x_abs = cur_x + glyph->info->xoff + glyph->x_offset;
    759 			glyph->y_abs = cur_y + glyph->info->yoff - glyph->y_offset;
    760 			cur_x += glyph->x_advance;
    761 			cur_y -= glyph->y_advance;
    762 		}
    763 		x += run->w;
    764 		run = run->next;
    765 	}
    766 }
    767 
    768 void
    769 ltk_text_run_destroy(struct ltk_text_run *tr) {
    770 	khash_t(glyphinfo) *gcache;
    771 	LtkFont *font;
    772 	LtkGlyph *glyph;
    773 	khint_t k;
    774 	LtkTextManager *tm = ltk_get_text_manager();
    775 	k = kh_get(glyphinfo, tm->glyph_cache, tr->font_id << 16 + tr->font_size);
    776 	gcache = kh_value(tm->glyph_cache, k);
    777 	for (int i = 0; i < tr->num_glyphs; i++) {
    778 		glyph = &tr->glyphs[i];
    779 		if (--glyph->info->refs < 1) {
    780 			k = kh_get(glyphinfo, gcache, glyph->info->id);
    781 			kh_del(glyphinfo, gcache, k);
    782 			ltk_destroy_glyph_info(glyph->info);
    783 		}
    784 	}
    785 	k = kh_get(fontstruct, tm->font_cache, tr->font_id);
    786 	font = kh_value(tm->font_cache, k);
    787 	if (--font->refs < 1) {
    788 		kh_del(fontstruct, tm->font_cache, k);
    789 		ltk_destroy_font(font);
    790 	}
    791 	free(tr->glyphs);
    792 	free(tr);
    793 }
    794 
    795 void
    796 ltk_text_line_destroy_runs(struct ltk_text_run *runs) {
    797 	struct ltk_text_run *cur, *next;
    798 	cur = runs;
    799 	while (cur) {
    800 		next = cur->next;
    801 		ltk_text_run_destroy(cur);
    802 		cur = next;
    803 	}
    804 }
    805 
    806 static void
    807 ltk_text_line_recalculate(struct ltk_text_line *tl) {
    808 	FriBidiCharType par_dir = FRIBIDI_TYPE_ON;
    809 	fribidi_log2vis(
    810 	    tl->log_buf->buf, tl->log_buf->len,
    811 	    &par_dir, tl->vis_buf->buf, tl->log2vis->buf, tl->vis2log->buf, tl->bidi_levels->buf
    812 	);
    813 	if (FRIBIDI_IS_RTL(par_dir))
    814 		tl->dir = HB_DIRECTION_RTL;
    815 	else
    816 		tl->dir = HB_DIRECTION_LTR;
    817 	struct ltk_text_run *old_runs = tl->first_run;
    818 	ltk_text_line_itemize(tl);
    819 	struct ltk_text_run *cur = tl->first_run;
    820 	ltk_text_line_shape(tl);
    821 	/* this needs to be done after shaping so the fonts, etc. aren't
    822 	   removed if their reference counts drop and then loaded again
    823 	   right afterwards */
    824 	if (old_runs)
    825 		ltk_text_line_destroy_runs(old_runs);
    826 }
    827 
    828 
    829 void
    830 ltk_text_line_insert_utf32(struct ltk_text_line *tl, size_t index, uint32_t *text, size_t len) {
    831 	ltk_array_insert_uint32(tl->log_buf, index, text, len);
    832 	ltk_array_prepare_gap_script(tl->scripts, index, len);
    833 	hb_unicode_funcs_t *ufuncs = hb_unicode_funcs_get_default();
    834 	for (int i = 0; i < len; i++)
    835 		tl->scripts->buf[index + i] = hb_unicode_script(ufuncs, text[i]);
    836 	ltk_array_resize_uint32(tl->vis_buf, tl->len + len);
    837 	ltk_array_resize_int(tl->log2vis, tl->len + len);
    838 	ltk_array_resize_int(tl->vis2log, tl->len + len);
    839 	ltk_array_resize_level(tl->bidi_levels, tl->len + len);
    840 	tl->len += len;
    841 	ltk_text_line_recalculate(tl);
    842 }
    843 
    844 /* must be NULL-terminated */
    845 void
    846 ltk_text_line_insert_utf8(struct ltk_text_line *tl, size_t index, char *text) {
    847 	size_t len = u8_strlen(text);
    848 	uint32_t *new = malloc(sizeof(uint32_t) * len);
    849 	if (!new) {
    850 		(void)fprintf(stderr, "Error allocating memory for string\n");
    851 		exit(1);
    852 	}
    853 	size_t inc = 0;
    854 	for (int i = 0; i < len; i++)
    855 		new[i] = u8_nextmemchar(text, &inc);
    856 	ltk_text_line_insert_utf32(tl, index, new, len);
    857 	free(new);
    858 }
    859 
    860 struct ltk_text_line *
    861 ltk_text_line_create(uint16_t font_size) {
    862 	struct ltk_text_line *line = malloc(sizeof(struct ltk_text_line));
    863 	if (!line) goto error;
    864 	line->log_buf = ltk_array_create_uint32(4);
    865 	line->vis_buf = ltk_array_create_uint32(4);
    866 	line->log2vis = ltk_array_create_int(4);
    867 	line->vis2log = ltk_array_create_int(4);
    868 	line->scripts = ltk_array_create_script(4);
    869 	line->bidi_levels = ltk_array_create_level(4);
    870 	line->soft_lines = ltk_array_create_line(1);
    871 	line->first_run = NULL;
    872 	line->next = NULL;
    873 	line->len = 0;
    874 	line->w_wrapped = line->h_wrapped = 0;
    875 	line->font_size = font_size;
    876 	return line;
    877 error:
    878 	(void)fprintf(stderr, "No memory left while creating text line\n");
    879 	exit(1);
    880 }
    881 
    882 void
    883 ltk_text_buffer_scroll(struct ltk_text_buffer *tb, int offset_y) {
    884 }
    885 
    886 void
    887 ltk_text_buffer_render(struct ltk_text_buffer *tb, int offset_y, int offset_x, int w, int h) {
    888 	struct ltk_text_line *cur = tb->head;
    889 	int cur_y = 0;
    890 	while (cur) {
    891 		if (cur_y > offset_y + h)
    892 			break;
    893 		if (cur_y > offset_y || cur_y + cur->h_wrapped > offset_y) {
    894 		}
    895 		cur_y += cur->h_wrapped;
    896 		cur = cur->next;
    897 	}
    898 }
    899 
    900 void
    901 ltk_text_buffer_wrap(struct ltk_text_buffer *tb, int max_width) {
    902 	struct ltk_text_line *cur = tb->head;
    903 	while (cur) {
    904 		ltk_text_line_wrap(cur, max_width);
    905 		cur = cur->next;
    906 	}
    907 }
    908 
    909 void
    910 ltk_text_buffer_insert_utf8_at_cursor(struct ltk_text_buffer *tb, char *text) {
    911 	size_t len = u8_strlen(text);
    912 	uint32_t *new = malloc(sizeof(uint32_t) * len);
    913 	if (!new) {
    914 		(void)fprintf(stderr, "Error allocating memory for string\n");
    915 		exit(1);
    916 	}
    917 	size_t inc = 0;
    918 	uint32_t c;
    919 	size_t actual_len = 0;
    920 	for (int i = 0; i < len; i++) {
    921 		c = u8_nextmemchar(text, &inc);
    922 		if (c == 0x000A) {
    923 			printf("newline\n");
    924 		} else {
    925 			actual_len++;
    926 			new[i] = c;
    927 		}
    928 	}
    929 	//ltk_text_line_insert_utf32(tb->cur_line, tb->cursor_pos, new, actual_len);
    930 	free(new);
    931 }
    932 
    933 struct ltk_text_buffer *
    934 ltk_text_buffer_create(void) {
    935 	struct ltk_text_buffer *buf = malloc(sizeof(struct ltk_text_buffer));
    936 	if (!buf) {
    937 		(void)fprintf(stderr, "No memory while creating text buffer\n");
    938 		exit(1);
    939 	}
    940 	buf->head = NULL;
    941 	buf->cursor_pos = 0;
    942 }
    943 
    944 void
    945 ltk_text_line_destroy(struct ltk_text_line *tl) {
    946 	ltk_array_destroy_uint32(tl->log_buf);
    947 	ltk_array_destroy_uint32(tl->vis_buf);
    948 	ltk_array_destroy_script(tl->scripts);
    949 	ltk_array_destroy_int(tl->log2vis);
    950 	ltk_array_destroy_int(tl->vis2log);
    951 	ltk_array_destroy_level(tl->bidi_levels);
    952 	ltk_array_destroy_deep_line(tl->soft_lines, &ltk_soft_line_destroy);
    953 	ltk_text_line_destroy_runs(tl->first_run);
    954 	free(tl);
    955 }