I need to execute a function every time a window scrolls. I mean the function to be executed at point "after" the window has been scrolled (with C-v, M-v or with the <down> key). I tried:

(add-hook 'window-scroll-functions 'my-function)

but it didn't work (emacs froze until C-g). How can I achieve it?

Additional details. My goal is to sync the Emacs window that contains some LaTeX code with the PDF viewer (Okular). I know I can do it with AUCTeX but, for some reasons, I can't use it.

I imagined a solution like:

(defun my-sync-scroll-up ()
  (interactive)
  (scroll-up-command)
  (my-function))

(global-set-key (kbd "C-v") 'my-sync-scroll-up)

rebinding the C-v shortcut to my new function but I'd prefer something more generalized and automatic.

The function I want to run after window scrolls is:

(defun okular-forward-search-mod ()
  (interactive)
  (setq PID (number-to-string (emacs-pid)))
  (call-process-shell-command
   (concat "okular --unique file:"
       (concat (file-name-sans-extension (buffer-file-name)) ".pdf")
       "#src:"
       (number-to-string (line-number-at-pos (point)))
       (buffer-file-name))
   )
  (call-process-shell-command (concat "wmctrl -ia $(wmctrl -lp | awk -vpid=$PID '$3=="
                      PID
                      " {print $1; exit}')"))
)
(add-hook 'window-scroll-functions 'okular-forward-search-mod)

On Ubuntu 16.04, Emacs 25.3.2.

  • 3
    If you provide additional details to explain the goal of my-function/forward-search and a minimal working example, then it may be easier for forum participants to suggest a better approach. Be aware that the window-scroll-functions hook may be called more than once per command loop, depending upon what is happening in the buffer and the user settings of where point should be when the command loop ends. E.g, a use may have customized scroll-conservatively to a number greater than 100; etc. Emacs scrolls and redisplay asks itself whether that is good enough; if not, Emacs scrolls ... – lawlist Nov 26 at 22:13
  • @lawlist I added the additional details you requested. I hope it can help. – Gabriele Nicolardi Nov 27 at 14:42
  • Does my-function attempt to scroll the window? The doc-string for the variable window-scroll-functions says: "Warning: Do not use this feature to alter the way the window is scrolled. It is not designed for that, and such use probably won’t work." – lawlist Nov 27 at 16:51
  • @lawlist no it does not attempt to scroll the window. – Gabriele Nicolardi Nov 27 at 22:09
  • Given your goal, I suggest using a timer that fires up after 1 second and only does something if the window has scrolled, plus a hook on window visibility changes. This way you won't slow down repeated scrolling commands. – Gilles Dec 5 at 20:18

Following the Gilles' suggestions I solved using an idle timer (see also Toggle timer function).

Here's the code I tested:

You need to run this command on the terminal:

okular --unique file.pdf ; emacs file.tex 

This is the elisp stuff:

(defun okular-forward-search-mod ()
  (interactive)
  (setq PID (number-to-string (emacs-pid)))
  (call-process-shell-command
   (concat "okular --unique file:"
       (concat (file-name-sans-extension (buffer-file-name)) ".pdf")
       "#src:"
       (number-to-string (line-number-at-pos (point)))
       (buffer-file-name))
   )
  (call-process-shell-command (concat "wmctrl -ia $(wmctrl -lp | awk -vpid=$PID '$3=="
                      PID
                      " {print $1; exit}')"))
  )

(defun sync-viewer-when-window-scrolls ()
   (interactive)
   (when (string= latex-contents-buffer-name (buffer-name))
     (unless (= latex-contents-window-start (window-start))
        (setq latex-contents-window-start (window-start))
    (okular-forward-search-mod)))
   )

(defvar forward-search-timer nil)

(defun toggle-sync-with-viewer () 
  (interactive)
  (unless (boundp 'latex-contents-window-start)
    (defvar latex-contents-window-start (window-start)))
  (unless (boundp 'latex-contents-buffer-name)
    (defvar latex-contents-buffer-name (buffer-name)))

  (if (not (timerp forward-search-timer))
      (progn
    (okular-forward-search-mod)    
    (setq forward-search-timer 
          (run-with-idle-timer 1 1 'sync-viewer-when-window-scrolls)))
    (cancel-timer forward-search-timer)
    (setq forward-search-timer nil)))

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.