tSplit code into several files and clean up a bit - 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 824365813f86fcdc01434477f39a8734183a8b18 | |
parent 88b7f6c08a1e2a47f33aa5b4d33f18df60b5011f | |
Author: lumidify <[email protected]> | |
Date: Sun, 11 Apr 2021 22:05:27 +0200 | |
Split code into several files and clean up a bit | |
Diffstat: | |
M .gitignore | 1 - | |
M Makefile | 19 +++++++++++++------ | |
A buffer.c | 269 +++++++++++++++++++++++++++++… | |
A buffer.h | 40 +++++++++++++++++++++++++++++… | |
A cache.c | 98 +++++++++++++++++++++++++++++… | |
A cache.h | 12 ++++++++++++ | |
A common.h | 31 +++++++++++++++++++++++++++++… | |
M ledit.c | 1050 +++++++++++++----------------… | |
A memory.c | 41 +++++++++++++++++++++++++++++… | |
A memory.h | 4 ++++ | |
10 files changed, 951 insertions(+), 614 deletions(-) | |
--- | |
diff --git a/.gitignore b/.gitignore | |
t@@ -1,5 +1,4 @@ | |
tmp | |
ledit | |
-ledit_old | |
*.core | |
*.o | |
diff --git a/Makefile b/Makefile | |
t@@ -7,18 +7,25 @@ PREFIX = /usr/local | |
MANPREFIX = ${PREFIX}/man | |
BIN = ${NAME} | |
-SRC = ${BIN:=.c} | |
MAN1 = ${BIN:=.1} | |
-CFLAGS = -g -D_POSIX_C_SOURCE=200809L `pkg-config --cflags x11 xkbfile pangoxf… | |
-LDFLAGS += `pkg-config --libs x11 xkbfile pangoxft xext` -lm | |
+OBJ = ${BIN:=.o} cache.o buffer.o memory.o | |
+HDR = cache.h buffer.h memory.h common.h | |
+ | |
+CFLAGS_LEDIT = ${CFLAGS} -g -Wall -Wextra -D_POSIX_C_SOURCE=200809L `pkg-confi… | |
+LDFLAGS_LEDIT = ${LDFLAGS} `pkg-config --libs x11 xkbfile pangoxft xext` -lm | |
all: ${BIN} | |
-.c: | |
- ${CC} ${CFLAGS} ${LDFLAGS} -o $@ $< | |
+${OBJ} : ${HDR} | |
+ | |
+.c.o: | |
+ ${CC} -c -o $@ $< ${CFLAGS_LEDIT} | |
+ | |
+${BIN}: ${OBJ} | |
+ ${CC} -o $@ ${OBJ} ${LDFLAGS_LEDIT} | |
clean: | |
- rm -f ${BIN} | |
+ rm -f ${BIN} ${OBJ} | |
.PHONY: all clean | |
diff --git a/buffer.c b/buffer.c | |
t@@ -0,0 +1,269 @@ | |
+#include <X11/Xlib.h> | |
+#include <X11/Xutil.h> | |
+#include <pango/pangoxft.h> | |
+#include <X11/extensions/Xdbe.h> | |
+ | |
+#include "memory.h" | |
+#include "common.h" | |
+#include "buffer.h" | |
+#include "cache.h" | |
+ | |
+static PangoAttrList *basic_attrs = NULL; | |
+ | |
+static void init_line(ledit_buffer *buffer, ledit_line *line); | |
+static void recalc_line_size_absolute(ledit_buffer *buffer); | |
+static void recalc_single_line_size(ledit_buffer *buffer, int line_index); | |
+ | |
+/* FIXME: destroy basic_attrs somewhere */ | |
+ledit_buffer * | |
+ledit_create_buffer(ledit_common_state *state) { | |
+ if (basic_attrs == NULL) { | |
+ basic_attrs = pango_attr_list_new(); | |
+ PangoAttribute *no_hyphens = pango_attr_insert_hyphens_new(FAL… | |
+ pango_attr_list_insert(basic_attrs, no_hyphens); | |
+ } | |
+ | |
+ ledit_buffer *buffer = ledit_malloc(sizeof(ledit_buffer)); | |
+ buffer->state = state; | |
+ buffer->lines = NULL; | |
+ buffer->lines_num = 0; | |
+ buffer->lines_cap = 0; | |
+ buffer->cur_line = 0; | |
+ buffer->cur_index = 0; | |
+ buffer->trailing = 0; | |
+ buffer->total_height = 0; | |
+ buffer->display_offset = 0; | |
+ ledit_append_line(buffer, -1, -1); | |
+ | |
+ return buffer; | |
+} | |
+ | |
+void | |
+ledit_destroy_buffer(ledit_buffer *buffer) { | |
+ for (int i = 0; i < buffer->lines_num; i++) { | |
+ g_object_unref(buffer->lines[i].layout); | |
+ free(buffer->lines[i].text); | |
+ } | |
+ free(buffer->lines); | |
+ free(buffer); | |
+} | |
+ | |
+void | |
+ledit_set_line_cursor_attrs(ledit_buffer *buffer, int line, int index) { | |
+ if (buffer->state->mode == NORMAL) { | |
+ PangoAttribute *attr0 = pango_attr_background_new(0, 0, 0); | |
+ PangoAttribute *attr1 = pango_attr_foreground_new(65535, 65535… | |
+ attr0->start_index = index; | |
+ attr0->end_index = index + 1; | |
+ attr1->start_index = index; | |
+ attr1->end_index = 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(buffer->lines[line].layout, list); | |
+ } else { | |
+ pango_layout_set_attributes(buffer->lines[line].layout, basic_… | |
+ } | |
+ buffer->lines[line].dirty = 1; | |
+} | |
+ | |
+void | |
+ledit_wipe_line_cursor_attrs(ledit_buffer *buffer, int line) { | |
+ pango_layout_set_attributes(buffer->lines[line].layout, basic_attrs); | |
+ buffer->lines[line].dirty = 1; | |
+} | |
+ | |
+void | |
+ledit_insert_text(ledit_buffer *buffer, int line_index, int index, char *text,… | |
+ ledit_line *line = &buffer->lines[line_index]; | |
+ if (len == -1) | |
+ len = strlen(text); | |
+ if (line->len + len > line->cap) { | |
+ line->cap *= 2; | |
+ if (line->cap == 0) | |
+ line->cap = 2; | |
+ line->text = ledit_realloc(line->text, line->cap); | |
+ } | |
+ memmove(line->text + index + len, line->text + index, line->len - inde… | |
+ memcpy(line->text + index, text, len); | |
+ line->len += len; | |
+ pango_layout_set_text(line->layout, line->text, line->len); | |
+ recalc_single_line_size(buffer, line_index); | |
+ line->dirty = 1; | |
+} | |
+ | |
+void | |
+ledit_render_line(ledit_buffer *buffer, int line_index) { | |
+ /* FIXME: check for <= 0 on size */ | |
+ ledit_line *line = &buffer->lines[line_index]; | |
+ if (line->cache_index == -1) | |
+ ledit_assign_free_cache_index(buffer, line_index); | |
+ ledit_cache_pixmap *pix = ledit_get_cache_pixmap(line->cache_index); | |
+ /* FIXME: sensible default pixmap sizes here */ | |
+ if (pix->pixmap == None || pix->draw == NULL) { | |
+ pix->pixmap = XCreatePixmap( | |
+ buffer->state->dpy, buffer->state->drawable, | |
+ line->w + 10, line->h + 10, buffer->state->depth | |
+ ); | |
+ pix->w = line->w + 10; | |
+ pix->h = line->h + 10; | |
+ pix->draw = XftDrawCreate( | |
+ buffer->state->dpy, pix->pixmap, | |
+ buffer->state->vis, buffer->state->cm | |
+ ); | |
+ } else if (pix->w < line->w || pix->h < line->h) { | |
+ int new_w = line->w > pix->w ? line->w + 10 : pix->w + 10; | |
+ int new_h = line->h > pix->h ? line->h + 10 : pix->h + 10; | |
+ XFreePixmap(buffer->state->dpy, pix->pixmap); | |
+ pix->pixmap = XCreatePixmap( | |
+ buffer->state->dpy, buffer->state->drawable, | |
+ new_w, new_h, buffer->state->depth | |
+ ); | |
+ pix->w = new_w; | |
+ pix->h = new_h; | |
+ XftDrawChange(pix->draw, pix->pixmap); | |
+ } | |
+ XftDrawRect(pix->draw, &buffer->state->bg, 0, 0, line->w, line->h); | |
+ pango_xft_render_layout(pix->draw, &buffer->state->fg, line->layout, 0… | |
+ line->dirty = 0; | |
+} | |
+ | |
+/* FIXME: use proper "viewport width" instead of just subtracting 10 */ | |
+static void | |
+init_line(ledit_buffer *buffer, ledit_line *line) { | |
+ line->parent_buffer = buffer; | |
+ line->layout = pango_layout_new(buffer->state->context); | |
+ pango_layout_set_width(line->layout, (buffer->state->w - 10) * PANGO_S… | |
+ pango_layout_set_font_description(line->layout, buffer->state->font); | |
+ pango_layout_set_wrap(line->layout, PANGO_WRAP_WORD_CHAR); | |
+ line->text = NULL; | |
+ line->cap = line->len = 0; | |
+ line->cache_index = -1; | |
+ line->dirty = 1; | |
+ /* FIXME: does this set line height reasonably when no text yet? */ | |
+ pango_layout_get_pixel_size(line->layout, &line->w, &line->h); | |
+ line->y_offset = 0; | |
+} | |
+ | |
+/* FIXME: error checking (index out of bounds, etc.) */ | |
+void | |
+ledit_append_line(ledit_buffer *buffer, int line_index, int text_index) { | |
+ if (buffer->lines_num >= buffer->lines_cap) { | |
+ buffer->lines_cap *= 2; | |
+ if (buffer->lines_cap == 0) | |
+ buffer->lines_cap = 2; | |
+ buffer->lines = ledit_realloc( | |
+ buffer->lines, buffer->lines_cap * sizeof(ledit_line) | |
+ ); | |
+ } | |
+ memmove( | |
+ buffer->lines + line_index + 2, | |
+ buffer->lines + line_index + 1, | |
+ (buffer->lines_num - (line_index + 1)) * sizeof(ledit_line) | |
+ ); | |
+ ledit_line *new_l = &buffer->lines[line_index + 1]; | |
+ init_line(buffer, new_l); | |
+ buffer->lines_num++; | |
+ if (text_index != -1) { | |
+ ledit_line *l = &buffer->lines[line_index]; | |
+ int len = l->len - text_index; | |
+ new_l->len = len; | |
+ new_l->cap = len + 10; | |
+ new_l->text = ledit_malloc(new_l->cap); | |
+ memcpy(new_l->text, l->text + text_index, len); | |
+ l->len = text_index; | |
+ pango_layout_set_text(new_l->layout, new_l->text, new_l->len); | |
+ pango_layout_set_text(l->layout, l->text, l->len); | |
+ /* FIXME: set height here */ | |
+ } | |
+ recalc_line_size_absolute(buffer); | |
+} | |
+ | |
+void | |
+ledit_delete_line_entry(ledit_buffer *buffer, int index) { | |
+ g_object_unref(buffer->lines[index].layout); | |
+ free(buffer->lines[index].text); | |
+ if (index < buffer->lines_num - 1) | |
+ memmove( | |
+ buffer->lines + index, buffer->lines + index + 1, | |
+ (buffer->lines_num - index - 1) * sizeof(ledit_line) | |
+ ); | |
+ buffer->lines_num--; | |
+ recalc_line_size_absolute(buffer); | |
+} | |
+ | |
+/* FIXME: use some sort of gap buffer (that would make this function | |
+ slightly more useful...) */ | |
+ledit_line * | |
+ledit_get_line(ledit_buffer *buffer, int index) { | |
+ return &buffer->lines[index]; | |
+} | |
+ | |
+static void | |
+recalc_single_line_size(ledit_buffer *buffer, int line_index) { | |
+ int w, h; | |
+ ledit_line *line = &buffer->lines[line_index]; | |
+ pango_layout_get_pixel_size(line->layout, &w, &h); | |
+ line->w = w; | |
+ /* if height changed, set height of current line | |
+ * and adjust offsets of all lines following it */ | |
+ if (line->h != h) { | |
+ int delta = h - line->h; | |
+ line->h = h; | |
+ buffer->total_height += delta; | |
+ if (buffer->total_height < 0) | |
+ buffer->total_height = 0; | |
+ for (int i = line_index + 1; i < buffer->lines_num; i++) { | |
+ buffer->lines[i].y_offset += delta; | |
+ if (buffer->lines[i].y_offset < 0) | |
+ buffer->lines[i].y_offset = 0; | |
+ } | |
+ } | |
+} | |
+ | |
+static void | |
+recalc_line_size_absolute(ledit_buffer *buffer) { | |
+ int w, h; | |
+ buffer->total_height = 0; | |
+ /* completely recalculate line sizes and offsets from scratch */ | |
+ for (int i = 0; i < buffer->lines_num; i++) { | |
+ buffer->lines[i].y_offset = buffer->total_height; | |
+ pango_layout_get_pixel_size(buffer->lines[i].layout, &w, &h); | |
+ buffer->total_height += h; | |
+ buffer->lines[i].w = w; | |
+ buffer->lines[i].h = h; | |
+ } | |
+} | |
+ | |
+int | |
+ledit_line_visible(ledit_buffer *buffer, int index) { | |
+ ledit_line *line = &buffer->lines[index]; | |
+ return line->y_offset < buffer->display_offset + buffer->state->h && | |
+ line->y_offset + line->h > buffer->display_offset; | |
+} | |
+ | |
+int | |
+ledit_delete_unicode_char(ledit_buffer *buffer, int line_index, int byte_index… | |
+ ledit_line *l = ledit_get_line(buffer, line_index); | |
+ int new_index = byte_index; | |
+ if (dir < 0) { | |
+ int i = buffer->cur_index - 1; | |
+ /* find valid utf8 char - this probably needs to be improved */ | |
+ while (i > 0 && ((l->text[i] & 0xC0) == 0x80)) | |
+ i--; | |
+ memmove(l->text + i, l->text + byte_index, l->len - byte_index… | |
+ l->len -= byte_index - i; | |
+ new_index = i; | |
+ } else { | |
+ int i = byte_index + 1; | |
+ while (i < l->len && ((l->text[i] & 0xC0) == 0x80)) | |
+ i++; | |
+ memmove(l->text + byte_index, l->text + i, l->len - i); | |
+ l->len -= i - byte_index; | |
+ } | |
+ pango_layout_set_text(l->layout, l->text, l->len); | |
+ recalc_single_line_size(buffer, line_index); | |
+ return new_index; | |
+} | |
diff --git a/buffer.h b/buffer.h | |
t@@ -0,0 +1,40 @@ | |
+typedef struct ledit_buffer ledit_buffer; | |
+ | |
+typedef struct { | |
+ PangoLayout *layout; | |
+ char *text; | |
+ ledit_buffer *parent_buffer; | |
+ int cap; /* allocated space for text */ | |
+ int len; /* actual length of text */ | |
+ int w; | |
+ int h; | |
+ long y_offset; /* pixel offset starting at the top of the file */ | |
+ int cache_index; /* index of pixmap in cache, or -1 if not assigned */ | |
+ char dirty; /* whether line needs to be rendered before being draw */ | |
+} ledit_line; | |
+ | |
+struct ledit_buffer { | |
+ ledit_common_state *state; /* general state, e.g. display, window, etc… | |
+ ledit_line *lines; /* array of lines */ | |
+ int lines_cap; /* number of lines allocated in array */ | |
+ int lines_num; /* number of used lines */ | |
+ int cur_line; /* current line */ | |
+ int cur_index; /* current byte index in line */ | |
+ int trailing; /* used by pango for determining if index is at | |
+ * beginning or end of character */ | |
+ long total_height; /* total pixel height of all lines */ | |
+ double display_offset; /* current pixel offset of viewport - this | |
+ * is a double to make scrolling smoother */ | |
+}; | |
+ | |
+ledit_buffer *ledit_create_buffer(ledit_common_state *state); | |
+void ledit_destroy_buffer(ledit_buffer *buffer); | |
+void ledit_set_line_cursor_attrs(ledit_buffer *buffer, int line, int index); | |
+void ledit_wipe_line_cursor_attrs(ledit_buffer *buffer, int line); | |
+void ledit_insert_text(ledit_buffer *buffer, int line_index, int index, char *… | |
+void ledit_render_line(ledit_buffer *buffer, int line_index); | |
+void ledit_append_line(ledit_buffer *buffer, int line_index, int text_index); | |
+void ledit_delete_line_entry(ledit_buffer *buffer, int index); | |
+ledit_line *ledit_get_line(ledit_buffer *buffer, int index); | |
+int ledit_line_visible(ledit_buffer *buffer, int index); | |
+int ledit_delete_unicode_char(ledit_buffer *buffer, int line_index, int byte_i… | |
diff --git a/cache.c b/cache.c | |
t@@ -0,0 +1,98 @@ | |
+#include <stdlib.h> | |
+#include <X11/Xlib.h> | |
+#include <X11/Xutil.h> | |
+#include <pango/pangoxft.h> | |
+#include <X11/extensions/Xdbe.h> | |
+ | |
+#include "common.h" | |
+#include "memory.h" | |
+#include "buffer.h" | |
+#include "cache.h" | |
+ | |
+ | |
+static struct { | |
+ ledit_common_state *state; | |
+ ledit_cache_pixmap *entries; | |
+ int entries_num; | |
+ int cur_replace_index; | |
+} cache = {NULL, NULL, 0, -1}; | |
+ | |
+void | |
+ledit_init_cache(ledit_common_state *state) { | |
+ cache.state = state; | |
+ /* FIXME: prevent overflow */ | |
+ cache.entries = ledit_malloc(20 * sizeof(ledit_cache_pixmap)); | |
+ for (int i = 0; i < 20; i++) { | |
+ cache.entries[i].pixmap = None; | |
+ cache.entries[i].draw = NULL; | |
+ cache.entries[i].line = -1; | |
+ } | |
+ cache.entries_num = 20; | |
+ cache.cur_replace_index = -1; | |
+} | |
+ | |
+void | |
+ledit_flush_cache(void) { | |
+ for (int i = 0; i < cache.entries_num; i++) { | |
+ cache.entries[i].line = -1; | |
+ } | |
+} | |
+ | |
+void | |
+ledit_destroy_cache(void) { | |
+ for (int i = 0; i < cache.entries_num; i++) { | |
+ if (cache.entries[i].pixmap != None) | |
+ XFreePixmap(cache.state->dpy, cache.entries[i].pixmap); | |
+ if (cache.entries[i].draw != NULL) | |
+ XftDrawDestroy(cache.entries[i].draw); | |
+ } | |
+ free(cache.entries); | |
+ cache.state = NULL; | |
+ cache.entries = NULL; | |
+ cache.entries_num = 0; | |
+ cache.cur_replace_index = -1; | |
+} | |
+ | |
+void | |
+ledit_assign_free_cache_index(ledit_buffer *buffer, int new_line_index) { | |
+ int entry_index; | |
+ int line_index; | |
+ ledit_line *line; | |
+ /* start at 1 because the cache->cur_replace_index is actually the las… | |
+ for (int i = 1; i <= cache.entries_num; i++) { | |
+ entry_index = (i + cache.cur_replace_index) % cache.entries_nu… | |
+ line_index = cache.entries[entry_index].line; | |
+ /* replace line when entry isn't assigned or currently assigne… | |
+ if (line_index == -1 || | |
+ (line_index >= 0 && | |
+ !ledit_line_visible(buffer, line_index))) { | |
+ if (line_index >= 0) { | |
+ line = ledit_get_line(buffer, line_index); | |
+ line->cache_index = -1; | |
+ } | |
+ cache.entries[entry_index].line = new_line_index; | |
+ cache.cur_replace_index = entry_index; | |
+ line = ledit_get_line(buffer, new_line_index); | |
+ line->cache_index = entry_index; | |
+ return; | |
+ } | |
+ } | |
+ | |
+ /* no free entry found, increase cache size */ | |
+ cache.entries = ledit_realloc(cache.entries, cache.entries_num * 2 * s… | |
+ entry_index = cache.entries_num; | |
+ for (int i = cache.entries_num; i < cache.entries_num * 2; i++) { | |
+ cache.entries[i].line = -1; | |
+ cache.entries[i].pixmap = None; | |
+ cache.entries[i].draw = NULL; | |
+ } | |
+ cache.entries_num *= 2; | |
+ cache.entries[entry_index].line = new_line_index; | |
+ line = ledit_get_line(buffer, new_line_index); | |
+ line->cache_index = entry_index; | |
+} | |
+ | |
+ledit_cache_pixmap * | |
+ledit_get_cache_pixmap(int index) { | |
+ return &cache.entries[index]; | |
+} | |
diff --git a/cache.h b/cache.h | |
t@@ -0,0 +1,12 @@ | |
+typedef struct { | |
+ Pixmap pixmap; | |
+ XftDraw *draw; | |
+ int w, h; | |
+ int line; | |
+} ledit_cache_pixmap; | |
+ | |
+void ledit_init_cache(ledit_common_state *state); | |
+void ledit_flush_cache(void); | |
+void ledit_destroy_cache(void); | |
+ledit_cache_pixmap *ledit_get_cache_pixmap(int index); | |
+void ledit_assign_free_cache_index(ledit_buffer *buffer, int line); | |
diff --git a/common.h b/common.h | |
t@@ -0,0 +1,31 @@ | |
+enum ledit_mode { | |
+ NORMAL = 1, | |
+ INSERT = 2, | |
+ VISUAL = 4 | |
+}; | |
+ | |
+typedef struct { | |
+ Display *dpy; | |
+ PangoFontMap *fontmap; | |
+ PangoContext *context; | |
+ PangoFontDescription *font; | |
+ Visual *vis; | |
+ GC gc; | |
+ Window win; | |
+ XdbeBackBuffer back_buf; | |
+ Drawable drawable; | |
+ Colormap cm; | |
+ int screen; | |
+ int depth; | |
+ int w; | |
+ int h; | |
+ int scroll_dragging; | |
+ int scroll_grab_handle; | |
+ enum ledit_mode mode; | |
+ XIM xim; | |
+ XIC xic; | |
+ XftColor fg; | |
+ XftColor bg; | |
+ XftColor scroll_bg; | |
+ Atom wm_delete_msg; | |
+} ledit_common_state; | |
diff --git a/ledit.c b/ledit.c | |
t@@ -1,4 +1,5 @@ | |
/* FIXME: horizontal scrolling (also need cache to avoid too large pixmaps) */ | |
+/* FIXME: sort out types for indices (currently just int, but that might overf… | |
#include <math.h> | |
#include <stdio.h> | |
#include <errno.h> | |
t@@ -19,74 +20,47 @@ | |
#include <X11/extensions/XKBrules.h> | |
#include <X11/extensions/Xdbe.h> | |
-static enum mode { | |
- NORMAL = 1, | |
- INSERT = 2, | |
- VISUAL = 4 | |
-} cur_mode = INSERT; | |
+#include "memory.h" | |
+#include "common.h" | |
+#include "buffer.h" | |
+#include "cache.h" | |
struct key { | |
- char *text; /* for keys that correspond with text */ | |
- KeySym keysym; /* for other keys, e.g. arrow keys */ | |
- enum mode modes; /* modes in which this keybinding is functional */ | |
- void (*func)(void); /* callback function */ | |
+ char *text; /* for keys that correspond with text */ | |
+ KeySym keysym; /* for other keys, e.g. arrow keys */ | |
+ enum ledit_mode modes; /* modes in which this keybinding is functional… | |
+ void (*func)(void); /* callback function */ | |
}; | |
-static struct { | |
- Display *dpy; | |
- GC gc; | |
- Window win; | |
- XdbeBackBuffer back_buf; | |
- Visual *vis; | |
- PangoFontMap *fontmap; | |
- PangoContext *context; | |
- PangoFontDescription *font; | |
- Colormap cm; | |
- int screen; | |
- int depth; | |
- XIM xim; | |
- XIC xic; | |
- int w; | |
- int h; | |
- XftColor fg; | |
- XftColor bg; | |
- | |
- 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 { | |
- 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 ledit_common_state state; | |
+static ledit_buffer *buffer; | |
+static void set_scroll_pos(double pos); | |
+static void get_scroll_pos_height(double *pos, double *height); | |
+static void resize_window(int w, int h); | |
static void mainloop(void); | |
static void setup(int argc, char *argv[]); | |
static void cleanup(void); | |
static void redraw(void); | |
-static void drag_motion(XEvent event); | |
+static int button_press(XEvent *event); | |
+static int button_release(XEvent *event); | |
+static int drag_motion(XEvent *event); | |
+static void ensure_cursor_shown(void); | |
static void resize_window(int w, int h); | |
-static void button_release(void); | |
-static void button_press(XEvent event); | |
+ | |
+static void backspace(void); | |
+static void delete_key(void); | |
+static void move_cursor(int dir); | |
+static void cursor_left(void); | |
+static void cursor_right(void); | |
+static void return_key(void); | |
+static void escape_key(void); | |
+static void i_key(void); | |
+static void line_down(void); | |
+static void line_up(void); | |
+static void zero_key(void); | |
+ | |
+static void change_keyboard(char *lang); | |
static void key_press(XEvent event); | |
int | |
t@@ -98,208 +72,20 @@ main(int argc, char *argv[]) { | |
return 0; | |
} | |
-static struct line { | |
- PangoLayout *layout; | |
- char *text; | |
- size_t cap; | |
- size_t len; | |
- int w; | |
- int h; | |
- long y_offset; | |
- int cache_index; | |
- char dirty; | |
-} *lines = NULL; | |
- | |
-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 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) { | |
- /* FIXME: check that layout created properly */ | |
- l->layout = pango_layout_new(state.context); | |
- pango_layout_set_width(l->layout, (state.w - 10) * PANGO_SCALE); | |
- pango_layout_set_font_description(l->layout, state.font); | |
- pango_layout_set_wrap(l->layout, PANGO_WRAP_WORD_CHAR); | |
- l->text = NULL; | |
- l->cap = l->len = 0; | |
- 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; | |
-} | |
- | |
-static void recalc_cur_line_size(void); | |
-static void recalc_line_size_absolute(void); | |
- | |
-static void | |
-insert_text(struct line *l, int index, char *text, int len) { | |
- if (len == -1) | |
- len = strlen(text); | |
- if (l->len + len > l->cap) { | |
- l->cap *= 2; | |
- if (l->cap == 0) | |
- l->cap = 2; | |
- l->text = realloc(l->text, l->cap); | |
- if (!l->text) exit(1); | |
- } | |
- memmove(l->text + index + len, l->text + index, l->len - index); | |
- memcpy(l->text + index, text, len); | |
- l->len += len; | |
- pango_layout_set_text(l->layout, l->text, l->len); | |
- recalc_cur_line_size(); | |
- l->dirty = 1; | |
-} | |
- | |
-static void insert_line_entry(int index); | |
- | |
-static void | |
-render_line(int line) { | |
- /* FIXME: check for <= 0 on size */ | |
- 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 | |
-append_line(int text_index, int line_index) { | |
- if (lines_num >= lines_cap) { | |
- lines_cap *= 2; | |
- if (lines_cap == 0) | |
- lines_cap = 2; | |
- lines = realloc(lines, lines_cap * sizeof(struct line)); | |
- if (!lines) exit(1); | |
- } | |
- memmove(lines + line_index + 2, lines + line_index + 1, (lines_num - (… | |
- struct line *new_l = &lines[line_index + 1]; | |
- init_line(new_l); | |
- lines_num++; | |
- if (text_index != -1) { | |
- struct line *l = &lines[line_index]; | |
- int len = l->len - text_index; | |
- new_l->len = len; | |
- new_l->cap = len + 10; | |
- new_l->text = malloc(new_l->cap); | |
- if (!new_l->text) exit(1); | |
- memcpy(new_l->text, l->text + text_index, len); | |
- l->len = text_index; | |
- pango_layout_set_text(new_l->layout, new_l->text, new_l->len); | |
- pango_layout_set_text(l->layout, l->text, l->len); | |
- /* FIXME: set height here */ | |
- } | |
- /* FIXME: update line heights, etc. */ | |
-} | |
- | |
-static void change_keyboard(char *lang); | |
- | |
-PangoAttrList *basic_attrs; | |
- | |
static void | |
get_scroll_pos_height(double *pos, double *height) { | |
- *height = ((double)state.h / total_height) * state.h; | |
- *pos = (cur_display_offset / (total_height - state.h)) * (state.h - *h… | |
+ *height = ((double)state.h / buffer->total_height) * state.h; | |
+ *pos = (buffer->display_offset / | |
+ (buffer->total_height - state.h)) * (state.h - *height); | |
} | |
static void | |
set_scroll_pos(double pos) { | |
- cur_display_offset = pos * (total_height / (double)state.h); | |
- if (cur_display_offset < 0) | |
- cur_display_offset = 0; | |
- if (cur_display_offset + state.h > total_height) | |
- cur_display_offset = total_height - state.h; | |
-} | |
- | |
-static int scroll_dragging = 0; | |
-static int scroll_grab_handle = 0; | |
- | |
-static void | |
-set_line_cursor_attrs(int line, int index) { | |
- if (cur_mode == NORMAL) { | |
- PangoAttribute *attr0 = pango_attr_background_new(0, 0, 0); | |
- PangoAttribute *attr1 = pango_attr_foreground_new(65535, 65535… | |
- attr0->start_index = index; | |
- attr0->end_index = index + 1; | |
- attr1->start_index = index; | |
- attr1->end_index = 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[line].layout, list); | |
- } 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; | |
+ buffer->display_offset = pos * (buffer->total_height / (double)state.h… | |
+ if (buffer->display_offset < 0) | |
+ buffer->display_offset = 0; | |
+ if (buffer->display_offset + state.h > buffer->total_height) | |
+ buffer->display_offset = buffer->total_height - state.h; | |
} | |
static void | |
t@@ -313,33 +99,22 @@ mainloop(void) { | |
} | |
printf("XKB (%d.%d) supported.\n", major, minor); | |
/* This should select the events when the keyboard mapping changes. | |
- When e.g. 'setxkbmap us' is executed, two events are sent, but I ha… | |
- change that. When the xkb layout switching is used (e.g. 'setxkbmap… | |
- this issue does not occur because only a state event is sent. */ | |
- XkbSelectEvents(state.dpy, XkbUseCoreKbd, XkbNewKeyboardNotifyMask, Xk… | |
- XkbSelectEventDetails(state.dpy, XkbUseCoreKbd, XkbStateNotify, XkbAll… | |
+ * When e.g. 'setxkbmap us' is executed, two events are sent, but I | |
+ * haven't figured out how to change that. When the xkb layout | |
+ * switching is used (e.g. 'setxkbmap -option grp:shifts_toggle'), | |
+ * this issue does not occur because only a state event is sent. */ | |
+ XkbSelectEvents( | |
+ state.dpy, XkbUseCoreKbd, | |
+ XkbNewKeyboardNotifyMask, XkbNewKeyboardNotifyMask | |
+ ); | |
+ XkbSelectEventDetails( | |
+ state.dpy, XkbUseCoreKbd, XkbStateNotify, | |
+ XkbAllStateComponentsMask, XkbGroupStateMask | |
+ ); | |
XSync(state.dpy, False); | |
int running = 1; | |
int change_kbd = 0; | |
- | |
- /*draw = XftDrawCreate(state.dpy, state.back_buf, state.vis, state.cm)… | |
- state.fontmap = pango_xft_get_font_map(state.dpy, state.screen); | |
- state.context = pango_font_map_create_context(state.fontmap); | |
- | |
- state.font = pango_font_description_from_string("Monospace"); | |
- pango_font_description_set_size(state.font, 16 * PANGO_SCALE); | |
- | |
- basic_attrs = pango_attr_list_new(); | |
- PangoAttribute *no_hyphens = pango_attr_insert_hyphens_new(FALSE); | |
- pango_attr_list_insert(basic_attrs, no_hyphens); | |
- | |
- append_line(-1, -1); | |
- | |
- XftColorAllocName(state.dpy, state.vis, state.cm, "#000000", &state.fg… | |
- XftColorAllocName(state.dpy, state.vis, state.cm, "#FFFFFF", &state.bg… | |
- XftColor scroll_bg; | |
- XftColorAllocName(state.dpy, state.vis, state.cm, "#CCCCCC", &scroll_b… | |
int need_redraw = 0; | |
redraw(); | |
t@@ -358,63 +133,20 @@ mainloop(void) { | |
need_redraw = 1; | |
break; | |
case ConfigureNotify: | |
- resize_window(event.xconfigure.width, event.xc… | |
- if (cur_display_offset > 0 && cur_display_offs… | |
- cur_display_offset = total_height - st… | |
- if (cur_display_offset < 0) | |
- cur_display_offset = 0; | |
- } | |
- redraw(); | |
+ resize_window( | |
+ event.xconfigure.width, | |
+ event.xconfigure.height | |
+ ); | |
need_redraw = 1; | |
break; | |
case ButtonPress: | |
- switch (event.xbutton.button) { | |
- case Button1: | |
- button_press(event); | |
- double scroll_h, scroll_y; | |
- get_scroll_pos_height(&scroll_… | |
- int x = event.xbutton.x; | |
- int y = event.xbutton.y; | |
- if (x >= state.w - 10) { | |
- scroll_dragging = 1; | |
- scroll_grab_handle = y; | |
- if (y < scroll_y || y … | |
- double new_scr… | |
- set_scroll_pos… | |
- } | |
- } | |
- break; | |
- case Button4: | |
- cur_display_offset -= 10; | |
- if (cur_display_offset < 0) | |
- cur_display_offset = 0; | |
- break; | |
- case Button5: | |
- if (cur_display_offset + state… | |
- cur_display_offset += … | |
- if (cur_display_offset… | |
- cur_display_of… | |
- } | |
- break; | |
- } | |
- need_redraw = 1; | |
+ need_redraw |= button_press(&event); | |
break; | |
case ButtonRelease: | |
- if (event.xbutton.button == Button1) { | |
- button_release(); | |
- scroll_dragging = 0; | |
- } | |
+ need_redraw |= button_release(&event); | |
break; | |
case MotionNotify: | |
- drag_motion(event); | |
- if (scroll_dragging) { | |
- double scroll_h, scroll_y; | |
- get_scroll_pos_height(&scroll_y, &scro… | |
- scroll_y += event.xbutton.y - scroll_g… | |
- scroll_grab_handle = event.xbutton.y; | |
- set_scroll_pos(scroll_y); | |
- } | |
- need_redraw = 1; | |
+ need_redraw |= drag_motion(&event); | |
break; | |
case KeyPress: | |
need_redraw = 1; | |
t@@ -432,90 +164,21 @@ mainloop(void) { | |
change_kbd = 0; | |
XkbStateRec s; | |
XkbGetState(state.dpy, XkbUseCoreKbd, &s); | |
- XkbDescPtr desc = XkbGetKeyboard(state.dpy, XkbAllComp… | |
- char *group = XGetAtomName(state.dpy, desc->names->gro… | |
+ XkbDescPtr desc = XkbGetKeyboard( | |
+ state.dpy, XkbAllComponentsMask, XkbUseCoreKbd | |
+ ); | |
+ char *group = XGetAtomName( | |
+ state.dpy, desc->names->groups[s.group] | |
+ ); | |
change_keyboard(group); | |
- /*char *symbols = XGetAtomName(state.dpy, desc->names-… | |
XFree(group); | |
- /*XFree(symbols);*/ | |
XkbFreeKeyboard(desc, XkbAllComponentsMask, True); | |
} | |
if (need_redraw) { | |
- XSetForeground(state.dpy, state.gc, state.bg.pixel); | |
- XFillRectangle(state.dpy, state.back_buf, state.gc, 0,… | |
- int h = 0; | |
- | |
- /*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 (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; | |
- if (h < cur_display_offset) { | |
- dest_y = 0; | |
- final_y = cur_display_offset -… | |
- final_h -= cur_display_offset … | |
- } | |
- if (dest_y + final_h > state.h) { | |
- final_h -= final_y + final_h -… | |
- } | |
- XCopyArea(state.dpy, cache.entries[lin… | |
- if (i == cur_line) { | |
- cur_line_y = h - cur_display_o… | |
- cursor_displayed = 1; | |
- } | |
- } | |
- if (h + lines[i].h >= cur_display_offset + sta… | |
- break; | |
- h += lines[i].h; | |
- } | |
+ redraw(); | |
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. */ | |
- 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, | |
- 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 … | |
- ); | |
- } | |
- } | |
- if (total_height > state.h) { | |
- XSetForeground(state.dpy, state.gc, scroll_bg.… | |
- XFillRectangle(state.dpy, state.back_buf, stat… | |
- XSetForeground(state.dpy, state.gc, state.fg.p… | |
- double scroll_h, scroll_y; | |
- get_scroll_pos_height(&scroll_y, &scroll_h); | |
- XFillRectangle(state.dpy, state.back_buf, stat… | |
- } | |
- | |
- XdbeSwapInfo swap_info; | |
- swap_info.swap_window = state.win; | |
- swap_info.swap_action = XdbeBackground; | |
- | |
- if (!XdbeSwapBuffers(state.dpy, &swap_info, 1)) | |
- exit(1); | |
- XFlush(state.dpy); | |
} | |
} | |
- pango_attr_list_unref(basic_attrs); | |
} | |
static void | |
t@@ -525,6 +188,8 @@ setup(int argc, char *argv[]) { | |
XSetWindowAttributes attrs; | |
XGCValues gcv; | |
+ state.scroll_dragging = 0; | |
+ state.scroll_grab_handle = 0; | |
state.w = 500; | |
state.h = 500; | |
state.dpy = XOpenDisplay(NULL); | |
t@@ -533,23 +198,35 @@ setup(int argc, char *argv[]) { | |
/* based on http://wili.cc/blog/xdbe.html */ | |
int major, minor; | |
if (XdbeQueryExtension(state.dpy, &major, &minor)) { | |
- printf("Xdbe (%d.%d) supported, using double buffering.\n", ma… | |
+ printf( | |
+ "Xdbe (%d.%d) supported, using double buffering.\n", | |
+ major, minor | |
+ ); | |
int num_screens = 1; | |
Drawable screens[] = { DefaultRootWindow(state.dpy) }; | |
- XdbeScreenVisualInfo *info = XdbeGetVisualInfo(state.dpy, scre… | |
+ XdbeScreenVisualInfo *info = XdbeGetVisualInfo( | |
+ state.dpy, screens, &num_screens | |
+ ); | |
if (!info || num_screens < 1 || info->count < 1) { | |
fprintf(stderr, "No visuals support Xdbe.\n"); | |
exit(1); | |
} | |
XVisualInfo xvisinfo_templ; | |
- xvisinfo_templ.visualid = info->visinfo[0].visual; // We know … | |
+ /* we know there's at least one */ | |
+ xvisinfo_templ.visualid = info->visinfo[0].visual; | |
xvisinfo_templ.screen = 0; | |
xvisinfo_templ.depth = info->visinfo[0].depth; | |
int matches; | |
- XVisualInfo *xvisinfo_match = | |
- XGetVisualInfo(state.dpy, VisualIDMask|VisualScreenMas… | |
+ XVisualInfo *xvisinfo_match = XGetVisualInfo( | |
+ state.dpy, | |
+ VisualIDMask | VisualScreenMask | VisualDepthMask, | |
+ &xvisinfo_templ, &matches | |
+ ); | |
if (!xvisinfo_match || matches < 1) { | |
- fprintf(stderr, "Couldn't match a Visual with double b… | |
+ fprintf( | |
+ stderr, | |
+ "Couldn't match a Visual with double buffering\n" | |
+ ); | |
exit(1); | |
} | |
state.vis = xvisinfo_match->visual; | |
t@@ -567,18 +244,38 @@ setup(int argc, char *argv[]) { | |
/* this causes the window contents to be kept | |
* when it is resized, leading to less flicker */ | |
attrs.bit_gravity = NorthWestGravity; | |
- state.win = XCreateWindow(state.dpy, DefaultRootWindow(state.dpy), 0, … | |
+ state.win = XCreateWindow( | |
+ state.dpy, DefaultRootWindow(state.dpy), 0, 0, | |
state.w, state.h, 0, state.depth, | |
- InputOutput, state.vis, CWBackPixel | CWColormap | CWBitGravity, &… | |
+ InputOutput, state.vis, | |
+ CWBackPixel | CWColormap | CWBitGravity, &attrs | |
+ ); | |
- state.back_buf = XdbeAllocateBackBufferName(state.dpy, state.win, Xdbe… | |
- init_cache(); | |
+ state.back_buf = XdbeAllocateBackBufferName( | |
+ state.dpy, state.win, XdbeBackground | |
+ ); | |
+ state.drawable = state.back_buf; | |
memset(&gcv, 0, sizeof(gcv)); | |
gcv.line_width = 1; | |
state.gc = XCreateGC(state.dpy, state.back_buf, GCLineWidth, &gcv); | |
- XSelectInput(state.dpy, state.win, StructureNotifyMask | KeyPressMask … | |
+ state.fontmap = pango_xft_get_font_map(state.dpy, state.screen); | |
+ state.context = pango_font_map_create_context(state.fontmap); | |
+ | |
+ state.font = pango_font_description_from_string("Monospace"); | |
+ pango_font_description_set_size(state.font, 12 * PANGO_SCALE); | |
+ | |
+ XftColorAllocName(state.dpy, state.vis, state.cm, "#000000", &state.fg… | |
+ XftColorAllocName(state.dpy, state.vis, state.cm, "#FFFFFF", &state.bg… | |
+ XftColorAllocName(state.dpy, state.vis, state.cm, "#CCCCCC", &state.sc… | |
+ | |
+ XSelectInput( | |
+ state.dpy, state.win, | |
+ StructureNotifyMask | KeyPressMask | | |
+ ButtonPressMask | ButtonReleaseMask | | |
+ PointerMotionMask | ExposureMask | |
+ ); | |
state.wm_delete_msg = XInternAtom(state.dpy, "WM_DELETE_WINDOW", False… | |
XSetWMProtocols(state.dpy, state.win, &state.wm_delete_msg, 1); | |
t@@ -589,203 +286,320 @@ setup(int argc, char *argv[]) { | |
if ((state.xim = XOpenIM(state.dpy, NULL, NULL, NULL)) == NUL… | |
XSetLocaleModifiers("@im="); | |
if ((state.xim = XOpenIM(state.dpy, NULL, NULL, NULL))… | |
- fprintf(stderr, "XOpenIM failed. Could not ope… | |
+ fprintf( | |
+ stderr, | |
+ "XOpenIM failed. Could not open input devi… | |
+ ); | |
exit(1); | |
} | |
} | |
} | |
- state.xic = XCreateIC(state.xim, XNInputStyle, XIMPreeditNothing | |
- | XIMStatusNothing, XNClientWindow,… | |
- XNFocusWindow, state.win, NULL); | |
+ state.xic = XCreateIC( | |
+ state.xim, XNInputStyle, | |
+ XIMPreeditNothing | XIMStatusNothing, | |
+ XNClientWindow, state.win, | |
+ XNFocusWindow, state.win, NULL | |
+ ); | |
if (state.xic == NULL) { | |
- fprintf(stderr, "XCreateIC failed. Could not obtain input meth… | |
+ fprintf( | |
+ stderr, | |
+ "XCreateIC failed. Could not obtain input method.\n" | |
+ ); | |
exit(1); | |
} | |
XSetICFocus(state.xic); | |
+ state.mode = INSERT; | |
+ | |
XMapWindow(state.dpy, state.win); | |
+ | |
+ ledit_init_cache(&state); | |
+ buffer = ledit_create_buffer(&state); | |
redraw(); | |
} | |
static void | |
cleanup(void) { | |
+ /* FIXME: cleanup everything else */ | |
+ ledit_destroy_cache(); | |
+ ledit_destroy_buffer(buffer); | |
XDestroyWindow(state.dpy, state.win); | |
XCloseDisplay(state.dpy); | |
} | |
static void | |
redraw(void) { | |
- XSetForeground(state.dpy, state.gc, BlackPixel(state.dpy, state.screen… | |
- XFillRectangle(state.dpy, state.back_buf, state.gc, 0, 0, state.w, sta… | |
-} | |
+ 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; | |
+ int cur_line_y = 0; | |
+ int cursor_displayed = 0; | |
+ for (int i = 0; i < buffer->lines_num; i++) { | |
+ ledit_line *line = ledit_get_line(buffer, i); | |
+ if (h + line->h > buffer->display_offset) { | |
+ if (line->dirty || line->cache_index == -1) { | |
+ ledit_render_line(buffer, i); | |
+ } | |
+ int final_y = 0; | |
+ int dest_y = h - buffer->display_offset; | |
+ int final_h = line->h; | |
+ if (h < buffer->display_offset) { | |
+ dest_y = 0; | |
+ final_y = buffer->display_offset - h; | |
+ final_h -= buffer->display_offset - h; | |
+ } | |
+ if (dest_y + final_h > state.h) { | |
+ final_h -= final_y + final_h - | |
+ buffer->display_offset - state.h; | |
+ } | |
+ ledit_cache_pixmap *pix = ledit_get_cache_pixmap( | |
+ line->cache_index | |
+ ); | |
+ XCopyArea( | |
+ state.dpy, pix->pixmap, | |
+ state.drawable, state.gc, | |
+ 0, final_y, line->w, final_h, 0, dest_y | |
+ ); | |
+ if (i == buffer->cur_line) { | |
+ cur_line_y = h - buffer->display_offset; | |
+ cursor_displayed = 1; | |
+ } | |
+ } | |
+ if (h + line->h >= buffer->display_offset + state.h) | |
+ break; | |
+ h += line->h; | |
+ } | |
-static void | |
-button_press(XEvent event) { | |
-} | |
+ XSetForeground(state.dpy, state.gc, state.fg.pixel); | |
+ PangoRectangle strong, weak; | |
+ ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line); | |
+ pango_layout_get_cursor_pos( | |
+ cur_line->layout, buffer->cur_index, &strong, &weak | |
+ ); | |
+ /* FIXME: long, int, etc. */ | |
+ int cursor_y = strong.y / PANGO_SCALE + cur_line_y; | |
+ if (cursor_displayed && cursor_y >= 0) { | |
+ if (state.mode == NORMAL && buffer->cur_index == cur_line->len… | |
+ XFillRectangle( | |
+ state.dpy, state.drawable, state.gc, | |
+ strong.x / PANGO_SCALE, cursor_y, | |
+ 10, strong.height / PANGO_SCALE | |
+ ); | |
+ } else if (state.mode == INSERT) { | |
+ XDrawLine( | |
+ state.dpy, state.drawable, state.gc, | |
+ strong.x / PANGO_SCALE, cursor_y, | |
+ strong.x / PANGO_SCALE, | |
+ (strong.y + strong.height) / PANGO_SCALE + cur_lin… | |
+ ); | |
+ } | |
+ } | |
+ if (buffer->total_height > state.h) { | |
+ XSetForeground(state.dpy, state.gc, state.scroll_bg.pixel); | |
+ XFillRectangle( | |
+ state.dpy, state.drawable, state.gc, | |
+ state.w - 10, 0, 10, state.h | |
+ ); | |
+ XSetForeground(state.dpy, state.gc, state.fg.pixel); | |
+ double scroll_h, scroll_y; | |
+ get_scroll_pos_height(&scroll_y, &scroll_h); | |
+ XFillRectangle( | |
+ state.dpy, state.drawable, state.gc, | |
+ state.w - 10, (int)round(scroll_y), 10, (int)round(scroll_… | |
+ ); | |
+ } | |
-static void | |
-button_release(void) { | |
+ XdbeSwapInfo swap_info; | |
+ swap_info.swap_window = state.win; | |
+ swap_info.swap_action = XdbeBackground; | |
+ | |
+ if (!XdbeSwapBuffers(state.dpy, &swap_info, 1)) | |
+ exit(1); | |
+ XFlush(state.dpy); | |
+} | |
+ | |
+static int | |
+button_press(XEvent *event) { | |
+ int x, y; | |
+ double scroll_h, scroll_y; | |
+ switch (event->xbutton.button) { | |
+ case Button1: | |
+ get_scroll_pos_height(&scroll_y, &scroll_h); | |
+ x = event->xbutton.x; | |
+ y = event->xbutton.y; | |
+ if (x >= state.w - 10) { | |
+ state.scroll_dragging = 1; | |
+ state.scroll_grab_handle = y; | |
+ if (y < scroll_y || y > scroll_y + scroll_h) { | |
+ double new_scroll_y = y - scroll_h / 2; | |
+ set_scroll_pos(new_scroll_y); | |
+ } | |
+ return 1; | |
+ } | |
+ break; | |
+ case Button4: | |
+ buffer->display_offset -= 10; | |
+ if (buffer->display_offset < 0) | |
+ buffer->display_offset = 0; | |
+ return 1; | |
+ case Button5: | |
+ if (buffer->display_offset + state.h < | |
+ buffer->total_height) { | |
+ buffer->display_offset += 10; | |
+ if (buffer->display_offset + state.h > | |
+ buffer->total_height) | |
+ buffer->display_offset = | |
+ buffer->total_height - state.h; | |
+ } | |
+ return 1; | |
+ } | |
+ return 0; | |
} | |
-static void | |
-ensure_cursor_shown(void) { | |
- PangoRectangle strong, weak; | |
- pango_layout_get_cursor_pos(lines[cur_line].layout, cur_index, &strong… | |
- long cursor_y = strong.y / PANGO_SCALE + lines[cur_line].y_offset; | |
- if (cursor_y < cur_display_offset) { | |
- cur_display_offset = cursor_y; | |
- } else if (cursor_y + strong.height / PANGO_SCALE > cur_display_offset… | |
- cur_display_offset = cursor_y - state.h + strong.height / PANG… | |
+static int | |
+button_release(XEvent *event) { | |
+ if (event->xbutton.button == Button1) { | |
+ state.scroll_dragging = 0; | |
+ return 1; | |
} | |
+ return 0; | |
} | |
-static void | |
-recalc_cur_line_size(void) { | |
- int w, h; | |
- pango_layout_get_pixel_size(lines[cur_line].layout, &w, &h); | |
- lines[cur_line].w = w; | |
- /* if height changed, set height of current line | |
- * and adjust offsets of all lines following it */ | |
- if (lines[cur_line].h != h) { | |
- int delta = h - lines[cur_line].h; | |
- lines[cur_line].h = h; | |
- /* protect against underflow even though | |
- * it should never happen anyways... */ | |
- if (delta < 0 && total_height < -delta) | |
- total_height = 0; | |
- else | |
- total_height += delta; | |
- for (int i = cur_line + 1; i < lines_num; i++) { | |
- /* yeah, maybe I should just use a signed type... */ | |
- if (delta < 0 && lines[i].y_offset < -delta) | |
- lines[i].y_offset = 0; | |
- else | |
- lines[i].y_offset += delta; | |
- } | |
+static int | |
+drag_motion(XEvent *event) { | |
+ if (state.scroll_dragging) { | |
+ double scroll_h, scroll_y; | |
+ get_scroll_pos_height(&scroll_y, &scroll_h); | |
+ scroll_y += event->xbutton.y - state.scroll_grab_handle; | |
+ state.scroll_grab_handle = event->xbutton.y; | |
+ set_scroll_pos(scroll_y); | |
+ return 1; | |
} | |
+ return 0; | |
} | |
static void | |
-recalc_line_size_absolute(void) { | |
- int w, h; | |
- total_height = 0; | |
- /* completely recalculate line sizes and offsets from scratch */ | |
- for (int i = 0; i < lines_num; i++) { | |
- lines[i].y_offset = total_height; | |
- pango_layout_get_pixel_size(lines[i].layout, &w, &h); | |
- total_height += h; | |
- lines[i].w = w; | |
- lines[i].h = h; | |
+ensure_cursor_shown(void) { | |
+ PangoRectangle strong, weak; | |
+ ledit_line *line = ledit_get_line(buffer, buffer->cur_line); | |
+ pango_layout_get_cursor_pos( | |
+ line->layout, buffer->cur_index, &strong, &weak | |
+ ); | |
+ long cursor_y = strong.y / PANGO_SCALE + line->y_offset; | |
+ if (cursor_y < buffer->display_offset) { | |
+ buffer->display_offset = cursor_y; | |
+ } else if (cursor_y + strong.height / PANGO_SCALE > | |
+ buffer->display_offset + state.h) { | |
+ buffer->display_offset = | |
+ cursor_y - state.h + strong.height / PANGO_SCALE; | |
} | |
} | |
+/* FIXME: move the recalculation part of this to buffer.c */ | |
static void | |
resize_window(int w, int h) { | |
state.w = w; | |
state.h = h; | |
- total_height = 0; | |
+ buffer->total_height = 0; | |
int tmp_w, tmp_h; | |
- for (int i = 0; i < lines_num; i++) { | |
+ for (int i = 0; i < buffer->lines_num; i++) { | |
+ ledit_line *line = ledit_get_line(buffer, i); | |
/* 10 pixels for scrollbar */ | |
- pango_layout_set_width(lines[i].layout, (w - 10) * PANGO_SCALE… | |
- pango_layout_get_pixel_size(lines[i].layout, &tmp_w, &tmp_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; | |
+ pango_layout_set_width(line->layout, (w - 10) * PANGO_SCALE); | |
+ pango_layout_get_pixel_size(line->layout, &tmp_w, &tmp_h); | |
+ line->h = tmp_h; | |
+ line->w = tmp_w; | |
+ line->y_offset = buffer->total_height; | |
+ line->dirty = 1; | |
+ buffer->total_height += tmp_h; | |
+ } | |
+ if (buffer->display_offset > 0 && | |
+ buffer->display_offset + state.h >= buffer->total_height) { | |
+ buffer->display_offset = buffer->total_height - state.h; | |
+ if (buffer->display_offset < 0) | |
+ buffer->display_offset = 0; | |
} | |
-} | |
- | |
-static void | |
-drag_motion(XEvent event) { | |
-} | |
- | |
-static void | |
-delete_line_entry(int index) { | |
- if (index < lines_num - 1) | |
- memmove(lines + index, lines + index + 1, (lines_num - index -… | |
- lines_num--; | |
} | |
static void | |
backspace(void) { | |
- if (cur_index == 0) { | |
- if (cur_line != 0) { | |
- struct line *l1 = &lines[cur_line - 1]; | |
- struct line *l2 = &lines[cur_line]; | |
+ if (buffer->cur_index == 0) { | |
+ if (buffer->cur_line != 0) { | |
+ ledit_line *l1 = ledit_get_line(buffer, buffer->cur_li… | |
+ ledit_line *l2 = ledit_get_line(buffer, buffer->cur_li… | |
int old_len = l1->len; | |
- insert_text(l1, l1->len, l2->text, l2->len); | |
- delete_line_entry(cur_line); | |
- cur_line--; | |
- cur_index = old_len; | |
- //total_height -= cur_line_height(); | |
- //set_cur_line_height(); | |
+ ledit_insert_text( | |
+ buffer, buffer->cur_line - 1, | |
+ l1->len, l2->text, l2->len | |
+ ); | |
+ ledit_delete_line_entry(buffer, buffer->cur_line); | |
+ buffer->cur_line--; | |
+ buffer->cur_index = old_len; | |
} | |
} else { | |
- int i = cur_index - 1; | |
- struct line *l = &lines[cur_line]; | |
- /* find valid utf8 char - this probably needs to be improved */ | |
- while (i > 0 && ((l->text[i] & 0xC0) == 0x80)) | |
- i--; | |
- memmove(l->text + i, l->text + cur_index, l->len - cur_index); | |
- l->len -= cur_index - i; | |
- cur_index = i; | |
- pango_layout_set_text(l->layout, l->text, l->len); | |
+ buffer->cur_index = ledit_delete_unicode_char( | |
+ buffer, buffer->cur_line, buffer->cur_index, -1 | |
+ ); | |
} | |
- set_line_cursor_attrs(cur_line, cur_index); | |
- recalc_cur_line_size(); | |
+ ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
} | |
static void | |
delete_key(void) { | |
- if (cur_index == lines[cur_line].len) { | |
- if (cur_line != lines_num - 1) { | |
- struct line *l1 = &lines[cur_line]; | |
- struct line *l2 = &lines[cur_line + 1]; | |
- int old_len = l1->len; | |
- insert_text(l1, l1->len, l2->text, l2->len); | |
- delete_line_entry(cur_line + 1); | |
- cur_index = old_len; | |
- /*total_height -= cur_line_height(); | |
- set_cur_line_height();*/ | |
+ ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line); | |
+ if (buffer->cur_index == cur_line->len) { | |
+ if (buffer->cur_line != buffer->lines_num - 1) { | |
+ ledit_line *next_line = ledit_get_line( | |
+ buffer, buffer->cur_line + 1 | |
+ ); | |
+ int old_len = cur_line->len; | |
+ ledit_insert_text( | |
+ buffer, buffer->cur_line, cur_line->len, | |
+ next_line->text, next_line->len | |
+ ); | |
+ ledit_delete_line_entry(buffer, buffer->cur_line + 1); | |
+ buffer->cur_index = old_len; | |
} | |
} else { | |
- int i = cur_index + 1; | |
- struct line *l = &lines[cur_line]; | |
- while (i < lines[cur_line].len && ((lines[cur_line].text[i] & … | |
- i++; | |
- memmove(l->text + cur_index, l->text + i, l->len - i); | |
- l->len -= i - cur_index; | |
- pango_layout_set_text(l->layout, l->text, l->len); | |
+ buffer->cur_index = ledit_delete_unicode_char( | |
+ buffer, buffer->cur_line, buffer->cur_index, 1 | |
+ ); | |
} | |
- set_line_cursor_attrs(cur_line, cur_index); | |
- recalc_cur_line_size(); | |
+ ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
} | |
static void | |
move_cursor(int dir) { | |
- int last_index = cur_index; | |
- pango_layout_move_cursor_visually(lines[cur_line].layout, TRUE, cur_in… | |
+ int last_index = buffer->cur_index; | |
+ ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line); | |
+ pango_layout_move_cursor_visually( | |
+ cur_line->layout, TRUE, | |
+ buffer->cur_index, buffer->trailing, dir, | |
+ &buffer->cur_index, &buffer->trailing | |
+ ); | |
/* we don't currently support a difference between the cursor being at | |
the end of a soft line and the beginning of the next line */ | |
- while (trailing > 0) { | |
- trailing--; | |
- cur_index++; | |
- while (cur_index < lines[cur_line].len && ((lines[cur_line].te… | |
- cur_index++; | |
+ while (buffer->trailing > 0) { | |
+ buffer->trailing--; | |
+ buffer->cur_index++; | |
+ while (buffer->cur_index < cur_line->len && | |
+ ((cur_line->text[buffer->cur_index] & 0xC0) == 0x80)) | |
+ buffer->cur_index++; | |
} | |
- if (cur_index < 0) | |
- cur_index = 0; | |
+ if (buffer->cur_index < 0) | |
+ buffer->cur_index = 0; | |
/* when in normal mode, the cursor cannot be at the very end | |
of the line because it's always covering a character */ | |
- if (cur_index >= lines[cur_line].len) { | |
- if (cur_mode == NORMAL) | |
- cur_index = last_index; | |
+ if (buffer->cur_index >= cur_line->len) { | |
+ if (state.mode == NORMAL) | |
+ buffer->cur_index = last_index; | |
else | |
- cur_index = lines[cur_line].len; | |
+ buffer->cur_index = cur_line->len; | |
} | |
- set_line_cursor_attrs(cur_line, cur_index); | |
+ ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
} | |
static void | |
t@@ -800,112 +614,129 @@ cursor_right(void) { | |
static void | |
return_key(void) { | |
- append_line(cur_index, cur_line); | |
+ ledit_append_line(buffer, buffer->cur_line, buffer->cur_index); | |
/* FIXME: these aren't needed, right? This only works in insert mode | |
* anyways, so there's nothing to wipe */ | |
- wipe_line_cursor_attrs(cur_line); | |
- cur_line++; | |
- set_line_cursor_attrs(cur_line, cur_index); | |
- cur_index = 0; | |
- recalc_line_size_absolute(); | |
+ ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
+ buffer->cur_line++; | |
+ ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
+ buffer->cur_index = 0; | |
} | |
static void | |
escape_key(void) { | |
- cur_mode = NORMAL; | |
+ state.mode = NORMAL; | |
PangoDirection dir = PANGO_DIRECTION_RTL; | |
- int tmp_index = cur_index; | |
- if (cur_index >= lines[cur_line].len) | |
+ int tmp_index = buffer->cur_index; | |
+ ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line); | |
+ if (buffer->cur_index >= cur_line->len) | |
tmp_index--; | |
if (tmp_index >= 0) | |
- dir = pango_layout_get_direction(lines[cur_line].layout, tmp_i… | |
+ dir = pango_layout_get_direction(cur_line->layout, tmp_index); | |
if (dir == PANGO_DIRECTION_RTL || dir == PANGO_DIRECTION_WEAK_RTL) { | |
cursor_right(); | |
} else { | |
cursor_left(); | |
} | |
- set_line_cursor_attrs(cur_line, cur_index); | |
- /* | |
- if (cur_index > 0) | |
- cursor_left(); | |
- */ | |
+ ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
} | |
static void | |
i_key(void) { | |
- cur_mode = INSERT; | |
- /* | |
- for (int i = 0; i < lines_num; i++) { | |
- pango_layout_set_attributes(lines[i].layout, NULL); | |
- } | |
- */ | |
- wipe_line_cursor_attrs(cur_line); | |
+ state.mode = INSERT; | |
+ ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
} | |
static void | |
line_down(void) { | |
- int lineno, x, trailing; | |
- pango_layout_index_to_line_x(lines[cur_line].layout, cur_index, 0, &li… | |
- int maxlines = pango_layout_get_line_count(lines[cur_line].layout); | |
- PangoLayoutLine *line = pango_layout_get_line_readonly(lines[cur_line]… | |
+ int lineno, x; | |
+ ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line); | |
+ pango_layout_index_to_line_x( | |
+ cur_line->layout, buffer->cur_index, 0, &lineno, &x | |
+ ); | |
+ int maxlines = pango_layout_get_line_count(cur_line->layout); | |
if (lineno == maxlines - 1) { | |
- wipe_line_cursor_attrs(cur_line); | |
+ ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
/* move to the next hard line */ | |
- if (cur_line < lines_num - 1) { | |
- cur_line++; | |
- PangoLayoutLine *nextline = pango_layout_get_line_read… | |
- if (pango_layout_line_x_to_index(nextline, x, &cur_ind… | |
+ if (buffer->cur_line < buffer->lines_num - 1) { | |
+ buffer->cur_line++; | |
+ cur_line = ledit_get_line(buffer, buffer->cur_line); | |
+ PangoLayoutLine *nextline = | |
+ pango_layout_get_line_readonly(cur_line->layout, 0… | |
+ if (pango_layout_line_x_to_index( | |
+ nextline, x, &buffer->cur_index, | |
+ &buffer->trailing) == FALSE) { | |
/* set it to *after* the last index of the lin… | |
- cur_index = nextline->start_index + nextline->… | |
+ buffer->cur_index = | |
+ nextline->start_index + nextline->length; | |
} | |
} | |
} else { | |
/* move to the next soft line */ | |
- PangoLayoutLine *nextline = pango_layout_get_line_readonly(lin… | |
- if (pango_layout_line_x_to_index(nextline, x, &cur_index, &tra… | |
+ PangoLayoutLine *nextline = | |
+ pango_layout_get_line_readonly(cur_line->layout, lineno + … | |
+ if (pango_layout_line_x_to_index( | |
+ nextline, x, &buffer->cur_index, | |
+ &buffer->trailing) == FALSE) { | |
/* set it to *after* the last index of the line */ | |
- cur_index = nextline->start_index + nextline->length; | |
+ buffer->cur_index = | |
+ nextline->start_index + nextline->length; | |
} | |
} | |
- if (cur_index > 0 && cur_mode == NORMAL && cur_index >= lines[cur_line… | |
+ if (buffer->cur_index > 0 && | |
+ state.mode == NORMAL && | |
+ buffer->cur_index >= cur_line->len) | |
cursor_left(); | |
- set_line_cursor_attrs(cur_line, cur_index); | |
+ ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
} | |
static void | |
line_up(void) { | |
- int lineno, x, trailing; | |
- pango_layout_index_to_line_x(lines[cur_line].layout, cur_index, 0, &li… | |
- PangoLayoutLine *line = pango_layout_get_line_readonly(lines[cur_line]… | |
+ int lineno, x; | |
+ ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line); | |
+ pango_layout_index_to_line_x( | |
+ cur_line->layout, buffer->cur_index, 0, &lineno, &x | |
+ ); | |
if (lineno == 0) { | |
- wipe_line_cursor_attrs(cur_line); | |
+ ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
/* move to the previous hard line */ | |
- if (cur_line > 0) { | |
- cur_line--; | |
- int maxlines = pango_layout_get_line_count(lines[cur_l… | |
- PangoLayoutLine *prevline = pango_layout_get_line_read… | |
- if (pango_layout_line_x_to_index(prevline, x, &cur_ind… | |
+ if (buffer->cur_line > 0) { | |
+ buffer->cur_line--; | |
+ cur_line = ledit_get_line(buffer, buffer->cur_line); | |
+ int maxlines = pango_layout_get_line_count(cur_line->l… | |
+ PangoLayoutLine *prevline = | |
+ pango_layout_get_line_readonly(cur_line->layout, m… | |
+ if (pango_layout_line_x_to_index( | |
+ prevline, x, &buffer->cur_index, | |
+ &buffer->trailing) == FALSE) { | |
/* set it to *after* the last index of the lin… | |
- cur_index = prevline->start_index + prevline->… | |
+ buffer->cur_index = | |
+ prevline->start_index + prevline->length; | |
} | |
} | |
} else { | |
/* move to the previous soft line */ | |
- PangoLayoutLine *prevline = pango_layout_get_line_readonly(lin… | |
- if (pango_layout_line_x_to_index(prevline, x, &cur_index, &tra… | |
+ PangoLayoutLine *prevline = | |
+ pango_layout_get_line_readonly(cur_line->layout, lineno - … | |
+ if (pango_layout_line_x_to_index( | |
+ prevline, x, &buffer->cur_index, | |
+ &buffer->trailing) == FALSE) { | |
/* set it to *after* the last index of the line */ | |
- cur_index = prevline->start_index + prevline->length; | |
+ buffer->cur_index = | |
+ prevline->start_index + prevline->length; | |
} | |
} | |
- if (cur_index > 0 && cur_mode == NORMAL && cur_index >= lines[cur_line… | |
+ if (buffer->cur_index > 0 && | |
+ state.mode == NORMAL && | |
+ buffer->cur_index >= cur_line->len) | |
cursor_left(); | |
- set_line_cursor_attrs(cur_line, cur_index); | |
+ ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
} | |
static void | |
zero_key(void) { | |
- cur_index = 0; | |
- set_line_cursor_attrs(cur_line, cur_index); | |
+ buffer->cur_index = 0; | |
+ ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
} | |
static struct key keys_en[] = { | |
t@@ -959,7 +790,7 @@ static struct key keys_hi[] = { | |
{"0", 0, NORMAL, &zero_key} | |
}; | |
-#define LENGTH(X) (sizeof X / sizeof X[0]) | |
+#define LENGTH(X) (sizeof(X) / sizeof(X[0])) | |
struct lang_keys { | |
char *lang; | |
t@@ -978,7 +809,7 @@ static struct lang_keys *cur_keys = &keys[0]; | |
static void change_keyboard(char *lang) { | |
printf("%s\n", lang); | |
- for (int i = 0; i < LENGTH(keys); i++) { | |
+ for (size_t i = 0; i < LENGTH(keys); i++) { | |
if (!strcmp(keys[i].lang, lang)) { | |
cur_keys = &keys[i]; | |
break; | |
t@@ -988,27 +819,32 @@ static void change_keyboard(char *lang) { | |
static void | |
key_press(XEvent event) { | |
- XWindowAttributes attrs; | |
char buf[32]; | |
KeySym sym; | |
/* FIXME: X_HAVE_UTF8_STRING See XmbLookupString(3) */ | |
- int n = Xutf8LookupString(state.xic, &event.xkey, buf, sizeof(buf), &s… | |
+ int n = Xutf8LookupString( | |
+ state.xic, &event.xkey, buf, sizeof(buf), &sym, NULL | |
+ ); | |
int found = 0; | |
for (int i = 0; i < cur_keys->num_keys; i++) { | |
if (cur_keys->keys[i].text) { | |
- if (n > 0 && (cur_keys->keys[i].modes & cur_mode) && !… | |
+ if (n > 0 && | |
+ (cur_keys->keys[i].modes & state.mode) && | |
+ !strncmp(cur_keys->keys[i].text, buf, n)) { | |
cur_keys->keys[i].func(); | |
found = 1; | |
} | |
- } else if ((cur_keys->keys[i].modes & cur_mode) && cur_keys->k… | |
+ } else if ((cur_keys->keys[i].modes & state.mode) && | |
+ cur_keys->keys[i].keysym == sym) { | |
cur_keys->keys[i].func(); | |
found = 1; | |
} | |
} | |
- if (cur_mode == INSERT && !found && n > 0) { | |
- insert_text(&lines[cur_line], cur_index, buf, n); | |
- cur_index += n; | |
- recalc_cur_line_size(); | |
+ if (state.mode == INSERT && !found && n > 0) { | |
+ ledit_insert_text( | |
+ buffer, buffer->cur_line, buffer->cur_index, buf, n | |
+ ); | |
+ buffer->cur_index += n; | |
} | |
ensure_cursor_shown(); | |
} | |
diff --git a/memory.c b/memory.c | |
t@@ -0,0 +1,41 @@ | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+ | |
+static void | |
+fatal_err(const char *msg) { | |
+ fprintf(stderr, "%s", msg); | |
+ exit(1); | |
+} | |
+ | |
+char * | |
+ledit_strdup(const char *s) { | |
+ char *str = strdup(s); | |
+ if (!str) | |
+ fatal_err("Out of memory.\n"); | |
+ return str; | |
+} | |
+ | |
+void * | |
+ledit_malloc(size_t size) { | |
+ void *ptr = malloc(size); | |
+ if (!ptr) | |
+ fatal_err("Out of memory.\n"); | |
+ return ptr; | |
+} | |
+ | |
+void * | |
+ledit_calloc(size_t nmemb, size_t size) { | |
+ void *ptr = calloc(nmemb, size); | |
+ if (!ptr) | |
+ fatal_err("Out of memory.\n"); | |
+ return ptr; | |
+} | |
+ | |
+void * | |
+ledit_realloc(void *ptr, size_t size) { | |
+ void *new_ptr = realloc(ptr, size); | |
+ if (!new_ptr) | |
+ fatal_err("Out of memory.\n"); | |
+ return new_ptr; | |
+} | |
diff --git a/memory.h b/memory.h | |
t@@ -0,0 +1,4 @@ | |
+char *ledit_strdup(const char *s); | |
+void *ledit_malloc(size_t size); | |
+void *ledit_calloc(size_t nmemb, size_t size); | |
+void *ledit_realloc(void *ptr, size_t size); |