From: Po Lu Date: Thu, 2 Dec 2021 03:01:59 +0000 (+0800) Subject: Add some primitive momentum-based precision scrolling X-Git-Tag: emacs-29.0.90~3629^2~8 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=603bdc8d38ce4325380d29423939e5287e8aac17;p=emacs.git Add some primitive momentum-based precision scrolling The algorithm used to scroll the display kinetically is very simple and needs improvement. Someone should work on that eventually. * lisp/pixel-scroll.el (pixel-scroll-precision-use-momentum): New user option. (pixel-scroll-precision-mode-map): Add `pixel-scroll-start-momentum'. (pixel-scroll-kinetic-state): (pixel-scroll-accumulate-velocity): (pixel-scroll-calculate-velocity): New functions. (pixel-scroll-start-momentum): New command. * src/xterm.c (handle_one_xevent): Fix touch-end event generation. --- diff --git a/lisp/pixel-scroll.el b/lisp/pixel-scroll.el index 2d6531a2d17..092d7215d31 100644 --- a/lisp/pixel-scroll.el +++ b/lisp/pixel-scroll.el @@ -68,6 +68,7 @@ (require 'mwheel) (require 'subr-x) +(require 'ring) (defvar pixel-wait 0 "Idle time on each step of pixel scroll specified in second. @@ -97,9 +98,17 @@ is always with pixel resolution.") (let ((map (make-sparse-keymap))) (define-key map [wheel-down] #'pixel-scroll-precision) (define-key map [wheel-up] #'pixel-scroll-precision) + (define-key map [touch-end] #'pixel-scroll-start-momentum) map) "The key map used by `pixel-scroll-precision-mode'.") +(defcustom pixel-scroll-precision-use-momentum nil + "If non-nil, continue to scroll the display after wheel movement stops. +This is only effective if supported by your mouse or touchpad." + :group 'mouse + :type 'boolean + :version "29.1") + (defun pixel-scroll-in-rush-p () "Return non-nil if next scroll should be non-smooth. When scrolling request is delivered soon after the previous one, @@ -475,9 +484,11 @@ wheel." (mwheel-scroll event nil) (with-selected-window window (condition-case nil - (if (< delta 0) - (pixel-scroll-precision-scroll-down (- delta)) - (pixel-scroll-precision-scroll-up delta)) + (progn + (if (< delta 0) + (pixel-scroll-precision-scroll-down (- delta)) + (pixel-scroll-precision-scroll-up delta)) + (pixel-scroll-accumulate-velocity delta)) ;; Do not ding at buffer limits. Show a message instead. (beginning-of-buffer (message (error-message-string '(beginning-of-buffer)))) @@ -485,6 +496,61 @@ wheel." (message (error-message-string '(end-of-buffer))))))))) (mwheel-scroll event nil)))) +(defun pixel-scroll-kinetic-state () + "Return the kinetic scroll state of the current window. +It is a vector of the form [ VELOCITY TIME ]." + (or (window-parameter nil 'kinetic-state) + (set-window-parameter nil 'kinetic-state + (vector (make-ring 4) nil)))) + +(defun pixel-scroll-accumulate-velocity (delta) + "Accumulate DELTA into the current window's kinetic scroll state." + (let* ((state (pixel-scroll-kinetic-state)) + (time (aref state 1))) + (when (and time (> (- (float-time) time) 0.5)) + (aset state 0 (make-ring 45))) + (ring-insert (aref state 0) + (cons (aset state 1 (float-time)) + delta)))) + +(defun pixel-scroll-calculate-velocity (state) + "Calculate velocity from the kinetic state vector STATE." + (let* ((ring (aref state 0)) + (elts (ring-elements ring)) + (total 0)) + (dolist (tem elts) + (setq total (+ total (cdr tem)))) + (/ total (* (- (caar elts) + (caar (last elts))) + 100)))) + +(defun pixel-scroll-start-momentum (event) + "Start kinetic scrolling for the touch event EVENT." + (interactive "e") + (when pixel-scroll-precision-use-momentum + (let ((window (mwheel-event-window event)) + (state nil)) + (with-selected-window window + (setq state (pixel-scroll-kinetic-state)) + (when (aref state 1) + (unwind-protect (progn + (aset state 0 + (pixel-scroll-calculate-velocity state)) + (let ((velocity (aref state 0))) + (if (> velocity 0) + (while (> velocity 0) + (pixel-scroll-precision-scroll-up 1) + (setq velocity (1- velocity)) + (sit-for 0.1) + (redisplay t)) + (while (< velocity 0) + (pixel-scroll-precision-scroll-down 1) + (setq velocity (1+ velocity)) + (sit-for 0.1) + (redisplay t))))) + (aset state 0 (make-ring 45)) + (aset state 1 nil))))))) + ;;;###autoload (define-minor-mode pixel-scroll-precision-mode "Toggle pixel scrolling. diff --git a/src/xterm.c b/src/xterm.c index fe26e41421f..3f7b9560345 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -10025,11 +10025,12 @@ handle_one_xevent (struct x_display_info *dpyinfo, val->emacs_value += delta; if (mwheel_coalesce_scroll_events - && (fabs (val->emacs_value) < 1)) + && (fabs (val->emacs_value) < 1) + && (fabs (delta) > 0)) continue; bool s = signbit (val->emacs_value); - inev.ie.kind = (delta != 0.0 + inev.ie.kind = (fabs (delta) > 0 ? (val->horizontal ? HORIZ_WHEEL_EVENT : WHEEL_EVENT) @@ -10040,17 +10041,20 @@ handle_one_xevent (struct x_display_info *dpyinfo, XSETINT (inev.ie.y, lrint (xev->event_y)); XSETFRAME (inev.ie.frame_or_window, f); - inev.ie.modifiers = !s ? up_modifier : down_modifier; - inev.ie.modifiers - |= x_x_to_emacs_modifiers (dpyinfo, - xev->mods.effective); + if (fabs (delta) > 0) + { + inev.ie.modifiers = !s ? up_modifier : down_modifier; + inev.ie.modifiers + |= x_x_to_emacs_modifiers (dpyinfo, + xev->mods.effective); + } scroll_unit = pow (FRAME_PIXEL_HEIGHT (f), 2.0 / 3.0); if (NUMBERP (Vx_scroll_event_delta_factor)) scroll_unit *= XFLOATINT (Vx_scroll_event_delta_factor); - if (delta != 0.0) + if (fabs (delta) > 0) { if (val->horizontal) {