本文主要是为了分析FreeGLUT与X11、GLX之间的关系,从最终用户的角度分析Mesa的初始化过程。
本文所使用的的FreeGLUT版本是3.2.1.
main --> glutCreateWindow --> fgCreateWindow
--> fgOpenWindow --> fgPlatformOpenWindow --> fghChooseConfig
--> glXChooseFBConfig --> glXGetFBConfigs --> __glXInitialize
int main(int argc, char *argv[])
{
glutInitWindowSize(300, 300);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
win = glutCreateWindow("Gears");
init(argc, argv);
glutDisplayFunc(draw);
glutReshapeFunc(reshape);
glutKeyboardFunc(key);
glutSpecialFunc(special);
glutVisibilityFunc(visible);
update_idle_func();
glutMainLoop();
return 0; /* ANSI C requires main to return int. */
}
/*
* Perform initialization. This usually happens on the program startup
* and restarting after glutMainLoop termination...
*/
void FGAPIENTRY glutInit( int* pargc, char** argv )
{
char* displayName = NULL;
char* geometry = NULL;
if( fgState.Initialised )
fgError( "illegal glutInit() reinitialization attempt" );
if (pargc && *pargc && argv && *argv && **argv)
{
fgState.ProgramName = strdup (*argv);
if( !fgState.ProgramName )
fgError ("Could not allocate space for the program's name.");
}
fgCreateStructure( );
fghParseCommandLineArguments ( pargc, argv, &displayName, &geometry );
/*
* Have the display created now. If there wasn't a "-display"
* in the program arguments, we will use the DISPLAY environment
* variable for opening the X display (see code above):
*/
fgPlatformInitialize( displayName );
/*
* Geometry parsing deferred until here because we may need the screen
* size.
*/
if ( geometry )
{
unsigned int parsedWidth, parsedHeight;
int mask = XParseGeometry( geometry,
&fgState.Position.X, &fgState.Position.Y,
&parsedWidth, &parsedHeight );
/* TODO: Check for overflow? */
fgState.Size.X = parsedWidth;
fgState.Size.Y = parsedHeight;
if( (mask & (WidthValue|HeightValue)) == (WidthValue|HeightValue) )
fgState.Size.Use = GL_TRUE;
if( ( mask & XNegative ) && !fgState.AllowNegativeWindowPosition )
fgState.Position.X += fgDisplay.ScreenWidth - fgState.Size.X;
if( ( mask & YNegative ) && !fgState.AllowNegativeWindowPosition )
fgState.Position.Y += fgDisplay.ScreenHeight - fgState.Size.Y;
if( (mask & (XValue|YValue)) == (XValue|YValue) )
fgState.Position.Use = GL_TRUE;
}
}
/*
* A call to this function should initialize all the display stuff...
*/
void fgPlatformInitialize( const char* displayName )
{
fgDisplay.pDisplay.Display = XOpenDisplay( displayName );
if( fgDisplay.pDisplay.Display == NULL )
fgError( "failed to open display '%s'", XDisplayName( displayName ) );
if ( fgState.XSyncSwitch )
XSynchronize(fgDisplay.pDisplay.Display, True);
#ifdef EGL_VERSION_1_0
fghPlatformInitializeEGL();
#else
if( !glXQueryExtension( fgDisplay.pDisplay.Display, NULL, NULL ) )
fgError( "OpenGL GLX extension not supported by display '%s'",
XDisplayName( displayName ) );
/* This forces AMD Catalyst drivers to initialize and register a shutdown
* function, which must be done before our own call to atexit to prevent
* a crash if glutMainLoop is not called or is not exited cleanly.
* (see bug #206)
*/
glXQueryExtensionsString( fgDisplay.pDisplay.Display,
DefaultScreen( fgDisplay.pDisplay.Display ));
#endif
fgDisplay.pDisplay.Screen = DefaultScreen( fgDisplay.pDisplay.Display );
fgDisplay.pDisplay.RootWindow = RootWindow(
fgDisplay.pDisplay.Display,
fgDisplay.pDisplay.Screen
);
fgDisplay.ScreenWidth = DisplayWidth(
fgDisplay.pDisplay.Display,
fgDisplay.pDisplay.Screen
);
fgDisplay.ScreenHeight = DisplayHeight(
fgDisplay.pDisplay.Display,
fgDisplay.pDisplay.Screen
);
fgDisplay.ScreenWidthMM = DisplayWidthMM(
fgDisplay.pDisplay.Display,
fgDisplay.pDisplay.Screen
);
fgDisplay.ScreenHeightMM = DisplayHeightMM(
fgDisplay.pDisplay.Display,
fgDisplay.pDisplay.Screen
);
fgDisplay.pDisplay.Connection = ConnectionNumber( fgDisplay.pDisplay.Display );
/* Create the window deletion atom */
fgDisplay.pDisplay.DeleteWindow = fghGetAtom("WM_DELETE_WINDOW");
/* Create the state and full screen atoms */
fgDisplay.pDisplay.State = None;
fgDisplay.pDisplay.StateFullScreen = None;
fgDisplay.pDisplay.NetWMPid = None;
fgDisplay.pDisplay.ClientMachine = None;
fgDisplay.pDisplay.NetWMSupported = fghNetWMSupported();
if (fgDisplay.pDisplay.NetWMSupported)
{
const Atom supported = fghGetAtom("_NET_SUPPORTED");
const Atom state = fghGetAtom("_NET_WM_STATE");
/* Check if the state hint is supported. */
if (fgHintPresent(fgDisplay.pDisplay.RootWindow, supported, state))
{
const Atom full_screen = fghGetAtom("_NET_WM_STATE_FULLSCREEN");
fgDisplay.pDisplay.State = state;
/* Check if the window manager supports full screen. */
/** Check "_NET_WM_ALLOWED_ACTIONS" on our window instead? **/
if (fgHintPresent(fgDisplay.pDisplay.RootWindow, supported, full_screen))
{
fgDisplay.pDisplay.StateFullScreen = full_screen;
}
}
fgDisplay.pDisplay.NetWMPid = fghGetAtom("_NET_WM_PID");
fgDisplay.pDisplay.ClientMachine = fghGetAtom("WM_CLIENT_MACHINE");
}
/* Get start time */
fgState.Time = fgSystemTime();
fgState.Initialised = GL_TRUE;
atexit(fgDeinitialize);
/* InputDevice uses GlutTimerFunc(), so fgState.Initialised must be TRUE */
fgInitialiseInputDevices();
}
/*
* Chooses a visual basing on the current display mode settings
*/
int fghChooseConfig(GLXFBConfig* fbconfig)
{
GLboolean wantIndexedMode = GL_FALSE;
int attributes[ 100 ];
int where = 0, numAuxBuffers;
/* First we have to process the display mode settings... */
if( fgState.DisplayMode & GLUT_INDEX ) {
ATTRIB_VAL( GLX_BUFFER_SIZE, 8 );
/* Buffer size is selected later. */
ATTRIB_VAL( GLX_RENDER_TYPE, GLX_COLOR_INDEX_BIT );
wantIndexedMode = GL_TRUE;
} else {
ATTRIB_VAL( GLX_RED_SIZE, 1 );
ATTRIB_VAL( GLX_GREEN_SIZE, 1 );
ATTRIB_VAL( GLX_BLUE_SIZE, 1 );
if( fgState.DisplayMode & GLUT_ALPHA ) {
ATTRIB_VAL( GLX_ALPHA_SIZE, 1 );
}
}
if( fgState.DisplayMode & GLUT_DOUBLE ) {
ATTRIB_VAL( GLX_DOUBLEBUFFER, True );
}
if( fgState.DisplayMode & GLUT_STEREO ) {
ATTRIB_VAL( GLX_STEREO, True );
}
if( fgState.DisplayMode & GLUT_DEPTH ) {
ATTRIB_VAL( GLX_DEPTH_SIZE, 1 );
}
if( fgState.DisplayMode & GLUT_STENCIL ) {
ATTRIB_VAL( GLX_STENCIL_SIZE, 1 );
}
if( fgState.DisplayMode & GLUT_ACCUM ) {
ATTRIB_VAL( GLX_ACCUM_RED_SIZE, 1 );
ATTRIB_VAL( GLX_ACCUM_GREEN_SIZE, 1 );
ATTRIB_VAL( GLX_ACCUM_BLUE_SIZE, 1 );
if( fgState.DisplayMode & GLUT_ALPHA ) {
ATTRIB_VAL( GLX_ACCUM_ALPHA_SIZE, 1 );
}
}
numAuxBuffers = fghNumberOfAuxBuffersRequested();
if ( numAuxBuffers > 0 ) {
ATTRIB_VAL( GLX_AUX_BUFFERS, numAuxBuffers );
}
if( fgState.DisplayMode & GLUT_SRGB ) {
ATTRIB_VAL( GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, True );
}
if (fgState.DisplayMode & GLUT_MULTISAMPLE) {
ATTRIB_VAL(GLX_SAMPLE_BUFFERS, 1);
ATTRIB_VAL(GLX_SAMPLES, fgState.SampleNumber);
}
/* Push a terminator at the end of the list */
ATTRIB( None );
{
GLXFBConfig * fbconfigArray; /* Array of FBConfigs */
int fbconfigArraySize; /* Number of FBConfigs in the array */
/* Get all FBConfigs that match "attributes". */
fbconfigArray = glXChooseFBConfig( fgDisplay.pDisplay.Display,
fgDisplay.pDisplay.Screen,
attributes,
&fbconfigArraySize ); // 死亡凝视
if (fbconfigArray != NULL)
{
int result __fg_unused; /* Returned by glXGetFBConfigAttrib, not checked. */
if( wantIndexedMode )
{
/*
* In index mode, we want the largest buffer size, i.e. visual
* depth. Here, FBConfigs are sorted by increasing buffer size
* first, so FBConfigs with the largest size come last.
*/
int bufferSizeMin, bufferSizeMax;
/* Get bufferSizeMin. */
result =
glXGetFBConfigAttrib( fgDisplay.pDisplay.Display,
fbconfigArray[0],
GLX_BUFFER_SIZE,
&bufferSizeMin );
/* Get bufferSizeMax. */
result =
glXGetFBConfigAttrib( fgDisplay.pDisplay.Display,
fbconfigArray[fbconfigArraySize - 1],
GLX_BUFFER_SIZE,
&bufferSizeMax );
if (bufferSizeMax > bufferSizeMin)
{
/*
* Free and reallocate fbconfigArray, keeping only FBConfigs
* with the largest buffer size.
*/
XFree(fbconfigArray);
/* Add buffer size token at the end of the list. */
where--;
ATTRIB_VAL( GLX_BUFFER_SIZE, bufferSizeMax );
ATTRIB( None );
fbconfigArray = glXChooseFBConfig( fgDisplay.pDisplay.Display,
fgDisplay.pDisplay.Screen,
attributes,
&fbconfigArraySize );
}
}
*fbconfig = fbconfigArray[0];
}
else
{
*fbconfig = NULL;
return 0;
}
XFree(fbconfigArray);
return 1;
}
}
int fghPlatformGlutGetGLX ( GLenum eWhat )
{
switch( eWhat )
{
/*
* The window/context specific queries are handled mostly by
* fgPlatformGetConfig().
*/
case GLUT_WINDOW_NUM_SAMPLES:
{
int nsamples = 0;
#ifdef GLX_VERSION_1_3
glGetIntegerv(GL_SAMPLES, &nsamples);
#endif
return nsamples;
}
/*
* The rest of GLX queries under X are general enough to use a macro to
* check them
*/
# define GLX_QUERY(a,b) case a: return fgPlatformGetConfig( b );
GLX_QUERY( GLUT_WINDOW_RGBA, GLX_RGBA );
GLX_QUERY( GLUT_WINDOW_DOUBLEBUFFER, GLX_DOUBLEBUFFER );
GLX_QUERY( GLUT_WINDOW_BUFFER_SIZE, GLX_BUFFER_SIZE );
GLX_QUERY( GLUT_WINDOW_STENCIL_SIZE, GLX_STENCIL_SIZE );
GLX_QUERY( GLUT_WINDOW_DEPTH_SIZE, GLX_DEPTH_SIZE );
GLX_QUERY( GLUT_WINDOW_RED_SIZE, GLX_RED_SIZE );
GLX_QUERY( GLUT_WINDOW_GREEN_SIZE, GLX_GREEN_SIZE );
GLX_QUERY( GLUT_WINDOW_BLUE_SIZE, GLX_BLUE_SIZE );
GLX_QUERY( GLUT_WINDOW_ALPHA_SIZE, GLX_ALPHA_SIZE );
GLX_QUERY( GLUT_WINDOW_ACCUM_RED_SIZE, GLX_ACCUM_RED_SIZE );
GLX_QUERY( GLUT_WINDOW_ACCUM_GREEN_SIZE, GLX_ACCUM_GREEN_SIZE );
GLX_QUERY( GLUT_WINDOW_ACCUM_BLUE_SIZE, GLX_ACCUM_BLUE_SIZE );
GLX_QUERY( GLUT_WINDOW_ACCUM_ALPHA_SIZE, GLX_ACCUM_ALPHA_SIZE );
GLX_QUERY( GLUT_WINDOW_STEREO, GLX_STEREO );
GLX_QUERY( GLUT_WINDOW_SRGB, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB);
# undef GLX_QUERY
/* I do not know yet if there will be a fgChooseVisual() function for Win32 */
case GLUT_DISPLAY_MODE_POSSIBLE:
{
/* We should not have to call fghChooseConfig again here. */
GLXFBConfig config;
return fghChooseConfig(&config);
}
/* This is system-dependent */
case GLUT_WINDOW_FORMAT_ID:
if( fgStructure.CurrentWindow == NULL )
return 0;
return fgPlatformGetConfig( GLX_VISUAL_ID );
default:
fgWarning( "glutGet(): missing enum handle %d", eWhat );
break;
}
return -1;
}
/*
* Opens a window. Requires a SFG_Window object created and attached
* to the freeglut structure. OpenGL context is created here.
*/
void fgPlatformOpenWindow( SFG_Window* window, const char* title,
GLboolean positionUse, int x, int y,
GLboolean sizeUse, int w, int h,
GLboolean gameMode, GLboolean isSubWindow )
{
XVisualInfo * visualInfo = NULL;
XSetWindowAttributes winAttr;
XTextProperty textProperty;
XSizeHints sizeHints;
XWMHints wmHints;
XEvent eventReturnBuffer; /* return buffer required for a call */
unsigned long mask;
unsigned int current_DisplayMode = fgState.DisplayMode ;
XEvent fakeEvent = {0};
/* Save the display mode if we are creating a menu window */
if( window->IsMenu && ( ! fgStructure.MenuContext ) )
fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB ;
#ifdef EGL_VERSION_1_0
#define WINDOW_CONFIG window->Window.pContext.egl.Config
#else
#define WINDOW_CONFIG window->Window.pContext.FBConfig
#endif
fghChooseConfig(&WINDOW_CONFIG); // 死亡凝视
*
* Opens a window. Requires a SFG_Window object created and attached
* to the freeglut structure. OpenGL context is created here.
*/
void fgOpenWindow( SFG_Window* window, const char* title,
GLboolean positionUse, int x, int y,
GLboolean sizeUse, int w, int h,
GLboolean gameMode, GLboolean isSubWindow )
{
fgPlatformOpenWindow( window, title,
positionUse, x, y,
sizeUse, w, h,
gameMode, isSubWindow ); // 死亡凝视
fgSetWindow( window );
#ifndef EGL_VERSION_1_0
window->Window.DoubleBuffered =
( fgState.DisplayMode & GLUT_DOUBLE ) ? 1 : 0;
if ( ! window->Window.DoubleBuffered )
{
glDrawBuffer ( GL_FRONT );
glReadBuffer ( GL_FRONT );
}
#else
/* - EGL is always double-buffered */
/* - No glDrawBuffer/glReadBuffer in GLES */
window->Window.DoubleBuffered = 1;
#endif
window->Window.attribute_v_coord = -1;
window->Window.attribute_v_normal = -1;
window->Window.attribute_v_texture = -1;
fgInitGL2();
window->State.WorkMask |= GLUT_INIT_WORK;
}
/*
* This private function creates, opens and adds to the hierarchy
* a freeglut window complete with OpenGL context and stuff...
*
* If parent is set to NULL, the window created will be a topmost one.
*/
SFG_Window* fgCreateWindow( SFG_Window* parent, const char* title,
GLboolean positionUse, int x, int y,
GLboolean sizeUse, int w, int h,
GLboolean gameMode, GLboolean isMenu )
{
/* Have the window object created */
SFG_Window *window = (SFG_Window *)calloc( 1, sizeof(SFG_Window) );
if( !window )
{
fgError( "Out of memory. Could not create window." );
}
fgPlatformCreateWindow ( window );
fghClearCallBacks( window );
SET_WCB( *window, Reshape, fghDefaultReshape, NULL);
/* Initialize the object properties */
window->ID = ++fgStructure.WindowID;
fgListInit( &window->Children );
if( parent )
{
fgListAppend( &parent->Children, &window->Node );
window->Parent = parent;
}
else
fgListAppend( &fgStructure.Windows, &window->Node );
/* Set the default mouse cursor */
window->State.Cursor = GLUT_CURSOR_INHERIT;
/* Mark window as menu if a menu is to be created */
window->IsMenu = isMenu;
/*
* Open the window now. The fgOpenWindow() function is system
* dependent, and resides in fg_window.c. Uses fgState.
*/
fgOpenWindow( window, title, positionUse, x, y, sizeUse, w, h, gameMode,
(GLboolean)(parent ? GL_TRUE : GL_FALSE) );
return window;
}
/*
* Creates a new top-level freeglut window
*/
int FGAPIENTRY glutCreateWindow( const char* title )
{
/* XXX GLUT does not exit; it simply calls "glutInit" quietly if the
* XXX application has not already done so. The "freeglut" community
* XXX decided not to go this route (freeglut-developer e-mail from
* XXX Steve Baker, 12/16/04, 4:22 PM CST, "Re: [Freeglut-developer]
* XXX Desired 'freeglut' behaviour when there is no current window")
*/
FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutCreateWindow" );
return fgCreateWindow( NULL, title,
fgState.Position.Use, fgState.Position.X, fgState.Position.Y,
fgState.Size.Use, fgState.Size.X, fgState.Size.Y,
GL_FALSE, GL_FALSE )->ID;
}
_GLX_PUBLIC GLXFBConfig *
glXGetFBConfigs(Display * dpy, int screen, int *nelements)
{
struct glx_display *priv = __glXInitialize(dpy); // 死亡凝视
struct glx_config **config_list = NULL;
struct glx_config *config;
unsigned num_configs = 0;
int i;
*nelements = 0;
if (priv && (priv->screens != NULL)
&& (screen >= 0) && (screen < ScreenCount(dpy))
&& (priv->screens[screen]->configs != NULL)
&& (priv->screens[screen]->configs->fbconfigID
!= (int) GLX_DONT_CARE)) {
for (config = priv->screens[screen]->configs; config != NULL;
config = config->next) {
if (config->fbconfigID != (int) GLX_DONT_CARE) {
num_configs++;
}
}
config_list = malloc(num_configs * sizeof *config_list);
if (config_list != NULL) {
*nelements = num_configs;
i = 0;
for (config = priv->screens[screen]->configs; config != NULL;
config = config->next) {
if (config->fbconfigID != (int) GLX_DONT_CARE) {
config_list[i] = config;
i++;
}
}
}
}
return (GLXFBConfig *) config_list;
}
_GLX_PUBLIC GLXFBConfig *
glXChooseFBConfig(Display * dpy, int screen,
const int *attribList, int *nitems)
{
struct glx_config **config_list;
int list_size;
config_list = (struct glx_config **)
glXGetFBConfigs(dpy, screen, &list_size); // 死亡凝视
if ((config_list != NULL) && (list_size > 0) && (attribList != NULL)) {
list_size = choose_fbconfig(config_list, list_size, attribList);
if (list_size == 0) {
free(config_list);
config_list = NULL;
}
}
*nitems = list_size;
return (GLXFBConfig *) config_list;
}