From: Eli Zaretskii Date: Wed, 1 Dec 2021 13:36:55 +0000 (+0200) Subject: Support precision mouse scrolling on MS-Windows X-Git-Tag: emacs-29.0.90~3632^2~3 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=bab29694047d060bb58ff88e1a4d2cc7ef05df2a;p=emacs.git 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) : New variable, to replace the above platform-specific ones. * doc/lispref/commands.texi (Misc Events): Improve wording of the description of mouse-wheel events. --- diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 920d380266f..073cdd8aa7b 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -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 diff --git a/src/haikuterm.c b/src/haikuterm.c index 6bf45894065..f3c37b0258e 100644 --- a/src/haikuterm.c +++ b/src/haikuterm.c @@ -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; diff --git a/src/keyboard.c b/src/keyboard.c index c98175aea0d..b3e6e5029be 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -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); } diff --git a/src/nsterm.m b/src/nsterm.m index 807309eb2eb..5c19b0cab82 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -6606,7 +6606,7 @@ not_in_argv (NSString *arg) * 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 @@ not_in_argv (NSString *arg) 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 @@ not_in_argv (NSString *arg) 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 @@ not_in_argv (NSString *arg) 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 @@ This variable is ignored on macOS < 10.7 and GNUstep. Default is t. */); 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, diff --git a/src/w32fns.c b/src/w32fns.c index c1686beaaa9..65463b52616 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -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 (); diff --git a/src/w32term.c b/src/w32term.c index 07a5cd35649..6b7cbbea6ca 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -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 diff --git a/src/xterm.c b/src/xterm.c index ed6a31125c9..d633953018c 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -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