commit 824365813f86fcdc01434477f39a8734183a8b18
parent 88b7f6c08a1e2a47f33aa5b4d33f18df60b5011f
Author: lumidify <nobody@lumidify.org>
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
@@ -1,5 +1,4 @@
tmp
ledit
-ledit_old
*.core
*.o
diff --git a/Makefile b/Makefile
@@ -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 pangoxft xext`
-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-config --cflags x11 xkbfile pangoxft xext`
+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
@@ -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(FALSE);
+ 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, 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_attrs);
+ }
+ 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, int len) {
+ 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 - index);
+ 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, 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_SCALE);
+ 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, int dir) {
+ 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
@@ -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 *text, int len);
+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_index, int dir);
diff --git a/cache.c b/cache.c
@@ -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 last entry that was replaced */
+ for (int i = 1; i <= cache.entries_num; i++) {
+ entry_index = (i + cache.cur_replace_index) % cache.entries_num;
+ line_index = cache.entries[entry_index].line;
+ /* replace line when entry isn't assigned or currently assigned line is not visible */
+ 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 * sizeof(ledit_cache_pixmap));
+ 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
@@ -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
@@ -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
@@ -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 overflow) */
#include <math.h>
#include <stdio.h>
#include <errno.h>
@@ -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
@@ -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 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) {
- /* 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 + 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
-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 - (line_index + 1)) * sizeof(struct line));
- 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 - *height);
+ *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, 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
@@ -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 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);
+ * 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_bg);
int need_redraw = 0;
redraw();
@@ -358,63 +133,20 @@ mainloop(void) {
need_redraw = 1;
break;
case ConfigureNotify:
- resize_window(event.xconfigure.width, event.xconfigure.height);
- if (cur_display_offset > 0 && cur_display_offset + state.h >= total_height) {
- cur_display_offset = total_height - state.h;
- 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_y, &scroll_h);
- 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 > scroll_y + scroll_h) {
- double new_scroll_y = y - scroll_h / 2;
- set_scroll_pos(new_scroll_y);
- }
- }
- break;
- case Button4:
- cur_display_offset -= 10;
- if (cur_display_offset < 0)
- cur_display_offset = 0;
- break;
- case Button5:
- if (cur_display_offset + state.h < total_height) {
- cur_display_offset += 10;
- if (cur_display_offset + state.h > total_height)
- cur_display_offset = total_height - state.h;
- }
- 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, &scroll_h);
- scroll_y += event.xbutton.y - scroll_grab_handle;
- 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;
@@ -432,90 +164,21 @@ mainloop(void) {
change_kbd = 0;
XkbStateRec s;
XkbGetState(state.dpy, XkbUseCoreKbd, &s);
- XkbDescPtr desc = XkbGetKeyboard(state.dpy, XkbAllComponentsMask, XkbUseCoreKbd);
- char *group = XGetAtomName(state.dpy, desc->names->groups[s.group]);
+ 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->symbols);*/
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, 0, state.w, state.h);
- 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_index == -1) {
- 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 - h;
- final_h -= cur_display_offset - h;
- }
- if (dest_y + final_h > state.h) {
- final_h -= final_y + final_h - cur_display_offset - state.h;
- }
- 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;
- }
- }
- if (h + lines[i].h >= cur_display_offset + state.h)
- 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, 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 (cur_mode == NORMAL && cur_index == lines[cur_line].len) {
- XFillRectangle(
- state.dpy, state.back_buf, state.gc,
- 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,
- strong.x / PANGO_SCALE, (strong.y + strong.height) / PANGO_SCALE + cur_line_y
- );
- }
- }
- if (total_height > state.h) {
- XSetForeground(state.dpy, state.gc, scroll_bg.pixel);
- XFillRectangle(state.dpy, state.back_buf, 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.back_buf, state.gc, state.w - 10, (int)round(scroll_y), 10, (int)round(scroll_h));
- }
-
- 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
@@ -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);
@@ -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", major, minor);
+ 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, screens, &num_screens);
+ 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 there's at least one
+ /* 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|VisualScreenMask|VisualDepthMask, &xvisinfo_templ, &matches);
+ 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 buffering\n");
+ fprintf(
+ stderr,
+ "Couldn't match a Visual with double buffering\n"
+ );
exit(1);
}
state.vis = xvisinfo_match->visual;
@@ -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, 0,
+ state.win = XCreateWindow(
+ state.dpy, DefaultRootWindow(state.dpy), 0, 0,
state.w, state.h, 0, state.depth,
- InputOutput, state.vis, CWBackPixel | CWColormap | CWBitGravity, &attrs);
+ InputOutput, state.vis,
+ CWBackPixel | CWColormap | CWBitGravity, &attrs
+ );
- state.back_buf = XdbeAllocateBackBufferName(state.dpy, state.win, XdbeBackground);
- 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 | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask);
+ 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.scroll_bg);
+
+ 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);
@@ -589,203 +286,320 @@ setup(int argc, char *argv[]) {
if ((state.xim = XOpenIM(state.dpy, NULL, NULL, NULL)) == NULL) {
XSetLocaleModifiers("@im=");
if ((state.xim = XOpenIM(state.dpy, NULL, NULL, NULL)) == NULL) {
- fprintf(stderr, "XOpenIM failed. Could not open input device.\n");
+ fprintf(
+ stderr,
+ "XOpenIM failed. Could not open input device.\n"
+ );
exit(1);
}
}
}
- state.xic = XCreateIC(state.xim, XNInputStyle, XIMPreeditNothing
- | XIMStatusNothing, XNClientWindow, state.win,
- 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 method.\n");
+ 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, state.h);
-}
+ 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_line_y
+ );
+ }
+ }
+ 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_h)
+ );
+ }
-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, &weak);
- 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 + state.h) {
- cur_display_offset = cursor_y - state.h + strong.height / PANGO_SCALE;
+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 - 1) * sizeof(struct line));
- 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_line - 1);
+ ledit_line *l2 = ledit_get_line(buffer, buffer->cur_line);
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_index);
}
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] & 0xC0) == 0x80))
- 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_index);
}
static void
move_cursor(int dir) {
- int last_index = cur_index;
- pango_layout_move_cursor_visually(lines[cur_line].layout, TRUE, cur_index, trailing, dir, &cur_index, &trailing);
+ 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].text[cur_index] & 0xC0) == 0x80))
- 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_index);
}
static void
@@ -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_index);
+ 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_index);
+ 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_index);
}
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, &lineno, &x);
- int maxlines = pango_layout_get_line_count(lines[cur_line].layout);
- PangoLayoutLine *line = pango_layout_get_line_readonly(lines[cur_line].layout, lineno);
+ 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_readonly(lines[cur_line].layout, 0);
- if (pango_layout_line_x_to_index(nextline, x, &cur_index, &trailing) == FALSE) {
+ 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 line */
- cur_index = nextline->start_index + nextline->length;
+ buffer->cur_index =
+ nextline->start_index + nextline->length;
}
}
} else {
/* move to the next soft line */
- PangoLayoutLine *nextline = pango_layout_get_line_readonly(lines[cur_line].layout, lineno + 1);
- if (pango_layout_line_x_to_index(nextline, x, &cur_index, &trailing) == FALSE) {
+ PangoLayoutLine *nextline =
+ pango_layout_get_line_readonly(cur_line->layout, lineno + 1);
+ 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].len)
+ 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_index);
}
static void
line_up(void) {
- int lineno, x, trailing;
- pango_layout_index_to_line_x(lines[cur_line].layout, cur_index, 0, &lineno, &x);
- PangoLayoutLine *line = pango_layout_get_line_readonly(lines[cur_line].layout, lineno);
+ 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_line].layout);
- PangoLayoutLine *prevline = pango_layout_get_line_readonly(lines[cur_line].layout, maxlines - 1);
- if (pango_layout_line_x_to_index(prevline, x, &cur_index, &trailing) == FALSE) {
+ 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->layout);
+ PangoLayoutLine *prevline =
+ pango_layout_get_line_readonly(cur_line->layout, maxlines - 1);
+ 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;
}
}
} else {
/* move to the previous soft line */
- PangoLayoutLine *prevline = pango_layout_get_line_readonly(lines[cur_line].layout, lineno - 1);
- if (pango_layout_line_x_to_index(prevline, x, &cur_index, &trailing) == FALSE) {
+ PangoLayoutLine *prevline =
+ pango_layout_get_line_readonly(cur_line->layout, lineno - 1);
+ 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].len)
+ 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_index);
}
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_index);
}
static struct key keys_en[] = {
@@ -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;
@@ -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;
@@ -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), &sym, NULL);
+ 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) && !strncmp(cur_keys->keys[i].text, buf, n)) {
+ 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->keys[i].keysym == sym) {
+ } 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
@@ -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
@@ -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);