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.
This commit is contained in:
Alan Third 2017-09-08 19:26:47 +01:00
parent 7b3d1c6beb
commit a5fec62b51
7 changed files with 184 additions and 14 deletions

View file

@ -1882,6 +1882,12 @@ of frame decorations on macOS 10.9+.
---
** 'process-attributes' on Darwin systems now returns more information.
---
** 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.
----------------------------------------------------------------------
This file is part of GNU Emacs.

View file

@ -232,6 +232,7 @@ non-Windows systems."
;; 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)

View file

@ -1270,6 +1270,11 @@ See `event-start' for a description of the value returned."
"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))
;;;; Extracting fields of the positions in an event.

View file

@ -736,6 +736,25 @@ See the documentation of `create-fontset-from-fontset-spec' for the format.")
(global-unset-key [horizontal-scroll-bar drag-mouse-1])
;;;; macOS-like defaults for trackpad and mouse wheel scrolling on
;;;; macOS 10.7+.
;; FIXME: This doesn't look right. Is there a better way to do this
;; that keeps customize happy?
(let ((appkit-version (progn
(string-match "^appkit-\\([^\s-]*\\)" ns-version-string)
(string-to-number (match-string 1 ns-version-string)))))
;; Appkit 1138 ~= macOS 10.7.
(when (and (featurep 'cocoa) (>= appkit-version 1138))
(setq mouse-wheel-scroll-amount '(1 ((shift) . 5) ((control))))
(put 'mouse-wheel-scroll-amount 'customized-value
(list (custom-quote (symbol-value 'mouse-wheel-scroll-amount))))
(setq mouse-wheel-progressive-speed nil)
(put 'mouse-wheel-progressive-speed 'customized-value
(list (custom-quote (symbol-value 'mouse-wheel-progressive-speed))))))
;;;; Color support.
;; Functions for color panel + drag

View file

@ -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);

View file

@ -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.

View file

@ -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. */