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