Julien De Bona
Free Software, Cooking, and Everything


Des couleurs pour le prompt

Publié le 2016-08-30.

J'aime avoir un prompt de couleur différente sur chaque machine, histoire de ne pas me perdre dans mes sessions SSH. D'un autre côté, j'ai la flemme de choisir une couleur différente pour chaque machine et d'adapter mon profil standard sur chaque machine alors qu'il serait si simple de le copier tel quel.

Xterm et la plupart des autres terminaux graphiques gèrent des couleurs sur 24 bits (3 valeurs rouge, vert, bleu de 0 à 255). C'est bien plus que nécessaire et ça permet de travailler de manière un peu intelligente, mais on ne peut pas se permettre d'être aussi brutal: la couleur du prompt doit contraster suffisamment avec l'arrière-plan du terminal (que je configure en blanc).

Les contraintes sont expliquées ici. Dans mon cas (arrière-plan blanc), elles s'expriment ainsi:

Luminance
299 * R + 587 * G + 114 * B < 130 000
Contraste
R + G + B < 265

Il me faut maintenant générer un triplet de valeurs RGB. On commence avec le nom de la machine et le nom d'utilisateur. Un checksum MD5 va les convertir en valeurs hexadécimales qui fourniront des valeurs décimales comprises entre 0 et 255. Je ne vais pas m'en servir comme valeurs absolues, mais comme ratio entre les 3 couleurs de base.

La seconde étape est de normaliser ces valeurs de telle manière que, tout en conservant les mêmes proportions, la composante la plus importante ait la valeur 255; cela m'assurera de ne pas avoir de couleurs trop ternes.

Les troisième et quatrième étapes comparent les couleurs aux contraintes de luminance et de contraste et les ramènent à des valeurs acceptables si nécessaire. Il ne reste alors plus qu'à injecter les valeurs obtenues dans le prompt, et à faire attention à ne pas diviser par zéro.

J'ai donc le code suivant dans mon ~/.bash_profile. En plus de la couleur, le prompt est en gras pour plus d'effet.

pscolor () {
        seed=$(echo "$(hostname)@$(id -un)" | md5sum)
        # Get raw values
        r=$((16#${seed:0:2}))
        g=$((16#${seed:2:2}))
        if [[ $r -gt $g ]]; then max=$r; else max=$g; fi
        b=$((16#${seed:4:2}))
        if [[ $b -gt $max ]]; then max=$b; fi
        if [[ $max -eq 0 ]]; then
                r=255
                g=255
                b=255
        else
                r=$(expr $r '*' 255 / $max)
                g=$(expr $g '*' 255 / $max)
                b=$(expr $b '*' 255 / $max)
        fi
        bright=$(expr 299 '*' $r + 587 '*' $g + 114 '*' $b)
        if [[ bright -gt 130000 ]]; then
                r=$(expr $r '*' 130000 / $bright)
                g=$(expr $g '*' 130000 / $bright)
                b=$(expr $b '*' 130000 / $bright)
        fi
        total=$(expr $r + $g + $b)
        if [[ total -gt 265 ]]; then
                r=$(expr $r '*' 265 / $total)
                g=$(expr $g '*' 265 / $total)
                b=$(expr $b '*' 265 / $total)
        fi
        echo "\033[38;2;${r};${g};${b}m"
}

if [[ "$TERM" = xterm* ]]; then
        PS1="\[\033[1m$(pscolor)\]$PS1\[\033[39;49m\033[0m\]"
fi

Quelques notes:

  1. Le code requiert bash.
  2. Les séquences de caractères non-imprimables doivent être entourées de \[ et \], faute de quoi les fins de lignes seront mal calculées.
  3. L'émulateur de terminal doit être configuré pour lancer un shell de connexion pour que le fichier .bash_profile soit lu.

tags: linux

Quelques tags ...