ledit

Text editor (WIP)
git clone git://lumidify.org/ledit.git (fast, but not encrypted)
git clone https://lumidify.org/git/ledit.git (encrypted, but very slow)
Log | Files | Refs | README | LICENSE

commit 88b7f6c08a1e2a47f33aa5b4d33f18df60b5011f
parent e9c86a9b95b0a349d3f34f2a15dd3afffafc3b52
Author: lumidify <nobody@lumidify.org>
Date:   Sat, 10 Apr 2021 21:18:46 +0200

Modify cache to cache individual lines

Diffstat:
Mledit.c | 291+++++++++++++++++++++++++++++--------------------------------------------------
1 file changed, 106 insertions(+), 185 deletions(-)

diff --git a/ledit.c b/ledit.c @@ -32,8 +32,6 @@ struct key { void (*func)(void); /* callback function */ }; -#define MAX_CACHE_PIXELS 100 - static struct { Display *dpy; GC gc; @@ -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); @@ -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) { @@ -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) @@ -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 @@ -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); @@ -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 @@ -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; @@ -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; @@ -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 ); } } @@ -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; @@ -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; } } @@ -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 @@ -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(); } @@ -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(); } @@ -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 @@ -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(); } @@ -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(); @@ -915,7 +840,6 @@ i_key(void) { } */ wipe_line_cursor_attrs(cur_line); - cache.dirty = 1; } static void @@ -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 @@ -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[] = {