Add support for sending commands to the editor. - sam - An updated version of the sam text editor.
 (HTM) git clone git://vernunftzentrum.de/sam.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
 (DIR) commit a52daf1643c6759b60847b7466e0d57ddf833cd6
 (DIR) parent 4b8e52f78d6efadffefb275c547bc20ba17b65c9
 (HTM) Author: Rob King <jking@deadpixi.com>
       Date:   Mon, 19 Sep 2016 16:43:33 -0500
       
       Add support for sending commands to the editor.
       
       Diffstat:
         doc/samrc                           |      12 +++++++++++-
         doc/samrc.5                         |      31 ++++++++++++++++++++++++-------
         include/libg.h                      |       6 ++++--
         libXg/Gwin.h                        |       2 +-
         libXg/gwin.c                        |      20 ++++++++++++--------
         libXg/xtbinit.c                     |       5 +++--
         samterm/main.c                      |      83 +++++++++++++++++--------------
         samterm/samrc.c                     |     159 ++++++++++++++++---------------
         samterm/samterm.h                   |       4 ++--
       
       9 files changed, 184 insertions(+), 138 deletions(-)
       ---
 (DIR) diff --git a/doc/samrc b/doc/samrc
       @@ -42,7 +42,17 @@ bind * Next command scrolldown
        # except for Control-D. Let's remove any special handling for that binding.
        unbind C d
        
       -# I like 13pt Inconsolata
       +# Control-Z sends an undo command.
       +bind C z command send u
       +
       +# Control-Return inserts a line below the current one
       +bind C Enter command send +-a/\n/
       +
       +# Control-S writes the file, Control-Shift-S writes all files.
       +bind C s command send w
       +bind CS s command send X w
       +
       +# I like 13pt Inconsolata (I need new glasses)
        font Inconsolata:size=13
        
        # Use black for text and borders, and an angry fruit salad for backgrounds
 (DIR) diff --git a/doc/samrc.5 b/doc/samrc.5
       @@ -28,6 +28,7 @@ Bind a key sequence to a command or a raw character.
        The forms are:
        .Bd -literal
        
       +    bind M K command C A
            bind M K command C
            bind M K raw C
        
       @@ -43,7 +44,6 @@ below
        .Em K
        is the name of a keyboard symbol suitable for passing to
        .Xr XStringToKeysym 3 ","
       -and
        .Em C
        is either a command name
        .Po
       @@ -58,9 +58,13 @@ or hexadecimal
        as in
        .Xr keyboard 5
        .Pc
       -specification of a character.
       +specification of a character,
       +and
       +.Em A
       +is an arbitrary string to use as an argument to a bound command.
        .Pp
       -Note that keyboard symbol names are implementation-defined and often case-sensitive.
       +Note that keyboard symbol names are implementation-defined and often case-sensitive,
       +and that not all commands will make use of arguments.
        .It unbind
        Remove all bindings associated with a key sequence.
        The form is:
       @@ -204,12 +208,12 @@ meaning Control, or
        meaning shift.
        .Pp
        For example,
       -bind the "write" command to
       -.Em Control-Shift-W ","
       +bind the "exchange" command to
       +.Em Control-Shift-E ","
        the following directive could be used:
        .Bd -literal
        
       -    bind CS w command write
       +    bind CS w command exchange
        
        .Ed
        .Pp
       @@ -237,10 +241,10 @@ cut        Cut selection        Control-Y
        snarf        Snarf selection        Control-C
        paste        Paste snarf buffer        Control-V
        exchange        Exchange snarf buffer        Control-Q
       -write        Write current file        None
        eol        Move to end of line        None
        bol        Move to beginning of line        None
        tab        Insert a (possibly expanded) tab        Tab
       +send        Send a command to the editor        None
        .TE
        .Pp
        Additionally,
       @@ -251,6 +255,19 @@ means that the given binding should perform no action
        and the command name
        .Dq default
        means that the given binding should perform whatever action was previously defined for it.
       +.Pp
       +For the
       +.Em send
       +command,
       +the command to send is specified in the argument of the binding.
       +For example, to bind
       +.Em Control-Z
       +to undo the last 10 changes, the following line binding could be used:
       +.Bd -literal
       +
       +    bind C z command send u10
       +
       +.Ed
        .Ss "Mouse Button States"
        Chords are described using two states:
        a beginning state and an end state.
 (DIR) diff --git a/include/libg.h b/include/libg.h
       @@ -63,6 +63,7 @@ struct  Mouse
            int     buttons; /* bit array: LMR=124 */
            Point       xy;
            unsigned long   msec;
       +    char *a;
        };
        
        struct  Keystroke
       @@ -71,6 +72,7 @@ struct  Keystroke
            int c;
            int t;
            Point p;
       +    const char *a;
        };
        
        struct Menu
       @@ -247,8 +249,8 @@ 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);
       +extern int installbinding(int, KeySym, int, int, const char *);
       +extern int installchord(int, int, int, int, const char *);
        extern int removebinding(int, KeySym);
        extern int removechord(int, int);
        
 (DIR) diff --git a/libXg/Gwin.h b/libXg/Gwin.h
       @@ -34,7 +34,7 @@ typedef struct {
            } Gwinmouse;
        
        typedef void (*Reshapefunc)(int, int, int, int);
       -typedef void (*Charfunc)(int, int, int, int, int);
       +typedef void (*Charfunc)(int, int, int, int, int, const char *);
        typedef void (*Mousefunc)(Gwinmouse*);
        
        /* Method declarations */
 (DIR) diff --git a/libXg/gwin.c b/libXg/gwin.c
       @@ -159,7 +159,7 @@ Mappingaction(Widget w, XEvent *e, String *p, Cardinal *np)
                        f = ((GwinWidget)w)->gwin.gotchar; \
                        if (f) \
                            for (c = 0; c < composing; c++) \
       -                        (*f)(compose[c], 0, Tcurrent, 0, 0)
       +                        (*f)(compose[c], 0, Tcurrent, 0, 0, NULL)
        
        typedef struct Unikeysym Unikeysym;
        struct Unikeysym{
       @@ -190,17 +190,19 @@ struct Keymapping{
            KeySym s;
            int k;
            int c;
       +    char a[];
        };
        
        static Keymapping *keymappings = NULL;
        
        int
       -installbinding(int m, KeySym s, int k, int c)
       +installbinding(int m, KeySym s, int k, int c, const char *a)
        {
            if (m < 0 || s == NoSymbol || k < 0 || c < 0)
                return -1;
        
       -    Keymapping *km = calloc(1, sizeof(Keymapping));
       +    a = a ? a : "";
       +    Keymapping *km = calloc(1, sizeof(Keymapping) + strlen(a) + 1);
            if (!km)
                return -1;
        
       @@ -208,7 +210,7 @@ installbinding(int m, KeySym s, int k, int c)
            km->s = s;
            km->k = k;
            km->c = c;
       -
       +    strcpy(km->a, a);
            km->next = keymappings;
            keymappings = km;
        
       @@ -285,7 +287,7 @@ Keyaction(Widget w, XEvent *e, String *p, Cardinal *np)
                            default:
                                f = ((GwinWidget)w)->gwin.gotchar;
                                if (f)
       -                            (*f)(m->c, m->k, Tcurrent, 0, 0);
       +                            (*f)(m->c, m->k, Tcurrent, 0, 0, m->a);
                                return;
                        }
                    }
       @@ -364,7 +366,7 @@ Keyaction(Widget w, XEvent *e, String *p, Cardinal *np)
        
            f = ((GwinWidget)w)->gwin.gotchar;
            if(f)
       -        (*f)(c, kind, Tcurrent, 0, 0);
       +        (*f)(c, kind, Tcurrent, 0, 0, NULL);
        }
        
        typedef struct Chordmapping Chordmapping;
       @@ -374,12 +376,13 @@ struct Chordmapping{
            int s2;
            int c;
            int t;
       +    const char *a;
        };
        
        static Chordmapping *chordmap = NULL;
        
        int
       -installchord(int s1, int s2, int c, int t)
       +installchord(int s1, int s2, int c, int t, const char *a)
        {
            if (s1 < 0 || s2 < 0 || c < 0 || (t != Tmouse && t != Tcurrent))
                return -1;
       @@ -392,6 +395,7 @@ installchord(int s1, int s2, int c, int t)
            m->s2 = s2;
            m->c = c;
            m->t = t;
       +    m->a = a;
        
            m->next = chordmap;
            chordmap = m;
       @@ -504,7 +508,7 @@ Mouseaction(Widget w, XEvent *e, String *p, Cardinal *np)
                        default:
                            kf = ((GwinWidget)w)->gwin.gotchar;
                            if (kf)
       -                        (*kf)(cm->c, Kcommand, cm->t, m.xy.x, m.xy.y);
       +                        (*kf)(cm->c, Kcommand, cm->t, m.xy.x, m.xy.y, NULL);
                
                            m.buttons = 0;
                            chording = true;
 (DIR) diff --git a/libXg/xtbinit.c b/libXg/xtbinit.c
       @@ -109,7 +109,7 @@ static int Stimer = -1;
        
        
        static void reshaped(int, int, int, int);
       -static void gotchar(int, int, int, int, int);
       +static void gotchar(int, int, int, int, int, const char *);
        static void gotmouse(Gwinmouse *);
        static int  ilog2(int);
        
       @@ -309,7 +309,7 @@ reshaped(int minx, int miny, int maxx, int maxy)
        }
        
        static void
       -gotchar(int c, int kind, int target, int x, int y)
       +gotchar(int c, int kind, int target, int x, int y, const char *arg)
        {
            Ebuf *eb;
            Keystroke k;
       @@ -323,6 +323,7 @@ gotchar(int c, int kind, int target, int x, int y)
            k.k = kind;
            k.t = target;
            k.p = Pt(x, y);
       +    k.a = arg;
            memcpy(eb->buf, &k, sizeof(Keystroke));
            esrc[Skeyboard].count++;
        }
 (DIR) diff --git a/samterm/main.c b/samterm/main.c
       @@ -103,11 +103,11 @@ main(int argc, char *argv[])
                        ;
                    current(&cmd.l[i]);
                    flsetselect(which, cmd.rasp.nrunes, cmd.rasp.nrunes);
       -            type(which, RExtern);
       +            type(which);
                }
                if(got&RKeyboard){
                    if(which)
       -                type(which, RKeyboard);
       +                type(which);
                    else
                        kbdblock();
                }
       @@ -481,7 +481,7 @@ flushtyping(int clearesc)
        }
        
        static long
       -cmdscrolldown(Flayer *l, long a, Text *t)
       +cmdscrolldown(Flayer *l, long a, Text *t, const char *arg)
        {
            flushtyping(0);
            center(l, l->origin + l->f.nchars + 1);
       @@ -489,7 +489,7 @@ cmdscrolldown(Flayer *l, long a, Text *t)
        }
        
        static long
       -cmdscrollup(Flayer *l, long a, Text *t)
       +cmdscrollup(Flayer *l, long a, Text *t, const char *arg)
        {
            flushtyping(0);
            if (oldcompat)
       @@ -500,7 +500,7 @@ cmdscrollup(Flayer *l, long a, Text *t)
        }
        
        static long
       -cmdcharleft(Flayer *l, long a, Text *t)
       +cmdcharleft(Flayer *l, long a, Text *t, const char *arg)
        {
            flsetselect(l, a, a);
            flushtyping(0);
       @@ -513,7 +513,7 @@ cmdcharleft(Flayer *l, long a, Text *t)
        }
        
        static long
       -cmdcharright(Flayer *l, long a, Text *t)
       +cmdcharright(Flayer *l, long a, Text *t, const char *arg)
        {
            flsetselect(l, a, a);
            flushtyping(0);
       @@ -526,7 +526,7 @@ cmdcharright(Flayer *l, long a, Text *t)
        }
        
        static long
       -cmdeol(Flayer *l, long a, Text *t)
       +cmdeol(Flayer *l, long a, Text *t, const char *arg)
        {
            flsetselect(l, a, a);
            flushtyping(1);
       @@ -543,7 +543,7 @@ cmdeol(Flayer *l, long a, Text *t)
        }
        
        static long
       -cmdbol(Flayer *l, long a, Text *t)
       +cmdbol(Flayer *l, long a, Text *t, const char *arg)
        {
            flsetselect(l, a, a);
            flushtyping(1);
       @@ -561,7 +561,7 @@ cmdbol(Flayer *l, long a, Text *t)
        }
        
        static long
       -cmdscrollupline(Flayer *l, long a, Text *t)
       +cmdscrollupline(Flayer *l, long a, Text *t, const char *arg)
        {
            if (l->origin > 0)
                hmoveto(t->tag, l->origin - 1, l);
       @@ -569,7 +569,7 @@ cmdscrollupline(Flayer *l, long a, Text *t)
        }
        
        static long
       -cmdscrolldownline(Flayer *l, long a, Text *t)
       +cmdscrolldownline(Flayer *l, long a, Text *t, const char *arg)
        {
            long e = t->rasp.nrunes;
        
       @@ -584,7 +584,7 @@ cmdscrolldownline(Flayer *l, long a, Text *t)
        }
        
        static long
       -cmdlineup(Flayer *l, long a, Text *t)
       +cmdlineup(Flayer *l, long a, Text *t, const char *arg)
        {
            flsetselect(l, a, a);
            flushtyping(1);
       @@ -611,7 +611,7 @@ cmdlineup(Flayer *l, long a, Text *t)
        }
        
        static long
       -cmdlinedown(Flayer *l, long a, Text *t)
       +cmdlinedown(Flayer *l, long a, Text *t, const char *arg)
        {
            flsetselect(l, a, a);
            flushtyping(1);
       @@ -645,7 +645,7 @@ cmdlinedown(Flayer *l, long a, Text *t)
        }
        
        static long
       -cmdjump(Flayer *l, long a, Text *u)
       +cmdjump(Flayer *l, long a, Text *u, const char *arg)
        {
            Text *t = NULL;
        
       @@ -665,7 +665,7 @@ cmdjump(Flayer *l, long a, Text *u)
        }
        
        static long
       -cmdescape(Flayer *l, long a, Text *t)
       +cmdescape(Flayer *l, long a, Text *t, const char *arg)
        {
            if (typeesc >= 0){
                l->p0 = typeesc;
       @@ -681,7 +681,7 @@ cmdescape(Flayer *l, long a, Text *t)
        }
        
        static long
       -cmddelword(Flayer *l, long a, Text *t)
       +cmddelword(Flayer *l, long a, Text *t, const char *arg)
        {
            if (l->f.p0 > 0 && a > 0)
                l->p0 = ctlw(&t->rasp, l->origin, a);
       @@ -703,7 +703,7 @@ cmddelword(Flayer *l, long a, Text *t)
        }
        
        static long
       -cmddelbol(Flayer *l, long a, Text *t)
       +cmddelbol(Flayer *l, long a, Text *t, const char *arg)
        {
            if (l->f.p0 > 0 && a > 0)
                l->p0 = ctlu(&t->rasp, l->origin, a);
       @@ -725,7 +725,7 @@ cmddelbol(Flayer *l, long a, Text *t)
        }
        
        static long
       -cmddel(Flayer *l, long a, Text *t)
       +cmddel(Flayer *l, long a, Text *t, const char *arg)
        {
            if (l->f.p0 > 0 && a > 0)
                l->p0 = a - 1;
       @@ -758,7 +758,7 @@ getlayer(const Flayer *l, const Text *t)
        }
        
        static long
       -cmdexchange(Flayer *l, long a, Text *t)
       +cmdexchange(Flayer *l, long a, Text *t, const char *arg)
        {
            int w = getlayer(l, t);
            if (w >= 0){
       @@ -771,7 +771,7 @@ cmdexchange(Flayer *l, long a, Text *t)
        }
        
        static long
       -cmdsnarf(Flayer *l, long a, Text *t)
       +cmdsnarf(Flayer *l, long a, Text *t, const char *arg)
        {
            flushtyping(0);
        
       @@ -783,7 +783,7 @@ cmdsnarf(Flayer *l, long a, Text *t)
        }
        
        static long
       -cmdcut(Flayer *l, long a, Text *t)
       +cmdcut(Flayer *l, long a, Text *t, const char *arg)
        {
            flushtyping(0);
        
       @@ -795,7 +795,7 @@ cmdcut(Flayer *l, long a, Text *t)
        }
        
        static long
       -cmdpaste(Flayer *l, long a, Text *t)
       +cmdpaste(Flayer *l, long a, Text *t, const char *arg)
        {
            flushtyping(0);
        
       @@ -807,16 +807,7 @@ cmdpaste(Flayer *l, long a, Text *t)
        }
        
        static long
       -cmdwrite(Flayer *l, long a, Text *t)
       -{
       -    flushtyping(0);
       -    outTs(Twrite, ((Text *)l->user1)->tag);
       -    setlock();
       -    return a;
       -}
       -
       -static long
       -cmdtab(Flayer *l, long a, Text *t)
       +cmdtab(Flayer *l, long a, Text *t, const char *arg)
        {
            flushtyping(0);
        
       @@ -837,12 +828,28 @@ cmdtab(Flayer *l, long a, Text *t)
        }
        
        static long
       -cmdnone(Flayer *l, long a, Text *t)
       +cmdsend(Flayer *l, long a, Text *t, const char *arg)
       +{
       +    flushtyping(0);
       +    cmdjump(l, a, t, NULL);
       +    for (const char *c = arg; *c != 0; c++){
       +        pushkbd(*c);
       +        type(&cmd.l[cmd.front]);
       +    }
       +    pushkbd('\n');
       +    type(&cmd.l[cmd.front]);
       +    cmdjump(l, a, t, NULL);
       +
       +    return a;
       +}
       +
       +static long
       +cmdnone(Flayer *l, long a, Text *t, const char *arg)
        {
            return a;
        }
        
       -typedef long (*Commandfunc)(Flayer *, long, Text *);
       +typedef long (*Commandfunc)(Flayer *, long, Text *, const char *);
        typedef struct CommandEntry CommandEntry;
        struct CommandEntry{
            Commandfunc f;
       @@ -869,15 +876,15 @@ CommandEntry commands[Cmax] ={
            [Cdelword]        = {cmddelword,        true,  false},
            [Cdelbol]         = {cmddelbol,         true,  false},
            [Cdel]            = {cmddel,            true,  true},
       -    [Cwrite]          = {cmdwrite,          true,  false},
            [Ceol]            = {cmdeol,            false, false},
            [Cbol]            = {cmdbol,            false, false},
       -    [Ctab]            = {cmdtab,            false, false}
       +    [Ctab]            = {cmdtab,            false, false},
       +    [Csend]           = {cmdsend,           false, false}
        };
        
        
        void
       -type(Flayer *l, int res)    /* what a bloody mess this is -- but it's getting better! */
       +type(Flayer *l)    /* what a bloody mess this is -- but it's getting better! */
        {
            Text *t = (Text *)l->user1;
            Rune buf[100];
       @@ -914,11 +921,11 @@ type(Flayer *l, int res)    /* what a bloody mess this is -- but it's getting be
                CommandEntry *e = &commands[k.c];
                if (!e->unlocked || !lock){
                    if (k.t == Tcurrent || oldcompat)
       -                a = e->f(l, a, t);
       +                a = e->f(l, a, t, k.a);
                    else{
                        Flayer *lt = flwhich(k.p);
                        if (lt)
       -                    lt->p0 = e->f(lt, lt->p0, (Text *)lt->user1);
       +                    lt->p0 = e->f(lt, lt->p0, (Text *)lt->user1, k.a);
                    }
                }
            }
 (DIR) diff --git a/samterm/samrc.c b/samterm/samrc.c
       @@ -40,10 +40,10 @@ static Namemapping commandmapping[] ={
            {"cut",             Ccut},
            {"paste",           Cpaste},
            {"exchange",        Cexchange},
       -    {"write",           Cwrite},
            {"eol",             Ceol},
            {"bol",             Cbol},
            {"tab",             Ctab},
       +    {"send",            Csend},
            {NULL, 0}
        };
        
       @@ -96,68 +96,69 @@ struct Defaultbinding{
            KeySym keysym;
            int kind;
            int command;
       +    const char *arg;
        };
        
        static Defaultbinding defaultbindings[] ={    
            /* Suppress control key combinations unless explicitly bound. */
       -    {ControlMask, XK_VoidSymbol,    Kcommand, Cnone},
       +    {ControlMask, XK_VoidSymbol,    Kcommand, Cnone,        NULL},
        
            /* Motion commands following the WordStar diamond. */
       -    {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},
       +    {ControlMask, XK_e,             Kcommand,  Clineup,     NULL},
       +    {ControlMask, XK_x,             Kcommand,  Clinedown,   NULL},
       +    {ControlMask, XK_d,             Kcommand,  Ccharright,  NULL},
       +    {ControlMask, XK_s,             Kcommand,  Ccharleft,   NULL},
       +    {ControlMask, XK_u,             Kcommand,  Cdelbol,     NULL},
       +    {ControlMask, XK_w,             Kcommand,  Cdelword,    NULL},
       +    {ControlMask, XK_k,             Kcommand,  Cjump,       NULL},
       +    {ControlMask, XK_BackSpace,     Kcommand,  Cdelword,    NULL},
       +    {ControlMask, XK_y,             Kcommand,  Ccut,        NULL},
       +    {ControlMask, XK_c,             Kcommand,  Csnarf,      NULL},
       +    {ControlMask, XK_v,             Kcommand,  Cpaste,      NULL},
       +    {ControlMask, XK_q,             Kcommand,  Cexchange,   NULL},
        
            /* 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},
       +    {0,           XK_Up,            Kcommand, Cscrollup,    NULL},
       +    {0,           XK_Prior,         Kcommand, Cscrollup,    NULL},
       +    {0,           XK_Left,          Kcommand, Cscrollup,    NULL},
       +    {0,           XK_Down,          Kcommand, Cscrolldown,  NULL},
       +    {0,           XK_Next,          Kcommand, Cscrolldown,  NULL},
       +    {0,           XK_Right,         Kcommand, Cscrolldown,  NULL},
       +    {0,           XK_Escape,        Kcommand, Cescape,      NULL},
            
            /* More fundamental stuff: backspace, delete, etc. */
       -    {0,           XK_BackSpace,     Kcommand, Cdel},
       -    {0,           XK_Delete,        Kcommand, Cdel},
       -    {0,           XK_Tab,           Kcommand, Ctab},
       -    {0,           XK_Return,        Kraw,     '\n'},
       -    {0,           XK_KP_Enter,      Kraw,     '\n'},
       -    {0,           XK_Linefeed,      Kraw,     '\r'},
       -    {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,           XK_BackSpace,     Kcommand, Cdel,     NULL},
       +    {0,           XK_Delete,        Kcommand, Cdel,     NULL},
       +    {0,           XK_Tab,           Kcommand, Ctab,     NULL},
       +    {0,           XK_Return,        Kraw,     '\n',     NULL},
       +    {0,           XK_KP_Enter,      Kraw,     '\n',     NULL},
       +    {0,           XK_Linefeed,      Kraw,     '\r',     NULL},
       +    {0,           XK_KP_0,          Kraw,     '0',      NULL},
       +    {0,           XK_KP_1,          Kraw,     '1',      NULL},
       +    {0,           XK_KP_2,          Kraw,     '2',      NULL},
       +    {0,           XK_KP_3,          Kraw,     '3',      NULL},
       +    {0,           XK_KP_4,          Kraw,     '4',      NULL},
       +    {0,           XK_KP_5,          Kraw,     '5',      NULL},
       +    {0,           XK_KP_6,          Kraw,     '6',      NULL},
       +    {0,           XK_KP_7,          Kraw,     '7',      NULL},
       +    {0,           XK_KP_8,          Kraw,     '8',      NULL},
       +    {0,           XK_KP_9,          Kraw,     '9',      NULL},
       +    {0,           XK_KP_Divide,     Kraw,     '/',      NULL},
       +    {0,           XK_KP_Multiply,   Kraw,     '*',      NULL},
       +    {0,           XK_KP_Subtract,   Kraw,     '-',      NULL},
       +    {0,           XK_KP_Add,        Kraw,     '+',      NULL},
       +    {0,           XK_KP_Decimal,    Kraw,     '.',      NULL},
       +    {0,           XK_hyphen,        Kraw,     '-',      NULL},
        
            /* Support traditional control sequences. */
       -    {ControlMask, XK_bracketleft,   Kcommand, Cescape},
       -    {ControlMask, XK_h,             Kcommand, Cdel},
       -    {ControlMask, XK_i,             Kcommand, Ctab},
       -    {ControlMask, XK_j,             Kraw,     '\n'},
       -    {ControlMask, XK_m,             Kraw,     '\r'},
       +    {ControlMask, XK_bracketleft,   Kcommand, Cescape,  NULL},
       +    {ControlMask, XK_h,             Kcommand, Cdel,     NULL},
       +    {ControlMask, XK_i,             Kcommand, Ctab,     NULL},
       +    {ControlMask, XK_j,             Kraw,     '\n',     NULL},
       +    {ControlMask, XK_m,             Kraw,     '\r',     NULL},
        
            /* Use Control-Tab to insert a literal tab when tab expansion is enabled. */
       -    {ControlMask, XK_Tab,           Kraw,     '\t'},
       +    {ControlMask, XK_Tab,           Kraw,     '\t',     NULL},
        
            {0,           0,                Kend,     0}
        };
       @@ -166,7 +167,7 @@ void
        installdefaultbindings(void)
        {
            for (Defaultbinding *b = defaultbindings; b->kind != Kend; b++)
       -        installbinding(b->modifiers, b->keysym, b->kind, b->command);
       +        installbinding(b->modifiers, b->keysym, b->kind, b->command, b->arg);
        }
        
        typedef struct Defaultchord Defaultchord;
       @@ -175,25 +176,26 @@ struct Defaultchord{
            int state2;
            int command;
            int target;
       +    const char *arg;
        };
        
        static Defaultchord defaultchords[] ={
       -    {B1, B1|B2,  Ccut,   Tcurrent},
       -    {B1, B1|B3,  Cpaste, Tcurrent},
       -    {B1|B2, B1,  Cnone,  Tcurrent},
       -    {B1|B3, B1,  Cnone,  Tcurrent},
       +    {B1, B1|B2,  Ccut,   Tcurrent, NULL},
       +    {B1, B1|B3,  Cpaste, Tcurrent, NULL},
       +    {B1|B2, B1,  Cnone,  Tcurrent, NULL},
       +    {B1|B3, B1,  Cnone,  Tcurrent, NULL},
        
       -    {B4, 0,  Cscrollupline,   Tmouse},
       -    {B5, 0,  Cscrolldownline, Tmouse},
       +    {B4, 0,  Cscrollupline,   Tmouse, NULL},
       +    {B5, 0,  Cscrolldownline, Tmouse, NULL},
        
       -    {0, 0, Kend, 0}
       +    {0, 0, Kend, 0, NULL}
        };
        
        void
        installdefaultchords(void)
        {
            for (Defaultchord *c = defaultchords; c->state1 != 0; c++)
       -        installchord(c->state1, c->state2, c->command, c->target);
       +        installchord(c->state1, c->state2, c->command, c->target, c->arg);
        }
        
        static int
       @@ -225,45 +227,45 @@ nametokeysym(const char *n)
        }
        
        static int
       -dirchord(const char *s1, const char *s2, const char *s3, const char *s4)
       +dirchord(const char *s1, const char *s2, const char *s3, const char *s4, const char *s5)
        {
       -    return installchord(buttontomask(s1), buttontomask(s2), nametocommand(s3), nametotarget(s4));
       +    return installchord(buttontomask(s1), buttontomask(s2), nametocommand(s3), nametotarget(s4), s5);
        }
        
        static int
       -dirraw(const char *s1, const char *s2, const char *s3, const char *s4)
       +dirraw(const char *s1, const char *s2, const char *s3, const char *s4, const char *s5)
        {
       -    return installbinding(modtomask(s1), nametokeysym(s2), Kraw, strtol(s3, NULL, 16));
       +    return installbinding(modtomask(s1), nametokeysym(s2), Kraw, strtol(s3, NULL, 16), NULL);
        }
        
        static int
       -dirrawliteral(const char *s1, const char *s2, const char *s3, const char *s4)
       +dirrawliteral(const char *s1, const char *s2, const char *s3, const char *s4, const char *s5)
        {
            if (strlen(s3) != 1)
                return -1;
       -    return installbinding(modtomask(s1), nametokeysym(s2), Kraw, s3[0]);
       +    return installbinding(modtomask(s1), nametokeysym(s2), Kraw, s3[0], NULL);
        }
        
        static int
       -dirbind(const char *s1, const char *s2, const char *s3, const char *s4)
       +dirbind(const char *s1, const char *s2, const char *s3, const char *s4, const char *s5)
        {
       -    return installbinding(modtomask(s1), nametokeysym(s2), Kcommand, nametocommand(s3));
       +    return installbinding(modtomask(s1), nametokeysym(s2), Kcommand, nametocommand(s3), s4);
        }
        
        static int
       -dirunbind(const char *s1, const char *s2, const char *s3, const char *s4)
       +dirunbind(const char *s1, const char *s2, const char *s3, const char *s4, const char *s5)
        {
            return removebinding(modtomask(s1), nametokeysym(s2));
        }
        
        static int
       -dirunchord(const char *s1, const char *s2, const char *s3, const char *s4)
       +dirunchord(const char *s1, const char *s2, const char *s3, const char *s4, const char *s5)
        {
            return removechord(buttontomask(s1), buttontomask(s2));
        }
        
        static int
       -dirforeground(const char *s1, const char *s2, const char *s3, const char *s4)
       +dirforeground(const char *s1, const char *s2, const char *s3, const char *s4, const char *s5)
        {
            if (strlen(s1) == 0)
                return -1;
       @@ -273,7 +275,7 @@ dirforeground(const char *s1, const char *s2, const char *s3, const char *s4)
        }
        
        static int
       -dirbackground(const char *s1, const char *s2, const char *s3, const char *s4)
       +dirbackground(const char *s1, const char *s2, const char *s3, const char *s4, const char *s5)
        {
            if (strlen(s1) == 0)
                return -1;
       @@ -283,7 +285,7 @@ dirbackground(const char *s1, const char *s2, const char *s3, const char *s4)
        }
        
        static int
       -dirborder(const char *s1, const char *s2, const char *s3, const char *s4)
       +dirborder(const char *s1, const char *s2, const char *s3, const char *s4, const char *s5)
        {
            if (strlen(s1) == 0)
                return -1;
       @@ -293,7 +295,7 @@ dirborder(const char *s1, const char *s2, const char *s3, const char *s4)
        }
        
        static int
       -dirfont(const char *s1, const char *s2, const char *s3, const char *s4)
       +dirfont(const char *s1, const char *s2, const char *s3, const char *s4, const char *s5)
        {
            if (strlen(s1) == 0)
                return -1;
       @@ -303,7 +305,7 @@ dirfont(const char *s1, const char *s2, const char *s3, const char *s4)
        }
        
        static int
       -dirtabs(const char *s1, const char *s2, const char *s3, const char *s4)
       +dirtabs(const char *s1, const char *s2, const char *s3, const char *s4, const char *s5)
        {
            int i = atoi(s1);
            if (i <= 0 || i > 12)
       @@ -314,7 +316,7 @@ dirtabs(const char *s1, const char *s2, const char *s3, const char *s4)
        }
        
        static int
       -direxpandtabs(const char *s1, const char *s2, const char *s3, const char *s4)
       +direxpandtabs(const char *s1, const char *s2, const char *s3, const char *s4, const char *s5)
        {
            if (strcasecmp(s1, "true") != 0 && strcasecmp(s1, "false") != 0)
                return -1;
       @@ -327,14 +329,16 @@ typedef struct Directive Directive;
        struct Directive{
            const char *format;
            int result;
       -    int (*action)(const char *, const char *, const char *, const char *);
       +    int (*action)(const char *, const char *, const char *, const char *, const char *);
        };
        
        Directive directives[] ={
       +    {" chord %5[Nn12345] %5[Nn12345] %99s %99s %1023[^\n]",       5, dirchord},
            {" chord %5[Nn12345] %5[Nn12345] %99s %99s",                  4, dirchord},
            {" unchord %5[Nn12345] %5[Nn12345]",                          2, dirunchord},
            {" bind %5[*camshNCAMSH12345] %99s raw 0x%4[0-9a-fA-F]",      3, dirraw},
            {" bind %5[*camshNCAMSH12345] %99s raw %1s",                  3, dirrawliteral},
       +    {" bind %5[*camshNCAMSH12345] %99s command %99s %1023[^\n]",  4, dirbind},
            {" bind %5[*camshNCAMSH12345] %99s command %99s",             3, dirbind},
            {" unbind %5[*camshNCAMSH12345] %99s",                        2, dirunbind},
            {" foreground %1023s",                                        1, dirforeground},
       @@ -359,6 +363,7 @@ loadrcfile(FILE *f)
                char s2[1024] = {0};
                char s3[1024] = {0};
                char s4[1024] = {0};
       +        char s5[1024] = {0};
                int rc = 0;
                bool found = false;
        
       @@ -367,8 +372,8 @@ loadrcfile(FILE *f)
                    continue;
        
                for (Directive *d = directives; d->format && !found; d++){
       -            if (sscanf(l, d->format, s1, s2, s3, s4) == d->result){
       -                rc = d->action(s1, s2, s3, s4);
       +            if (sscanf(l, d->format, s1, s2, s3, s4, s5) == d->result){
       +                rc = d->action(s1, s2, s3, s4, s5);
                        found = true;
                    }
                }
 (DIR) diff --git a/samterm/samterm.h b/samterm/samterm.h
       @@ -24,10 +24,10 @@ enum{
            Ccut,                  /* cut dot */
            Cpaste,                /* paste from snarf buffer */
            Cexchange,             /* exchange snarf buffer with OS */
       -    Cwrite,                /* write file */
            Ceol,                  /* move to beginning of line */
            Cbol,                  /* move to end of line */
            Ctab,                  /* insert a possibly expanded tab */
       +    Csend,                 /* send a command to the editor */
            Cmax                   /* invalid command */
        };
        
       @@ -130,7 +130,7 @@ void    Strgrow(Rune**, long*, int);
        int RESHAPED(void);
        void    reshape(void);
        void    rcv(void);
       -void    type(Flayer*, int);
       +void    type(Flayer*);
        void    menu2hit(void);
        void    menu3hit(void);
        void    scroll(Flayer*, int, int);