all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Alan Third <alan@idiocy.org>
To: Tak Kunihiro <homeros.misasa@gmail.com>
Cc: Emacs-Devel devel <emacs-devel@gnu.org>
Subject: Re: Smoother macOS touchpad scrolling
Date: Sat, 16 Sep 2017 23:33:17 +0100	[thread overview]
Message-ID: <20170916223317.GA58067@breton.holly.idiocy.org> (raw)
In-Reply-To: <20170910083758.GA30315@breton.holly.idiocy.org>

[-- Attachment #1: Type: text/plain, Size: 760 bytes --]

On Sun, Sep 10, 2017 at 09:37:58AM +0100, Alan Third wrote:
> 
> You mean one event per pixel to be scrolled, or pass the delta in the
> event?
> 
> I can certainly do the former, although it will potentially result in
> a torrent of scroll events.
> 
> The latter is, I think, banned as it can’t be replicated on a Free
> system, but is the better solution.

I was thinking about this, and it’s not actually a new feature, it’s
an optimization of a feature that already exists (progressive scroll).
The fact the optimization is only available on macOS is irrelevant.

So in that spirit I’ve attached another patch.

Can Eli or John let me know if I’m thinking about this right? And if
this patch *is* ok, is it too late for Emacs 26?
-- 
Alan Third

[-- Attachment #2: 0001-Provide-native-touchpad-scrolling-on-macOS.patch --]
[-- Type: text/plain, Size: 12031 bytes --]

From 9295fbb214b32555b55b0c1c56d0831bf6395c1c Mon Sep 17 00:00:00 2001
From: Alan Third <alan@idiocy.org>
Date: Fri, 8 Sep 2017 19:26:47 +0100
Subject: [PATCH] Provide native touchpad scrolling on macOS

* etc/NEWS: Describe changes.
* lisp/term/ns-win.el (mouse-wheel-scroll-amount,
mouse-wheel-progressive-speed): Set to smarter values for macOS touchpads.
* src/nsterm.m (emacsView::mouseDown): Use precise scrolling deltas to
calculate scrolling for touchpads and mouse wheels.
(syms_of_nsterm): Add variables 'ns-use-system-mwheel-acceleration',
'ns-touchpad-scroll-line-height' and 'ns-touchpad-use-momentum'.
* src/keyboard.c (make_lispy_event): Pass on .arg when relevant.
* src/termhooks.h (event_kind): Update comments re. WHEEL_EVENT.
* lisp/mwheel.el (mwheel-scroll): Use line count.
* lisp/subr.el (event-line-count): New function.
---
 etc/NEWS            |   6 ++
 lisp/mwheel.el      |   1 +
 lisp/subr.el        |   5 ++
 lisp/term/ns-win.el |   7 +++
 src/keyboard.c      |   5 +-
 src/nsterm.m        | 158 ++++++++++++++++++++++++++++++++++++++++++++++++----
 src/termhooks.h     |   4 +-
 7 files changed, 172 insertions(+), 14 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index 371cdf686c..6111738b0f 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -49,6 +49,12 @@ When you add a new item, use the appropriate mark if you are sure it applies,
 \f
 * Changes in Emacs 27.1 on Non-Free Operating Systems
 
+---
+** Mousewheel and trackpad scrolling on macOS 10.7+ now behaves more
+like the macOS default.  The new variables
+'ns-use-system-mwheel-acceleration', 'ns-touchpad-scroll-line-height'
+and 'ns-touchpad-use-momentum' can be used to customise the behavior.
+
 \f
 ----------------------------------------------------------------------
 This file is part of GNU Emacs.
diff --git a/lisp/mwheel.el b/lisp/mwheel.el
index 2956ba5516..0c0dcb3beb 100644
--- a/lisp/mwheel.el
+++ b/lisp/mwheel.el
@@ -232,6 +232,7 @@ mwheel-scroll
       ;; When the double-mouse-N comes in, a mouse-N has been executed already,
       ;; So by adding things up we get a squaring up (1, 3, 6, 10, 15, ...).
       (setq amt (* amt (event-click-count event))))
+    (when (numberp amt) (setq amt (* amt (event-line-count event))))
     (unwind-protect
 	(let ((button (mwheel-event-button event)))
 	  (cond ((eq button mouse-wheel-down-event)
diff --git a/lisp/subr.el b/lisp/subr.el
index 79ae1f4830..f0d0b24462 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -1270,6 +1270,11 @@ event-click-count
   "Return the multi-click count of EVENT, a click or drag event.
 The return value is a positive integer."
   (if (and (consp event) (integerp (nth 2 event))) (nth 2 event) 1))
+
+(defsubst event-line-count (event)
+  "Return the line count of EVENT, a mousewheel event.
+The return value is a positive integer."
+  (if (and (consp event) (integerp (nth 3 event))) (nth 3 event) 1))
 \f
 ;;;; Extracting fields of the positions in an event.
 
diff --git a/lisp/term/ns-win.el b/lisp/term/ns-win.el
index 68b659bf75..6aef27362f 100644
--- a/lisp/term/ns-win.el
+++ b/lisp/term/ns-win.el
@@ -736,6 +736,13 @@ ns-paste-secondary
 (global-unset-key [horizontal-scroll-bar drag-mouse-1])
 
 
+;;;; macOS-like defaults for trackpad and mouse wheel scrolling.
+
+(when (featurep 'cocoa)
+  (setq mouse-wheel-scroll-amount '(1 ((shift) . 5) ((control))))
+  (setq mouse-wheel-progressive-speed nil))
+
+
 ;;;; Color support.
 
 ;; Functions for color panel + drag
diff --git a/src/keyboard.c b/src/keyboard.c
index 4db50be855..e8701b8870 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -5925,7 +5925,10 @@ make_lispy_event (struct input_event *event)
 				      ASIZE (wheel_syms));
 	}
 
-	if (event->modifiers & (double_modifier | triple_modifier))
+        if (NUMBERP (event->arg))
+          return list4 (head, position, make_number (double_click_count),
+                        event->arg);
+	else if (event->modifiers & (double_modifier | triple_modifier))
 	  return list3 (head, position, make_number (double_click_count));
 	else
 	  return list2 (head, position);
diff --git a/src/nsterm.m b/src/nsterm.m
index 2751533533..776635980e 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -6498,24 +6498,139 @@ - (void)mouseDown: (NSEvent *)theEvent
 
   if ([theEvent type] == NSEventTypeScrollWheel)
     {
-      CGFloat delta = [theEvent deltaY];
-      /* Mac notebooks send wheel events w/delta =0 when trackpad scrolling */
-      if (delta == 0)
+#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
+      if ([theEvent respondsToSelector:@selector(hasPreciseScrollingDeltas)])
         {
-          delta = [theEvent deltaX];
-          if (delta == 0)
+#endif
+          /* If the input device is a touchpad or similar, use precise
+           * scrolling deltas.  These are measured in pixels, so we
+           * have to add them up until they exceed one line height,
+           * then we can send a scroll wheel event.
+           *
+           * If the device only has coarse scrolling deltas, like a
+           * real mousewheel, the deltas represent a ratio of whole
+           * lines, so round up the number of lines.  This means we
+           * always send one scroll event per click, but can still
+           * scroll more than one line if the OS tells us to.
+           */
+          bool horizontal;
+          int lines = 0;
+          int scrollUp = NO;
+
+          /* FIXME: At the top or bottom of the buffer we should
+           * ignore momentum-phase events.  */
+          if (! ns_touchpad_use_momentum
+              && [theEvent momentumPhase] != NSEventPhaseNone)
+            return;
+
+          if ([theEvent hasPreciseScrollingDeltas])
             {
-              NSTRACE_MSG ("deltaIsZero");
-              return;
+              static int totalDeltaX, totalDeltaY;
+              int lineHeight;
+
+              if (NUMBERP (ns_touchpad_scroll_line_height))
+                lineHeight = XINT (ns_touchpad_scroll_line_height);
+              else
+                {
+                  /* FIXME: Use actual line height instead of the default.  */
+                  lineHeight = default_line_pixel_height
+                    (XWINDOW (FRAME_SELECTED_WINDOW (emacsframe)));
+                }
+
+              if ([theEvent phase] == NSEventPhaseBegan)
+                {
+                  totalDeltaX = 0;
+                  totalDeltaY = 0;
+                }
+
+              totalDeltaX += [theEvent scrollingDeltaX];
+              totalDeltaY += [theEvent scrollingDeltaY];
+
+              /* Calculate the number of lines, if any, to scroll, and
+               * reset the total delta for the direction we're NOT
+               * scrolling so that small movements don't add up.  */
+              if (abs (totalDeltaX) > abs (totalDeltaY)
+                  && abs (totalDeltaX) > lineHeight)
+                {
+                  horizontal = YES;
+                  scrollUp = totalDeltaX > 0;
+
+                  lines = abs (totalDeltaX / lineHeight);
+                  totalDeltaX = totalDeltaX % lineHeight;
+                  totalDeltaY = 0;
+                }
+              else if (abs (totalDeltaY) >= abs (totalDeltaX)
+                       && abs (totalDeltaY) > lineHeight)
+                {
+                  horizontal = NO;
+                  scrollUp = totalDeltaY > 0;
+
+                  lines = abs (totalDeltaY / lineHeight);
+                  totalDeltaY = totalDeltaY % lineHeight;
+                  totalDeltaX = 0;
+                }
+
+              if (lines > 1 && ! ns_use_system_mwheel_acceleration)
+                lines = 1;
             }
-          emacs_event->kind = HORIZ_WHEEL_EVENT;
+          else
+            {
+              CGFloat delta;
+
+              if ([theEvent scrollingDeltaY] == 0)
+                {
+                  horizontal = YES;
+                  delta = [theEvent scrollingDeltaX];
+                }
+              else
+                {
+                  horizontal = NO;
+                  delta = [theEvent scrollingDeltaY];
+                }
+
+              lines = (ns_use_system_mwheel_acceleration)
+                ? ceil (fabs (delta)) : 1;
+
+              scrollUp = delta > 0;
+            }
+
+          if (lines == 0)
+            return;
+
+          emacs_event->kind = horizontal ? HORIZ_WHEEL_EVENT : WHEEL_EVENT;
+          emacs_event->arg = (make_number (lines));
+
+          emacs_event->code = 0;
+          emacs_event->modifiers = EV_MODIFIERS (theEvent) |
+            (scrollUp ? up_modifier : down_modifier);
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
         }
       else
-        emacs_event->kind = WHEEL_EVENT;
+#endif
+#endif /* defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
+#if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 1070
+        {
+          CGFloat delta = [theEvent deltaY];
+          /* Mac notebooks send wheel events w/delta =0 when trackpad scrolling */
+          if (delta == 0)
+            {
+              delta = [theEvent deltaX];
+              if (delta == 0)
+                {
+                  NSTRACE_MSG ("deltaIsZero");
+                  return;
+                }
+              emacs_event->kind = HORIZ_WHEEL_EVENT;
+            }
+          else
+            emacs_event->kind = WHEEL_EVENT;
 
-      emacs_event->code = 0;
-      emacs_event->modifiers = EV_MODIFIERS (theEvent) |
-        ((delta > 0) ? up_modifier : down_modifier);
+          emacs_event->code = 0;
+          emacs_event->modifiers = EV_MODIFIERS (theEvent) |
+            ((delta > 0) ? up_modifier : down_modifier);
+        }
+#endif
     }
   else
     {
@@ -6524,9 +6639,11 @@ - (void)mouseDown: (NSEvent *)theEvent
       emacs_event->modifiers = EV_MODIFIERS (theEvent)
                              | EV_UDMODIFIERS (theEvent);
     }
+
   XSETINT (emacs_event->x, lrint (p.x));
   XSETINT (emacs_event->y, lrint (p.y));
   EV_TRAILER (theEvent);
+  return;
 }
 
 
@@ -9166,6 +9283,23 @@ Nil means use fullscreen the old (< 10.7) way.  The old way works better with
 This variable is ignored on Mac OS X < 10.7 and GNUstep.  */);
   ns_use_srgb_colorspace = YES;
 
+  DEFVAR_BOOL ("ns-use-system-mwheel-acceleration",
+               ns_use_system_mwheel_acceleration,
+     doc: /*Non-nil means use macOS's standard mouse wheel acceleration.
+This variable is ignored on macOS < 10.7 and GNUstep.  Default is t.  */);
+  ns_use_system_mwheel_acceleration = YES;
+
+  DEFVAR_LISP ("ns-touchpad-scroll-line-height", ns_touchpad_scroll_line_height,
+               doc: /*The number of pixels touchpad scrolling considers a line.
+Nil or a non-number means use the default frame line height.
+This variable is ignored on macOS < 10.7 and GNUstep.  Default is nil.  */);
+  ns_touchpad_scroll_line_height = Qnil;
+
+  DEFVAR_BOOL ("ns-touchpad-use-momentum", ns_touchpad_use_momentum,
+               doc: /*Non-nil means touchpad scrolling uses momentum.
+This variable is ignored on macOS < 10.7 and GNUstep.  Default is t.  */);
+  ns_touchpad_use_momentum = YES;
+
   /* TODO: move to common code */
   DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars,
 	       doc: /* Which toolkit scroll bars Emacs uses, if any.
diff --git a/src/termhooks.h b/src/termhooks.h
index 97c128ba4e..b5171bf122 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -116,7 +116,9 @@ enum event_kind
 				   .frame_or_window gives the frame
 				   the wheel event occurred in.
 				   .timestamp gives a timestamp (in
-				   milliseconds) for the event.  */
+				   milliseconds) for the event.
+                                   .arg may contain the number of
+                                   lines to scroll.  */
   HORIZ_WHEEL_EVENT,            /* A wheel event generated by a second
                                    horizontal wheel that is present on some
                                    mice. See WHEEL_EVENT.  */
-- 
2.14.1


  parent reply	other threads:[~2017-09-16 22:33 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-09-08 18:46 Smoother macOS touchpad scrolling Alan Third
2017-09-08 22:21 ` Tak Kunihiro
2017-09-09  8:18   ` Alan Third
2017-09-10  1:20     ` Tak Kunihiro
2017-09-10  8:37       ` Alan Third
2017-09-11 18:09         ` Alan Third
2017-09-12  1:54           ` James Nguyen
2017-09-12 17:03             ` Alan Third
2017-09-12 23:13           ` Tak Kunihiro
2017-09-13 16:26             ` Better macOS scrolling (was: Smoother macOS touchpad scrolling) Alan Third
2017-09-13 22:52               ` Better macOS scrolling Tak Kunihiro
2017-09-16 22:33         ` Alan Third [this message]
2017-09-18 18:10           ` Smoother macOS touchpad scrolling Charles A. Roelli
2017-09-18 18:57             ` Charles A. Roelli
2017-09-18 19:03             ` Alan Third
2017-09-18 20:19               ` Charles A. Roelli
2017-09-19 19:12                 ` Alan Third

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170916223317.GA58067@breton.holly.idiocy.org \
    --to=alan@idiocy.org \
    --cc=emacs-devel@gnu.org \
    --cc=homeros.misasa@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.