diff --git a/README.md b/README.md index 1cec365e..68c1557a 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ information on what to include when reporting a bug. - Added `GLFW_FEATURE_UNIMPLEMENTED` error for incomplete backends (#1692) - Added `GLFW_ANGLE_PLATFORM_TYPE` init hint and `GLFW_ANGLE_PLATFORM_TYPE_*` values to select ANGLE backend (#1380) + - Made joystick subsystem initialize at first use (#1284,#1646) - Updated the minimum required CMake version to 3.1 - Disabled tests and examples by default when built as a CMake subdirectory - Bugfix: The CMake config-file package used an absolute path and was not diff --git a/docs/input.dox b/docs/input.dox index 331f9718..e6c3204f 100644 --- a/docs/input.dox +++ b/docs/input.dox @@ -550,10 +550,10 @@ int present = glfwJoystickPresent(GLFW_JOYSTICK_1); Each joystick has zero or more axes, zero or more buttons, zero or more hats, a human-readable name, a user pointer and an SDL compatible GUID. -When GLFW is initialized, detected joysticks are added to the beginning of -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. +Detected joysticks are added to the beginning of 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 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 diff --git a/docs/intro.dox b/docs/intro.dox index 1d041ae6..efc7c34c 100644 --- a/docs/intro.dox +++ b/docs/intro.dox @@ -22,8 +22,8 @@ There are also guides for the other areas of GLFW. Before most GLFW functions may be called, the library must be initialized. This initialization checks what features are available on the machine, -enumerates monitors and joysticks, initializes the timer and performs any -required platform-specific initialization. +enumerates monitors, initializes the timer and performs any required +platform-specific initialization. Only the following functions may be called before the library has been successfully initialized, and only from the main thread. diff --git a/docs/news.dox b/docs/news.dox index 0638bbfa..0fa3a632 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -55,6 +55,21 @@ applications. @subsection caveats_34 Caveats for version 3.4 +@subsubsection joysticks_34 Joystick support is initialized on demand + +The joystick part of GLFW is now initialized when first used, primarily to work +around faulty Windows drivers that cause DirectInput to take up to several +seconds to enumerate devices. + +This change will usually not be observable. However, if your application waits +for events without having first called any joystick function or created any +visible windows, the wait may never unblock as GLFW may not yet have subscribed +to joystick related OS events. + +To work around this, call any joystick function before waiting for events, for +example by setting a [joystick callback](@ref joystick_event). + + @subsubsection standalone_34 Tests and examples are disabled when built as a sub-project GLFW now does not build the tests and examples when it is added as diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 434e5beb..7cad8b8e 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -551,7 +551,6 @@ int _glfwPlatformInit(void) return GLFW_FALSE; _glfwInitTimerNS(); - _glfwInitJoysticksNS(); _glfwPollMonitorsNS(); @@ -605,7 +604,6 @@ void _glfwPlatformTerminate(void) free(_glfw.ns.clipboardString); _glfwTerminateNSGL(); - _glfwTerminateJoysticksNS(); } // autoreleasepool } diff --git a/src/cocoa_joystick.h b/src/cocoa_joystick.h index 9bf5cde4..23d2b86a 100644 --- a/src/cocoa_joystick.h +++ b/src/cocoa_joystick.h @@ -44,7 +44,3 @@ typedef struct _GLFWjoystickNS CFMutableArrayRef hats; } _GLFWjoystickNS; - -void _glfwInitJoysticksNS(void); -void _glfwTerminateJoysticksNS(void); - diff --git a/src/cocoa_joystick.m b/src/cocoa_joystick.m index 88636a87..386c3519 100644 --- a/src/cocoa_joystick.m +++ b/src/cocoa_joystick.m @@ -309,7 +309,7 @@ static void removeCallback(void* context, // Initialize joystick interface // -void _glfwInitJoysticksNS(void) +GLFWbool _glfwPlatformInitJoysticks(void) { CFMutableArrayRef matching; const long usages[] = @@ -328,7 +328,7 @@ void _glfwInitJoysticksNS(void) if (!matching) { _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create array"); - return; + return GLFW_FALSE; } for (size_t i = 0; i < sizeof(usages) / sizeof(long); i++) @@ -383,19 +383,23 @@ void _glfwInitJoysticksNS(void) // Execute the run loop once in order to register any initially-attached // joysticks CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false); + return GLFW_TRUE; } // Close all opened joystick handles // -void _glfwTerminateJoysticksNS(void) +void _glfwPlatformTerminateJoysticks(void) { int jid; for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) closeJoystick(_glfw.joysticks + jid); - CFRelease(_glfw.ns.hidManager); - _glfw.ns.hidManager = NULL; + if (_glfw.ns.hidManager) + { + CFRelease(_glfw.ns.hidManager); + _glfw.ns.hidManager = NULL; + } } diff --git a/src/init.c b/src/init.c index f9ce336a..a7b5af63 100644 --- a/src/init.c +++ b/src/init.c @@ -91,6 +91,7 @@ static void terminate(void) _glfw.mappingCount = 0; _glfwTerminateVulkan(); + _glfwPlatformTerminateJoysticks(); _glfwPlatformTerminate(); _glfw.initialized = GLFW_FALSE; diff --git a/src/input.c b/src/input.c index f6163093..97edacda 100644 --- a/src/input.c +++ b/src/input.c @@ -43,6 +43,22 @@ #define _GLFW_JOYSTICK_BUTTON 2 #define _GLFW_JOYSTICK_HATBIT 3 +// Initializes the platform joystick API if it has not been already +// +static GLFWbool initJoysticks(void) +{ + if (!_glfw.joysticksInitialized) + { + if (!_glfwPlatformInitJoysticks()) + { + _glfwPlatformTerminateJoysticks(); + return GLFW_FALSE; + } + } + + return _glfw.joysticksInitialized = GLFW_TRUE; +} + // Finds a mapping based on joystick GUID // static _GLFWmapping* findMapping(const char* guid) @@ -929,6 +945,9 @@ GLFWAPI int glfwJoystickPresent(int jid) return GLFW_FALSE; } + if (!initJoysticks()) + return GLFW_FALSE; + js = _glfw.joysticks + jid; if (!js->present) return GLFW_FALSE; @@ -954,6 +973,9 @@ GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count) return NULL; } + if (!initJoysticks()) + return GLFW_FALSE; + js = _glfw.joysticks + jid; if (!js->present) return NULL; @@ -983,6 +1005,9 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count) return NULL; } + if (!initJoysticks()) + return GLFW_FALSE; + js = _glfw.joysticks + jid; if (!js->present) return NULL; @@ -1016,6 +1041,9 @@ GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count) return NULL; } + if (!initJoysticks()) + return GLFW_FALSE; + js = _glfw.joysticks + jid; if (!js->present) return NULL; @@ -1042,6 +1070,9 @@ GLFWAPI const char* glfwGetJoystickName(int jid) return NULL; } + if (!initJoysticks()) + return GLFW_FALSE; + js = _glfw.joysticks + jid; if (!js->present) return NULL; @@ -1067,6 +1098,9 @@ GLFWAPI const char* glfwGetJoystickGUID(int jid) return NULL; } + if (!initJoysticks()) + return NULL; + js = _glfw.joysticks + jid; if (!js->present) return NULL; @@ -1112,6 +1146,10 @@ GLFWAPI void* glfwGetJoystickUserPointer(int jid) GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (!initJoysticks()) + return NULL; + _GLFW_SWAP_POINTERS(_glfw.callbacks.joystick, cbfun); return cbfun; } @@ -1191,6 +1229,9 @@ GLFWAPI int glfwJoystickIsGamepad(int jid) return GLFW_FALSE; } + if (!initJoysticks()) + return GLFW_FALSE; + js = _glfw.joysticks + jid; if (!js->present) return GLFW_FALSE; @@ -1216,6 +1257,9 @@ GLFWAPI const char* glfwGetGamepadName(int jid) return NULL; } + if (!initJoysticks()) + return NULL; + js = _glfw.joysticks + jid; if (!js->present) return NULL; @@ -1248,6 +1292,9 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state) return GLFW_FALSE; } + if (!initJoysticks()) + return GLFW_FALSE; + js = _glfw.joysticks + jid; if (!js->present) return GLFW_FALSE; diff --git a/src/internal.h b/src/internal.h index 9fd1a8e9..48053cde 100644 --- a/src/internal.h +++ b/src/internal.h @@ -539,6 +539,7 @@ struct _GLFWlibrary _GLFWmonitor** monitors; int monitorCount; + GLFWbool joysticksInitialized; _GLFWjoystick joysticks[GLFW_JOYSTICK_LAST + 1]; _GLFWmapping* mappings; int mappingCount; @@ -632,6 +633,8 @@ void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) void _glfwPlatformSetClipboardString(const char* string); const char* _glfwPlatformGetClipboardString(void); +GLFWbool _glfwPlatformInitJoysticks(void); +void _glfwPlatformTerminateJoysticks(void); int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode); void _glfwPlatformUpdateGamepadGUID(char* guid); diff --git a/src/linux_joystick.c b/src/linux_joystick.c index 1f9b35fe..553f12df 100644 --- a/src/linux_joystick.c +++ b/src/linux_joystick.c @@ -266,7 +266,7 @@ static int compareJoysticks(const void* fp, const void* sp) // Initialize joystick interface // -GLFWbool _glfwInitJoysticksLinux(void) +GLFWbool _glfwPlatformInitJoysticks(void) { const char* dirname = "/dev/input"; @@ -322,7 +322,7 @@ GLFWbool _glfwInitJoysticksLinux(void) // Close all opened joystick handles // -void _glfwTerminateJoysticksLinux(void) +void _glfwPlatformTerminateJoysticks(void) { int jid; @@ -333,14 +333,13 @@ void _glfwTerminateJoysticksLinux(void) closeJoystick(js); } - regfree(&_glfw.linjs.regex); - if (_glfw.linjs.inotify > 0) { if (_glfw.linjs.watch > 0) inotify_rm_watch(_glfw.linjs.inotify, _glfw.linjs.watch); close(_glfw.linjs.inotify); + regfree(&_glfw.linjs.regex); } } diff --git a/src/linux_joystick.h b/src/linux_joystick.h index 7373f130..05d5488f 100644 --- a/src/linux_joystick.h +++ b/src/linux_joystick.h @@ -55,8 +55,5 @@ typedef struct _GLFWlibraryLinux GLFWbool dropped; } _GLFWlibraryLinux; - -GLFWbool _glfwInitJoysticksLinux(void); -void _glfwTerminateJoysticksLinux(void); void _glfwDetectJoystickConnectionLinux(void); diff --git a/src/null_joystick.c b/src/null_joystick.c index 36c18aa2..933b282d 100644 --- a/src/null_joystick.c +++ b/src/null_joystick.c @@ -33,6 +33,15 @@ ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// +int _glfwPlatformInitJoysticks(void) +{ + return GLFW_TRUE; +} + +void _glfwPlatformTerminateJoysticks(void) +{ +} + int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) { return GLFW_FALSE; diff --git a/src/win32_init.c b/src/win32_init.c index 260e888e..cbc0fd83 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -580,7 +580,6 @@ int _glfwPlatformInit(void) return GLFW_FALSE; _glfwInitTimerWin32(); - _glfwInitJoysticksWin32(); _glfwPollMonitorsWin32(); return GLFW_TRUE; @@ -607,8 +606,6 @@ void _glfwPlatformTerminate(void) _glfwTerminateWGL(); _glfwTerminateEGL(); - _glfwTerminateJoysticksWin32(); - freeLibraries(); } diff --git a/src/win32_joystick.c b/src/win32_joystick.c index 49187dab..63fe3762 100644 --- a/src/win32_joystick.c +++ b/src/win32_joystick.c @@ -493,7 +493,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) // Initialize joystick interface // -void _glfwInitJoysticksWin32(void) +GLFWbool _glfwPlatformInitJoysticks(void) { if (_glfw.win32.dinput8.instance) { @@ -505,15 +505,17 @@ void _glfwInitJoysticksWin32(void) { _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create interface"); + return GLFW_FALSE; } } _glfwDetectJoystickConnectionWin32(); + return GLFW_TRUE; } // Close all opened joystick handles // -void _glfwTerminateJoysticksWin32(void) +void _glfwPlatformTerminateJoysticks(void) { int jid; diff --git a/src/win32_joystick.h b/src/win32_joystick.h index f593274e..b6a7adc3 100644 --- a/src/win32_joystick.h +++ b/src/win32_joystick.h @@ -48,9 +48,6 @@ typedef struct _GLFWjoystickWin32 GUID guid; } _GLFWjoystickWin32; - -void _glfwInitJoysticksWin32(void); -void _glfwTerminateJoysticksWin32(void); void _glfwDetectJoystickConnectionWin32(void); void _glfwDetectJoystickDisconnectionWin32(void); diff --git a/src/win32_window.c b/src/win32_window.c index bc93c62a..486c102e 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -531,6 +531,9 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_DEVICECHANGE: { + if (!_glfw.joysticksInitialized) + break; + if (wParam == DBT_DEVICEARRIVAL) { DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; diff --git a/src/wl_init.c b/src/wl_init.c index bc44c88e..21a808aa 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -1146,11 +1146,6 @@ int _glfwPlatformInit(void) // Sync so we got all initial output events wl_display_roundtrip(_glfw.wl.display); -#ifdef __linux__ - if (!_glfwInitJoysticksLinux()) - return GLFW_FALSE; -#endif - _glfwInitTimerPOSIX(); _glfw.wl.timerfd = -1; @@ -1213,9 +1208,6 @@ int _glfwPlatformInit(void) void _glfwPlatformTerminate(void) { -#ifdef __linux__ - _glfwTerminateJoysticksLinux(); -#endif _glfwTerminateEGL(); if (_glfw.wl.egl.handle) { diff --git a/src/x11_init.c b/src/x11_init.c index f16f9604..dd24f112 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -1377,11 +1377,6 @@ int _glfwPlatformInit(void) NULL); } -#if defined(__linux__) - if (!_glfwInitJoysticksLinux()) - return GLFW_FALSE; -#endif - _glfwInitTimerPOSIX(); _glfwPollMonitorsX11(); @@ -1475,10 +1470,6 @@ void _glfwPlatformTerminate(void) _glfwTerminateEGL(); _glfwTerminateGLX(); -#if defined(__linux__) - _glfwTerminateJoysticksLinux(); -#endif - if (_glfw.x11.xlib.handle) { _glfw_dlclose(_glfw.x11.xlib.handle);