;;; ultra-scroll.el --- Fast and smooth scrolling -*- lexical-binding: t; -*-
;; Copyright (C) 2023-2026  J.D. Smith

;; Author: J.D. Smith
;; URL: https://github.com/jdtsmith/ultra-scroll
;; Package-Requires: ((emacs "29.1"))
;; Package-Version: 20260501.1434
;; Package-Revision: c6decf7754ed
;; Keywords: convenience
;; Prefix: ultra-scroll
;; Separator: -

;; ultra-scroll is free software: you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation, either version 3 of the
;; License, or (at your option) any later version.

;; ultra-scroll is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;; General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:
;; ultra-scroll enables fast, smooth, jump-free scrolling for Emacs on
;; all builds that support varying pixel scroll delta event data.  On
;; emacs-mac it retains the swipe-to-scroll and pinch-out for tab
;; overview capabilities of that port.  On all ports, it can scroll
;; past images or other content taller than the window without issue.
;;
;; The strongly recommended scroll settings are:
;; 
;;  scroll-margin=0
;;
;; See also `pixel-scroll-precision-mode' in pixel-scroll.el.

;;; Code:
;;;; Requires
(require 'mac-win nil 'noerror)
(require 'pixel-scroll)
(require 'mwheel)
(require 'timer)

;;;; Customize
(defcustom ultra-scroll-mac-multiplier 1.
  "Multiplier for smooth scroll step for wheeled mice on emacs-mac.
This multiplies the fractional delta-y values generated by
regular mouse wheels by the value returned by
`frame-char-height'.  Increase it to increase scrolling speed on
such mice.  Note that some mice drivers emulate trackpads, and so
will not be affected by this setting.  Adjust scrolling speed
directly with those drivers instead."
  :group 'scrolling
  :type 'float)

(defcustom ultra-scroll-gc-percentage 0.67
  "Value to temporarily set `gc-cons-percentage'.
This is set on initial scrolling, and restored during idle
time (see `ultra-scroll-idle-time')."
  :type '(choice (const :value nil :tag "Disable") (float :tag "Fraction"))
  :group 'scrolling)

(defcustom ultra-scroll-idle-time 0.5
  "Idle time in sec after which to restore GC and related scroll parameters.
GC percentage is restored only if `ultra-scroll-gc-percentage' is non-nil."
  :type 'float
  :group 'scrolling)
(make-obsolete-variable 'ultra-scroll-gc-idle-time 'ultra-scroll-idle-time "0.4")

(defcustom ultra-scroll-hide-cursor t
  "Hide the cursor during scrolls after it reaches the window bounds.
Note that in versions prior to v0.6, this function took a time.  That
configuration is now set by `ultra-scroll-restore-time'."
  :type 'boolean
  :group 'scrolling)

(defcustom ultra-scroll-preserve-column t
  "Restore the column position after scroll completes.
Only takes effect if `ultra-scroll-restore-time' is
non-nil."
  :type 'boolean
  :group 'scrolling)

(defcustom ultra-scroll-push-mark t
  "Push the mark while scrolling.
If non-nil, push the position prior to scrolling as mark when point
leaves the window.  Normally the mark is only pushed if the last command
wasn't a scrolling command, so that multiple scrolling commands in
series, no matter the delay between them, will set only one mark at the
very beginning.  If this is set to the symbol `every', each new scroll
sequence will push the mark."
  :type '(choice (const :value nil :tag "Disable")
		 (const :value t :tag "Enable")
		 (const :value every :tag "On every new scroll"))
  :group 'scrolling)

(defcustom ultra-scroll-hide-functions '(hl-line-mode)
  "Functions to call when scrolling begins and ends.
The hook runs only when point reaches the window boundary (i.e. when
`window-point' changes).  This hook can also be used to specify modes to
disable temporarily during off-window scrolling.  Each function will be
called with a single argument: -1 when scrolling starts, and 1 when it
ends (see `ultra-scroll-restore-time' for the timing of
this).

If a member of this hook is a symbol ending in \"-mode\", the associated
mode function is only called if the symbol is bound and non-nil.  This
is to facilitate including mode functions directly, toggling them off
and on during scroll only if they are already active."
  :type 'hook
  :group 'scrolling)

(defcustom ultra-scroll-restore-time 0.25
  "Time in sec after scrolling ends to restore cursor, etc.
After scrolling begins and point leaves the initial window view,
`ultra-scroll' can optionally perform a number of different \"leave\"
functions.  These include:

1. hiding the cursor (`ultra-scroll-hide-cursor'),

2. saving the column position for later
   restoration (`ultra-scroll-preserve-column'),

3. pushing a mark at the position where scrolling began so you can
   easily return to it (`ultra-scroll-push-mark'), and

4. temporarily disabling other modes, or calling arbitrary start/end
   functions during long scrolls (`ultra-scroll-hide-functions')."
  :type '(float :tag "Time (s)")
  :group 'scrolling)

;;;; Leave-view actions (hide cursor, preserve column, etc.)
(defvar-local ultra-scroll--leave--timer nil)
(defvar-local ultra-scroll--leave-window-point-start nil)
(defvar-local ultra-scroll--leave-restore-functions nil)

(defun ultra-scroll--leave-restore (buf)
  "Restore BUF after scrolling point outside the initial window view.
See `ultra-scroll-restore-time' for details."
  (when (buffer-live-p buf)
    (with-current-buffer buf
      (run-hook-with-args 'ultra-scroll--leave-restore-functions 1)
      (kill-local-variable 'ultra-scroll--leave-restore-functions)
      (kill-local-variable 'ultra-scroll--leave--timer)
      (kill-local-variable 'ultra-scroll--leave-window-point-start))))

(defun ultra-scroll--restore-column (_v)
  "Attempt to restore the starting column position."
  (when-let* ((uss (window-parameter nil 'ultra-scroll--start-col)))
    (vertical-motion (cons (/ uss (frame-char-width)) 0))))

(defun ultra-scroll--leave (window)
  "Perform any configured leave actions in WINDOW if appropriate.
Leave actions happen only when the original window point has
changed (i.e. point has left the initial window view during scroll).
See `ultra-scroll-restore-time' for details on the available
actions."
  (if ultra-scroll--leave--timer	; leave actions have already run
      (timer-set-time ultra-scroll--leave--timer ; reschedule timer
		      (timer-relative-time
		       nil ultra-scroll-restore-time))
    (unless (eq (window-point window)	; not yet at window boundary
		ultra-scroll--leave-window-point-start)
      (setq ultra-scroll--leave--timer
	    (run-at-time ultra-scroll-restore-time nil
			 #'ultra-scroll--leave-restore
			 (window-buffer window)))
      
      ;; Cursor hiding
      (when ultra-scroll-hide-cursor
	(push
	 (cond
	  ((fboundp 'window-cursor-type)
	   (let ((orig (window-cursor-type window)))
	     (lambda (_v)
	       (set-window-cursor-type
		window
		(or (when-let
			((pending-type
			  (window-parameter window 'pending-cursor-type)))
		      (set-window-parameter window 'pending-cursor-type nil)
		      pending-type)
		    orig)))))
	       
	  ((local-variable-p 'cursor-type)
	   (let ((orig cursor-type))
	     (lambda (_v) (setq-local cursor-type orig))))
		 
	  (t (lambda (_v) (kill-local-variable 'cursor-type))))
	 ultra-scroll--leave-restore-functions)
	(if (fboundp 'set-window-cursor-type)
	    (set-window-cursor-type window nil)
	  (setq-local cursor-type nil)))

      ;; Schedule column restoration
      (when ultra-scroll-preserve-column
	(push #'ultra-scroll--restore-column
	      ultra-scroll--leave-restore-functions))

      ;; Push mark and remove window parameter
      (when-let* ((_ ultra-scroll-push-mark)
		  (pos (window-parameter window 'ultra-scroll--start-pos)))
	(push-mark pos 'nomsg)
	(set-window-parameter window 'ultra-scroll--start-pos nil))
    
      ;; Hide functions
      (run-hook-wrapped
       'ultra-scroll-hide-functions
       (lambda (fun)
	 (when (or (not (symbolp fun))
		   (not (string-suffix-p "-mode" (symbol-name fun)))
		   (and (boundp fun) (symbol-value fun)))
	   (push fun ultra-scroll--leave-restore-functions) ; schedule reenable
	   (funcall fun -1))				     ; disable
	 nil)))))

;;;; Other scroll begin/end config actions
(defvar ultra-scroll--gc-percentage-orig nil)
(defvar ultra-scroll--scroll-conservatively-orig nil)
(defvar ultra-scroll--timer nil)
(defun ultra-scroll--end-scroll ()
  "Reset GC variable and scroll settings during idle time."
  (when ultra-scroll--gc-percentage-orig
    (setq gc-cons-percentage ultra-scroll--gc-percentage-orig))
  (when (< ultra-scroll--scroll-conservatively-orig 100)
    (setq scroll-conservatively ultra-scroll--scroll-conservatively-orig))
  (setq ultra-scroll--timer nil))

(defsubst ultra-scroll--prepare-to-scroll (&optional window)
  "Prepare to scroll in WINDOW.
If not passed, WINDOW is the selected window.  Preparation includes
lifting GC percentage, setting scroll parameters, updating vscroll in
buffers displayed multiple times, and caching some information for
\"leave\" functions.  See `ultra-scroll-gc-percentage' to configuring
whether GC changes occur and the `gc-cons-percentage' level to set
temporarily."
  (unless ultra-scroll--timer  		; already scrolling
    ;; save window point
    (setq ultra-scroll--leave-window-point-start
	  (window-point window))

    ;; Update target column and saved mark position, but only if we're
    ;; not simply continuing with another scroll.
    (let ((non-scroll-p
	   (not (memq last-command
		      '( ultra-scroll ultra-scroll-mac
			 mac-mwheel-scroll
			 pixel-scroll-precision)))))
      ;; preserve target column
      (when (and ultra-scroll-preserve-column non-scroll-p)
	(set-window-parameter window 'ultra-scroll--start-col
			      (car (posn-x-y (posn-at-point)))))
      ;; Save potential push mark position
      (when (and ultra-scroll-push-mark
		 (or non-scroll-p (eq ultra-scroll-push-mark 'every)))
	(set-window-parameter window 'ultra-scroll--start-pos (point))))

    ;; Work around lag when same buffer has nonzero vscroll in another window (#32)
    (dolist (w (cdr (get-buffer-window-list (window-buffer window))))
      (set-window-vscroll w 0))

    ;; Update GC and scroll parameters during scroll
    (when ultra-scroll-gc-percentage
      (setq gc-cons-percentage
	    (max gc-cons-percentage ultra-scroll-gc-percentage)))
    (when (< scroll-conservatively 100)
      (setq ultra-scroll--scroll-conservatively-orig scroll-conservatively
	    scroll-conservatively 101))

    (setq ultra-scroll--timer
	  (run-with-idle-timer ultra-scroll-idle-time nil
			       #'ultra-scroll--end-scroll))))

;;;; Scroll
(defun ultra-scroll-down (delta)
  "Scroll the current window down by DELTA pixels.
DELTA should not be larger than the height of the current window."
  (let* ((initial (point))
	 (edges (window-edges nil t nil t))
	 (win-height (- (nth 3 edges) (nth 1 edges)))
	 (current-vs (window-vscroll nil t))
	 (off (+ (window-tab-line-height) (window-header-line-height)))
         (new-start (posn-point (posn-at-x-y 0 (+ delta off))))
	 (new-start-posn (and new-start (posn-at-point new-start))))
    (unless new-start-posn ; scroll delta could be larger than win height!
      (let ((last-start (or new-start 0)) quit)
	(while (and (> delta (- win-height off)) (not quit))
	  (setq new-start (posn-point (posn-at-x-y 0 (1- win-height))))
	  (if (<= new-start last-start)
	      (setq quit t)		; no progress being made
	    (setq new-start-posn (posn-at-point new-start)
		  last-start new-start
		  delta (- delta (or (cdr (posn-x-y new-start-posn)) 0)))
	    (set-window-start nil new-start)))))
    (when new-start
      (goto-char new-start)
      (unless (zerop (window-hscroll))
	(setq new-start (beginning-of-visual-line)))
      (if (>= (line-pixel-height) win-height)
	  ;; Jumbo line at top: just stay on it and increment vscroll
	  (set-window-vscroll nil (+ current-vs delta) t t)
	(if (eq new-start (window-start)) ; same start: just vscroll a bit more
	    (setq delta (+ current-vs delta))
	  (setq new-start-posn (posn-at-point new-start)
		delta (- delta (or (cdr (posn-x-y new-start-posn)) 0)))
	  (set-window-start nil new-start (not (zerop delta))))
	(set-window-vscroll nil delta t t)
	;; Avoid recentering
	(goto-char (posn-point (posn-at-x-y 0 off))) ; window-start may be above
	(if (zerop (vertical-motion 1))	; move down 1 line from top
	    (signal 'end-of-buffer nil))
	(if (> initial (point)) (goto-char initial))))))

(defun ultra-scroll-up (delta)
  "Scroll the current window up by DELTA pixels.
DELTA should be less than the window's height."
  (let* ((initial (point))
	 (edges (window-edges nil t nil t))
	 (win-height (- (nth 3 edges) (nth 1 edges)))
	 (win-start (window-start))
	 (current-vs (window-vscroll nil t))
	 (start win-start))
    (if (<= delta current-vs)	    ; simple case: just reduce vscroll
	(setq delta (- current-vs delta))
      ; Not enough vscroll: measure size above window-start
      (let* ((dims (window-text-pixel-size nil (cons start (- current-vs delta))
					   start nil nil nil t))
	     (pos (nth 2 dims))
	     (height (nth 1 dims)))
	(when (or (not pos) (eq pos (point-min)))
	  (signal 'beginning-of-buffer nil))
	(setq start (nth 2 dims)
	      delta (- (+ height current-vs) delta))) ; should be >= 0
      (unless (eq start win-start)
	(set-window-start nil start (not (zerop delta)))))
    (when (>= delta 0) (set-window-vscroll nil delta t t))
    
    ;; Position point to avoid recentering, moving up one line from
    ;; the bottom, if necessary.  "Jumbo" lines (taller than the
    ;; window height, usually due to images) must be handled
    ;; carefully.  Once they are within the window, point should stay
    ;; on the first tall object on the line until the top of the jumbo
    ;; line clears the top of the window, then immediately moved off
    ;; (above), via the full height character.  This is the only way to
    ;; avoid unwanted re-centering/motion trapping.
    (if (> (line-pixel-height) win-height) ; a jumbo on the line!
	(let ((end (max (point)
			(save-excursion
			  (end-of-visual-line)
			  (1- (point)))))) ; don't fall off
	  (when-let* ((pv (pos-visible-in-window-p end nil t))
		     ((and (> (length pv) 2) ; falls outside window
			   (zerop (nth 2 pv))))) ; but not at the top
	    (goto-char end) ; eol is usually full height
	    (goto-char start))) ; now move up
      (when-let* ((p (posn-at-x-y 0 (1- win-height))))
	(goto-char (posn-point p))
	(vertical-motion -1)
	(if (< initial (point)) (goto-char initial))))))

(defsubst ultra-scroll--scroll (delta window)
  "Scroll WINDOW by DELTA pixels (positive or negative)."
  (let (ignore)
    (unless (or (zerop delta)
		(and (setq ignore (window-parameter window 'ultra-scroll--ignore))
		     (or ; ignoring this window moving this direction
		      (and (eq (point) (car ignore))
			   (eq (cdr ignore) (< delta 0)))
		      (set-window-parameter window 'ultra-scroll--ignore nil))))
      (with-selected-window window
	(condition-case err
	    (if (< delta 0)
		(ultra-scroll-down (- delta))
	      (ultra-scroll-up delta))
	  ;; Do not ding at buffer limits.  Show a message instead (once!).
	  ((beginning-of-buffer end-of-buffer)
	   (let* ((end (eq (car err) 'end-of-buffer))
		  (p (if end (point-max) (point-min))))
	     (when ultra-scroll--leave-restore-functions (goto-char p))
	     (set-window-start window p)
	     (set-window-vscroll window 0 t t)
	     (set-window-parameter window 'ultra-scroll--ignore
				   (cons (point) end))
	     (message (error-message-string
		       (if end '(end-of-buffer) '(beginning-of-buffer)))))))
        (ultra-scroll--leave window)))))

(defun ultra-scroll (event &optional arg)
  "Smooth scroll EVENT.
EVENT and optional ARG are passed to `mwheel-scroll', unless
EVENT is a scrolling event."
  (interactive "e")
  (let ((delta (nth 4 event)))
    (if (not delta)
	(mwheel-scroll event arg)
      (let ((window (mwheel-event-window event)))
	(ultra-scroll--prepare-to-scroll window)
	(ultra-scroll--scroll (round (cdr delta)) window)))))

(declare-function mac-forward-wheel-event "mac-win")
(defun ultra-scroll-mac (event &optional arg)
  "Smooth scroll EVENT for emacs-mac.
EVENT and optional ARG are passed on to `mwheel-scroll', for any
events not handled here.  If swipe-tracking is enabled for
swipe-between-pages at the OS level, left-/right-swipe events
will be replayed for left/right touch ends."
  (interactive "e")
  (let ((ev-type (event-basic-type event))
	(plist (nth 3 event)))
    (if (not (memq ev-type '(wheel-up wheel-down)))
	(when (memq ev-type '(wheel-left wheel-right))
	  (if mouse-wheel-tilt-scroll
	      (mac-forward-wheel-event t 'mwheel-scroll event arg)
	    (when (and ;; "Swipe between pages" enabled.
		   (plist-get plist :swipe-tracking-from-scroll-events-enabled-p)
		   (eq (plist-get plist :momentum-phase) 'began))
	      ;; Post a swipe event when left/right momentum phase begins
	      (push (cons (event-convert-list
			   (nconc (delq 'click
					(delq 'double
					      (delq 'triple
						    (event-modifiers event))))
				  (if (eq (event-basic-type event) 'wheel-left)
				      '(swipe-left) '(swipe-right))))
			  (cdr event))
		    unread-command-events))))
      ;;  Note: emacs-mac encodes all scrolling information in the PLIST, as follows:
      ;;    trackpads:
      ;;      - `:scrolling-delta-x' and `:scrolling-delta-y' are set
      ;;        to pixel scroll amounts.
      ;;      - `:phase' is set to `began' on first scroll, then `changed'.
      ;;      - During momentum scroll, `:momentum-phase' is set to
      ;;        `began' then `changed', while `:phase' is `none'.
      ;;    some regular wheeled mice:
      ;;      - `:delta-x' and `:delta-y' are set to floating
      ;;        fractional line scroll amounts.
      ;;      - `:phase' is set to `began' on first scroll, then `changed'.
      ;;      - `:momentum-phase' is always `none'.
      (if-let* ((scroll-delta (plist-get plist :scrolling-delta-y)))
	  (let ((window (mwheel-event-window event))
		(delta (or scroll-delta
			   ;; regular non-touch scroll: fraction of a line
			   (* (plist-get plist :delta-y) (frame-char-height)
			      ultra-scroll-mac-multiplier))))
	    (ultra-scroll--prepare-to-scroll window)
	    (ultra-scroll--scroll (round delta) window))
	(mac-forward-wheel-event t 'mwheel-scroll event arg)))))

; scroll-isearch support
(put 'ultra-scroll 'scroll-command t)
(put 'ultra-scroll-mac 'scroll-command t)

;;;; Testing scroll data
(defun ultra-scroll-check (event-cnt)
  "Check and report on the scrolling event data your system provides.
This reads EVENT-CNT independent scroll events (default: 30) and checks
their PIXEL-DELTA values to see if they differ."
  (interactive "p")
  (let ((buf (get-buffer-create "*ultra-scroll-report*"))
	(nc (string-match "\\bNATIVE_COMP\\b" system-configuration-features))
	(inhibit-read-only t)
	(max-cnt (max event-cnt 30)) (cnt 1) deltas mac-basic ev tm)
    (message (concat "ultra-scroll: checking scroll data\n"
		     (format
		      "Scroll your mouse wheel or track-pad slow then fast to generate %d events"
		      max-cnt)))
    (while (and (setq ev (read-event)) (< cnt max-cnt))
      (when (memq (event-basic-type ev) '(wheel-up wheel-down))
	(unless tm (setq tm (current-time)))
	(message "Detected %2d/%2d wheel event%s" cnt max-cnt (if (> cnt 1) "s" ""))
	(cl-incf cnt)
	(if (featurep 'mac-win)
	    (let ((plist (nth 3 ev)))
	      (when (null plist)
		(error "Malformed wheel event detected! %s" ev))
	      (if-let* ((sdy (plist-get plist :scrolling-delta-y)))
		  (push sdy deltas)
		(if-let* ((dy (plist-get plist :delta-y)))
		    (progn
		      (push dy deltas)
		      (setq mac-basic t))
		  (error "Malformed wheel event detected!  %s" ev))))
	  (if-let* ((pix-delta (nth 4 ev)))
	      (push (cdr pix-delta) deltas)
	    (error "Malformed wheel event detected!  %s" ev)))))
    (setq tm (float-time (time-since tm)))
    (with-current-buffer buf
      (erase-buffer)
      (help-mode)
      (insert "  == ultra-scroll Scroll Event Report ==\n\n")
      (insert "* " (emacs-version) "\n* " (if nc "" "No ") "Native Comp Detected"
	      (if nc "\n" " (use native-comp for fastest scrolling performance)\n")
	      "\n")
      (when (and (featurep 'x) (not (featurep 'xinput2)))
	(insert " *** WARNING: Emacs on Linux/X11 must be compiled --with-xinput2\n"))
      (insert (format " ** %s scroll events%s detected in %0.2fs (%0.1f events/s)\n" cnt
		      (if mac-basic " [Mac basic mouse]" "") tm (/ (float cnt) tm)))
      (if (cl-every (lambda (x) (= (abs x) (abs (car deltas)))) deltas)
	  (insert (format " *** WARNING, all pixel scroll values == %0.2f " (car deltas))
		  "No real pixel scroll data stream?\n"
		  " ** (try again, or use pixel-scroll-precision instead)\n")
	(let* ((deltas (mapcar #'abs deltas))
	       (mean (/ (cl-reduce #'+ deltas ) max-cnt))
	       (min (apply #'min deltas))
	       (max (apply #'max deltas)))
	  (insert (format " *** %s pixel scroll data: %0.1f to %0.1f (%0.2f mean)\n"
			  (if mac-basic "Mac line-based" "Normal") min max mean)))))
    (display-buffer buf)))

;;;; Mode
;;;###autoload
(define-minor-mode ultra-scroll-mode
  "Toggle pixel precision scrolling.
When enabled, this minor mode scrolls the display precisely using
full trackpad or modern mouse capabilities.  It correctly scrolls
past images taller than the window height.  The mode enables
`pixel-scroll-precision-mode-map', overriding that mode's scroll
command, but other mode features, including interpolated page
scrolling, still function (if enabled).

Note that `ultra-scroll' does NOT do any interpolation of scroll
wheel data, and is intended for use with mouse/trackpad hardware
on systems providing pixel-level scroll data; see
`ultra-scroll-check' to investigate what kind of scrolling data
your system and hardware provide."
  :global t
  :group 'scrolling
  (pixel-scroll-precision-mode (if ultra-scroll-mode 1 -1)) ;; reuse
  (cond
   (ultra-scroll-mode
    (unless (= scroll-margin 0)
      (warn "ultra-scroll: scroll-margin = 0 is required for glitch-free smooth scrolling"))
    (when (and (featurep 'x) (not (featurep 'xinput2)))
      (warn "ultra-scroll: Emacs on Linux/X11 must be compiled --with-xinput2"))
    (define-key pixel-scroll-precision-mode-map [remap pixel-scroll-precision]
		(if (featurep 'mac-win) #'ultra-scroll-mac #'ultra-scroll))
    (setf (get 'pixel-scroll-precision-use-momentum 'us-orig-value)
	  pixel-scroll-precision-use-momentum)
    (setq pixel-scroll-precision-use-momentum nil)
    ;; pixel-scroll-precision after emacs v30 turns this off.  We
    ;; don't need to do so for scrolling, but a bug in half-visible
    ;; cursors lead to 150-400x slowdown in redisplay; see #32.
    (setq-default make-cursor-line-fully-visible nil)
    (setq ultra-scroll--gc-percentage-orig gc-cons-percentage
	  ultra-scroll--scroll-conservatively-orig scroll-conservatively))
   (t
    (define-key pixel-scroll-precision-mode-map [remap pixel-scroll-precision] nil)
    (setq pixel-scroll-precision-use-momentum
	  (get 'pixel-scroll-precision-use-momentum 'us-orig-value)))))

(provide 'ultra-scroll)
;;; ultra-scroll.el ends here

