1

I'm writing a function to enable binding a "double tap" on a key (or one key then another key in rapid succession) to a function.

It almost works, but not quite. Here is the code I have in my lisp evaluation test chamber:

(setq doubletap-secondtap nil)

(defun doubletap (doubletap-key1 doubletap-key2 doubletap-wait doubletap-function)
  (let* ((doubletap-secondtap 't)
         (doubletap-event (read-event nil nil doubletap-wait)))
           (if doubletap-event
             (if (equal doubletap-event doubletap-key2)
                 (funcall doubletap-function)
                 (setq unread-command-events
                   (append (list doubletap-key1 doubletap-event) unread-command-events)) )
           (setq unread-command-events
             (append (list doubletap-key1) unread-command-events)) )))

(defun doubletap-j-j-godmodeall () (interactive)
       (if doubletap-secondtap
         (funcall 'self-insert-command 1)
         (doubletap 106 106 0.3 'god-mode-all)))

(funcall 'doubletap-j-j-godmodeall)

(global-set-key (kbd "j") 'doubletap-j-j-godmodeall)

(global-set-key (kbd "j") 'self-insert-command)

The first function is intended to do the following:

wait doubletap-wait seconds for input

then

  • if input is not given, simulate a keypress of doubletap-key1
  • if input is given and it is not doubletap-key2, simulate two keypresses, first doubletap-key1 then doubletap-key2
  • if input is given and it is doubletap-key2, call doubletap-function

Because we use plan to bind doubletap-key1 to call the interactive function doubletap-j-j-godmodeall, we make a flag doubletap-secondtap to tell doubletap-j-j-godmodeall whether we are already inside the first function. If the flag is nil, it will call doubletap, but if it has a value, it will perform the default action self-insert-command.

doubletap-j-j-godmodeall works perfectly when it isn't bound to anything, as can be seen by evaluating the first three expressions and then evaluating the fourth expression, which is designed to activate god-mode for all buffers when two "j"s are pressed within 0.3 seconds, but otherwise behave like a normal function.

(this will only work if you have god-mode installed; if you don't, try a different function)

Unfortunately, when we evaluate the fifth expression, we find that its behavior becomes bizarre and unexpected. It hangs indefinitely until you either cancel it with "C-g" or press "j". If you press "j," it will toggle god-mode (even if you do it well after 0.3 seconds have passed).

Evaluate the last expression to restore normal behavior.

Can anyone explain this behavior, and more importantly, does anyone know how to make the function work correctly?

7
  • Thanks for fixing the formatting. I'll endeavor to figure out how to do it correctly next time.
    – Zorgoth
    Dec 14, 2015 at 5:11
  • I'm editing the function to a cleaner version that still has the same bug.
    – Zorgoth
    Dec 14, 2015 at 5:33
  • 5
    Have you seen key-chord-mode? It supports double-tapped keys.
    – PythonNut
    Dec 14, 2015 at 5:52
  • Thanks! That solves my practical problem. I'd still appreciate it if anyone could tall me what is wrong with my code, however.
    – Zorgoth
    Dec 14, 2015 at 6:10
  • 3
    You're putting j back onto the unread events list before you exit, so you just keep looping through the handler for that.
    – phils
    Dec 14, 2015 at 6:55

1 Answer 1

1

Based on phils' comment, this is what I needed to do to make it work:

(setq doubletap-flag nil)

(defun doubletap (doubletap-key1 doubletap-key2 doubletap-wait doubletap-function)
  (setq doubletap-flag 't)
   (let ((doubletap-event (read-event nil nil doubletap-wait)))
     (if doubletap-event
       (if (equal doubletap-event doubletap-key2)
         (progn (setq doubletap-flag nil) (funcall doubletap-function))
       (setq unread-command-events
             (append (list doubletap-key1 doubletap-event) unread-command-events)) )
     (setq unread-command-events
           (append (list doubletap-key1) unread-command-events)) )))

(defun doubletap-j-j-godmodeall () (interactive)
       (if doubletap-flag
         (progn (setq doubletap-flag nil) (funcall 'self-insert-command 1))
         (doubletap 106 106 0.3 'god-mode-all)))

(global-set-key (kbd "j") 'doubletap-j-j-godmodeall)

You'd need to make a second interactive function bound with a mode hook if you wanted to be able to leave god-mode the same way, since god-mode will map "j" to "C-j". I'm not going to implement this since key-chord-mode is a better solution to that practical problem, as noted by PythonNut. I'll note that I think the default delay times for key-chord-mode are too short. You can change them by setting the values of key-chord-one-key-delay and key-chord-two-key-delay.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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