tModify cache to cache individual lines - 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 <[email protected]> | |
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(-) | |
--- | |
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 las… | |
+ 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 assigne… | |
+ 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_disp… | |
+ 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(… | |
+ 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_displa… | |
- 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… | |
- end_line++) { | |
- } | |
- */ | |
- | |
- for (start_line = 0; | |
- start_line < lines_num - 1 && | |
- lines[start_line].y_offset + lines[start_line].h <= cur_display_o… | |
- 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… | |
- 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… | |
- break; /* should never happen */ | |
- } | |
- pango_xft_render_layout(cache.draw, &state.fg, lines[i].layout… | |
- 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 l… | |
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… | |
- 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… | |
- 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 + … | |
+ pix->w = l->w + 10; | |
+ pix->h = l->h + 10; | |
+ pix->draw = XftDrawCreate(state.dpy, pix->pixmap, state.vis, s… | |
+ } 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, … | |
+ 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,… | |
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 == NORMA… | |
- PangoAttribute *attr0 = pango_… | |
- PangoAttribute *attr1 = pango_… | |
- attr0->start_index = cur_index; | |
- attr0->end_index = cur_index +… | |
- attr1->start_index = cur_index; | |
- attr1->end_index = cur_index +… | |
- PangoAttribute *attr2 = pango_… | |
- PangoAttrList *list = pango_at… | |
- pango_attr_list_insert(list, a… | |
- pango_attr_list_insert(list, a… | |
- pango_attr_list_insert(list, a… | |
- pango_layout_set_attributes(li… | |
- } else { | |
- pango_layout_set_attributes(li… | |
- } | |
- render_line(&lines[i]); | |
- } | |
if (h + lines[i].h > cur_display_offset) { | |
+ if (lines[i].dirty || lines[i].cache_i… | |
+ 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 -… | |
} | |
- XCopyArea(state.dpy, lines[i].pix, sta… | |
+ XCopyArea(state.dpy, cache.entries[lin… | |
if (i == cur_line) { | |
cur_line_y = h - cur_display_o… | |
cursor_displayed = 1; | |
t@@ -526,32 +475,25 @@ mainloop(void) { | |
break; | |
h += lines[i].h; | |
} | |
- */ | |
- double offset = cur_display_offset - cache.start_offse… | |
- if (offset < 0) { | |
- printf("FIX THIS CODE 3!\n"); | |
- offset = 0; | |
- } | |
- XCopyArea(state.dpy, cache.pix, state.back_buf, state.… | |
need_redraw = 0; | |
XSetForeground(state.dpy, state.gc, state.fg.pixel); | |
PangoRectangle strong, weak; | |
pango_layout_get_cursor_pos(lines[cur_line].layout, cu… | |
/* FIXME: long, int, etc. */ | |
- long cursor_y = strong.y / PANGO_SCALE + lines[cur_lin… | |
- if (cursor_y >= cur_display_offset && cursor_y < cur_d… | |
+ int cursor_y = strong.y / PANGO_SCALE + cur_line_y; | |
+ if (cursor_displayed && cursor_y >= 0) { | |
if (cur_mode == NORMAL && cur_index == lines[c… | |
XFillRectangle( | |
state.dpy, state.back_buf, state.g… | |
- strong.x / PANGO_SCALE, cursor_y -… | |
+ strong.x / PANGO_SCALE, cursor_y, | |
10, strong.height / PANGO_SCALE | |
); | |
} else if (cur_mode == INSERT) { | |
XDrawLine( | |
state.dpy, state.back_buf, state.g… | |
- strong.x / PANGO_SCALE, cursor_y -… | |
- strong.x / PANGO_SCALE, (strong.y … | |
+ strong.x / PANGO_SCALE, cursor_y, | |
+ strong.x / PANGO_SCALE, (strong.y … | |
); | |
} | |
} | |
t@@ -630,10 +572,7 @@ setup(int argc, char *argv[]) { | |
InputOutput, state.vis, CWBackPixel | CWColormap | CWBitGravity, &… | |
state.back_buf = XdbeAllocateBackBufferName(state.dpy, state.win, Xdbe… | |
- cache.pix = XCreatePixmap(state.dpy, state.back_buf, 500, 500, state.d… | |
- 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… | |
cur_display_offset = cursor_y - state.h + strong.height / PANG… | |
- 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_CACH… | |
- XFreePixmap(state.dpy, cache.pix); | |
- cache.pix = XCreatePixmap(state.dpy, state.back_buf, state.w, … | |
- 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… | |
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… | |
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[] = { |