[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