Skip to content

Instantly share code, notes, and snippets.

@nilninull
Last active June 18, 2023 23:36
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nilninull/63c821c435935344ab66a2006483794a to your computer and use it in GitHub Desktop.
Save nilninull/63c821c435935344ab66a2006483794a to your computer and use it in GitHub Desktop.
A function for add a multi-click feature to xbindkeys
(use-modules (srfi srfi-19))
;; 100000000 nano seconds -> 0.1s
;; 200000000 nano seconds -> 0.2s
;; 300000000 nano seconds -> 0.3s
;; 400000000 nano seconds -> 0.4s
;; 500000000 nano seconds -> 0.5s
;; 1000000000 nano seconds -> 1s
;; This value is used for judge `clicking are continuously or not'
(define click-separation-threshold-nanosec 250000000)
(define (time-to-nanosecond time)
(+ (* (time-second time)
(expt 10 9))
(time-nanosecond time)))
(define (emit-command k commands)
(let ((cmd (assoc k commands)))
(if cmd
(eval (cdr cmd) (interaction-environment)))))
(define (multi-click-key key commands-normal . commands-long)
(let ((time (current-time))
(count 0))
(unless (list? key)
(set! key (list key)))
(set! commands-long (if (null? commands-long)
#f
(begin
(set! commands-long (car commands-long))
(if (eq? #t commands-long)
commands-normal
commands-long))))
(xbindkey-function key (lambda ()
(let ((prev-time time))
(set! time (current-time))
(if (> click-separation-threshold-nanosec (time-to-nanosecond (time-difference time prev-time)))
(set! count (+ count 1))
(set! count 1)))))
(xbindkey-function (cons 'release key) (lambda ()
(let ((diff-time (time-to-nanosecond (time-difference (current-time) time))))
(if (> click-separation-threshold-nanosec diff-time)
(call-with-new-thread (let ((start-time-count count))
(lambda ()
(usleep (/ (- click-separation-threshold-nanosec diff-time) 1000))
(if (= count start-time-count)
(emit-command start-time-count commands-normal)))))
(when commands-long
(emit-command count commands-long))))))))
;; some example settings
;; simple multi click settings
(multi-click-key 'b:10 '((1 . (run-command "xmessage single click"))
(2 . (run-command "xmessage double click"))
(3 . (run-command "xmessage triple click"))
(4 . (run-command "xmessage you can use 4 or more clicks"))))
;; the key can specify with modifier keys
(multi-click-key '(control b:10) '((1 . (run-command "xmessage single click with modifier keys"))))
;; second alist can define long hold click maps.
;; `long hold' means `last one click holds longer than click-separation-threshold-nanosec time'
(multi-click-key 'b:11 '((1 . (run-command "xmessage single click"))
(2 . (run-command "xmessage double click"))
(3 . (run-command "xmessage triple click"))
(4 . (run-command "xmessage you can use 4 or more clicks")))
'((1 . (run-command "xmessage long hold single click"))
(2 . (run-command "xmessage long hold double click (long hold click is only last one click)"))
(3 . (run-command "xmessage long hold triple click (long hold click is only last one click)"))))
;; If second alist is #t, normal click and long hold click use same mapping.
(multi-click-key 'b:12 '((1 . (run-command "xmessage single click"))
(2 . (run-command "xmessage double click"))
(3 . (run-command "xmessage triple click"))
(4 . (run-command "xmessage you can use 4 or more clicks")))
#t)
@FareesHussain
Copy link

I'm having some trouble understanding this.
Can you create one such that single click gives (ctrl + f) and double click gives(ctrl + s) for a button

@nilninull
Copy link
Author

I think you can emulate key presses with the xdotool app.

(multi-click-key 'b:1 '((1 . (run-command "xdotool key 'ctrl+f'"))
                        (2 . (run-command "xdotool key 'ctrl+s'")))
                 #t)

Please change the number part of b:1 to the corresponding mouse button number.

@222Phoenix
Copy link

222Phoenix commented Jun 16, 2023

Thanks for this code.

I'm trying to configure right click to be a normal right click, and double right click to be ctrl+shift+t to reopen a closed tab. This code should work but doesn't:

(multi-click-key 'b:3  '((1 . (run-command "xdotool click 3"))
                         (2 . (run-command "xdotool key 'ctrl+shift+t'"))

If I substitute click 3 for click 1 (left click) or click 2 (middle click), it works fine. Any ideas?

@nilninull
Copy link
Author

nilninull commented Jun 18, 2023

Considering the last line of your comment, I think `click 3' is looping.

The event caused by xdotool click 3 is once again being captured by xbindkeys process.

Combining the ungrab-all-keys and grab-all-keys functions may work well.

sample code

(multi-click-key 'b:3  '((1 . (begin
				(ungrab-all-keys)
				(run-command "xdotool click 3")
				(grab-all-keys)))
                         (2 . (run-command "xdotool key 'ctrl+shift+t'"))))

I am currently using Wayland, so the above code is unconfirmed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment