Colored nicks for rcirc.


(There’s also a bigger screenshot.)


To install, put it in your LoadPath and add the following to your InitFile:

    (eval-after-load 'rcirc '(require 'rcirc-color))

Determining the colors available in your init file

You can set your favorite colors manually:

    (setq rcirc-colors '("lemon chiffon" "lavender" "cornflower blue"))

Now new nicks will get assigned one of the three colors at random.

Manually changing colors using /color

You can change the colors assigned to nicks using /color NICK COLOR. A list of available color names is available via ‘M-x list-colors-display’.

You can also use hex codes of the form #xxx or #xxxxxx to refer to a color: #000 is black, #f00 is red, #0f0 is green, #00f is blue, and #fff is white. Using three hex digits allows you to refer to 256 different colors, using six hex digits allows you to refer to 65536 colors. Whether your display can actually display these colors depends on your display. If you’re using Emacs in a terminal emulator or on the console, chances are that you’re limited to 16 colors anyway.

Disabling automatic colors in your init file

If you never want automatic colors, set ‘rcirc-colors’ to nil:

    (setq rcirc-colors nil)

Only the colors you assign using /color NICK COLOR will take effect.

Defining groups with certain colors in your init file

You need to disable the automatic color picking, and you need to initialize the mapping table. The alist you see in the code has elements with a color as the key and a list of nicks as the value.

    (setq rcirc-colors nil
          rcirc-color-mapping (make-hash-table :test 'equal))
    (dolist (group '(("red" "forcer" "kilobug")
		     ("green" "kensanata")
                     ("gray" "fsbot" "birny" "lisppaste" "specbot")))
      (dolist (nick (cdr group))
	(puthash nick (cons 'foreground (car group)) rcirc-color-mapping)))

Listing the colors assigned interactively using /color

Without any parameters, /color lists all nicks in their respective colors (and the name of the color is shown in the help echo).


In the screenshot you can see how my own nick, kensanata, has been highlighted.

At the same time, I’ve used /KEYWORD to highlight bpalmer, just to confuse you. ;)

Compatibility layer for Emacs 21 users

Here’s an implementation of color-distance for Emacs 21, originally from fledermaus from the EmacsChannel [1]. It is based on the same colour metrics [2] Emacs 22 uses.

(defconst colour-triplet-regex

(defun parse-colour (colour frame)
  (cond ((and (stringp colour) (string-match colour-triplet-regex colour))
          (lambda (x)
            (* (string-to-int (match-string x colour) 16) 257)) '(1 2 3)))
        ((stringp colour)
         (if (fboundp 'color-values)
             (color-values colour frame)
           (x-color-values colour frame)))
        ((consp colour) colour)
        (t (error "colour (%S) is not a colour name, #xxxxxx or list"))) )

(defun colour-distance (colour-a colour-b &optional frame)
  "Return an integer distance between COLOUR-A and COLOUR-B on FRAME.
COLOUR-A and COLOUR-B may be either strings containing the color name,
or lists of the form (RED GREEN BLUE).
If FRAME is unspecified or nil, the current frame is used."
  (if frame nil (setq frame (selected-frame)))
  (let* ((colour-a (parse-colour colour-a frame))
         (colour-b (parse-colour colour-b frame))
         ;; now work out the various bit-shift values
         (r   (lsh (- (car   colour-a) (car   colour-b)) -8))
         (g   (lsh (- (cadr  colour-a) (cadr  colour-b)) -8))
         (b   (lsh (- (caddr colour-a) (caddr colour-b)) -8))
         (rmu (lsh (+ (car   colour-a) (car   colour-b)) -9)))
    ;; and now the magic formula-fu
    (+ (lsh (* (+ 512 rmu) r r) -8) (* 4 g g) (lsh (* (- 767 rmu) b b) -8)) ))

An alternative for systems where color-gray-p doesn’t work in Emacs CVS (bug report submitted):

(setq rcirc-colors
      (let (candidates)
        (dolist (item color-name-rgb-alist)
	  (destructuring-bind (color r g b) item
	    (let ((d (sqrt (+ (* (/ r 512) (/ r 512))
			      (* (/ g 512) (/ g 512))
			      (* (/ b 512) (/ b 512))))))
	      (if (and (not (= r g))
		       (not (= r b)); grey
		       (> d 10)
		       (< d 150))
		(setq candidates (cons color candidates))))))

Minimum Contrast

The blog post random foreground color generation has more background on random color generation based on G18: Ensuring that a contrast ratio of at least 4.5:1 exists between text (and images of text) and background behind the text.

Here is how to use the code for rcirc:

(defsubst circe-w3-contrast-c-to-l (c)
  (if (<= c 0.03928)
      (/ c 12.92)
    (expt (/ (+ c 0.055) 1.055) 2.4)))

(defsubst circe-w3-contrast-relative-luminance (rgb)
  (apply '+
         (cl-mapcar (lambda (color coefficient)
                      (* coefficient
                         (circe-w3-contrast-c-to-l color)))
                    '(0.2126 0.7152 0.0722))))

(defsubst circe-w3-contrast-contrast-ratio (color1 color2)
  (let ((l1 (+ 0.05 (circe-w3-contrast-relative-luminance color1)))
        (l2 (+ 0.05 (circe-w3-contrast-relative-luminance color2))))
    (if (> l1 l2)
        (/ l1 l2)
        (/ l2 l1))))

(defsubst circe-w3-contrast-rand ()
  (/ (random 42001) 42000.0))

(defsubst circe-w3-contrast-l-to-c (m)
  (if (<= m (/ 0.03928 12.92))
      (* m 12.92)
      (- (* (expt m (/ 1 2.4))

(defsubst circe-w3-contrast-nn (n)
  (cond ((< n 0) 0)
        ((> n 1) 1)
        (t n)))

(defsubst circe-w3-contrast-color-with-luminance-higher-than (N)
  (let* ((Rc 0.2126)
         (Gc 0.7152)
         (Bc 0.0722)

         (R-min-lum (circe-w3-contrast-nn (/ (- N Gc Bc) Rc)))
         (R-min-color (circe-w3-contrast-l-to-c R-min-lum))
         (R-color (+ R-min-color (* (circe-w3-contrast-rand) (- 1 R-min-color))))
         (R-lum (* Rc (circe-w3-contrast-c-to-l R-color)))

         (G-min-lum (circe-w3-contrast-nn (/ (- N R-lum Bc) Gc)))
         (G-min-color (circe-w3-contrast-l-to-c G-min-lum))
         (G-color (+ G-min-color (* (circe-w3-contrast-rand) (- 1 G-min-color))))
         (G-lum (* Gc (circe-w3-contrast-c-to-l G-color)))

         (B-min-lum (circe-w3-contrast-nn (/ (- N R-lum G-lum) Bc)))
         (B-min-color (circe-w3-contrast-l-to-c B-min-lum))
         (B-color (+ B-min-color (* (circe-w3-contrast-rand) (- 1 B-min-color))))
         (B-lum (* Bc (circe-w3-contrast-c-to-l B-color))))
    (list R-color G-color B-color)))

(defsubst circe-w3-contrast-color-with-luminance-lower-than (N)
  (let* ((Rc 0.2126)
         (Gc 0.7152)
         (Bc 0.0722)

         (R-max-lum (circe-w3-contrast-nn (/ N Rc)))
         (R-max-color (circe-w3-contrast-l-to-c R-max-lum))
         (R-color (* R-max-color (circe-w3-contrast-rand)))
         (R-lum (* Rc (circe-w3-contrast-c-to-l R-color)))

         (G-max-lum (circe-w3-contrast-nn (/ (- N R-lum) Gc)))
         (G-max-color (circe-w3-contrast-l-to-c G-max-lum))
         (G-color (* G-max-color (circe-w3-contrast-rand)))
         (G-lum (* Gc (circe-w3-contrast-c-to-l G-color)))

         (B-max-lum (circe-w3-contrast-nn (/ (- N R-lum G-lum) Bc)))
         (B-max-color (circe-w3-contrast-l-to-c B-max-lum))
         (B-color (* B-max-color (circe-w3-contrast-rand)))
         (B-lum (* Bc (circe-w3-contrast-c-to-l B-color))))
    (list R-color G-color B-color)))

(defsubst circe-w3-contrast-generate-contrast-color (color ratio)
  (let ((color-lum (circe-w3-contrast-relative-luminance color)))
    (if (< color-lum (- (/ 1.0 ratio) 0.05))
        (circe-w3-contrast-color-with-luminance-higher-than (+ (* (+ color-lum 0.05) ratio) 0.05))
        (circe-w3-contrast-color-with-luminance-lower-than (- (/ (+ color-lum 0.05) ratio) 0.05)))))

(defsubst circe-color-from-values (values)
  (apply 'concat 
         (cons "#" 
               (mapcar (lambda (val) 
                         (format "%02x" 
                                 (* (cond ((< val 0) 0)
                                          ((> val 1) 1)
                                          (t val))

(setq rcirc-colors
      (let (colors)
	(dotimes (n 200)
	  (setq colors
		(cons (circe-color-from-values 
			(mapcar (lambda (x) (/ x 65535.0))
				(color-values (face-background 'default)))

Change the 7 in the code above to something smaller such as 4.5 if you want a wider spread of colors.

A simpler piece of code

Here is a simpler explanation from Stack Overflow:

The safest bet is to conform with the World Wide Web Consortium’s (W3C) Web Content Accessibility Guidelines 2.0, which specify a brightness contrast ratio of 4.5:1 for regular text (12 pt or smaller), and 3.0:1 for large text. Contrast ratio is defined as:

[Y(b) + 0.05] / [Y(d) + 0.05]

Where Y(b) is the brightness (luminance) of the brighter color and Y(d) is the brightness of the darker color.

You calculate luminance Y by first converting each of the color’s RGB values to gamma adjusted normalize rgb values:

r = (R/255)^2.2
b = (B/255)^2.2
g = (G/255)^2.2

Then combine them using sRGB constants (rounded to 4 places):

Y = 0.2126*r + 0.7151*g + 0.0721*b

answered by Michael Zuschlag

Here’s how to translate this into Emacs Lisp. The code waits until rcirc is actually loaded before preparing the colors so that we can be reasonably sure any themes or other settings affecting the default background color have already taken effect.

;; To check out the list, evaluate (list-colors-display rcirc-colors)
;; To reset map (setq rcirc-color-mapping (make-hash-table :test 'equal))
(eval-after-load 'rcirc
  '(setq rcirc-colors
	 (let ((candidates nil)
	       (color nil)
	       (y1 (rcirc-colors-Y (face-background 'default))))
	   (dolist (item color-name-rgb-alist)
	     (setq color (car item))
	     (unless (color-gray-p color)
	       ;; Contrast ration is (Y(b) + 0.05) / (Y(d) + 0.05) where
	       ;; Y(b) is the brightness (luminance) of the brighter
	       ;; color and Y(d) is the brightness of the darker color.
	       (let* ((y2 (rcirc-colors-Y color))
		      (r (if (> y1 y2)
			     (/ (+ y1 0.05) (+ y2 0.05))
			   (/ (+ y2 0.05) (+ y1 0.05)))))
		 (when (> r 4.5)
		   (setq candidates (cons color candidates))))))

CategoryChatClient rcirc