3 // Copyright (C) 1999 Allen Akin All Rights Reserved.
5 // multisample changes: Copyright (c) 2008 VMware, Inc. All rights reserved.
7 // Permission is hereby granted, free of charge, to any person
8 // obtaining a copy of this software and associated documentation
9 // files (the "Software"), to deal in the Software without
10 // restriction, including without limitation the rights to use,
11 // copy, modify, merge, publish, distribute, sublicense, and/or
12 // sell copies of the Software, and to permit persons to whom the
13 // Software is furnished to do so, subject to the following
16 // The above copyright notice and this permission notice shall be
17 // included in all copies or substantial portions of the
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
21 // KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
22 // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
23 // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ALLEN AKIN BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
25 // AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
26 // OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 // DEALINGS IN THE SOFTWARE.
32 // dsconfig.cpp: Implementation of drawing surface configuration utilities
41 // disable the annoying warning : "forcing value to bool 'true' or 'false' (performance warning)"
42 #pragma warning (disable : 4800)
55 haveGLXExtension(::Display
* dpy
, const char* extName
) {
56 const char* extString
=
57 glXQueryExtensionsString(dpy
, DefaultScreen(dpy
));
58 // We don't cache the result, so that subsequent calls
59 // with different values of ``dpy'' will work correctly.
60 // Would be nice to improve this, though.
62 const char* start
= extString
;
64 const char* where
= strstr(start
, extName
);
68 // Make sure we're not fooled by extensions whose names
69 // have the desired extName as an initial substring:
70 const char* terminator
= where
+ strlen(extName
);
71 if ((where
== start
|| where
[-1] == ' ')
72 && (*terminator
== ' ' || *terminator
== 0))
83 typedef enum { // These variable tags are used as array indices,
84 // so they should represent a small dense set of
85 // nonnegative integers. 0 is reserved.
124 static struct {CanonVar var
; const char* name
;} varNames
[] = {
127 {VCANRGBA
, "canRGBA"},
133 {VBUFSIZE
, "bufSize"},
144 {VSAMPLES
, "multisample"},
145 {VCANWINDOW
, "window"},
146 {VCANPIXMAP
, "pixmap"},
147 {VCANPBUFFER
, "pBuffer"},
148 {VMAXPBUFFERWIDTH
, "maxPBufferWidth"},
149 {VMAXPBUFFERHEIGHT
, "maxPBufferHeight"},
150 {VMAXPBUFFERPIXELS
, "maxPBufferPixels"},
151 {VCANWINSYSRENDER
, "winsys"},
153 {VCONFORMANT
, "conformant"},
154 {VTRANSPARENT
, "transparent"},
162 const char* mapVarToName
[V_LAST
];
163 map
<string
, CanonVar
> mapNameToVar
;
164 bool mapsInitialized
= false;
168 for (unsigned i
= 0; i
< sizeof(varNames
)/sizeof(varNames
[0]); ++i
) {
169 mapVarToName
[varNames
[i
].var
] = varNames
[i
].name
;
170 mapNameToVar
[varNames
[i
].name
] = varNames
[i
].var
;
172 mapsInitialized
= true;
175 template<class T
> inline T
abs(T a
) {return (a
< 0)? -a
: a
;}
177 } // anonymous namespace
182 // init all config fields to zero
184 DrawingSurfaceConfig::zeroFields()
188 # if defined(GLX_VERSION_1_3)
191 #elif defined(__WIN__)
193 #elif defined(__AGL__)
196 # error "what's the config ID?"
219 #if defined(GLX_VERSION_1_3)
222 maxPBufferHeight
= 0;
223 maxPBufferPixels
= 0;
240 DrawingSurfaceConfig::DrawingSurfaceConfig(::Display
* dpy
, ::XVisualInfo
* pvi
) {
241 if (!mapsInitialized
)
247 visID
= vi
->visualid
;
248 # if defined(GLX_VERSION_1_3)
252 glXGetConfig(dpy
, vi
, GLX_RGBA
, &var
);
255 // There is no dual-personality Visual support in early
258 glXGetConfig(dpy
, vi
, GLX_BUFFER_SIZE
, &bufSize
);
260 glXGetConfig(dpy
, vi
, GLX_LEVEL
, &level
);
262 glXGetConfig(dpy
, vi
, GLX_DOUBLEBUFFER
, &var
);
265 glXGetConfig(dpy
, vi
, GLX_STEREO
, &var
);
268 glXGetConfig(dpy
, vi
, GLX_AUX_BUFFERS
, &aux
);
271 glXGetConfig(dpy
, vi
, GLX_RED_SIZE
, &r
);
272 glXGetConfig(dpy
, vi
, GLX_GREEN_SIZE
, &g
);
273 glXGetConfig(dpy
, vi
, GLX_BLUE_SIZE
, &b
);
274 glXGetConfig(dpy
, vi
, GLX_ALPHA_SIZE
, &a
);
278 glXGetConfig(dpy
, vi
, GLX_DEPTH_SIZE
, &z
);
280 glXGetConfig(dpy
, vi
, GLX_STENCIL_SIZE
, &s
);
283 glXGetConfig(dpy
, vi
, GLX_ACCUM_RED_SIZE
, &accR
);
284 glXGetConfig(dpy
, vi
, GLX_ACCUM_GREEN_SIZE
, &accG
);
285 glXGetConfig(dpy
, vi
, GLX_ACCUM_BLUE_SIZE
, &accB
);
286 glXGetConfig(dpy
, vi
, GLX_ACCUM_ALPHA_SIZE
, &accA
);
288 accR
= accG
= accB
= accA
= 0;
290 // Note that samples=0 means no multisampling!
291 // One might think that one sample per pixel means non-multisampling
292 // but that's not the convention used here.
296 glXGetConfig(dpy
, vi
, GLX_SAMPLE_BUFFERS
, &sampBuf
);
298 glXGetConfig(dpy
, vi
, GLX_SAMPLES
, &samples
);
302 canWindow
= canPixmap
= true;
303 // Only guaranteed in early versions of GLX.
305 # if defined(GLX_VERSION_1_3)
308 maxPBufferHeight
= 0;
309 maxPBufferPixels
= 0;
312 canWinSysRender
= true;
313 // Only guaranteed in early versions of GLX.
317 # if defined(GLX_EXT_visual_rating)
318 if (haveGLXExtension(dpy
, "GLX_EXT_visual_rating")) {
319 glXGetConfig(dpy
, vi
, GLX_VISUAL_CAVEAT_EXT
, &var
);
320 if (var
== GLX_SLOW_VISUAL_EXT
)
322 else if (var
== GLX_NON_CONFORMANT_VISUAL_EXT
)
328 transR
= transG
= transB
= transA
= transI
= 0;
329 # if defined(GLX_EXT_visual_info)
330 if (haveGLXExtension(dpy
, "GLX_EXT_visual_info")) {
331 glXGetConfig(dpy
, vi
, GLX_TRANSPARENT_TYPE_EXT
, &var
);
332 if (var
== GLX_TRANSPARENT_RGB_EXT
) {
333 glXGetConfig(dpy
, vi
,
334 GLX_TRANSPARENT_RED_VALUE_EXT
, &transR
);
335 glXGetConfig(dpy
, vi
,
336 GLX_TRANSPARENT_GREEN_VALUE_EXT
, &transG
);
337 glXGetConfig(dpy
, vi
,
338 GLX_TRANSPARENT_BLUE_VALUE_EXT
, &transB
);
339 glXGetConfig(dpy
, vi
,
340 GLX_TRANSPARENT_ALPHA_VALUE_EXT
, &transA
);
342 glXGetConfig(dpy
, vi
,
343 GLX_TRANSPARENT_INDEX_VALUE_EXT
, &transI
);
346 } // DrawingSurfaceConfig::DrawingSurfaceConfig
348 #if defined(GLX_VERSION_1_3)
349 DrawingSurfaceConfig::DrawingSurfaceConfig(::Display
* dpy
, ::GLXFBConfig
* pfbc
)
351 // silence warnings about unused parameters:
355 if (!mapsInitialized
)
357 // XXX Need to write drawing surface config code for GLX 1.3
358 cerr
<< "GLX 1.3 version of DrawingSurfaceConfig constructor is not"
360 } // DrawingSurfaceConfig::DrawingSurfaceConfig
363 #elif defined(__WIN__)
365 DrawingSurfaceConfig::DrawingSurfaceConfig(int id
, ::PIXELFORMATDESCRIPTOR
*ppfd
)
367 if (!mapsInitialized
)
373 canRGBA
= pfd
->iPixelType
== PFD_TYPE_RGBA
;
374 canCI
= pfd
->iPixelType
== PFD_TYPE_COLORINDEX
;
376 bufSize
= pfd
->cColorBits
+ pfd
->cAlphaBits
;
380 db
= pfd
->dwFlags
& PFD_DOUBLEBUFFER
;
382 stereo
= pfd
->dwFlags
& PFD_STEREO
;
384 aux
= pfd
->cAuxBuffers
;
396 s
= pfd
->cStencilBits
;
398 accR
= pfd
->cAccumRedBits
;
399 accG
= pfd
->cAccumGreenBits
;
400 accB
= pfd
->cAccumBlueBits
;
401 accA
= pfd
->cAccumAlphaBits
;
403 samples
= 0; // XXX implement properly for Windows!
405 canWindow
= pfd
->dwFlags
& PFD_DRAW_TO_WINDOW
;
407 canWinSysRender
= pfd
->dwFlags
& PFD_SUPPORT_GDI
;
409 if (pfd
->dwFlags
& PFD_GENERIC_FORMAT
)
411 if (pfd
->dwFlags
& PFD_GENERIC_ACCELERATED
)
413 // it's an MCD - at least it has some acceleration
428 // we'll assume that the OpenGL implementation thinks it is conformant
431 // chromakeying isn't supported
433 transR
= transG
= transB
= transA
= transI
= 0;
435 #elif defined(__BEWIN__)
437 DrawingSurfaceConfig::DrawingSurfaceConfig() {
439 if (!mapsInitialized
)
442 /* these values are estimates for the moment */
459 // This is a software-mode assumption
462 // we'll assume that the OpenGL implementation thinks it is conformant
465 // chromakeying isn't supported
467 transR
= transG
= transB
= transA
= transI
= 0;
470 #elif defined(__AGL__)
472 DrawingSurfaceConfig::DrawingSurfaceConfig(int id
, ::AGLPixelFormat pfd
)
476 if (!mapsInitialized
)
481 if (aglDescribePixelFormat( pf
, AGL_RGBA
, &i
))
482 canRGBA
= (i
== GL_TRUE
);
483 canCI
= (i
== GL_FALSE
);
485 if (aglDescribePixelFormat( pf
, AGL_BUFFER_SIZE
, &i
))
490 if (aglDescribePixelFormat( pf
, AGL_DOUBLEBUFFER
, &i
))
492 if (aglDescribePixelFormat( pf
, AGL_STEREO
, &i
))
493 stereo
= (i
== GL_TRUE
);
494 if (aglDescribePixelFormat( pf
, AGL_AUX_BUFFERS
, &i
))
498 aglDescribePixelFormat( pf
, AGL_RED_SIZE
, &r
);
499 aglDescribePixelFormat( pf
, AGL_GREEN_SIZE
, &g
);
500 aglDescribePixelFormat( pf
, AGL_BLUE_SIZE
, &b
);
501 aglDescribePixelFormat( pf
, AGL_ALPHA_SIZE
, &a
);
503 //this is a workaround for some versions of AGL
507 bufSize
= r
+ g
+ b
+ a
;
513 samples
= 0; // XXX implement properly for AGL
515 aglDescribePixelFormat( pf
, AGL_DEPTH_SIZE
, & z
);
516 aglDescribePixelFormat( pf
, AGL_STENCIL_SIZE
, & s
);
518 aglDescribePixelFormat( pf
, AGL_ACCUM_RED_SIZE
, & accR
);
519 aglDescribePixelFormat( pf
, AGL_ACCUM_GREEN_SIZE
, & accG
);
520 aglDescribePixelFormat( pf
, AGL_ACCUM_BLUE_SIZE
, & accB
);
521 aglDescribePixelFormat( pf
, AGL_ACCUM_ALPHA_SIZE
, & accA
);
523 aglDescribePixelFormat( pf
, AGL_WINDOW
, &i
);
525 aglDescribePixelFormat( pf
, AGL_OFFSCREEN
, &i
);
527 aglDescribePixelFormat( pf
, AGL_ACCELERATED
, & i
);
530 // we'll assume that the OpenGL implementation thinks it is conformant
533 // chromakeying isn't supported
535 transR
= transG
= transB
= transA
= transI
= 0;
540 DrawingSurfaceConfig::DrawingSurfaceConfig(string
& str
) {
541 if (!mapsInitialized
)
547 Lex
lex(str
.c_str());
549 for (lex
.next(); lex
.token
!= Lex::END
; lex
.next()) {
550 if (lex
.token
!= Lex::ID
)
551 throw Syntax("expected variable name",
554 if (lex
.token
!= Lex::ICONST
)
555 throw Syntax("expected integer value",
558 // Yes, this is an unpleasantly verbose way to
559 // handle this problem. However, it will be
560 // necessary when we have to deal with attributes
561 // that aren't all of a simple integral type.
563 switch (mapNameToVar
[lex
.id
]) {
565 # if defined(__X11__)
570 # if defined(GLX_VERSION_1_3)
575 canRGBA
= lex
.iValue
;
593 bufSize
= lex
.iValue
;
626 samples
= lex
.iValue
;
629 canWindow
= lex
.iValue
;
632 # if defined(__X11__)
633 canPixmap
= lex
.iValue
;
637 # if defined(GLX_VERSION_1_3)
638 canPBuffer
= lex
.iValue
;
641 case VMAXPBUFFERWIDTH
:
642 # if defined(GLX_VERSION_1_3)
643 maxPBufferWidth
= lex
.iValue
;
646 case VMAXPBUFFERHEIGHT
:
647 # if defined(GLX_VERSION_1_3)
648 maxPBufferHeight
= lex
.iValue
;
651 case VMAXPBUFFERPIXELS
:
652 # if defined(GLX_VERSION_1_3)
653 maxPBufferPixels
= lex
.iValue
;
656 case VCANWINSYSRENDER
:
657 canWinSysRender
= lex
.iValue
;
663 conformant
= lex
.iValue
;
666 transparent
= lex
.iValue
;
684 throw Syntax("unrecognized variable",
689 catch (Lex::Lexical e
) {
690 throw Syntax(e
.err
, e
.position
);
692 } // DrawingSurfaceConfing::DrawingSurfaceConfig
695 ///////////////////////////////////////////////////////////////////////////////
696 // canonicalDescription - return a description string that can be used
697 // to reconstruct the essential attributes of a drawing surface
698 // configuration. Note that visual ID numbers are included for
699 // completeness, but they must be ignored when attempting to compare
700 // two surface configurations; there's no guarantee that they'll be
701 // valid (or even relevant, since they may have been created on another
704 // This is ugly code, but it keeps the names used here and in
705 // the string-based constructor for DrawingSurfaceConfig in sync
707 ///////////////////////////////////////////////////////////////////////////////
709 DrawingSurfaceConfig::canonicalDescription() {
712 # if defined(__X11__)
713 s
<< mapVarToName
[VID
] << ' ' << visID
;
714 # if defined(GLX_VERSION_1_3)
715 s
<< ' ' << mapVarToName
[VFBCID
] << ' ' << fbcID
;
717 # elif defined(__WIN__)
718 s
<< mapVarToName
[VID
] << ' ' << pfdID
;
721 s
<< ' ' << mapVarToName
[VCANRGBA
] << ' ' << canRGBA
;
722 s
<< ' ' << mapVarToName
[VR
] << ' ' << r
723 << ' ' << mapVarToName
[VG
] << ' ' << g
724 << ' ' << mapVarToName
[VB
] << ' ' << b
725 << ' ' << mapVarToName
[VA
] << ' ' << a
;
727 s
<< ' ' << mapVarToName
[VCANCI
] << ' ' << canCI
;
729 s
<< ' ' << mapVarToName
[VBUFSIZE
] << ' ' << bufSize
;
731 s
<< ' ' << mapVarToName
[VLEVEL
] << ' ' << level
;
733 s
<< ' ' << mapVarToName
[VDB
] << ' ' << db
;
735 s
<< ' ' << mapVarToName
[VSTEREO
] << ' '<< stereo
;
737 s
<< ' ' << mapVarToName
[VAUX
] << ' ' << aux
;
739 s
<< ' ' << mapVarToName
[VZ
] << ' ' << z
;
741 s
<< ' ' << mapVarToName
[VS
] << ' ' << DrawingSurfaceConfig::s
;
743 s
<< ' ' << mapVarToName
[VACCUMR
] << ' ' << accR
744 << ' ' << mapVarToName
[VACCUMG
] << ' ' << accG
745 << ' ' << mapVarToName
[VACCUMB
] << ' ' << accB
746 << ' ' << mapVarToName
[VACCUMA
] << ' ' << accA
;
748 s
<< ' ' << mapVarToName
[VSAMPLES
] << ' ' << samples
;
750 s
<< ' ' << mapVarToName
[VCANWINDOW
] << ' ' << canWindow
;
752 # if defined(__X11__)
753 s
<< ' ' << mapVarToName
[VCANPIXMAP
] << ' ' << canPixmap
;
755 # if defined(GLX_VERSION_1_3)
756 s
<< ' ' << mapVarToName
[VCANPBUFFER
] << ' ' << canPBuffer
;
757 s
<< ' ' << mapVarToName
[VMAXPBUFFERWIDTH
] << ' ' << maxPBufferWidth
;
758 s
<< ' ' << mapVarToName
[VMAXPBUFFERHEIGHT
] << ' ' << maxPBufferHeight
;
759 s
<< ' ' << mapVarToName
[VMAXPBUFFERPIXELS
] << ' ' << maxPBufferPixels
;
764 s
<< ' ' << mapVarToName
[VCANWINSYSRENDER
] << ' ' << canWinSysRender
;
766 s
<< ' ' << mapVarToName
[VFAST
] << ' ' << fast
;
768 s
<< ' ' << mapVarToName
[VCONFORMANT
] << ' ' << conformant
;
770 s
<< ' ' << mapVarToName
[VTRANSPARENT
] << ' ' << transparent
;
771 s
<< ' ' << mapVarToName
[VTRANSR
] << ' ' << transR
772 << ' ' << mapVarToName
[VTRANSG
] << ' ' << transG
773 << ' ' << mapVarToName
[VTRANSB
] << ' ' << transB
774 << ' ' << mapVarToName
[VTRANSA
] << ' ' << transA
775 << ' ' << mapVarToName
[VTRANSI
] << ' ' << transI
;
778 } // DrawingSurfaceConfig::canonicalDescription
780 ///////////////////////////////////////////////////////////////////////////////
781 // conciseDescription - return a description string that's appropriate for
782 // reading by humans, rather than parsing by machine.
783 ///////////////////////////////////////////////////////////////////////////////
785 DrawingSurfaceConfig::conciseDescription() {
788 if (canRGBA
&& canCI
)
793 if (r
== g
&& g
== b
&& b
== a
)
796 s
<< "r" << r
<< "g" << g
<< "b" << b
799 if (r
== g
&& g
== b
)
802 s
<< "r" << r
<< "g" << g
<< "b" << b
;
807 if (canRGBA
) s
<< "+";
808 s
<< "ci" << bufSize
;
828 if (DrawingSurfaceConfig::s
)
829 s
<< ", s" << DrawingSurfaceConfig::s
;
833 if (accR
== accG
&& accG
== accB
835 s
<< ", accrgba" << accR
;
837 s
<< ", accr" << accR
<< "g" << accG
838 << "b" << accB
<< "a" << accA
;
840 if (accR
== accG
&& accG
== accB
)
841 s
<< ", accrgb" << accR
;
843 s
<< ", accr" << accR
<< "g" << accG
849 s
<< ", samples" << samples
;
859 # if defined(__X11__)
867 # if defined(GLX_VERSION_1_3)
880 s
<< ", nonconformant";
884 s
<< ", transrgba (" << transR
<< "," << transG
885 << "," << transB
<< "," << transA
<< ")";
888 s
<< ", transci (" << transI
<< ")";
892 # if defined(__X11__)
893 s
<< ", id " << visID
;
894 # if defined(GLX_VERSION_1_3)
896 s
<< ", fbcid " << fbcID
;
898 # elif defined(__WIN__)
899 s
<< ", id " << pfdID
;
903 } // DrawingSurfaceConfig::conciseDescription
905 ///////////////////////////////////////////////////////////////////////////////
906 // match - select a config that ``matches'' the current config.
907 // To keep this problem manageable, we'll assume that both the config
908 // to be matched (call it the ``A'' config) and the vector of configs to
909 // choose from (call them the ``B'' configs) were selected by a test
910 // using a single filter. Thus we can ignore any differences in buffer
911 // availability (because we know those are irrelevant to the test), and
912 // concentrate on picking configs for which the available buffers are
913 // (in some sense) closest in size.
915 // This will not be an acceptable solution in all cases, but it should
917 ///////////////////////////////////////////////////////////////////////////////
919 DrawingSurfaceConfig::match(vector
<DrawingSurfaceConfig
*>& choices
) {
920 typedef vector
<DrawingSurfaceConfig
*>::iterator cptr
;
922 // first try finding an exact match
923 for (cptr p
= choices
.begin(); p
< choices
.end(); ++p
) {
924 DrawingSurfaceConfig
& c
= **p
;
926 int pos
= static_cast<int>(p
- choices
.begin());
931 // next try finding the closest match
933 int bestError
= INT_MAX
;
935 for (cptr p
= choices
.begin(); p
< choices
.end(); ++p
) {
936 DrawingSurfaceConfig
& c
= **p
;
939 if (bufSize
&& c
.bufSize
)
940 error
+= abs(bufSize
- c
.bufSize
);
942 error
+= abs(r
- c
.r
);
944 error
+= abs(g
- c
.g
);
946 error
+= abs(b
- c
.b
);
948 error
+= abs(a
- c
.a
);
950 error
+= abs(z
- c
.z
);
952 error
+= abs(s
- c
.s
);
954 error
+= abs(accR
- c
.accR
);
956 error
+= abs(accG
- c
.accG
);
958 error
+= abs(accB
- c
.accB
);
960 error
+= abs(accA
- c
.accA
);
961 // Use a huge error value for multisample mismatch.
962 // Not sure this is the best solution.
963 if (samples
!= c
.samples
)
966 if (error
< bestError
) {
968 best
= static_cast<int>(p
- choices
.begin());
973 } // DrawingSurfaceConfig::match
975 // are two surface configs exactly the same?
977 DrawingSurfaceConfig::equal(const DrawingSurfaceConfig
&config
) const
981 visID
== config
.visID
&&
982 # if defined(GLX_VERSION_1_3)
983 fbcID
== config
.fbcID
&&
985 #elif defined(__WIN__)
986 pfdID
== config
.pfdID
&&
987 #elif defined(__AGL__)
988 pfID
== config
.pfID
&&
990 # error "what's the config ID?"
992 canRGBA
== config
.canRGBA
&&
993 canCI
== config
.canCI
&&
994 bufSize
== config
.bufSize
&&
995 level
== config
.level
&&
997 stereo
== config
.stereo
&&
1005 accR
== config
.accR
&&
1006 accG
== config
.accG
&&
1007 accB
== config
.accB
&&
1008 accA
== config
.accA
&&
1009 samples
== config
.samples
&&
1010 canWindow
== config
.canWindow
&&
1011 #if defined(__X11__)
1012 canPixmap
== config
.canPixmap
&&
1013 #if defined(GLX_VERSION_1_3)
1014 canPBuffer
== config
.canPBuffer
&&
1015 maxPBufferWidth
== config
.maxPBufferWidth
&&
1016 maxPBufferHeight
== config
.maxPBufferHeight
&&
1017 maxPBufferPixels
== config
.maxPBufferPixels
&&
1020 canWinSysRender
== config
.canWinSysRender
&&
1021 fast
== config
.fast
&&
1022 conformant
== config
.conformant
&&
1023 transparent
== config
.transparent
&&
1024 transR
== config
.transR
&&
1025 transG
== config
.transG
&&
1026 transB
== config
.transB
&&
1027 transA
== config
.transA
&&
1028 transI
== config
.transI
1035 } // namespace GLEAN