2
0
Эх сурвалжийг харах

Merge branch 'master' into multi-monitor

Conflicts:
	.gitignore
	src/CMakeLists.txt
	src/x11_window.c
Camilla Berglund 13 жил өмнө
parent
commit
83f5b920b9

+ 12 - 11
.gitignore

@@ -1,9 +1,18 @@
 .DS_Store
+Makefile
 CMakeCache.txt
 CMakeFiles
 cmake_install.cmake
 cmake_uninstall.cmake
 docs/Doxyfile
+src/config.h
+src/glfw3.pc
+src/libglfw3.so
+src/libglfw3.a
+src/libglfw3.dylib
+src/glfw3.lib
+src/glfw3.dll
+src/glfw3dll.lib
 examples/*.app
 examples/*.exe
 examples/boing
@@ -13,21 +22,11 @@ examples/splitview
 examples/triangle
 examples/wave
 src/config.h
-src/glfw.dll
-src/glfw.lib
-src/glfwdll.lib
-src/libglfw.a
-src/libglfw.dll
-src/libglfw.dylib
-src/libglfw.lib
-src/libglfw.pc
-src/libglfw.so
-src/libglfwdll.lib
 tests/*.app
 tests/*.exe
 tests/accuracy
+tests/clipboard
 tests/defaults
-tests/dynamic
 tests/events
 tests/fsaa
 tests/fsfocus
@@ -35,10 +34,12 @@ tests/gamma
 tests/glfwinfo
 tests/iconify
 tests/joysticks
+tests/modes
 tests/peter
 tests/reopen
 tests/sharing
 tests/tearing
+tests/threads
 tests/title
 tests/version
 tests/windows

+ 14 - 2
CMakeLists.txt

@@ -12,10 +12,17 @@ set(LIB_SUFFIX "" CACHE STRING "Takes an empty string or 64. Directory where lib
 
 option(GLFW_BUILD_EXAMPLES "Build the GLFW example programs" ON)
 option(GLFW_BUILD_TESTS "Build the GLFW test programs" ON)
+option(GLFW_NATIVE_API "Build the GLFW native API" OFF)
 option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
 
 find_package(OpenGL REQUIRED)
 
+set(CMAKE_THREAD_PREFER_PTHREADS YES)
+find_package(Threads)
+if (CMAKE_THREAD_LIBS_INIT)
+    list(APPEND glfw_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})
+endif()
+
 #--------------------------------------------------------------------
 # Enable all warnings on GCC, regardless of OS
 #--------------------------------------------------------------------
@@ -177,7 +184,7 @@ if (_GLFW_X11_GLX)
     endif()
 
     if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
-        set(_GLFW_USE_LINUX_JOYSTICKS 1)
+        set(_GLFW_HAS_LINUX_JOYSTICKS 1)
     endif()
 endif()
 
@@ -253,7 +260,12 @@ configure_file(${GLFW_SOURCE_DIR}/src/config.h.in
 # The src directory's CMakeLists.txt file installs the library
 #--------------------------------------------------------------------
 install(DIRECTORY include/GL DESTINATION include 
-        FILES_MATCHING PATTERN glfw3.h PATTERN glfw3native.h)
+        FILES_MATCHING PATTERN glfw3.h)
+
+if (GLFW_NATIVE_API)
+    install(DIRECTORY include/GL DESTINATION include 
+            FILES_MATCHING PATTERN glfw3native.h)
+endif()
 
 install(FILES COPYING.txt readme.html 
         DESTINATION share/doc/glfw-${GLFW_VERSION_FULL})

+ 1 - 1
examples/heightmap.c

@@ -581,7 +581,7 @@ int main(int argc, char** argv)
         exit(EXIT_FAILURE);
     }
 
-    glfwWindowHint(GLFW_WINDOW_RESIZABLE, GL_FALSE);
+    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
     glfwWindowHint(GLFW_OPENGL_VERSION_MAJOR, 3);
     glfwWindowHint(GLFW_OPENGL_VERSION_MINOR, 2);
     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

+ 15 - 12
include/GL/glfw3.h

@@ -399,18 +399,19 @@ extern "C" {
 #define GLFW_ACCUM_ALPHA_BITS     0x0002100A
 #define GLFW_AUX_BUFFERS          0x0002100B
 #define GLFW_STEREO               0x0002100C
-#define GLFW_WINDOW_RESIZABLE     0x0002100D
 #define GLFW_FSAA_SAMPLES         0x0002100E
 
 /* The following constants are used with both glfwGetWindowParam
  * and glfwWindowHint
  */
-#define GLFW_OPENGL_VERSION_MAJOR 0x0002100F
-#define GLFW_OPENGL_VERSION_MINOR 0x00021010
-#define GLFW_OPENGL_FORWARD_COMPAT 0x00021011
-#define GLFW_OPENGL_DEBUG_CONTEXT 0x00021012
-#define GLFW_OPENGL_PROFILE       0x00021013
-#define GLFW_OPENGL_ROBUSTNESS    0x00021014
+#define GLFW_OPENGL_VERSION_MAJOR 0x00022000
+#define GLFW_OPENGL_VERSION_MINOR 0x00022001
+#define GLFW_OPENGL_FORWARD_COMPAT 0x00022002
+#define GLFW_OPENGL_DEBUG_CONTEXT 0x00022003
+#define GLFW_OPENGL_PROFILE       0x00022004
+#define GLFW_OPENGL_ROBUSTNESS    0x00022005
+#define GLFW_RESIZABLE            0x00022006
+#define GLFW_VISIBLE              0x00022007
 
 /* GLFW_OPENGL_ROBUSTNESS mode tokens */
 #define GLFW_OPENGL_NO_ROBUSTNESS         0x00000000
@@ -550,13 +551,15 @@ GLFWAPI void glfwSetGammaRamp(const GLFWgammaramp* ramp);
 GLFWAPI void glfwWindowHint(int target, int hint);
 GLFWAPI GLFWwindow glfwCreateWindow(int width, int height, int mode, const char* title, GLFWwindow share);
 GLFWAPI void glfwDestroyWindow(GLFWwindow window);
-GLFWAPI void glfwSetWindowTitle(GLFWwindow, const char* title);
-GLFWAPI void glfwGetWindowSize(GLFWwindow, int* width, int* height);
-GLFWAPI void glfwSetWindowSize(GLFWwindow, int width, int height);
-GLFWAPI void glfwGetWindowPos(GLFWwindow, int* xpos, int* ypos);
-GLFWAPI void glfwSetWindowPos(GLFWwindow, int xpos, int ypos);
+GLFWAPI void glfwSetWindowTitle(GLFWwindow window, const char* title);
+GLFWAPI void glfwGetWindowSize(GLFWwindow window, int* width, int* height);
+GLFWAPI void glfwSetWindowSize(GLFWwindow window, int width, int height);
+GLFWAPI void glfwGetWindowPos(GLFWwindow window, int* xpos, int* ypos);
+GLFWAPI void glfwSetWindowPos(GLFWwindow window, int xpos, int ypos);
 GLFWAPI void glfwIconifyWindow(GLFWwindow window);
 GLFWAPI void glfwRestoreWindow(GLFWwindow window);
+GLFWAPI void glfwShowWindow(GLFWwindow window);
+GLFWAPI void glfwHideWindow(GLFWwindow window);
 GLFWAPI int  glfwGetWindowParam(GLFWwindow window, int param);
 GLFWAPI void glfwSetWindowUserPointer(GLFWwindow window, void* pointer);
 GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow window);

+ 11 - 2
readme.html

@@ -283,9 +283,11 @@ version of GLFW.</p>
   <li>Added <code>GLFW_OPENGL_ROBUSTNESS</code> window hint and associated strategy tokens for <code>GL_ARB_robustness</code> support</li>
   <li>Added <code>GLFW_OPENGL_REVISION</code> window parameter to make up for removal of <code>glfwGetGLVersion</code></li>
   <li>Added <code>GLFW_INCLUDE_GL3</code> macro for telling the GLFW header to include <code>gl3.h</code> header instead of <code>gl.h</code></li>
+  <li>Added <code>GLFW_VISIBLE</code> window hint and parameter for controlling and polling window visibility</li>
   <li>Added <code>windows</code> simple multi-window test program</li>
   <li>Added <code>sharing</code> simple OpenGL object sharing test program</li>
   <li>Added <code>modes</code> video mode enumeration and setting test program</li>
+  <li>Added <code>threads</code> simple multi-threaded rendering test program</li>
   <li>Added <code>glfw3native.h</code> header and platform-specific functions for explicit access to native display, window and context handles</li>
   <li>Added <code>glfwSetGamma</code>, <code>glfwSetGammaRamp</code> and <code>glfwGetGammaRamp</code> functions and <code>GLFWgammaramp</code> type for monitor gamma ramp control</li>
   <li>Added window parameter to <code>glfwSwapBuffers</code></li>
@@ -296,7 +298,7 @@ version of GLFW.</p>
   <li>Renamed <code>glfw.h</code> to <code>glfw3.h</code> to avoid conflicts with 2.x series</li>
   <li>Renamed <code>glfwOpenWindowHint</code> to <code>glfwWindowHint</code></li>
   <li>Renamed <code>GLFW_WINDOW</code> token to <code>GLFW_WINDOWED</code></li>
-  <li>Renamed <code>GLFW_WINDOW_NO_RESIZE</code> to <code>GLFW_WINDOW_RESIZABLE</code></li>
+  <li>Renamed <code>GLFW_WINDOW_NO_RESIZE</code> to <code>GLFW_RESIZABLE</code></li>
   <li>Renamed <code>GLFW_BUILD_DLL</code> to <code>_GLFW_BUILD_DLL</code></li>
   <li>Renamed <code>version</code> test to <code>glfwinfo</code></li>
   <li>Renamed <code>GLFW_NO_GLU</code> to <code>GLFW_INCLUDE_GLU</code> and made it disabled by default</li>
@@ -349,6 +351,7 @@ version of GLFW.</p>
   <li>[X11] Bugfix: Some window properties required by the ICCCM were not set</li>
   <li>[X11] Bugfix: Calling <code>glXCreateContextAttribsARB</code> with an unavailable OpenGL version caused the application to terminate with a <code>BadMatch</code> Xlib error</li>
   <li>[X11] Bugfix: A synchronization point necessary for jitter-free locked cursor mode was incorrectly removed</li>
+  <li>[X11] Bugfix: The window size hints were not updated when calling <code>glfwSetWindowSize</code> on a non-resizable window</li>
   <li>[Win32] Changed port to use Unicode mode only</li>
   <li>[Win32] Removed explicit support for versions of Windows older than Windows XP</li>
   <li>[Win32] Bugfix: Window activation and iconification did not work as expected</li>
@@ -907,7 +910,7 @@ their skills.  Special thanks go out to:</p>
   language</li>
 
   <li>Shane Liesegang, for providing a bug fix relating to Cocoa window
-  restoration</li>
+  restoration and reporting a bug on 32-bit Cocoa builds</li>
 
   <li>Tristam MacDonald, for his bug reports and feedback on the Cocoa port</li>
 
@@ -927,9 +930,15 @@ their skills.  Special thanks go out to:</p>
   Much of the Windows code of GLFW was originally based on Jeff's
   code</li>
 
+  <li>Julian Møller, for reporting a bug in the Cocoa joystick code</li>
+
   <li>Arturo J. Pérez, for a bug fix for cursor tracking on Mac OS X 10.6 Snow
   Leopard</li>
 
+  <li>Riku Salminen, for the initial implementation of
+  <code>glfwShowWindow</code> and <code>glfwHideWindow</code>, and for making
+  the X11 event processing able to support multi-threaded rendering</li>
+
   <li>Douglas C. Schmidt and Irfan Pyarali, for their excellent article
   <a href="http://www.cs.wustl.edu/~schmidt/win32-cv-1.html">Strategies for Implementing POSIX Condition Variables on Win32</a></li>
 

+ 17 - 5
src/CMakeLists.txt

@@ -10,7 +10,11 @@ if (_GLFW_COCOA_NSGL)
     set(glfw_HEADERS ${common_HEADERS} cocoa_platform.h)
     set(glfw_SOURCES ${common_SOURCES} cocoa_clipboard.m cocoa_fullscreen.m
                      cocoa_gamma.c cocoa_init.m cocoa_input.m cocoa_joystick.m
-                     cocoa_native.m cocoa_opengl.m cocoa_time.c cocoa_window.m)
+                     cocoa_opengl.m cocoa_time.c cocoa_window.m)
+
+    if (GLFW_NATIVE_API)
+        list(APPEND glfw_SOURCES cocoa_native.m)
+    endif()
 
     # For some reason, CMake doesn't know about .m
     set_source_files_properties(${glfw_SOURCES} PROPERTIES LANGUAGE C)
@@ -18,14 +22,22 @@ elseif (_GLFW_WIN32_WGL)
     set(glfw_HEADERS ${common_HEADERS} win32_platform.h)
     set(glfw_SOURCES ${common_SOURCES} win32_clipboard.c win32_fullscreen.c
                      win32_gamma.c win32_init.c win32_input.c win32_joystick.c
-                     win32_monitor.c win32_native.c win32_opengl.c win32_time.c
-                     win32_window.c win32_dllmain.c)
+                     win32_monitor.c win32_opengl.c win32_time.c win32_window.c
+                     win32_dllmain.c)
+
+    if (GLFW_NATIVE_API)
+        list(APPEND glfw_SOURCES win32_native.c)
+    endif()
 elseif (_GLFW_X11_GLX)
     set(glfw_HEADERS ${common_HEADERS} x11_platform.h)
     set(glfw_SOURCES ${common_SOURCES} x11_clipboard.c x11_fullscreen.c
                      x11_gamma.c x11_init.c x11_input.c x11_joystick.c
-                     x11_keysym2unicode.c x11_monitor.c x11_native.c
-                     x11_opengl.c x11_time.c x11_window.c)
+                     x11_keysym2unicode.c x11_monitor.c x11_opengl.c x11_time.c
+                     x11_window.c)
+
+    if (GLFW_NATIVE_API)
+        list(APPEND glfw_SOURCES x11_native.c)
+    endif()
 endif()
 
 add_library(glfw ${glfw_SOURCES} ${glfw_HEADERS})

+ 5 - 0
src/cocoa_init.m

@@ -101,6 +101,9 @@ int _glfwPlatformInit(void)
 
     _glfwInitJoysticks();
 
+    if (!_glfwInitOpenGL())
+        return GL_FALSE;
+
     _glfwLibrary.NS.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
     if (!_glfwLibrary.NS.eventSource)
         return GL_FALSE;
@@ -139,6 +142,8 @@ int _glfwPlatformTerminate(void)
 
     _glfwTerminateJoysticks();
 
+    _glfwTerminateOpenGL();
+
     return GL_TRUE;
 }
 

+ 4 - 4
src/cocoa_joystick.m

@@ -550,15 +550,15 @@ int _glfwPlatformGetJoystickAxes(int joy, float* axes, int numaxes)
 
     for (i = 0;  i < numaxes;  i++)
     {
-        _glfwJoystickElement* axes =
+        _glfwJoystickElement* elements =
             (_glfwJoystickElement*) CFArrayGetValueAtIndex(joystick.axes, i);
 
-        long readScale = axes->maxReport - axes->minReport;
+        long readScale = elements->maxReport - elements->minReport;
 
         if (readScale == 0)
-            axes[i] = axes->value;
+            axes[i] = elements->value;
         else
-            axes[i] = (2.0f * (axes->value - axes->minReport) / readScale) - 1.0f;
+            axes[i] = (2.0f * (elements->value - elements->minReport) / readScale) - 1.0f;
 
         if (i & 1)
             axes[i] = -axes[i];

+ 48 - 1
src/cocoa_opengl.m

@@ -29,11 +29,46 @@
 
 #include "internal.h"
 
+#include <pthread.h>
+
+
+//========================================================================
+// The per-thread current context/window pointer
+//========================================================================
+static pthread_key_t _glfwCurrentTLS;
+
 
 //////////////////////////////////////////////////////////////////////////
 //////                       GLFW platform API                      //////
 //////////////////////////////////////////////////////////////////////////
 
+//========================================================================
+// Initialize OpenGL support
+//========================================================================
+
+int _glfwInitOpenGL(void)
+{
+    if (pthread_key_create(&_glfwCurrentTLS, NULL) != 0)
+    {
+        _glfwSetError(GLFW_PLATFORM_ERROR,
+                      "Cocoa/NSGL: Failed to create context TLS");
+        return GL_FALSE;
+    }
+
+    return GL_TRUE;
+}
+
+
+//========================================================================
+// Terminate OpenGL support
+//========================================================================
+
+void _glfwTerminateOpenGL(void)
+{
+    pthread_key_delete(_glfwCurrentTLS);
+}
+
+
 //========================================================================
 // Make the OpenGL context associated with the specified window current
 //========================================================================
@@ -44,6 +79,18 @@ void _glfwPlatformMakeContextCurrent(_GLFWwindow* window)
         [window->NSGL.context makeCurrentContext];
     else
         [NSOpenGLContext clearCurrentContext];
+
+    pthread_setspecific(_glfwCurrentTLS, window);
+}
+
+
+//========================================================================
+// Return the window object whose context is current
+//========================================================================
+
+_GLFWwindow* _glfwPlatformGetCurrentContext(void)
+{
+    return (_GLFWwindow*) pthread_getspecific(_glfwCurrentTLS);
 }
 
 
@@ -64,7 +111,7 @@ void _glfwPlatformSwapBuffers(_GLFWwindow* window)
 
 void _glfwPlatformSwapInterval(int interval)
 {
-    _GLFWwindow* window = _glfwLibrary.currentWindow;
+    _GLFWwindow* window = _glfwPlatformGetCurrentContext();
 
     GLint sync = interval;
     [window->NSGL.context setValues:&sync forParameter:NSOpenGLCPSwapInterval];

+ 4 - 0
src/cocoa_platform.h

@@ -124,4 +124,8 @@ void _glfwTerminateJoysticks(void);
 GLboolean _glfwSetVideoMode(int* width, int* height, int* bpp, int* refreshRate);
 void _glfwRestoreVideoMode(void);
 
+// OpenGL support
+int _glfwInitOpenGL(void);
+void _glfwTerminateOpenGL(void);
+
 #endif // _platform_h_

+ 42 - 3
src/cocoa_window.m

@@ -131,6 +131,25 @@
     return NSTerminateCancel;
 }
 
+- (void)applicationDidHide:(NSNotification *)notification
+{
+    _GLFWwindow* window;
+
+    for (window = _glfwLibrary.windowListHead;  window;  window = window->next)
+        _glfwInputWindowVisibility(window, GL_FALSE);
+}
+
+- (void)applicationDidUnhide:(NSNotification *)notification
+{
+    _GLFWwindow* window;
+
+    for (window = _glfwLibrary.windowListHead;  window;  window = window->next)
+    {
+        if ([window->NS.object isVisible])
+            _glfwInputWindowVisibility(window, GL_TRUE);
+    }
+}
+
 @end
 
 
@@ -686,7 +705,7 @@ static GLboolean createWindow(_GLFWwindow* window,
     [window->NS.object setAcceptsMouseMovedEvents:YES];
     [window->NS.object center];
 
-    if ([window->NS.object respondsToSelector:@selector(setRestorable)])
+    if ([window->NS.object respondsToSelector:@selector(setRestorable:)])
         [window->NS.object setRestorable:NO];
 
     return GL_TRUE;
@@ -898,7 +917,6 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
     if (!createContext(window, wndconfig, fbconfig))
         return GL_FALSE;
 
-    [window->NS.object makeKeyAndOrderFront:nil];
     [window->NSGL.context setView:[window->NS.object contentView]];
 
     if (wndconfig->mode == GLFW_FULLSCREEN)
@@ -1022,6 +1040,27 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window)
 }
 
 
+//========================================================================
+// Show window
+//========================================================================
+
+void _glfwPlatformShowWindow(_GLFWwindow* window)
+{
+    [window->NS.object makeKeyAndOrderFront:nil];
+    _glfwInputWindowVisibility(window, GL_TRUE);
+}
+
+
+//========================================================================
+// Hide window
+//========================================================================
+
+void _glfwPlatformHideWindow(_GLFWwindow* window)
+{
+    [window->NS.object orderOut:nil];
+    _glfwInputWindowVisibility(window, GL_FALSE);
+}
+
 //========================================================================
 // Write back window parameters into GLFW window structure
 //========================================================================
@@ -1093,7 +1132,7 @@ void _glfwPlatformSetCursorPos(_GLFWwindow* window, int x, int y)
         CGPoint mainScreenOrigin = CGDisplayBounds(CGMainDisplayID()).origin;
         double mainScreenHeight = CGDisplayBounds(CGMainDisplayID()).size.height;
         CGPoint targetPoint = CGPointMake(globalPoint.x - mainScreenOrigin.x,
-                                        mainScreenHeight - globalPoint.y -
+                                          mainScreenHeight - globalPoint.y -
                                             mainScreenOrigin.y);
         CGDisplayMoveCursorToPoint(CGMainDisplayID(), targetPoint);
     }

+ 1 - 1
src/config.h.in

@@ -64,7 +64,7 @@
 #cmakedefine _GLFW_HAS_GLXGETPROCADDRESSEXT
 
 // Define this to 1 if the Linux joystick API is available
-#cmakedefine _GLFW_USE_LINUX_JOYSTICKS
+#cmakedefine _GLFW_HAS_LINUX_JOYSTICKS
 
 // The GLFW version as used by glfwGetVersionString
 #define _GLFW_VERSION_FULL "@GLFW_VERSION_FULL@"

+ 2 - 4
src/fullscreen.c

@@ -32,10 +32,8 @@
 #include "internal.h"
 
 #include <stdlib.h>
-#ifdef __APPLE__
-#include <sys/malloc.h>
-#else
-#include <malloc.h>
+#if _WIN32
+ #include <malloc.h>
 #endif
 
 

+ 7 - 1
src/internal.h

@@ -99,6 +99,7 @@ struct _GLFWhints
     int         auxBuffers;
     GLboolean   stereo;
     GLboolean   resizable;
+    GLboolean   visible;
     int         samples;
     int         glMajor;
     int         glMinor;
@@ -121,6 +122,7 @@ struct _GLFWwndconfig
     const char*   title;
     int           refreshRate;
     GLboolean     resizable;
+    GLboolean     visible;
     int           glMajor;
     int           glMinor;
     GLboolean     glForward;
@@ -171,6 +173,7 @@ struct _GLFWwindow
     int       positionX, positionY;
     int       mode;            // GLFW_WINDOW or GLFW_FULLSCREEN
     GLboolean resizable;       // GL_TRUE if user may resize this window
+    GLboolean visible;         // GL_TRUE if this window is visible
     int       refreshRate;     // monitor refresh rate
     void*     userPointer;
 
@@ -229,7 +232,6 @@ struct _GLFWlibrary
     _GLFWhints    hints;
 
     _GLFWwindow*  windowListHead;
-    _GLFWwindow*  currentWindow;
     _GLFWwindow*  activeWindow;
     _GLFWwindow*  cursorLockWindow;
     _GLFWmonitor* monitorListHead;
@@ -313,6 +315,8 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height);
 void _glfwPlatformSetWindowPos(_GLFWwindow* window, int x, int y);
 void _glfwPlatformIconifyWindow(_GLFWwindow* window);
 void _glfwPlatformRestoreWindow(_GLFWwindow* window);
+void _glfwPlatformShowWindow(_GLFWwindow* window);
+void _glfwPlatformHideWindow(_GLFWwindow* window);
 
 // Event processing
 void _glfwPlatformPollEvents(void);
@@ -320,6 +324,7 @@ void _glfwPlatformWaitEvents(void);
 
 // OpenGL context management
 void _glfwPlatformMakeContextCurrent(_GLFWwindow* window);
+_GLFWwindow* _glfwPlatformGetCurrentContext(void);
 void _glfwPlatformSwapBuffers(_GLFWwindow* window);
 void _glfwPlatformSwapInterval(int interval);
 void _glfwPlatformRefreshWindowParams(_GLFWwindow* window);
@@ -340,6 +345,7 @@ void _glfwInputWindowFocus(_GLFWwindow* window, GLboolean activated);
 void _glfwInputWindowPos(_GLFWwindow* window, int x, int y);
 void _glfwInputWindowSize(_GLFWwindow* window, int width, int height);
 void _glfwInputWindowIconify(_GLFWwindow* window, int iconified);
+void _glfwInputWindowVisibility(_GLFWwindow* window, int visible);
 void _glfwInputWindowDamage(_GLFWwindow* window);
 void _glfwInputWindowCloseRequest(_GLFWwindow* window);
 

+ 9 - 10
src/opengl.c

@@ -350,7 +350,7 @@ GLboolean _glfwIsValidContextConfig(_GLFWwndconfig* wndconfig)
 
 GLboolean _glfwRefreshContextParams(void)
 {
-    _GLFWwindow* window = _glfwLibrary.currentWindow;
+    _GLFWwindow* window = _glfwPlatformGetCurrentContext();
 
     if (!parseGLVersion(&window->glMajor,
                         &window->glMinor,
@@ -417,7 +417,7 @@ GLboolean _glfwRefreshContextParams(void)
 
 GLboolean _glfwIsValidContext(_GLFWwndconfig* wndconfig)
 {
-    _GLFWwindow* window = _glfwLibrary.currentWindow;
+    _GLFWwindow* window = _glfwPlatformGetCurrentContext();
 
     if (window->glMajor < wndconfig->glMajor ||
         (window->glMajor == wndconfig->glMajor &&
@@ -492,16 +492,15 @@ GLFWAPI void glfwMakeContextCurrent(GLFWwindow handle)
         return;
     }
 
-    if (_glfwLibrary.currentWindow == window)
+    if (_glfwPlatformGetCurrentContext() == window)
         return;
 
     _glfwPlatformMakeContextCurrent(window);
-    _glfwLibrary.currentWindow = window;
 }
 
 
 //========================================================================
-// Returns the window whose OpenGL context is current
+// Return the window object whose context is current
 //========================================================================
 
 GLFWAPI GLFWwindow glfwGetCurrentContext(void)
@@ -512,7 +511,7 @@ GLFWAPI GLFWwindow glfwGetCurrentContext(void)
         return NULL;
     }
 
-    return _glfwLibrary.currentWindow;
+    return _glfwPlatformGetCurrentContext();
 }
 
 
@@ -546,7 +545,7 @@ GLFWAPI void glfwSwapInterval(int interval)
         return;
     }
 
-    if (!_glfwLibrary.currentWindow)
+    if (!_glfwPlatformGetCurrentContext())
     {
         _glfwSetError(GLFW_NO_CURRENT_CONTEXT, NULL);
         return;
@@ -571,7 +570,7 @@ GLFWAPI int glfwExtensionSupported(const char* extension)
         return GL_FALSE;
     }
 
-    window = _glfwLibrary.currentWindow;
+    window = _glfwPlatformGetCurrentContext();
     if (!window)
     {
         _glfwSetError(GLFW_NO_CURRENT_CONTEXT, NULL);
@@ -632,7 +631,7 @@ GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname)
         return NULL;
     }
 
-    if (!_glfwLibrary.currentWindow)
+    if (!_glfwPlatformGetCurrentContext())
     {
         _glfwSetError(GLFW_NO_CURRENT_CONTEXT, NULL);
         return NULL;
@@ -660,7 +659,7 @@ GLFWAPI void glfwCopyContext(GLFWwindow hsrc, GLFWwindow hdst, unsigned long mas
     src = (_GLFWwindow*) hsrc;
     dst = (_GLFWwindow*) hdst;
 
-    if (_glfwLibrary.currentWindow == dst)
+    if (_glfwPlatformGetCurrentContext() == dst)
     {
         _glfwSetError(GLFW_INVALID_VALUE,
                       "glfwCopyContext: Cannot copy OpenGL state to a current context");

+ 35 - 5
src/win32_opengl.c

@@ -34,6 +34,24 @@
 #include <malloc.h>
 
 
+//========================================================================
+// Thread local storage attribute macro
+//========================================================================
+#if defined(_MSC_VER)
+ #define _GLFW_TLS __declspec(thread)
+#elif defined(__GNUC__)
+ #define _GLFW_TLS __thread
+#else
+ #define _GLFW_TLS
+#endif
+
+
+//========================================================================
+// The per-thread current context/window pointer
+//========================================================================
+static _GLFW_TLS _GLFWwindow* _glfwCurrentWindow = NULL;
+
+
 //========================================================================
 // Initialize WGL-specific extensions
 // This function is called once before initial context creation, i.e. before
@@ -443,7 +461,7 @@ static GLboolean createContext(_GLFWwindow* window,
         }
     }
 
-    glfwMakeContextCurrent(window);
+    _glfwPlatformMakeContextCurrent(window);
     initWGLExtensions(window);
 
     return GL_TRUE;
@@ -508,8 +526,8 @@ void _glfwDestroyContext(_GLFWwindow* window)
 {
     // This is duplicated from glfwDestroyWindow
     // TODO: Stop duplicating code
-    if (window == _glfwLibrary.currentWindow)
-        glfwMakeContextCurrent(NULL);
+    if (window == _glfwCurrentWindow)
+        _glfwPlatformMakeContextCurrent(NULL);
 
     if (window->WGL.context)
     {
@@ -539,6 +557,18 @@ void _glfwPlatformMakeContextCurrent(_GLFWwindow* window)
         wglMakeCurrent(window->WGL.DC, window->WGL.context);
     else
         wglMakeCurrent(NULL, NULL);
+
+    _glfwCurrentWindow = window;
+}
+
+
+//========================================================================
+// Return the window object whose context is current
+//========================================================================
+
+_GLFWwindow* _glfwPlatformGetCurrentContext(void)
+{
+    return _glfwCurrentWindow;
 }
 
 
@@ -558,7 +588,7 @@ void _glfwPlatformSwapBuffers(_GLFWwindow* window)
 
 void _glfwPlatformSwapInterval(int interval)
 {
-    _GLFWwindow* window = _glfwLibrary.currentWindow;
+    _GLFWwindow* window = _glfwCurrentWindow;
 
     if (window->WGL.EXT_swap_control)
         window->WGL.SwapIntervalEXT(interval);
@@ -573,7 +603,7 @@ int _glfwPlatformExtensionSupported(const char* extension)
 {
     const GLubyte* extensions;
 
-    _GLFWwindow* window = _glfwLibrary.currentWindow;
+    _GLFWwindow* window = _glfwCurrentWindow;
 
     if (window->WGL.GetExtensionsStringEXT != NULL)
     {

+ 1 - 1
src/win32_platform.h

@@ -195,7 +195,7 @@ typedef struct _GLFWlibraryWin32
 
     // Timer data
     struct {
-        GLboolean             hasPerformanceCounter;
+        GLboolean             hasPC;
         double                resolution;
         unsigned int          t0_32;
         __int64               t0_64;

+ 4 - 4
src/win32_time.c

@@ -45,13 +45,13 @@ void _glfwInitTimer(void)
 
     if (QueryPerformanceFrequency((LARGE_INTEGER*) &freq))
     {
-        _glfwLibrary.Win32.timer.hasPerformanceCounter = GL_TRUE;
+        _glfwLibrary.Win32.timer.hasPC = GL_TRUE;
         _glfwLibrary.Win32.timer.resolution = 1.0 / (double) freq;
         QueryPerformanceCounter((LARGE_INTEGER*) &_glfwLibrary.Win32.timer.t0_64);
     }
     else
     {
-        _glfwLibrary.Win32.timer.hasPerformanceCounter = GL_FALSE;
+        _glfwLibrary.Win32.timer.hasPC = GL_FALSE;
         _glfwLibrary.Win32.timer.resolution = 0.001; // winmm resolution is 1 ms
         _glfwLibrary.Win32.timer.t0_32 = _glfw_timeGetTime();
     }
@@ -71,7 +71,7 @@ double _glfwPlatformGetTime(void)
     double t;
     __int64 t_64;
 
-    if (_glfwLibrary.Win32.timer.hasPerformanceCounter)
+    if (_glfwLibrary.Win32.timer.hasPC)
     {
         QueryPerformanceCounter((LARGE_INTEGER*) &t_64);
         t =  (double)(t_64 - _glfwLibrary.Win32.timer.t0_64);
@@ -91,7 +91,7 @@ void _glfwPlatformSetTime(double t)
 {
     __int64 t_64;
 
-    if (_glfwLibrary.Win32.timer.hasPerformanceCounter)
+    if (_glfwLibrary.Win32.timer.hasPC)
     {
         QueryPerformanceCounter((LARGE_INTEGER*) &t_64);
         _glfwLibrary.Win32.timer.t0_64 = t_64 - (__int64) (t / _glfwLibrary.Win32.timer.resolution);

+ 29 - 93
src/win32_window.c

@@ -33,95 +33,6 @@
 #include <stdlib.h>
 #include <malloc.h>
 
-
-//========================================================================
-// Enable/disable minimize/restore animations
-//========================================================================
-
-static int setMinMaxAnimations(int enable)
-{
-    ANIMATIONINFO AI;
-    int old_enable;
-
-    // Get old animation setting
-    AI.cbSize = sizeof(ANIMATIONINFO);
-    SystemParametersInfo(SPI_GETANIMATION, AI.cbSize, &AI, 0);
-    old_enable = AI.iMinAnimate;
-
-    // If requested, change setting
-    if (old_enable != enable)
-    {
-        AI.iMinAnimate = enable;
-        SystemParametersInfo(SPI_SETANIMATION, AI.cbSize, &AI,
-                             SPIF_SENDCHANGE);
-    }
-
-    return old_enable;
-}
-
-
-//========================================================================
-// Focus the window and bring it to the top of the stack
-// Due to some nastiness with how XP handles SetForegroundWindow we have
-// to go through some really bizarre measures to achieve this
-//========================================================================
-
-static void setForegroundWindow(HWND hWnd)
-{
-    int try_count = 0;
-    int old_animate;
-
-    // Try the standard approach first...
-    BringWindowToTop(hWnd);
-    SetForegroundWindow(hWnd);
-
-    // If it worked, return now
-    if (hWnd == GetForegroundWindow())
-    {
-        // Try to modify the system settings (since this is the foreground
-        // process, we are allowed to do this)
-        SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, (LPVOID) 0,
-                             SPIF_SENDCHANGE);
-        return;
-    }
-
-    // For other Windows versions than 95 & NT4.0, the standard approach
-    // may not work, so if we failed we have to "trick" Windows into
-    // making our window the foureground window: Iconify and restore
-    // again. It is ugly, but it seems to work (we turn off those annoying
-    // zoom animations to make it look a bit better at least).
-
-    // Turn off minimize/restore animations
-    old_animate = setMinMaxAnimations(0);
-
-    // We try this a few times, just to be on the safe side of things...
-    do
-    {
-        // Iconify & restore
-        ShowWindow(hWnd, SW_HIDE);
-        ShowWindow(hWnd, SW_SHOWMINIMIZED);
-        ShowWindow(hWnd, SW_SHOWNORMAL);
-
-        // Try to get focus
-        BringWindowToTop(hWnd);
-        SetForegroundWindow(hWnd);
-
-        // We do not want to keep going on forever, so we keep track of
-        // how many times we tried
-        try_count++;
-    }
-    while (hWnd != GetForegroundWindow() && try_count <= 3);
-
-    // Restore the system minimize/restore animation setting
-    setMinMaxAnimations(old_animate);
-
-    // Try to modify the system settings (since this is now hopefully the
-    // foreground process, we are probably allowed to do this)
-    SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, (LPVOID) 0,
-                         SPIF_SENDCHANGE);
-}
-
-
 //========================================================================
 // Hide mouse cursor
 //========================================================================
@@ -506,6 +417,12 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
             return 0;
         }
 
+        case WM_SHOWWINDOW:
+        {
+            _glfwInputWindowVisibility(window, wParam ? GL_TRUE : GL_FALSE);
+            break;
+        }
+
         case WM_SYSCOMMAND:
         {
             switch (wParam & 0xfff0)
@@ -845,7 +762,7 @@ static int createWindow(_GLFWwindow* window,
     WCHAR* wideTitle;
 
     // Set common window styles
-    dwStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE;
+    dwStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
     dwExStyle = WS_EX_APPWINDOW;
 
     // Set window style, depending on fullscreen mode
@@ -1080,9 +997,6 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
                      SWP_NOMOVE | SWP_NOSIZE);
     }
 
-    setForegroundWindow(window->Win32.handle);
-    SetFocus(window->Win32.handle);
-
     return GL_TRUE;
 }
 
@@ -1202,6 +1116,28 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window)
 }
 
 
+//========================================================================
+// Show or hide window
+//========================================================================
+
+void _glfwPlatformShowWindow(_GLFWwindow* window)
+{
+    ShowWindow(window->Win32.handle, SW_SHOWNORMAL);
+    BringWindowToTop(window->Win32.handle);
+    SetForegroundWindow(window->Win32.handle);
+    SetFocus(window->Win32.handle);
+}
+
+
+//========================================================================
+// Show or hide window
+//========================================================================
+
+void _glfwPlatformHideWindow(_GLFWwindow* window)
+{
+    ShowWindow(window->Win32.handle, SW_HIDE);
+}
+
 //========================================================================
 // Write back window parameters into GLFW window structure
 //========================================================================

+ 71 - 10
src/window.c

@@ -33,10 +33,8 @@
 
 #include <string.h>
 #include <stdlib.h>
-#ifdef __APPLE__
-#include <sys/malloc.h>
-#else
-#include <malloc.h>
+#if _WIN32
+ #include <malloc.h>
 #endif
 
 
@@ -82,8 +80,9 @@ void _glfwSetDefaultWindowHints(void)
     _glfwLibrary.hints.glMajor = 1;
     _glfwLibrary.hints.glMinor = 0;
 
-    // The default is to allow window resizing
+    // The default is to show the window and allow window resizing
     _glfwLibrary.hints.resizable = GL_TRUE;
+    _glfwLibrary.hints.visible   = GL_TRUE;
 
     // The default is 24 bits of depth, 8 bits of color
     _glfwLibrary.hints.depthBits = 24;
@@ -182,6 +181,16 @@ void _glfwInputWindowIconify(_GLFWwindow* window, int iconified)
 }
 
 
+//========================================================================
+// Register window visibility events
+//========================================================================
+
+void _glfwInputWindowVisibility(_GLFWwindow* window, int visible)
+{
+    window->visible = visible;
+}
+
+
 //========================================================================
 // Register window damage events
 //========================================================================
@@ -252,6 +261,7 @@ GLFWAPI GLFWwindow glfwCreateWindow(int width, int height,
     wndconfig.title          = title;
     wndconfig.refreshRate    = Max(_glfwLibrary.hints.refreshRate, 0);
     wndconfig.resizable      = _glfwLibrary.hints.resizable ? GL_TRUE : GL_FALSE;
+    wndconfig.visible        = _glfwLibrary.hints.visible ? GL_TRUE : GL_FALSE;
     wndconfig.glMajor        = _glfwLibrary.hints.glMajor;
     wndconfig.glMinor        = _glfwLibrary.hints.glMinor;
     wndconfig.glForward      = _glfwLibrary.hints.glForward ? GL_TRUE : GL_FALSE;
@@ -356,6 +366,9 @@ GLFWAPI GLFWwindow glfwCreateWindow(int width, int height,
     glClear(GL_COLOR_BUFFER_BIT);
     _glfwPlatformSwapBuffers(window);
 
+    if (wndconfig.visible || mode == GLFW_FULLSCREEN)
+        glfwShowWindow(window);
+
     return window;
 }
 
@@ -413,9 +426,12 @@ GLFWAPI void glfwWindowHint(int target, int hint)
         case GLFW_STEREO:
             _glfwLibrary.hints.stereo = hint;
             break;
-        case GLFW_WINDOW_RESIZABLE:
+        case GLFW_RESIZABLE:
             _glfwLibrary.hints.resizable = hint;
             break;
+        case GLFW_VISIBLE:
+            _glfwLibrary.hints.visible = hint;
+            break;
         case GLFW_FSAA_SAMPLES:
             _glfwLibrary.hints.samples = hint;
             break;
@@ -463,8 +479,9 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow handle)
         return;
 
     // Clear the current context if this window's context is current
-    if (window == _glfwLibrary.currentWindow)
-        glfwMakeContextCurrent(NULL);
+    // TODO: Re-examine this in light of multithreading
+    if (window == _glfwPlatformGetCurrentContext())
+        _glfwPlatformMakeContextCurrent(NULL);
 
     // Clear the active window pointer if this is the active window
     if (window == _glfwLibrary.activeWindow)
@@ -652,6 +669,48 @@ GLFWAPI void glfwRestoreWindow(GLFWwindow handle)
 }
 
 
+//========================================================================
+// Window show
+//========================================================================
+
+GLFWAPI void glfwShowWindow(GLFWwindow handle)
+{
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+
+    if (!_glfwInitialized)
+    {
+        _glfwSetError(GLFW_NOT_INITIALIZED, NULL);
+        return;
+    }
+
+    if (window->mode == GLFW_FULLSCREEN)
+        return;
+
+    _glfwPlatformShowWindow(window);
+}
+
+
+//========================================================================
+// Window hide
+//========================================================================
+
+GLFWAPI void glfwHideWindow(GLFWwindow handle)
+{
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+
+    if (!_glfwInitialized)
+    {
+        _glfwSetError(GLFW_NOT_INITIALIZED, NULL);
+        return;
+    }
+
+    if (window->mode == GLFW_FULLSCREEN)
+        return;
+
+    _glfwPlatformHideWindow(window);
+}
+
+
 //========================================================================
 // Get window parameter
 //========================================================================
@@ -676,8 +735,10 @@ GLFWAPI int glfwGetWindowParam(GLFWwindow handle, int param)
             return window->closeRequested;
         case GLFW_REFRESH_RATE:
             return window->refreshRate;
-        case GLFW_WINDOW_RESIZABLE:
+        case GLFW_RESIZABLE:
             return window->resizable;
+        case GLFW_VISIBLE:
+            return window->visible;
         case GLFW_OPENGL_VERSION_MAJOR:
             return window->glMajor;
         case GLFW_OPENGL_VERSION_MINOR:
@@ -816,7 +877,7 @@ GLFWAPI void glfwSetWindowIconifyCallback(GLFWwindowiconifyfun cbfun)
 
 
 //========================================================================
-// Poll for new window and input events and close any flagged windows
+// Poll for new window and input events
 //========================================================================
 
 GLFWAPI void glfwPollEvents(void)

+ 5 - 2
src/x11_init.c

@@ -634,6 +634,8 @@ static void terminateDisplay(void)
 
 int _glfwPlatformInit(void)
 {
+    XInitThreads();
+
     if (!initDisplay())
         return GL_FALSE;
 
@@ -646,7 +648,8 @@ int _glfwPlatformInit(void)
 
     _glfwLibrary.X11.cursor = createNULLCursor();
 
-    _glfwInitJoysticks();
+    if (!_glfwInitJoysticks())
+        return GL_FALSE;
 
     _glfwInitMonitors();
 
@@ -720,7 +723,7 @@ const char* _glfwPlatformGetVersionString(void)
 #if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK)
         " clock_gettime"
 #endif
-#if defined(_GLFW_USE_LINUX_JOYSTICKS)
+#if defined(_GLFW_HAS_LINUX_JOYSTICKS)
         " Linux-joystick-API"
 #else
         " no-joystick-support"

+ 47 - 24
src/x11_joystick.c

@@ -30,17 +30,18 @@
 
 #include "internal.h"
 
-#ifdef _GLFW_USE_LINUX_JOYSTICKS
+#ifdef _GLFW_HAS_LINUX_JOYSTICKS
 #include <linux/joystick.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <errno.h>
-
+#include <regex.h>
+#include <dirent.h>
 #include <stdio.h>
 #include <stdlib.h>
-#endif // _GLFW_USE_LINUX_JOYSTICKS
+#endif // _GLFW_HAS_LINUX_JOYSTICKS
 
 
 //========================================================================
@@ -49,11 +50,11 @@
 
 static int openJoystickDevice(int joy, const char* path)
 {
-#ifdef _GLFW_USE_LINUX_JOYSTICKS
+#ifdef _GLFW_HAS_LINUX_JOYSTICKS
     char numAxes, numButtons;
     int fd, version;
 
-    fd = open(path, O_NONBLOCK);
+    fd = open(path, O_RDONLY | O_NONBLOCK);
     if (fd == -1)
         return GL_FALSE;
 
@@ -96,7 +97,7 @@ static int openJoystickDevice(int joy, const char* path)
     }
 
     _glfwLibrary.X11.joystick[joy].present = GL_TRUE;
-#endif // _GLFW_USE_LINUX_JOYSTICKS
+#endif // _GLFW_HAS_LINUX_JOYSTICKS
 
     return GL_TRUE;
 }
@@ -108,7 +109,7 @@ static int openJoystickDevice(int joy, const char* path)
 
 static void pollJoystickEvents(void)
 {
-#ifdef _GLFW_USE_LINUX_JOYSTICKS
+#ifdef _GLFW_HAS_LINUX_JOYSTICKS
     int i;
     ssize_t result;
     struct js_event e;
@@ -127,7 +128,7 @@ static void pollJoystickEvents(void)
             if (errno == ENODEV)
                 _glfwLibrary.X11.joystick[i].present = GL_FALSE;
 
-            if (result < sizeof(e))
+            if (result == -1)
                 break;
 
             // We don't care if it's an init event or not
@@ -159,7 +160,7 @@ static void pollJoystickEvents(void)
             }
         }
     }
-#endif // _GLFW_USE_LINUX_JOYSTICKS
+#endif // _GLFW_HAS_LINUX_JOYSTICKS
 }
 
 
@@ -171,30 +172,52 @@ static void pollJoystickEvents(void)
 // Initialize joystick interface
 //========================================================================
 
-void _glfwInitJoysticks(void)
+int _glfwInitJoysticks(void)
 {
-#ifdef _GLFW_USE_LINUX_JOYSTICKS
-    int i, j, joy = 0;
-    char path[20];
-    const char* bases[] =
+#ifdef _GLFW_HAS_LINUX_JOYSTICKS
+    int i, joy = 0;
+    regex_t regex;
+    DIR* dir;
+    const char* dirs[] =
     {
-        "/dev/input/js",
-        "/dev/js"
+        "/dev/input",
+        "/dev"
     };
 
-    for (i = 0;  i < sizeof(bases) / sizeof(bases[0]);  i++)
+    if (regcomp(&regex, "^js[0-9]\\+$", 0) != 0)
     {
-        for (j = 0;  j < 50;  j++)
+        _glfwSetError(GLFW_PLATFORM_ERROR, "X11: Failed to compile regex");
+        return GL_FALSE;
+    }
+
+    for (i = 0;  i < sizeof(dirs) / sizeof(dirs[0]);  i++)
+    {
+        struct dirent* entry;
+
+        dir = opendir(dirs[i]);
+        if (!dir)
+            continue;
+
+        while ((entry = readdir(dir)))
         {
-            if (joy > GLFW_JOYSTICK_LAST)
-                break;
+            char path[20];
+            regmatch_t match;
 
-            sprintf(path, "%s%i", bases[i], j);
+            if (regexec(&regex, entry->d_name, 1, &match, 0) != 0)
+                continue;
+
+            snprintf(path, sizeof(path), "%s/%s", dirs[i], entry->d_name);
             if (openJoystickDevice(joy, path))
                 joy++;
         }
+
+        closedir(dir);
     }
-#endif // _GLFW_USE_LINUX_JOYSTICKS
+
+    regfree(&regex);
+#endif // _GLFW_HAS_LINUX_JOYSTICKS
+
+    return GL_TRUE;
 }
 
 
@@ -204,7 +227,7 @@ void _glfwInitJoysticks(void)
 
 void _glfwTerminateJoysticks(void)
 {
-#ifdef _GLFW_USE_LINUX_JOYSTICKS
+#ifdef _GLFW_HAS_LINUX_JOYSTICKS
     int i;
 
     for (i = 0;  i <= GLFW_JOYSTICK_LAST;  i++)
@@ -218,7 +241,7 @@ void _glfwTerminateJoysticks(void)
             _glfwLibrary.X11.joystick[i].present = GL_FALSE;
         }
     }
-#endif // _GLFW_USE_LINUX_JOYSTICKS
+#endif // _GLFW_HAS_LINUX_JOYSTICKS
 }
 
 

+ 33 - 1
src/x11_opengl.c

@@ -38,6 +38,22 @@
 void (*glXGetProcAddressEXT(const GLubyte* procName))();
 
 
+//========================================================================
+// Thread local storage attribute macro
+//========================================================================
+#if defined(__GNUC__)
+ #define _GLFW_TLS __thread
+#else
+ #define _GLFW_TLS
+#endif
+
+
+//========================================================================
+// The per-thread current context/window pointer
+//========================================================================
+static _GLFW_TLS _GLFWwindow* _glfwCurrentWindow = NULL;
+
+
 //========================================================================
 // Returns the specified attribute of the specified GLXFBConfig
 // NOTE: Do not call this unless we have found GLX 1.3+ or GLX_SGIX_fbconfig
@@ -616,6 +632,10 @@ XVisualInfo* _glfwGetContextVisual(_GLFWwindow* window)
 }
 
 
+//////////////////////////////////////////////////////////////////////////
+//////                       GLFW platform API                      //////
+//////////////////////////////////////////////////////////////////////////
+
 //========================================================================
 // Make the OpenGL context associated with the specified window current
 //========================================================================
@@ -630,6 +650,18 @@ void _glfwPlatformMakeContextCurrent(_GLFWwindow* window)
     }
     else
         glXMakeCurrent(_glfwLibrary.X11.display, None, NULL);
+
+    _glfwCurrentWindow = window;
+}
+
+
+//========================================================================
+// Return the window object whose context is current
+//========================================================================
+
+_GLFWwindow* _glfwPlatformGetCurrentContext(void)
+{
+    return _glfwCurrentWindow;
 }
 
 
@@ -649,7 +681,7 @@ void _glfwPlatformSwapBuffers(_GLFWwindow* window)
 
 void _glfwPlatformSwapInterval(int interval)
 {
-    _GLFWwindow* window = _glfwLibrary.currentWindow;
+    _GLFWwindow* window = _glfwCurrentWindow;
 
     if (_glfwLibrary.GLX.EXT_swap_control)
     {

+ 1 - 1
src/x11_platform.h

@@ -322,7 +322,7 @@ void _glfwSetVideoMode(int* width, int* height, int* rate);
 void _glfwRestoreVideoMode(void);
 
 // Joystick input
-void _glfwInitJoysticks(void);
+int  _glfwInitJoysticks(void);
 void _glfwTerminateJoysticks(void);
 
 // Monitors

+ 106 - 78
src/x11_window.c

@@ -30,6 +30,8 @@
 
 #include "internal.h"
 
+#include <sys/select.h>
+
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -226,10 +228,6 @@ static GLboolean createWindow(_GLFWwindow* window,
 
     _glfwPlatformSetWindowTitle(window, wndconfig->title);
 
-    // Make sure the window is mapped before proceeding
-    XMapWindow(_glfwLibrary.X11.display, window->X11.handle);
-    XFlush(_glfwLibrary.X11.display);
-
     return GL_TRUE;
 }
 
@@ -460,34 +458,31 @@ static _GLFWwindow* findWindow(Window handle)
 
 
 //========================================================================
-// Get and process next X event (called by _glfwPlatformPollEvents)
+// Process the specified X event
 //========================================================================
 
-static void processSingleEvent(void)
+static void processEvent(XEvent *event)
 {
     _GLFWwindow* window;
 
-    XEvent event;
-    XNextEvent(_glfwLibrary.X11.display, &event);
-
-    switch (event.type)
+    switch (event->type)
     {
         case KeyPress:
         {
             // A keyboard key was pressed
-            window = findWindow(event.xkey.window);
+            window = findWindow(event->xkey.window);
             if (window == NULL)
                 return;
 
-            _glfwInputKey(window, translateKey(event.xkey.keycode), GLFW_PRESS);
-            _glfwInputChar(window, translateChar(&event.xkey));
+            _glfwInputKey(window, translateKey(event->xkey.keycode), GLFW_PRESS);
+            _glfwInputChar(window, translateChar(&event->xkey));
             break;
         }
 
         case KeyRelease:
         {
             // A keyboard key was released
-            window = findWindow(event.xkey.window);
+            window = findWindow(event->xkey.window);
             if (window == NULL)
                 return;
 
@@ -501,15 +496,15 @@ static void processSingleEvent(void)
                 XPeekEvent(_glfwLibrary.X11.display, &nextEvent);
 
                 if (nextEvent.type == KeyPress &&
-                    nextEvent.xkey.window == event.xkey.window &&
-                    nextEvent.xkey.keycode == event.xkey.keycode)
+                    nextEvent.xkey.window == event->xkey.window &&
+                    nextEvent.xkey.keycode == event->xkey.keycode)
                 {
                     // This last check is a hack to work around key repeats
                     // leaking through due to some sort of time drift
                     // Toshiyuki Takahashi can press a button 16 times per
                     // second so it's fairly safe to assume that no human is
                     // pressing the key 50 times per second (value is ms)
-                    if ((nextEvent.xkey.time - event.xkey.time) < 20)
+                    if ((nextEvent.xkey.time - event->xkey.time) < 20)
                     {
                         // Do not report anything for this event
                         break;
@@ -517,34 +512,34 @@ static void processSingleEvent(void)
                 }
             }
 
-            _glfwInputKey(window, translateKey(event.xkey.keycode), GLFW_RELEASE);
+            _glfwInputKey(window, translateKey(event->xkey.keycode), GLFW_RELEASE);
             break;
         }
 
         case ButtonPress:
         {
             // A mouse button was pressed or a scrolling event occurred
-            window = findWindow(event.xbutton.window);
+            window = findWindow(event->xbutton.window);
             if (window == NULL)
                 return;
 
-            if (event.xbutton.button == Button1)
+            if (event->xbutton.button == Button1)
                 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS);
-            else if (event.xbutton.button == Button2)
+            else if (event->xbutton.button == Button2)
                 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS);
-            else if (event.xbutton.button == Button3)
+            else if (event->xbutton.button == Button3)
                 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS);
 
             // XFree86 3.3.2 and later translates mouse wheel up/down into
             // mouse button 4 & 5 presses
-            else if (event.xbutton.button == Button4)
+            else if (event->xbutton.button == Button4)
                 _glfwInputScroll(window, 0.0, 1.0);
-            else if (event.xbutton.button == Button5)
+            else if (event->xbutton.button == Button5)
                 _glfwInputScroll(window, 0.0, -1.0);
 
-            else if (event.xbutton.button == Button6)
+            else if (event->xbutton.button == Button6)
                 _glfwInputScroll(window, -1.0, 0.0);
-            else if (event.xbutton.button == Button7)
+            else if (event->xbutton.button == Button7)
                 _glfwInputScroll(window, 1.0, 0.0);
 
             break;
@@ -553,23 +548,23 @@ static void processSingleEvent(void)
         case ButtonRelease:
         {
             // A mouse button was released
-            window = findWindow(event.xbutton.window);
+            window = findWindow(event->xbutton.window);
             if (window == NULL)
                 return;
 
-            if (event.xbutton.button == Button1)
+            if (event->xbutton.button == Button1)
             {
                 _glfwInputMouseClick(window,
                                      GLFW_MOUSE_BUTTON_LEFT,
                                      GLFW_RELEASE);
             }
-            else if (event.xbutton.button == Button2)
+            else if (event->xbutton.button == Button2)
             {
                 _glfwInputMouseClick(window,
                                      GLFW_MOUSE_BUTTON_MIDDLE,
                                      GLFW_RELEASE);
             }
-            else if (event.xbutton.button == Button3)
+            else if (event->xbutton.button == Button3)
             {
                 _glfwInputMouseClick(window,
                                      GLFW_MOUSE_BUTTON_RIGHT,
@@ -581,7 +576,7 @@ static void processSingleEvent(void)
         case EnterNotify:
         {
             // The cursor entered the window
-            window = findWindow(event.xcrossing.window);
+            window = findWindow(event->xcrossing.window);
             if (window == NULL)
                 return;
 
@@ -595,7 +590,7 @@ static void processSingleEvent(void)
         case LeaveNotify:
         {
             // The cursor left the window
-            window = findWindow(event.xcrossing.window);
+            window = findWindow(event->xcrossing.window);
             if (window == NULL)
                 return;
 
@@ -609,12 +604,12 @@ static void processSingleEvent(void)
         case MotionNotify:
         {
             // The cursor was moved
-            window = findWindow(event.xmotion.window);
+            window = findWindow(event->xmotion.window);
             if (window == NULL)
                 return;
 
-            if (event.xmotion.x != window->X11.cursorPosX ||
-                event.xmotion.y != window->X11.cursorPosY)
+            if (event->xmotion.x != window->X11.cursorPosX ||
+                event->xmotion.y != window->X11.cursorPosY)
             {
                 // The cursor was moved by something other than GLFW
 
@@ -625,17 +620,17 @@ static void processSingleEvent(void)
                     if (_glfwLibrary.activeWindow != window)
                         break;
 
-                    x = event.xmotion.x - window->X11.cursorPosX;
-                    y = event.xmotion.y - window->X11.cursorPosY;
+                    x = event->xmotion.x - window->X11.cursorPosX;
+                    y = event->xmotion.y - window->X11.cursorPosY;
                 }
                 else
                 {
-                    x = event.xmotion.x;
-                    y = event.xmotion.y;
+                    x = event->xmotion.x;
+                    y = event->xmotion.y;
                 }
 
-                window->X11.cursorPosX = event.xmotion.x;
-                window->X11.cursorPosY = event.xmotion.y;
+                window->X11.cursorPosX = event->xmotion.x;
+                window->X11.cursorPosY = event->xmotion.y;
                 window->X11.cursorCentered = GL_FALSE;
 
                 _glfwInputCursorMotion(window, x, y);
@@ -647,17 +642,17 @@ static void processSingleEvent(void)
         case ConfigureNotify:
         {
             // The window configuration changed somehow
-            window = findWindow(event.xconfigure.window);
+            window = findWindow(event->xconfigure.window);
             if (window == NULL)
                 return;
 
             _glfwInputWindowSize(window,
-                                 event.xconfigure.width,
-                                 event.xconfigure.height);
+                                 event->xconfigure.width,
+                                 event->xconfigure.height);
 
             _glfwInputWindowPos(window,
-                                event.xconfigure.x,
-                                event.xconfigure.y);
+                                event->xconfigure.x,
+                                event->xconfigure.y);
 
             break;
         }
@@ -665,11 +660,11 @@ static void processSingleEvent(void)
         case ClientMessage:
         {
             // Custom client message, probably from the window manager
-            window = findWindow(event.xclient.window);
+            window = findWindow(event->xclient.window);
             if (window == NULL)
                 return;
 
-            if ((Atom) event.xclient.data.l[0] == _glfwLibrary.X11.wmDeleteWindow)
+            if ((Atom) event->xclient.data.l[0] == _glfwLibrary.X11.wmDeleteWindow)
             {
                 // The window manager was asked to close the window, for example by
                 // the user pressing a 'close' window decoration button
@@ -677,17 +672,17 @@ static void processSingleEvent(void)
                 _glfwInputWindowCloseRequest(window);
             }
             else if (_glfwLibrary.X11.wmPing != None &&
-                     (Atom) event.xclient.data.l[0] == _glfwLibrary.X11.wmPing)
+                     (Atom) event->xclient.data.l[0] == _glfwLibrary.X11.wmPing)
             {
                 // The window manager is pinging the application to ensure it's
                 // still responding to events
 
-                event.xclient.window = _glfwLibrary.X11.root;
+                event->xclient.window = _glfwLibrary.X11.root;
                 XSendEvent(_glfwLibrary.X11.display,
-                           event.xclient.window,
+                           event->xclient.window,
                            False,
                            SubstructureNotifyMask | SubstructureRedirectMask,
-                           &event);
+                           event);
             }
 
             break;
@@ -696,10 +691,11 @@ static void processSingleEvent(void)
         case MapNotify:
         {
             // The window was mapped
-            window = findWindow(event.xmap.window);
+            window = findWindow(event->xmap.window);
             if (window == NULL)
                 return;
 
+            _glfwInputWindowVisibility(window, GL_TRUE);
             _glfwInputWindowIconify(window, GL_FALSE);
             break;
         }
@@ -707,10 +703,11 @@ static void processSingleEvent(void)
         case UnmapNotify:
         {
             // The window was unmapped
-            window = findWindow(event.xmap.window);
+            window = findWindow(event->xmap.window);
             if (window == NULL)
                 return;
 
+            _glfwInputWindowVisibility(window, GL_FALSE);
             _glfwInputWindowIconify(window, GL_TRUE);
             break;
         }
@@ -718,7 +715,7 @@ static void processSingleEvent(void)
         case FocusIn:
         {
             // The window gained focus
-            window = findWindow(event.xfocus.window);
+            window = findWindow(event->xfocus.window);
             if (window == NULL)
                 return;
 
@@ -733,7 +730,7 @@ static void processSingleEvent(void)
         case FocusOut:
         {
             // The window lost focus
-            window = findWindow(event.xfocus.window);
+            window = findWindow(event->xfocus.window);
             if (window == NULL)
                 return;
 
@@ -748,7 +745,7 @@ static void processSingleEvent(void)
         case Expose:
         {
             // The window's contents was damaged
-            window = findWindow(event.xexpose.window);
+            window = findWindow(event->xexpose.window);
             if (window == NULL)
                 return;
 
@@ -769,7 +766,7 @@ static void processSingleEvent(void)
         {
             // The selection conversion status is available
 
-            XSelectionEvent* request = &event.xselection;
+            XSelectionEvent* request = &event->xselection;
 
             if (_glfwReadSelection(request))
                 _glfwLibrary.X11.selection.status = _GLFW_CONVERSION_SUCCEEDED;
@@ -783,7 +780,7 @@ static void processSingleEvent(void)
         {
             // The contents of the selection was requested
 
-            XSelectionRequestEvent* request = &event.xselectionrequest;
+            XSelectionRequestEvent* request = &event->xselectionrequest;
 
             XEvent response;
             memset(&response, 0, sizeof(response));
@@ -808,11 +805,11 @@ static void processSingleEvent(void)
         default:
         {
 #if defined(_GLFW_HAS_XRANDR)
-            switch (event.type - _glfwLibrary.X11.RandR.eventBase)
+            switch (event->type - _glfwLibrary.X11.RandR.eventBase)
             {
                 case RRScreenChangeNotify:
                 {
-                    XRRUpdateConfiguration(&event);
+                    XRRUpdateConfiguration(event);
                     _glfwRefreshMonitors();
                     break;
                 }
@@ -958,7 +955,6 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
 void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
 {
     int mode = 0, rate, sizeChanged = GL_FALSE;
-    XSizeHints* sizehints;
 
     rate = window->refreshRate;
 
@@ -972,14 +968,14 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
     {
         // Update window size restrictions to match new window size
 
-        sizehints = XAllocSizeHints();
-        sizehints->flags = 0;
+        XSizeHints* hints = XAllocSizeHints();
 
-        sizehints->min_width  = sizehints->max_width  = width;
-        sizehints->min_height = sizehints->max_height = height;
+        hints->flags |= (PMinSize | PMaxSize);
+        hints->min_width  = hints->max_width  = width;
+        hints->min_height = hints->max_height = height;
 
-        XSetWMNormalHints(_glfwLibrary.X11.display, window->X11.handle, sizehints);
-        XFree(sizehints);
+        XSetWMNormalHints(_glfwLibrary.X11.display, window->X11.handle, hints);
+        XFree(hints);
     }
 
     // Change window size before changing fullscreen mode?
@@ -1047,6 +1043,28 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window)
 }
 
 
+//========================================================================
+// Show window
+//========================================================================
+
+void _glfwPlatformShowWindow(_GLFWwindow* window)
+{
+    XMapRaised(_glfwLibrary.X11.display, window->X11.handle);
+    XFlush(_glfwLibrary.X11.display);
+}
+
+
+//========================================================================
+// Hide window
+//========================================================================
+
+void _glfwPlatformHideWindow(_GLFWwindow* window)
+{
+    XUnmapWindow(_glfwLibrary.X11.display, window->X11.handle);
+    XFlush(_glfwLibrary.X11.display);
+}
+
+
 //========================================================================
 // Read back framebuffer parameters from the context
 //========================================================================
@@ -1094,13 +1112,18 @@ void _glfwPlatformRefreshWindowParams(_GLFWwindow* window)
 
 void _glfwPlatformPollEvents(void)
 {
-    _GLFWwindow* window;
+    XEvent event;
 
-    // Process all pending events
-    while (XPending(_glfwLibrary.X11.display))
-        processSingleEvent();
+    while (XCheckMaskEvent(_glfwLibrary.X11.display, ~0, &event) ||
+           XCheckTypedEvent(_glfwLibrary.X11.display, ClientMessage, &event))
+    {
+        processEvent(&event);
+    }
 
-    // Did the cursor move in an active window that has captured the cursor
+    // Check whether the cursor has moved inside an active window that has
+    // captured the cursor (because then it needs to be re-centered)
+
+    _GLFWwindow* window;
     window = _glfwLibrary.activeWindow;
     if (window)
     {
@@ -1127,13 +1150,18 @@ void _glfwPlatformPollEvents(void)
 
 void _glfwPlatformWaitEvents(void)
 {
-    XEvent event;
+    int fd;
+    fd_set fds;
+
+    fd = ConnectionNumber(_glfwLibrary.X11.display);
 
-    // Block waiting for an event to arrive
-    XNextEvent(_glfwLibrary.X11.display, &event);
-    XPutBackEvent(_glfwLibrary.X11.display, &event);
+    FD_ZERO(&fds);
+    FD_SET(fd, &fds);
+
+    XFlush(_glfwLibrary.X11.display);
 
-    _glfwPlatformPollEvents();
+    if (select(fd + 1, &fds, NULL, NULL, NULL) > 0)
+        _glfwPlatformPollEvents();
 }
 
 

+ 588 - 0
support/tinycthread.c

@@ -0,0 +1,588 @@
+/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*-
+Copyright (c) 2012 Marcus Geelnard
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+    1. The origin of this software must not be misrepresented; you must not
+    claim that you wrote the original software. If you use this software
+    in a product, an acknowledgment in the product documentation would be
+    appreciated but is not required.
+
+    2. Altered source versions must be plainly marked as such, and must not be
+    misrepresented as being the original software.
+
+    3. This notice may not be removed or altered from any source
+    distribution.
+*/
+
+#include "tinycthread.h"
+#include <stdlib.h>
+
+/* Platform specific includes */
+#if defined(_TTHREAD_POSIX_)
+  #include <signal.h>
+  #include <sched.h>
+  #include <unistd.h>
+  #include <sys/time.h>
+  #include <errno.h>
+#elif defined(_TTHREAD_WIN32_)
+  #include <process.h>
+  #include <sys/timeb.h>
+#endif
+
+/* Standard, good-to-have defines */
+#ifndef NULL
+  #define NULL (void*)0
+#endif
+#ifndef TRUE
+  #define TRUE 1
+#endif
+#ifndef FALSE
+  #define FALSE 0
+#endif
+
+int mtx_init(mtx_t *mtx, int type)
+{
+#if defined(_TTHREAD_WIN32_)
+  mtx->mAlreadyLocked = FALSE;
+  mtx->mRecursive = type & mtx_recursive;
+  InitializeCriticalSection(&mtx->mHandle);
+  return thrd_success;
+#else
+  int ret;
+  pthread_mutexattr_t attr;
+  pthread_mutexattr_init(&attr);
+  if (type & mtx_recursive)
+  {
+    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+  }
+  ret = pthread_mutex_init(mtx, &attr);
+  pthread_mutexattr_destroy(&attr);
+  return ret == 0 ? thrd_success : thrd_error;
+#endif
+}
+
+void mtx_destroy(mtx_t *mtx)
+{
+#if defined(_TTHREAD_WIN32_)
+  DeleteCriticalSection(&mtx->mHandle);
+#else
+  pthread_mutex_destroy(mtx);
+#endif
+}
+
+int mtx_lock(mtx_t *mtx)
+{
+#if defined(_TTHREAD_WIN32_)
+  EnterCriticalSection(&mtx->mHandle);
+  if (!mtx->mRecursive)
+  {
+    while(mtx->mAlreadyLocked) Sleep(1000); /* Simulate deadlock... */
+    mtx->mAlreadyLocked = TRUE;
+  }
+  return thrd_success;
+#else
+  return pthread_mutex_lock(mtx) == 0 ? thrd_success : thrd_error;
+#endif
+}
+
+int mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
+{
+  /* FIXME! */
+  (void)mtx;
+  (void)ts;
+  return thrd_error;
+}
+
+int mtx_trylock(mtx_t *mtx)
+{
+#if defined(_TTHREAD_WIN32_)
+  int ret = TryEnterCriticalSection(&mtx->mHandle) ? thrd_success : thrd_busy;
+  if ((!mtx->mRecursive) && (ret == thrd_success) && mtx->mAlreadyLocked)
+  {
+    LeaveCriticalSection(&mtx->mHandle);
+    ret = thrd_busy;
+  }
+  return ret;
+#else
+  return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;
+#endif
+}
+
+int mtx_unlock(mtx_t *mtx)
+{
+#if defined(_TTHREAD_WIN32_)
+  mtx->mAlreadyLocked = FALSE;
+  LeaveCriticalSection(&mtx->mHandle);
+  return thrd_success;
+#else
+  return pthread_mutex_unlock(mtx) == 0 ? thrd_success : thrd_error;;
+#endif
+}
+
+#if defined(_TTHREAD_WIN32_)
+#define _CONDITION_EVENT_ONE 0
+#define _CONDITION_EVENT_ALL 1
+#endif
+
+int cnd_init(cnd_t *cond)
+{
+#if defined(_TTHREAD_WIN32_)
+  cond->mWaitersCount = 0;
+
+  /* Init critical section */
+  InitializeCriticalSection(&cond->mWaitersCountLock);
+
+  /* Init events */
+  cond->mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL);
+  if (cond->mEvents[_CONDITION_EVENT_ONE] == NULL)
+  {
+    cond->mEvents[_CONDITION_EVENT_ALL] = NULL;
+    return thrd_error;
+  }
+  cond->mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL);
+  if (cond->mEvents[_CONDITION_EVENT_ALL] == NULL)
+  {
+    CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
+    cond->mEvents[_CONDITION_EVENT_ONE] = NULL;
+    return thrd_error;
+  }
+
+  return thrd_success;
+#else
+  return pthread_cond_init(cond, NULL) == 0 ? thrd_success : thrd_error;
+#endif
+}
+
+void cnd_destroy(cnd_t *cond)
+{
+#if defined(_TTHREAD_WIN32_)
+  if (cond->mEvents[_CONDITION_EVENT_ONE] != NULL)
+  {
+    CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
+  }
+  if (cond->mEvents[_CONDITION_EVENT_ALL] != NULL)
+  {
+    CloseHandle(cond->mEvents[_CONDITION_EVENT_ALL]);
+  }
+  DeleteCriticalSection(&cond->mWaitersCountLock);
+#else
+  pthread_cond_destroy(cond);
+#endif
+}
+
+int cnd_signal(cnd_t *cond)
+{
+#if defined(_TTHREAD_WIN32_)
+  int haveWaiters;
+
+  /* Are there any waiters? */
+  EnterCriticalSection(&cond->mWaitersCountLock);
+  haveWaiters = (cond->mWaitersCount > 0);
+  LeaveCriticalSection(&cond->mWaitersCountLock);
+
+  /* If we have any waiting threads, send them a signal */
+  if(haveWaiters)
+  {
+    if (SetEvent(cond->mEvents[_CONDITION_EVENT_ONE]) == 0)
+    {
+      return thrd_error;
+    }
+  }
+
+  return thrd_success;
+#else
+  return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error;
+#endif
+}
+
+int cnd_broadcast(cnd_t *cond)
+{
+#if defined(_TTHREAD_WIN32_)
+  int haveWaiters;
+
+  /* Are there any waiters? */
+  EnterCriticalSection(&cond->mWaitersCountLock);
+  haveWaiters = (cond->mWaitersCount > 0);
+  LeaveCriticalSection(&cond->mWaitersCountLock);
+
+  /* If we have any waiting threads, send them a signal */
+  if(haveWaiters)
+  {
+    if (SetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
+    {
+      return thrd_error;
+    }
+  }
+
+  return thrd_success;
+#else
+  return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error;
+#endif
+}
+
+#if defined(_TTHREAD_WIN32_)
+static int _cnd_timedwait_win32(cnd_t *cond, mtx_t *mtx, DWORD timeout)
+{
+  int result, lastWaiter;
+
+  /* Increment number of waiters */
+  EnterCriticalSection(&cond->mWaitersCountLock);
+  ++ cond->mWaitersCount;
+  LeaveCriticalSection(&cond->mWaitersCountLock);
+
+  /* Release the mutex while waiting for the condition (will decrease
+     the number of waiters when done)... */
+  mtx_unlock(mtx);
+
+  /* Wait for either event to become signaled due to cnd_signal() or
+     cnd_broadcast() being called */
+  result = WaitForMultipleObjects(2, cond->mEvents, FALSE, timeout);
+  if (result == WAIT_TIMEOUT)
+  {
+    return thrd_timeout;
+  }
+  else if (result == (int)WAIT_FAILED)
+  {
+    return thrd_error;
+  }
+
+  /* Check if we are the last waiter */
+  EnterCriticalSection(&cond->mWaitersCountLock);
+  -- cond->mWaitersCount;
+  lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) &&
+               (cond->mWaitersCount == 0);
+  LeaveCriticalSection(&cond->mWaitersCountLock);
+
+  /* If we are the last waiter to be notified to stop waiting, reset the event */
+  if (lastWaiter)
+  {
+    if (ResetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
+    {
+      return thrd_error;
+    }
+  }
+
+  /* Re-acquire the mutex */
+  mtx_lock(mtx);
+
+  return thrd_success;
+}
+#endif
+
+int cnd_wait(cnd_t *cond, mtx_t *mtx)
+{
+#if defined(_TTHREAD_WIN32_)
+  return _cnd_timedwait_win32(cond, mtx, INFINITE);
+#else
+  return pthread_cond_wait(cond, mtx) == 0 ? thrd_success : thrd_error;
+#endif
+}
+
+int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts)
+{
+#if defined(_TTHREAD_WIN32_)
+  struct timespec now;
+  if (clock_gettime(TIME_UTC, &now) == 0)
+  {
+    DWORD delta = (ts->tv_sec - now.tv_sec) * 1000 +
+                  (ts->tv_nsec - now.tv_nsec + 500000) / 1000000;
+    return _cnd_timedwait_win32(cond, mtx, delta);
+  }
+  else
+    return thrd_error;
+#else
+  int ret;
+  ret = pthread_cond_timedwait(cond, mtx, ts);
+  if (ret == ETIMEDOUT)
+  {
+    return thrd_timeout;
+  }
+  return ret == 0 ? thrd_success : thrd_error;
+#endif
+}
+
+
+/** Information to pass to the new thread (what to run). */
+typedef struct {
+  thrd_start_t mFunction; /**< Pointer to the function to be executed. */
+  void * mArg;            /**< Function argument for the thread function. */
+} _thread_start_info;
+
+/* Thread wrapper function. */
+#if defined(_TTHREAD_WIN32_)
+static unsigned WINAPI _thrd_wrapper_function(void * aArg)
+#elif defined(_TTHREAD_POSIX_)
+static void * _thrd_wrapper_function(void * aArg)
+#endif
+{
+  thrd_start_t fun;
+  void *arg;
+  int  res;
+#if defined(_TTHREAD_POSIX_)
+  void *pres;
+#endif
+
+  /* Get thread startup information */
+  _thread_start_info *ti = (_thread_start_info *) aArg;
+  fun = ti->mFunction;
+  arg = ti->mArg;
+
+  /* The thread is responsible for freeing the startup information */
+  free((void *)ti);
+
+  /* Call the actual client thread function */
+  res = fun(arg);
+
+#if defined(_TTHREAD_WIN32_)
+  return res;
+#else
+  pres = malloc(sizeof(int));
+  if (pres != NULL)
+  {
+    *(int*)pres = res;
+  }
+  return pres;
+#endif
+}
+
+int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
+{
+  /* Fill out the thread startup information (passed to the thread wrapper,
+     which will eventually free it) */
+  _thread_start_info* ti = (_thread_start_info*)malloc(sizeof(_thread_start_info));
+  if (ti == NULL)
+  {
+    return thrd_nomem;
+  }
+  ti->mFunction = func;
+  ti->mArg = arg;
+
+  /* Create the thread */
+#if defined(_TTHREAD_WIN32_)
+  *thr = (HANDLE)_beginthreadex(NULL, 0, _thrd_wrapper_function, (void *)ti, 0, NULL);
+#elif defined(_TTHREAD_POSIX_)
+  if(pthread_create(thr, NULL, _thrd_wrapper_function, (void *)ti) != 0)
+  {
+    *thr = 0;
+  }
+#endif
+
+  /* Did we fail to create the thread? */
+  if(!*thr)
+  {
+    free(ti);
+    return thrd_error;
+  }
+
+  return thrd_success;
+}
+
+thrd_t thrd_current(void)
+{
+#if defined(_TTHREAD_WIN32_)
+  return GetCurrentThread();
+#else
+  return pthread_self();
+#endif
+}
+
+int thrd_detach(thrd_t thr)
+{
+  /* FIXME! */
+  (void)thr;
+  return thrd_error;
+}
+
+int thrd_equal(thrd_t thr0, thrd_t thr1)
+{
+#if defined(_TTHREAD_WIN32_)
+  return thr0 == thr1;
+#else
+  return pthread_equal(thr0, thr1);
+#endif
+}
+
+void thrd_exit(int res)
+{
+#if defined(_TTHREAD_WIN32_)
+  ExitThread(res);
+#else
+  void *pres = malloc(sizeof(int));
+  if (pres != NULL)
+  {
+    *(int*)pres = res;
+  }
+  pthread_exit(pres);
+#endif
+}
+
+int thrd_join(thrd_t thr, int *res)
+{
+#if defined(_TTHREAD_WIN32_)
+  if (WaitForSingleObject(thr, INFINITE) == WAIT_FAILED)
+  {
+    return thrd_error;
+  }
+  if (res != NULL)
+  {
+    DWORD dwRes;
+    GetExitCodeThread(thr, &dwRes);
+    *res = dwRes;
+  }
+#elif defined(_TTHREAD_POSIX_)
+  void *pres;
+  int ires = 0;
+  if (pthread_join(thr, &pres) != 0)
+  {
+    return thrd_error;
+  }
+  if (pres != NULL)
+  {
+    ires = *(int*)pres;
+    free(pres);
+  }
+  if (res != NULL)
+  {
+    *res = ires;
+  }
+#endif
+  return thrd_success;
+}
+
+int thrd_sleep(const struct timespec *time_point, struct timespec *remaining)
+{
+  struct timespec now;
+#if defined(_TTHREAD_WIN32_)
+  DWORD delta;
+#else
+  long delta;
+#endif
+
+  /* Get the current time */
+  if (clock_gettime(TIME_UTC, &now) != 0)
+    return -2;  // FIXME: Some specific error code?
+
+#if defined(_TTHREAD_WIN32_)
+  /* Delta in milliseconds */
+  delta = (time_point->tv_sec - now.tv_sec) * 1000 +
+          (time_point->tv_nsec - now.tv_nsec + 500000) / 1000000;
+  if (delta > 0)
+  {
+    Sleep(delta);
+  }
+#else
+  /* Delta in microseconds */
+  delta = (time_point->tv_sec - now.tv_sec) * 1000000L +
+          (time_point->tv_nsec - now.tv_nsec + 500L) / 1000L;
+
+  /* On some systems, the usleep argument must be < 1000000 */
+  while (delta > 999999L)
+  {
+    usleep(999999);
+    delta -= 999999L;
+  }
+  if (delta > 0L)
+  {
+    usleep((useconds_t)delta);
+  }
+#endif
+
+  /* We don't support waking up prematurely (yet) */
+  if (remaining)
+  {
+    remaining->tv_sec = 0;
+    remaining->tv_nsec = 0;
+  }
+  return 0;
+}
+
+void thrd_yield(void)
+{
+#if defined(_TTHREAD_WIN32_)
+  Sleep(0);
+#else
+  sched_yield();
+#endif
+}
+
+int tss_create(tss_t *key, tss_dtor_t dtor)
+{
+#if defined(_TTHREAD_WIN32_)
+  /* FIXME: The destructor function is not supported yet... */
+  if (dtor != NULL)
+  {
+    return thrd_error;
+  }
+  *key = TlsAlloc();
+  if (*key == TLS_OUT_OF_INDEXES)
+  {
+    return thrd_error;
+  }
+#else
+  if (pthread_key_create(key, dtor) != 0)
+  {
+    return thrd_error;
+  }
+#endif
+  return thrd_success;
+}
+
+void tss_delete(tss_t key)
+{
+#if defined(_TTHREAD_WIN32_)
+  TlsFree(key);
+#else
+  pthread_key_delete(key);
+#endif
+}
+
+void *tss_get(tss_t key)
+{
+#if defined(_TTHREAD_WIN32_)
+  return TlsGetValue(key);
+#else
+  return pthread_getspecific(key);
+#endif
+}
+
+int tss_set(tss_t key, void *val)
+{
+#if defined(_TTHREAD_WIN32_)
+  if (TlsSetValue(key, val) == 0)
+  {
+    return thrd_error;
+  }
+#else
+  if (pthread_setspecific(key, val) != 0)
+  {
+    return thrd_error;
+  }
+#endif
+  return thrd_success;
+}
+
+#if defined(_TTHREAD_EMULATE_CLOCK_GETTIME_)
+int _tthread_clock_gettime(clockid_t clk_id, struct timespec *ts)
+{
+#if defined(_TTHREAD_WIN32_)
+  struct _timeb tb;
+  _ftime(&tb);
+  ts->tv_sec = (time_t)tb.time;
+  ts->tv_nsec = 1000000L * (long)tb.millitm;
+#else
+  struct timeval tv;
+  gettimeofday(&tv, NULL);
+  ts->tv_sec = (time_t)tv.tv_sec;
+  ts->tv_nsec = 1000L * (long)tv.tv_usec;
+#endif
+  return 0;
+}
+#endif // _TTHREAD_EMULATE_CLOCK_GETTIME_
+

+ 439 - 0
support/tinycthread.h

@@ -0,0 +1,439 @@
+/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*-
+Copyright (c) 2012 Marcus Geelnard
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+    1. The origin of this software must not be misrepresented; you must not
+    claim that you wrote the original software. If you use this software
+    in a product, an acknowledgment in the product documentation would be
+    appreciated but is not required.
+
+    2. Altered source versions must be plainly marked as such, and must not be
+    misrepresented as being the original software.
+
+    3. This notice may not be removed or altered from any source
+    distribution.
+*/
+
+#ifndef _TINYCTHREAD_H_
+#define _TINYCTHREAD_H_
+
+/**
+* @file
+* @mainpage TinyCThread API Reference
+*
+* @section intro_sec Introduction
+* TinyCThread is a minimal, portable implementation of basic threading
+* classes for C.
+*
+* They closely mimic the functionality and naming of the C11 standard, and
+* should be easily replaceable with the corresponding standard variants.
+*
+* @section port_sec Portability
+* The Win32 variant uses the native Win32 API for implementing the thread
+* classes, while for other systems, the POSIX threads API (pthread) is used.
+*
+* @section misc_sec Miscellaneous
+* The following special keywords are available: #_Thread_local.
+*
+* For more detailed information, browse the different sections of this
+* documentation. A good place to start is:
+* tinycthread.h.
+*/
+
+/* Which platform are we on? */
+#if !defined(_TTHREAD_PLATFORM_DEFINED_)
+  #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__)
+    #define _TTHREAD_WIN32_
+  #else
+    #define _TTHREAD_POSIX_
+  #endif
+  #define _TTHREAD_PLATFORM_DEFINED_
+#endif
+
+/* Activate some POSIX functionality (e.g. clock_gettime and recursive mutexes) */
+#if defined(_TTHREAD_POSIX_)
+  #undef _FEATURES_H
+  #if !defined(_GNU_SOURCE)
+    #define _GNU_SOURCE
+  #endif
+  #if !defined(_POSIX_C_SOURCE) || ((_POSIX_C_SOURCE - 0) < 199309L)
+    #undef _POSIX_C_SOURCE
+    #define _POSIX_C_SOURCE 199309L
+  #endif
+  #if !defined(_XOPEN_SOURCE) || ((_XOPEN_SOURCE - 0) < 500)
+    #undef _XOPEN_SOURCE
+    #define _XOPEN_SOURCE 500
+  #endif
+#endif
+
+/* Generic includes */
+#include <time.h>
+
+/* Platform specific includes */
+#if defined(_TTHREAD_POSIX_)
+  #include <pthread.h>
+#elif defined(_TTHREAD_WIN32_)
+  #ifndef WIN32_LEAN_AND_MEAN
+    #define WIN32_LEAN_AND_MEAN
+    #define __UNDEF_LEAN_AND_MEAN
+  #endif
+  #include <windows.h>
+  #ifdef __UNDEF_LEAN_AND_MEAN
+    #undef WIN32_LEAN_AND_MEAN
+    #undef __UNDEF_LEAN_AND_MEAN
+  #endif
+#endif
+
+/* Workaround for missing TIME_UTC: If time.h doesn't provide TIME_UTC,
+   it's quite likely that libc does not support it either. Hence, fall back to
+   the only other supported time specifier: CLOCK_REALTIME (and if that fails,
+   we're probably emulating clock_gettime anyway, so anything goes). */
+#ifndef TIME_UTC
+  #ifdef CLOCK_REALTIME
+    #define TIME_UTC CLOCK_REALTIME
+  #else
+    #define TIME_UTC 0
+  #endif
+#endif
+
+/* Workaround for missing clock_gettime (most Windows compilers, afaik) */
+#if defined(_TTHREAD_WIN32_) || defined(__APPLE_CC__)
+#define _TTHREAD_EMULATE_CLOCK_GETTIME_
+/* Emulate struct timespec */
+#if defined(_TTHREAD_WIN32_)
+struct _ttherad_timespec {
+  time_t tv_sec;
+  long   tv_nsec;
+};
+#define timespec _ttherad_timespec
+#endif
+
+/* Emulate clockid_t */
+typedef int _tthread_clockid_t;
+#define clockid_t _tthread_clockid_t
+
+/* Emulate clock_gettime */
+int _tthread_clock_gettime(clockid_t clk_id, struct timespec *ts);
+#define clock_gettime _tthread_clock_gettime
+#endif
+
+
+/** TinyCThread version (major number). */
+#define TINYCTHREAD_VERSION_MAJOR 1
+/** TinyCThread version (minor number). */
+#define TINYCTHREAD_VERSION_MINOR 1
+/** TinyCThread version (full version). */
+#define TINYCTHREAD_VERSION (TINYCTHREAD_VERSION_MAJOR * 100 + TINYCTHREAD_VERSION_MINOR)
+
+/**
+* @def _Thread_local
+* Thread local storage keyword.
+* A variable that is declared with the @c _Thread_local keyword makes the
+* value of the variable local to each thread (known as thread-local storage,
+* or TLS). Example usage:
+* @code
+* // This variable is local to each thread.
+* _Thread_local int variable;
+* @endcode
+* @note The @c _Thread_local keyword is a macro that maps to the corresponding
+* compiler directive (e.g. @c __declspec(thread)).
+* @note This directive is currently not supported on Mac OS X (it will give
+* a compiler error), since compile-time TLS is not supported in the Mac OS X
+* executable format. Also, some older versions of MinGW (before GCC 4.x) do
+* not support this directive.
+* @hideinitializer
+*/
+
+/* FIXME: Check for a PROPER value of __STDC_VERSION__ to know if we have C11 */
+#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) && !defined(_Thread_local)
+ #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__)
+  #define _Thread_local __thread
+ #else
+  #define _Thread_local __declspec(thread)
+ #endif
+#endif
+
+/* Macros */
+#define TSS_DTOR_ITERATIONS 0
+
+/* Function return values */
+#define thrd_error    0 /**< The requested operation failed */
+#define thrd_success  1 /**< The requested operation succeeded */
+#define thrd_timeout  2 /**< The time specified in the call was reached without acquiring the requested resource */
+#define thrd_busy     3 /**< The requested operation failed because a tesource requested by a test and return function is already in use */
+#define thrd_nomem    4 /**< The requested operation failed because it was unable to allocate memory */
+
+/* Mutex types */
+#define mtx_plain     1
+#define mtx_timed     2
+#define mtx_try       4
+#define mtx_recursive 8
+
+/* Mutex */
+#if defined(_TTHREAD_WIN32_)
+typedef struct {
+  CRITICAL_SECTION mHandle;   /* Critical section handle */
+  int mAlreadyLocked;         /* TRUE if the mutex is already locked */
+  int mRecursive;             /* TRUE if the mutex is recursive */
+} mtx_t;
+#else
+typedef pthread_mutex_t mtx_t;
+#endif
+
+/** Create a mutex object.
+* @param mtx A mutex object.
+* @param type Bit-mask that must have one of the following six values:
+*   @li @c mtx_plain for a simple non-recursive mutex
+*   @li @c mtx_timed for a non-recursive mutex that supports timeout
+*   @li @c mtx_try for a non-recursive mutex that supports test and return
+*   @li @c mtx_plain | @c mtx_recursive (same as @c mtx_plain, but recursive)
+*   @li @c mtx_timed | @c mtx_recursive (same as @c mtx_timed, but recursive)
+*   @li @c mtx_try | @c mtx_recursive (same as @c mtx_try, but recursive)
+* @return @ref thrd_success on success, or @ref thrd_error if the request could
+* not be honored.
+*/
+int mtx_init(mtx_t *mtx, int type);
+
+/** Release any resources used by the given mutex.
+* @param mtx A mutex object.
+*/
+void mtx_destroy(mtx_t *mtx);
+
+/** Lock the given mutex.
+* Blocks until the given mutex can be locked. If the mutex is non-recursive, and
+* the calling thread already has a lock on the mutex, this call will block
+* forever.
+* @param mtx A mutex object.
+* @return @ref thrd_success on success, or @ref thrd_error if the request could
+* not be honored.
+*/
+int mtx_lock(mtx_t *mtx);
+
+/** NOT YET IMPLEMENTED.
+*/
+int mtx_timedlock(mtx_t *mtx, const struct timespec *ts);
+
+/** Try to lock the given mutex.
+* The specified mutex shall support either test and return or timeout. If the
+* mutex is already locked, the function returns without blocking.
+* @param mtx A mutex object.
+* @return @ref thrd_success on success, or @ref thrd_busy if the resource
+* requested is already in use, or @ref thrd_error if the request could not be
+* honored.
+*/
+int mtx_trylock(mtx_t *mtx);
+
+/** Unlock the given mutex.
+* @param mtx A mutex object.
+* @return @ref thrd_success on success, or @ref thrd_error if the request could
+* not be honored.
+*/
+int mtx_unlock(mtx_t *mtx);
+
+/* Condition variable */
+#if defined(_TTHREAD_WIN32_)
+typedef struct {
+  HANDLE mEvents[2];                  /* Signal and broadcast event HANDLEs. */
+  unsigned int mWaitersCount;         /* Count of the number of waiters. */
+  CRITICAL_SECTION mWaitersCountLock; /* Serialize access to mWaitersCount. */
+} cnd_t;
+#else
+typedef pthread_cond_t cnd_t;
+#endif
+
+/** Create a condition variable object.
+* @param cond A condition variable object.
+* @return @ref thrd_success on success, or @ref thrd_error if the request could
+* not be honored.
+*/
+int cnd_init(cnd_t *cond);
+
+/** Release any resources used by the given condition variable.
+* @param cond A condition variable object.
+*/
+void cnd_destroy(cnd_t *cond);
+
+/** Signal a condition variable.
+* Unblocks one of the threads that are blocked on the given condition variable
+* at the time of the call. If no threads are blocked on the condition variable
+* at the time of the call, the function does nothing and return success.
+* @param cond A condition variable object.
+* @return @ref thrd_success on success, or @ref thrd_error if the request could
+* not be honored.
+*/
+int cnd_signal(cnd_t *cond);
+
+/** Broadcast a condition variable.
+* Unblocks all of the threads that are blocked on the given condition variable
+* at the time of the call. If no threads are blocked on the condition variable
+* at the time of the call, the function does nothing and return success.
+* @param cond A condition variable object.
+* @return @ref thrd_success on success, or @ref thrd_error if the request could
+* not be honored.
+*/
+int cnd_broadcast(cnd_t *cond);
+
+/** Wait for a condition variable to become signaled.
+* The function atomically unlocks the given mutex and endeavors to block until
+* the given condition variable is signaled by a call to cnd_signal or to
+* cnd_broadcast. When the calling thread becomes unblocked it locks the mutex
+* before it returns.
+* @param cond A condition variable object.
+* @param mtx A mutex object.
+* @return @ref thrd_success on success, or @ref thrd_error if the request could
+* not be honored.
+*/
+int cnd_wait(cnd_t *cond, mtx_t *mtx);
+
+/** Wait for a condition variable to become signaled.
+* The function atomically unlocks the given mutex and endeavors to block until
+* the given condition variable is signaled by a call to cnd_signal or to
+* cnd_broadcast, or until after the specified time. When the calling thread
+* becomes unblocked it locks the mutex before it returns.
+* @param cond A condition variable object.
+* @param mtx A mutex object.
+* @param xt A point in time at which the request will time out (absolute time).
+* @return @ref thrd_success upon success, or @ref thrd_timeout if the time
+* specified in the call was reached without acquiring the requested resource, or
+* @ref thrd_error if the request could not be honored.
+*/
+int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts);
+
+/* Thread */
+#if defined(_TTHREAD_WIN32_)
+typedef HANDLE thrd_t;
+#else
+typedef pthread_t thrd_t;
+#endif
+
+/** Thread start function.
+* Any thread that is started with the @ref thrd_create() function must be
+* started through a function of this type.
+* @param arg The thread argument (the @c arg argument of the corresponding
+*        @ref thrd_create() call).
+* @return The thread return value, which can be obtained by another thread
+* by using the @ref thrd_join() function.
+*/
+typedef int (*thrd_start_t)(void *arg);
+
+/** Create a new thread.
+* @param thr Identifier of the newly created thread.
+* @param func A function pointer to the function that will be executed in
+*        the new thread.
+* @param arg An argument to the thread function.
+* @return @ref thrd_success on success, or @ref thrd_nomem if no memory could
+* be allocated for the thread requested, or @ref thrd_error if the request
+* could not be honored.
+* @note A thread’s identifier may be reused for a different thread once the
+* original thread has exited and either been detached or joined to another
+* thread.
+*/
+int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
+
+/** Identify the calling thread.
+* @return The identifier of the calling thread.
+*/
+thrd_t thrd_current(void);
+
+/** NOT YET IMPLEMENTED.
+*/
+int thrd_detach(thrd_t thr);
+
+/** Compare two thread identifiers.
+* The function determines if two thread identifiers refer to the same thread.
+* @return Zero if the two thread identifiers refer to different threads.
+* Otherwise a nonzero value is returned.
+*/
+int thrd_equal(thrd_t thr0, thrd_t thr1);
+
+/** Terminate execution of the calling thread.
+* @param res Result code of the calling thread.
+*/
+void thrd_exit(int res);
+
+/** Wait for a thread to terminate.
+* The function joins the given thread with the current thread by blocking
+* until the other thread has terminated.
+* @param thr The thread to join with.
+* @param res If this pointer is not NULL, the function will store the result
+*        code of the given thread in the integer pointed to by @c res.
+* @return @ref thrd_success on success, or @ref thrd_error if the request could
+* not be honored.
+*/
+int thrd_join(thrd_t thr, int *res);
+
+/** Put the calling thread to sleep.
+* Suspend execution of the calling thread.
+* @param time_point A point in time at which the thread will resume (absolute time).
+* @param remaining If non-NULL, this parameter will hold the remaining time until
+*                  time_point upon return. This will typically be zero, but if
+*                  the thread was woken up by a signal that is not ignored before
+*                  time_point was reached @c remaining will hold a positive
+*                  time.
+* @return 0 (zero) on successful sleep, or -1 if an interrupt occurred.
+*/
+int thrd_sleep(const struct timespec *time_point, struct timespec *remaining);
+
+/** Yield execution to another thread.
+* Permit other threads to run, even if the current thread would ordinarily
+* continue to run.
+*/
+void thrd_yield(void);
+
+/* Thread local storage */
+#if defined(_TTHREAD_WIN32_)
+typedef DWORD tss_t;
+#else
+typedef pthread_key_t tss_t;
+#endif
+
+/** Destructor function for a thread-specific storage.
+* @param val The value of the destructed thread-specific storage.
+*/
+typedef void (*tss_dtor_t)(void *val);
+
+/** Create a thread-specific storage.
+* @param key The unique key identifier that will be set if the function is
+*        successful.
+* @param dtor Destructor function. This can be NULL.
+* @return @ref thrd_success on success, or @ref thrd_error if the request could
+* not be honored.
+* @note The destructor function is not supported under Windows. If @c dtor is
+* not NULL when calling this function under Windows, the function will fail
+* and return @ref thrd_error.
+*/
+int tss_create(tss_t *key, tss_dtor_t dtor);
+
+/** Delete a thread-specific storage.
+* The function releases any resources used by the given thread-specific
+* storage.
+* @param key The key that shall be deleted.
+*/
+void tss_delete(tss_t key);
+
+/** Get the value for a thread-specific storage.
+* @param key The thread-specific storage identifier.
+* @return The value for the current thread held in the given thread-specific
+* storage.
+*/
+void *tss_get(tss_t key);
+
+/** Set the value for a thread-specific storage.
+* @param key The thread-specific storage identifier.
+* @param val The value of the thread-specific storage to set for the current
+*        thread.
+* @return @ref thrd_success on success, or @ref thrd_error if the request could
+* not be honored.
+*/
+int tss_set(tss_t key, void *val);
+
+
+#endif /* _TINYTHREAD_H_ */
+

+ 15 - 1
tests/CMakeLists.txt

@@ -8,12 +8,19 @@ else()
     link_libraries(${glfw_LIBRARIES})
 endif()
 
+list(APPEND thread_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})
+if (UNIX AND NOT APPLE)
+    list(APPEND thread_LIBRARIES ${RT_LIBRARY})
+endif()
+
 include_directories(${GLFW_SOURCE_DIR}/include
                     ${GLFW_SOURCE_DIR}/support
                     ${OPENGL_INCLUDE_DIR})
 
 set(GETOPT ${GLFW_SOURCE_DIR}/support/getopt.h
            ${GLFW_SOURCE_DIR}/support/getopt.c)
+set(TINYCTHREAD ${GLFW_SOURCE_DIR}/support/tinycthread.h
+                ${GLFW_SOURCE_DIR}/support/tinycthread.c) 
 
 add_executable(clipboard clipboard.c ${GETOPT})
 add_executable(defaults defaults.c)
@@ -37,13 +44,20 @@ set_target_properties(sharing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Sharing")
 add_executable(tearing WIN32 MACOSX_BUNDLE tearing.c)
 set_target_properties(tearing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Tearing")
 
+add_executable(threads WIN32 MACOSX_BUNDLE threads.c ${TINYCTHREAD})
+set_target_properties(threads PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Threads")
+
 add_executable(title WIN32 MACOSX_BUNDLE title.c)
 set_target_properties(title PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Title")
 
 add_executable(windows WIN32 MACOSX_BUNDLE windows.c)
 set_target_properties(windows PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Windows")
 
-set(WINDOWS_BINARIES accuracy sharing tearing title windows)
+if (BUILD_SHARED_LIBS)
+    target_link_libraries(threads ${thread_LIBRARIES})
+endif()
+
+set(WINDOWS_BINARIES accuracy sharing tearing threads title windows)
 set(CONSOLE_BINARIES clipboard defaults events fsaa fsfocus gamma glfwinfo
                      iconify joysticks modes peter reopen)
 

+ 2 - 0
tests/defaults.c

@@ -83,6 +83,8 @@ int main(void)
         exit(EXIT_FAILURE);
     }
 
+    glfwWindowHint(GLFW_VISIBLE, GL_FALSE);
+
     window = glfwCreateWindow(0, 0, GLFW_WINDOWED, "Defaults", NULL);
     if (!window)
     {

+ 5 - 0
tests/glfwinfo.c

@@ -238,6 +238,8 @@ int main(int argc, char** argv)
     if (strategy)
         glfwWindowHint(GLFW_OPENGL_ROBUSTNESS, strategy);
 
+    glfwWindowHint(GLFW_VISIBLE, GL_FALSE);
+
     // We assume here that we stand a better chance of success by leaving all
     // possible details of pixel format selection to GLFW
 
@@ -288,6 +290,9 @@ int main(int argc, char** argv)
                get_glfw_profile_name(glfwGetWindowParam(window, GLFW_OPENGL_PROFILE)));
     }
 
+    printf("OpenGL context debug flag saved by GLFW: %s\n",
+           glfwGetWindowParam(window, GLFW_OPENGL_DEBUG_CONTEXT) ? "true" : "false");
+
     printf("OpenGL context renderer string: \"%s\"\n", glGetString(GL_RENDERER));
     printf("OpenGL context vendor string: \"%s\"\n", glGetString(GL_VENDOR));
 

+ 131 - 0
tests/threads.c

@@ -0,0 +1,131 @@
+//========================================================================
+// Multithreading test
+// Copyright (c) Camilla Berglund <[email protected]>
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would
+//    be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+//    be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+//    distribution.
+//
+//========================================================================
+//
+// This test is intended to verify whether the OpenGL context part of
+// the GLFW API is able to be used from multiple threads
+//
+//========================================================================
+
+#include <GL/glfw3.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <assert.h>
+
+#include "tinycthread.h"
+
+typedef struct
+{
+    GLFWwindow window;
+    const char* title;
+    float r, g, b;
+    thrd_t id;
+} Thread;
+
+static volatile GLboolean running = GL_TRUE;
+
+static int thread_main(void* data)
+{
+    const Thread* thread = (const Thread*) data;
+
+    glfwMakeContextCurrent(thread->window);
+    assert(glfwGetCurrentContext() == thread->window);
+
+    glfwSwapInterval(1);
+
+    while (running)
+    {
+        const float v = (float) fabs(sin(glfwGetTime() * 2.f));
+        glClearColor(thread->r * v, thread->g * v, thread->b * v, 0.f);
+
+        glClear(GL_COLOR_BUFFER_BIT);
+        glfwSwapBuffers(thread->window);
+    }
+
+    glfwMakeContextCurrent(NULL);
+    return 0;
+}
+
+int main(void)
+{
+    int i, result;
+    Thread threads[] =
+    {
+        { NULL, "Red", 1.f, 0.f, 0.f, 0 },
+        { NULL, "Green", 0.f, 1.f, 0.f, 0 },
+        { NULL, "Blue", 0.f, 0.f, 1.f, 0 }
+    };
+    const int count = sizeof(threads) / sizeof(Thread);
+
+    if (!glfwInit())
+    {
+        fprintf(stderr, "Failed to initialize GLFW: %s\n",
+                glfwErrorString(glfwGetError()));
+        exit(EXIT_FAILURE);
+    }
+
+    for (i = 0;  i < count;  i++)
+    {
+        threads[i].window = glfwCreateWindow(200, 200,
+                                             GLFW_WINDOWED,
+                                             threads[i].title,
+                                             NULL);
+        if (!threads[i].window)
+        {
+            fprintf(stderr, "Failed to open GLFW window: %s\n",
+                    glfwErrorString(glfwGetError()));
+            exit(EXIT_FAILURE);
+        }
+
+        glfwSetWindowPos(threads[i].window, 200 + 250 * i, 200);
+
+        if (thrd_create(&threads[i].id, thread_main, threads + i) !=
+            thrd_success)
+        {
+            fprintf(stderr, "Failed to create secondary thread\n");
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    while (running)
+    {
+        assert(glfwGetCurrentContext() == NULL);
+
+        glfwWaitEvents();
+
+        for (i = 0;  i < count;  i++)
+        {
+            if (glfwGetWindowParam(threads[i].window, GLFW_CLOSE_REQUESTED))
+                running = GL_FALSE;
+        }
+    }
+
+    for (i = 0;  i < count;  i++)
+        thrd_join(threads[i].id, &result);
+
+    exit(EXIT_SUCCESS);
+}
+