diff options
Diffstat (limited to 'src/libs/dsurf/dsconfig.cpp')
-rw-r--r-- | src/libs/dsurf/dsconfig.cpp | 767 |
1 files changed, 767 insertions, 0 deletions
diff --git a/src/libs/dsurf/dsconfig.cpp b/src/libs/dsurf/dsconfig.cpp new file mode 100644 index 0000000..81fbaba --- /dev/null +++ b/src/libs/dsurf/dsconfig.cpp @@ -0,0 +1,767 @@ +// BEGIN_COPYRIGHT +// +// Copyright (C) 1999 Allen Akin All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the +// Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +// KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ALLEN AKIN BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +// OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// END_COPYRIGHT + + +// dsconfig.cpp: Implementation of drawing surface configuration utilities +#include "dsconfig.h" +#include <iostream> +#include <strstream> +#include <string.h> +#include <map> +#include <limits.h> + +#ifdef __WIN__ +// disable the annoying warning : "forcing value to bool 'true' or 'false' (performance warning)" +#pragma warning (disable : 4800) +#endif + + +#include "lex.h" + + + +namespace { + +#ifdef __X11__ + +bool +haveGLXExtension(::Display* dpy, const char* extName) { + const char* extString = + glXQueryExtensionsString(dpy, DefaultScreen(dpy)); + // We don't cache the result, so that subsequent calls + // with different values of ``dpy'' will work correctly. + // Would be nice to improve this, though. + + const char* start = extString; + for (;;) { + const char* where = strstr(start, extName); + if (!where) + return false; + + // Make sure we're not fooled by extensions whose names + // have the desired extName as an initial substring: + const char* terminator = where + strlen(extName); + if ((where == start || where[-1] == ' ') + && (*terminator == ' ' || *terminator == 0)) + return true; + + start = terminator; + } + + return false; +} // haveGLXExtension + +#endif + +typedef enum { // These variable tags are used as array indices, + // so they should represent a small dense set of + // nonnegative integers. 0 is reserved. + VID = 1, + VFBCID, + VCANRGBA, + VR, + VG, + VB, + VA, + VCANCI, + VBUFSIZE, + VLEVEL, + VDB, + VSTEREO, + VAUX, + VZ, + VS, + VACCUMR, + VACCUMG, + VACCUMB, + VACCUMA, + VCANWINDOW, + VCANPIXMAP, + VCANPBUFFER, + VMAXPBUFFERWIDTH, + VMAXPBUFFERHEIGHT, + VMAXPBUFFERPIXELS, + VCANWINSYSRENDER, + VFAST, + VCONFORMANT, + VTRANSPARENT, + VTRANSR, + VTRANSG, + VTRANSB, + VTRANSA, + VTRANSI, + V_LAST +} CanonVar; + +struct {CanonVar var; char* name;} varNames[] = { + {VID, "id"}, + {VFBCID, "fbcID"}, + {VCANRGBA, "canRGBA"}, + {VR, "r"}, + {VG, "g"}, + {VB, "b"}, + {VA, "a"}, + {VCANCI, "canCI"}, + {VBUFSIZE, "bufSize"}, + {VLEVEL, "level"}, + {VDB, "db"}, + {VSTEREO, "stereo"}, + {VAUX, "aux"}, + {VZ, "z"}, + {VS, "s"}, + {VACCUMR, "accumR"}, + {VACCUMG, "accumG"}, + {VACCUMB, "accumB"}, + {VACCUMA, "accumA"}, + {VCANWINDOW, "window"}, + {VCANPIXMAP, "pixmap"}, + {VCANPBUFFER, "pBuffer"}, + {VMAXPBUFFERWIDTH, "maxPBufferWidth"}, + {VMAXPBUFFERHEIGHT, "maxPBufferHeight"}, + {VMAXPBUFFERPIXELS, "maxPBufferPixels"}, + {VCANWINSYSRENDER, "winsys"}, + {VFAST, "fast"}, + {VCONFORMANT, "conformant"}, + {VTRANSPARENT, "transparent"}, + {VTRANSR, "transR"}, + {VTRANSG, "transG"}, + {VTRANSB, "transB"}, + {VTRANSA, "transA"}, + {VTRANSI, "transI"} +}; + +char* mapVarToName[V_LAST]; +map<string, CanonVar> mapNameToVar; +bool mapsInitialized = false; + +void +initializeMaps() { + for (unsigned i = 0; i < sizeof(varNames)/sizeof(varNames[0]); ++i) { + mapVarToName[varNames[i].var] = varNames[i].name; + mapNameToVar[varNames[i].name] = varNames[i].var; + } + mapsInitialized = true; +} // initializeMaps + +template<class T> inline T abs(T a) {return (a < 0)? -a: a;} + +} // anonymous namespace + + +namespace GLEAN { + + +#if defined(__X11__) + +DrawingSurfaceConfig::DrawingSurfaceConfig(::Display* dpy, ::XVisualInfo* pvi) { + if (!mapsInitialized) + initializeMaps(); + + int var; + + vi = pvi; + visID = vi->visualid; + + glXGetConfig(dpy, vi, GLX_RGBA, &var); + canRGBA = var; + canCI = !var; + // There is no dual-personality Visual support in early + // versions of GLX. + + glXGetConfig(dpy, vi, GLX_BUFFER_SIZE, &bufSize); + + glXGetConfig(dpy, vi, GLX_LEVEL, &level); + + glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &var); + db = var; + + glXGetConfig(dpy, vi, GLX_STEREO, &var); + stereo = var; + + glXGetConfig(dpy, vi, GLX_AUX_BUFFERS, &aux); + + if (canRGBA) { + glXGetConfig(dpy, vi, GLX_RED_SIZE, &r); + glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &g); + glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &b); + glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &a); + } else + r = g = b = a = 0; + + glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &z); + + glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &s); + + if (canRGBA) { + glXGetConfig(dpy, vi, GLX_ACCUM_RED_SIZE, &accR); + glXGetConfig(dpy, vi, GLX_ACCUM_GREEN_SIZE, &accG); + glXGetConfig(dpy, vi, GLX_ACCUM_BLUE_SIZE, &accB); + glXGetConfig(dpy, vi, GLX_ACCUM_ALPHA_SIZE, &accA); + } else + accR = accG = accB = accA = 0; + + canWindow = canPixmap = true; + // Only guaranteed in early versions of GLX. + + canWinSysRender = true; + // Only guaranteed in early versions of GLX. + + fast = true; + conformant = true; +# if defined(GLX_EXT_visual_rating) + if (haveGLXExtension(dpy, "EXT_visual_rating")) { + glXGetConfig(dpy, vi, GLX_VISUAL_CAVEAT_EXT, &var); + if (var == GLX_SLOW_VISUAL_EXT) + fast = false; + else if (var == GLX_NON_CONFORMANT_VISUAL_EXT) + conformant = false; + } +# endif + + transparent = false; + transR = transG = transB = transA = transI = 0; +# if defined(GLX_EXT_visual_info) + if (haveGLXExtension(dpy, "EXT_visual_info")) { + glXGetConfig(dpy, vi, GLX_TRANSPARENT_TYPE_EXT, &var); + if (var == GLX_TRANSPARENT_RGB_EXT) { + glXGetConfig(dpy, vi, + GLX_TRANSPARENT_RED_VALUE_EXT, &transR); + glXGetConfig(dpy, vi, + GLX_TRANSPARENT_GREEN_VALUE_EXT, &transG); + glXGetConfig(dpy, vi, + GLX_TRANSPARENT_BLUE_VALUE_EXT, &transB); + glXGetConfig(dpy, vi, + GLX_TRANSPARENT_ALPHA_VALUE_EXT, &transA); + } else + glXGetConfig(dpy, vi, + GLX_TRANSPARENT_INDEX_VALUE_EXT, &transI); + } +# endif +} // DrawingSurfaceConfig::DrawingSurfaceConfig + +#if defined(GLX_VERSION_1_3) +DrawingSurfaceConfig::DrawingSurfaceConfig(::Display* dpy, ::GLXFBConfig* pfbc) +{ + if (!mapsInitialized) + initializeMaps(); +} // DrawingSurfaceConfig::DrawingSurfaceConfig +#error "XXX Need to write drawing surface config code for GLX 1.3" +#endif + +#elif defined(__WIN__) + +DrawingSurfaceConfig::DrawingSurfaceConfig(int id, ::PIXELFORMATDESCRIPTOR *ppfd) +{ + if (!mapsInitialized) + initializeMaps(); + + pfd = ppfd; + pfdID = id; + + canRGBA = pfd->iPixelType == PFD_TYPE_RGBA; + canCI = pfd->iPixelType == PFD_TYPE_COLORINDEX; + + bufSize = pfd->cColorBits + pfd->cAlphaBits; + + level = 0; + + db = pfd->dwFlags & PFD_DOUBLEBUFFER; + + stereo = pfd->dwFlags & PFD_STEREO; + + aux = pfd->cAuxBuffers; + + if (canRGBA) { + r = pfd->cRedBits; + g = pfd->cGreenBits; + b = pfd->cBlueBits; + a = pfd->cAlphaBits; + } + else + r = g = b = a = 0; + + z = pfd->cDepthBits; + s = pfd->cStencilBits; + + accR = pfd->cAccumRedBits; + accG = pfd->cAccumGreenBits; + accB = pfd->cAccumBlueBits; + accA = pfd->cAccumAlphaBits; + + canWindow = pfd->dwFlags & PFD_DRAW_TO_WINDOW; + + canWinSysRender = pfd->dwFlags & PFD_SUPPORT_GDI; + + if (pfd->dwFlags & PFD_GENERIC_FORMAT) + { + if (pfd->dwFlags & PFD_GENERIC_ACCELERATED) + { + // it's an MCD - at least it has some acceleration + fast = true; + } + else + { + // it's software + fast = false; + } + } + else + { + // it's an ICD + fast = true; + } + + // we'll assume that the OpenGL implementation thinks it is conformant + conformant = true; + + // chromakeying isn't supported + transparent = false; + transR = transG = transB = transA = transI = 0; +} + +#endif + + +DrawingSurfaceConfig::DrawingSurfaceConfig(string& str) { + if (!mapsInitialized) + initializeMaps(); + + try { + Lex lex(str.c_str()); + + for (lex.next(); lex.token != Lex::END; lex.next()) { + if (lex.token != Lex::ID) + throw Syntax("expected variable name", + lex.position()); + lex.next(); + if (lex.token != Lex::ICONST) + throw Syntax("expected integer value", + lex.position()); + + // Yes, this is an unpleasantly verbose way to + // handle this problem. However, it will be + // necessary when we have to deal with attributes + // that aren't all of a simple integral type. + + switch (mapNameToVar[lex.id]) { + case VID: +# if defined(__X11__) + visID = lex.iValue; +# endif + break; + case VFBCID: +# if defined(GLX_VERSION_1_3) + fbcID = lex.iValue; +# endif + break; + case VCANRGBA: + canRGBA = lex.iValue; + break; + case VR: + r = lex.iValue; + break; + case VG: + g = lex.iValue; + break; + case VB: + b = lex.iValue; + break; + case VA: + a = lex.iValue; + break; + case VCANCI: + canCI = lex.iValue; + break; + case VBUFSIZE: + bufSize = lex.iValue; + break; + case VLEVEL: + level = lex.iValue; + break; + case VDB: + db = lex.iValue; + break; + case VSTEREO: + stereo = lex.iValue; + break; + case VAUX: + aux = lex.iValue; + break; + case VZ: + z = lex.iValue; + break; + case VS: + s = lex.iValue; + break; + case VACCUMR: + accR = lex.iValue; + break; + case VACCUMG: + accG = lex.iValue; + break; + case VACCUMB: + accB = lex.iValue; + break; + case VACCUMA: + accA = lex.iValue; + break; + case VCANWINDOW: + canWindow = lex.iValue; + break; + case VCANPIXMAP: +# if defined(__X11__) + canPixmap = lex.iValue; +# endif + break; + case VCANPBUFFER: +# if defined(GLX_VERSION_1_3) + canPBuffer = lex.iValue; +# endif + break; + case VMAXPBUFFERWIDTH: +# if defined(GLX_VERSION_1_3) + maxPBufferWidth = lex.iValue; +# endif + break; + case VMAXPBUFFERHEIGHT: +# if defined(GLX_VERSION_1_3) + maxPBufferHeight = lex.iValue; +# endif + break; + case VMAXPBUFFERPIXELS: +# if defined(GLX_VERSION_1_3) + maxPBufferPixels = lex.iValue; +# endif + break; + case VCANWINSYSRENDER: + canWinSysRender = lex.iValue; + break; + case VFAST: + fast = lex.iValue; + break; + case VCONFORMANT: + conformant = lex.iValue; + break; + case VTRANSPARENT: + transparent = lex.iValue; + break; + case VTRANSR: + transR = lex.iValue; + break; + case VTRANSG: + transG = lex.iValue; + break; + case VTRANSB: + transB = lex.iValue; + break; + case VTRANSA: + transA = lex.iValue; + break; + case VTRANSI: + transI = lex.iValue; + break; + default: + throw Syntax("unrecognized variable", + lex.position()); + } + } + } + catch (Lex::Lexical e) { + throw Syntax(e.err, e.position); + } +} // DrawingSurfaceConfing::DrawingSurfaceConfig + + +/////////////////////////////////////////////////////////////////////////////// +// canonicalDescription - return a description string that can be used +// to reconstruct the essential attributes of a drawing surface +// configuration. Note that visual ID numbers are included for +// completeness, but they must be ignored when attempting to compare +// two surface configurations; there's no guarantee that they'll be +// valid (or even relevant, since they may have been created on another +// OS). +// +// This is ugly code, but it keeps the names used here and in +// the string-based constructor for DrawingSurfaceConfig in sync +// automatically. +/////////////////////////////////////////////////////////////////////////////// +string +DrawingSurfaceConfig::canonicalDescription() { + + // Would rather use ostringstream, but it's not available in + // egcs 1.1.2. + char buf[1024]; + ostrstream s(buf, sizeof(buf)); + +# if defined(__X11__) + s << mapVarToName[VID] << ' ' << visID; +# if defined(GLX_VERSION_1_3) + s << ' ' << mapVarToName[VFBCID] << ' ' << fbcID; +# endif +# elif defined(__WIN__) + s << mapVarToName[VID] << ' ' << pfdID; +# endif + + s << ' ' << mapVarToName[VCANRGBA] << ' ' << canRGBA; + s << ' ' << mapVarToName[VR] << ' ' << r + << ' ' << mapVarToName[VG] << ' ' << g + << ' ' << mapVarToName[VB] << ' ' << b + << ' ' << mapVarToName[VA] << ' ' << a; + + s << ' ' << mapVarToName[VCANCI] << ' ' << canCI; + + s << ' ' << mapVarToName[VBUFSIZE] << ' ' << bufSize; + + s << ' ' << mapVarToName[VLEVEL] << ' ' << level; + + s << ' ' << mapVarToName[VDB] << ' ' << db; + + s << ' ' << mapVarToName[VSTEREO] << ' '<< stereo; + + s << ' ' << mapVarToName[VAUX] << ' ' << aux; + + s << ' ' << mapVarToName[VZ] << ' ' << z; + + s << ' ' << mapVarToName[VS] << ' ' << DrawingSurfaceConfig::s; + + s << ' ' << mapVarToName[VACCUMR] << ' ' << accR + << ' ' << mapVarToName[VACCUMG] << ' ' << accG + << ' ' << mapVarToName[VACCUMB] << ' ' << accB + << ' ' << mapVarToName[VACCUMA] << ' ' << accA; + + s << ' ' << mapVarToName[VCANWINDOW] << ' ' << canWindow; + +# if defined(__X11__) + s << ' ' << mapVarToName[VCANPIXMAP] << ' ' << canPixmap; + +# if defined(GLX_VERSION_1_3) + s << ' ' << mapVarToName[VCANPBUFFER] << ' ' << canPBuffer; + s << ' ' << mapVarToName[VMAXPBUFFERWIDTH] << ' ' << maxPBufferWidth; + s << ' ' << mapVarToName[VMAXPBUFFERHEIGHT] << ' ' << maxPBufferHeight; + s << ' ' << mapVarToName[VMAXPBUFFERPIXELS] << ' ' << maxPBufferPixels; +# endif + +# endif + + s << ' ' << mapVarToName[VCANWINSYSRENDER] << ' ' << canWinSysRender; + + s << ' ' << mapVarToName[VFAST] << ' ' << fast; + + s << ' ' << mapVarToName[VCONFORMANT] << ' ' << conformant; + + s << ' ' << mapVarToName[VTRANSPARENT] << ' ' << transparent; + s << ' ' << mapVarToName[VTRANSR] << ' ' << transR + << ' ' << mapVarToName[VTRANSG] << ' ' << transG + << ' ' << mapVarToName[VTRANSB] << ' ' << transB + << ' ' << mapVarToName[VTRANSA] << ' ' << transA + << ' ' << mapVarToName[VTRANSI] << ' ' << transI; + + s << '\0'; + return s.str(); +} // DrawingSurfaceConfig::canonicalDescription + +/////////////////////////////////////////////////////////////////////////////// +// conciseDescription - return a description string that's appropriate for +// reading by humans, rather than parsing by machine. +/////////////////////////////////////////////////////////////////////////////// +string +DrawingSurfaceConfig::conciseDescription() { + char buf[1024]; + ostrstream s(buf, sizeof(buf)); + + if (canRGBA && canCI) + s << "dual "; + + if (canRGBA) { + if (a) { + if (r == g && g == b && b == a) + s << "rgba" << r; + else + s << "r" << r << "g" << g << "b" << b + << "a" << a; + } else { + if (r == g && g == b) + s << "rgb" << r; + else + s << "r" << r << "g" << g << "b" << b; + } + } + + if (canCI) { + if (canRGBA) s << "+"; + s << "ci" << bufSize; + } + + if (level < 0) + s << ", underlay"; + else if (level > 0) + s << ", overlay"; + + if (db) + s << ", db"; + + if (stereo) + s << ", stereo"; + + if (aux) + s << ", aux" << aux; + + if (z) + s << ", z" << z; + + if (DrawingSurfaceConfig::s) + s << ", s" << DrawingSurfaceConfig::s; + + if (accR) { + if (accA) { + if (accR == accG && accG == accB + && accB == accA) + s << ", accrgba" << accR; + else + s << ", accr" << accR << "g" << accG + << "b" << accB << "a" << accA; + } else { + if (accR == accG && accG == accB) + s << ", accrgb" << accR; + else + s << ", accr" << accR << "g" << accG + << "b" << accB; + } + } + + { + s << ", "; + bool sep = false; + if (canWindow) { + s << "win"; + sep = true; + } +# if defined(__X11__) + if (canPixmap) { + if (sep) + s << "+"; + s << "pmap"; + sep = true; + } +# endif +# if defined(GLX_VERSION_1_3) + if (canPBuffer) { + if (sep) + s << "+"; + s << "pbuf"; + } +# endif + } + + if (!fast) + s << ", slow"; + + if (!conformant) + s << ", nonconformant"; + + if (transparent) { + if (canRGBA) { + s << ", transrgba (" << transR << "," << transG + << "," << transB << "," << transA << ")"; + } + if (canCI) { + s << ", transci (" << transI << ")"; + } + } + +# if defined(__X11__) +# if defined(GLX_VERSION_1_3) + s << ", id " << fbcID; +# else + s << ", id " << visID; +# endif +# elif defined(__WIN__) + s << ", id " << pfdID; +# endif + + s << '\0'; + return s.str(); +} // DrawingSurfaceConfig::conciseDescription + +/////////////////////////////////////////////////////////////////////////////// +// match - select a config that ``matches'' the current config. +// To keep this problem manageable, we'll assume that both the config +// to be matched (call it the ``A'' config) and the vector of configs to +// choose from (call them the ``B'' configs) were selected by a test +// using a single filter. Thus we can ignore any differences in buffer +// availability (because we know those are irrelevant to the test), and +// concentrate on picking configs for which the available buffers are +// (in some sense) closest in size. +// +// This will not be an acceptable solution in all cases, but it should +// suffice for many. +/////////////////////////////////////////////////////////////////////////////// +int +DrawingSurfaceConfig::match(vector<DrawingSurfaceConfig*>& choices) { + typedef vector<DrawingSurfaceConfig*>::iterator cptr; + + int best = -1; + int bestError = INT_MAX; + + for (cptr p = choices.begin(); p < choices.end(); ++p) { + DrawingSurfaceConfig& c = **p; + int error = 0; + + if (bufSize && c.bufSize) + error += abs(bufSize - c.bufSize); + if (r && c.r) + error += abs(r - c.r); + if (g && c.g) + error += abs(g - c.g); + if (b && c.b) + error += abs(b - c.b); + if (a && c.a) + error += abs(a - c.a); + if (z && c.z) + error += abs(z - c.z); + if (s && c.s) + error += abs(s - c.s); + if (accR && c.accR) + error += abs(accR - c.accR); + if (accG && c.accG) + error += abs(accG - c.accG); + if (accB && c.accB) + error += abs(accB - c.accB); + if (accA && c.accA) + error += abs(accA - c.accA); + + if (error < bestError) { + bestError = error; + best = static_cast<int>(p - choices.begin()); + } + } + + return best; +} // DrawingSurfaceConfig::match + +} // namespace GLEAN |