Support precision mouse scrolling on MS-Windows

* src/w32fns.c (w32_wnd_proc): Pass the WM_SETTINGCHANGE message
to the Lisp thread.
* src/w32term.c (w32_construct_mouse_wheel): Support mice with
precision scrolling wheel.
(w32_get_mouse_wheel_vertical_delta): New function.
(w32_read_socket): When the WM_SETTINGCHANGE is received, call
'w32_get_mouse_wheel_vertical_delta'.
(w32_initialize): Call 'w32_get_mouse_wheel_vertical_delta' at
startup.
* src/nsterm.m (syms_of_nsterm):
* src/haikuterm.c (syms_of_haikuterm):
* src/xterm.c (syms_of_xterm): Remove window-system specific
variables for coalescing mwheel events.
* src/keyboard.c (syms_of_keyboard)
<mwheel-coalesce-scroll-events>: New variable, to replace the
above platform-specific ones.

* doc/lispref/commands.texi (Misc Events): Improve wording of the
description of mouse-wheel events.
This commit is contained in:
Eli Zaretskii 2021-12-01 15:36:55 +02:00
parent c13b49a110
commit bab2969404
7 changed files with 124 additions and 37 deletions

View file

@ -2001,19 +2001,19 @@ These kinds of event are generated by moving a mouse wheel. The
Events}), specifying the position of the mouse cursor when the event
occurred.
@var{clicks}, if present, is the number of times in quick succession
the wheel has been moved. @xref{Repeat Events}. @var{lines}, if
@var{clicks}, if present, is the number of times that the wheel was
moved in quick succession. @xref{Repeat Events}. @var{lines}, if
present and not @code{nil}, is the number of screen lines that should
be scrolled. @var{pixel-delta}, if present, is a pair of the form
@w{@code{(@var{x} . @var{y})}}, where @var{x} and @var{y} are the
number of pixels to scroll by in each axis.
be scrolled. @var{pixel-delta}, if present, is a cons cell of the
form @w{@code{(@var{x} . @var{y})}}, where @var{x} and @var{y} are the
numbers of pixels by which to scroll in each axis, a.k.a.@:
@dfn{pixelwise deltas}.
@cindex pixel-resolution wheel events
You can use @var{x} and @var{y} to determine how much the mouse wheel
has actually moved at pixel resolution.
For example, the pixelwise deltas could be used to scroll the display
at pixel resolution, exactly according to the user's turning the mouse
You can use these @var{x} and @var{y} pixelwise deltas to determine
how much the mouse wheel has actually moved at pixel resolution. For
example, the pixelwise deltas could be used to scroll the display at
pixel resolution, exactly according to the user's turning the mouse
wheel.
@vindex mouse-wheel-up-event

View file

@ -3024,7 +3024,7 @@ haiku_read_socket (struct terminal *terminal, struct input_event *hold_quit)
if (fabsf (py) >= FRAME_LINE_HEIGHT (f)
|| fabsf (px) >= FRAME_COLUMN_WIDTH (f)
|| !x_coalesce_scroll_events)
|| !mwheel_coalesce_scroll_events)
{
inev.kind = (fabsf (px) > fabsf (py)
? HORIZ_WHEEL_EVENT
@ -3565,10 +3565,6 @@ syms_of_haikuterm (void)
doc: /* SKIP: real doc in xterm.c. */);
Vx_toolkit_scroll_bars = Qt;
DEFVAR_BOOL ("x-coalesce-scroll-events", x_coalesce_scroll_events,
doc: /* SKIP: real doc in xterm.c. */);
x_coalesce_scroll_events = true;
DEFVAR_BOOL ("haiku-debug-on-fatal-error", haiku_debug_on_fatal_error,
doc: /* If non-nil, Emacs will launch the system debugger upon a fatal error. */);
haiku_debug_on_fatal_error = 1;

View file

@ -12571,6 +12571,12 @@ other similar functions ignore input events in `while-no-input-ignore-events'.
This flag may eventually be removed once this behavior is deemed safe. */);
input_pending_p_filter_events = true;
DEFVAR_BOOL ("mwheel-coalesce-scroll-events", mwheel_coalesce_scroll_events,
doc: /* Non-nil means send a wheel event only for scrolling at least one screen line.
Otherwise, a wheel event will be sent every time the mouse wheel is
moved. */);
mwheel_coalesce_scroll_events = true;
pdumper_do_now_and_after_load (syms_of_keyboard_for_pdumper);
}

View file

@ -6606,7 +6606,7 @@ - (void)mouseDown: (NSEvent *)theEvent
* reset the total delta for the direction we're NOT
* scrolling so that small movements don't add up. */
if (abs (totalDeltaX) > abs (totalDeltaY)
&& (!x_coalesce_scroll_events
&& (!mwheel_coalesce_scroll_events
|| abs (totalDeltaX) > lineHeight))
{
horizontal = YES;
@ -6614,14 +6614,14 @@ - (void)mouseDown: (NSEvent *)theEvent
lines = abs (totalDeltaX / lineHeight);
x = totalDeltaX;
if (!x_coalesce_scroll_events)
if (!mwheel_coalesce_scroll_events)
totalDeltaX = 0;
else
totalDeltaX = totalDeltaX % lineHeight;
totalDeltaY = 0;
}
else if (abs (totalDeltaY) >= abs (totalDeltaX)
&& (!x_coalesce_scroll_events
&& (!mwheel_coalesce_scroll_events
|| abs (totalDeltaY) > lineHeight))
{
horizontal = NO;
@ -6629,7 +6629,7 @@ - (void)mouseDown: (NSEvent *)theEvent
lines = abs (totalDeltaY / lineHeight);
y = totalDeltaY;
if (!x_coalesce_scroll_events)
if (!mwheel_coalesce_scroll_events)
totalDeltaY = 0;
else
totalDeltaY = totalDeltaY % lineHeight;
@ -6662,7 +6662,7 @@ - (void)mouseDown: (NSEvent *)theEvent
y = [theEvent scrollingDeltaY];
}
if (lines == 0 && x_coalesce_scroll_events)
if (lines == 0 && mwheel_coalesce_scroll_events)
return;
if (NUMBERP (Vns_scroll_event_delta_factor))
@ -10037,10 +10037,6 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with
doc: /* SKIP: real doc in xterm.c. */);
x_underline_at_descent_line = 0;
DEFVAR_BOOL ("x-coalesce-scroll-events", x_coalesce_scroll_events,
doc: /* SKIP: real doc in xterm.c. */);
x_coalesce_scroll_events = true;
DEFSYM (Qx_underline_at_descent_line, "x-underline-at-descent-line");
DEFVAR_LISP ("ns-scroll-event-delta-factor", Vns_scroll_event_delta_factor,

View file

@ -5173,6 +5173,13 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
goto dflt;
case WM_SETTINGCHANGE:
/* Inform the Lisp thread that some system-wide setting has
changed, so if Emacs is interested in some of them, it could
update its internal values. */
my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
goto dflt;
case WM_SETFOCUS:
dpyinfo->faked_key = 0;
reset_modifiers ();

View file

@ -164,6 +164,10 @@ int last_scroll_bar_drag_pos;
/* Keyboard code page - may be changed by language-change events. */
int w32_keyboard_codepage;
/* The number of screen lines to scroll for the default mouse-wheel
scroll amount, given by WHEEL_DELTA. */
static UINT w32_wheel_scroll_lines;
#ifdef CYGWIN
int w32_message_fd = -1;
#endif /* CYGWIN */
@ -271,6 +275,19 @@ XGetGCValues (void *ignore, XGCValues *gc,
}
#endif
static void
w32_get_mouse_wheel_vertical_delta (void)
{
if (os_subtype != OS_SUBTYPE_NT)
return;
UINT scroll_lines;
BOOL ret = SystemParametersInfo (SPI_GETWHEELSCROLLLINES, 0,
&scroll_lines, 0);
if (ret)
w32_wheel_scroll_lines = scroll_lines;
}
static void
w32_set_clip_rectangle (HDC hdc, RECT *rect)
{
@ -3203,32 +3220,94 @@ w32_construct_mouse_wheel (struct input_event *result, W32Msg *msg,
{
POINT p;
int delta;
static int sum_delta_y = 0;
result->kind = msg->msg.message == WM_MOUSEHWHEEL ? HORIZ_WHEEL_EVENT
: WHEEL_EVENT;
result->code = 0;
result->timestamp = msg->msg.time;
result->arg = Qnil;
/* A WHEEL_DELTA positive value indicates that the wheel was rotated
forward, away from the user (up); a negative value indicates that
the wheel was rotated backward, toward the user (down). */
delta = GET_WHEEL_DELTA_WPARAM (msg->msg.wParam);
if (delta == 0)
{
result->kind = NO_EVENT;
return Qnil;
}
/* With multiple monitors, we can legitimately get negative
coordinates, so cast to short to interpret them correctly. */
p.x = (short) LOWORD (msg->msg.lParam);
p.y = (short) HIWORD (msg->msg.lParam);
if (eabs (delta) < WHEEL_DELTA)
{
/* This is high-precision mouse wheel, which sends
fine-resolution wheel events. Produce a wheel event only if
the conditions for sending such an event are fulfilled. */
int scroll_unit = max (w32_wheel_scroll_lines, 1), nlines;
double value_to_report;
/* w32_wheel_scroll_lines == INT_MAX means the user asked for
"entire page" to be the scroll unit. We interpret that as
the height of the window under the mouse pointer. */
if (w32_wheel_scroll_lines == INT_MAX)
{
Lisp_Object window = window_from_coordinates (f, p.x, p.y, NULL,
false, false);
if (!WINDOWP (window))
{
result->kind = NO_EVENT;
return Qnil;
}
scroll_unit = XWINDOW (window)->pixel_height;
if (scroll_unit < 1) /* paranoia */
scroll_unit = 1;
}
/* If mwheel-coalesce-scroll-events is non-nil, report a wheel event
only when we have accumulated enough delta's for WHEEL_DELTA. */
if (mwheel_coalesce_scroll_events)
{
/* If the user changed the direction, reset the accumulated
deltas. */
if ((delta > 0) != (sum_delta_y > 0))
sum_delta_y = 0;
sum_delta_y += delta;
/* https://docs.microsoft.com/en-us/previous-versions/ms997498(v=msdn.10) */
if (eabs (sum_delta_y) < WHEEL_DELTA)
{
result->kind = NO_EVENT;
return Qnil;
}
value_to_report =
((double)FRAME_LINE_HEIGHT (f) * scroll_unit)
/ ((double)WHEEL_DELTA / sum_delta_y);
sum_delta_y = 0;
}
else
value_to_report =
((double)FRAME_LINE_HEIGHT (f) * scroll_unit)
/ ((double)WHEEL_DELTA / delta);
nlines = value_to_report / FRAME_LINE_HEIGHT (f) + 0.5;
result->arg = list3 (make_fixnum (nlines),
make_float (0.0),
make_float (value_to_report));
}
/* The up and down modifiers indicate if the wheel was rotated up or
down based on WHEEL_DELTA value. */
result->modifiers = (msg->dwModifiers
| ((delta < 0 ) ? down_modifier : up_modifier));
/* With multiple monitors, we can legitimately get negative
coordinates, so cast to short to interpret them correctly. */
p.x = (short) LOWORD (msg->msg.lParam);
p.y = (short) HIWORD (msg->msg.lParam);
/* For the case that F's w32 window is not msg->msg.hwnd. */
ScreenToClient (FRAME_W32_WINDOW (f), &p);
XSETINT (result->x, p.x);
XSETINT (result->y, p.y);
XSETFRAME (result->frame_or_window, f);
result->arg = Qnil;
return Qnil;
}
@ -4905,6 +4984,14 @@ w32_read_socket (struct terminal *terminal,
}
break;
case WM_SETTINGCHANGE:
/* We are only interested in changes of the number of lines
to scroll when the vertical mouse wheel is moved. This
is only supported on NT. */
if (msg.msg.wParam == SPI_SETWHEELSCROLLLINES)
w32_get_mouse_wheel_vertical_delta ();
break;
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
f = w32_window_to_frame (dpyinfo, msg.msg.hwnd);
@ -7522,6 +7609,8 @@ w32_initialize (void)
horizontal_scroll_bar_left_border = horizontal_scroll_bar_right_border
= GetSystemMetrics (SM_CYHSCROLL);
}
w32_get_mouse_wheel_vertical_delta ();
}
void

View file

@ -10024,7 +10024,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
val->emacs_value += delta;
if (x_coalesce_scroll_events
if (mwheel_coalesce_scroll_events
&& (fabs (val->emacs_value) < 1))
continue;
@ -15214,13 +15214,6 @@ consuming frame position adjustments. In newer versions of GTK, Emacs
always uses gtk_window_move and ignores the value of this variable. */);
x_gtk_use_window_move = true;
DEFVAR_BOOL ("x-coalesce-scroll-events", x_coalesce_scroll_events,
doc: /* Non-nil means send a wheel event only for scrolling at least one screen line.
Otherwise, a wheel event will be sent every time the mouse wheel is
moved. This option is only effective when Emacs is built with XInput
2, with Haiku windowing support, or with NS. */);
x_coalesce_scroll_events = true;
DEFVAR_LISP ("x-scroll-event-delta-factor", Vx_scroll_event_delta_factor,
doc: /* A scale to apply to pixel deltas reported in scroll events.
This option is only effective when Emacs is built with XInput 2