I like Emacs is that I can customize it to the way I want it. I can figure out inefficiencies in my daily workflows and processes and solve it using e-lisp. The inefficiency I tried to solve today, turned out to be interesting becase a) it was a very simple requirement that b) was a very non-general requirement and therefore difficult to find online, and c) turned out to have a fairly simple solution. Some of the ideas here can be useful in themselves.
First things first, my emacs setup is as follows: I use GNU screen, with a text-based emacs, I use emacsclient for speeding up things. I use rxvt-unicode as my X11 terminal emulator. When at work, I use the above. When at home, I do SSH to my work system, and run “screen -dR” and get back my entire system as it is. (Although it doesn’t matter for this post, but I’m sure some of you are curious: I use “C-z” as my Screen escape character.)
Now, the problem I’m trying to solve: I tend to use Caps Lock every now and then for SHOUTING_OUT_LOUD, and I sometimes forget to turn it off, and therefore I waste time backspacing every now and then. I need to solve this.
(Another digression for the hardcore Emacs fans out there: yes, I haven’t swapped CapsLock and Ctrl, and I don’t ever intend to. I use a ThinkPad keyboard on my desktop, and a ThinkPad keyboard on my — well, ThinkPad, which has a Ctrl key slightly to the right, which makes it very accessible. Really, I think swapping CapsLock and Ctrl is stupid.)
How would I solve this? I think the following works: I would like to change the color of my cursor when my CapsLock is turned on.
The cursor color
First let’s figure out how to change the cursor color. If I were using X11, I would’ve called set-cursor-color
and everything would look great. However, Emacs does not support changing the cursor color on a text terminal, and this is my first roadblock.
The reason it doesn’t support — as far as I understand — is that changing the terminal cursor color is a non-standard terminal capability. However rxvt-unicode and xterm do have this non-standard support. Try this from a urxvt:
echo -ne '\033]12;red\007'
You’d think we’re almost done, but this doesn’t yet solve my problem. Unfortunately GNU screen doesn’t support this escape sequence. But we can work around that: GNU screen has a [again, non-standard] escape sequence to send an escape sequence to the host terminal. So now, we have an escape sequence to change the cursor color from within GNU screen:
echo -ne '\033P\033]12;red\007\033\\'
It’s entirely possible that we can get GNU Screen to support the previous escape sequence directly by correctly playing with some termcaps and stuff like that, but I don’t have enough knowledge about those to do that, so let me just solve my own problem.
Finally, let’s write an e-lisp function to change the color of the cursor:
(defun noronha-set-cursor-color (color)
(send-string-to-terminal (concat "33P33]12;" color "0733\\")))
And now all we need to do is test if the CapsLock is on and off and … uh oh.
CapsLock
It turns out, that there’s no way of figuring out whether the CapsLock is on or off, from within a terminal. This is because the terminal does not send any information when the CapsLock key is pressed. What now?
But then Xorg does have information about the CapsLock key. For instance, on Debian/Ubuntu install x11-xkb-utils, and run “xkbvleds”. So it seems possible that running Emacs in X might provide some functionality for reading the CapsLock state. But that still wouldn’t solve my inefficiencies.
So here’s my solution: if we can keep an xkbvled kind of daemon running separately with the X display information, we can read off the CapsLock state from that. Here’s how to do that:
First: “apt-get source x11-xkb-utils” and patch the file xkbutils/xkbvleds.c
. What we’d like to do is, uncomment the code that displays the window, and add a small printf that displays the state of the CapsLock key in the event loop: “1” if it’s on, and “0” if it’s off. This is left as an exercise, it is not hard.
Now we have a program that displays the state of the CapsLock key everytime it changes. It does not need to continuously poll the X server and so runs efficiently.
Now we need e-lisp that reads this asynchronously. This turned out to be fun for me since it was the first time I was dealing with processes — and that too asynchronous processes from elisp.
;; my patched version of xkbvleds
(setq xkb-daemon-command "/home/noronha/builds/x11-xkb-utils-7.5+1/xkbutils/xkbvleds")
(setq xkb-process nil)
(defun noronha-set-caps-state (state)
(cond
((= state ?0)
(noronha-set-cursor-color "black"))
((= state ?1)
(noronha-set-cursor-color "blue"))))
(defun noronha-process-caps (process output)
(mapc 'noronha-set-caps-state (string-to-list output)))
(defun noronha-start-monitoring-caps (display)
(if xkb-process (delete-process xkb-process))
(let ((process-environment (cons (format "DISPLAY=%s" display) process-environment)))
(setq xkb-process (start-file-process "xkb-daemon" "xkb-daemon" xkb-daemon-command)))
(set-process-filter xkb-process 'noronha-process-caps))
(noronha-start-monitoring-caps ":0")
And, yes, it works.
(Finally we need to restart the xkbvleds program when we connect using “ssh -X” and then run screen. I haven’t done this yet, but that final step is simple: write a wrapper over screen that calls emacsclient and runs (noronha-start-monitoring-caps "$DISPLAY")
.)