changing the cursor color and shape dynamically

When typing some text, it's often useful to know what mode we're in – are we in overwrite-mode, in read-only-mode, or in normal insert-mode. The information is available in the mode-line – but wouldn't it be nicer to get some small visual cue, for example by changing the cursor color or style?

That indeed is possible. There are some existing ways to do this, described in EmacsWiki. However, I want to be able to control both the cursor color and cursor shape, and also distinguish between overwrite, read-only and 'normal' mode. Below is my attempt.

By putting the following snippet in your .emacs, the cursor becomes a yellow vertical bar during normal mode, it becomes a red block when you're in overwrite-mode and it becomes a gray vertical bar when you're in read-only mode.

;; Change cursor color according to mode; inspired by
;; http://www.emacswiki.org/emacs/ChangingCursorDynamically
(setq djcb-read-only-color       "gray")
;; valid values are t, nil, box, hollow, bar, (bar . WIDTH), hbar,
;; (hbar. HEIGHT); see the docs for set-cursor-type

(setq djcb-read-only-cursor-type 'hbar)
(setq djcb-overwrite-color       "red")
(setq djcb-overwrite-cursor-type 'box)
(setq djcb-normal-color          "yellow")
(setq djcb-normal-cursor-type    'bar)

(defun djcb-set-cursor-according-to-mode ()
  "change cursor color and type according to some minor modes."

      (set-cursor-color djcb-read-only-color)
      (setq cursor-type djcb-read-only-cursor-type))
      (set-cursor-color djcb-overwrite-color)
      (setq cursor-type djcb-overwrite-cursor-type))
      (set-cursor-color djcb-normal-color)
      (setq cursor-type djcb-normal-cursor-type))))

(add-hook 'post-command-hook 'djcb-set-cursor-according-to-mode)

You can change the colors and cursor types by modifying the various variables.

I should probably turn this into a proper minor mode, but for now this seems to work well.


vinhdizzo said...
This comment has been removed by the author.
Oscar said...

Using Emacs 23.1 on Windows XP and your above code, my block-cursors are transparent with a colored border (ie, when the block is supposed to be red, it becomes a white block with red borders) - why is that? :(

Anyone else experiencing the same issue?

Anonymous said...

What window theme is this? It looks very nice and clean

Tassilo said...

Hey, nice! Now I change the cursor type depending if a region is active. If so, I like the bar cursor better than the box one.

Here's the code:

(defun th-activate-mark-init ()
(setq cursor-type 'bar))
(add-hook 'activate-mark-hook 'th-activate-mark-init)

(defun th-deactivate-mark-init ()
(setq cursor-type 'box))
(add-hook 'deactivate-mark-hook 'th-deactivate-mark-init)

Anonymous said...

Cool ideas! I think, I'll stick with a red bar in default mode.

@oscar: replacing 'block by 'box in the code works for me. I'm using the same version of emacs - maybe they changed the naming.

djcb said...

@Oscar, @lifealgorithms: arghh... my usual last-minute error... indeed 'block should be 'box

@Anonymous: it's the 'New Wave' theme.

Pete said...

I like it. It's part of my .emacs file now.

vinhdizzo said...

this is indeed nice -- visualization communication of the editing mode.

i'm on mac os x with emacs 23.1 built from source. do u guys have this issue after using djcb's code?

hold C-n or down. the cursor doesn't move down slowly (u can't see it go line by line). u see it flashing at one line, then skip MULTIPLE LINES. this is so unappealing to my eyes that i had to remove this feature.

can u guys replicate this? thanks.

djcb said...

@vinhdizzo: if you see the code on emacswiki (linked in the article), the snippet there actually caches the current color (ie., only set it if it has changed). I found that unnecessary, (C-n is smooth here) but maybe it's still needed on MacOS?

vinhdizzo said...

@djcb i think all those conditionals is causing the slowness in the cursor for me. following your advice to the emacswiki page, i modified the code a little to change cursor-type (don't care about color, i care about type) for anyone that is interested:

(defvar hcz-set-cursor-type-type t)
(defvar hcz-set-cursor-type-buffer t)
(defun hcz-set-cursor-type-according-to-mode ()
"change cursor type according to some minor modes."
;; set-cursor-color is somewhat costly, so we only call it when needed:
;; setq cursor-type is somewhat costly, so we only call it when needed:
(let ((type
(if buffer-read-only 'hbar
(if overwrite-mode 'hollow
(unless (and
(string= type hcz-set-cursor-type-type)
(string= (buffer-name) hcz-set-cursor-type-buffer))
;;(set-cursor-color (setq hcz-set-cursor-color-color color))
(setq cursor-type (setq hcz-set-cursor-type-type type))
(setq hcz-set-cursor-type-buffer (buffer-name)))
(add-hook 'post-command-hook 'hcz-set-cursor-type-according-to-mode)

djcb said...

@vinhdizzo: ah, does this make things faster for you? I couldn't see any noticeable difference.

vinhdizzo said...

@djcb yes, it takes away that slowness / not smooth part in your code. could be a mac os x thing then...no idea.

sinewalker said...

Hi, I'm trying out Emacs 24 early release (Windows build from Google Code - http://code.google.com/p/emacs-for-windows/updates/list ). It's using the hbar cursor shape and it seems I can't get back to block/hollow no matter what I try.

I find the X11 style block/hollow to be much easier to see and read than the little bar, so I'd like to get the block back. I even tried this code but it's not having any effect.

Has anyone played with this yet? Should I report a bug or is it a feature?

sinewalker said...

actually, just tried in Emacs 23.3.1 for Windows and it doesn't work there either. So must be something weird in my setups. Never mind...

Farmario said...

when i use this, follow-mode doesn't work anymore, i can't understand why, any suggestion?
(emacs 24)

Edgar Thier said...

What about C-x C-+ ? Per default it is bound to text-scale-adjust which apparently does exactly what you want.