1 /*
2 * $Id: pals.c,v 1.1 2005-09-18 22:05:34 dhmunro Exp $
3 * palette handling for X11
4 */
5 /* Copyright (c) 2005, The Regents of the University of California.
6 * All rights reserved.
7 * This file is part of yorick (http://yorick.sourceforge.net).
8 * Read the accompanying LICENSE file for details.
9 */
10
11 #include "config.h"
12 #include "playx.h"
13 #include "pstdlib.h"
14
15 static int x_use_shared(p_scr *s, p_col_t color, p_col_t *pixel);
16 static void x_lose_shared(p_scr *s, p_col_t *pixels, int n);
17 static void x_all_shared(p_scr *s, XColor *map /*[256]*/);
18 static p_col_t x_best_shared(p_scr *s, p_col_t color,
19 XColor *map, int n);
20 static void x_cavailable(void *p, p_hashkey key, void *ctx);
21 static void x_list_dead(void *p, p_hashkey key, void *ctx);
22 static void x_find_dead(void *p, p_hashkey key, void *ctx);
23 static void x_mark_shared(void *p);
24
25 void
p_palette(p_win * w,p_col_t * colors,int n)26 p_palette(p_win *w, p_col_t *colors, int n)
27 {
28 p_scr *s = w->s;
29 p_col_t r, g, b;
30 int i;
31 if (n>240) n = 240;
32
33 if (w->parent) w = w->parent; /* pixmap uses parent palette */
34 s->gc_color = -1;
35
36 if (s->vclass==TrueColor || s->vclass==DirectColor) {
37 for (i=0 ; i<n ; i++) {
38 r = P_R(colors[i]);
39 g = P_G(colors[i]);
40 b = P_B(colors[i]);
41 w->pixels[i] = (s->pixels[r]&s->rmask) |
42 (s->pixels[g]&s->gmask) | (s->pixels[b]&s->bmask);
43 }
44 w->n_palette = n;
45
46 } else if (s->vclass!=PseudoColor) {
47 for (i=0 ; i<n ; i++) {
48 r = P_R(colors[i]);
49 g = P_G(colors[i]);
50 b = P_B(colors[i]);
51 w->pixels[i] = s->pixels[(r+g+b)/3];
52 }
53 w->n_palette = n;
54
55 } else if (w->rgb_pixels) {
56 for (i=0 ; i<n ; i++) {
57 r = P_R(colors[i]);
58 g = P_G(colors[i]);
59 b = P_B(colors[i]);
60 r = (r+32)>>6;
61 g = (g+16)>>5;
62 b = (b+32)>>6;
63 g += b+(b<<3);
64 w->pixels[i] = w->rgb_pixels[r+g+(g<<2)]; /* r + 5*g * 45*b */
65 }
66 w->n_palette = n;
67
68 } else {
69 XColor c;
70 int p;
71 p_col_t fg_pixel = s->colors[1].pixel;
72 Display *dpy = s->xdpy->dpy;
73 Visual *visual = DefaultVisual(dpy,s->scr_num);
74 int map_size = visual->map_entries;
75 if (map_size>256) map_size = 256;
76
77 if (w->cmap==None) {
78 /* this window uses default colormap (read-only shared colors) */
79 int n_old = w->n_palette;
80
81 /* (1) free our use of existing colors */
82 w->n_palette = 0;
83 x_lose_shared(s, w->pixels, n_old);
84 for (i=0 ; i<n_old ; i++) w->pixels[i] = fg_pixel;
85 if (n<=0) return;
86
87 /* (2) go for it, but prudently ask in bit reversed order */
88 for (i=0 ; i<256 ; i++) {
89 p = p_bit_rev[i];
90 if (p>=n) continue;
91 if (!x_use_shared(s, colors[p], &w->pixels[p])) break;
92 w->n_palette++;
93 }
94
95 /* (3) fallback if didn't get full request */
96 if (w->n_palette<n) {
97 XColor map[256];
98 int nsh;
99
100 /* query all colors, reserving a use of all sharable colors */
101 x_all_shared(s, map);
102 for (nsh=0 ; nsh<256 ; nsh++) {
103 if (map[nsh].flags) break;
104 map[nsh].red = (map[nsh].red >>8)&0xff;
105 map[nsh].green = (map[nsh].green>>8)&0xff;
106 map[nsh].blue = (map[nsh].blue >>8)&0xff;
107 }
108
109 /* take closest sharable pixel to those requested
110 * - allocate extra uses for colors used more than once */
111 for (; i<256 ; i++) {
112 p = p_bit_rev[i];
113 if (p>=n) continue;
114 w->pixels[p] = x_best_shared(s, colors[p], map, nsh);
115 w->n_palette++;
116 }
117
118 /* release shared colors we didn't ever use */
119 x_lose_shared(s, (p_col_t *)0, 0);
120 }
121
122 } else {
123 /* this window has a private colormap (read-write private colors) */
124 char used[256];
125
126 /* private colormaps will flash display when they are installed
127 * take two steps to minimize the annoyance:
128 * (1) be sure to allocate the 16 standard colors
129 * s->colors[0:15] the same as default cmap on screen
130 * -- x_color algorithm fails without this,
131 * done when cmap created
132 * (2) allocate palette colors from top down, since most
133 * X servers seem to pass out colors from bottom up and
134 * important permanent apps (window manager) started first
135 * -- allocate colors at bottom same as in default cmap */
136 for (i=0 ; i<map_size ; i++) used[i] = 0;
137 for (i=0 ; i<16 ; i++)
138 if (s->colors[i].pixel<map_size) used[s->colors[i].pixel] = 1;
139 for (p=map_size-1,i=0 ; p>=0 && i<n ; p--) {
140 if (used[p]) continue;
141 c.pixel = w->pixels[i] = p;
142 c.red = P_R(colors[i]) << 8;
143 c.green = P_G(colors[i]) << 8;
144 c.blue = P_B(colors[i]) << 8;
145 i++;
146 c.flags = DoRed | DoGreen | DoBlue;
147 XStoreColor(dpy, w->cmap, &c);
148 }
149 for (; i<240 ; i++) w->pixels[i] = fg_pixel;
150 if (p>=0) {
151 /* restore as much of the default colormap as possible */
152 Colormap cmap = DefaultColormap(dpy,s->scr_num);
153 XColor map[256];
154 for (i=0 ; i<=p ; i++) map[i].pixel = i;
155 XQueryColors(dpy, cmap, map, p+1);
156 for (i=0 ; i<=p ; i++) {
157 if (used[i]) continue;
158 map[i].flags = DoRed | DoGreen | DoBlue;
159 XStoreColor(dpy, w->cmap, &map[i]);
160 }
161 }
162 w->n_palette = n;
163 }
164 }
165 if (p_signalling) p_abort();
166 }
167
168 int
x_rgb_palette(p_win * w)169 x_rgb_palette(p_win *w)
170 {
171 if (w->parent) w = w->parent;
172 if (!w->rgb_pixels) {
173 p_scr *s = w->s;
174 p_col_t *pixels;
175 int i;
176 if (s->vclass!=PseudoColor) return 0;
177
178 /* would be friendlier to translate preexisting
179 * palette, but nuking it is far simpler */
180 p_palette(w, p_595, 225);
181 x_tmpzap(&s->tmp);
182 pixels = s->tmp = p_malloc(sizeof(p_col_t)*256);
183 if (!pixels) return 0;
184 for (i=0 ; i<256 ; i++) pixels[i] = w->pixels[i];
185 s->tmp = 0;
186 w->rgb_pixels = pixels;
187 p_palette(w, (p_col_t *)0, 0);
188 }
189 return 1;
190 }
191
192 /*------------------------------------------------------------------------*/
193
194 struct x_cshared {
195 unsigned long *usepxl; /* [uses,pixel] for each pixel */
196 unsigned long nextpxl; /* index into next free usepxl pair */
197 p_hashtab *bypixel; /* returns usepxl index given pixel value */
198 p_hashtab *bycolor; /* returns usepxl index given color value */
199 /* note: bypixel hash table is unnecessary if we were guaranteed
200 * that the pixel values are <256 for PseudoColor displays,
201 * as I assume in a few places (e.g.- x_all_shared below)
202 * - nevertheless, it ensures that x_use_shared, x_lose_shared
203 * and x_nuke_shared work for all possible X servers
204 */
205 };
206
207 struct x_deadpix {
208 unsigned long list[256], keys[256];
209 int n, m;
210 x_cshared *shared;
211 };
212 struct x_c2pix {
213 XColor *map;
214 int n;
215 };
216
217 static int
x_use_shared(p_scr * s,p_col_t color,p_col_t * pixel)218 x_use_shared(p_scr *s, p_col_t color, p_col_t *pixel)
219 {
220 x_cshared *shared = s->shared;
221 p_hashkey colkey = P_IHASH(color);
222 unsigned long *usepxl;
223
224 usepxl = p_hfind(shared->bycolor, colkey);
225
226 if (!usepxl) {
227 Display *dpy = s->xdpy->dpy;
228 Colormap cmap = DefaultColormap(dpy, s->scr_num);
229 p_hashkey pixkey;
230 XColor c;
231 unsigned long nextpxl = shared->nextpxl;
232 if (nextpxl>=512) return 0; /* only provide for 256 shared colors */
233 c.red = P_R(color) << 8;
234 c.green = P_G(color) << 8;
235 c.blue = P_B(color) << 8;
236 if (!XAllocColor(dpy, cmap, &c)) return 0; /* default colormap is full */
237 pixkey = P_IHASH(c.pixel);
238 usepxl = p_hfind(shared->bypixel, pixkey);
239 if (usepxl) { /* different colors may give same pixel */
240 XFreeColors(dpy, cmap, &c.pixel, 1, 0UL);
241 p_hinsert(shared->bycolor, colkey, usepxl);
242 } else {
243 usepxl = shared->usepxl + nextpxl;
244 shared->nextpxl = usepxl[0];
245 usepxl[0] = 0; /* this will be first use */
246 usepxl[1] = c.pixel;
247 p_hinsert(shared->bypixel, pixkey, usepxl);
248 p_hinsert(shared->bycolor, colkey, usepxl);
249 }
250 }
251
252 usepxl[0]++;
253 *pixel = usepxl[1];
254 return 1;
255 }
256
257 static void
x_lose_shared(p_scr * s,p_col_t * pixels,int n)258 x_lose_shared(p_scr *s, p_col_t *pixels, int n)
259 {
260 x_cshared *shared = s->shared;
261 unsigned long *usepxl;
262 struct x_deadpix deadpix;
263 int i;
264
265 if (!shared) {
266 shared = p_malloc(sizeof(x_cshared));
267 if (!shared) return;
268 shared->bycolor = p_halloc(256);
269 shared->bypixel = p_halloc(256);
270 shared->usepxl = p_malloc(sizeof(unsigned long)*512);
271 if (!shared->bycolor || !shared->usepxl) return;
272 shared->nextpxl = 0;
273 for (i=0 ; i<512 ; i+=2) shared->usepxl[i] = i+2;
274 s->shared = shared;
275 }
276
277 while ((n--)>0) {
278 usepxl = p_hfind(shared->bypixel, P_IHASH(pixels[n]));
279 if (usepxl && usepxl[0]) usepxl[0]--;
280 }
281 deadpix.shared = shared;
282 deadpix.n = deadpix.m = 0;
283 p_hiter(shared->bycolor, &x_find_dead, &deadpix);
284 for (i=0 ; i<deadpix.m ; i++)
285 p_hinsert(shared->bycolor, deadpix.keys[i], (void*)0);
286 p_hiter(shared->bypixel, &x_list_dead, &deadpix); /* also unlinks */
287 for (i=0 ; i<deadpix.n ; i++)
288 p_hinsert(shared->bypixel, P_IHASH(deadpix.list[i]), (void*)0);
289 if (deadpix.n) {
290 Display *dpy = s->xdpy->dpy;
291 XFreeColors(dpy, DefaultColormap(dpy,s->scr_num),
292 deadpix.list, deadpix.n, 0UL);
293 }
294 }
295
296 static void
x_all_shared(p_scr * s,XColor * map)297 x_all_shared(p_scr *s, XColor *map /*[256]*/)
298 {
299 x_cshared *shared = s->shared;
300 if (shared) {
301 int i, n;
302 unsigned long *usepxl;
303 struct x_c2pix available;
304 Display *dpy = s->xdpy->dpy;
305 Colormap cmap = DefaultColormap(dpy,s->scr_num);
306 Visual *visual = DefaultVisual(dpy,s->scr_num);
307 int map_size = visual->map_entries;
308 if (map_size>256) map_size = 256;
309
310 for (i=n=0 ; i<map_size ; i++)
311 if (!p_hfind(shared->bypixel, P_IHASH(i)))
312 map[n++].pixel = i;
313 if (!n) return; /* we already own all shared colors */
314
315 XQueryColors(dpy, cmap, map, n);
316 for (i=0 ; i<n ; i++)
317 if (XAllocColor(dpy, cmap, &map[i])) {
318 /* this color is sharable, record in bypixel table
319 * -includes the standard colors */
320 usepxl = shared->usepxl + shared->nextpxl;
321 shared->nextpxl = usepxl[0];
322 usepxl[0] = 0; /* we haven't actually used it yet */
323 usepxl[1] = map[i].pixel;
324 p_hinsert(shared->bypixel, P_IHASH(map[i].pixel), usepxl);
325 if (shared->nextpxl>=512) break;
326 }
327 available.map = map;
328 available.n = 0;
329 p_hiter(shared->bypixel, &x_cavailable, &available);
330 XQueryColors(dpy, cmap, map, available.n);
331 for (i=0 ; i<available.n ; i++) map[i].flags = 0;
332 if (i<256) map[i].flags = 1;
333 }
334 }
335
336 static p_col_t
x_best_shared(p_scr * s,p_col_t color,XColor * map,int n)337 x_best_shared(p_scr *s, p_col_t color, XColor *map, int n)
338 {
339 unsigned long *usepxl;
340 long d1, d0, tmp;
341 int j, k;
342 int r = P_R(color);
343 int g = P_G(color);
344 int b = P_B(color);
345 d0 = 3*256*256;
346 for (k=j=0 ; k<n ; k++) {
347 d1 = ((tmp = map[k].red - r), tmp*tmp);
348 d1 += ((tmp = map[k].green - g), tmp*tmp);
349 d1 += ((tmp = map[k].blue - b), tmp*tmp);
350 if (d1<d0) j = k, d0 = d1;
351 }
352 usepxl = p_hfind(s->shared->bypixel, P_IHASH(map[j].pixel));
353 usepxl[0]++;
354 return usepxl[1];
355 }
356
357 void
x_nuke_shared(p_scr * s)358 x_nuke_shared(p_scr *s)
359 {
360 x_cshared *shared = s->shared;
361 if (shared) {
362 unsigned long *usepxl = shared->usepxl;
363 void (*noaction)(void *)= 0;
364 Display *dpy = s->xdpy->dpy;
365 int i, n;
366 s->shared = 0;
367 p_hfree(shared->bypixel, noaction);
368 p_hfree(shared->bycolor, &x_mark_shared);
369 for (i=n=0 ; i<512 ; i+=2)
370 if (usepxl[i]==1) usepxl[n++] = usepxl[i+1];
371 if (n)
372 XFreeColors(dpy, DefaultColormap(dpy,s->scr_num), usepxl, n, 0UL);
373 p_free(usepxl);
374 p_free(shared);
375 }
376 if (p_signalling) p_abort();
377 }
378
379 /* ARGSUSED */
380 static void
x_cavailable(void * p,p_hashkey key,void * ctx)381 x_cavailable(void *p, p_hashkey key, void *ctx)
382 {
383 unsigned long *usepxl = p;
384 struct x_c2pix *available = ctx;
385 available->map[available->n++].pixel = usepxl[1];
386 }
387
388 /* ARGSUSED */
389 static void
x_list_dead(void * p,p_hashkey key,void * ctx)390 x_list_dead(void *p, p_hashkey key, void *ctx)
391 {
392 unsigned long *usepxl = p;
393 if (!usepxl[0]) {
394 struct x_deadpix *deadpix = ctx;
395 deadpix->list[deadpix->n++] = usepxl[1];
396 usepxl[0] = deadpix->shared->nextpxl;
397 deadpix->shared->nextpxl = usepxl - deadpix->shared->usepxl;
398 }
399 }
400
401 static void
x_find_dead(void * p,p_hashkey key,void * ctx)402 x_find_dead(void *p, p_hashkey key, void *ctx)
403 {
404 unsigned long *usepxl = p;
405 if (!usepxl[0]) {
406 struct x_deadpix *deadpix = ctx;
407 deadpix->keys[deadpix->m++] = key;
408 }
409 }
410
411 static void
x_mark_shared(void * p)412 x_mark_shared(void *p)
413 {
414 unsigned long *usepxl = p;
415 usepxl[0] = 1; /* 1 cannot be in nextpxl free list (always even) */
416 }
417