glxSwapBuffers does not take effect unless window is resized (bad WM graphics driver?)

I was able to get my code that displays a CPU framebuffer using OpenGL working on Windows, but it seems like glxSwapBuffer and wglSwapBuffer somehow act entirely different. The Windows version acts as it should, the Linux version hangs until window is resized.

Is it that I need to handle something x11 specific in my code? Or is the WMware driver for Linux Mint problematic?

Update: it’s likely the fact that I need to handle something extra in my code, or my event mask is bad (there’s zero documentation on what you should be handling, because jUSt UsE GLfW).

That would be my guess. Both the Windoze GDI and the Linux X calls can be pretty quirky to get right. There’s not zero docs. But this is 20 year old stuff so unlikely to just be listed up-top in your websearch results. You have to know where to look.

First, I’d ditch the VM (or at least put it off to the side) and render direct to the local display / X server with a decent graphics driver (e.g. NVIDIA). Who knows what odd quirks the VM might be introducing. Defer that and figure it out later.

Second, I had this “native X” path working on Linux years ago and used it for test progs when GLUT wasn’t enough. Try the attached simple test app (uses X and glX, not GLUT, GLFW, or some other abstraction layer).

Also, to see what you might be missing (for Windows or Linux), just look in some library like FreeGLUT, GLFW, etc. and see what they’re doing. It’s often some subtle API usage thing you just need to copy.

//------------------------------------------------------------------------------
// glx_simple_new - Shows how to create an X window and a new-style 
//   GL context without GLUT or any helper libraries.
//
//   COMPILE WITH:
//       g++ -g -o glx_simple_new glx_simple_new.cxx -lGLU -lGL -lX11

//------------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/time.h>
#define GL_GLEXT_PROTOTYPES
#define GLX_GLXEXT_PROTOTYPES
#include <GL/gl.h>
//#include <GL/glu.h>
#include <GL/glx.h>

struct MyWin
{
  Display  *display;
  Window    win;
  bool      displayed;
  int       width;
  int       height;
};

//----------------------------------------------------------------------------

const int   WIN_XPOS    = 256;
const int   WIN_YPOS    = 64;
const int   WIN_XRES    = 320;
const int   WIN_YRES    = 320;
const int   NUM_SAMPLES = 4;

//----------------------------------------------------------------------------

MyWin        Win;

//----------------------------------------------------------------------------

double elapsedMsec( const struct timeval &start, const struct timeval &stop )
{
  return ( ( stop.tv_sec  - start.tv_sec  ) * 1000.0 +
           ( stop.tv_usec - start.tv_usec ) / 1000.0 );
}

//----------------------------------------------------------------------------

double elapsedUsec( const struct timeval &start, const struct timeval &stop )
{
  return ( ( stop.tv_sec  - start.tv_sec  ) * 1000000.0 +
           ( stop.tv_usec - start.tv_usec ) );
}

//-----------------------------------------------------------------------------

/// check() - Check for GL errors, and report any queued

void check( const char hdr[] = "" )
{
  int err;

  while ( ( err = glGetError() ) != GL_NO_ERROR )
//    fprintf( stderr, "OpenGL Error at %s: %s\n", hdr, gluErrorString(err) );
    fprintf( stderr, "OpenGL Error at %s: 0x%x\n", hdr, err );
}

//----------------------------------------------------------------------------

void displayCB()
{
  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

  //---------------------------------------------------
  // FIXME: Insert GL draw code here
  //---------------------------------------------------

  // Display it
  glXSwapBuffers( Win.display, Win.win );

  check( "displayCB()" );
}

//----------------------------------------------------------------------------

void keyboardCB( KeySym sym, unsigned char key, int x, int y,
                 bool &setting_change )
{
  switch ( tolower( key ) )
  {
    case 27:
      // ESCape - We're done!
      exit (0);
      break;

    case 'k':
      printf( "You hit the 'k' key\n" );
      break;

    case 0:
      switch ( sym )
      {
        case XK_Left  : 
          printf( "You hit the Left Arrow key\n" );
          break;

        case XK_Right :
          printf( "You hit the Right Arrow key\n" );
          break;
      }
      break;
  }
}

//----------------------------------------------------------------------------

void reshapeCB( int width, int height )
{
  Win.width = width;
  Win.height = height;
}

//----------------------------------------------------------------------------

void queryXandGLX( Display *dpy, int screen )
{
  int major = 0;
  int minor = 0;
  int errorbase = 0;
  int eventbase = 0;
  int error;
 
  error = glXQueryVersion (dpy, &major, &minor);
  error = glXQueryExtension (dpy, &errorbase, &eventbase);
  const char* extensions       = glXQueryExtensionsString (dpy, screen);
  const char* clientvendor     = glXGetClientString (dpy, GLX_VENDOR);
  const char* clientversion    = glXGetClientString (dpy, GLX_VERSION);
  const char* clientextensions = glXGetClientString (dpy, GLX_EXTENSIONS);
  const char* servervendor     = glXQueryServerString (dpy, screen, GLX_VENDOR);
  const char* serverversion    = glXQueryServerString (dpy, screen, GLX_VERSION);
  const char* serverextensions = glXQueryServerString (dpy, screen, GLX_EXTENSIONS);
 
  printf( "--------------------------------------\n\n" );
  printf ("GLX version          ::: major.minor == %d.%d\n", major, minor);
  printf ("GLX offsets          ::: errorbase : eventbase == %d %d\n", 
          errorbase, eventbase);
  printf ("GLX extensions       ::: <%s>\n", extensions);
  printf ("GLX clientvendor     ::: <%s>\n", clientvendor);
  printf ("GLX clientversion    ::: <%s>\n", clientversion);
  printf ("GLX clientextensions ::: <%s>\n", clientextensions);
  printf ("GLX servervendor     ::: <%s>\n", servervendor);
  printf ("GLX serverversion    ::: <%s>\n", serverversion);
  printf ("GLX serverextensions ::: <%s>\n", serverextensions);
  printf( "\n--------------------------------------\n\n" );
}


//----------------------------------------------------------------------------

/** chooseFBConfig() - Try to find a framebuffer config that matches
 *    the specified pixel requirements.
 */

GLXFBConfig chooseFBConfig( Display *display, int screen )
{
  // Default template
  static const int Visual_attribs[] =
    {
      GLX_X_RENDERABLE    , True,
      GLX_DRAWABLE_TYPE   , GLX_WINDOW_BIT,
      GLX_RENDER_TYPE     , GLX_RGBA_BIT,
      GLX_X_VISUAL_TYPE   , GLX_TRUE_COLOR,
      GLX_RED_SIZE        , 8,
      GLX_GREEN_SIZE      , 8,
      GLX_BLUE_SIZE       , 8,
      GLX_ALPHA_SIZE      , 8,
      GLX_DEPTH_SIZE      , 24,
      GLX_STENCIL_SIZE    , 8,
      GLX_DOUBLEBUFFER    , True,
      GLX_SAMPLE_BUFFERS  , 1,
      GLX_SAMPLES         , 4,
      None
    };

  int attribs [ 100 ] ;
  memcpy( attribs, Visual_attribs, sizeof( Visual_attribs ) );

  // DELETED

  GLXFBConfig ret = 0;

  int fbcount;
  GLXFBConfig *fbc = glXChooseFBConfig( display, screen,
                                        attribs, &fbcount );
  if ( fbc )
  {
    if ( fbcount >= 1 )
      ret = fbc[0];

    XFree( fbc );
  }

  return ret;
}

//----------------------------------------------------------------------------

GLXContext createContext( Display *display, int screen,
                          GLXFBConfig fbconfig, XVisualInfo *visinfo, 
                          Window window )
{
#define GLX_CONTEXT_MAJOR_VERSION_ARB       0x2091
#define GLX_CONTEXT_MINOR_VERSION_ARB       0x2092
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);

  // Verify GL driver supports glXCreateContextAttribsARB()
  //   Create an old-style GLX context first, to get the correct function ptr.
  glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;

  GLXContext ctx_old = glXCreateContext( display, visinfo, 0, True );
  if ( !ctx_old ) 
  {
    printf( "Could not even allocate an old-style GL context!\n" );
    exit(1);
  }

  glXMakeCurrent ( display, window, ctx_old ) ;

  // Verify that GLX implementation supports the new context create call
  if ( strstr( glXQueryExtensionsString( display, screen ), 
               "GLX_ARB_create_context" ) != 0 )
    glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
      glXGetProcAddress( (const GLubyte *) "glXCreateContextAttribsARB" );

  if ( !glXCreateContextAttribsARB )
  {
    printf( "Can't create new-style GL context\n" );
    exit(1);
  }

  // Got the pointer.  Nuke old context.
  glXMakeCurrent( display, None, 0 );
  glXDestroyContext( display, ctx_old );

  // Try to allocate a GL 4.3 COMPATIBILITY context
  static int Context_attribs[] =
  {
    GLX_CONTEXT_MAJOR_VERSION_ARB, 4,
    GLX_CONTEXT_MINOR_VERSION_ARB, 3,
    GLX_CONTEXT_PROFILE_MASK_ARB , GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
    //GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
    //GLX_CONTEXT_FLAGS_ARB       , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
    //GLX_CONTEXT_FLAGS_ARB       , GLX_CONTEXT_DEBUG_BIT_ARB,
    None
  };

  GLXContext context = glXCreateContextAttribsARB( display, fbconfig, 0, 
                                                   True, Context_attribs );

  // Forcably wait on any resulting X errors
  XSync( display, False );

  if ( !context )
  {
    printf( "Failed to allocate a GL 4.3 context\n" );
    exit(1);
  }
  
  printf( "Created GL 4.3 context\n" );

  return context;
}

//----------------------------------------------------------------------------

void createWindow()
{
  // Init X and GLX
  Win.displayed = false;
  Display *display = Win.display = XOpenDisplay( ":0.0" );
  if ( !display )
    printf( "Cannot open X display\n" );

  int    screen   = DefaultScreen( display );
  Window root_win = RootWindow( display, screen );

  if ( !glXQueryExtension( display, 0, 0 ) )
    printf( "X Server doesn't support GLX extension\n" );

  // Query X and GLX
  //queryXandGLX( display, screen );

  // Pick an FBconfig and visual
  GLXFBConfig fbconfig = chooseFBConfig( display, screen );
  if ( !fbconfig )
  {
    printf( "Failed to get GLXFBConfig\n" );
    exit(1);
  }

  XVisualInfo *visinfo = glXGetVisualFromFBConfig( display, fbconfig );
  if ( !visinfo )
  {
    printf( "Failed to get XVisualInfo\n" );
    exit(1);
  }
  printf( "X Visual ID = 0x%.2x\n", int( visinfo->visualid ) );

  // Create the X window
  XSetWindowAttributes winAttr ;

  winAttr.event_mask = StructureNotifyMask | KeyPressMask ;
  winAttr.background_pixmap = None ;
  winAttr.background_pixel  = 0    ;
  winAttr.border_pixel      = 0    ;

  winAttr.colormap = XCreateColormap( display, root_win,
                                      visinfo->visual, AllocNone );
  
  unsigned int mask = CWBackPixmap | CWBorderPixel | CWColormap | CWEventMask;

  Window win = Win.win = XCreateWindow ( display, root_win, 
                                         WIN_XPOS, WIN_YPOS, 
                                         WIN_XRES, WIN_YRES, 0, 
                                         visinfo->depth, InputOutput, 
                                         visinfo->visual, mask, &winAttr ) ;

  XStoreName( Win.display, win, "My GLX Window");

  // Create an OpenGL context and attach it to our X window
  GLXContext context = createContext( display, screen, fbconfig, visinfo, win );

  if ( ! glXMakeCurrent( display, win, context ) )
    printf( "glXMakeCurrent failed.\n" );

  if ( ! glXIsDirect ( display, glXGetCurrentContext() ) )
    printf( "Indirect GLX rendering context obtained\n" );

  // Display the window
  XMapWindow( display, win );

  if ( ! glXMakeCurrent( display, win, context ) )
    printf( "glXMakeCurrent failed.\n" );

  check( "createWindow()" );

  printf( "Window Size    = %d x %d\n", WIN_XRES, WIN_YRES );
  printf( "Window Samples = %d\n", NUM_SAMPLES );
}

//----------------------------------------------------------------------------

void processXEvents( Atom wm_protocols, Atom wm_delete_window )
{
  bool setting_change = false;

  while ( XEventsQueued( Win.display, QueuedAfterFlush ) )
  {
    XEvent    event;
  
    XNextEvent( Win.display, &event );

    if( event.xany.window != Win.win )
      continue;

    switch ( event.type )
    {
      case MapNotify:
        {
          Win.displayed = true;
          break;
        }
      case ConfigureNotify:
        {
          XConfigureEvent &cevent = event.xconfigure;
          reshapeCB( cevent.width, cevent.height );
          break;
        }
      case KeyPress:
        {
          char      chr;
          KeySym    symbol;
          XComposeStatus status;
          
          XLookupString( &event.xkey, &chr, 1, &symbol, &status );

          keyboardCB( symbol, chr, event.xkey.x, event.xkey.y,
                      setting_change );
          break;
        }
      case ClientMessage:
        {
          if ( event.xclient.message_type      == wm_protocols &&
               Atom( event.xclient.data.l[0] ) == wm_delete_window )
          {
            //printf( "Received WM_DELETE_WINDOW\n" );
            exit(0);
          }
          break;
        }
    }
  }
}

//----------------------------------------------------------------------------

void mainLoop()
{
  // Register to receive window close events (the "X" window manager button)
  Atom wm_protocols     = XInternAtom( Win.display, "WM_PROTOCOLS"    , False);
  Atom wm_delete_window = XInternAtom( Win.display, "WM_DELETE_WINDOW", False);
  XSetWMProtocols( Win.display, Win.win, &wm_delete_window, True );

  while (1) 
  {
    // Redraw window (after it's mapped)
    if ( Win.displayed )
      displayCB();

    // Update frame rate
    static timeval last_xcheck   = {0,0};
    struct timeval now;
    gettimeofday( &now, 0 );
    
    // Check X events every 1/10 second
    if ( elapsedMsec( last_xcheck, now ) > 100 )
    {
      processXEvents( wm_protocols, wm_delete_window );
      last_xcheck = now;
    }
  }
}

//----------------------------------------------------------------------------

int main( int argc, char *argv[] )
{
  // Init globals
  Win.width = WIN_XRES, Win.height = WIN_YRES;

  // Create context and window
  createWindow();

  // Init OpenGL
  glViewport  ( 0, 0, Win.width, Win.height ); 
  glColorMask ( 1,1,1,1 );
  glClearColor( 0,0,1,1 );
  glClear     ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

  // Go
  printf( "Valid keys: Left, Right, k, ESC\n" );
  printf( "Press ESC to quit\n" );

  mainLoop();

  return 0;
}