github.com/Konstantin8105/c4go@v0.0.0-20240505174241-768bb1c65a51/tests/raylib/external/glfw/src/linux_joystick.c (about)

     1  //========================================================================
     2  // GLFW 3.4 Linux - www.glfw.org
     3  //------------------------------------------------------------------------
     4  // Copyright (c) 2002-2006 Marcus Geelnard
     5  // Copyright (c) 2006-2017 Camilla Löwy <elmindreda@glfw.org>
     6  //
     7  // This software is provided 'as-is', without any express or implied
     8  // warranty. In no event will the authors be held liable for any damages
     9  // arising from the use of this software.
    10  //
    11  // Permission is granted to anyone to use this software for any purpose,
    12  // including commercial applications, and to alter it and redistribute it
    13  // freely, subject to the following restrictions:
    14  //
    15  // 1. The origin of this software must not be misrepresented; you must not
    16  //    claim that you wrote the original software. If you use this software
    17  //    in a product, an acknowledgment in the product documentation would
    18  //    be appreciated but is not required.
    19  //
    20  // 2. Altered source versions must be plainly marked as such, and must not
    21  //    be misrepresented as being the original software.
    22  //
    23  // 3. This notice may not be removed or altered from any source
    24  //    distribution.
    25  //
    26  //========================================================================
    27  // It is fine to use C99 in this file because it will not be built with VS
    28  //========================================================================
    29  
    30  #include "internal.h"
    31  
    32  #include <sys/types.h>
    33  #include <sys/stat.h>
    34  #include <sys/inotify.h>
    35  #include <fcntl.h>
    36  #include <errno.h>
    37  #include <dirent.h>
    38  #include <stdio.h>
    39  #include <stdlib.h>
    40  #include <string.h>
    41  #include <unistd.h>
    42  
    43  #ifndef SYN_DROPPED // < v2.6.39 kernel headers
    44  // Workaround for CentOS-6, which is supported till 2020-11-30, but still on v2.6.32
    45  #define SYN_DROPPED 3
    46  #endif
    47  
    48  // Apply an EV_KEY event to the specified joystick
    49  //
    50  static void handleKeyEvent(_GLFWjoystick* js, int code, int value)
    51  {
    52      _glfwInputJoystickButton(js,
    53                               js->linjs.keyMap[code - BTN_MISC],
    54                               value ? GLFW_PRESS : GLFW_RELEASE);
    55  }
    56  
    57  // Apply an EV_ABS event to the specified joystick
    58  //
    59  static void handleAbsEvent(_GLFWjoystick* js, int code, int value)
    60  {
    61      const int index = js->linjs.absMap[code];
    62  
    63      if (code >= ABS_HAT0X && code <= ABS_HAT3Y)
    64      {
    65          static const char stateMap[3][3] =
    66          {
    67              { GLFW_HAT_CENTERED, GLFW_HAT_UP,       GLFW_HAT_DOWN },
    68              { GLFW_HAT_LEFT,     GLFW_HAT_LEFT_UP,  GLFW_HAT_LEFT_DOWN },
    69              { GLFW_HAT_RIGHT,    GLFW_HAT_RIGHT_UP, GLFW_HAT_RIGHT_DOWN },
    70          };
    71  
    72          const int hat = (code - ABS_HAT0X) / 2;
    73          const int axis = (code - ABS_HAT0X) % 2;
    74          int* state = js->linjs.hats[hat];
    75  
    76          // NOTE: Looking at several input drivers, it seems all hat events use
    77          //       -1 for left / up, 0 for centered and 1 for right / down
    78          if (value == 0)
    79              state[axis] = 0;
    80          else if (value < 0)
    81              state[axis] = 1;
    82          else if (value > 0)
    83              state[axis] = 2;
    84  
    85          _glfwInputJoystickHat(js, index, stateMap[state[0]][state[1]]);
    86      }
    87      else
    88      {
    89          const struct input_absinfo* info = &js->linjs.absInfo[code];
    90          float normalized = value;
    91  
    92          const int range = info->maximum - info->minimum;
    93          if (range)
    94          {
    95              // Normalize to 0.0 -> 1.0
    96              normalized = (normalized - info->minimum) / range;
    97              // Normalize to -1.0 -> 1.0
    98              normalized = normalized * 2.0f - 1.0f;
    99          }
   100  
   101          _glfwInputJoystickAxis(js, index, normalized);
   102      }
   103  }
   104  
   105  // Poll state of absolute axes
   106  //
   107  static void pollAbsState(_GLFWjoystick* js)
   108  {
   109      for (int code = 0;  code < ABS_CNT;  code++)
   110      {
   111          if (js->linjs.absMap[code] < 0)
   112              continue;
   113  
   114          struct input_absinfo* info = &js->linjs.absInfo[code];
   115  
   116          if (ioctl(js->linjs.fd, EVIOCGABS(code), info) < 0)
   117              continue;
   118  
   119          handleAbsEvent(js, code, info->value);
   120      }
   121  }
   122  
   123  #define isBitSet(bit, arr) (arr[(bit) / 8] & (1 << ((bit) % 8)))
   124  
   125  // Attempt to open the specified joystick device
   126  //
   127  static GLFWbool openJoystickDevice(const char* path)
   128  {
   129      for (int jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)
   130      {
   131          if (!_glfw.joysticks[jid].connected)
   132              continue;
   133          if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0)
   134              return GLFW_FALSE;
   135      }
   136  
   137      _GLFWjoystickLinux linjs = {0};
   138      linjs.fd = open(path, O_RDONLY | O_NONBLOCK);
   139      if (linjs.fd == -1)
   140          return GLFW_FALSE;
   141  
   142      char evBits[(EV_CNT + 7) / 8] = {0};
   143      char keyBits[(KEY_CNT + 7) / 8] = {0};
   144      char absBits[(ABS_CNT + 7) / 8] = {0};
   145      struct input_id id;
   146  
   147      if (ioctl(linjs.fd, EVIOCGBIT(0, sizeof(evBits)), evBits) < 0 ||
   148          ioctl(linjs.fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits) < 0 ||
   149          ioctl(linjs.fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits) < 0 ||
   150          ioctl(linjs.fd, EVIOCGID, &id) < 0)
   151      {
   152          _glfwInputError(GLFW_PLATFORM_ERROR,
   153                          "Linux: Failed to query input device: %s",
   154                          strerror(errno));
   155          close(linjs.fd);
   156          return GLFW_FALSE;
   157      }
   158  
   159      // Ensure this device supports the events expected of a joystick
   160      if (!isBitSet(EV_ABS, evBits))
   161      {
   162          close(linjs.fd);
   163          return GLFW_FALSE;
   164      }
   165  
   166      char name[256] = "";
   167  
   168      if (ioctl(linjs.fd, EVIOCGNAME(sizeof(name)), name) < 0)
   169          strncpy(name, "Unknown", sizeof(name));
   170  
   171      char guid[33] = "";
   172  
   173      // Generate a joystick GUID that matches the SDL 2.0.5+ one
   174      if (id.vendor && id.product && id.version)
   175      {
   176          sprintf(guid, "%02x%02x0000%02x%02x0000%02x%02x0000%02x%02x0000",
   177                  id.bustype & 0xff, id.bustype >> 8,
   178                  id.vendor & 0xff,  id.vendor >> 8,
   179                  id.product & 0xff, id.product >> 8,
   180                  id.version & 0xff, id.version >> 8);
   181      }
   182      else
   183      {
   184          sprintf(guid, "%02x%02x0000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00",
   185                  id.bustype & 0xff, id.bustype >> 8,
   186                  name[0], name[1], name[2], name[3],
   187                  name[4], name[5], name[6], name[7],
   188                  name[8], name[9], name[10]);
   189      }
   190  
   191      int axisCount = 0, buttonCount = 0, hatCount = 0;
   192  
   193      for (int code = BTN_MISC;  code < KEY_CNT;  code++)
   194      {
   195          if (!isBitSet(code, keyBits))
   196              continue;
   197  
   198          linjs.keyMap[code - BTN_MISC] = buttonCount;
   199          buttonCount++;
   200      }
   201  
   202      for (int code = 0;  code < ABS_CNT;  code++)
   203      {
   204          linjs.absMap[code] = -1;
   205          if (!isBitSet(code, absBits))
   206              continue;
   207  
   208          if (code >= ABS_HAT0X && code <= ABS_HAT3Y)
   209          {
   210              linjs.absMap[code] = hatCount;
   211              hatCount++;
   212              // Skip the Y axis
   213              code++;
   214          }
   215          else
   216          {
   217              if (ioctl(linjs.fd, EVIOCGABS(code), &linjs.absInfo[code]) < 0)
   218                  continue;
   219  
   220              linjs.absMap[code] = axisCount;
   221              axisCount++;
   222          }
   223      }
   224  
   225      _GLFWjoystick* js =
   226          _glfwAllocJoystick(name, guid, axisCount, buttonCount, hatCount);
   227      if (!js)
   228      {
   229          close(linjs.fd);
   230          return GLFW_FALSE;
   231      }
   232  
   233      strncpy(linjs.path, path, sizeof(linjs.path) - 1);
   234      memcpy(&js->linjs, &linjs, sizeof(linjs));
   235  
   236      pollAbsState(js);
   237  
   238      _glfwInputJoystick(js, GLFW_CONNECTED);
   239      return GLFW_TRUE;
   240  }
   241  
   242  #undef isBitSet
   243  
   244  // Frees all resources associated with the specified joystick
   245  //
   246  static void closeJoystick(_GLFWjoystick* js)
   247  {
   248      _glfwInputJoystick(js, GLFW_DISCONNECTED);
   249      close(js->linjs.fd);
   250      _glfwFreeJoystick(js);
   251  }
   252  
   253  // Lexically compare joysticks by name; used by qsort
   254  //
   255  static int compareJoysticks(const void* fp, const void* sp)
   256  {
   257      const _GLFWjoystick* fj = fp;
   258      const _GLFWjoystick* sj = sp;
   259      return strcmp(fj->linjs.path, sj->linjs.path);
   260  }
   261  
   262  
   263  //////////////////////////////////////////////////////////////////////////
   264  //////                       GLFW internal API                      //////
   265  //////////////////////////////////////////////////////////////////////////
   266  
   267  void _glfwDetectJoystickConnectionLinux(void)
   268  {
   269      if (_glfw.linjs.inotify <= 0)
   270          return;
   271  
   272      ssize_t offset = 0;
   273      char buffer[16384];
   274      const ssize_t size = read(_glfw.linjs.inotify, buffer, sizeof(buffer));
   275  
   276      while (size > offset)
   277      {
   278          regmatch_t match;
   279          const struct inotify_event* e = (struct inotify_event*) (buffer + offset);
   280  
   281          offset += sizeof(struct inotify_event) + e->len;
   282  
   283          if (regexec(&_glfw.linjs.regex, e->name, 1, &match, 0) != 0)
   284              continue;
   285  
   286          char path[PATH_MAX];
   287          snprintf(path, sizeof(path), "/dev/input/%s", e->name);
   288  
   289          if (e->mask & (IN_CREATE | IN_ATTRIB))
   290              openJoystickDevice(path);
   291          else if (e->mask & IN_DELETE)
   292          {
   293              for (int jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)
   294              {
   295                  if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0)
   296                  {
   297                      closeJoystick(_glfw.joysticks + jid);
   298                      break;
   299                  }
   300              }
   301          }
   302      }
   303  }
   304  
   305  
   306  //////////////////////////////////////////////////////////////////////////
   307  //////                       GLFW platform API                      //////
   308  //////////////////////////////////////////////////////////////////////////
   309  
   310  GLFWbool _glfwInitJoysticksLinux(void)
   311  {
   312      const char* dirname = "/dev/input";
   313  
   314      _glfw.linjs.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
   315      if (_glfw.linjs.inotify > 0)
   316      {
   317          // HACK: Register for IN_ATTRIB to get notified when udev is done
   318          //       This works well in practice but the true way is libudev
   319  
   320          _glfw.linjs.watch = inotify_add_watch(_glfw.linjs.inotify,
   321                                                dirname,
   322                                                IN_CREATE | IN_ATTRIB | IN_DELETE);
   323      }
   324  
   325      // Continue without device connection notifications if inotify fails
   326  
   327      if (regcomp(&_glfw.linjs.regex, "^event[0-9]\\+$", 0) != 0)
   328      {
   329          _glfwInputError(GLFW_PLATFORM_ERROR, "Linux: Failed to compile regex");
   330          return GLFW_FALSE;
   331      }
   332  
   333      int count = 0;
   334  
   335      DIR* dir = opendir(dirname);
   336      if (dir)
   337      {
   338          struct dirent* entry;
   339  
   340          while ((entry = readdir(dir)))
   341          {
   342              regmatch_t match;
   343  
   344              if (regexec(&_glfw.linjs.regex, entry->d_name, 1, &match, 0) != 0)
   345                  continue;
   346  
   347              char path[PATH_MAX];
   348  
   349              snprintf(path, sizeof(path), "%s/%s", dirname, entry->d_name);
   350  
   351              if (openJoystickDevice(path))
   352                  count++;
   353          }
   354  
   355          closedir(dir);
   356      }
   357  
   358      // Continue with no joysticks if enumeration fails
   359  
   360      qsort(_glfw.joysticks, count, sizeof(_GLFWjoystick), compareJoysticks);
   361      return GLFW_TRUE;
   362  }
   363  
   364  void _glfwTerminateJoysticksLinux(void)
   365  {
   366      for (int jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)
   367      {
   368          _GLFWjoystick* js = _glfw.joysticks + jid;
   369          if (js->connected)
   370              closeJoystick(js);
   371      }
   372  
   373      if (_glfw.linjs.inotify > 0)
   374      {
   375          if (_glfw.linjs.watch > 0)
   376              inotify_rm_watch(_glfw.linjs.inotify, _glfw.linjs.watch);
   377  
   378          close(_glfw.linjs.inotify);
   379          regfree(&_glfw.linjs.regex);
   380      }
   381  }
   382  
   383  GLFWbool _glfwPollJoystickLinux(_GLFWjoystick* js, int mode)
   384  {
   385      // Read all queued events (non-blocking)
   386      for (;;)
   387      {
   388          struct input_event e;
   389  
   390          errno = 0;
   391          if (read(js->linjs.fd, &e, sizeof(e)) < 0)
   392          {
   393              // Reset the joystick slot if the device was disconnected
   394              if (errno == ENODEV)
   395                  closeJoystick(js);
   396  
   397              break;
   398          }
   399  
   400          if (e.type == EV_SYN)
   401          {
   402              if (e.code == SYN_DROPPED)
   403                  _glfw.linjs.dropped = GLFW_TRUE;
   404              else if (e.code == SYN_REPORT)
   405              {
   406                  _glfw.linjs.dropped = GLFW_FALSE;
   407                  pollAbsState(js);
   408              }
   409          }
   410  
   411          if (_glfw.linjs.dropped)
   412              continue;
   413  
   414          if (e.type == EV_KEY)
   415              handleKeyEvent(js, e.code, e.value);
   416          else if (e.type == EV_ABS)
   417              handleAbsEvent(js, e.code, e.value);
   418      }
   419  
   420      return js->connected;
   421  }
   422  
   423  const char* _glfwGetMappingNameLinux(void)
   424  {
   425      return "Linux";
   426  }
   427  
   428  void _glfwUpdateGamepadGUIDLinux(char* guid)
   429  {
   430  }
   431