1 /***********************************************************/
2 /*                                                         */
3 /*  System dependent color management                      */
4 /*                                                         */
5 /***********************************************************/
6 
7 #include "unix/guts.h"
8 #include "Drawable.h"
9 #include "Window.h"
10 
11 /* have two color layouts for panel widgets (lists, edits) and gray widgets (buttons, labels) */
12 #define COLOR_DEFAULT_TEXT         0x000000
13 #define COLOR_DEFAULT_GRAY         0xcccccc
14 #define COLOR_DEFAULT_BACK         0xffffff
15 
16 #define COLOR_GRAY_NORMAL_TEXT     COLOR_DEFAULT_TEXT
17 #define COLOR_GRAY_NORMAL_BACK     COLOR_DEFAULT_GRAY
18 #define COLOR_GRAY_HILITE_TEXT     COLOR_DEFAULT_TEXT
19 #define COLOR_GRAY_HILITE_BACK     COLOR_DEFAULT_GRAY
20 #define COLOR_GRAY_DISABLED_TEXT   0x606060
21 #define COLOR_GRAY_DISABLED_BACK   0xcccccc
22 
23 #define COLOR_PANEL_NORMAL_TEXT    COLOR_DEFAULT_TEXT
24 #define COLOR_PANEL_NORMAL_BACK    COLOR_DEFAULT_BACK
25 #define COLOR_PANEL_HILITE_TEXT    COLOR_DEFAULT_BACK
26 #define COLOR_PANEL_HILITE_BACK    COLOR_DEFAULT_TEXT
27 #define COLOR_PANEL_DISABLED_TEXT  0x606060
28 #define COLOR_PANEL_DISABLED_BACK  0xdddddd
29 
30 #define COLOR_LIGHT3D              0xffffff
31 #define COLOR_DARK3D               0x808080
32 
33 #define COLORSET_GRAY_NORMAL       COLOR_GRAY_NORMAL_TEXT,   COLOR_GRAY_NORMAL_BACK
34 #define COLORSET_GRAY_HILITE       COLOR_GRAY_HILITE_TEXT,   COLOR_GRAY_HILITE_BACK
35 #define COLORSET_GRAY_ALT_HILITE   COLOR_GRAY_HILITE_BACK,   COLOR_GRAY_HILITE_TEXT
36 #define COLORSET_GRAY_DISABLED     COLOR_GRAY_DISABLED_TEXT, COLOR_GRAY_DISABLED_BACK
37 
38 #define COLORSET_PANEL_NORMAL      COLOR_PANEL_NORMAL_TEXT,   COLOR_PANEL_NORMAL_BACK
39 #define COLORSET_PANEL_HILITE      COLOR_PANEL_HILITE_TEXT,   COLOR_PANEL_HILITE_BACK
40 #define COLORSET_PANEL_DISABLED    COLOR_PANEL_DISABLED_TEXT, COLOR_PANEL_DISABLED_BACK
41 
42 #define COLORSET_3D                COLOR_LIGHT3D, COLOR_DARK3D
43 
44 #define COLORSET_GRAY              COLORSET_GRAY_NORMAL, COLORSET_GRAY_HILITE, \
45 											COLORSET_GRAY_DISABLED, COLORSET_3D
46 #define COLORSET_ALT_GRAY          COLORSET_GRAY_NORMAL, COLORSET_GRAY_ALT_HILITE, \
47 											COLORSET_GRAY_DISABLED, COLORSET_3D
48 #define COLORSET_PANEL             COLORSET_PANEL_NORMAL, COLORSET_PANEL_HILITE, \
49 											COLORSET_PANEL_DISABLED, COLORSET_3D
50 
51 static Color standard_button_colors[]      = { COLORSET_GRAY     };
52 static Color standard_checkbox_colors[]    = { COLORSET_GRAY     };
53 static Color standard_combo_colors[]       = { COLORSET_GRAY     };
54 static Color standard_dialog_colors[]      = { COLORSET_GRAY     };
55 static Color standard_edit_colors[]        = { COLORSET_PANEL    };
56 static Color standard_inputline_colors[]   = { COLORSET_PANEL    };
57 static Color standard_label_colors[]       = { COLORSET_GRAY     };
58 static Color standard_listbox_colors[]     = { COLORSET_PANEL    };
59 static Color standard_menu_colors[]        = { COLORSET_ALT_GRAY };
60 static Color standard_popup_colors[]       = { COLORSET_ALT_GRAY };
61 static Color standard_radio_colors[]       = { COLORSET_GRAY     };
62 static Color standard_scrollbar_colors[]   = { COLORSET_ALT_GRAY };
63 static Color standard_slider_colors[]      = { COLORSET_GRAY     };
64 static Color standard_widget_colors[]      = { COLORSET_ALT_GRAY };
65 static Color standard_window_colors[]      = { COLORSET_GRAY     };
66 static Color standard_application_colors[] = { COLORSET_GRAY     };
67 
68 static Color* standard_colors[] = {
69 	NULL,
70 	standard_button_colors,		/* Prima.Button.* */
71 	standard_checkbox_colors,		/* Prima.Checkbox.* */
72 	standard_combo_colors,		/* Prima.Combo.* */
73 	standard_dialog_colors,		/* Prima.Dialog.* */
74 	standard_edit_colors,		/*   ...etc... */
75 	standard_inputline_colors,
76 	standard_label_colors,
77 	standard_listbox_colors,
78 	standard_menu_colors,
79 	standard_popup_colors,
80 	standard_radio_colors,
81 	standard_scrollbar_colors,
82 	standard_slider_colors,
83 	standard_widget_colors,
84 	standard_window_colors,
85 	standard_application_colors,
86 };
87 
88 static const int MAX_COLOR_CLASS = sizeof( standard_colors) / sizeof( standard_colors[ 0]) - 1;
89 
90 Color **
prima_standard_colors(void)91 prima_standard_colors(void)
92 {
93 	return standard_colors;
94 }
95 
96 /* maps RGB or cl-constant value to RGB value.  */
97 Color
prima_map_color(Color clr,int * hint)98 prima_map_color( Color clr, int * hint)
99 {
100 	long cls;
101 	if ( hint) *hint = COLORHINT_NONE;
102 	if (( clr & clSysFlag) == 0) return clr;
103 
104 	cls = (clr & wcMask) >> 16;
105 	if ( cls <= 0 || cls > MAX_COLOR_CLASS) cls = (wcWidget) >> 16;
106 	if (( clr = ( clr & ~wcMask)) > clMaxSysColor) clr = clMaxSysColor;
107 	if ( clr == clSet)   {
108 		if ( hint) *hint = COLORHINT_WHITE;
109 		return 0xffffff;
110 	} else if ( clr == clClear) {
111 		if ( hint) *hint = COLORHINT_BLACK;
112 		return 0;
113 	} else if ( clr == clInvalid) {
114 		return 0xffffff;
115 	} else return standard_colors[cls][(clr & clSysMask) - 1];
116 }
117 
118 Color
apc_widget_map_color(Handle self,Color color)119 apc_widget_map_color( Handle self, Color color)
120 {
121 	if ((( color & clSysFlag) != 0) && (( color & wcMask) == 0)) color |= PWidget(self)-> widgetClass;
122 	return prima_map_color( color, NULL);
123 }
124 
125 static PHash  hatches;
126 static Bool   kill_hatches( Pixmap pixmap, int keyLen, void * key, void * dummy);
127 static Bool   prima_color_new( XColor * xc);
128 
129 /*
130 static int card[256];
131 static int cardi = 0;
132 static Bool
133 my_XAllocColor( Display * disp, Colormap cm, XColor * xc, int line)
134 {
135 	if ( !cardi) {
136 		cardi = 1;
137 		bzero( card, 256*sizeof(int));
138 	}
139 	if ( !XAllocColor(disp, cm, xc)) {
140 		printf("Failed alloc of %02x%02x%02x, at %d\n",
141 			xc-> red>>8, xc-> green>>8, xc-> blue>>8, line);
142 		return false;
143 	}
144 	printf("Alloc %02x%02x%02x, at %d: %d\n",
145 			xc-> red>>8, xc-> green>>8, xc-> blue>>8,
146 			line, xc-> pixel);
147 	card[xc-> pixel]++;
148 	return true;
149 }
150 
151 static void
152 my_XFreeColors( Display * disp, Colormap cm, long * ls, int count, long pal, int line)
153 {
154 	XSynchronize( DISP, true);
155 	printf("Free at %d:%d items\n", line, count);
156 	for ( pal = 0; pal < count; pal++, ls++) {
157 		printf("%ld.", *ls);
158 		XFreeColors( disp, cm, ls, 1, 0);
159 		XSync( disp, false);
160 		if ( !card[*ls]) printf("jopa!\n");
161 		else card[*ls]--;
162 	}
163 	printf("done\n");
164 }
165 
166 #define XAllocColor(a,b,c) my_XAllocColor(a,b,c,__LINE__)
167 #define XFreeColors(a,b,c,d,e) my_XFreeColors(a,b,c,d,e,__LINE__)
168 */
169 
170 static Bool
alloc_color(XColor * c)171 alloc_color( XColor * c)
172 {
173 	int diff = 5 * 256,
174 	r = c-> red,
175 	g = c-> green,
176 	b = c-> blue;
177 	if ( !XAllocColor( DISP, guts. defaultColormap, c)) return false;
178 	if (
179 		diff > abs( c-> red   - r) &&
180 		diff > abs( c-> green - g) &&
181 		diff > abs( c-> blue  - b)
182 	) return true;
183 	XFreeColors( DISP, guts. defaultColormap, &c->pixel, 1, 0);
184 	return false;
185 }
186 
187 /*
188 	Fills Brush structure. If dithering is needed,
189 brush.secondary and brush.balance are set. Tries to
190 get new colors via XAllocColor, assigns new color cells
191 to self if successfull.
192 	If no brush structure is given, no dithering is
193 preformed.
194 	Returns closest matching color, always the same as
195 brush-> primary.
196 */
197 unsigned long
prima_allocate_color(Handle self,Color color,Brush * brush)198 prima_allocate_color( Handle self, Color color, Brush * brush)
199 {
200 	DEFXX;
201 	Brush b;
202 	int a[3], hint;
203 
204 	if ( !brush) brush = &b;
205 	brush-> balance = 0;
206 	brush-> color = color = prima_map_color( color, &hint);
207 
208 	if ( hint)
209 		return ( brush-> primary = (( hint == COLORHINT_BLACK) ? LOGCOLOR_BLACK : LOGCOLOR_WHITE));
210 
211 	a[0] = COLOR_R(color);
212 	a[1] = COLOR_G(color);
213 	a[2] = COLOR_B(color);
214 
215 	if ( self && XF_LAYERED(XX) )
216 		return brush->primary =
217 			(((a[0] << guts. argb_bits. red_range  ) >> 8) << guts. argb_bits.   red_shift) |
218 			(((a[1] << guts. argb_bits. green_range) >> 8) << guts. argb_bits. green_shift) |
219 			(((a[2] << guts. argb_bits. blue_range ) >> 8) << guts. argb_bits.  blue_shift);
220 
221 	if ( guts. grayScale) {
222 		a[0] = a[1] = a[2] = ( a[0] + a[1] + a[2]) / 3;
223 		color = a[0] * ( 65536 + 256 + 1);
224 	}
225 	Pdebug("color: %s asked for %06x\n", self?PWidget(self)->name:"null", color);
226 	if (self && XT_IS_BITMAP(XX)) {
227 		Byte balance = ( a[0] + a[1] + a[2] + 6) / (3 * 4);
228 		if ( balance < 64) {
229 			brush-> primary   = 0;
230 			brush-> secondary = 1;
231 			brush-> balance   = balance;
232 		} else
233 			brush-> primary = 1;
234 	} else {
235 		if ( guts. palSize > 0) {
236 			int ab2;
237 			Bool dyna = guts. dynamicColors && self && X(self)-> type. widget && ( self != application);
238 			brush-> primary = prima_color_find( self, color, -1, &ab2, RANK_FREE);
239 
240 			if ( dyna && ab2 > 12) {
241 				XColor xc;
242 				xc. red   = COLOR_R16(color);
243 				xc. green = COLOR_G16(color);
244 				xc. blue  = COLOR_B16(color);
245 				xc. flags = DoGreen | DoBlue | DoRed;
246 				prima_color_sync();
247 				if ( alloc_color( &xc)) {
248 					prima_color_new( &xc);
249 					Pdebug("color: %s alloc %d ( wanted %06x). got %02x %02x %02x\n", PWidget(self)-> name, xc.pixel, color, xc.red>>8,xc.green>>8,xc.blue>>8);
250 					prima_color_add_ref( self, xc. pixel, RANK_NORMAL);
251 					return brush-> primary = xc. pixel;
252 				}
253 			}
254 
255 			if ( guts. useDithering && (brush != &b) && (ab2 > 12)) {
256 				if ( guts. grayScale && guts. systemColorMapSize > guts. palSize / 2) {
257 					int clr = ( COLOR_R(color) + COLOR_G(color) +
258 							COLOR_B(color)) / 3;
259 					int grd  = 256 / ( guts. systemColorMapSize - 1);
260 					int left = clr / grd;
261 					brush-> balance = ( clr - left * grd) * 64 / grd;
262 					brush-> primary = guts. systemColorMap[ left];
263 					brush-> secondary = guts. systemColorMap[ left + 1];
264 				} else {
265 					int i;
266 					Bool cubic = (XX-> type.dbm && guts. dynamicColors) ||
267 									( guts. colorCubeRib > 4);
268 DITHER:
269 					if ( cubic) {
270 /* fast search of dithering colors - based on color cube.
271 	used either on restricted drawables ( as dbm) or when
272 	have enough colors - small cubes give noisy picture
273 
274       .  .  .  *R"G"   assume here that blue component does not require dithering
275   R |                  R'G' and R"G" are 2 colors blended with proprotion to make
276     | '''* A           color A. R'G' is a closest cubic color. If A(G)/A(R) < y,
277     |    |             R"G" is G-point, if A(G)/A(R) > 1 + y, it's R-point, otherwise
278     *---------- G      it's RG-point. (y=sqrt(2)-1=0.41; y=0.41x and y=1.41x are
279 R'G' , B=0             maximal error lines). balance is computed as diff between
280                        R'G' and R"G"
281 */
282 						int base[3], l[3], z[3], r[3], cnt = 0, sum = 0;
283 						int grd = 256 / ( guts. colorCubeRib - 1);
284 						for ( i = 0; i < 3; i++) {
285 							base[i] = a[i] / grd;
286 							r[i] = l[i] = ( a[i] >= base[i] + grd / 2) ? 1 : 0;
287 							z[i] = l[i] ? (base[i] + 1) * grd - a[i]: a[i] - base[i] * grd;
288 						}
289 						if ( z[1] > 1) {
290 							int ratio1 = 100 * z[0] / z[1];
291 							int ratio2 = 100 * z[2] / z[1];
292 							if ( ratio1 > 59)  r[0] = r[0] ? 0 : 1;
293 							if ( ratio2 > 59)  r[2] = r[2] ? 0 : 1;
294 							if ( ratio1 < 141 && ratio2 < 141) r[1] = r[1] ? 0 : 1;
295 						}  else if ( z[2] > 1) {
296 							int ratio = 100 * z[0] / z[2];
297 							if ( ratio > 59) r[0] = r[0] ? 0 : 1;
298 							if ( ratio < 141) r[2] = r[2] ? 0 : 1;
299 						} else if ( z[0] > 1) {
300 							r[0] = r[0] ? 0 : 1;
301 						}
302 						for ( i = 0; i < 3; i++)
303 							if ( r[i] != l[i]) {
304 								sum += z[i];
305 								cnt++;
306 							}
307 						brush-> primary = guts. systemColorMap[
308 							l[2] + base[2] +
309 						( l[1] + base[1]) * guts.colorCubeRib +
310 						( l[0] + base[0]) * guts.colorCubeRib * guts.colorCubeRib];
311 						brush-> secondary = guts. systemColorMap[
312 							r[2] + base[2] +
313 						( r[1] + base[1]) * guts.colorCubeRib +
314 						( r[0] + base[0]) * guts.colorCubeRib * guts.colorCubeRib];
315 						brush-> balance = cnt ? (sum / cnt) * 64 / grd : 0;
316 					} else {
317 /*  slow search for dithering counterpart color; takes long time
318 	but gives closest possible colors.
319 
320            A*          A - color to be expressed by B and D
321            /|  .       B - closest color
322           / |    .     D - candidate color
323          /  |      .   C - closest color that can be expressed using B and D
324 *---*-------*  The objective is to find such D whose AC is minimal and
325 B   C       D  CD>BD. ( CD = (AD*AD-AB*AB+BD*BD)/2BD, AC=sqrt(AD*AD-CD*CD))
326 */
327 						int b[3], d[3], i;
328 						int ab2, bd2, ac2, ad2;
329 						float cd, bd, BMcd=0, BMbd=0;
330 						int maxDiff = 16777216, bestMatch = -1;
331 						int mincd = maxDiff;
332 						b[0] = guts. palette[brush-> primary].r;
333 						b[1] = guts. palette[brush-> primary].g;
334 						b[2] = guts. palette[brush-> primary].b;
335 						Pdebug("color:want %06x, closest is %06x\n", color, guts.palette[brush-> primary].composite);
336 						ab2 = (a[0]-b[0])*(a[0]-b[0]) +
337 								(a[1]-b[1])*(a[1]-b[1]) +
338 								(a[2]-b[2])*(a[2]-b[2]);
339 						for ( i = 0; i < guts.palSize; i++) {
340 							if ( guts.palette[i].rank == RANK_FREE) continue;
341 							d[0] = guts. palette[i].r;
342 							d[1] = guts. palette[i].g;
343 							d[2] = guts. palette[i].b;
344 							Pdebug("color:tasting %06x\n", guts.palette[i].composite);
345 							bd2 = (d[0]-b[0])*(d[0]-b[0]) +
346 									(d[1]-b[1])*(d[1]-b[1]) +
347 									(d[2]-b[2])*(d[2]-b[2]);
348 							bd  = sqrt( bd2);
349 							if ( bd == 0) continue;
350 							ad2 = (d[0]-a[0])*(d[0]-a[0]) +
351 									(d[1]-a[1])*(d[1]-a[1]) +
352 									(d[2]-a[2])*(d[2]-a[2]);
353 							cd  = ( ad2 - ab2 + bd2) / (2 * bd);
354 							Pdebug("color:bd:%g,bd2:%d, ad2:%d, cd:%g\n", bd, bd2, ad2, cd);
355 							if ( cd < bd) {
356 								ac2 = ad2 - cd * cd;
357 								Pdebug("color:ac2:%d\n", ac2);
358 								if ( ac2 < maxDiff || (( ac2 < maxDiff + 12) && (cd < mincd))) {
359 									maxDiff = ac2;
360 									bestMatch = i;
361 									BMcd = cd;
362 									BMbd = bd;
363 									mincd = cd;
364 									if ( mincd < 42) goto ENOUGH;
365 								}
366 							}
367 						}
368 ENOUGH:;
369 						if ( !guts. grayScale && maxDiff > (64/(guts.colorCubeRib-1))) {
370 							cubic = true;
371 							goto DITHER;
372 						}
373 						brush-> secondary = bestMatch;
374 						brush-> balance   = 63 - BMcd * 64 / BMbd;
375 					}
376 				}
377 			}
378 
379 			if ( dyna) {
380 				if ( wlpal_get( self, brush-> primary) == RANK_FREE)
381 					prima_color_add_ref( self, brush-> primary, RANK_NORMAL);
382 				if (( brush-> balance > 0) &&
383 					( wlpal_get( self, brush->secondary) == RANK_FREE))
384 					prima_color_add_ref( self, brush-> secondary, RANK_NORMAL);
385 			}
386 		} else
387 			brush-> primary =
388 				(((a[0] << guts. screen_bits. red_range  ) >> 8) << guts. screen_bits.   red_shift) |
389 				(((a[1] << guts. screen_bits. green_range) >> 8) << guts. screen_bits. green_shift) |
390 				(((a[2] << guts. screen_bits. blue_range ) >> 8) << guts. screen_bits.  blue_shift);
391 	}
392 	return brush-> primary;
393 }
394 
395 
396 static Bool
alloc_main_color_range(XColor * xc,int count,int maxDiff)397 alloc_main_color_range( XColor * xc, int count, int maxDiff)
398 {
399 	int idx;
400 	Bool err = false;
401 
402 	if ( count > guts. palSize) return false;
403 
404 	for ( idx = 0; idx < count; idx++)
405 		xc[idx]. pixel = 0xFFFFFFFF;
406 
407 	for ( idx = 0; idx < count; idx++) {
408 		int R = xc[idx]. red;
409 		int G = xc[idx]. green;
410 		int B = xc[idx]. blue;
411 		if ( !XAllocColor( DISP, guts. defaultColormap, &xc[idx])) {
412 			err = true;
413 			break;
414 		}
415 		if ( xc[idx]. pixel >= guts. palSize) {
416 			warn("color index out of range returned from XAllocColor()\n");
417 			return false;
418 		}
419 		if (( xc[idx]. blue / 256 - B / 256) * ( xc[idx]. blue / 256 - B / 256) +
420 			( xc[idx]. green / 256 - G / 256) * ( xc[idx]. green / 256 - G / 256) +
421 			( xc[idx]. red / 256 - R / 256) * ( xc[idx]. red / 256 - R / 256) >
422 			maxDiff) {
423 			err = true;
424 			break;
425 		}
426 	}
427 
428 	if ( err) {
429 		unsigned long cnt = 0, free[32];
430 		for ( idx = 0; idx < count; idx++)
431 			if ( xc[idx]. pixel != 0xFFFFFFFF) {
432 				free[ cnt++] = xc[idx]. pixel;
433 				if ( cnt == 32) {
434 					XFreeColors( DISP, guts. defaultColormap, free, 32, 0);
435 					cnt = 0;
436 				}
437 			}
438 		if ( cnt > 0)
439 			XFreeColors( DISP, guts. defaultColormap, free, cnt, 0);
440 		return false;
441 	}
442 	return true;
443 }
444 
445 static Bool
create_std_palettes(XColor * xc,int count)446 create_std_palettes( XColor * xc, int count)
447 {
448 	int idx;
449 	if ( !( guts. palette = malloc( sizeof( MainColorEntry) * guts. palSize)))
450 		return false;
451 	if ( !( guts. systemColorMap = malloc( sizeof( int) * count))) {
452 		free( guts. palette);
453 		guts. palette = NULL;
454 		return false;
455 	}
456 	bzero( guts. palette, sizeof( MainColorEntry) * guts. palSize);
457 
458 	for ( idx = 0; idx < guts. palSize; idx++) {
459 		guts. palette[ idx]. rank = RANK_FREE;
460 		list_create( &guts. palette[idx]. users, 0, 16);
461 	}
462 
463 	for ( idx = 0; idx < count; idx++) {
464 		int pixel = xc[idx]. pixel;
465 		guts. palette[ pixel]. r = xc[idx]. red / 256;
466 		guts. palette[ pixel]. g = xc[idx]. green / 256;
467 		guts. palette[ pixel]. b = xc[idx]. blue / 256;
468 		guts. palette[ pixel]. composite = RGB_COMPOSITE(
469 			guts. palette[ pixel]. r,
470 			guts. palette[ pixel]. g,
471 			guts. palette[ pixel]. b);
472 		guts. palette[ pixel]. rank = RANK_IMMUTABLE;
473 		guts. systemColorMap[ idx] = pixel;
474 	}
475 
476 	guts. systemColorMapSize = count;
477 
478 	return true;
479 }
480 
481 static void
fill_cubic(XColor * xc,int d)482 fill_cubic( XColor * xc, int d)
483 {
484 	int b, g, r, d2 = d * d, frac = 65535 / ( d - 1);
485 	for ( b = 0; b < d; b++) for ( g = 0; g < d; g++) for ( r = 0; r < d; r++) {
486 		int idx = b + g * d + r * d2;
487 		xc[idx]. blue = b * frac;
488 		xc[idx]. green = g * frac;
489 		xc[idx]. red = r * frac;
490 	}
491 }
492 
493 static char * do_visual = NULL;
494 static PList color_options = NULL;
495 
496 static void
set_color_class(int class,char * option,char * value)497 set_color_class( int class, char * option, char * value)
498 {
499 	if ( !value) {
500 		warn("`%s' must be given a value -- skipped\n", option);
501 		return;
502 	}
503 	if ( !color_options) color_options = plist_create( 8, 8);
504 	if ( !color_options) return;
505 	list_add( color_options, ( Handle) class);
506 	list_add( color_options, ( Handle) duplicate_string(value));
507 }
508 
509 static void
apply_color_class(int c_class,Color value)510 apply_color_class( int c_class, Color value)
511 {
512 	int i;
513 	Color ** t = standard_colors + 1;
514 	for ( i = 1; i < MAX_COLOR_CLASS; i++, t++) (*t)[c_class] = value;
515 	Mdebug("color: class %d=%06x\n", c_class, value);
516 }
517 
518 /* find color bounds and test if they are contiguous */
519 Bool
prima_find_color_mask_range(unsigned long mask,unsigned int * shift,unsigned int * range)520 prima_find_color_mask_range( unsigned long mask, unsigned int * shift, unsigned int * range)
521 {
522 	int i, from = 0, to = 0, stage = 0;
523 	for ( i = 0; i < 32; i++) {
524 		switch ( stage) {
525 		case 0:
526 			if (( mask & ( 1 << i)) != 0) {
527 				from = i;
528 				stage++;
529 			}
530 			break;
531 		case 1:
532 			if (( mask & ( 1 << i)) == 0) {
533 				to = i;
534 				stage++;
535 			}
536 			break;
537 		case 2:
538 			if (( mask & ( 1 << i)) != 0) {
539 				warn( "panic: unsupported pixel representation (0x%08lx)", mask);
540 				return false;
541 			}
542 		}
543 	}
544 	if ( to == 0) to = 32;
545 	*shift   = from;
546 	*range   = to - from;
547 	return true;
548 }
549 
550 Bool
prima_init_color_subsystem(char * error_buf)551 prima_init_color_subsystem(char * error_buf)
552 {
553 	int id, count, mask = VisualScreenMask|VisualDepthMask|VisualIDMask;
554 	XVisualInfo template, *list = NULL;
555 
556 	/* check if non-default depth is selected */
557 	id = -1;
558 	{
559 		char * c, * end;
560 		if (( c = do_visual) ||
561 			apc_fetch_resource( "Prima", "", "Visual", "visual",
562 										NULL_HANDLE, frString, &c)) {
563 			id = strtol( c, &end, 0);
564 			if ( *end) id = -1;
565 			if ( c != do_visual) free( c);
566 			template. visualid = id;
567 			list = XGetVisualInfo( DISP, VisualIDMask, &template, &count);
568 			if ( count <= 0) {
569 				warn("warning: visual id '%s' is not found\n", c);
570 				if ( list) XFree( list);
571 				id = -1;
572 			}
573 		}
574 	}
575 	free( do_visual);
576 	do_visual = NULL;
577 
578 FALLBACK_TO_DEFAULT_VISUAL:
579 	if ( id < 0) {
580 		template. screen   = SCREEN;
581 		template. depth    = guts. depth;
582 		template. visualid = XVisualIDFromVisual( XDefaultVisual( DISP, SCREEN));
583 
584 		list = XGetVisualInfo( DISP, mask, &template, &count);
585 		if ( count == 0) {
586 			sprintf( error_buf, "panic: no visuals found");
587 			return false;
588 		}
589 	}
590 
591 	guts. visual = list[0];
592 	guts. visualClass = guts. visual.
593 #if defined(__cplusplus) || defined(c_plusplus)
594 c_class;
595 #else
596 class;
597 #endif
598 	XFree( list);
599 
600 	if ( guts. depth > 11 && guts. visualClass != TrueColor && guts. visualClass != DirectColor) {/* XXX */
601 		if ( id >= 0) {
602 			warn("warning: visual 0x%x is unusable: cannot use %d bit depth for something not TrueColor or DirectColor\n", id, guts. depth);
603 			id = -1;
604 			goto FALLBACK_TO_DEFAULT_VISUAL;
605 		} else
606 			sprintf( error_buf, "panic: %d bit depth is not true color", guts. depth);
607 		return false;
608 	}
609 
610 
611 	guts. useDithering     = true;
612 	guts. dynamicColors    = false;
613 	guts. grayScale        = false;
614 	guts. palSize          = 1 << guts. depth;
615 	guts. palette          = NULL;
616 	guts. systemColorMap   = NULL;
617 	guts. systemColorMapSize = 0;
618 	guts. colorCubeRib     = 0;
619 
620 	if ( id >= 0) {
621 		guts. defaultColormap = XCreateColormap( DISP, guts. root, guts.visual.visual, AllocNone);
622 		guts. privateColormap = 1;
623 	} else
624 		guts. defaultColormap  = DefaultColormap( DISP, SCREEN);
625 
626 	guts. monochromeMap[0] = BlackPixel( DISP, SCREEN);
627 	guts. monochromeMap[1] = WhitePixel( DISP, SCREEN);
628 	switch ( guts. visualClass) {
629 	case DirectColor:
630 	case TrueColor:
631 		guts. useDithering = false;
632 		guts. palSize = 0;
633 		break;
634 
635 	case PseudoColor:
636 		{
637 			int max;
638 			XColor xc[216];
639 			guts. dynamicColors = true;
640 			if ( guts. privateColormap && guts. palSize > 26 ) {
641 				if ( guts. palSize > 125) max = 6; else
642 				if ( guts. palSize > 64)  max = 5; else
643 				if ( guts. palSize > 27)  max = 4; else max = 3;
644 			} else
645 				max = 2;
646 			fill_cubic( xc, max);
647 			if ( !alloc_main_color_range( xc, max * max * max, 27))
648 				goto BLACK_WHITE;
649 			if ( !create_std_palettes( xc, max * max * max)) {
650 				sprintf( error_buf, "No memory");
651 				return false;
652 			}
653 			guts. colorCubeRib = max;
654 		}
655 		break;
656 
657 	case StaticColor:
658 		{
659 			int d = 6, cd = 1;
660 			XColor xc[216];
661 			while ( d > 1) {
662 				if ( d * d * d <= guts. palSize) {
663 					fill_cubic( xc, d);
664 					if ( alloc_main_color_range( xc, d * d * d, cd * 3)) {
665 						if ( !create_std_palettes( xc, d * d * d)) {
666 							sprintf( error_buf, "No memory");
667 							return false;
668 						}
669 						break;
670 					}
671 				}
672 				d--;
673 				cd += 2;
674 			}
675 			if ( !guts. palette) goto BLACK_WHITE;
676 			if ( d < 2) goto BLACK_WHITE_ALLOCATED;
677 			guts. colorCubeRib = d;
678 		}
679 		break;
680 
681 	case StaticGray:
682 	case GrayScale:
683 		{
684 			XColor xc[256];
685 			int maxSteps = ( guts. visualClass == GrayScale && !guts. privateColormap ) ? 5 : 8;
686 			int wantSteps = ( maxSteps > guts. depth) ? guts. depth : maxSteps;
687 			while ( wantSteps > 1) {
688 				int i, shades = 1 << wantSteps, c = 0, ndiv = 65536 / (shades-1);
689 				for ( i = 0; i < shades; i++) {
690 					xc[i].red = xc[i]. green = xc[i]. blue = c;
691 					if (( c += ndiv) > 65535) c = 65535;
692 				}
693 				if ( alloc_main_color_range( xc, shades, 768 / shades)) {
694 					if ( !create_std_palettes( xc, shades)) {
695 						sprintf( error_buf, "No memory");
696 						return false;
697 					}
698 					break;
699 				}
700 				wantSteps--;
701 			}
702 			if ( !guts. palette) goto BLACK_WHITE;
703 			if ( wantSteps < 1) goto BLACK_WHITE_ALLOCATED;
704 			if ( wantSteps > 4) guts. useDithering = false;
705 			guts. colorCubeRib = wantSteps;
706 			guts. grayScale = true;
707 		}
708 		break;
709 	default:
710 BLACK_WHITE:
711 		{
712 			XColor xc[2];
713 			if ( guts. privateColormap) {
714 				xc[0]. red = xc[0]. green = xc[0]. blue = 0;
715 				xc[1]. red = xc[1]. green = xc[1]. blue = 0xffff;
716 				XAllocColor( DISP, guts. defaultColormap, xc);
717 				XAllocColor( DISP, guts. defaultColormap, xc + 1);
718 				guts. monochromeMap[0] = xc[0].pixel;
719 				guts. monochromeMap[1] = xc[1].pixel;
720 			} else {
721 				xc[0]. pixel = BlackPixel( DISP, SCREEN);
722 				xc[1]. pixel = WhitePixel( DISP, SCREEN);
723 				XQueryColors( DISP, guts. defaultColormap, xc, 2);
724 			}
725 			XCHECKPOINT;
726 			if ( !alloc_main_color_range( xc, 2, 65536) ||
727 				!create_std_palettes( xc, 2)) {
728 				sprintf( error_buf, "panic: unable to initialize color system");
729 				return false;
730 			}
731 		}
732 BLACK_WHITE_ALLOCATED:
733 		guts. useDithering = true;
734 		guts. grayScale    = true;
735 		guts. colorCubeRib = 1;
736 		break;
737 	}
738 
739 	if ( guts. palSize > 0 &&
740 		(guts. visualClass == StaticColor ||
741 		guts. visualClass == StaticGray)) {
742 		int i;
743 		XColor * xc;
744 		MainColorEntry * p;
745 		if ( !( xc = malloc( sizeof( XColor) * guts. palSize))) {
746 			sprintf( error_buf, "No memory");
747 			return false;
748 		}
749 		for ( i = 0; i < guts. palSize; i++) xc[i]. pixel = i;
750 		XQueryColors( DISP, guts. defaultColormap, xc, guts. palSize);
751 		XCHECKPOINT;
752 		p = guts. palette;
753 		for ( i = 0; i < guts. palSize; i++) {
754 			p-> r = xc[i]. red / 256;
755 			p-> g = xc[i]. green / 256;
756 			p-> b = xc[i]. blue / 256;
757 			p-> composite = RGB_COMPOSITE( p-> r, p-> g, p-> b);
758 			if ( p-> rank == RANK_FREE) p-> rank  = RANK_IMMUTABLE + 1;
759 			p++;
760 		}
761 		free( xc);
762 	}
763 
764 	if (( guts. ditherPatterns = malloc( sizeof( FillPattern) * 65))) {
765 		int i, x, y;
766 		FillPattern * p = guts. ditherPatterns;
767 		Byte map [64] = {
768 			0, 48, 12, 60,  3, 51, 15, 63,
769 			32, 16, 44, 28, 35, 19, 47, 31,
770 			8, 56,  4, 52, 11, 59,  7, 55,
771 			40, 24, 36, 20, 43, 27, 39, 23,
772 			2, 50, 14, 62,  1, 49, 13, 61,
773 			34, 18, 46, 30, 33, 17, 45, 29,
774 			10, 58,  6, 54,  9, 57,  5, 53,
775 			42, 26, 38, 22, 41, 25, 37, 21
776 		};
777 		bzero( p, sizeof( FillPattern) * 65);
778 		for ( i = 0; i < 65; i++, p++)
779 			for ( y = 0; y < 8; y++)
780 				for ( x = 0; x < 8; x++)
781 					if ( i <= map[y * 8 + x])
782 						(*p)[y] |= 1 << x;
783 
784 	} else {
785 		sprintf( error_buf, "No memory");
786 		return false;
787 	}
788 	if ( guts. palSize == 0) {
789 		prima_find_color_mask_range( guts. visual. red_mask,   &guts. screen_bits. red_shift,   &guts. screen_bits. red_range);
790 		prima_find_color_mask_range( guts. visual. green_mask, &guts. screen_bits. green_shift, &guts. screen_bits. green_range);
791 		prima_find_color_mask_range( guts. visual. blue_mask,  &guts. screen_bits. blue_shift,  &guts. screen_bits. blue_range);
792 		guts. screen_bits. red_mask   = guts. visual. red_mask;
793 		guts. screen_bits. green_mask = guts. visual. green_mask;
794 		guts. screen_bits. blue_mask  = guts. visual. blue_mask;
795 	}
796 	guts. localPalSize = guts. palSize / 4 + ((guts. palSize % 4) ? 1 : 0);
797 	hatches = hash_create();
798 
799 	/* get XRDB colors */
800 	{
801 		Color c;
802 		if ( apc_fetch_resource( "Prima", "", "Color", "color", NULL_HANDLE, frColor, &c))
803 			apply_color_class( ciFore, c);
804 		if ( apc_fetch_resource( "Prima", "", "Back", "backColor", NULL_HANDLE, frColor, &c))
805 			apply_color_class( ciBack, c);
806 		if ( apc_fetch_resource( "Prima", "", "HiliteColor", "hiliteColor", NULL_HANDLE, frColor, &c))
807 			apply_color_class( ciHiliteText, c);
808 		if ( apc_fetch_resource( "Prima", "", "HiliteBackColor", "hiliteBackColor", NULL_HANDLE, frColor, &c))
809 			apply_color_class( ciHilite, c);
810 		if ( apc_fetch_resource( "Prima", "", "DisabledColor", "disabledColor", NULL_HANDLE, frColor, &c))
811 			apply_color_class( ciDisabledText, c);
812 		if ( apc_fetch_resource( "Prima", "", "DisabledBackColor", "disabledBackColor", NULL_HANDLE, frColor, &c))
813 			apply_color_class( ciDisabled, c);
814 		if ( apc_fetch_resource( "Prima", "", "Light3DColor", "light3DColor", NULL_HANDLE, frColor, &c))
815 			apply_color_class( ciLight3DColor, c);
816 		if ( apc_fetch_resource( "Prima", "", "Dark3DColor", "dark3DColor", NULL_HANDLE, frColor, &c))
817 			apply_color_class( ciDark3DColor, c);
818 	}
819 
820 
821 	/* parse user colors */
822 	if ( color_options) {
823 		int i, c_class;
824 		char *value;
825 		XColor xcolor;
826 		for ( i = 0; i < color_options-> count; i+=2) {
827 			c_class = (int)   color_options-> items[i];
828 			value   = (char*) color_options-> items[i+1];
829 			if ( XParseColor( DISP, DefaultColormap( DISP, SCREEN), value, &xcolor)) {
830 				apply_color_class( c_class, ARGB(xcolor.red >> 8, xcolor.green >> 8, xcolor.blue >> 8));
831 			} else {
832 				warn("Cannot parse color value `%s`", value);
833 			}
834 			free( value);
835 		}
836 		plist_destroy( color_options);
837 	}
838 
839 	return true;
840 }
841 
842 Bool
prima_color_subsystem_set_option(char * option,char * value)843 prima_color_subsystem_set_option( char * option, char * value)
844 {
845 	if ( strcmp( option, "visual") == 0) {
846 		if ( value) {
847 			free( do_visual);
848 			do_visual = duplicate_string( value);
849 			Mdebug( "set visual: %s\n", do_visual);
850 		} else
851 			warn("`--visual' must be given value");
852 		return true;
853 	} else if ( strcmp( option, "fg") == 0) {
854 		set_color_class( ciFore, option, value);
855 	} else if ( strcmp( option, "bg") == 0) {
856 		set_color_class( ciBack, option, value);
857 	} else if ( strcmp( option, "hilite-bg") == 0) {
858 		set_color_class( ciHilite, option, value);
859 	} else if ( strcmp( option, "hilite-fg") == 0) {
860 		set_color_class( ciHiliteText, option, value);
861 	} else if ( strcmp( option, "disabled-bg") == 0) {
862 		set_color_class( ciDisabled, option, value);
863 	} else if ( strcmp( option, "disabled-fg") == 0) {
864 		set_color_class( ciDisabledText, option, value);
865 	} else if ( strcmp( option, "light") == 0) {
866 		set_color_class( ciLight3DColor, option, value);
867 	} else if ( strcmp( option, "dark") == 0) {
868 		set_color_class( ciDark3DColor, option, value);
869 	}
870 	return false;
871 }
872 
873 typedef struct
874 {
875 	int count;
876 	unsigned long free[256];
877 } FreeColorsStruct;
878 
879 void
prima_done_color_subsystem(void)880 prima_done_color_subsystem( void)
881 {
882 	int i;
883 	FreeColorsStruct fc;
884 
885 	if ( DISP) {
886 		hash_first_that( hatches, (void*)kill_hatches, NULL, NULL, NULL);
887 		fc. count = 0;
888 
889 		for ( i = 0; i < guts. palSize; i++) {
890 			list_destroy( &guts. palette[i]. users);
891 			if (
892 				!guts. privateColormap &&
893 				guts. palette[i]. rank > RANK_FREE &&
894 				guts. palette[i]. rank <= RANK_IMMUTABLE) {
895 				fc. free[ fc. count++] = i;
896 				if ( fc. count == 256) {
897 					XFreeColors( DISP, guts. defaultColormap, fc. free, 256, 0);
898 					fc. count = 0;
899 				}
900 			}
901 		}
902 		if ( fc. count > 0)
903 			XFreeColors( DISP, guts. defaultColormap, fc. free, fc. count, 0);
904 		XFreeColormap( DISP, guts. defaultColormap);
905 	}
906 
907 	hash_destroy( hatches, false);
908 	guts. defaultColormap = 0;
909 	free( guts. ditherPatterns);
910 	free( guts. palette);
911 	free( guts. systemColorMap);
912 	guts. palette = NULL;
913 	guts. systemColorMap = NULL;
914 	guts. ditherPatterns = NULL;
915 }
916 
917 /*
918 	Finds closest possible color in system palette.
919 	Colors can be selectively filtered using maxRank
920 	parameter - if it is greater that RANK_FREE, the colors
921 	with rank lower that maxRank are not matched. Ranking can
922 	make sense when self != NULL and self != application, and
923 	of course when color cell manipulation is possible. In other
924 	words, local palette is never used if maxRank > RANK_FREE.
925 	maxDiff tells the maximal difference for a color. If
926 	no color is found that is closer than maxDiff, -1 is returned
927 	and pointer to actual diff is returned.
928 	*/
929 int
prima_color_find(Handle self,long color,int maxDiff,int * diff,int maxRank)930 prima_color_find( Handle self, long color, int maxDiff, int * diff, int maxRank)
931 {
932 	int i, j, ret = -1;
933 	int global;
934 	int b = color & 0xff;
935 	int g = (color >> 8) & 0xff;
936 	int r = (color >> 16) & 0xff;
937 	int lossy = maxDiff != 0;
938 	if ( maxDiff < 0) maxDiff = 256 * 256 * 3;
939 	global = self ? (X(self)-> type. widget && ( self != application)) : true;
940 
941 	maxDiff++;
942 	if ( global || !guts. dynamicColors || (maxRank > RANK_FREE)) {
943 		for ( i = 0; i < guts. palSize; i++) {
944 			if ( guts. palette[i]. rank > maxRank) {
945 				if ( lossy) {
946 					int d =
947 						( b - guts. palette[i].b) * ( b - guts. palette[i].b) +
948 						( g - guts. palette[i].g) * ( g - guts. palette[i].g) +
949 						( r - guts. palette[i].r) * ( r - guts. palette[i].r);
950 					if ( d < maxDiff) {
951 						ret = i;
952 						maxDiff = d;
953 						if ( maxDiff == 0) break;
954 					}
955 				} else {
956 					if ( color == guts. palette[i]. composite) {
957 						ret = i;
958 						break;
959 					}
960 				}
961 			}
962 		}
963 	} else {
964 		for ( j = 0; j < guts. systemColorMapSize + guts. palSize; j++) {
965 			if ( j < guts. systemColorMapSize)
966 				i = guts. systemColorMap[j];
967 			else {
968 				i = j - guts. systemColorMapSize;
969 				if ( wlpal_get( self, i) == RANK_FREE) continue;
970 			}
971 			if ( lossy) {
972 				int d =
973 					( b - guts. palette[i].b) * ( b - guts. palette[i].b) +
974 					( g - guts. palette[i].g) * ( g - guts. palette[i].g) +
975 					( r - guts. palette[i].r) * ( r - guts. palette[i].r);
976 				if ( d < maxDiff) {
977 					ret = i;
978 					maxDiff = d;
979 					if ( maxDiff == 0) break;
980 				}
981 			} else {
982 				if ( color == guts. palette[i]. composite) {
983 					ret = i;
984 					break;
985 				}
986 			}
987 		}
988 	}
989 	if ( diff) *diff = maxDiff;
990 	return ret;
991 }
992 
993 static Bool
prima_color_new(XColor * xc)994 prima_color_new( XColor * xc)
995 {
996 	MainColorEntry * p = guts. palette + xc-> pixel;
997 	if ( p-> rank != RANK_FREE) {
998 		XFreeColors( DISP, guts. defaultColormap, &xc-> pixel, 1, 0);
999 		return false;
1000 	}
1001 	p-> r = xc-> red >> 8;
1002 	p-> g = xc-> green >> 8;
1003 	p-> b = xc-> blue >> 8;
1004 	p-> composite = RGB_COMPOSITE(p->r,p->g,p->b);
1005 	return true;
1006 }
1007 
1008 /*
1009 	Adds reference to widget that is responsible
1010 	for a color cell with given rank. Main palette
1011 	rank can be risen in response, but not lowered -
1012 	that is accomplished by prima_color_sync.
1013 	*/
1014 Bool
prima_color_add_ref(Handle self,int index,int rank)1015 prima_color_add_ref( Handle self, int index, int rank)
1016 {
1017 	int r, nr = (rank == RANK_PRIORITY) ? 2 : 1;
1018 	if ( index < 0 || index >= guts. palSize) return false;
1019 	if ( guts. palette[index]. rank == RANK_IMMUTABLE) return false;
1020 	if ( !self || ( self == application)) return false;
1021 	r = wlpal_get(self, index);
1022 	if ( r != 0 && r <= nr) return false;
1023 	if ( r == 0) list_add( &guts. palette[index]. users, self);
1024 	if ( rank > guts. palette[index]. rank)
1025 		guts. palette[index]. rank = rank;
1026 	wlpal_set( self, index, nr);
1027 	Pdebug("color:%s %s %d %d\n", PWidget(self)-> name, r ? "raised to " : "added as", nr, index);
1028 	return true;
1029 }
1030 
1031 /* Frees stale color references */
1032 int
prima_color_sync(void)1033 prima_color_sync( void)
1034 {
1035 	int i, count = 0, freed = 0;
1036 	unsigned long free[32];
1037 	MainColorEntry * p = guts. palette;
1038 	for ( i = 0; i < guts. palSize; i++, p++) {
1039 		if ( p-> touched) {
1040 			int j, max = RANK_FREE;
1041 			for ( j = 0; j < p-> users. count; j++) {
1042 				int rank;
1043 				if ( X(p-> users. items[j])-> type. widget) {
1044 					rank = wlpal_get( p-> users. items[j], i);
1045 					if ( rank > 0)
1046 						rank = ( rank > 1) ? RANK_PRIORITY : RANK_NORMAL;
1047 				} else
1048 					rank = RANK_LOCKED;
1049 				if ( max < rank) max = rank;
1050 				if ( max == RANK_LOCKED) break;
1051 			}
1052 			p-> rank = max;
1053 			if ( max == RANK_FREE) {
1054 				free[ count++] = i;
1055 				if ( count == 32) {
1056 					XFreeColors( DISP, guts. defaultColormap, free, 32, 0);
1057 					count = 0;
1058 					freed += 32;
1059 				}
1060 			}
1061 			p-> touched = false;
1062 		}
1063 	}
1064 	if ( count > 0)
1065 		XFreeColors( DISP, guts. defaultColormap, free, count, 0);
1066 	return freed + count;
1067 }
1068 
1069 /* updates contents of DefaultColormap.  */
1070 /* NB - never to be called with 'fast' set to true. */
1071 
1072 Bool
prima_palette_replace(Handle self,Bool fast)1073 prima_palette_replace( Handle self, Bool fast)
1074 {
1075 	DEFXX;
1076 	Bool restricted = fast || XX-> type. dbm;
1077 	int rank, psz, i, j, granted, stage, menu = 0;
1078 	unsigned long * req;
1079 	RGBColor * rqx;
1080 	MainColorEntry * p;
1081 	List widgets;
1082 
1083 	if ( !guts. dynamicColors) return true;
1084 	if ( self == application) return true;
1085 
1086 	if ( XX-> type.widget) rank = RANK_PRIORITY; else
1087 	if ( XX-> type.image || XX-> type. dbm) rank = RANK_LOCKED; else
1088 		return false;
1089 
1090 	if ( !fast) prima_palette_free( self, true); /* remove old entries */
1091 
1092 	psz = PDrawable( self)-> palSize + menu;
1093 	if ( XT_IS_WINDOW(X(self)) && PWindow(self)-> menu)
1094 		psz += (menu = ciMaxId + 1);
1095 	if ( psz == 0) {
1096 		prima_color_sync();
1097 		return true;
1098 	}
1099 
1100 	if ( !( req = malloc( sizeof( unsigned long) * psz)))
1101 		return false;
1102 
1103 	for ( i = 0; i < psz - menu; i++)
1104 		req[i] = RGB_COMPOSITE(
1105 			PWidget( self)-> palette[i].r,
1106 			PWidget( self)-> palette[i].g,
1107 			PWidget( self)-> palette[i].b);
1108 	for ( i = psz - menu; i < psz; i++)
1109 		req[i] = PWindow(self)-> menuColor[ i - psz + menu];
1110 
1111 	granted = 0;
1112 
1113 	if ( !restricted) XGrabServer( DISP);
1114 
1115 	/* fetch actual colors - they are useful when no free colorcells
1116 		available, but colormap has some good colors, which we don't
1117 		possess */
1118 	if ( !restricted) {
1119 		int count = 0, j;
1120 		XColor xc[32];
1121 		for ( i = 0; i < guts.palSize; i++)
1122 			if ( guts.palette[i].rank == RANK_FREE) {
1123 				xc[count++].pixel = i;
1124 				if ( count == 32) {
1125 					XQueryColors( DISP, guts. defaultColormap, xc, 32);
1126 					for ( j = 0; j < 32; j++) prima_color_new( &xc[j]);
1127 					count = 0;
1128 				}
1129 			}
1130 		if ( count > 0) {
1131 			XQueryColors( DISP, guts. defaultColormap, xc, count);
1132 			for ( j = 0; j < count; j++) prima_color_new( &xc[j]);
1133 		}
1134 	}
1135 
1136 	Pdebug("color replace:%s find match for %d colors\n", PWidget(self)-> name, psz);
1137 	/* find out if any allocated entries are present already */
1138 	for ( i = 0; i < psz; i++)
1139 		if (( req[i] & 0x80000000) == 0) {
1140 			unsigned long c = req[i];
1141 			for ( j = 0; j < guts. palSize; j++) {
1142 				MainColorEntry * p = guts. palette + j;
1143 				int pixel = j;
1144 				if ( p-> composite == c) {
1145 					if ( !restricted && (p-> rank == RANK_FREE)) {
1146 						XColor xc;
1147 						xc. red   = COLOR_R16(req[i]);
1148 						xc. green = COLOR_G16(req[i]);
1149 						xc. blue  = COLOR_B16(req[i]);
1150 						if ( alloc_color(&xc)) {
1151 							if ( prima_color_new( &xc))
1152 								/* to protect from sync - give actual status on SUCCESS */
1153 								guts.palette[xc.pixel].rank = RANK_IMMUTABLE + 1;
1154 							pixel = xc.pixel;
1155 						} else
1156 							continue;
1157 					}
1158 					req[i] |= 0x80000000;
1159 					if ( !restricted) prima_color_add_ref( self, pixel, rank);
1160 					granted++;
1161 					break;
1162 				}
1163 			}
1164 		}
1165 
1166 	Pdebug("color replace: granted %d\n", granted);
1167 	if ( restricted) {
1168 		free( req);
1169 		return true;
1170 	}
1171 
1172 
1173 	stage = RANK_NORMAL;
1174 	list_create( &widgets, 32, 128);
1175 	if ( granted == psz) {
1176 		free( req);
1177 		goto SUCCESS;
1178 	}
1179 
1180 ALLOC_STAGE:
1181 	/* allocate some colors */
1182 	prima_color_sync();
1183 	XCHECKPOINT;
1184 	for ( i = 0; i < psz; i++)
1185 		if (( req[i] & 0x80000000) == 0) {
1186 			XColor xc;
1187 			xc. red   = COLOR_R16(req[i]);
1188 			xc. green = COLOR_G16(req[i]);
1189 			xc. blue  = COLOR_B16(req[i]);
1190 			if ( alloc_color( &xc)) {
1191 				prima_color_new( &xc);
1192 				prima_color_add_ref( self, xc. pixel, rank);
1193 				granted++;
1194 				req[i] |= 0x80000000;
1195 			} else
1196 				break;
1197 		}
1198 		Pdebug("color replace :ok - now %d are granted\n", granted);
1199 
1200 	if ( granted == psz) {
1201 		free( req);
1202 		goto SUCCESS;
1203 	}
1204 
1205 	if ( stage == RANK_NORMAL) {
1206 		/* try to remove RANK_NORMAL colors */
1207 		p = guts. palette;
1208 		for ( i = 0; i < guts. palSize; i++, p++) {
1209 			if ( p-> rank == RANK_NORMAL) {
1210 				int j;
1211 				for ( j = 0; j < p-> users. count; j++) {
1212 					Handle wij = p-> users. items[j];
1213 					if ( list_index_of( &widgets, wij) < 0)
1214 						list_add( &widgets, wij);
1215 					if ( wlpal_get(wij, i) == RANK_NORMAL)
1216 						wlpal_set( wij, i, RANK_FREE);
1217 				}
1218 				list_delete_all( &p-> users, false);
1219 				p-> touched = true;
1220 				stage = RANK_PRIORITY;
1221 			}
1222 		}
1223 		if ( stage == RANK_PRIORITY) goto ALLOC_STAGE;
1224 	}
1225 
1226 	free( req);
1227 
1228 	if ( XX-> type. image) goto SUCCESS;
1229 
1230 	/* try to remove RANK_PRIORITY entries */
1231 	p = guts. palette;
1232 	for ( i = 0; i < guts. palSize; i++, p++) {
1233 		if ( p-> rank == RANK_PRIORITY) {
1234 			int j;
1235 			for ( j = 0; j < p-> users. count; j++) {
1236 				Handle wij = p-> users. items[j];
1237 				if ( X(wij)-> type. widget && list_index_of( &widgets, wij) < 0)
1238 					list_add( &widgets, wij);
1239 				wlpal_set( wij, i, RANK_FREE);
1240 			}
1241 			list_delete_all( &p-> users, false);
1242 			p-> touched = true;
1243 		}
1244 	}
1245 
1246 	psz = prima_color_sync();
1247 	if ( psz == 0) goto SUCCESS; /* free no RANK_PRIORITY colors :(  */
1248 	XCHECKPOINT;
1249 
1250 	/* collect big palette */
1251 	j = 0;
1252 	for ( i = 0; i < guts. palSize; i++)
1253 		if ( guts. palette[i]. rank != RANK_FREE)
1254 			j++;
1255 	stage = j; /* immutable and locked colors */
1256 	for ( i = 0; i < widgets. count; i++) {
1257 		j += PWidget( widgets. items[i])-> palSize;
1258 		if ( XT_IS_WINDOW(X(widgets. items[i])) &&
1259 			PWindow(widgets. items[i])-> menu)
1260 			j += ciMaxId + 1;
1261 	}
1262 
1263 	Pdebug("color: BIG:%d vs %d\n", j, psz);
1264 	if ( !( rqx = malloc( sizeof( RGBColor) * j))) goto SUCCESS; /* :O */
1265 
1266 	{
1267 		RGBColor * r = rqx;
1268 		for ( i = 0; i < guts. palSize; i++)
1269 			if ( guts. palette[i]. rank != RANK_FREE) {
1270 				r-> r = guts. palette[i]. r;
1271 				r-> g = guts. palette[i]. g;
1272 				r-> b = guts. palette[i]. b;
1273 				r++;
1274 			}
1275 		for ( i = 0; i < widgets. count; i++) {
1276 			memcpy( r, PWidget( widgets. items[i])-> palette,
1277 				PWidget( widgets. items[i])-> palSize * sizeof( RGBColor));
1278 			r += PWidget( widgets. items[i])-> palSize;
1279 			if ( XT_IS_WINDOW(X(widgets. items[i])) &&
1280 				PWindow(widgets. items[i])-> menu) {
1281 				int k;
1282 				for ( k = 0; k <= ciMaxId; k++, r++) {
1283 					r-> r = COLOR_R(PWindow(widgets. items[i])-> menuColor[k]);
1284 					r-> g = COLOR_G(PWindow(widgets. items[i])-> menuColor[k]);
1285 					r-> b = COLOR_B(PWindow(widgets. items[i])-> menuColor[k]);
1286 				}
1287 			}
1288 		}
1289 	}
1290 
1291 	/* squeeze palette */
1292 	if ( j > psz + stage) {
1293 		int k, tolerance = 0, t2 = 0, lim = psz + stage;
1294 		while ( 1) {
1295 			for ( i = 0; i < j; i++) {
1296 				RGBColor r = rqx[i];
1297 				for ( k = (( i + 1) > stage) ? i + 1 : stage; k < j; ) {
1298 					if (
1299 						( r.r - rqx[k].r) * ( r.r - rqx[k].r) +
1300 						( r.g - rqx[k].g) * ( r.g - rqx[k].g) +
1301 						( r.b - rqx[k].b) * ( r.b - rqx[k].b)
1302 						<= t2) {
1303 						if ( k < j - 1) rqx[k] = rqx[j-1];
1304 						if ( --j <= lim) goto ENOUGH;
1305 					} else
1306 						k++;
1307 				}
1308 			}
1309 			tolerance += 2;
1310 			t2 = tolerance * tolerance;
1311 		}
1312 ENOUGH:;
1313 	}
1314 
1315 	Pdebug("color replace: ok. XAllocColor again\n");
1316 	granted = 0;
1317 	for ( i = stage; i < stage + psz; i++) {
1318 		XColor xc;
1319 		xc. red   = rqx[i]. r << 8;
1320 		xc. green = rqx[i]. g << 8;
1321 		xc. blue  = rqx[i]. b << 8;
1322 		if ( alloc_color( &xc)) {
1323 			if ( prima_color_new( &xc)) {
1324 				/* give new color NORMAL status - to be cleaned automatically */
1325 				/* upon 1st sync() invocation */
1326 				guts. palette[xc. pixel]. touched = 1;
1327 				guts. palette[xc. pixel]. rank = RANK_NORMAL;
1328 				granted++;
1329 			}
1330 		} else
1331 			break;
1332 	}
1333 	free( rqx);
1334 	Pdebug("color replace: ok - %d out of %d \n", granted, psz);
1335 	XCHECKPOINT;
1336 
1337 	/* now give away colors that can be mapped to reduced palette */
1338 	prima_palette_replace( self, true);
1339 	for ( i = 0; i < widgets. count; i++)
1340 		prima_palette_replace( widgets. items[i], true);
1341 	XCHECKPOINT;
1342 
1343 SUCCESS:
1344 
1345 	/* restore status of pre-fetched colors */
1346 	for ( i = 0; i < guts. palSize; i++)
1347 		if ( guts.palette[i].rank == RANK_IMMUTABLE + 1)
1348 			guts.palette[i].rank = RANK_PRIORITY;
1349 
1350 	prima_color_sync();
1351 
1352 	XUngrabServer( DISP);
1353 
1354 	for ( i = 0; i < widgets. count; i++)
1355 		if ( PWidget( widgets. items[i])-> stage < csDead)
1356 			apc_widget_invalidate_rect( widgets. items[i], NULL);
1357 
1358 	Pdebug("color replace: exit\n");
1359 	list_destroy( &widgets);
1360 	return true;
1361 }
1362 
1363 Bool
prima_palette_alloc(Handle self)1364 prima_palette_alloc( Handle self)
1365 {
1366 	if ( !guts. dynamicColors) return true;
1367 	if ( !( X(self)-> palette = malloc( guts. localPalSize)))
1368 		return false;
1369 	bzero( X(self)-> palette,  guts. localPalSize);
1370 	return true;
1371 }
1372 
1373 void
prima_palette_free(Handle self,Bool priority)1374 prima_palette_free( Handle self, Bool priority)
1375 {
1376 	int i, max = priority ? 2 : 1;
1377 	if ( !guts. dynamicColors) return;
1378 	for ( i = 0; i < guts. palSize; i++) {
1379 		int rank = wlpal_get(self,i);
1380 		if ( rank > RANK_FREE && max >= rank) {
1381 			wlpal_set( self, i, RANK_FREE);
1382 			list_delete( &guts. palette[i]. users, self);
1383 			Pdebug("color: %s free %d, %d\n", PWidget(self)-> name, i, rank);
1384 			guts. palette[i]. touched = true;
1385 		}
1386 	}
1387 	Pdebug(":%s for %s\n", priority ? "PRIO" : "", PWidget(self)-> name);
1388 }
1389 
1390 int
prima_lpal_get(Byte * palette,int index)1391 prima_lpal_get( Byte * palette, int index)
1392 {
1393 	return LPAL_GET( index, palette[ LPAL_ADDR( index ) ]);
1394 }
1395 
1396 void
prima_lpal_set(Byte * palette,int index,int rank)1397 prima_lpal_set( Byte * palette, int index, int rank )
1398 {
1399 	palette[ LPAL_ADDR( index ) ] &=~ LPAL_MASK( index);
1400 	palette[ LPAL_ADDR( index ) ] |=  LPAL_SET( index, rank);
1401 }
1402 
1403 
kill_hatches(Pixmap pixmap,int keyLen,void * key,void * dummy)1404 static Bool kill_hatches( Pixmap pixmap, int keyLen, void * key, void * dummy)
1405 {
1406 	XFreePixmap( DISP, pixmap);
1407 	return false;
1408 }
1409 
1410 Pixmap
prima_get_hatch(FillPattern * fp)1411 prima_get_hatch( FillPattern * fp)
1412 {
1413 	int i;
1414 	Pixmap p;
1415 	FillPattern fprev;
1416 	Byte *mirrored_bits;
1417 	if ( memcmp( fp, fillPatterns[fpSolid], sizeof( FillPattern)) == 0)
1418 		return NULL_HANDLE;
1419 	if (( p = ( Pixmap) hash_fetch( hatches, fp, sizeof( FillPattern))))
1420 		return p;
1421 
1422 	mirrored_bits = ( guts.bit_order == MSBFirst) ? NULL : prima_mirror_bits();
1423 	for ( i = 0; i < sizeof( FillPattern); i++) {
1424 		fprev[i] = (*fp)[ sizeof(FillPattern) - i - 1];
1425 		if ( guts.bit_order != MSBFirst) fprev[i] = mirrored_bits[fprev[i]];
1426 	}
1427 	if (( p = XCreateBitmapFromData( DISP, guts. root, (char*)fprev, 8, 8)) == None) {
1428 		hash_first_that( hatches, (void*)kill_hatches, NULL, NULL, NULL);
1429 		hash_destroy( hatches, false);
1430 		hatches = hash_create();
1431 		if (( p = XCreateBitmapFromData( DISP, guts. root, (char*)fprev, 8, 8)) == None)
1432 			return NULL_HANDLE;
1433 	}
1434 	hash_store( hatches, fp, sizeof( FillPattern), ( void*) p);
1435 	return p;
1436 }
1437