diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 47de6f6b6..6d85f5cd5 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 @@ -306,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 @@ -343,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. diff --git a/COPYING.txt b/LICENSE.md similarity index 95% rename from COPYING.txt rename to LICENSE.md index acdac20b9..e4c6682eb 100644 --- a/COPYING.txt +++ 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 diff --git a/README.md b/README.md index 298a148e9..65420ef84 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) @@ -162,10 +165,15 @@ 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) +- [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) - [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) @@ -178,8 +186,15 @@ 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) +- [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` - [EGL] Bugfix: The test for `EGL_RGB_BUFFER` was invalid 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 65e4a67c3..8b2efcec2 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -297,6 +297,23 @@ extern "C" { #define GLFW_REPEAT 2 /*! @} */ +/*! @defgroup hat_state Joystick hat states + * + * 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. * @@ -928,6 +945,8 @@ extern "C" { /*! @addtogroup init * @{ */ +#define GLFW_JOYSTICK_HAT_BUTTONS 0x00050001 + #define GLFW_COCOA_CHDIR_RESOURCES 0x00051001 #define GLFW_COCOA_MENUBAR 0x00051002 /*! @} */ @@ -4003,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. * @@ -4036,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. * @@ -4065,13 +4091,72 @@ 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 values: + * + * 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 + * + * 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. + * + * @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. + * + * @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 + * 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. * 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/include/GLFW/glfw3native.h b/include/GLFW/glfw3native.h index 78e169be6..982eaa656 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. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4ddb41cc1..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 @@ -48,10 +49,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/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/cocoa_joystick.m b/src/cocoa_joystick.m index 07aa32b61..d1a230352 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; } @@ -204,7 +194,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; @@ -347,44 +338,57 @@ int _glfwPlatformPollJoystick(int jid, int mode) _GLFWjoyelementNS* axis = (_GLFWjoyelementNS*) CFArrayGetValueAtIndex(js->ns.axes, i); - const long value = getElementValue(js, axis); - const long delta = axis->maximum - axis->minimum; + const long raw = getElementValue(js, axis); + // Perform auto calibration + 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) { - 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); + const char value = getElementValue(js, button) - button->minimum; + _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) - hat->minimum; 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/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; } 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/cocoa_window.m b/src/cocoa_window.m index 657852ab8..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 @@ -1025,7 +1030,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]; @@ -1310,6 +1320,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]; 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 368cba969..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) { @@ -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 @@ -401,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; } @@ -438,6 +442,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, EGLint attribs[40]; EGLConfig config; EGLContext share = NULL; + int index = 0; if (!_glfw.egl.display) { @@ -478,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) { @@ -527,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); @@ -609,6 +621,8 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, "libGLESv2.dll", #elif defined(_GLFW_COCOA) "libGLESv2.dylib", +#elif defined(__CYGWIN__) + "libGLESv2-2.so", #else "libGLESv2.so.2", #endif 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; 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 478b1211e..7585c567d 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 @@ -112,6 +113,8 @@ static void terminate(void) _glfwTerminateVulkan(); _glfwPlatformTerminate(); + _glfwPlatformDestroyTls(&_glfw.context); + memset(&_glfw, 0, sizeof(_glfw)); } @@ -161,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(); @@ -168,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(); @@ -188,6 +194,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 923e3fca4..16459b3f5 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,10 +682,43 @@ 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); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", jid); + 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) { assert(jid >= GLFW_JOYSTICK_1); @@ -718,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(); } @@ -732,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 f43638dff..be9ced898 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*); @@ -266,6 +267,7 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void); */ struct _GLFWinitconfig { + GLFWbool hatButtons; struct { GLFWbool menubar; GLFWbool chdir; @@ -476,12 +478,22 @@ 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 _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 @@ -505,7 +517,13 @@ 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; @@ -539,12 +557,8 @@ 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 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 @@ -631,13 +645,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); + /*! @} */ @@ -823,6 +839,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 +935,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/mir_init.c b/src/mir_init.c index 2eccdde10..cf3dcf1ec 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; @@ -218,7 +215,6 @@ void _glfwPlatformTerminate(void) { _glfwTerminateEGL(); _glfwTerminateJoysticksLinux(); - _glfwTerminateThreadLocalStoragePOSIX(); _glfwDeleteEventQueueMir(_glfw.mir.eventQueue); diff --git a/src/mir_window.c b/src/mir_window.c index a4a069a71..273358e6e 100644 --- a/src/mir_window.c +++ b/src/mir_window.c @@ -401,10 +401,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; 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_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/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 b092d1c7b..636851288 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) @@ -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_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/src/win32_monitor.c b/src/win32_monitor.c index cc5e24f8f..06e0e0cf3 100644 --- a/src/win32_monitor.c +++ b/src/win32_monitor.c @@ -94,7 +94,7 @@ void _glfwPollMonitorsWin32(void) _GLFWmonitor** disconnected = NULL; DWORD adapterIndex, displayIndex; DISPLAY_DEVICEW adapter, display; - GLFWbool hasDisplays = GLFW_FALSE; + _GLFWmonitor* monitor; disconnectedCount = _glfw.monitorCount; if (disconnectedCount) @@ -105,30 +105,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 +121,46 @@ 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; + + monitor = createMonitor(&adapter, &display); + if (!monitor) + { + free(disconnected); + return; + } + + _glfwInputMonitor(monitor, 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++) { @@ -191,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); } } diff --git a/src/win32_platform.h b/src/win32_platform.h index 8cf2b5a48..d51dee25c 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -220,10 +220,10 @@ 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_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 // Win32-specific per-window data @@ -321,19 +321,19 @@ typedef struct _GLFWcursorWin32 // Win32-specific global timer data // -typedef struct _GLFWtimeWin32 +typedef struct _GLFWtimerWin32 { GLFWbool hasPC; uint64_t frequency; -} _GLFWtimeWin32; +} _GLFWtimerWin32; -// 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_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; } 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/win32_window.c b/src/win32_window.c index 1e0a587e9..363ef0f32 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -467,30 +467,23 @@ 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) - { - 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; 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/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) diff --git a/src/x11_init.c b/src/x11_init.c index 22600c965..1767b6954 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) { @@ -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) @@ -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)) @@ -530,26 +529,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); } @@ -562,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 = @@ -629,9 +624,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 @@ -664,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); } @@ -821,9 +814,6 @@ int _glfwPlatformInit(void) } } - if (!_glfwInitThreadLocalStoragePOSIX()) - return GLFW_FALSE; - #if defined(__linux__) if (!_glfwInitJoysticksLinux()) return GLFW_FALSE; @@ -884,7 +874,6 @@ void _glfwPlatformTerminate(void) #if defined(__linux__) _glfwTerminateJoysticksLinux(); #endif - _glfwTerminateThreadLocalStoragePOSIX(); } const char* _glfwPlatformGetVersionString(void) diff --git a/src/x11_monitor.c b/src/x11_monitor.c index a32d01f4e..f8d25e37f 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) @@ -437,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); } @@ -462,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); diff --git a/src/x11_platform.h b/src/x11_platform.h index b2e4f5de0..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; @@ -208,9 +207,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 +253,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 c65d3d22d..e1c2a3e3e 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); @@ -957,6 +963,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) { @@ -975,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) { @@ -1296,44 +1312,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, + _glfw.x11.root, + window->x11.handle, + 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); } @@ -1343,11 +1436,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, @@ -1368,21 +1461,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; @@ -1476,18 +1571,6 @@ static void processEvent(XEvent *event) return; } - case SelectionClear: - { - handleSelectionClear(event); - return; - } - - case SelectionRequest: - { - handleSelectionRequest(event); - return; - } - case DestroyNotify: return; } 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 { diff --git a/tests/joysticks.c b/tests/joysticks.c index 02d4f0e3f..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; @@ -142,6 +142,8 @@ int main(void) { nk_layout_row_dynamic(nk, 30, 1); + nk_checkbox_label(nk, "Hat buttons", &hat_buttons); + if (joystick_count) { for (i = 0; i < joystick_count; i++) @@ -167,29 +169,81 @@ 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) + 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); + + for (j = 0; j < button_count; j++) { - for (j = 0; j < axis_count; j++) - nk_slide_float(nk, -1.f, axes[j], 1.f, 0.1f); + 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); - buttons = glfwGetJoystickButtons(joysticks[i], &button_count); - if (button_count) + for (j = 0; j < hat_count; j++) { - for (j = 0; j < button_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)); } } }