Make joystick platform code init on demand

This makes joystick support initialize the first time a joystick
function is called, including those gamepad functions that are layered
on top of joystick functions.

Related to #1284.
Related to #1646.
This commit is contained in:
Camilla Löwy 2020-07-12 18:31:29 +02:00
parent f760b124ca
commit 782e6b6cef
19 changed files with 101 additions and 49 deletions

View File

@ -130,6 +130,7 @@ information on what to include when reporting a bug.
- Added `GLFW_FEATURE_UNIMPLEMENTED` error for incomplete backends (#1692) - Added `GLFW_FEATURE_UNIMPLEMENTED` error for incomplete backends (#1692)
- Added `GLFW_ANGLE_PLATFORM_TYPE` init hint and `GLFW_ANGLE_PLATFORM_TYPE_*` - Added `GLFW_ANGLE_PLATFORM_TYPE` init hint and `GLFW_ANGLE_PLATFORM_TYPE_*`
values to select ANGLE backend (#1380) values to select ANGLE backend (#1380)
- Made joystick subsystem initialize at first use (#1284,#1646)
- Updated the minimum required CMake version to 3.1 - Updated the minimum required CMake version to 3.1
- Disabled tests and examples by default when built as a CMake subdirectory - 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 - Bugfix: The CMake config-file package used an absolute path and was not

View File

@ -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, 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. a human-readable name, a user pointer and an SDL compatible GUID.
When GLFW is initialized, detected joysticks are added to the beginning of Detected joysticks are added to the beginning of the array. Once a joystick is
the array. Once a joystick is detected, it keeps its assigned ID until it is detected, it keeps its assigned ID until it is disconnected or the library is
disconnected or the library is terminated, so as joysticks are connected and terminated, so as joysticks are connected and disconnected, there may appear
disconnected, there may appear gaps in the IDs. gaps in the IDs.
Joystick axis, button and hat state is updated when polled and does not require 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 a window to be created or events to be processed. However, if you want joystick

View File

@ -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. Before most GLFW functions may be called, the library must be initialized.
This initialization checks what features are available on the machine, This initialization checks what features are available on the machine,
enumerates monitors and joysticks, initializes the timer and performs any enumerates monitors, initializes the timer and performs any required
required platform-specific initialization. platform-specific initialization.
Only the following functions may be called before the library has been Only the following functions may be called before the library has been
successfully initialized, and only from the main thread. successfully initialized, and only from the main thread.

View File

@ -55,6 +55,21 @@ applications.
@subsection caveats_34 Caveats for version 3.4 @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 @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 GLFW now does not build the tests and examples when it is added as

View File

@ -551,7 +551,6 @@ int _glfwPlatformInit(void)
return GLFW_FALSE; return GLFW_FALSE;
_glfwInitTimerNS(); _glfwInitTimerNS();
_glfwInitJoysticksNS();
_glfwPollMonitorsNS(); _glfwPollMonitorsNS();
@ -605,7 +604,6 @@ void _glfwPlatformTerminate(void)
free(_glfw.ns.clipboardString); free(_glfw.ns.clipboardString);
_glfwTerminateNSGL(); _glfwTerminateNSGL();
_glfwTerminateJoysticksNS();
} // autoreleasepool } // autoreleasepool
} }

View File

@ -44,7 +44,3 @@ typedef struct _GLFWjoystickNS
CFMutableArrayRef hats; CFMutableArrayRef hats;
} _GLFWjoystickNS; } _GLFWjoystickNS;
void _glfwInitJoysticksNS(void);
void _glfwTerminateJoysticksNS(void);

View File

@ -309,7 +309,7 @@ static void removeCallback(void* context,
// Initialize joystick interface // Initialize joystick interface
// //
void _glfwInitJoysticksNS(void) GLFWbool _glfwPlatformInitJoysticks(void)
{ {
CFMutableArrayRef matching; CFMutableArrayRef matching;
const long usages[] = const long usages[] =
@ -328,7 +328,7 @@ void _glfwInitJoysticksNS(void)
if (!matching) if (!matching)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create array"); _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create array");
return; return GLFW_FALSE;
} }
for (size_t i = 0; i < sizeof(usages) / sizeof(long); i++) for (size_t i = 0; i < sizeof(usages) / sizeof(long); i++)
@ -383,20 +383,24 @@ void _glfwInitJoysticksNS(void)
// Execute the run loop once in order to register any initially-attached // Execute the run loop once in order to register any initially-attached
// joysticks // joysticks
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false); CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
return GLFW_TRUE;
} }
// Close all opened joystick handles // Close all opened joystick handles
// //
void _glfwTerminateJoysticksNS(void) void _glfwPlatformTerminateJoysticks(void)
{ {
int jid; int jid;
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
closeJoystick(_glfw.joysticks + jid); closeJoystick(_glfw.joysticks + jid);
if (_glfw.ns.hidManager)
{
CFRelease(_glfw.ns.hidManager); CFRelease(_glfw.ns.hidManager);
_glfw.ns.hidManager = NULL; _glfw.ns.hidManager = NULL;
} }
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////

View File

@ -91,6 +91,7 @@ static void terminate(void)
_glfw.mappingCount = 0; _glfw.mappingCount = 0;
_glfwTerminateVulkan(); _glfwTerminateVulkan();
_glfwPlatformTerminateJoysticks();
_glfwPlatformTerminate(); _glfwPlatformTerminate();
_glfw.initialized = GLFW_FALSE; _glfw.initialized = GLFW_FALSE;

View File

@ -43,6 +43,22 @@
#define _GLFW_JOYSTICK_BUTTON 2 #define _GLFW_JOYSTICK_BUTTON 2
#define _GLFW_JOYSTICK_HATBIT 3 #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 // Finds a mapping based on joystick GUID
// //
static _GLFWmapping* findMapping(const char* guid) static _GLFWmapping* findMapping(const char* guid)
@ -929,6 +945,9 @@ GLFWAPI int glfwJoystickPresent(int jid)
return GLFW_FALSE; return GLFW_FALSE;
} }
if (!initJoysticks())
return GLFW_FALSE;
js = _glfw.joysticks + jid; js = _glfw.joysticks + jid;
if (!js->present) if (!js->present)
return GLFW_FALSE; return GLFW_FALSE;
@ -954,6 +973,9 @@ GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count)
return NULL; return NULL;
} }
if (!initJoysticks())
return GLFW_FALSE;
js = _glfw.joysticks + jid; js = _glfw.joysticks + jid;
if (!js->present) if (!js->present)
return NULL; return NULL;
@ -983,6 +1005,9 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count)
return NULL; return NULL;
} }
if (!initJoysticks())
return GLFW_FALSE;
js = _glfw.joysticks + jid; js = _glfw.joysticks + jid;
if (!js->present) if (!js->present)
return NULL; return NULL;
@ -1016,6 +1041,9 @@ GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count)
return NULL; return NULL;
} }
if (!initJoysticks())
return GLFW_FALSE;
js = _glfw.joysticks + jid; js = _glfw.joysticks + jid;
if (!js->present) if (!js->present)
return NULL; return NULL;
@ -1042,6 +1070,9 @@ GLFWAPI const char* glfwGetJoystickName(int jid)
return NULL; return NULL;
} }
if (!initJoysticks())
return GLFW_FALSE;
js = _glfw.joysticks + jid; js = _glfw.joysticks + jid;
if (!js->present) if (!js->present)
return NULL; return NULL;
@ -1067,6 +1098,9 @@ GLFWAPI const char* glfwGetJoystickGUID(int jid)
return NULL; return NULL;
} }
if (!initJoysticks())
return NULL;
js = _glfw.joysticks + jid; js = _glfw.joysticks + jid;
if (!js->present) if (!js->present)
return NULL; return NULL;
@ -1112,6 +1146,10 @@ GLFWAPI void* glfwGetJoystickUserPointer(int jid)
GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun)
{ {
_GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
if (!initJoysticks())
return NULL;
_GLFW_SWAP_POINTERS(_glfw.callbacks.joystick, cbfun); _GLFW_SWAP_POINTERS(_glfw.callbacks.joystick, cbfun);
return cbfun; return cbfun;
} }
@ -1191,6 +1229,9 @@ GLFWAPI int glfwJoystickIsGamepad(int jid)
return GLFW_FALSE; return GLFW_FALSE;
} }
if (!initJoysticks())
return GLFW_FALSE;
js = _glfw.joysticks + jid; js = _glfw.joysticks + jid;
if (!js->present) if (!js->present)
return GLFW_FALSE; return GLFW_FALSE;
@ -1216,6 +1257,9 @@ GLFWAPI const char* glfwGetGamepadName(int jid)
return NULL; return NULL;
} }
if (!initJoysticks())
return NULL;
js = _glfw.joysticks + jid; js = _glfw.joysticks + jid;
if (!js->present) if (!js->present)
return NULL; return NULL;
@ -1248,6 +1292,9 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state)
return GLFW_FALSE; return GLFW_FALSE;
} }
if (!initJoysticks())
return GLFW_FALSE;
js = _glfw.joysticks + jid; js = _glfw.joysticks + jid;
if (!js->present) if (!js->present)
return GLFW_FALSE; return GLFW_FALSE;

View File

@ -539,6 +539,7 @@ struct _GLFWlibrary
_GLFWmonitor** monitors; _GLFWmonitor** monitors;
int monitorCount; int monitorCount;
GLFWbool joysticksInitialized;
_GLFWjoystick joysticks[GLFW_JOYSTICK_LAST + 1]; _GLFWjoystick joysticks[GLFW_JOYSTICK_LAST + 1];
_GLFWmapping* mappings; _GLFWmapping* mappings;
int mappingCount; int mappingCount;
@ -632,6 +633,8 @@ void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
void _glfwPlatformSetClipboardString(const char* string); void _glfwPlatformSetClipboardString(const char* string);
const char* _glfwPlatformGetClipboardString(void); const char* _glfwPlatformGetClipboardString(void);
GLFWbool _glfwPlatformInitJoysticks(void);
void _glfwPlatformTerminateJoysticks(void);
int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode); int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode);
void _glfwPlatformUpdateGamepadGUID(char* guid); void _glfwPlatformUpdateGamepadGUID(char* guid);

View File

@ -266,7 +266,7 @@ static int compareJoysticks(const void* fp, const void* sp)
// Initialize joystick interface // Initialize joystick interface
// //
GLFWbool _glfwInitJoysticksLinux(void) GLFWbool _glfwPlatformInitJoysticks(void)
{ {
const char* dirname = "/dev/input"; const char* dirname = "/dev/input";
@ -322,7 +322,7 @@ GLFWbool _glfwInitJoysticksLinux(void)
// Close all opened joystick handles // Close all opened joystick handles
// //
void _glfwTerminateJoysticksLinux(void) void _glfwPlatformTerminateJoysticks(void)
{ {
int jid; int jid;
@ -333,14 +333,13 @@ void _glfwTerminateJoysticksLinux(void)
closeJoystick(js); closeJoystick(js);
} }
regfree(&_glfw.linjs.regex);
if (_glfw.linjs.inotify > 0) if (_glfw.linjs.inotify > 0)
{ {
if (_glfw.linjs.watch > 0) if (_glfw.linjs.watch > 0)
inotify_rm_watch(_glfw.linjs.inotify, _glfw.linjs.watch); inotify_rm_watch(_glfw.linjs.inotify, _glfw.linjs.watch);
close(_glfw.linjs.inotify); close(_glfw.linjs.inotify);
regfree(&_glfw.linjs.regex);
} }
} }

View File

@ -55,8 +55,5 @@ typedef struct _GLFWlibraryLinux
GLFWbool dropped; GLFWbool dropped;
} _GLFWlibraryLinux; } _GLFWlibraryLinux;
GLFWbool _glfwInitJoysticksLinux(void);
void _glfwTerminateJoysticksLinux(void);
void _glfwDetectJoystickConnectionLinux(void); void _glfwDetectJoystickConnectionLinux(void);

View File

@ -33,6 +33,15 @@
////// GLFW platform API ////// ////// GLFW platform API //////
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
int _glfwPlatformInitJoysticks(void)
{
return GLFW_TRUE;
}
void _glfwPlatformTerminateJoysticks(void)
{
}
int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode)
{ {
return GLFW_FALSE; return GLFW_FALSE;

View File

@ -580,7 +580,6 @@ int _glfwPlatformInit(void)
return GLFW_FALSE; return GLFW_FALSE;
_glfwInitTimerWin32(); _glfwInitTimerWin32();
_glfwInitJoysticksWin32();
_glfwPollMonitorsWin32(); _glfwPollMonitorsWin32();
return GLFW_TRUE; return GLFW_TRUE;
@ -607,8 +606,6 @@ void _glfwPlatformTerminate(void)
_glfwTerminateWGL(); _glfwTerminateWGL();
_glfwTerminateEGL(); _glfwTerminateEGL();
_glfwTerminateJoysticksWin32();
freeLibraries(); freeLibraries();
} }

View File

@ -493,7 +493,7 @@ static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user)
// Initialize joystick interface // Initialize joystick interface
// //
void _glfwInitJoysticksWin32(void) GLFWbool _glfwPlatformInitJoysticks(void)
{ {
if (_glfw.win32.dinput8.instance) if (_glfw.win32.dinput8.instance)
{ {
@ -505,15 +505,17 @@ void _glfwInitJoysticksWin32(void)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,
"Win32: Failed to create interface"); "Win32: Failed to create interface");
return GLFW_FALSE;
} }
} }
_glfwDetectJoystickConnectionWin32(); _glfwDetectJoystickConnectionWin32();
return GLFW_TRUE;
} }
// Close all opened joystick handles // Close all opened joystick handles
// //
void _glfwTerminateJoysticksWin32(void) void _glfwPlatformTerminateJoysticks(void)
{ {
int jid; int jid;

View File

@ -48,9 +48,6 @@ typedef struct _GLFWjoystickWin32
GUID guid; GUID guid;
} _GLFWjoystickWin32; } _GLFWjoystickWin32;
void _glfwInitJoysticksWin32(void);
void _glfwTerminateJoysticksWin32(void);
void _glfwDetectJoystickConnectionWin32(void); void _glfwDetectJoystickConnectionWin32(void);
void _glfwDetectJoystickDisconnectionWin32(void); void _glfwDetectJoystickDisconnectionWin32(void);

View File

@ -531,6 +531,9 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
case WM_DEVICECHANGE: case WM_DEVICECHANGE:
{ {
if (!_glfw.joysticksInitialized)
break;
if (wParam == DBT_DEVICEARRIVAL) if (wParam == DBT_DEVICEARRIVAL)
{ {
DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam;

View File

@ -1146,11 +1146,6 @@ int _glfwPlatformInit(void)
// Sync so we got all initial output events // Sync so we got all initial output events
wl_display_roundtrip(_glfw.wl.display); wl_display_roundtrip(_glfw.wl.display);
#ifdef __linux__
if (!_glfwInitJoysticksLinux())
return GLFW_FALSE;
#endif
_glfwInitTimerPOSIX(); _glfwInitTimerPOSIX();
_glfw.wl.timerfd = -1; _glfw.wl.timerfd = -1;
@ -1213,9 +1208,6 @@ int _glfwPlatformInit(void)
void _glfwPlatformTerminate(void) void _glfwPlatformTerminate(void)
{ {
#ifdef __linux__
_glfwTerminateJoysticksLinux();
#endif
_glfwTerminateEGL(); _glfwTerminateEGL();
if (_glfw.wl.egl.handle) if (_glfw.wl.egl.handle)
{ {

View File

@ -1377,11 +1377,6 @@ int _glfwPlatformInit(void)
NULL); NULL);
} }
#if defined(__linux__)
if (!_glfwInitJoysticksLinux())
return GLFW_FALSE;
#endif
_glfwInitTimerPOSIX(); _glfwInitTimerPOSIX();
_glfwPollMonitorsX11(); _glfwPollMonitorsX11();
@ -1475,10 +1470,6 @@ void _glfwPlatformTerminate(void)
_glfwTerminateEGL(); _glfwTerminateEGL();
_glfwTerminateGLX(); _glfwTerminateGLX();
#if defined(__linux__)
_glfwTerminateJoysticksLinux();
#endif
if (_glfw.x11.xlib.handle) if (_glfw.x11.xlib.handle)
{ {
_glfw_dlclose(_glfw.x11.xlib.handle); _glfw_dlclose(_glfw.x11.xlib.handle);