Introduction
Introduction Statistics Contact Development Disclaimer Help
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);
You are viewing proxied material from lumidify.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.