freeglut GameMode behaviour

There is not a great deal of solid documentation to be found about Glut’s
GameMode. I found the following on the web but I wonder if it is a reliable
explanation of how GameMode should function? Most examples merely show the
application entering GameMode and never returning. I have an application
that needs to go full-screen and also return to decorated window mode. I’m
working with freeglut 2.4.0 on both Win XP and Linux Ubuntu and with a
variety of graphics hardware Nvidia 8800 to Intel 945GM. I get erratic
behaviour – sometimes the return from GameMode has the video driver very
confused and a system has to be restarted – on another platform the return
takes several seconds.

–quote:
When game mode is entered, certain GLUT functionality is disable to
facilitate high-performance fullscreen rendering. GLUT pop-up menus are not
available while in game mode. Other created windows and subwindows are not
displayed in GLUT game mode. Game mode will also hide all other applications
running on the computer’s display screen. The intent of these restrictions
is to eliminate window clipping issues, permit screen display format
changes, and permit fullscreen rendering optimization such as page flipping
for fullscreen buffer swaps.

The value returned by glutEnterGameMode is a unique small integer identifier
for the fullscreen window. The range of allocated identifiers starts at one.
This window identifier can be used when calling glutSetWindow.

After leaving game mode, the GLUT functionality disabled in game mode is
available again. The game mode window (and its OpenGL rendering state) is
destroyed when leaving game mode. Any windows and subwindows created before
entering the game mode are displayed in their previous locations. The OpenGL
state of normal GLUT windows and subwindows is not disturbed by entering
and/or leaving game mode.
–unquote

Here’s a code fragment with my two functions the first to switch to full
screen the second to return. I had a problem with just entering and leaving
GameMode so I explicitly destroy the current window before entering GameMode
and I explicitly create a new window after returning from GameMode. My
application only has one window. The callbacks are re-registered after
entering GameMode and whenever a new window is created.

If I knew the design intent I could write explicitly for that and then start
debugging the particular OS and hardware issues that may be present.

Clive McCarthy

/*------------------------------------------------------------------------------

------------------------------------------------------------------------------*/
void switch_to_full_screen(void)
{
char mode_string[24];

full_screen = TRUE;

sprintf(mode_string, “%dx%d:32@60”, glutGet(GLUT_SCREEN_WIDTH),
glutGet(GLUT_SCREEN_HEIGHT));
glutGameModeString(mode_string);
if(glutGameModeGet(GLUT_GAME_MODE_POSSIBLE))
{
printf("GameMode %s is possible
", mode_string);
glutDestroyWindow(glut_pane_id); // destroys the current graphics window
glutEnterGameMode();
glut_OpenGL_call_backs();
glutSetCursor(GLUT_CURSOR_NONE); // hide the cursor
}
else
{
printf("GameMode %s NOT possible
", mode_string);
glutFullScreen();
}

#ifdef UBUNTU_PORT
system(“wmctrl -r"FourteenthStreetStudio” -b
add,fullscreen,skip_taskbar");
#endif

if(glutGameModeGet(GLUT_GAME_MODE_ACTIVE))
{
printf("GameMode is now active…
");
}
else
{
//printf("GameMode is NOT active
");
}

OpenGL_error_check(FILE, LINE);
glutPostRedisplay();
}
/*------------------------------------------------------------------------------
set the window’s width and position for debugging

Window managers (including gnome)
adhere to a standard interface, the EWMH/NetWM specification.
This allows programs that remote-control their functionality.
And fortunately, someone already has written one called “wmctrl”.
------------------------------------------------------------------------------*/
#ifdef FREEGLUT
#define WINDOW_X_ORIGIN 0
#define WINDOW_Y_ORIGIN 0
#else
#define WINDOW_X_ORIGIN 4
#define WINDOW_Y_ORIGIN 24
#endif
void set_debug_window_size(void)
{
int screen_width, screen_length;

full_screen = FALSE;

if(glutGameModeGet(GLUT_GAME_MODE_ACTIVE))
{
printf("returning from GameMode
");
glutLeaveGameMode();
glut_pane_id = glutCreateWindow(“FourteenthStreetStudio”);
glut_OpenGL_call_backs();
}

#ifdef UBUNTU_PORT
system(“wmctrl -r"FourteenthStreetStudio” -b
remove,fullscreen,skip_taskbar");
#endif

screen_width = glutGet(GLUT_SCREEN_WIDTH);
screen_length = glutGet(GLUT_SCREEN_HEIGHT);

switch(screen_width)
{
case 3840: // dual screen WUXGA
glutPositionWindow(WINDOW_X_ORIGIN + (screen_width / 2),
WINDOW_Y_ORIGIN);
glutReshapeWindow(1824, 1140); // 16:10
break;

case 3200: // dual screen UXGA
glutPositionWindow(WINDOW_X_ORIGIN + (screen_width / 2),
WINDOW_Y_ORIGIN);
glutReshapeWindow (1584, 990); // 16:10
break;

etc. etc.

I tried your code with a few little changes and it worked fine for me. The changes that I made were to remove the system calls and to call init() along with the glut_OpenGL_call_backs() each time. I changed the code so it toggles between fullscreen and windowed mode with any key press (except esc which exits the program). Note I ran the following code on Ubuntu 9.04+GNOME window manager with freeglut. I would be curious if you can compile and run it without problems.


// http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=263588#Post263588
#include <cstdio>
#include <cstdlib>
#include <GL/glut.h>

void display(void);
void init(void);
void set_debug_window_size(void);
void switch_to_full_screen(void);
#define TRUE true
#define FALSE false
bool full_screen = FALSE;
int glut_pane_id = 0;
void SampleGameModeKeyboard( unsigned char cChar, int nMouseX, int nMouseY )
{
  static int toggleMode=true;
  if( cChar == 27 ) exit(0);

  if (toggleMode)
  {
    set_debug_window_size();
    toggleMode=!toggleMode;
  }
  else
  {
    switch_to_full_screen();
    toggleMode=!toggleMode;
  }
}


void glut_OpenGL_call_backs()
{
  init();
  glutKeyboardFunc( SampleGameModeKeyboard );
  glutDisplayFunc(display);
};

void switch_to_full_screen(void)
{
  char mode_string[24];

  full_screen = TRUE;

  sprintf(mode_string, "%dx%d:32@60", glutGet(GLUT_SCREEN_WIDTH),
  glutGet(GLUT_SCREEN_HEIGHT));
  glutGameModeString(mode_string);
  if(glutGameModeGet(GLUT_GAME_MODE_POSSIBLE))
  {
    printf("GameMode %s is possible
", mode_string);
                                 // destroys the current graphics window
    glutDestroyWindow(glut_pane_id);
    glutEnterGameMode();
    glut_OpenGL_call_backs();
                                 // hide the cursor
    glutSetCursor(GLUT_CURSOR_NONE);
  }
  else
  {
    printf("GameMode %s NOT possible
", mode_string);
    glutFullScreen();
  }

  #ifdef UBUNTU_PORT
  //system("wmctrl -r\"FourteenthStreetStudio\" -b add,fullscreen,skip_taskbar");
  #endif

  if(glutGameModeGet(GLUT_GAME_MODE_ACTIVE))
  {
    printf("GameMode is now active...
");
  }
  else
  {
    //printf("GameMode is NOT active
");
  }

}


/*------------------------------------------------------------------------------
set the window's width and position for debugging

Window managers (including gnome)
adhere to a standard interface, the EWMH/NetWM specification.
This allows programs that remote-control their functionality.
And fortunately, someone already has written one called "wmctrl".
------------------------------------------------------------------------------*/
#ifdef __FREEGLUT__
#define WINDOW_X_ORIGIN 0
#define WINDOW_Y_ORIGIN 0
#else
#define WINDOW_X_ORIGIN 4
#define WINDOW_Y_ORIGIN 24
#endif
void set_debug_window_size(void)
{
  int screen_width, screen_length;

  full_screen = FALSE;

  if(glutGameModeGet(GLUT_GAME_MODE_ACTIVE))
  {
    printf("returning from GameMode
");
    glutLeaveGameMode();
    glut_pane_id = glutCreateWindow("FourteenthStreetStudio");
    glut_OpenGL_call_backs();
  }

  #ifdef UBUNTU_PORT
  //system("wmctrl -r\"FourteenthStreetStudio\" -b remove,fullscreen,skip_taskbar");
  #endif

  /*screen_width = glutGet(GLUT_SCREEN_WIDTH);
  screen_length = glutGet(GLUT_SCREEN_HEIGHT);

  switch(screen_width)
  {
  case 3840: // dual screen WUXGA
  glutPositionWindow(WINDOW_X_ORIGIN + (screen_width / 2),
  WINDOW_Y_ORIGIN);
  glutReshapeWindow(1824, 1140); // 16:10
  break;

  case 3200: // dual screen UXGA
  glutPositionWindow(WINDOW_X_ORIGIN + (screen_width / 2),
  WINDOW_Y_ORIGIN);
  glutReshapeWindow (1584, 990); // 16:10
  break;

  //etc
  }
  */
}


// following modified from http://www.opengl.org/resources/code/samples/glut_examples/examples/cube.c
/* Copyright (c) Mark J. Kilgard, 1997. */

/* This program is freely distributable without licensing fees
   and is provided without guarantee or warrantee expressed or
   implied. This program is -not- in the public domain. */

/* This program was requested by Patrick Earl; hopefully someone else
   will write the equivalent Direct3D immediate mode program. */

#include <GL/glut.h>

GLfloat light_diffuse[] =        /* Red diffuse light. */
{
  1.0, 0.0, 0.0, 1.0
};
GLfloat light_position[] =       /* Infinite light location. */
{
  1.0, 1.0, 1.0, 0.0
};
GLfloat n[6][3] =                /* Normals for the 6 faces of a cube. */
{
  {-1.0, 0.0, 0.0},
  { 0.0, 1.0, 0.0 } ,
  { 1.0, 0.0, 0.0 },
  {0.0, -1.0, 0.0},
  { 0.0, 0.0, 1.0 } ,
  { 0.0, 0.0, -1.0 }
};
GLint faces[6][4] =              /* Vertex indices for the 6 faces of a cube. */
{
  {0, 1, 2, 3},
  { 3, 2, 6, 7 } ,
  { 7, 6, 5, 4 },
  {4, 5, 1, 0},
  { 5, 6, 2, 1 } ,
  { 7, 4, 0, 3 }
};
GLfloat v[8][3];                 /* Will be filled in with X,Y,Z vertexes. */

void
drawBox(void)
{
  int i;

  for (i = 0; i < 6; i++)
  {
    glBegin(GL_QUADS);
    glNormal3fv(&n[i][0]);
    glVertex3fv(&v[faces[i][0]][0]);
    glVertex3fv(&v[faces[i][1]][0]);
    glVertex3fv(&v[faces[i][2]][0]);
    glVertex3fv(&v[faces[i][3]][0]);
    glEnd();
  }
}


void
display(void)
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  drawBox();
  glutSwapBuffers();
}


void
init(void)
{
  /* Setup cube vertex data. */
  v[0][0] = v[1][0] = v[2][0] = v[3][0] = -1;
  v[4][0] = v[5][0] = v[6][0] = v[7][0] = 1;
  v[0][1] = v[1][1] = v[4][1] = v[5][1] = -1;
  v[2][1] = v[3][1] = v[6][1] = v[7][1] = 1;
  v[0][2] = v[3][2] = v[4][2] = v[7][2] = 1;
  v[1][2] = v[2][2] = v[5][2] = v[6][2] = -1;

  /* Enable a single OpenGL light. */
  glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
  glLightfv(GL_LIGHT0, GL_POSITION, light_position);
  glEnable(GL_LIGHT0);
  glEnable(GL_LIGHTING);

  /* Use depth buffering for hidden surface elimination. */
  glEnable(GL_DEPTH_TEST);

  /* Setup the view of the cube. */
  glMatrixMode(GL_PROJECTION);
  gluPerspective( /* field of view in degree */ 40.0,
    /* aspect ratio */ 1.0,
    /* Z near */ 1.0, /* Z far */ 10.0);
  glMatrixMode(GL_MODELVIEW);
  gluLookAt(0.0, 0.0, 5.0,       /* eye is at (0,0,5) */
    0.0, 0.0, 0.0,               /* center is at (0,0,0) */
    0.0, 1.0, 0.);               /* up is in positive Y direction */

  /* Adjust cube position to be asthetic angle. */
  glTranslatef(0.0, 0.0, -1.0);
  glRotatef(60, 1.0, 0.0, 0.0);
  glRotatef(-20, 0.0, 0.0, 1.0);
}


int
main(int argc, char **argv)
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  glut_pane_id=glutCreateWindow("red 3D lighted cube");
  switch_to_full_screen();
  glutMainLoop();
  return 0;                      /* ANSI C requires main to return int. */
}

At long last I can confirm that the changes you suggest work just fine. I placed calls to re-initialize the rendering mode I use immediately after the calls to glut_OpenGL_call_backs() so that the newly created display mode and window are correctly set. Your code does this implicitly by calling init() within the glut_OpenGL_call_backs() registration.

In theory it should not be necessary to destroy the current window before entering GameMode but it becomes necessary because there is no ‘valid’ window when GameMode is exited in freeglut 2.4.0. So a new window has to be created, which means one has to be destroyed. I think the 2.6.0 release of freeglut may fix this.

Oh, and thank you!