-
Notifications
You must be signed in to change notification settings - Fork 6
/
cColorManager.c
1484 lines (1284 loc) · 43.5 KB
/
cColorManager.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*************************************************************************\
* Copyright (c) 1994-2004 The University of Chicago, as Operator of Argonne
* National Laboratory.
* Copyright (c) 1997-2003 Southeastern Universities Research Association,
* as Operator of Thomas Jefferson National Accelerator Facility.
* Copyright (c) 1997-2002 Deutches Elektronen-Synchrotron in der Helmholtz-
* Gemelnschaft (DESY).
* This file is distributed subject to a Software License Agreement found
* in the file LICENSE that is included with this distribution.
\*************************************************************************/
#define DEBUG_SCM 0
#include "cColorManager.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "Strip.h"
#ifndef max
#define max(a,b) ((a)>(b)?(a):(b))
#endif
#ifndef min
# define min(a,b) ((a)<(b)?(a):(b))
#endif
/* ccmVisualGetClassName
*
* Given a class (from the XVisualInfo structure), returns
* the appropriate string describing it.
*/
#define ccmVisualGetClassName(visual_class) \
(visual_class == DirectColor? "DirectColor" : \
visual_class == GrayScale? "GrayScale" : \
visual_class == PseudoColor? "PseudoColor" : \
visual_class == StaticColor? "StaticColor" : \
visual_class == StaticGray? "StaticGray" : \
visual_class == TrueColor? "TrueColor" : \
"[Unknown!]")
/* ccmVisualGetClass
*
* Given the address of an XVisualInfo structure, returns
* the class member. This is needed because the XVisualInfo
* strucutre is defined differently when compiled with C than
* when compiled with C++.
*/
#if defined(__cplusplus) || defined(C_plusplus)
# define ccmVisualGetClass(visual_info_ptr) ((visual_info_ptr)->c_class)
#else
# define ccmVisualGetClass(visual_info_ptr) ((visual_info_ptr)->class)
#endif
/* ccmVisualSetClass
*
* Sets the class member of the XVisualInfo structure whose
* address is passed in.
*/
#if defined(__cplusplus) || defined(C_plusplus)
# define ccmVisualSetClass(visual_info_ptr, class_type) \
((visual_info_ptr)->c_class = class_type)
#else
# define ccmVisualSetClass(visual_info_ptr, class_type) \
((visual_info_ptr)->class = class_type)
#endif
/* ccmVisualGetClassIsWritable
*
* Returns true if the visual class is writable visual.
*/
#define ccmVisualIsWritable(visual_class) \
((visual_class == DirectColor) || \
(visual_class == PseudoColor) || \
(visual_class == GrayScale))
typedef struct _scmKeepStruct
{
Pixel pixel;
short writable;
short ref;
}
scmKeepStruct;
typedef struct _CCMinfo
{
Display *display;
XVisualInfo xvi;
Colormap cmap;
int private_cmap;
/* preallocated pixels */
Pixel grab_pix[CCM_MAX_PREALLOC_CELLS];
int n_grab;
/* colors currently being used */
scmKeepStruct keep[CCM_MAX_KEEP_CELLS];
int n_keep;
/* current palette */
XColor palette[CCM_MAX_PALETTE_SIZE];
int palette_size;
}
CCMinfo;
#if !defined (DEBUG_COLORALLOC)
# define XwAllocColor XAllocColor
# define XwAllocColorCells XAllocColorCells
# define XwFreeColors XFreeColors
#else
/*********************************************************************
* D E B U G G I N G C O D E
*********************************************************************/
#define MAX_ALLOCATED 1024
static struct _ColorDebugInfo
{
Pixel pixel;
int ref;
} allocated[MAX_ALLOCATED];
static int n_allocated = 0;
static Status XwAllocColor (Display *dpy,
Colormap cmap,
XColor *colorcell_in_out)
{
Status stat;
int i;
if (stat = XAllocColor (dpy, cmap, colorcell_in_out))
{
for (i = 0; i < n_allocated; i++)
if (allocated[i].pixel == colorcell_in_out->pixel)
break;
if (i < n_allocated)
allocated[i].ref++;
else if (n_allocated < MAX_ALLOCATED) {
allocated[n_allocated].pixel = colorcell_in_out->pixel;
allocated[n_allocated].ref = 1;
n_allocated++;
}
else fprintf (stderr, "XwAllocColor: buffer overflow!\n");
}
return stat;
}
static Status XwAllocColorCells (Display *dpy,
Colormap cmap,
Bool contig,
unsigned long *plane_masks_return,
unsigned int nplanes,
unsigned long *pixels_return,
unsigned int npixels_return)
{
Status stat;
int i, j;
stat = XAllocColorCells
(dpy, cmap, contig,
plane_masks_return, nplanes,
pixels_return, npixels_return);
if (stat)
{
/* search through list of allocated colors and increment reference
* count for pixels already allocated, add new pixels. */
for (i = 0; i < npixels_return; i++)
{
for (j = 0; j < n_allocated; j++)
if (pixels_return[i] == allocated[j].pixel)
break;
if (j < n_allocated) /* found? */
allocated[j].ref++;
else if (n_allocated < MAX_ALLOCATED) {
allocated[n_allocated].pixel = pixels_return[i];
allocated[n_allocated].ref = 1;
n_allocated++;
}
else fprintf (stderr, "XwAllocColorCells: buffer overflow!\n");
}
}
return stat;
}
static Status XwFreeColors (Display *dpy,
Colormap cmap,
unsigned long *pixels,
int npixels,
unsigned long planes)
{
int i, j;
/* search through list of allocated colors and decrement reference
* count for pixels, remove unreferenced pixels. */
for (i = 0; i < npixels; i++)
{
for (j = 0; j < n_allocated; j++)
if (pixels[i] == allocated[j].pixel)
break;
if (j < n_allocated) /* found? */
{
if (--allocated[j].ref < 0)
fprintf
(stderr,
"XwFreeColors: pixel freed multiple times: %u\n",
allocated[j].pixel);
}
else
{
fprintf
(stderr,
"XwFreeColorCells: freeing unallocated pixel: %u!\n",
pixels[i]);
fflush (stderr);
}
}
/* pack the array of allocated cells to eliminate those just removed */
for (i = 0, j = 0; i < n_allocated; i++)
{
if (allocated[i].ref <= 0)
j++;
else if (j > 0)
allocated[i-j] = allocated[i];
}
n_allocated -= j;
return XFreeColors (dpy, cmap, pixels, npixels, planes);
}
#endif
/* compare_pixels
*
* comparison function for quicksort.
*/
int compare_pixels (const void *pa, const void *pb)
{
Pixel a = *(Pixel *)pa;
Pixel b = *(Pixel *)pb;
/* can't just subtract Pixels because they are unsigned */
if (a < b) return -1;
else return (a > b);
}
/* compare_xcolors_by_pixel
*
* comparison function for quicksort.
*/
int compare_xcolors_by_pixel (const void *pa, const void *pb)
{
XColor *a = (XColor *)pa;
XColor *b = (XColor *)pb;
/* can't just subtract Pixels because they are unsigned */
if (a->pixel < b->pixel) return -1;
else return (a->pixel > b->pixel);
}
/* compare_keep_by_pixel
*
* comparison function for quicksort.
*/
int compare_keep_by_pixel (const void *pa, const void *pb)
{
scmKeepStruct *a = (scmKeepStruct *)pa;
scmKeepStruct *b = (scmKeepStruct *)pb;
/* can't just subtract Pixels because they are unsigned */
if (a->pixel < b->pixel) return -1;
else return (a->pixel > b->pixel);
}
/*
* cColorManager_getvisual
*
* Fills the passed-in XVisualInfo structure with info for
* the visual which matches the masked template (choosing
* the one with greatest depth if several match), and
* returns the address of the buffer, or NULL pointer if
* no compatible visuals are available.
*/
XVisualInfo *getvisual (Display *display,
XVisualInfo *pxv,
long mask)
{
XVisualInfo xv_template, *xvlist;
int n, i;
xv_template = *pxv;
xvlist = XGetVisualInfo (display, mask, &xv_template, &n);
if (n <= 0)
pxv = NULL;
else
{
for (i = --n; n >= 0; n--)
if (xvlist[n].depth > xvlist[i].depth) i = n;
*pxv = xvlist[i];
XFree (xvlist);
}
return pxv;
}
/*
* cColorManager_init
*/
cColorManager cColorManager_init (Display *dpy,
int n_req)
{
CCMinfo *scmi;
XVisualInfo xvi;
XColor *cdefs;
Pixel *pixels;
int stat;
int i;
int visual_class;
int n_cells;
int r_shift, g_shift, b_shift;
unsigned short r_max, g_max, b_max;
/* initializations of xvi added to eliminate compile warnings */
xvi.bits_per_rgb=0;
xvi.blue_mask=0;
xvi.green_mask=0;
xvi.red_mask=0;
xvi.visual=0;
xvi.depth=0;
xvi.screen=0;
xvi.colormap_size=0;
xvi.class=0;
/* get info about default visual */
xvi.visualid = XVisualIDFromVisual (DefaultVisual (dpy, DefaultScreen(dpy)));
if (!getvisual (dpy, &xvi, VisualIDMask))
{
fprintf (stderr, "unable to get info for installed visual!\n");
return 0;
}
/* If we don't need writable colors, then we can just use the
* default visual. If it doesn't support color, too bad.
*
* If writable colors are requested, then one of several things
* must happen. If the default visual is writable, try to
* allocate n writable cells, creating a private colormap if
* necessary. If the default visual is read-only, search for
* a writable one. If successful, create a private colormap,
* otherwise fall back to default visual.
*/
n_req = min (n_req, CCM_MAX_PREALLOC_CELLS);
if (n_req > 0) /* do we want some writable color cells? */
{
visual_class = ccmVisualGetClass (&xvi);
if ((visual_class != PseudoColor) && (visual_class != DirectColor))
{
fprintf
(stderr,
"Default visual does not support writable color cells.\n"
"Looking for color visual supporting %d or more writable cells...\n",
n_req);
/* NOTE: for the following, even if we find a writable visual,
* we will have still failed to find an adequate one if it can't
* support the requested number of writable colorcells.
*/
/* PseudoColor ? */
ccmVisualSetClass (&xvi, PseudoColor);
if (getvisual (dpy, &xvi, VisualClassMask | VisualScreenMask))
stat = (xvi.colormap_size >= n_req);
else stat = 0;
/* DirectColor ? */
if (!stat)
{
ccmVisualSetClass (&xvi, DirectColor);
if (getvisual (dpy, &xvi, VisualClassMask | VisualScreenMask))
stat = (xvi.colormap_size >= n_req);
else stat = 0;
if (stat)
{
fprintf
(stderr,
"Display supports DirectColor, but this application does not\n"
"yet correctly deal with this visual class\n");
stat = 0;
}
}
/* failure ? */
if (!stat)
{
fprintf (stderr, "No such luck! Using default visual.\n");
/* fall back to default visual */
xvi.visualid = XVisualIDFromVisual (DefaultVisual (dpy, xvi.screen));
getvisual (dpy, &xvi, VisualIDMask);
}
/* result */
fprintf
(stderr,
"using visual %d\n"
"depth ...... %u\n"
"class ...... %s\n",
(int)xvi.visualid, xvi.depth,
ccmVisualGetClassName (ccmVisualGetClass (&xvi)));
}
}
/* we now know that we have a permissible visual. Create and
* initialize the info structure */
if (!(scmi = (CCMinfo *)malloc (sizeof (CCMinfo))))
{
fprintf (stderr, "cColorManager_init: unable to allocate memory\n");
return 0;
}
scmi->display = dpy;
scmi->xvi = xvi;
scmi->cmap = DefaultColormap (dpy, xvi.screen);
scmi->n_grab = 0;
scmi->n_keep = 0;
scmi->private_cmap = (xvi.visual != DefaultVisual (dpy, xvi.screen));
scmi->palette_size = 0;
visual_class = ccmVisualGetClass (&xvi);
/* if private colors were requested and we have a writable
* visual then try to allocate the desired number of cells.
* On failure, use a private colormap.
*/
if ((n_req > 0) && ccmVisualIsWritable (visual_class))
{
/* if we are using the default visual, and can't allocate
* from the default colormap, we need a private colormap */
if (xvi.visual == DefaultVisual (dpy, xvi.screen))
{
scmi->private_cmap = !XwAllocColorCells
(dpy, DefaultColormap (dpy, xvi.screen), 0, 0, 0,
scmi->grab_pix, n_req);
if (!scmi->private_cmap) scmi->n_grab = n_req;
}
}
/* If we need to create a private colormap, now's the time
* to do so.
*/
if (scmi->private_cmap)
{
scmi->cmap = XCreateColormap
(dpy, RootWindow (dpy, xvi.screen), xvi.visual, AllocNone);
/* now, if we are using a writable visual of the same depth as
* the default visual, let's get all of the colors from the
* default colormap and map them into all but n_req of cells
* in the new private colormap. This will decrease the number
* of color cells which will display false colors when the
* virtual colormaps are swapped in and out of hardware. This
* makes sense for PseudoColor visuals, but is of dubious value
* for DirectColor :(
*/
if ((stat = (scmi->cmap != DefaultColormap (dpy, xvi.screen))) &&
ccmVisualIsWritable (visual_class) &&
(xvi.depth == DefaultDepth (dpy, xvi.screen)))
{
n_cells = xvi.colormap_size;
cdefs = (XColor *)malloc (n_cells * sizeof (XColor));
if (!cdefs) {
fprintf (stderr, "unable to allocate memory\n");
free (scmi);
return 0;
}
pixels = (Pixel *)malloc (n_cells * sizeof (Pixel));
if (!pixels) {
fprintf (stderr, "unable to allocate memory\n");
free (cdefs); free (scmi);
return 0;
}
/* build array of color cell references */
if (visual_class == DirectColor)
{
/* only need subset of valid pixel values in order
* to gather all RGB values stored in pixel subfields */
/* red bits */
for (r_shift = 0; !((xvi.red_mask >> r_shift) & 1); r_shift++);
r_max = (unsigned short) (xvi.red_mask >> r_shift);
/* green bits */
for (g_shift = 0; !((xvi.green_mask >> g_shift) & 1); g_shift++);
g_max = (unsigned short) (xvi.red_mask >> g_shift);
/* blue bits */
for (b_shift = 0; !((xvi.blue_mask >> b_shift) & 1); b_shift++);
b_max = (unsigned short) (xvi.blue_mask >> b_shift);
/* now permute the various combinations of rgb values */
for (i = 0; i < n_cells; i++)
{
cdefs[i].red = (i % r_max) << r_shift;
cdefs[i].green = (i % g_max) << g_shift;
cdefs[i].blue = (i % b_max) << b_shift;
cdefs[i].flags = DoRed | DoGreen | DoBlue;
}
}
else
{
/* valid range of pixel values is {0, ..., colormap_size - 1} */
for (i = 0; i < n_cells; i++)
{
cdefs[i].pixel = (Pixel)i;
cdefs[i].flags = DoRed | DoGreen | DoBlue;
}
}
/* query the rgb colors for all pixels */
stat = XQueryColors
(dpy, DefaultColormap (dpy, xvi.screen), cdefs, n_cells);
/* !!! ALERT !!! this is a hack.
*
* The following snippet of code assumes that a color cell
* retains its rgb values after it has been freed by the
* application. This assumption makes sense, but is not
* actually defined anywhere, and is therefore not robust.
*
* There seems to be no straight-forward way to copy a colormap
* without allocating all of its cells non-sharable. At first,
* this doesn't seem like a problem --"this application is using
* a private colormap, so why should it care whether all the color
* cells are non-sharable?" However, the motif library wants to
* use the Xlib color routines to get pixel values for default
* colors (e.g. XmNbackground), and hence the problem arises.
*
* hack: Allocate all colorcells in the private colormap, then
* write the corresponding values from the old colormap. After
* this has been accomplished, free all but the topmost n_req cells,
* and then reallocate them as sharable.
*/
if (stat)
stat = XwAllocColorCells (dpy, scmi->cmap, False, 0, 0, pixels, n_cells);
if (stat) stat = XStoreColors (dpy, scmi->cmap, cdefs, n_cells);
if (stat)
{
/* make sure the pixels are sorted in ascending order so that when
* we free the first cells in the array, we are actually freeing the
* bottom cells from the colormap */
qsort (pixels, n_cells, sizeof(Pixel), compare_pixels);
stat = XwFreeColors (dpy, scmi->cmap, pixels, n_cells - n_req, 0);
for (i = 0; i < (n_cells - n_req); i ++)
XwAllocColor (dpy, scmi->cmap, &cdefs[i]);
}
if (stat)
{
/* pre-allocate the topmost n_req color cells */
for (i = 0; i < n_req; i++)
scmi->grab_pix[i] = cdefs[n_cells - i - 1].pixel;
scmi->n_grab = n_req;
}
free (cdefs);
free (pixels);
}
/* if stat is false, then our operation was unsuccessful.
* fall back to the default visual */
if (!stat)
{
fprintf
(stderr,
"Sorry, unable to create private colormap.\n"
"Falling back to default visual and default colormap\n");
xvi.visualid = XVisualIDFromVisual (DefaultVisual (dpy, xvi.screen));
getvisual (dpy, &xvi, VisualIDMask);
scmi->xvi = xvi;
scmi->cmap = DefaultColormap (dpy, xvi.screen);
scmi->n_grab = 0;
scmi->private_cmap = 0;
}
}
#if DEBUG_SCM && 0
printf("cColorManager_init: scmi=%x\n",scmi);
#endif
return (cColorManager)scmi;
}
/*
* cColorManager_delete
*/
void cColorManager_delete (cColorManager the_scm)
{
CCMinfo *scmi = (CCMinfo *)the_scm;
Pixel pixels[CCM_MAX_PALETTE_SIZE];
int i;
#if DEBUG_SCM
printf("cColorManager_delete: the_scm=%x\n",the_scm);
#endif
/* ignore errors since some cells may be private resources, or fix
this up */
XSync(scmi->display, False);
Strip_ignorexerrors();
if (scmi->private_cmap)
{
#if DEBUG_SCM
printf(" XFreeColormap\n");
#endif
XFreeColormap (scmi->display, scmi->cmap);
}
else if (scmi->n_grab > 0)
{
#if DEBUG_SCM
printf(" XFreeColormap\n");
#endif
XwFreeColors (scmi->display, scmi->cmap, scmi->grab_pix, scmi->n_grab, 0);
}
if (scmi->palette_size > 0)
{
#if DEBUG_SCM
printf(" XFreeColors palette_size=%d\n",scmi->palette_size);
#endif
for (i = 0; i < scmi->palette_size; i++)
pixels[i] = scmi->palette[i].pixel;
XwFreeColors (scmi->display, scmi->cmap, pixels, i, 0);
}
if (scmi->n_keep > 0)
{
#if DEBUG_SCM
printf(" XFreeColors n_keep=%d\n",scmi->n_keep);
#endif
for (i = 0; i < scmi->n_keep; i++)
pixels[i] = scmi->keep[i].pixel;
XwFreeColors (scmi->display, scmi->cmap, pixels, i, 0);
}
#if DEBUG_SCM
printf(" XFreeColors scmi=%x\n",scmi);
#endif
/* start handling errors again */
XSync(scmi->display, False);
Strip_handlexerrors();
free (scmi);
}
/*
* cColorManager_build_palette
*/
int cColorManager_build_palette (cColorManager the_scm,
XColor *buf,
int n_max)
{
CCMinfo *scmi = (CCMinfo *)the_scm;
int visual_class;
int i, j, k;
int stat;
int n_got;
int n_cells, n_writable, n_xcolors;
Pixel *cells;
Pixel pix;
XColor *xcolors;
XColor xcolor;
n_max = min (CCM_MAX_PALETTE_SIZE, n_max);
/* ok, we need to build a palette (an ordered assortment of color
* cells containing a wide variety of colors), comprised of readable
* but not writable cells contained in the colormap. If we are
* using a read-only visual, we just need to select an appropriate
* assortment of colors. If, however, we are using a writable visual
* and a non-private colormap, then we must gather only those cells
* which are sharable, but not writable (don't want to take potentially
* writable colorcells away from other apps).
*/
/* for the time-being, just grab any old color cells. I'll put
* in some more sophisticated color selection maximizing contrast
* once I've got everything else working :) */
visual_class = ccmVisualGetClass (&scmi->xvi);
/* read-only, or virtual ? */
if (!ccmVisualIsWritable (visual_class) || scmi->private_cmap)
{
n_cells = 1 << scmi->xvi.depth;
n_got = min (n_max, n_cells);
if (scmi->palette_size != n_got)
{
/* for simplicity, break the range of all pixel values into
* n_got bins, and randomly select a pixel value from within
* each */
for (i = 0; i < n_got; i++)
scmi->palette[i].pixel = (Pixel)
((i + (rand()/(float)RAND_MAX)) * (n_cells / (float)n_got));
XQueryColors (scmi->display, scmi->cmap, scmi->palette, n_got);
scmi->palette_size = n_got;
}
if (buf) memcpy (buf, scmi->palette, n_got * sizeof(XColor));
}
/* writable ?
*
* get only sharable and non-writable pixel values. This is
* a little tricky, since this information is not readily
* available from the server. We'll first allocate all free
* writable cells. Next, run through all the cells in the
* colormap. For each of these, if it is not writable (if
* we can't find it in the list of writable color cells),
* and isn't already in use, then try to allocate a color cell
* with the same RGB values from the colormap. If the cell
* returned is the same as the reference cell, then we know
* it's sharable, so put it in the palette, otherwise move on.
*
* The specifics vary depending on visual class.
*/
else
{
#if 1
/* first grab as many writable cells as possible. Need to allocate
* memory for array of cells then call XAllocColorCells repeatedly
* until the max number of available cells is found. Kind of clunky! */
n_cells = scmi->xvi.colormap_size;
cells = (Pixel *)malloc (n_cells * sizeof(Pixel));
if (!cells) {
fprintf (stderr, "cColorManager_build_palette: out of memory!\n");
return 0;
}
n_writable = cColorManager_grab_writables
((cColorManager)scmi, cells, n_cells);
/* now build an assortment of non-writable pixels referenced by
* xcolors --particulars depend upon visual type */
if (visual_class == DirectColor)
{
fprintf
(stderr, "Sorry, DirectColor palette is not yet fully implemented.\n");
return 0;
}
else /* most likely PseudoColor */
{
/* sort the resulting pixels in ascending order */
if (n_writable > 0)
qsort (cells, n_writable, sizeof (Pixel), compare_pixels);
/* build array of non-writable pixels */
n_xcolors = n_cells - n_writable;
xcolors = (XColor *)malloc (n_xcolors * sizeof (XColor));
if (!xcolors) {
fprintf (stderr, "cColorManager_build_palette: out of memory!\n");
free (cells);
return 0;
}
/* i indexes into sorted array of writable cells
* j counts number of entries in xcolors array being built
* pix ranges from smaller to larger pixel values for visual
*/
pix = 0; i = 0; j = 0;
while ((j < n_xcolors) && ((int)pix < n_cells))
{
/* since pix and cells[i] increase in the same direction */
while (i < n_writable)
if (cells[i] < pix)
i++;
else break;
if (i < n_writable)
{
if (pix != cells[i])
xcolors[j++].pixel = pix;
}
else xcolors[j++].pixel = pix;
pix++;
}
}
/* just to be sure */
n_xcolors = j;
/* get rgb values for referenced pixels */
XQueryColors (scmi->display, scmi->cmap, xcolors, n_xcolors);
/* now, for each unwritable cell, attempt to allocate a pixel
* with the same rgb values. If the returned pixel references
* the current cell, then it's sharable, so move on to the next;
* otherwise (not sharable), swap it with the last unchecked
* pixel. In this way, all sharable colors will accumulate at
* the front of the array, and all unsharable pixels at the end,
* and i will end up holding the number of sharable cells.
*/
/* i is index of first uninvestigated color, j is index of last */
i = 0; j = n_xcolors - 1;
while (i <= j)
{
/* color cell under consideration */
xcolor = xcolors[i];
stat = 0;
/* if the color cell is already in use by this application,
* and is not writable, then it's good --no need for further
* investigation. This search can get potentially time-consuming,
* since it's executed for each color cell under investigation.
* There's probably some better way, but this should suffice
* for the time-being. */
for (k = 0; k < scmi->n_keep; k++)
if (xcolor.pixel == scmi->keep[k].pixel)
{
stat = !scmi->keep[k].writable;
break;
}
if (!stat && (k == scmi->n_keep))
if ((stat = XwAllocColor (scmi->display, scmi->cmap, &xcolor)))
if (!(stat = (xcolor.pixel == xcolors[i].pixel)))
/* we allocated a color defined by the RGB values in
* the current color cell, but got a different color cell!
* Fortunately, the X server maintains a reference count
* for all allocated cells (see X Protocol reference for
* XFreeColors), so we can go ahead and free this one without
* having to worry abount invalidating the other references
* to the same cell */
XwFreeColors (scmi->display, scmi->cmap, &xcolor.pixel, 1, 0);
if (!stat) /* swap ith item for jth item, decrement j */
{
xcolor = xcolors[i];
xcolors[i] = xcolors[j];
xcolors[j] = xcolor;
j--;
}
else i++; /* move to next item */
}
/* if there are more sharable colormap entries than requested,
* we need to get rid of some. For the time being, just grab
* the first however many */
n_got = min (i, n_max);
memcpy (scmi->palette, xcolors, n_got * sizeof(XColor));
if (buf) memcpy (buf, scmi->palette, n_got * sizeof(XColor));
scmi->palette_size = n_got;
/* clean up --free any allocated writable cells, any unused
* shared colors and temporarily allocated memory */
if (n_writable > 0)
cColorManager_free_writables ((cColorManager)scmi, cells, n_writable);
if (n_got < i)
{
i -= n_got;
/* must free i cells starting from the one indexed by n_got in the
* xcolors array. Might as well use the cells array to free multiple
* pixels at once, rather than just one at a time. */
k = n_got;
do
{
for (j = 0; (j < i) && (j < n_cells); j++)
cells[j] = xcolors[k+j].pixel;
XwFreeColors (scmi->display, scmi->cmap, cells, j, 0);
k += j;
}
while (k < i);
}
free (xcolors);
free (cells);
#else /* ======================================================*/
if (visual_class == DirectColor)
{
fprintf (stderr, "Shit out of luck.\n");
return 0;
}
else /* most likely PseudoColor */
{
n_cells = scmi->xvi.colormap_size;
cells = (Pixel *)malloc (n_cells * sizeof(Pixel));
if (!cells) {
fprintf (stderr, "cColorManager_build_palette: out of memory!\n");
return 0;
}
xcolors = (XColor *)malloc (n_cells * sizeof (XColor));
if (!xcolors) {
fprintf (stderr, "cColorManager_build_palette: out of memory!\n");
free (cells);
return 0;
}
}
/* allocate as many writable cells as possible. Linear search now
* --replace with binary search later */
for (i = n_cells; i > 0; i--)
if (XwAllocColorCells (scmi->display, scmi->cmap, False, 0, 0, cells, i))
break;
/* sort the resulting pixels in ascending order */
if ((n_writable = i) > 0)
qsort (cells, n_writable, sizeof (Pixel), compare_pixels);
/* now, query the rgb values for all unwritable pixels.
* i gets reset to 0, and indexes into the array of
* writable color cells, for comparing against pixel
* values to be investigated for sharability */
for (pix = 0, i = 0, j = 0; pix < n_cells; pix++)
{
if (i < n_writable)
if (pix == cells[i])
{
i++;
continue;
}
xcolors[j++].pixel = pix;
}
n_cells -= n_writable; /* should be equivalent to n_cells = j */
XQueryColors (scmi->display, scmi->cmap, xcolors, n_cells);
/* now, for each unwritable cell, attempt to allocate a pixel
* with the same rgb values. If the returned pixel references
* the current cell, then it's sharable, so move on to the next;
* otherwise (not sharable), swap it with the last unchecked
* pixel. In this way, all sharable colors will accumulate at
* the front of the array, and all unsharable pixels at the end,
* and i will end up holding the number of sharable cells.
*/
/* i is index of first uninvestigated color, j is index of last */
i = 0; j = n_cells - 1;
while (i <= j)
{
/* color cell under consideration */
xcolor = xcolors[i];
stat = 0;
/* if the color cell is already in use by this application,
* and is not writable, then it's good --no need for further
* investigation. This search can get potentially time-consuming,
* since it's executed for each color cell under investigation.
* There's probably some better way, but this should suffice
* for the time-being. */
for (k = 0; k < scmi->n_keep; k++)
if (xcolor.pixel == scmi->keep[k].pixel)
{
stat = !scmi->keep[k].writable;
break;
}
if (!stat && (k == scmi->n_keep))
if (stat = XwAllocColor (scmi->display, scmi->cmap, &xcolor))
if (!(stat = (xcolor.pixel == xcolors[i].pixel)))
/* we allocated a color defined by the RGB values in
* the current color cell, but got a different color cell!
* Fortunately, the X server maintains a reference count
* for all allocated cells (see X Protocol reference for
* XFreeColors), so we can go ahead and free this one without
* having to worry abount invalidating the other references