Elisp, CapsLock, X11, and total awesomeness.

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").)

About these ads

8 Responses to Elisp, CapsLock, X11, and total awesomeness.

  1. S says:

    Epic. But you know you could just go back a few words and hit M-l, right? :-) (Or write a function to downcase the last few words… or even, as OSX does, have an option of detecting wHEN yOU tYPE lIKE tHIS, and turn Caps Lock off.)

    (As to swapping Capslockā€¦ my left-hand little finger really used to hurt before the change. Maybe you don’t need it on a Thinkpad keyboard, but on mine, it helps. :P My Screen escape is ` so that I don’t have to hit Ctrl.)

    • Arnie says:

      yeah, I thought of the downcase-previous-word, but then I might have a CamelCase, and so it gets complicated. I also thought of a upcase-previous-word and get rid of caps-lock altogether (type the word first and then upcase it) but I wasn’t sure if that’ll work very well when correcting errors.

      Btw, after writing this post, I realized the actual solution that this implies is even more simpler, just that I happened to start with elisp and then found out the roadblocks. Since this effects the entire terminal outside GNU screen, I might as well keep a daemon running before I even start GNU screen: that would be far more simpler and would behave identically. (e.g. I get the colored cursor for CapsLock even on a shell, which happens here too.)

    • Arnie says:

      Btw, your Ctrl key is in the same position as my thinkpad’s. :)

      Apparently, I have a strong pinky.

    • S says:

      Well, I don’t have a Ctrl key on the right. :-(

      BTW, you could also do a reversecase-previous-word(s), which would take care of camelCase and whatever else Caps Lock may have caused.

  2. bravo. ur such a geek :) (in good spirit :D)

  3. estragib says:

    I have my shell cursor change color depending on input mode and was wondering why it didn’t work in screen. Thanks for the info on how to make screen pass on escape sequences. That’s one ugly workaround conditional less in my rc.

  4. [...] if you need a way to know about capslock state ; http://arniealmighty.wordpress.com/2…l-awesomeness/ [...]

  5. i wana learn shell scripting can anyone tell how i can learn. is any site is available where i get some details about that

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: