Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

rework commands in light of long cache refresh time

The issue is that of get-iplayer itself managing its cache: if at any
point it decides that the cache is sufficiently stale, it goes off and
refreshes everything, which seems to take a long time -- if I'm
unlucky, breaking my ERC sessions (and in any case making the rest of
the editor session unusable).

Construct a background process and sentinel to manage the cache
updating before calling the real get-iplayer command.  The tricky bit
here is to make sure that the sentinel and real body get executed in
the right context.  Many things in the world are simpler with lexical
scoping; this would have been one of them, but I am still on emacs 23
and lexical binding is a thing of the future, so instead of making a
bunch of thunks to be called later, we have to save commands (well,
key sequences), but then those key sequences are divorced from the
window and frame that they were originally typed into, so we have to
get that context back.  Naive ways of doing that turn out not to work,
because execute-kbd-macro runs its own command loop, which first of
all selects a current frame and window, so if we've navigated away
from the *iplayer* window/buffer we end up adding keystrokes to
e.g. an ERC buffer, which is suboptimal; however, (ab)using the pre
and post command hooks seems to make things work.  So do that, and all
is well.
  • Loading branch information...
commit 561c68511a2aa272c658192b6e1c08fe666196ed 1 parent d78f76f
Christophe Rhodes authored

Showing 1 changed file with 73 additions and 3 deletions. Show diff stats Hide diff stats

  1. +73 3 iplayer.el
76 iplayer.el
... ... @@ -1,3 +1,66 @@
  1 +(defvar iplayer-updating-cache-process nil)
  2 +(defvar iplayer-updating-cache-sentinel-info nil)
  3 +(defvar iplayer-updating-cache-sentinel-executing nil)
  4 +
  5 +(defun iplayer-updating-cache-sentinel (process event)
  6 + ;; FIXME: assumes that all went well
  7 + (let* ((iplayer-updating-cache-sentinel-executing t)
  8 + (info (reverse iplayer-updating-cache-sentinel-info)))
  9 + (setq iplayer-updating-cache-process nil
  10 + iplayer-updating-cache-sentinel-info nil)
  11 + (dolist (info info)
  12 + (let ((iplayer-command-frame (car info))
  13 + (iplayer-command-window (cadr info))
  14 + (iplayer-command-buffer (caddr info))
  15 + (keys (car (cdddr info))))
  16 + (when (and (frame-live-p iplayer-command-frame)
  17 + (window-live-p iplayer-command-window)
  18 + (buffer-live-p iplayer-command-buffer))
  19 + (let ((old-frame (selected-frame))
  20 + (old-window (selected-window))
  21 + (old-buffer (current-buffer)))
  22 + (let ((pre-command-hook
  23 + (lambda ()
  24 + (select-frame iplayer-command-frame)
  25 + (select-window iplayer-command-window)
  26 + (set-buffer iplayer-command-buffer)))
  27 + (post-command-hook
  28 + (lambda ()
  29 + (select-window old-window)
  30 + (select-frame old-frame)
  31 + (set-buffer old-buffer))))
  32 + ;; KLUDGE: execute-kbd-macro executes a normal
  33 + ;; command-loop, whose first action is to select the
  34 + ;; current frame and window, which is why we contort
  35 + ;; things to select the frame/window/buffer we actually
  36 + ;; want in pre-command-hook. I'm actually surprised
  37 + ;; that it works, but mine is not too much to reason
  38 + ;; why; lots of other ways to try to achieve this didn't
  39 + ;; in fact work.
  40 + (execute-kbd-macro keys))))))
  41 + (message "Done updating iPlayer cache")))
  42 +
  43 +(defmacro define-iplayer-command (name arglist &rest body)
  44 + (let (docstring interactive)
  45 + (when (stringp (car body))
  46 + (setq docstring (car body) body (cdr body)))
  47 + (when (and (consp (car body)) (eql (caar body) 'interactive))
  48 + (setq interactive (car body) body (cdr body)))
  49 + `(defun ,name ,arglist
  50 + ,@(when docstring (list docstring))
  51 + ,@(when interactive (list interactive))
  52 + (unless iplayer-updating-cache-process
  53 + (setq iplayer-updating-cache-process
  54 + (start-process "updating-iplayer" " *updating-iplayer*"
  55 + "get-iplayer" "--type" "radio,tv" "-q"))
  56 + (set-process-sentinel iplayer-updating-cache-process
  57 + 'iplayer-updating-cache-sentinel)
  58 + (message "Updating iPlayer cache"))
  59 + (if iplayer-updating-cache-sentinel-executing
  60 + (progn ,@body)
  61 + (push (list (selected-frame) (selected-window) (current-buffer) (this-command-keys))
  62 + iplayer-updating-cache-sentinel-info)))))
  63 +
1 64 (defun get-iplayer-tree (&rest args)
2 65 (with-temp-buffer
3 66 (apply #'call-process "get-iplayer" nil t nil "--nocopyright" "--type" "radio,tv" "--tree" "--terse" args)
@@ -54,9 +117,15 @@
54 117 ("%" . "BBC Radio 5 live")
55 118 ("^" . "BBC 6 Music")
56 119 ("&" . "BBC 7")
57   - ("*" . "BBC Radio 4 Extra")))
  120 + ("*" . "BBC Radio 4 Extra"))
  121 + "Alist mapping keys to iPlayer channels.
  122 +
  123 +Used in the `iplayer-preset' command.")
  124 +
  125 +(define-iplayer-command iplayer-preset (&optional prefix)
  126 + "Switch display to a preset channel.
58 127
59   -(defun iplayer-preset (&optional prefix)
  128 +The presets are defined in the variable `iplayer-presets'."
60 129 (interactive "p")
61 130 (let ((keys (this-command-keys))
62 131 (presets (mapcar (lambda (x) (cons (read-kbd-macro (car x)) (cdr x))) iplayer-presets)))
@@ -101,7 +170,8 @@
101 170 (use-local-map iplayer-mode-map)
102 171 (setq major-mode 'iplayer-mode mode-name "iPlayer"))
103 172
104   -(defun iplayer ()
  173 +(define-iplayer-command iplayer ()
  174 + "Start the emacs iPlayer interface."
105 175 (interactive)
106 176 (setq mode-line-process nil)
107 177 (display-iplayer-tree (get-iplayer-tree)))

0 comments on commit 561c685

Please sign in to comment.
Something went wrong with that request. Please try again.