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 ("#rrggbb") 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,int adj)394 int pixel_to_color_string(
395 	Display *dpy, Colormap cmap, Pixel pixel, char *output,
396 	Bool use_hash, int adj)
397 {
398 	XColor color;
399 	int n;
400 
401 	color.pixel = pixel;
402 	color.red = 0;
403 	color.green = 0;
404 	color.blue = 0;
405 
406 	XQueryColor(dpy, cmap, &color);
407 
408 	/* Lighten color */
409 	if ( adj > 0 && adj <= 100 )
410 	{
411 		color.red = (int)( ((100-adj)*color.red+adj*65535)/100 );
412 		color.green = (int)( ((100-adj)*color.green+adj*65535)/100 );
413 		color.blue = (int)( ((100-adj)*color.blue+adj*65535)/100 );
414 	}
415 	/* Darken color */
416 	if ( adj >= -100 && adj < 0 )
417 	{
418 		color.red = (int)( (100+adj)*color.red/100 );
419 		color.green = (int)( (100+adj)*color.green/100 );
420 		color.blue = (int)( (100+adj)*color.blue/100 );
421 	}
422 
423 	if (!use_hash)
424 	{
425 		sprintf(
426 			output, "rgb:%04x/%04x/%04x%n", (int)color.red,
427 			(int)color.green, (int)color.blue, &n);
428 	}
429 	else
430 	{
431 		sprintf(
432 			output, "#%02x%02x%02x%n", (int)(color.red/256),
433 			(int)(color.green/256), (int)(color.blue/256), &n);
434 	}
435 
436 	return n;
437 }
438 
439 static char *colorset_names[] =
440 {
441 	"$[fg.cs",
442 	"$[bg.cs",
443 	"$[hilight.cs",
444 	"$[shadow.cs",
445 	"$[fgsh.cs",
446 	NULL
447 };
448 
GetSimpleColor(char * name)449 Pixel GetSimpleColor(char *name)
450 {
451 	XColor color;
452 	Bool is_illegal_rgb = False;
453 
454 	memset(&color, 0, sizeof(color));
455 	/* This is necessary because some X servers coredump when presented a
456 	 * malformed rgb colour name. */
457 	if (name && strncasecmp(name, "rgb:", 4) == 0)
458 	{
459 		int i;
460 		char *s;
461 
462 		for (i = 0, s = name + 4; *s; s++)
463 		{
464 			if (*s == '/')
465 				i++;
466 		}
467 		if (i != 2)
468 			is_illegal_rgb = True;
469 	}
470 
471 	if (is_illegal_rgb)
472 	{
473 		fvwm_debug(__func__, "Illegal RGB format \"%s\"\n", name);
474 	}
475 	else if (!XParseColor (Pdpy, Pcmap, name, &color))
476 	{
477 		fvwm_debug(__func__, "Cannot parse color \"%s\"\n",
478 			   name ? name : "<blank>");
479 	}
480 	else if (!PictureAllocColor(Pdpy, Pcmap, &color, True))
481 	{
482 		fvwm_debug(__func__, "Cannot allocate color \"%s\"\n", name);
483 	}
484 	return color.pixel;
485 }
486 
GetColor(char * name)487 Pixel GetColor(char *name)
488 {
489 	int i;
490 	int n;
491 	int cs;
492 	char *rest;
493 	XColor color;
494 
495 	switch ((i = GetTokenIndex(name, colorset_names, -1, &rest)))
496 	{
497 	case 0:
498 	case 1:
499 	case 2:
500 	case 3:
501 		if (!isdigit(*rest) || (*rest == '0' && *(rest + 1) != 0))
502 		{
503 			/* not a non-negative integer without leading zeros */
504 			fvwm_debug(__func__,
505 				   "Invalid colorset number in color '%s'\n",
506 				   name);
507 			return 0;
508 		}
509 		sscanf(rest, "%d%n", &cs, &n);
510 		if (*(rest + n) != ']')
511 		{
512 			fvwm_debug(__func__,
513 				   "No closing brace after '%d' in color '%s'\n",
514 				   cs, name);
515 			return 0;
516 		}
517 		if (*(rest + n + 1) != 0)
518 		{
519 			fvwm_debug(__func__,
520 				   "Trailing characters after brace in"
521 				   " color '%s'\n", name);
522 			return 0;
523 		}
524 		AllocColorset(cs);
525 		switch (i)
526 		{
527 		case 0:
528 			color.pixel = Colorset[cs].fg;
529 			break;
530 		case 1:
531 			color.pixel = Colorset[cs].bg;
532 			break;
533 		case 2:
534 			color.pixel = Colorset[cs].hilite;
535 			break;
536 		case 3:
537 			color.pixel = Colorset[cs].shadow;
538 			break;
539 		}
540 		if (!PictureAllocColor(Pdpy, Pcmap, &color, True))
541 		{
542 			fvwm_debug(__func__, "Cannot allocate color %d from"
543 				   " colorset %d\n", i, cs);
544 			return 0;
545 		}
546 		return color.pixel;
547 
548 	default:
549 		break;
550 	}
551 
552 	return GetSimpleColor(name);
553 }
554 
555 /* Allocates the color from the input Pixel again */
fvwmlib_clone_color(Pixel p)556 Pixel fvwmlib_clone_color(Pixel p)
557 {
558 	XColor c;
559 
560 	c.pixel = p;
561 	XQueryColor(Pdpy, Pcmap, &c);
562 	if (!PictureAllocColor(Pdpy, Pcmap, &c, True))
563 	{
564 		fvwm_debug(__func__, "Cannot allocate clone Pixel %d\n",
565 			   (int)p);
566 		return 0;
567 	}
568 
569 	return c.pixel;
570 }
571 
572 /* Free an array of colours (n colours), never free black */
fvwmlib_free_colors(Display * dpy,Pixel * pixels,int n,Bool no_limit)573 void fvwmlib_free_colors(Display *dpy, Pixel *pixels, int n, Bool no_limit)
574 {
575 	int i;
576 
577 	/* We don't ever free black - dirty hack to allow freeing colours at
578 	 * all */
579 	/* olicha: ???? */
580 	for (i = 0; i < n; i++)
581 	{
582 		if (pixels[i] != 0)
583 		{
584 			PictureFreeColors(
585 				dpy, Pcmap, pixels + i, 1, 0, no_limit);
586 		}
587 	}
588 
589 	return;
590 }
591 
592 /* 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)593 void fvwmlib_copy_color(
594 	Display *dpy, Pixel *dst_color, Pixel *src_color, Bool do_free_dest,
595 	Bool do_copy_src)
596 {
597 	if (do_free_dest)
598 	{
599 		fvwmlib_free_colors(dpy, dst_color, 1, True);
600 	}
601 	if (do_copy_src)
602 	{
603 		*dst_color = fvwmlib_clone_color(*src_color);
604 	}
605 }
606