[PATCH 2/2] RFC: Implement XInputSetState

Christoph Brill egore911 at gmail.com
Sat Feb 11 17:05:38 CST 2017


This patch basically asks for the right direction:

- Is using libevdev on Linux OK?
- Is opening the devices at DLL_PROCESS_ATTACH a good approach?
- Does the implementation for Linux belong to another file?
- Should the implementation try to call into other dlls (e.g. dinput) instead?
---
 dlls/xinput1_3/Makefile.in      |   2 +
 dlls/xinput1_3/xinput1_3_main.c | 174 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 172 insertions(+), 4 deletions(-)

diff --git a/dlls/xinput1_3/Makefile.in b/dlls/xinput1_3/Makefile.in
index cf8f730e99..c2683b0d50 100644
--- a/dlls/xinput1_3/Makefile.in
+++ b/dlls/xinput1_3/Makefile.in
@@ -4,4 +4,6 @@ IMPORTLIB = xinput
 C_SRCS = \
 	xinput1_3_main.c
 
+EXTRALIBS = -levdev
+
 RC_SRCS = version.rc
diff --git a/dlls/xinput1_3/xinput1_3_main.c b/dlls/xinput1_3/xinput1_3_main.c
index f3f0edfe1b..86a4b1ffb7 100644
--- a/dlls/xinput1_3/xinput1_3_main.c
+++ b/dlls/xinput1_3/xinput1_3_main.c
@@ -20,7 +20,17 @@
 #include "config.h"
 #include <assert.h>
 #include <stdarg.h>
+#include <stdio.h> /* sprintf */
 #include <string.h>
+#include <sys/stat.h> /* open */
+#include <fcntl.h> /* open */
+#include <errno.h> /* ENOENT */
+#include <unistd.h> /* close */
+
+#ifdef __linux__
+#include <linux/input.h>
+#include <libevdev-1.0/libevdev/libevdev.h>
+#endif
 
 #include "wine/debug.h"
 #include "windef.h"
@@ -39,14 +49,170 @@ static BOOL enabled = TRUE;
 struct
 {
     BOOL connected;
+    struct libevdev *dev;
+    int fd;
+    int effect_id;
+    XINPUT_VIBRATION last;
 } controllers[XUSER_MAX_COUNT];
 
+static void controllers_init(void)
+{
+    int i, j, fd, rc;
+    char path[50];
+    struct libevdev *dev = NULL;
+
+    j = 0;
+
+    /* Try to find a valid gamepad with vibration support */
+    for (i = 0; i < 100; i++)
+    {
+        /** Open the path nonblocking */
+        sprintf(path, "/dev/input/event%d", i);
+        fd = open(path, O_RDWR|O_NONBLOCK);
+
+        /* File does not exist, skip */
+        if (fd == ENOENT)
+        {
+            continue;
+        }
+
+        /* Error occured opening the file, skip */
+        if (fd < 0)
+        {
+            WARN("Failed to open device %s\n", path);
+            continue;
+        }
+
+        /* Create libevdev device */
+        rc = libevdev_new_from_fd(fd, &(dev));
+        if (rc < 0)
+        {
+            WARN("Failed to create device for %s\n", path);
+            close(fd);
+            continue;
+        }
+
+        /* Check for vibration support */
+        rc = libevdev_has_event_code(dev, EV_FF, FF_RUMBLE);
+        if (rc < 0)
+        {
+            WARN("Failed to check for force vibrate support on device %s\n", path);
+            libevdev_free(dev);
+            close(fd);
+            continue;
+        }
+
+        /* Device looks like a gamepad, add it */
+        if (rc == 1)
+        {
+            TRACE("Connected %d with %s\n", j, path);
+            controllers[j].connected = TRUE;
+            controllers[j].fd = fd;
+            controllers[j].dev = dev;
+            controllers[j].effect_id = 0;
+            controllers[j].last.wLeftMotorSpeed = 0;
+            controllers[j].last.wRightMotorSpeed = 0;
+
+            j++;
+            if (j >= XUSER_MAX_COUNT)
+            {
+                return;
+            }
+        }
+    }
+}
+
+static void controllers_deinit(void)
+{
+    int i;
+
+    for (i = 0; i < XUSER_MAX_COUNT; i++)
+    {
+        if (controllers[i].connected)
+        {
+            libevdev_free(controllers[i].dev);
+            controllers[i].dev = NULL;
+            close(controllers[i].fd);
+            controllers[i].fd = -1;
+        }
+    }
+}
+
+DWORD WINAPI XInputSetState_Impl(DWORD index, XINPUT_VIBRATION* vibration, BOOL keep)
+{
+    int rc;
+    struct input_event play;
+
+    if (vibration && (vibration->wLeftMotorSpeed > 0 || vibration->wRightMotorSpeed > 0))
+    {
+        /* Create the rumble effect */
+        struct ff_effect effect;
+        memset(&effect, 0, sizeof(struct ff_effect));
+        effect.id = -1;
+        effect.direction = 0; // down
+        effect.replay.length = 500; // ms
+        effect.replay.delay = 0;
+        effect.trigger.button = 0; // don't trigger on button press
+        effect.trigger.interval = 0;
+        effect.type = FF_RUMBLE;
+        if (keep)
+        {
+            effect.u.rumble.strong_magnitude = vibration->wLeftMotorSpeed;
+            effect.u.rumble.weak_magnitude = vibration->wRightMotorSpeed;
+        }
+        rc = ioctl(controllers[index].fd, EVIOCSFF, &effect);
+        if (rc < 0)
+        {
+            return ERROR_NOT_SUPPORTED;
+        }
+        controllers[index].effect_id = effect.id;
+        controllers[index].last.wLeftMotorSpeed = vibration->wLeftMotorSpeed;
+        controllers[index].last.wRightMotorSpeed = vibration->wRightMotorSpeed;
+
+        /* Play the rumble effect */
+        memset(&play, 0, sizeof(struct input_event));
+        play.type = EV_FF;
+        play.code = controllers[index].effect_id;
+        play.value = 1;
+
+        rc = write(controllers[index].fd, (const void*) &play, sizeof(play));
+        if (rc < 0)
+        {
+            return ERROR_NOT_SUPPORTED;
+        }
+    }
+    else
+    {
+        /** No vibration, cancel if necessary */
+        if (controllers[index].effect_id)
+        {
+            rc = ioctl(controllers[index].fd, EVIOCRMFF, controllers[index].effect_id);
+            controllers[index].effect_id = 0;
+            if (keep)
+            {
+                controllers[index].last.wLeftMotorSpeed = 0;
+                controllers[index].last.wRightMotorSpeed = 0;
+            }
+            if (rc < 0)
+            {
+                return ERROR_NOT_SUPPORTED;
+            }
+        }
+    }
+
+    return ERROR_SUCCESS;
+}
+
 BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved)
 {
     switch(reason)
     {
     case DLL_PROCESS_ATTACH:
         DisableThreadLibraryCalls(inst);
+        controllers_init();
+        break;
+    case DLL_PROCESS_DETACH:
+        controllers_deinit();
         break;
     }
     return TRUE;
@@ -69,7 +235,7 @@ void WINAPI DECLSPEC_HOTPATCH XInputEnable(BOOL enable)
         {
             if (controllers[i].connected)
             {
-                FIXME("XInputEnable should reenable vibration and resend the last event\n");
+                XInputSetState_Impl(i, &(controllers[i].last), FALSE);
             }
         }
     }
@@ -79,7 +245,7 @@ void WINAPI DECLSPEC_HOTPATCH XInputEnable(BOOL enable)
         {
             if (controllers[i].connected)
             {
-                FIXME("XInputEnable should disable vibration and keep the last event\n");
+                XInputSetState_Impl(i, NULL, FALSE);
             }
         }
     }
@@ -87,7 +253,7 @@ void WINAPI DECLSPEC_HOTPATCH XInputEnable(BOOL enable)
 
 DWORD WINAPI XInputSetState(DWORD index, XINPUT_VIBRATION* vibration)
 {
-    FIXME("(index %u, vibration %p) Stub!\n", index, vibration);
+    TRACE("XInputSetState(index %u, vibration %p)\n", index, vibration);
 
     if (index >= XUSER_MAX_COUNT)
         return ERROR_BAD_ARGUMENTS;
@@ -99,7 +265,7 @@ DWORD WINAPI XInputSetState(DWORD index, XINPUT_VIBRATION* vibration)
         return ERROR_SUCCESS;
     }
 
-    return ERROR_NOT_SUPPORTED;
+    return XInputSetState_Impl(index, vibration, TRUE);
 }
 
 DWORD WINAPI DECLSPEC_HOTPATCH XInputGetState(DWORD index, XINPUT_STATE* state)
-- 
2.11.1




More information about the wine-patches mailing list