1 /* -*-c-*- */
2 /*
3  * Around 12/20/99 we did the 3rd rewrite of the shadow/hilite stuff.
4  * (That I know about (dje).
5  * The first stuff I saw just applied a percentage.
6  * Then we got some code from SCWM.
7  * This stuff comes from "Visual.c" which is part of Lesstif.
8  * Here's their copyright:
9  *
10  * Copyright (C) 1995 Free Software Foundation, Inc.
11  *
12  * This file is part of the GNU LessTif Library.
13  *
14  * This library is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU Library General Public
16  * License as published by the Free Software Foundation; either
17  * version 2 of the License, or (at your option) any later version.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * Library General Public License for more details.
23  *
24  * You should have received a copy of the GNU Library General Public
25  * License along with this library; if not, see:
26  * <http://www.gnu.org/licenses/>
27  *
28  * The routine at the bottom "pixel_to_color_string" was not from Lesstif.
29  *
30  * Port by Dan Espen, no additional copyright
31  */
32 
33 #include "config.h"                     /* must be first */
34 
35 #include <stdio.h>
36 #include <X11/Xproto.h>                 /* for X functions in general */
37 #include "fvwmlib.h"                    /* prototype GetShadow GetHilit */
38 #include "Parse.h"
39 #include "Colorset.h"
40 #include "PictureBase.h"
41 #include "PictureUtils.h"
42 #include "ColorUtils.h"
43 
44 #define PCT_BRIGHTNESS                  (6 * 0xffff / 100)
45 
46 /* How much lighter/darker to make things in default routine */
47 
48 #define PCT_DARK_BOTTOM         70      /* lighter (less dark, actually) */
49 #define PCT_DARK_TOP            50      /* lighter */
50 #define PCT_LIGHT_BOTTOM        55      /* darker */
51 #define PCT_LIGHT_TOP           80      /* darker */
52 #define PCT_MEDIUM_BOTTOM_BASE  40      /* darker */
53 #define PCT_MEDIUM_BOTTOM_RANGE 25
54 #define PCT_MEDIUM_TOP_BASE     60      /* lighter */
55 #define PCT_MEDIUM_TOP_RANGE    -30
56 
57 /* The "brightness" of an RGB color.  The "right" way seems to use
58  * empirical values like the default thresholds below, but it boils down
59  * to red is twice as bright as blue and green is thrice blue.
60  */
61 
62 #define BRIGHTNESS(r,g,b) (2*(int)(r) + 3*(int)(g) + 1*(int)(b))
63 
64 /* From Xm.h on Solaris */
65 #define XmDEFAULT_DARK_THRESHOLD        15
66 #define XmDEFAULT_LIGHT_THRESHOLD       85
67 
68 static XColor color;
69 
70 /**** This part is the old fvwm way to calculate colours. Still used for
71  **** 'medium' brigness colours. */
72 #define DARKNESS_FACTOR 0.5
73 #define BRIGHTNESS_FACTOR 1.4
74 #define SCALE 65535.0
75 #define HALF_SCALE (SCALE / 2)
76 typedef enum {
77 	R_MAX_G_MIN, R_MAX_B_MIN,
78 	G_MAX_B_MIN, G_MAX_R_MIN,
79 	B_MAX_R_MIN, B_MAX_G_MIN
80 } MinMaxState;
81 static void
color_mult(unsigned short * red,unsigned short * green,unsigned short * blue,double k)82 color_mult (unsigned short *red,
83 	    unsigned short *green,
84 	    unsigned short *blue, double k)
85 {
86 	if (*red == *green && *red == *blue) {
87 		double temp;
88 		/* A shade of gray */
89 		temp = k * (double) (*red);
90 		if (temp > SCALE) {
91 			temp = SCALE;
92 		}
93 		*red = (unsigned short)(temp);
94 		*green = *red;
95 		*blue = *red;
96 	} else {
97 		/* Non-zero saturation */
98 		double r, g, b;
99 		double min, max;
100 		double a, l, s;
101 		double delta;
102 		double middle;
103 		MinMaxState min_max_state;
104 
105 		r = (double) *red;
106 		g = (double) *green;
107 		b = (double) *blue;
108 
109 		if (r > g) {
110 			if (r > b) {
111 				max = r;
112 				if (g < b) {
113 					min = g;
114 					min_max_state = R_MAX_G_MIN;
115 					a = b - g;
116 				} else {
117 					min = b;
118 					min_max_state = R_MAX_B_MIN;
119 					a = g - b;
120 				}
121 			} else {
122 				max = b;
123 				min = g;
124 				min_max_state = B_MAX_G_MIN;
125 				a = r - g;
126 			}
127 		} else {
128 			if (g > b) {
129 				max = g;
130 				if (b < r) {
131 					min = b;
132 					min_max_state = G_MAX_B_MIN;
133 					a = r - b;
134 				} else {
135 					min = r;
136 					min_max_state = G_MAX_R_MIN;
137 					a = b - r;
138 				}
139 			} else {
140 				max = b;
141 				min = r;
142 				min_max_state = B_MAX_R_MIN;
143 				a = g - r;
144 			}
145 		}
146 
147 		delta = max - min;
148 		a = a / delta;
149 
150 		l = (max + min) / 2;
151 		if (l <= HALF_SCALE) {
152 			s = max + min;
153 		} else {
154 			s = 2.0 * SCALE - (max + min);
155 		}
156 		s = delta/s;
157 
158 		l *= k;
159 		if (l > SCALE) {
160 			l = SCALE;
161 		}
162 		s *= k;
163 		if (s > 1.0) {
164 			s = 1.0;
165 		}
166 
167 		if (l <= HALF_SCALE) {
168 			max = l * (1 + s);
169 		} else {
170 			max = s * SCALE + l - s * l;
171 		}
172 
173 		min = 2 * l - max;
174 		delta = max - min;
175 		middle = min + delta * a;
176 
177 		switch (min_max_state) {
178 		case R_MAX_G_MIN:
179 			r = max;
180 			g = min;
181 			b = middle;
182 			break;
183 		case R_MAX_B_MIN:
184 			r = max;
185 			g = middle;
186 			b = min;
187 			break;
188 		case G_MAX_B_MIN:
189 			r = middle;
190 			g = max;
191 			b = min;
192 			break;
193 		case G_MAX_R_MIN:
194 			r = min;
195 			g = max;
196 			b = middle;
197 			break;
198 		case B_MAX_G_MIN:
199 			r = middle;
200 			g = min;
201 			b = max;
202 			break;
203 		case B_MAX_R_MIN:
204 			r = min;
205 			g = middle;
206 			b = max;
207 			break;
208 		}
209 
210 		*red = (unsigned short) r;
211 		*green = (unsigned short) g;
212 		*blue = (unsigned short) b;
213 	}
214 }
215 /**** End of original fvwm code. ****/
216 
GetShadowOrHiliteColor(Pixel background,float light,float dark,float factor)217 static XColor *GetShadowOrHiliteColor(
218 	Pixel background, float light, float dark, float factor)
219 {
220 	long brightness;
221 	unsigned int red, green, blue;
222 
223 	memset(&color, 0, sizeof(color));
224 	color.pixel = background;
225 	XQueryColor(Pdpy, Pcmap, &color);
226 	red = color.red;
227 	green = color.green;
228 	blue = color.blue;
229 
230 	brightness = BRIGHTNESS(red, green, blue);
231 	/* For "dark" backgrounds, make everything a fixed %age lighter */
232 	if (brightness < XmDEFAULT_DARK_THRESHOLD * PCT_BRIGHTNESS)
233 	{
234 		color.red = (unsigned short)
235 			(0xffff - ((0xffff - red) * dark + 50) / 100);
236 		color.green = (unsigned short)
237 			(0xffff - ((0xffff - green) * dark + 50) / 100);
238 		color.blue = (unsigned short)
239 			(0xffff - ((0xffff - blue) * dark + 50) / 100);
240 	}
241 	/* For "light" background, make everything a fixed %age darker */
242 	else if (brightness > XmDEFAULT_LIGHT_THRESHOLD * PCT_BRIGHTNESS)
243 	{
244 		color.red =
245 			(unsigned short)((red * light + 50) / 100);
246 		color.green =
247 			(unsigned short)((green * light + 50) / 100);
248 		color.blue =
249 			(unsigned short)((blue * light + 50) / 100);
250 	}
251 	/* For "medium" background, select is a fixed %age darker;
252 	 * top (lighter) and bottom (darker) are a variable %age
253 	 * based on the background's brightness
254 	 */
255 	else
256 	{
257 		color_mult(&color.red, &color.green, &color.blue, factor);
258 	}
259 	return &color;
260 }
261 
262 
GetShadowColor(Pixel background)263 XColor *GetShadowColor(Pixel background)
264 {
265 	return GetShadowOrHiliteColor(
266 		background, PCT_LIGHT_BOTTOM, PCT_DARK_BOTTOM, DARKNESS_FACTOR);
267 }
268 
GetShadow(Pixel background)269 Pixel GetShadow(Pixel background)
270 {
271 	XColor *colorp;
272 
273 	colorp = GetShadowColor(background);
274 	PictureAllocColor(Pdpy, Pcmap, colorp, True);
275 	if (colorp->pixel == background)
276 	{
277 		colorp->pixel = PictureGetNextColor(colorp->pixel, 1);
278 	}
279 	return colorp->pixel;
280 }
281 
GetHiliteColor(Pixel background)282 XColor *GetHiliteColor(Pixel background)
283 {
284 	return GetShadowOrHiliteColor(
285 		background, PCT_LIGHT_TOP, PCT_DARK_TOP, BRIGHTNESS_FACTOR);
286 }
287 
GetHilite(Pixel background)288 Pixel GetHilite(Pixel background)
289 {
290 	XColor *colorp;
291 
292 	colorp = GetHiliteColor(background);
293 	PictureAllocColor(Pdpy, Pcmap, colorp, True);
294 	if (colorp->pixel == background)
295 	{
296 		colorp->pixel = PictureGetNextColor(colorp->pixel, -1);
297 	}
298 	return colorp->pixel;
299 }
300 
GetForeShadowColor(Pixel foreground,Pixel background)301 XColor *GetForeShadowColor(Pixel foreground, Pixel background)
302 {
303 	XColor bg_color;
304 	float fg[3], bg[3];
305 	int result[3];
306 	int i;
307 
308 	memset(&color, 0, sizeof(color));
309 	memset(&bg_color, 0, sizeof(bg_color));
310 	color.pixel = foreground;
311 	bg_color.pixel = background;
312 	XQueryColor(Pdpy, Pcmap, &color);
313 	XQueryColor(Pdpy, Pcmap, &bg_color);
314 	fg[0] = color.red;
315 	fg[1] = color.green;
316 	fg[2] = color.blue;
317 	bg[0] = bg_color.red;
318 	bg[1]=  bg_color.green;
319 	bg[2] = bg_color.blue;
320 
321 	for (i=0; i<3; i++)
322 	{
323 		if (fg[i] - bg[i] < 8192 && fg[i] - bg[i] > -8192)
324 		{
325 			result[i] = 0;
326 		}
327 		else
328 		{
329 			result[i] = (int)((5 * bg[i] - fg[i]) / 4);
330 			if (fg[i] < bg[i] || result[i] < 0)
331 			{
332 				result[i] = (int)((3 * bg[i] + fg[i]) / 4);
333 			}
334 		}
335 	}
336 	color.red = result[0];
337 	color.green = result[1];
338 	color.blue = result[2];
339 
340 	return &color;
341 }
342 
GetForeShadow(Pixel foreground,Pixel background)343 Pixel GetForeShadow(Pixel foreground, Pixel background)
344 {
345 	XColor *colorp;
346 
347 	colorp = GetForeShadowColor(foreground, background);
348 	PictureAllocColor(Pdpy, Pcmap, colorp, True);
349 	if (colorp->pixel == background)
350 	{
351 		colorp->pixel = PictureGetNextColor(colorp->pixel, 1);
352 	}
353 	return colorp->pixel;
354 }
355 
GetTintedColor(Pixel in,Pixel tint,int percent)356 XColor *GetTintedColor(Pixel in, Pixel tint, int percent)
357 {
358 	XColor tint_color;
359 
360 	memset(&color, 0, sizeof(color));
361 	memset(&tint_color, 0, sizeof(tint_color));
362 	color.pixel = in;
363 	XQueryColor(Pdpy, Pcmap, &color);
364 	tint_color.pixel = tint;
365 	XQueryColor(Pdpy, Pcmap, &tint_color);
366 
367 	color.red = (unsigned short)
368 		(((100-percent)*color.red + tint_color.red * percent) / 100);
369 	color.green = (unsigned short)
370 		(((100-percent)*color.green + tint_color.green * percent) /
371 		 100);
372 	color.blue = (unsigned short)
373 		(((100-percent)*color.blue + tint_color.blue * percent) / 100);
374 	return &color;
375 }
376 
GetTintedPixel(Pixel in,Pixel tint,int percent)377 Pixel GetTintedPixel(Pixel in, Pixel tint, int percent)
378 {
379 	XColor *colorp;
380 
381 	colorp = GetTintedColor(in, tint, percent);
382 	PictureAllocColor(Pdpy, Pcmap, colorp, True);
383 	return colorp->pixel;
384 }
385 
386 /* This function converts the colour stored in a colorcell (pixel) into the
387  * string representation of a colour.  The output is printed at the
388  * address 'output'.  It is either in rgb format ("rgb:rrrr/gggg/bbbb") if
389  * use_hash is False or in hash notation ("#rrrrggggbbbb") if use_hash is true.
390  * The return value is the number of characters used by the string.  The
391  * rgb values of the output are undefined if the colorcell is invalid.  The
392  * memory area pointed at by 'output' must be at least 64 bytes (in case of
393  * future extensions and multibyte characters).*/
pixel_to_color_string(Display * dpy,Colormap cmap,Pixel pixel,char * output,Bool use_hash)394 int pixel_to_color_string(
395 	Display *dpy, Colormap cmap, Pixel pixel, char *output, Bool use_hash)
396 {
397 	XColor color;
398 	int n;
399 
400 	color.pixel = pixel;
401 	color.red = 0;
402 	color.green = 0;
403 	color.blue = 0;
404 
405 	XQueryColor(dpy, cmap, &color);
406 	if (!use_hash)
407 	{
408 		sprintf(
409 			output, "rgb:%04x/%04x/%04x%n", (int)color.red,
410 			(int)color.green, (int)color.blue, &n);
411 	}
412 	else
413 	{
414 		sprintf(
415 			output, "#%04x%04x%04x%n", (int)color.red,
416 			(int)color.green, (int)color.blue, &n);
417 	}
418 
419 	return n;
420 }
421 
422 static char *colorset_names[] =
423 {
424 	"$[fg.cs",
425 	"$[bg.cs",
426 	"$[hilight.cs",
427 	"$[shadow.cs",
428 	NULL
429 };
430 
GetSimpleColor(char * name)431 Pixel GetSimpleColor(char *name)
432 {
433 	XColor color;
434 	Bool is_illegal_rgb = False;
435 
436 	memset(&color, 0, sizeof(color));
437 	/* This is necessary because some X servers coredump when presented a
438 	 * malformed rgb colour name. */
439 	if (name && strncasecmp(name, "rgb:", 4) == 0)
440 	{
441 		int i;
442 		char *s;
443 
444 		for (i = 0, s = name + 4; *s; s++)
445 		{
446 			if (*s == '/')
447 				i++;
448 		}
449 		if (i != 2)
450 			is_illegal_rgb = True;
451 	}
452 
453 	if (is_illegal_rgb)
454 	{
455 		fprintf(stderr, "Illegal RGB format \"%s\"\n", name);
456 	}
457 	else if (!XParseColor (Pdpy, Pcmap, name, &color))
458 	{
459 		fprintf(stderr, "Cannot parse color \"%s\"\n",
460 			name ? name : "<blank>");
461 	}
462 	else if (!PictureAllocColor(Pdpy, Pcmap, &color, True))
463 	{
464 		fprintf(stderr, "Cannot allocate color \"%s\"\n", name);
465 	}
466 	return color.pixel;
467 }
468 
GetColor(char * name)469 Pixel GetColor(char *name)
470 {
471 	int i;
472 	int n;
473 	int cs;
474 	char *rest;
475 	XColor color;
476 
477 	switch ((i = GetTokenIndex(name, colorset_names, -1, &rest)))
478 	{
479 	case 0:
480 	case 1:
481 	case 2:
482 	case 3:
483 		if (!isdigit(*rest) || (*rest == '0' && *(rest + 1) != 0))
484 		{
485 			/* not a non-negative integer without leading zeros */
486 			fprintf(stderr,
487 				"Invalid colorset number in color '%s'\n",
488 				name);
489 			return 0;
490 		}
491 		sscanf(rest, "%d%n", &cs, &n);
492 		if (*(rest + n) != ']')
493 		{
494 			fprintf(stderr,
495 				"No closing brace after '%d' in color '%s'\n",
496 				cs, name);
497 			return 0;
498 		}
499 		if (*(rest + n + 1) != 0)
500 		{
501 			fprintf(stderr, "Trailing characters after brace in"
502 				" color '%s'\n", name);
503 			return 0;
504 		}
505 		AllocColorset(cs);
506 		switch (i)
507 		{
508 		case 0:
509 			color.pixel = Colorset[cs].fg;
510 			break;
511 		case 1:
512 			color.pixel = Colorset[cs].bg;
513 			break;
514 		case 2:
515 			color.pixel = Colorset[cs].hilite;
516 			break;
517 		case 3:
518 			color.pixel = Colorset[cs].shadow;
519 			break;
520 		}
521 		if (!PictureAllocColor(Pdpy, Pcmap, &color, True))
522 		{
523 			fprintf(stderr, "Cannot allocate color %d from"
524 				" colorset %d\n", i, cs);
525 			return 0;
526 		}
527 		return color.pixel;
528 
529 	default:
530 		break;
531 	}
532 
533 	return GetSimpleColor(name);
534 }
535 
536 /* Allocates the color from the input Pixel again */
fvwmlib_clone_color(Pixel p)537 Pixel fvwmlib_clone_color(Pixel p)
538 {
539 	XColor c;
540 
541 	c.pixel = p;
542 	XQueryColor(Pdpy, Pcmap, &c);
543 	if (!PictureAllocColor(Pdpy, Pcmap, &c, True))
544 	{
545 		fprintf(stderr, "Cannot allocate clone Pixel %d\n", (int)p);
546 		return 0;
547 	}
548 
549 	return c.pixel;
550 }
551 
552 /* Free an array of colours (n colours), never free black */
fvwmlib_free_colors(Display * dpy,Pixel * pixels,int n,Bool no_limit)553 void fvwmlib_free_colors(Display *dpy, Pixel *pixels, int n, Bool no_limit)
554 {
555 	int i;
556 
557 	/* We don't ever free black - dirty hack to allow freeing colours at
558 	 * all */
559 	/* olicha: ???? */
560 	for (i = 0; i < n; i++)
561 	{
562 		if (pixels[i] != 0)
563 		{
564 			PictureFreeColors(
565 				dpy, Pcmap, pixels + i, 1, 0, no_limit);
566 		}
567 	}
568 
569 	return;
570 }
571 
572 /* Copy one color and reallocate it */
fvwmlib_copy_color(Display * dpy,Pixel * dst_color,Pixel * src_color,Bool do_free_dest,Bool do_copy_src)573 void fvwmlib_copy_color(
574 	Display *dpy, Pixel *dst_color, Pixel *src_color, Bool do_free_dest,
575 	Bool do_copy_src)
576 {
577 	if (do_free_dest)
578 	{
579 		fvwmlib_free_colors(dpy, dst_color, 1, True);
580 	}
581 	if (do_copy_src)
582 	{
583 		*dst_color = fvwmlib_clone_color(*src_color);
584 	}
585 }
586