Merge branch 'master' into EGL

Conflicts:
	src/glx_opengl.c
	src/opengl.c
	tests/glfwinfo.c
This commit is contained in:
Camilla Berglund 2012-09-09 19:52:33 +02:00
commit 0272ce2e98
24 changed files with 1503 additions and 136 deletions

19
.gitignore vendored
View File

@ -6,13 +6,13 @@ cmake_uninstall.cmake
.DS_Store
docs/Doxyfile
src/config.h
src/libglfw.pc
src/libglfw.so
src/libglfw.a
src/libglfw.dylib
src/glfw.lib
src/glfw.dll
src/glfwdll.lib
src/glfw3.pc
src/libglfw3.so
src/libglfw3.a
src/libglfw3.dylib
src/glfw3.lib
src/glfw3.dll
src/glfw3dll.lib
examples/boing
examples/gears
examples/heightmap
@ -22,8 +22,8 @@ examples/wave
examples/*.app
examples/*.exe
tests/accuracy
tests/clipboard
tests/defaults
tests/dynamic
tests/events
tests/fsaa
tests/fsfocus
@ -31,11 +31,12 @@ tests/gamma
tests/glfwinfo
tests/iconify
tests/joysticks
tests/listmodes
tests/modes
tests/peter
tests/reopen
tests/sharing
tests/tearing
tests/threads
tests/title
tests/windows
tests/*.app

View File

@ -22,6 +22,12 @@ else()
find_package(OpenGL REQUIRED)
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
#--------------------------------------------------------------------

View File

@ -287,6 +287,7 @@ version of GLFW.</p>
<li>Added <code>windows</code> simple multi-window test program</li>
<li>Added <code>sharing</code> simple OpenGL object sharing test program</li>
<li>Added <code>modes</code> video mode enumeration and setting test program</li>
<li>Added <code>threads</code> simple multi-threaded rendering test program</li>
<li>Added <code>glfw3native.h</code> header and platform-specific functions for explicit access to native display, window and context handles</li>
<li>Added <code>glfwSetGamma</code>, <code>glfwSetGammaRamp</code> and <code>glfwGetGammaRamp</code> functions and <code>GLFWgammaramp</code> type for monitor gamma ramp control</li>
<li>Added window parameter to <code>glfwSwapBuffers</code></li>
@ -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: Calling <code>glXCreateContextAttribsARB</code> with an unavailable OpenGL version caused the application to terminate with a <code>BadMatch</code> Xlib error</li>
<li>[X11] Bugfix: A synchronization point necessary for jitter-free locked cursor mode was incorrectly removed</li>
<li>[X11] Bugfix: The window size hints were not updated when calling <code>glfwSetWindowSize</code> on a non-resizable window</li>
<li>[Win32] Changed port to use Unicode mode only</li>
<li>[Win32] Removed explicit support for versions of Windows older than Windows XP</li>
<li>[Win32] Bugfix: Window activation and iconification did not work as expected</li>
@ -907,7 +909,7 @@ their skills. Special thanks go out to:</p>
language</li>
<li>Shane Liesegang, for providing a bug fix relating to Cocoa window
restoration</li>
restoration and reporting a bug on 32-bit Cocoa builds</li>
<li>Tristam MacDonald, for his bug reports and feedback on the Cocoa port</li>
@ -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
code</li>
<li>Julian Møller, for reporting a bug in the Cocoa joystick code</li>
<li>Arturo J. Pérez, for a bug fix for cursor tracking on Mac OS X 10.6 Snow
Leopard</li>
<li>Riku Salminen, for making the X11 event processing able to support
multi-threaded rendering</li>
<li>Douglas C. Schmidt and Irfan Pyarali, for their excellent article
<a href="http://www.cs.wustl.edu/~schmidt/win32-cv-1.html">Strategies for Implementing POSIX Condition Variables on Win32</a></li>

View File

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

View File

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

View File

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

View File

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

View File

@ -686,7 +686,7 @@ static GLboolean createWindow(_GLFWwindow* window,
[window->NS.object setAcceptsMouseMovedEvents:YES];
[window->NS.object center];
if ([window->NS.object respondsToSelector:@selector(setRestorable)])
if ([window->NS.object respondsToSelector:@selector(setRestorable:)])
[window->NS.object setRestorable:NO];
return GL_TRUE;
@ -1100,7 +1100,7 @@ void _glfwPlatformSetCursorPos(_GLFWwindow* window, int x, int y)
CGPoint mainScreenOrigin = CGDisplayBounds(CGMainDisplayID()).origin;
double mainScreenHeight = CGDisplayBounds(CGMainDisplayID()).size.height;
CGPoint targetPoint = CGPointMake(globalPoint.x - mainScreenOrigin.x,
mainScreenHeight - globalPoint.y -
mainScreenHeight - globalPoint.y -
mainScreenOrigin.y);
CGDisplayMoveCursorToPoint(CGMainDisplayID(), targetPoint);
}

View File

@ -34,6 +34,24 @@
#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
//========================================================================
@ -508,6 +526,18 @@ void _glfwPlatformMakeContextCurrent(_GLFWwindow* window)
eglMakeCurrent(_glfwLibrary.EGL.display,
EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
_glfwCurrentWindow = window;
}
//========================================================================
// Return the window object whose context is current
//========================================================================
_GLFWwindow* _glfwPlatformGetCurrentContext(void)
{
return _glfwCurrentWindow;
}

View File

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

View File

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

View File

@ -208,7 +208,6 @@ struct _GLFWlibrary
_GLFWhints hints;
_GLFWwindow* windowListHead;
_GLFWwindow* currentWindow;
_GLFWwindow* activeWindow;
GLFWwindowsizefun windowSizeCallback;
@ -298,6 +297,7 @@ void _glfwPlatformWaitEvents(void);
// OpenGL context management
void _glfwPlatformMakeContextCurrent(_GLFWwindow* window);
_GLFWwindow* _glfwPlatformGetCurrentContext(void);
void _glfwPlatformSwapBuffers(_GLFWwindow* window);
void _glfwPlatformSwapInterval(int interval);
void _glfwPlatformRefreshWindowParams(_GLFWwindow* window);

View File

@ -392,7 +392,7 @@ GLboolean _glfwIsValidContextConfig(_GLFWwndconfig* wndconfig)
GLboolean _glfwRefreshContextParams(void)
{
_GLFWwindow* window = _glfwLibrary.currentWindow;
_GLFWwindow* window = _glfwPlatformGetCurrentContext();
if (!parseGLVersion(&window->clientAPI,
&window->glMajor,
@ -460,7 +460,7 @@ GLboolean _glfwRefreshContextParams(void)
GLboolean _glfwIsValidContext(_GLFWwndconfig* wndconfig)
{
_GLFWwindow* window = _glfwLibrary.currentWindow;
_GLFWwindow* window = _glfwPlatformGetCurrentContext();
if (window->glMajor < wndconfig->glMajor ||
(window->glMajor == wndconfig->glMajor &&
@ -535,11 +535,10 @@ GLFWAPI void glfwMakeContextCurrent(GLFWwindow handle)
return;
}
if (_glfwLibrary.currentWindow == window)
if (_glfwPlatformGetCurrentContext() == window)
return;
_glfwPlatformMakeContextCurrent(window);
_glfwLibrary.currentWindow = window;
}
@ -555,7 +554,7 @@ GLFWAPI GLFWwindow glfwGetCurrentContext(void)
return NULL;
}
return _glfwLibrary.currentWindow;
return _glfwPlatformGetCurrentContext();
}
@ -589,7 +588,7 @@ GLFWAPI void glfwSwapInterval(int interval)
return;
}
if (!_glfwLibrary.currentWindow)
if (!_glfwPlatformGetCurrentContext())
{
_glfwSetError(GLFW_NO_CURRENT_CONTEXT, NULL);
return;
@ -614,7 +613,7 @@ GLFWAPI int glfwExtensionSupported(const char* extension)
return GL_FALSE;
}
window = _glfwLibrary.currentWindow;
window = _glfwPlatformGetCurrentContext();
if (!window)
{
_glfwSetError(GLFW_NO_CURRENT_CONTEXT, NULL);
@ -675,7 +674,7 @@ GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname)
return NULL;
}
if (!_glfwLibrary.currentWindow)
if (!_glfwPlatformGetCurrentContext())
{
_glfwSetError(GLFW_NO_CURRENT_CONTEXT, NULL);
return NULL;
@ -703,7 +702,7 @@ GLFWAPI void glfwCopyContext(GLFWwindow hsrc, GLFWwindow hdst, unsigned long mas
src = (_GLFWwindow*) hsrc;
dst = (_GLFWwindow*) hdst;
if (_glfwLibrary.currentWindow == dst)
if (_glfwPlatformGetCurrentContext() == dst)
{
_glfwSetError(GLFW_INVALID_VALUE,
"glfwCopyContext: Cannot copy OpenGL state to a current context");

View File

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

View File

@ -33,10 +33,8 @@
#include <string.h>
#include <stdlib.h>
#ifdef __APPLE__
#include <sys/malloc.h>
#else
#include <malloc.h>
#if _WIN32
#include <malloc.h>
#endif
@ -468,8 +466,9 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow handle)
return;
// Clear the current context if this window's context is current
if (window == _glfwLibrary.currentWindow)
glfwMakeContextCurrent(NULL);
// TODO: Re-examine this in light of multithreading
if (window == _glfwPlatformGetCurrentContext())
_glfwPlatformMakeContextCurrent(NULL);
// Clear the active window pointer if this is the active window
if (window == _glfwLibrary.activeWindow)

View File

@ -634,6 +634,8 @@ static void terminateDisplay(void)
int _glfwPlatformInit(void)
{
XInitThreads();
if (!initDisplay())
return GL_FALSE;
@ -646,7 +648,8 @@ int _glfwPlatformInit(void)
_glfwLibrary.X11.cursor = createNULLCursor();
_glfwInitJoysticks();
if (!_glfwInitJoysticks())
return GL_FALSE;
// Start the timer
_glfwInitTimer();

View File

@ -37,7 +37,8 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <regex.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#endif // _GLFW_USE_LINUX_JOYSTICKS
@ -53,7 +54,7 @@ static int openJoystickDevice(int joy, const char* path)
char numAxes, numButtons;
int fd, version;
fd = open(path, O_NONBLOCK);
fd = open(path, O_RDONLY | O_NONBLOCK);
if (fd == -1)
return GL_FALSE;
@ -127,7 +128,7 @@ static void pollJoystickEvents(void)
if (errno == ENODEV)
_glfwLibrary.X11.joystick[i].present = GL_FALSE;
if (result < sizeof(e))
if (result == -1)
break;
// We don't care if it's an init event or not
@ -171,30 +172,52 @@ static void pollJoystickEvents(void)
// Initialize joystick interface
//========================================================================
void _glfwInitJoysticks(void)
int _glfwInitJoysticks(void)
{
#ifdef _GLFW_USE_LINUX_JOYSTICKS
int i, j, joy = 0;
char path[20];
const char* bases[] =
int i, joy = 0;
regex_t regex;
DIR* dir;
const char* dirs[] =
{
"/dev/input/js",
"/dev/js"
"/dev/input",
"/dev"
};
for (i = 0; i < sizeof(bases) / sizeof(bases[0]); i++)
if (regcomp(&regex, "^js[0-9]\\+$", 0) != 0)
{
for (j = 0; j < 50; j++)
{
if (joy > GLFW_JOYSTICK_LAST)
break;
_glfwSetError(GLFW_PLATFORM_ERROR, "X11: Failed to compile regex");
return GL_FALSE;
}
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(&regex, entry->d_name, 1, &match, 0) != 0)
continue;
snprintf(path, sizeof(path), "%s/%s", dirs[i], entry->d_name);
if (openJoystickDevice(joy, path))
joy++;
}
closedir(dir);
}
regfree(&regex);
#endif // _GLFW_USE_LINUX_JOYSTICKS
return GL_TRUE;
}

View File

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

View File

@ -30,6 +30,8 @@
#include "internal.h"
#include <sys/select.h>
#include <string.h>
#include <stdio.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;
XEvent event;
XNextEvent(_glfwLibrary.X11.display, &event);
switch (event.type)
switch (event->type)
{
case KeyPress:
{
// A keyboard key was pressed
window = findWindow(event.xkey.window);
window = findWindow(event->xkey.window);
if (window == NULL)
return;
_glfwInputKey(window, translateKey(event.xkey.keycode), GLFW_PRESS);
_glfwInputChar(window, translateChar(&event.xkey));
_glfwInputKey(window, translateKey(event->xkey.keycode), GLFW_PRESS);
_glfwInputChar(window, translateChar(&event->xkey));
break;
}
case KeyRelease:
{
// A keyboard key was released
window = findWindow(event.xkey.window);
window = findWindow(event->xkey.window);
if (window == NULL)
return;
@ -501,15 +500,15 @@ static void processSingleEvent(void)
XPeekEvent(_glfwLibrary.X11.display, &nextEvent);
if (nextEvent.type == KeyPress &&
nextEvent.xkey.window == event.xkey.window &&
nextEvent.xkey.keycode == event.xkey.keycode)
nextEvent.xkey.window == event->xkey.window &&
nextEvent.xkey.keycode == event->xkey.keycode)
{
// This last check is a hack to work around key repeats
// leaking through due to some sort of time drift
// Toshiyuki Takahashi can press a button 16 times per
// second so it's fairly safe to assume that no human is
// pressing the key 50 times per second (value is ms)
if ((nextEvent.xkey.time - event.xkey.time) < 20)
if ((nextEvent.xkey.time - event->xkey.time) < 20)
{
// Do not report anything for this event
break;
@ -517,34 +516,34 @@ static void processSingleEvent(void)
}
}
_glfwInputKey(window, translateKey(event.xkey.keycode), GLFW_RELEASE);
_glfwInputKey(window, translateKey(event->xkey.keycode), GLFW_RELEASE);
break;
}
case ButtonPress:
{
// A mouse button was pressed or a scrolling event occurred
window = findWindow(event.xbutton.window);
window = findWindow(event->xbutton.window);
if (window == NULL)
return;
if (event.xbutton.button == Button1)
if (event->xbutton.button == Button1)
_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS);
else if (event.xbutton.button == Button2)
else if (event->xbutton.button == Button2)
_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS);
else if (event.xbutton.button == Button3)
else if (event->xbutton.button == Button3)
_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS);
// XFree86 3.3.2 and later translates mouse wheel up/down into
// mouse button 4 & 5 presses
else if (event.xbutton.button == Button4)
else if (event->xbutton.button == Button4)
_glfwInputScroll(window, 0.0, 1.0);
else if (event.xbutton.button == Button5)
else if (event->xbutton.button == Button5)
_glfwInputScroll(window, 0.0, -1.0);
else if (event.xbutton.button == Button6)
else if (event->xbutton.button == Button6)
_glfwInputScroll(window, -1.0, 0.0);
else if (event.xbutton.button == Button7)
else if (event->xbutton.button == Button7)
_glfwInputScroll(window, 1.0, 0.0);
break;
@ -553,23 +552,23 @@ static void processSingleEvent(void)
case ButtonRelease:
{
// A mouse button was released
window = findWindow(event.xbutton.window);
window = findWindow(event->xbutton.window);
if (window == NULL)
return;
if (event.xbutton.button == Button1)
if (event->xbutton.button == Button1)
{
_glfwInputMouseClick(window,
GLFW_MOUSE_BUTTON_LEFT,
GLFW_RELEASE);
}
else if (event.xbutton.button == Button2)
else if (event->xbutton.button == Button2)
{
_glfwInputMouseClick(window,
GLFW_MOUSE_BUTTON_MIDDLE,
GLFW_RELEASE);
}
else if (event.xbutton.button == Button3)
else if (event->xbutton.button == Button3)
{
_glfwInputMouseClick(window,
GLFW_MOUSE_BUTTON_RIGHT,
@ -581,7 +580,7 @@ static void processSingleEvent(void)
case EnterNotify:
{
// The cursor entered the window
window = findWindow(event.xcrossing.window);
window = findWindow(event->xcrossing.window);
if (window == NULL)
return;
@ -595,7 +594,7 @@ static void processSingleEvent(void)
case LeaveNotify:
{
// The cursor left the window
window = findWindow(event.xcrossing.window);
window = findWindow(event->xcrossing.window);
if (window == NULL)
return;
@ -609,12 +608,12 @@ static void processSingleEvent(void)
case MotionNotify:
{
// The cursor was moved
window = findWindow(event.xmotion.window);
window = findWindow(event->xmotion.window);
if (window == NULL)
return;
if (event.xmotion.x != window->X11.cursorPosX ||
event.xmotion.y != window->X11.cursorPosY)
if (event->xmotion.x != window->X11.cursorPosX ||
event->xmotion.y != window->X11.cursorPosY)
{
// The cursor was moved by something other than GLFW
@ -625,17 +624,17 @@ static void processSingleEvent(void)
if (_glfwLibrary.activeWindow != window)
break;
x = event.xmotion.x - window->X11.cursorPosX;
y = event.xmotion.y - window->X11.cursorPosY;
x = event->xmotion.x - window->X11.cursorPosX;
y = event->xmotion.y - window->X11.cursorPosY;
}
else
{
x = event.xmotion.x;
y = event.xmotion.y;
x = event->xmotion.x;
y = event->xmotion.y;
}
window->X11.cursorPosX = event.xmotion.x;
window->X11.cursorPosY = event.xmotion.y;
window->X11.cursorPosX = event->xmotion.x;
window->X11.cursorPosY = event->xmotion.y;
window->X11.cursorCentered = GL_FALSE;
_glfwInputCursorMotion(window, x, y);
@ -647,17 +646,17 @@ static void processSingleEvent(void)
case ConfigureNotify:
{
// The window configuration changed somehow
window = findWindow(event.xconfigure.window);
window = findWindow(event->xconfigure.window);
if (window == NULL)
return;
_glfwInputWindowSize(window,
event.xconfigure.width,
event.xconfigure.height);
event->xconfigure.width,
event->xconfigure.height);
_glfwInputWindowPos(window,
event.xconfigure.x,
event.xconfigure.y);
event->xconfigure.x,
event->xconfigure.y);
break;
}
@ -665,11 +664,11 @@ static void processSingleEvent(void)
case ClientMessage:
{
// Custom client message, probably from the window manager
window = findWindow(event.xclient.window);
window = findWindow(event->xclient.window);
if (window == NULL)
return;
if ((Atom) event.xclient.data.l[0] == _glfwLibrary.X11.wmDeleteWindow)
if ((Atom) event->xclient.data.l[0] == _glfwLibrary.X11.wmDeleteWindow)
{
// The window manager was asked to close the window, for example by
// the user pressing a 'close' window decoration button
@ -677,17 +676,17 @@ static void processSingleEvent(void)
_glfwInputWindowCloseRequest(window);
}
else if (_glfwLibrary.X11.wmPing != None &&
(Atom) event.xclient.data.l[0] == _glfwLibrary.X11.wmPing)
(Atom) event->xclient.data.l[0] == _glfwLibrary.X11.wmPing)
{
// The window manager is pinging the application to ensure it's
// still responding to events
event.xclient.window = _glfwLibrary.X11.root;
event->xclient.window = _glfwLibrary.X11.root;
XSendEvent(_glfwLibrary.X11.display,
event.xclient.window,
event->xclient.window,
False,
SubstructureNotifyMask | SubstructureRedirectMask,
&event);
event);
}
break;
@ -696,7 +695,7 @@ static void processSingleEvent(void)
case MapNotify:
{
// The window was mapped
window = findWindow(event.xmap.window);
window = findWindow(event->xmap.window);
if (window == NULL)
return;
@ -707,7 +706,7 @@ static void processSingleEvent(void)
case UnmapNotify:
{
// The window was unmapped
window = findWindow(event.xmap.window);
window = findWindow(event->xmap.window);
if (window == NULL)
return;
@ -718,7 +717,7 @@ static void processSingleEvent(void)
case FocusIn:
{
// The window gained focus
window = findWindow(event.xfocus.window);
window = findWindow(event->xfocus.window);
if (window == NULL)
return;
@ -733,7 +732,7 @@ static void processSingleEvent(void)
case FocusOut:
{
// The window lost focus
window = findWindow(event.xfocus.window);
window = findWindow(event->xfocus.window);
if (window == NULL)
return;
@ -748,7 +747,7 @@ static void processSingleEvent(void)
case Expose:
{
// The window's contents was damaged
window = findWindow(event.xexpose.window);
window = findWindow(event->xexpose.window);
if (window == NULL)
return;
@ -769,7 +768,7 @@ static void processSingleEvent(void)
{
// The selection conversion status is available
XSelectionEvent* request = &event.xselection;
XSelectionEvent* request = &event->xselection;
if (_glfwReadSelection(request))
_glfwLibrary.X11.selection.status = _GLFW_CONVERSION_SUCCEEDED;
@ -783,7 +782,7 @@ static void processSingleEvent(void)
{
// The contents of the selection was requested
XSelectionRequestEvent* request = &event.xselectionrequest;
XSelectionRequestEvent* request = &event->xselectionrequest;
XEvent response;
memset(&response, 0, sizeof(response));
@ -808,11 +807,11 @@ static void processSingleEvent(void)
default:
{
#if defined(_GLFW_HAS_XRANDR)
switch (event.type - _glfwLibrary.X11.RandR.eventBase)
switch (event->type - _glfwLibrary.X11.RandR.eventBase)
{
case RRScreenChangeNotify:
{
XRRUpdateConfiguration(&event);
XRRUpdateConfiguration(event);
break;
}
}
@ -957,7 +956,6 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
{
int mode = 0, rate, sizeChanged = GL_FALSE;
XSizeHints* sizehints;
rate = window->refreshRate;
@ -971,14 +969,14 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
{
// Update window size restrictions to match new window size
sizehints = XAllocSizeHints();
sizehints->flags = 0;
XSizeHints* hints = XAllocSizeHints();
sizehints->min_width = sizehints->max_width = width;
sizehints->min_height = sizehints->max_height = height;
hints->flags |= (PMinSize | PMaxSize);
hints->min_width = hints->max_width = width;
hints->min_height = hints->max_height = height;
XSetWMNormalHints(_glfwLibrary.X11.display, window->X11.handle, sizehints);
XFree(sizehints);
XSetWMNormalHints(_glfwLibrary.X11.display, window->X11.handle, hints);
XFree(hints);
}
// Change window size before changing fullscreen mode?
@ -1093,13 +1091,18 @@ void _glfwPlatformRefreshWindowParams(_GLFWwindow* window)
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;
// 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;
if (window)
{
@ -1126,13 +1129,18 @@ void _glfwPlatformPollEvents(void)
void _glfwPlatformWaitEvents(void)
{
XEvent event;
int fd;
fd_set fds;
// Block waiting for an event to arrive
XNextEvent(_glfwLibrary.X11.display, &event);
XPutBackEvent(_glfwLibrary.X11.display, &event);
fd = ConnectionNumber(_glfwLibrary.X11.display);
_glfwPlatformPollEvents();
FD_ZERO(&fds);
FD_SET(fd, &fds);
XFlush(_glfwLibrary.X11.display);
if (select(fd + 1, &fds, NULL, NULL, NULL) > 0)
_glfwPlatformPollEvents();
}

588
support/tinycthread.c Normal file
View 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
View 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 threads 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_ */

View File

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

View File

@ -317,6 +317,9 @@ int main(int argc, char** argv)
get_client_api_name(api),
glGetString(GL_VENDOR));
printf("OpenGL context debug flag saved by GLFW: %s\n",
glfwGetWindowParam(window, GLFW_OPENGL_DEBUG_CONTEXT) ? "true" : "false");
if (major > 1)
{
printf("%s context shading language version: \"%s\"\n",

131
tests/threads.c Normal file
View 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);
}