ltkx

GUI toolkit for X11 (WIP)
git clone git://lumidify.org/ltkx.git
Log | Files | Refs | README | LICENSE

commit 57d85ccfe4c9ac3de63dab6979e65dbe0696f4b7
parent 05e4c9d086c3db2ad726f789cee1b9f8d7ea07b9
Author: lumidify <nobody@lumidify.org>
Date:   Fri,  8 May 2020 20:36:38 +0200

Refactor gap buffer

Diffstat:
M.gitignore | 2++
Agap_buffer.h | 156+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest1.c | 4++--
Mtext-hb.c | 15+++++++++------
Mtextedit_wip.c | 304++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mtextedit_wip.h | 41++++++++++++++++++++++++++++++++---------
6 files changed, 364 insertions(+), 158 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1 +1,3 @@ test +*.o +test1 diff --git a/gap_buffer.h b/gap_buffer.h @@ -0,0 +1,156 @@ +/* + * This file is part of the Lumidify ToolKit (LTK) + * Copyright (c) 2020 lumidify <nobody@lumidify.org> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _LTK_GAP_BUFFER_H_ +#define _LTK_GAP_BUFFER_H_ + +#include <stdio.h> +#include <stdlib.h> + +#define LTK_INIT_GAP_BUFFER_DECL(type) \ +struct ltk_gap_buffer_##type## { \ + type *buf; \ + size_t buf_size; \ + size_t gap_left; \ + size_t gap_size; \ +}; \ + \ +struct ltk_gap_buffer_##type## * ltk_gap_buffer_create_##type##(void); \ +struct ltk_gap_buffer_##type## * \ +ltk_gap_buffer_create_from_data_##type##(type *data, size_t len); \ +void ltk_gap_buffer_resize_gap_##type##(struct ltk_gap_buffer *gb, int len); \ +void ltk_gap_buffer_insert_##type##(struct ltk_gap_buffer_##type## *gb, \ + type *new, size_t start, size_t len); \ +void ltk_gap_buffer_insert_single_##type##( \ + struct ltk_gap_buffer_##type## *gb, type new); \ +void ltk_gap_buffer_move_gap_##type##( \ + struct ltk_gap_buffer_##type## *gb, size_t pos); \ +void ltk_gap_buffer_destroy_##type##(struct ltk_gap_buffer_##type## *gb); + +#define LTK_INIT_GAP_BUFFER_IMPL(type) \ +struct ltk_gap_buffer_##type## * \ +ltk_gap_buffer_create_##type##(void) { \ + struct ltk_gap_buffer_##type## *gb = \ + malloc(sizeof(struct ltk_gap_buffer)); \ + if (!gb) \ + goto error; \ + gb->buf = malloc(8 * sizeof(type)); \ + if (!gb->buf) \ + goto error; \ + gb->buf_size = 8; \ + gb->gap_left = 0; \ + gb->gap_size = 8; \ + return gb; \ +error: \ + (void)fprintf(stderr, "Out of memory while trying to" \ + "allocate gap buffer\n"); \ + exit(1); \ +} \ + \ +struct ltk_gap_buffer_##type## * \ +ltk_gap_buffer_create_from_data_##type##(type *data, size_t len) { \ + struct ltk_gap_buffer_##type## *gb = \ + malloc(sizeof(struct ltk_gap_buffer)); \ + if (!gb) { \ + (void)fprintf(stderr, "Out of memory while trying to" \ + "allocate gap buffer\n"); \ + exit(1); \ + } \ + gb->buf = data; \ + gb->buf_size = len; \ + gb->gap_left = 0; \ + gb->gap_size = 0; \ + return gb; \ +} \ + \ +void \ +ltk_gap_buffer_resize_gap_##type##(struct ltk_gap_buffer *gb, int len) { \ + /* FIXME: Should this use realloc? It's usually more efficient, but \ + in this case, I would still need to copy the part after the gap \ + manually, so it could potentially be copied twice, which really \ + wouldn't be good. Maybe use realloc if only a small part is after \ + the gap and just regular malloc otherwise? */ \ + int new_size = gb->buf_size - gb->gap-size + len; \ + struct ltk_gap_buffer_##type## *new = malloc(new_size * sizeof(type)); \ + if (!new) { \ + (void)fprintf(stderr, "Out of memory while trying to" \ + "resize gap buffer\n"); \ + exit(1); \ + } \ + for (int i = 0; i < gb->gap_left; i++) { \ + new[i] = gb->buf[i]; \ + } \ + for (int i = gb->gap_left + gb->gap_size; i < gb->buf_size) { \ + new[i - gb->gap_size + len] = gb->buf[i]; \ + } \ + free(gb->buf); \ + gb->buf = new; \ +} \ + \ +void \ +ltk_gap_buffer_insert_##type##(struct ltk_gap_buffer_##type## *gb, \ + type *new, size_t start, size_t len) { \ + if (gb->gap_size < len) \ + ltk_gap_buffer_resize_gap_##type##(gb, len + 8); \ + for (int i = 0; i < len; i++) { \ + gb->buf[gb->gap_left + i] = new[start + i]; \ + } \ + gb->gap_left = gb->gap_left + len; \ + gb->gap_size -= len; \ +} \ + \ +void \ +ltk_gap_buffer_insert_single_##type##( \ + struct ltk_gap_buffer_##type## *gb, type new) { \ + ltk_gap_buffer_insert_##type##(gb, &new, 0, 1); \ +} \ + \ +void \ +ltk_gap_buffer_move_gap_##type##( \ + struct ltk_gap_buffer_##type## *gb, size_t pos) { \ + if (pos == gb->gap_left) \ + return; \ + if (pos < 0 || pos > gb->buf_size - gb->gap_size) { \ + (void)fprintf(stderr, "Index out of range while moving" \ + "gap buffer gap\n"); \ + return; \ + } \ + if (pos >= gb->gap_left) { \ + for (int i = gb->gap_left; i < pos) { \ + gb->buf[i] = gb->buf[i + gb->gap_size]; \ + } \ + } else { \ + for (int i = gb->gap_left - 1; i >= pos; i--) { \ + gb->buf[i + gb->gap_size] = gb->buf[i]; \ + } \ + } \ + gb->gap_left = pos; \ +} \ + \ +void \ +ltk_gap_buffer_destroy_##type##(struct ltk_gap_buffer_##type## *gb) { \ + free(gb->buf); \ + free(gb); \ +} + +#endif /* _LTK_GAP_BUFFER_H_ */ diff --git a/test1.c b/test1.c @@ -34,8 +34,8 @@ int main(int argc, char *argv[]) LtkButton *button3 = ltk_create_button(window1, "I'm a button!", NULL); ltk_grid_widget(button3, grid1, 1, 0, 1, 1, LTK_STICKY_TOP | LTK_STICKY_BOTTOM | LTK_STICKY_RIGHT); //LtkButton *button4 = ltk_create_button(window1, "I'm a button!", NULL); - //LtkButton *button4 = ltk_create_button(window1, "ہمارے بارے میں blablabla", NULL); - LtkButton *button4 = ltk_create_button(window1, "پَیدایش", NULL); + LtkButton *button4 = ltk_create_button(window1, "ہمارے بارے میں blablabla", NULL); + //LtkButton *button4 = ltk_create_button(window1, "پَیدایش", NULL); ltk_grid_widget(button4, grid1, 1, 1, 1, 1, LTK_STICKY_LEFT | LTK_STICKY_BOTTOM); ltk_mainloop(); } diff --git a/text-hb.c b/text-hb.c @@ -338,11 +338,17 @@ ltk_create_text_line(LtkTextManager *tm, char *text, uint16_t fontid, uint16_t s } FriBidiCharType *pbase_dir = malloc(sizeof(FriBidiCharType) * ulen); for (int i = 0; i < ulen; i++) { - pbase_dir[i] = FRIBIDI_TYPE_ON; + pbase_dir[i] = i; } + FriBidiCharType pbase_dir1 = FRIBIDI_TYPE_ON; FriBidiChar *vis_str = malloc(sizeof(FriBidiChar) * ulen); ulen = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, text, strlen(text), log_str); - fribidi_log2vis(log_str, ulen, pbase_dir, vis_str, NULL, NULL, NULL); + fribidi_log2vis(log_str, ulen, &pbase_dir1, vis_str, pbase_dir, NULL, NULL); + printf("%d\n", pbase_dir1); + for (int i = 0; i < ulen; i++) { + printf("%d ", pbase_dir[i]); + } + printf("\n"); hb_unicode_funcs_t *ufuncs = hb_unicode_funcs_get_default(); hb_script_t cur_script = hb_unicode_script(ufuncs, vis_str[0]); @@ -500,7 +506,7 @@ ltk_create_text_segment(LtkTextManager *tm, uint32_t *text, unsigned int len, ui hb_direction_t dir = hb_script_get_horizontal_direction(script); hb_buffer_set_direction(buf, dir); hb_buffer_set_script(buf, script); - hb_buffer_add_codepoints(buf, ts->str, len, 1, len-1); + hb_buffer_add_codepoints(buf, ts->str, len, 0, len); /* According to https://harfbuzz.github.io/the-distinction-between-levels-0-and-1.html * this should be level 1 clustering instead of level 0 */ hb_buffer_set_cluster_level(buf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); @@ -518,9 +524,6 @@ ltk_create_text_segment(LtkTextManager *tm, uint32_t *text, unsigned int len, ui /* magic, do not touch */ LtkGlyph *glyph; for (int i = 0; i < text_len; i++) { - if (len == 7) { - printf("%d\n", ginf[i].cluster); - } gi = &ginf[i]; gp = &gpos[i]; glyph = malloc(sizeof(LtkGlyph)); diff --git a/textedit_wip.c b/textedit_wip.c @@ -316,143 +316,6 @@ ltk_get_font(LtkTextManager *tm, char *path) return id; } -/* FIXME: allow to either use fribidi for basic shaping and don't use harfbuzz then, - or just use harfbuzz (then fribidi doesn't need to do any shaping) */ -LtkTextLine * -ltk_create_text_line(LtkTextManager *tm, char *text, uint16_t fontid, uint16_t size) -{ - /* NOTE: This doesn't actually take fontid into account right now - should it? */ - LtkTextLine *tl = malloc(sizeof(LtkTextLine)); - tl->start_segment = NULL; - LtkTextSegment *cur_ts = NULL; - LtkTextSegment *new_ts = NULL; - uint16_t cur_font_id = fontid; - int k; - LtkFont *font; - - unsigned int ulen = u8_strlen(text); - FriBidiChar *log_str = malloc(sizeof(FriBidiChar) * ulen); - size_t inc = 0; - for (int i = 0; i < ulen; i++) { - log_str[i] = u8_nextmemchar(text, &inc); - } - FriBidiCharType *pbase_dir = malloc(sizeof(FriBidiCharType) * ulen); - for (int i = 0; i < ulen; i++) { - pbase_dir[i] = FRIBIDI_TYPE_ON; - } - FriBidiChar *vis_str = malloc(sizeof(FriBidiChar) * ulen); - ulen = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, text, strlen(text), log_str); - fribidi_log2vis(log_str, ulen, pbase_dir, vis_str, NULL, NULL, NULL); - - hb_unicode_funcs_t *ufuncs = hb_unicode_funcs_get_default(); - hb_script_t cur_script = hb_unicode_script(ufuncs, vis_str[0]); - hb_script_t last_script = cur_script; - size_t pos = 0; - size_t last_pos = 0; - size_t start_pos = 0; - uint32_t ch; - - for (int p = 0; p <= ulen; p++) { - cur_script = hb_unicode_script(ufuncs, vis_str[p]); - if (p == ulen || - (last_script != cur_script && - cur_script != HB_SCRIPT_INHERITED && - cur_script != HB_SCRIPT_COMMON)) { - FcPattern *pat = FcPatternDuplicate(tm->fcpattern); - FcPattern *match; - FcResult result; - FcPatternAddBool(pat, FC_SCALABLE, 1); - FcConfigSubstitute(NULL, pat, FcMatchPattern); - FcDefaultSubstitute(pat); - FcCharSet *cs = FcCharSetCreate(); - for (int i = start_pos; i < p; i++) { - FcCharSetAddChar(cs, vis_str[i]); - } - FcPatternAddCharSet(pat, FC_CHARSET, cs); - match = FcFontMatch(NULL, pat, &result); - char *file; - FcPatternGetString(match, FC_FILE, 0, &file); - cur_font_id = ltk_get_font(tm, file); - k = kh_get(fontstruct, tm->font_cache, cur_font_id); - font = kh_value(tm->font_cache, k); - FcPatternDestroy(match); - FcPatternDestroy(pat); - // handle case that this is the last character - if (p == ulen) { - last_script = cur_script; - } - /* FIXME: There should be better handling for cases - where an error occurs while creating the segment */ - new_ts = ltk_create_text_segment( - tm, vis_str + start_pos, - p - start_pos, cur_font_id, - size, last_script - ); - if (!new_ts) continue; - new_ts->next = NULL; - if (!tl->start_segment) tl->start_segment = new_ts; - if (cur_ts) cur_ts->next = new_ts; - cur_ts = new_ts; - - start_pos = p; - last_script = cur_script; - } - } - - free(vis_str); - free(log_str); - free(pbase_dir); - - /* calculate width of text line - NOTE: doesn't work with mixed horizontal and vertical text */ - LtkTextSegment *ts = tl->start_segment; - int is_hor = HB_DIRECTION_IS_HORIZONTAL(ts->dir); - tl->y_max = tl->x_max = INT_MIN; - tl->y_min = tl->x_min = INT_MAX; - tl->w = tl->h = 0; - while (ts) { - if (HB_DIRECTION_IS_HORIZONTAL(ts->dir) != is_hor) { - (void)fprintf(stderr, "WARNING: mixed horizontal/vertical text is not supported; ignoring\n"); - continue; - } - if (is_hor) { - if (tl->y_max < ts->y_max) { - tl->y_max = ts->y_max; - } - if (tl->y_min > ts->y_min) { - tl->y_min = ts->y_min; - } - tl->w += ts->w; - } else { - if (tl->x_max < ts->x_max) { - tl->x_max = ts->x_max; - } - if (tl->x_min > ts->x_min) { - tl->x_min = ts->x_min; - } - tl->h += ts->h; - } - ts = ts->next; - } - if (is_hor) { - tl->h = tl->y_max - tl->y_min; - } else { - tl->w = tl->x_max - tl->x_min; - } - - return tl; -} - -void -ltk_destroy_text_line(LtkTextLine *tl) { - LtkTextSegment *last_ts; - LtkTextSegment *cur_ts = tl->start_segment; - while (cur_ts) { - last_ts = cur_ts; - cur_ts = cur_ts->next; - ltk_destroy_text_segment(last_ts); - } -} /* FIXME: could use unsigned int for fontid and size as long as there is code to check neither of them become too large -> in case I want to get rid of uint_16_t, etc. */ @@ -719,19 +582,32 @@ ltk_gap_buffer_create(void) { struct ltk_gap_buffer *gb = malloc(sizeof(struct ltk_gap_buffer)); if (!gb) goto error; - gb->buf = malloc(4 * sizeof(uint32_t)); + gb->buf = malloc(8 * sizeof(uint32_t)); if (!gb->buf) goto error; gb->buf_size = 8; gb->gap_left = 0; gb->gap_size = 8; - gb->gap_end_left = 8; return gb; error: (void)fprintf(stderr, "Out of memory while trying to allocate gap buffer\n"); exit(1); } +struct ltk_gap_buffer * +ltk_gap_buffer_create_from_data(uint32_t *data, size_t len) { + struct ltk_gap_buffer *gb = malloc(sizeof(struct ltk_gap_buffer)); + if (!gb) { + (void)fprintf(stderr, "Out of memory while trying to allocate gap buffer\n"); + exit(1); + } + gb->buf = data; + gb->buf_size = len; + gb->gap_left = 0; + gb->gap_size = 0; + return gb; +} + void ltk_gap_buffer_resize_gap(struct ltk_gap_buffer *gb, int len) { /* FIXME: Should this use realloc? It's usually more efficient, but @@ -748,7 +624,7 @@ ltk_gap_buffer_resize_gap(struct ltk_gap_buffer *gb, int len) { for (int i = 0; i < gb->gap_left; i++) { new[i] = gb->buf[i]; } - for (int i = gb->gap_left + gb->gap_size; i < gb->gap_end_left) { + for (int i = gb->gap_left + gb->gap_size; i < gb->buf_size) { new[i - gb->gap_size + len] = gb->buf[i]; } free(gb->buf); @@ -775,7 +651,7 @@ void ltk_gap_buffer_move_gap(struct ltk_gap_buffer *gb, size_t pos) { if (pos == gb->gap_left) return; - if (pos < 0 || pos >= gb->gap_end_left - gb->gap_size) { + if (pos < 0 || pos > gb->buf_size - gb->gap_size) { (void)fprintf(stderr, "Index out of range while moving gap buffer gap\n"); return; } @@ -796,3 +672,149 @@ ltk_gap_buffer_destroy(struct ltk_gap_buffer *gb) { free(gb->buf); free(gb); } + +void +ltk_text_line_insert_text(struct ltk_text_line *tl, uint32_t *text, size_t len) { + /* check if any characters have a different script, only recalc then */ +} + +void +ltk_text_line_delete_text(struct ltk_text_line *tl, size_t len) { +} + +void +ltk_text_line_delete_cur_cluster(struct ltk_text_line *tl) { +} + +/* FIXME: allow to either use fribidi for basic shaping and don't use harfbuzz then, + or just use harfbuzz (then fribidi doesn't need to do any shaping) */ +LtkTextLine * +ltk_create_text_line(LtkTextManager *tm, char *text, uint16_t fontid, uint16_t size) +{ + /* NOTE: This doesn't actually take fontid into account right now - should it? */ + LtkTextLine *tl = malloc(sizeof(LtkTextLine)); + tl->start_segment = NULL; + LtkTextSegment *cur_ts = NULL; + LtkTextSegment *new_ts = NULL; + uint16_t cur_font_id = fontid; + int k; + LtkFont *font; + + unsigned int ulen = u8_strlen(text); + uint32_t *log_str = malloc(sizeof(uint32_t) * ulen); + size_t inc = 0; + for (int i = 0; i < ulen; i++) { + log_str[i] = u8_nextmemchar(text, &inc); + } + FriBidiChar *vis_str = malloc(sizeof(FriBidiChar) * ulen); + ulen = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, text, strlen(text), log_str); + fribidi_log2vis(log_str, ulen, pbase_dir, vis_str, NULL, NULL, NULL); + + hb_unicode_funcs_t *ufuncs = hb_unicode_funcs_get_default(); + hb_script_t cur_script = hb_unicode_script(ufuncs, vis_str[0]); + hb_script_t last_script = cur_script; + size_t pos = 0; + size_t last_pos = 0; + size_t start_pos = 0; + uint32_t ch; + + for (int p = 0; p <= ulen; p++) { + cur_script = hb_unicode_script(ufuncs, vis_str[p]); + if (p == ulen || + (last_script != cur_script && + cur_script != HB_SCRIPT_INHERITED && + cur_script != HB_SCRIPT_COMMON)) { + FcPattern *pat = FcPatternDuplicate(tm->fcpattern); + FcPattern *match; + FcResult result; + FcPatternAddBool(pat, FC_SCALABLE, 1); + FcConfigSubstitute(NULL, pat, FcMatchPattern); + FcDefaultSubstitute(pat); + FcCharSet *cs = FcCharSetCreate(); + for (int i = start_pos; i < p; i++) { + FcCharSetAddChar(cs, vis_str[i]); + } + FcPatternAddCharSet(pat, FC_CHARSET, cs); + match = FcFontMatch(NULL, pat, &result); + char *file; + FcPatternGetString(match, FC_FILE, 0, &file); + cur_font_id = ltk_get_font(tm, file); + k = kh_get(fontstruct, tm->font_cache, cur_font_id); + font = kh_value(tm->font_cache, k); + FcPatternDestroy(match); + FcPatternDestroy(pat); + // handle case that this is the last character + if (p == ulen) { + last_script = cur_script; + } + /* FIXME: There should be better handling for cases + where an error occurs while creating the segment */ + new_ts = ltk_create_text_segment( + tm, vis_str + start_pos, + p - start_pos, cur_font_id, + size, last_script + ); + if (!new_ts) continue; + new_ts->next = NULL; + if (!tl->start_segment) tl->start_segment = new_ts; + if (cur_ts) cur_ts->next = new_ts; + cur_ts = new_ts; + + start_pos = p; + last_script = cur_script; + } + } + + free(vis_str); + free(log_str); + + /* calculate width of text line + NOTE: doesn't work with mixed horizontal and vertical text */ + LtkTextSegment *ts = tl->start_segment; + int is_hor = HB_DIRECTION_IS_HORIZONTAL(ts->dir); + tl->y_max = tl->x_max = INT_MIN; + tl->y_min = tl->x_min = INT_MAX; + tl->w = tl->h = 0; + while (ts) { + if (HB_DIRECTION_IS_HORIZONTAL(ts->dir) != is_hor) { + (void)fprintf(stderr, "WARNING: mixed horizontal/vertical text is not supported; ignoring\n"); + continue; + } + if (is_hor) { + if (tl->y_max < ts->y_max) { + tl->y_max = ts->y_max; + } + if (tl->y_min > ts->y_min) { + tl->y_min = ts->y_min; + } + tl->w += ts->w; + } else { + if (tl->x_max < ts->x_max) { + tl->x_max = ts->x_max; + } + if (tl->x_min > ts->x_min) { + tl->x_min = ts->x_min; + } + tl->h += ts->h; + } + ts = ts->next; + } + if (is_hor) { + tl->h = tl->y_max - tl->y_min; + } else { + tl->w = tl->x_max - tl->x_min; + } + + return tl; +} + +void +ltk_destroy_text_line(LtkTextLine *tl) { + LtkTextSegment *last_ts; + LtkTextSegment *cur_ts = tl->start_segment; + while (cur_ts) { + last_ts = cur_ts; + cur_ts = cur_ts->next; + ltk_destroy_text_segment(last_ts); + } +} diff --git a/textedit_wip.h b/textedit_wip.h @@ -48,18 +48,41 @@ struct ltk_gap_buffer { size_t buf_size; size_t gap_left; size_t gap_size; - size_t gap_end_left; +}; + +/* FIXME: macro version of gap buffer */ +struct ltk_text_run { + /* maybe make gap buffer of glyphs? */ + struct ltk_glyph *head_glyph; + struct ltk_glyph *cur_glyph; + struct ltk_text_run *next; + LtkFont *font; + unsigned int w; + unsigned int h; + int start_x; + int start_y; + int x_min; + int y_min; + int x_max; + int y_max; + hb_script_t script; +} + +struct ltk_text_line { + struct ltk_gap_buffer *text_buf; /* buffer of the logical text */ + struct ltk_gap_buffer *visual_text; /* buffer of visual text */ + /* still need log2vis and vis2log */ + struct ltk_text_run *runs; /* first node in the linked list of runs */ + struct ltk_text_run *cur_run; /* current node in the linked list of runs */ + struct ltk_text_line *next; /* next text line in the buffer */ + unsigned int height; /* height of the line (including wrapping) */ + FribidiCharType dir; /* overall paragraph direction */ }; struct ltk_text_buffer { - uint32_t *buffer; - struct ltk_glyph *glyphs; - size_t buf_size; - size_t buf_left; - size_t buf_gap_size; - size_t glyphs_size; - size_t glyphs_left; - size_t glyphs_gap_size; + struct ltk_text_line *head; + struct ltk_text_line *cur_line; + unsigned int line_gap; }; typedef struct {