URI: 
       text_buffer.c - ltkx - GUI toolkit for X11 (old)
  HTML git clone git://lumidify.org/ltkx.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/ltkx.git (encrypted, but very slow)
  HTML git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltkx.git (over tor)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR 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 }