From 6350641f0aa27a4dd14c9c0b4e675680416b2c5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 1 May 2017 19:20:57 +0200 Subject: [PATCH] Add glfwGetError Related to #970. If you have opinions on the design or implementation of this function, please come join us in #970 before it is frozen for release. --- README.md | 1 + docs/intro.dox | 44 ++++++++++++++++++++++++++++++-------------- docs/news.dox | 9 +++++++++ include/GLFW/glfw3.h | 35 +++++++++++++++++++++++++++++++++++ src/init.c | 38 +++++++++++++++++++++++++++++++++++--- src/internal.h | 2 ++ src/posix_tls.c | 6 +++++- src/win32_tls.c | 6 +++++- 8 files changed, 122 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 155e5ae5..d6aed688 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,7 @@ information on what to include when reporting a bug. ## Changelog +- Added `glfwGetError` function for querying the last error code (#970) - Added `glfwGetKeyScancode` function that allows retrieving platform dependent scancodes for keys (#830) - Added `glfwSetWindowMaximizeCallback` and `GLFWwindowmaximizefun` for diff --git a/docs/intro.dox b/docs/intro.dox index de1b3ae0..55aa7862 100644 --- a/docs/intro.dox +++ b/docs/intro.dox @@ -30,6 +30,7 @@ successfully initialized, and only from the main thread. - @ref glfwGetVersion - @ref glfwGetVersionString + - @ref glfwGetError - @ref glfwSetErrorCallback - @ref glfwInitHint - @ref glfwInit @@ -131,9 +132,25 @@ not very helpful when trying to figure out _why_ the error occurred. Other functions have no return value reserved for errors, so error notification needs a separate channel. Finally, far from all GLFW functions have return values. -This is where the error callback comes in. This callback is called whenever an -error occurs. It is set with @ref glfwSetErrorCallback, a function that may be -called regardless of whether GLFW is initialized. +The last error code for the calling thread can be queried at any time with @ref +glfwGetError. + +@code +int error = glfwGetError(); +@endcode + +If no error has occurred since the last call, @ref GLFW_NO_ERROR is returned. +The error is cleared before the function returns. + +The error code indicates the general category of the error. Some error codes, +such as @ref GLFW_NOT_INITIALIZED has only a single meaning, whereas others like +@ref GLFW_PLATFORM_ERROR are used for many different errors. + +GLFW usually has much more information about the error than its general category +at the point where it occurred. This is where the error callback comes in. +This callback is called whenever an error occurs. It is set with @ref +glfwSetErrorCallback, a function that may be called regardless of whether GLFW +is initialized. @code glfwSetErrorCallback(error_callback); @@ -150,24 +167,23 @@ void error_callback(int error, const char* description) } @endcode -The error code indicates the general category of the error. Some error codes, -such as @ref GLFW_NOT_INITIALIZED has only a single meaning, whereas others like -@ref GLFW_PLATFORM_ERROR are used for many different errors. +The error callback is called after the error code is set, so calling @ref +glfwGetError from within the error callback returns the same value as the +callback argument. -Reported errors are never fatal. As long as GLFW was successfully initialized, -it will remain initialized and in a safe state until terminated regardless of -how many errors occur. If an error occurs during initialization that causes -@ref glfwInit to fail, any part of the library that was initialized will be -safely terminated. +__Reported errors are never fatal.__ As long as GLFW was successfully +initialized, it will remain initialized and in a safe state until terminated +regardless of how many errors occur. If an error occurs during initialization +that causes @ref glfwInit to fail, any part of the library that was initialized +will be safely terminated. The description string is only valid until the error callback returns, as it may have been generated specifically for that error. This lets GLFW provide much more specific error descriptions but means you must make a copy if you want to keep the description string. -@note Relying on erroneous behavior is not forward compatible. In other words, -do not rely on a currently invalid call to generate a specific error, as that -same call may in future versions generate a different error or become valid. +Do not rely on a currently invalid call to generate a specific error, as in the +future that same call may generate a different error or become valid. @section coordinate_systems Coordinate systems diff --git a/docs/news.dox b/docs/news.dox index 0be64b06..ad5de5aa 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -4,6 +4,15 @@ @section news_33 New features in 3.3 + +@subsection news_33_geterror Error query + +GLFW now supports querying the last error code for the calling thread with @ref +glfwGetError. + +@see @ref error_handling + + @subsection news_33_maximize Window maximization callback GLFW now supports window maximization notifications with @ref diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 2875dc5b..f0426ffb 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -546,6 +546,13 @@ extern "C" { * * @ingroup init * @{ */ +/*! @brief No error has occurred. + * + * No error has occurred. + * + * @analysis Yay. + */ +#define GLFW_NO_ERROR 0 /*! @brief GLFW has not been initialized. * * This occurs if a GLFW function was called that must not be called unless the @@ -1607,11 +1614,38 @@ GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev); */ GLFWAPI const char* glfwGetVersionString(void); +/*! @brief Returns and clears the last error for the calling thread. + * + * This function returns and clears the [error code](@ref error) of the last + * error that occurred on the calling thread. If no error has occurred since + * the last call, it returns @ref GLFW_NO_ERROR. + * + * @return The last error code for the calling thread, or @ref GLFW_NO_ERROR. + * + * @errors None. + * + * @remark This function may be called before @ref glfwInit. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref error_handling + * @sa @ref glfwSetErrorCallback + * + * @since Added in version 3.3. + * + * @ingroup init + */ +GLFWAPI int glfwGetError(void); + /*! @brief Sets the error callback. * * This function sets the error callback, which is called with an error code * and a human-readable description each time a GLFW error occurs. * + * The error code is set before the callback is called. Calling @ref + * glfwGetError from the error callback will return the same value as the error + * code argument. + * * The error callback is called on the thread where the error occurred. If you * are using GLFW from multiple threads, your error callback needs to be * written accordingly. @@ -1634,6 +1668,7 @@ GLFWAPI const char* glfwGetVersionString(void); * @thread_safety This function must only be called from the main thread. * * @sa @ref error_handling + * @sa @ref glfwGetError * * @since Added in version 3.0. * diff --git a/src/init.c b/src/init.c index 7585c567..a0c6e42e 100644 --- a/src/init.c +++ b/src/init.c @@ -43,6 +43,7 @@ _GLFWlibrary _glfw = { GLFW_FALSE }; // These are outside of _glfw so they can be used before initialization and // after termination // +static int _glfwErrorCode; static GLFWerrorfun _glfwErrorCallback; static _GLFWinitconfig _glfwInitHints = { @@ -113,7 +114,11 @@ static void terminate(void) _glfwTerminateVulkan(); _glfwPlatformTerminate(); + if (_glfwPlatformIsValidTls(&_glfw.error)) + _glfwErrorCode = (int) (intptr_t) _glfwPlatformGetTls(&_glfw.error); + _glfwPlatformDestroyTls(&_glfw.context); + _glfwPlatformDestroyTls(&_glfw.error); memset(&_glfw, 0, sizeof(_glfw)); } @@ -125,6 +130,11 @@ static void terminate(void) void _glfwInputError(int error, const char* format, ...) { + if (_glfw.initialized) + _glfwPlatformSetTls(&_glfw.error, (void*) (intptr_t) error); + else + _glfwErrorCode = error; + if (_glfwErrorCallback) { char buffer[8192]; @@ -164,15 +174,19 @@ GLFWAPI int glfwInit(void) memset(&_glfw, 0, sizeof(_glfw)); _glfw.hints.init = _glfwInitHints; - if (!_glfwPlatformCreateTls(&_glfw.context)) - return GLFW_FALSE; - if (!_glfwPlatformInit()) { terminate(); return GLFW_FALSE; } + if (!_glfwPlatformCreateTls(&_glfw.error)) + return GLFW_FALSE; + if (!_glfwPlatformCreateTls(&_glfw.context)) + return GLFW_FALSE; + + _glfwPlatformSetTls(&_glfw.error, (void*) (intptr_t) _glfwErrorCode); + _glfw.initialized = GLFW_TRUE; _glfw.timer.offset = _glfwPlatformGetTimerValue(); @@ -223,6 +237,24 @@ GLFWAPI const char* glfwGetVersionString(void) return _glfwPlatformGetVersionString(); } +GLFWAPI int glfwGetError(void) +{ + int error; + + if (_glfw.initialized) + { + error = (int) (intptr_t) _glfwPlatformGetTls(&_glfw.error); + _glfwPlatformSetTls(&_glfw.error, (intptr_t) GLFW_NO_ERROR); + } + else + { + error = _glfwErrorCode; + _glfwErrorCode = GLFW_NO_ERROR; + } + + return error; +} + GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun) { _GLFW_SWAP_POINTERS(_glfwErrorCallback, cbfun); diff --git a/src/internal.h b/src/internal.h index be9ced89..b7c35be9 100644 --- a/src/internal.h +++ b/src/internal.h @@ -517,6 +517,7 @@ struct _GLFWlibrary _GLFWjoystick joysticks[GLFW_JOYSTICK_LAST + 1]; + _GLFWtls error; _GLFWtls context; struct { @@ -653,6 +654,7 @@ GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls); void _glfwPlatformDestroyTls(_GLFWtls* tls); void* _glfwPlatformGetTls(_GLFWtls* tls); void _glfwPlatformSetTls(_GLFWtls* tls, void* value); +GLFWbool _glfwPlatformIsValidTls(_GLFWtls* tls); /*! @} */ diff --git a/src/posix_tls.c b/src/posix_tls.c index 980ff23b..cf2ae6fa 100644 --- a/src/posix_tls.c +++ b/src/posix_tls.c @@ -52,7 +52,6 @@ GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls) void _glfwPlatformDestroyTls(_GLFWtls* tls) { - assert(tls->posix.allocated == GLFW_TRUE); if (tls->posix.allocated) pthread_key_delete(tls->posix.key); memset(tls, 0, sizeof(_GLFWtls)); @@ -70,3 +69,8 @@ void _glfwPlatformSetTls(_GLFWtls* tls, void* value) pthread_setspecific(tls->posix.key, value); } +GLFWbool _glfwPlatformIsValidTls(_GLFWtls* tls) +{ + return tls->posix.allocated; +} + diff --git a/src/win32_tls.c b/src/win32_tls.c index 944f7e5e..9c20aad0 100644 --- a/src/win32_tls.c +++ b/src/win32_tls.c @@ -52,7 +52,6 @@ GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls) void _glfwPlatformDestroyTls(_GLFWtls* tls) { - assert(tls->win32.allocated == GLFW_TRUE); if (tls->win32.allocated) TlsFree(tls->win32.index); memset(tls, 0, sizeof(_GLFWtls)); @@ -70,3 +69,8 @@ void _glfwPlatformSetTls(_GLFWtls* tls, void* value) TlsSetValue(tls->win32.index, value); } +GLFWbool _glfwPlatformIsValidTls(_GLFWtls* tls) +{ + return tls->win32.allocated; +} +