URI: 
       tModify cache to cache individual lines - ledit - Text editor (WIP)
  HTML git clone git://lumidify.org/ledit.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/git/ledit.git (encrypted, but very slow)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 88b7f6c08a1e2a47f33aa5b4d33f18df60b5011f
   DIR parent e9c86a9b95b0a349d3f34f2a15dd3afffafc3b52
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Sat, 10 Apr 2021 21:18:46 +0200
       
       Modify cache to cache individual lines
       
       Diffstat:
         M ledit.c                             |     291 +++++++++++--------------------
       
       1 file changed, 106 insertions(+), 185 deletions(-)
       ---
   DIR diff --git a/ledit.c b/ledit.c
       t@@ -32,8 +32,6 @@ struct key {
                void (*func)(void); /* callback function */
        };
        
       -#define MAX_CACHE_PIXELS 100
       -
        static struct {
                Display *dpy;
                GC gc;
       t@@ -56,17 +54,31 @@ static struct {
                Atom wm_delete_msg;
        } state;
        
       +struct cache_pixmap {
       +        Pixmap pixmap;
       +        XftDraw *draw;
       +        int w, h;
       +        int line;
       +};
       +
        /* FIXME: possibly use at least 32 bits int? */
        static struct {
       -        Pixmap pix;
       -        XftDraw *draw;
       -        int pix_w, pix_h;
       -        long start_offset;
       -        int valid_height;
       -        int begin_line, begin_softline;
       -        int dirty;
       +        struct cache_pixmap *entries;
       +        int entries_num;
       +        int cur_replace_index;
        } cache;
        
       +static size_t lines_num = 0;
       +static size_t lines_cap = 0;
       +
       +static int cur_line = 0;
       +static int cur_subline = 0;
       +static int cur_index = 0;
       +static int trailing = 0;
       +static long total_height = 0;
       +static double cur_display_offset = 0;
       +
       +
        static void mainloop(void);
        static void setup(int argc, char *argv[]);
        static void cleanup(void);
       t@@ -94,24 +106,57 @@ static struct line {
                int w;
                int h;
                long y_offset;
       -        /*
       -        XftDraw *draw;
       -        Pixmap pix;
       -        unsigned int pix_w;
       -        unsigned int pix_h;
       -        */
       +        int cache_index;
                char dirty;
        } *lines = NULL;
        
       -static size_t lines_num = 0;
       -static size_t lines_cap = 0;
       +static void
       +init_cache(void) {
       +        /* FIXME: prevent overflow */
       +        cache.entries = malloc(20 * sizeof(struct cache_pixmap));
       +        if (!cache.entries) exit(1);
       +        for (int i = 0; i < 20; i++) {
       +                cache.entries[i].pixmap = None;
       +                cache.entries[i].line = -1;
       +        }
       +        cache.entries_num = 20;
       +        cache.cur_replace_index = -1;
       +}
        
       -static int cur_line = 0;
       -static int cur_subline = 0;
       -static int cur_index = 0;
       -static int trailing = 0;
       -static long total_height = 0;
       -static double cur_display_offset = 0;
       +static void
       +assign_free_cache_index(int line) {
       +        int found = 0;
       +        int real_index;
       +        int tmp_line;
       +        /* start at 1 because the cache->cur_replace_index is actually the last entry that was replaced */
       +        for (int i = 1; i <= cache.entries_num; i++) {
       +                real_index = (i + cache.cur_replace_index) % cache.entries_num;
       +                tmp_line = cache.entries[real_index].line;
       +                /* replace line when entry isn't assigned or currently assigned line is not visible */
       +                if (tmp_line == -1 ||
       +                    (tmp_line >= 0 &&
       +                     (lines[tmp_line].y_offset >= cur_display_offset + state.h ||
       +                      lines[tmp_line].y_offset + lines[tmp_line].h <= cur_display_offset))) {
       +                        if (tmp_line >= 0)
       +                                lines[tmp_line].cache_index = -1;
       +                        cache.entries[real_index].line = line;
       +                        cache.cur_replace_index = real_index;
       +                        lines[line].cache_index = real_index;
       +                        return;
       +                }
       +        }
       +
       +        /* no free entry found, increase cache size */
       +        cache.entries = realloc(cache.entries, cache.entries_num * 2 * sizeof(struct cache_pixmap));
       +        if (!cache.entries) exit(1);
       +        real_index = cache.entries_num;
       +        for (size_t i = cache.entries_num + 1; i < cache.entries_num * 2; i++) {
       +                cache.entries[i].line = -1;
       +        }
       +        cache.entries_num *= 2;
       +        cache.entries[real_index].line = line;
       +        lines[line].cache_index = real_index;
       +}
        
        static void
        init_line(struct line *l) {
       t@@ -122,97 +167,16 @@ init_line(struct line *l) {
                pango_layout_set_wrap(l->layout, PANGO_WRAP_WORD_CHAR);
                l->text = NULL;
                l->cap = l->len = 0;
       -        /*l->pix = None;*/
       +        l->cache_index = -1;
       +        l->dirty = 1;
                /* FIXME: does this set line height reasonably when no text yet? */
                pango_layout_get_pixel_size(l->layout, &l->w, &l->h);
                l->y_offset = 0;
       -        //l->dirty = 1;
        }
        
        static void recalc_cur_line_size(void);
        static void recalc_line_size_absolute(void);
        
       -enum CachePosition {
       -        CACHE_NONE,
       -        CACHE_BOTH,
       -        CACHE_TOP,
       -        CACHE_BOTTOM
       -};
       -
       -static void redraw_cache_complete(enum CachePosition pos);
       -static void redraw_cache_after_cur_line(void);
       -static void redraw_cache_only_cur_line(void);
       -
       -#define MAX_INT(a, b) ((a) > (b) ? (a) : (b))
       -#define MIN_INT(a, b) ((a) < (b) ? (a) : (b))
       -
       -/* FIXME: Implement pos */
       -static void
       -redraw_cache_complete(enum CachePosition pos) {
       -        long start = cur_display_offset > MAX_CACHE_PIXELS ? (long)(cur_display_offset) - MAX_CACHE_PIXELS : 0;
       -        long end = (long)(cur_display_offset) + state.h + MAX_CACHE_PIXELS;
       -        int start_line = 0;
       -        int end_line = 0;
       -
       -        /* FIXME: make this more efficient - we can't start at cur_line
       -         * because that may be off screen */
       -        /*
       -        for (start_line = cur_line;
       -             lines[start_line].y_offset > cur_display_offset;
       -             start_line--) {
       -        }
       -
       -        for (end_line = cur_line;
       -             end_line < lines_num &&
       -             lines[end_line].y_offset + lines[end_line].h < cur_display_offset + state.h;
       -             end_line++) {
       -        }
       -        */
       -
       -        for (start_line = 0;
       -             start_line < lines_num - 1 &&
       -             lines[start_line].y_offset + lines[start_line].h <= cur_display_offset;
       -             start_line++) {
       -                /* NOP */
       -        }
       -        for (end_line = start_line;
       -             end_line < lines_num - 1 &&
       -             lines[end_line].y_offset + lines[end_line].h < cur_display_offset + state.h;
       -             end_line++) {
       -                /* NOP */
       -        }
       -
       -        if (lines[start_line].y_offset < start) {
       -                printf("FIX THIS CODE 1!\n");
       -        }
       -        if (lines[end_line].y_offset + lines[end_line].h > end) {
       -                printf("FIX THIS CODE 2!\n");
       -        }
       -
       -        /* FIXME: only wipe what is necessary */
       -        XftDrawRect(cache.draw, &state.bg, 0, 0, cache.pix_w, cache.pix_h);
       -        int cur_y = 0;
       -        for (int i = start_line; i <= end_line; i++) {
       -                if (lines[i].w > cache.pix_w || cur_y + lines[i].h > cache.pix_h) {
       -                        break; /* should never happen */
       -                }
       -                pango_xft_render_layout(cache.draw, &state.fg, lines[i].layout, 0, cur_y * PANGO_SCALE);
       -                cur_y += lines[i].h;
       -        }
       -        cache.valid_height = cur_y;
       -        cache.start_offset = lines[start_line].y_offset;
       -        cache.begin_line = start_line;
       -        cache.dirty = 0;
       -}
       -
       -static void
       -redraw_cache_after_cur_line(void) {
       -}
       -
       -static void
       -redraw_cache_only_cur_line(void) {
       -}
       -
        static void
        insert_text(struct line *l, int index, char *text, int len) {
                if (len == -1)
       t@@ -229,33 +193,35 @@ insert_text(struct line *l, int index, char *text, int len) {
                l->len += len;
                pango_layout_set_text(l->layout, l->text, l->len);
                recalc_cur_line_size();
       -        cache.dirty = 1;
       +        l->dirty = 1;
        }
        
        static void insert_line_entry(int index);
        
        static void
       -render_line(struct line *l) {
       +render_line(int line) {
                /* FIXME: check for <= 0 on size */
       -        /*
       -        if (l->pix == None) {
       -                l->pix = XCreatePixmap(state.dpy, state.back_buf, l->w + 10, l->h + 10, state.depth);
       -                l->pix_w = l->w + 10;
       -                l->pix_h = l->h + 10;
       -                l->draw = XftDrawCreate(state.dpy, l->pix, state.vis, state.cm);
       -        } else if (l->pix_w < l->w || l->pix_h < l->h) {
       -                int new_w = l->w > l->pix_w ? l->w + 10 : l->pix_w + 10;
       -                int new_h = l->h > l->pix_h ? l->h + 10 : l->pix_h + 10;
       -                XFreePixmap(state.dpy, l->pix);
       -                l->pix = XCreatePixmap(state.dpy, state.back_buf, new_w, new_h, state.depth);
       -                l->pix_w = new_w;
       -                l->pix_h = new_h;
       -                XftDrawChange(l->draw, l->pix);
       -        }
       -        XftDrawRect(l->draw, &state.bg, 0, 0, l->w, l->h);
       -        pango_xft_render_layout(l->draw, &state.fg, l->layout, 0, 0);
       +        struct line *l = &lines[line];
       +        if (l->cache_index == -1)
       +                assign_free_cache_index(line);
       +        struct cache_pixmap *pix = &cache.entries[l->cache_index];
       +        if (pix->pixmap == None) {
       +                pix->pixmap = XCreatePixmap(state.dpy, state.back_buf, l->w + 10, l->h + 10, state.depth);
       +                pix->w = l->w + 10;
       +                pix->h = l->h + 10;
       +                pix->draw = XftDrawCreate(state.dpy, pix->pixmap, state.vis, state.cm);
       +        } else if (pix->w < l->w || pix->h < l->h) {
       +                int new_w = l->w > pix->w ? l->w + 10 : pix->w + 10;
       +                int new_h = l->h > pix->h ? l->h + 10 : pix->h + 10;
       +                XFreePixmap(state.dpy, pix->pixmap);
       +                pix->pixmap = XCreatePixmap(state.dpy, state.back_buf, new_w, new_h, state.depth);
       +                pix->w = new_w;
       +                pix->h = new_h;
       +                XftDrawChange(pix->draw, pix->pixmap);
       +        }
       +        XftDrawRect(pix->draw, &state.bg, 0, 0, l->w, l->h);
       +        pango_xft_render_layout(pix->draw, &state.fg, l->layout, 0, 0);
                l->dirty = 0;
       -        */
        }
        
        static void
       t@@ -274,7 +240,6 @@ append_line(int text_index, int line_index) {
                if (text_index != -1) {
                        struct line *l = &lines[line_index];
                        int len = l->len - text_index;
       -                //new_l->pix = None;
                        new_l->len = len;
                        new_l->cap = len + 10;
                        new_l->text = malloc(new_l->cap);
       t@@ -328,11 +293,13 @@ set_line_cursor_attrs(int line, int index) {
                } else {
                        pango_layout_set_attributes(lines[line].layout, basic_attrs);
                }
       +        lines[line].dirty = 1;
        }
        
        static void
        wipe_line_cursor_attrs(int line) {
                pango_layout_set_attributes(lines[line].layout, basic_attrs);
       +        lines[line].dirty = 1;
        }
        
        static void
       t@@ -477,34 +444,16 @@ mainloop(void) {
                                XSetForeground(state.dpy, state.gc, state.bg.pixel);
                                XFillRectangle(state.dpy, state.back_buf, state.gc, 0, 0, state.w, state.h);
                                int h = 0;
       -                        if (cache.dirty)
       -                                redraw_cache_complete(CACHE_BOTH);
       +
                                /*int cur_line_height = 0;*/
       -                        /*
                                int tmp_w, tmp_h;
                                int cur_line_y = 0;
                                int cursor_displayed = 0;
                                for (int i = 0; i < lines_num; i++) {
       -                                if (lines[i].dirty) {
       -                                        if (i == cur_line && cur_mode == NORMAL) {
       -                                                PangoAttribute *attr0 = pango_attr_background_new(0, 0, 0);
       -                                                PangoAttribute *attr1 = pango_attr_foreground_new(65535, 65535, 65535);
       -                                                attr0->start_index = cur_index;
       -                                                attr0->end_index = cur_index + 1;
       -                                                attr1->start_index = cur_index;
       -                                                attr1->end_index = cur_index + 1;
       -                                                PangoAttribute *attr2 = pango_attr_insert_hyphens_new(FALSE);
       -                                                PangoAttrList *list = pango_attr_list_new();
       -                                                pango_attr_list_insert(list, attr0);
       -                                                pango_attr_list_insert(list, attr1);
       -                                                pango_attr_list_insert(list, attr2);
       -                                                pango_layout_set_attributes(lines[cur_line].layout, list);
       -                                        } else {
       -                                                pango_layout_set_attributes(lines[i].layout, basic_attrs);
       -                                        }
       -                                        render_line(&lines[i]);
       -                                }
                                        if (h + lines[i].h > cur_display_offset) {
       +                                        if (lines[i].dirty || lines[i].cache_index == -1) {
       +                                                render_line(i);
       +                                        }
                                                int final_y = 0;
                                                int dest_y = h - cur_display_offset;
                                                int final_h = lines[i].h;
       t@@ -516,7 +465,7 @@ mainloop(void) {
                                                if (dest_y + final_h > state.h) {
                                                        final_h -= final_y + final_h - cur_display_offset - state.h;
                                                }
       -                                        XCopyArea(state.dpy, lines[i].pix, state.back_buf, state.gc, 0, final_y, lines[i].w, final_h, 0, dest_y);
       +                                        XCopyArea(state.dpy, cache.entries[lines[i].cache_index].pixmap, state.back_buf, state.gc, 0, final_y, lines[i].w, final_h, 0, dest_y);
                                                if (i == cur_line) {
                                                        cur_line_y = h - cur_display_offset;
                                                        cursor_displayed = 1;
       t@@ -526,32 +475,25 @@ mainloop(void) {
                                                break;
                                        h += lines[i].h;
                                }
       -                        */
       -                        double offset = cur_display_offset - cache.start_offset;
       -                        if (offset < 0) {
       -                                printf("FIX THIS CODE 3!\n");
       -                                offset = 0;
       -                        }
       -                        XCopyArea(state.dpy, cache.pix, state.back_buf, state.gc, 0, (int)offset, state.w - 10, state.h, 0, 0);
                                need_redraw = 0;
        
                                XSetForeground(state.dpy, state.gc, state.fg.pixel);
                                PangoRectangle strong, weak;
                                pango_layout_get_cursor_pos(lines[cur_line].layout, cur_index, &strong, &weak);
                                /* FIXME: long, int, etc. */
       -                        long cursor_y = strong.y / PANGO_SCALE + lines[cur_line].y_offset;
       -                        if (cursor_y >= cur_display_offset && cursor_y < cur_display_offset + state.h) {
       +                        int cursor_y = strong.y / PANGO_SCALE + cur_line_y;
       +                        if (cursor_displayed && cursor_y >= 0) {
                                        if (cur_mode == NORMAL && cur_index == lines[cur_line].len) {
                                                XFillRectangle(
                                                    state.dpy, state.back_buf, state.gc,
       -                                            strong.x / PANGO_SCALE, cursor_y - (long)cur_display_offset,
       +                                            strong.x / PANGO_SCALE, cursor_y,
                                                    10, strong.height / PANGO_SCALE
                                                );
                                        } else if (cur_mode == INSERT) {
                                                XDrawLine(
                                                    state.dpy, state.back_buf, state.gc,
       -                                            strong.x / PANGO_SCALE, cursor_y - (long)cur_display_offset,
       -                                            strong.x / PANGO_SCALE, (strong.y + strong.height) / PANGO_SCALE + lines[cur_line].y_offset - (long)cur_display_offset
       +                                            strong.x / PANGO_SCALE, cursor_y,
       +                                            strong.x / PANGO_SCALE, (strong.y + strong.height) / PANGO_SCALE + cur_line_y
                                                );
                                        }
                                }
       t@@ -630,10 +572,7 @@ setup(int argc, char *argv[]) {
                    InputOutput, state.vis, CWBackPixel | CWColormap | CWBitGravity, &attrs);
        
                state.back_buf = XdbeAllocateBackBufferName(state.dpy, state.win, XdbeBackground);
       -        cache.pix = XCreatePixmap(state.dpy, state.back_buf, 500, 500, state.depth);
       -        cache.pix_w = cache.pix_h = 500;
       -        cache.draw = XftDrawCreate(state.dpy, cache.pix, state.vis, state.cm);
       -        cache.dirty = 1;
       +        init_cache();
        
                memset(&gcv, 0, sizeof(gcv));
                gcv.line_width = 1;
       t@@ -695,10 +634,8 @@ ensure_cursor_shown(void) {
                long cursor_y = strong.y / PANGO_SCALE + lines[cur_line].y_offset;
                if (cursor_y < cur_display_offset) {
                        cur_display_offset = cursor_y;
       -                cache.dirty = 1;
                } else if (cursor_y + strong.height / PANGO_SCALE > cur_display_offset + state.h) {
                        cur_display_offset = cursor_y - state.h + strong.height / PANGO_SCALE;
       -                cache.dirty = 1;
                }
        }
        
       t@@ -755,16 +692,9 @@ resize_window(int w, int h) {
                        lines[i].h = tmp_h;
                        lines[i].w = tmp_w;
                        lines[i].y_offset = total_height;
       +                lines[i].dirty = 1;
                        total_height += tmp_h;
                }
       -        if (cache.pix_w < state.w - 10 || cache.pix_h < state.h + 2 * MAX_CACHE_PIXELS) {
       -                XFreePixmap(state.dpy, cache.pix);
       -                cache.pix = XCreatePixmap(state.dpy, state.back_buf, state.w, state.h + 2 * MAX_CACHE_PIXELS + 50, state.depth);
       -                cache.pix_w = state.w;
       -                cache.pix_h = state.h + 2 * MAX_CACHE_PIXELS + 50;
       -                XftDrawChange(cache.draw, cache.pix);
       -        }
       -        cache.dirty = 1;
        }
        
        static void
       t@@ -804,7 +734,6 @@ backspace(void) {
                        pango_layout_set_text(l->layout, l->text, l->len);
                }
                set_line_cursor_attrs(cur_line, cur_index);
       -        cache.dirty = 1;
                recalc_cur_line_size();
        }
        
       t@@ -831,7 +760,6 @@ delete_key(void) {
                        pango_layout_set_text(l->layout, l->text, l->len);
                }
                set_line_cursor_attrs(cur_line, cur_index);
       -        cache.dirty = 1;
                recalc_cur_line_size();
        }
        
       t@@ -858,7 +786,6 @@ move_cursor(int dir) {
                                cur_index = lines[cur_line].len;
                }
                set_line_cursor_attrs(cur_line, cur_index);
       -        cache.dirty = 1;
        }
        
        static void
       t@@ -879,7 +806,6 @@ return_key(void) {
                wipe_line_cursor_attrs(cur_line);
                cur_line++;
                set_line_cursor_attrs(cur_line, cur_index);
       -        cache.dirty = 1;
                cur_index = 0;
                recalc_line_size_absolute();
        }
       t@@ -899,7 +825,6 @@ escape_key(void) {
                        cursor_left();
                }
                set_line_cursor_attrs(cur_line, cur_index);
       -        cache.dirty = 1;
                /*
                if (cur_index > 0)
                        cursor_left();
       t@@ -915,7 +840,6 @@ i_key(void) {
                }
                */
                wipe_line_cursor_attrs(cur_line);
       -        cache.dirty = 1;
        }
        
        static void
       t@@ -946,7 +870,6 @@ line_down(void) {
                if (cur_index > 0 && cur_mode == NORMAL && cur_index >= lines[cur_line].len)
                        cursor_left();
                set_line_cursor_attrs(cur_line, cur_index);
       -        cache.dirty = 1;
        }
        
        static void
       t@@ -977,14 +900,12 @@ line_up(void) {
                if (cur_index > 0 && cur_mode == NORMAL && cur_index >= lines[cur_line].len)
                        cursor_left();
                set_line_cursor_attrs(cur_line, cur_index);
       -        cache.dirty = 1;
        }
        
        static void
        zero_key(void) {
                cur_index = 0;
                set_line_cursor_attrs(cur_line, cur_index);
       -        cache.dirty = 1;
        }
        
        static struct key keys_en[] = {