当前位置: 首页 > 工具软件 > Freeglut > 使用案例 >

FreeGLUT

蔡鹏程
2023-12-01


前言

本文主要是为了分析FreeGLUT与X11、GLX之间的关系,从最终用户的角度分析Mesa的初始化过程。
本文所使用的的FreeGLUT版本是3.2.1.

调用图

main --> glutCreateWindow --> fgCreateWindow
	--> fgOpenWindow --> fgPlatformOpenWindow --> fghChooseConfig
	--> glXChooseFBConfig --> glXGetFBConfigs --> __glXInitialize

源码解析

[mesa]demos/src/demos/gears.c

  • main
    • |–> glutInit
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. */
}

src\fg_init.c

  • glutInit
    • |–> fgPlatformInitialize
/*
 * 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;
    }
}

src\x11\fg_init_x11.c

  • fgPlatformInitialize
    • |–> XOpenDisplay
    • |–> glXQueryExtension
/*
 * 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();
}

src\x11\fg_window_x11_glx.c

  • fghChooseConfig
    • |–> glXChooseFBConfig
/*
 * 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;
    }
}

src\x11\fg_state_x11_glx.c

  • fghPlatformGlutGetGLX
    • |–> fghChooseConfig
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;
}

src\x11\fg_window_x11.c

  • fgPlatformOpenWindow
    • |–> fghChooseConfig
/*
 * 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); // 死亡凝视

src\fg_window.c

  • fgOpenWindow
    • |–> fgPlatformOpenWindow
*
 * 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;
}

src\fg_structure.c

  • fgCreateWindow
    • |–> fgOpenWindow
/*
 * 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;
}

src\fg_window.c

/*
 * 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;
}

[mesa]src/glx/glxcmds.c

  • glXChooseFBConfig
    • |–> glXGetFBConfigs
      • |–> __glXInitialize
_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;
}

相关阅读

相关文章

相关问答