Mouse chords are now configurable. - 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 35fa702ade26dc48a65c5825ea5d9f75964ba612
 (DIR) parent 13252882e8b54357f2542403aa83dbe3fa9316f3
 (HTM) Author: Rob King <jking@deadpixi.com>
       Date:   Thu,  1 Sep 2016 10:45:38 -0500
       
       Mouse chords are now configurable.
       
       This also removes more code from samterm.c/main, which has always been
       more complicated than it needs to be. Eventually we'll get it down to
       its minimal implementation! :)
       
       Diffstat:
         Makefile                            |       7 +++++--
         README.rst                          |       4 +++-
         chords.h.def                        |      23 +++++++++++++++++++++++
         libXg/gwin.c                        |      68 ++++++++++++++++++++++---------
         samterm/main.c                      |      30 ++++++++----------------------
       
       5 files changed, 88 insertions(+), 44 deletions(-)
       ---
 (DIR) diff --git a/Makefile b/Makefile
       @@ -6,7 +6,10 @@
        
        MODE?=user
        
       -all:        config.h config.mk commands.h lXg lframe rsamdir samdir samtermdir docdir
       +all:        chords.h config.h config.mk commands.h lXg lframe rsamdir samdir samtermdir docdir
       +
       +chords.h:
       +        cp chords.h.def chords.h
        
        commands.h:
                cp commands.h.def commands.h
       @@ -51,4 +54,4 @@ clean:
                cd rsam; $(MAKE) clean
        
        nuke: clean
       -        rm -f config.h commands.h config.mk
       +        rm -f chords.h config.h commands.h config.mk
 (DIR) diff --git a/README.rst b/README.rst
       @@ -107,7 +107,9 @@ Support for Two-Button Mice and Wheel Mice
            This version also supports scrolling with mouse wheels.
        
        Support for Mouse Chords
       -    The snarf, cut, and paste commands are accessible via mouse-button combinations ("chords").
       +    The commands available for keyboard binding are also accessible via mouse-button combinations ("chords").
       +    By default, the snarf, cut, and paste commands are mapped to chords.
       +    The binding of these chords is configurable at compile-time.
        
        Better Remote Editing Support
            This version of sam can use `ssh(1)` as its remote shell.
 (DIR) diff --git a/chords.h.def b/chords.h.def
       @@ -0,0 +1,23 @@
       +/* This file defines mappings of mouse chords to commands.
       + * The lines are of the form:
       + *
       + *    {start, end, kind, action}
       + *
       + * start    - the start state (i.e. what mouse buttons were pressed)
       + * end      - the end state (i.e. what mouse buttons are now pressed)
       + * action   - one of the commands listed in commands.h (or commands.h.def)
       + *
       + * The default configuration shipped with sam has the mouse chords of the
       + * "classic" Unix sam of the 1980s.
       + */
       +
       +{B1, B1|B2, Kcommand, Ccut},
       +{B1, B1|B3, Kcommand, Cpaste},
       +
       +/* The lines below "cancel" the mouse movement that is implicit above.
       + * If these lines are not present, dot will move to where the mouse is
       + * after the chords above. Some people might like that: if so, just
       + * remove these lines.
       + */
       +{B1|B3, B1, Kcommand, Cnone},
       +{B1|B2, B1, Kcommand, Cnone},
 (DIR) diff --git a/libXg/gwin.c b/libXg/gwin.c
       @@ -2,9 +2,6 @@
        #include <u.h>
        #include <libc.h>
        #include <stdio.h>
       -#if        defined(v10) || defined(HPUX)
       -typedef        char*        caddr_t;
       -#endif
        #include <X11/IntrinsicP.h>
        #include <X11/StringDefs.h>
        #include <X11/Xatom.h>
       @@ -203,16 +200,13 @@ Keyaction(Widget w, XEvent *e, String *p, Cardinal *np)
                XtTranslateKeycode(e->xany.display, (KeyCode)e->xkey.keycode, e->xkey.state, &md, &k);
        
            /* Check to see if it's a specially-handled key first. */
       -    Keymapping *m = keymappings;
       -    while (m->kind != Kend){
       +    for (Keymapping *m = keymappings; m && m->kind != Kend; m++){
                if (e->xkey.state == m->mask && k == m->sym){
       -                f = ((GwinWidget)w)->gwin.gotchar;
       -                if(f)
       -                        (*f)(m->result, m->kind);
       +            f = ((GwinWidget)w)->gwin.gotchar;
       +            if (f)
       +                (*f)(m->result, m->kind);
                    return;
                }
       -
       -        m++;
            }
        
                /*
       @@ -292,22 +286,41 @@ Keyaction(Widget w, XEvent *e, String *p, Cardinal *np)
                        (*f)(c, kind);
        }
        
       +typedef struct Chordmapping Chordmapping;
       +struct Chordmapping{
       +    int start;
       +    int end;
       +    int kind;
       +    int result;
       +};
       +
       +#define B1 Button1Mask
       +#define B2 Button2Mask
       +#define B3 Button3Mask
       +#define B4 Button4Mask
       +#define B5 Button5Mask
       +
       +Chordmapping chordmappings[] ={
       +    #include "../chords.h"
       +    {0, 0, Kend, 0}
       +};
       +
        static void
        Mouseaction(Widget w, XEvent *e, String *p, Cardinal *np)
        {
       -        int s;
       -        XButtonEvent *be;
       -        XMotionEvent *me;
       +        int s = 0;
       +    int ps = 0; /* the previous state */
       +        XButtonEvent *be = (XButtonEvent *)e;
       +        XMotionEvent *me = (XMotionEvent *)e;
                Gwinmouse m;
                Mousefunc f;
        
                switch(e->type){
                case ButtonPress:
       -                be = (XButtonEvent *)e;
                        m.xy.x = be->x;
                        m.xy.y = be->y;
                        m.msec = be->time;
       -                s = be->state;        /* the previous state */
       +                ps = s = be->state;
                        switch(be->button){
                        case 1:        s |= Button1Mask; break;
                        case 2:        s |= Button2Mask; break;
       @@ -317,11 +330,10 @@ Mouseaction(Widget w, XEvent *e, String *p, Cardinal *np)
                        }
                        break;
                case ButtonRelease:
       -                be = (XButtonEvent *)e;
                        m.xy.x = be->x;
                        m.xy.y = be->y;
                        m.msec = be->time;
       -                s = be->state;
       +                ps = s = be->state;
                        switch(be->button){
                        case 1:        s &= ~Button1Mask; break;
                        case 2:        s &= ~Button2Mask; break;
       @@ -331,8 +343,7 @@ Mouseaction(Widget w, XEvent *e, String *p, Cardinal *np)
                        }
                        break;
                case MotionNotify:
       -                me = (XMotionEvent *)e;
       -                s = me->state;
       +                ps = s = me->state;
                        m.xy.x = me->x;
                        m.xy.y = me->y;
                        m.msec = me->time;
       @@ -340,12 +351,31 @@ Mouseaction(Widget w, XEvent *e, String *p, Cardinal *np)
                default:
                        return;
                }
       +
       +    /* Check to see if it's a chord first. */
       +    for (Chordmapping *cm = chordmappings; cm && cm->kind != Kend; cm++){
       +        if (ps == cm->start && s == cm->end){
       +            Charfunc kf = ((GwinWidget)w)->gwin.gotchar;
       +            if (kf)
       +                (*kf)(cm->result, cm->kind);
       +
       +            memset(&m, 0, sizeof(m));
       +                // XXX m.buttons = 0;
       +                f = ((GwinWidget)w)->gwin.gotmouse;
       +                if(f)
       +                        (*f)(&m);
       +
       +            return;
       +        }
       +    }
       +
                m.buttons = 0;
                if(s & Button1Mask) m.buttons |= 1;
                if(s & Button2Mask) m.buttons |= 2;
                if(s & Button3Mask) m.buttons |= (s & ShiftMask) ? 2 : 4;
                if(s & Button4Mask) m.buttons |= 8;
                if(s & Button5Mask) m.buttons |= 16;
       +
                f = ((GwinWidget)w)->gwin.gotmouse;
                if(f)
                        (*f)(&m);
 (DIR) diff --git a/samterm/main.c b/samterm/main.c
       @@ -27,7 +27,6 @@ long        modified = 0;                /* strange lookahead for menus */
        char        lock = 1;
        char        hasunlocked = 0;
        int expandtabs = 0;
       -int        chord = 0;
        char *machine = "localhost";
        int nofifo = 0;
        
       @@ -102,11 +101,7 @@ main(int argc, char *argv[])
                                scr = which && ptinrect(mouse.xy, which->scroll);
                                if(mouse.buttons)
                                        flushtyping(1);
       -                        if (chord == 1 && !mouse.buttons)
       -                                chord = 0;
       -                        if (chord)
       -                                chord |= mouse.buttons & 7;
       -                        else if(mouse.buttons&1){
       +            if(mouse.buttons&1){
                                        if(nwhich){
                                                if(nwhich!=which)
                                                        current(nwhich);
       @@ -119,8 +114,6 @@ main(int argc, char *argv[])
                                                                t->lock++;
                                                        }else if(t!=&cmd)
                                                                outcmd();
       -                                                if(mouse.buttons&1)
       -                                                        chord = mouse.buttons;
                                                }
                                        }
                                }else if((mouse.buttons&2) && which){
       @@ -142,20 +135,6 @@ main(int argc, char *argv[])
                                }
                                mouseunblock();
                        }
       -                if(chord) {
       -                        t = (Text *)which->user1;
       -                        if(!t->lock){
       -                                int w = which-t->l;
       -                                if(chord&2){
       -                                        cut(t, w, 1, 1);
       -                                        chord &= ~2;
       -                                }
       -                                if(chord&4){
       -                                        paste(t, w);
       -                                        chord &= ~4;
       -                                }
       -                        }
       -                }
                }
        }
        
       @@ -781,6 +760,12 @@ cmdwrite(Flayer *l, long a, Text *t)
            return a;
        }
        
       +static long
       +cmdnone(Flayer *l, long a, Text *t)
       +{
       +    return a;
       +}
       +
        typedef long (*Commandfunc)(Flayer *, long, Text *);
        typedef struct CommandEntry CommandEntry;
        struct CommandEntry{
       @@ -789,6 +774,7 @@ struct CommandEntry{
        };
        
        CommandEntry commands[Cmax] ={
       +    [Cnone]       = {cmdnone,       0},
            [Cscrolldown] = {cmdscrolldown, 0},
            [Cscrollup]   = {cmdscrollup,   0},
            [Ccharleft]   = {cmdcharleft,   0},