Introduction
Introduction Statistics Contact Development Disclaimer Help
tAdd initial work that was done without version control - ledit - Text editor (…
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 78088f20cdb93ead1b980f3a4092ce0a1227aafa
Author: lumidify <[email protected]>
Date: Thu, 1 Apr 2021 19:31:23 +0200
Add initial work that was done without version control
Diffstat:
A .gitignore | 4 ++++
A Makefile | 24 ++++++++++++++++++++++++
A ledit.c | 859 +++++++++++++++++++++++++++++…
3 files changed, 887 insertions(+), 0 deletions(-)
---
diff --git a/.gitignore b/.gitignore
t@@ -0,0 +1,4 @@
+tmp
+ledit
+*.core
+*.o
diff --git a/Makefile b/Makefile
t@@ -0,0 +1,24 @@
+.POSIX:
+
+NAME = ledit
+VERSION = -999-prealpha0
+
+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
+
+all: ${BIN}
+
+.c:
+ ${CC} ${CFLAGS} ${LDFLAGS} -o $@ $<
+
+clean:
+ rm -f ${BIN}
+
+.PHONY: all clean
diff --git a/ledit.c b/ledit.c
t@@ -0,0 +1,859 @@
+#include <math.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+#include <locale.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include <X11/XF86keysym.h>
+#include <X11/cursorfont.h>
+
+#include <pango/pangoxft.h>
+
+#include <X11/XKBlib.h>
+#include <X11/extensions/XKBrules.h>
+#include <X11/extensions/Xdbe.h>
+
+static enum mode {
+ NORMAL = 1,
+ INSERT = 2,
+ VISUAL = 4
+} cur_mode = INSERT;
+
+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 */
+};
+
+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;
+
+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 void resize_window(int w, int h);
+static void button_release(void);
+static void button_press(XEvent event);
+static void key_press(XEvent event);
+
+int
+main(int argc, char *argv[]) {
+ setup(argc, argv);
+ mainloop();
+ cleanup();
+
+ return 0;
+}
+
+static struct line {
+ PangoLayout *layout;
+ XftDraw *draw;
+ char *text;
+ size_t cap;
+ size_t len;
+ Pixmap pix;
+ int w;
+ int h;
+ int pix_w;
+ int pix_h;
+ char dirty;
+} *lines = NULL;
+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 int total_height = 0;
+static int cur_display_offset = 0;
+
+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->pix = None;
+ /* FIXME: does this set line height reasonably when no text yet? */
+ pango_layout_get_pixel_size(l->layout, &l->w, &l->h);
+ l->dirty = 1;
+}
+
+static void recalc_height_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_height_absolute();
+ l->dirty = 1;
+}
+
+static void insert_line_entry(int index);
+
+static void
+render_line(struct line *l) {
+ /* FIXME: check for <= 0 on size */
+ if (l->pix == None) {
+ l->pix = XCreatePixmap(state.dpy, state.back_buf, l->w + 10, l…
+ l->pix_w = l->w + 10;
+ l->pix_h = l->h + 10;
+ l->draw = XftDrawCreate(state.dpy, l->pix, state.vis, state.cm…
+ } else if (l->pix_w < l->w || l->pix_h < l->h) {
+ int new_w = l->w > l->pix_w ? l->w + 10 : l->pix_w + 10;
+ int new_h = l->h > l->pix_h ? l->h + 10 : l->pix_h + 10;
+ XFreePixmap(state.dpy, l->pix);
+ l->pix = XCreatePixmap(state.dpy, state.back_buf, new_w, new_h…
+ l->pix_w = new_w;
+ l->pix_h = new_h;
+ XftDrawChange(l->draw, l->pix);
+ }
+ XftDrawRect(l->draw, &state.bg, 0, 0, l->w, l->h);
+ pango_xft_render_layout(l->draw, &state.fg, l->layout, 0, 0);
+ 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->pix = None;
+ 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 */
+ }
+}
+
+static void change_keyboard(char *lang);
+
+PangoAttrList *basic_attrs;
+
+static void
+mainloop(void) {
+ XEvent event;
+ int xkb_event_type;
+ int major, minor;
+ if (!XkbQueryExtension(state.dpy, 0, &xkb_event_type, NULL, &major, &m…
+ fprintf(stderr, "XKB not supported.");
+ exit(1);
+ }
+ 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…
+ 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…
+ int need_redraw = 0;
+ redraw();
+
+ while (running) {
+ do {
+ XNextEvent(state.dpy, &event);
+ if (event.type == xkb_event_type) {
+ change_kbd = 1;
+ continue;
+ }
+ if (XFilterEvent(&event, None))
+ continue;
+ switch (event.type) {
+ case Expose:
+ redraw();
+ 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();
+ need_redraw = 1;
+ break;
+ case ButtonPress:
+ switch (event.xbutton.button) {
+ case Button1:
+ button_press(event);
+ 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;
+ break;
+ case ButtonRelease:
+ if (event.xbutton.button == Button1)
+ button_release();
+ break;
+ case MotionNotify:
+ drag_motion(event);
+ break;
+ case KeyPress:
+ need_redraw = 1;
+ key_press(event);
+ break;
+ case ClientMessage:
+ if ((Atom)event.xclient.data.l[0] == state.wm_…
+ running = 0;
+ default:
+ break;
+ }
+ } while (XPending(state.dpy));
+
+ if (change_kbd) {
+ change_kbd = 0;
+ XkbStateRec s;
+ XkbGetState(state.dpy, XkbUseCoreKbd, &s);
+ XkbDescPtr desc = XkbGetKeyboard(state.dpy, XkbAllComp…
+ char *group = XGetAtomName(state.dpy, desc->names->gro…
+ 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 (lines[i].dirty) {
+ if (i == cur_line && cur_mode == NORMA…
+ PangoAttribute *attr0 = pango_…
+ PangoAttribute *attr1 = pango_…
+ attr0->start_index = cur_index;
+ attr0->end_index = cur_index +…
+ attr1->start_index = cur_index;
+ attr1->end_index = cur_index +…
+ PangoAttribute *attr2 = pango_…
+ PangoAttrList *list = pango_at…
+ pango_attr_list_insert(list, a…
+ pango_attr_list_insert(list, a…
+ pango_attr_list_insert(list, a…
+ pango_layout_set_attributes(li…
+ } else {
+ pango_layout_set_attributes(li…
+ }
+ render_line(&lines[i]);
+ }
+ if (h + lines[i].h > cur_display_offset) {
+ 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, lines[i].pix, sta…
+ 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;
+ }
+ need_redraw = 0;
+
+ XSetForeground(state.dpy, state.gc, BlackPixel(state.d…
+ PangoRectangle strong, weak;
+ pango_layout_get_cursor_pos(lines[cur_line].layout, cu…
+ 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) {
+ double scroll_h = ((double)state.h / total_hei…
+ double scroll_y = ((double)cur_display_offset …
+ 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
+setup(int argc, char *argv[]) {
+ setlocale(LC_CTYPE, "");
+ XSetLocaleModifiers("");
+ XSetWindowAttributes attrs;
+ XGCValues gcv;
+
+ state.w = 500;
+ state.h = 500;
+ state.dpy = XOpenDisplay(NULL);
+ state.screen = DefaultScreen(state.dpy);
+
+ /* 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…
+ int num_screens = 1;
+ Drawable screens[] = { DefaultRootWindow(state.dpy) };
+ XdbeScreenVisualInfo *info = XdbeGetVisualInfo(state.dpy, scre…
+ 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 …
+ xvisinfo_templ.screen = 0;
+ xvisinfo_templ.depth = info->visinfo[0].depth;
+ int matches;
+ XVisualInfo *xvisinfo_match =
+ XGetVisualInfo(state.dpy, VisualIDMask|VisualScreenMas…
+ if (!xvisinfo_match || matches < 1) {
+ fprintf(stderr, "Couldn't match a Visual with double b…
+ exit(1);
+ }
+ state.vis = xvisinfo_match->visual;
+ } else {
+ fprintf(stderr, "No Xdbe support.\n");
+ exit(1);
+ }
+
+ state.depth = DefaultDepth(state.dpy, state.screen);
+ state.cm = DefaultColormap(state.dpy, state.screen);
+
+ memset(&attrs, 0, sizeof(attrs));
+ attrs.background_pixmap = None;
+ attrs.colormap = state.cm;
+ state.win = XCreateWindow(state.dpy, DefaultRootWindow(state.dpy), 0, …
+ state.w, state.h, 0, state.depth,
+ InputOutput, state.vis, CWBackPixmap | CWColormap, &attrs);
+
+ state.back_buf = XdbeAllocateBackBufferName(state.dpy, state.win, Xdbe…
+
+ 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.wm_delete_msg = XInternAtom(state.dpy, "WM_DELETE_WINDOW", False…
+ XSetWMProtocols(state.dpy, state.win, &state.wm_delete_msg, 1);
+
+ /* blatantly stolen from st (simple terminal) */
+ if ((state.xim = XOpenIM(state.dpy, NULL, NULL, NULL)) == NULL) {
+ XSetLocaleModifiers("@im=local");
+ 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…
+ exit(1);
+ }
+ }
+ }
+ state.xic = XCreateIC(state.xim, XNInputStyle, XIMPreeditNothing
+ | XIMStatusNothing, XNClientWindow,…
+ XNFocusWindow, state.win, NULL);
+ if (state.xic == NULL) {
+ fprintf(stderr, "XCreateIC failed. Could not obtain input meth…
+ exit(1);
+ }
+ XSetICFocus(state.xic);
+
+ XMapWindow(state.dpy, state.win);
+ redraw();
+}
+
+static void
+cleanup(void) {
+ 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…
+}
+
+static void
+button_press(XEvent event) {
+}
+
+static void
+button_release(void) {
+}
+
+static void
+recalc_height(void) {
+ /*
+ int w, h;
+ pango_layout_get_pixel_size(lines[cur_line].layout, &w, &h);
+ total_height += (h - cur_line_height);
+ */
+ if (total_height < 0)
+ total_height = 0; /* should never actually happen */
+ /*cur_line_height = h;*/
+}
+
+static void
+set_cur_line_height(void) {
+ int w, h;
+ pango_layout_get_pixel_size(lines[cur_line].layout, &w, &h);
+ lines[cur_line].h = h;
+ /*cur_line_height = h;*/
+}
+
+static void
+recalc_height_absolute(void) {
+ int w, h;
+ total_height = 0;
+ for (int i = 0; i < lines_num; i++) {
+ pango_layout_get_pixel_size(lines[i].layout, &w, &h);
+ total_height += h;
+ lines[i].w = w;
+ lines[i].h = h;
+ }
+ set_cur_line_height();
+}
+
+static void
+resize_window(int w, int h) {
+ state.w = w;
+ state.h = h;
+ total_height = 0;
+ int tmp_w, tmp_h;
+ for (int i = 0; i < lines_num; 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);
+ total_height += tmp_h;
+ lines[i].h = tmp_h;
+ lines[i].dirty = 1;
+ }
+ //set_cur_line_height();
+}
+
+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];
+ 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();
+ }
+ } 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);
+ }
+ lines[cur_line].dirty = 1;
+ recalc_height_absolute();
+}
+
+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();*/
+ }
+ } 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);
+ }
+ lines[cur_line].dirty = 1;
+ recalc_height_absolute();
+}
+
+static void
+move_cursor(int dir) {
+ int last_index = cur_index;
+ pango_layout_move_cursor_visually(lines[cur_line].layout, TRUE, cur_in…
+ /* 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++;
+ }
+ if (cur_index < 0)
+ 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;
+ else
+ cur_index = lines[cur_line].len;
+ }
+ lines[cur_line].dirty = 1;
+}
+
+static void
+cursor_left(void) {
+ move_cursor(-1);
+}
+
+static void
+cursor_right(void) {
+ move_cursor(1);
+}
+
+static void
+return_key(void) {
+ append_line(cur_index, cur_line);
+ lines[cur_line].dirty = 1;
+ cur_line++;
+ lines[cur_line].dirty = 1;
+ cur_index = 0;
+ recalc_height_absolute();
+}
+
+static void
+escape_key(void) {
+ cur_mode = NORMAL;
+ PangoDirection dir = PANGO_DIRECTION_RTL;
+ int tmp_index = cur_index;
+ if (cur_index >= lines[cur_line].len)
+ tmp_index--;
+ if (tmp_index >= 0)
+ dir = pango_layout_get_direction(lines[cur_line].layout, tmp_i…
+ if (dir == PANGO_DIRECTION_RTL || dir == PANGO_DIRECTION_WEAK_RTL) {
+ cursor_right();
+ } else {
+ cursor_left();
+ }
+ lines[cur_line].dirty = 1;
+ /*
+ if (cur_index > 0)
+ cursor_left();
+ */
+}
+
+static void
+i_key(void) {
+ cur_mode = INSERT;
+ /*
+ for (int i = 0; i < lines_num; i++) {
+ pango_layout_set_attributes(lines[i].layout, NULL);
+ }
+ */
+ lines[cur_line].dirty = 1;
+}
+
+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]…
+ if (lineno == maxlines - 1) {
+ lines[cur_line].dirty = 1;
+ /* 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…
+ /* set it to *after* the last index of the lin…
+ cur_index = nextline->start_index + nextline->…
+ }
+ }
+ } 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…
+ /* set it to *after* the last index of the line */
+ cur_index = nextline->start_index + nextline->length;
+ }
+ }
+ if (cur_index > 0 && cur_mode == NORMAL && cur_index >= lines[cur_line…
+ cursor_left();
+ lines[cur_line].dirty = 1;
+}
+
+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]…
+ if (lineno == 0) {
+ lines[cur_line].dirty = 1;
+ /* 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…
+ /* set it to *after* the last index of the lin…
+ cur_index = prevline->start_index + prevline->…
+ }
+ }
+ } 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…
+ /* set it to *after* the last index of the line */
+ cur_index = prevline->start_index + prevline->length;
+ }
+ }
+ if (cur_index > 0 && cur_mode == NORMAL && cur_index >= lines[cur_line…
+ cursor_left();
+ lines[cur_line].dirty = 1;
+}
+
+static void
+zero_key(void) {
+ cur_index = 0;
+ lines[cur_line].dirty = 1;
+}
+
+static struct key keys_en[] = {
+ {NULL, XK_BackSpace, INSERT, &backspace},
+ {NULL, XK_Left, INSERT|NORMAL, &cursor_left},
+ {NULL, XK_Right, INSERT|NORMAL, &cursor_right},
+ {NULL, XK_Up, INSERT|NORMAL, &line_up},
+ {NULL, XK_Down, INSERT|NORMAL, &line_down},
+ {NULL, XK_Return, INSERT, &return_key},
+ {NULL, XK_Delete, INSERT, &delete_key},
+ {NULL, XK_Escape, INSERT, &escape_key},
+ {"i", 0, NORMAL, &i_key},
+ {"h", 0, NORMAL, &cursor_left},
+ {"l", 0, NORMAL, &cursor_right},
+ {"j", 0, NORMAL, &line_down},
+ {"k", 0, NORMAL, &line_up},
+ {"0", 0, NORMAL, &zero_key}
+};
+
+static struct key keys_ur[] = {
+ {NULL, XK_BackSpace, INSERT, &backspace},
+ {NULL, XK_Left, INSERT|NORMAL, &cursor_left},
+ {NULL, XK_Right, INSERT|NORMAL, &cursor_right},
+ {NULL, XK_Up, INSERT|NORMAL, &line_up},
+ {NULL, XK_Down, INSERT|NORMAL, &line_down},
+ {NULL, XK_Return, INSERT, &return_key},
+ {NULL, XK_Delete, INSERT, &delete_key},
+ {NULL, XK_Escape, INSERT, &escape_key},
+ {"ی", 0, NORMAL, &i_key},
+ {"ح", 0, NORMAL, &cursor_left},
+ {"ل", 0, NORMAL, &cursor_right},
+ {"ج", 0, NORMAL, &line_down},
+ {"ک", 0, NORMAL, &line_up},
+ {"0", 0, NORMAL, &zero_key}
+};
+
+static struct key keys_hi[] = {
+ {NULL, XK_BackSpace, INSERT, &backspace},
+ {NULL, XK_Left, INSERT|NORMAL, &cursor_left},
+ {NULL, XK_Right, INSERT|NORMAL, &cursor_right},
+ {NULL, XK_Up, INSERT|NORMAL, &line_up},
+ {NULL, XK_Down, INSERT|NORMAL, &line_down},
+ {NULL, XK_Return, INSERT, &return_key},
+ {NULL, XK_Delete, INSERT, &delete_key},
+ {NULL, XK_Escape, INSERT, &escape_key},
+ {"ि", 0, NORMAL, &i_key},
+ {"ह", 0, NORMAL, &cursor_left},
+ {"ल", 0, NORMAL, &cursor_right},
+ {"ज", 0, NORMAL, &line_down},
+ {"क", 0, NORMAL, &line_up},
+ {"0", 0, NORMAL, &zero_key}
+};
+
+#define LENGTH(X) (sizeof X / sizeof X[0])
+
+struct lang_keys {
+ char *lang;
+ struct key *keys;
+ int num_keys;
+};
+
+static struct lang_keys keys[] = {
+ {"English (US)", keys_en, LENGTH(keys_en)},
+ {"German", keys_en, LENGTH(keys_en)},
+ {"Urdu (Pakistan)", keys_ur, LENGTH(keys_ur)},
+ {"Hindi (Bolnagri)", keys_hi, LENGTH(keys_hi)}
+};
+
+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++) {
+ if (!strcmp(keys[i].lang, lang)) {
+ cur_keys = &keys[i];
+ break;
+ }
+ }
+}
+
+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 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) && !…
+ cur_keys->keys[i].func();
+ found = 1;
+ }
+ } else if ((cur_keys->keys[i].modes & cur_mode) && cur_keys->k…
+ 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;
+ }
+}
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.