From d88347ee7de728466a0931c6f707926a4653df9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 2 Mar 2017 17:52:32 +0100 Subject: [PATCH 01/36] Mir: Fix broken build Regression caused by e9560ef021c101def23a5c3b383d057b2a0e58d3. --- src/CMakeLists.txt | 5 +++-- src/mir_platform.h | 1 + src/mir_window.c | 19 +++++++++++++++---- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4ddb41cc1..f494619fb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,10 +48,11 @@ elseif (_GLFW_WAYLAND) BASENAME pointer-constraints-unstable-v1) elseif (_GLFW_MIR) set(glfw_HEADERS ${common_HEADERS} mir_platform.h linux_joystick.h - posix_time.h posix_tls.h xkb_unicode.h egl_context.h) + posix_time.h posix_tls.h xkb_unicode.h egl_context.h + osmesa_context.h) set(glfw_SOURCES ${common_SOURCES} mir_init.c mir_monitor.c mir_window.c linux_joystick.c posix_time.c posix_tls.c xkb_unicode.c - egl_context.c) + egl_context.c osmesa_context.c) elseif (_GLFW_OSMESA) set(glfw_HEADERS ${common_HEADERS} null_platform.h null_joystick.h posix_time.h posix_tls.h osmesa_context.h) diff --git a/src/mir_platform.h b/src/mir_platform.h index 39add6eeb..e9d60ecdd 100644 --- a/src/mir_platform.h +++ b/src/mir_platform.h @@ -52,6 +52,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceMirPresentationSupportKHR)(Vk #include "linux_joystick.h" #include "xkb_unicode.h" #include "egl_context.h" +#include "osmesa_context.h" #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlclose(handle) dlclose(handle) diff --git a/src/mir_window.c b/src/mir_window.c index 368b633ec..a3b383be5 100644 --- a/src/mir_window.c +++ b/src/mir_window.c @@ -405,10 +405,21 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (ctxconfig->client != GLFW_NO_API) { - if (!_glfwInitEGL()) - return GLFW_FALSE; - if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) - return GLFW_FALSE; + if (ctxconfig->source == GLFW_EGL_CONTEXT_API || + ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + { + if (!_glfwInitEGL()) + return GLFW_FALSE; + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } } return GLFW_TRUE; From 5b306f0390ba0b95d341b1a42e2983e25ba574ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 3 Mar 2017 14:22:38 +0100 Subject: [PATCH 02/36] Cleanup --- src/mir_window.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mir_window.c b/src/mir_window.c index a3b383be5..00d50ada9 100644 --- a/src/mir_window.c +++ b/src/mir_window.c @@ -443,10 +443,9 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) { MirSurfaceSpec* spec; - const char* e_title = title ? title : ""; spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); - mir_surface_spec_set_name(spec, e_title); + mir_surface_spec_set_name(spec, title); mir_surface_apply_spec(window->mir.surface, spec); mir_surface_spec_release(spec); From 0e0862cfc7694f87b5bcf1022b45c41a5f51c363 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 3 Mar 2017 17:51:40 +0000 Subject: [PATCH 03/36] Wayland: Fix broken build Regression caused by e9560ef021c101def23a5c3b383d057b2a0e58d3. --- src/CMakeLists.txt | 5 +++-- src/wl_platform.h | 1 + src/wl_window.c | 19 +++++++++++++++---- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f494619fb..438a7dea0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,10 +33,11 @@ elseif (_GLFW_X11) endif() elseif (_GLFW_WAYLAND) set(glfw_HEADERS ${common_HEADERS} wl_platform.h linux_joystick.h - posix_time.h posix_tls.h xkb_unicode.h egl_context.h) + posix_time.h posix_tls.h xkb_unicode.h egl_context.h + osmesa_context.h) set(glfw_SOURCES ${common_SOURCES} wl_init.c wl_monitor.c wl_window.c linux_joystick.c posix_time.c posix_tls.c xkb_unicode.c - egl_context.c) + egl_context.c osmesa_context.c) ecm_add_wayland_client_protocol(glfw_SOURCES PROTOCOL diff --git a/src/wl_platform.h b/src/wl_platform.h index f228d18a6..bea32350b 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -51,6 +51,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR #include "linux_joystick.h" #include "xkb_unicode.h" #include "egl_context.h" +#include "osmesa_context.h" #include "wayland-relative-pointer-unstable-v1-client-protocol.h" #include "wayland-pointer-constraints-unstable-v1-client-protocol.h" diff --git a/src/wl_window.c b/src/wl_window.c index f5269ab11..e6c554527 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -395,10 +395,21 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, if (ctxconfig->client != GLFW_NO_API) { - if (!_glfwInitEGL()) - return GLFW_FALSE; - if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) - return GLFW_FALSE; + if (ctxconfig->source == GLFW_EGL_CONTEXT_API || + ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + { + if (!_glfwInitEGL()) + return GLFW_FALSE; + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) + { + if (!_glfwInitOSMesa()) + return GLFW_FALSE; + if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } } if (wndconfig->title) From 12dcfd08b857342ff2bd25791a6713636b1c30a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Sun, 5 Mar 2017 16:45:09 +0100 Subject: [PATCH 04/36] Documentation work [ci skip] --- .github/CONTRIBUTING.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 47de6f6b6..2584ec3f2 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -51,6 +51,27 @@ __Don't worry about adding too much information__. Unimportant information can be abbreviated or removed later, but missing information can stall bug fixing, especially when your schedule doesn't align with that of the maintainer. +__Please provide text as text, not as images__. This includes code, error +messages and any other text. Text in images cannot be found by other users +searching for the same problem and may have to be re-typed by maintainers when +debugging. + +You don't need to manually indent your code or other text to quote it with +GitHub Markdown; just surround it with triple backticks: + + ``` + Some quoted text. + ``` + +You can also add syntax highlighting by appending the common file extension: + + ```c + int five(void) + { + return 5; + } + ``` + There are issue labels for both platforms and GPU manufacturers, so there is no need to mention these in the subject line. If you do, it will be removed when the issue is labeled. @@ -139,6 +160,9 @@ Always include the __operating system name and version__ (e.g. `Windows include the __GLFW release version__ (e.g. `3.1.2`), otherwise include the __GLFW commit ID__ (e.g. `3795d78b14ef06008889cc422a1fb8d642597751`) from Git. +If you are running your program in a virtual machine, please mention this and +include the __VM name and version__ (e.g. `VirtualBox 5.1`). + Please also include the __GLFW version string__ (`3.2.0 X11 EGL clock_gettime /dev/js`), as described [here](http://www.glfw.org/docs/latest/intro.html#intro_version_string), the @@ -178,6 +202,9 @@ Always include the __operating system name and version__ (e.g. `Windows include the __GLFW release version__ (e.g. `3.1.2`), otherwise include the __GLFW commit ID__ (e.g. `3795d78b14ef06008889cc422a1fb8d642597751`) from Git. +If you are running your program in a virtual machine, please mention this and +include the __VM name and version__ (e.g. `VirtualBox 5.1`). + Please also include any __error messages__ provided to your application via the [error callback](http://www.glfw.org/docs/latest/intro_guide.html#error_handling) and @@ -215,6 +242,9 @@ Always include the __operating system name and version__ (e.g. `Windows include the __GLFW release version__ (e.g. `3.1.2`), otherwise include the __GLFW commit ID__ (e.g. `3795d78b14ef06008889cc422a1fb8d642597751`) from Git. +If you are running your program in a virtual machine, please mention this and +include the __VM name and version__ (e.g. `VirtualBox 5.1`). + Please also include any __error messages__ provided to your application via the [error callback](http://www.glfw.org/docs/latest/intro_guide.html#error_handling) and From 368dec7ac712fc4a4a2f77aaec68b62fb8f98749 Mon Sep 17 00:00:00 2001 From: IntellectualKitty Date: Fri, 25 Nov 2016 20:56:24 -0700 Subject: [PATCH 05/36] Add glfwGetJoystickHats This merges the public part of the glfwGetJoystickHats work by @IntellectualKitty. The implementation needs replacing due to refactoring in preparation for gamecontrollerdb support. Closes #906. --- include/GLFW/glfw3.h | 71 ++++++++++++++++++++++++++++++++++++++++++++ src/input.c | 16 ++++++++++ tests/events.c | 8 +++-- 3 files changed, 92 insertions(+), 3 deletions(-) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 65e4a67c3..fcfc88f0f 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -297,6 +297,23 @@ extern "C" { #define GLFW_REPEAT 2 /*! @} */ +/*! @defgroup hat_directions Joystick hat directions + * + * See [joystick hat input](@ref joystick_hat) for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_HAT_CENTERED 0 +#define GLFW_HAT_UP 1 +#define GLFW_HAT_RIGHT 2 +#define GLFW_HAT_DOWN 4 +#define GLFW_HAT_LEFT 8 +#define GLFW_HAT_RIGHT_UP (GLFW_HAT_RIGHT | GLFW_HAT_UP) +#define GLFW_HAT_RIGHT_DOWN (GLFW_HAT_RIGHT | GLFW_HAT_DOWN) +#define GLFW_HAT_LEFT_UP (GLFW_HAT_LEFT | GLFW_HAT_UP) +#define GLFW_HAT_LEFT_DOWN (GLFW_HAT_LEFT | GLFW_HAT_DOWN) +/*! @} */ + /*! @defgroup keys Keyboard keys * @brief Keyboard key IDs. * @@ -4065,6 +4082,60 @@ GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count); */ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count); +/*! @brief Returns the state of all hats of the specified joystick. + * + * This function returns the state of all hats of the specified joystick. + * Each element in the array is one of the following: + * + * GLFW_HAT_CENTERED + * GLFW_HAT_UP + * GLFW_HAT_RIGHT + * GLFW_HAT_DOWN + * GLFW_HAT_LEFT + * GLFW_HAT_RIGHT_UP + * GLFW_HAT_RIGHT_DOWN + * GLFW_HAT_LEFT_UP + * GLFW_HAT_LEFT_DOWN + * + * For masking purposes, the hat state may be ANDed with the following primary + * directions: + * + * GLFW_HAT_UP + * GLFW_HAT_RIGHT + * GLFW_HAT_DOWN + * GLFW_HAT_LEFT + * + * Querying a joystick slot with no device present is not an error, but will + * cause this function to return `NULL`. Call @ref glfwJoystickPresent to + * check device presence. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @param[out] count Where to store the number of hat states in the returned + * array. This is set to zero if the joystick is not present or an error + * occurred. + * @return An array of hat states, or `NULL` if the joystick is not present + * or an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @remark @linux Linux does not currently support hats. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected, this function is called again for that joystick or the library + * is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_hat + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count); + /*! @brief Returns the name of the specified joystick. * * This function returns the name, encoded as UTF-8, of the specified joystick. diff --git a/src/input.c b/src/input.c index 923e3fca4..4c174d256 100644 --- a/src/input.c +++ b/src/input.c @@ -667,6 +667,22 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count) return _glfw.joysticks[jid].buttons; } +GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count) +{ + assert(count != NULL); + *count = 0; + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", jid); + return NULL; + } + + return NULL; +} + GLFWAPI const char* glfwGetJoystickName(int jid) { assert(jid >= GLFW_JOYSTICK_1); diff --git a/tests/events.c b/tests/events.c index 73a61ab21..7b42e4fd3 100644 --- a/tests/events.c +++ b/tests/events.c @@ -463,17 +463,19 @@ static void joystick_callback(int jid, int event) { if (event == GLFW_CONNECTED) { - int axisCount, buttonCount; + int axisCount, buttonCount, hatCount; glfwGetJoystickAxes(jid, &axisCount); glfwGetJoystickButtons(jid, &buttonCount); + glfwGetJoystickHats(jid, &hatCount); - printf("%08x at %0.3f: Joystick %i (%s) was connected with %i axes and %i buttons\n", + printf("%08x at %0.3f: Joystick %i (%s) was connected with %i axes, %i buttons, and %i hats\n", counter++, glfwGetTime(), jid, glfwGetJoystickName(jid), axisCount, - buttonCount); + buttonCount, + hatCount); } else { From 798d7c6d68bb17480fcda6074b1d5a946a05ed52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 1 Mar 2017 23:27:20 +0100 Subject: [PATCH 06/36] Implement glfwGetJoystickHats This moves the buttons-as-hats logic to shared code and adds the GLFW_JOYSTICK_HAT_BUTTONS input mode as a way to disable this legacy behavior. Fixes #889. --- README.md | 3 ++ docs/Doxyfile.in | 3 +- docs/input.dox | 69 ++++++++++++++++++++++++++++++++++------- docs/intro.dox | 6 ++++ docs/news.dox | 7 +++++ include/GLFW/glfw3.h | 58 ++++++++++++++++++++++------------- src/cocoa_joystick.m | 32 +++++++++++-------- src/init.c | 4 +++ src/input.c | 44 +++++++++++++++++++++++--- src/internal.h | 12 +++++++- src/linux_joystick.c | 2 +- src/win32_joystick.c | 51 ++++++++++++++++++++----------- tests/joysticks.c | 73 +++++++++++++++++++++++++++++++++++++------- 13 files changed, 282 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index 298a148e9..a979061e7 100644 --- a/README.md +++ b/README.md @@ -127,11 +127,14 @@ information on what to include when reporting a bug. - Added `glfwSetWindowMaximizeCallback` and `GLFWwindowmaximizefun` for receiving window maximization events (#778) - Added `glfwSetWindowAttrib` function for changing window attributes (#537) +- Added `glfwGetJoystickHats` function for querying joystick hats + (#889,#906,#934) - Added `glfwInitHint` function for setting library initialization hints - Added headless [OSMesa](http://mesa3d.org/osmesa.html) backend (#850) - Added definition of `GLAPIENTRY` to public header - Added `GLFW_CENTER_CURSOR` window hint for controlling cursor centering (#749,#842) +- Added `GLFW_JOYSTICK_HAT_BUTTONS` init hint (#889) - Added macOS specific `GLFW_COCOA_RETINA_FRAMEBUFFER` window hint - Added macOS specific `GLFW_COCOA_FRAME_AUTOSAVE` window hint (#195) - Added macOS specific `GLFW_COCOA_GRAPHICS_SWITCHING` window hint (#377,#935) diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index df8404210..16562cc83 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -204,7 +204,8 @@ ALIASES = "thread_safety=@par Thread safety\n" \ "x11=__X11:__" \ "wayland=__Wayland:__" \ "win32=__Windows:__" \ - "macos=__macOS:__" + "macos=__macOS:__" \ + "linux=__Linux:__" # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding diff --git a/docs/input.dox b/docs/input.dox index 8d6c46ad6..b5b853aad 100644 --- a/docs/input.dox +++ b/docs/input.dox @@ -510,21 +510,24 @@ A simple mouse wheel, being vertical, provides offsets along the Y-axis. The joystick functions expose connected joysticks and controllers, with both referred to as joysticks. It supports up to sixteen joysticks, ranging from -`GLFW_JOYSTICK_1`, `GLFW_JOYSTICK_2` up to `GLFW_JOYSTICK_LAST`. You can test -whether a [joystick](@ref joysticks) is present with @ref glfwJoystickPresent. +`GLFW_JOYSTICK_1`, `GLFW_JOYSTICK_2` up to and including `GLFW_JOYSTICK_16` or +`GLFW_JOYSTICK_LAST`. You can test whether a [joystick](@ref joysticks) is +present with @ref glfwJoystickPresent. @code int present = glfwJoystickPresent(GLFW_JOYSTICK_1); @endcode When GLFW is initialized, detected joysticks are added to to the beginning of -the array, starting with `GLFW_JOYSTICK_1`. Once a joystick is detected, it -keeps its assigned index until it is disconnected, so as joysticks are connected -and disconnected, they will become spread out. +the array. Once a joystick is detected, it keeps its assigned ID until it is +disconnected or the library is terminated, so as joysticks are connected and +disconnected, there may appear gaps in the IDs. -Joystick state is updated as needed when a joystick function is called and does -not require a window to be created or @ref glfwPollEvents or @ref glfwWaitEvents -to be called. +Joystick axis, button and hat state is updated when polled and does not require +a window to be created or events to be processed. However, if you want joystick +connection and disconnection events reliably delivered to the +[joystick callback](@ref joystick_event) then you must +[process events](@ref events). To see all the properties of all connected joysticks in real-time, run the `joysticks` test program. @@ -538,7 +541,7 @@ returned array. @code int count; -const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &count); +const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_5, &count); @endcode Each element in the returned array is a value between -1.0 and 1.0. @@ -552,11 +555,55 @@ returned array. @code int count; -const unsigned char* axes = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &count); +const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_3, &count); @endcode Each element in the returned array is either `GLFW_PRESS` or `GLFW_RELEASE`. +For backward compatibility with earlier versions that did not have @ref +glfwGetJoystickHats, the button array by default also includes all hats. See +the reference documentation for @ref glfwGetJoystickButtons for details. + + +@subsection joystick_hat Joystick hat states + +The states of all hats are returned by @ref glfwGetJoystickHats. See the +reference documentation for the lifetime of the returned array. + +@code +int count; +const unsigned char* hats = glfwGetJoystickHats(GLFW_JOYSTICK_7, &count); +@endcode + +Each element in the returned array is one of the following: + +Name | Value +--------------------- | -------------------------------- +`GLFW_HAT_CENTERED` | 0 +`GLFW_HAT_UP` | 1 +`GLFW_HAT_RIGHT` | 2 +`GLFW_HAT_DOWN` | 4 +`GLFW_HAT_LEFT` | 8 +`GLFW_HAT_RIGHT_UP` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_UP` +`GLFW_HAT_RIGHT_DOWN` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_DOWN` +`GLFW_HAT_LEFT_UP` | `GLFW_HAT_LEFT` \| `GLFW_HAT_UP` +`GLFW_HAT_LEFT_DOWN` | `GLFW_HAT_LEFT` \| `GLFW_HAT_DOWN` + +The diagonal directions are bitwise combinations of the primary (up, right, down +and left) directions and you can test for these individually by ANDing it with +the corresponding direction. + +@code +if (hats[2] & GLFW_HAT_RIGHT) +{ + // State of hat 2 could be right-up, right or right-down +} +@endcode + +For backward compatibility with earlier versions that did not have @ref +glfwGetJoystickHats, all hats are by default also included in the button array. +See the reference documentation for @ref glfwGetJoystickButtons for details. + @subsection joystick_name Joystick name @@ -565,7 +612,7 @@ glfwGetJoystickName. See the reference documentation for the lifetime of the returned string. @code -const char* name = glfwGetJoystickName(GLFW_JOYSTICK_1); +const char* name = glfwGetJoystickName(GLFW_JOYSTICK_4); @endcode Joystick names are not guaranteed to be unique. Two joysticks of the same model diff --git a/docs/intro.dox b/docs/intro.dox index 57d0da820..16456e5c8 100644 --- a/docs/intro.dox +++ b/docs/intro.dox @@ -77,6 +77,11 @@ platform but they will only affect their specific platform. Other platforms will simply ignore them. Setting these hints requires no platform specific headers or calls. +@anchor GLFW_JOYSTICK_HAT_BUTTONS +__GLFW_JOYSTICK_HAT_BUTTONS__ specifies whether to also expose joystick hats as +buttons, for compatibility with earlier versions of GLFW that did not have @ref +glfwGetJoystickHats. + @subsubsection init_hints_osx macOS specific hints @@ -95,6 +100,7 @@ initialized. Init hint | Default value | Supported values ------------------------------- | ------------- | ---------------- +@ref GLFW_JOYSTICK_HAT_BUTTONS | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` @ref GLFW_COCOA_CHDIR_RESOURCES | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` @ref GLFW_COCOA_MENUBAR | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` diff --git a/docs/news.dox b/docs/news.dox index 6c0897565..0be64b064 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -25,6 +25,13 @@ GLFW now supports changing the [GLFW_DECORATED](@ref GLFW_DECORATED_attrib), windows with @ref glfwSetWindowAttrib. +@subsection news_33_joyhats Support for joystick hats + +GLFW now supports querying the hats of a joystick with @ref glfwGetJoystickHats +and controlling whether hats are also exposed as buttons with the @ref +GLFW_JOYSTICK_HAT_BUTTONS init hint. + + @subsection news_33_inithint Support for initialization hints GLFW now supports setting library initialization hints with @ref glfwInitHint. diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index fcfc88f0f..8b2efcec2 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -297,7 +297,7 @@ extern "C" { #define GLFW_REPEAT 2 /*! @} */ -/*! @defgroup hat_directions Joystick hat directions +/*! @defgroup hat_state Joystick hat states * * See [joystick hat input](@ref joystick_hat) for how these are used. * @@ -945,6 +945,8 @@ extern "C" { /*! @addtogroup init * @{ */ +#define GLFW_JOYSTICK_HAT_BUTTONS 0x00050001 + #define GLFW_COCOA_CHDIR_RESOURCES 0x00051001 #define GLFW_COCOA_MENUBAR 0x00051002 /*! @} */ @@ -4020,7 +4022,7 @@ GLFWAPI int glfwJoystickPresent(int jid); * This function returns the values of all axes of the specified joystick. * Each element in the array is a value between -1.0 and 1.0. * - * Querying a joystick slot with no device present is not an error, but will + * Querying a joystick ID with no device present is not an error, but will * cause this function to return `NULL`. Call @ref glfwJoystickPresent to * check device presence. * @@ -4053,7 +4055,14 @@ GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count); * This function returns the state of all buttons of the specified joystick. * Each element in the array is either `GLFW_PRESS` or `GLFW_RELEASE`. * - * Querying a joystick slot with no device present is not an error, but will + * For backward compatibility with earlier versions that did not have @ref + * glfwGetJoystickHats, the button array also includes all hats, each + * represented as four buttons. The hats are in the same order as returned by + * __glfwGetJoystickHats__ and are in the order _up_, _right_, _down_ and + * _left_. To disable these extra buttons, set the @ref + * GLFW_JOYSTICK_HAT_BUTTONS init hint before initialization. + * + * Querying a joystick ID with no device present is not an error, but will * cause this function to return `NULL`. Call @ref glfwJoystickPresent to * check device presence. * @@ -4085,27 +4094,32 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count); /*! @brief Returns the state of all hats of the specified joystick. * * This function returns the state of all hats of the specified joystick. - * Each element in the array is one of the following: + * Each element in the array is one of the following values: * - * GLFW_HAT_CENTERED - * GLFW_HAT_UP - * GLFW_HAT_RIGHT - * GLFW_HAT_DOWN - * GLFW_HAT_LEFT - * GLFW_HAT_RIGHT_UP - * GLFW_HAT_RIGHT_DOWN - * GLFW_HAT_LEFT_UP - * GLFW_HAT_LEFT_DOWN + * Name | Value + * --------------------- | -------------------------------- + * `GLFW_HAT_CENTERED` | 0 + * `GLFW_HAT_UP` | 1 + * `GLFW_HAT_RIGHT` | 2 + * `GLFW_HAT_DOWN` | 4 + * `GLFW_HAT_LEFT` | 8 + * `GLFW_HAT_RIGHT_UP` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_UP` + * `GLFW_HAT_RIGHT_DOWN` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_DOWN` + * `GLFW_HAT_LEFT_UP` | `GLFW_HAT_LEFT` \| `GLFW_HAT_UP` + * `GLFW_HAT_LEFT_DOWN` | `GLFW_HAT_LEFT` \| `GLFW_HAT_DOWN` * - * For masking purposes, the hat state may be ANDed with the following primary - * directions: + * The diagonal directions are bitwise combinations of the primary (up, right, + * down and left) directions and you can test for these individually by ANDing + * it with the corresponding direction. * - * GLFW_HAT_UP - * GLFW_HAT_RIGHT - * GLFW_HAT_DOWN - * GLFW_HAT_LEFT + * @code + * if (hats[2] & GLFW_HAT_RIGHT) + * { + * // State of hat 2 could be right-up, right or right-down + * } + * @endcode * - * Querying a joystick slot with no device present is not an error, but will + * Querying a joystick ID with no device present is not an error, but will * cause this function to return `NULL`. Call @ref glfwJoystickPresent to * check device presence. * @@ -4119,7 +4133,7 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * - * @remark @linux Linux does not currently support hats. + * @bug @linux Joystick hats are currently unimplemented. * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified joystick is @@ -4142,7 +4156,7 @@ GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count); * The returned string is allocated and freed by GLFW. You should not free it * yourself. * - * Querying a joystick slot with no device present is not an error, but will + * Querying a joystick ID with no device present is not an error, but will * cause this function to return `NULL`. Call @ref glfwJoystickPresent to * check device presence. * diff --git a/src/cocoa_joystick.m b/src/cocoa_joystick.m index 07aa32b61..e828002b0 100644 --- a/src/cocoa_joystick.m +++ b/src/cocoa_joystick.m @@ -204,7 +204,8 @@ static void matchCallback(void* context, js = _glfwAllocJoystick(name, CFArrayGetCount(axes), - CFArrayGetCount(buttons) + CFArrayGetCount(hats) * 4); + CFArrayGetCount(buttons), + CFArrayGetCount(hats)); js->ns.device = device; js->ns.axes = axes; @@ -358,33 +359,38 @@ int _glfwPlatformPollJoystick(int jid, int mode) } else if (mode == _GLFW_POLL_BUTTONS) { - CFIndex i, bi = 0; + CFIndex i; for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++) { _GLFWjoyelementNS* button = (_GLFWjoyelementNS*) CFArrayGetValueAtIndex(js->ns.buttons, i); const char value = getElementValue(js, button) ? 1 : 0; - _glfwInputJoystickButton(jid, bi++, value); + _glfwInputJoystickButton(jid, i, value); } for (i = 0; i < CFArrayGetCount(js->ns.hats); i++) { + const int states[9] = + { + GLFW_HAT_UP, + GLFW_HAT_RIGHT_UP, + GLFW_HAT_RIGHT, + GLFW_HAT_RIGHT_DOWN, + GLFW_HAT_DOWN, + GLFW_HAT_LEFT_DOWN, + GLFW_HAT_LEFT, + GLFW_HAT_LEFT_UP, + GLFW_HAT_CENTERED + }; + _GLFWjoyelementNS* hat = (_GLFWjoyelementNS*) CFArrayGetValueAtIndex(js->ns.hats, i); - - // Bit fields of button presses for each direction, including nil - const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 }; - - long j, state = getElementValue(js, hat); + long state = getElementValue(js, hat); if (state < 0 || state > 8) state = 8; - for (j = 0; j < 4; j++) - { - const char value = directions[state] & (1 << j) ? 1 : 0; - _glfwInputJoystickButton(jid, bi++, value); - } + _glfwInputJoystickHat(jid, i, states[state]); } } diff --git a/src/init.c b/src/init.c index 478b1211e..b2da9a770 100644 --- a/src/init.c +++ b/src/init.c @@ -46,6 +46,7 @@ _GLFWlibrary _glfw = { GLFW_FALSE }; static GLFWerrorfun _glfwErrorCallback; static _GLFWinitconfig _glfwInitHints = { + GLFW_TRUE, // hat buttons { GLFW_TRUE, // menubar GLFW_TRUE // chdir @@ -188,6 +189,9 @@ GLFWAPI void glfwInitHint(int hint, int value) { switch (hint) { + case GLFW_JOYSTICK_HAT_BUTTONS: + _glfwInitHints.hatButtons = value; + return; case GLFW_COCOA_CHDIR_RESOURCES: _glfwInitHints.ns.chdir = value; return; diff --git a/src/input.c b/src/input.c index 4c174d256..115eefb27 100644 --- a/src/input.c +++ b/src/input.c @@ -140,6 +140,19 @@ void _glfwInputJoystickButton(int jid, int button, char value) _glfw.joysticks[jid].buttons[button] = value; } +void _glfwInputJoystickHat(int jid, int hat, char value) +{ + _GLFWjoystick* js = _glfw.joysticks + jid; + const int base = js->buttonCount + hat * 4; + + js->buttons[base + 0] = (value & 0x01) ? GLFW_PRESS : GLFW_RELEASE; + js->buttons[base + 1] = (value & 0x02) ? GLFW_PRESS : GLFW_RELEASE; + js->buttons[base + 2] = (value & 0x04) ? GLFW_PRESS : GLFW_RELEASE; + js->buttons[base + 3] = (value & 0x08) ? GLFW_PRESS : GLFW_RELEASE; + + js->hats[hat] = value; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// @@ -152,7 +165,10 @@ GLFWbool _glfwIsPrintable(int key) key == GLFW_KEY_KP_EQUAL; } -_GLFWjoystick* _glfwAllocJoystick(const char* name, int axisCount, int buttonCount) +_GLFWjoystick* _glfwAllocJoystick(const char* name, + int axisCount, + int buttonCount, + int hatCount) { int jid; _GLFWjoystick* js; @@ -170,9 +186,11 @@ _GLFWjoystick* _glfwAllocJoystick(const char* name, int axisCount, int buttonCou js->present = GLFW_TRUE; js->name = strdup(name); js->axes = calloc(axisCount, sizeof(float)); - js->buttons = calloc(buttonCount, 1); + js->buttons = calloc(buttonCount + hatCount * 4, 1); + js->hats = calloc(hatCount, 1); js->axisCount = axisCount; js->buttonCount = buttonCount; + js->hatCount = hatCount; return js; } @@ -182,6 +200,7 @@ void _glfwFreeJoystick(_GLFWjoystick* js) free(js->name); free(js->axes); free(js->buttons); + free(js->hats); memset(js, 0, sizeof(_GLFWjoystick)); } @@ -663,13 +682,23 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count) if (!_glfwPlatformPollJoystick(jid, _GLFW_POLL_BUTTONS)) return NULL; - *count = _glfw.joysticks[jid].buttonCount; + if (_glfw.hints.init.hatButtons) + { + *count = _glfw.joysticks[jid].buttonCount + + _glfw.joysticks[jid].hatCount * 4; + } + else + *count = _glfw.joysticks[jid].buttonCount; + return _glfw.joysticks[jid].buttons; } GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count) { + assert(jid >= GLFW_JOYSTICK_1); + assert(jid <= GLFW_JOYSTICK_LAST); assert(count != NULL); + *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); @@ -680,7 +709,14 @@ GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count) return NULL; } - return NULL; + if (!_glfw.joysticks[jid].present) + return NULL; + + if (!_glfwPlatformPollJoystick(jid, _GLFW_POLL_BUTTONS)) + return NULL; + + *count = _glfw.joysticks[jid].hatCount; + return _glfw.joysticks[jid].hats; } GLFWAPI const char* glfwGetJoystickName(int jid) diff --git a/src/internal.h b/src/internal.h index f43638dff..13214b071 100644 --- a/src/internal.h +++ b/src/internal.h @@ -266,6 +266,7 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void); */ struct _GLFWinitconfig { + GLFWbool hatButtons; struct { GLFWbool menubar; GLFWbool chdir; @@ -476,6 +477,8 @@ struct _GLFWjoystick int axisCount; unsigned char* buttons; int buttonCount; + unsigned char* hats; + int hatCount; char* name; // This is defined in the joystick API's joystick.h @@ -823,6 +826,13 @@ void _glfwInputJoystickAxis(int jid, int axis, float value); */ void _glfwInputJoystickButton(int jid, int button, char value); +/*! @brief Notifies shared code of the new value of a joystick hat. + * @param[in] jid The joystick whose hat to update. + * @param[in] button The index of the hat to update. + * @param[in] value The new value of the hat. + */ +void _glfwInputJoystickHat(int jid, int hat, char value); + //======================================================================== // Utility functions @@ -912,7 +922,7 @@ void _glfwFreeMonitor(_GLFWmonitor* monitor); /*! @brief Returns an available joystick object with arrays and name allocated. * @ingroup utility */ -_GLFWjoystick* _glfwAllocJoystick(const char* name, int axisCount, int buttonCount); +_GLFWjoystick* _glfwAllocJoystick(const char* name, int axisCount, int buttonCount, int hatCount); /*! @brief Frees arrays and name and flags the joystick object as unused. * @ingroup utility diff --git a/src/linux_joystick.c b/src/linux_joystick.c index 04cca5f5d..6e4b6a8c9 100644 --- a/src/linux_joystick.c +++ b/src/linux_joystick.c @@ -77,7 +77,7 @@ static GLFWbool openJoystickDevice(const char* path) ioctl(fd, JSIOCGAXES, &axisCount); ioctl(fd, JSIOCGBUTTONS, &buttonCount); - js = _glfwAllocJoystick(name, axisCount, buttonCount); + js = _glfwAllocJoystick(name, axisCount, buttonCount, 0); if (!js) { close(fd); diff --git a/src/win32_joystick.c b/src/win32_joystick.c index 0a0d3ef06..ecbf4b90e 100644 --- a/src/win32_joystick.c +++ b/src/win32_joystick.c @@ -427,7 +427,8 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) js = _glfwAllocJoystick(name, data.axisCount + data.sliderCount, - data.buttonCount + data.povCount * 4); + data.buttonCount, + data.povCount); if (!js) { IDirectInputDevice8_Release(device); @@ -512,7 +513,7 @@ void _glfwDetectJoystickConnectionWin32(void) if (XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS) continue; - js = _glfwAllocJoystick(getDeviceDescription(&xic), 6, 14); + js = _glfwAllocJoystick(getDeviceDescription(&xic), 6, 10, 1); if (!js) continue; @@ -561,7 +562,7 @@ int _glfwPlatformPollJoystick(int jid, int mode) if (js->win32.device) { - int i, j, ai = 0, bi = 0; + int i, ai = 0, bi = 0, pi = 0; HRESULT result; DIJOYSTATE state; @@ -612,19 +613,26 @@ int _glfwPlatformPollJoystick(int jid, int mode) case _GLFW_TYPE_POV: { - const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 }; + const int states[9] = + { + GLFW_HAT_UP, + GLFW_HAT_RIGHT_UP, + GLFW_HAT_RIGHT, + GLFW_HAT_RIGHT_DOWN, + GLFW_HAT_DOWN, + GLFW_HAT_LEFT_DOWN, + GLFW_HAT_LEFT, + GLFW_HAT_LEFT_UP, + GLFW_HAT_CENTERED + }; + // Screams of horror are appropriate at this point int state = LOWORD(*(DWORD*) data) / (45 * DI_DEGREES); if (state < 0 || state > 8) state = 8; - for (j = 0; j < 4; j++) - { - const char value = (directions[state] & (1 << j)) != 0; - _glfwInputJoystickButton(jid, bi, value); - bi++; - } - + _glfwInputJoystickHat(jid, pi, states[state]); + pi++; break; } } @@ -632,7 +640,7 @@ int _glfwPlatformPollJoystick(int jid, int mode) } else { - int i; + int i, dpad = 0; DWORD result; XINPUT_STATE xis; float axes[6] = { 0.f, 0.f, 0.f, 0.f, -1.f, -1.f }; @@ -647,11 +655,7 @@ int _glfwPlatformPollJoystick(int jid, int mode) XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_START, XINPUT_GAMEPAD_LEFT_THUMB, - XINPUT_GAMEPAD_RIGHT_THUMB, - XINPUT_GAMEPAD_DPAD_UP, - XINPUT_GAMEPAD_DPAD_RIGHT, - XINPUT_GAMEPAD_DPAD_DOWN, - XINPUT_GAMEPAD_DPAD_LEFT + XINPUT_GAMEPAD_RIGHT_THUMB }; result = XInputGetState(js->win32.index, &xis); @@ -693,11 +697,22 @@ int _glfwPlatformPollJoystick(int jid, int mode) for (i = 0; i < 6; i++) _glfwInputJoystickAxis(jid, i, axes[i]); - for (i = 0; i < 14; i++) + for (i = 0; i < 10; i++) { const char value = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0; _glfwInputJoystickButton(jid, i, value); } + + if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) + dpad |= GLFW_HAT_UP; + if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) + dpad |= GLFW_HAT_RIGHT; + if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) + dpad |= GLFW_HAT_DOWN; + if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) + dpad |= GLFW_HAT_LEFT; + + _glfwInputJoystickHat(jid, 0, dpad); } return GLFW_TRUE; diff --git a/tests/joysticks.c b/tests/joysticks.c index 02d4f0e3f..e00e9d95d 100644 --- a/tests/joysticks.c +++ b/tests/joysticks.c @@ -98,6 +98,7 @@ int main(void) memset(joysticks, 0, sizeof(joysticks)); glfwSetErrorCallback(error_callback); + glfwInitHint(GLFW_JOYSTICK_HAT_BUTTONS, GLFW_FALSE); if (!glfwInit()) exit(EXIT_FAILURE); @@ -142,6 +143,8 @@ int main(void) { nk_layout_row_dynamic(nk, 30, 1); + nk_label(nk, "Hat buttons disabled", NK_TEXT_LEFT); + if (joystick_count) { for (i = 0; i < joystick_count; i++) @@ -167,29 +170,77 @@ int main(void) NK_WINDOW_MINIMIZABLE | NK_WINDOW_TITLE)) { - int j, axis_count, button_count; + int j, axis_count, button_count, hat_count; const float* axes; const unsigned char* buttons; + const unsigned char* hats; nk_layout_row_dynamic(nk, 30, 1); axes = glfwGetJoystickAxes(joysticks[i], &axis_count); - if (axis_count) - { - for (j = 0; j < axis_count; j++) - nk_slide_float(nk, -1.f, axes[j], 1.f, 0.1f); - } + for (j = 0; j < axis_count; j++) + nk_slide_float(nk, -1.f, axes[j], 1.f, 0.1f); nk_layout_row_dynamic(nk, 30, 8); buttons = glfwGetJoystickButtons(joysticks[i], &button_count); - if (button_count) + for (j = 0; j < button_count; j++) { - for (j = 0; j < button_count; j++) + char name[16]; + snprintf(name, sizeof(name), "%i", j + 1); + nk_select_label(nk, name, NK_TEXT_CENTERED, buttons[j]); + } + + nk_layout_row_dynamic(nk, 30, 8); + + hats = glfwGetJoystickHats(joysticks[i], &hat_count); + for (j = 0; j < hat_count; j++) + { + float radius; + struct nk_rect area; + struct nk_vec2 center; + + if (nk_widget(&area, nk) != NK_WIDGET_VALID) + continue; + + center = nk_vec2(area.x + area.w / 2.f, + area.y + area.h / 2.f); + radius = NK_MIN(area.w, area.h) / 2.f; + + nk_stroke_circle(nk_window_get_canvas(nk), + nk_rect(center.x - radius, + center.y - radius, + radius * 2.f, + radius * 2.f), + 1.f, + nk_rgb(175, 175, 175)); + + if (hats[j]) { - char name[16]; - snprintf(name, sizeof(name), "%i", j + 1); - nk_select_label(nk, name, NK_TEXT_CENTERED, buttons[j]); + const float angles[] = + { + 0.f, 0.f, + NK_PI * 1.5f, NK_PI * 1.75f, + NK_PI, 0.f, + NK_PI * 1.25f, 0.f, + NK_PI * 0.5f, NK_PI * 0.25f, + 0.f, 0.f, + NK_PI * 0.75f, 0.f, + }; + const float cosa = nk_cos(angles[hats[j]]); + const float sina = nk_sin(angles[hats[j]]); + const struct nk_vec2 p0 = nk_vec2(0.f, -radius); + const struct nk_vec2 p1 = nk_vec2( radius / 2.f, -radius / 3.f); + const struct nk_vec2 p2 = nk_vec2(-radius / 2.f, -radius / 3.f); + + nk_fill_triangle(nk_window_get_canvas(nk), + center.x + cosa * p0.x + sina * p0.y, + center.y + cosa * p0.y - sina * p0.x, + center.x + cosa * p1.x + sina * p1.y, + center.y + cosa * p1.y - sina * p1.x, + center.x + cosa * p2.x + sina * p2.y, + center.y + cosa * p2.y - sina * p2.x, + nk_rgb(175, 175, 175)); } } } From 5ae781586f1c88d977067804f28dc67844399b57 Mon Sep 17 00:00:00 2001 From: "A. Tombs" Date: Tue, 7 Mar 2017 18:24:31 +0000 Subject: [PATCH 07/36] X11: Handle Selection* events despite NULL window X11 event processing currently discards events that can not be mapped to a current GLFW window. However, this breaks clipboard functionality by failing to respond to SelectionRequest and SelectionClear events. This commit moves processing of these important clipboard events to before the NULL window test so that they are always considered. Fixes #961. Closes #963. --- src/x11_window.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/x11_window.c b/src/x11_window.c index c65d3d22d..cc8ffd974 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -957,6 +957,17 @@ static void processEvent(XEvent *event) return; } + if (event->type == SelectionClear) + { + handleSelectionClear(event); + return; + } + else if (event->type == SelectionRequest) + { + handleSelectionRequest(event); + return; + } + window = findWindowByHandle(event->xany.window); if (window == NULL) { @@ -1476,18 +1487,6 @@ static void processEvent(XEvent *event) return; } - case SelectionClear: - { - handleSelectionClear(event); - return; - } - - case SelectionRequest: - { - handleSelectionRequest(event); - return; - } - case DestroyNotify: return; } From 43fc399cd1054483f81bd99223111b66e4440b1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 6 Mar 2017 18:23:06 +0100 Subject: [PATCH 08/36] Add hat button toggling to joystick test --- tests/joysticks.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/joysticks.c b/tests/joysticks.c index e00e9d95d..a51062db2 100644 --- a/tests/joysticks.c +++ b/tests/joysticks.c @@ -90,7 +90,7 @@ static const char* joystick_label(int jid) int main(void) { - int jid; + int jid, hat_buttons = GLFW_FALSE; GLFWwindow* window; struct nk_context* nk; struct nk_font_atlas* atlas; @@ -98,7 +98,6 @@ int main(void) memset(joysticks, 0, sizeof(joysticks)); glfwSetErrorCallback(error_callback); - glfwInitHint(GLFW_JOYSTICK_HAT_BUTTONS, GLFW_FALSE); if (!glfwInit()) exit(EXIT_FAILURE); @@ -143,7 +142,7 @@ int main(void) { nk_layout_row_dynamic(nk, 30, 1); - nk_label(nk, "Hat buttons disabled", NK_TEXT_LEFT); + nk_checkbox_label(nk, "Hat buttons", &hat_buttons); if (joystick_count) { @@ -178,12 +177,17 @@ int main(void) nk_layout_row_dynamic(nk, 30, 1); axes = glfwGetJoystickAxes(joysticks[i], &axis_count); + buttons = glfwGetJoystickButtons(joysticks[i], &button_count); + hats = glfwGetJoystickHats(joysticks[i], &hat_count); + + if (!hat_buttons) + button_count -= hat_count * 4; + for (j = 0; j < axis_count; j++) nk_slide_float(nk, -1.f, axes[j], 1.f, 0.1f); nk_layout_row_dynamic(nk, 30, 8); - buttons = glfwGetJoystickButtons(joysticks[i], &button_count); for (j = 0; j < button_count; j++) { char name[16]; @@ -193,7 +197,6 @@ int main(void) nk_layout_row_dynamic(nk, 30, 8); - hats = glfwGetJoystickHats(joysticks[i], &hat_count); for (j = 0; j < hat_count; j++) { float radius; From 6a8ade99c753be7511c866782ef4dde1ba92cedc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 8 Mar 2017 12:38:08 +0100 Subject: [PATCH 09/36] Cocoa: Remove errors emitted on headless systems Related to #958. --- src/cocoa_monitor.m | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/cocoa_monitor.m b/src/cocoa_monitor.m index 3b2eccfb2..41a6cff07 100644 --- a/src/cocoa_monitor.m +++ b/src/cocoa_monitor.m @@ -48,9 +48,8 @@ static char* getDisplayName(CGDirectDisplayID displayID) IOServiceMatching("IODisplayConnect"), &it) != 0) { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Failed to get display service port iterator"); - return 0; + // This may happen if a desktop Mac is running headless + return NULL; } while ((service = IOIteratorNext(it)) != 0) @@ -99,8 +98,6 @@ static char* getDisplayName(CGDirectDisplayID displayID) (const void**) &nameRef)) { // This may happen if a desktop Mac is running headless - _glfwInputError(GLFW_PLATFORM_ERROR, - "Cocoa: Failed to retrieve display name"); CFRelease(info); return NULL; } From e2ce3026a0ae01ecfeefc5af0b2f834d560303d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 14 Mar 2017 14:49:30 +0100 Subject: [PATCH 10/36] Rename license file --- COPYING.txt => LICENSE.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename COPYING.txt => LICENSE.md (100%) diff --git a/COPYING.txt b/LICENSE.md similarity index 100% rename from COPYING.txt rename to LICENSE.md From 0df9cc2fc516220eff62ddd838352f92e9c24aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 14 Mar 2017 18:40:41 +0100 Subject: [PATCH 11/36] Win32: Improve monitor enumeration This changes enumeration to add as a GLFW monitor any active adapter without displays, even if other active adapters do have displays. Related to #441. Fixes #960. --- README.md | 1 + src/win32_monitor.c | 78 ++++++++++++++++----------------------------- 2 files changed, 29 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index a979061e7..ad87fa020 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,7 @@ information on what to include when reporting a bug. - [Win32] Bugfix: Mouse capture logic lost secondary release messages (#954) - [Win32] Bugfix: The 32-bit Vulkan loader library static was not searched for - [Win32] Bugfix: Vulkan libraries have a new path as of SDK 1.0.42.0 (#956) +- [Win32] Bugfix: Monitors with no display devices were not enumerated (#960) - [X11] Replaced `_GLFW_HAS_XF86VM` compile-time option with dynamic loading - [X11] Bugfix: `glfwGetVideoMode` would segfault on Cygwin/X - [X11] Bugfix: Dynamic X11 library loading did not use full sonames (#941) diff --git a/src/win32_monitor.c b/src/win32_monitor.c index cc5e24f8f..d91a02f83 100644 --- a/src/win32_monitor.c +++ b/src/win32_monitor.c @@ -94,7 +94,6 @@ void _glfwPollMonitorsWin32(void) _GLFWmonitor** disconnected = NULL; DWORD adapterIndex, displayIndex; DISPLAY_DEVICEW adapter, display; - GLFWbool hasDisplays = GLFW_FALSE; disconnectedCount = _glfw.monitorCount; if (disconnectedCount) @@ -105,30 +104,6 @@ void _glfwPollMonitorsWin32(void) _glfw.monitorCount * sizeof(_GLFWmonitor*)); } - // HACK: Check if any active adapters have connected displays - // If not, this is a headless system or a VMware guest - - for (adapterIndex = 0; ; adapterIndex++) - { - ZeroMemory(&adapter, sizeof(DISPLAY_DEVICEW)); - adapter.cb = sizeof(DISPLAY_DEVICEW); - - if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0)) - break; - - if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE)) - continue; - - ZeroMemory(&display, sizeof(DISPLAY_DEVICEW)); - display.cb = sizeof(DISPLAY_DEVICEW); - - if (EnumDisplayDevicesW(adapter.DeviceName, 0, &display, 0)) - { - hasDisplays = GLFW_TRUE; - break; - } - } - for (adapterIndex = 0; ; adapterIndex++) { int type = _GLFW_INSERT_LAST; @@ -145,37 +120,40 @@ void _glfwPollMonitorsWin32(void) if (adapter.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) type = _GLFW_INSERT_FIRST; - if (hasDisplays) + for (displayIndex = 0; ; displayIndex++) { - for (displayIndex = 0; ; displayIndex++) + ZeroMemory(&display, sizeof(DISPLAY_DEVICEW)); + display.cb = sizeof(DISPLAY_DEVICEW); + + if (!EnumDisplayDevicesW(adapter.DeviceName, displayIndex, &display, 0)) + break; + + if (!(display.StateFlags & DISPLAY_DEVICE_ACTIVE)) + continue; + + for (i = 0; i < disconnectedCount; i++) { - ZeroMemory(&display, sizeof(DISPLAY_DEVICEW)); - display.cb = sizeof(DISPLAY_DEVICEW); - - if (!EnumDisplayDevicesW(adapter.DeviceName, displayIndex, &display, 0)) - break; - - for (i = 0; i < disconnectedCount; i++) + if (disconnected[i] && + wcscmp(disconnected[i]->win32.displayName, + display.DeviceName) == 0) { - if (disconnected[i] && - wcscmp(disconnected[i]->win32.displayName, - display.DeviceName) == 0) - { - disconnected[i] = NULL; - break; - } + disconnected[i] = NULL; + break; } - - if (i < disconnectedCount) - continue; - - _glfwInputMonitor(createMonitor(&adapter, &display), - GLFW_CONNECTED, type); - - type = _GLFW_INSERT_LAST; } + + if (i < disconnectedCount) + continue; + + _glfwInputMonitor(createMonitor(&adapter, &display), + GLFW_CONNECTED, type); + + type = _GLFW_INSERT_LAST; } - else + + // HACK: If an active adapter does not have any display devices + // (as sometimes happens), add it directly as a monitor + if (displayIndex == 0) { for (i = 0; i < disconnectedCount; i++) { From 97dbd8b63bbd15bb781e54a670b183dbd59f1bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 14 Mar 2017 19:21:52 +0100 Subject: [PATCH 12/36] Win32: Fix monitor event emission Fixes #784. --- README.md | 1 + src/win32_init.c | 4 ++-- src/win32_window.c | 11 +++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ad87fa020..eadb27369 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,7 @@ information on what to include when reporting a bug. - [Win32] Bugfix: The 32-bit Vulkan loader library static was not searched for - [Win32] Bugfix: Vulkan libraries have a new path as of SDK 1.0.42.0 (#956) - [Win32] Bugfix: Monitors with no display devices were not enumerated (#960) +- [Win32] Bugfix: Monitor events were not emitted (#784) - [X11] Replaced `_GLFW_HAS_XF86VM` compile-time option with dynamic loading - [X11] Bugfix: `glfwGetVideoMode` would segfault on Cygwin/X - [X11] Bugfix: Dynamic X11 library loading did not use full sonames (#941) diff --git a/src/win32_init.c b/src/win32_init.c index b092d1c7b..785d75d34 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -307,10 +307,10 @@ static HWND createHelperWindow(void) MSG msg; HWND window = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, _GLFW_WNDCLASSNAME, - L"GLFW helper window", + L"GLFW message window", WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, 0, 1, 1, - HWND_MESSAGE, NULL, + NULL, NULL, GetModuleHandleW(NULL), NULL); if (!window) diff --git a/src/win32_window.c b/src/win32_window.c index 1e0a587e9..7f8a63b20 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -467,14 +467,13 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, switch (uMsg) { + case WM_DISPLAYCHANGE: + _glfwPollMonitorsWin32(); + break; + case WM_DEVICECHANGE: { - if (wParam == DBT_DEVNODES_CHANGED) - { - _glfwPollMonitorsWin32(); - return TRUE; - } - else if (wParam == DBT_DEVICEARRIVAL) + if (wParam == DBT_DEVICEARRIVAL) { DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; if (dbh) From 21355e31db1b0f44f676979445efae6197a8bc03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 14 Mar 2017 20:39:18 +0100 Subject: [PATCH 13/36] Cleanup --- src/win32_window.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/win32_window.c b/src/win32_window.c index 7f8a63b20..363ef0f32 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -476,20 +476,14 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, if (wParam == DBT_DEVICEARRIVAL) { DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; - if (dbh) - { - if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) - _glfwDetectJoystickConnectionWin32(); - } + if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + _glfwDetectJoystickConnectionWin32(); } else if (wParam == DBT_DEVICEREMOVECOMPLETE) { DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; - if (dbh) - { - if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) - _glfwDetectJoystickDisconnectionWin32(); - } + if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + _glfwDetectJoystickDisconnectionWin32(); } break; From 7c88c927782ad052300d18250dff94dc4a249083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 14 Mar 2017 21:21:29 +0100 Subject: [PATCH 14/36] Cocoa: Fix window rect on leaving video mode Fixes #748. --- README.md | 2 ++ src/cocoa_window.m | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/README.md b/README.md index eadb27369..5f78926a6 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,8 @@ information on what to include when reporting a bug. function on macOS 10.12+ - [Cocoa] Bugfix: Running in AppSandbox would emit warnings (#816,#882) - [Cocoa] Bugfix: Windows created after the first were not cascaded (#195) +- [Cocoa] Bugfix: Leaving video mode with `glfwSetWindowMonitor` would set + incorrect position and size (#748) - [X11] Moved to XI2 `XI_RawMotion` for disable cursor mode motion input (#125) - [EGL] Added support for `EGL_KHR_get_all_proc_addresses` (#871) - [EGL] Bugfix: The test for `EGL_RGB_BUFFER` was invalid diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 657852ab8..543339525 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1310,6 +1310,10 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _glfwInputWindowMonitorChange(window, monitor); + // HACK: Allow the state cached in Cocoa to catch up to reality + // TODO: Solve this in a less terrible way + _glfwPlatformPollEvents(); + const NSUInteger styleMask = getStyleMask(window); [window->ns.object setStyleMask:styleMask]; [window->ns.object makeFirstResponder:window->ns.view]; From 3d8aa53573d488e45fa4a9f8ac40a6893386db63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 15 Mar 2017 01:19:54 +0100 Subject: [PATCH 15/36] Cocoa: Fix full screen window not being restorable Once a full screen window was iconified, it would appear to restore but then disappear. Fixes #848. --- README.md | 1 + src/cocoa_window.m | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f78926a6..60e8dce5e 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,7 @@ information on what to include when reporting a bug. - [Cocoa] Bugfix: Windows created after the first were not cascaded (#195) - [Cocoa] Bugfix: Leaving video mode with `glfwSetWindowMonitor` would set incorrect position and size (#748) +- [Cocoa] Bugfix: Iconified full screen windows could not be restored (#848) - [X11] Moved to XI2 `XI_RawMotion` for disable cursor mode motion input (#125) - [EGL] Added support for `EGL_KHR_get_all_proc_addresses` (#871) - [EGL] Bugfix: The test for `EGL_RGB_BUFFER` was invalid diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 543339525..92635035d 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1025,7 +1025,12 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, _glfw.ns.cascadePoint = [window->ns.object cascadeTopLeftFromPoint:_glfw.ns.cascadePoint]; if (wndconfig->resizable) - [window->ns.object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; + { + const NSWindowCollectionBehavior behavior = + NSWindowCollectionBehaviorFullScreenPrimary | + NSWindowCollectionBehaviorManaged; + [window->ns.object setCollectionBehavior:behavior]; + } if (wndconfig->floating) [window->ns.object setLevel:NSFloatingWindowLevel]; From 7c070f55be333769c30b8dd3d759dcbc3b8e0830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 15 Mar 2017 14:40:21 +0100 Subject: [PATCH 16/36] EGL: Add support for Cygwin --- src/egl_context.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/egl_context.c b/src/egl_context.c index 368cba969..80e9bca0d 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -291,6 +291,8 @@ GLFWbool _glfwInitEGL(void) "EGL.dll", #elif defined(_GLFW_COCOA) "libEGL.dylib", +#elif defined(__CYGWIN__) + "libEGL-1.so", #else "libEGL.so.1", #endif @@ -609,6 +611,8 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, "libGLESv2.dll", #elif defined(_GLFW_COCOA) "libGLESv2.dylib", +#elif defined(__CYGWIN__) + "libGLESv2-2.so", #else "libGLESv2.so.2", #endif From 27a8b3c17baf3952f74948b7842c2350144c8ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 15 Mar 2017 17:37:33 +0100 Subject: [PATCH 17/36] EGL: Add support for EGL_KHR_context_flush_control --- README.md | 1 + src/egl_context.c | 28 +++++++++++++++++++--------- src/egl_context.h | 4 ++++ 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 60e8dce5e..939aba181 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,7 @@ information on what to include when reporting a bug. - [Cocoa] Bugfix: Iconified full screen windows could not be restored (#848) - [X11] Moved to XI2 `XI_RawMotion` for disable cursor mode motion input (#125) - [EGL] Added support for `EGL_KHR_get_all_proc_addresses` (#871) +- [EGL] Added support for `EGL_KHR_context_flush_control` - [EGL] Bugfix: The test for `EGL_RGB_BUFFER` was invalid diff --git a/src/egl_context.c b/src/egl_context.c index 80e9bca0d..e73fc0b46 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -403,6 +403,8 @@ GLFWbool _glfwInitEGL(void) extensionSupportedEGL("EGL_KHR_gl_colorspace"); _glfw.egl.KHR_get_all_proc_addresses = extensionSupportedEGL("EGL_KHR_get_all_proc_addresses"); + _glfw.egl.KHR_context_flush_control = + extensionSupportedEGL("EGL_KHR_context_flush_control"); return GLFW_TRUE; } @@ -440,6 +442,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, EGLint attribs[40]; EGLConfig config; EGLContext share = NULL; + int index = 0; if (!_glfw.egl.display) { @@ -480,7 +483,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, if (_glfw.egl.KHR_create_context) { - int index = 0, mask = 0, flags = 0; + int mask = 0, flags = 0; if (ctxconfig->client == GLFW_OPENGL_API) { @@ -529,21 +532,28 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, if (flags) setEGLattrib(EGL_CONTEXT_FLAGS_KHR, flags); - - setEGLattrib(EGL_NONE, EGL_NONE); } else { - int index = 0; - if (ctxconfig->client == GLFW_OPENGL_ES_API) setEGLattrib(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major); - - setEGLattrib(EGL_NONE, EGL_NONE); } - // Context release behaviors (GL_KHR_context_flush_control) are not yet - // supported on EGL but are not a hard constraint, so ignore and continue + if (_glfw.egl.KHR_context_flush_control) + { + if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) + { + setEGLattrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, + EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR); + } + else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) + { + setEGLattrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, + EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR); + } + } + + setEGLattrib(EGL_NONE, EGL_NONE); window->context.egl.handle = eglCreateContext(_glfw.egl.display, config, share, attribs); diff --git a/src/egl_context.h b/src/egl_context.h index e1040acd5..f8393a49f 100644 --- a/src/egl_context.h +++ b/src/egl_context.h @@ -110,6 +110,9 @@ typedef MirEGLNativeWindowType EGLNativeWindowType; #define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31b3 #define EGL_GL_COLORSPACE_KHR 0x309d #define EGL_GL_COLORSPACE_SRGB_KHR 0x3089 +#define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097 +#define EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR 0 +#define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098 typedef int EGLint; typedef unsigned int EGLBoolean; @@ -181,6 +184,7 @@ typedef struct _GLFWlibraryEGL GLFWbool KHR_create_context_no_error; GLFWbool KHR_gl_colorspace; GLFWbool KHR_get_all_proc_addresses; + GLFWbool KHR_context_flush_control; void* handle; From 7410346c5cb1b5774a43b286d260973f04aa584f Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Thu, 16 Mar 2017 15:23:59 +0100 Subject: [PATCH 18/36] Cocoa: Allow undecorated windows to become main --- src/cocoa_window.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 92635035d..d2aab85f9 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -777,6 +777,11 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; return YES; } +- (BOOL)canBecomeMainWindow +{ + return YES; +} + @end From bff31f006eef29f9d2d68df19bde02677f1552b4 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Thu, 16 Mar 2017 16:22:00 +0100 Subject: [PATCH 19/36] Formatting --- include/GLFW/glfw3native.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/GLFW/glfw3native.h b/include/GLFW/glfw3native.h index 477d0ed12..ba59913ec 100644 --- a/include/GLFW/glfw3native.h +++ b/include/GLFW/glfw3native.h @@ -464,7 +464,7 @@ GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window); * @param[out] buffer Where to store the address of the color buffer, or * `NULL`. * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an - * [error](@ref error_handling) occurred. + * [error](@ref error_handling) occurred. * * @thread_safety This function may be called from any thread. Access is not * synchronized. @@ -485,7 +485,7 @@ GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height * @param[out] buffer Where to store the address of the depth buffer, or * `NULL`. * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an - * [error](@ref error_handling) occurred. + * [error](@ref error_handling) occurred. * * @thread_safety This function may be called from any thread. Access is not * synchronized. From 6a65341e14bc7745c52fe86fe53be08dbd682dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 16 Mar 2017 23:07:59 +0100 Subject: [PATCH 20/36] X11: Fix multiple issues in XDND support The code blindly expected UTF8_STRING for files. It did not downgrade based on source protocol version. It did not handle hostnames in text/uri-list data. It did not specify the source time stamp when converting the selection. It did not search the XdndTypeList when necessary. It did not ignore sources that specified invalid versions. While better, this is still not fully conformant. Hostnames are not validated and it does not guard against source crashes. Fixes #968. --- README.md | 1 + src/x11_init.c | 3 +- src/x11_platform.h | 5 +- src/x11_window.c | 159 ++++++++++++++++++++++++++++++++++----------- 4 files changed, 129 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 939aba181..76b384a37 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,7 @@ information on what to include when reporting a bug. - [X11] Bugfix: `glfwGetVideoMode` would segfault on Cygwin/X - [X11] Bugfix: Dynamic X11 library loading did not use full sonames (#941) - [X11] Bugfix: Window creation on 64-bit would read past top of stack (#951) +- [X11] Bugfix: XDND support had multiple non-conformance issues (#968) - [Linux] Bugfix: Event processing did not detect joystick disconnection (#932) - [Cocoa] Added support for Vulkan window surface creation via [MoltenVK](https://moltengl.com/moltenvk/) (#870) diff --git a/src/x11_init.c b/src/x11_init.c index 22600c965..734c89cc4 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -629,9 +629,10 @@ static GLFWbool initExtensions(void) _glfw.x11.XdndStatus = XInternAtom(_glfw.x11.display, "XdndStatus", False); _glfw.x11.XdndActionCopy = XInternAtom(_glfw.x11.display, "XdndActionCopy", False); _glfw.x11.XdndDrop = XInternAtom(_glfw.x11.display, "XdndDrop", False); - _glfw.x11.XdndLeave = XInternAtom(_glfw.x11.display, "XdndLeave", False); _glfw.x11.XdndFinished = XInternAtom(_glfw.x11.display, "XdndFinished", False); _glfw.x11.XdndSelection = XInternAtom(_glfw.x11.display, "XdndSelection", False); + _glfw.x11.XdndTypeList = XInternAtom(_glfw.x11.display, "XdndTypeList", False); + _glfw.x11.text_uri_list = XInternAtom(_glfw.x11.display, "text/uri-list", False); // ICCCM, EWMH and Motif window property atoms // These can be set safely even without WM support diff --git a/src/x11_platform.h b/src/x11_platform.h index b2e4f5de0..4ca5a1dae 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -208,9 +208,10 @@ typedef struct _GLFWlibraryX11 Atom XdndStatus; Atom XdndActionCopy; Atom XdndDrop; - Atom XdndLeave; Atom XdndFinished; Atom XdndSelection; + Atom XdndTypeList; + Atom text_uri_list; // Selection (clipboard) atoms Atom TARGETS; @@ -253,7 +254,9 @@ typedef struct _GLFWlibraryX11 } saver; struct { + int version; Window source; + Atom format; } xdnd; struct { diff --git a/src/x11_window.c b/src/x11_window.c index cc8ffd974..327bd750a 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -48,6 +48,8 @@ #define Button6 6 #define Button7 7 +#define _GLFW_XDND_VERSION 5 + // Wait for data to arrive using select // This avoids blocking other threads via the per-display Xlib lock that also @@ -415,7 +417,12 @@ static char** parseUriList(char* text, int* count) continue; if (strncmp(line, prefix, strlen(prefix)) == 0) + { line += strlen(prefix); + // TODO: Validate hostname + while (*line != '/') + line++; + } (*count)++; @@ -619,10 +626,9 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, XFree(hint); } - if (_glfw.x11.XdndAware) + // Announce support for Xdnd (drag and drop) { - // Announce support for Xdnd (drag and drop) - const Atom version = 5; + const Atom version = _GLFW_XDND_VERSION; XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*) &version, 1); @@ -1307,44 +1313,121 @@ static void processEvent(XEvent *event) else if (event->xclient.message_type == _glfw.x11.XdndEnter) { // A drag operation has entered the window - // TODO: Check if UTF-8 string is supported by the source + unsigned long i, count; + Atom* formats = NULL; + const GLFWbool list = event->xclient.data.l[1] & 1; + + _glfw.x11.xdnd.source = event->xclient.data.l[0]; + _glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24; + _glfw.x11.xdnd.format = None; + + if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) + return; + + if (list) + { + count = _glfwGetWindowPropertyX11(_glfw.x11.xdnd.source, + _glfw.x11.XdndTypeList, + XA_ATOM, + (unsigned char**) &formats); + } + else + { + count = 3; + formats = (Atom*) event->xclient.data.l + 2; + } + + for (i = 0; i < count; i++) + { + if (formats[i] == _glfw.x11.text_uri_list) + { + _glfw.x11.xdnd.format = _glfw.x11.text_uri_list; + break; + } + } + + if (list && formats) + XFree(formats); } else if (event->xclient.message_type == _glfw.x11.XdndDrop) { - // The drag operation has finished dropping on - // the window, ask to convert it to a UTF-8 string - _glfw.x11.xdnd.source = event->xclient.data.l[0]; - XConvertSelection(_glfw.x11.display, - _glfw.x11.XdndSelection, - _glfw.x11.UTF8_STRING, - _glfw.x11.XdndSelection, - window->x11.handle, CurrentTime); + // The drag operation has finished by dropping on the window + Time time = CurrentTime; + + if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) + return; + + if (_glfw.x11.xdnd.format) + { + if (_glfw.x11.xdnd.version >= 1) + time = event->xclient.data.l[2]; + + // Request the chosen format from the source window + XConvertSelection(_glfw.x11.display, + _glfw.x11.XdndSelection, + _glfw.x11.xdnd.format, + _glfw.x11.XdndSelection, + window->x11.handle, + time); + } + else if (_glfw.x11.xdnd.version >= 2) + { + XEvent reply; + memset(&reply, 0, sizeof(reply)); + + reply.type = ClientMessage; + reply.xclient.window = _glfw.x11.xdnd.source; + reply.xclient.message_type = _glfw.x11.XdndFinished; + reply.xclient.format = 32; + reply.xclient.data.l[0] = window->x11.handle; + reply.xclient.data.l[1] = 0; // The drag was rejected + reply.xclient.data.l[2] = None; + + XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, + False, NoEventMask, &reply); + XFlush(_glfw.x11.display); + } } else if (event->xclient.message_type == _glfw.x11.XdndPosition) { // The drag operation has moved over the window - const int absX = (event->xclient.data.l[2] >> 16) & 0xFFFF; - const int absY = (event->xclient.data.l[2]) & 0xFFFF; - int x, y; + const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff; + const int yabs = (event->xclient.data.l[2]) & 0xffff; + Window dummy; + int xpos, ypos; - _glfwPlatformGetWindowPos(window, &x, &y); - _glfwInputCursorPos(window, absX - x, absY - y); + if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) + return; + + XTranslateCoordinates(_glfw.x11.display, + window->x11.handle, + _glfw.x11.root, + xabs, yabs, + &xpos, &ypos, + &dummy); + + _glfwInputCursorPos(window, xpos, ypos); - // Reply that we are ready to copy the dragged data XEvent reply; memset(&reply, 0, sizeof(reply)); reply.type = ClientMessage; - reply.xclient.window = event->xclient.data.l[0]; + reply.xclient.window = _glfw.x11.xdnd.source; reply.xclient.message_type = _glfw.x11.XdndStatus; reply.xclient.format = 32; reply.xclient.data.l[0] = window->x11.handle; - reply.xclient.data.l[1] = 1; // Always accept the dnd with no rectangle reply.xclient.data.l[2] = 0; // Specify an empty rectangle reply.xclient.data.l[3] = 0; - reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy; - XSendEvent(_glfw.x11.display, event->xclient.data.l[0], + if (_glfw.x11.xdnd.format) + { + // Reply that we are ready to copy the dragged data + reply.xclient.data.l[1] = 1; // Accept with no rectangle + if (_glfw.x11.xdnd.version >= 2) + reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy; + } + + XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, False, NoEventMask, &reply); XFlush(_glfw.x11.display); } @@ -1354,11 +1437,11 @@ static void processEvent(XEvent *event) case SelectionNotify: { - if (event->xselection.property) + if (event->xselection.property == _glfw.x11.XdndSelection) { // The converted data from the drag operation has arrived char* data; - const int result = + const unsigned long result = _glfwGetWindowPropertyX11(event->xselection.requestor, event->xselection.property, event->xselection.target, @@ -1379,21 +1462,23 @@ static void processEvent(XEvent *event) if (data) XFree(data); - XEvent reply; - memset(&reply, 0, sizeof(reply)); + if (_glfw.x11.xdnd.version >= 2) + { + XEvent reply; + memset(&reply, 0, sizeof(reply)); - reply.type = ClientMessage; - reply.xclient.window = _glfw.x11.xdnd.source; - reply.xclient.message_type = _glfw.x11.XdndFinished; - reply.xclient.format = 32; - reply.xclient.data.l[0] = window->x11.handle; - reply.xclient.data.l[1] = result; - reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy; + reply.type = ClientMessage; + reply.xclient.window = _glfw.x11.xdnd.source; + reply.xclient.message_type = _glfw.x11.XdndFinished; + reply.xclient.format = 32; + reply.xclient.data.l[0] = window->x11.handle; + reply.xclient.data.l[1] = result; + reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy; - // Reply that all is well - XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, - False, NoEventMask, &reply); - XFlush(_glfw.x11.display); + XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, + False, NoEventMask, &reply); + XFlush(_glfw.x11.display); + } } return; From 2376d3b16abfd1f35c91bfa7115240b459745c82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 17 Mar 2017 01:10:20 +0100 Subject: [PATCH 21/36] Formatting --- LICENSE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.md b/LICENSE.md index acdac20b9..e4c6682eb 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright (c) 2002-2006 Marcus Geelnard +Copyright (c) 2002-2006 Marcus Geelnard Copyright (c) 2006-2016 Camilla Löwy This software is provided 'as-is', without any express or implied From 62414bff6e2fc76c775cac8086e9330f8e6f22d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Fri, 17 Mar 2017 14:09:07 +0100 Subject: [PATCH 22/36] X11: Fix inverted coordinate transform --- src/x11_window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/x11_window.c b/src/x11_window.c index 327bd750a..907fa7946 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -1400,8 +1400,8 @@ static void processEvent(XEvent *event) return; XTranslateCoordinates(_glfw.x11.display, - window->x11.handle, _glfw.x11.root, + window->x11.handle, xabs, yabs, &xpos, &ypos, &dummy); From aaf2800c9cf741faeade57932be3143bf9c5b755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Wed, 8 Mar 2017 13:58:09 +0100 Subject: [PATCH 23/36] Add internal TLS support Related to #970. --- src/cocoa_init.m | 4 --- src/context.c | 12 ++++----- src/egl_context.c | 6 ++--- src/glx_context.c | 4 +-- src/init.c | 5 ++++ src/internal.h | 21 ++++++++++++---- src/mir_init.c | 4 --- src/nsgl_context.m | 4 +-- src/null_init.c | 4 --- src/osmesa_context.c | 2 +- src/posix_tls.c | 58 +++++++++++++++++++++++-------------------- src/posix_tls.h | 9 +++---- src/wgl_context.c | 8 +++--- src/win32_init.c | 4 --- src/win32_platform.h | 9 +++---- src/win32_tls.c | 59 +++++++++++++++++++++++--------------------- src/window.c | 4 +-- src/wl_init.c | 4 --- src/x11_init.c | 4 --- 19 files changed, 109 insertions(+), 116 deletions(-) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 0913c476a..ba5ddb629 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -311,9 +311,6 @@ int _glfwPlatformInit(void) if (!initializeTIS()) return GLFW_FALSE; - if (!_glfwInitThreadLocalStoragePOSIX()) - return GLFW_FALSE; - _glfwInitTimerNS(); _glfwInitJoysticksNS(); @@ -362,7 +359,6 @@ void _glfwPlatformTerminate(void) _glfwTerminateNSGL(); _glfwTerminateJoysticksNS(); - _glfwTerminateThreadLocalStoragePOSIX(); [_glfw.ns.autoreleasePool release]; _glfw.ns.autoreleasePool = nil; diff --git a/src/context.c b/src/context.c index ed921a4b5..498e0e16a 100644 --- a/src/context.c +++ b/src/context.c @@ -331,7 +331,7 @@ GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig) NULL }; - window = _glfwPlatformGetCurrentContext(); + window = _glfwPlatformGetTls(&_glfw.context); window->context.source = ctxconfig->source; window->context.client = GLFW_OPENGL_API; @@ -578,7 +578,7 @@ GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFWwindow* previous = _glfwPlatformGetCurrentContext(); + _GLFWwindow* previous = _glfwPlatformGetTls(&_glfw.context); _GLFW_REQUIRE_INIT(); @@ -601,7 +601,7 @@ GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle) GLFWAPI GLFWwindow* glfwGetCurrentContext(void) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return (GLFWwindow*) _glfwPlatformGetCurrentContext(); + return _glfwPlatformGetTls(&_glfw.context); } GLFWAPI void glfwSwapBuffers(GLFWwindow* handle) @@ -626,7 +626,7 @@ GLFWAPI void glfwSwapInterval(int interval) _GLFW_REQUIRE_INIT(); - window = _glfwPlatformGetCurrentContext(); + window = _glfwPlatformGetTls(&_glfw.context); if (!window) { _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL); @@ -643,7 +643,7 @@ GLFWAPI int glfwExtensionSupported(const char* extension) _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); - window = _glfwPlatformGetCurrentContext(); + window = _glfwPlatformGetTls(&_glfw.context); if (!window) { _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL); @@ -708,7 +708,7 @@ GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname) _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - window = _glfwPlatformGetCurrentContext(); + window = _glfwPlatformGetTls(&_glfw.context); if (!window) { _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL); diff --git a/src/egl_context.c b/src/egl_context.c index e73fc0b46..328a04578 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -199,12 +199,12 @@ static void makeContextCurrentEGL(_GLFWwindow* window) } } - _glfwPlatformSetCurrentContext(window); + _glfwPlatformSetTls(&_glfw.context, window); } static void swapBuffersEGL(_GLFWwindow* window) { - if (window != _glfwPlatformGetCurrentContext()) + if (window != _glfwPlatformGetTls(&_glfw.context)) { _glfwInputError(GLFW_PLATFORM_ERROR, "EGL: The context must be current on the calling thread when swapping buffers"); @@ -233,7 +233,7 @@ static int extensionSupportedEGL(const char* extension) static GLFWglproc getProcAddressEGL(const char* procname) { - _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.context); if (window->context.egl.client) { diff --git a/src/glx_context.c b/src/glx_context.c index 2faf599ca..9c2ccb12c 100644 --- a/src/glx_context.c +++ b/src/glx_context.c @@ -165,7 +165,7 @@ static void makeContextCurrentGLX(_GLFWwindow* window) } } - _glfwPlatformSetCurrentContext(window); + _glfwPlatformSetTls(&_glfw.context, window); } static void swapBuffersGLX(_GLFWwindow* window) @@ -175,7 +175,7 @@ static void swapBuffersGLX(_GLFWwindow* window) static void swapIntervalGLX(int interval) { - _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.context); if (_glfw.glx.EXT_swap_control) { diff --git a/src/init.c b/src/init.c index b2da9a770..92450b59a 100644 --- a/src/init.c +++ b/src/init.c @@ -113,6 +113,8 @@ static void terminate(void) _glfwTerminateVulkan(); _glfwPlatformTerminate(); + _glfwPlatformDestroyTls(&_glfw.context); + memset(&_glfw, 0, sizeof(_glfw)); } @@ -162,6 +164,9 @@ GLFWAPI int glfwInit(void) memset(&_glfw, 0, sizeof(_glfw)); _glfw.hints.init = _glfwInitHints; + if (!_glfwPlatformCreateTls(&_glfw.context)) + return GLFW_FALSE; + if (!_glfwPlatformInit()) { terminate(); diff --git a/src/internal.h b/src/internal.h index 13214b071..d3a4f6878 100644 --- a/src/internal.h +++ b/src/internal.h @@ -69,6 +69,7 @@ typedef struct _GLFWlibrary _GLFWlibrary; typedef struct _GLFWmonitor _GLFWmonitor; typedef struct _GLFWcursor _GLFWcursor; typedef struct _GLFWjoystick _GLFWjoystick; +typedef struct _GLFWtls _GLFWtls; typedef void (* _GLFWmakecontextcurrentfun)(_GLFWwindow*); typedef void (* _GLFWswapbuffersfun)(_GLFWwindow*); @@ -485,6 +486,14 @@ struct _GLFWjoystick _GLFW_PLATFORM_JOYSTICK_STATE; }; +/*! @brief Thread local storage structure. + */ +struct _GLFWtls +{ + // This is defined in the platform's tls.h + _GLFW_PLATFORM_TLS_STATE; +}; + /*! @brief Library global data. */ struct _GLFWlibrary @@ -510,6 +519,8 @@ struct _GLFWlibrary uint64_t timerOffset; + _GLFWtls context; + struct { GLFWbool available; void* handle; @@ -546,8 +557,6 @@ struct _GLFWlibrary _GLFW_PLATFORM_LIBRARY_TIME_STATE; // This is defined in the platform's joystick.h _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE; - // This is defined in the platform's tls.h - _GLFW_PLATFORM_LIBRARY_TLS_STATE; // This is defined in egl_context.h _GLFW_EGL_LIBRARY_CONTEXT_STATE; // This is defined in osmesa_context.h @@ -634,13 +643,15 @@ void _glfwPlatformWaitEvents(void); void _glfwPlatformWaitEventsTimeout(double timeout); void _glfwPlatformPostEmptyEvent(void); -void _glfwPlatformSetCurrentContext(_GLFWwindow* context); -_GLFWwindow* _glfwPlatformGetCurrentContext(void); - void _glfwPlatformGetRequiredInstanceExtensions(char** extensions); int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); +GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls); +void _glfwPlatformDestroyTls(_GLFWtls* tls); +void* _glfwPlatformGetTls(_GLFWtls* tls); +void _glfwPlatformSetTls(_GLFWtls* tls, void* value); + /*! @} */ diff --git a/src/mir_init.c b/src/mir_init.c index 5df5d981e..3a96c78aa 100644 --- a/src/mir_init.c +++ b/src/mir_init.c @@ -190,9 +190,6 @@ int _glfwPlatformInit(void) createKeyTables(); - if (!_glfwInitThreadLocalStoragePOSIX()) - return GLFW_FALSE; - if (!_glfwInitJoysticksLinux()) return GLFW_FALSE; @@ -222,7 +219,6 @@ void _glfwPlatformTerminate(void) { _glfwTerminateEGL(); _glfwTerminateJoysticksLinux(); - _glfwTerminateThreadLocalStoragePOSIX(); _glfwDeleteEventQueueMir(_glfw.mir.eventQueue); diff --git a/src/nsgl_context.m b/src/nsgl_context.m index 976636443..f2902d7fa 100644 --- a/src/nsgl_context.m +++ b/src/nsgl_context.m @@ -34,7 +34,7 @@ static void makeContextCurrentNSGL(_GLFWwindow* window) else [NSOpenGLContext clearCurrentContext]; - _glfwPlatformSetCurrentContext(window); + _glfwPlatformSetTls(&_glfw.context, window); } static void swapBuffersNSGL(_GLFWwindow* window) @@ -45,7 +45,7 @@ static void swapBuffersNSGL(_GLFWwindow* window) static void swapIntervalNSGL(int interval) { - _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.context); GLint sync = interval; [window->context.nsgl.object setValues:&sync diff --git a/src/null_init.c b/src/null_init.c index e2c9c74bd..341473880 100644 --- a/src/null_init.c +++ b/src/null_init.c @@ -34,9 +34,6 @@ int _glfwPlatformInit(void) { - if (!_glfwInitThreadLocalStoragePOSIX()) - return GLFW_FALSE; - _glfwInitTimerPOSIX(); return GLFW_TRUE; } @@ -44,7 +41,6 @@ int _glfwPlatformInit(void) void _glfwPlatformTerminate(void) { _glfwTerminateOSMesa(); - _glfwTerminateThreadLocalStoragePOSIX(); } const char* _glfwPlatformGetVersionString(void) diff --git a/src/osmesa_context.c b/src/osmesa_context.c index 579dc391d..7ea656276 100644 --- a/src/osmesa_context.c +++ b/src/osmesa_context.c @@ -63,7 +63,7 @@ static void makeContextCurrentOSMesa(_GLFWwindow* window) } } - _glfwPlatformSetCurrentContext(window); + _glfwPlatformSetTls(&_glfw.context, window); } static GLFWglproc getProcAddressOSMesa(const char* procname) diff --git a/src/posix_tls.c b/src/posix_tls.c index 1b913a0f0..980ff23ba 100644 --- a/src/posix_tls.c +++ b/src/posix_tls.c @@ -27,42 +27,46 @@ #include "internal.h" - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWbool _glfwInitThreadLocalStoragePOSIX(void) -{ - if (pthread_key_create(&_glfw.posix_tls.context, NULL) != 0) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "POSIX: Failed to create context TLS"); - return GLFW_FALSE; - } - - _glfw.posix_tls.allocated = GLFW_TRUE; - return GLFW_TRUE; -} - -void _glfwTerminateThreadLocalStoragePOSIX(void) -{ - if (_glfw.posix_tls.allocated) - pthread_key_delete(_glfw.posix_tls.context); -} +#include +#include ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -void _glfwPlatformSetCurrentContext(_GLFWwindow* context) +GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls) { - pthread_setspecific(_glfw.posix_tls.context, context); + assert(tls->posix.allocated == GLFW_FALSE); + + if (pthread_key_create(&tls->posix.key, NULL) != 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "POSIX: Failed to create context TLS"); + return GLFW_FALSE; + } + + tls->posix.allocated = GLFW_TRUE; + return GLFW_TRUE; } -_GLFWwindow* _glfwPlatformGetCurrentContext(void) +void _glfwPlatformDestroyTls(_GLFWtls* tls) { - return pthread_getspecific(_glfw.posix_tls.context); + assert(tls->posix.allocated == GLFW_TRUE); + if (tls->posix.allocated) + pthread_key_delete(tls->posix.key); + memset(tls, 0, sizeof(_GLFWtls)); +} + +void* _glfwPlatformGetTls(_GLFWtls* tls) +{ + assert(tls->posix.allocated == GLFW_TRUE); + return pthread_getspecific(tls->posix.key); +} + +void _glfwPlatformSetTls(_GLFWtls* tls, void* value) +{ + assert(tls->posix.allocated == GLFW_TRUE); + pthread_setspecific(tls->posix.key, value); } diff --git a/src/posix_tls.h b/src/posix_tls.h index 8d033fba0..5d0e7c369 100644 --- a/src/posix_tls.h +++ b/src/posix_tls.h @@ -30,20 +30,17 @@ #include -#define _GLFW_PLATFORM_LIBRARY_TLS_STATE _GLFWtlsPOSIX posix_tls +#define _GLFW_PLATFORM_TLS_STATE _GLFWtlsPOSIX posix -// POSIX-specific global TLS data +// POSIX-specific thread local storage data // typedef struct _GLFWtlsPOSIX { GLFWbool allocated; - pthread_key_t context; + pthread_key_t key; } _GLFWtlsPOSIX; -GLFWbool _glfwInitThreadLocalStoragePOSIX(void); -void _glfwTerminateThreadLocalStoragePOSIX(void); - #endif // _glfw3_posix_tls_h_ diff --git a/src/wgl_context.c b/src/wgl_context.c index 0cbb68f53..c59997fc4 100644 --- a/src/wgl_context.c +++ b/src/wgl_context.c @@ -233,12 +233,12 @@ static void makeContextCurrentWGL(_GLFWwindow* window) if (window) { if (wglMakeCurrent(window->context.wgl.dc, window->context.wgl.handle)) - _glfwPlatformSetCurrentContext(window); + _glfwPlatformSetTls(&_glfw.context, window); else { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "WGL: Failed to make context current"); - _glfwPlatformSetCurrentContext(NULL); + _glfwPlatformSetTls(&_glfw.context, NULL); } } else @@ -249,7 +249,7 @@ static void makeContextCurrentWGL(_GLFWwindow* window) "WGL: Failed to clear current context"); } - _glfwPlatformSetCurrentContext(NULL); + _glfwPlatformSetTls(&_glfw.context, NULL); } } @@ -268,7 +268,7 @@ static void swapBuffersWGL(_GLFWwindow* window) static void swapIntervalWGL(int interval) { - _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.context); window->context.wgl.interval = interval; diff --git a/src/win32_init.c b/src/win32_init.c index 785d75d34..636851288 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -435,9 +435,6 @@ void _glfwInputErrorWin32(int error, const char* description) int _glfwPlatformInit(void) { - if (!_glfwInitThreadLocalStorageWin32()) - return GLFW_FALSE; - // To make SetForegroundWindow work as we want, we need to fiddle // with the FOREGROUNDLOCKTIMEOUT system setting (we do this as early // as possible in the hope of still being the foreground process) @@ -489,7 +486,6 @@ void _glfwPlatformTerminate(void) _glfwTerminateEGL(); _glfwTerminateJoysticksWin32(); - _glfwTerminateThreadLocalStorageWin32(); freeLibraries(); } diff --git a/src/win32_platform.h b/src/win32_platform.h index 8cf2b5a48..c77358e2d 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -221,9 +221,9 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)( #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWin32 win32 #define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWin32 win32 #define _GLFW_PLATFORM_LIBRARY_TIME_STATE _GLFWtimeWin32 win32_time -#define _GLFW_PLATFORM_LIBRARY_TLS_STATE _GLFWtlsWin32 win32_tls #define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorWin32 win32 #define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorWin32 win32 +#define _GLFW_PLATFORM_TLS_STATE _GLFWtlsWin32 win32 // Win32-specific per-window data @@ -328,12 +328,12 @@ typedef struct _GLFWtimeWin32 } _GLFWtimeWin32; -// Win32-specific global TLS data +// Win32-specific thread local storage data // typedef struct _GLFWtlsWin32 { GLFWbool allocated; - DWORD context; + DWORD index; } _GLFWtlsWin32; @@ -341,9 +341,6 @@ typedef struct _GLFWtlsWin32 GLFWbool _glfwRegisterWindowClassWin32(void); void _glfwUnregisterWindowClassWin32(void); -GLFWbool _glfwInitThreadLocalStorageWin32(void); -void _glfwTerminateThreadLocalStorageWin32(void); - WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source); char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source); void _glfwInputErrorWin32(int error, const char* description); diff --git a/src/win32_tls.c b/src/win32_tls.c index 2bcb68a54..944f7e5e0 100644 --- a/src/win32_tls.c +++ b/src/win32_tls.c @@ -27,43 +27,46 @@ #include "internal.h" - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWbool _glfwInitThreadLocalStorageWin32(void) -{ - _glfw.win32_tls.context = TlsAlloc(); - if (_glfw.win32_tls.context == TLS_OUT_OF_INDEXES) - { - _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "Win32: Failed to allocate TLS index"); - return GLFW_FALSE; - } - - _glfw.win32_tls.allocated = GLFW_TRUE; - return GLFW_TRUE; -} - -void _glfwTerminateThreadLocalStorageWin32(void) -{ - if (_glfw.win32_tls.allocated) - TlsFree(_glfw.win32_tls.context); -} +#include ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -void _glfwPlatformSetCurrentContext(_GLFWwindow* context) +GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls) { - TlsSetValue(_glfw.win32_tls.context, context); + assert(tls->win32.allocated == GLFW_FALSE); + + tls->win32.index = TlsAlloc(); + if (tls->win32.index == TLS_OUT_OF_INDEXES) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to allocate TLS index"); + return GLFW_FALSE; + } + + tls->win32.allocated = GLFW_TRUE; + return GLFW_TRUE; } -_GLFWwindow* _glfwPlatformGetCurrentContext(void) +void _glfwPlatformDestroyTls(_GLFWtls* tls) { - return TlsGetValue(_glfw.win32_tls.context); + assert(tls->win32.allocated == GLFW_TRUE); + if (tls->win32.allocated) + TlsFree(tls->win32.index); + memset(tls, 0, sizeof(_GLFWtls)); +} + +void* _glfwPlatformGetTls(_GLFWtls* tls) +{ + assert(tls->win32.allocated == GLFW_TRUE); + return TlsGetValue(tls->win32.index); +} + +void _glfwPlatformSetTls(_GLFWtls* tls, void* value) +{ + assert(tls->win32.allocated == GLFW_TRUE); + TlsSetValue(tls->win32.index, value); } diff --git a/src/window.c b/src/window.c index d16f75c59..bf98723ea 100644 --- a/src/window.c +++ b/src/window.c @@ -192,7 +192,7 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, window->denom = GLFW_DONT_CARE; // Save the currently current context so it can be restored later - previous = _glfwPlatformGetCurrentContext(); + previous = _glfwPlatformGetTls(&_glfw.context); if (ctxconfig.client != GLFW_NO_API) glfwMakeContextCurrent(NULL); @@ -408,7 +408,7 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow* handle) // The window's context must not be current on another thread when the // window is destroyed - if (window == _glfwPlatformGetCurrentContext()) + if (window == _glfwPlatformGetTls(&_glfw.context)) glfwMakeContextCurrent(NULL); _glfwPlatformDestroyWindow(window); diff --git a/src/wl_init.c b/src/wl_init.c index ecc94f2a7..8e12bafa7 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -667,9 +667,6 @@ int _glfwPlatformInit(void) // Sync so we got all initial output events wl_display_roundtrip(_glfw.wl.display); - if (!_glfwInitThreadLocalStoragePOSIX()) - return GLFW_FALSE; - if (!_glfwInitJoysticksLinux()) return GLFW_FALSE; @@ -695,7 +692,6 @@ void _glfwPlatformTerminate(void) { _glfwTerminateEGL(); _glfwTerminateJoysticksLinux(); - _glfwTerminateThreadLocalStoragePOSIX(); xkb_compose_state_unref(_glfw.wl.xkb.composeState); xkb_keymap_unref(_glfw.wl.xkb.keymap); diff --git a/src/x11_init.c b/src/x11_init.c index 734c89cc4..ca271546b 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -822,9 +822,6 @@ int _glfwPlatformInit(void) } } - if (!_glfwInitThreadLocalStoragePOSIX()) - return GLFW_FALSE; - #if defined(__linux__) if (!_glfwInitJoysticksLinux()) return GLFW_FALSE; @@ -885,7 +882,6 @@ void _glfwPlatformTerminate(void) #if defined(__linux__) _glfwTerminateJoysticksLinux(); #endif - _glfwTerminateThreadLocalStoragePOSIX(); } const char* _glfwPlatformGetVersionString(void) From 1982543cd2943151ab538ccb31ee48cd857ab2d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Sat, 18 Mar 2017 23:09:34 +0100 Subject: [PATCH 24/36] Cleanup --- src/cocoa_platform.h | 6 +++--- src/cocoa_time.c | 4 ++-- src/init.c | 2 +- src/input.c | 4 ++-- src/internal.h | 10 ++++++---- src/posix_time.c | 12 ++++++------ src/posix_time.h | 6 +++--- src/win32_platform.h | 6 +++--- src/win32_time.c | 12 ++++++------ 9 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index 0b47d611b..74db7fa59 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -66,7 +66,7 @@ typedef VkResult (APIENTRY *PFN_vkCreateMacOSSurfaceMVK)(VkInstance,const VkMacO #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNS ns #define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryNS ns -#define _GLFW_PLATFORM_LIBRARY_TIME_STATE _GLFWtimeNS ns_time +#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerNS ns #define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorNS ns #define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorNS ns @@ -151,11 +151,11 @@ typedef struct _GLFWcursorNS // Cocoa-specific global timer data // -typedef struct _GLFWtimeNS +typedef struct _GLFWtimerNS { uint64_t frequency; -} _GLFWtimeNS; +} _GLFWtimerNS; void _glfwInitTimerNS(void); diff --git a/src/cocoa_time.c b/src/cocoa_time.c index 8bbeb0103..3b2703515 100644 --- a/src/cocoa_time.c +++ b/src/cocoa_time.c @@ -40,7 +40,7 @@ void _glfwInitTimerNS(void) mach_timebase_info_data_t info; mach_timebase_info(&info); - _glfw.ns_time.frequency = (info.denom * 1e9) / info.numer; + _glfw.timer.ns.frequency = (info.denom * 1e9) / info.numer; } @@ -55,6 +55,6 @@ uint64_t _glfwPlatformGetTimerValue(void) uint64_t _glfwPlatformGetTimerFrequency(void) { - return _glfw.ns_time.frequency; + return _glfw.timer.ns.frequency; } diff --git a/src/init.c b/src/init.c index 92450b59a..7585c567d 100644 --- a/src/init.c +++ b/src/init.c @@ -174,7 +174,7 @@ GLFWAPI int glfwInit(void) } _glfw.initialized = GLFW_TRUE; - _glfw.timerOffset = _glfwPlatformGetTimerValue(); + _glfw.timer.offset = _glfwPlatformGetTimerValue(); // Not all window hints have zero as their default value glfwDefaultWindowHints(); diff --git a/src/input.c b/src/input.c index 115eefb27..16459b3f5 100644 --- a/src/input.c +++ b/src/input.c @@ -770,7 +770,7 @@ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle) GLFWAPI double glfwGetTime(void) { _GLFW_REQUIRE_INIT_OR_RETURN(0.0); - return (double) (_glfwPlatformGetTimerValue() - _glfw.timerOffset) / + return (double) (_glfwPlatformGetTimerValue() - _glfw.timer.offset) / _glfwPlatformGetTimerFrequency(); } @@ -784,7 +784,7 @@ GLFWAPI void glfwSetTime(double time) return; } - _glfw.timerOffset = _glfwPlatformGetTimerValue() - + _glfw.timer.offset = _glfwPlatformGetTimerValue() - (uint64_t) (time * _glfwPlatformGetTimerFrequency()); } diff --git a/src/internal.h b/src/internal.h index d3a4f6878..be9ced898 100644 --- a/src/internal.h +++ b/src/internal.h @@ -517,10 +517,14 @@ struct _GLFWlibrary _GLFWjoystick joysticks[GLFW_JOYSTICK_LAST + 1]; - uint64_t timerOffset; - _GLFWtls context; + struct { + uint64_t offset; + // This is defined in the platform's time.h + _GLFW_PLATFORM_LIBRARY_TIMER_STATE; + } timer; + struct { GLFWbool available; void* handle; @@ -553,8 +557,6 @@ struct _GLFWlibrary _GLFW_PLATFORM_LIBRARY_WINDOW_STATE; // This is defined in the context API's context.h _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE; - // This is defined in the platform's time.h - _GLFW_PLATFORM_LIBRARY_TIME_STATE; // This is defined in the platform's joystick.h _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE; // This is defined in egl_context.h diff --git a/src/posix_time.c b/src/posix_time.c index a0e276910..00b2831d1 100644 --- a/src/posix_time.c +++ b/src/posix_time.c @@ -44,14 +44,14 @@ void _glfwInitTimerPOSIX(void) if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { - _glfw.posix_time.monotonic = GLFW_TRUE; - _glfw.posix_time.frequency = 1000000000; + _glfw.timer.posix.monotonic = GLFW_TRUE; + _glfw.timer.posix.frequency = 1000000000; } else #endif { - _glfw.posix_time.monotonic = GLFW_FALSE; - _glfw.posix_time.frequency = 1000000; + _glfw.timer.posix.monotonic = GLFW_FALSE; + _glfw.timer.posix.frequency = 1000000; } } @@ -63,7 +63,7 @@ void _glfwInitTimerPOSIX(void) uint64_t _glfwPlatformGetTimerValue(void) { #if defined(CLOCK_MONOTONIC) - if (_glfw.posix_time.monotonic) + if (_glfw.timer.posix.monotonic) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); @@ -80,6 +80,6 @@ uint64_t _glfwPlatformGetTimerValue(void) uint64_t _glfwPlatformGetTimerFrequency(void) { - return _glfw.posix_time.frequency; + return _glfw.timer.posix.frequency; } diff --git a/src/posix_time.h b/src/posix_time.h index 17d5c17fd..f4cf4db1e 100644 --- a/src/posix_time.h +++ b/src/posix_time.h @@ -28,19 +28,19 @@ #ifndef _glfw3_posix_time_h_ #define _glfw3_posix_time_h_ -#define _GLFW_PLATFORM_LIBRARY_TIME_STATE _GLFWtimePOSIX posix_time +#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerPOSIX posix #include // POSIX-specific global timer data // -typedef struct _GLFWtimePOSIX +typedef struct _GLFWtimerPOSIX { GLFWbool monotonic; uint64_t frequency; -} _GLFWtimePOSIX; +} _GLFWtimerPOSIX; void _glfwInitTimerPOSIX(void); diff --git a/src/win32_platform.h b/src/win32_platform.h index c77358e2d..d51dee25c 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -220,7 +220,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)( #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWin32 win32 #define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWin32 win32 -#define _GLFW_PLATFORM_LIBRARY_TIME_STATE _GLFWtimeWin32 win32_time +#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerWin32 win32 #define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorWin32 win32 #define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorWin32 win32 #define _GLFW_PLATFORM_TLS_STATE _GLFWtlsWin32 win32 @@ -321,12 +321,12 @@ typedef struct _GLFWcursorWin32 // Win32-specific global timer data // -typedef struct _GLFWtimeWin32 +typedef struct _GLFWtimerWin32 { GLFWbool hasPC; uint64_t frequency; -} _GLFWtimeWin32; +} _GLFWtimerWin32; // Win32-specific thread local storage data // diff --git a/src/win32_time.c b/src/win32_time.c index a533c3772..f333cd444 100644 --- a/src/win32_time.c +++ b/src/win32_time.c @@ -40,13 +40,13 @@ void _glfwInitTimerWin32(void) if (QueryPerformanceFrequency((LARGE_INTEGER*) &frequency)) { - _glfw.win32_time.hasPC = GLFW_TRUE; - _glfw.win32_time.frequency = frequency; + _glfw.timer.win32.hasPC = GLFW_TRUE; + _glfw.timer.win32.frequency = frequency; } else { - _glfw.win32_time.hasPC = GLFW_FALSE; - _glfw.win32_time.frequency = 1000; + _glfw.timer.win32.hasPC = GLFW_FALSE; + _glfw.timer.win32.frequency = 1000; } } @@ -57,7 +57,7 @@ void _glfwInitTimerWin32(void) uint64_t _glfwPlatformGetTimerValue(void) { - if (_glfw.win32_time.hasPC) + if (_glfw.timer.win32.hasPC) { uint64_t value; QueryPerformanceCounter((LARGE_INTEGER*) &value); @@ -69,6 +69,6 @@ uint64_t _glfwPlatformGetTimerValue(void) uint64_t _glfwPlatformGetTimerFrequency(void) { - return _glfw.win32_time.frequency; + return _glfw.timer.win32.frequency; } From 55d0560746632deb2df0807f4ba190383a353ad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 23 Mar 2017 15:54:34 +0100 Subject: [PATCH 25/36] Cocoa: Fix range handling for hats and buttons Fixes #888. --- README.md | 1 + src/cocoa_joystick.m | 29 ++++++++++++----------------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 76b384a37..9baa86bfc 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,7 @@ information on what to include when reporting a bug. - [Cocoa] Bugfix: Leaving video mode with `glfwSetWindowMonitor` would set incorrect position and size (#748) - [Cocoa] Bugfix: Iconified full screen windows could not be restored (#848) +- [Cocoa] Bugfix: Value range was ignored for joystick hats and buttons (#888) - [X11] Moved to XI2 `XI_RawMotion` for disable cursor mode motion input (#125) - [EGL] Added support for `EGL_KHR_get_all_proc_addresses` (#871) - [EGL] Added support for `EGL_KHR_context_flush_control` diff --git a/src/cocoa_joystick.m b/src/cocoa_joystick.m index e828002b0..61d85a9f4 100644 --- a/src/cocoa_joystick.m +++ b/src/cocoa_joystick.m @@ -53,29 +53,19 @@ typedef struct _GLFWjoyelementNS // static long getElementValue(_GLFWjoystick* js, _GLFWjoyelementNS* element) { - IOReturn result = kIOReturnSuccess; IOHIDValueRef valueRef; long value = 0; - if (js && element && js->ns.device) + if (js->ns.device) { - result = IOHIDDeviceGetValue(js->ns.device, - element->native, - &valueRef); - - if (kIOReturnSuccess == result) + if (IOHIDDeviceGetValue(js->ns.device, + element->native, + &valueRef) == kIOReturnSuccess) { value = IOHIDValueGetIntegerValue(valueRef); - - // Record min and max for auto calibration - if (value < element->minimum) - element->minimum = value; - if (value > element->maximum) - element->maximum = value; } } - // Auto user scale return value; } @@ -349,8 +339,13 @@ int _glfwPlatformPollJoystick(int jid, int mode) CFArrayGetValueAtIndex(js->ns.axes, i); const long value = getElementValue(js, axis); - const long delta = axis->maximum - axis->minimum; + // Perform auto calibration + if (value < axis->minimum) + axis->minimum = value; + if (value > axis->maximum) + axis->maximum = value; + const long delta = axis->maximum - axis->minimum; if (delta == 0) _glfwInputJoystickAxis(jid, i, value); else @@ -365,7 +360,7 @@ int _glfwPlatformPollJoystick(int jid, int mode) { _GLFWjoyelementNS* button = (_GLFWjoyelementNS*) CFArrayGetValueAtIndex(js->ns.buttons, i); - const char value = getElementValue(js, button) ? 1 : 0; + const char value = getElementValue(js, button) - button->minimum; _glfwInputJoystickButton(jid, i, value); } @@ -386,7 +381,7 @@ int _glfwPlatformPollJoystick(int jid, int mode) _GLFWjoyelementNS* hat = (_GLFWjoyelementNS*) CFArrayGetValueAtIndex(js->ns.hats, i); - long state = getElementValue(js, hat); + long state = getElementValue(js, hat) - hat->minimum; if (state < 0 || state > 8) state = 8; From cf2eab5b4ee2de1808e80a2a49096f011fbd0b82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 23 Mar 2017 17:30:39 +0100 Subject: [PATCH 26/36] Cocoa: Made axis auto-calibration less jumpy --- src/cocoa_joystick.m | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/cocoa_joystick.m b/src/cocoa_joystick.m index 61d85a9f4..d1a230352 100644 --- a/src/cocoa_joystick.m +++ b/src/cocoa_joystick.m @@ -338,18 +338,21 @@ int _glfwPlatformPollJoystick(int jid, int mode) _GLFWjoyelementNS* axis = (_GLFWjoyelementNS*) CFArrayGetValueAtIndex(js->ns.axes, i); - const long value = getElementValue(js, axis); + const long raw = getElementValue(js, axis); // Perform auto calibration - if (value < axis->minimum) - axis->minimum = value; - if (value > axis->maximum) - axis->maximum = value; + if (raw < axis->minimum) + axis->minimum = raw; + if (raw > axis->maximum) + axis->maximum = raw; const long delta = axis->maximum - axis->minimum; if (delta == 0) - _glfwInputJoystickAxis(jid, i, value); + _glfwInputJoystickAxis(jid, i, 0.f); else - _glfwInputJoystickAxis(jid, i, (2.f * (value - axis->minimum) / delta) - 1.f); + { + const float value = (2.f * (raw - axis->minimum) / delta) - 1.f; + _glfwInputJoystickAxis(jid, i, value); + } } } else if (mode == _GLFW_POLL_BUTTONS) From 5fe4dfb511e12b954f9fba11d989df48f5f203fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Sun, 19 Mar 2017 05:30:27 +0100 Subject: [PATCH 27/36] X11: Narrow criteria for disabling RandR Fixes #972. --- README.md | 1 + src/x11_init.c | 19 ++++++++----------- src/x11_monitor.c | 7 ------- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 9baa86bfc..f537aeade 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,7 @@ information on what to include when reporting a bug. - [X11] Bugfix: Dynamic X11 library loading did not use full sonames (#941) - [X11] Bugfix: Window creation on 64-bit would read past top of stack (#951) - [X11] Bugfix: XDND support had multiple non-conformance issues (#968) +- [X11] Bugfix: The RandR monitor path was disabled despite working RandR (#972) - [Linux] Bugfix: Event processing did not detect joystick disconnection (#932) - [Cocoa] Added support for Vulkan window surface creation via [MoltenVK](https://moltengl.com/moltenvk/) (#870) diff --git a/src/x11_init.c b/src/x11_init.c index ca271546b..f283e6a55 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -530,26 +530,23 @@ static GLFWbool initExtensions(void) if (!sr->ncrtc || !XRRGetCrtcGammaSize(_glfw.x11.display, sr->crtcs[0])) { - // This is either a headless system or an older Nvidia binary driver - // with broken gamma support - // Flag it as useless and fall back to Xf86VidMode gamma, if - // available - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Detected broken RandR gamma ramp support"); + // This is likely an older Nvidia driver with broken gamma support + // Flag it as useless and fall back to xf86vm gamma, if available _glfw.x11.randr.gammaBroken = GLFW_TRUE; } - if (!sr->ncrtc || !sr->noutput || !sr->nmode) + if (!sr->ncrtc) { - // This is either a headless system or broken Cygwin/X RandR - // Flag it as useless and fall back to Xlib display functions - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: Detected broken RandR monitor support"); + // A system without CRTCs is likely a system with broken RandR + // Disable the RandR monitor path and fall back to core functions _glfw.x11.randr.monitorBroken = GLFW_TRUE; } XRRFreeScreenResources(sr); + } + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) + { XRRSelectInput(_glfw.x11.display, _glfw.x11.root, RROutputChangeNotifyMask); } diff --git a/src/x11_monitor.c b/src/x11_monitor.c index a32d01f4e..3d6172bae 100644 --- a/src/x11_monitor.c +++ b/src/x11_monitor.c @@ -202,13 +202,6 @@ void _glfwPollMonitorsX11(void) } free(disconnected); - - if (!_glfw.monitorCount) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "X11: RandR monitor support seems broken"); - _glfw.x11.randr.monitorBroken = GLFW_TRUE; - } } if (!_glfw.monitorCount) From b215a989f5f6b7af5fdb86d83547a989debc0dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Sun, 19 Mar 2017 22:21:46 +0100 Subject: [PATCH 28/36] Win32: Check for monitor object creation failure --- src/win32_monitor.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/win32_monitor.c b/src/win32_monitor.c index d91a02f83..06e0e0cf3 100644 --- a/src/win32_monitor.c +++ b/src/win32_monitor.c @@ -94,6 +94,7 @@ void _glfwPollMonitorsWin32(void) _GLFWmonitor** disconnected = NULL; DWORD adapterIndex, displayIndex; DISPLAY_DEVICEW adapter, display; + _GLFWmonitor* monitor; disconnectedCount = _glfw.monitorCount; if (disconnectedCount) @@ -145,8 +146,14 @@ void _glfwPollMonitorsWin32(void) if (i < disconnectedCount) continue; - _glfwInputMonitor(createMonitor(&adapter, &display), - GLFW_CONNECTED, type); + monitor = createMonitor(&adapter, &display); + if (!monitor) + { + free(disconnected); + return; + } + + _glfwInputMonitor(monitor, GLFW_CONNECTED, type); type = _GLFW_INSERT_LAST; } @@ -169,8 +176,14 @@ void _glfwPollMonitorsWin32(void) if (i < disconnectedCount) continue; - _glfwInputMonitor(createMonitor(&adapter, NULL), - GLFW_CONNECTED, type); + monitor = createMonitor(&adapter, NULL); + if (!monitor) + { + free(disconnected); + return; + } + + _glfwInputMonitor(monitor, GLFW_CONNECTED, type); } } From 6abb5cbcbb90c7f9c2111f1453db72d4d95f0eda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Sun, 19 Mar 2017 22:25:11 +0100 Subject: [PATCH 29/36] Formatting --- src/x11_monitor.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/x11_monitor.c b/src/x11_monitor.c index 3d6172bae..f8d25e37f 100644 --- a/src/x11_monitor.c +++ b/src/x11_monitor.c @@ -430,9 +430,9 @@ void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) _glfwAllocGammaArrays(ramp, size); - memcpy(ramp->red, gamma->red, size * sizeof(unsigned short)); + memcpy(ramp->red, gamma->red, size * sizeof(unsigned short)); memcpy(ramp->green, gamma->green, size * sizeof(unsigned short)); - memcpy(ramp->blue, gamma->blue, size * sizeof(unsigned short)); + memcpy(ramp->blue, gamma->blue, size * sizeof(unsigned short)); XRRFreeGamma(gamma); } @@ -455,9 +455,9 @@ void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) { XRRCrtcGamma* gamma = XRRAllocGamma(ramp->size); - memcpy(gamma->red, ramp->red, ramp->size * sizeof(unsigned short)); + memcpy(gamma->red, ramp->red, ramp->size * sizeof(unsigned short)); memcpy(gamma->green, ramp->green, ramp->size * sizeof(unsigned short)); - memcpy(gamma->blue, ramp->blue, ramp->size * sizeof(unsigned short)); + memcpy(gamma->blue, ramp->blue, ramp->size * sizeof(unsigned short)); XRRSetCrtcGamma(_glfw.x11.display, monitor->x11.crtc, gamma); XRRFreeGamma(gamma); From 120082ee3a5697b9170b1899a0ff74f80db3663c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 20 Mar 2017 01:17:21 +0100 Subject: [PATCH 30/36] Add Git commit message article --- .github/CONTRIBUTING.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 2584ec3f2..6d85f5cd5 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -336,6 +336,9 @@ other bugs and features to work on. If the patch fixes a bug introduced after the last release, it should not get a change log entry. +If you haven't already, read the excellent article [How to Write a Git Commit +Message](https://chris.beams.io/posts/git-commit/). + ## Contributing a feature @@ -373,6 +376,9 @@ If it adds a new OpenGL, OpenGL ES or Vulkan option or extension, support for it must be added to `tests/glfwinfo.c` and the behavior of the library when the extension is missing documented in `docs/compat.dox`. +If you haven't already, read the excellent article [How to Write a Git Commit +Message](https://chris.beams.io/posts/git-commit/). + Features will not be rejected because they don't include all the above parts, but please keep in mind that maintainer time is finite and that there are many other features and bugs to work on. From b0277a129b11588f2801c9ef6e4b2ab1d4a0c2ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 20 Mar 2017 17:48:45 +0100 Subject: [PATCH 31/36] Fix comment --- src/x11_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/x11_init.c b/src/x11_init.c index f283e6a55..8b6685895 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -455,7 +455,7 @@ static void detectEWMH(void) XFree(supportedAtoms); } -// Initialize X11 display and look for supported X11 extensions +// Look for and initialize supported X11 extensions // static GLFWbool initExtensions(void) { From 9b81d72c46065fd3e37ed0724bf7c16eb0e848ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 20 Mar 2017 17:49:05 +0100 Subject: [PATCH 32/36] X11: Fix libXi soname --- src/x11_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/x11_init.c b/src/x11_init.c index 8b6685895..a13f23f07 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -477,7 +477,7 @@ static GLFWbool initExtensions(void) &_glfw.x11.vidmode.errorBase); } - _glfw.x11.xi.handle = dlopen("libXi.so", RTLD_LAZY | RTLD_GLOBAL); + _glfw.x11.xi.handle = dlopen("libXi.so.6", RTLD_LAZY | RTLD_GLOBAL); if (_glfw.x11.xi.handle) { _glfw.x11.xi.QueryVersion = (PFN_XIQueryVersion) From 11c15b562d2619ea47187726d2bd243fb6923765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 20 Mar 2017 17:50:21 +0100 Subject: [PATCH 33/36] Remove superfluous comments --- src/x11_init.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/x11_init.c b/src/x11_init.c index a13f23f07..02ffcb577 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -503,7 +503,6 @@ static GLFWbool initExtensions(void) } } - // Check for RandR extension if (XRRQueryExtension(_glfw.x11.display, &_glfw.x11.randr.eventBase, &_glfw.x11.randr.errorBase)) @@ -559,7 +558,6 @@ static GLFWbool initExtensions(void) _glfw.x11.xinerama.available = GLFW_TRUE; } - // Check if Xkb is supported on this display _glfw.x11.xkb.major = 1; _glfw.x11.xkb.minor = 0; _glfw.x11.xkb.available = From 72d58d7b9318199a26ac01bb3040834bb10e3d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 20 Mar 2017 17:51:03 +0100 Subject: [PATCH 34/36] Cleanup --- src/x11_init.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/x11_init.c b/src/x11_init.c index 02ffcb577..1767b6954 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -660,11 +660,8 @@ static GLFWbool initExtensions(void) // static Cursor createHiddenCursor(void) { - unsigned char pixels[16 * 16 * 4]; + unsigned char pixels[16 * 16 * 4] = { 0 }; GLFWimage image = { 16, 16, pixels }; - - memset(pixels, 0, sizeof(pixels)); - return _glfwCreateCursorX11(&image, 0, 0); } From 4ff66a7818e3eaa5362c828a18220b12f1cd9bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 27 Mar 2017 01:19:41 +0200 Subject: [PATCH 35/36] X11: Fix IM-duplicated key events leaking through Fixes #747. Fixes #964. --- README.md | 1 + src/x11_platform.h | 3 +-- src/x11_window.c | 13 ++++++------- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index f537aeade..8ec76cde4 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,7 @@ information on what to include when reporting a bug. - [X11] Bugfix: Window creation on 64-bit would read past top of stack (#951) - [X11] Bugfix: XDND support had multiple non-conformance issues (#968) - [X11] Bugfix: The RandR monitor path was disabled despite working RandR (#972) +- [X11] Bugfix: IM-duplicated key events would leak at low polling rates (#747) - [Linux] Bugfix: Event processing did not detect joystick disconnection (#932) - [Cocoa] Added support for Vulkan window surface creation via [MoltenVK](https://moltengl.com/moltenvk/) (#870) diff --git a/src/x11_platform.h b/src/x11_platform.h index 4ca5a1dae..bafe88f4b 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -141,8 +141,7 @@ typedef struct _GLFWwindowX11 // The last position the cursor was warped to by GLFW int warpCursorPosX, warpCursorPosY; - // The information from the last KeyPress event - unsigned int lastKeyCode; + // The time of the last KeyPress event Time lastKeyTime; } _GLFWwindowX11; diff --git a/src/x11_window.c b/src/x11_window.c index 907fa7946..e1c2a3e3e 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -992,17 +992,16 @@ static void processEvent(XEvent *event) if (window->x11.ic) { // HACK: Ignore duplicate key press events generated by ibus - // Corresponding release events are filtered out by the - // GLFW key repeat logic - if (window->x11.lastKeyCode != keycode || - window->x11.lastKeyTime != event->xkey.time) + // These have the same timestamp as the original event + // Corresponding release events are filtered out + // implicitly by the GLFW key repeat logic + if (window->x11.lastKeyTime < event->xkey.time) { if (keycode) _glfwInputKey(window, key, keycode, GLFW_PRESS, mods); - } - window->x11.lastKeyCode = keycode; - window->x11.lastKeyTime = event->xkey.time; + window->x11.lastKeyTime = event->xkey.time; + } if (!filtered) { From 9f63a8abfac11ad6681b8f2937267992180b3549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 27 Mar 2017 18:40:04 +0200 Subject: [PATCH 36/36] Update changelog Issue #682 was implicitly fixed by 77a8f103d8f8519d1a723bbd8b0490b448f1f430. Closes #682. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8ec76cde4..65420ef84 100644 --- a/README.md +++ b/README.md @@ -190,6 +190,8 @@ information on what to include when reporting a bug. incorrect position and size (#748) - [Cocoa] Bugfix: Iconified full screen windows could not be restored (#848) - [Cocoa] Bugfix: Value range was ignored for joystick hats and buttons (#888) +- [Cocoa] Bugfix: Full screen framebuffer was incorrectly sized for some video + modes (#682) - [X11] Moved to XI2 `XI_RawMotion` for disable cursor mode motion input (#125) - [EGL] Added support for `EGL_KHR_get_all_proc_addresses` (#871) - [EGL] Added support for `EGL_KHR_context_flush_control`