about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAurelien Aptel <aaptel@suse.com>2019-12-03 20:51:51 +0100
committerAurelien Aptel <aaptel@suse.com>2019-12-03 21:10:39 +0100
commit4a672bda2202f66c616a7b326c5a497b67ade8fd (patch)
treef9db673806203d89a7bc78dfb8e6872dafd99909
parent15064d97397779d172c993100a763f48a52238e1 (diff)
downloadprofani-tty-4a672bda2202f66c616a7b326c5a497b67ade8fd.tar.gz
XEP-0392: color: implement color hashing
* add the HSL values of each of the 256 terminal colors
* add color_pair_cache_hash_str()
* move common code to _color_pair_cache_get() helper func

after hashing a string to a color, return the closest matching
terminal color using euclidian distance of the HSL diff vector (this
method was found empirically and seems to work well enough...)
-rw-r--r--src/config/color.c645
-rw-r--r--src/config/color.h14
2 files changed, 376 insertions, 283 deletions
diff --git a/src/config/color.c b/src/config/color.c
index 2315dd0b..a6bf865e 100644
--- a/src/config/color.c
+++ b/src/config/color.c
@@ -61,268 +61,294 @@ struct color_pair_cache
  * xterm default 256 colors
  * XXX: there are many duplicates... (eg blue3)
  */
-const char *color_names[COLOR_NAME_SIZE] = {
-    [0]  = "black",
-    [1]  = "red",
-    [2]  = "green",
-    [3]  = "yellow",
-    [4]  = "blue",
-    [5]  = "magenta",
-    [6]  = "cyan",
-    [7]  = "white",
-    [8]  = "lightblack",
-    [9]  = "lightred",
-    [10] = "lightgreen",
-    [11] = "lightyellow",
-    [12] = "lightblue",
-    [13] = "lightmagenta",
-    [14] = "lightcyan",
-    [15] = "lightwhite",
-    [16] = "grey0",
-    [17] = "navyblue",
-    [18] = "darkblue",
-    [19] = "blue3",
-    [20] = "blue3",
-    [21] = "blue1",
-    [22] = "darkgreen",
-    [23] = "deepskyblue4",
-    [24] = "deepskyblue4",
-    [25] = "deepskyblue4",
-    [26] = "dodgerblue3",
-    [27] = "dodgerblue2",
-    [28] = "green4",
-    [29] = "springgreen4",
-    [30] = "turquoise4",
-    [31] = "deepskyblue3",
-    [32] = "deepskyblue3",
-    [33] = "dodgerblue1",
-    [34] = "green3",
-    [35] = "springgreen3",
-    [36] = "darkcyan",
-    [37] = "lightseagreen",
-    [38] = "deepskyblue2",
-    [39] = "deepskyblue1",
-    [40] = "green3",
-    [41] = "springgreen3",
-    [42] = "springgreen2",
-    [43] = "cyan3",
-    [44] = "darkturquoise",
-    [45] = "turquoise2",
-    [46] = "green1",
-    [47] = "springgreen2",
-    [48] = "springgreen1",
-    [49] = "mediumspringgreen",
-    [50] = "cyan2",
-    [51] = "cyan1",
-    [52] = "darkred",
-    [53] = "deeppink4",
-    [54] = "purple4",
-    [55] = "purple4",
-    [56] = "purple3",
-    [57] = "blueviolet",
-    [58] = "orange4",
-    [59] = "grey37",
-    [60] = "mediumpurple4",
-    [61] = "slateblue3",
-    [62] = "slateblue3",
-    [63] = "royalblue1",
-    [64] = "chartreuse4",
-    [65] = "darkseagreen4",
-    [66] = "paleturquoise4",
-    [67] = "steelblue",
-    [68] = "steelblue3",
-    [69] = "cornflowerblue",
-    [70] = "chartreuse3",
-    [71] = "darkseagreen4",
-    [72] = "cadetblue",
-    [73] = "cadetblue",
-    [74] = "skyblue3",
-    [75] = "steelblue1",
-    [76] = "chartreuse3",
-    [77] = "palegreen3",
-    [78] = "seagreen3",
-    [79] = "aquamarine3",
-    [80] = "mediumturquoise",
-    [81] = "steelblue1",
-    [82] = "chartreuse2",
-    [83] = "seagreen2",
-    [84] = "seagreen1",
-    [85] = "seagreen1",
-    [86] = "aquamarine1",
-    [87] = "darkslategray2",
-    [88] = "darkred",
-    [89] = "deeppink4",
-    [90] = "darkmagenta",
-    [91] = "darkmagenta",
-    [92] = "darkviolet",
-    [93] = "purple",
-    [94] = "orange4",
-    [95] = "lightpink4",
-    [96] = "plum4",
-    [97] = "mediumpurple3",
-    [98] = "mediumpurple3",
-    [99] = "slateblue1",
-    [100] = "yellow4",
-    [101] = "wheat4",
-    [102] = "grey53",
-    [103] = "lightslategrey",
-    [104] = "mediumpurple",
-    [105] = "lightslateblue",
-    [106] = "yellow4",
-    [107] = "darkolivegreen3",
-    [108] = "darkseagreen",
-    [109] = "lightskyblue3",
-    [110] = "lightskyblue3",
-    [111] = "skyblue2",
-    [112] = "chartreuse2",
-    [113] = "darkolivegreen3",
-    [114] = "palegreen3",
-    [115] = "darkseagreen3",
-    [116] = "darkslategray3",
-    [117] = "skyblue1",
-    [118] = "chartreuse1",
-    [119] = "lightgreen",
-    [120] = "lightgreen",
-    [121] = "palegreen1",
-    [122] = "aquamarine1",
-    [123] = "darkslategray1",
-    [124] = "red3",
-    [125] = "deeppink4",
-    [126] = "mediumvioletred",
-    [127] = "magenta3",
-    [128] = "darkviolet",
-    [129] = "purple",
-    [130] = "darkorange3",
-    [131] = "indianred",
-    [132] = "hotpink3",
-    [133] = "mediumorchid3",
-    [134] = "mediumorchid",
-    [135] = "mediumpurple2",
-    [136] = "darkgoldenrod",
-    [137] = "lightsalmon3",
-    [138] = "rosybrown",
-    [139] = "grey63",
-    [140] = "mediumpurple2",
-    [141] = "mediumpurple1",
-    [142] = "gold3",
-    [143] = "darkkhaki",
-    [144] = "navajowhite3",
-    [145] = "grey69",
-    [146] = "lightsteelblue3",
-    [147] = "lightsteelblue",
-    [148] = "yellow3",
-    [149] = "darkolivegreen3",
-    [150] = "darkseagreen3",
-    [151] = "darkseagreen2",
-    [152] = "lightcyan3",
-    [153] = "lightskyblue1",
-    [154] = "greenyellow",
-    [155] = "darkolivegreen2",
-    [156] = "palegreen1",
-    [157] = "darkseagreen2",
-    [158] = "darkseagreen1",
-    [159] = "paleturquoise1",
-    [160] = "red3",
-    [161] = "deeppink3",
-    [162] = "deeppink3",
-    [163] = "magenta3",
-    [164] = "magenta3",
-    [165] = "magenta2",
-    [166] = "darkorange3",
-    [167] = "indianred",
-    [168] = "hotpink3",
-    [169] = "hotpink2",
-    [170] = "orchid",
-    [171] = "mediumorchid1",
-    [172] = "orange3",
-    [173] = "lightsalmon3",
-    [174] = "lightpink3",
-    [175] = "pink3",
-    [176] = "plum3",
-    [177] = "violet",
-    [178] = "gold3",
-    [179] = "lightgoldenrod3",
-    [180] = "tan",
-    [181] = "mistyrose3",
-    [182] = "thistle3",
-    [183] = "plum2",
-    [184] = "yellow3",
-    [185] = "khaki3",
-    [186] = "lightgoldenrod2",
-    [187] = "lightyellow3",
-    [188] = "grey84",
-    [189] = "lightsteelblue1",
-    [190] = "yellow2",
-    [191] = "darkolivegreen1",
-    [192] = "darkolivegreen1",
-    [193] = "darkseagreen1",
-    [194] = "honeydew2",
-    [195] = "lightcyan1",
-    [196] = "red1",
-    [197] = "deeppink2",
-    [198] = "deeppink1",
-    [199] = "deeppink1",
-    [200] = "magenta2",
-    [201] = "magenta1",
-    [202] = "orangered1",
-    [203] = "indianred1",
-    [204] = "indianred1",
-    [205] = "hotpink",
-    [206] = "hotpink",
-    [207] = "mediumorchid1",
-    [208] = "darkorange",
-    [209] = "salmon1",
-    [210] = "lightcoral",
-    [211] = "palevioletred1",
-    [212] = "orchid2",
-    [213] = "orchid1",
-    [214] = "orange1",
-    [215] = "sandybrown",
-    [216] = "lightsalmon1",
-    [217] = "lightpink1",
-    [218] = "pink1",
-    [219] = "plum1",
-    [220] = "gold1",
-    [221] = "lightgoldenrod2",
-    [222] = "lightgoldenrod2",
-    [223] = "navajowhite1",
-    [224] = "mistyrose1",
-    [225] = "thistle1",
-    [226] = "yellow1",
-    [227] = "lightgoldenrod1",
-    [228] = "khaki1",
-    [229] = "wheat1",
-    [230] = "cornsilk1",
-    [231] = "grey100",
-    [232] = "grey3",
-    [233] = "grey7",
-    [234] = "grey11",
-    [235] = "grey15",
-    [236] = "grey19",
-    [237] = "grey23",
-    [238] = "grey27",
-    [239] = "grey30",
-    [240] = "grey35",
-    [241] = "grey39",
-    [242] = "grey42",
-    [243] = "grey46",
-    [244] = "grey50",
-    [245] = "grey54",
-    [246] = "grey58",
-    [247] = "grey62",
-    [248] = "grey66",
-    [249] = "grey70",
-    [250] = "grey74",
-    [251] = "grey78",
-    [252] = "grey82",
-    [253] = "grey85",
-    [254] = "grey89",
-    [255] = "grey93",
+
+const struct color_def color_names[COLOR_NAME_SIZE] = {
+  [0]  =  { 0,      0,      0,  "black" },
+  [1]  =  { 0,      100,    25, "red" },
+  [2]  =  { 120,    100,    25, "green" },
+  [3]  =  { 60,     100,    25, "yellow" },
+  [4]  =  { 240,    100,    25, "blue" },
+  [5]  =  { 300,    100,    25, "magenta" },
+  [6]  =  { 180,    100,    25, "cyan" },
+  [7]  =  { 0,      0,      75, "white" },
+  [8]  =  { 0,      0,      50, "lightblack" },
+  [9]  =  { 0,      100,    50, "lightred" },
+  [10] =  { 120,    100,    50, "lightgreen" },
+  [11] =  { 60,     100,    50, "lightyellow" },
+  [12] =  { 240,    100,    50, "lightblue" },
+  [13] =  { 300,    100,    50, "lightmagenta" },
+  [14] =  { 180,    100,    50, "lightcyan" },
+  [15] =  { 0,      0,      100, "lightwhite" },
+  [16] =  { 0,      0,      0,  "grey0" },
+  [17] =  { 240,    100,    18, "navyblue" },
+  [18] =  { 240,    100,    26, "darkblue" },
+  [19] =  { 240,    100,    34, "blue3" },
+  [20] =  { 240,    100,    42, "blue3" },
+  [21] =  { 240,    100,    50, "blue1" },
+  [22] =  { 120,    100,    18, "darkgreen" },
+  [23] =  { 180,    100,    18, "deepskyblue4" },
+  [24] =  { 97,     100,    26, "deepskyblue4" },
+  [25] =  { 7,     100,    34, "deepskyblue4" },
+  [26] =  { 13,     100,    42, "dodgerblue3" },
+  [27] =  { 17,     100,    50, "dodgerblue2" },
+  [28] =  { 120,    100,    26, "green4" },
+  [29] =  { 62,     100,    26, "springgreen4" },
+  [30] =  { 180,    100,    26, "turquoise4" },
+  [31] =  { 93,     100,    34, "deepskyblue3" },
+  [32] =  { 2,     100,    42, "deepskyblue3" },
+  [33] =  { 8,     100,    50, "dodgerblue1" },
+  [34] =  { 120,    100,    34, "green3" },
+  [35] =  { 52,     100,    34, "springgreen3" },
+  [36] =  { 66,     100,    34, "darkcyan" },
+  [37] =  { 180,    100,    34, "lightseagreen" },
+  [38] =  { 91,     100,    42, "deepskyblue2" },
+  [39] =  { 98,     100,    50, "deepskyblue1" },
+  [40] =  { 120,    100,    42, "green3" },
+  [41] =  { 46,     100,    42, "springgreen3" },
+  [42] =  { 57,     100,    42, "springgreen2" },
+  [43] =  { 68,     100,    42, "cyan3" },
+  [44] =  { 180,    100,    42, "darkturquoise" },
+  [45] =  { 89,     100,    50, "turquoise2" },
+  [46] =  { 120,    100,    50, "green1" },
+  [47] =  { 42,     100,    50, "springgreen2" },
+  [48] =  { 51,     100,    50, "springgreen1" },
+  [49] =  { 61,     100,    50, "mediumspringgreen" },
+  [50] =  { 70,     100,    50, "cyan2" },
+  [51] =  { 180,    100,    50, "cyan1" },
+  [52] =  { 0,      100,    18, "darkred" },
+  [53] =  { 300,    100,    18, "deeppink4" },
+  [54] =  { 82,     100,    26, "purple4" },
+  [55] =  { 72,     100,    34, "purple4" },
+  [56] =  { 66,     100,    42, "purple3" },
+  [57] =  { 62,     100,    50, "blueviolet" },
+  [58] =  { 60,     100,    18, "orange4" },
+  [59] =  { 0,      0,      37, "grey37" },
+  [60] =  { 240,    17,     45, "mediumpurple4" },
+  [61] =  { 240,    33,     52, "slateblue3" },
+  [62] =  { 240,    60,     60, "slateblue3" },
+  [63] =  { 240,    100,    68, "royalblue1" },
+  [64] =  { 7,      100,    26, "chartreuse4" },
+  [65] =  { 120,    17,     45, "darkseagreen4" },
+  [66] =  { 180,    17,     45, "paleturquoise4" },
+  [67] =  { 210,    33,     52, "steelblue" },
+  [68] =  { 220,    60,     60, "steelblue3" },
+  [69] =  { 225,    100,    68, "cornflowerblue" },
+  [70] =  { 7,      100,    34, "chartreuse3" },
+  [71] =  { 120,    33,     52, "darkseagreen4" },
+  [72] =  { 150,    33,     52, "cadetblue" },
+  [73] =  { 180,    33,     52, "cadetblue" },
+  [74] =  { 200,    60,     60, "skyblue3" },
+  [75] =  { 210,    100,    68, "steelblue1" },
+  [76] =  { 3,      100,    42, "chartreuse3" },
+  [77] =  { 120,    60,     60, "palegreen3" },
+  [78] =  { 140,    60,     60, "seagreen3" },
+  [79] =  { 160,    60,     60, "aquamarine3" },
+  [80] =  { 180,    60,     60, "mediumturquoise" },
+  [81] =  { 195,    100,    68, "steelblue1" },
+  [82] =  { 7,      100,    50, "chartreuse2" },
+  [83] =  { 120,    100,    68, "seagreen2" },
+  [84] =  { 135,    100,    68, "seagreen1" },
+  [85] =  { 150,    100,    68, "seagreen1" },
+  [86] =  { 165,    100,    68, "aquamarine1" },
+  [87] =  { 180,    100,    68, "darkslategray2" },
+  [88] =  { 0,      100,    26, "darkred" },
+  [89] =  { 17,     100,    26, "deeppink4" },
+  [90] =  { 300,    100,    26, "darkmagenta" },
+  [91] =  { 86,     100,    34, "darkmagenta" },
+  [92] =  { 77,     100,    42, "darkviolet" },
+  [93] =  { 71,     100,    50, "purple" },
+  [94] =  { 2,      100,    26, "orange4" },
+  [95] =  { 0,      17,     45, "lightpink4" },
+  [96] =  { 300,    17,     45, "plum4" },
+  [97] =  { 270,    33,     52, "mediumpurple3" },
+  [98] =  { 260,    60,     60, "mediumpurple3" },
+  [99] =  { 255,    100,    68, "slateblue1" },
+  [100] = { 60,     100,    26, "yellow4" },
+  [101] = { 60,     17,     45, "wheat4" },
+  [102] = { 0,      0,      52, "grey53" },
+  [103] = { 240,    20,     60, "lightslategrey" },
+  [104] = { 240,    50,     68, "mediumpurple" },
+  [105] = { 240,    100,    76, "lightslateblue" },
+  [106] = { 3,      100,    34, "yellow4" },
+  [107] = { 90,     33,     52, "darkolivegreen3" },
+  [108] = { 120,    20,     60, "darkseagreen" },
+  [109] = { 180,    20,     60, "lightskyblue3" },
+  [110] = { 210,    50,     68, "lightskyblue3" },
+  [111] = { 220,    100,    76, "skyblue2" },
+  [112] = { 2,      100,    42, "chartreuse2" },
+  [113] = { 100,    60,     60, "darkolivegreen3" },
+  [114] = { 120,    50,     68, "palegreen3" },
+  [115] = { 150,    50,     68, "darkseagreen3" },
+  [116] = { 180,    50,     68, "darkslategray3" },
+  [117] = { 200,    100,    76, "skyblue1" },
+  [118] = { 8,      100,    50, "chartreuse1" },
+  [119] = { 105,    100,    68, "lightgreen" },
+  [120] = { 120,    100,    76, "lightgreen" },
+  [121] = { 140,    100,    76, "palegreen1" },
+  [122] = { 160,    100,    76, "aquamarine1" },
+  [123] = { 180,    100,    76, "darkslategray1" },
+  [124] = { 0,      100,    34, "red3" },
+  [125] = { 27,     100,    34, "deeppink4" },
+  [126] = { 13,     100,    34, "mediumvioletred" },
+  [127] = { 300,    100,    34, "magenta3" },
+  [128] = { 88,     100,    42, "darkviolet" },
+  [129] = { 81,     100,    50, "purple" },
+  [130] = { 2,      100,    34, "darkorange3" },
+  [131] = { 0,      33,     52, "indianred" },
+  [132] = { 330,    33,     52, "hotpink3" },
+  [133] = { 300,    33,     52, "mediumorchid3" },
+  [134] = { 280,    60,     60, "mediumorchid" },
+  [135] = { 270,    100,    68, "mediumpurple2" },
+  [136] = { 6,      100,    34, "darkgoldenrod" },
+  [137] = { 30,     33,     52, "lightsalmon3" },
+  [138] = { 0,      20,     60, "rosybrown" },
+  [139] = { 300,    20,     60, "grey63" },
+  [140] = { 270,    50,     68, "mediumpurple2" },
+  [141] = { 260,    100,    76, "mediumpurple1" },
+  [142] = { 60,     100,    34, "gold3" },
+  [143] = { 60,     33,     52, "darkkhaki" },
+  [144] = { 60,     20,     60, "navajowhite3" },
+  [145] = { 0,      0,      68, "grey69" },
+  [146] = { 240,    33,     76, "lightsteelblue3" },
+  [147] = { 240,    100,    84, "lightsteelblue" },
+  [148] = { 1,      100,    42, "yellow3" },
+  [149] = { 80,     60,     60, "darkolivegreen3" },
+  [150] = { 90,     50,     68, "darkseagreen3" },
+  [151] = { 120,    33,     76, "darkseagreen2" },
+  [152] = { 180,    33,     76, "lightcyan3" },
+  [153] = { 210,    100,    84, "lightskyblue1" },
+  [154] = { 8,      100,    50, "greenyellow" },
+  [155] = { 90,     100,    68, "darkolivegreen2" },
+  [156] = { 100,    100,    76, "palegreen1" },
+  [157] = { 120,    100,    84, "darkseagreen2" },
+  [158] = { 150,    100,    84, "darkseagreen1" },
+  [159] = { 180,    100,    84, "paleturquoise1" },
+  [160] = { 0,      100,    42, "red3" },
+  [161] = { 33,     100,    42, "deeppink3" },
+  [162] = { 22,     100,    42, "deeppink3" },
+  [163] = { 11,     100,    42, "magenta3" },
+  [164] = { 300,    100,    42, "magenta3" },
+  [165] = { 90,     100,    50, "magenta2" },
+  [166] = { 6,      100,    42, "darkorange3" },
+  [167] = { 0,      60,     60, "indianred" },
+  [168] = { 340,    60,     60, "hotpink3" },
+  [169] = { 320,    60,     60, "hotpink2" },
+  [170] = { 300,    60,     60, "orchid" },
+  [171] = { 285,    100,    68, "mediumorchid1" },
+  [172] = { 7,      100,    42, "orange3" },
+  [173] = { 20,     60,     60, "lightsalmon3" },
+  [174] = { 0,      50,     68, "lightpink3" },
+  [175] = { 330,    50,     68, "pink3" },
+  [176] = { 300,    50,     68, "plum3" },
+  [177] = { 280,    100,    76, "violet" },
+  [178] = { 8,      100,    42, "gold3" },
+  [179] = { 40,     60,     60, "lightgoldenrod3" },
+  [180] = { 30,     50,     68, "tan" },
+  [181] = { 0,      33,     76, "mistyrose3" },
+  [182] = { 300,    33,     76, "thistle3" },
+  [183] = { 270,    100,    84, "plum2" },
+  [184] = { 60,     100,    42, "yellow3" },
+  [185] = { 60,     60,     60, "khaki3" },
+  [186] = { 60,     50,     68, "lightgoldenrod2" },
+  [187] = { 60,     33,     76, "lightyellow3" },
+  [188] = { 0,      0,      84, "grey84" },
+  [189] = { 240,    100,    92, "lightsteelblue1" },
+  [190] = { 9,      100,    50, "yellow2" },
+  [191] = { 75,     100,    68, "darkolivegreen1" },
+  [192] = { 80,     100,    76, "darkolivegreen1" },
+  [193] = { 90,     100,    84, "darkseagreen1" },
+  [194] = { 120,    100,    92, "honeydew2" },
+  [195] = { 180,    100,    92, "lightcyan1" },
+  [196] = { 0,      100,    50, "red1" },
+  [197] = { 37,     100,    50, "deeppink2" },
+  [198] = { 28,     100,    50, "deeppink1" },
+  [199] = { 18,     100,    50, "deeppink1" },
+  [200] = { 9,     100,    50, "magenta2" },
+  [201] = { 300,    100,    50, "magenta1" },
+  [202] = { 2,      100,    50, "orangered1" },
+  [203] = { 0,      100,    68, "indianred1" },
+  [204] = { 345,    100,    68, "indianred1" },
+  [205] = { 330,    100,    68, "hotpink" },
+  [206] = { 315,    100,    68, "hotpink" },
+  [207] = { 300,    100,    68, "mediumorchid1" },
+  [208] = { 1,      100,    50, "darkorange" },
+  [209] = { 15,     100,    68, "salmon1" },
+  [210] = { 0,      100,    76, "lightcoral" },
+  [211] = { 340,    100,    76, "palevioletred1" },
+  [212] = { 320,    100,    76, "orchid2" },
+  [213] = { 300,    100,    76, "orchid1" },
+  [214] = { 1,      100,    50, "orange1" },
+  [215] = { 30,     100,    68, "sandybrown" },
+  [216] = { 20,     100,    76, "lightsalmon1" },
+  [217] = { 0,      100,    84, "lightpink1" },
+  [218] = { 330,    100,    84, "pink1" },
+  [219] = { 300,    100,    84, "plum1" },
+  [220] = { 0,      100,    50, "gold1" },
+  [221] = { 45,     100,    68, "lightgoldenrod2" },
+  [222] = { 40,     100,    76, "lightgoldenrod2" },
+  [223] = { 30,     100,    84, "navajowhite1" },
+  [224] = { 0,      100,    92, "mistyrose1" },
+  [225] = { 300,    100,    92, "thistle1" },
+  [226] = { 60,     100,    50, "yellow1" },
+  [227] = { 60,     100,    68, "lightgoldenrod1" },
+  [228] = { 60,     100,    76, "khaki1" },
+  [229] = { 60,     100,    84, "wheat1" },
+  [230] = { 60,     100,    92, "cornsilk1" },
+  [231] = { 0,      0,      100, "grey100" },
+  [232] = { 0,      0,      3,  "grey3" },
+  [233] = { 0,      0,      7,  "grey7" },
+  [234] = { 0,      0,      10, "grey11" },
+  [235] = { 0,      0,      14, "grey15" },
+  [236] = { 0,      0,      18, "grey19" },
+  [237] = { 0,      0,      22, "grey23" },
+  [238] = { 0,      0,      26, "grey27" },
+  [239] = { 0,      0,      30, "grey30" },
+  [240] = { 0,      0,      34, "grey35" },
+  [241] = { 0,      0,      37, "grey39" },
+  [242] = { 0,      0,      40, "grey42" },
+  [243] = { 0,      0,      46, "grey46" },
+  [244] = { 0,      0,      50, "grey50" },
+  [245] = { 0,      0,      54, "grey54" },
+  [246] = { 0,      0,      58, "grey58" },
+  [247] = { 0,      0,      61, "grey62" },
+  [248] = { 0,      0,      65, "grey66" },
+  [249] = { 0,      0,      69, "grey70" },
+  [250] = { 0,      0,      73, "grey74" },
+  [251] = { 0,      0,      77, "grey78" },
+  [252] = { 0,      0,      81, "grey82" },
+  [253] = { 0,      0,      85, "grey85" },
+  [254] = { 0,      0,      89, "grey89" },
+  [255] = { 0,      0,      93, "grey93" },
 };
 
 /* -1 is valid curses color */
 #define COL_ERR -2
 
+static inline int color_distance(const struct color_def *a, const struct color_def *b)
+{
+    int h = MIN((a->h - b->h)%360, (b->h - a->h)%360);
+    int s = (int)a->s - b->s;
+    int l = (int)a->l - b->l;
+    return h*h + s*s + l*l;
+}
+
+static int find_closest_col(int h, int s, int l)
+{
+    int i;
+    struct color_def a = {h, s, l};
+    int min = 0;
+    int dmin = color_distance(&a, &color_names[0]);
+
+    for (i = 1; i < COLOR_NAME_SIZE; i++) {
+        int d = color_distance(&a, &color_names[i]);
+        if (d < dmin) {
+            dmin = d;
+            min = i;
+        }
+    }
+    return min;
+}
+
 static int find_col(const char *col_name, int n)
 {
     int i;
@@ -346,7 +372,7 @@ static int find_col(const char *col_name, int n)
     }
 
     for (i = 0; i < COLOR_NAME_SIZE; i++) {
-        if (g_ascii_strcasecmp(name, color_names[i]) == 0) {
+        if (g_ascii_strcasecmp(name, color_names[i].name) == 0) {
             return i;
         }
     }
@@ -354,6 +380,39 @@ static int find_col(const char *col_name, int n)
     return COL_ERR;
 }
 
+static int color_hash(const char *str)
+{
+    GChecksum *cs = NULL;
+    guint8 buf[256] = {0};
+    gsize len = 256;
+    int rc = -1; /* default ncurse color */
+
+    cs = g_checksum_new(G_CHECKSUM_SHA1);
+    if (!cs)
+        goto out;
+
+    g_checksum_update(cs, (guint8*)str, strlen(str));
+    g_checksum_get_digest(cs, buf, &len);
+
+    // sha1 should be 20 bytes
+    if (len != 20)
+        goto out;
+
+    double h = ((buf[1] << 8) | buf[0]) / 65536. * 360.;
+
+    // red/green blindness correction
+    // h = fmod(fmod(h + 90., 180) - 90., 360.);
+
+    // blue blindness correction
+    // h = fmod(h, 180.);
+
+    rc = find_closest_col((int)h, 100, 50);
+
+  out:
+    g_checksum_free(cs);
+    return rc;
+}
+
 void color_pair_cache_reset(void)
 {
     if (cache.pairs) {
@@ -382,30 +441,9 @@ void color_pair_cache_reset(void)
     }
 }
 
-/**
- * color_pair_cache_get - parse color pair "fg_bg" and returns curses id
- *
- * if the pair doesn't exist it will allocate it in curses with init_pair
- * if the pair exists it returns its id
- */
-int color_pair_cache_get(const char *pair_name)
+static int _color_pair_cache_get(int fg, int bg)
 {
     int i;
-    const char *sep;
-    int fg, bg;
-
-    sep = strchr(pair_name, '_');
-    if (!sep) {
-        log_error("Color: color pair %s missing", pair_name);
-        return -1;
-    }
-
-    fg = find_col(pair_name, sep - pair_name);
-    bg = find_col(sep+1, strlen(sep));
-    if (fg == COL_ERR || bg == COL_ERR) {
-        log_error("Color: bad color name %s", pair_name);
-        return -1;
-    }
 
     if (COLORS < 256) {
         if (fg > 7 || bg > 7) {
@@ -424,7 +462,8 @@ int color_pair_cache_get(const char *pair_name)
     /* otherwise cache new pair */
 
     if (cache.size >= cache.capacity) {
-        log_error("Color: reached ncurses color pair cache of %d", COLOR_PAIRS);
+        log_error("Color: reached ncurses color pair cache of %d (COLOR_PAIRS=%d)",
+                  cache.capacity, COLOR_PAIRS);
         return -1;
     }
 
@@ -438,3 +477,47 @@ int color_pair_cache_get(const char *pair_name)
 
     return i;
 }
+
+/**
+ * color_pair_cache_hash_str - hash string to a color pair curses id
+ *
+ * Implements XEP-0392 ("Consistent Color Generation") as best as
+ * possible given a 256 colors terminal.
+ *
+ * hash a string into a color that will be used as fg
+ * use default color as bg
+ */
+int color_pair_cache_hash_str(const char *str)
+{
+    int fg = color_hash(str);
+    int bg = -1;
+
+    return _color_pair_cache_get(fg, bg);
+}
+
+/**
+ * color_pair_cache_get - parse color pair "fg_bg" and returns curses id
+ *
+ * if the pair doesn't exist it will allocate it in curses with init_pair
+ * if the pair exists it returns its id
+ */
+int color_pair_cache_get(const char *pair_name)
+{
+    const char *sep;
+    int fg, bg;
+
+    sep = strchr(pair_name, '_');
+    if (!sep) {
+        log_error("Color: color pair %s missing", pair_name);
+        return -1;
+    }
+
+    fg = find_col(pair_name, sep - pair_name);
+    bg = find_col(sep+1, strlen(sep));
+    if (fg == COL_ERR || bg == COL_ERR) {
+        log_error("Color: bad color name %s", pair_name);
+        return -1;
+    }
+
+    return _color_pair_cache_get(fg, bg);
+}
diff --git a/src/config/color.h b/src/config/color.h
index 247fffaf..6a5cf4a9 100644
--- a/src/config/color.h
+++ b/src/config/color.h
@@ -38,10 +38,20 @@
 
 /* to access color names */
 #define COLOR_NAME_SIZE 256
-extern const char *color_names[];
 
-/* to add or clear cache */
+#include <stdint.h>
+
+struct color_def {
+    uint16_t h; uint8_t s, l;
+    const char *name;
+};
+extern const struct color_def color_names[];
+
+/* hash string to color pair */
+int color_pair_cache_hash_str(const char *str);
+/* parse fg_bg string to color pair */
 int color_pair_cache_get(const char *pair_name);
+/* clear cache */
 void color_pair_cache_reset(void);
 
 #endif