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