xref: /haiku/src/apps/terminal/Colors.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
1 /*
2  * Copyright 2010-2013, Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stefano Ceccherini, stefano.ceccherini@gmail.com
7  *		Siarzhuk Zharski, zharik@gmx.li
8  */
9 
10 
11 #include "Colors.h"
12 
13 #include <ctype.h>
14 #include <stdio.h>
15 #include <strings.h>
16 
17 #include <Application.h>
18 #include <Catalog.h>
19 #include <Resources.h>
20 
21 
22 #undef B_TRANSLATION_CONTEXT
23 #define B_TRANSLATION_CONTEXT "Terminal colors scheme"
24 
25 
26 // Standard colors
27 const rgb_color kBlack = { 0, 0, 0, 255 };
28 const rgb_color kGreen = { 0, 255, 0, 255 };
29 const rgb_color kWhite = { 255, 255, 255, 255 };
30 const rgb_color kYellow = { 255, 255, 0, 255 };
31 
32 const struct ansi_color_scheme kAnsiColorSchemeDefault = {
33 	{ 40, 40, 40, 255 },
34 	{ 204, 0, 0, 255 },
35 	{ 78, 154, 6, 255 },
36 	{ 218, 168, 0, 255 },
37 	{ 51, 102, 152, 255 },
38 	{ 115, 68, 123, 255 },
39 	{ 6, 152, 154, 255 },
40 	{ 245, 245, 245, 255 }
41 };
42 
43 /* The 'bright' colors palette, relates to the existing
44    PREF_ANSI_<color>_HCOLOR macros used elsewhere */
45 const struct ansi_color_scheme kAnsiColorHSchemeDefault = {
46 	{ 128, 128, 128, 255 },
47 	{ 255, 0, 0, 255 },
48 	{ 0, 255, 0, 255 },
49 	{ 255, 255, 0, 255 },
50 	{ 0, 0, 255, 255 },
51 	{ 255, 0, 255, 255 },
52 	{ 0, 255, 255, 255 },
53 	{ 255, 255, 255, 255 }
54 };
55 
56 const struct color_scheme kColorSchemeDefault = {
57 	B_TRANSLATE("Default"),
58 	kBlack,
59 	kWhite,
60 	kWhite,
61 	kBlack,
62 	kWhite,
63 	kBlack,
64 	kAnsiColorSchemeDefault,
65 	kAnsiColorHSchemeDefault
66 };
67 
68 const struct color_scheme kColorSchemeBlue = {
69 	B_TRANSLATE("Blue"),
70 	kYellow,
71 	{ 0, 0, 139, 255 },
72 	kBlack,
73 	kYellow,
74 	kBlack,
75 	{ 0, 139, 139, 255 },
76 	kAnsiColorSchemeDefault,
77 	kAnsiColorHSchemeDefault
78 };
79 
80 const struct color_scheme kColorSchemeMidnight = {
81 	B_TRANSLATE("Midnight"),
82 	kWhite,
83 	kBlack,
84 	kBlack,
85 	kWhite,
86 	kBlack,
87 	kWhite,
88 	kAnsiColorSchemeDefault,
89 	kAnsiColorHSchemeDefault
90 };
91 
92 const struct color_scheme kColorSchemeProfessional = {
93 	B_TRANSLATE("Professional"),
94 	kWhite,
95 	{ 8, 8, 8, 255 },
96 	{ 50, 50, 50, 255 },
97 	kWhite,
98 	kWhite,
99 	{ 50, 50, 50, 255 },
100 	kAnsiColorSchemeDefault,
101 	kAnsiColorHSchemeDefault
102 };
103 
104 const struct color_scheme kColorSchemeRetro = {
105 	B_TRANSLATE("Retro"),
106 	kGreen,
107 	kBlack,
108 	kBlack,
109 	kGreen,
110 	kBlack,
111 	kGreen,
112 	kAnsiColorSchemeDefault,
113 	kAnsiColorHSchemeDefault
114 };
115 
116 const struct color_scheme kColorSchemeSlate = {
117 	B_TRANSLATE("Slate"),
118 	kWhite,
119 	{ 20, 20, 28, 255 },
120 	{ 70, 70, 70, 255 },
121 	{ 255, 200, 0, 255 },
122 	kWhite,
123 	{ 70, 70, 70, 255 },
124 	kAnsiColorSchemeDefault,
125 	kAnsiColorHSchemeDefault
126 };
127 
128 const struct color_scheme kColorSchemeSolarizedDark = {
129 	B_TRANSLATE("Solarized Dark"),
130 	{ 131, 148, 150, 255 },
131 	{ 0, 43, 54, 255 },
132 	{ 0, 46, 57, 255 },
133 	{ 120, 137, 139, 255 },
134 	{ 238, 232, 213, 255 },
135 	{ 88, 110, 117, 255 },
136 	{
137 		{ 7, 54, 66, 255 },
138 		{ 220, 50, 47, 255 },
139 		{ 133, 153, 0, 255 },
140 		{ 181, 137, 0, 255 },
141 		{ 38, 139, 210, 255 },
142 		{ 211, 54, 130, 255 },
143 		{ 42, 161, 152, 255 },
144 		{ 238, 232, 213, 255 }
145 	},
146 	{
147 		{ 0, 43, 54, 255 },
148 		{ 203, 75, 22, 255 },
149 		{ 88, 110, 117, 255 },
150 		{ 101, 123, 131, 255 },
151 		{ 131, 148, 150, 255 },
152 		{ 108, 113, 196, 255 },
153 		{ 147, 161, 161, 255 },
154 		{ 253, 246, 227, 255 }
155 	}
156 };
157 
158 const struct color_scheme kColorSchemeSolarizedLight = {
159 	B_TRANSLATE("Solarized Light"),
160 	{ 101, 123, 131, 255 },
161 	{ 253, 246, 227, 255 },
162 	{ 236, 228, 207, 255 },
163 	{ 90, 112, 120, 255 },
164 	{ 147, 161, 161, 255 },
165 	{ 7, 54, 66, 255 },
166 	{
167 		{ 7, 54, 66, 255 },
168 		{ 220, 50, 47, 255 },
169 		{ 133, 153, 0, 255 },
170 		{ 181, 137, 0, 255 },
171 		{ 38, 139, 210, 255 },
172 		{ 211, 54, 130, 255 },
173 		{ 42, 161, 152, 255 },
174 		{ 238, 232, 213, 255 }
175 	},
176 	{
177 		{ 0, 43, 54, 255 },
178 		{ 203, 75, 22, 255 },
179 		{ 88, 110, 117, 255 },
180 		{ 101, 123, 131, 255 },
181 		{ 131, 148, 150, 255 },
182 		{ 108, 113, 196, 255 },
183 		{ 147, 161, 161, 255 },
184 		{ 253, 246, 227, 255 }
185 	}
186 };
187 
188 const struct color_scheme kColorSchemeRelaxed = {
189 	B_TRANSLATE("Relaxed"),
190 	{ 217, 217, 217, 255 },
191 	{ 53, 58, 68, 255 },
192 	{ 27, 27, 27, 255 },
193 	{ 255, 203, 0, 255 },
194 	{ 53, 58, 68, 255 },
195 	{ 217, 217, 217, 255 },
196 	{
197 		{ 21, 21, 21, 255 },
198 		{ 188, 86, 83, 255 },
199 		{ 144, 157, 99, 255 },
200 		{ 235, 193, 122, 255 },
201 		{ 106, 135, 153, 255 },
202 		{ 176, 102, 152, 255 },
203 		{ 201, 223, 255, 255 },
204 		{ 217, 217, 217, 255 }
205 	},
206 	{
207 		{ 99, 99, 99, 255 },
208 		{ 188, 86, 83, 255 },
209 		{ 160, 172, 119, 255 },
210 		{ 235, 192, 122, 255 },
211 		{ 126, 170, 199, 255 },
212 		{ 176, 102, 152, 255 },
213 		{ 172, 187, 208, 255 },
214 		{ 247, 247, 247, 255 }
215 	}
216 };
217 
218 struct color_scheme gCustomColorScheme = {
219 	B_TRANSLATE("Custom")
220 };
221 
222 const color_scheme* gPredefinedColorSchemes[] = {
223 	&kColorSchemeDefault,
224 	&kColorSchemeBlue,
225 	&kColorSchemeMidnight,
226 	&kColorSchemeProfessional,
227 	&kColorSchemeRetro,
228 	&kColorSchemeSlate,
229 	&kColorSchemeSolarizedDark,
230 	&kColorSchemeSolarizedLight,
231 	&kColorSchemeRelaxed,
232 	&gCustomColorScheme,
233 	NULL
234 };
235 
236 
237 bool
238 ansi_color_scheme::operator==(const ansi_color_scheme& scheme)
239 {
240 	return black == scheme.black
241 		&& red == scheme.red
242 		&& green == scheme.green
243 		&& yellow == scheme.yellow
244 		&& blue == scheme.blue
245 		&& magenta == scheme.magenta
246 		&& cyan == scheme.cyan
247 		&& white == scheme.white;
248 }
249 
250 
251 bool
252 color_scheme::operator==(const color_scheme& scheme)
253 {
254 	return text_fore_color == scheme.text_fore_color
255 		&& text_back_color == scheme.text_back_color
256 		&& cursor_fore_color == scheme.cursor_fore_color
257 		&& cursor_back_color == scheme.cursor_back_color
258 		&& select_fore_color == scheme.select_fore_color
259 		&& select_back_color == scheme.select_back_color
260 		&& ansi_colors == scheme.ansi_colors
261 		&& ansi_colors_h == scheme.ansi_colors_h;
262 }
263 
264 // #pragma mark XColorsTable implementation
265 
266 XColorsTable gXColorsTable;
267 
268 
269 XColorsTable::XColorsTable()
270 		:
271 		fTable(NULL),
272 		fCount(0)
273 {
274 }
275 
276 
277 XColorsTable::~XColorsTable()
278 {
279 	// fTable as result of LoadResource call and owned
280 	// by BApplication so no need to free it explicitly
281 	fTable = NULL;
282 	fCount = 0;
283 }
284 
285 
286 status_t
287 XColorsTable::LookUpColor(const char* name, rgb_color* color)
288 {
289 	if (name == NULL || color == NULL)
290 		return B_BAD_DATA;
291 
292 	size_t length = strlen(name);
293 
294 	// first check for 'rgb:xx/xx/xx' or '#xxxxxx'-encoded color names
295 	u_int r = 0, g = 0, b = 0;
296 	float c = 0, m = 0, y = 0, k = 0;
297 	if ((length == 12 && sscanf(name, "rgb:%02x/%02x/%02x", &r, &g, &b) == 3)
298 		|| (length == 7 && sscanf(name, "#%02x%02x%02x", &r, &g, &b) == 3)) {
299 		color->set_to(r, g, b);
300 		return B_OK;
301 	// then check for 'rgb:xxxx/xxxx/xxxx' or '#xxxxxxxxxxxx'-encoded color names
302 	} else if ((length == 18 && sscanf(name, "rgb:%04x/%04x/%04x", &r, &g, &b) == 3)
303 		|| (length == 13 && sscanf(name, "#%04x%04x%04x", &r, &g, &b) == 3)) {
304 		color->set_to(r >> 8, g >> 8, b >> 8);
305 		return B_OK;
306 	// then check for 'cmyk:c.c/m.m/y.y/k.k' or 'cmy:c.c/m.m/y.y'-encoded color names
307 	} else if (sscanf(name, "cmyk:%f/%f/%f/%f", &c, &m, &y, &k) == 4
308 		|| sscanf(name, "cmy:%f/%f/%f", &c, &m, &y) == 3) {
309 		if (c >= 0 && m >= 0 && y >= 0 && k >= 0
310 			&& c <= 1 && m <= 1 && y <= 1 && k <= 1) {
311 			color->set_to((1 - c) * (1 - k) * 255,
312 				(1 - m) * (1 - k) * 255,
313 				(1 - y) * (1 - k) * 255);
314 			return B_OK;
315 		}
316 	}
317 
318 	// then search the X11 rgb table
319 	if (fTable == NULL) {
320 		status_t result = _LoadXColorsTable();
321 		if (result != B_OK)
322 			return result;
323 	}
324 
325 	// use binary search to lookup color name hash in table
326 	int left  = -1;
327 	int right = fCount;
328 	uint32 hash = _HashName(name);
329 	while ((right - left) > 1) {
330 		int i = (left + right) / 2;
331 		((fTable[i].hash < hash) ? left : right) = i;
332 	}
333 
334 	if (fTable[right].hash == hash) {
335 		*color = fTable[right].color;
336 		return B_OK;
337 	}
338 
339 	return B_NAME_NOT_FOUND;
340 }
341 
342 
343 status_t
344 XColorsTable::_LoadXColorsTable()
345 {
346 	BResources* res = BApplication::AppResources();
347 	if (res == NULL)
348 		return B_ERROR;
349 
350 	size_t size = 0;
351 	fTable = (_XColorEntry*)res->LoadResource(B_RAW_TYPE, "XColorsTable", &size);
352 	if (fTable == NULL || size < sizeof(_XColorEntry)) {
353 		fTable = NULL;
354 		return B_ENTRY_NOT_FOUND;
355 	}
356 
357 	fCount = size / sizeof(_XColorEntry);
358 	return B_OK;
359 }
360 
361 
362 uint32
363 XColorsTable::_HashName(const char* name)
364 {
365 	uint32 hash = 0;
366 	uint32 g = 0;
367 
368 	// Using modified P.J.Weinberger hash algorithm
369 	// Spaces are purged out, characters are upper-cased
370 	// 'E' replaced with 'A' (to join 'grey' and 'gray' variations)
371 	for (const char* p = name; *p; p++) {
372 		int ch = toupper(*p);
373 		switch (ch) {
374 		case ' ':
375 			break;
376 		case 'E':
377 			ch = 'A';
378 		default:
379 			hash = (hash << 4) + (ch & 0xFF);
380 			g = hash & 0xf0000000;
381 			if (g != 0) {
382 				hash ^= g >> 24;
383 				hash ^= g;
384 			}
385 			break;
386 		}
387 	}
388 
389 	return hash;
390 }
391