diff --git a/.gitignore b/.gitignore index ff40c17e..1366b5fd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,18 @@ .DS_Store +Makefile CMakeCache.txt CMakeFiles cmake_install.cmake cmake_uninstall.cmake docs/Doxyfile +src/config.h +src/glfw3.pc +src/libglfw3.so +src/libglfw3.a +src/libglfw3.dylib +src/glfw3.lib +src/glfw3.dll +src/glfw3dll.lib examples/*.app examples/*.exe examples/boing @@ -13,21 +22,11 @@ examples/splitview examples/triangle examples/wave src/config.h -src/glfw.dll -src/glfw.lib -src/glfwdll.lib -src/libglfw.a -src/libglfw.dll -src/libglfw.dylib -src/libglfw.lib -src/libglfw.pc -src/libglfw.so -src/libglfwdll.lib tests/*.app tests/*.exe tests/accuracy +tests/clipboard tests/defaults -tests/dynamic tests/events tests/fsaa tests/fsfocus @@ -35,10 +34,12 @@ tests/gamma tests/glfwinfo tests/iconify tests/joysticks +tests/modes tests/peter tests/reopen tests/sharing tests/tearing +tests/threads tests/title tests/version tests/windows diff --git a/CMakeLists.txt b/CMakeLists.txt index cd712c46..a1ad4923 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,10 +12,17 @@ set(LIB_SUFFIX "" CACHE STRING "Takes an empty string or 64. Directory where lib option(GLFW_BUILD_EXAMPLES "Build the GLFW example programs" ON) option(GLFW_BUILD_TESTS "Build the GLFW test programs" ON) +option(GLFW_NATIVE_API "Build the GLFW native API" OFF) option(BUILD_SHARED_LIBS "Build shared libraries" OFF) find_package(OpenGL REQUIRED) +set(CMAKE_THREAD_PREFER_PTHREADS YES) +find_package(Threads) +if (CMAKE_THREAD_LIBS_INIT) + list(APPEND glfw_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) +endif() + #-------------------------------------------------------------------- # Enable all warnings on GCC, regardless of OS #-------------------------------------------------------------------- @@ -177,7 +184,7 @@ if (_GLFW_X11_GLX) endif() if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - set(_GLFW_USE_LINUX_JOYSTICKS 1) + set(_GLFW_HAS_LINUX_JOYSTICKS 1) endif() endif() @@ -253,7 +260,12 @@ configure_file(${GLFW_SOURCE_DIR}/src/config.h.in # The src directory's CMakeLists.txt file installs the library #-------------------------------------------------------------------- install(DIRECTORY include/GL DESTINATION include - FILES_MATCHING PATTERN glfw3.h PATTERN glfw3native.h) + FILES_MATCHING PATTERN glfw3.h) + +if (GLFW_NATIVE_API) + install(DIRECTORY include/GL DESTINATION include + FILES_MATCHING PATTERN glfw3native.h) +endif() install(FILES COPYING.txt readme.html DESTINATION share/doc/glfw-${GLFW_VERSION_FULL}) diff --git a/examples/heightmap.c b/examples/heightmap.c index fce8b8de..5139a493 100644 --- a/examples/heightmap.c +++ b/examples/heightmap.c @@ -581,7 +581,7 @@ int main(int argc, char** argv) exit(EXIT_FAILURE); } - glfwWindowHint(GLFW_WINDOW_RESIZABLE, GL_FALSE); + glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); glfwWindowHint(GLFW_OPENGL_VERSION_MAJOR, 3); glfwWindowHint(GLFW_OPENGL_VERSION_MINOR, 2); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); diff --git a/include/GL/glfw3.h b/include/GL/glfw3.h index 335a2a3b..df6e26f3 100644 --- a/include/GL/glfw3.h +++ b/include/GL/glfw3.h @@ -399,18 +399,19 @@ extern "C" { #define GLFW_ACCUM_ALPHA_BITS 0x0002100A #define GLFW_AUX_BUFFERS 0x0002100B #define GLFW_STEREO 0x0002100C -#define GLFW_WINDOW_RESIZABLE 0x0002100D #define GLFW_FSAA_SAMPLES 0x0002100E /* The following constants are used with both glfwGetWindowParam * and glfwWindowHint */ -#define GLFW_OPENGL_VERSION_MAJOR 0x0002100F -#define GLFW_OPENGL_VERSION_MINOR 0x00021010 -#define GLFW_OPENGL_FORWARD_COMPAT 0x00021011 -#define GLFW_OPENGL_DEBUG_CONTEXT 0x00021012 -#define GLFW_OPENGL_PROFILE 0x00021013 -#define GLFW_OPENGL_ROBUSTNESS 0x00021014 +#define GLFW_OPENGL_VERSION_MAJOR 0x00022000 +#define GLFW_OPENGL_VERSION_MINOR 0x00022001 +#define GLFW_OPENGL_FORWARD_COMPAT 0x00022002 +#define GLFW_OPENGL_DEBUG_CONTEXT 0x00022003 +#define GLFW_OPENGL_PROFILE 0x00022004 +#define GLFW_OPENGL_ROBUSTNESS 0x00022005 +#define GLFW_RESIZABLE 0x00022006 +#define GLFW_VISIBLE 0x00022007 /* GLFW_OPENGL_ROBUSTNESS mode tokens */ #define GLFW_OPENGL_NO_ROBUSTNESS 0x00000000 @@ -550,13 +551,15 @@ GLFWAPI void glfwSetGammaRamp(const GLFWgammaramp* ramp); GLFWAPI void glfwWindowHint(int target, int hint); GLFWAPI GLFWwindow glfwCreateWindow(int width, int height, int mode, const char* title, GLFWwindow share); GLFWAPI void glfwDestroyWindow(GLFWwindow window); -GLFWAPI void glfwSetWindowTitle(GLFWwindow, const char* title); -GLFWAPI void glfwGetWindowSize(GLFWwindow, int* width, int* height); -GLFWAPI void glfwSetWindowSize(GLFWwindow, int width, int height); -GLFWAPI void glfwGetWindowPos(GLFWwindow, int* xpos, int* ypos); -GLFWAPI void glfwSetWindowPos(GLFWwindow, int xpos, int ypos); +GLFWAPI void glfwSetWindowTitle(GLFWwindow window, const char* title); +GLFWAPI void glfwGetWindowSize(GLFWwindow window, int* width, int* height); +GLFWAPI void glfwSetWindowSize(GLFWwindow window, int width, int height); +GLFWAPI void glfwGetWindowPos(GLFWwindow window, int* xpos, int* ypos); +GLFWAPI void glfwSetWindowPos(GLFWwindow window, int xpos, int ypos); GLFWAPI void glfwIconifyWindow(GLFWwindow window); GLFWAPI void glfwRestoreWindow(GLFWwindow window); +GLFWAPI void glfwShowWindow(GLFWwindow window); +GLFWAPI void glfwHideWindow(GLFWwindow window); GLFWAPI int glfwGetWindowParam(GLFWwindow window, int param); GLFWAPI void glfwSetWindowUserPointer(GLFWwindow window, void* pointer); GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow window); diff --git a/readme.html b/readme.html index 5f0a77bb..bb745470 100644 --- a/readme.html +++ b/readme.html @@ -283,9 +283,11 @@ version of GLFW.

  • Added GLFW_OPENGL_ROBUSTNESS window hint and associated strategy tokens for GL_ARB_robustness support
  • Added GLFW_OPENGL_REVISION window parameter to make up for removal of glfwGetGLVersion
  • Added GLFW_INCLUDE_GL3 macro for telling the GLFW header to include gl3.h header instead of gl.h
  • +
  • Added GLFW_VISIBLE window hint and parameter for controlling and polling window visibility
  • Added windows simple multi-window test program
  • Added sharing simple OpenGL object sharing test program
  • Added modes video mode enumeration and setting test program
  • +
  • Added threads simple multi-threaded rendering test program
  • Added glfw3native.h header and platform-specific functions for explicit access to native display, window and context handles
  • Added glfwSetGamma, glfwSetGammaRamp and glfwGetGammaRamp functions and GLFWgammaramp type for monitor gamma ramp control
  • Added window parameter to glfwSwapBuffers
  • @@ -296,7 +298,7 @@ version of GLFW.

  • Renamed glfw.h to glfw3.h to avoid conflicts with 2.x series
  • Renamed glfwOpenWindowHint to glfwWindowHint
  • Renamed GLFW_WINDOW token to GLFW_WINDOWED
  • -
  • Renamed GLFW_WINDOW_NO_RESIZE to GLFW_WINDOW_RESIZABLE
  • +
  • Renamed GLFW_WINDOW_NO_RESIZE to GLFW_RESIZABLE
  • Renamed GLFW_BUILD_DLL to _GLFW_BUILD_DLL
  • Renamed version test to glfwinfo
  • Renamed GLFW_NO_GLU to GLFW_INCLUDE_GLU and made it disabled by default
  • @@ -349,6 +351,7 @@ version of GLFW.

  • [X11] Bugfix: Some window properties required by the ICCCM were not set
  • [X11] Bugfix: Calling glXCreateContextAttribsARB with an unavailable OpenGL version caused the application to terminate with a BadMatch Xlib error
  • [X11] Bugfix: A synchronization point necessary for jitter-free locked cursor mode was incorrectly removed
  • +
  • [X11] Bugfix: The window size hints were not updated when calling glfwSetWindowSize on a non-resizable window
  • [Win32] Changed port to use Unicode mode only
  • [Win32] Removed explicit support for versions of Windows older than Windows XP
  • [Win32] Bugfix: Window activation and iconification did not work as expected
  • @@ -907,7 +910,7 @@ their skills. Special thanks go out to:

    language
  • Shane Liesegang, for providing a bug fix relating to Cocoa window - restoration
  • + restoration and reporting a bug on 32-bit Cocoa builds
  • Tristam MacDonald, for his bug reports and feedback on the Cocoa port
  • @@ -927,9 +930,15 @@ their skills. Special thanks go out to:

    Much of the Windows code of GLFW was originally based on Jeff's code +
  • Julian Møller, for reporting a bug in the Cocoa joystick code
  • +
  • Arturo J. Pérez, for a bug fix for cursor tracking on Mac OS X 10.6 Snow Leopard
  • +
  • Riku Salminen, for the initial implementation of + glfwShowWindow and glfwHideWindow, and for making + the X11 event processing able to support multi-threaded rendering
  • +
  • Douglas C. Schmidt and Irfan Pyarali, for their excellent article Strategies for Implementing POSIX Condition Variables on Win32
  • diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c7ffc3d9..e2eb45cc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,7 +10,11 @@ if (_GLFW_COCOA_NSGL) set(glfw_HEADERS ${common_HEADERS} cocoa_platform.h) set(glfw_SOURCES ${common_SOURCES} cocoa_clipboard.m cocoa_fullscreen.m cocoa_gamma.c cocoa_init.m cocoa_input.m cocoa_joystick.m - cocoa_native.m cocoa_opengl.m cocoa_time.c cocoa_window.m) + cocoa_opengl.m cocoa_time.c cocoa_window.m) + + if (GLFW_NATIVE_API) + list(APPEND glfw_SOURCES cocoa_native.m) + endif() # For some reason, CMake doesn't know about .m set_source_files_properties(${glfw_SOURCES} PROPERTIES LANGUAGE C) @@ -18,14 +22,22 @@ elseif (_GLFW_WIN32_WGL) set(glfw_HEADERS ${common_HEADERS} win32_platform.h) set(glfw_SOURCES ${common_SOURCES} win32_clipboard.c win32_fullscreen.c win32_gamma.c win32_init.c win32_input.c win32_joystick.c - win32_monitor.c win32_native.c win32_opengl.c win32_time.c - win32_window.c win32_dllmain.c) + win32_monitor.c win32_opengl.c win32_time.c win32_window.c + win32_dllmain.c) + + if (GLFW_NATIVE_API) + list(APPEND glfw_SOURCES win32_native.c) + endif() elseif (_GLFW_X11_GLX) set(glfw_HEADERS ${common_HEADERS} x11_platform.h) set(glfw_SOURCES ${common_SOURCES} x11_clipboard.c x11_fullscreen.c x11_gamma.c x11_init.c x11_input.c x11_joystick.c - x11_keysym2unicode.c x11_monitor.c x11_native.c - x11_opengl.c x11_time.c x11_window.c) + x11_keysym2unicode.c x11_monitor.c x11_opengl.c x11_time.c + x11_window.c) + + if (GLFW_NATIVE_API) + list(APPEND glfw_SOURCES x11_native.c) + endif() endif() add_library(glfw ${glfw_SOURCES} ${glfw_HEADERS}) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 76d5b8b2..95e752e3 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -101,6 +101,9 @@ int _glfwPlatformInit(void) _glfwInitJoysticks(); + if (!_glfwInitOpenGL()) + return GL_FALSE; + _glfwLibrary.NS.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); if (!_glfwLibrary.NS.eventSource) return GL_FALSE; @@ -139,6 +142,8 @@ int _glfwPlatformTerminate(void) _glfwTerminateJoysticks(); + _glfwTerminateOpenGL(); + return GL_TRUE; } diff --git a/src/cocoa_joystick.m b/src/cocoa_joystick.m index 7eac7f91..04c9e972 100644 --- a/src/cocoa_joystick.m +++ b/src/cocoa_joystick.m @@ -550,15 +550,15 @@ int _glfwPlatformGetJoystickAxes(int joy, float* axes, int numaxes) for (i = 0; i < numaxes; i++) { - _glfwJoystickElement* axes = + _glfwJoystickElement* elements = (_glfwJoystickElement*) CFArrayGetValueAtIndex(joystick.axes, i); - long readScale = axes->maxReport - axes->minReport; + long readScale = elements->maxReport - elements->minReport; if (readScale == 0) - axes[i] = axes->value; + axes[i] = elements->value; else - axes[i] = (2.0f * (axes->value - axes->minReport) / readScale) - 1.0f; + axes[i] = (2.0f * (elements->value - elements->minReport) / readScale) - 1.0f; if (i & 1) axes[i] = -axes[i]; diff --git a/src/cocoa_opengl.m b/src/cocoa_opengl.m index 2ffd774c..4d4c4299 100644 --- a/src/cocoa_opengl.m +++ b/src/cocoa_opengl.m @@ -29,11 +29,46 @@ #include "internal.h" +#include + + +//======================================================================== +// 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]; diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index cc1c1c91..2fb6143c 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -124,4 +124,8 @@ void _glfwTerminateJoysticks(void); GLboolean _glfwSetVideoMode(int* width, int* height, int* bpp, int* refreshRate); void _glfwRestoreVideoMode(void); +// OpenGL support +int _glfwInitOpenGL(void); +void _glfwTerminateOpenGL(void); + #endif // _platform_h_ diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 5129a21f..477a1fad 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -131,6 +131,25 @@ return NSTerminateCancel; } +- (void)applicationDidHide:(NSNotification *)notification +{ + _GLFWwindow* window; + + for (window = _glfwLibrary.windowListHead; window; window = window->next) + _glfwInputWindowVisibility(window, GL_FALSE); +} + +- (void)applicationDidUnhide:(NSNotification *)notification +{ + _GLFWwindow* window; + + for (window = _glfwLibrary.windowListHead; window; window = window->next) + { + if ([window->NS.object isVisible]) + _glfwInputWindowVisibility(window, GL_TRUE); + } +} + @end @@ -686,7 +705,7 @@ static GLboolean createWindow(_GLFWwindow* window, [window->NS.object setAcceptsMouseMovedEvents:YES]; [window->NS.object center]; - if ([window->NS.object respondsToSelector:@selector(setRestorable)]) + if ([window->NS.object respondsToSelector:@selector(setRestorable:)]) [window->NS.object setRestorable:NO]; return GL_TRUE; @@ -898,7 +917,6 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (!createContext(window, wndconfig, fbconfig)) return GL_FALSE; - [window->NS.object makeKeyAndOrderFront:nil]; [window->NSGL.context setView:[window->NS.object contentView]]; if (wndconfig->mode == GLFW_FULLSCREEN) @@ -1022,6 +1040,27 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window) } +//======================================================================== +// Show window +//======================================================================== + +void _glfwPlatformShowWindow(_GLFWwindow* window) +{ + [window->NS.object makeKeyAndOrderFront:nil]; + _glfwInputWindowVisibility(window, GL_TRUE); +} + + +//======================================================================== +// Hide window +//======================================================================== + +void _glfwPlatformHideWindow(_GLFWwindow* window) +{ + [window->NS.object orderOut:nil]; + _glfwInputWindowVisibility(window, GL_FALSE); +} + //======================================================================== // Write back window parameters into GLFW window structure //======================================================================== @@ -1093,7 +1132,7 @@ void _glfwPlatformSetCursorPos(_GLFWwindow* window, int x, int y) CGPoint mainScreenOrigin = CGDisplayBounds(CGMainDisplayID()).origin; double mainScreenHeight = CGDisplayBounds(CGMainDisplayID()).size.height; CGPoint targetPoint = CGPointMake(globalPoint.x - mainScreenOrigin.x, - mainScreenHeight - globalPoint.y - + mainScreenHeight - globalPoint.y - mainScreenOrigin.y); CGDisplayMoveCursorToPoint(CGMainDisplayID(), targetPoint); } diff --git a/src/config.h.in b/src/config.h.in index 01d541a2..a432d947 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -64,7 +64,7 @@ #cmakedefine _GLFW_HAS_GLXGETPROCADDRESSEXT // Define this to 1 if the Linux joystick API is available -#cmakedefine _GLFW_USE_LINUX_JOYSTICKS +#cmakedefine _GLFW_HAS_LINUX_JOYSTICKS // The GLFW version as used by glfwGetVersionString #define _GLFW_VERSION_FULL "@GLFW_VERSION_FULL@" diff --git a/src/fullscreen.c b/src/fullscreen.c index 37866261..6d8ada66 100644 --- a/src/fullscreen.c +++ b/src/fullscreen.c @@ -32,10 +32,8 @@ #include "internal.h" #include -#ifdef __APPLE__ -#include -#else -#include +#if _WIN32 + #include #endif diff --git a/src/internal.h b/src/internal.h index 8d5266da..9cf29690 100755 --- a/src/internal.h +++ b/src/internal.h @@ -99,6 +99,7 @@ struct _GLFWhints int auxBuffers; GLboolean stereo; GLboolean resizable; + GLboolean visible; int samples; int glMajor; int glMinor; @@ -121,6 +122,7 @@ struct _GLFWwndconfig const char* title; int refreshRate; GLboolean resizable; + GLboolean visible; int glMajor; int glMinor; GLboolean glForward; @@ -171,6 +173,7 @@ struct _GLFWwindow int positionX, positionY; int mode; // GLFW_WINDOW or GLFW_FULLSCREEN GLboolean resizable; // GL_TRUE if user may resize this window + GLboolean visible; // GL_TRUE if this window is visible int refreshRate; // monitor refresh rate void* userPointer; @@ -229,7 +232,6 @@ struct _GLFWlibrary _GLFWhints hints; _GLFWwindow* windowListHead; - _GLFWwindow* currentWindow; _GLFWwindow* activeWindow; _GLFWwindow* cursorLockWindow; _GLFWmonitor* monitorListHead; @@ -313,6 +315,8 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height); void _glfwPlatformSetWindowPos(_GLFWwindow* window, int x, int y); void _glfwPlatformIconifyWindow(_GLFWwindow* window); void _glfwPlatformRestoreWindow(_GLFWwindow* window); +void _glfwPlatformShowWindow(_GLFWwindow* window); +void _glfwPlatformHideWindow(_GLFWwindow* window); // Event processing void _glfwPlatformPollEvents(void); @@ -320,6 +324,7 @@ void _glfwPlatformWaitEvents(void); // OpenGL context management void _glfwPlatformMakeContextCurrent(_GLFWwindow* window); +_GLFWwindow* _glfwPlatformGetCurrentContext(void); void _glfwPlatformSwapBuffers(_GLFWwindow* window); void _glfwPlatformSwapInterval(int interval); void _glfwPlatformRefreshWindowParams(_GLFWwindow* window); @@ -340,6 +345,7 @@ void _glfwInputWindowFocus(_GLFWwindow* window, GLboolean activated); void _glfwInputWindowPos(_GLFWwindow* window, int x, int y); void _glfwInputWindowSize(_GLFWwindow* window, int width, int height); void _glfwInputWindowIconify(_GLFWwindow* window, int iconified); +void _glfwInputWindowVisibility(_GLFWwindow* window, int visible); void _glfwInputWindowDamage(_GLFWwindow* window); void _glfwInputWindowCloseRequest(_GLFWwindow* window); diff --git a/src/opengl.c b/src/opengl.c index a23a5f34..6f80fd7b 100644 --- a/src/opengl.c +++ b/src/opengl.c @@ -350,7 +350,7 @@ GLboolean _glfwIsValidContextConfig(_GLFWwndconfig* wndconfig) GLboolean _glfwRefreshContextParams(void) { - _GLFWwindow* window = _glfwLibrary.currentWindow; + _GLFWwindow* window = _glfwPlatformGetCurrentContext(); if (!parseGLVersion(&window->glMajor, &window->glMinor, @@ -417,7 +417,7 @@ GLboolean _glfwRefreshContextParams(void) GLboolean _glfwIsValidContext(_GLFWwndconfig* wndconfig) { - _GLFWwindow* window = _glfwLibrary.currentWindow; + _GLFWwindow* window = _glfwPlatformGetCurrentContext(); if (window->glMajor < wndconfig->glMajor || (window->glMajor == wndconfig->glMajor && @@ -492,16 +492,15 @@ GLFWAPI void glfwMakeContextCurrent(GLFWwindow handle) return; } - if (_glfwLibrary.currentWindow == window) + if (_glfwPlatformGetCurrentContext() == window) return; _glfwPlatformMakeContextCurrent(window); - _glfwLibrary.currentWindow = window; } //======================================================================== -// Returns the window whose OpenGL context is current +// Return the window object whose context is current //======================================================================== GLFWAPI GLFWwindow glfwGetCurrentContext(void) @@ -512,7 +511,7 @@ GLFWAPI GLFWwindow glfwGetCurrentContext(void) return NULL; } - return _glfwLibrary.currentWindow; + return _glfwPlatformGetCurrentContext(); } @@ -546,7 +545,7 @@ GLFWAPI void glfwSwapInterval(int interval) return; } - if (!_glfwLibrary.currentWindow) + if (!_glfwPlatformGetCurrentContext()) { _glfwSetError(GLFW_NO_CURRENT_CONTEXT, NULL); return; @@ -571,7 +570,7 @@ GLFWAPI int glfwExtensionSupported(const char* extension) return GL_FALSE; } - window = _glfwLibrary.currentWindow; + window = _glfwPlatformGetCurrentContext(); if (!window) { _glfwSetError(GLFW_NO_CURRENT_CONTEXT, NULL); @@ -632,7 +631,7 @@ GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname) return NULL; } - if (!_glfwLibrary.currentWindow) + if (!_glfwPlatformGetCurrentContext()) { _glfwSetError(GLFW_NO_CURRENT_CONTEXT, NULL); return NULL; @@ -660,7 +659,7 @@ GLFWAPI void glfwCopyContext(GLFWwindow hsrc, GLFWwindow hdst, unsigned long mas src = (_GLFWwindow*) hsrc; dst = (_GLFWwindow*) hdst; - if (_glfwLibrary.currentWindow == dst) + if (_glfwPlatformGetCurrentContext() == dst) { _glfwSetError(GLFW_INVALID_VALUE, "glfwCopyContext: Cannot copy OpenGL state to a current context"); diff --git a/src/win32_opengl.c b/src/win32_opengl.c index bf684094..d6e1b490 100644 --- a/src/win32_opengl.c +++ b/src/win32_opengl.c @@ -34,6 +34,24 @@ #include +//======================================================================== +// Thread local storage attribute macro +//======================================================================== +#if defined(_MSC_VER) + #define _GLFW_TLS __declspec(thread) +#elif defined(__GNUC__) + #define _GLFW_TLS __thread +#else + #define _GLFW_TLS +#endif + + +//======================================================================== +// The per-thread current context/window pointer +//======================================================================== +static _GLFW_TLS _GLFWwindow* _glfwCurrentWindow = NULL; + + //======================================================================== // Initialize WGL-specific extensions // This function is called once before initial context creation, i.e. before @@ -443,7 +461,7 @@ static GLboolean createContext(_GLFWwindow* window, } } - glfwMakeContextCurrent(window); + _glfwPlatformMakeContextCurrent(window); initWGLExtensions(window); return GL_TRUE; @@ -508,8 +526,8 @@ void _glfwDestroyContext(_GLFWwindow* window) { // This is duplicated from glfwDestroyWindow // TODO: Stop duplicating code - if (window == _glfwLibrary.currentWindow) - glfwMakeContextCurrent(NULL); + if (window == _glfwCurrentWindow) + _glfwPlatformMakeContextCurrent(NULL); if (window->WGL.context) { @@ -539,6 +557,18 @@ void _glfwPlatformMakeContextCurrent(_GLFWwindow* window) wglMakeCurrent(window->WGL.DC, window->WGL.context); else wglMakeCurrent(NULL, NULL); + + _glfwCurrentWindow = window; +} + + +//======================================================================== +// Return the window object whose context is current +//======================================================================== + +_GLFWwindow* _glfwPlatformGetCurrentContext(void) +{ + return _glfwCurrentWindow; } @@ -558,7 +588,7 @@ void _glfwPlatformSwapBuffers(_GLFWwindow* window) void _glfwPlatformSwapInterval(int interval) { - _GLFWwindow* window = _glfwLibrary.currentWindow; + _GLFWwindow* window = _glfwCurrentWindow; if (window->WGL.EXT_swap_control) window->WGL.SwapIntervalEXT(interval); @@ -573,7 +603,7 @@ int _glfwPlatformExtensionSupported(const char* extension) { const GLubyte* extensions; - _GLFWwindow* window = _glfwLibrary.currentWindow; + _GLFWwindow* window = _glfwCurrentWindow; if (window->WGL.GetExtensionsStringEXT != NULL) { diff --git a/src/win32_platform.h b/src/win32_platform.h index c0c2e157..aff0dc18 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -195,7 +195,7 @@ typedef struct _GLFWlibraryWin32 // Timer data struct { - GLboolean hasPerformanceCounter; + GLboolean hasPC; double resolution; unsigned int t0_32; __int64 t0_64; diff --git a/src/win32_time.c b/src/win32_time.c index 1e1d57f4..96f7b454 100644 --- a/src/win32_time.c +++ b/src/win32_time.c @@ -45,13 +45,13 @@ void _glfwInitTimer(void) if (QueryPerformanceFrequency((LARGE_INTEGER*) &freq)) { - _glfwLibrary.Win32.timer.hasPerformanceCounter = GL_TRUE; + _glfwLibrary.Win32.timer.hasPC = GL_TRUE; _glfwLibrary.Win32.timer.resolution = 1.0 / (double) freq; QueryPerformanceCounter((LARGE_INTEGER*) &_glfwLibrary.Win32.timer.t0_64); } else { - _glfwLibrary.Win32.timer.hasPerformanceCounter = GL_FALSE; + _glfwLibrary.Win32.timer.hasPC = GL_FALSE; _glfwLibrary.Win32.timer.resolution = 0.001; // winmm resolution is 1 ms _glfwLibrary.Win32.timer.t0_32 = _glfw_timeGetTime(); } @@ -71,7 +71,7 @@ double _glfwPlatformGetTime(void) double t; __int64 t_64; - if (_glfwLibrary.Win32.timer.hasPerformanceCounter) + if (_glfwLibrary.Win32.timer.hasPC) { QueryPerformanceCounter((LARGE_INTEGER*) &t_64); t = (double)(t_64 - _glfwLibrary.Win32.timer.t0_64); @@ -91,7 +91,7 @@ void _glfwPlatformSetTime(double t) { __int64 t_64; - if (_glfwLibrary.Win32.timer.hasPerformanceCounter) + if (_glfwLibrary.Win32.timer.hasPC) { QueryPerformanceCounter((LARGE_INTEGER*) &t_64); _glfwLibrary.Win32.timer.t0_64 = t_64 - (__int64) (t / _glfwLibrary.Win32.timer.resolution); diff --git a/src/win32_window.c b/src/win32_window.c index 8ff95351..a27687f2 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -33,95 +33,6 @@ #include #include - -//======================================================================== -// Enable/disable minimize/restore animations -//======================================================================== - -static int setMinMaxAnimations(int enable) -{ - ANIMATIONINFO AI; - int old_enable; - - // Get old animation setting - AI.cbSize = sizeof(ANIMATIONINFO); - SystemParametersInfo(SPI_GETANIMATION, AI.cbSize, &AI, 0); - old_enable = AI.iMinAnimate; - - // If requested, change setting - if (old_enable != enable) - { - AI.iMinAnimate = enable; - SystemParametersInfo(SPI_SETANIMATION, AI.cbSize, &AI, - SPIF_SENDCHANGE); - } - - return old_enable; -} - - -//======================================================================== -// Focus the window and bring it to the top of the stack -// Due to some nastiness with how XP handles SetForegroundWindow we have -// to go through some really bizarre measures to achieve this -//======================================================================== - -static void setForegroundWindow(HWND hWnd) -{ - int try_count = 0; - int old_animate; - - // Try the standard approach first... - BringWindowToTop(hWnd); - SetForegroundWindow(hWnd); - - // If it worked, return now - if (hWnd == GetForegroundWindow()) - { - // Try to modify the system settings (since this is the foreground - // process, we are allowed to do this) - SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, (LPVOID) 0, - SPIF_SENDCHANGE); - return; - } - - // For other Windows versions than 95 & NT4.0, the standard approach - // may not work, so if we failed we have to "trick" Windows into - // making our window the foureground window: Iconify and restore - // again. It is ugly, but it seems to work (we turn off those annoying - // zoom animations to make it look a bit better at least). - - // Turn off minimize/restore animations - old_animate = setMinMaxAnimations(0); - - // We try this a few times, just to be on the safe side of things... - do - { - // Iconify & restore - ShowWindow(hWnd, SW_HIDE); - ShowWindow(hWnd, SW_SHOWMINIMIZED); - ShowWindow(hWnd, SW_SHOWNORMAL); - - // Try to get focus - BringWindowToTop(hWnd); - SetForegroundWindow(hWnd); - - // We do not want to keep going on forever, so we keep track of - // how many times we tried - try_count++; - } - while (hWnd != GetForegroundWindow() && try_count <= 3); - - // Restore the system minimize/restore animation setting - setMinMaxAnimations(old_animate); - - // Try to modify the system settings (since this is now hopefully the - // foreground process, we are probably allowed to do this) - SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, (LPVOID) 0, - SPIF_SENDCHANGE); -} - - //======================================================================== // Hide mouse cursor //======================================================================== @@ -506,6 +417,12 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, return 0; } + case WM_SHOWWINDOW: + { + _glfwInputWindowVisibility(window, wParam ? GL_TRUE : GL_FALSE); + break; + } + case WM_SYSCOMMAND: { switch (wParam & 0xfff0) @@ -845,7 +762,7 @@ static int createWindow(_GLFWwindow* window, WCHAR* wideTitle; // Set common window styles - dwStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE; + dwStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN; dwExStyle = WS_EX_APPWINDOW; // Set window style, depending on fullscreen mode @@ -1080,9 +997,6 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, SWP_NOMOVE | SWP_NOSIZE); } - setForegroundWindow(window->Win32.handle); - SetFocus(window->Win32.handle); - return GL_TRUE; } @@ -1202,6 +1116,28 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window) } +//======================================================================== +// Show or hide window +//======================================================================== + +void _glfwPlatformShowWindow(_GLFWwindow* window) +{ + ShowWindow(window->Win32.handle, SW_SHOWNORMAL); + BringWindowToTop(window->Win32.handle); + SetForegroundWindow(window->Win32.handle); + SetFocus(window->Win32.handle); +} + + +//======================================================================== +// Show or hide window +//======================================================================== + +void _glfwPlatformHideWindow(_GLFWwindow* window) +{ + ShowWindow(window->Win32.handle, SW_HIDE); +} + //======================================================================== // Write back window parameters into GLFW window structure //======================================================================== diff --git a/src/window.c b/src/window.c index e59a1d25..b46065a7 100644 --- a/src/window.c +++ b/src/window.c @@ -33,10 +33,8 @@ #include #include -#ifdef __APPLE__ -#include -#else -#include +#if _WIN32 + #include #endif @@ -82,8 +80,9 @@ void _glfwSetDefaultWindowHints(void) _glfwLibrary.hints.glMajor = 1; _glfwLibrary.hints.glMinor = 0; - // The default is to allow window resizing + // The default is to show the window and allow window resizing _glfwLibrary.hints.resizable = GL_TRUE; + _glfwLibrary.hints.visible = GL_TRUE; // The default is 24 bits of depth, 8 bits of color _glfwLibrary.hints.depthBits = 24; @@ -182,6 +181,16 @@ void _glfwInputWindowIconify(_GLFWwindow* window, int iconified) } +//======================================================================== +// Register window visibility events +//======================================================================== + +void _glfwInputWindowVisibility(_GLFWwindow* window, int visible) +{ + window->visible = visible; +} + + //======================================================================== // Register window damage events //======================================================================== @@ -252,6 +261,7 @@ GLFWAPI GLFWwindow glfwCreateWindow(int width, int height, wndconfig.title = title; wndconfig.refreshRate = Max(_glfwLibrary.hints.refreshRate, 0); wndconfig.resizable = _glfwLibrary.hints.resizable ? GL_TRUE : GL_FALSE; + wndconfig.visible = _glfwLibrary.hints.visible ? GL_TRUE : GL_FALSE; wndconfig.glMajor = _glfwLibrary.hints.glMajor; wndconfig.glMinor = _glfwLibrary.hints.glMinor; wndconfig.glForward = _glfwLibrary.hints.glForward ? GL_TRUE : GL_FALSE; @@ -356,6 +366,9 @@ GLFWAPI GLFWwindow glfwCreateWindow(int width, int height, glClear(GL_COLOR_BUFFER_BIT); _glfwPlatformSwapBuffers(window); + if (wndconfig.visible || mode == GLFW_FULLSCREEN) + glfwShowWindow(window); + return window; } @@ -413,9 +426,12 @@ GLFWAPI void glfwWindowHint(int target, int hint) case GLFW_STEREO: _glfwLibrary.hints.stereo = hint; break; - case GLFW_WINDOW_RESIZABLE: + case GLFW_RESIZABLE: _glfwLibrary.hints.resizable = hint; break; + case GLFW_VISIBLE: + _glfwLibrary.hints.visible = hint; + break; case GLFW_FSAA_SAMPLES: _glfwLibrary.hints.samples = hint; break; @@ -463,8 +479,9 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow handle) return; // Clear the current context if this window's context is current - if (window == _glfwLibrary.currentWindow) - glfwMakeContextCurrent(NULL); + // TODO: Re-examine this in light of multithreading + if (window == _glfwPlatformGetCurrentContext()) + _glfwPlatformMakeContextCurrent(NULL); // Clear the active window pointer if this is the active window if (window == _glfwLibrary.activeWindow) @@ -652,6 +669,48 @@ GLFWAPI void glfwRestoreWindow(GLFWwindow handle) } +//======================================================================== +// Window show +//======================================================================== + +GLFWAPI void glfwShowWindow(GLFWwindow handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + + if (!_glfwInitialized) + { + _glfwSetError(GLFW_NOT_INITIALIZED, NULL); + return; + } + + if (window->mode == GLFW_FULLSCREEN) + return; + + _glfwPlatformShowWindow(window); +} + + +//======================================================================== +// Window hide +//======================================================================== + +GLFWAPI void glfwHideWindow(GLFWwindow handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + + if (!_glfwInitialized) + { + _glfwSetError(GLFW_NOT_INITIALIZED, NULL); + return; + } + + if (window->mode == GLFW_FULLSCREEN) + return; + + _glfwPlatformHideWindow(window); +} + + //======================================================================== // Get window parameter //======================================================================== @@ -676,8 +735,10 @@ GLFWAPI int glfwGetWindowParam(GLFWwindow handle, int param) return window->closeRequested; case GLFW_REFRESH_RATE: return window->refreshRate; - case GLFW_WINDOW_RESIZABLE: + case GLFW_RESIZABLE: return window->resizable; + case GLFW_VISIBLE: + return window->visible; case GLFW_OPENGL_VERSION_MAJOR: return window->glMajor; case GLFW_OPENGL_VERSION_MINOR: @@ -816,7 +877,7 @@ GLFWAPI void glfwSetWindowIconifyCallback(GLFWwindowiconifyfun cbfun) //======================================================================== -// Poll for new window and input events and close any flagged windows +// Poll for new window and input events //======================================================================== GLFWAPI void glfwPollEvents(void) diff --git a/src/x11_init.c b/src/x11_init.c index e5bb2b26..4f5ab060 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -634,6 +634,8 @@ static void terminateDisplay(void) int _glfwPlatformInit(void) { + XInitThreads(); + if (!initDisplay()) return GL_FALSE; @@ -646,7 +648,8 @@ int _glfwPlatformInit(void) _glfwLibrary.X11.cursor = createNULLCursor(); - _glfwInitJoysticks(); + if (!_glfwInitJoysticks()) + return GL_FALSE; _glfwInitMonitors(); @@ -720,7 +723,7 @@ const char* _glfwPlatformGetVersionString(void) #if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) " clock_gettime" #endif -#if defined(_GLFW_USE_LINUX_JOYSTICKS) +#if defined(_GLFW_HAS_LINUX_JOYSTICKS) " Linux-joystick-API" #else " no-joystick-support" diff --git a/src/x11_joystick.c b/src/x11_joystick.c index a5700c68..3d2f597d 100644 --- a/src/x11_joystick.c +++ b/src/x11_joystick.c @@ -30,17 +30,18 @@ #include "internal.h" -#ifdef _GLFW_USE_LINUX_JOYSTICKS +#ifdef _GLFW_HAS_LINUX_JOYSTICKS #include #include #include #include #include - +#include +#include #include #include -#endif // _GLFW_USE_LINUX_JOYSTICKS +#endif // _GLFW_HAS_LINUX_JOYSTICKS //======================================================================== @@ -49,11 +50,11 @@ static int openJoystickDevice(int joy, const char* path) { -#ifdef _GLFW_USE_LINUX_JOYSTICKS +#ifdef _GLFW_HAS_LINUX_JOYSTICKS char numAxes, numButtons; int fd, version; - fd = open(path, O_NONBLOCK); + fd = open(path, O_RDONLY | O_NONBLOCK); if (fd == -1) return GL_FALSE; @@ -96,7 +97,7 @@ static int openJoystickDevice(int joy, const char* path) } _glfwLibrary.X11.joystick[joy].present = GL_TRUE; -#endif // _GLFW_USE_LINUX_JOYSTICKS +#endif // _GLFW_HAS_LINUX_JOYSTICKS return GL_TRUE; } @@ -108,7 +109,7 @@ static int openJoystickDevice(int joy, const char* path) static void pollJoystickEvents(void) { -#ifdef _GLFW_USE_LINUX_JOYSTICKS +#ifdef _GLFW_HAS_LINUX_JOYSTICKS int i; ssize_t result; struct js_event e; @@ -127,7 +128,7 @@ static void pollJoystickEvents(void) if (errno == ENODEV) _glfwLibrary.X11.joystick[i].present = GL_FALSE; - if (result < sizeof(e)) + if (result == -1) break; // We don't care if it's an init event or not @@ -159,7 +160,7 @@ static void pollJoystickEvents(void) } } } -#endif // _GLFW_USE_LINUX_JOYSTICKS +#endif // _GLFW_HAS_LINUX_JOYSTICKS } @@ -171,30 +172,52 @@ static void pollJoystickEvents(void) // Initialize joystick interface //======================================================================== -void _glfwInitJoysticks(void) +int _glfwInitJoysticks(void) { -#ifdef _GLFW_USE_LINUX_JOYSTICKS - int i, j, joy = 0; - char path[20]; - const char* bases[] = +#ifdef _GLFW_HAS_LINUX_JOYSTICKS + int i, joy = 0; + regex_t regex; + DIR* dir; + const char* dirs[] = { - "/dev/input/js", - "/dev/js" + "/dev/input", + "/dev" }; - for (i = 0; i < sizeof(bases) / sizeof(bases[0]); i++) + if (regcomp(®ex, "^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(®ex, entry->d_name, 1, &match, 0) != 0) + continue; + + snprintf(path, sizeof(path), "%s/%s", dirs[i], entry->d_name); if (openJoystickDevice(joy, path)) joy++; } + + closedir(dir); } -#endif // _GLFW_USE_LINUX_JOYSTICKS + + regfree(®ex); +#endif // _GLFW_HAS_LINUX_JOYSTICKS + + return GL_TRUE; } @@ -204,7 +227,7 @@ void _glfwInitJoysticks(void) void _glfwTerminateJoysticks(void) { -#ifdef _GLFW_USE_LINUX_JOYSTICKS +#ifdef _GLFW_HAS_LINUX_JOYSTICKS int i; for (i = 0; i <= GLFW_JOYSTICK_LAST; i++) @@ -218,7 +241,7 @@ void _glfwTerminateJoysticks(void) _glfwLibrary.X11.joystick[i].present = GL_FALSE; } } -#endif // _GLFW_USE_LINUX_JOYSTICKS +#endif // _GLFW_HAS_LINUX_JOYSTICKS } diff --git a/src/x11_opengl.c b/src/x11_opengl.c index a7c3f61b..af1e0ffb 100644 --- a/src/x11_opengl.c +++ b/src/x11_opengl.c @@ -38,6 +38,22 @@ void (*glXGetProcAddressEXT(const GLubyte* procName))(); +//======================================================================== +// Thread local storage attribute macro +//======================================================================== +#if defined(__GNUC__) + #define _GLFW_TLS __thread +#else + #define _GLFW_TLS +#endif + + +//======================================================================== +// The per-thread current context/window pointer +//======================================================================== +static _GLFW_TLS _GLFWwindow* _glfwCurrentWindow = NULL; + + //======================================================================== // Returns the specified attribute of the specified GLXFBConfig // NOTE: Do not call this unless we have found GLX 1.3+ or GLX_SGIX_fbconfig @@ -616,6 +632,10 @@ XVisualInfo* _glfwGetContextVisual(_GLFWwindow* window) } +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + //======================================================================== // Make the OpenGL context associated with the specified window current //======================================================================== @@ -630,6 +650,18 @@ void _glfwPlatformMakeContextCurrent(_GLFWwindow* window) } else glXMakeCurrent(_glfwLibrary.X11.display, None, NULL); + + _glfwCurrentWindow = window; +} + + +//======================================================================== +// Return the window object whose context is current +//======================================================================== + +_GLFWwindow* _glfwPlatformGetCurrentContext(void) +{ + return _glfwCurrentWindow; } @@ -649,7 +681,7 @@ void _glfwPlatformSwapBuffers(_GLFWwindow* window) void _glfwPlatformSwapInterval(int interval) { - _GLFWwindow* window = _glfwLibrary.currentWindow; + _GLFWwindow* window = _glfwCurrentWindow; if (_glfwLibrary.GLX.EXT_swap_control) { diff --git a/src/x11_platform.h b/src/x11_platform.h index cde2542b..96f162f1 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -322,7 +322,7 @@ void _glfwSetVideoMode(int* width, int* height, int* rate); void _glfwRestoreVideoMode(void); // Joystick input -void _glfwInitJoysticks(void); +int _glfwInitJoysticks(void); void _glfwTerminateJoysticks(void); // Monitors diff --git a/src/x11_window.c b/src/x11_window.c index af5b35ab..fae26e67 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -30,6 +30,8 @@ #include "internal.h" +#include + #include #include #include @@ -226,10 +228,6 @@ static GLboolean createWindow(_GLFWwindow* window, _glfwPlatformSetWindowTitle(window, wndconfig->title); - // Make sure the window is mapped before proceeding - XMapWindow(_glfwLibrary.X11.display, window->X11.handle); - XFlush(_glfwLibrary.X11.display); - return GL_TRUE; } @@ -460,34 +458,31 @@ static _GLFWwindow* findWindow(Window handle) //======================================================================== -// Get and process next X event (called by _glfwPlatformPollEvents) +// Process the specified X event //======================================================================== -static void processSingleEvent(void) +static void processEvent(XEvent *event) { _GLFWwindow* window; - XEvent event; - XNextEvent(_glfwLibrary.X11.display, &event); - - switch (event.type) + switch (event->type) { case KeyPress: { // A keyboard key was pressed - window = findWindow(event.xkey.window); + window = findWindow(event->xkey.window); if (window == NULL) return; - _glfwInputKey(window, translateKey(event.xkey.keycode), GLFW_PRESS); - _glfwInputChar(window, translateChar(&event.xkey)); + _glfwInputKey(window, translateKey(event->xkey.keycode), GLFW_PRESS); + _glfwInputChar(window, translateChar(&event->xkey)); break; } case KeyRelease: { // A keyboard key was released - window = findWindow(event.xkey.window); + window = findWindow(event->xkey.window); if (window == NULL) return; @@ -501,15 +496,15 @@ static void processSingleEvent(void) XPeekEvent(_glfwLibrary.X11.display, &nextEvent); if (nextEvent.type == KeyPress && - nextEvent.xkey.window == event.xkey.window && - nextEvent.xkey.keycode == event.xkey.keycode) + nextEvent.xkey.window == event->xkey.window && + nextEvent.xkey.keycode == event->xkey.keycode) { // This last check is a hack to work around key repeats // leaking through due to some sort of time drift // Toshiyuki Takahashi can press a button 16 times per // second so it's fairly safe to assume that no human is // pressing the key 50 times per second (value is ms) - if ((nextEvent.xkey.time - event.xkey.time) < 20) + if ((nextEvent.xkey.time - event->xkey.time) < 20) { // Do not report anything for this event break; @@ -517,34 +512,34 @@ static void processSingleEvent(void) } } - _glfwInputKey(window, translateKey(event.xkey.keycode), GLFW_RELEASE); + _glfwInputKey(window, translateKey(event->xkey.keycode), GLFW_RELEASE); break; } case ButtonPress: { // A mouse button was pressed or a scrolling event occurred - window = findWindow(event.xbutton.window); + window = findWindow(event->xbutton.window); if (window == NULL) return; - if (event.xbutton.button == Button1) + if (event->xbutton.button == Button1) _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS); - else if (event.xbutton.button == Button2) + else if (event->xbutton.button == Button2) _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS); - else if (event.xbutton.button == Button3) + else if (event->xbutton.button == Button3) _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS); // XFree86 3.3.2 and later translates mouse wheel up/down into // mouse button 4 & 5 presses - else if (event.xbutton.button == Button4) + else if (event->xbutton.button == Button4) _glfwInputScroll(window, 0.0, 1.0); - else if (event.xbutton.button == Button5) + else if (event->xbutton.button == Button5) _glfwInputScroll(window, 0.0, -1.0); - else if (event.xbutton.button == Button6) + else if (event->xbutton.button == Button6) _glfwInputScroll(window, -1.0, 0.0); - else if (event.xbutton.button == Button7) + else if (event->xbutton.button == Button7) _glfwInputScroll(window, 1.0, 0.0); break; @@ -553,23 +548,23 @@ static void processSingleEvent(void) case ButtonRelease: { // A mouse button was released - window = findWindow(event.xbutton.window); + window = findWindow(event->xbutton.window); if (window == NULL) return; - if (event.xbutton.button == Button1) + if (event->xbutton.button == Button1) { _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE); } - else if (event.xbutton.button == Button2) + else if (event->xbutton.button == Button2) { _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_RELEASE); } - else if (event.xbutton.button == Button3) + else if (event->xbutton.button == Button3) { _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, @@ -581,7 +576,7 @@ static void processSingleEvent(void) case EnterNotify: { // The cursor entered the window - window = findWindow(event.xcrossing.window); + window = findWindow(event->xcrossing.window); if (window == NULL) return; @@ -595,7 +590,7 @@ static void processSingleEvent(void) case LeaveNotify: { // The cursor left the window - window = findWindow(event.xcrossing.window); + window = findWindow(event->xcrossing.window); if (window == NULL) return; @@ -609,12 +604,12 @@ static void processSingleEvent(void) case MotionNotify: { // The cursor was moved - window = findWindow(event.xmotion.window); + window = findWindow(event->xmotion.window); if (window == NULL) return; - if (event.xmotion.x != window->X11.cursorPosX || - event.xmotion.y != window->X11.cursorPosY) + if (event->xmotion.x != window->X11.cursorPosX || + event->xmotion.y != window->X11.cursorPosY) { // The cursor was moved by something other than GLFW @@ -625,17 +620,17 @@ static void processSingleEvent(void) if (_glfwLibrary.activeWindow != window) break; - x = event.xmotion.x - window->X11.cursorPosX; - y = event.xmotion.y - window->X11.cursorPosY; + x = event->xmotion.x - window->X11.cursorPosX; + y = event->xmotion.y - window->X11.cursorPosY; } else { - x = event.xmotion.x; - y = event.xmotion.y; + x = event->xmotion.x; + y = event->xmotion.y; } - window->X11.cursorPosX = event.xmotion.x; - window->X11.cursorPosY = event.xmotion.y; + window->X11.cursorPosX = event->xmotion.x; + window->X11.cursorPosY = event->xmotion.y; window->X11.cursorCentered = GL_FALSE; _glfwInputCursorMotion(window, x, y); @@ -647,17 +642,17 @@ static void processSingleEvent(void) case ConfigureNotify: { // The window configuration changed somehow - window = findWindow(event.xconfigure.window); + window = findWindow(event->xconfigure.window); if (window == NULL) return; _glfwInputWindowSize(window, - event.xconfigure.width, - event.xconfigure.height); + event->xconfigure.width, + event->xconfigure.height); _glfwInputWindowPos(window, - event.xconfigure.x, - event.xconfigure.y); + event->xconfigure.x, + event->xconfigure.y); break; } @@ -665,11 +660,11 @@ static void processSingleEvent(void) case ClientMessage: { // Custom client message, probably from the window manager - window = findWindow(event.xclient.window); + window = findWindow(event->xclient.window); if (window == NULL) return; - if ((Atom) event.xclient.data.l[0] == _glfwLibrary.X11.wmDeleteWindow) + if ((Atom) event->xclient.data.l[0] == _glfwLibrary.X11.wmDeleteWindow) { // The window manager was asked to close the window, for example by // the user pressing a 'close' window decoration button @@ -677,17 +672,17 @@ static void processSingleEvent(void) _glfwInputWindowCloseRequest(window); } else if (_glfwLibrary.X11.wmPing != None && - (Atom) event.xclient.data.l[0] == _glfwLibrary.X11.wmPing) + (Atom) event->xclient.data.l[0] == _glfwLibrary.X11.wmPing) { // The window manager is pinging the application to ensure it's // still responding to events - event.xclient.window = _glfwLibrary.X11.root; + event->xclient.window = _glfwLibrary.X11.root; XSendEvent(_glfwLibrary.X11.display, - event.xclient.window, + event->xclient.window, False, SubstructureNotifyMask | SubstructureRedirectMask, - &event); + event); } break; @@ -696,10 +691,11 @@ static void processSingleEvent(void) case MapNotify: { // The window was mapped - window = findWindow(event.xmap.window); + window = findWindow(event->xmap.window); if (window == NULL) return; + _glfwInputWindowVisibility(window, GL_TRUE); _glfwInputWindowIconify(window, GL_FALSE); break; } @@ -707,10 +703,11 @@ static void processSingleEvent(void) case UnmapNotify: { // The window was unmapped - window = findWindow(event.xmap.window); + window = findWindow(event->xmap.window); if (window == NULL) return; + _glfwInputWindowVisibility(window, GL_FALSE); _glfwInputWindowIconify(window, GL_TRUE); break; } @@ -718,7 +715,7 @@ static void processSingleEvent(void) case FocusIn: { // The window gained focus - window = findWindow(event.xfocus.window); + window = findWindow(event->xfocus.window); if (window == NULL) return; @@ -733,7 +730,7 @@ static void processSingleEvent(void) case FocusOut: { // The window lost focus - window = findWindow(event.xfocus.window); + window = findWindow(event->xfocus.window); if (window == NULL) return; @@ -748,7 +745,7 @@ static void processSingleEvent(void) case Expose: { // The window's contents was damaged - window = findWindow(event.xexpose.window); + window = findWindow(event->xexpose.window); if (window == NULL) return; @@ -769,7 +766,7 @@ static void processSingleEvent(void) { // The selection conversion status is available - XSelectionEvent* request = &event.xselection; + XSelectionEvent* request = &event->xselection; if (_glfwReadSelection(request)) _glfwLibrary.X11.selection.status = _GLFW_CONVERSION_SUCCEEDED; @@ -783,7 +780,7 @@ static void processSingleEvent(void) { // The contents of the selection was requested - XSelectionRequestEvent* request = &event.xselectionrequest; + XSelectionRequestEvent* request = &event->xselectionrequest; XEvent response; memset(&response, 0, sizeof(response)); @@ -808,11 +805,11 @@ static void processSingleEvent(void) default: { #if defined(_GLFW_HAS_XRANDR) - switch (event.type - _glfwLibrary.X11.RandR.eventBase) + switch (event->type - _glfwLibrary.X11.RandR.eventBase) { case RRScreenChangeNotify: { - XRRUpdateConfiguration(&event); + XRRUpdateConfiguration(event); _glfwRefreshMonitors(); break; } @@ -958,7 +955,6 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) { int mode = 0, rate, sizeChanged = GL_FALSE; - XSizeHints* sizehints; rate = window->refreshRate; @@ -972,14 +968,14 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) { // Update window size restrictions to match new window size - sizehints = XAllocSizeHints(); - sizehints->flags = 0; + XSizeHints* hints = XAllocSizeHints(); - sizehints->min_width = sizehints->max_width = width; - sizehints->min_height = sizehints->max_height = height; + hints->flags |= (PMinSize | PMaxSize); + hints->min_width = hints->max_width = width; + hints->min_height = hints->max_height = height; - XSetWMNormalHints(_glfwLibrary.X11.display, window->X11.handle, sizehints); - XFree(sizehints); + XSetWMNormalHints(_glfwLibrary.X11.display, window->X11.handle, hints); + XFree(hints); } // Change window size before changing fullscreen mode? @@ -1047,6 +1043,28 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window) } +//======================================================================== +// Show window +//======================================================================== + +void _glfwPlatformShowWindow(_GLFWwindow* window) +{ + XMapRaised(_glfwLibrary.X11.display, window->X11.handle); + XFlush(_glfwLibrary.X11.display); +} + + +//======================================================================== +// Hide window +//======================================================================== + +void _glfwPlatformHideWindow(_GLFWwindow* window) +{ + XUnmapWindow(_glfwLibrary.X11.display, window->X11.handle); + XFlush(_glfwLibrary.X11.display); +} + + //======================================================================== // Read back framebuffer parameters from the context //======================================================================== @@ -1094,13 +1112,18 @@ void _glfwPlatformRefreshWindowParams(_GLFWwindow* window) void _glfwPlatformPollEvents(void) { + 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) { @@ -1127,13 +1150,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(); } diff --git a/support/tinycthread.c b/support/tinycthread.c new file mode 100644 index 00000000..ced7cf3f --- /dev/null +++ b/support/tinycthread.c @@ -0,0 +1,588 @@ +/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*- +Copyright (c) 2012 Marcus Geelnard + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +#include "tinycthread.h" +#include + +/* Platform specific includes */ +#if defined(_TTHREAD_POSIX_) + #include + #include + #include + #include + #include +#elif defined(_TTHREAD_WIN32_) + #include + #include +#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_ + diff --git a/support/tinycthread.h b/support/tinycthread.h new file mode 100644 index 00000000..18451ef9 --- /dev/null +++ b/support/tinycthread.h @@ -0,0 +1,439 @@ +/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*- +Copyright (c) 2012 Marcus Geelnard + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +#ifndef _TINYCTHREAD_H_ +#define _TINYCTHREAD_H_ + +/** +* @file +* @mainpage TinyCThread API Reference +* +* @section intro_sec Introduction +* TinyCThread is a minimal, portable implementation of basic threading +* classes for C. +* +* They closely mimic the functionality and naming of the C11 standard, and +* should be easily replaceable with the corresponding standard variants. +* +* @section port_sec Portability +* The Win32 variant uses the native Win32 API for implementing the thread +* classes, while for other systems, the POSIX threads API (pthread) is used. +* +* @section misc_sec Miscellaneous +* The following special keywords are available: #_Thread_local. +* +* For more detailed information, browse the different sections of this +* documentation. A good place to start is: +* tinycthread.h. +*/ + +/* Which platform are we on? */ +#if !defined(_TTHREAD_PLATFORM_DEFINED_) + #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) + #define _TTHREAD_WIN32_ + #else + #define _TTHREAD_POSIX_ + #endif + #define _TTHREAD_PLATFORM_DEFINED_ +#endif + +/* Activate some POSIX functionality (e.g. clock_gettime and recursive mutexes) */ +#if defined(_TTHREAD_POSIX_) + #undef _FEATURES_H + #if !defined(_GNU_SOURCE) + #define _GNU_SOURCE + #endif + #if !defined(_POSIX_C_SOURCE) || ((_POSIX_C_SOURCE - 0) < 199309L) + #undef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 199309L + #endif + #if !defined(_XOPEN_SOURCE) || ((_XOPEN_SOURCE - 0) < 500) + #undef _XOPEN_SOURCE + #define _XOPEN_SOURCE 500 + #endif +#endif + +/* Generic includes */ +#include + +/* Platform specific includes */ +#if defined(_TTHREAD_POSIX_) + #include +#elif defined(_TTHREAD_WIN32_) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #define __UNDEF_LEAN_AND_MEAN + #endif + #include + #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_ */ + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bcad7ef5..11bcfe9e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,12 +8,19 @@ else() link_libraries(${glfw_LIBRARIES}) endif() +list(APPEND thread_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) +if (UNIX AND NOT APPLE) + list(APPEND thread_LIBRARIES ${RT_LIBRARY}) +endif() + include_directories(${GLFW_SOURCE_DIR}/include ${GLFW_SOURCE_DIR}/support ${OPENGL_INCLUDE_DIR}) set(GETOPT ${GLFW_SOURCE_DIR}/support/getopt.h ${GLFW_SOURCE_DIR}/support/getopt.c) +set(TINYCTHREAD ${GLFW_SOURCE_DIR}/support/tinycthread.h + ${GLFW_SOURCE_DIR}/support/tinycthread.c) add_executable(clipboard clipboard.c ${GETOPT}) add_executable(defaults defaults.c) @@ -37,13 +44,20 @@ set_target_properties(sharing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Sharing") add_executable(tearing WIN32 MACOSX_BUNDLE tearing.c) set_target_properties(tearing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Tearing") +add_executable(threads WIN32 MACOSX_BUNDLE threads.c ${TINYCTHREAD}) +set_target_properties(threads PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Threads") + add_executable(title WIN32 MACOSX_BUNDLE title.c) set_target_properties(title PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Title") add_executable(windows WIN32 MACOSX_BUNDLE windows.c) set_target_properties(windows PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Windows") -set(WINDOWS_BINARIES accuracy sharing tearing title windows) +if (BUILD_SHARED_LIBS) + target_link_libraries(threads ${thread_LIBRARIES}) +endif() + +set(WINDOWS_BINARIES accuracy sharing tearing threads title windows) set(CONSOLE_BINARIES clipboard defaults events fsaa fsfocus gamma glfwinfo iconify joysticks modes peter reopen) diff --git a/tests/defaults.c b/tests/defaults.c index 391b4984..2877cfd9 100644 --- a/tests/defaults.c +++ b/tests/defaults.c @@ -83,6 +83,8 @@ int main(void) exit(EXIT_FAILURE); } + glfwWindowHint(GLFW_VISIBLE, GL_FALSE); + window = glfwCreateWindow(0, 0, GLFW_WINDOWED, "Defaults", NULL); if (!window) { diff --git a/tests/glfwinfo.c b/tests/glfwinfo.c index 52d6e0de..d11c861d 100644 --- a/tests/glfwinfo.c +++ b/tests/glfwinfo.c @@ -238,6 +238,8 @@ int main(int argc, char** argv) if (strategy) glfwWindowHint(GLFW_OPENGL_ROBUSTNESS, strategy); + glfwWindowHint(GLFW_VISIBLE, GL_FALSE); + // We assume here that we stand a better chance of success by leaving all // possible details of pixel format selection to GLFW @@ -288,6 +290,9 @@ int main(int argc, char** argv) get_glfw_profile_name(glfwGetWindowParam(window, GLFW_OPENGL_PROFILE))); } + printf("OpenGL context debug flag saved by GLFW: %s\n", + glfwGetWindowParam(window, GLFW_OPENGL_DEBUG_CONTEXT) ? "true" : "false"); + printf("OpenGL context renderer string: \"%s\"\n", glGetString(GL_RENDERER)); printf("OpenGL context vendor string: \"%s\"\n", glGetString(GL_VENDOR)); diff --git a/tests/threads.c b/tests/threads.c new file mode 100644 index 00000000..35c83716 --- /dev/null +++ b/tests/threads.c @@ -0,0 +1,131 @@ +//======================================================================== +// Multithreading test +// Copyright (c) Camilla Berglund +// +// 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 + +#include +#include +#include +#include + +#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); +} +