Adding further comments to the removal of the "atexit" call from the Windows build...
[freeglut] / src / freeglut_init.c
index 6b6f3d5..5b5771b 100644 (file)
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
+#define FREEGLUT_BUILDING_LIB
 #include <GL/freeglut.h>
 #include "freeglut_internal.h"
 
+#if TARGET_HOST_POSIX_X11
+#include <limits.h>  /* LONG_MAX */
+#endif
+
 /*
  * TODO BEFORE THE STABLE RELEASE:
  *
@@ -64,16 +65,12 @@ SFG_State fgState = { { -1, -1, GL_FALSE },  /* Position */
                       GL_FALSE,              /* UseCurrentContext */
                       GL_FALSE,              /* GLDebugSwitch */
                       GL_FALSE,              /* XSyncSwitch */
-                      GL_TRUE,               /* KeyRepeat */
-                      0xffffffff,            /* Modifiers */
+                      GLUT_KEY_REPEAT_ON,    /* KeyRepeat */
+                      INVALID_MODIFIERS,     /* Modifiers */
                       0,                     /* FPSInterval */
                       0,                     /* SwapCount */
                       0,                     /* SwapTime */
-#if TARGET_HOST_WIN32 || TARGET_HOST_WINCE
-                      { 0, GL_FALSE },       /* Time */
-#else
-                      { { 0, 0 }, GL_FALSE },
-#endif
+                      0,                     /* Time */
                       { NULL, NULL },         /* Timers */
                       { NULL, NULL },         /* FreeTimers */
                       NULL,                   /* IdleCallback */
@@ -86,18 +83,167 @@ SFG_State fgState = { { -1, -1, GL_FALSE },  /* Position */
                       GLUT_ACTION_EXIT,       /* ActionOnWindowClose */
                       GLUT_EXEC_STATE_INIT,   /* ExecState */
                       NULL,                   /* ProgramName */
-                      GL_FALSE                /* JoysticksInitialised */
+                      GL_FALSE,               /* JoysticksInitialised */
+                      0,                      /* NumActiveJoysticks */
+                      GL_FALSE,               /* InputDevsInitialised */
+                      0,                      /* MouseWheelTicks */
+                      1,                      /* AuxiliaryBufferNumber */
+                      4,                      /* SampleNumber */
+                      1,                      /* MajorVersion */
+                      0,                      /* MinorVersion */
+                      0,                      /* ContextFlags */
+                      0,                      /* ContextProfile */
+                      NULL,                   /* ErrorFunc */
+                      NULL                    /* WarningFunc */
 };
 
 
 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
 
+#if TARGET_HOST_POSIX_X11
+
+/* Return the atom associated with "name". */
+static Atom fghGetAtom(const char * name)
+{
+  return XInternAtom(fgDisplay.Display, name, False);
+}
+
+/*
+ * Check if "property" is set on "window".  The property's values are returned
+ * through "data".  If the property is set and is of type "type", return the
+ * number of elements in "data".  Return zero otherwise.  In both cases, use
+ * "Xfree()" to free "data".
+ */
+static int fghGetWindowProperty(Window window,
+                               Atom property,
+                               Atom type,
+                               unsigned char ** data)
+{
+  /*
+   * Caller always has to use "Xfree()" to free "data", since
+   * "XGetWindowProperty() always allocates one extra byte in prop_return
+   * [i.e. "data"] (even if the property is zero length) [..]".
+   */
+
+  int status;  /*  Returned by "XGetWindowProperty". */
+
+  Atom          type_returned;
+  int           temp_format;             /*  Not used. */
+  unsigned long number_of_elements;
+  unsigned long temp_bytes_after;        /*  Not used. */
+
+
+  status = XGetWindowProperty(fgDisplay.Display,
+                             window,
+                             property,
+                             0,
+                             LONG_MAX,
+                             False,
+                             type,
+                             &type_returned,
+                             &temp_format,
+                             &number_of_elements,
+                             &temp_bytes_after,
+                             data);
+
+  FREEGLUT_INTERNAL_ERROR_EXIT(status == Success,
+                              "XGetWindowProperty failled",
+                              "fghGetWindowProperty");
+
+  if (type_returned != type)
+    {
+      number_of_elements = 0;
+    }
+
+  return number_of_elements;
+}
+
+/*  Check if the window manager is NET WM compliant. */
+static int fghNetWMSupported(void)
+{
+  Atom wm_check;
+  Window ** window_ptr_1;
+
+  int number_of_windows;
+  int net_wm_supported;
+
+
+  net_wm_supported = 0;
+
+  wm_check = fghGetAtom("_NET_SUPPORTING_WM_CHECK");
+  window_ptr_1 = malloc(sizeof(Window *));
+
+  /*
+   * Check that the window manager has set this property on the root window.
+   * The property must be the ID of a child window.
+   */
+  number_of_windows = fghGetWindowProperty(fgDisplay.RootWindow,
+                                           wm_check,
+                                           XA_WINDOW,
+                                           (unsigned char **) window_ptr_1);
+  if (number_of_windows == 1)
+    {
+      Window ** window_ptr_2;
+
+      window_ptr_2 = malloc(sizeof(Window *));
+
+      /* Check that the window has the same property set to the same value. */
+      number_of_windows = fghGetWindowProperty(**window_ptr_1,
+                                               wm_check,
+                                               XA_WINDOW,
+                                               (unsigned char **) window_ptr_2);
+      if ((number_of_windows == 1) && (**window_ptr_1 == **window_ptr_2))
+      {
+        /* NET WM compliant */
+        net_wm_supported = 1;
+      }
+
+      XFree(*window_ptr_2);
+      free(window_ptr_2);
+    }
+
+        XFree(*window_ptr_1);
+        free(window_ptr_1);
+
+        return net_wm_supported;
+}
+
+/*  Check if "hint" is present in "property" for "window". */
+int fgHintPresent(Window window, Atom property, Atom hint)
+{
+  Atom *atoms;
+  int number_of_atoms;
+  int supported;
+  int i;
+
+  supported = 0;
+
+  number_of_atoms = fghGetWindowProperty(window,
+                                        property,
+                                        XA_ATOM,
+                                        (unsigned char **) &atoms);
+  for (i = 0; i < number_of_atoms; i++)
+  {
+      if (atoms[i] == hint)
+      {
+          supported = 1;
+          break;
+      }
+  }
+
+  XFree(atoms);
+  return supported;
+}
+
+#endif /*  TARGET_HOST_POSIX_X11  */
+
+
 /*
  * A call to this function should initialize all the display stuff...
  */
 static void fghInitialize( const char* displayName )
 {
-#if TARGET_HOST_UNIX_X11
+#if TARGET_HOST_POSIX_X11
     fgDisplay.Display = XOpenDisplay( displayName );
 
     if( fgDisplay.Display == NULL )
@@ -134,20 +280,41 @@ static void fghInitialize( const char* displayName )
     fgDisplay.Connection = ConnectionNumber( fgDisplay.Display );
 
     /* Create the window deletion atom */
-    fgDisplay.DeleteWindow = XInternAtom(
-        fgDisplay.Display,
-        "WM_DELETE_WINDOW",
-        FALSE
-    );
+    fgDisplay.DeleteWindow = fghGetAtom("WM_DELETE_WINDOW");
+
+    /* Create the state and full screen atoms */
+    fgDisplay.State           = None;
+    fgDisplay.StateFullScreen = None;
+
+    if (fghNetWMSupported())
+    {
+      const Atom supported = fghGetAtom("_NET_SUPPORTED");
+      const Atom state     = fghGetAtom("_NET_WM_STATE");
+      
+      /* Check if the state hint is supported. */
+      if (fgHintPresent(fgDisplay.RootWindow, supported, state))
+      {
+        const Atom full_screen = fghGetAtom("_NET_WM_STATE_FULLSCREEN");
+        
+        fgDisplay.State = state;
+        
+        /* Check if the window manager supports full screen. */
+        /**  Check "_NET_WM_ALLOWED_ACTIONS" on our window instead? **/
+        if (fgHintPresent(fgDisplay.RootWindow, supported, full_screen))
+        {
+          fgDisplay.StateFullScreen = full_screen;
+        }
+      }
+    }
 
-#elif TARGET_HOST_WIN32 || TARGET_HOST_WINCE
+#elif TARGET_HOST_MS_WINDOWS
 
     WNDCLASS wc;
     ATOM atom;
 
     /* What we need to do is to initialize the fgDisplay global structure here. */
     fgDisplay.Instance = GetModuleHandle( NULL );
-
+    fgDisplay.DisplayName= displayName ? strdup(displayName) : 0 ;
     atom = GetClassInfo( fgDisplay.Instance, _T("FREEGLUT"), &wc );
 
     if( atom == 0 )
@@ -168,12 +335,12 @@ static void fghInitialize( const char* displayName )
         wc.hInstance      = fgDisplay.Instance;
         wc.hIcon          = LoadIcon( fgDisplay.Instance, _T("GLUT_ICON") );
 
-#if TARGET_HOST_WIN32
+#if defined(_WIN32_WCE)
+        wc.style          = CS_HREDRAW | CS_VREDRAW;
+#else
         wc.style          = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
         if (!wc.hIcon)
           wc.hIcon        = LoadIcon( NULL, IDI_WINLOGO );
-#else /* TARGET_HOST_WINCE */
-        wc.style          = CS_HREDRAW | CS_VREDRAW;
 #endif
 
         wc.hCursor        = LoadCursor( NULL, IDC_ARROW );
@@ -183,7 +350,7 @@ static void fghInitialize( const char* displayName )
 
         /* Register the window class */
         atom = RegisterClass( &wc );
-        assert( atom );
+        FREEGLUT_INTERNAL_ERROR_EXIT ( atom, "Window Class Not Registered", "fghInitialize" );
     }
 
     /* The screen dimensions can be obtained via GetSystemMetrics() calls */
@@ -199,10 +366,42 @@ static void fghInitialize( const char* displayName )
 
         ReleaseDC( desktop, context );
     }
+    /* If we have a DisplayName try to use it for metrics */
+    if( fgDisplay.DisplayName )
+    {
+        HDC context = CreateDC(fgDisplay.DisplayName,0,0,0);
+        if( context )
+        {
+           fgDisplay.ScreenWidth  = GetDeviceCaps( context, HORZRES );
+           fgDisplay.ScreenHeight = GetDeviceCaps( context, VERTRES );
+           fgDisplay.ScreenWidthMM  = GetDeviceCaps( context, HORZSIZE );
+           fgDisplay.ScreenHeightMM = GetDeviceCaps( context, VERTSIZE );
+           DeleteDC(context);
+        }
+        else
+           fgWarning("fghInitialize: "
+                     "CreateDC failed, Screen size info may be incorrect\n"
+          "This is quite likely caused by a bad '-display' parameter");
+      
+    }
+    /* Set the timer granularity to 1 ms */
+    timeBeginPeriod ( 1 );
 
 #endif
 
     fgState.Initialised = GL_TRUE;
+
+    /* Avoid registering atexit callback on Win32 as it results in an access
+     * violation due to calling into a module which has been unloaded.
+     * Any cleanup isn't needed on Windows anyway, the OS takes care of it.c
+     * see: http://blogs.msdn.com/b/oldnewthing/archive/2012/01/05/10253268.aspx
+     */
+#if ( TARGET_HOST_MS_WINDOWS == 0 )
+    atexit(fgDeinitialize);
+#endif
+
+    /* InputDevice uses GlutTimerFunc(), so fgState.Initialised must be TRUE */
+    fgInitialiseInputDevices();
 }
 
 /*
@@ -214,23 +413,28 @@ void fgDeinitialize( void )
 
     if( !fgState.Initialised )
     {
-        fgWarning( "fgDeinitialize(): "
-                   "no valid initialization has been performed" );
         return;
     }
 
-    /* fgState.Initialised = GL_FALSE; */
+       /* If we're in game mode, we want to leave game mode */
+    if( fgStructure.GameModeWindow ) {
+        glutLeaveGameMode();
+    }
 
     /* If there was a menu created, destroy the rendering context */
     if( fgStructure.MenuContext )
     {
+#if TARGET_HOST_POSIX_X11
+        /* Note that the MVisualInfo is not owned by the MenuContext! */
+        glXDestroyContext( fgDisplay.Display, fgStructure.MenuContext->MContext );
+#endif
         free( fgStructure.MenuContext );
         fgStructure.MenuContext = NULL;
     }
 
     fgDestroyStructure( );
 
-    while( ( timer = fgState.Timers.First ) )
+    while( ( timer = fgState.Timers.First) )
     {
         fgListRemove( &fgState.Timers, &timer->Node );
         free( timer );
@@ -242,11 +446,22 @@ void fgDeinitialize( void )
         free( timer );
     }
 
-#if !TARGET_HOST_WINCE
+#if !defined(_WIN32_WCE)
     if ( fgState.JoysticksInitialised )
         fgJoystickClose( );
-#endif /* !TARGET_HOST_WINCE */
+
+    if ( fgState.InputDevsInitialised )
+        fgInputDeviceClose( );
+#endif /* !defined(_WIN32_WCE) */
     fgState.JoysticksInitialised = GL_FALSE;
+    fgState.InputDevsInitialised = GL_FALSE;
+
+       fgState.MouseWheelTicks = 0;
+
+    fgState.MajorVersion = 1;
+    fgState.MinorVersion = 0;
+    fgState.ContextFlags = 0;
+    fgState.ContextProfile = 0;
 
     fgState.Initialised = GL_FALSE;
 
@@ -268,16 +483,14 @@ void fgDeinitialize( void )
     fgState.ActionOnWindowClose = GLUT_ACTION_EXIT;
     fgState.ExecState           = GLUT_EXEC_STATE_INIT;
 
-    fgState.KeyRepeat       = GL_TRUE;
-    fgState.Modifiers       = 0xffffffff;
+    fgState.KeyRepeat       = GLUT_KEY_REPEAT_ON;
+    fgState.Modifiers       = INVALID_MODIFIERS;
 
     fgState.GameModeSize.X  = 640;
     fgState.GameModeSize.Y  = 480;
     fgState.GameModeDepth   =  16;
     fgState.GameModeRefresh =  72;
 
-    fgState.Time.Set = GL_FALSE;
-
     fgListInit( &fgState.Timers );
     fgListInit( &fgState.FreeTimers );
 
@@ -295,8 +508,7 @@ void fgDeinitialize( void )
         fgState.ProgramName = NULL;
     }
 
-
-#if TARGET_HOST_UNIX_X11
+#if TARGET_HOST_POSIX_X11
 
     /*
      * Make sure all X-client data we have created will be destroyed on
@@ -310,14 +522,26 @@ void fgDeinitialize( void )
      */
     XCloseDisplay( fgDisplay.Display );
 
+#elif TARGET_HOST_MS_WINDOWS
+    if( fgDisplay.DisplayName )
+    {
+        free( fgDisplay.DisplayName );
+        fgDisplay.DisplayName = NULL;
+    }
+
+    /* Reset the timer granularity */
+    timeEndPeriod ( 1 );
+
 #endif
+
+    fgState.Initialised = GL_FALSE;
 }
 
 /*
  * Everything inside the following #ifndef is copied from the X sources.
  */
 
-#if TARGET_HOST_WIN32 || TARGET_HOST_WINCE
+#if TARGET_HOST_MS_WINDOWS
 
 /*
 
@@ -509,12 +733,14 @@ void FGAPIENTRY glutInit( int* pargc, char** argv )
 
     fgCreateStructure( );
 
-    fgElapsedTime( );
+    /* Get start time */
+    fgState.Time = fgSystemTime();
 
     /* check if GLUT_FPS env var is set */
-#if !TARGET_HOST_WINCE
+#ifndef _WIN32_WCE
     {
         const char *fps = getenv( "GLUT_FPS" );
+
         if( fps )
         {
             int interval;
@@ -527,7 +753,7 @@ void FGAPIENTRY glutInit( int* pargc, char** argv )
         }
     }
 
-    displayName = getenv( "DISPLAY");
+    displayName = getenv( "DISPLAY" );
 
     for( i = 1; i < argc; i++ )
     {
@@ -604,7 +830,7 @@ void FGAPIENTRY glutInit( int* pargc, char** argv )
             argv[ i ] = argv[ j ];
     }
 
-#endif /* TARGET_HOST_WINCE */
+#endif /* _WIN32_WCE */
 
     /*
      * Have the display created now. If there wasn't a "-display"
@@ -618,7 +844,7 @@ void FGAPIENTRY glutInit( int* pargc, char** argv )
      * size.
      */
 
-    if( geometry )
+    if (geometry )
     {
         unsigned int parsedWidth, parsedHeight;
         int mask = XParseGeometry( geometry,
@@ -642,6 +868,24 @@ void FGAPIENTRY glutInit( int* pargc, char** argv )
     }
 }
 
+#if TARGET_HOST_MS_WINDOWS
+void (__cdecl *__glutExitFunc)( int return_value ) = NULL;
+
+void FGAPIENTRY __glutInitWithExit( int *pargc, char **argv, void (__cdecl *exit_function)(int) )
+{
+  __glutExitFunc = exit_function;
+  glutInit(pargc, argv);
+}
+#endif
+
+/*
+ * Undoes all the "glutInit" stuff
+ */
+void FGAPIENTRY glutExit ( void )
+{
+  fgDeinitialize ();
+}
+
 /*
  * Sets the default initial window position for new windows
  */
@@ -682,7 +926,6 @@ void FGAPIENTRY glutInitDisplayMode( unsigned int displayMode )
 
 /* -- INIT DISPLAY STRING PARSING ------------------------------------------ */
 
-#define NUM_TOKENS             36
 static char* Tokens[] =
 {
     "alpha", "acca", "acc", "blue", "buffer", "conformant", "depth", "double",
@@ -691,19 +934,9 @@ static char* Tokens[] =
     "xstaticgray", "xgrayscale", "xstaticcolor", "xpseudocolor",
     "xtruecolor", "xdirectcolor",
     "xstaticgrey", "xgreyscale", "xstaticcolour", "xpseudocolour",
-    "xtruecolour", "xdirectcolour", "borderless"
-};
-
-static int TokenLengths[] =
-{
-    5,       4,      3,     4,      6,        10,           5,       6,
-    5,       5,       3,     3,     4,      3,     9,           7,
-    6,        6,        7,         4,      8,          8,          7,
-    11,            10,           12,             12,
-    10,           12,
-    11,            10,           13,             13,
-    11,            13,              10
+    "xtruecolour", "xdirectcolour", "borderless", "aux"
 };
+#define NUM_TOKENS             (sizeof(Tokens) / sizeof(*Tokens))
 
 void FGAPIENTRY glutInitDisplayString( const char* displayMode )
 {
@@ -713,19 +946,27 @@ void FGAPIENTRY glutInitDisplayString( const char* displayMode )
      * delimited by blanks or tabs.
      */
     char *token ;
-    int len = strlen ( displayMode );
+    size_t len = strlen ( displayMode );
     char *buffer = (char *)malloc ( (len+1) * sizeof(char) );
     memcpy ( buffer, displayMode, len );
     buffer[len] = '\0';
 
     token = strtok ( buffer, " \t" );
+
     while ( token )
     {
         /* Process this token */
         int i ;
+
+        /* Temporary fix:  Ignore any length specifications and at least
+         * process the basic token
+         * TODO:  Fix this permanently
+         */
+        size_t cleanlength = strcspn ( token, "=<>~!" );
+
         for ( i = 0; i < NUM_TOKENS; i++ )
         {
-            if ( strncmp ( token, Tokens[i], TokenLengths[i] ) == 0 ) break ;
+            if ( strncmp ( token, Tokens[i], cleanlength ) == 0 ) break ;
         }
 
         switch ( i )
@@ -822,63 +1063,67 @@ void FGAPIENTRY glutInitDisplayString( const char* displayMode )
         case 20 :  /* "win32pdf": (incorrect spelling but was there before */
         case 21 :  /* "win32pfd":  matches the Win32 Pixel Format Descriptor by
                       number */
-#if TARGET_HOST_WIN32
+#if TARGET_HOST_MS_WINDOWS
 #endif
             break ;
 
         case 22 :  /* "xvisual":  matches the X visual ID by number */
-#if TARGET_HOST_UNIX_X11
+#if TARGET_HOST_POSIX_X11
 #endif
             break ;
 
         case 23 :  /* "xstaticgray": */
         case 29 :  /* "xstaticgrey":  boolean indicating if the frame buffer
                       configuration's X visual is of type StaticGray */
-#if TARGET_HOST_UNIX_X11
+#if TARGET_HOST_POSIX_X11
 #endif
             break ;
 
         case 24 :  /* "xgrayscale": */
         case 30 :  /* "xgreyscale":  boolean indicating if the frame buffer
                       configuration's X visual is of type GrayScale */
-#if TARGET_HOST_UNIX_X11
+#if TARGET_HOST_POSIX_X11
 #endif
             break ;
 
         case 25 :  /* "xstaticcolor": */
         case 31 :  /* "xstaticcolour":  boolean indicating if the frame buffer
                       configuration's X visual is of type StaticColor */
-#if TARGET_HOST_UNIX_X11
+#if TARGET_HOST_POSIX_X11
 #endif
             break ;
 
         case 26 :  /* "xpseudocolor": */
         case 32 :  /* "xpseudocolour":  boolean indicating if the frame buffer
                       configuration's X visual is of type PseudoColor */
-#if TARGET_HOST_UNIX_X11
+#if TARGET_HOST_POSIX_X11
 #endif
             break ;
 
         case 27 :  /* "xtruecolor": */
         case 33 :  /* "xtruecolour":  boolean indicating if the frame buffer
                       configuration's X visual is of type TrueColor */
-#if TARGET_HOST_UNIX_X11
+#if TARGET_HOST_POSIX_X11
 #endif
             break ;
 
         case 28 :  /* "xdirectcolor": */
         case 34 :  /* "xdirectcolour":  boolean indicating if the frame buffer
                       configuration's X visual is of type DirectColor */
-#if TARGET_HOST_UNIX_X11
+#if TARGET_HOST_POSIX_X11
 #endif
             break ;
 
         case 35 :  /* "borderless":  windows should not have borders */
-#if TARGET_HOST_UNIX_X11
+#if TARGET_HOST_POSIX_X11
 #endif
             break ;
 
-        case 36 :  /* Unrecognized */
+        case 36 :  /* "aux":  some number of aux buffers */
+            glut_state_flag |= GLUT_AUX;
+            break ;
+
+        case 37 :  /* Unrecognized */
             fgWarning ( "WARNING - Display string token not recognized:  %s",
                         token );
             break ;
@@ -893,4 +1138,46 @@ void FGAPIENTRY glutInitDisplayString( const char* displayMode )
     fgState.DisplayMode = glut_state_flag;
 }
 
+/* -- SETTING OPENGL 3.0 CONTEXT CREATION PARAMETERS ---------------------- */
+
+void FGAPIENTRY glutInitContextVersion( int majorVersion, int minorVersion )
+{
+    /* We will make use of these valuse when creating a new OpenGL context... */
+    fgState.MajorVersion = majorVersion;
+    fgState.MinorVersion = minorVersion;
+}
+
+
+void FGAPIENTRY glutInitContextFlags( int flags )
+{
+    /* We will make use of this value when creating a new OpenGL context... */
+    fgState.ContextFlags = flags;
+}
+
+void FGAPIENTRY glutInitContextProfile( int profile )
+{
+    /* We will make use of this value when creating a new OpenGL context... */
+    fgState.ContextProfile = profile;
+}
+
+/* -------------- User Defined Error/Warning Handler Support -------------- */
+
+/*
+ * Sets the user error handler (note the use of va_list for the args to the fmt)
+ */
+void FGAPIENTRY glutInitErrorFunc( void (* vfgError) ( const char *fmt, va_list ap ) )
+{
+    /* This allows user programs to handle freeglut errors */
+    fgState.ErrorFunc = vfgError;
+}
+
+/*
+ * Sets the user warning handler (note the use of va_list for the args to the fmt)
+ */
+void FGAPIENTRY glutInitWarningFunc( void (* vfgWarning) ( const char *fmt, va_list ap ) )
+{
+    /* This allows user programs to handle freeglut warnings */
+    fgState.WarningFunc = vfgWarning;
+}
+
 /*** END OF FILE ***/