Added runtime rcfile parsing. - sam - An updated version of the sam text editor. | |
git clone git://vernunftzentrum.de/sam.git | |
Log | |
Files | |
Refs | |
LICENSE | |
--- | |
commit 071c95698bc28739fb0289c84ff46a2278d1270c | |
parent 140ba1042462570fc3c966652ea1d6e79cd3ae93 | |
Author: Rob King <[email protected]> | |
Date: Fri, 9 Sep 2016 13:07:48 -0500 | |
Added runtime rcfile parsing. | |
Diffstat: | |
include/libg.h | 2 ++ | |
libXg/gwin.c | 121 ++++++++++++++++++++++--------- | |
libXg/libgint.h | 4 ++++ | |
libXg/xtbinit.c | 2 ++ | |
samterm/Makefile | 2 +- | |
samterm/main.c | 12 ++++++++++++ | |
samterm/samrc.c | 255 +++++++++++++++++++++++++++++++ | |
samterm/samterm.h | 3 +++ | |
8 files changed, 365 insertions(+), 36 deletions(-) | |
--- | |
diff --git a/include/libg.h b/include/libg.h | |
@@ -230,4 +230,6 @@ extern XftColor bgcolor; | |
#define BPSHORT(p, v) ((p)[0]=(v), (p)[1]=((v)>>8)) | |
#define BPLONG(p, v) (BPSHORT(p, (v)), BPSHORT(p+2, (v)>>16)) | |
+extern int installbinding(int, KeySym, int, int); | |
+extern int installchord(int, int, int, int); | |
#endif | |
diff --git a/libXg/gwin.c b/libXg/gwin.c | |
@@ -160,19 +160,6 @@ Mappingaction(Widget w, XEvent *e, String *p, Cardinal *np) | |
for (c = 0; c < composing; c++) \ | |
(*f)(compose[c], 0, Tcurrent, 0, 0) | |
-typedef struct Keymapping Keymapping; | |
-struct Keymapping{ | |
- int mask; | |
- int sym; | |
- int kind; | |
- int result; | |
-}; | |
- | |
-Keymapping keymappings[] ={ | |
- #include "../commands.h" | |
- {0, 0, Kend, 0} | |
-}; | |
- | |
typedef struct Unikeysym Unikeysym; | |
struct Unikeysym{ | |
KeySym keysym; | |
@@ -195,6 +182,49 @@ keysymtoshort(KeySym k) | |
return k; | |
} | |
+typedef struct Keymapping Keymapping; | |
+struct Keymapping{ | |
+ Keymapping *next; | |
+ int m; | |
+ KeySym s; | |
+ int k; | |
+ int c; | |
+}; | |
+ | |
+static Keymapping *keymappings = NULL; | |
+ | |
+int | |
+installbinding(int m, KeySym s, int k, int c) | |
+{ | |
+ if (m < 0 || s == NoSymbol || k < 0 || c < 0) | |
+ return -1; | |
+ | |
+ Keymapping *km = calloc(1, sizeof(Keymapping)); | |
+ if (!km) | |
+ return -1; | |
+ | |
+ km->m = m; | |
+ km->s = s; | |
+ km->k = k; | |
+ km->c = c; | |
+ | |
+ km->next = keymappings; | |
+ keymappings = km; | |
+ | |
+ return 0; | |
+} | |
+ | |
+void | |
+freebindings(void) | |
+{ | |
+ Keymapping *m = keymappings; | |
+ while (m){ | |
+ Keymapping *n = m->next; | |
+ free(m); | |
+ m = n; | |
+ } | |
+} | |
+ | |
static void | |
Keyaction(Widget w, XEvent *e, String *p, Cardinal *np) | |
{ | |
@@ -220,12 +250,12 @@ Keyaction(Widget w, XEvent *e, String *p, Cardinal *np) | |
XtTranslateKeycode(e->xany.display, (KeyCode)e->xkey.keycode, e->xkey.stat… | |
/* Check to see if it's a specially-handled key first. */ | |
- for (Keymapping *m = keymappings; m && m->kind != Kend; m++){ | |
- if (k == m->sym && m->kind != Kdefault){ | |
- if ((e->xkey.state & m->mask) || m->mask == 0){ | |
+ for (Keymapping *m = keymappings; m; m = m->next){ | |
+ if (k == m->s){ | |
+ if ((e->xkey.state & m->m) || m->m == 0){ | |
f = ((GwinWidget)w)->gwin.gotchar; | |
if (f) | |
- (*f)(m->result, m->kind, Tcurrent, 0, 0); | |
+ (*f)(m->c, m->k, Tcurrent, 0, 0); | |
return; | |
} | |
} | |
@@ -308,24 +338,45 @@ Keyaction(Widget w, XEvent *e, String *p, Cardinal *np) | |
typedef struct Chordmapping Chordmapping; | |
struct Chordmapping{ | |
- int start; | |
- int end; | |
- int kind; | |
- int result; | |
- int target; | |
+ Chordmapping *next; | |
+ int s1; | |
+ int s2; | |
+ int c; | |
+ int t; | |
}; | |
-#define Bnone 0 | |
-#define B1 1 | |
-#define B2 2 | |
-#define B3 4 | |
-#define B4 8 | |
-#define B5 16 | |
+static Chordmapping *chordmap = NULL; | |
-Chordmapping chordmappings[] ={ | |
- #include "../chords.h" | |
- {0, 0, Kend, 0, 0} | |
-}; | |
+int | |
+installchord(int s1, int s2, int c, int t) | |
+{ | |
+ if (s1 < 0 || s2 < 0 || c < 0 || (t != Tmouse && t != Tcurrent)) | |
+ return -1; | |
+ | |
+ Chordmapping *m = calloc(1, sizeof(Chordmapping)); | |
+ if (!m) | |
+ return -1; | |
+ | |
+ m->s1 = s1; | |
+ m->s2 = s2; | |
+ m->c = c; | |
+ m->t = t; | |
+ | |
+ m->next = chordmap; | |
+ chordmap = m; | |
+ return 0; | |
+} | |
+ | |
+void | |
+freechords(void) | |
+{ | |
+ Chordmapping *m = chordmap; | |
+ while (m){ | |
+ Chordmapping *n = m->next; | |
+ free(m); | |
+ m = n; | |
+ } | |
+} | |
static void | |
Mouseaction(Widget w, XEvent *e, String *p, Cardinal *np) | |
@@ -391,11 +442,11 @@ Mouseaction(Widget w, XEvent *e, String *p, Cardinal *np) | |
if(s & Button5Mask) m.buttons |= 16; | |
/* Check to see if it's a chord first. */ | |
- for (Chordmapping *cm = chordmappings; cm && cm->kind != Kend; cm++){ | |
- if (ob == cm->start && m.buttons == cm->end){ | |
+ for (Chordmapping *cm = chordmap; cm; cm = cm->next){ | |
+ if (ob == cm->s1 && m.buttons == cm->s2){ | |
Charfunc kf = ((GwinWidget)w)->gwin.gotchar; | |
if (kf) | |
- (*kf)(cm->result, cm->kind, cm->target, m.xy.x, m.xy.y); | |
+ (*kf)(cm->c, Kcommand, cm->t, m.xy.x, m.xy.y); | |
m.buttons = 0; | |
} | |
diff --git a/libXg/libgint.h b/libXg/libgint.h | |
@@ -29,6 +29,10 @@ typedef char* caddr_t; | |
#undef Font | |
#undef Event | |
+/* binding and chord management */ | |
+void freechords(void); | |
+void freebindings(void); | |
+ | |
/* Cursor initialization */ | |
void initcursors(void); | |
diff --git a/libXg/xtbinit.c b/libXg/xtbinit.c | |
@@ -209,6 +209,8 @@ xtbinit(Errfunc f, char *class, int *pargc, char **argv, ch… | |
} | |
initcursors(); | |
+ atexit(freebindings); | |
+ atexit(freechords); | |
font = XftFontOpenName(_dpy, DefaultScreen(_dpy), getenv("FONT") ? getenv(… | |
screen.id = 0; | |
diff --git a/samterm/Makefile b/samterm/Makefile | |
@@ -22,7 +22,7 @@ CFLAGS=$(INCS) $(STANDARDS) $(INCLUDES) | |
LIBS=../libframe/libframe.a ../libXg/libXg.a | |
CC?=c99 | |
-OBJ=main.o flayer.o icons.o io.o menu.o mesg.o rasp.o scroll.o unix.o | |
+OBJ=main.o flayer.o icons.o io.o menu.o mesg.o rasp.o samrc.o scroll.o unix.o | |
all: samterm | |
diff --git a/samterm/main.c b/samterm/main.c | |
@@ -47,6 +47,18 @@ main(int argc, char *argv[]) | |
Rectangle r; | |
Flayer *nwhich; | |
int fwdbut; | |
+ char rcpath[PATH_MAX + 1] = {0}; | |
+ FILE *rc = NULL; | |
+ | |
+ installdefaultbindings(); | |
+ installdefaultchords(); | |
+ | |
+ snprintf(rcpath, PATH_MAX, "%s/.samrc", getenv("HOME") ? getenv("HOME") : … | |
+ rc = fopen(rcpath, "r"); | |
+ if (rc){ | |
+ loadrcfile(rc); | |
+ fclose(rc); | |
+ } | |
while ((opt = getopt(argc, argv, "efr:")) != -1){ | |
switch (opt){ | |
diff --git a/samterm/samrc.c b/samterm/samrc.c | |
@@ -0,0 +1,255 @@ | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+#include <strings.h> | |
+#include <X11/Xlib.h> | |
+#include <X11/keysym.h> | |
+ | |
+#include <commands.h> | |
+#include <u.h> | |
+#include <libg.h> | |
+ | |
+typedef struct Namemapping Namemapping; | |
+struct Namemapping{ | |
+ const char *name; | |
+ int value; | |
+}; | |
+ | |
+static Namemapping commandmapping[] ={ | |
+ {"escape", Cescape}, | |
+ {"scrolldown", Cscrolldown}, | |
+ {"scrollup", Cscrollup}, | |
+ {"scrolldownline", Cscrolldownline}, | |
+ {"scrollupline", Cscrollupline}, | |
+ {"jump", Cjump}, | |
+ {"charright", Ccharright}, | |
+ {"charleft", Ccharleft}, | |
+ {"linedown", Clinedown}, | |
+ {"lineup", Clineup}, | |
+ {"delword", Cdelword}, | |
+ {"delbol", Cdelbol}, | |
+ {"del", Cdel}, | |
+ {"snarf", Csnarf}, | |
+ {"cut", Ccut}, | |
+ {"paste", Cpaste}, | |
+ {"exchange", Cexchange}, | |
+ {"write", Cwrite}, | |
+ {"eol", Ceol}, | |
+ {"bol", Cbol}, | |
+ {NULL, 0} | |
+}; | |
+ | |
+static Namemapping targetmapping[] ={ | |
+ {"current", Tcurrent}, | |
+ {"mouse", Tmouse}, | |
+ {NULL, 0} | |
+}; | |
+ | |
+static Namemapping buttonmapping[] ={ | |
+ {"0", 0}, | |
+ {"n", 0}, | |
+#define B1 1 | |
+ {"1", 1}, | |
+#define B2 2 | |
+ {"2", 2}, | |
+#define B3 4 | |
+ {"3", 4}, | |
+#define B4 8 | |
+ {"4", 8}, | |
+#define B5 16 | |
+ {"5", 16}, | |
+ {NULL, 0} | |
+}; | |
+ | |
+static Namemapping modmapping[] ={ | |
+ {"n", 0}, | |
+ {"c", ControlMask}, | |
+ {"a", Mod1Mask}, | |
+ {"m", Mod1Mask}, | |
+ {"s", Mod2Mask}, | |
+ {"h", Mod3Mask}, | |
+ {"1", Mod1Mask}, | |
+ {"2", Mod2Mask}, | |
+ {"3", Mod3Mask}, | |
+ {"4", Mod4Mask}, | |
+ {"5", Mod5Mask}, | |
+ {NULL, 0} | |
+}; | |
+ | |
+static int | |
+lookupmapping(const char *n, Namemapping *m) | |
+{ | |
+ for (Namemapping *k = m; k->name != NULL; k++){ | |
+ if (strcasecmp(k->name, n) == 0) | |
+ return k->value; | |
+ } | |
+ | |
+ return -1; | |
+} | |
+ | |
+static int | |
+nametocommand(const char *n) | |
+{ | |
+ return lookupmapping(n, commandmapping); | |
+} | |
+ | |
+static int | |
+nametotarget(const char *n) | |
+{ | |
+ return lookupmapping(n, targetmapping); | |
+} | |
+ | |
+typedef struct Defaultbinding Defaultbinding; | |
+struct Defaultbinding{ | |
+ int modifiers; | |
+ KeySym keysym; | |
+ int kind; | |
+ int command; | |
+}; | |
+ | |
+static Defaultbinding defaultbindings[] ={ | |
+ /* Motion commands. */ | |
+ {ControlMask, XK_e, Kcommand, Clineup}, | |
+ {ControlMask, XK_x, Kcommand, Clinedown}, | |
+ {ControlMask, XK_d, Kcommand, Ccharright}, | |
+ {ControlMask, XK_s, Kcommand, Ccharleft}, | |
+ {ControlMask, XK_u, Kcommand, Cdelbol}, | |
+ {ControlMask, XK_w, Kcommand, Cdelword}, | |
+ {ControlMask, XK_k, Kcommand, Cjump}, | |
+ {ControlMask, XK_BackSpace, Kcommand, Cdelword}, | |
+ {ControlMask, XK_y, Kcommand, Ccut}, | |
+ {ControlMask, XK_c, Kcommand, Csnarf}, | |
+ {ControlMask, XK_v, Kcommand, Cpaste}, | |
+ {ControlMask, XK_q, Kcommand, Cexchange}, | |
+ | |
+ /* Use Control-Tab to insert a literal tab when tab expansion is enabled. … | |
+ {ControlMask, XK_Tab, Kcomposed, '\t'}, | |
+ | |
+ /* Handle arrow keys, page up/down, and escape. */ | |
+ {0, XK_Up, Kcommand, Cscrollup}, | |
+ {0, XK_Prior, Kcommand, Cscrollup}, | |
+ {0, XK_Left, Kcommand, Cscrollup}, | |
+ {0, XK_Down, Kcommand, Cscrolldown}, | |
+ {0, XK_Next, Kcommand, Cscrolldown}, | |
+ {0, XK_Right, Kcommand, Cscrolldown}, | |
+ {0, XK_Escape, Kcommand, Cescape}, | |
+ | |
+ /* More fundamental stuff: backspace, delete, etc. */ | |
+ {0, XK_BackSpace, Kcommand, Cdel}, | |
+ {0, XK_Delete, Kcommand, Cdel}, | |
+ {0, XK_Return, Kraw, '\n'}, | |
+ {0, XK_KP_Enter, Kraw, '\n'}, | |
+ {0, XK_Linefeed, Kraw, '\r'}, | |
+ {0, XK_Tab, Kraw, '\t'}, | |
+ {0, XK_KP_0, Kraw, '0'}, | |
+ {0, XK_KP_1, Kraw, '1'}, | |
+ {0, XK_KP_2, Kraw, '2'}, | |
+ {0, XK_KP_3, Kraw, '3'}, | |
+ {0, XK_KP_4, Kraw, '4'}, | |
+ {0, XK_KP_5, Kraw, '5'}, | |
+ {0, XK_KP_6, Kraw, '6'}, | |
+ {0, XK_KP_7, Kraw, '7'}, | |
+ {0, XK_KP_8, Kraw, '8'}, | |
+ {0, XK_KP_9, Kraw, '9'}, | |
+ {0, XK_KP_Divide, Kraw, '/'}, | |
+ {0, XK_KP_Multiply, Kraw, '*'}, | |
+ {0, XK_KP_Subtract, Kraw, '-'}, | |
+ {0, XK_KP_Add, Kraw, '+'}, | |
+ {0, XK_KP_Decimal, Kraw, '.'}, | |
+ {0, XK_hyphen, Kraw, '-'}, | |
+ {0, 0, Kend, 0} | |
+}; | |
+ | |
+void | |
+installdefaultbindings(void) | |
+{ | |
+ for (Defaultbinding *b = defaultbindings; b->kind != Kend; b++) | |
+ installbinding(b->modifiers, b->keysym, b->kind, b->command); | |
+} | |
+ | |
+typedef struct Defaultchord Defaultchord; | |
+struct Defaultchord{ | |
+ int state1; | |
+ int state2; | |
+ int command; | |
+ int target; | |
+}; | |
+ | |
+static Defaultchord defaultchords[] ={ | |
+ {B1, B1|B2, Ccut, Tcurrent}, | |
+ {B1, B1|B3, Cpaste, Tcurrent}, | |
+ {B1|B2, B1, Cnone, Tcurrent}, | |
+ {B1|B3, B1, Cnone, Tcurrent}, | |
+ | |
+ {B4, 0, Cscrollupline, Tmouse}, | |
+ {B5, 0, Cscrolldownline, Tmouse}, | |
+ | |
+ {0, 0, Kend, 0} | |
+}; | |
+ | |
+void | |
+installdefaultchords(void) | |
+{ | |
+ for (Defaultchord *c = defaultchords; c->state1 != 0; c++) | |
+ installchord(c->state1, c->state2, c->command, c->target); | |
+} | |
+ | |
+static int | |
+statetomask(const char *n, Namemapping *m) | |
+{ | |
+ int r = 0; | |
+ for (int i = 0; n[i] != 0; i++){ | |
+ char s[2] = {n[i], 0}; | |
+ int v = lookupmapping(s, m); | |
+ if (v < 0) | |
+ return -1; | |
+ r |= v; | |
+ } | |
+ | |
+ return r; | |
+} | |
+ | |
+void | |
+loadrcfile(FILE *f) | |
+{ | |
+ char *l = NULL; | |
+ size_t n = 0; | |
+ ssize_t r = 0; | |
+ size_t ln = 0; | |
+ | |
+ while ((r = getline(&l, &n, f)) >= 0){ | |
+ char s1[6] = {0}; | |
+ char s2[6] = {0}; | |
+ char cname[100] = {0}; | |
+ char tname[100] = {0}; | |
+ char c = 0; | |
+ unsigned short i = 0; | |
+ int rc = 0; | |
+ | |
+ ln++; | |
+ if (r == 0 || l[0] == '\n' || l[0] == 0) | |
+ continue; | |
+ | |
+ if (sscanf(l, " chord %5[Nn12345] %5[Nn12345] %99s %99s", s1, s2, cnam… | |
+ rc = installchord(statetomask(s1, buttonmapping), statetomask(s2, … | |
+ else if (sscanf(l, " bind %5[ncamshNCAMSH12345] %99s raw %hx", s1, s2,… | |
+ rc = installbinding(statetomask(s1, modmapping), XStringToKeysym(s… | |
+ else if (sscanf(l, " bind %5[ncamshNCAMSH12345] %99s composed %hx", s1… | |
+ rc = installbinding(statetomask(s1, modmapping), XStringToKeysym(s… | |
+ else if (sscanf(l, " bind %5[ncamshNCAMSH12345] %99s raw %c", s1, s2, … | |
+ rc = installbinding(statetomask(s1, modmapping), XStringToKeysym(s… | |
+ else if (sscanf(l, " bind %5[ncamshNCAMSH12345] %99s composed %c", s1,… | |
+ rc = installbinding(statetomask(s1, modmapping), XStringToKeysym(s… | |
+ else if (sscanf(l, " bind %5[ncamshNCAMSH12345] %99s command %99s", s1… | |
+ rc = installbinding(statetomask(s1, modmapping), XStringToKeysym(s… | |
+ else if (sscanf(l, " %[#]", &c) == 1) | |
+ continue; | |
+ else | |
+ fprintf(stderr, "invalid rc line %zd\n", ln); | |
+ | |
+ if (rc != 0) | |
+ fprintf(stderr, "invalid chord/binding on rc line %zd\n", ln); | |
+ } | |
+ | |
+ free(l); | |
+} | |
diff --git a/samterm/samterm.h b/samterm/samterm.h | |
@@ -157,3 +157,6 @@ void outlong(long); | |
void outvlong(void*); | |
void outsend(void); | |
int getlayer(const Flayer *l, const Text *t); | |
+void loadrcfile(FILE *); | |
+void installdefaultbindings(void); | |
+void installdefaultchords(void); |