2012-03-22 01:36:20 +08:00
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2003-01-22 00:45:36 +08:00
*
2012-03-22 01:36:20 +08:00
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
2003-01-22 00:45:36 +08:00
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
2012-03-22 01:36:20 +08:00
*
2003-01-22 00:45:36 +08:00
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
2012-03-22 01:36:20 +08:00
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2003-01-22 00:45:36 +08:00
* OpenSceneGraph Public License for more details.
*/
2001-12-19 08:38:23 +08:00
2001-12-22 06:48:19 +08:00
#ifndef OSG_DisplaySettings
#define OSG_DisplaySettings 1
2001-12-19 08:38:23 +08:00
#include <osg/Referenced>
2013-04-15 22:21:32 +08:00
#include <osg/Matrixd>
2010-04-30 19:48:30 +08:00
#include <osg/ref_ptr>
2001-12-19 08:38:23 +08:00
#include <string>
#include <vector>
namespace osg {
2003-02-19 05:58:40 +08:00
// forward declare
class ArgumentParser;
class ApplicationUsage;
2001-12-22 06:48:19 +08:00
/** DisplaySettings class for encapsulating what visuals are required and
2001-12-19 08:38:23 +08:00
* have been set up, and the status of stereo viewing.*/
2005-04-12 01:14:17 +08:00
class OSG_EXPORT DisplaySettings : public osg::Referenced
2001-12-19 08:38:23 +08:00
{
public:
2004-08-31 22:08:12 +08:00
/** Maintain a DisplaySettings singleton for objects to query at runtime.*/
2010-04-30 19:48:30 +08:00
static ref_ptr<DisplaySettings>& instance();
2002-02-28 08:11:31 +08:00
2007-08-22 17:42:27 +08:00
DisplaySettings():
Referenced(true)
2001-12-19 08:38:23 +08:00
{
setDefaults();
readEnvironmentalVariables();
}
2012-03-22 01:36:20 +08:00
2007-08-22 17:42:27 +08:00
DisplaySettings(ArgumentParser& arguments):
Referenced(true)
2001-12-19 08:38:23 +08:00
{
setDefaults();
readEnvironmentalVariables();
2003-03-14 18:49:06 +08:00
readCommandLine(arguments);
2001-12-19 08:38:23 +08:00
}
2001-12-22 06:48:19 +08:00
DisplaySettings(const DisplaySettings& vs);
2012-03-22 01:36:20 +08:00
2002-02-28 08:11:31 +08:00
2001-12-22 06:48:19 +08:00
DisplaySettings& operator = (const DisplaySettings& vs);
2012-03-22 01:36:20 +08:00
2004-05-05 17:16:12 +08:00
void setDisplaySettings(const DisplaySettings& vs);
2012-03-22 01:36:20 +08:00
2001-12-22 06:48:19 +08:00
void merge(const DisplaySettings& vs);
2001-12-19 08:38:23 +08:00
void setDefaults();
2012-03-22 01:36:20 +08:00
2004-05-05 17:16:12 +08:00
/** read the environmental variables.*/
2001-12-19 08:38:23 +08:00
void readEnvironmentalVariables();
2003-02-19 00:36:42 +08:00
/** read the commandline arguments.*/
2003-03-14 18:49:06 +08:00
void readCommandLine(ArgumentParser& arguments);
2012-03-22 01:36:20 +08:00
2003-10-01 23:56:52 +08:00
enum DisplayType
{
MONITOR,
POWERWALL,
REALITY_CENTER,
HEAD_MOUNTED_DISPLAY
};
void setDisplayType(DisplayType type) { _displayType = type; }
2012-03-22 01:36:20 +08:00
2003-10-01 23:56:52 +08:00
DisplayType getDisplayType() const { return _displayType; }
2001-12-19 08:38:23 +08:00
2002-09-02 20:31:35 +08:00
void setStereo(bool on) { _stereo = on; }
bool getStereo() const { return _stereo; }
2001-12-19 08:38:23 +08:00
enum StereoMode
{
QUAD_BUFFER,
ANAGLYPHIC,
HORIZONTAL_SPLIT,
2002-11-12 18:22:38 +08:00
VERTICAL_SPLIT,
LEFT_EYE,
2005-10-25 23:43:04 +08:00
RIGHT_EYE,
HORIZONTAL_INTERLACE,
2007-08-23 22:31:23 +08:00
VERTICAL_INTERLACE,
CHECKERBOARD
2001-12-19 08:38:23 +08:00
};
2012-03-22 01:36:20 +08:00
2002-09-02 20:31:35 +08:00
void setStereoMode(StereoMode mode) { _stereoMode = mode; }
StereoMode getStereoMode() const { return _stereoMode; }
2001-12-19 08:38:23 +08:00
2002-09-02 20:31:35 +08:00
void setEyeSeparation(float eyeSeparation) { _eyeSeparation = eyeSeparation; }
float getEyeSeparation() const { return _eyeSeparation; }
2001-12-19 08:38:23 +08:00
2002-04-15 06:21:59 +08:00
enum SplitStereoHorizontalEyeMapping
{
LEFT_EYE_LEFT_VIEWPORT,
LEFT_EYE_RIGHT_VIEWPORT
};
2012-03-22 01:36:20 +08:00
2002-04-15 06:21:59 +08:00
void setSplitStereoHorizontalEyeMapping(SplitStereoHorizontalEyeMapping m) { _splitStereoHorizontalEyeMapping = m; }
SplitStereoHorizontalEyeMapping getSplitStereoHorizontalEyeMapping() const { return _splitStereoHorizontalEyeMapping; }
2002-09-02 20:31:35 +08:00
void setSplitStereoHorizontalSeparation(int s) { _splitStereoHorizontalSeparation = s; }
int getSplitStereoHorizontalSeparation() const { return _splitStereoHorizontalSeparation; }
2002-04-15 06:21:59 +08:00
enum SplitStereoVerticalEyeMapping
{
LEFT_EYE_TOP_VIEWPORT,
2002-05-27 14:37:51 +08:00
LEFT_EYE_BOTTOM_VIEWPORT
2002-04-15 06:21:59 +08:00
};
void setSplitStereoVerticalEyeMapping(SplitStereoVerticalEyeMapping m) { _splitStereoVerticalEyeMapping = m; }
SplitStereoVerticalEyeMapping getSplitStereoVerticalEyeMapping() const { return _splitStereoVerticalEyeMapping; }
2002-09-02 20:31:35 +08:00
void setSplitStereoVerticalSeparation(int s) { _splitStereoVerticalSeparation = s; }
int getSplitStereoVerticalSeparation() const { return _splitStereoVerticalSeparation; }
2002-04-15 06:21:59 +08:00
2007-07-11 23:51:17 +08:00
void setSplitStereoAutoAdjustAspectRatio(bool flag) { _splitStereoAutoAdjustAspectRatio=flag; }
bool getSplitStereoAutoAdjustAspectRatio() const { return _splitStereoAutoAdjustAspectRatio; }
2003-05-27 19:03:49 +08:00
2001-12-19 08:38:23 +08:00
2003-11-05 00:38:10 +08:00
void setScreenWidth(float width) { _screenWidth = width; }
float getScreenWidth() const { return _screenWidth; }
2002-09-02 20:31:35 +08:00
void setScreenHeight(float height) { _screenHeight = height; }
float getScreenHeight() const { return _screenHeight; }
2001-12-22 06:48:19 +08:00
2003-11-05 00:38:10 +08:00
void setScreenDistance(float distance) { _screenDistance = distance; }
float getScreenDistance() const { return _screenDistance; }
2001-12-19 08:38:23 +08:00
2002-09-02 20:31:35 +08:00
void setDoubleBuffer(bool flag) { _doubleBuffer = flag; }
bool getDoubleBuffer() const { return _doubleBuffer; }
2001-12-19 08:38:23 +08:00
2002-09-02 20:31:35 +08:00
void setRGB(bool flag) { _RGB = flag; }
bool getRGB() const { return _RGB; }
2001-12-19 08:38:23 +08:00
2002-09-02 20:31:35 +08:00
void setDepthBuffer(bool flag) { _depthBuffer = flag; }
bool getDepthBuffer() const { return _depthBuffer; }
2001-12-19 08:38:23 +08:00
2002-09-02 20:31:35 +08:00
void setMinimumNumAlphaBits(unsigned int bits) { _minimumNumberAlphaBits = bits; }
unsigned int getMinimumNumAlphaBits() const { return _minimumNumberAlphaBits; }
bool getAlphaBuffer() const { return _minimumNumberAlphaBits!=0; }
2001-12-19 08:38:23 +08:00
2002-09-02 20:31:35 +08:00
void setMinimumNumStencilBits(unsigned int bits) { _minimumNumberStencilBits = bits; }
unsigned int getMinimumNumStencilBits() const { return _minimumNumberStencilBits; }
bool getStencilBuffer() const { return _minimumNumberStencilBits!=0; }
2001-12-19 08:38:23 +08:00
2005-11-03 03:15:18 +08:00
void setMinimumNumAccumBits(unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha);
unsigned int getMinimumNumAccumRedBits() const { return _minimumNumberAccumRedBits; }
unsigned int getMinimumNumAccumGreenBits() const { return _minimumNumberAccumGreenBits; }
unsigned int getMinimumNumAccumBlueBits() const { return _minimumNumberAccumBlueBits; }
unsigned int getMinimumNumAccumAlphaBits() const { return _minimumNumberAccumAlphaBits; }
bool getAccumBuffer() const { return (_minimumNumberAccumRedBits+_minimumNumberAccumGreenBits+_minimumNumberAccumBlueBits+_minimumNumberAccumAlphaBits)!=0; }
2002-02-28 08:11:31 +08:00
2006-07-26 23:29:26 +08:00
void setMaxNumberOfGraphicsContexts(unsigned int num);
unsigned int getMaxNumberOfGraphicsContexts() const;
2002-02-28 08:11:31 +08:00
2006-03-14 05:29:17 +08:00
void setNumMultiSamples(unsigned int samples) { _numMultiSamples = samples; }
unsigned int getNumMultiSamples() const { return _numMultiSamples; }
bool getMultiSamples() const { return _numMultiSamples!=0; }
2012-03-22 01:36:20 +08:00
2007-08-11 22:49:14 +08:00
void setCompileContextsHint(bool useCompileContexts) { _compileContextsHint = useCompileContexts; }
bool getCompileContextsHint() const { return _compileContextsHint; }
2012-03-22 01:36:20 +08:00
2007-08-23 19:00:12 +08:00
void setSerializeDrawDispatch(bool serializeDrawDispatch) { _serializeDrawDispatch = serializeDrawDispatch; }
bool getSerializeDrawDispatch() const { return _serializeDrawDispatch; }
2012-03-22 01:36:20 +08:00
2013-04-15 22:21:32 +08:00
void setUseSceneViewForStereoHint(bool hint) { _useSceneViewForStereoHint = hint; }
bool getUseSceneViewForStereoHint() const { return _useSceneViewForStereoHint; }
2008-10-27 06:22:38 +08:00
/** Set the hint for the total number of threads in the DatbasePager set up, inclusive of the number of http dedicated threads.*/
void setNumOfDatabaseThreadsHint(unsigned int numThreads) { _numDatabaseThreadsHint = numThreads; }
/** Get the hint for total number of threads in the DatbasePager set up, inclusive of the number of http dedicated threads.*/
unsigned int getNumOfDatabaseThreadsHint() const { return _numDatabaseThreadsHint; }
/** Set the hint for number of threads in the DatbasePager to dedicate to reading http requests.*/
2009-06-17 16:56:11 +08:00
void setNumOfHttpDatabaseThreadsHint(unsigned int numThreads) { _numHttpDatabaseThreadsHint = numThreads; }
2008-10-27 06:22:38 +08:00
/** Get the hint for number of threads in the DatbasePager dedicated to reading http requests.*/
unsigned int getNumOfHttpDatabaseThreadsHint() const { return _numHttpDatabaseThreadsHint; }
2012-03-22 01:36:20 +08:00
2008-11-20 20:03:21 +08:00
void setApplication(const std::string& application) { _application = application; }
const std::string& getApplication() { return _application; }
2002-02-28 08:11:31 +08:00
2009-09-23 02:45:24 +08:00
void setMaxTexturePoolSize(unsigned int size) { _maxTexturePoolSize = size; }
unsigned int getMaxTexturePoolSize() const { return _maxTexturePoolSize; }
2009-10-03 17:25:23 +08:00
void setMaxBufferObjectPoolSize(unsigned int size) { _maxBufferObjectPoolSize = size; }
unsigned int getMaxBufferObjectPoolSize() const { return _maxBufferObjectPoolSize; }
2009-09-23 02:45:24 +08:00
2012-03-22 01:36:20 +08:00
/**
Methods used to set and get defaults for Cameras implicit buffer attachments.
For more info: See description of Camera::setImplicitBufferAttachment method
2009-11-19 18:10:50 +08:00
2012-03-22 01:36:20 +08:00
DisplaySettings implicit buffer attachment selection defaults to: DEPTH and COLOR
2015-04-08 02:01:12 +08:00
for both primary (Render) FBO and secondary Multisample (Resolve) FBO
ie: IMPLICIT_DEPTH_BUFFER_ATTACHMENT | IMPLICIT_COLOR_BUFFER_ATTACHMENT
2009-11-19 18:10:50 +08:00
**/
enum ImplicitBufferAttachment
{
IMPLICIT_DEPTH_BUFFER_ATTACHMENT = (1 << 0),
IMPLICIT_STENCIL_BUFFER_ATTACHMENT = (1 << 1),
IMPLICIT_COLOR_BUFFER_ATTACHMENT = (1 << 2),
DEFAULT_IMPLICIT_BUFFER_ATTACHMENT = IMPLICIT_COLOR_BUFFER_ATTACHMENT | IMPLICIT_DEPTH_BUFFER_ATTACHMENT
};
typedef int ImplicitBufferAttachmentMask;
void setImplicitBufferAttachmentMask(ImplicitBufferAttachmentMask renderMask = DisplaySettings::DEFAULT_IMPLICIT_BUFFER_ATTACHMENT, ImplicitBufferAttachmentMask resolveMask = DisplaySettings::DEFAULT_IMPLICIT_BUFFER_ATTACHMENT )
{
_implicitBufferAttachmentRenderMask = renderMask;
_implicitBufferAttachmentResolveMask = resolveMask;
}
void setImplicitBufferAttachmentRenderMask(ImplicitBufferAttachmentMask implicitBufferAttachmentRenderMask)
{
_implicitBufferAttachmentRenderMask = implicitBufferAttachmentRenderMask;
}
void setImplicitBufferAttachmentResolveMask(ImplicitBufferAttachmentMask implicitBufferAttachmentResolveMask)
{
_implicitBufferAttachmentResolveMask = implicitBufferAttachmentResolveMask;
}
2015-04-08 02:01:12 +08:00
/** Get mask selecting default implicit buffer attachments for Cameras primary FBOs. */
2009-11-19 18:10:50 +08:00
ImplicitBufferAttachmentMask getImplicitBufferAttachmentRenderMask() const { return _implicitBufferAttachmentRenderMask; }
2015-04-08 02:01:12 +08:00
/** Get mask selecting default implicit buffer attachments for Cameras secondary MULTISAMPLE FBOs. */
2009-11-19 18:10:50 +08:00
ImplicitBufferAttachmentMask getImplicitBufferAttachmentResolveMask() const { return _implicitBufferAttachmentResolveMask;}
2010-09-30 22:25:27 +08:00
enum SwapMethod
{
SWAP_DEFAULT, // Leave swap method at default returned by choose Pixel Format.
2012-03-22 01:36:20 +08:00
SWAP_EXCHANGE, // Flip front / back buffer.
SWAP_COPY, // Copy back to front buffer.
2010-09-30 22:25:27 +08:00
SWAP_UNDEFINED // Move back to front buffer leaving contents of back buffer undefined.
};
2012-03-22 01:36:20 +08:00
2010-09-30 22:25:27 +08:00
/** Select preferred swap method */
void setSwapMethod( SwapMethod swapMethod ) { _swapMethod = swapMethod; }
/** Get preferred swap method */
SwapMethod getSwapMethod( void ) { return _swapMethod; }
2009-11-19 18:10:50 +08:00
2014-10-21 22:46:12 +08:00
/** Set whether Arb Sync should be used to manage the swaps buffers, 0 disables the use of the sync, greater than zero enables sync based on number of frames specified.*/
void setSyncSwapBuffers(unsigned int numFrames=0) { _syncSwapBuffers = numFrames; }
/** Set whether Arb Sync should be used to manage the swaps buffers.*/
unsigned int getSyncSwapBuffers() const { return _syncSwapBuffers; }
2009-11-11 23:25:42 +08:00
/** Set the hint of which OpenGL version to attempt to create a graphics context for.*/
void setGLContextVersion(const std::string& version) { _glContextVersion = version; }
/** Get the hint of which OpenGL version to attempt to create a graphics context for.*/
const std::string getGLContextVersion() const { return _glContextVersion; }
/** Set the hint of the flags to use in when creating graphic contexts.*/
void setGLContextFlags(unsigned int flags) { _glContextFlags = flags; }
/** Get the hint of the flags to use in when creating graphic contexts.*/
unsigned int getGLContextFlags() const { return _glContextFlags; }
2012-03-22 01:36:20 +08:00
2009-11-11 23:25:42 +08:00
/** Set the hint of the profile mask to use in when creating graphic contexts.*/
void setGLContextProfileMask(unsigned int mask) { _glContextProfileMask = mask; }
/** Get the hint of the profile mask to use in when creating graphic contexts.*/
unsigned int getGLContextProfileMask() const { return _glContextProfileMask; }
2013-10-23 03:12:34 +08:00
2014-12-18 18:59:07 +08:00
/** Set the NvOptimusEnablement value. Default can be set using OSG_NvOptimusEnablement env var.*/
void setNvOptimusEnablement(int value);
/** Get the NvOptimusEnablement value. */
int getNvOptimusEnablement() const;
2013-10-23 03:12:34 +08:00
2016-07-15 22:41:43 +08:00
enum GeometryImplementation
{
OLD_GEOMETRY_IMPLEMENTATION,
NEW_GEOMETRY_IMPLEMENTATION,
VERTEX_ARRAY_OBJECT
};
void setGeometryImplementation(GeometryImplementation gi) { _geometryImplementation = gi; }
GeometryImplementation getGeometryImplementation() const { return _geometryImplementation; }
2013-05-11 01:59:07 +08:00
void setKeystoneHint(bool enabled) { _keystoneHint = enabled; }
bool getKeystoneHint() const { return _keystoneHint; }
2013-10-23 03:12:34 +08:00
2013-05-09 23:18:14 +08:00
typedef std::vector<std::string> FileNames;
void setKeystoneFileNames(const FileNames& filenames) { _keystoneFileNames = filenames; }
FileNames& getKeystoneFileNames() { return _keystoneFileNames; }
const FileNames& getKeystoneFileNames() const { return _keystoneFileNames; }
2013-10-23 03:12:34 +08:00
2013-05-09 23:18:14 +08:00
typedef std::vector< osg::ref_ptr<osg::Object> > Objects;
void setKeystones(const Objects& objects) { _keystones = objects; }
Objects& getKeystones() { return _keystones; }
const Objects& getKeystones() const { return _keystones; }
2013-04-15 22:21:32 +08:00
2013-10-23 03:12:34 +08:00
enum OSXMenubarBehavior {
MENUBAR_AUTO_HIDE,
MENUBAR_FORCE_HIDE,
MENUBAR_FORCE_SHOW
};
OSXMenubarBehavior getOSXMenubarBehavior() const { return _OSXMenubarBehavior; }
void setOSXMenubarBehavior(OSXMenubarBehavior hint) { _OSXMenubarBehavior = hint; }
2013-04-15 22:21:32 +08:00
/** helper function for computing the left eye projection matrix.*/
virtual osg::Matrixd computeLeftEyeProjectionImplementation(const osg::Matrixd& projection) const;
/** helper function for computing the left eye view matrix.*/
virtual osg::Matrixd computeLeftEyeViewImplementation(const osg::Matrixd& view, double eyeSeperationScale=1.0) const;
/** helper function for computing the right eye view matrix.*/
virtual osg::Matrixd computeRightEyeProjectionImplementation(const osg::Matrixd& projection) const;
/** helper function for computing the right eye view matrix.*/
2013-05-09 23:18:14 +08:00
virtual osg::Matrixd computeRightEyeViewImplementation(const osg::Matrixd& view, double eyeSeperationScale=1.0) const;
2013-04-15 22:21:32 +08:00
2001-12-19 08:38:23 +08:00
protected:
2012-03-22 01:36:20 +08:00
2003-01-10 17:25:42 +08:00
virtual ~DisplaySettings();
2001-12-19 08:38:23 +08:00
2003-10-01 23:56:52 +08:00
DisplayType _displayType;
2002-04-15 06:21:59 +08:00
bool _stereo;
StereoMode _stereoMode;
2002-08-29 11:22:27 +08:00
float _eyeSeparation;
2003-11-05 00:38:10 +08:00
float _screenWidth;
2002-04-15 06:21:59 +08:00
float _screenHeight;
2003-11-05 00:38:10 +08:00
float _screenDistance;
2002-04-15 06:21:59 +08:00
SplitStereoHorizontalEyeMapping _splitStereoHorizontalEyeMapping;
2002-08-29 11:22:27 +08:00
int _splitStereoHorizontalSeparation;
2002-04-15 06:21:59 +08:00
SplitStereoVerticalEyeMapping _splitStereoVerticalEyeMapping;
2002-08-29 11:22:27 +08:00
int _splitStereoVerticalSeparation;
2003-05-27 19:03:49 +08:00
bool _splitStereoAutoAdjustAspectRatio;
2012-03-22 01:36:20 +08:00
2002-04-15 06:21:59 +08:00
bool _doubleBuffer;
bool _RGB;
bool _depthBuffer;
unsigned int _minimumNumberAlphaBits;
unsigned int _minimumNumberStencilBits;
2005-11-03 03:15:18 +08:00
unsigned int _minimumNumberAccumRedBits;
unsigned int _minimumNumberAccumGreenBits;
unsigned int _minimumNumberAccumBlueBits;
unsigned int _minimumNumberAccumAlphaBits;
2001-12-19 08:38:23 +08:00
2002-09-04 18:49:17 +08:00
unsigned int _maxNumOfGraphicsContexts;
2012-03-22 01:36:20 +08:00
2006-03-14 05:29:17 +08:00
unsigned int _numMultiSamples;
2012-03-22 01:36:20 +08:00
2007-08-11 22:49:14 +08:00
bool _compileContextsHint;
2007-08-23 19:00:12 +08:00
bool _serializeDrawDispatch;
2013-04-15 22:21:32 +08:00
bool _useSceneViewForStereoHint;
2001-12-19 08:38:23 +08:00
2008-10-27 06:22:38 +08:00
unsigned int _numDatabaseThreadsHint;
unsigned int _numHttpDatabaseThreadsHint;
2012-03-22 01:36:20 +08:00
2008-11-20 20:03:21 +08:00
std::string _application;
2008-10-27 06:22:38 +08:00
2009-09-23 02:45:24 +08:00
unsigned int _maxTexturePoolSize;
2009-10-03 17:25:23 +08:00
unsigned int _maxBufferObjectPoolSize;
2009-11-19 18:10:50 +08:00
ImplicitBufferAttachmentMask _implicitBufferAttachmentRenderMask;
ImplicitBufferAttachmentMask _implicitBufferAttachmentResolveMask;
2012-03-22 01:36:20 +08:00
2009-11-11 23:25:42 +08:00
std::string _glContextVersion;
unsigned int _glContextFlags;
unsigned int _glContextProfileMask;
2010-09-30 22:25:27 +08:00
SwapMethod _swapMethod;
2014-10-21 22:46:12 +08:00
unsigned int _syncSwapBuffers;
2013-10-23 03:12:34 +08:00
2016-07-15 22:41:43 +08:00
GeometryImplementation _geometryImplementation;
2013-05-11 01:59:07 +08:00
bool _keystoneHint;
2013-05-09 23:18:14 +08:00
FileNames _keystoneFileNames;
2013-10-23 03:12:34 +08:00
Objects _keystones;
OSXMenubarBehavior _OSXMenubarBehavior;
2001-12-19 08:38:23 +08:00
};
}
# endif