#include "vncviewer.h"
#include <limits.h>
#define INVALID_PIXEL 0xffffffff
#define MAX_CMAP_SIZE 256
#define BGR233_SIZE 256
unsigned long BGR233ToPixel[BGR233_SIZE];
Colormap cmap;
Visual *vis;
unsigned int visdepth, visbpp;
Bool allocColorFailed = False;
static int nBGR233ColoursAllocated;
static Bool GetPseudoColorVisualAndCmap(int depth);
static Bool GetTrueColorVisualAndCmap(int depth);
static int GetBPPForDepth(int depth);
static void SetupBGR233Map();
static void AllocateExactBGR233Colours();
static Bool AllocateBGR233Colour(int r, int g, int b);
void
SetVisualAndCmap()
{
if (appData.forceOwnCmap) {
if (!si.format.trueColour) {
if (GetPseudoColorVisualAndCmap(si.format.depth))
return;
}
if (GetPseudoColorVisualAndCmap(8))
return;
fprintf(stderr,"Couldn't find a matching PseudoColor visual.\n");
}
if (appData.forceTrueColour) {
if (GetTrueColorVisualAndCmap(appData.requestedDepth))
return;
fprintf(stderr,"Couldn't find a matching TrueColor visual.\n");
}
vis = DefaultVisual(dpy,DefaultScreen(dpy));
visdepth = DefaultDepth(dpy,DefaultScreen(dpy));
visbpp = GetBPPForDepth(visdepth);
cmap = DefaultColormap(dpy,DefaultScreen(dpy));
if (!appData.useBGR233 && (vis->class == TrueColor)) {
myFormat.bitsPerPixel = visbpp;
myFormat.depth = visdepth;
myFormat.trueColour = 1;
myFormat.bigEndian = (ImageByteOrder(dpy) == MSBFirst);
myFormat.redShift = ffs(vis->red_mask) - 1;
myFormat.greenShift = ffs(vis->green_mask) - 1;
myFormat.blueShift = ffs(vis->blue_mask) - 1;
myFormat.redMax = vis->red_mask >> myFormat.redShift;
myFormat.greenMax = vis->green_mask >> myFormat.greenShift;
myFormat.blueMax = vis->blue_mask >> myFormat.blueShift;
if (!appData.ffInfo) {
fprintf(stderr,
"Using default colormap which is TrueColor. Pixel format:\n");
PrintPixelFormat(&myFormat);
}
return;
}
appData.useBGR233 = True;
myFormat.bitsPerPixel = 8;
myFormat.depth = 8;
myFormat.trueColour = 1;
myFormat.bigEndian = 0;
myFormat.redMax = 7;
myFormat.greenMax = 7;
myFormat.blueMax = 3;
myFormat.redShift = 0;
myFormat.greenShift = 3;
myFormat.blueShift = 6;
if (!appData.ffInfo) {
fprintf(stderr,
"Using default colormap and translating from BGR233. Pixel format:\n");
PrintPixelFormat(&myFormat);
}
SetupBGR233Map();
}
static Bool
GetPseudoColorVisualAndCmap(int depth)
{
XVisualInfo tmpl;
XVisualInfo *vinfo;
int nvis;
tmpl.screen = DefaultScreen(dpy);
tmpl.depth = depth;
tmpl.class = PseudoColor;
tmpl.colormap_size = (1 << depth);
vinfo = XGetVisualInfo(dpy,
VisualScreenMask|VisualDepthMask|
VisualClassMask|VisualColormapSizeMask,
&tmpl, &nvis);
if (vinfo) {
vis = vinfo[0].visual;
visdepth = vinfo[0].depth;
XFree(vinfo);
visbpp = GetBPPForDepth(visdepth);
myFormat.bitsPerPixel = visbpp;
myFormat.depth = visdepth;
myFormat.trueColour = 0;
myFormat.bigEndian = (ImageByteOrder(dpy) == MSBFirst);
myFormat.redMax = myFormat.greenMax = myFormat.blueMax = 0;
myFormat.redShift = myFormat.greenShift = myFormat.blueShift = 0;
cmap = XCreateColormap(dpy, DefaultRootWindow(dpy), vis, AllocAll);
XtVaSetValues(toplevel, XtNcolormap, cmap, XtNdepth, visdepth,
XtNvisual, vis, NULL);
if (appData.fullScreen) {
XInstallColormap(dpy, cmap);
}
fprintf(stderr,"Using PseudoColor visual, depth %d. Pixel format:\n",
visdepth);
PrintPixelFormat(&myFormat);
return True;
}
return False;
}
static Bool
GetTrueColorVisualAndCmap(int depth)
{
XVisualInfo tmpl;
XVisualInfo *vinfo;
int nvis;
int mask = VisualScreenMask|VisualClassMask;
tmpl.screen = DefaultScreen(dpy);
tmpl.class = TrueColor;
if (depth != 0) {
tmpl.depth = depth;
mask |= VisualDepthMask;
}
vinfo = XGetVisualInfo(dpy, mask, &tmpl, &nvis);
if (vinfo) {
vis = vinfo[0].visual;
visdepth = vinfo[0].depth;
XFree(vinfo);
visbpp = GetBPPForDepth(visdepth);
myFormat.bitsPerPixel = visbpp;
myFormat.depth = visdepth;
myFormat.trueColour = 1;
myFormat.bigEndian = (ImageByteOrder(dpy) == MSBFirst);
myFormat.redShift = ffs(vis->red_mask) - 1;
myFormat.greenShift = ffs(vis->green_mask) - 1;
myFormat.blueShift = ffs(vis->blue_mask) - 1;
myFormat.redMax = vis->red_mask >> myFormat.redShift;
myFormat.greenMax = vis->green_mask >> myFormat.greenShift;
myFormat.blueMax = vis->blue_mask >> myFormat.blueShift;
cmap = XCreateColormap(dpy, DefaultRootWindow(dpy), vis, AllocNone);
XtVaSetValues(toplevel, XtNcolormap, cmap, XtNdepth, visdepth,
XtNvisual, vis, NULL);
if (appData.fullScreen) {
XInstallColormap(dpy, cmap);
}
fprintf(stderr,"Using TrueColor visual, depth %d. Pixel format:\n",
visdepth);
PrintPixelFormat(&myFormat);
return True;
}
return False;
}
static int
GetBPPForDepth(int depth)
{
XPixmapFormatValues *format;
int nformats;
int i;
int bpp;
format = XListPixmapFormats(dpy, &nformats);
for (i = 0; i < nformats; i++) {
if (format[i].depth == depth)
break;
}
if (i == nformats) {
fprintf(stderr,"no pixmap format for depth %d???\n", depth);
exit(1);
}
bpp = format[i].bits_per_pixel;
XFree(format);
if (bpp != 1 && bpp != 8 && bpp != 16 && bpp != 32) {
fprintf(stderr,"Can't cope with %d bits-per-pixel. Sorry.\n", bpp);
exit(1);
}
return bpp;
}
static void
SetupBGR233Map()
{
int r, g, b;
long i;
unsigned long nearestPixel = 0;
int cmapSize;
XColor cmapEntry[MAX_CMAP_SIZE];
Bool exactBGR233[MAX_CMAP_SIZE];
Bool shared[MAX_CMAP_SIZE];
Bool usedAsNearest[MAX_CMAP_SIZE];
int nSharedUsed = 0;
if (visdepth > 8) {
appData.nColours = 256;
}
for (i = 0; i < BGR233_SIZE; i++) {
BGR233ToPixel[i] = INVALID_PIXEL;
}
AllocateExactBGR233Colours();
fprintf(stderr,"Got %d exact BGR233 colours out of %d\n",
nBGR233ColoursAllocated, appData.nColours);
if (nBGR233ColoursAllocated < BGR233_SIZE) {
if (visdepth > 8) {
fprintf(stderr,"Error: couldn't allocate BGR233 colours even though "
"depth is %d\n", visdepth);
exit(1);
}
cmapSize = (1 << visdepth);
for (i = 0; i < cmapSize; i++) {
cmapEntry[i].pixel = i;
exactBGR233[i] = False;
shared[i] = False;
usedAsNearest[i] = False;
}
XQueryColors(dpy, cmap, cmapEntry, cmapSize);
for (i = 0; i < BGR233_SIZE; i++) {
if (BGR233ToPixel[i] != INVALID_PIXEL)
exactBGR233[BGR233ToPixel[i]] = True;
}
if (appData.useSharedColours) {
for (i = cmapSize-1; i >= 0; i--) {
if (!exactBGR233[i] &&
XAllocColor(dpy, cmap, &cmapEntry[i])) {
if (cmapEntry[i].pixel == i) {
shared[i] = True;
} else {
XFreeColors(dpy, cmap, &cmapEntry[i].pixel, 1, 0);
}
}
}
}
for (r = 0; r < 8; r++) {
for (g = 0; g < 8; g++) {
for (b = 0; b < 4; b++) {
if (BGR233ToPixel[(b<<6) | (g<<3) | r] == INVALID_PIXEL) {
unsigned long minDistance = ULONG_MAX;
for (i = 0; i < cmapSize; i++) {
if (exactBGR233[i] || shared[i]) {
unsigned long distance
= (abs(cmapEntry[i].red - r * 65535 / 7)
+ abs(cmapEntry[i].green - g * 65535 / 7)
+ abs(cmapEntry[i].blue - b * 65535 / 3));
if (distance < minDistance) {
minDistance = distance;
nearestPixel = i;
}
}
}
BGR233ToPixel[(b<<6) | (g<<3) | r] = nearestPixel;
if (shared[nearestPixel] && !usedAsNearest[nearestPixel])
nSharedUsed++;
usedAsNearest[nearestPixel] = True;
}
}
}
}
for (i = 0; i < cmapSize; i++) {
if (shared[i] && !usedAsNearest[i]) {
XFreeColors(dpy, cmap, (unsigned long *)&i, 1, 0);
}
}
fprintf(stderr,"Using %d existing shared colours\n", nSharedUsed);
}
}
static void
AllocateExactBGR233Colours()
{
int rv[] = {0,7,3,5,1,6,2,4};
int gv[] = {0,7,3,5,1,6,2,4};
int bv[] = {0,3,1,2};
int rn = 0;
int gn = 1;
int bn = 1;
int ri, gi, bi;
nBGR233ColoursAllocated = 0;
while (1) {
if (rn == 8)
break;
ri = rn;
for (gi = 0; gi < gn; gi++) {
for (bi = 0; bi < bn; bi++) {
if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi]))
return;
}
}
rn++;
if (gn == 8)
break;
gi = gn;
for (ri = 0; ri < rn; ri++) {
for (bi = 0; bi < bn; bi++) {
if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi]))
return;
}
}
gn++;
if (bn < 4) {
bi = bn;
for (ri = 0; ri < rn; ri++) {
for (gi = 0; gi < gn; gi++) {
if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi]))
return;
}
}
bn++;
}
}
}
static Bool
AllocateBGR233Colour(int r, int g, int b)
{
XColor c;
if (nBGR233ColoursAllocated >= appData.nColours)
return False;
c.red = r * 65535 / 7;
c.green = g * 65535 / 7;
c.blue = b * 65535 / 3;
if (!XAllocColor(dpy, cmap, &c))
return False;
BGR233ToPixel[(b<<6) | (g<<3) | r] = c.pixel;
nBGR233ColoursAllocated++;
return True;
}