mirror of
https://github.com/glfw/glfw.git
synced 2024-11-26 20:11:58 +00:00
Merge branch 'master' into EGL
Conflicts: src/glx_opengl.c src/opengl.c tests/glfwinfo.c
This commit is contained in:
commit
0272ce2e98
19
.gitignore
vendored
19
.gitignore
vendored
@ -6,13 +6,13 @@ cmake_uninstall.cmake
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
docs/Doxyfile
|
docs/Doxyfile
|
||||||
src/config.h
|
src/config.h
|
||||||
src/libglfw.pc
|
src/glfw3.pc
|
||||||
src/libglfw.so
|
src/libglfw3.so
|
||||||
src/libglfw.a
|
src/libglfw3.a
|
||||||
src/libglfw.dylib
|
src/libglfw3.dylib
|
||||||
src/glfw.lib
|
src/glfw3.lib
|
||||||
src/glfw.dll
|
src/glfw3.dll
|
||||||
src/glfwdll.lib
|
src/glfw3dll.lib
|
||||||
examples/boing
|
examples/boing
|
||||||
examples/gears
|
examples/gears
|
||||||
examples/heightmap
|
examples/heightmap
|
||||||
@ -22,8 +22,8 @@ examples/wave
|
|||||||
examples/*.app
|
examples/*.app
|
||||||
examples/*.exe
|
examples/*.exe
|
||||||
tests/accuracy
|
tests/accuracy
|
||||||
|
tests/clipboard
|
||||||
tests/defaults
|
tests/defaults
|
||||||
tests/dynamic
|
|
||||||
tests/events
|
tests/events
|
||||||
tests/fsaa
|
tests/fsaa
|
||||||
tests/fsfocus
|
tests/fsfocus
|
||||||
@ -31,11 +31,12 @@ tests/gamma
|
|||||||
tests/glfwinfo
|
tests/glfwinfo
|
||||||
tests/iconify
|
tests/iconify
|
||||||
tests/joysticks
|
tests/joysticks
|
||||||
tests/listmodes
|
tests/modes
|
||||||
tests/peter
|
tests/peter
|
||||||
tests/reopen
|
tests/reopen
|
||||||
tests/sharing
|
tests/sharing
|
||||||
tests/tearing
|
tests/tearing
|
||||||
|
tests/threads
|
||||||
tests/title
|
tests/title
|
||||||
tests/windows
|
tests/windows
|
||||||
tests/*.app
|
tests/*.app
|
||||||
|
@ -22,6 +22,12 @@ else()
|
|||||||
find_package(OpenGL REQUIRED)
|
find_package(OpenGL REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
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
|
# Enable all warnings on GCC, regardless of OS
|
||||||
#--------------------------------------------------------------------
|
#--------------------------------------------------------------------
|
||||||
|
@ -287,6 +287,7 @@ version of GLFW.</p>
|
|||||||
<li>Added <code>windows</code> simple multi-window test program</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>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>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>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 <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>
|
<li>Added window parameter to <code>glfwSwapBuffers</code></li>
|
||||||
@ -349,6 +350,7 @@ version of GLFW.</p>
|
|||||||
<li>[X11] Bugfix: Some window properties required by the ICCCM were not set</li>
|
<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: 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: 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] Changed port to use Unicode mode only</li>
|
||||||
<li>[Win32] Removed explicit support for versions of Windows older than Windows XP</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>
|
<li>[Win32] Bugfix: Window activation and iconification did not work as expected</li>
|
||||||
@ -907,7 +909,7 @@ their skills. Special thanks go out to:</p>
|
|||||||
language</li>
|
language</li>
|
||||||
|
|
||||||
<li>Shane Liesegang, for providing a bug fix relating to Cocoa window
|
<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>
|
<li>Tristam MacDonald, for his bug reports and feedback on the Cocoa port</li>
|
||||||
|
|
||||||
@ -924,9 +926,14 @@ their skills. Special thanks go out to:</p>
|
|||||||
Much of the Windows code of GLFW was originally based on Jeff's
|
Much of the Windows code of GLFW was originally based on Jeff's
|
||||||
code</li>
|
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
|
<li>Arturo J. Pérez, for a bug fix for cursor tracking on Mac OS X 10.6 Snow
|
||||||
Leopard</li>
|
Leopard</li>
|
||||||
|
|
||||||
|
<li>Riku Salminen, for making the X11 event processing able to support
|
||||||
|
multi-threaded rendering</li>
|
||||||
|
|
||||||
<li>Douglas C. Schmidt and Irfan Pyarali, for their excellent article
|
<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>
|
<a href="http://www.cs.wustl.edu/~schmidt/win32-cv-1.html">Strategies for Implementing POSIX Condition Variables on Win32</a></li>
|
||||||
|
|
||||||
|
@ -103,6 +103,9 @@ int _glfwPlatformInit(void)
|
|||||||
|
|
||||||
_glfwInitJoysticks();
|
_glfwInitJoysticks();
|
||||||
|
|
||||||
|
if (!_glfwInitOpenGL())
|
||||||
|
return GL_FALSE;
|
||||||
|
|
||||||
_glfwLibrary.NS.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
|
_glfwLibrary.NS.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
|
||||||
if (!_glfwLibrary.NS.eventSource)
|
if (!_glfwLibrary.NS.eventSource)
|
||||||
return GL_FALSE;
|
return GL_FALSE;
|
||||||
@ -143,6 +146,8 @@ int _glfwPlatformTerminate(void)
|
|||||||
|
|
||||||
_glfwTerminateJoysticks();
|
_glfwTerminateJoysticks();
|
||||||
|
|
||||||
|
_glfwTerminateOpenGL();
|
||||||
|
|
||||||
return GL_TRUE;
|
return GL_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -550,15 +550,15 @@ int _glfwPlatformGetJoystickAxes(int joy, float* axes, int numaxes)
|
|||||||
|
|
||||||
for (i = 0; i < numaxes; i++)
|
for (i = 0; i < numaxes; i++)
|
||||||
{
|
{
|
||||||
_glfwJoystickElement* axes =
|
_glfwJoystickElement* elements =
|
||||||
(_glfwJoystickElement*) CFArrayGetValueAtIndex(joystick.axes, i);
|
(_glfwJoystickElement*) CFArrayGetValueAtIndex(joystick.axes, i);
|
||||||
|
|
||||||
long readScale = axes->maxReport - axes->minReport;
|
long readScale = elements->maxReport - elements->minReport;
|
||||||
|
|
||||||
if (readScale == 0)
|
if (readScale == 0)
|
||||||
axes[i] = axes->value;
|
axes[i] = elements->value;
|
||||||
else
|
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)
|
if (i & 1)
|
||||||
axes[i] = -axes[i];
|
axes[i] = -axes[i];
|
||||||
|
@ -29,11 +29,46 @@
|
|||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
|
||||||
|
//========================================================================
|
||||||
|
// The per-thread current context/window pointer
|
||||||
|
//========================================================================
|
||||||
|
static pthread_key_t _glfwCurrentTLS;
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
////// GLFW platform API //////
|
////// 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
|
// Make the OpenGL context associated with the specified window current
|
||||||
//========================================================================
|
//========================================================================
|
||||||
@ -44,6 +79,18 @@ void _glfwPlatformMakeContextCurrent(_GLFWwindow* window)
|
|||||||
[window->NSGL.context makeCurrentContext];
|
[window->NSGL.context makeCurrentContext];
|
||||||
else
|
else
|
||||||
[NSOpenGLContext clearCurrentContext];
|
[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)
|
void _glfwPlatformSwapInterval(int interval)
|
||||||
{
|
{
|
||||||
_GLFWwindow* window = _glfwLibrary.currentWindow;
|
_GLFWwindow* window = _glfwPlatformGetCurrentContext();
|
||||||
|
|
||||||
GLint sync = interval;
|
GLint sync = interval;
|
||||||
[window->NSGL.context setValues:&sync forParameter:NSOpenGLCPSwapInterval];
|
[window->NSGL.context setValues:&sync forParameter:NSOpenGLCPSwapInterval];
|
||||||
|
@ -123,4 +123,8 @@ void _glfwTerminateJoysticks(void);
|
|||||||
GLboolean _glfwSetVideoMode(int* width, int* height, int* bpp, int* refreshRate);
|
GLboolean _glfwSetVideoMode(int* width, int* height, int* bpp, int* refreshRate);
|
||||||
void _glfwRestoreVideoMode(void);
|
void _glfwRestoreVideoMode(void);
|
||||||
|
|
||||||
|
// OpenGL support
|
||||||
|
int _glfwInitOpenGL(void);
|
||||||
|
void _glfwTerminateOpenGL(void);
|
||||||
|
|
||||||
#endif // _platform_h_
|
#endif // _platform_h_
|
||||||
|
@ -686,7 +686,7 @@ static GLboolean createWindow(_GLFWwindow* window,
|
|||||||
[window->NS.object setAcceptsMouseMovedEvents:YES];
|
[window->NS.object setAcceptsMouseMovedEvents:YES];
|
||||||
[window->NS.object center];
|
[window->NS.object center];
|
||||||
|
|
||||||
if ([window->NS.object respondsToSelector:@selector(setRestorable)])
|
if ([window->NS.object respondsToSelector:@selector(setRestorable:)])
|
||||||
[window->NS.object setRestorable:NO];
|
[window->NS.object setRestorable:NO];
|
||||||
|
|
||||||
return GL_TRUE;
|
return GL_TRUE;
|
||||||
|
@ -34,6 +34,24 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.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;
|
||||||
|
|
||||||
|
|
||||||
//========================================================================
|
//========================================================================
|
||||||
// Returns the specified attribute of the specified EGLConfig
|
// Returns the specified attribute of the specified EGLConfig
|
||||||
//========================================================================
|
//========================================================================
|
||||||
@ -508,6 +526,18 @@ void _glfwPlatformMakeContextCurrent(_GLFWwindow* window)
|
|||||||
eglMakeCurrent(_glfwLibrary.EGL.display,
|
eglMakeCurrent(_glfwLibrary.EGL.display,
|
||||||
EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_glfwCurrentWindow = window;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//========================================================================
|
||||||
|
// Return the window object whose context is current
|
||||||
|
//========================================================================
|
||||||
|
|
||||||
|
_GLFWwindow* _glfwPlatformGetCurrentContext(void)
|
||||||
|
{
|
||||||
|
return _glfwCurrentWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,9 +32,7 @@
|
|||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#ifdef __APPLE__
|
#if _WIN32
|
||||||
#include <sys/malloc.h>
|
|
||||||
#else
|
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -38,6 +38,22 @@
|
|||||||
void (*glXGetProcAddressEXT(const GLubyte* procName))();
|
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
|
// Returns the specified attribute of the specified GLXFBConfig
|
||||||
// NOTE: Do not call this unless we have found GLX 1.3+ or GLX_SGIX_fbconfig
|
// NOTE: Do not call this unless we have found GLX 1.3+ or GLX_SGIX_fbconfig
|
||||||
@ -611,6 +627,10 @@ void _glfwDestroyContext(_GLFWwindow* window)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
////// GLFW platform API //////
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
//========================================================================
|
//========================================================================
|
||||||
// Make the OpenGL context associated with the specified window current
|
// Make the OpenGL context associated with the specified window current
|
||||||
//========================================================================
|
//========================================================================
|
||||||
@ -625,6 +645,18 @@ void _glfwPlatformMakeContextCurrent(_GLFWwindow* window)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
glXMakeCurrent(_glfwLibrary.X11.display, None, NULL);
|
glXMakeCurrent(_glfwLibrary.X11.display, None, NULL);
|
||||||
|
|
||||||
|
_glfwCurrentWindow = window;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//========================================================================
|
||||||
|
// Return the window object whose context is current
|
||||||
|
//========================================================================
|
||||||
|
|
||||||
|
_GLFWwindow* _glfwPlatformGetCurrentContext(void)
|
||||||
|
{
|
||||||
|
return _glfwCurrentWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -644,7 +676,7 @@ void _glfwPlatformSwapBuffers(_GLFWwindow* window)
|
|||||||
|
|
||||||
void _glfwPlatformSwapInterval(int interval)
|
void _glfwPlatformSwapInterval(int interval)
|
||||||
{
|
{
|
||||||
_GLFWwindow* window = _glfwLibrary.currentWindow;
|
_GLFWwindow* window = _glfwCurrentWindow;
|
||||||
|
|
||||||
if (_glfwLibrary.GLX.EXT_swap_control)
|
if (_glfwLibrary.GLX.EXT_swap_control)
|
||||||
{
|
{
|
||||||
|
@ -208,7 +208,6 @@ struct _GLFWlibrary
|
|||||||
_GLFWhints hints;
|
_GLFWhints hints;
|
||||||
|
|
||||||
_GLFWwindow* windowListHead;
|
_GLFWwindow* windowListHead;
|
||||||
_GLFWwindow* currentWindow;
|
|
||||||
_GLFWwindow* activeWindow;
|
_GLFWwindow* activeWindow;
|
||||||
|
|
||||||
GLFWwindowsizefun windowSizeCallback;
|
GLFWwindowsizefun windowSizeCallback;
|
||||||
@ -298,6 +297,7 @@ void _glfwPlatformWaitEvents(void);
|
|||||||
|
|
||||||
// OpenGL context management
|
// OpenGL context management
|
||||||
void _glfwPlatformMakeContextCurrent(_GLFWwindow* window);
|
void _glfwPlatformMakeContextCurrent(_GLFWwindow* window);
|
||||||
|
_GLFWwindow* _glfwPlatformGetCurrentContext(void);
|
||||||
void _glfwPlatformSwapBuffers(_GLFWwindow* window);
|
void _glfwPlatformSwapBuffers(_GLFWwindow* window);
|
||||||
void _glfwPlatformSwapInterval(int interval);
|
void _glfwPlatformSwapInterval(int interval);
|
||||||
void _glfwPlatformRefreshWindowParams(_GLFWwindow* window);
|
void _glfwPlatformRefreshWindowParams(_GLFWwindow* window);
|
||||||
|
17
src/opengl.c
17
src/opengl.c
@ -392,7 +392,7 @@ GLboolean _glfwIsValidContextConfig(_GLFWwndconfig* wndconfig)
|
|||||||
|
|
||||||
GLboolean _glfwRefreshContextParams(void)
|
GLboolean _glfwRefreshContextParams(void)
|
||||||
{
|
{
|
||||||
_GLFWwindow* window = _glfwLibrary.currentWindow;
|
_GLFWwindow* window = _glfwPlatformGetCurrentContext();
|
||||||
|
|
||||||
if (!parseGLVersion(&window->clientAPI,
|
if (!parseGLVersion(&window->clientAPI,
|
||||||
&window->glMajor,
|
&window->glMajor,
|
||||||
@ -460,7 +460,7 @@ GLboolean _glfwRefreshContextParams(void)
|
|||||||
|
|
||||||
GLboolean _glfwIsValidContext(_GLFWwndconfig* wndconfig)
|
GLboolean _glfwIsValidContext(_GLFWwndconfig* wndconfig)
|
||||||
{
|
{
|
||||||
_GLFWwindow* window = _glfwLibrary.currentWindow;
|
_GLFWwindow* window = _glfwPlatformGetCurrentContext();
|
||||||
|
|
||||||
if (window->glMajor < wndconfig->glMajor ||
|
if (window->glMajor < wndconfig->glMajor ||
|
||||||
(window->glMajor == wndconfig->glMajor &&
|
(window->glMajor == wndconfig->glMajor &&
|
||||||
@ -535,11 +535,10 @@ GLFWAPI void glfwMakeContextCurrent(GLFWwindow handle)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_glfwLibrary.currentWindow == window)
|
if (_glfwPlatformGetCurrentContext() == window)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_glfwPlatformMakeContextCurrent(window);
|
_glfwPlatformMakeContextCurrent(window);
|
||||||
_glfwLibrary.currentWindow = window;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -555,7 +554,7 @@ GLFWAPI GLFWwindow glfwGetCurrentContext(void)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _glfwLibrary.currentWindow;
|
return _glfwPlatformGetCurrentContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -589,7 +588,7 @@ GLFWAPI void glfwSwapInterval(int interval)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_glfwLibrary.currentWindow)
|
if (!_glfwPlatformGetCurrentContext())
|
||||||
{
|
{
|
||||||
_glfwSetError(GLFW_NO_CURRENT_CONTEXT, NULL);
|
_glfwSetError(GLFW_NO_CURRENT_CONTEXT, NULL);
|
||||||
return;
|
return;
|
||||||
@ -614,7 +613,7 @@ GLFWAPI int glfwExtensionSupported(const char* extension)
|
|||||||
return GL_FALSE;
|
return GL_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
window = _glfwLibrary.currentWindow;
|
window = _glfwPlatformGetCurrentContext();
|
||||||
if (!window)
|
if (!window)
|
||||||
{
|
{
|
||||||
_glfwSetError(GLFW_NO_CURRENT_CONTEXT, NULL);
|
_glfwSetError(GLFW_NO_CURRENT_CONTEXT, NULL);
|
||||||
@ -675,7 +674,7 @@ GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_glfwLibrary.currentWindow)
|
if (!_glfwPlatformGetCurrentContext())
|
||||||
{
|
{
|
||||||
_glfwSetError(GLFW_NO_CURRENT_CONTEXT, NULL);
|
_glfwSetError(GLFW_NO_CURRENT_CONTEXT, NULL);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -703,7 +702,7 @@ GLFWAPI void glfwCopyContext(GLFWwindow hsrc, GLFWwindow hdst, unsigned long mas
|
|||||||
src = (_GLFWwindow*) hsrc;
|
src = (_GLFWwindow*) hsrc;
|
||||||
dst = (_GLFWwindow*) hdst;
|
dst = (_GLFWwindow*) hdst;
|
||||||
|
|
||||||
if (_glfwLibrary.currentWindow == dst)
|
if (_glfwPlatformGetCurrentContext() == dst)
|
||||||
{
|
{
|
||||||
_glfwSetError(GLFW_INVALID_VALUE,
|
_glfwSetError(GLFW_INVALID_VALUE,
|
||||||
"glfwCopyContext: Cannot copy OpenGL state to a current context");
|
"glfwCopyContext: Cannot copy OpenGL state to a current context");
|
||||||
|
@ -34,6 +34,24 @@
|
|||||||
#include <malloc.h>
|
#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
|
// Initialize WGL-specific extensions
|
||||||
// This function is called once before initial context creation, i.e. before
|
// This function is called once before initial context creation, i.e. before
|
||||||
@ -447,7 +465,7 @@ static GLboolean createContext(_GLFWwindow* window,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
glfwMakeContextCurrent(window);
|
_glfwPlatformMakeContextCurrent(window);
|
||||||
initWGLExtensions(window);
|
initWGLExtensions(window);
|
||||||
|
|
||||||
return GL_TRUE;
|
return GL_TRUE;
|
||||||
@ -512,8 +530,8 @@ void _glfwDestroyContext(_GLFWwindow* window)
|
|||||||
{
|
{
|
||||||
// This is duplicated from glfwDestroyWindow
|
// This is duplicated from glfwDestroyWindow
|
||||||
// TODO: Stop duplicating code
|
// TODO: Stop duplicating code
|
||||||
if (window == _glfwLibrary.currentWindow)
|
if (window == _glfwCurrentWindow)
|
||||||
glfwMakeContextCurrent(NULL);
|
_glfwPlatformMakeContextCurrent(NULL);
|
||||||
|
|
||||||
if (window->WGL.context)
|
if (window->WGL.context)
|
||||||
{
|
{
|
||||||
@ -543,6 +561,18 @@ void _glfwPlatformMakeContextCurrent(_GLFWwindow* window)
|
|||||||
wglMakeCurrent(window->WGL.DC, window->WGL.context);
|
wglMakeCurrent(window->WGL.DC, window->WGL.context);
|
||||||
else
|
else
|
||||||
wglMakeCurrent(NULL, NULL);
|
wglMakeCurrent(NULL, NULL);
|
||||||
|
|
||||||
|
_glfwCurrentWindow = window;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//========================================================================
|
||||||
|
// Return the window object whose context is current
|
||||||
|
//========================================================================
|
||||||
|
|
||||||
|
_GLFWwindow* _glfwPlatformGetCurrentContext(void)
|
||||||
|
{
|
||||||
|
return _glfwCurrentWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -562,7 +592,7 @@ void _glfwPlatformSwapBuffers(_GLFWwindow* window)
|
|||||||
|
|
||||||
void _glfwPlatformSwapInterval(int interval)
|
void _glfwPlatformSwapInterval(int interval)
|
||||||
{
|
{
|
||||||
_GLFWwindow* window = _glfwLibrary.currentWindow;
|
_GLFWwindow* window = _glfwCurrentWindow;
|
||||||
|
|
||||||
if (window->WGL.EXT_swap_control)
|
if (window->WGL.EXT_swap_control)
|
||||||
window->WGL.SwapIntervalEXT(interval);
|
window->WGL.SwapIntervalEXT(interval);
|
||||||
@ -577,7 +607,7 @@ int _glfwPlatformExtensionSupported(const char* extension)
|
|||||||
{
|
{
|
||||||
const GLubyte* extensions;
|
const GLubyte* extensions;
|
||||||
|
|
||||||
_GLFWwindow* window = _glfwLibrary.currentWindow;
|
_GLFWwindow* window = _glfwCurrentWindow;
|
||||||
|
|
||||||
if (window->WGL.GetExtensionsStringEXT != NULL)
|
if (window->WGL.GetExtensionsStringEXT != NULL)
|
||||||
{
|
{
|
||||||
|
@ -33,9 +33,7 @@
|
|||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#ifdef __APPLE__
|
#if _WIN32
|
||||||
#include <sys/malloc.h>
|
|
||||||
#else
|
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -468,8 +466,9 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow handle)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Clear the current context if this window's context is current
|
// Clear the current context if this window's context is current
|
||||||
if (window == _glfwLibrary.currentWindow)
|
// TODO: Re-examine this in light of multithreading
|
||||||
glfwMakeContextCurrent(NULL);
|
if (window == _glfwPlatformGetCurrentContext())
|
||||||
|
_glfwPlatformMakeContextCurrent(NULL);
|
||||||
|
|
||||||
// Clear the active window pointer if this is the active window
|
// Clear the active window pointer if this is the active window
|
||||||
if (window == _glfwLibrary.activeWindow)
|
if (window == _glfwLibrary.activeWindow)
|
||||||
|
@ -634,6 +634,8 @@ static void terminateDisplay(void)
|
|||||||
|
|
||||||
int _glfwPlatformInit(void)
|
int _glfwPlatformInit(void)
|
||||||
{
|
{
|
||||||
|
XInitThreads();
|
||||||
|
|
||||||
if (!initDisplay())
|
if (!initDisplay())
|
||||||
return GL_FALSE;
|
return GL_FALSE;
|
||||||
|
|
||||||
@ -646,7 +648,8 @@ int _glfwPlatformInit(void)
|
|||||||
|
|
||||||
_glfwLibrary.X11.cursor = createNULLCursor();
|
_glfwLibrary.X11.cursor = createNULLCursor();
|
||||||
|
|
||||||
_glfwInitJoysticks();
|
if (!_glfwInitJoysticks())
|
||||||
|
return GL_FALSE;
|
||||||
|
|
||||||
// Start the timer
|
// Start the timer
|
||||||
_glfwInitTimer();
|
_glfwInitTimer();
|
||||||
|
@ -37,7 +37,8 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <regex.h>
|
||||||
|
#include <dirent.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#endif // _GLFW_USE_LINUX_JOYSTICKS
|
#endif // _GLFW_USE_LINUX_JOYSTICKS
|
||||||
@ -53,7 +54,7 @@ static int openJoystickDevice(int joy, const char* path)
|
|||||||
char numAxes, numButtons;
|
char numAxes, numButtons;
|
||||||
int fd, version;
|
int fd, version;
|
||||||
|
|
||||||
fd = open(path, O_NONBLOCK);
|
fd = open(path, O_RDONLY | O_NONBLOCK);
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
return GL_FALSE;
|
return GL_FALSE;
|
||||||
|
|
||||||
@ -127,7 +128,7 @@ static void pollJoystickEvents(void)
|
|||||||
if (errno == ENODEV)
|
if (errno == ENODEV)
|
||||||
_glfwLibrary.X11.joystick[i].present = GL_FALSE;
|
_glfwLibrary.X11.joystick[i].present = GL_FALSE;
|
||||||
|
|
||||||
if (result < sizeof(e))
|
if (result == -1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// We don't care if it's an init event or not
|
// We don't care if it's an init event or not
|
||||||
@ -171,30 +172,52 @@ static void pollJoystickEvents(void)
|
|||||||
// Initialize joystick interface
|
// Initialize joystick interface
|
||||||
//========================================================================
|
//========================================================================
|
||||||
|
|
||||||
void _glfwInitJoysticks(void)
|
int _glfwInitJoysticks(void)
|
||||||
{
|
{
|
||||||
#ifdef _GLFW_USE_LINUX_JOYSTICKS
|
#ifdef _GLFW_USE_LINUX_JOYSTICKS
|
||||||
int i, j, joy = 0;
|
int i, joy = 0;
|
||||||
char path[20];
|
regex_t regex;
|
||||||
const char* bases[] =
|
DIR* dir;
|
||||||
|
const char* dirs[] =
|
||||||
{
|
{
|
||||||
"/dev/input/js",
|
"/dev/input",
|
||||||
"/dev/js"
|
"/dev"
|
||||||
};
|
};
|
||||||
|
|
||||||
for (i = 0; i < sizeof(bases) / sizeof(bases[0]); i++)
|
if (regcomp(®ex, "^js[0-9]\\+$", 0) != 0)
|
||||||
{
|
{
|
||||||
for (j = 0; j < 50; j++)
|
_glfwSetError(GLFW_PLATFORM_ERROR, "X11: Failed to compile regex");
|
||||||
{
|
return GL_FALSE;
|
||||||
if (joy > GLFW_JOYSTICK_LAST)
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
sprintf(path, "%s%i", bases[i], j);
|
for (i = 0; i < sizeof(dirs) / sizeof(dirs[0]); i++)
|
||||||
|
{
|
||||||
|
struct dirent* entry;
|
||||||
|
|
||||||
|
dir = opendir(dirs[i]);
|
||||||
|
if (!dir)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
while ((entry = readdir(dir)))
|
||||||
|
{
|
||||||
|
char path[20];
|
||||||
|
regmatch_t match;
|
||||||
|
|
||||||
|
if (regexec(®ex, entry->d_name, 1, &match, 0) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", dirs[i], entry->d_name);
|
||||||
if (openJoystickDevice(joy, path))
|
if (openJoystickDevice(joy, path))
|
||||||
joy++;
|
joy++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
regfree(®ex);
|
||||||
#endif // _GLFW_USE_LINUX_JOYSTICKS
|
#endif // _GLFW_USE_LINUX_JOYSTICKS
|
||||||
|
|
||||||
|
return GL_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -235,7 +235,7 @@ void _glfwSetVideoMode(int* width, int* height, int* rate);
|
|||||||
void _glfwRestoreVideoMode(void);
|
void _glfwRestoreVideoMode(void);
|
||||||
|
|
||||||
// Joystick input
|
// Joystick input
|
||||||
void _glfwInitJoysticks(void);
|
int _glfwInitJoysticks(void);
|
||||||
void _glfwTerminateJoysticks(void);
|
void _glfwTerminateJoysticks(void);
|
||||||
|
|
||||||
// Unicode support
|
// Unicode support
|
||||||
|
156
src/x11_window.c
156
src/x11_window.c
@ -30,6 +30,8 @@
|
|||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
|
#include <sys/select.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -460,34 +462,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;
|
_GLFWwindow* window;
|
||||||
|
|
||||||
XEvent event;
|
switch (event->type)
|
||||||
XNextEvent(_glfwLibrary.X11.display, &event);
|
|
||||||
|
|
||||||
switch (event.type)
|
|
||||||
{
|
{
|
||||||
case KeyPress:
|
case KeyPress:
|
||||||
{
|
{
|
||||||
// A keyboard key was pressed
|
// A keyboard key was pressed
|
||||||
window = findWindow(event.xkey.window);
|
window = findWindow(event->xkey.window);
|
||||||
if (window == NULL)
|
if (window == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_glfwInputKey(window, translateKey(event.xkey.keycode), GLFW_PRESS);
|
_glfwInputKey(window, translateKey(event->xkey.keycode), GLFW_PRESS);
|
||||||
_glfwInputChar(window, translateChar(&event.xkey));
|
_glfwInputChar(window, translateChar(&event->xkey));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case KeyRelease:
|
case KeyRelease:
|
||||||
{
|
{
|
||||||
// A keyboard key was released
|
// A keyboard key was released
|
||||||
window = findWindow(event.xkey.window);
|
window = findWindow(event->xkey.window);
|
||||||
if (window == NULL)
|
if (window == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -501,15 +500,15 @@ static void processSingleEvent(void)
|
|||||||
XPeekEvent(_glfwLibrary.X11.display, &nextEvent);
|
XPeekEvent(_glfwLibrary.X11.display, &nextEvent);
|
||||||
|
|
||||||
if (nextEvent.type == KeyPress &&
|
if (nextEvent.type == KeyPress &&
|
||||||
nextEvent.xkey.window == event.xkey.window &&
|
nextEvent.xkey.window == event->xkey.window &&
|
||||||
nextEvent.xkey.keycode == event.xkey.keycode)
|
nextEvent.xkey.keycode == event->xkey.keycode)
|
||||||
{
|
{
|
||||||
// This last check is a hack to work around key repeats
|
// This last check is a hack to work around key repeats
|
||||||
// leaking through due to some sort of time drift
|
// leaking through due to some sort of time drift
|
||||||
// Toshiyuki Takahashi can press a button 16 times per
|
// Toshiyuki Takahashi can press a button 16 times per
|
||||||
// second so it's fairly safe to assume that no human is
|
// second so it's fairly safe to assume that no human is
|
||||||
// pressing the key 50 times per second (value is ms)
|
// 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
|
// Do not report anything for this event
|
||||||
break;
|
break;
|
||||||
@ -517,34 +516,34 @@ static void processSingleEvent(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_glfwInputKey(window, translateKey(event.xkey.keycode), GLFW_RELEASE);
|
_glfwInputKey(window, translateKey(event->xkey.keycode), GLFW_RELEASE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case ButtonPress:
|
case ButtonPress:
|
||||||
{
|
{
|
||||||
// A mouse button was pressed or a scrolling event occurred
|
// A mouse button was pressed or a scrolling event occurred
|
||||||
window = findWindow(event.xbutton.window);
|
window = findWindow(event->xbutton.window);
|
||||||
if (window == NULL)
|
if (window == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (event.xbutton.button == Button1)
|
if (event->xbutton.button == Button1)
|
||||||
_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS);
|
_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);
|
_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);
|
_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS);
|
||||||
|
|
||||||
// XFree86 3.3.2 and later translates mouse wheel up/down into
|
// XFree86 3.3.2 and later translates mouse wheel up/down into
|
||||||
// mouse button 4 & 5 presses
|
// mouse button 4 & 5 presses
|
||||||
else if (event.xbutton.button == Button4)
|
else if (event->xbutton.button == Button4)
|
||||||
_glfwInputScroll(window, 0.0, 1.0);
|
_glfwInputScroll(window, 0.0, 1.0);
|
||||||
else if (event.xbutton.button == Button5)
|
else if (event->xbutton.button == Button5)
|
||||||
_glfwInputScroll(window, 0.0, -1.0);
|
_glfwInputScroll(window, 0.0, -1.0);
|
||||||
|
|
||||||
else if (event.xbutton.button == Button6)
|
else if (event->xbutton.button == Button6)
|
||||||
_glfwInputScroll(window, -1.0, 0.0);
|
_glfwInputScroll(window, -1.0, 0.0);
|
||||||
else if (event.xbutton.button == Button7)
|
else if (event->xbutton.button == Button7)
|
||||||
_glfwInputScroll(window, 1.0, 0.0);
|
_glfwInputScroll(window, 1.0, 0.0);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -553,23 +552,23 @@ static void processSingleEvent(void)
|
|||||||
case ButtonRelease:
|
case ButtonRelease:
|
||||||
{
|
{
|
||||||
// A mouse button was released
|
// A mouse button was released
|
||||||
window = findWindow(event.xbutton.window);
|
window = findWindow(event->xbutton.window);
|
||||||
if (window == NULL)
|
if (window == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (event.xbutton.button == Button1)
|
if (event->xbutton.button == Button1)
|
||||||
{
|
{
|
||||||
_glfwInputMouseClick(window,
|
_glfwInputMouseClick(window,
|
||||||
GLFW_MOUSE_BUTTON_LEFT,
|
GLFW_MOUSE_BUTTON_LEFT,
|
||||||
GLFW_RELEASE);
|
GLFW_RELEASE);
|
||||||
}
|
}
|
||||||
else if (event.xbutton.button == Button2)
|
else if (event->xbutton.button == Button2)
|
||||||
{
|
{
|
||||||
_glfwInputMouseClick(window,
|
_glfwInputMouseClick(window,
|
||||||
GLFW_MOUSE_BUTTON_MIDDLE,
|
GLFW_MOUSE_BUTTON_MIDDLE,
|
||||||
GLFW_RELEASE);
|
GLFW_RELEASE);
|
||||||
}
|
}
|
||||||
else if (event.xbutton.button == Button3)
|
else if (event->xbutton.button == Button3)
|
||||||
{
|
{
|
||||||
_glfwInputMouseClick(window,
|
_glfwInputMouseClick(window,
|
||||||
GLFW_MOUSE_BUTTON_RIGHT,
|
GLFW_MOUSE_BUTTON_RIGHT,
|
||||||
@ -581,7 +580,7 @@ static void processSingleEvent(void)
|
|||||||
case EnterNotify:
|
case EnterNotify:
|
||||||
{
|
{
|
||||||
// The cursor entered the window
|
// The cursor entered the window
|
||||||
window = findWindow(event.xcrossing.window);
|
window = findWindow(event->xcrossing.window);
|
||||||
if (window == NULL)
|
if (window == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -595,7 +594,7 @@ static void processSingleEvent(void)
|
|||||||
case LeaveNotify:
|
case LeaveNotify:
|
||||||
{
|
{
|
||||||
// The cursor left the window
|
// The cursor left the window
|
||||||
window = findWindow(event.xcrossing.window);
|
window = findWindow(event->xcrossing.window);
|
||||||
if (window == NULL)
|
if (window == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -609,12 +608,12 @@ static void processSingleEvent(void)
|
|||||||
case MotionNotify:
|
case MotionNotify:
|
||||||
{
|
{
|
||||||
// The cursor was moved
|
// The cursor was moved
|
||||||
window = findWindow(event.xmotion.window);
|
window = findWindow(event->xmotion.window);
|
||||||
if (window == NULL)
|
if (window == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (event.xmotion.x != window->X11.cursorPosX ||
|
if (event->xmotion.x != window->X11.cursorPosX ||
|
||||||
event.xmotion.y != window->X11.cursorPosY)
|
event->xmotion.y != window->X11.cursorPosY)
|
||||||
{
|
{
|
||||||
// The cursor was moved by something other than GLFW
|
// The cursor was moved by something other than GLFW
|
||||||
|
|
||||||
@ -625,17 +624,17 @@ static void processSingleEvent(void)
|
|||||||
if (_glfwLibrary.activeWindow != window)
|
if (_glfwLibrary.activeWindow != window)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
x = event.xmotion.x - window->X11.cursorPosX;
|
x = event->xmotion.x - window->X11.cursorPosX;
|
||||||
y = event.xmotion.y - window->X11.cursorPosY;
|
y = event->xmotion.y - window->X11.cursorPosY;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
x = event.xmotion.x;
|
x = event->xmotion.x;
|
||||||
y = event.xmotion.y;
|
y = event->xmotion.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
window->X11.cursorPosX = event.xmotion.x;
|
window->X11.cursorPosX = event->xmotion.x;
|
||||||
window->X11.cursorPosY = event.xmotion.y;
|
window->X11.cursorPosY = event->xmotion.y;
|
||||||
window->X11.cursorCentered = GL_FALSE;
|
window->X11.cursorCentered = GL_FALSE;
|
||||||
|
|
||||||
_glfwInputCursorMotion(window, x, y);
|
_glfwInputCursorMotion(window, x, y);
|
||||||
@ -647,17 +646,17 @@ static void processSingleEvent(void)
|
|||||||
case ConfigureNotify:
|
case ConfigureNotify:
|
||||||
{
|
{
|
||||||
// The window configuration changed somehow
|
// The window configuration changed somehow
|
||||||
window = findWindow(event.xconfigure.window);
|
window = findWindow(event->xconfigure.window);
|
||||||
if (window == NULL)
|
if (window == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_glfwInputWindowSize(window,
|
_glfwInputWindowSize(window,
|
||||||
event.xconfigure.width,
|
event->xconfigure.width,
|
||||||
event.xconfigure.height);
|
event->xconfigure.height);
|
||||||
|
|
||||||
_glfwInputWindowPos(window,
|
_glfwInputWindowPos(window,
|
||||||
event.xconfigure.x,
|
event->xconfigure.x,
|
||||||
event.xconfigure.y);
|
event->xconfigure.y);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -665,11 +664,11 @@ static void processSingleEvent(void)
|
|||||||
case ClientMessage:
|
case ClientMessage:
|
||||||
{
|
{
|
||||||
// Custom client message, probably from the window manager
|
// Custom client message, probably from the window manager
|
||||||
window = findWindow(event.xclient.window);
|
window = findWindow(event->xclient.window);
|
||||||
if (window == NULL)
|
if (window == NULL)
|
||||||
return;
|
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 window manager was asked to close the window, for example by
|
||||||
// the user pressing a 'close' window decoration button
|
// the user pressing a 'close' window decoration button
|
||||||
@ -677,17 +676,17 @@ static void processSingleEvent(void)
|
|||||||
_glfwInputWindowCloseRequest(window);
|
_glfwInputWindowCloseRequest(window);
|
||||||
}
|
}
|
||||||
else if (_glfwLibrary.X11.wmPing != None &&
|
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
|
// The window manager is pinging the application to ensure it's
|
||||||
// still responding to events
|
// still responding to events
|
||||||
|
|
||||||
event.xclient.window = _glfwLibrary.X11.root;
|
event->xclient.window = _glfwLibrary.X11.root;
|
||||||
XSendEvent(_glfwLibrary.X11.display,
|
XSendEvent(_glfwLibrary.X11.display,
|
||||||
event.xclient.window,
|
event->xclient.window,
|
||||||
False,
|
False,
|
||||||
SubstructureNotifyMask | SubstructureRedirectMask,
|
SubstructureNotifyMask | SubstructureRedirectMask,
|
||||||
&event);
|
event);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -696,7 +695,7 @@ static void processSingleEvent(void)
|
|||||||
case MapNotify:
|
case MapNotify:
|
||||||
{
|
{
|
||||||
// The window was mapped
|
// The window was mapped
|
||||||
window = findWindow(event.xmap.window);
|
window = findWindow(event->xmap.window);
|
||||||
if (window == NULL)
|
if (window == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -707,7 +706,7 @@ static void processSingleEvent(void)
|
|||||||
case UnmapNotify:
|
case UnmapNotify:
|
||||||
{
|
{
|
||||||
// The window was unmapped
|
// The window was unmapped
|
||||||
window = findWindow(event.xmap.window);
|
window = findWindow(event->xmap.window);
|
||||||
if (window == NULL)
|
if (window == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -718,7 +717,7 @@ static void processSingleEvent(void)
|
|||||||
case FocusIn:
|
case FocusIn:
|
||||||
{
|
{
|
||||||
// The window gained focus
|
// The window gained focus
|
||||||
window = findWindow(event.xfocus.window);
|
window = findWindow(event->xfocus.window);
|
||||||
if (window == NULL)
|
if (window == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -733,7 +732,7 @@ static void processSingleEvent(void)
|
|||||||
case FocusOut:
|
case FocusOut:
|
||||||
{
|
{
|
||||||
// The window lost focus
|
// The window lost focus
|
||||||
window = findWindow(event.xfocus.window);
|
window = findWindow(event->xfocus.window);
|
||||||
if (window == NULL)
|
if (window == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -748,7 +747,7 @@ static void processSingleEvent(void)
|
|||||||
case Expose:
|
case Expose:
|
||||||
{
|
{
|
||||||
// The window's contents was damaged
|
// The window's contents was damaged
|
||||||
window = findWindow(event.xexpose.window);
|
window = findWindow(event->xexpose.window);
|
||||||
if (window == NULL)
|
if (window == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -769,7 +768,7 @@ static void processSingleEvent(void)
|
|||||||
{
|
{
|
||||||
// The selection conversion status is available
|
// The selection conversion status is available
|
||||||
|
|
||||||
XSelectionEvent* request = &event.xselection;
|
XSelectionEvent* request = &event->xselection;
|
||||||
|
|
||||||
if (_glfwReadSelection(request))
|
if (_glfwReadSelection(request))
|
||||||
_glfwLibrary.X11.selection.status = _GLFW_CONVERSION_SUCCEEDED;
|
_glfwLibrary.X11.selection.status = _GLFW_CONVERSION_SUCCEEDED;
|
||||||
@ -783,7 +782,7 @@ static void processSingleEvent(void)
|
|||||||
{
|
{
|
||||||
// The contents of the selection was requested
|
// The contents of the selection was requested
|
||||||
|
|
||||||
XSelectionRequestEvent* request = &event.xselectionrequest;
|
XSelectionRequestEvent* request = &event->xselectionrequest;
|
||||||
|
|
||||||
XEvent response;
|
XEvent response;
|
||||||
memset(&response, 0, sizeof(response));
|
memset(&response, 0, sizeof(response));
|
||||||
@ -808,11 +807,11 @@ static void processSingleEvent(void)
|
|||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
#if defined(_GLFW_HAS_XRANDR)
|
#if defined(_GLFW_HAS_XRANDR)
|
||||||
switch (event.type - _glfwLibrary.X11.RandR.eventBase)
|
switch (event->type - _glfwLibrary.X11.RandR.eventBase)
|
||||||
{
|
{
|
||||||
case RRScreenChangeNotify:
|
case RRScreenChangeNotify:
|
||||||
{
|
{
|
||||||
XRRUpdateConfiguration(&event);
|
XRRUpdateConfiguration(event);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -957,7 +956,6 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
|
|||||||
void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
|
void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
|
||||||
{
|
{
|
||||||
int mode = 0, rate, sizeChanged = GL_FALSE;
|
int mode = 0, rate, sizeChanged = GL_FALSE;
|
||||||
XSizeHints* sizehints;
|
|
||||||
|
|
||||||
rate = window->refreshRate;
|
rate = window->refreshRate;
|
||||||
|
|
||||||
@ -971,14 +969,14 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
|
|||||||
{
|
{
|
||||||
// Update window size restrictions to match new window size
|
// Update window size restrictions to match new window size
|
||||||
|
|
||||||
sizehints = XAllocSizeHints();
|
XSizeHints* hints = XAllocSizeHints();
|
||||||
sizehints->flags = 0;
|
|
||||||
|
|
||||||
sizehints->min_width = sizehints->max_width = width;
|
hints->flags |= (PMinSize | PMaxSize);
|
||||||
sizehints->min_height = sizehints->max_height = height;
|
hints->min_width = hints->max_width = width;
|
||||||
|
hints->min_height = hints->max_height = height;
|
||||||
|
|
||||||
XSetWMNormalHints(_glfwLibrary.X11.display, window->X11.handle, sizehints);
|
XSetWMNormalHints(_glfwLibrary.X11.display, window->X11.handle, hints);
|
||||||
XFree(sizehints);
|
XFree(hints);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change window size before changing fullscreen mode?
|
// Change window size before changing fullscreen mode?
|
||||||
@ -1093,13 +1091,18 @@ void _glfwPlatformRefreshWindowParams(_GLFWwindow* window)
|
|||||||
|
|
||||||
void _glfwPlatformPollEvents(void)
|
void _glfwPlatformPollEvents(void)
|
||||||
{
|
{
|
||||||
|
XEvent event;
|
||||||
|
|
||||||
|
while (XCheckMaskEvent(_glfwLibrary.X11.display, ~0, &event) ||
|
||||||
|
XCheckTypedEvent(_glfwLibrary.X11.display, ClientMessage, &event))
|
||||||
|
{
|
||||||
|
processEvent(&event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
_GLFWwindow* window;
|
||||||
|
|
||||||
// Process all pending events
|
|
||||||
while (XPending(_glfwLibrary.X11.display))
|
|
||||||
processSingleEvent();
|
|
||||||
|
|
||||||
// Did the cursor move in an active window that has captured the cursor
|
|
||||||
window = _glfwLibrary.activeWindow;
|
window = _glfwLibrary.activeWindow;
|
||||||
if (window)
|
if (window)
|
||||||
{
|
{
|
||||||
@ -1126,12 +1129,17 @@ void _glfwPlatformPollEvents(void)
|
|||||||
|
|
||||||
void _glfwPlatformWaitEvents(void)
|
void _glfwPlatformWaitEvents(void)
|
||||||
{
|
{
|
||||||
XEvent event;
|
int fd;
|
||||||
|
fd_set fds;
|
||||||
|
|
||||||
// Block waiting for an event to arrive
|
fd = ConnectionNumber(_glfwLibrary.X11.display);
|
||||||
XNextEvent(_glfwLibrary.X11.display, &event);
|
|
||||||
XPutBackEvent(_glfwLibrary.X11.display, &event);
|
|
||||||
|
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(fd, &fds);
|
||||||
|
|
||||||
|
XFlush(_glfwLibrary.X11.display);
|
||||||
|
|
||||||
|
if (select(fd + 1, &fds, NULL, NULL, NULL) > 0)
|
||||||
_glfwPlatformPollEvents();
|
_glfwPlatformPollEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
588
support/tinycthread.c
Normal file
588
support/tinycthread.c
Normal file
@ -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
support/tinycthread.h
Normal file
439
support/tinycthread.h
Normal file
@ -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_ */
|
||||||
|
|
@ -8,12 +8,19 @@ else()
|
|||||||
link_libraries(${glfw_LIBRARIES})
|
link_libraries(${glfw_LIBRARIES})
|
||||||
endif()
|
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
|
include_directories(${GLFW_SOURCE_DIR}/include
|
||||||
${GLFW_SOURCE_DIR}/support
|
${GLFW_SOURCE_DIR}/support
|
||||||
${OPENGL_INCLUDE_DIR})
|
${OPENGL_INCLUDE_DIR})
|
||||||
|
|
||||||
set(GETOPT ${GLFW_SOURCE_DIR}/support/getopt.h
|
set(GETOPT ${GLFW_SOURCE_DIR}/support/getopt.h
|
||||||
${GLFW_SOURCE_DIR}/support/getopt.c)
|
${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(clipboard clipboard.c ${GETOPT})
|
||||||
add_executable(defaults defaults.c)
|
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)
|
add_executable(tearing WIN32 MACOSX_BUNDLE tearing.c)
|
||||||
set_target_properties(tearing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Tearing")
|
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)
|
add_executable(title WIN32 MACOSX_BUNDLE title.c)
|
||||||
set_target_properties(title PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Title")
|
set_target_properties(title PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Title")
|
||||||
|
|
||||||
add_executable(windows WIN32 MACOSX_BUNDLE windows.c)
|
add_executable(windows WIN32 MACOSX_BUNDLE windows.c)
|
||||||
set_target_properties(windows PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Windows")
|
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
|
set(CONSOLE_BINARIES clipboard defaults events fsaa fsfocus gamma glfwinfo
|
||||||
iconify joysticks modes peter reopen)
|
iconify joysticks modes peter reopen)
|
||||||
|
|
||||||
|
@ -317,6 +317,9 @@ int main(int argc, char** argv)
|
|||||||
get_client_api_name(api),
|
get_client_api_name(api),
|
||||||
glGetString(GL_VENDOR));
|
glGetString(GL_VENDOR));
|
||||||
|
|
||||||
|
printf("OpenGL context debug flag saved by GLFW: %s\n",
|
||||||
|
glfwGetWindowParam(window, GLFW_OPENGL_DEBUG_CONTEXT) ? "true" : "false");
|
||||||
|
|
||||||
if (major > 1)
|
if (major > 1)
|
||||||
{
|
{
|
||||||
printf("%s context shading language version: \"%s\"\n",
|
printf("%s context shading language version: \"%s\"\n",
|
||||||
|
131
tests/threads.c
Normal file
131
tests/threads.c
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
//========================================================================
|
||||||
|
// Multithreading test
|
||||||
|
// Copyright (c) Camilla Berglund <elmindreda@elmindreda.org>
|
||||||
|
//
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user