textedit_wip.c (21936B)
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 /* NOTE: THIS DOESN'T HAVE ANY USABLE CODE YET! */ 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <stdint.h> 29 #include <limits.h> 30 #include <X11/Xlib.h> 31 #include <X11/Xutil.h> 32 #include "stb_truetype.h" /* http://nothings.org/stb/stb_truetype.h */ 33 #include <fontconfig/fontconfig.h> 34 #include "khash.h" 35 #include <fribidi.h> 36 #include <harfbuzz/hb.h> 37 #include <harfbuzz/hb-ot.h> 38 #include "textedit_wip.h" 39 #include "text-common.h" 40 #include "ltk.h" 41 42 extern Ltk *ltk_global; 43 44 LTK_GAP_BUFFER_INIT_IMPL(uint32, uint32_t) 45 LTK_GAP_BUFFER_INIT_IMPL(script, hb_script_t) 46 LTK_GAP_BUFFER_INIT_IMPL(int, int) 47 LTK_ARRAY_INIT_IMPL(level, FriBidiLevel) 48 LTK_ARRAY_INIT_IMPL(int, int) 49 LTK_STACK_INIT_IMPL(script, int, hb_script_t, pair_index, script); 50 51 /* based on http://codemadness.org/git/dwm-font/file/drw.c.html#l315 */ 52 void 53 ltk_render_text_line( 54 struct ltk_text_line *tl, 55 int max_width, 56 Display *dpy, 57 Window window, 58 GC gc, 59 Colormap colormap, 60 XColor fg, 61 XColor bg) 62 { 63 int cur_x = 0, cur_y = 0; 64 int par_is_rtl = FRIBIDI_IS_RTL(tl->dir); 65 ltk_array_clear_int(tl->wrap_indeces); 66 ltk_array_append_int(tl->wrap_indeces, 0); 67 68 /* FIXME: wrap bidi text properly */ 69 struct ltk_text_run *cur = tl->first_run; 70 do { 71 for (int i = 0; i < cur->num_glyphs; i++) { 72 cur_x += cur->glyphs[i]->x_advance; 73 if (cur_x > max_width) { 74 int j = 0; 75 for (j = i; j >= 0; j--) { 76 if (cur->glyphs[j]->cluster != cur->glyphs[i]->cluster) { 77 /* must increase one again so the actual 78 next character is used */ 79 j++; 80 break; 81 } 82 } 83 i = j; 84 /* FIXME: handle case that this is the same as the last index */ 85 ltk_array_append_int(tl->wrap_indeces, cur->glyphs[i].cluster); 86 cur_x = 0; 87 } 88 } 89 } while (cur = cur->next); 90 91 if (tl->img) XDestroyImage(tl->img); 92 XWindowAttributes attrs; 93 XGetWindowAttributes(dpy, window, &attrs); 94 int depth = attrs.depth; 95 tl->img = XCreateImage(dpy, CopyFromParent, depth, ZPixmap, 0, NULL, max_width, tl->h * tl->wrap_indeces->len, 32, 0); 96 tl->img->data = calloc(img->bytes_per_line, img->height); 97 XInitImage(tl->img); 98 99 int b; 100 for (int i = 0; i < tl->h * tl->wrap_indeces->len; i++) { 101 b = tl->img->bytes_per_line * i; 102 for (int j = 0; j < max_width; j++) { 103 tl->img->data[b++] = bg.blue / 257; 104 tl->img->data[b++] = bg.green / 257; 105 tl->img->data[b++] = bg.red / 257; 106 b++; 107 } 108 } 109 110 cur = tl->first_run; 111 int x, y; 112 int cur_line_x = 0; 113 int cur_line = 0; 114 LtkGlyph *glyph; 115 /* FIXME: Ints are compared with size_t's in various places. Maybe I should fix that. */ 116 /* FIXME: how should an empty line be handled? This doesn't use a do-for 117 loop in case tl->first_run is NULL, but I should probably decide what 118 to do in that case */ 119 while (cur) { 120 for (int k = 0; k < cur->len; k++) { 121 glyph = cur->glyphs[k]; 122 if (cur_line < tl->wrap_indeces->len - 1 && 123 glyph->cluster >= tl->wrap_indeces->buf[cur_line + 1]) { 124 cur_line++; 125 cur_line_x += glyph->x_abs - cur_line_x; 126 } 127 x = glyph->x_abs - cur_line_x; 128 y = glyph->y_abs + tl->h * cur_line; 129 for (int i = 0; i < glyph->info->h; i++) { 130 for (int j = 0; j < glyph->info->w; j++) { 131 b = (y + i) * tl->img->bytes_per_line + (x + j) * 4; 132 a = glyph->info->alphamap[i * glyph->info->w + j] / 255.0; 133 tl->img->data[b] = (fg.blue * a + (1 - a) * (uint16_t)tl->img->data[b] * 257) / 257; 134 tl->img->data[b + 1] = (fg.green * a + (1 - a) * (uint16_t)tl->img->data[b + 1] * 257) / 257; 135 tl->img->data[b + 2] = (fg.red * a + (1 - a) * (uint16_t)tl->img->data[b + 2] * 257) / 257; 136 } 137 } 138 } 139 cur = cur->next; 140 } 141 } 142 143 /* 144 NOTE: The following notes are outdated. 145 Notes: When inserting a character, check what the direction of the surrounding 146 script is - if it is the same (or a weak direction), then just insert the char 147 without re-doing the bidi algorithm. Only redo the whole line/paragraph is the 148 dir of the inserted character is different. Assume the text is LTR until a strong 149 character is inserted, i.e. the line should keep a flag if it already has a strong 150 dir set. 151 When reordering, the cursor needs to be kept in the right place: 152 a) The logical to visual mapping from FriBidi needs to be used to find the position 153 of the cursor in the text that gets passed to Harfbuzz. 154 b) The cluster values from Harfbuzz need to be used to figure out where the cursor 155 is on the screen. 156 Harfbuzz also should be given some context - maybe even completely reshape the 157 text until the last and next space? That would only be a problem with weird very 158 long words (mainly nonsense). At least maybe take up to five or so chars on each 159 side and pass even more with the optional context-passing feature for hb_shape. 160 Question: Does both the logical and visual text need to be stored? The logical text 161 is technically the master copy, but the visual text is what gets passed to harfbuzz. 162 -> Actually, since the visual text has to be split into script runs anyways, maybe 163 just append text to that (and also add it to the master buffer simultaneously). As 164 soon as a different script is inserted, everything has to be redone anyways. Also, 165 when reshaping with context, only the text in the current run has to be passed at all. 166 */ 167 168 /* Begin stuff stolen from raqm */ 169 170 /* Special paired characters for script detection */ 171 static size_t paired_len = 34; 172 static const FriBidiChar paired_chars[] = { 173 0x0028, 0x0029, /* ascii paired punctuation */ 174 0x003c, 0x003e, 175 0x005b, 0x005d, 176 0x007b, 0x007d, 177 0x00ab, 0x00bb, /* guillemets */ 178 0x2018, 0x2019, /* general punctuation */ 179 0x201c, 0x201d, 180 0x2039, 0x203a, 181 0x3008, 0x3009, /* chinese paired punctuation */ 182 0x300a, 0x300b, 183 0x300c, 0x300d, 184 0x300e, 0x300f, 185 0x3010, 0x3011, 186 0x3014, 0x3015, 187 0x3016, 0x3017, 188 0x3018, 0x3019, 189 0x301a, 0x301b 190 }; 191 192 static int 193 get_pair_index (const FriBidiChar ch) { 194 int lower = 0; 195 int upper = paired_len - 1; 196 197 while (lower <= upper) { 198 int mid = (lower + upper) / 2; 199 if (ch < paired_chars[mid]) 200 upper = mid - 1; 201 else if (ch > paired_chars[mid]) 202 lower = mid + 1; 203 else 204 return mid; 205 } 206 207 return -1; 208 } 209 210 #define STACK_IS_EMPTY(stack) ((stack)->size <= 0) 211 #define IS_OPEN(pair_index) (((pair_index) & 1) == 0) 212 213 /* Resolve the script for each character in the input string, if the character 214 * script is common or inherited it takes the script of the character before it 215 * except paired characters which we try to make them use the same script. We 216 * then split the BiDi runs, if necessary, on script boundaries. 217 */ 218 static int 219 ltk_resolve_scripts(struct ltk_text_line *tl) { 220 int last_script_index = -1; 221 int last_set_index = -1; 222 hb_script_t last_script = HB_SCRIPT_INVALID; 223 hb_script_t cur_script; 224 ltk_stack_script *stack = NULL; 225 hb_unicode_funcs_t* unicode_funcs = hb_unicode_funcs_get_default(); 226 227 stack = ltk_stack_create_script(tl->len); 228 229 for (int i = 0; i < (int) tl->len; i++) { 230 cur_script = ltk_gap_buffer_get_script(tl->scripts, i); 231 if (cur_script == HB_SCRIPT_COMMON && last_script_index != -1) { 232 int pair_index = get_pair_index(ltk_gap_buffer_get_uint32(tl->log_buf, i)); 233 if (pair_index >= 0) { 234 if (IS_OPEN (pair_index)) { 235 /* is a paired character */ 236 ltk_gap_buffer_set_script(tl->scripts, i, last_script); 237 last_set_index = i; 238 ltk_stack_push_script(stack, cur_script, pair_index); 239 } else { 240 /* is a close paired character */ 241 /* find matching opening (by getting the last 242 * even index for current odd index) */ 243 while (!STACK_IS_EMPTY(stack) && 244 stack->pair_index[stack->size] != (pair_index & ~1)) { 245 ltk_stack_pop_script(stack); 246 } 247 if (!STACK_IS_EMPTY(stack)) { 248 ltk_gap_buffer_set_script( 249 tl->scripts, i, 250 ltk_stack_top2_script(stack, HB_SCRIPT_INVALID)); 251 last_script = cur_script; 252 last_set_index = i; 253 } else { 254 ltk_gap_buffer_set_script(tl->scripts, i, last_script); 255 last_set_index = i; 256 } 257 } 258 } else { 259 ltk_gap_buffer_set_script(tl->scripts, i, last_script); 260 last_set_index = i; 261 } 262 } else if (cur_script == HB_SCRIPT_INHERITED && last_script_index != -1) { 263 ltk_gap_buffer_set_script(tl->scripts, i, last_script); 264 last_set_index = i; 265 } else { 266 for (int j = last_set_index + 1; j < i; ++j) 267 ltk_gap_buffer_set_script(tl->scripts, j, cur_script); 268 last_script = cur_script; 269 last_script_index = i; 270 last_set_index = i; 271 } 272 } 273 274 /* Loop backwards and change any remaining Common or Inherit characters to 275 * take the script if the next character. 276 * https://github.com/HOST-Oman/libraqm/issues/95 277 */ 278 hb_script_t scr; 279 for (int i = tl->len - 2; i >= 0; --i) { 280 scr = ltk_gap_buffer_get_script(tl->scripts, i); 281 if (scr == HB_SCRIPT_INHERITED || scr == HB_SCRIPT_COMMON) { 282 ltk_gap_buffer_set_script(tl->scripts, 283 ltk_gap_buffer_get_script(tl->scripts, i + 1)); 284 } 285 } 286 287 ltk_stack_destroy_script(stack); 288 289 return 1; 290 } 291 292 /* End stuff stolen from raqm */ 293 /* Update: That's a lie; much more is stolen from raqm. */ 294 295 static struct 296 ltk_text_run_create(size_t start_index, size_t len, hb_script_t script, hb_direction_t dir) { 297 struct ltk_text_run *run = malloc(sizeof(struct ltk_text_run)); 298 if (!run) { 299 (void)fprintf(stderr, "Cannot allocate memory for text run.\n"); 300 exit(1); 301 } 302 run->start_index = start_index; 303 run->len = len; 304 run->script = script; 305 run->dir = dir; 306 run->last = NULL; 307 run->next = NULL; 308 } 309 310 static void 311 ltk_text_line_itemize(struct ltk_text_line *tl) { 312 ltk_resolve_scripts(tl); 313 struct ltk_text_run *first_run = NULL; 314 struct ltk_text_run *cur_run = NULL; 315 FriBidilevel last_level; 316 FriBidilevel cur_level; 317 hb_script_t last_script; 318 hb_script_t cur_script; 319 size_t start_index = 0; 320 size_t end_index; 321 hb_direction_t dir; 322 int par_is_rtl = FRIBIDI_IS_RTL(tl->dir); 323 while (start_index < tl->len) { 324 end_index = start_index; 325 cur_level = last_level = ltk_gap_buffer_get_level(tl->bidi_levels, start_index); 326 cur_script = last_script = ltk_gap_buffer_get_script(tl->scripts, start_index); 327 while (end_index < tl->len && 328 cur_level == last_level && cur_script == last_script) { 329 end_index++; 330 cur_level = ltk_gap_buffer_get_level(tl->bidi_levels, end_index); 331 cur_script = ltk_gap_buffer_get_script(tl->scripts, end_index); 332 } 333 dir = HB_DIRECTION_LTR; 334 if (FRIBIDI_LEVEL_IS_RTL(last_level)) 335 dir = HB_DIRECTION_RTL; 336 struct ltk_text_run *new = ltk_text_run_create( 337 start_index, end_index - start_index, last_script, dir); 338 if (!first_run) { 339 first_run = cur_run = new; 340 } else { 341 if (par_is_rtl) { 342 new->next = cur_run; 343 cur_run->last = new; 344 } else { 345 cur_run->next = new; 346 new->last = cur_run; 347 } 348 cur_run = new; 349 } 350 start_index = end_index; 351 } 352 tl->first_run = tl->cur_run = first_run; 353 tl->last_run = cur_run; 354 } 355 356 /* FIXME: return start_x, etc. instead of saving in struct text run */ 357 static void 358 ltk_text_run_shape(LtkTextManager *tm, struct ltk_text_run *tr, 359 struct ltk_text_line *tl, uint16_t font_size, uint16_t font_id, int *ret_y_max) { 360 khash_t(glyphinfo) *glyph_cache; 361 khint_t k; 362 363 tr->font_id = font_id; 364 uint32_t attr = font_id << 16 + font_size; 365 /* FIXME: turn this into ltk_get_glyph_cache */ 366 k = kh_get(glyphcache, tm->glyph_cache, attr); 367 if (k == kh_end(tm->glyph_cache)) { 368 k = ltk_create_glyph_cache(tm, font_id, font_size); 369 } 370 glyph_cache = kh_value(tm->glyph_cache, k); 371 372 hb_buffer_t *buf; 373 hb_glyph_info_t *ginf, *gi; 374 hb_glyph_position_t *gpos, *gp; 375 tr->num_glyphs = 0; 376 377 buf = hb_buffer_create(); 378 hb_buffer_set_direction(buf, run->dir); 379 hb_buffer_set_script(buf, run->script); 380 /* WARNING: vis_buf has to be normalized (without gap) for this! */ 381 hb_buffer_add_codepoints(buf, tl->vis-buf, tl->len, tr->start_index, tr->len); 382 /* According to https://harfbuzz.github.io/the-distinction-between-levels-0-and-1.html 383 * this should be level 1 clustering instead of level 0 */ 384 hb_buffer_set_cluster_level(buf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); 385 hb_shape(font->hb, buf, NULL, 0); 386 ginf = hb_buffer_get_glyph_infos(buf, &tr->num_glyphs); 387 gpos = hb_buffer_get_glyph_positions(buf, &tr->num_glyphs); 388 float scale = stbtt_ScaleForMappingEmToPixels(&tr->font->info, font_size); 389 390 int x_min = INT_MAX, x_max = INT_MIN, y_min = INT_MAX, y_max = INT_MIN; 391 int x_abs = 0, y_abs = 0, x1_abs, y1_abs, x2_abs, y2_abs; 392 /* magic, do not touch */ 393 tr->glyphs = malloc(sizeof(LtkGlyph) * num_glyph); 394 if (!tr->glyphs) { 395 (void)fprintf("Cannot allocate space for glyphs.\n"); 396 exit(1); 397 } 398 /* FIXME: should x_max be calculated using glyph->info->w? 399 The advance might be different and not represent where pixels 400 will actually have to be drawn. */ 401 LtkGlyph *glyph; 402 for (int i = 0; i < tr->num_glyphs; i++) { 403 gi = &ginf[i]; 404 gp = &gpos[i]; 405 glyph = tr->glyphs[i]; 406 glyph->cluster = gi->cluster; 407 glyph->info = ltk_get_glyph_info(tr->font, gi->codepoint, scale, glyph_cache); 408 glyph->info->refs++; 409 /* FIXME: round instead of just casting */ 410 glyph->x_offset = (int)(gp->x_offset * scale); 411 glyph->y_offset = (int)(gp->y_offset * scale); 412 glyph->x_advance = (int)(gp->x_advance * scale); 413 glyph->y_advance = (int)(gp->y_advance * scale); 414 415 /* Calculate position in order to determine full size of text segment */ 416 x1_abs = x_abs + glyph->info->xoff + glyph->x_offset; 417 y1_abs = y_abs + glyph->info->yoff - glyph->y_offset; 418 /* Okay, wait, so should I check if the script is horizontal, and then add 419 x_advance instead of glyph->info->w? It seems that the glyph width is 420 usually smaller than x_advance, and spaces etc. are completely lost 421 because their glyph width is 0. I have to distinguish between horizontal 422 and vertical scripts, though because to calculate the maximum y position 423 for horizontal scripts, I still need to use the glyph height since 424 y_advance doesn't really do much there. I dunno, at least *something* 425 works now... */ 426 /* FIXME: THIS PROBABLY DOESN'T REALLY WORK */ 427 if (HB_DIRECTION_IS_HORIZONTAL(tr->dir)) { 428 x2_abs = x1_abs + glyph->x_advance; 429 y2_abs = y1_abs + glyph->info->h; 430 } else { 431 x2_abs = x1_abs + glyph->info->w; 432 y2_abs = y1_abs - glyph->y_advance; 433 } 434 if (x1_abs < x_min) x_min = x1_abs; 435 if (y1_abs < y_min) y_min = y1_abs; 436 if (x2_abs > x_max) x_max = x2_abs; 437 if (y2_abs > y_max) y_max = y2_abs; 438 x_abs += glyph->x_advance; 439 y_abs -= glyph->y_advance; 440 } 441 tr->start_x = -x_min; 442 tr->start_y = -y_min; 443 tr->w = x_max - x_min; 444 *ret_y_max = y_max; 445 446 tr->font->refs++; 447 } 448 449 static void 450 ltk_text_line_shape(LtkTextManager *tm, struct ltk_text_line *tl) { 451 struct ltk_text_run *run = tl->runs; 452 tl->y_max = INT_MIN; 453 tl->y_min = INT_MAX; 454 tl->w = tl->h = 0; 455 int x_max, y_max; 456 while (run) { 457 FcPattern *pat = FcPatternDuplicate(tm->fcpattern); 458 FcPattern *match; 459 FcResult result; 460 FcPatternAddBool(pat, FC_SCALABLE, 1); 461 FcConfigSubstitute(NULL, pat, FcMatchPattern); 462 FcDefaultSubstitute(pat); 463 FcCharSet *cs = FcCharSetCreate(); 464 for (int i = run->start_index; i < run->start_index + run->len; i++) { 465 FcCharSetAddChar(cs, ltk_gap_buffer_get_uint32(tl->vis_buf, i)); 466 } 467 FcPatternAddCharSet(pat, FC_CHARSET, cs); 468 match = FcFontMatch(NULL, pat, &result); 469 char *file; 470 FcPatternGetString(match, FC_FILE, 0, &file); 471 uint16_t font_id = ltk_get_font(tm, file); 472 khint_t k = kh_get(fontstruct, tm->font_cache, font_id); 473 run->font = kh_value(tm->font_cache, k); 474 FcPatternDestroy(match); 475 FcPatternDestroy(pat); 476 ltk_text_run_shape(tm, run, tl->font_size, font_id, &y_max); 477 if (tl->y_max < y_max) 478 tl->y_max = y_max; 479 /* tr->start_y is -y_min */ 480 if (tl->y_min > -tr->start_y) 481 tl->y_min = -tr->start_y; 482 tl->w += tr->w; 483 run = run->next; 484 }; 485 tl->h = tl->y_max - tl->y_min; 486 487 /* calculate the actual position of the characters */ 488 run = tl->runs; 489 int x = 0; 490 LtkGlyph *glyph; 491 while (run) { 492 int cur_x = x + run->start_x; 493 int cur_y = tl->h - tl->y_max; /* baseline (I think?) */ 494 for (int i = 0; i < run->len; i++) { 495 glyph = run->glyphs[i]; 496 glyph->x_abs = cur_x + glyph->info->xoff + glyph->x_offset; 497 glyph->y_abs = cur_y - glyph->info->yoff - glyph->y_offset; 498 cur_x += glyph->x_advance; 499 cur_y -= glyph->y_advance; 500 } 501 x += run->w; 502 run = run->next; 503 } 504 } 505 506 /* FIXME: Don't destroy fonts, etc. every time the line is recalculated */ 507 /* Maybe decrease font refs but don't destroy until after the runs have been re-shaped */ 508 void 509 ltk_text_run_destroy(struct ltk_text_line *tl, struct ltk_text_run *tr) { 510 khash_t(glyphinfo) *gcache; 511 LtkFont *font; 512 LtkGlyph *glyph; 513 khint_t k; 514 k = kh_get(glyphinfo, ltk_global->tm->glyph_cache, tr->font_id << 16 + tl->font_size); 515 gcache = kh_value(ltk_global->tm->glyph_cache, k); 516 for (int i = 0; i < tr->len; i++) { 517 glyph = tr->glyphs[i]; 518 if (--glyph->info->refs < 1) { 519 k = kh_get(glyphinfo, cache, glyph->info->id); 520 kh_del(glyphinfo, cache, k); 521 ltk_destroy_glyph_info(glyph->info); 522 } 523 } 524 k = kh_get(fontstruct, ltk_global->tm->font_cache, tr->font_id); 525 font = kh_value(ltk_global->tm->font_cache, k); 526 if (--font->refs < 1) { 527 kh_del(fontstruct, ltk_global->tm->font_cache, k); 528 ltk_destroy_font(font); 529 } 530 free(tr->glyphs); 531 free(tr); 532 } 533 534 void 535 ltk_text_line_destroy_runs(struct ltk_text_line *tl) { 536 struct ltk_text_run *cur, *last; 537 cur = tl->first_run; 538 while (cur) { 539 last = cur; 540 ltk_text_run_destroy(cur); 541 cur = last->next; 542 } 543 } 544 545 static void 546 ltk_text_line_recalculate(LtkTextManager *tm, struct ltk_text_line *tl) { 547 ltk_gap_buffer_clear_uint32(tl->vis_buf); 548 ltk_gap_buffer_clear_int(tl->log2vis); 549 ltk_gap_buffer_clear_int(tl->vis2log); 550 size_t gap_pos = tl->log_buf->gap_left; 551 size_t gap_end = tl->log_buf->buf_size - tl->log_buf->gap_size; 552 ltk_gap_buffer_move_gap_uint32(tl->log_buf, gap_end); 553 fribidi_log2vis( 554 tl->log_buf, tl->log_buf->buf_size - tl->log_buf->gap_size, 555 &tl->dir, tl->vis_buf, tl->log2vis, tl->vis2log, tl->bidi_levels 556 ); 557 ltk_gap_buffer_move_gap_uint32(tl->log_buf, gap_pos); 558 ltk_text_line_destroy_runs(tl); /* FIXME: IMPLEMENT */ 559 ltk_text_line_itemize(tl); 560 ltk_text_line_shape(tm, tl); 561 } 562 563 void 564 ltk_text_line_insert_text(struct ltk_text_line *tl, uint32_t *text, size_t len) { 565 /* check if any characters have a different script, only recalc then */ 566 /* 567 hb_unicode_funcs_t *uf= hb_unicode_funcs_get_default(); 568 struct ltk_text_run *run = tl->cur_run; 569 int recalc = 0; 570 hb_script_t script; 571 for (int i = 0; i < len; i++) { 572 scr = hb_unicode_script(uf, text[i]); 573 if (script != run->script && 574 script != HB_SCRIPT_INHERITED && 575 script != HB_SCRIPT_COMMON) { 576 recalc = 1; 577 } 578 } 579 */ 580 ltk_gap_buffer_insert_uint32(tl->log_buf, text, 0, len); 581 if (len > tl->scripts->gap-size) 582 ltk_gap_buffer_resize_gap_script(tl->scripts, len + 8); 583 hb_unicode_funcs_t *ufuncs = hb_unicode_funcs_get_default(); 584 for (int i = 0; i < len; i++) { 585 ltk_gap_buffer_insert_single_script( 586 tl->scripts, hb_unicode_script(ufuncs, text[i])); 587 } 588 if (len > tl->vis_buf->gap_size) 589 ltk_gap_buffer_resize_gap_uint32(tl->vis_buf, len + 8); 590 if (len > tl->log2vis->gap_size) 591 ltk_gap_buffer_resize_gap_int(tl->log2vis, len + 8); 592 if (len > tl->vis2log->gap_size) 593 ltk_gap_buffer_resize_gap_int(tl->vis2log, len + 8); 594 if (len + tl->bidi_levels->len > tl->bidi_levels->buf_size) 595 ltk_array_resize_levels(tl->bidi_levels, tl->bidi_levels->len + len + 8); 596 tl->len += len; 597 ltk_text_line_recalculate(tl); 598 } 599 600 struct ltk_text_line * 601 ltk_text_line_create(void) { 602 struct ltk_text_line *line = malloc(sizeof(struct ltk_text_line)); 603 if (!line) goto error; 604 line->log_buf = ltk_gap_buffer_create_uint32(); 605 line->scripts = ltk_gap_buffer_create_script(); 606 line->vis_buf = ltk_gap_buffer_create_uint32(); 607 line->log2vis = ltk_gap_buffer_create_int(); 608 line->vis2log = ltk_gap_buffer_create_int(); 609 line->bidi_levels = ltk_array_create_levels(8); 610 line->wrap_indeces = ltk_array_create_int(1); 611 line->runs = NULL; 612 line->cur_run = NULL; 613 line->next = NULL; 614 line->height = 0; 615 line->dir = FRIBIDI_TYPE_ON; 616 line->len = 0; 617 error: 618 (void)fprintf(stderr, "No memory left while creating text line\n"); 619 exit(1); 620 } 621 622 struct ltk_text_buffer * 623 ltk_text_buffer_create(void) { 624 struct ltk_text_buffer *buf = malloc(sizeof(struct ltk_text_buffer)); 625 if (!buf) { 626 (void)fprintf(stderr, "No memory while creating text buffer\n"); 627 exit(1); 628 } 629 buf->head = ltk_text_line_create(); 630 buf->cur_line = buf->head; 631 buf->line_gap = 0; 632 } 633 634 void 635 ltk_text_line_destroy(struct ltk_text_line *tl) { 636 ltk_gap_buffer_destroy_uint32(tl->log_buf); 637 ltk_gap_buffer_destroy_uint32(tl->vis_buf); 638 ltk_gap_buffer_destroy_script(tl->scripts); 639 ltk_gap_buffer_destroy_int(tl->log2vis); 640 ltk_gap_buffer_destroy_int(tl->vis2log); 641 ltk_array_destroy_level(tl->bidi_levels); 642 ltk_array_destroy_int(tl->wrap_indeces); 643 ltk_text_line_destroy_runs(tl); 644 if (tl->img) XDestroyImage(tl->img); 645 free(tl); 646 }