1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2 
3 #include "vt_color.h"
4 
5 #include <stdio.h>  /* sscanf */
6 #include <string.h> /* strcmp */
7 #include <pobl/bl_debug.h>
8 #include <pobl/bl_map.h>
9 #include <pobl/bl_mem.h>
10 #include <pobl/bl_file.h>
11 #include <pobl/bl_conf_io.h>
12 #include <pobl/bl_str.h> /* strdup */
13 
14 typedef struct rgb {
15   u_int8_t red;
16   u_int8_t green;
17   u_int8_t blue;
18   u_int8_t alpha;
19 
20 } rgb_t;
21 
22 BL_MAP_TYPEDEF(color_rgb, vt_color_t, rgb_t);
23 
24 /* --- static variables --- */
25 
26 static char *color_name_table[] = {
27     "hl_black", "hl_red", "hl_green", "hl_yellow", "hl_blue", "hl_magenta", "hl_cyan", "hl_white",
28 };
29 
30 static u_int8_t vtsys_color_rgb_table[][3] = {
31     {0x00, 0x00, 0x00},
32     {0xcd, 0x00, 0x00},
33     {0x00, 0xcd, 0x00},
34     {0xcd, 0xcd, 0x00},
35     {0x00, 0x00, 0xee},
36     {0xcd, 0x00, 0xcd},
37     {0x00, 0xcd, 0xcd},
38     {0xe5, 0xe5, 0xe5},
39 
40     {0x7f, 0x7f, 0x7f},
41     {0xff, 0x00, 0x00},
42     {0x00, 0xff, 0x00},
43     {0xff, 0xff, 0x00},
44     {0x5c, 0x5c, 0xff},
45     {0xff, 0x00, 0xff},
46     {0x00, 0xff, 0xff},
47     {0xff, 0xff, 0xff},
48 };
49 
50 #if 0
51 static u_int8_t color256_rgb_table[][3] = {
52     /* CUBE COLOR(0x10-0xe7) */
53     {0x00, 0x00, 0x00},
54     {0x00, 0x00, 0x5f},
55     {0x00, 0x00, 0x87},
56     {0x00, 0x00, 0xaf},
57     {0x00, 0x00, 0xd7},
58     {0x00, 0x00, 0xff},
59     {0x00, 0x5f, 0x00},
60     {0x00, 0x5f, 0x5f},
61     {0x00, 0x5f, 0x87},
62     {0x00, 0x5f, 0xaf},
63     {0x00, 0x5f, 0xd7},
64     {0x00, 0x5f, 0xff},
65     {0x00, 0x87, 0x00},
66     {0x00, 0x87, 0x5f},
67     {0x00, 0x87, 0x87},
68     {0x00, 0x87, 0xaf},
69     {0x00, 0x87, 0xd7},
70     {0x00, 0x87, 0xff},
71     {0x00, 0xaf, 0x00},
72     {0x00, 0xaf, 0x5f},
73     {0x00, 0xaf, 0x87},
74     {0x00, 0xaf, 0xaf},
75     {0x00, 0xaf, 0xd7},
76     {0x00, 0xaf, 0xff},
77     {0x00, 0xd7, 0x00},
78     {0x00, 0xd7, 0x5f},
79     {0x00, 0xd7, 0x87},
80     {0x00, 0xd7, 0xaf},
81     {0x00, 0xd7, 0xd7},
82     {0x00, 0xd7, 0xff},
83     {0x00, 0xff, 0x00},
84     {0x00, 0xff, 0x5f},
85     {0x00, 0xff, 0x87},
86     {0x00, 0xff, 0xaf},
87     {0x00, 0xff, 0xd7},
88     {0x00, 0xff, 0xff},
89     {0x5f, 0x00, 0x00},
90     {0x5f, 0x00, 0x5f},
91     {0x5f, 0x00, 0x87},
92     {0x5f, 0x00, 0xaf},
93     {0x5f, 0x00, 0xd7},
94     {0x5f, 0x00, 0xff},
95     {0x5f, 0x5f, 0x00},
96     {0x5f, 0x5f, 0x5f},
97     {0x5f, 0x5f, 0x87},
98     {0x5f, 0x5f, 0xaf},
99     {0x5f, 0x5f, 0xd7},
100     {0x5f, 0x5f, 0xff},
101     {0x5f, 0x87, 0x00},
102     {0x5f, 0x87, 0x5f},
103     {0x5f, 0x87, 0x87},
104     {0x5f, 0x87, 0xaf},
105     {0x5f, 0x87, 0xd7},
106     {0x5f, 0x87, 0xff},
107     {0x5f, 0xaf, 0x00},
108     {0x5f, 0xaf, 0x5f},
109     {0x5f, 0xaf, 0x87},
110     {0x5f, 0xaf, 0xaf},
111     {0x5f, 0xaf, 0xd7},
112     {0x5f, 0xaf, 0xff},
113     {0x5f, 0xd7, 0x00},
114     {0x5f, 0xd7, 0x5f},
115     {0x5f, 0xd7, 0x87},
116     {0x5f, 0xd7, 0xaf},
117     {0x5f, 0xd7, 0xd7},
118     {0x5f, 0xd7, 0xff},
119     {0x5f, 0xff, 0x00},
120     {0x5f, 0xff, 0x5f},
121     {0x5f, 0xff, 0x87},
122     {0x5f, 0xff, 0xaf},
123     {0x5f, 0xff, 0xd7},
124     {0x5f, 0xff, 0xff},
125     {0x87, 0x00, 0x00},
126     {0x87, 0x00, 0x5f},
127     {0x87, 0x00, 0x87},
128     {0x87, 0x00, 0xaf},
129     {0x87, 0x00, 0xd7},
130     {0x87, 0x00, 0xff},
131     {0x87, 0x5f, 0x00},
132     {0x87, 0x5f, 0x5f},
133     {0x87, 0x5f, 0x87},
134     {0x87, 0x5f, 0xaf},
135     {0x87, 0x5f, 0xd7},
136     {0x87, 0x5f, 0xff},
137     {0x87, 0x87, 0x00},
138     {0x87, 0x87, 0x5f},
139     {0x87, 0x87, 0x87},
140     {0x87, 0x87, 0xaf},
141     {0x87, 0x87, 0xd7},
142     {0x87, 0x87, 0xff},
143     {0x87, 0xaf, 0x00},
144     {0x87, 0xaf, 0x5f},
145     {0x87, 0xaf, 0x87},
146     {0x87, 0xaf, 0xaf},
147     {0x87, 0xaf, 0xd7},
148     {0x87, 0xaf, 0xff},
149     {0x87, 0xd7, 0x00},
150     {0x87, 0xd7, 0x5f},
151     {0x87, 0xd7, 0x87},
152     {0x87, 0xd7, 0xaf},
153     {0x87, 0xd7, 0xd7},
154     {0x87, 0xd7, 0xff},
155     {0x87, 0xff, 0x00},
156     {0x87, 0xff, 0x5f},
157     {0x87, 0xff, 0x87},
158     {0x87, 0xff, 0xaf},
159     {0x87, 0xff, 0xd7},
160     {0x87, 0xff, 0xff},
161     {0xaf, 0x00, 0x00},
162     {0xaf, 0x00, 0x5f},
163     {0xaf, 0x00, 0x87},
164     {0xaf, 0x00, 0xaf},
165     {0xaf, 0x00, 0xd7},
166     {0xaf, 0x00, 0xff},
167     {0xaf, 0x5f, 0x00},
168     {0xaf, 0x5f, 0x5f},
169     {0xaf, 0x5f, 0x87},
170     {0xaf, 0x5f, 0xaf},
171     {0xaf, 0x5f, 0xd7},
172     {0xaf, 0x5f, 0xff},
173     {0xaf, 0x87, 0x00},
174     {0xaf, 0x87, 0x5f},
175     {0xaf, 0x87, 0x87},
176     {0xaf, 0x87, 0xaf},
177     {0xaf, 0x87, 0xd7},
178     {0xaf, 0x87, 0xff},
179     {0xaf, 0xaf, 0x00},
180     {0xaf, 0xaf, 0x5f},
181     {0xaf, 0xaf, 0x87},
182     {0xaf, 0xaf, 0xaf},
183     {0xaf, 0xaf, 0xd7},
184     {0xaf, 0xaf, 0xff},
185     {0xaf, 0xd7, 0x00},
186     {0xaf, 0xd7, 0x5f},
187     {0xaf, 0xd7, 0x87},
188     {0xaf, 0xd7, 0xaf},
189     {0xaf, 0xd7, 0xd7},
190     {0xaf, 0xd7, 0xff},
191     {0xaf, 0xff, 0x00},
192     {0xaf, 0xff, 0x5f},
193     {0xaf, 0xff, 0x87},
194     {0xaf, 0xff, 0xaf},
195     {0xaf, 0xff, 0xd7},
196     {0xaf, 0xff, 0xff},
197     {0xd7, 0x00, 0x00},
198     {0xd7, 0x00, 0x5f},
199     {0xd7, 0x00, 0x87},
200     {0xd7, 0x00, 0xaf},
201     {0xd7, 0x00, 0xd7},
202     {0xd7, 0x00, 0xff},
203     {0xd7, 0x5f, 0x00},
204     {0xd7, 0x5f, 0x5f},
205     {0xd7, 0x5f, 0x87},
206     {0xd7, 0x5f, 0xaf},
207     {0xd7, 0x5f, 0xd7},
208     {0xd7, 0x5f, 0xff},
209     {0xd7, 0x87, 0x00},
210     {0xd7, 0x87, 0x5f},
211     {0xd7, 0x87, 0x87},
212     {0xd7, 0x87, 0xaf},
213     {0xd7, 0x87, 0xd7},
214     {0xd7, 0x87, 0xff},
215     {0xd7, 0xaf, 0x00},
216     {0xd7, 0xaf, 0x5f},
217     {0xd7, 0xaf, 0x87},
218     {0xd7, 0xaf, 0xaf},
219     {0xd7, 0xaf, 0xd7},
220     {0xd7, 0xaf, 0xff},
221     {0xd7, 0xd7, 0x00},
222     {0xd7, 0xd7, 0x5f},
223     {0xd7, 0xd7, 0x87},
224     {0xd7, 0xd7, 0xaf},
225     {0xd7, 0xd7, 0xd7},
226     {0xd7, 0xd7, 0xff},
227     {0xd7, 0xff, 0x00},
228     {0xd7, 0xff, 0x5f},
229     {0xd7, 0xff, 0x87},
230     {0xd7, 0xff, 0xaf},
231     {0xd7, 0xff, 0xd7},
232     {0xd7, 0xff, 0xff},
233     {0xff, 0x00, 0x00},
234     {0xff, 0x00, 0x5f},
235     {0xff, 0x00, 0x87},
236     {0xff, 0x00, 0xaf},
237     {0xff, 0x00, 0xd7},
238     {0xff, 0x00, 0xff},
239     {0xff, 0x5f, 0x00},
240     {0xff, 0x5f, 0x5f},
241     {0xff, 0x5f, 0x87},
242     {0xff, 0x5f, 0xaf},
243     {0xff, 0x5f, 0xd7},
244     {0xff, 0x5f, 0xff},
245     {0xff, 0x87, 0x00},
246     {0xff, 0x87, 0x5f},
247     {0xff, 0x87, 0x87},
248     {0xff, 0x87, 0xaf},
249     {0xff, 0x87, 0xd7},
250     {0xff, 0x87, 0xff},
251     {0xff, 0xaf, 0x00},
252     {0xff, 0xaf, 0x5f},
253     {0xff, 0xaf, 0x87},
254     {0xff, 0xaf, 0xaf},
255     {0xff, 0xaf, 0xd7},
256     {0xff, 0xaf, 0xff},
257     {0xff, 0xd7, 0x00},
258     {0xff, 0xd7, 0x5f},
259     {0xff, 0xd7, 0x87},
260     {0xff, 0xd7, 0xaf},
261     {0xff, 0xd7, 0xd7},
262     {0xff, 0xd7, 0xff},
263     {0xff, 0xff, 0x00},
264     {0xff, 0xff, 0x5f},
265     {0xff, 0xff, 0x87},
266     {0xff, 0xff, 0xaf},
267     {0xff, 0xff, 0xd7},
268     {0xff, 0xff, 0xff},
269 
270     /* GRAY SCALE COLOR(0xe8-0xff) */
271     {0x08, 0x08, 0x08},
272     {0x12, 0x12, 0x12},
273     {0x1c, 0x1c, 0x1c},
274     {0x26, 0x26, 0x26},
275     {0x30, 0x30, 0x30},
276     {0x3a, 0x3a, 0x3a},
277     {0x44, 0x44, 0x44},
278     {0x4e, 0x4e, 0x4e},
279     {0x58, 0x58, 0x58},
280     {0x62, 0x62, 0x62},
281     {0x6c, 0x6c, 0x6c},
282     {0x76, 0x76, 0x76},
283     {0x80, 0x80, 0x80},
284     {0x8a, 0x8a, 0x8a},
285     {0x94, 0x94, 0x94},
286     {0x9e, 0x9e, 0x9e},
287     {0xa8, 0xa8, 0xa8},
288     {0xb2, 0xb2, 0xb2},
289     {0xbc, 0xbc, 0xbc},
290     {0xc6, 0xc6, 0xc6},
291     {0xd0, 0xd0, 0xd0},
292     {0xda, 0xda, 0xda},
293     {0xe4, 0xe4, 0xe4},
294     {0xee, 0xee, 0xee},
295 };
296 #endif
297 
298 static char *color_file = "mlterm/color";
299 static BL_MAP(color_rgb) color_config;
300 static u_int num_changed_256_colors;
301 
302 static struct {
303   u_int is_changed : 1;
304   u_int mark : 7;
305   u_int8_t red;
306   u_int8_t green;
307   u_int8_t blue;
308 
309 } * ext_color_table;
310 static u_int ext_color_mark = 1;
311 
312 static int color_distance_threshold = COLOR_DISTANCE_THRESHOLD;
313 static int use_pseudo_color = 0;
314 
315 /* --- static functions --- */
316 
get_default_rgb(vt_color_t color,u_int8_t * red,u_int8_t * green,u_int8_t * blue)317 static void get_default_rgb(vt_color_t color, /* is 255 or less */
318                             u_int8_t *red, u_int8_t *green, u_int8_t *blue) {
319   if (IS_VTSYS_COLOR(color)) {
320     *red = vtsys_color_rgb_table[color][0];
321     *green = vtsys_color_rgb_table[color][1];
322     *blue = vtsys_color_rgb_table[color][2];
323   } else if (color <= 0xe7) {
324     u_int8_t tmp;
325 
326     tmp = (color - 0x10) % 6;
327     *blue = tmp ? (tmp * 40 + 55) & 0xff : 0;
328 
329     tmp = ((color - 0x10) / 6) % 6;
330     *green = tmp ? (tmp * 40 + 55) & 0xff : 0;
331 
332     tmp = ((color - 0x10) / 36) % 6;
333     *red = tmp ? (tmp * 40 + 55) & 0xff : 0;
334   } else /* if( color >= 0xe8) */
335   {
336     u_int8_t tmp;
337 
338     tmp = (color - 0xe8) * 10 + 8;
339 
340     *blue = tmp;
341     *green = tmp;
342     *red = tmp;
343   }
344 }
345 
get_color_rgb_pair(vt_color_t color)346 static BL_PAIR(color_rgb) get_color_rgb_pair(vt_color_t color) {
347   BL_PAIR(color_rgb) pair;
348 
349   bl_map_get(color_config, color, pair);
350 
351   return pair;
352 }
353 
color_config_set_rgb(vt_color_t color,u_int8_t red,u_int8_t green,u_int8_t blue,u_int8_t alpha)354 static int color_config_set_rgb(vt_color_t color, /* is 255 or less */
355                                 u_int8_t red, u_int8_t green, u_int8_t blue, u_int8_t alpha) {
356   BL_PAIR(color_rgb) pair;
357   int result;
358   rgb_t rgb;
359 
360   rgb.red = red;
361   rgb.green = green;
362   rgb.blue = blue;
363   rgb.alpha = alpha;
364 
365   if ((pair = get_color_rgb_pair(color))) {
366     u_int8_t r;
367     u_int8_t g;
368     u_int8_t b;
369 
370     if (pair->value.red == red && pair->value.green == green && pair->value.blue == blue &&
371         pair->value.alpha == alpha) {
372 /* Not changed */
373 
374 #ifdef DEBUG
375       bl_debug_printf(BL_DEBUG_TAG " color %d rgb(%02x%02x%02x%02x) not changed.\n", color, red,
376                       green, blue, alpha);
377 #endif
378 
379       return 0;
380     }
381 
382     get_default_rgb(color, &r, &g, &b);
383 
384     if (r == red && g == green && b == blue && alpha == 0xff) {
385       if (IS_256_COLOR(color)) {
386         num_changed_256_colors--;
387       }
388 
389       bl_map_erase_simple(result, color_config, color);
390     } else {
391       pair->value = rgb;
392     }
393 
394     return 1;
395   } else {
396     u_int8_t r;
397     u_int8_t g;
398     u_int8_t b;
399 
400     /*
401      * The same rgb as the default is rejected in 256 color.
402      * The same rgb as the default is not rejected in sys color
403      * for backward compatibility with 3.1.5 or before. (If rejected,
404      * mlterm -fg hl_white doesn't work even if hl_white is defined
405      * in ~/.mlterm/color.)
406      */
407 
408     if (IS_256_COLOR(color)) {
409       if (!vt_get_color_rgba(color, &r, &g, &b, NULL)) {
410         return 0;
411       }
412 
413       if (red == r && green == g && blue == b && alpha == 0xff) {
414 /* Not changed */
415 
416 #ifdef DEBUG
417         bl_debug_printf(BL_DEBUG_TAG " color %d rgb(%02x%02x%02x%02x) not changed.\n", color, red,
418                         green, blue, alpha);
419 #endif
420 
421         return 0;
422       }
423 #ifdef DEBUG
424       else {
425         bl_debug_printf(BL_DEBUG_TAG " color %d rgb(%02x%02x%02x) changed => %02x%02x%02x.\n",
426                         color, r, g, b, red, green, blue);
427       }
428 #endif
429 
430       num_changed_256_colors++;
431     }
432 
433     bl_map_set(result, color_config, color, rgb);
434 
435     return result;
436   }
437 }
438 
color_config_get_rgb(vt_color_t color,u_int8_t * red,u_int8_t * green,u_int8_t * blue,u_int8_t * alpha)439 static int color_config_get_rgb(vt_color_t color, u_int8_t *red, u_int8_t *green, u_int8_t *blue,
440                                 u_int8_t *alpha /* can be NULL */
441                                 ) {
442   BL_PAIR(color_rgb) pair;
443 
444   if ((pair = get_color_rgb_pair(color)) == NULL) {
445     return 0;
446   }
447 
448   *red = pair->value.red;
449   *blue = pair->value.blue;
450   *green = pair->value.green;
451   if (alpha) {
452     *alpha = pair->value.alpha;
453   }
454 
455 #ifdef __DEBUG
456   bl_debug_printf(BL_DEBUG_TAG " %d's rgb => %d %d %d\n", color, *red, *blue, *green);
457 #endif
458 
459   return 1;
460 }
461 
parse_conf(char * color_name,char * rgb)462 static int parse_conf(char *color_name, char *rgb) {
463   u_int8_t red;
464   u_int8_t green;
465   u_int8_t blue;
466   u_int8_t alpha;
467   vt_color_t color;
468 
469   /*
470    * Illegal color name is rejected.
471    */
472   if ((color = vt_get_color(color_name)) == VT_UNKNOWN_COLOR) {
473     return 0;
474   }
475 
476   if (*rgb == '\0') {
477     if (!get_color_rgb_pair(color)) {
478       return 0;
479     } else {
480       int result;
481 
482       if (IS_256_COLOR(color)) {
483         num_changed_256_colors--;
484       }
485 
486       bl_map_erase_simple(result, color_config, color);
487 
488       return 1;
489     }
490   } else {
491     /* XXX "red", "green", "blue" and so on are available for rgb */
492     vt_color_t rgb_color = vt_get_color(rgb);
493 
494     if ((rgb_color != VT_UNKNOWN_COLOR) ?
495         !vt_get_color_rgba(color, &red, &green, &blue, &alpha) :
496         !vt_color_parse_rgb_name(&red, &green, &blue, &alpha, rgb)) {
497 #ifdef DEBUG
498       bl_warn_printf(BL_DEBUG_TAG " illegal rgblist format (%s,%s)\n", color_name, rgb);
499 #endif
500 
501       return 0;
502     }
503   }
504 
505 #ifdef __DEBUG
506   bl_debug_printf("%s(%d) = red %x green %x blue %x\n", color_name, color, red, green, blue);
507 #endif
508 
509   return color_config_set_rgb(color, red, green, blue, alpha);
510 }
511 
read_conf(const char * filename)512 static void read_conf(const char *filename) {
513   bl_file_t *from;
514   char *color_name;
515   char *rgb;
516 
517   if (!(from = bl_file_open(filename, "r"))) {
518 #ifdef DEBUG
519     bl_warn_printf(BL_DEBUG_TAG " %s couldn't be opened.\n", filename);
520 #endif
521 
522     return;
523   }
524 
525   while (bl_conf_io_read(from, &color_name, &rgb)) {
526     parse_conf(color_name, rgb);
527   }
528 
529   bl_file_close(from);
530 }
531 
532 /* --- global functions --- */
533 
vt_set_color_mode(const char * mode)534 void vt_set_color_mode(const char *mode) {
535   if (strcmp(mode, "256") == 0) {
536     color_distance_threshold = COLOR_DISTANCE_THRESHOLD;
537     use_pseudo_color = 1;
538   } else {
539     if (strcmp(mode, "true") == 0) {
540       color_distance_threshold = 40; /* 9 + 30 + 1 */
541     } else                           /* if( strcmp( mode , "high") == 0 */
542     {
543       color_distance_threshold = COLOR_DISTANCE_THRESHOLD;
544     }
545     use_pseudo_color = 0;
546   }
547 }
548 
vt_get_color_mode(void)549 char *vt_get_color_mode(void) {
550   if (use_pseudo_color) {
551     return "256";
552   } else if (color_distance_threshold == 40) {
553     return "true";
554   } else {
555     return "high";
556   }
557 }
558 
vt_color_config_init(void)559 void vt_color_config_init(void) {
560   char *rcpath;
561 
562   bl_map_new_with_size(vt_color_t, rgb_t, color_config, bl_map_hash_int, bl_map_compare_int, 16);
563 
564   if ((rcpath = bl_get_sys_rc_path(color_file))) {
565     read_conf(rcpath);
566     free(rcpath);
567   }
568 
569   if ((rcpath = bl_get_user_rc_path(color_file))) {
570     read_conf(rcpath);
571     free(rcpath);
572   }
573 }
574 
vt_color_config_final(void)575 void vt_color_config_final(void) {
576   bl_map_destroy(color_config);
577   color_config = NULL;
578 }
579 
580 /*
581  * Return value 0 means customization failed or not changed.
582  * Return value -1 means saving failed.
583  */
vt_customize_color_file(char * color,char * rgb,int save)584 int vt_customize_color_file(char *color, char *rgb, int save) {
585   if (!color_config || !parse_conf(color, rgb)) {
586     return 0;
587   }
588 
589   if (save) {
590     char *path;
591     bl_conf_write_t *conf;
592 
593     if ((path = bl_get_user_rc_path(color_file)) == NULL) {
594       return -1;
595     }
596 
597     conf = bl_conf_write_open(path);
598     free(path);
599     if (conf == NULL) {
600       return -1;
601     }
602 
603     bl_conf_io_write(conf, color, rgb);
604 
605     bl_conf_write_close(conf);
606   }
607 
608   return 1;
609 }
610 
vt_get_color_name(vt_color_t color)611 char *vt_get_color_name(vt_color_t color) {
612   if (IS_VTSYS_COLOR(color)) {
613     if (color & VT_BOLD_COLOR_MASK) {
614       return color_name_table[color & ~VT_BOLD_COLOR_MASK];
615     } else {
616       return color_name_table[color] + 3;
617     }
618   } else if (IS_256_COLOR(color)) {
619     /* XXX Not reentrant */
620     static char color_name_256[4];
621 
622     snprintf(color_name_256, sizeof(color_name_256), "%d", color);
623 
624     return color_name_256;
625   } else {
626     return NULL;
627   }
628 }
629 
vt_get_color(const char * name)630 vt_color_t vt_get_color(const char *name) {
631   vt_color_t color;
632 
633   if (sscanf(name, "%d", (int*)&color) == 1) {
634     if (IS_VTSYS256_COLOR(color)) {
635       return color;
636     }
637   }
638 
639   for (color = VT_BLACK; color <= VT_WHITE; color++) {
640     if (strcmp(name, color_name_table[color] + 3) == 0) {
641       return color;
642     } else if (strcmp(name, color_name_table[color]) == 0) {
643       return color | VT_BOLD_COLOR_MASK;
644     }
645   }
646 
647   return VT_UNKNOWN_COLOR;
648 }
649 
vt_get_color_rgba(vt_color_t color,u_int8_t * red,u_int8_t * green,u_int8_t * blue,u_int8_t * alpha)650 int vt_get_color_rgba(vt_color_t color, u_int8_t *red, u_int8_t *green, u_int8_t *blue,
651                       u_int8_t *alpha /* can be NULL */
652                       ) {
653   if (!IS_VALID_COLOR_EXCEPT_SPECIAL_COLORS(color)) {
654     return 0;
655   }
656 
657   if (IS_EXT_COLOR(color)) {
658     if (!ext_color_table || ext_color_table[EXT_COLOR_TO_INDEX(color)].mark == 0) {
659       return 0;
660     }
661 
662     *red = ext_color_table[EXT_COLOR_TO_INDEX(color)].red;
663     *green = ext_color_table[EXT_COLOR_TO_INDEX(color)].green;
664     *blue = ext_color_table[EXT_COLOR_TO_INDEX(color)].blue;
665   } else if (color_config && color_config_get_rgb(color, red, green, blue, alpha)) {
666     return 1;
667   } else {
668     get_default_rgb(color, red, green, blue);
669   }
670 
671   if (alpha) {
672     *alpha = 0xff;
673   }
674 
675   return 1;
676 }
677 
vt_color_parse_rgb_name(u_int8_t * red,u_int8_t * green,u_int8_t * blue,u_int8_t * alpha,const char * name)678 int vt_color_parse_rgb_name(u_int8_t *red, u_int8_t *green, u_int8_t *blue, u_int8_t *alpha,
679                             const char *name) {
680   int r;
681   int g;
682   int b;
683   int a;
684   size_t name_len;
685   char *format;
686   int has_alpha;
687   int long_color;
688 
689   a = 0xffff;
690   has_alpha = 0;
691   long_color = 0;
692 
693   name_len = strlen(name);
694 
695   if (name_len >= 14) {
696     /*
697      * XXX
698      * "RRRR-GGGG-BBBB" length is 14, but 2.4.0 or before accepts
699      * "RRRR-GGGG-BBBB....."(trailing any characters) format and
700      * what is worse "RRRR-GGGG-BBBB;" appears in etc/color sample file.
701      * So, more than 14 length is also accepted for backward compatiblity.
702      */
703     if (sscanf(name, "%4x-%4x-%4x", &r, &g, &b) == 3) {
704       goto end;
705     } else if (name_len == 16) {
706       format = "rgba:%2x/%2x/%2x/%2x";
707       has_alpha = 1;
708     } else if (name_len == 17) {
709       format = "#%4x%4x%4x%4x";
710       has_alpha = 1;
711       long_color = 1;
712     } else if (name_len == 18) {
713       format = "rgb:%4x/%4x/%4x";
714       long_color = 1;
715     } else if (name_len == 24) {
716       format = "rgba:%4x/%4x/%4x/%4x";
717       long_color = 1;
718       has_alpha = 1;
719     } else {
720       goto fail;
721     }
722   } else {
723     if (name_len == 7) {
724       format = "#%2x%2x%2x";
725     } else if (name_len == 9) {
726       format = "#%2x%2x%2x%2x";
727       has_alpha = 1;
728     } else if (name_len == 12) {
729       format = "rgb:%2x/%2x/%2x";
730     } else if (name_len == 13) {
731       format = "#%4x%4x%4x";
732       long_color = 1;
733     } else {
734       goto fail;
735     }
736   }
737 
738   if (sscanf(name, format, &r, &g, &b, &a) != (3 + has_alpha)) {
739     goto fail;
740   }
741 
742 end:
743   if (long_color) {
744     *red = (r >> 8) & 0xff;
745     *green = (g >> 8) & 0xff;
746     *blue = (b >> 8) & 0xff;
747     *alpha = (a >> 8) & 0xff;
748   } else {
749     *red = r;
750     *green = g;
751     *blue = b;
752     *alpha = a & 0xff;
753   }
754 
755 #ifdef __DEBUG
756   bl_debug_printf(BL_DEBUG_TAG " %s => %x %x %x %x\n", name, *red, *green, *blue, *alpha);
757 #endif
758 
759   return 1;
760 
761 fail:
762 #if 1
763   /* Backward compatibility with mlterm-3.1.5 or before. */
764   if (color_config) {
765     /*
766      * If 'name' is a color name defined in ~/.mlterm/color, its rgb is returned.
767      * (for ui_load_named_xcolor())
768      */
769     vt_color_t color;
770 
771     if ((color = vt_get_color(name)) != VT_UNKNOWN_COLOR &&
772         color_config_get_rgb(color, red, green, blue, alpha)) {
773       return 1;
774     }
775   }
776 #endif
777 
778   return 0;
779 }
780 
vt_color_force_linear_search(int flag)781 void vt_color_force_linear_search(int flag) {
782   if (flag) {
783     num_changed_256_colors++;
784   } else {
785     num_changed_256_colors--;
786   }
787 }
788 
789 /*
790  * Return the number of colors which should be searched after this function.
791  */
vt_get_closest_256_color(vt_color_t * closest,u_int * min_diff,u_int8_t red,u_int8_t green,u_int8_t blue,int threshold)792 u_int vt_get_closest_256_color(vt_color_t *closest, u_int *min_diff, u_int8_t red, u_int8_t green,
793                                u_int8_t blue, int threshold) {
794   int r, g, b;
795   int tmp;
796   vt_color_t color;
797   u_int8_t rgb[3];
798   u_int diff;
799   int diff_r, diff_g, diff_b;
800   int count;
801   int num;
802 
803   if (num_changed_256_colors > 0) {
804     return 256;
805   }
806 
807   /*
808    * 0 - 47 => 0
809    * 48 - 114 => 1
810    * 115 - 154 => 2
811    * ...
812    */
813   tmp = (red <= 114 ? (red >= 48) : ((red - 55) + 20) / 40);
814   r = tmp ? (tmp * 40 + 55) & 0xff : 0;
815   color = tmp * 36;
816   tmp = (green <= 114 ? (green >= 48) : ((green - 55) + 20) / 40);
817   g = tmp ? (tmp * 40 + 55) & 0xff : 0;
818   color += tmp * 6;
819   tmp = (blue <= 114 ? (blue >= 48) : ((blue - 55) + 20) / 40);
820   b = tmp ? (tmp * 40 + 55) & 0xff : 0;
821   color += tmp;
822   /* lazy color-space conversion */
823   diff_r = red - r;
824   diff_g = green - g;
825   diff_b = blue - b;
826   diff = COLOR_DISTANCE(diff_r, diff_g, diff_b);
827   if (diff < *min_diff) {
828     *min_diff = diff;
829     *closest = color + 0x10;
830     if (diff < threshold) {
831       return 0;
832     }
833   }
834 
835   num = 1;
836   rgb[0] = red;
837 
838   if (red != green) {
839     rgb[num++] = green;
840   }
841 
842   if (red != blue && green != blue) {
843     rgb[num++] = blue;
844   }
845 
846   for (count = 0; count < num; count++) {
847     tmp = (rgb[count] >= 233 ? 23 : (rgb[count] <= 12 ? 0 : ((rgb[count] - 8) + 5) / 10));
848     r = g = b = tmp * 10 + 8;
849     /* lazy color-space conversion */
850     diff_r = red - r;
851     diff_g = green - g;
852     diff_b = blue - b;
853     diff = COLOR_DISTANCE(diff_r, diff_g, diff_b);
854     if (diff < *min_diff) {
855       *min_diff = diff;
856       *closest = tmp + 0xe8;
857       if (diff < threshold) {
858         return 0;
859       }
860     }
861   }
862 
863   return 16;
864 }
865 
vt_get_closest_color(u_int8_t red,u_int8_t green,u_int8_t blue)866 vt_color_t vt_get_closest_color(u_int8_t red, u_int8_t green, u_int8_t blue) {
867   vt_color_t closest = VT_UNKNOWN_COLOR;
868   vt_color_t color;
869   u_int linear_search_max;
870   u_int min = 0xffffff;
871   vt_color_t oldest_color;
872   u_int oldest_mark;
873   u_int max = 0;
874 
875 #ifdef __DEBUG
876   vt_color_t hit_closest = VT_UNKNOWN_COLOR;
877   vt_get_closest_256_color(&hit_closest, &min, red, green, blue);
878   min = 0xffffff;
879 
880   for (color = 16; color < 256; color++)
881 #else
882   if ((linear_search_max = vt_get_closest_256_color(&closest, &min, red, green, blue,
883                                                     color_distance_threshold)) == 0) {
884     return closest;
885   }
886 
887   for (color = 0; color < linear_search_max; color++)
888 #endif
889   {
890     u_int8_t r;
891     u_int8_t g;
892     u_int8_t b;
893     u_int8_t a;
894 
895     if (vt_get_color_rgba(color, &r, &g, &b, &a) && a == 0xff) {
896       u_int diff;
897       int diff_r, diff_g, diff_b;
898 
899       /* lazy color-space conversion */
900       diff_r = red - r;
901       diff_g = green - g;
902       diff_b = blue - b;
903       diff = COLOR_DISTANCE(diff_r, diff_g, diff_b);
904       if (diff < min) {
905         min = diff;
906         closest = color;
907         if (diff < color_distance_threshold) {
908 #ifdef __DEBUG
909           /*
910            * XXX
911            * The result of linear search of boundary values
912            * (115, 195 etc) is different from that of
913            * vt_get_closest_256_color() of it.
914            */
915           if (closest != hit_closest) {
916             bl_debug_printf("ERROR %x %x %x -> %x<=>%x\n", red, green, blue, closest, hit_closest);
917           }
918 #endif
919 
920           return closest;
921         }
922       }
923     }
924   }
925 
926 #ifdef __DEBUG
927   /*
928    * XXX
929    * The result of linear search of boundary values (115, 195 etc) is
930    * different from that of vt_get_closest_256_color() of it.
931    */
932   if (closest != hit_closest) {
933     bl_debug_printf("ERROR %x %x %x -> %x<=>%x\n", red, green, blue, closest, hit_closest);
934   }
935 #endif
936 
937   if (use_pseudo_color ||
938       (!ext_color_table && !(ext_color_table = calloc(MAX_EXT_COLORS, sizeof(*ext_color_table))))) {
939     return closest;
940   }
941 
942   if ((oldest_mark = ext_color_mark / 2) == MAX_EXT_COLORS / 2) {
943     oldest_mark = 1;
944   } else {
945     oldest_mark++;
946   }
947 
948   if (ext_color_mark == MAX_EXT_COLORS) {
949     ext_color_mark = 2;
950   } else {
951     ext_color_mark++;
952   }
953 
954   color = 0;
955   while (ext_color_table[color].mark) {
956     u_int diff;
957     int diff_r, diff_g, diff_b;
958 
959     /* lazy color-space conversion */
960     diff_r = red - ext_color_table[color].red;
961     diff_g = green - ext_color_table[color].green;
962     diff_b = blue - ext_color_table[color].blue;
963     diff = COLOR_DISTANCE(diff_r, diff_g, diff_b);
964     if (diff < min) {
965       min = diff;
966       if (diff < color_distance_threshold) {
967         /* Set new mark */
968         ext_color_table[color].mark = ext_color_mark / 2;
969 
970 #ifdef __DEBUG
971         bl_debug_printf(BL_DEBUG_TAG " Use cached ext color %x\n", INDEX_TO_EXT_COLOR(color));
972 #endif
973 
974         return INDEX_TO_EXT_COLOR(color);
975       }
976     }
977 
978     if (max == MAX_EXT_COLORS / 2) {
979       /* do nothing */
980     } else if (ext_color_table[color].mark == oldest_mark) {
981       max = MAX_EXT_COLORS / 2;
982       oldest_color = color;
983     } else {
984       if (ext_color_table[color].mark < oldest_mark) {
985         diff = oldest_mark - ext_color_table[color].mark;
986       } else /* if( ext_color_table[color].mark > oldest_mark) */
987       {
988         diff = oldest_mark + (MAX_EXT_COLORS / 2) - ext_color_table[color].mark;
989       }
990 
991       if (diff > max) {
992         max = diff;
993         oldest_color = color;
994       }
995     }
996 
997     if (++color == MAX_EXT_COLORS) {
998       color = oldest_color;
999       ext_color_table[color].is_changed = 1;
1000 
1001 #ifdef DEBUG
1002       bl_debug_printf(BL_DEBUG_TAG " Destroy ext color %x mark %x\n", INDEX_TO_EXT_COLOR(color),
1003                       ext_color_table[color].mark);
1004 #endif
1005 
1006       break;
1007     }
1008   }
1009 
1010   ext_color_table[color].mark = ext_color_mark / 2;
1011   ext_color_table[color].red = red;
1012   ext_color_table[color].green = green;
1013   ext_color_table[color].blue = blue;
1014 
1015 #ifdef DEBUG
1016   bl_debug_printf(BL_DEBUG_TAG "New ext color %.2x: r%.2x g%x b%.2x mark %x\n",
1017                   INDEX_TO_EXT_COLOR(color), red, green, blue, ext_color_table[color].mark);
1018 #endif
1019 
1020   return INDEX_TO_EXT_COLOR(color);
1021 }
1022 
vt_ext_color_is_changed(vt_color_t color)1023 int vt_ext_color_is_changed(vt_color_t color) {
1024   if (ext_color_table[EXT_COLOR_TO_INDEX(color)].is_changed) {
1025     ext_color_table[EXT_COLOR_TO_INDEX(color)].is_changed = 0;
1026 
1027     return 1;
1028   } else {
1029     return 0;
1030   }
1031 }
1032