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},