Ver código fonte

Add glfwSetJoystickCallback

Camilla Berglund 9 anos atrás
pai
commit
8a7fa306ce
12 arquivos alterados com 220 adições e 101 exclusões
  1. 2 0
      README.md
  2. 27 0
      docs/input.dox
  3. 40 0
      include/GLFW/glfw3.h
  4. 4 0
      src/cocoa_joystick.m
  5. 13 0
      src/input.c
  6. 8 0
      src/internal.h
  7. 29 19
      src/linux_joystick.c
  8. 2 0
      src/linux_joystick.h
  9. 3 0
      src/win32_joystick.c
  10. 12 2
      src/x11_window.c
  11. 24 0
      tests/events.c
  12. 56 80
      tests/joysticks.c

+ 2 - 0
README.md

@@ -88,6 +88,8 @@ does not find Doxygen, the documentation will not be generated.
  - Added `glfwWaitEventsTimeout` for waiting for events for a set amount of time
  - Added `glfwSetWindowIcon` for setting the icon of a window
  - Added `glfwGetTimerValue` and `glfwGetTimerFrequency` for raw timer access
+ - Added `glfwSetJoystickCallback` and `GLFWjoystickfun` for joystick connection
+   and disconnection events
  - Added `GLFW_NO_API` for creating window without contexts
  - Added `GLFW_CONTEXT_NO_ERROR` context hint for `GL_KHR_no_error` support
  - Added `GLFW_INCLUDE_VULKAN` for including the Vulkan header

+ 27 - 0
docs/input.dox

@@ -547,6 +547,33 @@ and make may have the same name.  Only the [joystick token](@ref joysticks) is
 guaranteed to be unique, and only until that joystick is disconnected.
 
 
+@subsection joystick_event Joystick configuration changes
+
+If you wish to be notified when a joystick is connected or disconnected, set
+a joystick callback.
+
+@code
+glfwSetJoystickCallback(joystick_callback);
+@endcode
+
+The callback function receives the ID of the joystick that has been connected
+and disconnected and the event that occurred.
+
+@code
+void joystick_callback(int joy, int event)
+{
+    if (event == GLFW_CONNECTED)
+    {
+        // The joystick was connected
+    }
+    else if (event == GLFW_DISCONNECTED)
+    {
+        // The joystick was disconnected
+    }
+}
+@endcode
+
+
 @section time Time input
 
 GLFW provides high-resolution time input, in seconds, with @ref glfwGetTime.

+ 40 - 0
include/GLFW/glfw3.h

@@ -1097,6 +1097,23 @@ typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**);
  */
 typedef void (* GLFWmonitorfun)(GLFWmonitor*,int);
 
+/*! @brief The function signature for joystick configuration callbacks.
+ *
+ *  This is the function signature for joystick configuration callback
+ *  functions.
+ *
+ *  @param[in] joy The joystick that was connected or disconnected.
+ *  @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`.
+ *
+ *  @sa @ref joystick_event
+ *  @sa glfwSetJoystickCallback
+ *
+ *  @since Added in version 3.2.
+ *
+ *  @ingroup input
+ */
+typedef void (* GLFWjoystickfun)(int,int);
+
 /*! @brief Video mode type.
  *
  *  This describes a single video mode.
@@ -3596,6 +3613,29 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count);
  */
 GLFWAPI const char* glfwGetJoystickName(int joy);
 
+/*! @brief Sets the joystick configuration callback.
+ *
+ *  This function sets the joystick configuration callback, or removes the
+ *  currently set callback.  This is called when a joystick is connected to or
+ *  disconnected from the system.
+ *
+ *  @param[in] cbfun The new callback, or `NULL` to remove the currently set
+ *  callback.
+ *  @return The previously set callback, or `NULL` if no callback was set or the
+ *  library had not been [initialized](@ref intro_init).
+ *
+ *  @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
+ *
+ *  @thread_safety This function must only be called from the main thread.
+ *
+ *  @sa @ref joystick_event
+ *
+ *  @since Added in version 3.2.
+ *
+ *  @ingroup input
+ */
+GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun);
+
 /*! @brief Sets the clipboard to the specified string.
  *
  *  This function sets the system clipboard to the specified, UTF-8 encoded

+ 4 - 0
src/cocoa_joystick.m

@@ -190,6 +190,8 @@ static void removeJoystick(_GLFWjoydeviceNS* joystick)
     free(joystick->buttons);
 
     memset(joystick, 0, sizeof(_GLFWjoydeviceNS));
+
+    _glfwInputJoystickChange(joystick - _glfw.ns_js.devices, GLFW_DISCONNECTED);
 }
 
 // Polls for joystick axis events and updates GLFW state
@@ -329,6 +331,8 @@ static void matchCallback(void* context,
                             sizeof(float));
     joystick->buttons = calloc(CFArrayGetCount(joystick->buttonElements) +
                                CFArrayGetCount(joystick->hatElements) * 4, 1);
+
+    _glfwInputJoystickChange(joy, GLFW_CONNECTED);
 }
 
 // Callback for user-initiated joystick removal

+ 13 - 0
src/input.c

@@ -220,6 +220,12 @@ void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths)
         window->callbacks.drop((GLFWwindow*) window, count, paths);
 }
 
+void _glfwInputJoystickChange(int joy, int event)
+{
+    if (_glfw.callbacks.joystick)
+        _glfw.callbacks.joystick(joy, event);
+}
+
 
 //////////////////////////////////////////////////////////////////////////
 //////                       GLFW internal API                      //////
@@ -621,6 +627,13 @@ GLFWAPI const char* glfwGetJoystickName(int joy)
     return _glfwPlatformGetJoystickName(joy);
 }
 
+GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun)
+{
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+    _GLFW_SWAP_POINTERS(_glfw.callbacks.joystick, cbfun);
+    return cbfun;
+}
+
 GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string)
 {
     _GLFWwindow* window = (_GLFWwindow*) handle;

+ 8 - 0
src/internal.h

@@ -448,6 +448,7 @@ struct _GLFWlibrary
 
     struct {
         GLFWmonitorfun  monitor;
+        GLFWjoystickfun joystick;
     } callbacks;
 
     // This is defined in the window API's platform.h
@@ -947,6 +948,13 @@ void _glfwInputError(int error, const char* format, ...);
  */
 void _glfwInputDrop(_GLFWwindow* window, int count, const char** names);
 
+/*! @brief Notifies shared code of a joystick connection/disconnection event.
+ *  @param[in] joy The joystick that was connected or disconnected.
+ *  @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`.
+ *  @ingroup event
+ */
+void _glfwInputJoystickChange(int joy, int event);
+
 
 //========================================================================
 // Utility functions

+ 29 - 19
src/linux_joystick.c

@@ -100,6 +100,8 @@ static GLFWbool openJoystickDevice(const char* path)
     ioctl(fd, JSIOCGBUTTONS, &buttonCount);
     js->buttonCount = (int) buttonCount;
     js->buttons = calloc(buttonCount, 1);
+
+    _glfwInputJoystickChange(joy, GLFW_CONNECTED);
 #endif // __linux__
     return GLFW_TRUE;
 }
@@ -109,25 +111,7 @@ static GLFWbool openJoystickDevice(const char* path)
 static GLFWbool pollJoystickEvents(_GLFWjoystickLinux* js)
 {
 #if defined(__linux__)
-    ssize_t offset = 0;
-    char buffer[16384];
-
-    const ssize_t size = read(_glfw.linux_js.inotify, buffer, sizeof(buffer));
-
-    while (size > offset)
-    {
-        regmatch_t match;
-        const struct inotify_event* e = (struct inotify_event*) (buffer + offset);
-
-        if (regexec(&_glfw.linux_js.regex, e->name, 1, &match, 0) == 0)
-        {
-            char path[20];
-            snprintf(path, sizeof(path), "/dev/input/%s", e->name);
-            openJoystickDevice(path);
-        }
-
-        offset += sizeof(struct inotify_event) + e->len;
-    }
+    _glfwPollJoystickEvents();
 
     if (!js->present)
         return GLFW_FALSE;
@@ -149,6 +133,9 @@ static GLFWbool pollJoystickEvents(_GLFWjoystickLinux* js)
                 free(js->path);
 
                 memset(js, 0, sizeof(_GLFWjoystickLinux));
+
+                _glfwInputJoystickChange(js - _glfw.linux_js.js,
+                                         GLFW_DISCONNECTED);
             }
 
             break;
@@ -285,6 +272,29 @@ void _glfwTerminateJoysticksLinux(void)
 #endif // __linux__
 }
 
+void _glfwPollJoystickEvents(void)
+{
+    ssize_t offset = 0;
+    char buffer[16384];
+
+    const ssize_t size = read(_glfw.linux_js.inotify, buffer, sizeof(buffer));
+
+    while (size > offset)
+    {
+        regmatch_t match;
+        const struct inotify_event* e = (struct inotify_event*) (buffer + offset);
+
+        if (regexec(&_glfw.linux_js.regex, e->name, 1, &match, 0) == 0)
+        {
+            char path[20];
+            snprintf(path, sizeof(path), "/dev/input/%s", e->name);
+            openJoystickDevice(path);
+        }
+
+        offset += sizeof(struct inotify_event) + e->len;
+    }
+}
+
 
 //////////////////////////////////////////////////////////////////////////
 //////                       GLFW platform API                      //////

+ 2 - 0
src/linux_joystick.h

@@ -64,4 +64,6 @@ typedef struct _GLFWjoylistLinux
 GLFWbool _glfwInitJoysticksLinux(void);
 void _glfwTerminateJoysticksLinux(void);
 
+void _glfwPollJoystickEvents(void);
+
 #endif // _glfw3_linux_joystick_h_

+ 3 - 0
src/win32_joystick.c

@@ -97,6 +97,8 @@ static GLFWbool openJoystickDevice(DWORD index)
     js->name = strdup(getDeviceDescription(&xic));
     js->index = index;
 
+    _glfwInputJoystickChange(joy, GLFW_CONNECTED);
+
     return GLFW_TRUE;
 }
 
@@ -120,6 +122,7 @@ static GLFWbool pollJoystickEvents(_GLFWjoystickWin32* js, int flags)
         {
             free(js->name);
             memset(js, 0, sizeof(_GLFWjoystickWin32));
+            _glfwInputJoystickChange((int) (js - _glfw.win32_js), GLFW_DISCONNECTED);
         }
 
         return GLFW_FALSE;

+ 12 - 2
src/x11_window.c

@@ -54,11 +54,19 @@
 void selectDisplayConnection(struct timeval* timeout)
 {
     fd_set fds;
-    int result;
+    int result, count;
     const int fd = ConnectionNumber(_glfw.x11.display);
 
     FD_ZERO(&fds);
     FD_SET(fd, &fds);
+#if defined(__linux__)
+    FD_SET(_glfw.linux_js.inotify, &fds);
+#endif
+
+    if (fd > _glfw.linux_js.inotify)
+        count = fd + 1;
+    else
+        count = _glfw.linux_js.inotify + 1;
 
     // NOTE: We use select instead of an X function like XNextEvent, as the
     //       wait inside those are guarded by the mutex protecting the display
@@ -68,7 +76,7 @@ void selectDisplayConnection(struct timeval* timeout)
     // TODO: Update timeout value manually
     do
     {
-        result = select(fd + 1, &fds, NULL, NULL, timeout);
+        result = select(count, &fds, NULL, NULL, timeout);
     }
     while (result == -1 && errno == EINTR && timeout == NULL);
 }
@@ -1999,6 +2007,8 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window)
 
 void _glfwPlatformPollEvents(void)
 {
+    _glfwPollJoystickEvents();
+
     int count = XPending(_glfw.x11.display);
     while (count--)
     {

+ 24 - 0
tests/events.c

@@ -451,6 +451,29 @@ static void monitor_callback(GLFWmonitor* monitor, int event)
     }
 }
 
+static void joystick_callback(int joy, int event)
+{
+    if (event == GLFW_CONNECTED)
+    {
+        int axisCount, buttonCount;
+
+        glfwGetJoystickAxes(joy, &axisCount);
+        glfwGetJoystickButtons(joy, &buttonCount);
+
+        printf("%08x at %0.3f: Joystick %i (%s) was connected with %i axes and %i buttons\n",
+               counter++, glfwGetTime(),
+               joy,
+               glfwGetJoystickName(joy),
+               axisCount,
+               buttonCount);
+    }
+    else
+    {
+        printf("%08x at %0.3f: Joystick %i was disconnected\n",
+               counter++, glfwGetTime(), joy);
+    }
+}
+
 int main(int argc, char** argv)
 {
     Slot* slots;
@@ -467,6 +490,7 @@ int main(int argc, char** argv)
     printf("Library initialized\n");
 
     glfwSetMonitorCallback(monitor_callback);
+    glfwSetJoystickCallback(joystick_callback);
 
     while ((ch = getopt(argc, argv, "hfn:")) != -1)
     {

+ 56 - 80
tests/joysticks.c

@@ -39,17 +39,7 @@
 #define strdup(x) _strdup(x)
 #endif
 
-typedef struct Joystick
-{
-    int present;
-    char* name;
-    float* axes;
-    unsigned char* buttons;
-    int axis_count;
-    int button_count;
-} Joystick;
-
-static Joystick joysticks[GLFW_JOYSTICK_LAST - GLFW_JOYSTICK_1 + 1];
+static int joysticks[GLFW_JOYSTICK_LAST + 1];
 static int joystick_count = 0;
 
 static void error_callback(int error, const char* description)
@@ -62,19 +52,23 @@ static void framebuffer_size_callback(GLFWwindow* window, int width, int height)
     glViewport(0, 0, width, height);
 }
 
-static void draw_joystick(Joystick* j, int x, int y, int width, int height)
+static void draw_joystick(int index, int x, int y, int width, int height)
 {
     int i;
+    int axis_count, button_count;
+    const float* axes;
+    const unsigned char* buttons;
     const int axis_height = 3 * height / 4;
     const int button_height = height / 4;
 
-    if (j->axis_count)
+    axes = glfwGetJoystickAxes(joysticks[index], &axis_count);
+    if (axis_count)
     {
-        const int axis_width = width / j->axis_count;
+        const int axis_width = width / axis_count;
 
-        for (i = 0;  i < j->axis_count;  i++)
+        for (i = 0;  i < axis_count;  i++)
         {
-            float value = j->axes[i] / 2.f + 0.5f;
+            float value = axes[i] / 2.f + 0.5f;
 
             glColor3f(0.3f, 0.3f, 0.3f);
             glRecti(x + i * axis_width,
@@ -90,13 +84,14 @@ static void draw_joystick(Joystick* j, int x, int y, int width, int height)
         }
     }
 
-    if (j->button_count)
+    buttons = glfwGetJoystickButtons(joysticks[index], &button_count);
+    if (button_count)
     {
-        const int button_width = width / j->button_count;
+        const int button_width = width / button_count;
 
-        for (i = 0;  i < j->button_count;  i++)
+        for (i = 0;  i < button_count;  i++)
         {
-            if (j->buttons[i])
+            if (buttons[i])
                 glColor3f(1.f, 1.f, 1.f);
             else
                 glColor3f(0.3f, 0.3f, 0.3f);
@@ -120,79 +115,58 @@ static void draw_joysticks(GLFWwindow* window)
     glOrtho(0.f, width, height, 0.f, 1.f, -1.f);
     glMatrixMode(GL_MODELVIEW);
 
-    for (i = 0;  i < sizeof(joysticks) / sizeof(Joystick);  i++)
+    for (i = 0;  i < joystick_count;  i++)
     {
-        Joystick* j = joysticks + i;
-
-        if (j->present)
-        {
-            draw_joystick(j,
-                          0, offset * height / joystick_count,
-                          width, height / joystick_count);
-            offset++;
-        }
+        draw_joystick(i,
+                      0, offset * height / joystick_count,
+                      width, height / joystick_count);
+        offset++;
     }
 }
 
-static void refresh_joysticks(void)
+static void joystick_callback(int joy, int event)
 {
-    int i;
-
-    for (i = 0;  i < sizeof(joysticks) / sizeof(Joystick);  i++)
+    if (event == GLFW_CONNECTED)
     {
-        Joystick* j = joysticks + i;
+        int axis_count, button_count;
 
-        if (glfwJoystickPresent(GLFW_JOYSTICK_1 + i))
-        {
-            const float* axes;
-            const unsigned char* buttons;
-            int axis_count, button_count;
-
-            free(j->name);
-            j->name = strdup(glfwGetJoystickName(GLFW_JOYSTICK_1 + i));
-
-            axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1 + i, &axis_count);
-            if (axis_count != j->axis_count)
-            {
-                j->axis_count = axis_count;
-                j->axes = realloc(j->axes, j->axis_count * sizeof(float));
-            }
+        glfwGetJoystickAxes(joy, &axis_count);
+        glfwGetJoystickButtons(joy, &button_count);
 
-            memcpy(j->axes, axes, axis_count * sizeof(float));
+        printf("Found joystick %i named \'%s\' with %i axes, %i buttons\n",
+                joy + 1,
+                glfwGetJoystickName(joy),
+                axis_count,
+                button_count);
 
-            buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1 + i, &button_count);
-            if (button_count != j->button_count)
-            {
-                j->button_count = button_count;
-                j->buttons = realloc(j->buttons, j->button_count);
-            }
-
-            memcpy(j->buttons, buttons, button_count * sizeof(unsigned char));
+        joysticks[joystick_count++] = joy;
+    }
+    else if (event == GLFW_DISCONNECTED)
+    {
+        int i;
 
-            if (!j->present)
-            {
-                printf("Found joystick %i named \'%s\' with %i axes, %i buttons\n",
-                       i + 1, j->name, j->axis_count, j->button_count);
+        for (i = 0;  i < joystick_count;  i++)
+        {
+            if (joysticks[i] == joy)
+                break;
+        }
 
-                joystick_count++;
-            }
+        for (i = i + 1;  i < joystick_count;  i++)
+            joysticks[i - 1] = joysticks[i];
 
-            j->present = GLFW_TRUE;
-        }
-        else
-        {
-            if (j->present)
-            {
-                printf("Lost joystick %i named \'%s\'\n", i + 1, j->name);
+        printf("Lost joystick %i\n", joy + 1);
+        joystick_count--;
+    }
+}
 
-                free(j->name);
-                free(j->axes);
-                free(j->buttons);
-                memset(j, 0, sizeof(Joystick));
+static void find_joysticks(void)
+{
+    int joy;
 
-                joystick_count--;
-            }
-        }
+    for (joy = GLFW_JOYSTICK_1;  joy <= GLFW_JOYSTICK_LAST;  joy++)
+    {
+        if (glfwJoystickPresent(joy))
+            joystick_callback(joy, GLFW_CONNECTED);
     }
 }
 
@@ -207,6 +181,9 @@ int main(void)
     if (!glfwInit())
         exit(EXIT_FAILURE);
 
+    find_joysticks();
+    glfwSetJoystickCallback(joystick_callback);
+
     window = glfwCreateWindow(640, 480, "Joystick Test", NULL, NULL);
     if (!window)
     {
@@ -224,7 +201,6 @@ int main(void)
     {
         glClear(GL_COLOR_BUFFER_BIT);
 
-        refresh_joysticks();
         draw_joysticks(window);
 
         glfwSwapBuffers(window);