From 6b1feeef6bd29ad77c67181f11315b8dbc4e72ca Mon Sep 17 00:00:00 2001
From: Devin Hussey <husseydevin@gmail.com>
Date: Fri, 8 Jun 2018 12:16:47 -0400
Subject: [PATCH] Add an option to control mouse wheel smooth scroll speed.

nanorc syntax:

set scrollspeed [numlines][,numclicks]

nano will now scroll numlines after accepting numclicks wheel
events.

For example, a settings of 5,2 will scroll 5 lines after two wheel
events.

numlines can be negative for reverse scrolling, but numclicks must
be positive. Both must not be zero.

The second setting is to add precision control to wheels or trackpads
that smooth scroll by sending multiple events.

This is very basic, the setting should reset after any extra events
or after a scroll in the opposite direction, but it is a good start
for now.

Signed-off-by: Devin Hussey <husseydevin@gmail.com>
---
 doc/nanorc.5 | 14 ++++++++++++++
 src/global.c |  5 +++++
 src/proto.h  |  4 ++++
 src/rcfile.c | 36 ++++++++++++++++++++++++++++++++++++
 src/winio.c  | 25 ++++++++++++++++++++-----
 5 files changed, 79 insertions(+), 5 deletions(-)

diff --git a/doc/nanorc.5 b/doc/nanorc.5
index 12a9fd73..a005424f 100644
--- a/doc/nanorc.5
+++ b/doc/nanorc.5
@@ -222,6 +222,20 @@ won't work properly with this option enabled.
 .B set regexp
 Do extended regular expression searches by default.
 .TP
+.B set scrollspeed \fInumlines\fR,\fInumsteps\fR
+Only works when smooth scrolling is enabled.  Sets the scroll speed.
+\fInumlines\fR sets how many lines scrolled after a mouse wheel click.
+If \fInumsteps\fR is greater than 1, nano will wait for \fInumsteps\fR
+wheel clicks before moving the cursor.  The default value is 3,1, or three
+lines per mouse wheel.
+.sp
+You rarely need \fInumsteps\fR if you have a mouse wheel that operates in
+clicks; the main use for this is for smooth scroll trackpads/mice which
+deliver many smaller scroll events at once.
+.sp
+\fInumlines\fR must not be zero (negative values result in reverse
+scrolling), and \fInumsteps\fR must be greater than zero.
+.TP
 .B set selectedcolor \fIfgcolor\fR,\fIbgcolor\fR
 Specify the color combination to use for selected text.
 See \fBset titlecolor\fR for more details.
diff --git a/src/global.c b/src/global.c
index fc5fad35..e530960a 100644
--- a/src/global.c
+++ b/src/global.c
@@ -68,6 +68,11 @@ bool also_the_last = FALSE;
 int didfind = 0;
 		/* Whether the last search found something. */
 
+#ifdef ENABLE_MOUSE
+int scroll_speed[2] = { 3, 1 };
+		/* The number of lines scrolled[0] per x mouse wheel clicks[1]. */
+#endif
+
 int controlleft, controlright, controlup, controldown, controlhome, controlend;
 #ifndef NANO_TINY
 int shiftcontrolleft, shiftcontrolright, shiftcontrolup, shiftcontroldown;
diff --git a/src/proto.h b/src/proto.h
index 399725a8..f0c2ecb3 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -55,6 +55,10 @@ extern bool also_the_last;
 
 extern int didfind;
 
+#ifdef ENABLE_MOUSE
+extern int scroll_speed[2];
+#endif
+
 extern int controlleft;
 extern int controlright;
 extern int controlup;
diff --git a/src/rcfile.c b/src/rcfile.c
index 2e6675ea..c9f0f798 100644
--- a/src/rcfile.c
+++ b/src/rcfile.c
@@ -55,6 +55,7 @@ static const rcoption rcopts[] = {
 	{"morespace", MORE_SPACE},
 #ifdef ENABLE_MOUSE
 	{"mouse", USE_MOUSE},
+	{"scrollspeed", 0},
 #endif
 #ifdef ENABLE_MULTIBUFFER
 	{"multibuffer", MULTIBUFFER},
@@ -182,6 +183,36 @@ char *parse_next_word(char *ptr)
 #endif /* ENABLE_NANORC || ENABLE_HISTORIES */
 
 #ifdef ENABLE_NANORC
+#ifdef ENABLE_MOUSE
+/* Parses the scrollspeed option, in the format x,y representing
+ * scroll_speed[0] and scroll_speed[1]. */
+static void set_scroll_speed(char *speed) {
+	char *comma = strchr(speed, ',');
+
+	if (comma != NULL) {
+		int value;
+
+		if (*(comma + 1) == '\0')
+			rcfile_error(N_("Scroll speed value expected after comma."));
+
+		value = (int)strtol(comma + 1, NULL, 10);
+
+		if (value <= 0)
+			rcfile_error(N_("Invalid scroll count speed."));
+
+		scroll_speed[1] = value;
+	}
+	if (*speed != ',') {
+		int value = (int)strtol(speed, NULL, 10);
+
+		if (value == 0)
+			rcfile_error(N_("Invalid scroll speed."));
+
+		scroll_speed[0] = value;
+	}
+}
+#endif
+
 /* Parse an argument, with optional quotes, after a keyword that takes
  * one.  If the next word starts with a ", we say that it ends with the
  * last " of the line.  Otherwise, we interpret it as usual, so that the
@@ -1145,6 +1176,11 @@ void parse_rcfile(FILE *rcstream, bool syntax_only)
 			}
 		} else
 #endif
+#ifdef ENABLE_MOUSE
+		if (strcasecmp(rcopts[i].name, "scrollspeed") == 0) {
+			set_scroll_speed(option);
+		} else
+#endif
 #ifdef ENABLE_JUSTIFY
 		if (strcasecmp(rcopts[i].name, "punct") == 0) {
 			if (has_blank_mbchars(option)) {
diff --git a/src/winio.c b/src/winio.c
index 281488a8..fbe79810 100644
--- a/src/winio.c
+++ b/src/winio.c
@@ -1702,11 +1702,26 @@ int get_mouseinput(int *mouse_y, int *mouse_x, bool allow_shortcuts)
 
 		if (in_edit || (in_bottomwin && *mouse_y == 0)) {
 			int i;
-
-			/* One roll of the mouse wheel should move three lines. */
-			for (i = 0; i < 3; i++)
-				unget_kbinput((mevent.bstate & BUTTON4_PRESSED) ?
-								KEY_UP : KEY_DOWN, FALSE);
+			static int wheel_counter = 0;
+
+
+			wheel_counter++;
+			/* Wait until the user has pressed the scroll wheel n times. */
+			if (wheel_counter == scroll_speed[1]) {
+				wheel_counter = 0;
+				/* Move the number of lines set by the user, backwards if
+				 * scroll_speed < 0. */
+				for (i = 0; i < abs(scroll_speed[0]); i++) {
+					if (scroll_speed[0] > 0)
+						unget_kbinput((mevent.bstate & BUTTON4_PRESSED) ?
+										KEY_UP : KEY_DOWN, FALSE);
+					else
+						unget_kbinput((mevent.bstate & BUTTON4_PRESSED) ?
+										KEY_DOWN : KEY_UP, FALSE);
+				}
+			} else {
+				return 2;
+			}
 
 			return 1;
 		} else
-- 
2.17.0

